@gradial/aci 0.1.20-preview.3 → 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 (224) 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 +2 -2
  27. package/dist/next/middleware.js +6 -6
  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/react/Media.d.ts +24 -0
  35. package/dist/react/Media.js +30 -0
  36. package/dist/react/Picture.d.ts +12 -0
  37. package/dist/react/Picture.js +11 -0
  38. package/dist/react/VideoPlayer.d.ts +13 -0
  39. package/dist/react/{GradialVideoPlayer.js → VideoPlayer.js} +6 -5
  40. package/dist/react/index.d.ts +333 -3
  41. package/dist/react/index.js +89 -3
  42. package/dist/registry.d.ts +2 -2
  43. package/dist/registry.js +3 -3
  44. package/dist/types/block-ref.d.ts +26 -0
  45. package/dist/types/block-ref.js +28 -0
  46. package/dist/types/component.d.ts +36 -32
  47. package/dist/types/config.d.ts +1 -1
  48. package/dist/types/image.d.ts +33 -20
  49. package/dist/types/image.js +42 -50
  50. package/dist/types/index.d.ts +1 -0
  51. package/dist/types/index.js +1 -0
  52. package/dist/types/link.d.ts +38 -0
  53. package/dist/types/link.js +58 -0
  54. package/dist/types/media.d.ts +16 -35
  55. package/dist/types/media.js +15 -84
  56. package/dist/types/renderer.d.ts +1 -1
  57. package/dist/types/rich-text.d.ts +40 -0
  58. package/dist/types/rich-text.js +39 -0
  59. package/dist/types/video.d.ts +7 -13
  60. package/dist/types/video.js +3 -3
  61. package/package.json +28 -76
  62. package/src/cli/compile-registry.mjs +120 -63
  63. package/src/cli/validate-content.mjs +38 -75
  64. package/src/types/image.ts +79 -60
  65. package/templates/astro/template/.aci.yaml +27 -0
  66. package/templates/astro/template/.storybook/main.ts +79 -0
  67. package/templates/astro/template/.storybook/manager.ts +6 -0
  68. package/templates/astro/template/.storybook/preview.ts +33 -0
  69. package/templates/astro/template/astro.config.mjs +37 -0
  70. package/templates/astro/template/package.json.tmpl +41 -0
  71. package/templates/astro/template/src/cms/contracts/layouts/index.ts +18 -0
  72. package/templates/astro/template/src/cms/registry.ts +28 -0
  73. package/templates/astro/template/src/components/chrome/Footer.astro +107 -0
  74. package/templates/astro/template/src/components/chrome/Header.astro +94 -0
  75. package/templates/astro/template/src/components/elements/AtomicBlock.astro +15 -0
  76. package/templates/astro/template/src/components/elements/AtomicBlocks.astro +16 -0
  77. package/templates/astro/template/src/components/elements/Button.astro +51 -0
  78. package/templates/astro/template/src/components/elements/ButtonBlock.astro +25 -0
  79. package/templates/astro/template/src/components/elements/Container.astro +11 -0
  80. package/templates/astro/template/src/components/elements/Icon.astro +31 -0
  81. package/templates/astro/template/src/components/elements/Logo.astro +29 -0
  82. package/templates/astro/template/src/components/elements/RemoteImage.astro +41 -0
  83. package/templates/astro/template/src/components/sections/ContainerSection.astro +32 -0
  84. package/templates/astro/template/src/components/sections/FeatureCard.astro +23 -0
  85. package/templates/astro/template/src/components/sections/FeatureGrid.astro +35 -0
  86. package/templates/astro/template/src/components/sections/HomeHero.astro +51 -0
  87. package/templates/astro/template/src/components/sections/StatsBar.astro +33 -0
  88. package/templates/astro/template/src/design-system/styles.css +399 -0
  89. package/templates/astro/template/src/design-system/tokens/colors.css +60 -0
  90. package/templates/astro/template/src/design-system/tokens/components.css +69 -0
  91. package/templates/astro/template/src/design-system/tokens/index.css +6 -0
  92. package/templates/astro/template/src/design-system/tokens/radii.css +5 -0
  93. package/templates/astro/template/src/design-system/tokens/shadows.css +5 -0
  94. package/templates/astro/template/src/design-system/tokens/spacing.css +13 -0
  95. package/templates/astro/template/src/design-system/tokens/typography.css +24 -0
  96. package/templates/astro/template/src/env.d.ts +1 -0
  97. package/templates/astro/template/src/fixtures/atomicBlocks.ts +33 -0
  98. package/templates/astro/template/src/fixtures/composition.ts +31 -0
  99. package/templates/astro/template/src/fixtures/index.ts +17 -0
  100. package/templates/astro/template/src/fixtures/sections.ts +39 -0
  101. package/templates/astro/template/src/layouts/SiteLayout.astro +30 -0
  102. package/templates/astro/template/src/pages/[...slug].astro +61 -0
  103. package/templates/astro/template/src/render/RenderPage.astro +66 -0
  104. package/templates/astro/template/src/stories/CompositionStoryFrame.astro +15 -0
  105. package/templates/astro/template/src/stories/HomeHero.stories.ts +24 -0
  106. package/templates/astro/template/src/stories/SectionStoryFrame.astro +16 -0
  107. package/templates/astro/template/src/stories/StoryDocFrame.astro +31 -0
  108. package/templates/astro/template/tsconfig.json +11 -0
  109. package/templates/common/.agents/AGENTS.md +37 -0
  110. package/templates/common/.agents/skills/aci/SKILL.md +162 -0
  111. package/templates/common/.agents/skills/authoring/SKILL.md +590 -0
  112. package/templates/common/.agents/skills/authoring/references/block-registry.md +151 -0
  113. package/templates/common/.agents/skills/authoring/references/contracts.md +153 -0
  114. package/templates/common/.agents/skills/authoring/references/design-system.md +171 -0
  115. package/templates/common/.agents/skills/authoring/references/image-assets.md +73 -0
  116. package/templates/common/.agents/skills/authoring/references/repo-structure.md +157 -0
  117. package/templates/common/.agents/skills/migration/SKILL.md +160 -0
  118. package/templates/common/.agents/skills/migration/references/analyzing-source.md +215 -0
  119. package/templates/common/.agents/skills/migration/references/component-migration.md +200 -0
  120. package/templates/common/.agents/skills/migration/references/selector-map.md +31 -0
  121. package/templates/common/.content/.gradial-dam/blobs/sha256/7f/bf/7fbfc7fe230ff12688c496f660119e41d9725c4504d82e310c15665c73c09292 +0 -0
  122. package/templates/common/.content/.gradial-dam/blobs/sha256/a0/f5/a0f506689de9b15a7055f70103a3f02cd3ba105771ae2de6754df2ede1210d3a +0 -0
  123. package/templates/common/.content/.gradial-dam/blobs/sha256/dd/b7/ddb7e9642be08317bcd4b2bd26b58ba8d0d34dd7239e03e684a4f3634fa3845b +0 -0
  124. package/templates/common/.content/.gradial-dam/blobs/sha256/fb/32/fb3260e8286ea8231ea1c77b9c0be92d6edd9026565731eb14cfdcfd6b1b75b3 +0 -0
  125. package/templates/common/.content/config/site.json +192 -0
  126. package/templates/common/.content/dam/folders/seed/ast_site_about_hero/asset.json +9 -0
  127. package/templates/common/.content/dam/folders/seed/ast_site_about_hero/editorial.json +14 -0
  128. package/templates/common/.content/dam/folders/seed/ast_site_about_hero/governance.json +23 -0
  129. package/templates/common/.content/dam/folders/seed/ast_site_about_hero/versions/v0001.json +37 -0
  130. package/templates/common/.content/dam/folders/seed/ast_site_home_hero/asset.json +9 -0
  131. package/templates/common/.content/dam/folders/seed/ast_site_home_hero/editorial.json +14 -0
  132. package/templates/common/.content/dam/folders/seed/ast_site_home_hero/governance.json +23 -0
  133. package/templates/common/.content/dam/folders/seed/ast_site_home_hero/versions/v0001.json +37 -0
  134. package/templates/common/.content/fragments/navbar/_index.json +25 -0
  135. package/templates/common/.content/fragments/site-footer/_index.json +62 -0
  136. package/templates/common/.content/pages/about/_index.json +51 -0
  137. package/templates/common/.content/pages/home/_index.json +112 -0
  138. package/templates/common/.content/schemas/site.schema.json +123 -0
  139. package/templates/common/README.md.tmpl +45 -0
  140. package/templates/common/contracts/components/button.contract.ts +19 -0
  141. package/templates/common/contracts/components/container.contract.ts +19 -0
  142. package/templates/common/contracts/components/featureCard.contract.ts +15 -0
  143. package/templates/common/contracts/components/featureGrid.contract.ts +16 -0
  144. package/templates/common/contracts/components/homeHero.contract.ts +36 -0
  145. package/templates/common/contracts/components/index.ts +21 -0
  146. package/templates/common/contracts/components/siteFooter.contract.ts +45 -0
  147. package/templates/common/contracts/components/siteNavigation.contract.ts +30 -0
  148. package/templates/common/contracts/components/statsBar.contract.ts +19 -0
  149. package/templates/common/design-system/styles.css +346 -0
  150. package/templates/common/design-system/tokens/colors.css +110 -0
  151. package/templates/common/design-system/tokens/components.css +86 -0
  152. package/templates/common/design-system/tokens/index.css +6 -0
  153. package/templates/common/design-system/tokens/radii.css +5 -0
  154. package/templates/common/design-system/tokens/shadows.css +9 -0
  155. package/templates/common/design-system/tokens/spacing.css +26 -0
  156. package/templates/common/design-system/tokens/typography.css +32 -0
  157. package/templates/common/eslint-rules/no-raw-tailwind-values.js +127 -0
  158. package/templates/common/public/assets/Dwire-model.png +0 -0
  159. package/templates/common/public/assets/bare-metal-logo.png +0 -0
  160. package/templates/common/public/assets/meridian-logo.svg +8 -0
  161. package/templates/common/public/assets/seattle.avif +0 -0
  162. package/templates/nextjs/template/.aci.yaml +25 -0
  163. package/templates/nextjs/template/.storybook/main.ts +13 -0
  164. package/templates/nextjs/template/.storybook/preview.ts +34 -0
  165. package/templates/nextjs/template/eslint.config.mjs +36 -0
  166. package/templates/nextjs/template/next-env.d.ts +6 -0
  167. package/templates/nextjs/template/next.config.ts +8 -0
  168. package/templates/nextjs/template/package.json.tmpl +46 -0
  169. package/templates/nextjs/template/postcss.config.mjs +7 -0
  170. package/templates/nextjs/template/src/app/[[...slug]]/page.tsx +8 -0
  171. package/templates/nextjs/template/src/app/gradial/assets/[releaseId]/[...path]/route.ts +31 -0
  172. package/templates/nextjs/template/src/app/layout.tsx +29 -0
  173. package/templates/nextjs/template/src/cms/contracts/components/button.contract.ts +19 -0
  174. package/templates/nextjs/template/src/cms/contracts/components/container.contract.ts +20 -0
  175. package/templates/nextjs/template/src/cms/contracts/components/homeHero.contract.ts +36 -0
  176. package/templates/nextjs/template/src/cms/contracts/components/index.ts +15 -0
  177. package/templates/nextjs/template/src/cms/contracts/components/siteFooter.contract.ts +45 -0
  178. package/templates/nextjs/template/src/cms/contracts/components/siteNavigation.contract.ts +30 -0
  179. package/templates/nextjs/template/src/cms/contracts/layouts/index.ts +16 -0
  180. package/templates/nextjs/template/src/cms/registry.ts +28 -0
  181. package/templates/nextjs/template/src/components/chrome/Footer.tsx +86 -0
  182. package/templates/nextjs/template/src/components/chrome/Header.tsx +74 -0
  183. package/templates/nextjs/template/src/components/elements/AtomicBlock.tsx +13 -0
  184. package/templates/nextjs/template/src/components/elements/Button.tsx +40 -0
  185. package/templates/nextjs/template/src/components/elements/ButtonBlock.tsx +18 -0
  186. package/templates/nextjs/template/src/components/elements/Container.tsx +14 -0
  187. package/templates/nextjs/template/src/components/elements/Icon.tsx +29 -0
  188. package/templates/nextjs/template/src/components/elements/Logo.tsx +38 -0
  189. package/templates/nextjs/template/src/components/sections/ContainerSection.tsx +28 -0
  190. package/templates/nextjs/template/src/components/sections/FeatureCard.tsx +25 -0
  191. package/templates/nextjs/template/src/components/sections/FeatureGrid.tsx +36 -0
  192. package/templates/nextjs/template/src/components/sections/HomeHero.tsx +62 -0
  193. package/templates/nextjs/template/src/components/sections/StatsBar.tsx +30 -0
  194. package/templates/nextjs/template/src/middleware.ts +4 -0
  195. package/templates/nextjs/template/src/stories/Button.stories.tsx +38 -0
  196. package/templates/nextjs/template/src/stories/Header.stories.tsx +30 -0
  197. package/templates/nextjs/template/src/stories/HomeHero.stories.tsx +33 -0
  198. package/templates/nextjs/template/tsconfig.json +23 -0
  199. package/templates/nextjs/template/vercel.json +6 -0
  200. package/dist/content/contract.d.ts +0 -83
  201. package/dist/content/contract.js +0 -104
  202. package/dist/content/tailwind-validator.d.ts +0 -6
  203. package/dist/content/tailwind-validator.js +0 -31
  204. package/dist/content/validation.d.ts +0 -108
  205. package/dist/content/validation.js +0 -183
  206. package/dist/react/GradialMedia.d.ts +0 -24
  207. package/dist/react/GradialMedia.js +0 -31
  208. package/dist/react/GradialPicture.d.ts +0 -14
  209. package/dist/react/GradialPicture.js +0 -30
  210. package/dist/react/GradialVideoPlayer.d.ts +0 -13
  211. package/dist/sveltekit/index.d.ts +0 -25
  212. package/dist/sveltekit/index.js +0 -66
  213. package/dist/sveltekit/preview.d.ts +0 -5
  214. package/dist/sveltekit/preview.js +0 -33
  215. package/src/types/component.ts +0 -49
  216. package/src/types/config.ts +0 -37
  217. package/src/types/data.ts +0 -47
  218. package/src/types/index.ts +0 -10
  219. package/src/types/layout.ts +0 -29
  220. package/src/types/media.ts +0 -125
  221. package/src/types/page.ts +0 -47
  222. package/src/types/render-mode.ts +0 -10
  223. package/src/types/renderer.ts +0 -83
  224. package/src/types/video.ts +0 -66
