Skip to content

Commit c77a7b7

Browse files
authored
fix(web): align compact button touch targets (#17748)
1 parent bd2346e commit c77a7b7

9 files changed

Lines changed: 58 additions & 27 deletions

‎apps/web/src/components/BranchToolbarBranchSelector.tsx‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -779,7 +779,11 @@ export function BranchToolbarBranchSelector({
779779
)}
780780
/>
781781
<ComposerContextLabel displayMode={displayMode}>
782-
<MiddleTruncate value={triggerLabel} className="w-full" />
782+
<MiddleTruncate
783+
value={triggerLabel}
784+
variant={displayMode === "panel" ? "cap-aligned" : "default"}
785+
className="w-full"
786+
/>
783787
</ComposerContextLabel>
784788
{displayMode !== "panel" ? (
785789
<ChevronDownIcon className="size-3 shrink-0 opacity-50" />

‎apps/web/src/components/ComposerContextLabel.tsx‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import type { ReactNode } from "react";
22

33
import { cn } from "../lib/utils";
4+
import { THREAD_DETAILS_PANEL_LABEL_CLASS } from "./chat/threadDetailsPanelStyles";
45

56
/** Keeps text measurable while the composer's outer label box collapses. */
67
export function ComposerContextLabel({
@@ -16,14 +17,15 @@ export function ComposerContextLabel({
1617
className={cn(
1718
"min-w-0",
1819
displayMode === "panel"
19-
? "flex-1 truncate text-left"
20+
? "flex-1 overflow-x-clip overflow-y-visible text-left"
2021
: "max-w-[240px] group-data-[compact]/composer-context:max-w-0",
2122
)}
2223
>
2324
<span
2425
data-composer-label-motion
2526
className={cn(
2627
"block w-full min-w-0 truncate",
28+
displayMode === "panel" && THREAD_DETAILS_PANEL_LABEL_CLASS,
2729
displayMode === "toolbar" &&
2830
"max-w-[240px] transition-opacity duration-180 ease-drawer group-data-[compact]/composer-context:opacity-0 motion-reduce:transition-none",
2931
)}

‎apps/web/src/components/GitActionsControl.tsx‎

Lines changed: 23 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ import { type DraftId, useComposerDraftStore } from "~/composerDraftStore";
127127
import {
128128
THREAD_DETAILS_PANEL_CHEVRON_CLASS,
129129
THREAD_DETAILS_PANEL_ICON_CLASS,
130+
THREAD_DETAILS_PANEL_LABEL_CLASS,
130131
THREAD_DETAILS_PANEL_SPLIT_GROUP_CLASS,
131132
THREAD_DETAILS_PANEL_SPLIT_SEPARATOR_CLASS,
132133
} from "./chat/threadDetailsPanelStyles";
@@ -441,23 +442,25 @@ function GitActionProgressButtonContent({
441442
aria-live="polite"
442443
className={cn(
443444
"grid min-w-0 flex-1 items-center",
444-
// Pin the title row to the button's minimum content height (min-height
445-
// minus vertical padding and border) so revealing the output row
446-
// extends the button downward without re-centering — the title must
447-
// not shift. No row gap: the collapsed output row must contribute zero
448-
// height so the single-line running button matches the static button
449-
// exactly. The panel column gap matches the static row's icon-to-label
450-
// distance (gap-2.5 plus the label's ml-0.5). In the panel the elapsed
451-
// counter renders outside the button (in the menu-chevron slot), so
452-
// there is no trailing column.
445+
// Pin the title row to the button's minimum content height so output
446+
// expands below it. Panel controls leave 24px after padding and border.
447+
// No row gap: collapsed output must not add height. The panel's elapsed
448+
// counter uses the menu-chevron slot, so it has no trailing column.
453449
isPanel
454-
? "grid-cols-[auto_minmax(0,1fr)] grid-rows-[1.75rem] gap-x-3"
450+
? "grid-cols-[auto_minmax(0,1fr)] grid-rows-[1.5rem] gap-x-3"
455451
: "grid-cols-[auto_minmax(0,1fr)_auto] grid-rows-[1.25rem] gap-x-2.5 sm:grid-rows-[1rem]",
456452
)}
457453
role="status"
458454
>
459455
<Spinner aria-hidden="true" className="row-start-1 -mx-0.5 shrink-0" />
460-
<p className="row-start-1 min-w-0 truncate text-left">{progress.status}</p>
456+
<p
457+
className={cn(
458+
"row-start-1 min-w-0 truncate text-left",
459+
isPanel && THREAD_DETAILS_PANEL_LABEL_CLASS,
460+
)}
461+
>
462+
{progress.status}
463+
</p>
461464
{!isPanel ? (
462465
<GitActionElapsedTime
463466
startedAtMs={progress.startedAtMs}
@@ -496,11 +499,13 @@ function GitActionSuccessButtonContent({ success }: { success: InlineGitActionSu
496499
return (
497500
<div
498501
aria-live="polite"
499-
className="grid min-w-0 flex-1 grid-cols-[auto_minmax(0,1fr)] grid-rows-[1.75rem] items-center gap-x-3"
502+
className="grid min-w-0 flex-1 grid-cols-[auto_minmax(0,1fr)] grid-rows-[1.5rem] items-center gap-x-3"
500503
role="status"
501504
>
502505
<CheckIcon aria-hidden="true" className="size-3.5 shrink-0 text-success" />
503-
<p className="min-w-0 truncate text-left">{success.title}</p>
506+
<p className={cn("min-w-0 truncate text-left", THREAD_DETAILS_PANEL_LABEL_CLASS)}>
507+
{success.title}
508+
</p>
504509
<div
505510
className={cn(
506511
"col-start-2 grid min-w-0 transition-[grid-template-rows,opacity] duration-200 ease-out motion-reduce:transition-none",
@@ -1704,7 +1709,7 @@ export default function GitActionsControl({
17041709
<PopoverTrigger
17051710
openOnHover
17061711
nativeButton={false}
1707-
render={<span className="block w-max cursor-not-allowed" />}
1712+
render={<span className="block w-full cursor-not-allowed" />}
17081713
>
17091714
<MenuItem
17101715
density={presentation === "menu" ? "touch" : "default"}
@@ -1824,7 +1829,7 @@ export default function GitActionsControl({
18241829
onClick={initializeGit}
18251830
>
18261831
<GitBranchPlusIcon className="size-3.5" aria-hidden />
1827-
<span className="ml-0.5">
1832+
<span className={cn("ml-0.5", isPanel && THREAD_DETAILS_PANEL_LABEL_CLASS)}>
18281833
{initAction.isPending ? "Initializing..." : "Initialize Git"}
18291834
</span>
18301835
</ThreadDetailsControl>
@@ -1882,7 +1887,7 @@ export default function GitActionsControl({
18821887
<span
18831888
className={cn(
18841889
"sr-only @3xl/header-actions:not-sr-only @3xl/header-actions:ml-0.5",
1885-
isPanel && "not-sr-only ml-0 truncate",
1890+
isPanel && cn("not-sr-only ml-0 truncate", THREAD_DETAILS_PANEL_LABEL_CLASS),
18861891
)}
18871892
>
18881893
{quickAction.label}
@@ -1909,7 +1914,7 @@ export default function GitActionsControl({
19091914
<span
19101915
className={cn(
19111916
"sr-only @3xl/header-actions:not-sr-only @3xl/header-actions:ml-0.5",
1912-
isPanel && "not-sr-only ml-0 truncate",
1917+
isPanel && cn("not-sr-only ml-0 truncate", THREAD_DETAILS_PANEL_LABEL_CLASS),
19131918
)}
19141919
>
19151920
{quickAction.label}
@@ -1979,7 +1984,7 @@ export default function GitActionsControl({
19791984
onClick={onOpenChanges}
19801985
>
19811986
<FileDiffIcon className={THREAD_DETAILS_PANEL_ICON_CLASS} aria-hidden />
1982-
<span className="flex-1 text-left">Changes</span>
1987+
<span className={cn("flex-1 text-left", THREAD_DETAILS_PANEL_LABEL_CLASS)}>Changes</span>
19831988
<span className="flex items-center gap-1 font-mono text-2xs tabular-nums">
19841989
<span className="text-success">+{changesTotals?.insertions ?? 0}</span>
19851990
<span className="text-destructive">-{changesTotals?.deletions ?? 0}</span>

‎apps/web/src/components/ProjectScriptsControl.tsx‎

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ import { cn } from "~/lib/utils";
5050
import {
5151
THREAD_DETAILS_PANEL_CHEVRON_CLASS,
5252
THREAD_DETAILS_PANEL_ICON_CLASS,
53+
THREAD_DETAILS_PANEL_LABEL_CLASS,
5354
THREAD_DETAILS_PANEL_SPLIT_GROUP_CLASS,
5455
THREAD_DETAILS_PANEL_SPLIT_SEPARATOR_CLASS,
5556
} from "./chat/threadDetailsPanelStyles";
@@ -322,7 +323,7 @@ export default function ProjectScriptsControl({
322323
<span
323324
className={cn(
324325
"sr-only @3xl/header-actions:not-sr-only @3xl/header-actions:ml-0.5",
325-
isPanel && "not-sr-only ml-0 truncate",
326+
isPanel && cn("not-sr-only ml-0 truncate", THREAD_DETAILS_PANEL_LABEL_CLASS),
326327
)}
327328
>
328329
{primaryScript.name}
@@ -387,7 +388,9 @@ export default function ProjectScriptsControl({
387388
onClick={() => setActionsMenuOpen({ presentation, scripts: false, imports: true })}
388389
>
389390
<WrenchIcon className={THREAD_DETAILS_PANEL_ICON_CLASS} />
390-
<span className="min-w-0 truncate">Actions</span>
391+
<span className={cn("min-w-0 truncate", THREAD_DETAILS_PANEL_LABEL_CLASS)}>
392+
Actions
393+
</span>
391394
</ThreadDetailsControl>
392395
<span aria-hidden="true" className={THREAD_DETAILS_PANEL_SPLIT_SEPARATOR_CLASS} />
393396
<Menu
@@ -456,7 +459,7 @@ export default function ProjectScriptsControl({
456459
<span
457460
className={cn(
458461
"sr-only @3xl/header-actions:not-sr-only @3xl/header-actions:ml-0.5",
459-
isPanel && "not-sr-only ml-0.5",
462+
isPanel && cn("not-sr-only ml-0.5", THREAD_DETAILS_PANEL_LABEL_CLASS),
460463
)}
461464
>
462465
{isPanel ? "Add project script" : "Add action"}

‎apps/web/src/components/chat/ComposerPrimaryActions.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -370,7 +370,7 @@ export const ComposerPrimaryActions = memo(function ComposerPrimaryActions({
370370
<button
371371
type="button"
372372
className={cn(
373-
"flex h-7 shrink-0 cursor-pointer items-center gap-1.5 rounded-full border px-2.5 text-xs tabular-nums outline-none transition-colors focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-64 [&_svg]:pointer-events-none [&_svg]:size-3.5",
373+
"flex h-9 shrink-0 cursor-pointer items-center gap-1.5 rounded-full border px-2.5 text-xs tabular-nums outline-none transition-colors focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-64 sm:h-8 [&_svg]:pointer-events-none [&_svg]:size-3.5",
374374
keepFullHistory
375375
? "border-border text-muted-foreground hover:text-foreground"
376376
: "border-warning/40 text-warning hover:bg-warning/8",

‎apps/web/src/components/chat/OpenInPicker.tsx‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ import { useAtomCommand } from "~/state/use-atom-command";
6464
import {
6565
THREAD_DETAILS_PANEL_CHEVRON_CLASS,
6666
THREAD_DETAILS_PANEL_ICON_CLASS,
67+
THREAD_DETAILS_PANEL_LABEL_CLASS,
6768
THREAD_DETAILS_PANEL_SPLIT_GROUP_CLASS,
6869
THREAD_DETAILS_PANEL_SPLIT_SEPARATOR_CLASS,
6970
} from "./threadDetailsPanelStyles";
@@ -421,7 +422,7 @@ export const OpenInPicker = memo(function OpenInPicker({
421422
compact
422423
? "sr-only"
423424
: "sr-only @3xl/header-actions:not-sr-only @3xl/header-actions:ml-0.5",
424-
isPanel && "not-sr-only ml-0 min-w-0 truncate",
425+
isPanel && cn("not-sr-only ml-0 min-w-0 truncate", THREAD_DETAILS_PANEL_LABEL_CLASS),
425426
)}
426427
>
427428
{primaryLabel}

‎apps/web/src/components/chat/ThreadDetailsPrRow.tsx‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ import { Button } from "../ui/button";
7575
import { Tooltip, TooltipPopup, TooltipTrigger } from "../ui/tooltip";
7676
import {
7777
THREAD_DETAILS_PANEL_ICON_CLASS,
78+
THREAD_DETAILS_PANEL_LABEL_CLASS,
7879
THREAD_DETAILS_PANEL_SPLIT_GROUP_CLASS,
7980
THREAD_DETAILS_PANEL_SPLIT_SEPARATOR_CLASS,
8081
} from "./threadDetailsPanelStyles";
@@ -398,7 +399,9 @@ export function ThreadDetailsPrRow({
398399
}
399400
>
400401
{icon}
401-
<span className="min-w-0 flex-1 truncate text-left">{text}</span>
402+
<span className={cn("min-w-0 flex-1 truncate text-left", THREAD_DETAILS_PANEL_LABEL_CLASS)}>
403+
{text}
404+
</span>
402405
</TooltipTrigger>
403406
{rowTooltip}
404407
</Tooltip>

‎apps/web/src/components/chat/threadDetailsPanelStyles.ts‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,11 @@ const THREAD_DETAILS_PANEL_SPLIT_GROUP_SURFACE_CLASS = `${THREAD_DETAILS_PANEL_H
1414

1515
export const THREAD_DETAILS_PANEL_ROW_CONTENT_CLASS = "gap-2.5 px-2.5 text-left";
1616

17+
// Center capitals with icons. Only clip horizontally so truncated labels retain
18+
// accents and descenders outside the trimmed text box.
19+
export const THREAD_DETAILS_PANEL_LABEL_CLASS =
20+
"[text-box:trim-both_cap_alphabetic] overflow-x-clip overflow-y-visible";
21+
1722
const THREAD_DETAILS_PANEL_CONTROL_CLASS = `h-8 min-w-0 rounded-lg border-transparent ${THREAD_DETAILS_PANEL_ROW_CONTENT_CLASS} text-[13px] font-medium text-foreground/80`;
1823

1924
export const THREAD_DETAILS_PANEL_ROW_CLASS = `${THREAD_DETAILS_PANEL_CONTROL_CLASS} w-full justify-start ${THREAD_DETAILS_PANEL_ROW_SURFACE_CLASS}`;

‎apps/web/src/components/ui/middle-truncate.tsx‎

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ export function MiddleTruncate({
1616
value,
1717
tail,
1818
showTitle = true,
19+
variant = "default",
1920
className,
2021
...props
2122
}: Omit<ComponentProps<"span">, "children"> & {
@@ -24,12 +25,19 @@ export function MiddleTruncate({
2425
tail?: number;
2526
/** The full value on hover. Off where a tooltip already carries it. */
2627
showTitle?: boolean;
28+
/** Align capital letters with adjacent icons while preserving accents and descenders. */
29+
variant?: "default" | "cap-aligned";
2730
}) {
2831
const split = splitForMiddleTruncate(value, tail);
2932
return (
3033
<span
3134
{...(showTitle ? { title: value } : {})}
32-
className={cn("inline-flex min-w-0 max-w-full overflow-hidden whitespace-nowrap", className)}
35+
className={cn(
36+
"inline-flex min-w-0 max-w-full overflow-hidden whitespace-nowrap",
37+
variant === "cap-aligned" &&
38+
"overflow-x-clip overflow-y-visible [&>span]:[text-box:trim-both_cap_alphabetic] [&>span]:overflow-x-clip [&>span]:overflow-y-visible",
39+
className,
40+
)}
3341
{...props}
3442
>
3543
{split ? (

0 commit comments

Comments
 (0)