From 6a5f8b1aef1417d7dc85b5d0a229d2db1930eb7c Mon Sep 17 00:00:00 2001 From: Jacob Hoffman-Andrews Date: Thu, 6 Jan 2022 19:48:24 -0500 Subject: [PATCH] Simplify and unify rustdoc sidebar styles This switches to just use size, weight, and spacing to distinguish headings in the sidebar. We no longer use boxes, horizontal bars, or centering to distinguish headings. This makes it much easier to understand the hierarchy of headings, and reduces visual noise. I also refactored how the mobile topbar works. Previously, we tried to shift around elements from the sidebar to make the topbar. Now, the topbar gets its own elements, which can be styled on their own. This makes styling and reasoning about those elements simpler. Because the heading font sizes are bigger, increase the sidebar width slightly. As a very minor change, removed version from the "All types" page. It's now only on the crate page. --- src/librustdoc/html/render/context.rs | 12 +- src/librustdoc/html/render/mod.rs | 31 +- src/librustdoc/html/static/css/rustdoc.css | 304 +++++++----------- src/librustdoc/html/static/css/themes/ayu.css | 27 +- .../html/static/css/themes/dark.css | 27 +- .../html/static/css/themes/light.css | 27 +- src/librustdoc/html/static/js/main.js | 60 ++-- .../html/static/js/source-script.js | 2 +- src/librustdoc/templates/page.html | 14 +- src/test/rustdoc-gui/anchors.goml | 2 +- .../rustdoc-gui/code-blocks-overflow.goml | 2 +- .../rustdoc-gui/docblock-table-overflow.goml | 4 +- src/test/rustdoc-gui/headings.goml | 2 +- src/test/rustdoc-gui/item-info-width.goml | 2 +- .../rustdoc-gui/search-result-display.goml | 2 +- src/test/rustdoc-gui/sidebar-mobile.goml | 23 +- src/test/rustdoc-gui/sidebar-source-code.goml | 4 + src/test/rustdoc-gui/sidebar.goml | 16 +- .../rustdoc-gui/type-declation-overflow.goml | 11 +- src/test/rustdoc/cap-lints.rs | 2 +- src/test/rustdoc/crate-version-escape.rs | 3 +- src/test/rustdoc/crate-version.rs | 2 +- src/test/rustdoc/titles.rs | 7 +- src/test/rustdoc/typedef.rs | 2 +- 24 files changed, 228 insertions(+), 360 deletions(-) diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs index 865e14f694dcc..2455d56bd2b3f 100644 --- a/src/librustdoc/html/render/context.rs +++ b/src/librustdoc/html/render/context.rs @@ -554,16 +554,8 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { extra_scripts: &[], static_extra_scripts: &[], }; - let sidebar = if let Some(ref version) = self.shared.cache.crate_version { - format!( - "

Crate {}

\ -
\ -

Version {}

\ -
\ -

Back to index

", - crate_name, - Escape(version), - ) + let sidebar = if self.shared.cache.crate_version.is_some() { + format!("

Crate {}

", crate_name) } else { String::new() }; diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index a79b3e8ed084b..29a793f3110b1 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -1744,13 +1744,6 @@ fn print_sidebar(cx: &Context<'_>, it: &clean::Item, buffer: &mut Buffer) { buffer, "

{}{}

", match *it.kind { - clean::StructItem(..) => "Struct ", - clean::TraitItem(..) => "Trait ", - clean::PrimitiveItem(..) => "Primitive Type ", - clean::UnionItem(..) => "Union ", - clean::EnumItem(..) => "Enum ", - clean::TypedefItem(..) => "Type Definition ", - clean::ForeignTypeItem => "Foreign Type ", clean::ModuleItem(..) => if it.is_crate() { "Crate " @@ -1763,26 +1756,14 @@ fn print_sidebar(cx: &Context<'_>, it: &clean::Item, buffer: &mut Buffer) { ); } + buffer.write_str("
"); if it.is_crate() { + write!(buffer, "
    "); if let Some(ref version) = cx.cache().crate_version { - write!( - buffer, - "
    \ -
    \ -

    Version {}

    \ -
    ", - Escape(version), - ); + write!(buffer, "
  • Version {}
  • ", Escape(version)); } - } - - buffer.write_str("
    "); - if it.is_crate() { - write!( - buffer, - "

    See all {}'s items

    ", - it.name.as_ref().expect("crates always have a name"), - ); + write!(buffer, "
  • All Items
  • "); + buffer.write_str("
