@microbit/ui 0.1.0-alpha.9 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (113) hide show
  1. package/LICENSE.md +8 -0
  2. package/README.md +171 -20
  3. package/lang/ui.ar.json +62 -0
  4. package/lang/ui.ca.json +43 -3
  5. package/lang/ui.cy.json +62 -0
  6. package/lang/ui.de.json +44 -4
  7. package/lang/ui.en-us.json +40 -0
  8. package/lang/ui.en.json +40 -0
  9. package/lang/ui.es-es.json +43 -3
  10. package/lang/ui.fr.json +44 -4
  11. package/lang/ui.ga-ie.json +43 -3
  12. package/lang/ui.it.json +62 -0
  13. package/lang/ui.ja.json +44 -4
  14. package/lang/ui.ko.json +44 -4
  15. package/lang/ui.lo.json +62 -0
  16. package/lang/ui.lol.json +40 -0
  17. package/lang/ui.nl.json +44 -4
  18. package/lang/ui.pl.json +44 -4
  19. package/lang/ui.pt-br.json +44 -4
  20. package/lang/ui.vi.json +62 -0
  21. package/lang/ui.zh-cn.json +44 -4
  22. package/lang/ui.zh-tw.json +44 -4
  23. package/package.json +13 -11
  24. package/src/Avatar.recipe.ts +191 -0
  25. package/src/Avatar.tsx +278 -0
  26. package/src/Breadcrumb.recipe.ts +45 -0
  27. package/src/Breadcrumb.tsx +114 -0
  28. package/src/Button.recipe.ts +88 -50
  29. package/src/Button.tsx +67 -14
  30. package/src/ButtonGroup.tsx +37 -15
  31. package/src/Card.recipe.ts +1 -2
  32. package/src/Card.tsx +2 -1
  33. package/src/Checkbox.recipe.ts +13 -12
  34. package/src/Checkbox.tsx +103 -34
  35. package/src/CheckboxGroup.tsx +70 -0
  36. package/src/CloseButton.tsx +3 -3
  37. package/src/CloseIcon.tsx +6 -4
  38. package/src/Code.tsx +1 -1
  39. package/src/Collapse.tsx +13 -14
  40. package/src/ComboBox.tsx +246 -0
  41. package/src/Divider.tsx +40 -7
  42. package/src/Drawer.recipe.ts +21 -10
  43. package/src/Drawer.tsx +3 -4
  44. package/src/ExternalLink.tsx +43 -0
  45. package/src/Fade.tsx +18 -4
  46. package/src/Field.recipe.ts +114 -0
  47. package/src/Field.tsx +187 -0
  48. package/src/GridList.recipe.ts +57 -0
  49. package/src/GridList.tsx +81 -0
  50. package/src/Heading.recipe.ts +20 -1
  51. package/src/Heading.tsx +3 -3
  52. package/src/Icon.tsx +23 -7
  53. package/src/IconButton.tsx +8 -18
  54. package/src/Image.tsx +1 -1
  55. package/src/Input.recipe.ts +30 -26
  56. package/src/Input.tsx +16 -7
  57. package/src/InputGroup.tsx +9 -9
  58. package/src/Kbd.tsx +1 -1
  59. package/src/Link.tsx +3 -3
  60. package/src/LinkBox.tsx +2 -3
  61. package/src/LinkButton.tsx +14 -13
  62. package/src/List.tsx +5 -7
  63. package/src/ListBox.recipe.ts +43 -0
  64. package/src/ListBox.tsx +88 -0
  65. package/src/Menu.recipe.ts +22 -19
  66. package/src/Menu.tsx +54 -28
  67. package/src/Modal.recipe.ts +17 -9
  68. package/src/Modal.tsx +123 -24
  69. package/src/MoreMenuButton.tsx +63 -0
  70. package/src/NativeSelect.tsx +37 -14
  71. package/src/NativeSelectField.tsx +84 -0
  72. package/src/NumberField.recipe.ts +56 -15
  73. package/src/NumberField.tsx +74 -22
  74. package/src/PopoverArrow.tsx +1 -2
  75. package/src/ProgressBar.tsx +3 -5
  76. package/src/Radio.recipe.ts +13 -12
  77. package/src/Radio.tsx +2 -29
  78. package/src/RadioGroup.tsx +68 -0
  79. package/src/Select.recipe.ts +211 -0
  80. package/src/Select.tsx +194 -0
  81. package/src/SharedUIProvider.tsx +63 -7
  82. package/src/Skeleton.tsx +146 -0
  83. package/src/Slide.tsx +2 -2
  84. package/src/Slider.recipe.ts +27 -16
  85. package/src/Slider.tsx +20 -12
  86. package/src/Spinner.tsx +11 -9
  87. package/src/Svg.tsx +2 -3
  88. package/src/Switch.recipe.ts +40 -21
  89. package/src/Switch.tsx +35 -5
  90. package/src/Text.recipe.ts +26 -0
  91. package/src/Text.tsx +6 -2
  92. package/src/TextField.tsx +35 -38
  93. package/src/Toast.recipe.ts +52 -15
  94. package/src/Toast.tsx +147 -48
  95. package/src/Tooltip.recipe.ts +32 -0
  96. package/src/Tooltip.tsx +68 -28
  97. package/src/TooltipButton.tsx +229 -0
  98. package/src/UnmountCallback.tsx +2 -2
  99. package/src/VisuallyHidden.tsx +1 -1
  100. package/src/base-preset.ts +323 -84
  101. package/src/base-tokens.ts +0 -3
  102. package/src/button-icon.ts +4 -5
  103. package/src/data-attrs.ts +16 -0
  104. package/src/dense-preset.ts +107 -0
  105. package/src/hooks/useBreakpointValue.ts +4 -4
  106. package/src/hooks/useClipboard.ts +5 -6
  107. package/src/hooks/useDisclosure.ts +31 -0
  108. package/src/hooks/useMediaQuery.ts +3 -4
  109. package/src/hooks/usePrevious.ts +1 -4
  110. package/src/index.ts +19 -3
  111. package/src/rac-locale.ts +33 -0
  112. package/src/system.ts +22 -1
  113. package/src/TextField.recipe.ts +0 -54
