]> Piment Noir Git Repositories - e-mobility-charging-stations-simulator.git/commitdiff
feat(ui-web): add dracula, gruvbox-dark, rose-pine themes and fix surface hierarchy
authorJérôme Benoit <jerome.benoit@sap.com>
Thu, 30 Apr 2026 00:24:52 +0000 (02:24 +0200)
committerJérôme Benoit <jerome.benoit@sap.com>
Thu, 30 Apr 2026 00:24:52 +0000 (02:24 +0200)
ui/common/src/config/schema.ts
ui/web/README.md
ui/web/src/assets/themes/catppuccin-latte.css
ui/web/src/assets/themes/dracula.css [new file with mode: 0644]
ui/web/src/assets/themes/gruvbox-dark.css [new file with mode: 0644]
ui/web/src/assets/themes/rose-pine.css [new file with mode: 0644]
ui/web/src/assets/themes/sap-horizon.css
ui/web/src/assets/themes/teal-light.css
ui/web/src/main.ts
ui/web/tests/unit/shared/composables/useTheme.test.ts

index 615c7414df879e12f8f59d8735a0685f463eeb52..98e0adb3afe4f26a582cb992399eabe07ebc0232 100644 (file)
@@ -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',
index 7d2bc8dc7ab3831540815459eed0c2e635c444ad..34fb19175720f3415d7bf044707654f00ea572b3 100644 (file)
@@ -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)           |
index 4fcfb19a97744b4736950feaeed2c905cc349b4e..ae5cba6bbb22854e34ad4e0cfcd6994d638b98d9 100644 (file)
@@ -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 (file)
index 0000000..b1a5f92
--- /dev/null
@@ -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 (file)
index 0000000..a3b9833
--- /dev/null
@@ -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 (file)
index 0000000..537d2ed
--- /dev/null
@@ -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;
+}
index d52b4a5e90d9536f4876f76ce95dc5c0f4e4f045..c74975d394b8f98a6f2cfce7ee35b1f0b301364a 100644 (file)
@@ -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) */
index 407b2e1108590bc148e6ff9923b96ae7a99ba02f..f26c8c11c76c2d6778812d1e1510162b175af605 100644 (file)
   --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;
index ad2d0841f44dc15ba3d66fa807c133b264945e23..a574a7763615f17ffe1a2ada6601bf49a21a206b 100644 (file)
@@ -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'
index 91e12c4c8ea7d73f79a09246adabe2fe8d276f32..a80838cb950ed289bb897ac7f43deee07e8f63d7 100644 (file)
@@ -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')