Skip to content

Commit 2852443

Browse files
committed
rustdoc: use <details> tag for the source code sidebar
This fixes the extremely poor accessibility of the old system, making it possible to navigate the sidebar by keyboard, and also implicitly gives the sidebar items the correct ARIA roles.
1 parent 5b9775f commit 2852443

File tree

7 files changed

+53
-82
lines changed

7 files changed

+53
-82
lines changed

src/librustdoc/html/static/css/rustdoc.css

+8-27
Original file line numberDiff line numberDiff line change
@@ -1528,38 +1528,19 @@ kbd {
15281528
margin-bottom: 1em;
15291529
}
15301530

1531-
div.children {
1532-
padding-left: 27px;
1533-
display: none;
1534-
}
1535-
div.name {
1531+
details.dir-entry > summary {
1532+
margin: 0 0 0 13px;
1533+
list-style-position: outside;
15361534
cursor: pointer;
1537-
position: relative;
1538-
margin-left: 16px;
15391535
}
1540-
div.files > a {
1541-
display: block;
1542-
padding: 0 3px;
1543-
}
1544-
div.files > a:hover, div.name:hover {
1545-
background-color: #a14b4b;
1536+
1537+
details.dir-entry div.folders, details.dir-entry div.files {
1538+
padding-left: 27px;
15461539
}
1547-
div.name.expand + .children {
1540+
1541+
details.dir-entry a {
15481542
display: block;
15491543
}
1550-
div.name::before {
1551-
content: "\25B6";
1552-
padding-left: 4px;
1553-
font-size: 0.625rem;
1554-
position: absolute;
1555-
left: -16px;
1556-
top: 4px;
1557-
}
1558-
div.name.expand::before {
1559-
transform: rotate(90deg);
1560-
left: -15px;
1561-
top: 2px;
1562-
}
15631544

15641545
/* The hideme class is used on summary tags that contain a span with
15651546
placeholder text shown only when the toggle is closed. For instance,

src/librustdoc/html/static/css/themes/ayu.css

+2-1
Original file line numberDiff line numberDiff line change
@@ -630,7 +630,8 @@ kbd {
630630
color: #fff;
631631
border-bottom-color: #5c6773;
632632
}
633-
#source-sidebar div.files > a:hover, div.name:hover {
633+
#source-sidebar div.files > a:hover, details.dir-entry summary:hover,
634+
#source-sidebar div.files > a:focus, details.dir-entry summary:focus {
634635
background-color: #14191f;
635636
color: #ffb44c;
636637
}

src/librustdoc/html/static/css/themes/dark.css

+2-1
Original file line numberDiff line numberDiff line change
@@ -500,7 +500,8 @@ kbd {
500500
#source-sidebar > .title {
501501
border-bottom-color: #ccc;
502502
}
503-
#source-sidebar div.files > a:hover, div.name:hover {
503+
#source-sidebar div.files > a:hover, details.dir-entry summary:hover,
504+
#source-sidebar div.files > a:focus, details.dir-entry summary:focus {
504505
background-color: #444;
505506
}
506507
#source-sidebar div.files > .selected {

src/librustdoc/html/static/css/themes/light.css

+2-1
Original file line numberDiff line numberDiff line change
@@ -484,7 +484,8 @@ kbd {
484484
#source-sidebar > .title {
485485
border-bottom-color: #ccc;
486486
}
487-
#source-sidebar div.files > a:hover, div.name:hover {
487+
#source-sidebar div.files > a:hover, details.dir-entry summary:hover,
488+
#source-sidebar div.files > a:focus, details.dir-entry summary:focus {
488489
background-color: #E0E0E0;
489490
}
490491
#source-sidebar div.files > .selected {

src/librustdoc/html/static/js/source-script.js

+11-18
Original file line numberDiff line numberDiff line change
@@ -13,33 +13,27 @@ const rootPath = document.getElementById("rustdoc-vars").attributes["data-root-p
1313
let oldScrollPosition = 0;
1414

1515
function createDirEntry(elem, parent, fullPath, hasFoundFile) {
16-
const name = document.createElement("div");
17-
name.className = "name";
16+
const dirEntry = document.createElement("details");
17+
const summary = document.createElement("summary");
18+
19+
dirEntry.className = "dir-entry";
1820

1921
fullPath += elem["name"] + "/";
2022

21-
name.onclick = ev => {
22-
if (hasClass(ev.target, "expand")) {
23-
removeClass(ev.target, "expand");
24-
} else {
25-
addClass(ev.target, "expand");
26-
}
27-
};
28-
name.innerText = elem["name"];
23+
summary.innerText = elem["name"];
24+
dirEntry.appendChild(summary);
2925

30-
const children = document.createElement("div");
31-
children.className = "children";
3226
const folders = document.createElement("div");
3327
folders.className = "folders";
3428
if (elem.dirs) {
3529
for (const dir of elem.dirs) {
3630
if (createDirEntry(dir, folders, fullPath, hasFoundFile)) {
37-
addClass(name, "expand");
31+
dirEntry.open = true;
3832
hasFoundFile = true;
3933
}
4034
}
4135
}
42-
children.appendChild(folders);
36+
dirEntry.appendChild(folders);
4337

4438
const files = document.createElement("div");
4539
files.className = "files";
@@ -51,15 +45,14 @@ function createDirEntry(elem, parent, fullPath, hasFoundFile) {
5145
const w = window.location.href.split("#")[0];
5246
if (!hasFoundFile && w === file.href) {
5347
file.className = "selected";
54-
addClass(name, "expand");
48+
dirEntry.open = true;
5549
hasFoundFile = true;
5650
}
5751
files.appendChild(file);
5852
}
5953
}
60-
children.appendChild(files);
61-
parent.appendChild(name);
62-
parent.appendChild(children);
54+
dirEntry.appendChild(files);
55+
parent.appendChild(dirEntry);
6356
return hasFoundFile;
6457
}
6558

src/test/rustdoc-gui/sidebar-source-code-display.goml

+21-21
Original file line numberDiff line numberDiff line change
@@ -27,29 +27,29 @@ reload:
2727
// Waiting for the sidebar to be displayed...
2828
wait-for-css: ("#sidebar-toggle", {"visibility": "visible", "opacity": 1})
2929
assert-css: (
30-
"#source-sidebar .expand + .children a.selected",
30+
"#source-sidebar details[open] > .files a.selected",
3131
{"color": "rgb(0, 0, 0)", "background-color": "rgb(255, 255, 255)"},
3232
)
3333
// Without hover.
3434
assert-css: (
35-
"#source-sidebar .expand + .children > .files a:not(.selected)",
35+
"#source-sidebar details[open] > .files a:not(.selected)",
3636
{"color": "rgb(0, 0, 0)", "background-color": "rgba(0, 0, 0, 0)"},
3737
)
3838
// With hover.
39-
move-cursor-to: "#source-sidebar .expand + .children > .files a:not(.selected)"
39+
move-cursor-to: "#source-sidebar details[open] > .files a:not(.selected)"
4040
assert-css: (
41-
"#source-sidebar .expand + .children > .files a:not(.selected)",
41+
"#source-sidebar details[open] > .files a:not(.selected)",
4242
{"color": "rgb(0, 0, 0)", "background-color": "rgb(224, 224, 224)"},
4343
)
4444
// Without hover.
4545
assert-css: (
46-
"#source-sidebar .expand + .children .folders .name",
46+
"#source-sidebar details[open] > .folders > details > summary",
4747
{"color": "rgb(0, 0, 0)", "background-color": "rgba(0, 0, 0, 0)"},
4848
)
4949
// With hover.
50-
move-cursor-to: "#source-sidebar .expand + .children .folders .name"
50+
move-cursor-to: "#source-sidebar details[open] > .folders > details > summary"
5151
assert-css: (
52-
"#source-sidebar .expand + .children .folders .name",
52+
"#source-sidebar details[open] > .folders > details > summary",
5353
{"color": "rgb(0, 0, 0)", "background-color": "rgb(224, 224, 224)"},
5454
)
5555

@@ -59,29 +59,29 @@ reload:
5959
// Waiting for the sidebar to be displayed...
6060
wait-for-css: ("#sidebar-toggle", {"visibility": "visible", "opacity": 1})
6161
assert-css: (
62-
"#source-sidebar .expand + .children a.selected",
62+
"#source-sidebar details[open] > .files > a.selected",
6363
{"color": "rgb(221, 221, 221)", "background-color": "rgb(51, 51, 51)"},
6464
)
6565
// Without hover.
6666
assert-css: (
67-
"#source-sidebar .expand + .children > .files a:not(.selected)",
67+
"#source-sidebar details[open] > .files > a:not(.selected)",
6868
{"color": "rgb(221, 221, 221)", "background-color": "rgba(0, 0, 0, 0)"},
6969
)
7070
// With hover.
71-
move-cursor-to: "#source-sidebar .expand + .children > .files a:not(.selected)"
71+
move-cursor-to: "#source-sidebar details[open] > .files a:not(.selected)"
7272
assert-css: (
73-
"#source-sidebar .expand + .children > .files a:not(.selected)",
73+
"#source-sidebar details[open] > .files a:not(.selected)",
7474
{"color": "rgb(221, 221, 221)", "background-color": "rgb(68, 68, 68)"},
7575
)
7676
// Without hover.
7777
assert-css: (
78-
"#source-sidebar .expand + .children .folders .name",
78+
"#source-sidebar details[open] > .folders > details > summary",
7979
{"color": "rgb(221, 221, 221)", "background-color": "rgba(0, 0, 0, 0)"},
8080
)
8181
// With hover.
82-
move-cursor-to: "#source-sidebar .expand + .children .folders .name"
82+
move-cursor-to: "#source-sidebar details[open] > .folders > details > summary"
8383
assert-css: (
84-
"#source-sidebar .expand + .children .folders .name",
84+
"#source-sidebar details[open] > .folders > details > summary",
8585
{"color": "rgb(221, 221, 221)", "background-color": "rgb(68, 68, 68)"},
8686
)
8787

@@ -91,29 +91,29 @@ reload:
9191
// Waiting for the sidebar to be displayed...
9292
wait-for-css: ("#sidebar-toggle", {"visibility": "visible", "opacity": 1})
9393
assert-css: (
94-
"#source-sidebar .expand + .children a.selected",
94+
"#source-sidebar details[open] > .files a.selected",
9595
{"color": "rgb(255, 180, 76)", "background-color": "rgb(20, 25, 31)"},
9696
)
9797
// Without hover.
9898
assert-css: (
99-
"#source-sidebar .expand + .children > .files a:not(.selected)",
99+
"#source-sidebar details[open] > .files a:not(.selected)",
100100
{"color": "rgb(197, 197, 197)", "background-color": "rgba(0, 0, 0, 0)"},
101101
)
102102
// With hover.
103-
move-cursor-to: "#source-sidebar .expand + .children > .files a:not(.selected)"
103+
move-cursor-to: "#source-sidebar details[open] > .files a:not(.selected)"
104104
assert-css: (
105-
"#source-sidebar .expand + .children > .files a:not(.selected)",
105+
"#source-sidebar details[open] > .files a:not(.selected)",
106106
{"color": "rgb(255, 180, 76)", "background-color": "rgb(20, 25, 31)"},
107107
)
108108
// Without hover.
109109
assert-css: (
110-
"#source-sidebar .expand + .children .folders .name",
110+
"#source-sidebar details[open] > .folders > details > summary",
111111
{"color": "rgb(197, 197, 197)", "background-color": "rgba(0, 0, 0, 0)"},
112112
)
113113
// With hover.
114-
move-cursor-to: "#source-sidebar .expand + .children .folders .name"
114+
move-cursor-to: "#source-sidebar details[open] > .folders > details > summary"
115115
assert-css: (
116-
"#source-sidebar .expand + .children .folders .name",
116+
"#source-sidebar details[open] > .folders > details > summary",
117117
{"color": "rgb(255, 180, 76)", "background-color": "rgb(20, 25, 31)"},
118118
)
119119

src/test/rustdoc-gui/source-code-page.goml

+7-13
Original file line numberDiff line numberDiff line change
@@ -34,19 +34,13 @@ assert-document-property: ({"URL": "/lib.rs.html"}, ENDS_WITH)
3434
click: "#sidebar-toggle"
3535
assert: ".source-sidebar-expanded"
3636

37-
// We check that the first entry of the sidebar is collapsed (which, for whatever reason,
38-
// is number 2 and not 1...).
39-
assert-attribute: ("#source-sidebar .name:nth-child(2)", {"class": "name"})
40-
assert-text: ("#source-sidebar .name:nth-child(2)", "implementors")
41-
// We also check its children are hidden too.
42-
assert-css: ("#source-sidebar .name:nth-child(2) + .children", {"display": "none"})
37+
// We check that the first entry of the sidebar is collapsed
38+
assert-property: ("#source-sidebar details:first-of-type", {"open": "false"})
39+
assert-text: ("#source-sidebar details:first-of-type > summary", "implementors")
4340
// We now click on it.
44-
click: "#source-sidebar .name:nth-child(2)"
45-
assert-attribute: ("#source-sidebar .name:nth-child(2)", {"class": "name expand"})
46-
// Checking that its children are displayed as well.
47-
assert-css: ("#source-sidebar .name:nth-child(2) + .children", {"display": "block"})
41+
click: "#source-sidebar details:first-of-type > summary"
42+
assert-property: ("#source-sidebar details:first-of-type", {"open": "true"})
4843

4944
// And now we collapse it again.
50-
click: "#source-sidebar .name:nth-child(2)"
51-
assert-attribute: ("#source-sidebar .name:nth-child(2)", {"class": "name"})
52-
assert-css: ("#source-sidebar .name:nth-child(2) + .children", {"display": "none"})
45+
click: "#source-sidebar details:first-of-type > summary"
46+
assert-property: ("#source-sidebar details:first-of-type", {"open": "false"})

0 commit comments

Comments
 (0)