@gradial/aci 0.1.20-preview.2 → 0.1.20

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 (249) hide show
  1. package/README.md +48 -110
  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 +14 -3
  7. package/dist/astro/index.js +24 -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 +2 -0
  11. package/dist/content/index.js +2 -0
  12. package/dist/content/provider.d.ts +19 -0
  13. package/dist/content/provider.js +7 -0
  14. package/dist/content/resolve-slots.d.ts +9 -0
  15. package/dist/content/resolve-slots.js +24 -0
  16. package/dist/content/types.d.ts +1 -1
  17. package/dist/define-component.d.ts +1 -2
  18. package/dist/define-component.js +73 -11
  19. package/dist/define-layout.d.ts +2 -0
  20. package/dist/define-layout.js +3 -0
  21. package/dist/dev/content-watch.d.ts +37 -0
  22. package/dist/dev/content-watch.js +154 -0
  23. package/dist/dev/index.d.ts +24 -43
  24. package/dist/dev/index.js +4 -169
  25. package/dist/index.d.ts +14 -5
  26. package/dist/index.js +8 -8
  27. package/dist/next/asset-route.d.ts +2 -0
  28. package/dist/next/asset-route.js +12 -4
  29. package/dist/next/config.d.ts +2 -2
  30. package/dist/next/config.js +6 -6
  31. package/dist/next/content-watch.js +6 -1
  32. package/dist/next/index.d.ts +2 -2
  33. package/dist/next/index.js +2 -2
  34. package/dist/next/middleware.d.ts +7 -3
  35. package/dist/next/middleware.js +12 -7
  36. package/dist/next/page.d.ts +16 -15
  37. package/dist/next/page.js +30 -10
  38. package/dist/next/render.d.ts +5 -0
  39. package/dist/next/render.js +17 -0
  40. package/dist/next/root-layout.d.ts +4 -0
  41. package/dist/next/root-layout.js +5 -0
  42. package/dist/next/server.d.ts +14 -13
  43. package/dist/next/server.js +13 -3
  44. package/dist/providers/file.d.ts +2 -0
  45. package/dist/providers/file.js +61 -1
  46. package/dist/providers/s3.d.ts +1 -0
  47. package/dist/providers/s3.js +14 -1
  48. package/dist/react/Media.d.ts +24 -0
  49. package/dist/react/Media.js +30 -0
  50. package/dist/react/Picture.d.ts +12 -0
  51. package/dist/react/Picture.js +11 -0
  52. package/dist/react/VideoPlayer.d.ts +13 -0
  53. package/dist/react/{GradialVideoPlayer.js → VideoPlayer.js} +6 -5
  54. package/dist/react/index.d.ts +333 -3
  55. package/dist/react/index.js +89 -3
  56. package/dist/registry.d.ts +15 -0
  57. package/dist/registry.js +10 -0
  58. package/dist/testing/index.d.ts +3 -0
  59. package/dist/testing/index.js +15 -2
  60. package/dist/types/block-ref.d.ts +26 -0
  61. package/dist/types/block-ref.js +28 -0
  62. package/dist/types/component.d.ts +36 -41
  63. package/dist/types/config.d.ts +1 -2
  64. package/dist/types/image.d.ts +33 -20
  65. package/dist/types/image.js +42 -50
  66. package/dist/types/index.d.ts +1 -0
  67. package/dist/types/index.js +1 -0
  68. package/dist/types/link.d.ts +38 -0
  69. package/dist/types/link.js +58 -0
  70. package/dist/types/media.d.ts +16 -35
  71. package/dist/types/media.js +15 -84
  72. package/dist/types/page.d.ts +1 -2
  73. package/dist/types/render-mode.d.ts +0 -1
  74. package/dist/types/renderer.d.ts +1 -1
  75. package/dist/types/rich-text.d.ts +40 -0
  76. package/dist/types/rich-text.js +39 -0
  77. package/dist/types/video.d.ts +7 -13
  78. package/dist/types/video.js +3 -3
  79. package/package.json +32 -74
  80. package/src/cli/compile-registry.mjs +236 -59
  81. package/src/cli/validate-content.mjs +38 -75
  82. package/src/types/image.ts +79 -60
  83. package/templates/astro/template/.aci.yaml +27 -0
  84. package/templates/astro/template/.storybook/main.ts +79 -0
  85. package/templates/astro/template/.storybook/manager.ts +6 -0
  86. package/templates/astro/template/.storybook/preview.ts +33 -0
  87. package/templates/astro/template/astro.config.mjs +37 -0
  88. package/templates/astro/template/package.json.tmpl +41 -0
  89. package/templates/astro/template/src/cms/contracts/layouts/index.ts +18 -0
  90. package/templates/astro/template/src/cms/registry.ts +28 -0
  91. package/templates/astro/template/src/components/chrome/Footer.astro +107 -0
  92. package/templates/astro/template/src/components/chrome/Header.astro +94 -0
  93. package/templates/astro/template/src/components/elements/AtomicBlock.astro +15 -0
  94. package/templates/astro/template/src/components/elements/AtomicBlocks.astro +16 -0
  95. package/templates/astro/template/src/components/elements/Button.astro +51 -0
  96. package/templates/astro/template/src/components/elements/ButtonBlock.astro +25 -0
  97. package/templates/astro/template/src/components/elements/Container.astro +11 -0
  98. package/templates/astro/template/src/components/elements/Icon.astro +31 -0
  99. package/templates/astro/template/src/components/elements/Logo.astro +29 -0
  100. package/templates/astro/template/src/components/elements/RemoteImage.astro +41 -0
  101. package/templates/astro/template/src/components/sections/ContainerSection.astro +32 -0
  102. package/templates/astro/template/src/components/sections/FeatureCard.astro +23 -0
  103. package/templates/astro/template/src/components/sections/FeatureGrid.astro +35 -0
  104. package/templates/astro/template/src/components/sections/HomeHero.astro +51 -0
  105. package/templates/astro/template/src/components/sections/StatsBar.astro +33 -0
  106. package/templates/astro/template/src/design-system/styles.css +399 -0
  107. package/templates/astro/template/src/design-system/tokens/colors.css +60 -0
  108. package/templates/astro/template/src/design-system/tokens/components.css +69 -0
  109. package/templates/astro/template/src/design-system/tokens/index.css +6 -0
  110. package/templates/astro/template/src/design-system/tokens/radii.css +5 -0
  111. package/templates/astro/template/src/design-system/tokens/shadows.css +5 -0
  112. package/templates/astro/template/src/design-system/tokens/spacing.css +13 -0
  113. package/templates/astro/template/src/design-system/tokens/typography.css +24 -0
  114. package/templates/astro/template/src/env.d.ts +1 -0
  115. package/templates/astro/template/src/fixtures/atomicBlocks.ts +33 -0
  116. package/templates/astro/template/src/fixtures/composition.ts +31 -0
  117. package/templates/astro/template/src/fixtures/index.ts +17 -0
  118. package/templates/astro/template/src/fixtures/sections.ts +39 -0
  119. package/templates/astro/template/src/layouts/SiteLayout.astro +30 -0
  120. package/templates/astro/template/src/pages/[...slug].astro +61 -0
  121. package/templates/astro/template/src/render/RenderPage.astro +66 -0
  122. package/templates/astro/template/src/stories/CompositionStoryFrame.astro +15 -0
  123. package/templates/astro/template/src/stories/HomeHero.stories.ts +24 -0
  124. package/templates/astro/template/src/stories/SectionStoryFrame.astro +16 -0
  125. package/templates/astro/template/src/stories/StoryDocFrame.astro +31 -0
  126. package/templates/astro/template/tsconfig.json +11 -0
  127. package/templates/common/.agents/AGENTS.md +37 -0
  128. package/templates/common/.agents/skills/aci/SKILL.md +162 -0
  129. package/templates/common/.agents/skills/authoring/SKILL.md +590 -0
  130. package/templates/common/.agents/skills/authoring/references/block-registry.md +151 -0
  131. package/templates/common/.agents/skills/authoring/references/contracts.md +153 -0
  132. package/templates/common/.agents/skills/authoring/references/design-system.md +171 -0
  133. package/templates/common/.agents/skills/authoring/references/image-assets.md +73 -0
  134. package/templates/common/.agents/skills/authoring/references/repo-structure.md +157 -0
  135. package/templates/common/.agents/skills/migration/SKILL.md +160 -0
  136. package/templates/common/.agents/skills/migration/references/analyzing-source.md +215 -0
  137. package/templates/common/.agents/skills/migration/references/component-migration.md +200 -0
  138. package/templates/common/.agents/skills/migration/references/selector-map.md +31 -0
  139. package/templates/common/.content/.gradial-dam/blobs/sha256/7f/bf/7fbfc7fe230ff12688c496f660119e41d9725c4504d82e310c15665c73c09292 +0 -0
  140. package/templates/common/.content/.gradial-dam/blobs/sha256/a0/f5/a0f506689de9b15a7055f70103a3f02cd3ba105771ae2de6754df2ede1210d3a +0 -0
  141. package/templates/common/.content/.gradial-dam/blobs/sha256/dd/b7/ddb7e9642be08317bcd4b2bd26b58ba8d0d34dd7239e03e684a4f3634fa3845b +0 -0
  142. package/templates/common/.content/.gradial-dam/blobs/sha256/fb/32/fb3260e8286ea8231ea1c77b9c0be92d6edd9026565731eb14cfdcfd6b1b75b3 +0 -0
  143. package/templates/common/.content/config/site.json +192 -0
  144. package/templates/common/.content/dam/folders/seed/ast_site_about_hero/asset.json +9 -0
  145. package/templates/common/.content/dam/folders/seed/ast_site_about_hero/editorial.json +14 -0
  146. package/templates/common/.content/dam/folders/seed/ast_site_about_hero/governance.json +23 -0
  147. package/templates/common/.content/dam/folders/seed/ast_site_about_hero/versions/v0001.json +37 -0
  148. package/templates/common/.content/dam/folders/seed/ast_site_home_hero/asset.json +9 -0
  149. package/templates/common/.content/dam/folders/seed/ast_site_home_hero/editorial.json +14 -0
  150. package/templates/common/.content/dam/folders/seed/ast_site_home_hero/governance.json +23 -0
  151. package/templates/common/.content/dam/folders/seed/ast_site_home_hero/versions/v0001.json +37 -0
  152. package/templates/common/.content/fragments/navbar/_index.json +25 -0
  153. package/templates/common/.content/fragments/site-footer/_index.json +62 -0
  154. package/templates/common/.content/pages/about/_index.json +51 -0
  155. package/templates/common/.content/pages/home/_index.json +112 -0
  156. package/templates/common/.content/schemas/site.schema.json +123 -0
  157. package/templates/common/README.md.tmpl +45 -0
  158. package/templates/common/contracts/components/button.contract.ts +19 -0
  159. package/templates/common/contracts/components/container.contract.ts +19 -0
  160. package/templates/common/contracts/components/featureCard.contract.ts +15 -0
  161. package/templates/common/contracts/components/featureGrid.contract.ts +16 -0
  162. package/templates/common/contracts/components/homeHero.contract.ts +36 -0
  163. package/templates/common/contracts/components/index.ts +21 -0
  164. package/templates/common/contracts/components/siteFooter.contract.ts +45 -0
  165. package/templates/common/contracts/components/siteNavigation.contract.ts +30 -0
  166. package/templates/common/contracts/components/statsBar.contract.ts +19 -0
  167. package/templates/common/design-system/styles.css +346 -0
  168. package/templates/common/design-system/tokens/colors.css +110 -0
  169. package/templates/common/design-system/tokens/components.css +86 -0
  170. package/templates/common/design-system/tokens/index.css +6 -0
  171. package/templates/common/design-system/tokens/radii.css +5 -0
  172. package/templates/common/design-system/tokens/shadows.css +9 -0
  173. package/templates/common/design-system/tokens/spacing.css +26 -0
  174. package/templates/common/design-system/tokens/typography.css +32 -0
  175. package/templates/common/eslint-rules/no-raw-tailwind-values.js +127 -0
  176. package/templates/common/public/assets/Dwire-model.png +0 -0
  177. package/templates/common/public/assets/bare-metal-logo.png +0 -0
  178. package/templates/common/public/assets/meridian-logo.svg +8 -0
  179. package/templates/common/public/assets/seattle.avif +0 -0
  180. package/templates/nextjs/template/.aci.yaml +25 -0
  181. package/templates/nextjs/template/.storybook/main.ts +13 -0
  182. package/templates/nextjs/template/.storybook/preview.ts +34 -0
  183. package/templates/nextjs/template/eslint.config.mjs +36 -0
  184. package/templates/nextjs/template/next-env.d.ts +6 -0
  185. package/templates/nextjs/template/next.config.ts +8 -0
  186. package/templates/nextjs/template/package.json.tmpl +46 -0
  187. package/templates/nextjs/template/postcss.config.mjs +7 -0
  188. package/templates/nextjs/template/src/app/[[...slug]]/page.tsx +8 -0
  189. package/templates/nextjs/template/src/app/gradial/assets/[releaseId]/[...path]/route.ts +31 -0
  190. package/templates/nextjs/template/src/app/layout.tsx +29 -0
  191. package/templates/nextjs/template/src/cms/contracts/components/button.contract.ts +19 -0
  192. package/templates/nextjs/template/src/cms/contracts/components/container.contract.ts +20 -0
  193. package/templates/nextjs/template/src/cms/contracts/components/homeHero.contract.ts +36 -0
  194. package/templates/nextjs/template/src/cms/contracts/components/index.ts +15 -0
  195. package/templates/nextjs/template/src/cms/contracts/components/siteFooter.contract.ts +45 -0
  196. package/templates/nextjs/template/src/cms/contracts/components/siteNavigation.contract.ts +30 -0
  197. package/templates/nextjs/template/src/cms/contracts/layouts/index.ts +16 -0
  198. package/templates/nextjs/template/src/cms/registry.ts +28 -0
  199. package/templates/nextjs/template/src/components/chrome/Footer.tsx +86 -0
  200. package/templates/nextjs/template/src/components/chrome/Header.tsx +74 -0
  201. package/templates/nextjs/template/src/components/elements/AtomicBlock.tsx +13 -0
  202. package/templates/nextjs/template/src/components/elements/Button.tsx +40 -0
  203. package/templates/nextjs/template/src/components/elements/ButtonBlock.tsx +18 -0
  204. package/templates/nextjs/template/src/components/elements/Container.tsx +14 -0
  205. package/templates/nextjs/template/src/components/elements/Icon.tsx +29 -0
  206. package/templates/nextjs/template/src/components/elements/Logo.tsx +38 -0
  207. package/templates/nextjs/template/src/components/sections/ContainerSection.tsx +28 -0
  208. package/templates/nextjs/template/src/components/sections/FeatureCard.tsx +25 -0
  209. package/templates/nextjs/template/src/components/sections/FeatureGrid.tsx +36 -0
  210. package/templates/nextjs/template/src/components/sections/HomeHero.tsx +62 -0
  211. package/templates/nextjs/template/src/components/sections/StatsBar.tsx +30 -0
  212. package/templates/nextjs/template/src/middleware.ts +4 -0
  213. package/templates/nextjs/template/src/stories/Button.stories.tsx +38 -0
  214. package/templates/nextjs/template/src/stories/Header.stories.tsx +30 -0
  215. package/templates/nextjs/template/src/stories/HomeHero.stories.tsx +33 -0
  216. package/templates/nextjs/template/tsconfig.json +23 -0
  217. package/templates/nextjs/template/vercel.json +6 -0
  218. package/dist/content/cache.test.d.ts +0 -1
  219. package/dist/content/cache.test.js +0 -86
  220. package/dist/content/contract.d.ts +0 -83
  221. package/dist/content/contract.js +0 -104
  222. package/dist/content/tailwind-validator.d.ts +0 -6
  223. package/dist/content/tailwind-validator.js +0 -31
  224. package/dist/content/validation.d.ts +0 -108
  225. package/dist/content/validation.js +0 -184
  226. package/dist/next/preview-banner.d.ts +0 -4
  227. package/dist/next/preview-banner.js +0 -51
  228. package/dist/next/preview.d.ts +0 -7
  229. package/dist/next/preview.js +0 -37
  230. package/dist/react/GradialMedia.d.ts +0 -24
  231. package/dist/react/GradialMedia.js +0 -31
  232. package/dist/react/GradialPicture.d.ts +0 -14
  233. package/dist/react/GradialPicture.js +0 -30
  234. package/dist/react/GradialVideoPlayer.d.ts +0 -13
  235. package/dist/sveltekit/index.d.ts +0 -23
  236. package/dist/sveltekit/index.js +0 -62
  237. package/dist/sveltekit/preview.d.ts +0 -5
  238. package/dist/sveltekit/preview.js +0 -33
  239. package/src/cli/verify-renderer.mjs +0 -73
  240. package/src/types/component.ts +0 -59
  241. package/src/types/config.ts +0 -38
  242. package/src/types/data.ts +0 -47
  243. package/src/types/index.ts +0 -10
  244. package/src/types/layout.ts +0 -29
  245. package/src/types/media.ts +0 -125
  246. package/src/types/page.ts +0 -48
  247. package/src/types/render-mode.ts +0 -18
  248. package/src/types/renderer.ts +0 -83
  249. package/src/types/video.ts +0 -66
