From: Jérôme Benoit Date: Thu, 30 Apr 2026 00:24:52 +0000 (+0200) Subject: feat(ui-web): add dracula, gruvbox-dark, rose-pine themes and fix surface hierarchy X-Git-Url: https://git.piment-noir.org/?a=commitdiff_plain;h=b3a6af77aca2c5747771044be3bbdfd926093e52;p=e-mobility-charging-stations-simulator.git feat(ui-web): add dracula, gruvbox-dark, rose-pine themes and fix surface hierarchy --- diff --git a/ui/common/src/config/schema.ts b/ui/common/src/config/schema.ts index 615c7414..98e0adb3 100644 --- a/ui/common/src/config/schema.ts +++ b/ui/common/src/config/schema.ts @@ -5,6 +5,9 @@ import { AuthenticationType, Protocol, ProtocolVersion } from '../types/UIProtoc export const SKIN_IDS = ['classic', 'modern'] as const export const THEME_IDS = [ 'catppuccin-latte', + 'dracula', + 'gruvbox-dark', + 'rose-pine', 'sap-horizon', 'teal-dark', 'teal-light', diff --git a/ui/web/README.md b/ui/web/README.md index 7d2bc8dc..34fb1917 100644 --- a/ui/web/README.md +++ b/ui/web/README.md @@ -142,6 +142,9 @@ Set `theme` in `config.json` to a filename (without `.css`) from `src/assets/the | Theme | Style | Source | | ------------------- | ----- | ---------------------------------------------------------------- | | `tokyo-night-storm` | Dark | [Tokyo Night](https://github.com/enkia/tokyo-night-vscode-theme) | +| `dracula` | Dark | [Dracula](https://github.com/dracula/dracula-theme) | +| `gruvbox-dark` | Dark | [Gruvbox](https://github.com/morhetz/gruvbox) | +| `rose-pine` | Dark | [Rosé Pine](https://github.com/rose-pine/rose-pine-theme) | | `teal-dark` | Dark | Material Teal | | `teal-light` | Light | Material Teal | | `catppuccin-latte` | Light | [Catppuccin](https://github.com/catppuccin/catppuccin) | diff --git a/ui/web/src/assets/themes/catppuccin-latte.css b/ui/web/src/assets/themes/catppuccin-latte.css index 4fcfb19a..ae5cba6b 100644 --- a/ui/web/src/assets/themes/catppuccin-latte.css +++ b/ui/web/src/assets/themes/catppuccin-latte.css @@ -23,8 +23,8 @@ --color-bg-caption: var(--ctp-crust); --color-bg-button: var(--ctp-blue); --color-bg-button-hover: color-mix(in srgb, var(--ctp-blue) 67%, transparent); - --color-text: var(--ctp-text); - --color-text-strong: var(--ctp-subtext1); + --color-text: var(--ctp-subtext1); + --color-text-strong: var(--ctp-text); --color-text-muted: var(--ctp-overlay1); --color-text-on-button: var(--ctp-base); --color-primary: var(--ctp-blue); @@ -34,8 +34,8 @@ --color-shadow-inset: rgba(0, 0, 0, 0.1); /* Surface hierarchy */ - --color-bg-raised: var(--ctp-surface0); - --color-bg-sunken: var(--ctp-mantle); + --color-bg-raised: var(--ctp-base); + --color-bg-sunken: var(--ctp-crust); /* State colors (Material 700 for light mode readability) */ --color-state-ok: #2e7d32; diff --git a/ui/web/src/assets/themes/dracula.css b/ui/web/src/assets/themes/dracula.css new file mode 100644 index 00000000..b1a5f928 --- /dev/null +++ b/ui/web/src/assets/themes/dracula.css @@ -0,0 +1,51 @@ +/* Dracula */ + +:root[data-theme='dracula'] { + /* Palette */ + --drac-bg: #282a36; + --drac-bg-dark: #21222c; + --drac-current: #44475a; + --drac-highlight: #515672; + --drac-border: #363848; + --drac-fg: #f8f8f2; + --drac-comment: #6272a4; + --drac-purple: #bd93f9; + --drac-pink: #ff79c6; + --drac-green: #50fa7b; + --drac-red: #ff5555; + --drac-orange: #ffb86c; + --drac-yellow: #f1fa8c; + --drac-cyan: #8be9fd; + + /* Semantic */ + --color-bg: var(--drac-bg); + --color-bg-surface: var(--drac-bg-dark); + --color-bg-input: var(--drac-bg-dark); + --color-bg-hover: var(--drac-current); + --color-bg-active: var(--drac-highlight); + --color-bg-header: var(--drac-bg-dark); + --color-bg-caption: var(--drac-current); + --color-bg-button: var(--drac-purple); + --color-bg-button-hover: color-mix(in srgb, var(--drac-purple) 67%, transparent); + --color-text: var(--drac-fg); + --color-text-strong: var(--drac-fg); + --color-text-muted: var(--drac-comment); + --color-text-on-button: var(--drac-bg); + --color-primary: var(--drac-purple); + --color-border: var(--drac-current); + --color-border-row: var(--drac-border); + --color-accent: var(--drac-pink); + --color-shadow-inset: rgba(0, 0, 0, 0.4); + + /* Surface hierarchy */ + --color-bg-raised: var(--drac-current); + --color-bg-sunken: var(--drac-bg-dark); + + /* State colors */ + --color-state-ok: var(--drac-green); + --color-state-warn: var(--drac-orange); + --color-state-err: var(--drac-red); + --color-state-idle: var(--drac-comment); + + color-scheme: dark; +} diff --git a/ui/web/src/assets/themes/gruvbox-dark.css b/ui/web/src/assets/themes/gruvbox-dark.css new file mode 100644 index 00000000..a3b98334 --- /dev/null +++ b/ui/web/src/assets/themes/gruvbox-dark.css @@ -0,0 +1,51 @@ +/* Gruvbox Dark */ + +:root[data-theme='gruvbox-dark'] { + /* Palette */ + --grv-bg0-hard: #1d2021; + --grv-bg0: #282828; + --grv-bg1: #3c3836; + --grv-bg2: #504945; + --grv-bg3: #665c54; + --grv-fg0: #fbf1c7; + --grv-fg1: #ebdbb2; + --grv-fg3: #bdae93; + --grv-fg4: #a89984; + --grv-orange: #fe8019; + --grv-yellow: #fabd2f; + --grv-green: #b8bb26; + --grv-red: #fb4934; + --grv-aqua: #8ec07c; + + /* Semantic */ + --color-bg: var(--grv-bg0); + --color-bg-surface: var(--grv-bg0-hard); + --color-bg-input: var(--grv-bg0-hard); + --color-bg-hover: var(--grv-bg1); + --color-bg-active: var(--grv-bg2); + --color-bg-header: var(--grv-bg0-hard); + --color-bg-caption: var(--grv-bg1); + --color-bg-button: var(--grv-orange); + --color-bg-button-hover: color-mix(in srgb, var(--grv-orange) 67%, transparent); + --color-text: var(--grv-fg1); + --color-text-strong: var(--grv-fg0); + --color-text-muted: var(--grv-fg4); + --color-text-on-button: var(--grv-bg0); + --color-primary: var(--grv-orange); + --color-border: var(--grv-bg1); + --color-border-row: var(--grv-bg2); + --color-accent: var(--grv-aqua); + --color-shadow-inset: rgba(0, 0, 0, 0.4); + + /* Surface hierarchy */ + --color-bg-raised: var(--grv-bg2); + --color-bg-sunken: var(--grv-bg0-hard); + + /* State colors */ + --color-state-ok: var(--grv-green); + --color-state-warn: var(--grv-yellow); + --color-state-err: var(--grv-red); + --color-state-idle: var(--grv-fg4); + + color-scheme: dark; +} diff --git a/ui/web/src/assets/themes/rose-pine.css b/ui/web/src/assets/themes/rose-pine.css new file mode 100644 index 00000000..537d2ed7 --- /dev/null +++ b/ui/web/src/assets/themes/rose-pine.css @@ -0,0 +1,51 @@ +/* Rosé Pine */ + +:root[data-theme='rose-pine'] { + /* Palette */ + --rp-base: #191724; + --rp-surface: #1f1d2e; + --rp-overlay: #26233a; + --rp-highlight-low: #2a2740; + --rp-highlight-med: #312e48; + --rp-muted: #6e6a86; + --rp-subtle: #908caa; + --rp-text: #e0def4; + --rp-love: #eb6f92; + --rp-gold: #f6c177; + --rp-rose: #ebbcba; + --rp-pine: #31748f; + --rp-foam: #9ccfd8; + --rp-iris: #c4a7e7; + + /* Semantic */ + --color-bg: var(--rp-surface); + --color-bg-surface: var(--rp-base); + --color-bg-input: var(--rp-base); + --color-bg-hover: var(--rp-overlay); + --color-bg-active: var(--rp-highlight-med); + --color-bg-header: var(--rp-base); + --color-bg-caption: var(--rp-overlay); + --color-bg-button: var(--rp-iris); + --color-bg-button-hover: color-mix(in srgb, var(--rp-iris) 67%, transparent); + --color-text: var(--rp-text); + --color-text-strong: var(--rp-text); + --color-text-muted: var(--rp-subtle); + --color-text-on-button: var(--rp-base); + --color-primary: var(--rp-iris); + --color-border: var(--rp-overlay); + --color-border-row: var(--rp-highlight-low); + --color-accent: var(--rp-rose); + --color-shadow-inset: rgba(0, 0, 0, 0.4); + + /* Surface hierarchy */ + --color-bg-raised: var(--rp-overlay); + --color-bg-sunken: var(--rp-base); + + /* State colors */ + --color-state-ok: var(--rp-foam); + --color-state-warn: var(--rp-gold); + --color-state-err: var(--rp-love); + --color-state-idle: var(--rp-muted); + + color-scheme: dark; +} diff --git a/ui/web/src/assets/themes/sap-horizon.css b/ui/web/src/assets/themes/sap-horizon.css index d52b4a5e..c74975d3 100644 --- a/ui/web/src/assets/themes/sap-horizon.css +++ b/ui/web/src/assets/themes/sap-horizon.css @@ -40,7 +40,7 @@ --color-shadow-inset: rgba(34, 53, 72, 0.15); /* Surface hierarchy */ - --color-bg-raised: var(--sap-bg-shell); + --color-bg-raised: var(--sap-bg-base); --color-bg-sunken: var(--sap-hover); /* State colors (SAP Horizon palette) */ diff --git a/ui/web/src/assets/themes/teal-light.css b/ui/web/src/assets/themes/teal-light.css index 407b2e11..f26c8c11 100644 --- a/ui/web/src/assets/themes/teal-light.css +++ b/ui/web/src/assets/themes/teal-light.css @@ -29,14 +29,14 @@ --color-text-muted: var(--tl-fg-muted); --color-text-on-button: var(--tl-white); --color-primary: var(--tl-teal); - --color-border: #e4e7eb; - --color-border-row: #eef0f3; + --color-border: var(--tl-bg-active); + --color-border-row: var(--tl-bg-hover); --color-accent: var(--tl-teal-dim); --color-shadow-inset: rgba(16, 24, 40, 0.1); /* Surface hierarchy */ - --color-bg-raised: var(--tl-bg-hover); - --color-bg-sunken: var(--tl-bg-raised); + --color-bg-raised: var(--tl-bg-base); + --color-bg-sunken: var(--tl-bg-active); /* State colors */ --color-state-ok: #2e7d32; diff --git a/ui/web/src/main.ts b/ui/web/src/main.ts index ad2d0841..a574a776 100644 --- a/ui/web/src/main.ts +++ b/ui/web/src/main.ts @@ -29,6 +29,9 @@ import 'vue-toast-notification/dist/theme-bootstrap.css' import './assets/shared.css' import './assets/themes/base.css' import './assets/themes/catppuccin-latte.css' +import './assets/themes/dracula.css' +import './assets/themes/gruvbox-dark.css' +import './assets/themes/rose-pine.css' import './assets/themes/sap-horizon.css' import './assets/themes/teal-dark.css' import './assets/themes/teal-light.css' diff --git a/ui/web/tests/unit/shared/composables/useTheme.test.ts b/ui/web/tests/unit/shared/composables/useTheme.test.ts index 91e12c4c..a80838cb 100644 --- a/ui/web/tests/unit/shared/composables/useTheme.test.ts +++ b/ui/web/tests/unit/shared/composables/useTheme.test.ts @@ -20,11 +20,14 @@ describe('useTheme', () => { expect(activeThemeId.value).toBe('tokyo-night-storm') }) - it('should return availableThemes with 5 entries', () => { + it('should return availableThemes with 8 entries', () => { const { availableThemes } = useTheme() - expect(availableThemes.length).toBe(5) + expect(availableThemes.length).toBe(8) expect(availableThemes).toContain('tokyo-night-storm') expect(availableThemes).toContain('catppuccin-latte') + expect(availableThemes).toContain('dracula') + expect(availableThemes).toContain('gruvbox-dark') + expect(availableThemes).toContain('rose-pine') expect(availableThemes).toContain('teal-dark') expect(availableThemes).toContain('teal-light') expect(availableThemes).toContain('sap-horizon')