Skip to content

Commit c3a1d56

Browse files
Add tabs/tab shortcodes (#380)
* Add tabs/tab shortcodes * Fix getstarted button * Remove documentation for now (see #382) --------- Co-authored-by: Jarrod Millman <jarrod.millman@gmail.com>
1 parent 7cbc4eb commit c3a1d56

6 files changed

Lines changed: 251 additions & 59 deletions

File tree

assets/js/tabs.js

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
/* From https://www.w3.org/WAI/ARIA/apg/patterns/tabs/examples/tabs-automatic/ */
2+
3+
/*
4+
* This content is licensed according to the W3C Software License at
5+
* https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document
6+
*
7+
* File: tabs-automatic.js
8+
*
9+
* Desc: Tablist widget that implements ARIA Authoring Practices
10+
*/
11+
12+
"use strict";
13+
14+
class TabsAutomatic {
15+
constructor(groupNode) {
16+
this.tablistNode = groupNode;
17+
18+
this.tabs = [];
19+
20+
this.firstTab = null;
21+
this.lastTab = null;
22+
23+
this.tabs = Array.from(this.tablistNode.querySelectorAll("[role=tab]"));
24+
this.tabpanels = [];
25+
26+
for (var i = 0; i < this.tabs.length; i += 1) {
27+
var tab = this.tabs[i];
28+
var tabpanel = document.getElementById(tab.getAttribute("aria-controls"));
29+
30+
tab.tabIndex = -1;
31+
tab.setAttribute("aria-selected", "false");
32+
this.tabpanels.push(tabpanel);
33+
34+
tab.addEventListener("keydown", this.onKeydown.bind(this));
35+
tab.addEventListener("click", this.onClick.bind(this));
36+
37+
if (!this.firstTab) {
38+
this.firstTab = tab;
39+
}
40+
this.lastTab = tab;
41+
}
42+
43+
this.setSelectedTab(this.firstTab, false);
44+
}
45+
46+
setSelectedTab(currentTab, setFocus) {
47+
if (typeof setFocus !== "boolean") {
48+
setFocus = true;
49+
}
50+
for (var i = 0; i < this.tabs.length; i += 1) {
51+
var tab = this.tabs[i];
52+
if (currentTab === tab) {
53+
tab.setAttribute("aria-selected", "true");
54+
tab.removeAttribute("tabindex");
55+
this.tabpanels[i].classList.remove("is-hidden");
56+
if (setFocus) {
57+
tab.focus();
58+
}
59+
} else {
60+
tab.setAttribute("aria-selected", "false");
61+
tab.tabIndex = -1;
62+
this.tabpanels[i].classList.add("is-hidden");
63+
}
64+
}
65+
}
66+
67+
setSelectedToPreviousTab(currentTab) {
68+
var index;
69+
70+
if (currentTab === this.firstTab) {
71+
this.setSelectedTab(this.lastTab);
72+
} else {
73+
index = this.tabs.indexOf(currentTab);
74+
this.setSelectedTab(this.tabs[index - 1]);
75+
}
76+
}
77+
78+
setSelectedToNextTab(currentTab) {
79+
var index;
80+
81+
if (currentTab === this.lastTab) {
82+
this.setSelectedTab(this.firstTab);
83+
} else {
84+
index = this.tabs.indexOf(currentTab);
85+
this.setSelectedTab(this.tabs[index + 1]);
86+
}
87+
}
88+
89+
/* EVENT HANDLERS */
90+
91+
onKeydown(event) {
92+
var tgt = event.currentTarget,
93+
flag = false;
94+
95+
switch (event.key) {
96+
case "ArrowLeft":
97+
this.setSelectedToPreviousTab(tgt);
98+
flag = true;
99+
break;
100+
101+
case "ArrowRight":
102+
this.setSelectedToNextTab(tgt);
103+
flag = true;
104+
break;
105+
106+
case "Home":
107+
this.setSelectedTab(this.firstTab);
108+
flag = true;
109+
break;
110+
111+
case "End":
112+
this.setSelectedTab(this.lastTab);
113+
flag = true;
114+
break;
115+
116+
default:
117+
break;
118+
}
119+
120+
if (flag) {
121+
event.stopPropagation();
122+
event.preventDefault();
123+
}
124+
}
125+
126+
onClick(event) {
127+
this.setSelectedTab(event.currentTarget);
128+
}
129+
}
130+
131+
// Initialize tablist
132+
133+
window.addEventListener("load", function () {
134+
var tablists = document.querySelectorAll("[role=tablist].automatic");
135+
for (var i = 0; i < tablists.length; i++) {
136+
new TabsAutomatic(tablists[i]);
137+
}
138+
});

assets/theme-css/bulma.css

Lines changed: 1 addition & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,6 @@
3939

4040
.button,
4141
.file,
42-
.tabs {
43-
-webkit-touch-callout: none;
44-
-webkit-user-select: none;
45-
-moz-user-select: none;
46-
-ms-user-select: none;
47-
user-select: none;
48-
}
4942
.navbar-link:not(.is-arrowless)::after,
5043
.select:not(.is-multiple):not(.is-loading)::after {
5144
border: 3px solid transparent;
@@ -70,7 +63,6 @@
7063
.message:not(:last-child),
7164
.progress:not(:last-child),
7265
.table:not(:last-child),
73-
.tabs:not(:last-child),
7466
.title:not(:last-child) {
7567
margin-bottom: 1.5rem;
7668
}
@@ -1233,55 +1225,6 @@ a.navbar-item:hover {
12331225
background-color: #fafafa;
12341226
}
12351227
}
1236-
.tabs {
1237-
-webkit-overflow-scrolling: touch;
1238-
align-items: stretch;
1239-
display: flex;
1240-
font-size: 1rem;
1241-
justify-content: space-between;
1242-
overflow: hidden;
1243-
overflow-x: auto;
1244-
white-space: nowrap;
1245-
}
1246-
.tabs a {
1247-
align-items: center;
1248-
border-bottom-color: #dbdbdb;
1249-
border-bottom-style: solid;
1250-
border-bottom-width: 1px;
1251-
color: #4a4a4a;
1252-
display: flex;
1253-
justify-content: center;
1254-
margin-bottom: -1px;
1255-
padding: 0.5em 1em;
1256-
vertical-align: top;
1257-
}
1258-
.tabs a:hover {
1259-
border-bottom-color: #363636;
1260-
color: #363636;
1261-
}
1262-
.tabs li {
1263-
display: block;
1264-
}
1265-
.tabs li.is-active a {
1266-
border-bottom-color: #485fc7;
1267-
color: #485fc7;
1268-
}
1269-
.tabs ul {
1270-
align-items: center;
1271-
border-bottom-color: #dbdbdb;
1272-
border-bottom-style: solid;
1273-
border-bottom-width: 1px;
1274-
display: flex;
1275-
flex-grow: 1;
1276-
flex-shrink: 0;
1277-
justify-content: flex-start;
1278-
}
1279-
.tabs .icon:first-child {
1280-
margin-right: 0.5em;
1281-
}
1282-
.tabs .icon:last-child {
1283-
margin-left: 0.5em;
1284-
}
12851228
.column {
12861229
display: block;
12871230
flex-basis: 0;
@@ -1332,7 +1275,7 @@ a.navbar-item:hover {
13321275
.hero .navbar {
13331276
background: 0 0;
13341277
}
1335-
.hero .tabs ul {
1278+
.hero ul {
13361279
border-bottom: none;
13371280
}
13381281
.section {

assets/theme-css/tabs.css

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
.tabs {
2+
display: block;
3+
padding-bottom: 1.5rem;
4+
}
5+
.tabs :is(h1, h2, h3, h4, h5, h6) {
6+
margin-top: 0.5rem;
7+
}
8+
9+
[role="tablist"] {
10+
display: inline-block;
11+
min-width: 100%;
12+
}
13+
14+
[role="tab"],
15+
[role="tab"]:focus,
16+
[role="tab"]:hover {
17+
display: inline-block;
18+
position: relative;
19+
z-index: 2;
20+
top: 1px;
21+
margin: 0;
22+
margin-top: 4px;
23+
padding: 2px 2px 4px;
24+
border: 1px solid hsl(219deg 1% 72%);
25+
border-bottom: 1px solid hsl(219deg 1% 72%);
26+
border-radius: 5px 5px 0 0;
27+
outline: none;
28+
font-weight: bold;
29+
max-width: 22%;
30+
overflow: hidden;
31+
text-align: left;
32+
cursor: pointer;
33+
}
34+
35+
[role="tab"][aria-selected="true"] {
36+
padding: 2px 2px 4px;
37+
margin-top: 0;
38+
border-width: 1px;
39+
border-top-width: 6px;
40+
border-top-color: rgb(36 116 214);
41+
border-bottom-color: hsl(220deg 43% 99%);
42+
background: hsl(220deg 43% 99%);
43+
}
44+
45+
[role="tab"][aria-selected="false"] {
46+
padding: 2px 2px 4px;
47+
border-bottom: 1px solid hsl(219deg 1% 72%);
48+
}
49+
50+
[role="tab"] span.focus {
51+
display: inline-block;
52+
margin: 2px;
53+
padding: 4px 6px;
54+
}
55+
56+
/* This makes the tab bounce around */
57+
/* [role="tab"]:hover span.focus, */
58+
/* [role="tab"]:focus span.focus, */
59+
/* [role="tab"]:active span.focus { */
60+
/* padding: 2px 2px 4px; */
61+
/* border: 1px solid rgb(36 116 214); */
62+
/* border-radius: 3px; */
63+
/* } */
64+
65+
[role="tabpanel"] {
66+
padding: 1rem;
67+
border: 1px solid hsl(219deg 1% 72%);
68+
border-radius: 0 5px 5px;
69+
min-height: 10em;
70+
width: 100%;
71+
overflow: auto;
72+
background: var(--pst-color-on-background);
73+
}
74+
75+
[role="tabpanel"].is-hidden {
76+
display: none;
77+
}
78+
79+
[role="tabpanel"] p {
80+
margin: 0;
81+
}

layouts/partials/hero.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
{{ end }}
2323
{{ if $buttonText }}
2424
<div class="hero-cta">
25-
<a href="{{ $buttonLink }}"><button class="cta-button button">{{ $buttonText }}</button></a>
25+
<a href="{{ $buttonLink }}"><button class="cta-button">{{ $buttonText }}</button></a>
2626
</div>
2727
{{ end }}
2828
</div>

layouts/shortcodes/tab.html

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{{- if .Parent -}}
2+
{{- $name := trim (.Get 0) " " -}}
3+
{{- if not (.Parent.Scratch.Get "tabs") -}}
4+
{{- .Parent.Scratch.Set "tabs" slice -}}
5+
{{- end -}}
6+
{{- with .Inner -}}
7+
{{- $.Parent.Scratch.Add "tabs" (dict "name" $name "content" . ) -}}
8+
{{- end -}}
9+
{{- else -}}
10+
{{- errorf "%q: tab shortcode missing its parent" .Page.Path -}}
11+
{{- end -}}

layouts/shortcodes/tabs.html

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<!-- Render inner tabs, which adds them to a scratch variable for this parent shortcode -->
2+
{{- .Inner -}}
3+
{{- $groupId := .Page.Scratch.Get "tabgroup" | default 0 -}}
4+
{{- .Page.Scratch.Set "tabgroup" (add $groupId 1) -}}
5+
<div class="tabs">
6+
<div role="tablist" class="automatic">
7+
{{- range $idx, $tab := .Scratch.Get "tabs" }}
8+
<button id="{{ $groupId }}-tab-{{ $idx }}" type="button" role="tab" aria-selected="{{ cond (eq $idx 0) "true" "false" }}" aria-controls="{{ $groupId }}-tabpanel-{{ $idx }}">
9+
<span class="focus">{{ .name }}</span>
10+
{{- end }}
11+
</div>
12+
{{- range $idx, $tab := .Scratch.Get "tabs" }}
13+
<div id="{{ $groupId }}-tabpanel-{{ $idx }}" role="tabpanel" tabindex="0" aria-labelledby="${{ $groupId }}-tab-{{ $idx }}">
14+
<p>
15+
{{- .content | markdownify -}}
16+
</p>
17+
</div>
18+
{{- end }}
19+
</div>

0 commit comments

Comments
 (0)