# Onboarding illustrations — the decided direction

```
Status: Current
Owner: _null
Last reviewed: 2026-08-18
Governs: docs/design/dist/** — the onboarding illustration set and what it is for
Review trigger: Any illustration added or replaced here; the onboarding artwork
                in issue #29 landing in the app
```

> **References, not shipping assets.** Nothing in this folder goes in the APK.
> These are the decided direction for onboarding, drawn from
> [`../BRAND_GUIDE.md`](../BRAND_GUIDE.md) §22's master prompt. The vectors that
> eventually ship are drawn *from* these.

Seven illustrations in a matched light/dark pair, named for the composables they
replace in
[`../../../core/designsystem/src/main/kotlin/dev/privacyllc/period/designsystem/art/Illustrations.kt`](../../../core/designsystem/src/main/kotlin/dev/privacyllc/period/designsystem/art/Illustrations.kt):

| File stem | Onboarding step | Composable |
| --- | --- | --- |
| `01_WelcomeIllustration` | 1 Welcome | `WelcomeIllustration` |
| `02_LastPeriodIllustration` | 2 Last period start | *new* |
| `03_PeriodEndIllustration` | 3 Period end | *new* |
| `04_LearningIllustration` | 4 Previous history | `LearningIllustration` |
| `05_PrivacyIllustration` | 5 Privacy promise | `PrivacyIllustration` |
| `06_NotificationPrivacyIllustration` | 6 Reminder privacy | *new* |
| `07_ForecastIllustration` | 7 First forecast | *new* |
| `EmptyStateIllustration` | — not onboarding | `EmptyStateIllustration` |

Each stem exists as `Light/<stem>_light.webp` and `Dark/<stem>_dark.webp`, at
**identical dimensions per card**, so a theme switch swaps the image without
moving the layout.

`EmptyStateIllustration` is the odd one and deliberately unnumbered: it is
Today's "nothing logged yet" state, not an onboarding step, and it is also drawn
in the onboarding previews. Its ring is empty because nothing has been logged —
the same ring the other illustrations fill with cycle-day markers, which is what
makes the empty state read as *waiting* rather than *broken*.

## What these got right, and why it is worth not undoing

Each of these was a correction during review, and every one of them would be easy
to reintroduce by regenerating carelessly:

- **No text of any kind is painted into the pixels.** The app renders all seven
  titles and subtitles itself through `Heading(...)` in `OnboardingScreen.kt`.
  Art carrying its own title prints every heading twice, cannot be translated,
  cannot scale with the user's font size, and is invisible to TalkBack.
- **No step-number badge.** `LearningIllustration` is not only onboarding step 4
  — it is also the Insights empty state, where a "4" means nothing. Baked numbers
  also freeze the running order, and this flow has already changed shape once. A
  step indicator, if wanted, belongs in live UI where TalkBack can read it.
- **`07_Forecast` shows a calendar and a cycle arc, and names no fields.** An
  earlier draft drew a forecast panel listing *Fertile window* and *Ovulation*,
  which the real first-forecast screen deliberately does not show — on a fresh
  install it reports confidence Low and Today reads "Not enough history to
  estimate". A later draft named the correct fields but painted *Confidence —
  Low* permanently, which is wrong for anyone with a settled cycle. Naming
  nothing is the only version that cannot contradict the live forecast.
- **The dark set is a real dark composition**, not the light one dimmed — the
  midnight-plum ground `BRAND_GUIDE.md` §22 specifies.

## These now ship

**#29 is closed.** All eight are in the app, in both themes, as WebP drawables in
[`../../../core/designsystem/src/main/res/`](../../../core/designsystem/src/main/res/)
— `drawable/` for light and `drawable-night/` for dark, so Android resolves the
theme rather than any code of ours. The files here stay as the full-resolution
sources; the shipped copies are downscaled to the height they are actually drawn
at.

They are **not** redrawn as vectors, which is what #29 originally called for.
That was the right plan for artwork that did not exist and the wrong one for
gradient landscapes: there is no honest `VectorDrawable` of one, §42 objects only
to *unnecessary* raster, and the whole set costs 130 KB of the release APK.
`CycleMarkers.kt` keeps its vectors, because a marker's shape carries meaning and
the progress arc is drawn from the user's own cycle.

**The painted-in corners needed clipping.** Each drawing is composed as a card
with its own rounded corner, so the pixels outside that curve are the card's own
backdrop — near-black in the dark set, near-white in the light one. Drawn
unclipped they showed as four notches against the app background: obvious on a
device, invisible in code review. `Illustration` in `Illustrations.kt` clips just
inside the painted curve, and what is left reads as a deliberate rounded card.

## Format

WebP at the delivered resolution — 312 KB for all sixteen, against 6.8 MB as
PNG, and the same format [`../../data/img/`](../../data/img/) uses for the marks
privacyllc.dev renders. The delivery `.zip` archives are deliberately not
tracked; see [`.gitignore`](.gitignore).
