Render pixel-accurate Apple Notes (iPhone, light mode) screenshot mockups from a JSON note spec. Outputs HTML + PNG at the iPhone 16/15 Pro native 1180×2556. Supports paragraphs, images, checklists, dividers, autocorrect underline, smart quotes, and an optional iOS keyboard chrome overlay used by the parent video-ad molecule.
Generate Apple Notes screens that look like real iPhone screenshots — correct status bar, floating toolbar pill, bold title, body text with paragraph spacing, yellow text cursor, autocorrect underline, optional embedded images and checklists, and an optional iOS keyboard.
Purpose
Turn a JSON note spec into a pixel-accurate Apple Notes (iPhone, light mode) screenshot — HTML + PNG at the native 1180×2556 (DPR 2). It is the deterministic, no-LLM renderer that the create-apple-notes-video-ad molecule drives frame-by-frame to fake a typing animation, and a standalone tool for one-off "noted to self" beats in ads, social posts, and storyboards. The orchestrating agent owns the copy; this atom owns the fidelity (status bar, smart punctuation, cursor, autocorrect underline, toolbar, keyboard chrome).
When to use
Need a fake Apple Notes screenshot for an ad, social post, video scene, or product mockup.
Need a sequence of Apple Notes frames (title-only → adding paragraphs → cursor moves) to drive a typing-animation video — used by create-apple-notes-video-ad.
Need a static "noted to self" beat in a storyboard.
If you're rendering an iMessage thread, use skills/atoms/messaging/create-imessage-mockup instead.
Inputs
The skill is deterministic — it does not embed an LLM. The orchestrating agent composes a note JSON matching the schema below, then invokes the renderer.
Note JSON schema
{
"title": "Hello",
"body": [
{ "type": "paragraph", "text": "This is a screen recording of me typing on apple notes app." },
{ "type": "paragraph", "text": "Let's see how this feels." }
],
"cursor": "end",
"autocorrect_underline": ["this", "how"],
"status_bar": {
"time": "9:41",
"battery_pct": 87,
"battery_low": false,
"show_focus_glyph": false
},
"show_keyboard": false,
"keyboard_state": {
"suggestions": ["see", "go", "do"],
"shift": "lower",
"letters_row1": "qwertyuiop",
"letters_row2": "asdfghjkl",
"letters_row3": "zxcvbnm"
},
"with_iphone_frame": false
}
title — string. Bold display headline, single line.
image — { "src": "<path|url>", "caption": "optional" }. Relative paths are resolved against the spec file's directory and inlined as data URIs.
checklist — { "items": [{ "text": "...", "checked": true|false }] }. Checked items get yellow filled circles with strikethrough; unchecked get gray-outlined circles.
divider — horizontal rule.
cursor — "title" | "end" | null. "title" places the yellow caret at the end of the title; "end" places it at the end of the last paragraph; null hides it.
autocorrect_underline[] — exact-case word list. Each occurrence gets the Apple Notes mid-typing yellow underline (<span class="spell">).
status_bar — time, battery_pct (0–100), battery_low (color battery red), show_focus_glyph (DND/car icon next to time). The Dynamic Island is not rendered.
show_keyboard — true overlays the iOS QWERTY + formatting toolbar.
keyboard_state — only consulted when show_keyboard is true. shift = "lower" | "upper".
with_iphone_frame — adds a thin dark bezel inset (for hero compositions).
parent dir for the dated output folder; default ./apple-notes-mockup-exports/
--name <slug>
override the output folder slug
--out-html <path> / --out-png <path>
write directly to explicit paths (skip dated folder)
--viewport WxH
override the render viewport (default 1180x2556)
--dpr N
device-pixel ratio (default 2)
Output
<output>/<YYYY-MM-DD>-<slug>/
index.html # full standalone HTML (images inlined as data URIs)
screenshot.png # rendered PNG at the chosen viewport × DPR
spec.json # copy of the input for reproducibility
Default <output> is ./apple-notes-mockup-exports/ in the cwd.
Workflow
Receive a prompt or existing note JSON.
If only a prompt was given: compose a note.json matching the schema, save it.
Run node render.js --note <path> [--with-keyboard].
Open the resulting PNG to verify the layout. If something looks wrong (wrong wrap, wrong cursor position, wrong battery color), edit the JSON and re-render.
Setup (one-time)
cd create-apple-notes-mockup
npm install
npx playwright install chromium
A symlink to create-imessage-mockup/node_modules ships with the repo, so on a fresh clone npm install is only needed if you want this skill to be self-contained.
6 reference notes covering every variation (title-only, mid-typing, long multi-paragraph, with checklist, with image, frame-1 lowercase-keyboard)
tests/run-all.sh
Render every example into tests/output/ for visual review
apple-notes-skill-build.html
Side-by-side review board comparing reference video frames to generated PNGs
Quality Checks
Before declaring a note shippable:
Title renders as bold display headline, not regular weight.
Body text uses Apple's smart punctuation — ' is curly, " is curly, … is a single character.
Yellow cursor (#FFCC00) sits flush at the end of the target text, ~5px wide.
Body paragraphs are separated by one empty visual line (no manual <br>).
Autocorrect underlines are short yellow lines flush under the flagged token, with case-sensitive matching.
Status bar: time left, signal/wifi/battery right. Battery is red iff battery_low: true. No Dynamic Island.
Scroll mask: status bar and toolbar have opaque white backgrounds so body content scrolled by the molecule's driver doesn't bleed through. Verify by translating .note by -300px in DevTools and confirming the title is fully masked by the toolbar — not visible behind/through it.
Toolbar: floating back-chevron pill (left), white capsule with undo/share/more, yellow Done circle (right). All cast soft shadows.
Font width differs from reference (Chromium picked up a non-SF fallback)
Adjust font-size on .note-body in steps of 2px, or widen .note by reducing left/right padding
Title looks thin / not bold enough
Host doesn't have SF Pro Display; system falls back to a lighter face
Set font-weight: 900 (already the default) and check `fc-list
"Hello's", "Let's" render with straight quote
Smart-punctuation pre-processor disabled
Verify smartQuotes() is called in generate.js before escapeHtml
Image doesn't render (blank space)
Remote URL blocked or relative path didn't resolve
Use a local file path; render.js inlines local images as base64
Autocorrect underline also matches "This" when only "this" was intended
Regex was case-insensitive
The regex is case-sensitive by default. Pass exact-case strings in autocorrect_underline
Empty paragraph collapses (no cursor space)
<p class="note-paragraph"></p> with no content has zero height
The CSS sets margin: 0 0 84px 0 so an empty paragraph still occupies one paragraph gap; ensure you didn't strip the trailing margin
Yellow Done circle looks orange
CSS color drift
Pure Apple yellow is #FFCC00. Cursor + Done share the value; do not change to #FFC107 (Material amber)
Keyboard suggestion Hello's renders with straight apostrophe
Smart quotes not applied to suggestions
Confirmed applied via smartQuotes(s) in generate.js
Reference
The visual spec was extracted from apple-notes-typing.MP4 (a 36-second iPhone screen recording of typing into Apple Notes in light mode). See apple-notes-skill-build.html in this folder for the side-by-side proof — each example is shown alongside the reference frame it targets.
1---2name: create-apple-notes-mockup3description: Render pixel-accurate Apple Notes (iPhone, light mode) screenshot mockups from a JSON note spec. Outputs HTML + PNG at the iPhone 16/15 Pro native 1180×2556. Supports paragraphs, images, checklists, dividers, autocorrect underline, smart quotes, and an optional iOS keyboard chrome overlay used by the parent video-ad molecule.4---56# create-apple-notes-mockup
78Generate Apple Notes screens that look like real iPhone screenshots — correct status bar, floating toolbar pill, bold title, body text with paragraph spacing, yellow text cursor, autocorrect underline, optional embedded images and checklists, and an optional iOS keyboard.
910## Purpose
1112Turn a JSON note spec into a pixel-accurate Apple Notes (iPhone, light mode) screenshot — HTML + PNG at the native 1180×2556 (DPR 2). It is the deterministic, no-LLM renderer that the `create-apple-notes-video-ad` molecule drives frame-by-frame to fake a typing animation, and a standalone tool for one-off "noted to self" beats in ads, social posts, and storyboards. The orchestrating agent owns the copy; this atom owns the fidelity (status bar, smart punctuation, cursor, autocorrect underline, toolbar, keyboard chrome).
1314## When to use
1516- Need a fake Apple Notes screenshot for an ad, social post, video scene, or product mockup.
17- Need a sequence of Apple Notes frames (title-only → adding paragraphs → cursor moves) to drive a typing-animation video — used by `create-apple-notes-video-ad`.
18- Need a static "noted to self" beat in a storyboard.
1920If you're rendering an iMessage thread, use `skills/atoms/messaging/create-imessage-mockup` instead.
2122## Inputs
2324The skill is deterministic — it does not embed an LLM. The orchestrating agent composes a note JSON matching the schema below, then invokes the renderer.
2526### Note JSON schema
2728```json
29{
30 "title": "Hello",
31 "body": [
32 { "type": "paragraph", "text": "This is a screen recording of me typing on apple notes app." },
33 { "type": "paragraph", "text": "Let's see how this feels." }
34 ],
35 "cursor": "end",
36 "autocorrect_underline": ["this", "how"],
37 "status_bar": {
38 "time": "9:41",
39 "battery_pct": 87,
40 "battery_low": false,
41 "show_focus_glyph": false
42 },
43 "show_keyboard": false,
44 "keyboard_state": {
45 "suggestions": ["see", "go", "do"],
46 "shift": "lower",
47 "letters_row1": "qwertyuiop",
48 "letters_row2": "asdfghjkl",
49 "letters_row3": "zxcvbnm"
50 },
51 "with_iphone_frame": false
52}
53```
5455- `title` — string. Bold display headline, single line.
56- `body[]` — block list. Supported block types:
57 - `paragraph` — `{ "text": "..." }`. Smart punctuation is auto-applied (`'` → `'`, `"` → `"`, `...` → `…`, `--` → `–`, `---` → `—`).
58 - `image` — `{ "src": "<path|url>", "caption": "optional" }`. Relative paths are resolved against the spec file's directory and inlined as data URIs.
59 - `checklist` — `{ "items": [{ "text": "...", "checked": true|false }] }`. Checked items get yellow filled circles with strikethrough; unchecked get gray-outlined circles.
60 - `divider` — horizontal rule.
61- `cursor` — `"title" | "end" | null`. `"title"` places the yellow caret at the end of the title; `"end"` places it at the end of the last paragraph; `null` hides it.
62- `autocorrect_underline[]` — exact-case word list. Each occurrence gets the Apple Notes mid-typing yellow underline (`<span class="spell">`).
63- `status_bar` — `time`, `battery_pct` (0–100), `battery_low` (color battery red), `show_focus_glyph` (DND/car icon next to time). The Dynamic Island is not rendered.
64- `show_keyboard` — `true` overlays the iOS QWERTY + formatting toolbar.
65- `keyboard_state` — only consulted when `show_keyboard` is true. `shift` = `"lower" | "upper"`.
66- `with_iphone_frame` — adds a thin dark bezel inset (for hero compositions).
6768## CLI
6970```bash
71node render.js --note examples/mid.json
72node render.js --note examples/long.json --output ./my-exports/ --name todays-note
73node render.js --note my-note.json --with-keyboard
74node render.js --note my-note.json --viewport 1080x1920 # 9:16 ad delivery size
75```
7677### Flags
7879| Flag | Effect |
80|---|---|
81| `--note <path>` | (required) JSON file matching the schema above |
82| `--with-keyboard` | force-enables `show_keyboard` (overrides spec) |
83| `--with-iphone-frame` | force-enables hardware bezel |
84| `--no-frame` | force-disables hardware bezel |
85| `--output <dir>` | parent dir for the dated output folder; default `./apple-notes-mockup-exports/` |
86| `--name <slug>` | override the output folder slug |
87| `--out-html <path>` / `--out-png <path>` | write directly to explicit paths (skip dated folder) |
88| `--viewport WxH` | override the render viewport (default `1180x2556`) |
89| `--dpr N` | device-pixel ratio (default `2`) |
9091## Output
9293```
94<output>/<YYYY-MM-DD>-<slug>/
95 index.html # full standalone HTML (images inlined as data URIs)
96 screenshot.png # rendered PNG at the chosen viewport × DPR
97 spec.json # copy of the input for reproducibility
98```
99100Default `<output>` is `./apple-notes-mockup-exports/` in the cwd.
101102## Workflow
1031041. Receive a prompt or existing note JSON.
1052. If only a prompt was given: compose a `note.json` matching the schema, save it.
1063. Run `node render.js --note <path> [--with-keyboard]`.
1074. Open the resulting PNG to verify the layout. If something looks wrong (wrong wrap, wrong cursor position, wrong battery color), edit the JSON and re-render.
108109## Setup (one-time)
110111```bash
112cd create-apple-notes-mockup
113npm install
114npx playwright install chromium
115```
116117A symlink to `create-imessage-mockup/node_modules` ships with the repo, so on a fresh clone `npm install` is only needed if you want this skill to be self-contained.
118119## Files
120121| File | Purpose |
122|---|---|
123| `render.js` | CLI entry — parses flags, resolves image paths, generates HTML, takes screenshot |
124| `generate.js` | Note JSON → standalone HTML page (smart-punctuation + autocorrect underline applied here) |
125| `screenshot.js` | HTML → PNG via Playwright (chromium headless) at 1180×2556, DPR 2 |
126| `templates/note.css` | All visual styling (status bar, toolbar, body, checklist, image, keyboard) |
127| `templates/icons.js` | Inline SVG icons (back chevron, undo, share, more, done check, signal, wifi, battery, format toolbar icons, keyboard glyphs) |
128| `assets/samples/sample-landscape.jpg` | Sample image used by `examples/with-image.json` |
129| `examples/*.json` | 6 reference notes covering every variation (title-only, mid-typing, long multi-paragraph, with checklist, with image, frame-1 lowercase-keyboard) |
130| `tests/run-all.sh` | Render every example into `tests/output/` for visual review |
131| `apple-notes-skill-build.html` | Side-by-side review board comparing reference video frames to generated PNGs |
132133## Quality Checks
134135Before declaring a note shippable:
136137- [ ] Title renders as bold display headline, not regular weight.
138- [ ] Body text uses Apple's smart punctuation — `'` is curly, `"` is curly, `…` is a single character.
139- [ ] Yellow cursor (`#FFCC00`) sits flush at the end of the target text, ~5px wide.
140- [ ] Body paragraphs are separated by one empty visual line (no manual `<br>`).
141- [ ] Autocorrect underlines are short yellow lines flush under the flagged token, with case-sensitive matching.
142- [ ] Status bar: time left, signal/wifi/battery right. Battery is red iff `battery_low: true`. No Dynamic Island.
143- [ ] **Scroll mask:** status bar and toolbar have opaque white backgrounds so body content scrolled by the molecule's driver doesn't bleed through. Verify by translating `.note` by `-300px` in DevTools and confirming the title is fully masked by the toolbar — not visible behind/through it.
144- [ ] Toolbar: floating back-chevron pill (left), white capsule with undo/share/more, yellow Done circle (right). All cast soft shadows.
145- [ ] Keyboard (when enabled): QWERTY layout matches `shift` casing, suggestion strip shows 3 words, formatting toolbar pill above keys, mic + globe at bottom.
146- [ ] Image blocks render with rounded corners (~18px) and respect the body left padding.
147- [ ] Checklist: filled yellow circles + strikethrough on checked items; gray outline circles otherwise.
148149## Failure Modes
150151| Symptom | Cause | Fix |
152|---|---|---|
153| Body text wraps a word too early or too late | Font width differs from reference (Chromium picked up a non-SF fallback) | Adjust `font-size` on `.note-body` in steps of 2px, or widen `.note` by reducing left/right padding |
154| Title looks thin / not bold enough | Host doesn't have SF Pro Display; system falls back to a lighter face | Set `font-weight: 900` (already the default) and check `fc-list | grep -i "SF Pro"` |
155| "Hello's", "Let's" render with straight quote | Smart-punctuation pre-processor disabled | Verify `smartQuotes()` is called in `generate.js` before `escapeHtml` |
156| Image doesn't render (blank space) | Remote URL blocked or relative path didn't resolve | Use a local file path; render.js inlines local images as base64 |
157| Autocorrect underline also matches "This" when only "this" was intended | Regex was case-insensitive | The regex is case-sensitive by default. Pass exact-case strings in `autocorrect_underline` |
158| Empty paragraph collapses (no cursor space) | `<p class="note-paragraph"></p>` with no content has zero height | The CSS sets `margin: 0 0 84px 0` so an empty paragraph still occupies one paragraph gap; ensure you didn't strip the trailing margin |
159| Yellow Done circle looks orange | CSS color drift | Pure Apple yellow is `#FFCC00`. Cursor + Done share the value; do not change to `#FFC107` (Material amber) |
160| Keyboard suggestion `Hello's` renders with straight apostrophe | Smart quotes not applied to suggestions | Confirmed applied via `smartQuotes(s)` in `generate.js` |
161162## Reference
163164The visual spec was extracted from `apple-notes-typing.MP4` (a 36-second iPhone screen recording of typing into Apple Notes in light mode). See `apple-notes-skill-build.html` in this folder for the side-by-side proof — each example is shown alongside the reference frame it targets.
Run npx skillmds add majiayu000/create-apple-notes-mockup in your terminal (requires Node.js), paste this page's agent-chat prompt into Claude, Cursor, or any MCP-connected agent, or download the SKILL.md file and copy it into your agent's skills directory.
Render pixel-accurate Apple Notes (iPhone, light mode) screenshot mockups from a JSON note spec. Outputs HTML + PNG at the iPhone 16/15 Pro native 1180×2556. Supports paragraphs, images, checklists, dividers, autocorrect underline, smart quotes, and an optional iOS keyboard chrome overlay used by the parent video-ad molecule. It is listed under Web & Frontend on SkillMD.
This skill has not completed SkillMD's automated safety review yet. Capability flags: reads secrets. SkillMD never runs a skill's scripts for you; review the SKILL.md before installing.
This skill is tagged as working with Claude Code, Claude.ai, OpenAI Codex. SKILL.md is an open format, so most agents that read a skills directory can load it too.
Yes. Installing skills from SkillMD is free, and the skill stays under its author's original license.
majiayu000 (@majiayu000) published this skill. Their other Agent Skills are listed on their SkillMD profile.