fix(app): iOS HIG Phase 5 — glass button system & .touch-target utility
- Add responsive glass-button-sm: 44px min-height on mobile (≤768px) - Replace !h-7 !min-h-0 overrides with responsive min-h-[44px] md:min-h-0 - Add .touch-target utility class (44px min, inline-flex centered) - Refactor 21 icon buttons to use .touch-target class Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
c554b0d280
commit
a0edbe7d20
+4
-4
@@ -44,13 +44,13 @@
|
||||
|
||||
## Phase 4: Touch Target Gaps & ContentPanel Tab Bar
|
||||
|
||||
- [ ] **T14** — Fix ContentPanel.vue tab bar. Tab buttons likely have `gap-1` (4px) between them. Increase to `gap-2` (8px). Ensure each tab button has at least 44px height. The tab labels should use `text-xs` minimum (from Phase 1 fixes). The tab bar should be horizontally scrollable on mobile with sufficient padding.
|
||||
- [x] **T14** — Fix ContentPanel.vue tab bar. Tab buttons likely have `gap-1` (4px) between them. Increase to `gap-2` (8px). Ensure each tab button has at least 44px height. The tab labels should use `text-xs` minimum (from Phase 1 fixes). The tab bar should be horizontally scrollable on mobile with sufficient padding.
|
||||
|
||||
- [ ] **T15** — Audit and fix ALL remaining `gap-0.5` and `gap-1` between clickable elements. Search all `.vue` files for `gap-0.5` and `gap-1`. For each result, check if the container holds clickable elements (buttons, links, tabs). If so, increase to `gap-2` (8px). Leave non-clickable layout gaps (like text spacing) unchanged.
|
||||
- [x] **T15** — Audit and fix ALL remaining `gap-0.5` and `gap-1` between clickable elements. Search all `.vue` files for `gap-0.5` and `gap-1`. For each result, check if the container holds clickable elements (buttons, links, tabs). If so, increase to `gap-2` (8px). Leave non-clickable layout gaps (like text spacing) unchanged.
|
||||
|
||||
- [ ] **T16** — Fix grid card touch targets. In all `*Grid.vue` components, verify that cards have sufficient tap area. Cards themselves are usually fine (full-width), but action buttons within cards (favorite, play, expand) need 44px targets. Check: `FilmGrid.vue`, `SongGrid.vue`, `BookGrid.vue`, `TVSeriesGrid.vue`, `PodcastGrid.vue`, `AppsGrid.vue`, `ArchyAppsGrid.vue`, `ImageGrid.vue`, `PlaceGrid.vue`.
|
||||
- [x] **T16** — Fix grid card touch targets. In all `*Grid.vue` components, verify that cards have sufficient tap area. Cards themselves are usually fine (full-width), but action buttons within cards (favorite, play, expand) need 44px targets. Check: `FilmGrid.vue`, `SongGrid.vue`, `BookGrid.vue`, `TVSeriesGrid.vue`, `PodcastGrid.vue`, `AppsGrid.vue`, `ArchyAppsGrid.vue`, `ImageGrid.vue`, `PlaceGrid.vue`.
|
||||
|
||||
- [ ] **TEST:P4** — Run `pnpm typecheck && pnpm lint && pnpm test`. Commit and push.
|
||||
- [x] **TEST:P4** — Run `pnpm typecheck && pnpm lint && pnpm test`. Commit and push.
|
||||
|
||||
## Phase 5: Glass Button System & Utility Classes
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
v-if="branches.length > 1"
|
||||
class="flex items-center justify-center px-4 py-1.5 animate-fade-up-fast"
|
||||
>
|
||||
<div class="glass-button-sm !h-7 !min-h-0 flex items-center gap-1.5 px-2 text-xs">
|
||||
<div class="glass-button-sm min-h-[44px] md:min-h-0 md:!h-7 flex items-center gap-1.5 px-2 text-xs">
|
||||
<button
|
||||
class="min-w-[44px] min-h-[44px] flex items-center justify-center rounded-md hover:bg-white/10 transition-colors text-white/60 hover:text-white/90 disabled:opacity-30 disabled:cursor-default"
|
||||
:disabled="currentIndex <= 0"
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<div class="flex items-center justify-between gap-2">
|
||||
<button
|
||||
ref="modelPickerTriggerRef"
|
||||
class="min-w-[44px] min-h-[44px] rounded-xl path-glass-icon flex items-center justify-center shrink-0 transition-colors cursor-pointer text-[#fafafa] hover:text-white"
|
||||
class="touch-target rounded-xl path-glass-icon shrink-0 transition-colors cursor-pointer text-[#fafafa] hover:text-white"
|
||||
:title="`AI model: ${modelDisplayName}`"
|
||||
aria-label="Select AI model"
|
||||
@click="showModelPicker = !showModelPicker"
|
||||
@@ -15,7 +15,7 @@
|
||||
</button>
|
||||
<div class="flex items-center gap-2 shrink-0">
|
||||
<button
|
||||
class="min-w-[44px] min-h-[44px] rounded-xl path-glass-icon flex items-center justify-center transition-colors"
|
||||
class="touch-target rounded-xl path-glass-icon transition-colors"
|
||||
:class="webSearchEnabled
|
||||
? 'text-accent'
|
||||
: 'text-white/70 hover:text-white'"
|
||||
@@ -29,7 +29,7 @@
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="min-w-[44px] min-h-[44px] rounded-xl path-glass-icon flex items-center justify-center transition-colors"
|
||||
class="touch-target rounded-xl path-glass-icon transition-colors"
|
||||
:class="chatStore.showHistory
|
||||
? 'text-accent'
|
||||
: 'text-white/70 hover:text-white'"
|
||||
@@ -43,7 +43,7 @@
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="min-w-[44px] min-h-[44px] rounded-xl path-glass-icon flex items-center justify-center transition-colors"
|
||||
class="touch-target rounded-xl path-glass-icon transition-colors"
|
||||
:class="chatStore.chatCollapsed
|
||||
? 'text-accent'
|
||||
: 'text-white/70 hover:text-white'"
|
||||
@@ -60,7 +60,7 @@
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="min-w-[44px] min-h-[44px] rounded-xl path-glass-icon flex items-center justify-center transition-colors"
|
||||
class="touch-target rounded-xl path-glass-icon transition-colors"
|
||||
:class="comparison.isComparing.value
|
||||
? 'text-accent'
|
||||
: 'text-white/70 hover:text-white'"
|
||||
@@ -74,7 +74,7 @@
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="min-w-[44px] min-h-[44px] rounded-xl path-glass-icon flex items-center justify-center transition-colors text-white/70 hover:text-white"
|
||||
class="touch-target rounded-xl path-glass-icon transition-colors text-white/70 hover:text-white"
|
||||
aria-label="Settings"
|
||||
title="Settings"
|
||||
@click="$emit('openSettings')"
|
||||
@@ -87,7 +87,7 @@
|
||||
|
||||
<button
|
||||
ref="menuTriggerRef"
|
||||
class="min-w-[44px] min-h-[44px] rounded-xl path-glass-icon flex items-center justify-center transition-colors text-white/70 hover:text-white"
|
||||
class="touch-target rounded-xl path-glass-icon transition-colors text-white/70 hover:text-white"
|
||||
aria-label="Conversation menu"
|
||||
@click="showMenu = !showMenu"
|
||||
>
|
||||
@@ -97,7 +97,7 @@
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="min-w-[44px] min-h-[44px] rounded-xl path-glass-icon flex items-center justify-center transition-colors text-white/70 hover:text-white"
|
||||
class="touch-target rounded-xl path-glass-icon transition-colors text-white/70 hover:text-white"
|
||||
aria-label="New conversation"
|
||||
@click="$emit('newChat')"
|
||||
>
|
||||
@@ -108,7 +108,7 @@
|
||||
|
||||
<button
|
||||
v-if="showClose"
|
||||
class="min-w-[44px] min-h-[44px] rounded-xl path-glass-icon flex items-center justify-center transition-colors text-white/70 hover:text-white"
|
||||
class="touch-target rounded-xl path-glass-icon transition-colors text-white/70 hover:text-white"
|
||||
aria-label="Close"
|
||||
@click="$emit('close')"
|
||||
>
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
<p class="text-xs text-white/50 truncate">{{ replyTo.excerpt }}</p>
|
||||
</div>
|
||||
<button
|
||||
class="shrink-0 min-w-[44px] min-h-[44px] flex items-center justify-center rounded-md hover:bg-white/10 transition-colors text-white/40 hover:text-white/70"
|
||||
class="shrink-0 touch-target rounded-md hover:bg-white/10 transition-colors text-white/40 hover:text-white/70"
|
||||
aria-label="Cancel reply"
|
||||
@click="$emit('clearReply')"
|
||||
>
|
||||
@@ -88,7 +88,7 @@
|
||||
<!-- Image attach button -->
|
||||
<button
|
||||
v-if="!streaming && images.length < MAX_IMAGES"
|
||||
class="shrink-0 min-w-[44px] min-h-[44px] flex items-center justify-center rounded-lg text-white/40 hover:text-white/70 hover:bg-white/10 transition-all"
|
||||
class="shrink-0 touch-target rounded-lg text-white/40 hover:text-white/70 hover:bg-white/10 transition-all"
|
||||
aria-label="Attach image"
|
||||
@click="openFilePicker"
|
||||
>
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<div class="flex items-center gap-2 px-1 py-0.5 rounded-lg bg-black/60 backdrop-blur-md border border-white/10 shadow-lg">
|
||||
<button
|
||||
v-if="isUser"
|
||||
class="min-w-[44px] min-h-[44px] flex items-center justify-center rounded-md text-white/50 hover:text-white/90 hover:bg-white/10 transition-colors"
|
||||
class="touch-target rounded-md text-white/50 hover:text-white/90 hover:bg-white/10 transition-colors"
|
||||
title="Edit message"
|
||||
@click.stop="startEditing"
|
||||
>
|
||||
@@ -23,14 +23,14 @@
|
||||
</button>
|
||||
<button
|
||||
v-if="!isUser"
|
||||
class="min-w-[44px] min-h-[44px] flex items-center justify-center rounded-md text-white/50 hover:text-white/90 hover:bg-white/10 transition-colors"
|
||||
class="touch-target rounded-md text-white/50 hover:text-white/90 hover:bg-white/10 transition-colors"
|
||||
title="Regenerate response"
|
||||
@click.stop="$emit('regenerate')"
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="w-3.5 h-3.5" viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M4 2a1 1 0 011 1v2.101a7.002 7.002 0 0111.601 2.566 1 1 0 11-1.885.666A5.002 5.002 0 005.999 7H9a1 1 0 010 2H4a1 1 0 01-1-1V3a1 1 0 011-1zm.008 9.057a1 1 0 011.276.61A5.002 5.002 0 0014.001 13H11a1 1 0 110-2h5a1 1 0 011 1v5a1 1 0 11-2 0v-2.101a7.002 7.002 0 01-11.601-2.566 1 1 0 01.61-1.276z" clip-rule="evenodd" /></svg>
|
||||
</button>
|
||||
<button
|
||||
class="min-w-[44px] min-h-[44px] flex items-center justify-center rounded-md text-white/50 hover:text-white/90 hover:bg-white/10 transition-colors"
|
||||
class="touch-target rounded-md text-white/50 hover:text-white/90 hover:bg-white/10 transition-colors"
|
||||
title="Reply"
|
||||
@click.stop="$emit('reply', message.id, message.content)"
|
||||
>
|
||||
@@ -38,7 +38,7 @@
|
||||
</button>
|
||||
<button
|
||||
v-if="!isUser"
|
||||
class="min-w-[44px] min-h-[44px] flex items-center justify-center rounded-md text-white/50 hover:text-white/90 hover:bg-white/10 transition-colors"
|
||||
class="touch-target rounded-md text-white/50 hover:text-white/90 hover:bg-white/10 transition-colors"
|
||||
title="Branch from here"
|
||||
@click.stop="$emit('branch', message.id)"
|
||||
>
|
||||
@@ -47,7 +47,7 @@
|
||||
<div v-if="!isUser" class="w-px h-4 bg-white/10 mx-0.5" />
|
||||
<button
|
||||
v-if="!isUser"
|
||||
class="min-w-[44px] min-h-[44px] flex items-center justify-center rounded-md transition-colors"
|
||||
class="touch-target rounded-md transition-colors"
|
||||
:class="message.feedback === 'up' ? 'text-green-400' : 'text-white/50 hover:text-green-400/80 hover:bg-white/10'"
|
||||
title="Good response"
|
||||
@click.stop="toggleFeedback('up')"
|
||||
@@ -56,7 +56,7 @@
|
||||
</button>
|
||||
<button
|
||||
v-if="!isUser"
|
||||
class="min-w-[44px] min-h-[44px] flex items-center justify-center rounded-md transition-colors"
|
||||
class="touch-target rounded-md transition-colors"
|
||||
:class="message.feedback === 'down' ? 'text-red-400' : 'text-white/50 hover:text-red-400/80 hover:bg-white/10'"
|
||||
title="Poor response"
|
||||
@click.stop="toggleFeedback('down')"
|
||||
@@ -83,11 +83,11 @@
|
||||
/>
|
||||
<div class="flex gap-2 justify-end">
|
||||
<button
|
||||
class="glass-button-sm !h-7 !min-h-0 text-xs text-white/60 hover:text-white/90 px-3"
|
||||
class="glass-button-sm min-h-[44px] md:min-h-0 md:!h-7 text-xs text-white/60 hover:text-white/90 px-3"
|
||||
@click="cancelEdit"
|
||||
>Cancel</button>
|
||||
<button
|
||||
class="glass-button-sm !h-7 !min-h-0 text-xs bg-accent/20 text-accent hover:bg-accent/30 px-3"
|
||||
class="glass-button-sm min-h-[44px] md:min-h-0 md:!h-7 text-xs bg-accent/20 text-accent hover:bg-accent/30 px-3"
|
||||
@click="submitEdit"
|
||||
>Save & Resend</button>
|
||||
</div>
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
{{ matchCount > 0 ? `${currentMatchIndex + 1}/${matchCount}` : 'No results' }}
|
||||
</span>
|
||||
<button
|
||||
class="min-w-[44px] min-h-[44px] flex items-center justify-center rounded-md hover:bg-white/10 transition-colors text-white/50 hover:text-white/80 disabled:opacity-30"
|
||||
class="touch-target rounded-md hover:bg-white/10 transition-colors text-white/50 hover:text-white/80 disabled:opacity-30"
|
||||
:disabled="matchCount === 0"
|
||||
aria-label="Previous match"
|
||||
@click="prevMatch"
|
||||
@@ -32,7 +32,7 @@
|
||||
</svg>
|
||||
</button>
|
||||
<button
|
||||
class="min-w-[44px] min-h-[44px] flex items-center justify-center rounded-md hover:bg-white/10 transition-colors text-white/50 hover:text-white/80 disabled:opacity-30"
|
||||
class="touch-target rounded-md hover:bg-white/10 transition-colors text-white/50 hover:text-white/80 disabled:opacity-30"
|
||||
:disabled="matchCount === 0"
|
||||
aria-label="Next match"
|
||||
@click="nextMatch"
|
||||
@@ -42,7 +42,7 @@
|
||||
</svg>
|
||||
</button>
|
||||
<button
|
||||
class="min-w-[44px] min-h-[44px] flex items-center justify-center rounded-md hover:bg-white/10 transition-colors text-white/50 hover:text-white/80"
|
||||
class="touch-target rounded-md hover:bg-white/10 transition-colors text-white/50 hover:text-white/80"
|
||||
aria-label="Close search"
|
||||
@click="close"
|
||||
>
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
{{ panelTitle }}
|
||||
</h3>
|
||||
<button
|
||||
class="min-w-[44px] min-h-[44px] flex items-center justify-center rounded-lg transition-colors"
|
||||
class="touch-target rounded-lg transition-colors"
|
||||
:class="isDark
|
||||
? 'text-white/70 hover:bg-white/10'
|
||||
: 'text-gray-500 hover:bg-black/5'"
|
||||
|
||||
@@ -126,6 +126,24 @@ body {
|
||||
padding-inline: 0.75rem;
|
||||
}
|
||||
|
||||
.touch-target {
|
||||
min-width: 44px;
|
||||
min-height: 44px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.glass-button {
|
||||
min-height: 48px;
|
||||
}
|
||||
|
||||
.glass-button-sm {
|
||||
min-height: 44px !important;
|
||||
}
|
||||
}
|
||||
|
||||
.gradient-card {
|
||||
background: linear-gradient(135deg, rgba(255, 255, 255, 0.1) 0%, rgba(0, 0, 0, 0.8) 100%);
|
||||
backdrop-filter: blur(18px);
|
||||
|
||||
Reference in New Issue
Block a user