@microbit/ui 0.1.0-alpha.8 → 0.1.0

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 (114) hide show
  1. package/LICENSE.md +8 -0
  2. package/README.md +171 -20
  3. package/lang/ui.ar.json +62 -0
  4. package/lang/ui.ca.json +43 -3
  5. package/lang/ui.cy.json +62 -0
  6. package/lang/ui.de.json +44 -4
  7. package/lang/ui.en-us.json +40 -0
  8. package/lang/ui.en.json +40 -0
  9. package/lang/ui.es-es.json +43 -3
  10. package/lang/ui.fr.json +44 -4
  11. package/lang/ui.ga-ie.json +43 -3
  12. package/lang/ui.it.json +62 -0
  13. package/lang/ui.ja.json +44 -4
  14. package/lang/ui.ko.json +44 -4
  15. package/lang/ui.lo.json +62 -0
  16. package/lang/ui.lol.json +40 -0
  17. package/lang/ui.nl.json +44 -4
  18. package/lang/ui.pl.json +44 -4
  19. package/lang/ui.pt-br.json +44 -4
  20. package/lang/ui.vi.json +62 -0
  21. package/lang/ui.zh-cn.json +44 -4
  22. package/lang/ui.zh-tw.json +44 -4
  23. package/package.json +12 -10
  24. package/reset.css +12 -0
  25. package/src/Avatar.recipe.ts +191 -0
  26. package/src/Avatar.tsx +278 -0
  27. package/src/Breadcrumb.recipe.ts +45 -0
  28. package/src/Breadcrumb.tsx +114 -0
  29. package/src/Button.recipe.ts +88 -50
  30. package/src/Button.tsx +67 -14
  31. package/src/ButtonGroup.tsx +37 -15
  32. package/src/Card.recipe.ts +1 -2
  33. package/src/Card.tsx +2 -1
  34. package/src/Checkbox.recipe.ts +49 -14
  35. package/src/Checkbox.tsx +111 -32
  36. package/src/CheckboxGroup.tsx +70 -0
  37. package/src/CloseButton.tsx +3 -3
  38. package/src/CloseIcon.tsx +6 -4
  39. package/src/Code.tsx +1 -1
  40. package/src/Collapse.tsx +13 -14
  41. package/src/ComboBox.tsx +246 -0
  42. package/src/Divider.tsx +40 -7
  43. package/src/Drawer.recipe.ts +21 -10
  44. package/src/Drawer.tsx +3 -4
  45. package/src/ExternalLink.tsx +43 -0
  46. package/src/Fade.tsx +18 -4
  47. package/src/Field.recipe.ts +114 -0
  48. package/src/Field.tsx +187 -0
  49. package/src/GridList.recipe.ts +57 -0
  50. package/src/GridList.tsx +81 -0
  51. package/src/Heading.recipe.ts +20 -1
  52. package/src/Heading.tsx +3 -3
  53. package/src/Icon.tsx +23 -7
  54. package/src/IconButton.tsx +8 -18
  55. package/src/Image.tsx +1 -1
  56. package/src/Input.recipe.ts +41 -28
  57. package/src/Input.tsx +23 -7
  58. package/src/InputGroup.tsx +26 -12
  59. package/src/Kbd.tsx +1 -1
  60. package/src/Link.tsx +3 -3
  61. package/src/LinkBox.tsx +2 -3
  62. package/src/LinkButton.tsx +14 -13
  63. package/src/List.tsx +5 -7
  64. package/src/ListBox.recipe.ts +43 -0
  65. package/src/ListBox.tsx +88 -0
  66. package/src/Menu.recipe.ts +22 -19
  67. package/src/Menu.tsx +54 -28
  68. package/src/Modal.recipe.ts +17 -9
  69. package/src/Modal.tsx +123 -24
  70. package/src/MoreMenuButton.tsx +63 -0
  71. package/src/NativeSelect.tsx +41 -14
  72. package/src/NativeSelectField.tsx +84 -0
  73. package/src/NumberField.recipe.ts +56 -15
  74. package/src/NumberField.tsx +74 -22
  75. package/src/PopoverArrow.tsx +1 -2
  76. package/src/ProgressBar.tsx +3 -5
  77. package/src/Radio.recipe.ts +108 -0
  78. package/src/Radio.tsx +62 -0
  79. package/src/RadioGroup.tsx +68 -0
  80. package/src/Select.recipe.ts +211 -0
  81. package/src/Select.tsx +194 -0
  82. package/src/SharedUIProvider.tsx +63 -7
  83. package/src/Skeleton.tsx +146 -0
  84. package/src/Slide.tsx +2 -2
  85. package/src/Slider.recipe.ts +27 -16
  86. package/src/Slider.tsx +20 -12
  87. package/src/Spinner.tsx +11 -9
  88. package/src/Svg.tsx +2 -3
  89. package/src/Switch.recipe.ts +57 -11
  90. package/src/Switch.tsx +39 -7
  91. package/src/Text.recipe.ts +26 -0
  92. package/src/Text.tsx +6 -2
  93. package/src/TextField.tsx +39 -40
  94. package/src/Toast.recipe.ts +52 -15
  95. package/src/Toast.tsx +147 -48
  96. package/src/Tooltip.recipe.ts +32 -0
  97. package/src/Tooltip.tsx +68 -28
  98. package/src/TooltipButton.tsx +229 -0
  99. package/src/UnmountCallback.tsx +2 -2
  100. package/src/VisuallyHidden.tsx +1 -1
  101. package/src/base-preset.ts +329 -84
  102. package/src/base-tokens.ts +0 -3
  103. package/src/button-icon.ts +4 -5
  104. package/src/data-attrs.ts +16 -0
  105. package/src/dense-preset.ts +107 -0
  106. package/src/hooks/useBreakpointValue.ts +4 -4
  107. package/src/hooks/useClipboard.ts +43 -7
  108. package/src/hooks/useDisclosure.ts +31 -0
  109. package/src/hooks/useMediaQuery.ts +3 -4
  110. package/src/hooks/usePrevious.ts +1 -4
  111. package/src/index.ts +20 -3
  112. package/src/rac-locale.ts +33 -0
  113. package/src/system.ts +22 -1
  114. package/src/TextField.recipe.ts +0 -54
