@signal9/era-ui 16.0.1 → 18.0.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.
@@ -44,7 +44,7 @@
44
44
  "slug": "utilities",
45
45
  "title": "Utilities",
46
46
  "summary": "The CSS classes era ships alongside the components — links, ink-centred text, shimmer, hidden scrollbars.",
47
- "tokenEstimate": 11478,
47
+ "tokenEstimate": 11937,
48
48
  "sections": [
49
49
  "Overview",
50
50
  "era-interactive",
@@ -57,6 +57,7 @@
57
57
  "era-token-ink",
58
58
  "era-label-ink",
59
59
  "glass-blur",
60
+ "rounded-row",
60
61
  "era-shimmer",
61
62
  "era-collapse",
62
63
  "h-icon · h-pill · h-chip · h-control · h-bar",
@@ -1075,6 +1076,16 @@
1075
1076
  "description": "Tailwind v4 cannot resolve var() inside backdrop-blur-[], so the filter\nis a registered utility, gated to glass subtrees.\n\nIt lives HERE rather than in surfaces/glass.css, where a surface's own\nchrome would naturally sit, because glass.css is imported into a cascade\nlayer and `@utility` cannot be nested inside one. Surface-scoped RULES stay\nin their surface file; surface-scoped UTILITIES have to come out.",
1076
1077
  "css": "@utility` cannot be nested inside one. Surface-scoped RULES stay\n * in their surface file; surface-scoped UTILITIES have to come out.\n *\n * @use the backdrop blur for a floating panel (popover, dialog, taskbar) — it\n * applies only inside a data-surface=\"glass\" subtree and is inert elsewhere,\n * so it is safe to leave on a panel that renders under every surface. */\n@utility glass-blur {\n\t[data-surface='glass'] & {\n\t\tbackdrop-filter: blur(var(--era-blur)) saturate(var(--era-saturate));\n\t\t-webkit-backdrop-filter: blur(var(--era-blur)) saturate(var(--era-saturate));\n\t}\n}"
1077
1078
  },
