srcdev-nuxt-components 9.3.7 → 9.3.8

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.
Files changed (108) hide show
  1. package/.claude/hooks/check-component-update.sh +30 -1
  2. package/.claude/skills/components/breadcrumb.md +49 -0
  3. package/.claude/skills/components/content-docs.md +1 -1
  4. package/.claude/skills/components/service-detail.md +156 -0
  5. package/.claude/skills/components/services-card-grid.md +9 -9
  6. package/.claude/skills/components/services-card.md +82 -30
  7. package/.claude/skills/index.md +3 -1
  8. package/.claude/skills/qa-panel.md +2 -2
  9. package/.vscode/srcdev-component-breadcrumb.code-snippets +14 -0
  10. package/.vscode/srcdev-component-service-detail.code-snippets +49 -0
  11. package/.vscode/srcdev-component-services-card.code-snippets +108 -0
  12. package/app/components/01.atoms/navigation/breadcrumb/Breadcrumb.vue +94 -0
  13. package/app/components/01.atoms/navigation/breadcrumb/CONSUMER-STYLING.md +32 -0
  14. package/app/components/01.atoms/navigation/breadcrumb/stories/Breadcrumb.stories.ts +87 -0
  15. package/app/components/01.atoms/navigation/breadcrumb/tests/Breadcrumb.spec.ts +87 -0
  16. package/app/components/01.atoms/navigation/breadcrumb/tests/__snapshots__/Breadcrumb.spec.ts.snap +13 -0
  17. package/app/components/03.organisms/services/service-detail/CONSUMER-STYLING.md +181 -0
  18. package/app/components/03.organisms/services/service-detail/ServiceDetail.vue +480 -0
  19. package/app/components/03.organisms/services/service-detail/stories/ServiceDetail.stories.ts +282 -0
  20. package/app/components/03.organisms/services/service-detail/tests/ServiceDetail.spec.ts +270 -0
  21. package/app/components/03.organisms/services/service-detail/tests/__snapshots__/ServiceDetail.spec.ts.snap +99 -0
  22. package/app/components/03.organisms/services/services-card/CONSUMER-STYLING.md +129 -0
  23. package/app/components/03.organisms/services/services-card/ServicesCard.vue +153 -39
  24. package/app/components/03.organisms/services/services-card/stories/ServicesCard.stories.ts +65 -0
  25. package/app/components/03.organisms/services/services-card/tests/ServicesCard.spec.ts +82 -0
  26. package/app/components/03.organisms/services/services-card/tests/__snapshots__/ServicesCard.spec.ts.snap +13 -5
  27. package/app/components/03.organisms/services/services-grids/ServicesCardGrid.vue +3 -7
  28. package/app/components/03.organisms/services/services-grids/stories/ServicesCardGrid.stories.ts +126 -2
  29. package/app/components/03.organisms/services/services-grids/stories/ServicesSectionGrid.stories.ts +6 -0
  30. package/app/components/03.organisms/services/services-grids/tests/__snapshots__/ServicesCardGrid.spec.ts.snap +51 -27
  31. package/app/types/components/breadcrumb.ts +4 -0
  32. package/app/types/components/index.ts +1 -0
  33. package/package.json +2 -2
  34. package/app/pages/auto-grid.vue +0 -328
  35. package/app/pages/banner-video.vue +0 -238
  36. package/app/pages/forms/examples/buttons/index.vue +0 -259
  37. package/app/pages/forms/examples/material/checkbox-radio-panels.vue +0 -244
  38. package/app/pages/forms/examples/material/cssbattle.vue +0 -60
  39. package/app/pages/forms/examples/material/text-fields.vue +0 -910
  40. package/app/pages/grid-stack.vue +0 -252
  41. package/app/pages/index.vue +0 -127
  42. package/app/pages/page-hero-highlights.vue +0 -204
  43. package/app/pages/samaritan.vue +0 -327
  44. package/app/pages/typography/hero-text.vue +0 -97
  45. package/app/pages/typography/index.vue +0 -39
  46. package/app/pages/typography/page-body.vue +0 -171
  47. package/app/pages/typography/page-heading.vue +0 -76
  48. package/app/pages/typography/page-link.vue +0 -103
  49. package/app/pages/ui/accordian.vue +0 -211
  50. package/app/pages/ui/anchor-scroll-tab-navigation.vue +0 -191
  51. package/app/pages/ui/anchor-scroll.vue +0 -367
  52. package/app/pages/ui/animated-svg-text.vue +0 -85
  53. package/app/pages/ui/block-decorators.vue +0 -142
  54. package/app/pages/ui/card-core.vue +0 -82
  55. package/app/pages/ui/carousel-basic.vue +0 -292
  56. package/app/pages/ui/carousel-flip.vue +0 -449
  57. package/app/pages/ui/carousel-infinite.vue +0 -260
  58. package/app/pages/ui/clipped-panels.vue +0 -101
  59. package/app/pages/ui/contact-section.vue +0 -323
  60. package/app/pages/ui/container-glow.vue +0 -106
  61. package/app/pages/ui/content-width.vue +0 -126
  62. package/app/pages/ui/deep-expanding-menu.vue +0 -138
  63. package/app/pages/ui/display-avatar.vue +0 -253
  64. package/app/pages/ui/display-banner.vue +0 -78
  65. package/app/pages/ui/display-chip.vue +0 -367
  66. package/app/pages/ui/display-details.vue +0 -171
  67. package/app/pages/ui/display-dialog.vue +0 -344
  68. package/app/pages/ui/display-pill.vue +0 -396
  69. package/app/pages/ui/display-prompt.vue +0 -284
  70. package/app/pages/ui/display-toast.vue +0 -311
  71. package/app/pages/ui/expanding-panel.vue +0 -326
  72. package/app/pages/ui/glowing-border.vue +0 -111
  73. package/app/pages/ui/layout-content-docs.vue +0 -72
  74. package/app/pages/ui/layout-grid-a.vue +0 -96
  75. package/app/pages/ui/layout-grid-b.vue +0 -132
  76. package/app/pages/ui/magnetic-navigation.vue +0 -50
  77. package/app/pages/ui/marquee-scroller.vue +0 -99
  78. package/app/pages/ui/mask-element.vue +0 -104
  79. package/app/pages/ui/masonry-columns.vue +0 -55
  80. package/app/pages/ui/masonry-grid-ordered-experiment.vue +0 -79
  81. package/app/pages/ui/masonry-grid-ordered.vue +0 -56
  82. package/app/pages/ui/masonry-grid-sorted.vue +0 -55
  83. package/app/pages/ui/masonry-grid.vue +0 -55
  84. package/app/pages/ui/navigation/navigation-horizontal.vue +0 -484
  85. package/app/pages/ui/navigation/site-navigation/about.vue +0 -36
  86. package/app/pages/ui/navigation/site-navigation/contact.vue +0 -39
  87. package/app/pages/ui/navigation/site-navigation/index.vue +0 -38
  88. package/app/pages/ui/navigation/site-navigation/portfolio.vue +0 -43
  89. package/app/pages/ui/navigation/site-navigation/services.vue +0 -42
  90. package/app/pages/ui/navigation/tab-navigation.vue +0 -493
  91. package/app/pages/ui/page-row.vue +0 -127
  92. package/app/pages/ui/price-list.vue +0 -65
  93. package/app/pages/ui/profile-section.vue +0 -142
  94. package/app/pages/ui/qr-code/[componentName].vue +0 -218
  95. package/app/pages/ui/rotating-carousel.vue +0 -154
  96. package/app/pages/ui/scroll-reveal-image.vue +0 -483
  97. package/app/pages/ui/section-parallax.vue +0 -54
  98. package/app/pages/ui/services/colour-finder.vue +0 -43
  99. package/app/pages/ui/services/services-cards.vue +0 -57
  100. package/app/pages/ui/services/services-section/[slug].vue +0 -61
  101. package/app/pages/ui/services/services-sections-compact.vue +0 -64
  102. package/app/pages/ui/settings.vue +0 -54
  103. package/app/pages/ui/simple-grid.vue +0 -55
  104. package/app/pages/ui/slider-gallery.vue +0 -261
  105. package/app/pages/ui/tabs-y.vue +0 -107
  106. package/app/pages/ui/tabs.vue +0 -189
  107. package/app/pages/ui/tooltips.vue +0 -104
  108. package/app/pages/ui/wipe-away-vertical.vue +0 -129
