@microbit/ui 0.1.0-alpha.3

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 (67) hide show
  1. package/LICENSE.md +21 -0
  2. package/README.md +118 -0
  3. package/lang/ui.ca.json +22 -0
  4. package/lang/ui.en-us.json +22 -0
  5. package/lang/ui.en.json +22 -0
  6. package/lang/ui.es-es.json +22 -0
  7. package/lang/ui.fr.json +22 -0
  8. package/lang/ui.ja.json +22 -0
  9. package/lang/ui.ko.json +22 -0
  10. package/lang/ui.lol.json +22 -0
  11. package/lang/ui.nl.json +22 -0
  12. package/lang/ui.pl.json +22 -0
  13. package/lang/ui.pt-br.json +22 -0
  14. package/lang/ui.zh-tw.json +22 -0
  15. package/package.json +54 -0
  16. package/src/Button.recipe.ts +162 -0
  17. package/src/Button.tsx +84 -0
  18. package/src/ButtonGroup.tsx +63 -0
  19. package/src/Card.recipe.ts +49 -0
  20. package/src/Card.tsx +56 -0
  21. package/src/Checkbox.recipe.ts +71 -0
  22. package/src/Checkbox.tsx +66 -0
  23. package/src/CloseButton.tsx +87 -0
  24. package/src/CloseIcon.tsx +40 -0
  25. package/src/Divider.tsx +21 -0
  26. package/src/Drawer.recipe.ts +98 -0
  27. package/src/Drawer.tsx +138 -0
  28. package/src/Heading.recipe.ts +52 -0
  29. package/src/Heading.tsx +14 -0
  30. package/src/Icon.tsx +55 -0
  31. package/src/IconButton.tsx +39 -0
  32. package/src/Image.tsx +11 -0
  33. package/src/Input.recipe.ts +63 -0
  34. package/src/Input.tsx +33 -0
  35. package/src/InputGroup.tsx +48 -0
  36. package/src/Link.tsx +23 -0
  37. package/src/LinkBox.tsx +88 -0
  38. package/src/List.tsx +27 -0
  39. package/src/Menu.recipe.ts +88 -0
  40. package/src/Menu.tsx +172 -0
  41. package/src/Modal.recipe.ts +163 -0
  42. package/src/Modal.tsx +265 -0
  43. package/src/NativeSelect.tsx +76 -0
  44. package/src/PopoverArrow.tsx +52 -0
  45. package/src/ProgressBar.tsx +61 -0
  46. package/src/SharedUIProvider.tsx +55 -0
  47. package/src/Slide.tsx +54 -0
  48. package/src/Slider.recipe.ts +91 -0
  49. package/src/Slider.tsx +99 -0
  50. package/src/Spinner.tsx +69 -0
  51. package/src/Svg.tsx +23 -0
  52. package/src/Switch.recipe.ts +80 -0
  53. package/src/Switch.tsx +60 -0
  54. package/src/Text.tsx +12 -0
  55. package/src/TextField.recipe.ts +54 -0
  56. package/src/TextField.tsx +72 -0
  57. package/src/Toast.recipe.ts +98 -0
  58. package/src/Toast.tsx +165 -0
  59. package/src/Tooltip.tsx +91 -0
  60. package/src/UnmountCallback.tsx +18 -0
  61. package/src/VisuallyHidden.tsx +14 -0
  62. package/src/base-preset.ts +278 -0
  63. package/src/chakra-tokens.ts +1014 -0
  64. package/src/hooks/useBreakpointValue.ts +63 -0
  65. package/src/index.ts +45 -0
  66. package/src/messages.ts +17 -0
  67. package/src/system.ts +36 -0
