Skip to content
Hive

screenote · Complete light and dark themes

Questions and decisions

brainstorm.md

Round 1

Q1. How should existing users with no saved theme preference be migrated: immediately follow their operating-system preference, preserve the public site's current dark/dashboard's current light appearance until they choose, or use another rule?

A1.

Immediately follow the operating-system preference for every user who has no saved explicit preference. Do not preserve the current public-dark/dashboard-light split. Treat the absence of a preference as System.

Q2. Should an explicit Light, Dark, or System choice be stored only in the current browser, synced to a signed-in account across devices, or use account sync with a browser fallback—and what should happen to a signed-out choice when the user signs in or out?

A2.

Use account sync for signed-in users with a browser/local-storage fallback for signed-out users and for first paint before account data loads. On sign-in, an existing account preference wins; if the account has no preference, adopt the browser choice and save it to the account. On sign-out, retain the effective choice in that browser so the UI does not unexpectedly change.

Q3. What is the definitive surface boundary for this work: besides the public website and signed-in dashboard, which of sign-in/sign-up, shared or embedded Screenotes, framework error pages, emails, and exported or printable views are in scope or explicitly out of scope?

A3.

In scope: the public marketing site, sign-in/sign-up/password-reset flows, the complete signed-in dashboard, and shared/public Screenote views rendered by the web application. Include normal framework error and not-found pages when they use the application shell. Out of scope for this release: transactional emails, exported files, print-specific styling, and third-party/embed surfaces that Screenote does not control.

Q4. Where must the three-state theme switcher remain discoverable on public and signed-in pages, including narrow/mobile layouts, and may signed-out visitors use the same control?

A4.

Make the three-state control available to signed-out visitors and signed-in users. On desktop, place it in the public-site navigation and the dashboard user/settings menu; on narrow/mobile layouts, place it in the corresponding navigation drawer/menu. It must be keyboard accessible, clearly labelled, and show the current Light, Dark, or System selection rather than only a sun/moon icon.

Q5. Should each new theme preserve the existing visual identity through semantic recoloring, or may the public light theme and dashboard dark theme introduce broader visual changes; are theme-specific logos, illustrations, or other brand assets expected?

A5.

Preserve the existing Screenote visual identity and layout through semantic recoloring. The public light theme and dashboard dark theme may refine shadows, borders, elevation, and muted surfaces where necessary for polish and contrast, but should not become redesigns. Reuse existing brand assets where they work; add theme-specific logo/illustration variants only when an existing asset is unreadable or visually broken in one theme.

Q6. For screenshots, uploaded media, rich text, editor content, code/diffs, and user-authored colors, should the content remain visually unchanged while only its surrounding chrome adapts, or should any of that content itself be transformed for the active theme?

A6.

Keep user-authored content and uploaded screenshots/media unchanged. Adapt only the surrounding chrome. Syntax highlighting, diffs, editors, and rich-text surfaces may use theme-aware presentation palettes, but must not rewrite stored content or transform uploaded images. Preserve intentional user-authored colors unless contrast would make application controls or essential text inaccessible.

Q7. What accessibility standard is the release bar—for example WCAG 2.2 AA for text, controls, non-text UI, focus indicators, disabled states, and validation/error messaging—and are there any stricter brand contrast requirements?

A7.

Use WCAG 2.2 AA as the release bar across both themes, including text, controls, non-text UI, focus indicators, disabled states, and validation/error messaging. No stricter brand-specific target is required, but do not accept regressions merely because a color is part of the current palette.

Q8. Which concrete pages, journeys, and states must automated browser coverage exercise before this is accepted, and what supported browser/device matrix must prove first paint, switching, persistence, System-mode OS changes, and representative empty/loading/error/dialog/editor states?

A8.

Automated coverage must exercise representative public, authentication, dashboard, shared Screenote, editor/comment/answer, dialog, table/card, and empty/loading/error states. Verify first paint has no wrong-theme flash; explicit Light and Dark switching; persistence across reload and sign-in/sign-out; System mode following a simulated OS change; keyboard operation and accessible naming of the switcher; and representative visual/contrast regressions. Required browser baseline: current Chromium desktop plus a mobile viewport in CI; keep implementation standards-compatible for Firefox and Safari and add focused cross-browser tests where theme bootstrapping or media-query behavior differs.

Requirements

Actors

  • Signed-out visitor — browses the marketing site, auth flows, and shared Screenote links; may set a theme that lives in their browser only.
  • Signed-in user — uses the dashboard; their explicit choice syncs to their account across devices.
  • Shared-link viewer — opens a public/shared Screenote rendered by the web app; treated as a signed-out visitor.
  • Operating system — supplies prefers-color-scheme and may change it mid-session.

Theme model

  • Preference is one of light, dark, system. Absence of a stored preference is system.
  • Effective theme = light/dark when explicit; otherwise resolved from prefers-color-scheme (fall back to light when the OS reports no preference).
  • Existing users are not grandfathered: today's public-dark/dashboard-light split is replaced by OS-driven resolution on first load after release.
  • One semantic token set drives both themes. The three token islands present today must collapse into it: :root light tokens in app/assets/stylesheets/application.css, .landing-body --landing-* in landing.css, and --dark-* in auth.css.
  • Semantic names (surface, elevated surface, border, text, muted text, accent, danger/success/warning, focus ring), not literal color names — no --dark-bg used in a light context.
  • CSS color-scheme is set alongside the tokens so native form controls, scrollbars, and <meta name="theme-color"> follow the active theme.

