Skip to content

Commit 4be630f

Browse files
authored
Fix hit area icon links buttons (#1866)
Part of #1865. Fixes external issue Quansight-Labs/czi-scientific-python-mgmt#94. This pull request started as a simple increase of the hit area for the navbar icon links, but then I realized that with #1846 having been merged, I could do a little bit of code cleanup at the same time.
1 parent 41d6946 commit 4be630f

26 files changed

+186
-244
lines changed

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

+42-97
Original file line numberDiff line numberDiff line change
@@ -164,114 +164,59 @@ $link-hover-decoration-thickness: string.unquote(
164164
}
165165
}
166166

167-
// Navigation bar current page link styles
168-
// ---------------------------------------
169-
// Adds a bottom underline, this leaves enough space for the hover state without
170-
// cluttering the navbar.
171-
// We want the side box shadow to have the same thickness as the hover underline
172-
@mixin link-navbar-current {
173-
font-weight: 600;
174-
color: var(--pst-color-primary);
167+
// Heaver navbar text and icon links
168+
// ---------------------------------
169+
// (includes light/dark mode button)
170+
171+
// This mixin makes it possible to show hover/underline and focus/ring styles at
172+
// the same time. The trick is to use:
173+
// - a pseudo-element with bottom border for the hover underline
174+
// - a CSS outline for the focus ring.
175+
176+
// Normally we use box-shadow for underline and outline for focus ring. But we
177+
// cannot apply box-shadow and outline together on the same element because the
178+
// border-radius value that we use to round the outline will also round the
179+
// box-shadow used for the underline. We also cannot use text-underline because
180+
// it does not work on non-text links, nor do we want to use it on text links
181+
// that we want to treat as blocks, such as the header nav links because the
182+
// underline will wrap across two lines if the link text also wraps across two
183+
// lines.
184+
@mixin link-style-block {
185+
color: var(--pst-color-text-muted);
175186

176-
@if $link-hover-decoration-thickness {
177-
border-bottom: $link-hover-decoration-thickness
178-
solid
179-
var(--pst-color-primary);
180-
}
181-
}
187+
// Set position relative so that the child ::before pseudo-element's absolute
188+
// position is relative to this element.
189+
position: relative;
182190

183-
// Navigation bar icon links hover styles
184-
// --------------------------------------
185-
// Adds a bottom box-shadow - since there is no text we cannot use text-decoration
186-
// We want the side box shadow to have the same thickness as the hover underline
187-
@mixin icon-navbar-hover {
188-
&:hover {
189-
color: var(--pst-color-link-hover);
191+
// Set up pseudo-element used for hover underline styles
192+
&::before {
193+
content: "";
194+
display: block;
195+
position: absolute;
196+
inset: 0;
197+
background-color: transparent;
190198

191199
@if $link-hover-decoration-thickness {
192-
box-shadow: 0
193-
$link-hover-decoration-thickness
194-
0
195-
var(--pst-color-link-hover);
200+
bottom: calc(-1 * $link-hover-decoration-thickness);
201+
margin: $link-hover-decoration-thickness 0;
196202
}
197203
}
198-
}
199-
200-
// Mixin for links in the header (and the More dropdown toggle).
201-
202-
// The mixin assumes it will be applied to some element X with a markup structure
203-
// like: X > .nav-link, or X > .dropdown-toggle.
204-
205-
// It also assumes X.current is how the app annotates which item in the header nav
206-
// corresponds to the section in the docs that the user is currently reading.
207-
@mixin header-link {
208-
// Target the child and not the parent because we want the underline in the
209-
// mobile sidebar to only span the width of the text not the entire row/line.
210-
> .nav-link,
211-
> .dropdown-toggle {
212-
border-radius: 2px;
213-
color: var(--pst-color-text-muted);
214-
}
215-
216-
> .nav-link {
217-
// Set up pseudo-element for hover and current states below.
218-
position: relative;
219204

205+
&:hover {
206+
color: var(--pst-color-secondary);
207+
text-decoration: none; // override the link-style-hover mixin
220208
&::before {
221-
content: "";
222-
display: block;
223-
position: absolute;
224-
inset: 0;
225-
background-color: transparent;
226-
}
227-
228-
// Underline on hover.
229-
// - Don't use text-decoration because it will wrap across two lines if
230-
// the link text also wraps across two lines.
231-
// - Use pseudo-element in order to avoid the border-radius values
232-
// rounding the edges of the underline. (And since a header link can be
233-
// both focused and hovered at the same time and we want the focus ring
234-
// but not the underline to be rounded, we cannot use a box shadow or
235-
// bottom border link element to create the underline, or else it will
236-
// be rounded and if we apply border-radius 0 then the hovered focus
237-
// ring would go from rounded to sharp. So we have to use the
238-
// pseudo-element.)
239-
&:hover {
240-
color: var(--pst-color-secondary);
241-
text-decoration: none; // override the link-style-hover mixin
242-
&::before {
243-
border-bottom: 3px solid var(--pst-color-secondary);
209+
@if $link-hover-decoration-thickness {
210+
border-bottom: $link-hover-decoration-thickness
211+
solid
212+
var(--pst-color-secondary);
244213
}
245214
}
246-
247-
&:focus-visible {
248-
box-shadow: none; // override Bootstrap
249-
outline: 3px solid var(--pst-color-accent);
250-
outline-offset: 3px;
251-
}
252-
}
253-
254-
> .dropdown-toggle {
255-
&:focus-visible {
256-
box-shadow: $focus-ring-box-shadow;
257-
}
258-
259-
&:hover {
260-
text-decoration: none;
261-
box-shadow: 0 0 0 $focus-ring-width var(--pst-color-link-hover); // purple focus ring
262-
// Brighten the text on hover (muted -> base)
263-
color: var(--pst-color-text-base);
264-
}
265215
}
266216

267-
&.current {
268-
> .nav-link {
269-
color: var(--pst-color-primary);
270-
271-
// Underline the current navbar item
272-
&::before {
273-
border-bottom: 3px solid var(--pst-color-primary);
274-
}
275-
}
217+
&:focus-visible {
218+
box-shadow: none; // override Bootstrap
219+
outline: 3px solid var(--pst-color-accent);
220+
outline-offset: 3px;
276221
}
277222
}

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

+11
Original file line numberDiff line numberDiff line change
@@ -57,3 +57,14 @@
5757
}
5858
}
5959
}
60+
61+
// Minimum mouse hit area
62+
// ----------------------
63+
// Ensures that the element has a minimum hit area that conforms to
64+
// accessibility guidelines. For WCAG AA, we need 24px x 24px, see:
65+
// https://www.w3.org/WAI/WCAG22/Understanding/target-size-minimum.html
66+
@mixin min-hit-area() {
67+
box-sizing: border-box;
68+
min-width: 24px;
69+
min-height: 24px;
70+
}

src/pydata_sphinx_theme/assets/styles/base/_base.scss

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ body {
77
background-color: var(--pst-color-background);
88
font-family: var(--pst-font-family-base);
99
font-weight: 400;
10-
line-height: 1.65;
10+
line-height: $line-height-body;
1111
color: var(--pst-color-text-base);
1212
min-height: 100vh;
1313
display: flex;

src/pydata_sphinx_theme/assets/styles/components/_icon-links.scss

+23-19
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,29 @@
22
* Icon links in the navbar
33
*/
44

5-
.navbar-icon-links {
5+
.pst-navbar-icon {
6+
// Extra specificity needed for overrides
7+
html & {
8+
@include min-hit-area;
9+
@include link-style-block;
10+
11+
display: flex;
12+
align-items: center;
13+
justify-content: center;
14+
15+
// Bootstrap overrides
16+
border-radius: 0;
17+
border: none;
18+
font-size: 1rem;
19+
line-height: $line-height-body; // Override Boostrap, which defines a separate line-height for buttons
20+
padding: $navbar-link-padding-y 0; // Horizontal white space in nav bar between items is controlled via column gap rule on the container.
21+
22+
// Make the navbar icon links have the same size as the navbar text links
23+
height: calc(2 * $navbar-link-padding-y + $line-height-body * 1rem);
24+
}
25+
}
26+
27+
ul.navbar-icon-links {
628
display: flex;
729
flex-flow: row wrap;
830
column-gap: 1rem;
@@ -12,24 +34,6 @@
1234
margin-bottom: 0;
1335
list-style: none;
1436

15-
// Remove the padding so that we can define it with flexbox gap above
16-
li.nav-item a.nav-link {
17-
padding-left: 0;
18-
padding-right: 0;
19-
20-
@include icon-navbar-hover;
21-
22-
&:focus {
23-
color: inherit;
24-
}
25-
}
26-
27-
// Spacing and centering
28-
a span {
29-
display: flex;
30-
align-items: center;
31-
}
32-
3337
// Icons styling
3438
i {
3539
&.fa-brands,

src/pydata_sphinx_theme/assets/styles/components/_navbar-links.scss

+5-11
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,10 @@
11
/**
22
* Navigation links in the navbar and icon links
33
*/
4-
.navbar-nav,
5-
.navbar-icon-links {
4+
ul.navbar-nav {
5+
// Reduce padding of nested `ul` items a bit
66
ul {
7-
display: block;
8-
list-style: none;
9-
10-
// Reduce padding of nested `ul` items a bit
11-
ul {
12-
padding: 0 0 0 1rem;
13-
}
7+
padding: 0 0 0 1rem;
148
}
159

1610
// Navbar links - do not have an underline by default
@@ -22,8 +16,8 @@
2216
display: flex;
2317
align-items: center;
2418
height: 100%;
25-
padding-top: 0.25rem;
26-
padding-bottom: 0.25rem;
19+
padding-top: $navbar-link-padding-y;
20+
padding-bottom: $navbar-link-padding-y;
2721

2822
@include link-style-text;
2923
}

src/pydata_sphinx_theme/assets/styles/components/_search.scss

+2-20
Original file line numberDiff line numberDiff line change
@@ -67,26 +67,8 @@
6767
*/
6868

6969
// Search link icon should be a bit bigger since it is separate from icon links
70-
.search-button {
71-
display: flex;
72-
align-items: center;
73-
align-content: center;
74-
color: var(--pst-color-text-muted);
75-
padding: 0;
76-
border-radius: 0;
77-
border: none; // Override Bootstrap button border
78-
font-size: 1rem; // Override Bootstrap button font size
79-
80-
// Override Bootstrap button padding-x. Whitespace in nav bar is controlled
81-
// via column gap rule on the container.
82-
padding-left: 0;
83-
padding-right: 0;
84-
85-
@include icon-navbar-hover;
86-
87-
i {
88-
font-size: 1.3rem;
89-
}
70+
.search-button i {
71+
font-size: 1.3rem;
9072
}
9173

9274
// __search-container will only show up when we use the search pop-up bar

src/pydata_sphinx_theme/assets/styles/components/_switcher-theme.scss

+7-25
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,7 @@
33
*/
44

55
.theme-switch-button {
6-
color: var(--pst-color-text-muted);
7-
border-radius: 0;
8-
border: none; // Override Bootstrap button border
9-
font-size: 1rem; // Override Bootstrap's button font size
10-
11-
// Override Bootstrap button padding-x. Whitespace in nav bar is controlled
12-
// via column gap rule on the container.
13-
padding-left: 0;
14-
padding-right: 0;
15-
16-
&:hover {
17-
@include icon-navbar-hover;
18-
}
19-
20-
span {
6+
.theme-switch {
217
display: none;
228

239
&:active {
@@ -31,14 +17,10 @@
3117
}
3218
}
3319

34-
html[data-mode="auto"] .theme-switch-button span[data-mode="auto"] {
35-
display: flex;
36-
}
37-
38-
html[data-mode="light"] .theme-switch-button span[data-mode="light"] {
39-
display: flex;
40-
}
41-
42-
html[data-mode="dark"] .theme-switch-button span[data-mode="dark"] {
43-
display: flex;
20+
@each $mode in auto, light, dark {
21+
html[data-mode="#{$mode}"]
22+
.theme-switch-button
23+
.theme-switch[data-mode="#{$mode}"] {
24+
display: inline; // inline needed for span height to be calculated using inherited font size and line height
25+
}
4426
}

src/pydata_sphinx_theme/assets/styles/components/_switcher-version.scss

+4
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,10 @@ button.version-switcher__button,
7272
font-size: 1.1em; // A bit smaller than other menu font
7373
z-index: $zindex-modal; // higher than the sidebars
7474

75+
// Make sure it meets WCAG target size requirement no matter the version
76+
// string displayed in the button
77+
@include min-hit-area;
78+
7579
@include media-breakpoint-up($breakpoint-sidebar-primary) {
7680
font-size: unset;
7781
}

0 commit comments

Comments
 (0)