Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added

- Dark mode — toggle button in the header switches between light and dark palettes; preference persisted in `localStorage`; respects `prefers-color-scheme` on first visit; driven entirely by CSS custom properties on `[data-theme="dark"]`

## [0.6.0] - 2026-05-26

### Added
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ The dashboard will be available at `/solid_stack` (or whatever path you choose).
- **Job history view** — paginated list of all finished jobs with class name, queue, duration, and finished-at time; filterable by queue (click a badge), class substring, and time period; CSV export respects active filters
- **Auto-refresh** — dashboard, jobs, processes, and history views poll automatically; pauses when the tab is hidden or a checkbox is checked; intervals configurable via `dashboard_refresh_interval` and `default_refresh_interval`
- **Turbo Stream** job discard — removes the row inline without a full page reload
- **Dark mode** — toggle button in the header switches between light and dark palettes; preference persisted in `localStorage`; respects `prefers-color-scheme` on first visit

### Configuration

Expand Down
3 changes: 1 addition & 2 deletions ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@ The path to v1.0.0 is staged: first achieve feature parity with `solid_queue_das
> _Make the interface feel fast and operational, not just functional._

### Added
- **Dark mode** — Stimulus theme controller toggles a `data-theme` attribute; CSS custom properties drive both light and dark palettes; preference persisted in `localStorage`
- **Empty-state improvements** — contextual empty states per section with actionable next steps
o- **Empty-state improvements** — contextual empty states per section with actionable next steps
- **Inline notifications** — flash-style Turbo Stream feedback on bulk actions
- **Responsive layout** — stats cards and tables adapt to narrow viewports
- **CSS audit** — review all inline styles, consolidate utility classes, remove duplication, and enforce consistent use of CSS custom properties across all stylesheets
Expand Down
21 changes: 21 additions & 0 deletions app/assets/stylesheets/solid_stack_web/_02_layout.css
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,27 @@
height: 52px;
}

.sqw-header__inner .sqw-nav { flex: 1; }

.sqw-theme-toggle {
display: flex;
align-items: center;
justify-content: center;
width: 32px;
height: 32px;
padding: 0;
background: none;
border: 1px solid var(--border);
border-radius: var(--radius);
cursor: pointer;
font-size: 16px;
color: var(--text);
line-height: 1;
flex-shrink: 0;
transition: background 0.1s, border-color 0.1s;
}
.sqw-theme-toggle:hover { background: var(--bg); }

.sqw-header__logo {
font-weight: 700;
font-size: 16px;
Expand Down
22 changes: 22 additions & 0 deletions app/assets/stylesheets/solid_stack_web/_12_dark_mode.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
[data-theme="dark"] {
--bg: #0d1117;
--surface: #161b22;
--border: #30363d;
--text: #e6edf3;
--muted: #8b949e;
--primary: #58a6ff;
--danger: #f85149;
--warning: #d29922;
--success: #3fb950;
--info: #39c5cf;
--purple: #bc8cff;
--shadow: 0 1px 3px rgba(0,0,0,.4);
}

[data-theme="dark"] .sqw-flash--notice { background: #1b3a2b; color: #3fb950; border-color: #2d6a4f; }
[data-theme="dark"] .sqw-flash--alert { background: #3d1118; color: #f85149; border-color: #6a2030; }

[data-theme="dark"] .sqw-theme-toggle {
border-color: var(--border);
color: var(--text);
}
4 changes: 3 additions & 1 deletion app/javascript/solid_stack_web/application.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ import RefreshController from "solid_stack_web/refresh_controller"
import SearchController from "solid_stack_web/search_controller"
import SelectionController from "solid_stack_web/selection_controller"
import SparklineTooltipController from "solid_stack_web/sparkline_tooltip_controller"
import ThemeController from "solid_stack_web/theme_controller"

const application = Application.start()
application.register("refresh", RefreshController)
application.register("search", SearchController)
application.register("selection", SelectionController)
application.register("sparkline-tooltip", SparklineTooltipController)
application.register("sparkline-tooltip", SparklineTooltipController)
application.register("theme", ThemeController)
26 changes: 26 additions & 0 deletions app/javascript/solid_stack_web/theme_controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
static targets = ["toggle"]

connect() {
const saved = localStorage.getItem("sqw-theme")
const preferred = window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light"
this.apply(saved || preferred)
}

toggle() {
const current = document.documentElement.getAttribute("data-theme") || "light"
const next = current === "dark" ? "light" : "dark"
localStorage.setItem("sqw-theme", next)
this.apply(next)
}

apply(theme) {
document.documentElement.setAttribute("data-theme", theme)
if (this.hasToggleTarget) {
this.toggleTarget.textContent = theme === "dark" ? "☀" : "☽"
this.toggleTarget.setAttribute("aria-label", theme === "dark" ? "Switch to light mode" : "Switch to dark mode")
}
}
}
4 changes: 3 additions & 1 deletion app/views/layouts/solid_stack_web/application.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
<%= inline_styles %>
<%= javascript_importmap_tags "solid_stack_web" %>
</head>
<body>
<body data-controller="theme">
<header class="sqw-header">
<div class="sqw-header__inner">
<%= link_to "Solid Stack", root_path, class: "sqw-header__logo" %>
Expand All @@ -22,6 +22,8 @@
<%= link_to "Cable", cable_path,
class: "sqw-nav__link#{" sqw-nav__link--active" if current_section == :cable}" %>
</nav>
<button class="sqw-theme-toggle" aria-label="Switch to dark mode"
data-theme-target="toggle" data-action="theme#toggle">☽</button>
</div>
</header>

Expand Down
1 change: 1 addition & 0 deletions config/importmap.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@
pin "solid_stack_web/search_controller", to: "solid_stack_web/search_controller.js"
pin "solid_stack_web/selection_controller", to: "solid_stack_web/selection_controller.js"
pin "solid_stack_web/sparkline_tooltip_controller", to: "solid_stack_web/sparkline_tooltip_controller.js"
pin "solid_stack_web/theme_controller", to: "solid_stack_web/theme_controller.js"