srcdev-nuxt-components 9.3.5 → 9.3.6

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.
@@ -2,11 +2,11 @@
2
2
 
3
3
  ## Overview
4
4
 
5
- `ExpandingPanel` is a single expand/collapse panel built on the native `<details>`/`<summary>` element. It animates open/close by styling the `::details-content` pseudo-element (the browser's own anonymous box wrapping everything after `<summary>`), supports `v-model` for controlled state, and can be locked open with `forceOpened`. Multiple panels can be grouped into a native accordion by sharing the same `name` prop (see `AccordianCore`).
5
+ `ExpandingPanel` is a single expand/collapse panel built on the native `<details>`/`<summary>` element. It animates open/close by styling the `::details-content` pseudo-element (the browser's own anonymous box wrapping everything after `<summary>`) where supported, supports `v-model` for controlled state, and can be locked open with `forceOpened`. Multiple panels can be grouped into a native accordion by sharing the same `name` prop (see `AccordianCore`).
6
6
 
7
- `::details-content` is Baseline "newly available" (Sept 2025) — Chrome, Firefox, and Safari all generate the box. The height *animation* itself depends on `interpolate-size: allow-keywords`, which as of 2026 only runs in Chromium; other browsers still open/close correctly, just without the transition (accepted progressive-enhancement trade-off, not a bug).
7
+ **WebKit (Safari/iOS) does not support `::details-content`** as of 2026-08, confirmed via real-device testing — Chrome's device-emulation mode cannot catch this, since it's still the Blink engine underneath regardless of which browser UI it's simulating. WebKit silently drops the whole rule block: no console warning, no error, just no animation and (more seriously) `content-is-on-top`'s `position: absolute` never applying, so an overlay panel renders in normal document flow and can break the surrounding layout. Fixed 2026-08-22: `ExpandingPanel` now carries an `@supports not selector(::details-content)` fallback block that reproduces `ExpandingPanelClassic`'s `grid-template-rows` technique automatically — no consumer action needed, this works transparently on every browser. See pitfall #19 in the repo's `CLAUDE.md` for the full mechanism.
8
8
 
9
- If a consumer needs the animation to run identically in every browser today, use [`ExpandingPanelClassic`](expanding-panel-classic.md) instead — same props/model/slots API, older `grid-template-rows` implementation with no Baseline-2025 dependency.
9
+ `ExpandingPanelClassic` still exists as a standalone option — same props/model/slots API, always uses the `grid-template-rows` implementation with no pseudo-element dependency at all — but there's no longer a compatibility reason to prefer it over `ExpandingPanel`.
10
10
 
11
11
  ---
12
12
 
@@ -145,7 +145,9 @@ Always supply a meaningful `name` prop when using multiple panels on the same pa
145
145
 
146
146
  ## Styling the content when contentIsOnTop
147
147
 
148
- `background-color`, `padding`, and a shadow can be set directly on `.expanding-panel-content` (or on a wrapper inside the `#content` slot — either works). The clipping boundary is now `::details-content` itself (`overflow: clip`, animated `height`), and `.expanding-panel-content` is a *child* of that box so its own box, including padding/border/background, is correctly clipped to the animated height regardless of where the styling lives. This is a change from the previous `.inner`-based implementation, which had to clip its own box and so couldn't carry visual styling directly.
148
+ `background-color`, `padding`, and a shadow can be set directly on `.expanding-panel-content` (or on a wrapper inside the `#content` slot — either works) in browsers that support `::details-content`: the clipping boundary there is `::details-content` itself (`overflow: clip`, animated `height`), and `.expanding-panel-content` is a *child* of that box, so its own box is correctly clipped regardless of where the styling lives.
149
+
150
+ In the WebKit fallback path (no `::details-content` support), `.expanding-panel-content` itself is the animated grid track (`grid-template-rows: 0fr → 1fr`) and remains the element being sized — so styling placed directly on it is still clipped correctly there too. The one structural difference: the fallback wraps the `#content` slot in an internal `.inner` div (needed so a plain child's `min-height: auto` doesn't stop the row collapsing to true `0`) — this is purely an implementation detail for the collapse mechanism and isn't a styling hook; don't target `.inner` directly, style `.expanding-panel-content` or the slot's own markup instead, same as the `::details-content` path.
149
151
 
150
152
  ---
151
153
 
@@ -211,7 +213,7 @@ See [component-local-style-override.md](../component-local-style-override.md) fo
211
213
 
212
214
  ## Notes
213
215
 
214
- - The open/close animation targets `::details-content` (`height: 0 → auto`, `overflow: clip`) — no JS height measurement needed. Requires `interpolate-size: allow-keywords` support (Chromium only as of 2026); other browsers still toggle correctly, just instantly.
216
+ - The open/close animation targets `::details-content` (`height: 0 → auto`, `overflow: clip`) where supported — no JS height measurement needed. Requires `interpolate-size: allow-keywords` for the height transition itself (Chromium only as of 2026; other browsers with `::details-content` support still toggle correctly, just instantly). WebKit has neither, and falls back to `ExpandingPanelClassic`'s `grid-template-rows` technique automatically (see the Overview section above) — that path animates in every browser regardless of `interpolate-size`.
215
217
  - `content-is-on-top` is applied to the root `.expanding-panel` element, not `.expanding-panel-content` — style overrides must scope through it, e.g. `.expanding-panel.my-panel .expanding-panel-content { ... }`.
216
218
  - When `forceOpened` is `true`, `open` stays `true` regardless of `v-model`, but `v-model` still updates internally on clicks (useful if you later set `forceOpened` back to `false`).
217
219
  - Group panels into a native accordion (only one open at a time) by passing the same `name` to multiple panels or use `AccordianCore` which handles this automatically.
@@ -8,7 +8,7 @@ exports[`AccordianCore > renders correct HTML structure with all props set 1`] =
8
8
  <div class="label-wrapper"></div>
9
9
  <div class="icon-wrapper"><span class="iconify i-bi:caret-down-fill icon" aria-hidden="true"></span></div>
10
10
  </summary>
11
- <div id="id-snapshot-accordian-content" class="expanding-panel-content" aria-labelledby="id-snapshot-accordian-trigger" role="region"></div>
11
+ <div id="id-snapshot-accordian-content" class="expanding-panel-content" aria-labelledby="id-snapshot-accordian-trigger" role="region"><div class="inner"></div></div>
12
12
  </details>
13
13
  </div>
14
14
  <div class="expanding-panel accordian-item" icon-size="medium">
@@ -17,7 +17,7 @@ exports[`AccordianCore > renders correct HTML structure with all props set 1`] =
17
17
  <div class="label-wrapper"></div>
18
18
  <div class="icon-wrapper"><span class="iconify i-bi:caret-down-fill icon" aria-hidden="true"></span></div>
19
19
  </summary>
20
- <div id="id-snapshot-accordian-content" class="expanding-panel-content" aria-labelledby="id-snapshot-accordian-trigger" role="region"></div>
20
+ <div id="id-snapshot-accordian-content" class="expanding-panel-content" aria-labelledby="id-snapshot-accordian-trigger" role="region"><div class="inner"></div></div>
21
21
  </details>
22
22
  </div>
23
23
  <div class="expanding-panel accordian-item" icon-size="medium">
@@ -26,7 +26,7 @@ exports[`AccordianCore > renders correct HTML structure with all props set 1`] =
26
26
  <div class="label-wrapper"></div>
27
27
  <div class="icon-wrapper"><span class="iconify i-bi:caret-down-fill icon" aria-hidden="true"></span></div>
28
28
  </summary>
29
- <div id="id-snapshot-accordian-content" class="expanding-panel-content" aria-labelledby="id-snapshot-accordian-trigger" role="region"></div>
29
+ <div id="id-snapshot-accordian-content" class="expanding-panel-content" aria-labelledby="id-snapshot-accordian-trigger" role="region"><div class="inner"></div></div>
30
30
  </details>
31
31
  </div>
32
32
  </div>"
@@ -40,7 +40,7 @@ exports[`AccordianCore > renders correct HTML structure with default props 1`] =
40
40
  <div class="label-wrapper"></div>
41
41
  <div class="icon-wrapper"><span class="iconify i-bi:caret-down-fill icon" aria-hidden="true"></span></div>
42
42
  </summary>
43
- <div id="id-v-0-0-0-content" class="expanding-panel-content" aria-labelledby="id-v-0-0-0-trigger" role="region"></div>
43
+ <div id="id-v-0-0-0-content" class="expanding-panel-content" aria-labelledby="id-v-0-0-0-trigger" role="region"><div class="inner"></div></div>
44
44
  </details>
45
45
  </div>
46
46
  <div class="expanding-panel accordian-item" icon-size="medium">
@@ -49,7 +49,7 @@ exports[`AccordianCore > renders correct HTML structure with default props 1`] =
49
49
  <div class="label-wrapper"></div>
50
50
  <div class="icon-wrapper"><span class="iconify i-bi:caret-down-fill icon" aria-hidden="true"></span></div>
51
51
  </summary>
52
- <div id="id-v-0-0-1-content" class="expanding-panel-content" aria-labelledby="id-v-0-0-1-trigger" role="region"></div>
52
+ <div id="id-v-0-0-1-content" class="expanding-panel-content" aria-labelledby="id-v-0-0-1-trigger" role="region"><div class="inner"></div></div>
53
53
  </details>
54
54
  </div>
55
55
  </div>"
@@ -63,7 +63,7 @@ exports[`AccordianCore > renders correct HTML structure with populated slots 1`]
63
63
  <div class="label-wrapper"><span>Summary 0</span></div>
64
64
  <div class="icon-wrapper"><span>Icon 0</span></div>
65
65
  </summary>
66
- <div id="id-v-0-0-0-content" class="expanding-panel-content" aria-labelledby="id-v-0-0-0-trigger" role="region"><span>Content 0</span></div>
66
+ <div id="id-v-0-0-0-content" class="expanding-panel-content" aria-labelledby="id-v-0-0-0-trigger" role="region"><div class="inner"><span>Content 0</span></div></div>
67
67
  </details>
68
68
  </div>
69
69
  <div class="expanding-panel accordian-item" icon-size="medium">
@@ -72,7 +72,7 @@ exports[`AccordianCore > renders correct HTML structure with populated slots 1`]
72
72
  <div class="label-wrapper"><span>Summary 1</span></div>
73
73
  <div class="icon-wrapper"><span>Icon 1</span></div>
74
74
  </summary>
75
- <div id="id-v-0-0-1-content" class="expanding-panel-content" aria-labelledby="id-v-0-0-1-trigger" role="region"><span>Content 1</span></div>
75
+ <div id="id-v-0-0-1-content" class="expanding-panel-content" aria-labelledby="id-v-0-0-1-trigger" role="region"><div class="inner"><span>Content 1</span></div></div>
76
76
  </details>
77
77
  </div>
78
78
  </div>"
@@ -90,11 +90,16 @@ visual style, pass a modifier class:
90
90
  - `--expanding-panel-content-gap` and `--expanding-panel-content-z-index` only take effect when
91
91
  `contentIsOnTop` is `true` — they're no-ops for the default (in-flow) layout.
92
92
  - `background-color`, `padding`, `border`, and shadow are all safe to set directly on
93
- `.expanding-panel-content`. The open/close animation clips via `overflow: clip` on
94
- `::details-content` (the native anonymous box wrapping everything after `<summary>`), and
95
- `.expanding-panel-content` is a child of that box — so its own box is correctly clipped to the
96
- animated height while closed/closing. A wrapper inside the `#content` slot works equally well
97
- if you prefer to keep the styling scoped to your own markup.
93
+ `.expanding-panel-content`. Where `::details-content` is supported (not WebKit see below), the
94
+ open/close animation clips via `overflow: clip` on that pseudo-element, and
95
+ `.expanding-panel-content` is a child of it, so its own box is correctly clipped to the animated
96
+ height while closed/closing. A wrapper inside the `#content` slot works equally well if you
97
+ prefer to keep the styling scoped to your own markup.
98
+ - WebKit (Safari/iOS) doesn't support `::details-content` — `ExpandingPanel` falls back to
99
+ `ExpandingPanelClassic`'s `grid-template-rows` technique there automatically, no consumer action
100
+ needed. That path wraps the `#content` slot in an internal `.inner` div (an implementation
101
+ detail of the collapse mechanism, not a styling hook) but styling on `.expanding-panel-content`
102
+ itself still works correctly, same as the `::details-content` path.
98
103
  - Don't stack multiple `contentIsOnTop` panels as direct siblings (linked via a shared `name` or
99
104
  not). The overlay is absolutely positioned so it doesn't push the next element down — which
100
105
  means a sibling `ExpandingPanel` placed right after it sits exactly where the overlay renders,
@@ -23,7 +23,9 @@
23
23
  :aria-labelledby="`id-${name}-trigger`"
24
24
  role="region"
25
25
  >
26
- <slot name="content"></slot>
26
+ <div class="inner">
27
+ <slot name="content"></slot>
28
+ </div>
27
29
  </div>
28
30
  </details>
29
31
  </div>
@@ -187,6 +189,50 @@ const onDetailsToggle = (event: Event) => {
187
189
  }
188
190
  }
