@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,74 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type { ComponentContract } from './component.js';
|
|
3
|
+
|
|
4
|
+
const BLOCK_REF_KEY = '_gradialBlockRef';
|
|
5
|
+
|
|
6
|
+
export interface BlockRefMeta {
|
|
7
|
+
allowed: readonly string[];
|
|
8
|
+
multiple: boolean;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const blockRefBase = z.object({
|
|
12
|
+
id: z.string().min(1),
|
|
13
|
+
component: z.string().min(1),
|
|
14
|
+
props: z.record(z.string(), z.unknown()),
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
type InferContractProps<T> = T extends ComponentContract<string, infer Schema>
|
|
18
|
+
? Schema extends z.ZodType<infer P> ? P : Record<string, unknown>
|
|
19
|
+
: Record<string, unknown>;
|
|
20
|
+
|
|
21
|
+
type InferContractId<T> = T extends ComponentContract<infer Id, unknown> ? Id : never;
|
|
22
|
+
|
|
23
|
+
export type BlockRefEntry<T extends ComponentContract> = {
|
|
24
|
+
id: string;
|
|
25
|
+
component: InferContractId<T>;
|
|
26
|
+
props: InferContractProps<T>;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export type BlockRefUnion<T extends readonly ComponentContract[]> = {
|
|
30
|
+
[K in keyof T]: BlockRefEntry<T[K]>;
|
|
31
|
+
}[number];
|
|
32
|
+
|
|
33
|
+
function attachMeta<T extends z.ZodTypeAny>(
|
|
34
|
+
schema: T,
|
|
35
|
+
allowed: readonly string[],
|
|
36
|
+
multiple: boolean,
|
|
37
|
+
): T {
|
|
38
|
+
Object.defineProperty(schema, BLOCK_REF_KEY, {
|
|
39
|
+
value: { allowed, multiple } satisfies BlockRefMeta,
|
|
40
|
+
enumerable: false,
|
|
41
|
+
writable: false,
|
|
42
|
+
configurable: false,
|
|
43
|
+
});
|
|
44
|
+
return schema;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function blockRefArray<const T extends readonly ComponentContract[]>(
|
|
48
|
+
contracts: T,
|
|
49
|
+
): z.ZodArray<z.ZodType<BlockRefUnion<T>>> {
|
|
50
|
+
return attachMeta(
|
|
51
|
+
z.array(blockRefBase) as unknown as z.ZodArray<z.ZodType<BlockRefUnion<T>>>,
|
|
52
|
+
contracts.map((c) => c.id),
|
|
53
|
+
true,
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function blockRef<const T extends readonly ComponentContract[]>(
|
|
58
|
+
contracts: T,
|
|
59
|
+
): z.ZodType<BlockRefUnion<T>> {
|
|
60
|
+
return attachMeta(
|
|
61
|
+
blockRefBase as unknown as z.ZodType<BlockRefUnion<T>>,
|
|
62
|
+
contracts.map((c) => c.id),
|
|
63
|
+
false,
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function getBlockRefMeta(schema: unknown): BlockRefMeta | null {
|
|
68
|
+
if (schema && typeof schema === 'object' && BLOCK_REF_KEY in schema) {
|
|
69
|
+
return (schema as Record<string, unknown>)[BLOCK_REF_KEY] as BlockRefMeta;
|
|
70
|
+
}
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export type BlockRef = z.infer<typeof blockRefBase>;
|
package/src/types/image.ts
CHANGED
|
@@ -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
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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
|
|
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
|
-
|
|
23
|
-
sources:
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
76
|
+
// ---------------------------------------------------------------------------
|
|
77
|
+
// Shared attrs helper — consumed by React, Astro, and Svelte components
|
|
78
|
+
// ---------------------------------------------------------------------------
|
|
58
79
|
|
|
59
|
-
export
|
|
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
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
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
|
-
|
|
91
|
-
|
|
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
|
-
|
|
95
|
-
|
|
96
|
-
.
|
|
97
|
-
.
|
|
98
|
-
.
|
|
99
|
-
|
|
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,28 @@
|
|
|
1
|
+
version: "1"
|
|
2
|
+
siteId: "site_astro"
|
|
3
|
+
domain: "meridian.local"
|
|
4
|
+
framework: astro
|
|
5
|
+
|
|
6
|
+
source:
|
|
7
|
+
root: "./"
|
|
8
|
+
outDir: "dist"
|
|
9
|
+
publicDir: "public"
|
|
10
|
+
|
|
11
|
+
componentRegistry: ./src/cms/contracts/components/index.ts
|
|
12
|
+
layoutRegistry: ./src/cms/contracts/layouts/index.ts
|
|
13
|
+
|
|
14
|
+
capabilities:
|
|
15
|
+
staticRender: true
|
|
16
|
+
ssr: true
|
|
17
|
+
ssrIslands: true
|
|
18
|
+
clientIslands: true
|
|
19
|
+
fragmentRender: true
|
|
20
|
+
|
|
21
|
+
routes:
|
|
22
|
+
cmsManaged: "/[...slug]"
|
|
23
|
+
frameworkOwned:
|
|
24
|
+
- "/api/*"
|
|
25
|
+
- "/_astro/*"
|
|
26
|
+
- "/storybook/*"
|
|
27
|
+
|
|
28
|
+
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,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>
|