@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.
Files changed (70) hide show
  1. package/CHANGELOG.md +77 -0
  2. package/README.md +26 -4
  3. package/dist/astro/index.js +2 -5
  4. package/dist/cli/emit-error-pages.d.mts +2 -1
  5. package/dist/cli/emit-error-pages.mjs +13 -270
  6. package/dist/content/component-block.d.ts +17 -0
  7. package/dist/content/component-block.js +40 -0
  8. package/dist/content/resolve-slots.js +2 -4
  9. package/dist/index.d.ts +2 -1
  10. package/dist/index.js +1 -0
  11. package/dist/internal/aci-root.d.ts +1 -0
  12. package/dist/internal/aci-root.js +49 -0
  13. package/dist/next/config.js +6 -13
  14. package/dist/next/error-seat-payload.d.ts +1 -1
  15. package/dist/next/error-seat-payload.js +1 -2
  16. package/dist/next/error-seat.js +3 -4
  17. package/dist/next/render.js +2 -8
  18. package/dist/platform-tests/adapters/astro.d.ts +2 -0
  19. package/dist/platform-tests/adapters/astro.js +101 -0
  20. package/dist/platform-tests/adapters/next.d.ts +9 -0
  21. package/dist/platform-tests/adapters/next.js +112 -0
  22. package/dist/platform-tests/adapters/site.d.ts +29 -0
  23. package/dist/platform-tests/adapters/site.js +92 -0
  24. package/dist/platform-tests/adapters/types.d.ts +27 -0
  25. package/dist/platform-tests/adapters/types.js +1 -0
  26. package/dist/platform-tests/index.d.ts +47 -0
  27. package/dist/platform-tests/index.js +50 -0
  28. package/dist/platform-tests/sample-props.d.ts +2 -0
  29. package/dist/platform-tests/sample-props.js +82 -0
  30. package/dist/platform-tests/site-check.d.ts +51 -0
  31. package/dist/platform-tests/site-check.js +193 -0
  32. package/dist/registry.d.ts +1 -1
  33. package/dist/testing/conformance/index.d.ts +2 -1
  34. package/dist/testing/conformance/index.js +2 -1
  35. package/dist/testing/conformance/next-stubs.d.ts +13 -4
  36. package/dist/testing/conformance/next-stubs.js +76 -4
  37. package/dist/testing/conformance/preset.js +1 -2
  38. package/dist/testing/conformance/root-identity.d.ts +0 -29
  39. package/dist/testing/conformance/root-identity.js +36 -344
  40. package/dist/testing/conformance/types.d.ts +1 -1
  41. package/dist/types/component.d.ts +11 -3
  42. package/dist/types/component.js +10 -1
  43. package/package.json +9 -8
  44. package/src/cli/emit-error-pages.mjs +13 -273
  45. package/src/types/component.test.ts +17 -0
  46. package/src/types/component.ts +18 -3
  47. package/templates/astro/starter-content/src/components/chrome/Footer.astro +3 -4
  48. package/templates/astro/starter-content/src/components/chrome/Header.astro +3 -4
  49. package/templates/astro/starter-content/src/components/elements/Button.astro +4 -4
  50. package/templates/astro/starter-content/src/components/elements/ButtonBlock.astro +3 -4
  51. package/templates/astro/starter-content/src/components/sections/ContainerSection.astro +4 -4
  52. package/templates/astro/starter-content/src/components/sections/FeatureCard.astro +4 -4
  53. package/templates/astro/starter-content/src/components/sections/FeatureGrid.astro +4 -4
  54. package/templates/astro/starter-content/src/components/sections/HomeHero.astro +3 -4
  55. package/templates/astro/starter-content/src/components/sections/PersonalizationChoice.astro +4 -4
  56. package/templates/astro/starter-content/src/components/sections/StatsBar.astro +4 -4
  57. package/templates/astro/template/package.json.tmpl +1 -1
  58. package/templates/common/.agents/skills/authoring/SKILL.md +6 -0
  59. package/templates/common/.agents/skills/authoring/references/block-registry.md +41 -7
  60. package/templates/common/.agents/skills/authoring/references/sdk-migrations.md +87 -0
  61. package/templates/nextjs/starter-content/src/components/chrome/Footer.tsx +5 -4
  62. package/templates/nextjs/starter-content/src/components/chrome/Header.tsx +5 -4
  63. package/templates/nextjs/starter-content/src/components/elements/ButtonBlock.tsx +5 -4
  64. package/templates/nextjs/starter-content/src/components/sections/ContainerSection.tsx +9 -9
  65. package/templates/nextjs/starter-content/src/components/sections/FeatureCard.tsx +5 -4
  66. package/templates/nextjs/starter-content/src/components/sections/FeatureGrid.tsx +9 -9
  67. package/templates/nextjs/starter-content/src/components/sections/HomeHero.tsx +5 -4
  68. package/templates/nextjs/starter-content/src/components/sections/PersonalizationChoice.tsx +10 -10
  69. package/templates/nextjs/starter-content/src/components/sections/StatsBar.tsx +5 -4
  70. 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": "node ./node_modules/@gradial/aci/src/cli/emit-error-pages.mjs prepare && astro 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 `RootIdentityProps` (exported from
7
- `@gradial/aci`) and spread the rest of its props onto its root element. The
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 RootIdentityProps from @gradial/aci`, and one
12
- that does not spread fails the `rootIdentity` conformance check under
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
- export function StatsBar({ stats = [], ...root }: ContentProps & RootIdentityProps) {
17
- return <section {...root} className="…">…</section>;
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 { RootIdentityProps } from '@gradial/aci';
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 RootIdentityProps {
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({ brandHref, brandLabel, brandLogo = null, columns = [], social = [], legalLinks = [], copyright = '', ...root }: FooterProps) {
21
+ export function Footer(props: FooterProps) {
22
+ const { brandHref, brandLabel, brandLogo = null, columns = [], social = [], legalLinks = [], copyright = '' } = props;
22
23
  return (
23
- <footer {...root} className="bm-footer relative pt-[var(--spacing-footer-y)]" id="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 { RootIdentityProps } from '@gradial/aci';
5
+ import { aciRootAttrs, type AciRootProps } from '@gradial/aci';
6
6
  import type { NavItem, UtilityItem } from '@gradial/aci/content';
7
7
 
8
- interface HeaderProps extends RootIdentityProps {
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({ brandHref, brandLabel, brandLogo = null, navigation = [], utilities = [], ...root }: HeaderProps) {
16
+ export function Header(props: HeaderProps) {
17
+ const { brandHref, brandLabel, brandLogo = null, navigation = [], utilities = [] } = props;
17
18
  return (
18
- <header {...root} className="bm-header fixed top-0 left-0 right-0 z-[var(--z-header)] border-b border-[var(--header-border)]">
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 { RootIdentityProps } from '@gradial/aci';
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 & RootIdentityProps;
6
+ export type ButtonBlockProps = ContentProps & AciRootProps;
7
7
 
8
- export function ButtonBlock({ label = '', href = '#', variant = 'primary', newWindow = false, icon = '', iconPosition = 'before', className = '', ...root }: ButtonBlockProps) {
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} {...root}>
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 { RootIdentityProps } from '@gradial/aci';
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
- theme = 'auto',
8
- className = '',
9
- content,
10
- renderChildren,
11
- ...root
12
- }: ContentProps & RootIdentityProps & { renderChildren: RenderChildren<NonNullable<ContentProps['content']>> }) {
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}`} {...root}>
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 { RootIdentityProps } from '@gradial/aci';
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 & RootIdentityProps;
5
+ export type FeatureCardProps = ContentProps & AciRootProps;
6
6
 
