@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.
Files changed (227) hide show
  1. package/README.md +48 -108
  2. package/bin/{aci → aci-darwin-arm64} +0 -0
  3. package/bin/aci.js +99 -6
  4. package/dist/assets/index.d.ts +3 -3
  5. package/dist/assets/index.js +3 -3
  6. package/dist/astro/index.d.ts +12 -3
  7. package/dist/astro/index.js +20 -5
  8. package/dist/content/assets.d.ts +17 -0
  9. package/dist/content/assets.js +65 -0
  10. package/dist/content/index.d.ts +1 -0
  11. package/dist/content/index.js +1 -0
  12. package/dist/define-component.d.ts +1 -2
  13. package/dist/define-component.js +73 -5
  14. package/dist/dev/content-watch.d.ts +37 -0
  15. package/dist/dev/content-watch.js +154 -0
  16. package/dist/dev/index.d.ts +24 -43
  17. package/dist/dev/index.js +4 -169
  18. package/dist/index.d.ts +14 -6
  19. package/dist/index.js +8 -9
  20. package/dist/next/asset-route.js +2 -2
  21. package/dist/next/config.d.ts +2 -2
  22. package/dist/next/config.js +5 -5
  23. package/dist/next/content-watch.js +6 -1
  24. package/dist/next/index.d.ts +2 -2
  25. package/dist/next/index.js +2 -2
  26. package/dist/next/middleware.d.ts +1 -0
  27. package/dist/next/middleware.js +49 -8
  28. package/dist/next/page.d.ts +1 -1
  29. package/dist/next/page.js +14 -12
  30. package/dist/next/render.d.ts +3 -3
  31. package/dist/next/render.js +11 -9
  32. package/dist/next/server.d.ts +12 -12
  33. package/dist/next/server.js +1 -1
  34. package/dist/preview/core.d.ts +5 -0
  35. package/dist/preview/core.js +1 -1
  36. package/dist/react/Media.d.ts +24 -0
  37. package/dist/react/Media.js +30 -0
  38. package/dist/react/Picture.d.ts +12 -0
  39. package/dist/react/Picture.js +11 -0
  40. package/dist/react/VideoPlayer.d.ts +13 -0
  41. package/dist/react/{GradialVideoPlayer.js → VideoPlayer.js} +6 -5
  42. package/dist/react/index.d.ts +333 -3
  43. package/dist/react/index.js +89 -3
  44. package/dist/registry.d.ts +2 -2
  45. package/dist/registry.js +3 -3
  46. package/dist/types/block-ref.d.ts +26 -0
  47. package/dist/types/block-ref.js +28 -0
  48. package/dist/types/component.d.ts +36 -32
  49. package/dist/types/config.d.ts +1 -1
  50. package/dist/types/image.d.ts +33 -20
  51. package/dist/types/image.js +42 -50
  52. package/dist/types/index.d.ts +1 -0
  53. package/dist/types/index.js +1 -0
  54. package/dist/types/link.d.ts +38 -0
  55. package/dist/types/link.js +58 -0
  56. package/dist/types/media.d.ts +16 -35
  57. package/dist/types/media.js +15 -84
  58. package/dist/types/renderer.d.ts +1 -1
  59. package/dist/types/rich-text.d.ts +40 -0
  60. package/dist/types/rich-text.js +39 -0
  61. package/dist/types/video.d.ts +7 -13
  62. package/dist/types/video.js +3 -3
  63. package/package.json +29 -76
  64. package/src/cli/compile-registry.mjs +120 -63
  65. package/src/cli/validate-content.mjs +38 -75
  66. package/src/types/block-ref.ts +74 -0
  67. package/src/types/image.ts +79 -60
  68. package/templates/astro/template/.aci.yaml +28 -0
  69. package/templates/astro/template/.storybook/main.ts +79 -0
  70. package/templates/astro/template/.storybook/manager.ts +6 -0
  71. package/templates/astro/template/.storybook/preview.ts +33 -0
  72. package/templates/astro/template/astro.config.mjs +37 -0
  73. package/templates/astro/template/package.json.tmpl +41 -0
  74. package/templates/astro/template/src/cms/contracts/layouts/index.ts +18 -0
  75. package/templates/astro/template/src/cms/registry.ts +28 -0
  76. package/templates/astro/template/src/components/chrome/Footer.astro +107 -0
  77. package/templates/astro/template/src/components/chrome/Header.astro +94 -0
  78. package/templates/astro/template/src/components/elements/AtomicBlock.astro +15 -0
  79. package/templates/astro/template/src/components/elements/AtomicBlocks.astro +16 -0
  80. package/templates/astro/template/src/components/elements/Button.astro +51 -0
  81. package/templates/astro/template/src/components/elements/ButtonBlock.astro +25 -0
  82. package/templates/astro/template/src/components/elements/Container.astro +11 -0
  83. package/templates/astro/template/src/components/elements/Icon.astro +31 -0
  84. package/templates/astro/template/src/components/elements/Logo.astro +29 -0
  85. package/templates/astro/template/src/components/elements/RemoteImage.astro +41 -0
  86. package/templates/astro/template/src/components/sections/ContainerSection.astro +32 -0
  87. package/templates/astro/template/src/components/sections/FeatureCard.astro +23 -0
  88. package/templates/astro/template/src/components/sections/FeatureGrid.astro +35 -0
  89. package/templates/astro/template/src/components/sections/HomeHero.astro +51 -0
  90. package/templates/astro/template/src/components/sections/StatsBar.astro +33 -0
  91. package/templates/astro/template/src/design-system/styles.css +399 -0
  92. package/templates/astro/template/src/design-system/tokens/colors.css +60 -0
  93. package/templates/astro/template/src/design-system/tokens/components.css +69 -0
  94. package/templates/astro/template/src/design-system/tokens/index.css +6 -0
  95. package/templates/astro/template/src/design-system/tokens/radii.css +5 -0
  96. package/templates/astro/template/src/design-system/tokens/shadows.css +5 -0
  97. package/templates/astro/template/src/design-system/tokens/spacing.css +13 -0
  98. package/templates/astro/template/src/design-system/tokens/typography.css +24 -0
  99. package/templates/astro/template/src/env.d.ts +1 -0
  100. package/templates/astro/template/src/fixtures/atomicBlocks.ts +33 -0
  101. package/templates/astro/template/src/fixtures/composition.ts +31 -0
  102. package/templates/astro/template/src/fixtures/index.ts +17 -0
  103. package/templates/astro/template/src/fixtures/sections.ts +39 -0
  104. package/templates/astro/template/src/layouts/SiteLayout.astro +30 -0
  105. package/templates/astro/template/src/pages/[...slug].astro +61 -0
  106. package/templates/astro/template/src/render/RenderPage.astro +66 -0
  107. package/templates/astro/template/src/stories/CompositionStoryFrame.astro +15 -0
  108. package/templates/astro/template/src/stories/HomeHero.stories.ts +24 -0
  109. package/templates/astro/template/src/stories/SectionStoryFrame.astro +16 -0
  110. package/templates/astro/template/src/stories/StoryDocFrame.astro +31 -0
  111. package/templates/astro/template/tsconfig.json +11 -0
  112. package/templates/common/.agents/AGENTS.md +37 -0
  113. package/templates/common/.agents/skills/aci/SKILL.md +162 -0
  114. package/templates/common/.agents/skills/authoring/SKILL.md +590 -0
  115. package/templates/common/.agents/skills/authoring/references/block-registry.md +151 -0
  116. package/templates/common/.agents/skills/authoring/references/contracts.md +153 -0
  117. package/templates/common/.agents/skills/authoring/references/design-system.md +171 -0
  118. package/templates/common/.agents/skills/authoring/references/image-assets.md +73 -0
  119. package/templates/common/.agents/skills/authoring/references/repo-structure.md +157 -0
  120. package/templates/common/.agents/skills/migration/SKILL.md +160 -0
  121. package/templates/common/.agents/skills/migration/references/analyzing-source.md +215 -0
  122. package/templates/common/.agents/skills/migration/references/component-migration.md +200 -0
  123. package/templates/common/.agents/skills/migration/references/selector-map.md +31 -0
  124. package/templates/common/.content/.gradial-dam/blobs/sha256/7f/bf/7fbfc7fe230ff12688c496f660119e41d9725c4504d82e310c15665c73c09292 +0 -0
  125. package/templates/common/.content/.gradial-dam/blobs/sha256/a0/f5/a0f506689de9b15a7055f70103a3f02cd3ba105771ae2de6754df2ede1210d3a +0 -0
  126. package/templates/common/.content/.gradial-dam/blobs/sha256/dd/b7/ddb7e9642be08317bcd4b2bd26b58ba8d0d34dd7239e03e684a4f3634fa3845b +0 -0
  127. package/templates/common/.content/.gradial-dam/blobs/sha256/fb/32/fb3260e8286ea8231ea1c77b9c0be92d6edd9026565731eb14cfdcfd6b1b75b3 +0 -0
  128. package/templates/common/.content/config/site.json +192 -0
  129. package/templates/common/.content/dam/folders/seed/ast_site_about_hero/asset.json +9 -0
  130. package/templates/common/.content/dam/folders/seed/ast_site_about_hero/editorial.json +14 -0
  131. package/templates/common/.content/dam/folders/seed/ast_site_about_hero/governance.json +23 -0
  132. package/templates/common/.content/dam/folders/seed/ast_site_about_hero/versions/v0001.json +37 -0
  133. package/templates/common/.content/dam/folders/seed/ast_site_home_hero/asset.json +9 -0
  134. package/templates/common/.content/dam/folders/seed/ast_site_home_hero/editorial.json +14 -0
  135. package/templates/common/.content/dam/folders/seed/ast_site_home_hero/governance.json +23 -0
  136. package/templates/common/.content/dam/folders/seed/ast_site_home_hero/versions/v0001.json +37 -0
  137. package/templates/common/.content/fragments/navbar/_index.json +25 -0
  138. package/templates/common/.content/fragments/site-footer/_index.json +62 -0
  139. package/templates/common/.content/pages/about/_index.json +51 -0
  140. package/templates/common/.content/pages/home/_index.json +112 -0
  141. package/templates/common/.content/schemas/site.schema.json +123 -0
  142. package/templates/common/README.md.tmpl +45 -0
  143. package/templates/common/contracts/components/button.contract.ts +19 -0
  144. package/templates/common/contracts/components/container.contract.ts +19 -0
  145. package/templates/common/contracts/components/featureCard.contract.ts +15 -0
  146. package/templates/common/contracts/components/featureGrid.contract.ts +16 -0
  147. package/templates/common/contracts/components/homeHero.contract.ts +36 -0
  148. package/templates/common/contracts/components/index.ts +21 -0
  149. package/templates/common/contracts/components/siteFooter.contract.ts +45 -0
  150. package/templates/common/contracts/components/siteNavigation.contract.ts +30 -0
  151. package/templates/common/contracts/components/statsBar.contract.ts +19 -0
  152. package/templates/common/design-system/styles.css +346 -0
  153. package/templates/common/design-system/tokens/colors.css +110 -0
  154. package/templates/common/design-system/tokens/components.css +86 -0
  155. package/templates/common/design-system/tokens/index.css +6 -0
  156. package/templates/common/design-system/tokens/radii.css +5 -0
  157. package/templates/common/design-system/tokens/shadows.css +9 -0
  158. package/templates/common/design-system/tokens/spacing.css +26 -0
  159. package/templates/common/design-system/tokens/typography.css +32 -0
  160. package/templates/common/eslint-rules/no-raw-tailwind-values.js +127 -0
  161. package/templates/common/public/assets/Dwire-model.png +0 -0
  162. package/templates/common/public/assets/bare-metal-logo.png +0 -0
  163. package/templates/common/public/assets/meridian-logo.svg +8 -0
  164. package/templates/common/public/assets/seattle.avif +0 -0
  165. package/templates/nextjs/template/.aci.yaml +26 -0
  166. package/templates/nextjs/template/.storybook/main.ts +13 -0
  167. package/templates/nextjs/template/.storybook/preview.ts +34 -0
  168. package/templates/nextjs/template/eslint.config.mjs +36 -0
  169. package/templates/nextjs/template/next-env.d.ts +6 -0
  170. package/templates/nextjs/template/next.config.ts +8 -0
  171. package/templates/nextjs/template/package.json.tmpl +46 -0
  172. package/templates/nextjs/template/postcss.config.mjs +7 -0
  173. package/templates/nextjs/template/src/app/[[...slug]]/page.tsx +8 -0
  174. package/templates/nextjs/template/src/app/gradial/assets/[releaseId]/[...path]/route.ts +31 -0
  175. package/templates/nextjs/template/src/app/layout.tsx +29 -0
  176. package/templates/nextjs/template/src/cms/contracts/components/button.contract.ts +19 -0
  177. package/templates/nextjs/template/src/cms/contracts/components/container.contract.ts +20 -0
  178. package/templates/nextjs/template/src/cms/contracts/components/homeHero.contract.ts +36 -0
  179. package/templates/nextjs/template/src/cms/contracts/components/index.ts +15 -0
  180. package/templates/nextjs/template/src/cms/contracts/components/siteFooter.contract.ts +45 -0
  181. package/templates/nextjs/template/src/cms/contracts/components/siteNavigation.contract.ts +30 -0
  182. package/templates/nextjs/template/src/cms/contracts/layouts/index.ts +16 -0
  183. package/templates/nextjs/template/src/cms/registry.ts +28 -0
  184. package/templates/nextjs/template/src/components/chrome/Footer.tsx +86 -0
  185. package/templates/nextjs/template/src/components/chrome/Header.tsx +74 -0
  186. package/templates/nextjs/template/src/components/elements/AtomicBlock.tsx +13 -0
  187. package/templates/nextjs/template/src/components/elements/Button.tsx +40 -0
  188. package/templates/nextjs/template/src/components/elements/ButtonBlock.tsx +18 -0
  189. package/templates/nextjs/template/src/components/elements/Container.tsx +14 -0
  190. package/templates/nextjs/template/src/components/elements/Icon.tsx +29 -0
  191. package/templates/nextjs/template/src/components/elements/Logo.tsx +38 -0
  192. package/templates/nextjs/template/src/components/sections/ContainerSection.tsx +28 -0
  193. package/templates/nextjs/template/src/components/sections/FeatureCard.tsx +25 -0
  194. package/templates/nextjs/template/src/components/sections/FeatureGrid.tsx +36 -0
  195. package/templates/nextjs/template/src/components/sections/HomeHero.tsx +62 -0
  196. package/templates/nextjs/template/src/components/sections/StatsBar.tsx +30 -0
  197. package/templates/nextjs/template/src/middleware.ts +4 -0
  198. package/templates/nextjs/template/src/stories/Button.stories.tsx +38 -0
  199. package/templates/nextjs/template/src/stories/Header.stories.tsx +30 -0
  200. package/templates/nextjs/template/src/stories/HomeHero.stories.tsx +33 -0
  201. package/templates/nextjs/template/tsconfig.json +23 -0
  202. package/templates/nextjs/template/vercel.json +6 -0
  203. package/dist/content/contract.d.ts +0 -83
  204. package/dist/content/contract.js +0 -104
  205. package/dist/content/tailwind-validator.d.ts +0 -6
  206. package/dist/content/tailwind-validator.js +0 -31
  207. package/dist/content/validation.d.ts +0 -108
  208. package/dist/content/validation.js +0 -183
  209. package/dist/react/GradialMedia.d.ts +0 -24
  210. package/dist/react/GradialMedia.js +0 -31
  211. package/dist/react/GradialPicture.d.ts +0 -14
  212. package/dist/react/GradialPicture.js +0 -30
  213. package/dist/react/GradialVideoPlayer.d.ts +0 -13
  214. package/dist/sveltekit/index.d.ts +0 -25
  215. package/dist/sveltekit/index.js +0 -66
  216. package/dist/sveltekit/preview.d.ts +0 -5
  217. package/dist/sveltekit/preview.js +0 -33
  218. package/src/types/component.ts +0 -49
  219. package/src/types/config.ts +0 -37
  220. package/src/types/data.ts +0 -47
  221. package/src/types/index.ts +0 -10
  222. package/src/types/layout.ts +0 -29
  223. package/src/types/media.ts +0 -125
  224. package/src/types/page.ts +0 -47
  225. package/src/types/render-mode.ts +0 -10
  226. package/src/types/renderer.ts +0 -83
  227. package/src/types/video.ts +0 -66
