Shell: tile settings are switches, and the popover ends where it should

Every boolean (and `switch`) in a tile's gear popover renders as the same
toggle the admin page uses for its module switches — 34×19 track, accent
when on, border track and muted knob when off — with the label on the
left and the switch on the right, so the two surfaces read the same. On
means shown or enabled, off hidden or disabled; the label names the thing.
A real checkbox still sits under the track: Tab reaches it, Space flips it
(with a focus ring), clicking the label text toggles it, and the existing
live-commit wiring reads it unchanged. `switch`-typed instance fields,
which previously fell through to a text input, take the same path.

The popover's bottom was 20 px of nothing: the last field kept its 10 px
margin under the popover's own 10 px padding once the Apply/Cancel row
went. The last child now drops its margin, top and bottom match (11 px
each, border included), and in column layouts the grid gap alone spaces
the rows.

Class names, for the admin page to mirror: `.field.check` (the row),
`.check-label`, `.switch`, `.switch .track`.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Waters Church
2026-09-13 13:03:58 -04:00
co-authored by Claude Fable 5.1
parent c46f98818e
commit 0fd052e49d
2 changed files with 72 additions and 7 deletions
+57 -4
View File
@@ -589,12 +589,15 @@ body.fullscreen #grid { padding-bottom: 0; }
.tile-settings details.field-group[open] > summary.field-group-title::before { transform: rotate(90deg); }
.tile-settings details.field-group[open] > summary.field-group-title { margin-bottom: 8px; }
.tile-settings .field-group .field:last-child { margin-bottom: 0; }
/* Settings apply as they change, so nothing sits under the last field — its
bottom margin would only pad the popover's own padding a second time. */
.tile-settings > :last-child { margin-bottom: 0; }
.tile-settings .field-group-body.is-columns {
display: grid;
grid-template-columns: repeat(var(--cols, 2), minmax(0, 1fr));
gap: 3px 14px;
gap: 6px 14px; /* the grid gap is the whole row spacing: no per-field margin */
}
.tile-settings .field-group-body.is-columns .field { margin-bottom: 3px; }
.tile-settings .field-group-body.is-columns .field { margin-bottom: 0; }
.tile-settings .field-group-body.is-columns .field-group-help { grid-column: 1 / -1; }
@container (max-width: 420px) {
.tile-settings .field-group-body.is-columns { grid-template-columns: 1fr 1fr; }
@@ -636,14 +639,64 @@ body.fullscreen #grid { padding-bottom: 0; }
outline: none;
border-color: var(--accent);
}
/* boolean / switch settings: label on the left, switch on the right (help,
when there is any, wraps under both) */
.field.check {
flex-direction: row;
flex-wrap: wrap;
align-items: center;
gap: 8px;
justify-content: space-between;
gap: 4px 10px;
color: var(--text);
cursor: pointer;
}
.field.check input { width: 15px; height: 15px; accent-color: var(--accent); }
.field.check .check-label { flex: 1 1 0; min-width: 0; }
.field.check .field-help { flex-basis: 100%; }
/* The switch: the admin page's module switch, same track, knob and colours,
so a toggle reads the same on both surfaces. On = shown/enabled (accent),
off = hidden/disabled (border track, muted knob). The real checkbox lies
invisibly over the track — still focusable, so Tab reaches it and Space
flips it — and the label around it makes the text clickable too. */
.switch {
position: relative;
display: inline-flex;
align-items: center;
flex: none;
cursor: pointer;
}
.switch input {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
margin: 0;
opacity: 0;
pointer-events: none;
}
.switch .track {
display: block;
position: relative;
width: 34px;
height: 19px;
border-radius: 999px;
background: var(--border);
transition: background 0.2s;
}
.switch .track::after {
content: "";
position: absolute;
top: 2px;
left: 2px;
width: 15px;
height: 15px;
border-radius: 50%;
background: var(--muted);
transition: transform 0.2s, background 0.2s;
}
.switch input:checked + .track { background: var(--accent); }
.switch input:checked + .track::after { transform: translateX(15px); background: var(--bg); }
.switch input:focus-visible + .track { outline: 2px solid var(--accent); outline-offset: 2px; }
/* color fields: label on the left, swatch on the right */
.field.color-field {