@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).
|
|
6
|
-
*
|
|
7
|
-
*
|
|
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
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
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
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
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
|
|
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
|
-
|
|
28
|
-
|
|
29
|
-
if (!trim || trim === 'none' || trim === 'normal')
|
|
44
|
+
const trim = activeTrim(s);
|
|
45
|
+
if (!trim)
|
|
30
46
|
return null;
|
|
31
|
-
//
|
|
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;
|
package/dist/styles/index.css
CHANGED
|
@@ -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
|
|
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
|
-
*
|
|
116
|
-
*
|
|
117
|
-
*
|
|
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
|
-
|
|
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}
|