@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
@@ -1,28 +1,47 @@
1
1
  import { z } from 'zod';
2
2
 
3
+ // ---------------------------------------------------------------------------
4
+ // Image source variant (responsive derivative)
5
+ //
6
+ // Each entry is one concrete derivative produced by the compiler for the asset
7
+ // slot. Renderers pick the primary variant and build a srcset from the list.
8
+ // ---------------------------------------------------------------------------
9
+
3
10
  export interface ImageSource {
11
+ /** Derivative URL. */
4
12
  src: string;
13
+ /** Intrinsic width in pixels. */
5
14
  width: number;
15
+ /** Intrinsic height in pixels. */
6
16
  height: number;
17
+ /** MIME type hint, e.g. image/webp. */
7
18
  type: string;
8
19
  }
9
20
 
10
- export interface PictureSource {
11
- media?: string;
12
- type: string;
13
- srcset: string;
14
- sizes?: string;
15
- }
21
+ // ---------------------------------------------------------------------------
22
+ // Image — DAM-backed image asset with compiler-resolved derivatives
23
+ //
24
+ // Content authors write an asset reference: { "$type": "dam.assetRef", ... }
25
+ // The compiler resolves it to this Image value based on the asset's media type.
26
+ // ---------------------------------------------------------------------------
16
27
 
