Skip to content

Commit a8b3ed1

Browse files
authored
Add button focus styles (#2007)
* Add button focus styles * Fix primary button focus styles * Fix safari fallback * Fix toggle switch snapshot * Create strange-jobs-add.md
1 parent da0cd6f commit a8b3ed1

File tree

5 files changed

+162
-63
lines changed

5 files changed

+162
-63
lines changed

.changeset/strange-jobs-add.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@primer/react": patch
3+
---
4+
5+
Add button focus styles

src/Button/styles.ts

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,17 @@ import {VariantType} from './types'
22
import {Theme} from '../ThemeProvider'
33

44
export const TEXT_ROW_HEIGHT = '20px' // custom value off the scale
5+
const focusOutlineStyles = {
6+
outline: '2px solid',
7+
outlineColor: 'accent.fg',
8+
outlineOffset: '-2px'
9+
}
10+
const fallbackFocus = {
11+
...focusOutlineStyles,
12+
':not(:focus-visible)': {
13+
outline: 'solid 1px transparent'
14+
}
15+
}
516

617
export const getVariantStyles = (variant: VariantType = 'default', theme?: Theme) => {
718
const style = {
@@ -14,8 +25,9 @@ export const getVariantStyles = (variant: VariantType = 'default', theme?: Theme
1425
},
1526
// focus must come before :active so that the active box shadow overrides
1627
'&:focus:not([disabled])': {
17-
boxShadow: `${theme?.shadows.btn.focusShadow}`
28+
...fallbackFocus
1829
},
30+
'&:focus-visible:not([disabled])': focusOutlineStyles,
1931
'&:active:not([disabled])': {
2032
backgroundColor: 'btn.activeBg',
2133
borderColor: 'btn.activeBorder'
@@ -42,7 +54,12 @@ export const getVariantStyles = (variant: VariantType = 'default', theme?: Theme
4254
},
4355
// focus must come before :active so that the active box shadow overrides
4456
'&:focus:not([disabled])': {
45-
boxShadow: `${theme?.shadows.btn.primary.focusShadow}`
57+
boxShadow: 'inset 0 0 0 3px',
58+
...fallbackFocus
59+
},
60+
'&:focus-visible:not([disabled])': {
61+
...focusOutlineStyles,
62+
boxShadow: 'inset 0 0 0 3px'
4663
},
4764
'&:active:not([disabled])': {
4865
backgroundColor: 'btn.primary.selectedBg',
@@ -80,9 +97,9 @@ export const getVariantStyles = (variant: VariantType = 'default', theme?: Theme
8097
},
8198
// focus must come before :active so that the active box shadow overrides
8299
'&:focus:not([disabled])': {
83-
borderColor: 'btn.danger.focusBorder',
84-
boxShadow: `${theme?.shadows.btn.danger.focusShadow}`
100+
...fallbackFocus
85101
},
102+
'&:focus-visible:not([disabled])': focusOutlineStyles,
86103
'&:active:not([disabled])': {
87104
color: 'btn.danger.selectedText',
88105
backgroundColor: 'btn.danger.selectedBg',
@@ -119,8 +136,9 @@ export const getVariantStyles = (variant: VariantType = 'default', theme?: Theme
119136
},
120137
// focus must come before :active so that the active box shadow overrides
121138
'&:focus:not([disabled])': {
122-
boxShadow: `${theme?.shadows.btn.focusShadow}`
139+
...fallbackFocus
123140
},
141+
'&:focus-visible:not([disabled])': focusOutlineStyles,
124142
'&:active:not([disabled])': {
125143
backgroundColor: 'btn.selectedBg'
126144
},
@@ -152,10 +170,9 @@ export const getVariantStyles = (variant: VariantType = 'default', theme?: Theme
152170
},
153171
// focus must come before :active so that the active box shadow overrides
154172
'&:focus:not([disabled])': {
155-
borderColor: 'btn.outline.focusBorder',
156-
boxShadow: `${theme?.shadows.btn.outline.focusShadow}`
173+
...fallbackFocus
157174
},
158-
175+
'&:focus-visible:not([disabled])': focusOutlineStyles,
159176
'&:active:not([disabled])': {
160177
color: 'btn.outline.selectedText',
161178
backgroundColor: 'btn.outline.selectedBg',
@@ -242,9 +259,6 @@ export const getBaseStyles = (theme?: Theme) => ({
242259
userSelect: 'none',
243260
textDecoration: 'none',
244261
textAlign: 'center',
245-
'&:focus': {
246-
outline: 'none'
247-
},
248262
'&:disabled': {
249263
cursor: 'default'
250264
},

src/__tests__/__snapshots__/ActionMenu.test.tsx.snap

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,6 @@ exports[`ActionMenu renders consistently 1`] = `
4444
box-shadow: 0 1px 0 rgba(27,31,36,0.04),inset 0 1px 0 rgba(255,255,255,0.25);
4545
}
4646
47-
.c1:focus {
48-
outline: none;
49-
}
50-
5147
.c1:disabled {
5248
cursor: default;
5349
color: #8c959f;
@@ -86,7 +82,19 @@ exports[`ActionMenu renders consistently 1`] = `
8682
}
8783
8884
.c1:focus:not([disabled]) {
89-
box-shadow: 0 0 0 3px rgba(9,105,218,0.3);
85+
outline: 2px solid;
86+
outline-color: #0969da;
87+
outline-offset: -2px;
88+
}
89+
90+
.c1:focus:not([disabled]):not(:focus-visible) {
91+
outline: solid 1px transparent;
92+
}
93+
94+
.c1:focus-visible:not([disabled]) {
95+
outline: 2px solid;
96+
outline-color: #0969da;
97+
outline-offset: -2px;
9098
}
9199
92100
.c1:active:not([disabled]) {

src/__tests__/__snapshots__/Button.test.tsx.snap

Lines changed: 80 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,6 @@ exports[`Button renders consistently 1`] = `
3333
box-shadow: 0 1px 0 rgba(27,31,36,0.04),inset 0 1px 0 rgba(255,255,255,0.25);
3434
}
3535
36-
.c0:focus {
37-
outline: none;
38-
}
39-
4036
.c0:disabled {
4137
cursor: default;
4238
color: #8c959f;
@@ -75,7 +71,19 @@ exports[`Button renders consistently 1`] = `
7571
}
7672
7773
.c0:focus:not([disabled]) {
78-
box-shadow: 0 0 0 3px rgba(9,105,218,0.3);
74+
outline: 2px solid;
75+
outline-color: #0969da;
76+
outline-offset: -2px;
77+
}
78+
79+
.c0:focus:not([disabled]):not(:focus-visible) {
80+
outline: solid 1px transparent;
81+
}
82+
83+
.c0:focus-visible:not([disabled]) {
84+
outline: 2px solid;
85+
outline-color: #0969da;
86+
outline-offset: -2px;
7987
}
8088
8189
.c0:active:not([disabled]) {
@@ -131,10 +139,6 @@ exports[`Button styles danger button appropriately 1`] = `
131139
box-shadow: undefined;
132140
}
133141
134-
.c0:focus {
135-
outline: none;
136-
}
137-
138142
.c0:disabled {
139143
cursor: default;
140144
color: btn.danger.disabledText;
@@ -186,8 +190,19 @@ exports[`Button styles danger button appropriately 1`] = `
186190
}
187191
188192
.c0:focus:not([disabled]) {
189-
border-color: btn.danger.focusBorder;
190-
box-shadow: undefined;
193+
outline: 2px solid;
194+
outline-color: accent.fg;
195+
outline-offset: -2px;
196+
}
197+
198+
.c0:focus:not([disabled]):not(:focus-visible) {
199+
outline: solid 1px transparent;
200+
}
201+
202+
.c0:focus-visible:not([disabled]) {
203+
outline: 2px solid;
204+
outline-color: accent.fg;
205+
outline-offset: -2px;
191206
}
192207
193208
.c0:active:not([disabled]) {
@@ -255,10 +270,6 @@ exports[`Button styles icon only button to make it a square 1`] = `
255270
box-shadow: undefined,undefined;
256271
}
257272
258-
.c0:focus {
259-
outline: none;
260-
}
261-
262273
.c0:disabled {
263274
cursor: default;
264275
color: primer.fg.disabled;
@@ -281,7 +292,19 @@ exports[`Button styles icon only button to make it a square 1`] = `
281292
}
282293
283294
.c0:focus:not([disabled]) {
284-
box-shadow: undefined;
295+
outline: 2px solid;
296+
outline-color: accent.fg;
297+
outline-offset: -2px;
298+
}
299+
300+
.c0:focus:not([disabled]):not(:focus-visible) {
301+
outline: solid 1px transparent;
302+
}
303+
304+
.c0:focus-visible:not([disabled]) {
305+
outline: 2px solid;
306+
outline-color: accent.fg;
307+
outline-offset: -2px;
285308
}
286309
287310
.c0:active:not([disabled]) {
@@ -358,10 +381,6 @@ exports[`Button styles invisible button appropriately 1`] = `
358381
box-shadow: none;
359382
}
360383
361-
.c0:focus {
362-
outline: none;
363-
}
364-
365384
.c0:disabled {
366385
cursor: default;
367386
color: primer.fg.disabled;
@@ -400,7 +419,19 @@ exports[`Button styles invisible button appropriately 1`] = `
400419
}
401420
402421
.c0:focus:not([disabled]) {
403-
box-shadow: undefined;
422+
outline: 2px solid;
423+
outline-color: accent.fg;
424+
outline-offset: -2px;
425+
}
426+
427+
.c0:focus:not([disabled]):not(:focus-visible) {
428+
outline: solid 1px transparent;
429+
}
430+
431+
.c0:focus-visible:not([disabled]) {
432+
outline: 2px solid;
433+
outline-color: accent.fg;
434+
outline-offset: -2px;
404435
}
405436
406437
.c0:active:not([disabled]) {
@@ -461,10 +492,6 @@ exports[`Button styles outline button appropriately 1`] = `
461492
background-color: btn.bg;
462493
}
463494
464-
.c0:focus {
465-
outline: none;
466-
}
467-
468495
.c0:disabled {
469496
cursor: default;
470497
color: btn.outline.disabledText;
@@ -516,8 +543,19 @@ exports[`Button styles outline button appropriately 1`] = `
516543
}
517544
518545
.c0:focus:not([disabled]) {
519-
border-color: btn.outline.focusBorder;
520-
box-shadow: undefined;
546+
outline: 2px solid;
547+
outline-color: accent.fg;
548+
outline-offset: -2px;
549+
}
550+
551+
.c0:focus:not([disabled]):not(:focus-visible) {
552+
outline: solid 1px transparent;
553+
}
554+
555+
.c0:focus-visible:not([disabled]) {
556+
outline: 2px solid;
557+
outline-color: accent.fg;
558+
outline-offset: -2px;
521559
}
522560
523561
.c0:active:not([disabled]) {
@@ -584,10 +622,6 @@ exports[`Button styles primary button appropriately 1`] = `
584622
box-shadow: undefined;
585623
}
586624
587-
.c0:focus {
588-
outline: none;
589-
}
590-
591625
.c0:disabled {
592626
cursor: default;
593627
color: btn.primary.disabledText;
@@ -630,7 +664,21 @@ exports[`Button styles primary button appropriately 1`] = `
630664
}
631665
632666
.c0:focus:not([disabled]) {
633-
box-shadow: undefined;
667+
box-shadow: inset 0 0 0 3px;
668+
outline: 2px solid;
669+
outline-color: accent.fg;
670+
outline-offset: -2px;
671+
}
672+
673+
.c0:focus:not([disabled]):not(:focus-visible) {
674+
outline: solid 1px transparent;
675+
}
676+
677+
.c0:focus-visible:not([disabled]) {
678+
outline: 2px solid;
679+
outline-color: accent.fg;
680+
outline-offset: -2px;
681+
box-shadow: inset 0 0 0 3px;
634682
}
635683
636684
.c0:active:not([disabled]) {

0 commit comments

Comments
 (0)