@cplieger/web-terminal-ui 5.0.1 → 5.0.2

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.
@@ -42,6 +42,33 @@
42
42
  background: var(--text-active);
43
43
  }
44
44
 
45
+ /* ===== Hover gating: any-hover, never hover =====
46
+ Every gated hover rule in this package uses @media (any-hover: hover). Do not
47
+ write @media (hover: hover) here.
48
+
49
+ What the gate is for: a bare touchscreen fires no pointer-leave, so a tapped
50
+ control keeps its :hover wash until the next tap somewhere else. What went
51
+ wrong: `hover` and `pointer` describe only the PRIMARY input, and iPadOS
52
+ reports hover: none / pointer: coarse whether or not a Magic Keyboard trackpad
53
+ is attached. So (hover: hover) is false on every touch-primary device,
54
+ INCLUDING the ones driving a real pointer.
55
+
56
+ For the coarse-only controls that made the gate not a tradeoff but a deletion,
57
+ and it shipped that way: .wt-tab-kb renders only under @media (pointer: coarse)
58
+ (30-tabs.css) and the whole .wt-switcher subtree only under (pointer: coarse)
59
+ plus .wt-narrow (31-switcher.css). Pairing either with (hover: hover) makes the
60
+ two conditions complementary, so the hover rule was unreachable on every device
61
+ that exists. Measured against the deployed bundle at iPad width: the keyboard
62
+ button is a visible 44x44 while a forced :hover leaves its background at
63
+ rgba(0, 0, 0, 0), where the ungated .wt-tab-new beside it resolves to
64
+ --tab-active-bg.
65
+
66
+ `any-hover` asks the question the gate meant to ask — can ANY attached device
67
+ hover — so a bare iPhone/iPad still answers none (no wash, nothing to stick)
68
+ while a trackpad or Pencil answers hover and brings a real pointer-leave with
69
+ it. Same defect class as Tailwind's hoverOnlyWhenSupported
70
+ (tailwindlabs/tailwindcss#11109). */
71
+
45
72
  /* Keyboard-focus ring for every chrome control (all interactive chrome is a
46
73
  real <button>: tabs, +, keyboard, switcher rows, menu items, the FAB).
47
74
  :focus-visible only — a tap/click never shows the ring (the UA heuristic),
@@ -147,7 +174,7 @@
147
174
  font-weight: 600;
148
175
  }
149
176
 
150
- @media (hover: hover) {
177
+ @media (any-hover: hover) {
151
178
  :where(.wt-root) .wt-fatal-reload:hover {
152
179
  background: color-mix(in oklch, var(--accent), var(--text) 12%);
153
180
  }
package/css/30-tabs.css CHANGED
@@ -158,11 +158,12 @@
158
158
  vocabulary and stays themeable through one token. It is a rung subtler than the
159
159
  inactive chip's 15%/25% because it starts from an already-lifted fill.
160
160
 
161
- Hover is gated to real hover pointers. Tapping an INACTIVE chip makes it
162
- active, so a touchscreen's sticky :hover leaves with the class; tapping the
163
- ACTIVE chip changes nothing, so an ungated hover would stay lit until the next
164
- tap somewhere else. Touch feedback there is :active, which is correct. */
165
- @media (hover: hover) {
161
+ Hover is gated (see the any-hover note in 10-primitives.css). Tapping an
162
+ INACTIVE chip makes it active, so a touchscreen's sticky :hover leaves with the
163
+ class; tapping the ACTIVE chip changes nothing, so an ungated hover would stay
164
+ lit until the next tap somewhere else. Touch feedback there is :active, which is
165
+ correct. */
166
+ @media (any-hover: hover) {
166
167
  :where(.wt-root) .wt-tab-active:hover {
167
168
  background: color-mix(in oklch, var(--tab-active-bg), var(--tab-active-fg) 12%);
168
169
  }
@@ -382,13 +383,15 @@
382
383
 
383
384
  /* Hover wash, matching every other bar control (.wt-tab-new, the switcher's
384
385
  buttons) — without it this was the one control in the chrome that gave no
385
- pointer feedback at all. Gated to real hover pointers because the button only
386
- SHOWS on a coarse pointer (the iPad strip): a plain touchscreen has no
387
- pointer-leave, so :hover would stick after a tap and the wash would linger;
388
- the feedback there is :active, from .wt-btn. Before .wt-active / .wt-armed so
389
- those toggled fills win on a hovered button (equal specificity, source order
390
- decides). */
391
- @media (hover: hover) {
386
+ pointer feedback at all. Gated on any-hover, NOT hover: this button renders only
387
+ under (pointer: coarse) below, and a touch-primary device reports hover: none
388
+ even with a trackpad attached, so a (hover: hover) gate here made the wash
389
+ unreachable on every device rather than restricting it to hovering ones (the
390
+ note in 10-primitives.css has the measurement). On a bare touchscreen any-hover
391
+ is none, so the feedback there is still :active, from .wt-btn. Before
392
+ .wt-active / .wt-armed so those toggled fills win on a hovered button (equal
393
+ specificity, source order decides). */
394
+ @media (any-hover: hover) {
392
395
  :where(.wt-root) .wt-tab-kb:hover {
393
396
  background: var(--tab-hover-bg);
394
397
  }
@@ -148,7 +148,7 @@
148
148
  color: color-mix(in oklch, var(--text) 55%, transparent);
149
149
  }
150
150
 
151
- @media (hover: hover) {
151
+ @media (any-hover: hover) {
152
152
  :where(.wt-root) .wt-switcher-current-close:hover {
153
153
  color: var(--text);
154
154
  }
@@ -216,10 +216,11 @@
216
216
  overflow: hidden;
217
217
  }
218
218
 
219
- /* Hover wash like the switch and "+" buttons beside it; gated to real hover
220
- pointers for the same reason they are (a sticky :hover after a tap). Before
221
- .wt-active / .wt-armed so those fills win on a hovered button. */
222
- @media (hover: hover) {
219
+ /* Hover wash like the switch and "+" buttons beside it; gated on any-hover for
220
+ the same reason they are (see the note in 10-primitives.css — this whole
221
+ subtree renders only on a coarse pointer, where hover: hover is always false).
222
+ Before .wt-active / .wt-armed so those fills win on a hovered button. */
223
+ @media (any-hover: hover) {
223
224
  :where(.wt-root) .wt-switcher-kb:hover {
224
225
  background: var(--tab-hover-bg);
225
226
  }
@@ -292,7 +293,7 @@
292
293
  pointer-events: none;
293
294
  }
294
295
 
295
- @media (hover: hover) {
296
+ @media (any-hover: hover) {
296
297
  :where(.wt-root) .wt-switcher-switch:hover {
297
298
  background: var(--tab-hover-bg);
298
299
  }
@@ -320,15 +321,17 @@
320
321
  }
321
322
 
322
323
  /* Mobile "+": the primary action, carried in the accent colour so it reads as
323
- permanent and prominent. Hover fill is gated to real hover pointers: on touch,
324
- :hover sticks after a tap (no pointer-leave), so the purple would linger — the
325
- press feedback there comes from :active, which clears on release. */
324
+ permanent and prominent. Hover fill is gated on any-hover (see the note in
325
+ 10-primitives.css): on a bare touchscreen any-hover is none, so the purple
326
+ cannot linger after a tap and the press feedback comes from :active; a trackpad
327
+ or Pencil reports hover and gets the wash, which a (hover: hover) gate denied it.
328
+ */
326
329
  :where(.wt-root) .wt-switcher-new {
327
330
  flex: none;
328
331
  color: var(--accent);
329
332
  }
330
333
 
331
- @media (hover: hover) {
334
+ @media (any-hover: hover) {
332
335
  :where(.wt-root) .wt-switcher-new:hover {
333
336
  background: var(--tab-hover-bg);
334
337
  }
@@ -418,7 +421,7 @@
418
421
  overflow: hidden;
419
422
  }
420
423
 
421
- @media (hover: hover) {
424
+ @media (any-hover: hover) {
422
425
  :where(.wt-root) .wt-switcher-row-select:hover {
423
426
  background: color-mix(in oklch, var(--tab-bg), var(--text) 15%);
424
427
  }
@@ -458,10 +461,13 @@
458
461
  color: color-mix(in oklch, var(--text) 55%, transparent);
459
462
  }
460
463
 
461
- /* Hover only on real hover pointers: on touch, :hover sticks after a tap, so
462
- when closing a tab shifts the list under the finger the newly-under close
463
- would latch white. Touch feedback comes from the surrounding row's :active. */
464
- @media (hover: hover) {
464
+ /* Hover gated on any-hover (see the note in 10-primitives.css): on a bare
465
+ touchscreen any-hover is none, so when closing a tab shifts the list under the
466
+ finger the newly-under close cannot latch white, and the feedback comes from the
467
+ surrounding row's :active. A trackpad or Pencil reports hover and gets the
468
+ brighten, which a (hover: hover) gate denied it — this subtree only ever renders
469
+ on a coarse-primary device. */
470
+ @media (any-hover: hover) {
465
471
  :where(.wt-root) .wt-switcher-row-close:hover {
466
472
  color: var(--text);
467
473
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cplieger/web-terminal-ui",
3
- "version": "5.0.1",
3
+ "version": "5.0.2",
4
4
  "type": "module",
5
5
  "description": "Reference touch-first browser UI for @cplieger/web-terminal-engine: a composable kernel + feature modules (tabs, activity monitor, mobile toolbar, context menu, IME, predictive echo, viewport handling) and CSS. One createTerminal() call.",
6
6
  "license": "GPL-3.0-or-later",