@getvitops/generator 8.2.0 → 8.3.0-alpha.1
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 +116 -0
- package/assets/css/index.css +8 -0
- package/assets/css/patterns/callout.css +39 -0
- package/assets/css/patterns/embed.css +39 -0
- package/assets/css/patterns/forms.css +14 -0
- package/assets/css/patterns/logos.css +45 -0
- package/assets/css/patterns/section.css +61 -0
- package/assets/css/patterns/sitenav.css +262 -0
- package/assets/css/patterns/stats.css +61 -0
- package/assets/css/patterns/steps.css +98 -0
- package/assets/css/patterns/tone.css +38 -0
- package/assets/css/patterns/widget-area.css +22 -0
- package/assets/js/elements.js +3 -3
- package/config.schema.json +126 -10
- package/dist/{config-BfcWKH-5.mjs → config-D5xuz8--.mjs} +79 -15
- package/dist/config-D5xuz8--.mjs.map +1 -0
- package/dist/config.d.mts +77 -18
- package/dist/config.d.mts.map +1 -1
- package/dist/config.mjs +1 -1
- package/dist/index.d.mts +40 -3
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +574 -42
- package/dist/index.mjs.map +1 -1
- package/dist/{schema-BV2n2phj.d.mts → schema-C-P9dF9F.d.mts} +49 -3
- package/dist/schema-C-P9dF9F.d.mts.map +1 -0
- package/dist/{tokens-C1RJwFeG.mjs → tokens-CaIGQQLk.mjs} +56 -11
- package/dist/tokens-CaIGQQLk.mjs.map +1 -0
- package/dist/{tokens-BzBbTeTi.d.mts → tokens-DH5cpvKd.d.mts} +24 -7
- package/dist/tokens-DH5cpvKd.d.mts.map +1 -0
- package/dist/tokens.d.mts +1 -1
- package/dist/tokens.mjs +1 -1
- package/package.json +4 -4
- package/schema.json +95 -5
- package/dist/config-BfcWKH-5.mjs.map +0 -1
- package/dist/config-Bhg3Xv3D.mjs +0 -1774
- package/dist/config-Bhg3Xv3D.mjs.map +0 -1
- package/dist/schema-BV2n2phj.d.mts.map +0 -1
- package/dist/tokens-BzBbTeTi.d.mts.map +0 -1
- package/dist/tokens-C1RJwFeG.mjs.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,121 @@
|
|
|
1
1
|
# @getvitops/generator
|
|
2
2
|
|
|
3
|
+
## 8.3.0-alpha.1
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 24c568b: Escape hatches on every block, a section theme, widget areas, a badges block, Tailwind scanning of package renderers, and a sturdier `vitops audit`.
|
|
8
|
+
|
|
9
|
+
**@getvitops/emdash**
|
|
10
|
+
|
|
11
|
+
- Every `vitops.*` block and `vitops_section` gains three fields: `anchor` (rendered as the `id`, so `#contact` works), `className` (`class_name` on the section type: block-type slugs are lowercase) and `style`. `style` takes CSS custom properties only (`--p-card: 2rem`); any other declaration, and any value containing `url(`, `@`, `{`, `}` or a backslash, is dropped. With the `tailwind` format a class typed in the CMS only works if the site already uses it. They are applied to the block's own outermost element (the grid `<ul>` for `vitops.cards`), never an extra wrapper, so a block's markup is the same whether or not an editor fills them in.
|
|
12
|
+
- `vitops_section` gains a `theme` select: a role, or `<role>:solid`, with readable text chosen by the generator's contrast rules. It overrides `tone`/`color`/`solid`, which stay for existing content. Other site roles still go through `tone`.
|
|
13
|
+
- New `vitops.badges` block (a row of `.badge` or `.tag` labels, each with an optional tone, icon and link) and `vitops.widgetArea` block, plus `<VitopsWidgetArea name level />` from `@getvitops/emdash/astro`. A widget area is content defined once under Widgets and shown wherever it is placed. An area cannot hold another area, and an area containing a `core:*` component widget is rendered by EmDash's own renderer.
|
|
14
|
+
- Existing sites: re-run the block-type seed only on a new database; on an existing one add the `theme`, `anchor`, `className` and `style` fields to the `vitops_section` block type in the admin.
|
|
15
|
+
|
|
16
|
+
**@getvitops/create**: the emdash template seeds `announcement` and `pre-footer` widget areas, renders them in `Layout.astro`, and documents when to use a Section, a block or a widget area. Sites scaffolded earlier add the two `<VitopsWidgetArea>` lines and the `widgetAreas` seed entries by hand.
|
|
17
|
+
|
|
18
|
+
**@getvitops/generator / astro / vite** (`tailwind` format only): the Astro integration now adds `@source` lines for `@getvitops/astro` and `@getvitops/emdash`, because Tailwind v4 does not scan `node_modules`; utilities used only by their renderers (`font-display`, `font-caption`, `font-footnote`) were never emitted and the page silently rendered at body size. New `sources` option on `generate()` and the Vite plugin. A site's `src/package-classes.txt` workaround can be deleted. css and bricks: no change.
|
|
19
|
+
|
|
20
|
+
**@getvitops/cli**: `vitops emdash check-prerender` now also warns when a page uses a type-role class (`font-display`, …) the built CSS has no rule for; `--strict` makes it fail. `vitops audit` checks for its optional packages and a Chromium binary before starting the preview server (so a failed run no longer leaves port 4398 busy), stops the server on Ctrl+C and termination, and takes its page list from the built sitemap, then the menus; the guessed defaults (`/privacy`, `/contact`, …) are used only when neither exists.
|
|
21
|
+
|
|
22
|
+
**@getvitops/utils**: the audit helpers behind the above (`preflight`, `fetchSitemapPaths`, `sitemapLocs`, `pathsFromSitemapLocs`).
|
|
23
|
+
|
|
24
|
+
**@getvitops/core** (all formats): new `.block-anchor` rule gives an anchored block `scroll-margin-top: var(--header-offset, 5rem)`; set `--header-offset` to a sticky header's height. New `widget-area.css` partial. No other consumer-facing change.
|
|
25
|
+
|
|
26
|
+
The other fixed-group packages: no consumer-facing change beyond the above.
|
|
27
|
+
|
|
28
|
+
**Also fixed in the emdash template** (`site.json`): it defined only the `display`, `heading` and `body` type roles while the blocks use `title`, `eyebrow`, `lead`, `caption` and `footnote`, so those rendered at body size with no rule behind them. The roles are added with the reference design system's specs. A site scaffolded earlier adds the same five entries under `designSystem.themes.default.typography.roles`; `vitops emdash check-prerender` now names any that are missing.
|
|
29
|
+
|
|
30
|
+
### Patch Changes
|
|
31
|
+
|
|
32
|
+
- Updated dependencies [24c568b]
|
|
33
|
+
- @getvitops/utils@8.3.0-alpha.1
|
|
34
|
+
- @getvitops/config@8.3.0-alpha.1
|
|
35
|
+
|
|
36
|
+
## 8.3.0-alpha.0
|
|
37
|
+
|
|
38
|
+
### Minor Changes
|
|
39
|
+
|
|
40
|
+
- 93d2067: New framework patterns and an `Entries` component for the EmDash block library.
|
|
41
|
+
|
|
42
|
+
**CSS, all formats** (css, bricks and inlined into tailwind's `components` layer; no existing class changes): `tone` (a shared tint/solid colour convention driven by `--tone-*` custom properties, plus `.accent` for a run inside a heading), `section` (head and heading-left `--split` layout), `steps` (numbered `<ol>` with a connector line), `stats` (figures and `--tiles`), `callout` (a call-to-action band), `logos` (a still partner grid) and `embed` (a consent-gated iframe frame). All are structural partials, classified `vitops.components`.
|
|
43
|
+
|
|
44
|
+
**`@getvitops/astro`:** new `Entries.astro` (`@getvitops/astro/components/Entries.astro`) emits `<wc-entries>` itself with the semantic heading + `<dl>` fallback.
|
|
45
|
+
|
|
46
|
+
**`@getvitops/generator`:** `TierEntry` gains `emdashExempt` (a reason a shipped web component has no EmDash block). `tiers.test.ts` now fails if a shipped `wc-*` has neither `emdash` nor `emdashExempt`, and the components doc and the docs site list the exemptions.
|
|
47
|
+
|
|
48
|
+
No other consumer-facing change in `@getvitops/core`, `@getvitops/utils`, `@getvitops/cli` or `@getvitops/vite` beyond the version bump.
|
|
49
|
+
|
|
50
|
+
- 161c7f3: A contact form that works end to end: `vitops setup turnstile`, `<wc-turnstile>`, the `vitops.form` EmDash block, and the route the emdash template ships to receive it.
|
|
51
|
+
|
|
52
|
+
**CSS, all formats** (no existing class changes): `forms.css` gains `.form-notice--target:not(:target)` (a message revealed only when the URL fragment names it, which is how a prerendered page shows a no-JS form's result) and `:where(wc-turnstile) { display: block }`.
|
|
53
|
+
|
|
54
|
+
**`@getvitops/core`:** new `<wc-turnstile sitekey="…">` in `elements.js`. It loads Cloudflare Turnstile on the first interaction with its form (no request to `challenges.cloudflare.com` for a visitor who only reads the page), holds an early submit until the token arrives, and leaves its slotted no-JS explanation in place until JS takes over. Turnstile is category `necessary`, so it is not behind the consent banner.
|
|
55
|
+
|
|
56
|
+
**`@getvitops/utils` / `@getvitops/cli`:** new `@getvitops/utils/turnstile` and `vitops setup turnstile [--dry] [--check] [--no-write] [--no-secret]`. It plans from the site's hostnames, creates a managed widget unless one already covers them (an existing widget is adopted, never edited, rotated or deleted), writes the public key to `site.security.turnstile.siteKey`, and pipes the one-time secret to `wrangler secret put VITOPS_TURNSTILE_SECRET` without writing it to any file. Needs `CLOUDFLARE_API_TOKEN` with **Account > Turnstile > Edit** and `CLOUDFLARE_ACCOUNT_ID`.
|
|
57
|
+
|
|
58
|
+
**`@getvitops/astro`:** `virtual:getvitops/head` gains `turnstileSiteKey` (read from `site.security.turnstile.siteKey`) so blocks can render the widget. No other change.
|
|
59
|
+
|
|
60
|
+
**`@getvitops/generator`:** `TIERS` gains a `turnstile` entry (wc-turnstile, claimed by `vitops.form`); `wc-multi-field`'s `emdashExempt` reason is reworded now the form block exists.
|
|
61
|
+
|
|
62
|
+
**`@getvitops/emdash`:** new `vitops.form` block: a field repeater (text, email, phone, long text, choose one, tick box), required flags, help text, success/error messages, optional Turnstile. It renders a semantic `<form method="post">` that works with no JavaScript, with a honeypot named per `createConversionRoute`'s defaults. A field named `phone` submits as `tel`, because the route reserves `phone` and `event`.
|
|
63
|
+
|
|
64
|
+
**`@getvitops/create`:** the emdash template ships `src/pages/api/track.ts` (`prerender = false`, `createConversionRoute()`), a seeded contact form, and `challenges.cloudflare.com` in both templates' Report-Only CSP (`script-src`, `frame-src`). The route refuses a post with nowhere to deliver and fails closed when a site key exists without `VITOPS_TURNSTILE_SECRET`. SETUP.html gains the Turnstile step. Existing sites: copy the route and the CSP change, then run `vitops setup turnstile`.
|
|
65
|
+
|
|
66
|
+
- 175d16e: **Added: `vitops.reviews` and `vitops.nav` EmDash blocks.** Non-breaking.
|
|
67
|
+
|
|
68
|
+
- **`vitops.reviews`** renders Google reviews from the committed `.vitops/reviews.json` cache (`vitops reviews`) as a grid, carousel or marquee of `<ReviewCard>`. Fields: location key, layout, columns, limit, lowest rating, order, featured ids, reviewer photos (off by default), tone/colour. Which reviews show is `selectReviews()`, the one shared decision. Review JSON-LD is not offered (Google treats self-marked-up reviews as ineligible for rich results). A new review reaches the page after `vitops reviews` is re-run, committed and the site rebuilt, not when an editor saves; with no cache file the block renders nothing (a note in `astro dev`).
|
|
69
|
+
- **`vitops.nav`** renders a named EmDash menu inside page content (site map, section links, footer-style columns), never the header. Fields: menu (picked from the site's menus via a new `menus/list` plugin route, with `footer-sitemap`/`footer-legal`/`primary` as the static fallback), accessible name, visible heading and level, layout `list | inline | columns | sitenav`, depth 1-3, tone/colour. It is a `<nav>` with a name that is never `Primary`, `Site map` or `Legal` (the layout's landmarks), `aria-current="page"`, no runtime JS; a missing or empty menu renders nothing.
|
|
70
|
+
- `@getvitops/astro`: **`virtual:getvitops/reviews`**, served by `vitops()`, is `.vitops/reviews.json` as a build-time import (an empty cache when absent or unreadable), because a Worker cannot read the file. `<ReviewCard>` now renders the review's source link (`.review__source`, `rel="nofollow noopener noreferrer"`), which the pattern already styled. **Fix:** `src/json-ld.ts` was missing from the published `files`, so any consumer importing `<ReviewCard>` (which composes `schemas/Review.astro`) failed to build with an unresolved import. `@getvitops/emdash` now needs this version of `@getvitops/astro` for the `vitops.reviews` block.
|
|
71
|
+
- `@getvitops/utils`: new Worker-safe subpath **`@getvitops/utils/reviews/select`** (`selectReviews` and the review types, no `node:fs`). `selectReviews` now takes only the display fields (`featured`, `minRating`, `limit`, `sort`), so a caller with no configured `locationId` can use it; existing calls are unchanged.
|
|
72
|
+
- `@getvitops/core`: new `.navlist` pattern in `patterns/sitenav.css` (`.navlist`, `__item`, `__link`, `__label`, `__sub`, `--inline`, `--columns`). **All formats** (css, bricks, tailwind). No existing class changes.
|
|
73
|
+
- `@getvitops/generator`: `TIERS.review` lists `vitops.reviews` and `TIERS.sitenav` lists `vitops.nav` (and `.navlist`), so `vitops docs components` and the docsite show them.
|
|
74
|
+
|
|
75
|
+
- 83d6f3c: Theming escape hatches, so a real palette builds without raw ramp steps. All additive; existing configs build unchanged.
|
|
76
|
+
|
|
77
|
+
- **Dark-only sites.** `designSystem.defaultColorScheme: "dark"` (accepted by the schema before, but ignored) now makes the dark tokens the `:root` appearance with `color-scheme: dark`, emits no light block or `data-theme` flip, and skips light-mode contrast checks. Reaches css, bricks and tailwind formats. Do not ship `<wc-color-scheme-toggle>` on such a site.
|
|
78
|
+
- **Role solid step and foreground.** `colors.roles.<role>` may be `{ hue, kind?, solid?, foreground? }`. `solid: 400` starts the solid family at step 400 (bold and x-bold walk one and two rungs darker); `foreground: "dark" | "light"` pins `text-on-<role>`. The APCA check (Lc 60) still runs on the pair emitted. The string shorthand is unchanged. All formats. The live editor's `roleTokens` still assume the default 600 family when re-pointing a role.
|
|
79
|
+
- **Split shadows.** `shadows.<name>` may be `{ box?, drop? }`: `box` (spread, inset, layers) is `--shadow-<name>`; `drop` is `--shadow-<name>-drop` and feeds `.drop-shadow-<name>` and the `shadow:` state shortcut. The string form is unchanged. css/bricks emit `.drop-shadow-*`; tailwind emits the `-drop` token only.
|
|
80
|
+
- **Layout tokens.** New optional `layout` block (`widths.{measure,breakout,spotlight,nav}`, `regionSpace.{min,preferred,max}`) emitted as `--width-*` / `--region-space*` on `:root` in css, bricks and tailwind formats, and exposed in `design-manifest.json` (`layout`, `reverseIndex`). Keys you omit keep the framework defaults.
|
|
81
|
+
- **`acknowledgeOffLadder: true`** on a palette hue silences that hue's off-ladder anchor warnings.
|
|
82
|
+
- **Typography:** `transform` and `decoration` are now accepted as aliases of `text-transform` and `text-decoration` (canonical wins when both are present) instead of warning and being dropped.
|
|
83
|
+
- **Docs strings** (`authoring.md`, JSON Schema hovers) state the Lc 60 muted-text requirement against the page plane and give a type-scale recipe for marketing headings.
|
|
84
|
+
- `@getvitops/utils`: `pickOn` takes an optional `prefer: 'light' | 'dark'`.
|
|
85
|
+
|
|
86
|
+
- 892b204: Generated legal documents gain a **Quebec (Law 25) jurisdiction**: `site.legal.jurisdiction: "qc"` renders the privacy policy, terms of use and cookie notice from new, Quebec-specific templates (not the `ca` prose). The privacy policy adds the person in charge of the protection of personal information, technologies that identify, locate or profile, transfers _outside Quebec_ (so a provider elsewhere in Canada counts), retention and anonymization, and the rights of access, rectification, portability, withdrawal, de-indexing and review of automated decisions, with the Commission d'accès à l'information as the complaint body. Every `qc` document carries a second, prominent review banner (counsel review, designated person, impact assessment, French-language requirement). Two optional config facts back it: `privacyPolicy.privacyOfficerTitle` and `privacyPolicy.impactAssessments` (the impact-assessment sentence renders only when attested). The "off until you choose" profiling assurance renders only under an opt-in consent model. **Not legal advice and not reviewed by counsel.**
|
|
87
|
+
|
|
88
|
+
CASL: when a form collects a newsletter sign-up, the `ca` **and** `qc` privacy policies now include a short "Commercial Electronic Messages" paragraph (consent, sender identification, unsubscribe). Existing `ca` documents change only for sites with such a form. No CSS change; no change to `ca` documents otherwise.
|
|
89
|
+
|
|
90
|
+
- 06d6112: Fix the EmDash `/design-system` admin page erroring on a tailwind site. It fetches `/vitops/design-manifest.json`, which the `tailwind` format never emitted and the integration only mirrored under `editor: true` (which also loads `editor.js` on every public page).
|
|
91
|
+
|
|
92
|
+
- **generator** (`tailwind` format only): `design-manifest.json` is now emitted alongside `tailwind.css`. No change to the CSS.
|
|
93
|
+
- **astro**: new `vitops({ editorManifest: true })` writes everything an outside host needs into `public/vitops/` (`design-manifest.json`, `editor.js`, a standalone `editor.css`, and `design-system-editor.html`) without loading the editor on public pages. `editor: true` still loads it from `<Head />`.
|
|
94
|
+
- **vite**: `editorManifestDir` also writes `vitops/editor.css`, from a throwaway `css`-format pass on tailwind sites.
|
|
95
|
+
- **emdash**: the `/design-system` admin page was unstyled and overflowed the admin, because the editor borrows the framework CSS and the admin never loads it. It now embeds `/vitops/design-system-editor.html` in an iframe, so the design system can't leak into the admin's styles. **Requires `editorManifest: true`** in `vitops({ … })`.
|
|
96
|
+
- **create**: the EmDash template sets `editorManifest: true`. Existing sites: add it to `vitops({ … })` in `astro.config.mjs`.
|
|
97
|
+
|
|
98
|
+
No consumer-facing change in the other toolchain packages.
|
|
99
|
+
|
|
100
|
+
- 6297337: `organization.address.streetAddress` is now optional (a home-based or service-area business with no public street address), and `organization.description` is a new optional, localisable field. The generated legal documents omit an absent street line; JSON-LD builders already skipped absent address fields. Non-breaking; no CSS change.
|
|
101
|
+
- 55061e2: **Added: `<SiteNav>` and `<SiteFooter>` for Astro/EmDash sites.** Both are tier-1 markup over the existing `sitenav` pattern (no runtime JS: `<details>` accordions + Popover API drawer). Non-breaking.
|
|
102
|
+
|
|
103
|
+
- `<SiteNav {...await loadNavMenus(getMenu)} />` renders EmDash menu `primary` (the default: depth 3, dropdowns from the `md` container width, drawer below). If a `primary-mobile` menu exists it replaces the nav below `md` (depth 1); if `primary-mega` exists it replaces it from `xl` (mega panels). Adding the menu in the admin is the whole change. Only one tier is in the accessibility tree at a time. An item's `titleAttr` is its description (EmDash has no description field yet).
|
|
104
|
+
- `<SiteFooter sitemap legal name>` renders `footer-sitemap` + `footer-legal` + a copyright line.
|
|
105
|
+
- `@getvitops/astro/components/sitenav.ts` exports the pure helpers (`loadNavMenus`, `selectTiers`, ...).
|
|
106
|
+
- CSS (`patterns/sitenav.css`, **all formats**): tier visibility classes (`.sitenav-tier--below-*`/`--from-*`), `.sitenav--mega`, `.sitenav__desc`, `.sitefooter*`, `aria-current` styling, reduced-motion and forced-colors handling. Existing `.sitenav` rules are unchanged, so the Bricks element is unaffected.
|
|
107
|
+
- `@getvitops/create` (emdash template): the layout now renders header and footer through these components, with the colour-scheme toggle in the header. It reads menus `primary`, `footer-sitemap` and `footer-legal`; a template whose seed still has the old `footer` menu renders no footer links until the seed is updated.
|
|
108
|
+
- `@getvitops/generator`: `TIERS.sitenav` now lists the two Astro components.
|
|
109
|
+
|
|
110
|
+
### Patch Changes
|
|
111
|
+
|
|
112
|
+
- Updated dependencies [161c7f3]
|
|
113
|
+
- Updated dependencies [175d16e]
|
|
114
|
+
- Updated dependencies [83d6f3c]
|
|
115
|
+
- Updated dependencies [b47df4d]
|
|
116
|
+
- @getvitops/utils@8.3.0-alpha.0
|
|
117
|
+
- @getvitops/config@8.3.0-alpha.0
|
|
118
|
+
|
|
3
119
|
## 8.2.0
|
|
4
120
|
|
|
5
121
|
### Minor Changes
|
package/assets/css/index.css
CHANGED
|
@@ -72,6 +72,14 @@
|
|
|
72
72
|
@import './patterns/text-effects.css';
|
|
73
73
|
@import './patterns/svg.css';
|
|
74
74
|
@import './patterns/marquee.css';
|
|
75
|
+
@import './patterns/tone.css';
|
|
76
|
+
@import './patterns/section.css';
|
|
77
|
+
@import './patterns/widget-area.css';
|
|
78
|
+
@import './patterns/steps.css';
|
|
79
|
+
@import './patterns/stats.css';
|
|
80
|
+
@import './patterns/callout.css';
|
|
81
|
+
@import './patterns/logos.css';
|
|
82
|
+
@import './patterns/embed.css';
|
|
75
83
|
@import './patterns/counter.css';
|
|
76
84
|
@import './patterns/nav.css';
|
|
77
85
|
@import './patterns/carousel.css';
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Callout — a call-to-action band: a short message and the action it points at, set
|
|
3
|
+
* apart from the page. Not a banner: `banner` is a dismissible announcement; this is
|
|
4
|
+
* content that stays.
|
|
5
|
+
*
|
|
6
|
+
* Message left and actions right when there is room, stacked when there is not. Colour
|
|
7
|
+
* is `.tone` (tone.css); `--center` stacks and centres at every width.
|
|
8
|
+
*/
|
|
9
|
+
.callout {
|
|
10
|
+
display: grid;
|
|
11
|
+
gap: var(--callout-gap, 1rem 2rem);
|
|
12
|
+
align-items: center;
|
|
13
|
+
padding: var(--callout-padding, clamp(1.25rem, 4cqi, 2.5rem));
|
|
14
|
+
border: var(--b-card, var(--b-card-group, var(--b-default)));
|
|
15
|
+
border-radius: var(--br-card, var(--br-card-group, var(--br-default)));
|
|
16
|
+
|
|
17
|
+
.callout__body {
|
|
18
|
+
display: grid;
|
|
19
|
+
gap: 0.5rem;
|
|
20
|
+
min-inline-size: 0;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.callout__title,
|
|
24
|
+
.callout__text,
|
|
25
|
+
.callout__actions {
|
|
26
|
+
margin: 0;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
&.callout--center {
|
|
30
|
+
text-align: center;
|
|
31
|
+
justify-items: center;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
@container (min-width: 48rem) {
|
|
36
|
+
.callout:not(.callout--center) {
|
|
37
|
+
grid-template-columns: minmax(0, 1fr) auto;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Embed — a third-party iframe (map, video) held at a fixed aspect ratio, behind consent.
|
|
3
|
+
*
|
|
4
|
+
* The iframe ships with NO `src`: the address rides on `data-consent-src`, and the consent
|
|
5
|
+
* runtime assigns it only once the visitor has granted the category. Until then the
|
|
6
|
+
* `.embed__placeholder` — a real link to the same content, so it is useful with no JS — is
|
|
7
|
+
* what the page shows; `[data-vitops-activated]` on the iframe (set by the runtime) hides
|
|
8
|
+
* it. No third-party request is made before consent.
|
|
9
|
+
*/
|
|
10
|
+
.embed {
|
|
11
|
+
position: relative;
|
|
12
|
+
aspect-ratio: var(--embed-ratio, 16 / 9);
|
|
13
|
+
overflow: hidden;
|
|
14
|
+
border: var(--b-card, var(--b-card-group, var(--b-default)));
|
|
15
|
+
border-radius: var(--br-card, var(--br-card-group, var(--br-default)));
|
|
16
|
+
|
|
17
|
+
.embed__frame {
|
|
18
|
+
position: absolute;
|
|
19
|
+
inset: 0;
|
|
20
|
+
inline-size: 100%;
|
|
21
|
+
block-size: 100%;
|
|
22
|
+
border: 0;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.embed__placeholder {
|
|
26
|
+
position: absolute;
|
|
27
|
+
inset: 0;
|
|
28
|
+
display: grid;
|
|
29
|
+
place-content: center;
|
|
30
|
+
gap: 0.5rem;
|
|
31
|
+
padding: 1rem;
|
|
32
|
+
text-align: center;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/* Once the runtime has loaded the frame, the placeholder has nothing left to say. */
|
|
36
|
+
.embed__frame[data-vitops-activated] ~ .embed__placeholder {
|
|
37
|
+
display: none;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -369,3 +369,17 @@ select.custom-select {
|
|
|
369
369
|
:where(wc-multi-field) {
|
|
370
370
|
display: block;
|
|
371
371
|
}
|
|
372
|
+
|
|
373
|
+
/* `<wc-turnstile>` wraps a fallback paragraph and the widget box, both blocks (light DOM, no `:host`). */
|
|
374
|
+
:where(wc-turnstile) {
|
|
375
|
+
display: block;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
/*
|
|
379
|
+
* A message shown only when the URL fragment names it. A prerendered page cannot know whether a
|
|
380
|
+
* no-JS post succeeded, so a form's success and error notices are always in the markup and the
|
|
381
|
+
* endpoint's redirect (`#form-sent` / `#form-error`) reveals one.
|
|
382
|
+
*/
|
|
383
|
+
.form-notice--target:not(:target) {
|
|
384
|
+
display: none;
|
|
385
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Logos — a static grid of partner/client marks. `marquee` is the scrolling version;
|
|
3
|
+
* this one holds still, which is what most "key partners" rows should do (nothing to
|
|
4
|
+
* pause, nothing to track, no motion to opt out of).
|
|
5
|
+
*
|
|
6
|
+
* Each item is a plain image, or a link around one. Marks are normalised to a common
|
|
7
|
+
* height rather than a common width, because logos vary far more in aspect ratio than
|
|
8
|
+
* in how tall they should read. `--grayscale` quiets them until hover or focus.
|
|
9
|
+
*/
|
|
10
|
+
.logos {
|
|
11
|
+
display: flex;
|
|
12
|
+
flex-wrap: wrap;
|
|
13
|
+
align-items: center;
|
|
14
|
+
justify-content: var(--logos-justify, center);
|
|
15
|
+
gap: var(--logos-gap, 1.5rem 2.5rem);
|
|
16
|
+
margin: 0;
|
|
17
|
+
padding: 0;
|
|
18
|
+
list-style: none;
|
|
19
|
+
|
|
20
|
+
.logos__item {
|
|
21
|
+
display: grid;
|
|
22
|
+
place-items: center;
|
|
23
|
+
margin: 0;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.logos__item img {
|
|
27
|
+
block-size: var(--logos-height, 2.5rem);
|
|
28
|
+
inline-size: auto;
|
|
29
|
+
max-inline-size: 100%;
|
|
30
|
+
object-fit: contain;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
&.logos--grayscale .logos__item img {
|
|
34
|
+
filter: grayscale(1);
|
|
35
|
+
opacity: 0.7;
|
|
36
|
+
transition:
|
|
37
|
+
filter 0.2s,
|
|
38
|
+
opacity 0.2s;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
&.logos--grayscale .logos__item:is(:hover, :focus-within) img {
|
|
42
|
+
filter: none;
|
|
43
|
+
opacity: 1;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Section — the editorial wrapper most pages are made of: an optional kicker, a heading,
|
|
3
|
+
* a lead-in line, then a body of other blocks.
|
|
4
|
+
*
|
|
5
|
+
* Composed from the layout primitives rather than reinventing them: the outer element
|
|
6
|
+
* is `.region` (vertical rhythm) and the content sits on `.centered`'s tracks, so this
|
|
7
|
+
* partial only adds what neither has — the head's own rhythm and the `--split` layout,
|
|
8
|
+
* which puts the head beside the body (heading-left) above the container breakpoint.
|
|
9
|
+
* Backdrop and colour come from `.tone` (tone.css), never from here.
|
|
10
|
+
*/
|
|
11
|
+
.section {
|
|
12
|
+
.section__head {
|
|
13
|
+
display: grid;
|
|
14
|
+
gap: var(--section-head-gap, 0.75rem);
|
|
15
|
+
align-content: start;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.section__kicker,
|
|
19
|
+
.section__title {
|
|
20
|
+
margin: 0;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.section__sub {
|
|
24
|
+
margin: 0;
|
|
25
|
+
max-inline-size: var(--width-measure, 65ch);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.section__body {
|
|
29
|
+
display: grid;
|
|
30
|
+
gap: var(--section-body-gap, 1.5rem);
|
|
31
|
+
min-inline-size: 0;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/* Head and body stack until there is room for two columns (below). */
|
|
35
|
+
&.section--split .section__inner {
|
|
36
|
+
display: grid;
|
|
37
|
+
gap: var(--section-split-gap, clamp(1.5rem, 4cqi, 3rem));
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
&.section--center .section__head {
|
|
41
|
+
text-align: center;
|
|
42
|
+
justify-items: center;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
@container (min-width: 48rem) {
|
|
47
|
+
.section.section--split .section__inner {
|
|
48
|
+
grid-template-columns:
|
|
49
|
+
minmax(0, var(--section-split-head, 1fr))
|
|
50
|
+
minmax(0, var(--section-split-body, 2fr));
|
|
51
|
+
align-items: start;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/*
|
|
56
|
+
* A block given an anchor id (`@getvitops/emdash`'s `anchor` field). The offset keeps the target
|
|
57
|
+
* clear of a sticky header; set `--header-offset` to the header's height. All formats.
|
|
58
|
+
*/
|
|
59
|
+
.block-anchor {
|
|
60
|
+
scroll-margin-top: var(--header-offset, 5rem);
|
|
61
|
+
}
|
|
@@ -425,3 +425,265 @@
|
|
|
425
425
|
display: none;
|
|
426
426
|
}
|
|
427
427
|
}
|
|
428
|
+
|
|
429
|
+
/* ── Tiers (`<SiteNav>`'s optional `primary-mobile` / `primary-mega` menus) ──────
|
|
430
|
+
Each tier is its own `<nav>` in a `.sitenav-tier` wrapper, and a wrapper is
|
|
431
|
+
`display: none` outside its container-width window — so exactly one tier is ever
|
|
432
|
+
in the accessibility tree. Same container as the `--bp-*` flag above (the nearest
|
|
433
|
+
ancestor `container-type`, `body` per layout.css), so the hand-over width and the
|
|
434
|
+
drawer→navbar flip can never disagree. One pair per breakpoint; `sitenav.ts`'s
|
|
435
|
+
`VISIBILITY_CLASSES` lists them and `sitenav.test.ts` checks each is defined here. */
|
|
436
|
+
@container (width < 30rem) {
|
|
437
|
+
.sitenav-tier--from-sm {
|
|
438
|
+
display: none;
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
@container (width >= 30rem) {
|
|
442
|
+
.sitenav-tier--below-sm {
|
|
443
|
+
display: none;
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
@container (width < 48rem) {
|
|
447
|
+
.sitenav-tier--from-md {
|
|
448
|
+
display: none;
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
@container (width >= 48rem) {
|
|
452
|
+
.sitenav-tier--below-md {
|
|
453
|
+
display: none;
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
@container (width < 64rem) {
|
|
457
|
+
.sitenav-tier--from-lg {
|
|
458
|
+
display: none;
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
@container (width >= 64rem) {
|
|
462
|
+
.sitenav-tier--below-lg {
|
|
463
|
+
display: none;
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
@container (width < 80rem) {
|
|
467
|
+
.sitenav-tier--from-xl {
|
|
468
|
+
display: none;
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
@container (width >= 80rem) {
|
|
472
|
+
.sitenav-tier--below-xl {
|
|
473
|
+
display: none;
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
/* ── Item description (EmDash `titleAttr` convention — see SiteNav.astro) ───── */
|
|
478
|
+
.sitenav__link--described {
|
|
479
|
+
flex-direction: column;
|
|
480
|
+
align-items: flex-start;
|
|
481
|
+
gap: 0.125rem;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
.sitenav__desc {
|
|
485
|
+
font-size: 0.875em;
|
|
486
|
+
opacity: 0.75;
|
|
487
|
+
font-weight: normal;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
/* The current page. Text-weight + a non-colour cue, so it survives forced-colors. */
|
|
491
|
+
.sitenav__link[aria-current='page'] {
|
|
492
|
+
font-weight: 600;
|
|
493
|
+
text-decoration: underline;
|
|
494
|
+
text-underline-offset: 0.25em;
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
/* ── Mega variant — WIDE ONLY ───────────────────────────────────────────────
|
|
498
|
+
Top-level submenus become full-width panels of columns under the nav row. The
|
|
499
|
+
branch item stops being the positioning context so the panel anchors to
|
|
500
|
+
`.sitenav--mega` instead. Inside the wide branch, with the same reasoning as the
|
|
501
|
+
rest of the file: a rule here competes at its bare selector weight. */
|
|
502
|
+
@container style(--_sitenav-wide: 1) {
|
|
503
|
+
.sitenav--mega {
|
|
504
|
+
position: relative;
|
|
505
|
+
}
|
|
506
|
+
.sitenav--mega .sitenav__list > .sitenav__item--branch {
|
|
507
|
+
position: static;
|
|
508
|
+
}
|
|
509
|
+
.sitenav--mega .sitenav__list > .sitenav__item--branch > .sitenav__submenu {
|
|
510
|
+
inset-inline: 0;
|
|
511
|
+
inline-size: 100%;
|
|
512
|
+
flex-direction: row;
|
|
513
|
+
flex-wrap: wrap;
|
|
514
|
+
align-items: flex-start;
|
|
515
|
+
gap: 1.5rem;
|
|
516
|
+
padding: 1.5rem;
|
|
517
|
+
}
|
|
518
|
+
/* Third level is a column inside the panel, not a fly-out. */
|
|
519
|
+
.sitenav--mega .sitenav__submenu .sitenav__item--branch {
|
|
520
|
+
position: static;
|
|
521
|
+
display: block;
|
|
522
|
+
}
|
|
523
|
+
.sitenav--mega .sitenav__submenu .sitenav__item--branch > .sitenav__submenu {
|
|
524
|
+
position: static;
|
|
525
|
+
opacity: 1;
|
|
526
|
+
visibility: visible;
|
|
527
|
+
pointer-events: auto;
|
|
528
|
+
translate: none;
|
|
529
|
+
box-shadow: none;
|
|
530
|
+
border: none;
|
|
531
|
+
padding: 0.25rem 0 0;
|
|
532
|
+
min-inline-size: 0;
|
|
533
|
+
}
|
|
534
|
+
.sitenav--mega .sitenav__submenu .sitenav__disclosure {
|
|
535
|
+
display: none;
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
/* ── In-page menu (`vitops.nav` block) ─────────────────────────────────────────
|
|
540
|
+
*
|
|
541
|
+
* A menu read in the body of a page — a site map, a section's links, footer-style columns —
|
|
542
|
+
* as plain nested lists. No disclosure and no drawer: that is the header's job (`.sitenav`).
|
|
543
|
+
*
|
|
544
|
+
* <nav aria-label="Services"><ul class="navlist navlist--columns">
|
|
545
|
+
* <li class="navlist__item"><a class="navlist__link">…</a><ul class="navlist__sub">…</ul></li>
|
|
546
|
+
*
|
|
547
|
+
* Modifiers: `--inline` (one wrapping row) and `--columns` (an auto-fit grid, each top-level
|
|
548
|
+
* item a column with its sub-links beneath it). */
|
|
549
|
+
.navlist,
|
|
550
|
+
.navlist__sub {
|
|
551
|
+
list-style: none;
|
|
552
|
+
margin: 0;
|
|
553
|
+
padding: 0;
|
|
554
|
+
display: flex;
|
|
555
|
+
flex-direction: column;
|
|
556
|
+
gap: 0.25rem;
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
/* The rhythm utility's `li + li` margin is the flow's job; the list's own gap does it here. */
|
|
560
|
+
.navlist__item {
|
|
561
|
+
margin: 0;
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
.navlist__sub {
|
|
565
|
+
margin-block-start: 0.25rem;
|
|
566
|
+
padding-inline-start: 1rem;
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
.navlist__link {
|
|
570
|
+
color: inherit;
|
|
571
|
+
text-decoration: underline;
|
|
572
|
+
text-underline-offset: 0.2em;
|
|
573
|
+
padding-block: 0.25rem;
|
|
574
|
+
border-radius: var(--br-control, 0.25rem);
|
|
575
|
+
|
|
576
|
+
&:focus-visible {
|
|
577
|
+
outline: 2px solid var(--sitenav-focus-color, var(--color-border-focus));
|
|
578
|
+
outline-offset: 2px;
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
&[aria-current='page'] {
|
|
582
|
+
font-weight: 600;
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
.navlist__label {
|
|
587
|
+
font-weight: 600;
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
.navlist--inline {
|
|
591
|
+
flex-direction: row;
|
|
592
|
+
flex-wrap: wrap;
|
|
593
|
+
gap: 0.25rem 1.25rem;
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
.navlist--columns {
|
|
597
|
+
display: grid;
|
|
598
|
+
grid-template-columns: repeat(auto-fit, minmax(min(12rem, 100%), 1fr));
|
|
599
|
+
gap: 1.5rem;
|
|
600
|
+
|
|
601
|
+
> .navlist__item > :is(.navlist__link, .navlist__label) {
|
|
602
|
+
font-weight: 600;
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
> .navlist__item > .navlist__sub {
|
|
606
|
+
padding-inline-start: 0;
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
@media (forced-colors: active) {
|
|
611
|
+
.navlist__link:focus-visible {
|
|
612
|
+
outline-color: Highlight;
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
/* ── Footer (`<SiteFooter>`) ───────────────────────────────────────────────── */
|
|
617
|
+
.sitefooter {
|
|
618
|
+
margin-block-start: var(--space-2xl, 3rem);
|
|
619
|
+
display: flex;
|
|
620
|
+
flex-direction: column;
|
|
621
|
+
gap: 1rem;
|
|
622
|
+
padding-block: var(--space-l, 2rem);
|
|
623
|
+
border-block-start: 1px solid var(--color-border-neutral, currentColor);
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
.sitefooter__nav {
|
|
627
|
+
display: flex;
|
|
628
|
+
flex-wrap: wrap;
|
|
629
|
+
gap: 1rem 3rem;
|
|
630
|
+
justify-content: space-between;
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
.sitefooter__list {
|
|
634
|
+
list-style: none;
|
|
635
|
+
margin: 0;
|
|
636
|
+
padding: 0;
|
|
637
|
+
display: flex;
|
|
638
|
+
flex-wrap: wrap;
|
|
639
|
+
gap: 0.25rem 1.25rem;
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
.sitefooter__link {
|
|
643
|
+
color: inherit;
|
|
644
|
+
text-decoration: underline;
|
|
645
|
+
text-underline-offset: 0.2em;
|
|
646
|
+
padding-block: 0.25rem;
|
|
647
|
+
border-radius: var(--br-control, 0.25rem);
|
|
648
|
+
|
|
649
|
+
&:focus-visible {
|
|
650
|
+
outline: 2px solid var(--sitenav-focus-color, var(--color-border-focus));
|
|
651
|
+
outline-offset: 2px;
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
&[aria-current='page'] {
|
|
655
|
+
font-weight: 600;
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
.sitefooter__copy {
|
|
660
|
+
margin: 0;
|
|
661
|
+
font-size: 0.875em;
|
|
662
|
+
opacity: 0.8;
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
/* ── Motion + forced colours (both site-nav and footer) ────────────────────── */
|
|
666
|
+
@media (prefers-reduced-motion: reduce) {
|
|
667
|
+
.sitenav__panel,
|
|
668
|
+
.sitenav__panel::backdrop,
|
|
669
|
+
.sitenav__submenu,
|
|
670
|
+
.sitenav__item--branch {
|
|
671
|
+
transition: none;
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
@media (forced-colors: active) {
|
|
676
|
+
.sitenav__link:focus-visible,
|
|
677
|
+
.sitenav__toggle:focus-visible,
|
|
678
|
+
.sitenav__close:focus-visible,
|
|
679
|
+
.sitefooter__link:focus-visible {
|
|
680
|
+
outline-color: Highlight;
|
|
681
|
+
}
|
|
682
|
+
.sitenav__panel,
|
|
683
|
+
.sitenav__submenu {
|
|
684
|
+
border: 1px solid CanvasText;
|
|
685
|
+
}
|
|
686
|
+
.sitenav__link[aria-current='page'] {
|
|
687
|
+
text-decoration-thickness: 2px;
|
|
688
|
+
}
|
|
689
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stats — a row of headline figures ("11 hrs/week", "82%") and, as `.stats--tiles`, a
|
|
3
|
+
* grid of labelled tiles with a group, a delta and a state (the generic kernel of a
|
|
4
|
+
* dashboard mock: "Open jobs 24 +3 this week").
|
|
5
|
+
*
|
|
6
|
+
* The figure is the `.counter` pattern, so the count-up is `<wc-counter>`'s; this
|
|
7
|
+
* partial only lays the figures out. With no JS and no CSS each figure is plain text
|
|
8
|
+
* beside its label.
|
|
9
|
+
*
|
|
10
|
+
* `data-state` on a tile (`up` / `down`) drives the delta's colour through the semantic
|
|
11
|
+
* roles, so "good" and "bad" follow the site's own success/danger rather than a hard-coded
|
|
12
|
+
* green and red. The delta's words stay in the text: colour is never the only carrier.
|
|
13
|
+
*/
|
|
14
|
+
.stats {
|
|
15
|
+
display: grid;
|
|
16
|
+
gap: var(--stats-gap, 1.5rem);
|
|
17
|
+
grid-template-columns: repeat(auto-fit, minmax(min(100%, var(--stats-min, 12rem)), 1fr));
|
|
18
|
+
margin: 0;
|
|
19
|
+
padding: 0;
|
|
20
|
+
list-style: none;
|
|
21
|
+
|
|
22
|
+
.stats__item {
|
|
23
|
+
display: grid;
|
|
24
|
+
gap: 0.25rem;
|
|
25
|
+
align-content: start;
|
|
26
|
+
margin: 0;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.stats__figure,
|
|
30
|
+
.stats__label,
|
|
31
|
+
.stats__group,
|
|
32
|
+
.stats__delta {
|
|
33
|
+
margin: 0;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.stats__figure {
|
|
37
|
+
color: var(--tone-fg, inherit);
|
|
38
|
+
font-variant-numeric: tabular-nums;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.stats__group {
|
|
42
|
+
text-transform: uppercase;
|
|
43
|
+
letter-spacing: 0.05em;
|
|
44
|
+
opacity: 0.8;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.stats__item[data-state='up'] .stats__delta {
|
|
48
|
+
color: var(--color-text-success, inherit);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.stats__item[data-state='down'] .stats__delta {
|
|
52
|
+
color: var(--color-text-danger, inherit);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
&.stats--tiles .stats__item {
|
|
56
|
+
padding: var(--stats-tile-padding, 1rem 1.25rem);
|
|
57
|
+
border: var(--b-card, var(--b-card-group, var(--b-default)));
|
|
58
|
+
border-radius: var(--br-card, var(--br-card-group, var(--br-default)));
|
|
59
|
+
background-color: var(--tone-bg, transparent);
|
|
60
|
+
}
|
|
61
|
+
}
|