@@ -1,22 +1,62 @@
1
1
  {
2
+ "ui.breadcrumb": {
3
+ "defaultMessage": "Trilha de navegação",
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": "Fechar",
4
8
  "description": "Close button text or label"
5
9
  },
10
+ "ui.combobox-listbox": {
11
+ "defaultMessage": "Sugestões",
12
+ "description": "Accessible label for a ComboBox's dropdown list of suggestions"
13
+ },
14
+ "ui.combobox-trigger": {
15
+ "defaultMessage": "Mostrar sugestões",
16
+ "description": "Accessible label for the button that opens a ComboBox's suggestions dropdown"
17
+ },
18
+ "ui.loading": {
19
+ "defaultMessage": "Carregando",
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": "Diminuir {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": "Aumentar {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": "Selecione um 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": "Seleciona",
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 {# notificação} other {# notificações}}.",
44
+ "description": "Accessible label for the notifications area; count is the number of notifications showing"
45
+ },
6
46
  "ui.toast-status-error": {
7
- "defaultMessage": "Error",
47
+ "defaultMessage": "Erro",
8
48
  "description": "Announced by screen readers before an error notification"
9
49
  },
10
50
  "ui.toast-status-info": {
11
- "defaultMessage": "Information",
51
+ "defaultMessage": "Informações",
12
52
  "description": "Announced by screen readers before an informational notification"
13
53
  },
14
54
  "ui.toast-status-success": {
15
- "defaultMessage": "Success",
55
+ "defaultMessage": "Sucesso",
16
56
  "description": "Announced by screen readers before a success notification"
17
57
  },
18
58
  "ui.toast-status-warning": {
19
- "defaultMessage": "Warning",
59
+ "defaultMessage": "Atenção",
20
60
  "description": "Announced by screen readers before a warning notification"
21
61
  }
22
62
  }
@@ -0,0 +1,62 @@
1
+ {
2
+ "ui.breadcrumb": {
3
+ "defaultMessage": "Đường dẫn",
4
+ "description": "Accessible label for the breadcrumb navigation trail (the WAI-ARIA APG's conventional name)"
5
+ },
6
+ "ui.close-action": {
7
+ "defaultMessage": "Đóng",
8
+ "description": "Close button text or label"
9
+ },
10
+ "ui.combobox-listbox": {
11
+ "defaultMessage": "Gợi ý",
12
+ "description": "Accessible label for a ComboBox's dropdown list of suggestions"
13
+ },
14
+ "ui.combobox-trigger": {
15
+ "defaultMessage": "Hiển thị gợi ý",
16
+ "description": "Accessible label for the button that opens a ComboBox's suggestions dropdown"
17
+ },
18
+ "ui.loading": {
19
+ "defaultMessage": "Đang tải",
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": "Giảm {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": "Tăng {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": "Chọn một mục",
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": "Lựa chọn",
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, other {# thông báo}}.",
44
+ "description": "Accessible label for the notifications area; count is the number of notifications showing"
45
+ },
46
+ "ui.toast-status-error": {
47
+ "defaultMessage": "Lỗi",
48
+ "description": "Announced by screen readers before an error notification"
49
+ },
50
+ "ui.toast-status-info": {
51
+ "defaultMessage": "Thông tin",
52
+ "description": "Announced by screen readers before an informational notification"
53
+ },
54
+ "ui.toast-status-success": {
55
+ "defaultMessage": "Thành công",
56
+ "description": "Announced by screen readers before a success notification"
57
+ },
58
+ "ui.toast-status-warning": {
59
+ "defaultMessage": "Cảnh báo",
60
+ "description": "Announced by screen readers before a warning notification"
61
+ }
62
+ }
@@ -1,22 +1,62 @@
1
1
  {
2
+ "ui.breadcrumb": {
3
+ "defaultMessage": "痕迹导航",
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": "关闭",
4
8
  "description": "Close button text or label"
5
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": "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}",
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, one {# 个通知} other {# 个通知}}。",
44
+ "description": "Accessible label for the notifications area; count is the number of notifications showing"
45
+ },
6
46
  "ui.toast-status-error": {
7
- "defaultMessage": "Error",
47
+ "defaultMessage": "错误",
8
48
  "description": "Announced by screen readers before an error notification"
9
49
  },
10
50
  "ui.toast-status-info": {
11
- "defaultMessage": "Information",
51
+ "defaultMessage": "信息",
12
52
  "description": "Announced by screen readers before an informational notification"
13
53
  },
14
54
  "ui.toast-status-success": {
15
- "defaultMessage": "Success",
55
+ "defaultMessage": "成功",
16
56
  "description": "Announced by screen readers before a success notification"
17
57
  },
18
58
  "ui.toast-status-warning": {
19
- "defaultMessage": "Warning",
59
+ "defaultMessage": "警告",
20
60
  "description": "Announced by screen readers before a warning notification"
21
61
  }
22
62
  }
@@ -1,22 +1,62 @@
1
1
  {
2
+ "ui.breadcrumb": {
3
+ "defaultMessage": "巡覽路徑",
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": "關閉",
4
8
  "description": "Close button text or label"
5
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": "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}",
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, one {# 個通知} other {# 個通知}}。",
44
+ "description": "Accessible label for the notifications area; count is the number of notifications showing"
45
+ },
6
46
  "ui.toast-status-error": {
7
- "defaultMessage": "Error",
47
+ "defaultMessage": "錯誤",
8
48
  "description": "Announced by screen readers before an error notification"
9
49
  },
10
50
  "ui.toast-status-info": {
11
- "defaultMessage": "Information",
51
+ "defaultMessage": "資訊",
12
52
  "description": "Announced by screen readers before an informational notification"
13
53
  },
14
54
  "ui.toast-status-success": {
15
- "defaultMessage": "Success",
55
+ "defaultMessage": "成功",
16
56
  "description": "Announced by screen readers before a success notification"
17
57
  },
18
58
  "ui.toast-status-warning": {
19
- "defaultMessage": "Warning",
59
+ "defaultMessage": "警告",
20
60
  "description": "Announced by screen readers before a warning notification"
21
61
  }
22
62
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@microbit/ui",
3
- "version": "0.1.0-alpha.8",
3
+ "version": "0.1.0",
4
4
  "description": "micro:bit design-system primitives: react-aria-components + Panda CSS with a design language ported from Chakra UI v2. Ships as source; see README for the consumption setup.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -8,6 +8,7 @@
8
8
  ".": "./src/index.ts",
9
9
  "./base-preset": "./src/base-preset.ts",
10
10
  "./base-tokens": "./src/base-tokens.ts",
11
+ "./dense-preset": "./src/dense-preset.ts",
11
12
  "./messages": "./src/messages.ts",
12
13
  "./postcss-legacy-safari": "./postcss-legacy-safari.cjs",
13
14
  "./reset.css": "./reset.css",
@@ -25,14 +26,13 @@
25
26
  "scripts": {
26
27
  "panda": "panda codegen",
27
28
  "typecheck": "npm run panda && tsc --noEmit",
28
- "prestorybook": "npm run panda",
29
- "storybook": "storybook dev -p 6006 --no-open",
30
- "prebuild-storybook": "npm run panda",
31
- "build-storybook": "storybook build"
29
+ "pretest": "npm run panda",
30
+ "test": "vitest run"
32
31
  },
33
32
  "peerDependencies": {
34
33
  "@pandacss/dev": "^1.11.4",
35
34
  "react": "^18.3.1",
35
+ "react-aria": "^3.50.0",
36
36
  "react-aria-components": "^1.19.0",
37
37
  "react-icons": "^4.12.0 || ^5.0.0",
38
38
  "react-intl": "^6.6.8 || ^7.0.0"
@@ -40,19 +40,21 @@
40
40
  "devDependencies": {
41
41
  "@pandacss/dev": "^1.11.4",
42
42
  "@storybook/react-vite": "^10.5.2",
43
+ "@testing-library/react": "^16.3.2",
44
+ "@testing-library/user-event": "^14.6.1",
45
+ "@types/node": "^26.1.1",
43
46
  "@types/react": "^18.3.3",
44
47
  "@types/react-dom": "^18.3.0",
45
48
  "@vitejs/plugin-react": "^4.5.2",
49
+ "jsdom": "^29.1.1",
46
50
  "react": "^18.3.1",
51
+ "react-aria": "^3.50.0",
47
52
  "react-aria-components": "^1.19.0",
48
53
  "react-dom": "^18.3.1",
49
54
  "react-icons": "^4.12.0",
50
55
  "react-intl": "^6.6.8",
51
- "storybook": "^10.5.2",
52
56
  "typescript": "^5.4.2",
53
- "vite": "^6.3.5"
54
- },
55
- "publishConfig": {
56
- "tag": "alpha"
57
+ "vite": "^6.3.5",
58
+ "vitest": "^4.1.10"
57
59
  }
58
60
  }
package/reset.css CHANGED
@@ -21,3 +21,15 @@
21
21
  border-color: var(--colors-gray-200);
22
22
  word-wrap: break-word;
23
23
  }
24
+
25
+ /*
26
+ * Panda's preflight (base layer) beats the rule above with
27
+ * `border-color: var(--global-color-border, currentcolor)`. The currentcolor
28
+ * fallback poisons `border-color: inherit` chains (inherit copies the
29
+ * currentcolor KEYWORD, which then resolves against the child's own `color`
30
+ * — e.g. white on the checkbox/radio control). Defining the hook keeps the
31
+ * default border colour a real colour, as Chakra's global reset did.
32
+ */
33
+ :root {
34
+ --global-color-border: var(--colors-gray-200);
35
+ }
@@ -0,0 +1,191 @@
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
+ * Avatar slot recipe — a circle showing an image, the initials of a name, or
10
+ * a generic person glyph, optionally with a badge pinned to one corner.
11
+ *
12
+ * The background and text colour come from `var(--avatar-bg)` and
13
+ * `var(--avatar-color)` rather than being flat values, because the component
14
+ * derives them from the name (see Avatar.tsx) and writes them as inline custom
15
+ * properties. Two reasons, both about letting a call site win with a plain
16
+ * `css={{ bg: …, color: … }}`: an inline *property* would beat any class,
17
+ * where an inline *variable* only feeds this declaration; and both must stay
18
+ * single-class selectors, since a state selector like `&[data-light-bg]`
19
+ * would outrank the call site's utility class on specificity.
20
+ *
21
+ * The `calc(size / 2.5)` font size is resolved per size so an app preset can
22
+ * restate either independently (classroom's avatars are a grade larger).
23
+ *
24
+ * Registered in the base preset (base-preset.ts), which also has the
25
+ * `staticCss` entry that keeps the runtime-prop variants generated.
26
+ */
27
+ export const avatar = defineSlotRecipe({
28
+ className: "avatar",
29
+ slots: ["root", "label", "image", "badge"],
30
+ base: {
31
+ root: {
32
+ display: "inline-flex",
33
+ alignItems: "center",
34
+ justifyContent: "center",
35
+ flexShrink: 0,
36
+ position: "relative",
37
+ verticalAlign: "top",
38
+ textAlign: "center",
39
+ textTransform: "uppercase",
40
+ fontWeight: "medium",
41
+ borderRadius: "full",
42
+ // The no-name defaults; the name-derived pair arrives inline.
43
+ // gray.350 is the decorative-fill stop — 400+ are reserved for
44
+ // accessible outlines and text (see the ramp in base-preset.ts).
45
+ background: "var(--avatar-bg, token(colors.gray.350))",
46
+ color: "var(--avatar-color, token(colors.white))",
47
+ borderColor: "white",
48
+ },
49
+ label: {
50
+ lineHeight: "1",
51
+ },
52
+ image: {
53
+ width: "100%",
54
+ height: "100%",
55
+ objectFit: "cover",
56
+ borderRadius: "inherit",
57
+ },
58
+ badge: {
59
+ position: "absolute",
60
+ display: "flex",
61
+ alignItems: "center",
62
+ justifyContent: "center",
63
+ borderRadius: "full",
64
+ // em-relative, so a badge keeps its proportions at every avatar size.
65
+ borderWidth: "0.2em",
66
+ borderStyle: "solid",
67
+ borderColor: "white",
68
+ // The corner variants pin the badge with logical insets but nudge it
69
+ // out over the edge with a transform, which is physical — so the
70
+ // outward direction is a variable the RTL rule negates. Without it a
71
+ // `start` badge would sit at the right corner and nudge left, i.e.
72
+ // inwards over the avatar.
73
+ // Fallbacks carry a unit: `calc(-1 * 0)` is a number, which is invalid
74
+ // where a length is wanted, and would drop the RTL transform whole.
75
+ transform:
76
+ "translate(var(--avatar-badge-x, 0px), var(--avatar-badge-y, 0px))",
77
+ _rtl: {
78
+ transform:
79
+ "translate(calc(-1 * var(--avatar-badge-x, 0px)), var(--avatar-badge-y, 0px))",
80
+ },
81
+ },
82
+ },
83
+ variants: {
84
+ // The container size, and the `calc(size / 2.5)` font size kept as a
85
+ // calc over the same token so both track a preset that rescales `sizes`
86
+ // (the dense preset does, by 0.88).
87
+ //
88
+ // The font size lands on the root *and* the label. They are separate
89
+ // declarations so an app can
90
+ // move one without the other: the root's is the em basis for a badge,
91
+ // the label's is how big the initials are, and the two are not always
92
+ // the same wish.
93
+ size: {
94
+ "2xs": {
95
+ root: {
96
+ width: "4",
97
+ height: "4",
98
+ fontSize: "calc(token(sizes.4) / 2.5)",
99
+ },
100
+ label: { fontSize: "calc(token(sizes.4) / 2.5)" },
101
+ },
102
+ xs: {
103
+ root: {
104
+ width: "6",
105
+ height: "6",
106
+ fontSize: "calc(token(sizes.6) / 2.5)",
107
+ },
108
+ label: { fontSize: "calc(token(sizes.6) / 2.5)" },
109
+ },
110
+ sm: {
111
+ root: {
112
+ width: "8",
113
+ height: "8",
114
+ fontSize: "calc(token(sizes.8) / 2.5)",
115
+ },
116
+ label: { fontSize: "calc(token(sizes.8) / 2.5)" },
117
+ },
118
+ md: {
119
+ root: {
120
+ width: "12",
121
+ height: "12",
122
+ fontSize: "calc(token(sizes.12) / 2.5)",
123
+ },
124
+ label: { fontSize: "calc(token(sizes.12) / 2.5)" },
125
+ },
126
+ lg: {
127
+ root: {
128
+ width: "16",
129
+ height: "16",
130
+ fontSize: "calc(token(sizes.16) / 2.5)",
131
+ },
132
+ label: { fontSize: "calc(token(sizes.16) / 2.5)" },
133
+ },
134
+ xl: {
135
+ root: {
136
+ width: "24",
137
+ height: "24",
138
+ fontSize: "calc(token(sizes.24) / 2.5)",
139
+ },
140
+ label: { fontSize: "calc(token(sizes.24) / 2.5)" },
141
+ },
142
+ "2xl": {
143
+ root: {
144
+ width: "32",
145
+ height: "32",
146
+ fontSize: "calc(token(sizes.32) / 2.5)",
147
+ },
148
+ label: { fontSize: "calc(token(sizes.32) / 2.5)" },
149
+ },
150
+ },
151
+ /** Which corner the badge sits in. */
152
+ placement: {
153
+ "top-start": {
154
+ badge: {
155
+ top: "0",
156
+ insetStart: "0",
157
+ "--avatar-badge-x": "-25%",
158
+ "--avatar-badge-y": "-25%",
159
+ },
160
+ },
161
+ "top-end": {
162
+ badge: {
163
+ top: "0",
164
+ insetEnd: "0",
165
+ "--avatar-badge-x": "25%",
166
+ "--avatar-badge-y": "-25%",
167
+ },
168
+ },
169
+ "bottom-start": {
170
+ badge: {
171
+ bottom: "0",
172
+ insetStart: "0",
173
+ "--avatar-badge-x": "-25%",
174
+ "--avatar-badge-y": "25%",
175
+ },
176
+ },
177
+ "bottom-end": {
178
+ badge: {
179
+ bottom: "0",
180
+ insetEnd: "0",
181
+ "--avatar-badge-x": "25%",
182
+ "--avatar-badge-y": "25%",
183
+ },
184
+ },
185
+ },
186
+ },
187
+ defaultVariants: {
188
+ size: "md",
189
+ placement: "bottom-end",
190
+ },
191
+ });