Add customizable theme color#582
Conversation
|
@sourcery-ai review |
|
@greptileai review |
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
Reviewer's GuideAdds a customizable accent color system and Appearance settings UI, refactors theme binding and logo usage to be accent-aware, and updates global styles to derive surfaces and effects from accent variables instead of hard-coded purple values. Sequence diagram for selecting a custom accent colorsequenceDiagram
actor User
participant AppearanceSettingsComponent
participant ThemeService
participant LocalStorage
participant DOMRoot as DocumentElement
User->>AppearanceSettingsComponent: Change color input
AppearanceSettingsComponent->>AppearanceSettingsComponent: onCustomColorChange(event)
AppearanceSettingsComponent->>ThemeService: setCustomAccent(hex)
ThemeService->>ThemeService: _customAccent.set(hex)
ThemeService->>LocalStorage: setItem(CUSTOM_ACCENT_KEY, hex)
alt accent is not custom
ThemeService->>ThemeService: setAccent("custom")
ThemeService->>LocalStorage: setItem(ACCENT_KEY, "custom")
end
Note over ThemeService,DOMRoot: Angular signal effects already bound in bindToDom
ThemeService-->>DOMRoot: effect data-accent="custom"
ThemeService-->>DOMRoot: effect applyCustomAccent(hex)
ThemeService->>DOMRoot: set CSS vars --brand-50..950, --accent-rgb
Class diagram for customizable theme color and appearance settingsclassDiagram
direction LR
class ThemeService {
<<injectable>>
- _theme: Theme
- _performanceMode: boolean
- _fullWidth: boolean
- _accent: Accent
- _customAccent: string
- static THEME_KEY: string
- static PERFORMANCE_MODE_KEY: string
- static FULL_WIDTH_KEY: string
- static ACCENT_KEY: string
- static CUSTOM_ACCENT_KEY: string
- static DEFAULT_CUSTOM_ACCENT: string
- static BRAND_SHADES: readonly number[]
- static LIGHTNESS_STOPS: Record<number, number>
+ theme: Signal<Theme>
+ performanceMode: Signal<boolean>
+ fullWidth: Signal<boolean>
+ accent: Signal<Accent>
+ customAccent: Signal<string>
+ constructor()
+ setTheme(theme: Theme): void
+ setPerformanceMode(value: boolean): void
+ setFullWidth(value: boolean): void
+ setAccent(accent: Accent): void
+ setCustomAccent(hex: string): void
- restoreFromStorage(): void
- detectSystemPreferences(): void
- bindToDom(): void
- isAccent(value: string): boolean
- applyCustomAccent(hex: string): void
- clearInlineAccent(): void
}
class AppearanceSettingsComponent {
<<standalone component>>
- themeService: ThemeService
+ theme: Signal<Theme>
+ accent: Signal<Accent>
+ customAccent: Signal<string>
+ presetSwatches: AccentSwatch[]
+ constructor()
+ selectTheme(theme: Theme): void
+ selectAccent(accent: Accent): void
+ onCustomColorChange(event: Event): void
}
class AccentSwatch {
<<interface>>
+ value: Accent
+ label: string
+ color: string
}
class LogoComponent {
<<standalone component>>
+ ariaLabel: Input<string>
}
class NavSidebarComponent {
<<standalone component>>
- hub: AppHubService
- auth: AuthService
- themeService: ThemeService
+ theme: Signal<Theme>
+ collapsed: Input<boolean>
+ mobileOpen: Input<boolean>
+ themedIconSrc(src: string): string
+ logout(): void
}
class NotificationsComponent {
<<component>>
- api: NotificationApi
- toast: ToastService
- confirmService: ConfirmService
- themeService: ThemeService
+ theme: Signal<Theme>
+ themedIconSrc(src: string): string
}
class Theme {
<<type alias>>
+ dark
+ light
}
class Accent {
<<type alias>>
+ default
+ blue
+ green
+ rose
+ amber
+ teal
+ custom
}
class AccentPreset {
<<const union>>
+ default
+ blue
+ green
+ rose
+ amber
+ teal
}
ThemeService --> Theme : uses
ThemeService --> Accent : manages
ThemeService --> AccentPreset : presets
AppearanceSettingsComponent --> ThemeService : injects
AppearanceSettingsComponent --> Accent : selects
AppearanceSettingsComponent --> AccentSwatch : builds
NavSidebarComponent --> ThemeService : injects
NavSidebarComponent --> LogoComponent : uses
NotificationsComponent --> ThemeService : injects
Accent <|-- AccentPreset
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
Hey - I've found 1 issue, and left some high level feedback:
- The
themedIconSrchelper relies on filenames ending in-light.svgand a simple string replacement to derive dark variants; consider either handling missing/other suffixes more defensively or passing in an explicit light/dark pair to avoid brittle coupling to file naming conventions. - Accent preset base colors are defined both in
_accents.scssand in thePRESET_SWATCHESmap inappearance-settings.component.ts; pulling these from a single source (or generating the swatch colors from CSS variables) would reduce the risk of the values drifting out of sync. - In
ThemeService.applyCustomAccent, you recomputedocument.documentElementinstead of reusing the cachedrootelement frombindToDom; capturing and reusing a single reference would slightly simplify the code and avoid repeated DOM lookups.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- The `themedIconSrc` helper relies on filenames ending in `-light.svg` and a simple string replacement to derive dark variants; consider either handling missing/other suffixes more defensively or passing in an explicit light/dark pair to avoid brittle coupling to file naming conventions.
- Accent preset base colors are defined both in `_accents.scss` and in the `PRESET_SWATCHES` map in `appearance-settings.component.ts`; pulling these from a single source (or generating the swatch colors from CSS variables) would reduce the risk of the values drifting out of sync.
- In `ThemeService.applyCustomAccent`, you recompute `document.documentElement` instead of reusing the cached `root` element from `bindToDom`; capturing and reusing a single reference would slightly simplify the code and avoid repeated DOM lookups.
## Individual Comments
### Comment 1
<location path="code/frontend/src/app/core/services/theme.service.ts" line_range="98-101" />
<code_context>
+ localStorage.setItem(ACCENT_KEY, accent);
+ }
+
+ setCustomAccent(hex: string): void {
+ this._customAccent.set(hex);
+ localStorage.setItem(CUSTOM_ACCENT_KEY, hex);
+ if (this._accent() !== 'custom') {
+ this.setAccent('custom');
+ }
</code_context>
<issue_to_address>
**suggestion:** Consider validating custom accent values on write, not only on restore.
`setCustomAccent` stores any hex string without validation, while `restoreFromStorage` / `applyCustomAccent` validate via `hexToRgb`/regex. This can leave invalid values in localStorage that are never applied and give no feedback to the user. Please validate in `setCustomAccent` (e.g. reject/normalise invalid values) or fall back to `DEFAULT_CUSTOM_ACCENT` on failure so storage and runtime stay consistent.
Suggested implementation:
```typescript
setCustomAccent(hex: string): void {
const candidate = hex.trim();
const isValid = this.hexToRgb(candidate) !== null;
const valueToStore = isValid ? candidate : DEFAULT_CUSTOM_ACCENT;
this._customAccent.set(valueToStore);
localStorage.setItem(CUSTOM_ACCENT_KEY, valueToStore);
if (this._accent() !== 'custom') {
```
This change assumes:
1. `this.hexToRgb` is already defined on `ThemeService` and returns `null` (or a falsy value) when the hex string is invalid, as implied by its use in `restoreFromStorage` / `applyCustomAccent`.
2. `DEFAULT_CUSTOM_ACCENT` is already defined in this file and matches the default that `restoreFromStorage` uses.
If `hexToRgb` has a different signature or behavior (e.g. throws instead of returning `null` on invalid input), you should adapt the `isValid` check accordingly (e.g. wrap the call in a try/catch and treat exceptions as invalid).
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
| setCustomAccent(hex: string): void { | ||
| this._customAccent.set(hex); | ||
| localStorage.setItem(CUSTOM_ACCENT_KEY, hex); | ||
| if (this._accent() !== 'custom') { |
There was a problem hiding this comment.
suggestion: Consider validating custom accent values on write, not only on restore.
setCustomAccent stores any hex string without validation, while restoreFromStorage / applyCustomAccent validate via hexToRgb/regex. This can leave invalid values in localStorage that are never applied and give no feedback to the user. Please validate in setCustomAccent (e.g. reject/normalise invalid values) or fall back to DEFAULT_CUSTOM_ACCENT on failure so storage and runtime stay consistent.
Suggested implementation:
setCustomAccent(hex: string): void {
const candidate = hex.trim();
const isValid = this.hexToRgb(candidate) !== null;
const valueToStore = isValid ? candidate : DEFAULT_CUSTOM_ACCENT;
this._customAccent.set(valueToStore);
localStorage.setItem(CUSTOM_ACCENT_KEY, valueToStore);
if (this._accent() !== 'custom') {This change assumes:
this.hexToRgbis already defined onThemeServiceand returnsnull(or a falsy value) when the hex string is invalid, as implied by its use inrestoreFromStorage/applyCustomAccent.DEFAULT_CUSTOM_ACCENTis already defined in this file and matches the default thatrestoreFromStorageuses.
If hexToRgb has a different signature or behavior (e.g. throws instead of returning null on invalid input), you should adapt the isValid check accordingly (e.g. wrap the call in a try/catch and treat exceptions as invalid).
Greptile SummaryThis PR adds a customisable accent-color system to the frontend: six CSS presets (default, blue, green, rose, amber, teal) driven by Confidence Score: 5/5Safe to merge; only P2 findings remain after previous review threads addressed the main concerns. No P0 or P1 issues found. The two P2 findings are a deliberate design departure from a CLAUDE.md gotcha (light-theme sidebar is now light-tinted instead of dark-purple) and a minor floating-point drift between --accent-rgb and --brand-500 for custom colors. code/frontend/src/styles/_themes.scss — light-theme sidebar change should be verified against the documented design intent in CLAUDE.md Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
U([User picks accent]) --> AP[AppearanceSettingsComponent]
AP -->|selectAccent preset| SA[ThemeService.setAccent]
AP -->|onCustomColorChange| SC[ThemeService.setCustomAccent]
SA --> AS[_accent signal]
SC --> CS[_customAccent signal]
SC -->|if not already custom| SA
AS --> EFF{effect — bindToDom}
CS --> EFF
EFF -->|preset| ATTR[setAttribute data-accent = preset]
EFF -->|preset| CLR[clearInlineAccent — remove inline --brand-* and --accent-rgb]
EFF -->|custom| ATTR2[setAttribute data-accent = custom]
EFF -->|custom| APL[applyCustomAccent]
APL --> HSL[hex → RGB → HSL]
HSL --> SCALE[generate --brand-50..950 via LIGHTNESS_STOPS]
SCALE --> DOM[write CSS custom props to documentElement.style]
APL --> RGB[write --accent-rgb]
ATTR --> CSS[_accents.scss preset block applies]
CLR --> CSS2[_tokens.scss root values restored]
Reviews (2): Last reviewed commit: "extract themedIconSrc helper to theme se..." | Re-trigger Greptile |
📝 WalkthroughWalkthroughAdds runtime accent presets and custom accent support, a new Appearance settings UI, a reusable Logo component, theme-aware icon handling, and replaces hardcoded purple values with a theme-driven Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~55 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (2)
code/frontend/src/app/ui/modal/modal.component.scss (1)
31-31: Consider fully tokenizing the modal border gradient.One stop is now theme-driven, but the other tinted stop is still fixed blue. Making both stops theme-based will keep modal chroma consistent across custom accents.
Proposed tweak
- background: linear-gradient(135deg, rgba(var(--accent-rgb), 0.3), transparent 50%, rgba(59, 130, 246, 0.2)); + background: linear-gradient(135deg, rgba(var(--accent-rgb), 0.3), transparent 50%, rgba(var(--accent-rgb), 0.2));🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@code/frontend/src/app/ui/modal/modal.component.scss` at line 31, The modal gradient currently uses a theme token for the first stop (rgba(var(--accent-rgb), 0.3)) but a hard-coded blue for the second stop (rgba(59, 130, 246, 0.2)); update the background declaration in modal.component.scss to use a theme-driven token for the second stop as well (e.g., use a dedicated CSS variable like --accent-tint-rgb or reuse --accent-rgb with a different alpha) and provide a sensible fallback so both gradient stops are driven by theme values and keep modal chroma consistent across custom accents.code/frontend/src/app/features/settings/notifications/notifications.component.ts (1)
739-741: Theming helper implementation looks correct, but there's code duplication.The
themedIconSrcmethod logic is correct for swapping icon variants based on theme. However, this exact method is duplicated inNavSidebarComponent. Consider extracting this to a shared utility or theThemeServiceitself to avoid duplication.♻️ Optional: Extract to ThemeService
// In ThemeService themedIconSrc(src: string): string { return this._theme() === 'dark' ? src : src.replace('-light.svg', '-dark.svg'); }Then in components:
-themedIconSrc(src: string): string { - return this.theme() === 'dark' ? src : src.replace('-light.svg', '-dark.svg'); -} +// Use this.themeService.themedIconSrc(src) in template🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@code/frontend/src/app/features/settings/notifications/notifications.component.ts` around lines 739 - 741, The themedIconSrc implementation is duplicated between NotificationsComponent and NavSidebarComponent; extract it to a single shared place (preferably ThemeService or a small util) and have both components call that single method instead of their own copies. Specifically, move the logic from themedIconSrc(src: string) into ThemeService (e.g., ThemeService.themedIconSrc or export a util function), update NotificationsComponent and NavSidebarComponent to import and call the new method, and remove the duplicated themedIconSrc implementations; ensure dependencies and imports are updated and unit/usage references to themedIconSrc in both components are replaced with the centralized method.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@code/frontend/src/app/core/services/theme.service.ts`:
- Around line 98-104: The setCustomAccent(hex: string) method stores the input
unvalidated which can leave UI state inconsistent; validate the hex before
calling this._customAccent.set and localStorage.setItem (e.g., ensure it matches
a hex color regex like /^#?[0-9A-Fa-f]{6}$/ or your accepted formats), normalize
it to a consistent form (prepend '#' if needed), and only then persist and call
this.setAccent('custom'); if validation fails, do not mutate _customAccent or
localStorage and return/handle gracefully.
In
`@code/frontend/src/app/features/settings/notifications/notifications.component.html`:
- Line 87: The template is passing provider.iconLightUrl into themedIconSrc but
the helper currently inverts variants; update the logic in
notifications.component.ts (themedIconSrc) so it chooses provider.iconDarkUrl
when the app is in dark mode and provider.iconLightUrl when in light mode (i.e.,
if isDarkTheme return iconDarkUrl else return iconLightUrl), or alternatively
change the template to pass provider.iconDarkUrl if you intended the helper to
receive the dark URL; adjust the mapping for the function themedIconSrc and
ensure references to provider.iconLightUrl / provider.iconDarkUrl are used
consistently.
In `@code/frontend/src/app/layout/nav-sidebar/nav-sidebar.component.html`:
- Line 72: The img in nav-sidebar.component template is decorative but currently
uses [alt]="item.label" causing duplicate announcements; update the <img> for
icon rendering (themedIconSrc) to be treated as decorative by removing the bound
alt text and using an empty alt attribute (or aria-hidden="true") instead of
item.label so screen readers only read the link text; locate the img with
themedIconSrc(item.iconSrc), item.label and class "sidebar__item-img" to make
this change.
In `@code/frontend/src/styles.scss`:
- Around line 21-22: The skeleton gradient still contains a hardcoded blue stop
(rgba(59, 130, 246, 0.06)); update the gradient to use the same CSS variable as
the other stop so the shimmer uses the site's accent color (i.e., replace the
hardcoded rgba(...) with an rgba using var(--accent-rgb) and the desired alpha),
making the second stop consistent with the existing rgba(var(--accent-rgb),
0.08) usage in styles.scss for the skeleton gradient.
---
Nitpick comments:
In
`@code/frontend/src/app/features/settings/notifications/notifications.component.ts`:
- Around line 739-741: The themedIconSrc implementation is duplicated between
NotificationsComponent and NavSidebarComponent; extract it to a single shared
place (preferably ThemeService or a small util) and have both components call
that single method instead of their own copies. Specifically, move the logic
from themedIconSrc(src: string) into ThemeService (e.g.,
ThemeService.themedIconSrc or export a util function), update
NotificationsComponent and NavSidebarComponent to import and call the new
method, and remove the duplicated themedIconSrc implementations; ensure
dependencies and imports are updated and unit/usage references to themedIconSrc
in both components are replaced with the centralized method.
In `@code/frontend/src/app/ui/modal/modal.component.scss`:
- Line 31: The modal gradient currently uses a theme token for the first stop
(rgba(var(--accent-rgb), 0.3)) but a hard-coded blue for the second stop
(rgba(59, 130, 246, 0.2)); update the background declaration in
modal.component.scss to use a theme-driven token for the second stop as well
(e.g., use a dedicated CSS variable like --accent-tint-rgb or reuse --accent-rgb
with a different alpha) and provide a sensible fallback so both gradient stops
are driven by theme values and keep modal chroma consistent across custom
accents.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro Plus
Run ID: c02a109d-3b54-4f70-9add-adcdf8843b81
⛔ Files ignored due to path filters (14)
Logo/logo.svgis excluded by!**/*.svgcode/frontend/public/icons/ext/apprise-dark.svgis excluded by!**/*.svgcode/frontend/public/icons/ext/discord-dark.svgis excluded by!**/*.svgcode/frontend/public/icons/ext/gotify-dark.svgis excluded by!**/*.svgcode/frontend/public/icons/ext/lidarr-dark.svgis excluded by!**/*.svgcode/frontend/public/icons/ext/notifiarr-dark.svgis excluded by!**/*.svgcode/frontend/public/icons/ext/ntfy-dark.svgis excluded by!**/*.svgcode/frontend/public/icons/ext/pushover-dark.svgis excluded by!**/*.svgcode/frontend/public/icons/ext/radarr-dark.svgis excluded by!**/*.svgcode/frontend/public/icons/ext/readarr-dark.svgis excluded by!**/*.svgcode/frontend/public/icons/ext/sonarr-dark.svgis excluded by!**/*.svgcode/frontend/public/icons/ext/telegram-dark.svgis excluded by!**/*.svgcode/frontend/public/icons/ext/whisparr-dark.svgis excluded by!**/*.svgdocs/static/img/cleanuparr.svgis excluded by!**/*.svg
📒 Files selected for processing (43)
code/frontend/src/app/app.config.tscode/frontend/src/app/app.routes.tscode/frontend/src/app/core/services/theme.service.tscode/frontend/src/app/features/auth/login/login.component.scsscode/frontend/src/app/features/auth/setup/setup.component.scsscode/frontend/src/app/features/dashboard/dashboard.component.scsscode/frontend/src/app/features/events/events.component.scsscode/frontend/src/app/features/logs-component/logs.component.scsscode/frontend/src/app/features/seeker-stats/quality-tab/quality-tab.component.scsscode/frontend/src/app/features/seeker-stats/searches-tab/searches-tab.component.scsscode/frontend/src/app/features/seeker-stats/upgrades-tab/upgrades-tab.component.scsscode/frontend/src/app/features/settings/appearance/appearance-settings.component.htmlcode/frontend/src/app/features/settings/appearance/appearance-settings.component.scsscode/frontend/src/app/features/settings/appearance/appearance-settings.component.tscode/frontend/src/app/features/settings/notifications/notifications.component.htmlcode/frontend/src/app/features/settings/notifications/notifications.component.tscode/frontend/src/app/features/strikes/strikes.component.scsscode/frontend/src/app/layout/auth-layout/auth-layout.component.htmlcode/frontend/src/app/layout/auth-layout/auth-layout.component.scsscode/frontend/src/app/layout/auth-layout/auth-layout.component.tscode/frontend/src/app/layout/nav-sidebar/nav-sidebar.component.htmlcode/frontend/src/app/layout/nav-sidebar/nav-sidebar.component.scsscode/frontend/src/app/layout/nav-sidebar/nav-sidebar.component.tscode/frontend/src/app/layout/toolbar/toolbar.component.scsscode/frontend/src/app/ui/badge/badge.component.scsscode/frontend/src/app/ui/button/button.component.scsscode/frontend/src/app/ui/card/card.component.scsscode/frontend/src/app/ui/chip-input/chip-input.component.scsscode/frontend/src/app/ui/index.tscode/frontend/src/app/ui/logo/logo.component.htmlcode/frontend/src/app/ui/logo/logo.component.scsscode/frontend/src/app/ui/logo/logo.component.tscode/frontend/src/app/ui/modal/modal.component.scsscode/frontend/src/app/ui/number-input/number-input.component.scsscode/frontend/src/app/ui/size-input/size-input.component.scsscode/frontend/src/styles.scsscode/frontend/src/styles/_accents.scsscode/frontend/src/styles/_animations.scsscode/frontend/src/styles/_glass.scsscode/frontend/src/styles/_list-layout.scsscode/frontend/src/styles/_themes.scsscode/frontend/src/styles/_tokens.scsscode/frontend/src/styles/_variables.scss
|
@coderabbitai review |
|
@greptileai review |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@code/frontend/src/styles/_accents.scss`:
- Around line 1-12: Remove the empty single-line comment causing the
scss/comment-no-empty lint error by deleting the standalone "//" line in the
Accent Presets header block (the empty comment between the explanatory lines),
or replace it with a meaningful comment text; ensure the rest of the header
comments remain unchanged so the preset documentation and note about
ACCENT_PRESET_HEX are preserved.
In `@code/frontend/src/styles/_themes.scss`:
- Around line 10-15: The duplicate custom-property declarations (e.g.,
--surface-ground, --surface-section, --surface-card) must be split so the simple
fallback stays in the root block and the color-mix() value is moved into an
`@supports` (color-mix) override; i.e., leave the solid/fallback declaration
(e.g., --surface-ground: `#0c0614`) and remove the duplicate color-mix line from
that block, then add an `@supports` (selector(--color-mix) or `@supports`
(color-mix(in srgb, var(--brand-500) 8%, `#000`))) block that re-declares the same
custom properties using color-mix() so the browser that supports it will
override the fallback; apply the same change to the other duplicate pairs
mentioned (the other --surface-* declarations).
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 1d218743-a40d-4880-be3b-9be21914f097
📒 Files selected for processing (10)
code/frontend/src/app/core/services/theme.service.tscode/frontend/src/app/features/settings/appearance/appearance-settings.component.tscode/frontend/src/app/features/settings/notifications/notifications.component.htmlcode/frontend/src/app/features/settings/notifications/notifications.component.tscode/frontend/src/app/layout/nav-sidebar/nav-sidebar.component.htmlcode/frontend/src/app/layout/nav-sidebar/nav-sidebar.component.tscode/frontend/src/app/ui/modal/modal.component.scsscode/frontend/src/styles.scsscode/frontend/src/styles/_accents.scsscode/frontend/src/styles/_themes.scss
✅ Files skipped from review due to trivial changes (1)
- code/frontend/src/app/ui/modal/modal.component.scss
🚧 Files skipped from review as they are similar to previous changes (3)
- code/frontend/src/app/layout/nav-sidebar/nav-sidebar.component.html
- code/frontend/src/app/features/settings/notifications/notifications.component.html
- code/frontend/src/app/features/settings/appearance/appearance-settings.component.ts
Summary by Sourcery
Add user-configurable theme accent colors and update the UI to support accent presets and custom colors, including a refreshed brand palette and logo component.
New Features:
Enhancements: