chore(loop): iOS HIG compliance plan — 24 tasks across 7 phases

Font sizes (text-[10px]→text-xs, text-[8-9px]→text-[11px]),
touch targets (44×44px minimum for all buttons), input zoom
prevention (16px minimum), gap compliance (8px between targets),
glass button system updates, and comprehensive verification.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Dorian
2026-03-04 12:43:44 +00:00
co-authored by Claude Opus 4.6
parent bda670d8d9
commit dde3859a2e
2 changed files with 124 additions and 81 deletions
+53 -29
View File
@@ -1,60 +1,84 @@
# Overnight Plan — Archy Integration, Content Hardening & Feature Completion # Overnight Plan — iOS HIG Compliance & Continued Hardening
> **Format**: `- [ ]` = pending, `- [x]` = done. > **Format**: `- [ ]` = pending, `- [x]` = done.
> Make at least 30 attempts on any difficult task before moving on. Loop reads this file. > Make at least 30 attempts on any difficult task before moving on. Loop reads this file.
> After each phase, commit and push.
## Phase 1: Critical ContentPanel Wiring Gaps ## Phase 1: Input & Text Foundation (Prevent iOS Zoom + Readability)
- [x] **T1**Wire ImageGrid + ImageDetail into ContentPanel.vue. Import components, destructure `panelImages`, `selectedImage`, `openImageDetail`, `closeImageDetail` from `useContentPanel()`. Add ImageGrid render block following BookGrid/TVSeriesGrid pattern. Add ImageDetail overlay following BookDetail pattern. Add `selectedImage` to `hasDetailOpen` computed. Reference ContentGridView.vue for how ImageGrid is already used there. - [ ] **T1**Fix all input/textarea font sizes to prevent iOS auto-zoom. Every `<input>`, `<textarea>`, and `<select>` must use `text-base` (16px) minimum. Files to fix: `ChatInput.vue` (main message textarea uses `text-sm`), `ChatSearch.vue` (search input), `SettingsModal.vue` (memory inputs use `text-xs`), `PersonaSelector.vue`, `MemoryPanel.vue`, `PluginSettingsForm.vue`, `ShareToNostr.vue`, `AdvancedSettings.vue`, `PromptPalette.vue`. Search for all `<input` and `<textarea` tags across `packages/app/src/` and verify each has at least `text-base`. Do NOT change the visual design — only increase font size on the form elements themselves.
- [x] **T2**Wire PlaceGrid + PlaceDetail into ContentPanel.vue. Import components, destructure `panelPlaces`, `selectedPlace`, `openPlaceDetail`, `closePlaceDetail` from `useContentPanel()`. Add PlaceGrid render block + PlaceDetail overlay. Add `selectedPlace` to `hasDetailOpen` computed. Follow same patterns as T1. - [ ] **T2**Replace all `text-[10px]` with `text-xs` (12px) across the entire app. There are ~301 instances. Use find-and-replace but verify each file still looks correct. The main offenders: `ChatMessage.vue` (timestamps, token counts, edit labels), `SettingsModal.vue` (section headers), `ContentPanel.vue` (tab labels), all `*Detail.vue` and `*Grid.vue` files. Run `grep -r 'text-\[10px\]' packages/app/src/` to find all instances. Replace with `text-xs`. This is the single biggest compliance fix.
- [x] **T3**Verify code tab rendering in ContentPanel.vue. Ensure `panelCodeBlocks` is destructured and code content renders when the code tab is active. If missing, add a code block display component or inline rendering with syntax highlighting. The code tab should show extracted code blocks from AI responses. - [ ] **T3**Replace all `text-[9px]` and `text-[8px]` with `text-[11px]` (iOS absolute minimum). There are ~100 instances. These are used in badges, version numbers, metadata labels. Files: `PluginMarketplace.vue`, `NostrGrid.vue`, `DesignSystemDetail.vue`, `DesignSystemGrid.vue`, settings components. Search with `grep -r 'text-\[9px\]\|text-\[8px\]' packages/app/src/`. Replace with `text-[11px]`.
- [x] **TEST:P1** — Run `cd packages/app && npx vitest run` and `pnpm typecheck && pnpm lint`. Fix all failures before proceeding. - [ ] **T4** — Replace all `text-[11px]` with `text-xs` (12px). There are ~76 instances plus the new ones from T3. These are in `SettingsModal.vue`, `BranchSwitcher.vue`, `DesignSystemGrid.vue`. After T3 creates new `text-[11px]` instances, this task bumps them all to `text-xs`. Search with `grep -r 'text-\[11px\]' packages/app/src/`. Replace with `text-xs`.
## Phase 2: Archy Service Integration into AIUI - [ ] **TEST:P1** — Run `pnpm typecheck && pnpm lint && pnpm test`. Fix all failures. Commit and push.
- [x] **T4** — Extend useArchy composable to support richer context categories. Add new reactive refs and fetch methods for: `wallet` (Lightning balance, channel count, node pubkey), `files` (Nextcloud file listings — name, path, size, modified date), `media` (photo count, recent photos, music files). Update `buildArchyContext()` to include wallet balance, file counts, and media summary in the AI system prompt. Update `fetchPermittedContext()` to request these new categories from the bridge. The data format should match what Archy's contextBroker will return (see archyBridge.ts categories: 'apps' | 'system' | 'network' | 'wallet' | 'files'). File: `packages/app/src/composables/useArchy.ts` ## Phase 2: Touch Targets — Chat Interface (44×44px Minimum)
- [x] **T5**Create Archy app cards for AIUI. Build a data file at `packages/app/src/data/archy-apps.ts` that maps Archy service IDs to AIUI-friendly metadata: display name, description, icon (use emoji for now), category, default port, and deep-link URL pattern (e.g., `/app/nextcloud`, `/app/mempool`). Include all major services: bitcoin-core, lnd, core-lightning, btcpay-server, mempool, nextcloud, immich, nostr-rs-relay, home-assistant, grafana, searxng, ollama, penpot, onlyoffice, fedimint, meshtastic. When running in Archy, merge this with live `installedApps` data from useArchy to show actual status. File: `packages/app/src/data/archy-apps.ts` - [ ] **T5**Fix ChatMessage.vue action buttons. The edit/reply/branch/feedback buttons use `w-7 h-7` (28px). Change the button wrapper to `min-w-[44px] min-h-[44px]` with `flex items-center justify-center` while keeping the inner SVG icon at `w-4 h-4` or `w-5 h-5`. This makes the tap target 44px but the visual icon stays the same size. Also fix the `gap-0.5` (2px) between action buttons to `gap-2` (8px). Apply to all button rows in this file.
- [x] **T6**Create an ArchyAppsGrid component. Build `packages/app/src/components/content/ArchyAppsGrid.vue` that displays Archy node apps in a grid. Each card shows: app name, status (running/stopped/not installed), description snippet, and an action button (Open if running, Install if not). Use glass-card styling. On click, call `useArchy().requestAction('open-app', { appId })` for running apps. When not embedded in Archy, show the apps as informational cards without action buttons. Wire this into ContentPanel.vue under a new conditional: when the 'app' tab is active AND `useArchy().isEmbedded`, show ArchyAppsGrid above or instead of the generic AppsGrid. - [ ] **T6**Fix ChatHeader.vue toolbar buttons. All 9 buttons use `w-8 h-8` or `w-9 h-9` (32-36px). Wrap each in a 44px touch target: `min-w-[44px] min-h-[44px] flex items-center justify-center`. Keep the visual icon sizes unchanged. Fix any `gap-1` between toolbar buttons to `gap-2`.
- [x] **T7**Wire Archy wallet context into AI responses. When the AI mentions Bitcoin/Lightning and Archy wallet data is available, the system prompt should include: wallet balance (sats), number of channels, total capacity. This lets the AI say things like "Your node has 500,000 sats across 3 channels" when relevant. In `useArchy.ts`, add `walletInfo` ref with `{ balanceSats?: number, channelCount?: number, totalCapacitySats?: number, nodePubkey?: string }`. In `buildArchyContext()`, append wallet section when data exists. - [ ] **T7**Fix ChatInput.vue buttons. The image attach button (`w-8 h-8`), close reply button (`w-5 h-5`), and send button need 44px minimum touch targets. Also fix the send button row gap to 8px minimum between targets.
- [x] **T8**Wire Archy file context for AI awareness. When files permission is granted, the AI should know what files the user has. In `useArchy.ts`, add `fileList` ref as `{ name: string, path: string, size?: number, modified?: string, type: 'file' | 'folder' }[]`. In `buildArchyContext()`, summarize: "You have X files in Nextcloud. Recent files: file1.pdf, file2.jpg, notes/todo.md". Limit to 20 most recent files to keep prompt concise. The AI can then reference user's files conversationally. - [ ] **T8**Fix ChatSearch.vue navigation buttons. Previous/Next/Close buttons use `w-6 h-6` (24px). Expand to 44px touch targets with padding.
- [x] **TEST:P2**Run `pnpm test && pnpm typecheck && pnpm lint`. Fix all failures before proceeding. - [ ] **T9**Fix ArticleReader.vue and PdfViewer.vue controls. Close/navigation buttons use `w-8 h-8` (32px). Expand to 44px. Fix button gaps.
## Phase 3: Content Extraction Hardening - [ ] **TEST:P2** — Run `pnpm typecheck && pnpm lint && pnpm test`. Commit and push.
- [x] **T9** — Expand extraction quality test suite to 100+ tests. Add tests for: podcast extraction (library `[[podcast:p1]]` tags + `[[podcast_ext:]]` tags), app extraction (`extractApps`), film extraction (library `[[film:f1]]` tags + `[[film_ext:]]` tags), magazine section extraction, websites/news detection from markdown links and bare domain extraction, edge cases (empty strings, very long text, unicode characters, special chars in titles), mixed content with 3+ types in one response, `filterTabsByContext` with every specialized path (news+TV, nostr+apps, app+code, etc). Each test should use realistic AI response text patterns. File: `packages/app/src/__tests__/extractionQuality.test.ts` ## Phase 3: Touch Targets — Content & Settings Components
- [x] **T10** — Fix any extraction bugs found during T9 testing. Run the expanded test suite, fix all failures in `contentExtraction.ts` and/or `contentFiltering.ts`. Iterate until all tests pass. - [ ] **T10** — Fix all content detail view buttons. Search for `w-7 h-7`, `w-6 h-6`, `w-5 h-5` button patterns in: `BookDetail.vue`, `FilmDetail.vue`, `TVSeriesDetail.vue`, `SongDetail.vue`, `PodcastDetail.vue`, `PlaceDetail.vue`, `ImageDetail.vue`, `AppDetail.vue`, `MagazineSectionDetail.vue`, `NostrGrid.vue`. Expand all interactive buttons to 44px minimum touch targets. Keep icon sizes unchanged.
- [x] **T11**Verify filterTabsByContext prioritization for all paths. Write specific tests ensuring: news queries show news/magazine first but include TV/books/etc if present, nostr queries show nostr first, app queries show apps first, preferred tab from `preferredFirstTab()` is always first in general case, no content type is ever silently dropped. File: `packages/app/src/__tests__/extractionQuality.test.ts` - [ ] **T11**Fix SettingsModal.vue and SettingsPanel.vue buttons. Close button (`w-7 h-7`), color picker buttons (`w-8 h-8`), edit/delete buttons (`w-5 h-5`). All need 44px. Fix all `gap-0.5` to `gap-2`.
- [x] **TEST:P3**Run `cd packages/app && npx vitest run src/__tests__/extractionQuality.test.ts` — must have 100+ tests all passing. Then run `pnpm typecheck && pnpm lint`. - [ ] **T12**Fix MemoryPanel.vue, PersonaSelector.vue, and PluginMarketplace.vue buttons. Edit/delete memory buttons (`w-5 h-5`), persona buttons, plugin action buttons. All need 44px touch targets with 8px gaps.
## Phase 4: Archy Integration Tests - [ ] **T13** — Fix MapRenderer.vue fullscreen button, BranchSwitcher.vue buttons, and any remaining small buttons found by searching for `w-5 h-5`, `w-6 h-6`, `w-7 h-7`, `w-8 h-8` on clickable elements across all `.vue` files. Each interactive element needs 44px minimum.
- [x] **T12**Create Archy integration test suite. File: `packages/app/src/__tests__/archyIntegration.test.ts`. Test: archyBridge message handling (mock postMessage for context:response, theme:response, permissions:update), useArchy composable (init when embedded, buildArchyContext output format with various data combinations, fetchPermittedContext with mocked bridge, requestAction delegation). Mock `window.parent` and `window.addEventListener`. Test wallet/file context formatting. Test base-aware API paths resolve correctly for both `/` and `/aiui/` base paths. - [ ] **TEST:P3**Run `pnpm typecheck && pnpm lint && pnpm test`. Commit and push.
- [x] **T13** — Test ArchyAppsGrid component. Write tests verifying: renders app cards from archy-apps.ts data, shows correct status when merged with live data, action buttons work (open-app calls requestAction), graceful fallback when not embedded (shows informational cards only), glass-card styling applied. ## Phase 4: Touch Target Gaps & ContentPanel Tab Bar
- [x] **TEST:P4**Run `pnpm test && pnpm typecheck && pnpm lint`. All tests must pass. - [ ] **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.
## Phase 5: Code Mode UX Completion - [ ] **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] **T14**Add Esc key to exit code mode on desktop. In ChatWindow.vue or ChatPage.vue, add a `keydown` event listener for Escape that calls `exitCodeMode()` from `useCodeContext` when code mode is active (`codeMode.value === true`). Only handle on desktop (not mobile). Add/cleanup in onMounted/onUnmounted. - [ ] **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] **T15**Show selected project indicator near chat input when in code mode. When `codeMode` is true and a project is selected (`currentProject` from `useCodeContext`), display the project name as a small pill/badge near the chat input area. Use accent/orange styling (bg-accent/15, text-accent). - [ ] **TEST:P4**Run `pnpm typecheck && pnpm lint && pnpm test`. Commit and push.
- [x] **T16** — Wire selected design tokens + files into AI context. When sending a message in code mode, include selected design tokens and file contents as context in the system prompt. Read from `useCodeContext()`. Keep concise. ## Phase 5: Glass Button System & Utility Classes
- [x] **TEST:P5**Run `pnpm typecheck && pnpm lint`. Fix all failures before proceeding. - [ ] **T17**Update `.glass-button` and `.glass-button-sm` in `packages/app/src/styles/main.css` to enforce 44px minimum height on mobile. Add a responsive rule: on screens ≤ 768px, `.glass-button` gets `min-height: 48px` and `.glass-button-sm` gets `min-height: 44px`. This catches all uses of these classes automatically. On desktop they stay as-is.
## Phase 6: Final Verification & Cleanup - [ ] **T18** — Audit all `!h-7 !min-h-0` overrides. These force 28px height, defeating the glass button minimum. Search all `.vue` files for `!h-7` and `!min-h-0`. Replace with mobile-responsive approach: keep desktop size but ensure 44px minimum on mobile using `min-h-[44px] md:min-h-0`.
- [x] **T17**Final verification pass. Run `pnpm test` (all tests pass), `pnpm typecheck` (no type errors), `pnpm lint` (0 errors). Fix any issues. Commit all remaining changes with a summary commit message. Push to remote. - [ ] **T19**Add a `.touch-target` utility class to `main.css`: `.touch-target { min-width: 44px; min-height: 44px; display: inline-flex; align-items: center; justify-content: center; }`. Then refactor at least 15 icon buttons across the app to use this class instead of manual min-w/min-h. This creates a reusable pattern for future development.
- [ ] **TEST:P5** — Run `pnpm typecheck && pnpm lint && pnpm test`. Commit and push.
## Phase 6: Modals, Dialogs & Overlays
- [ ] **T20** — Fix PassphraseDialog.vue. Submit button uses `h-10` (40px, 4px short). Change to `h-11` (44px). Verify input field is `text-base`. Ensure the dialog has proper padding for mobile.
- [ ] **T21** — Audit all modal/overlay/dialog components for mobile compliance. Check: `ArticleOverlay.vue`, `ShareToNostr.vue`, `ConversationExport.vue`, `ComparisonMode.vue`, `NostrProfileEditor.vue`, `NostrRelayManager.vue`. Each should have: 44px button touch targets, 16px input text, 8px button gaps, proper padding on mobile.
- [ ] **T22** — Fix the sidebar/conversation list touch targets. `ConversationList.vue` or sidebar entries should have at least 44px height per item. Delete/rename buttons within list items need 44px targets.
- [ ] **TEST:P6** — Run `pnpm typecheck && pnpm lint && pnpm test`. Commit and push.
## Phase 7: Comprehensive Verification
- [ ] **T23** — Run a final grep audit to verify NO remaining violations:
- `text-[10px]`, `text-[9px]`, `text-[8px]` — should return 0 results in `packages/app/src/`
- `text-[11px]` — should return 0 results (all bumped to text-xs)
- `gap-0.5` between clickable elements — should be 0
- All inputs have `text-base` minimum
Fix any remaining violations found.
- [ ] **T24** — Run `pnpm test` (all tests pass), `pnpm typecheck` (no errors), `pnpm lint` (0 errors). Final commit with summary message. Push to remote.
+71 -52
View File
@@ -1,83 +1,102 @@
You are hardening the AIUI content extraction pipeline, integrating Archy services, and completing incomplete features. Read these files first: You are making the AIUI app compliant with iOS Human Interface Guidelines (HIG) for font sizes, touch targets, and button hitboxes — WITHOUT changing the visual design. Read these files first:
1. `loop/plan.md` — Your task checklist (mark items `- [x]` as you complete them) 1. `loop/plan.md` — Your task checklist (mark items `- [x]` as you complete them)
2. `CLAUDE.md` — Project conventions, design system rules, and coding standards 2. `CLAUDE.md` — Project conventions, design system rules, and coding standards
## Key Context ## Key Principle
### Content Extraction Pipeline **Same design, better accessibility.** The app should look identical but be properly tappable and readable on mobile. All changes are about sizing minimums, not visual redesign.
- `packages/app/src/composables/contentExtraction.ts` — Extracts books, films, TV, songs, podcasts, places, images, code blocks, apps from AI response text
- `packages/app/src/composables/contentFiltering.ts` — Query/response classifiers (isBookQuery, isTVQuery, etc.) and tab routing (filterTabsByContext)
- `packages/app/src/composables/useContentPanel.ts` — Wires extraction functions to Vue reactive state and tab system
- `packages/app/src/components/content/ContentPanel.vue` — Main content panel rendering grids and detail views
- `packages/app/src/components/content/ContentGridView.vue` — Grid view (already has ImageGrid/PlaceGrid wired)
- `packages/app/src/__tests__/extractionQuality.test.ts` — Extraction quality test suite (currently 64 tests)
Content flows: AI response text → extraction functions → useContentPanel refs → filterTabsByContext → ContentPanel/ContentGridView rendering ## iOS HIG Requirements
### Archy Integration (CRITICAL — this is the main new work) 1. **Font Sizes:**
- `packages/app/src/services/archyBridge.ts` — PostMessage client for AIUI ↔ Archipelago IPC (already implemented) - Body text: 17px (but we use 14px base — OK, it's a dense app)
- `packages/app/src/composables/useArchy.ts` — Reactive Vue wrapper (just created, needs extension) - Footnote: 13px minimum
- `packages/app/src/composables/useAI.ts` — Already injects Archy context via `buildArchyContext()` in `buildSystemPrompt()` - Caption: 11px ABSOLUTE minimum (nothing smaller than 11px ever)
- Form inputs: 16px minimum (prevents iOS auto-zoom on focus)
Archy is a self-hosted Linux node OS with 23+ apps. AIUI runs as an iframe inside Archy at `/aiui/`. Communication is via postMessage with 5 context categories: apps, system, network, wallet, files. The bridge is initialized when `?embedded=true` is in the URL. 2. **Touch Targets:**
- All interactive elements: 44×44px minimum tappable area
- The VISUAL icon can be smaller (e.g., 20px SVG), but the tappable wrapper must be 44×44px
- Pattern: `<button class="min-w-[44px] min-h-[44px] flex items-center justify-center"><svg class="w-5 h-5" ...></button>`
**Archy Services Available** (that AIUI should know about): 3. **Gaps Between Targets:**
- Bitcoin Core (blockchain data, UTXO, fee estimates) - Minimum 8px (gap-2) between any two tappable elements
- LND / Core Lightning / Lightning Stack (channels, payments, invoices) - `gap-0.5` (2px) and `gap-1` (4px) between buttons is a violation
- BTCPay Server (payment processing)
- Mempool (blockchain explorer)
- Nextcloud (files, notes, contacts, calendar via WebDAV)
- Immich (photos, ML-tagging)
- Nostr relays (nostr-rs-relay, strfry)
- Home Assistant (smart home)
- Grafana (monitoring)
- SearXNG (privacy search)
- Ollama (local LLM)
- Penpot (design)
- OnlyOffice (documents)
- Fedimint (federated custody)
- Meshtastic (mesh networking)
- DID Wallet (Web5 identity)
**Data Handshake**: AIUI requests context via `archyBridge.requestContext(category)`. Archy's contextBroker responds with sanitized data (no secrets, no IPs, no keys). Currently `wallet` and `files` categories are stubs in Archy — build the AIUI consumer side anyway so it's ready when Archy implements them. Use realistic mock shapes. ## Current Violations Found (from audit)
**API Path Note**: All API paths use `import.meta.env.BASE_URL` prefix so they work at both `/` (dev) and `/aiui/` (production). This was just implemented — don't change it. - **301× `text-[10px]`** — timestamps, labels, metadata throughout app
- **76× `text-[11px]`** — settings, branch switcher, design system
- **100× `text-[9px]` / `text-[8px]`** — badges, version numbers, plugin metadata
- **40+ buttons below 44×44px** — `w-7 h-7` (28px), `w-8 h-8` (32px), `w-6 h-6` (24px), `w-5 h-5` (20px)
- **20+ locations with gap < 8px** between clickable elements
- **9+ inputs below 16px** — ChatInput textarea, settings inputs, search
## Key Files
- `packages/app/src/styles/main.css` — Glass button system, utility classes
- `packages/app/src/components/chat/ChatMessage.vue` — Message actions, timestamps
- `packages/app/src/components/chat/ChatInput.vue` — Main input, send button
- `packages/app/src/components/chat/ChatHeader.vue` — Toolbar buttons
- `packages/app/src/components/chat/ChatSearch.vue` — Search nav buttons
- `packages/app/src/components/chat/SettingsModal.vue` — Settings UI
- `packages/app/src/components/content/ContentPanel.vue` — Tab bar
- `packages/app/src/components/content/*Grid.vue` — All content grids
- `packages/app/src/components/content/*Detail.vue` — All detail views
- `packages/app/src/components/settings/` — Settings components
- `packages/app/src/components/ui/` — Shared UI components
## Design System Rules (must follow for every file you touch) ## Design System Rules (must follow for every file you touch)
- **Glass morphism only** — use `.glass`, `.glass-card`, `.glass-button`, `.glass-button-sm` from `src/styles/main.css` - **Glass morphism only** — use `.glass`, `.glass-card`, `.glass-button`, `.glass-button-sm` from `src/styles/main.css`
- **No light-mode conditionals** — never use `isDark ?` pattern. The app is dark-only. Remove any existing light-mode code you encounter. - **No light-mode conditionals** — never use `isDark ?` pattern. The app is dark-only.
- **No `bg-gray-*` or `bg-white`** — use `bg-white/5`, `bg-white/10`, `bg-black/35` etc. - **No `bg-gray-*` or `bg-white`** — use `bg-white/5`, `bg-white/10`, `bg-black/35` etc.
- **Text opacity scale** — `text-white/25``/40``/60``/70``/80``/90``/96``text-white` - **Text opacity scale** — `text-white/25``/40``/60``/70``/80``/90``/96``text-white`
- **Accent colour** — `text-accent` (`#F7931A`), `bg-accent/15`, `border-accent/30` for Bitcoin-orange highlights - **Accent colour** — `text-accent` (`#F7931A`), `bg-accent/15`, `border-accent/30`
- **Touch targets** — min 44×44px, 8px gaps between targets
- **Font size** — min `16px` on all inputs (prevents iOS zoom)
- **Animations** — `animate-scale-in`, `animate-fade-up`, `animate-fade-in` from design system only - **Animations** — `animate-scale-in`, `animate-fade-up`, `animate-fade-in` from design system only
## How to Fix Touch Targets (Pattern)
**Before (violation):**
```html
<button class="w-7 h-7 rounded-full" @click="doThing">
<svg class="w-4 h-4" ...></svg>
</button>
```
**After (compliant):**
```html
<button class="min-w-[44px] min-h-[44px] flex items-center justify-center rounded-full" @click="doThing">
<svg class="w-4 h-4" ...></svg>
</button>
```
Or use the utility class (after T19):
```html
<button class="touch-target rounded-full" @click="doThing">
<svg class="w-4 h-4" ...></svg>
</button>
```
## For each task in loop/plan.md: ## For each task in loop/plan.md:
1. Find the first unchecked `- [ ]` item 1. Find the first unchecked `- [ ]` item
2. Read the task description carefully 2. Read the task description carefully
3. Read the relevant source files before making changes 3. Read the relevant source files before making changes
4. Implement the feature/fix following conventions and design system rules 4. Make the change — keep visual design identical, only adjust sizing
5. Run `pnpm typecheck && pnpm lint` — fix all errors before committing 5. Run `pnpm typecheck && pnpm lint` — fix all errors
6. Run `pnpm test` — fix any failing tests 6. Run `pnpm test` — fix any failing tests
7. If the task involves tests, run `cd packages/app && npx vitest run` specifically 7. Commit: `type(scope): description` (conventional commits)
8. Commit: `type(scope): description` (conventional commits) 8. Mark it done `- [x]` in `loop/plan.md`
9. Mark it done `- [x]` in `loop/plan.md` 9. Move to the next unchecked task immediately
10. Move to the next unchecked task immediately
## Rules ## Rules
- Never skip a testing gate — if tests fail, fix them before moving on - Never skip a testing gate — if tests fail, fix them before moving on
- If a task is proving difficult, keep trying different approaches. Make at least 30 genuine attempts before giving up and moving on. - If a task is proving difficult, keep trying different approaches. Make at least 30 genuine attempts.
- Always run typecheck + lint after every code change - Always run typecheck + lint after every code change
- Keep initial bundle < 250 KB gzipped — lazy-load anything heavy - The visual design must NOT change — same colors, same layout, same glass morphism
- Bitcoin only — never altcoins, never fiat payment flows - Only sizes of tappable areas, font minimums, and gaps change
- Privacy first — no external analytics calls, no telemetry
- Do not stop until all tasks are checked or you are rate limited - Do not stop until all tasks are checked or you are rate limited
- When adding tests, aim for realistic AI response patterns, not trivial examples - Commit and push after each TEST: task
- When wiring components into ContentPanel.vue, follow the exact same pattern used for existing content types (BookGrid/BookDetail, TVSeriesGrid/TVSeriesDetail, etc.)
- When building Archy integration, the AIUI side should be resilient — always handle the case where Archy doesn't respond or data is empty
- Commit and push after each completed phase (TEST: tasks)