Add dark mode with toggle and localStorage persistence

Uses Tailwind v4 class-based dark mode with a Stimulus controller for
toggling. Respects prefers-color-scheme as default, prevents FOUC with
an inline script, and persists the user's choice in localStorage. All
views updated with dark: variants for backgrounds, text, borders,
badges, buttons, and form inputs.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Dan Milne
2026-03-22 00:37:58 +11:00
parent 43958f50ce
commit 3d98261a51
38 changed files with 744 additions and 636 deletions

View File

@@ -3,19 +3,19 @@
<% form_object = form.respond_to?(:object) ? form.object : (object || form) %>
<% if form_object&.errors&.any? %>
<div class="rounded-md bg-red-50 p-4 mb-6 border border-red-200" role="alert" aria-labelledby="form-errors-title" data-form-errors-target="container">
<div class="rounded-md bg-red-50 dark:bg-red-900/30 p-4 mb-6 border border-red-200 dark:border-red-700" role="alert" aria-labelledby="form-errors-title" data-form-errors-target="container">
<div class="flex">
<div class="flex-shrink-0">
<svg class="h-5 w-5 text-red-400" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<svg class="h-5 w-5 text-red-400 dark:text-red-300" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clip-rule="evenodd"/>
</svg>
</div>
<div class="ml-3 flex-1">
<h3 id="form-errors-title" class="text-sm font-medium text-red-800">
<h3 id="form-errors-title" class="text-sm font-medium text-red-800 dark:text-red-200">
<%= pluralize(form_object.errors.count, "error") %> prohibited this <%= form_object.class.name.downcase.gsub(/^admin::/, '') %> from being saved:
</h3>
<div class="mt-2">
<ul class="list-disc space-y-1 pl-5 text-sm text-red-700">
<ul class="list-disc space-y-1 pl-5 text-sm text-red-700 dark:text-red-300">
<% form_object.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
@@ -24,7 +24,7 @@
</div>
<div class="ml-auto pl-3">
<div class="-mx-1.5 -my-1.5">
<button type="button" data-action="click->form-errors#dismiss" class="inline-flex rounded-md bg-red-50 p-1.5 text-red-500 hover:bg-red-100 focus:outline-none focus:ring-2 focus:ring-red-600 focus:ring-offset-2 focus:ring-offset-red-50" aria-label="Dismiss">
<button type="button" data-action="click->form-errors#dismiss" class="inline-flex rounded-md bg-red-50 dark:bg-red-900/30 p-1.5 text-red-500 dark:text-red-400 hover:bg-red-100 dark:hover:bg-red-900/50 focus:outline-none focus:ring-2 focus:ring-red-600 focus:ring-offset-2 focus:ring-offset-red-50 dark:focus:ring-offset-gray-900" aria-label="Dismiss">
<svg class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z" />
</svg>
@@ -33,4 +33,4 @@
</div>
</div>
</div>
<% end %>
<% end %>