Skip to content

feat: add theme change button - #119

Open
kingazm wants to merge 1 commit into
mainfrom
theme-change-button
Open

feat: add theme change button#119
kingazm wants to merge 1 commit into
mainfrom
theme-change-button

Conversation

@kingazm

@kingazm kingazm commented May 10, 2026

Copy link
Copy Markdown
Collaborator

📄 Pull Request Description


🧩 What was changed?

Added a light/dark theme toggle button to the top bar, placed directly to the left of the existing contrast slider button.

  • app/components/shared/top_bar.tsx - new toggle button + isDark state and effect
  • app/tailwind_styles.css - switched dark mode from media-query to class-based
  • app/root.tsx - added inline script for flash-free theme initialization
  • app/i18n.ts - added themeToggle translation key (EN + PL)

💡 Why was it changed?

Previously, dark mode was controlled entirely by the OS/browser prefers-color-scheme setting with
no way to override it from within the app. Users who want dark mode on a light-mode system (or
vice versa) had no way to switch. This brings the theme control in-app, consistent with how the
contrast and language settings already work. In addition, this was also project's supervisor suggestion.


⚙️ How was it implemented?

  • isDark is initialised from localStorage('jucaneat-theme'), falling back to
    window.matchMedia('(prefers-color-scheme: dark)') so first-time visitors get the theme
    automatically.
  • A useEffect toggles the dark class on and persists the choice to localStorage
    on every change.
  • Flash prevention: An inline <script> in root.tsx runs synchronously before the renders,
    applying the dark class from storage before React hydrates. No flash of wrong theme on hard
    reload.
  • Added tailwind @variant dark (&:where(.dark, .dark *)); to tailwind_styles.css to switch from
    the default @media (prefers-color-scheme) variant to a class-based one, so the button actually
    controls all dark: utility classes.
  • Button: Uses the same xsm size and border/bg styling as the EN/PL and contrast buttons. Shows
    Moon icon in light mode and Sun icon in dark mode (lucide-react, already a project dependency).

⚠️ Side Effects or Risks

  • Behaviour change for existing users: Dark mode now requires the dark class on instead of
    the OS media query. On first load, the inline script mirrors the system preference, so existing
    users will see no change, but if localStorage ever has a stale value it will override the system
    setting.
  • Classes in the app now respond to the class rather than the media query. Any component
    that relied on automatic OS-driven dark mode will now only switch when the user explicitly
    toggles or when the inline script fires on load. This is intentional but is a global behaviour
    change.

✅ Checklist

  • All 4 sections above are clearly filled out
  • Tests and documentation updated N/A, tested manually

@kingazm
kingazm requested a review from cssma May 10, 2026 17:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant