xertica-ui 2.7.0 → 2.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +16 -0
- package/README.md +19 -11
- package/bin/generate-tokens.ts +25 -9
- package/components/assistant/xertica-assistant/xertica-assistant.tsx +1 -1
- package/components/ui/button/button.test.tsx +2 -2
- package/components/ui/button/button.tsx +6 -6
- package/components/ui/card/card.tsx +14 -2
- package/components/ui/input/input.tsx +1 -1
- package/components/ui/input-otp/input-otp.tsx +1 -1
- package/components/ui/search/search.tsx +1 -1
- package/components/ui/select/select.tsx +2 -2
- package/components/ui/switch/switch.tsx +1 -1
- package/components/ui/textarea/textarea.tsx +1 -1
- package/contexts/BrandColorsContext.tsx +50 -2
- package/contexts/theme-data.ts +100 -3
- package/dist/AssistantChart-BFBJXVcm.js +3383 -0
- package/dist/AssistantChart-BTnE94FR.cjs +3551 -0
- package/dist/AssistantChart-BvccsQMx.cjs +3551 -0
- package/dist/AssistantChart-CkskcBVD.js +3383 -0
- package/dist/AssistantChart-CrPkO9ip.js +3383 -0
- package/dist/AssistantChart-DKz2O1R8.cjs +3551 -0
- package/dist/AssistantChart-DYL6UEvo.cjs +3551 -0
- package/dist/AssistantChart-keo-ogKi.js +3383 -0
- package/dist/AudioPlayer-AoU5MTiM.cjs +1282 -0
- package/dist/AudioPlayer-Bx1vrblm.js +1278 -0
- package/dist/AudioPlayer-D0-I6J3J.cjs +1282 -0
- package/dist/AudioPlayer-DIWX_Yg7.cjs +1282 -0
- package/dist/AudioPlayer-DrWJnS7c.js +1278 -0
- package/dist/AudioPlayer-_P-Z7pAK.js +1278 -0
- package/dist/BrandColorsContext-B25Bpr3b.cjs +819 -0
- package/dist/BrandColorsContext-BCieATdW.js +812 -0
- package/dist/BrandColorsContext-DP0DXAXS.js +824 -0
- package/dist/BrandColorsContext-DjpC9wR_.cjs +798 -0
- package/dist/BrandColorsContext-MpXFhte7.cjs +831 -0
- package/dist/BrandColorsContext-V3tOc-Zw.js +791 -0
- package/dist/CodeBlock-CK4rO6lM.js +221 -0
- package/dist/CodeBlock-CKXamfVd.cjs +223 -0
- package/dist/CodeBlock-CVmE7isS.cjs +223 -0
- package/dist/CodeBlock-D7i5-cMv.js +221 -0
- package/dist/CodeBlock-Dg56j2ut.cjs +223 -0
- package/dist/CodeBlock-Dt6nUQoo.js +221 -0
- package/dist/CustomTooltipContent-Bxynuq7T.js +33 -0
- package/dist/CustomTooltipContent-BzjOSA0P.cjs +54 -0
- package/dist/CustomTooltipContent-C-ZeCNsf.js +33 -0
- package/dist/CustomTooltipContent-CON_QrXm.cjs +54 -0
- package/dist/CustomTooltipContent-UR_n2QfW.js +33 -0
- package/dist/CustomTooltipContent-v1SXjoFn.cjs +54 -0
- package/dist/FeatureCard-4n_8a63v.cjs +497 -0
- package/dist/FeatureCard-BLiAWT3w.js +485 -0
- package/dist/FeatureCard-BXRW97Ef.cjs +497 -0
- package/dist/FeatureCard-CDgLlWE2.js +485 -0
- package/dist/FeatureCard-CgQ8lq-I.cjs +497 -0
- package/dist/FeatureCard-Cp8cVC6T.cjs +497 -0
- package/dist/FeatureCard-D0MwrTRP.js +485 -0
- package/dist/FeatureCard-uE5i0in_.js +485 -0
- package/dist/ImageWithFallback-BOh1FM3I.js +98 -0
- package/dist/ImageWithFallback-BQO7952C.js +98 -0
- package/dist/ImageWithFallback-BkMXHCvV.cjs +122 -0
- package/dist/ImageWithFallback-C72nwszA.cjs +122 -0
- package/dist/ImageWithFallback-CCUMDuEC.cjs +122 -0
- package/dist/ImageWithFallback-DQD5TTlY.js +98 -0
- package/dist/LanguageSelector-1XhM7R1i.cjs +48 -0
- package/dist/LanguageSelector-B3ZVNc55.js +46 -0
- package/dist/LanguageSelector-Cj6zl0CM.js +46 -0
- package/dist/LanguageSelector-DK1zD4VH.cjs +48 -0
- package/dist/LanguageSelector-DseCAa0p.cjs +48 -0
- package/dist/LanguageSelector-Du09anfM.js +46 -0
- package/dist/LanguageSelector-PZHlFfEs.cjs +48 -0
- package/dist/LanguageSelector-SB-NJSmx.js +46 -0
- package/dist/PasswordStrengthMeter-B3S6i_k4.cjs +179 -0
- package/dist/PasswordStrengthMeter-B8ky1nJI.js +173 -0
- package/dist/PasswordStrengthMeter-C5VEopH-.cjs +179 -0
- package/dist/PasswordStrengthMeter-CIm7_jNT.js +173 -0
- package/dist/PasswordStrengthMeter-DKSAbQzu.js +173 -0
- package/dist/PasswordStrengthMeter-DxLu54p-.cjs +179 -0
- package/dist/PasswordStrengthMeter-k-e9Pole.cjs +179 -0
- package/dist/PasswordStrengthMeter-pAi2viq5.js +173 -0
- package/dist/ThemeToggle-4pQbVkpK.js +215 -0
- package/dist/ThemeToggle-B6rIVLvC.cjs +219 -0
- package/dist/ThemeToggle-BUOTps5l.cjs +219 -0
- package/dist/ThemeToggle-CO5mPl6e.js +215 -0
- package/dist/ThemeToggle-DbZQ0JSV.cjs +219 -0
- package/dist/ThemeToggle-vLXEq2TS.js +215 -0
- package/dist/VerifyEmailPage-BTjI2c9n.js +2954 -0
- package/dist/VerifyEmailPage-CJGfDSOx.js +2954 -0
- package/dist/VerifyEmailPage-DOlZUNc0.cjs +2963 -0
- package/dist/VerifyEmailPage-DQPRl_RJ.js +2954 -0
- package/dist/VerifyEmailPage-DrdqMbLC.cjs +2963 -0
- package/dist/VerifyEmailPage-hyDfeCRa.cjs +2963 -0
- package/dist/VerifyEmailPage-kKSXl8vu.cjs +2963 -0
- package/dist/VerifyEmailPage-w4N-bDhU.js +2954 -0
- package/dist/XerticaOrbe-B9tegV1n.js +1925 -0
- package/dist/XerticaOrbe-BQ_NjwPS.cjs +1927 -0
- package/dist/XerticaOrbe-BdUX171Q.cjs +1927 -0
- package/dist/XerticaOrbe-BdlKXRHU.cjs +1927 -0
- package/dist/XerticaOrbe-CByPAFE5.js +1925 -0
- package/dist/XerticaOrbe-Ccu9eFMu.js +1925 -0
- package/dist/XerticaProvider-BVid1oqn.cjs +46 -0
- package/dist/XerticaProvider-Bz_YfNY4.js +44 -0
- package/dist/XerticaProvider-CtvYfRbZ.cjs +46 -0
- package/dist/XerticaProvider-DMiyExQS.js +44 -0
- package/dist/XerticaProvider-DP2lzKip.cjs +46 -0
- package/dist/XerticaProvider-De79hhgp.js +44 -0
- package/dist/XerticaProvider-Dkk1Vbsy.js +44 -0
- package/dist/XerticaProvider-EdzH9Nrw.cjs +46 -0
- package/dist/alert-dialog-9XYAn5z5.js +819 -0
- package/dist/alert-dialog-B281hud0.cjs +872 -0
- package/dist/alert-dialog-BVWbrcrw.cjs +872 -0
- package/dist/alert-dialog-B_Ko1xpu.js +819 -0
- package/dist/alert-dialog-CIxE8T-J.cjs +872 -0
- package/dist/alert-dialog-DW-_iPuf.js +819 -0
- package/dist/assistant.cjs.js +2 -2
- package/dist/assistant.es.js +2 -2
- package/dist/avatar-BJwIMbZ_.js +54 -0
- package/dist/avatar-C4Zh5fAJ.cjs +77 -0
- package/dist/avatar-CTEUKZUT.js +54 -0
- package/dist/avatar-D1bZhlhL.cjs +77 -0
- package/dist/avatar-DKIxHeKG.cjs +77 -0
- package/dist/avatar-v16eZC8p.js +54 -0
- package/dist/blocks.cjs.js +2 -2
- package/dist/blocks.es.js +2 -2
- package/dist/brand.cjs.js +4 -4
- package/dist/brand.es.js +4 -4
- package/dist/breadcrumb-B1mUiaWB.cjs +98 -0
- package/dist/breadcrumb-BIRgmem-.js +90 -0
- package/dist/breadcrumb-BSng-oUf.cjs +98 -0
- package/dist/breadcrumb-CES7furU.js +90 -0
- package/dist/breadcrumb-CP275kbF.cjs +98 -0
- package/dist/breadcrumb-D7hr1EvJ.js +90 -0
- package/dist/button-BYtQygX5.cjs +85 -0
- package/dist/button-BnAW-a_w.js +62 -0
- package/dist/button-CUPDQews.js +62 -0
- package/dist/button-DFa-rtPy.js +62 -0
- package/dist/button-DV1G6XrI.cjs +85 -0
- package/dist/button-Kd9Fj53S.cjs +85 -0
- package/dist/cli.js +94 -10
- package/dist/components/ui/card/card.d.ts +11 -1
- package/dist/contexts/theme-data.d.ts +15 -1
- package/dist/dropdown-menu-B9wTZ0uX.cjs +225 -0
- package/dist/dropdown-menu-BeBtZOrs.cjs +225 -0
- package/dist/dropdown-menu-D7mRDGis.js +190 -0
- package/dist/dropdown-menu-D8ZmqHB7.js +190 -0
- package/dist/dropdown-menu-_AWBeOts.cjs +225 -0
- package/dist/dropdown-menu-qGzLO_HO.js +190 -0
- package/dist/hooks.cjs.js +1 -1
- package/dist/hooks.es.js +1 -1
- package/dist/index.cjs.js +24 -24
- package/dist/index.es.js +24 -24
- package/dist/input-B53E6K8G.js +65 -0
- package/dist/input-B7Q7vcoG.cjs +91 -0
- package/dist/input-BysqLMK1.js +65 -0
- package/dist/input-CStdFsaH.cjs +91 -0
- package/dist/input-CXYth6nW.cjs +91 -0
- package/dist/input-CX_fWE2D.cjs +91 -0
- package/dist/input-CitUVnEn.js +65 -0
- package/dist/input-wkBYTp-B.js +65 -0
- package/dist/layout.cjs.js +1 -1
- package/dist/layout.es.js +1 -1
- package/dist/media.cjs.js +1 -1
- package/dist/media.es.js +1 -1
- package/dist/pages.cjs.js +1 -1
- package/dist/pages.es.js +1 -1
- package/dist/rich-text-editor-BHiRUzWZ.js +2894 -0
- package/dist/rich-text-editor-BWMRUMJ2.js +2894 -0
- package/dist/rich-text-editor-BoE6Ks5x.cjs +2968 -0
- package/dist/rich-text-editor-BzuEwR_X.js +2894 -0
- package/dist/rich-text-editor-CcopGbQ-.js +2894 -0
- package/dist/rich-text-editor-CmxSRJIi.cjs +2968 -0
- package/dist/rich-text-editor-DyYAu28v.cjs +2968 -0
- package/dist/rich-text-editor-JS_eMQCC.cjs +2968 -0
- package/dist/select-BNfFJwYp.js +130 -0
- package/dist/select-CN8IDjGB.js +130 -0
- package/dist/select-CwRzQHA7.cjs +161 -0
- package/dist/select-CwgD3nMD.js +130 -0
- package/dist/select-DEHUm_AC.cjs +161 -0
- package/dist/select-DJDgpVm3.cjs +161 -0
- package/dist/select-DSLWQLmo.cjs +161 -0
- package/dist/select-YFx0wCYT.js +130 -0
- package/dist/sidebar-BF7JEL7U.js +1151 -0
- package/dist/sidebar-Bjr2doX5.cjs +1154 -0
- package/dist/sidebar-CMrNCmhb.cjs +1154 -0
- package/dist/sidebar-CO9x8AVn.cjs +1154 -0
- package/dist/sidebar-Cn_KB4c6.js +1151 -0
- package/dist/sidebar-Db_C30ph.cjs +1154 -0
- package/dist/sidebar-miPHsRz4.js +1151 -0
- package/dist/sidebar-pZF8mLLu.js +1151 -0
- package/dist/skeleton-BX2XTjMO.cjs +87 -0
- package/dist/skeleton-BoLK0iV7.js +78 -0
- package/dist/skeleton-Bysq8yeB.js +79 -0
- package/dist/skeleton-C7nspYnC.cjs +87 -0
- package/dist/skeleton-DL4V5Vub.cjs +87 -0
- package/dist/skeleton-DSMhdqp1.js +78 -0
- package/dist/skeleton-DhJswvTW.cjs +88 -0
- package/dist/skeleton-DicUfozA.js +78 -0
- package/dist/slider-77T_Eqw4.js +56 -0
- package/dist/slider-B6puMLXS.cjs +78 -0
- package/dist/slider-B785N7lZ.js +56 -0
- package/dist/slider-C6yssKyZ.cjs +78 -0
- package/dist/slider-CKZO9gVh.cjs +78 -0
- package/dist/slider-Cn-ImSze.js +56 -0
- package/dist/tooltip-BiJVGob3.cjs +72 -0
- package/dist/tooltip-D2OPn-1v.cjs +72 -0
- package/dist/tooltip-DlCHQuh1.js +48 -0
- package/dist/tooltip-fQ2Va0G8.cjs +72 -0
- package/dist/tooltip-oAw00FX2.js +48 -0
- package/dist/tooltip-u147L0wD.js +48 -0
- package/dist/ui.cjs.js +13 -13
- package/dist/ui.es.js +13 -13
- package/dist/xertica-assistant-C2A0R94j.js +2173 -0
- package/dist/xertica-assistant-CSUprvTR.js +2173 -0
- package/dist/xertica-assistant-Caubm0HR.js +2173 -0
- package/dist/xertica-assistant-DEexPM2u.cjs +2180 -0
- package/dist/xertica-assistant-Dh1dmEx3.cjs +2180 -0
- package/dist/xertica-assistant-GGcyHEZG.cjs +2180 -0
- package/dist/xertica-assistant-USefPe2L.js +2173 -0
- package/dist/xertica-assistant-cwSHFwfv.cjs +2180 -0
- package/dist/xertica-ui.css +1 -1
- package/package.json +3 -2
- package/styles/xertica/base.css +38 -0
- package/styles/xertica/tokens.css +9 -0
- package/templates/src/app/App.tsx +5 -1
- package/templates/src/features/home/ui/HomeContent.tsx +12 -13
- package/templates/src/features/template/ui/TemplateContent.tsx +569 -26
- package/templates/src/locales/en/pages/templates.json +112 -4
- package/templates/src/locales/es/pages/templates.json +112 -4
- package/templates/src/locales/pt-BR/pages/templates.json +112 -4
- package/templates/src/styles/xertica/tokens.css +9 -0
- package/templates/vite.config.ts +25 -0
package/CHANGELOG.md
CHANGED
|
@@ -11,6 +11,22 @@ Versioning follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
11
11
|
|
|
12
12
|
---
|
|
13
13
|
|
|
14
|
+
## [2.8.0] — 2026-07-09
|
|
15
|
+
|
|
16
|
+
### Added
|
|
17
|
+
|
|
18
|
+
- **Novo tema `xertica`** — a nova identidade visual da Xertica (papel quente `#FFFEF8`, tintas vivas e o preto da marca como acento geral), somando-se aos temas já existentes em `contexts/theme-data.ts` → `colorThemes`. Botões neste tema ganham radius pill (`buttonRadius: '9999px'`), stroke em todas as variantes preenchidas (`buttonStrokeWidth`/`buttonStrokeColor(Dark)`), botão primário amarelo (`#FAF338`) com contorno e texto pretos, e botão secundário na cor preta da marca (`buttonSecondaryBg(Dark)`/`buttonSecondaryForeground(Dark)`). O amarelo é **intencionalmente restrito** ao botão `default` e ao estado ligado do `Switch` — o acento geral do tema (`--primary`, usado por links, radio buttons, foco, logo, etc.) é Negro Xertica `#1E1E1E` (Crema `#F2EDD8` no dark mode), evitado de virar amarelo sem contraste em elementos que não são botão/switch. O tema `xertica-original` (agora exibido como **"Xertica Classic"**) permanece o padrão — o `id` não foi alterado, então projetos scaffolded e chamadas `setTheme('xertica-original')` continuam funcionando sem mudanças.
|
|
19
|
+
- **`BrandColors` ganha campos opcionais de forma/estrutura de botão** — `backgroundLight`, `buttonRadius`, `buttonStrokeWidth`, `buttonStrokeColor`/`buttonStrokeColorDark`, `buttonSecondaryBg`/`buttonSecondaryBgDark`, `buttonSecondaryForeground`/`buttonSecondaryForegroundDark`, e `buttonPrimaryBg`/`buttonPrimaryBgDark`, `buttonPrimaryForeground`/`buttonPrimaryForegroundDark` (acento do botão `default` e do `Switch`, com fallback para `var(--primary)`/`var(--primary-foreground)`). Todos são opcionais e têm fallback idêntico ao comportamento anterior, então os 9 temas pré-existentes ficam pixel-idênticos. Removido o campo morto `radius?: string` (nunca lido por `applyColors()`).
|
|
20
|
+
- **`components/ui/button/button.tsx`** — as variantes preenchidas (`default`, `destructive`, `secondary`, `success`, `info`, `warning`) agora aceitam um stroke via `--button-stroke-width`/`--button-stroke-color`; a variante `default` passou a usar `--button-primary-bg`/`--button-primary-foreground` (em vez de `bg-primary`/`text-primary-foreground` direto) para permitir um acento de botão distinto do `--primary` semântico geral; a variante `secondary` passou a usar os tokens `--button-secondary-bg`/`--button-secondary-foreground` (com fallback para `--secondary`/`--secondary-foreground`) em vez do modificador de opacidade `/80`, que não funcionava sobre cores vindas de `var()`.
|
|
21
|
+
- **`components/ui/switch/switch.tsx`** — o estado marcado (`checked`) passou a usar `--button-primary-bg` em vez de `bg-primary`, para acompanhar o mesmo acento do botão `default`.
|
|
22
|
+
|
|
23
|
+
### Changed
|
|
24
|
+
|
|
25
|
+
- `bin/generate-tokens.ts` e os `tokens.css` estáticos (`styles/xertica/`, `templates/src/styles/xertica/`) passam a emitir `--button-stroke-width`, `--button-stroke-color`, `--button-secondary-bg`, `--button-secondary-foreground`, `--button-primary-bg`, `--button-primary-foreground`, e `--radius-button`/`--background` (light) respeitam os novos campos do tema quando definidos.
|
|
26
|
+
- `.storybook/preview.tsx` — toolbar "Brand Color" renomeia `xertica-original` para "Xertica Classic" e adiciona a entrada "Xertica" (`xertica`).
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
14
30
|
## [2.7.0] — 2026-07-07
|
|
15
31
|
|
|
16
32
|
### Added
|
package/README.md
CHANGED
|
@@ -353,11 +353,12 @@ Xertica UI uses semantic CSS tokens. **Never use raw colors or generic Tailwind
|
|
|
353
353
|
|
|
354
354
|
### Color Themes
|
|
355
355
|
|
|
356
|
-
|
|
356
|
+
Ten built-in themes — each sets the primary color, sidebar, chart palette, and dark-mode surface hue. Select at `init` time or change later with `npx xertica-ui update → Theme only`.
|
|
357
357
|
|
|
358
358
|
| Theme | ID | Primary | Sidebar | Dark bg |
|
|
359
359
|
|---|---|---|---|---|
|
|
360
|
-
|  Xertica | `xertica-original` | `#2C275B` | `#2C275B` | `#05050d` |
|
|
360
|
+
|  Xertica Classic | `xertica-original` | `#2C275B` | `#2C275B` | `#05050d` |
|
|
361
|
+
|  Xertica | `xertica` | `#1E1E1E` | `#1E1E1E` | `#141311` |
|
|
361
362
|
|  Zinc | `zinc` | `#18181B` | `#18181B` | `#05050d` |
|
|
362
363
|
|  Slate | `slate` | `#0F172A` | `#0F172A` | `#05050d` |
|
|
363
364
|
|  Blue | `blue` | `#2563EB` | `#1E3A8A` | `#03050f` |
|
|
@@ -369,6 +370,8 @@ Nine built-in themes — each sets the primary color, sidebar, chart palette, an
|
|
|
369
370
|
|
|
370
371
|
> Each badge renders the **exact primary color** of that theme as a filled swatch. Generated by the CLI and stored in `src/styles/xertica/tokens.css`.
|
|
371
372
|
|
|
373
|
+
**"Xertica" theme — serigrafia identity:** pill-shaped buttons (`--radius-button: 9999px`) and a visible stroke on every filled button variant. The general brand accent (`--primary`, used by links, focus rings, radio buttons, the logo, etc.) is Negro Xertica (`#1E1E1E`, `#F2EDD8` in dark mode) for reliable contrast everywhere. Yellow (`#FAF338`) is intentionally scoped to just two controls — the `Button` `default` variant and `Switch`'s checked state — via the `--button-primary-bg`/`--button-primary-foreground` tokens, both with black text/outline. The `secondary` button is black (`#1E1E1E`). The page background uses a warm off-white paper tone (`#FFFEF8`) while cards stay pure white. The full accent palette (and the "serigrafia" color-intersection combinations meant for print/marketing pieces — e.g. Celeste `#1899AF` + Magenta `#C45BAA` → Violeta `#5A4A96`) is documented in `contexts/theme-data.ts`.
|
|
374
|
+
|
|
372
375
|
**CLI — Select at project creation:**
|
|
373
376
|
|
|
374
377
|
```bash
|
|
@@ -381,7 +384,7 @@ npx xertica-ui@latest init my-app
|
|
|
381
384
|
```bash
|
|
382
385
|
npx xertica-ui update
|
|
383
386
|
# → choose "Theme only"
|
|
384
|
-
# → select any of the
|
|
387
|
+
# → select any of the 10 themes
|
|
385
388
|
# → src/styles/xertica/tokens.css is fully regenerated
|
|
386
389
|
# → .xertica.json is updated with the new themeId for future updates
|
|
387
390
|
```
|
|
@@ -396,6 +399,7 @@ npx xertica-ui update
|
|
|
396
399
|
<XerticaProvider defaultColorTheme="violet">
|
|
397
400
|
<XerticaProvider defaultColorTheme="amber">
|
|
398
401
|
<XerticaProvider defaultColorTheme="orange">
|
|
402
|
+
<XerticaProvider defaultColorTheme="xertica"> {/* new serigrafia identity */}
|
|
399
403
|
<XerticaProvider defaultColorTheme="xertica-original"> {/* default */}
|
|
400
404
|
|
|
401
405
|
// Custom hex — sets primary only; dark surfaces use the neutral default
|
|
@@ -414,7 +418,8 @@ npx xertica-ui update
|
|
|
414
418
|
| `--card`, `--popover`| ✅ Dark mode only (hue-tinted) |
|
|
415
419
|
| `--muted`, `--accent`| ✅ Dark mode only (hue-tinted) |
|
|
416
420
|
| `--border`, `--input`| ✅ Dark mode only (hue-tinted) |
|
|
417
|
-
| Light mode surfaces | ❌ Always white/zinc (
|
|
421
|
+
| Light mode surfaces | ❌ Always white/zinc, unless the theme sets `backgroundLight` (only `xertica` does) |
|
|
422
|
+
| `--radius-button`, button stroke, `secondary` button colors | ✅ Only when the theme defines `buttonRadius`/`buttonStroke*`/`buttonSecondary*` (only `xertica` does) |
|
|
418
423
|
|
|
419
424
|
### Mobile Content Padding
|
|
420
425
|
|
|
@@ -467,13 +472,16 @@ See [`docs/i18n.md`](./docs/i18n.md) for setup, runtime configuration, and addin
|
|
|
467
472
|
|
|
468
473
|
## 📜 Scripts
|
|
469
474
|
|
|
470
|
-
| Command
|
|
471
|
-
|
|
|
472
|
-
| `npm run dev`
|
|
473
|
-
| `npm run
|
|
474
|
-
| `npm run
|
|
475
|
-
| `npm run
|
|
476
|
-
| `npm run
|
|
475
|
+
| Command | Description |
|
|
476
|
+
| ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
477
|
+
| `npm run dev` | Runs the `templates/` scaffold with live Design System source (via monorepo alias) — the same app a `npx xertica-ui init` consumer gets. Use this to test Design System changes against real pages. |
|
|
478
|
+
| `npm run dev:pages` | Runs the root harness (`App.tsx`) for the standalone `xertica-ui/pages` showcase subpath. |
|
|
479
|
+
| `npm run build` | Production bundle |
|
|
480
|
+
| `npm run storybook` | Launch component library documentation |
|
|
481
|
+
| `npm run test` | Run unit tests via Vitest |
|
|
482
|
+
| `npm run type-check` | TypeScript validation |
|
|
483
|
+
|
|
484
|
+
> **Note**: `templates/` has its own dependencies — run `npm install` inside `templates/` once before the first `npm run dev`.
|
|
477
485
|
|
|
478
486
|
---
|
|
479
487
|
|
package/bin/generate-tokens.ts
CHANGED
|
@@ -42,13 +42,13 @@ export const generateTokensCss = (theme: ColorTheme): string => {
|
|
|
42
42
|
--xertica-dark: ${rgba(colors.sidebarDark)};
|
|
43
43
|
|
|
44
44
|
/* Semantic Colors */
|
|
45
|
-
--background: rgba(255, 255, 255, 1);
|
|
45
|
+
--background: ${colors.backgroundLight ? rgba(colors.backgroundLight) : 'rgba(255, 255, 255, 1)'};
|
|
46
46
|
--foreground: rgba(9, 9, 11, 1);
|
|
47
|
-
|
|
48
|
-
--card: rgba(255, 255, 255, 1);
|
|
47
|
+
|
|
48
|
+
--card: ${colors.cardLight ? rgba(colors.cardLight) : 'rgba(255, 255, 255, 1)'};
|
|
49
49
|
--card-foreground: rgba(9, 9, 11, 1);
|
|
50
|
-
|
|
51
|
-
--popover: rgba(255, 255, 255, 1);
|
|
50
|
+
|
|
51
|
+
--popover: ${colors.cardLight ? rgba(colors.cardLight) : 'rgba(255, 255, 255, 1)'};
|
|
52
52
|
--popover-foreground: rgba(9, 9, 11, 1);
|
|
53
53
|
|
|
54
54
|
--primary: var(--xertica-primary);
|
|
@@ -59,7 +59,7 @@ export const generateTokensCss = (theme: ColorTheme): string => {
|
|
|
59
59
|
--secondary: rgba(244, 244, 245, 1);
|
|
60
60
|
--secondary-foreground: rgba(24, 24, 27, 1);
|
|
61
61
|
|
|
62
|
-
--muted: rgba(244, 244, 245, 1);
|
|
62
|
+
--muted: ${colors.backgroundLight ? rgba(colors.backgroundLight) : 'rgba(244, 244, 245, 1)'};
|
|
63
63
|
--muted-foreground: rgba(113, 113, 122, 1);
|
|
64
64
|
|
|
65
65
|
--accent: rgba(244, 244, 245, 1);
|
|
@@ -77,6 +77,7 @@ export const generateTokensCss = (theme: ColorTheme): string => {
|
|
|
77
77
|
--warning-foreground: rgba(28, 25, 23, 1);
|
|
78
78
|
|
|
79
79
|
--border: rgba(228, 228, 231, 1);
|
|
80
|
+
--input-border: ${colors.inputBorderColor || 'var(--border)'};
|
|
80
81
|
--input: rgba(244, 244, 245, 0.5);
|
|
81
82
|
--input-background: rgba(244, 244, 245, 0.5);
|
|
82
83
|
--ring: ${rgba(colors.primary, 0.5)};
|
|
@@ -106,9 +107,17 @@ export const generateTokensCss = (theme: ColorTheme): string => {
|
|
|
106
107
|
|
|
107
108
|
/* Spacing & Radius */
|
|
108
109
|
--radius: 6px;
|
|
109
|
-
--radius-button: 12px;
|
|
110
|
+
--radius-button: ${colors.buttonRadius || '12px'};
|
|
110
111
|
--radius-card: 12px;
|
|
111
|
-
|
|
112
|
+
|
|
113
|
+
/* Button stroke & secondary (theme-overridable; defaults preserve the flat look) */
|
|
114
|
+
--button-stroke-width: ${colors.buttonStrokeWidth || '0px'};
|
|
115
|
+
--button-stroke-color: ${colors.buttonStrokeColor || 'transparent'};
|
|
116
|
+
--button-secondary-bg: ${colors.buttonSecondaryBg || 'var(--secondary)'};
|
|
117
|
+
--button-secondary-foreground: ${colors.buttonSecondaryForeground || 'var(--secondary-foreground)'};
|
|
118
|
+
--button-primary-bg: ${colors.buttonPrimaryBg || 'var(--primary)'};
|
|
119
|
+
--button-primary-foreground: ${colors.buttonPrimaryForeground || 'var(--primary-foreground)'};
|
|
120
|
+
|
|
112
121
|
--elevation-sm: 0px 0px 48px 0px rgba(0, 0, 0, 0.1);
|
|
113
122
|
|
|
114
123
|
/* Typography */
|
|
@@ -209,7 +218,14 @@ export const generateTokensCss = (theme: ColorTheme): string => {
|
|
|
209
218
|
--input: ${rgba(colors.darkMuted, 0.5)};
|
|
210
219
|
--input-background: ${rgba(colors.darkMuted, 0.5)};
|
|
211
220
|
--ring: ${rgba(colors.primaryDarkMode, 0.5)};
|
|
212
|
-
|
|
221
|
+
|
|
222
|
+
/* Button stroke & secondary (theme-overridable; defaults preserve the flat look) */
|
|
223
|
+
--button-stroke-color: ${colors.buttonStrokeColorDark || colors.buttonStrokeColor || 'transparent'};
|
|
224
|
+
--button-secondary-bg: ${colors.buttonSecondaryBgDark || colors.buttonSecondaryBg || 'var(--secondary)'};
|
|
225
|
+
--button-secondary-foreground: ${colors.buttonSecondaryForegroundDark || colors.buttonSecondaryForeground || 'var(--secondary-foreground)'};
|
|
226
|
+
--button-primary-bg: ${colors.buttonPrimaryBgDark || colors.buttonPrimaryBg || 'var(--primary)'};
|
|
227
|
+
--button-primary-foreground: ${colors.buttonPrimaryForegroundDark || colors.buttonPrimaryForeground || 'var(--primary-foreground)'};
|
|
228
|
+
|
|
213
229
|
--elevation-sm: 0px 0px 48px 0px rgba(0, 0, 0, 0.3);
|
|
214
230
|
|
|
215
231
|
/* Charts — dark mode: slightly lighter/more saturated for contrast on dark bg */
|
|
@@ -441,7 +441,7 @@ export function XerticaAssistant({
|
|
|
441
441
|
{/* Main Assistant Container */}
|
|
442
442
|
<div
|
|
443
443
|
className={cn(
|
|
444
|
-
`${containerHeight} flex flex-col bg-
|
|
444
|
+
`${containerHeight} flex flex-col bg-card relative`,
|
|
445
445
|
!isFullPage && 'border-l border-border shadow-sm',
|
|
446
446
|
isMobile && isExpanded && 'fixed inset-0 z-[100] h-[100dvh] w-full border-l-0 shadow-2xl',
|
|
447
447
|
className
|
|
@@ -8,7 +8,7 @@ describe('Button', () => {
|
|
|
8
8
|
render(<Button>Click me</Button>);
|
|
9
9
|
const button = screen.getByRole('button', { name: /click me/i });
|
|
10
10
|
expect(button).toBeInTheDocument();
|
|
11
|
-
expect(button).toHaveClass('bg-primary');
|
|
11
|
+
expect(button).toHaveClass('bg-button-primary');
|
|
12
12
|
});
|
|
13
13
|
|
|
14
14
|
it('renders correctly with different variants', () => {
|
|
@@ -59,6 +59,6 @@ describe('Button', () => {
|
|
|
59
59
|
);
|
|
60
60
|
const link = screen.getByRole('link', { name: /link button/i });
|
|
61
61
|
expect(link).toBeInTheDocument();
|
|
62
|
-
expect(link).toHaveClass('bg-primary'); // Should still have button styles
|
|
62
|
+
expect(link).toHaveClass('bg-button-primary'); // Should still have button styles
|
|
63
63
|
});
|
|
64
64
|
});
|
|
@@ -10,18 +10,18 @@ const buttonVariants = cva(
|
|
|
10
10
|
{
|
|
11
11
|
variants: {
|
|
12
12
|
variant: {
|
|
13
|
-
default: 'bg-primary text-
|
|
13
|
+
default: 'border-button-stroke bg-button-primary text-button-primary',
|
|
14
14
|
destructive:
|
|
15
|
-
'bg-destructive text-destructive-foreground hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40',
|
|
15
|
+
'border-button-stroke bg-destructive text-destructive-foreground hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40',
|
|
16
16
|
outline: 'border border-border bg-background hover:bg-accent hover:text-accent-foreground',
|
|
17
|
-
secondary: 'bg-secondary text-
|
|
17
|
+
secondary: 'border-button-stroke bg-button-secondary text-button-secondary',
|
|
18
18
|
ghost: 'hover:bg-accent hover:text-accent-foreground',
|
|
19
19
|
link: 'text-primary underline-offset-4 hover:underline',
|
|
20
20
|
success:
|
|
21
|
-
'bg-success text-success-foreground hover:bg-success/90 focus-visible:ring-success/20 dark:focus-visible:ring-success/40',
|
|
22
|
-
info: 'bg-info text-info-foreground hover:bg-info/90 focus-visible:ring-info/20 dark:focus-visible:ring-info/40',
|
|
21
|
+
'border-button-stroke bg-success text-success-foreground hover:bg-success/90 focus-visible:ring-success/20 dark:focus-visible:ring-success/40',
|
|
22
|
+
info: 'border-button-stroke bg-info text-info-foreground hover:bg-info/90 focus-visible:ring-info/20 dark:focus-visible:ring-info/40',
|
|
23
23
|
warning:
|
|
24
|
-
'bg-warning text-warning-foreground hover:bg-warning/90 focus-visible:ring-warning/20 dark:focus-visible:ring-warning/40',
|
|
24
|
+
'border-button-stroke bg-warning text-warning-foreground hover:bg-warning/90 focus-visible:ring-warning/20 dark:focus-visible:ring-warning/40',
|
|
25
25
|
},
|
|
26
26
|
size: {
|
|
27
27
|
default: 'h-9 px-4 py-2 has-[>svg]:px-3',
|
|
@@ -2,6 +2,16 @@ import * as React from 'react';
|
|
|
2
2
|
|
|
3
3
|
import { cn } from '../../shared/utils';
|
|
4
4
|
|
|
5
|
+
export interface CardProps extends React.ComponentProps<'div'> {
|
|
6
|
+
/**
|
|
7
|
+
* Background variant.
|
|
8
|
+
* - `default`: uses the theme's `--card` token (e.g. Marfim on the Xertica theme).
|
|
9
|
+
* - `white`: forces a solid white background regardless of the active theme,
|
|
10
|
+
* for cards that should stand out from the theme's own card tone.
|
|
11
|
+
*/
|
|
12
|
+
variant?: 'default' | 'white';
|
|
13
|
+
}
|
|
14
|
+
|
|
5
15
|
/**
|
|
6
16
|
* Primary content container surface.
|
|
7
17
|
*
|
|
@@ -13,13 +23,15 @@ import { cn } from '../../shared/utils';
|
|
|
13
23
|
* 1. NEVER create raw divs with `bg-white dark:bg-black rounded border shadow` — use `<Card>`.
|
|
14
24
|
* 2. Compose with the semantic order: `CardHeader > CardTitle + CardDescription > CardContent > CardFooter`.
|
|
15
25
|
* 3. For inline action buttons in the header, use `<CardAction>` (rendered right-aligned automatically).
|
|
26
|
+
* 4. Use `variant="white"` for cards that need a solid white background instead of the theme's card tone.
|
|
16
27
|
*/
|
|
17
|
-
function Card({ className, ...props }:
|
|
28
|
+
function Card({ className, variant = 'default', ...props }: CardProps) {
|
|
18
29
|
return (
|
|
19
30
|
<div
|
|
20
31
|
data-slot="card"
|
|
21
32
|
className={cn(
|
|
22
|
-
'
|
|
33
|
+
'text-card-foreground flex flex-col gap-6 rounded-[var(--radius-card)]',
|
|
34
|
+
variant === 'white' ? 'bg-white' : 'bg-card',
|
|
23
35
|
className
|
|
24
36
|
)}
|
|
25
37
|
{...props}
|
|
@@ -38,7 +38,7 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
|
|
38
38
|
type={type}
|
|
39
39
|
data-slot="input"
|
|
40
40
|
className={cn(
|
|
41
|
-
'flex w-full min-w-0 bg-background rounded-[var(--radius)] border border-
|
|
41
|
+
'flex w-full min-w-0 bg-background rounded-[var(--radius)] border border-input-stroke transition-colors outline-none text-foreground',
|
|
42
42
|
'placeholder:text-muted-foreground',
|
|
43
43
|
'focus:ring-2 focus:ring-primary focus:border-transparent',
|
|
44
44
|
'disabled:cursor-not-allowed disabled:opacity-50',
|
|
@@ -74,7 +74,7 @@ function InputOTPSlot({
|
|
|
74
74
|
data-slot="input-otp-slot"
|
|
75
75
|
data-active={isActive}
|
|
76
76
|
className={cn(
|
|
77
|
-
'data-[active=true]:border-primary data-[active=true]:ring-primary/20 data-[active=true]:aria-invalid:ring-destructive/20 aria-invalid:border-destructive data-[active=true]:aria-invalid:border-destructive relative flex items-center justify-center border border-
|
|
77
|
+
'data-[active=true]:border-primary data-[active=true]:ring-primary/20 data-[active=true]:aria-invalid:ring-destructive/20 aria-invalid:border-destructive data-[active=true]:aria-invalid:border-destructive relative flex items-center justify-center border border-input-stroke rounded-[var(--radius)] bg-background transition-all outline-none data-[active=true]:z-10 data-[active=true]:ring-2',
|
|
78
78
|
sizeClasses[size],
|
|
79
79
|
className
|
|
80
80
|
)}
|
|
@@ -109,7 +109,7 @@ const Search = React.forwardRef<HTMLInputElement, SearchProps>(
|
|
|
109
109
|
onChange={handleChange}
|
|
110
110
|
onKeyDown={handleKeyDown}
|
|
111
111
|
className={cn(
|
|
112
|
-
'flex w-full rounded-[var(--radius)] border border-
|
|
112
|
+
'flex w-full rounded-[var(--radius)] border border-input-stroke bg-background text-foreground transition-colors outline-none',
|
|
113
113
|
'placeholder:text-muted-foreground',
|
|
114
114
|
'focus:ring-2 focus:ring-primary focus:border-transparent',
|
|
115
115
|
'disabled:cursor-not-allowed disabled:opacity-50',
|
|
@@ -48,7 +48,7 @@ const SelectTrigger = React.forwardRef<
|
|
|
48
48
|
data-slot="select-trigger"
|
|
49
49
|
data-size={size}
|
|
50
50
|
className={cn(
|
|
51
|
-
'flex w-full items-center justify-between gap-2 bg-background rounded-[var(--radius)] border border-
|
|
51
|
+
'flex w-full items-center justify-between gap-2 bg-background rounded-[var(--radius)] border border-input-stroke transition-colors outline-none whitespace-nowrap text-foreground',
|
|
52
52
|
'data-[placeholder]:text-muted-foreground',
|
|
53
53
|
'focus:ring-2 focus:ring-primary focus:border-transparent',
|
|
54
54
|
'disabled:cursor-not-allowed disabled:opacity-50',
|
|
@@ -107,7 +107,7 @@ const SelectContent = React.forwardRef<
|
|
|
107
107
|
ref={ref}
|
|
108
108
|
data-slot="select-content"
|
|
109
109
|
className={cn(
|
|
110
|
-
'bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 relative z-50 max-h-(--radix-select-content-available-height) min-w-[8rem] origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-[var(--radius)] border border-
|
|
110
|
+
'bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 relative z-50 max-h-(--radix-select-content-available-height) min-w-[8rem] origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-[var(--radius)] border border-input-stroke shadow-md',
|
|
111
111
|
position === 'popper' &&
|
|
112
112
|
'data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1',
|
|
113
113
|
className
|
|
@@ -45,7 +45,7 @@ const Switch = React.forwardRef<
|
|
|
45
45
|
<SwitchPrimitive.Root
|
|
46
46
|
className={cn(
|
|
47
47
|
'peer inline-flex shrink-0 items-center rounded-full border border-transparent transition-all outline-none disabled:cursor-not-allowed disabled:opacity-50',
|
|
48
|
-
'data-[state=checked]:bg-primary',
|
|
48
|
+
'data-[state=checked]:bg-button-primary',
|
|
49
49
|
'data-[state=unchecked]:bg-muted',
|
|
50
50
|
'focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]',
|
|
51
51
|
trackClasses[size],
|
|
@@ -43,7 +43,7 @@ const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
|
|
|
43
43
|
<textarea
|
|
44
44
|
data-slot="textarea"
|
|
45
45
|
className={cn(
|
|
46
|
-
'flex min-h-16 w-full bg-background rounded-[var(--radius)] border border-
|
|
46
|
+
'flex min-h-16 w-full bg-background rounded-[var(--radius)] border border-input-stroke transition-colors outline-none resize-none text-foreground',
|
|
47
47
|
'placeholder:text-muted-foreground',
|
|
48
48
|
'focus:ring-2 focus:ring-primary focus:border-transparent',
|
|
49
49
|
'disabled:cursor-not-allowed disabled:opacity-50',
|
|
@@ -132,6 +132,10 @@ export const BrandColorsProvider: React.FC<BrandColorsProviderProps> = ({
|
|
|
132
132
|
// 3. Radius
|
|
133
133
|
cssVars.push(`--radius: ${radius}rem`);
|
|
134
134
|
|
|
135
|
+
// 3b. Button shape overrides (identical in both modes; theme-specific, always emitted)
|
|
136
|
+
cssVars.push(`--radius-button: ${colors.buttonRadius || '12px'}`);
|
|
137
|
+
cssVars.push(`--button-stroke-width: ${colors.buttonStrokeWidth || '0px'}`);
|
|
138
|
+
|
|
135
139
|
// 4. Charts
|
|
136
140
|
cssVars.push(`--chart-1: ${colors.chart1}`);
|
|
137
141
|
cssVars.push(`--chart-2: ${colors.chart2}`);
|
|
@@ -193,6 +197,48 @@ export const BrandColorsProvider: React.FC<BrandColorsProviderProps> = ({
|
|
|
193
197
|
darkSurfaceVars.push(`--input-background: ${colors.darkMuted}`);
|
|
194
198
|
}
|
|
195
199
|
|
|
200
|
+
// 7b. Button stroke/secondary tokens + light-mode page background.
|
|
201
|
+
// Always emitted (with fallbacks matching the pre-existing look) so
|
|
202
|
+
// switching away from a theme that defines them never leaves a stale value.
|
|
203
|
+
const lightModeVars: string[] = [
|
|
204
|
+
`--button-stroke-color: ${colors.buttonStrokeColor || 'transparent'}`,
|
|
205
|
+
`--button-secondary-bg: ${colors.buttonSecondaryBg || 'var(--secondary)'}`,
|
|
206
|
+
`--button-secondary-foreground: ${colors.buttonSecondaryForeground || 'var(--secondary-foreground)'}`,
|
|
207
|
+
`--button-primary-bg: ${colors.buttonPrimaryBg || 'var(--primary)'}`,
|
|
208
|
+
`--button-primary-foreground: ${colors.buttonPrimaryForeground || 'var(--primary-foreground)'}`,
|
|
209
|
+
];
|
|
210
|
+
if (colors.backgroundLight) {
|
|
211
|
+
// The app shell backdrop (e.g. AppLayout's `bg-muted` wrapper behind the
|
|
212
|
+
// sidebar/content) reads `--muted`, not `--background` — mirror the
|
|
213
|
+
// override so the shell matches the page background instead of falling
|
|
214
|
+
// back to the neutral gray every other theme uses.
|
|
215
|
+
lightModeVars.push(`--background: ${colors.backgroundLight}`);
|
|
216
|
+
lightModeVars.push(`--muted: ${colors.backgroundLight}`);
|
|
217
|
+
}
|
|
218
|
+
if (colors.cardLight) {
|
|
219
|
+
lightModeVars.push(`--card: ${colors.cardLight}`);
|
|
220
|
+
lightModeVars.push(`--popover: ${colors.cardLight}`);
|
|
221
|
+
}
|
|
222
|
+
if (colors.inputBorderColor) {
|
|
223
|
+
lightModeVars.push(`--input-border: ${colors.inputBorderColor}`);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
darkSurfaceVars.push(
|
|
227
|
+
`--button-stroke-color: ${colors.buttonStrokeColorDark || colors.buttonStrokeColor || 'transparent'}`
|
|
228
|
+
);
|
|
229
|
+
darkSurfaceVars.push(
|
|
230
|
+
`--button-secondary-bg: ${colors.buttonSecondaryBgDark || colors.buttonSecondaryBg || 'var(--secondary)'}`
|
|
231
|
+
);
|
|
232
|
+
darkSurfaceVars.push(
|
|
233
|
+
`--button-secondary-foreground: ${colors.buttonSecondaryForegroundDark || colors.buttonSecondaryForeground || 'var(--secondary-foreground)'}`
|
|
234
|
+
);
|
|
235
|
+
darkSurfaceVars.push(
|
|
236
|
+
`--button-primary-bg: ${colors.buttonPrimaryBgDark || colors.buttonPrimaryBg || 'var(--primary)'}`
|
|
237
|
+
);
|
|
238
|
+
darkSurfaceVars.push(
|
|
239
|
+
`--button-primary-foreground: ${colors.buttonPrimaryForegroundDark || colors.buttonPrimaryForeground || 'var(--primary-foreground)'}`
|
|
240
|
+
);
|
|
241
|
+
|
|
196
242
|
// Apply primary-related tokens directly as inline styles on <html>.
|
|
197
243
|
// Inline styles have the highest possible specificity and always win over
|
|
198
244
|
// any stylesheet rule, regardless of order or selector specificity.
|
|
@@ -216,13 +262,15 @@ export const BrandColorsProvider: React.FC<BrandColorsProviderProps> = ({
|
|
|
216
262
|
document.head.appendChild(styleEl);
|
|
217
263
|
}
|
|
218
264
|
|
|
265
|
+
const lightModeBlock = `:root:not(.dark) {\n ${lightModeVars.join(';\n ')};\n}`;
|
|
266
|
+
|
|
219
267
|
const darkSurfaceBlock =
|
|
220
268
|
darkSurfaceVars.length > 0
|
|
221
269
|
? `\n:root[data-mode='dark'], .dark {\n ${darkSurfaceVars.join(';\n ')};\n}`
|
|
222
270
|
: '';
|
|
223
271
|
|
|
224
|
-
// The style tag handles
|
|
225
|
-
styleEl.innerHTML = darkSurfaceBlock
|
|
272
|
+
// The style tag handles the surface/button tokens that vary per theme per mode.
|
|
273
|
+
styleEl.innerHTML = `${lightModeBlock}${darkSurfaceBlock}`.trim();
|
|
226
274
|
}, [colors, currentTheme, radius, useCustomTokens]);
|
|
227
275
|
|
|
228
276
|
// Aplicar cores quando elas mudarem
|
package/contexts/theme-data.ts
CHANGED
|
@@ -31,8 +31,44 @@ export interface BrandColors {
|
|
|
31
31
|
chart4: string;
|
|
32
32
|
chart5: string;
|
|
33
33
|
|
|
34
|
-
// Optional:
|
|
35
|
-
|
|
34
|
+
// Optional: light-mode page/shell background override (defaults to white
|
|
35
|
+
// when unset). Drives --background and --muted (the app-shell backdrop).
|
|
36
|
+
backgroundLight?: string;
|
|
37
|
+
|
|
38
|
+
// Optional: light-mode surface override for cards, popovers, the header,
|
|
39
|
+
// and the assistant panel (defaults to white when unset). Drives --card
|
|
40
|
+
// and --popover — kept distinct from backgroundLight so a theme can use a
|
|
41
|
+
// different tone for "page" vs "elevated surface".
|
|
42
|
+
cardLight?: string;
|
|
43
|
+
|
|
44
|
+
// Optional: light-mode stroke color for form fields only (Input, Select,
|
|
45
|
+
// Textarea, Search, InputOTP) — defaults to the shared --border token when
|
|
46
|
+
// unset. Deliberately scoped away from --border/--sidebar-border so
|
|
47
|
+
// structural strokes (header divider, sidebar popovers, <Separator>) keep
|
|
48
|
+
// their normal subtle look; only actual form controls need a stronger,
|
|
49
|
+
// always-visible stroke regardless of the surrounding background.
|
|
50
|
+
inputBorderColor?: string;
|
|
51
|
+
|
|
52
|
+
// Optional: Button shape & stroke overrides (defaults preserve current look when unset)
|
|
53
|
+
buttonRadius?: string;
|
|
54
|
+
buttonStrokeWidth?: string;
|
|
55
|
+
buttonStrokeColor?: string;
|
|
56
|
+
buttonStrokeColorDark?: string;
|
|
57
|
+
|
|
58
|
+
// Optional: Button `secondary` variant colors (defaults to the shared --secondary token when unset)
|
|
59
|
+
buttonSecondaryBg?: string;
|
|
60
|
+
buttonSecondaryBgDark?: string;
|
|
61
|
+
buttonSecondaryForeground?: string;
|
|
62
|
+
buttonSecondaryForegroundDark?: string;
|
|
63
|
+
|
|
64
|
+
// Optional: accent used by the Button `default` variant and Switch's checked
|
|
65
|
+
// state only (defaults to the shared --primary token when unset). Lets a
|
|
66
|
+
// theme use a vibrant color for these two controls without it bleeding into
|
|
67
|
+
// links, focus rings, radio buttons, the logo, etc. via --primary.
|
|
68
|
+
buttonPrimaryBg?: string;
|
|
69
|
+
buttonPrimaryBgDark?: string;
|
|
70
|
+
buttonPrimaryForeground?: string;
|
|
71
|
+
buttonPrimaryForegroundDark?: string;
|
|
36
72
|
}
|
|
37
73
|
|
|
38
74
|
export interface ColorTheme {
|
|
@@ -66,7 +102,7 @@ export const PALETTE = {
|
|
|
66
102
|
export const colorThemes: ColorTheme[] = [
|
|
67
103
|
{
|
|
68
104
|
id: 'xertica-original',
|
|
69
|
-
name: 'Xertica
|
|
105
|
+
name: 'Xertica Classic',
|
|
70
106
|
description: 'A identidade visual clássica da Xertica com o gradiente original',
|
|
71
107
|
colors: {
|
|
72
108
|
primary: '#2C275B',
|
|
@@ -100,6 +136,67 @@ export const colorThemes: ColorTheme[] = [
|
|
|
100
136
|
accent: '#72CDFD',
|
|
101
137
|
},
|
|
102
138
|
},
|
|
139
|
+
{
|
|
140
|
+
id: 'xertica',
|
|
141
|
+
name: 'Xertica',
|
|
142
|
+
description: 'A nova identidade visual da Xertica: papel quente, tintas vivas e botões em estilo serigrafia',
|
|
143
|
+
colors: {
|
|
144
|
+
// General-purpose brand accent (logo, links, radio buttons, focus rings,
|
|
145
|
+
// sidebar active state, badges, etc). Kept as Negro Xertica / Crema so it
|
|
146
|
+
// stays legible everywhere — yellow is reserved for buttonPrimaryBg below.
|
|
147
|
+
primary: '#1E1E1E', // Negro Xertica
|
|
148
|
+
primaryForeground: '#FFFEF8', // Marfim
|
|
149
|
+
primaryLight: '#1E1E1E',
|
|
150
|
+
primaryDarkMode: '#F2EDD8', // Crema
|
|
151
|
+
primaryForegroundDark: '#1E1E1E',
|
|
152
|
+
|
|
153
|
+
sidebarLight: '#1E1E1E', // Negro Xertica
|
|
154
|
+
sidebarDark: '#1E1E1E',
|
|
155
|
+
|
|
156
|
+
darkBackground: '#141311',
|
|
157
|
+
darkCard: '#1E1E1E',
|
|
158
|
+
darkMuted: '#26241f',
|
|
159
|
+
darkBorder: '#38352c',
|
|
160
|
+
|
|
161
|
+
gradientStart: '#1899AF', // Celeste
|
|
162
|
+
gradientEnd: '#C45BAA', // Magenta
|
|
163
|
+
gradientStartDark: '#126578',
|
|
164
|
+
gradientEndDark: '#8F3F7D',
|
|
165
|
+
|
|
166
|
+
chart1: '#3D63AC', // Azul
|
|
167
|
+
chart2: '#1899AF', // Celeste
|
|
168
|
+
chart3: '#FAF338', // Amarillo
|
|
169
|
+
chart4: '#DE5B48', // Rojo Coral
|
|
170
|
+
chart5: '#2E8B5A', // Verde
|
|
171
|
+
|
|
172
|
+
backgroundLight: '#E9E6DC', // page/shell background
|
|
173
|
+
cardLight: '#FFFEF8', // Marfim — cards, header, assistant panel
|
|
174
|
+
inputBorderColor: 'rgba(30, 30, 30, 0.4)', // Negro Xertica stroke — form fields only
|
|
175
|
+
|
|
176
|
+
buttonRadius: '9999px',
|
|
177
|
+
buttonStrokeWidth: '2px',
|
|
178
|
+
buttonStrokeColor: '#1E1E1E',
|
|
179
|
+
buttonStrokeColorDark: '#F2EDD8', // Crema
|
|
180
|
+
|
|
181
|
+
buttonSecondaryBg: '#1E1E1E',
|
|
182
|
+
buttonSecondaryBgDark: '#1E1E1E',
|
|
183
|
+
buttonSecondaryForeground: '#FFFEF8',
|
|
184
|
+
buttonSecondaryForegroundDark: '#FFFEF8',
|
|
185
|
+
|
|
186
|
+
// Amarillo stays vibrant, but only here — Button `default` variant and
|
|
187
|
+
// Switch's checked track, per the brand rule that yellow is the one
|
|
188
|
+
// accent allowed to dominate, and only in those two contexts.
|
|
189
|
+
buttonPrimaryBg: '#FAF338',
|
|
190
|
+
buttonPrimaryBgDark: '#FAF338',
|
|
191
|
+
buttonPrimaryForeground: '#1E1E1E',
|
|
192
|
+
buttonPrimaryForegroundDark: '#1E1E1E',
|
|
193
|
+
},
|
|
194
|
+
preview: {
|
|
195
|
+
primary: '#FAF338',
|
|
196
|
+
secondary: '#1E1E1E',
|
|
197
|
+
accent: '#1899AF',
|
|
198
|
+
},
|
|
199
|
+
},
|
|
103
200
|
{
|
|
104
201
|
id: 'zinc',
|
|
105
202
|
name: 'Zinc',
|