Skip to content

Commit 41515db

Browse files
committed
GridStack - Skills added
1 parent 94b6ee8 commit 41515db

3 files changed

Lines changed: 151 additions & 7 deletions

File tree

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
---
2+
name: GridStack
3+
description: GridStack CSS Grid z-axis stacking component — slot API, z-order rules, sizing behaviour, consumer patterns (video+overlay, image+text)
4+
type: reference
5+
---
6+
7+
# GridStack
8+
9+
## Overview
10+
11+
`GridStack` stacks slot content in the z-axis using a single `grid-template-areas: "stack"` — no `position: absolute` needed. Every slot is wrapped in a `.grid-stack__layer` div sharing that grid area. The container sizes itself from the tallest layer; all layers stretch to fill that height.
12+
13+
## Props
14+
15+
| Prop | Type | Default | Description |
16+
|------|------|---------|-------------|
17+
| `tag` | `"div" \| "section" \| "article" \| "main"` | `"div"` | HTML element rendered as the root. |
18+
| `styleClassPassthrough` | `string \| string[]` | `[]` | Extra classes applied to the root element. |
19+
20+
## Slot API
21+
22+
Any named slot is accepted — there are no declared slot names. Convention is `layer-1`, `layer-2`, `layer-3` etc., but any name works. The component iterates `$slots` and wraps each in a `.grid-stack__layer`.
23+
24+
**Z-order rule: DOM order = z-order. The last slot is on top.**
25+
26+
```vue
27+
<GridStack>
28+
<template #layer-1><!-- base, behind everything --></template>
29+
<template #layer-2><!-- middle --></template>
30+
<template #layer-3><!-- on top --></template>
31+
</GridStack>
32+
```
33+
34+
## Basic usage
35+
36+
```vue
37+
<GridStack>
38+
<template #layer-1>
39+
<img src="/images/hero.jpg" alt="" />
40+
</template>
41+
<template #layer-2>
42+
<div class="hero-overlay">
43+
<h1>Heading over image</h1>
44+
</div>
45+
</template>
46+
</GridStack>
47+
```
48+
49+
## Common patterns
50+
51+
### Video background + overlay
52+
53+
```vue
54+
<GridStack>
55+
<template #layer-1>
56+
<BannerVideo
57+
src="/videos/hero.mp4"
58+
poster="/images/hero-poster.jpg"
59+
alt=""
60+
depth="lg"
61+
/>
62+
</template>
63+
<template #layer-2>
64+
<div class="video-overlay">
65+
<h1>Content over video</h1>
66+
</div>
67+
</template>
68+
</GridStack>
69+
```
70+
71+
### Decorative background + content
72+
73+
```vue
74+
<GridStack tag="section">
75+
<template #layer-1>
76+
<div class="decorative-bg" aria-hidden="true"></div>
77+
</template>
78+
<template #layer-2>
79+
<div class="section-content">
80+
<p>Real content here</p>
81+
</div>
82+
</template>
83+
</GridStack>
84+
```
85+
86+
## Sizing
87+
88+
The container height is determined by the tallest child layer. All layers stretch to match. If layers have different intrinsic heights, the shorter ones will stretch — use `align-self` on the layer's inner content to control vertical position within the stretched space.
89+
90+
To pin the stack to a fixed height, set it on the root from the consuming page:
91+
92+
```css
93+
.my-page {
94+
.grid-stack {
95+
block-size: 60rem;
96+
}
97+
}
98+
```
99+
100+
## CSS classes
101+
102+
| Class | Element |
103+
|---|---|
104+
| `.grid-stack` | Root element |
105+
| `.grid-stack__layer` | Wrapper div around each slot — all share `grid-area: stack` |
106+
107+
## Consumer styling
108+
109+
No `:deep()` needed — `@layer components` means page styles win automatically.
110+
111+
```vue
112+
<style>
113+
.my-page {
114+
.grid-stack {
115+
border-radius: 1.2rem;
116+
overflow: hidden; /* clips layers to rounded corners */
117+
}
118+
119+
/* Style the overlay layer by targeting content inside it */
120+
.my-overlay {
121+
display: grid;
122+
place-items: center;
123+
pointer-events: none; /* let clicks through to the layer below */
124+
}
125+
}
126+
</style>
127+
```
128+
129+
## Notes
130+
131+
- `pointer-events: none` on overlay layers (and `pointer-events: auto` on interactive children within them) is the standard pattern for overlays that shouldn't block interaction with layers behind them.
132+
- There is no built-in `z-index` — stacking is handled purely by DOM order. If a consumer applies `z-index` on a layer for other reasons, be aware it creates a new stacking context.
133+
- The slot name is used as the Vue `:key` on the layer wrapper, so slot names must be unique.

