xertica-ui 2.6.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 (304) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/README.md +20 -12
  3. package/bin/generate-tokens.ts +25 -9
  4. package/components/assistant/xertica-assistant/use-assistant.ts +5 -6
  5. package/components/assistant/xertica-assistant/xertica-assistant.tsx +1 -1
  6. package/{templates/src/features/auth/ui → components/blocks/auth}/AuthPageShell.tsx +8 -3
  7. package/components/blocks/auth/PasswordStrengthMeter.tsx +41 -0
  8. package/components/blocks/auth/SocialLoginButtons.tsx +95 -0
  9. package/components/blocks/auth/index.ts +11 -0
  10. package/components/blocks/auth/usePasswordStrength.ts +13 -0
  11. package/components/blocks/index.ts +1 -0
  12. package/components/pages/forgot-password-page/ForgotPasswordPage.tsx +45 -139
  13. package/components/pages/login-page/LoginPage.tsx +52 -145
  14. package/components/pages/reset-password-page/ResetPasswordPage.tsx +79 -169
  15. package/components/pages/verify-email-page/VerifyEmailPage.tsx +48 -150
  16. package/components/ui/button/button.test.tsx +2 -2
  17. package/components/ui/button/button.tsx +6 -6
  18. package/components/ui/card/card.tsx +14 -2
  19. package/components/ui/input/input.tsx +1 -1
  20. package/components/ui/input-otp/input-otp.tsx +1 -1
  21. package/components/ui/search/search.tsx +1 -1
  22. package/components/ui/select/select.tsx +2 -2
  23. package/components/ui/switch/switch.tsx +1 -1
  24. package/components/ui/textarea/textarea.tsx +1 -1
  25. package/contexts/BrandColorsContext.tsx +50 -2
  26. package/contexts/theme-data.ts +100 -3
  27. package/dist/AssistantChart-BFBJXVcm.js +3383 -0
  28. package/dist/AssistantChart-BTnE94FR.cjs +3551 -0
  29. package/dist/AssistantChart-BvccsQMx.cjs +3551 -0
  30. package/dist/AssistantChart-CImCNLfD.cjs +3551 -0
  31. package/dist/AssistantChart-CkskcBVD.js +3383 -0
  32. package/dist/AssistantChart-CrPkO9ip.js +3383 -0
  33. package/dist/AssistantChart-DKz2O1R8.cjs +3551 -0
  34. package/dist/AssistantChart-DYL6UEvo.cjs +3551 -0
  35. package/dist/AssistantChart-DfdIfmQr.js +3383 -0
  36. package/dist/AssistantChart-keo-ogKi.js +3383 -0
  37. package/dist/AudioPlayer-AoU5MTiM.cjs +1282 -0
  38. package/dist/AudioPlayer-Bx1vrblm.js +1278 -0
  39. package/dist/AudioPlayer-D0-I6J3J.cjs +1282 -0
  40. package/dist/AudioPlayer-DIWX_Yg7.cjs +1282 -0
  41. package/dist/AudioPlayer-DrWJnS7c.js +1278 -0
  42. package/dist/AudioPlayer-_P-Z7pAK.js +1278 -0
  43. package/dist/BrandColorsContext-B25Bpr3b.cjs +819 -0
  44. package/dist/BrandColorsContext-BCieATdW.js +812 -0
  45. package/dist/BrandColorsContext-DP0DXAXS.js +824 -0
  46. package/dist/BrandColorsContext-DjpC9wR_.cjs +798 -0
  47. package/dist/BrandColorsContext-MpXFhte7.cjs +831 -0
  48. package/dist/BrandColorsContext-V3tOc-Zw.js +791 -0
  49. package/dist/CodeBlock-CK4rO6lM.js +221 -0
  50. package/dist/CodeBlock-CKXamfVd.cjs +223 -0
  51. package/dist/CodeBlock-CVmE7isS.cjs +223 -0
  52. package/dist/CodeBlock-D7i5-cMv.js +221 -0
  53. package/dist/CodeBlock-Dg56j2ut.cjs +223 -0
  54. package/dist/CodeBlock-Dt6nUQoo.js +221 -0
  55. package/dist/CustomTooltipContent-Bxynuq7T.js +33 -0
  56. package/dist/CustomTooltipContent-BzjOSA0P.cjs +54 -0
  57. package/dist/CustomTooltipContent-C-ZeCNsf.js +33 -0
  58. package/dist/CustomTooltipContent-CON_QrXm.cjs +54 -0
  59. package/dist/CustomTooltipContent-UR_n2QfW.js +33 -0
  60. package/dist/CustomTooltipContent-v1SXjoFn.cjs +54 -0
  61. package/dist/FeatureCard-4n_8a63v.cjs +497 -0
  62. package/dist/FeatureCard-BLiAWT3w.js +485 -0
  63. package/dist/FeatureCard-BXRW97Ef.cjs +497 -0
  64. package/dist/FeatureCard-BhpfmPPx.js +485 -0
  65. package/dist/FeatureCard-CDgLlWE2.js +485 -0
  66. package/dist/FeatureCard-CgQ8lq-I.cjs +497 -0
  67. package/dist/FeatureCard-Cp8cVC6T.cjs +497 -0
  68. package/dist/FeatureCard-D-5yS_OD.cjs +497 -0
  69. package/dist/FeatureCard-D0MwrTRP.js +485 -0
  70. package/dist/FeatureCard-uE5i0in_.js +485 -0
  71. package/dist/ImageWithFallback-BOh1FM3I.js +98 -0
  72. package/dist/ImageWithFallback-BQO7952C.js +98 -0
  73. package/dist/ImageWithFallback-BkMXHCvV.cjs +122 -0
  74. package/dist/ImageWithFallback-C72nwszA.cjs +122 -0
  75. package/dist/ImageWithFallback-CCUMDuEC.cjs +122 -0
  76. package/dist/ImageWithFallback-CfkAkctC.cjs +122 -0
  77. package/dist/ImageWithFallback-DQD5TTlY.js +98 -0
  78. package/dist/ImageWithFallback-bw-CxxDc.js +98 -0
  79. package/dist/LanguageContext-CX_zdxre.js +1912 -0
  80. package/dist/LanguageContext-CjhSSqJT.cjs +1917 -0
  81. package/dist/LanguageSelector-1XhM7R1i.cjs +48 -0
  82. package/dist/LanguageSelector-B3ZVNc55.js +46 -0
  83. package/dist/LanguageSelector-BR3Ss0k_.js +46 -0
  84. package/dist/LanguageSelector-CHOLPmHr.cjs +48 -0
  85. package/dist/LanguageSelector-Cj6zl0CM.js +46 -0
  86. package/dist/LanguageSelector-DK1zD4VH.cjs +48 -0
  87. package/dist/LanguageSelector-DseCAa0p.cjs +48 -0
  88. package/dist/LanguageSelector-Du09anfM.js +46 -0
  89. package/dist/LanguageSelector-PZHlFfEs.cjs +48 -0
  90. package/dist/LanguageSelector-SB-NJSmx.js +46 -0
  91. package/dist/PasswordStrengthMeter-B3S6i_k4.cjs +179 -0
  92. package/dist/PasswordStrengthMeter-B8ky1nJI.js +173 -0
  93. package/dist/PasswordStrengthMeter-C5VEopH-.cjs +179 -0
  94. package/dist/PasswordStrengthMeter-C8irs0G1.js +173 -0
  95. package/dist/PasswordStrengthMeter-CIm7_jNT.js +173 -0
  96. package/dist/PasswordStrengthMeter-DKSAbQzu.js +173 -0
  97. package/dist/PasswordStrengthMeter-DxLu54p-.cjs +179 -0
  98. package/dist/PasswordStrengthMeter-MLzhax-d.cjs +179 -0
  99. package/dist/PasswordStrengthMeter-k-e9Pole.cjs +179 -0
  100. package/dist/PasswordStrengthMeter-pAi2viq5.js +173 -0
  101. package/dist/ThemeContext-CEY9pCn-.cjs +86 -0
  102. package/dist/ThemeContext-Dk3tH7Ym.js +83 -0
  103. package/dist/ThemeToggle-4pQbVkpK.js +215 -0
  104. package/dist/ThemeToggle-B6rIVLvC.cjs +219 -0
  105. package/dist/ThemeToggle-BUOTps5l.cjs +219 -0
  106. package/dist/ThemeToggle-BpM_iHh8.js +215 -0
  107. package/dist/ThemeToggle-CO5mPl6e.js +215 -0
  108. package/dist/ThemeToggle-CZRlv0d4.cjs +219 -0
  109. package/dist/ThemeToggle-DbZQ0JSV.cjs +219 -0
  110. package/dist/ThemeToggle-vLXEq2TS.js +215 -0
  111. package/dist/VerifyEmailPage-BKiaw2YK.js +2954 -0
  112. package/dist/VerifyEmailPage-BTjI2c9n.js +2954 -0
  113. package/dist/VerifyEmailPage-Beaicc6V.cjs +2963 -0
  114. package/dist/VerifyEmailPage-BehPBp0F.cjs +2963 -0
  115. package/dist/VerifyEmailPage-CJGfDSOx.js +2954 -0
  116. package/dist/VerifyEmailPage-DOlZUNc0.cjs +2963 -0
  117. package/dist/VerifyEmailPage-DQPRl_RJ.js +2954 -0
  118. package/dist/VerifyEmailPage-Djb6hthM.js +2954 -0
  119. package/dist/VerifyEmailPage-DrdqMbLC.cjs +2963 -0
  120. package/dist/VerifyEmailPage-hyDfeCRa.cjs +2963 -0
  121. package/dist/VerifyEmailPage-kKSXl8vu.cjs +2963 -0
  122. package/dist/VerifyEmailPage-w4N-bDhU.js +2954 -0
  123. package/dist/XerticaOrbe-B9tegV1n.js +1925 -0
  124. package/dist/XerticaOrbe-BQ_NjwPS.cjs +1927 -0
  125. package/dist/XerticaOrbe-BdUX171Q.cjs +1927 -0
  126. package/dist/XerticaOrbe-BdlKXRHU.cjs +1927 -0
  127. package/dist/XerticaOrbe-CByPAFE5.js +1925 -0
  128. package/dist/XerticaOrbe-Ccu9eFMu.js +1925 -0
  129. package/dist/XerticaProvider-BVid1oqn.cjs +46 -0
  130. package/dist/XerticaProvider-BijpC_py.cjs +46 -0
  131. package/dist/XerticaProvider-Bz_YfNY4.js +44 -0
  132. package/dist/XerticaProvider-C2wDRQhT.js +44 -0
  133. package/dist/XerticaProvider-CtvYfRbZ.cjs +46 -0
  134. package/dist/XerticaProvider-DMiyExQS.js +44 -0
  135. package/dist/XerticaProvider-DP2lzKip.cjs +46 -0
  136. package/dist/XerticaProvider-De79hhgp.js +44 -0
  137. package/dist/XerticaProvider-Dkk1Vbsy.js +44 -0
  138. package/dist/XerticaProvider-EdzH9Nrw.cjs +46 -0
  139. package/dist/alert-dialog-9XYAn5z5.js +819 -0
  140. package/dist/alert-dialog-B281hud0.cjs +872 -0
  141. package/dist/alert-dialog-BVWbrcrw.cjs +872 -0
  142. package/dist/alert-dialog-B_Ko1xpu.js +819 -0
  143. package/dist/alert-dialog-BzIcTKn_.cjs +872 -0
  144. package/dist/alert-dialog-CIxE8T-J.cjs +872 -0
  145. package/dist/alert-dialog-DW-_iPuf.js +819 -0
  146. package/dist/alert-dialog-kty5pxQp.js +819 -0
  147. package/dist/assistant.cjs.js +2 -2
  148. package/dist/assistant.es.js +2 -2
  149. package/dist/avatar-BJwIMbZ_.js +54 -0
  150. package/dist/avatar-C4Zh5fAJ.cjs +77 -0
  151. package/dist/avatar-CTEUKZUT.js +54 -0
  152. package/dist/avatar-D1bZhlhL.cjs +77 -0
  153. package/dist/avatar-DKIxHeKG.cjs +77 -0
  154. package/dist/avatar-v16eZC8p.js +54 -0
  155. package/dist/blocks.cjs.js +7 -3
  156. package/dist/blocks.es.js +2 -2
  157. package/dist/brand.cjs.js +8 -7
  158. package/dist/brand.es.js +4 -3
  159. package/dist/breadcrumb-B1mUiaWB.cjs +98 -0
  160. package/dist/breadcrumb-BIRgmem-.js +90 -0
  161. package/dist/breadcrumb-BSng-oUf.cjs +98 -0
  162. package/dist/breadcrumb-CES7furU.js +90 -0
  163. package/dist/breadcrumb-CP275kbF.cjs +98 -0
  164. package/dist/breadcrumb-D7hr1EvJ.js +90 -0
  165. package/dist/button-BYtQygX5.cjs +85 -0
  166. package/dist/button-BnAW-a_w.js +62 -0
  167. package/dist/button-CUPDQews.js +62 -0
  168. package/dist/button-DFa-rtPy.js +62 -0
  169. package/dist/button-DV1G6XrI.cjs +85 -0
  170. package/dist/button-Kd9Fj53S.cjs +85 -0
  171. package/dist/cli.js +94 -10
  172. package/dist/components/blocks/auth/AuthPageShell.d.ts +12 -0
  173. package/dist/components/blocks/auth/PasswordStrengthMeter.d.ts +7 -0
  174. package/dist/components/blocks/auth/SocialLoginButtons.d.ts +8 -0
  175. package/dist/components/blocks/auth/index.d.ts +8 -0
  176. package/dist/components/blocks/auth/usePasswordStrength.d.ts +3 -0
  177. package/dist/components/blocks/index.d.ts +1 -0
  178. package/dist/components/pages/reset-password-page/ResetPasswordPage.d.ts +1 -1
  179. package/dist/components/ui/card/card.d.ts +11 -1
  180. package/dist/contexts/theme-data.d.ts +15 -1
  181. package/dist/dropdown-menu-B9wTZ0uX.cjs +225 -0
  182. package/dist/dropdown-menu-BeBtZOrs.cjs +225 -0
  183. package/dist/dropdown-menu-D7mRDGis.js +190 -0
  184. package/dist/dropdown-menu-D8ZmqHB7.js +190 -0
  185. package/dist/dropdown-menu-_AWBeOts.cjs +225 -0
  186. package/dist/dropdown-menu-qGzLO_HO.js +190 -0
  187. package/dist/hooks.cjs.js +6 -5
  188. package/dist/hooks.es.js +3 -2
  189. package/dist/index.cjs.js +42 -37
  190. package/dist/index.es.js +25 -24
  191. package/dist/input-B53E6K8G.js +65 -0
  192. package/dist/input-B7Q7vcoG.cjs +91 -0
  193. package/dist/input-BysqLMK1.js +65 -0
  194. package/dist/input-CStdFsaH.cjs +91 -0
  195. package/dist/input-CXYth6nW.cjs +91 -0
  196. package/dist/input-CX_fWE2D.cjs +91 -0
  197. package/dist/input-CitUVnEn.js +65 -0
  198. package/dist/input-wkBYTp-B.js +65 -0
  199. package/dist/layout.cjs.js +1 -1
  200. package/dist/layout.es.js +1 -1
  201. package/dist/media.cjs.js +1 -1
  202. package/dist/media.es.js +1 -1
  203. package/dist/pages.cjs.js +1 -1
  204. package/dist/pages.es.js +1 -1
  205. package/dist/rich-text-editor-BHiRUzWZ.js +2894 -0
  206. package/dist/rich-text-editor-BWMRUMJ2.js +2894 -0
  207. package/dist/rich-text-editor-BoE6Ks5x.cjs +2968 -0
  208. package/dist/rich-text-editor-BzuEwR_X.js +2894 -0
  209. package/dist/rich-text-editor-CcopGbQ-.js +2894 -0
  210. package/dist/rich-text-editor-CmxSRJIi.cjs +2968 -0
  211. package/dist/rich-text-editor-DyYAu28v.cjs +2968 -0
  212. package/dist/rich-text-editor-JS_eMQCC.cjs +2968 -0
  213. package/dist/select-BNfFJwYp.js +130 -0
  214. package/dist/select-CN8IDjGB.js +130 -0
  215. package/dist/select-CwRzQHA7.cjs +161 -0
  216. package/dist/select-CwgD3nMD.js +130 -0
  217. package/dist/select-DEHUm_AC.cjs +161 -0
  218. package/dist/select-DJDgpVm3.cjs +161 -0
  219. package/dist/select-DSLWQLmo.cjs +161 -0
  220. package/dist/select-YFx0wCYT.js +130 -0
  221. package/dist/sidebar-B7YzwAp3.js +1151 -0
  222. package/dist/sidebar-BF7JEL7U.js +1151 -0
  223. package/dist/sidebar-Bjr2doX5.cjs +1154 -0
  224. package/dist/sidebar-CMrNCmhb.cjs +1154 -0
  225. package/dist/sidebar-CO9x8AVn.cjs +1154 -0
  226. package/dist/sidebar-Cb0U7NsV.cjs +1154 -0
  227. package/dist/sidebar-Cn_KB4c6.js +1151 -0
  228. package/dist/sidebar-Db_C30ph.cjs +1154 -0
  229. package/dist/sidebar-miPHsRz4.js +1151 -0
  230. package/dist/sidebar-pZF8mLLu.js +1151 -0
  231. package/dist/skeleton-BX2XTjMO.cjs +87 -0
  232. package/dist/skeleton-BoLK0iV7.js +78 -0
  233. package/dist/skeleton-Bysq8yeB.js +79 -0
  234. package/dist/skeleton-C7nspYnC.cjs +87 -0
  235. package/dist/skeleton-DL4V5Vub.cjs +87 -0
  236. package/dist/skeleton-DSMhdqp1.js +78 -0
  237. package/dist/skeleton-DhJswvTW.cjs +88 -0
  238. package/dist/skeleton-DicUfozA.js +78 -0
  239. package/dist/slider-77T_Eqw4.js +56 -0
  240. package/dist/slider-B6puMLXS.cjs +78 -0
  241. package/dist/slider-B785N7lZ.js +56 -0
  242. package/dist/slider-C6yssKyZ.cjs +78 -0
  243. package/dist/slider-CKZO9gVh.cjs +78 -0
  244. package/dist/slider-Cn-ImSze.js +56 -0
  245. package/dist/tooltip-BiJVGob3.cjs +72 -0
  246. package/dist/tooltip-D2OPn-1v.cjs +72 -0
  247. package/dist/tooltip-DlCHQuh1.js +48 -0
  248. package/dist/tooltip-fQ2Va0G8.cjs +72 -0
  249. package/dist/tooltip-oAw00FX2.js +48 -0
  250. package/dist/tooltip-u147L0wD.js +48 -0
  251. package/dist/ui.cjs.js +17 -17
  252. package/dist/ui.es.js +13 -13
  253. package/dist/xertica-assistant-8fh8KA2K.js +2173 -0
  254. package/dist/xertica-assistant-BjfG3zyL.cjs +2180 -0
  255. package/dist/xertica-assistant-C2A0R94j.js +2173 -0
  256. package/dist/xertica-assistant-CSUprvTR.js +2173 -0
  257. package/dist/xertica-assistant-Caubm0HR.js +2173 -0
  258. package/dist/xertica-assistant-DEexPM2u.cjs +2180 -0
  259. package/dist/xertica-assistant-Dh1dmEx3.cjs +2180 -0
  260. package/dist/xertica-assistant-GGcyHEZG.cjs +2180 -0
  261. package/dist/xertica-assistant-USefPe2L.js +2173 -0
  262. package/dist/xertica-assistant-cwSHFwfv.cjs +2180 -0
  263. package/dist/xertica-ui.css +1 -1
  264. package/docs/i18n.md +2 -0
  265. package/package.json +3 -2
  266. package/styles/xertica/base.css +38 -0
  267. package/styles/xertica/tokens.css +9 -0
  268. package/templates/package.json +2 -2
  269. package/templates/src/app/App.tsx +5 -1
  270. package/templates/src/features/auth/ui/ForgotPasswordContent.tsx +1 -2
  271. package/templates/src/features/auth/ui/LoginContent.tsx +1 -2
  272. package/templates/src/features/auth/ui/ResetPasswordContent.tsx +3 -57
  273. package/templates/src/features/auth/ui/VerifyEmailContent.tsx +1 -2
  274. package/templates/src/features/home/index.ts +0 -10
  275. package/templates/src/features/home/ui/HomeContent.tsx +19 -74
  276. package/templates/src/features/template/index.ts +0 -4
  277. package/templates/src/features/template/ui/TemplateContent.tsx +397 -1107
  278. package/templates/src/locales/en/pages/templates.json +70 -270
  279. package/templates/src/locales/es/pages/templates.json +77 -277
  280. package/templates/src/locales/pt-BR/pages/templates.json +77 -277
  281. package/templates/src/styles/xertica/tokens.css +9 -0
  282. package/templates/vite.config.ts +25 -0
  283. package/templates/src/features/auth/ui/SocialLoginButtons.tsx +0 -78
  284. package/templates/src/features/home/data/mock.ts +0 -41
  285. package/templates/src/features/home/hooks/useFeatureCards.ts +0 -20
  286. package/templates/src/features/home/store/dashboardStore.ts +0 -25
  287. package/templates/src/features/template/ui/CrudTemplate.tsx +0 -112
  288. package/templates/src/features/template/ui/DashboardTemplate.tsx +0 -110
  289. package/templates/src/features/template/ui/FormTemplate.tsx +0 -117
  290. package/templates/src/features/template/ui/LoginTemplate.tsx +0 -59
  291. package/templates/src/locales/en/pages/crudTemplate.json +0 -25
  292. package/templates/src/locales/en/pages/dashboardTemplate.json +0 -20
  293. package/templates/src/locales/en/pages/formTemplate.json +0 -16
  294. package/templates/src/locales/en/pages/loginTemplate.json +0 -9
  295. package/templates/src/locales/es/pages/crudTemplate.json +0 -25
  296. package/templates/src/locales/es/pages/dashboardTemplate.json +0 -20
  297. package/templates/src/locales/es/pages/formTemplate.json +0 -16
  298. package/templates/src/locales/es/pages/loginTemplate.json +0 -9
  299. package/templates/src/locales/pt-BR/pages/crudTemplate.json +0 -25
  300. package/templates/src/locales/pt-BR/pages/dashboardTemplate.json +0 -20
  301. package/templates/src/locales/pt-BR/pages/formTemplate.json +0 -16
  302. package/templates/src/locales/pt-BR/pages/loginTemplate.json +0 -9
  303. package/templates/vite.config.d.ts +0 -2
  304. package/templates/vite.config.js +0 -20
