Touch-target audit: 44px minimum on mobile interactive elements (Slice 9)

Apply the mobile-touch-target CSS class to 40 interactive elements across
12 files. The class applies min-height/min-width:44px only below md
(max-width:767px), satisfying WCAG 2.5.5 / Apple HIG on touch devices.
Desktop behavior is unchanged.

Audit log (before -> after hit-area):
- App.tsx: hamburger/dark-mode/sign-out (32/32/28 -> 44)
- Dashboard.tsx: shortcut open/edit/delete (28 -> 44), enabled switch (18 -> 44)
- Media.tsx: mobile pagination prev/next (28 -> 44)
- FileBrowser.impl.tsx: 'Open Settings' alert button (28 -> 44)
- UsersPage.impl.tsx: compose toolbar bold/italic/link/list (32 -> 44),
  attachment remove button (16 -> 44)
- Settings.tsx: machine switch (18 -> 44), clear/add-machine buttons (28 -> 44),
  reset-db checkboxes x3 (16 -> 44)
- Actions.tsx: 'Add action' button (28 -> 44)
- ServicePage.tsx: service enabled switch (18 -> 44)
- ServicesPage.tsx: service switch/open-link/delete-icon (18/28/32 -> 44)
- ObservabilityPage.tsx: retry + 4 asChild link buttons (28 -> 44)
- WidgetConfigDialog.tsx: 4 icon buttons (32 -> 44), 2 switches (18 -> 44),
  2 add-widget buttons (28 -> 44)
- SessionActivityPanel.tsx: 'Open in Users' button (28 -> 44)

Deliberately skipped: default-size text buttons (32px, borderline), desktop-only
sidebar toggle, DataTable internals (desktop-only below md), Select triggers.
Dashboard anchor pills and HoverEditButton already had the class from Slices 1/2.

