Skip to content

feat: add dark-light mode support for default theme#1120

Open
Robert27 wants to merge 3 commits intosysadminsmedia:mainfrom
Robert27:feat/autotheme
Open

feat: add dark-light mode support for default theme#1120
Robert27 wants to merge 3 commits intosysadminsmedia:mainfrom
Robert27:feat/autotheme

Conversation

@Robert27
Copy link
Copy Markdown
Contributor

@Robert27 Robert27 commented Dec 9, 2025

What type of PR is this?

  • feature

What this PR does / why we need it:

This PR adds dark/light mode support to the homebox theme with a 3-way toggle (System, Light, Dark) in the sidebar footer.

image image

Changes made:

  • frontend/composables/use-theme.ts: Fixed FIXME by implementing proper theme class tracking (removed inefficient hack)
  • frontend/components/App/DarkModeToggle.vue: Added localized 3-way toggle with "System" label, fixed button text to show current selection
  • frontend/layouts/default.vue: Moved toggle to sidebar footer, only shows for homebox theme
  • frontend/assets/css/main.css: Updated homebox light mode (brighter background, lighter boxes) and dark mode (darker, neutral colors)
  • frontend/components/ui/sidebar/Sidebar.vue: Added right border for better visual separation
  • frontend/public/set-theme.js: Updated to properly handle homebox theme dark mode
  • frontend/locales/en.json: Added translation keys for dark mode options

Which issue(s) this PR fixes:

See #1117

Special notes for your reviewer:

  • Dark mode only works for homebox theme (as intended)
  • FIXME comment properly resolved
  • Localization keys need to be added to other language files

Testing

(fill-in or delete this section)

Manual testing:

  • Verified toggle appears in sidebar footer only for homebox theme
  • Tested all three modes (System, Light, Dark) - all work correctly
  • Verified theme persists across page reloads
  • Confirmed dark mode only applies to homebox theme
  • Verified button text shows current selection correctly

Code verification:

  • All code compiles successfully
  • No linter errors
  • FIXME properly resolved

Summary by CodeRabbit

Release Notes

  • New Features

    • Added a dark mode toggle to the sidebar footer with three options: Auto (system preference), Light, and Dark
    • Dark mode styling now available for the Homebox theme
  • Style

    • Updated color scheme for Homebox theme with improved contrast and visual hierarchy in dark mode

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Dec 9, 2025

Walkthrough

This PR introduces dark mode support for the homebox theme by extending the theme composable with a DarkModePreference type and setDarkMode function, creating a new DarkModeToggle component, updating CSS variables for light/dark color schemes, and integrating the toggle into the sidebar footer.

Changes

Cohort / File(s) Summary
Theme & Preference System
frontend/composables/use-preferences.ts, frontend/composables/use-theme.ts
Added new DarkModePreference type ("auto" | "light" | "dark"), extended LocationViewPreferences with darkMode field, expanded UseTheme API with darkMode computed ref and setDarkMode function, introduced system dark mode detection via media queries, and implemented DOM lifecycle management for theme classes and dark-mode attributes.
Dark Mode UI Component
frontend/components/App/DarkModeToggle.vue
New Vue SFC component exposing a dropdown menu for selecting dark mode preference (auto/light/dark) with localized labels and icons, delegating theme updates to useTheme composable.
Styling & Layout
frontend/assets/css/main.css, frontend/components/ui/sidebar/Sidebar.vue, frontend/layouts/default.vue
Adjusted CSS custom properties in :root and .homebox for lighter defaults, added dark-mode color scheme block for homebox theme, reordered mobile-sheet classes in sidebar, added persistent right border to sidebar, and integrated DarkModeToggle into sidebar footer for homebox theme only.
Localization
frontend/locales/en.json
Added four new menu keys: dark_mode, dark_mode_dark, dark_mode_light, dark_mode_system with English display strings.
Initialization & Bootstrap
frontend/app.vue, frontend/public/set-theme.js
Added conditional homebox class binding to Html element, enhanced set-theme.js to read full preferences object from localStorage and apply theme-specific dark mode handling on page load.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20–25 minutes

Areas requiring extra attention:

  • frontend/composables/use-theme.ts — Core logic for dark mode preference application, system media query integration, DOM class/attribute lifecycle, and preference reactivity; verify correctness of auto/light/dark mode branching and watcher synchronization
  • frontend/assets/css/main.css — Ensure new dark-mode color scheme variables are correctly scoped to homebox + dark context and don't leak into other themes
  • frontend/public/set-theme.js — Critical bootstrap script; verify localStorage parsing handles missing/malformed preferences gracefully and dark mode is only applied for homebox theme

Possibly related PRs

Suggested labels

⬆️ enhancement

Suggested reviewers

  • tankerkiller125
  • katosdev

