@microbit/ui 0.0.0-gray.ramp.90

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 (102) hide show
  1. package/LICENSE.md +53 -0
  2. package/README.md +222 -0
  3. package/lang/ui.ca.json +22 -0
  4. package/lang/ui.cy.json +22 -0
  5. package/lang/ui.de.json +22 -0
  6. package/lang/ui.en-us.json +22 -0
  7. package/lang/ui.en.json +22 -0
  8. package/lang/ui.es-es.json +22 -0
  9. package/lang/ui.fr.json +22 -0
  10. package/lang/ui.ga-ie.json +22 -0
  11. package/lang/ui.it.json +22 -0
  12. package/lang/ui.ja.json +22 -0
  13. package/lang/ui.ko.json +22 -0
  14. package/lang/ui.lol.json +22 -0
  15. package/lang/ui.nl.json +22 -0
  16. package/lang/ui.pl.json +22 -0
  17. package/lang/ui.pt-br.json +22 -0
  18. package/lang/ui.zh-cn.json +22 -0
  19. package/lang/ui.zh-tw.json +22 -0
  20. package/package.json +66 -0
  21. package/postcss-legacy-safari.cjs +96 -0
  22. package/reset.css +35 -0
  23. package/src/Avatar.recipe.ts +168 -0
  24. package/src/Avatar.tsx +276 -0
  25. package/src/Button.recipe.ts +192 -0
  26. package/src/Button.tsx +69 -0
  27. package/src/ButtonGroup.tsx +70 -0
  28. package/src/Card.recipe.ts +49 -0
  29. package/src/Card.tsx +56 -0
  30. package/src/Checkbox.recipe.ts +111 -0
  31. package/src/Checkbox.tsx +106 -0
  32. package/src/CloseButton.tsx +87 -0
  33. package/src/CloseIcon.tsx +40 -0
  34. package/src/Code.tsx +20 -0
  35. package/src/Collapse.tsx +180 -0
  36. package/src/ComboBox.tsx +192 -0
  37. package/src/Divider.tsx +85 -0
  38. package/src/Drawer.recipe.ts +98 -0
  39. package/src/Drawer.tsx +138 -0
  40. package/src/Fade.tsx +48 -0
  41. package/src/GridList.recipe.ts +58 -0
  42. package/src/GridList.tsx +81 -0
  43. package/src/Heading.recipe.ts +52 -0
  44. package/src/Heading.tsx +14 -0
  45. package/src/Icon.tsx +75 -0
  46. package/src/IconButton.tsx +44 -0
  47. package/src/Image.tsx +11 -0
  48. package/src/Input.recipe.ts +75 -0
  49. package/src/Input.tsx +44 -0
  50. package/src/InputGroup.tsx +62 -0
  51. package/src/Kbd.tsx +26 -0
  52. package/src/Link.tsx +23 -0
  53. package/src/LinkBox.tsx +88 -0
  54. package/src/LinkButton.tsx +80 -0
  55. package/src/List.tsx +31 -0
  56. package/src/ListBox.recipe.ts +43 -0
  57. package/src/ListBox.tsx +88 -0
  58. package/src/Menu.recipe.ts +124 -0
  59. package/src/Menu.tsx +291 -0
  60. package/src/Modal.recipe.ts +163 -0
  61. package/src/Modal.tsx +377 -0
  62. package/src/NativeSelect.tsx +94 -0
  63. package/src/NumberField.recipe.ts +67 -0
  64. package/src/NumberField.tsx +86 -0
  65. package/src/PopoverArrow.tsx +67 -0
  66. package/src/ProgressBar.tsx +61 -0
  67. package/src/Radio.recipe.ts +112 -0
  68. package/src/Radio.tsx +89 -0
  69. package/src/Select.recipe.ts +210 -0
  70. package/src/Select.tsx +153 -0
  71. package/src/SharedUIProvider.tsx +55 -0
  72. package/src/Skeleton.tsx +146 -0
  73. package/src/Slide.tsx +54 -0
  74. package/src/Slider.recipe.ts +102 -0
  75. package/src/Slider.tsx +163 -0
  76. package/src/Spinner.tsx +74 -0
  77. package/src/Svg.tsx +23 -0
  78. package/src/Switch.recipe.ts +107 -0
  79. package/src/Switch.tsx +62 -0
  80. package/src/Text.recipe.ts +29 -0
  81. package/src/Text.tsx +16 -0
  82. package/src/TextField.recipe.ts +54 -0
  83. package/src/TextField.tsx +91 -0
  84. package/src/Toast.recipe.ts +98 -0
  85. package/src/Toast.tsx +173 -0
  86. package/src/Tooltip.recipe.ts +37 -0
  87. package/src/Tooltip.tsx +76 -0
  88. package/src/UnmountCallback.tsx +18 -0
  89. package/src/VisuallyHidden.tsx +14 -0
  90. package/src/base-preset.ts +371 -0
  91. package/src/base-tokens.ts +1017 -0
  92. package/src/button-icon.ts +23 -0
  93. package/src/data-attrs.ts +16 -0
  94. package/src/dense-preset.ts +108 -0
  95. package/src/hooks/useBreakpointValue.ts +63 -0
  96. package/src/hooks/useClipboard.ts +64 -0
  97. package/src/hooks/useDisclosure.ts +32 -0
  98. package/src/hooks/useMediaQuery.ts +28 -0
  99. package/src/hooks/usePrevious.ts +18 -0
  100. package/src/index.ts +63 -0
  101. package/src/messages.ts +17 -0
  102. package/src/system.ts +45 -0
