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,198 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { h } from "vue";
|
|
3
|
+
import { mountSuspended } from "@nuxt/test-utils/runtime";
|
|
4
|
+
import PageHeroHighlights from "../PageHeroHighlights.vue";
|
|
5
|
+
|
|
6
|
+
describe("PageHeroHighlights", () => {
|
|
7
|
+
it("mounts without error", async () => {
|
|
8
|
+
const wrapper = await mountSuspended(PageHeroHighlights);
|
|
9
|
+
expect(wrapper.vm).toBeTruthy();
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it("renders correct HTML structure", async () => {
|
|
13
|
+
const wrapper = await mountSuspended(PageHeroHighlights, {
|
|
14
|
+
slots: {
|
|
15
|
+
header: "<h1>Page Title</h1>",
|
|
16
|
+
highlights: "<div class='highlight'>Highlight 1</div>",
|
|
17
|
+
content: "<p>Content</p>",
|
|
18
|
+
},
|
|
19
|
+
});
|
|
20
|
+
expect(wrapper.html()).toMatchSnapshot();
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it("renders as a div by default", async () => {
|
|
24
|
+
const wrapper = await mountSuspended(PageHeroHighlights);
|
|
25
|
+
expect(wrapper.element.tagName.toLowerCase()).toBe("div");
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it("renders with the correct tag when tag prop is provided", async () => {
|
|
29
|
+
for (const tag of ["section", "main"] as const) {
|
|
30
|
+
const wrapper = await mountSuspended(PageHeroHighlights, {
|
|
31
|
+
props: { tag },
|
|
32
|
+
});
|
|
33
|
+
expect(wrapper.element.tagName.toLowerCase()).toBe(tag);
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it("renders header slot content", async () => {
|
|
38
|
+
const wrapper = await mountSuspended(PageHeroHighlights, {
|
|
39
|
+
slots: { header: "<h1 class='page-title'>Dashboard</h1>" },
|
|
40
|
+
});
|
|
41
|
+
expect(wrapper.find(".page-title").exists()).toBe(true);
|
|
42
|
+
expect(wrapper.html()).toContain("Dashboard");
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it("renders highlights slot content", async () => {
|
|
46
|
+
const wrapper = await mountSuspended(PageHeroHighlights, {
|
|
47
|
+
slots: {
|
|
48
|
+
highlights: [
|
|
49
|
+
"<div class='highlight-card'>Card 1</div>",
|
|
50
|
+
"<div class='highlight-card'>Card 2</div>",
|
|
51
|
+
].join(""),
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
expect(wrapper.findAll(".highlight-card").length).toBe(2);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it("renders content slot content", async () => {
|
|
58
|
+
const wrapper = await mountSuspended(PageHeroHighlights, {
|
|
59
|
+
slots: { content: "<p class='body-text'>Page body</p>" },
|
|
60
|
+
});
|
|
61
|
+
expect(wrapper.find(".body-text").exists()).toBe(true);
|
|
62
|
+
expect(wrapper.html()).toContain("Page body");
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it("content slot is rendered inside .content-slot", async () => {
|
|
66
|
+
const wrapper = await mountSuspended(PageHeroHighlights, {
|
|
67
|
+
slots: { content: "<p class='body-text'>Page body</p>" },
|
|
68
|
+
});
|
|
69
|
+
expect(wrapper.find(".content-slot .body-text").exists()).toBe(true);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it("header slot is rendered inside .header-slot", async () => {
|
|
73
|
+
const wrapper = await mountSuspended(PageHeroHighlights, {
|
|
74
|
+
slots: { header: "<h1 class='page-title'>Dashboard</h1>" },
|
|
75
|
+
});
|
|
76
|
+
expect(wrapper.find(".header-slot .page-title").exists()).toBe(true);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it("adds aria-labelledby when tag is section", async () => {
|
|
80
|
+
const wrapper = await mountSuspended(PageHeroHighlights, {
|
|
81
|
+
props: { tag: "section" },
|
|
82
|
+
});
|
|
83
|
+
expect(wrapper.find(".page-hero-highlights").attributes("aria-labelledby")).toBeTruthy();
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it("adds aria-labelledby when tag is main", async () => {
|
|
87
|
+
const wrapper = await mountSuspended(PageHeroHighlights, {
|
|
88
|
+
props: { tag: "main" },
|
|
89
|
+
});
|
|
90
|
+
expect(wrapper.find(".page-hero-highlights").attributes("aria-labelledby")).toBeTruthy();
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it("does not add aria-labelledby when tag is div", async () => {
|
|
94
|
+
const wrapper = await mountSuspended(PageHeroHighlights, {
|
|
95
|
+
props: { tag: "div" },
|
|
96
|
+
});
|
|
97
|
+
expect(wrapper.find(".page-hero-highlights").attributes("aria-labelledby")).toBeUndefined();
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it("exposes headingId via header scoped slot and aria-labelledby matches", async () => {
|
|
101
|
+
const wrapper = await mountSuspended(PageHeroHighlights, {
|
|
102
|
+
props: { tag: "section" },
|
|
103
|
+
slots: {
|
|
104
|
+
header: (props: Record<string, unknown>) =>
|
|
105
|
+
h("h1", { id: props.headingId, class: "page-title" }, "Dashboard"),
|
|
106
|
+
},
|
|
107
|
+
});
|
|
108
|
+
const labelledBy = wrapper.find(".page-hero-highlights").attributes("aria-labelledby");
|
|
109
|
+
expect(labelledBy).toBeTruthy();
|
|
110
|
+
expect(wrapper.find(".page-title").attributes("id")).toBe(labelledBy);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it("applies flexible-widths class by default", async () => {
|
|
114
|
+
const wrapper = await mountSuspended(PageHeroHighlights);
|
|
115
|
+
expect(wrapper.find(".highlights-row").classes()).toContain("flexible-widths");
|
|
116
|
+
expect(wrapper.find(".highlights-row").classes()).not.toContain("equal-widths");
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it("applies equal-widths class when highlightsEqualWidths is true", async () => {
|
|
120
|
+
const wrapper = await mountSuspended(PageHeroHighlights, {
|
|
121
|
+
props: { highlightsEqualWidths: true },
|
|
122
|
+
});
|
|
123
|
+
expect(wrapper.find(".highlights-row").classes()).toContain("equal-widths");
|
|
124
|
+
expect(wrapper.find(".highlights-row").classes()).not.toContain("flexible-widths");
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it("applies justify-start class by default", async () => {
|
|
128
|
+
const wrapper = await mountSuspended(PageHeroHighlights);
|
|
129
|
+
expect(wrapper.find(".highlights-row").classes()).toContain("justify-start");
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it("applies the correct justify class for each highlightsJustify value", async () => {
|
|
133
|
+
const values = ["start", "center", "end", "space-between", "space-around"] as const;
|
|
134
|
+
for (const value of values) {
|
|
135
|
+
const wrapper = await mountSuspended(PageHeroHighlights, {
|
|
136
|
+
props: { highlightsJustify: value },
|
|
137
|
+
});
|
|
138
|
+
expect(wrapper.find(".highlights-row").classes()).toContain(`justify-${value}`);
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it("does not apply highlight-title-baseline class by default", async () => {
|
|
143
|
+
const wrapper = await mountSuspended(PageHeroHighlights);
|
|
144
|
+
expect(wrapper.find(".page-hero-highlights").classes()).not.toContain("highlight-title-baseline");
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it("applies highlight-title-baseline class when highlightTitleBaseline is true", async () => {
|
|
148
|
+
const wrapper = await mountSuspended(PageHeroHighlights, {
|
|
149
|
+
props: { highlightTitleBaseline: true },
|
|
150
|
+
});
|
|
151
|
+
expect(wrapper.find(".page-hero-highlights").classes()).toContain("highlight-title-baseline");
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
it("applies styleClassPassthrough classes", async () => {
|
|
155
|
+
const wrapper = await mountSuspended(PageHeroHighlights, {
|
|
156
|
+
props: { styleClassPassthrough: ["extra-class", "another-class"] },
|
|
157
|
+
});
|
|
158
|
+
const el = wrapper.find(".page-hero-highlights");
|
|
159
|
+
expect(el.classes()).toContain("extra-class");
|
|
160
|
+
expect(el.classes()).toContain("another-class");
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
describe("gridColumns", () => {
|
|
164
|
+
interface ComponentInstance {
|
|
165
|
+
gridColumns: string;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
it("defaults to fixed 16px gutters with no maxWidth", async () => {
|
|
169
|
+
const wrapper = await mountSuspended(PageHeroHighlights);
|
|
170
|
+
const vm = wrapper.vm as unknown as ComponentInstance;
|
|
171
|
+
expect(vm.gridColumns).toBe("16px 1fr 16px");
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
it("returns centered max-width columns when maxWidth is set and contentAlign is center", async () => {
|
|
175
|
+
const wrapper = await mountSuspended(PageHeroHighlights, {
|
|
176
|
+
props: { maxWidth: "1064px", contentAlign: "center" },
|
|
177
|
+
});
|
|
178
|
+
const vm = wrapper.vm as unknown as ComponentInstance;
|
|
179
|
+
expect(vm.gridColumns).toBe("max(16px, (100% - 1064px) / 2) 1fr max(16px, (100% - 1064px) / 2)");
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
it("returns start-aligned columns when maxWidth is set and contentAlign is start", async () => {
|
|
183
|
+
const wrapper = await mountSuspended(PageHeroHighlights, {
|
|
184
|
+
props: { maxWidth: "1064px", contentAlign: "start" },
|
|
185
|
+
});
|
|
186
|
+
const vm = wrapper.vm as unknown as ComponentInstance;
|
|
187
|
+
expect(vm.gridColumns).toBe("16px minmax(0, 1064px) 1fr");
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
it("ignores contentAlign when maxWidth is not set", async () => {
|
|
191
|
+
const wrapper = await mountSuspended(PageHeroHighlights, {
|
|
192
|
+
props: { contentAlign: "start" },
|
|
193
|
+
});
|
|
194
|
+
const vm = wrapper.vm as unknown as ComponentInstance;
|
|
195
|
+
expect(vm.gridColumns).toBe("16px 1fr 16px");
|
|
196
|
+
});
|
|
197
|
+
});
|
|
198
|
+
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
|
2
|
+
|
|
3
|
+
exports[`PageHeroHighlights > renders correct HTML structure 1`] = `
|
|
4
|
+
"<div class="page-hero-highlights">
|
|
5
|
+
<div class="header-row">
|
|
6
|
+
<div class="header-slot">
|
|
7
|
+
<h1>Page Title</h1>
|
|
8
|
+
</div>
|
|
9
|
+
</div>
|
|
10
|
+
<div class="highlights-row flexible-widths justify-start">
|
|
11
|
+
<div class="highlight">Highlight 1</div>
|
|
12
|
+
</div>
|
|
13
|
+
<div class="content-row">
|
|
14
|
+
<div class="content-slot">
|
|
15
|
+
<p>Content</p>
|
|
16
|
+
</div>
|
|
17
|
+
</div>
|
|
18
|
+
</div>"
|
|
19
|
+
`;
|
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
<div ref="containerGlowWrapper" class="container-glow-wrapper" :class="elementClasses">
|
|
3
3
|
<component
|
|
4
4
|
:is="tag"
|
|
5
|
-
v-for="(
|
|
6
|
-
:key="
|
|
5
|
+
v-for="(_, name) in $slots"
|
|
6
|
+
:key="name"
|
|
7
7
|
ref="containerGlowItem"
|
|
8
8
|
class="container-glow-core"
|
|
9
9
|
>
|
|
10
10
|
<div class="glows"></div>
|
|
11
|
-
<slot :name="
|
|
11
|
+
<slot :name="name"></slot>
|
|
12
12
|
</component>
|
|
13
13
|
</div>
|
|
14
14
|
</template>
|
|
@@ -23,30 +23,23 @@ interface Config {
|
|
|
23
23
|
inactiveOpacity: number;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
spread: 80,
|
|
44
|
-
blur: 20,
|
|
45
|
-
gap: 32,
|
|
46
|
-
vertical: false,
|
|
47
|
-
inactiveOpacity: 0,
|
|
48
|
-
}),
|
|
49
|
-
},
|
|
26
|
+
interface Props {
|
|
27
|
+
tag?: string;
|
|
28
|
+
config?: Config;
|
|
29
|
+
styleClassPassthrough?: string | string[];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
33
|
+
tag: "div",
|
|
34
|
+
config: () => ({
|
|
35
|
+
proximity: 40,
|
|
36
|
+
spread: 80,
|
|
37
|
+
blur: 20,
|
|
38
|
+
gap: 32,
|
|
39
|
+
vertical: false,
|
|
40
|
+
inactiveOpacity: 0,
|
|
41
|
+
}),
|
|
42
|
+
styleClassPassthrough: () => [],
|
|
50
43
|
});
|
|
51
44
|
|
|
52
45
|
const { elementClasses } = useStyleClassPassthrough(props.styleClassPassthrough);
|
|
@@ -86,134 +86,135 @@ const { elementClasses } = useStyleClassPassthrough(props.styleClassPassthrough)
|
|
|
86
86
|
|
|
87
87
|
<style lang="css">
|
|
88
88
|
@layer components {
|
|
89
|
-
.input-button-core {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
89
|
+
.input-button-core {
|
|
90
|
+
display: grid;
|
|
91
|
+
grid-auto-flow: column;
|
|
92
|
+
gap: var(--button-icon-gap);
|
|
93
|
+
justify-content: center;
|
|
94
|
+
align-items: center;
|
|
95
|
+
box-sizing: content-box;
|
|
96
|
+
border-radius: var(--button-border-radius);
|
|
97
|
+
font-family: var(--font-family);
|
|
98
|
+
padding-inline: var(--button-padding-inline);
|
|
99
|
+
padding-block: var(--button-padding-block);
|
|
100
|
+
touch-action: manipulation;
|
|
101
|
+
overflow: hidden;
|
|
102
|
+
transition: all var(--control-transition-duration) var(--control-transition-ease);
|
|
103
|
+
|
|
104
|
+
&.is-link {
|
|
105
|
+
display: inline-grid;
|
|
106
|
+
}
|
|
107
107
|
|
|
108
|
-
|
|
108
|
+
/*
|
|
109
109
|
* Variants
|
|
110
110
|
**/
|
|
111
|
-
|
|
112
|
-
background-color: var(--theme-button-primary-surface);
|
|
113
|
-
color: var(--theme-button-primary-text);
|
|
114
|
-
border: var(--button-border-width) solid var(--theme-button-primary-border);
|
|
115
|
-
outline: var(--button-outline-width) solid var(--theme-button-primary-outline);
|
|
116
|
-
|
|
117
|
-
&:hover,
|
|
118
|
-
&:focus-visible {
|
|
119
|
-
background-color: var(--theme-button-secondary-surface);
|
|
120
|
-
color: var(--theme-button-secondary-text);
|
|
121
|
-
border-color: var(--theme-button-primary-border-active);
|
|
122
|
-
outline-color: var(--theme-button-primary-outline-active);
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
&.is-pending {
|
|
126
|
-
background-color: color-mix(in oklab, var(--theme-button-primary-surface) 50%, transparent);
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
&.secondary {
|
|
131
|
-
background-color: var(--theme-button-secondary-surface);
|
|
132
|
-
border: var(--button-border-width) solid var(--theme-button-secondary-border);
|
|
133
|
-
color: var(--theme-button-secondary-text);
|
|
134
|
-
outline: var(--button-outline-width) solid var(--theme-button-secondary-outline);
|
|
135
|
-
|
|
136
|
-
&:hover,
|
|
137
|
-
&:focus-visible {
|
|
111
|
+
&.primary {
|
|
138
112
|
background-color: var(--theme-button-primary-surface);
|
|
139
113
|
color: var(--theme-button-primary-text);
|
|
140
|
-
border
|
|
141
|
-
outline
|
|
114
|
+
border: var(--button-border-width) solid var(--theme-button-primary-border);
|
|
115
|
+
outline: var(--button-outline-width) solid var(--theme-button-primary-outline);
|
|
116
|
+
|
|
117
|
+
&:hover,
|
|
118
|
+
&:focus-visible {
|
|
119
|
+
background-color: var(--theme-button-secondary-surface);
|
|
120
|
+
color: var(--theme-button-secondary-text);
|
|
121
|
+
border-color: var(--theme-button-primary-border-active);
|
|
122
|
+
outline-color: var(--theme-button-primary-outline-active);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
&.is-pending {
|
|
126
|
+
background-color: color-mix(in oklab, var(--theme-button-primary-surface) 50%, transparent);
|
|
127
|
+
}
|
|
142
128
|
}
|
|
143
|
-
}
|
|
144
129
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
130
|
+
&.secondary {
|
|
131
|
+
background-color: var(--theme-button-secondary-surface);
|
|
132
|
+
border: var(--button-border-width) solid var(--theme-button-secondary-border);
|
|
133
|
+
color: var(--theme-button-secondary-text);
|
|
134
|
+
outline: var(--button-outline-width) solid var(--theme-button-secondary-outline);
|
|
135
|
+
|
|
136
|
+
&:hover,
|
|
137
|
+
&:focus-visible {
|
|
138
|
+
background-color: var(--theme-button-primary-surface);
|
|
139
|
+
color: var(--theme-button-primary-text);
|
|
140
|
+
border-color: var(--theme-button-secondary-border-active);
|
|
141
|
+
outline-color: var(--theme-button-secondary-outline-active);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
151
144
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
border
|
|
155
|
-
|
|
145
|
+
&.tertiary {
|
|
146
|
+
background-color: var(--theme-button-tertiary-surface);
|
|
147
|
+
border: var(--button-border-width) solid transparent;
|
|
148
|
+
color: var(--theme-button-tertiary-text);
|
|
149
|
+
text-decoration: underline;
|
|
150
|
+
outline: var(--button-outline-width) solid transparent;
|
|
151
|
+
|
|
152
|
+
&:hover,
|
|
153
|
+
&:focus-visible {
|
|
154
|
+
border-color: var(--theme-button-tertiary-border-active);
|
|
155
|
+
outline-color: var(--theme-button-tertiary-outline-active);
|
|
156
|
+
}
|
|
156
157
|
}
|
|
157
|
-
}
|
|
158
158
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
159
|
+
&.primary,
|
|
160
|
+
&.secondary,
|
|
161
|
+
&.tertiary {
|
|
162
|
+
&.pill {
|
|
163
|
+
border-radius: 100vw;
|
|
164
|
+
}
|
|
164
165
|
}
|
|
165
|
-
}
|
|
166
166
|
|
|
167
|
-
|
|
167
|
+
/*
|
|
168
168
|
* Shared States
|
|
169
169
|
**/
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
&:focus-visible {
|
|
175
|
-
outline-width: var(--button-focus-ring-width);
|
|
176
|
-
outline-offset: var(--button-focus-ring-offset);
|
|
177
|
-
}
|
|
170
|
+
&:hover {
|
|
171
|
+
cursor: pointer;
|
|
172
|
+
}
|
|
178
173
|
|
|
179
|
-
&[readonly] {
|
|
180
|
-
opacity: 0.5;
|
|
181
|
-
&:hover,
|
|
182
174
|
&:focus-visible {
|
|
183
|
-
|
|
184
|
-
|
|
175
|
+
outline-width: var(--button-focus-ring-width);
|
|
176
|
+
outline-offset: var(--button-focus-ring-offset);
|
|
185
177
|
}
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
.button-text {
|
|
189
|
-
display: inline-block;
|
|
190
|
-
white-space: nowrap;
|
|
191
|
-
font-size: var(--button-font-size);
|
|
192
|
-
line-height: var(--button-line-height);
|
|
193
|
-
font-weight: var(--button-font-weight);
|
|
194
|
-
}
|
|
195
178
|
|
|
196
|
-
|
|
197
|
-
|
|
179
|
+
&[readonly] {
|
|
180
|
+
opacity: 0.5;
|
|
181
|
+
&:hover,
|
|
182
|
+
&:focus-visible {
|
|
183
|
+
cursor: not-allowed;
|
|
184
|
+
pointer-events: none;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
198
187
|
|
|
199
|
-
.
|
|
200
|
-
aspect-ratio: 1;
|
|
188
|
+
.button-text {
|
|
201
189
|
display: inline-block;
|
|
202
|
-
|
|
203
|
-
|
|
190
|
+
white-space: nowrap;
|
|
191
|
+
font-size: var(--button-font-size);
|
|
192
|
+
line-height: var(--button-line-height);
|
|
193
|
+
font-weight: var(--button-font-weight);
|
|
194
|
+
text-transform: var(--button-text-transform);
|
|
204
195
|
}
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
&.icon-only {
|
|
208
|
-
aspect-ratio: 1;
|
|
209
|
-
border-radius: var(--button-border-radius-icon-only);
|
|
210
|
-
margin: 0;
|
|
211
|
-
padding: 0;
|
|
212
196
|
|
|
213
197
|
.btn-icon {
|
|
214
|
-
|
|
198
|
+
display: flex;
|
|
199
|
+
|
|
200
|
+
.icon {
|
|
201
|
+
aspect-ratio: 1;
|
|
202
|
+
display: inline-block;
|
|
203
|
+
height: var(--input-icon-size);
|
|
204
|
+
width: var(--input-icon-size);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
&.icon-only {
|
|
209
|
+
aspect-ratio: 1;
|
|
210
|
+
border-radius: var(--button-border-radius-icon-only);
|
|
211
|
+
margin: 0;
|
|
212
|
+
padding: 0;
|
|
213
|
+
|
|
214
|
+
.btn-icon {
|
|
215
|
+
margin: 1.2rem;
|
|
216
|
+
}
|
|
215
217
|
}
|
|
216
218
|
}
|
|
217
219
|
}
|
|
218
|
-
}
|
|
219
220
|
</style>
|
|
@@ -5,8 +5,8 @@ import StorybookComponent from "../GlowingBorder.vue";
|
|
|
5
5
|
interface GlowingBorderStoryArgs {
|
|
6
6
|
variant: "vivid" | "subtle" | "silver" | "steel";
|
|
7
7
|
content: string;
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
showCardCore: boolean;
|
|
9
|
+
cardCoreVariant: "solid" | "subtle" | "soft" | "outline";
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
export default {
|
|
@@ -28,17 +28,17 @@ export default {
|
|
|
28
28
|
category: "Content",
|
|
29
29
|
},
|
|
30
30
|
},
|
|
31
|
-
|
|
31
|
+
showCardCore: {
|
|
32
32
|
control: { type: "boolean" },
|
|
33
|
-
description: "Show content wrapped in a
|
|
33
|
+
description: "Show content wrapped in a CardCore component",
|
|
34
34
|
table: {
|
|
35
35
|
category: "Content",
|
|
36
36
|
},
|
|
37
37
|
},
|
|
38
|
-
|
|
38
|
+
cardCoreVariant: {
|
|
39
39
|
control: { type: "select" },
|
|
40
40
|
options: ["solid", "subtle", "soft", "outline"],
|
|
41
|
-
description: "
|
|
41
|
+
description: "CardCore variant when showCardCore is true",
|
|
42
42
|
table: {
|
|
43
43
|
category: "Content",
|
|
44
44
|
},
|
|
@@ -54,8 +54,8 @@ export default {
|
|
|
54
54
|
variant: "vivid",
|
|
55
55
|
content:
|
|
56
56
|
"This is default slot content for the GlowingBorder component. As it's a slot, any HTML content can be placed here.",
|
|
57
|
-
|
|
58
|
-
|
|
57
|
+
showCardCore: false,
|
|
58
|
+
cardCoreVariant: "solid",
|
|
59
59
|
},
|
|
60
60
|
} as Meta<GlowingBorderStoryArgs>;
|
|
61
61
|
|
|
@@ -70,32 +70,32 @@ const Template: StoryFn<GlowingBorderStoryArgs> = (args) => ({
|
|
|
70
70
|
:variant="args.variant"
|
|
71
71
|
:style-class-passthrough="['storybook-demo']"
|
|
72
72
|
>
|
|
73
|
-
<div v-if="!args.
|
|
73
|
+
<div v-if="!args.showCardCore" style="padding: 20px; color: var(--slate-02);">
|
|
74
74
|
<h3 style="margin: 0 0 12px 0; font-size: 1.5rem; font-weight: 600;">GlowingBorder Header</h3>
|
|
75
75
|
<h4 style="margin: 0 0 16px 0; font-size: 1.25rem; font-weight: 500;">GlowingBorder Content</h4>
|
|
76
76
|
<p style="margin: 0 0 12px 0; line-height: 1.6;">{{ args.content }}</p>
|
|
77
77
|
<p style="margin: 0; font-size: 0.875rem; color: var(--slate-09);">GlowingBorder Footer</p>
|
|
78
78
|
</div>
|
|
79
79
|
|
|
80
|
-
<!--
|
|
81
|
-
<
|
|
80
|
+
<!-- CardCore version -->
|
|
81
|
+
<CardCore
|
|
82
82
|
v-else
|
|
83
|
-
:variant="args.
|
|
83
|
+
:variant="args.cardCoreVariant"
|
|
84
84
|
:has-dividers="true"
|
|
85
85
|
:no-outline="false"
|
|
86
86
|
>
|
|
87
87
|
<template #header>
|
|
88
|
-
<h2 style="margin: 0; font-size: 1.5rem; font-weight: 600;">
|
|
88
|
+
<h2 style="margin: 0; font-size: 1.5rem; font-weight: 600;">CardCore Header</h2>
|
|
89
89
|
</template>
|
|
90
90
|
<template #default>
|
|
91
|
-
<h3 style="margin: 0 0 12px 0; font-size: 1.25rem; font-weight: 500; color: var(--slate-02);">
|
|
91
|
+
<h3 style="margin: 0 0 12px 0; font-size: 1.25rem; font-weight: 500; color: var(--slate-02);">CardCore Content</h3>
|
|
92
92
|
<p style="margin: 0 0 12px 0; line-height: 1.6; color: var(--slate-02);">{{ args.content }}</p>
|
|
93
93
|
<p style="margin: 0; line-height: 1.6; color: var(--slate-02);">This demonstrates how GlowingBorder can wrap other components.</p>
|
|
94
94
|
</template>
|
|
95
95
|
<template #footer>
|
|
96
|
-
<p style="margin: 0; font-size: 0.875rem; color: var(--slate-02);">
|
|
96
|
+
<p style="margin: 0; font-size: 0.875rem; color: var(--slate-02);">CardCore Footer</p>
|
|
97
97
|
</template>
|
|
98
|
-
</
|
|
98
|
+
</CardCore>
|
|
99
99
|
</StorybookComponent>
|
|
100
100
|
</div>
|
|
101
101
|
`,
|
|
@@ -131,11 +131,11 @@ Steel.args = {
|
|
|
131
131
|
"Steel variant provides a cool, industrial glow effect that's perfect for technical or utilitarian design themes.",
|
|
132
132
|
};
|
|
133
133
|
|
|
134
|
-
export const
|
|
135
|
-
|
|
134
|
+
export const WithCardCore = Template.bind({});
|
|
135
|
+
WithCardCore.args = {
|
|
136
136
|
variant: "vivid",
|
|
137
137
|
content:
|
|
138
|
-
"This example shows how GlowingBorder can enhance other components like
|
|
139
|
-
|
|
140
|
-
|
|
138
|
+
"This example shows how GlowingBorder can enhance other components like CardCore, creating layered visual effects.",
|
|
139
|
+
showCardCore: true,
|
|
140
|
+
cardCoreVariant: "solid",
|
|
141
141
|
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { MaybeRefOrGetter } from "vue";
|
|
2
|
+
|
|
3
|
+
const LABELLED_TAGS = new Set(["section", "main", "article", "aside"]);
|
|
4
|
+
|
|
5
|
+
export function useAriaLabelledById(tag: MaybeRefOrGetter<string>) {
|
|
6
|
+
const headingId = useId();
|
|
7
|
+
|
|
8
|
+
const ariaLabelledby = computed(() => {
|
|
9
|
+
return LABELLED_TAGS.has(toValue(tag)) ? headingId : undefined;
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
return { headingId, ariaLabelledby };
|
|
13
|
+
}
|