srcdev-nuxt-components 9.1.7 → 9.1.9

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.
@@ -7,7 +7,7 @@
7
7
  <div class="info-wrapper" :class="infoWrapperClasses">
8
8
  <EyebrowText font-size="large" :text-content="serviceData.subtitle" />
9
9
  <HeroText
10
- tag="h1"
10
+ :tag="headerTag"
11
11
  font-size="title"
12
12
  :text-content="[
13
13
  {
@@ -33,7 +33,7 @@
33
33
 
34
34
  <HeroText
35
35
  v-if="!isSummary"
36
- tag="h2"
36
+ :tag="headerTag"
37
37
  axis="horizontal"
38
38
  font-size="subheading"
39
39
  :text-content="serviceData.heroHeading"
@@ -47,7 +47,7 @@
47
47
 
48
48
  <HeroText
49
49
  v-if="!isSummary"
50
- tag="h2"
50
+ :tag="headerTag"
51
51
  axis="horizontal"
52
52
  font-size="subheading"
53
53
  :text-content="[{ text: 'The Process ', styleClass: 'normal' }]"
@@ -69,7 +69,7 @@
69
69
 
70
70
  <HeroText
71
71
  v-if="!isSummary"
72
- tag="h2"
72
+ :tag="headerTag"
73
73
  axis="horizontal"
74
74
  font-size="subheading"
75
75
  :text-content="[{ text: 'Ideal For', styleClass: 'normal' }]"
@@ -87,7 +87,7 @@
87
87
 
88
88
  <HeroText
89
89
  v-if="!isSummary"
90
- tag="h2"
90
+ :tag="headerTag"
91
91
  axis="horizontal"
92
92
  font-size="subheading"
93
93
  :text-content="[{ text: 'Aftercare &amp; Maintenance', styleClass: 'normal' }]"
@@ -98,7 +98,7 @@
98
98
 
99
99
  <HeroText
100
100
  v-if="!isSummary"
101
- tag="h2"
101
+ :tag="headerTag"
102
102
  axis="horizontal"
103
103
  font-size="subheading"
104
104
  :text-content="[{ text: 'Frequently Asked Questions', styleClass: 'normal' }]"
@@ -144,6 +144,7 @@ import type { Service } from "~/types/types.services";
144
144
 
145
145
  interface Props {
146
146
  tag?: "div" | "section" | "article" | "main";
147
+ headerTag?: "h1" | "h2" | "h3";
147
148
  index?: number;
148
149
  serviceData: Service;
149
150
  isSummary?: boolean;
@@ -153,6 +154,7 @@ interface Props {
153
154
  }
154
155
  const props = withDefaults(defineProps<Props>(), {
155
156
  tag: "div",
157
+ headerTag: "h2",
156
158
  index: 0,
157
159
  isSummary: false,
158
160
  summaryAlignment: "center",
@@ -2,7 +2,7 @@
2
2
  <component
3
3
  :is="tag"
4
4
  class="page-hero-highlights"
5
- :class="[elementClasses, componentClasses, { 'has-content-panel': contentPanel }]"
5
+ :class="[elementClasses, componentClasses]"
6
6
  :aria-labelledby="ariaLabelledby"
7
7
  >
8
8
  <div class="header-row">
@@ -26,7 +26,7 @@ interface Props {
26
26
  tag?: "div" | "section" | "main";
27
27
  highlightsEqualWidths?: boolean;
28
28
  highlightsJustify?: "start" | "center" | "end" | "space-between" | "space-around";
29
- maxWidth?: string;
29
+ widthConstrained?: boolean;
30
30
  contentAlign?: "start" | "center";
31
31
  contentPanel?: boolean;
32
32
  highlightTitleBaseline?: boolean;
@@ -37,22 +37,19 @@ const props = withDefaults(defineProps<Props>(), {
37
37
  tag: "div",
38
38
  highlightsEqualWidths: false,
39
39
  highlightsJustify: "start",
40
- maxWidth: undefined,
40
+ widthConstrained: false,
41
41
  contentAlign: "center",
42
42
  contentPanel: true,
43
43
  highlightTitleBaseline: false,
44
44
  styleClassPassthrough: () => [],
45
45
  });
46
46
 
47
- const gridColumns = computed(() => {
48
- if (!props.maxWidth) return "16px 1fr 16px";
49
- if (props.contentAlign === "start") return `16px minmax(0, ${props.maxWidth}) minmax(16px, 1fr)`;
50
- return `max(16px, (100% - ${props.maxWidth}) / 2) 1fr max(16px, (100% - ${props.maxWidth}) / 2)`;
51
- });
52
-
53
47
  const { headingId, ariaLabelledby } = useAriaLabelledById(() => props.tag);
54
48
  const componentClasses = computed(() => ({
55
49
  "highlight-title-baseline": props.highlightTitleBaseline,
50
+ [props.contentAlign]: true,
51
+ "width-constrained": props.widthConstrained,
52
+ "has-content-panel": props.contentPanel,
56
53
  }));
57
54
 
58
55
  const highlightClasses = computed(() => ({
@@ -73,7 +70,14 @@ watch(
73
70
 
74
71
  <style lang="css">
75
72
  .page-hero-highlights {
73
+ /* Layout tokens */
74
+ --max-width: 1064px;
75
+ --page-hero-highlights-gutter-mobile: 16px;
76
+ --page-hero-highlights-gutter-tablet: 40px;
77
+ --page-hero-highlights-gutter-desktop: 32px;
78
+
76
79
  /* User themable tokens */
80
+
77
81
  --header-row-background-colour: darkblue;
78
82
 
79
83
  --highlights-row-item-gap: 1rem;
@@ -105,11 +109,46 @@ watch(
105
109
  --highlight-padding-block-start: 0; /* We're setting the title height via row height, so this should be exposed for override in consuming page */
106
110
  }
107
111
 
112
+ /* Internal layout variables */
113
+ --page-hero-highlights-gutter: var(--page-hero-highlights-gutter-mobile);
114
+
115
+ @container (width >= 768px) {
116
+ --page-hero-highlights-gutter: var(--page-hero-highlights-gutter-tablet);
117
+ }
118
+ @container (width >= 1024px) {
119
+ --page-hero-highlights-gutter: var(--page-hero-highlights-gutter-desktop);
120
+ }
121
+
122
+ /* Private geometry */
123
+ --header-slot-grid-row: 1;
124
+ &.highlight-title-baseline {
125
+ --header-slot-grid-row: 1 / span 2;
126
+ }
127
+
108
128
  display: grid;
109
- grid-template-columns: v-bind(gridColumns);
129
+ /* grid-template-columns: v-bind(gridColumns); */
110
130
  grid-template-rows: auto var(--highlight-title-height) 1fr auto;
111
131
  gap: 0;
112
132
 
133
+ &.width-constrained {
134
+ grid-template-columns: var(--page-hero-highlights-gutter) 1fr var(--page-hero-highlights-gutter);
135
+ }
136
+
137
+ &:not(.width-constrained) {
138
+ &.start {
139
+ grid-template-columns: var(--page-hero-highlights-gutter) minmax(0, var(--max-width)) minmax(
140
+ var(--page-hero-highlights-gutter),
141
+ 1fr
142
+ );
143
+ }
144
+ &.center {
145
+ grid-template-columns: max(var(--page-hero-highlights-gutter), (100% - var(--max-width)) / 2) 1fr max(
146
+ var(--page-hero-highlights-gutter),
147
+ (100% - var(--max-width)) / 2
148
+ );
149
+ }
150
+ }
151
+
113
152
  .header-row {
114
153
  /* Element geometry */
115
154
  grid-column: 1 / -1;
@@ -123,7 +162,7 @@ watch(
123
162
 
124
163
  .header-slot {
125
164
  grid-column: 2;
126
- grid-row: 1;
165
+ grid-row: var(--header-slot-grid-row);
127
166
  container-type: inline-size;
128
167
  }
129
168
  }
@@ -34,14 +34,24 @@ watch(
34
34
  <style lang="css">
35
35
  .page-hero-highlights-header {
36
36
  /* User themable tokens */
37
- --phh-padding-block: 1.6rem;
37
+ --phh-padding-block-mobile: 1.6rem 3.2rem;
38
+ --phh-padding-block-tablet: 2.4rem 4.8rem;
39
+ --phh-padding-block-desktop: 3.2rem 6.4rem;
38
40
  --phh-gap: 1.6rem;
39
41
  --phh-end-gap: 0.8rem;
40
42
 
41
43
  display: flex;
42
44
  flex-direction: column;
43
45
  gap: var(--phh-gap);
44
- padding-block: var(--phh-padding-block);
46
+
47
+ padding-block: var(--phh-padding-block-mobile);
48
+
49
+ @container (width >= 768px) {
50
+ padding-block: var(--phh-padding-block-tablet);
51
+ }
52
+ @container (width >= 1024px) {
53
+ padding-block: var(--phh-padding-block-desktop);
54
+ }
45
55
 
46
56
  &:has(.phh-end) {
47
57
  @container (width >= 768px) {
@@ -6,7 +6,7 @@ type StoryArgs = {
6
6
  tag?: "div" | "section" | "main";
7
7
  highlightsEqualWidths?: boolean;
8
8
  highlightsJustify?: "start" | "center" | "end" | "space-between" | "space-around";
9
- maxWidth?: string;
9
+ widthConstrained?: boolean;
10
10
  contentAlign?: "start" | "center";
11
11
  contentPanel?: boolean;
12
12
  highlightTitleBaseline?: boolean;
@@ -34,11 +34,10 @@ const meta: Meta<StoryArgs> = {
34
34
  options: ["start", "center", "end", "space-between", "space-around"],
35
35
  description: "Justification of highlight items along the main axis",
36
36
  },
37
- maxWidth: {
38
- control: { type: "select" },
39
- options: ["", "600px", "800px", "1024px", "1064px", "1200px", "1440px"],
37
+ widthConstrained: {
38
+ control: "boolean",
40
39
  description:
41
- "Max width of the central content column. Gutters grow to enforce the constraint; below this width they hold at 16px.",
40
+ "When true, caps the central column at --max-width (default 1064px). Gutters grow responsively to enforce the constraint. Override --max-width via styleClassPassthrough to change the cap value.",
42
41
  },
43
42
  contentAlign: {
44
43
  control: { type: "inline-radio" },
@@ -78,7 +77,7 @@ const meta: Meta<StoryArgs> = {
78
77
  tag: "div",
79
78
  highlightsEqualWidths: false,
80
79
  highlightsJustify: "start",
81
- maxWidth: "",
80
+ widthConstrained: false,
82
81
  contentAlign: "center",
83
82
  contentPanel: true,
84
83
  highlightTitleBaseline: false,
@@ -119,6 +118,12 @@ All layout and visual properties are customisable via CSS custom properties. Set
119
118
 
120
119
  \`\`\`css
121
120
  .page-hero-highlights {
121
+ /* Grid layout */
122
+ --max-width: 1064px;
123
+ --page-hero-highlights-gutter-mobile: 16px;
124
+ --page-hero-highlights-gutter-tablet: 40px;
125
+ --page-hero-highlights-gutter-desktop: 32px;
126
+
122
127
  /* Header zone */
123
128
  --header-row-background-colour: darkblue;
124
129
 
@@ -196,6 +201,12 @@ All layout and visual properties are customisable via CSS custom properties. Set
196
201
  ─────────────────────────────────────────────────────────────────── */
197
202
  .page-hero-highlights {
198
203
 
204
+ /* Grid layout */
205
+ --max-width: 1064px;
206
+ --page-hero-highlights-gutter-mobile: 16px;
207
+ --page-hero-highlights-gutter-tablet: 40px;
208
+ --page-hero-highlights-gutter-desktop: 32px;
209
+
199
210
  /* Header zone */
200
211
  --header-row-background-colour: darkblue;
201
212
 
@@ -330,10 +341,10 @@ export const NoSlotContent: Story = {
330
341
  }),
331
342
  };
332
343
 
333
- /** Max width centered — content column capped at 800px with equal growing gutters either side. */
344
+ /** Max width centered — content column capped at --max-width (1064px) with equal growing gutters either side. */
334
345
  export const MaxWidthCentered: Story = {
335
346
  name: "Max Width — Centered",
336
- args: { maxWidth: "800px", contentAlign: "center" },
347
+ args: { widthConstrained: true, contentAlign: "center" },
337
348
  render: (args: StoryArgs) => ({
338
349
  components: { PageHeroHighlights },
339
350
  setup() {
@@ -343,7 +354,7 @@ export const MaxWidthCentered: Story = {
343
354
  <PageHeroHighlights v-bind="componentArgs" :style="bgStyles">
344
355
  <template #header>
345
356
  <p class="page-heading-1">Dashboard</p>
346
- <p class="page-body-normal">Content column is capped at 800px — gutters grow equally on both sides.</p>
357
+ <p class="page-body-normal">Content column is capped at --max-width (1064px by default) — gutters grow equally on both sides.</p>
347
358
  </template>
348
359
 
349
360
  <template #highlights>
@@ -370,10 +381,10 @@ export const MaxWidthCentered: Story = {
370
381
  }),
371
382
  };
372
383
 
373
- /** Max width start — content column capped at 800px, pinned to the left with a fixed 16px gutter. */
384
+ /** Max width start — content column capped at --max-width (1064px), pinned to the left. */
374
385
  export const MaxWidthStart: Story = {
375
386
  name: "Max Width — Start",
376
- args: { maxWidth: "800px", contentAlign: "start" },
387
+ args: { widthConstrained: true, contentAlign: "start" },
377
388
  render: (args: StoryArgs) => ({
378
389
  components: { PageHeroHighlights },
379
390
  setup() {
@@ -383,7 +394,7 @@ export const MaxWidthStart: Story = {
383
394
  <PageHeroHighlights v-bind="componentArgs" :style="bgStyles">
384
395
  <template #header>
385
396
  <p class="page-heading-1">Dashboard</p>
386
- <p class="page-body-normal">Content column is capped at 800px, aligned to the start — right side takes the remaining space.</p>
397
+ <p class="page-body-normal">Content column is capped at --max-width (1064px by default), aligned to the start — right side takes the remaining space.</p>
387
398
  </template>
388
399
 
389
400
  <template #highlights>
@@ -168,39 +168,30 @@ describe("PageHeroHighlights", () => {
168
168
  expect(el.classes()).toContain("another-class");
169
169
  });
170
170
 
171
- describe("gridColumns", () => {
172
- interface ComponentInstance {
173
- gridColumns: string;
174
- }
175
-
176
- it("defaults to fixed 16px gutters with no maxWidth", async () => {
171
+ describe("grid layout classes", () => {
172
+ it("applies center class by default", async () => {
177
173
  const wrapper = await mountSuspended(PageHeroHighlights);
178
- const vm = wrapper.vm as unknown as ComponentInstance;
179
- expect(vm.gridColumns).toBe("16px 1fr 16px");
174
+ expect(wrapper.find(".page-hero-highlights").classes()).toContain("center");
180
175
  });
181
176
 
182
- it("returns centered max-width columns when maxWidth is set and contentAlign is center", async () => {
183
- const wrapper = await mountSuspended(PageHeroHighlights, {
184
- props: { maxWidth: "1064px", contentAlign: "center" },
185
- });
186
- const vm = wrapper.vm as unknown as ComponentInstance;
187
- expect(vm.gridColumns).toBe("max(16px, (100% - 1064px) / 2) 1fr max(16px, (100% - 1064px) / 2)");
177
+ it("does not apply width-constrained class by default", async () => {
178
+ const wrapper = await mountSuspended(PageHeroHighlights);
179
+ expect(wrapper.find(".page-hero-highlights").classes()).not.toContain("width-constrained");
188
180
  });
189
181
 
190
- it("returns start-aligned columns when maxWidth is set and contentAlign is start", async () => {
182
+ it("applies width-constrained class when widthConstrained is true", async () => {
191
183
  const wrapper = await mountSuspended(PageHeroHighlights, {
192
- props: { maxWidth: "1064px", contentAlign: "start" },
184
+ props: { widthConstrained: true },
193
185
  });
194
- const vm = wrapper.vm as unknown as ComponentInstance;
195
- expect(vm.gridColumns).toBe("16px minmax(0, 1064px) minmax(16px, 1fr)");
186
+ expect(wrapper.find(".page-hero-highlights").classes()).toContain("width-constrained");
196
187
  });
197
188
 
198
- it("ignores contentAlign when maxWidth is not set", async () => {
189
+ it("applies start class when contentAlign is start", async () => {
199
190
  const wrapper = await mountSuspended(PageHeroHighlights, {
200
191
  props: { contentAlign: "start" },
201
192
  });
202
- const vm = wrapper.vm as unknown as ComponentInstance;
203
- expect(vm.gridColumns).toBe("16px 1fr 16px");
193
+ expect(wrapper.find(".page-hero-highlights").classes()).toContain("start");
194
+ expect(wrapper.find(".page-hero-highlights").classes()).not.toContain("center");
204
195
  });
205
196
  });
206
197
  });
@@ -1,7 +1,7 @@
1
1
  // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
2
 
3
3
  exports[`PageHeroHighlights > renders correct HTML structure 1`] = `
4
- "<div class="page-hero-highlights has-content-panel">
4
+ "<div class="page-hero-highlights center has-content-panel">
5
5
  <div class="header-row">
6
6
  <div class="header-slot">
7
7
  <h1>Page Title</h1>
@@ -4,12 +4,12 @@
4
4
  <template #layout-content>
5
5
  <PageHeroHighlights
6
6
  tag="section"
7
- max-width="1064px"
8
- content-align="start"
9
- :content-panel="true"
10
- :highlights-equal-widths="false"
11
- highlights-justify="start"
12
- :highlight-title-baseline="true"
7
+ :width-constrained="widthConstrained"
8
+ :content-align="contentAlign"
9
+ :content-panel="contentPanel"
10
+ :highlights-equal-widths="highlightsEqualWidths"
11
+ :highlights-justify="highlightsJustify"
12
+ :highlight-title-baseline="highlightTitleBaseline"
13
13
  :style-class-passthrough="['mbe-32']"
14
14
  >
15
15
  <template #header="{ headingId }">
@@ -38,6 +38,54 @@
38
38
  <p class="page-heading-2">Recent Activity</p>
39
39
  <p class="page-body-normal">Your most recent transactions and events will appear here.</p>
40
40
  </div>
41
+
42
+ <div class="demo-controls">
43
+ <p class="demo-controls__heading">Props</p>
44
+ <div class="demo-controls__toggles">
45
+ <ToggleSwitchWithLabelInline
46
+ v-model="widthConstrained"
47
+ name="widthConstrained"
48
+ label="widthConstrained"
49
+ />
50
+ <ToggleSwitchWithLabelInline v-model="contentPanel" name="contentPanel" label="contentPanel" />
51
+ <ToggleSwitchWithLabelInline
52
+ v-model="highlightsEqualWidths"
53
+ name="highlightsEqualWidths"
54
+ label="highlightsEqualWidths"
55
+ />
56
+ <ToggleSwitchWithLabelInline
57
+ v-model="highlightTitleBaseline"
58
+ name="highlightTitleBaseline"
59
+ label="highlightTitleBaseline"
60
+ />
61
+ </div>
62
+ <div class="demo-controls__selects">
63
+ <MultipleRadiobuttons
64
+ v-model="contentAlign"
65
+ v-model:field-data="contentAlignData"
66
+ name="contentAlign"
67
+ legend="contentAlign"
68
+ label="contentAlign"
69
+ error-message=""
70
+ :is-button="true"
71
+ :is-pill="true"
72
+ :field-has-error="false"
73
+ options-layout="inline"
74
+ />
75
+ <MultipleRadiobuttons
76
+ v-model="highlightsJustify"
77
+ v-model:field-data="highlightsJustifyData"
78
+ name="highlightsJustify"
79
+ legend="highlightsJustify"
80
+ label="highlightsJustify"
81
+ error-message=""
82
+ :is-button="true"
83
+ :is-pill="true"
84
+ :field-has-error="false"
85
+ options-layout="inline"
86
+ />
87
+ </div>
88
+ </div>
41
89
  </template>
42
90
  </PageHeroHighlights>
43
91
  </template>
@@ -46,6 +94,8 @@
46
94
  </template>
47
95
 
48
96
  <script setup lang="ts">
97
+ import type { IFormMultipleOptions } from "~/types/forms/types.forms";
98
+
49
99
  definePageMeta({
50
100
  layout: false,
51
101
  });
@@ -62,13 +112,52 @@ useHead({
62
112
  class: "page-hero-highlights-page",
63
113
  },
64
114
  });
115
+
116
+ const widthConstrained = ref(false);
117
+ const contentAlign = ref<"start" | "center">("start");
118
+ const contentPanel = ref(true);
119
+ const highlightsEqualWidths = ref(false);
120
+ const highlightsJustify = ref<"start" | "center" | "end" | "space-between" | "space-around">("start");
121
+ const highlightTitleBaseline = ref(true);
122
+
123
+ const contentAlignData = ref<IFormMultipleOptions>({
124
+ data: [
125
+ { id: "align-start", name: "contentAlign", value: "start", label: "start" },
126
+ { id: "align-center", name: "contentAlign", value: "center", label: "center" },
127
+ ],
128
+ total: 2,
129
+ skip: 0,
130
+ limit: 2,
131
+ });
132
+
133
+ const highlightsJustifyData = ref<IFormMultipleOptions>({
134
+ data: [
135
+ { id: "justify-start", name: "highlightsJustify", value: "start", label: "start" },
136
+ { id: "justify-center", name: "highlightsJustify", value: "center", label: "center" },
137
+ { id: "justify-end", name: "highlightsJustify", value: "end", label: "end" },
138
+ { id: "justify-space-between", name: "highlightsJustify", value: "space-between", label: "space-between" },
139
+ { id: "justify-space-around", name: "highlightsJustify", value: "space-around", label: "space-around" },
140
+ ],
141
+ total: 5,
142
+ skip: 0,
143
+ limit: 5,
144
+ });
65
145
  </script>
66
146
 
67
147
  <style lang="css">
68
148
  .page-hero-highlights-page {
69
149
  .page-hero-highlights {
150
+ /* Layout tokens */
151
+ --max-width: 1064px;
152
+ --page-hero-highlights-gutter-mobile: 16px;
153
+ --page-hero-highlights-gutter-tablet: 40px;
154
+ --page-hero-highlights-gutter-desktop: 32px;
155
+
70
156
  .page-hero-highlights-header {
71
- padding-block: 4rem 3rem;
157
+ /* User themable tokens */
158
+ --phh-padding-block-mobile: 1.6rem 6rem;
159
+ --phh-padding-block-tablet: 2.4rem 6rem;
160
+ --phh-padding-block-desktop: 4.2rem 7.4rem;
72
161
  }
73
162
 
74
163
  .content-row {
@@ -76,10 +165,40 @@ useHead({
76
165
  color: black;
77
166
 
78
167
  .some-class {
79
- min-height: 40svh;
168
+ padding-block-end: 2.4rem;
80
169
  }
81
170
  }
82
171
  }
83
172
  }
173
+
174
+ .demo-controls {
175
+ margin-block-start: 2.4rem;
176
+ padding: 1.6rem;
177
+ border: 1px solid var(--slate-06);
178
+ border-radius: 0.8rem;
179
+ background: var(--slate-01);
180
+
181
+ &__heading {
182
+ font-size: 1.1rem;
183
+ font-weight: 600;
184
+ text-transform: uppercase;
185
+ letter-spacing: 0.08em;
186
+ color: var(--slate-08);
187
+ margin-block-end: 1.2rem;
188
+ }
189
+
190
+ &__toggles {
191
+ display: flex;
192
+ flex-wrap: wrap;
193
+ gap: 0.4rem 2.4rem;
194
+ margin-block-end: 1.6rem;
195
+ }
196
+
197
+ &__selects {
198
+ display: flex;
199
+ flex-direction: column;
200
+ gap: 0.8rem;
201
+ }
202
+ }
84
203
  }
85
204
  </style>
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "srcdev-nuxt-components",
3
3
  "type": "module",
4
- "version": "9.1.7",
4
+ "version": "9.1.9",
5
5
  "main": "nuxt.config.ts",
6
6
  "types": "types.d.ts",
7
7
  "license": "MIT",