Skip to content

Commit 85aba79

Browse files
authored
Match Sphinx toggle button and Sphinx Design hover and focus styles (#2061)
This PR makes the hover and focus styles more consistent for the various collapsible admonitions we support (Sphinx Design and Sphinx Toggle Button). It also makes the focus rings better match the [design system](https://www.figma.com/design/SnLFWtSKEBLYrtLHbs4TSE/PyData-Sphinx-Theme-Design-System-(Community)?node-id=1233-8360&node-type=frame&t=4kx1rhhCuQNUGldF-0). Screenshot of the admonition focus rings from the design system: ![](https://github.com/user-attachments/assets/6725942d-85e9-41da-bce9-eabe3f39a57e) Screenshots from my local git branch, for comparison: ![image](https://github.com/user-attachments/assets/797e298c-d755-455e-b470-9690ea41b353) ![image](https://github.com/user-attachments/assets/7ac58e27-edcf-4baf-8783-da886eab0802)
1 parent 933ce14 commit 85aba79

File tree

5 files changed

+108
-28
lines changed

5 files changed

+108
-28
lines changed

src/pydata_sphinx_theme/assets/styles/abstracts/_links.scss

+1-1
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,6 @@ $link-hover-decoration-thickness: string.unquote(
217217
&:focus-visible {
218218
box-shadow: none; // override Bootstrap
219219
outline: 3px solid var(--pst-color-accent);
220-
outline-offset: 3px;
220+
outline-offset: $focus-ring-width;
221221
}
222222
}

src/pydata_sphinx_theme/assets/styles/abstracts/_mixins.scss

+12
Original file line numberDiff line numberDiff line change
@@ -64,3 +64,15 @@
6464
min-width: 24px;
6565
min-height: 24px;
6666
}
67+
68+
// Meant to darken the element on hover in light mode, or
69+
// lighten on hover in dark mode.
70+
@mixin hover-darken-lighten {
71+
&:hover {
72+
filter: brightness(0.9);
73+
74+
html[data-theme="dark"] & {
75+
filter: brightness(1.1);
76+
}
77+
}
78+
}

src/pydata_sphinx_theme/assets/styles/extensions/_sphinx_design.scss

+13-3
Original file line numberDiff line numberDiff line change
@@ -322,12 +322,20 @@ details.sd-dropdown {
322322
top: 0.7rem;
323323
}
324324

325+
@include hover-darken-lighten;
326+
325327
// Focus ring
326328
&:focus:focus-visible {
327329
outline: $focus-ring-outline;
328-
outline-offset: -$focus-ring-width;
330+
outline-offset: $focus-ring-offset;
331+
border-radius: $focus-ring-width;
329332
}
330333
}
334+
335+
&[open] summary.sd-card-header:focus:focus-visible {
336+
border-bottom-left-radius: 0;
337+
border-bottom-right-radius: 0;
338+
}
331339
}
332340

333341
/*******************************************************************************
@@ -358,14 +366,16 @@ html {
358366
.sd-btn-#{$name},
359367
.sd-btn-outline-#{$name} {
360368
&:focus-visible {
369+
outline: var(--sd-color-#{$name}) solid $focus-ring-width;
370+
outline-offset: $focus-ring-offset;
371+
border-radius: $focus-ring-width;
372+
361373
// Override Sphinx Design's use of -highlight colors. The -highlight
362374
// colors are 15% darker, so this would create the effect of darkening
363375
// the button when focused but we just want the button to have a focus
364376
// ring of the same (non-highlight) color.
365377
background-color: var(--sd-color-#{$name}) !important;
366378
border-color: var(--sd-color-#{$name}) !important;
367-
outline: var(--sd-color-#{$name}) solid $focus-ring-width;
368-
outline-offset: $focus-ring-width;
369379
}
370380
}
371381
}

src/pydata_sphinx_theme/assets/styles/extensions/_togglebutton.scss

+77-24
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
/**
22
* Sphinx togglebutton
3+
*
4+
* The rules in this style sheet are meant to tweak the
5+
* [sphinx-togglebutton](https://sphinx-togglebutton.readthedocs.io/en/latest/index.html)
6+
* extension so that it matches the look and feel of this theme.
37
*/
48

59
.bd-content {
@@ -17,8 +21,31 @@
1721
}
1822
}
1923