1079
+ {
1080
+ "name": "rounded-row",
1081
+ "names": [
1082
+ "rounded-row"
1083
+ ],
1084
+ "file": "index.css",
1085
+ "useCase": "the radius for a row inside a container that pads it by the gutter — a nav rail, a command list, any scrolling list of controls. For a row in a menu PANEL, whose inset is the surface-owned frame rather than the gutter, use rounded-menu-item.",
1086
+ "description": "THE ROW RADIUS — concentricity for a row that is inset, WITH its inset intact.\n\nThe law is rd_outer − rd_inner = gap. A row inside a container that pads it by\nthe gutter therefore takes the container's radius MINUS the gutter — and no\ntier radius equals that at more than one density, which is why reaching for\n`rounded-control` and hoping looks wrong: in a control-radius pane the row and\nthe pane end up with identical curvature separated by 5px of gap, two arcs at\none corner with no relationship.\n\nThe alternative — deleting the gap so equal radii become legal — is the wrong\ntrade and was briefly shipped. The ladder exists so a gap and concentric\ncorners COEXIST; spending one to buy the other gives up the thing it is for.\n\nThe calc runs on the ROW, not on the container, which is what makes it\nautomatic: `--era-container-rd` is inherited, so a container that is not\ncontrol-radius publishes its own and every row inside re-derives. era's\ncommand palette does exactly that — it is rd-bar, so its rows resolve to\nrd-bar − gap, which IS rd-control (control-inset-bar is the gutter), and the\npalette's geometry is unchanged by this.",
1087
+ "css": "@utility rounded-row {\n\tborder-radius: max(0px, calc(var(--era-container-rd, var(--era-rd-control)) - var(--era-gap)));\n}"
1088
+ },
1078
1089
  {
1079
1090
  "name": "era-shimmer",
1080
1091
  "names": [
@@ -100,6 +100,16 @@
100
100
  "description": "Tailwind v4 cannot resolve var() inside backdrop-blur-[], so the filter\nis a registered utility, gated to glass subtrees.\n\nIt lives HERE rather than in surfaces/glass.css, where a surface's own\nchrome would naturally sit, because glass.css is imported into a cascade\nlayer and `@utility` cannot be nested inside one. Surface-scoped RULES stay\nin their surface file; surface-scoped UTILITIES have to come out.",
101
101
  "css": "@utility` cannot be nested inside one. Surface-scoped RULES stay\n * in their surface file; surface-scoped UTILITIES have to come out.\n *\n * @use the backdrop blur for a floating panel (popover, dialog, taskbar) — it\n * applies only inside a data-surface=\"glass\" subtree and is inert elsewhere,\n * so it is safe to leave on a panel that renders under every surface. */\n@utility glass-blur {\n\t[data-surface='glass'] & {\n\t\tbackdrop-filter: blur(var(--era-blur)) saturate(var(--era-saturate));\n\t\t-webkit-backdrop-filter: blur(var(--era-blur)) saturate(var(--era-saturate));\n\t}\n}"
102
102
  },
103
+ {
104
+ "name": "rounded-row",
105
+ "names": [
106
+ "rounded-row"
107
+ ],
108
+ "file": "index.css",
109
+ "useCase": "the radius for a row inside a container that pads it by the gutter — a nav rail, a command list, any scrolling list of controls. For a row in a menu PANEL, whose inset is the surface-owned frame rather than the gutter, use rounded-menu-item.",
110
+ "description": "THE ROW RADIUS — concentricity for a row that is inset, WITH its inset intact.\n\nThe law is rd_outer − rd_inner = gap. A row inside a container that pads it by\nthe gutter therefore takes the container's radius MINUS the gutter — and no\ntier radius equals that at more than one density, which is why reaching for\n`rounded-control` and hoping looks wrong: in a control-radius pane the row and\nthe pane end up with identical curvature separated by 5px of gap, two arcs at\none corner with no relationship.\n\nThe alternative — deleting the gap so equal radii become legal — is the wrong\ntrade and was briefly shipped. The ladder exists so a gap and concentric\ncorners COEXIST; spending one to buy the other gives up the thing it is for.\n\nThe calc runs on the ROW, not on the container, which is what makes it\nautomatic: `--era-container-rd` is inherited, so a container that is not\ncontrol-radius publishes its own and every row inside re-derives. era's\ncommand palette does exactly that — it is rd-bar, so its rows resolve to\nrd-bar − gap, which IS rd-control (control-inset-bar is the gutter), and the\npalette's geometry is unchanged by this.",
111
+ "css": "@utility rounded-row {\n\tborder-radius: max(0px, calc(var(--era-container-rd, var(--era-rd-control)) - var(--era-gap)));\n}"
112
+ },
103
113
  {
104
114
  "name": "era-shimmer",
105
115
  "names": [
@@ -70,6 +70,7 @@ source uses it where a name exists.
70
70
  | `era-token-ink` | the ink centring for a token pill — a badge, a KV value, any short caps-or-digits label in a fixed-height box, CENTRED horizontally and with no icon sibling. For a left-aligned sentence-case label use era-label-ink; when an icon really must sit beside the text, see the note there. |
71
71
  | `era-label-ink` | the ink centring for a fixed-height control holding ONE line of sentence-case text and no icon — a menu item, a nav trigger, a button label. Never alongside `truncate` (the trimmed box becomes the clip). |
72
72
  | `glass-blur` | the backdrop blur for a floating panel (popover, dialog, taskbar) — it applies only inside a data-surface="glass" subtree and is inert elsewhere, so it is safe to leave on a panel that renders under every surface. |
73
+ | `rounded-row` | the radius for a row inside a container that pads it by the gutter — a nav rail, a command list, any scrolling list of controls. For a row in a menu PANEL, whose inset is the surface-owned frame rather than the gutter, use rounded-menu-item. |
73
74
  | `era-shimmer` | the in-progress text treatment — put it on a "working…" / "thinking…" label while a task streams, instead of a spinner beside the text. |
74
75
  | `era-collapse` | the height sweep for a bits-ui Collapsible.Content — put it on the Content element itself (it carries its own overflow: hidden). |
75
76
  | `h-icon` `h-pill` `h-chip` `h-control` `h-bar` | the tier-height ladder — h-icon (icon) · h-pill (nested pill) · h-chip (standalone pill) · h-control (control) · h-bar (bar); the named form of h-(--era-h-*). |
@@ -421,6 +422,38 @@ Declared in `index.css`.
421
422
  }
422
423
  ```
423
424
 
425
+ ## rounded-row
426
+
427
+ **Use it for:** the radius for a row inside a container that pads it by the gutter — a nav rail, a command list, any scrolling list of controls. For a row in a menu PANEL, whose inset is the surface-owned frame rather than the gutter, use rounded-menu-item.
428
+
429
+ THE ROW RADIUS — concentricity for a row that is inset, WITH its inset intact.
430
+
431
+ The law is rd_outer − rd_inner = gap. A row inside a container that pads it by
432
+ the gutter therefore takes the container's radius MINUS the gutter — and no
433
+ tier radius equals that at more than one density, which is why reaching for
434
+ `rounded-control` and hoping looks wrong: in a control-radius pane the row and
435
+ the pane end up with identical curvature separated by 5px of gap, two arcs at
436
+ one corner with no relationship.
437
+
438
+ The alternative — deleting the gap so equal radii become legal — is the wrong
439
+ trade and was briefly shipped. The ladder exists so a gap and concentric
440
+ corners COEXIST; spending one to buy the other gives up the thing it is for.
441
+
442
+ The calc runs on the ROW, not on the container, which is what makes it
443
+ automatic: `--era-container-rd` is inherited, so a container that is not
444
+ control-radius publishes its own and every row inside re-derives. era's
445
+ command palette does exactly that — it is rd-bar, so its rows resolve to
446
+ rd-bar − gap, which IS rd-control (control-inset-bar is the gutter), and the
447
+ palette's geometry is unchanged by this.
448
+
449
+ Declared in `index.css`.
450
+
451
+ ```css
452
+ @utility rounded-row {
453
+ border-radius: max(0px, calc(var(--era-container-rd, var(--era-rd-control)) - var(--era-gap)));
454
+ }
455
+ ```
456
+
424
457
  ## era-shimmer
425
458
 
426
459
  **Use it for:** the in-progress text treatment — put it on a "working…" / "thinking…" label while a task streams, instead of a spinner beside the text.
@@ -411,6 +411,21 @@
411
411
  --era-mask-glyph: square;
412
412
  }
413
413
 
414
+ /* THE AXIS HAS TO GO BOTH WAYS. Only `square` had a rule: the rounded value
415
+ * lived on :root alone, so a subtree could be squared and never un-squared —
416
+ * a `data-corners="rounded"` island inside a square page had nothing to restore
417
+ * the scale with, and inherited 0. Every other axis can be overridden in either
418
+ * direction on any subtree, and this one silently could not.
419
+ *
420
+ * No specificity fight here, unlike the font axis: these set a custom property
421
+ * on the element that CARRIES the attribute, and descendants resolve it by
422
+ * inheritance. The nearest ancestor wins because that is what inheritance does,
423
+ * regardless of which value era declares last. */
424
+ [data-corners='rounded'] {
425
+ --era-roundness-scale: 1;
426
+ --era-mask-glyph: disc;
427
+ }
428
+
414
429
  /* Animate the roundness scale itself — not border-radius. sp changes snap
415
430
  instantly (responsive scrub); the corners toggle animates scale 1 ↔ 0,
416
431
  which cascades through the rd-* calc chain to every element at 60 fps.
@@ -406,6 +406,34 @@
406
406
  }
407
407
  }
408
408
 
409
+ /* THE ROW RADIUS — concentricity for a row that is inset, WITH its inset intact.
410
+ *
411
+ * The law is rd_outer − rd_inner = gap. A row inside a container that pads it by
412
+ * the gutter therefore takes the container's radius MINUS the gutter — and no
413
+ * tier radius equals that at more than one density, which is why reaching for
414
+ * `rounded-control` and hoping looks wrong: in a control-radius pane the row and
415
+ * the pane end up with identical curvature separated by 5px of gap, two arcs at
416
+ * one corner with no relationship.
417
+ *
418
+ * The alternative — deleting the gap so equal radii become legal — is the wrong
419
+ * trade and was briefly shipped. The ladder exists so a gap and concentric
420
+ * corners COEXIST; spending one to buy the other gives up the thing it is for.
421
+ *
422
+ * The calc runs on the ROW, not on the container, which is what makes it
423
+ * automatic: `--era-container-rd` is inherited, so a container that is not
424
+ * control-radius publishes its own and every row inside re-derives. era's
425
+ * command palette does exactly that — it is rd-bar, so its rows resolve to
426
+ * rd-bar − gap, which IS rd-control (control-inset-bar is the gutter), and the
427
+ * palette's geometry is unchanged by this.
428
+ *
429
+ * @use the radius for a row inside a container that pads it by the gutter — a
430
+ * nav rail, a command list, any scrolling list of controls. For a row in a menu
431
+ * PANEL, whose inset is the surface-owned frame rather than the gutter, use
432
+ * rounded-menu-item. */
433
+ @utility rounded-row {
434
+ border-radius: max(0px, calc(var(--era-container-rd, var(--era-rd-control)) - var(--era-gap)));
435
+ }
436
+
409
437
  /* In-progress text shimmer — a bright sweep through muted text (the standard
410
438
  * "working…" label treatment in streaming UIs). Timing derives from the motion
411
439
  * axis (10× --era-duration ≈ 1.5s at normal, 2.6s at extra); at instant the
@@ -8,7 +8,7 @@
8
8
  <Command.Item
9
9
  bind:ref
10
10
  class={cn(
11
- 'flex h-control cursor-pointer items-center gap-gutter rounded-control px-inset-control text-body leading-none text-fg outline-none select-none not-has-[svg]:era-label-ink data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[selected]:bg-highlight',
11
+ 'flex h-control cursor-pointer items-center gap-gutter rounded-row px-inset-control text-body leading-none text-fg outline-none select-none not-has-[svg]:era-label-ink data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[selected]:bg-highlight',
12
12
  className
13
13
  )}
14
14
  {...restProps}
@@ -8,7 +8,7 @@
8
8
  <Command.LinkItem
9
9
  bind:ref
10
10
  class={cn(
11
- 'flex h-control cursor-pointer items-center gap-gutter rounded-control px-inset-control text-body leading-none text-fg no-underline outline-none select-none not-has-[svg]:era-label-ink data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[selected]:bg-highlight',
11
+ 'flex h-control cursor-pointer items-center gap-gutter rounded-row px-inset-control text-body leading-none text-fg no-underline outline-none select-none not-has-[svg]:era-label-ink data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[selected]:bg-highlight',
12
12
  className
13
13
  )}
14
14
  {...restProps}
@@ -24,7 +24,11 @@
24
24
  // glass-blur, like every other floating panel (menus, dialogs, pickers):
25
25
  // inert on opaque surfaces, blurs the backdrop when the palette floats.
26
26
  !bare &&
27
- 'overflow-hidden rounded-bar border border-(color:--era-border-color) bg-well p-gutter shadow-lg glass-blur',
27
+ // [--era-container-rd:…] publishes this panel's own radius, so the rows
28
+ // inside re-derive against rd-bar rather than the rd-control default.
29
+ // rd-bar − gutter IS rd-control, so the palette renders unchanged; what
30
+ // changes is that the rows now say WHY they are that radius.
31
+ 'overflow-hidden rounded-bar border border-(color:--era-border-color) bg-well p-gutter shadow-lg glass-blur [--era-container-rd:var(--era-rd-bar)]',
28
32
  className
29
33
  )}
30
34
  {...restProps}
@@ -111,7 +111,7 @@ const spacingGroups = Object.fromEntries(SPACING_PREFIXES.map((prefix) => [
111
111
  ]));
112
112
  const radiusGroups = Object.fromEntries(RADIUS_SIDES.map((side) => [
113
113
  `rounded-${side}`,
114
- [{ [`rounded-${side}`]: [...TIERS, 'menu-item'] }]
114
+ [{ [`rounded-${side}`]: [...TIERS, 'menu-item', 'row'] }]
115
115
  ]));
116
116
  export const twMergeConfig = {
117
117
  extend: {
@@ -124,7 +124,7 @@ export const twMergeConfig = {
124
124
  'font-size': [{ text: ['body', 'micro', 'lede', 'heading', 'title', 'display'] }],
125
125
  leading: [{ leading: ['body'] }],
126
126
  // era's radius rungs. rounded-sm/md/lg are Tailwind's own and already known.
127
- rounded: [{ rounded: [...TIERS, 'menu-item'] }],
127
+ rounded: [{ rounded: [...TIERS, 'menu-item', 'row'] }],
128
128
  shadow: [{ shadow: ['well', 'pressed', 'highlight'] }],
129
129
  z: [{ z: ['chrome', 'drawer', 'overlay', 'toast', 'debug'] }],
130
130
  ease: [{ ease: ['base'] }],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@signal9/era-ui",
3
- "version": "16.0.1",
3
+ "version": "18.0.0",
4
4
  "scripts": {
5
5
  "dev": "vite dev --host",
6
6
  "build": "vite build && npm run prepack",
@@ -32,6 +32,7 @@
32
32
  "files": [
33
33
  "dist",
34
34
  "skill",
35
+ "CHANGELOG.md",
35
36
  "!dist/**/*.test.*",
36
37
  "!dist/**/*.spec.*"
37
38
  ],
package/skill/SKILL.md CHANGED
@@ -39,9 +39,24 @@ a review finding.
39
39
  | Font | `data-font` | `mono` (default) `sans` `serif` |
40
40
  | Motion | `data-motion` | `instant` `normal` `extra` |
41
41
 
42
- Set them on `<html>` (portalled content inherits) or any subtree. A monospace
43
- root needs `html { font-size: 16px }` (browser quirk: monospace roots default
44
- to ~13px, silently shrinking every rem-derived token).
42
+ **SET EVERY AXIS ON `<html>`.** A subtree override works for everything
43
+ rendered inside that subtree and does NOT reach anything that portals.
44
+ Dialog, AlertDialog, Sheet, DropdownMenu, ContextMenu, Menubar, Tooltip and
45
+ LinkPreview render at `document.body`, so they take the ROOT's axes, not the
46
+ ones on the wrapper their trigger sits in.
47
+
48
+ Measured: a `<Card>` inside a `data-surface="bevel"` div resolves to bevel and
49
+ gets the chisel; a `<Dialog.Content>` opened from that same div resolves to
50
+ `flat` and gets flat's chrome. Two surfaces in one screen, and nothing errors.
51
+
52
+ So a subtree override is for a REGION you want to look different — a settings
53
+ panel that is deliberately denser. If you want the whole app on bevel, set it
54
+ at the root and never on a wrapper. The same applies to `data-mode`: a
55
+ `data-mode="spacious"` section will open balanced menus if the root is
56
+ balanced.
57
+
58
+ A monospace root needs `html { font-size: 16px }` (browser quirk: monospace
59
+ roots default to ~13px, silently shrinking every rem-derived token).
45
60
 
46
61
  ## The tier ladder
47
62