@@ -6,9 +6,8 @@
6
6
  import { defineSlotRecipe } from "@pandacss/dev";
7
7
 
8
8
  /**
9
- * Switch slot recipe Chakra's switch with the default blue colorScheme
10
- * (light mode) and its sm/md/lg size scale. The track has a 2px inset; the
11
- * thumb matches the track height and slides by the track width/height
9
+ * Switch slot recipe with an sm/md/lg size scale. The track has a 2px inset;
10
+ * the thumb matches the track height and slides by the track width/height
12
11
  * difference when selected.
13
12
  *
14
13
  * State styling keys off data attributes stamped by the shared-ui Switch
@@ -43,7 +42,7 @@ export const switchRecipe = defineSlotRecipe({
43
42
  bg: "controlCheckedBg",
44
43
  },
45
44
  "&[data-focus-visible]": {
46
- focusShadow: "outline",
45
+ focusRing: "outline",
47
46
  },
48
47
  "&[data-disabled]": { opacity: 0.4 },
49
48
  // Forced-colors modes strip author backgrounds, flattening track and
@@ -60,6 +59,19 @@ export const switchRecipe = defineSlotRecipe({
60
59
  transitionProperty: "transform",
61
60
  transitionDuration: "normal",
62
61
  borderRadius: "inherit",
62
+ // The thumb rests at the track's start and slides to its end, so the
63
+ // travel has to reverse in RTL — a transform is physical however the
64
+ // rest of the switch is laid out. Distance is per size, below.
65
+ // Fallbacks carry a unit: `calc(-1 * 0)` is a number, which is invalid
66
+ // where a length is wanted, and would drop the RTL transform whole.
67
+ "&[data-selected]": {
68
+ transform: "translateX(var(--switch-travel, 0px))",
69
+ },
70
+ _rtl: {
71
+ "&[data-selected]": {
72
+ transform: "translateX(calc(-1 * var(--switch-travel, 0px)))",
73
+ },
74
+ },
63
75
  _forcedColors: {
64
76
  bg: "ButtonText",
65
77
  "&[data-selected]": { bg: "SelectedItemText" },
@@ -72,36 +84,43 @@ export const switchRecipe = defineSlotRecipe({
72
84
  },
73
85
  },
74
86
  variants: {
75
- // Chakra's Switch size scale (track width x height; the selected-thumb
76
- // translate is the difference between them).
87
+ // `start` puts the label first and the control at the row's end — the
88
+ // settings-row pattern (a preference name beside its switch), the toggle
89
+ // counterpart of the field recipe's `labelPosition="side"`. Values are
90
+ // start/end rather than top/side because the default label is already
91
+ // beside the control, after it. The label keeps an end margin so a long
92
+ // translation can't butt against the track (the SelectFormControl
93
+ // lesson — see the field-chrome roadmap bullet).
94
+ labelPosition: {
95
+ end: {},
96
+ start: {
97
+ root: {
98
+ flexDirection: "row-reverse",
99
+ justifyContent: "space-between",
100
+ width: "100%",
101
+ },
102
+ label: { marginStart: "0", marginEnd: "3" },
103
+ },
104
+ },
105
+ // Track width x height per size; the thumb's travel is the difference
106
+ // between them.
77
107
  size: {
78
108
  sm: {
79
109
  track: { width: "1.375rem", height: "3" },
80
- thumb: {
81
- width: "3",
82
- height: "3",
83
- "&[data-selected]": { transform: "translateX(0.625rem)" },
84
- },
110
+ thumb: { width: "3", height: "3", "--switch-travel": "0.625rem" },
85
111
  },
86
112
  md: {
87
113
  track: { width: "1.875rem", height: "4" },
88
- thumb: {
89
- width: "4",
90
- height: "4",
91
- "&[data-selected]": { transform: "translateX(0.875rem)" },
92
- },
114
+ thumb: { width: "4", height: "4", "--switch-travel": "0.875rem" },
93
115
  },
94
116
  lg: {
95
117
  track: { width: "2.875rem", height: "6" },
96
- thumb: {
97
- width: "6",
98
- height: "6",
99
- "&[data-selected]": { transform: "translateX(1.375rem)" },
100
- },
118
+ thumb: { width: "6", height: "6", "--switch-travel": "1.375rem" },
101
119
  },
102
120
  },
103
121
  },
104
122
  defaultVariants: {
105
123
  size: "md",
124
+ labelPosition: "end",
106
125
  },
107
126
  });
package/src/Switch.tsx CHANGED
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * SPDX-License-Identifier: MIT
5
5
  */
