|
| 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. |
0 commit comments