@@ -0,0 +1,590 @@
1
+ ---
2
+ name: authoring
3
+ description: Author content and build components for ACI sites. Use when creating or editing content JSON, adding components, writing contracts, wiring the component registry, working with design tokens, building Storybook stories, or understanding the contract/runtime split.
4
+ ---
5
+
6
+ # Authoring — Content and Components
7
+
8
+ ## Content Authoring
9
+
10
+ ### Content Locations
11
+
12
+ | Path | Purpose |
13
+ |------|---------|
14
+ | `.content/config/site.json` | Site metadata, SEO defaults |
15
+ | `.content/pages/{slug}/_index.json` | Page content (slug maps to route) |
16
+ | `.content/fragments/{id}/_index.json` | Shared fragments (navbar, footer — used by layout defaults) |
17
+ | `.aci/schemas/page.schema.json` | Page validation schema (auto-generated from contracts) |
18
+
19
+ ### Page Structure
20
+
21
+ Every page JSON requires:
22
+
23
+ ```json
24
+ {
25
+ "$type": "page",
26
+ "id": "unique-page-id",
27
+ "status": "published",
28
+ "layout": "default",
29
+ "metadata": {
30
+ "title": "Page Title",
31
+ "description": "Meta description"
32
+ },
33
+ "regions": {
34
+ "main": []
35
+ }
36
+ }
37
+ ```
38
+
39
+ The `layout` field is required — it determines which slots the page can
40
+ fill. The `default` layout provides `header`/`main`/`footer` slots with
41
+ fragment defaults for chrome (navbar, footer). Pages typically only fill
42
+ `regions.main`; chrome comes from the layout's fragment defaults.
43
+
44
+ ### Route Mapping
45
+
46
+ Pages map to URL routes by their folder path under `/pages/`:
47
+
48
+ | Page Path | Route |
49
+ |-----------|-------|
50
+ | `/pages/home/_index.json` | `/` |
51
+ | `/pages/about/_index.json` | `/about` |
52
+ | `/pages/products/_index.json` | `/products` |
53
+ | `/pages/products/shoes/_index.json` | `/products/shoes` |
54
+
55
+ The `home` slug is special-cased to route `/`. All other slugs map directly.
56
+
57
+ ### Content Rules
58
+
59
+ - Never hard-code content inside component code. All content lives in JSON.
60
+ - Migrate copy verbatim. Do not paraphrase, summarize, or invent.
61
+ - Global elements (header, footer, nav) are CMS-managed fragments in
62
+ `.content/fragments/`, rendered via layout fragment defaults. They are
63
+ not hardcoded in framework layout files.
64
+ - Use `$ref:` prefixes for cross-references between content documents.
65
+
66
+ ### Block Structure
67
+
68
+ ```json
69
+ {
70
+ "id": "hero-section",
71
+ "component": "home_hero",
72
+ "props": {
73
+ "headline": "Ship content updates across every framework.",
74
+ "ctaLabel": "Get started",
75
+ "ctaHref": "/docs"
76
+ }
77
+ }
78
+ ```
79
+
80
+ - `component` must match a contract `id` exactly.
81
+ - `props` must validate against the contract's Zod schema.
82
+ - `id` is unique within the page.
83
+
84
+ ### Nested Blocks
85
+
86
+ Components that accept child blocks use `blockRef()` in their Zod schema
87
+ instead of a separate `slots` property. Nested blocks live inside `props`,
88
+ not in a sibling `slots` field.
89
+
90
+ **Contract:**
91
+ ```ts
92
+ import { defineComponentContract, blockRef } from '@gradial/aci';
93
+ import { z } from 'zod';
94
+
95
+ export const schema = z.object({
96
+ theme: z.enum(['auto', 'light', 'dark']).optional(),
97
+ className: z.string().optional(),
98
+ content: blockRef({
99
+ allowed: ['button', 'feature_grid', 'stats_bar'],
100
+ multiple: true,
101
+ }).optional(),
102
+ });
103
+
104
+ export const containerContract = defineComponentContract({
105
+ id: 'container',
106
+ props: schema,
107
+ });
108
+ ```
109
+
110
+ **Content JSON:**
111
+ ```json
112
+ {
113
+ "id": "cta-section",
114
+ "component": "container",
115
+ "props": {
116
+ "className": "space-y-4",
117
+ "content": [
118
+ {
119
+ "id": "primary-cta",
120
+ "component": "button",
121
+ "props": {
122
+ "label": "Get started",
123
+ "href": "#start",
124
+ "variant": "primary"
125
+ }
126
+ },
127
+ {
128
+ "id": "secondary-cta",
129
+ "component": "button",
130
+ "props": {
131
+ "label": "Learn more",
132
+ "href": "#learn",
133
+ "variant": "secondary"
134
+ }
135
+ }
136
+ ]
137
+ }
138
+ }
139
+ ```
140
+
141
+ The `blockRef()` function:
142
+ - Takes `{ allowed: string[], multiple?: boolean }`.
143
+ - `allowed` restricts which component IDs can be nested.
144
+ - `multiple: true` (default) allows an array of blocks.
145
+ - The compiler scans the schema for `blockRef` fields and auto-discovers
146
+ block slots — no separate `blockSlots` declaration needed.
147
+
148
+ ### Button Block
149
+
150
+ The `button` component is a common atomic block:
151
+
152
+ ```json
153
+ {
154
+ "id": "cta-button",
155
+ "component": "button",
156
+ "props": {
157
+ "label": "Get started",
158
+ "href": "https://example.com",
159
+ "variant": "primary",
160
+ "icon": "arrow_forward",
161
+ "iconPosition": "after",
162
+ "newWindow": true
163
+ }
164
+ }
165
+ ```
166
+
167
+ - `label` and `href` are required.
168
+ - `variant`: `primary` | `secondary` | `outline` | `tertiary`
169
+ - `icon`: a registered icon name.
170
+ - `iconPosition`: `before` | `after`
171
+ - `newWindow`: boolean for `target="_blank"`.
172
+
173
+ ### Site Config
174
+
175
+ Global elements (navigation, footer, branding, SEO) live in
176
+ `.content/config/site.json`:
177
+
178
+ ```json
179
+ {
180
+ "$type": "site",
181
+ "id": "meridian",
182
+ "status": "published",
183
+ "title": "Meridian",
184
+ "domain": "www.meridian.local",
185
+ "defaultLocale": "en-us",
186
+ "locales": ["en-us"],
187
+ "brandHref": "/",
188
+ "brandLogo": {
189
+ "src": "/assets/meridian-logo.svg",
190
+ "alt": "Meridian"
191
+ },
192
+ "seo": {
193
+ "title": "Meridian",
194
+ "description": "Global satellite internet from anywhere on Earth.",
195
+ "siteName": "Meridian"
196
+ },
197
+ "navigation": [
198
+ { "id": "home", "label": "Home", "href": "/" },
199
+ { "id": "about", "label": "About", "href": "/about/" }
200
+ ],
201
+ "footer": {
202
+ "columns": [...],
203
+ "social": [...],
204
+ "legalLinks": [...],
205
+ "copyright": "Copyright (c) 2026 ..."
206
+ }
207
+ }
208
+ ```
209
+
210
+ ### ID Conventions
211
+
212
+ - Every block must have a unique `id` within the page.
213
+ - Use kebab-case: `hero-heading`, `feature-cta`, `card-1-image`.
214
+ - Prefix with section context for clarity: `callouts-heading`, `demo-cta`.
215
+
216
+ ### className Validation
217
+
218
+ The `className` field accepts any valid Tailwind CSS utility class, as
219
+ long as every value ultimately derives from the design system's CSS custom
220
+ properties. The validator rejects raw hex/px/rem bracket values (e.g.,
221
+ `[#fff]`, `[12px]`) but allows `[var(--...)]` bracket syntax for CSS
222
+ variable references.
223
+
224
+ ### Image References
225
+
226
+ Use DAM asset references for images:
227
+
228
+ ```json
229
+ {
230
+ "image": {
231
+ "$type": "dam.assetRef",
232
+ "assetId": "ast_site_home_hero",
233
+ "alt": "Hero illustration"
234
+ }
235
+ }
236
+ ```
237
+
238
+ The compiler resolves these to Gradial image objects with primary URLs,
239
+ sources, and dimensions.
240
+
241
+ ## Component Development
242
+
243
+ ### Contract / Runtime Split
244
+
245
+ Every CMS-managed component has two parts:
246
+
247
+ 1. **Contract** (`src/cms/contracts/components/{name}.contract.ts`) —
248
+ compile-time metadata: component name, Zod schema, render modes, image
249
+ slots. Imports only `@gradial/aci` and `zod`.
250
+
251
+ 2. **Runtime** (`src/components/`) — the actual framework component
252
+ (Astro or React). May import contract types for prop typing.
253
+
254
+ The ACI compiler imports only contracts. Contracts must never import
255
+ framework components, CSS, browser globals, or runtime modules.
256
+
257
+ Only Next.js and Astro are supported. SvelteKit support has been removed.
258
+
259
+ ### Adding a New Component
260
+
261
+ #### 1. Create the contract
262
+
263
+ ```ts
264
+ // src/cms/contracts/components/featureShowcase.contract.ts
265
+ import { defineComponentContract, ImageSchema } from '@gradial/aci';
266
+ import { z } from 'zod';
267
+
268
+ export const schema = z.object({
269
+ headline: z.string().min(1),
270
+ description: z.string().optional(),
271
+ image: ImageSchema,
272
+ features: z.array(z.object({
273
+ title: z.string().min(1),
274
+ description: z.string().min(1),
275
+ })).optional(),
276
+ ctaLabel: z.string().optional(),
277
+ ctaHref: z.string().optional(),
278
+ });
279
+
280
+ export type ContentProps = z.infer<typeof schema>;
281
+
282
+ export const featureShowcaseContract = defineComponentContract({
283
+ id: 'feature_showcase',
284
+ props: schema,
285
+ imageSlots: {
286
+ image: {
287
+ formats: ['image/avif', 'image/webp', 'image/jpeg'],
288
+ sizes: '50vw',
289
+ outputs: [
290
+ { aspectRatio: '16:9', widths: [640, 960, 1280] },
291
+ ],
292
+ },
293
+ },
294
+ });
295
+ ```
296
+
297
+ #### 2. Export from the barrel
298
+
299
+ ```ts
300
+ // src/cms/contracts/components/index.ts
301
+ import { featureShowcaseContract } from './featureShowcase.contract';
302
+
303
+ export default [
304
+ // ... existing contracts
305
+ featureShowcaseContract,
306
+ ];
307
+ ```
308
+
309
+ #### 3. Create the runtime component
310
+
311
+ **Astro:**
312
+ ```astro
313
+ ---
314
+ // src/components/sections/FeatureShowcase.astro
315
+ import { primaryImageSource } from '@gradial/aci';
316
+ import type { ContentProps } from '../../cms/contracts/components/featureShowcase.contract';
317
+
318
+ interface Props extends ContentProps {
319
+ className?: string;
320
+ }
321
+
322
+ const { headline, description, image, features, ctaLabel, ctaHref, className } = Astro.props;
323
+ ---
324
+ <section class={`py-[var(--spacing-section-y)] ${className ?? ''}`}>
325
+ <div class="mx-auto max-w-[var(--max-width-container)] px-[var(--spacing-container)]">
326
+ <h2 class="text-[length:var(--text-size-h2)]">{headline}</h2>
327
+ {description && <p class="text-[length:var(--text-size-body-large)]">{description}</p>}
328
+ {image && <img src={primaryImageSource(image).src} alt={image.alt ?? ''} />}
329
+ {features?.map(f => (
330
+ <div>
331
+ <h3>{f.title}</h3>
332
+ <p>{f.description}</p>
333
+ </div>
334
+ ))}
335
+ </div>
336
+ </section>
337
+ ```
338
+
339
+ **React (Next.js):**
340
+ ```tsx
341
+ // src/components/sections/FeatureShowcase.tsx
342
+ import { primaryImageSource } from '@gradial/aci';
343
+ import type { ContentProps } from '../../cms/contracts/components/featureShowcase.contract';
344
+
345
+ type FeatureShowcaseProps = ContentProps & { className?: string };
346
+
347
+ export function FeatureShowcase({ headline, description, image, features, className }: FeatureShowcaseProps) {
348
+ return (
349
+ <section className={`py-[var(--spacing-section-y)] ${className ?? ''}`}>
350
+ <div className="mx-auto max-w-[var(--max-width-container)] px-[var(--spacing-container)]">
351
+ <h2 className="text-[length:var(--text-size-h2)]">{headline}</h2>
352
+ {description && <p className="text-[length:var(--text-size-body-large)]">{description}</p>}
353
+ {image && <img src={primaryImageSource(image).src} alt={image.alt ?? ''} />}
354
+ </div>
355
+ </section>
356
+ );
357
+ }
358
+ ```
359
+
360
+ #### 4. Wire into the component registry
361
+
362
+ Add a `[contract, component]` tuple in `src/cms/registry.ts`:
363
+
364
+ ```ts
365
+ import { createRegistry } from '@gradial/aci';
366
+ import { featureShowcaseContract } from './contracts/components/featureShowcase.contract';
367
+ import { FeatureShowcase } from '@/components/sections/FeatureShowcase';
368
+
369
+ export const registry = createRegistry([
370
+ // ... existing entries
371
+ [featureShowcaseContract, FeatureShowcase],
372
+ ]);
373
+ ```
374
+
375
+ See `references/block-registry.md` for the full registry pattern for each
376
+ framework.
377
+
378
+ #### 5. Add content
379
+
380
+ ```json
381
+ // .content/pages/home/_index.json (inside regions.main)
382
+ {
383
+ "id": "features",
384
+ "component": "feature_showcase",
385
+ "props": {
386
+ "headline": "Why teams choose us",
387
+ "image": {
388
+ "$type": "dam.assetRef",
389
+ "assetId": "feature_image",
390
+ "alt": "Product dashboard"
391
+ },
392
+ "features": [
393
+ { "title": "Fast", "description": "Sub-second page loads" },
394
+ { "title": "Secure", "description": "SOC 2 compliant" }
395
+ ]
396
+ }
397
+ }
398
+ ```
399
+
400
+ #### 6. Verify
401
+
402
+ ```bash
403
+ npm run aci:compile # Contract compiles
404
+ npm run aci:validate # Content validates
405
+ npm run typecheck # Types check
406
+ npm run build # Framework builds
407
+ ```
408
+
409
+ ### Contract Rules
410
+
411
+ - Import `@gradial/aci`, `zod`, and shared schema helpers only.
412
+ - Export a conventional `<camelName>Contract` value.
413
+ - Export `ContentProps` from `z.infer<typeof schema>`.
414
+ - Use `defineComponentContract({ id, props: schema })` — the `id` is the
415
+ contract name used in content JSON `component` fields and the registry.
416
+ - `imageSlots` must match all `ImageSchema` fields, including
417
+ nested dot paths like `items.image`.
418
+ - `renderModes` has been removed from the contract API. There is no
419
+ `renderModes` field on `defineComponentContract`.
420
+
421
+ ### Composition Props
422
+
423
+ Props like `className`, slots, and rendered child nodes stay outside the
424
+ CMS contract. They are framework concerns, not content:
425
+
426
+ ```ts
427
+ // Contract: only content props
428
+ export const schema = z.object({
429
+ headline: z.string(),
430
+ });
431
+
432
+ // Runtime: content props + composition props
433
+ type Props = ContentProps & { className?: string };
434
+ ```
435
+
436
+ ### Import Rules
437
+
438
+ 1. **Contract files** import only `@gradial/aci`, `zod`, and shared
439
+ schema helpers. No React, Astro, CSS, browser globals, or runtime
440
+ modules.
441
+ 2. **Runtime components** may import contract types and framework code.
442
+ 3. **Registry files** (`src/cms/registry.ts`) import both contracts and
443
+ runtime components — they are the wiring point.
444
+ 4. Use `@gradial/aci` as the SDK package. Do not use `@baremetal/runtime`.
445
+
446
+ ### Layout Contracts
447
+
448
+ Layouts define page-level slot structure and optional fragment defaults
449
+ for chrome. Fragment defaults let CMS-managed fragments (navbar, footer)
450
+ fill slots automatically when the page doesn't provide blocks for them:
451
+
452
+ ```ts
453
+ // src/cms/contracts/layouts/index.ts
454
+ export default [
455
+ {
456
+ name: 'default',
457
+ slots: [
458
+ { name: 'header', required: false }, // filled by fragment default
459
+ { name: 'main', required: true }, // filled by page regions
460
+ { name: 'footer', required: false }, // filled by fragment default
461
+ ],
462
+ defaults: {
463
+ header: [{ kind: 'fragment-ref', fragmentId: 'navbar', inline: true }],
464
+ footer: [{ kind: 'fragment-ref', fragmentId: 'site-footer', inline: true }],
465
+ },
466
+ },
467
+ ];
468
+ ```
469
+
470
+ Fragment content lives in `.content/fragments/{id}/_index.json` and has
471
+ a `component` + `props` just like page blocks. Chrome components (Header,
472
+ Footer) are registered in the registry alongside page components.
473
+
474
+ The `layout` field in page JSON is **required** — there is no implicit
475
+ default. Every page must declare which layout it uses.
476
+
477
+ ## Component Tiers
478
+
479
+ Three directories, no more:
480
+
481
+ | Tier | Directory | Purpose |
482
+ |------|-----------|---------|
483
+ | elements | `src/components/elements/` | Small reusable UI + atomic content blocks |
484
+ | sections | `src/components/sections/` | Page-level section components |
485
+ | chrome | `src/components/chrome/` | Site chrome (Header, Footer, Nav) |
486
+
487
+ Do not create additional tiers (molecules, patterns, atoms as separate
488
+ directories). Three directories are sufficient.
489
+
490
+ ## Design System
491
+
492
+ ### Design Tokens
493
+
494
+ Tokens are CSS custom properties in `@theme` blocks under
495
+ `src/design-system/tokens/`. Tailwind v4 reads them and auto-generates
496
+ utility classes.
497
+
498
+ | File | Scope |
499
+ |------|-------|
500
+ | `colors.css` | Brand colors, semantic tokens, surfaces, borders |
501
+ | `typography.css` | Font families, sizes, weights, line heights |
502
+ | `spacing.css` | Spacing scale, container padding, section spacing |
503
+ | `radii.css` | Border radius values |
504
+ | `shadows.css` | Card and overlay shadows |
505
+ | `components.css` | Component-level tokens (buttons, header, footer) |
506
+ | `index.css` | Barrel import for all token files |
507
+
508
+ ### Using Tokens
509
+
510
+ **Direct Tailwind utilities (preferred):**
511
+ - Color: `text-fg`, `text-fg-muted`, `bg-surface-default`, `border-edge`
512
+ - Radii: `rounded-card`, `rounded-button`
513
+ - Shadows: `shadow-card-default`
514
+ - Standard spacing: `px-4`, `gap-6`, `w-24`, `max-w-3xl`
515
+
516
+ **Bracket syntax (for tokens without direct utilities):**
517
+ - Gradients: `bg-[image:var(--gradient-cta-dark)]`
518
+ - Typography: `text-[length:var(--text-size-body)]`, `font-[var(--font-weight-semibold)]`
519
+ - Spacing tokens: `py-[var(--spacing-section-y)]`, `p-[var(--spacing-card)]`
520
+
521
+ ### Forbidden Values
522
+
523
+ **No raw hex colors**: `text-[#8b72ff]`, `bg-[#f6f6f7]`
524
+ **No raw pixel values**: `p-[12px]`, `w-[200px]`, `gap-[24px]`
525
+ **No raw rem values**: `text-[1.25rem]`, `p-[0.75rem]`
526
+ **No Tailwind named colors**: `text-gray-500`, `bg-purple-400`
527
+
528
+ Use CSS variable tokens instead. If a token doesn't exist, add it to the
529
+ appropriate `tokens/*.css` file. The ESLint rule `no-raw-tailwind-values`
530
+ enforces this in component code.
531
+
532
+ ### Theme System
533
+
534
+ Three theme classes control semantic token resolution:
535
+
536
+ | Class | Use Case |
537
+ |-------|----------|
538
+ | `theme-auto` | Normal page content following browser light/dark preference |
539
+ | `theme-dark` | Fixed dark surfaces (header, footer, hero) |
540
+ | `theme-accent` | CTA-style accent surfaces |
541
+
542
+ ### Adding New Tokens
543
+
544
+ 1. Add the token to the appropriate `tokens/*.css` file inside `@theme`:
545
+ ```css
546
+ @theme {
547
+ --color-brand-500: #6b5ce7;
548
+ }
549
+ ```
550
+ 2. Reference it from component code.
551
+ 3. Tailwind v4 will auto-generate a utility if the token name matches a
552
+ Tailwind namespace.
553
+
554
+ ## Storybook
555
+
556
+ Every ACI frontend repo should have Storybook configured. Stories live
557
+ under `src/stories/` and follow the framework's Storybook setup.
558
+
559
+ ```bash
560
+ npm run storybook # Dev mode
561
+ npm run build:storybook # Build all stories
562
+ ```
563
+
564
+ ## Deep Dive: Component Registry
565
+
566
+ Read `references/block-registry.md` for the `createRegistry` pattern,
567
+ framework-specific usage, and registry rules.
568
+
569
+ ## Deep Dive: Image Slots and Assets
570
+
571
+ Read `references/image-assets.md` for `ImageSchema` usage, image
572
+ slot configuration, nested dot paths, and runtime image rendering rules.
573
+
574
+ ## Deep Dive: Contracts
575
+
576
+ Read `references/contracts.md` for the full contract file structure,
577
+ `defineComponentContract` API, `.aci.yaml` config reference, plain
578
+ layout contract objects, and contract barrel patterns.
579
+
580
+ ## Deep Dive: Design System
581
+
582
+ Read `references/design-system.md` for grid layout patterns, the
583
+ critical Tailwind purge warning, component CSS discipline rules,
584
+ chrome-specific override scopes, and token creation workflow.
585
+
586
+ ## Deep Dive: Repository Structure
587
+
588
+ Read `references/repo-structure.md` for the full annotated directory
589
+ layout, framework-specific paths, naming conventions, and generated
590
+ files list.