From 5dd9c5d3813f68c6fdbcbca9dc8a10d6702bcb14 Mon Sep 17 00:00:00 2001 From: Sal Date: Thu, 12 Feb 2026 14:27:43 -0500 Subject: [PATCH 1/7] update core component to be accessible --- .../navigation/navigation.a11y.test.ts | 56 +++++-- .../lib/components/navigation/navigation.less | 20 ++- .../navigation/navigation.visual.test.ts | 65 ++++++--- .../product/components/navigation.html | 137 ++++++++++-------- 4 files changed, 178 insertions(+), 100 deletions(-) diff --git a/packages/stacks-classic/lib/components/navigation/navigation.a11y.test.ts b/packages/stacks-classic/lib/components/navigation/navigation.a11y.test.ts index 23af1a8741..eb92922df2 100644 --- a/packages/stacks-classic/lib/components/navigation/navigation.a11y.test.ts +++ b/packages/stacks-classic/lib/components/navigation/navigation.a11y.test.ts @@ -2,7 +2,14 @@ import { html } from "@open-wc/testing"; import { runA11yTests } from "../../test/a11y-test-utils"; import "../../index"; -const items = [ +interface NavigationItem { + label: string; + title?: boolean; + selected?: boolean; + dropdown?: boolean; +} + +const items: NavigationItem[] = [ { label: "Group 1", title: true, @@ -38,20 +45,43 @@ const items = [ }, ]; -const getChildren = (includeTitles = false): string => - items - .map((item) => { +const getChildren = (includeTitles = false): string => { + const getClasses = function (item: NavigationItem) { + return `s-navigation--item${ + item.selected ? " is-selected" : "" + }${item.dropdown ? " s-navigation--item__dropdown" : ""}`; + }; + + if (!includeTitles) { + return items + .map((item) => { + if (item.title) { + return ""; //don't print title + } + return `
  • ${item.label}
  • `; + }) + .join(""); + } else { + //Vertical nav + let html = ""; + for (const item of items) { if (item.title) { - return includeTitles - ? `
  • ${item.label}
  • ` - : ""; + if (html.length > 0) { + html += ""; + } + const groupName = item.label.replace(" ", ""); + html += `
  • + +
  • "; + return html; + } +}; describe("navigation", () => { runA11yTests({ diff --git a/packages/stacks-classic/lib/components/navigation/navigation.less b/packages/stacks-classic/lib/components/navigation/navigation.less index 24e07ad0d2..2bfcb816c8 100644 --- a/packages/stacks-classic/lib/components/navigation/navigation.less +++ b/packages/stacks-classic/lib/components/navigation/navigation.less @@ -1,4 +1,5 @@ -.s-navigation { +.s-navigation, +.s-navigation ul { --_na-fd: row; --_na-fw: wrap; --_na-p: var(--su2) 0; @@ -15,7 +16,6 @@ --_na-item-selected-bg-hover: var(--_na-item-bg-hover); --_na-item-selected-h: var(--su2); --_na-item-text-ta: center; - --_na-title-mt: var(--su24); --_na-after-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath d='M11.35 4.35 6 9.71.65 4.35l.7-.7L6 8.29l4.65-4.64z'/%3E%3C/svg%3E"); --_na-after-bg-color: var(--black-400); @@ -42,7 +42,8 @@ --_na-item-p: var(--su6) var(--su4); } - &&__vertical { + &&__vertical, + &&__vertical ul { --_na-fd: column; --_na-gap: 0; --_na-p: 0; @@ -51,6 +52,7 @@ --_na-item-selected-h: 0; --_na-item-p: var(--su6) var(--su8); --_na-item-fc: var(--black-600); + & .s-navigation--item { &.is-selected { --_na-item-bg: var(--black-150); @@ -151,20 +153,22 @@ } & &--title { - &:first-child { - --_na-title-mt: 0; - } - & .s-btn { color: var(--black-400); } - margin-top: var(--_na-title-mt); + margin-bottom: 0; font-size: var(--fs-fine); + font-weight: normal; color: var(--black-400); padding: calc(var(--su16) + var(--su2)) var(--su8); } + //Add top margin to titles except the first one + & > li ~ li .s-navigation--title { + margin-top: var(--su24); + } + & &--icon { color: inherit; margin-right: var(--su4); diff --git a/packages/stacks-classic/lib/components/navigation/navigation.visual.test.ts b/packages/stacks-classic/lib/components/navigation/navigation.visual.test.ts index 38285937e3..3d0520e54c 100644 --- a/packages/stacks-classic/lib/components/navigation/navigation.visual.test.ts +++ b/packages/stacks-classic/lib/components/navigation/navigation.visual.test.ts @@ -9,7 +9,14 @@ const filledIcon = IconHomeFill.replace( 'class="s-navigation--icon ' ); -const items = [ +interface NavigationItem { + label: string; + title?: boolean; + selected?: boolean; + dropdown?: boolean; +} + +const items: NavigationItem[] = [ { label: "Group 1", title: true, @@ -45,25 +52,47 @@ const items = [ }, ]; -const getChildren = (includeTitles = false, includeIcons = false): string => - items - .map((item) => { +const getChildren = (includeTitles = false, includeIcons = false): string => { + const getClasses = function (item: NavigationItem) { + return `s-navigation--item${ + item.selected ? " is-selected" : "" + }${item.dropdown ? " s-navigation--item__dropdown" : ""}`; + }; + + const getIcon = function (item: NavigationItem) { + return includeIcons ? (item.selected ? filledIcon : outlineIcon) : ""; + }; + + if (!includeTitles) { + return items + .map((item) => { + if (item.title) { + return ""; //don't print title + } + return `
  • ${getIcon(item)}${item.label}
  • `; + }) + .join(""); + } else { + //Vertical nav + let html = ""; + for (const item of items) { if (item.title) { - return includeTitles - ? `
  • ${item.label}
  • ` - : ""; + if (html.length > 0) { + html += ""; + } + const groupName = item.label.replace(" ", ""); + html += `
  • + +
  • "; + return html; + } +}; describe("navigation", () => { runVisualTests({ diff --git a/packages/stacks-docs/product/components/navigation.html b/packages/stacks-docs/product/components/navigation.html index e0afc170af..f5d2a49e57 100644 --- a/packages/stacks-docs/product/components/navigation.html +++ b/packages/stacks-docs/product/components/navigation.html @@ -607,43 +607,51 @@ {% highlight html %} @@ -651,35 +659,42 @@
    From 76ce5c4c3784a4abbc394c7eb88d20c79c46875e Mon Sep 17 00:00:00 2001 From: Sal Date: Thu, 12 Feb 2026 17:33:43 -0500 Subject: [PATCH 2/7] rename navigationtitle to navigationgroup --- .../Navigation/{NavigationTitle.svelte => NavigationGroup.svelte} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename packages/stacks-svelte/src/components/Navigation/{NavigationTitle.svelte => NavigationGroup.svelte} (100%) diff --git a/packages/stacks-svelte/src/components/Navigation/NavigationTitle.svelte b/packages/stacks-svelte/src/components/Navigation/NavigationGroup.svelte similarity index 100% rename from packages/stacks-svelte/src/components/Navigation/NavigationTitle.svelte rename to packages/stacks-svelte/src/components/Navigation/NavigationGroup.svelte From 9780a0eeef0a2a08eb935b239ae628e4e4af3f51 Mon Sep 17 00:00:00 2001 From: Sal Date: Thu, 12 Feb 2026 17:34:26 -0500 Subject: [PATCH 3/7] svelte changes --- .../Navigation/Navigation.stories.svelte | 170 +++++++++--------- .../components/Navigation/Navigation.svelte | 22 +-- .../components/Navigation/Navigation.test.ts | 100 ++++++----- .../Navigation/NavigationGroup.svelte | 50 ++++-- .../Navigation/NavigationItem.svelte | 16 +- .../src/components/Notice/Notice.test.ts | 6 +- .../stacks-svelte/src/components/index.ts | 2 +- 7 files changed, 199 insertions(+), 167 deletions(-) diff --git a/packages/stacks-svelte/src/components/Navigation/Navigation.stories.svelte b/packages/stacks-svelte/src/components/Navigation/Navigation.stories.svelte index 5f971db800..5d8cd9c4f7 100644 --- a/packages/stacks-svelte/src/components/Navigation/Navigation.stories.svelte +++ b/packages/stacks-svelte/src/components/Navigation/Navigation.stories.svelte @@ -2,7 +2,7 @@ import { defineMeta } from "@storybook/addon-svelte-csf"; import Navigation from "./Navigation.svelte"; import NavigationItem from "./NavigationItem.svelte"; - import NavigationTitle from "./NavigationTitle.svelte"; + import NavigationGroup from "./NavigationGroup.svelte"; import Icon from "../Icon/Icon.svelte"; import Button from "../Button/Button.svelte"; import ActivityIndicator from "../ActivityIndicator/ActivityIndicator.svelte"; @@ -46,7 +46,7 @@ title: "Components/Navigation", component: Navigation, // @ts-expect-error: subcomponents is not typed correctly - see related issue https://github.com/storybookjs/storybook/issues/23170 - subcomponents: { NavigationItem, NavigationTitle }, + subcomponents: { NavigationItem, NavigationGroup }, }); const horizontalItems = [ @@ -282,22 +282,24 @@ - - {#each ["Label 1", "Label 2", "Label 3"] as label (label)} - (base = label)} - /> - {/each} - - {#each ["Label 4", "Label 5", "Label 6"] as label (label)} - (base = label)} - /> - {/each} + + {#each ["Label 1", "Label 2", "Label 3"] as label (label)} + (base = label)} + /> + {/each} + + + {#each ["Label 4", "Label 5", "Label 6"] as label (label)} + (base = label)} + /> + {/each} + @@ -319,7 +321,11 @@ - + tSelected === item.text )} - {#snippet trailing()}
    @@ -385,76 +392,77 @@
    {/snippet} -
    - - {@const selectedIndex = groupItems.findIndex( - (item) => tSelected === item.text - )} - {@const itemsBefore = - selectedIndex >= 0 - ? groupItems.slice(0, selectedIndex) - : groupItems} - {@const itemsAfter = - selectedIndex >= 0 ? groupItems.slice(selectedIndex + 1) : []} + {@const selectedIndex = groupItems.findIndex( + (item) => tSelected === item.text + )} + {@const itemsBefore = + selectedIndex >= 0 + ? groupItems.slice(0, selectedIndex) + : groupItems} + {@const itemsAfter = + selectedIndex >= 0 + ? groupItems.slice(selectedIndex + 1) + : []} - {#each itemsBefore as item (item.text)} - {#if !isCollapsed} - (tSelected = item.text)} - animate - > - {#snippet trailing()} - {#if item.activity} - - {/if} - {/snippet} - - {/if} - {/each} - {#if selectedItem} - (tSelected = selectedItem.text)} - > - {#snippet trailing()} - {#if selectedItem.activity} - - {/if} - {/snippet} - - {/if} - {#each itemsAfter as item (item.text)} - {#if !isCollapsed} + {#each itemsBefore as item (item.text)} + {#if !isCollapsed} + (tSelected = item.text)} + animate + > + {#snippet trailing()} + {#if item.activity} + + {/if} + {/snippet} + + {/if} + {/each} + {#if selectedItem} (tSelected = item.text)} - animate + icon={selectedItem.icon} + iconSelected={selectedItem.iconSelected} + text={selectedItem.text} + selected + onclick={() => (tSelected = selectedItem.text)} > {#snippet trailing()} - {#if item.activity} + {#if selectedItem.activity} {/if} {/snippet} {/if} - {/each} + {#each itemsAfter as item (item.text)} + {#if !isCollapsed} + (tSelected = item.text)} + animate + > + {#snippet trailing()} + {#if item.activity} + + {/if} + {/snippet} + + {/if} + {/each} + {/each}
    diff --git a/packages/stacks-svelte/src/components/Navigation/Navigation.svelte b/packages/stacks-svelte/src/components/Navigation/Navigation.svelte index 251edaf3b9..b5ddf6e098 100644 --- a/packages/stacks-svelte/src/components/Navigation/Navigation.svelte +++ b/packages/stacks-svelte/src/components/Navigation/Navigation.svelte @@ -1,5 +1,6 @@ -