
Ios Taste
- 196 installs
- 191 repo stars
- Updated July 24, 2026
- pproenca/dot-skills
ios-taste: A skill for development. This provides functionality for development workflows.
Key points
- ios-taste
Ios Taste by the numbers
- 196 all-time installs (skills.sh)
- +9 installs in the week ending Aug 4, 2026 (Skillselion tracking)
- Ranked #2,066 of 4,347 Backend & APIs skills by installs in the Skillselion catalog
- Data as of Aug 4, 2026 (Skillselion catalog sync)
npx skills add https://github.com/pproenca/dot-skills --skill ios-tasteAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 196 |
|---|---|
| repo stars | ★ 191 |
| Last updated | July 24, 2026 |
| Repository | pproenca/dot-skills ↗ |
How do I use ios-taste for development tasks?
Use ios-taste for development tasks
Who is it for?
Best when you're working on backend & apis and need structured help with ios-taste.
Skip if: Teams with no backend & apis needs, or anyone wanting a generic chat assistant without this specific workflow.
When should I use this skill?
When you need to use ios-taste for development tasks, or when ios-taste: a skill for development. this provides functionality for development workflows.
What you get
Structured output aligned to ios-taste: ios-taste.
Files
iOS Taste
Taste doesn't start at the pixel level. It starts at "who is this person and what do they need?" The visual refinement is the LAST step. The first step is understanding the user's world deeply enough that the interface design feels inevitable — like it couldn't have been designed any other way.
Your default mode skips straight to layout. It produces technically correct SwiftUI that looks generic because it was never grounded in a real person's needs. This skill changes the order of operations: think like a designer first, then write code.
Phase 0: The 0.5-Second Test (ORIENT before everything)
Before designing anything, answer ONE question:
**What does the user SEE in the first half-second — before they
read a single word?**
This is not about content. It's about the SHAPE of the screen. Close your eyes and picture it. What dominates?
- A ring at 40% of screen height? → Fitness dashboard
- A gradient card with bold white text? → Music/travel/content
- A large number floating in space? → Finance/health metric
- A grid of thumbnails? → Photo/recipe/shopping collection
- A clean form with generous space? → Settings/profile
If your answer is "a List with rows of text" → STOP. That's a spreadsheet, not an app. Go back and find the visual shape.
Write the 0.5-second answer as the FIRST line of the experience brief:
// 0.5s: Four warm gradient cards stacked on black — a cookbookThis single sentence anchors every decision that follows. If the code you write doesn't produce that shape, something went wrong.
Phase 1: Design Thinking (Before You Touch SwiftUI)
Before writing a single line of code, answer these questions. Write the answers down as comments or in your thinking. If you skip this phase, your output will look like every other AI-generated UI — correct but soulless.
1. Who is the user?
Not "a fitness enthusiast." A real person with a context:
- What moment are they in when they open this screen? (Rushing
between meetings? Relaxing on the couch? Mid-workout?)
- What did they just do before arriving here? (Finished a run?
Browsed a list? Got a notification?)
- What do they want to accomplish in under 10 seconds?
This shapes EVERYTHING. A user mid-workout needs giant tap targets and glanceable data. A user browsing recipes at home wants rich detail and discovery. A user configuring settings wants to find the one toggle they care about and leave.
2. What should they FEEL?
This is the question that separates designed apps from information displays. Apple Fitness doesn't show you data — it motivates you to move. Every design choice serves that emotional goal.
Before choosing components, decide the emotional intent:
- Motivated → bold colors, progress visualization, celebration
moments, large achievement numbers
- Calm / focused → muted tones, generous space, subtle motion
- Efficient → compact layouts, clear hierarchy, minimal chrome
- Delighted → unexpected animation, rich materials, playful
moments (achievement badges, confetti, 3D icons)
- Confident → clean data presentation, trust colors (blue/green),
professional typography
The emotional intent drives every visual decision downstream: color palette, scale, spacing, whether data is listed or visualized, whether the screen feels dense or spacious.
3. What are their goals and pain points?
For each screen, identify:
- Primary goal — the ONE thing most users come here to do
- Secondary goals — things some users occasionally need
- Pain points — what frustrates users in this domain?
A fitness settings screen: the primary goal isn't "see all settings." It's "change the one thing that's been bugging me" — maybe the weekly goal is too low, or notifications come at the wrong time. The pain point is wading through 30 options to find the one they need.
3. What features serve those goals?
Map goals to features. Not "what features could this screen have?" but "what's the minimum set of features that makes the primary goal effortless?" Every feature that doesn't serve a goal is clutter.
Group features by priority:
- Must-have — blocks the primary goal without it
- Should-have — significantly improves the experience
- Could-have — nice but the user doesn't miss it if it's absent
4. How do features become screens?
This is information architecture — deciding what goes where:
- One primary action per screen. If a screen tries to do two
things, split it into two screens or use progressive disclosure.
- Group by user intent, not by data type. A user doesn't think
"I want to see my notification settings." They think "I want my phone to stop buzzing during workouts." Group features by the problem they solve, not by their technical category.
- Navigation follows the user's mental model. Settings → Profile
is obvious. Settings → "Health Integrations" → Apple Health → Data Permissions is three levels deep for something the user sets once. Consider whether it needs its own screen or can be inline.
5. What components serve each feature?
NOW you think about SwiftUI — but through the lens of user intent:
- Toggle vs Picker — if the choice is binary, use Toggle. If
there are 3+ options, use Picker. If the options need explanation, use a navigation link to a selection screen.
- Stepper vs Slider — steppers for precise numeric values
(1, 2, 3 reps). Sliders for ranges where the exact value matters less (brightness, volume, a weekly hour target).
- Inline vs Push navigation — show detail inline when it's
1-2 lines. Push to a new screen when the detail is rich enough to deserve its own context.
- Sheet vs Push — sheets for self-contained tasks (compose,
edit profile, filter). Push for drilling into hierarchical content.
- List vs ScrollView — List for homogeneous collections
(contacts, settings, messages). ScrollView for heterogeneous layouts (a recipe detail with hero image, ingredients, and steps).
The component choice IS the design. A slider for a weekly workout goal feels exploratory and forgiving. A stepper for the same value feels precise and clinical. Neither is wrong — the right choice depends on who the user is and what moment they're in.
Phase 2: Visual Design
After Phase 1, you know who the user is, what they need, how they should feel, and what components serve those needs. Now make it beautiful. The emotional intent from Phase 1 drives every choice here.
1. Hierarchy Through Scale
Not just font weight — dramatic scale contrast. The most important thing on screen should be physically large, not just bold.
- Hero numbers at display scale — a calorie count, a step count,
a price should dominate the screen. Use .system(size: 64) or .largeTitle with .fontDesign(.rounded). Apple Fitness shows "120" as 40% of the screen. Don't shrink important data into a row.
- Supporting text whispers — everything that isn't the hero
element gets .caption or .footnote in .secondary. The contrast between the hero and the support IS the hierarchy.
- Space as luxury — leave empty areas. A number floating in a
sea of black or white is more powerful than the same number crammed into a dense list. Space communicates importance.
2. Color Is Math, Not Vibes
NEVER pick colors by hand. Color harmony is a solved mathematical problem. This skill bundles a palette generator that computes every color from a single seed hue — analogous harmony, WCAG contrast validated, light and dark mode variants.
Before writing any view code, run the palette generator:
python scripts/generate_palette.py \
--seed <hue-degrees> \
--mode both \
--items <collection-count> \
--app "App Name"Seed hue guide:
- 0–30° = warm (cooking, social, dating)
- 30–60° = golden (finance, productivity)
- 60–150° = green (health, fitness, nature)
- 150–210° = cyan/teal (tech, communication)
- 210–270° = blue (trust, business, weather)
- 270–330° = purple (creative, music, luxury)
- 330–360° = pink/red (energy, food)
Include the generated enum Palette { ... } at the top of your Swift file and use ONLY those colors. The palette is computed — every color is mathematically related to the seed, contrast ratios are pre-validated, and light/dark mode variants are included.
Rules that never break:
- One seed hue per app. Everything derives from it.
- Collections use analogous variations (the
--itemsflag),
not random hues. They sit together because they're ±30° of seed.
- Never use `Color.red`, `.green`, `.blue` as palette colors —
those are semantic system colors for status indicators.
- Use `Palette.primary`, `Palette.cardBackground`, etc. — not
ad-hoc Color(hue:) calls scattered through the view code.
3. Show Data, Don't List It
When data is the content (fitness metrics, financial stats, progress), VISUALIZE it instead of putting it in a label:
- Rings and gauges for progress toward a goal
- Sparkline charts for trends over time
- Large hero numbers with unit labels in small caps
- Color-coded bars for composition (macro nutrients, time split)
A LabeledContent("Steps", value: "8,432") is information. A large "8,432" in .title with a sparkline below it is an experience. The emotional intent from Phase 1 tells you which one to use.
4. Card-Based Composition
Don't default to .insetGrouped List for everything. Compose with rounded rect containers when the content is heterogeneous:
- Cards with
RoundedRectangle(cornerRadius: 16)and
.fill(.secondary.opacity(0.15)) on dark backgrounds
- Each card is a self-contained visual unit with its own hierarchy
- Cards can have gradient backgrounds for visual richness (like
Apple Fitness+ Plans cards)
- Use
LazyVGridorLazyVStackinside aScrollViewfor
card-based layouts
Lists are for homogeneous rows (contacts, messages, settings). Cards are for dashboards, summaries, and content-rich screens.
5. Content Realism
The data IS the design. Every preview tells a coherent story:
- Real names ("Elena Marsh"), plausible numbers ("$47.83", "4.3"),
varied lengths, temporal realism ("2 hours ago", "Yesterday")
- Data relationships that make sense (Designer → Design dept)
- If your preview data looks fake, your design looks fake
6. Restraint
What you leave out defines taste. No instruction headers. No uniform icons. No tutorial overlays. No demo naming. For every element, ask: "what happens if I remove this?" If nothing — remove it.
4. Craft
The invisible details that feel right:
.monospacedDigit()on changing numbers@ScaledMetricon custom sizes.contentTransition(.numericText())on counters.sensoryFeedback()on meaningful state changes (not haptic spam)LabeledContentfor key-value pairs- Accessibility as design, not compliance
5. Character
Each screen has a distinct personality. Character comes from:
- Domain-appropriate containers and color palettes
- Content-specific typography and interaction patterns
- Cover the nav bar — can you still tell what app this is?
Applying Both Phases
When asked to build a SwiftUI view:
1. Phase 1 — Think through the user, their goals, feature groupings, screen structure, and component choices. Write brief notes (as code comments or in your response) showing your design reasoning. This is not optional — it's what separates a designed experience from a decorated layout.
2. Phase 2 — Write the SwiftUI code with all five fundamentals applied. Start with realistic data models and preview content. Build minimal, add only what earns its place, then polish with craft details.
3. Self-check — Before finishing, ask: "Would a real user using this app in the moment I identified in Phase 1 feel like this screen was designed for them?" If not, something in Phase 1 was wrong — go back.
What "No Taste" Looks Like
// NO TASTE — jumped straight to layout, no user thinking
struct DemoView: View {
var body: some View {
NavigationStack {
List {
Section("Instructions") {
Text("This demo shows how lists work")
}
Section("Items") {
ForEach(1...5, id: \.self) { i in
HStack {
Image(systemName: "star")
Text("Item \(i)")
Spacer()
Text("Detail")
.foregroundStyle(.secondary)
}
}
}
}
.navigationTitle("Demo")
}
}
}No user thinking. No goals. Instruction header. Uniform icons. Numbered placeholders. Generic naming. No character.
What Taste Looks Like
// GOLDEN — Weather-inspired fitness dashboard
// User: Alex, 28, just finished a morning run, wants to see today's stats
// Emotional intent: MOTIVATED — celebrate the effort, inspire tomorrow
// Hero: calorie ring dominating the top half
struct FitnessCardView: View {
let calories: Int = 847
let goal: Int = 1000
var body: some View {
ScrollView {
VStack(spacing: 20) {
// Hero ring — 40% of visible screen, not a row in a list
ZStack {
Circle()
.stroke(.quaternary, lineWidth: 20)
Circle()
.trim(from: 0, to: Double(calories) / Double(goal))
.stroke(calorieGradient, style: StrokeStyle(lineWidth: 20, lineCap: .round))
.rotationEffect(.degrees(-90))
VStack(spacing: 4) {
Text("\(calories)")
.font(.system(size: 56, weight: .bold, design: .rounded))
Text("of \(goal) cal")
.font(.subheadline)
.foregroundStyle(.secondary)
}
}
.frame(width: 220, height: 220)
.padding(.top, 20)
// Stat cards — NOT LabeledContent rows
HStack(spacing: 12) {
statCard("Distance", value: "5.2 km", color: .blue)
statCard("Time", value: "28:14", color: .green)
statCard("Pace", value: "5'26\"", color: .orange)
}
}
.padding()
}
}
private func statCard(_ label: String, value: String, color: Color) -> some View {
VStack(spacing: 6) {
Text(value)
.font(.system(.title3, design: .rounded))
.fontWeight(.semibold)
Text(label)
.font(.caption)
.foregroundStyle(.secondary)
}
.frame(maxWidth: .infinity)
.padding(.vertical, 16)
.background(color.opacity(0.1), in: .rect(cornerRadius: 12))
}
private var calorieGradient: AngularGradient {
AngularGradient(colors: [.red, .orange, .yellow], center: .center)
}
}Design comment explains user moment and emotional intent. Hero ring dominates the screen (not a ProgressView in a List row). Stat cards use color backgrounds, not LabeledContent. You know this is a fitness app without reading the title — the ring IS the identity.
Component Palette Quick Reference
When you instinctively reach for a tutorial component, STOP:
| NEVER (reflex) | GOLDEN (reach for this instead) |
|---|---|
List + ForEach | ScrollView + LazyVStack with cards |
Form + Section | ScrollView + GroupBox(.regularMaterial) |
LabeledContent for metrics | Hero typography .system(size:design:.rounded) |
ProgressView | Circle().trim(from:to:) or Canvas |
Button("Label") | .borderedProminent + .controlSize(.large) |
Toggle in Section | Segmented control or custom pill |
.system(size:) for text | .largeTitle / .title + .fontDesign(.rounded) |
Default List background | Gradients, .regularMaterial, colored containers |
Modern iOS 18+ APIs to reach for: MeshGradient, .scrollTransition, .containerRelativeFrame, .visualEffect, Canvas, TimelineView, .scrollTargetBehavior(.paging), UnevenRoundedRectangle.
Apple reference: Weather (gradient cards), Stocks (hero charts), Health (colored rings), Fitness (activity cards), Journal (photo cards), Contacts (gradient posters, glass avatars, per-entity color identity).
The Screen Becomes the Content
Study iOS Contacts: the detail view isn't a form with a contact's data. The entire screen IS the contact — a full-bleed gradient that matches the person's avatar color, a glass-bordered monogram, the name in massive bold type. It's a poster, not a record.
This is the highest level of taste: the UI dissolves into the content. The screen doesn't frame the data — it becomes the data.
Techniques for this:
- Per-entity gradients — each contact, playlist, or recipe gets
its own color identity. Use MeshGradient or LinearGradient derived from the entity's accent color. The background extends behind the navigation bar with .ignoresSafeArea().
- Glass and material layering — avatar circles with
.stroke(.ultraThinMaterial) borders. Action buttons in .ultraThinMaterial circles. Cards using .regularMaterial that let the gradient show through. Depth without shadows.
- Smart typography in lists — Apple Contacts bolds the LAST name
and leaves the first name regular weight. This tiny detail makes alphabetical scanning dramatically faster. Find the equivalent typographic hierarchy for your domain.
- Edit mode preserves beauty — even the Contacts edit form uses
dark cards, colored action buttons (red minus, green plus), and the same avatar hero. Edit mode should never degrade to a generic form — it maintains the visual language of the view mode.
Reference: Apple Design DNA
When making specific design decisions, read references/apple-design-dna.md in this skill's directory. It contains patterns extracted by systematically crawling Apple Fitness and Apple Contacts in the iOS simulator — real accessibility trees, real measurements, real design analysis per screen.
Key sections to consult:
- Dashboard vs Utility mode — when to use dark cards vs light lists
- Universal measurements — card radius (16pt), padding (18pt), gap (10pt)
- Onboarding templates — feature-list vs hero-illustration patterns
- Button hierarchy — filled primary, outline secondary, position-based destructive
- Glass-on-gradient — the Contacts poster technique for premium detail views
- Empty states — show visualization skeletons, not "no data" messages
The Mindset
You are not a developer who can also design. You are a designer who thinks about people first and expresses the result in SwiftUI. The code is the medium. The product is the moment when a human picks up their phone and the interface feels like it was made just for them.
iOS Taste
This curated skill mirrors SKILL.md. When maintaining it, keep the trigger language focused on SwiftUI user-facing interface work and keep supporting references in references/.
{
"version": "1.5.4",
"organization": "dot-skills",
"technology": "SwiftUI",
"date": "May 2026",
"abstract": "Designs iOS 18+ SwiftUI experiences with real taste — starting from user goals, not pixels. Covers view composition, navigation, animations, and native platform integration."
}
Apple Design DNA — Cross-App Synthesis
Extracted from systematic crawling of Apple Fitness, Apple Contacts, and Apple Weather. Sources: iOS 26.2 simulator + real iPhone 16 Pro Max (iOS 26.3.1) via WebDriverAgent, March 2026.
The Three Modes of Apple App Design
Mode 1: Dashboard (Fitness)
- Background: Pure black (#000000)
- Content: Cards floating on void
- Data: Visualizations (rings, sparklines, trend arrows)
- Emotion: Engagement, motivation, identity
- Typography: Large values in accent color, small labels in gray
- Navigation: Tab bar + card drill-down
- Use when: The app's purpose is monitoring, tracking, or progress
Mode 2: Utility (Contacts)
- Background: System/light OR poster gradient
- Content: List rows with standard patterns
- Data: Structured text (labels + values)
- Emotion: Efficiency, familiarity, trust
- Typography: Bold key identifier (last name), regular secondary
- Navigation: Standard push/pop list-detail
- Use when: The app's purpose is finding, managing, or organizing
Mode 3: Ambient (Weather)
- Background: Dynamic atmospheric gradient (the sky IS the app)
- Content: Frosted glass module cards on the sky
- Data: Per-module visualizations (gauges, compasses, arcs, maps)
- Emotion: Immersive, ambient, atmospheric
- Typography: Temperature at impossible scale (~121pt), whispered labels
- Navigation: Horizontal page swipe between cities, vertical scroll for modules
- Use when: The app's purpose is ambient awareness or environmental monitoring
Universal Design Principles
1. The Screen IS The Content
Both apps demonstrate this: Fitness makes the Activity Ring the visual identity of the app. Contacts makes the poster gradient the person's visual identity. In neither case is the content "placed on" a background — the content IS the background.
2. One Accent Color, Used Sparingly
Fitness uses neon green (#A8FF00) in exactly these places:
- Profile avatar ring
- CTA buttons (filled)
- Active tab bar icon
- Metric values
That's it. Four placements across the entire app.
3. Card vs Row Grammar
- Card: Used for dashboard/overview items. Self-contained,
rounded corners, dark fill, generous padding. Cards say "here's a snapshot — tap to dive deeper."
- Row: Used for detail/action lists. Full-width, divider-separated,
minimal padding. Rows say "here's structured data — scan and act." The transition from card→row signals depth level.
4. Onboarding: Two Templates
Feature List (Workout tab):
Large Title
Subtitle (gray)
[🟢 Icon] Bold Title — Description
[🟢 Icon] Bold Title — Description
[🟢 Icon] Bold Title — Description
Privacy note (small, gray)
[=== Continue ===]Hero Illustration (Sharing tab):
[Floating circle composition with Memoji/icons]
Large Title (centered)
Description (centered, gray)
Privacy note (small)
[=== Get Started ===]Use Feature List for functionality. Use Hero Illustration for identity/social.
5. Button Hierarchy
- Primary: Filled with accent color (green for Fitness), dark text
- Secondary: Outline in accent color, accent text, transparent fill
- Tertiary/System: System blue text, no border
- Destructive: Isolated by position (bottom, separate section), NOT red
6. Empty States Show Structure
When there's no data, Apple shows the visualization skeleton:
- Empty ring (not "No data" text)
- Chart axes without data points
- Dotted guide lines
The structure teaches the user what data will look like BEFORE they have it.
7. Glass-on-Gradient (Contacts Poster)
Detail views can use a gradient background with frosted glass sections:
Layer 0: Full-bleed gradient (person's/item's identity)
Layer 1: Semi-transparent frosted cards (grouped actions)
Layer 2: White text on glassThis creates depth without cards-on-white and makes utility views feel premium.
8. Inline Data Enhancement
Instead of "View on Map →", Contacts embeds a map thumbnail inline. Instead of "See Trends →", Fitness embeds a sparkline in the metric card. The preview IS the data. No navigation needed for first-level insight.
8b. Modular Card Grid (Weather)
Weather uses a module system where each card has the same anatomy but a different visualization type:
Icon + LABEL (caps, tiny) → Hero Value (large) → Visualization → Natural LanguageVisualizations include: gradient bars (UV, air quality), compass dial (wind), sun arc (sunrise/sunset), barometer gauge (pressure), map overlay (wind map), rendered moon phase, temperature range bars with color gradients.
Full-width modules stack vertically. Half-width modules pair in 2-column rows. All modules use the same frosted glass material, corner radius, and spacing.
8c. Natural Language as Content (Weather)
Most Weather modules include a SENTENCE explaining the number:
- "Wind is making it feel colder."
- "Perfectly clear view."
- "The dew point is 7° right now."
Numbers become understanding. This humanizes data-heavy screens.
9. Privacy as Design Element
Both apps handle data/privacy proactively:
- Handshake icon (🤝) for data sharing disclosures
- "About ... & Privacy" links in green
- Disclosures BEFORE the CTA, not after
- AI features default to OFF (opt-in, not opt-out)
10. Typography That Scans
- Contacts: Bold LAST name, regular first name → scan by family name
- Fitness: Large COLORED values, small gray labels → scan by numbers
- Both apps optimize for the MOST LIKELY scan pattern of their content
Measurements Reference
| Element | Size | Notes |
|---|---|---|
| Card corner radius | ~16pt | Consistent across both apps |
| Screen edge padding | ~18pt | Content margin from device edge |
| Card internal padding | ~16pt | Content margin inside cards |
| Card vertical gap | ~10pt | Space between stacked cards |
| List row height | ~54pt | Standard content row |
| Monogram circle | ~44pt | List size |
| Monogram circle (detail) | ~120pt | Contact poster size |
| Action button circle | ~58pt | Call, message, etc. |
| Play button | ~45pt | Workout start |
| Metric value font | ~34pt | Step count, distance |
| Card header font | ~17pt | Card titles |
| Section header | ~13pt | A, B, C section letters |
| Tab bar height | ~75pt | Bottom tab bar |
11. Haptics as State Transitions (Calendar)
Calendar is the most haptic-rich Apple app. Haptic patterns:
- Mode change: Medium impact on long-press (browse → create/edit)
- Snap points: Light impact at each 15-minute position during drag
- Level change: Medium impact when pinch crosses a zoom boundary
- Selection: Selection haptic on day tap
Anti-pattern: haptic on every scroll, every button, every animation. Calendar uses haptics SURGICALLY — only for mode changes, discrete positions, and confirmations.
12. Time as Visual Space (Calendar)
Calendar uses the Y-axis as a data dimension — vertical position IS time. This spatial metaphor means:
- Empty space = free time (visible, not just "no events")
- Event height = duration (tall block = long meeting)
- Current time = red "now line" moving down the grid in real-time
- Long-press position → event start time (spatial input, not form input)
13. Adaptive Information Density (Calendar)
Calendar offers 4 display modes for the same data: Compact (dots), Stacked (blocks), Details (Gantt bars), List (text below grid). The data doesn't change — the representation does. This pattern is valuable for any data-rich app where users have different density preferences.
14. Hierarchical Zoom (Calendar)
Four zoom levels with pinch transitions: Year → Month → Week → Day. Each level shows more detail. Today is always marked in RED across all levels. The zoom feels like temporal cartography — zooming into time the same way you zoom into a map.
16. Semantic Color Per Domain (Health)
Health's 13 categories each have a dedicated color (Activity=orange, Heart=pink, Sleep=purple, Nutrition=green, etc.). The color persists everywhere: category icon, list row, detail view labels, chart accents. This is a DOMAIN COLOR SYSTEM — color encodes content category, not brand. Different from Fitness (one accent) or Weather (gradient-as-identity).
17. Data + Education Inline (Health)
Health embeds educational content ("About Steps" + Mayo Clinic attribution) directly below data charts. Every metric has its own "About" section. The app teaches while it tracks. Design lesson: if users might not fully understand the data, educate inline — not in help overlays.
18. Metric Detail Template (Health)
Every Health metric follows: period selector (D/W/M/6M/Y) → summary card (hero number) → interactive chart (bar/line/scatter varies by data type) → educational content → related apps. The chart is ~40% of the screen — it IS the content, not supplementary.
20. Annotation Layer Pattern (Photos Markup)
Markup treats annotations as a transparent OVERLAY on immutable content. The photo is never modified — ink, shapes, and text are a separate layer composited on save. Tools: 11 drawing pens (scrollable strip), + menu for Text/Shape/Signature/Loupe. PencilKit provides this for free in SwiftUI.
Pattern for any app needing image annotation:
Layer 0: Base image (body diagram, floor plan, photo)
Layer 1: Annotation overlay (PencilKit canvas)
Layer 2: Tool controls (floating, auto-hide capable)21. Dense Grid to Full-Screen (Photos)
Library grid: 4 columns, ~2pt gaps, edge-to-edge (no horizontal padding). This is the densest layout in any Apple app. Detail viewer: full-screen with auto-hiding floating controls. The content IS the screen — chrome is temporary and toggled by tapping.
22. Per-Entity Gradient Identity (Weather + Contacts)
Every entity in a collection can have its own visual identity derived from its DATA, not assigned arbitrarily:
- Weather cities: gradient from current weather conditions
- Contacts: gradient from avatar/poster color
- Potential: recipes from dish colors, playlists from album art
The gradient is truthful — it IS the data, rendered as atmosphere.
Apps Crawled
Apple Fitness (com.apple.Fitness)
Screens: 11 captured
- 01: Summary + onboarding overlay
- 02: Summary dashboard (clean)
- 03: Summary scrolled (Trends, Trainer Tips)
- 04: Fitness+ plan completion (celebration)
- 05: Fitness+ browse (empty state)
- 06: Workout tab onboarding (feature list)
- 07: Workout Buddy setup (AI opt-in)
- 08: Workout type selection (card grid)
- 09: Workout types scrolled (more types + Add)
- 10: Sharing tab (hero illustration onboarding)
- 11: Activity Ring detail
Apple Contacts (com.apple.MobileAddressBook)
Screens: 3 captured (iOS simulator)
- 01: Contact list (alphabetic sections)
- 02: Contact detail (poster view)
- 03: Contact detail scrolled (data + actions)
Apple Weather (com.apple.weather)
Screens: 7 captured (real iPhone 16 Pro Max via WDA)
- 01: Main forecast (London sunset, hero temperature)
- 02: 10-day forecast scrolled (temperature range bars)
- 03: Modules grid (Air Pollution + Wind Map)
- 04: Modules grid 2 (Feels Like + UV Index + Wind compass)
- 05: Modules grid 3 (Sunrise arc + Precipitation + Visibility + Humidity + Moon)
- 06: Modules bottom (Averages + Pressure gauge + Report + Footer)
- 07: City list (per-city gradient cards)
Apple Calendar (com.apple.mobilecal)
Screens: 7 captured (real iPhone 16 Pro Max via WDA)
- 01: Week view (today + tomorrow columns, time grid, all-day events)
- 02: Month grid (date cells with event dots, split with event list)
- 03: Year view (12 mini-months, red = now across all levels)
- 04: Multi-calendar events (same holiday from different calendars)
- 06: Details mode (Gantt-like event bars in month grid)
- 07: Current time indicator (red now-line, haptic interaction catalog)
- 08: Long-press event creation (pre-filled time from press position)
- 09: Full creation form (Event/Reminder segmented, grouped sections)
Apple Health (com.apple.Health)
Screens: 5 captured (real iPhone 16 Pro Max via WDA)
- 01: Welcome onboarding (hero illustration, icon constellation)
- 02: Privacy screen (heart-lock icon, full-page privacy commitment)
- 05: Browse categories (13 categories, semantic colors per domain)
- 06: Editorial content (Cycle Tracking — magazine-style article cards with custom artwork)
- 07: Activity category (time-sectioned cards, sparkline previews, Move ring inline)
- 08: Steps detail (D/W/M/6M/Y period selector, bar chart, educational "About" + Mayo Clinic)
Apple Photos (com.apple.mobileslideshow)
Screens: 5 captured (real iPhone 16 Pro Max via WDA)
- 01: Library grid (4-column dense thumbnails, edge-to-edge, 4,932 items)
- 02: Photo detail viewer (full-screen, auto-hide floating controls, thumbnail strip)
- 03: Edit mode (Adjust/Filters/Crop/Clean Up tabs, adjustment dial with haptics)
- 05: Markup mode (11 drawing tools in scrollable strip, color picker, PencilKit)
- 06: Markup insert menu (Text, Shape, Signature, Loupe — the annotation toolkit)
#!/usr/bin/env python3
"""
Generate a mathematically harmonious SwiftUI color palette from a single seed hue.
Usage:
python generate_palette.py --seed 15 --mode both --items 6
python generate_palette.py --seed 210 --mode dark --items 0
python generate_palette.py --seed 120 --app "Fitness Tracker"
Arguments:
--seed Hue angle in degrees (0-360). Examples:
0-30 = warm (cooking, social)
30-60 = golden (finance, productivity)
60-150 = green (health, fitness, nature)
150-210 = cyan/teal (tech, communication)
210-270 = blue (trust, business, weather)
270-330 = purple (creative, music, luxury)
330-360 = pink/red (energy, dating, food)
--mode light, dark, or both (default: both)
--items Number of collection items needing distinct colors (default: 0)
--app Optional app name for the generated enum comment
Output: A complete Swift `enum Palette { ... }` block ready to paste.
All colors use HSB with exact values. Contrast ratios are validated.
"""
import argparse
import colorsys
import math
def hsb_to_rgb(h: float, s: float, b: float) -> tuple[float, float, float]:
"""Convert HSB (h in 0-1, s in 0-1, b in 0-1) to RGB (0-1)."""
return colorsys.hsv_to_rgb(h, s, b)
def relative_luminance(r: float, g: float, b: float) -> float:
"""WCAG relative luminance from linear RGB."""
def linearize(c):
return c / 12.92 if c <= 0.04045 else ((c + 0.055) / 1.055) ** 2.4
rl, gl, bl = linearize(r), linearize(g), linearize(b)
return 0.2126 * rl + 0.7152 * gl + 0.0722 * bl
def contrast_ratio(lum1: float, lum2: float) -> float:
"""WCAG contrast ratio between two luminances."""
lighter = max(lum1, lum2)
darker = min(lum1, lum2)
return (lighter + 0.05) / (darker + 0.05)
def validate_text_on_bg(h: float, s: float, b: float, text_white: bool) -> bool:
"""Check if white/black text has ≥4.5:1 contrast on this HSB background."""
r, g, bl = hsb_to_rgb(h, s, b)
bg_lum = relative_luminance(r, g, bl)
text_lum = 1.0 if text_white else 0.0
return contrast_ratio(bg_lum, text_lum) >= 4.5
def adjust_for_contrast(h: float, s: float, b: float, text_white: bool) -> tuple[float, float, float]:
"""Adjust brightness to ensure WCAG 4.5:1 contrast with text color."""
if text_white:
# Darken until contrast passes
while b > 0.1 and not validate_text_on_bg(h, s, b, True):
b -= 0.02
else:
# Lighten until contrast passes
while b < 0.99 and not validate_text_on_bg(h, s, b, False):
b += 0.02
s = max(0.02, s - 0.01) # desaturate slightly as we lighten
return h, s, b
def generate_palette(seed_deg: int, mode: str, item_count: int, app_name: str) -> str:
"""Generate a complete Swift Palette enum."""
seed = seed_deg / 360.0 # normalize to 0-1
lines = []
lines.append(f"// Generated palette for {app_name or 'app'} — seed hue: {seed_deg}°")
lines.append(f"// Analogous harmony, WCAG contrast validated")
lines.append("// Usage: Palette.primary, Palette.cardBackground, etc.")
lines.append("")
lines.append("import SwiftUI")
lines.append("")
lines.append("enum Palette {")
def color_line(name: str, h: float, s: float, b: float, comment: str = "") -> str:
h_mod = h % 1.0
cmt = f" // {comment}" if comment else ""
return f" static let {name} = Color(hue: {h_mod:.4f}, saturation: {s:.3f}, brightness: {b:.3f}){cmt}"
def hex_from_hsb(h: float, s: float, b: float) -> str:
r, g, bl = hsb_to_rgb(h % 1.0, s, b)
return f"#{int(r*255):02x}{int(g*255):02x}{int(bl*255):02x}"
# --- Dark mode palette ---
if mode in ("dark", "both"):
if mode == "both":
lines.append("")
lines.append(" // MARK: - Dark Mode")
lines.append("")
# Primary: brand identity (use for 30% — headers, icons, active states)
ph, ps, pb = adjust_for_contrast(seed, 0.70, 0.85, True)
lines.append(color_line("primary", ph, ps, pb, f"30% brand — {hex_from_hsb(ph, ps, pb)}"))
# Secondary: muted echo of primary — SAME hue, lower saturation
# (use for 60% — backgrounds, large surfaces, cards)
sh, ss, sb = adjust_for_contrast(seed, 0.22, 0.65, True)
lines.append(color_line("secondary", sh, ss, sb, f"60% surfaces — {hex_from_hsb(sh, ss, sb)}"))
# Accent: split-complementary (+150°) — vibrant but not aggressive
# (use for 10% — CTAs, badges, highlights, interactive elements)
ah, as_, ab = adjust_for_contrast(seed + 0.417, 0.70, 0.85, True)
lines.append(color_line("accent", ah, as_, ab, f"10% accent — {hex_from_hsb(ah, as_, ab)}"))
# Card background: very dark, slight hue tint
lines.append(color_line("cardBackground", seed, 0.12, 0.14, "dark card"))
# Surface: slightly lighter than pure black
lines.append(color_line("surface", seed, 0.05, 0.08, "elevated surface"))
# Text colors
lines.append(" static let textPrimary = Color.white")
lines.append(" static let textSecondary = Color(white: 0.65)")
# --- Light mode palette ---
if mode in ("light", "both"):
if mode == "both":
lines.append("")
lines.append(" // MARK: - Light Mode")
lines.append("")
prefix = "light" if mode == "both" else ""
# Primary: brand identity for light mode
ph, ps, pb = seed, 0.65, 0.55 # darker in light mode for contrast
name = f"{prefix}Primary" if prefix else "primary"
lines.append(color_line(name, ph, ps, pb, f"30% brand — {hex_from_hsb(ph, ps, pb)}"))
# Secondary: muted primary — same hue, very low saturation
sh = seed
name = f"{prefix}Secondary" if prefix else "secondary"
lines.append(color_line(name, sh, 0.10, 0.90, f"60% surfaces — {hex_from_hsb(sh, 0.10, 0.90)}"))
# Accent: split-complementary (+150°)
ah = seed + 0.417
name = f"{prefix}Accent" if prefix else "accent"
lines.append(color_line(name, ah, 0.55, 0.60, f"10% accent — {hex_from_hsb(ah, 0.55, 0.60)}"))
# Card background: very low saturation, high brightness
name = f"{prefix}CardBackground" if prefix else "cardBackground"
lines.append(color_line(name, seed, 0.04, 0.97, "light card"))
# Surface
name = f"{prefix}Surface" if prefix else "surface"
lines.append(color_line(name, seed, 0.02, 0.99, "page background"))
# Text
if prefix:
lines.append(" static let lightTextPrimary = Color(white: 0.1)")
lines.append(" static let lightTextSecondary = Color(white: 0.45)")
else:
lines.append(" static let textPrimary = Color(white: 0.1)")
lines.append(" static let textSecondary = Color(white: 0.45)")
# --- Collection item colors ---
if item_count > 0:
lines.append("")
lines.append(f" // MARK: - Collection ({item_count} items)")
lines.append("")
# Adaptive spread: wider hue range for more items, but vary
# saturation and brightness too for perceptual separation.
# Hue spread scales with item count (60° base, up to 120° for 20 items)
base_spread = 0.167 # 60°
hue_spread = min(base_spread + (item_count - 1) * 0.005, 0.333) # cap at 120°
for i in range(item_count):
t = float(i) / max(1, item_count - 1) if item_count > 1 else 0.5
item_hue = seed + (t * hue_spread) - (hue_spread / 2)
# Cycle saturation and brightness across 3 tiers for perceptual distance.
# Large brightness gaps (0.55 → 0.80 → 0.68) create strong ΔL* in Lab space,
# which dominates perceptual distance more than hue or saturation.
tier = i % 3
if tier == 0:
sat_dark, bri_dark = 0.70, 0.55
sat_light, bri_light = 0.45, 0.82
elif tier == 1:
sat_dark, bri_dark = 0.40, 0.78
sat_light, bri_light = 0.20, 0.95
else:
sat_dark, bri_dark = 0.55, 0.68
sat_light, bri_light = 0.35, 0.88
if mode in ("dark", "both"):
ih, is_, ib = adjust_for_contrast(item_hue, sat_dark, bri_dark, True)
lines.append(color_line(f"item{i}", ih, is_, ib,
f"{hex_from_hsb(ih, is_, ib)}"))
if mode in ("light", "both") and mode == "both":
ih, is_, ib = adjust_for_contrast(item_hue, sat_light, bri_light, False)
lines.append(color_line(f"lightItem{i}", ih, is_, ib,
f"{hex_from_hsb(ih, is_, ib)}"))
elif mode == "light":
ih, is_, ib = adjust_for_contrast(item_hue, sat_light, bri_light, False)
lines.append(color_line(f"item{i}", ih, is_, ib,
f"{hex_from_hsb(ih, is_, ib)}"))
# --- Semantic colors ---
lines.append("")
lines.append(" // MARK: - Semantic")
lines.append("")
lines.append(" static let success = Color(hue: 0.3889, saturation: 0.65, brightness: 0.70) // #3fb34f")
lines.append(" static let warning = Color(hue: 0.1111, saturation: 0.75, brightness: 0.90) // #e6a31a")
lines.append(" static let error = Color(hue: 0.0000, saturation: 0.70, brightness: 0.85) // #d94141")
lines.append("}")
lines.append("")
# Summary
lines.append(f"// Seed: {seed_deg}° | Mode: {mode} | Items: {item_count}")
lines.append(f"// Harmony: analogous (±30°) | Contrast: WCAG AA validated")
lines.append(f"// Dark text on light backgrounds, white text on dark backgrounds")
return "\n".join(lines)
def main():
parser = argparse.ArgumentParser(description="Generate a SwiftUI color palette")
parser.add_argument("--seed", type=int, required=True, help="Seed hue in degrees (0-360)")
parser.add_argument("--mode", choices=["light", "dark", "both"], default="both")
parser.add_argument("--items", type=int, default=0, help="Number of collection item colors")
parser.add_argument("--app", type=str, default="", help="App name for comment")
args = parser.parse_args()
if not 0 <= args.seed <= 360:
parser.error("Seed must be 0-360")
if args.items > 12:
import sys
print(f"Warning: {args.items} items requested. Perceptual distinguishability "
f"degrades above 12 items in an analogous palette. Consider using "
f"12 or fewer, or grouping items by category.", file=sys.stderr)
print(generate_palette(args.seed, args.mode, min(args.items, 20), args.app))
if __name__ == "__main__":
main()
#!/usr/bin/env python3
"""
Exhaustive verification of the palette generator.
Tests ALL 361 seed hues × 21 item counts = 7,581 combinations.
Verifies every output satisfies color theory invariants.
If this passes, the palette generator is proven correct by enumeration.
Usage:
python verify_palette.py # run full verification
python verify_palette.py --verbose # show each violation
"""
import sys
import colorsys
import argparse
import math
from generate_palette import generate_palette, hsb_to_rgb, relative_luminance, contrast_ratio
# --- CIE Lab conversion for perceptual distance ---
def rgb_to_xyz(r: float, g: float, b: float) -> tuple[float, float, float]:
"""Convert linear RGB (0-1) to CIE XYZ (D65 illuminant)."""
def linearize(c):
return c / 12.92 if c <= 0.04045 else ((c + 0.055) / 1.055) ** 2.4
rl, gl, bl = linearize(r), linearize(g), linearize(b)
x = 0.4124564 * rl + 0.3575761 * gl + 0.1804375 * bl
y = 0.2126729 * rl + 0.7151522 * gl + 0.0721750 * bl
z = 0.0193339 * rl + 0.1191920 * gl + 0.9503041 * bl
return x, y, z
def xyz_to_lab(x: float, y: float, z: float) -> tuple[float, float, float]:
"""Convert CIE XYZ to CIE Lab (D65 reference white)."""
xn, yn, zn = 0.95047, 1.00000, 1.08883 # D65
def f(t):
return t ** (1/3) if t > 0.008856 else (7.787 * t) + (16/116)
fx, fy, fz = f(x / xn), f(y / yn), f(z / zn)
L = 116 * fy - 16
a = 500 * (fx - fy)
b = 200 * (fy - fz)
return L, a, b
def hsb_to_lab(h: float, s: float, b: float) -> tuple[float, float, float]:
"""Convert HSB to CIE Lab via RGB → XYZ → Lab."""
r, g, bl = hsb_to_rgb(h, s, b)
x, y, z = rgb_to_xyz(r, g, bl)
return xyz_to_lab(x, y, z)
def delta_e(lab1: tuple, lab2: tuple) -> float:
"""CIE76 ΔE* — Euclidean distance in Lab space."""
return math.sqrt(sum((a - b) ** 2 for a, b in zip(lab1, lab2)))
def parse_palette_colors(output: str) -> list[dict]:
"""Extract all Color(...) values from generated Swift output."""
import re
colors = []
for line in output.split('\n'):
# Match: static let <name> = Color(hue: <h>, saturation: <s>, brightness: <b>)
m = re.search(
r'static let (\w+) = Color\(hue: ([\d.]+), saturation: ([\d.]+), brightness: ([\d.]+)\)',
line
)
if m:
colors.append({
'name': m.group(1),
'h': float(m.group(2)),
's': float(m.group(3)),
'b': float(m.group(4)),
})
# Match: Color.white
if 'Color.white' in line:
m2 = re.search(r'static let (\w+) = Color\.white', line)
if m2:
colors.append({'name': m2.group(1), 'h': 0, 's': 0, 'b': 1.0})
# Match: Color(white: <v>)
m3 = re.search(r'static let (\w+) = Color\(white: ([\d.]+)\)', line)
if m3:
colors.append({
'name': m3.group(1),
'h': 0, 's': 0, 'b': float(m3.group(2)),
})
return colors
def hue_distance(h1: float, h2: float) -> float:
"""Shortest angular distance between two hues (0-1 scale), returned in degrees."""
d = abs(h1 - h2)
d = min(d, 1.0 - d)
return d * 360.0
def verify_invariants(seed_deg: int, item_count: int, verbose: bool = False) -> list[str]:
"""Verify all invariants for a single (seed, items) combination."""
violations = []
output = generate_palette(seed_deg, "both", item_count, "test")
colors = parse_palette_colors(output)
seed_hue = seed_deg / 360.0
if not colors:
violations.append(f"seed={seed_deg}, items={item_count}: no colors parsed")
return violations
for c in colors:
name, h, s, b = c['name'], c['h'], c['s'], c['b']
# INVARIANT 1: HSB bounds
if not (0 <= h <= 1 and 0 <= s <= 1 and 0 <= b <= 1):
violations.append(f"BOUNDS: {name} h={h:.4f} s={s:.3f} b={b:.3f} out of [0,1]")
# INVARIANT 2: Light mode backgrounds are bright
if 'light' in name.lower() and 'background' in name.lower():
if b < 0.90:
violations.append(f"LIGHT_BG: {name} brightness={b:.3f} < 0.90")
if 'light' in name.lower() and 'surface' in name.lower():
if b < 0.90:
violations.append(f"LIGHT_SURFACE: {name} brightness={b:.3f} < 0.90")
# INVARIANT 3: Dark mode backgrounds are dark
if name in ('cardBackground', 'surface'):
if b > 0.25:
violations.append(f"DARK_BG: {name} brightness={b:.3f} > 0.25")
# INVARIANT 4: Contrast — white text on dark colors
dark_bg_names = ['primary', 'secondary', 'accent', 'cardBackground', 'surface']
dark_bg_names += [f'item{i}' for i in range(item_count)]
text_white_lum = relative_luminance(1, 1, 1)
for c in colors:
if c['name'] in dark_bg_names:
r, g, bl = hsb_to_rgb(c['h'], c['s'], c['b'])
bg_lum = relative_luminance(r, g, bl)
cr = contrast_ratio(text_white_lum, bg_lum)
if cr < 3.0: # AA large text minimum
violations.append(
f"CONTRAST: white on {c['name']} = {cr:.2f}:1 < 3.0:1 "
f"(h={c['h']:.4f} s={c['s']:.3f} b={c['b']:.3f})"
)
# INVARIANT 5: Light mode — dark text on light backgrounds
light_bg_names = [f'lightItem{i}' for i in range(item_count)]
text_dark_lum = relative_luminance(0.1, 0.1, 0.1)
for c in colors:
if c['name'] in light_bg_names:
r, g, bl = hsb_to_rgb(c['h'], c['s'], c['b'])
bg_lum = relative_luminance(r, g, bl)
cr = contrast_ratio(bg_lum, text_dark_lum)
if cr < 3.0:
violations.append(
f"CONTRAST_LIGHT: dark text on {c['name']} = {cr:.2f}:1 < 3.0:1 "
f"(h={c['h']:.4f} s={c['s']:.3f} b={c['b']:.3f})"
)
# INVARIANT 6: Collection items within harmonic range of seed
# Analogous: ±30° for ≤6 items. Extended analogous: ±60° for larger collections.
item_colors = [c for c in colors if c['name'].startswith('item') and not c['name'].startswith('light')]
max_dist = 35.0 if item_count <= 6 else 65.0 # allow wider spread for larger collections
for c in item_colors:
dist = hue_distance(c['h'], seed_hue)
if dist > max_dist:
violations.append(
f"HARMONY: {c['name']} hue={c['h']*360:.1f}° is {dist:.1f}° from seed {seed_deg}° (>{max_dist:.0f}°)"
)
# INVARIANT 7: Adjacent items are distinguishable (ΔH ≥ 3°)
if len(item_colors) >= 2:
for i in range(len(item_colors) - 1):
dist = hue_distance(item_colors[i]['h'], item_colors[i+1]['h'])
if dist < 2.0:
violations.append(
f"DISTINGUISH_HUE: {item_colors[i]['name']} and {item_colors[i+1]['name']} "
f"are only {dist:.1f}° apart"
)
# INVARIANT 8: Pairwise perceptual distance (CIE Lab ΔE*)
# Any two collection items must be perceptually distinguishable
if len(item_colors) >= 2:
for i in range(len(item_colors)):
for j in range(i + 1, len(item_colors)):
lab_i = hsb_to_lab(item_colors[i]['h'], item_colors[i]['s'], item_colors[i]['b'])
lab_j = hsb_to_lab(item_colors[j]['h'], item_colors[j]['s'], item_colors[j]['b'])
de = delta_e(lab_i, lab_j)
if de < 4.0: # ΔE < 4 means colors are barely distinguishable
violations.append(
f"PERCEPTUAL: {item_colors[i]['name']} and {item_colors[j]['name']} "
f"ΔE*={de:.1f} < 4.0 (barely distinguishable)"
)
# INVARIANT 9: Primary/secondary/accent are perceptually distinct from each other
core_names = ['primary', 'secondary', 'accent']
core_colors = [c for c in colors if c['name'] in core_names]
if len(core_colors) >= 2:
for i in range(len(core_colors)):
for j in range(i + 1, len(core_colors)):
lab_i = hsb_to_lab(core_colors[i]['h'], core_colors[i]['s'], core_colors[i]['b'])
lab_j = hsb_to_lab(core_colors[j]['h'], core_colors[j]['s'], core_colors[j]['b'])
de = delta_e(lab_i, lab_j)
if de < 15.0: # Core palette colors need strong separation
violations.append(
f"CORE_SEPARATION: {core_colors[i]['name']} and {core_colors[j]['name']} "
f"ΔE*={de:.1f} < 15.0 (too similar for core palette)"
)
# INVARIANT 10: Card background vs page background are distinguishable
card_bg = next((c for c in colors if c['name'] == 'cardBackground'), None)
surface = next((c for c in colors if c['name'] == 'surface'), None)
if card_bg and surface:
lab_card = hsb_to_lab(card_bg['h'], card_bg['s'], card_bg['b'])
lab_surface = hsb_to_lab(surface['h'], surface['s'], surface['b'])
de = delta_e(lab_card, lab_surface)
if de < 3.0:
violations.append(
f"BG_LAYERS: cardBackground and surface ΔE*={de:.1f} < 3.0 (layers indistinct)"
)
return violations
def main():
parser = argparse.ArgumentParser()
parser.add_argument('--verbose', action='store_true')
args = parser.parse_args()
total_tests = 0
total_violations = 0
all_violations = []
print("Exhaustive palette verification")
print(f"Testing all 361 seed hues × 21 item counts = 7,581 combinations\n")
for seed in range(361):
for items in range(21):
total_tests += 1
violations = verify_invariants(seed, items, args.verbose)
if violations:
total_violations += len(violations)
all_violations.extend(violations)
if args.verbose:
for v in violations:
print(f" FAIL seed={seed:3d}° items={items:2d}: {v}")
# Progress
if seed % 36 == 0:
print(f" [{seed:3d}/360] {total_tests:,} tests, {total_violations} violations")
print(f"\n{'='*60}")
print(f"Total tests: {total_tests:,}")
print(f"Total violations: {total_violations}")
if total_violations == 0:
print(f"\n✓ ALL INVARIANTS HOLD for every possible input.")
print(f" The palette generator is proven correct by exhaustive enumeration.")
else:
print(f"\n✗ {total_violations} violations found.")
# Show unique violation types
types = set(v.split(':')[0] for v in all_violations)
print(f" Violation types: {', '.join(sorted(types))}")
# Show first few
print(f"\n First 10 violations:")
for v in all_violations[:10]:
print(f" {v}")
sys.exit(1)
if __name__ == "__main__":
main()
Related skills
FAQ
What does ios-taste do?
ios-taste: A skill for development. This provides functionality for development workflows.
When should I use ios-taste?
When you need to use ios-taste for development tasks, or when ios-taste: a skill for development. this provides functionality for development workflows.
What are the main capabilities?
ios-taste.