Skip to content

Commit

Permalink
feat(topbar): add skip link (#1692)
Browse files Browse the repository at this point in the history
Co-authored-by: Giamir Buoncristiani <[email protected]>
Co-authored-by: Dan Cormier <[email protected]>
  • Loading branch information
3 people authored Apr 16, 2024
1 parent e7e8280 commit 2682dff
Show file tree
Hide file tree
Showing 7 changed files with 107 additions and 41 deletions.
7 changes: 6 additions & 1 deletion docs/_data/topbar.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@
"applies": "`.s-topbar`",
"description": "Forced dark theme"
},
{
"class": ".s-topbar--skip-link",
"description": "Applied to an anchor tag for bypassing the top-level navigation links and jump to the main content on a page. [See WCAG Technique G1](https://www.w3.org/WAI/WCAG22/Techniques/general/G1)",
"applies": "Child of `.s-topbar`"
},
{
"class": ".s-topbar--container",
"description": "Add **atomic** `classes` here to customize internal content width; defaults to `.wmx12`",
Expand Down Expand Up @@ -68,4 +73,4 @@
"applies": "`.s-topbar--searchbar`",
"description": "On mobile, this class shows the search input below the topbar. Toggle this class with a button"
}
]
]
1 change: 1 addition & 0 deletions docs/_includes/header.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<header class="s-topbar stacks-topbar ps-fixed js-stacks-topbar print:d-none">
<a href="#content" class="s-topbar--skip-link">Skip to main content</a>
<div class="s-topbar--container px8">
<a href="#" class="s-topbar--menu-btn d-none md:d-flex js-hamburger-btn"><span></span></a>
<a class="s-topbar--logo" href="{{ "/" | url }}">
Expand Down
2 changes: 1 addition & 1 deletion docs/_includes/layouts/page.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<div class="d-flex mx-auto w100 wmx12">
{% include 'navigation.html' %}

<main id="content" class="d-flex fl-grow1 ps-relative t64 py24 pl48 md:pl24 sm:pl16 sm:pr16">
<main id="content" class="d-flex fl-grow1 ps-relative t24 pt64 pb24 pl48 md:pl24 sm:pl16 sm:pr16">
{% if hide-menu != true %}
<div class="flex--item order-last ml32 sm:d-none print:d-none" style="width: 14rem;">
<div class="ps-fixed t64 b0 py12 overflow-y-auto" style="width: 14rem;">
Expand Down
3 changes: 2 additions & 1 deletion docs/_includes/topbar.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<header class="s-topbar {{ additionalClass }} z-base js-topbar-example">
<a href="#content" class="s-topbar--skip-link">Skip to main content</a>
<div class="s-topbar--container {{ containerClass }}">
{% if showMenu %}
<a href="#" class="s-topbar--menu-btn" aria-label="menu"><span></span></a>
Expand Down Expand Up @@ -152,4 +153,4 @@
</ol>
</nav>
</div>
</header>
</header>
98 changes: 63 additions & 35 deletions docs/product/components/topbar.html
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
<div class="stacks-preview">
{% highlight html %}
<header class="s-topbar">
<a href="#content" class="s-topbar--skip-link">Skip to main content</a>
<div class="s-topbar--container">
<a href="" class="s-topbar--menu-btn"><span></span></a>
<a href="" class="s-topbar--logo">@Svg.LogoGlyph.with("native")</a>
Expand All @@ -58,6 +59,26 @@
{% render 'topbar.html', hideNavigation: true, hideSearch: true, showMenu: true %}
</div>
</div>

{% header "h3", "Skip Link" %}
<p class="stacks-copy">All topbars must include a skip link.
Usually a skip link should be the first focusable element of a page (with the expection of cookie consent dialogs).
Skip links are a mechanism to bypass blocks of content that are repeated on multiple web pages and comply with <a class="s-link" href="https://www.w3.org/WAI/WCAG22/Understanding/bypass-blocks">WCAG SC 2.4.1</a>.</p>

{% tip, "info", "mb24" %}
<strong>Note:</strong> Skip links become visible only when focused via keyboard navigation and they are announced by screen readers. In the example below hit <code class="stacks-code">Tab</code> on your keyboard to see it appearing when in focus.
{% endtip %}
<div class="stacks-preview">
{% highlight html %}
<header class="s-topbar">
<a href="#content" class="s-topbar--skip-link">Skip to main content</a>
<div class="s-topbar--container"></div>
</header>
{% endhighlight %}
<div class="stacks-preview--example bg-black-100">
{% render 'topbar.html', hideNavigation: true, hideSearch: true %}
</div>
</div>
</section>

<section class="stacks-section">
Expand Down Expand Up @@ -90,21 +111,24 @@ <h4 class="fs-body2 mb8">{{ variation | split: "__" | last | capitalize }}</h4>
<div class="stacks-preview">
{% highlight html %}
<header class="s-topbar">
<a href="" class="s-topbar--logo"></a>
<a href="#content" class="s-topbar--skip-link">Skip to main content</a>
<div class="s-topbar--container">
<a href="" class="s-topbar--logo"></a>

<form id="search" class="s-topbar--searchbar" autocomplete="off">
<div class="s-select">
<select aria-label="Search scope"></select>
</div>
<div class="s-topbar--searchbar--input-group">
<input type="text" placeholder="Search…" value="" autocomplete="off" class="s-input s-input__search" />
@Svg.Search.With("s-input-icon s-input-icon__search")
</div>
</form>
<form id="search" class="s-topbar--searchbar" autocomplete="off">
<div class="s-select">
<select aria-label="Search scope"></select>
</div>
<div class="s-topbar--searchbar--input-group">
<input type="text" placeholder="Search…" value="" autocomplete="off" class="s-input s-input__search" />
@Svg.Search.With("s-input-icon s-input-icon__search")
</div>
</form>

