srcdev-nuxt-components 9.6.18 → 9.6.20
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/rules/consumer/css-relative-colour-calc.md +15 -0
- package/.claude/rules/consumer/ids-from-useid.md +3 -2
- package/.claude/rules/consumer/vue-dynamic-arg-no-spaces.md +16 -0
- package/.claude/skills/component-compliance-checklist.md +11 -1
- package/.claude/skills/component-dynamic-slots.md +2 -2
- package/.claude/skills/component-naming.md +1 -1
- package/.claude/skills/components/accordion-chat.md +99 -0
- package/.claude/skills/components/accordion-group.md +98 -0
- package/.claude/skills/components/action-menu.md +11 -5
- package/.claude/skills/components/auto-grid.md +3 -1
- package/.claude/skills/components/capture-qr-code.md +9 -5
- package/.claude/skills/components/carousel-flip.md +41 -30
- package/.claude/skills/components/column-flow-grid.md +2 -0
- package/.claude/skills/components/dashboard-quad-grid.md +7 -2
- package/.claude/skills/components/dashboard-stats-grid.md +4 -0
- package/.claude/skills/components/decode-qr-code.md +7 -2
- package/.claude/skills/components/deep-expanding-menu-classic.md +31 -30
- package/.claude/skills/components/deep-expanding-menu.md +22 -21
- package/.claude/skills/components/expanding-panel-classic.md +9 -7
- package/.claude/skills/components/expanding-panel.md +9 -8
- package/.claude/skills/components/grid-stack.md +2 -0
- package/.claude/skills/components/layout-grid-by-cols.md +2 -0
- package/.claude/skills/components/masonry-grid.md +9 -1
- package/.claude/skills/components/page-row.md +3 -0
- package/.claude/skills/components/samaritan-prompt.md +24 -8
- package/.claude/skills/components/site-header.md +16 -2
- package/.claude/skills/components/sparkle-effect.md +79 -0
- package/.claude/skills/index.md +16 -14
- package/.claude/skills/storybook-add-story.md +33 -0
- package/.claude/skills/testing-add-unit-test.md +26 -0
- package/.claude/skills/using-component-skills.md +1 -1
- package/.vscode/srcdev-component-accordion-chat.code-snippets +42 -0
- package/.vscode/srcdev-component-accordion-group.code-snippets +38 -0
- package/.vscode/srcdev-component-action-menu.code-snippets +3 -3
- package/.vscode/srcdev-component-capture-decode-qr-code.code-snippets +11 -2
- package/.vscode/srcdev-component-carousel-flip.code-snippets +22 -4
- package/.vscode/srcdev-component-deep-expanding-menu-classic.code-snippets +32 -2
- package/.vscode/srcdev-component-deep-expanding-menu.code-snippets +31 -1
- package/.vscode/srcdev-component-samaritan-prompt.code-snippets +5 -4
- package/.vscode/srcdev-component-site-header.code-snippets +13 -1
- package/.vscode/srcdev-component-sparkle-effect.code-snippets +33 -0
- package/app/components/01.atoms/animations/sparkle-effect/CONSUMER-STYLING.md +75 -0
- package/app/components/01.atoms/animations/sparkle-effect/SparkleEffect.vue +213 -0
- package/app/components/01.atoms/animations/sparkle-effect/stories/SparkleEffect.stories.ts +303 -0
- package/app/components/01.atoms/animations/sparkle-effect/tests/SparkleEffect.spec.ts +150 -0
- package/app/components/01.atoms/content-wrappers/docs-pages/ContentDocs.vue +1 -1
- package/app/components/01.atoms/content-wrappers/layout-grid/dashboard-quad-grid/CONSUMER-STYLING.md +12 -2
- package/app/components/01.atoms/content-wrappers/layout-grid/dashboard-quad-grid/DashboardQuadGrid.vue +19 -17
- package/app/components/01.atoms/content-wrappers/layout-grid/dashboard-quad-grid/stories/DashboardQuadGrid.stories.ts +42 -0
- package/app/components/01.atoms/content-wrappers/layout-grid/dashboard-quad-grid/tests/DashboardQuadGrid.spec.ts +31 -44
- package/app/components/01.atoms/content-wrappers/layout-grid/dashboard-stats-grid/CONSUMER-STYLING.md +13 -1
- package/app/components/01.atoms/content-wrappers/layout-grid/dashboard-stats-grid/DashboardStatsGrid.vue +40 -31
- package/app/components/01.atoms/content-wrappers/layout-grid/dashboard-stats-grid/stories/DashboardStatsGrid.stories.ts +47 -0
- package/app/components/01.atoms/content-wrappers/layout-grid/dashboard-stats-grid/tests/DashboardStatsGrid.spec.ts +51 -53
- package/app/components/01.atoms/content-wrappers/layout-grid/layout-grid-by-cols/CONSUMER-STYLING.md +3 -0
- package/app/components/01.atoms/content-wrappers/layout-grid/layout-grid-by-cols/LayoutGridByCols.vue +4 -0
- package/app/components/01.atoms/content-wrappers/layout-grid/layout-grid-by-cols/stories/LayoutGridByCols.stories.ts +47 -2
- package/app/components/01.atoms/content-wrappers/layout-grid/layout-grid-by-cols/tests/LayoutGridByCols.spec.ts +36 -23
- package/app/components/01.atoms/grids/auto-grid/AutoGrid.vue +7 -3
- package/app/components/01.atoms/grids/auto-grid/CONSUMER-STYLING.md +1 -1
- package/app/components/01.atoms/grids/auto-grid/stories/AutoGrid.stories.ts +46 -1
- package/app/components/01.atoms/grids/auto-grid/tests/AutoGrid.spec.ts +40 -27
- package/app/components/01.atoms/grids/auto-grid/tests/__snapshots__/AutoGrid.spec.ts.snap +1 -1
- package/app/components/01.atoms/grids/column-flow-grid/CONSUMER-STYLING.md +9 -0
- package/app/components/01.atoms/grids/column-flow-grid/ColumnFlowGrid.vue +3 -2
- package/app/components/01.atoms/grids/column-flow-grid/stories/ColumnFlowGrid.stories.ts +46 -0
- package/app/components/01.atoms/grids/column-flow-grid/tests/ColumnFlowGrid.spec.ts +24 -11
- package/app/components/01.atoms/grids/grid-stack/CONSUMER-STYLING.md +3 -0
- package/app/components/01.atoms/grids/grid-stack/GridStack.vue +2 -0
- package/app/components/01.atoms/grids/grid-stack/stories/GridStack.stories.ts +41 -0
- package/app/components/01.atoms/grids/grid-stack/tests/GridStack.spec.ts +26 -13
- package/app/components/01.atoms/grids/masonry-grid/CONSUMER-STYLING.md +15 -0
- package/app/components/01.atoms/grids/masonry-grid/MasonryGrid.vue +51 -31
- package/app/components/01.atoms/grids/masonry-grid/stories/MasonryGrid.stories.ts +48 -0
- package/app/components/01.atoms/grids/masonry-grid/tests/MasonryGrid.spec.ts +84 -29
- package/app/components/01.atoms/page-row/CONSUMER-STYLING.md +6 -1
- package/app/components/01.atoms/page-row/PageRow.vue +16 -15
- package/app/components/01.atoms/page-row/stories/PageRow.stories.ts +23 -0
- package/app/components/01.atoms/page-row/tests/PageRow.spec.ts +6 -5
- package/app/components/02.molecules/action-menu/ActionMenu.vue +6 -2
- package/app/components/02.molecules/action-menu/ActionMenuItem.vue +6 -3
- package/app/components/02.molecules/action-menu/CONSUMER-STYLING.md +11 -4
- package/app/components/02.molecules/action-menu/stories/ActionMenu.stories.ts +130 -18
- package/app/components/02.molecules/expandable/accordion-chat/AccordionChat.vue +227 -0
- package/app/components/02.molecules/expandable/accordion-chat/CONSUMER-STYLING.md +150 -0
- package/app/components/02.molecules/expandable/accordion-chat/stories/AccordionChat.stories.ts +347 -0
- package/app/components/02.molecules/expandable/accordion-chat/tests/AccordionChat.spec.ts +205 -0
- package/app/components/02.molecules/expandable/accordion-chat/tests/__snapshots__/AccordionChat.spec.ts.snap +40 -0
- package/app/components/02.molecules/expandable/{accordian/AccordianCore.vue → accordion-group/AccordionGroup.vue} +32 -20
- package/app/components/02.molecules/expandable/accordion-group/CONSUMER-STYLING.md +66 -0
- package/app/components/02.molecules/expandable/accordion-group/stories/AccordionGroup.stories.ts +238 -0
- package/app/components/02.molecules/expandable/accordion-group/tests/AccordionGroup.spec.ts +172 -0
- package/app/components/02.molecules/expandable/accordion-group/tests/__snapshots__/AccordionGroup.spec.ts.snap +45 -0
- package/app/components/02.molecules/expandable/expanding-panel/CONSUMER-STYLING.md +1 -0
- package/app/components/02.molecules/expandable/expanding-panel/ExpandingPanel.vue +11 -6
- package/app/components/02.molecules/expandable/expanding-panel/stories/ExpandingPanel.stories.ts +82 -1
- package/app/components/02.molecules/expandable/expanding-panel/tests/ExpandingPanel.spec.ts +24 -30
- package/app/components/02.molecules/expandable/expanding-panel/tests/__snapshots__/ExpandingPanel.spec.ts.snap +8 -8
- package/app/components/02.molecules/expandable/expanding-panel-classic/CONSUMER-STYLING.md +1 -0
- package/app/components/02.molecules/expandable/expanding-panel-classic/ExpandingPanelClassic.vue +10 -5
- package/app/components/02.molecules/expandable/expanding-panel-classic/stories/ExpandingPanelClassic.stories.ts +83 -1
- package/app/components/02.molecules/expandable/expanding-panel-classic/tests/ExpandingPanelClassic.spec.ts +24 -30
- package/app/components/02.molecules/expandable/expanding-panel-classic/tests/__snapshots__/ExpandingPanelClassic.spec.ts.snap +8 -8
- package/app/components/02.molecules/navigation/deep-expanding-menu/CONSUMER-STYLING.md +63 -44
- package/app/components/02.molecules/navigation/deep-expanding-menu/DeepExpandingMenu.vue +74 -37
- package/app/components/02.molecules/navigation/deep-expanding-menu/stories/DeepExpandingMenu.stories.ts +139 -19
- package/app/components/02.molecules/navigation/deep-expanding-menu/tests/DeepExpandingMenu.spec.ts +90 -27
- package/app/components/02.molecules/navigation/deep-expanding-menu/tests/__snapshots__/DeepExpandingMenu.spec.ts.snap +8 -8
- package/app/components/02.molecules/navigation/deep-expanding-menu-classic/CONSUMER-STYLING.md +58 -26
- package/app/components/02.molecules/navigation/deep-expanding-menu-classic/DeepExpandingMenuClassic.vue +113 -57
- package/app/components/02.molecules/navigation/deep-expanding-menu-classic/stories/DeepExpandingMenuClassic.stories.ts +139 -19
- package/app/components/02.molecules/navigation/deep-expanding-menu-classic/tests/DeepExpandingMenuClassic.spec.ts +140 -22
- package/app/components/02.molecules/navigation/deep-expanding-menu-classic/tests/__snapshots__/DeepExpandingMenuClassic.spec.ts.snap +9 -9
- package/app/components/02.molecules/qr-code/CONSUMER-STYLING.md +22 -7
- package/app/components/02.molecules/qr-code/CaptureQrCode.vue +63 -71
- package/app/components/02.molecules/qr-code/DecodeQrCode.vue +47 -10
- package/app/components/02.molecules/qr-code/stories/CaptureQrCode.stories.ts +114 -26
- package/app/components/02.molecules/qr-code/stories/DecodeQrCode.stories.ts +115 -24
- package/app/components/02.molecules/qr-code/tests/CaptureQrCode.spec.ts +124 -51
- package/app/components/02.molecules/qr-code/tests/DecodeQrCode.spec.ts +74 -25
- package/app/components/02.molecules/samaritan-prompt/CONSUMER-STYLING.md +22 -10
- package/app/components/02.molecules/samaritan-prompt/SamaritanPrompt.vue +30 -12
- package/app/components/02.molecules/samaritan-prompt/SamaritanPromptMixed.vue +34 -10
- package/app/components/02.molecules/samaritan-prompt/stories/SamaritanPrompt.stories.ts +55 -4
- package/app/components/02.molecules/samaritan-prompt/stories/SamaritanPromptMixed.stories.ts +67 -7
- package/app/components/02.molecules/samaritan-prompt/tests/SamaritanPrompt.spec.ts +113 -29
- package/app/components/02.molecules/samaritan-prompt/tests/SamaritanPromptMixed.spec.ts +92 -45
- package/app/components/03.organisms/image-galleries/carousel-flip/CONSUMER-STYLING.md +82 -29
- package/app/components/03.organisms/image-galleries/carousel-flip/CarouselFlip.vue +176 -113
- package/app/components/03.organisms/image-galleries/carousel-flip/stories/CarouselFlip.stories.ts +153 -23
- package/app/components/03.organisms/image-galleries/carousel-flip/tests/CarouselFlip.spec.ts +119 -63
- package/app/components/03.organisms/responsive-header/tests/__snapshots__/NavigationItems.spec.ts.snap +2 -2
- package/app/components/03.organisms/responsive-header/tests/__snapshots__/ResponsiveHeader.spec.ts.snap +2 -2
- package/app/components/03.organisms/services/service-detail/tests/ServiceDetail.spec.ts +8 -8
- package/app/components/03.organisms/services/service-detail/tests/__snapshots__/ServiceDetail.spec.ts.snap +4 -4
- package/app/components/03.organisms/site-header/CONSUMER-STYLING.md +41 -23
- package/app/components/03.organisms/site-header/SiteHeader.vue +16 -21
- package/app/components/03.organisms/site-header/stories/SiteHeader.stories.ts +138 -0
- package/app/components/03.organisms/site-header/tests/SiteHeader.spec.ts +53 -26
- package/app/components/03.organisms/site-header/tests/__snapshots__/SiteHeader.spec.ts.snap +2 -2
- package/app/types/components/qr-code.d.ts +12 -0
- package/app/utils/graphemes.ts +8 -0
- package/package.json +1 -1
- package/.claude/skills/components/accordian-core.md +0 -180
- package/.vscode/srcdev-component-accordian-core.code-snippets +0 -74
- package/app/components/01.atoms/animations/entry/.nuxt/app.config.mjs +0 -21
- package/app/components/01.atoms/animations/entry/.nuxt/components.d.ts +0 -66
- package/app/components/01.atoms/animations/entry/.nuxt/imports.d.ts +0 -30
- package/app/components/01.atoms/animations/entry/.nuxt/manifest/meta/test.json +0 -1
- package/app/components/01.atoms/animations/entry/.nuxt/types/app.config.d.ts +0 -35
- package/app/components/01.atoms/animations/entry/.nuxt/types/build.d.ts +0 -23
- package/app/components/01.atoms/animations/entry/.nuxt/types/builder-env.d.ts +0 -1
- package/app/components/01.atoms/animations/entry/.nuxt/types/components.d.ts +0 -71
- package/app/components/01.atoms/animations/entry/.nuxt/types/imports.d.ts +0 -386
- package/app/components/01.atoms/animations/entry/.nuxt/types/middleware.d.ts +0 -11
- package/app/components/01.atoms/animations/entry/.nuxt/types/modules.d.ts +0 -79
- package/app/components/01.atoms/animations/entry/.nuxt/types/nitro-layouts.d.ts +0 -17
- package/app/components/01.atoms/animations/entry/.nuxt/types/nitro-nuxt.d.ts +0 -64
- package/app/components/01.atoms/animations/entry/.nuxt/types/plugins.d.ts +0 -29
- package/app/components/01.atoms/animations/entry/.nuxt/types/runtime-config.d.ts +0 -32
- package/app/components/01.atoms/animations/entry/.nuxt/types/shared-imports.d.ts +0 -10
- package/app/components/01.atoms/animations/entry/.nuxt/types/vue-shim.d.ts +0 -0
- package/app/components/01.atoms/animations/entry/.nuxtrc +0 -1
- package/app/components/01.atoms/animations/entry/node_modules/.cache/nuxt/chrome-workspace.json +0 -1
- package/app/components/01.atoms/animations/entry/node_modules/.cache/vite/client/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +0 -1
- package/app/components/02.molecules/expandable/accordian/CONSUMER-STYLING.md +0 -100
- package/app/components/02.molecules/expandable/accordian/stories/AccordianCore.stories.ts +0 -272
- package/app/components/02.molecules/expandable/accordian/tests/AccordianCore.spec.ts +0 -257
- package/app/components/02.molecules/expandable/accordian/tests/__snapshots__/AccordianCore.spec.ts.snap +0 -95
- package/app/components/03.organisms/responsive-header/tests/.nuxt/app.config.mjs +0 -21
- package/app/components/03.organisms/responsive-header/tests/.nuxt/components.d.ts +0 -66
- package/app/components/03.organisms/responsive-header/tests/.nuxt/imports.d.ts +0 -30
- package/app/components/03.organisms/responsive-header/tests/.nuxt/manifest/meta/test.json +0 -1
- package/app/components/03.organisms/responsive-header/tests/.nuxt/types/app.config.d.ts +0 -35
- package/app/components/03.organisms/responsive-header/tests/.nuxt/types/build.d.ts +0 -23
- package/app/components/03.organisms/responsive-header/tests/.nuxt/types/builder-env.d.ts +0 -1
- package/app/components/03.organisms/responsive-header/tests/.nuxt/types/components.d.ts +0 -71
- package/app/components/03.organisms/responsive-header/tests/.nuxt/types/imports.d.ts +0 -386
- package/app/components/03.organisms/responsive-header/tests/.nuxt/types/middleware.d.ts +0 -11
- package/app/components/03.organisms/responsive-header/tests/.nuxt/types/modules.d.ts +0 -79
- package/app/components/03.organisms/responsive-header/tests/.nuxt/types/nitro-layouts.d.ts +0 -17
- package/app/components/03.organisms/responsive-header/tests/.nuxt/types/nitro-nuxt.d.ts +0 -64
- package/app/components/03.organisms/responsive-header/tests/.nuxt/types/plugins.d.ts +0 -29
- package/app/components/03.organisms/responsive-header/tests/.nuxt/types/runtime-config.d.ts +0 -32
- package/app/components/03.organisms/responsive-header/tests/.nuxt/types/shared-imports.d.ts +0 -10
- package/app/components/03.organisms/responsive-header/tests/.nuxt/types/vue-shim.d.ts +0 -0
- package/app/components/03.organisms/responsive-header/tests/node_modules/.cache/nuxt/chrome-workspace.json +0 -1
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import { computed, ref } from "vue";
|
|
1
2
|
import DeepExpandingMenu from "../DeepExpandingMenu.vue";
|
|
3
|
+
import CanvasSwitcher from "../../../../01.atoms/canvas-switcher/CanvasSwitcher.vue";
|
|
2
4
|
import type { Meta, StoryObj } from "@nuxtjs/storybook";
|
|
3
|
-
import type { ResponsiveHeaderNavItem } from "~/types/components";
|
|
5
|
+
import type { MediaCanvas, ResponsiveHeaderNavItem } from "~/types/components";
|
|
4
6
|
|
|
5
7
|
const navLinks: ResponsiveHeaderNavItem[] = [
|
|
6
8
|
{ name: "Home", path: "/" },
|
|
@@ -16,63 +18,181 @@ const navLinks: ResponsiveHeaderNavItem[] = [
|
|
|
16
18
|
{ name: "Contact", path: "/contact" },
|
|
17
19
|
];
|
|
18
20
|
|
|
19
|
-
const
|
|
21
|
+
const stressNavLinks: ResponsiveHeaderNavItem[] = [
|
|
22
|
+
{ name: "H", path: "/" },
|
|
23
|
+
{
|
|
24
|
+
name: "Dienstleistungsangebotsübersichtsseite",
|
|
25
|
+
childLinksTitle:
|
|
26
|
+
"Unsere vollständige Übersicht aller Dienstleistungen, Behandlungen und Pflegeangebote für Haut und Haar in Northtown",
|
|
27
|
+
childLinks: [
|
|
28
|
+
{ name: "Haarschnitt", path: "/services/haircuts" },
|
|
29
|
+
{ name: "https://example.com/a/very/long/unbroken/path/that/never/wraps/on/its/own", path: "/long-url" },
|
|
30
|
+
{ name: "🎨 Colouring", path: "/services/colouring" },
|
|
31
|
+
{ name: "Styling", path: "/services/styling-1" },
|
|
32
|
+
{ name: "Styling", path: "/services/styling-2" },
|
|
33
|
+
{ name: "<b>Not bold</b>", path: "/services/html" },
|
|
34
|
+
{ name: "قص الشعر", path: "/services/rtl" },
|
|
35
|
+
...Array.from({ length: 24 }, (_, i) => ({ name: `Treatment ${i + 1}`, path: `/services/treatment-${i + 1}` })),
|
|
36
|
+
],
|
|
37
|
+
},
|
|
38
|
+
{ name: "Gruppe ohne Titel", childLinks: [{ name: "Only link", path: "/only" }] },
|
|
39
|
+
{ name: "Empty group", childLinksTitle: "Nothing here yet", childLinks: [] },
|
|
40
|
+
{ name: "Supercalifragilisticexpialidociousandthensomemoretext", path: "/unbroken" },
|
|
41
|
+
{ name: "🙂", path: "/emoji" },
|
|
42
|
+
{ name: "Contact", path: "/contact" },
|
|
43
|
+
{ name: "Contact", path: "/contact" },
|
|
44
|
+
{
|
|
45
|
+
name: "About us and our story",
|
|
46
|
+
childLinksTitle: "About Alex Morgan",
|
|
47
|
+
childLinks: [
|
|
48
|
+
{ name: "Team", path: "/about/team" },
|
|
49
|
+
{ name: "History", path: "/about/history" },
|
|
50
|
+
],
|
|
51
|
+
},
|
|
52
|
+
];
|
|
53
|
+
|
|
54
|
+
type LineClamp = "none" | "1" | "2" | "3";
|
|
55
|
+
|
|
56
|
+
// Story-only arg: drives a CSS token, not a DeepExpandingMenu prop. See the argTypes entry.
|
|
57
|
+
type StoryArgs = InstanceType<typeof DeepExpandingMenu>["$props"] & {
|
|
58
|
+
headingLineClamp: LineClamp;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const meta: Meta<StoryArgs> = {
|
|
20
62
|
title: "Molecules/DeepExpandingMenu",
|
|
21
63
|
component: DeepExpandingMenu,
|
|
22
64
|
argTypes: {
|
|
65
|
+
headingLineClamp: {
|
|
66
|
+
control: "select",
|
|
67
|
+
options: ["none", "1", "2", "3"] satisfies LineClamp[],
|
|
68
|
+
description:
|
|
69
|
+
"**Story control, not a prop.** Sets the `--deep-expanding-menu-panel-heading-line-clamp` CSS token on a wrapper so you can try it here. " +
|
|
70
|
+
"To use it in an app, set the token in your own CSS, e.g. `.site-nav { --deep-expanding-menu-panel-heading-line-clamp: 1; }`. " +
|
|
71
|
+
"`1` is single-line ellipsis, `none` (the default) shows everything.",
|
|
72
|
+
table: { category: "CSS tokens (story only, set in your CSS)", defaultValue: { summary: "none" } },
|
|
73
|
+
},
|
|
23
74
|
tag: {
|
|
24
75
|
control: { type: "select" },
|
|
25
|
-
options: ["div", "section", "nav"
|
|
76
|
+
options: ["div", "section", "nav"],
|
|
26
77
|
description: "Root element tag",
|
|
78
|
+
table: { category: "Basic", defaultValue: { summary: "nav" } },
|
|
79
|
+
},
|
|
80
|
+
ariaLabel: {
|
|
81
|
+
control: "text",
|
|
82
|
+
description: "Accessible name for the root. Set it when the page has more than one `<nav>`",
|
|
27
83
|
table: { category: "Basic" },
|
|
28
84
|
},
|
|
29
|
-
navLinks: {
|
|
85
|
+
navLinks: {
|
|
86
|
+
control: "object",
|
|
87
|
+
description: "Top-level items: `path` renders a link, `childLinks` renders a toggle and panel",
|
|
88
|
+
table: { category: "Content" },
|
|
89
|
+
},
|
|
30
90
|
styleClassPassthrough: { table: { disable: true } },
|
|
31
91
|
},
|
|
32
92
|
args: {
|
|
33
93
|
tag: "nav",
|
|
34
94
|
navLinks,
|
|
95
|
+
headingLineClamp: "none",
|
|
35
96
|
},
|
|
97
|
+
decorators: [
|
|
98
|
+
(story, context) => ({
|
|
99
|
+
components: { story, CanvasSwitcher },
|
|
100
|
+
setup() {
|
|
101
|
+
const canvasName = ref<MediaCanvas>(context.parameters.initialCanvas ?? "fullWidthCanvas");
|
|
102
|
+
return { canvasName };
|
|
103
|
+
},
|
|
104
|
+
template: `
|
|
105
|
+
<div style="padding: 1.2rem 1.6rem; border-block-end: 1px solid currentColor;">
|
|
106
|
+
<CanvasSwitcher v-model:canvas-name="canvasName" />
|
|
107
|
+
</div>
|
|
108
|
+
<div :class="canvasName" style="margin-inline: auto; padding: 2rem; outline: 1px dashed currentColor;">
|
|
109
|
+
<story />
|
|
110
|
+
</div>
|
|
111
|
+
`,
|
|
112
|
+
}),
|
|
113
|
+
],
|
|
36
114
|
parameters: {
|
|
115
|
+
layout: "fullscreen",
|
|
37
116
|
docs: {
|
|
38
117
|
description: {
|
|
39
118
|
component:
|
|
40
|
-
"
|
|
119
|
+
"Top-level nav row where each item is a direct link (`path`) or a toggle that opens a panel of child links (`childLinks`, with an optional `childLinksTitle` heading). " +
|
|
120
|
+
"Built on CSS anchor positioning and the Popover API, with a JS fallback for Safari 16-18. Props: `navLinks`, `tag`, `ariaLabel`. " +
|
|
121
|
+
"See `DeepExpandingMenuClassic` for the `<details>`-based alternative.",
|
|
41
122
|
},
|
|
42
123
|
},
|
|
43
124
|
},
|
|
44
125
|
};
|
|
45
126
|
|
|
46
127
|
export default meta;
|
|
47
|
-
type Story = StoryObj<
|
|
128
|
+
type Story = StoryObj<StoryArgs>;
|
|
129
|
+
|
|
130
|
+
function useStorySetup(args: StoryArgs) {
|
|
131
|
+
const componentArgs = computed(() => {
|
|
132
|
+
const { headingLineClamp: _headingLineClamp, ...rest } = args;
|
|
133
|
+
return rest;
|
|
134
|
+
});
|
|
135
|
+
const tokenStyles = computed(() => ({
|
|
136
|
+
"--deep-expanding-menu-panel-heading-line-clamp": args.headingLineClamp,
|
|
137
|
+
}));
|
|
138
|
+
return { componentArgs, tokenStyles };
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const render = (args: StoryArgs) => ({
|
|
142
|
+
components: { DeepExpandingMenu },
|
|
143
|
+
setup() {
|
|
144
|
+
return useStorySetup(args);
|
|
145
|
+
},
|
|
146
|
+
template: `<div :style="tokenStyles"><DeepExpandingMenu v-bind="componentArgs" /></div>`,
|
|
147
|
+
});
|
|
48
148
|
|
|
49
149
|
export const Default: Story = {
|
|
50
|
-
render
|
|
51
|
-
components: { DeepExpandingMenu },
|
|
52
|
-
setup() {
|
|
53
|
-
return { args };
|
|
54
|
-
},
|
|
55
|
-
template: `<DeepExpandingMenu v-bind="args" style="padding: 4rem;" />`,
|
|
56
|
-
}),
|
|
150
|
+
render,
|
|
57
151
|
};
|
|
58
152
|
|
|
59
153
|
export const CustomColours: Story = {
|
|
154
|
+
name: "Custom Colours",
|
|
60
155
|
render: (args) => ({
|
|
61
156
|
components: { DeepExpandingMenu },
|
|
62
157
|
setup() {
|
|
63
|
-
return
|
|
158
|
+
return useStorySetup(args);
|
|
64
159
|
},
|
|
65
160
|
template: `
|
|
66
|
-
<
|
|
67
|
-
|
|
68
|
-
style="
|
|
69
|
-
|
|
161
|
+
<div
|
|
162
|
+
:style="tokenStyles"
|
|
163
|
+
style="--deep-expanding-menu-panel-background-colour: #1a1a1a; --deep-expanding-menu-panel-border-colour: transparent; --deep-expanding-menu-group-link-colour: white; --deep-expanding-menu-group-link-border-colour-hover: white; --deep-expanding-menu-panel-heading-colour: #aaaaaa;"
|
|
164
|
+
>
|
|
165
|
+
<DeepExpandingMenu v-bind="componentArgs" />
|
|
166
|
+
</div>
|
|
70
167
|
`,
|
|
71
168
|
}),
|
|
72
169
|
parameters: {
|
|
73
170
|
docs: {
|
|
74
171
|
description: {
|
|
75
|
-
story: "Overriding the public
|
|
172
|
+
story: "Overriding the public `--deep-expanding-menu-*` tokens on a wrapper element.",
|
|
173
|
+
},
|
|
174
|
+
},
|
|
175
|
+
},
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
export const StressTest: Story = {
|
|
179
|
+
name: "Stress Test (Worst-Case Data)",
|
|
180
|
+
args: {
|
|
181
|
+
navLinks: stressNavLinks,
|
|
182
|
+
},
|
|
183
|
+
render,
|
|
184
|
+
parameters: {
|
|
185
|
+
initialCanvas: "mobileCanvas",
|
|
186
|
+
docs: {
|
|
187
|
+
story: { inline: false, height: "720px" },
|
|
188
|
+
description: {
|
|
189
|
+
story:
|
|
190
|
+
"Hostile nav data at every `CanvasSwitcher` width. Check that: the top-level row wraps instead of overflowing; " +
|
|
191
|
+
"the long German label and the unbroken word break inside the canvas; the caret stays beside a wrapped toggle label; " +
|
|
192
|
+
"the first panel's long heading wraps (try the story-only `headingLineClamp` CSS token control), its unbroken URL link stays inside the panel, " +
|
|
193
|
+
"`<b>Not bold</b>` renders as text, the right-to-left link reads correctly, and its 31 links scroll inside the panel; " +
|
|
194
|
+
"duplicate `Styling` and `Contact` items both render; a group without a title shows its list only; " +
|
|
195
|
+
"an empty group opens with its heading and no list; the last group's panel flips to stay on screen near the right edge.",
|
|
76
196
|
},
|
|
77
197
|
},
|
|
78
198
|
},
|
package/app/components/02.molecules/navigation/deep-expanding-menu/tests/DeepExpandingMenu.spec.ts
CHANGED
|
@@ -17,49 +17,62 @@ const defaultNavLinks: ResponsiveHeaderNavItem[] = [
|
|
|
17
17
|
{ name: "Contact", path: "/contact" },
|
|
18
18
|
];
|
|
19
19
|
|
|
20
|
+
const mounted: Awaited<ReturnType<typeof mountSuspended>>[] = [];
|
|
21
|
+
|
|
22
|
+
const mountMenu = async (options: { props?: Record<string, unknown>; attachTo?: HTMLElement } = {}) => {
|
|
23
|
+
const wrapper = await mountSuspended(DeepExpandingMenu, options);
|
|
24
|
+
mounted.push(wrapper);
|
|
25
|
+
return wrapper;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
afterEach(() => {
|
|
29
|
+
mounted.splice(0).forEach((wrapper) => wrapper.unmount());
|
|
30
|
+
vi.restoreAllMocks();
|
|
31
|
+
});
|
|
32
|
+
|
|
20
33
|
describe("DeepExpandingMenu", () => {
|
|
21
34
|
it("mounts without error", async () => {
|
|
22
|
-
const wrapper = await
|
|
35
|
+
const wrapper = await mountMenu({ props: { navLinks: defaultNavLinks } });
|
|
23
36
|
expect(wrapper.vm).toBeTruthy();
|
|
24
37
|
});
|
|
25
38
|
|
|
26
39
|
it("renders correct HTML structure", async () => {
|
|
27
|
-
const wrapper = await
|
|
40
|
+
const wrapper = await mountMenu({ props: { navLinks: defaultNavLinks } });
|
|
28
41
|
expect(wrapper.html()).toMatchSnapshot();
|
|
29
42
|
});
|
|
30
43
|
|
|
31
44
|
it("renders a <nav> by default", async () => {
|
|
32
|
-
const wrapper = await
|
|
45
|
+
const wrapper = await mountMenu({ props: { navLinks: defaultNavLinks } });
|
|
33
46
|
expect(wrapper.element.tagName.toLowerCase()).toBe("nav");
|
|
34
47
|
});
|
|
35
48
|
|
|
36
49
|
it("renders the given tag", async () => {
|
|
37
|
-
const wrapper = await
|
|
50
|
+
const wrapper = await mountMenu({
|
|
38
51
|
props: { navLinks: defaultNavLinks, tag: "div" },
|
|
39
52
|
});
|
|
40
53
|
expect(wrapper.element.tagName.toLowerCase()).toBe("div");
|
|
41
54
|
});
|
|
42
55
|
|
|
43
56
|
it("renders a direct link for items with a path", async () => {
|
|
44
|
-
const wrapper = await
|
|
45
|
-
const links = wrapper.findAll(".
|
|
57
|
+
const wrapper = await mountMenu({ props: { navLinks: defaultNavLinks } });
|
|
58
|
+
const links = wrapper.findAll(".deep-expanding-menu-link");
|
|
46
59
|
expect(links.map((l) => l.text())).toEqual(["Home", "Contact"]);
|
|
47
60
|
});
|
|
48
61
|
|
|
49
62
|
it("renders a toggle group for items with childLinks", async () => {
|
|
50
|
-
const wrapper = await
|
|
51
|
-
expect(wrapper.find(".
|
|
63
|
+
const wrapper = await mountMenu({ props: { navLinks: defaultNavLinks } });
|
|
64
|
+
expect(wrapper.find(".deep-expanding-menu-group-toggle").text()).toContain("Services");
|
|
52
65
|
});
|
|
53
66
|
|
|
54
67
|
it("renders each child link inside the group panel", async () => {
|
|
55
|
-
const wrapper = await
|
|
56
|
-
const childLinks = wrapper.findAll(".
|
|
68
|
+
const wrapper = await mountMenu({ props: { navLinks: defaultNavLinks } });
|
|
69
|
+
const childLinks = wrapper.findAll(".deep-expanding-menu-group-link");
|
|
57
70
|
expect(childLinks.map((l) => l.text())).toEqual(["Haircuts", "Colouring"]);
|
|
58
71
|
});
|
|
59
72
|
|
|
60
73
|
it("renders the childLinksTitle heading", async () => {
|
|
61
|
-
const wrapper = await
|
|
62
|
-
expect(wrapper.find(".
|
|
74
|
+
const wrapper = await mountMenu({ props: { navLinks: defaultNavLinks } });
|
|
75
|
+
expect(wrapper.find(".deep-expanding-menu-group-heading").text()).toBe("Our services");
|
|
63
76
|
});
|
|
64
77
|
|
|
65
78
|
it("gives each group toggle a unique popovertarget", async () => {
|
|
@@ -67,21 +80,21 @@ describe("DeepExpandingMenu", () => {
|
|
|
67
80
|
{ name: "A", childLinksTitle: "A", childLinks: [{ name: "A1", path: "/a1" }] },
|
|
68
81
|
{ name: "B", childLinksTitle: "B", childLinks: [{ name: "B1", path: "/b1" }] },
|
|
69
82
|
];
|
|
70
|
-
const wrapper = await
|
|
71
|
-
const toggles = wrapper.findAll(".
|
|
83
|
+
const wrapper = await mountMenu({ props: { navLinks } });
|
|
84
|
+
const toggles = wrapper.findAll(".deep-expanding-menu-group-toggle");
|
|
72
85
|
const targets = toggles.map((t) => t.attributes("popovertarget"));
|
|
73
86
|
expect(new Set(targets).size).toBe(2);
|
|
74
87
|
});
|
|
75
88
|
|
|
76
89
|
it("applies styleClassPassthrough classes", async () => {
|
|
77
|
-
const wrapper = await
|
|
90
|
+
const wrapper = await mountMenu({
|
|
78
91
|
props: { navLinks: defaultNavLinks, styleClassPassthrough: ["custom-class"] },
|
|
79
92
|
});
|
|
80
93
|
expect(wrapper.classes()).toContain("custom-class");
|
|
81
94
|
});
|
|
82
95
|
|
|
83
96
|
it("resets classes when styleClassPassthrough prop changes", async () => {
|
|
84
|
-
const wrapper = await
|
|
97
|
+
const wrapper = await mountMenu({
|
|
85
98
|
props: { navLinks: defaultNavLinks, styleClassPassthrough: ["initial-class"] },
|
|
86
99
|
});
|
|
87
100
|
expect(wrapper.classes()).toContain("initial-class");
|
|
@@ -91,9 +104,60 @@ describe("DeepExpandingMenu", () => {
|
|
|
91
104
|
expect(wrapper.classes()).toContain("updated-class");
|
|
92
105
|
});
|
|
93
106
|
|
|
107
|
+
it("labels the root when ariaLabel is set, and leaves it unlabelled otherwise", async () => {
|
|
108
|
+
const unlabelled = await mountMenu({ props: { navLinks: defaultNavLinks } });
|
|
109
|
+
expect(unlabelled.attributes("aria-label")).toBeUndefined();
|
|
110
|
+
|
|
111
|
+
const labelled = await mountMenu({
|
|
112
|
+
props: { navLinks: defaultNavLinks, ariaLabel: "Main" },
|
|
113
|
+
});
|
|
114
|
+
expect(labelled.attributes("aria-label")).toBe("Main");
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it("gives the child link list a list role", async () => {
|
|
118
|
+
const wrapper = await mountMenu({ props: { navLinks: defaultNavLinks } });
|
|
119
|
+
expect(wrapper.find(".deep-expanding-menu-group-list").attributes("role")).toBe("list");
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it("omits the heading when childLinksTitle is missing or empty", async () => {
|
|
123
|
+
const navLinks: ResponsiveHeaderNavItem[] = [
|
|
124
|
+
{ name: "A", childLinks: [{ name: "A1", path: "/a1" }] },
|
|
125
|
+
{ name: "B", childLinksTitle: "", childLinks: [{ name: "B1", path: "/b1" }] },
|
|
126
|
+
];
|
|
127
|
+
const wrapper = await mountMenu({ props: { navLinks } });
|
|
128
|
+
expect(wrapper.find(".deep-expanding-menu-group-heading").exists()).toBe(false);
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it("omits the list when a group has no child links", async () => {
|
|
132
|
+
const navLinks: ResponsiveHeaderNavItem[] = [{ name: "Empty", childLinksTitle: "Empty", childLinks: [] }];
|
|
133
|
+
const wrapper = await mountMenu({ props: { navLinks } });
|
|
134
|
+
expect(wrapper.find(".deep-expanding-menu-group-list").exists()).toBe(false);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it("keeps child links with duplicate names in step when the list is reordered", async () => {
|
|
138
|
+
const childLinks = [
|
|
139
|
+
{ name: "Same", path: "/one" },
|
|
140
|
+
{ name: "Same", path: "/two" },
|
|
141
|
+
{ name: "Other", path: "/three" },
|
|
142
|
+
];
|
|
143
|
+
const wrapper = await mountMenu({
|
|
144
|
+
props: { navLinks: [{ name: "Group", childLinksTitle: "Group", childLinks }] },
|
|
145
|
+
});
|
|
146
|
+
await wrapper.setProps({
|
|
147
|
+
navLinks: [{ name: "Group", childLinksTitle: "Group", childLinks: [childLinks[2]!, childLinks[0]!] }],
|
|
148
|
+
});
|
|
149
|
+
const links = wrapper.findAll(".deep-expanding-menu-group-link");
|
|
150
|
+
expect(links.map((l) => l.attributes("href"))).toEqual(["/three", "/one"]);
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it("hides the caret icon from assistive technology", async () => {
|
|
154
|
+
const wrapper = await mountMenu({ props: { navLinks: defaultNavLinks } });
|
|
155
|
+
expect(wrapper.find(".deep-expanding-menu-group-icon").attributes("aria-hidden")).toBe("true");
|
|
156
|
+
});
|
|
157
|
+
|
|
94
158
|
it("doesn't give the link panels a menu role", async () => {
|
|
95
|
-
const wrapper = await
|
|
96
|
-
expect(wrapper.find(".
|
|
159
|
+
const wrapper = await mountMenu({ props: { navLinks: defaultNavLinks } });
|
|
160
|
+
expect(wrapper.find(".deep-expanding-menu-group-panel").attributes("role")).toBeUndefined();
|
|
97
161
|
});
|
|
98
162
|
|
|
99
163
|
describe("open state", () => {
|
|
@@ -116,9 +180,9 @@ describe("DeepExpandingMenu", () => {
|
|
|
116
180
|
});
|
|
117
181
|
|
|
118
182
|
it("tracks the open group from toggle events, whichever order they arrive in", async () => {
|
|
119
|
-
const wrapper = await
|
|
120
|
-
const [first, second] = wrapper.findAll(".
|
|
121
|
-
const toggles = wrapper.findAll(".
|
|
183
|
+
const wrapper = await mountMenu({ props: { navLinks: twoGroups } });
|
|
184
|
+
const [first, second] = wrapper.findAll(".deep-expanding-menu-group-panel");
|
|
185
|
+
const toggles = wrapper.findAll(".deep-expanding-menu-group-toggle");
|
|
122
186
|
|
|
123
187
|
first!.element.dispatchEvent(toggleEvent("open"));
|
|
124
188
|
await nextTick();
|
|
@@ -142,9 +206,9 @@ describe("DeepExpandingMenu", () => {
|
|
|
142
206
|
});
|
|
143
207
|
|
|
144
208
|
it("opens, switches and closes groups from their toggles", async () => {
|
|
145
|
-
const wrapper = await
|
|
146
|
-
const toggles = wrapper.findAll(".
|
|
147
|
-
const panels = wrapper.findAll(".
|
|
209
|
+
const wrapper = await mountMenu({ props: { navLinks: twoGroups } });
|
|
210
|
+
const toggles = wrapper.findAll(".deep-expanding-menu-group-toggle");
|
|
211
|
+
const panels = wrapper.findAll(".deep-expanding-menu-group-panel");
|
|
148
212
|
|
|
149
213
|
await toggles[0]!.trigger("click");
|
|
150
214
|
expect(panels[0]!.classes()).toContain("deep-expanding-menu-panel-open");
|
|
@@ -160,14 +224,13 @@ describe("DeepExpandingMenu", () => {
|
|
|
160
224
|
});
|
|
161
225
|
|
|
162
226
|
it("closes the open group on Escape", async () => {
|
|
163
|
-
const wrapper = await
|
|
164
|
-
await wrapper.findAll(".
|
|
227
|
+
const wrapper = await mountMenu({ props: { navLinks: twoGroups }, attachTo: document.body });
|
|
228
|
+
await wrapper.findAll(".deep-expanding-menu-group-toggle")[0]!.trigger("click");
|
|
165
229
|
await nextTick();
|
|
166
230
|
|
|
167
231
|
document.dispatchEvent(new KeyboardEvent("keydown", { key: "Escape" }));
|
|
168
232
|
await nextTick();
|
|
169
233
|
expect(wrapper.find(".deep-expanding-menu-panel-open").exists()).toBe(false);
|
|
170
|
-
wrapper.unmount();
|
|
171
234
|
});
|
|
172
235
|
});
|
|
173
236
|
});
|
|
@@ -2,16 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
exports[`DeepExpandingMenu > renders correct HTML structure 1`] = `
|
|
4
4
|
"<nav class="deep-expanding-menu">
|
|
5
|
-
<div class="inner"><a href="/" class="
|
|
6
|
-
<div class="
|
|
7
|
-
<div id="popovertarget-v-0-0-1" class="
|
|
8
|
-
<h4 class="page-heading-4 mb-6">Our services</h4>
|
|
9
|
-
<ul class="
|
|
10
|
-
<li class="
|
|
11
|
-
<li class="
|
|
5
|
+
<div class="deep-expanding-menu-inner"><a href="/" class="deep-expanding-menu-link">Home</a>
|
|
6
|
+
<div class="deep-expanding-menu-group" style="--_anchor-name: --anchor-v-0-0-1;"><button type="button" popovertarget="popovertarget-v-0-0-1" class="deep-expanding-menu-group-toggle" aria-expanded="false"><span class="deep-expanding-menu-group-label">Services</span><span class="iconify i-bi:caret-down-fill deep-expanding-menu-group-icon" aria-hidden="true"></span></button>
|
|
7
|
+
<div id="popovertarget-v-0-0-1" class="deep-expanding-menu-group-panel" popover="">
|
|
8
|
+
<h4 class="deep-expanding-menu-group-heading page-heading-4 mb-6">Our services</h4>
|
|
9
|
+
<ul class="deep-expanding-menu-group-list" role="list">
|
|
10
|
+
<li class="deep-expanding-menu-group-item"><a href="/services/haircuts" class="deep-expanding-menu-group-link">Haircuts</a></li>
|
|
11
|
+
<li class="deep-expanding-menu-group-item"><a href="/services/colouring" class="deep-expanding-menu-group-link">Colouring</a></li>
|
|
12
12
|
</ul>
|
|
13
13
|
</div>
|
|
14
|
-
</div><a href="/contact" class="
|
|
14
|
+
</div><a href="/contact" class="deep-expanding-menu-link">Contact</a>
|
|
15
15
|
</div>
|
|
16
16
|
</nav>"
|
|
17
17
|
`;
|
package/app/components/02.molecules/navigation/deep-expanding-menu-classic/CONSUMER-STYLING.md
CHANGED
|
@@ -2,44 +2,72 @@
|
|
|
2
2
|
|
|
3
3
|
## Public token API
|
|
4
4
|
|
|
5
|
+
> Changed 2026-10-21: colour defaults now come from `--theme-*` tokens (were `--blue-10`, `white`,
|
|
6
|
+
> `black`, `--slate-10`), `--deep-expanding-menu-classic-panel-offset-top` defaults to
|
|
7
|
+
> `calc(100% + 1rem)` (was `4rem`, which overlapped a wrapped toggle label), the panel widths switch
|
|
8
|
+
> on the menu's own width instead of the viewport, and `--deep-expanding-menu-classic-gap` was
|
|
9
|
+
> removed (the root grid has one area, so it never did anything).
|
|
10
|
+
|
|
11
|
+
### Top-level row
|
|
12
|
+
|
|
5
13
|
| Token | Default | Controls |
|
|
6
14
|
|---|---|---|
|
|
7
|
-
| `--deep-expanding-menu-classic-gap` | `
|
|
8
|
-
| `--deep-expanding-menu-classic-
|
|
9
|
-
| `--deep-expanding-menu-classic-link-border-width` | `0.2rem` | Bottom border width on links and group toggles |
|
|
15
|
+
| `--deep-expanding-menu-classic-item-gap` | `2.4rem` | Gap between top-level items (row and column, the row wraps) |
|
|
16
|
+
| `--deep-expanding-menu-classic-link-border-width` | `0.2rem` | Bottom border width on links, group toggles and child links |
|
|
10
17
|
| `--deep-expanding-menu-classic-link-padding-block` | `0.8rem` | Vertical padding on links and group toggles |
|
|
11
|
-
| `--deep-expanding-menu-classic-link-border-colour-hover` | `var(--
|
|
18
|
+
| `--deep-expanding-menu-classic-link-border-colour-hover` | `var(--theme-border)` | Bottom border colour on hover/focus |
|
|
19
|
+
| `--deep-expanding-menu-classic-focus-outline-width` | `0.2rem` | Keyboard focus ring width on links and toggles |
|
|
20
|
+
| `--deep-expanding-menu-classic-focus-outline-colour` | `var(--theme-ring, currentcolor)` | Keyboard focus ring colour |
|
|
12
21
|
| `--deep-expanding-menu-classic-icon-size` | `1.2rem` | Caret icon size |
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
|
17
|
-
|
|
22
|
+
|
|
23
|
+
### Panel
|
|
24
|
+
|
|
25
|
+
| Token | Default | Controls |
|
|
26
|
+
|---|---|---|
|
|
27
|
+
| `--deep-expanding-menu-classic-panel-offset-top` | `calc(100% + 1rem)` | Panel offset from the top of its group |
|
|
28
|
+
| `--deep-expanding-menu-classic-panel-width` | `20rem` | Panel width while the menu is under `768px` wide |
|
|
29
|
+
| `--deep-expanding-menu-classic-panel-width-tablet` | `40rem` | Panel width once the menu is `768px` wide |
|
|
30
|
+
| `--deep-expanding-menu-classic-panel-width-desktop` | `60rem` | Panel width once the menu is `1024px` wide |
|
|
31
|
+
| `--deep-expanding-menu-classic-panel-text-colour` | `var(--theme-text)` | Panel text colour |
|
|
32
|
+
| `--deep-expanding-menu-classic-panel-background-colour` | `var(--theme-surface-subtle)` | Panel background |
|
|
18
33
|
| `--deep-expanding-menu-classic-panel-border-width` | `0.1rem` | Panel border width |
|
|
19
|
-
| `--deep-expanding-menu-classic-panel-border-colour` | `
|
|
34
|
+
| `--deep-expanding-menu-classic-panel-border-colour` | `var(--theme-border)` | Panel border colour |
|
|
20
35
|
| `--deep-expanding-menu-classic-panel-border-radius` | `1.2rem` | Panel corner radius |
|
|
21
36
|
| `--deep-expanding-menu-classic-panel-shadow` | `0 0 1rem rgba(0, 0, 0, 0.1)` | Panel box-shadow |
|
|
22
37
|
| `--deep-expanding-menu-classic-panel-padding` | `1.2rem` | Panel padding |
|
|
23
|
-
| `--deep-expanding-menu-classic-panel-heading-colour` | `var(--
|
|
24
|
-
| `--deep-expanding-menu-classic-panel-
|
|
25
|
-
| `--deep-expanding-menu-classic-
|
|
26
|
-
| `--deep-expanding-menu-classic-group-link-
|
|
38
|
+
| `--deep-expanding-menu-classic-panel-heading-colour` | `var(--theme-text)` | `childLinksTitle` heading colour |
|
|
39
|
+
| `--deep-expanding-menu-classic-panel-heading-line-clamp` | `none` | Lines of `childLinksTitle` before an ellipsis. `1` is single-line ellipsis, `none` shows everything |
|
|
40
|
+
| `--deep-expanding-menu-classic-panel-list-gap` | `1.2rem` | Gap between the panel's heading and list, and between child link grid items |
|
|
41
|
+
| `--deep-expanding-menu-classic-group-link-colour` | `var(--theme-text)` | Child link text colour |
|
|
42
|
+
| `--deep-expanding-menu-classic-group-link-border-colour-hover` | `var(--theme-text)` | Child link bottom border colour on hover/focus |
|
|
43
|
+
|
|
44
|
+
Each panel width is capped at the menu's own width (`min(<token>, 100cqi)`). Private tokens (not
|
|
45
|
+
public API): `--_icon-transform` (caret flip).
|
|
46
|
+
|
|
47
|
+
## State hooks
|
|
27
48
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
--deep-expanding-menu-classic-panel-border-colour: transparent;
|
|
32
|
-
--deep-expanding-menu-classic-group-link-colour: white;
|
|
33
|
-
}
|
|
34
|
-
```
|
|
49
|
+
| Hook | Meaning |
|
|
50
|
+
|---|---|
|
|
51
|
+
| `.deep-expanding-menu-classic-group[open]` | Group open (flips the caret) |
|
|
35
52
|
|
|
36
|
-
|
|
53
|
+
Inner elements: `.deep-expanding-menu-classic-inner` (the flex row),
|
|
54
|
+
`.deep-expanding-menu-classic-link` (direct link), `.deep-expanding-menu-classic-group` (the
|
|
55
|
+
`<details>`), `.deep-expanding-menu-classic-group-toggle` (the `<summary>`),
|
|
56
|
+
`.deep-expanding-menu-classic-group-label`, `.deep-expanding-menu-classic-group-icon`,
|
|
57
|
+
`.deep-expanding-menu-classic-group-panel`, `.deep-expanding-menu-classic-group-heading`,
|
|
58
|
+
`.deep-expanding-menu-classic-group-list`, `.deep-expanding-menu-classic-group-item`,
|
|
59
|
+
`.deep-expanding-menu-classic-group-link`.
|
|
37
60
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
61
|
+
> Changed 2026-10-21: these were `.inner`, `.navigation-link`, `.navigation-group`,
|
|
62
|
+
> `.navigation-group-toggle`, `.icon`, `.navigation-group-panel`, `.navigation-group-list`,
|
|
63
|
+
> `.navigation-group-item` and `.navigation-group-link`. The heading had no class of its own.
|
|
41
64
|
|
|
42
|
-
|
|
65
|
+
## Sizing model
|
|
66
|
+
|
|
67
|
+
The root is an inline-size container named `deep-expanding-menu-classic`, so it can't size itself
|
|
68
|
+
from its content: give it a width from its parent (it fills a block or grid cell by default). A
|
|
69
|
+
panel opens from its toggle's left edge, so a group near the menu's right edge can still overrun it
|
|
70
|
+
by up to the panel's width; `DeepExpandingMenu` flips its panel instead.
|
|
43
71
|
|
|
44
72
|
## Local overrides
|
|
45
73
|
|
|
@@ -49,3 +77,7 @@ Set the tokens above on an element you own (a page or section class, or a class
|
|
|
49
77
|
`<style scoped>`, tokens set on your element still work, but selectors that reach inside the component need
|
|
50
78
|
`:deep()`. Patterns and examples: `.claude/skills/component-local-style-override.md`.
|
|
51
79
|
|
|
80
|
+
## Class passthrough
|
|
81
|
+
|
|
82
|
+
`style-class-passthrough` adds classes to the root element (the `<nav>` by default). It's the
|
|
83
|
+
usual place to scope token overrides to one instance.
|