srcdev-nuxt-components 9.5.3 → 9.5.5
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/.claude/skills/colour-scheme-disable.md +1 -1
- package/.claude/skills/component-compliance-checklist.md +214 -0
- package/.claude/skills/component-inline-action-button.md +2 -2
- package/.claude/skills/components/carousel-flip.md +3 -3
- package/.claude/skills/components/clipped-panel.md +2 -2
- package/.claude/skills/components/dashboard-quad-grid.md +2 -2
- package/.claude/skills/components/dashboard-stats-grid.md +1 -1
- package/.claude/skills/components/deep-expanding-menu-classic.md +1 -1
- package/.claude/skills/components/deep-expanding-menu.md +1 -1
- package/.claude/skills/components/google-reviews.md +193 -0
- package/.claude/skills/components/input-number.md +4 -0
- package/.claude/skills/components/input-otp.md +120 -0
- package/.claude/skills/components/section-parallax.md +1 -1
- package/.claude/skills/components/select-menu.md +19 -5
- package/.claude/skills/components/triple-toggle-switch.md +7 -1
- package/.claude/skills/composable-colour-scheme.md +2 -0
- package/.claude/skills/index.md +5 -1
- package/.claude/skills/new-app-scaffold.md +1 -1
- package/.claude/skills/theming-colour-ramps.md +6 -33
- package/.claude/skills/theming-component-token-pattern.md +30 -0
- package/.claude/skills/theming-dark-mode.md +243 -0
- package/.claude/skills/theming-override-default.md +6 -7
- package/.claude/skills/theming-partial-override.md +11 -11
- package/.vscode/srcdev-component-google-reviews.code-snippets +78 -0
- package/.vscode/srcdev-component-input-otp.code-snippets +47 -0
- package/.vscode/srcdev-component-select-menu.code-snippets +13 -0
- package/README.md +81 -41
- package/app/assets/styles/setup/01.config/_head.css +0 -9
- package/app/assets/styles/setup/03.theming/_default.css +29 -29
- package/app/assets/styles/setup/03.theming/_error.css +3 -3
- package/app/assets/styles/setup/03.theming/_theme-slots.css +13 -16
- package/app/assets/styles/setup/a11y/_variables.css +2 -2
- package/app/components/02.molecules/action-menu/stories/ActionMenu.stories.ts +7 -7
- package/app/components/02.molecules/select-menu/CONSUMER-STYLING.md +19 -1
- package/app/components/02.molecules/select-menu/SelectMenu.vue +54 -3
- package/app/components/02.molecules/select-menu/stories/SelectMenu.stories.ts +35 -0
- package/app/components/02.molecules/select-menu/tests/SelectMenu.spec.ts +9 -0
- package/app/components/02.molecules/select-menu/tests/__snapshots__/SelectMenu.spec.ts.snap +12 -12
- package/app/components/03.organisms/google-reviews/CONSUMER-STYLING.md +110 -0
- package/app/components/03.organisms/google-reviews/GoogleReviewCard.vue +161 -0
- package/app/components/03.organisms/google-reviews/GoogleReviews.vue +275 -0
- package/app/components/03.organisms/google-reviews/stories/GoogleReviews.stories.ts +323 -0
- package/app/components/03.organisms/google-reviews/tests/GoogleReviewCard.spec.ts +104 -0
- package/app/components/03.organisms/google-reviews/tests/GoogleReviews.spec.ts +232 -0
- package/app/components/03.organisms/google-reviews/tests/__snapshots__/GoogleReviewCard.spec.ts.snap +9 -0
- package/app/components/03.organisms/google-reviews/tests/__snapshots__/GoogleReviews.spec.ts.snap +43 -0
- package/app/components/05.forms/input-number/CONSUMER-STYLING.md +1 -1
- package/app/components/05.forms/input-number/InputNumber.vue +2 -2
- package/app/components/05.forms/input-otp/CONSUMER-STYLING.md +59 -0
- package/app/components/05.forms/input-otp/InputOtp.vue +254 -0
- package/app/components/05.forms/input-otp/InputOtpField.vue +106 -0
- package/app/components/05.forms/input-otp/stories/InputOtp.stories.ts +100 -0
- package/app/components/05.forms/input-otp/stories/InputOtpField.stories.ts +163 -0
- package/app/components/05.forms/input-otp/tests/InputOtp.spec.ts +275 -0
- package/app/components/05.forms/input-otp/tests/InputOtpField.spec.ts +76 -0
- package/app/components/05.forms/patterns/stories/MigratedFieldsForm.stories.ts +151 -33
- package/app/components/05.forms/triple-toggle-switch/TripleToggleSwitch.vue +11 -3
- package/app/composables/useGoogleReviews.ts +4 -0
- package/app/types/components/google-reviews.d.ts +18 -0
- package/app/types/components/index.ts +1 -0
- package/nuxt.config.ts +9 -9
- package/package.json +9 -8
|
@@ -57,7 +57,7 @@ The component sets:
|
|
|
57
57
|
- `background-position: var(--section-parallax-background-position, center)`
|
|
58
58
|
- `background-size: var(--section-parallax-background-size, cover)`
|
|
59
59
|
- `min-height: var(--section-parallax-min-height, 100svh)`
|
|
60
|
-
- `background-color: var(--section-parallax-background-colour,
|
|
60
|
+
- `background-color: var(--section-parallax-background-colour, var(--slate-01))` (visible if image fails to load)
|
|
61
61
|
|
|
62
62
|
Inside `@media (hover: hover) and (pointer: fine) { @supports (background-attachment: fixed) { ... } }`:
|
|
63
63
|
|
|
@@ -17,7 +17,7 @@ open between picks.
|
|
|
17
17
|
|
|
18
18
|
## Components
|
|
19
19
|
|
|
20
|
-
### SelectMenu
|
|
20
|
+
### SelectMenu props
|
|
21
21
|
|
|
22
22
|
| Prop | Type | Default | Notes |
|
|
23
23
|
|---|---|---|---|
|
|
@@ -29,9 +29,10 @@ open between picks.
|
|
|
29
29
|
| `showChevron` | `boolean` | `true` | Show the trailing chevron in the trigger. |
|
|
30
30
|
| `multiple` | `boolean` | `false` | Allow selecting more than one option. Each option gets a checkbox indicator, `v-model` becomes an array, and picking an option leaves the popover open so more can be toggled. Trigger icon is not shown when `true` (no single option to represent). |
|
|
31
31
|
| `showSelectionInTrigger` | `boolean` | `false` | Multiple mode only. When `true`, the trigger text updates to a comma-separated list of the currently checked options instead of staying fixed on `placeholder`/`label`. No effect outside `multiple`. |
|
|
32
|
+
| `inputVariant` | `InputUiVariant` | `"normal"` | Trigger border style, same prop as `InputSelect`: `"normal"` (bordered box) or `"underlined"` (bottom border only, square corners), added as a class on the root. `"outlined"` has no CSS, same as the `05.forms` inputs. |
|
|
32
33
|
| `styleClassPassthrough` | `string \| string[]` | `[]` | Extra classes on the root `<div>`. |
|
|
33
34
|
|
|
34
|
-
|
|
35
|
+
#### v-model
|
|
35
36
|
|
|
36
37
|
`defineModel<string | number | (string | number)[] | undefined>({ default: undefined })` — the
|
|
37
38
|
selected value(s). In single-select mode (default) it's the one selected option's `value`, or
|
|
@@ -44,7 +45,7 @@ is optional by design (see that rule's `defineModel` gap noted in the repo's led
|
|
|
44
45
|
docs). For several independent single-select categories, place multiple `SelectMenu` instances
|
|
45
46
|
side by side rather than using `multiple` on one instance — see the "Filter bar" section below.
|
|
46
47
|
|
|
47
|
-
|
|
48
|
+
#### Type import
|
|
48
49
|
|
|
49
50
|
```ts
|
|
50
51
|
import type { SelectMenuOption } from "~/types/components/select-menu";
|
|
@@ -170,8 +171,11 @@ Quick reference:
|
|
|
170
171
|
|
|
171
172
|
## Notes
|
|
172
173
|
|
|
173
|
-
- **Popover API + CSS anchor positioning** — same mechanism as `ActionMenu`.
|
|
174
|
-
(Chrome 114+, Firefox 125+, Safari 17+)
|
|
174
|
+
- **Popover API + CSS anchor positioning** — same mechanism as `ActionMenu`. The Popover API is
|
|
175
|
+
broadly supported (Chrome 114+, Firefox 125+, Safari 17+), but CSS anchor positioning is much
|
|
176
|
+
newer (Chromium 125+, Safari 26+), and without it the popover isn't placed under the trigger. No
|
|
177
|
+
polyfill or fallback is included, so check support before using this where older devices
|
|
178
|
+
matter; a native `<select>` (`InputSelect`) is the safer choice there.
|
|
175
179
|
- **Left-aligned popover** (`left: anchor(left)`) — unlike `ActionMenu` which right-aligns. Matches
|
|
176
180
|
native `<select>` dropdown behaviour. Flips above the trigger near the bottom of the viewport.
|
|
177
181
|
- **Keyboard navigation** follows the WAI-ARIA listbox pattern: `ArrowDown`/`ArrowUp` move between
|
|
@@ -184,6 +188,16 @@ Quick reference:
|
|
|
184
188
|
`[role="option"]` if one exists (in `multiple` mode, the first checked item), otherwise the first
|
|
185
189
|
option (unlike `ActionMenu`, which always focuses the first item since it has no selection state).
|
|
186
190
|
- **`aria-multiselectable="true"`** is set on the listbox only when `multiple` is `true`.
|
|
191
|
+
- **Selected rows are tinted** via `[aria-selected="true"]` (`--select-menu-item-surface-selected`,
|
|
192
|
+
default `--theme-surface-subtle`), in both single and multiple mode. Hover/focus is declared after
|
|
193
|
+
it at equal specificity, so the hover surface still shows on a selected row. Selected text colour,
|
|
194
|
+
font weight and border colour have their own `-selected` tokens falling back to the resting ones.
|
|
195
|
+
The row border width (`--select-menu-item-border-width`, default `0`) applies to every row with a
|
|
196
|
+
transparent resting colour, so a selected border never shifts layout. A hidden `::after` carrying
|
|
197
|
+
`data-label` in the selected weight reserves the label column's width, so a bolder selected
|
|
198
|
+
weight doesn't resize the popover. Hover/focus has `-text-color-hover` and `-border-hover`, which
|
|
199
|
+
fall back to the row's *current* state (selected or resting), not always to resting. There's no
|
|
200
|
+
hover font-weight token on purpose: the width reserve only covers one alternate weight.
|
|
187
201
|
- **Chevron rotation is pure CSS** via `:has(.select-menu-popover:popover-open)` on the root — no
|
|
188
202
|
JS state drives the visual. `isOpen` is still tracked internally, but only to set `aria-expanded`
|
|
189
203
|
on the trigger.
|
|
@@ -28,7 +28,7 @@ for that for a theme switch; use `TripleToggleSwitch` directly for any other thr
|
|
|
28
28
|
| `name` | `string` | `"triple-toggle-switch"` | Shared radio `name`. Give each instance on a page its own. |
|
|
29
29
|
| `aria-label` | `string` | `""` | Accessible name for the `role="radiogroup"`. Omitted from the DOM when empty. Always pass one. |
|
|
30
30
|
| `theme` | `FormUiTheme` | `"default"` | Rendered as `data-theme`. |
|
|
31
|
-
| `step-animation-duration` | `string` | `"250ms"` | Marker slide duration. Ignored under `prefers-reduced-motion`. |
|
|
31
|
+
| `step-animation-duration` | `string` | `"250ms"` | Marker slide duration; also times the icon colour swap (see Behaviour). Ignored under `prefers-reduced-motion`. |
|
|
32
32
|
| `style-class-passthrough` | `string \| string[]` | `[]` | Classes on the root. |
|
|
33
33
|
|
|
34
34
|
Option `id`s are used as element ids (and as a class on each icon), so they must be unique on the
|
|
@@ -66,6 +66,12 @@ const options = ref<IFormMultipleOptions>({
|
|
|
66
66
|
- The marker is measured from the first option's width on mount and fades in after ~250ms. It
|
|
67
67
|
isn't re-measured on resize, so changing the icon-size/padding tokens after mount (e.g. toggling
|
|
68
68
|
a class) leaves the marker at its old size.
|
|
69
|
+
- Icon colour swaps are timed to the marker: the newly active icon waits half of
|
|
70
|
+
`stepAnimationDuration`, then fades to the active colour over the other half, so it lands as the
|
|
71
|
+
marker arrives rather than turning white on the bare track. The previous icon fades back straight
|
|
72
|
+
away. The marker takes the same time whatever the distance, so no per-index delay is needed.
|
|
73
|
+
- Option circles are sized by their content (1em icon + equal padding) with `place-items: center`,
|
|
74
|
+
not `aspect-ratio`; older iPad Safari mis-centred the icons under the `aspect-ratio` version.
|
|
69
75
|
- Marker gradients only exist for the values `system`, `light` and `dark`; other values get the
|
|
70
76
|
plain `--triple-toggle-switch-marker-surface` background.
|
|
71
77
|
|
|
@@ -59,6 +59,8 @@ The three valid values are:
|
|
|
59
59
|
| `"light"` | Forces light theme regardless of OS setting |
|
|
60
60
|
| `"dark"` | Forces dark theme regardless of OS setting |
|
|
61
61
|
|
|
62
|
+
The attribute only switches schemes; the layer ships light colour values only. The app supplies the dark values, see `theming-dark-mode.md`.
|
|
63
|
+
|
|
62
64
|
## Notes
|
|
63
65
|
|
|
64
66
|
- `onMounted` is used to read `localStorage` — `currentColourScheme` is always `"auto"` during SSR and hydrates client-side. Do not read it server-side.
|
package/.claude/skills/index.md
CHANGED
|
@@ -32,10 +32,12 @@ Each skill is a single markdown file named `<area>-<task>.md`.
|
|
|
32
32
|
├── theming-override-default.md — replace the entire default theme with a custom palette (set --theme-hue/--theme-chroma)
|
|
33
33
|
├── theming-partial-override.md — override a specific colour token category (palette, buttons, inputs) without a full theme replacement
|
|
34
34
|
├── theming-form-geometry-tokens.md — non-colour form/button/input tokens (padding, border-radius, gaps): full inventory, why partial override doesn't need duplicating the source files
|
|
35
|
-
├── theming-component-token-pattern.md — standard shape for overridable component colours: public token + inline fallback, no bare var()/no private-only indirection; the three tiers (theme slots / cross-component families / component-scoped); rollout status
|
|
35
|
+
├── theming-component-token-pattern.md — standard shape for overridable component colours: public token + inline fallback, no bare var()/no private-only indirection; the three tiers (theme slots / cross-component families / component-scoped); line-clamp tokens for overflowing text; rollout status
|
|
36
|
+
├── theming-dark-mode.md — add a dark scheme in a consumer app (layer is light-only): enable plumbing, full dark token set without light-dark(), light-dark() alternative, light-only pinning
|
|
36
37
|
├── colour-scheme-disable.md — disable light/dark scheme support in a consumer app
|
|
37
38
|
├── component-dynamic-slots.md — named dynamic slots ($slots iteration) vs indexed dynamic slots (itemCount pattern)
|
|
38
39
|
├── component-naming.md — Control/Field naming convention (InputNumber + InputNumberField, no Core/Default/WithLabel suffixes), rename procedure, backlog with consumer usage
|
|
40
|
+
├── component-compliance-checklist.md — definition of done for every component (tier, props, tokens, CONSUMER-STYLING.md layout, tests, story, skill doc, snippet, a11y, i18n, naming, pitfalls); shared by /new-component and /migrate-component
|
|
39
41
|
├── component-local-style-override.md — overriding a component from a consumer app: public tokens on an ancestor, unscoped page blocks, per-instance class/passthrough, consumer `<style scoped>` + :deep() rules, never @layer overrides
|
|
40
42
|
├── component-prop-driven-container-layout.md — vary CSS grid layout inside @container queries using data-* attribute selectors
|
|
41
43
|
├── page-transitions.md — pageTransition/layoutTransition setup; the self-wrapped <NuxtLayout> + layout:false anti-pattern that pulls the header/nav into the transition boundary; fade CSS
|
|
@@ -80,6 +82,7 @@ Each skill is a single markdown file named `<area>-<task>.md`.
|
|
|
80
82
|
├── page-hero-highlights.md — PageHeroHighlights template: hero + highlights strip grid, CSS custom property theming
|
|
81
83
|
├── slider-gallery.md — SliderGallery: full-screen image carousel, IGalleryData, pause-on-hover/reduced-motion auto-advance, prefixed classes, CSS tokens
|
|
82
84
|
├── services-card.md — ServicesCard props (incl. eyebrowConfig/heroConfig), actions slot, CSS tokens, page boilerplate
|
|
85
|
+
├── google-reviews.md — GoogleReviews + GoogleReviewCard: Google Places reviews in a scrolling row; env vars, server route, useGoogleReviews, props, slots, tokens, Google terms
|
|
83
86
|
├── services-card-grid.md — ServicesCardGrid props, config pass-through, CSS tokens, full page boilerplate
|
|
84
87
|
├── service-summary-grid.md — ServiceSummaryGrid props, useAlternateReverse zigzag layout, page boilerplate
|
|
85
88
|
├── service-summary.md — ServiceSummary props, summary-link slot, DisplayPill duration/price (renamed/stripped from ServicesSection — full mode is now ServiceDetail)
|
|
@@ -137,6 +140,7 @@ Each skill is a single markdown file named `<area>-<task>.md`.
|
|
|
137
140
|
├── input-button.md — InputButton (renamed from InputButtonCore 2026-09-28): button/NuxtLink/a, variants, icon slots, readonly (aria-disabled, activation blocked), tokens
|
|
138
141
|
├── input-range-core.md — InputRangeCore: native range-slider primitive, markers/datalist slots, dead --theme-form-range-accent-color token fixed, CSS token API; Variants section covers InputRangeDefault
|
|
139
142
|
├── input-number.md — InputNumber: native number-input primitive, left/right step-button slots, missing base class/placeholder/mismatched slot-selector bugs fixed, CSS token API; Variants section covers InputNumberField
|
|
143
|
+
├── input-otp.md — InputOtp: one-time-code digit boxes (paste/autofill spreading, Backspace/arrow/Home/End nav, Enter submits, leading zeros kept, complete event, hidden input); Variants section covers InputOtpField
|
|
140
144
|
├── form-field.md — FormField: layout wrapper for one form control (width cap, gutter, field spacing), data-width/data-has-gutter/data-invalid hooks, dead .underline rule removed, CSS token API
|
|
141
145
|
├── form-fieldset.md — FormFieldset: shared fieldset+legend for checkbox/radio groups, groupRole prop (hardcoded radiogroup bug fixed), aria-required only on radiogroup, dead description slot removed, legend token API
|
|
142
146
|
├── input-error.md — InputError: red error strip every *Field wrapper renders under its control, data-visible/data-detached/data-input-variant hooks, generic .inner/.message class names prefixed, dead compact prop and hidden-state border CSS removed, icon prop, CSS token API
|
|
@@ -339,5 +339,5 @@ npm install
|
|
|
339
339
|
- If security modules are not wanted, omit `nuxt-security` from `dependencies` and remove it
|
|
340
340
|
from `modules` and the `security` config block in `nuxt.config.ts`. Same for `@nuxtjs/robots`.
|
|
341
341
|
- The `colourScheme.enabled: false` default is correct for most consumer apps — enable only if
|
|
342
|
-
the app needs light/dark switching.
|
|
342
|
+
the app needs light/dark switching (then follow `theming-dark-mode.md`: the layer ships light values only).
|
|
343
343
|
- Check the latest `srcdev-nuxt-components` version on npm before writing `package.json`.
|
|
@@ -254,8 +254,8 @@ In `app/assets/styles/setup/02.colours/index.css` (create if it doesn't exist):
|
|
|
254
254
|
--theme-chroma: var(--palette-gold-chroma);
|
|
255
255
|
|
|
256
256
|
/* Page-level tokens — readable named steps, not raw oklch */
|
|
257
|
-
--colour-text-accent:
|
|
258
|
-
--colour-text-eyebrow:
|
|
257
|
+
--colour-text-accent: var(--gold-09);
|
|
258
|
+
--colour-text-eyebrow: var(--gold-09);
|
|
259
259
|
}
|
|
260
260
|
|
|
261
261
|
/* Optional: make it available as a data-theme variant too */
|
|
@@ -295,37 +295,10 @@ approach whenever you need `--gold-09` style references in your CSS.
|
|
|
295
295
|
|
|
296
296
|
## Light / dark mode
|
|
297
297
|
|
|
298
|
-
|
|
299
|
-
`
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
html {
|
|
303
|
-
color-scheme: light dark; /* OS preference */
|
|
304
|
-
&.light { color-scheme: light; }
|
|
305
|
-
&.dark { color-scheme: dark; }
|
|
306
|
-
}
|
|
307
|
-
```
|
|
308
|
-
|
|
309
|
-
Use `light-dark()` in your own override values too:
|
|
310
|
-
|
|
311
|
-
```css
|
|
312
|
-
:where(html) {
|
|
313
|
-
--theme-hue: 85;
|
|
314
|
-
--theme-chroma: 0.20;
|
|
315
|
-
--page-bg: light-dark(var(--slate-00), var(--slate-08));
|
|
316
|
-
}
|
|
317
|
-
```
|
|
318
|
-
|
|
319
|
-
**Multi-value caveat**: `light-dark()` cannot contain comma-separated values (e.g. box-shadow
|
|
320
|
-
lists). Use intermediate scalar vars:
|
|
321
|
-
|
|
322
|
-
```css
|
|
323
|
-
--_shadow-a: light-dark(0.08, 0.5);
|
|
324
|
-
--my-shadow: 0 8px 32px rgba(0, 0, 0, var(--_shadow-a));
|
|
325
|
-
```
|
|
326
|
-
|
|
327
|
-
The `useColourScheme()` composable toggles the `.light` / `.dark` class on `<html>`. See
|
|
328
|
-
`composable-colour-scheme.md` for the full API.
|
|
298
|
+
The layer ships light values only (since 2026-10-04): no token uses `light-dark()`, because older
|
|
299
|
+
iPad Safari doesn't support it. Dark mode is the consumer app's decision. `theming-dark-mode.md`
|
|
300
|
+
has the full setup: enabling the scheme plumbing, the complete dark token set to copy (works on
|
|
301
|
+
older Safari), and the shorter `light-dark()` alternative for apps that don't need it.
|
|
329
302
|
|
|
330
303
|
## Hue angle reference
|
|
331
304
|
|
|
@@ -53,6 +53,36 @@ an inline fallback, even if that fallback happens to be a Tier 1 or Tier 2 token
|
|
|
53
53
|
colour, fixed on purpose across states), reuse the base token — don't manufacture a second name
|
|
54
54
|
for a value that never diverges.
|
|
55
55
|
|
|
56
|
+
## Line-clamp tokens
|
|
57
|
+
|
|
58
|
+
Text that could overflow (names, titles, descriptions, body text) gets one public token per
|
|
59
|
+
element, `--{component}-{element}-line-clamp`. It covers both ellipsis cases: `1` is single-line
|
|
60
|
+
ellipsis, any other number caps the lines with an ellipsis on the last, `none` shows everything.
|
|
61
|
+
Prefer this over a prop: line count is presentation, and CSS handles it without a re-render.
|
|
62
|
+
|
|
63
|
+
```css
|
|
64
|
+
.google-review-card-author-name {
|
|
65
|
+
display: -webkit-box;
|
|
66
|
+
-webkit-box-orient: vertical;
|
|
67
|
+
overflow: hidden;
|
|
68
|
+
overflow-wrap: anywhere; /* unbroken strings still wrap before the clamp applies */
|
|
69
|
+
-webkit-line-clamp: var(--google-reviews-author-line-clamp, 2);
|
|
70
|
+
line-clamp: var(--google-reviews-author-line-clamp, 2);
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
- Default to `none` unless the layout clearly needs a cap (a row of equal-height cards, say). Use
|
|
75
|
+
`none` for "no clamp", never a large number like `100` (`ServicesCard`'s
|
|
76
|
+
`--description-line-clamp: 100` predates this; switch it to `none` when it's next migrated).
|
|
77
|
+
- Don't clamp text that must stay readable: legal or attribution text, error messages, link text
|
|
78
|
+
that names its destination.
|
|
79
|
+
- Clamped text stays in the DOM, so screen readers read all of it; sighted users lose the rest,
|
|
80
|
+
which is why `none` is the default. If a clamped item has nowhere else to read it in full,
|
|
81
|
+
consider a "read more" link or a `title`.
|
|
82
|
+
- `display: -webkit-box` makes an inline element (`<a>`, `<span>`) block-level; check the
|
|
83
|
+
layout around it.
|
|
84
|
+
- Document it in `CONSUMER-STYLING.md` with its default and the `1`/`none` options.
|
|
85
|
+
|
|
56
86
|
## Multi-level fallback chains are fine
|
|
57
87
|
|
|
58
88
|
A component that wants to *look like* another component's default (rather than duplicating a
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
# Dark Mode in a Consumer App
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
Since 2026-10-04 this layer ships **light colour values only**. No token in the layer uses
|
|
6
|
+
`light-dark()`, because older iPad Safari doesn't support it. A custom property holding an
|
|
7
|
+
unsupported `light-dark()` is stored without complaint, then every property that reads it becomes
|
|
8
|
+
invalid at computed-value time: transparent backgrounds, `currentColor` borders, no console error.
|
|
9
|
+
|
|
10
|
+
Dark mode is the consumer app's decision. The layer still provides all the switching plumbing:
|
|
11
|
+
|
|
12
|
+
| Part | What it does |
|
|
13
|
+
|---|---|
|
|
14
|
+
| `runtimeConfig.public.colourScheme.enabled` | Turns the plumbing on. `new-app-scaffold.md` defaults it to `false`, so set it to `true` (see `colour-scheme-disable.md` for the config shape) |
|
|
15
|
+
| Head script (`modules/colour-scheme.ts`, `utils/colour-scheme-init.ts`) | Before first paint, sets `html[data-color-scheme]` to the saved `auto`, `light` or `dark` (default `auto`) |
|
|
16
|
+
| `useColourScheme()` | Reads/writes the saved scheme. See `composable-colour-scheme.md` |
|
|
17
|
+
| `DisplayThemeSwitch` | Ready-made system/light/dark picker. See `components/display-theme-switch.md` |
|
|
18
|
+
|
|
19
|
+
What the app supplies is the dark **values**, and the CSS `color-scheme` property. The layer
|
|
20
|
+
doesn't declare `color-scheme` at all (removed 2026-10-04), so browsers render native UI
|
|
21
|
+
(scrollbars, date and select pickers, form control internals) light unless the app says
|
|
22
|
+
otherwise. There are two ways to do it.
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## Option A: attribute + media query (works on older Safari)
|
|
27
|
+
|
|
28
|
+
Redeclare the tokens you want dark under `html[data-color-scheme="dark"]`, and again inside a
|
|
29
|
+
`prefers-color-scheme: dark` media query for `auto`. The two blocks hold the same declarations;
|
|
30
|
+
that duplication is the cost of not using `light-dark()`.
|
|
31
|
+
|
|
32
|
+
Put it in its own file, imported **last** in the app's `03.theming/index.css` (see
|
|
33
|
+
`consumer-styles-structure.md`). The selectors are wrapped in `:where()` like every other consumer
|
|
34
|
+
token file, so they have zero specificity and win over the app's own light values by coming later.
|
|
35
|
+
|
|
36
|
+
```css
|
|
37
|
+
/* setup/03.theming/index.css */
|
|
38
|
+
@import "./_default.css";
|
|
39
|
+
@import "./_forms.css";
|
|
40
|
+
@import "./_dark.css"; /* last */
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Full token set
|
|
44
|
+
|
|
45
|
+
These are the dark values the layer used to ship, so starting from this file gives you back the
|
|
46
|
+
previous built-in dark mode exactly. Delete what you don't need, or swap in your own palette's steps
|
|
47
|
+
(`--gold-04` etc.). If your `_default.css` changed a page-level token to another palette (e.g.
|
|
48
|
+
`--colour-text-accent: var(--gold-09)`), use that palette's step here too.
|
|
49
|
+
|
|
50
|
+
```css
|
|
51
|
+
/* Dark scheme: token values for html[data-color-scheme="dark"], and "auto" on a dark OS. */
|
|
52
|
+
|
|
53
|
+
/* ---- 1. Page-level, a11y and input tokens: html only ---- */
|
|
54
|
+
:where(html[data-color-scheme="dark"]) {
|
|
55
|
+
color-scheme: dark; /* native UI goes dark too */
|
|
56
|
+
|
|
57
|
+
--page-bg: var(--slate-08);
|
|
58
|
+
--colour-text-default: var(--slate-01);
|
|
59
|
+
--colour-text-accent: var(--blue-05);
|
|
60
|
+
--colour-text-eyebrow: var(--blue-05);
|
|
61
|
+
--colour-link-default: var(--blue-02);
|
|
62
|
+
--colour-link-hover: var(--blue-03);
|
|
63
|
+
|
|
64
|
+
--theme-input-surface: var(--slate-10);
|
|
65
|
+
--theme-input-surface-hover: var(--slate-09);
|
|
66
|
+
--theme-input-text-color-normal: var(--slate-01);
|
|
67
|
+
--theme-input-placeholder: var(--slate-04);
|
|
68
|
+
|
|
69
|
+
--focus-box-shadow-colour-on: var(--slate-00);
|
|
70
|
+
--box-shadow-on: 0 0 0 0.3rem var(--slate-00);
|
|
71
|
+
|
|
72
|
+
/* Only if you use StepperList */
|
|
73
|
+
--stepper-list-counter-circle-text: var(--blue-02);
|
|
74
|
+
--stepper-list-counter-circle-border: var(--blue-02);
|
|
75
|
+
--stepper-list-counter-disc-background: var(--blue-02);
|
|
76
|
+
--stepper-list-counter-disc-border: var(--blue-02);
|
|
77
|
+
--stepper-list-counter-square-text: var(--blue-02);
|
|
78
|
+
--stepper-list-counter-square-border: var(--blue-02);
|
|
79
|
+
--stepper-list-connector-color: var(--blue-02);
|
|
80
|
+
--stepper-list-icon-color: var(--blue-02);
|
|
81
|
+
|
|
82
|
+
/* Only if you use TreatmentConsultant */
|
|
83
|
+
--treatment-consultant-primary-colour: var(--amber-02);
|
|
84
|
+
--treatment-consultant-primary-foreground: var(--amber-02);
|
|
85
|
+
--treatment-consultant-muted-colour: var(--amber-05);
|
|
86
|
+
--treatment-consultant-muted-foreground: var(--amber-05);
|
|
87
|
+
--treatment-consultant-background: var(--amber-10);
|
|
88
|
+
--treatment-consultant-foreground: var(--amber-02);
|
|
89
|
+
--treatment-consultant-border-colour: var(--amber-03);
|
|
90
|
+
--treatment-consultant-checked-surface-colour: var(--green-10);
|
|
91
|
+
--treatment-consultant-checked-stroke-colour: var(--green-03);
|
|
92
|
+
--treatment-consultant-conflict-surface-colour: var(--sunset-09);
|
|
93
|
+
--treatment-consultant-conflict-stroke-colour: var(--sunset-03);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/* ---- 2. Semantic slots: html AND every themed element ---- */
|
|
97
|
+
:where(
|
|
98
|
+
html[data-color-scheme="dark"],
|
|
99
|
+
html[data-color-scheme="dark"] [data-theme],
|
|
100
|
+
html[data-color-scheme="dark"] [data-invalid]
|
|
101
|
+
) {
|
|
102
|
+
--theme-surface: var(--colour-theme-9);
|
|
103
|
+
--theme-surface-inverted: var(--colour-theme-2);
|
|
104
|
+
--theme-surface-hover: var(--colour-theme-7);
|
|
105
|
+
--theme-accent: var(--colour-theme-4);
|
|
106
|
+
--theme-surface-subtle: var(--colour-theme-9);
|
|
107
|
+
--theme-border: var(--colour-theme-5);
|
|
108
|
+
--theme-border-focus: var(--colour-theme-3);
|
|
109
|
+
--theme-ring: var(--colour-theme-9);
|
|
110
|
+
--theme-text: var(--colour-theme-2);
|
|
111
|
+
--theme-text-inverted: var(--colour-theme-7);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/* ---- 3. Warning keeps its AA-safe step 6 surface in both schemes ---- */
|
|
115
|
+
:where(html[data-color-scheme="dark"] [data-theme="warning"]) {
|
|
116
|
+
--theme-surface: var(--colour-theme-6);
|
|
117
|
+
--theme-surface-hover: var(--colour-theme-7);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/* ---- 4. Form error tokens ---- */
|
|
121
|
+
:where(
|
|
122
|
+
html[data-color-scheme="dark"] [data-invalid],
|
|
123
|
+
html[data-color-scheme="dark"] [data-theme="error"]
|
|
124
|
+
) {
|
|
125
|
+
--theme-error-surface: var(--red-07);
|
|
126
|
+
--theme-error-border: var(--red-07);
|
|
127
|
+
--theme-error-outline: var(--red-05);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/* ---- 5. "auto" on a dark OS: repeat blocks 1-4 with data-color-scheme="auto" ---- */
|
|
131
|
+
@media (prefers-color-scheme: dark) {
|
|
132
|
+
:where(html[data-color-scheme="auto"]) {
|
|
133
|
+
/* block 1 declarations */
|
|
134
|
+
}
|
|
135
|
+
:where(
|
|
136
|
+
html[data-color-scheme="auto"],
|
|
137
|
+
html[data-color-scheme="auto"] [data-theme],
|
|
138
|
+
html[data-color-scheme="auto"] [data-invalid]
|
|
139
|
+
) {
|
|
140
|
+
/* block 2 declarations */
|
|
141
|
+
}
|
|
142
|
+
:where(html[data-color-scheme="auto"] [data-theme="warning"]) {
|
|
143
|
+
/* block 3 declarations */
|
|
144
|
+
}
|
|
145
|
+
:where(
|
|
146
|
+
html[data-color-scheme="auto"] [data-invalid],
|
|
147
|
+
html[data-color-scheme="auto"] [data-theme="error"]
|
|
148
|
+
) {
|
|
149
|
+
/* block 4 declarations */
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### Why the selectors look like that
|
|
155
|
+
|
|
156
|
+
- **Semantic slots must be redeclared on `[data-theme]` and `[data-invalid]` elements**, not only
|
|
157
|
+
on `html`. The layer declares them on all three (`_theme-slots.css`) so `var(--colour-theme-N)`
|
|
158
|
+
resolves against each themed element's own hue. A dark value set only on `html` would be
|
|
159
|
+
computed once with the default hue and inherited, and every `data-theme="success"`/`"error"`
|
|
160
|
+
element would turn the default blue.
|
|
161
|
+
- **`[data-theme="warning"]` needs its own block** because the layer gives warning a fixed step 6
|
|
162
|
+
surface (the lightest orange step where light text still passes AA). Block 2 would otherwise
|
|
163
|
+
overwrite it.
|
|
164
|
+
- **No `data-color-scheme` attribute at all** only happens if the plumbing is disabled. If you
|
|
165
|
+
render before the head script can run (rare), add `html:not([data-color-scheme])` next to the
|
|
166
|
+
`auto` selector in block 5.
|
|
167
|
+
|
|
168
|
+
### Component tokens
|
|
169
|
+
|
|
170
|
+
Components whose own public tokens default to fixed light values (e.g.
|
|
171
|
+
`--clipped-panel-background-colour`, `--glass-panel-*`, `--carousel-flip-*`,
|
|
172
|
+
`--dashboard-quad-grid-*`) don't change with the semantic slots. Add any you use to block 1 (and
|
|
173
|
+
block 5). Each component's `CONSUMER-STYLING.md` lists its colour tokens.
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
## Option B: `light-dark()` (shorter, modern browsers only)
|
|
178
|
+
|
|
179
|
+
If the app doesn't need to support older Safari (`light-dark()` needs Safari 17.5+, Chrome 123+,
|
|
180
|
+
Firefox 120+), set each token once with both values. `light-dark()` picks its branch from the
|
|
181
|
+
`color-scheme` property, which the layer doesn't set, so the app declares it, following the
|
|
182
|
+
attribute:
|
|
183
|
+
|
|
184
|
+
```css
|
|
185
|
+
html {
|
|
186
|
+
color-scheme: light dark; /* "auto": follow the OS */
|
|
187
|
+
|
|
188
|
+
&[data-color-scheme="light"] { color-scheme: light; }
|
|
189
|
+
&[data-color-scheme="dark"] { color-scheme: dark; }
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
:where(html) {
|
|
193
|
+
--page-bg: light-dark(var(--slate-00), var(--slate-08));
|
|
194
|
+
--colour-text-default: light-dark(var(--slate-09), var(--slate-01));
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
:where(html, [data-theme], [data-invalid]) {
|
|
198
|
+
--theme-surface: light-dark(var(--colour-theme-7), var(--colour-theme-9));
|
|
199
|
+
--theme-text: light-dark(var(--colour-theme-9), var(--colour-theme-2));
|
|
200
|
+
/* ...the rest of block 2 from Option A, each as light-dark(layer value, dark value) */
|
|
201
|
+
}
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
The light value for each token is the layer's current default (see
|
|
205
|
+
`app/assets/styles/setup/03.theming/` and `a11y/_variables.css` in the package). The same rules
|
|
206
|
+
apply: semantic slots on `[data-theme]`/`[data-invalid]` too, and a separate rule for
|
|
207
|
+
`[data-theme="warning"]`.
|
|
208
|
+
|
|
209
|
+
**Multi-value caveat**: `light-dark()` can't hold comma-separated values (e.g. a box-shadow list).
|
|
210
|
+
Put the varying part in a scalar var:
|
|
211
|
+
|
|
212
|
+
```css
|
|
213
|
+
--_shadow-a: light-dark(0.08, 0.5);
|
|
214
|
+
--my-shadow: 0 8px 32px rgba(0, 0, 0, var(--_shadow-a));
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
On a browser without `light-dark()`, Option B fails the same silent way described in the
|
|
218
|
+
Overview. Don't mix: if older Safari matters for any page, use Option A everywhere.
|
|
219
|
+
|
|
220
|
+
---
|
|
221
|
+
|
|
222
|
+
## Light-only apps
|
|
223
|
+
|
|
224
|
+
If the app has no dark mode, leave `colourScheme.enabled: false` (the scaffold default) and add
|
|
225
|
+
nothing. With no `color-scheme` declared anywhere, native UI stays light even on a dark OS.
|
|
226
|
+
|
|
227
|
+
---
|
|
228
|
+
|
|
229
|
+
## Storybook in a consumer app
|
|
230
|
+
|
|
231
|
+
This repo's own Storybook is set up as a consumer and is a working reference:
|
|
232
|
+
`.storybook/color-scheme.css` holds blocks 1-4, and `.storybook/preview.ts` turns its "Follow OS"
|
|
233
|
+
toolbar option into `data-color-scheme="light"`/`"dark"` using `matchMedia`, so it needs no block 5.
|
|
234
|
+
Those files aren't in the npm package; the token set above is the same content.
|
|
235
|
+
|
|
236
|
+
---
|
|
237
|
+
|
|
238
|
+
## Related
|
|
239
|
+
|
|
240
|
+
- `composable-colour-scheme.md`: `useColourScheme()` API
|
|
241
|
+
- `colour-scheme-disable.md`: the `enabled` flag and turning everything off
|
|
242
|
+
- `theming-colour-ramps.md`: ramps, semantic slots, palettes for your dark values
|
|
243
|
+
- `consumer-styles-structure.md`: where `_dark.css` sits in the app's CSS tree
|
|
@@ -120,10 +120,10 @@ Create `app/assets/styles/setup/03.theming/_default.css`:
|
|
|
120
120
|
--theme-chroma: var(--palette-gold-chroma);
|
|
121
121
|
|
|
122
122
|
/* Page-level tokens — readable named steps, not raw oklch */
|
|
123
|
-
--page-bg:
|
|
124
|
-
--colour-text-default:
|
|
125
|
-
--colour-text-accent:
|
|
126
|
-
--colour-text-eyebrow:
|
|
123
|
+
--page-bg: var(--slate-00);
|
|
124
|
+
--colour-text-default: var(--slate-09);
|
|
125
|
+
--colour-text-accent: var(--gold-09);
|
|
126
|
+
--colour-text-eyebrow: var(--gold-09);
|
|
127
127
|
}
|
|
128
128
|
```
|
|
129
129
|
|
|
@@ -172,8 +172,8 @@ export default defineNuxtConfig({
|
|
|
172
172
|
The layer's `theme-ramp.css` declares the formula on `:where(html, [data-theme], [data-invalid])`.
|
|
173
173
|
When your override sets `--theme-hue`/`--theme-chroma` on `:where(html)`, every `--colour-theme-*`
|
|
174
174
|
step recalculates to your palette. Semantic slots (`--theme-surface`, `--theme-text`, etc.) are
|
|
175
|
-
declared on that same selector
|
|
176
|
-
|
|
175
|
+
declared on that same selector with light values only. The layer ships no dark scheme; to add
|
|
176
|
+
one, see `theming-dark-mode.md`.
|
|
177
177
|
|
|
178
178
|
The generated `_gold.css` gives you a named alias for each step (`--gold-09` etc.) so your
|
|
179
179
|
page-level token values stay readable. The values match what the formula would produce for
|
|
@@ -183,7 +183,6 @@ that hue/chroma pair.
|
|
|
183
183
|
|
|
184
184
|
- Define `--colour-theme-0` through `--colour-theme-10` — the formula handles it
|
|
185
185
|
- Redeclare `--theme-surface`, `--theme-border`, `--theme-ring` etc. — they auto-update
|
|
186
|
-
- Write separate light/dark files — use `light-dark()` in a single file
|
|
187
186
|
- Override any button or input component tokens — they all consume semantic slots
|
|
188
187
|
|
|
189
188
|
## rem sizing
|
|
@@ -88,8 +88,8 @@ Page-level tokens:
|
|
|
88
88
|
```css
|
|
89
89
|
/* Make primary buttons darker/more saturated (step-7/9 defaults, step-9/7 hover) */
|
|
90
90
|
:where(html) {
|
|
91
|
-
--theme-surface:
|
|
92
|
-
--theme-surface-hover:
|
|
91
|
+
--theme-surface: oklch(48% 0.18 85);
|
|
92
|
+
--theme-surface-hover: oklch(32% 0.18 85);
|
|
93
93
|
}
|
|
94
94
|
```
|
|
95
95
|
|
|
@@ -97,9 +97,9 @@ Page-level tokens:
|
|
|
97
97
|
|
|
98
98
|
```css
|
|
99
99
|
:where(html) {
|
|
100
|
-
--theme-input-surface:
|
|
101
|
-
--theme-input-text-color-normal:
|
|
102
|
-
--theme-input-placeholder:
|
|
100
|
+
--theme-input-surface: oklch(0.99 0 0);
|
|
101
|
+
--theme-input-text-color-normal: oklch(0.20 0.01 255);
|
|
102
|
+
--theme-input-placeholder: oklch(0.60 0.01 255);
|
|
103
103
|
}
|
|
104
104
|
```
|
|
105
105
|
|
|
@@ -107,9 +107,9 @@ Page-level tokens:
|
|
|
107
107
|
|
|
108
108
|
```css
|
|
109
109
|
:where(html) {
|
|
110
|
-
--page-bg:
|
|
111
|
-
--colour-text-default:
|
|
112
|
-
--colour-text-accent:
|
|
110
|
+
--page-bg: oklch(0.98 0 0);
|
|
111
|
+
--colour-text-default: oklch(0.20 0 0);
|
|
112
|
+
--colour-text-accent: oklch(0.30 0.16 85);
|
|
113
113
|
}
|
|
114
114
|
```
|
|
115
115
|
|
|
@@ -140,8 +140,8 @@ To restrict an override to a specific section, scope to a wrapper class instead
|
|
|
140
140
|
|
|
141
141
|
.hero-cta {
|
|
142
142
|
/* Just these buttons use a different surface colour */
|
|
143
|
-
--theme-surface:
|
|
144
|
-
--theme-surface-hover:
|
|
143
|
+
--theme-surface: oklch(48% 0.16 85);
|
|
144
|
+
--theme-surface-hover: oklch(32% 0.14 85);
|
|
145
145
|
}
|
|
146
146
|
```
|
|
147
147
|
|
|
@@ -160,6 +160,6 @@ To change how `data-theme="success"` looks without affecting other themes:
|
|
|
160
160
|
## Notes
|
|
161
161
|
|
|
162
162
|
- Only override the tokens you actually need — everything else falls back to layer defaults
|
|
163
|
-
-
|
|
163
|
+
- The layer ships light values only (no `light-dark()`, for older iPad Safari). For dark mode, redeclare tokens under `html[data-color-scheme="dark"]` plus a `prefers-color-scheme` block for `auto`, or use `light-dark()` yourself if you accept the browser-support cost. Full setup in `theming-dark-mode.md`.
|
|
164
164
|
- `--slate-*` neutral tokens come from the layer and do not need to be redefined unless you genuinely want different neutrals
|
|
165
165
|
- For a full palette replacement (changing the layer default), use `theming-override-default.md` instead
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
{
|
|
2
|
+
"SRCDEV GoogleReviews Basic": {
|
|
3
|
+
"description": "GoogleReviews fed by useGoogleReviews() (needs NUXT_GOOGLE_REVIEWS_API_KEY and NUXT_GOOGLE_REVIEWS_PLACE_ID)",
|
|
4
|
+
"scope": "vue,html",
|
|
5
|
+
"body": [
|
|
6
|
+
"<GoogleReviews :data=\"${1:googleReviews}\" />"
|
|
7
|
+
]
|
|
8
|
+
},
|
|
9
|
+
"SRCDEV GoogleReviews Script": {
|
|
10
|
+
"description": "Fetch Google reviews from the layer's server route",
|
|
11
|
+
"scope": "typescript,javascript,vue",
|
|
12
|
+
"body": [
|
|
13
|
+
"const { data: ${1:googleReviews} } = await useGoogleReviews();"
|
|
14
|
+
]
|
|
15
|
+
},
|
|
16
|
+
"SRCDEV GoogleReviews With Heading": {
|
|
17
|
+
"description": "GoogleReviews labelled by its own heading, optionally filtered",
|
|
18
|
+
"scope": "vue,html",
|
|
19
|
+
"body": [
|
|
20
|
+
"<GoogleReviews :data=\"${1:googleReviews}\" :min-rating=\"${2:0}\">",
|
|
21
|
+
" <template #heading=\"{ headingId }\">",
|
|
22
|
+
" <h2 :id=\"headingId\">${3:What clients say}</h2>",
|
|
23
|
+
" </template>",
|
|
24
|
+
"</GoogleReviews>"
|
|
25
|
+
]
|
|
26
|
+
},
|
|
27
|
+
"SRCDEV GoogleReviews Custom Card": {
|
|
28
|
+
"description": "GoogleReviews with each card rendered through the card slot",
|
|
29
|
+
"scope": "vue,html",
|
|
30
|
+
"body": [
|
|
31
|
+
"<GoogleReviews :data=\"${1:googleReviews}\">",
|
|
32
|
+
" <template #card=\"{ review }\">",
|
|
33
|
+
" <GoogleReviewCard :review=\"review\" :style-class-passthrough=\"['${2:my-review}']\" />",
|
|
34
|
+
" </template>",
|
|
35
|
+
"</GoogleReviews>"
|
|
36
|
+
]
|
|
37
|
+
},
|
|
38
|
+
"SRCDEV GoogleReviews CSS Override": {
|
|
39
|
+
"description": "CSS override scaffold for GoogleReviews — scope to a page or section class. See CONSUMER-STYLING.md for the full token list.",
|
|
40
|
+
"scope": "css",
|
|
41
|
+
"body": [
|
|
42
|
+
".$1my-page {",
|
|
43
|
+
" .google-reviews {",
|
|
44
|
+
" --google-reviews-gap: ;",
|
|
45
|
+
" --google-reviews-card-width: ;",
|
|
46
|
+
" --google-reviews-scroll-padding: ;",
|
|
47
|
+
" --google-reviews-scrollbar-width: ;",
|
|
48
|
+
" --google-reviews-scrollbar-thumb-colour: ;",
|
|
49
|
+
" --google-reviews-scrollbar-track-colour: ;",
|
|
50
|
+
" --google-reviews-summary-rating-font-size: ;",
|
|
51
|
+
" --google-reviews-focus-outline-width: ;",
|
|
52
|
+
" --google-reviews-focus-outline-colour: ;",
|
|
53
|
+
"",
|
|
54
|
+
" --google-reviews-card-gap: ;",
|
|
55
|
+
" --google-reviews-card-padding: ;",
|
|
56
|
+
" --google-reviews-card-border-width: ;",
|
|
57
|
+
" --google-reviews-card-border-colour: ;",
|
|
58
|
+
" --google-reviews-card-border-radius: ;",
|
|
59
|
+
" --google-reviews-card-surface: ;",
|
|
60
|
+
" --google-reviews-card-text-colour: ;",
|
|
61
|
+
" --google-reviews-avatar-surface: ;",
|
|
62
|
+
" --google-reviews-avatar-text-colour: ;",
|
|
63
|
+
" --google-reviews-author-font-weight: ;",
|
|
64
|
+
" --google-reviews-author-line-clamp: ;",
|
|
65
|
+
" --google-reviews-text-line-height: ;",
|
|
66
|
+
" --google-reviews-text-line-clamp: ;",
|
|
67
|
+
" --google-reviews-link-colour: ;",
|
|
68
|
+
"",
|
|
69
|
+
" --google-reviews-star-size: ;",
|
|
70
|
+
" --google-reviews-star-colour: ;",
|
|
71
|
+
" --google-reviews-star-empty-colour: ;",
|
|
72
|
+
" --google-reviews-meta-font-size: ;",
|
|
73
|
+
" --google-reviews-meta-text-colour: ;",
|
|
74
|
+
" }",
|
|
75
|
+
"}"
|
|
76
|
+
]
|
|
77
|
+
}
|
|
78
|
+
}
|