@gradial/aci 0.1.20-preview.3 → 0.1.21
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/README.md +48 -108
- package/bin/{aci → aci-darwin-arm64} +0 -0
- package/bin/aci.js +99 -6
- package/dist/assets/index.d.ts +3 -3
- package/dist/assets/index.js +3 -3
- package/dist/astro/index.d.ts +12 -3
- package/dist/astro/index.js +20 -5
- package/dist/content/assets.d.ts +17 -0
- package/dist/content/assets.js +65 -0
- package/dist/content/index.d.ts +1 -0
- package/dist/content/index.js +1 -0
- package/dist/define-component.d.ts +1 -2
- package/dist/define-component.js +73 -5
- package/dist/dev/content-watch.d.ts +37 -0
- package/dist/dev/content-watch.js +154 -0
- package/dist/dev/index.d.ts +24 -43
- package/dist/dev/index.js +4 -169
- package/dist/index.d.ts +14 -6
- package/dist/index.js +8 -9
- package/dist/next/asset-route.js +2 -2
- package/dist/next/config.d.ts +2 -2
- package/dist/next/config.js +5 -5
- package/dist/next/content-watch.js +6 -1
- package/dist/next/index.d.ts +2 -2
- package/dist/next/index.js +2 -2
- package/dist/next/middleware.d.ts +1 -0
- package/dist/next/middleware.js +49 -8
- package/dist/next/page.d.ts +1 -1
- package/dist/next/page.js +14 -12
- package/dist/next/render.d.ts +3 -3
- package/dist/next/render.js +11 -9
- package/dist/next/server.d.ts +12 -12
- package/dist/next/server.js +1 -1
- package/dist/preview/core.d.ts +5 -0
- package/dist/preview/core.js +1 -1
- package/dist/react/Media.d.ts +24 -0
- package/dist/react/Media.js +30 -0
- package/dist/react/Picture.d.ts +12 -0
- package/dist/react/Picture.js +11 -0
- package/dist/react/VideoPlayer.d.ts +13 -0
- package/dist/react/{GradialVideoPlayer.js → VideoPlayer.js} +6 -5
- package/dist/react/index.d.ts +333 -3
- package/dist/react/index.js +89 -3
- package/dist/registry.d.ts +2 -2
- package/dist/registry.js +3 -3
- package/dist/types/block-ref.d.ts +26 -0
- package/dist/types/block-ref.js +28 -0
- package/dist/types/component.d.ts +36 -32
- package/dist/types/config.d.ts +1 -1
- package/dist/types/image.d.ts +33 -20
- package/dist/types/image.js +42 -50
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.js +1 -0
- package/dist/types/link.d.ts +38 -0
- package/dist/types/link.js +58 -0
- package/dist/types/media.d.ts +16 -35
- package/dist/types/media.js +15 -84
- package/dist/types/renderer.d.ts +1 -1
- package/dist/types/rich-text.d.ts +40 -0
- package/dist/types/rich-text.js +39 -0
- package/dist/types/video.d.ts +7 -13
- package/dist/types/video.js +3 -3
- package/package.json +29 -76
- package/src/cli/compile-registry.mjs +120 -63
- package/src/cli/validate-content.mjs +38 -75
- package/src/types/block-ref.ts +74 -0
- package/src/types/image.ts +79 -60
- package/templates/astro/template/.aci.yaml +28 -0
- package/templates/astro/template/.storybook/main.ts +79 -0
- package/templates/astro/template/.storybook/manager.ts +6 -0
- package/templates/astro/template/.storybook/preview.ts +33 -0
- package/templates/astro/template/astro.config.mjs +37 -0
- package/templates/astro/template/package.json.tmpl +41 -0
- package/templates/astro/template/src/cms/contracts/layouts/index.ts +18 -0
- package/templates/astro/template/src/cms/registry.ts +28 -0
- package/templates/astro/template/src/components/chrome/Footer.astro +107 -0
- package/templates/astro/template/src/components/chrome/Header.astro +94 -0
- package/templates/astro/template/src/components/elements/AtomicBlock.astro +15 -0
- package/templates/astro/template/src/components/elements/AtomicBlocks.astro +16 -0
- package/templates/astro/template/src/components/elements/Button.astro +51 -0
- package/templates/astro/template/src/components/elements/ButtonBlock.astro +25 -0
- package/templates/astro/template/src/components/elements/Container.astro +11 -0
- package/templates/astro/template/src/components/elements/Icon.astro +31 -0
- package/templates/astro/template/src/components/elements/Logo.astro +29 -0
- package/templates/astro/template/src/components/elements/RemoteImage.astro +41 -0
- package/templates/astro/template/src/components/sections/ContainerSection.astro +32 -0
- package/templates/astro/template/src/components/sections/FeatureCard.astro +23 -0
- package/templates/astro/template/src/components/sections/FeatureGrid.astro +35 -0
- package/templates/astro/template/src/components/sections/HomeHero.astro +51 -0
- package/templates/astro/template/src/components/sections/StatsBar.astro +33 -0
- package/templates/astro/template/src/design-system/styles.css +399 -0
- package/templates/astro/template/src/design-system/tokens/colors.css +60 -0
- package/templates/astro/template/src/design-system/tokens/components.css +69 -0
- package/templates/astro/template/src/design-system/tokens/index.css +6 -0
- package/templates/astro/template/src/design-system/tokens/radii.css +5 -0
- package/templates/astro/template/src/design-system/tokens/shadows.css +5 -0
- package/templates/astro/template/src/design-system/tokens/spacing.css +13 -0
- package/templates/astro/template/src/design-system/tokens/typography.css +24 -0
- package/templates/astro/template/src/env.d.ts +1 -0
- package/templates/astro/template/src/fixtures/atomicBlocks.ts +33 -0
- package/templates/astro/template/src/fixtures/composition.ts +31 -0
- package/templates/astro/template/src/fixtures/index.ts +17 -0
- package/templates/astro/template/src/fixtures/sections.ts +39 -0
- package/templates/astro/template/src/layouts/SiteLayout.astro +30 -0
- package/templates/astro/template/src/pages/[...slug].astro +61 -0
- package/templates/astro/template/src/render/RenderPage.astro +66 -0
- package/templates/astro/template/src/stories/CompositionStoryFrame.astro +15 -0
- package/templates/astro/template/src/stories/HomeHero.stories.ts +24 -0
- package/templates/astro/template/src/stories/SectionStoryFrame.astro +16 -0
- package/templates/astro/template/src/stories/StoryDocFrame.astro +31 -0
- package/templates/astro/template/tsconfig.json +11 -0
- package/templates/common/.agents/AGENTS.md +37 -0
- package/templates/common/.agents/skills/aci/SKILL.md +162 -0
- package/templates/common/.agents/skills/authoring/SKILL.md +590 -0
- package/templates/common/.agents/skills/authoring/references/block-registry.md +151 -0
- package/templates/common/.agents/skills/authoring/references/contracts.md +153 -0
- package/templates/common/.agents/skills/authoring/references/design-system.md +171 -0
- package/templates/common/.agents/skills/authoring/references/image-assets.md +73 -0
- package/templates/common/.agents/skills/authoring/references/repo-structure.md +157 -0
- package/templates/common/.agents/skills/migration/SKILL.md +160 -0
- package/templates/common/.agents/skills/migration/references/analyzing-source.md +215 -0
- package/templates/common/.agents/skills/migration/references/component-migration.md +200 -0
- package/templates/common/.agents/skills/migration/references/selector-map.md +31 -0
- package/templates/common/.content/.gradial-dam/blobs/sha256/7f/bf/7fbfc7fe230ff12688c496f660119e41d9725c4504d82e310c15665c73c09292 +0 -0
- package/templates/common/.content/.gradial-dam/blobs/sha256/a0/f5/a0f506689de9b15a7055f70103a3f02cd3ba105771ae2de6754df2ede1210d3a +0 -0
- package/templates/common/.content/.gradial-dam/blobs/sha256/dd/b7/ddb7e9642be08317bcd4b2bd26b58ba8d0d34dd7239e03e684a4f3634fa3845b +0 -0
- package/templates/common/.content/.gradial-dam/blobs/sha256/fb/32/fb3260e8286ea8231ea1c77b9c0be92d6edd9026565731eb14cfdcfd6b1b75b3 +0 -0
- package/templates/common/.content/config/site.json +192 -0
- package/templates/common/.content/dam/folders/seed/ast_site_about_hero/asset.json +9 -0
- package/templates/common/.content/dam/folders/seed/ast_site_about_hero/editorial.json +14 -0
- package/templates/common/.content/dam/folders/seed/ast_site_about_hero/governance.json +23 -0
- package/templates/common/.content/dam/folders/seed/ast_site_about_hero/versions/v0001.json +37 -0
- package/templates/common/.content/dam/folders/seed/ast_site_home_hero/asset.json +9 -0
- package/templates/common/.content/dam/folders/seed/ast_site_home_hero/editorial.json +14 -0
- package/templates/common/.content/dam/folders/seed/ast_site_home_hero/governance.json +23 -0
- package/templates/common/.content/dam/folders/seed/ast_site_home_hero/versions/v0001.json +37 -0
- package/templates/common/.content/fragments/navbar/_index.json +25 -0
- package/templates/common/.content/fragments/site-footer/_index.json +62 -0
- package/templates/common/.content/pages/about/_index.json +51 -0
- package/templates/common/.content/pages/home/_index.json +112 -0
- package/templates/common/.content/schemas/site.schema.json +123 -0
- package/templates/common/README.md.tmpl +45 -0
- package/templates/common/contracts/components/button.contract.ts +19 -0
- package/templates/common/contracts/components/container.contract.ts +19 -0
- package/templates/common/contracts/components/featureCard.contract.ts +15 -0
- package/templates/common/contracts/components/featureGrid.contract.ts +16 -0
- package/templates/common/contracts/components/homeHero.contract.ts +36 -0
- package/templates/common/contracts/components/index.ts +21 -0
- package/templates/common/contracts/components/siteFooter.contract.ts +45 -0
- package/templates/common/contracts/components/siteNavigation.contract.ts +30 -0
- package/templates/common/contracts/components/statsBar.contract.ts +19 -0
- package/templates/common/design-system/styles.css +346 -0
- package/templates/common/design-system/tokens/colors.css +110 -0
- package/templates/common/design-system/tokens/components.css +86 -0
- package/templates/common/design-system/tokens/index.css +6 -0
- package/templates/common/design-system/tokens/radii.css +5 -0
- package/templates/common/design-system/tokens/shadows.css +9 -0
- package/templates/common/design-system/tokens/spacing.css +26 -0
- package/templates/common/design-system/tokens/typography.css +32 -0
- package/templates/common/eslint-rules/no-raw-tailwind-values.js +127 -0
- package/templates/common/public/assets/Dwire-model.png +0 -0
- package/templates/common/public/assets/bare-metal-logo.png +0 -0
- package/templates/common/public/assets/meridian-logo.svg +8 -0
- package/templates/common/public/assets/seattle.avif +0 -0
- package/templates/nextjs/template/.aci.yaml +26 -0
- package/templates/nextjs/template/.storybook/main.ts +13 -0
- package/templates/nextjs/template/.storybook/preview.ts +34 -0
- package/templates/nextjs/template/eslint.config.mjs +36 -0
- package/templates/nextjs/template/next-env.d.ts +6 -0
- package/templates/nextjs/template/next.config.ts +8 -0
- package/templates/nextjs/template/package.json.tmpl +46 -0
- package/templates/nextjs/template/postcss.config.mjs +7 -0
- package/templates/nextjs/template/src/app/[[...slug]]/page.tsx +8 -0
- package/templates/nextjs/template/src/app/gradial/assets/[releaseId]/[...path]/route.ts +31 -0
- package/templates/nextjs/template/src/app/layout.tsx +29 -0
- package/templates/nextjs/template/src/cms/contracts/components/button.contract.ts +19 -0
- package/templates/nextjs/template/src/cms/contracts/components/container.contract.ts +20 -0
- package/templates/nextjs/template/src/cms/contracts/components/homeHero.contract.ts +36 -0
- package/templates/nextjs/template/src/cms/contracts/components/index.ts +15 -0
- package/templates/nextjs/template/src/cms/contracts/components/siteFooter.contract.ts +45 -0
- package/templates/nextjs/template/src/cms/contracts/components/siteNavigation.contract.ts +30 -0
- package/templates/nextjs/template/src/cms/contracts/layouts/index.ts +16 -0
- package/templates/nextjs/template/src/cms/registry.ts +28 -0
- package/templates/nextjs/template/src/components/chrome/Footer.tsx +86 -0
- package/templates/nextjs/template/src/components/chrome/Header.tsx +74 -0
- package/templates/nextjs/template/src/components/elements/AtomicBlock.tsx +13 -0
- package/templates/nextjs/template/src/components/elements/Button.tsx +40 -0
- package/templates/nextjs/template/src/components/elements/ButtonBlock.tsx +18 -0
- package/templates/nextjs/template/src/components/elements/Container.tsx +14 -0
- package/templates/nextjs/template/src/components/elements/Icon.tsx +29 -0
- package/templates/nextjs/template/src/components/elements/Logo.tsx +38 -0
- package/templates/nextjs/template/src/components/sections/ContainerSection.tsx +28 -0
- package/templates/nextjs/template/src/components/sections/FeatureCard.tsx +25 -0
- package/templates/nextjs/template/src/components/sections/FeatureGrid.tsx +36 -0
- package/templates/nextjs/template/src/components/sections/HomeHero.tsx +62 -0
- package/templates/nextjs/template/src/components/sections/StatsBar.tsx +30 -0
- package/templates/nextjs/template/src/middleware.ts +4 -0
- package/templates/nextjs/template/src/stories/Button.stories.tsx +38 -0
- package/templates/nextjs/template/src/stories/Header.stories.tsx +30 -0
- package/templates/nextjs/template/src/stories/HomeHero.stories.tsx +33 -0
- package/templates/nextjs/template/tsconfig.json +23 -0
- package/templates/nextjs/template/vercel.json +6 -0
- package/dist/content/contract.d.ts +0 -83
- package/dist/content/contract.js +0 -104
- package/dist/content/tailwind-validator.d.ts +0 -6
- package/dist/content/tailwind-validator.js +0 -31
- package/dist/content/validation.d.ts +0 -108
- package/dist/content/validation.js +0 -183
- package/dist/react/GradialMedia.d.ts +0 -24
- package/dist/react/GradialMedia.js +0 -31
- package/dist/react/GradialPicture.d.ts +0 -14
- package/dist/react/GradialPicture.js +0 -30
- package/dist/react/GradialVideoPlayer.d.ts +0 -13
- package/dist/sveltekit/index.d.ts +0 -25
- package/dist/sveltekit/index.js +0 -66
- package/dist/sveltekit/preview.d.ts +0 -5
- package/dist/sveltekit/preview.js +0 -33
- package/src/types/component.ts +0 -49
- package/src/types/config.ts +0 -37
- package/src/types/data.ts +0 -47
- package/src/types/index.ts +0 -10
- package/src/types/layout.ts +0 -29
- package/src/types/media.ts +0 -125
- package/src/types/page.ts +0 -47
- package/src/types/render-mode.ts +0 -10
- package/src/types/renderer.ts +0 -83
- package/src/types/video.ts +0 -66
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { Container } from '@/components/elements/Container';
|
|
2
|
+
import { Logo } from '@/components/elements/Logo';
|
|
3
|
+
import type { FooterColumn, FooterGroup, FooterLink } from '@gradial/aci/content';
|
|
4
|
+
|
|
5
|
+
interface FooterProps {
|
|
6
|
+
brandHref: string;
|
|
7
|
+
brandLabel: string;
|
|
8
|
+
brandLogo?: { src: string; alt?: string } | null;
|
|
9
|
+
columns?: FooterColumn[];
|
|
10
|
+
social?: FooterLink[];
|
|
11
|
+
legalLinks?: FooterLink[];
|
|
12
|
+
copyright?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function columnGroups(column: FooterColumn): FooterGroup[] {
|
|
16
|
+
if (column.groups?.length) return column.groups;
|
|
17
|
+
return [{ id: column.id, title: column.title || '', links: column.links || [] }];
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function Footer({ brandHref, brandLabel, brandLogo = null, columns = [], social = [], legalLinks = [], copyright = '' }: FooterProps) {
|
|
21
|
+
return (
|
|
22
|
+
<footer className="bm-footer relative pt-[var(--spacing-footer-y)]" id="footer">
|
|
23
|
+
<div className="absolute inset-x-0 top-0 h-px bg-gradient-to-r from-transparent via-[var(--color-brand-500)] to-transparent opacity-50" />
|
|
24
|
+
<Container>
|
|
25
|
+
<div className="footer-main-grid grid grid-cols-1 gap-12 lg:grid-cols-2">
|
|
26
|
+
<div className="max-w-sm">
|
|
27
|
+
<a href={brandHref} className="no-underline">
|
|
28
|
+
<Logo label={brandLabel} src={brandLogo?.src || ''} alt={brandLogo?.alt || brandLabel} className="w-[var(--width-logo-footer-mobile)] md:w-[var(--width-logo-footer)]" />
|
|
29
|
+
</a>
|
|
30
|
+
</div>
|
|
31
|
+
<div className="grid grid-cols-2 gap-8 sm:grid-cols-3 lg:grid-cols-4">
|
|
32
|
+
{columns.map((column) => (
|
|
33
|
+
<div key={column.id}>
|
|
34
|
+
{columnGroups(column).map((group) => (
|
|
35
|
+
<section key={group.id} aria-label={group.title} className="mb-8">
|
|
36
|
+
<h2 className="mb-4 text-[length:var(--text-size-eyebrow)] font-[var(--font-weight-bold)] uppercase tracking-[var(--letter-spacing-label)] text-[var(--color-text-primary)]">
|
|
37
|
+
{group.title}
|
|
38
|
+
</h2>
|
|
39
|
+
<ul className="list-none space-y-3 pl-0">
|
|
40
|
+
{group.links.map((link) => (
|
|
41
|
+
<li key={link.id}>
|
|
42
|
+
<a href={link.href} className="text-[length:var(--text-size-body-small)] text-[var(--color-text-secondary)] no-underline transition-colors hover:text-[var(--color-brand-400)]">
|
|
43
|
+
{link.label}
|
|
44
|
+
</a>
|
|
45
|
+
</li>
|
|
46
|
+
))}
|
|
47
|
+
</ul>
|
|
48
|
+
</section>
|
|
49
|
+
))}
|
|
50
|
+
</div>
|
|
51
|
+
))}
|
|
52
|
+
<section aria-label="Social" className="mb-8">
|
|
53
|
+
<h2 className="mb-4 text-[length:var(--text-size-eyebrow)] font-[var(--font-weight-bold)] uppercase tracking-[var(--letter-spacing-label)] text-[var(--color-text-primary)]">
|
|
54
|
+
Social
|
|
55
|
+
</h2>
|
|
56
|
+
<ul className="list-none space-y-3 pl-0">
|
|
57
|
+
{social.map((item) => (
|
|
58
|
+
<li key={item.id}>
|
|
59
|
+
<a href={item.href} className="flex items-center gap-2 text-[length:var(--text-size-body-small)] text-[var(--color-text-secondary)] no-underline transition-colors hover:text-[var(--color-brand-400)]">
|
|
60
|
+
<span className="flex h-8 w-8 items-center justify-center rounded-button border border-[var(--color-border-default)] text-[length:var(--text-size-caption)] transition-colors hover:border-[var(--color-brand-500)] hover:text-[var(--color-brand-400)]">
|
|
61
|
+
{item.icon || item.label.slice(0, 1)}
|
|
62
|
+
</span>
|
|
63
|
+
{item.label}
|
|
64
|
+
</a>
|
|
65
|
+
</li>
|
|
66
|
+
))}
|
|
67
|
+
</ul>
|
|
68
|
+
</section>
|
|
69
|
+
</div>
|
|
70
|
+
</div>
|
|
71
|
+
<div className="mt-12 flex flex-col gap-4 border-t border-[var(--color-border-default)] pt-8 sm:flex-row sm:items-center sm:justify-between">
|
|
72
|
+
<ul className="list-none flex flex-wrap gap-4 pl-0">
|
|
73
|
+
{legalLinks.map((link) => (
|
|
74
|
+
<li key={link.id}>
|
|
75
|
+
<a href={link.href} className="text-[length:var(--text-size-caption)] text-[var(--color-text-tertiary)] no-underline transition-colors hover:text-[var(--color-brand-400)]">
|
|
76
|
+
{link.label}
|
|
77
|
+
</a>
|
|
78
|
+
</li>
|
|
79
|
+
))}
|
|
80
|
+
</ul>
|
|
81
|
+
<p className="text-[length:var(--text-size-caption)] text-[var(--color-text-tertiary)]">{copyright}</p>
|
|
82
|
+
</div>
|
|
83
|
+
</Container>
|
|
84
|
+
</footer>
|
|
85
|
+
);
|
|
86
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { Button } from '@/components/elements/Button';
|
|
2
|
+
import { Container } from '@/components/elements/Container';
|
|
3
|
+
import { Icon } from '@/components/elements/Icon';
|
|
4
|
+
import { Logo } from '@/components/elements/Logo';
|
|
5
|
+
import type { NavItem, UtilityItem } from '@gradial/aci/content';
|
|
6
|
+
|
|
7
|
+
interface HeaderProps {
|
|
8
|
+
brandHref: string;
|
|
9
|
+
brandLabel: string;
|
|
10
|
+
brandLogo?: { src: string; alt?: string } | null;
|
|
11
|
+
navigation?: NavItem[];
|
|
12
|
+
utilities?: UtilityItem[];
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function Header({ brandHref, brandLabel, brandLogo = null, navigation = [], utilities = [] }: HeaderProps) {
|
|
16
|
+
return (
|
|
17
|
+
<header className="bm-header fixed top-0 left-0 right-0 z-[var(--z-header)] border-b border-[var(--header-border)]">
|
|
18
|
+
<a href="#content-main" className="sr-only focus:not-sr-only focus:absolute focus:top-4 focus:left-4 focus:z-[var(--z-overlay)] focus:rounded-button focus:bg-[var(--color-space-900)] focus:px-4 focus:py-2 focus:text-[var(--color-white)]">
|
|
19
|
+
Skip to main content
|
|
20
|
+
</a>
|
|
21
|
+
<Container className="flex h-[var(--height-header)] items-center justify-between gap-6">
|
|
22
|
+
<a href={brandHref} className="flex items-center gap-2 no-underline">
|
|
23
|
+
<Logo label={brandLabel} src={brandLogo?.src || ''} alt={brandLogo?.alt || brandLabel} />
|
|
24
|
+
</a>
|
|
25
|
+
<nav className="hidden md:flex items-center gap-8" aria-label="Primary">
|
|
26
|
+
{navigation.map((item) => (
|
|
27
|
+
<a
|
|
28
|
+
key={item.id}
|
|
29
|
+
href={item.href}
|
|
30
|
+
className="text-[length:var(--text-size-body-small)] font-[var(--font-weight-medium)] text-[var(--header-muted)] no-underline transition-colors hover:text-[var(--header-link-hover)]"
|
|
31
|
+
>
|
|
32
|
+
{item.label}
|
|
33
|
+
</a>
|
|
34
|
+
))}
|
|
35
|
+
</nav>
|
|
36
|
+
<div className="hidden md:flex items-center gap-3">
|
|
37
|
+
{utilities.map((item) => (
|
|
38
|
+
<Button key={item.id} href={item.href} variant={item.variant || 'outline'}>{item.label}</Button>
|
|
39
|
+
))}
|
|
40
|
+
</div>
|
|
41
|
+
<div className="md:hidden">
|
|
42
|
+
<input id="site-mobile-nav" className="peer sr-only" type="checkbox" />
|
|
43
|
+
<label htmlFor="site-mobile-nav" className="cursor-pointer text-[var(--header-icon)]" aria-label="Open main navigation">
|
|
44
|
+
<Icon name="menu" />
|
|
45
|
+
</label>
|
|
46
|
+
<div className="bm-mobile-nav peer-checked:flex fixed inset-0 z-[var(--z-overlay)] hidden flex-col">
|
|
47
|
+
<Container className="flex items-center justify-between py-4">
|
|
48
|
+
<a href={brandHref} className="no-underline">
|
|
49
|
+
<Logo label={brandLabel} src={brandLogo?.src || ''} alt={brandLogo?.alt || brandLabel} />
|
|
50
|
+
</a>
|
|
51
|
+
<label htmlFor="site-mobile-nav" className="cursor-pointer text-[var(--mobile-nav-muted)]" aria-label="Close main navigation">
|
|
52
|
+
<Icon name="close" />
|
|
53
|
+
</label>
|
|
54
|
+
</Container>
|
|
55
|
+
<nav className="flex flex-col gap-4 px-[var(--spacing-container)] py-6" aria-label="Mobile main navigation">
|
|
56
|
+
{navigation.map((item) => (
|
|
57
|
+
<a key={item.id} href={item.href} className="text-[length:var(--mobile-nav-link-size)] leading-[var(--mobile-nav-link-line-height)] font-[var(--font-weight-medium)] text-[var(--mobile-nav-text)] no-underline">
|
|
58
|
+
{item.label}
|
|
59
|
+
</a>
|
|
60
|
+
))}
|
|
61
|
+
</nav>
|
|
62
|
+
{utilities.length ? (
|
|
63
|
+
<div className="flex flex-col gap-3 px-[var(--spacing-container)] py-4">
|
|
64
|
+
{utilities.map((item) => (
|
|
65
|
+
<Button key={item.id} href={item.href} variant={item.variant || 'outline'}>{item.label}</Button>
|
|
66
|
+
))}
|
|
67
|
+
</div>
|
|
68
|
+
) : null}
|
|
69
|
+
</div>
|
|
70
|
+
</div>
|
|
71
|
+
</Container>
|
|
72
|
+
</header>
|
|
73
|
+
);
|
|
74
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { KernelAtomicBlock } from '@gradial/aci/content';
|
|
2
|
+
import { ButtonBlock } from './ButtonBlock';
|
|
3
|
+
|
|
4
|
+
interface AtomicBlockProps {
|
|
5
|
+
block: KernelAtomicBlock;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function AtomicBlock({ block }: AtomicBlockProps) {
|
|
9
|
+
if (block.component === 'button') {
|
|
10
|
+
return <ButtonBlock {...block.props as Parameters<typeof ButtonBlock>[0]} />;
|
|
11
|
+
}
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
import type { ButtonVariant } from '@gradial/aci/content';
|
|
3
|
+
|
|
4
|
+
type ButtonSize = 'default' | 'lg';
|
|
5
|
+
|
|
6
|
+
interface ButtonProps {
|
|
7
|
+
href: string;
|
|
8
|
+
variant?: ButtonVariant;
|
|
9
|
+
size?: ButtonSize;
|
|
10
|
+
className?: string;
|
|
11
|
+
target?: string;
|
|
12
|
+
rel?: string;
|
|
13
|
+
children: ReactNode;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const variantClasses: Record<ButtonVariant, string> = {
|
|
17
|
+
primary: 'bg-[var(--button-primary-bg)] text-[var(--button-primary-text)] border-[var(--button-primary-border)] hover:bg-[var(--button-primary-hover-bg)] hover:border-[var(--button-primary-hover-border)] shadow-[var(--shadow-glow)] hover:shadow-[var(--shadow-glow-lg)]',
|
|
18
|
+
secondary: 'bg-[var(--button-secondary-bg)] text-[var(--button-secondary-text)] border-[var(--button-secondary-border)] hover:bg-[var(--button-secondary-hover-bg)] hover:border-[var(--button-secondary-hover-border)]',
|
|
19
|
+
outline: 'bg-[var(--button-outline-bg)] text-[var(--button-outline-text)] border-[var(--button-outline-border)] hover:bg-[var(--button-outline-hover-bg)] hover:border-[var(--button-outline-hover-border)]',
|
|
20
|
+
tertiary: 'bg-[var(--button-tertiary-bg)] text-[var(--button-tertiary-text)] border-[var(--button-tertiary-border)] hover:bg-[var(--button-tertiary-hover-bg)] hover:text-[var(--button-tertiary-hover-text)]',
|
|
21
|
+
ghost: 'bg-[var(--button-ghost-bg)] text-[var(--button-ghost-text)] border-[var(--button-ghost-border)] hover:bg-[var(--button-ghost-hover-bg)] hover:text-[var(--button-ghost-hover-text)]',
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const sizeClasses: Record<ButtonSize, string> = {
|
|
25
|
+
default: 'px-[var(--spacing-button-x)] py-[var(--spacing-button-y)] min-h-[var(--height-button-min)] text-[length:var(--text-button)]',
|
|
26
|
+
lg: 'px-7 py-3.5 min-h-[3.5rem] text-[length:var(--text-size-body-large)]',
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export function Button({ href, variant = 'primary', size = 'default', className = '', target, rel, children }: ButtonProps) {
|
|
30
|
+
return (
|
|
31
|
+
<a
|
|
32
|
+
className={`inline-flex items-center justify-center gap-2 rounded-button leading-button font-[var(--font-weight-semibold)] no-underline transition-all duration-[var(--transition-duration-fast)] ${variantClasses[variant]} ${sizeClasses[size]} ${className}`}
|
|
33
|
+
href={href}
|
|
34
|
+
target={target}
|
|
35
|
+
rel={rel}
|
|
36
|
+
>
|
|
37
|
+
{children}
|
|
38
|
+
</a>
|
|
39
|
+
);
|
|
40
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Button } from '@/components/elements/Button';
|
|
2
|
+
import { Icon } from '@/components/elements/Icon';
|
|
3
|
+
import type { ContentProps } from '@/cms/contracts/components/button.contract';
|
|
4
|
+
|
|
5
|
+
export type ButtonBlockProps = ContentProps;
|
|
6
|
+
|
|
7
|
+
export function ButtonBlock({ label = '', href = '#', variant = 'primary', newWindow = false, icon = '', iconPosition = 'before', className = '' }: ButtonBlockProps) {
|
|
8
|
+
const target = newWindow ? '_blank' : undefined;
|
|
9
|
+
const rel = newWindow ? 'noreferrer' : undefined;
|
|
10
|
+
|
|
11
|
+
return (
|
|
12
|
+
<Button href={href} variant={variant} target={target} rel={rel} className={className}>
|
|
13
|
+
{icon && iconPosition === 'before' ? <Icon name={icon} /> : null}
|
|
14
|
+
<span>{label}</span>
|
|
15
|
+
{icon && iconPosition === 'after' ? <Icon name={icon} /> : null}
|
|
16
|
+
</Button>
|
|
17
|
+
);
|
|
18
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
|
|
3
|
+
interface ContainerProps {
|
|
4
|
+
className?: string;
|
|
5
|
+
children: ReactNode;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function Container({ className = '', children }: ContainerProps) {
|
|
9
|
+
return (
|
|
10
|
+
<div className={`mx-auto max-w-[var(--max-width-container)] px-[var(--spacing-container)] ${className}`}>
|
|
11
|
+
{children}
|
|
12
|
+
</div>
|
|
13
|
+
);
|
|
14
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
interface IconProps {
|
|
2
|
+
name?: string;
|
|
3
|
+
className?: string;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
const paths: Record<string, string> = {
|
|
7
|
+
arrow_forward: 'M647-440H160v-80h487L423-744l57-56 320 320-320 320-57-56 224-224Z',
|
|
8
|
+
close: 'm256-200-56-56 224-224-224-224 56-56 224 224 224-224 56 56-224 224 224 224-56 56-224-224-224 224Z',
|
|
9
|
+
menu: 'M120-240v-80h720v80H120Zm0-200v-80h720v80H120Zm0-200v-80h720v80H120Z',
|
|
10
|
+
globe: 'M480-80q-82.5 0-155.5-31.5t-127-86Q143-252 111.5-325T80-480q0-83 31.5-156t86-127q54.5-54.5 127-86T480-880q83 0 156 31.5t127 86q54.5 54.5 86 127T880-480q0 82.5-31.5 155.5t-86 127q-54.5 54.5-127 86T480-80Zm0-80q139-36 209.5-131.5T760-480H520v-280q-64 8-117 38.5T320-640h-40q-54 77-87 153.5T160-480q0 131.5 69.5 227T480-160Zm0 80q-83 0-156-31.5t-127-86q-54.5-54.5-86-127T80-480q0-83 31.5-156t86-127q54.5-54.5 127-86T480-880q83 0 156 31.5t127 86q54.5 54.5 86 127T880-480q0 83-31.5 156t-86 127q-54.5 54.5-127 86T480-80Z',
|
|
11
|
+
speed: 'M400-120 320-760l400 360H440l80 440-120-160Z',
|
|
12
|
+
setup: 'm480-240 160-96-40-200 160-120-200-80-80-200-80 200-200 80 160 120-40 200 160 96Z',
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export function Icon({ name = '', className = '' }: IconProps) {
|
|
16
|
+
const path = paths[name] || paths.arrow_forward;
|
|
17
|
+
|
|
18
|
+
return (
|
|
19
|
+
<svg
|
|
20
|
+
className={`inline-block h-6 w-6 text-[color:var(--color-icon-default)] ${className}`}
|
|
21
|
+
viewBox="0 -960 960 960"
|
|
22
|
+
aria-hidden="true"
|
|
23
|
+
focusable="false"
|
|
24
|
+
fill="currentColor"
|
|
25
|
+
>
|
|
26
|
+
<path d={path} />
|
|
27
|
+
</svg>
|
|
28
|
+
);
|
|
29
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import Image from 'next/image';
|
|
2
|
+
|
|
3
|
+
interface LogoProps {
|
|
4
|
+
label: string;
|
|
5
|
+
src?: string;
|
|
6
|
+
alt?: string;
|
|
7
|
+
className?: string;
|
|
8
|
+
width?: number;
|
|
9
|
+
height?: number;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function Logo({
|
|
13
|
+
label,
|
|
14
|
+
src = '',
|
|
15
|
+
alt,
|
|
16
|
+
className = '',
|
|
17
|
+
width = 200,
|
|
18
|
+
height = 40,
|
|
19
|
+
}: LogoProps) {
|
|
20
|
+
if (!src) {
|
|
21
|
+
return (
|
|
22
|
+
<span className={`font-[var(--font-weight-bold)] text-[length:var(--text-size-brand)] [font-family:var(--font-family-heading)] ${className}`}>
|
|
23
|
+
{label}
|
|
24
|
+
</span>
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<Image
|
|
30
|
+
className={`h-auto w-[var(--width-logo)] ${className}`}
|
|
31
|
+
src={src}
|
|
32
|
+
alt={alt || label}
|
|
33
|
+
width={width}
|
|
34
|
+
height={height}
|
|
35
|
+
priority
|
|
36
|
+
/>
|
|
37
|
+
);
|
|
38
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Container } from '@/components/elements/Container';
|
|
2
|
+
import { type ContentProps } from '@/cms/contracts/components/container.contract';
|
|
3
|
+
import type { RenderChildren } from '@gradial/aci/react';
|
|
4
|
+
|
|
5
|
+
export async function ContainerSection({
|
|
6
|
+
theme = 'auto',
|
|
7
|
+
className = '',
|
|
8
|
+
content,
|
|
9
|
+
renderChildren,
|
|
10
|
+
}: ContentProps & { renderChildren: RenderChildren<NonNullable<ContentProps['content']>> }) {
|
|
11
|
+
const buttonCount = content?.filter((b) => b.component === 'button').length ?? 0;
|
|
12
|
+
const layout = buttonCount === 1
|
|
13
|
+
? 'flex-col items-center'
|
|
14
|
+
: 'flex-col items-center sm:flex-row sm:justify-center sm:gap-4';
|
|
15
|
+
|
|
16
|
+
return (
|
|
17
|
+
<section className={`theme-${theme} relative overflow-hidden py-[var(--spacing-section-y)] ${className}`}>
|
|
18
|
+
{theme === 'dark' ? (
|
|
19
|
+
<div className="pointer-events-none absolute inset-0 bg-[image:var(--gradient-hero-base)] opacity-30" />
|
|
20
|
+
) : null}
|
|
21
|
+
<Container className="relative">
|
|
22
|
+
<div className={`flex ${layout} gap-6 text-center`}>
|
|
23
|
+
{content ? await renderChildren(content) : null}
|
|
24
|
+
</div>
|
|
25
|
+
</Container>
|
|
26
|
+
</section>
|
|
27
|
+
);
|
|
28
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Icon } from '@/components/elements/Icon';
|
|
2
|
+
import type { ContentProps } from '@/cms/contracts/components/featureCard.contract';
|
|
3
|
+
|
|
4
|
+
export type FeatureCardProps = ContentProps;
|
|
5
|
+
|
|
6
|
+
export function FeatureCard({ icon = '', title = '', description = '' }: FeatureCardProps) {
|
|
7
|
+
return (
|
|
8
|
+
<div className="group relative overflow-hidden rounded-card border border-[var(--color-border-default)] bg-[var(--color-white)] p-[var(--spacing-card)] shadow-[var(--shadow-card-default)] transition-all duration-[var(--transition-duration-fast)] hover:-translate-y-1 hover:border-[var(--color-border-strong)] hover:shadow-[var(--shadow-elevated)]">
|
|
9
|
+
<div className="absolute inset-x-0 top-0 h-px bg-gradient-to-r from-transparent via-[var(--color-brand-500)] to-transparent opacity-0 transition-opacity duration-[var(--transition-duration-fast)] group-hover:opacity-100" />
|
|
10
|
+
{icon ? (
|
|
11
|
+
<div className="mb-6 flex h-12 w-12 items-center justify-center rounded-button border border-[var(--color-border-default)] bg-[var(--color-surface-100)] text-[var(--color-brand-600)] shadow-[var(--shadow-glow-sm)] transition-colors group-hover:border-[var(--color-brand-500)] group-hover:text-[var(--color-brand-500)]">
|
|
12
|
+
<Icon name={icon} className="h-6 w-6" />
|
|
13
|
+
</div>
|
|
14
|
+
) : null}
|
|
15
|
+
<h3 className="text-[length:var(--text-size-h3)] font-[var(--font-weight-semibold)] text-[var(--color-text-primary)]">
|
|
16
|
+
{title}
|
|
17
|
+
</h3>
|
|
18
|
+
{description ? (
|
|
19
|
+
<p className="mt-3 text-[length:var(--text-size-body)] leading-relaxed text-[var(--color-text-secondary)]">
|
|
20
|
+
{description}
|
|
21
|
+
</p>
|
|
22
|
+
) : null}
|
|
23
|
+
</div>
|
|
24
|
+
);
|
|
25
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Container } from '@/components/elements/Container';
|
|
2
|
+
import { type ContentProps } from '@/cms/contracts/components/featureGrid.contract';
|
|
3
|
+
import type { RenderChildren } from '@gradial/aci/react';
|
|
4
|
+
|
|
5
|
+
export async function FeatureGrid({
|
|
6
|
+
title = '',
|
|
7
|
+
subtitle = '',
|
|
8
|
+
features,
|
|
9
|
+
renderChildren,
|
|
10
|
+
}: ContentProps & { renderChildren: RenderChildren<NonNullable<ContentProps['features']>> }) {
|
|
11
|
+
const count = features?.length ?? 0;
|
|
12
|
+
const cols = count <= 2 ? 'sm:grid-cols-2' : 'sm:grid-cols-2 lg:grid-cols-3';
|
|
13
|
+
|
|
14
|
+
return (
|
|
15
|
+
<section className="theme-light relative overflow-hidden py-[var(--spacing-section-y)] bg-[var(--color-white)]">
|
|
16
|
+
<div className="pointer-events-none absolute inset-0 bg-[image:var(--gradient-hero-base)] opacity-50" />
|
|
17
|
+
<Container className="relative">
|
|
18
|
+
<div className="mb-14 max-w-[var(--max-width-container-narrow)]">
|
|
19
|
+
{title ? (
|
|
20
|
+
<h2 className="text-[length:var(--text-size-h2-mobile)] font-[var(--font-weight-bold)] leading-tight text-[var(--color-text-primary)] md:text-[length:var(--text-size-h2)]">
|
|
21
|
+
{title}
|
|
22
|
+
</h2>
|
|
23
|
+
) : null}
|
|
24
|
+
{subtitle ? (
|
|
25
|
+
<p className="mt-4 text-[length:var(--text-size-body-large)] leading-relaxed text-[var(--color-text-secondary)]">
|
|
26
|
+
{subtitle}
|
|
27
|
+
</p>
|
|
28
|
+
) : null}
|
|
29
|
+
</div>
|
|
30
|
+
<div className={`grid gap-6 ${cols}`}>
|
|
31
|
+
{features ? await renderChildren(features) : null}
|
|
32
|
+
</div>
|
|
33
|
+
</Container>
|
|
34
|
+
</section>
|
|
35
|
+
);
|
|
36
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { Button } from '@/components/elements/Button';
|
|
2
|
+
import { Container } from '@/components/elements/Container';
|
|
3
|
+
import { primaryImageSource } from '@gradial/aci';
|
|
4
|
+
import type { ContentProps } from '@/cms/contracts/components/homeHero.contract';
|
|
5
|
+
|
|
6
|
+
type HeroImage = NonNullable<ContentProps['image']>;
|
|
7
|
+
|
|
8
|
+
export type HomeHeroProps = Omit<ContentProps, 'image'> & { image?: HeroImage };
|
|
9
|
+
|
|
10
|
+
export function HomeHero({ eyebrow = '', headline = '', description = '', ctaLabel = '', ctaHref = '#', image }: HomeHeroProps) {
|
|
11
|
+
const imageSrc = heroImageSrc(image);
|
|
12
|
+
const imageAlt = image?.alt || headline;
|
|
13
|
+
|
|
14
|
+
return (
|
|
15
|
+
<section
|
|
16
|
+
className="theme-light relative flex min-h-[var(--height-hero-min)] items-center bg-cover bg-center"
|
|
17
|
+
aria-label={imageAlt}
|
|
18
|
+
style={{
|
|
19
|
+
backgroundImage: imageSrc
|
|
20
|
+
? `url("${imageSrc}")`
|
|
21
|
+
: undefined,
|
|
22
|
+
}}
|
|
23
|
+
>
|
|
24
|
+
{/* Soft ambient glow */}
|
|
25
|
+
<div className="pointer-events-none absolute inset-0 bg-[image:var(--gradient-hero-base)]" />
|
|
26
|
+
{/* Warm radial accent */}
|
|
27
|
+
<div className="pointer-events-none absolute inset-0 bg-[image:var(--gradient-hero-radial)]" />
|
|
28
|
+
{/* Light overlay to keep image airy but legible */}
|
|
29
|
+
<div className="pointer-events-none absolute inset-0 bg-[image:var(--gradient-hero-overlay)]" />
|
|
30
|
+
{/* Bottom fade for extra contrast on text */}
|
|
31
|
+
<div className="pointer-events-none absolute inset-0 bg-gradient-to-t from-[var(--color-white)] via-transparent to-transparent" />
|
|
32
|
+
|
|
33
|
+
<Container className="relative z-10 py-[var(--spacing-hero-py)] pt-[var(--height-header)]">
|
|
34
|
+
<div className="max-w-[var(--width-hero-content)]">
|
|
35
|
+
{eyebrow ? (
|
|
36
|
+
<p className="mb-5 text-[length:var(--text-size-eyebrow)] font-[var(--font-weight-bold)] uppercase tracking-[var(--letter-spacing-label)] text-[var(--color-accent-warm)]">
|
|
37
|
+
{eyebrow}
|
|
38
|
+
</p>
|
|
39
|
+
) : null}
|
|
40
|
+
<h1 className="text-[length:var(--text-size-h1-mobile)] font-[var(--font-weight-bold)] leading-tight text-[var(--color-text-primary)] md:text-[length:var(--text-size-h1)]">
|
|
41
|
+
{headline}
|
|
42
|
+
</h1>
|
|
43
|
+
{description ? (
|
|
44
|
+
<p className="mt-6 max-w-2xl text-[length:var(--text-size-body-large)] leading-relaxed text-[var(--color-text-secondary)]">
|
|
45
|
+
{description}
|
|
46
|
+
</p>
|
|
47
|
+
) : null}
|
|
48
|
+
{ctaLabel ? (
|
|
49
|
+
<div className="mt-10">
|
|
50
|
+
<Button href={ctaHref} variant="primary" size="lg">{ctaLabel}</Button>
|
|
51
|
+
</div>
|
|
52
|
+
) : null}
|
|
53
|
+
</div>
|
|
54
|
+
</Container>
|
|
55
|
+
</section>
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function heroImageSrc(image?: HeroImage) {
|
|
60
|
+
if (!image || image.sources.length === 0) return '';
|
|
61
|
+
return primaryImageSource(image).src;
|
|
62
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Container } from '@/components/elements/Container';
|
|
2
|
+
import type { ContentProps } from '@/cms/contracts/components/statsBar.contract';
|
|
3
|
+
|
|
4
|
+
export type StatsBarProps = ContentProps;
|
|
5
|
+
|
|
6
|
+
export function StatsBar({ stats = [] }: StatsBarProps) {
|
|
7
|
+
return (
|
|
8
|
+
<section className="theme-light relative border-y border-[var(--color-border-default)] bg-[var(--color-surface-100)] py-[var(--spacing-section-y)]">
|
|
9
|
+
<div className="pointer-events-none absolute inset-0 bg-[image:var(--gradient-hero-base)] opacity-40" />
|
|
10
|
+
<Container className="relative">
|
|
11
|
+
<div className="grid gap-10 sm:grid-cols-2 lg:grid-cols-4">
|
|
12
|
+
{stats.map((stat, index) => (
|
|
13
|
+
<div key={`${stat.label}-${index}`} className="relative text-center">
|
|
14
|
+
<p className="text-[length:var(--text-size-h2-mobile)] font-[var(--font-weight-bold)] leading-tight text-[var(--color-text-primary)] md:text-[length:var(--text-size-h2)]">
|
|
15
|
+
{stat.value}
|
|
16
|
+
{stat.suffix ? <span className="text-[var(--color-accent-warm)]">{stat.suffix}</span> : null}
|
|
17
|
+
</p>
|
|
18
|
+
<p className="mt-3 text-[length:var(--text-size-body-small)] font-[var(--font-weight-medium)] uppercase tracking-[var(--letter-spacing-label)] text-[var(--color-text-secondary)]">
|
|
19
|
+
{stat.label}
|
|
20
|
+
</p>
|
|
21
|
+
{index < stats.length - 1 ? (
|
|
22
|
+
<div className="hidden lg:block absolute right-0 top-1/2 h-16 w-px -translate-y-1/2 bg-[var(--color-border-default)]" />
|
|
23
|
+
) : null}
|
|
24
|
+
</div>
|
|
25
|
+
))}
|
|
26
|
+
</div>
|
|
27
|
+
</Container>
|
|
28
|
+
</section>
|
|
29
|
+
);
|
|
30
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/nextjs';
|
|
2
|
+
import { Button } from '@/components/elements/Button';
|
|
3
|
+
|
|
4
|
+
const meta: Meta<typeof Button> = {
|
|
5
|
+
title: 'Elements/Button',
|
|
6
|
+
component: Button,
|
|
7
|
+
parameters: {
|
|
8
|
+
layout: 'centered',
|
|
9
|
+
},
|
|
10
|
+
tags: ['autodocs'],
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export default meta;
|
|
14
|
+
type Story = StoryObj<typeof Button>;
|
|
15
|
+
|
|
16
|
+
export const Primary: Story = {
|
|
17
|
+
args: {
|
|
18
|
+
href: '#',
|
|
19
|
+
variant: 'primary',
|
|
20
|
+
children: 'Get started',
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export const Secondary: Story = {
|
|
25
|
+
args: {
|
|
26
|
+
href: '#',
|
|
27
|
+
variant: 'secondary',
|
|
28
|
+
children: 'Learn more',
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export const Outline: Story = {
|
|
33
|
+
args: {
|
|
34
|
+
href: '#',
|
|
35
|
+
variant: 'outline',
|
|
36
|
+
children: 'Book a demo',
|
|
37
|
+
},
|
|
38
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/nextjs';
|
|
2
|
+
import { Header } from '@/components/chrome/Header';
|
|
3
|
+
|
|
4
|
+
const meta: Meta<typeof Header> = {
|
|
5
|
+
title: 'Chrome/Header',
|
|
6
|
+
component: Header,
|
|
7
|
+
parameters: {
|
|
8
|
+
layout: 'fullscreen',
|
|
9
|
+
},
|
|
10
|
+
tags: ['autodocs'],
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export default meta;
|
|
14
|
+
type Story = StoryObj<typeof Header>;
|
|
15
|
+
|
|
16
|
+
export const Default: Story = {
|
|
17
|
+
args: {
|
|
18
|
+
brandHref: '/',
|
|
19
|
+
brandLabel: 'My Site',
|
|
20
|
+
navigation: [
|
|
21
|
+
{ id: 'home', label: 'Home', href: '/' },
|
|
22
|
+
{ id: 'about', label: 'About', href: '/about' },
|
|
23
|
+
{ id: 'docs', label: 'Docs', href: '/#docs' },
|
|
24
|
+
],
|
|
25
|
+
utilities: [
|
|
26
|
+
{ id: 'login', label: 'Login', href: '/#login', variant: 'secondary' },
|
|
27
|
+
{ id: 'demo', label: 'Book a demo', href: '/#demo', variant: 'outline' },
|
|
28
|
+
],
|
|
29
|
+
},
|
|
30
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/nextjs';
|
|
2
|
+
import { HomeHero } from '@/components/sections/HomeHero';
|
|
3
|
+
|
|
4
|
+
const meta: Meta<typeof HomeHero> = {
|
|
5
|
+
title: 'Sections/HomeHero',
|
|
6
|
+
component: HomeHero,
|
|
7
|
+
parameters: {
|
|
8
|
+
layout: 'fullscreen',
|
|
9
|
+
},
|
|
10
|
+
tags: ['autodocs'],
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export default meta;
|
|
14
|
+
type Story = StoryObj<typeof HomeHero>;
|
|
15
|
+
|
|
16
|
+
export const Default: Story = {
|
|
17
|
+
args: {
|
|
18
|
+
eyebrow: 'Welcome',
|
|
19
|
+
headline: 'Ship content updates across every framework.',
|
|
20
|
+
description: 'One shared compiler-shaped page now drives every local framework preview.',
|
|
21
|
+
ctaLabel: 'Get started',
|
|
22
|
+
ctaHref: '#get-started',
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export const NoEyebrow: Story = {
|
|
27
|
+
args: {
|
|
28
|
+
headline: 'Build something great',
|
|
29
|
+
description: 'A simple hero without an eyebrow.',
|
|
30
|
+
ctaLabel: 'Learn more',
|
|
31
|
+
ctaHref: '#',
|
|
32
|
+
},
|
|
33
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"lib": ["dom", "dom.iterable", "es2022"],
|
|
5
|
+
"allowJs": false,
|
|
6
|
+
"skipLibCheck": true,
|
|
7
|
+
"strict": true,
|
|
8
|
+
"noEmit": true,
|
|
9
|
+
"esModuleInterop": true,
|
|
10
|
+
"module": "esnext",
|
|
11
|
+
"moduleResolution": "bundler",
|
|
12
|
+
"resolveJsonModule": true,
|
|
13
|
+
"isolatedModules": true,
|
|
14
|
+
"jsx": "preserve",
|
|
15
|
+
"incremental": true,
|
|
16
|
+
"plugins": [{ "name": "next" }],
|
|
17
|
+
"paths": {
|
|
18
|
+
"@/*": ["./src/*"]
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
|
22
|
+
"exclude": ["node_modules"]
|
|
23
|
+
}
|