No new tests (the class applies via @media which jsdom doesn't honor).
116 tests pass; lint/build green.

Refs openspec/changes/mobile-responsive-parity/ (spec R6, tasks slice 9).
This commit is contained in:
Developer
2026-06-26 14:37:40 +00:00
parent 7808822a55
commit 30f1b6e6db
12 changed files with 45 additions and 24 deletions
+1 -1
View File
@@ -367,7 +367,7 @@ export function Actions() {
<Button
variant="outline"
size="sm"
className="w-full"
className="mobile-touch-target w-full"
onClick={createNew}
>
Add action
+4 -2
View File
@@ -354,6 +354,7 @@ function ShortcutDialog({
<div className="flex items-center gap-2">
<Switch
id="shortcut-enabled"
className="mobile-touch-target"
checked={draft.enabled}
onCheckedChange={(checked) =>
onChange({ ...draft, enabled: checked })
@@ -424,13 +425,14 @@ function ShortcutCard({
size="sm"
disabled={!shortcut.enabled || !href}
onClick={onOpen}
className="mobile-touch-target"
>
Open
</Button>
<Button size="sm" variant="outline" onClick={onEdit}>
<Button size="sm" variant="outline" onClick={onEdit} className="mobile-touch-target">
Edit
</Button>
<Button size="sm" variant="destructive" onClick={onDelete}>
<Button size="sm" variant="destructive" onClick={onDelete} className="mobile-touch-target">
Delete
</Button>
</div>
+1
View File
@@ -879,6 +879,7 @@ export function FileBrowser() {
variant="outline"
size="sm"
onClick={() => navigateToSettings("/settings")}
className="mobile-touch-target"
>
Open Settings
</Button>
+2
View File
@@ -167,6 +167,7 @@ function MediaMobilePagination({
}
disabled={pageIndex <= 0}
aria-label="Previous page"
className="mobile-touch-target"
>
Previous
</Button>
@@ -180,6 +181,7 @@ function MediaMobilePagination({
}))
}
disabled={pageIndex >= pageCount - 1}
className="mobile-touch-target"
aria-label="Next page"
>
Next
+1
View File
@@ -238,6 +238,7 @@ export function ServicePage() {
<div className="flex items-center gap-2">
<Switch
id="service-enabled"
className="mobile-touch-target"
checked={enabled}
onCheckedChange={setEnabled}
/>
+3 -1
View File
@@ -234,6 +234,7 @@ function CreateServiceDialog({
<div className="flex items-center gap-2">
<Switch
id="service-enabled"
className="mobile-touch-target"
checked={draft.enabled}
onCheckedChange={(checked) =>
setDraft({ ...draft, enabled: checked })
@@ -328,6 +329,7 @@ export function ServicesPage() {
<Button
variant="ghost"
size="sm"
className="mobile-touch-target"
onClick={() =>
navigate(`/services/${s.service_type}/${s.id}`)
}
@@ -337,7 +339,7 @@ export function ServicesPage() {
<Button
variant="ghost"
size="icon"
className="h-8 w-8 text-destructive"
className="mobile-touch-target h-8 w-8 text-destructive"
onClick={() => setDeleteId(s.id)}
>
<Trash2 className="h-4 w-4" />
+6 -2
View File
@@ -232,6 +232,7 @@ function MachineEditor({
<div className="flex items-center gap-2">
<Switch
id="machine-enabled"
className="mobile-touch-target"
checked={draft.enabled}
onCheckedChange={(checked) =>
setDraft((current) => ({ ...current, enabled: checked }))
@@ -534,7 +535,7 @@ function SSHKeyManager({
<Button
variant="outline"
size="sm"
className="w-full"
className="mobile-touch-target w-full"
onClick={() => {
clear();
}}
@@ -782,6 +783,7 @@ function ResetLocalDatabaseCard() {
<div className="flex flex-col gap-3">
<label className="flex items-center gap-2 text-sm">
<Checkbox
className="mobile-touch-target"
checked={ackSettings}
onCheckedChange={(checked) => setAckSettings(Boolean(checked))}
/>
@@ -789,6 +791,7 @@ function ResetLocalDatabaseCard() {
</label>
<label className="flex items-center gap-2 text-sm">
<Checkbox
className="mobile-touch-target"
checked={ackIndex}
onCheckedChange={(checked) => setAckIndex(Boolean(checked))}
/>
@@ -796,6 +799,7 @@ function ResetLocalDatabaseCard() {
</label>
<label className="flex items-center gap-2 text-sm">
<Checkbox
className="mobile-touch-target"
checked={ackIrreversible}
onCheckedChange={(checked) =>
setAckIrreversible(Boolean(checked))
@@ -973,7 +977,7 @@ export function Settings() {
<Button
variant="outline"
size="sm"
className="w-full"
className="mobile-touch-target w-full"
onClick={() => {
clearSSHValidation();
setMachineDraft(emptyMachine("local"));
+9 -5
View File
@@ -891,9 +891,10 @@ export function UsersPage() {
<div className="flex flex-wrap gap-1">
<Tooltip>
<TooltipTrigger asChild>
<UiButton
<UiButton
variant="ghost"
size="icon"
className="mobile-touch-target"
onClick={() => insertMarkup("<strong>", "</strong>")}
aria-label="Bold"
>
@@ -904,9 +905,10 @@ export function UsersPage() {
</Tooltip>
<Tooltip>
<TooltipTrigger asChild>
<UiButton
<UiButton
variant="ghost"
size="icon"
className="mobile-touch-target"
onClick={() => insertMarkup("<em>", "</em>")}
aria-label="Italic"
>
@@ -917,9 +919,10 @@ export function UsersPage() {
</Tooltip>
<Tooltip>
<TooltipTrigger asChild>
<UiButton
<UiButton
variant="ghost"
size="icon"
className="mobile-touch-target"
onClick={addLink}
aria-label="Link"
>
@@ -930,9 +933,10 @@ export function UsersPage() {
</Tooltip>
<Tooltip>
<TooltipTrigger asChild>
<UiButton
<UiButton
variant="ghost"
size="icon"
className="mobile-touch-target"
onClick={() => insertMarkup("<ul><li>", "</li></ul>")}
aria-label="Bullet list"
>
@@ -994,7 +998,7 @@ export function UsersPage() {
type="button"
aria-label={`Remove ${file.name}`}
onClick={() => removeAttachment(index)}
className="inline-flex items-center text-current [&>svg]:size-3"
className="mobile-touch-target inline-flex items-center text-current [&>svg]:size-3"
>
<Trash2 />
</button>