189
191
  }
192
+
193
+ /* ::details-content isn't supported in WebKit (Safari/iOS) as of 2026-08 — the two rule
194
+ blocks above are silently dropped there entirely (no error, no animation, and crucially
195
+ content-is-on-top's position: absolute never applies, so the overlay renders in normal
196
+ document flow instead). This reproduces both using ExpandingPanelClassic's
197
+ grid-template-rows technique (broadly supported, including Safari), targeting the real
198
+ .expanding-panel-content element (and its .inner child, matching Classic's structure —
199
+ .inner's own overflow:hidden is what lets the grid row actually collapse to 0, since a
200
+ plain block child's default min-height:auto would otherwise floor it at content height)
201
+ instead of the pseudo-element. */
202
+ @supports not selector(::details-content) {
203
+ .expanding-panel-details {
204
+ .expanding-panel-content {
205
+ display: grid;
206
+ grid-template-rows: 0fr;
207
+ transition: grid-template-rows v-bind(animationDurationStr) ease-in-out;
208
+ will-change: grid-template-rows;
209
+
210
+ .inner {
211
+ overflow: hidden;
212
+ margin-top: 0;
213
+ }
214
+ }
215
+
216
+ &[open] .expanding-panel-content {
217
+ grid-template-rows: 1fr;
218
+ }
219
+ }
220
+ }
221
+
222
+ &.content-is-on-top {
223
+ @supports not selector(::details-content) {
224
+ .expanding-panel-details {
225
+ position: relative;
226
+
227
+ .expanding-panel-content {
228
+ position: absolute;
229
+ top: calc(100% + var(--expanding-panel-content-gap, 0px));
230
+ inset-inline: 0;
231
+ z-index: var(--expanding-panel-content-z-index, 10);
232
+ }
233
+ }
234
+ }
235
+ }
190
236
  }
