Skip to content

feat: customizable accent color#678

Merged
kmendell merged 1 commit intomainfrom
feat/accent-color
Oct 10, 2025
Merged

feat: customizable accent color#678
kmendell merged 1 commit intomainfrom
feat/accent-color

Conversation

@kmendell
Copy link
Member

@kmendell kmendell commented Oct 10, 2025

Summary by CodeRabbit

  • New Features
    • Added Accent Color customization in Settings with preset and custom options. Changes apply instantly across the UI and logos, with a sensible default.
  • UI/Style
    • Updated buttons and sidebar environment switcher to use the primary theme for a more consistent look.
    • Logos now update reactively to reflect the selected accent color.
    • Introduced improved radio group controls for a clearer selection experience.
  • Localization
    • Added translations for accent color settings and a new “Cancel” label.

@kmendell kmendell requested a review from a team as a code owner October 10, 2025 16:55
@kmendell
Copy link
Member Author

@coderabbitai review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 10, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 10, 2025

📝 Walkthrough

Walkthrough

Introduces accent color support across backend and frontend. Backend adds AccentColor setting, defaults, DTO, and passes Settings into ApplicationImagesService, which now recolors SVG logos. Frontend adds accent color picker, custom color dialog, radio group UI components, utilities to apply CSS variables, reactive logo URLs, settings schema updates, and related UI/i18n tweaks.

Changes