Flow — resolution, persistence, sync

  • First paint (any visitor): a blocking, nonce-carrying inline script in layouts/_head.html.erb reads the stored preference, resolves it, and sets the theme attribute on <html> before first paint. CSP is script_src :self with a nonce generator, so the tag must use nonce: true.
  • Signed out: explicit choice is written to browser local storage and survives reload and navigation across all three layouts (landing, auth, application).
  • Signed in: explicit choice is persisted on the user record (the users table has no preference column today — a migration is required) and applied on every device. Local storage still backs first paint before account data is available.
  • Sign-in merge: an existing account preference wins over the browser value; if the account has none, the browser choice is adopted and saved to the account.
  • Sign-out: the effective theme is retained in that browser — the UI must not visibly change at sign-out.
  • System mode live: while system is selected, a prefers-color-scheme change flips the UI without reload, with no full-page refresh and no loss of page state.
  • Server-rendered pages must not emit a hardcoded theme that contradicts the client-resolved one (no wrong-theme HTML that the script then corrects).

Flow — the switcher

  • One three-state control (Light / Dark / System) available to signed-out visitors and signed-in users.
  • Desktop placement: public-site navigation (layouts/landing.html.erb) and the dashboard user/settings menu (layouts/application.html.erb header).
  • Narrow/mobile placement: the corresponding navigation drawer/menu.
  • The control shows the current selection as text, not a bare sun/moon glyph; it is reachable and operable by keyboard, has an accessible name, and exposes which option is selected to assistive tech.
  • Selecting an option applies the theme immediately, with no page reload.

Surfaces in scope

  • Public marketing site; sign-in, sign-up, and password-reset flows; the complete signed-in dashboard; shared/public Screenote views rendered by the app; framework error and not-found pages that use the application shell.
  • Every component family gets tokens applied: navigation and header, forms and validation messaging, dialogs, tables, cards, the annotation editor and canvas chrome, comments, answer/resolution flows, flash messages, badges, banners, and empty/loading/error states.

Visual identity and user content

  • Semantic recoloring only — preserve the existing Screenote identity and layout. Shadows, borders, elevation, and muted surfaces may be refined for contrast and polish; neither theme becomes a redesign.
  • Reuse existing brand assets; add a theme-specific logo or illustration variant only where an asset is unreadable or visually broken in one theme.
  • Uploaded screenshots and user-authored content render unchanged in both themes — only surrounding chrome adapts. No image filtering, inversion, or dimming of uploaded media.
  • Syntax highlighting, diffs, editors, and rich-text surfaces may use theme-aware presentation palettes but must never rewrite stored content.
  • Intentional user-authored colors are preserved unless they would make an application control or essential text inaccessible.

Accessibility bar

  • WCAG 2.2 AA in both themes for text, controls, non-text UI, focus indicators, disabled states, and validation/error messaging.
  • Focus visibility is verified against both themes' backgrounds — a focus ring that disappears on the dark surface is a defect.
  • No stricter brand target, and no exemption for a failing color merely because it ships in today's palette.

Acceptance examples

  • First-time visitor, OS set to dark → landing page and dashboard both render dark; switcher reads System.
  • First-time visitor, OS set to light → landing page renders light (a change from today) and dashboard renders light; switcher reads System.
  • Signed-out visitor picks Dark, reloads → still dark; picks System again with OS light → returns to light.
  • Signed-out visitor with Dark signs in to an account whose preference is Light → UI becomes light and the switcher reads Light.
  • Signed-out visitor with Dark signs in to an account with no preference → stays dark, and dark is saved to the account; a second signed-in device loads dark.
  • Signed-in user sets Light, signs out → browser remains light.
  • System selected, OS flips dark → light with the page open → UI updates live, no reload.
  • Reload with Dark stored → no light flash: the first painted frame is already dark.
  • Keyboard user tabs to the switcher, changes the option with the keyboard, and the accessible name conveys the current selection.
  • A screenshot with annotation pins renders byte-identical image pixels in both themes while the surrounding canvas chrome, pins, and comment panel adapt.
  • Every in-scope page passes automated contrast checks in both themes.

Automated coverage

  • MiniTest unit/integration coverage for preference resolution, the sign-in merge rule, sign-out retention, and persistence of the account preference.
  • Playwright system tests (existing ApplicationSystemTestCase, Capybara against the dev server on :3005) covering: no wrong-theme flash on first paint; explicit Light/Dark switching; persistence across reload and sign-in/sign-out; System following a simulated OS change via Playwright media emulation; keyboard operation and accessible naming of the switcher; representative visual/contrast regressions.
  • Representative states exercised: public, authentication, dashboard, shared Screenote, editor/comment/answer, dialogs, tables/cards, and empty/loading/error.
  • CI baseline: current Chromium desktop plus a mobile viewport. Implementation stays standards-compatible with Firefox and Safari (PLAYWRIGHT_BROWSER already parameterizes the browser); add focused cross-browser tests only where theme bootstrapping or media-query behavior diverges.
  • Follow existing conventions: data-testid hooks, fixtures, BEM class names, Stimulus for behavior, no inline styles, no npm build step.

Out of scope

  • Transactional emails and mailer layouts.
  • Exported files and print-specific styling.
  • Third-party and embed surfaces Screenote does not control.
  • Any redesign, layout change, or new visual direction beyond recoloring and contrast polish.

← Back to task

HiveDev, when it’s ready.

HiveDev is the hosted product being built around open-source Hive. It isn’t available yet; join the waitlist to hear when it is. We’ll use your email for HiveDev availability updates.

Read our privacy notice.