DryUI Feedback
Single job: take one feedback submission and apply the smallest change that satisfies it. Read the screenshot, decode the structured intents, edit the source, run check, mark resolved. Stop.
The submission shape
A submission is a presentation JSON object (fetch with MCP feedback_get_submissions or curl http://127.0.0.1:4748/submissions/<id>):
id, url, viewport, scroll preferredScreenshotPath ← read this first screenshotPath: { png, webp } ← raw screenshot paths; png may be empty for legacy rows textNotes[] ← direct text instructions extracted from drawings drawingHints[] ← paired { drawing, hint } entries summary ← counts by intent kind, hint corner, and structured change kind drawings[] ← raw annotations the user drew hints[] ← raw parallel array: each drawing's nearest element + position components[] ← components the user added through the inspector removed[] ← elements the user removed moved[] ← elements the user dragged to a new position
The screenshot is the most direct signal — open preferredScreenshotPath before reading any structured data. Everything else exists to disambiguate what you're seeing. The raw arrays are preserved as escape hatches when the presentation fields are not enough.
Decoding the four intent kinds
Each submission can carry zero or more of these. Treat them as instructions, not suggestions.
drawings[] — annotations
Each entry has a kind (freehand / arrow / text / eraser) and coordinates. Treat textNotes[] as direct user instructions — they're literal sentences the user typed onto the page (e.g. "more padding here", "make this blue", "remove this"). Use drawingHints[] to pair each drawing with its nearest DOM hint; the raw drawings[] and hints[] arrays remain available if you need index-level detail.
Arrows usually point from a label to the thing being modified. Freehand sketches mark a region without naming it — combine with the screenshot to figure out what's circled.
components[] — additions
Each entry: { kind, label, props, rect }. The user picked a DryUI component (e.g. "Card", "Button") and dropped it at rect (viewport coordinates). Apply by inserting that component into the source at the closest matching DOM ancestor, with the user's label and props.
removed[] — deletions
Each entry: { tag, selector?, rect }. The user wants this element gone. Remove the corresponding source node. If the surrounding area becomes empty, leave it empty — don't backfill. The user can ask for a replacement in a follow-up submission.
moved[] — repositions
Each entry: { tag, selector?, originalRect, currentRect }. The user dragged an element from originalRect to currentRect (viewport coordinates). The screenshot shows a solid blue outline at the new position and a dashed blue ghost at the original position so you can see the displacement at a glance. Treat this as a layout intent — the user wants the element to live where the solid outline sits. Edit the data-layout block in src/layout.css (different grid area, reordered template, new track) to match. Apply directly when the move fits an existing sibling slot.
Layout discipline
DryUI does not ship a layout component. All page-level display: grid and display: flex declarations live in src/layout.css (or @container blocks within it). Nowhere else for page layout.
Structural changes (new named region, new responsive shift, different grid template) and region-internal changes (padding inside a wrapper, gap between sibling elements, borders or backgrounds the user requested) are both yours to apply. For structural edits, work in src/layout.css under the existing [data-layout='<name>'] block — extend the template, add a new data-layout-area, or add an @container page (min-width: ...) rule. Pick the smallest edit that satisfies the feedback. Use the --dry-space-* token scale (--dry-space-2 = 8px, --dry-space-3 = 12px, --dry-space-4 = 16px, --dry-space-6 = 24px, --dry-space-8 = 32px) for spacing values; match nearby usage in the file rather than picking magnitudes by feel.
Lint trip-wires that will block the dev server
The DryUI lint runs as a Vite preprocessor — violations crash the dev server with a stack trace, so the user sees a red overlay instead of their page. Avoid every one of these when editing:
dryui/no-width— nowidth:/inline-size:(ormin-/max-variants). Grid children are sized by their track insrc/layout.css. The only allowed unit family is the typographic measure (ch,em,ex).dryui/no-global— no:global(...)selectors. Use scoped CSS, data-attributes, or CSS variables.dryui/no-inline-style— nostyle="..."attributes. Use scoped classes or CSS variables.dryui/no-style-directive— nostyle:foo={bar}directives.dryui/no-raw-native-element— use DryUI components, not raw HTML, for anything DryUI covers.dryui/no-flexanddryui/no-raw-grid— flex and grid declarations only insrc/layout.css.
Component preferences (avoid lint and a11y regressions)
| Reach for | Not |
|---|---|
<Heading level> | <h1>–<h6> |
<Text> | <p> / <span> for body |
<Button> | <button> |
<Input> | <input type="text"> |
<Slider> | <input type="range"> |
<Select> | <select> |
<Checkbox> | <input type="checkbox"> |
<Separator> | <hr> |
<Field.Root> + <Label> | unlabelled form inputs |
Raw HTML is permitted only for semantic landmarks (<header>, <nav>, <aside>, <main>, <footer>, <section>, <article>) and for content the DryUI surface doesn't cover (<ol>, <ul>, <li>, <a>).
Restraint
You're applying one submission. Do only what the feedback asks for:
- Don't refactor adjacent code that wasn't called out.
- Don't reformat the whole file.
- Don't "improve" things the user didn't mention.
- Don't add tests, comments, or docs unless the feedback explicitly asks.
- Match the file's existing patterns — if it uses
Heading level={3}for section headers, your additions should too.
If the feedback is ambiguous, prefer the smaller change. The user can always send another submission.
Hand-off table
| Feedback shape | Hand off to |
|---|---|
| Pure design tokens / theme decisions | (theme agent — coming) |
| Forms wiring / a11y / focus / keyboard | (forms agent — coming) |
| Microcopy or content-only edits | apply directly |
| Padding / spacing / colour / size on existing element | apply directly |
| Structural layout change (new region, new responsive breakpoint, different template) | apply directly in src/layout.css |
Component swap (e.g., raw <button> → <Button>) | apply directly |
Adding a component the user explicitly placed via components[] | apply directly |
Removing an element via removed[] | apply directly |
Moving an element via moved[] | apply directly |
Default to "apply directly". Layout-shape edits live in src/layout.css; component edits live in the route file.
Workflow
- Get the submission. Use MCP
feedback_get_submissions(preferred) orcurl http://127.0.0.1:4748/submissions/<id>. - Claim it. Call MCP
feedback_claim_submission(orcurl -X POST http://127.0.0.1:4748/submissions/<id>/claim -H "Content-Type: application/json" -d '{"agent":"claude","name":"Claude Code","model":"claude-opus-4-7","version":"<cli-version>"}'). This flips the dashboard status to Processing so the human knows you've picked it up, stamps the start time, and records which AI / model / version is working — the completed-tasks view uses that history. Pass the most specificname,model, andversionyou know. Skip this step only when reproducing the workflow in tests. - Read the screenshot. Use the
Readtool onpreferredScreenshotPath. This is the ground truth. - Locate the page in source. The submission's
urlmaps to a route. Forhttp://localhost:5174/foothat'ssrc/routes/foo/+page.svelte. For the index,src/routes/+page.svelte. - Pair drawings with hints. Prefer
drawingHints[]; each pair has{ drawing, hint }. If you need the raw arrays, for eachdrawings[i], look athints[i].elementto find the DOM target, andhints[i].corner+hints[i].percentX/Yfor sub-element placement. - Apply intents. In order:
drawings(textNotes[]are instructions),components(additions),removed(deletions),moved(repositions — usually asrc/layout.cssedit). Make the smallest source edit that satisfies each. - Run checks. Run the project’s focused check/build/test command for the changed file or package. Fix any violations the edit introduced — re-read this skill's lint section if confused.
- Resolve. Call MCP
feedback_resolve_submissionwith the submission id, orcurl -X PATCH http://127.0.0.1:4748/submissions/<id> -H "Content-Type: application/json" -d '{"status":"resolved"}'. The server stampsresolvedAtand computesdurationMsfrom your claim so the dashboard can show how long the task took. Required to clear the submission from the queue. - If you bail out, release. If you claimed a submission but decided not to apply it (out of scope, blocked, etc.), call MCP
feedback_release_submissionso the dashboard moves it back to Pending instead of leaving it stuck under your name.
Tone
Quiet. State the submission id and what intents it carries. Claim, make the edit, run the relevant project check, resolve. The user already wrote the feedback — don't re-explain it back to them.