191
237
  }
192
238
  </style>
@@ -7,7 +7,9 @@ exports[`ExpandingPanel > renders correct HTML structure with all props set 1`]
7
7
  <div class="label-wrapper"></div>
8
8
  <div class="icon-wrapper icon-wrapper--hidden"><span class="iconify i-bi:caret-down-fill icon" aria-hidden="true"></span></div>
9
9
  </summary>
10
- <div id="id-snap-all-content" class="expanding-panel-content" aria-labelledby="id-snap-all-trigger" role="region"></div>
10
+ <div id="id-snap-all-content" class="expanding-panel-content" aria-labelledby="id-snap-all-trigger" role="region">
11
+ <div class="inner"></div>
12
+ </div>
11
13
  </details>
12
14
  </div>"
13
15
  `;
@@ -19,7 +21,9 @@ exports[`ExpandingPanel > renders correct HTML structure with contentIsOnTop tru
19
21
  <div class="label-wrapper"></div>
20
22
  <div class="icon-wrapper"><span class="iconify i-bi:caret-down-fill icon" aria-hidden="true"></span></div>
21
23
  </summary>
22
- <div id="id-snap-content-on-top-content" class="expanding-panel-content" aria-labelledby="id-snap-content-on-top-trigger" role="region"></div>
24
+ <div id="id-snap-content-on-top-content" class="expanding-panel-content" aria-labelledby="id-snap-content-on-top-trigger" role="region">
25
+ <div class="inner"></div>
26
+ </div>
23
27
  </details>
24
28
  </div>"
25
29
  `;
@@ -31,7 +35,9 @@ exports[`ExpandingPanel > renders correct HTML structure with default props 1`]
31
35
  <div class="label-wrapper"></div>
32
36
  <div class="icon-wrapper"><span class="iconify i-bi:caret-down-fill icon" aria-hidden="true"></span></div>
33
37
  </summary>
34
- <div id="id-snap-default-content" class="expanding-panel-content" aria-labelledby="id-snap-default-trigger" role="region"></div>
38
+ <div id="id-snap-default-content" class="expanding-panel-content" aria-labelledby="id-snap-default-trigger" role="region">
39
+ <div class="inner"></div>
40
+ </div>
35
41
  </details>
36
42
  </div>"
37
43
  `;
@@ -44,7 +50,9 @@ exports[`ExpandingPanel > renders correct HTML structure with populated slots 1`
44
50
  <div class="icon-wrapper"><span>▸</span></div>
45
51
  </summary>
46
52
  <div id="id-snap-slots-content" class="expanding-panel-content" aria-labelledby="id-snap-slots-trigger" role="region">
47
- <p>Content text</p>
53
+ <div class="inner">
54
+ <p>Content text</p>
55
+ </div>
48
56
  </div>
49
57
  </details>
50
58
  </div>"
@@ -13,12 +13,14 @@ exports[`NavigationItems > renders correct HTML structure with default props 1`]
13
13
  <div class="icon-wrapper"><span class="iconify i-mdi:chevron-down icon" aria-hidden="true"></span></div>
14
14
  </summary>
15
15
  <div id="id-overflow-navigation-group-content" class="expanding-panel-content" aria-labelledby="id-overflow-navigation-group-trigger" role="region">
16
- <div class="overflow-navigation-sub-nav-inner">
17
- <ul class="overflow-navigation-sub-nav-list">
18
- <li class="overflow-navigation-sub-nav-item"><a href="/forms/examples/buttons" class="overflow-navigation-sub-nav-link" role="menuitem"><span class="overflow-navigation-sub-nav-text">Buttons</span></a></li>
19
- <li class="overflow-navigation-sub-nav-item"><a href="/ui/tabs" class="overflow-navigation-sub-nav-link" role="menuitem"><span class="overflow-navigation-sub-nav-text">Tabs</span></a></li>
20
- </ul>
21
- <div aria-hidden="true" class="overflow-sub-nav-indicator-hovered"></div>
16
+ <div class="inner">
17
+ <div class="overflow-navigation-sub-nav-inner">
18
+ <ul class="overflow-navigation-sub-nav-list">
19
+ <li class="overflow-navigation-sub-nav-item"><a href="/forms/examples/buttons" class="overflow-navigation-sub-nav-link" role="menuitem"><span class="overflow-navigation-sub-nav-text">Buttons</span></a></li>
20
+ <li class="overflow-navigation-sub-nav-item"><a href="/ui/tabs" class="overflow-navigation-sub-nav-link" role="menuitem"><span class="overflow-navigation-sub-nav-text">Tabs</span></a></li>
21
+ </ul>
22
+ <div aria-hidden="true" class="overflow-sub-nav-indicator-hovered"></div>
23
+ </div>
22
24
  </div>
23
25
  </div>
24
26
  </details>
@@ -46,12 +46,14 @@ exports[`ResponsiveHeader > renders correct HTML structure with default props 1`
46
46
  <div class="icon-wrapper"><span class="iconify i-mdi:chevron-down icon" aria-hidden="true"></span></div>
47
47
  </summary>
48
48
  <div id="id-overflow-navigation-group-content" class="expanding-panel-content" aria-labelledby="id-overflow-navigation-group-trigger" role="region">
49
- <div class="overflow-navigation-sub-nav-inner">
50
- <ul class="overflow-navigation-sub-nav-list">
51
- <li class="overflow-navigation-sub-nav-item"><a href="/forms/examples/buttons" class="overflow-navigation-sub-nav-link" role="menuitem"><span class="overflow-navigation-sub-nav-text">Buttons</span></a></li>
52
- <li class="overflow-navigation-sub-nav-item"><a href="/ui/tabs" class="overflow-navigation-sub-nav-link" role="menuitem"><span class="overflow-navigation-sub-nav-text">Tabs</span></a></li>
53
- </ul>
54
- <div aria-hidden="true" class="overflow-sub-nav-indicator-hovered"></div>
49
+ <div class="inner">
50
+ <div class="overflow-navigation-sub-nav-inner">
51
+ <ul class="overflow-navigation-sub-nav-list">
52
+ <li class="overflow-navigation-sub-nav-item"><a href="/forms/examples/buttons" class="overflow-navigation-sub-nav-link" role="menuitem"><span class="overflow-navigation-sub-nav-text">Buttons</span></a></li>
53
+ <li class="overflow-navigation-sub-nav-item"><a href="/ui/tabs" class="overflow-navigation-sub-nav-link" role="menuitem"><span class="overflow-navigation-sub-nav-text">Tabs</span></a></li>
54
+ </ul>
55
+ <div aria-hidden="true" class="overflow-sub-nav-indicator-hovered"></div>
56
+ </div>
55
57
  </div>
56
58
  </div>
57
59
  </details>
@@ -67,12 +67,12 @@ exports[`ServicesSection > renders correct HTML structure 1`] = `
67
67
  <div class="label-wrapper">FAQ question?</div>
68
68
  <div class="icon-wrapper"><span class="iconify i-mdi:chevron-down" aria-hidden="true"></span></div>
69
69
  </summary>
70
- </details>
71
- <div id="id-faq-v-0-0-0-content" class="expanding-panel-content" aria-labelledby="id-faq-v-0-0-0-trigger" role="region">
72
- <div class="inner">
73
- <p class="services-section__faq-answer">FAQ answer.</p>
70
+ <div id="id-faq-v-0-0-0-content" class="expanding-panel-content" aria-labelledby="id-faq-v-0-0-0-trigger" role="region">
71
+ <div class="inner">
72
+ <p class="services-section__faq-answer">FAQ answer.</p>
73
+ </div>
74
74
  </div>
75
- </div>
75
+ </details>
76
76
  </div>
77
77
  </div>
78
78
  <div class="glass-panel services-section__glass-panel p-24">
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "srcdev-nuxt-components",
3
3
  "type": "module",
4
- "version": "9.3.5",
4
+ "version": "9.3.6",
5
5
  "main": "nuxt.config.ts",
6
6
  "types": "types.d.ts",
7
7
  "license": "MIT",