@@ -0,0 +1,127 @@
1
+ const UNIT = '(?:px|rem|em|vh|vw|dvh|dvw|svh|svw|ch|ex|cap|ic|lh|rlh|cm|mm|in|pt|pc|%)';
2
+ const HEX_OR_LENGTH = new RegExp(`#[0-9a-fA-F]{3,8}\\b|\\d+(?:\\.\\d+)?${UNIT}`);
3
+ const RATIO_ONLY = /^\d+\/\d+$/;
4
+ const VARIANT_PREFIX = /^(!)?(?:(?:hover|focus|focus-within|focus-visible|active|disabled|visited|checked|first|last|odd|even|sm|md|lg|xl|2xl|dark|light|motion-safe|motion-reduce|portrait|landscape|print|rtl|ltr|open|placeholder|before|after|first-line|first-letter|selection|marker|group-hover|group-focus|peer-hover|peer-focus|has|not|aria-\w+|data-\w+):)*/;
5
+
6
+ function extractArbitrary(token) {
7
+ const bare = token.replace(VARIANT_PREFIX, '');
8
+ const open = bare.indexOf('[');
9
+ if (open === -1) return null;
10
+ const close = bare.lastIndexOf(']');
11
+ if (close <= open) return null;
12
+ return bare.slice(open + 1, close);
13
+ }
14
+
15
+ function isBadArbitrary(content) {
16
+ // Allow anything containing a CSS variable reference.
17
+ if (content.includes('var(--')) return false;
18
+ // Allow values with no digits (selectors, keywords like `&>div`).
19
+ if (!/\d/.test(content) && !content.includes('#')) return false;
20
+ // Strip a leading data-type hint like `length:` or `color:`.
21
+ const value = content.replace(/^[a-z-]+:/, '');
22
+ if (RATIO_ONLY.test(value)) return false;
23
+ if (HEX_OR_LENGTH.test(value)) return true;
24
+ // Unitless numeric value (e.g. z-[60], w-[300]). Flag.
25
+ if (/\d/.test(value)) return true;
26
+ return false;
27
+ }
28
+
29
+ function findBadClassTokens(classString) {
30
+ const tokens = classString.trim().split(/\s+/).filter(Boolean);
31
+ const bad = [];
32
+ for (const token of tokens) {
33
+ const arb = extractArbitrary(token);
34
+ if (arb !== null && isBadArbitrary(arb)) {
35
+ bad.push(token);
36
+ }
37
+ }
38
+ return bad;
39
+ }
40
+
41
+ function findBadStyleMatches(styleString) {
42
+ // Strip var(--...) references so legitimate token names containing digits don't match.
43
+ const stripped = styleString.replace(/var\([^)]*\)/g, '');
44
+ const matches = stripped.match(new RegExp(HEX_OR_LENGTH.source, 'g'));
45
+ return matches || [];
46
+ }
47
+
48
+ function reportClass(context, node, value) {
49
+ if (typeof value !== 'string' || !value.includes('[')) return;
50
+ const bad = findBadClassTokens(value);
51
+ if (bad.length > 0) {
52
+ context.report({
53
+ node,
54
+ message: `Raw arbitrary Tailwind values are not allowed: ${bad.join(', ')}. Use design system tokens (var(--...)) instead.`
55
+ });
56
+ }
57
+ }
58
+
59
+ function reportStyle(context, node, value) {
60
+ if (typeof value !== 'string') return;
61
+ const bad = findBadStyleMatches(value);
62
+ if (bad.length > 0) {
63
+ context.report({
64
+ node,
65
+ message: `Raw values are not allowed in inline style: ${bad.join(', ')}. Use design system tokens (var(--...)) instead.`
66
+ });
67
+ }
68
+ }
69
+
70
+ /** @type {import('eslint').Rule.RuleModule} */
71
+ const noRawTailwindValues = {
72
+ meta: {
73
+ type: 'suggestion',
74
+ docs: {
75
+ description: 'Disallow raw hex/length values in Tailwind arbitrary values and inline style attributes; require design tokens via var(--...).'
76
+ },
77
+ schema: []
78
+ },
79
+ create(context) {
80
+ function handleClassNode(node, value) {
81
+ reportClass(context, node, value);
82
+ }
83
+
84
+ function handleStyleStringNode(node, value) {
85
+ reportStyle(context, node, value);
86
+ }
87
+
88
+ function visitClassLiteral(node) {
89
+ handleClassNode(node, node.value);
90
+ }
91
+ function visitClassTemplate(node) {
92
+ for (const quasi of node.quasis) handleClassNode(node, quasi.value.raw);
93
+ }
94
+
95
+ return {
96
+ // JSX/Astro class attribute
97
+ [`JSXAttribute[name.name=/^(class|className)$/] > Literal`]: visitClassLiteral,
98
+ [`JSXAttribute[name.name=/^(class|className)$/] > TemplateLiteral`]: visitClassTemplate,
99
+
100
+ // Object-form { class: '...' } (e.g. clsx args, defineProps defaults)
101
+ 'Property[key.name="class"] > Literal'(node) {
102
+ handleClassNode(node, node.value);
103
+ },
104
+ 'Property[key.name="class"] > TemplateLiteral'(node) {
105
+ for (const quasi of node.quasis) handleClassNode(node, quasi.value.raw);
106
+ },
107
+
108
+ // JSX/Astro style="..." (string form, common in Astro and HTML)
109
+ 'JSXAttribute[name.name="style"] > Literal'(node) {
110
+ handleStyleStringNode(node, node.value);
111
+ },
112
+ 'JSXAttribute[name.name="style"] > TemplateLiteral'(node) {
113
+ for (const quasi of node.quasis) handleStyleStringNode(node, quasi.value.raw);
114
+ },
115
+
116
+ // React style={{ color: '#fff', padding: '10px' }}
117
+ 'JSXAttribute[name.name="style"] JSXExpressionContainer > ObjectExpression > Property > Literal'(node) {
118
+ if (typeof node.value === 'string') handleStyleStringNode(node, node.value);
119
+ },
120
+ 'JSXAttribute[name.name="style"] JSXExpressionContainer > ObjectExpression > Property > TemplateLiteral'(node) {
121
+ for (const quasi of node.quasis) handleStyleStringNode(node, quasi.value.raw);
122
+ }
123
+ };
124
+ }
125
+ };
126
+
127
+ export default noRawTailwindValues;
@@ -0,0 +1,8 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 40" fill="#0f172a">
2
+ <g aria-label="Meridian">
3
+ <circle cx="16" cy="20" r="6" fill="#0f172a"/>
4
+ <path d="M16 6v6M16 28v6M6 20h6M20 20h6" stroke="#0f172a" stroke-width="3" stroke-linecap="round"/>
5
+ <ellipse cx="16" cy="20" rx="14" ry="5" fill="none" stroke="#0f172a" stroke-width="2" transform="rotate(-30 16 20)"/>
6
+ </g>
7
+ <text x="42" y="26" font-family="Space Grotesk, Inter, sans-serif" font-size="22" font-weight="700" letter-spacing="-0.02em" fill="#0f172a">Meridian</text>
8
+ </svg>
@@ -0,0 +1,25 @@
1
+ version: "1"
2
+ siteId: "site_nextjs"
3
+ framework: next
4
+
5
+ source:
6
+ root: "./"
7
+
8
+ componentRegistry: ./src/cms/contracts/components/index.ts
9
+ layoutRegistry: ./src/cms/contracts/layouts/index.ts
10
+
11
+ capabilities:
12
+ staticRender: true
13
+ ssr: true
14
+ ssrIslands: true
15
+ clientIslands: true
16
+ fragmentRender: true
17
+
18
+ routes:
19
+ cmsManaged: "/[...slug]"
20
+ frameworkOwned:
21
+ - "/api/*"
22
+ - "/_next/*"
23
+ - "/.gradial-dam/*"
24
+
25
+ rendererProtocol: stdio-json
@@ -0,0 +1,13 @@
1
+ import type { StorybookConfig } from '@storybook/nextjs';
2
+
3
+ const config: StorybookConfig = {
4
+ stories: ['../src/**/*.stories.@(ts|tsx|mdx)'],
5
+ addons: ['@storybook/addon-docs'],
6
+ framework: {
7
+ name: '@storybook/nextjs',
8
+ options: {}
9
+ },
10
+ staticDirs: ['../public'],
11
+ };
12
+
13
+ export default config;
@@ -0,0 +1,34 @@
1
+ import '../src/design-system/styles.css';
2
+
3
+ const preview = {
4
+ globalTypes: {
5
+ theme: {
6
+ description: 'Theme for components',
7
+ toolbar: {
8
+ title: 'Theme',
9
+ icon: 'sun',
10
+ items: ['light', 'dark'],
11
+ dynamicTitle: true
12
+ }
13
+ }
14
+ },
15
+ decorators: [
16
+ // Storybook decorator types are verbose; keep the starter example focused.
17
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
18
+ (Story: any, context: any) => {
19
+ const theme = context.globals.theme || 'light';
20
+ document.body.className = `theme-${theme} min-h-screen bg-[var(--color-surface-default)]`;
21
+ return Story();
22
+ }
23
+ ],
24
+ parameters: {
25
+ layout: 'fullscreen',
26
+ options: {
27
+ storySort: {
28
+ order: ['Core Atomic Blocks', 'Composition', '*']
29
+ }
30
+ }
31
+ }
32
+ };
33
+
34
+ export default preview;
@@ -0,0 +1,36 @@
1
+ import { dirname, resolve } from 'node:path';
2
+ import { fileURLToPath } from 'node:url';
3
+ import { includeIgnoreFile } from '@eslint/compat';
4
+ import { FlatCompat } from '@eslint/eslintrc';
5
+ import eslintPlugin from 'eslint-plugin-import';
6
+ import noRawTailwind from './eslint-rules/no-raw-tailwind-values.js';
7
+
8
+ const __filename = fileURLToPath(import.meta.url);
9
+ const __dirname = dirname(__filename);
10
+ const compat = new FlatCompat({ baseDirectory: __dirname });
11
+ const gitignorePath = resolve(__dirname, '.gitignore');
12
+
13
+ const eslintConfig = [
14
+ includeIgnoreFile(gitignorePath),
15
+ { ignores: ['next-env.d.ts'] },
16
+ ...compat.extends('next/core-web-vitals', 'next/typescript'),
17
+ {
18
+ plugins: {
19
+ import: eslintPlugin,
20
+ },
21
+ },
22
+ {
23
+ plugins: {
24
+ 'aci-rules': {
25
+ rules: {
26
+ 'no-raw-tailwind-values': noRawTailwind,
27
+ },
28
+ },
29
+ },
30
+ rules: {
31
+ 'aci-rules/no-raw-tailwind-values': 'warn',
32
+ },
33
+ },
34
+ ];
35
+
36
+ export default eslintConfig;
@@ -0,0 +1,6 @@
1
+ /// <reference types="next" />
2
+ /// <reference types="next/image-types/global" />
3
+ /// <reference path="./.next/types/routes.d.ts" />
4
+
5
+ // NOTE: This file should not be edited
6
+ // see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
@@ -0,0 +1,8 @@
1
+ import { withAci } from '@gradial/aci/next/config';
2
+ import type { NextConfig } from 'next';
3
+
4
+ const nextConfig: NextConfig = {
5
+ reactStrictMode: true,
6
+ };
7
+
8
+ export default withAci(nextConfig);
@@ -0,0 +1,46 @@
1
+ {
2
+ "name": "{{siteName}}",
3
+ "version": "{{version}}",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "ACI_CONTENT_PROVIDER=file next dev -H 0.0.0.0",
8
+ "build": "next build",
9
+ "preview": "next start -H 0.0.0.0",
10
+ "typecheck": "tsc --noEmit",
11
+ "aci:compile": "node ./node_modules/@gradial/aci/bin/aci.js build --compile-only",
12
+ "content:compile": "node ./node_modules/@gradial/aci/bin/aci.js build --skip-code --content ./.content --out ./.aci/compiled",
13
+ "aci:build": "node ./node_modules/@gradial/aci/bin/aci.js build --content ./.content",
14
+ "aci:doctor": "node ./node_modules/@gradial/aci/bin/aci.js doctor",
15
+ "aci:validate": "node ./node_modules/@gradial/aci/bin/aci.js build --validate-only",
16
+ "lint": "eslint .",
17
+ "storybook": "storybook dev -p 6006",
18
+ "build:storybook": "storybook build"
19
+ },
20
+ "dependencies": {
21
+ "@gradial/aci": "0.2.0-rc.0",
22
+ "js-yaml": "^4.1.1",
23
+ "next": "^15.1.6",
24
+ "react": "^19.0.0",
25
+ "react-dom": "^19.0.0",
26
+ "zod": "^4.0.0"
27
+ },
28
+ "devDependencies": {
29
+ "@types/js-yaml": "^4.0.9",
30
+ "@types/node": "^24.10.1",
31
+ "@types/react": "^19.0.0",
32
+ "@types/react-dom": "^19.0.0",
33
+ "@eslint/compat": "^1.2.0",
34
+ "@eslint/eslintrc": "^3.2.0",
35
+ "eslint": "^9.18.0",
36
+ "eslint-config-next": "^15.1.6",
37
+ "eslint-plugin-import": "^2.29.1",
38
+ "tailwindcss": "^4.2.4",
39
+ "@tailwindcss/postcss": "^4.2.4",
40
+ "tsx": "^4.20.0",
41
+ "typescript": "5.9.3",
42
+ "storybook": "^9.0.0",
43
+ "@storybook/nextjs": "^9.0.0",
44
+ "@storybook/addon-docs": "^9.0.0"
45
+ }
46
+ }
@@ -0,0 +1,7 @@
1
+ const config = {
2
+ plugins: {
3
+ '@tailwindcss/postcss': {},
4
+ },
5
+ };
6
+
7
+ export default config;
@@ -0,0 +1,8 @@
1
+ import { createPage } from '@gradial/aci/next';
2
+ import { registry } from '@/cms/registry';
3
+
4
+ const page = createPage(registry);
5
+
6
+ export const dynamic = 'force-dynamic';
7
+ export const generateMetadata = page.generateMetadata;
8
+ export default page.default;
@@ -0,0 +1,31 @@
1
+ import { getReleaseAssetResponse } from '@gradial/aci/content';
2
+ import { FileContentProvider } from '@gradial/aci/providers/file';
3
+
4
+ const LOCAL_RELEASE_ID = 'local';
5
+
6
+ type AssetRouteContext = {
7
+ params: Promise<{
8
+ releaseId?: string;
9
+ path?: string[];
10
+ }>;
11
+ };
12
+
13
+ export const runtime = 'nodejs';
14
+ export const dynamic = 'force-dynamic';
15
+
16
+ export async function GET(_request: Request, { params }: AssetRouteContext): Promise<Response> {
17
+ const resolvedParams = await params;
18
+ const releaseId = resolvedParams.releaseId || '';
19
+ const assetPath = '/' + (resolvedParams.path || []).join('/');
20
+ if (!releaseId || assetPath === '/') {
21
+ return new Response('asset not found', { status: 404 });
22
+ }
23
+ if (releaseId === LOCAL_RELEASE_ID) {
24
+ return new FileContentProvider().fetchRaw(assetPath);
25
+ }
26
+ try {
27
+ return await getReleaseAssetResponse({ releaseId, assetPath });
28
+ } catch {
29
+ return new Response('asset not found', { status: 404 });
30
+ }
31
+ }
@@ -0,0 +1,29 @@
1
+ import type { ReactNode } from 'react';
2
+ import { Inter, Space_Grotesk } from 'next/font/google';
3
+ import { DevRefresh } from '@gradial/aci/next';
4
+ import '@/design-system/styles.css';
5
+
6
+ const inter = Inter({
7
+ subsets: ['latin'],
8
+ variable: '--font-inter',
9
+ display: 'swap',
10
+ weight: ['400', '500', '600', '700'],
11
+ });
12
+
13
+ const spaceGrotesk = Space_Grotesk({
14
+ subsets: ['latin'],
15
+ variable: '--font-space-grotesk',
16
+ display: 'swap',
17
+ weight: ['400', '500', '600', '700'],
18
+ });
19
+
20
+ export default function Layout({ children }: { children: ReactNode }) {
21
+ return (
22
+ <html lang="en" className={`${inter.variable} ${spaceGrotesk.variable}`}>
23
+ <body>
24
+ {children}
25
+ <DevRefresh />
26
+ </body>
27
+ </html>
28
+ );
29
+ }
@@ -0,0 +1,19 @@
1
+ import { defineComponentContract } from '@gradial/aci';
2
+ import { z } from 'zod';
3
+
4
+ export const schema = z.object({
5
+ label: z.string().min(1),
6
+ href: z.string().min(1),
7
+ variant: z.enum(['primary', 'secondary', 'tertiary', 'outline', 'ghost']).optional(),
8
+ newWindow: z.boolean().optional(),
9
+ icon: z.string().optional(),
10
+ iconPosition: z.enum(['before', 'after']).optional(),
11
+ className: z.string().optional(),
12
+ });
13
+
14
+ export type ContentProps = z.infer<typeof schema>;
15
+
16
+ export const buttonContract = defineComponentContract({
17
+ id: 'button',
18
+ props: schema,
19
+ });
@@ -0,0 +1,20 @@
1
+ import { defineComponentContract } from '@gradial/aci';
2
+ import { z } from 'zod';
3
+
4
+ export const schema = z.object({
5
+ theme: z.enum(['auto', 'light', 'dark']).optional(),
6
+ className: z.string().optional(),
7
+ });
8
+
9
+ export type ContentProps = z.infer<typeof schema>;
10
+
11
+ export const containerContract = defineComponentContract({
12
+ id: 'container',
13
+ props: schema,
14
+ slots: {
15
+ content: {
16
+ allowed: ['button'],
17
+ multiple: true,
18
+ },
19
+ },
20
+ });
@@ -0,0 +1,36 @@
1
+ import { defineComponentContract, ImageSchema } from '@gradial/aci';
2
+ import { z } from 'zod';
3
+
4
+ export const schema = z.object({
5
+ eyebrow: z.string().optional(),
6
+ headline: z.string().min(1),
7
+ description: z.string().optional(),
8
+ ctaLabel: z.string().min(1),
9
+ ctaHref: z.string().min(1),
10
+ image: ImageSchema,
11
+ });
12
+
13
+ export type ContentProps = z.infer<typeof schema>;
14
+
15
+ export const homeHeroContract = defineComponentContract({
16
+ id: 'home_hero',
17
+ props: schema,
18
+ imageSlots: {
19
+ image: {
20
+ formats: ['image/avif', 'image/webp', 'image/jpeg'],
21
+ sizes: '100vw',
22
+ outputs: [
23
+ {
24
+ aspectRatio: '16:9',
25
+ widths: [768, 1280, 1920],
26
+ media: '(min-width: 768px)',
27
+ },
28
+ {
29
+ aspectRatio: '3:4',
30
+ widths: [480, 720, 960],
31
+ media: '(max-width: 767px)',
32
+ },
33
+ ],
34
+ },
35
+ },
36
+ });
@@ -0,0 +1,15 @@
1
+ import { buttonContract } from './button.contract';
2
+ import { containerContract } from './container.contract';
3
+ import { homeHeroContract } from './homeHero.contract';
4
+ import { siteNavigationContract } from './siteNavigation.contract';
5
+ import { siteFooterContract } from './siteFooter.contract';
6
+
7
+ const componentContracts = [
8
+ homeHeroContract,
9
+ containerContract,
10
+ buttonContract,
11
+ siteNavigationContract,
12
+ siteFooterContract,
13
+ ];
14
+
15
+ export default componentContracts;
@@ -0,0 +1,45 @@
1
+ import { defineComponentContract } from '@gradial/aci';
2
+ import { z } from 'zod';
3
+
4
+ const linkSchema = z.object({
5
+ id: z.string().min(1),
6
+ label: z.string().min(1),
7
+ href: z.string().min(1),
8
+ });
9
+
10
+ const groupSchema = z.object({
11
+ id: z.string().min(1),
12
+ title: z.string(),
13
+ links: z.array(linkSchema),
14
+ });
15
+
16
+ const columnSchema = z.object({
17
+ id: z.string().min(1),
18
+ title: z.string().optional(),
19
+ links: z.array(linkSchema).optional(),
20
+ groups: z.array(groupSchema).optional(),
21
+ });
22
+
23
+ const socialSchema = linkSchema.extend({
24
+ icon: z.string().optional(),
25
+ });
26
+
27
+ export const schema = z.object({
28
+ brandHref: z.string().min(1),
29
+ brandLabel: z.string().min(1),
30
+ brandLogo: z.object({
31
+ src: z.string(),
32
+ alt: z.string(),
33
+ }).nullable().optional(),
34
+ columns: z.array(columnSchema),
35
+ social: z.array(socialSchema).optional(),
36
+ legalLinks: z.array(linkSchema).optional(),
37
+ copyright: z.string().optional(),
38
+ });
39
+
40
+ export type ContentProps = z.infer<typeof schema>;
41
+
42
+ export const siteFooterContract = defineComponentContract({
43
+ id: 'site-footer',
44
+ props: schema,
45
+ });
@@ -0,0 +1,30 @@
1
+ import { defineComponentContract } from '@gradial/aci';
2
+ import { z } from 'zod';
3
+
4
+ const linkSchema = z.object({
5
+ id: z.string().min(1),
6
+ label: z.string().min(1),
7
+ href: z.string().min(1),
8
+ });
9
+
10
+ const utilitySchema = linkSchema.extend({
11
+ variant: z.enum(['primary', 'secondary', 'tertiary', 'outline', 'ghost']).optional(),
12
+ });
13
+
14
+ export const schema = z.object({
15
+ brandHref: z.string().min(1),
16
+ brandLabel: z.string().min(1),
17
+ brandLogo: z.object({
18
+ src: z.string(),
19
+ alt: z.string(),
20
+ }).nullable().optional(),
21
+ navigation: z.array(linkSchema),
22
+ utilities: z.array(utilitySchema).optional(),
23
+ });
24
+
25
+ export type ContentProps = z.infer<typeof schema>;
26
+
27
+ export const siteNavigationContract = defineComponentContract({
28
+ id: 'site-navigation',
29
+ props: schema,
30
+ });
@@ -0,0 +1,16 @@
1
+ const layoutContracts = [
2
+ {
3
+ name: 'default',
4
+ slots: [
5
+ { name: 'header', required: false },
6
+ { name: 'main', required: true },
7
+ { name: 'footer', required: false },
8
+ ],
9
+ defaults: {
10
+ header: [{ kind: 'fragment-ref', fragmentId: 'navbar', inline: true }],
11
+ footer: [{ kind: 'fragment-ref', fragmentId: 'site-footer', inline: true }],
12
+ },
13
+ },
14
+ ];
15
+
16
+ export default layoutContracts;
@@ -0,0 +1,28 @@
1
+ import { createRegistry } from '@gradial/aci';
2
+ import { homeHeroContract } from './contracts/components/homeHero.contract';
3
+ import { containerContract } from './contracts/components/container.contract';
4
+ import { buttonContract } from './contracts/components/button.contract';
5
+ import { siteNavigationContract } from './contracts/components/siteNavigation.contract';
6
+ import { siteFooterContract } from './contracts/components/siteFooter.contract';
7
+ import { featureGridContract } from './contracts/components/featureGrid.contract';
8
+ import { featureCardContract } from './contracts/components/featureCard.contract';
9
+ import { statsBarContract } from './contracts/components/statsBar.contract';
10
+ import { HomeHero } from '@/components/sections/HomeHero';
11
+ import { ContainerSection } from '@/components/sections/ContainerSection';
12
+ import { ButtonBlock } from '@/components/elements/ButtonBlock';
13
+ import { Header } from '@/components/chrome/Header';
14
+ import { Footer } from '@/components/chrome/Footer';
15
+ import { FeatureGrid } from '@/components/sections/FeatureGrid';
16
+ import { FeatureCard } from '@/components/sections/FeatureCard';
17
+ import { StatsBar } from '@/components/sections/StatsBar';
18
+
19
+ export const registry = createRegistry([
20
+ [homeHeroContract, HomeHero],
21
+ [containerContract, ContainerSection],
22
+ [buttonContract, ButtonBlock],
23
+ [siteNavigationContract, Header],
24
+ [siteFooterContract, Footer],
25
+ [featureGridContract, FeatureGrid],
26
+ [featureCardContract, FeatureCard],
27
+ [statsBarContract, StatsBar],
28
+ ]);