"); } match *it.kind { @@ -1806,7 +1787,7 @@ fn print_sidebar(cx: &Context<'_>, it: &clean::Item, buffer: &mut Buffer) { // to navigate the documentation (though slightly inefficiently). if !it.is_mod() { - buffer.write_str("

Other items in
"); + buffer.write_str("

In "); for (i, name) in cx.current.iter().take(parentlen).enumerate() { if i > 0 { buffer.write_str("::"); diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index bfdce74de67da..dbc068ce6b13b 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -169,8 +169,7 @@ h1.fqn { section hierarchies. */ h2, .top-doc h3, -.top-doc h4, -.sidebar .others h3 { +.top-doc h4 { border-bottom: 1px solid; } h3.code-header { @@ -206,7 +205,11 @@ div.impl-items > div { } h1, h2, h3, h4, h5, h6, -.sidebar, a.source, .search-input, .search-results .result-name, +.sidebar, +.mobile-topbar, +a.source, +.search-input, +.search-results .result-name, .content table td:first-child > a, .item-left > a, .out-of-band, @@ -261,6 +264,11 @@ textarea { margin: 0; } +button { + /* Buttons on Safari have different default padding than other platforms. Make them the same. */ + padding: 1px 6px; +} + /* end tweaks for normalize.css 8 */ .rustdoc { @@ -359,15 +367,25 @@ nav.sub { } .sidebar { - width: 200px; + font-size: 0.9rem; + width: 250px; + min-width: 200px; overflow-y: scroll; position: sticky; - min-width: 200px; height: 100vh; top: 0; left: 0; } +.sidebar-elems, +.sidebar > .location { + padding-left: 24px; +} + +.sidebar .location { + overflow-wrap: anywhere; +} + .rustdoc.source .sidebar { width: 50px; min-width: 0px; @@ -396,6 +414,10 @@ nav.sub { visibility: visible; } +#all-types { + margin-top: 1em; +} + /* Improve the scrollbar display on firefox */ * { scrollbar-width: initial; @@ -415,48 +437,28 @@ nav.sub { -webkit-box-shadow: inset 0; } -.sidebar .block > ul > li { - margin-right: -10px; -} - /* Everything else */ .hidden { display: none !important; } -.logo-container { +.sidebar .logo-container { display: flex; margin-top: 10px; margin-bottom: 10px; justify-content: center; } +.version { + overflow-wrap: break-word; +} + .logo-container > img { height: 100px; width: 100px; } -.sidebar .location { - border: 1px solid; - font-size: 1.0625rem; - margin: 30px 10px 20px 10px; - text-align: center; - word-wrap: break-word; - font-weight: inherit; - padding: 0; -} - -.sidebar .version { - font-size: 0.9375rem; - text-align: center; - border-bottom: 1px solid; - overflow-wrap: break-word; - overflow-wrap: anywhere; - word-wrap: break-word; /* deprecated */ - word-break: break-word; /* Chrome, non-standard */ -} - .location:empty { border: none; } @@ -470,48 +472,45 @@ nav.sub { .block { padding: 0; - margin-bottom: 14px; -} -.block h2, .block h3 { - text-align: center; } .block ul, .block li { - margin: 0 10px; padding: 0; list-style: none; } .block a { display: block; + padding: 0.3em; + margin-left: -0.3em; + text-overflow: ellipsis; overflow: hidden; - line-height: 15px; - padding: 7px 5px; - font-size: 0.875rem; - font-weight: 300; - transition: border 500ms ease-out; } -.sidebar-title { - border-top: 1px solid; - border-bottom: 1px solid; - text-align: center; - font-size: 1.0625rem; - margin-bottom: 5px; - font-weight: inherit; +.sidebar h2 { + border-bottom: none; + font-weight: 500; padding: 0; + margin: 0; + margin-top: 1rem; + margin-bottom: 1rem; } -.sidebar-links { - margin-bottom: 15px; +.sidebar h3 { + font-size: 1.1rem; + font-weight: 500; + padding: 0; + margin: 0; + margin-top: 0.5rem; + margin-bottom: 0.25rem; } -.sidebar-links > a { - padding-left: 10px; - width: 100%; +.sidebar-links, +.block { + margin-bottom: 2em; } -.sidebar-menu { +.mobile-topbar { display: none; } @@ -784,13 +783,12 @@ nav.sub { margin-top: 0; } -nav:not(.sidebar) { +nav.sub { flex-grow: 1; - border-bottom: 1px solid; padding-bottom: 10px; margin-bottom: 25px; } -.source nav:not(.sidebar).sub { +.source nav.sub { margin-left: 32px; } nav.main { @@ -1395,18 +1393,6 @@ pre.rust { margin-left: 5px; } -#all-types { - text-align: center; - border: 1px solid; - margin: 0 10px; - margin-bottom: 10px; - display: block; - border-radius: 7px; -} -#all-types > p { - margin: 5px 0; -} - #sidebar-toggle { position: sticky; top: 0; @@ -1752,8 +1738,12 @@ details.rustdoc-toggle[open] > summary.hideme::after { } @media (max-width: 700px) { - body { + .rustdoc { padding-top: 0px; + /* Sidebar should overlay main content, rather than pushing main content to the right. + Turn off `display: flex` on the body element. */ + display: block; + scroll-margin-top: 45px; } main { @@ -1761,134 +1751,110 @@ details.rustdoc-toggle[open] > summary.hideme::after { padding-top: 0px; } - /* Space is at a premium on mobile, so remove the theme-picker icon. */ - #theme-picker { - display: none; - width: 0; - } - .rustdoc { flex-direction: column; } - .rustdoc:not(.source) > .sidebar { - width: 100%; - height: 45px; - min-height: 40px; - max-height: 45px; - margin: 0; - padding: 0 15px; - position: static; - z-index: 11; - overflow-y: hidden; + /* Hide the logo and item name from the sidebar. Those are displayed + in the mobile-topbar instead. */ + .sidebar .sidebar-logo, + .sidebar .location { + display: none; } - .rustdoc.source > .sidebar { + .sidebar-elems { + margin-top: 1em; + } + + .sidebar { position: fixed; - top: 0; - left: 0; + top: 45px; + /* Hide the sidebar offscreen while not in use. Doing this instead of display: none means + the sidebar stays visible for screen readers, which is useful for navigation. */ + left: -1000px; + margin-left: 0; + background-color: rgba(0,0,0,0); margin: 0; + padding: 0; + padding-left: 15px; z-index: 11; - width: 0; } - .sidebar.mobile { - position: sticky !important; + /* The source view uses a different design for the sidebar toggle, and doesn't have a topbar, + so don't bump down the main content or the sidebar. */ + .source main, + .source .sidebar { top: 0; + padding: 0; + } + + .sidebar.shown, + .sidebar.expanded, + .sidebar:focus-within { left: 0; - width: 100%; - margin-left: 0; - background-color: rgba(0,0,0,0); } - .sidebar > .location { - float: right; - margin: 0px; - margin-top: 2px; - padding: 3px 10px 1px 10px; - min-height: 39px; - background: inherit; - text-align: left; - font-size: 1.5rem; + .rustdoc.source > .sidebar { + position: fixed; + margin: 0; + z-index: 11; + width: 0; } - .sidebar .location:empty { - padding: 0; + .mobile-topbar .location { + border: none; + margin: 0; + margin-left: auto; + padding: 0.3em; + padding-right: 0.6em; + text-overflow: ellipsis; + overflow-x: hidden; } - .rustdoc:not(.source) .sidebar .logo-container { - width: 35px; - height: 35px; - margin-top: 5px; - margin-bottom: 5px; - float: left; - margin-left: 50px; + .mobile-topbar .logo-container { + max-height: 45px; } - .sidebar .logo-container > img { + .mobile-topbar .logo-container > img { max-width: 35px; max-height: 35px; + margin-left: 20px; + margin-top: 5px; + margin-bottom: 5px; } - .sidebar-menu { - position: fixed; + .mobile-topbar { + display: flex; + flex-direction: row; + position: sticky; z-index: 10; font-size: 2rem; cursor: pointer; - width: 45px; + height: 45px; + width: 100%; left: 0; top: 0; - text-align: center; - display: block; - border-bottom: 1px solid; - border-right: 1px solid; - height: 45px; } - .rustdoc.source > .sidebar > .sidebar-menu { + .source .mobile-topbar { display: none; } - /* We do NOT hide this element so that alternative device readers still have this information - available. */ - .sidebar-elems { - position: fixed; - z-index: 1; - top: 45px; - bottom: 0; - width: 246px; - /* We move the sidebar to the left by its own width so it doesn't appear. */ - left: -246px; - overflow-y: auto; - border-right: 1px solid; - } - - .sidebar > .block.version { - overflow: hidden; - border-bottom: none; - margin-bottom: 0; - height: 100%; - padding-left: 12px; - } - .sidebar > .block.version > div.narrow-helper { - float: left; - width: 1px; - height: 100%; - } - .sidebar > .block.version > p { - /* hide Version text if too narrow */ - margin: 0; - min-width: 55px; - /* vertically center */ - display: flex; - align-items: center; - height: 100%; + .sidebar-menu-toggle { + width: 45px; + border: none; } .source nav:not(.sidebar).sub { margin-left: 32px; } + /* Space is at a premium on mobile, so remove the theme-picker icon. */ + #theme-picker { + display: none; + width: 0; + } + .content { margin-left: 0px; } @@ -1925,28 +1891,6 @@ details.rustdoc-toggle[open] > summary.hideme::after { height: 50px; } - .show-it, .sidebar-elems:focus-within { - z-index: 2; - left: 0; - } - - .show-it > .block.items { - margin: 8px 0; - } - - .show-it > .block.items > ul { - margin: 0; - } - - .show-it > .block.items > ul > li { - text-align: center; - margin: 2px 0; - } - - .show-it > .block.items > ul > li > a { - font-size: 1.3125rem; - } - /* Because of ios, we need to actually have a full height sidebar title so the * actual sidebar can show up. But then we need to make it transparent so we don't * hide content. The filler just allows to create the background for the sidebar @@ -1967,10 +1911,6 @@ details.rustdoc-toggle[open] > summary.hideme::after { left: -11px; } - #all-types { - margin: 10px; - } - .sidebar.expanded #sidebar-toggle { font-size: 1.5rem; } diff --git a/src/librustdoc/html/static/css/themes/ayu.css b/src/librustdoc/html/static/css/themes/ayu.css index 885fe7c09a973..82a2be67ceb0c 100644 --- a/src/librustdoc/html/static/css/themes/ayu.css +++ b/src/librustdoc/html/static/css/themes/ayu.css @@ -57,7 +57,7 @@ pre, .rustdoc.source .example-wrap { background-color: #191f26; } -.sidebar { +.sidebar, .mobile-topbar, .sidebar-menu-toggle { background-color: #14191f; } @@ -100,12 +100,6 @@ pre, .rustdoc.source .example-wrap { background-color: #14191f; } -.sidebar .location { - border-color: #000; - background-color: #0f1419; - color: #fff; -} - .sidebar-elems .location { color: #ff7733; } @@ -114,15 +108,6 @@ pre, .rustdoc.source .example-wrap { color: #fff; } -.sidebar .version { - border-bottom-color: #424c57; -} - -.sidebar-title { - border-top-color: #5c6773; - border-bottom-color: #5c6773; -} - .block a:hover { background: transparent; color: #ffb44c; @@ -228,7 +213,8 @@ a.anchor, .small-section-header a, #source-sidebar a, pre.rust a, -.sidebar a, +.sidebar h2 a, +.sidebar h3 a, .in-band a { color: #c5c5c5; } @@ -581,13 +567,6 @@ kbd { } } -#all-types { - background-color: #14191f; -} -#all-types:hover { - background-color: rgba(70, 70, 70, 0.33); -} - .search-results .result-name span.alias { color: #c5c5c5; } diff --git a/src/librustdoc/html/static/css/themes/dark.css b/src/librustdoc/html/static/css/themes/dark.css index 71af77978328f..761bf50dd36b1 100644 --- a/src/librustdoc/html/static/css/themes/dark.css +++ b/src/librustdoc/html/static/css/themes/dark.css @@ -28,7 +28,7 @@ pre, .rustdoc.source .example-wrap { background-color: #2A2A2A; } -.sidebar { +.sidebar, .mobile-topbar, .sidebar-menu-toggle { background-color: #505050; } @@ -69,21 +69,6 @@ pre, .rustdoc.source .example-wrap { background-color: #565656; } -.sidebar .location { - border-color: #fff; - background: #575757; - color: #DDD; -} - -.sidebar .version { - border-bottom-color: #DDD; -} - -.sidebar-title { - border-top-color: #777; - border-bottom-color: #777; -} - .block a:hover { background: #444; } @@ -186,7 +171,8 @@ a.anchor, .small-section-header a, #source-sidebar a, pre.rust a, -.sidebar a, +.sidebar h2 a, +.sidebar h3 a, .in-band a { color: #ddd; } @@ -453,13 +439,6 @@ kbd { } } -#all-types { - background-color: #505050; -} -#all-types:hover { - background-color: #606060; -} - .search-results .result-name span.alias { color: #fff; } diff --git a/src/librustdoc/html/static/css/themes/light.css b/src/librustdoc/html/static/css/themes/light.css index e462fd16237f2..7cca7d4004b5e 100644 --- a/src/librustdoc/html/static/css/themes/light.css +++ b/src/librustdoc/html/static/css/themes/light.css @@ -30,7 +30,7 @@ pre, .rustdoc.source .example-wrap { background-color: #F5F5F5; } -.sidebar { +.sidebar, .mobile-topbar, .sidebar-menu-toggle { background-color: #F5F5F5; } @@ -69,21 +69,6 @@ pre, .rustdoc.source .example-wrap { background-color: #f1f1f1; } -.sidebar .location { - border-color: #000; - background-color: #fff; - color: #333; -} - -.sidebar .version { - border-bottom-color: #DDD; -} - -.sidebar-title { - border-top-color: #777; - border-bottom-color: #777; -} - .block a:hover { background: #F5F5F5; } @@ -183,7 +168,8 @@ a.anchor, .small-section-header a, #source-sidebar a, pre.rust a, -.sidebar a, +.sidebar h2 a, +.sidebar h3 a, .in-band a { color: #000; } @@ -440,13 +426,6 @@ kbd { } } -#all-types { - background-color: #fff; -} -#all-types:hover { - background-color: #f9f9f9; -} - .search-results .result-name span.alias { color: #000; } diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js index e7d1eedd35871..161b95d99930e 100644 --- a/src/librustdoc/html/static/js/main.js +++ b/src/librustdoc/html/static/js/main.js @@ -67,6 +67,13 @@ function resourcePath(basename, extension) { ty: sidebarVars.attributes["data-ty"].value, relpath: sidebarVars.attributes["data-relpath"].value, }; + // FIXME: It would be nicer to generate this text content directly in HTML, + // but with the current code it's hard to get the right information in the right place. + var mobileLocationTitle = document.querySelector(".mobile-topbar h2.location"); + var locationTitle = document.querySelector(".sidebar h2.location"); + if (mobileLocationTitle && locationTitle) { + mobileLocationTitle.innerText = locationTitle.innerText; + } } }()); @@ -309,37 +316,6 @@ function hideThemeButtonState() { return null; } - function showSidebar() { - var elems = document.getElementsByClassName("sidebar-elems")[0]; - if (elems) { - addClass(elems, "show-it"); - } - var sidebar = document.getElementsByClassName("sidebar")[0]; - if (sidebar) { - addClass(sidebar, "mobile"); - var filler = document.getElementById("sidebar-filler"); - if (!filler) { - var div = document.createElement("div"); - div.id = "sidebar-filler"; - sidebar.appendChild(div); - } - } - } - - function hideSidebar() { - var elems = document.getElementsByClassName("sidebar-elems")[0]; - if (elems) { - removeClass(elems, "show-it"); - } - var sidebar = document.getElementsByClassName("sidebar")[0]; - removeClass(sidebar, "mobile"); - var filler = document.getElementById("sidebar-filler"); - if (filler) { - filler.remove(); - } - document.getElementsByTagName("body")[0].style.marginTop = ""; - } - var toggleAllDocsId = "toggle-all-docs"; var main = document.getElementById(MAIN_ID); var savedHash = ""; @@ -374,7 +350,8 @@ function hideThemeButtonState() { function onHashChange(ev) { // If we're in mobile mode, we should hide the sidebar in any case. - hideSidebar(); + var sidebar = document.getElementsByClassName("sidebar")[0]; + removeClass(sidebar, "shown"); handleHashes(ev); } @@ -866,6 +843,11 @@ function hideThemeButtonState() { }); }()); + function hideSidebar() { + var sidebar = document.getElementsByClassName("sidebar")[0]; + removeClass(sidebar, "shown"); + } + function handleClick(id, f) { var elem = document.getElementById(id); if (elem) { @@ -906,16 +888,16 @@ function hideThemeButtonState() { }; }); - var sidebar_menu = document.getElementsByClassName("sidebar-menu")[0]; - if (sidebar_menu) { - sidebar_menu.onclick = function() { + var sidebar_menu_toggle = document.getElementsByClassName("sidebar-menu-toggle")[0]; + if (sidebar_menu_toggle) { + sidebar_menu_toggle.addEventListener("click", function() { var sidebar = document.getElementsByClassName("sidebar")[0]; - if (hasClass(sidebar, "mobile")) { - hideSidebar(); + if (!hasClass(sidebar, "shown")) { + addClass(sidebar, "shown"); } else { - showSidebar(); + removeClass(sidebar, "shown"); } - }; + }); } var buildHelperPopup = function() { diff --git a/src/librustdoc/html/static/js/source-script.js b/src/librustdoc/html/static/js/source-script.js index 81dc0b2fb1eb9..498f60e9f25ae 100644 --- a/src/librustdoc/html/static/js/source-script.js +++ b/src/librustdoc/html/static/js/source-script.js @@ -139,7 +139,7 @@ function createSourceSidebar() { currentFile, hasFoundFile); }); - container.insertBefore(sidebar, document.querySelector(".sidebar-logo").nextSibling); + container.appendChild(sidebar); // Focus on the current file in the source files sidebar. var selected_elem = sidebar.getElementsByClassName("selected")[0]; if (typeof selected_elem !== "undefined") { diff --git a/src/librustdoc/templates/page.html b/src/librustdoc/templates/page.html index 1ef001ec2b719..1322b854b7fc7 100644 --- a/src/librustdoc/templates/page.html +++ b/src/librustdoc/templates/page.html @@ -72,8 +72,20 @@

{#- -#} {#- -#} {{- layout.external_html.before_content|safe -}} +