Skip to content

Commit 3521c7f

Browse files
authored
Topbar component (#754)
1 parent 9369bfa commit 3521c7f

18 files changed

+980
-90
lines changed

docs/.eleventy.js

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,35 @@
11
const hljs = require("highlight.js");
22
const syntaxHighlight = require("eleventy-plugin-highlightjs");
33
const pluginTOC = require("eleventy-plugin-nesting-toc");
4+
const markdownIt = require("markdown-it")("commonmark");
45
const markdownShortcode = require("eleventy-plugin-markdown-shortcode");
56
const { default: Icons, Spots } = require("@stackoverflow/stacks-icons");
67
const { version } = require("../package.json");
78

9+
// customize markdown-it rendering to add our classes
10+
markdownIt.use(function(md) {
11+
// mapping of tag: classes
12+
const customClasses = {
13+
"code": "stacks-code"
14+
}
15+
16+
const addClasses = function(tokens) {
17+
tokens.forEach(function(token) {
18+
if (token.tag in customClasses) {
19+
token.attrJoin("class", customClasses[token.tag]);
20+
}
21+
22+
if (token.children) {
23+
addClasses(token.children);
24+
}
25+
});
26+
};
27+
28+
md.core.ruler.push("add-custom-classes", function(state) {
29+
addClasses(state.tokens);
30+
});
31+
});
32+
833
module.exports = function(eleventyConfig) {
934
eleventyConfig.setQuietMode(true); // Reduce the console output
1035
eleventyConfig.addLayoutAlias('home', 'layouts/home.html');
@@ -114,6 +139,9 @@ module.exports = function(eleventyConfig) {
114139
return {version}.version;
115140
});
116141

142+
eleventyConfig.addLiquidFilter("markdown", function(content) {
143+
return markdownIt.renderInline(content);
144+
});
117145

118146
// highlightjs line-numbering support
119147
// add `linenums` or `linenums:startNumber` to the start of your code for detection

docs/_data/site-navigation.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,10 @@
302302
"title": "Toggle switch",
303303
"url": "/product/components/toggle-switch/"
304304
},
305+
{
306+
"title": "Topbar",
307+
"url": "/product/components/topbar/"
308+
},
305309
{
306310
"title": "Uploader",
307311
"url": "/product/components/uploader/"

docs/_data/topbar.json

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
[
2+
{
3+
"class": ".s-topbar",
4+
"description": "Base class"
5+
},
6+
{
7+
"class": ".s-topbar__light",
8+
"applies": "`.s-topbar`",
9+
"description": "Forced light theme"
10+
},
11+
{
12+
"class": ".s-topbar__dark",
13+
"applies": "`.s-topbar`",
14+
"description": "Forced dark theme"
15+
},
16+
{
17+
"class": ".s-topbar--container",
18+
"description": "Add **atomic** `classes` here to customize internal content width; defaults to `.wmx12`",
19+
"applies": "Child of `.s-topbar`"
20+
},
21+
{
22+
"class": ".s-topbar--logo",
23+
"description": "The logo. Can apply `.is-selected` to this",
24+
"applies": "Child of `.s-topbar`"
25+
},
26+
{
27+
"class": ".s-topbar--menu-btn",
28+
"description": "Hamburger menu icon. Add `.is-selected` to turn it into an X",
29+
"applies": "Child of `.s-topbar`"
30+
},
31+
{
32+
"class": ".s-topbar--content",
33+
"description": "Contains `li > .s-topbar--item` elements that don’t look like anything in particular",
34+
"applies": "Child of `.s-topbar`"
35+
},
36+
{
37+
"class": ".s-topbar--ctas",
38+
"description": "Provides layout for various calls to action, e.g. log in or sign up links. This container provides layout only, leaving child elements to their own styling.",
39+
"applies": "Child of `.s-topbar`"
40+
},
41+
{
42+
"class": ".s-topbar--item",
43+
"description": "A topbar item element, styles itself based on where it is placed. Can add `.is-selected`",
44+
"applies": "Child of `.s-topbar--content > li`,\n`.s-topbar--ctas > li`"
45+
},
46+
{
47+
"class": ".s-topbar--notice",
48+
"description": "A badge-styled notice that stands out. Add `.is-unread` to make it stand out more",
49+
"applies": "Child of `.s-topbar`"
50+
},
51+
{
52+
"class": ".s-topbar--searchbar",
53+
"description": "Contains a search input and an optional `.s-select`. Hides itself on mobile.",
54+
"applies": "Child of `.s-topbar`"
55+
},
56+
{
57+
"class": ".s-topbar--searchbar--input-group",
58+
"description": "Holds the search input + icon",
59+
"applies": "Child of `.s-topbar--searchbar`"
60+
},
61+
{
62+
"class": ".s-topbar--searchbar__open",
63+
"applies": "`.s-topbar--searchbar`",
64+
"description": "On mobile, this class shows the search input below the topbar. Toggle this class with a button"
65+
}
66+
]

docs/_includes/header.html

Lines changed: 66 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,90 +1,96 @@
1-
<header class="bg-black-050 ps-fixed h64 t0 l0 r0 z-nav-fixed bb bc-black-100 js-stacks-topbar print:d-none">
2-
<div class="d-flex ai-center px16 h100 mx-auto wmx12">
3-
<button class="s-btn__unset c-pointer d-flex flex__center p8 mr8 fc-black-300 d-none md:d-block js-hamburger-btn">
4-
{% icon "Hamburger", "js-hamburger-icon" %}
5-
{% icon "Clear", "d-none js-hamburger-close-icon" %}
6-
</button>
7-
<a class="flex--item js-logo" href="{{ "/" | relative_url }}">
1+
<header class="s-topbar stacks-topbar ps-fixed h64 js-stacks-topbar print:d-none">
2+
<div class="s-topbar--container px8">
3+
<a href="#" class="s-topbar--menu-btn d-none md:d-flex js-hamburger-btn"><span></span></a>
4+
<a class="s-topbar--logo" href="{{ "/" | relative_url }}">
85
<span class="v-visible-sr">Stacks home</span>
96
{% icon "LogoGlyphMd", "native" %}
107
</a>
118

129
{% if site-navigation %}
13-
<nav class="s-navigation mx16 fw-nowrap sm:d-none">
10+
<ul class="s-navigation ml8 fw-nowrap sm:d-none">
1411
{% assign slug = page.url | replace:'/',' ' | truncatewords: 2 | remove:'...' %}
1512

1613
{% for section in site-navigation.sections %}
1714
{% assign url = section.url | replace:'/',' ' | truncatewords: 2 | remove:'...' %}
18-
<a class="s-navigation--item{% if url == slug %} is-selected{% endif %}" href="{{ section.url }}">{{ section.title }}</a>
15+
<li><a class="s-navigation--item{% if url == slug %} is-selected{% endif %}" href="{{ section.url }}">{{ section.title }}</a></li>
1916
{% endfor %}
20-
</nav>
17+
</ul>
2118
{% endif %}
2219

23-
<div class="d-flex ai-center jc-end fl-grow1">
24-
<div class="d-flex ai-center js-links">
25-
<a class="flex--item fc-black-300 sm:d-none" title="Stack Overflow" href="https://stackoverflow.com">
20+
<ol class="s-topbar--content sm:ml0 overflow-hidden">
21+
<li>
22+
<a class="s-topbar--item sm:d-none" title="Stack Overflow" href="https://stackoverflow.com">
2623
{% icon "LogoGlyphSm" %}
2724
</a>
28-
29-
<a class="flex--item fc-black-300 ml12 sm:ml16" title="Stacks on GitHub" href="https://github.com/StackExchange/Stacks">
25+
</li>
26+
<li>
27+
<a class="s-topbar--item title="Stacks on GitHub" href="https://github.com/StackExchange/Stacks">
3028
{% icon "GitHub", "", "24" %}
3129
</a>
32-
33-
<a class="flex--item fc-black-300 ml12" title="Stacks Template on Codepen" href="https://codepen.io/pen?template=VwvKygL">
30+
</li>
31+
<li>
32+
<a class="s-topbar--item" title="Stacks Template on Codepen" href="https://codepen.io/pen?template=VwvKygL">
3433
<svg aria-hidden="true" class="svg-icon iconCodepen" width="24" height="24" viewBox="0 0 18 18"><path d="M12.67 8.17l-2.98-2v-3.2l5.38 3.6-2.4 1.6zM13.9 9l1.73-1.15v2.3L13.9 9zm-4.2 2.82l2.98-2 2.4 1.62-5.38 3.59v-3.2zm-4.36-2l2.98 2v3.2l-5.38-3.58 2.4-1.61zM4.1 9l-1.73 1.15v-2.3L4.1 9zm4.2-2.82l-2.98 2-2.4-1.62L8.3 2.97v3.2zm.7 4.45L6.57 9 9 7.37 11.43 9 9 10.63zm7.99-4.19l-.01-.05-.01-.04-.02-.05-.02-.03a.6.6 0 0 0-.02-.05l-.02-.03a.69.69 0 0 0-.15-.17L16.7 6h-.02L9.4 1.11a.69.69 0 0 0-.77 0L1.3 5.99h-.02c0 .02-.02.02-.03.03a.81.81 0 0 0-.12.13.69.69 0 0 0-.03.04l-.02.03-.02.05-.02.03-.02.05v.04L1 6.44v.03a.7.7 0 0 0-.01.1v4.87a.7.7 0 0 0 0 .09l.01.03.01.05.01.04.02.05.02.03a.51.51 0 0 0 .07.12.53.53 0 0 0 .08.1c.02 0 .03.02.04.03l.03.02h.02l7.3 4.88a.69.69 0 0 0 .77 0l7.31-4.87h.02c0-.02.02-.02.03-.03a.72.72 0 0 0 .04-.04l.02-.02a.62.62 0 0 0 .13-.19l.02-.03a.6.6 0 0 0 .02-.05v-.04l.02-.05v-.03a.7.7 0 0 0 .01-.1V6.57a.7.7 0 0 0 0-.09l-.01-.03z"/></svg>
3534
</a>
36-
</div>
35+
</li>
36+
</ol>
3737

38-
<div class="flex--item ps-relative ml16 w100 wmx3 sm:wmx-initial sm:ml0 sm:d-none js-search">
39-
<input id="searchbox" class="s-input s-input__search bar-md js-stacks-search-bar" type="text" placeholder="Search Stacks…">
38+
<div class="s-topbar--searchbar w100 wmx3 sm:wmx-initial js-search">
39+
<div class="s-topbar--searchbar--input-group">
40+
<input id="searchbox" type="text" placeholder="Search Stacks…" value="" autocomplete="off" class="s-input s-input__search" />
4041
{% icon "Search", "s-input-icon s-input-icon__search" %}
4142
</div>
43+
</div>
4244

43-
<button class="d-flex flex__center s-btn__unset c-pointer p8 ml-auto fc-black-300 d-none sm:d-block js-search-btn">
44-
{% icon "Search", "js-search-icon" %}
45-
{% icon "Clear", "d-none js-search-close-icon" %}
46-
</button>
47-
48-
<button class="s-btn__unset c-pointer flex--item fc-black-300 ml12 stacks-theme-button"
49-
aria-controls="theming-popover"
50-
data-controller="s-popover"
51-
data-action="s-popover#toggle"
52-
data-s-popover-toggle-class="is-selected"
53-
data-s-popover-placement="bottom-end"
54-
title="Select a theme">
55-
{% icon "Theme", "", "24" %}
56-
</button>
45+
<ol class="s-topbar--content ml0 sm:fl1 overflow-hidden">
46+
<li class="d-none sm:d-inline-flex ml-auto">
47+
<button class="s-topbar--item s-btn__unset c-pointer p8 ml-auto js-search-btn">
48+
{% icon "Search", "js-search-icon" %}
49+
{% icon "Clear", "d-none js-search-close-icon" %}
50+
</button>
51+
</li>
52+
<li>
53+
<button type="button" class="s-topbar--item s-btn__unset c-pointer stacks-theme-button"
54+
aria-controls="theming-popover"
55+
data-controller="s-popover"
56+
data-action="s-popover#toggle"
57+
data-s-popover-toggle-class="is-selected"
58+
data-s-popover-placement="bottom-end"
59+
title="Select a theme">
60+
{% icon "Theme", "", "24" %}
61+
</button>
62+
</li>
63+
</ol>
5764

58-
<div class="s-popover w-auto wmn-initial wmx-initial"
59-
id="theming-popover"
60-
role="menu">
61-
<div class="s-popover--arrow"></div>
62-
<div class="d-flex fd-column gs12 gsy">
63-
<div class="flex--item">
64-
<div class="d-flex ai-center jc-space-between gs8">
65-
<label class="flex--item s-label fs-body1 fw-normal" for="toggle-theme-dark">Dark mode</label>
66-
<div class="flex--item s-toggle-switch">
67-
<input id="toggle-theme-dark" type="checkbox">
68-
<div class="s-toggle-switch--indicator"></div>
69-
</div>
65+
<div class="s-popover w-auto wmn-initial wmx-initial"
66+
id="theming-popover"
67+
role="menu">
68+
<div class="s-popover--arrow"></div>
69+
<div class="d-flex fd-column gs12 gsy">
70+
<div class="flex--item">
71+
<div class="d-flex ai-center jc-space-between gs8">
72+
<label class="flex--item s-label fs-body1 fw-normal" for="toggle-theme-dark">Dark mode</label>
73+
<div class="flex--item s-toggle-switch">
74+
<input id="toggle-theme-dark" type="checkbox">
75+
<div class="s-toggle-switch--indicator"></div>
7076
</div>
7177
</div>
72-
<div class="flex--item">
73-
<div class="d-flex ai-center jc-space-between gs8">
74-
<label class="flex--item s-label fs-body1 fw-normal" for="toggle-theme-custom">Custom theme</label>
75-
<div class="flex--item s-toggle-switch">
76-
<input id="toggle-theme-custom" type="checkbox">
77-
<div class="s-toggle-switch--indicator"></div>
78-
</div>
78+
</div>
79+
<div class="flex--item">
80+
<div class="d-flex ai-center jc-space-between gs8">
81+
<label class="flex--item s-label fs-body1 fw-normal" for="toggle-theme-custom">Custom theme</label>
82+
<div class="flex--item s-toggle-switch">
83+
<input id="toggle-theme-custom" type="checkbox">
84+
<div class="s-toggle-switch--indicator"></div>
7985
</div>
8086
</div>
81-
<div class="flex--item">
82-
<div class="d-flex ai-center jc-space-between gs8">
83-
<label class="flex--item s-label fs-body1 fw-normal js-highcontrast-btn" for="toggle-theme-highcontrast">High contrast mode</label>
84-
<div class="flex--item s-toggle-switch">
85-
<input id="toggle-theme-highcontrast" type="checkbox">
86-
<div class="s-toggle-switch--indicator"></div>
87-
</div>
87+
</div>
88+
<div class="flex--item">
89+
<div class="d-flex ai-center jc-space-between gs8">
90+
<label class="flex--item s-label fs-body1 fw-normal js-highcontrast-btn" for="toggle-theme-highcontrast">High contrast mode</label>
91+
<div class="flex--item s-toggle-switch">
92+
<input id="toggle-theme-highcontrast" type="checkbox">
93+
<div class="s-toggle-switch--indicator"></div>
8894
</div>
8995
</div>
9096
</div>

docs/assets/js/feature.topbar.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
$(".js-topbar-example .js-search-button").on("click", function() {
2+
$(this).closest(".s-topbar")
3+
.find(".s-topbar--searchbar")
4+
.toggleClass("s-topbar--searchbar__open");
5+
});
6+
7+
$(".js-topbar-example .s-topbar--menu-btn").on("click", function() {
8+
$(this).toggleClass("is-selected");
9+
});
10+
11+
$(".js-topbar-example .s-topbar--notice").on("click", function() {
12+
$(this).toggleClass("is-unread");
13+
});

docs/assets/js/hamburger.js

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,12 @@
11
$(document).ready(function() {
22
var navigation = $(".js-navigation");
3-
var closeIcon = $(".js-hamburger-close-icon");
4-
var hamburgerIcon = $(".js-hamburger-icon");
53
var hamburgerBtn = $(".js-hamburger-btn");
64

7-
hamburgerBtn.click(function(e) {
5+
hamburgerBtn.on("click", function(e) {
86
e.preventDefault();
97
e.stopPropagation();
108

11-
hamburgerIcon.toggleClass("d-none");
12-
closeIcon.toggleClass("d-none");
9+
hamburgerBtn.toggleClass("is-selected");
1310
navigation.toggleClass("md:d-none");
1411
});
1512
});

docs/assets/js/navigation.js

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
$(document).ready(function() {
22
// Cache some variables
33
var navigation = $(".js-navigation");
4-
var closeIcon = $(".js-hamburger-close-icon");
5-
var hamburgerIcon = $(".js-hamburger-icon");
4+
var hamburgerBtn = $(".js-hamburger-btn");
65

76
// Disable any empty links
87
$("a[href='#']").click(function(e) {
@@ -11,8 +10,7 @@ $(document).ready(function() {
1110

1211
function regenerateMenu () {
1312
// Hide the navigation if we've opened it
14-
hamburgerIcon.removeClass("d-none");
15-
closeIcon.addClass("d-none");
13+
hamburgerBtn.removeClass("is-selected");
1614
navigation.addClass("md:d-none");
1715
}
1816

docs/assets/js/search.js

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,28 +6,21 @@ $(document).ready(function() {
66
});
77

88
// Show or hide search
9-
var searchBar = $(".js-stacks-search-bar");
109
var searchContainer = $(".js-search");
1110
var searchCloseIcon = $(".js-search-close-icon");
1211
var searchIcon = $(".js-search-icon");
1312
var searchBtn = $(".js-search-btn");
14-
var hamburgerBtn = $(".js-hamburger-btn");
15-
var logo = $(".js-logo");
16-
var links = $(".js-links");
1713

1814
searchBtn.click(function(e) {
1915
e.preventDefault();
2016
e.stopPropagation();
2117

2218
searchIcon.toggleClass("d-none");
2319
searchCloseIcon.toggleClass("d-none");
24-
searchContainer.toggleClass("sm:d-none");
25-
hamburgerBtn.toggleClass("md:d-block");
26-
logo.toggleClass("sm:d-none");
27-
links.toggleClass("sm:d-none");
20+
searchContainer.toggleClass("s-topbar--searchbar__open");
2821

29-
if ( searchIcon.hasClass("d-none") ) {
30-
searchBar.focus();
22+
if (searchContainer.hasClass("s-topbar--searchbar__open") ) {
23+
searchContainer.find("input").focus();
3124
}
3225
});
3326
});

docs/assets/less/stacks-documentation.less

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@
3434
right: 9%;
3535
}
3636

37+
.stacks-topbar.s-topbar .s-topbar--content {
38+
--theme-topbar-item-color: var(--black-300);
39+
}
40+
3741
.stacks-home-nav {
3842
margin-bottom: 316px;
3943
}
@@ -397,14 +401,14 @@
397401
.stacks-theme-button {
398402
color: var(--black-300);
399403
position: relative;
400-
padding-right: @su12 !important;
404+
padding-right: @su24 !important;
401405

402406
&:after {
403407
content: "";
404408
position: absolute;
405409
z-index: @zi-active;
406410
top: calc(50% - 2px);
407-
right: 0;
411+
right: @su12 - @su2;
408412
border-style: solid;
409413
border-width: @su4;
410414
border-top-width: @su4;

0 commit comments

Comments
 (0)