@gradial/aci 0.1.53-rc.2 → 0.1.53-rc.4
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 +77 -0
- package/README.md +26 -4
- package/dist/astro/index.js +2 -5
- package/dist/cli/emit-error-pages.d.mts +2 -1
- package/dist/cli/emit-error-pages.mjs +13 -270
- package/dist/content/component-block.d.ts +17 -0
- package/dist/content/component-block.js +40 -0
- package/dist/content/resolve-slots.js +2 -4
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -0
- package/dist/internal/aci-root.d.ts +1 -0
- package/dist/internal/aci-root.js +49 -0
- package/dist/next/config.js +6 -13
- package/dist/next/error-seat-payload.d.ts +1 -1
- package/dist/next/error-seat-payload.js +1 -2
- package/dist/next/error-seat.js +3 -4
- package/dist/next/render.js +2 -8
- package/dist/platform-tests/adapters/astro.d.ts +2 -0
- package/dist/platform-tests/adapters/astro.js +101 -0
- package/dist/platform-tests/adapters/next.d.ts +9 -0
- package/dist/platform-tests/adapters/next.js +112 -0
- package/dist/platform-tests/adapters/site.d.ts +29 -0
- package/dist/platform-tests/adapters/site.js +92 -0
- package/dist/platform-tests/adapters/types.d.ts +27 -0
- package/dist/platform-tests/adapters/types.js +1 -0
- package/dist/platform-tests/index.d.ts +47 -0
- package/dist/platform-tests/index.js +50 -0
- package/dist/platform-tests/sample-props.d.ts +2 -0
- package/dist/platform-tests/sample-props.js +82 -0
- package/dist/platform-tests/site-check.d.ts +51 -0
- package/dist/platform-tests/site-check.js +193 -0
- package/dist/registry.d.ts +1 -1
- package/dist/testing/conformance/index.d.ts +2 -1
- package/dist/testing/conformance/index.js +2 -1
- package/dist/testing/conformance/next-stubs.d.ts +13 -4
- package/dist/testing/conformance/next-stubs.js +76 -4
- package/dist/testing/conformance/preset.js +1 -2
- package/dist/testing/conformance/root-identity.d.ts +0 -29
- package/dist/testing/conformance/root-identity.js +36 -344
- package/dist/testing/conformance/types.d.ts +1 -1
- package/dist/types/component.d.ts +11 -3
- package/dist/types/component.js +10 -1
- package/package.json +9 -8
- package/src/cli/emit-error-pages.mjs +13 -273
- package/src/types/component.test.ts +17 -0
- package/src/types/component.ts +18 -3
- package/templates/astro/starter-content/src/components/chrome/Footer.astro +3 -4
- package/templates/astro/starter-content/src/components/chrome/Header.astro +3 -4
- package/templates/astro/starter-content/src/components/elements/Button.astro +4 -4
- package/templates/astro/starter-content/src/components/elements/ButtonBlock.astro +3 -4
- package/templates/astro/starter-content/src/components/sections/ContainerSection.astro +4 -4
- package/templates/astro/starter-content/src/components/sections/FeatureCard.astro +4 -4
- package/templates/astro/starter-content/src/components/sections/FeatureGrid.astro +4 -4
- package/templates/astro/starter-content/src/components/sections/HomeHero.astro +3 -4
- package/templates/astro/starter-content/src/components/sections/PersonalizationChoice.astro +4 -4
- package/templates/astro/starter-content/src/components/sections/StatsBar.astro +4 -4
- package/templates/astro/template/package.json.tmpl +1 -1
- package/templates/common/.agents/skills/authoring/SKILL.md +6 -0
- package/templates/common/.agents/skills/authoring/references/block-registry.md +41 -7
- package/templates/common/.agents/skills/authoring/references/sdk-migrations.md +87 -0
- package/templates/nextjs/starter-content/src/components/chrome/Footer.tsx +5 -4
- package/templates/nextjs/starter-content/src/components/chrome/Header.tsx +5 -4
- package/templates/nextjs/starter-content/src/components/elements/ButtonBlock.tsx +5 -4
- package/templates/nextjs/starter-content/src/components/sections/ContainerSection.tsx +9 -9
- package/templates/nextjs/starter-content/src/components/sections/FeatureCard.tsx +5 -4
- package/templates/nextjs/starter-content/src/components/sections/FeatureGrid.tsx +9 -9
- package/templates/nextjs/starter-content/src/components/sections/HomeHero.tsx +5 -4
- package/templates/nextjs/starter-content/src/components/sections/PersonalizationChoice.tsx +10 -10
- package/templates/nextjs/starter-content/src/components/sections/StatsBar.tsx +5 -4
- package/templates/nextjs/template/package.json.tmpl +1 -1
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"dev": "astro dev --host 0.0.0.0",
|
|
8
|
-
"build": "
|
|
8
|
+
"build": "aci error-pages prepare && astro build",
|
|
9
9
|
"preview": "ACI_CONTENT_ROOT=./.aci/compiled astro preview --host 0.0.0.0",
|
|
10
10
|
"typecheck": "ACI_CONTENT_ROOT=./.aci/compiled astro check --tsconfig ./tsconfig.json",
|
|
11
11
|
"test": "node --import tsx --test tests/**/*.test.ts",
|
|
@@ -918,3 +918,9 @@ Read `references/asset-rendering.md` for `imageAttrs()`, `Picture`,
|
|
|
918
918
|
Read `references/type-safety.md` for `ContentProps` from contracts,
|
|
919
919
|
`as any` prohibition, CSS custom property typing, site config
|
|
920
920
|
generics, and block slot type imports.
|
|
921
|
+
|
|
922
|
+
## Deep Dive: SDK Migrations
|
|
923
|
+
|
|
924
|
+
Read `references/sdk-migrations.md` when an `@gradial/aci` upgrade's
|
|
925
|
+
changelog says `Migration required`: what changed, which files to touch, the
|
|
926
|
+
fix, and how to confirm it with `npm test`.
|
|
@@ -3,21 +3,55 @@
|
|
|
3
3
|
The component registry maps contract names to runtime components. It is built
|
|
4
4
|
using `createRegistry` from `@gradial/aci` with raw `[contract, component]` tuples.
|
|
5
5
|
|
|
6
|
-
Every registered component must extend `
|
|
7
|
-
`@gradial/aci`) and
|
|
6
|
+
Every registered component must extend `AciRootProps` (exported from
|
|
7
|
+
`@gradial/aci`) and put `{...aciRootAttrs(props)}` on its root element. The
|
|
8
8
|
renderer passes `data-aci-block` and `data-aci-component` to every block so
|
|
9
9
|
analytics can join events to content; a component that does not declare the
|
|
10
10
|
type fails `createRegistry` typecheck with
|
|
11
|
-
`<id>: props type must extend
|
|
12
|
-
|
|
13
|
-
`npm test`.
|
|
11
|
+
`<id>: props type must extend AciRootProps from @gradial/aci`, and one that
|
|
12
|
+
does not place them on its root fails the `rootIdentity` conformance check
|
|
13
|
+
under `npm test`.
|
|
14
14
|
|
|
15
15
|
```tsx
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
import { aciRootAttrs, type AciRootProps } from '@gradial/aci';
|
|
17
|
+
import type { ContentProps } from '@/cms/contracts/components/statsBar.contract';
|
|
18
|
+
|
|
19
|
+
export function StatsBar(props: ContentProps & AciRootProps) {
|
|
20
|
+
const { stats = [] } = props;
|
|
21
|
+
return <section {...aciRootAttrs(props)} className="…">…</section>;
|
|
22
|
+
}
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
In Astro, write `<section {...aciRootAttrs(Astro.props)}>`. See
|
|
26
|
+
`sdk-migrations.md` for the upgrade from a component that predates the
|
|
27
|
+
contract.
|
|
28
|
+
|
|
29
|
+
**Empty state** — A block renders its root on every render, empty content included. Render
|
|
30
|
+
the root with `aciRootAttrs` and no children instead of returning `null`.
|
|
31
|
+
|
|
32
|
+
```tsx
|
|
33
|
+
if (stats.length === 0) return <section {...aciRootAttrs(props)} />;
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
```astro
|
|
37
|
+
<section {...aciRootAttrs(Astro.props)}>{stats.map((s) => <Stat {...s} />)}</section>
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
**Client-only widget** — Render a server placeholder root that carries the attributes and holds the
|
|
41
|
+
client-only part.
|
|
42
|
+
|
|
43
|
+
```tsx
|
|
44
|
+
const Map = dynamic(() => import('./Map'), { ssr: false });
|
|
45
|
+
|
|
46
|
+
export function StoreMap(props: ContentProps & AciRootProps) {
|
|
47
|
+
return <div {...aciRootAttrs(props)}><Map {...props.map} /></div>;
|
|
18
48
|
}
|
|
19
49
|
```
|
|
20
50
|
|
|
51
|
+
```astro
|
|
52
|
+
<div {...aciRootAttrs(Astro.props)}><Map client:only="react" {...map} /></div>
|
|
53
|
+
```
|
|
54
|
+
|
|
21
55
|
The tuple form provides **type-level validation** — the component must accept
|
|
22
56
|
at least the props derived from the contract's Zod schema. If you register a
|
|
23
57
|
component with the wrong prop type, TypeScript will fail at the call site.
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# SDK Migrations
|
|
2
|
+
|
|
3
|
+
Some `@gradial/aci` releases require a change in the site's code. Each such
|
|
4
|
+
release has a `### Migration required` section in the SDK `CHANGELOG.md`
|
|
5
|
+
(`node_modules/@gradial/aci/CHANGELOG.md`) that links here. There is no
|
|
6
|
+
migrate command and no file to record: make the change, then confirm it with
|
|
7
|
+
`npm test`. If the site is not ready, pin the previous `@gradial/aci` version
|
|
8
|
+
and rebuild.
|
|
9
|
+
|
|
10
|
+
## Root identity (0.1.52-rc.24)
|
|
11
|
+
|
|
12
|
+
Every registered component must put the two root identity props the renderer
|
|
13
|
+
passes, `data-aci-block` and `data-aci-component`, on its own root element.
|
|
14
|
+
Analytics joins events to content by them, and block-level features build on
|
|
15
|
+
them.
|
|
16
|
+
|
|
17
|
+
- **Which files** — every component in `src/cms/registry.ts`. Not the
|
|
18
|
+
elements those components render inside themselves.
|
|
19
|
+
- **The fix** — extend `AciRootProps` (formerly `RootIdentityProps`) and put
|
|
20
|
+
`{...aciRootAttrs(props)}` on the root element. A rest-spread (`...root`,
|
|
21
|
+
then `{...root}` on the root) also works.
|
|
22
|
+
- **Who checks it** — `npm test` runs the `rootIdentity` conformance check,
|
|
23
|
+
which renders each component and names the file of each one that fails.
|
|
24
|
+
On Next, `next build` fails first through the registry type check;
|
|
25
|
+
`astro build` does not check it.
|
|
26
|
+
|
|
27
|
+
Next.js:
|
|
28
|
+
|
|
29
|
+
```tsx
|
|
30
|
+
import { aciRootAttrs, type AciRootProps } from '@gradial/aci';
|
|
31
|
+
|
|
32
|
+
export function StatsBar(props: ContentProps & AciRootProps) {
|
|
33
|
+
const { stats = [] } = props;
|
|
34
|
+
return <section {...aciRootAttrs(props)} className="…">…</section>;
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Astro:
|
|
39
|
+
|
|
40
|
+
```astro
|
|
41
|
+
---
|
|
42
|
+
import { aciRootAttrs, type AciRootProps } from '@gradial/aci';
|
|
43
|
+
|
|
44
|
+
interface Props extends AciRootProps {
|
|
45
|
+
stats?: Stat[];
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const { stats = [] } = Astro.props;
|
|
49
|
+
---
|
|
50
|
+
<section {...aciRootAttrs(Astro.props)} class="…">…</section>
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
`aciRootAttrs` and the `AciRootProps` name ship after 0.1.52-rc.24. On an
|
|
54
|
+
older release, write `RootIdentityProps` and a rest-spread instead.
|
|
55
|
+
|
|
56
|
+
To confirm, run `npm test`. The `rootIdentity` check passes, or it lists each
|
|
57
|
+
failing component with its source file, the defect, and the fix.
|
|
58
|
+
|
|
59
|
+
### Empty state
|
|
60
|
+
|
|
61
|
+
A block renders its root on every render, empty content included. Render
|
|
62
|
+
the root with `aciRootAttrs` and no children instead of returning `null`.
|
|
63
|
+
|
|
64
|
+
```tsx
|
|
65
|
+
if (stats.length === 0) return <section {...aciRootAttrs(props)} />;
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
```astro
|
|
69
|
+
<section {...aciRootAttrs(Astro.props)}>{stats.map((s) => <Stat {...s} />)}</section>
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Client-only widget
|
|
73
|
+
|
|
74
|
+
Render a server placeholder root that carries the attributes and holds the
|
|
75
|
+
client-only part.
|
|
76
|
+
|
|
77
|
+
```tsx
|
|
78
|
+
const Map = dynamic(() => import('./Map'), { ssr: false });
|
|
79
|
+
|
|
80
|
+
export function StoreMap(props: ContentProps & AciRootProps) {
|
|
81
|
+
return <div {...aciRootAttrs(props)}><Map {...props.map} /></div>;
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
```astro
|
|
86
|
+
<div {...aciRootAttrs(Astro.props)}><Map client:only="react" {...map} /></div>
|
|
87
|
+
```
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { Container } from '@/components/elements/Container';
|
|
2
|
-
import type
|
|
2
|
+
import { aciRootAttrs, type AciRootProps } from '@gradial/aci';
|
|
3
3
|
import { Logo } from '@/components/elements/Logo';
|
|
4
4
|
import type { FooterColumn, FooterGroup, FooterLink } from '@gradial/aci/content';
|
|
5
5
|
|
|
6
|
-
interface FooterProps extends
|
|
6
|
+
interface FooterProps extends AciRootProps {
|
|
7
7
|
brandHref: string;
|
|
8
8
|
brandLabel: string;
|
|
9
9
|
brandLogo?: { src: string; alt?: string } | null;
|
|
@@ -18,9 +18,10 @@ function columnGroups(column: FooterColumn): FooterGroup[] {
|
|
|
18
18
|
return [{ id: column.id, title: column.title || '', links: column.links || [] }];
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
export function Footer(
|
|
21
|
+
export function Footer(props: FooterProps) {
|
|
22
|
+
const { brandHref, brandLabel, brandLogo = null, columns = [], social = [], legalLinks = [], copyright = '' } = props;
|
|
22
23
|
return (
|
|
23
|
-
<footer {...
|
|
24
|
+
<footer {...aciRootAttrs(props)} className="bm-footer relative pt-[var(--spacing-footer-y)]" id="footer">
|
|
24
25
|
<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" />
|
|
25
26
|
<Container>
|
|
26
27
|
<div className="footer-main-grid grid grid-cols-1 gap-12 lg:grid-cols-2">
|
|
@@ -2,10 +2,10 @@ import { Button } from '@/components/elements/Button';
|
|
|
2
2
|
import { Container } from '@/components/elements/Container';
|
|
3
3
|
import { Icon } from '@/components/elements/Icon';
|
|
4
4
|
import { Logo } from '@/components/elements/Logo';
|
|
5
|
-
import type
|
|
5
|
+
import { aciRootAttrs, type AciRootProps } from '@gradial/aci';
|
|
6
6
|
import type { NavItem, UtilityItem } from '@gradial/aci/content';
|
|
7
7
|
|
|
8
|
-
interface HeaderProps extends
|
|
8
|
+
interface HeaderProps extends AciRootProps {
|
|
9
9
|
brandHref: string;
|
|
10
10
|
brandLabel: string;
|
|
11
11
|
brandLogo?: { src: string; alt?: string } | null;
|
|
@@ -13,9 +13,10 @@ interface HeaderProps extends RootIdentityProps {
|
|
|
13
13
|
utilities?: UtilityItem[];
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
export function Header(
|
|
16
|
+
export function Header(props: HeaderProps) {
|
|
17
|
+
const { brandHref, brandLabel, brandLogo = null, navigation = [], utilities = [] } = props;
|
|
17
18
|
return (
|
|
18
|
-
<header {...
|
|
19
|
+
<header {...aciRootAttrs(props)} className="bm-header fixed top-0 left-0 right-0 z-[var(--z-header)] border-b border-[var(--header-border)]">
|
|
19
20
|
<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)]">
|
|
20
21
|
Skip to main content
|
|
21
22
|
</a>
|
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
import { Button } from '@/components/elements/Button';
|
|
2
2
|
import { Icon } from '@/components/elements/Icon';
|
|
3
|
-
import type
|
|
3
|
+
import { aciRootAttrs, type AciRootProps } from '@gradial/aci';
|
|
4
4
|
import type { ContentProps } from '@/cms/contracts/components/button.contract';
|
|
5
5
|
|
|
6
|
-
export type ButtonBlockProps = ContentProps &
|
|
6
|
+
export type ButtonBlockProps = ContentProps & AciRootProps;
|
|
7
7
|
|
|
8
|
-
export function ButtonBlock(
|
|
8
|
+
export function ButtonBlock(props: ButtonBlockProps) {
|
|
9
|
+
const { label = '', href = '#', variant = 'primary', newWindow = false, icon = '', iconPosition = 'before', className = '' } = props;
|
|
9
10
|
const target = newWindow ? '_blank' : undefined;
|
|
10
11
|
const rel = newWindow ? 'noreferrer' : undefined;
|
|
11
12
|
|
|
12
13
|
return (
|
|
13
|
-
<Button href={href} variant={variant} target={target} rel={rel} className={className} {...
|
|
14
|
+
<Button href={href} variant={variant} target={target} rel={rel} className={className} {...aciRootAttrs(props)}>
|
|
14
15
|
{icon && iconPosition === 'before' ? <Icon name={icon} /> : null}
|
|
15
16
|
<span>{label}</span>
|
|
16
17
|
{icon && iconPosition === 'after' ? <Icon name={icon} /> : null}
|
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
import { Container } from '@/components/elements/Container';
|
|
2
2
|
import { type ContentProps } from '@/cms/contracts/components/container.contract';
|
|
3
|
-
import type
|
|
3
|
+
import { aciRootAttrs, type AciRootProps } from '@gradial/aci';
|
|
4
4
|
import type { RenderChildren } from '@gradial/aci/react';
|
|
5
5
|
|
|
6
|
-
export async function ContainerSection({
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
}
|
|
6
|
+
export async function ContainerSection(props: ContentProps & AciRootProps & { renderChildren: RenderChildren<NonNullable<ContentProps['content']>> }) {
|
|
7
|
+
const {
|
|
8
|
+
theme = 'auto',
|
|
9
|
+
className = '',
|
|
10
|
+
content,
|
|
11
|
+
renderChildren,
|
|
12
|
+
} = props;
|
|
13
13
|
const buttonCount = content?.filter((b) => b.component === 'button').length ?? 0;
|
|
14
14
|
const layout = buttonCount === 1
|
|
15
15
|
? 'flex-col items-center'
|
|
16
16
|
: 'flex-col items-center sm:flex-row sm:justify-center sm:gap-4';
|
|
17
17
|
|
|
18
18
|
return (
|
|
19
|
-
<section className={`theme-${theme} relative overflow-hidden py-[var(--spacing-section-y)] ${className}`} {...
|
|
19
|
+
<section className={`theme-${theme} relative overflow-hidden py-[var(--spacing-section-y)] ${className}`} {...aciRootAttrs(props)}>
|
|
20
20
|
{theme === 'dark' ? (
|
|
21
21
|
<div className="pointer-events-none absolute inset-0 bg-[image:var(--gradient-hero-base)] opacity-30" />
|
|
22
22
|
) : null}
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { Icon } from '@/components/elements/Icon';
|
|
2
|
-
import type
|
|
2
|
+
import { aciRootAttrs, type AciRootProps } from '@gradial/aci';
|
|
3
3
|
import type { ContentProps } from '@/cms/contracts/components/featureCard.contract';
|
|
4
4
|
|
|
5
|
-
export type FeatureCardProps = ContentProps &
|
|
5
|
+
export type FeatureCardProps = ContentProps & AciRootProps;
|
|
6
6
|
|
|
7
|
-
export function FeatureCard(
|
|
7
|
+
export function FeatureCard(props: FeatureCardProps) {
|
|
8
|
+
const { icon = '', title = '', description = '' } = props;
|
|
8
9
|
return (
|
|
9
|
-
<div {...
|
|
10
|
+
<div {...aciRootAttrs(props)} 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)]">
|
|
10
11
|
<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" />
|
|
11
12
|
{icon ? (
|
|
12
13
|
<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)]">
|
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
import { Container } from '@/components/elements/Container';
|
|
2
2
|
import { type ContentProps } from '@/cms/contracts/components/featureGrid.contract';
|
|
3
|
-
import type
|
|
3
|
+
import { aciRootAttrs, type AciRootProps } from '@gradial/aci';
|
|
4
4
|
import type { RenderChildren } from '@gradial/aci/react';
|
|
5
5
|
|
|
6
|
-
export async function FeatureGrid({
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
}
|
|
6
|
+
export async function FeatureGrid(props: ContentProps & AciRootProps & { renderChildren: RenderChildren<NonNullable<ContentProps['features']>> }) {
|
|
7
|
+
const {
|
|
8
|
+
title = '',
|
|
9
|
+
subtitle = '',
|
|
10
|
+
features,
|
|
11
|
+
renderChildren,
|
|
12
|
+
} = props;
|
|
13
13
|
const count = features?.length ?? 0;
|
|
14
14
|
const cols = count <= 2 ? 'sm:grid-cols-2' : 'sm:grid-cols-2 lg:grid-cols-3';
|
|
15
15
|
|
|
16
16
|
return (
|
|
17
|
-
<section className="theme-light relative overflow-hidden py-[var(--spacing-section-y)] bg-[var(--color-white)]" {...
|
|
17
|
+
<section className="theme-light relative overflow-hidden py-[var(--spacing-section-y)] bg-[var(--color-white)]" {...aciRootAttrs(props)}>
|
|
18
18
|
<div className="pointer-events-none absolute inset-0 bg-[image:var(--gradient-hero-base)] opacity-50" />
|
|
19
19
|
<Container className="relative">
|
|
20
20
|
<div className="mb-14 max-w-[var(--max-width-container-narrow)]">
|
|
@@ -1,19 +1,20 @@
|
|
|
1
1
|
import { Button } from '@/components/elements/Button';
|
|
2
2
|
import { Container } from '@/components/elements/Container';
|
|
3
|
-
import { primaryImageSource, type
|
|
3
|
+
import { aciRootAttrs, primaryImageSource, type AciRootProps } from '@gradial/aci';
|
|
4
4
|
import type { ContentProps } from '@/cms/contracts/components/homeHero.contract';
|
|
5
5
|
|
|
6
6
|
type HeroImage = NonNullable<ContentProps['image']>;
|
|
7
7
|
|
|
8
|
-
export type HomeHeroProps = Omit<ContentProps, 'image'> & { image?: HeroImage } &
|
|
8
|
+
export type HomeHeroProps = Omit<ContentProps, 'image'> & { image?: HeroImage } & AciRootProps;
|
|
9
9
|
|
|
10
|
-
export function HomeHero(
|
|
10
|
+
export function HomeHero(props: HomeHeroProps) {
|
|
11
|
+
const { eyebrow = '', headline = '', description = '', ctaLabel = '', ctaHref = '#', image } = props;
|
|
11
12
|
const imageSrc = heroImageSrc(image);
|
|
12
13
|
const imageAlt = image?.alt || headline;
|
|
13
14
|
|
|
14
15
|
return (
|
|
15
16
|
<section
|
|
16
|
-
{...
|
|
17
|
+
{...aciRootAttrs(props)}
|
|
17
18
|
className="theme-light relative flex min-h-[var(--height-hero-min)] items-center bg-cover bg-center"
|
|
18
19
|
aria-label={imageAlt}
|
|
19
20
|
style={{
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import { useEffect, useState } from 'react';
|
|
4
|
-
import type
|
|
4
|
+
import { aciRootAttrs, type AciRootProps } from '@gradial/aci';
|
|
5
5
|
import type { ContentProps } from '@/cms/contracts/components/personalizationChoice.contract';
|
|
6
6
|
|
|
7
7
|
type AciWindow = Window & { aci?: (command: string, value: unknown) => void };
|
|
@@ -10,14 +10,14 @@ function hasCookie(name: string): boolean {
|
|
|
10
10
|
return document.cookie.split('; ').some((value) => value.startsWith(`${name}=`));
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
export function PersonalizationChoice({
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
}
|
|
13
|
+
export function PersonalizationChoice(props: ContentProps & AciRootProps) {
|
|
14
|
+
const {
|
|
15
|
+
title,
|
|
16
|
+
description,
|
|
17
|
+
redLabel,
|
|
18
|
+
blueLabel,
|
|
19
|
+
refreshLabel,
|
|
20
|
+
} = props;
|
|
21
21
|
const [consent, setConsent] = useState(false);
|
|
22
22
|
const [marker, setMarker] = useState(false);
|
|
23
23
|
|
|
@@ -32,7 +32,7 @@ export function PersonalizationChoice({
|
|
|
32
32
|
}, []);
|
|
33
33
|
|
|
34
34
|
return (
|
|
35
|
-
<section className="border-y border-[var(--color-border)] bg-[var(--color-surface)] py-[var(--spacing-section-y)]" {...
|
|
35
|
+
<section className="border-y border-[var(--color-border)] bg-[var(--color-surface)] py-[var(--spacing-section-y)]" {...aciRootAttrs(props)}>
|
|
36
36
|
<div className="mx-auto max-w-[var(--layout-content-max)] px-[var(--layout-gutter)] text-center">
|
|
37
37
|
<h2 className="text-[length:var(--text-h2)] font-[var(--font-weight-semibold)] text-[var(--color-fg)]">{title}</h2>
|
|
38
38
|
{description ? <p className="mx-auto mt-3 max-w-2xl text-[var(--color-fg-muted)]">{description}</p> : null}
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { Container } from '@/components/elements/Container';
|
|
2
|
-
import type
|
|
2
|
+
import { aciRootAttrs, type AciRootProps } from '@gradial/aci';
|
|
3
3
|
import type { ContentProps } from '@/cms/contracts/components/statsBar.contract';
|
|
4
4
|
|
|
5
|
-
export type StatsBarProps = ContentProps &
|
|
5
|
+
export type StatsBarProps = ContentProps & AciRootProps;
|
|
6
6
|
|
|
7
|
-
export function StatsBar(
|
|
7
|
+
export function StatsBar(props: StatsBarProps) {
|
|
8
|
+
const { stats = [] } = props;
|
|
8
9
|
return (
|
|
9
|
-
<section {...
|
|
10
|
+
<section {...aciRootAttrs(props)} className="theme-light relative border-y border-[var(--color-border-default)] bg-[var(--color-surface-100)] py-[var(--spacing-section-y)]">
|
|
10
11
|
<div className="pointer-events-none absolute inset-0 bg-[image:var(--gradient-hero-base)] opacity-40" />
|
|
11
12
|
<Container className="relative">
|
|
12
13
|
<div className="grid gap-10 sm:grid-cols-2 lg:grid-cols-4">
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"dev": "ACI_CONTENT_PROVIDER=file next dev -H 0.0.0.0",
|
|
8
|
-
"build": "
|
|
8
|
+
"build": "aci error-pages prepare && next build && aci error-pages emit",
|
|
9
9
|
"preview": "next start -H 0.0.0.0",
|
|
10
10
|
"typecheck": "tsc --noEmit",
|
|
11
11
|
"test": "node --import tsx --test tests/**/*.test.ts",
|