Skip to content

This blog post uses Rails and Hotwire to let you edit the color scheme of this site, Joy of Rails.

Give it a try.


Link to headingHow it works

Pretty cool, huh? Here are the key ingredients:

  • CSS Variables: Also known as CSS custom properties
  • SQLite: A color_schemes table to store curated color schemes
  • Cookies: A session cookie to store your saved color scheme selection
  • Hotwire: Server-rendered HTML and a single-page app experience powered by Rails, Turbo Drive, and Turbo Frames

I started with the premise of using a monochromatic color scheme based on the eleven-item color scale Tailwind uses for each of its default color sets. The curated options are all generated from uicolors.app.

Each color scheme is a row in the color_schemes table, with a name and CSS hex code values for each of the eleven weights.

db/migrate/<timestamp>_create_color_schemes.rb
class CreateColorSchemes < ActiveRecord::Migration[7.1]
  def change
    create_table :color_schemes do |t|
      t.string :name, null: false, index: {unique: true}
      t.string :weight_50, null: false
      t.string :weight_100, null: false
      t.string :weight_200, null: false
      # ... and so on
    end
  end
end

CSS variables make it easy to change repeated CSS in a lot of places. You can set a CSS variable with double dashes, --. The CSS variable can be accessed using the var() expression. CSS variables can be overridden and can be defined in terms of other variables.

Here’s a simplified a view of how I used CSS variables to define the main background color of the <body> element. Using the pseudo-class :root means the CSS variable can be accessed from any scope in CSS.

:root {
  --theme-color-50: var(--my-color-50, var(--default-color-50));
  --theme-color-100: var(--my-color-100, var(--default-color-100));
  --theme-color-200: var(--my-color-200, var(--default-color-200));h
  /* and so on */
}

body {
  background-color: var(--theme-color-50);
}

This approach is consistent with the Tailwind docs for using CSS variables to customize Tailwind colors for those of you that may be using (or interested in using) Tailwind.

Most of the buttons in the color scheme demo are connected to <form> elements with actions that point to the ColorSchemesController in the Joy of Rails application.

When you click the "Save" button, the application stores the id of the chosen color scheme in your Rails session:

session[:color_scheme_id] = @color_scheme.id

When the page is rendered, the application checks for the presence of the session data to query for the desired color scheme:

session[:color_scheme_id] && ColorScheme.find(session[:color_scheme_id])

The color scheme CSS variables are written directly into a style tag:

app/view/layouts/application.html.erb
<style>
  :root {
    --my-color-50: <%= @color_scheme.weight_50.to_json %>;
    --my-color-100: <%= @color_scheme.weight_100.to_json %>;
    --my-color-200: <%= @color_scheme.weight_200.to_json %>;
    /* and so on */
  }
</style>

Most of the time-consuming work required to assemble this post was in researching the color palettes and building the site theme with various background and text colors defined in CSS variables.

Link to headingProgressively enhanced

Did you notice how the color choice updated automatically in place? At least, it should have for visitors that have JavaScript enabled.

One of the things I love about Rails and Hotwire is that it’s possible to adopt progressive enhancement.

Progressive enhancement is a design philosophy that provides a baseline of essential content and functionality to as many users as possible, while delivering the best possible experience only to users of the most modern browsers that can run all the required code.

Source: MDN Glossary

In this case, visitors who don’t have JavaScript enabled would see a link to the the color schemes settings page. That page has most of the functionality you see here.

This design is made possible by the fact that the color scheme preview HTML is all rendered on the server with Ruby on Rails. With JavaScript enabled, Turbo JavaScript "upgrades" the default browser form submissions and link clicks to AJAX requests and use the HTML response to replace the parts of the page in place. Without JavaScript, visitors will still get the desired results after a full page navigation and/or redirects.

Progressive enhancement hasn’t necessarily been a hot topic alongside the popular JavaScript frameworks that rely on client-side rendering. It’s nice to see that most of what you see on these pages is static content and shouldn’t require a lot of code running in your browser to view.

Link to headingLook at all the things I’m not doing

Notice how I do not mention any of the major JavaScript frameworks in the list of tools I used to build custom color schemes.

I’m not using any hooks to manage local state. I’m not making any explicit calls to fetch in my application code.

In fact, I wrote only a one line of custom JavaScript to make the color scheme preview selection work: on an onchange handler:

<select onchange="this.form.requestSubmit()">
  <!-- options -->
</select>

This isn’t to say I don’t like writing JavaScript or that I don’t think you should use JavaScript. I’m not here to tell you that you shouldn’t use React. Or Vue, Svelte, Solid, or Angular. Personally, I think JavaScript and the popular JavaScript frameworks are great. I recognize why and how they have become so popular.

I’m also not here to tell you that Rails with Hotwire is objectively better than any of these JavaScript frameworks or similar options.

But—and this is the important point—I didn’t need any client-side rendering or JavaScript component library to make this work. It is possible to build a single-page application experience in Rails. It’s not always necessary to build a single-page application in JavaScript.

For a solo developer with a busy home life and a full-time job, saving time and energy for other important tasks, like writing, with a language and framework I love; this makes all the difference.


Would you like to see an in-depth tutorial on how to build the custom color scheme functionality? Did you find a bug or do you have questions about the content? Please send me an email. You can also connect with me on Twitter, Github, Mastodon, and Linkedin.

Curious to peek behind the curtain and get a glimpse of the magic? Joy of Rails is open source on Github. Feel free to look through the code and contribute.

And if I’ve captured your interest, please subscribe to hear more from me and get notified of new articles by email.

That does it for another glimpse into what’s possible with Ruby on Rails. I hope you enjoyed it.

Rainbow
Rainbow, by Jasper (my son)