@microbit/ui 0.1.0-alpha.3 → 0.1.0-alpha.31
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 +40 -0
- package/README.md +279 -26
- package/lang/ui.ar.json +62 -0
- package/lang/ui.ca.json +43 -3
- package/lang/ui.cy.json +62 -0
- package/lang/ui.de.json +62 -0
- package/lang/ui.en-us.json +40 -0
- package/lang/ui.en.json +40 -0
- package/lang/ui.es-es.json +43 -3
- package/lang/ui.fr.json +44 -4
- package/lang/ui.ga-ie.json +62 -0
- package/lang/ui.it.json +62 -0
- package/lang/ui.ja.json +44 -4
- package/lang/ui.ko.json +44 -4
- package/lang/ui.lo.json +62 -0
- package/lang/ui.lol.json +40 -0
- package/lang/ui.nl.json +44 -4
- package/lang/ui.pl.json +44 -4
- package/lang/ui.pt-br.json +44 -4
- package/lang/ui.vi.json +62 -0
- package/lang/ui.zh-cn.json +62 -0
- package/lang/ui.zh-tw.json +44 -4
- package/package.json +19 -10
- package/postcss-legacy-safari.cjs +96 -0
- package/reset.css +35 -0
- package/src/Avatar.recipe.ts +191 -0
- package/src/Avatar.tsx +278 -0
- package/src/Breadcrumb.recipe.ts +45 -0
- package/src/Breadcrumb.tsx +114 -0
- package/src/Button.recipe.ts +88 -50
- package/src/Button.tsx +68 -30
- package/src/ButtonGroup.tsx +37 -15
- package/src/Card.recipe.ts +1 -2
- package/src/Card.tsx +2 -1
- package/src/Checkbox.recipe.ts +49 -14
- package/src/Checkbox.tsx +111 -32
- package/src/CheckboxGroup.tsx +70 -0
- package/src/CloseButton.tsx +3 -3
- package/src/CloseIcon.tsx +6 -4
- package/src/Code.tsx +20 -0
- package/src/Collapse.tsx +179 -0
- package/src/ComboBox.tsx +246 -0
- package/src/Divider.tsx +72 -8
- package/src/Drawer.recipe.ts +21 -10
- package/src/Drawer.tsx +3 -4
- package/src/ExternalLink.tsx +43 -0
- package/src/Fade.tsx +62 -0
- package/src/Field.recipe.ts +114 -0
- package/src/Field.tsx +187 -0
- package/src/GridList.recipe.ts +57 -0
- package/src/GridList.tsx +81 -0
- package/src/Heading.recipe.ts +20 -1
- package/src/Heading.tsx +3 -3
- package/src/Icon.tsx +23 -7
- package/src/IconButton.tsx +8 -13
- package/src/Image.tsx +1 -1
- package/src/Input.recipe.ts +41 -28
- package/src/Input.tsx +23 -7
- package/src/InputGroup.tsx +26 -12
- package/src/Kbd.tsx +26 -0
- package/src/Link.tsx +3 -3
- package/src/LinkBox.tsx +2 -3
- package/src/LinkButton.tsx +81 -0
- package/src/List.tsx +8 -6
- package/src/ListBox.recipe.ts +43 -0
- package/src/ListBox.tsx +88 -0
- package/src/Menu.recipe.ts +51 -17
- package/src/Menu.tsx +117 -2
- package/src/Modal.recipe.ts +17 -9
- package/src/Modal.tsx +137 -24
- package/src/MoreMenuButton.tsx +63 -0
- package/src/NativeSelect.tsx +41 -14
- package/src/NativeSelectField.tsx +84 -0
- package/src/NumberField.recipe.ts +108 -0
- package/src/NumberField.tsx +138 -0
- package/src/PopoverArrow.tsx +19 -5
- package/src/ProgressBar.tsx +3 -5
- package/src/Radio.recipe.ts +108 -0
- package/src/Radio.tsx +62 -0
- package/src/RadioGroup.tsx +68 -0
- package/src/Select.recipe.ts +211 -0
- package/src/Select.tsx +194 -0
- package/src/SharedUIProvider.tsx +63 -7
- package/src/Skeleton.tsx +146 -0
- package/src/Slide.tsx +2 -2
- package/src/Slider.recipe.ts +27 -16
- package/src/Slider.tsx +78 -6
- package/src/Spinner.tsx +11 -9
- package/src/Svg.tsx +2 -3
- package/src/Switch.recipe.ts +57 -11
- package/src/Switch.tsx +39 -7
- package/src/Text.recipe.ts +26 -0
- package/src/Text.tsx +6 -2
- package/src/TextField.tsx +54 -40
- package/src/Toast.recipe.ts +52 -15
- package/src/Toast.tsx +151 -46
- package/src/Tooltip.recipe.ts +32 -0
- package/src/Tooltip.tsx +68 -28
- package/src/TooltipButton.tsx +229 -0
- package/src/UnmountCallback.tsx +2 -2
- package/src/VisuallyHidden.tsx +1 -1
- package/src/base-preset.ts +336 -84
- package/src/{chakra-tokens.ts → base-tokens.ts} +7 -7
- package/src/button-icon.ts +22 -0
- package/src/data-attrs.ts +16 -0
- package/src/dense-preset.ts +107 -0
- package/src/hooks/useBreakpointValue.ts +4 -4
- package/src/hooks/useClipboard.ts +63 -0
- package/src/hooks/useDisclosure.ts +31 -0
- package/src/hooks/useMediaQuery.ts +27 -0
- package/src/hooks/usePrevious.ts +15 -0
- package/src/index.ts +29 -3
- package/src/rac-locale.ts +33 -0
- package/src/system.ts +22 -1
- package/src/TextField.recipe.ts +0 -54
package/LICENSE.md
CHANGED
|
@@ -19,3 +19,43 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
19
19
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
20
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
21
|
SOFTWARE.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Third-party notices
|
|
26
|
+
|
|
27
|
+
This package includes design tokens and component styling derived from
|
|
28
|
+
[Chakra UI](https://github.com/chakra-ui/chakra-ui) v2: the token scales in
|
|
29
|
+
`src/base-tokens.ts` were snapshotted from `@chakra-ui/theme`'s default
|
|
30
|
+
values, and the component recipes were ported from Chakra's component styles.
|
|
31
|
+
Chakra UI is used under the MIT License:
|
|
32
|
+
|
|
33
|
+
> MIT License
|
|
34
|
+
>
|
|
35
|
+
> Copyright (c) 2019 Segun Adebayo
|
|
36
|
+
>
|
|
37
|
+
> Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
38
|
+
> of this software and associated documentation files (the "Software"), to deal
|
|
39
|
+
> in the Software without restriction, including without limitation the rights
|
|
40
|
+
> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
41
|
+
> copies of the Software, and to permit persons to whom the Software is
|
|
42
|
+
> furnished to do so, subject to the following conditions:
|
|
43
|
+
>
|
|
44
|
+
> The above copyright notice and this permission notice shall be included in all
|
|
45
|
+
> copies or substantial portions of the Software.
|
|
46
|
+
>
|
|
47
|
+
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
48
|
+
> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
49
|
+
> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
50
|
+
> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
51
|
+
> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
52
|
+
> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
53
|
+
> SOFTWARE.
|
|
54
|
+
|
|
55
|
+
This package's `lang/ui.<locale>.json` catalogs include translations of a few
|
|
56
|
+
user-interface strings copied from
|
|
57
|
+
[React Spectrum](https://github.com/adobe/react-spectrum) — from react-aria at
|
|
58
|
+
tag `react-aria-components@1.19.0`, and from the Spectrum 2 InlineAlert
|
|
59
|
+
component's catalogs (see the README's "Strings" section for which). React
|
|
60
|
+
Spectrum is copyright Adobe and its contributors, and is used under the Apache
|
|
61
|
+
License, Version 2.0: <https://www.apache.org/licenses/LICENSE-2.0>.
|
package/README.md
CHANGED
|
@@ -1,32 +1,34 @@
|
|
|
1
1
|
# @microbit/ui
|
|
2
2
|
|
|
3
3
|
react-aria-components + Panda CSS primitives with a design language ported from
|
|
4
|
-
the Micro:bit Educational Foundation apps' original
|
|
4
|
+
the Micro:bit Educational Foundation apps' original
|
|
5
|
+
[Chakra UI](https://chakra-ui.com/) v2 themes (see
|
|
6
|
+
[Chakra UI heritage](#chakra-ui-heritage-and-license)).
|
|
5
7
|
|
|
6
8
|
The package **ships as source**: components import `styled-system/*`, which
|
|
7
9
|
each consumer generates with its own Panda preset stack. There is no build
|
|
8
|
-
step and no CSS shipped — the consumer's
|
|
9
|
-
its tree uses
|
|
10
|
+
step and no CSS shipped — the consumer's Panda run produces exactly the styles
|
|
11
|
+
its tree uses (`panda codegen` for the `styled-system/*` helpers, the Panda
|
|
12
|
+
PostCSS plugin for the CSS).
|
|
10
13
|
|
|
11
14
|
## App-side installation
|
|
12
15
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
an app must do:
|
|
16
|
+
The repo's Storybook harness (`apps/storybook`) is a worked example of the
|
|
17
|
+
setup below, minus the app/brand presets. Everything an app must do:
|
|
16
18
|
|
|
17
19
|
1. **Panda preset stack** (`panda.config.ts`): `@pandacss/preset-base`, then
|
|
18
20
|
the **base preset** (`@microbit/ui/base-preset` — the complete micro:bit
|
|
19
|
-
design system
|
|
20
|
-
|
|
21
|
-
|
|
21
|
+
design system), then optionally `@microbit/ui/dense-preset` (the × 0.88
|
|
22
|
+
spacing / × 0.9 font-size density the information-dense apps use), then
|
|
23
|
+
optionally the app's own preset, then optionally a **private brand
|
|
24
|
+
preset** (Foundation colours, licensed fonts).
|
|
22
25
|
|
|
23
26
|
Later presets override earlier ones token-by-token — the base recipes and
|
|
24
27
|
semantic tokens reference the brand tokens, which is how a brand swap
|
|
25
28
|
restyles everything without touching recipes. Set `eject: true` (the stack
|
|
26
29
|
supplies the full token system). After changing an _external_ preset
|
|
27
|
-
dependency, regenerate clean: `rm -rf styled-system
|
|
28
|
-
|
|
29
|
-
changes.
|
|
30
|
+
dependency, regenerate clean: `rm -rf styled-system && npm run panda` —
|
|
31
|
+
incremental codegen does not detect external preset changes.
|
|
30
32
|
|
|
31
33
|
2. **Include this package's source** in `panda.config.ts` so Panda extracts
|
|
32
34
|
the styles the components use:
|
|
@@ -39,11 +41,40 @@ npm run panda` — incremental codegen does not detect external preset
|
|
|
39
41
|
3. **Resolve `styled-system/*` onto the generated output** for all
|
|
40
42
|
importers, this package's source included — a `styled-system` alias in
|
|
41
43
|
both `tsconfig.json` `paths` and the bundler config (see the
|
|
42
|
-
`viteFinal` in `.storybook/main.ts`).
|
|
43
|
-
4. **
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
44
|
+
`viteFinal` in `apps/storybook`'s `.storybook/main.ts`).
|
|
45
|
+
4. **Generate and load the CSS** with Panda's PostCSS plugin. Keep Vite's
|
|
46
|
+
default transformer — do **not** set `css.transformer: "lightningcss"`,
|
|
47
|
+
which disables PostCSS. Add a `postcss.config.cjs`:
|
|
48
|
+
|
|
49
|
+
```js
|
|
50
|
+
module.exports = { plugins: { "@pandacss/dev/postcss": {} } };
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Run `panda codegen` as a `prepare`/`predev` step so the `styled-system/*`
|
|
54
|
+
helpers exist before `tsc`; the plugin generates the CSS during the bundle.
|
|
55
|
+
Import **one** entry stylesheet — first, before app styles — that declares
|
|
56
|
+
the cascade-layer order; the plugin injects the generated CSS into it (the
|
|
57
|
+
declaration must list all of Panda's layers, hence ≥5 names):
|
|
58
|
+
|
|
59
|
+
```css
|
|
60
|
+
/* e.g. src/layers.css, imported once at the app root */
|
|
61
|
+
@layer reset, vendor, base, tokens, recipes, utilities;
|
|
62
|
+
|
|
63
|
+
@import "@microbit/ui/reset.css" layer(reset);
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
The `reset.css` import is **required**: it carries the
|
|
67
|
+
`* { border-color; word-wrap }` defaults, which must sit in the bottom
|
|
68
|
+
layer (the legacy-Safari cascade-layer flattening specificity-boosts
|
|
69
|
+
higher layers above CSS it can't see — runtime-injected styles, other
|
|
70
|
+
files). Without it, elements that set a border
|
|
71
|
+
width but no colour render `currentColor` borders.
|
|
72
|
+
The `vendor` layer is for third-party stylesheets: import any vendor CSS
|
|
73
|
+
with `@import "..." layer(vendor)` so it beats the preflight reset but
|
|
74
|
+
loses to app styling. See `apps/storybook`'s
|
|
75
|
+
`.storybook/{layers.css,preview.tsx,main.ts}` + `postcss.config.cjs` for
|
|
76
|
+
the worked example.
|
|
77
|
+
|
|
47
78
|
5. **react-intl**: an `IntlProvider` above any shared-ui usage. English
|
|
48
79
|
works with no setup (components carry inline `defaultMessage`); for
|
|
49
80
|
other locales compile this package's `lang/ui.<locale>.json` into the
|
|
@@ -53,10 +84,120 @@ node_modules/@microbit/ui/lang/ui.fr.json --ast --out-file ...` (multiple
|
|
|
53
84
|
input files merge; ids are `ui.`-namespaced so they can't collide). This
|
|
54
85
|
keeps the strings in the app's lazily loaded locale chunks rather than
|
|
55
86
|
an eagerly bundled catalog-of-all-locales.
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
87
|
+
|
|
88
|
+
The `locale` you pass must be the language the app actually renders in,
|
|
89
|
+
which is not always the user's language setting: apps that list languages
|
|
90
|
+
only an embedded product (e.g. MakeCode) is translated into fall back to
|
|
91
|
+
their English catalog for those, and should then pass `en`, keeping the
|
|
92
|
+
chosen language in their own settings for the embed. Everything downstream
|
|
93
|
+
reads this locale as a statement about the rendered page — `<html lang>`
|
|
94
|
+
and `<html dir>` via `SharedUIProvider`, react-aria's text direction and
|
|
95
|
+
built-in strings, `Intl` number/date formatting and plural rules. A catalog
|
|
96
|
+
that loads but has per-message gaps (an incomplete translation) is still
|
|
97
|
+
that language; only a wholesale fallback to the English catalog should
|
|
98
|
+
claim `en`.
|
|
99
|
+
|
|
100
|
+
6. **`SharedUIProvider`** inside the `IntlProvider`, wrapping the app. It
|
|
101
|
+
passes the locale on to react-aria, which translates its own built-in
|
|
102
|
+
strings (see [Strings](#strings)); without it those follow the browser
|
|
103
|
+
rather than the app's language setting. It also keeps `<html lang>` and
|
|
104
|
+
`<html dir>` in step with the locale, so assistive tech announces the page
|
|
105
|
+
in the app's language and an RTL language lays out the right way round —
|
|
106
|
+
pass `setDocumentLang={false}` where the app doesn't own the document it's
|
|
107
|
+
mounted in (an embedded widget). Also takes an optional overlay-close
|
|
108
|
+
registrar, so the app can dismiss open menus from outside the tree (e.g.
|
|
109
|
+
the Android hardware back button).
|
|
110
|
+
|
|
111
|
+
An app that passes `setDocumentLang={false}` **must set `dir` itself**,
|
|
112
|
+
on whatever element it mounts into, if it offers an RTL language. The two
|
|
113
|
+
halves of mirroring read the direction from different places: react-aria
|
|
114
|
+
takes it from the provider locale in JS and mirrors regardless of the DOM,
|
|
115
|
+
while logical properties and Panda's `_rtl` rules match on `dir`. With one
|
|
116
|
+
side mirrored and the other not, components that combine both come apart —
|
|
117
|
+
`Slider`'s thumb moves to the mirrored end while its filled track stays.
|
|
118
|
+
|
|
119
|
+
7. **`ToastProvider`** once near the root, inside the two providers above.
|
|
120
|
+
|
|
121
|
+
## Upgrading in an app
|
|
122
|
+
|
|
123
|
+
After bumping the `@microbit/ui` version:
|
|
124
|
+
|
|
125
|
+
1. `npm install` — the apps' postinstall runs `panda codegen`, but incremental
|
|
126
|
+
codegen does not detect external preset changes, so regenerate clean:
|
|
127
|
+
`rm -rf styled-system && npm run panda`.
|
|
128
|
+
2. `npm run i18n:compile` — recompiles the app's per-locale catalogs so new
|
|
129
|
+
and retranslated `ui.*` strings ship (otherwise they fall back to English,
|
|
130
|
+
or stay missing).
|
|
131
|
+
|
|
132
|
+
## Legacy browser support (Safari < 15) — temporary
|
|
133
|
+
|
|
134
|
+
Panda's output uses two things Safari below 15 mishandles. If an app must
|
|
135
|
+
support that far back (e.g. Safari 14.1 web views), add the app-side wiring
|
|
136
|
+
below. **All of it is meant to be deleted once the app's support floor rises
|
|
137
|
+
past these browsers** — it lives entirely in the consuming app's build config,
|
|
138
|
+
never in shipped component source. This package's own Storybook does _not_ use
|
|
139
|
+
any of it (it targets modern browsers, where these work natively).
|
|
140
|
+
|
|
141
|
+
Two concerns:
|
|
142
|
+
|
|
143
|
+
1. **`@layer`** — Safari < 15.4 drops `@layer` blocks wholesale, leaving the
|
|
144
|
+
app unstyled. Flatten them with `@csstools/postcss-cascade-layers` (which
|
|
145
|
+
rewrites layers into `:not(#\#)` specificity fallbacks; ~+8% gzipped CSS,
|
|
146
|
+
mostly compressible).
|
|
147
|
+
2. **Logical shorthands + `var()`** — Safari 14.x silently drops
|
|
148
|
+
`padding-inline: var(--…)` and friends (a literal value, or the -start/-end
|
|
149
|
+
longhands, both work). Panda emits these shorthands for its px/py/mx/my
|
|
150
|
+
utilities, so most token spacing collapses. Expand them to longhands with
|
|
151
|
+
this package's `postcss-legacy-safari` plugin (kept logical, so RTL flips).
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
npm i -D @csstools/postcss-cascade-layers
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
```js
|
|
158
|
+
// postcss.config.cjs — the two legacy plugins run AFTER Panda's (step 4), so
|
|
159
|
+
// switch that config to array form:
|
|
160
|
+
const {
|
|
161
|
+
expandLogicalShorthands,
|
|
162
|
+
} = require("@microbit/ui/postcss-legacy-safari");
|
|
163
|
+
|
|
164
|
+
module.exports = {
|
|
165
|
+
plugins: [
|
|
166
|
+
require("@pandacss/dev/postcss")(),
|
|
167
|
+
expandLogicalShorthands(),
|
|
168
|
+
require("@csstools/postcss-cascade-layers"),
|
|
169
|
+
],
|
|
170
|
+
};
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
```ts
|
|
174
|
+
// vite.config.ts — pin the CSS/JS floor. Otherwise the lightningcss minifier
|
|
175
|
+
// inherits build.target and downlevels logical longhands into fragile
|
|
176
|
+
// :lang()-based physical rules. Keep in sync with package.json "browserslist".
|
|
177
|
+
const BUILD_TARGETS = ["safari14.1", "ios14.5", "chrome90", "edge90", "firefox88"];
|
|
178
|
+
// ...
|
|
179
|
+
build: {
|
|
180
|
+
target: BUILD_TARGETS,
|
|
181
|
+
cssTarget: BUILD_TARGETS,
|
|
182
|
+
cssMinify: "lightningcss", // lightningcss as minifier only, not the transformer
|
|
183
|
+
},
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
To drop it all: raise `BUILD_TARGETS`/`browserslist` past the affected
|
|
187
|
+
browsers, then remove the two PostCSS plugins (and this package's
|
|
188
|
+
`postcss-legacy-safari` export).
|
|
189
|
+
|
|
190
|
+
RTL is fine at this floor, with one thing to know. Panda's `_rtl` condition
|
|
191
|
+
emits `:where([dir=rtl], :dir(rtl))`, and `:dir()` is Safari 16.4. At the
|
|
192
|
+
pinned targets lightningcss rewrites that arm into a `:lang()` list, leaving
|
|
193
|
+
the `[dir=rtl]` arm — which is the one `SharedUIProvider` sets — intact. So
|
|
194
|
+
the legacy build matches on `dir` as intended, and additionally on
|
|
195
|
+
`lang="ar"` and friends, which modern builds don't. Everything else the
|
|
196
|
+
mirroring uses (logical longhands, `text-align: start`, custom properties in
|
|
197
|
+
`calc()`) passes through untouched; only logical _shorthands_ need the shim,
|
|
198
|
+
as before. `:where()` contributes no specificity, so the RTL rules win on
|
|
199
|
+
source order — de-layering preserves it, since it pads later layers rather
|
|
200
|
+
than reordering within one.
|
|
60
201
|
|
|
61
202
|
## The CSS-variable contract
|
|
62
203
|
|
|
@@ -75,15 +216,59 @@ keep them stable:
|
|
|
75
216
|
- Brand/app presets may change token _values_, never token _names_.
|
|
76
217
|
|
|
77
218
|
Semantic tokens (`languageText`, `statusBarBg`, `danger.*`, `toast*Bg`,
|
|
78
|
-
`controlCheckedBg`, `focusBorder`, …) are the extension points
|
|
79
|
-
override; they resolve through var indirection, so overrides
|
|
80
|
-
the token is consumed.
|
|
219
|
+
`button.*`, `controlCheckedBg`, `focusBorder`, …) are the extension points
|
|
220
|
+
brand presets override; they resolve through var indirection, so overrides
|
|
221
|
+
apply wherever the token is consumed.
|
|
222
|
+
|
|
223
|
+
`focusRing` and `focusBorder` need more care: their values are condition
|
|
224
|
+
objects and a merge replaces a value wholesale, so the flat form drops the
|
|
225
|
+
on-dark flip below. Keep the shape:
|
|
226
|
+
|
|
227
|
+
```ts
|
|
228
|
+
focusBorder: { value: { base: "{colors.brand.700}", _onDark: "{colors.white}" } };
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
## Dark surfaces
|
|
232
|
+
|
|
233
|
+
Focus indicators are surface-aware through one tag. The default focus ring
|
|
234
|
+
is ink; on a dark surface it must be white, so tag the surface element by
|
|
235
|
+
spreading the exported constant:
|
|
236
|
+
|
|
237
|
+
```tsx
|
|
238
|
+
import { darkSurface } from "@microbit/ui";
|
|
239
|
+
|
|
240
|
+
<header {...darkSurface}>…</header>; // a black toolbar, a coloured sidebar bar
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
Custom properties inherit, so tagging the bar covers the bar itself and
|
|
244
|
+
every control inside it — including ones added later. If the tagged element
|
|
245
|
+
is focusable, tag one level in instead: its own ring is drawn _outside_ it,
|
|
246
|
+
on whatever is behind it. (The Toast does this — the card is dark and
|
|
247
|
+
focusable, so the tag sits on its close button.) Portalled overlays (a
|
|
248
|
+
modal opened from a dark toolbar) escape the tag with the DOM, which is
|
|
249
|
+
correct. Under the hood it is `data-surface="dark"`, which the preset's
|
|
250
|
+
`onDark` condition scopes the `focusRing`/`focusBorder` token flips to.
|
|
251
|
+
|
|
252
|
+
Two rules:
|
|
253
|
+
|
|
254
|
+
- **Dark surfaces must tag** — the ink ring is near-invisible on them, and
|
|
255
|
+
there is no automatic detection.
|
|
256
|
+
- **Tag surfaces, not themes**: "dark" describes the surface's own
|
|
257
|
+
luminance, so tag surfaces that are dark by design (a black toolbar, a
|
|
258
|
+
coloured sidebar bar) rather than anything relative to the app's overall
|
|
259
|
+
look. This is what will let the tag survive a dark mode if we ship one:
|
|
260
|
+
designed-dark surfaces stay dark and keep their tags; everything else
|
|
261
|
+
stays untagged, and a dark mode would flip the untagged defaults via
|
|
262
|
+
token conditions, never via markup.
|
|
263
|
+
|
|
264
|
+
Rule of thumb for coloured bars: tag when the surface lacks 3:1 contrast
|
|
265
|
+
against ink — roughly, darker than the grey ramp's 500.
|
|
81
266
|
|
|
82
267
|
## Runtime token lookups
|
|
83
268
|
|
|
84
269
|
For values that feed _computation_ rather than stylesheets (canvas painting,
|
|
85
|
-
colour math, inline `style` for data-driven values
|
|
86
|
-
|
|
270
|
+
colour math, inline `style` for data-driven values that Panda's static
|
|
271
|
+
extraction can't see), import the runtime lookup:
|
|
87
272
|
|
|
88
273
|
```ts
|
|
89
274
|
import { token } from "@microbit/ui"; // re-exports styled-system/tokens
|
|
@@ -116,3 +301,71 @@ Crowdin via the repo-root `npm run update-translations -- <path to extracted
|
|
|
116
301
|
Crowdin ZIP>` (config-driven over packages in
|
|
117
302
|
`bin/update-translations.cjs`), after which you run `npm run i18n:tidy`
|
|
118
303
|
from the root.
|
|
304
|
+
|
|
305
|
+
`bin/i18n-packages.cjs` lists every locale a consuming app ships, so a string
|
|
306
|
+
translated for one app is in place for the next. `i18n:tidy` backfills anything
|
|
307
|
+
missing from English, so an untranslated string renders English rather than
|
|
308
|
+
blank.
|
|
309
|
+
|
|
310
|
+
Until this package's Crowdin project is wired up, the non-English catalogs are
|
|
311
|
+
pre-Crowdin seeds — some strings AI-drafted, all of them for Crowdin to review
|
|
312
|
+
— credited as follows. Translations of the same strings elsewhere in the
|
|
313
|
+
micro:bit translation programme: ml-trainer, python-editor-v3 and classroom,
|
|
314
|
+
and MakeCode's editor strings, which are not in the pxt repo but can be fetched
|
|
315
|
+
per locale from
|
|
316
|
+
`https://makecode.microbit.org/api/translations?lang=<locale>&filename=strings.json&approved=true`.
|
|
317
|
+
The `ui.toast-status-*` words come from Spectrum 2's InlineAlert catalogs
|
|
318
|
+
(`@react-spectrum/s2/intl/*.json`, keys `inlinealert.informative` / `notice` /
|
|
319
|
+
`negative` / `positive`), under the same licence and notice as the react-aria
|
|
320
|
+
seed below.
|
|
321
|
+
|
|
322
|
+
### react-aria's own strings
|
|
323
|
+
|
|
324
|
+
Separately from all of the above, react-aria has built-in strings of its own
|
|
325
|
+
and translates them from catalogs it bundles, nothing to do with react-intl.
|
|
326
|
+
`SharedUIProvider` hands it the app's locale so the two agree. It bundles 34
|
|
327
|
+
locales; of ours, ca, cy, ga-IE, lo and vi (and the `lol` pseudo-locale) are not
|
|
328
|
+
among them and fall back to English, and there is no supported way to teach it
|
|
329
|
+
more (the rest resolve, including where our id is less specific than
|
|
330
|
+
react-aria's — `fr` finds its fr-FR).
|
|
331
|
+
|
|
332
|
+
Where react-aria lets a prop replace one of those strings, the component
|
|
333
|
+
passes our own react-intl message instead, so the string rides `lang/` and
|
|
334
|
+
Crowdin like everything else and the missing locales can catch up:
|
|
335
|
+
|
|
336
|
+
- `ui.numberfield-increase` / `ui.numberfield-decrease` — NumberField's
|
|
337
|
+
stepper button labels
|
|
338
|
+
- `ui.toast-region` — the toast region's landmark label (counts the visible
|
|
339
|
+
toasts, as react-aria's does)
|
|
340
|
+
- `ui.combobox-trigger` / `ui.combobox-listbox` — the button that opens a
|
|
341
|
+
ComboBox and its popup list
|
|
342
|
+
- `ui.select-placeholder` — Select's placeholder when the caller passes none
|
|
343
|
+
- `ui.select-row-action` — the name of a `slot="selection"` checkbox in a
|
|
344
|
+
GridList row, read together with the row's content. (A future Table's
|
|
345
|
+
select-all header checkbox shares the slot name and will need its own
|
|
346
|
+
message.)
|
|
347
|
+
|
|
348
|
+
Their non-English translations were pre-seeded from react-aria's own catalogs
|
|
349
|
+
([adobe/react-spectrum](https://github.com/adobe/react-spectrum) at tag
|
|
350
|
+
`react-aria-components@1.19.0`, Apache 2.0 — see the notice in
|
|
351
|
+
[LICENSE.md](LICENSE.md)) for the locales both sides cover; the five above had
|
|
352
|
+
no such source. That provenance is recorded here rather than in `lang/` because
|
|
353
|
+
Crowdin roundtrips rewrite those files.
|
|
354
|
+
|
|
355
|
+
What react-aria does not expose as a prop — live announcements ("2 items
|
|
356
|
+
selected"), its hidden dismiss buttons — still comes from its bundled catalogs
|
|
357
|
+
and falls back to English in the locales above.
|
|
358
|
+
|
|
359
|
+
## Chakra UI heritage and license
|
|
360
|
+
|
|
361
|
+
This package's design language began as a faithful port of
|
|
362
|
+
[Chakra UI](https://chakra-ui.com/) v2's, done so the apps it serves could
|
|
363
|
+
leave Chakra without a redesign: `src/base-tokens.ts` was snapshotted from
|
|
364
|
+
`@chakra-ui/theme`'s default token scales, and the `*.recipe.ts` files were
|
|
365
|
+
ported from Chakra's component styles onto Panda config recipes.
|
|
366
|
+
However far it evolves from that starting point, it owes its foundations to
|
|
367
|
+
Chakra. Thanks to Segun Adebayo and the Chakra UI contributors.
|
|
368
|
+
|
|
369
|
+
The package is [MIT](LICENSE.md) © Micro:bit Educational Foundation and
|
|
370
|
+
contributors; Chakra UI is MIT © Segun Adebayo, and its notice is carried
|
|
371
|
+
in [LICENSE.md](LICENSE.md).
|
package/lang/ui.ar.json
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
{
|
|
2
|
+
"ui.breadcrumb": {
|
|
3
|
+
"defaultMessage": "مسار التنقل",
|
|
4
|
+
"description": "Accessible label for the breadcrumb navigation trail (the WAI-ARIA APG's conventional name)"
|
|
5
|
+
},
|
|
6
|
+
"ui.close-action": {
|
|
7
|
+
"defaultMessage": "إغلاق",
|
|
8
|
+
"description": "Close button text or label"
|
|
9
|
+
},
|
|
10
|
+
"ui.combobox-listbox": {
|
|
11
|
+
"defaultMessage": "مقترحات",
|
|
12
|
+
"description": "Accessible label for a ComboBox's dropdown list of suggestions"
|
|
13
|
+
},
|
|
14
|
+
"ui.combobox-trigger": {
|
|
15
|
+
"defaultMessage": "عرض المقترحات",
|
|
16
|
+
"description": "Accessible label for the button that opens a ComboBox's suggestions dropdown"
|
|
17
|
+
},
|
|
18
|
+
"ui.loading": {
|
|
19
|
+
"defaultMessage": "جاري التحميل",
|
|
20
|
+
"description": "Announced by screen readers for a button showing a loading spinner"
|
|
21
|
+
},
|
|
22
|
+
"ui.new-tab-notice": {
|
|
23
|
+
"defaultMessage": "يفتح في علامة تبويب جديدة",
|
|
24
|
+
"description": "Visually hidden suffix on links that open a new tab, announced by screen readers"
|
|
25
|
+
},
|
|
26
|
+
"ui.numberfield-decrease": {
|
|
27
|
+
"defaultMessage": "خفض {fieldLabel}",
|
|
28
|
+
"description": "Accessible label for a number field's decrement stepper; fieldLabel is the field's label and may be empty"
|
|
29
|
+
},
|
|
30
|
+
"ui.numberfield-increase": {
|
|
31
|
+
"defaultMessage": "زيادة {fieldLabel}",
|
|
32
|
+
"description": "Accessible label for a number field's increment stepper; fieldLabel is the field's label and may be empty"
|
|
33
|
+
},
|
|
34
|
+
"ui.select-placeholder": {
|
|
35
|
+
"defaultMessage": "حدد عنصرًا",
|
|
36
|
+
"description": "Shown in a Select's trigger while nothing is chosen and the app supplies no placeholder"
|
|
37
|
+
},
|
|
38
|
+
"ui.select-row-action": {
|
|
39
|
+
"defaultMessage": "تحديد",
|
|
40
|
+
"description": "Accessible label for a row-selection checkbox in a list or table; screen readers read it together with the row's content"
|
|
41
|
+
},
|
|
42
|
+
"ui.toast-region": {
|
|
43
|
+
"defaultMessage": "{count, plural, zero {لا إشعارات} one {إشعار واحد} two {إشعاران} few {# إشعارات} many {# إشعارًا} other {# إشعار}}.",
|
|
44
|
+
"description": "Accessible label for the notifications area; count is the number of notifications showing"
|
|
45
|
+
},
|
|
46
|
+
"ui.toast-status-error": {
|
|
47
|
+
"defaultMessage": "خطأ",
|
|
48
|
+
"description": "Announced by screen readers before an error notification"
|
|
49
|
+
},
|
|
50
|
+
"ui.toast-status-info": {
|
|
51
|
+
"defaultMessage": "معلومات",
|
|
52
|
+
"description": "Announced by screen readers before an informational notification"
|
|
53
|
+
},
|
|
54
|
+
"ui.toast-status-success": {
|
|
55
|
+
"defaultMessage": "تم بنجاح",
|
|
56
|
+
"description": "Announced by screen readers before a success notification"
|
|
57
|
+
},
|
|
58
|
+
"ui.toast-status-warning": {
|
|
59
|
+
"defaultMessage": "تحذير",
|
|
60
|
+
"description": "Announced by screen readers before a warning notification"
|
|
61
|
+
}
|
|
62
|
+
}
|
package/lang/ui.ca.json
CHANGED
|
@@ -1,22 +1,62 @@
|
|
|
1
1
|
{
|
|
2
|
+
"ui.breadcrumb": {
|
|
3
|
+
"defaultMessage": "Rastres de navegació",
|
|
4
|
+
"description": "Accessible label for the breadcrumb navigation trail (the WAI-ARIA APG's conventional name)"
|
|
5
|
+
},
|
|
2
6
|
"ui.close-action": {
|
|
3
7
|
"defaultMessage": "Tanca ",
|
|
4
8
|
"description": "Close button text or label"
|
|
5
9
|
},
|
|
10
|
+
"ui.combobox-listbox": {
|
|
11
|
+
"defaultMessage": "Suggeriments",
|
|
12
|
+
"description": "Accessible label for a ComboBox's dropdown list of suggestions"
|
|
13
|
+
},
|
|
14
|
+
"ui.combobox-trigger": {
|
|
15
|
+
"defaultMessage": "Mostra els suggeriments",
|
|
16
|
+
"description": "Accessible label for the button that opens a ComboBox's suggestions dropdown"
|
|
17
|
+
},
|
|
18
|
+
"ui.loading": {
|
|
19
|
+
"defaultMessage": "S'està carregant",
|
|
20
|
+
"description": "Announced by screen readers for a button showing a loading spinner"
|
|
21
|
+
},
|
|
22
|
+
"ui.new-tab-notice": {
|
|
23
|
+
"defaultMessage": "opens in a new tab",
|
|
24
|
+
"description": "Visually hidden suffix on links that open a new tab, announced by screen readers"
|
|
25
|
+
},
|
|
26
|
+
"ui.numberfield-decrease": {
|
|
27
|
+
"defaultMessage": "Decrementa {fieldLabel}",
|
|
28
|
+
"description": "Accessible label for a number field's decrement stepper; fieldLabel is the field's label and may be empty"
|
|
29
|
+
},
|
|
30
|
+
"ui.numberfield-increase": {
|
|
31
|
+
"defaultMessage": "Incrementa {fieldLabel}",
|
|
32
|
+
"description": "Accessible label for a number field's increment stepper; fieldLabel is the field's label and may be empty"
|
|
33
|
+
},
|
|
34
|
+
"ui.select-placeholder": {
|
|
35
|
+
"defaultMessage": "Selecciona un element",
|
|
36
|
+
"description": "Shown in a Select's trigger while nothing is chosen and the app supplies no placeholder"
|
|
37
|
+
},
|
|
38
|
+
"ui.select-row-action": {
|
|
39
|
+
"defaultMessage": "Selecciona",
|
|
40
|
+
"description": "Accessible label for a row-selection checkbox in a list or table; screen readers read it together with the row's content"
|
|
41
|
+
},
|
|
42
|
+
"ui.toast-region": {
|
|
43
|
+
"defaultMessage": "{count, plural, one {# notificació} other {# notificacions}}.",
|
|
44
|
+
"description": "Accessible label for the notifications area; count is the number of notifications showing"
|
|
45
|
+
},
|
|
6
46
|
"ui.toast-status-error": {
|
|
7
47
|
"defaultMessage": "Error",
|
|
8
48
|
"description": "Announced by screen readers before an error notification"
|
|
9
49
|
},
|
|
10
50
|
"ui.toast-status-info": {
|
|
11
|
-
"defaultMessage": "
|
|
51
|
+
"defaultMessage": "Informació",
|
|
12
52
|
"description": "Announced by screen readers before an informational notification"
|
|
13
53
|
},
|
|
14
54
|
"ui.toast-status-success": {
|
|
15
|
-
"defaultMessage": "
|
|
55
|
+
"defaultMessage": "Completat amb èxit",
|
|
16
56
|
"description": "Announced by screen readers before a success notification"
|
|
17
57
|
},
|
|
18
58
|
"ui.toast-status-warning": {
|
|
19
|
-
"defaultMessage": "
|
|
59
|
+
"defaultMessage": "Advertència",
|
|
20
60
|
"description": "Announced by screen readers before a warning notification"
|
|
21
61
|
}
|
|
22
62
|
}
|
package/lang/ui.cy.json
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
{
|
|
2
|
+
"ui.breadcrumb": {
|
|
3
|
+
"defaultMessage": "Briwsionyn Bara",
|
|
4
|
+
"description": "Accessible label for the breadcrumb navigation trail (the WAI-ARIA APG's conventional name)"
|
|
5
|
+
},
|
|
6
|
+
"ui.close-action": {
|
|
7
|
+
"defaultMessage": "Cau",
|
|
8
|
+
"description": "Close button text or label"
|
|
9
|
+
},
|
|
10
|
+
"ui.combobox-listbox": {
|
|
11
|
+
"defaultMessage": "Suggestions",
|
|
12
|
+
"description": "Accessible label for a ComboBox's dropdown list of suggestions"
|
|
13
|
+
},
|
|
14
|
+
"ui.combobox-trigger": {
|
|
15
|
+
"defaultMessage": "Show suggestions",
|
|
16
|
+
"description": "Accessible label for the button that opens a ComboBox's suggestions dropdown"
|
|
17
|
+
},
|
|
18
|
+
"ui.loading": {
|
|
19
|
+
"defaultMessage": "Wrthi'n Llwytho",
|
|
20
|
+
"description": "Announced by screen readers for a button showing a loading spinner"
|
|
21
|
+
},
|
|
22
|
+
"ui.new-tab-notice": {
|
|
23
|
+
"defaultMessage": "opens in a new tab",
|
|
24
|
+
"description": "Visually hidden suffix on links that open a new tab, announced by screen readers"
|
|
25
|
+
},
|
|
26
|
+
"ui.numberfield-decrease": {
|
|
27
|
+
"defaultMessage": "Decrease {fieldLabel}",
|
|
28
|
+
"description": "Accessible label for a number field's decrement stepper; fieldLabel is the field's label and may be empty"
|
|
29
|
+
},
|
|
30
|
+
"ui.numberfield-increase": {
|
|
31
|
+
"defaultMessage": "Increase {fieldLabel}",
|
|
32
|
+
"description": "Accessible label for a number field's increment stepper; fieldLabel is the field's label and may be empty"
|
|
33
|
+
},
|
|
34
|
+
"ui.select-placeholder": {
|
|
35
|
+
"defaultMessage": "Select an item",
|
|
36
|
+
"description": "Shown in a Select's trigger while nothing is chosen and the app supplies no placeholder"
|
|
37
|
+
},
|
|
38
|
+
"ui.select-row-action": {
|
|
39
|
+
"defaultMessage": "Dewis",
|
|
40
|
+
"description": "Accessible label for a row-selection checkbox in a list or table; screen readers read it together with the row's content"
|
|
41
|
+
},
|
|
42
|
+
"ui.toast-region": {
|
|
43
|
+
"defaultMessage": "{count, plural, one {# notification} other {# notifications}}.",
|
|
44
|
+
"description": "Accessible label for the notifications area; count is the number of notifications showing"
|
|
45
|
+
},
|
|
46
|
+
"ui.toast-status-error": {
|
|
47
|
+
"defaultMessage": "Gwall",
|
|
48
|
+
"description": "Announced by screen readers before an error notification"
|
|
49
|
+
},
|
|
50
|
+
"ui.toast-status-info": {
|
|
51
|
+
"defaultMessage": "Information",
|
|
52
|
+
"description": "Announced by screen readers before an informational notification"
|
|
53
|
+
},
|
|
54
|
+
"ui.toast-status-success": {
|
|
55
|
+
"defaultMessage": "Llwyddiant",
|
|
56
|
+
"description": "Announced by screen readers before a success notification"
|
|
57
|
+
},
|
|
58
|
+
"ui.toast-status-warning": {
|
|
59
|
+
"defaultMessage": "Rhybudd",
|
|
60
|
+
"description": "Announced by screen readers before a warning notification"
|
|
61
|
+
}
|
|
62
|
+
}
|
package/lang/ui.de.json
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
{
|
|
2
|
+
"ui.breadcrumb": {
|
|
3
|
+
"defaultMessage": "Navigationspfad",
|
|
4
|
+
"description": "Accessible label for the breadcrumb navigation trail (the WAI-ARIA APG's conventional name)"
|
|
5
|
+
},
|
|
6
|
+
"ui.close-action": {
|
|
7
|
+
"defaultMessage": "Schließen",
|
|
8
|
+
"description": "Close button text or label"
|
|
9
|
+
},
|
|
10
|
+
"ui.combobox-listbox": {
|
|
11
|
+
"defaultMessage": "Empfehlungen",
|
|
12
|
+
"description": "Accessible label for a ComboBox's dropdown list of suggestions"
|
|
13
|
+
},
|
|
14
|
+
"ui.combobox-trigger": {
|
|
15
|
+
"defaultMessage": "Empfehlungen anzeigen",
|
|
16
|
+
"description": "Accessible label for the button that opens a ComboBox's suggestions dropdown"
|
|
17
|
+
},
|
|
18
|
+
"ui.loading": {
|
|
19
|
+
"defaultMessage": "Laden",
|
|
20
|
+
"description": "Announced by screen readers for a button showing a loading spinner"
|
|
21
|
+
},
|
|
22
|
+
"ui.new-tab-notice": {
|
|
23
|
+
"defaultMessage": "opens in a new tab",
|
|
24
|
+
"description": "Visually hidden suffix on links that open a new tab, announced by screen readers"
|
|
25
|
+
},
|
|
26
|
+
"ui.numberfield-decrease": {
|
|
27
|
+
"defaultMessage": "{fieldLabel} verringern",
|
|
28
|
+
"description": "Accessible label for a number field's decrement stepper; fieldLabel is the field's label and may be empty"
|
|
29
|
+
},
|
|
30
|
+
"ui.numberfield-increase": {
|
|
31
|
+
"defaultMessage": "{fieldLabel} erhöhen",
|
|
32
|
+
"description": "Accessible label for a number field's increment stepper; fieldLabel is the field's label and may be empty"
|
|
33
|
+
},
|
|
34
|
+
"ui.select-placeholder": {
|
|
35
|
+
"defaultMessage": "Element wählen",
|
|
36
|
+
"description": "Shown in a Select's trigger while nothing is chosen and the app supplies no placeholder"
|
|
37
|
+
},
|
|
38
|
+
"ui.select-row-action": {
|
|
39
|
+
"defaultMessage": "Auswählen",
|
|
40
|
+
"description": "Accessible label for a row-selection checkbox in a list or table; screen readers read it together with the row's content"
|
|
41
|
+
},
|
|
42
|
+
"ui.toast-region": {
|
|
43
|
+
"defaultMessage": "{count, plural, one {# Benachrichtigung} other {# Benachrichtigungen}}.",
|
|
44
|
+
"description": "Accessible label for the notifications area; count is the number of notifications showing"
|
|
45
|
+
},
|
|
46
|
+
"ui.toast-status-error": {
|
|
47
|
+
"defaultMessage": "Fehler",
|
|
48
|
+
"description": "Announced by screen readers before an error notification"
|
|
49
|
+
},
|
|
50
|
+
"ui.toast-status-info": {
|
|
51
|
+
"defaultMessage": "Informationen",
|
|
52
|
+
"description": "Announced by screen readers before an informational notification"
|
|
53
|
+
},
|
|
54
|
+
"ui.toast-status-success": {
|
|
55
|
+
"defaultMessage": "Erfolg",
|
|
56
|
+
"description": "Announced by screen readers before a success notification"
|
|
57
|
+
},
|
|
58
|
+
"ui.toast-status-warning": {
|
|
59
|
+
"defaultMessage": "Warnung",
|
|
60
|
+
"description": "Announced by screen readers before a warning notification"
|
|
61
|
+
}
|
|
62
|
+
}
|