Describe the screen, don't draw it: a prompt playbook for UI specs without a design tool¶
I am a Python and C++ developer. I have built backend services, simulations, and command-line tools, and I have never opened Figma in my life. Yet this project -- a multi-device board game platform -- has a complete, detailed user-interface specification: a management-shell spec full of ASCII mockups, a per-screen breakdown for every screen, a state-by-state transition table, and a frozen layout doc for each game. None of it was drawn in a design tool. All of it was talked into existence with an AI, in words, and then frozen into files.
This is the second post in a builder's-eye series about building an app with AI. The first, From an idea in a chat window to a repo the robot can build, is the on-ramp: how a vague idea becomes planning docs in a git repo with a workflow orchestrator wired up. The third, The orchestrated build-and-feedback loop, covers what shipping through that orchestrator feels like once tickets are flowing. This middle post is the bridge between them: it is the part where a screen you can only describe in a sentence becomes a specification precise enough for the robot to build from.
The load-bearing idea is one sentence: you do not need to draw a UI to specify one. You need to answer the right questions in the right order. And ASCII mockups -- the kind you make out of + - | = characters -- are a fast, text-native design surface that both you and the AI can edit in words. "Move the join code to the top." "The timer is too prominent." That is the whole interaction. No pixels, no mouse, no learning a tool.
What follows is the actual method, as a playbook of seven copy-paste prompts. Each one is a stage. You run them roughly in order, looping inside a stage until you are happy, then moving on. They are grounded in how this project's real specs were produced -- I will show you a real screen from the repo, primed into the AI as an example, and edited in plain language.
Why a playbook and not one magic prompt¶
The single biggest mistake non-designers make with AI is asking for everything at once: "design me a settings screen." You get back a plausible-looking blob that is wrong in ways you cannot articulate, because you never decided what the screen was for. The fix is to decompose the act of specifying a UI into stages that each answer one question:
- Inventory -- what are the screens at all?
- Per-screen Q&A -- what does this one screen do, in every state, and what must it never show?
- ASCII mockup -- render it as text, then edit it in words.
- Variations -- when you can't picture it, ask for choices and pick.
- Freeze -- turn the agreed thing into a durable spec file.
- Constraint cross-check -- audit it against the hard rules.
- Example-priming -- seed the whole thing with a spec you already shipped, so the next screen inherits your conventions for free.
Run them in that order the first time. After that you will skip around. Here is each one.
Prompt 1: inventory the screens before you design any of them¶
Before you design a single screen you have to know how many there are. The trap is that "the same screen" often is not -- a TV showing the public game state and a player's phone showing their private hand are two different screens that happen to share a name. This project frames everything by role and device (TABLE on a TV, PLAYER on a phone, REFEREE on a tablet, OBSERVER anywhere, REVIEW post-game), and that framing falls straight out of the first prompt.
This prompt makes the AI interview you. It refuses to draw anything yet.
You are helping me specify the user interface for an app, and I am not a designer. Do not draw anything yet. First, interview me to build a screen inventory.
Ask me questions one at a time until we have enumerated every distinct screen. For each screen, capture exactly three things: 1. WHO looks at it (which role / kind of user), and on WHAT device (phone, tablet, TV, laptop). 2. The SINGLE most important thing that person does or learns there. 3. How they arrive at it and where they go next.
Rules: - One screen is one (role, device, primary-purpose) combination. If two roles look at "the same" screen but see different things or do different things, those are TWO screens, not one. - Do not let me describe layout, colors, or components yet. If I start, stop me and bring me back to who / device / purpose. - When you think the inventory is complete, play it back as a numbered table with columns: Screen | Who | Device | Primary purpose | Arrives from | Goes to. Then ask me what is missing before we continue.
Start by asking me what the app is and who uses it.
The output is a screen inventory table -- the same shape as the screen inventory that lives in this project's management-shell spec. You now have a map. Nothing is designed yet, and that is the point.
Prompt 2: specify one screen at a time, including what must NOT appear¶
Now you take one row of that inventory and pin it down completely -- still in words, still no drawing. The most important part of this prompt is the last question, and it is the one every "design me a screen" prompt omits: what must this screen never show?
That question is where a project's real constraints live. On this platform it is where two hard rules surface. Hidden-information games mean a player's private data must never appear on another player's screen or on the public TV -- the server filters every broadcast by role. And the profile model is deliberately thin: a screen must never ask for an email, a password, a real name, or a photo of a person, because the platform does not have user accounts at all. You do not get those constraints by asking "what goes on this screen." You get them by asking "what must NOT."
We are now specifying ONE screen from the inventory:
Walk me through, one question at a time: 1. PRIMARY action: the one thing this screen exists to let the user do. 2. SECONDARY actions: everything else reachable from here, ranked. 3. STATES: describe what the screen shows in each of -- loading, empty (no data yet), error, success / normal, and any in-progress state. 4. WHAT MUST NOT APPEAR HERE: list everything that is deliberately absent. Push me hard on this. Ask specifically about: information this role is not allowed to see; data that belongs to another user or another role; controls that would let someone do something the rules forbid; and anything we are choosing NOT to collect or NOT to show at all.
Treat item 4 as load-bearing, not an afterthought. The things a screen must NOT show are as much a part of the spec as the things it must.
When done, play the whole screen back as a structured spec: Primary action / Secondary actions / States (one line each) / Must-not-appear. Ask me to confirm before we move on.
Prompt 3: ASCII mockups are the design surface¶
Here is the move that makes this whole method work without a design tool. You have the AI render the agreed spec as an ASCII mockup -- a sketch built from + - | = characters -- and then you correct it in plain language. The mockup is text, so editing it is just talking. You never touch a canvas.
Take the confirmed spec for
Constraints on the mockup: - Plain ASCII only. Use + - | = for borders and boxes. No box-drawing or other non-ASCII characters. - It is a layout-and-content sketch, not a pixel design. Show WHERE things sit and WHAT they say, not exact sizes, fonts, or colors. - Label regions in plain words (header, primary action, list, footer). - Show the NORMAL / success state first. Then show the empty state and the error state as separate small mockups underneath.
After you render it, stop and wait. I will correct it in plain language -- for example "move the join code to the top", "the timer is too prominent", "the list should be above the buttons". Apply each edit and re-render the WHOLE mockup so I can see the result. We iterate in text until I say it is right.
I will show this one in action with a real screen further down, because the before/after is the most convincing part of the whole post.
Prompt 4: ask for variations, then choose¶
The hardest thing about not being a designer is that you cannot picture the options. You know "list of buttons" is not quite right but you cannot imagine the alternative. The fix is to make the AI imagine them for you. Ask for two or three labeled layout variants of the same spec, each with its trade-offs in prose, and pick. Choosing is far easier than conjuring.
For
Produce 2-3 DIFFERENT ASCII layout variants for the same confirmed spec. They must all satisfy the spec (same actions, same states, same must-not-appear list) -- only the arrangement differs.
For each variant: - Give it a short label (for example "A: action-first", "B: status-first"). - Render the ASCII mockup (same ASCII rules as before: + - | = borders). - Below it, write 2-3 sentences of trade-offs in plain prose: what this layout is good at, what it costs, and who it suits (which device, which role, novice vs. frequent user).
Do not recommend one yet. Let me read all of them and pick. After I pick, ask me whether I want to graft any element from a variant I did not choose.
Prompt 5: freeze the spec into a durable doc¶
Everything so far has happened in a chat, and -- per the first post in this series -- a chat is the worst possible home for a decision. The next AI session cannot read it. So the last creative stage converts the agreed mockups into a durable file shaped like this project's real per-game stage specs: each lives at the game docs, with numbered sections, an ASCII mockup per surface, a states list, and notes that record why. (Management-layer screens get a section in the shared management-shell spec instead.) This is the artifact a build ticket will later cite. It is the loop closing back to post A's rule: capture as docs, not memory.
We have agreed the mockups and behavior for
Emit a single Markdown document with this shape: - A title and a one-paragraph scope note (what this doc covers, and what it does NOT cover / what it inherits from elsewhere). - One numbered section per screen. Each section contains: - The agreed ASCII mockup (normal state), in a fenced code block. - A "States" list: loading / empty / error / success and what each shows. - A "Notes" list: the decisions we made and WHY -- especially anything non-obvious, and anything we deliberately left out. - A "Must not appear" list carried straight from the per-screen spec. - A short "Open questions / deferred" section for anything we punted on.
Write it so a future reader who was NOT in this conversation could build the screen from this document alone. Plain ASCII only, including the mockups.
Prompt 6: cross-check against the hard rules¶
A frozen spec can still quietly violate a project rule -- a stray "email" field, a private value leaking onto a public screen, a panel that appears only when some counter crosses a threshold. So before the spec is done, audit it. This prompt encodes three of this project's real, enforced rules:
- Identity is device-only. The allowed identity fields are screen name, two-letter initials, a preferred color, and a preset avatar -- and nothing else. No accounts, no chat, no demographic fields. (CLAUDE.md load-bearing rule 10, "Profiles are DEVICE-only. No personhood.")
- Role-based view filtering runs on every broadcast. Private data for one role must never reach another role's screen; every hidden-information feature needs at least one thing the non-privileged role must not see. (CLAUDE.md rule 4.)
- Layout is stable across game state. A region's position and presence depend only on role and device, never on an in-game event or a changing data value. (CLAUDE.md rule 13, "Game-board stage layouts are stable across game state.")
Adapt the rule list to your own project; the shape of the audit is the reusable part.
Audit the frozen spec for
Rules: 1. Identity is device-only. The only identity fields that may appear are screen name, short initials, a preferred color, and a preset avatar. FLAG any field that implies a person account: email, password, real name, photo of a person, age, location, pronouns, friends, chat, or DMs. 2. Each viewer sees only what their role is allowed to see. If a screen has multiple roles, confirm that private data for one role cannot appear on another role's version of the screen. For any hidden-information feature, confirm there is at least one thing the non-privileged role must NOT see. 3. Layout is stable. The position and presence of regions must depend only on role and device, never on a changing data value or an in-app event mid-session. FLAG any region that appears, disappears, or moves because of an in-game event or a data value.
Report as a table: Rule | Verdict | Evidence (quoted line) | Fix if needed.
Prompt 7: prime with a worked example -- this is the flywheel¶
The first six prompts work from a blank slate. The seventh is the one that compounds. Once you have frozen one spec, you never start cold again: you paste an existing, shipped spec into the AI as a few-shot example and tell it to produce the next screen in the same shape. The new spec inherits the section layout, the ASCII conventions, the states-and-notes structure, and the privacy rules of the old one -- for free, without you re-explaining any of it.
Here is a frozen UI spec from a screen we already shipped. Study its shape, conventions, and level of detail. Then we will specify a NEW screen the same way, inheriting these conventions instead of inventing new ones.
Confirm back to me, in a few bullets: - The section / heading shape this spec uses. - The ASCII conventions (which border characters, how regions are labelled). - How it records states, notes, and must-not-appear constraints. - The privacy / role / layout rules it appears to follow.
Then say: "Ready. Tell me the new screen and we will produce the same shape." We then re-run the inventory, per-screen, mockup, and freeze prompts -- but every output must match the conventions of the example above, so the new spec joins the same library and reads like a sibling of it.
The worked example: priming with a real screen¶
Here is the project's actual HOME / lobby screen, lifted (lightly trimmed) from the management-shell spec at the overview docs (Section 6.1). This is exactly what a frozen ASCII mockup looks like in the repo:
+--------------------------------------------------+ | DXXX [ Display ] [Avatar] | <- sticky top bar +--------------------------------------------------+ | ADD | | [Device code] [Player] [Observer] [Referee] | +--------------------------------------------------+ | THIS DEVICE | | (o) Player ( ) Observer ( ) Referee [Leave]| +--------------------------------------------------+ | GAME | | Game type [Memory Match v] (host-only edit) | | Preset: (o) Casual ( ) Competitive ( ) Tour. | | Provider: [Claude][OpenAI][Ollama][Local] | | LLM key: [........] (claude/openai only) | | Model: [Haiku 4.5 v] | | [ Start ] <- if Host or no lobby yet | +--------------------------------------------------+ | LOBBY | | Players: [Alex *][Sam X] | | Observers: [Jordan X] | | Referees: [--] | +--------------------------------------------------+ | SHARE | | Player Observer Referee | | [QR ] [QR ] [QR ] [Link] x 3 | +--------------------------------------------------+
When you paste a spec like this into Prompt 7, the AI absorbs the conventions without being told them: borders are + - |, each section is an ALL-CAPS label, controls are [bracketed], the active radio is (o), annotations hang off <- arrows. Ask it to spec a brand-new screen and it will hand you back something that looks like it belongs in the same file.
And here is the plain-language editing loop (Prompt 3) on this exact screen. The real spec settled on the order above -- but suppose you were specifying it and wanted people to be able to join before they fiddle with game settings. You would just say:
Move the SHARE section up to just under the top bar, so people can scan a QR and join before anyone touches the GAME settings.
The AI re-renders the whole thing with SHARE relocated:
+--------------------------------------------------+ | DXXX [ Display ] [Avatar] | <- sticky top bar +--------------------------------------------------+ | SHARE | <- moved up | Player Observer Referee | | [QR ] [QR ] [QR ] [Link] x 3 | +--------------------------------------------------+ | ADD | | [Device code] [Player] [Observer] [Referee] | +--------------------------------------------------+ | GAME | | Game type [Memory Match v] (host-only edit) | | ... (unchanged) | +--------------------------------------------------+
That is the entire interaction model. No tool, no canvas, no dragging. You said a sentence; the design changed. You can do this for an hour and converge on a layout you are happy with, and every step of it is plain text you could paste into a commit.
The compounding example library¶
Here is the thesis the whole playbook is built around, and the reason the seventh prompt matters more than the other six: a good prompt plus an ever-growing set of worked examples makes each new screen faster to spec than the last. The prompt is a constant. The example library is what compounds.
This project already runs on that flywheel. Every shipped game has a frozen UI spec under the game docs, with a companion the game docs that points at the rendered mockups. Memory Match and Bluff are the two current worked examples (their engines and stages live under the relevant engine module and the private source tree). When I specify the next game, I do not start from a blank prompt -- I prime the AI with the game docs and say "do the same for my game." The new game inherits Memory Match's section shape, its ASCII conventions, its states-and-notes rigor, and its privacy posture without my re-deriving any of it.
And the loop closes: the spec-freeze step (Prompt 5) is what feeds the library. Each frozen spec becomes the next game's few-shot example. Game three is easier to spec than game two, which was easier than game one. That is not a nice-to-have side effect; it is the stated point of doing it this way, and it is why this post sits in the middle of a series whose capstone, The cross-game compounding flywheel, is entirely about that compounding.
The honest edges¶
I would be selling you something if I stopped there, so here is what ASCII mockups genuinely cannot do.
They cannot express color. The HOME mockup above shows where the active-role ring goes, but "red for the defender, blue for the aggressor" is a note in prose beside the mockup, not something the + - | characters can carry.
They cannot express motion. A card flip, a match pulse, a turn-passing animation -- all of that is timing and easing, and ASCII is a still frame. This project's specs describe motion in words and timing budgets, separately from the layout sketch.
They cannot express exact spacing or responsive reflow. ASCII gives you order and grouping -- this is above that, these three sit together -- but not pixel spacing, not breakpoints, not how the layout reflows from a 10-inch tablet down to a phone. The mockup is a layout-and-content contract, not a pixel spec, and you should say so out loud in the frozen doc so nobody mistakes it for one.
There is one more honest edge that is specific to this project and worth naming, because the prompts can surface it but not fully capture it. This platform has a hard rule that a game board's layout stays stable across game state -- regions do not jump around as the game plays (CLAUDE.md rule 13). A single ASCII mockup is a snapshot; it cannot, by itself, prove the layout is stable across a state transition. The constraint cross-check prompt (Prompt 6) can flag a region whose presence depends on a data value, but verifying genuine stability is a job for the build and its tests, not for the sketch. The prompt names the rule; the pixels keep it.
None of these are reasons to reach for a design tool. They are the edges of the medium. Inside those edges -- which is most of the work of specifying a UI -- describing the screen in words and ASCII is faster, more editable, and far more AI-legible than anything you would draw. You answer the right questions in the right order, you correct the sketch in plain language, you freeze it into a file, and you hand that file to the build loop.
Which is exactly where the next post picks up: once the spec is frozen and committed, what does it feel like to watch the orchestrator turn it into shipped code? That is The orchestrated build-and-feedback loop. If you have not yet seen how the planning docs and the repo get set up in the first place, start with From an idea in a chat window to a repo the robot can build. And if you are a backend developer eyeing all this with suspicion about whether you can even drive UI work you cannot write, the closest neighbor is Shipping a React client I have not read -- the honest boundary behind doing exactly that.
You do not need to draw your UI. You need to describe it well, and let the example library do the rest.