@getvitops/generator 0.4.0 → 0.6.0
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/dist/index.d.mts +20 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +628 -219
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
- package/schema.json +99 -34
- package/site.schema.json +223 -66
package/dist/index.mjs
CHANGED
|
@@ -13,17 +13,25 @@ import { generateOklchPalette, hexToOklch, oklchStringToHex, pickOn } from "@get
|
|
|
13
13
|
* Authored with `zod/mini` (tree-shakeable Zod v4). Everything else derives from it:
|
|
14
14
|
* • the `DesignSystem` TS type (`z.infer`), used throughout the generator,
|
|
15
15
|
* • the published JSON Schema (`toJSONSchema`), emitted to `schema.json` for `$schema` refs,
|
|
16
|
-
* • runtime validation (`validate`) for the CLI and to gate `generate
|
|
16
|
+
* • runtime validation (`validate`) for the CLI and to gate `generate`,
|
|
17
|
+
* • the generated `authoring.md` reference doc (walks `jsonSchema`'s descriptions).
|
|
17
18
|
*/
|
|
18
|
-
|
|
19
|
+
/**
|
|
20
|
+
* Attach a JSON-Schema `description`. zod/mini has no `.describe()` method; the
|
|
21
|
+
* mini-API equivalent is `.check(z.describe(…))`, and `.check()` clones — so a
|
|
22
|
+
* shared sub-schema (`Scale`, `CssDecls`, …) can carry a different description
|
|
23
|
+
* per use-site without mutating the original.
|
|
24
|
+
*/
|
|
25
|
+
const desc = (schema, text) => schema.check(z.describe(text));
|
|
26
|
+
const CssDecls = desc(z.record(z.string(), z.string()), "A CSS declaration block: property → value. Values stay strings (they can be hex, var(), clamp(), keywords, …); the generator, not the schema, interprets them.");
|
|
19
27
|
const Scalar = z.union([z.string(), z.number()]);
|
|
20
28
|
const VarMap = z.record(z.string(), Scalar);
|
|
21
|
-
const SeededRamp = z.object({
|
|
22
|
-
seed: z.string(),
|
|
23
|
-
anchors: z.optional(z.record(z.string(), z.string()))
|
|
24
|
-
});
|
|
25
|
-
const FixedRamp = z.object({ tones: z.union([z.array(z.string()), z.record(z.string(), z.string())]) });
|
|
26
|
-
const Ramp = z.union([SeededRamp, FixedRamp]);
|
|
29
|
+
const SeededRamp = desc(z.object({
|
|
30
|
+
seed: desc(z.string(), "Seed colour (hex or oklch()). An 11-step numeric scale (50…950, tinted near-white → tinted near-black) is generated in OKLCH from it; the seed is preserved at its natural step."),
|
|
31
|
+
anchors: z.optional(desc(z.record(z.string(), z.string()), "Step → colour overrides (hex or oklch()) that pin specific steps of the generated scale; the remaining steps interpolate around them."))
|
|
32
|
+
}), "Seeded hue: the 11-step scale is GENERATED in OKLCH from `seed` (anchors pin specific steps).");
|
|
33
|
+
const FixedRamp = desc(z.object({ tones: desc(z.union([z.array(z.string()), z.record(z.string(), z.string())]), "Fixed brand kit: authored tones placed verbatim at their nearest steps plus tinted off-white/off-black endpoints; no interpolation. Either an ordered light → dark array or a step → colour map.") }), "Fixed hue: authored brand tones used verbatim; no generation.");
|
|
34
|
+
const Ramp = desc(z.union([SeededRamp, FixedRamp]), "A palette hue, authored one of two ways: `{ seed, anchors? }` generates an 11-step numeric OKLCH scale (50…950) from the seed, or `{ tones }` supplies a fixed brand kit used verbatim.");
|
|
27
35
|
const UtilityName = z.enum([
|
|
28
36
|
"bg",
|
|
29
37
|
"text",
|
|
@@ -32,66 +40,66 @@ const UtilityName = z.enum([
|
|
|
32
40
|
"fill",
|
|
33
41
|
"stroke"
|
|
34
42
|
]);
|
|
35
|
-
const Colors = z.object({
|
|
36
|
-
palette: z.record(z.string(), Ramp),
|
|
37
|
-
roles: z.record(z.string(), z.string()),
|
|
38
|
-
utilities: z.optional(z.array(UtilityName))
|
|
39
|
-
});
|
|
43
|
+
const Colors = desc(z.object({
|
|
44
|
+
palette: desc(z.record(z.string(), Ramp), "Palette hues by name. Each becomes an 11-step numeric OKLCH scale (`--color-<hue>-50…950`)."),
|
|
45
|
+
roles: desc(z.record(z.string(), z.string()), "Maps each semantic role (neutral, surface, ui-primary/secondary/accent, brand-primary/secondary, info, success, warning, danger) to the palette hue backing it. The generator derives every role's FUNCTIONAL tokens (`--<role>-{bg,bg-muted,border,border-bold,solid,solid-bold,on-solid,text,text-muted,text-x-muted}`) plus emphasis stops from the hue's scale, and dark mode flips automatically — there is no per-appearance scheme grammar."),
|
|
46
|
+
utilities: z.optional(desc(z.array(UtilityName), "Which colour utility-class families to emit (`bg-*`, `text-*`, `border-*`, `outline-*`, `fill-*`, `stroke-*`). Defaults to bg, text, border."))
|
|
47
|
+
}), "The colour system (the only required section): `palette` hues become generated OKLCH scales; `roles` map semantic roles onto those hues, from which all functional tokens and dark mode derive.");
|
|
40
48
|
const Scale = z.object({
|
|
41
|
-
base: z.string(),
|
|
42
|
-
ratio: z.number(),
|
|
43
|
-
steps: z.optional(z.number()),
|
|
44
|
-
names: z.optional(z.array(z.string())),
|
|
45
|
-
baseStep: z.optional(z.number()),
|
|
46
|
-
baseline: z.optional(z.string()),
|
|
47
|
-
fluid: z.optional(z.object({
|
|
48
|
-
minVw: z.string(),
|
|
49
|
-
maxVw: z.string(),
|
|
50
|
-
minRatio: z.number()
|
|
51
|
-
}))
|
|
52
|
-
});
|
|
53
|
-
const Pattern = z.object({
|
|
54
|
-
group: z.optional(z.string()),
|
|
55
|
-
overrides: z.optional(CssDecls),
|
|
56
|
-
element: z.optional(z.string()),
|
|
57
|
-
class: z.optional(z.string()),
|
|
58
|
-
default_role: z.optional(z.string()),
|
|
59
|
-
base: z.optional(CssDecls),
|
|
60
|
-
states: z.optional(z.record(z.string(), z.record(z.string(), z.unknown()))),
|
|
61
|
-
roles: z.optional(z.array(z.string()))
|
|
62
|
-
});
|
|
63
|
-
const Patterns = z.object({
|
|
64
|
-
defaults: z.optional(CssDecls),
|
|
65
|
-
radii: z.optional(CssDecls),
|
|
66
|
-
groups: z.optional(z.record(z.string(), CssDecls)),
|
|
67
|
-
z: z.optional(z.record(z.string(), z.number())),
|
|
68
|
-
items: z.optional(z.record(z.string(), Pattern))
|
|
69
|
-
});
|
|
70
|
-
const TypographyRole = z.record(z.string(), Scalar);
|
|
71
|
-
const Typography = z.object({
|
|
72
|
-
families: z.optional(z.record(z.string(), z.string())),
|
|
73
|
-
roles: z.optional(z.record(z.string(), TypographyRole)),
|
|
74
|
-
headings: z.optional(z.record(z.string(), z.string()))
|
|
75
|
-
});
|
|
76
|
-
const AnimEffect = z.object({
|
|
77
|
-
kf: z.string(),
|
|
78
|
-
css: z.optional(VarMap),
|
|
79
|
-
vars: z.optional(VarMap)
|
|
80
|
-
});
|
|
81
|
-
const Animations = z.object({
|
|
82
|
-
effects: z.optional(z.record(z.string(), AnimEffect)),
|
|
83
|
-
journeys: z.optional(z.object({
|
|
84
|
-
base: z.optional(z.record(z.string(), VarMap)),
|
|
85
|
-
compose: z.optional(z.array(z.array(z.string())))
|
|
86
|
-
}))
|
|
49
|
+
base: desc(z.string(), "Anchor size (a CSS length, e.g. \"1rem\") — the value at `baseStep`."),
|
|
50
|
+
ratio: desc(z.number(), "Modular ratio between adjacent steps at large viewports."),
|
|
51
|
+
steps: desc(z.optional(z.number()), "Token count when `names` is absent (steps are then named 1..steps)."),
|
|
52
|
+
names: desc(z.optional(z.array(z.string())), "Step names, smallest → largest (e.g. [\"xs\",\"sm\",\"md\",…]); each becomes a token suffix."),
|
|
53
|
+
baseStep: desc(z.optional(z.number()), "1-based index of the step whose value is `base`."),
|
|
54
|
+
baseline: desc(z.optional(z.string()), "Named step used as the fluid pivot / GUI scale centre (defaults to `baseStep`)."),
|
|
55
|
+
fluid: desc(z.optional(z.object({
|
|
56
|
+
minVw: desc(z.string(), "Viewport width (CSS length) where fluid scaling bottoms out."),
|
|
57
|
+
maxVw: desc(z.string(), "Viewport width (CSS length) where fluid scaling tops out."),
|
|
58
|
+
minRatio: desc(z.number(), "Modular ratio at/below `minVw` (usually < `ratio`).")
|
|
59
|
+
})), "Makes the scale fluid: each step compiles to a clamp() that interpolates from `minRatio` at `minVw` to `ratio` at `maxVw`.")
|
|
87
60
|
});
|
|
61
|
+
const Pattern = desc(z.object({
|
|
62
|
+
group: desc(z.optional(z.string()), "Token-cascade group this pattern belongs to (e.g. tag / control / panel); base declarations resolve through `--<prop>-<group>` before `--<prop>-default`."),
|
|
63
|
+
overrides: z.optional(desc(CssDecls, "Per-pattern token overrides, emitted as `--<prop>-<name>-group` values.")),
|
|
64
|
+
element: desc(z.optional(z.string()), "Style at element level via zero-specificity `:where(<element>)` (instead of, or alongside, a class)."),
|
|
65
|
+
class: desc(z.optional(z.string()), "Class name to emit (defaults to the pattern's key)."),
|
|
66
|
+
default_role: desc(z.optional(z.string()), "Semantic colour role applied to the bare/default variant."),
|
|
67
|
+
base: z.optional(desc(CssDecls, "Base CSS declarations. Geometry properties (padding, border-radius, border, box-shadow, font-size) are wrapped in per-pattern override hooks (`--p-<name>`, `--br-<name>`, `--b-<name>`, `--ds-<name>`, `--fs-<name>`) so consumers can restyle one pattern by setting one variable.")),
|
|
68
|
+
states: desc(z.optional(z.record(z.string(), z.record(z.string(), z.unknown()))), "Interaction states (hover / active / focus-visible), each a map of shortcuts: `step` (intensify fills/text by n emphasis stops), `scale` (transform scale), `lift` (translateY + shadow), `shadow` (a shadow name → drop-shadow(var(--shadow-<name>)), or true → lift shadow), `ring` (focus ring), or raw `css` declarations. Hover rules are wrapped in `@media (hover: hover)`."),
|
|
69
|
+
roles: desc(z.optional(z.array(z.string())), "Semantic colour role variants to emit as `<pattern>-<role>` classes (fills use the role solid / on-solid tokens).")
|
|
70
|
+
}), "One component pattern (button, link, badge, card, …): base declarations + interaction states + semantic role variants, resolved through the pattern token cascade.");
|
|
71
|
+
const Patterns = desc(z.object({
|
|
72
|
+
defaults: z.optional(desc(CssDecls, "Cascade-wide fallback tokens, emitted as `--<prop>-default`.")),
|
|
73
|
+
radii: z.optional(desc(CssDecls, "Shape primitives, emitted as `--br-<name>` (referenced by pattern bases).")),
|
|
74
|
+
groups: desc(z.optional(z.record(z.string(), CssDecls)), "Group-level tokens, emitted as `--<prop>-<group>`; patterns opt in via their `group` key."),
|
|
75
|
+
z: desc(z.optional(z.record(z.string(), z.number())), "Z-index tiers → `--z-tier-<name>`."),
|
|
76
|
+
items: desc(z.optional(z.record(z.string(), Pattern)), "The component patterns to emit, keyed by name.")
|
|
77
|
+
}), "Component patterns and their token cascade: `defaults` → `groups` → per-pattern `overrides`, plus shape (`radii`) and z-index primitives.");
|
|
78
|
+
const TypographyRole = desc(z.record(z.string(), Scalar), "An open bag of CSS-ish keys (family / size / weight / line-height / tracking / transform / decoration / text-wrap / …); the generator maps known keys and passes the rest through.");
|
|
79
|
+
const Typography = desc(z.object({
|
|
80
|
+
families: desc(z.optional(z.record(z.string(), z.string())), "Role-facing family aliases → CSS font values, usually referencing the top-level `fonts` tokens (e.g. \"var(--font-display)\")."),
|
|
81
|
+
roles: desc(z.optional(z.record(z.string(), TypographyRole)), "Semantic type roles (display, title, heading, body, quote, caption, eyebrow, code, lead, footnote, tag, …), each emitted as a `font-<role>` class."),
|
|
82
|
+
headings: desc(z.optional(z.record(z.string(), z.string())), "Maps heading elements (h1…h6) to type roles so bare headings pick up role styling.")
|
|
83
|
+
}), "Typography: family aliases, semantic type roles (→ `font-<role>` classes), and the heading-element → role mapping.");
|
|
84
|
+
const AnimEffect = desc(z.object({
|
|
85
|
+
kf: desc(z.string(), "Keyframe family driving the effect: composite (transform/opacity), paint, or layout."),
|
|
86
|
+
css: z.optional(desc(VarMap, "Extra literal declarations merged into the effect class as-is.")),
|
|
87
|
+
vars: z.optional(desc(VarMap, "Effect endpoint variables (`--<key>: <value>`, e.g. opacity-from, translate-y-to) that override the keyframe defaults."))
|
|
88
|
+
}), "A named animation effect class — a pure value layer (`--_anim` + `--<prop>-from/-to`) over the static keyframe engine.");
|
|
89
|
+
const Animations = desc(z.object({
|
|
90
|
+
effects: desc(z.optional(z.record(z.string(), AnimEffect)), "Effect classes to emit (`.fade-in`, `.reveal-left`, …), keyed by class name."),
|
|
91
|
+
journeys: desc(z.optional(z.object({
|
|
92
|
+
base: desc(z.optional(z.record(z.string(), VarMap)), "Named journey building blocks: part name → var map."),
|
|
93
|
+
compose: desc(z.optional(z.array(z.array(z.string()))), "Combinations of base parts, each emitted as a `.<parts>-journey` class.")
|
|
94
|
+
})), "Multi-part journey classes composed from `base` building blocks.")
|
|
95
|
+
}), "Animation effect + journey classes (pure value layers). The animation engine itself — keyframes, drivers, floats, utilities — is static framework CSS, not configured here.");
|
|
88
96
|
const DesignSystemSchema = z.object({
|
|
89
|
-
$schema: z.optional(z.string()),
|
|
97
|
+
$schema: desc(z.optional(z.string()), "URL of the published JSON Schema (stamped by `vitops init`) so editors provide autocomplete + validation."),
|
|
90
98
|
colors: Colors,
|
|
91
|
-
shadows: z.optional(z.record(z.string(), z.string())),
|
|
92
|
-
fonts: z.optional(z.record(z.string(), z.string())),
|
|
93
|
-
typeScale: z.optional(Scale),
|
|
94
|
-
spaceScale: z.optional(Scale),
|
|
99
|
+
shadows: desc(z.optional(z.record(z.string(), z.string())), "Named shadows → `--shadow-<name>` tokens and `.drop-shadow-<name>` utilities. Values are shadow parameter lists (offset/blur/colour), applied via `filter: drop-shadow(…)`."),
|
|
100
|
+
fonts: desc(z.optional(z.record(z.string(), z.string())), "Raw font stacks by name, emitted as `--font-<name>` tokens (referenced by `typography.families`)."),
|
|
101
|
+
typeScale: desc(z.optional(Scale), "Fluid modular TYPE scale → `--text-<name>` tokens, consumed by typography roles and text-size utilities."),
|
|
102
|
+
spaceScale: desc(z.optional(Scale), "Fluid modular SPACE scale → `--space-<name>` tokens, consumed by spacing/gap utilities and vertical rhythm."),
|
|
95
103
|
patterns: z.optional(Patterns),
|
|
96
104
|
typography: z.optional(Typography),
|
|
97
105
|
animations: z.optional(Animations)
|
|
@@ -142,6 +150,89 @@ function validate(input) {
|
|
|
142
150
|
};
|
|
143
151
|
}
|
|
144
152
|
|
|
153
|
+
//#endregion
|
|
154
|
+
//#region src/shared.ts
|
|
155
|
+
/**
|
|
156
|
+
* Data shared between the generator (`generate.ts`) and the docs bundle
|
|
157
|
+
* (`docs.ts`). Lives in its own module because `generate.ts` imports `docs.ts` —
|
|
158
|
+
* importing back the other way would be a cycle.
|
|
159
|
+
*/
|
|
160
|
+
/**
|
|
161
|
+
* Pattern base geometry props → per-pattern override-hook shorthand. Each hook
|
|
162
|
+
* wraps the authored value (`padding: var(--p-button, 0.6em 1.2em)`) so a
|
|
163
|
+
* consumer restyles one pattern by setting one variable.
|
|
164
|
+
*/
|
|
165
|
+
const BASE_HOOK = {
|
|
166
|
+
padding: "p",
|
|
167
|
+
"border-radius": "br",
|
|
168
|
+
border: "b",
|
|
169
|
+
"box-shadow": "ds",
|
|
170
|
+
"font-size": "fs"
|
|
171
|
+
};
|
|
172
|
+
/**
|
|
173
|
+
* Framework utility classes whose names collide with Tailwind's own defaults.
|
|
174
|
+
* The `tailwind` format strips any rule targeting these — Tailwind provides
|
|
175
|
+
* them, so consumers use Tailwind's utilities for these names instead.
|
|
176
|
+
*/
|
|
177
|
+
const TW_CLASH = new Set([
|
|
178
|
+
"block",
|
|
179
|
+
"inline",
|
|
180
|
+
"inline-block",
|
|
181
|
+
"flow-root",
|
|
182
|
+
"flex",
|
|
183
|
+
"inline-flex",
|
|
184
|
+
"grid",
|
|
185
|
+
"inline-grid",
|
|
186
|
+
"table",
|
|
187
|
+
"inline-table",
|
|
188
|
+
"table-caption",
|
|
189
|
+
"table-cell",
|
|
190
|
+
"table-row",
|
|
191
|
+
"contents",
|
|
192
|
+
"hidden",
|
|
193
|
+
"list-item",
|
|
194
|
+
"static",
|
|
195
|
+
"fixed",
|
|
196
|
+
"absolute",
|
|
197
|
+
"relative",
|
|
198
|
+
"sticky",
|
|
199
|
+
"isolate",
|
|
200
|
+
"visible",
|
|
201
|
+
"invisible",
|
|
202
|
+
"collapse",
|
|
203
|
+
"sr-only",
|
|
204
|
+
"not-sr-only",
|
|
205
|
+
"flex-row",
|
|
206
|
+
"flex-row-reverse",
|
|
207
|
+
"flex-col",
|
|
208
|
+
"flex-col-reverse",
|
|
209
|
+
"flex-wrap",
|
|
210
|
+
"flex-nowrap",
|
|
211
|
+
"items-start",
|
|
212
|
+
"items-end",
|
|
213
|
+
"items-center",
|
|
214
|
+
"items-baseline",
|
|
215
|
+
"items-stretch",
|
|
216
|
+
"justify-start",
|
|
217
|
+
"justify-end",
|
|
218
|
+
"justify-center",
|
|
219
|
+
"justify-between",
|
|
220
|
+
"justify-around",
|
|
221
|
+
"justify-evenly",
|
|
222
|
+
"content-start",
|
|
223
|
+
"content-end",
|
|
224
|
+
"content-center",
|
|
225
|
+
"content-between",
|
|
226
|
+
"content-around",
|
|
227
|
+
"content-evenly",
|
|
228
|
+
"text-left",
|
|
229
|
+
"text-center",
|
|
230
|
+
"text-right",
|
|
231
|
+
"text-justify",
|
|
232
|
+
"text-start",
|
|
233
|
+
"text-end"
|
|
234
|
+
]);
|
|
235
|
+
|
|
145
236
|
//#endregion
|
|
146
237
|
//#region src/tokens.ts
|
|
147
238
|
/**
|
|
@@ -822,6 +913,11 @@ This is a **rule reference**, not an exhaustive list: each family below is a nam
|
|
|
822
913
|
plus the set of tokens it expands over. Applying a rule to any listed token yields a valid
|
|
823
914
|
class (e.g. rule \`bg-<color>\` + colour \`pine-xl\` → \`bg-pine-xl\`).
|
|
824
915
|
|
|
916
|
+
The tokens themselves are authored in \`design-system.json\` — see
|
|
917
|
+
[/authoring.md](../authoring.md); the systems behind them are explained in
|
|
918
|
+
[/concepts/](../concepts/index.md); per-format output differences (including which of
|
|
919
|
+
these utilities Tailwind provides natively) in [/formats.md](../formats.md).
|
|
920
|
+
|
|
825
921
|
## Responsive & state variant grammar
|
|
826
922
|
|
|
827
923
|
Every utility accepts a **container-breakpoint prefix**; animation utilities also accept a
|
|
@@ -911,7 +1007,9 @@ element; scroll-linked entrances resolve within the first portion of the element
|
|
|
911
1007
|
## Component patterns
|
|
912
1008
|
|
|
913
1009
|
Each pattern is a base class \`<pattern>\` with interaction states (hover/active/focus-visible)
|
|
914
|
-
baked in; coloured patterns add role variants via rule **\`<pattern>-<role>\`**.
|
|
1010
|
+
baked in; coloured patterns add role variants via rule **\`<pattern>-<role>\`**. (How the
|
|
1011
|
+
pattern CSS is assembled — token cascade, \`--p-<pattern>\`-style override hooks, state
|
|
1012
|
+
shortcuts — is explained in [/concepts/patterns.md](../concepts/patterns.md).)
|
|
915
1013
|
|
|
916
1014
|
- Patterns: ${code(patterns)}.
|
|
917
1015
|
- Roles (for coloured patterns like \`badge\`, \`tag\`, \`status\`, \`button\`):
|
|
@@ -920,6 +1018,368 @@ baked in; coloured patterns add role variants via rule **\`<pattern>-<role>\`**.
|
|
|
920
1018
|
- Shape primitives: \`--br-<name>\` radii — ${code(radii)}.
|
|
921
1019
|
`;
|
|
922
1020
|
}
|
|
1021
|
+
const isPrimitive = (n) => !n.properties && !n.anyOf && n.type != null && n.type !== "object" && n.type !== "array";
|
|
1022
|
+
function typeLabel(node) {
|
|
1023
|
+
if (node.enum) return node.enum.map(String).join(" | ");
|
|
1024
|
+
if (node.anyOf) return node.anyOf.every(isPrimitive) ? node.anyOf.map((v) => v.type).join(" | ") : "one of";
|
|
1025
|
+
if (node.type === "array") {
|
|
1026
|
+
const it = node.items;
|
|
1027
|
+
if (it?.enum) return `array of ${it.enum.map(String).join(" | ")}`;
|
|
1028
|
+
return it?.type && it.type !== "object" ? `array of ${it.type}` : "array";
|
|
1029
|
+
}
|
|
1030
|
+
if (node.type === "object") {
|
|
1031
|
+
const ap = node.additionalProperties;
|
|
1032
|
+
return ap && typeof ap === "object" ? "map" : "object";
|
|
1033
|
+
}
|
|
1034
|
+
return node.type ?? "value";
|
|
1035
|
+
}
|
|
1036
|
+
/** A node's documentable children: object properties, map values, or array items. */
|
|
1037
|
+
function childEntries(node) {
|
|
1038
|
+
if (node.properties) {
|
|
1039
|
+
const req = node.required ?? [];
|
|
1040
|
+
return Object.entries(node.properties).map(([k, v]) => ({
|
|
1041
|
+
label: k,
|
|
1042
|
+
node: v,
|
|
1043
|
+
required: req.includes(k)
|
|
1044
|
+
}));
|
|
1045
|
+
}
|
|
1046
|
+
const ap = node.additionalProperties;
|
|
1047
|
+
if (ap && typeof ap === "object" && (ap.description || ap.properties || ap.anyOf)) return [{
|
|
1048
|
+
label: "<name>",
|
|
1049
|
+
node: ap,
|
|
1050
|
+
required: false
|
|
1051
|
+
}];
|
|
1052
|
+
if (node.items && (node.items.description || node.items.properties || node.items.anyOf)) return [{
|
|
1053
|
+
label: "[items]",
|
|
1054
|
+
node: node.items,
|
|
1055
|
+
required: false
|
|
1056
|
+
}];
|
|
1057
|
+
return [];
|
|
1058
|
+
}
|
|
1059
|
+
function renderSchemaNode(label, node, required, depth, out) {
|
|
1060
|
+
const indent = " ".repeat(depth);
|
|
1061
|
+
const meta = `(${typeLabel(node)}${required ? ", required" : ""})`;
|
|
1062
|
+
out.push(`${indent}- \`${label}\` ${meta}${node.description ? ` — ${node.description}` : ""}`);
|
|
1063
|
+
if (depth >= 3) return;
|
|
1064
|
+
if (node.anyOf && !node.anyOf.every(isPrimitive)) {
|
|
1065
|
+
for (const variant of node.anyOf) {
|
|
1066
|
+
out.push(`${indent} - *one of*${variant.description ? ` — ${variant.description}` : ""}`);
|
|
1067
|
+
for (const c of childEntries(variant)) renderSchemaNode(c.label, c.node, c.required, depth + 2, out);
|
|
1068
|
+
}
|
|
1069
|
+
return;
|
|
1070
|
+
}
|
|
1071
|
+
for (const c of childEntries(node)) renderSchemaNode(c.label, c.node, c.required, depth + 1, out);
|
|
1072
|
+
}
|
|
1073
|
+
function renderAuthoring() {
|
|
1074
|
+
const schema = jsonSchema;
|
|
1075
|
+
const required = schema.required ?? [];
|
|
1076
|
+
const sections = [];
|
|
1077
|
+
for (const [key, node] of Object.entries(schema.properties ?? {})) {
|
|
1078
|
+
if (key === "$schema") continue;
|
|
1079
|
+
const parts = [`## \`${key}\`${required.includes(key) ? "" : " *(optional)*"}`];
|
|
1080
|
+
if (node.description) parts.push("", node.description);
|
|
1081
|
+
const bullets = [];
|
|
1082
|
+
for (const c of childEntries(node)) renderSchemaNode(c.label, c.node, c.required, 0, bullets);
|
|
1083
|
+
if (bullets.length) parts.push("", ...bullets);
|
|
1084
|
+
sections.push(parts.join("\n"));
|
|
1085
|
+
}
|
|
1086
|
+
return `${frontmatter({
|
|
1087
|
+
type: "Config Reference",
|
|
1088
|
+
title: "Vitops — design-system.json authoring reference",
|
|
1089
|
+
description: "Every field of the design-system.json config, generated from the published JSON Schema so it always matches validation.",
|
|
1090
|
+
resource: DS_PATH,
|
|
1091
|
+
tags: [
|
|
1092
|
+
"config",
|
|
1093
|
+
"schema",
|
|
1094
|
+
"authoring",
|
|
1095
|
+
"design-system"
|
|
1096
|
+
]
|
|
1097
|
+
})}
|
|
1098
|
+
|
|
1099
|
+
# \`design-system.json\` authoring reference
|
|
1100
|
+
|
|
1101
|
+
The single source of truth every output format is generated from. Each consumer authors
|
|
1102
|
+
their own config — there is no shared canonical token set. The field docs below are
|
|
1103
|
+
rendered from the published JSON Schema, so they always match what \`vitops validate\`
|
|
1104
|
+
enforces.
|
|
1105
|
+
|
|
1106
|
+
- Set \`"$schema": "${SCHEMA_URL}"\` in the config for editor autocomplete + validation.
|
|
1107
|
+
- Scaffold a starter config with \`vitops init\`; check one with \`vitops validate\`.
|
|
1108
|
+
- The *why* behind each section: [colour system](concepts/color.md),
|
|
1109
|
+
[type & space scales](concepts/scales.md), [component patterns](concepts/patterns.md).
|
|
1110
|
+
- What each output format does with these tokens: [formats.md](formats.md).
|
|
1111
|
+
|
|
1112
|
+
${sections.join("\n\n")}
|
|
1113
|
+
`;
|
|
1114
|
+
}
|
|
1115
|
+
function renderFormats(ds) {
|
|
1116
|
+
const clashList = Array.from(TW_CLASH).sort();
|
|
1117
|
+
const spaceName = ds.spaceScale?.baseline ?? ds.spaceScale?.names?.[0] ?? "m";
|
|
1118
|
+
return `${frontmatter({
|
|
1119
|
+
type: "Formats Reference",
|
|
1120
|
+
title: "Vitops — output formats (tailwind vs css vs bricks)",
|
|
1121
|
+
description: "What each vitops generate format emits, what the target platform provides instead, and the Tailwind-specific rules (which framework utilities are stripped in favour of Tailwind defaults).",
|
|
1122
|
+
resource: DS_PATH,
|
|
1123
|
+
tags: [
|
|
1124
|
+
"formats",
|
|
1125
|
+
"tailwind",
|
|
1126
|
+
"bricks",
|
|
1127
|
+
"css",
|
|
1128
|
+
"design-system"
|
|
1129
|
+
]
|
|
1130
|
+
})}
|
|
1131
|
+
|
|
1132
|
+
# Output formats — \`tailwind\` vs \`css\` vs \`bricks\`
|
|
1133
|
+
|
|
1134
|
+
One config, three targets: \`vitops generate --format <tailwind|css|bricks>\`. The **class
|
|
1135
|
+
vocabulary is the same** everywhere (see [css/classes.md](css/classes.md)); what differs is
|
|
1136
|
+
which layers the generator emits versus which the platform provides, and the variant
|
|
1137
|
+
separator (\`-\` in CSS/Bricks, \`:\` / \`@\` in Tailwind).
|
|
1138
|
+
|
|
1139
|
+
## \`tailwind\` — single-file Tailwind v4 layer
|
|
1140
|
+
|
|
1141
|
+
Emits one self-contained \`tailwind.css\` (plus \`tokens.json\`): \`@import "tailwindcss"\`,
|
|
1142
|
+
\`@theme\` tokens, the framework's structural CSS + component patterns inlined, and
|
|
1143
|
+
\`@utility\` definitions for the bespoke families (type roles, animation effects,
|
|
1144
|
+
split ratios, track placement).
|
|
1145
|
+
|
|
1146
|
+
**Use Tailwind's own utilities for these class names.** The framework's rules for them are
|
|
1147
|
+
deliberately stripped from the bundle because Tailwind provides them natively — writing
|
|
1148
|
+
them still works, but they are Tailwind's, not the framework's:
|
|
1149
|
+
|
|
1150
|
+
${clashList.map((c) => `\`${c}\``).join(", ")}
|
|
1151
|
+
|
|
1152
|
+
Other Tailwind-specific behaviour:
|
|
1153
|
+
|
|
1154
|
+
- **Variants are Tailwind's job.** No pre-expanded breakpoint/state classes are emitted
|
|
1155
|
+
(the framework's \`@container (min-width: …)\` variant blocks are dropped); use Tailwind
|
|
1156
|
+
syntax — \`@md:split-1-2\`, \`hover:flip-fade-in\`. Container breakpoints are registered as
|
|
1157
|
+
\`--container-{sm,md,lg,xl}\` = 30/48/64/80rem, backing the \`@sm:\`…\`@xl:\` variants.
|
|
1158
|
+
- **The space scale is NOT mapped into Tailwind's \`--spacing-*\` namespace** (that would
|
|
1159
|
+
corrupt Tailwind's numeric multipliers and \`max-w-*\` sizes). Numeric utilities like
|
|
1160
|
+
\`p-4\` keep Tailwind's 0.25rem meaning; use the design-system scale via arbitrary
|
|
1161
|
+
values — \`p-(--space-${spaceName})\`, \`gap-(--space-${spaceName})\`.
|
|
1162
|
+
- **Functional colour roles are plain \`:root\` variables, not \`@theme\` colours**, so
|
|
1163
|
+
Tailwind doesn't auto-derive utilities from them; the emitted \`@utility\` set
|
|
1164
|
+
(\`bg-<role>\`, \`text-<role>-muted\`, …) is the public API. The raw hue scales ARE
|
|
1165
|
+
\`@theme\` colours, so native \`bg-<hue>-500\`-style utilities work.
|
|
1166
|
+
|
|
1167
|
+
## \`css\` — standalone bundle
|
|
1168
|
+
|
|
1169
|
+
Emits a bundled, self-contained \`styles.css\` + \`tokens.json\` + \`design-manifest.json\`.
|
|
1170
|
+
The colour and font/scale layers are fully included, and every utility family is
|
|
1171
|
+
pre-expanded — including breakpoint/state variants with \`-\` separators
|
|
1172
|
+
(\`md-split-1-2\`, \`hover-fade-in\`). For non-Bricks, non-Tailwind consumers and the
|
|
1173
|
+
docs build.
|
|
1174
|
+
|
|
1175
|
+
## \`bricks\` — WordPress / Bricks Builder payload
|
|
1176
|
+
|
|
1177
|
+
Emits the full deployable theme payload: \`styles.min.css\`, the Bricks import JSONs
|
|
1178
|
+
(\`bricks-colors-{named,semantic}.json\`, \`bricks-variables.json\`), \`tokens.json\`, the JS
|
|
1179
|
+
bundles (polyfills / elements / deferred), the Bricks element PHP under \`bricks/\`, and
|
|
1180
|
+
this docs bundle under \`docs/\`.
|
|
1181
|
+
|
|
1182
|
+
- **Bricks provides the token layer.** \`color.css\` and \`type-tokens.css\` are one-line
|
|
1183
|
+
stubs: the colour \`:root\` tokens, dark-mode overrides, colour utility classes, fonts,
|
|
1184
|
+
and type/space scales are generated live by Bricks' Color / Font / Variables Managers
|
|
1185
|
+
from the imported JSONs. Semantic palette entries carry \`darkEnabled\` + a \`dark\` ref so
|
|
1186
|
+
Bricks emits the dark-mode overrides on import.
|
|
1187
|
+
- Everything else (patterns, shadows, typography roles, animation effects, structural
|
|
1188
|
+
framework CSS) ships in \`styles.min.css\` as in the other formats.
|
|
1189
|
+
- Pattern states reference shadows by name, compiled to
|
|
1190
|
+
\`filter: drop-shadow(var(--shadow-<name>))\`.
|
|
1191
|
+
`;
|
|
1192
|
+
}
|
|
1193
|
+
function renderColorConcept(ds) {
|
|
1194
|
+
const hues = Object.keys(ds.colors.palette ?? {});
|
|
1195
|
+
const roles = Object.keys(ds.colors.roles ?? {});
|
|
1196
|
+
return `${frontmatter({
|
|
1197
|
+
type: "Design Concept",
|
|
1198
|
+
title: "Vitops colour system — seeded scales, functional tokens, automatic dark mode",
|
|
1199
|
+
description: "How palette hues become 11-step OKLCH scales, how semantic roles derive functional tokens from them, and how dark mode flips automatically.",
|
|
1200
|
+
resource: DS_PATH,
|
|
1201
|
+
tags: [
|
|
1202
|
+
"color",
|
|
1203
|
+
"oklch",
|
|
1204
|
+
"dark-mode",
|
|
1205
|
+
"design-system"
|
|
1206
|
+
]
|
|
1207
|
+
})}
|
|
1208
|
+
|
|
1209
|
+
# Colour system
|
|
1210
|
+
|
|
1211
|
+
Authoring is two maps in \`design-system.json\` (see [authoring.md](../authoring.md)):
|
|
1212
|
+
\`colors.palette\` (hues) and \`colors.roles\` (semantic role → hue). Everything else —
|
|
1213
|
+
scales, functional tokens, utilities, dark mode — is derived.
|
|
1214
|
+
|
|
1215
|
+
## From seed to scale
|
|
1216
|
+
|
|
1217
|
+
Each palette hue becomes an **11-step numeric OKLCH scale**, \`--color-<hue>-50…950\`,
|
|
1218
|
+
running tinted near-white → tinted near-black. Two authoring modes:
|
|
1219
|
+
|
|
1220
|
+
- **Seeded** (\`{ seed, anchors? }\`): the scale is generated from one colour. The seed is
|
|
1221
|
+
preserved at its natural lightness step; \`anchors\` pin specific steps and the rest
|
|
1222
|
+
interpolate around them.
|
|
1223
|
+
- **Fixed** (\`{ tones }\`): a brand kit used verbatim — each authored tone lands at its
|
|
1224
|
+
nearest step, endpoints are tinted off-white/off-black, no interpolation.
|
|
1225
|
+
|
|
1226
|
+
Current hues: ${code(hues)}.
|
|
1227
|
+
|
|
1228
|
+
## Functional tokens (the public vocabulary)
|
|
1229
|
+
|
|
1230
|
+
Each role in \`colors.roles\` (currently ${code(roles)}) derives a family of
|
|
1231
|
+
**job-named** tokens from its hue's scale:
|
|
1232
|
+
|
|
1233
|
+
- \`--<role>-bg\`, \`--<role>-bg-muted\` — background washes.
|
|
1234
|
+
- \`--<role>-border\`, \`--<role>-border-bold\` — borders.
|
|
1235
|
+
- \`--<role>-solid\`, \`--<role>-solid-bold\` — opaque fills (buttons, badges), paired with
|
|
1236
|
+
\`--<role>-on-solid\` for a guaranteed-contrast foreground.
|
|
1237
|
+
- \`--<role>-text\`, \`--<role>-text-muted\`, \`--<role>-text-x-muted\` — content colours.
|
|
1238
|
+
- **Emphasis stops** \`--color-<role>-{x-muted,muted,bold,x-bold}\` — appearance-relative:
|
|
1239
|
+
\`muted\` recedes toward the background extreme and \`bold\` advances toward the
|
|
1240
|
+
foreground, in *either* light or dark.
|
|
1241
|
+
- Plus \`--surface-glass\` (translucent surface) and \`--overlay\` (scrim).
|
|
1242
|
+
|
|
1243
|
+
Classes name the token, not the tone: \`bg-<role>\`, \`text-<role>-muted\`, \`text-on-<role>\`,
|
|
1244
|
+
\`border-<role>\` (see [css/classes.md](../css/classes.md)).
|
|
1245
|
+
|
|
1246
|
+
## Automatic dark mode
|
|
1247
|
+
|
|
1248
|
+
Dark mode is a **functional flip** under \`:root[data-brx-theme="dark"]\`: background and
|
|
1249
|
+
text ends of each scale swap, while \`solid\` fills stay mode-stable with a recomputed
|
|
1250
|
+
\`on-solid\` foreground. There is no per-appearance scheme grammar to author and no named
|
|
1251
|
+
steps — a role token means the same *job* in both appearances.
|
|
1252
|
+
|
|
1253
|
+
## Contrast guarantees
|
|
1254
|
+
|
|
1255
|
+
Text tokens target APCA Lc ≥ 75 and muted text Lc ≥ 60, in **both** appearances —
|
|
1256
|
+
enforced by the generator's unit tests, not left to the author.
|
|
1257
|
+
`;
|
|
1258
|
+
}
|
|
1259
|
+
function renderScalesConcept(ds) {
|
|
1260
|
+
const t = ds.typeScale;
|
|
1261
|
+
const s = ds.spaceScale;
|
|
1262
|
+
const scaleLine = (label, sc) => sc ? `- **${label}**: base \`${sc.base}\` at step \`${sc.names?.[(sc.baseStep ?? 1) - 1] ?? sc.baseStep}\`, ratio ${sc.ratio}${sc.fluid ? ` (${sc.fluid.minRatio} below ${sc.fluid.minVw})` : ""}; steps ${code(sc.names ?? [])}.` : `- **${label}**: not configured.`;
|
|
1263
|
+
return `${frontmatter({
|
|
1264
|
+
type: "Design Concept",
|
|
1265
|
+
title: "Vitops type & space scales — fluid modular scales",
|
|
1266
|
+
description: "How typeScale and spaceScale compile to clamp()-based fluid modular scales, and which utilities consume the resulting tokens.",
|
|
1267
|
+
resource: DS_PATH,
|
|
1268
|
+
tags: [
|
|
1269
|
+
"typography",
|
|
1270
|
+
"spacing",
|
|
1271
|
+
"fluid",
|
|
1272
|
+
"modular-scale",
|
|
1273
|
+
"design-system"
|
|
1274
|
+
]
|
|
1275
|
+
})}
|
|
1276
|
+
|
|
1277
|
+
# Type & space scales
|
|
1278
|
+
|
|
1279
|
+
Both scales share one model (authored in \`design-system.json\` as \`typeScale\` /
|
|
1280
|
+
\`spaceScale\` — see [authoring.md](../authoring.md)): a **fluid modular scale**.
|
|
1281
|
+
|
|
1282
|
+
## The model
|
|
1283
|
+
|
|
1284
|
+
- \`base\` anchors the value at \`baseStep\`; every other step is a power of the \`ratio\`
|
|
1285
|
+
away (step n = base × ratio^(n − baseStep)).
|
|
1286
|
+
- With \`fluid\`, each step compiles to a **\`clamp()\`**: the scale uses \`fluid.minRatio\`
|
|
1287
|
+
at/below \`fluid.minVw\` and grows to \`ratio\` at \`fluid.maxVw\`, interpolating between —
|
|
1288
|
+
so large steps spread apart on wide viewports and compress on phones, with no
|
|
1289
|
+
breakpoint jumps. \`baseline\` names the pivot step that stays closest to \`base\`.
|
|
1290
|
+
- \`names\` become the token suffixes.
|
|
1291
|
+
|
|
1292
|
+
${scaleLine("Type", t)}
|
|
1293
|
+
${scaleLine("Space", s)}
|
|
1294
|
+
|
|
1295
|
+
## What consumes them
|
|
1296
|
+
|
|
1297
|
+
- **Type**: \`--text-<name>\` tokens → typography role sizes (\`font-<role>\` classes,
|
|
1298
|
+
heading defaults) and text-size utilities.
|
|
1299
|
+
- **Space**: \`--space-<name>\` tokens → gap (\`g\`), spacing utilities, and \`rhythm\`
|
|
1300
|
+
(relationship-based vertical margins between headings, paragraphs, lists, media).
|
|
1301
|
+
Prefer \`rhythm\` for vertical flow over per-element margins.
|
|
1302
|
+
- In the \`tailwind\` format these stay in their own namespaces (\`--text-*\` via \`@theme\`,
|
|
1303
|
+
\`--space-*\` as plain vars) — see [formats.md](../formats.md) for why \`--spacing-*\` is
|
|
1304
|
+
deliberately not used.
|
|
1305
|
+
`;
|
|
1306
|
+
}
|
|
1307
|
+
function renderPatternsConcept(ds) {
|
|
1308
|
+
const items = ds.patterns?.items ?? {};
|
|
1309
|
+
const groups = Object.keys(ds.patterns?.groups ?? {});
|
|
1310
|
+
const patternNames = Object.keys(items);
|
|
1311
|
+
const hookTable = Object.entries(BASE_HOOK).map(([prop, sfx]) => `| \`${prop}\` | \`--${sfx}-<pattern>\` |`).join("\n");
|
|
1312
|
+
return `${frontmatter({
|
|
1313
|
+
type: "Design Concept",
|
|
1314
|
+
title: "Vitops component patterns — token cascade, override hooks, states",
|
|
1315
|
+
description: "How pattern CSS is assembled: the token cascade (defaults → groups → per-pattern overrides), per-pattern override-hook variables, state shortcuts, and role variants.",
|
|
1316
|
+
resource: DS_PATH,
|
|
1317
|
+
tags: [
|
|
1318
|
+
"patterns",
|
|
1319
|
+
"components",
|
|
1320
|
+
"cascade",
|
|
1321
|
+
"css",
|
|
1322
|
+
"design-system"
|
|
1323
|
+
]
|
|
1324
|
+
})}
|
|
1325
|
+
|
|
1326
|
+
# Component patterns — the CSS chain
|
|
1327
|
+
|
|
1328
|
+
Patterns (currently ${code(patternNames)}) are authored declaratively under \`patterns\` in
|
|
1329
|
+
\`design-system.json\` (see [authoring.md](../authoring.md)) and compiled to CSS in the
|
|
1330
|
+
**\`components\` cascade layer** — so utility classes (declared in a later layer) always win
|
|
1331
|
+
over pattern styling without specificity fights.
|
|
1332
|
+
|
|
1333
|
+
## 1 — Token cascade
|
|
1334
|
+
|
|
1335
|
+
Shared geometry resolves through a variable chain, most-specific first:
|
|
1336
|
+
|
|
1337
|
+
- \`patterns.defaults\` → \`--<prop>-default\` (cascade-wide fallbacks).
|
|
1338
|
+
- \`patterns.groups.<group>\` → \`--<prop>-<group>\` (e.g. groups ${code(groups)}); a pattern
|
|
1339
|
+
opts in via its \`group\` key.
|
|
1340
|
+
- Per-pattern: each grouped pattern gets aliases \`--<prop>-<name>-group\` →
|
|
1341
|
+
\`var(--<prop>-<group>)\`, and \`overrides\` replace individual aliases with literal values.
|
|
1342
|
+
- \`patterns.radii\` → \`--br-<name>\` shape primitives; \`patterns.z\` → \`--z-tier-<name>\`.
|
|
1343
|
+
|
|
1344
|
+
## 2 — Base declarations & override hooks
|
|
1345
|
+
|
|
1346
|
+
Each pattern's \`base\` block is emitted on its selector — a class (\`.<class ?? name>\`) or,
|
|
1347
|
+
with \`element\`, a **zero-specificity** \`:where(<element>)\` rule so author CSS can always
|
|
1348
|
+
override it. Geometry properties are wrapped in a **per-pattern override hook**:
|
|
1349
|
+
|
|
1350
|
+
| base property | hook variable |
|
|
1351
|
+
| --- | --- |
|
|
1352
|
+
${hookTable}
|
|
1353
|
+
|
|
1354
|
+
e.g. \`padding: var(--p-button, 0.6em 1.2em)\` — a consumer restyles every button by setting
|
|
1355
|
+
\`--p-button\` on \`:root\`, without touching the pattern.
|
|
1356
|
+
|
|
1357
|
+
## 3 — States
|
|
1358
|
+
|
|
1359
|
+
\`states\` (hover / active / focus-visible) compile from shortcuts:
|
|
1360
|
+
|
|
1361
|
+
- \`step: n\` — intensify the pattern's colour one emphasis stop: fills swap
|
|
1362
|
+
\`--<role>-solid\` → \`--<role>-solid-bold\`; text patterns swap the \`bold\` emphasis stop →
|
|
1363
|
+
\`x-bold\`.
|
|
1364
|
+
- \`scale: 0.97\` — transform scale; \`lift: "<length>"\` — \`translate: 0 calc(-1 * <length>)\`.
|
|
1365
|
+
- \`shadow: "<name>"\` — \`filter: drop-shadow(var(--shadow-<name>))\`; \`shadow: true\` — the
|
|
1366
|
+
generic lift box-shadow.
|
|
1367
|
+
- \`ring: true\` — focus ring (\`box-shadow\` in the role's \`muted\` stop, outline removed).
|
|
1368
|
+
- \`css: { … }\` — raw declarations escape hatch.
|
|
1369
|
+
|
|
1370
|
+
Any pattern with states also gets a composed transition block (translate / scale / filter /
|
|
1371
|
+
box-shadow / colours, \`--interact-duration\` / \`--interact-easing\` overridable). Hover
|
|
1372
|
+
rules are wrapped in \`@media (hover: hover)\` so touch devices never stick.
|
|
1373
|
+
|
|
1374
|
+
## 4 — Role variants
|
|
1375
|
+
|
|
1376
|
+
\`roles\` lists semantic colour variants: class patterns emit \`.<pattern>-<role>\`
|
|
1377
|
+
(\`.badge-success\`), element patterns emit \`<element>.<role>\` (\`button.danger\`). Fill
|
|
1378
|
+
patterns set \`background-color: var(--<role>-solid)\` + \`color: var(--<role>-on-solid)\`;
|
|
1379
|
+
text patterns use the role's \`bold\` emphasis stop. \`default_role\` colours the bare,
|
|
1380
|
+
unsuffixed pattern. States re-apply per variant with the variant's role.
|
|
1381
|
+
`;
|
|
1382
|
+
}
|
|
923
1383
|
function renderTopIndex() {
|
|
924
1384
|
return `${INDEX_NOTE}
|
|
925
1385
|
|
|
@@ -931,10 +1391,25 @@ variable-driven CSS framework plus progressively-enhanced web components, genera
|
|
|
931
1391
|
|
|
932
1392
|
# Contents
|
|
933
1393
|
|
|
1394
|
+
* [Authoring reference](authoring.md) - every design-system.json field, generated from the JSON Schema
|
|
1395
|
+
* [Output formats](formats.md) - tailwind vs css vs bricks: what's emitted, what the platform provides, which utilities Tailwind owns
|
|
1396
|
+
* [Concepts](concepts/) - the colour system, type/space scales, and pattern CSS architecture
|
|
934
1397
|
* [CSS framework](css/) - the class vocabulary (colour, type, space, layout, animation, component patterns), stated as naming rules
|
|
935
1398
|
* [Bricks Builder](bricks/) - custom elements and how to style them
|
|
936
1399
|
`;
|
|
937
1400
|
}
|
|
1401
|
+
function renderConceptsIndex() {
|
|
1402
|
+
return `${INDEX_NOTE}
|
|
1403
|
+
|
|
1404
|
+
# Design-system concepts
|
|
1405
|
+
|
|
1406
|
+
# Contents
|
|
1407
|
+
|
|
1408
|
+
* [Colour system](color.md) - seeded OKLCH scales, functional tokens, automatic dark mode
|
|
1409
|
+
* [Type & space scales](scales.md) - fluid modular scales and the tokens they emit
|
|
1410
|
+
* [Component patterns](patterns.md) - token cascade, override hooks, states, role variants
|
|
1411
|
+
`;
|
|
1412
|
+
}
|
|
938
1413
|
function renderCssIndex() {
|
|
939
1414
|
return `${INDEX_NOTE}
|
|
940
1415
|
|
|
@@ -974,6 +1449,12 @@ function generateDocs(ds, assetsDir) {
|
|
|
974
1449
|
const elementsDir = join(assetsDir, "bricks", "elements");
|
|
975
1450
|
return {
|
|
976
1451
|
"index.md": renderTopIndex(),
|
|
1452
|
+
"authoring.md": renderAuthoring(),
|
|
1453
|
+
"formats.md": renderFormats(ds),
|
|
1454
|
+
"concepts/index.md": renderConceptsIndex(),
|
|
1455
|
+
"concepts/color.md": renderColorConcept(ds),
|
|
1456
|
+
"concepts/scales.md": renderScalesConcept(ds),
|
|
1457
|
+
"concepts/patterns.md": renderPatternsConcept(ds),
|
|
977
1458
|
"css/index.md": renderCssIndex(),
|
|
978
1459
|
"css/classes.md": renderCssClasses(ds),
|
|
979
1460
|
"bricks/index.md": renderBricksIndex(),
|
|
@@ -1094,13 +1575,6 @@ const STATE_DEFAULT = {
|
|
|
1094
1575
|
"hue-rotate": "0deg",
|
|
1095
1576
|
clip: "none"
|
|
1096
1577
|
};
|
|
1097
|
-
const BASE_HOOK = {
|
|
1098
|
-
padding: "p",
|
|
1099
|
-
"border-radius": "br",
|
|
1100
|
-
border: "b",
|
|
1101
|
-
"box-shadow": "ds",
|
|
1102
|
-
"font-size": "fs"
|
|
1103
|
-
};
|
|
1104
1578
|
const id = (key) => createHash("sha256").update(key).digest("hex").slice(0, 6);
|
|
1105
1579
|
function build(ds, format, assetsDir) {
|
|
1106
1580
|
const BRICKS = format === "bricks";
|
|
@@ -1721,64 +2195,6 @@ body {
|
|
|
1721
2195
|
const animEngine = readFileSync(join(cssDir, "animation.css"), "utf8");
|
|
1722
2196
|
parts.push(`/* ── Animation engine (animation.css) ── */\n@layer components {\n${animEngine}\n}`);
|
|
1723
2197
|
parts.push(`/* ── Component patterns ── */\n@layer components {\n${patternsCss}\n}`);
|
|
1724
|
-
const TW_CLASH = new Set([
|
|
1725
|
-
"block",
|
|
1726
|
-
"inline",
|
|
1727
|
-
"inline-block",
|
|
1728
|
-
"flow-root",
|
|
1729
|
-
"flex",
|
|
1730
|
-
"inline-flex",
|
|
1731
|
-
"grid",
|
|
1732
|
-
"inline-grid",
|
|
1733
|
-
"table",
|
|
1734
|
-
"inline-table",
|
|
1735
|
-
"table-caption",
|
|
1736
|
-
"table-cell",
|
|
1737
|
-
"table-row",
|
|
1738
|
-
"contents",
|
|
1739
|
-
"hidden",
|
|
1740
|
-
"list-item",
|
|
1741
|
-
"static",
|
|
1742
|
-
"fixed",
|
|
1743
|
-
"absolute",
|
|
1744
|
-
"relative",
|
|
1745
|
-
"sticky",
|
|
1746
|
-
"isolate",
|
|
1747
|
-
"visible",
|
|
1748
|
-
"invisible",
|
|
1749
|
-
"collapse",
|
|
1750
|
-
"sr-only",
|
|
1751
|
-
"not-sr-only",
|
|
1752
|
-
"flex-row",
|
|
1753
|
-
"flex-row-reverse",
|
|
1754
|
-
"flex-col",
|
|
1755
|
-
"flex-col-reverse",
|
|
1756
|
-
"flex-wrap",
|
|
1757
|
-
"flex-nowrap",
|
|
1758
|
-
"items-start",
|
|
1759
|
-
"items-end",
|
|
1760
|
-
"items-center",
|
|
1761
|
-
"items-baseline",
|
|
1762
|
-
"items-stretch",
|
|
1763
|
-
"justify-start",
|
|
1764
|
-
"justify-end",
|
|
1765
|
-
"justify-center",
|
|
1766
|
-
"justify-between",
|
|
1767
|
-
"justify-around",
|
|
1768
|
-
"justify-evenly",
|
|
1769
|
-
"content-start",
|
|
1770
|
-
"content-end",
|
|
1771
|
-
"content-center",
|
|
1772
|
-
"content-between",
|
|
1773
|
-
"content-around",
|
|
1774
|
-
"content-evenly",
|
|
1775
|
-
"text-left",
|
|
1776
|
-
"text-center",
|
|
1777
|
-
"text-right",
|
|
1778
|
-
"text-justify",
|
|
1779
|
-
"text-start",
|
|
1780
|
-
"text-end"
|
|
1781
|
-
]);
|
|
1782
2198
|
const droppedClashes = /* @__PURE__ */ new Set();
|
|
1783
2199
|
let droppedVariantBlocks = 0;
|
|
1784
2200
|
const stripForTailwind = (css) => {
|
|
@@ -1960,8 +2376,7 @@ async function generate(options) {
|
|
|
1960
2376
|
* Pure: no YAML/env/fs. `resolveSiteConfig` takes an already-parsed object; a
|
|
1961
2377
|
* thin loader (reading YAML, injecting `SITE_ENV`) lives outside this module.
|
|
1962
2378
|
*/
|
|
1963
|
-
|
|
1964
|
-
const LocalizableSchema = z.union([z.string(), z.record(z.string(), z.string())]);
|
|
2379
|
+
const LocalizableSchema = desc(z.union([z.string(), z.record(z.string(), z.string())]), "A string, or a per-locale map of strings (`{ en: \"…\", fr: \"…\" }`).");
|
|
1965
2380
|
const ImageRefSchema = z.union([z.string(), z.object({
|
|
1966
2381
|
src: z.string(),
|
|
1967
2382
|
alt: z.string(),
|
|
@@ -1986,13 +2401,13 @@ const DayOfWeek = z.enum([
|
|
|
1986
2401
|
"Sunday"
|
|
1987
2402
|
]);
|
|
1988
2403
|
const DesignSystemEntry = z.extend(DesignSystemPatchSchema, {
|
|
1989
|
-
displayName: z.optional(LocalizableSchema),
|
|
1990
|
-
extends: z.optional(z.string())
|
|
2404
|
+
displayName: desc(z.optional(LocalizableSchema), "Human-readable theme name (localisable)."),
|
|
2405
|
+
extends: desc(z.optional(z.string()), "Another `designSystem` key to inherit from; this entry then only supplies what it overrides.")
|
|
1991
2406
|
});
|
|
1992
2407
|
const LocaleSchema = z.object({
|
|
1993
|
-
name: z.string(),
|
|
1994
|
-
tagline: z.optional(z.string()),
|
|
1995
|
-
basePath: z.optional(z.string())
|
|
2408
|
+
name: desc(z.string(), "Display name of the locale (e.g. \"English\")."),
|
|
2409
|
+
tagline: desc(z.optional(z.string()), "Site tagline in this locale."),
|
|
2410
|
+
basePath: desc(z.optional(z.string()), "URL prefix for this locale (e.g. \"/fr\").")
|
|
1996
2411
|
});
|
|
1997
2412
|
const HttpRedirect = z.union([
|
|
1998
2413
|
z.literal(301),
|
|
@@ -2006,14 +2421,13 @@ const DomainAliasSchema = z.object({
|
|
|
2006
2421
|
redirectTo: z.string(),
|
|
2007
2422
|
environment: z.optional(z.string())
|
|
2008
2423
|
});
|
|
2009
|
-
|
|
2010
|
-
const PostalAddressSchema = z.object({
|
|
2424
|
+
const PostalAddressSchema = desc(z.object({
|
|
2011
2425
|
streetAddress: z.string(),
|
|
2012
|
-
addressLocality: z.string(),
|
|
2013
|
-
addressRegion: z.optional(z.string()),
|
|
2426
|
+
addressLocality: desc(z.string(), "City."),
|
|
2427
|
+
addressRegion: desc(z.optional(z.string()), "State / province."),
|
|
2014
2428
|
postalCode: z.optional(z.string()),
|
|
2015
|
-
addressCountry: z.string()
|
|
2016
|
-
});
|
|
2429
|
+
addressCountry: desc(z.string(), "Country (ISO 3166-1 alpha-2 preferred).")
|
|
2430
|
+
}), "schema.org `PostalAddress` field names, so JSON-LD generation is lossless.");
|
|
2017
2431
|
const ContactObjectSchema = z.object({
|
|
2018
2432
|
name: z.optional(z.string()),
|
|
2019
2433
|
email: z.optional(z.email()),
|
|
@@ -2072,8 +2486,7 @@ const OrganizationSchema = z.object({
|
|
|
2072
2486
|
address: z.optional(PostalAddressSchema),
|
|
2073
2487
|
taxID: z.optional(z.string()),
|
|
2074
2488
|
vatID: z.optional(z.string()),
|
|
2075
|
-
|
|
2076
|
-
sameAs: z.optional(z.array(z.url()))
|
|
2489
|
+
sameAs: desc(z.optional(z.array(z.url())), "Profile URLs → JSON-LD `sameAs` (mirrors `links`).")
|
|
2077
2490
|
});
|
|
2078
2491
|
const DnsRecordType = z.enum([
|
|
2079
2492
|
"A",
|
|
@@ -2101,11 +2514,11 @@ const DnsDomainSchema = z.object({
|
|
|
2101
2514
|
});
|
|
2102
2515
|
const RobotsSchema = z.string();
|
|
2103
2516
|
const EnvironmentSchema = z.object({
|
|
2104
|
-
url: z.url(),
|
|
2105
|
-
api: z.optional(z.url()),
|
|
2106
|
-
analytics: z.optional(z.boolean()),
|
|
2107
|
-
robots: z.optional(RobotsSchema),
|
|
2108
|
-
variant: z.optional(z.string())
|
|
2517
|
+
url: desc(z.url(), "Public origin of this environment."),
|
|
2518
|
+
api: desc(z.optional(z.url()), "API origin, when different from `url`."),
|
|
2519
|
+
analytics: desc(z.optional(z.boolean()), "Whether analytics fire in this environment."),
|
|
2520
|
+
robots: desc(z.optional(RobotsSchema), "Robots policy (e.g. \"noindex,nofollow\" for dev)."),
|
|
2521
|
+
variant: desc(z.optional(z.string()), "Active `abTesting.variants` key for this environment.")
|
|
2109
2522
|
});
|
|
2110
2523
|
const AbVariantSchema = z.object({
|
|
2111
2524
|
environment: z.string(),
|
|
@@ -2152,11 +2565,10 @@ const FontSchema = z.object({
|
|
|
2152
2565
|
formats: z.optional(z.array(z.string())),
|
|
2153
2566
|
unicodeRange: z.optional(z.array(z.string())),
|
|
2154
2567
|
variants: z.optional(z.array(FontVariantSchema)),
|
|
2155
|
-
|
|
2156
|
-
preload: z.optional(z.union([z.boolean(), z.array(z.object({
|
|
2568
|
+
preload: desc(z.optional(z.union([z.boolean(), z.array(z.object({
|
|
2157
2569
|
weight: FontWeight,
|
|
2158
2570
|
style: z.optional(FontStyle)
|
|
2159
|
-
}))]))
|
|
2571
|
+
}))])), "Emit `<Font preload />` for this family (true, or a list of weight/style faces).")
|
|
2160
2572
|
});
|
|
2161
2573
|
const PresentSchema = z.optional(z.record(z.string(), z.unknown()));
|
|
2162
2574
|
const NavItemSchema = z.object({
|
|
@@ -2289,18 +2701,18 @@ const TwitterSchema = z.object({
|
|
|
2289
2701
|
image: z.optional(ImageRefSchema)
|
|
2290
2702
|
});
|
|
2291
2703
|
const SiteConfigSchema = z.object({
|
|
2292
|
-
defaultLocale: z.string(),
|
|
2293
|
-
locales: z.record(z.string(), LocaleSchema),
|
|
2294
|
-
organization: z.optional(OrganizationSchema),
|
|
2295
|
-
contact: z.optional(ContactConfigSchema),
|
|
2296
|
-
domains: z.optional(z.object({
|
|
2297
|
-
canonical: z.url(),
|
|
2298
|
-
aliases: z.optional(z.array(DomainAliasSchema))
|
|
2299
|
-
})),
|
|
2300
|
-
primaryLocation: z.optional(z.string()),
|
|
2301
|
-
locations: z.optional(z.record(z.string(), LocationSchema)),
|
|
2302
|
-
services: z.optional(z.record(z.string(), ServiceSchema)),
|
|
2303
|
-
links: z.optional(z.object({
|
|
2704
|
+
defaultLocale: desc(z.string(), "The locale used when none is specified; must be a `locales` key."),
|
|
2705
|
+
locales: desc(z.record(z.string(), LocaleSchema), "Locales the site is published in, keyed by BCP 47 tag (e.g. \"en\", \"fr\")."),
|
|
2706
|
+
organization: desc(z.optional(OrganizationSchema), "schema.org Organization details for JSON-LD (name, legalName, logo, tax IDs, sameAs profiles)."),
|
|
2707
|
+
contact: desc(z.optional(ContactConfigSchema), "Primary contact: either a `locations` key (string reference) or an inline name/email/phone/address object."),
|
|
2708
|
+
domains: desc(z.optional(z.object({
|
|
2709
|
+
canonical: desc(z.url(), "The canonical origin used for absolute URLs and SEO."),
|
|
2710
|
+
aliases: desc(z.optional(z.array(DomainAliasSchema)), "Alias domains and how they redirect to the canonical one.")
|
|
2711
|
+
})), "Canonical domain + redirecting aliases."),
|
|
2712
|
+
primaryLocation: desc(z.optional(z.string()), "The main `locations` key (for JSON-LD and defaults)."),
|
|
2713
|
+
locations: desc(z.optional(z.record(z.string(), LocationSchema)), "Physical locations (schema.org LocalBusiness): address, geo, opening hours, service area."),
|
|
2714
|
+
services: desc(z.optional(z.record(z.string(), ServiceSchema)), "Services offered (schema.org Service/Offer): name, description, slug, price offers."),
|
|
2715
|
+
links: desc(z.optional(z.object({
|
|
2304
2716
|
googleMaps: z.optional(z.url()),
|
|
2305
2717
|
instagram: z.optional(z.url()),
|
|
2306
2718
|
facebook: z.optional(z.url()),
|
|
@@ -2308,52 +2720,52 @@ const SiteConfigSchema = z.object({
|
|
|
2308
2720
|
linkedin: z.optional(z.url()),
|
|
2309
2721
|
youtube: z.optional(z.url()),
|
|
2310
2722
|
github: z.optional(z.url())
|
|
2311
|
-
})),
|
|
2312
|
-
dns: z.optional(z.record(z.string(), DnsDomainSchema)),
|
|
2313
|
-
cloudflare: z.optional(z.record(z.string(), z.unknown())),
|
|
2314
|
-
environments: z.record(z.string(), EnvironmentSchema),
|
|
2315
|
-
abTesting: z.optional(z.object({
|
|
2723
|
+
})), "Public profile URLs (also feed JSON-LD `sameAs` via `organization.sameAs`)."),
|
|
2724
|
+
dns: desc(z.optional(z.record(z.string(), DnsDomainSchema)), "Desired DNS state per domain (provider, nameservers, records) — declarative reference, not applied automatically."),
|
|
2725
|
+
cloudflare: desc(z.optional(z.record(z.string(), z.unknown())), "Deliberate escape hatch for provider-specific Cloudflare settings — out of scope to model here."),
|
|
2726
|
+
environments: desc(z.record(z.string(), EnvironmentSchema), "Deploy environments (production, dev, …): URL, API origin, analytics toggle, robots policy, active A/B variant."),
|
|
2727
|
+
abTesting: desc(z.optional(z.object({
|
|
2316
2728
|
enabled: z.boolean(),
|
|
2317
2729
|
cookieName: z.optional(z.string()),
|
|
2318
|
-
cookieMaxAge: z.optional(z.number().check(z.int(), z.positive())),
|
|
2319
|
-
splitRatio: z.optional(z.number().check(z.gte(0), z.lte(1))),
|
|
2320
|
-
variants: z.optional(z.record(z.string(), AbVariantSchema))
|
|
2321
|
-
})),
|
|
2322
|
-
designSystem: z.record(z.string(), DesignSystemEntry),
|
|
2323
|
-
defaultTheme: z.optional(z.string()),
|
|
2324
|
-
defaultColorScheme: z.optional(z.enum(["light", "dark"])),
|
|
2325
|
-
respectSystemPreference: z.optional(z.boolean()),
|
|
2326
|
-
fonts: z.optional(z.array(FontSchema)),
|
|
2327
|
-
tags: z.optional(z.record(z.string(), z.unknown())),
|
|
2328
|
-
postTypes: z.optional(z.record(z.string(), z.unknown())),
|
|
2329
|
-
galleries: z.optional(z.record(z.string(), GallerySchema)),
|
|
2330
|
-
testimonials: z.optional(z.record(z.string(), TestimonialGroupSchema)),
|
|
2331
|
-
templates: z.optional(z.record(z.string(), TemplateSchema)),
|
|
2332
|
-
navigation: z.optional(z.object({ activeTemplate: z.optional(z.object({
|
|
2333
|
-
default: z.optional(z.string()),
|
|
2334
|
-
breakpoints: z.optional(z.record(z.string(), z.string()))
|
|
2335
|
-
})) })),
|
|
2336
|
-
seo: z.optional(z.object({
|
|
2337
|
-
titleTemplate: z.optional(z.string()),
|
|
2730
|
+
cookieMaxAge: desc(z.optional(z.number().check(z.int(), z.positive())), "Seconds."),
|
|
2731
|
+
splitRatio: desc(z.optional(z.number().check(z.gte(0), z.lte(1))), "Fraction of traffic sent to the variant (0–1)."),
|
|
2732
|
+
variants: desc(z.optional(z.record(z.string(), AbVariantSchema)), "Named variants; each targets an environment and may deep-merge config `overrides`.")
|
|
2733
|
+
})), "A/B testing: cookie-based split plus named variants whose `overrides` patch the config per environment."),
|
|
2734
|
+
designSystem: desc(z.record(z.string(), DesignSystemEntry), "Named-theme map of design systems. `default` is the base; other entries may `extends` another and supply a partial patch. Light/dark is automatic (functional tokens flip per appearance), not a separate theme entry. A bare design system (with `colors`) is shorthand for `{ default: … }`."),
|
|
2735
|
+
defaultTheme: desc(z.optional(z.string()), "Which `designSystem` entry to use by default (convention: \"default\")."),
|
|
2736
|
+
defaultColorScheme: desc(z.optional(z.enum(["light", "dark"])), "Initial appearance to render before any user/system preference applies."),
|
|
2737
|
+
respectSystemPreference: desc(z.optional(z.boolean()), "Follow the user's OS light/dark preference."),
|
|
2738
|
+
fonts: desc(z.optional(z.array(FontSchema)), "Font LOADING (a serialisable projection of the Astro Fonts API: provider, weights, subsets, preload). Font TOKENS live in `designSystem.<theme>.fonts` and reference the same `cssVariable`."),
|
|
2739
|
+
tags: desc(z.optional(z.record(z.string(), z.unknown())), "Free-form content-tag taxonomy (site-specific; not interpreted by the generator)."),
|
|
2740
|
+
postTypes: desc(z.optional(z.record(z.string(), z.unknown())), "Free-form content-type definitions (site-specific; not interpreted by the generator)."),
|
|
2741
|
+
galleries: desc(z.optional(z.record(z.string(), GallerySchema)), "Named image galleries (title, tags, images with localisable alt/caption)."),
|
|
2742
|
+
testimonials: desc(z.optional(z.record(z.string(), TestimonialGroupSchema)), "Named testimonial groups (quote, name, role, rating, date)."),
|
|
2743
|
+
templates: desc(z.optional(z.record(z.string(), TemplateSchema)), "Named content templates rendered at SSR time: `nav` (menu trees), `form` (field lists + submit), or `nodes` (raw element trees)."),
|
|
2744
|
+
navigation: desc(z.optional(z.object({ activeTemplate: desc(z.optional(z.object({
|
|
2745
|
+
default: desc(z.optional(z.string()), "A `templates` key."),
|
|
2746
|
+
breakpoints: desc(z.optional(z.record(z.string(), z.string())), "Breakpoint name → `templates` key overrides.")
|
|
2747
|
+
})), "Which nav template is active, optionally per breakpoint.") })), "Site navigation settings (which nav template renders where)."),
|
|
2748
|
+
seo: desc(z.optional(z.object({
|
|
2749
|
+
titleTemplate: desc(z.optional(z.string()), "Page-title pattern (e.g. \"%s · Acme\")."),
|
|
2338
2750
|
descriptionTemplate: z.optional(z.string()),
|
|
2339
|
-
robots: z.optional(RobotsSchema),
|
|
2751
|
+
robots: desc(z.optional(RobotsSchema), "e.g. \"index,follow\" / \"noindex,nofollow\"."),
|
|
2340
2752
|
googleSiteVerification: z.optional(z.string()),
|
|
2341
2753
|
bingSiteVerification: z.optional(z.string()),
|
|
2342
2754
|
openGraph: z.optional(OpenGraphSchema),
|
|
2343
2755
|
twitter: z.optional(TwitterSchema)
|
|
2344
|
-
})),
|
|
2345
|
-
analytics: z.optional(z.object({
|
|
2756
|
+
})), "SEO defaults: title/description templates, robots policy, verification tokens, Open Graph + Twitter cards."),
|
|
2757
|
+
analytics: desc(z.optional(z.object({
|
|
2346
2758
|
googleAnalyticsId: z.optional(z.string()),
|
|
2347
2759
|
plausibleDomain: z.optional(z.string()),
|
|
2348
2760
|
googleTagManagerId: z.optional(z.string())
|
|
2349
|
-
})),
|
|
2350
|
-
notifications: z.optional(z.object({ email: z.optional(z.email()) })),
|
|
2351
|
-
tracking: z.optional(z.object({
|
|
2761
|
+
})), "Analytics provider IDs (gated per environment via `environments.<env>.analytics`)."),
|
|
2762
|
+
notifications: desc(z.optional(z.object({ email: z.optional(z.email()) })), "Where site notifications (e.g. form submissions) are sent."),
|
|
2763
|
+
tracking: desc(z.optional(z.object({
|
|
2352
2764
|
enabled: z.optional(z.boolean()),
|
|
2353
2765
|
platforms: z.optional(z.array(z.string()))
|
|
2354
|
-
})),
|
|
2355
|
-
security: z.optional(z.object({ turnstile: z.optional(z.object({ siteKey: z.optional(z.string()) })) })),
|
|
2356
|
-
legal: z.optional(z.object({
|
|
2766
|
+
})), "Marketing/conversion tracking toggles per platform."),
|
|
2767
|
+
security: desc(z.optional(z.object({ turnstile: z.optional(z.object({ siteKey: desc(z.optional(z.string()), "Cloudflare Turnstile site key (public).") })) })), "Security integrations (bot protection)."),
|
|
2768
|
+
legal: desc(z.optional(z.object({
|
|
2357
2769
|
privacyPolicy: z.optional(z.object({
|
|
2358
2770
|
enabled: z.boolean(),
|
|
2359
2771
|
url: z.optional(z.url()),
|
|
@@ -2366,7 +2778,7 @@ const SiteConfigSchema = z.object({
|
|
|
2366
2778
|
})),
|
|
2367
2779
|
cookieConsent: z.optional(z.object({
|
|
2368
2780
|
enabled: z.boolean(),
|
|
2369
|
-
type: z.optional(z.enum(["opt-in", "opt-out"])),
|
|
2781
|
+
type: desc(z.optional(z.enum(["opt-in", "opt-out"])), "Consent model."),
|
|
2370
2782
|
position: z.optional(z.enum([
|
|
2371
2783
|
"top",
|
|
2372
2784
|
"bottom",
|
|
@@ -2374,11 +2786,11 @@ const SiteConfigSchema = z.object({
|
|
|
2374
2786
|
])),
|
|
2375
2787
|
categories: z.optional(z.array(z.string()))
|
|
2376
2788
|
}))
|
|
2377
|
-
})),
|
|
2378
|
-
icons: z.optional(z.object({
|
|
2379
|
-
ui: z.optional(z.string()),
|
|
2380
|
-
brand: z.optional(z.string()),
|
|
2381
|
-
semantic: z.optional(z.array(z.string())),
|
|
2789
|
+
})), "Legal pages + cookie-consent configuration."),
|
|
2790
|
+
icons: desc(z.optional(z.object({
|
|
2791
|
+
ui: desc(z.optional(z.string()), "Icon set used for UI chrome (e.g. \"lucide\")."),
|
|
2792
|
+
brand: desc(z.optional(z.string()), "Icon set used for brand marks."),
|
|
2793
|
+
semantic: desc(z.optional(z.array(z.string())), "Named semantic icons to include."),
|
|
2382
2794
|
"fa7-solid": z.optional(z.array(z.string())),
|
|
2383
2795
|
"fa7-regular": z.optional(z.array(z.string())),
|
|
2384
2796
|
"fa7-light": z.optional(z.array(z.string())),
|
|
@@ -2387,24 +2799,21 @@ const SiteConfigSchema = z.object({
|
|
|
2387
2799
|
"simple-icons": z.optional(z.array(z.string())),
|
|
2388
2800
|
"material-symbols": z.optional(z.array(z.string())),
|
|
2389
2801
|
lucide: z.optional(z.array(z.string()))
|
|
2390
|
-
})),
|
|
2391
|
-
favicon: z.optional(z.object({
|
|
2392
|
-
source: z.string(),
|
|
2393
|
-
lowResSource: z.optional(z.string()),
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
backgroundColor: z.optional(z.string())
|
|
2400
|
-
})),
|
|
2401
|
-
deployment: z.optional(z.object({
|
|
2802
|
+
})), "Icon sets and the specific icons to bundle from each (keys are iconify collection names)."),
|
|
2803
|
+
favicon: desc(z.optional(z.object({
|
|
2804
|
+
source: desc(z.string(), "Source image (SVG/PNG) the favicon set is generated from."),
|
|
2805
|
+
lowResSource: desc(z.optional(z.string()), "Alternate source for small raster sizes (16/32px) when the main source scales down poorly."),
|
|
2806
|
+
name: desc(z.optional(z.string()), "App name for the generated web manifest / PWA."),
|
|
2807
|
+
themeColor: desc(z.optional(z.string()), "PWA theme color (also `<meta name=\"theme-color\">`)."),
|
|
2808
|
+
backgroundColor: desc(z.optional(z.string()), "PWA background color.")
|
|
2809
|
+
})), "Favicon/PWA asset generation (consumed by `@getvitops/utils` favicon tooling)."),
|
|
2810
|
+
deployment: desc(z.optional(z.object({
|
|
2402
2811
|
platform: z.optional(z.string()),
|
|
2403
2812
|
buildCommand: z.optional(z.string()),
|
|
2404
2813
|
deployCommand: z.optional(z.string()),
|
|
2405
2814
|
outputDirectory: z.optional(z.string()),
|
|
2406
2815
|
type: z.optional(z.string())
|
|
2407
|
-
}))
|
|
2816
|
+
})), "How the site is built and deployed (platform, commands, output directory) — informational for tooling.")
|
|
2408
2817
|
});
|
|
2409
2818
|
const SITE_SCHEMA_URL = "https://unpkg.com/@getvitops/generator/site.schema.json";
|
|
2410
2819
|
/** The published JSON Schema (draft-2020-12), derived from the zod schema. */
|
|
@@ -2773,5 +3182,5 @@ function defaultConfig() {
|
|
|
2773
3182
|
}
|
|
2774
3183
|
|
|
2775
3184
|
//#endregion
|
|
2776
|
-
export { DesignSystemSchema, SCHEMA_URL, SITE_SCHEMA_URL, SiteConfigSchema, deepMerge, defaultConfig, generate, generateDocs, jsonSchema, resolveSiteConfig, resolveTheme, siteJsonSchema, stripNulls, validate, validateSite };
|
|
3185
|
+
export { BASE_HOOK, DesignSystemSchema, SCHEMA_URL, SITE_SCHEMA_URL, SiteConfigSchema, TW_CLASH, deepMerge, defaultConfig, generate, generateDocs, jsonSchema, resolveSiteConfig, resolveTheme, siteJsonSchema, stripNulls, validate, validateSite };
|
|
2777
3186
|
//# sourceMappingURL=index.mjs.map
|