Skip to content

Commit f096065

Browse files
Standardize focus (#933)
* Fix focus outline width and offset * Remove redundant class * Remove focus animation * Standardize focus * Add standardized component classes for focus outline * Use component class for focus styles * Use standard focus outline * Add offset and inset utils * Add mint focus outline component class * Refactor button focus styles * Add component classes for outline focus * Standardize focus * Refactor component classes * Revert to use ring on focus * Format * Revert script * Fix focus ring component class * Standardize focus rings * Fix format * Add changeset * Strip down number of focus component classes * Fix format * Add comments
1 parent 9efdc87 commit f096065

File tree

10 files changed

+42
-16
lines changed

10 files changed

+42
-16
lines changed

.changeset/lucky-apples-argue.md

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'@obosbbl/grunnmuren-tailwind': patch
3+
'@obosbbl/grunnmuren-react': patch
4+
---
5+
6+
Standardizes focus styles.

packages/react/src/accordion/Accordion.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ function AccordionItem(props: AccordionItemProps, ref: Ref<HTMLDivElement>) {
123123
aria-controls={contentId}
124124
aria-expanded={isOpen}
125125
// Use outline with offset as focus indicator, this does not cover the left mint border on the expanded content and works with or without a background color on the accordion container
126-
className="flex min-h-[44px] w-full items-center justify-between gap-1.5 rounded-lg px-2 py-3.5 text-left focus-visible:outline focus-visible:outline-4 focus-visible:outline-offset-[-6px] focus-visible:outline-black"
126+
className="flex min-h-[44px] w-full items-center justify-between gap-1.5 rounded-lg px-2 py-3.5 text-left focus-visible:outline-focus focus-visible:outline-focus-inset"
127127
id={buttonId}
128128
onClick={handleOpenChange}
129129
>

packages/react/src/alertbox/Alertbox.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ const Alertbox = ({
165165
<button
166166
className={cx(
167167
'-m-2 grid h-11 w-11 place-items-center rounded-xl',
168-
'focus-visible:outline-none focus-visible:-outline-offset-8 focus-visible:outline-black',
168+
'focus-visible:outline-focus focus-visible:-outline-offset-8',
169169
)}
170170
onClick={close}
171171
aria-label={translations.close[locale]}
@@ -178,7 +178,7 @@ const Alertbox = ({
178178
className={cx(
179179
'relative col-span-full row-start-2 -my-3 inline-flex max-w-fit cursor-pointer items-center gap-1 py-3 text-sm leading-6',
180180
// Focus styles:
181-
'outline-none after:absolute after:bottom-3 after:left-0 after:right-0 after:h-0 after:bg-transparent after:transition-all after:duration-200',
181+
'outline-none after:absolute after:bottom-3 after:left-0 after:right-0 after:h-0',
182182
'focus-visible:after:h-[2px] focus-visible:after:bg-black',
183183
)}
184184
onClick={() => setIsExpanded((prevState) => !prevState)}

packages/react/src/backlink/Backlink.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ function Backlink(
3939
<Component
4040
className={cx(
4141
className,
42-
'group flex max-w-fit cursor-pointer items-center gap-3 rounded-md p-2.5 no-underline focus:outline-none data-[focus-visible]:ring data-[focus-visible]:ring-black',
42+
'group flex max-w-fit cursor-pointer items-center gap-3 rounded-md p-2.5 no-underline focus-visible:outline-focus',
4343
)}
4444
{...restProps}
4545
// @ts-expect-error ignore the type of the ref here

packages/react/src/breadcrumbs/Breadcrumb.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ function Breadcrumb(props: BreadcrumbProps, ref: Ref<HTMLLIElement>) {
3232
<Link
3333
href={href}
3434
// use outline instead of ring for focus marker that can be offset without creating a white background between the focus marker and the element content
35-
className="rounded-sm focus:outline-none group-last:no-underline data-[focus-visible]:outline data-[focus-visible]:outline-offset-2 data-[focus-visible]:outline-black"
35+
className="rounded-sm data-[focus-visible]:focus-visible:outline-focus focus:outline-none group-last:no-underline"
3636
>
3737
{children}
3838
</Link>

packages/react/src/button/Button.tsx

+4-4
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import { mergeRefs, useLayoutEffect } from '@react-aria/utils';
1414

1515
const buttonVariants = cva({
1616
base: [
17-
'inline-flex min-h-[44px] cursor-pointer items-center justify-center whitespace-nowrap rounded-lg font-medium transition-all duration-200 focus:outline-none data-[focus-visible]:ring-2 data-[focus-visible]:ring-offset-2',
17+
'inline-flex min-h-[44px] cursor-pointer items-center justify-center whitespace-nowrap rounded-lg font-medium transition-colors duration-200 focus-visible:outline-focus-offset',
1818
],
1919
variants: {
2020
/**
@@ -32,9 +32,9 @@ const buttonVariants = cva({
3232
* @default green
3333
*/
3434
color: {
35-
green: 'focus-visible:ring-black',
36-
mint: 'focus-visible:ring-mint focus-visible:ring-offset-green-dark',
37-
white: 'focus-visible:ring-white focus-visible:ring-offset-blue',
35+
green: 'focus-visible:outline-focus',
36+
mint: 'focus-visible:outline-focus focus-visible:outline-mint',
37+
white: 'focus-visible:outline-focus focus-visible:outline-white',
3838
},
3939
/**
4040
* When the button is without text, but with a single icon.

packages/react/src/checkbox/Checkbox.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ function CheckmarkBox() {
2929
// selected
3030
'group-data-[selected]:!border-green group-data-[selected]:!bg-green',
3131
// focus
32-
'group-data-[focus-visible]:ring-2 group-data-[focus-visible]:ring-black group-data-[focus-visible]:ring-offset-[9px]',
32+
'group-data-[focus-visible]:outline-focus-offset',
3333
// hovered
3434
'group-data-[hovered]:border-green group-data-[hovered]:group-data-[invalid]:border-red group-data-[hovered]:bg-green-lightest group-data-[hovered]:group-data-[invalid]:bg-red-light',
3535
// invalid - The border is 1 px thicker when invalid. We don't actually want to change the border width, as that causes the element's size to change

packages/react/src/classes.ts

+5-5
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,16 @@ const input = cva({
1515
'box-content min-h-6 py-2.5',
1616
'rounded-md text-base font-normal leading-6 placeholder-[#727070] outline-none ring-1 ring-black',
1717
// invalid styles
18-
'group-data-[invalid]:ring-2 group-data-[invalid]:ring-red',
18+
'group-data-[invalid]:ring-focus group-data-[invalid]:ring-red',
1919
// Fix invisible ring on safari: https://github.com/tailwindlabs/tailwindcss.com/issues/1135
2020
'appearance-none',
2121
],
2222
variants: {
2323
// Focus rings. Can either be :focus or :focus-visible based on the needs of the particular component.
2424
focusModifier: {
25-
focus: 'focus:ring-2 group-data-[invalid]:focus:ring',
25+
focus: 'focus:ring-focus group-data-[invalid]:focus:ring',
2626
visible:
27-
'data-[focus-visible]:ring-2 group-data-[invalid]:data-[focus-visible]:ring',
27+
'data-[focus-visible]:ring-focus group-data-[invalid]:data-[focus-visible]:ring',
2828
},
2929
isGrouped: {
3030
false: 'px-3',
@@ -38,8 +38,8 @@ const input = cva({
3838
});
3939

4040
const inputGroup = cx([
41-
'inline-flex items-center gap-3 overflow-hidden rounded-md bg-white px-3 text-base ring-1 ring-black focus-within:ring-2',
42-
'group-data-[invalid]:ring-2 group-data-[invalid]:ring-red group-data-[invalid]:focus-within:ring',
41+
'inline-flex items-center gap-3 overflow-hidden rounded-md bg-white px-3 text-base ring-1 ring-black focus-within:ring-focus',
42+
'group-data-[invalid]:ring-focus group-data-[invalid]:ring-red group-data-[invalid]:focus-within:ring',
4343
]);
4444

4545
const dropdown = {

packages/react/src/radiogroup/Radio.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ const defaultClasses = cx([
2121
// hover
2222
'data-[hovered]:before:border-green data-[hovered]:before:bg-green-lightest data-[hovered]:data-[invalid]:before:bg-red-light',
2323
// focus
24-
'data-[focus-visible]:before:ring data-[focus-visible]:before:ring-black data-[focus-visible]:before:ring-offset-[9px]',
24+
'data-[focus-visible]:before:ring-focus-offset',
2525
// invalid - The border is 1 px thicker when invalid. We don't actually want to change the border width, as that causes the element's size to change
2626
// so we use an inner outline to artifically pad the border
2727
'data-[invalid]:before:outline-solid data-[invalid]:before:border-red data-[invalid]:data-[selected]:before:!bg-red data-[invalid]:before:outline data-[invalid]:before:outline-[3px] data-[invalid]:before:outline-offset-[-3px] data-[invalid]:before:outline-red',

packages/tailwind/tailwind-base.cjs

+20
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,26 @@ module.exports = (options = {}) => {
333333
'.description': {
334334
[description]: {},
335335
},
336+
/** Standard black focus outline */
337+
'.outline-focus': {
338+
'@apply outline outline-2 outline-black': {},
339+
},
340+
/** Standard black focus outline with offset */
341+
'.outline-focus-offset': {
342+
'@apply outline-focus outline-offset-2': {},
343+
},
344+
/** Standard black focus outline with negative offset (inset) */
345+
'.outline-focus-inset': {
346+
'@apply outline-focus -outline-offset-4': {},
347+
},
348+
/** Standard black focus ring */
349+
'.ring-focus': {
350+
'@apply ring-2 ring-black': {},
351+
},
352+
/** Standard black focus ring with offset */
353+
'.ring-focus-offset': {
354+
'@apply ring-focus ring-offset-2': {},
355+
},
336356
});
337357
}),
338358
plugin(function ({ addBase }) {

0 commit comments

Comments
 (0)