@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.
- package/LICENSE.md +21 -0
- package/README.md +118 -0
- package/lang/ui.ca.json +22 -0
- package/lang/ui.en-us.json +22 -0
- package/lang/ui.en.json +22 -0
- package/lang/ui.es-es.json +22 -0
- package/lang/ui.fr.json +22 -0
- package/lang/ui.ja.json +22 -0
- package/lang/ui.ko.json +22 -0
- package/lang/ui.lol.json +22 -0
- package/lang/ui.nl.json +22 -0
- package/lang/ui.pl.json +22 -0
- package/lang/ui.pt-br.json +22 -0
- package/lang/ui.zh-tw.json +22 -0
- package/package.json +54 -0
- package/src/Button.recipe.ts +162 -0
- package/src/Button.tsx +84 -0
- package/src/ButtonGroup.tsx +63 -0
- package/src/Card.recipe.ts +49 -0
- package/src/Card.tsx +56 -0
- package/src/Checkbox.recipe.ts +71 -0
- package/src/Checkbox.tsx +66 -0
- package/src/CloseButton.tsx +87 -0
- package/src/CloseIcon.tsx +40 -0
- package/src/Divider.tsx +21 -0
- package/src/Drawer.recipe.ts +98 -0
- package/src/Drawer.tsx +138 -0
- package/src/Heading.recipe.ts +52 -0
- package/src/Heading.tsx +14 -0
- package/src/Icon.tsx +55 -0
- package/src/IconButton.tsx +39 -0
- package/src/Image.tsx +11 -0
- package/src/Input.recipe.ts +63 -0
- package/src/Input.tsx +33 -0
- package/src/InputGroup.tsx +48 -0
- package/src/Link.tsx +23 -0
- package/src/LinkBox.tsx +88 -0
- package/src/List.tsx +27 -0
- package/src/Menu.recipe.ts +88 -0
- package/src/Menu.tsx +172 -0
- package/src/Modal.recipe.ts +163 -0
- package/src/Modal.tsx +265 -0
- package/src/NativeSelect.tsx +76 -0
- package/src/PopoverArrow.tsx +52 -0
- package/src/ProgressBar.tsx +61 -0
- package/src/SharedUIProvider.tsx +55 -0
- package/src/Slide.tsx +54 -0
- package/src/Slider.recipe.ts +91 -0
- package/src/Slider.tsx +99 -0
- package/src/Spinner.tsx +69 -0
- package/src/Svg.tsx +23 -0
- package/src/Switch.recipe.ts +80 -0
- package/src/Switch.tsx +60 -0
- package/src/Text.tsx +12 -0
- package/src/TextField.recipe.ts +54 -0
- package/src/TextField.tsx +72 -0
- package/src/Toast.recipe.ts +98 -0
- package/src/Toast.tsx +165 -0
- package/src/Tooltip.tsx +91 -0
- package/src/UnmountCallback.tsx +18 -0
- package/src/VisuallyHidden.tsx +14 -0
- package/src/base-preset.ts +278 -0
- package/src/chakra-tokens.ts +1014 -0
- package/src/hooks/useBreakpointValue.ts +63 -0
- package/src/index.ts +45 -0
- package/src/messages.ts +17 -0
- 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;
|