srcdev-nuxt-components 9.5.7 → 9.6.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/.claude/skills/component-compliance-checklist.md +8 -2
- package/.claude/skills/component-dynamic-slots.md +3 -3
- package/.claude/skills/component-export-types.md +10 -16
- package/.claude/skills/component-naming.md +1 -1
- package/.claude/skills/components/alert-content.md +9 -1
- package/.claude/skills/components/breadcrumb.md +5 -0
- package/.claude/skills/components/container-glow.md +21 -8
- package/.claude/skills/components/display-avatar.md +13 -7
- package/.claude/skills/components/display-chip.md +37 -24
- package/.claude/skills/components/display-pill.md +58 -8
- package/.claude/skills/components/display-prompt.md +8 -5
- package/.claude/skills/components/display-toast.md +21 -4
- package/.claude/skills/components/glowing-border.md +20 -2
- package/.claude/skills/components/input-button.md +3 -0
- package/.claude/skills/components/responsive-header.md +59 -0
- package/.claude/skills/components/select-menu.md +54 -5
- package/.claude/skills/components/service-detail.md +32 -0
- package/.claude/skills/components/service-summary.md +8 -0
- package/.claude/skills/components/services-card-grid.md +5 -3
- package/.claude/skills/components/services-card.md +22 -12
- package/.claude/skills/components/site-header.md +2 -2
- package/.claude/skills/components/slider-gallery.md +10 -4
- package/.claude/skills/components/tabbed-content.md +300 -0
- package/.claude/skills/composable-anchor-scroll.md +16 -15
- package/.claude/skills/index.md +4 -5
- package/.claude/skills/storybook-add-story.md +53 -0
- package/.claude/skills/theming-status-tokens.md +60 -0
- package/.vscode/srcdev-component-display-avatar.code-snippets +2 -1
- package/.vscode/srcdev-component-display-chip.code-snippets +3 -2
- package/.vscode/srcdev-component-display-pill.code-snippets +10 -0
- package/.vscode/srcdev-component-responsive-header.code-snippets +16 -0
- package/.vscode/srcdev-component-select-menu.code-snippets +28 -0
- package/.vscode/srcdev-component-service-detail.code-snippets +1 -0
- package/.vscode/srcdev-component-service-summary.code-snippets +3 -1
- package/.vscode/srcdev-component-services-card.code-snippets +38 -36
- package/.vscode/srcdev-component-services-grids.code-snippets +2 -1
- package/.vscode/{srcdev-component-tabs-core.code-snippets → srcdev-component-tabbed-content.code-snippets} +35 -16
- package/app/assets/styles/setup/03.theming/_status.css +24 -0
- package/app/assets/styles/setup/03.theming/index.css +1 -0
- package/app/components/01.atoms/animations/container-glow/CONSUMER-STYLING.md +39 -35
- package/app/components/01.atoms/animations/container-glow/ContainerGlow.vue +25 -15
- package/app/components/01.atoms/animations/container-glow/stories/ContainerGlow.stories.ts +130 -24
- package/app/components/01.atoms/animations/container-glow/tests/ContainerGlow.spec.ts +45 -0
- package/app/components/01.atoms/animations/container-glow/tests/__snapshots__/ContainerGlow.spec.ts.snap +2 -2
- package/app/components/01.atoms/animations/glowing-border/CONSUMER-STYLING.md +76 -19
- package/app/components/01.atoms/animations/glowing-border/GlowingBorder.vue +12 -10
- package/app/components/01.atoms/animations/glowing-border/stories/GlowingBorder.stories.ts +201 -42
- package/app/components/01.atoms/animations/glowing-border/tests/GlowingBorder.spec.ts +15 -4
- package/app/components/01.atoms/animations/glowing-border/tests/__snapshots__/GlowingBorder.spec.ts.snap +1 -1
- package/app/components/01.atoms/display-pill/CONSUMER-STYLING.md +78 -9
- package/app/components/01.atoms/display-pill/DisplayPill.vue +59 -23
- package/app/components/01.atoms/display-pill/stories/DisplayPill.stories.ts +240 -16
- package/app/components/01.atoms/display-pill/tests/DisplayPill.spec.ts +43 -14
- package/app/components/01.atoms/display-pill/tests/__snapshots__/DisplayPill.spec.ts.snap +3 -3
- package/app/components/01.atoms/navigation/breadcrumb/Breadcrumb.vue +16 -4
- package/app/components/01.atoms/navigation/breadcrumb/CONSUMER-STYLING.md +28 -5
- package/app/components/01.atoms/navigation/breadcrumb/stories/Breadcrumb.stories.ts +94 -0
- package/app/components/01.atoms/navigation/breadcrumb/tests/Breadcrumb.spec.ts +27 -0
- package/app/components/01.atoms/tabbed-content/CONSUMER-STYLING.md +201 -0
- package/app/components/01.atoms/tabbed-content/TabbedContent.vue +750 -0
- package/app/components/01.atoms/tabbed-content/stories/TabbedContent.stories.ts +279 -0
- package/app/components/01.atoms/tabbed-content/tests/TabbedContent.spec.ts +816 -0
- package/app/components/02.molecules/alert-content/AlertContent.vue +11 -2
- package/app/components/02.molecules/alert-content/AlertContentInner.vue +19 -0
- package/app/components/02.molecules/alert-content/CONSUMER-STYLING.md +17 -4
- package/app/components/02.molecules/alert-content/stories/AlertContent.stories.ts +200 -17
- package/app/components/02.molecules/alert-content/tests/AlertContent.spec.ts +33 -0
- package/app/components/02.molecules/display-avatar/CONSUMER-STYLING.md +7 -5
- package/app/components/02.molecules/display-avatar/DisplayAvatar.vue +6 -2
- package/app/components/02.molecules/display-avatar/stories/DisplayAvatar.stories.ts +24 -3
- package/app/components/02.molecules/display-avatar/tests/DisplayAvatar.spec.ts +19 -0
- package/app/components/02.molecules/display-avatar/tests/__snapshots__/DisplayAvatar.spec.ts.snap +2 -1
- package/app/components/02.molecules/display-chip/CONSUMER-STYLING.md +52 -19
- package/app/components/02.molecules/display-chip/DisplayChip.vue +72 -54
- package/app/components/02.molecules/display-chip/stories/DisplayChip.stories.ts +103 -6
- package/app/components/02.molecules/display-chip/tests/DisplayChip.spec.ts +98 -29
- package/app/components/02.molecules/display-chip/tests/__snapshots__/DisplayChip.spec.ts.snap +8 -4
- package/app/components/02.molecules/prompt/CONSUMER-STYLING.md +9 -1
- package/app/components/02.molecules/prompt/DisplayPrompt.vue +2 -2
- package/app/components/02.molecules/prompt/stories/DisplayPrompt.stories.ts +208 -71
- package/app/components/02.molecules/prompt/tests/DisplayPrompt.spec.ts +19 -6
- package/app/components/02.molecules/select-menu/CONSUMER-STYLING.md +66 -8
- package/app/components/02.molecules/select-menu/SelectMenu.vue +141 -18
- package/app/components/02.molecules/select-menu/stories/SelectMenu.stories.ts +232 -2
- package/app/components/02.molecules/select-menu/tests/SelectMenu.spec.ts +151 -0
- package/app/components/02.molecules/select-menu/tests/__snapshots__/SelectMenu.spec.ts.snap +6 -2
- package/app/components/02.molecules/toast/CONSUMER-STYLING.md +91 -27
- package/app/components/02.molecules/toast/DisplayToast.vue +109 -50
- package/app/components/02.molecules/toast/DisplayToastProvider.vue +109 -45
- package/app/components/02.molecules/toast/stories/DisplayToast.stories.ts +23 -0
- package/app/components/02.molecules/toast/stories/DisplayToastProvider.stories.ts +73 -0
- package/app/components/02.molecules/toast/tests/DisplayToast.spec.ts +132 -18
- package/app/components/02.molecules/toast/tests/DisplayToastProvider.spec.ts +105 -39
- package/app/components/03.organisms/image-galleries/slider-gallery/CONSUMER-STYLING.md +19 -1
- package/app/components/03.organisms/image-galleries/slider-gallery/SliderGallery.vue +65 -7
- package/app/components/03.organisms/image-galleries/slider-gallery/stories/SliderGallery.stories.ts +201 -63
- package/app/components/03.organisms/image-galleries/slider-gallery/tests/SliderGallery.spec.ts +41 -2
- package/app/components/03.organisms/image-galleries/slider-gallery/tests/__snapshots__/SliderGallery.spec.ts.snap +6 -6
- package/app/components/03.organisms/responsive-header/CONSUMER-STYLING.md +42 -11
- package/app/components/03.organisms/responsive-header/NavigationItems.vue +60 -16
- package/app/components/03.organisms/responsive-header/ResponsiveHeader.vue +86 -24
- package/app/components/03.organisms/responsive-header/stories/ResponsiveHeader.stories.ts +52 -0
- package/app/components/03.organisms/responsive-header/tests/NavigationItems.spec.ts +20 -0
- package/app/components/03.organisms/responsive-header/tests/ResponsiveHeader.spec.ts +40 -0
- package/app/components/03.organisms/services/service-detail/CONSUMER-STYLING.md +92 -41
- package/app/components/03.organisms/services/service-detail/ServiceDetail.vue +198 -73
- package/app/components/03.organisms/services/service-detail/stories/ServiceDetail.stories.ts +157 -9
- package/app/components/03.organisms/services/service-detail/tests/ServiceDetail.spec.ts +58 -0
- package/app/components/03.organisms/services/service-detail/tests/__snapshots__/ServiceDetail.spec.ts.snap +5 -4
- package/app/components/03.organisms/services/service-summary/CONSUMER-STYLING.md +30 -0
- package/app/components/03.organisms/services/service-summary/ServiceSummary.vue +32 -8
- package/app/components/03.organisms/services/service-summary/stories/ServiceSummary.stories.ts +147 -0
- package/app/components/03.organisms/services/service-summary/tests/ServiceSummary.spec.ts +26 -0
- package/app/components/03.organisms/services/service-summary/tests/__snapshots__/ServiceSummary.spec.ts.snap +2 -2
- package/app/components/03.organisms/services/services-card/CONSUMER-STYLING.md +104 -63
- package/app/components/03.organisms/services/services-card/ServicesCard.vue +92 -66
- package/app/components/03.organisms/services/services-card/stories/ServicesCard.stories.ts +194 -27
- package/app/components/03.organisms/services/services-card/tests/ServicesCard.spec.ts +34 -8
- package/app/components/03.organisms/services/services-card/tests/__snapshots__/ServicesCard.spec.ts.snap +8 -8
- package/app/components/03.organisms/services/services-grids/CONSUMER-STYLING.md +9 -0
- package/app/components/03.organisms/services/services-grids/ServicesCardGrid.vue +2 -1
- package/app/components/03.organisms/services/services-grids/stories/ServiceSummaryGrid.stories.ts +46 -6
- package/app/components/03.organisms/services/services-grids/stories/ServicesCardGrid.stories.ts +89 -11
- package/app/components/03.organisms/services/services-grids/tests/ServicesCardGrid.spec.ts +8 -0
- package/app/components/03.organisms/services/services-grids/tests/__snapshots__/ServiceSummaryGrid.spec.ts.snap +6 -6
- package/app/components/03.organisms/services/services-grids/tests/__snapshots__/ServicesCardGrid.spec.ts.snap +24 -24
- package/app/components/05.forms/input-button/CONSUMER-STYLING.md +8 -0
- package/app/components/05.forms/input-button/InputButton.vue +3 -1
- package/app/composables/tests/useAnchorScroll.spec.ts +8 -0
- package/app/composables/useAnchorScroll.ts +1 -1
- package/app/composables/useTabs.ts +148 -206
- package/app/types/components/display-chip.d.ts +8 -4
- package/app/types/components/display-toast.d.ts +2 -0
- package/app/types/components/index.ts +0 -1
- package/app/types/components/select-menu.d.ts +2 -0
- package/app/utils/pausableTimeout.ts +41 -0
- package/nuxt.config.ts +1 -1
- package/package.json +1 -1
- package/.claude/skills/components/site-navigation.md +0 -145
- package/.claude/skills/components/tab-navigation.md +0 -272
- package/.claude/skills/components/tabs-core.md +0 -187
- package/.vscode/srcdev-component-site-navigation.code-snippets +0 -38
- package/.vscode/srcdev-component-tab-navigation.code-snippets +0 -30
- package/app/components/01.atoms/navigation/tabs/CONSUMER-STYLING.md +0 -120
- package/app/components/01.atoms/navigation/tabs/TabsCore.vue +0 -272
- package/app/components/01.atoms/navigation/tabs/stories/TabsCore.stories.ts +0 -199
- package/app/components/01.atoms/navigation/tabs/tests/TabsCore.spec.ts +0 -274
- package/app/components/02.molecules/navigation/site-navigation/CONSUMER-STYLING.md +0 -109
- package/app/components/02.molecules/navigation/site-navigation/SiteNavigation.vue +0 -751
- package/app/components/02.molecules/navigation/site-navigation/stories/SiteNavigation.stories.ts +0 -356
- package/app/components/02.molecules/navigation/site-navigation/tests/SiteNavigation.spec.ts +0 -367
- package/app/components/02.molecules/navigation/site-navigation/tests/__snapshots__/SiteNavigation.spec.ts.snap +0 -30
- package/app/components/02.molecules/navigation/tab-navigation/CONSUMER-STYLING.md +0 -150
- package/app/components/02.molecules/navigation/tab-navigation/TabNavigation.vue +0 -518
- package/app/components/02.molecules/navigation/tab-navigation/stories/TabNavigation.stories.ts +0 -340
- package/app/components/02.molecules/navigation/tab-navigation/tests/TabNavigation.spec.ts +0 -339
- package/app/components/02.molecules/navigation/tab-navigation/tests/__snapshots__/TabNavigation.spec.ts.snap +0 -33
- package/app/composables/useNavCollapse.ts +0 -86
- package/app/types/components/nav-item.d.ts +0 -11
|
@@ -1,40 +1,8 @@
|
|
|
1
|
+
# ServiceDetail — Consumer Styling Guide
|
|
1
2
|
|
|
2
3
|
> **Changed 2026-09-27**: `--service-detail-hero-pill-bg` (and the hero text colour on the pills)
|
|
3
4
|
> never applied before, because `DisplayPill`'s `neutral` variant ignored the base tokens. It now
|
|
4
5
|
> falls back to them, so the hero pills are transparent with the hero text colour as documented.
|
|
5
|
-
# ServiceDetail — Consumer Styling Guide
|
|
6
|
-
|
|
7
|
-
## Layout building blocks
|
|
8
|
-
|
|
9
|
-
The hero banner is a [GridStack](../../../../../.claude/skills/components/grid-stack.md) with two
|
|
10
|
-
stacked layers — an `image` layer (the service photo) behind a `content` layer (the scrim +
|
|
11
|
-
breadcrumb/title/pills). Each layer's root element is itself a
|
|
12
|
-
[PageRow](../../../../../.claude/skills/components/page-row.md): `.service-detail__hero-image-row`
|
|
13
|
-
(prop `heroImageVariant`, default `"full"`) wraps the photo, and `.service-detail__hero-overlay`
|
|
14
|
-
(prop `heroContentVariant`, default `"content"`) *is* the scrim/alignment element, not a wrapper
|
|
15
|
-
around one — a `PageRow`'s own box always spans the full width of its parent regardless of variant,
|
|
16
|
-
only its children default into the variant's grid column, so the one element can carry the
|
|
17
|
-
full-bleed scrim while its child (`.service-detail__hero-content`) still gets pulled to the
|
|
18
|
-
configured column. This means the photo bleeds to whatever width `.service-detail` itself is allowed
|
|
19
|
-
(edge-to-edge if `ServiceDetail` is placed without a content-constraining wrapper around it), while
|
|
20
|
-
the breadcrumb/title/pills always stay pulled into the page's normal content-width column,
|
|
21
|
-
regardless of how wide the photo bleeds. `--service-detail-hero-padding` now only controls
|
|
22
|
-
**block** (vertical) padding on the scrim — horizontal alignment of the text is handled entirely by
|
|
23
|
-
`.service-detail__hero-overlay`'s own `PageRow` grid, not by padding, so it can't fight the page's
|
|
24
|
-
content-grid gutters.
|
|
25
|
-
|
|
26
|
-
The two-column body (main content + sticky sidebar) is wrapped in its own `PageRow`, whose variant
|
|
27
|
-
is a prop: `bodyVariant` (`"full" | "popout" | "content" | "inset-content"`, default `"content"`).
|
|
28
|
-
The closing full-width CTA banner is wrapped the same way via `finalCtaVariant` (same type, same
|
|
29
|
-
`"content"` default). This means a consumer no longer needs to wrap `ServiceDetail` itself in an
|
|
30
|
-
outer `PageRow` purely to constrain either section's width — pass `body-variant`/`final-cta-variant`
|
|
31
|
-
directly. It's still fine (and necessary for a genuine full-bleed hero) to place `ServiceDetail`
|
|
32
|
-
inside an outer, wider `PageRow` — see
|
|
33
|
-
[service-summary.md](../../../../../.claude/skills/components/service-summary.md)-style nesting
|
|
34
|
-
notes in [page-row.md](../../../../../.claude/skills/components/page-row.md) for how nested page-rows
|
|
35
|
-
compose.
|
|
36
|
-
|
|
37
|
-
---
|
|
38
6
|
|
|
39
7
|
## Public token API
|
|
40
8
|
|
|
@@ -44,12 +12,25 @@ compose.
|
|
|
44
12
|
|---|---|---|
|
|
45
13
|
| `--service-detail-hero-border-radius` | `0.8rem` | Corner rounding on the hero (clips the image/content layers — set to `0` for a true edge-to-edge bleed hero) |
|
|
46
14
|
| `--service-detail-hero-content-align` | `end` | `align-content` on `.service-detail__hero-content` (the breadcrumb/title/pills wrapper) — where that content sits within the hero's block axis (`end` = bottom-anchored) |
|
|
47
|
-
| `--service-detail-hero-min-height-mobile` | `32rem` |
|
|
48
|
-
| `--service-detail-hero-min-height-tablet` | `36rem` |
|
|
49
|
-
| `--service-detail-hero-min-height-desktop` | `42rem` |
|
|
15
|
+
| `--service-detail-hero-min-height-mobile` | `32rem` | Minimum hero banner height below the `768px` container breakpoint. The hero grows past it when its content (a long title, say) needs more room; the image always fills the hero (`object-fit`/`object-position` control cropping) |
|
|
16
|
+
| `--service-detail-hero-min-height-tablet` | `36rem` | Minimum hero banner height from the `768px` container breakpoint |
|
|
17
|
+
| `--service-detail-hero-min-height-desktop` | `42rem` | Minimum hero banner height from the `1024px` container breakpoint |
|
|
18
|
+
|
|
19
|
+
> Changed 2026-10-06: these were applied as a fixed `height`, so a long title was clipped by the
|
|
20
|
+
> hero. They are now a true minimum (`min-block-size`), matching their names.
|
|
21
|
+
>
|
|
22
|
+
> Also 2026-10-06: line-clamp tokens for the hero eyebrow and title, sidebar labels and values,
|
|
23
|
+
> and the final CTA heading and body. The hero title's and final CTA heading's spacing moved from
|
|
24
|
+
> utility classes (`mb-20`, `mbs-0`/`mbe-8`) into the component CSS, same values, so the clamp can
|
|
25
|
+
> keep descenders and italic overhang visible.
|
|
50
26
|
| `--service-detail-hero-padding` | `3.2rem` | **Block-axis (vertical)** padding around the breadcrumb/title/pills content — horizontal alignment comes from the nested content `PageRow`, not this token |
|
|
51
27
|
| `--service-detail-hero-scrim` | `linear-gradient(0deg, rgb(0 0 0 / 70%) 0%, rgb(0 0 0 / 10%) 60%, transparent 100%)` | Overlay gradient behind the hero text, for legibility over the image |
|
|
52
28
|
| `--service-detail-hero-text-colour` | `white` | Colour of the breadcrumb, eyebrow, and title over the hero image |
|
|
29
|
+
| `--service-detail-hero-eyebrow-line-clamp` | `none` | Max lines of the hero eyebrow (subtitle), ellipsis on the last. `1` is single-line ellipsis |
|
|
30
|
+
| `--service-detail-hero-title-line-clamp` | `none` | Max lines of the hero title |
|
|
31
|
+
|
|
32
|
+
For the breadcrumb, set Breadcrumb's own `--breadcrumb-item-line-clamp` (it reaches the hero
|
|
33
|
+
breadcrumb from any ancestor).
|
|
53
34
|
| `--service-detail-breadcrumb-margin-block-end` | `0.8rem` | Space below the breadcrumb |
|
|
54
35
|
| `--service-detail-hero-pills-gap` | `0.8rem` | Gap between the duration and price pills |
|
|
55
36
|
| `--service-detail-hero-pill-bg` | `transparent` | Background of the price/duration pills (via `DisplayPill`'s `--display-pill-background`) |
|
|
@@ -100,8 +81,8 @@ target `--stepper-list-*` directly under `.service-detail__process` — see
|
|
|
100
81
|
| `--service-detail-ideal-for-gap` | `1.2rem` | Gap between ideal-for cards |
|
|
101
82
|
| `--service-detail-ideal-for-item-gap` | `1rem` | Gap between icon and text within a card |
|
|
102
83
|
| `--service-detail-ideal-for-item-padding` | `1.6rem` | Padding inside a card |
|
|
103
|
-
| `--service-detail-ideal-for-item-background` | `var(--
|
|
104
|
-
| `--service-detail-ideal-for-item-border-colour` | `
|
|
84
|
+
| `--service-detail-ideal-for-item-background` | `var(--theme-surface-subtle)` | Card background colour |
|
|
85
|
+
| `--service-detail-ideal-for-item-border-colour` | `transparent` | Card border colour |
|
|
105
86
|
| `--service-detail-ideal-for-item-border-radius` | `0.4rem` | Card corner rounding |
|
|
106
87
|
| `--service-detail-ideal-for-icon-size` | `1.6rem` | Icon size |
|
|
107
88
|
| `--service-detail-ideal-for-icon-colour` | `var(--colour-text-accent)` | Icon colour |
|
|
@@ -129,17 +110,28 @@ The ideal-for grid becomes 2 columns at a **500px container width**.
|
|
|
129
110
|
| `--service-detail-sidebar-row-book-cta-padding-block` | `2rem` | Vertical padding on the `book-cta` slot's own row (only renders when the slot has content) |
|
|
130
111
|
| `--service-detail-sidebar-row-book-cta-justify-content` | `end` | Horizontal alignment of the `book-cta` slot content within its row |
|
|
131
112
|
| `--service-detail-sidebar-row-label-font-size` | `1.2rem` | Font size of a row's label (e.g. "Price") |
|
|
113
|
+
| `--service-detail-sidebar-label-line-clamp` | `none` | Max lines of the "Book This Service" / "You May Also Like" labels |
|
|
114
|
+
| `--service-detail-sidebar-value-line-clamp` | `none` | Max lines of a row's value (price, duration, location) |
|
|
115
|
+
|
|
116
|
+
A row's label takes the width of its own words (up to half the row) and wraps between words only;
|
|
117
|
+
the value takes the rest and wraps anywhere.
|
|
132
118
|
| `--service-detail-sidebar-note-font-size` | `1.2rem` | Font size of the `sidebar-note` slot content |
|
|
133
119
|
| `--service-detail-sidebar-note-margin-block-start` | `1.2rem` | Space above the `sidebar-note` slot content |
|
|
134
120
|
| `--service-detail-related-items-gap` | `1rem` | Gap between related-service item cards |
|
|
135
|
-
| `--service-detail-related-item-background` | `var(--
|
|
121
|
+
| `--service-detail-related-item-background` | `var(--theme-surface-subtle)` | Background colour of a related-service item's card |
|
|
136
122
|
| `--service-detail-related-item-padding` | `1rem` | Padding inside a related-service item's card |
|
|
137
|
-
| `--service-detail-related-item-border-colour` | `
|
|
123
|
+
| `--service-detail-related-item-border-colour` | `transparent` | Border colour of a related-service item's card |
|
|
138
124
|
| `--service-detail-related-item-border-radius` | `0.4rem` | Corner rounding of a related-service item's card |
|
|
139
125
|
| `--service-detail-related-item-gap` | `1.2rem` | Gap between a related item's thumbnail and its text |
|
|
140
126
|
| `--service-detail-related-image-size` | `5.6rem` | Width/height of a related-service thumbnail |
|
|
141
127
|
| `--service-detail-related-image-border-radius` | `0.4rem` | Corner rounding of a related-service thumbnail |
|
|
142
128
|
| `--service-detail-related-price-font-size` | `1.2rem` | Font size of a related item's price |
|
|
129
|
+
| `--service-detail-related-title-line-clamp` | `none` | Max lines of a related item's title (default item only, not the `related-service` slot). `1` is single-line ellipsis |
|
|
130
|
+
|
|
131
|
+
> Changed 2026-10-06: the ideal-for and related-service cards defaulted to dark `--slate-09` with a
|
|
132
|
+
> `--slate-06` border but set no text colour, so on a light page their inherited dark text was
|
|
133
|
+
> unreadable. They now default to `var(--theme-surface-subtle)` with a transparent border. To keep
|
|
134
|
+
> dark cards, set the four `-background`/`-border-colour` tokens.
|
|
143
135
|
| `--service-detail-related-price-margin-block-start` | `0.6rem` | Space between a related item's title and its price |
|
|
144
136
|
| `--service-detail-muted-opacity` | `0.7` | Opacity shared by every secondary/muted text element (sidebar label, sidebar note, related price, final CTA body) |
|
|
145
137
|
|
|
@@ -153,6 +145,11 @@ The booking card is a `GlassPanel` — its own `--glass-panel-*` tokens also app
|
|
|
153
145
|
| `--service-detail-final-cta-margin-block-start` | `3.2rem` | Space above the banner |
|
|
154
146
|
| `--service-detail-final-cta-padding-block` | `3.2rem 3.2rem` | Padding (block-start block-end) around the banner content, below its divider |
|
|
155
147
|
| `--service-detail-final-cta-divider-colour` | `currentColor` | Divider line above the banner |
|
|
148
|
+
| `--service-detail-final-cta-heading-line-clamp` | `none` | Max lines of the banner heading |
|
|
149
|
+
| `--service-detail-final-cta-body-line-clamp` | `none` | Max lines of the banner body text |
|
|
150
|
+
|
|
151
|
+
The `final-cta` slot sits in `.service-detail__final-cta-action`, which never shrinks: long copy
|
|
152
|
+
wraps beside it instead of pushing the button out of the banner.
|
|
156
153
|
|
|
157
154
|
The banner becomes a single row (copy left, CTA right) at a **700px container width**. Its own
|
|
158
155
|
breakout width is controlled by the `finalCtaVariant` prop (default `"content"`), not a token —
|
|
@@ -160,6 +157,47 @@ see Layout building blocks above.
|
|
|
160
157
|
|
|
161
158
|
---
|
|
162
159
|
|
|
160
|
+
## State hooks
|
|
161
|
+
|
|
162
|
+
No state classes. Inner classes are all `service-detail__*` (hero, hero image/overlay/content, breadcrumb,
|
|
163
|
+
hero pills, body, main, process, ideal-for and its items/icon, faqs and each faq/question, sidebar,
|
|
164
|
+
booking card, sidebar label/rows/note, related items, final CTA). FAQ questions render one heading level
|
|
165
|
+
below `subheadingTag` (`h3` for the default `h2`, `h4` for `h3`).
|
|
166
|
+
|
|
167
|
+
---
|
|
168
|
+
|
|
169
|
+
## Layout building blocks
|
|
170
|
+
|
|
171
|
+
The hero banner is a [GridStack](../../../../../.claude/skills/components/grid-stack.md) with two
|
|
172
|
+
stacked layers — an `image` layer (the service photo) behind a `content` layer (the scrim +
|
|
173
|
+
breadcrumb/title/pills). Each layer's root element is itself a
|
|
174
|
+
[PageRow](../../../../../.claude/skills/components/page-row.md): `.service-detail__hero-image-row`
|
|
175
|
+
(prop `heroImageVariant`, default `"full"`) wraps the photo, and `.service-detail__hero-overlay`
|
|
176
|
+
(prop `heroContentVariant`, default `"content"`) *is* the scrim/alignment element, not a wrapper
|
|
177
|
+
around one — a `PageRow`'s own box always spans the full width of its parent regardless of variant,
|
|
178
|
+
only its children default into the variant's grid column, so the one element can carry the
|
|
179
|
+
full-bleed scrim while its child (`.service-detail__hero-content`) still gets pulled to the
|
|
180
|
+
configured column. This means the photo bleeds to whatever width `.service-detail` itself is allowed
|
|
181
|
+
(edge-to-edge if `ServiceDetail` is placed without a content-constraining wrapper around it), while
|
|
182
|
+
the breadcrumb/title/pills always stay pulled into the page's normal content-width column,
|
|
183
|
+
regardless of how wide the photo bleeds. `--service-detail-hero-padding` now only controls
|
|
184
|
+
**block** (vertical) padding on the scrim — horizontal alignment of the text is handled entirely by
|
|
185
|
+
`.service-detail__hero-overlay`'s own `PageRow` grid, not by padding, so it can't fight the page's
|
|
186
|
+
content-grid gutters.
|
|
187
|
+
|
|
188
|
+
The two-column body (main content + sticky sidebar) is wrapped in its own `PageRow`, whose variant
|
|
189
|
+
is a prop: `bodyVariant` (`"full" | "popout" | "content" | "inset-content"`, default `"content"`).
|
|
190
|
+
The closing full-width CTA banner is wrapped the same way via `finalCtaVariant` (same type, same
|
|
191
|
+
`"content"` default). This means a consumer no longer needs to wrap `ServiceDetail` itself in an
|
|
192
|
+
outer `PageRow` purely to constrain either section's width — pass `body-variant`/`final-cta-variant`
|
|
193
|
+
directly. It's still fine (and necessary for a genuine full-bleed hero) to place `ServiceDetail`
|
|
194
|
+
inside an outer, wider `PageRow` — see
|
|
195
|
+
[service-summary.md](../../../../../.claude/skills/components/service-summary.md)-style nesting
|
|
196
|
+
notes in [page-row.md](../../../../../.claude/skills/components/page-row.md) for how nested page-rows
|
|
197
|
+
compose.
|
|
198
|
+
|
|
199
|
+
---
|
|
200
|
+
|
|
163
201
|
## Text content — props, not CSS
|
|
164
202
|
|
|
165
203
|
Section headings, sidebar labels, and CTA copy are **props**, not hardcoded strings:
|
|
@@ -177,6 +215,14 @@ Section headings, sidebar labels, and CTA copy are **props**, not hardcoded stri
|
|
|
177
215
|
| `relatedServicesHeading` | `"You May Also Like"` |
|
|
178
216
|
| `finalCtaHeading` | `"Ready to book your appointment?"` |
|
|
179
217
|
| `finalCtaBody` | `"Get in touch to book your appointment."` |
|
|
218
|
+
| `pricePrefix` | `"From"` (hero price pill; `""` shows the price alone) |
|
|
219
|
+
| `breadcrumbAriaLabel` | Breadcrumb's own default |
|
|
220
|
+
| `idealForIcon` | `"mdi:diamond-stone"` (decorative, hidden from assistive tech) |
|
|
221
|
+
|
|
222
|
+
A section is left out, heading and all, when its data is empty (`process`, `idealFor`, `faqs`,
|
|
223
|
+
`maintenance`, a `heroHeading` with no text), and so is any heading or label prop set to `""`. Empty
|
|
224
|
+
`subtitle`, `title`, `price`, `duration`, `longDescription`, `whatIsIt` and FAQ questions/answers render
|
|
225
|
+
nothing, and empty breadcrumb labels are dropped.
|
|
180
226
|
|
|
181
227
|
```vue
|
|
182
228
|
<ServiceDetail
|
|
@@ -226,6 +272,12 @@ never reach the hero pills or breadcrumb. Use the `--service-detail-hero-*` toke
|
|
|
226
272
|
|
|
227
273
|
---
|
|
228
274
|
|
|
275
|
+
## Class passthrough
|
|
276
|
+
|
|
277
|
+
`style-class-passthrough` adds classes to the root `.service-detail`. Reactive after mount.
|
|
278
|
+
|
|
279
|
+
---
|
|
280
|
+
|
|
229
281
|
## Notes
|
|
230
282
|
|
|
231
283
|
- Routing is entirely delegated to the consumer via slots: `book-cta` (scoped `serviceData`),
|
|
@@ -251,4 +303,3 @@ never reach the hero pills or breadcrumb. Use the `--service-detail-hero-*` toke
|
|
|
251
303
|
`@container` queries against the component's own width, not the viewport — they are not tokens
|
|
252
304
|
since resizing them would need corresponding `minmax()`/`grid-template-columns` changes too, not
|
|
253
305
|
just spacing (same rationale as `ServiceSummary`'s `--service-summary-grid-gap-desktop`).
|
|
254
|
-
|