@microbit/ui 0.0.0-gray.ramp.90 → 0.0.0-gray.ramp.91

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@microbit/ui",
3
- "version": "0.0.0-gray.ramp.90",
3
+ "version": "0.0.0-gray.ramp.91",
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",
@@ -17,8 +17,17 @@ const transitionCommon =
17
17
  *
18
18
  * Focus matches both native `:focus-visible` (plain inputs; browsers treat any
19
19
  * focus in a text field as focus-visible) and react-aria's `data-focused`
20
- * (inputs inside RAC TextField). Focus is declared after invalid so a focused
21
- * invalid field shows the focus ring, as in Chakra.
20
+ * (inputs inside RAC TextField).
21
+ *
22
+ * Hover, invalid and focus all set `borderColor`, so their precedence has to be
23
+ * hover < invalid < focus. Declaration order will not buy that: Panda sorts a
24
+ * recipe's state rules itself, ranking selectors against a fixed
25
+ * link/visited/focus/hover/active table, which puts `_hover` *after* focus and
26
+ * after anything the table doesn't mention (`[data-invalid]`). Equal-specificity
27
+ * rules then leave hover winning. So the ladder is spelled with repeated `&`
28
+ * instead — `&&` and `&&&` emit `.input.input` and `.input.input.input`, making
29
+ * precedence specificity rather than order, which nothing downstream can
30
+ * resort. Variants still override freely; they land in a later cascade layer.
22
31
  *
23
32
  * Registered in the base preset (base-preset.ts), which also has the
24
33
  * `staticCss` entry that keeps the runtime-prop size variants generated.
@@ -42,11 +51,11 @@ export const input = defineRecipe({
42
51
  bg: "inherit",
43
52
  color: "inherit",
44
53
  _hover: { borderColor: "gray.500" },
45
- "&[data-invalid], &:user-invalid": {
54
+ "&&:is([data-invalid], :user-invalid)": {
46
55
  borderColor: "danger.500",
47
56
  boxShadow: "0 0 0 1px token(colors.danger.500)",
48
57
  },
49
- "&:is(:focus-visible, [data-focused])": {
58
+ "&&&:is(:focus-visible, [data-focused])": {
50
59
  zIndex: 1,
51
60
  borderColor: "focusBorder",
52
61
  boxShadow: "0 0 0 1px token(colors.focusBorder)",
@@ -85,9 +85,12 @@ export const select = defineSlotRecipe({
85
85
  // `> &` rather than a descendant selector, so an app's own invalid form
86
86
  // wrapper cannot paint every control inside it red.
87
87
  //
88
- // Declared after hover and before focus so red beats a hover tint and
89
- // the focus ring beats red, as in the input recipe.
90
- "[data-invalid] > &": {
88
+ // Doubled `&` for the same reason as the input recipe: hover, invalid and
89
+ // focus all set `borderColor`, and Panda sorts state rules by its own
90
+ // pseudo-class table rather than declaration order, so hover would win
91
+ // these ties. The repeated `&` makes the hover < invalid < focus ladder a
92
+ // matter of specificity instead.
93
+ "[data-invalid] > &&": {
91
94
  borderColor: "danger.500",
92
95
  boxShadow: "0 0 0 1px token(colors.danger.500)",
93
96
  },
@@ -101,7 +104,7 @@ export const select = defineSlotRecipe({
101
104
  // focus moves to an option (aria-activedescendant) — which strips RAC's
102
105
  // attribute for as long as the list has an active option, real focus
103
106
  // never having left. Select's trigger holds no input, so it can't match.
104
- "&[data-focus-visible], &:has(input:focus)": {
107
+ "&&&[data-focus-visible], &&&:has(input:focus)": {
105
108
  boxShadow: "0 0 0 1px token(colors.focusBorder)",
106
109
  borderColor: "focusBorder",
107
110
  outline: "2px solid transparent",