<nav class="s-topbar--navigation" aria-label="primary navigation">
<ol class="s-topbar--content"></ol>
</nav>
<nav class="s-topbar--navigation" aria-label="primary navigation">
<ol class="s-topbar--content"></ol>
</nav>
</div>
</header>
{% endhighlight %}
<div class="stacks-preview--example bg-black-100">
Expand All @@ -126,31 +150,34 @@ <h4 class="fs-body2 mb8">Input only</h4>
<div class="stacks-preview">
{% highlight html %}
<header class="s-topbar">
<a href="" class="s-topbar--menu-btn"></a>
<a href="" class="s-topbar--logo"></a>
<a href="#content" class="s-topbar--skip-link">Skip to main content</a>
<div class="s-topbar--container">
<a href="" class="s-topbar--menu-btn"></a>
<a href="" class="s-topbar--logo"></a>

<a href="#" class="s-topbar--notice is-unread">
New
</a>
<a href="#" class="s-topbar--notice is-unread">
New
</a>

<nav aria-label="More from Stack Overflow">
<ol class="s-navigation">
<li><a href="" class="s-navigation--item">About</a></li>
<li><a href="" class="s-navigation--item is-selected">Products</a></li>
<li><a href="" class="s-navigation--item">For Teams</a></li>
</ol>
</nav>
<nav aria-label="More from Stack Overflow">
<ol class="s-navigation">
<li><a href="" class="s-navigation--item">About</a></li>
<li><a href="" class="s-navigation--item is-selected">Products</a></li>
<li><a href="" class="s-navigation--item">For Teams</a></li>
</ol>
</nav>

<nav class="s-topbar--navigation" aria-label="Log in or sign up">
<ol class="s-topbar--content">
<li>
<a href="" class="s-topbar--item s-topbar--item__unset s-btn s-btn__filled">Log in</a>
</li>
<li>
<a href="" class="s-topbar--item s-topbar--item__unset ml4 s-btn s-btn__filled">Sign up</a>
</li>
</ol>
</nav>
<nav class="s-topbar--navigation" aria-label="Log in or sign up">
<ol class="s-topbar--content">
<li>
<a href="" class="s-topbar--item s-topbar--item__unset s-btn s-btn__filled">Log in</a>
</li>
<li>
<a href="" class="s-topbar--item s-topbar--item__unset ml4 s-btn s-btn__filled">Sign up</a>
</li>
</ol>
</nav>
</div>
</header>
{% endhighlight %}
<div class="stacks-preview--example bg-black-100">
Expand All @@ -163,6 +190,7 @@ <h4 class="fs-body2 mb8">Input only</h4>
<div class="stacks-preview">
{% highlight html %}
<header class="s-topbar">
<a href="#content" class="s-topbar--skip-link">Skip to main content</a>
<div class="s-topbar--container wmx75"></div>
</header>
{% endhighlight %}
Expand Down
36 changes: 33 additions & 3 deletions lib/components/topbar/topbar.less
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
.s-topbar {
--_tb-bt: var(--theme-topbar-accent-border, 3px solid var(--theme-primary));
--_tb-h: var(--theme-topbar-height, calc(var(--su-static48) + var(--su-static8)));
// CHILD COMPONENT CUSTOM PROPERTIES
// Item
--_tb-item-bg: unset;
Expand Down Expand Up @@ -109,6 +111,21 @@
}

// CHILD ELEMENTS
&:has(> &--skip-link:focus) {
.s-topbar {
&--container {
height: var(--_tb-h);
}
&--skip-link {
border-bottom: var(--_tb-bt);
}
}

border-top: none;
display: block;
height: auto;
}

& a&--logo {
&:focus-visible {
.focus-styles(true);
Expand Down Expand Up @@ -401,6 +418,20 @@
padding: var(--_tb-searchbar-p);
}

& &--skip-link {
&:not(:focus) {
&:extend(.v-visible-sr);
}

background-color: var(--theme-secondary-100);
border-bottom: var(--_tb-bt);
display: block;
outline: none;
padding: var(--su12);
text-align: center;
}


.s-navigation {
.s-navigation--item {
&:not(.is-selected) {
Expand All @@ -427,13 +458,12 @@
}
}

// STATIC COMPONENT STYLES
align-items: center;
background-color: var(--theme-topbar-background-color, var(--white));
border-bottom: var(--theme-topbar-bottom-border, var(--su-static1) solid var(--black-225));
border-top: var(--theme-topbar-accent-border, 3px solid var(--theme-primary));
border-top: var(--_tb-bt);
display: flex;
height: var(--theme-topbar-height, calc(var(--su-static48) + var(--su-static8)));
height: var(--_tb-h);
min-width: auto;
position: relative;
width: 100%;
Expand Down
1 change: 1 addition & 0 deletions lib/components/topbar/topbar.visual.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ const topbarChildren = ({
unsetItem?: boolean;
}) => {
return `
<a href="#content" class="s-topbar--skip-link">Skip to main content</a>
<div class="s-topbar--container">
${hamburger ? (hamburger === "unselected" ? children.hamburger.unselected : children.hamburger.selected) : ""}
${children.logo}
Expand Down

0 comments on commit 2682dff

Please sign in to comment.