Dark Mode
Add beautiful dark mode support to your Rails application with RapidRails UI's built-in theme system.
How Dark Mode Works
RapidRails UI uses a smart dark mode system that respects your OS preferences while giving you full control.
It combines prefers-color-scheme detection with data-theme attributes for the best of both worlds.
Smart Theme Detection
The theme system automatically:
- Detects your OS theme using
prefers-color-schemeon first visit - Watches for OS changes and updates automatically if you haven't set a preference
- Remembers your choice in localStorage when you manually toggle the theme
- Allows override so you can use a different theme than your OS setting
Best of both worlds:
If you never click the theme toggle, the site automatically matches your OS theme and updates when you change it. If you manually toggle the theme, your preference is saved and respected regardless of OS changes.
How It Works Technically
RapidRails UI configures Tailwind CSS v4 to use a data-theme attribute via a custom variant in app/assets/tailwind/application.css:
@import "tailwindcss";
/**
* Dark Mode Configuration (Tailwind v4)
* Uses data-theme attribute instead of class
* See: https://tailwindcss.com/docs/dark-mode
*/
@custom-variant dark (&:where([data-theme="dark"], [data-theme="dark"] *));
The Stimulus controller then sets the data-theme attribute on the HTML element, which triggers Tailwind's dark: classes:
<html data-theme="dark" style="color-scheme: light;">
<!-- All dark: classes now activate -->
<button class="bg-white dark:bg-zinc-900">
Button
</button>
</html>
When data-theme="dark" is set, Tailwind's dark: variant becomes active, applying all dark mode styles automatically.
Automatic Setup (Recommended)
If you ran the RapidRails UI installer, dark mode is already configured for you:
rails generate rapid_rails_ui:install
The installer automatically sets up:
app/assets/tailwind/application.css
app/views/shared/_dark_mode_switcher.html.erb
app/javascript/controllers/theme_controller.js
Manual Setup
If you prefer to set up dark mode manually or customize the installer's defaults, follow these steps:
Step 1: Configure Tailwind CSS
Add the dark mode custom variant to your Tailwind CSS file:
// app/assets/tailwind/application.css
@import "tailwindcss";
@custom-variant dark (&:where([data-theme="dark"], [data-theme="dark"] *));
Step 2: Add Theme Toggle
Create a simple theme switcher using Stimulus:
// app/javascript/controllers/theme_controller.js
import { Controller } from "@hotwired/stimulus"
export default class extends Controller {
connect() {
// Load saved theme or default to light
const theme = localStorage.getItem("theme") || "light"
this.setTheme(theme)
}
toggle() {
const current = document.documentElement.getAttribute("data-theme")
const next = current === "dark" ? "light" : "dark"
this.setTheme(next)
}
setTheme(theme) {
document.documentElement.setAttribute("data-theme", theme)
localStorage.setItem("theme", theme)
}
}
Step 3: Add Toggle Button
Add a button in your layout to toggle the theme:
// app/views/layouts/application.html.erb
<div data-controller="theme">
<button
data-action="click->theme#toggle"
class="p-2 rounded-lg hover:bg-zinc-100 dark:hover:bg-zinc-800"
>
Toggle Dark Mode
</button>
</div>
Using the Theme Switcher
If you used the installer, RapidRails UI provides a ready-to-use theme switcher component with sun/moon icons that automatically toggles between light and dark modes.
Basic Usage
Add the switcher anywhere in your layout:
// app/views/layouts/application.html.erb
<header>
<nav>
<!-- Your navigation -->
<%= render 'shared/dark_mode_switcher' %>
</nav>
</header>
The switcher will display:
- A sun icon in dark mode (click to switch to light)
- A moon icon in light mode (click to switch to dark)
- Smooth transitions between themes
- Persisted preference in localStorage
Customizing Dark Mode Colors
All RapidRails UI components use Tailwind's dark: variant with semantic color choices.
You can customize dark mode colors in two ways:
Option 1: Per-Component Override
Override colors using the class option. When fully customizing colors, omit the color parameter to avoid conflicts:
<%= rui_button(
"Custom Dark Mode",
class: "bg-pink-600 dark:bg-purple-600 hover:bg-pink-700 dark:hover:bg-purple-700 text-white"
) %>
Option 2: Global Configuration
Customize default dark mode colors in your RapidRails UI initializer:
# config/initializers/rapid_rails_ui.rb
RapidRailsUI.configure do |config|
config.colors[:primary] = {
base: "blue-600",
hover: "blue-700",
active: "blue-800",
text: "white",
dark: {
base: "purple-500", # Custom dark mode primary
hover: "purple-600",
active: "purple-700",
text: "white"
}
}
end
Note: Global color customization is an advanced feature. Most apps won't need it, the default dark mode colors are carefully chosen for accessibility and aesthetics.
Testing Dark Mode
Verify your dark mode setup works correctly:
Visual Test
- Start your Rails server and open your application
- Click the theme switcher (sun/moon icon)
- Verify the page switches between light and dark themes
- Refresh the page, theme should persist
- Inspect the
<html>element, should havedata-theme="dark"ordata-theme="light"
Component Test
Add a test component to any view:
<div class="p-8 bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-800">
<h2 class="text-2xl font-bold text-zinc-900 dark:text-white mb-4">
Dark Mode Test
</h2>
<p class="text-zinc-600 dark:text-zinc-400 mb-4">
This text should change color in dark mode.
</p>
<%= rui_button("Primary Button", color: :primary) %>
<%= rui_button("Danger Button", color: :danger, variant: :outline) %>
</div>
Dark Mode Test
This text should change color in dark mode.
Toggle dark mode, all elements should adapt their colors automatically.
Success! If all colors adapt correctly when toggling, your dark mode is working perfectly.