@getvitops/cli 0.9.0 → 2.0.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/CHANGELOG.md +440 -0
- package/README.md +28 -10
- package/dist/cli.mjs +700 -100
- package/dist/cli.mjs.map +1 -1
- package/package.json +3 -3
- package/skill/SKILL.md +57 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,445 @@
|
|
|
1
1
|
# @getvitops/cli
|
|
2
2
|
|
|
3
|
+
## 2.0.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- bf453b0: Anywhere the toolchain takes a config, that file may now be a `design-system.json` **or** the larger
|
|
8
|
+
site config that embeds one.
|
|
9
|
+
|
|
10
|
+
If you keep your tokens inside a `company.json` / `site.json`, you had to maintain a second file for
|
|
11
|
+
the tooling's sake — or duplicate your whole token set, because a site config must carry a complete
|
|
12
|
+
`designSystem`. Now you point the same option at the file you already have:
|
|
13
|
+
|
|
14
|
+
```js
|
|
15
|
+
// astro.config.mjs
|
|
16
|
+
getvitops({ css: { input: 'company.json', format: 'css' } });
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
vitops generate --input company.json --format css --out dist
|
|
21
|
+
vitops lint --input company.json --src src
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
The design system is taken from `designSystem.themes[theme]` with its `extends` chain resolved —
|
|
25
|
+
`defaultTheme`, else `default`, else whatever `--theme` / the `theme` option names. Nothing changes
|
|
26
|
+
for a plain `design-system.json`; the two are told apart by shape, not by filename, so you can call
|
|
27
|
+
the file anything.
|
|
28
|
+
|
|
29
|
+
**A site config also supplies the site-level facts generation reads**, so the path is declared once
|
|
30
|
+
rather than per option:
|
|
31
|
+
- `designSystem.defaultColorScheme: "system"` — emits the `prefers-color-scheme` block, which is
|
|
32
|
+
what makes `<color-scheme-toggle>`'s System position do anything.
|
|
33
|
+
- `legal.*.enabled` — renders the documents. `legal: {}` is now the whole declaration; `legal.input`
|
|
34
|
+
is optional.
|
|
35
|
+
- `icons.sprite` — builds `icons.svg`.
|
|
36
|
+
- `fonts` — `fonts: true` reads the families from it.
|
|
37
|
+
|
|
38
|
+
Each of those is already gated on a field in that config, so nothing new appears unless the config
|
|
39
|
+
asks for it. An explicit `site` option still wins when the two are genuinely different files.
|
|
40
|
+
|
|
41
|
+
Also:
|
|
42
|
+
- **`vitops validate` routes on the file's shape.** Pointed at a site config it used to report a
|
|
43
|
+
single `unrecognized_keys` for `designSystem` and nothing about the file's actual contents — a
|
|
44
|
+
wrong answer that reads like a right one. It now validates a site config as one, including the
|
|
45
|
+
cross-field integrity JSON Schema can't express, and checks every theme resolves to a complete
|
|
46
|
+
design system (`validateSite` only checked that the `extends` chain resolved, not what it resolved
|
|
47
|
+
to).
|
|
48
|
+
- **`generate()` gained `theme` and `siteEnv`.** The A/B variant for `siteEnv` is applied before the
|
|
49
|
+
theme is selected, so a variant can override tokens.
|
|
50
|
+
- **The theme editor's Save to source follows the design system into a site config.** Its patch is
|
|
51
|
+
design-system-relative, so the dev server merges into that subtree and writes only the surrounding
|
|
52
|
+
file whole. It locates the subtree in the raw on-disk object rather than the normalised one, so an
|
|
53
|
+
author who wrote either `designSystem` shorthand still gets their own keys edited.
|
|
54
|
+
|
|
55
|
+
- 04a51d8: Icons: one semantic vocabulary across icon sets, with the bundle derived from your source.
|
|
56
|
+
|
|
57
|
+
Name icons by meaning — `<Icon name="menu" />` resolves to `fa7-solid:bars`, `ph:list` or
|
|
58
|
+
`lucide:menu` depending on the set you configure, so swapping sets is a config edit rather than a
|
|
59
|
+
find-and-replace. A name containing `:` still passes through untouched, which is the escape hatch
|
|
60
|
+
for a set-specific glyph.
|
|
61
|
+
|
|
62
|
+
**New `icons` option on `getvitops()`.** Configures the sets once per site and, under
|
|
63
|
+
`output: 'server'`, derives astro-icon's `include` map by scanning your source — the list most
|
|
64
|
+
projects end up maintaining by hand. On a static build no `include` is passed at all, because
|
|
65
|
+
astro-icon is already zero-config there and a list could only drop a glyph the scan couldn't see.
|
|
66
|
+
Names you declare that don't resolve fail the build; names only the scan found just warn; names
|
|
67
|
+
computed at runtime are reported with file and line so you can declare them.
|
|
68
|
+
|
|
69
|
+
**New `<Icon />` component**, and a real fix with it: `Popover`, `Details` and `Drawer` imported
|
|
70
|
+
`astro-icon/components` at module scope, so the _optional_ peer was resolved whether or not an icon
|
|
71
|
+
ever rendered — hard-failing anyone who hadn't installed it. Engines now load dynamically. The
|
|
72
|
+
per-component `iconResolver` prop still works but is deprecated in favour of the integration option.
|
|
73
|
+
|
|
74
|
+
**New SVG sprite** (`icons.sprite: true` in your site config) for consumers that can't run an icon
|
|
75
|
+
integration — Bricks/WordPress, EmDash renderers, plain HTML. `<use href="…/icons.svg#ph--list">`,
|
|
76
|
+
no JavaScript and no icon-API request. Every semantic name also gets a set-independent `icon-<name>`
|
|
77
|
+
alias, so sprite markup survives an icon-set change. WordPress gets `vitops_icon()`, a
|
|
78
|
+
`[vitops_icon]` shortcode and a **Vitops → Icon** Bricks element.
|
|
79
|
+
|
|
80
|
+
**New `vitops icons`** command: reports which icons your source uses, which names couldn't be
|
|
81
|
+
resolved, and which are computed at runtime; `--sprite` builds the sprite, `--json` for CI.
|
|
82
|
+
|
|
83
|
+
**Renamed, and completed to all four directions.** Chevrons and arrows are the
|
|
84
|
+
one family whose _meaning is_ its direction, and that direction is physical, not
|
|
85
|
+
logical — a chevron in a details marker points down in every writing mode. So they
|
|
86
|
+
are named for where they point: `expand-more`/`expand-less` →
|
|
87
|
+
`chevron-down`/`chevron-up`, and `arrow-forward`/`arrow-back` →
|
|
88
|
+
`arrow-right`/`arrow-left`. `chevron-left`/`chevron-right` keep their names.
|
|
89
|
+
`arrow-up`/`arrow-down` are new, so both families now cover all four directions.
|
|
90
|
+
`lightning` is new.
|
|
91
|
+
|
|
92
|
+
If you passed any of the old names to `<Icon />`, `resolveIcon` or an `icons`
|
|
93
|
+
config, update them. They fail loudly rather than silently: an unresolvable
|
|
94
|
+
declared name throws, and `vitops icons` reports scanned ones.
|
|
95
|
+
|
|
96
|
+
**Fixed three Font Awesome mappings that named no real glyph** and so rendered an
|
|
97
|
+
empty box: `login`/`logout` were mapped to `login`/`logout` (Font Awesome calls
|
|
98
|
+
them `right-to-bracket`/`right-from-bracket`) and `backup` to `backup`
|
|
99
|
+
(`cloud-arrow-up`). Every value in all four UI sets is now checked against the
|
|
100
|
+
installed collection.
|
|
101
|
+
|
|
102
|
+
**Phosphor (`ph`) joins the semantic map**, with all 83 names verified against the real icon set.
|
|
103
|
+
Phosphor keeps every weight in one collection and varies the name (`list`, `list-bold`), unlike Font
|
|
104
|
+
Awesome's per-weight collections, so `resolveIcon` and `generateIconInclude` gained a `weight`
|
|
105
|
+
option for sets shaped that way.
|
|
106
|
+
|
|
107
|
+
Fixes: `site.icons` was a closed object, so any icon collection not in its hand-written key list was
|
|
108
|
+
silently dropped during validation — your config passed and the icons never bundled. It accepts any
|
|
109
|
+
collection name now.
|
|
110
|
+
|
|
111
|
+
- bf453b0: `vitops lint` now catches hand-written CSS that re-implements a framework primitive.
|
|
112
|
+
|
|
113
|
+
The existing linter finds classes that resolve to nothing — you named something and it isn't there.
|
|
114
|
+
This is the costlier inverse, where the code **works**: a centred container written by hand, a
|
|
115
|
+
two-column split behind a media query, a flex row. Nothing is broken, so nothing ever surfaces it,
|
|
116
|
+
and the design system quietly stops being where those decisions live.
|
|
117
|
+
|
|
118
|
+
Three rules to start, each requiring the combination that makes the intent unambiguous
|
|
119
|
+
(`margin-inline: auto` alone is not a centred track):
|
|
120
|
+
|
|
121
|
+
| Found | Suggested |
|
|
122
|
+
| ----------------------------------------------------------- | ------------------------------------------------------------------------- |
|
|
123
|
+
| `max-inline-size: var(--width-*)` + `margin-inline: auto` | `.centered`, widening a child with `breakout` / `spotlight` / `fullbleed` |
|
|
124
|
+
| `grid-template-columns: 1fr 2fr` inside a `min-width` query | `md-split-1-2` — or `@md:split-1-2` in the tailwind format |
|
|
125
|
+
| `display: flex` + `align-items: center` | `flex items-center`, or `.cluster-start` |
|
|
126
|
+
|
|
127
|
+
**Findings now carry a severity, and suggestions do not fail the command.** These are judgement
|
|
128
|
+
calls, and a reuse hint that broke CI on the day it shipped would be a worse defect than the drift it
|
|
129
|
+
reports. `--strict` promotes them for anyone who wants the ratchet. Everything the linter reported
|
|
130
|
+
before is an `error` and still exits 1.
|
|
131
|
+
|
|
132
|
+
`.css` files and `<style>` blocks are now read (the class linter only ever looked at markup). The
|
|
133
|
+
generated stylesheet is skipped by its `GENERATED … do not edit by hand` banner rather than by path —
|
|
134
|
+
the `css` format writes `styles.css` into `src/styles`, squarely inside what `--src` scans, and
|
|
135
|
+
linting it made the framework report `.split` as reinventing `.split`.
|
|
136
|
+
|
|
137
|
+
Two other gaps closed:
|
|
138
|
+
- **The format-spelling check ran in one direction only.** `md-split-1-2` in a tailwind project was
|
|
139
|
+
caught; `@md:split-1-2` in a css/bricks project was not, because variant stripping removed the
|
|
140
|
+
`@md:` regardless of format and left a bare class that matched nothing. The same silent no-op, in
|
|
141
|
+
the other direction, unreported. Stripping is format-aware now.
|
|
142
|
+
- **`grid-auto` and the whole `m-*` rhythm family were missing from the tailwind format entirely** —
|
|
143
|
+
not dropped via `TW_CLASH`, just never re-emitted when `layout.css` is skipped and a subset
|
|
144
|
+
re-emitted in its place. Unlike the `<bp>-` classes they have no Tailwind equivalent, and unlike a
|
|
145
|
+
misspelt class the linter cannot flag them, because they aren't anchored to your config. They emit
|
|
146
|
+
as `@utility` definitions now, keyed to the same `--rhythm-*` variables as the css format.
|
|
147
|
+
|
|
148
|
+
Run against this repo's own docs site, the rules found one real instance on the first pass; it is
|
|
149
|
+
fixed in the same change.
|
|
150
|
+
|
|
151
|
+
- bf453b0: Maskable favicons are now opaque, so they stop rendering as a logo in a black box.
|
|
152
|
+
|
|
153
|
+
`icon-mask.png` is declared `purpose: "maskable"` and `apple-touch-icon.png` is linked on every
|
|
154
|
+
page. Both sit a deliberately-inset logo on a larger canvas — that inset **is** the maskable safe
|
|
155
|
+
zone and was always correct — but the canvas was filled with `alpha: 0`. From any transparent
|
|
156
|
+
source that left 36% of `icon-mask.png` and 40% of `apple-touch-icon.png` transparent, on two files
|
|
157
|
+
whose entire contract is full bleed: the OS crops them to its own shape and composites the rest onto
|
|
158
|
+
whatever it likes, usually black. iOS discards alpha on the apple-touch-icon outright.
|
|
159
|
+
|
|
160
|
+
`backgroundColor` was already the obvious input for this and already plumbed — as far as the web
|
|
161
|
+
manifest, and no further. So the raster and the manifest disagreed about the very colour meant to
|
|
162
|
+
sit behind the icon.
|
|
163
|
+
|
|
164
|
+
Now:
|
|
165
|
+
- those two outputs are composited onto `backgroundColor`, defaulting to `#ffffff` — the same
|
|
166
|
+
default the manifest's `background_color` already used;
|
|
167
|
+
- `favicon.svg`, `icon-{16,32,192,512}.png` and `favicon.ico` keep the source's transparency, since
|
|
168
|
+
none of them is maskable;
|
|
169
|
+
- a transparent source with no `backgroundColor` set now warns, rather than silently inheriting
|
|
170
|
+
white — a dark logo would lose against it;
|
|
171
|
+
- `icon-192`/`icon-512` declare `purpose: "any"` explicitly. Omitting it was legal (unset means
|
|
172
|
+
"any"), but with a maskable present and nothing claiming "any", some launchers picked the maskable
|
|
173
|
+
— the one with the safe-zone inset — for slots that wanted a plain icon.
|
|
174
|
+
|
|
175
|
+
`vitops favicon` gained `--background <hex>`; it previously had no way to set this at all.
|
|
176
|
+
`getvitops({ favicon })` and the Vite plugin now forward the option they were already accepting.
|
|
177
|
+
|
|
178
|
+
### Patch Changes
|
|
179
|
+
|
|
180
|
+
- bf453b0: Say plainly that `fonts` in `design-system.json` is stacks only and loads nothing.
|
|
181
|
+
|
|
182
|
+
The field's description was complete and self-consistent — "raw font stacks by name, emitted as
|
|
183
|
+
`--font-<name>` tokens" — and told you nothing about where the `@font-face` comes from. Following
|
|
184
|
+
it literally leads to installing a `@fontsource*` package and importing its CSS in a layout, which
|
|
185
|
+
renders correctly and silently gives up subsetting, preload, and the `size-adjust` /
|
|
186
|
+
`ascent-override` fallback metrics: a CLS regression that looks like a working setup.
|
|
187
|
+
|
|
188
|
+
The field is unchanged. What changed is that four surfaces now name the boundary and point at the
|
|
189
|
+
fix — declare the family in Astro's `fonts:` config and point the token at its `cssVariable`:
|
|
190
|
+
- the `fonts` description in the JSON Schema, and therefore `vitops docs authoring`
|
|
191
|
+
- `SKILL.md`, which previously never mentioned fonts at all
|
|
192
|
+
- the generated `tailwind.css` header and its `@theme` fonts comment
|
|
193
|
+
- the generated `type-tokens.css` header (css format)
|
|
194
|
+
|
|
195
|
+
- Updated dependencies [4756788]
|
|
196
|
+
- Updated dependencies [bf453b0]
|
|
197
|
+
- Updated dependencies [bf453b0]
|
|
198
|
+
- Updated dependencies [bf453b0]
|
|
199
|
+
- Updated dependencies [bf453b0]
|
|
200
|
+
- Updated dependencies [bf453b0]
|
|
201
|
+
- Updated dependencies [bf453b0]
|
|
202
|
+
- Updated dependencies [bf453b0]
|
|
203
|
+
- Updated dependencies [bf453b0]
|
|
204
|
+
- Updated dependencies [04a51d8]
|
|
205
|
+
- Updated dependencies [bf453b0]
|
|
206
|
+
- Updated dependencies [bf453b0]
|
|
207
|
+
- Updated dependencies [bf453b0]
|
|
208
|
+
- Updated dependencies [bf453b0]
|
|
209
|
+
- Updated dependencies [bf453b0]
|
|
210
|
+
- Updated dependencies [bf453b0]
|
|
211
|
+
- Updated dependencies [bf453b0]
|
|
212
|
+
- Updated dependencies [bf453b0]
|
|
213
|
+
- Updated dependencies [bf453b0]
|
|
214
|
+
- @getvitops/generator@2.0.0
|
|
215
|
+
- @getvitops/utils@2.0.0
|
|
216
|
+
|
|
217
|
+
## 1.0.0
|
|
218
|
+
|
|
219
|
+
### Major Changes
|
|
220
|
+
|
|
221
|
+
- bb92a14: **The colour system is rebuilt on a target-prefixed grammar and a shared lightness ladder.**
|
|
222
|
+
This is a breaking change to every colour token and utility class.
|
|
223
|
+
|
|
224
|
+
## Why
|
|
225
|
+
|
|
226
|
+
Two axes shared one class namespace — functional _planes_ (`--<role>-bg-muted`) and
|
|
227
|
+
appearance-relative _stops_ (`--color-<role>-muted`) — arbitrated by a "plane wins" rule. The
|
|
228
|
+
result was not a scale. On the shipped palette, light mode:
|
|
229
|
+
|
|
230
|
+
| class | resolved to | step |
|
|
231
|
+
| ---------------------------------- | ------------ | --------------------------------------------- |
|
|
232
|
+
| `bg-ui-accent-x-muted` | stop | 100 |
|
|
233
|
+
| `bg-ui-accent-muted` | plane | 100 — identical, so `x-muted` was a dead rung |
|
|
234
|
+
| `bg-ui-accent` | plane | 50 — _lighter_ than both its "muted" rungs |
|
|
235
|
+
| `border-ui-accent-muted` / `-bold` | stop / plane | 300 / 300 — identical |
|
|
236
|
+
| `text-ui-accent-bold` | stop | 700 — _lighter_ than `text-…-muted` (800) |
|
|
237
|
+
|
|
238
|
+
Every family was non-monotonic, two had duplicate rungs, and `--color-<role>-muted` was
|
|
239
|
+
unreachable through any `bg-` class.
|
|
240
|
+
|
|
241
|
+
## The new grammar
|
|
242
|
+
|
|
243
|
+
```
|
|
244
|
+
--color-<target>-<role>[-<variant>] target ∈ bg | text | icon | border
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
The target is **inside** the name, so `bg-danger-muted` and `text-danger-muted` are different
|
|
248
|
+
tokens and there is nothing left to arbitrate. **The class name is the token name minus
|
|
249
|
+
`--color-`** — one vocabulary instead of two.
|
|
250
|
+
|
|
251
|
+
Variants are ordinal (`xx-muted` < `x-muted` < `muted` < bare < `bold` < `x-bold`) and the
|
|
252
|
+
tables are sparse: only cells that hold their contrast target exist.
|
|
253
|
+
|
|
254
|
+
## Role kinds
|
|
255
|
+
|
|
256
|
+
`colors.roles` values may now be `{ hue, kind }` as well as a bare hue string:
|
|
257
|
+
|
|
258
|
+
```jsonc
|
|
259
|
+
"roles": {
|
|
260
|
+
"danger": "rust", // shorthand => chromatic
|
|
261
|
+
"surface": { "hue": "navy", "kind": "surface" }
|
|
262
|
+
}
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
- **`surface`** — a page/panel colour: `bg-<role>` is the card, `bg-<role>-muted` the page
|
|
266
|
+
behind it, `bg-<role>-x-muted` a well, `bg-<role>-bold` the inverse surface. Full text scale.
|
|
267
|
+
- **`chromatic`** (default) — a signal colour: tints (`bg-<role>-x-muted`/`-muted`) and solids
|
|
268
|
+
(`bg-<role>-solid[-bold|-x-bold]`), with **no bare `bg-<role>`** — say how loud you mean.
|
|
269
|
+
`text-on-<role>` is the guaranteed foreground for the solid family.
|
|
270
|
+
|
|
271
|
+
## Palette generation
|
|
272
|
+
|
|
273
|
+
Every ramp now sits on one **fixed lightness ladder** (50 → L 0.98 … 950 → L 0.21); only chroma
|
|
274
|
+
and hue vary. That is what makes a step mean the same lightness in every hue. Previously each
|
|
275
|
+
seed transposed the curve, so relative luminance at step 300 ranged from 0.253 to 0.384 across
|
|
276
|
+
the shipped palette — the same variant read differently depending on the role.
|
|
277
|
+
|
|
278
|
+
Authored colours are still reproduced **exactly**. A `seed`, an `anchors` entry or a `tones`
|
|
279
|
+
value is pinned verbatim at its nearest step; every other step takes the ladder. Snapping brand
|
|
280
|
+
colours to the ladder was measured and rejected: 11 of 18 real brand hexes moved beyond a
|
|
281
|
+
just-noticeable difference (worst ΔE-OK 0.050 — Facebook's `#1877F2` → `#0067e1`) and six left
|
|
282
|
+
sRGB gamut. Deviation is now bounded and local to pinned steps, and warns past ~0.03 L.
|
|
283
|
+
|
|
284
|
+
Two tones that claim the same step now **error** instead of one silently overwriting the other;
|
|
285
|
+
the record form (`tones: { "600": "…", "700": "…" }`) is how you resolve it.
|
|
286
|
+
|
|
287
|
+
## New
|
|
288
|
+
- **`icon-<role>`** — a non-text tier, so a glyph may run more vivid than text. `icon` is now a
|
|
289
|
+
default utility family alongside `bg`/`text`/`border`.
|
|
290
|
+
- **`--color-border-focus`** — the focus-ring tone, taken from `ui-primary`'s solid.
|
|
291
|
+
- **Contrast is enforced at build time**, not only in tests: text ≥ APCA Lc 75 on its primary
|
|
292
|
+
background, ≥ 60 on secondary planes, icons and surface boundaries ≥ 45, both appearances.
|
|
293
|
+
A violation now fails `generate`. Chromatic text is checked against the _surface_ planes it
|
|
294
|
+
actually sits on, not only its own tints. `text-<role>-x-muted` (placeholder) and `-xx-muted`
|
|
295
|
+
(disabled) are explicitly exempt; nothing else is.
|
|
296
|
+
|
|
297
|
+
## Migrating
|
|
298
|
+
|
|
299
|
+
Rename `--<role>-<suffix>` → `--color-<target>-<role>[-<variant>]`, and the same for classes:
|
|
300
|
+
|
|
301
|
+
| before | after |
|
|
302
|
+
| --------------------------------------- | ---------------------------------------------------------------- |
|
|
303
|
+
| `--<role>-bg` / `bg-<role>` (chromatic) | `--color-bg-<role>-x-muted` / `bg-<role>-x-muted` |
|
|
304
|
+
| `--<role>-bg-muted` | `--color-bg-<role>-muted` |
|
|
305
|
+
| `--<role>-solid` / `-solid-bold` | `--color-bg-<role>-solid` / `-solid-bold` |
|
|
306
|
+
| `--<role>-on-solid` / `text-on-<role>` | `--color-text-on-<role>` (class unchanged) |
|
|
307
|
+
| `--<role>-text` / `-text-muted` | `--color-text-<role>` / `-muted` |
|
|
308
|
+
| `--<role>-border` / `-border-bold` | `--color-border-<role>` / `-bold` |
|
|
309
|
+
| `--color-<role>-muted` (stop) | judge by use: `--color-bg-<role>-muted` or `--color-text-<role>` |
|
|
310
|
+
|
|
311
|
+
**`surface` background names rotate**, value-preserving: what was `--surface-bg` (the page) is
|
|
312
|
+
now `--color-bg-surface-muted`; what was `--surface-bg-bold` (raised) is now
|
|
313
|
+
`--color-bg-surface`. Elevation is expressed by which token you reach for — page `bg-muted`,
|
|
314
|
+
card `bg` — rather than a raised/sunken pair, which is what lets a future surfaces axis flatten
|
|
315
|
+
it without touching markup.
|
|
316
|
+
|
|
317
|
+
`vitops lint` reports role classes that no longer resolve, and now derives its suggestions from
|
|
318
|
+
what the generator actually emits rather than a hand-maintained list.
|
|
319
|
+
|
|
320
|
+
### Minor Changes
|
|
321
|
+
|
|
322
|
+
- bb92a14: Add a fourth output format, `design`, that emits `DESIGN.md` — the agent-facing brief in
|
|
323
|
+
[google-labs-code/design.md](https://github.com/google-labs-code/design.md) format.
|
|
324
|
+
|
|
325
|
+
```sh
|
|
326
|
+
vitops generate --format design --out . # DESIGN.md, and nothing else
|
|
327
|
+
vitops generate --format css,design # compose it with a stylesheet
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
The file is YAML front matter carrying the tokens (`colors`, `typography`, `rounded`,
|
|
331
|
+
`spacing`, `components`, cross-referenced with `{group.token}`) followed by a prose body
|
|
332
|
+
carrying the rationale — colour model, fluid scales, layout vocabulary, elevation, shape
|
|
333
|
+
cascade, component tiers, do's and don'ts. Every section is rendered from your config, so
|
|
334
|
+
it cannot describe a system the other formats don't build. Point a coding agent, a Figma
|
|
335
|
+
import, or a designer at this one file when they don't have the toolchain; `vitops docs`
|
|
336
|
+
remains the richer reference for those who do.
|
|
337
|
+
|
|
338
|
+
It is emitted with `--out .` in mind: DESIGN.md conventionally lives at a repo root beside
|
|
339
|
+
`AGENTS.md`, not in a build directory.
|
|
340
|
+
|
|
341
|
+
Three things the spec cannot express, handled the same way every time and explained in the
|
|
342
|
+
emitted prose so the file is self-describing:
|
|
343
|
+
- **Fluid `clamp()` sizes** → the maximum (desktop) value, since a spec `Dimension` is a
|
|
344
|
+
bare number plus px/em/rem.
|
|
345
|
+
- **Dark mode** → light values only, with the automatic functional flip explained. Role
|
|
346
|
+
tokens are emitted as `{colors.<hue>-<step>}` references into the raw ramps rather than
|
|
347
|
+
flattened hexes, so the role → ramp lineage survives the export — flattening them is
|
|
348
|
+
exactly what breaks dark mode downstream.
|
|
349
|
+
- **A `50%` radius** → dropped from `rounded` (it is not a `Dimension`) and named in the
|
|
350
|
+
Shapes prose instead, so nothing is silently lost.
|
|
351
|
+
|
|
352
|
+
**New:** an optional `meta` key in `design-system.json` (`{ name, description }`) supplies
|
|
353
|
+
the brand name and the Overview paragraph. It affects no other format.
|
|
354
|
+
|
|
355
|
+
**New:** `StylesheetFormat` (`Exclude<Format, 'design'>`), exported from
|
|
356
|
+
`@getvitops/generator`. `@getvitops/astro`'s `css.format` now takes that narrower type —
|
|
357
|
+
`design` produces no stylesheet to inject, so passing it there is a type error rather than
|
|
358
|
+
a missing-file failure at build time. `vitops lint --format` is likewise restricted to the
|
|
359
|
+
three CSS formats. No change for anyone already passing `tailwind`, `css` or `bricks`.
|
|
360
|
+
|
|
361
|
+
- bb92a14: Generate legal documents from your site config
|
|
362
|
+
|
|
363
|
+
`vitops legal` renders a privacy policy, terms of service and cookie notice from a site
|
|
364
|
+
config, in markdown, HTML or EmDash Portable Text:
|
|
365
|
+
|
|
366
|
+
```sh
|
|
367
|
+
vitops legal --out ./content # every enabled document, as markdown
|
|
368
|
+
vitops legal --doc privacy --format html # one document, as an HTML fragment
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
The documents are **derived from your config**, not filled into a form. The analytics
|
|
372
|
+
provider they name is the one whose ID you set; the personal information they list is what
|
|
373
|
+
your configured forms actually collect; the countries they name come from the providers you
|
|
374
|
+
use. So a provider swap updates the policy on the next build, and the fix for a wrong policy
|
|
375
|
+
is a corrected config — hand-editing the output is overwritten.
|
|
376
|
+
|
|
377
|
+
Enable documents under `legal`, which gains the facts the prose asserts:
|
|
378
|
+
|
|
379
|
+
```jsonc
|
|
380
|
+
{
|
|
381
|
+
"legal": {
|
|
382
|
+
"jurisdiction": "ca", // only 'ca' (PIPEDA) ships today
|
|
383
|
+
"privacyPolicy": {
|
|
384
|
+
"enabled": true,
|
|
385
|
+
"lastUpdated": "2026-08-01",
|
|
386
|
+
"retention": "24 months after our last contact with you",
|
|
387
|
+
// Third parties the config cannot imply. Analytics, Turnstile and your
|
|
388
|
+
// deploy platform are detected automatically — list only the rest.
|
|
389
|
+
"processors": [
|
|
390
|
+
{
|
|
391
|
+
"name": "Stripe",
|
|
392
|
+
"purpose": "payment processing",
|
|
393
|
+
"country": "the United States",
|
|
394
|
+
},
|
|
395
|
+
],
|
|
396
|
+
},
|
|
397
|
+
"termsOfService": { "enabled": true },
|
|
398
|
+
"cookieConsent": {
|
|
399
|
+
"enabled": true,
|
|
400
|
+
"type": "opt-in",
|
|
401
|
+
"categories": ["Essential", "Analytics"],
|
|
402
|
+
},
|
|
403
|
+
},
|
|
404
|
+
}
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
Delivery, by stack:
|
|
408
|
+
- **Any stack** — `vitops legal`. No integration code; prints to stdout without `--out`.
|
|
409
|
+
- **WordPress/Bricks** — `vitops generate --site <path>` also writes `dist/legal/*.html`, and
|
|
410
|
+
the theme loader now registers `[vitops_legal doc="privacy"]` to render one in a page. The
|
|
411
|
+
document updates on the next deploy with no action in WordPress.
|
|
412
|
+
- **Astro** — `getvitops({ legal: { input: 'site.json', out: 'src/content/legal' } })` writes
|
|
413
|
+
markdown into a content collection and re-renders when the site config changes. It needs a
|
|
414
|
+
`css` config (that is what registers the Vite plugin); without one, use the CLI.
|
|
415
|
+
- **EmDash** — `--format portable-text`, pasted into the admin.
|
|
416
|
+
|
|
417
|
+
Also new on the public API: `generateLegal()`, `renderMarkdown()`, `renderNodes()`,
|
|
418
|
+
`derivePolicyVars()`, `parseMarkdown()` / `toHtmlFragment()` / `toPortableText()`, and
|
|
419
|
+
`resolvePrivacyContact()`.
|
|
420
|
+
|
|
421
|
+
Two things to know before you publish anything this produces:
|
|
422
|
+
- **It is not legal advice.** Every document opens with a review banner saying so. The
|
|
423
|
+
bundled terms-of-service prose in particular is generic website boilerplate and
|
|
424
|
+
deliberately does not cover sales, refunds, subscriptions, accounts or user-generated
|
|
425
|
+
content — a site doing any of those needs clauses drafted for it.
|
|
426
|
+
- **It is only as true as your config.** A policy asserting things your site does not do is
|
|
427
|
+
worse than no policy. Check that the config describes reality before you ship the output.
|
|
428
|
+
|
|
429
|
+
`validateSite` now rejects a config that enables a privacy policy without a contact for
|
|
430
|
+
privacy requests or a `domains.canonical`, since both are interpolated into sentences that
|
|
431
|
+
would otherwise render blank.
|
|
432
|
+
|
|
433
|
+
### Patch Changes
|
|
434
|
+
|
|
435
|
+
- Updated dependencies [bb92a14]
|
|
436
|
+
- Updated dependencies [bb92a14]
|
|
437
|
+
- Updated dependencies [bb92a14]
|
|
438
|
+
- Updated dependencies [bb92a14]
|
|
439
|
+
- Updated dependencies [eeb059f]
|
|
440
|
+
- @getvitops/generator@1.0.0
|
|
441
|
+
- @getvitops/utils@1.0.0
|
|
442
|
+
|
|
3
443
|
## 0.9.0
|
|
4
444
|
|
|
5
445
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# @getvitops/cli
|
|
2
2
|
|
|
3
|
-
CLI for the Vitops design-system generator. Turn a `design-system.json` into
|
|
4
|
-
|
|
3
|
+
CLI for the Vitops design-system generator. Turn a `design-system.json` into Tailwind v4,
|
|
4
|
+
Bricks, `DESIGN.md`, or standalone CSS output.
|
|
5
5
|
|
|
6
6
|
```sh
|
|
7
7
|
# scaffold a starter config (stamps a $schema for editor autocomplete)
|
|
@@ -17,14 +17,19 @@ npx vitops generate --format bricks,css --out dist
|
|
|
17
17
|
|
|
18
18
|
## Commands
|
|
19
19
|
|
|
20
|
-
| command | does
|
|
21
|
-
| -------------------------------------------------- |
|
|
22
|
-
| `vitops init [--out design-system.json] [--force]` | write a starter config with `$schema`
|
|
23
|
-
| `vitops validate <file>` | schema-check (non-zero exit on error)
|
|
24
|
-
| `vitops generate -i <file> -f <formats> -o <dir>` |
|
|
25
|
-
| `vitops favicon -i <svg\|png> -o <dir>` | generate a favicon set from a source image
|
|
26
|
-
| `vitops agents [-o AGENTS.md] [--docs-dir <dir>]` | link the agent skill + AGENTS.md pointer
|
|
27
|
-
| `vitops docs [topic] [--all]` | print live reference docs to stdout
|
|
20
|
+
| command | does |
|
|
21
|
+
| -------------------------------------------------- | ----------------------------------------------- |
|
|
22
|
+
| `vitops init [--out design-system.json] [--force]` | write a starter config with `$schema` |
|
|
23
|
+
| `vitops validate <file>` | schema-check (non-zero exit on error) |
|
|
24
|
+
| `vitops generate -i <file> -f <formats> -o <dir>` | `tailwind` / `bricks` / `design` / `css` output |
|
|
25
|
+
| `vitops favicon -i <svg\|png> -o <dir>` | generate a favicon set from a source image |
|
|
26
|
+
| `vitops agents [-o AGENTS.md] [--docs-dir <dir>]` | link the agent skill + AGENTS.md pointer |
|
|
27
|
+
| `vitops docs [topic] [--all]` | print live reference docs to stdout |
|
|
28
|
+
|
|
29
|
+
Every `--input` takes a `design-system.json` **or** the larger site config that embeds one
|
|
30
|
+
(`company.json` / `site.json`) — told apart by shape, so name the file whatever you like. Add
|
|
31
|
+
`--theme <name>` to build a `designSystem.themes` entry other than the default. `vitops validate`
|
|
32
|
+
routes the same way and checks a site config as a site config.
|
|
28
33
|
|
|
29
34
|
## Using the output
|
|
30
35
|
|
|
@@ -80,6 +85,19 @@ The CLI only writes files — how they reach WordPress is up to you:
|
|
|
80
85
|
- **CI:** run `vitops generate --format bricks` in your pipeline and deploy the artifact the
|
|
81
86
|
same way you deploy the rest of the theme.
|
|
82
87
|
|
|
88
|
+
**Agent brief (`DESIGN.md`).** `--format design` emits a single `DESIGN.md` and **no CSS** — the
|
|
89
|
+
portable brief in [design.md](https://github.com/google-labs-code/design.md) format (YAML token
|
|
90
|
+
front matter, then prose) for a coding agent or design tool that doesn't have the toolchain
|
|
91
|
+
installed. It conventionally lives at the repo root beside `AGENTS.md`:
|
|
92
|
+
|
|
93
|
+
```sh
|
|
94
|
+
vitops generate --input design-system.json --format design --out .
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
`--format` is comma-separated, but a run shares one `--out` — so keep the brief its own invocation
|
|
98
|
+
whenever your stylesheet goes somewhere other than the repo root. Regenerate it with your CSS; it's
|
|
99
|
+
derived from the same config, so it can't drift from what the browser gets.
|
|
100
|
+
|
|
83
101
|
## Teaching AI agents (`vitops agents` + `vitops docs`)
|
|
84
102
|
|
|
85
103
|
This package **ships an agent skill** (`skill/SKILL.md`, the [Agent Skills](https://code.claude.com/docs/en/skills)
|