@@ -1,8 +1,23 @@
1
+ import { computed } from "vue";
1
2
  import ServicesCardGrid from "../ServicesCardGrid.vue";
2
3
  import type { Meta, StoryObj } from "@nuxtjs/storybook";
3
4
  import type { Service } from "~/types/types.services";
4
5
 
5
- const meta: Meta<typeof ServicesCardGrid> = {
6
+ type StoryArgs = {
7
+ tag?: "div" | "section" | "main";
8
+ eyebrowConfig?: { tag?: "p" | "div" | "span"; fontSize?: "large" | "medium" | "small" };
9
+ heroConfig?: {
10
+ tag?: "h1" | "h2" | "h3" | "h4" | "h5" | "h6";
11
+ fontSize?: "display" | "title" | "heading" | "subheading" | "label";
12
+ };
13
+ hrefBase?: string;
14
+ buttonTextPrefix?: string;
15
+ styleClassPassthrough?: string | string[];
16
+ /** Story-only — not a real ServicesCardGrid prop, see useStorySetup below. */
17
+ lineClamp?: number;
18
+ };
19
+
20
+ const meta: Meta<StoryArgs> = {
6
21
  title: "Organisms/Services/Services Card Grid",
7
22
  component: ServicesCardGrid,
8
23
  argTypes: {
@@ -31,6 +46,11 @@ const meta: Meta<typeof ServicesCardGrid> = {
31
46
  control: "object",
32
47
  description: "Additional CSS classes applied to the root element",
33
48
  },
49
+ lineClamp: {
50
+ control: { type: "number", min: 1, max: 10, step: 1 },
51
+ description:
52
+ "Story-only control — sets ServicesCard's --description-line-clamp custom property on the grid wrapper (not a real component prop) to demo description clamping across cards",
53
+ },
34
54
  },
35
55
  args: {
36
56
  tag: "div",
@@ -39,8 +59,15 @@ const meta: Meta<typeof ServicesCardGrid> = {
39
59
  hrefBase: "/services/",
40
60
  buttonTextPrefix: "Enquire about",
41
61
  styleClassPassthrough: [],
62
+ lineClamp: 3,
42
63
  },
43
64
  parameters: {
65
+ backgrounds: {
66
+ default: "storybook-canvas",
67
+ options: {
68
+ "storybook-canvas": { name: "Canvas", value: "oklch(0.163 0.005 17)" },
69
+ },
70
+ },
44
71
  docs: {
45
72
  description: {
46
73
  component:
@@ -51,7 +78,7 @@ const meta: Meta<typeof ServicesCardGrid> = {
51
78
  };
52
79
 
53
80
  export default meta;
54
- type Story = StoryObj<typeof ServicesCardGrid>;
81
+ type Story = StoryObj<StoryArgs>;
55
82
 
56
83
  // ─── Fixtures ─────────────────────────────────────────────────────────────────
57
84
 
@@ -81,8 +108,79 @@ const sampleServices: Service[] = [
81
108
  makeService("colour-treatment", "Colour Treatment", "Add dimension and depth", "https://picsum.photos/seed/card-c/600/800"),
82
109
  ];
83
110
 
111
+ const makeServiceWithDescription = (
112
+ slug: string,
113
+ title: string,
114
+ subtitle: string,
115
+ image: string,
116
+ shortDescription: string
117
+ ): Service => ({ ...makeService(slug, title, subtitle, image), shortDescription });
118
+
119
+ // Six cards with progressively longer shortDescription text, to demo how the same
120
+ // lineClamp value clips each card's description at a different point.
121
+ const lineClampServices: Service[] = [
122
+ makeServiceWithDescription(
123
+ "one-liner",
124
+ "Fringe Trim",
125
+ "A quick refresh",
126
+ "https://picsum.photos/seed/clamp-1/600/800",
127
+ "A quick tidy-up."
128
+ ),
129
+ makeServiceWithDescription(
130
+ "short-desc",
131
+ "Blow Dry",
132
+ "Salon finish, fast",
133
+ "https://picsum.photos/seed/clamp-2/600/800",
134
+ "A smooth, voluminous blow dry finished with a light-hold spray for lasting body."
135
+ ),
136
+ makeServiceWithDescription(
137
+ "medium-desc",
138
+ "Balayage",
139
+ "Freehand colour artistry",
140
+ "https://picsum.photos/seed/clamp-3/600/800",
141
+ "Colour swept on by hand, bespoke to your hair's natural fall and texture. Creates soft, sun-kissed dimension that grows out gracefully with minimal upkeep."
142
+ ),
143
+ makeServiceWithDescription(
144
+ "long-desc",
145
+ "Locs Installation",
146
+ "Start your loc journey",
147
+ "https://picsum.photos/seed/clamp-4/600/800",
148
+ "Professional loc installation tailored to your hair type. We work with all textures and lengths to create beautiful, long-lasting locs, with a full consultation beforehand to agree size, style, and maintenance schedule."
149
+ ),
150
+ makeServiceWithDescription(
151
+ "very-long-desc",
152
+ "Keratin Treatment",
153
+ "Smooth, frizz-free hair",
154
+ "https://picsum.photos/seed/clamp-5/600/800",
155
+ "A deep-conditioning keratin treatment that smooths the hair cuticle, reduces frizz, and cuts down on daily styling time. Results typically last three to five months depending on hair type, porosity, and aftercare routine, and can be combined with a colour service for a full transformation."
156
+ ),
157
+ makeServiceWithDescription(
158
+ "extra-long-desc",
159
+ "Full Colour Correction",
160
+ "Rebuild from the roots",
161
+ "https://picsum.photos/seed/clamp-6/600/800",
162
+ "A multi-session colour correction service for hair that's had previous colour go wrong — box dye build-up, uneven tone, brassiness, or over-processed ends. We start with a full strand test and consultation, then work in stages to lift, tone, and rebuild condition safely, protecting hair integrity at every step rather than rushing to a single-visit result that risks further damage."
163
+ ),
164
+ ];
165
+
84
166
  // ─── Stories ──────────────────────────────────────────────────────────────────
85
167
 
168
+ /**
169
+ * lineClamp is a story-only control (not a real ServicesCardGrid prop) that sets
170
+ * --description-line-clamp on the grid wrapper — the global custom property each
171
+ * ServicesCard's .description reads for its -webkit-line-clamp value.
172
+ */
173
+ function useStorySetup(args: StoryArgs) {
174
+ const clampStyle = computed(() => ({
175
+ "--description-line-clamp": String(args.lineClamp ?? 3),
176
+ }));
177
+ const componentArgs = computed(() => {
178
+ const { lineClamp: _lineClamp, ...rest } = args;
179
+ return rest;
180
+ });
181
+ return { clampStyle, componentArgs };
182
+ }
183
+
86
184
  export const Default: Story = {
87
185
  render: (args) => ({
88
186
  components: { ServicesCardGrid },
@@ -180,3 +278,29 @@ export const EmptyData: Story = {
180
278
  },
181
279
  },
182
280
  };
281
+
282
+ export const DescriptionLineClamp: Story = {
283
+ name: "Description Line Clamp",
284
+ args: {
285
+ lineClamp: 3,
286
+ },
287
+ render: (args) => ({
288
+ components: { ServicesCardGrid },
289
+ setup() {
290
+ return { ...useStorySetup(args), lineClampServices };
291
+ },
292
+ template: `
293
+ <div :style="clampStyle">
294
+ <ServicesCardGrid v-bind="componentArgs" :services-data="lineClampServices" />
295
+ </div>
296
+ `,
297
+ }),
298
+ parameters: {
299
+ docs: {
300
+ description: {
301
+ story:
302
+ "Six cards with progressively longer shortDescription text, all sharing one --description-line-clamp value set via the lineClamp control. Use the control to see how the same clamp value affects a one-line description (no visible clamping) versus a long paragraph (clamped with an ellipsis) — card heights stay equal since the meta row and actions slot sit below the clamped description rather than growing with it.",
303
+ },
304
+ },
305
+ },
306
+ };
@@ -32,6 +32,12 @@ const meta: Meta<typeof ServicesSectionGrid> = {
32
32
  styleClassPassthrough: [],
33
33
  },
34
34
  parameters: {
35
+ backgrounds: {
36
+ default: "storybook-canvas",
37
+ options: {
38
+ "storybook-canvas": { name: "Canvas", value: "oklch(0.163 0.005 17)" },
39
+ },
40
+ },
35
41
  docs: {
36
42
  description: {
37
43
  component:
@@ -4,39 +4,63 @@ exports[`ServicesCardGrid > renders correct HTML structure 1`] = `
4
4
  "<div class="services-card-grid">
5
5
  <div class="services-card">
6
6
  <div class="image-wrapper"><img data-nuxt-img="" srcset="/_ipx/_/images/locs-installation.jpg 1x, /_ipx/_/images/locs-installation.jpg 2x" alt="Locs Installation" loading="lazy" class="image" src="/_ipx/_/images/locs-installation.jpg"></div>
7
- <div class="eyebrow-text large">Subtitle for Locs Installation</div>
8
- <h2 class="hero-text heading axis-horizontal">
9
- <!--v-if--><span class="text-block-0 normal">Locs Installation</span>
10
- </h2>
11
- <div class="description">Short description for Locs Installation.</div><a href="/ui/services/services-section/locs-installation" data-testid="input-button-core" data-theme="default" class="input-button-core secondary mbs-24 is-link">
12
- <!--v-if-->
13
- <!--v-if--><span class="button-text">Enquire about Locs Installation</span><span class="btn-icon right"><span class="iconify i-mdi:arrow-right icon" aria-hidden="true"></span></span>
14
- <!--v-if-->
15
- </a>
7
+ <div class="details-wrapper">
8
+ <div class="eyebrow-text large">Subtitle for Locs Installation</div>
9
+ <h2 class="hero-text heading axis-horizontal">
10
+ <!--v-if--><span class="text-block-0 normal">Locs Installation</span>
11
+ </h2>
12
+ <div class="description">Short description for Locs Installation.</div>
13
+ <div class="footer">
14
+ <div class="meta">
15
+ <div class="meta-duration">60 mins</div>
16
+ <div class="meta-price">£50</div>
17
+ </div><a href="/services/locs-installation" data-testid="input-button-core" data-theme="default" class="input-button-core secondary mbs-24 is-link">
18
+ <!--v-if-->
19
+ <!--v-if--><span class="button-text">Enquire about Locs Installation</span><span class="btn-icon right"><span class="iconify i-mdi:arrow-right icon" aria-hidden="true"></span></span>
20
+ <!--v-if-->
21
+ </a>
22
+ </div>
23
+ </div>
16
24
  </div>
17
25
  <div class="services-card">
18
26
  <div class="image-wrapper"><img data-nuxt-img="" srcset="/_ipx/_/images/locs-retwist.jpg 1x, /_ipx/_/images/locs-retwist.jpg 2x" alt="Locs Retwist" loading="lazy" class="image" src="/_ipx/_/images/locs-retwist.jpg"></div>
19
- <div class="eyebrow-text large">Subtitle for Locs Retwist</div>
20
- <h2 class="hero-text heading axis-horizontal">
21
- <!--v-if--><span class="text-block-0 normal">Locs Retwist</span>
22
- </h2>
23
- <div class="description">Short description for Locs Retwist.</div><a href="/ui/services/services-section/locs-retwist" data-testid="input-button-core" data-theme="default" class="input-button-core secondary mbs-24 is-link">
24
- <!--v-if-->
25
- <!--v-if--><span class="button-text">Enquire about Locs Retwist</span><span class="btn-icon right"><span class="iconify i-mdi:arrow-right icon" aria-hidden="true"></span></span>
26
- <!--v-if-->
27
- </a>
27
+ <div class="details-wrapper">
28
+ <div class="eyebrow-text large">Subtitle for Locs Retwist</div>
29
+ <h2 class="hero-text heading axis-horizontal">
30
+ <!--v-if--><span class="text-block-0 normal">Locs Retwist</span>
31
+ </h2>
32
+ <div class="description">Short description for Locs Retwist.</div>
33
+ <div class="footer">
34
+ <div class="meta">
35
+ <div class="meta-duration">60 mins</div>
36
+ <div class="meta-price">£50</div>
37
+ </div><a href="/services/locs-retwist" data-testid="input-button-core" data-theme="default" class="input-button-core secondary mbs-24 is-link">
38
+ <!--v-if-->
39
+ <!--v-if--><span class="button-text">Enquire about Locs Retwist</span><span class="btn-icon right"><span class="iconify i-mdi:arrow-right icon" aria-hidden="true"></span></span>
40
+ <!--v-if-->
41
+ </a>
42
+ </div>
43
+ </div>
28
44
  </div>
29
45
  <div class="services-card">
30
46
  <div class="image-wrapper"><img data-nuxt-img="" srcset="/_ipx/_/images/colour-treatment.jpg 1x, /_ipx/_/images/colour-treatment.jpg 2x" alt="Colour Treatment" loading="lazy" class="image" src="/_ipx/_/images/colour-treatment.jpg"></div>
31
- <div class="eyebrow-text large">Subtitle for Colour Treatment</div>
32
- <h2 class="hero-text heading axis-horizontal">
33
- <!--v-if--><span class="text-block-0 normal">Colour Treatment</span>
34
- </h2>
35
- <div class="description">Short description for Colour Treatment.</div><a href="/ui/services/services-section/colour-treatment" data-testid="input-button-core" data-theme="default" class="input-button-core secondary mbs-24 is-link">
36
- <!--v-if-->
37
- <!--v-if--><span class="button-text">Enquire about Colour Treatment</span><span class="btn-icon right"><span class="iconify i-mdi:arrow-right icon" aria-hidden="true"></span></span>
38
- <!--v-if-->
39
- </a>
47
+ <div class="details-wrapper">
48
+ <div class="eyebrow-text large">Subtitle for Colour Treatment</div>
49
+ <h2 class="hero-text heading axis-horizontal">
50
+ <!--v-if--><span class="text-block-0 normal">Colour Treatment</span>
51
+ </h2>
52
+ <div class="description">Short description for Colour Treatment.</div>
53
+ <div class="footer">
54
+ <div class="meta">
55
+ <div class="meta-duration">60 mins</div>
56
+ <div class="meta-price">£50</div>
57
+ </div><a href="/services/colour-treatment" data-testid="input-button-core" data-theme="default" class="input-button-core secondary mbs-24 is-link">
58
+ <!--v-if-->
59
+ <!--v-if--><span class="button-text">Enquire about Colour Treatment</span><span class="btn-icon right"><span class="iconify i-mdi:arrow-right icon" aria-hidden="true"></span></span>
60
+ <!--v-if-->
61
+ </a>
62
+ </div>
63
+ </div>
40
64
  </div>
41
65
  </div>"
42
66
  `;
@@ -0,0 +1,4 @@
1
+ export interface BreadcrumbItem {
2
+ label: string;
3
+ to?: string;
4
+ }
@@ -14,3 +14,4 @@ export * from "./hero-text"
14
14
  export * from "./navigation-horizontal.d"
15
15
  export * from "./social-icons-list.d"
16
16
  export * from "./content-docs.d"
17
+ export * from "./breadcrumb"
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "srcdev-nuxt-components",
3
3
  "type": "module",
4
- "version": "9.3.7",
4
+ "version": "9.3.8",
5
5
  "main": "nuxt.config.ts",
6
6
  "types": "types.d.ts",
7
7
  "license": "MIT",
@@ -16,7 +16,7 @@
16
16
  "cleaninstall": "npm run clean && npm run reinstall",
17
17
  "generate:ramps": "node scripts/generate-ramps.mjs",
18
18
  "check:ramps": "node scripts/check-ramps.mjs",
19
- "dev": "npm run generate:ramps && SRCDEV_STANDALONE=true nuxt dev",
19
+ "dev": "npm run generate:ramps && SRCDEV_STANDALONE=true storybook dev -p 6006",
20
20
  "build": "npm run generate:ramps && SRCDEV_STANDALONE=true nuxt build",
21
21
  "generate": "npm run generate:ramps && SRCDEV_STANDALONE=true nuxt generate",
22
22
  "preview": "SRCDEV_STANDALONE=true nuxt preview",
@@ -1,328 +0,0 @@
1
- <template>
2
- <div>
3
- <NuxtLayout name="default">
4
- <template #layout-content>
5
- <PageRow tag="div" variant="full" :style-class-passthrough="['mbe-32']">
6
- <h1 class="page-heading-1">AutoGrid</h1>
7
- <p class="page-body-normal">
8
- Responsive auto-fit CSS grid wrapper. Columns auto-fit to a minimum width; when
9
- <code>is-responsive</code>
10
- is enabled, the minimum shifts at container breakpoints via
11
- <code>@container</code>
12
- queries.
13
- </p>
14
- </PageRow>
15
-
16
- <div v-if="isDev" class="qa-panel">
17
- <details class="qa-panel__details">
18
- <summary class="qa-panel__summary">
19
- <span class="qa-panel__title">QA — AutoGrid</span>
20
- <code class="qa-panel__status">
21
- tag:{{ qaTag }} · responsive:{{ qaIsResponsive ? "on" : "off" }} · small:{{ qaMinColSizeSmall }} ·
22
- default:{{ qaMinColSizeDefault }} · large:{{ qaMinColSizeLarge }} · gap:{{ qaGap }}
23
- </code>
24
- </summary>
25
- <div class="qa-panel__body">
26
- <div class="qa-panel__group">
27
- <span class="qa-panel__label">tag</span>
28
- <div class="qa-panel__chips">
29
- <button
30
- v-for="opt in tagOptions"
31
- :key="opt"
32
- class="qa-panel__chip"
33
- :class="{ 'is-active': qaTag === opt }"
34
- @click="qaTag = opt"
35
- >
36
- {{ opt }}
37
- </button>
38
- </div>
39
- </div>
40
-
41
- <div class="qa-panel__group">
42
- <span class="qa-panel__label">is-responsive</span>
43
- <div class="qa-panel__chips">
44
- <button
45
- v-for="opt in [true, false]"
46
- :key="String(opt)"
47
- class="qa-panel__chip"
48
- :class="{ 'is-active': qaIsResponsive === opt }"
49
- @click="qaIsResponsive = opt"
50
- >
51
- {{ opt ? "on" : "off" }}
52
- </button>
53
- </div>
54
- </div>
55
-
56
- <div class="qa-panel__group">
57
- <span class="qa-panel__label">min-col-size-small</span>
58
- <div class="qa-panel__chips">
59
- <button
60
- v-for="preset in sizePresets"
61
- :key="preset"
62
- class="qa-panel__chip"
63
- :class="{ 'is-active': qaMinColSizeSmall === preset }"
64
- @click="qaMinColSizeSmall = preset"
65
- >
66
- {{ preset }}
67
- </button>
68
- </div>
69
- <input v-model="qaMinColSizeSmall" placeholder="e.g. 200px" class="qa-panel__input" />
70
- </div>
71
-
72
- <div class="qa-panel__group">
73
- <span class="qa-panel__label">min-col-size-default</span>
74
- <div class="qa-panel__chips">
75
- <button
76
- v-for="preset in sizePresets"
77
- :key="preset"
78
- class="qa-panel__chip"
79
- :class="{ 'is-active': qaMinColSizeDefault === preset }"
80
- @click="qaMinColSizeDefault = preset"
81
- >
82
- {{ preset }}
83
- </button>
84
- </div>
85
- <input v-model="qaMinColSizeDefault" placeholder="e.g. 300px" class="qa-panel__input" />
86
- </div>
87
-
88
- <div class="qa-panel__group">
89
- <span class="qa-panel__label">min-col-size-large</span>
90
- <div class="qa-panel__chips">
91
- <button
92
- v-for="preset in sizePresets"
93
- :key="preset"
94
- class="qa-panel__chip"
95
- :class="{ 'is-active': qaMinColSizeLarge === preset }"
96
- @click="qaMinColSizeLarge = preset"
97
- >
98
- {{ preset }}
99
- </button>
100
- </div>
101
- <input v-model="qaMinColSizeLarge" placeholder="e.g. 350px" class="qa-panel__input" />
102
- </div>
103
-
104
- <div class="qa-panel__group">
105
- <span class="qa-panel__label">gap</span>
106
- <div class="qa-panel__chips">
107
- <button
108
- v-for="preset in gapPresets"
109
- :key="preset"
110
- class="qa-panel__chip"
111
- :class="{ 'is-active': qaGap === preset }"
112
- @click="qaGap = preset"
113
- >
114
- {{ preset }}
115
- </button>
116
- </div>
117
- <input v-model="qaGap" placeholder="e.g. 2rem" class="qa-panel__input" />
118
- </div>
119
- </div>
120
- </details>
121
- </div>
122
-
123
- <PageRow tag="div" variant="full" :style-class-passthrough="['mbe-32']">
124
- <div class="auto-grid-demo-container">
125
- <AutoGrid
126
- :tag="qaTag"
127
- :is-responsive="qaIsResponsive"
128
- :min-col-size-small="qaMinColSizeSmall"
129
- :min-col-size-default="qaMinColSizeDefault"
130
- :min-col-size-large="qaMinColSizeLarge"
131
- :style="qaStyle"
132
- >
133
- <template v-for="card in statCards" #[card.id] :key="card.id">
134
- <div class="stat-card">
135
- <span class="stat-card__label">{{ card.label }}</span>
136
- <span class="stat-card__value">{{ card.value }}</span>
137
- </div>
138
- </template>
139
- </AutoGrid>
140
- </div>
141
- </PageRow>
142
- </template>
143
- </NuxtLayout>
144
- </div>
145
- </template>
146
-
147
- <script setup lang="ts">
148
- definePageMeta({ layout: false });
149
-
150
- useHead({
151
- title: "AutoGrid",
152
- meta: [{ name: "description", content: "AutoGrid component demo" }],
153
- bodyAttrs: { class: "auto-grid-page" },
154
- });
155
-
156
- const isDev = import.meta.dev;
157
-
158
- const tagOptions = ["div", "section", "article", "main"] as const;
159
- const qaTag = ref<"div" | "section" | "article" | "main">("div");
160
- const qaIsResponsive = ref(false);
161
- const qaMinColSizeSmall = ref("250px");
162
- const qaMinColSizeDefault = ref("300px");
163
- const qaMinColSizeLarge = ref("350px");
164
- const qaGap = ref("1rem");
165
-
166
- const sizePresets = ["150px", "200px", "250px", "300px", "350px", "400px"];
167
- const gapPresets = ["0.5rem", "1rem", "1.6rem", "2.4rem", "3.2rem"];
168
-
169
- const qaStyle = computed(() => ({
170
- "--auto-grid-min-col-size-small": qaMinColSizeSmall.value,
171
- "--auto-grid-min-col-size-default": qaMinColSizeDefault.value,
172
- "--auto-grid-min-col-size-large": qaMinColSizeLarge.value,
173
- "--auto-grid-gap": qaGap.value,
174
- }));
175
-
176
- const statCards = [
177
- { id: "item-1", label: "Revenue", value: "£24,500" },
178
- { id: "item-2", label: "Clients", value: "142" },
179
- { id: "item-3", label: "Bookings", value: "38" },
180
- { id: "item-4", label: "Avg. Rating", value: "4.9" },
181
- { id: "item-5", label: "New Users", value: "76" },
182
- { id: "item-6", label: "Retention", value: "91%" },
183
- ];
184
- </script>
185
-
186
- <style lang="css">
187
- .auto-grid-page {
188
- code {
189
- font-family: monospace;
190
- font-size: 0.9em;
191
- background: var(--slate-02);
192
- padding: 0.1em 0.4em;
193
- border-radius: 0.3rem;
194
- }
195
-
196
- .auto-grid-demo-container {
197
- container-type: inline-size;
198
- }
199
-
200
- .stat-card {
201
- display: flex;
202
- flex-direction: column;
203
- gap: 0.8rem;
204
- padding: 2.4rem;
205
- background: white;
206
- border-radius: 0.8rem;
207
- box-shadow: 0 2px 8px oklch(0% 0 0 / 0.08);
208
- }
209
-
210
- .stat-card__label {
211
- font-size: 1.2rem;
212
- font-weight: 600;
213
- text-transform: uppercase;
214
- letter-spacing: 0.05em;
215
- color: var(--slate-08, #6b7280);
216
- }
217
-
218
- .stat-card__value {
219
- font-size: 2.4rem;
220
- font-weight: 700;
221
- color: var(--slate-12, #111827);
222
- }
223
-
224
- /* ── QA Panel ──────────────────────────────────────────────────── */
225
-
226
- .qa-panel {
227
- background: oklch(15% 0 0);
228
- color: white;
229
- font-size: 1.3rem;
230
- }
231
-
232
- .qa-panel__details {
233
- padding: 1rem 2rem;
234
- }
235
-
236
- .qa-panel__summary {
237
- cursor: pointer;
238
- display: flex;
239
- align-items: center;
240
- gap: 1.6rem;
241
- list-style: none;
242
- user-select: none;
243
-
244
- &::-webkit-details-marker {
245
- display: none;
246
- }
247
- }
248
-
249
- .qa-panel__title {
250
- font-weight: 600;
251
- font-size: 1.1rem;
252
- text-transform: uppercase;
253
- letter-spacing: 0.08em;
254
- }
255
-
256
- .qa-panel__status {
257
- font-family: monospace;
258
- font-size: 1.2rem;
259
- background: oklch(0% 0 0 / 0.3);
260
- padding: 0.2rem 0.8rem;
261
- border-radius: 0.4rem;
262
- user-select: text;
263
- cursor: text;
264
- }
265
-
266
- .qa-panel__body {
267
- display: flex;
268
- flex-wrap: wrap;
269
- gap: 2.4rem;
270
- padding-block: 1.2rem 0.4rem;
271
- }
272
-
273
- .qa-panel__group {
274
- display: flex;
275
- flex-direction: column;
276
- gap: 0.6rem;
277
- }
278
-
279
- .qa-panel__label {
280
- font-size: 1.1rem;
281
- text-transform: uppercase;
282
- letter-spacing: 0.08em;
283
- opacity: 0.55;
284
- }
285
-
286
- .qa-panel__chips {
287
- display: flex;
288
- flex-wrap: wrap;
289
- gap: 0.4rem;
290
- }
291
-
292
- .qa-panel__chip {
293
- font-family: monospace;
294
- font-size: 1.2rem;
295
- color: white;
296
- background: oklch(0% 0 0 / 0.25);
297
- border: 1px solid oklch(100% 0 0 / 0.18);
298
- padding: 0.3rem 1rem;
299
- border-radius: 0.4rem;
300
- cursor: pointer;
301
- transition: background 0.15s;
302
-
303
- &:hover {
304
- background: oklch(0% 0 0 / 0.4);
305
- }
306
-
307
- &.is-active {
308
- background: oklch(55% 0.18 240);
309
- border-color: oklch(55% 0.18 240);
310
- }
311
- }
312
-
313
- .qa-panel__input {
314
- font-family: monospace;
315
- font-size: 1.2rem;
316
- color: white;
317
- background: oklch(0% 0 0 / 0.25);
318
- border: 1px solid oklch(100% 0 0 / 0.18);
319
- padding: 0.3rem 1rem;
320
- border-radius: 0.4rem;
321
- width: 18rem;
322
-
323
- &::placeholder {
324
- opacity: 0.45;
325
- }
326
- }
327
- }
328
- </style>