7
- export function FeatureCard({ icon = '', title = '', description = '', ...root }: FeatureCardProps) {
7
+ export function FeatureCard(props: FeatureCardProps) {
8
+ const { icon = '', title = '', description = '' } = props;
8
9
  return (
9
- <div {...root} 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
+ <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 { RootIdentityProps } from '@gradial/aci';
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
- title = '',
8
- subtitle = '',
9
- features,
10
- renderChildren,
11
- ...root
12
- }: ContentProps & RootIdentityProps & { renderChildren: RenderChildren<NonNullable<ContentProps['features']>> }) {
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)]" {...root}>
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 RootIdentityProps } from '@gradial/aci';
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 } & RootIdentityProps;
8
+ export type HomeHeroProps = Omit<ContentProps, 'image'> & { image?: HeroImage } & AciRootProps;
9
9
 
10
- export function HomeHero({ eyebrow = '', headline = '', description = '', ctaLabel = '', ctaHref = '#', image, ...root }: HomeHeroProps) {
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
- {...root}
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 { RootIdentityProps } from '@gradial/aci';
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
- title,
15
- description,
16
- redLabel,
17
- blueLabel,
18
- refreshLabel,
19
- ...root
20
- }: ContentProps & RootIdentityProps) {
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)]" {...root}>
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 { RootIdentityProps } from '@gradial/aci';
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 & RootIdentityProps;
5
+ export type StatsBarProps = ContentProps & AciRootProps;
6
6
 
7
- export function StatsBar({ stats = [], ...root }: StatsBarProps) {
7
+ export function StatsBar(props: StatsBarProps) {
8
+ const { stats = [] } = props;
8
9
  return (
9
- <section {...root} className="theme-light relative border-y border-[var(--color-border-default)] bg-[var(--color-surface-100)] py-[var(--spacing-section-y)]">
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": "node ./node_modules/@gradial/aci/src/cli/emit-error-pages.mjs prepare && next build && node ./node_modules/@gradial/aci/src/cli/emit-error-pages.mjs emit",
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",