gooseworks-ai/render-ios-keyboard
Render a static iOS QWERTY keyboard as inline HTML+CSS sized for the 750-wide 9:16 stage. Includes the 3-suggestion bar, alpha keys, shift/backspace, 123/emoji/space/return row, and the bottom globe+mic strip. No animation logic — slide up/down is the molecule's job (CSS transform on the .keyboard root).
npx skills add https://github.com/gooseworks-ai/goose-skills --skill render-ios-keyboard
Provide a drop-in iOS QWERTY keyboard fragment that pairs with messaging atoms (create-chatgpt-mockup, create-imessage-mockup) when a video needs to show someone typing. Static only: the keyboard sits at the bottom of the screen, the molecule's recorder slides it up/down with transform: translateY(...).
const { renderKeyboardHTML, renderKeyboardCSS } = require('./generate');
const html = renderKeyboardHTML({
suggestions: ['"He"', 'Hey', 'Heating'], // optional, default ['I', 'The', "I'm"]
layout: 'qwerty-lower', // 'qwerty-lower' | 'qwerty-upper' | 'numbers'
});
The atom returns an HTML fragment (one <div class="ios-keyboard">…</div>) plus the matching CSS. The CSS uses scoped class names so it won't collide with the rest of the page.
const { renderKeyboardHTML, renderKeyboardCSS } = require('./generate'); — no build step, no dependencies beyond Node's fs/path.renderKeyboardHTML reads opts.layout and looks it up in ROWS: qwerty-lower (default), qwerty-upper, or numbers. An unknown layout silently falls back to qwerty-lower. The molecule decides which state to show; this atom never switches on its own.opts.suggestions is an array of up to three strings rendered into the suggestion bar; each value is run through escapeHTML so quotes/angle brackets are safe. Omit it to get the default ['I', 'The', "I'm"].<div class="ios-keyboard" data-layout="…"> containing, in order: the .kb-suggestions bar, three letter rows (row-1 10 keys, row-2 9 keys padded with two 18px spacers, row-3 = shift/#+= modifier + 7 keys + backspace), the row-bottom (123/ABC switch, emoji, wide space, return), and the .kb-system-row globe + mic strip.renderKeyboardCSS() (which reads templates/keyboard.css from disk) and place it in a <style> block on the page. The molecule mounts the fragment at the bottom of the stage next to .composer-wrap.transform. The owning molecule slides the keyboard in/out via transform: translateY(...) on the .ios-keyboard root (the CSS sets will-change: transform for this).render.js wraps the fragment + CSS in a 750×1334 white .stage HTML page and writes it to --out for eyeballing padding/colors.node render.js --out /tmp/kb.html --suggestions 'I,The,I'\''m'
Produces a standalone HTML page with just the keyboard, useful for tweaking padding/colors.
| Output | Shape |
|---|---|
| renderKeyboardHTML(opts) | HTML string (one root <div class="ios-keyboard">) |
| renderKeyboardCSS() | CSS string (scoped to .ios-keyboard *) |
The molecule injects these into the chatgpt-mockup page next to .composer-wrap.
| File | Purpose |
|---|---|
| generate.js | Returns {renderKeyboardHTML, renderKeyboardCSS} |
| render.js | Standalone CLI for visual review |
| templates/keyboard.css | All keyboard styling |
layout but are visual-only — the molecule decides which to show.renderKeyboardHTML() returns a single root element starting with <div class="ios-keyboard" and carrying the matching data-layout attribute (qwerty-lower | qwerty-upper | numbers)..kb-suggestions bar, three letter rows (.row-1, .row-2, .row-3), one .row-bottom, and one .kb-system-row — in that DOM order.suggestions array (default ['I', 'The', "I'm"]); the bar renders one .kb-suggestion span per entry with vertical dividers between adjacent pills.qwerty-lower renders lowercase letters and a shift glyph; qwerty-upper renders uppercase letters; numbers renders the digit/symbol rows with the #+= modifier and an ABC switch key instead of 123.'<b>' or '"He"' yields <b> / "He", never raw markup.renderKeyboardCSS() returns the full contents of templates/keyboard.css, scoped under .ios-keyboard, and the fragment carries no inline style except the two 18px row-2 spacer divs.123/emoji/return modifier keys are visibly wider than letter keys; space is one wide white pill; the globe sits bottom-left and mic bottom-right in the system strip.<script> and no JS event handlers — so a recorder can slide it with a CSS transform without re-rendering.layout value → ROWS[layout] is undefined, so the renderer silently falls back to qwerty-lower. Symptom: you asked for uppercase or numbers and got lowercase letters. Fix: pass exactly 'qwerty-lower', 'qwerty-upper', or 'numbers'..kb-suggestion (they flex: 1 1 0), so a long array squashes the pills and breaks the iOS-accurate 3-up layout. Keep suggestions to three or fewer; long strings are clipped with text-overflow: ellipsis.renderKeyboardHTML() without also emitting renderKeyboardCSS() produces an unstyled stack of divs (no rounded keys, no gray tray). Always include both on the page.templates/keyboard.css missing or unreadable → renderKeyboardCSS() calls fs.readFileSync and will throw ENOENT. Run from a checkout where templates/keyboard.css sits next to generate.js.render.js run without --out → the CLI prints the usage line and exits with code 1; nothing is written. Always pass --out <path>.Take gooseworks-ai/render-ios-keyboard from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.