refactor(lanes): drop the tick box and radio dots from the Add-lane modal

Both controls already render as cards that light up when selected — the same
selected/unselected treatment LaneStripCard uses — so the tick box and the two
radio dots were a second indicator of a state the card was already showing.

Neither input is removed, only hidden (`sr-only`): they are still what a
keyboard toggles and what a screen reader announces, so the cards take a
`focus-within` ring in exchange for the focus ring that went with them.

The router radios needed one thing the checkbox did not. "AI Router" is
disabled until the profile list lands, and the greyed-out dot was the only
thing showing that; hidden, it would have read as a card you can click that
does nothing — the dead control the existing comment warns about. So disabled
is now its own branch: muted text, reduced opacity, no hover, not-allowed
cursor.

Layout follows: no flex row with a gap when there is one child left, and the
two equal-width router cards centre their label rather than leaving it hard
against an empty left edge.

Visual only. No behaviour, API or schema change.
This commit is contained in:
2026-09-15 14:53:00 +07:00
parent 6e685ce1c3
commit c3f8dec104
+31 -21
View File
@@ -475,22 +475,31 @@ export function AddLaneModal({
] as const
).map((option) => {
const active = option.value === "native" ? router === "native" : router !== "native";
// Clicking "AI Router" before the list lands would resolve back to
// Native and read as a dead control.
const disabled = option.value === "router" && !routersLoaded;
return (
<label
key={option.value}
className={`flex flex-1 cursor-pointer items-center gap-2 rounded-lg border p-2.5 text-xs shadow-sm transition-colors ${
active
? "border-accent bg-accent/10 text-accent"
: "border-border bg-surface-2 text-fg-secondary hover:border-border-light hover:bg-surface-3"
// No radio dot: the card itself lights up when selected. That
// leaves the DISABLED state with nothing to show it, since the
// greyed-out dot was carrying it — hence the explicit branch.
// The input only goes visually hidden, so keyboard and screen
// readers still get a real radio group, and the card carries
// its focus ring.
className={`block flex-1 rounded-lg border p-2.5 text-center text-xs shadow-sm transition-colors focus-within:ring-2 focus-within:ring-accent/50 ${
disabled
? "cursor-not-allowed border-border bg-surface-2 text-fg-muted opacity-60"
: active
? "cursor-pointer border-accent bg-accent/10 text-accent"
: "cursor-pointer border-border bg-surface-2 text-fg-secondary hover:border-border-light hover:bg-surface-3"
}`}
>
<input
type="radio"
name="add-lane-router"
className="h-3.5 w-3.5 accent-accent"
// Clicking "AI Router" before the list lands would resolve
// back to Native and read as a dead control.
disabled={option.value === "router" && !routersLoaded}
className="sr-only"
disabled={disabled}
checked={active}
onChange={() =>
// Switching to "AI Router" lands on the first saved
@@ -536,9 +545,12 @@ export function AddLaneModal({
{tool === "claude" && (
<label
// Card that lights up when ticked, matching LaneStripCard's
// selected/unselected treatment.
className={`flex cursor-pointer items-start gap-2.5 rounded-lg border p-3 shadow-sm transition-colors ${
// No tick box: the card itself lights up when selected, the same
// treatment LaneStripCard uses, which makes a second indicator of
// the same state redundant. The input stays and only goes visually
// hidden — it is still what a keyboard toggles and what a screen
// reader announces — so the card carries the focus ring for it.
className={`block cursor-pointer rounded-lg border p-3 shadow-sm transition-colors focus-within:ring-2 focus-within:ring-accent/50 ${
writeDocs
? "border-accent bg-accent/10"
: "border-border bg-surface-2 hover:border-border-light hover:bg-surface-3"
@@ -551,17 +563,15 @@ export function AddLaneModal({
aria-label={t("addLaneWriteDocsLabel")}
checked={writeDocs}
onChange={(e) => setWriteDocs(e.target.checked)}
className="mt-0.5 h-3.5 w-3.5 shrink-0 accent-accent"
className="sr-only"
/>
<span>
<span
className={`block text-xs font-medium ${writeDocs ? "text-accent" : "text-fg-secondary"}`}
>
{t("addLaneWriteDocsLabel")}
</span>
<span className="mt-1 block text-[10px] text-fg-muted">
{t("addLaneWriteDocsHint")}
</span>
<span
className={`block text-xs font-medium ${writeDocs ? "text-accent" : "text-fg-secondary"}`}
>
{t("addLaneWriteDocsLabel")}
</span>
<span className="mt-1 block text-[10px] text-fg-muted">
{t("addLaneWriteDocsHint")}
</span>
</label>
)}