Fix the settings tabs, and space a form from what follows it
**The Audio tab rendered nothing.** The tabs are radios plus sibling selectors, and the CSS named every tab twice -- once to highlight its label, once to show its panel. A tab added without also adding those two rules gets a label that selects nothing, which is not something anyone catches in review; it looks like a blank page. Replaced with rules that derive what they can. The active label is `input:checked + .tabs__tab`, which needs to know nothing at all. The panel is matched by position -- CSS cannot compare a radio's id with a panel's data-tab -- so the Nth radio shows the Nth panel. Both lists render in the same order and a conditional tab drops out of both at once, so they cannot drift. There is a test asserting the two orders match, including with Audio absent. **A card following a form sat flush against Save.** The "Try it" panel on the search page read as another field of the settings form. The gap belongs to the form rather than to its action row: the action row is always its form's last child, so a bottom margin there has nothing to push away from. Adds `.form-actions` and a bottom margin on a form that is a direct child of an admin page. Also says plainly in the dictation settings that a server hosting one model ignores the model field, so `whisper-1` there is a label rather than a selection. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -72,23 +72,64 @@
|
||||
|
||||
.tabs__panel { display: none; }
|
||||
|
||||
/* Each radio activates its own label and its own panel. Written out because
|
||||
CSS has no way to derive one from the other. */
|
||||
#tab-account:checked ~ label[for="tab-account"],
|
||||
#tab-models:checked ~ label[for="tab-models"],
|
||||
#tab-appearance:checked ~ label[for="tab-appearance"],
|
||||
#tab-security:checked ~ label[for="tab-security"] {
|
||||
/* The active tab's label. Each radio is immediately followed by its own label,
|
||||
so this needs to know nothing about how many tabs there are or what they are
|
||||
called. */
|
||||
.tabs__bar input:checked + .tabs__tab {
|
||||
color: var(--ink);
|
||||
border-bottom-color: var(--accent);
|
||||
}
|
||||
.tabs__bar:has(#tab-account:checked) ~ .tabs__body [data-tab="tab-account"],
|
||||
.tabs__bar:has(#tab-models:checked) ~ .tabs__body [data-tab="tab-models"],
|
||||
.tabs__bar:has(#tab-appearance:checked) ~ .tabs__body [data-tab="tab-appearance"],
|
||||
.tabs__bar:has(#tab-security:checked) ~ .tabs__body [data-tab="tab-security"] {
|
||||
|
||||
/*
|
||||
The active panel, matched by position.
|
||||
|
||||
CSS cannot compare a radio's id with a panel's data-tab, so this used to name
|
||||
every tab twice -- and a tab added without also adding its two rules here
|
||||
rendered a label that selected nothing. That is not a failure anyone spots in
|
||||
review; it looks like a blank page.
|
||||
|
||||
Position is derivable, so it is used instead: the Nth radio shows the Nth
|
||||
panel. Both lists are rendered in the same order, and a conditional tab drops
|
||||
out of both at once, so they cannot drift apart. :nth-of-type counts by
|
||||
element name, which is why the panels are <section> and the alerts above them
|
||||
are <div> -- the alerts are not counted.
|
||||
|
||||
Enumerated to eight, comfortably more than exist. A ninth tab needs one line.
|
||||
*/
|
||||
.tabs__bar:has(input:nth-of-type(1):checked) ~ .tabs__body .tabs__panel:nth-of-type(1),
|
||||
.tabs__bar:has(input:nth-of-type(2):checked) ~ .tabs__body .tabs__panel:nth-of-type(2),
|
||||
.tabs__bar:has(input:nth-of-type(3):checked) ~ .tabs__body .tabs__panel:nth-of-type(3),
|
||||
.tabs__bar:has(input:nth-of-type(4):checked) ~ .tabs__body .tabs__panel:nth-of-type(4),
|
||||
.tabs__bar:has(input:nth-of-type(5):checked) ~ .tabs__body .tabs__panel:nth-of-type(5),
|
||||
.tabs__bar:has(input:nth-of-type(6):checked) ~ .tabs__body .tabs__panel:nth-of-type(6),
|
||||
.tabs__bar:has(input:nth-of-type(7):checked) ~ .tabs__body .tabs__panel:nth-of-type(7),
|
||||
.tabs__bar:has(input:nth-of-type(8):checked) ~ .tabs__body .tabs__panel:nth-of-type(8) {
|
||||
display: block;
|
||||
}
|
||||
.tabs__tab:has(:focus-visible) { outline: 2px solid var(--accent); outline-offset: -2px; }
|
||||
|
||||
/*
|
||||
A form's action row, and the space after the form it closes.
|
||||
|
||||
Not a plain .btn-row: a settings form is a stack of cards, and Save has to
|
||||
read as belonging to the cards above it rather than to whatever comes next.
|
||||
The gap that matters is the one *after* the form -- a card following it (the
|
||||
"Try it" panel on the search page, say) otherwise sits flush against Save and
|
||||
looks like another field of the same form. That margin therefore belongs to
|
||||
the form, not to the action row: the action row is always its form's last
|
||||
child, so a bottom margin there would have nothing to push away from.
|
||||
*/
|
||||
.form-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--sp-2);
|
||||
flex-wrap: wrap;
|
||||
margin-top: var(--sp-5);
|
||||
}
|
||||
|
||||
.admin-page > form { margin-bottom: var(--sp-8); }
|
||||
.admin-page > form:last-child { margin-bottom: 0; }
|
||||
|
||||
/* --- Cards ----------------------------------------------------------------- */
|
||||
.card {
|
||||
background: var(--surface);
|
||||
|
||||
@@ -61,8 +61,9 @@
|
||||
<input class="input" id="stt-model" name="stt_model"
|
||||
value="{{ values.stt_model }}" placeholder="whisper-1">
|
||||
<p class="field__hint">
|
||||
Sent even to servers that only host one; a router in front of several
|
||||
needs it.
|
||||
A server hosting one model <strong>ignores this</strong> and uses
|
||||
whatever it was started with — <code>whisper-1</code> is then just a
|
||||
label. It only selects anything on a server that hosts several.
|
||||
</p>
|
||||
</div>
|
||||
<div class="field">
|
||||
@@ -179,6 +180,6 @@
|
||||
<div id="audio-test-tts"></div>
|
||||
</section>
|
||||
|
||||
<div class="btn-row"><button class="btn btn--primary" type="submit">Save settings</button></div>
|
||||
<div class="form-actions"><button class="btn btn--primary" type="submit">Save settings</button></div>
|
||||
</form>
|
||||
{% endblock %}
|
||||
|
||||
@@ -84,6 +84,6 @@
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<div class="btn-row"><button class="btn btn--primary" type="submit">Save settings</button></div>
|
||||
<div class="form-actions"><button class="btn btn--primary" type="submit">Save settings</button></div>
|
||||
</form>
|
||||
{% endblock %}
|
||||
|
||||
@@ -128,7 +128,7 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="btn-row"><button class="btn btn--primary" type="submit">Save settings</button></div>
|
||||
<div class="form-actions"><button class="btn btn--primary" type="submit">Save settings</button></div>
|
||||
</form>
|
||||
|
||||
<section class="card">
|
||||
|
||||
Reference in New Issue
Block a user