diff --git a/docs/examples/gallery.md b/docs/examples/gallery.md index 888b6d416..74d7321cb 100644 --- a/docs/examples/gallery.md +++ b/docs/examples/gallery.md @@ -35,7 +35,7 @@ Thanks for your support! link: https://fairlearn.org/main/about/ - title: Feature-engine link: https://feature-engine.readthedocs.io/ -- title: idtracker.ai +- title: idtracker.ai link: https://idtracker.ai/ - title: MegEngine link: https://www.megengine.org.cn/doc/stable/en/index.html diff --git a/docs/index.md b/docs/index.md index 0e5562f93..c5f5d407f 100644 --- a/docs/index.md +++ b/docs/index.md @@ -21,13 +21,11 @@ A clean, Bootstrap-based Sphinx theme by and for [the PyData community](https:// - header: "{fas}`circle-half-stroke;pst-color-primary` Light / Dark theme" content: "Users can toggle between light and dark themes interactively." - header: "{fas}`palette;pst-color-primary` Customizable UI and themes" - content: "Customize colors and branding with CSS variables, and build custom UIs with [Sphinx Design](user_guide/web-components)." + content: "Customize colors and branding with CSS variables, and build custom UIs with [Sphinx Design components](user_guide/web-components)." - header: "{fab}`python;pst-color-primary` Supports PyData and Jupyter" - content: "CSS and UI support for Jupyter extensions and PyData execution outputs." - link: "examples/pydata.html" + content: "CSS and UI support for [Jupyter extensions](examples/execution) and [PyData execution outputs](examples/pydata.ipynb)." - header: "{fas}`lightbulb;pst-color-primary` Example Gallery" - content: "See our gallery of projects that use this theme." - link: "examples/gallery.html" + content: "See [our gallery](examples/gallery.md) of projects that use this theme." ``` ```{seealso} diff --git a/src/pydata_sphinx_theme/assets/styles/abstracts/_links.scss b/src/pydata_sphinx_theme/assets/styles/abstracts/_links.scss index f3fe96be1..eb8b6a963 100644 --- a/src/pydata_sphinx_theme/assets/styles/abstracts/_links.scss +++ b/src/pydata_sphinx_theme/assets/styles/abstracts/_links.scss @@ -86,7 +86,6 @@ $link-hover-decoration-thickness: unquote("max(3px, .1875rem, .12em)") !default; color: var(--pst-color-link-hover); } } - @include focus-indicator; } // Text link styles @@ -102,7 +101,6 @@ $link-hover-decoration-thickness: unquote("max(3px, .1875rem, .12em)") !default; @include link-decoration; @include link-decoration-hover; } - @include focus-indicator; } // Sidebar and TOC links @@ -137,10 +135,8 @@ $link-hover-decoration-thickness: unquote("max(3px, .1875rem, .12em)") !default; font-weight: 600; color: var(--pst-color-primary); @if $link-hover-decoration-thickness { - box-shadow: inset - $link-hover-decoration-thickness - 0px - 0px + border-left: $link-hover-decoration-thickness + solid var(--pst-color-primary); } } @@ -175,10 +171,3 @@ $link-hover-decoration-thickness: unquote("max(3px, .1875rem, .12em)") !default; } } } - -// Focus indicator -@mixin focus-indicator { - &:focus-visible { - outline: 2px solid var(--pst-color-accent); - } -} diff --git a/src/pydata_sphinx_theme/assets/styles/base/_base.scss b/src/pydata_sphinx_theme/assets/styles/base/_base.scss index efa349103..7eb0d7ce5 100644 --- a/src/pydata_sphinx_theme/assets/styles/base/_base.scss +++ b/src/pydata_sphinx_theme/assets/styles/base/_base.scss @@ -179,3 +179,13 @@ pre { background-color: var(--pst-color-secondary); border: none; } + +// Focus ring +// +// Note: The Bootstrap stylesheet provides the focus ring (customized via Sass +// variables in _bootstrap.scss) in some cases. This rules covers all other +// cases. +:focus-visible { + outline: $focus-ring-outline; + box-shadow: none; // override Bootstrap +} diff --git a/src/pydata_sphinx_theme/assets/styles/components/_search.scss b/src/pydata_sphinx_theme/assets/styles/components/_search.scss index 91ed09eb2..891a19701 100644 --- a/src/pydata_sphinx_theme/assets/styles/components/_search.scss +++ b/src/pydata_sphinx_theme/assets/styles/components/_search.scss @@ -58,8 +58,6 @@ &:focus, &:focus-visible { border: none; - box-shadow: none; - outline: 3px solid var(--pst-color-accent); background-color: var(--pst-color-background); color: var(--pst-color-text-muted); } @@ -75,11 +73,16 @@ align-items: center; align-content: center; color: var(--pst-color-text-muted); - // Needed to match other icons hover - padding: 0 0 0.25rem 0; border-radius: 0; + border: none; // Override Bootstrap button border + font-size: 1rem; // Override Bootstrap button font size + + // Override Bootstrap button padding-x. Whitespace in nav bar is controlled + // via column gap rule on the container. + padding-left: 0; + padding-right: 0; + @include icon-navbar-hover; - @include focus-indicator; i { font-size: 1.3rem; @@ -136,10 +139,11 @@ * Lives at components/search-button-field.html */ .search-button-field { + $search-button-border-radius: 1.5em; display: inline-flex; align-items: center; border: var(--pst-color-border) solid 1px; - border-radius: 1.5em; + border-radius: $search-button-border-radius; color: var(--pst-color-text-muted); padding: 0.5em; background-color: var(--pst-color-surface); @@ -147,8 +151,9 @@ &:hover { border: 2px solid var(--pst-color-link-hover); } + &:focus-visible { - border: 2px solid var(--pst-color-accent); + border-radius: $search-button-border-radius; } // The keyboard shotcut text diff --git a/src/pydata_sphinx_theme/assets/styles/components/_switcher-theme.scss b/src/pydata_sphinx_theme/assets/styles/components/_switcher-theme.scss index 99f58f60f..f51a0a3b2 100644 --- a/src/pydata_sphinx_theme/assets/styles/components/_switcher-theme.scss +++ b/src/pydata_sphinx_theme/assets/styles/components/_switcher-theme.scss @@ -3,18 +3,23 @@ */ .theme-switch-button { - // overide bootstrap settings - margin: 0 -0.5rem; - padding: 0; // We pad the `span` not the container color: var(--pst-color-text-muted); border-radius: 0; - @include focus-indicator; + border: none; // Override Bootstrap button border + font-size: 1rem; // Override Bootstrap's button font size + + // Override Bootstrap button padding-x. Whitespace in nav bar is controlled + // via column gap rule on the container. + padding-left: 0; + padding-right: 0; + + &:hover { + @include icon-navbar-hover; + } span { display: none; - padding: 0.5em; - @include icon-navbar-hover; &:active { text-decoration: none; color: var(--pst-color-link-hover); diff --git a/src/pydata_sphinx_theme/assets/styles/components/_switcher-version.scss b/src/pydata_sphinx_theme/assets/styles/components/_switcher-version.scss index 84baed7a7..2ed0baa66 100644 --- a/src/pydata_sphinx_theme/assets/styles/components/_switcher-version.scss +++ b/src/pydata_sphinx_theme/assets/styles/components/_switcher-version.scss @@ -8,7 +8,6 @@ button.btn.version-switcher__button { } @include link-style-hover; - @include focus-indicator; &:active { color: var(--pst-color-text-base); border-color: var(--pst-color-border); diff --git a/src/pydata_sphinx_theme/assets/styles/components/_toc-inpage.scss b/src/pydata_sphinx_theme/assets/styles/components/_toc-inpage.scss index 01db3945c..026ba823f 100644 --- a/src/pydata_sphinx_theme/assets/styles/components/_toc-inpage.scss +++ b/src/pydata_sphinx_theme/assets/styles/components/_toc-inpage.scss @@ -48,4 +48,8 @@ nav.page-toc { } } } + + :focus-visible { + border-radius: 0.125rem; + } } diff --git a/src/pydata_sphinx_theme/assets/styles/content/_admonitions.scss b/src/pydata_sphinx_theme/assets/styles/content/_admonitions.scss index a71490580..b7d0ffadd 100644 --- a/src/pydata_sphinx_theme/assets/styles/content/_admonitions.scss +++ b/src/pydata_sphinx_theme/assets/styles/content/_admonitions.scss @@ -3,13 +3,14 @@ * Admonitions CSS originally inspired by https://squidfunk.github.io/mkdocs-material/getting-started/ */ $admonition-border-radius: 0.25rem; +$admonition-left-border-width: 0.2rem; div.admonition, .admonition { margin: 1.5625em auto; padding: 0 0.6rem 0.8rem 0.6rem; overflow: hidden; page-break-inside: avoid; - border-left: 0.2rem solid; + border-left: $admonition-left-border-width solid; border-color: var(--pst-color-info); border-radius: $admonition-border-radius; background-color: var(--pst-color-on-background); @@ -226,7 +227,7 @@ div.admonition, margin-top: 0; // Undo the .sidebar directive border - border-width: 0 0 0 0.2rem; + border-width: 0 0 0 $admonition-left-border-width; // TODO: these semantic-color-names border-color rules might no longer be // needed when we drop support for Sphinx 4 / docutils 0.17 diff --git a/src/pydata_sphinx_theme/assets/styles/extensions/_copybutton.scss b/src/pydata_sphinx_theme/assets/styles/extensions/_copybutton.scss index be6c887f7..859dc78de 100644 --- a/src/pydata_sphinx_theme/assets/styles/extensions/_copybutton.scss +++ b/src/pydata_sphinx_theme/assets/styles/extensions/_copybutton.scss @@ -31,4 +31,13 @@ div.highlight button.copybtn { color: var(--pst-color-text); background-color: var(--pst-color-surface); } + + &:focus { + // For keyboard users, make the copy button visible when focussed. + opacity: 1; + } + + &:focus-visible { + outline: $focus-ring-outline; + } } diff --git a/src/pydata_sphinx_theme/assets/styles/extensions/_sphinx_design.scss b/src/pydata_sphinx_theme/assets/styles/extensions/_sphinx_design.scss index 798164998..8c4428f42 100644 --- a/src/pydata_sphinx_theme/assets/styles/extensions/_sphinx_design.scss +++ b/src/pydata_sphinx_theme/assets/styles/extensions/_sphinx_design.scss @@ -149,6 +149,22 @@ html[data-theme="light"] { .sd-card-body { background-color: var(--pst-color-panel-background); } + + // Focus ring for link-cards + .sd-stretched-link:focus-visible { + // Don't put the focus ring on the element (it has zero height in Sphinx Design cards) + outline: none; + + // Put the focus ring on the element's ::after pseudo-element + &:after { + outline: $focus-ring-outline; + border-radius: 0.25rem; // copied from Sphinx Design CSS for .sd-card + } + } + + &.sd-card-hover:hover { + border-color: var(--pst-color-link-hover); + } } /******************************************************************************* * tabs @@ -256,5 +272,11 @@ details.sd-dropdown { .sd-summary-down { top: 0.7rem; } + + // Focus ring + &:focus-visible { + outline: $focus-ring-outline; + outline-offset: -$focus-ring-width; + } } } diff --git a/src/pydata_sphinx_theme/assets/styles/extensions/_togglebutton.scss b/src/pydata_sphinx_theme/assets/styles/extensions/_togglebutton.scss index 9211b91ac..626aabde4 100644 --- a/src/pydata_sphinx_theme/assets/styles/extensions/_togglebutton.scss +++ b/src/pydata_sphinx_theme/assets/styles/extensions/_togglebutton.scss @@ -8,6 +8,43 @@ button.toggle-button { color: inherit; } + + // Focus ring + // + // Sphinx-togglebutton makes the entire admonition header clickable, but + // only the button within the header is focusable. We want the entire + // clickable area to be surrounded with a focus ring, so that's why we use + // the :focus-within selector, rather than a :focus-visible selector on the + // button. + &:focus-within { + overflow: visible; + + // The complicated focus ring styles here are a consequence of the markup + // and border styles for this particular admonition class. (For the other + // type of admonition on this site, the focus ring style is achieved with + // simple `outline` and `outline-offset` rules on the admonition's + // header.) The problem is that Sphinx-togglebutton puts the admonition's + // left border on the outermost container (rather than separately setting + // the left border on the container's children). This makes it complicated + // to get the focus ring to simultaneously cover the left border in the + // header and align perfectly on the right with the body. + .admonition-title:focus-within:before { + content: ""; + transform: translateX( + -$admonition-left-border-width + ); // align left edges of admonition and ring + width: calc(100% + $admonition-left-border-width); // align right edges + height: 100%; + border: $focus-ring-outline; + border-radius: $focus-ring-width; + } + + // When expanded, sharpen the bottom left and right corners of the focus ring + &:not(.toggle-hidden) .admonition-title:focus-within:before { + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; + } + } } // Details buttons @@ -16,5 +53,11 @@ summary { border-left: 3px solid var(--pst-color-primary); } + + // When expanded, sharpen the bottom left and right corners of the focus ring + &[open] :focus-visible { + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; + } } } diff --git a/src/pydata_sphinx_theme/assets/styles/sections/_header.scss b/src/pydata_sphinx_theme/assets/styles/sections/_header.scss index 0e491274f..3572c07d4 100644 --- a/src/pydata_sphinx_theme/assets/styles/sections/_header.scss +++ b/src/pydata_sphinx_theme/assets/styles/sections/_header.scss @@ -28,6 +28,10 @@ padding-right: 1rem; } + :focus-visible { + border-radius: 0.125rem; + } + // These items will define the height of the header .navbar-item { height: var(--pst-header-height); @@ -99,7 +103,6 @@ color: var(--pst-color-text-muted); border: none; @include link-style-hover; - @include focus-indicator; } .dropdown-menu { @@ -190,7 +193,6 @@ } } @include icon-navbar-hover; - @include focus-indicator; } // Hide the navbar header items on mobile because they're in the sidebar diff --git a/src/pydata_sphinx_theme/assets/styles/sections/_sidebar-primary.scss b/src/pydata_sphinx_theme/assets/styles/sections/_sidebar-primary.scss index df8f819c5..08d8e78d6 100644 --- a/src/pydata_sphinx_theme/assets/styles/sections/_sidebar-primary.scss +++ b/src/pydata_sphinx_theme/assets/styles/sections/_sidebar-primary.scss @@ -23,6 +23,11 @@ font-size: var(--pst-sidebar-font-size); } + // Focus styles + :focus-visible { + border-radius: 0.125rem; + } + // override bootstrap when navlink are displayed in the sidebar .nav-link { font-size: var(--pst-sidebar-font-size-mobile); @@ -80,6 +85,26 @@ border: none; background-color: inherit; font-size: inherit; + + .dropdown-item { + &:hover, + &:focus { + // In the mobile sidebar, the dropdown menu is inlined with the + // other links, which do not have background-color changes on hover + // and focus + background-color: unset; + } + } + } + } + + .bd-navbar-elements { + .nav-link { + &:focus-visible { + box-shadow: none; // Override Bootstrap + outline: $focus-ring-outline; + outline-offset: $focus-ring-width; + } } } @@ -93,7 +118,7 @@ .sidebar-header-items__end { display: flex; align-items: center; - gap: 0.5rem; + gap: 1rem; } @include media-breakpoint-up($breakpoint-sidebar-primary) { @@ -171,8 +196,6 @@ /* Between-page links and captions */ nav.bd-links { - margin-right: -1rem; - @include media-breakpoint-up($breakpoint-sidebar-primary) { display: block; } diff --git a/src/pydata_sphinx_theme/assets/styles/sections/_skip-link.scss b/src/pydata_sphinx_theme/assets/styles/sections/_skip-link.scss index 2252ffb44..b82f94506 100644 --- a/src/pydata_sphinx_theme/assets/styles/sections/_skip-link.scss +++ b/src/pydata_sphinx_theme/assets/styles/sections/_skip-link.scss @@ -1,6 +1,6 @@ /*** - * Rules for the UX/UI of skip navigation link btn. - *It's only visible to people + * Rules for the UX/UI of skip navigation link btn. + * It's only visible to people * navigating with keyboard for accessibility purposes * ref: https://www.youtube.com/watch?v=VUR0I5mqq7I ***/ @@ -12,8 +12,6 @@ right: 0; text-align: center; background-color: var(--pst-color-warning); - // Ensure we are using a WCAG conformant colour - color: var(--pst-color-warning-text) !important; padding: 0.5rem; z-index: $zindex-modal; border-bottom: 1px solid var(--pst-color-border); @@ -21,9 +19,17 @@ // This shows / hides the button transform: translateY(-100%); transition: transform 150ms ease-in-out; - &:focus { + &:focus-within { transform: translateY(0%); - // ensure this is visible - outline: 3px solid $foundation-black; + } + + a { + // Ensure we are using a WCAG conformant colour + color: var(--pst-color-warning-text) !important; + + &:focus-visible { + // use color with sufficient contrast + outline-color: $foundation-black; + } } } diff --git a/src/pydata_sphinx_theme/assets/styles/variables/_bootstrap.scss b/src/pydata_sphinx_theme/assets/styles/variables/_bootstrap.scss index 8cf8303d6..ded367106 100644 --- a/src/pydata_sphinx_theme/assets/styles/variables/_bootstrap.scss +++ b/src/pydata_sphinx_theme/assets/styles/variables/_bootstrap.scss @@ -22,3 +22,16 @@ $dropdown-link-hover-bg: var(--pst-color-surface); $dropdown-dark-link-hover-bg: var(--pst-color-surface); $dropdown-link-active-bg: var(--pst-color-surface); $dropdown-dark-link-active-bg: var(--pst-color-surface); + +$focus-ring-width: 0.1875rem; // 3px at 100% zoom (0.1875 * 16px base font = 3px) +$focus-ring-opacity: 1; +$focus-ring-color: var(--pst-color-accent); +$focus-ring-blur: 0; +$focus-ring-box-shadow: 0 0 $focus-ring-blur $focus-ring-width $focus-ring-color; +// outline creates the same style of focus ring, it just uses CSS outline instead of box shadow +$focus-ring-outline: $focus-ring-color solid $focus-ring-width; +$btn-focus-box-shadow: $focus-ring-box-shadow; + +.btn { + --bs-btn-focus-box-shadow: #{$btn-focus-box-shadow}; +} diff --git a/src/pydata_sphinx_theme/theme/pydata_sphinx_theme/layout.html b/src/pydata_sphinx_theme/theme/pydata_sphinx_theme/layout.html index a3c33ab92..1de5fa4ad 100644 --- a/src/pydata_sphinx_theme/theme/pydata_sphinx_theme/layout.html +++ b/src/pydata_sphinx_theme/theme/pydata_sphinx_theme/layout.html @@ -50,7 +50,7 @@ {# A button hidden by default to help assistive devices quickly jump to main content #} {# ref: https://www.youtube.com/watch?v=VUR0I5mqq7I #} - + {%- endblock %}