From cb858bf4c9348a7dacee10cf638410624ee43a76 Mon Sep 17 00:00:00 2001 From: Admin Date: Sun, 5 Apr 2026 11:51:23 +0500 Subject: [PATCH] chor: delete all ios-ux skill --- .opencode/skills/ios-ux/SKILL.md | 156 ------------------------------- 1 file changed, 156 deletions(-) delete mode 100644 .opencode/skills/ios-ux/SKILL.md diff --git a/.opencode/skills/ios-ux/SKILL.md b/.opencode/skills/ios-ux/SKILL.md deleted file mode 100644 index 1d222ac..0000000 --- a/.opencode/skills/ios-ux/SKILL.md +++ /dev/null @@ -1,156 +0,0 @@ ---- -name: ios-ux -description: iOS/SwiftUI UI & UX review and implementation guidelines for LibNovel. Enforces Apple HIG, iOS 17+ APIs, spring animations, haptics, accessibility, performance, and offline handling. Load this skill for any iOS view work. -compatibility: opencode ---- - -# iOS UI/UX Skill — LibNovel - -Load this skill whenever working on SwiftUI views in `ios/`. It defines design standards, review process for screenshots, and implementation rules. - ---- - -## Screenshot Review Process - -When the user provides a screenshot of the app: - -1. **Analyze first** — identify specific UI/UX issues across these categories: - - Visual hierarchy and spacing - - Typography (size, weight, contrast) - - Color and material usage - - Animation and interactivity gaps - - Accessibility problems - - Deprecated or non-native patterns -2. **Present a numbered list** of suggested improvements with brief rationale for each. -3. **Ask for confirmation** before writing any code: "Should I apply all of these, or only specific ones?" -4. Apply only what the user confirms. - ---- - -## Design System - -### Colors & Materials -- **Accent**: `Color.amber` (project-defined). Use for active state, selection indicators, progress fills, and CTAs. -- **Backgrounds**: Prefer `.regularMaterial`, `.ultraThinMaterial`, or `.thinMaterial` over hard-coded `Color.black.opacity(x)` or `Color(.systemBackground)`. -- **Dark overlays** (e.g. full-screen players): Use `KFImage` blurred background + `Color.black.opacity(0.5–0.6)` overlay. Never use a flat solid black background. -- **Semantic colors**: Use `.primary`, `.secondary`, `.tertiary` foreground styles. Avoid hard-coded `Color.white` except on dark material contexts (full-screen player). -- **No hardcoded color literals** — use `Color+App.swift` extensions or system semantic colors. - -### Typography -- Use the SF Pro system font via `.font(.title)`, `.font(.body)`, etc. — never hardcode font names except for intentional stylistic accents (e.g. "Snell Roundhand" for voice watermark). -- Apply `.fontWeight()` and `.fontDesign()` modifiers rather than custom font families. -- Support Dynamic Type — never hardcode a fixed font size as the sole option without a `.minimumScaleFactor` or system font size modifier. -- Hierarchy: title3.bold for primary labels, subheadline for secondary, caption/caption2 for metadata. - -### Spacing & Layout -- Minimum touch target: **44×44 pt**. Use `.frame(minWidth: 44, minHeight: 44)` or `.contentShape(Rectangle())` on small icons. -- Prefer 16–20 pt horizontal padding on full-width containers; 12 pt for compact inner elements. -- Use `VStack(spacing:)` and `HStack(spacing:)` explicitly — never rely on default spacing for production UI. -- Corner radii: 12–14 pt for cards/chips, 10 pt for small badges, 20–24 pt for large cover art. - ---- - -## Animation Rules - -### Spring Animations (default for all interactive transitions) -- Use `.spring(response:dampingFraction:)` for state-driven layout changes, selection feedback, and appear/disappear transitions. -- Recommended defaults: - - Interactive elements: `response: 0.3, dampingFraction: 0.7` - - Entrance animations: `response: 0.45–0.5, dampingFraction: 0.7` - - Quick snappy feedback: `response: 0.2, dampingFraction: 0.6` -- Reserve `.easeInOut` only for non-interactive, ambient animations (e.g. opacity pulses, generating overlays). - -### SF Symbol Transitions -- Always use `contentTransition(.symbolEffect(.replace.downUp))` when a symbol name changes based on state (play/pause, checkmark/circle, etc.). -- Use `.symbolEffect(.variableColor.cumulative)` for continuous animations (waveform, loading indicators). -- Use `.symbolEffect(.bounce)` for one-shot entrance emphasis (e.g. completion checkmark appearing). -- Use `.symbolEffect(.pulse)` for error/warning states that need attention. - -### Repeating Animations -- Use `phaseAnimator` for any looping animation that previously used manual `@State` + `withAnimation` chains. -- Do not use `Timer` publishers for UI animation — prefer `phaseAnimator` or `TimelineView`. - ---- - -## Haptic Feedback - -Add `UIImpactFeedbackGenerator` to every user-initiated interactive control: -- `.light` — toggle switches, selection chips, secondary actions, slider drag start. -- `.medium` — primary transport buttons (play/pause, chapter skip), significant confirmations. -- `.heavy` — destructive actions (only if no confirmation dialog). - -Pattern: -```swift -Button { - UIImpactFeedbackGenerator(style: .light).impactOccurred() - // action -} label: { ... } -``` - -Do **not** add haptics to: -- Programmatic state changes not directly triggered by a tap. -- Buttons inside `List` rows that already use swipe actions. -- Scroll events. - ---- - -## iOS 17+ API Usage - -Flag and replace any of the following deprecated patterns: - -| Deprecated | Replace with | -|---|---| -| `NavigationView` | `NavigationStack` | -| `@StateObject` / `ObservableObject` (new types only) | `@Observable` macro | -| `DispatchQueue.main.async` | `await MainActor.run` or `@MainActor` | -| Manual `@State` animation chains for repeating loops | `phaseAnimator` | -| `.animation(_:)` without `value:` | `.animation(_:value:)` | -| `AnyView` wrapping for conditional content | `@ViewBuilder` + `Group` | - -Do **not** refactor existing `ObservableObject` types to `@Observable` unless explicitly asked — only apply `@Observable` to new types. - ---- - -## Accessibility - -Every view must: -- Support VoiceOver: add `.accessibilityLabel()` to icon-only buttons and image views. -- Support Dynamic Type: test that text doesn't truncate at xxxLarge without a layout adjustment. -- Meet contrast ratio: text on tinted backgrounds must be legible — avoid `.opacity(0.25)` or lower for any user-readable text. -- Touch targets ≥ 44pt (see Spacing above). -- Interactive controls must have `.accessibilityAddTraits(.isButton)` if not using `Button`. -- Do not rely solely on color to convey state — pair color with icon or label. - ---- - -## Performance - -- **Isolate high-frequency observers**: Any view that observes a `PlaybackProgress` (timer-tick updates) must be a separate sub-view that `@ObservedObject`-observes only the progress object — not the parent view. This prevents the entire parent from re-rendering every 0.5 seconds. -- **Avoid `id()` overuse**: Only use `.id()` to force view recreation when necessary (e.g. background image on track change). Prefer `onChange(of:)` for side effects. -- **Lazy containers**: Use `LazyVStack` / `LazyHStack` inside `ScrollView` for lists of 20+ items. `List` is inherently lazy and does not need this. -- **Image loading**: Always use `KFImage` (Kingfisher) with `.placeholder` for remote images. Never use `AsyncImage` for cover art — it has no disk cache. -- **Avoid `AnyView`**: It breaks structural identity and hurts diffing. Use `@ViewBuilder` or `Group { }` instead. - ---- - -## Offline & Error States - -Every view that makes network calls must: -1. Wrap the body in a `VStack` with `OfflineBanner` at the top, gated on `networkMonitor.isConnected`. -2. Suppress network errors silently when offline via `ErrorAlertModifier` — do not show an alert when the device is offline. -3. Gate `.task` / `.onAppear` network calls: `guard networkMonitor.isConnected else { return }`. -4. Show a non-blocking inline empty state (not a full-screen error) for failed loads when online. - ---- - -## Component Checklist (before submitting any view change) - -- [ ] All interactive elements ≥ 44pt touch target -- [ ] SF Symbol state changes use `contentTransition(.symbolEffect(...))` -- [ ] State-driven layout transitions use `.spring(response:dampingFraction:)` -- [ ] Tappable controls have haptic feedback -- [ ] No `NavigationView`, no `DispatchQueue.main.async`, no `.animation(_:)` without `value:` -- [ ] High-frequency observers are isolated sub-views -- [ ] Offline state handled with `OfflineBanner` + `NetworkMonitor` -- [ ] VoiceOver labels on icon-only buttons -- [ ] No hardcoded `Color.black` / `Color.white` / `Color(.systemBackground)` where a material applies