@@ -0,0 +1,151 @@
1
+ # Component Registry Patterns
2
+
3
+ The component registry maps contract names to runtime components. It is built
4
+ using `createRegistry` from `@gradial/aci` with raw `[contract, component]` tuples.
5
+
6
+ The tuple form provides **type-level validation** — the component must accept
7
+ at least the props derived from the contract's Zod schema. If you register a
8
+ component with the wrong prop type, TypeScript will fail at the call site.
9
+
10
+ **Never use `Record<string, ComponentType<any>>`.** This hides prop
11
+ mismatches and name typos until runtime.
12
+
13
+ **Never alias** — each contract name maps to exactly one runtime component.
14
+ No `hero` + `page_hero` pointing to the same component.
15
+
16
+ ## Registry File
17
+
18
+ All frameworks use a single `src/cms/registry.ts` file:
19
+
20
+ ```ts
21
+ import { createRegistry } from '@gradial/aci';
22
+ import { homeHeroContract } from './contracts/components/homeHero.contract';
23
+ import { containerContract } from './contracts/components/container.contract';
24
+ import { buttonContract } from './contracts/components/button.contract';
25
+ import { HomeHero } from '@/components/sections/HomeHero';
26
+ import { ContainerSection } from '@/components/sections/ContainerSection';
27
+ import { ButtonBlock } from '@/components/elements/ButtonBlock';
28
+
29
+ export const registry = createRegistry([
30
+ [homeHeroContract, HomeHero],
31
+ [containerContract, ContainerSection],
32
+ [buttonContract, ButtonBlock],
33
+ ]);
34
+ ```
35
+
36
+ When adding a new component:
37
+ 1. Create the contract in `src/cms/contracts/components/{name}.contract.ts`
38
+ 2. Export it from `src/cms/contracts/components/index.ts`
39
+ 3. Create the runtime component in `src/components/{elements|sections|chrome}/`
40
+ 4. Add a `[contract, component]` tuple in `src/cms/registry.ts`
41
+
42
+ ## Next.js Pattern
43
+
44
+ The SDK provides `createPage(registry)` which owns param normalization, content
45
+ loading, layout resolution, metadata generation, notFound handling, slot
46
+ resolution (including fragment defaults), and block rendering.
47
+
48
+ Chrome (Header/Footer) is **not** in `layout.tsx`. It is CMS-managed via
49
+ layout fragment defaults. The `layout.tsx` file is minimal:
50
+
51
+ ```tsx
52
+ // src/app/layout.tsx — minimal, no chrome
53
+ import type { ReactNode } from 'react';
54
+ import '@/design-system/styles.css';
55
+
56
+ export default function Layout({ children }: { children: ReactNode }) {
57
+ return (
58
+ <html lang="en">
59
+ <body>{children}</body>
60
+ </html>
61
+ );
62
+ }
63
+ ```
64
+
65
+ ```tsx
66
+ // src/app/[[...slug]]/page.tsx — SDK owns all rendering
67
+ import { createPage } from '@gradial/aci/next/page';
68
+ import { registry } from '@/cms/registry';
69
+
70
+ const page = createPage(registry);
71
+ export const dynamic = 'force-dynamic';
72
+ export const generateMetadata = page.generateMetadata;
73
+ export default page.default;
74
+ ```
75
+
76
+ `createPage` loads the page's layout, resolves all slots (filling from page
77
+ regions or layout fragment defaults), and renders blocks via the registry.
78
+ The `main` slot is wrapped in `<main>`; other slots render as fragments.
79
+
80
+ ## Custom Server Composition
81
+
82
+ For route-specific server behavior such as blogs, events, and product detail
83
+ pages, use public server-only providers:
84
+
85
+ ```ts
86
+ import { loadRenderInput } from '@gradial/aci/content';
87
+ import { FileContentProvider } from '@gradial/aci/providers/file';
88
+
89
+ const provider = new FileContentProvider();
90
+ const input = await loadRenderInput(provider, '/events');
91
+ ```
92
+
93
+ Do not import providers from client components. Browser fetch should go through
94
+ an app-owned HTTP endpoint backed by a server-side `ContentProvider`.
95
+
96
+ ## Astro Pattern
97
+
98
+ Astro uses a catch-all route and renders via the shared registry:
99
+
100
+ ```astro
101
+ ---
102
+ // src/pages/[...slug].astro
103
+ import { createPage } from '@gradial/aci/astro/page';
104
+ import { registry } from '@/cms/registry';
105
+
106
+ const page = createPage(registry);
107
+ export const getStaticPaths = page.getStaticPaths;
108
+ export default page.default;
109
+ ---
110
+ ```
111
+
112
+ The `createPage` helper owns param normalization, content loading, layout
113
+ resolution, metadata generation, notFound handling, slot resolution
114
+ (including fragment defaults), and block rendering.
115
+
116
+ ### Astro-Specific Paths
117
+
118
+ | Concern | Path |
119
+ |---------|------|
120
+ | Routes | `src/pages/[...slug].astro` |
121
+ | Layout | `src/layouts/SiteLayout.astro` |
122
+ | Config | `astro.config.mjs` with `withAci()` |
123
+ | Registry | `src/cms/registry.ts` (shared) |
124
+ | Storybook | `.storybook/main.ts`, `.storybook/preview.ts` |
125
+
126
+ ## Middleware
127
+
128
+ ```ts
129
+ // src/middleware.ts
130
+ import { createMiddleware } from '@gradial/aci/next/middleware';
131
+ export default createMiddleware();
132
+ export { config } from '@gradial/aci/next/middleware';
133
+ ```
134
+
135
+ ## Asset Routes
136
+
137
+ ```ts
138
+ // src/app/gradial/assets/[releaseId]/[...path]/route.ts
139
+ export { GET, runtime, dynamic } from '@gradial/aci/next/asset-route';
140
+ ```
141
+
142
+ ## Anti-Patterns
143
+
144
+ - `Record<string, any>` or `Record<string, ComponentType<any>>` — hides type errors
145
+ - `createRegistryEntry()` wrapper — use raw `[contract, component]` tuples instead
146
+ - Aliasing: two contract names → same component
147
+ - Hand-rolled content loading in route files instead of `createPage`
148
+ - `src/lib/content.ts` shim duplicating SDK behavior
149
+ - Local Vite middleware for DAM derivative serving instead of `withAci()`
150
+ - SvelteKit patterns (conditional rendering, `svelte:component`) — SvelteKit
151
+ support has been removed
@@ -0,0 +1,153 @@
1
+ # Contracts Reference
2
+
3
+ ## Component Contract API
4
+
5
+ ```ts
6
+ import { defineComponentContract, ImageSchema } from '@gradial/aci';
7
+ import { z } from 'zod';
8
+
9
+ export const schema = z.object({
10
+ headline: z.string().min(1),
11
+ image: ImageSchema,
12
+ });
13
+
14
+ export type ContentProps = z.infer<typeof schema>;
15
+
16
+ export const heroContract = defineComponentContract({
17
+ id: 'hero',
18
+ props: schema,
19
+ imageSlots: {
20
+ image: {
21
+ outputs: [{ aspectRatio: '16:9', widths: [960, 1280, 1920] }],
22
+ formats: ['webp'],
23
+ sizes: '100vw',
24
+ },
25
+ },
26
+ });
27
+ ```
28
+
29
+ The `defineComponentContract` function takes:
30
+ - `id` — the contract name used in content JSON `component` fields and the
31
+ registry key. snake_case.
32
+ - `props` — the Zod schema for content props.
33
+ - `imageSlots` (optional) — image processing configuration.
34
+ - `displayName` (optional) — human-readable name.
35
+ - `validate` (optional) — additional validation functions.
36
+
37
+ `renderModes` has been removed from the contract API. There is no
38
+ `renderModes`, `canStatic`, `canSSR`, or `canClientIsland` field.
39
+
40
+ ## Contract Rules
41
+
42
+ - Import `@gradial/aci`, `zod`, and shared schema helpers only.
43
+ - Do not import React components, Astro components, CSS, browser
44
+ globals, framework runtime modules, or customer content.
45
+ - Export a conventional `<camelName>Contract` value.
46
+ - Export `ContentProps` from `z.infer<typeof schema>` so runtime
47
+ implementation can use it.
48
+ - Use contract `id` as the content JSON `component` value and runtime
49
+ registry key.
50
+ - `renderModes` has been removed. Do not include it in contracts.
51
+
52
+ ## Component Barrel
53
+
54
+ ```ts
55
+ // src/cms/contracts/components/index.ts
56
+ import { heroContract } from './hero.contract';
57
+ import { buttonContract } from './button.contract';
58
+
59
+ export default [heroContract, buttonContract];
60
+ ```
61
+
62
+ ## Layout Contract API
63
+
64
+ ```ts
65
+ export default [
66
+ {
67
+ name: 'marketing',
68
+ slots: [
69
+ { name: 'main', required: true },
70
+ { name: 'footer', required: false },
71
+ ],
72
+ },
73
+ ];
74
+ ```
75
+
76
+ Content pages reference layouts by name:
77
+
78
+ ```json
79
+ {
80
+ "layout": "marketing",
81
+ "regions": {
82
+ "main": [...],
83
+ "footer": [...]
84
+ }
85
+ }
86
+ ```
87
+
88
+ ## Multiple Runtime Mappings
89
+
90
+ One runtime component may implement multiple contract names. Define
91
+ separate contract files, then map each contract name to the same runtime
92
+ component explicitly in the registry.
93
+
94
+ ## .aci.yaml Configuration
95
+
96
+ Declares ACI contract entrypoints and framework behavior. Lives at the
97
+ project root.
98
+
99
+ ```yaml
100
+ version: "1"
101
+ siteId: "site_example"
102
+ framework: astro # astro | next
103
+
104
+ source:
105
+ root: "./"
106
+ outDir: "dist" # Astro only
107
+ publicDir: "public" # Astro only
108
+
109
+ componentRegistry: ./src/cms/contracts/components/index.ts
110
+ layoutRegistry: ./src/cms/contracts/layouts/index.ts
111
+
112
+ capabilities:
113
+ staticRender: true
114
+ ssr: true
115
+ ssrIslands: true
116
+ clientIslands: true
117
+ fragmentRender: true
118
+
119
+ routes:
120
+ cmsManaged: "/[...slug]"
121
+ frameworkOwned:
122
+ - "/api/*"
123
+ - "/_next/*" # Next.js
124
+ - "/.gradial-dam/*"
125
+
126
+ rendererProtocol: stdio-json
127
+ ```
128
+
129
+ ### Fields
130
+
131
+ | Field | Purpose |
132
+ |-------|---------|
133
+ | `version` | Config schema version |
134
+ | `siteId` | Unique site identifier |
135
+ | `framework` | `astro` or `next` (SvelteKit removed) |
136
+ | `source.root` | Source root directory |
137
+ | `source.outDir` | Build output directory (Astro) |
138
+ | `source.publicDir` | Public assets directory (Astro) |
139
+ | `componentRegistry` | Path to component contract barrel |
140
+ | `layoutRegistry` | Path to layout contract barrel |
141
+ | `capabilities` | Render capabilities (static, SSR, islands, fragments) |
142
+ | `routes.cmsManaged` | Catch-all route pattern for CMS pages |
143
+ | `routes.frameworkOwned` | Route patterns excluded from CMS routing |
144
+ | `rendererProtocol` | Renderer communication protocol |
145
+
146
+ ### Removed fields
147
+
148
+ - `rendererEntry` has been removed from `.aci.yaml`. Do not include it.
149
+
150
+ ### Rules
151
+
152
+ - `componentRegistry` and `layoutRegistry` point to contract-only modules.
153
+ - No secrets in `.aci.yaml`, docs, fixtures, or generated files.
@@ -0,0 +1,171 @@
1
+ # Design System Reference
2
+
3
+ ## Source of Truth
4
+
5
+ Design tokens are CSS custom properties defined in `@theme` blocks under
6
+ `src/design-system/tokens/`. Tailwind v4 reads them and auto-generates
7
+ utility classes.
8
+
9
+ | File | Scope |
10
+ |------|-------|
11
+ | `colors.css` | Brand colors, semantic tokens, surfaces, borders |
12
+ | `typography.css` | Font families, sizes, weights, line heights |
13
+ | `spacing.css` | Spacing scale, container padding, section spacing |
14
+ | `radii.css` | Border radius values |
15
+ | `shadows.css` | Card and overlay shadows |
16
+ | `components.css` | Component-level tokens (buttons, header, footer) |
17
+ | `index.css` | Barrel import for all token files |
18
+
19
+ ## Using Design Tokens
20
+
21
+ ### Direct Tailwind utilities (preferred)
22
+
23
+ Tailwind v4 derives utilities from `@theme` tokens. Use these directly:
24
+
25
+ - **Color**: `text-fg`, `text-fg-muted`, `bg-surface-default`, `bg-page-inverse`, `border-edge`
26
+ - **Radii**: `rounded-card`, `rounded-button`
27
+ - **Shadows**: `shadow-card-default`
28
+
29
+ Standard spacing/sizing utilities (`px-4`, `gap-6`, `w-24`, `max-w-3xl`)
30
+ are safe because Tailwind v4 derives its spacing scale from `--spacing`.
31
+
32
+ Layout utilities (`items-center`, `overflow-hidden`, `aspect-video`,
33
+ `uppercase`, `text-lg`, breakpoint prefixes like `lg:`) are all fine.
34
+
35
+ ### Bracket syntax (for tokens without direct utilities)
36
+
37
+ For values without a direct Tailwind utility mapping — gradients,
38
+ typography tokens, complex custom properties — use `var(--...)` bracket
39
+ syntax:
40
+
41
+ - **Gradients**: `bg-[image:var(--gradient-cta-dark)]`, `bg-[image:var(--gradient-hero-overlay)]`
42
+ - **Typography**: `text-[length:var(--text-size-body)]`, `font-[var(--font-weight-semibold)]`, `[font-family:var(--font-family-heading)]`, `leading-[var(--leading-heading)]`
43
+ - **Spacing tokens**: `py-[var(--spacing-section-y)]`, `p-[var(--spacing-card)]`
44
+
45
+ ## Forbidden Values
46
+
47
+ **No raw hex colors**: `text-[#8b72ff]`, `bg-[#f6f6f7]`
48
+ **No raw pixel values**: `p-[12px]`, `w-[200px]`, `gap-[24px]`
49
+ **No raw rem values**: `text-[1.25rem]`, `p-[0.75rem]`
50
+ **No Tailwind named colors**: `text-gray-500`, `bg-purple-400`
51
+
52
+ Use CSS variable tokens instead. If a token doesn't exist for a value you
53
+ need, add it to the appropriate `tokens/*.css` file.
54
+
55
+ The ESLint rule `no-raw-tailwind-values` enforces this in component code.
56
+ Raw values are allowed only inside `src/design-system/tokens/` and
57
+ `src/design-system/styles.css`.
58
+
59
+ ## Component CSS Discipline
60
+
61
+ - Use Tailwind classes for layout, spacing, responsive behavior, display,
62
+ flex/grid, sizing, and positioning.
63
+ - Use CSS custom properties from the design system for colors, surfaces,
64
+ borders, typography, font sizes, radii, shadows, gradients, spacing
65
+ scales, and theme behavior.
66
+ - Do not hard-code color, font family, or font size values in component
67
+ markup when a design token can express the intent.
68
+ - Do not create component-specific text color tokens when contextual tokens
69
+ are enough. Prefer `--color-text-default`, `--color-text-default-muted`,
70
+ `--color-surface-default`, `--color-border-default`.
71
+ - Keep button variant tokens explicit for contrast-sensitive states.
72
+
73
+ ## Theme System
74
+
75
+ Three theme classes control semantic token resolution:
76
+
77
+ | Class | Use Case |
78
+ |-------|----------|
79
+ | `theme-auto` | Normal page content following browser light/dark preference |
80
+ | `theme-dark` | Fixed dark surfaces (header, footer, hero) |
81
+ | `theme-accent` | CTA-style accent surfaces |
82
+
83
+ Theme classes override semantic tokens:
84
+
85
+ ```css
86
+ .theme-dark {
87
+ --color-fg: var(--color-fg-inverse);
88
+ --color-surface-default: var(--color-page-inverse);
89
+ --color-edge: var(--color-edge-inverse);
90
+ /* ... */
91
+ }
92
+ ```
93
+
94
+ Apply theme classes to wrapper elements:
95
+
96
+ ```html
97
+ <div class="theme-dark">
98
+ <p class="text-fg">This text uses the dark theme fg color.</p>
99
+ </div>
100
+ ```
101
+
102
+ ### Chrome-Specific Override Scopes
103
+
104
+ Header, footer, and mobile nav have dedicated class scopes for
105
+ self-contained chrome theming:
106
+
107
+ | Scope | Purpose |
108
+ |-------|---------|
109
+ | `.bm-header` | Header background, text, borders, button overrides |
110
+ | `.bm-footer` | Footer background, text, borders, button overrides |
111
+ | `.bm-mobile-nav` | Mobile nav background, text, borders |
112
+
113
+ These scopes set their own token values independently of the theme system.
114
+ Use them to override chrome appearance without leaking overrides into page
115
+ content.
116
+
117
+ ## Grid Layout
118
+
119
+ Use a 12-column grid mental model for layout sections. Grid components
120
+ that render a variable number of children must compute column classes
121
+ dynamically:
122
+
123
+ | Items | Columns | Responsive Pattern |
124
+ |-------|---------|-------------------|
125
+ | 1 | 12 | full width |
126
+ | 2 | 6 each | `sm:grid-cols-2` |
127
+ | 3 | 4 each | `sm:grid-cols-2 lg:grid-cols-3` |
128
+ | 4 | 3 each | `sm:grid-cols-2 lg:grid-cols-4` |
129
+
130
+ ### Critical: Tailwind Purge Safety
131
+
132
+ Tailwind purges unused CSS classes at build time. Dynamic column classes
133
+ must appear as **complete string literals** in source code — not assembled
134
+ via string interpolation.
135
+
136
+ **Bad** (class is purged, no CSS generated):
137
+ ```tsx
138
+ <div className={`grid-cols-${columns}`}>
139
+ ```
140
+
141
+ **Good** (complete literal, survives purge):
142
+ ```tsx
143
+ const gridClasses: Record<number, string> = {
144
+ 1: 'grid-cols-1',
145
+ 2: 'sm:grid-cols-2',
146
+ 3: 'sm:grid-cols-2 lg:grid-cols-3',
147
+ 4: 'sm:grid-cols-2 lg:grid-cols-4',
148
+ };
149
+ <div className={gridClasses[columns]}>
150
+ ```
151
+
152
+ This applies to all dynamic Tailwind classes, not just grid columns. Any
153
+ class that is conditionally applied must be a complete literal visible to
154
+ the Tailwind scanner.
155
+
156
+ ## Adding New Tokens
157
+
158
+ When a source value doesn't map to an existing token:
159
+
160
+ 1. Add the token to the appropriate `tokens/*.css` file inside `@theme`:
161
+ ```css
162
+ @theme {
163
+ --color-brand-500: #6b5ce7;
164
+ }
165
+ ```
166
+ 2. Reference it from component code:
167
+ ```html
168
+ <div class="text-[var(--color-brand-500)]">
169
+ ```
170
+ 3. Tailwind v4 will auto-generate a utility if the token name matches a
171
+ Tailwind namespace (e.g. `--color-*` generates `text-*`, `bg-*`).
@@ -0,0 +1,73 @@
1
+ # Image Slots and Assets
2
+
3
+ ## ImageSchema
4
+
5
+ Use `ImageSchema` for DAM-managed image fields in contracts:
6
+
7
+ ```ts
8
+ import { ImageSchema } from '@gradial/aci';
9
+ import { z } from 'zod';
10
+
11
+ export const schema = z.object({
12
+ image: ImageSchema,
13
+ cards: z.array(z.object({
14
+ image: ImageSchema.optional(),
15
+ })),
16
+ });
17
+ ```
18
+
19
+ ## Image Slots Configuration
20
+
21
+ Declare `imageSlots` matching every `ImageSchema` field, including nested array
22
+ paths:
23
+
24
+ ```ts
25
+ export const contract = defineComponentContract({
26
+ name: 'cards',
27
+ schema,
28
+ imageSlots: {
29
+ image: {
30
+ formats: ['image/webp'],
31
+ sizes: '100vw',
32
+ outputs: [{ aspectRatio: '16:9', widths: [960, 1280] }],
33
+ },
34
+ 'cards.image': {
35
+ formats: ['image/webp'],
36
+ sizes: '50vw',
37
+ outputs: [{ aspectRatio: '1:1', widths: [320, 640] }],
38
+ },
39
+ },
40
+ });
41
+ ```
42
+
43
+ ## Runtime Image Rendering Rules
44
+
45
+ - Read image values from CMS props, never hard-code.
46
+ - Render Gradial images as `<picture>` when sources are present.
47
+ - Use `primaryImageSource(image).src` from `@gradial/aci` to get the primary URL.
48
+ - Preserve `width`/`height` from the primary source to prevent layout shift.
49
+ - For CSS backgrounds, set a CSS custom property from the content asset URL.
50
+ - Treat image URLs as opaque props — do not synthesize placeholder images
51
+ during code artifact builds.
52
+
53
+ > **API note:** The schema helper is named `ImageSchema` (not
54
+ > `GradialImageSchema`). The runtime helper is `primaryImageSource()`.
55
+ > Use `primaryImageSource(image).src` for the primary URL.
56
+
57
+ ## Content Image References
58
+
59
+ In content JSON, use DAM asset references:
60
+
61
+ ```json
62
+ {
63
+ "image": {
64
+ "$type": "dam.assetRef",
65
+ "assetId": "ast_site_home_hero",
66
+ "alt": "Hero illustration"
67
+ }
68
+ }
69
+ ```
70
+
71
+ The compiler resolves these to Gradial image objects with `sources`, `alt`,
72
+ `assetId`, and `versionId`. Each source carries `src`, `width`, `height`, and
73
+ `type`.