@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
@@ -0,0 +1,278 @@
1
+ /**
2
+ * (c) 2026, Micro:bit Educational Foundation and contributors
3
+ *
4
+ * SPDX-License-Identifier: MIT
5
+ */
6
+ import { definePreset } from "@pandacss/dev";
7
+ import {
8
+ blurs,
9
+ borders,
10
+ breakpoints,
11
+ colors,
12
+ durations,
13
+ easings,
14
+ fontSizes,
15
+ fontWeights,
16
+ letterSpacings,
17
+ lineHeights,
18
+ radii,
19
+ shadows,
20
+ sizes,
21
+ spacing,
22
+ zIndex,
23
+ } from "./chakra-tokens";
24
+ // Config recipes are colocated with the shared-ui components they style; this
25
+ // preset registers them so Panda merges them at codegen time.
26
+ import { button } from "./Button.recipe";
27
+ import { card } from "./Card.recipe";
28
+ import { checkbox } from "./Checkbox.recipe";
29
+ import { drawer } from "./Drawer.recipe";
30
+ import { heading } from "./Heading.recipe";
31
+ import { input } from "./Input.recipe";
32
+ import { menu } from "./Menu.recipe";
33
+ import { slider } from "./Slider.recipe";
34
+ import { switchRecipe } from "./Switch.recipe";
35
+ import { dialog } from "./Modal.recipe";
36
+ import { field } from "./TextField.recipe";
37
+ import { toast } from "./Toast.recipe";
38
+
39
+ /**
40
+ * The base preset: the complete, working micro:bit design system. Chakra v2's
41
+ * default token scales (chakra-tokens.ts snapshot), the micro:bit house style
42
+ * (pill `radii.button`, `outline*` focus shadows, Helvetica fonts, the
43
+ * `language`/`toolbar` button variants in Button.recipe.ts, the
44
+ * `languageText`/`toast*Bg`/`statusBarBg` semantic tokens), the shared-ui
45
+ * component recipes, the react-aria condition widening, the Chakra-reset
46
+ * parity `globalCss`, and the `staticCss` that keeps runtime-prop recipe
47
+ * variants generated. Used alone it renders in the OSS default look.
48
+ *
49
+ * ── Brand contract ──────────────────────────────────────────────────────
50
+ * A private brand preset (a sibling repo, e.g. CreateAI) is merged AFTER this
51
+ * one to restyle everything by overriding just these token *values* (never
52
+ * their names — see the CSS-var contract in the README):
53
+ * - colours: the `brand` and `brand2` ramps (OSS defaults: Chakra
54
+ * blue / Chakra's unmodified gray). Other ramps a brand tweaks
55
+ * (teal/purple/pink/…) already exist in the Chakra scales below.
56
+ * - font: `display` (OSS default: Helvetica; e.g. GT Walsheim privately).
57
+ * The recipes and semantic tokens here reference those, so a brand swap needs
58
+ * no recipe changes. With no private preset, these OSS defaults stand.
59
+ */
60
+ export const basePreset = definePreset({
61
+ name: "microbit-ui-base",
62
+ theme: {
63
+ breakpoints,
64
+ keyframes: {
65
+ // Spinner's revolution (the only keyframe a shared-ui component uses).
66
+ spin: {
67
+ "0%": { transform: "rotate(0deg)" },
68
+ "100%": { transform: "rotate(360deg)" },
69
+ },
70
+ },
71
+ tokens: {
72
+ colors: {
73
+ ...colors,
74
+ gray: {
75
+ ...colors.gray,
76
+ // Very light grays the family's designs use below Chakra's 50.
77
+ 10: { value: "#fcfcfc" },
78
+ 25: { value: "#f5f5f5" },
79
+ },
80
+ // OSS default brand ramps (see the brand contract above). `brand`
81
+ // aliases Chakra blue; `brand2` Chakra's *unmodified* gray (not the
82
+ // `gray` above, whose 10/25 additions are lighter — getting this
83
+ // wrong once made card text near-invisible).
84
+ brand: colors.blue,
85
+ brand2: colors.gray,
86
+ },
87
+ spacing,
88
+ sizes,
89
+ fontSizes,
90
+ fontWeights,
91
+ lineHeights,
92
+ letterSpacings,
93
+ zIndex,
94
+ blurs,
95
+ borders,
96
+ durations,
97
+ easings,
98
+ radii: {
99
+ ...radii,
100
+ // Pill button radius (4/4 apps in the family census).
101
+ button: { value: "2rem" },
102
+ },
103
+ shadows: {
104
+ ...shadows,
105
+ // Chakra's outline shadow widened to 4px, plus dark/light-surface
106
+ // companions. Consumed via the `focusShadow` utility.
107
+ outline: { value: "0 0 0 4px rgba(66, 153, 225, 0.6)" },
108
+ outlineDark: { value: "0 0 0 4px rgba(0, 0, 0, 0.5)" },
109
+ outlineLight: { value: "0 0 0 4px rgba(255, 255, 255, 0.8)" },
110
+ },
111
+ fonts: {
112
+ // Helvetica heading/body (4/4 apps); a brand preset leaves these and
113
+ // overrides only `display` (the marketing font — see the brand
114
+ // contract above). `mono` is Chakra's default stack.
115
+ heading: { value: "Helvetica, Arial, sans-serif" },
116
+ body: { value: "Helvetica, Arial, sans-serif" },
117
+ mono: {
118
+ value:
119
+ 'SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace',
120
+ },
121
+ // OSS default marketing font (the `marketing` heading variant). No OSS
122
+ // brand display face, so it falls back to the body stack.
123
+ display: { value: "Helvetica, Arial, sans-serif" },
124
+ },
125
+ },
126
+ semanticTokens: {
127
+ colors: {
128
+ // Checked/focus states of form controls (Chakra's default blue
129
+ // colorScheme). Checkbox/Switch checked backgrounds, Input focus
130
+ // border, and the Slider/ProgressBar fills (the last two are
131
+ // near-dead defaults - current call sites override them).
132
+ controlCheckedBg: { value: "{colors.blue.500}" },
133
+ controlCheckedHoverBg: { value: "{colors.blue.600}" },
134
+ focusBorder: { value: "{colors.blue.500}" },
135
+ sliderFilledTrack: { value: "{colors.blue.500}" },
136
+ progressFilledTrack: { value: "{colors.blue.500}" },
137
+ // Error/destructive ramp (Chakra red). Destructive button variants,
138
+ // field error states and the error toast; the record* button
139
+ // variants deliberately stay on red.* (recording vocabulary, not
140
+ // danger).
141
+ danger: {
142
+ 50: { value: "{colors.red.50}" },
143
+ 100: { value: "{colors.red.100}" },
144
+ 500: { value: "{colors.red.500}" },
145
+ 600: { value: "{colors.red.600}" },
146
+ 700: { value: "{colors.red.700}" },
147
+ },
148
+ // The `language` button variant's text colour is the one place the
149
+ // brands diverge structurally (OSS uses the grey brand2 ramp, the
150
+ // CreateAI brand its blue brand ramp with no hover change). Driven by
151
+ // these semantic tokens so the recipe stays shared and a brand preset
152
+ // overrides only the values.
153
+ languageText: { value: "{colors.brand2.500}" },
154
+ languageTextHover: { value: "{colors.brand2.600}" },
155
+ // Toast status colours: the Chakra-era toast Alert restyle (teal for
156
+ // every status except error) shared across the app family.
157
+ toastInfoBg: { value: "{colors.teal.800}" },
158
+ toastSuccessBg: { value: "{colors.teal.800}" },
159
+ toastWarningBg: { value: "{colors.teal.800}" },
160
+ toastErrorBg: { value: "{colors.danger.600}" },
161
+ // The native app's status-bar area colour, shared by the ActionBar
162
+ // and the full-size dialog's safe-area gradient.
163
+ statusBarBg: { value: "{colors.brand2.500}" },
164
+ },
165
+ },
166
+ recipes: {
167
+ button,
168
+ heading,
169
+ input,
170
+ },
171
+ slotRecipes: {
172
+ card,
173
+ checkbox,
174
+ dialog,
175
+ drawer,
176
+ field,
177
+ menu,
178
+ slider,
179
+ switchRecipe,
180
+ toast,
181
+ },
182
+ },
183
+ // What ChakraProvider used to inject and Panda's preflight doesn't cover:
184
+ // the theme's styles.global (body text/background defaults, global
185
+ // border/placeholder colours) plus the parts of Chakra's CSS reset that
186
+ // Panda's has no equivalent for — kerning/text-rendering (their absence
187
+ // shifts glyphs page-wide), word-wrap and touch-action. Token references
188
+ // resolve against the merged preset stack, so the values track any brand
189
+ // overrides exactly as they did under Chakra's runtime theme.
190
+ globalCss: {
191
+ html: {
192
+ textRendering: "optimizeLegibility",
193
+ touchAction: "manipulation",
194
+ },
195
+ body: {
196
+ position: "relative",
197
+ minHeight: "100%",
198
+ fontFeatureSettings: '"kern"',
199
+ fontFamily: "body",
200
+ color: "gray.800",
201
+ bg: "white",
202
+ transitionProperty: "background-color",
203
+ transitionDuration: "normal",
204
+ lineHeight: "base",
205
+ },
206
+ "*::placeholder": {
207
+ color: "gray.500",
208
+ },
209
+ "*, *::before, *::after": {
210
+ borderColor: "gray.200",
211
+ wordWrap: "break-word",
212
+ },
213
+ // Panda's preflight, unlike Chakra's reset, doesn't set the pointer
214
+ // cursor on buttons. Recipes' disabled states (cursor: not-allowed)
215
+ // override this from the higher recipes layer.
216
+ "button, [role='button']": {
217
+ cursor: "pointer",
218
+ },
219
+ // Panda's preflight balance-wraps headings; Chakra didn't, and balanced
220
+ // multi-line headings break at different points (mobile/translations).
221
+ "h1, h2, h3, h4, h5, h6": {
222
+ textWrap: "wrap",
223
+ },
224
+ },
225
+ // shared-ui components forward `variant`/`size`/etc. as runtime props to
226
+ // the recipe functions, so Panda's static analysis can't see which variants
227
+ // are used and would emit no CSS for them. Generate every recipe variant.
228
+ // Lives in the preset (not the consumer's panda.config.ts) so no consumer
229
+ // can silently lose runtime-prop variants.
230
+ staticCss: {
231
+ recipes: {
232
+ button: ["*"],
233
+ heading: ["*"],
234
+ card: ["*"],
235
+ // Dialog size is chosen with responsive objects ({ base, md }) passed
236
+ // as a runtime prop, so generate the breakpoint-prefixed variants too.
237
+ dialog: [{ size: ["*"], responsive: true }, { centered: ["*"] }],
238
+ drawer: ["*"],
239
+ // Toast status is chosen at runtime from the toast content.
240
+ toast: ["*"],
241
+ },
242
+ },
243
+ utilities: {
244
+ extend: {
245
+ // The app-wide focus indicator, usually inside `_focusVisible`. Values
246
+ // are the outline* shadow token names. The transparent outline is for
247
+ // forced-colors modes, which strip box-shadows but recolour outlines to
248
+ // a visible system colour. (Named to avoid preset-base's outline-based
249
+ // `focusRing` utility, whose values would break this transform.)
250
+ focusShadow: {
251
+ className: "focus-shadow",
252
+ values: ["outline", "outlineDark", "outlineLight"],
253
+ transform: (value: string, { token }) => ({
254
+ outline: "2px solid transparent",
255
+ outlineOffset: "2px",
256
+ boxShadow: token(`shadows.${value}`),
257
+ }),
258
+ },
259
+ },
260
+ },
261
+ // Widen the interaction conditions so the Chakra-shaped recipe/style objects
262
+ // (`_hover`/`_active`/`_focusVisible`/`_disabled`) also respond to
263
+ // react-aria-components' data attributes, not just native pseudo-classes.
264
+ conditions: {
265
+ extend: {
266
+ hover: "&:is(:hover, [data-hovered])",
267
+ active: "&:is(:active, [data-pressed])",
268
+ focusVisible: "&:is(:focus-visible, [data-focus-visible])",
269
+ disabled:
270
+ "&:is(:disabled, [disabled], [data-disabled], [aria-disabled=true])",
271
+ // High-contrast/forced-palette modes (e.g. Windows High Contrast), which
272
+ // strip author backgrounds and box-shadows.
273
+ forcedColors: "@media (forced-colors: active)",
274
+ },
275
+ },
276
+ });
277
+
278
+ export default basePreset;