@@ -0,0 +1,23 @@
1
+ /**
2
+ * (c) 2026, Micro:bit Educational Foundation and contributors
3
+ *
4
+ * SPDX-License-Identifier: MIT
5
+ */
6
+ import { cva } from "styled-system/css";
7
+
8
+ // Chakra's ButtonIcon: keeps the glyph centred and spaced from the label
9
+ // (iconSpacing 0.5rem). Shared by Button and LinkButton; deliberately not
10
+ // exported from the package index.
11
+ export const buttonIcon = cva({
12
+ base: {
13
+ display: "inline-flex",
14
+ alignSelf: "center",
15
+ flexShrink: 0,
16
+ },
17
+ variants: {
18
+ side: {
19
+ left: { marginEnd: "2" },
20
+ right: { marginStart: "2" },
21
+ },
22
+ },
23
+ });
@@ -0,0 +1,16 @@
1
+ /**
2
+ * (c) 2026, Micro:bit Educational Foundation and contributors
3
+ *
4
+ * SPDX-License-Identifier: MIT
5
+ */
6
+
7
+ /**
8
+ * The `data-*` entries of a props object, for components that let a caller
9
+ * put test hooks on an inner element rather than the one their props land on.
10
+ *
11
+ * Internal: not exported from the package.
12
+ */
13
+ export const dataAttrs = (props: object): Record<string, unknown> =>
14
+ Object.fromEntries(
15
+ Object.entries(props).filter(([key]) => key.startsWith("data-")),
16
+ );
@@ -0,0 +1,108 @@
1
+ /**
2
+ * (c) 2026, Micro:bit Educational Foundation and contributors
3
+ *
4
+ * SPDX-License-Identifier: MIT
5
+ */
6
+ import { definePreset } from "@pandacss/dev";
7
+
8
+ const toTokens = (values: Record<string, string>) =>
9
+ Object.fromEntries(
10
+ Object.entries(values).map(([k, value]) => [k, { value }]),
11
+ ) as Record<string, { value: string }>;
12
+
13
+ /**
14
+ * The numeric spacing/size grid, Chakra's 0.25rem step × 0.88.
15
+ *
16
+ * Only the numeric scale is touched: the named `sizes` (`xs`…`8xl`, `max`,
17
+ * `full`, `container.*`) stay at their base-preset values, as they did in
18
+ * both apps' Chakra themes.
19
+ */
20
+ const scale = toTokens({
21
+ px: "1px",
22
+ 0.5: "0.11rem",
23
+ 1: "0.22rem",
24
+ 1.5: "0.33rem",
25
+ 2: "0.44rem",
26
+ 2.5: "0.55rem",
27
+ 3: "0.66rem",
28
+ 3.5: "0.77rem",
29
+ 4: "0.88rem",
30
+ 5: "1.1rem",
31
+ 6: "1.32rem",
32
+ 7: "1.54rem",
33
+ 8: "1.76rem",
34
+ 9: "1.98rem",
35
+ 10: "2.2rem",
36
+ 12: "2.64rem",
37
+ 14: "3.08rem",
38
+ 16: "3.52rem",
39
+ 20: "4.4rem",
40
+ 24: "5.28rem",
41
+ 28: "6.16rem",
42
+ 32: "7.04rem",
43
+ 36: "7.92rem",
44
+ 40: "8.8rem",
45
+ 44: "9.68rem",
46
+ 48: "10.56rem",
47
+ 52: "11.44rem",
48
+ 56: "12.32rem",
49
+ 60: "13.2rem",
50
+ 64: "14.08rem",
51
+ 72: "15.84rem",
52
+ 80: "17.6rem",
53
+ 96: "21.12rem",
54
+ });
55
+
56
+ /**
57
+ * Font sizes from `md` up, × 0.9. `xs`/`sm` keep their full size so small
58
+ * text never gets too small, and `3xs`/`2xs` (which neither app's theme
59
+ * listed) stay at their base-preset values.
60
+ */
61
+ const denseFontSizes = toTokens({
62
+ md: "0.9rem",
63
+ lg: "1.012rem",
64
+ xl: "1.125rem",
65
+ "2xl": "1.35rem",
66
+ "3xl": "1.687rem",
67
+ "4xl": "2.025rem",
68
+ "5xl": "2.7rem",
69
+ "6xl": "3.375rem",
70
+ "7xl": "4.05rem",
71
+ "8xl": "5.4rem",
72
+ "9xl": "7.2rem",
73
+ });
74
+
75
+ /**
76
+ * The dense preset — an optional density override for the information-dense
77
+ * apps in the family. Stacks between the base preset and the app preset:
78
+ *
79
+ * ```ts
80
+ * presets: ["@pandacss/preset-base", basePreset, densePreset, appPreset]
81
+ * ```
82
+ *
83
+ * Both python-editor and classroom shipped the same "make everything
84
+ * smaller" Chakra theme change (2022): the numeric spacing/sizes grid at
85
+ * × 0.88 and `fontSizes` from `md` up at × 0.9. The two themes' values were
86
+ * byte-identical, so the scale lives here rather than being replicated in
87
+ * each app preset (migration-playbook gotcha #25 — a global scale override
88
+ * hides from every safeguard, so it needs to be explicit and shared).
89
+ *
90
+ * Whether this density stays or the family aligns on one scale is an open
91
+ * design question; when it is answered, this preset is the single place the
92
+ * answer lands (deleting it from an app's stack un-shrinks that app).
93
+ * ml-trainer and data-microbit-org do not use it.
94
+ */
95
+ export const densePreset = definePreset({
96
+ name: "microbit-ui-dense",
97
+ theme: {
98
+ extend: {
99
+ tokens: {
100
+ spacing: scale,
101
+ sizes: scale,
102
+ fontSizes: denseFontSizes,
103
+ },
104
+ },
105
+ },
106
+ });
107
+
108
+ export default densePreset;
@@ -0,0 +1,63 @@
1
+ /**
2
+ * (c) 2026, Micro:bit Educational Foundation and contributors
3
+ *
4
+ * SPDX-License-Identifier: MIT
5
+ */
6
+ import { useEffect, useState } from "react";
7
+ import { token } from "styled-system/tokens";
8
+
9
+ // Min-width breakpoints from the preset's token scale (runtime lookup so the
10
+ // hook tracks any preset override rather than duplicating the values).
11
+ const BREAKPOINTS = {
12
+ sm: token("breakpoints.sm"),
13
+ md: token("breakpoints.md"),
14
+ lg: token("breakpoints.lg"),
15
+ xl: token("breakpoints.xl"),
16
+ "2xl": token("breakpoints.2xl"),
17
+ } as const;
18
+
19
+ type Breakpoint = "base" | keyof typeof BREAKPOINTS;
20
+
21
+ const ORDER: Breakpoint[] = ["base", "sm", "md", "lg", "xl", "2xl"];
22
+
23
+ const activeBreakpoint = (): Breakpoint => {
24
+ let current: Breakpoint = "base";
25
+ for (const [name, min] of Object.entries(BREAKPOINTS)) {
26
+ if (window.matchMedia(`(min-width: ${min})`).matches) {
27
+ current = name as Breakpoint;
28
+ }
29
+ }
30
+ return current;
31
+ };
32
+
33
+ /**
34
+ * JS-side responsive value resolver, replacing Chakra's `useBreakpointValue`.
35
+ * Panda handles responsive *styles* in CSS; this is for responsive *logic*
36
+ * (choosing a prop value, branching behaviour). Resolves to the value at the
37
+ * active breakpoint, falling back to the nearest smaller one that is defined.
38
+ */
39
+ export function useBreakpointValue<T>(
40
+ values: Partial<Record<Breakpoint, T>>,
41
+ ): T | undefined {
42
+ const [bp, setBp] = useState<Breakpoint>(() =>
43
+ typeof window === "undefined" ? "base" : activeBreakpoint(),
44
+ );
45
+
46
+ useEffect(() => {
47
+ const update = () => setBp(activeBreakpoint());
48
+ update();
49
+ const lists = Object.values(BREAKPOINTS).map((min) =>
50
+ window.matchMedia(`(min-width: ${min})`),
51
+ );
52
+ lists.forEach((l) => l.addEventListener("change", update));
53
+ return () => lists.forEach((l) => l.removeEventListener("change", update));
54
+ }, []);
55
+
56
+ for (let i = ORDER.indexOf(bp); i >= 0; i--) {
57
+ const value = values[ORDER[i]];
58
+ if (value !== undefined) {
59
+ return value;
60
+ }
61
+ }
62
+ return undefined;
63
+ }
@@ -0,0 +1,64 @@
1
+ /**
2
+ * (c) 2026, Micro:bit Educational Foundation and contributors
3
+ *
4
+ * SPDX-License-Identifier: MIT
5
+ */
6
+ import { useCallback, useEffect, useRef, useState } from "react";
7
+
8
+ /**
9
+ * Copy a value to the clipboard with a transient `hasCopied` flag for
10
+ * "Copied!" feedback. Replaces Chakra's `useClipboard`.
11
+ */
12
+ export function useClipboard(
13
+ value: string,
14
+ timeoutMs = 1500,
15
+ ): { onCopy: () => void; hasCopied: boolean } {
16
+ const [hasCopied, setHasCopied] = useState(false);
17
+ const timeoutRef = useRef<ReturnType<typeof setTimeout>>();
18
+ const markCopied = useCallback(() => {
19
+ setHasCopied(true);
20
+ clearTimeout(timeoutRef.current);
21
+ timeoutRef.current = setTimeout(() => setHasCopied(false), timeoutMs);
22
+ }, [timeoutMs]);
23
+ const onCopy = useCallback(() => {
24
+ if (navigator.clipboard?.writeText) {
25
+ navigator.clipboard
26
+ .writeText(value)
27
+ .then(markCopied, () => execCommandCopy(value) && markCopied());
28
+ } else if (execCommandCopy(value)) {
29
+ markCopied();
30
+ }
31
+ }, [value, markCopied]);
32
+ useEffect(() => () => clearTimeout(timeoutRef.current), []);
33
+ return { onCopy, hasCopied };
34
+ }
35
+
36
+ /**
37
+ * Chakra's useClipboard wrapped the execCommand-based copy-to-clipboard
38
+ * package, which works on a user gesture in contexts the async API refuses:
39
+ * insecure (non-localhost http) origins, where `navigator.clipboard` is
40
+ * undefined, and frames without a clipboard-write permissions policy, where
41
+ * `writeText` rejects.
42
+ */
43
+ function execCommandCopy(value: string): boolean {
44
+ const active = document.activeElement;
45
+ const textarea = document.createElement("textarea");
46
+ textarea.value = value;
47
+ textarea.setAttribute("readonly", "");
48
+ textarea.style.position = "fixed";
49
+ textarea.style.opacity = "0";
50
+ document.body.appendChild(textarea);
51
+ textarea.select();
52
+ let copied = false;
53
+ try {
54
+ copied = document.execCommand("copy");
55
+ } catch {
56
+ // Unsupported (execCommand is deprecated but universally kept for copy);
57
+ // report failure so the caller shows no false "Copied!".
58
+ }
59
+ textarea.remove();
60
+ if (active instanceof HTMLElement) {
61
+ active.focus();
62
+ }
63
+ return copied;
64
+ }
@@ -0,0 +1,32 @@
1
+ /**
2
+ * (c) 2026, Micro:bit Educational Foundation and contributors
3
+ *
4
+ * SPDX-License-Identifier: MIT
5
+ */
6
+ import { useCallback, useMemo, useState } from "react";
7
+
8
+ export interface Disclosure {
9
+ isOpen: boolean;
10
+ onOpen: () => void;
11
+ onClose: () => void;
12
+ onToggle: () => void;
13
+ }
14
+
15
+ /**
16
+ * useDisclosure — Chakra's hook of the same name: the open/closed state of a
17
+ * dialog, menu or drawer, and the three functions that change it.
18
+ *
19
+ * A thin `useState` wrapper, kept because it is the shape a Chakra app's
20
+ * dialog call sites are written in, and because a stable object means a
21
+ * disclosure can be passed to a memoised child without re-rendering it.
22
+ */
23
+ export const useDisclosure = (defaultIsOpen = false): Disclosure => {
24
+ const [isOpen, setIsOpen] = useState(defaultIsOpen);
25
+ const onOpen = useCallback(() => setIsOpen(true), []);
26
+ const onClose = useCallback(() => setIsOpen(false), []);
27
+ const onToggle = useCallback(() => setIsOpen((open) => !open), []);
28
+ return useMemo(
29
+ () => ({ isOpen, onOpen, onClose, onToggle }),
30
+ [isOpen, onOpen, onClose, onToggle],
31
+ );
32
+ };
@@ -0,0 +1,28 @@
1
+ /**
2
+ * (c) 2026, Micro:bit Educational Foundation and contributors
3
+ *
4
+ * SPDX-License-Identifier: MIT
5
+ */
6
+ import { useCallback, useSyncExternalStore } from "react";
7
+
8
+ /**
9
+ * Tracks a raw CSS media query, replacing Chakra's `useMediaQuery` for
10
+ * queries that aren't breakpoint-based (custom widths, height-based
11
+ * queries). For the preset's breakpoint scale prefer `useBreakpointValue`.
12
+ * Returns false during SSR.
13
+ */
14
+ export function useMediaQuery(query: string): boolean {
15
+ const subscribe = useCallback(
16
+ (onChange: () => void) => {
17
+ const list = window.matchMedia(query);
18
+ list.addEventListener("change", onChange);
19
+ return () => list.removeEventListener("change", onChange);
20
+ },
21
+ [query],
22
+ );
23
+ return useSyncExternalStore(
24
+ subscribe,
25
+ () => window.matchMedia(query).matches,
26
+ () => false,
27
+ );
28
+ }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * (c) 2026, Micro:bit Educational Foundation and contributors
3
+ *
4
+ * SPDX-License-Identifier: MIT
5
+ */
6
+ import { useEffect, useRef } from "react";
7
+
8
+ /**
9
+ * The value from the previous render (undefined on the first render).
10
+ * Replaces Chakra's `usePrevious`.
11
+ */
12
+ export function usePrevious<T>(value: T): T | undefined {
13
+ const ref = useRef<T>();
14
+ useEffect(() => {
15
+ ref.current = value;
16
+ });
17
+ return ref.current;
18
+ }
package/src/index.ts ADDED
@@ -0,0 +1,63 @@
1
+ /**
2
+ * (c) 2026, Micro:bit Educational Foundation and contributors
3
+ *
4
+ * SPDX-License-Identifier: MIT
5
+ */
6
+ /**
7
+ * shared-ui — react-aria-components + Panda CSS primitives replacing Chakra,
8
+ * designed to be extracted into a library shared across sibling apps. Visuals
9
+ * match the Chakra theme; behaviour follows react-aria patterns.
10
+ */
11
+ export * from "./system";
12
+ export * from "./Avatar";
13
+ export * from "./Button";
14
+ export * from "./LinkButton";
15
+ export * from "./ButtonGroup";
16
+ export * from "./Card";
17
+ export * from "./Checkbox";
18
+ export * from "./IconButton";
19
+ export * from "./Image";
20
+ export * from "./Input";
21
+ export * from "./InputGroup";
22
+ export * from "./LinkBox";
23
+ export * from "./NativeSelect";
24
+ export * from "./NumberField";
25
+ export * from "./ProgressBar";
26
+ export * from "./Radio";
27
+ export * from "./Skeleton";
28
+ export * from "./Slide";
29
+ export * from "./Slider";
30
+ export * from "./Select";
31
+ export * from "./Spinner";
32
+ export * from "./Svg";
33
+ export * from "./Switch";
34
+ export * from "./Text";
35
+ export * from "./TextField";
36
+ export * from "./Heading";
37
+ export * from "./Link";
38
+ export * from "./Icon";
39
+ export * from "./CloseButton";
40
+ export * from "./CloseIcon";
41
+ export * from "./Code";
42
+ export * from "./Collapse";
43
+ export * from "./Fade";
44
+ export * from "./Kbd";
45
+ export * from "./Divider";
46
+ export * from "./GridList";
47
+ export * from "./Drawer";
48
+ export * from "./List";
49
+ export * from "./ListBox";
50
+ export * from "./ComboBox";
51
+ export * from "./Menu";
52
+ export * from "./Modal";
53
+ export * from "./PopoverArrow";
54
+ export * from "./SharedUIProvider";
55
+ export * from "./Tooltip";
56
+ export * from "./Toast";
57
+ export * from "./VisuallyHidden";
58
+ export { useBreakpointValue } from "./hooks/useBreakpointValue";
59
+ export { useClipboard } from "./hooks/useClipboard";
60
+ export { useDisclosure } from "./hooks/useDisclosure";
61
+ export type { Disclosure } from "./hooks/useDisclosure";
62
+ export { useMediaQuery } from "./hooks/useMediaQuery";
63
+ export { usePrevious } from "./hooks/usePrevious";
@@ -0,0 +1,17 @@
1
+ /**
2
+ * (c) 2026, Micro:bit Educational Foundation and contributors
3
+ *
4
+ * SPDX-License-Identifier: MIT
5
+ */
6
+ import en from "../lang/ui.en.json";
7
+
8
+ /**
9
+ * Descriptor for one of this package's messages. The English text rides along
10
+ * as defaultMessage so components render English without any catalog merging;
11
+ * for other locales apps compile lang/ui.<locale>.json into their per-locale
12
+ * catalogs (see README "Strings").
13
+ */
14
+ export const uiMessage = (id: keyof typeof en) => ({
15
+ id,
16
+ defaultMessage: en[id].defaultMessage,
17
+ });
package/src/system.ts ADDED
@@ -0,0 +1,45 @@
1
+ /**
2
+ * (c) 2026, Micro:bit Educational Foundation and contributors
3
+ *
4
+ * SPDX-License-Identifier: MIT
5
+ */
6
+ /**
7
+ * Stable re-export of the Panda styling primitives and layout patterns, so
8
+ * shared-ui consumers (and a future extracted library) import from one place
9
+ * rather than reaching into the generated `styled-system` directly.
10
+ */
11
+ export { css, cva, sva, cx } from "styled-system/css";
12
+ export { token } from "styled-system/tokens";
13
+ export type { SystemStyleObject } from "styled-system/types";
14
+ // react-aria collection types call sites need for selection handlers.
15
+ export type { Key, Selection } from "react-aria-components";
16
+ export type {
17
+ BoxProps,
18
+ FlexProps,
19
+ StackProps,
20
+ HstackProps,
21
+ VstackProps,
22
+ GridProps,
23
+ } from "styled-system/jsx";
24
+
25
+ // Layout patterns — the Panda-native equivalents of Chakra's Box/Flex/Stack/etc.
26
+ //
27
+ // `styled` is re-exported for the `styled(Component)` form, which works from
28
+ // anywhere. The `styled.tag` JSX form does NOT: Panda recognises the factory by
29
+ // the module it was imported from, so `<styled.table css={…}>` on a `styled`
30
+ // imported from here silently produces no CSS. Import it from
31
+ // "styled-system/jsx" for that (playbook gotcha #41).
32
+ export {
33
+ AspectRatio,
34
+ Box,
35
+ Container,
36
+ Flex,
37
+ Stack,
38
+ HStack,
39
+ VStack,
40
+ Grid,
41
+ GridItem,
42
+ Center,
43
+ Wrap,
44
+ styled,
45
+ } from "styled-system/jsx";