Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .changeset/proud-wasps-travel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
"@stackoverflow/stacks": patch
"@stackoverflow/stacks-svelte": patch
---

fix(navigation): make vertical navigation more accessible

BREAKING CHANGES:
* Navigation markup has been updated
* Svelte NavigationTitle component has been renamed to NavigationGroup
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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 `<li><a href="#" class="${getClasses(item)}">${item.label}</a></li>`;
})
.join("");
} else {
//Vertical nav
let html = "";
for (const item of items) {
if (item.title) {
return includeTitles
? `<li class="s-navigation--title">${item.label}</li>`
: "";
if (html.length > 0) {
html += "</ul></li>";
}
const groupName = item.label.replace(" ", "");
html += `<li>
<h4 class="s-navigation--title" id="nav-${groupName}">${item.label}</h4>
<ul aria-labelledby="nav-${groupName}">
`;
} else {
html += `<li><a href="#" class="${getClasses(item)}">${item.label}</a></li>`;
}
const classes = `s-navigation--item${
item.selected ? " is-selected" : ""
}${item.dropdown ? " s-navigation--item__dropdown" : ""}`;
return `<li><a href="#" class="${classes}">${item.label}</a></li>`;
})
.join("");
}
html += "</ul></li>";
return html;
}
};

describe("navigation", () => {
runA11yTests({
Expand Down
20 changes: 12 additions & 8 deletions packages/stacks-classic/lib/components/navigation/navigation.less
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
.s-navigation {
.s-navigation,
.s-navigation ul {
--_na-fd: row;
--_na-fw: wrap;
--_na-p: var(--su2) 0;
Expand All @@ -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);

Expand All @@ -42,7 +42,8 @@
--_na-item-p: var(--su6) var(--su4);
}

&&__vertical {
&&__vertical,
&&__vertical ul {
--_na-fd: column;
--_na-gap: 0;
--_na-p: 0;
Expand All @@ -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);
Expand Down Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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 `<li><a href="#" class="${getClasses(item)}">${getIcon(item)}${item.label}</a></li>`;
})
.join("");
} else {
//Vertical nav
let html = "";
for (const item of items) {
if (item.title) {
return includeTitles
? `<li class="s-navigation--title">${item.label}</li>`
: "";
if (html.length > 0) {
html += "</ul></li>";
}
const groupName = item.label.replace(" ", "");
html += `<li>
<h4 class="s-navigation--title" id="nav-${groupName}">${item.label}</h4>
<ul aria-labelledby="nav-${groupName}">
`;
} else {
html += `<li><a href="#" class="${getClasses(item)}">${getIcon(item)}${item.label}</a></li>`;
}
const icon = includeIcons
? item.selected
? filledIcon
: outlineIcon
: "";
const classes = `s-navigation--item${
item.selected ? " is-selected" : ""
}${item.dropdown ? " s-navigation--item__dropdown" : ""}`;
return `<li><a href="#" class="${classes}">${icon}${item.label}</a></li>`;
})
.join("");
}
html += "</ul></li>";
return html;
}
};

