diff --git a/web/src/settings.ts b/web/src/settings.ts index 0184b92..c28adc5 100644 --- a/web/src/settings.ts +++ b/web/src/settings.ts @@ -722,13 +722,18 @@ export function settingsMain(views: { hide: () => void }[], goHome: () => void): * A reorder control: the same `control control-quiet control-icon` the * sibling rows in this view use (#231), with a chevron off the shared * glyph set (#251) rather than the words "up" and "down". + * + * `data-move` is how `refreshProviderEdges` finds it (#255). Position in + * the row is not a contract — the day a third button joins the row, an + * ordinal selector would quietly disable the wrong control. */ - function moveButton(glyph: "chevronUp" | "chevronDown", label: string): HTMLButtonElement { + function moveButton(direction: "up" | "down", label: string): HTMLButtonElement { const button = el("button", "control control-quiet control-icon provider-move"); button.type = "button"; + button.dataset.move = direction; button.setAttribute("aria-label", label); button.title = label; - button.append(icon(glyph)); + button.append(icon(direction === "up" ? "chevronUp" : "chevronDown")); return button; } @@ -758,8 +763,8 @@ export function settingsMain(views: { hide: () => void }[], goHome: () => void): budgetInput.classList.add("provider-budget"); budgetInput.setAttribute("aria-label", `${provider} daily budget`); - const up = moveButton("chevronUp", `move ${provider} up`); - const down = moveButton("chevronDown", `move ${provider} down`); + const up = moveButton("up", `move ${provider} up`); + const down = moveButton("down", `move ${provider} down`); up.addEventListener("click", () => { const previous = item.previousElementSibling; @@ -865,8 +870,8 @@ export function settingsMain(views: { hide: () => void }[], goHome: () => void): refreshProviderEdges = () => { const items = [...list.children]; items.forEach((item, index) => { - const up = item.querySelector("button:nth-of-type(1)"); - const down = item.querySelector("button:nth-of-type(2)"); + const up = item.querySelector('[data-move="up"]'); + const down = item.querySelector('[data-move="down"]'); if (up) up.disabled = index === 0; if (down) down.disabled = index === items.length - 1; }); diff --git a/web/src/style.css b/web/src/style.css index 5d9d316..92a53aa 100644 --- a/web/src/style.css +++ b/web/src/style.css @@ -1093,7 +1093,10 @@ body { border-color: var(--line); } -.control-quiet:hover, +/* `:not(:disabled)` because this rule sits after `.control:disabled` at the + same specificity: without it a disabled quiet control lights accent-bright + under the cursor and invites a click that does nothing (#255). */ +.control-quiet:hover:not(:disabled), .control-quiet[aria-expanded="true"] { color: var(--accent-bright); border-color: var(--accent); @@ -2407,7 +2410,3 @@ select.form-input { .provider-budget { width: 5.5rem; } - -.provider-move:disabled { - visibility: hidden; -}