20-
// Admonition toggles
21-
.admonition {
24+
// Apply this mixin to the element that will be hovered. These styles are
25+
// intended to match what sphinx-design does for its dropdown admonitions.
26+
@mixin icon-hover-effects {
27+
&:hover .tb-icon {
28+
opacity: 1;
29+
scale: 1.1;
30+
}
31+
32+
.tb-icon {
33+
opacity: 0.6;
34+
}
35+
}
36+
37+
// Collapsible admonition, implemented as <div> + <button>
38+
.dropdown.admonition.toggle {
39+
// The title is visible when the admonition is collapsed and expanded
40+
.admonition-title {
41+
@include icon-hover-effects;
42+
@include hover-darken-lighten;
43+
44+
&:hover {
45+
box-shadow: none;
46+
}
47+
}
48+
2249
button.toggle-button {
2350
color: inherit;
2451

@@ -34,49 +61,75 @@
3461
// Focus ring
3562
// ----------
3663
// Sphinx-togglebutton makes the entire admonition header clickable, but
37-
// only the button within the header is focusable. We want the entire
38-
// clickable area to be surrounded with a focus ring, so that's why we use
39-
// the :focus-within selector, rather than a :focus-visible selector on the
40-
// button.
41-
&:focus-within {
64+
// only the button within the header is focusable. But we want the entire
65+
// header and not just the button inside the header to be surrounded by a
66+
// a focus ring.
67+
&:has(:focus-visible) {
68+
/* Override Sphinx Toggle Button. Make the overflow visible, otherwise the
69+
focus ring is hidden. */
4270
overflow: visible;
4371

44-
// The complicated focus ring styles here are a consequence of the markup
45-
// and border styles for this particular admonition class. (For the other
46-
// type of admonition on this site, the focus ring style is achieved with
47-
// simple `outline` and `outline-offset` rules on the admonition's
48-
// header.) The problem is that Sphinx-togglebutton puts the admonition's
49-
// left border on the outermost container (rather than separately setting
50-
// the left border on the container's children). This makes it complicated
51-
// to get the focus ring to simultaneously cover the left border in the
52-
// header and align perfectly on the right with the body.
53-
.admonition-title:focus-within::before {
72+
/*
73+
Why not just do the following?
74+
75+
```
76+
.admonition-title {
77+
outline: $focus-ring-outline;
78+
}
79+
```
80+
81+
Why use ::before? If we put the focus ring on the ::before pseudo-element,
82+
we can reposition the focus ring by repositioning the pseudo-element.
83+
84+
Why reposition? The left edge of the admonition title box does not align
85+
with the left edge of the overall admonition box. There is a left border
86+
that belongs to the overall box. The border is outside of the admonition
87+
title, which means it is also outside of a focus ring around the title. We
88+
can make the focus ring bigger, with `outline-offset`, but this will
89+
result in a ring that looks off-centered. So we have to pull the ring left
90+
and stretch it right. That's what the pseudo-element allows us to do.
91+
92+
Note: we do not have to do this for collapsible admonitions made with
93+
Sphinx Design (as opposed to sphinx-togglebutton).
94+
*/
95+
.admonition-title::before {
5496
content: "";
55-
transform: translateX(
56-
-$admonition-left-border-width
57-
); // align left edges of admonition and ring
5897

59-
width: calc(100% + $admonition-left-border-width); // align right edges
98+
// pull the focus ring left and expand it right to be perfectly centered
99+
// between the left border and the right edge of the admonition title
100+
left: -$admonition-left-border-width;
101+
width: calc(100% + $admonition-left-border-width);
60102
height: 100%;
61-
border: $focus-ring-outline;
103+
outline: $focus-ring-outline;
104+
outline-offset: $focus-ring-offset;
62105
border-radius: $focus-ring-width;
63106
}
64107

65108
// When expanded, sharpen the bottom left and right corners of the focus ring
66-
&:not(.toggle-hidden) .admonition-title:focus-within::before {
109+
&:not(.toggle-hidden) .admonition-title::before {
67110
border-bottom-left-radius: 0;
68111
border-bottom-right-radius: 0;
69112
}
70113
}
71114
}
72115

73-
// Details buttons
116+
// Collapsible component, implemented as <details> + <summary>
74117
details.toggle-details {
75118
// Over-ride border color to re-use our primary color
76119
summary {
77120
border-left: 3px solid var(--pst-color-primary);
78121

79122
@include chevron-down;
123+
@include icon-hover-effects;
124+
@include hover-darken-lighten;
125+
126+
&:hover {
127+
box-shadow: none;
128+
}
129+
130+
&:focus-visible {
131+
outline-offset: $focus-ring-offset;
132+
}
80133
}
81134

82135
// When expanded, sharpen the bottom left and right corners of the focus ring

src/pydata_sphinx_theme/assets/styles/variables/_bootstrap.scss

+5
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@ $focus-ring-color: var(--pst-color-accent);
2626
$focus-ring-blur: 0;
2727
$focus-ring-box-shadow: 0 0 $focus-ring-blur $focus-ring-width $focus-ring-color;
2828

29+
// For many elements, we do not use `outline-offset`. For some we set the offset
30+
// equal to the focus ring width (either outwards or inwards). But for some
31+
// other elements (e.g., collapsible admonitions) we set it to this value.
32+
$focus-ring-offset: 0.125rem; // 2px at 100% zoom (0.125 * 16px base font = 2px)
33+
2934
// outline creates the same style of focus ring, it just uses CSS outline instead of box shadow
3035
$focus-ring-outline: $focus-ring-color solid $focus-ring-width;
3136
$btn-focus-box-shadow: $focus-ring-box-shadow;

0 commit comments

Comments
 (0)