Compare commits

...

1 Commits

Author SHA1 Message Date
Lluis Agusti
948119d071 feat(frontend): add hit-area utility for improved touch targets
Install the hit-area Tailwind CSS utility (via shadcn registry) and apply
it to small interactive elements in the Copilot and Library Agent pages.
The utility expands clickable zones using CSS pseudo-elements without
affecting visual layout.

Targets improved:
- File chip remove button (FileChips.tsx) — ~12px → hit-area-4
- Chat sidebar "more actions" trigger (ChatSidebar.tsx) — ~20px → hit-area-3
- Mobile dropdown trigger (CopilotPage.tsx) — ~20px → hit-area-3
- Collapsed tool group toggle (CollapsedToolGroup.tsx) — hit-area-y-2
- MessageAction buttons (message.tsx) — 32px → hit-area-2
- Sidebar dropdown triggers ×4 (*ActionsDropdown.tsx) — ~20px → hit-area-3

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 16:32:09 +08:00
10 changed files with 160 additions and 9 deletions

View File

@@ -151,7 +151,7 @@ export function CopilotPage() {
<DropdownMenu>
<DropdownMenuTrigger asChild>
<button
className="rounded p-1.5 hover:bg-neutral-100"
className="hit-area-3 rounded p-1.5 hover:bg-neutral-100"
aria-label="More actions"
>
<DotsThree className="h-5 w-5 text-neutral-600" />

View File

@@ -33,7 +33,7 @@ export function FileChips({ files, onRemove, isUploading }: Props) {
type="button"
aria-label={`Remove ${file.name}`}
onClick={() => onRemove(index)}
className="ml-0.5 rounded-full p-0.5 text-zinc-400 transition-colors hover:bg-zinc-200 hover:text-zinc-600"
className="hit-area-4 ml-0.5 rounded-full p-0.5 text-zinc-400 transition-colors hover:bg-zinc-200 hover:text-zinc-600"
>
<XIcon className="h-3 w-3" weight="bold" />
</button>

View File

@@ -96,7 +96,7 @@ export function CollapsedToolGroup({ parts }: Props) {
onClick={() => setExpanded(!expanded)}
aria-expanded={expanded}
aria-controls={panelId}
className="flex items-center gap-1.5 text-sm text-muted-foreground transition-colors hover:text-foreground"
className="hit-area-y-2 flex items-center gap-1.5 text-sm text-muted-foreground transition-colors hover:text-foreground"
>
<CaretRightIcon
size={12}

View File

@@ -341,7 +341,7 @@ export function ChatSidebar() {
<DropdownMenuTrigger asChild>
<button
onClick={(e) => e.stopPropagation()}
className="absolute right-2 top-1/2 -translate-y-1/2 rounded-full p-1.5 text-zinc-600 transition-all hover:bg-neutral-100"
className="hit-area-3 absolute right-2 top-1/2 -translate-y-1/2 rounded-full p-1.5 text-zinc-600 transition-all hover:bg-neutral-100"
aria-label="More actions"
>
<DotsThree className="h-4 w-4" />

View File

@@ -65,7 +65,7 @@ export function ScheduleActionsDropdown({ agent, schedule, onDeleted }: Props) {
<DropdownMenu>
<DropdownMenuTrigger asChild>
<button
className="ml-auto shrink-0 rounded p-1 hover:bg-gray-100"
className="hit-area-3 ml-auto shrink-0 rounded p-1 hover:bg-gray-100"
onClick={(e) => e.stopPropagation()}
aria-label="More actions"
>

View File

@@ -108,7 +108,7 @@ export function TaskActionsDropdown({ agent, run, onDeleted }: Props) {
<DropdownMenu>
<DropdownMenuTrigger asChild>
<button
className="ml-auto shrink-0 rounded p-1 hover:bg-gray-100"
className="hit-area-3 ml-auto shrink-0 rounded p-1 hover:bg-gray-100"
onClick={(e) => e.stopPropagation()}
aria-label="More actions"
>

View File

@@ -67,7 +67,7 @@ export function TemplateActionsDropdown({ agent, template, onDeleted }: Props) {
<DropdownMenu>
<DropdownMenuTrigger asChild>
<button
className="ml-auto shrink-0 rounded p-1 hover:bg-gray-100"
className="hit-area-3 ml-auto shrink-0 rounded p-1 hover:bg-gray-100"
onClick={(e) => e.stopPropagation()}
aria-label="More actions"
>

View File

@@ -67,7 +67,7 @@ export function TriggerActionsDropdown({ agent, trigger, onDeleted }: Props) {
<DropdownMenu>
<DropdownMenuTrigger asChild>
<button
className="ml-auto shrink-0 rounded p-1 hover:bg-gray-100"
className="hit-area-3 ml-auto shrink-0 rounded p-1 hover:bg-gray-100"
onClick={(e) => e.stopPropagation()}
aria-label="More actions"
>

View File

@@ -196,3 +196,148 @@ body[data-google-picker-open="true"] [data-dialog-content] {
[data-streamdown="table-wrapper"] td {
padding: 0.875rem 1rem; /* py-3.5 px-4 */
}
@utility hit-area-debug {
position: relative;
&::before {
content: "";
position: absolute;
top: var(--hit-area-t, 0px);
right: var(--hit-area-r, 0px);
bottom: var(--hit-area-b, 0px);
left: var(--hit-area-l, 0px);
pointer-events: inherit;
@apply border border-dashed border-blue-500 bg-blue-500/10;
}
&:hover::before {
@apply border border-dashed border-green-500 bg-green-500/10;
}
}
@utility hit-area {
position: relative;
&::before {
content: "";
position: absolute;
top: var(--hit-area-t, 0px);
right: var(--hit-area-r, 0px);
bottom: var(--hit-area-b, 0px);
left: var(--hit-area-l, 0px);
pointer-events: inherit;
}
}
@utility hit-area-* {
position: relative;
--hit-area-t: --spacing(--value(number) * -1);
--hit-area-t: calc(--value([*]) * -1);
--hit-area-b: --spacing(--value(number) * -1);
--hit-area-b: calc(--value([*]) * -1);
--hit-area-l: --spacing(--value(number) * -1);
--hit-area-l: calc(--value([*]) * -1);
--hit-area-r: --spacing(--value(number) * -1);
--hit-area-r: calc(--value([*]) * -1);
&::before {
content: "";
position: absolute;
top: var(--hit-area-t, 0px);
right: var(--hit-area-r, 0px);
bottom: var(--hit-area-b, 0px);
left: var(--hit-area-l, 0px);
pointer-events: inherit;
}
}
@utility hit-area-l-* {
position: relative;
--hit-area-l: --spacing(--value(number) * -1);
--hit-area-l: calc(--value([*]) * -1);
&::before {
content: "";
position: absolute;
top: var(--hit-area-t, 0px);
right: var(--hit-area-r, 0px);
bottom: var(--hit-area-b, 0px);
left: var(--hit-area-l, 0px);
pointer-events: inherit;
}
}
@utility hit-area-r-* {
position: relative;
--hit-area-r: --spacing(--value(number) * -1);
--hit-area-r: calc(--value([*]) * -1);
&::before {
content: "";
position: absolute;
top: var(--hit-area-t, 0px);
right: var(--hit-area-r, 0px);
bottom: var(--hit-area-b, 0px);
left: var(--hit-area-l, 0px);
pointer-events: inherit;
}
}
@utility hit-area-t-* {
position: relative;
--hit-area-t: --spacing(--value(number) * -1);
--hit-area-t: calc(--value([*]) * -1);
&::before {
content: "";
position: absolute;
top: var(--hit-area-t, 0px);
right: var(--hit-area-r, 0px);
bottom: var(--hit-area-b, 0px);
left: var(--hit-area-l, 0px);
pointer-events: inherit;
}
}
@utility hit-area-b-* {
position: relative;
--hit-area-b: --spacing(--value(number) * -1);
--hit-area-b: calc(--value([*]) * -1);
&::before {
content: "";
position: absolute;
top: var(--hit-area-t, 0px);
right: var(--hit-area-r, 0px);
bottom: var(--hit-area-b, 0px);
left: var(--hit-area-l, 0px);
pointer-events: inherit;
}
}
@utility hit-area-x-* {
position: relative;
--hit-area-l: --spacing(--value(number) * -1);
--hit-area-l: calc(--value([*]) * -1);
--hit-area-r: --spacing(--value(number) * -1);
--hit-area-r: calc(--value([*]) * -1);
&::before {
content: "";
position: absolute;
top: var(--hit-area-t, 0px);
right: var(--hit-area-r, 0px);
bottom: var(--hit-area-b, 0px);
left: var(--hit-area-l, 0px);
pointer-events: inherit;
}
}
@utility hit-area-y-* {
position: relative;
--hit-area-t: --spacing(--value(number) * -1);
--hit-area-t: calc(--value([*]) * -1);
--hit-area-b: --spacing(--value(number) * -1);
--hit-area-b: calc(--value([*]) * -1);
&::before {
content: "";
position: absolute;
top: var(--hit-area-t, 0px);
right: var(--hit-area-r, 0px);
bottom: var(--hit-area-b, 0px);
left: var(--hit-area-l, 0px);
pointer-events: inherit;
}
}

View File

@@ -84,7 +84,13 @@ export const MessageAction = ({
...props
}: MessageActionProps) => {
const button = (
<Button size={size} type="button" variant={variant} {...props}>
<Button
size={size}
type="button"
variant={variant}
className={cn("hit-area-2", props.className)}
{...props}
>
{children}
<span className="sr-only">{label || tooltip}</span>
</Button>