Skip to content

Commit 28ee5da

Browse files
committed
rustdoc: show crate name beside small logo
This commit changes the layout to something a bit less "look at my logo!!!111" gigantic, and makes it clearer where clicking the logo will actually take you. It also means the crate name is persistently at the top of the sidebar, even when in a sub-item page, and clicking that name takes you back to the root. | | Short crate name | Long crate name | |---------|------------------|-----------------| | Root | ![short-root] | ![long-root] | Subpage | ![short-subpage] | ![long-subpage] [short-root]: https://github.com/rust-lang/rust/assets/1593513/fe2ce102-d4b8-44e6-9f7b-68636a907f56 [short-subpage]: https://github.com/rust-lang/rust/assets/1593513/29501663-56c0-4151-b7de-d2637e167125 [long-root]: https://github.com/rust-lang/rust/assets/1593513/f6a385c0-b4c5-4a9c-954b-21b38de4192f [long-subpage]: https://github.com/rust-lang/rust/assets/1593513/97ec47b4-61bf-4ebe-b461-0d2187b8c6ca https://notriddle.com/rustdoc-html-demo-4/logo-lockup/image/index.html https://notriddle.com/rustdoc-html-demo-4/logo-lockup/crossbeam_channel/index.html https://notriddle.com/rustdoc-html-demo-4/logo-lockup/adler/struct.Adler32.html https://notriddle.com/rustdoc-html-demo-4/logo-lockup/crossbeam_channel/struct.Sender.html This improves visual information density (the construct with the logo and crate name is *shorter* than the logo on its own, because it's not square) and navigation clarity (we can now see what clicking the Rust logo does, specifically). Compare this with the layout at [Phoenix's Hexdocs] (which is what this proposal is closely based on), the old proposal on [Internals Discourse] (which always says "Rust standard library" in the sidebar, but doesn't do the side-by-side layout). [Phoenix's Hexdocs]: https://hexdocs.pm/phoenix/1.7.7/overview.html [Internals Discourse]: https://internals.rust-lang.org/t/poc-of-a-new-design-for-the-generated-rustdoc/11018 In newer versions of rustdoc, the crate name and version are always shown in the sidebar, even in subpages. Clicking the crate name does the same thing clicking the logo always did: return you to the crate root. While this actually takes up less screen real estate than the old layout on desktop, it takes up more HTML. It's also a bit more visually complex. I could do what the Internals POC did and keep the vertically stacked layout all the time, instead of doing a horizontal stack where possible. It would take up more screen real estate, though. This design is lifted almost verbatim from Hexdocs. It seems to work for them. [`opentelemetry_process_propagator`], for example, has a long application name. [`opentelemetry_process_propagator`]: https://hexdocs.pm/opentelemetry_process_propagator/OpentelemetryProcessPropagator.html Has anyone written the rationale on why the Rust logo shows up on projects that aren't the standard library? If we turned it off on non-standard crates by default, it would line wrap crate names a lot less often. Or maybe we should encourage crate authors to include their own logo more often? It certainly helps give people a better sense of "place." I'm not sure of anything that directly follows up this one. Plenty of other changes could be made to improve the layout, like * coming up with a less cluttered way to do disclosure (there's a lot of `[-]` on the page) * doing a better job of separating lateral navigation (vec::Vec links to vec::IntoIter) and the table of contents (vec::Vec links to vec::Vec::new) * giving readers more control of how much rustdoc hows them, and giving doc authors more control of how much it generates * better search that reduces the need to browse But those are mostly orthogonal, not future possibilities unlocked by this change.
1 parent 37fda98 commit 28ee5da

File tree

13 files changed

+95
-48
lines changed

13 files changed

+95
-48
lines changed