.claude/skills/index.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,8 @@ Each skill is a single markdown file named `<area>-<task>.md`.
6565
├── glass-panel.md — GlassPanel props, slots, CSS token API (--glass-panel-bg/border-color/shadow/highlight), theming override
6666
├── navigation-horizontal.md — NavigationHorizontal props, NavItemData type, CSS token API, import path gotcha
6767
├── input-copy-core.md — InputCopyCore: readonly copy-to-clipboard input; props, emits, slots, CSS classes, usage
68-
├── banner-video.md — BannerVideo: full-width hero video banner, objectFit/objectPosition, responsive max-height, reduced-motion fallback, CSS tokens
68+
├── banner-video.md — BannerVideo: full-width hero video banner, depth tier system, objectFit/objectPosition, reduced-motion fallback, CSS tokens
69+
├── grid-stack.md — GridStack: CSS Grid z-axis stacking, slot API, z-order rules, sizing, video+overlay and image+text patterns
6970
├── scroll-reveal-frame.md — ScrollRevealFrame: generic parallax clipping frame, slot API, image grid pattern, CSS tokens, browser support
7071
├── scroll-reveal-image.md — ScrollRevealImage: single-image parallax reveal, focalX, imgWidth/imgHeight, responsive frame height
7172
├── site-navigation.md — SiteNavigation: responsive nav with auto-collapse, burger menu, decorator indicators, CSS token API

app/components/01.atoms/banner-video/BannerVideo.vue

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ interface Props {
7474
}
7575
7676
const props = withDefaults(defineProps<Props>(), {
77-
tag: "section",
77+
tag: "div",
7878
alt: "",
7979
imgWidth: 1920,
8080
imgHeight: 1080,
@@ -161,11 +161,21 @@ onActivated(() => {
161161
width: 100%;
162162
overflow: hidden;
163163
164-
&[data-depth="xs"] { --_max-height: var(--theme-banner-video-max-height-xs, clamp(12rem, 15vw, 24rem)); }
165-
&[data-depth="sm"] { --_max-height: var(--theme-banner-video-max-height-sm, clamp(18rem, 22vw, 36rem)); }
166-
&[data-depth="md"] { --_max-height: var(--theme-banner-video-max-height-md, clamp(28rem, 38vw, 56rem)); }
167-
&[data-depth="lg"] { --_max-height: var(--theme-banner-video-max-height-lg, clamp(40rem, 52vw, 72rem)); }
168-
&[data-depth="xl"] { --_max-height: var(--theme-banner-video-max-height-xl, clamp(52rem, 65vw, 90rem)); }
164+
&[data-depth="xs"] {
165+
--_max-height: var(--theme-banner-video-max-height-xs, clamp(12rem, 15vw, 24rem));
166+
}
167+
&[data-depth="sm"] {
168+
--_max-height: var(--theme-banner-video-max-height-sm, clamp(18rem, 22vw, 36rem));
169+
}
170+
&[data-depth="md"] {
171+
--_max-height: var(--theme-banner-video-max-height-md, clamp(28rem, 38vw, 56rem));
172+
}
173+
&[data-depth="lg"] {
174+
--_max-height: var(--theme-banner-video-max-height-lg, clamp(40rem, 52vw, 72rem));
175+
}
176+
&[data-depth="xl"] {
177+
--_max-height: var(--theme-banner-video-max-height-xl, clamp(52rem, 65vw, 90rem));
178+
}
169179
170180
.video {
171181
grid-area: media;

0 commit comments

Comments
 (0)