# Building Duo-Blur with Claude Code

Duo-Blur was built with [Claude Code](https://claude.com/claude-code) over about two weeks
of sessions, from 11 to 24 September 2026. This file is the prompting side of it: the
prompts that changed direction, and one combined prompt per stage so you can rebuild each
result yourself.

It wasn't one prompt. Some days went in circles, mostly over the same mistake (putting the
effects on the image instead of the screen). The combined prompts below already contain
what those days taught, which is why they're long.

The write-up is a two-part series on [designengineer.ing](https://designengineer.ing):

1. [The fold](https://designengineer.ing/iphone-duo-fold-variable-blur-swiftui): gyro tilt and the progressive blur
2. [The OS and Mail](https://designengineer.ing/iphone-duo-mini-os-swiftui): the mini OS around it, and Mail as the first real app

## Before you start

- **Xcode 26 or later** and an **iOS 26+** target. A **real iPad** for the fold: the
  Simulator has no gyroscope.
- **Give Claude your references, not descriptions.** Every good turn in these sessions
  started from an image, a screenshot from the device, or a Figma link:
  - A reference image or video of the effect you're after.
  - Apple's [Designing for iPhone Duo](https://developer.apple.com/design/human-interface-guidelines/designing-for-iphone-duo) HIG.
  - The [iOS and iPadOS 27 Figma kit](https://www.figma.com/community) (connect the Figma MCP
    server so Claude can read node values instead of eyeballing them).
- **Keep the rules in `CLAUDE.md`.** This repo's [CLAUDE.md](CLAUDE.md) is the "iPad Duo
  Mini OS" rulebook that Claude reads at the start of every session. Write yours early;
  it's what stops the same correction being needed twice.
- **Ask for understanding before code.** The most useful habit in these sessions was
  "tell me what you understood first", then "now implement".

---

## Part 1: The fold

### The prompts that mattered

> Here is what we are going to build using the gyroscope for iPad. Before writing any
> code, tell me what you can see.

> Can you add a visualiser for the progressive blur, and sliders for the blur values, so I
> can tune them?

> We need to make all the effects on the screen, not on the image.

> The blur is not on the image but on the screen surface. The blur near the edge of the
> left screen comes first as the screen moves, so it doesn't show that the screen is
> empty, and by the time it goes above 45 degrees it reaches the middle. Check the
> screenshot and talk to me before making any changes.

> 1. Left panel only.
> 2. We are simulating the fold, so the right surface acts like the second half.
> 3. The icons should only move about 24 px from the centre, with a spring.

> Instead of having multiple sliders, tie all of them to one slider.

> I'd like it to go to a max of 85% and then stick with the same blur and everything.

### Rebuild it in one prompt

```text
Build an iPad app in SwiftUI (iOS 26+, landscape only) that fakes a
book-fold display on a flat screen, driven by the gyroscope.

- Lay out a static home screen on a 1194 × 834 design canvas and
  scale it to cover the display (not fit).
- Read tilt from CoreMotion's gravity vector: asin of the component,
  normalised to -1…1, low-pass smoothed, 60 Hz. Use the device's long
  axis (gy), flip the sign for landscapeLeft, and add a small deadzone
  so a level iPad stays perfectly flat.
- Split the screen at the centre crease, rendering both halves from
  the same canvas so there's no seam. Keep the right half flat. Rotate
  the left half about the crease with rotation3DEffect, up to 80°,
  and cap the fold at 85%.
- Put every effect on the screen surface, never on the image:
  a progressive blur (Core Animation's private variableBlur filter on
  a UIVisualEffectView's backdrop; its mask is read by ALPHA, so use a
  white image whose alpha fades) plus a dark scrim, over the left half
  only, heaviest at the outer edge.
- The frost leads the hinge: it sweeps from the outer edge and reaches
  the crease when the fold is halfway (about 45°), with a front-loaded
  curve so most of the blur arrives early.
- Nudge the right-hand widgets and icon grid right by up to 24 pt as
  it folds.
- Keep every tuning value (max blur, scrim, hinge angle, cap, gain) as
  constants at the top of the view.
- Add one Simulator-only slider in place of the gyro, so it can be
  tuned at the desk.

Tell me what you understood before implementing, then show me the
result in the iPad simulator after each step.
```

---

## Part 2: The OS

The OS covers the Duo's inner display only. An iPad has one screen, so the outer display
isn't simulated.

### The prompts that mattered

> This is what we are trying to emulate: Apple's Designing for iPhone Duo. We don't have an
> official emulator yet and we have an iPad, so make the logic work for our use case.

> Is there any way we can get the spec for iOS 27 from the Figma file?

> We need to make the designs at iPhone Duo size and then scale them to the iPad.

> We're emulating a device that hasn't launched, as a mini OS inside the iPad. Make a note
> that everything we build from now on follows the iPad Duo Mini OS.

> Are you aware of how the app opening animation works in iOS?

> Anywhere else we show the status bar, it should be a live clock.

### Rebuild it in one prompt

```text
Turn the fold app into a small "iPhone Duo" OS running full screen on
an iPad. The iPad is only the host display: every screen must read as
Duo system UI, never as an iPad app in a window.

- First, pull the iOS 27 Duo spec from the Figma kit (colours, type
  sizes, the vertical control bar, layout margins, display sizes) into
  docs/iOS27-Duo-Spec.md and a DS enum of design tokens, each value
  noting the Figma node it came from. Snap type to the standard iOS
  sizes.
- Author every component at the Duo's native size (inner display
  951 × 669) and scale it onto the iPad once, by height:
  k = 834 / 669, aspect ratio preserved, centred.
- Hide every trace of iPadOS: ignoresSafeArea, statusBarHidden,
  persistentSystemOverlays(.hidden), and UIRequiresFullScreen so the
  landscape lock holds.
- One DuoStatusBar component (live clock over Wi-Fi) used on every
  screen, with an optional frozen time for pixel comparison.
- A DuoRootView shell: the folding home screen, and apps that open by
  zooming out of their icon's exact frame as an opaque tile, the
  corner radius morphing from the icon squircle to the screen. The
  home dims, blurs and scales back behind. Close reverses it.
- A DuoAppTemplate from the kit: the trailing vertical bar with status
  cluster, nav capsule, tab capsule and detached search, in Liquid
  Glass. Add a pixel-check preview that renders it 1:1 on a chroma key
  with the clock at 9:41.
- A DuoArrangement primitive shaped like Apple's ArrangementView
  (primary, secondary, split/overlay style) that takes an explicit
  fold value, so it can be swapped for the real one later.
- Write all of these rules into CLAUDE.md.

Tell me what you understood before implementing, and run the iPad
simulator after each step.
```

---

## Part 2, continued: Mail

### The prompts that mattered

> Implement the navigation on the tap of Mail. I've drawn rectangles in Figma for the
> spacing. Keep in mind we're making a working app: the content scrolls and we can select
> threads.

> There's a click angle of the fold, from 180 to about 170, where it goes 50/50.
> Otherwise it stays the way we have it.

> We need a boolean for the switch: tilt right to go 50/50 and tilt left to go back to
> the default screen.

> Now I want more interaction. Write the design for the taps first: compose, reply,
> reply all, forward and delete.

> We don't have to open the keyboard by default when replying. Also use the
> .numericText animation when the email changes.

> The blur will happen, and then the layout shift will happen.

### Rebuild it in one prompt

```text
Build Mail as the first Duo app, following the mini OS rules in
CLAUDE.md. Before writing code, write the interaction design to
docs/Mail-Interactions.md (what each tap does, sheets, timings, Undo)
and check it with me.

- Inbox list and reading pane in a split DuoArrangement. The flat
  ratio comes from the Figma frame; a partial fold is 50/50. The
  control rail stays pinned to the trailing edge.
- Tilt is a switch, not a dial: tilting right past 10° sets 50/50,
  tilting left past 10° sets the default split, and level keeps the
  last one. Hide each switch behind the fold frost in three beats:
  frost the left half fully, change the layout under it, then clear.
  Keep the 60 Hz frost in its own small view.
- A vertical rail of real buttons: they sink to 0.92 with a soft
  highlight while pressed and bounce their SF Symbol on release.
- One compose sheet for Compose, Reply, Reply All and Forward; only
  the prefill and the starting focus differ. Replies open without the
  keyboard. In a partial fold the sheet moves to the leading half so
  the original stays readable.
- Draw the Duo keyboard from the kit as a SwiftUI view at native size
  that only reports keys; a MailDraft model handles typing, recipient
  tokens and Return.
- Delete with no confirmation: the message shrinks into the trash
  button, the row collapses, the next email is selected, and an Undo
  capsule shows for 4 seconds. Send shows Undo Send for 10 seconds.
- Simulator-only dev controls: a tilt slider and a Figma reference
  overlay, wrapped in #if targetEnvironment(simulator).

Tell me what you understood before implementing, and run the iPad
simulator after each step.
```
