From dddcd6f90914802213718c01a6a9b340a40f293b Mon Sep 17 00:00:00 2001 From: Carlos Costa Date: Thu, 12 Nov 2026 18:25:47 -0300 Subject: [PATCH 01/57] feat(tokens): define dynamic preset variables and status semantic mappings --- src/dashkit/dashkit.css | 133 +++++++++++++++++++++++++--------------- 1 file changed, 85 insertions(+), 48 deletions(-) diff --git a/src/dashkit/dashkit.css b/src/dashkit/dashkit.css index 8cf95ff..291d136 100644 --- a/src/dashkit/dashkit.css +++ b/src/dashkit/dashkit.css @@ -30,53 +30,53 @@ --color-ds-primary-900: var(--ds-color-primary-900, var(--color-blue-900)); --color-ds-primary-950: var(--ds-color-primary-950, var(--color-blue-950)); - --color-ds-info-50: var(--color-blue-50); - --color-ds-info-100: var(--color-blue-100); - --color-ds-info-200: var(--color-blue-200); - --color-ds-info-300: var(--color-blue-300); - --color-ds-info-400: var(--color-blue-400); - --color-ds-info-500: var(--color-blue-500); - --color-ds-info-600: var(--color-blue-600); - --color-ds-info-700: var(--color-blue-700); - --color-ds-info-800: var(--color-blue-800); - --color-ds-info-900: var(--color-blue-900); - --color-ds-info-950: var(--color-blue-950); - - --color-ds-success-50: var(--color-emerald-50); - --color-ds-success-100: var(--color-emerald-100); - --color-ds-success-200: var(--color-emerald-200); - --color-ds-success-300: var(--color-emerald-300); - --color-ds-success-400: var(--color-emerald-400); - --color-ds-success-500: var(--color-emerald-500); - --color-ds-success-600: var(--color-emerald-600); - --color-ds-success-700: var(--color-emerald-700); - --color-ds-success-800: var(--color-emerald-800); - --color-ds-success-900: var(--color-emerald-900); - --color-ds-success-950: var(--color-emerald-950); - - --color-ds-warning-50: var(--color-amber-50); - --color-ds-warning-100: var(--color-amber-100); - --color-ds-warning-200: var(--color-amber-200); - --color-ds-warning-300: var(--color-amber-300); - --color-ds-warning-400: var(--color-amber-400); - --color-ds-warning-500: var(--color-amber-500); - --color-ds-warning-600: var(--color-amber-600); - --color-ds-warning-700: var(--color-amber-700); - --color-ds-warning-800: var(--color-amber-800); - --color-ds-warning-900: var(--color-amber-900); - --color-ds-warning-950: var(--color-amber-950); - - --color-ds-danger-50: var(--color-red-50); - --color-ds-danger-100: var(--color-red-100); - --color-ds-danger-200: var(--color-red-200); - --color-ds-danger-300: var(--color-red-300); - --color-ds-danger-400: var(--color-red-400); - --color-ds-danger-500: var(--color-red-500); - --color-ds-danger-600: var(--color-red-600); - --color-ds-danger-700: var(--color-red-700); - --color-ds-danger-800: var(--color-red-800); - --color-ds-danger-900: var(--color-red-900); - --color-ds-danger-950: var(--color-red-950); + --color-ds-info-50: var(--ds-color-info-50, var(--color-blue-50)); + --color-ds-info-100: var(--ds-color-info-100, var(--color-blue-100)); + --color-ds-info-200: var(--ds-color-info-200, var(--color-blue-200)); + --color-ds-info-300: var(--ds-color-info-300, var(--color-blue-300)); + --color-ds-info-400: var(--ds-color-info-400, var(--color-blue-400)); + --color-ds-info-500: var(--ds-color-info-500, var(--color-blue-500)); + --color-ds-info-600: var(--ds-color-info-600, var(--color-blue-600)); + --color-ds-info-700: var(--ds-color-info-700, var(--color-blue-700)); + --color-ds-info-800: var(--ds-color-info-800, var(--color-blue-800)); + --color-ds-info-900: var(--ds-color-info-900, var(--color-blue-900)); + --color-ds-info-950: var(--ds-color-info-950, var(--color-blue-950)); + + --color-ds-success-50: var(--ds-color-success-50, var(--color-emerald-50)); + --color-ds-success-100: var(--ds-color-success-100, var(--color-emerald-100)); + --color-ds-success-200: var(--ds-color-success-200, var(--color-emerald-200)); + --color-ds-success-300: var(--ds-color-success-300, var(--color-emerald-300)); + --color-ds-success-400: var(--ds-color-success-400, var(--color-emerald-400)); + --color-ds-success-500: var(--ds-color-success-500, var(--color-emerald-500)); + --color-ds-success-600: var(--ds-color-success-600, var(--color-emerald-600)); + --color-ds-success-700: var(--ds-color-success-700, var(--color-emerald-700)); + --color-ds-success-800: var(--ds-color-success-800, var(--color-emerald-800)); + --color-ds-success-900: var(--ds-color-success-900, var(--color-emerald-900)); + --color-ds-success-950: var(--ds-color-success-950, var(--color-emerald-950)); + + --color-ds-warning-50: var(--ds-color-warning-50, var(--color-amber-50)); + --color-ds-warning-100: var(--ds-color-warning-100, var(--color-amber-100)); + --color-ds-warning-200: var(--ds-color-warning-200, var(--color-amber-200)); + --color-ds-warning-300: var(--ds-color-warning-300, var(--color-amber-300)); + --color-ds-warning-400: var(--ds-color-warning-400, var(--color-amber-400)); + --color-ds-warning-500: var(--ds-color-warning-500, var(--color-amber-500)); + --color-ds-warning-600: var(--ds-color-warning-600, var(--color-amber-600)); + --color-ds-warning-700: var(--ds-color-warning-700, var(--color-amber-700)); + --color-ds-warning-800: var(--ds-color-warning-800, var(--color-amber-800)); + --color-ds-warning-900: var(--ds-color-warning-900, var(--color-amber-900)); + --color-ds-warning-950: var(--ds-color-warning-950, var(--color-amber-950)); + + --color-ds-danger-50: var(--ds-color-danger-50, var(--color-red-50)); + --color-ds-danger-100: var(--ds-color-danger-100, var(--color-red-100)); + --color-ds-danger-200: var(--ds-color-danger-200, var(--color-red-200)); + --color-ds-danger-300: var(--ds-color-danger-300, var(--color-red-300)); + --color-ds-danger-400: var(--ds-color-danger-400, var(--color-red-400)); + --color-ds-danger-500: var(--ds-color-danger-500, var(--color-red-500)); + --color-ds-danger-600: var(--ds-color-danger-600, var(--color-red-600)); + --color-ds-danger-700: var(--ds-color-danger-700, var(--color-red-700)); + --color-ds-danger-800: var(--ds-color-danger-800, var(--color-red-800)); + --color-ds-danger-900: var(--ds-color-danger-900, var(--color-red-900)); + --color-ds-danger-950: var(--ds-color-danger-950, var(--color-red-950)); --color-background: var(--ds-bg); --color-foreground: var(--ds-fg); @@ -128,7 +128,6 @@ --color-chart-4: var(--ds-chart-4); --color-chart-5: var(--ds-chart-5); - --radius: 0.75rem; --radius-xl: calc(var(--radius) * 1.3333); --radius-2xl: calc(var(--radius) * 1.6667); --radius-lg: var(--radius); @@ -141,6 +140,16 @@ 2. Base Variable Definitions ──────────────────────────────────────────────── */ :root { + --radius: 0.75rem; + --ds-pill-radius: calc(var(--radius) * 9999); + --ds-border-width: 1px; + --ds-button-border: transparent; + --ds-button-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05); + --ds-button-outlined-shadow: none; + --ds-button-soft-shadow: none; + --ds-card-shadow: none; + --ds-input-shadow: none; + --ds-bg: var(--color-ds-100); --ds-fg: var(--color-ds-800); @@ -341,6 +350,34 @@ @utility ds-surface-glass { @apply bg-popover/95 backdrop-blur-md; } @utility ds-active-press { @apply active:scale-[0.97] transition-transform duration-150 ease-out; } +.ds-pill { + border-radius: var(--ds-pill-radius, 9999px); +} +.ds-border-width { + border-width: var(--ds-border-width, 1px); +} +.ds-card-shadow { + box-shadow: var(--ds-card-shadow, none); +} +.ds-input-surface { + border-width: var(--ds-border-width, 1px); + box-shadow: var(--ds-input-shadow, none); +} +.ds-button-surface { + border-width: var(--ds-border-width, 1px); + border-color: var(--ds-button-border, transparent); + box-shadow: var(--ds-button-shadow, 0 1px 2px 0 rgb(0 0 0 / 0.05)); +} +.ds-button-soft-surface { + border-width: var(--ds-border-width, 1px); + border-color: var(--ds-button-border, transparent); + box-shadow: var(--ds-button-soft-shadow, none); +} +.ds-button-outlined-surface { + border-width: var(--ds-border-width, 1px); + box-shadow: var(--ds-button-outlined-shadow, none); +} + /* ──────────────────────────────────────────────── 5. Colors utilities From 3da0ec86f9e2131549714c7f2825d7bc97f2db8d Mon Sep 17 00:00:00 2001 From: Carlos Costa Date: Thu, 12 Nov 2026 18:25:48 -0300 Subject: [PATCH 02/57] feat(presets): add neo-brutalist theme preset tokens --- src/dashkit/presets/brutalist.css | 83 +++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 src/dashkit/presets/brutalist.css diff --git a/src/dashkit/presets/brutalist.css b/src/dashkit/presets/brutalist.css new file mode 100644 index 0000000..b09e7e9 --- /dev/null +++ b/src/dashkit/presets/brutalist.css @@ -0,0 +1,83 @@ +/* ───────────────────────────────────────────────────────────────────────────── + Dashkit Preset: Brutalism + @preset brutalist + @recommended-attrs data-base="stone" data-primary="orange" data-preset="brutalist" + @description Sharp 0px geometry, 2px ink borders, flat solid fills, hard offset shadows. + ───────────────────────────────────────────────────────────────────────────── */ + +[data-preset="brutalist"] { + /* 1. Geometry & Surface Elevation */ + --radius: 0rem; + --ds-pill-radius: 0rem; + --ds-border-width: 2px; + --ds-primary-gradient: none; + --ds-danger-gradient: none; + + --ds-button-border: var(--ds-border); + --ds-button-shadow: 3px 3px 0px 0px var(--ds-border); + --ds-button-outlined-shadow: 3px 3px 0px 0px var(--ds-border); + --ds-button-soft-shadow: 3px 3px 0px 0px var(--ds-border); + --ds-card-shadow: 4px 4px 0px 0px var(--ds-border); + --ds-input-shadow: 2px 2px 0px 0px var(--ds-border); + + /* 2. Core Surfaces & Borders (Light) */ + --ds-bg: #f5f2eb; + --ds-fg: var(--color-ds-950); + --ds-card: #fffdf9; + --ds-card-fg: var(--color-ds-950); + --ds-popover: #fffdf9; + --ds-popover-fg: var(--color-ds-950); + --ds-popover-border: var(--color-ds-900); + + --ds-border: var(--color-ds-900); + --ds-border-subtle: var(--color-ds-400); + + /* 3. Interactive & Muted Tokens (Light) */ + --ds-secondary: var(--color-ds-200); + --ds-secondary-fg: var(--color-ds-950); + --ds-muted: #ebe5d9; + --ds-muted-fg: var(--color-ds-700); + + /* 4. Form Controls (Light) */ + --ds-input-bg: #ffffff; + --ds-input-border: var(--color-ds-900); + --ds-input-focus: var(--color-ds-950); + --ds-input-fg: var(--color-ds-950); + --ds-input-ring: var(--color-ds-900); +} + +.dark[data-preset="brutalist"], +.dark [data-preset="brutalist"] { + /* Core Surfaces & Borders (Dark) */ + --ds-bg: #121110; + --ds-fg: var(--color-ds-50); + --ds-card: #1c1917; + --ds-card-fg: var(--color-ds-50); + --ds-popover: #1c1917; + --ds-popover-fg: var(--color-ds-50); + --ds-popover-border: var(--color-ds-300); + + --ds-border: var(--color-ds-300); + --ds-border-subtle: var(--color-ds-700); + + /* Surface Elevation (Dark) */ + --ds-button-border: var(--ds-border); + --ds-button-shadow: 3px 3px 0px 0px var(--ds-border); + --ds-button-outlined-shadow: 3px 3px 0px 0px var(--ds-border); + --ds-button-soft-shadow: 3px 3px 0px 0px var(--ds-border); + --ds-card-shadow: 4px 4px 0px 0px var(--ds-border); + --ds-input-shadow: 2px 2px 0px 0px var(--ds-border); + + /* Interactive & Muted Tokens (Dark) */ + --ds-secondary: var(--color-ds-800); + --ds-secondary-fg: var(--color-ds-50); + --ds-muted: var(--color-ds-800); + --ds-muted-fg: var(--color-ds-300); + + /* Form Controls (Dark) */ + --ds-input-bg: #0c0a09; + --ds-input-border: var(--color-ds-300); + --ds-input-focus: var(--color-ds-50); + --ds-input-fg: var(--color-ds-50); + --ds-input-ring: var(--color-ds-300); +} From a9b75ddbbb49f2067a87ee93892eab6e43ccbc31 Mon Sep 17 00:00:00 2001 From: Carlos Costa Date: Thu, 12 Nov 2026 18:25:49 -0300 Subject: [PATCH 03/57] feat(presets): add cyber terminal matrix theme preset tokens --- src/dashkit/presets/matrix.css | 146 +++++++++++++++++++++++++++++++++ 1 file changed, 146 insertions(+) create mode 100644 src/dashkit/presets/matrix.css diff --git a/src/dashkit/presets/matrix.css b/src/dashkit/presets/matrix.css new file mode 100644 index 0000000..dcab9b8 --- /dev/null +++ b/src/dashkit/presets/matrix.css @@ -0,0 +1,146 @@ +/* ───────────────────────────────────────────────────────────────────────────── + Dashkit Preset: Matrix + @preset matrix + @recommended-attrs class="dark" data-base="zinc" data-primary="green" data-preset="matrix" + @description Phosphor-green CRT terminal palette, 2px technical corners, monospace UI, subtle glow. + ───────────────────────────────────────────────────────────────────────────── */ + +[data-preset="matrix"] { + /* 1. Geometry, Typography & Surface Elevation */ + --radius: 0.125rem; + --ds-pill-radius: var(--radius-md); + --ds-border-width: 1px; + --ds-primary-gradient: none; + --ds-danger-gradient: none; + --font-sans: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace); + + --ds-button-border: #16a34a; + --ds-button-shadow: 0 0 10px -2px rgb(22 163 74 / 0.35); + --ds-button-outlined-shadow: 0 0 8px -2px rgb(22 163 74 / 0.2); + --ds-button-soft-shadow: none; + --ds-card-shadow: 0 0 20px -8px rgb(22 163 74 / 0.2); + --ds-input-shadow: inset 0 0 6px -2px rgb(22 163 74 / 0.15); + + /* 2. Base Scale Override (Phosphor Green Monochromatic Ramp) + Ensures components using ds-0..ds-950 (Pagination, ProgressBar track, + ButtonGroup active state, Slider, Switch, Divider) match the CRT palette. */ + --ds-color-0: #ecfdf5; + --ds-color-50: #ecfdf5; + --ds-color-100: #d1fae5; + --ds-color-200: #bbf7d0; + --ds-color-300: #86efac; + --ds-color-400: #4ade80; + --ds-color-500: #22c55e; + --ds-color-600: #16a34a; + --ds-color-700: #14532d; + --ds-color-800: #0a2612; + --ds-color-900: #05160a; + --ds-color-950: #020804; + + /* 3. Info Scale Override (Terminal Cyan-Emerald Tint) */ + --ds-color-info-50: #ecfdf5; + --ds-color-info-100: #d1fae5; + --ds-color-info-200: #a7f3d0; + --ds-color-info-300: #6ee7b7; + --ds-color-info-400: #34d399; + --ds-color-info-500: #10b981; + --ds-color-info-600: #059669; + --ds-color-info-700: #047857; + --ds-color-info-800: #065f46; + --ds-color-info-900: #064e3b; + --ds-color-info-950: #022c22; + + /* 4. Core Surfaces & Borders (Light) */ + --ds-bg: #ecfdf5; + --ds-fg: #052e16; + --ds-card: #f0fdf4; + --ds-card-fg: #052e16; + --ds-popover: #f0fdf4; + --ds-popover-fg: #052e16; + --ds-popover-border: #16a34a; + --ds-popover-item: #dcfce7; + --ds-popover-item-selected: #bbf7d0; + --ds-popover-item-selected-fg: #052e16; + + --ds-border: #16a34a; + --ds-border-subtle: #86efac; + + /* 5. Interactive & Muted Tokens (Light) */ + --ds-primary: #15803d; + --ds-primary-fg: #f0fdf4; + --ds-secondary: #dcfce7; + --ds-secondary-fg: #052e16; + --ds-muted: #dcfce7; + --ds-muted-fg: #15803d; + --ds-accent: #bbf7d0; + --ds-accent-fg: #052e16; + --ds-info: #059669; + --ds-info-fg: #ecfdf5; + + /* 6. Form Controls & Charts (Light) */ + --ds-input-bg: #f0fdf4; + --ds-input-border: #16a34a; + --ds-input-focus: #15803d; + --ds-input-fg: #052e16; + --ds-input-ring: #22c55e; + + --ds-chart-1: #16a34a; + --ds-chart-2: #059669; + --ds-chart-3: #65a30d; + --ds-chart-4: #0d9488; + --ds-chart-5: #10b981; +} + +.dark[data-preset="matrix"], +.dark [data-preset="matrix"] { + /* Phosphor highlight for ds-0 elements (Slider thumb, inverted Tooltip/Toast) */ + --ds-color-0: #4ade80; + + /* Core Surfaces & Borders (Dark CRT) */ + --ds-bg: #020804; + --ds-fg: #4ade80; + --ds-card: #041007; + --ds-card-fg: #86efac; + --ds-popover: #051409; + --ds-popover-fg: #86efac; + --ds-popover-border: #15803d; + --ds-popover-item: #092210; + --ds-popover-item-selected: #14532d; + --ds-popover-item-selected-fg: #86efac; + + --ds-border: #14532d; + --ds-border-subtle: #0a2914; + + /* Interactive & Muted Tokens (Dark CRT) */ + --ds-primary: #22c55e; + --ds-primary-fg: #020804; + --ds-secondary: #0a2612; + --ds-secondary-fg: #86efac; + --ds-muted: #07190c; + --ds-muted-fg: #4ade80; + --ds-accent: #14532d; + --ds-accent-fg: #86efac; + --ds-info: #10b981; + --ds-info-fg: #020804; + + /* Surface Elevation (Dark CRT Glow) */ + --ds-button-border: #22c55e; + --ds-button-shadow: 0 0 12px -2px rgb(34 197 94 / 0.45); + --ds-button-outlined-shadow: 0 0 10px -2px rgb(34 197 94 / 0.25); + --ds-button-soft-shadow: 0 0 8px -2px rgb(34 197 94 / 0.2); + --ds-card-shadow: 0 0 24px -8px rgb(34 197 94 / 0.22); + --ds-input-shadow: inset 0 0 8px -2px rgb(34 197 94 / 0.18); + + /* Form Controls & Charts (Dark CRT) */ + --ds-input-bg: #030c05; + --ds-input-border: #15803d; + --ds-input-focus: #22c55e; + --ds-input-fg: #86efac; + --ds-input-ring: #16a34a; + + --ds-chart-1: #22c55e; + --ds-chart-2: #4ade80; + --ds-chart-3: #86efac; + --ds-chart-4: #10b981; + --ds-chart-5: #a3e635; +} From 0dc1caf3fe35ff46c403113ddc36bbaf97888fd8 Mon Sep 17 00:00:00 2001 From: Carlos Costa Date: Thu, 12 Nov 2026 18:25:51 -0300 Subject: [PATCH 04/57] feat(presets): add preset aggregator stylesheet --- src/dashkit/presets/index.css | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 src/dashkit/presets/index.css diff --git a/src/dashkit/presets/index.css b/src/dashkit/presets/index.css new file mode 100644 index 0000000..5b50faf --- /dev/null +++ b/src/dashkit/presets/index.css @@ -0,0 +1,6 @@ +/* ───────────────────────────────────────────────────────────────────────────── + Dashkit Style Presets Showcase Barrel + Each preset is a self-contained token override layer on top of `dashkit.css`. + ───────────────────────────────────────────────────────────────────────────── */ +@import "./brutalist.css"; +@import "./matrix.css"; From ec977e593fec32ad40019c4fd3957eaa91fd1f4f Mon Sep 17 00:00:00 2001 From: Carlos Costa Date: Thu, 12 Nov 2026 18:25:52 -0300 Subject: [PATCH 05/57] fix(portal): sync data-preset and data-dark-bg attributes to portals --- src/dashkit/utils/portalTheme.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/dashkit/utils/portalTheme.ts b/src/dashkit/utils/portalTheme.ts index 255b8c9..7f71d1a 100644 --- a/src/dashkit/utils/portalTheme.ts +++ b/src/dashkit/utils/portalTheme.ts @@ -2,6 +2,7 @@ export interface PortalThemeAttrs { 'data-primary'?: string; 'data-base'?: string; 'data-dark-bg'?: string; + 'data-preset'?: string; } export function getPortalThemeAttrs(source?: Element | null): PortalThemeAttrs { @@ -19,10 +20,12 @@ export function getPortalThemeAttrs(source?: Element | null): PortalThemeAttrs { const primary = el.closest('[data-primary]')?.getAttribute('data-primary'); const base = el.closest('[data-base]')?.getAttribute('data-base'); const darkBg = el.closest('[data-dark-bg]')?.getAttribute('data-dark-bg'); + const preset = el.closest('[data-preset]')?.getAttribute('data-preset'); if (primary) attrs['data-primary'] = primary; if (base) attrs['data-base'] = base; if (darkBg) attrs['data-dark-bg'] = darkBg; + if (preset) attrs['data-preset'] = preset; return attrs; } From d033328c2478672357f83acb2b5f8b8572a1f060 Mon Sep 17 00:00:00 2001 From: Carlos Costa Date: Thu, 12 Nov 2026 18:25:53 -0300 Subject: [PATCH 06/57] test(portal): verify data-preset attribute synchronization --- src/dashkit/utils/portalTheme.test.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/dashkit/utils/portalTheme.test.ts b/src/dashkit/utils/portalTheme.test.ts index bc0113b..6c14bd9 100644 --- a/src/dashkit/utils/portalTheme.test.ts +++ b/src/dashkit/utils/portalTheme.test.ts @@ -7,6 +7,7 @@ describe('getPortalThemeAttrs', () => { wrapper.setAttribute('data-primary', 'emerald'); wrapper.setAttribute('data-base', 'stone'); wrapper.setAttribute('data-dark-bg', 'oled'); + wrapper.setAttribute('data-preset', 'brutalist'); const child = document.createElement('button'); wrapper.appendChild(child); @@ -16,6 +17,7 @@ describe('getPortalThemeAttrs', () => { 'data-primary': 'emerald', 'data-base': 'stone', 'data-dark-bg': 'oled', + 'data-preset': 'brutalist', }); document.body.removeChild(wrapper); From 0f9a5b12ad3cda77a2688896a89db08a722d3290 Mon Sep 17 00:00:00 2001 From: Carlos Costa Date: Thu, 12 Nov 2026 18:25:54 -0300 Subject: [PATCH 07/57] fix(avatar): update avatar group counter to semantic surface tokens --- src/dashkit/Avatar/AvatarGroup.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dashkit/Avatar/AvatarGroup.tsx b/src/dashkit/Avatar/AvatarGroup.tsx index 7606693..e6b9946 100644 --- a/src/dashkit/Avatar/AvatarGroup.tsx +++ b/src/dashkit/Avatar/AvatarGroup.tsx @@ -42,7 +42,7 @@ export function AvatarGroup({ children, max, size = 'md', spacing = 'md', classN )} From a3491d49fe9efed074d4fd37fad2f266575b3019 Mon Sep 17 00:00:00 2001 From: Carlos Costa Date: Thu, 12 Nov 2026 18:25:55 -0300 Subject: [PATCH 08/57] fix(badge): use semantic neutral tokens for solid base badge --- src/dashkit/Badge/Badge.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dashkit/Badge/Badge.tsx b/src/dashkit/Badge/Badge.tsx index 0ef61b7..a1e6128 100644 --- a/src/dashkit/Badge/Badge.tsx +++ b/src/dashkit/Badge/Badge.tsx @@ -27,7 +27,7 @@ const badgeVariants = cva( }, compoundVariants: [ // Base - { variant: 'solid', color: 'base', className: 'bg-ds-500 text-white' }, + { variant: 'solid', color: 'base', className: 'bg-ds-600 text-ds-50 dark:bg-ds-500 dark:text-ds-950' }, { variant: 'soft', color: 'base', className: 'bg-ds-500/15 text-ds-600 dark:text-ds-400 dark:bg-ds-500/20' }, // Success { variant: 'solid', color: 'success', className: 'bg-ds-success-600 text-ds-success-50 dark:bg-ds-success-500 dark:text-ds-success-950' }, From 5435ff3bf6c5c6f68d64651382a2dec66a01bb57 Mon Sep 17 00:00:00 2001 From: Carlos Costa Date: Thu, 12 Nov 2026 18:25:56 -0300 Subject: [PATCH 09/57] fix(breadcrumb): replace dark white text with semantic token --- src/dashkit/Breadcrumb/BreadcrumbItem.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dashkit/Breadcrumb/BreadcrumbItem.tsx b/src/dashkit/Breadcrumb/BreadcrumbItem.tsx index 6145d4b..7266efe 100644 --- a/src/dashkit/Breadcrumb/BreadcrumbItem.tsx +++ b/src/dashkit/Breadcrumb/BreadcrumbItem.tsx @@ -20,8 +20,8 @@ export const BreadcrumbItem = React.forwardRef {children} From 75aac49591480c9c22a1cf672c6e475ec50cea95 Mon Sep 17 00:00:00 2001 From: Carlos Costa Date: Thu, 12 Nov 2026 18:25:57 -0300 Subject: [PATCH 10/57] fix(button): support tactile shadows and border tokens in presets --- src/dashkit/Button/Button.tsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/dashkit/Button/Button.tsx b/src/dashkit/Button/Button.tsx index bc1be5c..4762ff4 100644 --- a/src/dashkit/Button/Button.tsx +++ b/src/dashkit/Button/Button.tsx @@ -16,9 +16,9 @@ const buttonVariants = cva( { variants: { variant: { - filled: '', - outlined: 'bg-card text-foreground border', - soft: '', + filled: 'border ds-button-surface', + outlined: 'bg-card text-foreground border ds-button-outlined-surface', + soft: 'border ds-button-soft-surface', ghost: 'bg-transparent border border-transparent', }, color: { @@ -31,7 +31,7 @@ const buttonVariants = cva( lg: 'h-11 text-base', }, rounded: { - true: 'rounded-full', + true: 'rounded-full ds-pill', false: 'rounded-md', }, }, @@ -40,7 +40,7 @@ const buttonVariants = cva( { variant: 'filled', color: 'primary', - className: 'ds-primary-gradient text-primary-foreground shadow-xs hover:brightness-105', + className: 'ds-primary-gradient text-primary-foreground hover:brightness-105', }, { variant: 'filled', @@ -51,7 +51,7 @@ const buttonVariants = cva( { variant: 'outlined', color: 'primary', - className: 'border-ds-300 hover:bg-ds-50 dark:border-ds-700 dark:hover:bg-ds-800/50 dark:hover:brightness-110', + className: 'border-border hover:bg-muted/60 dark:hover:bg-muted/60 dark:hover:brightness-110', }, { variant: 'outlined', From 0dabccb4f9e13992162241a26e3500c899687e7c Mon Sep 17 00:00:00 2001 From: Carlos Costa Date: Thu, 12 Nov 2026 18:25:58 -0300 Subject: [PATCH 11/57] fix(button-group): integrate ds-border-width for brutalist borders --- src/dashkit/ButtonGroup/ButtonGroup.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/dashkit/ButtonGroup/ButtonGroup.tsx b/src/dashkit/ButtonGroup/ButtonGroup.tsx index 453128e..1c97cb7 100644 --- a/src/dashkit/ButtonGroup/ButtonGroup.tsx +++ b/src/dashkit/ButtonGroup/ButtonGroup.tsx @@ -45,9 +45,8 @@ export function ButtonGroupItem({ - -``` - -#### Available Base Palettes (`data-base`) -- `zinc` (default), `slate`, `gray`, `neutral`, `stone`, `taupe`, `mauve`, `mist`, `olive` - -#### Available Primary Palettes (`data-primary`) -- `blue` (default), `violet`, `indigo`, `emerald`, `green`, `rose`, `orange`, `amber`, `cyan`, `teal`, `fuchsia`, `red`, `yellow`, `lime`, `sky`, `purple`, `pink` - -#### OLED Dark Mode (`data-dark-bg`) -- `oled` (switches `.dark` background `--ds-bg` to pure `#000000` black) - -### Static Token Customization - -To customize Dashkit statically, edit the variables in `:root` and `.dark` inside `src/dashkit/dashkit.css`. - -### Example: Customizing Roundness - -Because `--radius-sm` through `--radius-2xl` are computed proportionally from `--radius`, changing `--radius` in `:root` updates the entire system: - -```css -:root { - --radius: 1rem; /* Soft, rounded geometry (use 0rem for sharp corners) */ -} -``` - -### Example: Custom Brand Color - -To change the primary color to a custom purple: +Dashkit connects semantic utility classes to CSS custom properties via `@theme inline` in `src/dashkit/dashkit.css`: ```css -:root { - --ds-primary: #7c3aed; /* Violet 600 */ - --ds-primary-fg: #ffffff; -} - -.dark { - --ds-primary: #a78bfa; /* Violet 400 */ +@theme inline { + --color-background: var(--ds-bg); + --color-foreground: var(--ds-fg); + --color-card: var(--ds-card); + --color-popover: var(--ds-popover); + --color-border: var(--ds-border); + --color-primary: var(--ds-primary); + --color-primary-foreground: var(--ds-primary-fg); + --color-input-bg: var(--ds-input-bg); + --color-input-border: var(--ds-input-border); + --color-input-focus: var(--ds-input-focus); + --color-input-fg: var(--ds-input-fg); } ``` -## Creating Your Own Tokens - -When building your own components, you can add new tokens to the `:root` block to keep your design consistent: - -1. **Define the token** in `dashkit.css`: - ```css - :root { - --ds-special-bg: var(--color-ds-primary-500); - } - ``` -2. **Map it** in `@theme inline`: - ```css - @theme inline { - --color-special-bg: var(--ds-special-bg); - } - ``` -3. **Use it** in your component: - ```tsx -
- My custom themed component -
- ``` - -## AI-First Customization Protocol - -Dashkit is architected for **AI-driven customization via prompts**. Because you own the source code in `src/dashkit/`, AI agents should follow a strict 3-tier hierarchy instead of scattering inline classes across pages: - -1. **Tier 1 — Global Theme (`src/dashkit/dashkit.css`)**: Change `--ds-primary`, `--ds-bg`, `--ds-card`, `--radius`, or set `--ds-primary-gradient: none` for flat buttons. -2. **Tier 2 — Component Variants (`src/dashkit/[Component]/[Component].tsx`)**: Edit `cva(...)` definitions directly in the component file when changing a component's default look or adding a new variant across the app. -3. **Tier 3 — One-Off Overrides (`className` & `data-slot`)**: Use `className` for single-instance tweaks (the `cn` utility automatically resolves conflicts between standard Tailwind classes like `rounded-full` / `bg-none` and Dashkit's `ds-rounded*` / `ds-*-gradient`). For sub-elements inside form components (`Input`, `Select`, `Combobox`, `DatePicker`, etc.), target `data-slot` attributes (e.g., `className="[&_[data-slot=label]]:uppercase [&_[data-slot=trigger]]:rounded-full"`). - -### AI Prompt Example - -Copy and paste this prompt to quickly generate a unique theme: - -> **AI Prompt:** -> "I want to create a new theme for Dashkit UI. The theme should use a professional 'Emerald and Slate' palette. Please update `dashkit.css` by: -> 1. Updating the `:root` and `.dark` variables for bg, fg, primary, and secondary. -> 2. Setting `--radius: 0.5rem` for a sharper, denser geometric profile. -> 3. Ensuring inputs use a clean, bordered style with subtle rings. -> Keep all page components pure without inline style overrides." - - - +--- + +## Token Reference + +### Core Surfaces & Borders +- `--ds-bg`: Base canvas background (`bg-background`) +- `--ds-fg`: Main foreground text (`text-foreground`) +- `--ds-card`: Card and panel surface (`bg-card`) +- `--ds-card-fg`: Card text foreground (`text-card-foreground`) +- `--ds-popover`: Floating surfaces for dropdowns, tooltips, dialogs (`bg-popover`) +- `--ds-border`: Default border color (`border-border`) + +### Interactive & Brand Actions +- `--ds-primary`: Primary action color (`bg-primary`, `text-primary`) +- `--ds-primary-fg`: Text on primary action (`text-primary-foreground`) +- `--ds-primary-gradient`: Linear gradient for filled buttons (`none` for flat style) +- `--ds-secondary`: Secondary action background (`bg-secondary`) +- `--ds-accent`: Subtle selection and hover highlight (`bg-accent`) + +### Status & Feedback +- `--ds-danger` / `--ds-danger-fg`: Error and destructive actions (`text-danger`, `bg-danger`) +- `--ds-success` / `--ds-success-fg`: Confirmation and positive indicators (`text-success`) +- `--ds-warning` / `--ds-warning-fg`: Caution and warning alerts (`text-warning`) +- `--ds-info` / `--ds-info-fg`: Informational badges and icons (`text-info`) + +### Form & Input Controls +- `--ds-input-bg`: Input field background (`bg-input-bg`) +- `--ds-input-border`: Unfocused input border (`border-input-border`) +- `--ds-input-focus`: Focused ring and border highlight (`focus:border-input-focus`) +- `--ds-input-fg`: Input value text color (`text-input-fg`) +- `--ds-input-ring`: Focus ring box-shadow color + +### Geometry, Corner Radius & Tactile Shadows +All corner radii derive dynamically from `--radius`. Modifying `--radius` updates all components instantly without React changes: +- `--radius`: Master geometric scale (Default: `0.75rem`) +- `--radius-sm`: Derived as `max(0px, calc(var(--radius) - 5px))` (Checkboxes, small Badges) +- `--radius-md`: Derived as `max(0px, calc(var(--radius) - 3px))` (Inputs, Buttons) +- `--radius-lg`: Derived as `var(--radius)` (Cards, Popovers) +- `--radius-xl`: Derived as `calc(var(--radius) * 1.3333)` (Modals, Drawers) +- `--radius-2xl`: Derived as `calc(var(--radius) * 1.6667)` (Hero containers) +- `--ds-pill-radius`: Derived as `calc(var(--radius) * 9999)` (Pill Buttons, Switches, Chips, Sliders; set to `0rem` for square pills) +- `--ds-border-width`: Structural border thickness for surfaces and controls (Default: `1px`, `2px` in Brutalism) +- `--ds-card-shadow`: Surface elevation, hard offset, or glow shadow (Default: `none`) +- `--ds-button-shadow`: Tactile elevation on buttons (Default: `0 1px 2px 0 rgb(0 0 0 / 0.05)`) +- `--ds-button-border`: Border color for filled/soft buttons (Default: `transparent`, `var(--ds-border)` in Brutalism) + +--- + +## Dynamic Runtime Attributes + +Dashkit supports instant dynamic switching via HTML attributes without changing CSS files: + +### Base Neutral Palette (`data-base`) +Applies neutral gray scales (`ds-0` through `ds-950`): +- Values: `zinc` (default), `slate`, `gray`, `neutral`, `stone`, `taupe`, `mauve`, `mist`, `olive` +- Scope: Set on `` or scoped on any container `
` + +### Primary Brand Color (`data-primary`) +Applies primary brand swatches (`ds-primary-50` through `ds-primary-950`): +- Values: `blue` (default), `violet`, `indigo`, `emerald`, `green`, `rose`, `orange`, `amber`, `cyan`, `teal`, `fuchsia`, `red`, `yellow`, `lime`, `sky`, `purple`, `pink` +- Scope: Set on `` or scoped on any container `
` + +### Dark & OLED Background (`data-dark-bg`) +- `data-dark-bg="oled"`: Forces `.dark` background `--ds-bg` to pure `#000000` black. + +--- + +## Preset Delta Layer (`src/dashkit/presets/`) + +Presets are token-override layers scoped to `[data-preset=""]`: + +- `src/dashkit/dashkit.css`: Base engine and default token values. +- `src/dashkit/presets/brutalist.css`: Neo-Brutalist preset (`--radius: 0rem`, `--ds-pill-radius: 0rem`, `--ds-border-width: 2px`, hard offset shadows). +- `src/dashkit/presets/matrix.css`: Cyber Terminal preset (`--radius: 0.125rem`, phosphor green palette, CRT glow shadows, monospace font). +- `src/dashkit/presets/index.css`: Preset aggregator. From 1398c8191b29660b633748c251c72f7f8cfd32ed Mon Sep 17 00:00:00 2001 From: Carlos Costa Date: Thu, 12 Nov 2026 18:26:30 -0300 Subject: [PATCH 42/57] docs(tabs): document pills segmented control variant --- src/pages/tabs.mdx | 60 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/src/pages/tabs.mdx b/src/pages/tabs.mdx index f17afab..8703efb 100644 --- a/src/pages/tabs.mdx +++ b/src/pages/tabs.mdx @@ -52,6 +52,60 @@ The component is highly accessible and supports a fluid layout for its triggers. ``` +## Pills Variant (Segmented Control) + +Use `variant="pills"` to render tabs inside a compact, segmented track with an elevated floating indicator that smoothly slides between options. + +
+ + + Overview + Analytics + Reports + Notifications + + +
+

Platform Overview

+

General metric summary across all edge environments.

+
+
+ +
+

Live Analytics

+

Real-time latency, request rate, and error rate telemetry.

+
+
+ +
+

Generated Reports

+

Weekly and monthly compliance export records.

+
+
+ +
+

Notification Feeds

+

System warnings and deployment completion triggers.

+
+
+
+
+ +```tsx + + + Overview + Analytics + Reports + Notifications + + Overview metrics + Analytics telemetry + Generated reports + Notification feeds + +``` + ## Icons & Advanced Previews You can easily integrate icons and complex child layouts within your tabs. @@ -149,6 +203,12 @@ The indicator uses Framer Motion's `layoutId` to achieve a "sliding" effect when "defaultValue": "—", "description": "Callback fired when the active tab changes." }, + { + "prop": "variant", + "type": "'line' | 'pills'", + "defaultValue": "'line'", + "description": "Visual styling variant: 'line' for standard bottom-bordered tabs, 'pills' for segmented control." + }, { "prop": "children", "type": "ReactNode", From b51e0271ab8509ac2a128f760efa3a24161ecfb8 Mon Sep 17 00:00:00 2001 From: Carlos Costa Date: Thu, 12 Nov 2026 18:26:31 -0300 Subject: [PATCH 43/57] feat(theme): define presets configuration and token metadata --- src/partials/themeConfig.ts | 496 ++++++++++++++++++++++++++++++++++++ 1 file changed, 496 insertions(+) diff --git a/src/partials/themeConfig.ts b/src/partials/themeConfig.ts index 0ce80a3..4a2293b 100644 --- a/src/partials/themeConfig.ts +++ b/src/partials/themeConfig.ts @@ -1,3 +1,6 @@ +import brutalistCssRaw from '../dashkit/presets/brutalist.css?raw'; +import matrixCssRaw from '../dashkit/presets/matrix.css?raw'; + export type BaseColor = | 'slate' | 'gray' @@ -30,6 +33,8 @@ export type PrimaryColor = export type DarkBackground = 'midnight' | 'oled'; +export type ThemePreset = 'default' | 'brutalist' | 'matrix'; + export interface ColorOption { id: T; label: string; @@ -42,6 +47,41 @@ export interface DarkBgOption { description: string; } +export interface PresetTokenEntry { + variable: string; + category: 'Core & Surface' | 'Interactive' | 'Form & Geometry'; + description: string; + lightValue: string; + darkValue: string; + lightPreviewColor?: string; + darkPreviewColor?: string; +} + +export interface ThemePresetOption { + id: ThemePreset; + label: string; + tagline: string; + description: string; + baseColor: BaseColor; + primaryColor: PrimaryColor; + darkBg: DarkBackground; + preferredTheme?: 'light' | 'dark'; + radius: string; + radiusScale: { + sm: string; + md: string; + lg: string; + xl: string; + '2xl': string; + }; + fontLabel: string; + surfaceLabel: string; + accentHex: string; + presetFile: string; + tokens: PresetTokenEntry[]; + cssSnippet: string; +} + export const BASE_COLORS: ColorOption[] = [ { id: 'slate', label: 'Slate', color: '#64748b' }, { id: 'gray', label: 'Gray', color: '#6b7280' }, @@ -78,3 +118,459 @@ export const DARK_BG_OPTIONS: DarkBgOption[] = [ { id: 'midnight', label: 'Midnight', description: 'Deep graphite dark mode' }, { id: 'oled', label: 'OLED', description: 'Pure black #000000' }, ]; + +export const THEME_PRESETS: ThemePresetOption[] = [ + { + id: 'default', + label: 'Default', + tagline: 'Clean & balanced', + description: + 'Balanced 0.75rem geometry, Zinc neutral surfaces, Blue brand actions, and subtle depth gradients.', + baseColor: 'zinc', + primaryColor: 'blue', + darkBg: 'midnight', + radius: '0.75rem', + radiusScale: { + sm: '7px (calc(0.75rem - 5px))', + md: '9px (calc(0.75rem - 3px))', + lg: '12px (0.75rem)', + xl: '16px (calc(0.75rem * 1.3333))', + '2xl': '20px (calc(0.75rem * 1.6667))', + }, + fontLabel: 'Inter Variable (Sans)', + surfaceLabel: 'Subtle tactile gradient', + accentHex: '#2563eb', + presetFile: 'src/dashkit/dashkit.css', + tokens: [ + { + variable: '--ds-bg', + category: 'Core & Surface', + description: 'Main application background', + lightValue: 'var(--color-ds-100)', + darkValue: 'var(--color-ds-950)', + lightPreviewColor: '#f4f4f5', + darkPreviewColor: '#09090b', + }, + { + variable: '--ds-fg', + category: 'Core & Surface', + description: 'Main foreground text color', + lightValue: 'var(--color-ds-800)', + darkValue: 'var(--color-ds-100)', + lightPreviewColor: '#27272a', + darkPreviewColor: '#f4f4f5', + }, + { + variable: '--ds-card', + category: 'Core & Surface', + description: 'Surface color for Cards & containers', + lightValue: 'var(--color-ds-0)', + darkValue: 'var(--color-ds-900)', + lightPreviewColor: '#ffffff', + darkPreviewColor: '#18181b', + }, + { + variable: '--ds-border', + category: 'Core & Surface', + description: 'Default structural border token', + lightValue: 'var(--color-ds-300)', + darkValue: 'var(--color-ds-800)', + lightPreviewColor: '#d4d4d8', + darkPreviewColor: '#27272a', + }, + { + variable: '--ds-primary', + category: 'Interactive', + description: 'Primary brand action color', + lightValue: 'var(--color-ds-primary-600)', + darkValue: 'var(--color-ds-primary-500)', + lightPreviewColor: '#2563eb', + darkPreviewColor: '#3b82f6', + }, + { + variable: '--ds-secondary', + category: 'Interactive', + description: 'Secondary neutral action fill', + lightValue: 'var(--color-ds-200)', + darkValue: 'var(--color-ds-800)', + lightPreviewColor: '#e4e4e7', + darkPreviewColor: '#27272a', + }, + { + variable: '--ds-input-bg', + category: 'Form & Geometry', + description: 'Form control surface background', + lightValue: 'var(--color-ds-100)', + darkValue: 'var(--color-ds-800)', + lightPreviewColor: '#f4f4f5', + darkPreviewColor: '#27272a', + }, + { + variable: '--ds-input-border', + category: 'Form & Geometry', + description: 'Form control border color', + lightValue: 'var(--color-ds-300)', + darkValue: 'var(--color-ds-700)', + lightPreviewColor: '#d4d4d8', + darkPreviewColor: '#3f3f46', + }, + { + variable: '--radius', + category: 'Form & Geometry', + description: 'Master geometric corner radius', + lightValue: '0.75rem', + darkValue: '0.75rem', + }, + { + variable: '--ds-pill-radius', + category: 'Form & Geometry', + description: 'Pill radius for Buttons, Chips, Switches & Sliders', + lightValue: 'calc(var(--radius) * 9999)', + darkValue: 'calc(var(--radius) * 9999)', + }, + { + variable: '--ds-border-width', + category: 'Form & Geometry', + description: 'Structural border width for Cards, Buttons & Inputs', + lightValue: '1px', + darkValue: '1px', + }, + { + variable: '--ds-button-shadow', + category: 'Interactive', + description: 'Elevation / offset shadow for Button controls', + lightValue: '0 1px 2px 0 rgb(0 0 0 / 0.05)', + darkValue: '0 1px 2px 0 rgb(0 0 0 / 0.05)', + }, + { + variable: '--ds-card-shadow', + category: 'Core & Surface', + description: 'Default elevation / offset shadow for Cards', + lightValue: 'none', + darkValue: 'none', + }, + { + variable: '--ds-primary-gradient', + category: 'Form & Geometry', + description: 'Button fill elevation gradient', + lightValue: 'linear-gradient(to bottom, var(--ds-primary), ...)', + darkValue: 'linear-gradient(to bottom, var(--ds-primary), ...)', + }, + ], + cssSnippet: `:root { + --radius: 0.75rem; + --ds-pill-radius: calc(var(--radius) * 9999); + --ds-border-width: 1px; + --ds-button-border: transparent; + --ds-button-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05); + --ds-card-shadow: none; + --ds-input-shadow: none; + + --ds-bg: var(--color-ds-100); + --ds-fg: var(--color-ds-800); + --ds-card: var(--color-ds-0); + --ds-card-fg: var(--color-ds-900); + --ds-border: var(--color-ds-300); + + --ds-primary: var(--color-ds-primary-600); + --ds-primary-fg: var(--color-white); + + --ds-input-bg: var(--color-ds-100); + --ds-input-border: var(--color-ds-300); +} + +.dark { + --ds-bg: var(--color-ds-950); + --ds-fg: var(--color-ds-100); + --ds-card: var(--color-ds-900); + --ds-card-fg: var(--color-ds-100); + --ds-border: var(--color-ds-800); + + --ds-primary: var(--color-ds-primary-500); + --ds-input-bg: var(--color-ds-800); + --ds-input-border: var(--color-ds-700); +}`, + }, + { + id: 'brutalist', + label: 'Brutalism', + tagline: 'Sharp & high-contrast', + description: + 'Zero border-radius (0rem), flat solid fills without gradients, warm architectural paper surfaces, stark borders, and punchy Orange accents.', + baseColor: 'stone', + primaryColor: 'orange', + darkBg: 'midnight', + preferredTheme: 'light', + radius: '0rem', + radiusScale: { + sm: '0px (max(0px, 0rem - 5px))', + md: '0px (max(0px, 0rem - 3px))', + lg: '0px (0rem)', + xl: '0px (calc(0rem * 1.3333))', + '2xl': '0px (calc(0rem * 1.6667))', + }, + fontLabel: 'Inter Variable (Sans)', + surfaceLabel: 'Flat solid + Stark borders', + accentHex: '#ea580c', + presetFile: 'src/dashkit/presets/brutalist.css', + tokens: [ + { + variable: '--ds-bg', + category: 'Core & Surface', + description: 'Warm archival stone background', + lightValue: '#f5f2eb', + darkValue: '#121110', + lightPreviewColor: '#f5f2eb', + darkPreviewColor: '#121110', + }, + { + variable: '--ds-fg', + category: 'Core & Surface', + description: 'High-contrast ink foreground', + lightValue: 'var(--color-ds-950)', + darkValue: 'var(--color-ds-50)', + lightPreviewColor: '#0c0a09', + darkPreviewColor: '#fafaf9', + }, + { + variable: '--ds-card', + category: 'Core & Surface', + description: 'Crisp cream/charcoal card surface', + lightValue: '#fffdf9', + darkValue: '#1c1917', + lightPreviewColor: '#fffdf9', + darkPreviewColor: '#1c1917', + }, + { + variable: '--ds-border', + category: 'Core & Surface', + description: 'Stark architectural grid border', + lightValue: 'var(--color-ds-900)', + darkValue: 'var(--color-ds-300)', + lightPreviewColor: '#1c1917', + darkPreviewColor: '#d6d3d1', + }, + { + variable: '--ds-primary', + category: 'Interactive', + description: 'Industrial safety orange accent', + lightValue: 'var(--color-ds-primary-600)', + darkValue: 'var(--color-ds-primary-500)', + lightPreviewColor: '#ea580c', + darkPreviewColor: '#f97316', + }, + { + variable: '--ds-secondary', + category: 'Interactive', + description: 'Warm stone secondary surface', + lightValue: 'var(--color-ds-200)', + darkValue: 'var(--color-ds-800)', + lightPreviewColor: '#e7e5e4', + darkPreviewColor: '#292524', + }, + { + variable: '--ds-input-bg', + category: 'Form & Geometry', + description: 'High-contrast input fill', + lightValue: '#ffffff', + darkValue: '#0c0a09', + lightPreviewColor: '#ffffff', + darkPreviewColor: '#0c0a09', + }, + { + variable: '--ds-input-border', + category: 'Form & Geometry', + description: 'Stark ink border on form controls', + lightValue: 'var(--color-ds-900)', + darkValue: 'var(--color-ds-300)', + lightPreviewColor: '#1c1917', + darkPreviewColor: '#d6d3d1', + }, + { + variable: '--radius', + category: 'Form & Geometry', + description: 'Sharp 90-degree corners everywhere', + lightValue: '0rem', + darkValue: '0rem', + }, + { + variable: '--ds-pill-radius', + category: 'Form & Geometry', + description: 'Forces pill Buttons, Chips & Switches to 0px', + lightValue: '0rem', + darkValue: '0rem', + }, + { + variable: '--ds-border-width', + category: 'Form & Geometry', + description: 'Bold 2px structural borders on Cards, Buttons & Inputs', + lightValue: '2px', + darkValue: '2px', + }, + { + variable: '--ds-button-shadow', + category: 'Interactive', + description: 'Hard Neo-Brutalist offset shadow on Buttons', + lightValue: '3px 3px 0px 0px var(--ds-border)', + darkValue: '3px 3px 0px 0px var(--ds-border)', + }, + { + variable: '--ds-card-shadow', + category: 'Core & Surface', + description: 'Hard Neo-Brutalist offset shadow on Cards', + lightValue: '4px 4px 0px 0px var(--ds-border)', + darkValue: '4px 4px 0px 0px var(--ds-border)', + }, + { + variable: '--ds-primary-gradient', + category: 'Form & Geometry', + description: 'Pure flat solid buttons (no gradient)', + lightValue: 'none', + darkValue: 'none', + }, + ], + cssSnippet: brutalistCssRaw.trim(), + }, + { + id: 'matrix', + label: 'Matrix', + tagline: 'Cyber terminal', + description: + 'Phosphor-green terminal palette on deep OLED black, crisp 2px technical corners (0.125rem), flat surfaces, and full-interface monospace typography.', + baseColor: 'zinc', + primaryColor: 'green', + darkBg: 'oled', + preferredTheme: 'dark', + radius: '0.125rem', + radiusScale: { + sm: '0px (max(0px, 2px - 5px))', + md: '0px (max(0px, 2px - 3px))', + lg: '2px (0.125rem)', + xl: '2.6px (calc(0.125rem * 1.3333))', + '2xl': '3.3px (calc(0.125rem * 1.6667))', + }, + fontLabel: 'JetBrains Mono (Monospace)', + surfaceLabel: 'CRT Terminal Phosphor', + accentHex: '#22c55e', + presetFile: 'src/dashkit/presets/matrix.css', + tokens: [ + { + variable: '--ds-bg', + category: 'Core & Surface', + description: 'Deep CRT terminal background', + lightValue: '#ecfdf5', + darkValue: '#020804', + lightPreviewColor: '#ecfdf5', + darkPreviewColor: '#020804', + }, + { + variable: '--ds-fg', + category: 'Core & Surface', + description: 'Phosphor green foreground text & active indicators', + lightValue: '#052e16', + darkValue: '#4ade80', + lightPreviewColor: '#052e16', + darkPreviewColor: '#4ade80', + }, + { + variable: '--ds-color-0..950', + category: 'Core & Surface', + description: 'Monochromatic phosphor green scale (tracks, pagination, tabs)', + lightValue: '#ecfdf5 → #020804', + darkValue: '#4ade80 → #020804', + lightPreviewColor: '#bbf7d0', + darkPreviewColor: '#0a2612', + }, + { + variable: '--ds-card', + category: 'Core & Surface', + description: 'Terminal panel surface', + lightValue: '#f0fdf4', + darkValue: '#041007', + lightPreviewColor: '#f0fdf4', + darkPreviewColor: '#041007', + }, + { + variable: '--ds-border', + category: 'Core & Surface', + description: 'Matrix grid line border', + lightValue: '#16a34a', + darkValue: '#14532d', + lightPreviewColor: '#16a34a', + darkPreviewColor: '#14532d', + }, + { + variable: '--ds-primary', + category: 'Interactive', + description: 'Neon phosphor green action color', + lightValue: '#15803d', + darkValue: '#22c55e', + lightPreviewColor: '#15803d', + darkPreviewColor: '#22c55e', + }, + { + variable: '--ds-secondary', + category: 'Interactive', + description: 'Subtle console highlight fill', + lightValue: '#dcfce7', + darkValue: '#092210', + lightPreviewColor: '#dcfce7', + darkPreviewColor: '#092210', + }, + { + variable: '--ds-input-bg', + category: 'Form & Geometry', + description: 'Command-prompt input background', + lightValue: '#f0fdf4', + darkValue: '#030c05', + lightPreviewColor: '#f0fdf4', + darkPreviewColor: '#030c05', + }, + { + variable: '--ds-input-border', + category: 'Form & Geometry', + description: 'Terminal input border', + lightValue: '#16a34a', + darkValue: '#15803d', + lightPreviewColor: '#16a34a', + darkPreviewColor: '#15803d', + }, + { + variable: '--radius', + category: 'Form & Geometry', + description: 'Technical 2px micro-bevel radius', + lightValue: '0.125rem', + darkValue: '0.125rem', + }, + { + variable: '--ds-pill-radius', + category: 'Form & Geometry', + description: 'Replaces round pills with 2px terminal corners', + lightValue: 'var(--radius-md)', + darkValue: 'var(--radius-md)', + }, + { + variable: '--ds-button-shadow', + category: 'Interactive', + description: 'Phosphor CRT glow on Buttons', + lightValue: '0 0 10px -2px rgb(22 163 74 / 0.35)', + darkValue: '0 0 12px -2px rgb(34 197 94 / 0.45)', + }, + { + variable: '--ds-card-shadow', + category: 'Core & Surface', + description: 'Subtle phosphor ambient glow on Cards', + lightValue: '0 0 20px -8px rgb(22 163 74 / 0.2)', + darkValue: '0 0 24px -8px rgb(34 197 94 / 0.22)', + }, + { + variable: '--font-sans', + category: 'Form & Geometry', + description: 'Global monospace font override', + lightValue: 'var(--font-mono)', + darkValue: 'var(--font-mono)', + }, + ], + cssSnippet: matrixCssRaw.trim(), + }, +]; From 6206becdd779692d1761f2f671c410b24c7b637f Mon Sep 17 00:00:00 2001 From: Carlos Costa Date: Thu, 12 Nov 2026 18:26:32 -0300 Subject: [PATCH 44/57] feat(theme): add preset state and updater to ThemeContext --- src/partials/ThemeContext.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/partials/ThemeContext.ts b/src/partials/ThemeContext.ts index f55b44b..6ce5f91 100644 --- a/src/partials/ThemeContext.ts +++ b/src/partials/ThemeContext.ts @@ -1,8 +1,8 @@ import { createContext, useContext } from 'react'; -import type { BaseColor, PrimaryColor, DarkBackground } from './themeConfig'; +import type { BaseColor, PrimaryColor, DarkBackground, ThemePreset } from './themeConfig'; export type Theme = 'light' | 'dark'; -export type { BaseColor, PrimaryColor, DarkBackground }; +export type { BaseColor, PrimaryColor, DarkBackground, ThemePreset }; export interface ThemeContextType { theme: Theme; @@ -13,6 +13,8 @@ export interface ThemeContextType { setPrimaryColor: (primary: PrimaryColor) => void; darkBg: DarkBackground; setDarkBg: (bg: DarkBackground) => void; + preset: ThemePreset; + setPreset: (preset: ThemePreset) => void; } export const ThemeContext = createContext(undefined); From be84e3b7fafdd65903a9bef1041be898d3a10718 Mon Sep 17 00:00:00 2001 From: Carlos Costa Date: Thu, 12 Nov 2026 18:26:33 -0300 Subject: [PATCH 45/57] feat(theme): apply data-preset attribute and manage preset lifecycle --- src/partials/ThemeProvider.tsx | 47 ++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/src/partials/ThemeProvider.tsx b/src/partials/ThemeProvider.tsx index 46016d9..5c2a7dd 100644 --- a/src/partials/ThemeProvider.tsx +++ b/src/partials/ThemeProvider.tsx @@ -5,7 +5,9 @@ import { type BaseColor, type PrimaryColor, type DarkBackground, + type ThemePreset, } from './ThemeContext'; +import { THEME_PRESETS } from './themeConfig'; /** * Disables CSS transitions globally for a single tick during theme / palette switches. @@ -68,6 +70,14 @@ export function ThemeProvider({ children }: { children: ReactNode }) { return 'midnight'; }); + const [preset, setPresetState] = useState(() => { + if (typeof window !== 'undefined') { + const saved = localStorage.getItem('dashkit-preset') as ThemePreset; + if (saved && THEME_PRESETS.some(p => p.id === saved)) return saved; + } + return 'default'; + }); + const setBaseColor = (base: BaseColor) => { const restore = disableTransitions(); setBaseColorState(base); @@ -95,6 +105,40 @@ export function ThemeProvider({ children }: { children: ReactNode }) { restore(); }; + const setPreset = (nextPreset: ThemePreset) => { + const restore = disableTransitions(); + const config = THEME_PRESETS.find(p => p.id === nextPreset) ?? THEME_PRESETS[0]; + const root = window.document.documentElement; + + setPresetState(nextPreset); + root.setAttribute('data-preset', nextPreset); + localStorage.setItem('dashkit-preset', nextPreset); + + setBaseColorState(config.baseColor); + root.setAttribute('data-base', config.baseColor); + localStorage.setItem('dashkit-base-color', config.baseColor); + + setPrimaryColorState(config.primaryColor); + root.setAttribute('data-primary', config.primaryColor); + localStorage.setItem('dashkit-primary-color', config.primaryColor); + + setDarkBgState(config.darkBg); + root.setAttribute('data-dark-bg', config.darkBg); + localStorage.setItem('dashkit-dark-bg', config.darkBg); + + if (config.preferredTheme) { + setTheme(config.preferredTheme); + if (config.preferredTheme === 'dark') { + root.classList.add('dark'); + } else { + root.classList.remove('dark'); + } + localStorage.setItem('theme', config.preferredTheme); + } + + restore(); + }; + const toggleTheme = () => { const restore = disableTransitions(); setTheme(prev => { @@ -122,6 +166,7 @@ export function ThemeProvider({ children }: { children: ReactNode }) { root.setAttribute('data-base', baseColor); root.setAttribute('data-primary', primaryColor); root.setAttribute('data-dark-bg', darkBg); + root.setAttribute('data-preset', preset); }, []); return ( @@ -135,6 +180,8 @@ export function ThemeProvider({ children }: { children: ReactNode }) { setPrimaryColor, darkBg, setDarkBg, + preset, + setPreset, }} > {children} From a8b8e9b8a7b6573b5e9c460a715095abf9d12476 Mon Sep 17 00:00:00 2001 From: Carlos Costa Date: Thu, 12 Nov 2026 18:26:34 -0300 Subject: [PATCH 46/57] feat(theme): refactor customizer with preset list and clean tokens --- src/partials/ThemeCustomizer.tsx | 57 +++++++++++++++++++++++++++++--- 1 file changed, 52 insertions(+), 5 deletions(-) diff --git a/src/partials/ThemeCustomizer.tsx b/src/partials/ThemeCustomizer.tsx index badffbb..fa93dbd 100644 --- a/src/partials/ThemeCustomizer.tsx +++ b/src/partials/ThemeCustomizer.tsx @@ -2,6 +2,7 @@ import { useTheme } from './ThemeContext'; import { BASE_COLORS, PRIMARY_COLORS, + THEME_PRESETS, type BaseColor, type PrimaryColor, } from './themeConfig'; @@ -23,13 +24,14 @@ export function ThemeCustomizer({ className, size = 'sm' }: ThemeCustomizerProps setBaseColor, primaryColor, setPrimaryColor, + preset, + setPreset, } = useTheme(); - const isDefault = baseColor === 'zinc' && primaryColor === 'blue'; + const isDefault = preset === 'default' && baseColor === 'zinc' && primaryColor === 'blue'; const handleReset = () => { - setBaseColor('zinc'); - setPrimaryColor('blue'); + setPreset('default'); }; return ( @@ -44,12 +46,57 @@ export function ThemeCustomizer({ className, size = 'sm' }: ThemeCustomizerProps /> - +
{/* Header */}

Theme Customizer

-

Adjust base & brand colors in real-time

+

Switch design presets & palettes in real-time

+
+ + {/* Preset Section */} +
+
+ + Style Preset + + + {THEME_PRESETS.find(p => p.id === preset)?.label} + +
+
+ {THEME_PRESETS.map(p => { + const isSelected = preset === p.id; + return ( + + ); + })} +
{/* Primary Color Section */} From e8b9c3a939617fb3a644b4727130ca7a7ee13cc6 Mon Sep 17 00:00:00 2001 From: Carlos Costa Date: Thu, 12 Nov 2026 18:26:35 -0300 Subject: [PATCH 47/57] feat(docs): add interactive LLM-friendly customization playground --- src/partials/CustomizationPlayground.tsx | 333 +++++++++++++++++++++++ 1 file changed, 333 insertions(+) create mode 100644 src/partials/CustomizationPlayground.tsx diff --git a/src/partials/CustomizationPlayground.tsx b/src/partials/CustomizationPlayground.tsx new file mode 100644 index 0000000..5a27389 --- /dev/null +++ b/src/partials/CustomizationPlayground.tsx @@ -0,0 +1,333 @@ +import { useState } from 'react'; +import { useTheme } from './ThemeContext'; +import { + THEME_PRESETS, + BASE_COLORS, + PRIMARY_COLORS, + type BaseColor, + type PrimaryColor, +} from './themeConfig'; +import { + Card, + CardHeader, + CardTitle, + CardDescription, + CardContent, +} from '../dashkit/Card'; +import { Badge } from '../dashkit/Badge'; +import { Button } from '../dashkit/Button/Button'; +import { + Table, + TableHeader, + TableBody, + TableRow, + TableHead, + TableCell, +} from '../dashkit/Table/Table'; +import { FiCheck, FiCopy, FiRotateCcw, FiSun, FiMoon } from 'react-icons/fi'; + +export function CustomizationPlayground() { + const { + theme, + toggleTheme, + baseColor, + setBaseColor, + primaryColor, + setPrimaryColor, + preset, + setPreset, + } = useTheme(); + + const [copiedCss, setCopiedCss] = useState(false); + + const activePreset = + THEME_PRESETS.find(p => p.id === preset) ?? THEME_PRESETS[0]; + + const handleCopyCss = async () => { + try { + await navigator.clipboard.writeText(activePreset.cssSnippet); + setCopiedCss(true); + setTimeout(() => setCopiedCss(false), 2000); + } catch (err) { + console.error('Failed to copy CSS snippet:', err); + } + }; + + return ( +
+ {/* 1. Preset Switcher Card */} + + +
+
+ Interactive Theme Presets + + Select a preset below to transform the entire documentation site and inspect its live CSS tokens. + +
+
+ + +
+
+
+ + + {/* Preset Option Cards (Stacked List) */} +
+ {THEME_PRESETS.map(item => { + const isSelected = preset === item.id; + return ( + + ); + })} +
+ + {/* Palette Fine-Tuning */} +
+
+ + Primary Brand Palette (`data-primary="{primaryColor}"`) + +
+ {PRIMARY_COLORS.map(c => { + const isSelected = primaryColor === c.id; + return ( + + ); + })} +
+
+ +
+ + Base Neutral Palette (`data-base="{baseColor}"`) + +
+ {BASE_COLORS.map(c => { + const isSelected = baseColor === c.id; + return ( + + ); + })} +
+
+
+
+
+ + {/* 2. Dynamic Core Variables Table */} + + +
+
+ + Active Design Tokens — {activePreset.label} Preset + + + Real-time CSS custom properties currently powering the interface in Light and Dark modes. + +
+ +
+
+ + + + + Token Variable + Category + Light Value (`:root`) + Dark Value (`.dark`) + Role + + + + {activePreset.tokens.map(token => ( + + + {token.variable} + + + + + + + {token.lightPreviewColor && ( + + )} + {token.lightValue} + + + + + {token.darkPreviewColor && ( + + )} + {token.darkValue} + + + + {token.description} + + + ))} + +
+
+
+ + {/* 3. Dynamic Radius Scale & Generated CSS */} +
+ + + Proportional Radius Scale + + All component corner tokens derive automatically from{' '} + + --radius: {activePreset.radius} + + . + + + + {( + [ + ['--radius-sm', activePreset.radiusScale.sm, 'ds-rounded-sm'], + ['--radius-md', activePreset.radiusScale.md, 'ds-rounded-md'], + ['--radius-lg', activePreset.radiusScale.lg, 'ds-rounded-lg'], + ['--radius-xl', activePreset.radiusScale.xl, 'ds-rounded-xl'], + ['--radius-2xl', activePreset.radiusScale['2xl'], 'ds-rounded-2xl'], + ] as const + ).map(([tokenName, computedText, radiusClass]) => ( +
+
+
+
+
+ {tokenName} +
+
+ {computedText} +
+
+
+ +
+ ))} + + + + + +
+
+ Preset Source — `{activePreset.presetFile}` + + Self-contained CSS token delta for {activePreset.label}. Copy into src/dashkit/dashkit.css or import from {activePreset.presetFile}. + +
+ +
+
+ +
+              {activePreset.cssSnippet}
+            
+
+
+
+
+ ); +} From 7b8ffc73de1938267f90940aa157e87c67bce189 Mon Sep 17 00:00:00 2001 From: Carlos Costa Date: Thu, 12 Nov 2026 18:26:36 -0300 Subject: [PATCH 48/57] feat(showcase): integrate tabs pills preset switcher into showcase --- src/partials/HomeExamples.tsx | 61 +++++++++++++++++++++++++++++++++-- 1 file changed, 59 insertions(+), 2 deletions(-) diff --git a/src/partials/HomeExamples.tsx b/src/partials/HomeExamples.tsx index 7f6b778..4529882 100644 --- a/src/partials/HomeExamples.tsx +++ b/src/partials/HomeExamples.tsx @@ -1,14 +1,71 @@ +import { Link } from 'react-router-dom'; +import { FiArrowUpRight } from 'react-icons/fi'; import { Tabs, TabsList, TabsTrigger, TabsContent } from '../dashkit/Tabs/Tabs'; +import { Button } from '../dashkit/Button/Button'; +import { useTheme } from './ThemeContext'; +import { THEME_PRESETS, type ThemePreset } from './themeConfig'; import { ColumnOne } from './home-examples/ColumnOne'; import { ColumnTwo } from './home-examples/ColumnTwo'; import { ColumnThree } from './home-examples/ColumnThree'; import { ColumnFour } from './home-examples/ColumnFour'; export function HomeExamples() { + const { preset, setPreset } = useTheme(); + const activePreset = + THEME_PRESETS.find(p => p.id === preset) ?? THEME_PRESETS[0]; + return ( <> + {/* Live Theme Preset Switcher Bar */} +
+
+ {/* Official Dashkit Tabs (variant="pills") */} +
+ setPreset(val as ThemePreset)} + > + + {THEME_PRESETS.map(item => ( + + + {item.label} + + ))} + + +
+ + {/* Preset Metadata & Docs Action */} +
+ + {activePreset.tagline} + / + {activePreset.radius} + + + +
+
+
+ {/* Mobile Tabbed View (< 768px) */} -
+
Forms @@ -32,7 +89,7 @@ export function HomeExamples() {
{/* Desktop Multi-column Grid (>= 768px) */} -
+
From 5bf31863642ab0d0a5490eb3d9c6a967efebf2dc Mon Sep 17 00:00:00 2001 From: Carlos Costa Date: Thu, 12 Nov 2026 18:26:37 -0300 Subject: [PATCH 49/57] fix(showcase): adapt home column one to dynamic tokens --- src/partials/home-examples/ColumnOne.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/partials/home-examples/ColumnOne.tsx b/src/partials/home-examples/ColumnOne.tsx index fe1b9d5..4b53499 100644 --- a/src/partials/home-examples/ColumnOne.tsx +++ b/src/partials/home-examples/ColumnOne.tsx @@ -473,7 +473,7 @@ export function ColumnOne() {
- + Delivery Active (200 OK)
Automatic backup snapshot daily - Encrypted + Encrypted