@signal9/era-ui 2.27.0 → 2.27.1

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.
@@ -2,37 +2,65 @@
2
2
  * The chopped-text bug.
3
3
  *
4
4
  * `era-text-trim` centres the ink by trimming the line box down to the x-height
5
- * band (text-box: trim-both ex alphabetic). That is exactly right on a control
6
- * with a FIXED height — the tier holds the box open and the trim only re-centres
7
- * what is inside it.
5
+ * band (text-box: trim-both ex alphabetic). It has two failure modes, and they
6
+ * are NOT the same check — the first one is why the Input shipped with every
7
+ * ascender sliced off the text you typed while this audit sat here reporting
8
+ * nothing.
8
9
  *
9
- * Put the same utility on an AUTO-height element that also clips its overflow
10
- * (`truncate`, `overflow-hidden`, an <input>) and the box collapses to the
11
- * ~7.5px x-height band. The clip then slices every ascender, cap and descender
12
- * off the glyphs, and the text renders as a chopped middle stripe.
10
+ * 1. NATIVE EDITABLES (<input>, <textarea>, <select>).
11
+ * A fixed height does NOT make these safe. The UA lays out an inner editor
12
+ * box and clips to IT, and that inner box is the trimmed line box — so the
13
+ * trim collapses it to the x-height band no matter how tall the control is,
14
+ * and the glyphs lose their tops and tails ("d" renders as "o"). The
15
+ * element's own height therefore says nothing about whether it is broken:
16
+ * the trim being ACTIVE is the whole defect. These elements also hold their
17
+ * text in `value`, not in textContent, which is the other reason the old
18
+ * height-and-textContent check could never fire on them.
13
19
  *
14
- * The tell is unmistakable in the geometry: a box shorter than its own font-size
15
- * that is still clipping. No static check can see it whether the box collapses
16
- * depends on whether some OTHER class set a height.
20
+ * 2. AUTO-HEIGHT CLIPPING ELEMENTS (a `truncate` child, overflow-hidden with no
21
+ * height). Nothing holds the box open, so it collapses to the ~7.5px band and
22
+ * the clip chops the glyphs into a middle stripe. Here the tell IS geometric:
23
+ * a box shorter than its own font-size that is still clipping. A FIXED-height
24
+ * non-editable box is fine — the ink overflows the trimmed LINE box but stays
25
+ * inside the padding box, which is what clips.
17
26
  */
