xertica-ui 2.8.0 → 2.9.7
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 +108 -0
- package/README.md +1 -1
- package/bin/cli.ts +327 -87
- package/bin/language-config.ts +58 -18
- package/components/blocks/auth/AuthPageShell.tsx +63 -18
- package/components/blocks/auth/SocialLoginButtons.tsx +3 -3
- package/components/blocks/index.ts +1 -0
- package/components/blocks/marketing/CTASection.tsx +73 -0
- package/components/blocks/marketing/ComplianceBadgeRow.tsx +49 -0
- package/components/blocks/marketing/EvidenceTimeline.tsx +91 -0
- package/components/blocks/marketing/FeatureRow.tsx +78 -0
- package/components/blocks/marketing/Hero.tsx +125 -0
- package/components/blocks/marketing/HowItWorksSteps.tsx +90 -0
- package/components/blocks/marketing/LogoCloud.tsx +47 -0
- package/components/blocks/marketing/MarketingFooter.tsx +109 -0
- package/components/blocks/marketing/MarketingNavbar.tsx +192 -0
- package/components/blocks/marketing/PillBadge.tsx +96 -0
- package/components/blocks/marketing/SectionHeading.tsx +82 -0
- package/components/blocks/marketing/ServiceCardGrid.tsx +238 -0
- package/components/blocks/marketing/StatBand.tsx +99 -0
- package/components/blocks/marketing/TopAccentCards.tsx +91 -0
- package/components/blocks/marketing/index.ts +41 -0
- package/components/brand/index.ts +1 -0
- package/components/brand/isotype/Isotype.tsx +64 -0
- package/components/brand/isotype/IsotypeDiagonal.tsx +51 -0
- package/components/brand/isotype/IsotypeFrames.tsx +82 -0
- package/components/brand/isotype/IsotypeMini.tsx +65 -0
- package/components/brand/isotype/IsotypeTwist.tsx +68 -0
- package/components/brand/isotype/frame-variants.ts +20 -0
- package/components/brand/isotype/index.ts +9 -0
- package/components/brand/isotype/isotype-diagonal.mdx +52 -0
- package/components/brand/isotype/isotype-diagonal.stories.tsx +44 -0
- package/components/brand/isotype/isotype-diagonal.test.tsx +180 -0
- package/components/brand/isotype/isotype-frames.mdx +50 -0
- package/components/brand/isotype/isotype-frames.stories.tsx +63 -0
- package/components/brand/isotype/isotype-frames.test.tsx +92 -0
- package/components/brand/isotype/isotype-mini.mdx +61 -0
- package/components/brand/isotype/isotype-mini.stories.tsx +75 -0
- package/components/brand/isotype/isotype-mini.test.tsx +45 -0
- package/components/brand/isotype/isotype-twist.test.tsx +71 -0
- package/components/brand/isotype/isotype.mdx +61 -0
- package/components/brand/isotype/isotype.stories.tsx +74 -0
- package/components/brand/isotype/isotype.test.tsx +41 -0
- package/components/brand/isotype/mini-patterns.ts +50 -0
- package/components/brand/isotype/patterns.ts +83 -0
- package/components/brand/isotype/twist-variants.ts +20 -0
- package/components/pages/forgot-password-page/ForgotPasswordPage.tsx +3 -6
- package/components/pages/index.ts +1 -0
- package/components/pages/landing-page/LandingPage.tsx +360 -0
- package/components/pages/landing-page/index.ts +1 -0
- package/components/pages/login-page/LoginPage.tsx +3 -6
- package/components/pages/reset-password-page/ResetPasswordPage.tsx +1 -4
- package/components/pages/verify-email-page/VerifyEmailPage.tsx +2 -5
- package/components/ui/button/button.test.tsx +1 -1
- package/components/ui/button/button.tsx +7 -6
- package/components/ui/card/card.test.tsx +12 -0
- package/components/ui/card/card.tsx +5 -3
- package/components/ui/input-otp/input-otp.test.tsx +14 -1
- package/contexts/BrandColorsContext.tsx +9 -5
- package/contexts/theme-data.ts +32 -5
- package/dist/AssistantChart-BU48lxL2.js +3383 -0
- package/dist/AssistantChart-CoN6ga6Z.js +3383 -0
- package/dist/AssistantChart-DIRYJ7WY.js +3383 -0
- package/dist/AssistantChart-DOnY1ZlD.cjs +3551 -0
- package/dist/AssistantChart-DjM3qnp5.cjs +3551 -0
- package/dist/AssistantChart-hxPyE1S9.cjs +3551 -0
- package/dist/AudioPlayer-CpihsbaD.js +1278 -0
- package/dist/AudioPlayer-CvGajzMC.cjs +1282 -0
- package/dist/AudioPlayer-CvHpFqDG.cjs +1282 -0
- package/dist/AudioPlayer-D4S7GQou.js +1278 -0
- package/dist/BrandColorsContext-BNKLUVcU.js +829 -0
- package/dist/BrandColorsContext-DNZGHItQ.cjs +836 -0
- package/dist/BrandColorsContext-Ug_TZ8yn.cjs +846 -0
- package/dist/BrandColorsContext-z0O2LlZz.js +839 -0
- package/dist/CodeBlock-BkaZf-R8.cjs +223 -0
- package/dist/CodeBlock-CKu8LgbP.cjs +223 -0
- package/dist/CodeBlock-DiLS5LLI.js +221 -0
- package/dist/CodeBlock-zdylBGdZ.js +221 -0
- package/dist/CustomTooltipContent-BAR5qCQg.js +33 -0
- package/dist/CustomTooltipContent-CBLHBxX8.cjs +54 -0
- package/dist/CustomTooltipContent-CjWm3jjV.js +33 -0
- package/dist/CustomTooltipContent-DTV9MN9E.cjs +54 -0
- package/dist/EvidenceTimeline-DrRk4yqb.cjs +560 -0
- package/dist/EvidenceTimeline-l4MaV4C7.js +546 -0
- package/dist/FeatureCard-CIbBHqeY.cjs +497 -0
- package/dist/FeatureCard-DtoKPSfW.cjs +497 -0
- package/dist/FeatureCard-P4iEpBmB.js +485 -0
- package/dist/FeatureCard-e3f8bnF0.js +485 -0
- package/dist/ImageWithFallback-C1ww9Hg3.cjs +122 -0
- package/dist/ImageWithFallback-CBrkAEo3.cjs +122 -0
- package/dist/ImageWithFallback-CGk5zPH-.js +98 -0
- package/dist/ImageWithFallback-CfnoFFSP.js +98 -0
- package/dist/Isotype--fRZWwS6.js +85 -0
- package/dist/Isotype-0dpLqetB.js +85 -0
- package/dist/Isotype-B8XLEXSB.cjs +92 -0
- package/dist/Isotype-ChKwN7e-.cjs +91 -0
- package/dist/IsotypeDiagonal-CuPYPP4f.js +84 -0
- package/dist/IsotypeDiagonal-eNcOXRAc.cjs +91 -0
- package/dist/IsotypeDiagonal-oiwpXZhE.js +104 -0
- package/dist/IsotypeDiagonal-xMFoFXkb.cjs +109 -0
- package/dist/IsotypeFrames-BcWtyAWp.cjs +83 -0
- package/dist/IsotypeFrames-BnvSJKiH.cjs +73 -0
- package/dist/IsotypeFrames-iDRTRuIu.js +79 -0
- package/dist/IsotypeFrames-qoXOvOJt.js +69 -0
- package/dist/IsotypeMini-CQtWVo3h.js +126 -0
- package/dist/IsotypeMini-Cu_sW5yN.cjs +134 -0
- package/dist/IsotypeTwist-BTdNL4iV.js +106 -0
- package/dist/IsotypeTwist-DvsUgZs_.cjs +115 -0
- package/dist/LanguageContext-B1aOXO-K.js +1987 -0
- package/dist/LanguageContext-BP0k7AzI.js +1993 -0
- package/dist/LanguageContext-CChcIHIG.cjs +1998 -0
- package/dist/LanguageContext-ubC7TSs_.cjs +1992 -0
- package/dist/LanguageSelector-BLFPXGo8.cjs +48 -0
- package/dist/LanguageSelector-CYRzWkRZ.cjs +48 -0
- package/dist/LanguageSelector-CyHjZgC4.cjs +48 -0
- package/dist/LanguageSelector-DE8lIySO.js +46 -0
- package/dist/LanguageSelector-DUr1hjBW.js +46 -0
- package/dist/LanguageSelector-DjbWIKWC.js +46 -0
- package/dist/LogoCloud-C_uMKVNg.cjs +513 -0
- package/dist/LogoCloud-DJsMPNPh.js +500 -0
- package/dist/MarketingFooter-B_ycEdTJ.cjs +727 -0
- package/dist/MarketingFooter-Bhx-2Vlr.cjs +727 -0
- package/dist/MarketingFooter-Br1MGK9e.js +699 -0
- package/dist/MarketingFooter-BwE1vmeX.cjs +700 -0
- package/dist/MarketingFooter-CsZ1_xL9.js +709 -0
- package/dist/MarketingFooter-DCwf78Q4.cjs +716 -0
- package/dist/MarketingFooter-KH2r-OOL.js +709 -0
- package/dist/MarketingFooter-nB7G7gQ5.js +682 -0
- package/dist/SectionHeading-DEeOKcAd.cjs +857 -0
- package/dist/SectionHeading-DMNObcwm.js +838 -0
- package/dist/ThemeToggle--CGJjQJv.cjs +219 -0
- package/dist/ThemeToggle-Bn_7Pmfe.js +215 -0
- package/dist/ThemeToggle-CoL8Ady7.js +215 -0
- package/dist/ThemeToggle-D2Wzm5bn.cjs +219 -0
- package/dist/VerifyEmailPage-90lK6pP4.js +3258 -0
- package/dist/VerifyEmailPage-BJX1Rter.cjs +3320 -0
- package/dist/VerifyEmailPage-BSWvOgry.js +3310 -0
- package/dist/VerifyEmailPage-CDqIkhx-.js +3224 -0
- package/dist/VerifyEmailPage-Crxje3ZB.js +3323 -0
- package/dist/VerifyEmailPage-D2ShbuEN.cjs +3333 -0
- package/dist/VerifyEmailPage-DEBZXQ7c.cjs +3310 -0
- package/dist/VerifyEmailPage-DGaX-jZv.cjs +3268 -0
- package/dist/VerifyEmailPage-ZtOPkgOA.cjs +3234 -0
- package/dist/VerifyEmailPage-s927GSiR.js +3300 -0
- package/dist/XerticaOrbe-BHex09xs.js +1925 -0
- package/dist/XerticaOrbe-BKW3LJEz.js +1925 -0
- package/dist/XerticaOrbe-Br2i99U_.cjs +1927 -0
- package/dist/XerticaOrbe-kjFmuyx-.cjs +1927 -0
- package/dist/XerticaProvider-Bi5lKC9N.js +44 -0
- package/dist/XerticaProvider-BrPMVhiv.js +44 -0
- package/dist/XerticaProvider-CnX5kt5W.cjs +46 -0
- package/dist/XerticaProvider-DEKNJTlN.js +44 -0
- package/dist/XerticaProvider-X_wavDYA.cjs +46 -0
- package/dist/XerticaProvider-b3n_oL8w.cjs +46 -0
- package/dist/alert-dialog--qdlmB5i.cjs +872 -0
- package/dist/alert-dialog-C48TOdYU.cjs +872 -0
- package/dist/alert-dialog-CEPpnpXt.js +819 -0
- package/dist/alert-dialog-NjflitDa.js +819 -0
- package/dist/assistant.cjs.js +2 -2
- package/dist/assistant.es.js +2 -2
- package/dist/avatar-CtEKgg7s.cjs +77 -0
- package/dist/avatar-D03MNQxs.js +54 -0
- package/dist/avatar-Df7KgSvs.js +54 -0
- package/dist/avatar-Gy0_Xu9i.cjs +77 -0
- package/dist/blocks.cjs.js +33 -18
- package/dist/blocks.es.js +2 -2
- package/dist/brand.cjs.js +110 -4
- package/dist/brand.es.js +98 -4
- package/dist/breadcrumb-BGO5PZPG.js +90 -0
- package/dist/breadcrumb-BvGAY4ne.cjs +98 -0
- package/dist/breadcrumb-CU-punra.js +90 -0
- package/dist/breadcrumb-DjV9wfX1.cjs +98 -0
- package/dist/button-B05czY_8.js +62 -0
- package/dist/button-B3SE1liJ.js +62 -0
- package/dist/button-BMDlWZR8.cjs +85 -0
- package/dist/button-DVhloDtr.cjs +85 -0
- package/dist/cli.js +280 -62
- package/dist/components/blocks/auth/AuthPageShell.d.ts +21 -5
- package/dist/components/blocks/index.d.ts +1 -0
- package/dist/components/blocks/marketing/CTASection.d.ts +28 -0
- package/dist/components/blocks/marketing/ComplianceBadgeRow.d.ts +23 -0
- package/dist/components/blocks/marketing/EvidenceTimeline.d.ts +26 -0
- package/dist/components/blocks/marketing/FeatureRow.d.ts +25 -0
- package/dist/components/blocks/marketing/Hero.d.ts +35 -0
- package/dist/components/blocks/marketing/HowItWorksSteps.d.ts +29 -0
- package/dist/components/blocks/marketing/LogoCloud.d.ts +22 -0
- package/dist/components/blocks/marketing/MarketingFooter.d.ts +42 -0
- package/dist/components/blocks/marketing/MarketingNavbar.d.ts +47 -0
- package/dist/components/blocks/marketing/PillBadge.d.ts +21 -0
- package/dist/components/blocks/marketing/SectionHeading.d.ts +23 -0
- package/dist/components/blocks/marketing/ServiceCardGrid.d.ts +65 -0
- package/dist/components/blocks/marketing/StatBand.d.ts +25 -0
- package/dist/components/blocks/marketing/TopAccentCards.d.ts +29 -0
- package/dist/components/blocks/marketing/index.d.ts +28 -0
- package/dist/components/brand/index.d.ts +1 -0
- package/dist/components/brand/isotype/Isotype.d.ts +27 -0
- package/dist/components/brand/isotype/IsotypeDiagonal.d.ts +19 -0
- package/dist/components/brand/isotype/IsotypeFrames.d.ts +21 -0
- package/dist/components/brand/isotype/IsotypeMini.d.ts +26 -0
- package/dist/components/brand/isotype/IsotypeTwist.d.ts +22 -0
- package/dist/components/brand/isotype/frame-variants.d.ts +11 -0
- package/dist/components/brand/isotype/index.d.ts +9 -0
- package/dist/components/brand/isotype/mini-patterns.d.ts +25 -0
- package/dist/components/brand/isotype/patterns.d.ts +42 -0
- package/dist/components/brand/isotype/twist-variants.d.ts +11 -0
- package/dist/components/pages/forgot-password-page/ForgotPasswordPage.d.ts +2 -2
- package/dist/components/pages/index.d.ts +1 -0
- package/dist/components/pages/landing-page/LandingPage.d.ts +18 -0
- package/dist/components/pages/landing-page/index.d.ts +1 -0
- package/dist/components/pages/login-page/LoginPage.d.ts +2 -2
- package/dist/components/pages/verify-email-page/VerifyEmailPage.d.ts +1 -1
- package/dist/components/ui/card/card.d.ts +3 -1
- package/dist/contexts/theme-data.d.ts +6 -0
- package/dist/dropdown-menu-ChQa9zW3.cjs +225 -0
- package/dist/dropdown-menu-DRvnsQAt.cjs +225 -0
- package/dist/dropdown-menu-DUk81i5Y.js +190 -0
- package/dist/dropdown-menu-NlUHhVmK.js +190 -0
- package/dist/hooks.cjs.js +2 -2
- package/dist/hooks.es.js +2 -2
- package/dist/index.cjs.js +57 -41
- package/dist/index.es.js +25 -25
- package/dist/input-Csp_Metb.cjs +91 -0
- package/dist/input-DVRwGVLx.js +65 -0
- package/dist/input-Dqyj7Hdw.cjs +91 -0
- package/dist/input-dOgrFQ-3.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 +2 -1
- package/dist/pages.es.js +1 -1
- package/dist/rich-text-editor-7K7UjLJN.cjs +2968 -0
- package/dist/rich-text-editor-B3jLP2bm.cjs +2968 -0
- package/dist/rich-text-editor-BntMVQsB.js +2894 -0
- package/dist/rich-text-editor-Diz_onyB.js +2894 -0
- package/dist/rich-text-editor-RWvK4hd3.cjs +2968 -0
- package/dist/rich-text-editor-gauEkzPM.js +2894 -0
- package/dist/select-B6PygS38.cjs +161 -0
- package/dist/select-BD763J5Y.cjs +161 -0
- package/dist/select-QJDKZITN.js +130 -0
- package/dist/select-q7aDfuPn.js +130 -0
- package/dist/sidebar-Bkk1YAsD.cjs +1154 -0
- package/dist/sidebar-CuzYeOSz.cjs +1154 -0
- package/dist/sidebar-CwlW3LXE.js +1151 -0
- package/dist/sidebar-DNkf-Ug6.js +1151 -0
- package/dist/sidebar-DaS3fplw.cjs +1154 -0
- package/dist/sidebar-FDCY42oP.js +1151 -0
- package/dist/skeleton-BtBwJ39W.js +79 -0
- package/dist/skeleton-CJyg0eJ1.cjs +88 -0
- package/dist/skeleton-D8WaauK0.cjs +88 -0
- package/dist/skeleton-DBYtjaZW.cjs +88 -0
- package/dist/skeleton-DY_lSFWf.js +79 -0
- package/dist/skeleton-Df65Ox10.js +79 -0
- package/dist/slider-BKV_vvsh.cjs +78 -0
- package/dist/slider-B_lUjFNa.cjs +78 -0
- package/dist/slider-CtBpIaU4.js +56 -0
- package/dist/slider-KgWmSJbx.js +56 -0
- package/dist/tooltip-BPttuPxU.js +48 -0
- package/dist/tooltip-CIKRzQqE.cjs +72 -0
- package/dist/tooltip-CoDByjE_.cjs +72 -0
- package/dist/tooltip-_MAwsiOL.js +48 -0
- package/dist/ui.cjs.js +13 -13
- package/dist/ui.es.js +13 -13
- package/dist/xertica-assistant-B1xAl2Vn.js +2173 -0
- package/dist/xertica-assistant-BBUMaUXw.js +2173 -0
- package/dist/xertica-assistant-CWuBwfzt.cjs +2180 -0
- package/dist/xertica-assistant-D2hlwiEP.js +2173 -0
- package/dist/xertica-assistant-DN2J_sQQ.cjs +2180 -0
- package/dist/xertica-assistant-DdYS6WXT.cjs +2180 -0
- package/dist/xertica-ui.css +1 -1
- package/llms-compact.txt +1 -1
- package/llms.txt +1 -1
- package/package.json +4 -2
- package/styles/xertica/base.css +8 -0
- package/styles/xertica/tokens.css +1 -0
- package/templates/package.json +1 -1
- package/templates/src/app/components/AuthGuard.tsx +6 -0
- package/templates/src/features/auth/ui/ForgotPasswordContent.tsx +1 -4
- package/templates/src/features/auth/ui/LoginContent.tsx +1 -4
- package/templates/src/features/auth/ui/ResetPasswordContent.tsx +1 -4
- package/templates/src/features/auth/ui/VerifyEmailContent.tsx +1 -4
- package/templates/src/locales/en/pages/landing.json +175 -0
- package/templates/src/locales/es/pages/landing.json +175 -0
- package/templates/src/locales/pt-BR/pages/landing.json +175 -0
- package/templates/src/pages/LandingPage.tsx +349 -0
- package/templates/src/styles/xertica/tokens.css +1 -0
|
@@ -0,0 +1,360 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import {
|
|
3
|
+
BookOpen,
|
|
4
|
+
Braces,
|
|
5
|
+
Code2,
|
|
6
|
+
Component,
|
|
7
|
+
Figma,
|
|
8
|
+
FileCode2,
|
|
9
|
+
Github,
|
|
10
|
+
Layers,
|
|
11
|
+
Package,
|
|
12
|
+
ShieldCheck,
|
|
13
|
+
Sparkles,
|
|
14
|
+
SquareTerminal,
|
|
15
|
+
SwatchBook,
|
|
16
|
+
} from 'lucide-react';
|
|
17
|
+
import { useTranslation } from 'react-i18next';
|
|
18
|
+
import { IsotypeDiagonal, IsotypeFrames } from '../../brand/isotype';
|
|
19
|
+
import { XerticaLogo } from '../../brand/xertica-logo';
|
|
20
|
+
import {
|
|
21
|
+
ComplianceBadgeRow,
|
|
22
|
+
CTASection,
|
|
23
|
+
FeatureRow,
|
|
24
|
+
Hero,
|
|
25
|
+
HowItWorksSteps,
|
|
26
|
+
MarketingFooter,
|
|
27
|
+
MarketingNavbar,
|
|
28
|
+
SectionHeading,
|
|
29
|
+
ServiceCardGrid,
|
|
30
|
+
StatBand,
|
|
31
|
+
TopAccentCards,
|
|
32
|
+
} from '../../blocks/marketing';
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Public marketing landing page for the Xertica-ui design system.
|
|
36
|
+
*
|
|
37
|
+
* @description
|
|
38
|
+
* A full composition of the `blocks/marketing` primitives — navbar, hero,
|
|
39
|
+
* framing cards, service grids, alternating feature rows, a "how it works"
|
|
40
|
+
* steps block, compliance badges, a closing CTA, and a footer.
|
|
41
|
+
* Renders in its own scrollable root, independent of the authenticated
|
|
42
|
+
* app shell's fixed-height layout.
|
|
43
|
+
*
|
|
44
|
+
* @ai-rules
|
|
45
|
+
* 1. This page is presentational only — all CTAs use placeholder `href="#"` links; wire real routes/handlers when integrating.
|
|
46
|
+
* 2. All visuals are token-driven placeholders (brand isotypes, inline SVG logos) to avoid any network/image dependency.
|
|
47
|
+
* 3. Keep composing exclusively from `components/blocks/marketing` — do not hand-roll sections that a block already covers.
|
|
48
|
+
* 4. All copy comes from the `landing` i18n namespace (`locales/<lang>/pages/landing.json`) via `useTranslation()` — never hardcode user-facing text here.
|
|
49
|
+
*/
|
|
50
|
+
export function LandingPage() {
|
|
51
|
+
const { t } = useTranslation();
|
|
52
|
+
|
|
53
|
+
return (
|
|
54
|
+
<div className="h-full w-full overflow-y-auto bg-background">
|
|
55
|
+
<MarketingNavbar
|
|
56
|
+
logo={<XerticaLogo className="h-8 w-auto text-primary dark:text-foreground" variant="theme" />}
|
|
57
|
+
links={[
|
|
58
|
+
{ label: t('landing.nav.product'), href: '#' },
|
|
59
|
+
{ label: t('landing.nav.resources'), href: '#' },
|
|
60
|
+
{ label: t('landing.nav.pricing'), href: '#' },
|
|
61
|
+
{ label: t('landing.nav.contact'), href: '#' },
|
|
62
|
+
]}
|
|
63
|
+
cta={{ label: t('landing.nav.login'), href: '#' }}
|
|
64
|
+
openMenuLabel={t('landing.nav.openMenu')}
|
|
65
|
+
closeMenuLabel={t('landing.nav.closeMenu')}
|
|
66
|
+
/>
|
|
67
|
+
|
|
68
|
+
<Hero
|
|
69
|
+
eyebrow={{ label: t('landing.hero.eyebrow'), icon: <Sparkles /> }}
|
|
70
|
+
title={t('landing.hero.title')}
|
|
71
|
+
description={t('landing.hero.description')}
|
|
72
|
+
primaryCta={{ label: t('landing.hero.primaryCta'), href: '#' }}
|
|
73
|
+
secondaryCta={{ label: t('landing.hero.secondaryCta'), href: '#' }}
|
|
74
|
+
// Isotype (plain grid mark) is out of rotation for now — only Diagonal + Frames are in active use.
|
|
75
|
+
// visual={<Isotype className="mx-auto w-full max-w-md" />}
|
|
76
|
+
visual={<IsotypeDiagonal className="mx-auto w-full max-w-md" />}
|
|
77
|
+
/>
|
|
78
|
+
|
|
79
|
+
<section className="mx-auto max-w-7xl px-4 py-16 sm:px-6 lg:px-8 md:py-24">
|
|
80
|
+
<TopAccentCards
|
|
81
|
+
items={[
|
|
82
|
+
{
|
|
83
|
+
title: t('landing.framing.purpose.title'),
|
|
84
|
+
body: t('landing.framing.purpose.body'),
|
|
85
|
+
color: 'chart-4',
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
title: t('landing.framing.problem.title'),
|
|
89
|
+
body: t('landing.framing.problem.body'),
|
|
90
|
+
bullets: [
|
|
91
|
+
t('landing.framing.problem.bullet1'),
|
|
92
|
+
t('landing.framing.problem.bullet2'),
|
|
93
|
+
t('landing.framing.problem.bullet3'),
|
|
94
|
+
],
|
|
95
|
+
color: 'chart-5',
|
|
96
|
+
},
|
|
97
|
+
]}
|
|
98
|
+
/>
|
|
99
|
+
</section>
|
|
100
|
+
|
|
101
|
+
<section className="bg-card">
|
|
102
|
+
<div className="mx-auto max-w-7xl px-4 py-16 sm:px-6 lg:px-8 md:py-24">
|
|
103
|
+
<SectionHeading
|
|
104
|
+
title={t('landing.ecosystem.title')}
|
|
105
|
+
accentColor="chart-1"
|
|
106
|
+
className="mb-12"
|
|
107
|
+
/>
|
|
108
|
+
|
|
109
|
+
<ServiceCardGrid
|
|
110
|
+
variant="card"
|
|
111
|
+
columns={4}
|
|
112
|
+
items={[
|
|
113
|
+
{
|
|
114
|
+
icon: <Figma />,
|
|
115
|
+
color: 'primary',
|
|
116
|
+
title: t('landing.ecosystem.figma.title'),
|
|
117
|
+
description: t('landing.ecosystem.figma.description'),
|
|
118
|
+
bullets: [
|
|
119
|
+
t('landing.ecosystem.figma.bullet1'),
|
|
120
|
+
t('landing.ecosystem.figma.bullet2'),
|
|
121
|
+
],
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
icon: <FileCode2 />,
|
|
125
|
+
color: 'chart-1',
|
|
126
|
+
title: t('landing.ecosystem.vscode.title'),
|
|
127
|
+
description: t('landing.ecosystem.vscode.description'),
|
|
128
|
+
bullets: [
|
|
129
|
+
t('landing.ecosystem.vscode.bullet1'),
|
|
130
|
+
t('landing.ecosystem.vscode.bullet2'),
|
|
131
|
+
],
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
icon: <Component />,
|
|
135
|
+
color: 'chart-5',
|
|
136
|
+
title: t('landing.ecosystem.storybook.title'),
|
|
137
|
+
description: t('landing.ecosystem.storybook.description'),
|
|
138
|
+
bullets: [
|
|
139
|
+
t('landing.ecosystem.storybook.bullet1'),
|
|
140
|
+
t('landing.ecosystem.storybook.bullet2'),
|
|
141
|
+
],
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
icon: <Github />,
|
|
145
|
+
color: 'chart-2',
|
|
146
|
+
title: t('landing.ecosystem.github.title'),
|
|
147
|
+
description: t('landing.ecosystem.github.description'),
|
|
148
|
+
bullets: [
|
|
149
|
+
t('landing.ecosystem.github.bullet1'),
|
|
150
|
+
t('landing.ecosystem.github.bullet2'),
|
|
151
|
+
],
|
|
152
|
+
},
|
|
153
|
+
]}
|
|
154
|
+
/>
|
|
155
|
+
</div>
|
|
156
|
+
</section>
|
|
157
|
+
|
|
158
|
+
<FeatureRow
|
|
159
|
+
eyebrow={t('landing.features.theming.eyebrow')}
|
|
160
|
+
title={t('landing.features.theming.title')}
|
|
161
|
+
description={t('landing.features.theming.description')}
|
|
162
|
+
bullets={[
|
|
163
|
+
t('landing.features.theming.bullet1'),
|
|
164
|
+
t('landing.features.theming.bullet2'),
|
|
165
|
+
t('landing.features.theming.bullet3'),
|
|
166
|
+
]}
|
|
167
|
+
visual={<IsotypeFrames variant="blue-magenta" className="mx-auto w-full max-w-md" />}
|
|
168
|
+
/>
|
|
169
|
+
|
|
170
|
+
<FeatureRow
|
|
171
|
+
tone="card"
|
|
172
|
+
reverse
|
|
173
|
+
eyebrow={t('landing.features.composition.eyebrow')}
|
|
174
|
+
title={t('landing.features.composition.title')}
|
|
175
|
+
description={t('landing.features.composition.description')}
|
|
176
|
+
bullets={[
|
|
177
|
+
t('landing.features.composition.bullet1'),
|
|
178
|
+
t('landing.features.composition.bullet2'),
|
|
179
|
+
t('landing.features.composition.bullet3'),
|
|
180
|
+
]}
|
|
181
|
+
visual={<IsotypeDiagonal className="mx-auto w-full max-w-md" />}
|
|
182
|
+
/>
|
|
183
|
+
|
|
184
|
+
<FeatureRow
|
|
185
|
+
eyebrow={t('landing.features.onboarding.eyebrow')}
|
|
186
|
+
title={t('landing.features.onboarding.title')}
|
|
187
|
+
description={t('landing.features.onboarding.description')}
|
|
188
|
+
bullets={[
|
|
189
|
+
t('landing.features.onboarding.bullet1'),
|
|
190
|
+
t('landing.features.onboarding.bullet2'),
|
|
191
|
+
t('landing.features.onboarding.bullet3'),
|
|
192
|
+
]}
|
|
193
|
+
visual={<IsotypeFrames variant="blue-yellow" className="mx-auto w-full max-w-md" />}
|
|
194
|
+
/>
|
|
195
|
+
|
|
196
|
+
<section className="mx-auto max-w-7xl px-4 py-16 sm:px-6 lg:px-8 md:py-24">
|
|
197
|
+
<SectionHeading
|
|
198
|
+
title={t('landing.useCases.title')}
|
|
199
|
+
accentColor="chart-4"
|
|
200
|
+
className="mb-12"
|
|
201
|
+
/>
|
|
202
|
+
|
|
203
|
+
<ServiceCardGrid
|
|
204
|
+
variant="card"
|
|
205
|
+
columns={3}
|
|
206
|
+
items={[
|
|
207
|
+
{
|
|
208
|
+
color: 'chart-2',
|
|
209
|
+
title: t('landing.useCases.ecommerce.title'),
|
|
210
|
+
description: t('landing.useCases.ecommerce.description'),
|
|
211
|
+
checklist: [
|
|
212
|
+
t('landing.useCases.ecommerce.check1'),
|
|
213
|
+
t('landing.useCases.ecommerce.check2'),
|
|
214
|
+
],
|
|
215
|
+
className: 'border',
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
color: 'primary',
|
|
219
|
+
title: t('landing.useCases.saas.title'),
|
|
220
|
+
description: t('landing.useCases.saas.description'),
|
|
221
|
+
checklist: [t('landing.useCases.saas.check1'), t('landing.useCases.saas.check2')],
|
|
222
|
+
className: 'border',
|
|
223
|
+
},
|
|
224
|
+
{
|
|
225
|
+
color: 'chart-4',
|
|
226
|
+
title: t('landing.useCases.fintech.title'),
|
|
227
|
+
description: t('landing.useCases.fintech.description'),
|
|
228
|
+
checklist: [
|
|
229
|
+
t('landing.useCases.fintech.check1'),
|
|
230
|
+
t('landing.useCases.fintech.check2'),
|
|
231
|
+
],
|
|
232
|
+
className: 'border',
|
|
233
|
+
},
|
|
234
|
+
]}
|
|
235
|
+
/>
|
|
236
|
+
</section>
|
|
237
|
+
|
|
238
|
+
<section className="bg-sidebar text-sidebar-foreground">
|
|
239
|
+
<div className="mx-auto max-w-5xl px-4 py-16 sm:px-6 lg:px-8 md:py-24">
|
|
240
|
+
<SectionHeading
|
|
241
|
+
title={t('landing.methodology.title')}
|
|
242
|
+
tone="sidebar"
|
|
243
|
+
accentColor="primary"
|
|
244
|
+
className="mb-12"
|
|
245
|
+
/>
|
|
246
|
+
|
|
247
|
+
<HowItWorksSteps
|
|
248
|
+
items={[
|
|
249
|
+
{
|
|
250
|
+
icon: <SwatchBook />,
|
|
251
|
+
title: t('landing.methodology.tokensFirst.title'),
|
|
252
|
+
description: t('landing.methodology.tokensFirst.description'),
|
|
253
|
+
color: 'chart-4',
|
|
254
|
+
},
|
|
255
|
+
{
|
|
256
|
+
icon: <Layers />,
|
|
257
|
+
title: t('landing.methodology.primitives.title'),
|
|
258
|
+
description: t('landing.methodology.primitives.description'),
|
|
259
|
+
color: 'chart-2',
|
|
260
|
+
},
|
|
261
|
+
{
|
|
262
|
+
icon: <Braces />,
|
|
263
|
+
title: t('landing.methodology.compositionBlocks.title'),
|
|
264
|
+
description: t('landing.methodology.compositionBlocks.description'),
|
|
265
|
+
color: 'chart-1',
|
|
266
|
+
},
|
|
267
|
+
{
|
|
268
|
+
icon: <SquareTerminal />,
|
|
269
|
+
title: t('landing.methodology.cliTemplates.title'),
|
|
270
|
+
description: t('landing.methodology.cliTemplates.description'),
|
|
271
|
+
color: 'warning',
|
|
272
|
+
},
|
|
273
|
+
]}
|
|
274
|
+
/>
|
|
275
|
+
</div>
|
|
276
|
+
</section>
|
|
277
|
+
|
|
278
|
+
<section className="mx-auto max-w-7xl px-4 py-16 sm:px-6 lg:px-8 md:py-24">
|
|
279
|
+
<div className="rounded-2xl border border-border bg-card p-8 md:p-12">
|
|
280
|
+
<ComplianceBadgeRow
|
|
281
|
+
badges={[
|
|
282
|
+
{
|
|
283
|
+
icon: <ShieldCheck />,
|
|
284
|
+
label: t('landing.compliance.wcag.label'),
|
|
285
|
+
description: t('landing.compliance.wcag.description'),
|
|
286
|
+
},
|
|
287
|
+
{
|
|
288
|
+
icon: <Code2 />,
|
|
289
|
+
label: t('landing.compliance.typescript.label'),
|
|
290
|
+
description: t('landing.compliance.typescript.description'),
|
|
291
|
+
},
|
|
292
|
+
{
|
|
293
|
+
icon: <Package />,
|
|
294
|
+
label: t('landing.compliance.treeShakeable.label'),
|
|
295
|
+
description: t('landing.compliance.treeShakeable.description'),
|
|
296
|
+
},
|
|
297
|
+
{
|
|
298
|
+
icon: <BookOpen />,
|
|
299
|
+
label: t('landing.compliance.themeable.label'),
|
|
300
|
+
description: t('landing.compliance.themeable.description'),
|
|
301
|
+
},
|
|
302
|
+
]}
|
|
303
|
+
/>
|
|
304
|
+
|
|
305
|
+
<div className="my-10 border-t border-border" />
|
|
306
|
+
|
|
307
|
+
<StatBand
|
|
308
|
+
columns={3}
|
|
309
|
+
stats={[
|
|
310
|
+
{ value: '99.9%', label: t('landing.stats.docsUptime'), color: 'success' },
|
|
311
|
+
{ value: '<24h', label: t('landing.stats.avgResponseTime'), color: 'info' },
|
|
312
|
+
{ value: '100%', label: t('landing.stats.typedComponents'), color: 'chart-5' },
|
|
313
|
+
]}
|
|
314
|
+
/>
|
|
315
|
+
</div>
|
|
316
|
+
</section>
|
|
317
|
+
|
|
318
|
+
<section className="mx-auto max-w-7xl px-4 pb-16 sm:px-6 lg:px-8 md:pb-24">
|
|
319
|
+
<CTASection
|
|
320
|
+
title={t('landing.cta.title')}
|
|
321
|
+
description={t('landing.cta.description')}
|
|
322
|
+
cta={{ label: t('landing.cta.primary'), href: '#' }}
|
|
323
|
+
secondaryCta={{ label: t('landing.cta.secondary'), href: '#' }}
|
|
324
|
+
/>
|
|
325
|
+
</section>
|
|
326
|
+
|
|
327
|
+
<MarketingFooter
|
|
328
|
+
logo={<XerticaLogo className="h-8 w-auto text-primary dark:text-foreground" variant="theme" />}
|
|
329
|
+
tagline={t('landing.footer.tagline')}
|
|
330
|
+
columns={[
|
|
331
|
+
{
|
|
332
|
+
heading: t('landing.footer.product.heading'),
|
|
333
|
+
links: [
|
|
334
|
+
{ label: t('landing.footer.product.components'), href: '#' },
|
|
335
|
+
{ label: t('landing.footer.product.blocks'), href: '#' },
|
|
336
|
+
{ label: t('landing.footer.product.themes'), href: '#' },
|
|
337
|
+
],
|
|
338
|
+
},
|
|
339
|
+
{
|
|
340
|
+
heading: t('landing.footer.resources.heading'),
|
|
341
|
+
links: [
|
|
342
|
+
{ label: t('landing.footer.resources.docs'), href: '#' },
|
|
343
|
+
{ label: t('landing.footer.resources.cli'), href: '#' },
|
|
344
|
+
{ label: t('landing.footer.resources.changelog'), href: '#' },
|
|
345
|
+
],
|
|
346
|
+
},
|
|
347
|
+
{
|
|
348
|
+
heading: t('landing.footer.company.heading'),
|
|
349
|
+
links: [
|
|
350
|
+
{ label: t('landing.footer.company.about'), href: '#' },
|
|
351
|
+
{ label: t('landing.footer.company.contact'), href: '#' },
|
|
352
|
+
{ label: t('landing.footer.company.careers'), href: '#' },
|
|
353
|
+
],
|
|
354
|
+
},
|
|
355
|
+
]}
|
|
356
|
+
copyright={t('landing.footer.copyright')}
|
|
357
|
+
/>
|
|
358
|
+
</div>
|
|
359
|
+
);
|
|
360
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { LandingPage } from './LandingPage';
|
|
@@ -22,13 +22,13 @@ interface LoginPageProps {
|
|
|
22
22
|
* Standard Login Page component.
|
|
23
23
|
*
|
|
24
24
|
* @description
|
|
25
|
-
* A complete, responsive login screen featuring a
|
|
25
|
+
* A complete, responsive login screen featuring a brand isotype side-panel (on desktop)
|
|
26
26
|
* and a focused authentication form. Supports email/password login and placeholders
|
|
27
27
|
* for Social/SSO providers (Google, MT Login, gov.br).
|
|
28
28
|
*
|
|
29
29
|
* @ai-rules
|
|
30
30
|
* 1. State Management: You must provide the `onLogin` callback to handle the actual authentication logic.
|
|
31
|
-
* 2. Visuals:
|
|
31
|
+
* 2. Visuals: Left panel renders `IsotypeTwist` via `AuthPageShell`'s `leftPanel="isotype"` — no hero image on this page.
|
|
32
32
|
* 3. Routing: Uses `react-router` for navigation to the forgot-password flow.
|
|
33
33
|
*/
|
|
34
34
|
export function LoginPage({ onLogin }: LoginPageProps) {
|
|
@@ -62,10 +62,7 @@ export function LoginPage({ onLogin }: LoginPageProps) {
|
|
|
62
62
|
};
|
|
63
63
|
|
|
64
64
|
return (
|
|
65
|
-
<AuthPageShell
|
|
66
|
-
imageSrc="https://images.unsplash.com/photo-1551434678-e076c223a692?w=1200&h=800&fit=crop&auto=format"
|
|
67
|
-
imageAlt={t('login.heroImageAlt')}
|
|
68
|
-
>
|
|
65
|
+
<AuthPageShell leftPanel="isotype">
|
|
69
66
|
<div className="text-center">
|
|
70
67
|
<div className="flex items-center justify-center mb-4">
|
|
71
68
|
<XerticaLogo className="h-12 w-auto text-primary dark:text-foreground" variant="theme" />
|
|
@@ -62,10 +62,7 @@ export function ResetPasswordPage() {
|
|
|
62
62
|
};
|
|
63
63
|
|
|
64
64
|
return (
|
|
65
|
-
<AuthPageShell
|
|
66
|
-
imageSrc="https://images.unsplash.com/photo-1555949963-aa79dcee981c?w=1200&h=800&fit=crop&auto=format"
|
|
67
|
-
imageAlt={t('resetPassword.heroImageAlt')}
|
|
68
|
-
>
|
|
65
|
+
<AuthPageShell leftPanel="isotype">
|
|
69
66
|
<button
|
|
70
67
|
onClick={() => navigate('/login')}
|
|
71
68
|
className="flex items-center gap-2 text-muted-foreground hover:text-foreground transition-colors"
|
|
@@ -12,7 +12,7 @@ import { useTranslation } from 'react-i18next';
|
|
|
12
12
|
* @description
|
|
13
13
|
* An informative screen shown after a password reset request. It instructs
|
|
14
14
|
* the user to check their email and provides a "Resend email" feature.
|
|
15
|
-
* Includes the same consistent layout with
|
|
15
|
+
* Includes the same consistent layout with the shared brand isotype side-panel.
|
|
16
16
|
*
|
|
17
17
|
* @ai-rules
|
|
18
18
|
* 1. Data Source: It retrieves the email from the router's `location.state`.
|
|
@@ -46,10 +46,7 @@ export function VerifyEmailPage() {
|
|
|
46
46
|
};
|
|
47
47
|
|
|
48
48
|
return (
|
|
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
|
-
>
|
|
49
|
+
<AuthPageShell leftPanel="isotype">
|
|
53
50
|
<div className="text-center">
|
|
54
51
|
<div className="flex items-center justify-center mb-4">
|
|
55
52
|
<XerticaLogo className="h-12 w-auto text-primary dark:text-foreground" variant="theme" />
|
|
@@ -16,7 +16,7 @@ describe('Button', () => {
|
|
|
16
16
|
expect(screen.getByRole('button')).toHaveClass('bg-destructive');
|
|
17
17
|
|
|
18
18
|
rerender(<Button variant="outline">Outline</Button>);
|
|
19
|
-
expect(screen.getByRole('button')).toHaveClass('border-
|
|
19
|
+
expect(screen.getByRole('button')).toHaveClass('border-outline-stroke');
|
|
20
20
|
|
|
21
21
|
rerender(<Button variant="ghost">Ghost</Button>);
|
|
22
22
|
expect(screen.getByRole('button')).toHaveClass('hover:bg-accent');
|
|
@@ -6,22 +6,23 @@ import { Loader2 } from 'lucide-react';
|
|
|
6
6
|
import { cn } from '../../shared/utils';
|
|
7
7
|
|
|
8
8
|
const buttonVariants = cva(
|
|
9
|
-
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-[var(--radius-button)] text-sm font-
|
|
9
|
+
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-[var(--radius-button)] text-sm font-button-weight transition-colors disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
|
|
10
10
|
{
|
|
11
11
|
variants: {
|
|
12
12
|
variant: {
|
|
13
13
|
default: 'border-button-stroke bg-button-primary text-button-primary',
|
|
14
14
|
destructive:
|
|
15
|
-
'
|
|
16
|
-
outline:
|
|
15
|
+
'bg-destructive text-destructive-foreground hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40',
|
|
16
|
+
outline:
|
|
17
|
+
'border border-outline-stroke bg-background hover:bg-accent hover:text-accent-foreground',
|
|
17
18
|
secondary: 'border-button-stroke bg-button-secondary text-button-secondary',
|
|
18
19
|
ghost: 'hover:bg-accent hover:text-accent-foreground',
|
|
19
20
|
link: 'text-primary underline-offset-4 hover:underline',
|
|
20
21
|
success:
|
|
21
|
-
'
|
|
22
|
-
info: '
|
|
22
|
+
'bg-success text-success-foreground hover:bg-success/90 focus-visible:ring-success/20 dark:focus-visible:ring-success/40',
|
|
23
|
+
info: 'bg-info text-info-foreground hover:bg-info/90 focus-visible:ring-info/20 dark:focus-visible:ring-info/40',
|
|
23
24
|
warning:
|
|
24
|
-
'
|
|
25
|
+
'bg-warning text-warning-foreground hover:bg-warning/90 focus-visible:ring-warning/20 dark:focus-visible:ring-warning/40',
|
|
25
26
|
},
|
|
26
27
|
size: {
|
|
27
28
|
default: 'h-9 px-4 py-2 has-[>svg]:px-3',
|
|
@@ -52,4 +52,16 @@ describe('Card', () => {
|
|
|
52
52
|
const { container } = render(<Card className="custom-card">Content</Card>);
|
|
53
53
|
expect(container.querySelector('[data-slot="card"]')).toHaveClass('custom-card');
|
|
54
54
|
});
|
|
55
|
+
|
|
56
|
+
it('defaults to the theme-reactive card background and foreground', () => {
|
|
57
|
+
const { container } = render(<Card>Content</Card>);
|
|
58
|
+
expect(container.querySelector('[data-slot="card"]')).toHaveClass('bg-card', 'text-card-foreground');
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('pairs variant="white" with a fixed dark text color, not the theme-reactive one', () => {
|
|
62
|
+
const { container } = render(<Card variant="white">Content</Card>);
|
|
63
|
+
const card = container.querySelector('[data-slot="card"]');
|
|
64
|
+
expect(card).toHaveClass('bg-white', 'text-neutral-900');
|
|
65
|
+
expect(card).not.toHaveClass('text-card-foreground');
|
|
66
|
+
});
|
|
55
67
|
});
|
|
@@ -7,7 +7,9 @@ export interface CardProps extends React.ComponentProps<'div'> {
|
|
|
7
7
|
* Background variant.
|
|
8
8
|
* - `default`: uses the theme's `--card` token (e.g. Marfim on the Xertica theme).
|
|
9
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.
|
|
10
|
+
* for cards that should stand out from the theme's own card tone. Pairs
|
|
11
|
+
* with a fixed dark text color, since the theme's `--card-foreground` can
|
|
12
|
+
* be near-white in dark mode.
|
|
11
13
|
*/
|
|
12
14
|
variant?: 'default' | 'white';
|
|
13
15
|
}
|
|
@@ -30,8 +32,8 @@ function Card({ className, variant = 'default', ...props }: CardProps) {
|
|
|
30
32
|
<div
|
|
31
33
|
data-slot="card"
|
|
32
34
|
className={cn(
|
|
33
|
-
'
|
|
34
|
-
variant === 'white' ? 'bg-white' : 'bg-card',
|
|
35
|
+
'flex flex-col gap-6 rounded-[var(--radius-card)]',
|
|
36
|
+
variant === 'white' ? 'bg-white text-neutral-900' : 'bg-card text-card-foreground',
|
|
35
37
|
className
|
|
36
38
|
)}
|
|
37
39
|
{...props}
|
|
@@ -1,10 +1,23 @@
|
|
|
1
1
|
import { render, screen, fireEvent } from '@testing-library/react';
|
|
2
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
3
3
|
import { InputOTP, InputOTPGroup, InputOTPSlot, InputOTPSeparator } from './input-otp';
|
|
4
4
|
import { OTPInputContext } from 'input-otp';
|
|
5
5
|
import React from 'react';
|
|
6
6
|
|
|
7
7
|
describe('InputOTP', () => {
|
|
8
|
+
// input-otp schedules internal setTimeout(0/10/50) calls on mount that it
|
|
9
|
+
// never cancels on unmount. Under real timers those fire after this file's
|
|
10
|
+
// jsdom environment is torn down, throwing `window is not defined` as an
|
|
11
|
+
// unhandled error in later test files. Fake timers keep them from ever
|
|
12
|
+
// firing for real.
|
|
13
|
+
beforeEach(() => {
|
|
14
|
+
vi.useFakeTimers();
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
afterEach(() => {
|
|
18
|
+
vi.useRealTimers();
|
|
19
|
+
});
|
|
20
|
+
|
|
8
21
|
it('renders children correctly', () => {
|
|
9
22
|
render(
|
|
10
23
|
<OTPInputContext.Provider value={{ slots: [{ char: 'A' }] } as any}>
|
|
@@ -87,13 +87,13 @@ export const BrandColorsProvider: React.FC<BrandColorsProviderProps> = ({
|
|
|
87
87
|
const gradientStart = isDark ? colors.gradientStartDark : colors.gradientStart;
|
|
88
88
|
const gradientEnd = isDark ? colors.gradientEndDark : colors.gradientEnd;
|
|
89
89
|
|
|
90
|
-
// --- Semantic Colors (Fixed Standard) ---
|
|
90
|
+
// --- Semantic Colors (Fixed Standard, overridable per theme in light mode) ---
|
|
91
91
|
// Success (Green), Warning (Amber/Yellow), Info (Blue), Destructive (Red)
|
|
92
92
|
const semantic = {
|
|
93
|
-
success: isDark ? '#4ade80' : '#047857', // Green 400 / 700
|
|
94
|
-
warning: isDark ? '#fbbf24' : '#b45309', // Amber 400 / 700
|
|
95
|
-
info: isDark ? '#60a5fa' : '#1d4ed8', // Blue 400 / 700
|
|
96
|
-
destructive: isDark ? '#f87171' : '#b91c1c', // Red 400 / 700
|
|
93
|
+
success: isDark ? '#4ade80' : colors.successColor || '#047857', // Green 400 / 700
|
|
94
|
+
warning: isDark ? '#fbbf24' : colors.warningColor || '#b45309', // Amber 400 / 700
|
|
95
|
+
info: isDark ? '#60a5fa' : colors.infoColor || '#1d4ed8', // Blue 400 / 700
|
|
96
|
+
destructive: isDark ? '#f87171' : colors.destructiveColor || '#b91c1c', // Red 400 / 700
|
|
97
97
|
foreground: isDark ? '#050505' : '#ffffff',
|
|
98
98
|
};
|
|
99
99
|
|
|
@@ -135,6 +135,7 @@ export const BrandColorsProvider: React.FC<BrandColorsProviderProps> = ({
|
|
|
135
135
|
// 3b. Button shape overrides (identical in both modes; theme-specific, always emitted)
|
|
136
136
|
cssVars.push(`--radius-button: ${colors.buttonRadius || '12px'}`);
|
|
137
137
|
cssVars.push(`--button-stroke-width: ${colors.buttonStrokeWidth || '0px'}`);
|
|
138
|
+
cssVars.push(`--button-font-weight: ${colors.buttonFontWeight || '500'}`);
|
|
138
139
|
|
|
139
140
|
// 4. Charts
|
|
140
141
|
cssVars.push(`--chart-1: ${colors.chart1}`);
|
|
@@ -222,6 +223,9 @@ export const BrandColorsProvider: React.FC<BrandColorsProviderProps> = ({
|
|
|
222
223
|
if (colors.inputBorderColor) {
|
|
223
224
|
lightModeVars.push(`--input-border: ${colors.inputBorderColor}`);
|
|
224
225
|
}
|
|
226
|
+
if (colors.outlineBorderColor) {
|
|
227
|
+
lightModeVars.push(`--outline-border: ${colors.outlineBorderColor}`);
|
|
228
|
+
}
|
|
225
229
|
|
|
226
230
|
darkSurfaceVars.push(
|
|
227
231
|
`--button-stroke-color: ${colors.buttonStrokeColorDark || colors.buttonStrokeColor || 'transparent'}`
|
package/contexts/theme-data.ts
CHANGED
|
@@ -49,11 +49,29 @@ export interface BrandColors {
|
|
|
49
49
|
// always-visible stroke regardless of the surrounding background.
|
|
50
50
|
inputBorderColor?: string;
|
|
51
51
|
|
|
52
|
+
// Optional: light-mode border color for the Button `outline` variant only
|
|
53
|
+
// — defaults to the shared --border token when unset. Same rationale as
|
|
54
|
+
// inputBorderColor: an outline button needs a stronger, always-visible
|
|
55
|
+
// contour when a theme makes --background and --muted identical, without
|
|
56
|
+
// affecting structural strokes elsewhere.
|
|
57
|
+
outlineBorderColor?: string;
|
|
58
|
+
|
|
59
|
+
// Optional: light-mode overrides for the semantic status colors (success,
|
|
60
|
+
// warning, info, destructive) used by alerts, badges, and status buttons —
|
|
61
|
+
// each defaults to the shared fixed value (see BrandColorsContext) when
|
|
62
|
+
// unset. Dark mode always uses the fixed value regardless of these, since
|
|
63
|
+
// no dark-mode-specific brand value has been defined yet.
|
|
64
|
+
successColor?: string;
|
|
65
|
+
warningColor?: string;
|
|
66
|
+
infoColor?: string;
|
|
67
|
+
destructiveColor?: string;
|
|
68
|
+
|
|
52
69
|
// Optional: Button shape & stroke overrides (defaults preserve current look when unset)
|
|
53
70
|
buttonRadius?: string;
|
|
54
71
|
buttonStrokeWidth?: string;
|
|
55
72
|
buttonStrokeColor?: string;
|
|
56
73
|
buttonStrokeColorDark?: string;
|
|
74
|
+
buttonFontWeight?: string;
|
|
57
75
|
|
|
58
76
|
// Optional: Button `secondary` variant colors (defaults to the shared --secondary token when unset)
|
|
59
77
|
buttonSecondaryBg?: string;
|
|
@@ -158,10 +176,12 @@ export const colorThemes: ColorTheme[] = [
|
|
|
158
176
|
darkMuted: '#26241f',
|
|
159
177
|
darkBorder: '#38352c',
|
|
160
178
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
179
|
+
// Flat fill, same value on every stop — this theme uses solid ink
|
|
180
|
+
// colors, never gradients (Negro Xertica, matching sidebar/secondary-button).
|
|
181
|
+
gradientStart: '#1E1E1E',
|
|
182
|
+
gradientEnd: '#1E1E1E',
|
|
183
|
+
gradientStartDark: '#1E1E1E',
|
|
184
|
+
gradientEndDark: '#1E1E1E',
|
|
165
185
|
|
|
166
186
|
chart1: '#3D63AC', // Azul
|
|
167
187
|
chart2: '#1899AF', // Celeste
|
|
@@ -172,11 +192,18 @@ export const colorThemes: ColorTheme[] = [
|
|
|
172
192
|
backgroundLight: '#E9E6DC', // page/shell background
|
|
173
193
|
cardLight: '#FFFEF8', // Marfim — cards, header, assistant panel
|
|
174
194
|
inputBorderColor: 'rgba(30, 30, 30, 0.4)', // Negro Xertica stroke — form fields only
|
|
195
|
+
outlineBorderColor: 'rgba(30, 30, 30, 0.4)', // Negro Xertica stroke — Button `outline` variant only
|
|
196
|
+
|
|
197
|
+
successColor: '#2E8B5A', // Verde
|
|
198
|
+
warningColor: '#E37744', // Naranja
|
|
199
|
+
infoColor: '#3D63AC', // Azul
|
|
200
|
+
destructiveColor: '#DE5B48', // Rojo Coral
|
|
175
201
|
|
|
176
202
|
buttonRadius: '9999px',
|
|
177
|
-
buttonStrokeWidth: '
|
|
203
|
+
buttonStrokeWidth: '1px',
|
|
178
204
|
buttonStrokeColor: '#1E1E1E',
|
|
179
205
|
buttonStrokeColorDark: '#F2EDD8', // Crema
|
|
206
|
+
buttonFontWeight: '400',
|
|
180
207
|
|
|
181
208
|
buttonSecondaryBg: '#1E1E1E',
|
|
182
209
|
buttonSecondaryBgDark: '#1E1E1E',
|