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.
Files changed (228) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/README.md +19 -11
  3. package/bin/generate-tokens.ts +25 -9
  4. package/components/assistant/xertica-assistant/xertica-assistant.tsx +1 -1
  5. package/components/ui/button/button.test.tsx +2 -2
  6. package/components/ui/button/button.tsx +6 -6
  7. package/components/ui/card/card.tsx +14 -2
  8. package/components/ui/input/input.tsx +1 -1
  9. package/components/ui/input-otp/input-otp.tsx +1 -1
  10. package/components/ui/search/search.tsx +1 -1
  11. package/components/ui/select/select.tsx +2 -2
  12. package/components/ui/switch/switch.tsx +1 -1
  13. package/components/ui/textarea/textarea.tsx +1 -1
  14. package/contexts/BrandColorsContext.tsx +50 -2
  15. package/contexts/theme-data.ts +100 -3
  16. package/dist/AssistantChart-BFBJXVcm.js +3383 -0
  17. package/dist/AssistantChart-BTnE94FR.cjs +3551 -0
  18. package/dist/AssistantChart-BvccsQMx.cjs +3551 -0
  19. package/dist/AssistantChart-CkskcBVD.js +3383 -0
  20. package/dist/AssistantChart-CrPkO9ip.js +3383 -0
  21. package/dist/AssistantChart-DKz2O1R8.cjs +3551 -0
  22. package/dist/AssistantChart-DYL6UEvo.cjs +3551 -0
  23. package/dist/AssistantChart-keo-ogKi.js +3383 -0
  24. package/dist/AudioPlayer-AoU5MTiM.cjs +1282 -0
  25. package/dist/AudioPlayer-Bx1vrblm.js +1278 -0
  26. package/dist/AudioPlayer-D0-I6J3J.cjs +1282 -0
  27. package/dist/AudioPlayer-DIWX_Yg7.cjs +1282 -0
  28. package/dist/AudioPlayer-DrWJnS7c.js +1278 -0
  29. package/dist/AudioPlayer-_P-Z7pAK.js +1278 -0
  30. package/dist/BrandColorsContext-B25Bpr3b.cjs +819 -0
  31. package/dist/BrandColorsContext-BCieATdW.js +812 -0
  32. package/dist/BrandColorsContext-DP0DXAXS.js +824 -0
  33. package/dist/BrandColorsContext-DjpC9wR_.cjs +798 -0
  34. package/dist/BrandColorsContext-MpXFhte7.cjs +831 -0
  35. package/dist/BrandColorsContext-V3tOc-Zw.js +791 -0
  36. package/dist/CodeBlock-CK4rO6lM.js +221 -0
  37. package/dist/CodeBlock-CKXamfVd.cjs +223 -0
  38. package/dist/CodeBlock-CVmE7isS.cjs +223 -0
  39. package/dist/CodeBlock-D7i5-cMv.js +221 -0
  40. package/dist/CodeBlock-Dg56j2ut.cjs +223 -0
  41. package/dist/CodeBlock-Dt6nUQoo.js +221 -0
  42. package/dist/CustomTooltipContent-Bxynuq7T.js +33 -0
  43. package/dist/CustomTooltipContent-BzjOSA0P.cjs +54 -0
  44. package/dist/CustomTooltipContent-C-ZeCNsf.js +33 -0
  45. package/dist/CustomTooltipContent-CON_QrXm.cjs +54 -0
  46. package/dist/CustomTooltipContent-UR_n2QfW.js +33 -0
  47. package/dist/CustomTooltipContent-v1SXjoFn.cjs +54 -0
  48. package/dist/FeatureCard-4n_8a63v.cjs +497 -0
  49. package/dist/FeatureCard-BLiAWT3w.js +485 -0
  50. package/dist/FeatureCard-BXRW97Ef.cjs +497 -0
  51. package/dist/FeatureCard-CDgLlWE2.js +485 -0
  52. package/dist/FeatureCard-CgQ8lq-I.cjs +497 -0
  53. package/dist/FeatureCard-Cp8cVC6T.cjs +497 -0
  54. package/dist/FeatureCard-D0MwrTRP.js +485 -0
  55. package/dist/FeatureCard-uE5i0in_.js +485 -0
  56. package/dist/ImageWithFallback-BOh1FM3I.js +98 -0
  57. package/dist/ImageWithFallback-BQO7952C.js +98 -0
  58. package/dist/ImageWithFallback-BkMXHCvV.cjs +122 -0
  59. package/dist/ImageWithFallback-C72nwszA.cjs +122 -0
  60. package/dist/ImageWithFallback-CCUMDuEC.cjs +122 -0
  61. package/dist/ImageWithFallback-DQD5TTlY.js +98 -0
  62. package/dist/LanguageSelector-1XhM7R1i.cjs +48 -0
  63. package/dist/LanguageSelector-B3ZVNc55.js +46 -0
  64. package/dist/LanguageSelector-Cj6zl0CM.js +46 -0
  65. package/dist/LanguageSelector-DK1zD4VH.cjs +48 -0
  66. package/dist/LanguageSelector-DseCAa0p.cjs +48 -0
  67. package/dist/LanguageSelector-Du09anfM.js +46 -0
  68. package/dist/LanguageSelector-PZHlFfEs.cjs +48 -0
  69. package/dist/LanguageSelector-SB-NJSmx.js +46 -0
  70. package/dist/PasswordStrengthMeter-B3S6i_k4.cjs +179 -0
  71. package/dist/PasswordStrengthMeter-B8ky1nJI.js +173 -0
  72. package/dist/PasswordStrengthMeter-C5VEopH-.cjs +179 -0
  73. package/dist/PasswordStrengthMeter-CIm7_jNT.js +173 -0
  74. package/dist/PasswordStrengthMeter-DKSAbQzu.js +173 -0
  75. package/dist/PasswordStrengthMeter-DxLu54p-.cjs +179 -0
  76. package/dist/PasswordStrengthMeter-k-e9Pole.cjs +179 -0
  77. package/dist/PasswordStrengthMeter-pAi2viq5.js +173 -0
  78. package/dist/ThemeToggle-4pQbVkpK.js +215 -0
  79. package/dist/ThemeToggle-B6rIVLvC.cjs +219 -0
  80. package/dist/ThemeToggle-BUOTps5l.cjs +219 -0
  81. package/dist/ThemeToggle-CO5mPl6e.js +215 -0
  82. package/dist/ThemeToggle-DbZQ0JSV.cjs +219 -0
  83. package/dist/ThemeToggle-vLXEq2TS.js +215 -0
  84. package/dist/VerifyEmailPage-BTjI2c9n.js +2954 -0
  85. package/dist/VerifyEmailPage-CJGfDSOx.js +2954 -0
  86. package/dist/VerifyEmailPage-DOlZUNc0.cjs +2963 -0
  87. package/dist/VerifyEmailPage-DQPRl_RJ.js +2954 -0
  88. package/dist/VerifyEmailPage-DrdqMbLC.cjs +2963 -0
  89. package/dist/VerifyEmailPage-hyDfeCRa.cjs +2963 -0
  90. package/dist/VerifyEmailPage-kKSXl8vu.cjs +2963 -0
  91. package/dist/VerifyEmailPage-w4N-bDhU.js +2954 -0
  92. package/dist/XerticaOrbe-B9tegV1n.js +1925 -0
  93. package/dist/XerticaOrbe-BQ_NjwPS.cjs +1927 -0
  94. package/dist/XerticaOrbe-BdUX171Q.cjs +1927 -0
  95. package/dist/XerticaOrbe-BdlKXRHU.cjs +1927 -0
  96. package/dist/XerticaOrbe-CByPAFE5.js +1925 -0
  97. package/dist/XerticaOrbe-Ccu9eFMu.js +1925 -0
  98. package/dist/XerticaProvider-BVid1oqn.cjs +46 -0
  99. package/dist/XerticaProvider-Bz_YfNY4.js +44 -0
  100. package/dist/XerticaProvider-CtvYfRbZ.cjs +46 -0
  101. package/dist/XerticaProvider-DMiyExQS.js +44 -0
  102. package/dist/XerticaProvider-DP2lzKip.cjs +46 -0
  103. package/dist/XerticaProvider-De79hhgp.js +44 -0
  104. package/dist/XerticaProvider-Dkk1Vbsy.js +44 -0
  105. package/dist/XerticaProvider-EdzH9Nrw.cjs +46 -0
  106. package/dist/alert-dialog-9XYAn5z5.js +819 -0
  107. package/dist/alert-dialog-B281hud0.cjs +872 -0
  108. package/dist/alert-dialog-BVWbrcrw.cjs +872 -0
  109. package/dist/alert-dialog-B_Ko1xpu.js +819 -0
  110. package/dist/alert-dialog-CIxE8T-J.cjs +872 -0
  111. package/dist/alert-dialog-DW-_iPuf.js +819 -0
  112. package/dist/assistant.cjs.js +2 -2
  113. package/dist/assistant.es.js +2 -2
  114. package/dist/avatar-BJwIMbZ_.js +54 -0
  115. package/dist/avatar-C4Zh5fAJ.cjs +77 -0
  116. package/dist/avatar-CTEUKZUT.js +54 -0
  117. package/dist/avatar-D1bZhlhL.cjs +77 -0
  118. package/dist/avatar-DKIxHeKG.cjs +77 -0
  119. package/dist/avatar-v16eZC8p.js +54 -0
  120. package/dist/blocks.cjs.js +2 -2
  121. package/dist/blocks.es.js +2 -2
  122. package/dist/brand.cjs.js +4 -4
  123. package/dist/brand.es.js +4 -4
  124. package/dist/breadcrumb-B1mUiaWB.cjs +98 -0
  125. package/dist/breadcrumb-BIRgmem-.js +90 -0
  126. package/dist/breadcrumb-BSng-oUf.cjs +98 -0
  127. package/dist/breadcrumb-CES7furU.js +90 -0
  128. package/dist/breadcrumb-CP275kbF.cjs +98 -0
  129. package/dist/breadcrumb-D7hr1EvJ.js +90 -0
  130. package/dist/button-BYtQygX5.cjs +85 -0
  131. package/dist/button-BnAW-a_w.js +62 -0
  132. package/dist/button-CUPDQews.js +62 -0
  133. package/dist/button-DFa-rtPy.js +62 -0
  134. package/dist/button-DV1G6XrI.cjs +85 -0
  135. package/dist/button-Kd9Fj53S.cjs +85 -0
  136. package/dist/cli.js +94 -10
  137. package/dist/components/ui/card/card.d.ts +11 -1
  138. package/dist/contexts/theme-data.d.ts +15 -1
  139. package/dist/dropdown-menu-B9wTZ0uX.cjs +225 -0
  140. package/dist/dropdown-menu-BeBtZOrs.cjs +225 -0
  141. package/dist/dropdown-menu-D7mRDGis.js +190 -0
  142. package/dist/dropdown-menu-D8ZmqHB7.js +190 -0
  143. package/dist/dropdown-menu-_AWBeOts.cjs +225 -0
  144. package/dist/dropdown-menu-qGzLO_HO.js +190 -0
  145. package/dist/hooks.cjs.js +1 -1
  146. package/dist/hooks.es.js +1 -1
  147. package/dist/index.cjs.js +24 -24
  148. package/dist/index.es.js +24 -24
  149. package/dist/input-B53E6K8G.js +65 -0
  150. package/dist/input-B7Q7vcoG.cjs +91 -0
  151. package/dist/input-BysqLMK1.js +65 -0
  152. package/dist/input-CStdFsaH.cjs +91 -0
  153. package/dist/input-CXYth6nW.cjs +91 -0
  154. package/dist/input-CX_fWE2D.cjs +91 -0
  155. package/dist/input-CitUVnEn.js +65 -0
  156. package/dist/input-wkBYTp-B.js +65 -0
  157. package/dist/layout.cjs.js +1 -1
  158. package/dist/layout.es.js +1 -1
  159. package/dist/media.cjs.js +1 -1
  160. package/dist/media.es.js +1 -1
  161. package/dist/pages.cjs.js +1 -1
  162. package/dist/pages.es.js +1 -1
  163. package/dist/rich-text-editor-BHiRUzWZ.js +2894 -0
  164. package/dist/rich-text-editor-BWMRUMJ2.js +2894 -0
  165. package/dist/rich-text-editor-BoE6Ks5x.cjs +2968 -0
  166. package/dist/rich-text-editor-BzuEwR_X.js +2894 -0
  167. package/dist/rich-text-editor-CcopGbQ-.js +2894 -0
  168. package/dist/rich-text-editor-CmxSRJIi.cjs +2968 -0
  169. package/dist/rich-text-editor-DyYAu28v.cjs +2968 -0
  170. package/dist/rich-text-editor-JS_eMQCC.cjs +2968 -0
  171. package/dist/select-BNfFJwYp.js +130 -0
  172. package/dist/select-CN8IDjGB.js +130 -0
  173. package/dist/select-CwRzQHA7.cjs +161 -0
  174. package/dist/select-CwgD3nMD.js +130 -0
  175. package/dist/select-DEHUm_AC.cjs +161 -0
  176. package/dist/select-DJDgpVm3.cjs +161 -0
  177. package/dist/select-DSLWQLmo.cjs +161 -0
  178. package/dist/select-YFx0wCYT.js +130 -0
  179. package/dist/sidebar-BF7JEL7U.js +1151 -0
  180. package/dist/sidebar-Bjr2doX5.cjs +1154 -0
  181. package/dist/sidebar-CMrNCmhb.cjs +1154 -0
  182. package/dist/sidebar-CO9x8AVn.cjs +1154 -0
  183. package/dist/sidebar-Cn_KB4c6.js +1151 -0
  184. package/dist/sidebar-Db_C30ph.cjs +1154 -0
  185. package/dist/sidebar-miPHsRz4.js +1151 -0
  186. package/dist/sidebar-pZF8mLLu.js +1151 -0
  187. package/dist/skeleton-BX2XTjMO.cjs +87 -0
  188. package/dist/skeleton-BoLK0iV7.js +78 -0
  189. package/dist/skeleton-Bysq8yeB.js +79 -0
  190. package/dist/skeleton-C7nspYnC.cjs +87 -0
  191. package/dist/skeleton-DL4V5Vub.cjs +87 -0
  192. package/dist/skeleton-DSMhdqp1.js +78 -0
  193. package/dist/skeleton-DhJswvTW.cjs +88 -0
  194. package/dist/skeleton-DicUfozA.js +78 -0
  195. package/dist/slider-77T_Eqw4.js +56 -0
  196. package/dist/slider-B6puMLXS.cjs +78 -0
  197. package/dist/slider-B785N7lZ.js +56 -0
  198. package/dist/slider-C6yssKyZ.cjs +78 -0
  199. package/dist/slider-CKZO9gVh.cjs +78 -0
  200. package/dist/slider-Cn-ImSze.js +56 -0
  201. package/dist/tooltip-BiJVGob3.cjs +72 -0
  202. package/dist/tooltip-D2OPn-1v.cjs +72 -0
  203. package/dist/tooltip-DlCHQuh1.js +48 -0
  204. package/dist/tooltip-fQ2Va0G8.cjs +72 -0
  205. package/dist/tooltip-oAw00FX2.js +48 -0
  206. package/dist/tooltip-u147L0wD.js +48 -0
  207. package/dist/ui.cjs.js +13 -13
  208. package/dist/ui.es.js +13 -13
  209. package/dist/xertica-assistant-C2A0R94j.js +2173 -0
  210. package/dist/xertica-assistant-CSUprvTR.js +2173 -0
  211. package/dist/xertica-assistant-Caubm0HR.js +2173 -0
  212. package/dist/xertica-assistant-DEexPM2u.cjs +2180 -0
  213. package/dist/xertica-assistant-Dh1dmEx3.cjs +2180 -0
  214. package/dist/xertica-assistant-GGcyHEZG.cjs +2180 -0
  215. package/dist/xertica-assistant-USefPe2L.js +2173 -0
  216. package/dist/xertica-assistant-cwSHFwfv.cjs +2180 -0
  217. package/dist/xertica-ui.css +1 -1
  218. package/package.json +3 -2
  219. package/styles/xertica/base.css +38 -0
  220. package/styles/xertica/tokens.css +9 -0
  221. package/templates/src/app/App.tsx +5 -1
  222. package/templates/src/features/home/ui/HomeContent.tsx +12 -13
  223. package/templates/src/features/template/ui/TemplateContent.tsx +569 -26
  224. package/templates/src/locales/en/pages/templates.json +112 -4
  225. package/templates/src/locales/es/pages/templates.json +112 -4
  226. package/templates/src/locales/pt-BR/pages/templates.json +112 -4
  227. package/templates/src/styles/xertica/tokens.css +9 -0
  228. 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
