srcdev-nuxt-components 9.0.14 → 9.0.16
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-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 +14 -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 +139 -0
- package/app/components/02.molecules/navigation/navigation-horizontal/NavigationHorizontalAdvanced.vue +172 -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 +493 -0
- package/app/pages/ui/services/services-section/[slug].vue +3 -1
- package/nuxt.config.ts +4 -1
- 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
|
@@ -1,465 +0,0 @@
|
|
|
1
|
-
import type { Meta, StoryFn } from "@nuxtjs/storybook";
|
|
2
|
-
import ContentContainerComponent from "../ContentContainer.vue";
|
|
3
|
-
|
|
4
|
-
// Define the args interface
|
|
5
|
-
interface ContentContainerArgs {
|
|
6
|
-
dataTestid: string;
|
|
7
|
-
tag: string;
|
|
8
|
-
id?: string;
|
|
9
|
-
styleClassPassthrough: string[];
|
|
10
|
-
isLandmark: boolean;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export default {
|
|
14
|
-
title: "Components/Layouts/ContentContainer",
|
|
15
|
-
component: ContentContainerComponent,
|
|
16
|
-
argTypes: {
|
|
17
|
-
// Semantic
|
|
18
|
-
tag: {
|
|
19
|
-
control: { type: "select" },
|
|
20
|
-
options: ["div", "section", "article", "aside", "header", "footer", "main", "nav", "ul", "ol"],
|
|
21
|
-
description: "HTML tag to render as",
|
|
22
|
-
table: {
|
|
23
|
-
category: "Semantic",
|
|
24
|
-
},
|
|
25
|
-
},
|
|
26
|
-
isLandmark: {
|
|
27
|
-
control: { type: "boolean" },
|
|
28
|
-
description: "Whether this element should be a landmark (adds tabindex and aria-label)",
|
|
29
|
-
table: {
|
|
30
|
-
category: "Accessibility",
|
|
31
|
-
},
|
|
32
|
-
},
|
|
33
|
-
// Content
|
|
34
|
-
dataTestid: {
|
|
35
|
-
control: { type: "text" },
|
|
36
|
-
description: "Test ID for the inner content container element",
|
|
37
|
-
table: {
|
|
38
|
-
category: "Testing",
|
|
39
|
-
},
|
|
40
|
-
},
|
|
41
|
-
id: {
|
|
42
|
-
control: { type: "text" },
|
|
43
|
-
description: "ID attribute for the component",
|
|
44
|
-
table: {
|
|
45
|
-
category: "HTML",
|
|
46
|
-
},
|
|
47
|
-
},
|
|
48
|
-
// Hide complex props
|
|
49
|
-
styleClassPassthrough: {
|
|
50
|
-
table: {
|
|
51
|
-
disable: true,
|
|
52
|
-
},
|
|
53
|
-
},
|
|
54
|
-
},
|
|
55
|
-
args: {
|
|
56
|
-
tag: "div",
|
|
57
|
-
isLandmark: false,
|
|
58
|
-
dataTestid: "content-container",
|
|
59
|
-
id: "",
|
|
60
|
-
styleClassPassthrough: [],
|
|
61
|
-
},
|
|
62
|
-
parameters: {
|
|
63
|
-
docs: {
|
|
64
|
-
description: {
|
|
65
|
-
component:
|
|
66
|
-
"A responsive content container that uses CSS Container Queries to provide optimal content width. Automatically adjusts from fluid width on small screens to a maximum 1064px width on larger screens (≥1064px) with proper gutters.",
|
|
67
|
-
},
|
|
68
|
-
},
|
|
69
|
-
},
|
|
70
|
-
} as Meta<ContentContainerArgs>;
|
|
71
|
-
|
|
72
|
-
const Template: StoryFn<ContentContainerArgs> = (args) => ({
|
|
73
|
-
components: { ContentContainerComponent },
|
|
74
|
-
setup() {
|
|
75
|
-
const sampleContent = {
|
|
76
|
-
shortText: "Brief sample content.",
|
|
77
|
-
mediumText:
|
|
78
|
-
"This is a medium-length text example that demonstrates how the content container adapts to different screen sizes and maintains optimal reading width.",
|
|
79
|
-
longText:
|
|
80
|
-
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium.",
|
|
81
|
-
};
|
|
82
|
-
|
|
83
|
-
return { args, sampleContent };
|
|
84
|
-
},
|
|
85
|
-
template: `
|
|
86
|
-
<div style="background: linear-gradient(135deg, #f0f9ff 0%, #e0f2fe 100%); min-height: 100vh; padding: 20px 0;">
|
|
87
|
-
<ContentContainerComponent
|
|
88
|
-
:data-testid="args.dataTestid"
|
|
89
|
-
:tag="args.tag"
|
|
90
|
-
:id="args.id || undefined"
|
|
91
|
-
:style-class-passthrough="args.styleClassPassthrough"
|
|
92
|
-
:is-landmark="args.isLandmark"
|
|
93
|
-
>
|
|
94
|
-
<div style="padding: 20px; background: white; border-radius: 12px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);">
|
|
95
|
-
<h2 style="margin: 0 0 16px 0; color: #374151; font-size: 24px;">
|
|
96
|
-
Content Container Example
|
|
97
|
-
</h2>
|
|
98
|
-
<p style="margin: 0 0 16px 0; color: #6b7280; line-height: 1.6;">
|
|
99
|
-
{{ sampleContent.longText }}
|
|
100
|
-
</p>
|
|
101
|
-
<div style="padding: 16px; background: #f3f4f6; border-radius: 8px; margin: 16px 0;">
|
|
102
|
-
<h3 style="margin: 0 0 8px 0; color: #374151; font-size: 16px;">Container Behavior</h3>
|
|
103
|
-
<ul style="margin: 0; color: #6b7280; line-height: 1.5; padding-left: 20px;">
|
|
104
|
-
<li><strong>Small screens:</strong> Fluid width with 16px gutters</li>
|
|
105
|
-
<li><strong>≥1064px screens:</strong> Fixed 1064px max-width, centered</li>
|
|
106
|
-
<li><strong>Container queries:</strong> Responsive layout without media queries</li>
|
|
107
|
-
</ul>
|
|
108
|
-
</div>
|
|
109
|
-
<p style="margin: 0; color: #6b7280; font-size: 14px; font-family: monospace; background: #e5e7eb; padding: 8px; border-radius: 4px;">
|
|
110
|
-
Tag: {{ args.tag }} | Landmark: {{ args.isLandmark ? 'Yes' : 'No' }}
|
|
111
|
-
</p>
|
|
112
|
-
</div>
|
|
113
|
-
</ContentContainerComponent>
|
|
114
|
-
</div>
|
|
115
|
-
`,
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
// ===== BASIC STORIES =====
|
|
119
|
-
|
|
120
|
-
export const Default = Template.bind({});
|
|
121
|
-
Default.args = {};
|
|
122
|
-
|
|
123
|
-
// ===== SEMANTIC TAG STORIES =====
|
|
124
|
-
|
|
125
|
-
const SemanticTemplate: StoryFn<ContentContainerArgs> = (args) => ({
|
|
126
|
-
components: { ContentContainerComponent },
|
|
127
|
-
setup() {
|
|
128
|
-
const getTagInfo = (tag: string) => {
|
|
129
|
-
const tagInfo = {
|
|
130
|
-
div: { description: "Generic container", use: "Default wrapper element" },
|
|
131
|
-
section: { description: "Thematic grouping", use: "Groups related content together" },
|
|
132
|
-
article: { description: "Self-contained content", use: "Blog posts, news articles, user comments" },
|
|
133
|
-
aside: { description: "Tangentially related", use: "Sidebars, pull quotes, advertising" },
|
|
134
|
-
header: { description: "Introductory content", use: "Page headers, section headers" },
|
|
135
|
-
footer: { description: "Footer information", use: "Page footers, section footers" },
|
|
136
|
-
main: { description: "Main content", use: "Primary content of the document" },
|
|
137
|
-
nav: { description: "Navigation links", use: "Navigation menus, breadcrumbs" },
|
|
138
|
-
};
|
|
139
|
-
return tagInfo[tag as keyof typeof tagInfo] || { description: "Generic container", use: "Default usage" };
|
|
140
|
-
};
|
|
141
|
-
|
|
142
|
-
return { args, getTagInfo };
|
|
143
|
-
},
|
|
144
|
-
template: `
|
|
145
|
-
<div style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); min-height: 60vh; padding: 40px 0;">
|
|
146
|
-
<ContentContainerComponent
|
|
147
|
-
:data-testid="args.dataTestid"
|
|
148
|
-
:tag="args.tag"
|
|
149
|
-
:style-class-passthrough="args.styleClassPassthrough"
|
|
150
|
-
:is-landmark="args.isLandmark"
|
|
151
|
-
>
|
|
152
|
-
<div style="padding: 30px; background: white; border-radius: 12px; text-align: center;">
|
|
153
|
-
<div style="display: inline-block; padding: 8px 16px; background: #667eea; color: white; border-radius: 6px; font-family: monospace; font-size: 16px; margin-bottom: 20px;">
|
|
154
|
-
<{{ args.tag }}>
|
|
155
|
-
</div>
|
|
156
|
-
<h2 style="margin: 0 0 12px 0; color: #374151; font-size: 22px;">
|
|
157
|
-
{{ getTagInfo(args.tag).description }}
|
|
158
|
-
</h2>
|
|
159
|
-
<p style="margin: 0 0 20px 0; color: #6b7280; line-height: 1.5; max-width: 400px; margin-left: auto; margin-right: auto;">
|
|
160
|
-
{{ getTagInfo(args.tag).use }}
|
|
161
|
-
</p>
|
|
162
|
-
<div style="padding: 16px; background: #f8fafc; border-radius: 8px; border-left: 4px solid #667eea;">
|
|
163
|
-
<p style="margin: 0; color: #374151; font-size: 14px;">
|
|
164
|
-
This semantic element helps structure your content meaningfully for both users and assistive technologies.
|
|
165
|
-
</p>
|
|
166
|
-
</div>
|
|
167
|
-
</div>
|
|
168
|
-
</ContentContainerComponent>
|
|
169
|
-
</div>
|
|
170
|
-
`,
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
export const SemanticSection = SemanticTemplate.bind({});
|
|
174
|
-
SemanticSection.args = {
|
|
175
|
-
tag: "section",
|
|
176
|
-
};
|
|
177
|
-
|
|
178
|
-
export const SemanticArticle = SemanticTemplate.bind({});
|
|
179
|
-
SemanticArticle.args = {
|
|
180
|
-
tag: "article",
|
|
181
|
-
};
|
|
182
|
-
|
|
183
|
-
export const SemanticAside = SemanticTemplate.bind({});
|
|
184
|
-
SemanticAside.args = {
|
|
185
|
-
tag: "aside",
|
|
186
|
-
};
|
|
187
|
-
|
|
188
|
-
export const SemanticHeader = SemanticTemplate.bind({});
|
|
189
|
-
SemanticHeader.args = {
|
|
190
|
-
tag: "header",
|
|
191
|
-
};
|
|
192
|
-
|
|
193
|
-
export const SemanticMain = SemanticTemplate.bind({});
|
|
194
|
-
SemanticMain.args = {
|
|
195
|
-
tag: "main",
|
|
196
|
-
isLandmark: true,
|
|
197
|
-
};
|
|
198
|
-
|
|
199
|
-
export const SemanticNav = SemanticTemplate.bind({});
|
|
200
|
-
SemanticNav.args = {
|
|
201
|
-
tag: "nav",
|
|
202
|
-
isLandmark: true,
|
|
203
|
-
};
|
|
204
|
-
|
|
205
|
-
// ===== CONTENT EXAMPLES =====
|
|
206
|
-
|
|
207
|
-
const ContentExampleTemplate: StoryFn<ContentContainerArgs> = (args) => ({
|
|
208
|
-
components: { ContentContainerComponent },
|
|
209
|
-
setup() {
|
|
210
|
-
return { args };
|
|
211
|
-
},
|
|
212
|
-
template: `
|
|
213
|
-
<div style="background: #f9fafb;">
|
|
214
|
-
<ContentContainerComponent
|
|
215
|
-
:data-testid="args.dataTestid"
|
|
216
|
-
:tag="args.tag"
|
|
217
|
-
:style-class-passthrough="args.styleClassPassthrough"
|
|
218
|
-
:is-landmark="args.isLandmark"
|
|
219
|
-
>
|
|
220
|
-
<article style="padding: 40px; background: white; border-radius: 0;">
|
|
221
|
-
<header style="margin-bottom: 30px; text-align: center;">
|
|
222
|
-
<h1 style="margin: 0 0 8px 0; color: #1f2937; font-size: 32px; font-weight: 700;">
|
|
223
|
-
The Art of Responsive Design
|
|
224
|
-
</h1>
|
|
225
|
-
<p style="margin: 0; color: #6b7280; font-size: 16px;">
|
|
226
|
-
How container queries are revolutionizing web layouts
|
|
227
|
-
</p>
|
|
228
|
-
</header>
|
|
229
|
-
|
|
230
|
-
<div style="line-height: 1.7; color: #374151;">
|
|
231
|
-
<p style="margin: 0 0 24px 0; font-size: 18px; color: #4b5563;">
|
|
232
|
-
Container queries represent a paradigm shift in responsive design, moving beyond viewport-based breakpoints
|
|
233
|
-
to component-centric layouts that adapt to their container's size rather than the entire screen.
|
|
234
|
-
</p>
|
|
235
|
-
|
|
236
|
-
<h2 style="margin: 32px 0 16px 0; color: #1f2937; font-size: 24px; font-weight: 600;">
|
|
237
|
-
Understanding Container Queries
|
|
238
|
-
</h2>
|
|
239
|
-
|
|
240
|
-
<p style="margin: 0 0 16px 0;">
|
|
241
|
-
Traditional media queries respond to viewport dimensions, but container queries respond to the size of
|
|
242
|
-
a specific element. This ContentContainer component demonstrates this concept by adjusting its layout
|
|
243
|
-
based on its own width, not the browser's width.
|
|
244
|
-
</p>
|
|
245
|
-
|
|
246
|
-
<div style="padding: 24px; background: #f0f9ff; border-left: 4px solid #3b82f6; border-radius: 0 8px 8px 0; margin: 24px 0;">
|
|
247
|
-
<h3 style="margin: 0 0 8px 0; color: #1e40af; font-size: 16px; font-weight: 600;">Key Benefits</h3>
|
|
248
|
-
<ul style="margin: 0; color: #1e40af;">
|
|
249
|
-
<li>Components are truly reusable across different contexts</li>
|
|
250
|
-
<li>No need to know parent container dimensions</li>
|
|
251
|
-
<li>More granular control over responsive behavior</li>
|
|
252
|
-
</ul>
|
|
253
|
-
</div>
|
|
254
|
-
|
|
255
|
-
<p style="margin: 0 0 16px 0;">
|
|
256
|
-
In this component, content flows naturally on smaller containers with appropriate gutters,
|
|
257
|
-
then switches to a fixed maximum width with centered alignment when the container reaches 1064px or wider.
|
|
258
|
-
</p>
|
|
259
|
-
|
|
260
|
-
<h2 style="margin: 32px 0 16px 0; color: #1f2937; font-size: 24px; font-weight: 600;">
|
|
261
|
-
Implementation Details
|
|
262
|
-
</h2>
|
|
263
|
-
|
|
264
|
-
<p style="margin: 0 0 16px 0;">
|
|
265
|
-
The magic happens through CSS Container Queries combined with CSS Grid. The container establishes
|
|
266
|
-
a containment context, then child elements can respond to the container's inline size.
|
|
267
|
-
</p>
|
|
268
|
-
|
|
269
|
-
<pre style="background: #1f2937; color: #e5e7eb; padding: 20px; border-radius: 8px; overflow-x: auto; margin: 24px 0; font-size: 14px; line-height: 1.4;"><code>container-type: inline-size;
|
|
270
|
-
container-name: content-container;
|
|
271
|
-
|
|
272
|
-
@container content-container (width >= 1064px) {
|
|
273
|
-
--content-max-width: 1064px;
|
|
274
|
-
--gutter: 0;
|
|
275
|
-
}</code></pre>
|
|
276
|
-
|
|
277
|
-
<p style="margin: 0;">
|
|
278
|
-
This approach creates more maintainable, flexible layouts that work consistently regardless of
|
|
279
|
-
where the component is placed in your application.
|
|
280
|
-
</p>
|
|
281
|
-
</div>
|
|
282
|
-
</article>
|
|
283
|
-
</ContentContainerComponent>
|
|
284
|
-
</div>
|
|
285
|
-
`,
|
|
286
|
-
});
|
|
287
|
-
|
|
288
|
-
export const BlogArticle = ContentExampleTemplate.bind({});
|
|
289
|
-
BlogArticle.args = {
|
|
290
|
-
tag: "article",
|
|
291
|
-
};
|
|
292
|
-
BlogArticle.parameters = {
|
|
293
|
-
docs: {
|
|
294
|
-
description: {
|
|
295
|
-
story:
|
|
296
|
-
"Example of a blog article layout showing how the ContentContainer provides optimal reading width and responsive behavior.",
|
|
297
|
-
},
|
|
298
|
-
},
|
|
299
|
-
};
|
|
300
|
-
|
|
301
|
-
// ===== ACCESSIBILITY STORIES =====
|
|
302
|
-
|
|
303
|
-
const AccessibilityTemplate: StoryFn<ContentContainerArgs> = (args) => ({
|
|
304
|
-
components: { ContentContainerComponent },
|
|
305
|
-
setup() {
|
|
306
|
-
return { args };
|
|
307
|
-
},
|
|
308
|
-
template: `
|
|
309
|
-
<div style="background: #f3f4f6; padding: 20px;">
|
|
310
|
-
<div style="background: white; border-radius: 12px; padding: 20px; margin-bottom: 20px; box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);">
|
|
311
|
-
<h3 style="margin: 0 0 16px 0; color: #374151;">Accessibility Features</h3>
|
|
312
|
-
<p style="margin: 0 0 16px 0; color: #6b7280; line-height: 1.5;">
|
|
313
|
-
When <code>isLandmark</code> is enabled, the container becomes keyboard focusable and includes appropriate ARIA labeling for screen readers.
|
|
314
|
-
</p>
|
|
315
|
-
<div style="padding: 12px; background: #f3f4f6; border-radius: 6px; border-left: 3px solid #6366f1;">
|
|
316
|
-
<strong style="color: #4338ca;">Current state:</strong>
|
|
317
|
-
{{ args.isLandmark ? 'Landmark enabled (focusable, labeled)' : 'Standard container (no landmark features)' }}
|
|
318
|
-
</div>
|
|
319
|
-
</div>
|
|
320
|
-
|
|
321
|
-
<ContentContainerComponent
|
|
322
|
-
:data-testid="args.dataTestid"
|
|
323
|
-
:tag="args.tag"
|
|
324
|
-
:style-class-passthrough="args.styleClassPassthrough"
|
|
325
|
-
:is-landmark="args.isLandmark"
|
|
326
|
-
>
|
|
327
|
-
<div style="padding: 24px; background: white; border-radius: 8px; border: 2px solid #d1d5db;">
|
|
328
|
-
<div :style="{
|
|
329
|
-
padding: '16px',
|
|
330
|
-
background: args.isLandmark ? '#dbeafe' : '#f9fafb',
|
|
331
|
-
borderRadius: '8px',
|
|
332
|
-
border: args.isLandmark ? '2px solid #3b82f6' : '2px solid #e5e7eb'
|
|
333
|
-
}">
|
|
334
|
-
<h3 style="margin: 0 0 12px 0; color: #374151;">
|
|
335
|
-
{{ args.isLandmark ? '🏷️ Landmark Container' : '📦 Standard Container' }}
|
|
336
|
-
</h3>
|
|
337
|
-
<p style="margin: 0 0 16px 0; color: #6b7280; line-height: 1.5;">
|
|
338
|
-
{{ args.isLandmark
|
|
339
|
-
? 'This container is marked as a landmark region. Screen reader users can navigate directly to it using landmark navigation commands.'
|
|
340
|
-
: 'This is a regular content container without special accessibility features.'
|
|
341
|
-
}}
|
|
342
|
-
</p>
|
|
343
|
-
<div style="font-family: monospace; font-size: 12px; background: rgba(0, 0, 0, 0.05); padding: 8px; border-radius: 4px;">
|
|
344
|
-
<div>tabIndex: {{ args.isLandmark ? '0' : 'null' }}</div>
|
|
345
|
-
<div>aria-label: {{ args.isLandmark ? '"Content Container Landmark"' : 'undefined' }}</div>
|
|
346
|
-
</div>
|
|
347
|
-
</div>
|
|
348
|
-
</div>
|
|
349
|
-
</ContentContainerComponent>
|
|
350
|
-
</div>
|
|
351
|
-
`,
|
|
352
|
-
});
|
|
353
|
-
|
|
354
|
-
export const WithLandmark = AccessibilityTemplate.bind({});
|
|
355
|
-
WithLandmark.args = {
|
|
356
|
-
tag: "section",
|
|
357
|
-
isLandmark: true,
|
|
358
|
-
};
|
|
359
|
-
WithLandmark.parameters = {
|
|
360
|
-
docs: {
|
|
361
|
-
description: {
|
|
362
|
-
story:
|
|
363
|
-
"ContentContainer with landmark accessibility features enabled - makes the container focusable and properly labeled for assistive technologies.",
|
|
364
|
-
},
|
|
365
|
-
},
|
|
366
|
-
};
|
|
367
|
-
|
|
368
|
-
export const WithoutLandmark = AccessibilityTemplate.bind({});
|
|
369
|
-
WithoutLandmark.args = {
|
|
370
|
-
tag: "div",
|
|
371
|
-
isLandmark: false,
|
|
372
|
-
};
|
|
373
|
-
WithoutLandmark.parameters = {
|
|
374
|
-
docs: {
|
|
375
|
-
description: {
|
|
376
|
-
story: "Standard ContentContainer without landmark features - regular content container behavior.",
|
|
377
|
-
},
|
|
378
|
-
},
|
|
379
|
-
};
|
|
380
|
-
|
|
381
|
-
// ===== RESPONSIVE DEMONSTRATION =====
|
|
382
|
-
|
|
383
|
-
const ResponsiveTemplate: StoryFn = () => ({
|
|
384
|
-
components: { ContentContainerComponent },
|
|
385
|
-
template: `
|
|
386
|
-
<div style="background: #f8fafc; padding: 20px;">
|
|
387
|
-
<div style="margin-bottom: 40px;">
|
|
388
|
-
<h2 style="text-align: center; margin: 0 0 16px 0; color: #374151; font-size: 28px;">
|
|
389
|
-
Responsive Behavior Demonstration
|
|
390
|
-
</h2>
|
|
391
|
-
<p style="text-align: center; margin: 0 0 32px 0; color: #6b7280; max-width: 600px; margin-left: auto; margin-right: auto; line-height: 1.6;">
|
|
392
|
-
The ContentContainer adapts its layout based on its own width using container queries.
|
|
393
|
-
Resize your browser to see the responsive behavior in action.
|
|
394
|
-
</p>
|
|
395
|
-
</div>
|
|
396
|
-
|
|
397
|
-
<!-- Small container demonstration -->
|
|
398
|
-
<div style="margin-bottom: 40px;">
|
|
399
|
-
<h3 style="margin: 0 0 16px 0; color: #374151;">Narrow Container (< 1064px)</h3>
|
|
400
|
-
<div style="max-width: 800px; border: 2px dashed #cbd5e1; padding: 20px; margin-bottom: 20px;">
|
|
401
|
-
<ContentContainerComponent tag="div">
|
|
402
|
-
<div style="padding: 20px; background: #fef3c7; border-radius: 8px; text-align: center;">
|
|
403
|
-
<h4 style="margin: 0 0 8px 0; color: #92400e;">Fluid Width Mode</h4>
|
|
404
|
-
<p style="margin: 0; color: #b45309; font-size: 14px;">
|
|
405
|
-
Content uses available width with 16px gutters on each side
|
|
406
|
-
</p>
|
|
407
|
-
</div>
|
|
408
|
-
</ContentContainerComponent>
|
|
409
|
-
</div>
|
|
410
|
-
</div>
|
|
411
|
-
|
|
412
|
-
<!-- Large container demonstration -->
|
|
413
|
-
<div style="margin-bottom: 40px;">
|
|
414
|
-
<h3 style="margin: 0 0 16px 0; color: #374151;">Wide Container (≥ 1064px)</h3>
|
|
415
|
-
<div style="border: 2px dashed #cbd5e1; padding: 20px;">
|
|
416
|
-
<ContentContainerComponent tag="div">
|
|
417
|
-
<div style="padding: 20px; background: #dcfce7; border-radius: 8px; text-align: center;">
|
|
418
|
-
<h4 style="margin: 0 0 8px 0; color: #166534;">Fixed Width Mode</h4>
|
|
419
|
-
<p style="margin: 0; color: #15803d; font-size: 14px;">
|
|
420
|
-
Content is constrained to 1064px max-width and centered within the container
|
|
421
|
-
</p>
|
|
422
|
-
</div>
|
|
423
|
-
</ContentContainerComponent>
|
|
424
|
-
</div>
|
|
425
|
-
</div>
|
|
426
|
-
|
|
427
|
-
<!-- Technical details -->
|
|
428
|
-
<ContentContainerComponent tag="section">
|
|
429
|
-
<div style="padding: 24px; background: white; border-radius: 12px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);">
|
|
430
|
-
<h3 style="margin: 0 0 16px 0; color: #374151;">How It Works</h3>
|
|
431
|
-
<div style="display: grid; gap: 16px;">
|
|
432
|
-
<div style="padding: 16px; background: #f1f5f9; border-radius: 8px; border-left: 4px solid #3b82f6;">
|
|
433
|
-
<h4 style="margin: 0 0 8px 0; color: #1e40af; font-size: 14px; font-weight: 600;">Container Queries</h4>
|
|
434
|
-
<p style="margin: 0; color: #475569; font-size: 14px; line-height: 1.5;">
|
|
435
|
-
Uses <code>@container</code> rules to respond to the container's own width, not the viewport width.
|
|
436
|
-
</p>
|
|
437
|
-
</div>
|
|
438
|
-
<div style="padding: 16px; background: #f0fdf4; border-radius: 8px; border-left: 4px solid #22c55e;">
|
|
439
|
-
<h4 style="margin: 0 0 8px 0; color: #15803d; font-size: 14px; font-weight: 600;">CSS Grid Layout</h4>
|
|
440
|
-
<p style="margin: 0; color: #166534; font-size: 14px; line-height: 1.5;">
|
|
441
|
-
Named grid lines create flexible layouts that adapt to content needs automatically.
|
|
442
|
-
</p>
|
|
443
|
-
</div>
|
|
444
|
-
<div style="padding: 16px; background: #fef7ff; border-radius: 8px; border-left: 4px solid #a855f7;">
|
|
445
|
-
<h4 style="margin: 0 0 8px 0; color: #7c3aed; font-size: 14px; font-weight: 600;">Component Reusability</h4>
|
|
446
|
-
<p style="margin: 0; color: #8b5cf6; font-size: 14px; line-height: 1.5;">
|
|
447
|
-
Works consistently regardless of where it's placed in your layout hierarchy.
|
|
448
|
-
</p>
|
|
449
|
-
</div>
|
|
450
|
-
</div>
|
|
451
|
-
</div>
|
|
452
|
-
</ContentContainerComponent>
|
|
453
|
-
</div>
|
|
454
|
-
`,
|
|
455
|
-
});
|
|
456
|
-
|
|
457
|
-
export const ResponsiveBehavior = ResponsiveTemplate.bind({});
|
|
458
|
-
ResponsiveBehavior.parameters = {
|
|
459
|
-
docs: {
|
|
460
|
-
description: {
|
|
461
|
-
story:
|
|
462
|
-
"Interactive demonstration of how the ContentContainer responds to different container widths using CSS Container Queries instead of media queries.",
|
|
463
|
-
},
|
|
464
|
-
},
|
|
465
|
-
};
|
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<div class="ui-content-grid" :class="[applyClasses]" :data-testid="dataTestid">
|
|
3
|
-
<div v-if="slots.slot1" class="col-1">
|
|
4
|
-
<slot name="slot1"></slot>
|
|
5
|
-
</div>
|
|
6
|
-
<div v-if="slots.slot2" class="col-2">
|
|
7
|
-
<slot name="slot2"></slot>
|
|
8
|
-
</div>
|
|
9
|
-
</div>
|
|
10
|
-
</template>
|
|
11
|
-
|
|
12
|
-
<script setup lang="ts">
|
|
13
|
-
const props = defineProps({
|
|
14
|
-
dataTestid: {
|
|
15
|
-
type: String,
|
|
16
|
-
default: "ui-content-grid",
|
|
17
|
-
},
|
|
18
|
-
applyClasses: {
|
|
19
|
-
type: String,
|
|
20
|
-
default: "",
|
|
21
|
-
},
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
const slots = useSlots();
|
|
25
|
-
</script>
|
|
26
|
-
|
|
27
|
-
<style lang="css">
|
|
28
|
-
@layer components {
|
|
29
|
-
.ui-content-grid {
|
|
30
|
-
--_margin-inline: 0;
|
|
31
|
-
--_grid-template-columns: repeat(4, 1fr);
|
|
32
|
-
--_grid-template-rows: repeat(2, auto);
|
|
33
|
-
--_grid-gap: 1.6rem;
|
|
34
|
-
|
|
35
|
-
display: grid;
|
|
36
|
-
gap: var(--_grid-gap);
|
|
37
|
-
grid-template-columns: var(--_grid-template-columns);
|
|
38
|
-
grid-template-rows: var(--_grid-template-rows);
|
|
39
|
-
margin-inline: var(--_margin-inline);
|
|
40
|
-
|
|
41
|
-
@container content (min-width: 768px) {
|
|
42
|
-
--_margin-inline: 0;
|
|
43
|
-
--_grid-template-columns: repeat(6, 1fr);
|
|
44
|
-
--_grid-gap: 3.2rem;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
@container content (min-width: 1024px) {
|
|
48
|
-
--_margin-inline: 0;
|
|
49
|
-
--_grid-template-columns: repeat(12, 1fr);
|
|
50
|
-
--_grid-template-rows: initial;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
.col-1 {
|
|
54
|
-
--_grid-column: 1 / span 4;
|
|
55
|
-
--_grid-row: 1;
|
|
56
|
-
grid-column: var(--_grid-column);
|
|
57
|
-
grid-row: var(--_grid-row);
|
|
58
|
-
|
|
59
|
-
@container content (min-width: 768px) {
|
|
60
|
-
--_grid-column: 1 / span 6;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
@container content (min-width: 1024px) {
|
|
64
|
-
--_grid-column: 1 / span 6;
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
.col-2 {
|
|
69
|
-
--_grid-column: 1 / span 4;
|
|
70
|
-
--_grid-row: 2;
|
|
71
|
-
grid-column: var(--_grid-column);
|
|
72
|
-
grid-row: var(--_grid-row);
|
|
73
|
-
|
|
74
|
-
@container content (min-width: 768px) {
|
|
75
|
-
--_grid-column: 1 / span 6;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
@container content (min-width: 1024px) {
|
|
79
|
-
--_grid-column: 7 / span 6;
|
|
80
|
-
--_grid-row: 1;
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
</style>
|
|
@@ -1,122 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<component
|
|
3
|
-
:is="tag"
|
|
4
|
-
class="display-card"
|
|
5
|
-
:class="[variant, elementClasses, { 'has-dividers': hasDividers }, { 'no-outline': noOutline }]"
|
|
6
|
-
>
|
|
7
|
-
<div v-if="slots.header" class="card-row display-card-header">
|
|
8
|
-
<slot name="header"></slot>
|
|
9
|
-
</div>
|
|
10
|
-
<div v-if="slots.default" class="card-row display-card-content">
|
|
11
|
-
<slot name="default"></slot>
|
|
12
|
-
</div>
|
|
13
|
-
<div v-if="slots.footer" class="card-row display-card-footer">
|
|
14
|
-
<slot name="footer"></slot>
|
|
15
|
-
</div>
|
|
16
|
-
</component>
|
|
17
|
-
</template>
|
|
18
|
-
|
|
19
|
-
<script setup lang="ts">
|
|
20
|
-
const props = defineProps({
|
|
21
|
-
tag: {
|
|
22
|
-
type: String,
|
|
23
|
-
default: "div",
|
|
24
|
-
validator(value: string) {
|
|
25
|
-
return ["div", "section", "article", "aside", "main", "nav"].includes(value);
|
|
26
|
-
},
|
|
27
|
-
},
|
|
28
|
-
hasDividers: {
|
|
29
|
-
type: Boolean,
|
|
30
|
-
default: false,
|
|
31
|
-
},
|
|
32
|
-
noOutline: {
|
|
33
|
-
type: Boolean,
|
|
34
|
-
default: false,
|
|
35
|
-
},
|
|
36
|
-
variant: {
|
|
37
|
-
type: String,
|
|
38
|
-
default: "solid",
|
|
39
|
-
validator(value: string) {
|
|
40
|
-
return ["solid", "subtle", "soft", "outline"].includes(value);
|
|
41
|
-
},
|
|
42
|
-
},
|
|
43
|
-
styleClassPassthrough: {
|
|
44
|
-
type: [String, Array] as PropType<string | string[]>,
|
|
45
|
-
default: () => [],
|
|
46
|
-
},
|
|
47
|
-
});
|
|
48
|
-
const slots = useSlots();
|
|
49
|
-
|
|
50
|
-
const { elementClasses, resetElementClasses } = useStyleClassPassthrough(props.styleClassPassthrough);
|
|
51
|
-
|
|
52
|
-
watch(
|
|
53
|
-
() => props.styleClassPassthrough,
|
|
54
|
-
() => {
|
|
55
|
-
resetElementClasses(props.styleClassPassthrough);
|
|
56
|
-
}
|
|
57
|
-
);
|
|
58
|
-
</script>
|
|
59
|
-
|
|
60
|
-
<style lang="css">
|
|
61
|
-
@layer components {
|
|
62
|
-
.display-card {
|
|
63
|
-
--_inner-padding: 1rem;
|
|
64
|
-
--_background-color: transparent;
|
|
65
|
-
--_border-color: transparent;
|
|
66
|
-
--_border-width: 0.2rem;
|
|
67
|
-
--_box-shadow-color: transparent;
|
|
68
|
-
|
|
69
|
-
display: grid;
|
|
70
|
-
grid-auto-flow: row;
|
|
71
|
-
/* gap: 1rem; */
|
|
72
|
-
border-radius: 0.5rem;
|
|
73
|
-
overflow: hidden;
|
|
74
|
-
|
|
75
|
-
background-color: var(--_background-color, transparent);
|
|
76
|
-
border: var(--_border-width) solid var(--_border-color, transparent);
|
|
77
|
-
box-shadow: 0 0 0.4rem var(--_border-width) var(--_box-shadow-color, transparent);
|
|
78
|
-
|
|
79
|
-
&.no-outline {
|
|
80
|
-
--_border-width: 0;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
&.solid {
|
|
84
|
-
--_background-color: light-dark(var(--slate-00), var(--slate-10));
|
|
85
|
-
--_border-color: light-dark(var(--slate-04), var(--slate-08));
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
&.subtle {
|
|
89
|
-
--_background-color: color-mix(in oklab, light-dark(var(--slate-01), var(--slate-08)) 50%, transparent);
|
|
90
|
-
--_border-color: light-dark(var(--slate-03), var(--slate-09));
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
&.soft {
|
|
94
|
-
--_background-color: color-mix(in oklab, light-dark(var(--slate-01), var(--slate-08)) 20%, transparent);
|
|
95
|
-
--_box-shadow-color: color-mix(in oklab, light-dark(var(--slate-02), var(--slate-08)) 80%, transparent);
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
&.outline {
|
|
99
|
-
--_background-color: transparent;
|
|
100
|
-
--_border-color: light-dark(var(--slate-04), var(--slate-08));
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
&.has-dividers {
|
|
104
|
-
.card-row + .card-row {
|
|
105
|
-
border-top: 0.2rem solid var(--_border-color);
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
.display-card-header {
|
|
110
|
-
padding: var(--_inner-padding);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
.display-card-content {
|
|
114
|
-
padding: var(--_inner-padding);
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
.display-card-footer {
|
|
118
|
-
padding: var(--_inner-padding);
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
</style>
|