@@ -1,9 +1,8 @@
1
1
  import React, { useState } from 'react';
2
2
  import { Button } from '../../ui/button';
3
3
  import { XerticaLogo } from '../../brand/xertica-logo';
4
- import { ImageWithFallback } from '../../figma/ImageWithFallback';
5
- import { LanguageSelector } from '../../brand/language-selector';
6
- import { ArrowLeft, Mail, CheckCircle2, Lock } from 'lucide-react';
4
+ import { AuthPageShell, SocialLoginButtons } from '../../blocks/auth';
5
+ import { ArrowLeft, Mail, CheckCircle2 } from 'lucide-react';
7
6
  import { useNavigate, useLocation } from 'react-router-dom';
8
7
  import { useTranslation } from 'react-i18next';
9
8
 
@@ -47,160 +46,59 @@ export function VerifyEmailPage() {
47
46
  };
48
47
 
49
48
  return (
50
- <div className="h-screen w-full flex overflow-y-auto">
51
- {/* Left side - Full background image */}
52
- <div className="hidden lg:flex lg:flex-1 relative overflow-hidden">
53
- {/* Background image filling all space */}
54
- <ImageWithFallback
55
- src="https://images.unsplash.com/photo-1563986768609-322da13575f3?w=1200&h=800&fit=crop&auto=format"
56
- alt={t('verifyEmail.heroImageAlt')}
57
- className="absolute inset-0 w-full h-full object-cover"
58
- />
59
-
60
- {/* Gradient overlay */}
61
- <div className="absolute inset-0 bg-[image:var(--gradient-diagonal)] opacity-80" />
62
- </div>
63
-
64
- {/* Right side - Content */}
65
- <div className="flex-1 flex items-center justify-center px-4 sm:px-6 lg:px-8 lg:flex-none lg:w-1/2 relative bg-muted">
66
- {/* Language selector in the top right corner */}
67
- <div className="absolute top-4 right-4 z-20">
68
- <LanguageSelector variant="minimal" showIcon={false} />
49
+ <AuthPageShell
50
+ imageSrc="https://images.unsplash.com/photo-1563986768609-322da13575f3?w=1200&h=800&fit=crop&auto=format"
51
+ imageAlt={t('verifyEmail.heroImageAlt')}
52
+ >
53
+ <div className="text-center">
54
+ <div className="flex items-center justify-center mb-4">
55
+ <XerticaLogo className="h-12 w-auto text-primary dark:text-foreground" variant="theme" />
69
56
  </div>
70
57
 
71
- {/* Mobile background gradient */}
72
- <div className="absolute inset-0 lg:hidden bg-[image:var(--gradient-diagonal)] opacity-10 dark:opacity-5" />
73
-
74
- <div className="w-full max-w-sm space-y-6 relative z-10">
75
- {/* Form header */}
76
- <div className="text-center">
77
- <div className="flex items-center justify-center mb-4">
78
- <XerticaLogo
79
- className="h-12 w-auto text-primary dark:text-foreground"
80
- variant="theme"
81
- />
82
- </div>
83
-
84
- {/* Email icon */}
85
- <div className="flex items-center justify-center mb-4">
86
- <div className="p-4 bg-primary/10 rounded-[var(--radius)]">
87
- <Mail className="w-12 h-12 text-primary" />
88
- </div>
89
- </div>
90
-
91
- <h2 className="text-sm text-muted-foreground">{t('verifyEmail.heading')}</h2>
92
- <p className="mt-2 text-muted-foreground">{t('verifyEmail.instructionsSent')}</p>
93
- <p className="mt-1 text-primary">{email}</p>
94
- </div>
95
-
96
- {/* Instructions */}
97
- <div className="bg-accent rounded-[var(--radius)] p-4 space-y-3">
98
- <p className="text-muted-foreground">{t('verifyEmail.instructions')}</p>
99
- <div className="flex items-start gap-2 text-muted-foreground">
100
- <CheckCircle2 className="w-4 h-4 mt-0.5 flex-shrink-0 text-[var(--chart-2)]" />
101
- <span className="text-sm">{t('verifyEmail.checkSpam')}</span>
102
- </div>
103
- </div>
104
-
105
- {/* Success message on resend */}
106
- {resendSuccess && (
107
- <div className="bg-[var(--chart-2)]/10 border border-[var(--chart-2)]/20 rounded-[var(--radius)] p-3 flex items-center gap-2">
108
- <CheckCircle2 className="w-5 h-5 text-[var(--chart-2)] flex-shrink-0" />
109
- <span className="text-sm text-[var(--chart-2)]">
110
- {t('verifyEmail.resentSuccess')}
111
- </span>
112
- </div>
113
- )}
114
-
115
- {/* Action buttons */}
116
- <div className="space-y-3">
117
- <p className="text-center text-muted-foreground">{t('verifyEmail.notReceived')}</p>
118
- <Button
119
- variant="outline"
120
- className="w-full"
121
- onClick={handleResend}
122
- disabled={isResending}
123
- >
124
- {isResending ? t('verifyEmail.resending') : t('verifyEmail.resend')}
125
- </Button>
126
-
127
- <Button
128
- type="button"
129
- onClick={() => navigate('/login')}
130
- variant="outline"
131
- className="w-full text-muted-foreground hover:text-foreground"
132
- >
133
- <ArrowLeft className="w-4 h-4 mr-2" />
134
- {t('verifyEmail.backToLogin')}
135
- </Button>
136
- </div>
137
-
138
- {/* Divider */}
139
- <div className="relative">
140
- <div className="absolute inset-0 flex items-center">
141
- <div className="w-full border-t border-border"></div>
142
- </div>
143
- <div className="relative flex justify-center text-sm">
144
- <span className="bg-muted px-2 text-muted-foreground">
145
- {t('login.orContinueWith')}
146
- </span>
147
- </div>
58
+ <div className="flex items-center justify-center mb-4">
59
+ <div className="p-4 bg-primary/10 rounded-[var(--radius)]">
60
+ <Mail className="w-12 h-12 text-primary" />
148
61
  </div>
62
+ </div>
149
63
 
150
- {/* Social/SSO login options */}
151
- <div className="space-y-3">
152
- {/* Google */}
153
- <Button
154
- type="button"
155
- variant="outline"
156
- className="w-full justify-center"
157
- onClick={() => handleSocialLogin('Google')}
158
- >
159
- <svg className="w-5 h-5 mr-2" viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg">
160
- <path
161
- fill="#EA4335"
162
- d="M24 9.5c3.54 0 6.71 1.22 9.21 3.6l6.85-6.85C35.9 2.38 30.47 0 24 0 14.62 0 6.51 5.38 2.56 13.22l7.98 6.19C12.43 13.72 17.74 9.5 24 9.5z"
163
- ></path>
164
- <path
165
- fill="#4285F4"
166
- d="M46.98 24.55c0-1.57-.15-3.09-.38-4.55H24v9.02h12.94c-.58 2.96-2.26 5.48-4.78 7.18l7.73 6c4.51-4.18 7.09-10.36 7.09-17.65z"
167
- ></path>
168
- <path
169
- fill="#FBBC05"
170
- d="M10.53 28.59c-.48-1.45-.76-2.99-.76-4.59s.27-3.14.76-4.59l-7.98-6.19C.92 16.46 0 20.12 0 24c0 3.88.92 7.54 2.56 10.78l7.97-6.19z"
171
- ></path>
172
- <path
173
- fill="#34A853"
174
- d="M24 48c6.48 0 11.93-2.13 15.89-5.81l-7.73-6c-2.15 1.45-4.92 2.3-8.16 2.3-6.26 0-11.57-4.22-13.47-9.91l-7.98 6.19C6.51 42.62 14.62 48 24 48z"
175
- ></path>
176
- <path fill="none" d="M0 0h48v48H0z"></path>
177
- </svg>
178
- <span>{t('login.signInWithGoogle')}</span>
179
- </Button>
64
+ <h2 className="text-sm text-muted-foreground">{t('verifyEmail.heading')}</h2>
65
+ <p className="mt-2 text-muted-foreground">{t('verifyEmail.instructionsSent')}</p>
66
+ <p className="mt-1 text-primary">{email}</p>
67
+ </div>
180
68
 
181
- {/* MT Login */}
182
- <Button
183
- type="button"
184
- variant="outline"
185
- className="w-full justify-center"
186
- onClick={() => handleSocialLogin('MT Login')}
187
- >
188
- <Lock className="w-5 h-5 mr-2 text-[var(--chart-4)]" />
189
- <span>{t('login.signInWithMTLogin')}</span>
190
- </Button>
69
+ <div className="bg-accent rounded-[var(--radius)] p-4 space-y-3">
70
+ <p className="text-muted-foreground">{t('verifyEmail.instructions')}</p>
71
+ <div className="flex items-start gap-2 text-muted-foreground">
72
+ <CheckCircle2 className="w-4 h-4 mt-0.5 flex-shrink-0 text-[var(--chart-2)]" />
73
+ <span className="text-sm">{t('verifyEmail.checkSpam')}</span>
74
+ </div>
75
+ </div>
191
76
 
192
- {/* gov.br */}
193
- <Button
194
- type="button"
195
- variant="outline"
196
- className="w-full justify-center font-normal"
197
- onClick={() => handleSocialLogin('gov.br')}
198
- >
199
- <span>{t('login.signInWithGovBr')}</span>
200
- </Button>
201
- </div>
77
+ {resendSuccess && (
78
+ <div className="bg-[var(--chart-2)]/10 border border-[var(--chart-2)]/20 rounded-[var(--radius)] p-3 flex items-center gap-2">
79
+ <CheckCircle2 className="w-5 h-5 text-[var(--chart-2)] flex-shrink-0" />
80
+ <span className="text-sm text-[var(--chart-2)]">{t('verifyEmail.resentSuccess')}</span>
202
81
  </div>
82
+ )}
83
+
84
+ <div className="space-y-3">
85
+ <p className="text-center text-muted-foreground">{t('verifyEmail.notReceived')}</p>
86
+ <Button variant="outline" className="w-full" onClick={handleResend} disabled={isResending}>
87
+ {isResending ? t('verifyEmail.resending') : t('verifyEmail.resend')}
88
+ </Button>
89
+
90
+ <Button
91
+ type="button"
92
+ onClick={() => navigate('/login')}
93
+ variant="outline"
94
+ className="w-full text-muted-foreground hover:text-foreground"
95
+ >
96
+ <ArrowLeft className="w-4 h-4 mr-2" />
97
+ {t('verifyEmail.backToLogin')}
98
+ </Button>
203
99
  </div>
204
- </div>
100
+
101
+ <SocialLoginButtons onSocialLogin={handleSocialLogin} />
102
+ </AuthPageShell>
205
103
  );
206
104
  }
@@ -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',