Getting SwiftUI previews to stop crashing
A thread on what actually fixes preview crashes, and what only looks like it does.
Design system
Everything below is rendered by the stylesheet the site ships. The colour values and icon list are read out of the source at render time, so this page cannot drift from the build.
Direction 1b: a rounded device frame holding three skyline bars,
the centre one tallest and accented. The mark is inlined as SVG
rather than an <img> so the frame and outer
bars take currentColor while the centre bar takes
--color-accent — which is what lets one file serve
both themes.
Lockups
The mark at size
The 16px mark is a redraw, not a reduction: 1b drops to two bars
and thickens the stroke, because three bars silt up at that size.
Use logo-mark-16 below 20px.
Skyline motif
Terracotta is the only brand hue. Teal, ochre and plum exist for categories and platform chips; they are deliberately as earthy as the terracotta so a page of mixed tags still reads as one palette.
Ramps
Semantic tokens — light / dark
Measured from the tokens, in both themes, every time this page
renders. test/design/contrast_test.rb asserts the
same numbers, so a regression fails the build rather than waiting
to be noticed here.
| Pair | Tokens | Needs | Light | Dark |
|---|---|---|---|---|
| Body text |
--color-text on --color-surface |
4.5:1 | 15.57:1 | 15.57:1 |
| Secondary text |
--color-text-body on --color-surface |
4.5:1 | 7.16:1 | 11.53:1 |
| Muted metadata |
--color-text-muted on --color-surface |
4.5:1 | 5.82:1 | 6.41:1 |
| Subtle text (18px+) |
--color-text-subtle on --color-surface |
3.0:1 | 3.50:1 | 4.45:1 |
| Links |
--color-accent-text on --color-surface |
4.5:1 | 6.47:1 | 6.64:1 |
| Accent as fill only |
--color-accent on --color-surface |
3.0:1 | 4.39:1 | 6.64:1 |
| Primary button label |
--color-on-accent on --color-accent |
4.5:1 | 4.63:1 | 6.64:1 |
| Focus ring |
--color-focus on --color-surface |
3.0:1 | 4.39:1 | 6.64:1 |
| Success |
--color-success on --color-success-bg |
4.5:1 | 5.42:1 | 7.42:1 |
| Warning |
--color-warning on --color-warning-bg |
4.5:1 | 4.90:1 | 7.74:1 |
| Danger |
--color-danger on --color-danger-bg |
4.5:1 | 6.40:1 | 5.64:1 |
| Info |
--color-info on --color-info-bg |
4.5:1 | 5.28:1 | 6.76:1 |
Why the accent is split in two
1b's accent #B4542E measures 4.39:1 on its own
paper — under the 4.5:1 body text needs. It stays as
--color-accent for fills, graphics and the logo bar,
where the bar is 3:1. Links and accent text use
--color-accent-text (#8E3F20, 6.47:1)
instead. Same for the exploration's caption grey, which survives
only as --color-text-subtle at 18px and above.
Three roles. Newsreader carries the wordmark's voice into
headings; IBM Plex Sans does the reading; IBM Plex Mono handles
code, metadata and the .community lockup. Plex Sans
and Plex Mono are one superfamily, so they sit together without
being tuned.
Families
Shipping to the same small screens
The platforms differ, the problems rhyme. A community for people building on iOS, Android, Flutter and React Native.
swift build --configuration release
Scale
Tracking
Eyebrow · mono · 0.16em
.community · lockup · 0.2em
Lucide for the workhorse set, Simple Icons for platform marks, and
a small custom family drawn on 1b's grid. All compiled into one
inline <symbol> sprite by
bin/build-icons.mjs — inline rather than external,
because an external <use> does not inherit
currentColor in Safari or Firefox.
Platform marks are third-party trademarks, used as published rather than redrawn. That means they are not visually uniform: Apple, Android, Flutter and React are shape-only, while Swift and Kotlin are solid tiles with the mark knocked out. Expect the latter two to read heavier in a chip — it is their artwork, not a rendering fault.
Intents
Sizes, and with icons
On dark
The linked variant makes the whole card a target via a pseudo element on the title link, so the accessible name stays the title rather than the whole card's text.
A thread on what actually fixes preview crashes, and what only looks like it does.
Six months in, here's what we'd do differently.
A plain card, for content that isn't itself a destination.
Colour never carries meaning alone — every chip has a label, and platform chips carry their mark as well.
Platform chips
States, outlines, badges
Avatars
Heads up
Threads older than a year are archived but stay readable.
Your reply was posted.
You're editing a post from two years ago.
That username is already taken.
Pagination & breadcrumb
Long-form markup with no classes on it, inside
.prose — posts, guides, the code of conduct.
The platforms differ, the problems rhyme. A report that helps names the device, the OS version and what you expected instead.
Settings → AboutA bug report is a message to someone who cannot see your screen.
if #available(iOS 17, *) {
logger.debug("preview crash: \(context.description)")
}
Press ⌘ R to rebuild.