srcdev-nuxt-components 9.0.15 → 9.0.17
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/settings.json +25 -0
- package/.claude/skills/component-aria-landmark.md +68 -0
- package/.claude/skills/component-dynamic-slots.md +150 -0
- package/.claude/skills/component-export-types.md +61 -0
- package/.claude/skills/component-local-style-override.md +126 -0
- package/.claude/skills/component-prop-driven-container-layout.md +42 -0
- package/.claude/skills/components/accordian-core.md +159 -0
- package/.claude/skills/components/contact-section.md +101 -0
- package/.claude/skills/components/expanding-panel.md +156 -0
- package/.claude/skills/components/eyebrow-text.md +25 -0
- package/.claude/skills/components/hero-text.md +25 -0
- package/.claude/skills/components/layout-grid-by-cols.md +147 -0
- package/.claude/skills/components/layout-row.md +35 -0
- package/.claude/skills/components/link-text.md +33 -0
- package/.claude/skills/components/page-hero-highlights.md +224 -0
- package/.claude/skills/components/services-card.md +28 -0
- package/.claude/skills/components/services-section.md +25 -0
- package/.claude/skills/components/stepper-list.md +227 -0
- package/.claude/skills/css-grid-max-width-gutters.md +67 -0
- package/.claude/skills/index.md +15 -3
- package/.claude/skills/storybook-add-story.md +60 -0
- package/.claude/skills/testing-add-unit-test.md +56 -0
- package/app/assets/styles/setup/01.config/index.css +0 -1
- package/app/assets/styles/setup/03.theming/default/_dark.css +2 -2
- package/app/assets/styles/setup/04.elements/forms/02.typography.css +1 -0
- package/app/assets/styles/setup/05.typography/02.utility-classes/_font-classes-page-link.css +14 -14
- package/app/assets/styles/setup/index.css +0 -1
- package/app/components/01.atoms/card/CardCore.vue +92 -0
- package/app/components/01.atoms/card/stories/CardCore.stories.ts +132 -0
- package/app/components/01.atoms/card/tests/CardCore.spec.ts +207 -0
- package/app/components/01.atoms/card/tests/__snapshots__/CardCore.spec.ts.snap +43 -0
- package/app/components/01.atoms/content-wrappers/content-columns-2/ContentColumns2.vue +51 -0
- package/app/components/01.atoms/content-wrappers/content-columns-2/stories/ContentColumns2.stories.ts +110 -0
- package/app/components/01.atoms/content-wrappers/content-columns-2/tests/ContentColumns2.spec.ts +105 -0
- package/app/components/01.atoms/content-wrappers/content-columns-2/tests/__snapshots__/ContentColumns2.spec.ts.snap +14 -0
- package/app/components/01.atoms/content-wrappers/content-width/ContentWidth.vue +88 -0
- package/app/components/01.atoms/content-wrappers/content-width/stories/ContentWidth.stories.ts +362 -0
- package/app/components/01.atoms/content-wrappers/content-width/tests/ContentWidth.spec.ts +132 -0
- package/app/components/01.atoms/content-wrappers/layout-grid/layout-grid-by-cols/LayoutGridByCols.vue +71 -0
- package/app/components/01.atoms/content-wrappers/layout-grid/layout-grid-by-cols/stories/LayoutGridByCols.stories.ts +219 -0
- package/app/components/01.atoms/content-wrappers/layout-grid/layout-grid-by-cols/tests/LayoutGridByCols.spec.ts +174 -0
- package/app/components/01.atoms/content-wrappers/layout-grid/layout-grid-by-cols/tests/__snapshots__/LayoutGrid.spec.ts.snap +36 -0
- package/app/components/01.atoms/content-wrappers/layout-grid/layout-grid-by-cols/tests/__snapshots__/LayoutGridByCols.spec.ts.snap +36 -0
- package/app/components/01.atoms/content-wrappers/layout-grid/layout-grid-by-width/LayoutGridByWidth.vue +70 -0
- package/app/components/01.atoms/content-wrappers/layout-grid/layout-grid-by-width/stories/LayoutGridByWidth.stories.ts +220 -0
- package/app/components/01.atoms/content-wrappers/layout-grid/layout-grid-by-width/tests/LayoutGridByWidth.spec.ts +174 -0
- package/app/components/01.atoms/content-wrappers/layout-grid/layout-grid-by-width/tests/__snapshots__/LayoutGrid.spec.ts.snap +36 -0
- package/app/components/01.atoms/content-wrappers/layout-grid/layout-grid-by-width/tests/__snapshots__/LayoutGridByCols.spec.ts.snap +36 -0
- package/app/components/01.atoms/content-wrappers/layout-grid/layout-grid-by-width/tests/__snapshots__/LayoutGridByWidth.spec.ts.snap +36 -0
- package/app/components/01.atoms/text-blocks/eyebrow-text/stories/EyebrowText.stories.ts +1 -1
- package/app/components/01.atoms/text-blocks/hero-text/stories/HeroText.stories.ts +1 -1
- package/app/components/01.atoms/text-blocks/link-text/stories/LinkText.stories.ts +1 -1
- package/app/components/02.molecules/contact-section/stories/ContactSection.stories.ts +5 -0
- package/app/components/02.molecules/contact-section/tests/ContactSection.spec.ts +15 -0
- package/app/components/02.molecules/contact-section/tests/ContactSection.vue +25 -17
- package/app/components/{accordian → 02.molecules/expandable/accordian}/stories/AccordianCore.stories.ts +1 -1
- package/app/components/02.molecules/expandable/expanding-panel/stories/ExpandingPanel.stories.ts +245 -0
- package/app/components/02.molecules/expandable/expanding-panel/tests/ExpandingPanel.spec.ts +351 -0
- package/app/components/02.molecules/expandable/expanding-panel/tests/__snapshots__/ExpandingPanel.spec.ts.snap +38 -0
- package/app/components/02.molecules/navigation/navigation-horizontal/NavigationHorizontal.vue +162 -0
- package/app/components/02.molecules/navigation/navigation-horizontal/stories/NavigationHorizontal.stories.ts +373 -0
- package/app/components/02.molecules/navigation/navigation-horizontal/tests/NavigationHorizontal.spec.ts +152 -0
- package/app/components/02.molecules/navigation/navigation-horizontal/tests/__snapshots__/NavigationHorizontal.spec.ts.snap +17 -0
- package/app/components/02.molecules/profile-section/ProfileSection.vue +2 -3
- package/app/components/02.molecules/profile-section/tests/ProfileSection.spec.ts +2 -2
- package/app/components/02.molecules/stepper-list/StepperList.vue +131 -92
- package/app/components/02.molecules/stepper-list/stories/StepperList.stories.ts +31 -0
- package/app/components/02.molecules/stepper-list/tests/StepperList.spec.ts +24 -0
- package/app/components/02.molecules/stepper-list/tests/__snapshots__/StepperList.spec.ts.snap +22 -9
- package/app/components/03.organisms/image-galleries/slider-gallery/SliderGallery.vue +782 -0
- package/app/components/03.organisms/image-galleries/slider-gallery/stories/SliderGallery.stories.ts +233 -0
- package/app/components/03.organisms/image-galleries/slider-gallery/tests/SliderGallery.spec.ts +226 -0
- package/app/components/03.organisms/image-galleries/slider-gallery/tests/__snapshots__/SliderGallery.spec.ts.snap +69 -0
- package/app/components/03.organisms/services/services-grids/ServicesCardGrid.vue +1 -1
- package/app/components/03.organisms/services/services-grids/ServicesSectionGrid.vue +1 -1
- package/app/components/03.organisms/services/services-section/ServicesSection.vue +2 -3
- package/app/components/04.templates/page-hero-highlights/PageHeroHighlights.vue +239 -0
- package/app/components/04.templates/page-hero-highlights/stories/PageHeroHighlights.stories.ts +404 -0
- package/app/components/04.templates/page-hero-highlights/tests/PageHeroHighlights.spec.ts +198 -0
- package/app/components/04.templates/page-hero-highlights/tests/__snapshots__/PageHeroHighlights.spec.ts.snap +19 -0
- package/app/components/container-glow/ContainerGlowCore.vue +20 -27
- package/app/components/forms/input-button/InputButtonCore.vue +105 -104
- package/app/components/glowing-border/stories/GlowingBorder.stories.ts +21 -21
- package/app/composables/useAriaLabelledById.ts +13 -0
- package/app/layouts/default.vue +8 -3
- package/app/pages/forms/examples/buttons/index.vue +6 -6
- package/app/pages/forms/examples/material/checkbox-radio-panels.vue +3 -3
- package/app/pages/forms/examples/material/text-fields.vue +607 -610
- package/app/pages/page-hero-highlights.vue +81 -0
- package/app/pages/ui/{display-card.vue → card-core.vue} +15 -15
- package/app/pages/ui/contact-section.vue +1 -1
- package/app/pages/ui/container-glow.vue +1 -1
- package/app/pages/ui/content-width.vue +126 -0
- package/app/pages/ui/glowing-border.vue +9 -9
- package/app/pages/ui/navigation/navigation-horizontal.vue +484 -0
- package/app/pages/ui/services/services-section/[slug].vue +3 -1
- package/app/types/components/index.ts +1 -0
- package/app/types/components/navigation-horizontal.d.ts +11 -0
- package/package.json +2 -2
- package/app/assets/styles/setup/01.config/_basic-resets.css +0 -9
- package/app/components/content-columns/TwoColumns.vue +0 -59
- package/app/components/content-columns/stories/TwoColumns.stories.ts +0 -561
- package/app/components/content-containers/ContentContainer.vue +0 -89
- package/app/components/content-containers/stories/ContentContainer.stories.ts +0 -465
- package/app/components/content-grid/ContentGrid.vue +0 -85
- package/app/components/display-card/DisplayCard.vue +0 -122
- package/app/components/image-galleries/SliderGallery.vue +0 -786
- package/app/pages/ui/content-container.vue +0 -112
- /package/app/components/{accordian → 02.molecules/expandable/accordian}/AccordianCore.vue +0 -0
- /package/app/components/{accordian → 02.molecules/expandable/accordian}/tests/AccordianCore.spec.ts +0 -0
- /package/app/components/{accordian → 02.molecules/expandable/accordian}/tests/__snapshots__/AccordianCore.spec.ts.snap +0 -0
- /package/app/components/{expanding-panel → 02.molecules/expandable/expanding-panel}/ExpandingPanel.vue +0 -0
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# ContactSection Component
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
`ContactSection` is a two-column molecule that pairs a 3-item info list (using `StepperList` internally) with a contact form slot. On narrow viewports the columns stack; at 768 px and above they sit side by side.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Props reference
|
|
10
|
+
|
|
11
|
+
| Prop | Type | Default | Notes |
|
|
12
|
+
|------|------|---------|-------|
|
|
13
|
+
| `tag` | `"div" \| "section" \| "article" \| "main"` | `"div"` | Root HTML element. Use `"section"` when the landmark is meaningful. |
|
|
14
|
+
| `stepperIndicatorSize` | `string` | `"3rem"` | Passed through to the internal `StepperList` `indicatorSize` prop. Any valid CSS length. |
|
|
15
|
+
| `styleClassPassthrough` | `string \| string[]` | `[]` | Extra CSS classes applied to the root element. |
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Slot API
|
|
20
|
+
|
|
21
|
+
The component exposes 7 slots — 3 info-item slots, 3 indicator slots, and a form slot.
|
|
22
|
+
|
|
23
|
+
| Slot | Purpose |
|
|
24
|
+
|------|---------|
|
|
25
|
+
| `#item-0` | Content of the first info item |
|
|
26
|
+
| `#item-1` | Content of the second info item |
|
|
27
|
+
| `#item-2` | Content of the third info item |
|
|
28
|
+
| `#indicator-0` | Custom indicator icon for item 0 (optional — CSS counter bubble shown if omitted) |
|
|
29
|
+
| `#indicator-1` | Custom indicator icon for item 1 (optional) |
|
|
30
|
+
| `#indicator-2` | Custom indicator icon for item 2 (optional) |
|
|
31
|
+
| `#form` | Contact form or any right-column content |
|
|
32
|
+
|
|
33
|
+
Slots are **zero-indexed**. The internal `StepperList` is always rendered with `itemCount="3"` and `:connected="false"`.
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## Usage examples
|
|
38
|
+
|
|
39
|
+
### Default (no slots)
|
|
40
|
+
|
|
41
|
+
```vue
|
|
42
|
+
<ContactSection />
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Renders three placeholder `<p>` tags and an empty form column.
|
|
46
|
+
|
|
47
|
+
### With info content and a form
|
|
48
|
+
|
|
49
|
+
```vue
|
|
50
|
+
<ContactSection tag="section" :stepper-indicator-size="'2.4rem'">
|
|
51
|
+
<template #item-0>
|
|
52
|
+
<div>
|
|
53
|
+
<strong>Get in touch</strong>
|
|
54
|
+
<p class="page-body-normal">We'd love to hear from you.</p>
|
|
55
|
+
</div>
|
|
56
|
+
</template>
|
|
57
|
+
<template #item-1>
|
|
58
|
+
<div>
|
|
59
|
+
<strong>Email us</strong>
|
|
60
|
+
<p class="page-body-normal"><a href="mailto:hello@example.com">hello@example.com</a></p>
|
|
61
|
+
</div>
|
|
62
|
+
</template>
|
|
63
|
+
<template #item-2>
|
|
64
|
+
<div>
|
|
65
|
+
<strong>Call us</strong>
|
|
66
|
+
<p class="page-body-normal"><a href="tel:+441234567890">+44 1234 567 890</a></p>
|
|
67
|
+
</div>
|
|
68
|
+
</template>
|
|
69
|
+
<template #form>
|
|
70
|
+
<form>...</form>
|
|
71
|
+
</template>
|
|
72
|
+
</ContactSection>
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### With custom indicator icons
|
|
76
|
+
|
|
77
|
+
```vue
|
|
78
|
+
<ContactSection>
|
|
79
|
+
<template #indicator-0>
|
|
80
|
+
<Icon name="lucide-map-pin" class="indicator-icon" />
|
|
81
|
+
</template>
|
|
82
|
+
<template #item-0>
|
|
83
|
+
<div>
|
|
84
|
+
<strong>Location</strong>
|
|
85
|
+
<p class="page-body-normal">123 High Street, Bath, BA1 1AA</p>
|
|
86
|
+
</div>
|
|
87
|
+
</template>
|
|
88
|
+
<!-- repeat for indicator-1/item-1, indicator-2/item-2 -->
|
|
89
|
+
</ContactSection>
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Custom indicator content should use the `indicator-icon` class so the icon inherits the correct size and colour from `StepperList`.
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## Notes
|
|
97
|
+
|
|
98
|
+
- `stepperIndicatorSize` passes directly to the internal `StepperList` `indicatorSize` — use it to scale the indicator bubble/icon area. Default `"3rem"` is 30 px at the project's `62.5%` rem base.
|
|
99
|
+
- The internal `StepperList` always uses `tag="ul"`, `:connected="false"`, and `indicator-alignment="top"`. These are not configurable from `ContactSection`.
|
|
100
|
+
- Auto-imported in Nuxt — no manual import needed.
|
|
101
|
+
- See [stepper-list.md](stepper-list.md) for `StepperList` prop details and CSS custom property theming.
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
# ExpandingPanel Component
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
`ExpandingPanel` is a single expand/collapse panel built on the native `<details>`/`<summary>` element. It animates open/close via a CSS grid-template-rows trick, supports `v-model` for controlled state, and can be locked open with `forceOpened`. Multiple panels can be grouped into a native accordion by sharing the same `name` prop (see `AccordianCore`).
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Props reference
|
|
10
|
+
|
|
11
|
+
| Prop | Type | Default | Notes |
|
|
12
|
+
|------|------|---------|-------|
|
|
13
|
+
| `name` | `string` | `useId()` | Identifies the panel. Used in ARIA attributes (`id-{name}-trigger`, `id-{name}-content`). If omitted, a unique id is generated automatically. |
|
|
14
|
+
| `animationDuration` | `number` | `400` | Expand/collapse transition duration in milliseconds. Pass `0` to disable animation. |
|
|
15
|
+
| `forceOpened` | `boolean` | `false` | When `true`, the panel is always open. The toggle icon is hidden and clicks do not close the panel. |
|
|
16
|
+
| `styleClassPassthrough` | `string \| string[]` | `[]` | Extra CSS classes applied to the root `.expanding-panel` element. |
|
|
17
|
+
|
|
18
|
+
## Model
|
|
19
|
+
|
|
20
|
+
| Model | Type | Default | Notes |
|
|
21
|
+
|-------|------|---------|-------|
|
|
22
|
+
| `v-model` | `boolean` | `false` | Controls open/closed state. Bind to a `ref<boolean>` to manage state externally. |
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## Slots
|
|
27
|
+
|
|
28
|
+
| Slot | Purpose |
|
|
29
|
+
|------|---------|
|
|
30
|
+
| `#summary` | Content rendered inside the clickable `<summary>` row (label area). |
|
|
31
|
+
| `#icon` | Custom toggle icon. Defaults to a `bi:caret-down-fill` icon that flips on open. Hidden when `forceOpened` is `true`. |
|
|
32
|
+
| `#content` | Content revealed when the panel is open. Can contain any markup. |
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## Usage examples
|
|
37
|
+
|
|
38
|
+
### Basic uncontrolled panel
|
|
39
|
+
|
|
40
|
+
```vue
|
|
41
|
+
<ExpandingPanel name="delivery">
|
|
42
|
+
<template #summary>
|
|
43
|
+
<span>Delivery & Returns</span>
|
|
44
|
+
</template>
|
|
45
|
+
<template #content>
|
|
46
|
+
<p>Free standard delivery on orders over £50.</p>
|
|
47
|
+
</template>
|
|
48
|
+
</ExpandingPanel>
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Controlled via v-model
|
|
52
|
+
|
|
53
|
+
```vue
|
|
54
|
+
<script setup lang="ts">
|
|
55
|
+
const isOpen = ref(false);
|
|
56
|
+
</script>
|
|
57
|
+
|
|
58
|
+
<template>
|
|
59
|
+
<ExpandingPanel name="faq-1" v-model="isOpen">
|
|
60
|
+
<template #summary><span>What is your returns policy?</span></template>
|
|
61
|
+
<template #content><p>You can return any item within 30 days.</p></template>
|
|
62
|
+
</ExpandingPanel>
|
|
63
|
+
<button @click="isOpen = !isOpen">Toggle externally</button>
|
|
64
|
+
</template>
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Force opened (always visible, no toggle)
|
|
68
|
+
|
|
69
|
+
```vue
|
|
70
|
+
<ExpandingPanel name="notice" :force-opened="true">
|
|
71
|
+
<template #summary><strong>Important notice</strong></template>
|
|
72
|
+
<template #content>
|
|
73
|
+
<p>This panel cannot be collapsed.</p>
|
|
74
|
+
</template>
|
|
75
|
+
</ExpandingPanel>
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Custom icon
|
|
79
|
+
|
|
80
|
+
```vue
|
|
81
|
+
<ExpandingPanel name="custom-icon">
|
|
82
|
+
<template #summary><span>Section title</span></template>
|
|
83
|
+
<template #icon>
|
|
84
|
+
<svg width="12" height="12" viewBox="0 0 12 12">
|
|
85
|
+
<path d="M6 9L1 3h10z" fill="currentColor" />
|
|
86
|
+
</svg>
|
|
87
|
+
</template>
|
|
88
|
+
<template #content><p>Content here.</p></template>
|
|
89
|
+
</ExpandingPanel>
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Slow animation
|
|
93
|
+
|
|
94
|
+
```vue
|
|
95
|
+
<ExpandingPanel name="slow" :animation-duration="800">
|
|
96
|
+
<template #summary><span>Slow panel</span></template>
|
|
97
|
+
<template #content><p>Opens and closes over 800 ms.</p></template>
|
|
98
|
+
</ExpandingPanel>
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
## ARIA / accessibility
|
|
104
|
+
|
|
105
|
+
The component wires ARIA automatically from the `name` prop:
|
|
106
|
+
|
|
107
|
+
| Element | Attribute | Value |
|
|
108
|
+
|---------|-----------|-------|
|
|
109
|
+
| `<summary>` | `id` | `id-{name}-trigger` |
|
|
110
|
+
| `<summary>` | `aria-controls` | `id-{name}-content` |
|
|
111
|
+
| `<summary>` | `aria-expanded` | `true` / `false` |
|
|
112
|
+
| content div | `id` | `id-{name}-content` |
|
|
113
|
+
| content div | `aria-labelledby` | `id-{name}-trigger` |
|
|
114
|
+
| content div | `role` | `region` |
|
|
115
|
+
|
|
116
|
+
Always supply a meaningful `name` prop when using multiple panels on the same page to avoid duplicate IDs.
|
|
117
|
+
|
|
118
|
+
---
|
|
119
|
+
|
|
120
|
+
## Local style override scaffold
|
|
121
|
+
|
|
122
|
+
When consuming this component, scaffold a style block using `styleClassPassthrough`. Delete the block if unused.
|
|
123
|
+
|
|
124
|
+
See [component-local-style-override.md](../component-local-style-override.md) for the full pattern.
|
|
125
|
+
|
|
126
|
+
```vue
|
|
127
|
+
<ExpandingPanel name="my-item" :style-class-passthrough="['my-panel']">
|
|
128
|
+
...
|
|
129
|
+
</ExpandingPanel>
|
|
130
|
+
|
|
131
|
+
<style>
|
|
132
|
+
/* ─── ExpandingPanel local overrides ───────────────────────────────
|
|
133
|
+
Colours, borders, geometry only — do not override behaviour.
|
|
134
|
+
Delete this block if no overrides are needed.
|
|
135
|
+
─────────────────────────────────────────────────────────────────── */
|
|
136
|
+
.expanding-panel {
|
|
137
|
+
&.my-panel {
|
|
138
|
+
/* Border */
|
|
139
|
+
/* border-block-end: 1px solid currentColor; */
|
|
140
|
+
|
|
141
|
+
/* Summary row geometry */
|
|
142
|
+
/* .expanding-panel-details .expanding-panel-summary { padding-block: 1.2rem; } */
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
</style>
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
## Notes
|
|
151
|
+
|
|
152
|
+
- The open/close animation uses `grid-template-rows: 0fr → 1fr` — no JS height measurement needed.
|
|
153
|
+
- When `forceOpened` is `true`, `open` stays `true` regardless of `v-model`, but `v-model` still updates internally on clicks (useful if you later set `forceOpened` back to `false`).
|
|
154
|
+
- Group panels into a native accordion (only one open at a time) by passing the same `name` to multiple panels or use `AccordianCore` which handles this automatically.
|
|
155
|
+
- Auto-imported in Nuxt — no manual import needed.
|
|
156
|
+
- File: `app/components/02.molecules/expandable/expanding-panel/ExpandingPanel.vue`
|
|
@@ -77,6 +77,31 @@ Text is always `text-transform: uppercase` — do not pass pre-uppercased string
|
|
|
77
77
|
|
|
78
78
|
Override via `styleClassPassthrough` or a parent HOC `<style>` block targeting `.eyebrow-text`.
|
|
79
79
|
|
|
80
|
+
## Local style override scaffold
|
|
81
|
+
|
|
82
|
+
When consuming this component, scaffold a style block using `styleClassPassthrough`. Delete the block if unused.
|
|
83
|
+
|
|
84
|
+
See [component-local-style-override.md](../component-local-style-override.md) for the full pattern.
|
|
85
|
+
|
|
86
|
+
```vue
|
|
87
|
+
<EyebrowText :style-class-passthrough="['my-eyebrow']" text-content="Our Services" />
|
|
88
|
+
|
|
89
|
+
<style>
|
|
90
|
+
/* ─── EyebrowText local overrides ──────────────────────────────────
|
|
91
|
+
Colours, borders, geometry only — do not override behaviour.
|
|
92
|
+
Delete this block if no overrides are needed.
|
|
93
|
+
─────────────────────────────────────────────────────────────────── */
|
|
94
|
+
.eyebrow-text {
|
|
95
|
+
&.my-eyebrow {
|
|
96
|
+
/* Colours */
|
|
97
|
+
/* --colour-text-eyebrow: var(--brand-accent); */
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
</style>
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
> **Note:** Font size is controlled via the `fontSize` prop and theme tokens `--eyebrow-text-large/medium/small` — define these at theme level, not as local overrides.
|
|
104
|
+
|
|
80
105
|
## Notes
|
|
81
106
|
|
|
82
107
|
- Component is auto-imported in Nuxt — no import needed.
|
|
@@ -103,6 +103,31 @@ Key CSS custom properties:
|
|
|
103
103
|
- `--colour-text-accent` — colour applied to `.accent` spans and the icon
|
|
104
104
|
- `--hero-text-{scale}` — font size per scale value
|
|
105
105
|
|
|
106
|
+
## Local style override scaffold
|
|
107
|
+
|
|
108
|
+
When consuming this component, scaffold a style block using `styleClassPassthrough`. Delete the block if unused.
|
|
109
|
+
|
|
110
|
+
See [component-local-style-override.md](../component-local-style-override.md) for the full pattern.
|
|
111
|
+
|
|
112
|
+
```vue
|
|
113
|
+
<HeroText :style-class-passthrough="['my-hero']" tag="h1" :text-content="[...]" />
|
|
114
|
+
|
|
115
|
+
<style>
|
|
116
|
+
/* ─── HeroText local overrides ─────────────────────────────────────
|
|
117
|
+
Colours, borders, geometry only — do not override behaviour.
|
|
118
|
+
Delete this block if no overrides are needed.
|
|
119
|
+
─────────────────────────────────────────────────────────────────── */
|
|
120
|
+
.hero-text {
|
|
121
|
+
&.my-hero {
|
|
122
|
+
/* Colours */
|
|
123
|
+
/* --colour-text-accent: var(--brand-primary); */
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
</style>
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
> **Note:** Font size is controlled via the `fontSize` prop and theme tokens `--hero-text-display/title/heading/subheading/label` — define these at theme level, not as local overrides.
|
|
130
|
+
|
|
106
131
|
## Notes
|
|
107
132
|
|
|
108
133
|
- Text segments are trimmed and a trailing space is automatically appended between segments in horizontal axis — do not manually pad `text` values.
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
# LayoutGridByCols Component
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
`LayoutGridByCols` is a CSS grid layout wrapper that arranges content into a fixed number of equal-width columns. It uses **named dynamic slots** — the component renders whatever slots the consumer passes, in the order they appear. It collapses to a single column below a configurable breakpoint.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Slot pattern
|
|
10
|
+
|
|
11
|
+
Pass any number of slots with any names. The component renders each one in document order inside the grid.
|
|
12
|
+
|
|
13
|
+
```vue
|
|
14
|
+
<LayoutGridByCols :column-count="3">
|
|
15
|
+
<template #item-0><ServicesCard /></template>
|
|
16
|
+
<template #item-1><ServicesCard /></template>
|
|
17
|
+
<template #item-2><ServicesCard /></template>
|
|
18
|
+
</LayoutGridByCols>
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
When filling from a data array, use a dynamic slot name in a `v-for`:
|
|
22
|
+
|
|
23
|
+
```vue
|
|
24
|
+
<LayoutGridByCols :column-count="3">
|
|
25
|
+
<template v-for="(item, i) in items" #[`item-${i}`] :key="i">
|
|
26
|
+
<ServicesCard :data="item" />
|
|
27
|
+
</template>
|
|
28
|
+
</LayoutGridByCols>
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## Props reference
|
|
34
|
+
|
|
35
|
+
> **Hyphenation rule**: Vue's ESLint config enforces `vue/attribute-hyphenation`. Always write camelCase prop names hyphenated in templates: `:column-count`, `:single-col-below`, `:style-class-passthrough`.
|
|
36
|
+
|
|
37
|
+
| Prop (template form) | Type | Default | Notes |
|
|
38
|
+
|------|------|---------|-------|
|
|
39
|
+
| `:column-count` | `2 \| 3 \| 4 \| 5 \| 6` | `2` | Number of equal-width columns above `single-col-below`. Minimum is 2. |
|
|
40
|
+
| `:gap` | `string` | `"1rem"` | Any valid CSS length or shorthand. |
|
|
41
|
+
| `:single-col-below` | `string` | `"768px"` | Container width below which the grid collapses to a single column. |
|
|
42
|
+
| `tag` | `"div" \| "section"` | `"div"` | Use `"section"` for semantic page regions. |
|
|
43
|
+
| `label` | `string` | `""` | Required when `tag="section"` — rendered as a visually-hidden `<p>` linked via `aria-labelledby`. |
|
|
44
|
+
| `:style-class-passthrough` | `string \| string[]` | `[]` | Extra CSS classes on the root element. |
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## Usage examples
|
|
49
|
+
|
|
50
|
+
### Two-column grid (default)
|
|
51
|
+
|
|
52
|
+
```vue
|
|
53
|
+
<LayoutGridByCols>
|
|
54
|
+
<template #left>
|
|
55
|
+
<p>Left cell</p>
|
|
56
|
+
</template>
|
|
57
|
+
<template #right>
|
|
58
|
+
<p>Right cell</p>
|
|
59
|
+
</template>
|
|
60
|
+
</LayoutGridByCols>
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Three-column card grid
|
|
64
|
+
|
|
65
|
+
```vue
|
|
66
|
+
<LayoutGridByCols :column-count="3" gap="2rem">
|
|
67
|
+
<template #card-a><ServicesCard title="Card A" /></template>
|
|
68
|
+
<template #card-b><ServicesCard title="Card B" /></template>
|
|
69
|
+
<template #card-c><ServicesCard title="Card C" /></template>
|
|
70
|
+
</LayoutGridByCols>
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### Section with accessible label
|
|
74
|
+
|
|
75
|
+
```vue
|
|
76
|
+
<LayoutGridByCols tag="section" label="Our team" :column-count="4" gap="1.6rem">
|
|
77
|
+
<template #alice><TeamCard name="Alice" /></template>
|
|
78
|
+
<template #bob><TeamCard name="Bob" /></template>
|
|
79
|
+
<template #carol><TeamCard name="Carol" /></template>
|
|
80
|
+
<template #dan><TeamCard name="Dan" /></template>
|
|
81
|
+
</LayoutGridByCols>
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Data-driven with v-for
|
|
85
|
+
|
|
86
|
+
```vue
|
|
87
|
+
<LayoutGridByCols :column-count="3">
|
|
88
|
+
<template v-for="(item, i) in items" #[`item-${i}`] :key="i">
|
|
89
|
+
<Card :data="item" />
|
|
90
|
+
</template>
|
|
91
|
+
</LayoutGridByCols>
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## Accessibility
|
|
97
|
+
|
|
98
|
+
- When `tag="section"`, the root element automatically receives `aria-labelledby` pointing to a visually-hidden `<p>` with the `label` value.
|
|
99
|
+
- Always provide a meaningful `label` when using `tag="section"`.
|
|
100
|
+
- When `tag="div"`, no label or ARIA attributes are added.
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
## Responsive behaviour
|
|
105
|
+
|
|
106
|
+
Uses **CSS container queries** (`container-type: inline-size`) — responds to its own container width, not the viewport.
|
|
107
|
+
|
|
108
|
+
- **Below `singleColBelow`**: single-column stacked layout.
|
|
109
|
+
- **At or above `singleColBelow`**: `columnCount`-column grid.
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## Local style override scaffold
|
|
114
|
+
|
|
115
|
+
When consuming this component, scaffold a style block using `styleClassPassthrough`. Delete the block if unused.
|
|
116
|
+
|
|
117
|
+
See [component-local-style-override.md](../component-local-style-override.md) for the full pattern.
|
|
118
|
+
|
|
119
|
+
```vue
|
|
120
|
+
<LayoutGridByCols :style-class-passthrough="['my-grid']" :column-count="3">
|
|
121
|
+
...
|
|
122
|
+
</LayoutGridByCols>
|
|
123
|
+
|
|
124
|
+
<style>
|
|
125
|
+
/* ─── LayoutGridByCols local overrides ─────────────────────────────
|
|
126
|
+
Colours, borders, geometry only — do not override behaviour.
|
|
127
|
+
Delete this block if no overrides are needed.
|
|
128
|
+
─────────────────────────────────────────────────────────────────── */
|
|
129
|
+
.layout-grid-by-cols {
|
|
130
|
+
&.my-grid {
|
|
131
|
+
/* Colours */
|
|
132
|
+
/* background: var(--brand-surface); */
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
</style>
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
> **Note:** `gap` and `column-count` are prop-driven — use the props rather than CSS overrides for layout changes.
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
## Notes
|
|
143
|
+
|
|
144
|
+
- Auto-imported in Nuxt — no manual import needed.
|
|
145
|
+
- `column-count` is clamped to a minimum of 2 internally.
|
|
146
|
+
- `gap` accepts any CSS value including compound values like `"1rem 2rem"`.
|
|
147
|
+
- Slot names can be anything — semantic or indexed. Document order determines render order.
|
|
@@ -196,6 +196,41 @@ Override in consuming app to adjust all track sizes globally:
|
|
|
196
196
|
|
|
197
197
|
---
|
|
198
198
|
|
|
199
|
+
## Local style override scaffold
|
|
200
|
+
|
|
201
|
+
When consuming this component, scaffold a style block using `styleClassPassthrough`. Delete the block if unused.
|
|
202
|
+
|
|
203
|
+
See [component-local-style-override.md](../component-local-style-override.md) for the full pattern.
|
|
204
|
+
|
|
205
|
+
```vue
|
|
206
|
+
<LayoutRow variant="content" :style-class-passthrough="['my-row']">
|
|
207
|
+
...
|
|
208
|
+
</LayoutRow>
|
|
209
|
+
|
|
210
|
+
<style>
|
|
211
|
+
/* ─── LayoutRow local overrides ────────────────────────────────────
|
|
212
|
+
Colours, borders, geometry only — do not override behaviour.
|
|
213
|
+
Delete this block if no overrides are needed.
|
|
214
|
+
─────────────────────────────────────────────────────────────────── */
|
|
215
|
+
.layout-row {
|
|
216
|
+
&.my-row {
|
|
217
|
+
/* Track widths — for this instance only */
|
|
218
|
+
/* --content-max-width: 1200px; */
|
|
219
|
+
/* --popout-max-width: 1600px; */
|
|
220
|
+
/* --inset-content-max-width: 900px; */
|
|
221
|
+
/* --minimum-content-padding: 2rem; */
|
|
222
|
+
|
|
223
|
+
/* Colours */
|
|
224
|
+
/* background: var(--brand-surface); */
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
</style>
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
> **Note:** Track width custom properties set here affect only this instance. For site-wide changes, set them at `:root` or theme level.
|
|
231
|
+
|
|
232
|
+
---
|
|
233
|
+
|
|
199
234
|
## Notes
|
|
200
235
|
|
|
201
236
|
- Auto-imported in Nuxt — no import needed.
|
|
@@ -93,6 +93,39 @@ Key CSS custom properties — define these in your consuming app to control appe
|
|
|
93
93
|
|
|
94
94
|
Override via `styleClassPassthrough` or a parent HOC `<style>` block targeting `.link-text`.
|
|
95
95
|
|
|
96
|
+
## Local style override scaffold
|
|
97
|
+
|
|
98
|
+
When consuming this component, scaffold a style block using `styleClassPassthrough`. Delete the block if unused.
|
|
99
|
+
|
|
100
|
+
See [component-local-style-override.md](../component-local-style-override.md) for the full pattern.
|
|
101
|
+
|
|
102
|
+
```vue
|
|
103
|
+
<LinkText :style-class-passthrough="['my-link']" to="/path" link-text="Learn More" />
|
|
104
|
+
|
|
105
|
+
<style>
|
|
106
|
+
/* ─── LinkText local overrides ─────────────────────────────────────
|
|
107
|
+
Colours, borders, geometry only — do not override behaviour.
|
|
108
|
+
Delete this block if no overrides are needed.
|
|
109
|
+
─────────────────────────────────────────────────────────────────── */
|
|
110
|
+
.link-text {
|
|
111
|
+
&.my-link {
|
|
112
|
+
/* Colours */
|
|
113
|
+
/* --link-text-colour: var(--brand-primary); */
|
|
114
|
+
/* --link-text-colour-hover: var(--brand-primary-dark); */
|
|
115
|
+
|
|
116
|
+
/* Typography */
|
|
117
|
+
/* --link-text-font-size: 1.6rem; */
|
|
118
|
+
/* --link-text-decoration: underline; */
|
|
119
|
+
/* --link-text-decoration-hover: none; */
|
|
120
|
+
/* --link-text-underline-offset: 0.3em; */
|
|
121
|
+
|
|
122
|
+
/* Geometry */
|
|
123
|
+
/* --link-text-gap: 0.6em; */
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
</style>
|
|
127
|
+
```
|
|
128
|
+
|
|
96
129
|
## Notes
|
|
97
130
|
|
|
98
131
|
- Component is auto-imported in Nuxt — no import needed.
|