@getvitops/generator 8.3.0-alpha.1 → 8.3.0-alpha.2
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 +38 -0
- package/assets/css/patterns/card.css +6 -0
- package/assets/css/patterns/counter.css +17 -0
- package/assets/css/patterns/logos.css +20 -0
- package/assets/css/patterns/sitenav.css +19 -1
- package/assets/css/patterns/stats.css +3 -0
- package/assets/css/patterns/steps.css +7 -0
- package/dist/index.d.mts +4 -4
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +54 -37
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,43 @@
|
|
|
1
1
|
# @getvitops/generator
|
|
2
2
|
|
|
3
|
+
## 8.3.0-alpha.2
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 0a47856: Build correctness and gaps from the second upstream round.
|
|
8
|
+
|
|
9
|
+
- **`@getvitops/emdash`: fixes a circular import that emptied the plugin block map in the static build.** `Section.astro` and `WidgetArea.astro` imported `emdash/ui` statically, which imports `virtual:emdash/block-components`, which is `@getvitops/emdash/astro` again. Depending on import order, every `vitops_*` block inside a section body was dropped from prerendered pages with only a `PortableText [components.type] is missing …` warning. Both now import it lazily, and a test fails if any renderer imports `emdash/ui` statically. No code change needed in sites.
|
|
10
|
+
- **`@getvitops/cli`: `vitops emdash build`** runs `astro build` and then `check-prerender`, and fails when the build reports a block type with no component (reported once per type). The template's `build`, `deploy` and `deploy:dev` scripts use it; existing sites replace `astro build && vitops emdash check-prerender` with `vitops emdash build`. `check-prerender` also warns (fails with `--strict`) on a page without exactly one `<h1>`.
|
|
11
|
+
- **`@getvitops/emdash`: `vitops_hero` and `vitops_section` heading level** accepts `h1`–`h6` and `p` (a heading that is styled but not in the outline). The hero defaults to `h1`; set the level to `2` on a page whose layout already renders an `h1` (the template's seed does).
|
|
12
|
+
- **`@getvitops/emdash`: `vitops_logos`** shows an entry's name as text (`.logos__name`) when it has no image. `@getvitops/core` adds the rule (all formats).
|
|
13
|
+
- **`@getvitops/emdash`: an empty content widget** (no text, no blocks) no longer renders its title, and an area whose widgets are all empty renders nothing.
|
|
14
|
+
- **`@getvitops/create`:** the template's `pages` collection gets `urlPattern: "/{slug}"` (the sitemap listed `/pages/<slug>`, which 404s; existing sites change it under Content Types), and `.gitignore` ignores only `src/styles/tailwind.css` and `tokens.json`, so site CSS kept in `src/styles/` is committed.
|
|
15
|
+
- **`@getvitops/emdash`: per-item colour.** The rows of `vitops_cards`, `vitops_stats`, `vitops_steps` and the hero's `cards` take their own `tone` / `color` (blank inherits the block's), so one block can hold a pine item and an amber one. Hero cards also take an `eyebrow` and a button (`ctaLabel` / `ctaHref`; with a button the card is no longer a stretched link).
|
|
16
|
+
- **`@getvitops/generator` / `@getvitops/emdash`: solid fills read.** The generator emits `--color-<hue>-<step>-on` for every step of every hue, the foreground for a fill at that step picked by the same APCA rule as `text-on-<role>` (css and tailwind formats, a plain `:root`, not `@theme`). A block fill chosen by `color` (hue and step) uses it instead of a fixed white..
|
|
17
|
+
- **`@getvitops/core`: `.counter__value.sr-only`** hides itself, so a build that never emitted the `.sr-only` utility no longer shows a counting figure twice (all formats).
|
|
18
|
+
- **`@getvitops/astro` / `@getvitops/core`: `SiteFooter` groups.** A top-level item of the `footer-sitemap` menu with children becomes a column (its label is the heading, its children the links, one level only); childless items stay in the flat list. Each column is a named `<nav>`.
|
|
19
|
+
- **`@getvitops/astro`: planned links and header buttons.** A menu item with `draft` in its CSS classes stays in the menu but is not rendered (at every level, in the header and the footer). `<SiteNav>` has an `actions` slot for header buttons that are not menu items, and an item with `cta` in its CSS classes renders as a button.
|
|
20
|
+
- **`@getvitops/cli`: `check-prerender` warnings** (fail with `--strict`): links to pages the build did not produce, a form with no delivery address (no `site.notifications.email`, location `email` or `site.crm`), and form fields missing from every `site.templates` form entry (the generated privacy policy reads those). It reads `./site.json`, or `--site=<path>`. `vitops setup … --dry-run` is accepted as `--dry`.
|
|
21
|
+
- **`@getvitops/emdash` / `@getvitops/astro`: "tick any number" form field** (`vitops_form` field kind `checkboxes`): a fieldset of checkboxes sharing one name. The conversion route joins repeated values (`IT, Marketing`) instead of keeping the last. The "required" toggle is not applied to the group (HTML cannot require one of several).
|
|
22
|
+
- **`@getvitops/core`: size hooks.** `.steps__title`, `.stats__figure` and `.card__title` read `--steps-title-size` (default one step below `heading`), `--stats-figure-size` and `--card-title-size`, set on the block through its `style` field. They set the `--heading-fs` / `--display-fs` properties the type-role utilities already read, so no extra class or prop is needed (all formats).
|
|
23
|
+
- **`@getvitops/create`:** the template's `site.json` lists its contact form under `site.templates`, so the privacy policy inventory and the new check agree.
|
|
24
|
+
- **`@getvitops/emdash`: `theme` on every block, on widget areas and on your own markup.** A `theme` select (`<role>` tinted, `<role>:solid` filled, populated from the site's roles by a new `themes/list` route) is a common field on every `vitops_*` block, applied to the block's own root with the role's computed readable text. `<VitopsWidgetArea theme="…">` takes the same, and `themeAttrs('surface')` gives `{ class, style }` for a layout `<body>`. `vitops_section`'s existing `theme` now goes through the same path.
|
|
25
|
+
- **`@getvitops/cli` / `@getvitops/emdash`: pages with a dropped block fail the build.** `vitops_section` and widget areas emit an invisible `<template data-vitops-unrendered="…">` for any body block with no renderer, and `check-prerender` fails the page naming the types (nested bodies only; `vitops emdash build` also scans the build log, which covers top-level bodies). A regression test imports the plugin entry first with a mock of the real virtual module, so the old import cycle fails it.
|
|
26
|
+
- **`@getvitops/emdash`: `formTemplateFromBlock(block)`** turns a saved `vitops_form` value into a `site.templates` form entry (names as submitted, a `checkboxes` field as a `checkbox` with options), to paste into `site.json` so the generated privacy policy lists what the form collects. The build cannot read the CMS database, so this is run by hand; `check-prerender` warns when fields are missing.
|
|
27
|
+
- **`@getvitops/utils`: `hasFormDelivery`** (`@getvitops/utils/notify`): the one test for "has this form anywhere to go", used by `check-prerender`.
|
|
28
|
+
- **`@getvitops/emdash`:** a `color` hue tint now flips with the colour scheme (`light-dark()`); hero option cards sit at `h2` under an `h1` hero; menu items also accept `hidden: true` (same as the `draft` class); `vitops docs blocks` documents `theme`, the per-row colours, the `--<role>-fs` type-size hook and the form checks.
|
|
29
|
+
- **`@getvitops/create`:** the template redirects `/home` to `/`, and the guides use `vp exec` instead of `vpx`.
|
|
30
|
+
|
|
31
|
+
### Patch Changes
|
|
32
|
+
|
|
33
|
+
- 0a47856: **Breaking: every block type is now `vitops_<name>`** (`vitops_cards`, `vitops_hero`, `vitops_actionLink`, …), where the Portable Text blocks were `vitops.<name>` and the section block type was already `vitops_section`. EmDash validates a block type's slug as `[a-z][a-z0-9_]*`, so `vitops.section` was never possible and the toolkit had two styles for one kind of thing. Underscore is the one form valid everywhere, so it is now the only one.
|
|
34
|
+
|
|
35
|
+
Migration: in stored content and in `seed/seed.json`, rename `_type` values from `vitops.<name>` to `vitops_<name>`; in `PortableText` `components`, rename the keys the same way; the `blocks: { include, exclude }` option takes full type names, so rename those too. `vt_*` names in a site are that site's own types and are unaffected. A search-and-replace of `vitops.(actionLink|imageCompare|copyButton|banner|details|carousel|gallery|cards|accordion|hero|marquee|logos|steps|stats|callout|entries|pricing|embed|form|reviews|nav|badges|widgetArea)` with `vitops_$1` covers it. `vitops emdash build` reports any block left on the old name as having no component.
|
|
36
|
+
|
|
37
|
+
- Updated dependencies [0a47856]
|
|
38
|
+
- @getvitops/utils@8.3.0-alpha.2
|
|
39
|
+
- @getvitops/config@8.3.0-alpha.2
|
|
40
|
+
|
|
3
41
|
## 8.3.0-alpha.1
|
|
4
42
|
|
|
5
43
|
### Minor Changes
|
|
@@ -33,3 +33,9 @@ wc-cards {
|
|
|
33
33
|
[data-card-link] {
|
|
34
34
|
cursor: pointer;
|
|
35
35
|
}
|
|
36
|
+
|
|
37
|
+
/* A card's title keeps the heading role's size unless `--card-title-size` is set on the block
|
|
38
|
+
(its `style` field). The role utility reads --heading-fs; `inherit` passes through the site's. */
|
|
39
|
+
.card__title {
|
|
40
|
+
--heading-fs: var(--card-title-size, inherit);
|
|
41
|
+
}
|
|
@@ -17,6 +17,11 @@
|
|
|
17
17
|
* `display: none`/`visibility: hidden` stops contributing to the accessible
|
|
18
18
|
* name, and that name must read the final value for the whole animation, not
|
|
19
19
|
* just at rest.
|
|
20
|
+
*
|
|
21
|
+
* The visually-hidden rule is restated here for `.counter__value.sr-only` rather than left to the
|
|
22
|
+
* utility: a site whose build does not emit `.sr-only` (the tailwind format only generates
|
|
23
|
+
* utilities it sees used, and does not scan packages) would otherwise show the figure twice
|
|
24
|
+
* while it counts, once as text and once as the animation.
|
|
20
25
|
*/
|
|
21
26
|
@property --_n {
|
|
22
27
|
syntax: '<number>';
|
|
@@ -28,6 +33,18 @@
|
|
|
28
33
|
display: none;
|
|
29
34
|
}
|
|
30
35
|
|
|
36
|
+
.counter__value.sr-only {
|
|
37
|
+
position: absolute;
|
|
38
|
+
inline-size: 1px;
|
|
39
|
+
block-size: 1px;
|
|
40
|
+
margin: -1px;
|
|
41
|
+
padding: 0;
|
|
42
|
+
overflow: hidden;
|
|
43
|
+
clip-path: inset(50%);
|
|
44
|
+
white-space: nowrap;
|
|
45
|
+
border: 0;
|
|
46
|
+
}
|
|
47
|
+
|
|
31
48
|
wc-counter[running] .counter__presentation {
|
|
32
49
|
display: inline;
|
|
33
50
|
animation: counter-run var(--counter-duration, 1.2s) var(--counter-easing, var(--custom-ease-out))
|
|
@@ -6,6 +6,9 @@
|
|
|
6
6
|
* Each item is a plain image, or a link around one. Marks are normalised to a common
|
|
7
7
|
* height rather than a common width, because logos vary far more in aspect ratio than
|
|
8
8
|
* in how tall they should read. `--grayscale` quiets them until hover or focus.
|
|
9
|
+
*
|
|
10
|
+
* An entry with no image is its name set as text (`.logos__name`): a partner without a wordmark
|
|
11
|
+
* still belongs in the row. The name is sized from the same `--logos-height` so it reads as a mark.
|
|
9
12
|
*/
|
|
10
13
|
.logos {
|
|
11
14
|
display: flex;
|
|
@@ -30,6 +33,23 @@
|
|
|
30
33
|
object-fit: contain;
|
|
31
34
|
}
|
|
32
35
|
|
|
36
|
+
.logos__name {
|
|
37
|
+
font-size: calc(var(--logos-height, 2.5rem) * 0.55);
|
|
38
|
+
line-height: 1.1;
|
|
39
|
+
font-weight: 600;
|
|
40
|
+
text-align: center;
|
|
41
|
+
text-wrap: balance;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
&.logos--grayscale .logos__item .logos__name {
|
|
45
|
+
opacity: 0.7;
|
|
46
|
+
transition: opacity 0.2s;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
&.logos--grayscale .logos__item:is(:hover, :focus-within) .logos__name {
|
|
50
|
+
opacity: 1;
|
|
51
|
+
}
|
|
52
|
+
|
|
33
53
|
&.logos--grayscale .logos__item img {
|
|
34
54
|
filter: grayscale(1);
|
|
35
55
|
opacity: 0.7;
|
|
@@ -536,7 +536,7 @@
|
|
|
536
536
|
}
|
|
537
537
|
}
|
|
538
538
|
|
|
539
|
-
/* ── In-page menu (`
|
|
539
|
+
/* ── In-page menu (`vitops_nav` block) ─────────────────────────────────────────
|
|
540
540
|
*
|
|
541
541
|
* A menu read in the body of a page — a site map, a section's links, footer-style columns —
|
|
542
542
|
* as plain nested lists. No disclosure and no drawer: that is the header's job (`.sitenav`).
|
|
@@ -613,6 +613,14 @@
|
|
|
613
613
|
}
|
|
614
614
|
}
|
|
615
615
|
|
|
616
|
+
/* Header buttons that are not menu items (`<SiteNav><a slot="actions" class="cta">…`). */
|
|
617
|
+
.sitenav__actions {
|
|
618
|
+
display: flex;
|
|
619
|
+
flex-wrap: wrap;
|
|
620
|
+
align-items: center;
|
|
621
|
+
gap: 0.5rem;
|
|
622
|
+
}
|
|
623
|
+
|
|
616
624
|
/* ── Footer (`<SiteFooter>`) ───────────────────────────────────────────────── */
|
|
617
625
|
.sitefooter {
|
|
618
626
|
margin-block-start: var(--space-2xl, 3rem);
|
|
@@ -639,6 +647,16 @@
|
|
|
639
647
|
gap: 0.25rem 1.25rem;
|
|
640
648
|
}
|
|
641
649
|
|
|
650
|
+
.sitefooter__heading {
|
|
651
|
+
margin: 0 0 0.5rem;
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
/* A column from a menu item with children: a heading over a stacked list. */
|
|
655
|
+
.sitefooter__list--column {
|
|
656
|
+
flex-direction: column;
|
|
657
|
+
gap: 0.125rem;
|
|
658
|
+
}
|
|
659
|
+
|
|
642
660
|
.sitefooter__link {
|
|
643
661
|
color: inherit;
|
|
644
662
|
text-decoration: underline;
|
|
@@ -34,6 +34,9 @@
|
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
.stats__figure {
|
|
37
|
+
/* Display size by default; --stats-figure-size on the block overrides it (the role utility
|
|
38
|
+
reads --display-fs). `inherit` passes through whatever the site set for the role. */
|
|
39
|
+
--display-fs: var(--stats-figure-size, inherit);
|
|
37
40
|
color: var(--tone-fg, inherit);
|
|
38
41
|
font-variant-numeric: tabular-nums;
|
|
39
42
|
}
|
|
@@ -69,6 +69,13 @@
|
|
|
69
69
|
.steps__text {
|
|
70
70
|
margin: 0;
|
|
71
71
|
}
|
|
72
|
+
|
|
73
|
+
/* A step title is a sub-heading, not a section heading: one notch down from the role's size.
|
|
74
|
+
Set --steps-title-size on the block (its `style` field) to change it. The role utility reads
|
|
75
|
+
--heading-fs, so this sets the property it already consumes rather than fighting its layer. */
|
|
76
|
+
.steps__title {
|
|
77
|
+
--heading-fs: var(--steps-title-size, var(--text-l, 1.2rem));
|
|
78
|
+
}
|
|
72
79
|
}
|
|
73
80
|
|
|
74
81
|
/* Side by side once there is room: the marker sits above its text and the line runs across. */
|
package/dist/index.d.mts
CHANGED
|
@@ -67,7 +67,7 @@ interface GenerateOptions {
|
|
|
67
67
|
* `tailwind` format only: directories (or globs) Tailwind should scan for class names, written
|
|
68
68
|
* as `@source` lines after `@import "tailwindcss"`. Paths are relative to the emitted
|
|
69
69
|
* `tailwind.css`. Tailwind v4 does not scan `node_modules`, so a utility used only by a package's
|
|
70
|
-
* own renderer (`font-display` in `
|
|
70
|
+
* own renderer (`font-display` in `vitops_stats`) is otherwise never generated — silently.
|
|
71
71
|
* Ignored by the css and bricks formats, which ship every framework class.
|
|
72
72
|
*/
|
|
73
73
|
sources?: string[];
|
|
@@ -273,9 +273,9 @@ interface TierEntry {
|
|
|
273
273
|
bricks?: string;
|
|
274
274
|
/**
|
|
275
275
|
* `@getvitops/emdash` Portable Text block `_type`s that render this pattern, e.g.
|
|
276
|
-
* `
|
|
276
|
+
* `vitops_carousel`. An array because one pattern can have several blocks
|
|
277
277
|
* (`details` has a single disclosure and an accordion group) and one block can span
|
|
278
|
-
* patterns (`
|
|
278
|
+
* patterns (`vitops_actionLink` is `link`, `btn` and `cta`).
|
|
279
279
|
*
|
|
280
280
|
* Tier 3, a sibling of `astro` and `bricks`: a block is a platform wrapper that an
|
|
281
281
|
* editor drops into a page, and each one is an adapter over this entry's Astro
|
|
@@ -686,7 +686,7 @@ export declare function toHtmlFragment(blocks: Block[]): string;
|
|
|
686
686
|
* Two deliberate mappings:
|
|
687
687
|
* - the `# ` heading is **dropped** — in EmDash the page title is a field
|
|
688
688
|
* (`data.title`), and repeating it as the first content block renders twice;
|
|
689
|
-
* - the review blockquote becomes a `
|
|
689
|
+
* - the review blockquote becomes a `vitops_banner` with `tone: "warning"`,
|
|
690
690
|
* which is exactly the block the hand-written placeholder pages used.
|
|
691
691
|
*
|
|
692
692
|
* `_key`s are derived from position rather than randomness, so regenerating an
|
package/dist/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/generate.ts","../src/docs.ts","../src/tiers.ts","../src/token-refs.ts","../src/legal/providers.ts","../src/legal/derive.ts","../src/legal/templates/cookies.ca.ts","../src/legal/templates/cookies.qc.ts","../src/legal/templates/privacy.ca.ts","../src/legal/templates/privacy.qc.ts","../src/legal/templates/terms.ca.ts","../src/legal/templates/terms.qc.ts","../src/legal/templates/index.ts","../src/legal/render.ts","../src/legal/index.ts","../src/robots.ts","../src/index.ts"],"mappings":";;;;;
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/generate.ts","../src/docs.ts","../src/tiers.ts","../src/token-refs.ts","../src/legal/providers.ts","../src/legal/derive.ts","../src/legal/templates/cookies.ca.ts","../src/legal/templates/cookies.qc.ts","../src/legal/templates/privacy.ca.ts","../src/legal/templates/privacy.qc.ts","../src/legal/templates/terms.ca.ts","../src/legal/templates/terms.qc.ts","../src/legal/templates/index.ts","../src/legal/render.ts","../src/legal/index.ts","../src/robots.ts","../src/index.ts"],"mappings":"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"}
|
package/dist/index.mjs
CHANGED
|
@@ -6,6 +6,7 @@ import { dirname, join } from "node:path";
|
|
|
6
6
|
import { fileURLToPath } from "node:url";
|
|
7
7
|
import { createHash } from "node:crypto";
|
|
8
8
|
import { transform } from "lightningcss";
|
|
9
|
+
import { pickOn } from "@getvitops/utils/color";
|
|
9
10
|
import { AD_PLATFORMS, AD_PROVIDER_KEYS } from "@getvitops/utils/ads";
|
|
10
11
|
import { generateIconInclude, nodesToHtml, resolveIcon, t } from "@getvitops/utils";
|
|
11
12
|
import { resolveSitemapUrl } from "@getvitops/utils/indexing";
|
|
@@ -49,7 +50,7 @@ const TIERS = {
|
|
|
49
50
|
use: "`<Tree items={…} />` — it emits `<wc-tree>` itself, so do NOT add your own wrapper."
|
|
50
51
|
},
|
|
51
52
|
carousel: {
|
|
52
|
-
emdash: ["
|
|
53
|
+
emdash: ["vitops_carousel"],
|
|
53
54
|
css: c([
|
|
54
55
|
"carousel",
|
|
55
56
|
"carousel__track",
|
|
@@ -67,7 +68,7 @@ const TIERS = {
|
|
|
67
68
|
use: "`<Carousel slides={…} label=\"…\" />` — it emits `<wc-carousel>` itself, so do NOT add your own wrapper. `.carousel` is the shell and `.carousel__track` the scroller; add `loop` for the cloned infinite strip (`autoplay` implies it)."
|
|
68
69
|
},
|
|
69
70
|
gallery: {
|
|
70
|
-
emdash: ["
|
|
71
|
+
emdash: ["vitops_gallery"],
|
|
71
72
|
css: c([
|
|
72
73
|
"gallery",
|
|
73
74
|
"gallery__item",
|
|
@@ -83,7 +84,7 @@ const TIERS = {
|
|
|
83
84
|
use: "`<Gallery images={…} label=\"…\" />` — it emits `<wc-gallery>` wrapping a `<Carousel />` (which emits its own `<wc-carousel>`) itself, so do NOT add your own wrapper around either. Each thumbnail is a plain `<a href=\"#slide-id\">` into the carousel's own `<li id=\"slide-id\">` inside a single `<dialog class=\"lightbox-dialog\">` (see `lightbox`), so it is a working modal lightbox — WITH prev/next — before any script runs. Stepping between images is the carousel's job, not the gallery's."
|
|
84
85
|
},
|
|
85
86
|
entries: {
|
|
86
|
-
emdash: ["
|
|
87
|
+
emdash: ["vitops_entries"],
|
|
87
88
|
css: c([
|
|
88
89
|
"entries__table",
|
|
89
90
|
"entries__scroll",
|
|
@@ -99,7 +100,7 @@ const TIERS = {
|
|
|
99
100
|
use: "`<Entries entries={[{ heading, pairs: [{ term, value }] }]} />` — it emits `<wc-entries>` itself, so do NOT add your own wrapper. By hand: `<wc-entries>` around `<h3>` + `<dl>` pairs. The tier-2 exemplar: semantic pairs with no JS, a table with it."
|
|
100
101
|
},
|
|
101
102
|
copy: {
|
|
102
|
-
emdash: ["
|
|
103
|
+
emdash: ["vitops_copyButton"],
|
|
103
104
|
css: c([
|
|
104
105
|
"copy",
|
|
105
106
|
"copy__button",
|
|
@@ -115,7 +116,7 @@ const TIERS = {
|
|
|
115
116
|
use: "`<wc-copy>` around the content and a `[data-copy]` button."
|
|
116
117
|
},
|
|
117
118
|
dismissable: {
|
|
118
|
-
emdash: ["
|
|
119
|
+
emdash: ["vitops_banner"],
|
|
119
120
|
css: c(["banner__close", "notification__close"], "patterns/banner.css"),
|
|
120
121
|
wc: {
|
|
121
122
|
tag: "wc-dismissable",
|
|
@@ -141,7 +142,7 @@ const TIERS = {
|
|
|
141
142
|
use: "`<wc-split-panel>` with two children. Unenhanced they simply stack."
|
|
142
143
|
},
|
|
143
144
|
"image-compare": {
|
|
144
|
-
emdash: ["
|
|
145
|
+
emdash: ["vitops_imageCompare"],
|
|
145
146
|
css: c([
|
|
146
147
|
"image-compare",
|
|
147
148
|
"image-compare__before",
|
|
@@ -156,7 +157,7 @@ const TIERS = {
|
|
|
156
157
|
use: "`<wc-image-compare>` with two images. Both are visible without JS."
|
|
157
158
|
},
|
|
158
159
|
marquee: {
|
|
159
|
-
emdash: ["
|
|
160
|
+
emdash: ["vitops_marquee"],
|
|
160
161
|
css: c([
|
|
161
162
|
"marquee",
|
|
162
163
|
"marquee__content",
|
|
@@ -194,7 +195,7 @@ const TIERS = {
|
|
|
194
195
|
use: "`.section.region` > `.centered` > `.section__inner` holding `.section__head` (kicker, heading, sub) and `.section__body`. `.section--split` puts the head beside the body when wide. In EmDash it is a block TYPE (`vitops_section`) with a Portable Text body that accepts any other block — see `vitopsBlockTypes()`."
|
|
195
196
|
},
|
|
196
197
|
steps: {
|
|
197
|
-
emdash: ["
|
|
198
|
+
emdash: ["vitops_steps"],
|
|
198
199
|
css: c([
|
|
199
200
|
"steps",
|
|
200
201
|
"steps__item",
|
|
@@ -206,7 +207,7 @@ const TIERS = {
|
|
|
206
207
|
use: "`<ol class=\"steps tone\">` of `<li class=\"steps__item\">` each holding a `.steps__body` with a real heading. The number is a CSS counter and the connector line a pseudo-element; `.steps--row` runs it sideways when wide."
|
|
207
208
|
},
|
|
208
209
|
stats: {
|
|
209
|
-
emdash: ["
|
|
210
|
+
emdash: ["vitops_stats"],
|
|
210
211
|
css: c([
|
|
211
212
|
"stats",
|
|
212
213
|
"stats__item",
|
|
@@ -219,7 +220,7 @@ const TIERS = {
|
|
|
219
220
|
use: "`<ul class=\"stats\">` of `.stats__item`, each figure a `<Counter>`. `.stats--tiles` adds `.stats__group` and `.stats__delta`; `data-state=\"up|down\"` colours the delta through the success/danger roles."
|
|
220
221
|
},
|
|
221
222
|
callout: {
|
|
222
|
-
emdash: ["
|
|
223
|
+
emdash: ["vitops_callout"],
|
|
223
224
|
css: c([
|
|
224
225
|
"callout",
|
|
225
226
|
"callout__body",
|
|
@@ -231,16 +232,17 @@ const TIERS = {
|
|
|
231
232
|
use: "`.callout.tone` with a `.callout__body` and `.callout__actions`. A call-to-action band that stays on the page — NOT `banner`, which is a dismissible announcement."
|
|
232
233
|
},
|
|
233
234
|
logos: {
|
|
234
|
-
emdash: ["
|
|
235
|
+
emdash: ["vitops_logos"],
|
|
235
236
|
css: c([
|
|
236
237
|
"logos",
|
|
237
238
|
"logos__item",
|
|
239
|
+
"logos__name",
|
|
238
240
|
"logos--grayscale"
|
|
239
241
|
], "patterns/logos.css"),
|
|
240
|
-
use: "`<ul class=\"logos\">` of `.logos__item` images (alt text = the name). A still grid; `marquee` is the scrolling version."
|
|
242
|
+
use: "`<ul class=\"logos\">` of `.logos__item` images (alt text = the name), or `.logos__name` text when there is no image. A still grid; `marquee` is the scrolling version."
|
|
241
243
|
},
|
|
242
244
|
embed: {
|
|
243
|
-
emdash: ["
|
|
245
|
+
emdash: ["vitops_embed"],
|
|
244
246
|
css: c([
|
|
245
247
|
"embed",
|
|
246
248
|
"embed__frame",
|
|
@@ -249,7 +251,7 @@ const TIERS = {
|
|
|
249
251
|
use: "`.embed` around an `<iframe class=\"embed__frame\" data-consent=\"marketing\" data-consent-src=\"…\">` with NO `src`, and a `.embed__placeholder`. The consent runtime assigns the address after the grant; the placeholder holds a real link. Never give the iframe a live `src`."
|
|
250
252
|
},
|
|
251
253
|
counter: {
|
|
252
|
-
emdash: ["
|
|
254
|
+
emdash: ["vitops_stats"],
|
|
253
255
|
css: c([
|
|
254
256
|
"counter",
|
|
255
257
|
"counter__value",
|
|
@@ -264,7 +266,7 @@ const TIERS = {
|
|
|
264
266
|
use: "`<Counter value=\"94%\" />` — it emits `<wc-counter>` itself, so do NOT add your own wrapper. The final value is parsed from the fallback text, never passed separately."
|
|
265
267
|
},
|
|
266
268
|
"multi-field": {
|
|
267
|
-
emdashExempt: "repeats rows a visitor adds at submit time (several phone numbers, guests); `
|
|
269
|
+
emdashExempt: "repeats rows a visitor adds at submit time (several phone numbers, guests); `vitops_form` has a fixed, editor-defined field list and no such field type",
|
|
268
270
|
css: c([
|
|
269
271
|
"form-group",
|
|
270
272
|
"input-group",
|
|
@@ -279,14 +281,14 @@ const TIERS = {
|
|
|
279
281
|
use: "`<wc-multi-field>` around a field template. One row submits fine with no JS."
|
|
280
282
|
},
|
|
281
283
|
turnstile: {
|
|
282
|
-
emdash: ["
|
|
284
|
+
emdash: ["vitops_form"],
|
|
283
285
|
css: c(["form-group"], "patterns/forms.css"),
|
|
284
286
|
wc: {
|
|
285
287
|
tag: "wc-turnstile",
|
|
286
288
|
registered: true,
|
|
287
289
|
adds: "loads Cloudflare Turnstile on first interaction with the form and holds a submit until its token arrives"
|
|
288
290
|
},
|
|
289
|
-
use: "`<wc-turnstile sitekey=\"…\">` inside a `<form>`, around a paragraph saying the spam check needs JavaScript. Pair it with `createConversionRoute({ turnstile })`; the `
|
|
291
|
+
use: "`<wc-turnstile sitekey=\"…\">` inside a `<form>`, around a paragraph saying the spam check needs JavaScript. Pair it with `createConversionRoute({ turnstile })`; the `vitops_form` block does both."
|
|
290
292
|
},
|
|
291
293
|
"color-scheme-toggle": {
|
|
292
294
|
emdashExempt: "site chrome (header/footer), not page content; belongs in the nav/layout",
|
|
@@ -375,7 +377,7 @@ const TIERS = {
|
|
|
375
377
|
use: "Not shipped. See `color-wheel`."
|
|
376
378
|
},
|
|
377
379
|
details: {
|
|
378
|
-
emdash: ["
|
|
380
|
+
emdash: ["vitops_details", "vitops_accordion"],
|
|
379
381
|
css: c([
|
|
380
382
|
"details",
|
|
381
383
|
"details-trigger",
|
|
@@ -413,7 +415,7 @@ const TIERS = {
|
|
|
413
415
|
use: "`<NavShell>` with a `nav` slot, plus `<NavShellToggle>` if the toggle must live outside the shell."
|
|
414
416
|
},
|
|
415
417
|
subgrid: {
|
|
416
|
-
emdash: ["
|
|
418
|
+
emdash: ["vitops_pricing"],
|
|
417
419
|
css: c([
|
|
418
420
|
"subgrid",
|
|
419
421
|
"subgrid-card",
|
|
@@ -423,8 +425,8 @@ const TIERS = {
|
|
|
423
425
|
use: "`<Subgrid>` — emits `<ul class=\"subgrid\" role=\"list\">`; author `<li class=\"card subgrid-card\">` items yourself, so tranches align across the set."
|
|
424
426
|
},
|
|
425
427
|
card: {
|
|
426
|
-
emdash: ["
|
|
427
|
-
css: c(["card"], "patterns/card.css", true),
|
|
428
|
+
emdash: ["vitops_cards"],
|
|
429
|
+
css: c(["card", "card__title"], "patterns/card.css", true),
|
|
428
430
|
wc: {
|
|
429
431
|
tag: "wc-cards",
|
|
430
432
|
registered: true,
|
|
@@ -444,7 +446,7 @@ const TIERS = {
|
|
|
444
446
|
use: "`<Icon name=\"menu\" />`. Names are semantic and resolve per configured set; a name containing `:` passes through."
|
|
445
447
|
},
|
|
446
448
|
split: {
|
|
447
|
-
emdash: ["
|
|
449
|
+
emdash: ["vitops_hero"],
|
|
448
450
|
css: c([
|
|
449
451
|
"split",
|
|
450
452
|
"split-1-2",
|
|
@@ -472,9 +474,9 @@ const TIERS = {
|
|
|
472
474
|
"navlist--columns"
|
|
473
475
|
], "patterns/sitenav.css"),
|
|
474
476
|
astro: [...a("SiteNav", "css"), ...a("SiteFooter", "css")],
|
|
475
|
-
emdash: ["
|
|
477
|
+
emdash: ["vitops_nav"],
|
|
476
478
|
bricks: "vitops-sitenav",
|
|
477
|
-
use: "`.sitenav` with `--bp-*` and a drawer direction. `<details>` + Invoker Commands, no JS. In Astro: `<SiteNav {...await loadNavMenus(getMenu)} />` renders EmDash menu `primary` (the default: dropdowns from `md`, drawer below) and, if they exist, `primary-mobile` (depth-1 nav below `md`) and `primary-mega` (mega panels from `xl`) — adding the menu in the admin is the whole change. An item's `titleAttr` is its description. `<SiteFooter sitemap legal name>` renders `footer-sitemap` + `footer-legal` + the copyright line. For a menu inside page content (a site map, a section's links, footer-style columns) use `.navlist` — plain nested lists, `--inline` / `--columns` — or, in EmDash, the `
|
|
479
|
+
use: "`.sitenav` with `--bp-*` and a drawer direction. `<details>` + Invoker Commands, no JS. In Astro: `<SiteNav {...await loadNavMenus(getMenu)} />` renders EmDash menu `primary` (the default: dropdowns from `md`, drawer below) and, if they exist, `primary-mobile` (depth-1 nav below `md`) and `primary-mega` (mega panels from `xl`) — adding the menu in the admin is the whole change. An item's `titleAttr` is its description. `<SiteFooter sitemap legal name>` renders `footer-sitemap` + `footer-legal` + the copyright line. For a menu inside page content (a site map, a section's links, footer-style columns) use `.navlist` — plain nested lists, `--inline` / `--columns` — or, in EmDash, the `vitops_nav` block (menu, layout `list|inline|columns|sitenav`, depth 1–3, heading, tone). Name each `<nav>` uniquely: the header is `Primary` and the footer `Site map` / `Legal`, and the block never reuses those."
|
|
478
480
|
},
|
|
479
481
|
"split-link": {
|
|
480
482
|
css: c([
|
|
@@ -486,17 +488,17 @@ const TIERS = {
|
|
|
486
488
|
use: "`.split-link` — a link plus a disclosure toggle. No JS."
|
|
487
489
|
},
|
|
488
490
|
btn: {
|
|
489
|
-
emdash: ["
|
|
491
|
+
emdash: ["vitops_actionLink"],
|
|
490
492
|
css: c(["btn"], void 0, true),
|
|
491
493
|
use: "`<button>` gets it with no class; `.btn` carries it to other tags. `fill: false`, so states drive `color`."
|
|
492
494
|
},
|
|
493
495
|
cta: {
|
|
494
|
-
emdash: ["
|
|
496
|
+
emdash: ["vitops_actionLink"],
|
|
495
497
|
css: c(["cta", "cta-danger"], void 0, true),
|
|
496
498
|
use: "`.cta` on any element — usually `<a>`, since a CTA navigates. Filled, with role variants."
|
|
497
499
|
},
|
|
498
500
|
link: {
|
|
499
|
-
emdash: ["
|
|
501
|
+
emdash: ["vitops_actionLink"],
|
|
500
502
|
css: c([
|
|
501
503
|
"link",
|
|
502
504
|
"skip-link",
|
|
@@ -506,18 +508,18 @@ const TIERS = {
|
|
|
506
508
|
use: "`<a>` gets it at zero specificity; `.link` for other tags. `.stretched-link` makes one link cover its positioned ancestor (whole-card click, no JS — but the card text stops being selectable); `.raised` lifts content back above that overlay."
|
|
507
509
|
},
|
|
508
510
|
badge: {
|
|
509
|
-
emdash: ["
|
|
511
|
+
emdash: ["vitops_badges"],
|
|
510
512
|
css: c(["badge", "badge-indicator"], "patterns/tag.css", true),
|
|
511
513
|
use: "`.badge` — a STATIC label. Use `tag` if it is dismissable or editable."
|
|
512
514
|
},
|
|
513
515
|
"widget-area": {
|
|
514
|
-
emdash: ["
|
|
516
|
+
emdash: ["vitops_widgetArea"],
|
|
515
517
|
css: c([
|
|
516
518
|
"widget-area",
|
|
517
519
|
"widget__title",
|
|
518
520
|
"widget__menu"
|
|
519
521
|
], "patterns/widget-area.css"),
|
|
520
|
-
use: "Editor-managed regions: `<VitopsWidgetArea name=\"footer-columns\" />` (`@getvitops/emdash/astro`) renders an area from Widgets in the admin — titled groups, a menu as a labelled `<nav>`, content as Portable Text. In a page, the `
|
|
522
|
+
use: "Editor-managed regions: `<VitopsWidgetArea name=\"footer-columns\" />` (`@getvitops/emdash/astro`) renders an area from Widgets in the admin — titled groups, a menu as a labelled `<nav>`, content as Portable Text. In a page, the `vitops_widgetArea` block places one. An area holding a `core:*` component widget falls back to EmDash's own renderer, and an area cannot hold another. Prerendered pages show an edit after the next deploy."
|
|
521
523
|
},
|
|
522
524
|
tag: {
|
|
523
525
|
css: c([
|
|
@@ -581,8 +583,8 @@ const TIERS = {
|
|
|
581
583
|
"review__source"
|
|
582
584
|
], "patterns/review.css", true),
|
|
583
585
|
astro: a("ReviewCard", "css"),
|
|
584
|
-
emdash: ["
|
|
585
|
-
use: "`<ReviewCard review={…} />` — one card, from `vitops reviews`' `.vitops/reviews.json` cache. Put several inside `<Carousel>` slides or `<Marquee>` items; there is no group component and no `wc-review`. In EmDash, `
|
|
586
|
+
emdash: ["vitops_reviews"],
|
|
587
|
+
use: "`<ReviewCard review={…} />` — one card, from `vitops reviews`' `.vitops/reviews.json` cache. Put several inside `<Carousel>` slides or `<Marquee>` items; there is no group component and no `wc-review`. In EmDash, `vitops_reviews` is that whole row: it reads the cache as a build-time import (`virtual:getvitops/reviews`, served by `vitops()`; empty when the file is absent), picks reviews with `selectReviews()`, and lays them out as a grid, carousel or marquee. A new review appears after `vitops reviews` is re-run, committed and the site rebuilt."
|
|
586
588
|
},
|
|
587
589
|
tabs: {
|
|
588
590
|
css: c(["tabs"], void 0, true),
|
|
@@ -606,7 +608,7 @@ const TIERS = {
|
|
|
606
608
|
use: "`.navbar` with `--start/--center/--end`."
|
|
607
609
|
},
|
|
608
610
|
banner: {
|
|
609
|
-
emdash: ["
|
|
611
|
+
emdash: ["vitops_banner"],
|
|
610
612
|
css: c([
|
|
611
613
|
"banner",
|
|
612
614
|
"banner__content",
|
|
@@ -2505,7 +2507,7 @@ ${bricks}
|
|
|
2505
2507
|
Portable Text block ${code("_type")}s from ${code("@getvitops/emdash")}. Repeating patterns are
|
|
2506
2508
|
repeater fields. A Portable Text block cannot contain other blocks, so a pattern that must hold
|
|
2507
2509
|
them is a schema-level block TYPE with a Portable Text body instead — ${code("vitops_section")},
|
|
2508
|
-
from ${code("vitopsBlockTypes()")}. Its body accepts every ${code("
|
|
2510
|
+
from ${code("vitopsBlockTypes()")}. Its body accepts every ${code("vitops_*")} block below.
|
|
2509
2511
|
|
|
2510
2512
|
| Block | Pattern | Renders via |
|
|
2511
2513
|
| --- | --- | --- |
|
|
@@ -4429,7 +4431,7 @@ function toHtmlFragment(blocks) {
|
|
|
4429
4431
|
* Two deliberate mappings:
|
|
4430
4432
|
* - the `# ` heading is **dropped** — in EmDash the page title is a field
|
|
4431
4433
|
* (`data.title`), and repeating it as the first content block renders twice;
|
|
4432
|
-
* - the review blockquote becomes a `
|
|
4434
|
+
* - the review blockquote becomes a `vitops_banner` with `tone: "warning"`,
|
|
4433
4435
|
* which is exactly the block the hand-written placeholder pages used.
|
|
4434
4436
|
*
|
|
4435
4437
|
* `_key`s are derived from position rather than randomness, so regenerating an
|
|
@@ -4442,7 +4444,7 @@ function toPortableText(blocks, prefix) {
|
|
|
4442
4444
|
if (b.kind === "heading" && b.level === 1) return;
|
|
4443
4445
|
if (b.kind === "quote") {
|
|
4444
4446
|
out.push({
|
|
4445
|
-
_type: "
|
|
4447
|
+
_type: "vitops_banner",
|
|
4446
4448
|
_key: key,
|
|
4447
4449
|
message: b.spans.map((s) => s.text).join(""),
|
|
4448
4450
|
tone: "warning",
|
|
@@ -4494,7 +4496,7 @@ const REVIEW_NOTICE = "> Generated from this site's configuration. It is a start
|
|
|
4494
4496
|
* designated and is published, that a privacy impact assessment precedes any
|
|
4495
4497
|
* communication outside Quebec, that consent mechanics match the Act, and — easy
|
|
4496
4498
|
* to miss — that content directed at Quebec consumers is available in French. It
|
|
4497
|
-
* is a second blockquote (a second `
|
|
4499
|
+
* is a second blockquote (a second `vitops_banner` in Portable Text) so it cannot
|
|
4498
4500
|
* be skimmed past as part of the first.
|
|
4499
4501
|
*/
|
|
4500
4502
|
const QC_REVIEW_NOTICE = "> Quebec (Law 25): this document is written for the Quebec privacy regime and has NOT been reviewed by counsel. Before publishing, have a lawyer confirm that a person in charge of the protection of personal information is designated, that any transfer of information outside Quebec is preceded by a privacy impact assessment, that your consent and cookie mechanisms match the Act, and whether a French version is required for your audience. It is not legal advice.";
|
|
@@ -5234,6 +5236,18 @@ function generateLegal(cfg, opts = {}) {
|
|
|
5234
5236
|
*/
|
|
5235
5237
|
const HERE = dirname(fileURLToPath(import.meta.url));
|
|
5236
5238
|
const DEFAULT_ASSETS = join(HERE, "..", "assets");
|
|
5239
|
+
/**
|
|
5240
|
+
* `--color-<hue>-<step>-on`: the readable text colour for a fill at that step, computed with the
|
|
5241
|
+
* same APCA pick as `text-on-<role>`. A role has one (`--color-text-on-<role>`); a raw hue step
|
|
5242
|
+
* did not, so a block filling with `color: amber-500` had no site-defined foreground to use. These
|
|
5243
|
+
* live in a plain `:root`, never `@theme`: a `--color-*` theme key would mint `text-<hue>-<step>-on`
|
|
5244
|
+
* utilities nobody asked for.
|
|
5245
|
+
*/
|
|
5246
|
+
function hueOnTokens(palette) {
|
|
5247
|
+
const out = [];
|
|
5248
|
+
for (const [name, hue] of Object.entries(palette)) for (const [n, hex] of Object.entries(hue.numeric)) out.push(`--color-${name}-${n}-on: ${pickOn(hex)};`);
|
|
5249
|
+
return out;
|
|
5250
|
+
}
|
|
5237
5251
|
const UTILITY_PROPS = {
|
|
5238
5252
|
bg: "background-color",
|
|
5239
5253
|
text: "color",
|
|
@@ -5522,6 +5536,8 @@ function build(ds, format, assetsDir, buildOpts = {}) {
|
|
|
5522
5536
|
css += ` color-scheme: ${buildOpts.darkOnly ? "dark" : "light"};\n`;
|
|
5523
5537
|
css += ` /* Hue scales (numeric steps, 50 = tinted near-white … 950 = tinted near-black) */\n`;
|
|
5524
5538
|
for (const [name, hue] of Object.entries(expandedPalette)) for (const [n, hex] of Object.entries(hue.numeric)) css += ` --color-${name}-${n}: ${hex};\n`;
|
|
5539
|
+
css += `\n /* Foreground for a fill at a hue step (--color-<hue>-<step>-on) */\n`;
|
|
5540
|
+
for (const line of hueOnTokens(expandedPalette)) css += ` ${line}\n`;
|
|
5525
5541
|
css += `\n /* Role tokens (the public API) */\n`;
|
|
5526
5542
|
for (const fr of scaleRoles) for (const [t, val] of Object.entries(fr.light)) css += ` ${tokenVar(fr.role, t)}: ${val};\n`;
|
|
5527
5543
|
if (focusRole) {
|
|
@@ -6034,6 +6050,7 @@ function emitTailwind(ctx) {
|
|
|
6034
6050
|
fn += ` --surface-glass: color-mix(in oklch, var(--color-bg-surface) 72%, transparent);\n`;
|
|
6035
6051
|
fn += ` --overlay: color-mix(in oklch, var(--color-${surface.hue}-950) 45%, transparent);\n`;
|
|
6036
6052
|
}
|
|
6053
|
+
for (const line of hueOnTokens(expandedPalette)) fn += ` ${line}\n`;
|
|
6037
6054
|
if (ctx.darkOnly) fn = ` color-scheme: dark;\n${fn}`;
|
|
6038
6055
|
parts.push(`/* Role tokens (the public API) */\n:root {\n${fn}}\n`);
|
|
6039
6056
|
}
|