Security Recommendations

  • localStorage usage: Ensure the preferences object stored in localStorage contains only non-sensitive data (theme, dark mode setting); avoid storing authentication tokens, API keys, or PII in preferences.
  • DOM manipulation: The DarkModeToggle and applyDarkMode logic directly set class names and data attributes on the HTML element. Verify that all values written to the DOM (e.g., theme class names, data-dark-mode attribute values) come from controlled, hardcoded lists (like darkModeOptions) and cannot be influenced by user input or external data to prevent XSS.
  • CSS custom properties: Review the CSS custom property injection in main.css to ensure no untrusted content can flow into style values; current implementation uses static variable definitions, which is safe.

Poem

🌙✨ Dark mode blooms, a toggle takes the stage,
Homebox theme now wears both light and shadow's cage,
From auto to choice, the preference takes flight,
Where DOM dances with classes, day yields to night! 🌗

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Title check ⚠️ Warning The title 'feat: add dark-light mode support for default theme' is partially related but misleading—it claims 'default theme' when the PR actually implements dark mode exclusively for the 'homebox' theme. Update title to 'feat: add dark/light mode support for homebox theme' to accurately reflect that dark mode is limited to the homebox theme only.
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (1 passed)
Check name Status Explanation
Description check ✅ Passed The PR description is well-structured with all required sections completed: type (feature), purpose with visual examples, comprehensive file-by-file changes, linked issue (#1117), reviewer notes, and testing details.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@Robert27 Robert27 changed the title feat: add dark-light mode support for homebox theme feat: add dark-light mode support for default theme Dec 9, 2025
@coderabbitai coderabbitai bot added the ⬆️ enhancement New feature or request label Dec 9, 2025
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
frontend/public/set-theme.js (1)

3-6: Consider validating the preferences structure and theme value.

The parsed preferences object could be malformed or tampered with in localStorage. While this is a low-risk scenario (self-XSS), validating the structure adds robustness:

  1. preferences.theme could be any string, not just valid DaisyTheme values
  2. preferences.darkMode falls back to "auto" which is good

For defense in depth, consider validating that theme is one of the expected values before applying it to the DOM.

  const preferences = JSON.parse(localStorage.getItem("homebox/preferences/location"));
  if (preferences) {
    const theme = preferences.theme;
    const darkMode = preferences.darkMode || "auto";
+   
+   // Basic validation - only apply known themes
+   const validThemes = ["homebox", "light", "dark", "cupcake", "bumblebee", "emerald", "corporate", "synthwave", "retro", "cyberpunk", "valentine", "halloween", "garden", "forest", "aqua", "lofi", "pastel", "fantasy", "wireframe", "black", "luxury", "dracula", "cmyk", "autumn", "business", "acid", "lemonade", "night", "coffee", "winter"];
+   if (!validThemes.includes(theme)) {
+     console.warn("Invalid theme in preferences:", theme);
+     return;
+   }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4334f92 and e3182bd.

📒 Files selected for processing (9)
  • frontend/app.vue (1 hunks)
  • frontend/assets/css/main.css (1 hunks)
  • frontend/components/App/DarkModeToggle.vue (1 hunks)
  • frontend/components/ui/sidebar/Sidebar.vue (2 hunks)
  • frontend/composables/use-preferences.ts (2 hunks)
  • frontend/composables/use-theme.ts (3 hunks)
  • frontend/layouts/default.vue (2 hunks)
  • frontend/locales/en.json (1 hunks)
  • frontend/public/set-theme.js (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,vue}

⚙️ CodeRabbit configuration file

**/*.{ts,vue}: Check for hardcoded strings in UI components that should be translatable.
Look for:

  • String literals in Vue components (e.g. Click me)
  • Alert messages, error messages, and user-facing text
  • Placeholder text and labels

Files:

  • frontend/composables/use-preferences.ts
  • frontend/layouts/default.vue
  • frontend/components/App/DarkModeToggle.vue
  • frontend/app.vue
  • frontend/composables/use-theme.ts
  • frontend/components/ui/sidebar/Sidebar.vue
🧬 Code graph analysis (1)
frontend/composables/use-preferences.ts (1)
frontend/lib/data/themes.ts (1)
  • DaisyTheme (1-31)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Backend Server Tests / Go
  • GitHub Check: build (linux/arm/v7)
  • GitHub Check: build (linux/amd64)
  • GitHub Check: build (linux/arm64)
🔇 Additional comments (19)
frontend/composables/use-preferences.ts (2)

14-15: LGTM! Clean type definition and preference integration.

The DarkModePreference type is well-defined with appropriate literal union values. The integration into LocationViewPreferences follows the existing pattern.

Also applies to: 22-22


52-52: Good default choice.

Defaulting to "auto" respects user system preferences and provides the expected behavior out of the box.

frontend/locales/en.json (1)

612-615: LGTM! Translation keys properly added.

The keys follow the existing menu.* naming convention. As noted in the PR, these keys need to be added to other language files for complete i18n support.

frontend/composables/use-theme.ts (5)

19-21: Good use of VueUse's reactive media query.

The currentThemeClass tracking pattern prevents CSS class accumulation when switching themes.


23-47: Clean DOM manipulation with proper class tracking.

The function correctly manages theme classes by tracking the current class and removing it before applying the new one, preventing class accumulation.


60-90: LGTM! Dark mode logic is well-structured.

The function correctly:

  • Restricts dark mode to the homebox theme
  • Handles all three preference modes (auto/light/dark)
  • Syncs with system preference when in auto mode

113-124: Well-scoped watchers for preference changes.

The system dark mode watcher correctly only triggers applyDarkMode when the preference is "auto", avoiding unnecessary DOM updates.


126-134: Guard prevents redundant theme applications.

The check themeRef.value !== newTheme avoids unnecessary DOM updates when the theme is already applied.

frontend/public/set-theme.js (2)

19-40: Logic duplication with use-theme.ts - keep in sync.

The dark mode logic here mirrors applyDarkMode in use-theme.ts. This duplication is acceptable for FOUC prevention, but changes to one must be reflected in the other to maintain consistency.


43-45: Appropriate error handling for a pre-hydration script.

Silent failure with console logging is correct here - theme initialization should not break the application.

frontend/app.vue (1)

8-8: Potential duplication of homebox class management in template binding.

The :class binding on line 8 that conditionally applies the homebox class may duplicate logic from applyThemeToDOM in use-theme.ts (lines 39-43). If both mechanisms manage the same class, this could introduce race conditions during hydration or cause redundant class operations.

Verify whether:

  • The template binding is necessary for SSR initial render correctness
  • applyThemeToDOM alone is sufficient without the template-level class binding
frontend/layouts/default.vue (2)

93-113: LGTM! Clean integration of dark mode toggle.

The conditional rendering of the DarkModeToggle component is properly scoped to the homebox theme, and the refactored footer structure maintains consistency with the existing sidebar component patterns. All user-facing strings are properly internationalized using $t().


238-238: LGTM! Import statement is correct.

The DarkModeToggle component is properly imported and used in the template.

frontend/assets/css/main.css (2)

8-37: LGTM! Updated homebox light mode colors.

The adjusted color values provide a brighter, lighter appearance for the homebox theme in light mode. The changes maintain consistency across all color tokens.


39-72: LGTM! Well-structured dark mode variant.

The dark mode implementation is properly scoped to the homebox theme using dual selectors (data-dark-mode="dark" and .dark class). The color palette uses neutral, darker tones with appropriate contrast ratios. The comprehensive set of CSS custom properties ensures all UI elements are styled correctly in dark mode.

frontend/components/App/DarkModeToggle.vue (2)

1-49: LGTM! Excellent internationalization and type safety.

The component properly uses useI18n and defines all user-facing labels as translation functions. The use of TypeScript types (DarkModePreference, Component) ensures type safety, and the computed currentOption with optional chaining provides defensive fallback handling.


51-72: LGTM! Template correctly uses translations and reactive values.

All user-facing text is properly internationalized using $t() and the label functions. The active option highlighting (line 64) correctly compares the reactive darkMode value, which auto-unwraps in Vue 3 templates.

frontend/components/ui/sidebar/Sidebar.vue (2)

34-34: LGTM! Class reordering for consistency.

The classes have been reordered without any functional changes. This maintains the same styling while improving code organization.


82-84: LGTM! Added visual separation with border.

The addition of border-r border-sidebar-border provides the right border for visual separation mentioned in the PR objectives. The styling properly uses the CSS custom property for theme consistency.

@tturnerdev
Copy link
Copy Markdown
Contributor

I pulled this and tested locally. LGTM, didn't see any issues and love this feature. Would be great to get it merged in!

@tonyaellie
Copy link
Copy Markdown
Collaborator

Currently we are avoiding merging non bug fix changes until we get the 0.22 release done (so as to try to prevent us from introducing new bugs)

@tturnerdev
Copy link
Copy Markdown
Contributor

Currently we are avoiding merging non bug fix changes until we get the 0.22 release done (so as to try to prevent us from introducing new bugs)

Awesome, sounds good. Thanks for your work on Homebox, really appreciate it!

@tonyaellie
Copy link
Copy Markdown
Collaborator

Sorry for the delay in getting to this.

Overall I do quite like the dark design and it makes sense to me, my hesitation with merging this is if we do overhaul the themes system (which is something I think very much needs doing) then we may be adding more stuff that will either need backwards compat or people will be annoyed is missing.

@katosdev
Copy link
Copy Markdown
Contributor

Sorry for the delay in getting to this.

Overall I do quite like the dark design and it makes sense to me, my hesitation with merging this is if we do overhaul the themes system (which is something I think very much needs doing) then we may be adding more stuff that will either need backwards compat or people will be annoyed is missing.

I tend to agree with @tonyaellie here.
I’m afraid for now I’d probably feature-freeze this for now until the themes system gets an overhaul.

Thoughts please @tankerkiller125 ?

@tturnerdev
Copy link
Copy Markdown
Contributor

I tend to agree with @tonyaellie here. I’m afraid for now I’d probably feature-freeze this for now until the themes system gets an overhaul.

Thoughts please @tankerkiller125 ?

Unfortunate, but understandable for sure. Thanks for taking the time to review.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

⬆️ enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants