srcdev-nuxt-components 9.5.5 → 9.5.7
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/skills/component-compliance-checklist.md +6 -2
- package/.claude/skills/components/action-menu.md +21 -15
- package/.claude/skills/components/deep-expanding-menu.md +18 -6
- package/.claude/skills/components/display-tooltip-defined.md +2 -1
- package/.claude/skills/components/display-tooltip.md +10 -4
- package/.claude/skills/components/navigation-items.md +1 -0
- package/.claude/skills/components/opening-hours.md +12 -0
- package/.claude/skills/components/pop-over.md +11 -8
- package/.claude/skills/components/price-list.md +10 -1
- package/.claude/skills/components/responsive-header.md +25 -0
- package/.claude/skills/components/select-menu.md +18 -8
- package/.claude/skills/components/site-header.md +2 -0
- package/.claude/skills/components/ui-block-decorated.md +7 -0
- package/.claude/skills/composable-anchored-popover.md +108 -0
- package/.claude/skills/composable-tooltips-guide.md +3 -1
- package/.claude/skills/index.md +1 -0
- package/.claude/skills/storybook-add-story.md +14 -0
- package/.vscode/srcdev-component-responsive-header.code-snippets +3 -1
- package/.vscode/srcdev-component-site-header.code-snippets +3 -1
- package/app/assets/styles/setup/06.utility-classes/animations/_animation-scroller-x.css +5 -3
- package/app/assets/styles/setup/06.utility-classes/animations/_auto-rotate.css +5 -3
- package/app/assets/styles/setup/06.utility-classes/animations/_entry-exit-blur.css +5 -3
- package/app/assets/styles/setup/06.utility-classes/animations/_entry-slide-in.css +5 -3
- package/app/assets/styles/setup/06.utility-classes/animations/_entry-zoom-reveal.css +5 -3
- package/app/components/01.atoms/animations/entry/CONSUMER-STYLING.md +6 -2
- package/app/components/01.atoms/display-tooltip/CONSUMER-STYLING.md +15 -3
- package/app/components/01.atoms/display-tooltip/DisplayTooltip.vue +62 -3
- package/app/components/01.atoms/display-tooltip/tests/DisplayTooltip.spec.ts +36 -1
- package/app/components/01.atoms/display-tooltip/tests/__snapshots__/DisplayTooltip.spec.ts.snap +1 -1
- package/app/components/01.atoms/navigation/skip-links/SkipLinks.vue +1 -1
- package/app/components/01.atoms/pop-over/CONSUMER-STYLING.md +9 -3
- package/app/components/01.atoms/pop-over/PopOver.vue +70 -9
- package/app/components/01.atoms/pop-over/tests/PopOver.spec.ts +53 -0
- package/app/components/01.atoms/pop-over/tests/__snapshots__/PopOver.spec.ts.snap +1 -1
- package/app/components/01.atoms/ui-block-decorated/CONSUMER-STYLING.md +12 -4
- package/app/components/01.atoms/ui-block-decorated/UiBlockDecorated.vue +20 -16
- package/app/components/01.atoms/ui-block-decorated/stories/UiBlockDecorated.stories.ts +124 -96
- package/app/components/02.molecules/action-menu/ActionMenu.vue +41 -16
- package/app/components/02.molecules/action-menu/CONSUMER-STYLING.md +16 -3
- package/app/components/02.molecules/action-menu/tests/ActionMenu.spec.ts +28 -0
- package/app/components/02.molecules/action-menu/tests/__snapshots__/ActionMenu.spec.ts.snap +3 -3
- package/app/components/02.molecules/display-tooltip-defined/DisplayTooltipDefined.vue +2 -1
- package/app/components/02.molecules/display-tooltip-defined/stories/DisplayTooltipDefined.stories.ts +7 -4
- package/app/components/02.molecules/display-tooltip-defined/tests/DisplayTooltipDefined.spec.ts +20 -1
- package/app/components/02.molecules/display-tooltip-defined/tests/__snapshots__/DisplayTooltipDefined.spec.ts.snap +1 -1
- package/app/components/02.molecules/navigation/deep-expanding-menu/CONSUMER-STYLING.md +18 -4
- package/app/components/02.molecules/navigation/deep-expanding-menu/DeepExpandingMenu.vue +87 -16
- package/app/components/02.molecules/navigation/deep-expanding-menu/tests/DeepExpandingMenu.spec.ts +83 -1
- package/app/components/02.molecules/navigation/deep-expanding-menu/tests/__snapshots__/DeepExpandingMenu.spec.ts.snap +2 -2
- package/app/components/02.molecules/navigation/site-navigation/SiteNavigation.vue +1 -1
- package/app/components/02.molecules/navigation/tab-navigation/TabNavigation.vue +1 -1
- package/app/components/02.molecules/opening-hours/CONSUMER-STYLING.md +11 -1
- package/app/components/02.molecules/opening-hours/OpeningHours.vue +71 -43
- package/app/components/02.molecules/opening-hours/stories/OpeningHours.stories.ts +138 -6
- package/app/components/02.molecules/opening-hours/tests/OpeningHours.spec.ts +53 -0
- package/app/components/02.molecules/opening-hours/tests/__snapshots__/OpeningHours.spec.ts.snap +1 -1
- package/app/components/02.molecules/opening-hours/tests/buildOpeningHoursSpecification.spec.ts +37 -1
- package/app/components/02.molecules/price-list/CONSUMER-STYLING.md +17 -5
- package/app/components/02.molecules/price-list/PriceList.vue +32 -9
- package/app/components/02.molecules/price-list/stories/PriceList.stories.ts +125 -29
- package/app/components/02.molecules/price-list/tests/PriceList.spec.ts +20 -0
- package/app/components/02.molecules/select-menu/CONSUMER-STYLING.md +17 -5
- package/app/components/02.molecules/select-menu/SelectMenu.vue +51 -16
- package/app/components/02.molecules/select-menu/tests/SelectMenu.spec.ts +36 -0
- package/app/components/03.organisms/google-reviews/stories/GoogleReviews.stories.ts +9 -4
- package/app/components/03.organisms/responsive-header/CONSUMER-STYLING.md +14 -0
- package/app/components/03.organisms/responsive-header/NavigationItems.vue +23 -14
- package/app/components/03.organisms/responsive-header/ResponsiveHeader.vue +58 -30
- package/app/components/03.organisms/responsive-header/stories/ResponsiveHeader.stories.ts +149 -18
- package/app/components/03.organisms/responsive-header/tests/.nuxt/app.config.mjs +21 -0
- package/app/components/03.organisms/responsive-header/tests/.nuxt/components.d.ts +66 -0
- package/app/components/03.organisms/responsive-header/tests/.nuxt/imports.d.ts +30 -0
- package/app/components/03.organisms/responsive-header/tests/.nuxt/manifest/meta/test.json +1 -0
- package/app/components/03.organisms/responsive-header/tests/.nuxt/types/app.config.d.ts +35 -0
- package/app/components/03.organisms/responsive-header/tests/.nuxt/types/build.d.ts +23 -0
- package/app/components/03.organisms/responsive-header/tests/.nuxt/types/builder-env.d.ts +1 -0
- package/app/components/03.organisms/responsive-header/tests/.nuxt/types/components.d.ts +71 -0
- package/app/components/03.organisms/responsive-header/tests/.nuxt/types/imports.d.ts +386 -0
- package/app/components/03.organisms/responsive-header/tests/.nuxt/types/middleware.d.ts +11 -0
- package/app/components/03.organisms/responsive-header/tests/.nuxt/types/modules.d.ts +79 -0
- package/app/components/03.organisms/responsive-header/tests/.nuxt/types/nitro-layouts.d.ts +17 -0
- package/app/components/03.organisms/responsive-header/tests/.nuxt/types/nitro-nuxt.d.ts +64 -0
- package/app/components/03.organisms/responsive-header/tests/.nuxt/types/plugins.d.ts +29 -0
- package/app/components/03.organisms/responsive-header/tests/.nuxt/types/runtime-config.d.ts +32 -0
- package/app/components/03.organisms/responsive-header/tests/.nuxt/types/shared-imports.d.ts +10 -0
- package/app/components/03.organisms/responsive-header/tests/.nuxt/types/vue-shim.d.ts +0 -0
- package/app/components/03.organisms/responsive-header/tests/NavigationItems.spec.ts +16 -0
- package/app/components/03.organisms/responsive-header/tests/ResponsiveHeader.spec.ts +91 -6
- package/app/components/03.organisms/responsive-header/tests/__snapshots__/ResponsiveHeader.spec.ts.snap +9 -9
- package/app/components/03.organisms/responsive-header/tests/node_modules/.cache/nuxt/chrome-workspace.json +1 -0
- package/app/components/03.organisms/site-header/SiteHeader.vue +8 -0
- package/app/components/03.organisms/site-header/tests/__snapshots__/SiteHeader.spec.ts.snap +1 -1
- package/app/composables/tests/useAnchoredPopover.spec.ts +231 -0
- package/app/composables/tests/useTooltips.spec.ts +69 -0
- package/app/composables/useAnchoredPopover.ts +183 -0
- package/app/composables/useTooltips.ts +37 -20
- package/app/utils/opening-hours.ts +23 -2
- package/package.json +1 -1
|
@@ -37,6 +37,7 @@ global token so it matches the rest of the design system out of the box.
|
|
|
37
37
|
| `--select-menu-popover-min-width` | `18rem` | Minimum popover width |
|
|
38
38
|
| `--select-menu-popover-max-height` | `32rem` | Maximum popover height before scrolling |
|
|
39
39
|
| `--select-menu-popover-shadow` | `0 0.4rem 1.6rem rgb(0 0 0 / 12%)` | Popover drop shadow |
|
|
40
|
+
| `--select-menu-popover-z-index` | `999999` | Stacking order in browsers without CSS anchor positioning (see Notes). Ignored where the popover renders in the top layer |
|
|
40
41
|
| `--select-menu-transition-duration` | `200ms` | Open/close fade and chevron-rotate duration |
|
|
41
42
|
|
|
42
43
|
### Options
|
|
@@ -69,7 +70,13 @@ global token so it matches the rest of the design system out of the box.
|
|
|
69
70
|
| Hook | Where | Meaning |
|
|
70
71
|
|---|---|---|
|
|
71
72
|
| `.normal` / `.underlined` | `.select-menu` root | The `inputVariant` prop. `underlined` removes the trigger's top and side borders and its corner radius, matching `InputSelect`'s underlined variant. |
|
|
72
|
-
|
|
|
73
|
+
| `.select-menu-trigger[aria-expanded="true"]` | trigger | Menu open (rotates the chevron). Works with and without the Popover API. |
|
|
74
|
+
| `.select-menu-popover:popover-open` | popover | Menu open, Popover API browsers. |
|
|
75
|
+
| `.select-menu-popover-open` | popover | Menu open, browsers without the Popover API (Safari 16 and older). Style both open hooks in **separate rules**: a selector list containing `:popover-open` is dropped whole where it's unsupported. |
|
|
76
|
+
| `[data-placement="top"]` | popover | Flipped above the trigger, browsers without CSS anchor positioning only. |
|
|
77
|
+
|
|
78
|
+
> Changed 2026-10-05: the chevron hook moved from `:has(.select-menu-popover:popover-open)` on the
|
|
79
|
+
> root to the trigger's `aria-expanded`, so it also works without the Popover API.
|
|
73
80
|
|
|
74
81
|
---
|
|
75
82
|
|
|
@@ -186,10 +193,15 @@ story.
|
|
|
186
193
|
|
|
187
194
|
## Notes
|
|
188
195
|
|
|
189
|
-
- **Popover API + CSS anchor positioning** — same mechanism as `ActionMenu
|
|
190
|
-
|
|
196
|
+
- **Popover API + CSS anchor positioning, with fallbacks** — same mechanism as `ActionMenu`, via the
|
|
197
|
+
shared `useAnchoredPopover` composable. Without anchor positioning (e.g. Safari 17–18), the
|
|
198
|
+
popover is placed with `position: fixed` from the trigger's measured position, re-measured on
|
|
199
|
+
scroll and resize. Without the Popover API (Safari 16 and older), the menu also opens and
|
|
200
|
+
closes in JS, with outside-click and Escape dismissal. In that mode it isn't in the top layer, so
|
|
201
|
+
`--select-menu-popover-z-index` matters, and an ancestor with `transform`, `filter` or
|
|
202
|
+
`contain` becomes its containing block and can misplace it.
|
|
191
203
|
- **Popover left-aligns with the trigger** by default (`left: anchor(left)`), unlike `ActionMenu`
|
|
192
204
|
which right-aligns — matches native `<select>` dropdown behaviour. Flips above the trigger near
|
|
193
205
|
the bottom of the viewport (`position-try-fallbacks: flip-block`).
|
|
194
|
-
- **Chevron rotation** is
|
|
195
|
-
|
|
206
|
+
- **Chevron rotation** is driven by the trigger's `aria-expanded`, which tracks the open state in
|
|
207
|
+
every browser.
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div
|
|
2
|
+
<div
|
|
3
|
+
ref="rootRef"
|
|
4
|
+
class="select-menu"
|
|
5
|
+
:class="[inputVariant, elementClasses]"
|
|
6
|
+
:style="`--_anchor-name: ${anchorName}`"
|
|
7
|
+
>
|
|
3
8
|
<button
|
|
4
9
|
ref="triggerRef"
|
|
5
10
|
:popovertarget="menuId"
|
|
@@ -9,6 +14,7 @@
|
|
|
9
14
|
:aria-label="label"
|
|
10
15
|
aria-haspopup="listbox"
|
|
11
16
|
:aria-expanded="isOpen"
|
|
17
|
+
@click="handleTriggerClick"
|
|
12
18
|
>
|
|
13
19
|
<Icon
|
|
14
20
|
v-if="showIcon && !multiple && selectedOption?.icon"
|
|
@@ -30,6 +36,10 @@
|
|
|
30
36
|
ref="popoverRef"
|
|
31
37
|
popover
|
|
32
38
|
class="select-menu-popover"
|
|
39
|
+
:class="{ 'select-menu-popover-open': usesFallbackPopover && isOpen }"
|
|
40
|
+
:style="positionStyle"
|
|
41
|
+
:data-placement="popoverPlacement"
|
|
42
|
+
@beforetoggle="handleBeforeToggle"
|
|
33
43
|
@toggle="handleToggle"
|
|
34
44
|
@keydown="handleKeydown"
|
|
35
45
|
>
|
|
@@ -103,9 +113,9 @@ const id = useId();
|
|
|
103
113
|
const menuId = `select-menu-${id}`;
|
|
104
114
|
const anchorName = `--select-menu-anchor-${id}`;
|
|
105
115
|
|
|
116
|
+
const rootRef = ref<HTMLDivElement | null>(null);
|
|
106
117
|
const triggerRef = ref<HTMLButtonElement | null>(null);
|
|
107
118
|
const popoverRef = ref<HTMLDivElement | null>(null);
|
|
108
|
-
const isOpen = ref(false);
|
|
109
119
|
|
|
110
120
|
const selectedValues = computed(() => (props.multiple && Array.isArray(modelValue.value) ? modelValue.value : []));
|
|
111
121
|
const selectedOption = computed(() =>
|
|
@@ -137,6 +147,23 @@ const isSelected = (option: SelectMenuOption): boolean =>
|
|
|
137
147
|
const getMenuItems = (): HTMLElement[] =>
|
|
138
148
|
Array.from(popoverRef.value?.querySelectorAll<HTMLElement>('[role="option"]') ?? []);
|
|
139
149
|
|
|
150
|
+
const focusInitialItem = () => {
|
|
151
|
+
const items = getMenuItems();
|
|
152
|
+
const selectedIndex = items.findIndex((el) => el.getAttribute("aria-selected") === "true");
|
|
153
|
+
items[selectedIndex === -1 ? 0 : selectedIndex]?.focus();
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
const {
|
|
157
|
+
isOpen,
|
|
158
|
+
usesFallbackPopover,
|
|
159
|
+
positionStyle,
|
|
160
|
+
popoverPlacement,
|
|
161
|
+
hide,
|
|
162
|
+
handleTriggerClick,
|
|
163
|
+
handleBeforeToggle,
|
|
164
|
+
handleToggle,
|
|
165
|
+
} = useAnchoredPopover({ rootRef, triggerRef, popoverRef, onOpen: focusInitialItem });
|
|
166
|
+
|
|
140
167
|
const selectOption = (option: SelectMenuOption) => {
|
|
141
168
|
if (props.multiple) {
|
|
142
169
|
const next = selectedValues.value.includes(option.value)
|
|
@@ -146,20 +173,10 @@ const selectOption = (option: SelectMenuOption) => {
|
|
|
146
173
|
return;
|
|
147
174
|
}
|
|
148
175
|
modelValue.value = option.value;
|
|
149
|
-
|
|
176
|
+
hide();
|
|
150
177
|
triggerRef.value?.focus();
|
|
151
178
|
};
|
|
152
179
|
|
|
153
|
-
const handleToggle = (event: Event) => {
|
|
154
|
-
const toggleEvent = event as ToggleEvent;
|
|
155
|
-
isOpen.value = toggleEvent.newState === "open";
|
|
156
|
-
if (isOpen.value) {
|
|
157
|
-
const items = getMenuItems();
|
|
158
|
-
const selectedIndex = items.findIndex((el) => el.getAttribute("aria-selected") === "true");
|
|
159
|
-
items[selectedIndex === -1 ? 0 : selectedIndex]?.focus();
|
|
160
|
-
}
|
|
161
|
-
};
|
|
162
|
-
|
|
163
180
|
/**
|
|
164
181
|
* Keyboard navigation following the WAI-ARIA listbox pattern.
|
|
165
182
|
*
|
|
@@ -168,7 +185,7 @@ const handleToggle = (event: Event) => {
|
|
|
168
185
|
* Enter / Space — select the focused option. Closes the menu in single-select mode;
|
|
169
186
|
* toggles the checkbox and keeps the menu open in multi-select mode.
|
|
170
187
|
* Tab — close the menu; let the browser Tab naturally.
|
|
171
|
-
* Escape —
|
|
188
|
+
* Escape — native light dismiss, or the fallback listener without the Popover API.
|
|
172
189
|
*/
|
|
173
190
|
const handleKeydown = (event: KeyboardEvent) => {
|
|
174
191
|
const items = getMenuItems();
|
|
@@ -201,7 +218,7 @@ const handleKeydown = (event: KeyboardEvent) => {
|
|
|
201
218
|
break;
|
|
202
219
|
}
|
|
203
220
|
case "Tab":
|
|
204
|
-
|
|
221
|
+
hide();
|
|
205
222
|
break;
|
|
206
223
|
}
|
|
207
224
|
};
|
|
@@ -288,7 +305,7 @@ watch(
|
|
|
288
305
|
border-radius: 0;
|
|
289
306
|
}
|
|
290
307
|
|
|
291
|
-
|
|
308
|
+
.select-menu-trigger[aria-expanded="true"] .select-menu-trigger-chevron {
|
|
292
309
|
transform: rotate(180deg);
|
|
293
310
|
}
|
|
294
311
|
|
|
@@ -329,6 +346,24 @@ watch(
|
|
|
329
346
|
}
|
|
330
347
|
}
|
|
331
348
|
|
|
349
|
+
/* Kept apart from :popover-open, which would invalidate a shared selector list where unsupported. */
|
|
350
|
+
&.select-menu-popover-open {
|
|
351
|
+
display: block;
|
|
352
|
+
opacity: 1;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
@supports not (anchor-name: --a) {
|
|
356
|
+
position: fixed;
|
|
357
|
+
top: calc(var(--_anchor-bottom, 0px) + var(--select-menu-block-distance, 0.4rem));
|
|
358
|
+
left: var(--_anchor-left, 0px);
|
|
359
|
+
z-index: var(--select-menu-popover-z-index, 999999);
|
|
360
|
+
|
|
361
|
+
&[data-placement="top"] {
|
|
362
|
+
top: auto;
|
|
363
|
+
bottom: calc(var(--_anchor-top-inverse, 0px) + var(--select-menu-block-distance, 0.4rem));
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
332
367
|
.select-menu-list {
|
|
333
368
|
display: grid;
|
|
334
369
|
grid-template-columns: auto auto 1fr;
|
|
@@ -482,4 +482,40 @@ describe("SelectMenu", () => {
|
|
|
482
482
|
expect(wrapper.classes()).toContain("select-menu");
|
|
483
483
|
});
|
|
484
484
|
});
|
|
485
|
+
|
|
486
|
+
// -------------------------
|
|
487
|
+
// Without the Popover API (Safari 16 and older)
|
|
488
|
+
// -------------------------
|
|
489
|
+
describe("Without the Popover API", () => {
|
|
490
|
+
beforeEach(() => {
|
|
491
|
+
Object.defineProperty(HTMLElement.prototype, "showPopover", { value: undefined, writable: true, configurable: true });
|
|
492
|
+
});
|
|
493
|
+
|
|
494
|
+
it("opens from the trigger with the fallback open class and aria-expanded", async () => {
|
|
495
|
+
wrapper = await createWrapper();
|
|
496
|
+
const popover = wrapper.find(".select-menu-popover");
|
|
497
|
+
expect(popover.classes()).not.toContain("select-menu-popover-open");
|
|
498
|
+
|
|
499
|
+
await wrapper.find(".select-menu-trigger").trigger("click");
|
|
500
|
+
expect(popover.classes()).toContain("select-menu-popover-open");
|
|
501
|
+
expect(wrapper.find(".select-menu-trigger").attributes("aria-expanded")).toBe("true");
|
|
502
|
+
});
|
|
503
|
+
|
|
504
|
+
it("closes when a single-select option is chosen", async () => {
|
|
505
|
+
wrapper = await createWrapper();
|
|
506
|
+
await wrapper.find(".select-menu-trigger").trigger("click");
|
|
507
|
+
await wrapper.findAll('[role="option"]')[1]!.trigger("click");
|
|
508
|
+
|
|
509
|
+
expect(wrapper.emitted("update:modelValue")?.[0]).toEqual(["fr"]);
|
|
510
|
+
expect(wrapper.find(".select-menu-popover").classes()).not.toContain("select-menu-popover-open");
|
|
511
|
+
});
|
|
512
|
+
|
|
513
|
+
it("stays open while toggling options in multiple mode", async () => {
|
|
514
|
+
wrapper = await createWrapper({ multiple: true, modelValue: [] });
|
|
515
|
+
await wrapper.find(".select-menu-trigger").trigger("click");
|
|
516
|
+
await wrapper.findAll('[role="option"]')[1]!.trigger("click");
|
|
517
|
+
|
|
518
|
+
expect(wrapper.find(".select-menu-popover").classes()).toContain("select-menu-popover-open");
|
|
519
|
+
});
|
|
520
|
+
});
|
|
485
521
|
});
|
|
@@ -54,14 +54,19 @@ const meta: Meta<StoryArgs> = {
|
|
|
54
54
|
authorLineClamp: {
|
|
55
55
|
control: "select",
|
|
56
56
|
options: lineClampOptions,
|
|
57
|
-
description:
|
|
58
|
-
|
|
57
|
+
description:
|
|
58
|
+
"**Story control, not a prop.** Sets the `--google-reviews-author-line-clamp` CSS token on a wrapper. " +
|
|
59
|
+
"To use it in an app, set the token in your own CSS, e.g. `.reviews { --google-reviews-author-line-clamp: 1; }`. " +
|
|
60
|
+
"`1` is single-line ellipsis.",
|
|
61
|
+
table: { category: "CSS tokens (story only, set in your CSS)" },
|
|
59
62
|
},
|
|
60
63
|
textLineClamp: {
|
|
61
64
|
control: "select",
|
|
62
65
|
options: lineClampOptions,
|
|
63
|
-
description:
|
|
64
|
-
|
|
66
|
+
description:
|
|
67
|
+
"**Story control, not a prop.** Sets the `--google-reviews-text-line-clamp` CSS token on a wrapper. " +
|
|
68
|
+
"To use it in an app, set the token in your own CSS, e.g. `.reviews { --google-reviews-text-line-clamp: 3; }`.",
|
|
69
|
+
table: { category: "CSS tokens (story only, set in your CSS)" },
|
|
65
70
|
},
|
|
66
71
|
data: { control: "object", description: "GoogleReviewsData, usually from useGoogleReviews()" },
|
|
67
72
|
minRating: { control: { type: "range", min: 0, max: 5, step: 1 }, description: "Hide reviews below this rating; shows the filter notice" },
|
|
@@ -11,6 +11,7 @@ are documented alongside it since that's where they're consumed.
|
|
|
11
11
|
|---|---|---|
|
|
12
12
|
| `--responsive-header-link-font-size` | `inherit` | Nav-link font-size. **Always set this to a fixed value.** Leaving it `inherit` means a fluid ancestor font-size (`clamp()`/`vw`) can drift nav-item text width without ever re-triggering the overflow-collapse measurement — see the component skill doc's "measurement pipeline" section for the full mechanism. |
|
|
13
13
|
| `--responsive-header-link-color` | `inherit` | Link/summary text colour. |
|
|
14
|
+
| `--responsive-header-main-nav-justify-content` | `space-between` | How the main nav's groups are spread along the bar. Use `flex-start`, `safe center` or `safe end`; plain `center`/`end` let items that don't fit spill off the start edge, where they're clipped. (Safari 16 ignores `safe` values and falls back to start alignment.) |
|
|
14
15
|
| `--responsive-header-color` | `inherit` | Root element text colour. |
|
|
15
16
|
| `--responsive-header-margin` | `0` | Root element margin. |
|
|
16
17
|
| `--responsive-header-bg` | `transparent` | Root element background. |
|
|
@@ -20,8 +21,21 @@ are documented alongside it since that's where they're consumed.
|
|
|
20
21
|
| `--responsive-header-max-height` | `none` | Root element max-height. |
|
|
21
22
|
| `--responsive-header-inline-size` | `100%` | Root element inline-size. |
|
|
22
23
|
| `--responsive-header-sub-nav-bg` / `-border` / `-border-radius` / `-padding` | `Canvas` / `1px solid #efefef75` / `8px` / `12px` | Top-bar dropdown panel (`.main-navigation-sub-nav`). |
|
|
24
|
+
| `--responsive-header-sub-nav-max-inline-size` | `min(48rem, calc(100vw - 3.2rem))` | Widest the top-bar dropdown panel gets; long child names wrap inside it. |
|
|
25
|
+
| `--responsive-header-sub-nav-max-block-size` | `70vh` | Tallest the top-bar dropdown panel gets before it scrolls, so a long child list stays reachable under a sticky header. |
|
|
23
26
|
| `--responsive-header-overflow-btn-bg` / `-size` / `-border` / `-outline` / `-icon-color` / `-hover-outline` | `Canvas` / `20px` / `1px solid #ffffff90` / `1px solid #ffffff10` / `inherit` / `1px solid #ffffff` | Overflow burger button. |
|
|
24
27
|
| `--responsive-header-overflow-nav-bg` / `-border` / `-border-radius` / `-padding-block` | `Canvas` / `1px solid #ffffff90` / `8px` / `12px` | Overflow panel container. |
|
|
28
|
+
| `--responsive-header-overflow-nav-max-block-size` | `70vh` | Tallest the overflow panel gets before it scrolls. |
|
|
29
|
+
| `--overflow-nav-max-inline-size` | `calc(100vw - 3.2rem)` | Widest the overflow panel's content gets (set on `NavigationItems`); long names wrap inside it. |
|
|
30
|
+
|
|
31
|
+
Link and dropdown text is never line-clamped: it names where the link goes. Long unbroken names
|
|
32
|
+
wrap inside the dropdown and overflow panels; in the top bar, an item too wide to fit moves to the
|
|
33
|
+
overflow menu.
|
|
34
|
+
|
|
35
|
+
> **Changed 2026-10-05**: the dropdown and overflow panels had no maximum size, so a long child
|
|
36
|
+
> name or a long child list ran off-screen. They are now capped by the four tokens above.
|
|
37
|
+
> `--_overflow-drop-down-width`, a private token nothing ever set, is gone (`min-width: fit-content`
|
|
38
|
+
> directly).
|
|
25
39
|
| `--responsive-nav-decorator-indicator-color` | `currentColor` | Active-item indicator bar/underline. |
|
|
26
40
|
| `--responsive-nav-decorator-hovered-indicator-color` | inherits the above | Indicator colour while hovering. |
|
|
27
41
|
| `--responsive-nav-decorator-hovered-bg` | `oklch(100% 0 0 / 8%)` | Hover highlight background behind the hovered item. |
|
|
@@ -27,8 +27,7 @@
|
|
|
27
27
|
'is-active': isActiveNavItem(link),
|
|
28
28
|
}"
|
|
29
29
|
:style="{
|
|
30
|
-
'--_main-navigation-item-width':
|
|
31
|
-
mainNavigationState.clonedNavLinks?.[groupKey]?.[localIndex]?.config?.width + 'px',
|
|
30
|
+
'--_main-navigation-item-width': itemWidth(mainNavigationState.clonedNavLinks?.[groupKey]?.[localIndex]),
|
|
32
31
|
}"
|
|
33
32
|
:data-group-key="groupKey"
|
|
34
33
|
:data-local-index="localIndex"
|
|
@@ -48,8 +47,7 @@
|
|
|
48
47
|
'is-active': isActiveNavItem(link),
|
|
49
48
|
}"
|
|
50
49
|
:style="{
|
|
51
|
-
'--_main-navigation-item-width':
|
|
52
|
-
mainNavigationState.clonedNavLinks?.[groupKey]?.[localIndex]?.config?.width + 'px',
|
|
50
|
+
'--_main-navigation-item-width': itemWidth(mainNavigationState.clonedNavLinks?.[groupKey]?.[localIndex]),
|
|
53
51
|
}"
|
|
54
52
|
:data-group-key="groupKey"
|
|
55
53
|
:data-local-index="localIndex"
|
|
@@ -66,11 +64,11 @@
|
|
|
66
64
|
<template #summary>
|
|
67
65
|
<span
|
|
68
66
|
class="overflow-navigation-text"
|
|
69
|
-
:aria-label="
|
|
67
|
+
:aria-label="submenuLabel(link)"
|
|
70
68
|
role="menuitem"
|
|
71
69
|
:aria-haspopup="true"
|
|
72
70
|
>
|
|
73
|
-
{{ link.childLinksTitle }}
|
|
71
|
+
{{ link.childLinksTitle ?? link.name }}
|
|
74
72
|
</span>
|
|
75
73
|
</template>
|
|
76
74
|
<template #icon>
|
|
@@ -81,7 +79,7 @@
|
|
|
81
79
|
<ul class="overflow-navigation-sub-nav-list" @mouseleave="hoveredChildKey = null">
|
|
82
80
|
<li
|
|
83
81
|
v-for="(childLink, childIndex) in link.childLinks"
|
|
84
|
-
:key="
|
|
82
|
+
:key="childIndex"
|
|
85
83
|
class="overflow-navigation-sub-nav-item"
|
|
86
84
|
:class="{
|
|
87
85
|
'is-hovered': hoveredChildKey === `${String(groupKey)}-${localIndex}-${childIndex}`,
|
|
@@ -117,6 +115,8 @@ interface Props {
|
|
|
117
115
|
styleClassPassthrough?: string | string[];
|
|
118
116
|
/** aria-label on the overflow menu — override for localisation. */
|
|
119
117
|
ariaLabel?: string;
|
|
118
|
+
/** aria-label on each submenu summary; {title} is replaced with the item's title. */
|
|
119
|
+
submenuAriaLabel?: string;
|
|
120
120
|
}
|
|
121
121
|
|
|
122
122
|
const props = withDefaults(defineProps<Props>(), {
|
|
@@ -127,8 +127,12 @@ const props = withDefaults(defineProps<Props>(), {
|
|
|
127
127
|
panelVariant: "classic",
|
|
128
128
|
styleClassPassthrough: () => [],
|
|
129
129
|
ariaLabel: "Overflow navigation menu",
|
|
130
|
+
submenuAriaLabel: "{title} submenu",
|
|
130
131
|
});
|
|
131
132
|
|
|
133
|
+
const submenuLabel = (link: ResponsiveHeaderNavItem) =>
|
|
134
|
+
props.submenuAriaLabel.replace("{title}", link.childLinksTitle ?? link.name);
|
|
135
|
+
|
|
132
136
|
const panelComponent = computed(() => (props.panelVariant === "modern" ? ExpandingPanel : ExpandingPanelClassic));
|
|
133
137
|
|
|
134
138
|
const hoveredItemKey = ref<string | null>(null);
|
|
@@ -178,6 +182,9 @@ const widestNavLinkWidthInMainNavigationState = computed(() => {
|
|
|
178
182
|
}, 0);
|
|
179
183
|
});
|
|
180
184
|
|
|
185
|
+
const itemWidth = (link?: ResponsiveHeaderNavItem) =>
|
|
186
|
+
link?.config?.width === undefined ? undefined : `${link.config.width}px`;
|
|
187
|
+
|
|
181
188
|
const { elementClasses, resetElementClasses } = useStyleClassPassthrough(props.styleClassPassthrough);
|
|
182
189
|
|
|
183
190
|
// Performance: Use immediate flag and more efficient watching
|
|
@@ -199,6 +206,7 @@ watch(
|
|
|
199
206
|
on this element to avoid cascade conflicts with ancestor overrides.
|
|
200
207
|
|
|
201
208
|
--overflow-nav-padding-inline (default: 0.8rem)
|
|
209
|
+
--overflow-nav-max-inline-size (default: calc(100vw - 3.2rem))
|
|
202
210
|
--overflow-nav-items-gap (default: 0px)
|
|
203
211
|
--overflow-nav-items-padding-block (default: 0.8rem)
|
|
204
212
|
|
|
@@ -212,10 +220,15 @@ watch(
|
|
|
212
220
|
--overflow-nav-decorator-hovered-bg (default: oklch(100% 0 0 / 6%))
|
|
213
221
|
──────────────────────────────────────────────────────────────────────── */
|
|
214
222
|
|
|
223
|
+
--_max-inline-size: var(--overflow-nav-max-inline-size, calc(100vw - 3.2rem));
|
|
224
|
+
|
|
215
225
|
display: flex;
|
|
216
226
|
flex-direction: column;
|
|
217
227
|
gap: var(--overflow-nav-items-gap, 0px);
|
|
218
228
|
position: relative;
|
|
229
|
+
inline-size: max-content;
|
|
230
|
+
max-inline-size: var(--_max-inline-size);
|
|
231
|
+
overflow-wrap: anywhere;
|
|
219
232
|
|
|
220
233
|
.overflow-navigation-list {
|
|
221
234
|
display: none;
|
|
@@ -224,7 +237,7 @@ watch(
|
|
|
224
237
|
display: flex;
|
|
225
238
|
flex-direction: column;
|
|
226
239
|
gap: var(--overflow-nav-items-gap, 0px);
|
|
227
|
-
min-width: var(--_overflow-navigation-list-min-width,
|
|
240
|
+
min-width: min(var(--_overflow-navigation-list-min-width, 0px), var(--_max-inline-size));
|
|
228
241
|
}
|
|
229
242
|
|
|
230
243
|
.overflow-navigation-item {
|
|
@@ -262,9 +275,7 @@ watch(
|
|
|
262
275
|
border-bottom: 0.1rem solid var(--overflow-nav-link-border-color, #efefef75);
|
|
263
276
|
|
|
264
277
|
.label-wrapper {
|
|
265
|
-
|
|
266
|
-
text-wrap: nowrap;
|
|
267
|
-
}
|
|
278
|
+
min-inline-size: 0;
|
|
268
279
|
}
|
|
269
280
|
.icon-wrapper {
|
|
270
281
|
padding: 0;
|
|
@@ -325,9 +336,7 @@ watch(
|
|
|
325
336
|
border-bottom: 0.1rem solid var(--overflow-nav-link-border-color, #efefef75);
|
|
326
337
|
|
|
327
338
|
.label-wrapper {
|
|
328
|
-
|
|
329
|
-
text-wrap: nowrap;
|
|
330
|
-
}
|
|
339
|
+
min-inline-size: 0;
|
|
331
340
|
}
|
|
332
341
|
.icon-wrapper {
|
|
333
342
|
padding: 0;
|
|
@@ -32,8 +32,7 @@
|
|
|
32
32
|
'is-active': isActiveNavItem(link),
|
|
33
33
|
}"
|
|
34
34
|
:style="{
|
|
35
|
-
'--_main-navigation-item-width':
|
|
36
|
-
mainNavigationState.clonedNavLinks?.[groupKey]?.[localIndex]?.config?.width + 'px',
|
|
35
|
+
'--_main-navigation-item-width': itemWidth(mainNavigationState.clonedNavLinks?.[groupKey]?.[localIndex]),
|
|
37
36
|
}"
|
|
38
37
|
:data-group-key="groupKey"
|
|
39
38
|
:data-local-index="localIndex"
|
|
@@ -52,7 +51,7 @@
|
|
|
52
51
|
<details v-else ref="navigationDetails" class="main-navigation-details" name="navigation-group">
|
|
53
52
|
<summary
|
|
54
53
|
class="main-navigation-details-summary has-toggle-icon"
|
|
55
|
-
:aria-label="
|
|
54
|
+
:aria-label="submenuLabel(link)"
|
|
56
55
|
@mouseenter="handleSummaryHover($event)"
|
|
57
56
|
@focusin="handleSummaryHover($event)"
|
|
58
57
|
@click.prevent="handleSummaryAction($event)"
|
|
@@ -61,11 +60,15 @@
|
|
|
61
60
|
<Icon name="mdi:chevron-down" class="icon" :aria-hidden="true" />
|
|
62
61
|
<Icon v-if="link.iconName" :name="link.iconName" class="decorator-icon" aria-hidden="true" />
|
|
63
62
|
|
|
64
|
-
{{ link.childLinksTitle }}
|
|
63
|
+
{{ link.childLinksTitle ?? link.name }}
|
|
65
64
|
</summary>
|
|
66
65
|
<div class="main-navigation-sub-nav" role="menu" @mouseenter="handleSubNavHover">
|
|
67
66
|
<ul class="main-navigation-sub-nav-list">
|
|
68
|
-
<li
|
|
67
|
+
<li
|
|
68
|
+
v-for="(childLink, childIndex) in link.childLinks"
|
|
69
|
+
:key="childIndex"
|
|
70
|
+
class="main-navigation-sub-nav-item"
|
|
71
|
+
>
|
|
69
72
|
<NuxtLink :to="childLink.path" class="main-navigation-sub-nav-link" role="menuitem">
|
|
70
73
|
{{ childLink.name }}
|
|
71
74
|
</NuxtLink>
|
|
@@ -85,7 +88,7 @@
|
|
|
85
88
|
:class="overflowDetailsClass ? [overflowDetailsClass] : []"
|
|
86
89
|
name="overflow-group"
|
|
87
90
|
>
|
|
88
|
-
<summary class="overflow-details-summary has-toggle-icon">
|
|
91
|
+
<summary class="overflow-details-summary has-toggle-icon" :aria-label="overflowButtonLabel">
|
|
89
92
|
<Icon
|
|
90
93
|
:name="overflowDetailsSummaryIcons.more ?? 'gravity-ui:ellipsis'"
|
|
91
94
|
class="icon"
|
|
@@ -104,6 +107,7 @@
|
|
|
104
107
|
:main-navigation-state="mainNavigationState"
|
|
105
108
|
:panel-variant="panelVariant"
|
|
106
109
|
:aria-label="overflowMenuAriaLabel"
|
|
110
|
+
:submenu-aria-label="submenuAriaLabel"
|
|
107
111
|
/>
|
|
108
112
|
</div>
|
|
109
113
|
</details>
|
|
@@ -136,6 +140,10 @@ interface Props {
|
|
|
136
140
|
secondaryNavAriaLabel?: string;
|
|
137
141
|
/** aria-label on the overflow menu itself (forwarded to NavigationItems) — override for localisation. */
|
|
138
142
|
overflowMenuAriaLabel?: string;
|
|
143
|
+
/** Accessible name of the overflow/burger button, which shows only icons. */
|
|
144
|
+
overflowButtonLabel?: string;
|
|
145
|
+
/** aria-label on each dropdown summary; {title} is replaced with the item's title. Forwarded to NavigationItems. */
|
|
146
|
+
submenuAriaLabel?: string;
|
|
139
147
|
}
|
|
140
148
|
|
|
141
149
|
const props = withDefaults(defineProps<Props>(), {
|
|
@@ -155,8 +163,13 @@ const props = withDefaults(defineProps<Props>(), {
|
|
|
155
163
|
mainNavAriaLabel: "Main navigation",
|
|
156
164
|
secondaryNavAriaLabel: "Secondary navigation",
|
|
157
165
|
overflowMenuAriaLabel: "Overflow navigation menu",
|
|
166
|
+
overflowButtonLabel: "More navigation",
|
|
167
|
+
submenuAriaLabel: "{title} submenu",
|
|
158
168
|
});
|
|
159
169
|
|
|
170
|
+
const submenuLabel = (link: ResponsiveHeaderNavItem) =>
|
|
171
|
+
props.submenuAriaLabel.replace("{title}", link.childLinksTitle ?? link.name);
|
|
172
|
+
|
|
160
173
|
const collapseNavigationBelowWidth = computed(
|
|
161
174
|
() => props.collapseBreakpoint !== null || props.collapseAtMainNavIntersection
|
|
162
175
|
);
|
|
@@ -173,6 +186,7 @@ const {
|
|
|
173
186
|
const slots = useSlots();
|
|
174
187
|
|
|
175
188
|
const navigationWrapperRef = useTemplateRef<HTMLDivElement>("navigationWrapper");
|
|
189
|
+
const mainNavRef = useTemplateRef<HTMLElement>("mainNav");
|
|
176
190
|
|
|
177
191
|
const closeAllNavigationDetails = () => {
|
|
178
192
|
navigationDetailsRefs.value?.forEach((element) => {
|
|
@@ -361,7 +375,10 @@ const mainNavigationState = ref<ResponsiveHeaderState>({
|
|
|
361
375
|
firstNav: false,
|
|
362
376
|
secondNav: false,
|
|
363
377
|
},
|
|
364
|
-
|
|
378
|
+
// Copied so the geometry pass's config writes never land in the consumer's own nav data
|
|
379
|
+
clonedNavLinks: Object.fromEntries(
|
|
380
|
+
Object.entries(props.responsiveNavLinks).map(([groupKey, group]) => [groupKey, group.map((link) => ({ ...link }))])
|
|
381
|
+
),
|
|
365
382
|
hasSecondNav: Object.keys(props.responsiveNavLinks).length > 1,
|
|
366
383
|
});
|
|
367
384
|
|
|
@@ -398,12 +415,13 @@ const navigationDetailsRefs = useTemplateRef<HTMLElement[]>("navigationDetails")
|
|
|
398
415
|
|
|
399
416
|
const overflowDetailsRef = useTemplateRef<HTMLDetailsElement>("overflowDetails");
|
|
400
417
|
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
418
|
+
// Item-based rather than reading navListVisibility.firstNav/secondNav, so any group key works and
|
|
419
|
+
// empty nav data never shows a burger with nothing in it
|
|
420
|
+
const showOverflowDetails = computed(() =>
|
|
421
|
+
Object.values(mainNavigationState.value.clonedNavLinks ?? {}).some((group) =>
|
|
422
|
+
group.some((link) => link.config?.visible === false)
|
|
423
|
+
)
|
|
424
|
+
);
|
|
407
425
|
|
|
408
426
|
const mainNavigationMarginBlockEnd = computed(() => {
|
|
409
427
|
return secondaryNavRects.value ? secondaryNavRects.value.width + props.gapBetweenFirstAndSecondNav : 0;
|
|
@@ -462,20 +480,18 @@ const determineNavigationItemVisibility = (rect: DOMRect) => {
|
|
|
462
480
|
return false;
|
|
463
481
|
}
|
|
464
482
|
|
|
465
|
-
//
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
);
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
// Default to visible
|
|
474
|
-
return true;
|
|
483
|
+
// An item fits if it ends within the main nav, whose margin-inline-end already reserves the
|
|
484
|
+
// secondary nav plus the gap. Measured live with the item so both read the same layout.
|
|
485
|
+
const mainNavRect = mainNavRef.value?.getBoundingClientRect();
|
|
486
|
+
if (!mainNavRect) return true;
|
|
487
|
+
return Math.floor(rect.right) <= Math.floor(mainNavRect.right);
|
|
475
488
|
};
|
|
476
489
|
|
|
477
490
|
const initMainNavigationState = () => {
|
|
478
|
-
if (!mainNavigationItemsRefs.value)
|
|
491
|
+
if (!mainNavigationItemsRefs.value?.length) {
|
|
492
|
+
isGeometryReady.value = true;
|
|
493
|
+
return;
|
|
494
|
+
}
|
|
479
495
|
|
|
480
496
|
mainNavigationItemsRefs.value.forEach(async (item) => {
|
|
481
497
|
// await nextTick()
|
|
@@ -642,6 +658,9 @@ onMounted(async () => {
|
|
|
642
658
|
|
|
643
659
|
useResizeObserver(navigationWrapperRef, runGeometryPass);
|
|
644
660
|
|
|
661
|
+
const itemWidth = (link?: ResponsiveHeaderNavItem) =>
|
|
662
|
+
link?.config?.width === undefined ? undefined : `${link.config.width}px`;
|
|
663
|
+
|
|
645
664
|
const { elementClasses, resetElementClasses } = useStyleClassPassthrough(props.styleClassPassthrough);
|
|
646
665
|
|
|
647
666
|
watch(
|
|
@@ -690,6 +709,8 @@ watch(
|
|
|
690
709
|
--responsive-header-sub-nav-border (default: 1px solid #efefef75)
|
|
691
710
|
--responsive-header-sub-nav-border-radius (default: 8px)
|
|
692
711
|
--responsive-header-sub-nav-padding (default: 12px)
|
|
712
|
+
--responsive-header-sub-nav-max-inline-size (default: min(48rem, calc(100vw - 3.2rem)))
|
|
713
|
+
--responsive-header-sub-nav-max-block-size (default: 70vh)
|
|
693
714
|
|
|
694
715
|
--responsive-header-overflow-btn-bg (default: Canvas)
|
|
695
716
|
--responsive-header-overflow-btn-size (default: 20px)
|
|
@@ -702,12 +723,14 @@ watch(
|
|
|
702
723
|
--responsive-header-overflow-nav-border (default: 1px solid #ffffff90)
|
|
703
724
|
--responsive-header-overflow-nav-border-radius (default: 8px)
|
|
704
725
|
--responsive-header-overflow-nav-padding-block (default: 12px)
|
|
726
|
+
--responsive-header-overflow-nav-max-block-size (default: 70vh)
|
|
705
727
|
|
|
706
728
|
--responsive-nav-decorator-indicator-color (default: currentColor)
|
|
707
729
|
--responsive-nav-decorator-hovered-indicator-color (default: inherits --responsive-nav-decorator-indicator-color)
|
|
708
730
|
--responsive-nav-decorator-hovered-bg (default: oklch(100% 0 0 / 8%))
|
|
709
731
|
|
|
710
732
|
--responsive-header-link-font-size (default: inherit)
|
|
733
|
+
--responsive-header-main-nav-justify-content (default: space-between)
|
|
711
734
|
──────────────────────────────────────────────────────────────────────── */
|
|
712
735
|
|
|
713
736
|
/* `inherit` means a viewport-relative (vw/clamp) ancestor font-size silently
|
|
@@ -749,7 +772,7 @@ watch(
|
|
|
749
772
|
display: flex;
|
|
750
773
|
flex-wrap: nowrap;
|
|
751
774
|
flex-grow: 1;
|
|
752
|
-
justify-content: space-between;
|
|
775
|
+
justify-content: var(--responsive-header-main-nav-justify-content, space-between);
|
|
753
776
|
gap: 60px;
|
|
754
777
|
overflow-x: hidden;
|
|
755
778
|
margin-inline-end: v-bind(mainNavigationMarginBlockEndStr);
|
|
@@ -782,7 +805,7 @@ watch(
|
|
|
782
805
|
.main-navigation-link {
|
|
783
806
|
display: flex;
|
|
784
807
|
gap: 6px;
|
|
785
|
-
|
|
808
|
+
white-space: nowrap;
|
|
786
809
|
font-size: var(--responsive-header-link-font-size, inherit);
|
|
787
810
|
color: var(--responsive-header-link-color, inherit);
|
|
788
811
|
text-decoration: none;
|
|
@@ -821,7 +844,7 @@ watch(
|
|
|
821
844
|
.has-toggle-icon {
|
|
822
845
|
display: flex;
|
|
823
846
|
gap: 6px;
|
|
824
|
-
|
|
847
|
+
white-space: nowrap;
|
|
825
848
|
|
|
826
849
|
/* Reserved for the same reason as .decorator-icon above — an
|
|
827
850
|
unsized chevron would widen the summary once its SVG loads. */
|
|
@@ -868,6 +891,9 @@ watch(
|
|
|
868
891
|
translate: 0 12px;
|
|
869
892
|
|
|
870
893
|
min-width: var(--_main-navigation-item-width);
|
|
894
|
+
max-inline-size: var(--responsive-header-sub-nav-max-inline-size, min(48rem, calc(100vw - 3.2rem)));
|
|
895
|
+
max-block-size: var(--responsive-header-sub-nav-max-block-size, 70vh);
|
|
896
|
+
overflow-y: auto;
|
|
871
897
|
|
|
872
898
|
.main-navigation-sub-nav-list {
|
|
873
899
|
display: grid;
|
|
@@ -883,7 +909,7 @@ watch(
|
|
|
883
909
|
|
|
884
910
|
.main-navigation-sub-nav-link {
|
|
885
911
|
display: block;
|
|
886
|
-
|
|
912
|
+
overflow-wrap: anywhere;
|
|
887
913
|
text-decoration: none;
|
|
888
914
|
color: var(--responsive-header-link-color, inherit);
|
|
889
915
|
}
|
|
@@ -994,7 +1020,7 @@ watch(
|
|
|
994
1020
|
align-items: center;
|
|
995
1021
|
justify-content: center;
|
|
996
1022
|
padding-inline: 5px;
|
|
997
|
-
|
|
1023
|
+
white-space: nowrap;
|
|
998
1024
|
|
|
999
1025
|
aspect-ratio: 1;
|
|
1000
1026
|
border-radius: 4px;
|
|
@@ -1048,7 +1074,9 @@ watch(
|
|
|
1048
1074
|
padding-block: var(--responsive-header-overflow-nav-padding-block, 12px);
|
|
1049
1075
|
margin: 0;
|
|
1050
1076
|
z-index: 999;
|
|
1051
|
-
min-width:
|
|
1077
|
+
min-width: fit-content;
|
|
1078
|
+
max-block-size: var(--responsive-header-overflow-nav-max-block-size, 70vh);
|
|
1079
|
+
overflow-y: auto;
|
|
1052
1080
|
|
|
1053
1081
|
display: grid;
|
|
1054
1082
|
grid-auto-flow: row;
|