17
- export interface GradialImage {
28
+ export interface Image {
29
+ /** Discriminator — always 'gradial.image'. */
18
30
  $type: 'gradial.image';
31
+ /** DAM asset identifier. */
19
32
  assetId: string;
33
+ /** DAM version identifier. */
20
34
  versionId: string;
35
+ /** Accessible description of the image content. */
21
36
  alt: string;
22
- fallback: ImageSource;
23
- sources: PictureSource[] | null;
37
+ /** Responsive derivative variants produced for the asset slot. */
38
+ sources: ImageSource[];
24
39
  }
25
40
 
41
+ // ---------------------------------------------------------------------------
42
+ // Image slot contract (declared by components that accept image props)
43
+ // ---------------------------------------------------------------------------
44
+
26
45
  export interface ImageSlotContract {
27
46
  outputs: SlotOutput[];
28
47
  formats: string[];
@@ -35,66 +54,66 @@ export interface SlotOutput {
35
54
  media?: string;
36
55
  }
37
56
 
38
- export const GradialImageSchema = z.object({
57
+ // ---------------------------------------------------------------------------
58
+ // Zod schema
59
+ // ---------------------------------------------------------------------------
60
+
61
+ export const ImageSourceSchema = z.object({
62
+ src: z.string().min(1),
63
+ width: z.number().int().nonnegative(),
64
+ height: z.number().int().nonnegative(),
65
+ type: z.string().min(1),
66
+ });
67
+
68
+ export const ImageSchema = z.object({
39
69
  $type: z.literal('gradial.image'),
40
70
  assetId: z.string().min(1),
41
71
  versionId: z.string().min(1),
42
72
  alt: z.string(),
43
- fallback: z.object({
44
- src: z.string().min(1),
45
- width: z.number().int().nonnegative(),
46
- height: z.number().int().nonnegative(),
47
- type: z.string().min(1),
48
- }),
49
- sources: z.array(z.object({
50
- media: z.string().optional(),
51
- type: z.string().min(1),
52
- srcset: z.string().min(1),
53
- sizes: z.string().optional(),
54
- })).nullable().default([]),
73
+ sources: z.array(ImageSourceSchema).min(1),
55
74
  });
56
75
 
57
- export type ImageHTMLAttributes = Record<string, string | number | boolean | undefined | null>;
76
+ // ---------------------------------------------------------------------------
77
+ // Shared attrs helper — consumed by React, Astro, and Svelte components
78
+ // ---------------------------------------------------------------------------
58
79
 
59
- export function renderImageHTML(image: GradialImage, attrs: ImageHTMLAttributes = {}): string {
60
- const imgAttrs = renderAttrs({
61
- ...attrs,
62
- src: image.fallback.src,
63
- alt: image.alt,
64
- width: image.fallback.width > 0 ? image.fallback.width : undefined,
65
- height: image.fallback.height > 0 ? image.fallback.height : undefined,
66
- });
67
- const img = `<img${imgAttrs}>`;
68
- if (!image.sources?.length) {
69
- return img;
70
- }
71
- const sources = image.sources.map((source) => `<source${renderAttrs({
72
- media: source.media,
73
- type: source.type,
74
- srcset: source.srcset,
75
- sizes: source.sizes,
76
- })}>`).join('');
77
- return `<picture>${sources}${img}</picture>`;
78
- }
80
+ export type ImageAttrs = Record<string, unknown>;
79
81
 
80
- function renderAttrs(attrs: ImageHTMLAttributes): string {
81
- const rendered = Object.entries(attrs)
82
- .filter(([, value]) => value !== undefined && value !== null && value !== false)
83
- .map(([name, value]) => value === true ? escapeName(name) : `${escapeName(name)}="${escapeAttr(String(value))}"`);
84
- if (!rendered.length) {
85
- return '';
86
- }
87
- return ' ' + rendered.join(' ');
82
+ /**
83
+ * Picks the primary variant from an image's sources.
84
+ * Uses the largest width; ties fall back to the first entry.
85
+ */
86
+ export function primaryImageSource(image: Image): ImageSource {
87
+ return image.sources.reduce((largest, source) =>
88
+ source.width > largest.width ? source : largest,
89
+ image.sources[0],
90
+ );
88
91
  }
89
92
 
90
- function escapeName(name: string): string {
91
- return name.replace(/[^A-Za-z0-9_:-]/g, '');
92
- }
93
+ /**
94
+ * Computes the full set of <img> attributes for an Image.
95
+ *
96
+ * Framework components (React <Picture>, Astro <img>, Svelte <img>) call this
97
+ * and spread the result into their native element. No HTML string serialization.
98
+ */
99
+ export function imageAttrs(image: Image, overrides: ImageAttrs = {}): ImageAttrs {
100
+ const primary = primaryImageSource(image);
101
+ const srcset = image.sources
102
+ .map((s) => `${s.src} ${s.width}w`)
103
+ .join(', ');
104
+ const sizes = image.sources
105
+ .map((s) => `(max-width: ${s.width}px) ${s.width}px`)
106
+ .join(', ');
93
107
 
94
- function escapeAttr(value: string): string {
95
- return value
96
- .replace(/&/g, '&amp;')
97
- .replace(/"/g, '&quot;')
98
- .replace(/</g, '&lt;')
99
- .replace(/>/g, '&gt;');
108
+ return {
109
+ src: primary.src,
110
+ alt: image.alt,
111
+ width: primary.width > 0 ? primary.width : undefined,
112
+ height: primary.height > 0 ? primary.height : undefined,
113
+ srcSet: srcset || undefined,
114
+ sizes: sizes || undefined,
115
+ loading: 'lazy',
116
+ decoding: 'async',
117
+ ...overrides,
118
+ };
100
119
  }
@@ -0,0 +1,27 @@
1
+ version: "1"
2
+ siteId: "site_astro"
3
+ framework: astro
4
+
5
+ source:
6
+ root: "./"
7
+ outDir: "dist"
8
+ publicDir: "public"
9
+
10
+ componentRegistry: ./src/cms/contracts/components/index.ts
11
+ layoutRegistry: ./src/cms/contracts/layouts/index.ts
12
+
13
+ capabilities:
14
+ staticRender: true
15
+ ssr: true
16
+ ssrIslands: true
17
+ clientIslands: true
18
+ fragmentRender: true
19
+
20
+ routes:
21
+ cmsManaged: "/[...slug]"
22
+ frameworkOwned:
23
+ - "/api/*"
24
+ - "/_astro/*"
25
+ - "/storybook/*"
26
+
27
+ rendererProtocol: stdio-json
@@ -0,0 +1,79 @@
1
+ import { dirname, resolve } from 'node:path';
2
+ import { fileURLToPath } from 'node:url';
3
+ import tailwindcss from '@tailwindcss/postcss';
4
+
5
+ const sharedRoot = fileURLToPath(new URL('../src', import.meta.url));
6
+
7
+ function withSharedAlias(alias: any) {
8
+ if (Array.isArray(alias)) {
9
+ return [...alias, { find: '@shared', replacement: sharedRoot }];
10
+ }
11
+
12
+ return {
13
+ ...alias,
14
+ '@shared': sharedRoot
15
+ };
16
+ }
17
+
18
+ const config = {
19
+ stories: ['../src/**/*.stories.@(ts|mdx)'],
20
+ addons: ['@storybook/addon-docs'],
21
+ framework: {
22
+ name: '@storybook-astro/framework',
23
+ options: {}
24
+ },
25
+ core: {
26
+ builder: '@storybook/builder-vite'
27
+ },
28
+ staticDirs: ['../public'],
29
+ viteFinal: async (config: any) => {
30
+ const astroComponentPaths = new Set<string>();
31
+ const buildServerPlugin = config.plugins?.flat().find(
32
+ (p: any) => p?.name === 'storybook-astro:build-server'
33
+ );
34
+
35
+ if (buildServerPlugin) {
36
+ const origWriteBundle = buildServerPlugin.writeBundle;
37
+ buildServerPlugin.writeBundle = async function(this: any, ...args: any[]) {
38
+ for (const componentPath of astroComponentPaths) {
39
+ buildServerPlugin.resolveId?.call(
40
+ this,
41
+ componentPath,
42
+ componentPath
43
+ );
44
+ }
45
+ return origWriteBundle?.apply(this, args);
46
+ };
47
+ }
48
+
49
+ config.plugins = [
50
+ {
51
+ name: 'storybook-astro:resolve-compat',
52
+ enforce: 'pre' as const,
53
+ resolveId(id: string, importer: string | undefined) {
54
+ if (id?.endsWith('.astro') && importer) {
55
+ astroComponentPaths.add(resolve(dirname(importer), id));
56
+ }
57
+ return undefined;
58
+ }
59
+ },
60
+ ...config.plugins
61
+ ];
62
+
63
+ return {
64
+ ...config,
65
+ resolve: {
66
+ ...config.resolve,
67
+ alias: withSharedAlias(config.resolve?.alias)
68
+ },
69
+ css: {
70
+ ...config.css,
71
+ postcss: {
72
+ plugins: [tailwindcss()]
73
+ }
74
+ }
75
+ };
76
+ }
77
+ };
78
+
79
+ export default config;
@@ -0,0 +1,6 @@
1
+ import { addons } from 'storybook/manager-api';
2
+ import { themes } from 'storybook/theming';
3
+
4
+ addons.setConfig({
5
+ theme: themes.dark
6
+ });
@@ -0,0 +1,33 @@
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
+ (storyFn: any, context: any) => {
17
+ const theme = context.globals.theme || 'light';
18
+ document.body.setAttribute('data-visual-root', '');
19
+ document.body.className = `theme-${theme} min-h-screen bg-[var(--color-surface-default)]`;
20
+ return storyFn();
21
+ }
22
+ ],
23
+ parameters: {
24
+ layout: 'fullscreen',
25
+ options: {
26
+ storySort: {
27
+ order: ['Core Atomic Blocks', 'Composition', '*']
28
+ }
29
+ }
30
+ }
31
+ };
32
+
33
+ export default preview;
@@ -0,0 +1,37 @@
1
+ import path from 'node:path';
2
+ import { defineConfig } from 'astro/config';
3
+ import node from '@astrojs/node';
4
+ import tailwindcss from '@tailwindcss/postcss';
5
+ import { withAci } from '@gradial/aci/astro';
6
+
7
+ const pageRuntimeBuild = process.env.ACI_CODE_ARTIFACT_BUILD === '1';
8
+ const rawBasePath = String(process.env.ACI_SITE_BASE_PATH || '').trim().replace(/\/+$/g, '');
9
+ const base = rawBasePath
10
+ ? rawBasePath.startsWith('/') ? rawBasePath : `/${rawBasePath}`
11
+ : '/';
12
+ const rawOutDir = String(process.env.ACI_ASTRO_OUT_DIR || '').trim();
13
+ const rawCacheDir = String(process.env.ACI_ASTRO_CACHE_DIR || '').trim();
14
+ const outDir = rawOutDir
15
+ ? (path.isAbsolute(rawOutDir) ? rawOutDir : `./${rawOutDir}`)
16
+ : 'dist';
17
+
18
+ export default defineConfig({
19
+ adapter: pageRuntimeBuild ? node({ mode: 'middleware' }) : undefined,
20
+ integrations: [withAci()],
21
+ srcDir: './src',
22
+ outDir,
23
+ cacheDir: rawCacheDir
24
+ ? (path.isAbsolute(rawCacheDir) ? rawCacheDir : `./${rawCacheDir}`)
25
+ : undefined,
26
+ output: pageRuntimeBuild ? 'server' : 'static',
27
+ trailingSlash: 'always',
28
+ base,
29
+ devToolbar: { enabled: false },
30
+ vite: {
31
+ css: {
32
+ postcss: {
33
+ plugins: [tailwindcss()],
34
+ },
35
+ },
36
+ },
37
+ });
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "{{siteName}}",
3
+ "version": "{{version}}",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "astro dev --host 0.0.0.0",
8
+ "build": "astro build",
9
+ "preview": "ACI_CONTENT_ROOT=./.aci/compiled astro preview --host 0.0.0.0",
10
+ "typecheck": "ACI_CONTENT_ROOT=./.aci/compiled astro check --tsconfig ./tsconfig.json",
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
+ "storybook": "storybook dev -p 6006",
17
+ "build:storybook": "storybook build"
18
+ },
19
+ "dependencies": {
20
+ "@gradial/aci": "^0.1.20",
21
+ "js-yaml": "^4.1.0",
22
+ "marked": "^18.0.3",
23
+ "zod": "^4.0.0"
24
+ },
25
+ "devDependencies": {
26
+ "@astrojs/check": "^0.9.8",
27
+ "@astrojs/node": "^10.1.4",
28
+ "@tailwindcss/postcss": "^4.2.4",
29
+ "@types/js-yaml": "^4.0.9",
30
+ "@types/node": "^25.8.0",
31
+ "astro": "^6.1.9",
32
+ "tailwindcss": "^4.2.4",
33
+ "tsx": "^4.20.0",
34
+ "typescript": "5.9.3",
35
+ "vite": "^7.2.7",
36
+ "storybook": "^10.0.0",
37
+ "@storybook-astro/framework": "^1.6.0",
38
+ "@storybook/addon-docs": "^10.0.0",
39
+ "@storybook/builder-vite": "^10.0.0"
40
+ }
41
+ }
@@ -0,0 +1,18 @@
1
+ import { defineLayoutContract, slot, fragmentDefault } from '@gradial/aci';
2
+
3
+ const layoutContracts = [
4
+ defineLayoutContract({
5
+ name: 'default',
6
+ slots: [
7
+ slot('header', false),
8
+ slot('main', true),
9
+ slot('footer', false),
10
+ ],
11
+ defaults: {
12
+ header: [fragmentDefault('navbar')],
13
+ footer: [fragmentDefault('site-footer')],
14
+ },
15
+ }),
16
+ ];
17
+
18
+ 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.astro';
11
+ import ContainerSection from '../components/sections/ContainerSection.astro';
12
+ import ButtonBlock from '../components/elements/ButtonBlock.astro';
13
+ import Header from '../components/chrome/Header.astro';
14
+ import Footer from '../components/chrome/Footer.astro';
15
+ import FeatureGrid from '../components/sections/FeatureGrid.astro';
16
+ import FeatureCard from '../components/sections/FeatureCard.astro';
17
+ import StatsBar from '../components/sections/StatsBar.astro';
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
+ ]);
@@ -0,0 +1,107 @@
1
+ ---
2
+ import Container from '../elements/Container.astro';
3
+ import Logo from '../elements/Logo.astro';
4
+
5
+ interface FooterLink {
6
+ id: string;
7
+ label: string;
8
+ href: string;
9
+ }
10
+
11
+ interface FooterGroup {
12
+ id: string;
13
+ title: string;
14
+ links: FooterLink[];
15
+ }
16
+
17
+ interface FooterColumn {
18
+ id: string;
19
+ title?: string;
20
+ groups?: FooterGroup[];
21
+ links?: FooterLink[];
22
+ }
23
+
24
+ interface Props {
25
+ brandHref: string;
26
+ brandLabel?: string;
27
+ brandLogo?: { src: string; alt?: string } | null;
28
+ social?: Array<FooterLink & { icon?: string }>;
29
+ columns?: FooterColumn[];
30
+ legalLinks?: FooterLink[];
31
+ copyright?: string;
32
+ }
33
+
34
+ const {
35
+ brandHref,
36
+ brandLabel = 'Gradial',
37
+ brandLogo = null,
38
+ social = [],
39
+ columns = [],
40
+ legalLinks = [],
41
+ copyright = ''
42
+ } = Astro.props;
43
+
44
+ function columnGroups(column: FooterColumn): FooterGroup[] {
45
+ if (column.groups?.length) {
46
+ return column.groups;
47
+ }
48
+
49
+ return [{
50
+ id: column.id,
51
+ title: column.title || '',
52
+ links: column.links || []
53
+ }];
54
+ }
55
+ ---
56
+
57
+ <footer class="bm-footer [font-family:var(--font-family-base)] text-fg" id="footer">
58
+ <Container className="py-[var(--spacing-footer-y)]">
59
+ <div class="grid gap-12 pb-24 md:grid-cols-[var(--footer-grid-columns)] lg:gap-24">
60
+ <a href={brandHref} class="self-start">
61
+ <Logo
62
+ label={brandLabel}
63
+ src={brandLogo?.src || ''}
64
+ alt={brandLogo?.alt || brandLabel}
65
+ className="w-[var(--width-logo-footer-mobile)] md:w-[var(--width-logo-footer)]"
66
+ />
67
+ </a>
68
+ <div class="grid gap-10 sm:grid-cols-2 lg:grid-cols-4">
69
+ {columns.map((column) => (
70
+ <div class="space-y-10">
71
+ {columnGroups(column).map((group) => (
72
+ <section class="space-y-5" aria-label={group.title}>
73
+ <h2 class="text-[length:var(--text-size-body-small)] font-[var(--font-weight-semibold)] text-fg-muted">{group.title}</h2>
74
+ <ul class="space-y-4 text-[length:var(--text-size-body-small)] font-[var(--font-weight-semibold)] leading-body text-fg">
75
+ {group.links.map((link) => (
76
+ <li><a class="transition hover:text-link-hover" href={link.href}>{link.label}</a></li>
77
+ ))}
78
+ </ul>
79
+ </section>
80
+ ))}
81
+ </div>
82
+ ))}
83
+ <section class="space-y-5" aria-label="Social">
84
+ <h2 class="text-[length:var(--text-size-body-small)] font-[var(--font-weight-semibold)] text-fg-muted">Social</h2>
85
+ <ul class="space-y-4 text-[length:var(--text-size-body-small)] font-[var(--font-weight-semibold)] text-fg">
86
+ {social.map((item) => (
87
+ <li>
88
+ <a class="inline-flex items-center gap-2 transition hover:text-link-hover" href={item.href}>
89
+ <span class="min-w-5 text-[length:var(--text-size-caption)] text-fg-muted">{item.icon || item.label.slice(0, 1)}</span>
90
+ <span>{item.label}</span>
91
+ </a>
92
+ </li>
93
+ ))}
94
+ </ul>
95
+ </section>
96
+ </div>
97
+ </div>
98
+ <div class="flex flex-col gap-6 border-t border-edge pt-8 text-[length:var(--text-size-caption)] font-[var(--font-weight-semibold)] text-fg-muted md:flex-row md:items-center md:justify-between">
99
+ <ul class="flex flex-wrap gap-x-9 gap-y-3 text-fg">
100
+ {legalLinks.map((link) => (
101
+ <li><a class="transition hover:text-link-hover" href={link.href}>{link.label}</a></li>
102
+ ))}
103
+ </ul>
104
+ <p>{copyright}</p>
105
+ </div>
106
+ </Container>
107
+ </footer>
@@ -0,0 +1,94 @@
1
+ ---
2
+ import Button from '../elements/Button.astro';
3
+ import Container from '../elements/Container.astro';
4
+ import Icon from '../elements/Icon.astro';
5
+ import Logo from '../elements/Logo.astro';
6
+
7
+ interface NavItem {
8
+ id: string;
9
+ label: string;
10
+ href: string;
11
+ }
12
+
13
+ interface UtilityItem extends NavItem {
14
+ variant?: 'primary' | 'secondary' | 'outline' | 'ghost';
15
+ }
16
+
17
+ interface Props {
18
+ brandHref: string;
19
+ brandLabel?: string;
20
+ brandLogo?: { src: string; alt?: string } | null;
21
+ navigation?: NavItem[];
22
+ utilities?: UtilityItem[];
23
+ }
24
+
25
+ const {
26
+ brandHref,
27
+ brandLabel = 'Gradial',
28
+ brandLogo = null,
29
+ navigation = [],
30
+ utilities = []
31
+ } = Astro.props;
32
+ ---
33
+
34
+ <header class="bm-header sticky top-0 z-[var(--z-header)] [font-family:var(--font-family-base)] text-fg">
35
+ <a
36
+ href="#content-main"
37
+ class="sr-only focus:not-sr-only focus:absolute focus:left-4 focus:top-4 focus:z-10 focus:rounded-button focus:bg-white focus:px-4 focus:py-2 focus:text-black"
38
+ >
39
+ Skip to main content
40
+ </a>
41
+ <Container className="flex min-h-[var(--height-header)] items-center justify-between gap-4">
42
+ <a href={brandHref} class="shrink-0">
43
+ <Logo label={brandLabel} src={brandLogo?.src || ''} alt={brandLogo?.alt || brandLabel} />
44
+ </a>
45
+ <nav class="hidden items-center gap-9 text-[length:var(--text-size-body-small)] font-[var(--font-weight-semibold)] text-fg-muted lg:flex" aria-label="Primary">
46
+ {navigation.map((item) => (
47
+ <a class="transition hover:text-link-hover" href={item.href}>{item.label}</a>
48
+ ))}
49
+ </nav>
50
+ <div class="flex shrink-0 items-center gap-3">
51
+ <div class="hidden items-center gap-3 sm:flex">
52
+ {utilities.map((item) => (
53
+ <Button href={item.href} variant={item.variant || 'outline'}>{item.label}</Button>
54
+ ))}
55
+ </div>
56
+ <div class="lg:hidden">
57
+ <input id="site-mobile-nav" class="peer sr-only" type="checkbox" />
58
+ <label
59
+ for="site-mobile-nav"
60
+ class="inline-grid size-11 cursor-pointer place-items-center rounded-button border border-edge text-fg transition hover:text-link-hover"
61
+ aria-label="Open main navigation"
62
+ >
63
+ <Icon name="menu" className="size-6" />
64
+ </label>
65
+ <div class="bm-mobile-nav pointer-events-none fixed inset-0 z-[var(--z-overlay)] flex translate-x-full flex-col px-[var(--spacing-container)] py-5 text-fg opacity-0 transition duration-300 ease-out peer-checked:pointer-events-auto peer-checked:translate-x-0 peer-checked:opacity-100">
66
+ <div class="flex min-h-[var(--height-header)] items-center justify-between gap-4">
67
+ <a href={brandHref} class="shrink-0">
68
+ <Logo label={brandLabel} src={brandLogo?.src || ''} alt={brandLogo?.alt || brandLabel} />
69
+ </a>
70
+ <label
71
+ for="site-mobile-nav"
72
+ class="inline-grid size-11 cursor-pointer place-items-center rounded-button border border-edge text-fg transition hover:text-link-hover"
73
+ aria-label="Close main navigation"
74
+ >
75
+ <Icon name="close" className="size-6" />
76
+ </label>
77
+ </div>
78
+ <nav class="grid flex-1 content-center gap-4 py-10 font-[var(--font-weight-bold)] text-[length:var(--mobile-nav-link-size)] leading-[var(--mobile-nav-link-line-height)] text-fg" aria-label="Mobile main navigation">
79
+ {navigation.map((item) => (
80
+ <a class="transition hover:text-link-hover" href={item.href}>{item.label}</a>
81
+ ))}
82
+ </nav>
83
+ {utilities.length ? (
84
+ <div class="grid gap-3 border-t border-edge pt-5 sm:hidden">
85
+ {utilities.map((item) => (
86
+ <Button href={item.href} variant={item.variant || 'outline'} className="w-full">{item.label}</Button>
87
+ ))}
88
+ </div>
89
+ ) : null}
90
+ </div>
91
+ </div>
92
+ </div>
93
+ </Container>
94
+ </header>
@@ -0,0 +1,15 @@
1
+ ---
2
+ import type { KernelAtomicBlock } from '@gradial/aci/content';
3
+ import ButtonBlock from './ButtonBlock.astro';
4
+
5
+ interface Props {
6
+ block: KernelAtomicBlock;
7
+ }
8
+
9
+ const { block } = Astro.props;
10
+ const props = (block.props || {}) as any;
11
+ ---
12
+
13
+ {block.component === 'button' ? (
14
+ <ButtonBlock {...props} />
15
+ ) : null}