Cohort / File(s) Summary of changes
Service bootstrap wiring
backend/internal/bootstrap/services_bootstrap.go
Passes SettingsService into NewApplicationImagesService to match new constructor signature.
Settings model and DTO
backend/internal/models/settings.go, backend/internal/dto/settings_dto.go
Adds AccentColor setting to models.Settings and UpdateSettingsDto (JSON: accentColor).
Application images service
backend/internal/services/app_images_service.go
Adds SettingsService dependency; recolors SVG logos using AccentColor; updates constructor signature.
Settings service defaults
backend/internal/services/settings_service.go
Adds default AccentColor value in getDefaultSettings.
i18n strings
frontend/messages/en.json
Adds strings for accent color UI and a new “cancel” entry.
Accent color components
frontend/src/lib/components/accent-color/*
Adds AccentColorPicker and CustomColor dialog components for selecting predefined or custom colors.
Radio group UI primitives
frontend/src/lib/components/ui/radio-group/...
Adds radio group wrapper and item components; index re-exports RadioGroup and RadioGroupItem.
Config store and utilities
frontend/src/lib/stores/config-store.ts, frontend/src/lib/utils/accent-color-util.ts
Applies accent color via CSS variables on settings load/set; introduces applyAccentColor utility with contrast/ring computation.
Image utilities and reactive logos
frontend/src/lib/utils/image.util.ts, frontend/src/lib/components/sidebar/sidebar-logo.svelte, frontend/src/routes/auth/login/+page.svelte, frontend/src/routes/onboarding/+layout.svelte
Appends color-based cache buster to logo URLs; components derive reactive logoUrl to reflect color changes.
Settings page
frontend/src/routes/settings/general/+page.svelte
Extends form schema and UI to include accentColor; wires picker; persists value via updateSettingsConfig.
Types
frontend/src/lib/types/settings.type.ts
Adds accentColor: string to Settings type.
Styling adjustments
frontend/src/lib/components/quick-actions.svelte, frontend/src/lib/components/sidebar/sidebar-env-switcher.svelte
Switches various violet/sidebar-* classes to primary/primary-foreground for consistency with accent color.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title “feat: customizable accent color” succinctly and accurately captures the primary purpose of the changeset, which revolves around adding support for accent color customization throughout the application. It follows a conventional commit style, is clear, and avoids any extraneous noise. A teammate reviewing the history can immediately understand that this PR introduces the feature for customizing accent colors.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/accent-color

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.

Copy link
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: 2

🧹 Nitpick comments (6)
frontend/src/lib/components/accent-color/custom-color.svelte (1)

25-30: Consider optimizing color validation.

The isValidColor function creates a new DOM element on every validation call, which could impact performance during rapid input changes (e.g., typing or pasting).

Consider using CSS.supports for validation:

 function isValidColor(color: string): boolean {
-  // Create a temporary element to test if the color is valid
-  const testElement = document.createElement('div');
-  testElement.style.color = color;
-  return testElement.style.color !== '';
+  return CSS.supports('color', color);
 }

This approach is more efficient and doesn't create DOM elements. However, verify browser compatibility if targeting older browsers.

backend/internal/services/settings_service.go (1)

101-101: Enforce and document OKLCH accentColor support

  • In SettingsService.UpdateSettings (backend/internal/services/settings_service.go), validate AccentColor (e.g., via a CSS-color parser) and reject or default invalid formats before saving.
  • Document OKLCH support minimum versions: Chrome/Edge 111+, Firefox 113+, Safari 15.4+, Opera 97+, Samsung Internet recent.
  • Provide a CSS fallback (e.g., hex) for unsupported browsers in applyAccentColorToSVG or the frontend.
frontend/src/lib/components/sidebar/sidebar-logo.svelte (1)

6-6: Remove unused import.

The settingsStore import appears unused in this file. While getApplicationLogo() does use settingsStore internally, Svelte 5's $derived automatically tracks reactive dependencies without requiring explicit imports.

Apply this diff to remove the unused import:

-	import settingsStore from '$lib/stores/config-store';
frontend/src/lib/components/accent-color/accent-color-picker.svelte (1)

15-23: Consider extracting the color palette to a shared config.

The hardcoded accentColors array is fine for now, but if these colors are used elsewhere or need to be maintained centrally, consider extracting them to a separate configuration file (e.g., $lib/config/accent-colors.ts).

frontend/src/lib/utils/accent-color-util.ts (1)

45-45: Refactor the RGB value parsing for clarity.

Line 45 applies .map(Number) to the regex match array, which includes the full match at index 0 (resulting in NaN), before destructuring. This works but is inefficient and unclear.

Apply this diff for a clearer approach:

-	const [, r, g, b] = rgbMatch.map(Number);
+	const r = Number(rgbMatch[1]);
+	const g = Number(rgbMatch[2]);
+	const b = Number(rgbMatch[3]);
backend/internal/services/app_images_service.go (1)

96-96: Consider more robust color replacement.

Line 96 uses a simple string replacement that only targets fill:#6D28D9 in the SVG. If the SVG uses the color in other contexts (e.g., direct fill attributes, stroke, or class-based styles), this replacement won't catch them.

If the SVG structure is guaranteed to only use this specific style pattern, the current approach is acceptable. Otherwise, consider a more comprehensive replacement strategy, such as parsing the SVG as XML and updating all relevant color attributes.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 87f60d6 and 4f001cd.

📒 Files selected for processing (21)
  • backend/internal/bootstrap/services_bootstrap.go (1 hunks)
  • backend/internal/dto/settings_dto.go (1 hunks)
  • backend/internal/models/settings.go (1 hunks)
  • backend/internal/services/app_images_service.go (2 hunks)
  • backend/internal/services/settings_service.go (1 hunks)
  • frontend/messages/en.json (2 hunks)
  • frontend/src/lib/components/accent-color/accent-color-picker.svelte (1 hunks)
  • frontend/src/lib/components/accent-color/custom-color.svelte (1 hunks)
  • frontend/src/lib/components/quick-actions.svelte (1 hunks)
  • frontend/src/lib/components/sidebar/sidebar-env-switcher.svelte (3 hunks)
  • frontend/src/lib/components/sidebar/sidebar-logo.svelte (2 hunks)
  • frontend/src/lib/components/ui/radio-group/index.ts (1 hunks)
  • frontend/src/lib/components/ui/radio-group/radio-group-item.svelte (1 hunks)
  • frontend/src/lib/components/ui/radio-group/radio-group.svelte (1 hunks)
  • frontend/src/lib/stores/config-store.ts (1 hunks)
  • frontend/src/lib/types/settings.type.ts (1 hunks)
  • frontend/src/lib/utils/accent-color-util.ts (1 hunks)
  • frontend/src/lib/utils/image.util.ts (1 hunks)
  • frontend/src/routes/auth/login/+page.svelte (2 hunks)
  • frontend/src/routes/onboarding/+layout.svelte (2 hunks)
  • frontend/src/routes/settings/general/+page.svelte (5 hunks)
⏰ 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: test
  • GitHub Check: build
  • GitHub Check: Run Golangci-lint
  • GitHub Check: Analyze (go)
🔇 Additional comments (35)
backend/internal/models/settings.go (1)

58-58: LGTM!

The AccentColor field is properly structured and follows the established pattern for public settings fields.

frontend/messages/en.json (2)

4-8: LGTM!

The translation keys for the accent color feature are well-structured and appropriately descriptive.


1265-1266: No duplicate translation keys. cancel and common_cancel are distinct entries sharing the same English value by design; no change needed.

Likely an incorrect or invalid review comment.

backend/internal/dto/settings_dto.go (1)

27-27: LGTM!

The AccentColor field is correctly added to the DTO, following the established pattern with optional pointer type and omitempty tag.

frontend/src/lib/components/accent-color/custom-color.svelte (1)

1-75: Well-structured component with good UX.

The component properly handles validation, state management, and provides good user feedback with the color swatch preview.

frontend/src/lib/components/sidebar/sidebar-env-switcher.svelte (1)

92-92: LGTM! Styling aligns with accent color system.

The changes from sidebar-primary to primary classes enable the environment switcher to use the customizable accent color. The implementation is consistent throughout the component.

Also applies to: 108-108, 160-167

frontend/src/lib/types/settings.type.ts (1)

23-23: LGTM!

The accentColor field is properly added to the Settings type and aligns with the backend implementation.

frontend/src/lib/stores/config-store.ts (1)

3-3: Excellent refactoring for centralized accent color management.

The changes ensure that accent color is consistently applied whenever settings are loaded or updated. The refactoring to call set() from reload() eliminates code duplication.

Also applies to: 8-16

frontend/src/routes/onboarding/+layout.svelte (1)

13-14: LGTM! Proper reactive binding for accent color support.

The implementation correctly uses Svelte 5's $derived to make the logo URL reactive to accent color changes. The binding is clean and follows the pattern consistently applied across other route files.

Also applies to: 31-31

frontend/src/routes/auth/login/+page.svelte (1)

27-28: LGTM! Consistent reactive logo implementation.

The reactive logoUrl implementation matches the pattern used in other routes, ensuring the login page logo responds to accent color changes.

Also applies to: 209-209

frontend/src/lib/components/quick-actions.svelte (1)

112-112: LGTM! Theme color migration to primary accent.

The styling changes correctly update the refresh button from hardcoded violet colors to the primary theme color, aligning with the new accent color system.

Also applies to: 118-118, 120-120

frontend/src/lib/components/sidebar/sidebar-logo.svelte (1)

10-11: LGTM! Proper reactive logo with collapsed state handling.

The implementation correctly makes the logo reactive to accent color changes while also respecting the sidebar's collapsed state.

Also applies to: 27-27

backend/internal/bootstrap/services_bootstrap.go (1)

44-44: LGTM! Proper dependency injection for settings.

The ApplicationImagesService initialization correctly receives the settings service, maintaining proper dependency order since Settings is initialized earlier in the bootstrap sequence.

frontend/src/lib/utils/image.util.ts (1)

8-18: LGTM! Correct accent color integration with cache busting.

The implementation properly:

  • Reads accent color from settings with a sensible default
  • Constructs the URL with correct query parameter separators
  • Uses encodeURIComponent for safety
  • Integrates with the existing cache busting mechanism

The use of get(settingsStore) is appropriate here since this function is called from $derived contexts in components, ensuring efficient reactivity.

frontend/src/lib/components/ui/radio-group/radio-group.svelte (1)

1-19: LGTM! Well-structured radio group wrapper.

The component follows best practices for Svelte 5:

  • Correct use of $bindable for two-way binding
  • Proper prop spreading with ...restProps
  • Uses cn utility for class merging
  • Includes data-slot attribute for styling customization
frontend/src/lib/components/ui/radio-group/index.ts (1)

1-10: LGTM! Standard barrel export pattern.

The exports properly expose both internal component names and user-facing aliases, providing a clean public API while maintaining flexibility.

frontend/src/lib/components/ui/radio-group/radio-group-item.svelte (2)

1-11: LGTM!

The script section is well-structured with proper imports, type annotations, and Svelte 5 runes syntax for reactive props.


13-31: LGTM!

The template implementation is correct. The conditional rendering of the checked indicator using Svelte 5 snippets is idiomatic, and the styling is appropriate for a radio button.

frontend/src/lib/components/accent-color/accent-color-picker.svelte (3)

29-32: LGTM!

The handler correctly updates the selected color and applies it via the utility function.


44-50: LGTM!

The conditional rendering logic correctly displays predefined colors, a custom color swatch if applicable, and a button to open the custom color dialog.


79-89: LGTM!

The conditional rendering of the Plus icon for custom color selection and Check icon for selected items is implemented correctly.

frontend/src/routes/settings/general/+page.svelte (7)

12-12: LGTM!

The new imports for accent color functionality are appropriate.

Also applies to: 18-19


22-25: LGTM!

Proper initialization with a fallback to 'default' ensures accentColor is always defined.


31-36: LGTM!

The form schema correctly includes accentColor as a string. The validation is appropriate for this use case.


40-46: LGTM!

Change detection correctly includes accentColor alongside other form fields.


56-66: LGTM!

The updateSettingsConfig function properly handles settings updates with proper error handling.


89-89: LGTM!

The reset function correctly includes accentColor.


155-171: LGTM!

The accent color picker UI is properly integrated with correct prop bindings and respects the read-only state.

frontend/src/lib/utils/accent-color-util.ts (3)

1-20: LGTM!

The applyAccentColor function correctly handles both the default case (by removing properties) and custom colors (by setting CSS variables with proper foreground contrast and ring colors).


22-27: LGTM!

The contrast calculation uses an appropriate threshold and returns suitable foreground colors in the oklch format.


47-57: LGTM!

The WCAG relative luminance calculation is correctly implemented following the standard formula.

backend/internal/services/app_images_service.go (4)

15-19: LGTM!

Adding the settingsService field is appropriate for retrieving the accent color configuration.


21-26: LGTM!

The constructor properly accepts and initializes the settingsService dependency.


61-77: LGTM!

The refactored GetImage method with early unlock is a good improvement. The conditional SVG color application for logo images is implemented correctly.


79-99: Add fallback for oklch() accent color in SVG

  • oklch() works only in modern browsers (Chrome ≥116, Edge ≥116, Firefox ≥113, Safari ≥16.2); older versions and some inline-SVG contexts may ignore it.
  • Provide an sRGB fallback (e.g. hex/rgb equivalent) or wrap the oklch() fill in a @supports(color: oklch(...)) (or @media (color-gamut: p3)) rule and test across your target browser matrix.

@kmendell kmendell merged commit 2ef033f into main Oct 10, 2025
13 checks passed
@kmendell kmendell deleted the feat/accent-color branch October 10, 2025 17:32
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