efficient-easel 0.105.1 → 1.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.
- package/README.md +103 -29
- package/dist/{LinkPicker-W2Rtxetz.d.ts → LinkPicker-hPEr1MJx.d.ts} +6 -4
- package/dist/autosave/index.d.ts +5 -5
- package/dist/chrome/chrome.css +50 -273
- package/dist/chrome/index.d.ts +61 -56
- package/dist/chrome/index.js +1 -1
- package/dist/chunk-2GVCAIPX.js +1 -0
- package/dist/chunk-4GNPKHV4.js +64 -0
- package/dist/chunk-5545A3PC.js +4 -0
- package/dist/chunk-7SQF6LZL.js +1 -0
- package/dist/chunk-BXMXTO36.js +1 -0
- package/dist/{chunk-NYKIQJT2.js → chunk-CKEPQNSQ.js} +1 -1
- package/dist/chunk-DJMMTWFP.js +1 -0
- package/dist/{chunk-PQXIFBDY.js → chunk-E2O2MRZR.js} +1 -1
- package/dist/chunk-EA2HTX37.js +5 -0
- package/dist/{chunk-SARZN4M7.js → chunk-FEK3S6HN.js} +1 -1
- package/dist/chunk-FPAVNX3Y.js +6 -0
- package/dist/chunk-HMJS344F.js +1 -0
- package/dist/chunk-JLTZQ4US.js +1 -0
- package/dist/chunk-JMGPPXNQ.js +24 -0
- package/dist/chunk-K56YDHYQ.js +1 -0
- package/dist/chunk-LOMEH6DG.js +1 -0
- package/dist/{chunk-V4LF7D4F.js → chunk-MAEXP3KE.js} +1 -1
- package/dist/chunk-MJM6SFD3.js +3 -0
- package/dist/chunk-NLRW4BGG.js +8 -0
- package/dist/chunk-OJAJVH7S.js +5 -0
- package/dist/chunk-ONRYLR2D.js +2 -0
- package/dist/chunk-ORR3Q4WV.js +1 -0
- package/dist/chunk-OTEXGJDU.js +5 -0
- package/dist/chunk-P2VEMZH5.js +1 -0
- package/dist/chunk-PT6V53OO.js +1 -0
- package/dist/chunk-PYCLHOSA.js +9 -0
- package/dist/chunk-Q27BB5ZC.js +14 -0
- package/dist/chunk-QLD57GBB.js +6 -0
- package/dist/{chunk-NWIW7AWH.js → chunk-QMNML7XH.js} +1 -1
- package/dist/chunk-QR4DLZ3P.js +5 -0
- package/dist/chunk-TJ7YIRWQ.js +21 -0
- package/dist/{chunk-3P6AMTOS.js → chunk-VPFNZ7TB.js} +1 -1
- package/dist/chunk-W3XX2CNW.js +16 -0
- package/dist/chunk-X6DQWK2C.js +1 -0
- package/dist/chunk-XWR5YVA7.js +9 -0
- package/dist/chunk-YABL4EWX.js +7 -0
- package/dist/chunk-ZNNATBDC.js +1 -0
- package/dist/chunk-ZP4G3X44.js +4 -0
- package/dist/copy/copy.css +66 -177
- package/dist/copy/index.d.ts +0 -2
- package/dist/copy/index.js +31 -32
- package/dist/drag/drag.css +31 -11
- package/dist/drag/index.d.ts +10 -0
- package/dist/drag/index.js +1 -1
- package/dist/easel/index.d.ts +10 -1
- package/dist/easel/index.js +1 -1
- package/dist/fields/fields.css +35 -304
- package/dist/fields/index.d.ts +87 -95
- package/dist/fields/index.js +1 -1
- package/dist/gold/gold.css +3 -3
- package/dist/gold/index.d.ts +6 -5
- package/dist/gold/index.js +1 -1
- package/dist/{host-BQ_b9Qzk.d.ts → host-CiuzZJXj.d.ts} +11 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.js +1 -1
- package/dist/menu/index.d.ts +49 -93
- package/dist/menu/index.js +1 -1
- package/dist/menu/menu.css +37 -246
- package/dist/node-ops/index.d.ts +1 -1
- package/dist/panel/index.d.ts +46 -72
- package/dist/panel/index.js +17 -18
- package/dist/panel/panel.css +143 -372
- package/dist/pick/index.d.ts +52 -33
- package/dist/pick/index.js +7 -7
- package/dist/pick/pick.css +19 -150
- package/dist/rail/index.d.ts +48 -90
- package/dist/rail/index.js +1 -1
- package/dist/rail/rail.css +21 -108
- package/dist/screen/components/AddBlockRow.d.ts +8 -2
- package/dist/screen/components/AddBlockRow.js +1 -1
- package/dist/screen/components/AddChildRow.d.ts +4 -3
- package/dist/screen/components/AddChildRow.js +1 -1
- package/dist/screen/components/AddSidePanel.js +1 -1
- package/dist/screen/components/MediaSidePanel.d.ts +2 -2
- package/dist/screen/components/MediaSidePanel.js +1 -1
- package/dist/screen/components/NodeContextMenu.d.ts +31 -9
- package/dist/screen/components/NodeContextMenu.js +1 -1
- package/dist/screen/components/PageEditGuardNotices.js +1 -1
- package/dist/screen/components/ScreenChrome.js +1 -1
- package/dist/screen/components/ScreenDocumentContext.d.ts +2 -2
- package/dist/screen/components/ScreenDocumentContext.js +1 -1
- package/dist/screen/components/ScreenMount.d.ts +8 -8
- package/dist/screen/components/ScreenMount.js +1 -1
- package/dist/screen/components/ScreenStoreProvider.d.ts +8 -8
- package/dist/screen/components/ScreenStoreProvider.js +1 -1
- package/dist/screen/components/SectionScreenShell.js +16 -16
- package/dist/screen/components/SidePanelHead.d.ts +4 -2
- package/dist/screen/components/SidePanelHead.js +1 -1
- package/dist/screen/components/UiIcon.d.ts +10 -26
- package/dist/screen/components/UiIcon.js +2 -1
- package/dist/screen/components/panel-widths.d.ts +15 -1
- package/dist/screen/components/panel-widths.js +1 -1
- package/dist/screen/components/usePartBridge.d.ts +8 -8
- package/dist/screen/components/usePartBridge.js +1 -1
- package/dist/screen/index.d.ts +9 -9
- package/dist/screen/index.js +1 -1
- package/dist/screen/lib/admin-bridge.d.ts +1 -1
- package/dist/screen/lib/admin-bridge.js +1 -1
- package/dist/screen/lib/chrome-theme.d.ts +20 -12
- package/dist/screen/lib/chrome-theme.js +1 -1
- package/dist/screen/lib/document-addressing.d.ts +2 -2
- package/dist/screen/lib/host-chrome.d.ts +1 -1
- package/dist/screen/lib/host.d.ts +2 -2
- package/dist/screen/lib/key-scope.d.ts +31 -2
- package/dist/screen/lib/key-scope.js +1 -1
- package/dist/screen/lib/links/link-filters.d.ts +1 -1
- package/dist/screen/lib/media-panel.d.ts +2 -2
- package/dist/screen/lib/media-picker.d.ts +2 -2
- package/dist/screen/lib/page-lock.d.ts +1 -1
- package/dist/screen/lib/puck/section-doc.d.ts +5 -1
- package/dist/screen/lib/puck/section-doc.js +1 -1
- package/dist/screen/lib/puck/style-clipboard.d.ts +8 -8
- package/dist/screen/lib/puck/use-section-ops.js +1 -1
- package/dist/screen/lib/screen-parts.d.ts +47 -10
- package/dist/screen/lib/screen-parts.js +1 -1
- package/dist/screen/lib/section-templates.d.ts +1 -1
- package/dist/screen/lib/session-context.d.ts +1 -1
- package/dist/screen/lib/session.d.ts +1 -1
- package/dist/screen/screen.css +54 -206
- package/dist/screen/words.d.ts +17 -1
- package/dist/screen/words.js +1 -1
- package/dist/shell/index.d.ts +134 -118
- package/dist/shell/index.js +1 -1
- package/dist/shell/shell.css +130 -88
- package/dist/strings/catalogue.d.ts +5 -5
- package/dist/strings/catalogue.js +1 -1
- package/dist/strings/index.d.ts +5 -5
- package/dist/strings/index.js +1 -1
- package/dist/style-clipboard/index.d.ts +1 -1
- package/dist/text-styles/index.d.ts +12 -3
- package/dist/text-styles/index.js +33 -38
- package/dist/text-styles/text-styles.css +39 -71
- package/dist/tree/index.d.ts +49 -44
- package/dist/tree/index.js +1 -1
- package/dist/tree/tree.css +31 -653
- package/dist/{types-DJDBtdB2.d.ts → types-BqGnUVX5.d.ts} +32 -32
- package/dist/types-BsyPh6EW.d.ts +99 -0
- package/dist/{types-DWS-s9z-.d.ts → types-CsgMwHHa.d.ts} +14 -11
- package/dist/{types-CqY85foL.d.ts → types-Dt1CFR1S.d.ts} +4 -2
- package/dist/{wordFit-D4S36PvB.d.ts → wordFit-7XpmhcaW.d.ts} +3 -5
- package/dist/words-Cl9xMUYY.d.ts +41 -0
- package/dist/{words-D_Lnw1Gr.d.ts → words-CvFnwGSr.d.ts} +1 -1
- package/package.json +5 -1
- package/dist/chunk-2UX75JDP.js +0 -1
- package/dist/chunk-3EDSDPIP.js +0 -1
- package/dist/chunk-3QVB4AG2.js +0 -3
- package/dist/chunk-3WSPDAK2.js +0 -1
- package/dist/chunk-53G47SWO.js +0 -1
- package/dist/chunk-5MZ3RNQQ.js +0 -13
- package/dist/chunk-5ZBBXZPV.js +0 -6
- package/dist/chunk-7FBDZTUD.js +0 -2
- package/dist/chunk-7V5WZ46F.js +0 -2
- package/dist/chunk-BXZ6WLHC.js +0 -22
- package/dist/chunk-BZ5ASXOL.js +0 -1
- package/dist/chunk-EOJNJCMJ.js +0 -16
- package/dist/chunk-EWVJSRRY.js +0 -6
- package/dist/chunk-FMGFZNPW.js +0 -1
- package/dist/chunk-H24N5TMZ.js +0 -11
- package/dist/chunk-HLEET6NV.js +0 -65
- package/dist/chunk-IE4GFYKO.js +0 -1
- package/dist/chunk-KTVH2ZRL.js +0 -15
- package/dist/chunk-N7VRF7PR.js +0 -5
- package/dist/chunk-OWBO4WKA.js +0 -0
- package/dist/chunk-P67FP7PG.js +0 -9
- package/dist/chunk-P6O363EE.js +0 -6
- package/dist/chunk-Q4N7C6CT.js +0 -10
- package/dist/chunk-QH6LCNOX.js +0 -8
- package/dist/chunk-RW4XBUUW.js +0 -1
- package/dist/chunk-TTJ54QGG.js +0 -18
- package/dist/chunk-VSHV6KJQ.js +0 -5
- package/dist/chunk-VSOP4GUM.js +0 -1
- package/dist/chunk-WZ7TGAAT.js +0 -3
- package/dist/chunk-YC4TB3CJ.js +0 -25
- package/dist/chunk-ZFDCDJNZ.js +0 -16
- package/dist/chunk-ZNS7727Y.js +0 -12
- package/dist/chunk-ZTTWFAAC.js +0 -1
- package/dist/types-Djdq6Xkd.d.ts +0 -138
- package/dist/words-DgHcY5qB.d.ts +0 -33
package/dist/panel/panel.css
CHANGED
|
@@ -3,8 +3,9 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Values come from the shell token layer (`efficient-easel/shell.css`), never from
|
|
5
5
|
* literals: this file is the storefront editor's approved panel design expressed
|
|
6
|
-
* once, so both editors read the same rules.
|
|
7
|
-
* dark
|
|
6
|
+
* once, so both editors read the same rules. The token layer resolves to
|
|
7
|
+
* efficient-shell's tokens, so the panel follows the shell's theme, dark
|
|
8
|
+
* included (UI-24).
|
|
8
9
|
*
|
|
9
10
|
* Two details are deliberate and were decided by measurement, not taste:
|
|
10
11
|
* · rows sit 32px apart while a label sits 12px from its own control, so a
|
|
@@ -188,13 +189,10 @@
|
|
|
188
189
|
|
|
189
190
|
The context lives HERE rather than in a host's own panel rule so the shell is
|
|
190
191
|
self-contained: a host that never declares `container-type` would silently
|
|
191
|
-
pin the strip to one state with nothing to see and nothing to fail.
|
|
192
|
-
|
|
193
|
-
`.es-rail-fit` is the top-bar rail's column (`TopBarRail`), which reads the
|
|
194
|
-
word-fit rule below: its container is the left panel's own width. */
|
|
192
|
+
pin the strip to one state with nothing to see and nothing to fail. (The
|
|
193
|
+
top-bar rail's words are measured by the shell's IconRail since 4.165.0.) */
|
|
195
194
|
.es-tabs-fit,
|
|
196
|
-
.es-segs-fit
|
|
197
|
-
.es-rail-fit {
|
|
195
|
+
.es-segs-fit {
|
|
198
196
|
container-type: inline-size;
|
|
199
197
|
flex: none;
|
|
200
198
|
}
|
|
@@ -249,133 +247,28 @@
|
|
|
249
247
|
min-width: 0;
|
|
250
248
|
}
|
|
251
249
|
|
|
252
|
-
/* THE TRACK
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
and every other filled row here — `.es-group-head` at `width: 100%` — runs
|
|
265
|
-
the full width of the body's content box; a track inset 14px further than the
|
|
266
|
-
rows beneath it is visibly out of line. It is also worth 28px of room, which
|
|
267
|
-
is the difference between three segments fitting the shop's default panel and
|
|
268
|
-
not.
|
|
269
|
-
|
|
270
|
-
NOWRAP, unlike the strip it replaces. A segmented control is one row by
|
|
271
|
-
definition — a second row of track under the first is not a wrapped control,
|
|
272
|
-
it is two controls. The guarantee that no bucket is ever out of reach is
|
|
273
|
-
re-made out of different parts: segments that share the leftover space, and
|
|
274
|
-
the drop rules below. */
|
|
250
|
+
/* THE TRACK AND ITS CHIPS ARE THE SHELL'S (efficient-easel issue 199 step 4, harness
|
|
251
|
+
UI-24). `SettingsTabs` is the shell's `Tabs` (`pill`), `SettingsSegmented`
|
|
252
|
+
its `Segmented`: the grey track, the chosen chip filled with the accent (card
|
|
253
|
+
66138's segmented look, Lewis 2026-10-06's blue), the quiet chips' ink, the
|
|
254
|
+
focus ring, a count (`CountBadge`) and a chip out of force are the kit's.
|
|
255
|
+
What stays here is LAYOUT the shell has no prop for:
|
|
256
|
+
|
|
257
|
+
FULL WIDTH, ONE ROW. A track is a filled row, and every other filled row
|
|
258
|
+
here runs the full width of the body's content box; a segmented control is
|
|
259
|
+
one row by definition, so it never wraps. A SEGMENT TAKES ITS OWN WIDTH, THEN
|
|
260
|
+
SHARES WHAT IS LEFT (`1 1 auto`, not equal segments: three equal segments
|
|
261
|
+
need a 267px track against the 281px the shop's default panel gives). */
|
|
275
262
|
.es-tabs,
|
|
276
263
|
.es-segs {
|
|
277
264
|
display: flex;
|
|
278
265
|
flex-wrap: nowrap;
|
|
279
|
-
|
|
280
|
-
padding: var(--es-track-pad);
|
|
281
|
-
background: var(--es-track-bg);
|
|
282
|
-
border-radius: var(--es-row-radius);
|
|
283
|
-
flex: none;
|
|
266
|
+
width: 100%;
|
|
284
267
|
}
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
`1 1 auto` and not `1 1 0`: equal segments were measured and are too
|
|
289
|
-
expensive. Every equal segment takes the width of the WIDEST label, and
|
|
290
|
-
"Content" is 85.36px at 12px/600, so three equal segments need a 267px track
|
|
291
|
-
and four need 355px — against the 281px the shop's default panel gives. With
|
|
292
|
-
natural widths the track still reaches both edges, and "Style" is not forced
|
|
293
|
-
to be as wide as "Content" to get there.
|
|
294
|
-
|
|
295
|
-
`box-sizing` for the same reason the switch sets it: this file assumes no
|
|
296
|
-
reset of the host's. */
|
|
297
|
-
.es-tab,
|
|
298
|
-
.es-seg {
|
|
299
|
-
box-sizing: border-box;
|
|
268
|
+
.es-tabs > [role='tab'],
|
|
269
|
+
.es-segs > button {
|
|
300
270
|
flex: 1 1 auto;
|
|
301
|
-
border: 0;
|
|
302
|
-
background: transparent;
|
|
303
|
-
border-radius: 6px;
|
|
304
|
-
padding: 4px var(--es-chip-pad-x);
|
|
305
|
-
font: inherit;
|
|
306
|
-
font-size: 12px;
|
|
307
|
-
font-weight: 600;
|
|
308
|
-
color: var(--es-text-secondary);
|
|
309
271
|
white-space: nowrap;
|
|
310
|
-
cursor: pointer;
|
|
311
|
-
display: inline-flex;
|
|
312
|
-
align-items: center;
|
|
313
|
-
justify-content: center;
|
|
314
|
-
gap: var(--es-chip-icon-gap);
|
|
315
|
-
transition: background 0.12s, color 0.12s, box-shadow 0.12s;
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
/* A quiet segment is READABLE, not faint. It was `--es-muted`, which on the new
|
|
319
|
-
grey track sits at about 4.3:1 and misses AA; this is about 8.9:1. The track
|
|
320
|
-
is what says "pressable" now, so the label no longer has to whisper to stay
|
|
321
|
-
out of the chosen tab's way. */
|
|
322
|
-
.es-tab:hover:not(.is-active),
|
|
323
|
-
.es-seg:hover:not(.is-active) { color: var(--es-text); }
|
|
324
|
-
|
|
325
|
-
/* Keyboard focus, themed rather than left to the UA. Same ring as the switch,
|
|
326
|
-
so the panel answers a keyboard the one way. */
|
|
327
|
-
.es-tab:focus-visible,
|
|
328
|
-
.es-seg:focus-visible {
|
|
329
|
-
outline: none;
|
|
330
|
-
box-shadow: 0 0 0 3px var(--es-accent-ring);
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
/* THE CHOSEN CHIP IS BLUE (Lewis, 2026-10-06): 「用蓝色的背景, 不要黑色的了」.
|
|
334
|
-
|
|
335
|
-
Said while the shop's hand-built "pick one of 2-3" rungs were merged into
|
|
336
|
-
this control. It was `--es-text`, near-black, since card 66302; before that a
|
|
337
|
-
raised white chip (card 66138). Both editors draw every segmented control and
|
|
338
|
-
the settings tab strip from this one rule, so this is the whole change.
|
|
339
|
-
|
|
340
|
-
THE NUMBERS STILL HOLD, which is why the colour could move at all. Card 66302
|
|
341
|
-
made the chip the ONLY state cue once the words drop at the narrow end, and
|
|
342
|
-
WCAG 1.4.11 asks 3:1 of it: `--es-accent` on `--es-track-bg` is
|
|
343
|
-
4.70:1, and the white word on it is 5.17:1 (1.4.3 asks 4.5:1 at 12px).
|
|
344
|
-
|
|
345
|
-
⚠️ THE LIFT STAYS, AND IT IS NOT THE MESSAGE. `--es-raised-shadow` seats the
|
|
346
|
-
chip on the track; the colour is what tells the states apart, because a
|
|
347
|
-
shadow cannot be scored and that is how this once sat at 1.10:1 unnoticed.
|
|
348
|
-
`tests/panel-chip-contrast.test.ts` pins the ratios AND, since the ruling,
|
|
349
|
-
the colour itself. */
|
|
350
|
-
.es-tab.is-active,
|
|
351
|
-
.es-seg.is-active {
|
|
352
|
-
background: var(--es-accent);
|
|
353
|
-
color: var(--es-on-accent);
|
|
354
|
-
box-shadow: var(--es-raised-shadow);
|
|
355
|
-
border-radius: 6px;
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
/* Focused AND chosen. The quiet chip's ring is a translucent accent wash, and
|
|
359
|
-
laid against a chip that is itself the accent it reads as the chip growing,
|
|
360
|
-
not as focus. So this ring stands OFF the chip: a 2px gap in the panel
|
|
361
|
-
colour, then a solid accent ring — the blue chip, a white gap, a blue line. */
|
|
362
|
-
.es-tab.is-active:focus-visible,
|
|
363
|
-
.es-seg.is-active:focus-visible {
|
|
364
|
-
box-shadow: 0 0 0 2px var(--es-panel), 0 0 0 4px var(--es-accent);
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
.es-tab-badge {
|
|
368
|
-
min-width: 16px;
|
|
369
|
-
height: 16px;
|
|
370
|
-
padding: 0 4px;
|
|
371
|
-
border-radius: 999px;
|
|
372
|
-
background: var(--es-accent);
|
|
373
|
-
color: var(--es-on-accent);
|
|
374
|
-
font-size: 10px;
|
|
375
|
-
font-weight: 700;
|
|
376
|
-
display: inline-flex;
|
|
377
|
-
align-items: center;
|
|
378
|
-
justify-content: center;
|
|
379
272
|
}
|
|
380
273
|
|
|
381
274
|
/* The glyph in the chip: 16px of drawing, the same size a field's icon takes,
|
|
@@ -408,74 +301,86 @@
|
|
|
408
301
|
.es-tab-icon svg,
|
|
409
302
|
.es-seg-icon svg { width: var(--es-chip-icon); height: var(--es-chip-icon); display: block; }
|
|
410
303
|
|
|
304
|
+
/* A tab with a glyph holds its glyph and its word in ONE shell Tooltip (which
|
|
305
|
+
says the word on hover, where the panel has dropped it). The Tooltip's
|
|
306
|
+
wrapper stands where the shell's tab put them side by side: the same
|
|
307
|
+
`gap-1.5`, so the measured thresholds below still hold. A clipped word is
|
|
308
|
+
out of the flow, so a glyph-only tab keeps no gap. */
|
|
309
|
+
.es-tab-tip {
|
|
310
|
+
position: relative;
|
|
311
|
+
display: inline-flex;
|
|
312
|
+
align-items: center;
|
|
313
|
+
gap: calc(1.5 * var(--spacing));
|
|
314
|
+
}
|
|
315
|
+
|
|
411
316
|
/* ── where the word stops fitting ──────────────────────────────────────────── */
|
|
412
317
|
|
|
413
|
-
/* MEASURED
|
|
414
|
-
*
|
|
415
|
-
*
|
|
416
|
-
*
|
|
417
|
-
*
|
|
418
|
-
*
|
|
318
|
+
/* RE-MEASURED ON THE SHELL'S PILL (efficient-easel issue 199, part C, 2026-10-10).
|
|
319
|
+
* The chip is efficient-shell's `Tabs` pill since step 4, wider than the 0.106
|
|
320
|
+
* chip the old numbers were taken on, so the thresholds were measured again: in
|
|
321
|
+
* headless Chromium, against the COMPILED shell CSS and these sheets with their
|
|
322
|
+
* @container tiers taken out, the shop's four buckets Content / Media / Style /
|
|
323
|
+
* Layout with 16px glyphs, by EDGE GEOMETRY — the right edge of the last tab
|
|
324
|
+
* against the right content edge of the track (not `scrollWidth`, which says
|
|
325
|
+
* "fits" about a flex button drawing past itself).
|
|
419
326
|
*
|
|
420
327
|
* Smallest container width with nothing drawn past the track edge:
|
|
421
328
|
*
|
|
422
329
|
* tabs words glyph + active word glyphs only
|
|
423
|
-
* 2
|
|
424
|
-
* 3
|
|
425
|
-
* 4
|
|
330
|
+
* 2 197px 151px 92px
|
|
331
|
+
* 3 280px 195px 136px
|
|
332
|
+
* 4 375px 239px 180px
|
|
426
333
|
*
|
|
427
|
-
*
|
|
428
|
-
*
|
|
429
|
-
*
|
|
430
|
-
*
|
|
431
|
-
* truth is arithmetic and there is nothing to measure: a chip with no word is a
|
|
432
|
-
* glyph in its padding, so the strip is `n × 32px` plus `(n − 1)` gaps plus the
|
|
433
|
-
* track's own padding — 73, 108, 143. The 4-tab cell was right by luck of being
|
|
434
|
-
* above the floor. Re-measured with the search starting at 40px, all three
|
|
435
|
-
* agree with the arithmetic to the pixel; `(d)` now pins them to it, so the
|
|
436
|
-
* cell cannot drift from the sheet again.
|
|
334
|
+
* The glyphs-only column is arithmetic (`n` chips of a 16px glyph in `px-3`,
|
|
335
|
+
* `gap-1` apart in a `p-1` track) and the measurement agrees with it to the
|
|
336
|
+
* pixel, which is what says the method is sound; `tests/panel-layout.test.ts`
|
|
337
|
+
* (d) pins the arithmetic.
|
|
437
338
|
*
|
|
438
339
|
* The drag band gives containers from 181px (a 220px panel, the minimum) to
|
|
439
|
-
* 281px (a 320px panel, the default
|
|
440
|
-
* minus the 11px reserved scrollbar gutter). Read against it:
|
|
340
|
+
* 281px (a 320px panel, the default). Read against it:
|
|
441
341
|
*
|
|
442
|
-
* · TWO tabs
|
|
443
|
-
*
|
|
444
|
-
* · THREE tabs carry their words down to
|
|
445
|
-
*
|
|
446
|
-
*
|
|
447
|
-
*
|
|
342
|
+
* · TWO tabs now need 197px for both words, so at the narrow end of the band
|
|
343
|
+
* the tab not being read loses its word (it never did on the old chip).
|
|
344
|
+
* · THREE tabs carry their words down to 280px — the default panel, just —
|
|
345
|
+
* then one word down to 195px, then glyphs.
|
|
346
|
+
* · FOUR tabs need 375px, which the band never reaches: glyph-plus-one-word
|
|
347
|
+
* down to 239px, then glyphs, which fit the 181px floor.
|
|
448
348
|
*
|
|
449
349
|
* WHY THE RULES ARE KEYED ON THE TAB COUNT. With natural widths, whether the
|
|
450
350
|
* words fit is a question about the SUM of the labels, so the answer moves with
|
|
451
351
|
* how many buckets a section has. A width-only rule would have to answer for
|
|
452
352
|
* the worst case — four tabs — and would then strip the word off a TWO-tab
|
|
453
|
-
* strip at the default panel width.
|
|
454
|
-
* remove, so it cannot be the fix for it.
|
|
353
|
+
* strip at the default panel width.
|
|
455
354
|
*
|
|
456
355
|
* Only `.has-icon` is ever touched. A tab with no glyph has nothing to fall
|
|
457
356
|
* back to, so it keeps its word at every width — a button holding neither word
|
|
458
|
-
* nor glyph is not a narrower segment, it is an empty one.
|
|
357
|
+
* nor glyph is not a narrower segment, it is an empty one. A word is clipped,
|
|
358
|
+
* never `display: none`: the shell's tab takes its name from it. */
|
|
359
|
+
|
|
360
|
+
/* TWO OR MORE, under 197px: the word on the tab he is not reading goes. */
|
|
361
|
+
@container (width < 197px) {
|
|
362
|
+
.es-tabs:has([role='tab']:nth-child(2)) [role='tab']:not([aria-selected='true']) .es-tab-word.has-icon { position: absolute; width: 1px; height: 1px; padding: 0; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; }
|
|
363
|
+
}
|
|
459
364
|
|
|
460
|
-
/* THREE OR MORE, under
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
@container (width < 240px) {
|
|
464
|
-
.es-tabs:has(.es-tab:nth-child(3)) .es-tab.has-icon:not(.is-active) .es-tab-word { display: none; }
|
|
365
|
+
/* THREE OR MORE, under 280px. */
|
|
366
|
+
@container (width < 280px) {
|
|
367
|
+
.es-tabs:has([role='tab']:nth-child(3)) [role='tab']:not([aria-selected='true']) .es-tab-word.has-icon { position: absolute; width: 1px; height: 1px; padding: 0; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; }
|
|
465
368
|
}
|
|
466
369
|
|
|
467
|
-
/* FOUR OR MORE: a fourth label is another
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
.es-tabs:has(.es-tab:nth-child(4)) .es-tab.has-icon:not(.is-active) .es-tab-word { display: none; }
|
|
370
|
+
/* FOUR OR MORE, under 375px: a fourth label is another ~95px of track. */
|
|
371
|
+
@container (width < 375px) {
|
|
372
|
+
.es-tabs:has([role='tab']:nth-child(4)) [role='tab']:not([aria-selected='true']) .es-tab-word.has-icon { position: absolute; width: 1px; height: 1px; padding: 0; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; }
|
|
471
373
|
}
|
|
472
374
|
|
|
473
|
-
/*
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
375
|
+
/* THREE OR MORE at the narrow end, under 195px: the last word goes as well. */
|
|
376
|
+
@container (width < 195px) {
|
|
377
|
+
.es-tabs:has([role='tab']:nth-child(3)) [role='tab'][aria-selected='true'] .es-tab-word.has-icon { position: absolute; width: 1px; height: 1px; padding: 0; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; }
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
/* FOUR OR MORE, under 239px: the strip is four glyphs on a track. It reads —
|
|
381
|
+
the track and the chips are what say "these are buttons". */
|
|
382
|
+
@container (width < 239px) {
|
|
383
|
+
.es-tabs:has([role='tab']:nth-child(4)) [role='tab'][aria-selected='true'] .es-tab-word.has-icon { position: absolute; width: 1px; height: 1px; padding: 0; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; }
|
|
479
384
|
}
|
|
480
385
|
|
|
481
386
|
/* ── the segmented control: the same chip, a different promise ─────────────── */
|
|
@@ -571,16 +476,18 @@
|
|
|
571
476
|
whether it was given a `fit`, so the arithmetic below is reached by a strip
|
|
572
477
|
that has one and never by a strip that does not. What an unmeasured strip
|
|
573
478
|
does is the strip's own call: a segmented control keeps every word (there is
|
|
574
|
-
nowhere honest to drop one)
|
|
479
|
+
nowhere honest to drop one). */
|
|
575
480
|
.es-segs {
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
--es-fit-
|
|
481
|
+
/* The shell's `sm` Segmented, read off Tailwind's spacing step (the host's
|
|
482
|
+
`--spacing`): a chip pads px-2.5, the track p-1, its chips gap-1, a glyph
|
|
483
|
+
and its word gap-1.5. The word-fit arithmetic must add what is drawn. */
|
|
484
|
+
--es-fit-chip: calc(2 * 2.5 * var(--spacing) + var(--es-chip-icon));
|
|
485
|
+
--es-fit-word-gap: calc(1.5 * var(--spacing));
|
|
486
|
+
--es-fit-gap: var(--spacing);
|
|
487
|
+
--es-fit-ends: var(--spacing);
|
|
580
488
|
}
|
|
581
489
|
|
|
582
|
-
.es-segs.is-measured
|
|
583
|
-
.es-rail-row.is-measured {
|
|
490
|
+
.es-segs.is-measured {
|
|
584
491
|
--es-fit-strip: calc(2 * var(--es-fit-ends) + (var(--es-fit-count) - 1) * var(--es-fit-gap));
|
|
585
492
|
|
|
586
493
|
--es-fit-words-from: calc(
|
|
@@ -604,8 +511,7 @@
|
|
|
604
511
|
|
|
605
512
|
`100cqi` stays a unit until a word USES it, so it is the width of the
|
|
606
513
|
container around that word — the strip's own `-fit` wrapper. */
|
|
607
|
-
.es-segs
|
|
608
|
-
.es-rail-row.is-measured {
|
|
514
|
+
.es-segs {
|
|
609
515
|
--es-fit-room-all: calc((100cqi - var(--es-fit-words-from, 0px)) * 10000);
|
|
610
516
|
--es-fit-room-chosen: calc((100cqi - var(--es-fit-chosen-from, 0px)) * 10000);
|
|
611
517
|
}
|
|
@@ -614,8 +520,7 @@
|
|
|
614
520
|
switch has to be a length for `max-width` to clamp it. Nothing is lost by
|
|
615
521
|
it: the label is on the button as `title` and `aria-label` in every state, so
|
|
616
522
|
the name a screen reader reads never depended on the span being drawn. */
|
|
617
|
-
.es-seg-word
|
|
618
|
-
.es-rail-row.is-measured .es-rail-word {
|
|
523
|
+
.es-seg-word {
|
|
619
524
|
overflow: hidden;
|
|
620
525
|
max-width: var(--es-fit-room-all);
|
|
621
526
|
}
|
|
@@ -625,8 +530,7 @@
|
|
|
625
530
|
and the chips are what say "these are buttons"; a track that also still says
|
|
626
531
|
which value is set reads better, and it costs one word. On the rail "chosen"
|
|
627
532
|
is the button whose panel is open. */
|
|
628
|
-
.es-
|
|
629
|
-
.es-rail-row.is-measured .es-rail-btn.is-active .es-rail-word { max-width: var(--es-fit-room-chosen); }
|
|
533
|
+
.es-segs > button[aria-pressed='true'] .es-seg-word { max-width: var(--es-fit-room-chosen); }
|
|
630
534
|
|
|
631
535
|
/* THE GAP GOES WITH THE WORD IT SEPARATES. The chip's `gap` is a flex gap, and
|
|
632
536
|
a flex gap does not care that the word is now zero wide — it would leave 6px
|
|
@@ -636,66 +540,21 @@
|
|
|
636
540
|
untouched and keeps the flex gap, which is right there: its word is
|
|
637
541
|
`display: none`, so the gap goes with the element, and a tab also has a badge
|
|
638
542
|
on the other side of that word for the gap to separate. */
|
|
639
|
-
.es-
|
|
640
|
-
.es-
|
|
641
|
-
.es-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
`disabled` rather than as a fade a host paints over them. A faded parent
|
|
655
|
-
leaves every segment focusable and announcing as ENABLED, which is the defect
|
|
656
|
-
the 2026-08-06 audit and card 66034 closed; it is also why the prop had to
|
|
657
|
-
come here at all, since nothing outside this component owns those buttons.
|
|
658
|
-
|
|
659
|
-
`--es-disabled` is the shell's existing token for exactly this state, already
|
|
660
|
-
worn by the drill row's quiet glyph. Nothing here is a literal, and nothing
|
|
661
|
-
here removes anything: the track keeps its border and the pressed chip keeps
|
|
662
|
-
its lift, so the strip still READS as a strip with an answer in it. */
|
|
663
|
-
.es-seg:disabled { cursor: not-allowed; color: var(--es-disabled); }
|
|
664
|
-
|
|
665
|
-
/* The chosen one stays the loudest thing on the track. It is still the
|
|
666
|
-
merchant's answer — a pressed chip faded to the same grey as the ones beside
|
|
667
|
-
it stops saying which answer that was.
|
|
668
|
-
|
|
669
|
-
GREY, NOT BLUE, AND NOT FADED (2026-10-06). The live chip is the accent now,
|
|
670
|
-
and blue is the colour that says "this is in force" — so an answer out of
|
|
671
|
-
force drops to `--es-muted`. The arithmetic decided the rest: the blue chip
|
|
672
|
-
faded to half over the panel is 2.04:1 against its faded track, under the
|
|
673
|
-
3:1 a state cue needs. `--es-muted` with no fade is 4.39:1 on the track and
|
|
674
|
-
its white word 4.83:1. That is why the whole strip no longer fades (below):
|
|
675
|
-
any fade took this chip under 3:1 again.
|
|
676
|
-
|
|
677
|
-
⚠️ THE WORD COLOUR BELONGS TO THE CHIP'S GROUND, NOT TO THE TRACK (card
|
|
678
|
-
66302): a label written for one ground went unreadable when the ground moved
|
|
679
|
-
under it. So the ground and the word are declared together, here. */
|
|
680
|
-
.es-seg:disabled.is-active {
|
|
681
|
-
background: var(--es-muted);
|
|
682
|
-
color: var(--es-on-accent);
|
|
683
|
-
}
|
|
684
|
-
|
|
685
|
-
/* And none of them answers the pointer any more: the hover rule above would
|
|
686
|
-
otherwise keep lighting a segment that cannot be chosen. */
|
|
687
|
-
.es-seg:disabled:hover:not(.is-active) { color: var(--es-disabled); }
|
|
688
|
-
|
|
689
|
-
/* THE WHOLE CONTROL, when every option went at once. Every button is then
|
|
690
|
-
`:disabled`, so the quiet words are already `--es-disabled` and the chosen
|
|
691
|
-
chip is already grey — the strip says "out of force" through them.
|
|
692
|
-
|
|
693
|
-
IT USED TO FADE TO 0.5, and that fade is gone on purpose (2026-10-06). An
|
|
694
|
-
opacity on the track composites the chosen chip with it, and no chip colour
|
|
695
|
-
the ruling allows survives that above 3:1: blue faded is 2.04:1, the grey
|
|
696
|
-
above faded is about 2:1. The chip is the one thing a disabled strip must
|
|
697
|
-
still say clearly, so the fade went rather than the chip. */
|
|
698
|
-
.es-segs[data-disabled] { cursor: not-allowed; }
|
|
543
|
+
.es-segs.is-measured > button { gap: 0; }
|
|
544
|
+
.es-segs.is-measured > button:has(.es-seg-icon) .es-seg-word { margin-left: clamp(0px, var(--es-fit-room-all), var(--es-fit-word-gap)); }
|
|
545
|
+
.es-segs.is-measured > button[aria-pressed='true']:has(.es-seg-icon) .es-seg-word { margin-left: clamp(0px, var(--es-fit-room-chosen), var(--es-fit-word-gap)); }
|
|
546
|
+
|
|
547
|
+
/* ── the chosen segment ──────────────────────────────────────────────────────
|
|
548
|
+
The shell's Segmented draws it (efficient-shell 4.164.0): an inset 1px
|
|
549
|
+
`--ink-faint` ring on the white chip, 3:1 and more on the track in both
|
|
550
|
+
themes (WCAG 1.4.11; `tests/panel-chip-contrast.test.ts`). It supersedes the
|
|
551
|
+
accent fill this sheet painted as a SHELL GAP (Lewis, 2026-10-06): under
|
|
552
|
+
harness UI-24 the shell's design wins, and the gap is closed. */
|
|
553
|
+
|
|
554
|
+
/* ── a segment out of force (card 66303) ─────────────────────────────────────
|
|
555
|
+
The chip out of force is the shell's (its own `disabled`, dimmed); the whole
|
|
556
|
+
control out of force says so on its fit box, for the pointer. */
|
|
557
|
+
.es-segs-fit[data-disabled] { cursor: not-allowed; }
|
|
699
558
|
|
|
700
559
|
/* ── a group: the soft pill row that folds ─────────────────────────────────── */
|
|
701
560
|
|
|
@@ -734,7 +593,10 @@
|
|
|
734
593
|
color: var(--es-text);
|
|
735
594
|
background: var(--es-hover);
|
|
736
595
|
border-radius: var(--es-row-radius);
|
|
737
|
-
|
|
596
|
+
/* Its words start where a tree row's do: the shell's `TreeRow` pads `px-2`,
|
|
597
|
+
two of the shell's `--spacing` steps, and so does this (the Layers panel
|
|
598
|
+
drew a heading at 12px over rows at 8px: two text columns). */
|
|
599
|
+
padding: 8px calc(2 * var(--spacing));
|
|
738
600
|
/* A heading needs more air above it than the settings it heads have between
|
|
739
601
|
them (38px), or it reads as one more row. Floored on `--es-row-gap` so it
|
|
740
602
|
rises with the rest of the panel (card 66203). */
|
|
@@ -889,7 +751,8 @@ button.es-group-head:hover { background: var(--es-chip-bg); }
|
|
|
889
751
|
the part inside a component of its own (it reads a live value through a
|
|
890
752
|
hook) is invisible to SettingsField, and without this its label stayed grey
|
|
891
753
|
beside a Reset — measured on the website's section Width row. */
|
|
892
|
-
.es-field:has(> .es-field-head > .es-default-reset.is-changed) > .es-field-head .es-field-label
|
|
754
|
+
.es-field:has(> .es-field-head > .es-default-reset.is-changed) > .es-field-head .es-field-label,
|
|
755
|
+
.es-field:has(> .es-field-head > span > .es-default-reset.is-changed) > .es-field-head .es-field-label {
|
|
893
756
|
color: var(--es-accent-text);
|
|
894
757
|
}
|
|
895
758
|
|
|
@@ -950,7 +813,7 @@ button.es-group-head:hover { background: var(--es-chip-bg); }
|
|
|
950
813
|
margin: 0;
|
|
951
814
|
padding: 0;
|
|
952
815
|
border: 0;
|
|
953
|
-
border-radius:
|
|
816
|
+
border-radius: var(--radius-md);
|
|
954
817
|
background: transparent;
|
|
955
818
|
color: var(--es-muted);
|
|
956
819
|
}
|
|
@@ -983,14 +846,18 @@ button.es-group-head:hover { background: var(--es-chip-bg); }
|
|
|
983
846
|
outline-offset: 1px;
|
|
984
847
|
}
|
|
985
848
|
|
|
986
|
-
|
|
849
|
+
/* A pressable part explains itself in the shell's Tooltip, whose wrapper is
|
|
850
|
+
then the head's item: the rules below place either. */
|
|
851
|
+
.es-field-name + .es-default-reset,
|
|
852
|
+
.es-field-name + span:has(> .es-default-reset) { margin-left: auto; }
|
|
987
853
|
|
|
988
854
|
/* THE GREY VALUE IS THE "DEFAULT" WORD NOW (option E): a row whose slot is
|
|
989
855
|
empty draws its value grey, and the value takes the text colour once the
|
|
990
856
|
setting is changed. Scoped to rows that carry the part, so a value on a row
|
|
991
857
|
with nothing to reset keeps its colour. */
|
|
992
858
|
.es-field:has(> .es-field-head > span.es-default-reset) .es-field-value { color: var(--es-muted); }
|
|
993
|
-
.es-field-value + .es-default-reset
|
|
859
|
+
.es-field-value + .es-default-reset,
|
|
860
|
+
.es-field-value + span:has(> .es-default-reset) { margin-left: -4px; }
|
|
994
861
|
|
|
995
862
|
/* A boolean row is ONE line (card 8801, Option A). The switch rides the label
|
|
996
863
|
line in the `action` slot, so there is no control row under it — and with no
|
|
@@ -1007,73 +874,10 @@ button.es-group-head:hover { background: var(--es-chip-bg); }
|
|
|
1007
874
|
.es-field.is-inline { margin-top: max(var(--es-row-gap), var(--es-rhythm-between-tight)); }
|
|
1008
875
|
.es-field.is-inline .es-field-head { margin-bottom: 0; }
|
|
1009
876
|
|
|
1010
|
-
/* ── the switch: one yes-or-no shape, both editors
|
|
1011
|
-
|
|
1012
|
-
.es-switch
|
|
1013
|
-
|
|
1014
|
-
-webkit-appearance: none;
|
|
1015
|
-
box-sizing: border-box;
|
|
1016
|
-
margin: 0;
|
|
1017
|
-
padding: 0;
|
|
1018
|
-
border: 0;
|
|
1019
|
-
flex: none;
|
|
1020
|
-
position: relative;
|
|
1021
|
-
width: 34px;
|
|
1022
|
-
height: 20px;
|
|
1023
|
-
border-radius: 999px;
|
|
1024
|
-
background: var(--es-toggle-off);
|
|
1025
|
-
cursor: pointer;
|
|
1026
|
-
transition: background 0.15s ease;
|
|
1027
|
-
}
|
|
1028
|
-
|
|
1029
|
-
/* The knob. `box-sizing` is load-bearing, not tidiness: the locked knob below
|
|
1030
|
-
takes a 1px border, and under content-box that border would grow it to 18px
|
|
1031
|
-
and eat the 2px gap the whole shape is built on. */
|
|
1032
|
-
.es-switch::after {
|
|
1033
|
-
content: '';
|
|
1034
|
-
box-sizing: border-box;
|
|
1035
|
-
position: absolute;
|
|
1036
|
-
top: 2px;
|
|
1037
|
-
left: 2px;
|
|
1038
|
-
width: 16px;
|
|
1039
|
-
height: 16px;
|
|
1040
|
-
border-radius: 50%;
|
|
1041
|
-
background: var(--es-knob);
|
|
1042
|
-
/* The knob has to lift off the track, and the shadow is a plain token on
|
|
1043
|
-
purpose. A softer mix reads slightly better, but if color-mix() ever fails
|
|
1044
|
-
to parse the WHOLE declaration drops and the knob goes flat with no
|
|
1045
|
-
warning — not a risk worth carrying in a leaf both editors render
|
|
1046
|
-
thousands of times for the sake of a 1px shadow. */
|
|
1047
|
-
box-shadow: 0 1px 2px var(--es-input-border);
|
|
1048
|
-
transition: transform 0.15s cubic-bezier(0.2, 0.8, 0.3, 1);
|
|
1049
|
-
}
|
|
1050
|
-
|
|
1051
|
-
/* On is the accent — the same blue as an active tab, so the panel keeps ONE
|
|
1052
|
-
accent. The travel is 14px: 34 track − 16 knob − 2 gap − 2 gap. */
|
|
1053
|
-
.es-switch[aria-checked='true'] { background: var(--es-accent); }
|
|
1054
|
-
.es-switch[aria-checked='true']::after { transform: translateX(14px); }
|
|
1055
|
-
|
|
1056
|
-
.es-switch:hover:not(:disabled) { filter: brightness(0.96); }
|
|
1057
|
-
|
|
1058
|
-
.es-switch:focus-visible {
|
|
1059
|
-
outline: none;
|
|
1060
|
-
box-shadow: 0 0 0 3px var(--es-accent-ring);
|
|
1061
|
-
}
|
|
1062
|
-
|
|
1063
|
-
/* Held down: the knob stretches the way a physical one would.
|
|
1064
|
-
|
|
1065
|
-
ON, it must stretch LEFT. The knob is 16px at translateX(14px), so growing it
|
|
1066
|
-
to 19px in place would put its right edge at 2 + 14 + 19 = 35px inside a 34px
|
|
1067
|
-
track and poke it past the round end. Pulling the travel back to 11px lands
|
|
1068
|
-
the right edge on 32 and keeps the 2px gap on both sides. */
|
|
1069
|
-
.es-switch:active:not(:disabled)::after { width: 19px; }
|
|
1070
|
-
.es-switch[aria-checked='true']:active:not(:disabled)::after { transform: translateX(11px); }
|
|
1071
|
-
|
|
1072
|
-
/* Locked. Still readable as on or off, but flat and grey, and the knob trades
|
|
1073
|
-
its shadow for a hairline so it does not look pressable. */
|
|
1074
|
-
.es-switch:disabled { background: var(--es-divider); cursor: not-allowed; }
|
|
1075
|
-
.es-switch:disabled[aria-checked='true'] { background: var(--es-input-border); }
|
|
1076
|
-
.es-switch:disabled::after { box-shadow: none; border: 1px solid var(--es-divider); }
|
|
877
|
+
/* ── the switch: one yes-or-no shape, both editors ─────────────────────────
|
|
878
|
+
The shell's `Switch` since efficient-easel issue 199 step 4 (harness UI-24): its
|
|
879
|
+
track, knob, on colour, focus ring and locked look are the kit's. `.es-switch`
|
|
880
|
+
stays on its wrapper as a name a host's layout may hold. */
|
|
1077
881
|
|
|
1078
882
|
/* ── a drill-in row: name, preview, chevron (card 8819) ────────────────────── */
|
|
1079
883
|
|
|
@@ -1215,14 +1019,10 @@ button.es-group-head:hover { background: var(--es-chip-bg); }
|
|
|
1215
1019
|
.es-drill-chev::before { content: '\203A'; }
|
|
1216
1020
|
|
|
1217
1021
|
@media (prefers-reduced-motion: reduce) {
|
|
1218
|
-
.es-tab,
|
|
1219
|
-
.es-seg,
|
|
1220
1022
|
/* `button.` and not `.es-group-head`: the transition it cancels is declared
|
|
1221
1023
|
on `button.es-group-head`, and a plainer selector would lose to it. */
|
|
1222
1024
|
button.es-group-head,
|
|
1223
|
-
.es-drill
|
|
1224
|
-
.es-switch,
|
|
1225
|
-
.es-switch::after { transition: none; }
|
|
1025
|
+
.es-drill { transition: none; }
|
|
1226
1026
|
}
|
|
1227
1027
|
|
|
1228
1028
|
/* ── SettingsPlacement — the 3×3 position grid ─────────────────────────────
|
|
@@ -1246,23 +1046,20 @@ button.es-group-head:hover { background: var(--es-chip-bg); }
|
|
|
1246
1046
|
padding: 4px;
|
|
1247
1047
|
background: var(--es-track-bg);
|
|
1248
1048
|
border: 1px solid var(--es-input-border);
|
|
1249
|
-
border-radius:
|
|
1049
|
+
border-radius: var(--radius-lg);
|
|
1250
1050
|
}
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
padding: 0;
|
|
1051
|
+
/* Each cell is the shell's IconButton in its Tooltip's wrapper: the wrapper is
|
|
1052
|
+
the grid's item, and the button fills it. The button's own look (hover,
|
|
1053
|
+
focus ring, disabled) is the shell's. */
|
|
1054
|
+
.es-placement > span,
|
|
1055
|
+
.es-corners > span {
|
|
1257
1056
|
display: flex;
|
|
1258
|
-
align-items: center;
|
|
1259
|
-
justify-content: center;
|
|
1260
|
-
transition: background 0.12s;
|
|
1261
1057
|
}
|
|
1262
|
-
.es-placement-cell
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1058
|
+
.es-placement-cell,
|
|
1059
|
+
.es-corners-cell {
|
|
1060
|
+
width: 100%;
|
|
1061
|
+
height: 100%;
|
|
1062
|
+
}
|
|
1266
1063
|
.es-placement-dot {
|
|
1267
1064
|
width: 7px;
|
|
1268
1065
|
height: 7px;
|
|
@@ -1294,9 +1091,11 @@ button.es-group-head:hover { background: var(--es-chip-bg); }
|
|
|
1294
1091
|
/* A position the caller has closed. It keeps its square — nine that became
|
|
1295
1092
|
eight would read as a broken control — and stops answering the pointer; the
|
|
1296
1093
|
reason is on the button's `title`. */
|
|
1297
|
-
.es-placement-cell.is-off { cursor: default; }
|
|
1298
|
-
.es-placement-cell.is-off:hover { background: transparent; }
|
|
1299
1094
|
.es-placement-cell.is-off .es-placement-dot { opacity: 0.22; }
|
|
1095
|
+
/* Off with a reason is `aria-disabled` (the keyboard reaches it and hears why),
|
|
1096
|
+
so the pointer is told here what `:disabled` used to tell it. */
|
|
1097
|
+
.es-placement-cell.is-off,
|
|
1098
|
+
.es-corners-cell.is-off { cursor: not-allowed; }
|
|
1300
1099
|
/* A cell that draws a GLYPH instead of its dot — the Scrolling Bar's
|
|
1301
1100
|
`Scroll direction`, where four live cells mean a direction of travel and a
|
|
1302
1101
|
dot cannot say which way. It follows the cell through every state the dot
|
|
@@ -1326,12 +1125,7 @@ button.es-group-head:hover { background: var(--es-chip-bg); }
|
|
|
1326
1125
|
}
|
|
1327
1126
|
.es-placement-cell.is-off .es-placement-icon { opacity: 0.22; }
|
|
1328
1127
|
/* Keyboard focus is visible, like every other control in this panel. */
|
|
1329
|
-
.es-placement-cell:focus-visible {
|
|
1330
|
-
outline: 2px solid var(--es-accent-ring);
|
|
1331
|
-
outline-offset: 1px;
|
|
1332
|
-
}
|
|
1333
1128
|
@media (prefers-reduced-motion: reduce) {
|
|
1334
|
-
.es-placement-cell,
|
|
1335
1129
|
.es-placement-icon,
|
|
1336
1130
|
.es-placement-dot { transition: none; }
|
|
1337
1131
|
}
|
|
@@ -1353,7 +1147,7 @@ button.es-group-head:hover { background: var(--es-chip-bg); }
|
|
|
1353
1147
|
height: 96px;
|
|
1354
1148
|
padding: 4px;
|
|
1355
1149
|
background: var(--es-track-bg);
|
|
1356
|
-
border-radius:
|
|
1150
|
+
border-radius: var(--radius-lg);
|
|
1357
1151
|
}
|
|
1358
1152
|
/* The BOX the four corners belong to. Inset so the cells sit ON its corners
|
|
1359
1153
|
rather than beside it, dashed so it reads as the outline of the thing being
|
|
@@ -1366,23 +1160,6 @@ button.es-group-head:hover { background: var(--es-chip-bg); }
|
|
|
1366
1160
|
opacity: 0.45;
|
|
1367
1161
|
pointer-events: none;
|
|
1368
1162
|
}
|
|
1369
|
-
.es-corners-cell {
|
|
1370
|
-
position: relative;
|
|
1371
|
-
border: none;
|
|
1372
|
-
background: transparent;
|
|
1373
|
-
border-radius: 6px;
|
|
1374
|
-
cursor: pointer;
|
|
1375
|
-
padding: 0;
|
|
1376
|
-
display: flex;
|
|
1377
|
-
align-items: center;
|
|
1378
|
-
justify-content: center;
|
|
1379
|
-
}
|
|
1380
|
-
.es-corners-cell:hover { background: var(--es-hover); }
|
|
1381
|
-
/* THE MARK IS THE ANSWER, drawn rather than labelled. Each cell holds a square
|
|
1382
|
-
whose MATCHING corner is rounded when the corner is on and square when it is
|
|
1383
|
-
off, so the state is carried by SHAPE as well as by fill — which is what
|
|
1384
|
-
keeps it readable for someone who cannot separate the accent from the
|
|
1385
|
-
ground. The per-corner radius is set by the four rules below. */
|
|
1386
1163
|
.es-corners-mark {
|
|
1387
1164
|
width: 18px;
|
|
1388
1165
|
height: 18px;
|
|
@@ -1400,21 +1177,15 @@ button.es-group-head:hover { background: var(--es-chip-bg); }
|
|
|
1400
1177
|
background: var(--es-accent);
|
|
1401
1178
|
opacity: 1;
|
|
1402
1179
|
}
|
|
1403
|
-
.es-corners-tl.is-on .es-corners-mark { border-radius:
|
|
1404
|
-
.es-corners-tr.is-on .es-corners-mark { border-radius: 0
|
|
1405
|
-
.es-corners-br.is-on .es-corners-mark { border-radius: 0 0
|
|
1406
|
-
.es-corners-bl.is-on .es-corners-mark { border-radius: 0 0 0
|
|
1180
|
+
.es-corners-tl.is-on .es-corners-mark { border-radius: var(--radius-lg) 0 0 0; }
|
|
1181
|
+
.es-corners-tr.is-on .es-corners-mark { border-radius: 0 var(--radius-lg) 0 0; }
|
|
1182
|
+
.es-corners-br.is-on .es-corners-mark { border-radius: 0 0 var(--radius-lg) 0; }
|
|
1183
|
+
.es-corners-bl.is-on .es-corners-mark { border-radius: 0 0 0 var(--radius-lg); }
|
|
1407
1184
|
/* A corner the caller has closed. It keeps its cell — four that became three
|
|
1408
1185
|
would read as a broken control — and stops answering the pointer; the reason
|
|
1409
1186
|
is on the button's `title`. */
|
|
1410
|
-
.es-corners-cell.is-off { cursor: default; }
|
|
1411
|
-
.es-corners-cell.is-off:hover { background: transparent; }
|
|
1412
1187
|
.es-corners-cell.is-off .es-corners-mark { opacity: 0.22; }
|
|
1413
1188
|
/* Keyboard focus is visible, like every other control in this panel. */
|
|
1414
|
-
.es-corners-cell:focus-visible {
|
|
1415
|
-
outline: 2px solid var(--es-accent-ring);
|
|
1416
|
-
outline-offset: 1px;
|
|
1417
|
-
}
|
|
1418
1189
|
@media (prefers-reduced-motion: reduce) {
|
|
1419
1190
|
.es-corners-mark { transition: none; }
|
|
1420
1191
|
}
|