27
+ const EDITABLE = 'input, textarea, select';
28
+ /** The trim, as the browser actually resolved it. Chromium exposes the longhand. */
29
+ function activeTrim(s) {
30
+ const trim = s.getPropertyValue('text-box-trim').trim() || s.getPropertyValue('text-box').trim();
31
+ if (!trim || trim === 'none' || trim === 'normal')
32
+ return null;
33
+ return trim;
34
+ }
18
35
  export const collapsedTextTrim = {
19
36
  id: 'typography/collapsed-text-trim',
20
37
  name: 'Trimmed text box has not collapsed',
21
- description: 'era-text-trim on an auto-height element that clips overflow collapses the box to the x-height band, slicing the tops and tails off the glyphs.',
38
+ description: 'era-text-trim clips the glyphs when it lands on a native editable (any height) or on an auto-height element that clips its overflow.',
22
39
  category: 'layout',
23
40
  severity: 'error',
24
41
  selector: '*',
25
42
  check(el) {
26
43
  const s = getComputedStyle(el);
27
- // Is the trim active on this element? Chromium exposes the longhand.
28
- const trim = s.getPropertyValue('text-box-trim').trim() || s.getPropertyValue('text-box').trim();
29
- if (!trim || trim === 'none' || trim === 'normal')
44
+ const trim = activeTrim(s);
45
+ if (!trim)
30
46
  return null;
31
- // Is it clipping?
47
+ // (1) A native editable is broken by the trim at ANY height — it clips to the
48
+ // trimmed inner editor box. No geometry test, and no textContent test: an
49
+ // input's text lives in `value`, and an EMPTY input is just as broken, it
50
+ // simply has nothing to chop yet.
51
+ if (el.matches(EDITABLE)) {
52
+ const issue = {
53
+ auditId: 'typography/collapsed-text-trim',
54
+ element: el,
55
+ message: `text-box trim (${trim}) on a <${el.tagName.toLowerCase()}> — a native editable clips to the trimmed inner editor box, so the text you type loses its ascenders and descenders ("d" renders as "o"). Drop the trim; the tier height centres it.`,
56
+ details: { trim, tag: el.tagName.toLowerCase() }
57
+ };
58
+ return issue;
59
+ }
60
+ // (2) Everything else: only a collapsed, clipping box is a defect.
32
61
  const clips = /hidden|clip|auto|scroll/.test(s.overflowX + s.overflowY);
33
62
  if (!clips)
34
63
  return null;
35
- // Does it hold any text of its own?
36
64
  if (!el.textContent?.trim())
37
65
  return null;
38
66
  const h = el.getBoundingClientRect().height;
@@ -101,20 +101,29 @@
101
101
  * Where text-box is supported it fully determines the box, so the two
102
102
  * declarations never fight. Worst-case residual: 0.44px.
103
103
  *
104
- * The one hard rule concerns overflow clipping, and it is HEIGHT-conditional.
105
- * On an AUTO-height element that also clips — a `truncate` child, a
106
- * <textarea>, an auto-height <input> — trim-both collapses the box to the
107
- * x-height band (~7.5px at 14px) and the clip slices off every ascender, cap,
108
- * and descender (text renders as a chopped middle stripe): NEVER do that.
109
- * A FIXED-height control is the intended home, and is safe: the explicit
110
- * `h-(--era-*)` overrides the collapse, so a single-line <input> (Input,
111
- * command-input) and clipping display tokens (KV) correctly carry it. For
112
- * truncating text, put the trim on the fixed-height ROW and let the child
113
- * center via the row's items-center.
104
+ * The hard rules concern overflow clipping, and there are TWO of them.
114
105
  *
115
- * @use vertically center single-line control text (a label beside an icon, an
116
- * input's own text) without a translateY nudge fixed-height elements only;
117
- * see era-text-trim-caps for ALL-CAPS strings.
106
+ * 1. NEVER on a NATIVE EDITABLE <input>, <textarea>, <select>. These clip to
107
+ * a UA-owned inner editor box that IS the trimmed line box, so a fixed
108
+ * height does NOT hold it open: whatever you type gets its ascenders and
109
+ * descenders sliced off (a "d" renders as an "o"). This rule used to read
110
+ * the other way round — it claimed a fixed-height <input> was the trim's
111
+ * intended home — and the Input component shipped chopped text as a result.
112
+ * A tier-height input centres its own text; it needs no trim.
113
+ *
114
+ * 2. NEVER on an AUTO-height element that clips — a `truncate` child, an
115
+ * auto-height box with overflow-hidden. With no height to hold it open the
116
+ * box collapses to the x-height band (~7.5px at 14px) and the clip chops the
117
+ * glyphs into a middle stripe. Put the trim on the fixed-height ROW instead
118
+ * and let the child centre via the row's items-center.
119
+ *
120
+ * A fixed-height NON-editable element is the safe home: the ink overflows the
121
+ * trimmed LINE box but stays inside the padding box, which is what actually
122
+ * clips. Both rules are enforced at runtime by typography/collapsed-text-trim.
123
+ *
124
+ * @use vertically center single-line control text (a label beside an icon, a
125
+ * button's own text) without a translateY nudge — fixed-height, non-editable
126
+ * elements only; see era-text-trim-caps for ALL-CAPS strings.
118
127
  */
119
128
  @utility era-text-trim {
120
129
  line-height: 1;
@@ -16,7 +16,11 @@
16
16
  bind:this={ref}
17
17
  bind:value
18
18
  class={cn(
19
- 'flex h-(--era-h-md) w-full era-interactive rounded-(--era-rd-md) bg-(--era-surface-bg-elevated) px-(--era-inset-md) text-body era-text-trim text-fg shadow-(--era-shadow-well) placeholder:text-muted hover:bg-(--era-highlight) focus:bg-(--era-highlight)',
19
+ // No era-text-trim here: an <input> clips to a UA-owned inner editor box that
20
+ // IS the trimmed line box, so the trim slices the ascenders and descenders off
21
+ // what you type (a "d" renders as an "o"). The fixed tier height centres the
22
+ // text on its own. See the utility's note in styles/index.css.
23
+ 'flex h-(--era-h-md) w-full era-interactive rounded-(--era-rd-md) bg-(--era-surface-bg-elevated) px-(--era-inset-md) text-body text-fg shadow-(--era-shadow-well) placeholder:text-muted hover:bg-(--era-highlight) focus:bg-(--era-highlight)',
20
24
  className
21
25
  )}
22
26
  {...restProps}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@signal9/era-ui",
3
- "version": "2.27.0",
3
+ "version": "2.27.1",
4
4
  "scripts": {
5
5
  "dev": "vite dev --host",
6
6
  "build": "vite build && npm run prepack",