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