src/librustdoc/html/layout.rs

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ pub(crate) struct Layout {
1717
pub(crate) external_html: ExternalHtml,
1818
pub(crate) default_settings: FxHashMap<String, String>,
1919
pub(crate) krate: String,
20+
pub(crate) krate_version: String,
2021
/// The given user css file which allow to customize the generated
2122
/// documentation theme.
2223
pub(crate) css_file_extension: Option<PathBuf>,

src/librustdoc/html/render/context.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -534,6 +534,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
534534
external_html,
535535
default_settings,
536536
krate: krate.name(tcx).to_string(),
537+
krate_version: cache.crate_version.as_deref().unwrap_or_default().to_string(),
537538
css_file_extension: extension_css,
538539
scrape_examples_extension: !call_locations.is_empty(),
539540
};
@@ -669,10 +670,9 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
669670

670671
let blocks = sidebar_module_like(all.item_sections());
671672
let bar = Sidebar {
672-
title_prefix: "Crate ",
673-
title: crate_name.as_str(),
673+
title_prefix: "",
674+
title: "",
674675
is_crate: false,
675-
version: "",
676676
blocks: vec![blocks],
677677
path: String::new(),
678678
};

src/librustdoc/html/render/sidebar.rs

+3-6
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ pub(super) struct Sidebar<'a> {
1919
pub(super) title_prefix: &'static str,
2020
pub(super) title: &'a str,
2121
pub(super) is_crate: bool,
22-
pub(super) version: &'a str,
2322
pub(super) blocks: Vec<LinkBlock<'a>>,
2423
pub(super) path: String,
2524
}
@@ -99,12 +98,12 @@ pub(super) fn print_sidebar(cx: &Context<'_>, it: &clean::Item, buffer: &mut Buf
9998
|| it.is_primitive()
10099
|| it.is_union()
101100
|| it.is_enum()
102-
|| it.is_mod()
101+
// crate title is displayed as part of logo lockup
102+
|| (it.is_mod() && !it.is_crate())
103103
|| it.is_type_alias()
104104
{
105105
(
106106
match *it.kind {
107-
clean::ModuleItem(..) if it.is_crate() => "Crate ",
108107
clean::ModuleItem(..) => "Module ",
109108
_ => "",
110109
},
@@ -113,14 +112,12 @@ pub(super) fn print_sidebar(cx: &Context<'_>, it: &clean::Item, buffer: &mut Buf
113112
} else {
114113
("", "")
115114
};
116-
let version =
117-
if it.is_crate() { cx.cache().crate_version.as_deref().unwrap_or_default() } else { "" };
118115
let path: String = if !it.is_mod() {
119116
cx.current.iter().map(|s| s.as_str()).intersperse("::").collect()
120117
} else {
121118
"".into()
122119
};
123-
let sidebar = Sidebar { title_prefix, title, is_crate: it.is_crate(), version, blocks, path };
120+
let sidebar = Sidebar { title_prefix, title, is_crate: it.is_crate(), blocks, path };
124121
sidebar.render_into(buffer).unwrap();
125122
}
126123

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

+45-12
Original file line numberDiff line numberDiff line change
@@ -461,19 +461,9 @@ img {
461461
display: none !important;
462462
}
463463

464-
.sidebar .logo-container {
465-
margin-top: 10px;
466-
margin-bottom: 10px;
467-
text-align: center;
468-
}
469-
470-
.version {
471-
overflow-wrap: break-word;
472-
}
473-
474464
.logo-container > img {
475-
height: 100px;
476-
width: 100px;
465+
height: 48px;
466+
width: 48px;
477467
}
478468

479469
ul.block, .block li {
@@ -510,6 +500,8 @@ ul.block, .block li {
510500
color: var(--sidebar-link-color);
511501
}
512502
.sidebar .current,
503+
.sidebar .current a,
504+
.sidebar-crate a.logo-container:hover + h2 a,
513505
.sidebar a:hover:not(.logo-container) {
514506
background-color: var(--sidebar-current-link-background-color);
515507
}
@@ -524,6 +516,47 @@ ul.block, .block li {
524516
overflow: hidden;
525517
}
526518

519+
.sidebar-crate {
520+
display: flex;
521+
align-items: center;
522+
justify-content: center;
523+
margin: 0 32px;
524+
column-gap: 32px;
525+
flex-wrap: wrap;
526+
}
527+
528+
.sidebar-crate h2 {
529+
flex-grow: 1;
530+
/* This setup with the margins and row-gap is designed to make flex-wrap
531+
work the way we want. If they're in the side-by-side lockup, there
532+
should be a 16px margin to the left of the logo (visually the same as
533+
the 24px one on everything else, which are not giant circles) and 8px
534+
between it and the crate's name and version. When they're line wrapped,
535+
the logo needs to have the same margin on both sides of itself (to
536+
center properly) and the crate name and version need 24px on their
537+
left margin. */
538+
margin: 0 -8px;
539+
}
540+
541+
.sidebar-crate .logo-container {
542+
margin: 10px -16px;
543+
text-align: center;
544+
}
545+
546+
.sidebar-crate h2 a {
547+
display: block;
548+
margin-left: -0.25rem;
549+
padding-left: 0.25rem;
550+
margin-right: -24px;
551+
}
552+
553+
.sidebar-crate h2 .version {
554+
display: block;
555+
font-weight: normal;
556+
font-size: 1rem;
557+
overflow-wrap: break-word;
558+
}
559+
527560
.mobile-topbar {
528561
display: none;
529562
}

src/librustdoc/html/static/js/main.js

+6-2
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,13 @@ function setMobileTopbar() {
5151
// but with the current code it's hard to get the right information in the right place.
5252
const mobileTopbar = document.querySelector(".mobile-topbar");
5353
const locationTitle = document.querySelector(".sidebar h2.location");
54-
if (mobileTopbar && locationTitle) {
54+
if (mobileLocationTitle) {
5555
const mobileTitle = document.createElement("h2");
56-
mobileTitle.innerHTML = locationTitle.innerHTML;
56+
if (hasClass(document.body, "crate")) {
57+
mobileLocationTitle.innerText = `Crate ${window.currentCrate}`;
58+
} else if (locationTitle) {
59+
mobileLocationTitle.innerHTML = locationTitle.innerHTML;
60+
}
5761
mobileTopbar.appendChild(mobileTitle);
5862
}
5963
}

src/librustdoc/html/templates/page.html

+15-7
Original file line numberDiff line numberDiff line change
@@ -88,13 +88,21 @@
8888
{% endif %}
8989
<nav class="sidebar"> {# #}
9090
{% if page.css_class != "src" %}
91-
<a class="logo-container" href="{{page.root_path|safe}}{{krate_with_trailing_slash|safe}}index.html"> {# #}
92-
{% if !layout.logo.is_empty() %}
93-
<img src="{{layout.logo}}" alt="logo"> {# #}
94-
{% else %}
95-
<img class="rust-logo" src="{{static_root_path|safe}}{{files.rust_logo_svg}}" alt="logo"> {# #}
96-
{% endif %}
97-
</a> {# #}
91+
<div class="sidebar-crate">
92+
<a class="logo-container" href="{{page.root_path|safe}}{{krate_with_trailing_slash|safe}}index.html"> {# #}
93+
{% if !layout.logo.is_empty() %}
94+
<img src="{{layout.logo}}" alt="logo"> {# #}
95+
{% else %}
96+
<img class="rust-logo" src="{{static_root_path|safe}}{{files.rust_logo_svg}}" alt="logo"> {# #}
97+
{% endif %}
98+
</a> {# #}
99+
<h2> {# #}
100+
<a href="{{page.root_path|safe}}{{krate_with_trailing_slash|safe}}index.html">{{layout.krate}}</a> {# #}
101+
{% if !layout.krate_version.is_empty() %}
102+
<span class="version">{{+ layout.krate_version}}</span>
103+
{% endif %}
104+
</h2>
105+
</div>
98106
{% endif %}
99107
{{ sidebar|safe }}
100108
</nav> {# #}

src/librustdoc/html/templates/sidebar.html

-3
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,6 @@ <h2 class="location"> {# #}
66
<div class="sidebar-elems">
77
{% if is_crate %}
88
<ul class="block">
9-
{% if !version.is_empty() %}
10-
<li class="version">Version {{+ version}}</li>
11-
{% endif %}
129
<li><a id="all-types" href="all.html">All Items</a></li> {# #}
1310
</ul>
1411
{% endif %}

tests/rustdoc-gui/huge-logo.goml

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ go-to: "file://" + |DOC_PATH| + "/huge_logo/index.html"
44

55
set-window-size: (1280, 1024)
66
// offsetWidth = width of sidebar
7-
assert-property: (".sidebar .logo-container", {"offsetWidth": "200", "offsetHeight": 100})
8-
assert-property: (".sidebar .logo-container img", {"offsetWidth": "100", "offsetHeight": 100})
7+
assert-property: (".sidebar-crate .logo-container", {"offsetWidth": "48", "offsetHeight": 48})
8+
assert-property: (".sidebar-crate .logo-container img", {"offsetWidth": "48", "offsetHeight": 48})
99

1010
set-window-size: (400, 600)
1111
// offset = size + margin

tests/rustdoc-gui/sidebar-mobile.goml

+1-1
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ assert-position: ("#method\.must_use", {"y": 46})
5050
// Check that the bottom-most item on the sidebar menu can be scrolled fully into view.
5151
click: ".sidebar-menu-toggle"
5252
scroll-to: ".block.keyword li:nth-child(1)"
53-
compare-elements-position-near: (".block.keyword li:nth-child(1)", ".mobile-topbar", {"y": 543.19})
53+
compare-elements-position-near: (".block.keyword li:nth-child(1)", ".mobile-topbar", {"y": 544})
5454

5555
// Now checking the background color of the sidebar.
5656
show-text: true

tests/rustdoc-gui/sidebar.goml

+15-9
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,9 @@ set-local-storage: {"rustdoc-theme": "light"}
5050
// We reload the page so the local storage settings are being used.
5151
reload:
5252

53-
assert-text: (".sidebar > .location", "Crate test_docs")
54-
// In modules, we only have one "location" element.
55-
assert-count: (".sidebar .location", 1)
53+
assert-text: (".sidebar > .sidebar-crate > h2 > a", "test_docs")
54+
// Crate root has no "location" element
55+
assert-count: (".sidebar .location", 0)
5656
assert-count: (".sidebar h2", 1)
5757
assert-text: ("#all-types", "All Items")
5858
assert-css: ("#all-types", {"color": "#356da4"})
@@ -74,8 +74,9 @@ assert-text: ("#structs + .item-table .item-name > a", "Foo")
7474
click: "#structs + .item-table .item-name > a"
7575

7676
// PAGE: struct.Foo.html
77+
assert-count: (".sidebar .sidebar-crate", 1)
7778
assert-count: (".sidebar .location", 1)
78-
assert-count: (".sidebar h2", 2)
79+
assert-count: (".sidebar h2", 3)
7980
// We check that there is no crate listed outside of the top level.
8081
assert-false: ".sidebar-elems > .crate"
8182

@@ -94,7 +95,8 @@ click: ".sidebar-elems ul.crate > li:first-child > a"
9495
// PAGE: lib2/index.html
9596
go-to: "file://" + |DOC_PATH| + "/lib2/index.html"
9697
assert-property: (".sidebar", {"clientWidth": "200"})
97-
assert-text: (".sidebar > .location", "Crate lib2")
98+
assert-text: (".sidebar > .sidebar-crate > h2 > a", "lib2")
99+
assert-count: (".sidebar .location", 0)
98100
// We check that we have the crates list and that the "current" on is now "lib2".
99101
assert-text: (".sidebar-elems ul.crate > li > a.current", "lib2")
100102
// We now go to the "foobar" function page.
@@ -108,21 +110,25 @@ click: "#functions + .item-table .item-name > a"
108110

109111
// PAGE: fn.foobar.html
110112
// In items containing no items (like functions or constants) and in modules, we have no
111-
// "location" elements. Only the parent module h2.
113+
// "location" elements. Only the parent module h2 and crate.
114+
assert-text: (".sidebar > .sidebar-crate > h2 > a", "lib2")
112115
assert-count: (".sidebar .location", 0)
113-
assert-count: (".sidebar h2", 1)
116+
assert-count: (".sidebar h2", 2)
114117
assert-text: (".sidebar .sidebar-elems h2", "In lib2")
115118
// We check that we don't have the crate list.
116119
assert-false: ".sidebar-elems > .crate"
117120

118121
go-to: "./module/index.html"
119122
assert-property: (".sidebar", {"clientWidth": "200"})
123+
assert-text: (".sidebar > .sidebar-crate > h2 > a", "lib2")
120124
assert-text: (".sidebar > .location", "Module module")
125+
assert-count: (".sidebar .location", 1)
121126
// We check that we don't have the crate list.
122127
assert-false: ".sidebar-elems > .crate"
123128

124129
go-to: "./sub_module/sub_sub_module/index.html"
125130
assert-property: (".sidebar", {"clientWidth": "200"})
131+
assert-text: (".sidebar > .sidebar-crate > h2 > a", "lib2")
126132
assert-text: (".sidebar > .location", "Module sub_sub_module")
127133
// We check that we don't have the crate list.
128134
assert-false: ".sidebar-elems .crate"
@@ -152,14 +158,14 @@ assert-property: (".sidebar", {"clientWidth": "200"})
152158

153159
// Checks that all.html and index.html have their sidebar link in the same place.
154160
go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
155-
store-property: (".sidebar .location a", {
161+
store-property: (".sidebar .sidebar-crate h2 a", {
156162
"clientWidth": index_sidebar_width,
157163
"clientHeight": index_sidebar_height,
158164
"offsetTop": index_sidebar_y,
159165
"offsetLeft": index_sidebar_x,
160166
})
161167
go-to: "file://" + |DOC_PATH| + "/test_docs/all.html"
162-
assert-property: (".sidebar .location a", {
168+
assert-property: (".sidebar .sidebar-crate h2 a", {
163169
"clientWidth": |index_sidebar_width|,
164170
"clientHeight": |index_sidebar_height|,
165171
"offsetTop": |index_sidebar_y|,

tests/rustdoc/crate-version-escape.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22

33
#![crate_name = "foo"]
44

5-
// @has 'foo/index.html' '//li[@class="version"]' 'Version <script>alert("hi")</script>'
5+
// @has 'foo/index.html' '//*[@class="version"]' '<script>alert("hi")</script>'

tests/rustdoc/crate-version.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
// compile-flags: --crate-version=1.3.37
22

3-
// @has 'crate_version/index.html' '//*[@class="version"]' 'Version 1.3.37'
3+
// @has 'crate_version/index.html' '//*[@class="version"]' '1.3.37'

tests/rustdoc/titles.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
#![feature(rustc_attrs)]
33

44
// @matches 'foo/index.html' '//h1' 'Crate foo'
5-
// @matches 'foo/index.html' '//h2[@class="location"]' 'Crate foo'
5+
// @matches 'foo/index.html' '//div[@class="sidebar-crate"]/h2/a' 'foo'
6+
// @count 'foo/index.html' '//h2[@class="location"]' 0
67

78
// @matches 'foo/foo_mod/index.html' '//h1' 'Module foo::foo_mod'
89
// @matches 'foo/foo_mod/index.html' '//h2[@class="location"]' 'Module foo_mod'

0 commit comments

Comments
 (0)