- Nine 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`.
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
- | ![#2C275B](https://img.shields.io/badge/-%232C275B-2C275B?style=flat-square) Xertica | `xertica-original` | `#2C275B` | `#2C275B` | `#05050d` |
360
+ | ![#2C275B](https://img.shields.io/badge/-%232C275B-2C275B?style=flat-square) Xertica Classic | `xertica-original` | `#2C275B` | `#2C275B` | `#05050d` |
361
+ | ![#1E1E1E](https://img.shields.io/badge/-%231E1E1E-1E1E1E?style=flat-square) Xertica | `xertica` | `#1E1E1E` | `#1E1E1E` | `#141311` |
361
362
  | ![#18181B](https://img.shields.io/badge/-%2318181B-18181B?style=flat-square) Zinc | `zinc` | `#18181B` | `#18181B` | `#05050d` |
362
363
  | ![#0F172A](https://img.shields.io/badge/-%230F172A-0F172A?style=flat-square) Slate | `slate` | `#0F172A` | `#0F172A` | `#05050d` |
363
364
  | ![#2563EB](https://img.shields.io/badge/-%232563EB-2563EB?style=flat-square) 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 9 themes
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 (unchanged) |
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 | Description |
471
- | -------------------- | -------------------------------------- |
472
- | `npm run dev` | Start development server |
473
- | `npm run build` | Production bundle |
474
- | `npm run storybook` | Launch component library documentation |
475
- | `npm run test` | Run unit tests via Vitest |
476
- | `npm run type-check` | TypeScript validation |
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
 
@@ -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-background relative`,
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-primary-foreground hover:bg-primary/90',
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-secondary-foreground hover:bg-secondary/80',
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 }: React.ComponentProps<'div'>) {
28
+ function Card({ className, variant = 'default', ...props }: CardProps) {
18
29
  return (
19
30
  <div
20
31
  data-slot="card"
21
32
  className={cn(
22
- 'bg-card text-card-foreground flex flex-col gap-6 rounded-[var(--radius-card)]',
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-border transition-colors outline-none text-foreground',
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-border rounded-[var(--radius)] bg-background transition-all outline-none data-[active=true]:z-10 data-[active=true]:ring-2',
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-border bg-background text-foreground transition-colors outline-none',
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-border transition-colors outline-none whitespace-nowrap text-foreground',
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-border shadow-md',
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-border transition-colors outline-none resize-none text-foreground',
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 only the surface tokens that vary per theme in dark mode.
225
- styleEl.innerHTML = darkSurfaceBlock.trim();
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
@@ -31,8 +31,44 @@ export interface BrandColors {
31
31
  chart4: string;
32
32
  chart5: string;
33
33
 
34
- // Optional: Radius override per theme
35
- radius?: string;
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 Original',
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',