describe("navigation", () => {
runVisualTests({
Expand Down
6 changes: 3 additions & 3 deletions packages/stacks-docs/_includes/layouts/home.html
Original file line number Diff line number Diff line change
Expand Up @@ -202,11 +202,11 @@ <h1 class="fw-bold mln2 mb0 mtn4 fs-display1 lh-xs">Stacks</h1>
<div class="bg-black-100 ai-center d-flex fd-column jc-center w100">
<nav class="d-grid grid__2 fs-body2 g16 px64 py32 w100 wmx12 lg:px32 md:py24 sm:grid__1 sm:g8 sm:p24" aria-label="Global">
{% for card in home.cards %}
<a class="s-card ai-center d-flex g32 p16" href="{{ card.url }}">
<a class="s-card s-link ai-center d-flex g32 p16" href="{{ card.url }}">
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you! I've been bothered by the ugly browser-default blue/purple each time I hit the docs.

{% spot card.spot, "native h96 w96 sm:d-none" %}
<div class="fl-grow1 w75">
<div class="fw-bold fs-headline1 mb2">{{ card.title }}</div>
<div class="fs-body3 fc-medium">{{ card.description }}</div>
<h2 class="fw-bold fs-headline1 mb2">{{ card.title }}</h2>
<span class="fs-body3 fc-medium">{{ card.description }}</span>
</div>
</a>
{% endfor %}
Expand Down
137 changes: 76 additions & 61 deletions packages/stacks-docs/product/components/navigation.html
Original file line number Diff line number Diff line change
Expand Up @@ -607,79 +607,94 @@
{% highlight html %}
<nav aria-label="…">
<ul class="s-navigation s-navigation__vertical">
<li class="s-navigation--title">…</li>
<li>
<a href="…" class="s-navigation--item is-selected">
<span class="s-navigation--item-text" data-text="…">…</span>
</a>
</li>
<li>
<a href="…" class="s-navigation--item">
<span class="s-navigation--item-text" data-text="…">…</span>
</a>
<h4 class="s-navigation--title" id="nav-section1">…</h4>
<ul aria-labelledby="nav-section1">
<li>
<a href="…" class="s-navigation--item is-selected">
<span class="s-navigation--item-text" data-text="…">…</span>
</a>
</li>
<li>
<a href="…" class="s-navigation--item">
<span class="s-navigation--item-text" data-text="…">…</span>
</a>
</li>
</ul>
</li>

<li class="s-navigation--title">…</li>
<li>
<a href="…" class="s-navigation--item">
<span class="s-navigation--item-text" data-text="…">…</span>
</a>
</li>
<li>
<a href="…" class="s-navigation--item">
<span class="s-navigation--item-text" data-text="…">…</span>
</a>
</li>
<li>
<a href="…" class="s-navigation--item">
<span class="s-navigation--item-text" data-text="…">…</span>
</a>
</li>
<li>
<a href="…" class="s-navigation--item">
<span class="s-navigation--item-text" data-text="…">…</span>
</a>
</li>
<li>
<a href="…" class="s-navigation--item">
<span class="s-navigation--item-text" data-text="…">…</span>
</a>
<h4 class="s-navigation--title" id="nav-section2">…</h4>
<ul aria-labelledby="nav-section2">
<li>
<a href="…" class="s-navigation--item">
<span class="s-navigation--item-text" data-text="…">…</span>
</a>
</li>
<li>
<a href="…" class="s-navigation--item">
<span class="s-navigation--item-text" data-text="…">…</span>
</a>
</li>
<li>
<a href="…" class="s-navigation--item">
<span class="s-navigation--item-text" data-text="…">…</span>
</a>
</li>
<li>
<a href="…" class="s-navigation--item">
<span class="s-navigation--item-text" data-text="…">…</span>
</a>
</li>
<li>
<a href="…" class="s-navigation--item">
<span class="s-navigation--item-text" data-text="…">…</span>
</a>
</li>
</ul>
</li>
</ul>
</nav>
{% endhighlight %}
<div class="docs-preview--example">
<nav class="ws2" aria-label="titles navigation">
<ul class="s-navigation s-navigation__vertical">
<li class="s-navigation--title">Resources</li>
<li><a href="#" class="s-navigation--item is-selected">Icons</a></li>
<li><a href="#" class="s-navigation--item">Spot illustrations</a></li>

<li class="s-navigation--title">Base</li>
<li>
<a href="#" class="s-navigation--item is-selected">
{% icon "HomeFill", "s-navigation--icon" %}
<span class="s-navigation--item-text" data-text="Home">Home</span>
</a>
</li>
<li>
<a href="#" class="s-navigation--item">
{% icon "Jobs", "s-navigation--icon" %}
<span class="s-navigation--item-text" data-text="Jobs">Jobs</span>
</a>
</li>
<li>
<a href="#" class="s-navigation--item">
{% icon "Bookmark", "s-navigation--icon" %}
<span class="s-navigation--item-text" data-text="Saves">Saves</span>
</a>
</li>
<li>
<a href="#" class="s-navigation--item">
{% icon "UserStack", "s-navigation--icon" %}
<span class="s-navigation--item-text" data-text="Users">Users</span>
</a>
</li>
<h4 class="s-navigation--title" id="nav-resources">Resources</h4>
<ul aria-labelledby="nav-resources">
<li><a href="#" class="s-navigation--item is-selected">Icons</a></li>
<li><a href="#" class="s-navigation--item">Spot illustrations</a></li>
</ul>
</li>
<li>
<h4 class="s-navigation--title" id="nav-base">Base</h4>
<ul aria-labelledby="nav-base">
<li>
<a href="#" class="s-navigation--item is-selected">
{% icon "HomeFill", "s-navigation--icon" %}
<span class="s-navigation--item-text" data-text="Home">Home</span>
</a>
</li>
<li>
<a href="#" class="s-navigation--item">
{% icon "Jobs", "s-navigation--icon" %}
<span class="s-navigation--item-text" data-text="Jobs">Jobs</span>
</a>
</li>
<li>
<a href="#" class="s-navigation--item">
{% icon "Bookmark", "s-navigation--icon" %}
<span class="s-navigation--item-text" data-text="Saves">Saves</span>
</a>
</li>
<li>
<a href="#" class="s-navigation--item">
{% icon "UserStack", "s-navigation--icon" %}
<span class="s-navigation--item-text" data-text="Users">Users</span>
</a>
</li>
</ul>
</li>
</ul>
</nav>
</div>
Expand Down
Loading