@signal9/era-ui 2.28.1 → 2.29.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.
@@ -3,21 +3,24 @@ import { BOUNDED_TIER_MAX, getGaps } from '../../measure.js';
3
3
  /*
4
4
  * The zero-gap bug, caught at the source.
5
5
  *
6
- * This is the copy button's original defect made mechanical: it shipped as
7
- * `size="icon"` (an h-xs button) holding an `size-(--era-h-xs)` glyph, so
8
- * (14 14) / 2 = 0 the icon filled its own button edge to edge and read as a
9
- * glyph jammed against its border. Every individual class was legal; the tier was
10
- * legal; only the NESTING was wrong, which is why nothing but a rendered
11
- * measurement could see it.
6
+ * The defect: an icon sized at the tier of a control that has PADDING it should
7
+ * be spending on the icon a text button, a select trigger — so the glyph fills
8
+ * the box edge to edge and reads as jammed against its border. Every individual
9
+ * class is legal; the tier is legal; only the NESTING is wrong, which is why
10
+ * nothing but a rendered measurement can see it.
12
11
  *
13
- * The same shape recurs whenever an icon is sized at the tier of the control that
14
- * holds it, so the check is simply: an icon inside a bounded control must have
15
- * some breathing room.
12
+ * The exception is a purpose-built icon button the `icon` variant, a zero-
13
+ * padding SQUARE (aspect-square px-0) that exists to hold exactly one glyph. It
14
+ * has no padding to spend, and it does not need any: a lucide glyph draws well
15
+ * inside its own viewBox, so a box that is flush is still optically inset, and
16
+ * the chip/pane dismiss buttons that render this way look right. A flush icon is
17
+ * the tier error ONLY where the container had room and didn't use it — so the
18
+ * check keys on that, not on flushness alone.
16
19
  */
17
20
  export const iconFlush = {
18
21
  id: 'layout/icon-flush',
19
22
  name: 'Icon flush against its control',
20
- description: 'An icon inside a bounded control must leave a gap on every side. A zero gap means the glyph was sized at its container’s own tier and fills it edge to edge.',
23
+ description: 'An icon inside a PADDED bounded control must leave a gap on every side. A zero gap there means the glyph was sized at its container’s own tier and fills padding it should have spent on breathing room. A zero-padding square icon button is exempt — it exists to hold one glyph flush.',
21
24
  category: 'layout',
22
25
  severity: 'error',
23
26
  selector: 'svg',
@@ -33,7 +36,20 @@ export const iconFlush = {
33
36
  // The control has to actually be a control: a bare <span> wrapper around an
34
37
  // icon legitimately hugs it (that is how an icon gets centred inside a
35
38
  // button in the first place), so require chrome — a radius.
36
- if (parseFloat(getComputedStyle(container).borderTopLeftRadius) <= 0)
39
+ const cs = getComputedStyle(container);
40
+ if (parseFloat(cs.borderTopLeftRadius) <= 0)
41
+ return null;
42
+ // A purpose-built icon button (aspect-square px-0) is a square with no
43
+ // padding to spend — holding its glyph flush is its whole job, not the tier
44
+ // error this hunts. The bug is an icon that ate a control's padding; a
45
+ // control with none can't have that bug. So exempt the square, unpadded
46
+ // holder and keep the audit pointed at padded controls.
47
+ const square = Math.abs(box.width - box.height) <= TOL;
48
+ const unpadded = parseFloat(cs.paddingTop) <= TOL &&
49
+ parseFloat(cs.paddingRight) <= TOL &&
50
+ parseFloat(cs.paddingBottom) <= TOL &&
51
+ parseFloat(cs.paddingLeft) <= TOL;
52
+ if (square && unpadded)
37
53
  return null;
38
54
  const g = getGaps(icon, container);
39
55
  const flushY = Math.abs(g.top) <= TOL && Math.abs(g.bottom) <= TOL;
@@ -263,9 +263,15 @@
263
263
  focuses it (opening the soft keyboard + palette). Positioned against the
264
264
  desktop, above the home-indicator safe area. Hidden from sm up — pointer
265
265
  devices reach the bar and hotkey directly. Only for a top bar; a bottom bar
266
- already puts the prompt in reach. -->
266
+ already puts the prompt in reach.
267
+
268
+ data-mode="touch" scopes the DENSITY axis to just this control, so it and
269
+ its icon re-tier to the touch scale (h-md 48px, icon 22px) however dense the
270
+ shell around it is — a proper thumb target for the one action a phone user
271
+ can't otherwise reach, without hardcoding a size. -->
267
272
  {#if mobileLauncher && position === 'top'}
268
273
  <div
274
+ data-mode="touch"
269
275
  class={cn(
270
276
  'absolute right-(--era-gap) bottom-[max(var(--era-gap),env(safe-area-inset-bottom))] sm:hidden',
271
277
  LAYER.taskbar
@@ -274,6 +280,7 @@
274
280
  <Button
275
281
  icon
276
282
  size="default"
283
+ tone="accent"
277
284
  aria-label="Open command bar"
278
285
  class="rounded-full shadow-(--era-shadow-lg)"
279
286
  onclick={() => commandBar?.focusInput()}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@signal9/era-ui",
3
- "version": "2.28.1",
3
+ "version": "2.29.0",
4
4
  "scripts": {
5
5
  "dev": "vite dev --host",
6
6
  "build": "vite build && npm run prepack",