gui: styles hold up in Safari 15.0–15.3 again (macOS 12, #220) — no :has(), and :focus-visible kept out of selector lists
old-webkit failed on main: app.css and routing.css had :focus-visible inside selector lists (.quota-refresh, .sess-go, .acap, .alane, .us-handle, the routing group and member grips) and inside :is() (.am-or's grip). Safari before 15.4 drops the whole rule, so the hover reveal went too. Those parts now sit in rules of their own. library.css and routing.css used :has(). A library row with agent chips is now marked .with-agents by agentChips, and a request whose tag says where it went upstream is marked .upstreamed. The keys-row collapse (#841) and the library chips' rings kept a control out with :not(:focus-visible), which Safari before 15.4 also drops whole. Each now has an @supports not selector(:focus-visible) copy on :focus. old-webkit's checker now splits a selector list on its own commas, not those inside :is()/:not(). It also wants that fallback wherever :not(...:focus-visible...) is used. The new old-webkit fails on origin/main's assets (12 findings) and passes with this change in Chromium and WebKit. account-cap, lane-limit, fold-show-grip, group-member-drag, library-all, library-items, pick-skills, skill-name-many-agents, routing-upstream, usage-arrange, usage-card-refresh and click-scroll pass in both. group-arrange fails in Chromium en the same way on origin/main.
This commit is contained in:
1 parent
a3726f657e
commit
69665a6960
6 files changed
+65
-18
No files matched your search
+30
-10
@@ -695,7 +695,9 @@ body.window .field.solo { grid-column: auto; }
|
||||
card's other lines do (#840) */
|
||||
.quota-refresh { display: inline-flex; align-items: center; justify-content: center; width: 0; height: 14px; margin: 0; padding: 0; border: 0; border-radius: 4px; background: none; color: var(--faint); vertical-align: -3px; opacity: 0; overflow: hidden; cursor: pointer; transition: width .12s, margin .12s, opacity .12s; }
|
||||
.quota-refresh svg { flex: none; }
|
||||
.subscription-card:hover .quota-refresh, .quota-refresh:focus-visible, .quota-refresh.busy { width: 14px; margin: 0 3px 0 -2px; opacity: 1; }
|
||||
.subscription-card:hover .quota-refresh, .quota-refresh.busy { width: 14px; margin: 0 3px 0 -2px; opacity: 1; }
|
||||
/* apart: Safari before 15.4 drops a whole selector list with :focus-visible in it */
|
||||
.quota-refresh:focus-visible { width: 14px; margin: 0 3px 0 -2px; opacity: 1; }
|
||||
.quota-refresh:hover { color: var(--fg); background: var(--pill-hover); }
|
||||
.quota-refresh.busy { cursor: default; }
|
||||
.quota-refresh.busy svg { animation: spin .8s linear infinite; }
|
||||
@@ -1126,7 +1128,9 @@ details.cx-part[open] > summary .cx-h { margin-bottom: 2px; }
|
||||
/* a row of the Usage page's list opens on the Sessions page (#752) */
|
||||
.row.sess.sess-link { cursor: pointer; }
|
||||
.row.sess .sess-go { flex: none; display: inline-flex; color: var(--faint); }
|
||||
.row.sess.sess-link:hover .sess-go, .row.sess.sess-link:focus-visible .sess-go { color: var(--fg-2); }
|
||||
.row.sess.sess-link:hover .sess-go { color: var(--fg-2); }
|
||||
/* apart: Safari before 15.4 drops a whole selector list with :focus-visible in it */
|
||||
.row.sess.sess-link:focus-visible .sess-go { color: var(--fg-2); }
|
||||
.sess-all { flex: none; white-space: nowrap; }
|
||||
.sess-resume { flex: none; height: 24px; padding: 0 10px; border-radius: 7px; border: 0; background: var(--pill); color: var(--fg); font-size: 11.5px; font-weight: 500; white-space: nowrap; }
|
||||
.sess-resume:hover { background: var(--pill-hover); }
|
||||
@@ -1874,14 +1878,18 @@ body.window .ag-sk-icon { width: 22px; height: 22px; border-radius: 6px; }
|
||||
.acc .amodels:hover, .acc .amodels.open { color: var(--fg); background: var(--pill-hover); }
|
||||
/* an account's usage cap: as the models pill, shown on hover till one is set */
|
||||
.acc .acap { flex: none; display: inline-flex; align-items: center; height: 20px; padding: 0 7px; font: inherit; font-size: 11px; color: var(--muted); border: 0; border-radius: 5px; background: none; box-shadow: inset 0 0 0 1px var(--line); white-space: nowrap; cursor: default; opacity: 0; transition: opacity .15s, background .12s, color .12s; }
|
||||
.acc:hover .acap, .acc .acap.open, .acc .acap:focus-visible, .acc .acap.set { opacity: 1; }
|
||||
.acc:hover .acap, .acc .acap.open, .acc .acap.set { opacity: 1; }
|
||||
/* apart: Safari before 15.4 drops a whole selector list with :focus-visible in it */
|
||||
.acc .acap:focus-visible { opacity: 1; }
|
||||
.acc .acap.set { color: var(--accent); background: var(--accent-soft); box-shadow: none; font-weight: 500; }
|
||||
.acc .acap:hover, .acc .acap.open { color: var(--fg); background: var(--pill-hover); }
|
||||
.acc .acap-in { width: 64px; }
|
||||
/* an account's or key's limit on requests at once (#892), and how many
|
||||
run and wait under it: shown on hover unless a limit applies */
|
||||
.acc .alane { flex: none; display: inline-flex; align-items: center; height: 20px; padding: 0 7px; font: inherit; font-size: 11px; color: var(--muted); border: 0; border-radius: 5px; background: none; box-shadow: inset 0 0 0 1px var(--line); white-space: nowrap; cursor: default; opacity: 0; transition: opacity .15s, background .12s, color .12s; font-variant-numeric: tabular-nums; }
|
||||
.acc:hover .alane, .acc .alane.open, .acc .alane:focus-visible, .acc .alane.set { opacity: 1; }
|
||||
.acc:hover .alane, .acc .alane.open, .acc .alane.set { opacity: 1; }
|
||||
/* apart: Safari before 15.4 drops a whole selector list with :focus-visible in it */
|
||||
.acc .alane:focus-visible { opacity: 1; }
|
||||
.acc .alane.set { color: var(--fg); background: var(--pill-hover); box-shadow: none; }
|
||||
.acc .alane.busy-lane { color: var(--accent); background: var(--accent-soft); font-weight: 500; }
|
||||
.acc .alane.queued { color: var(--amber); }
|
||||
@@ -1903,14 +1911,22 @@ body.window .ag-sk-icon { width: 22px; height: 22px; border-radius: 6px; }
|
||||
badge gives its own up first. First and Make first stay one line */
|
||||
.accts.keys .acc .plan { flex: 0 1 auto; min-width: 3em; overflow: hidden; text-overflow: ellipsis; }
|
||||
.accts.keys .acc > :is(.amodels:not(.set), .proto:not(.set), .alane:not(.set), .text.quiet) { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||
.accts.keys .acc:not(:hover) > :is(.amodels:not(.set), .proto:not(.set), .alane:not(.set), .text.quiet):not(:focus-visible, .open) { max-width: 0; padding-left: 0; padding-right: 0; box-shadow: none; }
|
||||
.accts.keys .acc > :is(.amodels, .alane):not(.set):not(:focus-visible, .open) { flex-shrink: 10000; }
|
||||
.accts.keys .acc:not(:hover) > :is(.amodels:not(.set), .proto:not(.set), .alane:not(.set), .text.quiet):not(.open):not(:focus-visible) { max-width: 0; padding-left: 0; padding-right: 0; box-shadow: none; }
|
||||
.accts.keys .acc > :is(.amodels, .alane):not(.set):not(.open):not(:focus-visible) { flex-shrink: 10000; }
|
||||
/* the limit pill unset on a key's row is quiet text, as the row's other
|
||||
hover-only controls: no padding to keep, and hidden it takes back the
|
||||
gap before it too (#892) */
|
||||
.accts.keys .acc > .alane:not(.set):not(:focus-visible, .open) { padding: 0; box-shadow: none; background: none; }
|
||||
.accts.keys .acc:not(:hover) > .alane:not(.set):not(:focus-visible, .open) { margin-left: -9px; }
|
||||
.accts.keys .acc > .alane:not(.set):not(.open):not(:focus-visible) { padding: 0; box-shadow: none; background: none; }
|
||||
.accts.keys .acc:not(:hover) > .alane:not(.set):not(.open):not(:focus-visible) { margin-left: -9px; }
|
||||
.accts.keys .acc > :is(.proto, .text.quiet) { flex-shrink: 0; }
|
||||
/* Safari before 15.4 has no :focus-visible and drops the rules above
|
||||
whole: there a focused control stays out on :focus instead */
|
||||
@supports not selector(:focus-visible) {
|
||||
.accts.keys .acc:not(:hover) > :is(.amodels:not(.set), .proto:not(.set), .alane:not(.set), .text.quiet):not(.open):not(:focus) { max-width: 0; padding-left: 0; padding-right: 0; box-shadow: none; }
|
||||
.accts.keys .acc > :is(.amodels, .alane):not(.set):not(.open):not(:focus) { flex-shrink: 10000; }
|
||||
.accts.keys .acc > .alane:not(.set):not(.open):not(:focus) { padding: 0; box-shadow: none; background: none; }
|
||||
.accts.keys .acc:not(:hover) > .alane:not(.set):not(.open):not(:focus) { margin-left: -9px; }
|
||||
}
|
||||
.accts.keys .acc .using, .accts.keys .acc > .text:not(.quiet) { flex: none; white-space: nowrap; }
|
||||
#gatewayKeys .acc { min-width: 0; }
|
||||
#gatewayKeys .acc .plan { flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
|
||||
@@ -2714,7 +2730,9 @@ body.panel .modal { padding: 16px 12px; }
|
||||
with 3.5px between them the right column was cut in half (Hu9956, #842) */
|
||||
.subscription-head .us-handle { position: relative; width: 24px; height: 24px; margin: -4px -3px; }
|
||||
.subscription-head .us-handle::before { content: ""; position: absolute; left: -8px; top: 50%; width: 6px; height: 11px; margin-top: -5.5px; background: radial-gradient(circle, var(--muted) .9px, transparent 1.2px) 0 0 / 3px 3.75px; opacity: 0; transition: opacity .15s; }
|
||||
.subscription-card:is(:hover, .dragging) .us-handle::before, .us-handle:focus-visible::before { opacity: 1; }
|
||||
.subscription-card:is(:hover, .dragging) .us-handle::before { opacity: 1; }
|
||||
/* apart: Safari before 15.4 drops a whole selector list with :focus-visible in it */
|
||||
.us-handle:focus-visible::before { opacity: 1; }
|
||||
.subscription-usage.sorting { user-select: none; -webkit-user-select: none; }
|
||||
.subscription-usage.sorting > .subscription-card { transition: transform .22s cubic-bezier(.22, 1, .36, 1); }
|
||||
.subscription-usage.sorting .us-handle { cursor: grabbing; }
|
||||
@@ -2904,7 +2922,9 @@ body.window .row.agent .who.with-models { flex-wrap: wrap; row-gap: 0; }
|
||||
.am-order { padding-top: 4px; }
|
||||
.am-rows.am-order .am-mr { padding-left: 14px; cursor: grab; user-select: none; -webkit-user-select: none; }
|
||||
.am-or .grip { flex: none; width: 6px; height: 11px; margin-right: -4px; background: radial-gradient(circle, var(--muted) .9px, transparent 1.2px) 0 0 / 3px 3.75px; opacity: 0; transition: opacity .15s; }
|
||||
.am-or:is(:hover, :focus-visible, .dragging) .grip { opacity: 1; }
|
||||
.am-or:is(:hover, .dragging) .grip { opacity: 1; }
|
||||
/* apart: Safari before 15.4 drops a whole selector list with :focus-visible in it */
|
||||
.am-or:focus-visible .grip { opacity: 1; }
|
||||
@media (hover: none) { .am-or .grip { opacity: 1; } }
|
||||
.am-or .ix { flex: none; min-width: 16px; font-size: 11.5px; color: var(--faint); font-variant-numeric: tabular-nums; text-align: right; }
|
||||
.am-order.sorting .am-or { transition: transform .22s cubic-bezier(.22, 1, .36, 1); cursor: grabbing; }
|
||||
|
||||
@@ -74,7 +74,7 @@ body.window .lib-head { top: -2px; margin: -2px -16px 0; padding: 8px 16px 4px;
|
||||
the row wider than the list (#257) */
|
||||
.lib-row .who { flex: 1 1 auto; min-width: 0; overflow-wrap: anywhere; }
|
||||
/* and beside a hand of many agents' chips it keeps room to be read (#936) */
|
||||
.lib-row:has(> .lib-agents) > .who { min-width: min(140px, 32%); }
|
||||
.lib-row.with-agents > .who { min-width: min(140px, 32%); }
|
||||
/* a server's or skill's picture sits level with the top of its name */
|
||||
.lib-row > .lib-logo, .lib-row > .lib-glyph { align-self: flex-start; margin-top: 1px; }
|
||||
.lib-row .name.mono, .lib-pick .name.mono { font: 500 12.5px var(--code); }
|
||||
@@ -217,7 +217,7 @@ input.lib-setname:focus { border-color: var(--accent); box-shadow: 0 0 0 3px col
|
||||
the spacer before them is the difference. A WebKit without container
|
||||
units keeps -7px and 3px. */
|
||||
@supports (width: 1cqw) {
|
||||
.lib-row:has(> .lib-agents) { container-type: inline-size; }
|
||||
.lib-row.with-agents { container-type: inline-size; }
|
||||
.lib-row > .lib-agents {
|
||||
--lib-n: var(--lib-agent-count, 1);
|
||||
--lib-gap: calc((48cqw - 60px - var(--lib-n) * 24px) / max(1, var(--lib-n) - 1));
|
||||
@@ -524,3 +524,13 @@ input.lib-setname:focus { border-color: var(--accent); box-shadow: 0 0 0 3px col
|
||||
.lib-subhead > .lib-agents { margin-left: auto; }
|
||||
.lib-pickbar .lib-groupname { flex: 1 1 140px; min-width: 100px; max-width: 240px; margin: 0; }
|
||||
.lib-grouphead .lib-grouprename-in { height: 24px; margin: 0; max-width: 260px; font-weight: 500; }
|
||||
|
||||
/* Safari before 15.4 has no :focus-visible and drops the chips' rings
|
||||
above: there they go by :focus */
|
||||
@supports not selector(:focus-visible) {
|
||||
.lib-row > .lib-agents .lib-ag:not(:focus) { outline: 1.5px solid var(--card); outline-offset: 0; }
|
||||
.lib-row > .lib-agents:hover .lib-ag:not(:focus), .lib-row > .lib-agents:focus-within .lib-ag:not(:focus) { outline-color: transparent; }
|
||||
.lib-row.click:hover > .lib-agents .lib-ag:not(:focus) { outline-color: var(--card-2); }
|
||||
.lib-row.click:hover > .lib-agents:hover .lib-ag:not(:focus) { outline-color: transparent; }
|
||||
.lib-grouphead > .lib-agents .lib-ag:not(:focus) { outline-color: var(--card-2); }
|
||||
}
|
||||
@@ -223,6 +223,9 @@
|
||||
on ||= [];
|
||||
const box = el("div", "lib-agents");
|
||||
box.style.setProperty("--lib-agent-count", all.length);
|
||||
// the row it is put in says so (.with-agents), for its name's room:
|
||||
// Safari before 15.4 has no :has(). Every caller appends it at once.
|
||||
queueMicrotask(() => box.parentElement?.classList.contains("lib-row") && box.parentElement.classList.add("with-agents"));
|
||||
for (const a of all) {
|
||||
const always = opts.always?.(a) || "";
|
||||
const has = on.includes(a.id) || !!always;
|
||||
|
||||
@@ -201,8 +201,9 @@
|
||||
.rt-req .to .swap { flex: none; max-width: 70%; }
|
||||
.rt-req .to .routed, .pr-to .routed { flex: none; max-width: 45%; overflow: hidden; text-overflow: ellipsis; font-size: 10.5px; line-height: 16px; color: var(--muted); white-space: nowrap; }
|
||||
.rt-steps li.swap::before { background: var(--amber); }
|
||||
/* short of room, where it went gives way down to the provider's name, then this does */
|
||||
.rt-req .to:has(.upstream) .said { min-width: min(10em, 45%); }
|
||||
/* short of room, where it went gives way down to the provider's name, then this does
|
||||
(.upstreamed, set with the tag: Safari before 15.4 has no :has()) */
|
||||
.rt-req .to.upstreamed .said { min-width: min(10em, 45%); }
|
||||
.rt-req .to .upstream, .pr-to .upstream { flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; font-size: 10.5px; line-height: 16px; color: var(--muted); white-space: nowrap; }
|
||||
.rt-req .meta { display: flex; align-items: baseline; justify-content: flex-end; flex-wrap: wrap; gap: 2px 0; color: var(--muted); font-variant-numeric: tabular-nums; }
|
||||
.rt-metric { display: inline-flex; align-items: baseline; gap: 4px; white-space: nowrap; min-width: 0; }
|
||||
@@ -310,7 +311,9 @@ button.rt-session { cursor: pointer; }
|
||||
on Discord) */
|
||||
.rt-group .rt-ghandle.ag-handle { position: relative; align-items: center; justify-content: center; width: auto; min-width: 24px; height: 24px; padding: 0 3px; }
|
||||
.rt-group .rt-ghandle::before { content: ""; position: absolute; left: -8px; top: 50%; width: 5px; height: 11px; margin-top: -5.5px; background: radial-gradient(circle, var(--muted) .9px, transparent 1.2px) 0 0 / 3.5px 3.75px; opacity: 0; transition: opacity .15s; }
|
||||
.rt-group:is(:hover, .dragging) .rt-ghandle::before, .rt-group .rt-ghandle.open::before, .rt-group .rt-ghandle:focus-visible::before { opacity: 1; }
|
||||
.rt-group:is(:hover, .dragging) .rt-ghandle::before, .rt-group .rt-ghandle.open::before { opacity: 1; }
|
||||
/* apart: Safari before 15.4 drops a whole selector list with :focus-visible in it */
|
||||
.rt-group .rt-ghandle:focus-visible::before { opacity: 1; }
|
||||
/* while a row is dragged the others slide out of its way */
|
||||
.rt-groups.sorting { user-select: none; -webkit-user-select: none; }
|
||||
.rt-groups.sorting > .rt-group { transition: transform .22s cubic-bezier(.22, 1, .36, 1); }
|
||||
@@ -420,7 +423,9 @@ button.rt-session { cursor: pointer; }
|
||||
hover, dragged or from the keyboard only, as on the groups' cards */
|
||||
.rt-gedit .fbrow .rt-mhandle.ag-handle { position: relative; width: 18px; height: 22px; margin: 0 -2px; font: inherit; color: var(--muted); font-variant-numeric: tabular-nums; }
|
||||
.rt-gedit .fbrow .rt-mhandle::before { content: ""; position: absolute; left: -7px; top: 50%; width: 5px; height: 11px; margin-top: -5.5px; background: radial-gradient(circle, var(--muted) .9px, transparent 1.2px) 0 0 / 3.5px 3.75px; opacity: 0; transition: opacity .15s; }
|
||||
.rt-gedit .fbrow:is(:hover, .dragging) .rt-mhandle::before, .rt-gedit .fbrow .rt-mhandle:focus-visible::before { opacity: 1; }
|
||||
.rt-gedit .fbrow:is(:hover, .dragging) .rt-mhandle::before { opacity: 1; }
|
||||
/* apart: Safari before 15.4 drops a whole selector list with :focus-visible in it */
|
||||
.rt-gedit .fbrow .rt-mhandle:focus-visible::before { opacity: 1; }
|
||||
.rt-gedit .fbl.sorting { user-select: none; -webkit-user-select: none; }
|
||||
.rt-gedit .fbl.sorting > .fbrow { transition: transform .22s cubic-bezier(.22, 1, .36, 1); }
|
||||
.rt-gedit .fbl.sorting .rt-mhandle { cursor: grabbing; }
|
||||
|
||||
@@ -1757,7 +1757,7 @@
|
||||
}
|
||||
if (tr?.swapped && tr.done && tryOk(tr)) to.append(swapTag(tr, true)); // beside the model asked for
|
||||
else if (tr?.routed && tr.done && tryOk(tr)) to.append(routedTag(tr));
|
||||
if (tr?.upstream && tr.done && tryOk(tr)) to.append(upstreamTag(tr));
|
||||
if (tr?.upstream && tr.done && tryOk(tr)) { to.append(upstreamTag(tr)); to.classList.add("upstreamed"); }
|
||||
const info = el("span", "meta");
|
||||
if (meta.length) info.append(el("span", "retry-count", meta.join(" · ")));
|
||||
info.append(...metrics.map((m) => metricElement(m, true)));
|
||||
|
||||
@@ -155,10 +155,19 @@ test("the styles hold up in Safari 15.0", async () => {
|
||||
for (const m of css.matchAll(/([^{}]+)\{/g)) {
|
||||
const sel = m[1].trim();
|
||||
if (sel.startsWith("@")) continue;
|
||||
const parts = sel.split(",");
|
||||
// the list's own commas, not those inside :is() or :not()
|
||||
const parts = []; let depth = 0, from = 0;
|
||||
for (let i = 0; i < sel.length; i++) {
|
||||
if (sel[i] === "(") depth++;
|
||||
else if (sel[i] === ")") depth--;
|
||||
else if (sel[i] === "," && !depth) { parts.push(sel.slice(from, i)); from = i + 1; }
|
||||
}
|
||||
parts.push(sel.slice(from));
|
||||
if (parts.some((p) => p.includes(":focus-visible")) && !parts.every((p) => p.includes(":focus-visible"))) bad.push(`${f}:~${lineOf(m.index)} :focus-visible in a selector list (Safari 15.4 drops the rule): ${sel.slice(0, 90)}`);
|
||||
if (/:is\([^)]*:focus-visible/.test(sel)) bad.push(`${f}:~${lineOf(m.index)} :focus-visible inside :is(): ${sel.slice(0, 90)}`);
|
||||
}
|
||||
// a rule kept off a focused control drops whole there: one for :focus beside it
|
||||
if (/:not\([^)]*:focus-visible/.test(css) && !/@supports not selector\(:focus-visible\)/.test(css)) bad.push(`${f}: :not(:focus-visible) (Safari 15.4) with no @supports not selector(:focus-visible) fallback`);
|
||||
if (/@container\b/.test(css) && !/@supports not \(container-type: inline-size\)/.test(css)) bad.push(`${f}: @container (Safari 16) with no @supports not (container-type: inline-size) fallback`);
|
||||
if (/:\s*subgrid\b/.test(css) && !/@supports not \(grid-template-columns: subgrid\)/.test(css)) {
|
||||
// a subgrid that only lines rows up across cards may go without
|
||||
|
||||
Reference in new issue
Block a user