package/src/Button.tsx ADDED
@@ -0,0 +1,84 @@
1
+ /**
2
+ * (c) 2026, Micro:bit Educational Foundation and contributors
3
+ *
4
+ * SPDX-License-Identifier: MIT
5
+ */
6
+ import { forwardRef, ReactNode } from "react";
7
+ import {
8
+ Button as RACButton,
9
+ ButtonProps as RACButtonProps,
10
+ } from "react-aria-components";
11
+ import { css, cva, cx } from "styled-system/css";
12
+ import { button, ButtonVariantProps } from "styled-system/recipes";
13
+ import { SystemStyleObject } from "styled-system/types";
14
+
15
+ // Chakra's ButtonIcon: keeps the glyph centred and spaced from the label
16
+ // (iconSpacing 0.5rem).
17
+ const buttonIcon = cva({
18
+ base: {
19
+ display: "inline-flex",
20
+ alignSelf: "center",
21
+ flexShrink: 0,
22
+ },
23
+ variants: {
24
+ side: {
25
+ left: { marginEnd: "2" },
26
+ right: { marginStart: "2" },
27
+ },
28
+ },
29
+ });
30
+
31
+ export interface ButtonProps
32
+ extends Omit<RACButtonProps, "className" | "children">,
33
+ ButtonVariantProps {
34
+ /** Per-instance style overrides, merged after the recipe. */
35
+ css?: SystemStyleObject;
36
+ className?: string;
37
+ /** Icon rendered before the label, matching Chakra's `leftIcon`. */
38
+ leftIcon?: ReactNode;
39
+ /** Icon rendered after the label, matching Chakra's `rightIcon`. */
40
+ rightIcon?: ReactNode;
41
+ children?: ReactNode;
42
+ }
43
+
44
+ /**
45
+ * Button — react-aria-components <Button> styled with the `button` config
46
+ * recipe. The recipe's interaction states are driven by RAC's data attributes
47
+ * (see the preset's widened conditions), so disabled/hover/press/focus all work
48
+ * without extra wiring.
49
+ */
50
+ export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
51
+ function Button(
52
+ {
53
+ variant,
54
+ size,
55
+ css: cssProp,
56
+ className,
57
+ leftIcon,
58
+ rightIcon,
59
+ children,
60
+ ...rest
61
+ },
62
+ ref,
63
+ ) {
64
+ return (
65
+ <RACButton
66
+ ref={ref}
67
+ className={cx(
68
+ button({ variant, size }),
69
+ cssProp ? css(cssProp) : undefined,
70
+ className,
71
+ )}
72
+ {...rest}
73
+ >
74
+ {leftIcon ? (
75
+ <span className={buttonIcon({ side: "left" })}>{leftIcon}</span>
76
+ ) : null}
77
+ {children}
78
+ {rightIcon ? (
79
+ <span className={buttonIcon({ side: "right" })}>{rightIcon}</span>
80
+ ) : null}
81
+ </RACButton>
82
+ );
83
+ },
84
+ );
@@ -0,0 +1,63 @@
1
+ /**
2
+ * (c) 2026, Micro:bit Educational Foundation and contributors
3
+ *
4
+ * SPDX-License-Identifier: MIT
5
+ */
6
+ import { forwardRef, HTMLAttributes } from "react";
7
+ import { css, cx } from "styled-system/css";
8
+ import { SystemStyleObject } from "styled-system/types";
9
+
10
+ export interface ButtonGroupProps extends HTMLAttributes<HTMLDivElement> {
11
+ /**
12
+ * Square the inner radii so adjacent buttons form one control (Chakra's
13
+ * `isAttached`). Otherwise buttons get a small gap.
14
+ */
15
+ isAttached?: boolean;
16
+ /** Per-instance style overrides. */
17
+ css?: SystemStyleObject;
18
+ className?: string;
19
+ }
20
+
21
+ /**
22
+ * ButtonGroup — lays out related buttons in a row. Works with any button
23
+ * elements (shared-ui or native). Replaces Chakra's ButtonGroup.
24
+ */
25
+ export const ButtonGroup = forwardRef<HTMLDivElement, ButtonGroupProps>(
26
+ function ButtonGroup({ isAttached, css: cssProp, className, ...rest }, ref) {
27
+ return (
28
+ <div
29
+ ref={ref}
30
+ className={cx(
31
+ css({
32
+ display: "inline-flex",
33
+ alignItems: "center",
34
+ // Buttons are position: relative, so without this an attached
35
+ // sibling paints over the focused button's focus-ring shadow
36
+ // (Chakra's Button applied zIndex 1 on focus when grouped).
37
+ "& > *": { _focus: { zIndex: 1 } },
38
+ }),
39
+ isAttached
40
+ ? css({
41
+ gap: 0,
42
+ // :first-child/:last-child, not :first-of-type: attached
43
+ // groups can mix element types (e.g. select + button), and
44
+ // -of-type matches per element type.
45
+ "& > *:first-child:not(:last-child)": {
46
+ borderEndRadius: 0,
47
+ },
48
+ "& > *:not(:first-child):not(:last-child)": {
49
+ borderRadius: 0,
50
+ },
51
+ "& > *:not(:first-child):last-child": {
52
+ borderStartRadius: 0,
53
+ },
54
+ })
55
+ : css({ gap: 2 }),
56
+ cssProp ? css(cssProp) : undefined,
57
+ className,
58
+ )}
59
+ {...rest}
60
+ />
61
+ );
62
+ },
63
+ );
@@ -0,0 +1,49 @@
1
+ /**
2
+ * (c) 2026, Micro:bit Educational Foundation and contributors
3
+ *
4
+ * SPDX-License-Identifier: MIT
5
+ */
6
+ import { defineSlotRecipe } from "@pandacss/dev";
7
+
8
+ /**
9
+ * Card slot recipe — Chakra's Card at its default md size (light mode), with
10
+ * the `elevated` (default) and `outline` variants this app uses.
11
+ *
12
+ * Registered in the base preset (base-preset.ts); `variant` is
13
+ * forwarded as a runtime prop so the variants are generated via `staticCss`.
14
+ */
15
+ export const card = defineSlotRecipe({
16
+ className: "card",
17
+ slots: ["container", "body"],
18
+ base: {
19
+ container: {
20
+ display: "flex",
21
+ flexDirection: "column",
22
+ position: "relative",
23
+ minWidth: 0,
24
+ wordWrap: "break-word",
25
+ bg: "white",
26
+ borderRadius: "md",
27
+ color: "inherit",
28
+ },
29
+ body: {
30
+ padding: "5",
31
+ flex: "1 1 0%",
32
+ },
33
+ },
34
+ variants: {
35
+ variant: {
36
+ elevated: {
37
+ container: { boxShadow: "base" },
38
+ },
39
+ outline: {
40
+ container: {
41
+ borderWidth: "1px",
42
+ borderStyle: "solid",
43
+ borderColor: "gray.200",
44
+ },
45
+ },
46
+ },
47
+ },
48
+ defaultVariants: { variant: "elevated" },
49
+ });
package/src/Card.tsx ADDED
@@ -0,0 +1,56 @@
1
+ /**
2
+ * (c) 2026, Micro:bit Educational Foundation and contributors
3
+ *
4
+ * SPDX-License-Identifier: MIT
5
+ */
6
+ import { forwardRef, HTMLAttributes } from "react";
7
+ import { css, cx } from "styled-system/css";
8
+ import { card, CardVariantProps } from "styled-system/recipes";
9
+ import { SystemStyleObject } from "styled-system/types";
10
+
11
+ export interface CardProps
12
+ extends HTMLAttributes<HTMLDivElement>,
13
+ CardVariantProps {
14
+ /** Per-instance style overrides, merged after the recipe. */
15
+ css?: SystemStyleObject;
16
+ }
17
+
18
+ /**
19
+ * Card — Chakra's <Card> equivalent (md size). Place a CardBody inside.
20
+ */
21
+ export const Card = forwardRef<HTMLDivElement, CardProps>(function Card(
22
+ { variant, css: cssProp, className, ...rest },
23
+ ref,
24
+ ) {
25
+ return (
26
+ <div
27
+ ref={ref}
28
+ className={cx(
29
+ card({ variant }).container,
30
+ cssProp ? css(cssProp) : undefined,
31
+ className,
32
+ )}
33
+ {...rest}
34
+ />
35
+ );
36
+ });
37
+
38
+ export interface CardBodyProps extends HTMLAttributes<HTMLDivElement> {
39
+ css?: SystemStyleObject;
40
+ }
41
+
42
+ export const CardBody = forwardRef<HTMLDivElement, CardBodyProps>(
43
+ function CardBody({ css: cssProp, className, ...rest }, ref) {
44
+ return (
45
+ <div
46
+ ref={ref}
47
+ className={cx(
48
+ card({}).body,
49
+ cssProp ? css(cssProp) : undefined,
50
+ className,
51
+ )}
52
+ {...rest}
53
+ />
54
+ );
55
+ },
56
+ );
@@ -0,0 +1,71 @@
1
+ /**
2
+ * (c) 2026, Micro:bit Educational Foundation and contributors
3
+ *
4
+ * SPDX-License-Identifier: MIT
5
+ */
6
+ import { defineSlotRecipe } from "@pandacss/dev";
7
+
8
+ /**
9
+ * Checkbox slot recipe — Chakra's md checkbox with the default blue
10
+ * colorScheme (light mode). The control's `borderColor: inherit` picks up a
11
+ * `borderColor` set on the root, matching Chakra's convention for tinting the
12
+ * box from the call site.
13
+ *
14
+ * State styling keys off data attributes stamped on the control by the
15
+ * shared-ui Checkbox (react-aria provides the state via render props).
16
+ *
17
+ * Registered in the base preset (base-preset.ts). No variants, so it
18
+ * needs no `staticCss` entry.
19
+ */
20
+ export const checkbox = defineSlotRecipe({
21
+ className: "checkbox",
22
+ slots: ["root", "control", "icon", "label"],
23
+ base: {
24
+ root: {
25
+ display: "inline-flex",
26
+ alignItems: "center",
27
+ verticalAlign: "top",
28
+ cursor: "pointer",
29
+ position: "relative",
30
+ "&[data-disabled]": { cursor: "not-allowed" },
31
+ },
32
+ control: {
33
+ display: "inline-flex",
34
+ alignItems: "center",
35
+ justifyContent: "center",
36
+ flexShrink: 0,
37
+ width: "4",
38
+ height: "4",
39
+ transitionProperty: "box-shadow",
40
+ transitionDuration: "normal",
41
+ border: "2px solid",
42
+ borderRadius: "sm",
43
+ borderColor: "inherit",
44
+ color: "white",
45
+ bg: "white",
46
+ "&[data-selected]": {
47
+ bg: "controlCheckedBg",
48
+ borderColor: "controlCheckedBg",
49
+ color: "white",
50
+ _hover: {
51
+ bg: "controlCheckedHoverBg",
52
+ borderColor: "controlCheckedHoverBg",
53
+ },
54
+ },
55
+ "&[data-focus-visible]": {
56
+ focusShadow: "outline",
57
+ },
58
+ },
59
+ icon: {
60
+ width: "0.75rem",
61
+ height: "0.75rem",
62
+ transitionProperty: "transform",
63
+ transitionDuration: "normal",
64
+ },
65
+ label: {
66
+ userSelect: "none",
67
+ marginStart: "2",
68
+ "&[data-disabled]": { opacity: 0.4 },
69
+ },
70
+ },
71
+ });
@@ -0,0 +1,66 @@
1
+ /**
2
+ * (c) 2026, Micro:bit Educational Foundation and contributors
3
+ *
4
+ * SPDX-License-Identifier: MIT
5
+ */
6
+ import { ReactNode } from "react";
7
+ import {
8
+ Checkbox as RACCheckbox,
9
+ CheckboxProps as RACCheckboxProps,
10
+ } from "react-aria-components";
11
+ import { css, cx } from "styled-system/css";
12
+ import { checkbox } from "styled-system/recipes";
13
+ import { SystemStyleObject } from "styled-system/types";
14
+
15
+ export interface CheckboxProps
16
+ extends Omit<RACCheckboxProps, "className" | "children" | "style"> {
17
+ /** Per-instance style overrides for the root, merged after the recipe. */
18
+ css?: SystemStyleObject;
19
+ className?: string;
20
+ children?: ReactNode;
21
+ }
22
+
23
+ /**
24
+ * Checkbox — react-aria-components <Checkbox> styled like Chakra's md
25
+ * checkbox. Children render as the label; wrap them in a visually-hidden
26
+ * span for icon-less checkboxes.
27
+ */
28
+ export const Checkbox = ({
29
+ css: cssProp,
30
+ className,
31
+ children,
32
+ ...rest
33
+ }: CheckboxProps) => {
34
+ const slots = checkbox();
35
+ return (
36
+ <RACCheckbox
37
+ className={cx(slots.root, cssProp ? css(cssProp) : undefined, className)}
38
+ {...rest}
39
+ >
40
+ {({ isSelected, isFocusVisible }) => (
41
+ <>
42
+ <span
43
+ className={slots.control}
44
+ data-selected={isSelected || undefined}
45
+ data-focus-visible={isFocusVisible || undefined}
46
+ aria-hidden
47
+ >
48
+ {isSelected && (
49
+ <svg viewBox="0 0 12 10" className={slots.icon} aria-hidden>
50
+ <polyline
51
+ points="1.5 6 4.5 9 10.5 1"
52
+ fill="none"
53
+ stroke="currentColor"
54
+ strokeWidth="2"
55
+ strokeLinecap="round"
56
+ strokeLinejoin="round"
57
+ />
58
+ </svg>
59
+ )}
60
+ </span>
61
+ {children != null && <span className={slots.label}>{children}</span>}
62
+ </>
63
+ )}
64
+ </RACCheckbox>
65
+ );
66
+ };
@@ -0,0 +1,87 @@
1
+ /**
2
+ * (c) 2026, Micro:bit Educational Foundation and contributors
3
+ *
4
+ * SPDX-License-Identifier: MIT
5
+ */
6
+ import { ButtonHTMLAttributes, forwardRef } from "react";
7
+ import { css, cx } from "styled-system/css";
8
+ import { SystemStyleObject } from "styled-system/types";
9
+ import { CloseIcon } from "./CloseIcon";
10
+
11
+ export interface CloseButtonProps
12
+ extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, "className"> {
13
+ /** Chakra CloseButton sizes: sm 24px box, md 32px (default). */
14
+ size?: "sm" | "md";
15
+ /**
16
+ * Grow the touch target 8px beyond the visible button on every side via an
17
+ * ::after overlay.
18
+ */
19
+ expandHitArea?: boolean;
20
+ "aria-label": string;
21
+ /** Per-instance style overrides, merged after the base. */
22
+ css?: SystemStyleObject;
23
+ className?: string;
24
+ }
25
+
26
+ /**
27
+ * CloseButton — Chakra's standalone X button. A plain button (not react-aria)
28
+ * so call sites can extend the hit area with pseudo-elements, which
29
+ * react-aria's press bounding-rect check would defeat.
30
+ */
31
+ export const CloseButton = forwardRef<HTMLButtonElement, CloseButtonProps>(
32
+ function CloseButton(
33
+ { size = "md", expandHitArea = false, css: cssProp, className, ...rest },
34
+ ref,
35
+ ) {
36
+ return (
37
+ <button
38
+ ref={ref}
39
+ type="button"
40
+ className={cx(
41
+ // A single css() call so per-instance overrides of base properties
42
+ // (e.g. borderRadius) merge rather than racing on stylesheet order.
43
+ css(
44
+ {
45
+ display: "inline-flex",
46
+ alignItems: "center",
47
+ justifyContent: "center",
48
+ flexShrink: 0,
49
+ position: "relative",
50
+ cursor: "pointer",
51
+ bg: "transparent",
52
+ border: "none",
53
+ color: "inherit",
54
+ outline: "none",
55
+ borderRadius: "md",
56
+ transitionProperty: "background-color, box-shadow",
57
+ transitionDuration: "normal",
58
+ _hover: { bg: "blackAlpha.100" },
59
+ _active: { bg: "blackAlpha.200" },
60
+ _focusVisible: { focusShadow: "outline" },
61
+ },
62
+ size === "sm"
63
+ ? { width: "6", height: "6", fontSize: "2xs" }
64
+ : { width: "8", height: "8", fontSize: "xs" },
65
+ expandHitArea
66
+ ? {
67
+ _after: {
68
+ position: "absolute",
69
+ top: -2,
70
+ right: -2,
71
+ bottom: -2,
72
+ left: -2,
73
+ content: '""',
74
+ },
75
+ }
76
+ : undefined,
77
+ cssProp,
78
+ ),
79
+ className,
80
+ )}
81
+ {...rest}
82
+ >
83
+ <CloseIcon />
84
+ </button>
85
+ );
86
+ },
87
+ );
@@ -0,0 +1,40 @@
1
+ /**
2
+ * (c) 2026, Micro:bit Educational Foundation and contributors
3
+ *
4
+ * SPDX-License-Identifier: MIT
5
+ */
6
+ import { css, cx } from "styled-system/css";
7
+ import { SystemStyleObject } from "styled-system/types";
8
+
9
+ export interface CloseIconProps {
10
+ css?: SystemStyleObject;
11
+ className?: string;
12
+ }
13
+
14
+ /**
15
+ * CloseIcon — the "✕" glyph used on close buttons (dialogs, toasts). This is
16
+ * Chakra's exact CloseButton path so the visual matches; sized to `1em` and
17
+ * `fill: currentColor` like the other icons. Reused wherever a close control
18
+ * is needed.
19
+ */
20
+ export const CloseIcon = ({ css: cssProp, className }: CloseIconProps) => (
21
+ <svg
22
+ viewBox="0 0 24 24"
23
+ focusable="false"
24
+ aria-hidden="true"
25
+ className={cx(
26
+ css({
27
+ width: "1em",
28
+ height: "1em",
29
+ display: "inline-block",
30
+ lineHeight: "1em",
31
+ flexShrink: 0,
32
+ fill: "currentColor",
33
+ ...cssProp,
34
+ }),
35
+ className,
36
+ )}
37
+ >
38
+ <path d="M.439,21.44a1.5,1.5,0,0,0,2.122,2.121L11.823,14.3a.25.25,0,0,1,.354,0l9.262,9.263a1.5,1.5,0,1,0,2.122-2.121L14.3,12.177a.25.25,0,0,1,0-.354l9.263-9.262A1.5,1.5,0,0,0,21.439.44L12.177,9.7a.25.25,0,0,1-.354,0L2.561.44A1.5,1.5,0,0,0,.439,2.561L9.7,11.823a.25.25,0,0,1,0,.354Z" />
39
+ </svg>
40
+ );
@@ -0,0 +1,21 @@
1
+ /**
2
+ * (c) 2026, Micro:bit Educational Foundation and contributors
3
+ *
4
+ * SPDX-License-Identifier: MIT
5
+ */
6
+ import { styled } from "styled-system/jsx";
7
+
8
+ /**
9
+ * Divider — a horizontal rule matching Chakra's <Divider> (hairline at 60%
10
+ * opacity; set `borderColor` to tint). Horizontal only.
11
+ */
12
+ export const Divider = styled("hr", {
13
+ base: {
14
+ border: 0,
15
+ borderBottomWidth: "1px",
16
+ borderBottomStyle: "solid",
17
+ borderColor: "gray.200",
18
+ opacity: 0.6,
19
+ width: "100%",
20
+ },
21
+ });
@@ -0,0 +1,98 @@
1
+ /**
2
+ * (c) 2026, Micro:bit Educational Foundation and contributors
3
+ *
4
+ * SPDX-License-Identifier: MIT
5
+ */
6
+ import { defineSlotRecipe } from "@pandacss/dev";
7
+
8
+ /**
9
+ * Drawer slot recipe — Chakra's default Drawer parts (light mode) at its
10
+ * default `xs` size (20rem panel). Consumed by the shared-ui Drawer, which maps
11
+ * the slots onto react-aria-components' ModalOverlay / Modal / Dialog.
12
+ *
13
+ * The enter transition approximates Chakra's slide spring (damping 25,
14
+ * stiffness 180) with a decelerating tween; exit matches its 0.15s ease-in-out
15
+ * tween.
16
+ *
17
+ * Registered in the base preset (base-preset.ts); `placement` is
18
+ * forwarded as a runtime prop so the variants are generated via `staticCss`.
19
+ */
20
+ export const drawer = defineSlotRecipe({
21
+ className: "drawer",
22
+ slots: ["overlay", "content", "inner", "header", "body"],
23
+ base: {
24
+ overlay: {
25
+ position: "fixed",
26
+ inset: 0,
27
+ w: "100%",
28
+ h: "100%",
29
+ bg: "blackAlpha.600",
30
+ zIndex: "modal",
31
+ opacity: 1,
32
+ transition: "opacity 0.2s ease-out",
33
+ "&[data-entering]": { opacity: 0 },
34
+ "&[data-exiting]": { opacity: 0 },
35
+ _motionReduce: { transition: "none" },
36
+ },
37
+ content: {
38
+ position: "fixed",
39
+ top: 0,
40
+ bottom: 0,
41
+ width: "100%",
42
+ maxWidth: "xs",
43
+ maxH: "100dvh",
44
+ color: "inherit",
45
+ bg: "white",
46
+ boxShadow: "lg",
47
+ display: "flex",
48
+ flexDirection: "column",
49
+ outline: "none",
50
+ zIndex: "modal",
51
+ transform: "translateX(0)",
52
+ transition: "transform 0.3s cubic-bezier(0, 0, 0.2, 1)",
53
+ "&[data-exiting]": {
54
+ transition: "transform 0.15s cubic-bezier(0.4, 0, 0.2, 1)",
55
+ },
56
+ _motionReduce: { transition: "none" },
57
+ },
58
+ inner: {
59
+ outline: "none",
60
+ display: "flex",
61
+ flexDirection: "column",
62
+ width: "100%",
63
+ height: "100%",
64
+ },
65
+ header: {
66
+ px: "6",
67
+ py: "4",
68
+ fontSize: "xl",
69
+ fontWeight: "semibold",
70
+ flexShrink: 0,
71
+ },
72
+ body: {
73
+ px: "6",
74
+ py: "2",
75
+ flex: "1",
76
+ overflow: "auto",
77
+ },
78
+ },
79
+ variants: {
80
+ placement: {
81
+ left: {
82
+ content: {
83
+ left: 0,
84
+ "&[data-entering]": { transform: "translateX(-100%)" },
85
+ "&[data-exiting]": { transform: "translateX(-100%)" },
86
+ },
87
+ },
88
+ right: {
89
+ content: {
90
+ right: 0,
91
+ "&[data-entering]": { transform: "translateX(100%)" },
92
+ "&[data-exiting]": { transform: "translateX(100%)" },
93
+ },
94
+ },
95
+ },
96
+ },
97
+ defaultVariants: { placement: "left" },
98
+ });