6
- import { ReactNode } from "react";
6
+ import { ReactNode, useId } from "react";
7
7
  import {
8
8
  Switch as RACSwitch,
9
9
  SwitchProps as RACSwitchProps,
@@ -11,6 +11,7 @@ import {
11
11
  import { css, cx } from "styled-system/css";
12
12
  import { switchRecipe, SwitchRecipeVariantProps } from "styled-system/recipes";
13
13
  import { SystemStyleObject } from "styled-system/types";
14
+ import { FieldHelperText } from "./Field";
14
15
 
15
16
  export interface SwitchProps
16
17
  extends Omit<RACSwitchProps, "className" | "children" | "style">,
@@ -19,24 +20,42 @@ export interface SwitchProps
19
20
  css?: SystemStyleObject;
20
21
  className?: string;
21
22
  children?: ReactNode;
23
+ /**
24
+ * Help text below the switch, wired to its `aria-describedby` — the same
25
+ * chrome the labelled fields' `helperText` renders. With it the component
26
+ * gains a wrapping `<div>`, so the switch-plus-text moves as one block.
27
+ */
28
+ helperText?: ReactNode;
29
+ /** Per-instance style overrides for the helper text. */
30
+ helperTextCss?: SystemStyleObject;
22
31
  }
23
32
 
24
33
  /**
25
- * Switch — react-aria-components <Switch> styled like Chakra's switch.
26
- * Children render as the label; pass `aria-label` for label-less switches.
34
+ * Switch — a styled react-aria-components <Switch>. Children render as the
35
+ * label; pass `aria-label` for label-less switches. `labelPosition="start"`
36
+ * is the settings-row layout: label first, switch at the row's end.
27
37
  */
28
38
  export const Switch = ({
29
39
  size,
40
+ labelPosition,
30
41
  css: cssProp,
31
42
  className,
32
43
  children,
44
+ helperText,
45
+ helperTextCss,
33
46
  ...rest
34
47
  }: SwitchProps) => {
35
- const slots = switchRecipe({ size });
36
- return (
48
+ const slots = switchRecipe({ size, labelPosition });
49
+ const helperId = useId();
50
+ const describedBy =
51
+ [rest["aria-describedby"], helperText != null ? helperId : undefined]
52
+ .filter(Boolean)
53
+ .join(" ") || undefined;
54
+ const switchElement = (
37
55
  <RACSwitch
38
56
  className={cx(slots.root, cssProp ? css(cssProp) : undefined, className)}
39
57
  {...rest}
58
+ aria-describedby={describedBy}
40
59
  >
41
60
  {({ isSelected, isFocusVisible, isDisabled }) => {
42
61
  const state = {
@@ -59,4 +78,15 @@ export const Switch = ({
59
78
  }}
60
79
  </RACSwitch>
61
80
  );
81
+ if (helperText == null) {
82
+ return switchElement;
83
+ }
84
+ return (
85
+ <div>
86
+ {switchElement}
87
+ <FieldHelperText id={helperId} css={helperTextCss}>
88
+ {helperText}
89
+ </FieldHelperText>
90
+ </div>
91
+ );
62
92
  };
@@ -0,0 +1,26 @@
1
+ /**
2
+ * (c) 2026, Micro:bit Educational Foundation and contributors
3
+ *
4
+ * SPDX-License-Identifier: MIT
5
+ */
6
+ import { defineRecipe } from "@pandacss/dev";
7
+
8
+ /**
9
+ * Text recipe — sizes only, no default: with no `size`, Text inherits, which
10
+ * is what most family apps expect. A config recipe so an app preset can set
11
+ * an app-wide default size by extending `defaultVariants` (python-editor
12
+ * defaults to md, which its shrunken fontSize scale renders at 0.9rem).
13
+ *
14
+ * Registered in the base preset (base-preset.ts).
15
+ */
16
+ export const text = defineRecipe({
17
+ className: "text",
18
+ jsx: ["Text"],
19
+ variants: {
20
+ size: {
21
+ sm: { fontSize: "sm" },
22
+ md: { fontSize: "md" },
23
+ lg: { fontSize: "lg" },
24
+ },
25
+ },
26
+ });
package/src/Text.tsx CHANGED
@@ -4,9 +4,13 @@
4
4
  * SPDX-License-Identifier: MIT
5
5
  */
6
6
  import { styled } from "styled-system/jsx";
7
+ import { text } from "styled-system/recipes";
7
8
 
8
9
  /**
9
10
  * Text — a paragraph that accepts Panda style props. Use `as` to render a
10
- * different element (`span`, `div`, `h2`). Replaces Chakra's <Text>.
11
+ * different element (`span`, `div`, `h2`).
12
+ * Backed by the `text` config recipe: `size` picks a type-scale size, and an
13
+ * app preset can extend the recipe's `defaultVariants` to give all its Text
14
+ * a default size (see Text.recipe.ts).
11
15
  */
12
- export const Text = styled("p");
16
+ export const Text = styled("p", text);
package/src/TextField.tsx CHANGED
@@ -5,84 +5,81 @@
5
5
  */
6
6
  import { FocusEvent, forwardRef, ReactNode } from "react";
7
7
  import {
8
- FieldError,
9
8
  Input as RACInput,
10
- Label as RACLabel,
11
- Text as RACText,
12
9
  TextField as RACTextField,
13
10
  TextFieldProps as RACTextFieldProps,
14
11
  } from "react-aria-components";
15
- import { css, cx } from "styled-system/css";
16
12
  import { field, input, InputVariantProps } from "styled-system/recipes";
17
13
  import { SystemStyleObject } from "styled-system/types";
14
+ import {
15
+ FieldLabel,
16
+ FieldLayoutProps,
17
+ FieldSupport,
18
+ FieldSupportProps,
19
+ } from "./Field";
18
20
 
19
21
  export interface TextFieldProps
20
22
  extends Omit<
21
23
  RACTextFieldProps,
22
24
  "className" | "children" | "style" | "onFocus" | "onBlur"
23
25
  >,
24
- InputVariantProps {
25
- /** Visible label (Chakra's FormLabel; asterisk added when `isRequired`). */
26
+ InputVariantProps,
27
+ FieldSupportProps,
28
+ FieldLayoutProps {
29
+ /** Visible label (asterisk added when `isRequired`). */
26
30
  label: ReactNode;
27
- /** Help text below the input (Chakra's FormHelperText). */
28
- helperText?: ReactNode;
29
- /** Shown below the input when `isInvalid` (Chakra's FormErrorMessage). */
30
- errorMessage?: ReactNode;
31
- /** Per-instance style overrides for the helper text. */
32
- helperTextCss?: SystemStyleObject;
31
+ /** Label style overrides. */
32
+ labelCss?: SystemStyleObject;
33
33
  onFocus?: (e: FocusEvent<HTMLInputElement>) => void;
34
34
  /** Input autocapitalize attribute (react-aria's TextField omits it). */
35
35
  autoCapitalize?: "off" | "none" | "on" | "sentences" | "words" | "characters";
36
36
  }
37
37
 
38
38
  /**
39
- * TextField — a labelled single-line text input, collapsing Chakra's
40
- * FormControl/FormLabel/Input/FormHelperText/FormErrorMessage. The ref is
41
- * forwarded to the input element.
39
+ * TextField — a labelled single-line text input with optional help and error
40
+ * text. The ref is forwarded to the input element.
42
41
  */
43
42
  export const TextField = forwardRef<HTMLInputElement, TextFieldProps>(
44
43
  function TextField(
45
44
  {
46
45
  label,
46
+ labelCss,
47
47
  helperText,
48
48
  errorMessage,
49
49
  helperTextCss,
50
+ labelPosition,
50
51
  onFocus,
51
52
  autoCapitalize,
52
- size,
53
- ...rest
53
+ ...props
54
54
  },
55
55
  ref,
56
56
  ) {
57
- const slots = field();
57
+ // As Input: forward every recipe variant group, not just `size`, so a
58
+ // preset that adds one keeps working.
59
+ const [variantProps, rest] = input.splitVariantProps(props);
60
+ const slots = field({ size: variantProps.size, labelPosition });
58
61
  return (
59
62
  <RACTextField {...rest} className={slots.root}>
60
- <RACLabel className={slots.label}>
63
+ <FieldLabel
64
+ size={variantProps.size}
65
+ labelPosition={labelPosition}
66
+ isRequired={rest.isRequired}
67
+ css={labelCss}
68
+ >
61
69
  {label}
62
- {rest.isRequired ? (
63
- <span aria-hidden className={slots.requiredIndicator}>
64
- *
65
- </span>
66
- ) : null}
67
- </RACLabel>
70
+ </FieldLabel>
68
71
  <RACInput
69
72
  ref={ref}
70
- className={input({ size })}
73
+ className={input(variantProps)}
71
74
  onFocus={onFocus}
72
75
  autoCapitalize={autoCapitalize}
73
76
  />
74
- {helperText && (
75
- <RACText
76
- slot="description"
77
- className={cx(
78
- slots.helperText,
79
- helperTextCss ? css(helperTextCss) : undefined,
80
- )}
81
- >
82
- {helperText}
83
- </RACText>
84
- )}
85
- <FieldError className={slots.errorMessage}>{errorMessage}</FieldError>
77
+ <FieldSupport
78
+ helperText={helperText}
79
+ errorMessage={errorMessage}
80
+ helperTextCss={helperTextCss}
81
+ labelPosition={labelPosition}
82
+ />
86
83
  </RACTextField>
87
84
  );
88
85
  },
@@ -6,10 +6,9 @@
6
6
  import { defineSlotRecipe } from "@pandacss/dev";
7
7
 
8
8
  /**
9
- * Toast slot recipe — the Chakra-era solid Alert restyled as a toast (white
10
- * text, teal for every status except error). Status colours come from the
11
- * `toast*Bg` semantic tokens so brand presets can retune them without
12
- * touching the recipe.
9
+ * Toast slot recipe — a solid alert card with white text, teal for every
10
+ * status except error. Status colours come from the `toast*Bg` semantic
11
+ * tokens so brand presets can retune them without touching the recipe.
13
12
  *
14
13
  * Registered in the base preset (base-preset.ts). The status variant
15
14
  * is chosen at runtime from the toast content, so it needs the preset's
@@ -17,13 +16,36 @@ import { defineSlotRecipe } from "@pandacss/dev";
17
16
  */
18
17
  export const toast = defineSlotRecipe({
19
18
  className: "toast",
20
- slots: ["region", "root", "icon", "title", "description", "closeButton"],
19
+ slots: [
20
+ "region",
21
+ "root",
22
+ "content",
23
+ "body",
24
+ "icon",
25
+ "title",
26
+ "description",
27
+ "closeButton",
28
+ ],
21
29
  base: {
22
30
  region: {
23
31
  position: "fixed",
24
32
  top: "4",
25
33
  left: "50%",
26
34
  transform: "translateX(-50%)",
35
+ // Percentage centring of a text-width region lands the toasts on a
36
+ // fractional pixel. Chrome floors ::view-transition-group's transform to
37
+ // a whole CSS pixel, so the snapshot the enter/exit animation displays
38
+ // sits up to 1px left of the element it stands in for, and every toast
39
+ // jumps right as the transition hands back to the real DOM (visible
40
+ // above 1x, where the two no longer round to the same device pixel).
41
+ // Rounding the region's own offsets keeps it on whole pixels so the two
42
+ // agree. round() postdates view transitions in Chrome (130 vs 111), so
43
+ // the guard keeps the plain centring for the versions in between rather
44
+ // than dropping the declarations and losing it altogether.
45
+ "@supports (left: round(50%, 1px))": {
46
+ left: "round(50%, 1px)",
47
+ transform: "translateX(round(-50%, 1px))",
48
+ },
27
49
  zIndex: "toast",
28
50
  display: "flex",
29
51
  flexDirection: "column",
@@ -34,18 +56,35 @@ export const toast = defineSlotRecipe({
34
56
  position: "relative",
35
57
  display: "flex",
36
58
  alignItems: "flex-start",
37
- gap: "3",
38
59
  p: "4",
39
60
  // Leave room for the absolutely-positioned close button plus breathing
40
61
  // space so its hover area never overlaps the title text (24px button at
41
- // 4px inset spans ~4-28px from the right; content stops at 40px for a
42
- // ~12px gap).
43
- paddingRight: "10",
62
+ // 4px inset spans ~4-28px from the end edge; content stops at 40px for
63
+ // a ~12px gap). Logical, like the button's own inset.
64
+ paddingEnd: "10",
44
65
  borderRadius: "md",
45
66
  boxShadow: "lg",
46
67
  color: "white",
47
68
  maxW: "sm",
48
69
  pointerEvents: "auto",
70
+ // The card is focusable (RAC gives it tabindex), so it takes the
71
+ // house ring rather than the UA default. Ink, because the ring is
72
+ // drawn on the page, not the card — Toast.tsx tags the close button.
73
+ outline: "none",
74
+ _focusVisible: { focusRing: "outline" },
75
+ },
76
+ // The icon has to be inside the alert region to be announced with the
77
+ // message, so this row — not the root — is what lays the two out.
78
+ content: {
79
+ display: "flex",
80
+ alignItems: "flex-start",
81
+ gap: "3",
82
+ // Let the text shrink below its content width so long words wrap
83
+ // instead of pushing past the toast's maxW.
84
+ minWidth: 0,
85
+ },
86
+ body: {
87
+ minWidth: 0,
49
88
  },
50
89
  icon: {
51
90
  fontSize: "1.25rem",
@@ -58,9 +97,8 @@ export const toast = defineSlotRecipe({
58
97
  mt: "1",
59
98
  },
60
99
  closeButton: {
61
- // A sized box with the glyph centred (like Chakra's CloseButton size
62
- // "sm": 24px box, 2xs glyph) gives padding around the X and a hover
63
- // affordance.
100
+ // A sized box with the glyph centred (24px box, 2xs glyph) gives
101
+ // padding around the X and a hover affordance.
64
102
  position: "absolute",
65
103
  top: "1",
66
104
  insetEnd: "1",
@@ -79,11 +117,10 @@ export const toast = defineSlotRecipe({
79
117
  outline: "none",
80
118
  transitionProperty: "background-color, box-shadow",
81
119
  transitionDuration: "normal",
82
- // Chakra's CloseButton hover is a subtle dark overlay (blackAlpha) in
83
- // light mode, not a bright highlight.
120
+ // Hover is a subtle dark overlay (blackAlpha), not a bright highlight.
84
121
  _hover: { bg: "blackAlpha.100" },
85
122
  _active: { bg: "blackAlpha.200" },
86
- _focusVisible: { focusShadow: "outline" },
123
+ _focusVisible: { focusRing: "outline" },
87
124
  },
88
125
  },
89
126
  variants: {