mirror of
https://github.com/alkimake/paperclip.git
synced 2026-06-14 01:50:39 +09:00
Bundle the wireframe skill into the skills catalog
Adds the wireframe skill (low-fi black-and-white SVG wireframes + viewer page) as a bundled catalog skill under catalog/bundled/product/wireframe, alongside its references/ docs and assets/ templates. Regenerates generated/catalog.json (8 -> 9 skills). The skill ships static svg/html template assets, so its derived trust level is "assets" rather than "markdown_only". The server's real install-time security gate (assertCatalogSkillInstallable) blocks only "scripts_executables", and "assets" skills are installable, so the shipped-catalog markdown-only invariant is refined to gate on executable scripts instead. No skill ships executable scripts. Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
parent
911a1e8b0d
commit
1cf3b792b5
9 changed files with 1624 additions and 3 deletions
|
|
@ -0,0 +1,193 @@
|
|||
---
|
||||
name: wireframe
|
||||
description: Produce low-fidelity black-and-white UI wireframes as standalone SVG files, optionally bundled into a single-page HTML viewer and published via the here-now skill. Use when the user asks to "wireframe X", "sketch a screen for", "draft a layout", "low-fi mockup", "rough mock", "make a page to view the wireframes", "build a viewer for these screens", or to "deploy / publish / host the wireframes". Do NOT use when the user wants production UI code, branded designs, hi-fi mockups, or animated/interactive prototypes — use frontend-design or similar instead.
|
||||
key: paperclipai/bundled/product/wireframe
|
||||
recommendedForRoles:
|
||||
- designer
|
||||
- product
|
||||
- engineer
|
||||
tags:
|
||||
- design
|
||||
- wireframe
|
||||
- ux
|
||||
- prototyping
|
||||
- svg
|
||||
---
|
||||
|
||||
# Wireframe
|
||||
|
||||
Produce low-fidelity, black-and-white UI wireframes as **standalone SVG files**. The goal is to communicate **structure** — what goes where, in what order, at roughly what size — without committing to colour, brand, or polish.
|
||||
|
||||
## When to use
|
||||
|
||||
Trigger on phrases like:
|
||||
|
||||
- "wireframe a [screen / page / flow] for X"
|
||||
- "low-fi / lo-fi mockup of X"
|
||||
- "draft a layout for X"
|
||||
- "rough sketch of the [dashboard / settings / login / ...] page"
|
||||
- "show me how X would lay out before I build it"
|
||||
|
||||
Skip and defer to `frontend-design` (or similar) when the request mentions: brand, polish, real components, "production-ready", colour palettes, hi-fi, Figma export, or actual code/HTML/React deliverables.
|
||||
|
||||
## House style — non-negotiable
|
||||
|
||||
Wireframes are diagnostic, not decorative. Lock these tokens on every output:
|
||||
|
||||
| Token | Value | Notes |
|
||||
| ---------------- | ------------------------------------------------- | -------------------------------------- |
|
||||
| Stroke | `#000` width `1.5` | All borders, dividers, outlines |
|
||||
| Fill (boxes) | `#fff` | Default for cards/containers |
|
||||
| Placeholder fill | `#e6e6e6` | Image/avatar/empty-state regions |
|
||||
| Text colour | `#000` for labels, `#666` for placeholder text | No other colours |
|
||||
| Accent | `#d33` (dashed) — annotation layer ONLY | Never inside real UI elements |
|
||||
| Font | `font-family="-apple-system, system-ui, sans-serif"` | Single typeface across the whole file |
|
||||
| Type scale | `12` caption · `14` body · `20` heading · `28` title | No other sizes |
|
||||
| Grid | 8px snap, 24px gutter | All x/y/w/h must be multiples of 8 |
|
||||
| Default canvas | `1280×800` desktop, `375×812` mobile, `768×1024` tablet | Pick one and state it in the comment |
|
||||
|
||||
If you need to highlight a specific region for a callout, use the **annotation layer** (red dashed). Never colourise the wireframe itself.
|
||||
|
||||
## Workflow
|
||||
|
||||
1. **Confirm scope.** What screen(s)? Which viewport (desktop / tablet / mobile)? Single screen or multi-screen flow? If unclear, ask one question, then proceed with the most likely default.
|
||||
2. **Pick a canvas** from the table above. State the viewport in your reply.
|
||||
3. **Compose from primitives.** Read `references/components.md` and assemble the screen from the primitive snippets. Snap every coordinate to 8px.
|
||||
4. **Write the SVG to a file.** Default path: `wireframes/<slug>.svg` in the working directory. Filename slug describes the screen (`login.svg`, `dashboard.svg`, `settings-account.svg`).
|
||||
5. **Emit a textual annotation list** in your reply, mapping each numbered region in the SVG to a one-line description ("1 — primary nav, 2 — search input, 3 — list of recent items"). This makes the wireframe accessible, queryable, and reviewable in text-only channels.
|
||||
6. **For multi-screen flows**, produce one SVG per screen and a summary `flow.svg` that arranges thumbnails left-to-right with arrows between them.
|
||||
|
||||
## Quick start — minimal SVG
|
||||
|
||||
```svg
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1280" height="800" viewBox="0 0 1280 800"
|
||||
font-family="-apple-system, system-ui, sans-serif" fill="#fff" stroke="#000" stroke-width="1.5">
|
||||
<!-- canvas border -->
|
||||
<rect x="0" y="0" width="1280" height="800" />
|
||||
|
||||
<!-- example: a button -->
|
||||
<g transform="translate(48, 48)">
|
||||
<rect width="120" height="40" rx="4" />
|
||||
<text x="60" y="25" font-size="14" text-anchor="middle" stroke="none" fill="#000">Continue</text>
|
||||
</g>
|
||||
</svg>
|
||||
```
|
||||
|
||||
Two house-style gotchas worth memorising:
|
||||
|
||||
- Always set `stroke="none"` on `<text>` elements (text inherits the parent stroke and gets a halo otherwise).
|
||||
- Always wrap text fill explicitly (`fill="#000"`) since the parent group fill is `#fff` for boxes.
|
||||
|
||||
## Primitive library
|
||||
|
||||
The full set of reusable primitives lives in `references/components.md`. Load it whenever you need a primitive whose exact markup you do not have in working memory. Do not re-derive primitives from scratch — copy the snippet and adjust coordinates.
|
||||
|
||||
Primitives provided:
|
||||
|
||||
- **Inputs:** button (filled, outlined, icon), text input, textarea, dropdown, checkbox, radio, toggle, search input
|
||||
- **Layout:** card, section divider, sidebar, two-column, three-column
|
||||
- **Navigation:** navbar, tab bar, breadcrumb, pagination, sidebar nav
|
||||
- **Content:** heading, paragraph block, list row, table, key-value pair, metric tile
|
||||
- **Media:** image placeholder, avatar (circle/square), video placeholder
|
||||
- **Overlay:** modal, drawer, toast, tooltip, dropdown menu (open state)
|
||||
- **Annotation:** numbered callout, dashed region highlight, arrow connector
|
||||
|
||||
## Grid, palette, and type scale
|
||||
|
||||
For exact pixel values, palette tokens, and type sizes, see `references/grid-system.md`.
|
||||
|
||||
## Worked examples
|
||||
|
||||
`references/examples.md` contains four complete wireframes you can copy and adapt:
|
||||
|
||||
1. Login screen (mobile, 375×812)
|
||||
2. Admin dashboard (desktop, 1280×800)
|
||||
3. Settings page with form (desktop, 1280×800)
|
||||
4. Modal confirmation overlay (desktop, 1280×800)
|
||||
|
||||
When the user's request is close to one of these, start from the example and modify, rather than building from blank.
|
||||
|
||||
## Output convention
|
||||
|
||||
Every wireframe response should include:
|
||||
|
||||
1. The SVG file written to disk (path stated explicitly).
|
||||
2. The SVG inlined in your reply (so it renders in markdown previews).
|
||||
3. A short numbered annotation list mapping regions to intent.
|
||||
4. Any explicit assumption you made (viewport, signed-in state, empty/populated, dark/light not applicable since monochrome).
|
||||
|
||||
## If the user requests a website / viewer page
|
||||
|
||||
Trigger on phrases like "make a page that shows the screens", "single page I can scroll", "build a viewer", "let me click through the wireframes", "show them all on one page", or any request to bundle multiple wireframes into a browsable artifact (not a production site).
|
||||
|
||||
Build **one static `index.html`** that loads the SVG wireframes directly. Do not turn this into a React app or component library — it's a review surface, not product UI.
|
||||
|
||||
**File layout** (default):
|
||||
|
||||
```
|
||||
design/<task-slug>/
|
||||
index.html
|
||||
wireframes/ # the SVGs from this skill
|
||||
screenshots/ # any reference screenshots
|
||||
```
|
||||
|
||||
**Page anatomy** (start from `assets/site-template.html` and adjust — do not re-derive the CSS):
|
||||
|
||||
- **Sticky sidebar TOC** (240px on desktop) listing every screen with anchor links. Group by Flow / Screens / Open questions.
|
||||
- **Hero header** at the top: crumb (issue id), title, one-paragraph summary, pill row of meta tags (`12 screens`, `Lo-fi · monochrome`, `Click any wireframe to zoom`).
|
||||
- **One section per screen** with a 2-column grid: wireframe on the left, reference image + numbered annotations + a "Why this changes" callout on the right. The wireframe `<img>` points to the SVG file directly — do not inline it.
|
||||
- **Click-to-zoom lightbox** for any element marked `[data-zoom]`. Esc and backdrop click both close.
|
||||
- **Flow diagram** section near the top that loads `wireframes/flow.svg` full-width.
|
||||
- **Open questions** section at the bottom for unresolved decisions.
|
||||
|
||||
**House style for the viewer** (matches the wireframes themselves):
|
||||
|
||||
- Palette: `--bg: #fafaf8`, `--panel: #fff`, `--ink: #111`, `--muted: #666`, `--line: #e5e5e0`, `--accent: #d33` (red dashed callouts only).
|
||||
- System font stack only: `-apple-system, system-ui, "Segoe UI", sans-serif`. No web fonts.
|
||||
- 8px-based spacing, `border-radius: 8px` on cards, 1px `--line` borders, no shadows except the hover lift on `.wire`.
|
||||
- The viewer chrome is allowed to be slightly more polished than the wireframes (subtle hover, rounded cards) — but never colourful. The wireframes themselves stay strictly monochrome.
|
||||
|
||||
**Responsive** (verify before reporting done):
|
||||
|
||||
- ≥980px: two-column grid, sidebar TOC visible.
|
||||
- 900–980px: grid stacks to one column, TOC still sidebar.
|
||||
- <900px (tablet/phone): TOC collapses to a sticky `<details>` disclosure at the top of the page, defaults closed; tapping a link auto-closes it. Sections get `scroll-margin-top: 80px` so anchor jumps clear the sticky bar.
|
||||
- <560px (phone): tighter type/spacing scale, hero shrinks, lightbox switches from flex-centered to block layout at full viewport width with `touch-action: pinch-zoom` so users can pinch in further.
|
||||
|
||||
**Verification** before handing off: open the file in a browser and walk it at 1440×900, 768×1024, and 390×844. Confirm anchor jumps land cleanly, lightbox opens/closes, and SVGs render at the right aspect.
|
||||
|
||||
## If the user asks to deploy / publish / host the wireframes
|
||||
|
||||
Defer to the **`here-now` skill** — it owns publishing, anonymous vs. permanent sites, claim tokens, and credentials. Do not roll your own hosting.
|
||||
|
||||
Load the `here-now` skill and follow its `publish.sh` recipe. The shape is:
|
||||
|
||||
```bash
|
||||
cd design/<task-slug>
|
||||
{path-to-here-now}/scripts/publish.sh .
|
||||
# → https://{adjective-noun-suffix}.here.now/
|
||||
```
|
||||
|
||||
If `here-now` isn't installed for the current agent, install it (`npx skills add heredotnow/skill --skill here-now -g`) or escalate to whoever owns the agent's skill set. Do not roll your own hosting.
|
||||
|
||||
Things to remember when invoking it:
|
||||
|
||||
- Publish the **directory containing `index.html` at its root**, not the parent. `index.html` must be at the root of the published tree.
|
||||
- Without saved credentials the site is **anonymous and expires in 24 hours**. With a saved API key, it's permanent. If the user wants a permanent URL, follow the `here-now` skill's sign-in-code flow — don't fake your way around it.
|
||||
- For an update, pass `--slug {existing-slug}` so the URL stays stable across review rounds (the script auto-loads the claim token from `.herenow/state.json`).
|
||||
- Read `publish_result.*` lines from script stderr to determine `auth_mode` and the claim URL — do not read `.herenow/state.json` and present its contents as the source of truth.
|
||||
- Always share the `siteUrl` from the current run; if anonymous, also share the claim URL and the 24h expiry warning.
|
||||
|
||||
## What this skill is NOT for
|
||||
|
||||
- **Production UI code** — use `frontend-design` or write React/HTML directly.
|
||||
- **Hi-fi or branded mockups** — use Figma or a design tool, not this.
|
||||
- **Interactive prototypes** — SVG is static; export multi-screen flows as a flow.svg.
|
||||
- **Diagrams of system architecture, sequence flows, or data models** — use mermaid or plantuml.
|
||||
- **Illustrations or art** — use `example-skills:canvas-design` or `algorithmic-art`.
|
||||
|
||||
## Bundled assets
|
||||
|
||||
- `assets/template.svg` — blank desktop canvas with hidden 8px-grid guides; copy as a starting point.
|
||||
- `assets/template-mobile.svg` — same for 375×812 mobile.
|
||||
- `assets/site-template.html` — minimal review-viewer page (sticky TOC + responsive collapse + lightbox). Copy to `design/<task-slug>/index.html` and fill in the sections when the user requests a website.
|
||||
|
|
@ -0,0 +1,356 @@
|
|||
<!doctype html>
|
||||
<!--
|
||||
Wireframe review viewer — copy to design/<task-slug>/index.html and fill in.
|
||||
House style: monochrome, system font, 8px grid, sticky TOC, lightbox.
|
||||
Replace TODO markers; duplicate <section class="screen"> per wireframe.
|
||||
-->
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>TODO — review viewer title</title>
|
||||
<style>
|
||||
:root {
|
||||
--bg: #fafaf8;
|
||||
--panel: #ffffff;
|
||||
--ink: #111111;
|
||||
--muted: #666666;
|
||||
--line: #e5e5e0;
|
||||
--accent: #d33;
|
||||
--maxw: 1200px;
|
||||
}
|
||||
* { box-sizing: border-box; }
|
||||
html, body { margin: 0; padding: 0; }
|
||||
body {
|
||||
font: 15px/1.55 -apple-system, system-ui, "Segoe UI", sans-serif;
|
||||
background: var(--bg);
|
||||
color: var(--ink);
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
a { color: inherit; }
|
||||
|
||||
.shell { display: grid; grid-template-columns: 240px 1fr; min-height: 100vh; }
|
||||
|
||||
.toc {
|
||||
position: sticky; top: 0; align-self: start;
|
||||
height: 100vh; overflow: auto;
|
||||
padding: 24px 16px 24px 24px;
|
||||
border-right: 1px solid var(--line);
|
||||
background: var(--panel);
|
||||
font-size: 13px;
|
||||
z-index: 20;
|
||||
}
|
||||
.toc h1 { font-size: 14px; margin: 0 0 4px; letter-spacing: 0.04em; text-transform: uppercase; color: var(--muted); }
|
||||
.toc h2 { font-size: 12px; margin: 24px 0 6px; letter-spacing: 0.04em; text-transform: uppercase; color: var(--muted); font-weight: 700; }
|
||||
.toc a { display: block; text-decoration: none; padding: 6px 8px; margin-left: -8px; border-radius: 4px; color: var(--ink); }
|
||||
.toc a:hover { background: var(--bg); }
|
||||
.toc .num { display: inline-block; width: 22px; color: var(--muted); }
|
||||
.toc .toc-summary { display: none; }
|
||||
.toc .toc-body { display: block; }
|
||||
|
||||
main { padding: 48px 56px 96px; max-width: 1300px; min-width: 0; }
|
||||
|
||||
header.hero { max-width: var(--maxw); margin-bottom: 64px; }
|
||||
header.hero .crumb { color: var(--muted); font-size: 12px; letter-spacing: 0.04em; text-transform: uppercase; }
|
||||
header.hero h1 { font-size: 36px; line-height: 1.15; margin: 8px 0 16px; }
|
||||
header.hero p { color: var(--muted); max-width: 720px; font-size: 16px; }
|
||||
header.hero .pills { margin-top: 20px; display: flex; gap: 8px; flex-wrap: wrap; }
|
||||
header.hero .pill { font-size: 12px; padding: 4px 10px; border: 1px solid var(--line); border-radius: 999px; background: var(--panel); color: var(--muted); }
|
||||
|
||||
section { scroll-margin-top: 24px; max-width: var(--maxw); margin-bottom: 96px; }
|
||||
section .lede { color: var(--muted); margin: 0 0 12px; font-size: 12px; letter-spacing: 0.04em; text-transform: uppercase; font-weight: 700; }
|
||||
section h2 { font-size: 28px; line-height: 1.2; margin: 0 0 8px; }
|
||||
section h2 .step-num { display: inline-block; width: 40px; color: var(--muted); }
|
||||
section h3 { font-size: 16px; margin: 24px 0 8px; }
|
||||
section .desc { color: var(--muted); margin: 0 0 24px; max-width: 760px; }
|
||||
|
||||
/* canvas: desktop wire (wide) on left, mobile wire (tall narrow) middle, notes right.
|
||||
Mobile is a first-class deliverable — never tabbed, never stacked away. */
|
||||
.grid {
|
||||
display: grid; gap: 24px;
|
||||
grid-template-columns: minmax(0, 1.4fr) 220px minmax(0, 0.9fr);
|
||||
align-items: start;
|
||||
}
|
||||
@media (max-width: 1180px) {
|
||||
.grid { grid-template-columns: minmax(0, 1fr) 200px; }
|
||||
.grid > .notes-col { grid-column: 1 / -1; }
|
||||
}
|
||||
@media (max-width: 980px) { .grid { grid-template-columns: 1fr; } .grid > .mobile-col { max-width: 280px; } }
|
||||
|
||||
/* tablet */
|
||||
@media (max-width: 900px) {
|
||||
.shell { grid-template-columns: 1fr; }
|
||||
.toc {
|
||||
position: sticky; top: 0;
|
||||
height: auto; max-height: 60vh;
|
||||
padding: 12px 16px;
|
||||
border-right: none;
|
||||
border-bottom: 1px solid var(--line);
|
||||
}
|
||||
.toc .toc-summary {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
list-style: none;
|
||||
padding: 4px 0;
|
||||
}
|
||||
.toc .toc-summary::-webkit-details-marker { display: none; }
|
||||
.toc .toc-summary .title { font-size: 13px; font-weight: 600; }
|
||||
.toc .toc-summary .crumb { font-size: 11px; color: var(--muted); letter-spacing: 0.04em; text-transform: uppercase; }
|
||||
.toc .toc-summary .chevron {
|
||||
width: 10px; height: 10px;
|
||||
border-right: 1.5px solid var(--ink);
|
||||
border-bottom: 1.5px solid var(--ink);
|
||||
transform: rotate(45deg);
|
||||
transition: transform 120ms ease;
|
||||
margin-right: 4px;
|
||||
}
|
||||
.toc[open] .toc-summary .chevron { transform: rotate(225deg) translate(-2px, -2px); }
|
||||
.toc .toc-body { display: none; padding-top: 8px; border-top: 1px solid var(--line); margin-top: 8px; }
|
||||
.toc[open] .toc-body { display: block; }
|
||||
main { padding: 24px 20px 64px; }
|
||||
header.hero { margin-bottom: 40px; }
|
||||
header.hero h1 { font-size: 28px; }
|
||||
header.hero p { font-size: 15px; }
|
||||
section { margin-bottom: 64px; scroll-margin-top: 80px; }
|
||||
section h2 { font-size: 24px; }
|
||||
section h2 .step-num { width: 32px; }
|
||||
.flow-section { padding: 20px; margin-bottom: 40px; scroll-margin-top: 80px; }
|
||||
}
|
||||
|
||||
/* phone */
|
||||
@media (max-width: 560px) {
|
||||
main { padding: 16px 14px 56px; }
|
||||
header.hero { margin-bottom: 32px; }
|
||||
header.hero h1 { font-size: 24px; line-height: 1.2; }
|
||||
header.hero p { font-size: 14px; }
|
||||
section { margin-bottom: 48px; }
|
||||
section h2 { font-size: 20px; line-height: 1.25; }
|
||||
section h2 .step-num { width: 28px; }
|
||||
section h3 { font-size: 15px; margin: 20px 0 6px; }
|
||||
section .desc { font-size: 14px; }
|
||||
.grid { gap: 16px; }
|
||||
.flow-section { padding: 16px; border-radius: 8px; }
|
||||
.wire .label { padding: 8px 10px; font-size: 11px; gap: 8px; }
|
||||
body { font-size: 14px; }
|
||||
}
|
||||
|
||||
.wire {
|
||||
background: var(--panel);
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
cursor: zoom-in;
|
||||
transition: transform 80ms ease, box-shadow 80ms ease;
|
||||
}
|
||||
.wire:hover { box-shadow: 0 8px 24px rgba(0,0,0,0.08); }
|
||||
.wire .label {
|
||||
padding: 10px 14px;
|
||||
font-size: 12px;
|
||||
color: var(--muted);
|
||||
border-bottom: 1px solid var(--line);
|
||||
display: flex; justify-content: space-between; align-items: center;
|
||||
}
|
||||
.wire img { display: block; width: 100%; height: auto; background: #fff; }
|
||||
/* mobile wire renders narrow inside its 220px column so it reads as a phone */
|
||||
.wire.mobile-wire img { max-width: 220px; margin: 0 auto; }
|
||||
|
||||
.ref {
|
||||
background: var(--panel);
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 8px;
|
||||
padding: 14px;
|
||||
}
|
||||
.ref .label { font-size: 12px; color: var(--muted); margin-bottom: 8px; display: flex; justify-content: space-between; align-items: baseline; }
|
||||
.ref img { display: block; width: 100%; height: auto; border: 1px solid var(--line); border-radius: 4px; cursor: zoom-in; }
|
||||
|
||||
.notes ul { padding-left: 20px; margin: 8px 0; }
|
||||
.notes li { margin-bottom: 6px; }
|
||||
.notes .why {
|
||||
background: var(--panel);
|
||||
border: 1px solid var(--line);
|
||||
border-left: 3px solid var(--ink);
|
||||
padding: 14px 18px;
|
||||
border-radius: 4px;
|
||||
margin-top: 12px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.flow-section { background: var(--panel); border: 1px solid var(--line); border-radius: 12px; padding: 32px; margin-bottom: 64px; max-width: var(--maxw); }
|
||||
.flow-section h2 { margin-top: 0; }
|
||||
.flow-section .wire { margin-top: 16px; }
|
||||
|
||||
/* lightbox */
|
||||
.lightbox {
|
||||
position: fixed; inset: 0;
|
||||
background: rgba(0,0,0,0.86);
|
||||
display: none;
|
||||
align-items: center; justify-content: center;
|
||||
z-index: 100;
|
||||
cursor: zoom-out;
|
||||
padding: 32px;
|
||||
}
|
||||
.lightbox.open { display: flex; }
|
||||
.lightbox img { max-width: 100%; max-height: 100%; background: #fff; box-shadow: 0 30px 80px rgba(0,0,0,0.4); border-radius: 4px; }
|
||||
.lightbox .close { position: absolute; top: 16px; right: 24px; font-size: 28px; color: #fff; cursor: pointer; }
|
||||
.lightbox .caption { position: absolute; bottom: 16px; left: 50%; transform: translateX(-50%); color: #fff; font-size: 13px; opacity: 0.9; }
|
||||
|
||||
/* mobile lightbox — must come after the desktop rules to win the cascade */
|
||||
@media (max-width: 560px) {
|
||||
.lightbox { padding: 12px; overflow: auto; touch-action: pinch-zoom; }
|
||||
.lightbox.open { display: block; }
|
||||
.lightbox img { display: block; width: 100%; max-width: none; max-height: none; height: auto; margin: 40px auto 56px; box-shadow: none; }
|
||||
.lightbox .close { position: fixed; top: 8px; right: 12px; font-size: 32px; }
|
||||
.lightbox .caption { position: fixed; left: 50%; transform: translateX(-50%); font-size: 12px; bottom: 12px; text-align: center; background: rgba(0,0,0,0.7); border-radius: 4px; padding: 6px 12px; }
|
||||
}
|
||||
|
||||
.footer { color: var(--muted); font-size: 12px; max-width: var(--maxw); border-top: 1px solid var(--line); padding-top: 24px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="shell">
|
||||
<details class="toc">
|
||||
<summary class="toc-summary">
|
||||
<span>
|
||||
<span class="crumb">TODO-ID · TODO short title</span><br>
|
||||
<span class="title">Jump to a screen</span>
|
||||
</span>
|
||||
<span class="chevron" aria-hidden="true"></span>
|
||||
</summary>
|
||||
<nav class="toc-body" aria-label="Section navigation">
|
||||
<h1>TODO-ID</h1>
|
||||
<div style="font-size: 13px; color: var(--muted); margin-bottom: 16px;">TODO short title</div>
|
||||
|
||||
<h2>Flow</h2>
|
||||
<a href="#flow"><span class="num">⤳</span>Flow diagram</a>
|
||||
|
||||
<h2>Screens</h2>
|
||||
<a href="#s1"><span class="num">1</span>TODO screen 1 name</a>
|
||||
<!-- duplicate per screen -->
|
||||
|
||||
<h2>Open questions</h2>
|
||||
<a href="#open"><span class="num">?</span>For reviewer</a>
|
||||
</nav>
|
||||
</details>
|
||||
|
||||
<main>
|
||||
<header class="hero">
|
||||
<div class="crumb">TODO-ID · Wireframes</div>
|
||||
<h1>TODO — long title</h1>
|
||||
<p>TODO — one-paragraph summary of what this set covers and why.</p>
|
||||
<div class="pills">
|
||||
<span class="pill">TODO N screens</span>
|
||||
<span class="pill">Desktop + mobile</span>
|
||||
<span class="pill">Lo-fi · monochrome</span>
|
||||
<span class="pill">Click any wireframe to zoom</span>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- ========== Flow diagram (optional) ========== -->
|
||||
<section id="flow" class="flow-section">
|
||||
<div class="lede">Flow</div>
|
||||
<h2>TODO — flow title</h2>
|
||||
<p class="desc">TODO — describe arrow conventions (solid = happy path, dashed = branch).</p>
|
||||
<div class="wire" data-zoom data-caption="Flow diagram">
|
||||
<div class="label"><span>flow.svg</span><span>TODO WxH</span></div>
|
||||
<img src="wireframes/flow.svg" alt="Flow diagram" />
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ========== Screen template — duplicate per screen ==========
|
||||
Three cells per row: desktop wire | mobile wire | notes-col.
|
||||
Mobile is mandatory; only drop it when the user explicitly opted out. -->
|
||||
<section id="s1">
|
||||
<div class="lede">Step 1 · TODO group</div>
|
||||
<h2><span class="step-num">1.</span>TODO screen name</h2>
|
||||
<p class="desc">TODO — one-paragraph describing the screen's purpose.</p>
|
||||
<div class="grid">
|
||||
<div class="wire" data-zoom data-caption="01 · TODO screen name (desktop)">
|
||||
<div class="label"><span>01-screen.svg</span><span>1280×800 · desktop</span></div>
|
||||
<img src="wireframes/01-screen.svg" alt="TODO screen name (desktop) wireframe" />
|
||||
</div>
|
||||
<div class="wire mobile-wire mobile-col" data-zoom data-caption="01 · TODO screen name (mobile)">
|
||||
<div class="label"><span>mobile</span><span>375×812</span></div>
|
||||
<img src="wireframes/01-screen-mobile.svg" alt="TODO screen name (mobile) wireframe" />
|
||||
</div>
|
||||
<div class="notes-col">
|
||||
<!-- Optional reference (real-world example we're modelling) -->
|
||||
<div class="ref">
|
||||
<div class="label"><span>Reference</span><span>TODO source</span></div>
|
||||
<img src="screenshots/TODO-ref.png" alt="TODO reference" data-zoom />
|
||||
</div>
|
||||
<div class="notes">
|
||||
<h3>Annotations</h3>
|
||||
<ul>
|
||||
<li><b>1</b> — TODO callout 1.</li>
|
||||
<li><b>2</b> — TODO callout 2 (note where mobile diverges).</li>
|
||||
</ul>
|
||||
<div class="why"><b>Why this changes:</b> TODO rationale vs current state.</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ========== Open questions ========== -->
|
||||
<section id="open">
|
||||
<div class="lede">For reviewer</div>
|
||||
<h2>Open questions</h2>
|
||||
<div class="notes">
|
||||
<ul>
|
||||
<li><b>TODO</b> — Open question 1.</li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="footer">
|
||||
Wireframes follow the in-tree <code>wireframe</code> skill house style — black on white, 1.5pt strokes, 8px grid, type sizes 12/14/20/28. Red dashed annotations are the call-out layer only and not part of the proposed UI.
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<div class="lightbox" id="lb" aria-hidden="true">
|
||||
<span class="close" id="lbClose">×</span>
|
||||
<img id="lbImg" alt="" />
|
||||
<div class="caption" id="lbCap"></div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Click-to-zoom: any element marked [data-zoom] OR any <img> inside [data-zoom]
|
||||
const lb = document.getElementById('lb');
|
||||
const lbImg = document.getElementById('lbImg');
|
||||
const lbCap = document.getElementById('lbCap');
|
||||
const lbClose = document.getElementById('lbClose');
|
||||
|
||||
document.querySelectorAll('[data-zoom]').forEach((el) => {
|
||||
el.addEventListener('click', () => {
|
||||
const target = el.tagName === 'IMG' ? el : el.querySelector('img');
|
||||
if (!target) return;
|
||||
lbImg.src = target.src;
|
||||
lbImg.alt = target.alt;
|
||||
lbCap.textContent = el.dataset.caption || target.alt || '';
|
||||
lb.classList.add('open');
|
||||
});
|
||||
});
|
||||
|
||||
function close() { lb.classList.remove('open'); }
|
||||
lb.addEventListener('click', close);
|
||||
lbClose.addEventListener('click', close);
|
||||
document.addEventListener('keydown', (e) => { if (e.key === 'Escape') close(); });
|
||||
|
||||
// TOC: open by default on desktop (<details> would otherwise hide body),
|
||||
// closed by default on mobile so the long list doesn't dominate the viewport.
|
||||
const toc = document.querySelector('details.toc');
|
||||
if (toc) {
|
||||
const mq = window.matchMedia('(max-width: 900px)');
|
||||
const apply = () => { toc.open = !mq.matches; };
|
||||
apply();
|
||||
mq.addEventListener('change', apply);
|
||||
// On mobile, collapse the TOC after the user taps a link.
|
||||
toc.querySelectorAll('.toc-body a').forEach((a) => {
|
||||
a.addEventListener('click', () => { if (mq.matches) toc.open = false; });
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="375" height="812" viewBox="0 0 375 812"
|
||||
font-family="-apple-system, system-ui, sans-serif" fill="#fff" stroke="#000" stroke-width="1.5">
|
||||
<!-- canvas -->
|
||||
<rect x="0" y="0" width="375" height="812" />
|
||||
|
||||
<!-- 8px grid guides -->
|
||||
<g data-region="grid" stroke="#e6e6e6" stroke-width="0.5" fill="none">
|
||||
<path d="M 16 0 V 812" stroke="#d33" stroke-dasharray="2 4" />
|
||||
<path d="M 359 0 V 812" stroke="#d33" stroke-dasharray="2 4" />
|
||||
<!-- safe-area top (status bar 44px) and bottom (home indicator 34px) -->
|
||||
<line x1="0" y1="44" x2="375" y2="44" />
|
||||
<line x1="0" y1="778" x2="375" y2="778" />
|
||||
</g>
|
||||
|
||||
<!-- replace this comment with your mobile wireframe primitives.
|
||||
Mobile defaults:
|
||||
- 16px outer margin (content width 343)
|
||||
- 48px tap target minimum
|
||||
- status-bar reserve 0-44, home-indicator reserve 778-812
|
||||
-->
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 925 B |
|
|
@ -0,0 +1,24 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="1280" height="800" viewBox="0 0 1280 800"
|
||||
font-family="-apple-system, system-ui, sans-serif" fill="#fff" stroke="#000" stroke-width="1.5">
|
||||
<!-- canvas -->
|
||||
<rect x="0" y="0" width="1280" height="800" />
|
||||
|
||||
<!-- 8px grid guides (toggle visibility by removing or commenting this group) -->
|
||||
<g data-region="grid" stroke="#e6e6e6" stroke-width="0.5" fill="none">
|
||||
<!-- vertical lines every 8px -->
|
||||
<path d="M 8 0 V 800 M 16 0 V 800 M 24 0 V 800 M 32 0 V 800 M 40 0 V 800 M 48 0 V 800 M 56 0 V 800 M 64 0 V 800 M 72 0 V 800 M 80 0 V 800 M 88 0 V 800 M 96 0 V 800" />
|
||||
<!-- column ruler at 24px outer margin + 12 columns of 88 with 8 gutter -->
|
||||
<path d="M 48 0 V 800" stroke="#d33" stroke-dasharray="2 4" />
|
||||
<path d="M 1232 0 V 800" stroke="#d33" stroke-dasharray="2 4" />
|
||||
</g>
|
||||
|
||||
<!-- replace this comment block with your wireframe primitives.
|
||||
Each primitive should be wrapped in its own <g transform="translate(x, y)"> with a numbered comment:
|
||||
<!- - 1: navbar - ->
|
||||
<g transform="translate(0, 0)" data-region="navbar"> ... </g>
|
||||
Then in your reply, list:
|
||||
1 - navbar with global search
|
||||
2 - sidebar with primary nav
|
||||
...
|
||||
-->
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
|
|
@ -0,0 +1,482 @@
|
|||
# Component primitives
|
||||
|
||||
Copy these snippets directly into your SVG. Each primitive is wrapped in a `<g transform="translate(0, 0)">` so you can position it by changing the translate values. All sizes follow the 8px grid and the type scale defined in `grid-system.md`.
|
||||
|
||||
## Conventions used below
|
||||
|
||||
- `W`, `H` placeholders mean "pick a multiple of 8 that fits your layout".
|
||||
- `Label`, `Placeholder`, etc. mean "replace with the actual copy".
|
||||
- The root `<svg>` is assumed to set: `font-family="-apple-system, system-ui, sans-serif" fill="#fff" stroke="#000" stroke-width="1.5"`.
|
||||
- Always set `stroke="none"` on `<text>` elements.
|
||||
|
||||
---
|
||||
|
||||
## Inputs
|
||||
|
||||
### Button (filled)
|
||||
|
||||
```svg
|
||||
<g transform="translate(0,0)">
|
||||
<rect width="120" height="40" rx="4" fill="#000" />
|
||||
<text x="60" y="25" font-size="14" text-anchor="middle" stroke="none" fill="#fff">Continue</text>
|
||||
</g>
|
||||
```
|
||||
|
||||
### Button (outlined / secondary)
|
||||
|
||||
```svg
|
||||
<g transform="translate(0,0)">
|
||||
<rect width="120" height="40" rx="4" />
|
||||
<text x="60" y="25" font-size="14" text-anchor="middle" stroke="none" fill="#000">Cancel</text>
|
||||
</g>
|
||||
```
|
||||
|
||||
### Button (icon-only square)
|
||||
|
||||
```svg
|
||||
<g transform="translate(0,0)">
|
||||
<rect width="40" height="40" rx="4" />
|
||||
<!-- glyph: a plus -->
|
||||
<line x1="14" y1="20" x2="26" y2="20" />
|
||||
<line x1="20" y1="14" x2="20" y2="26" />
|
||||
</g>
|
||||
```
|
||||
|
||||
### Text input
|
||||
|
||||
```svg
|
||||
<g transform="translate(0,0)">
|
||||
<rect width="320" height="40" rx="4" />
|
||||
<text x="12" y="25" font-size="14" stroke="none" fill="#666">Placeholder text</text>
|
||||
</g>
|
||||
```
|
||||
|
||||
### Search input (with magnifier)
|
||||
|
||||
```svg
|
||||
<g transform="translate(0,0)">
|
||||
<rect width="480" height="40" rx="20" />
|
||||
<circle cx="20" cy="20" r="6" />
|
||||
<line x1="24" y1="24" x2="30" y2="30" />
|
||||
<text x="40" y="25" font-size="14" stroke="none" fill="#666">Search…</text>
|
||||
</g>
|
||||
```
|
||||
|
||||
### Textarea
|
||||
|
||||
```svg
|
||||
<g transform="translate(0,0)">
|
||||
<rect width="480" height="120" rx="4" />
|
||||
<text x="12" y="24" font-size="14" stroke="none" fill="#666">Type your message…</text>
|
||||
</g>
|
||||
```
|
||||
|
||||
### Dropdown (collapsed)
|
||||
|
||||
```svg
|
||||
<g transform="translate(0,0)">
|
||||
<rect width="200" height="40" rx="4" />
|
||||
<text x="12" y="25" font-size="14" stroke="none" fill="#000">Selected value</text>
|
||||
<!-- chevron -->
|
||||
<polyline points="180,17 188,25 180,33" fill="none" />
|
||||
</g>
|
||||
```
|
||||
|
||||
### Checkbox (unchecked / checked)
|
||||
|
||||
```svg
|
||||
<!-- unchecked -->
|
||||
<g transform="translate(0,0)">
|
||||
<rect width="20" height="20" rx="2" />
|
||||
</g>
|
||||
|
||||
<!-- checked -->
|
||||
<g transform="translate(0,0)">
|
||||
<rect width="20" height="20" rx="2" fill="#000" />
|
||||
<polyline points="4,11 9,16 16,6" stroke="#fff" fill="none" />
|
||||
</g>
|
||||
```
|
||||
|
||||
### Radio (unselected / selected)
|
||||
|
||||
```svg
|
||||
<!-- unselected -->
|
||||
<g transform="translate(0,0)">
|
||||
<circle cx="10" cy="10" r="9" />
|
||||
</g>
|
||||
|
||||
<!-- selected -->
|
||||
<g transform="translate(0,0)">
|
||||
<circle cx="10" cy="10" r="9" />
|
||||
<circle cx="10" cy="10" r="4" fill="#000" />
|
||||
</g>
|
||||
```
|
||||
|
||||
### Toggle (off / on)
|
||||
|
||||
```svg
|
||||
<!-- off -->
|
||||
<g transform="translate(0,0)">
|
||||
<rect width="40" height="20" rx="10" />
|
||||
<circle cx="10" cy="10" r="6" fill="#000" />
|
||||
</g>
|
||||
|
||||
<!-- on -->
|
||||
<g transform="translate(0,0)">
|
||||
<rect width="40" height="20" rx="10" fill="#000" />
|
||||
<circle cx="30" cy="10" r="6" fill="#fff" />
|
||||
</g>
|
||||
```
|
||||
|
||||
### Form field (label + input + help)
|
||||
|
||||
```svg
|
||||
<g transform="translate(0,0)">
|
||||
<text x="0" y="14" font-size="14" font-weight="600" stroke="none" fill="#000">Email address</text>
|
||||
<g transform="translate(0,24)">
|
||||
<rect width="320" height="40" rx="4" />
|
||||
<text x="12" y="25" font-size="14" stroke="none" fill="#666">you@example.com</text>
|
||||
</g>
|
||||
<text x="0" y="84" font-size="12" stroke="none" fill="#666">We'll never share this with anyone.</text>
|
||||
</g>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Layout
|
||||
|
||||
### Card
|
||||
|
||||
```svg
|
||||
<g transform="translate(0,0)">
|
||||
<rect width="384" height="200" rx="6" />
|
||||
<text x="24" y="40" font-size="20" font-weight="600" stroke="none" fill="#000">Card title</text>
|
||||
<text x="24" y="68" font-size="14" stroke="none" fill="#666">Supporting copy goes here.</text>
|
||||
<line x1="0" y1="160" x2="384" y2="160" />
|
||||
<text x="24" y="184" font-size="14" stroke="none" fill="#000">Footer action</text>
|
||||
</g>
|
||||
```
|
||||
|
||||
### Section divider
|
||||
|
||||
```svg
|
||||
<g transform="translate(0,0)">
|
||||
<line x1="0" y1="0" x2="1184" y2="0" stroke="#666" />
|
||||
</g>
|
||||
```
|
||||
|
||||
### Two-column split (60 / 40)
|
||||
|
||||
```svg
|
||||
<g transform="translate(0,0)">
|
||||
<rect width="704" height="400" />
|
||||
<rect x="728" width="456" height="400" />
|
||||
</g>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Navigation
|
||||
|
||||
### Navbar (top)
|
||||
|
||||
```svg
|
||||
<g transform="translate(0,0)">
|
||||
<rect width="1280" height="64" />
|
||||
<!-- logo placeholder -->
|
||||
<rect x="24" y="16" width="32" height="32" rx="4" fill="#e6e6e6" />
|
||||
<!-- nav items -->
|
||||
<text x="80" y="40" font-size="14" stroke="none" fill="#000">Dashboard</text>
|
||||
<text x="184" y="40" font-size="14" stroke="none" fill="#666">Projects</text>
|
||||
<text x="272" y="40" font-size="14" stroke="none" fill="#666">Reports</text>
|
||||
<text x="352" y="40" font-size="14" stroke="none" fill="#666">Settings</text>
|
||||
<!-- right side: avatar -->
|
||||
<circle cx="1240" cy="32" r="16" fill="#e6e6e6" />
|
||||
</g>
|
||||
```
|
||||
|
||||
### Sidebar nav
|
||||
|
||||
```svg
|
||||
<g transform="translate(0,0)">
|
||||
<rect width="240" height="800" />
|
||||
<text x="24" y="40" font-size="20" font-weight="600" stroke="none" fill="#000">App</text>
|
||||
<!-- active item -->
|
||||
<rect x="0" y="80" width="240" height="40" fill="#e6e6e6" />
|
||||
<text x="24" y="105" font-size="14" stroke="none" fill="#000">Dashboard</text>
|
||||
<!-- inactive items -->
|
||||
<text x="24" y="153" font-size="14" stroke="none" fill="#666">Projects</text>
|
||||
<text x="24" y="201" font-size="14" stroke="none" fill="#666">Reports</text>
|
||||
<text x="24" y="249" font-size="14" stroke="none" fill="#666">Team</text>
|
||||
<text x="24" y="297" font-size="14" stroke="none" fill="#666">Settings</text>
|
||||
</g>
|
||||
```
|
||||
|
||||
### Tab bar
|
||||
|
||||
```svg
|
||||
<g transform="translate(0,0)">
|
||||
<line x1="0" y1="48" x2="640" y2="48" />
|
||||
<!-- active tab -->
|
||||
<text x="24" y="32" font-size="14" font-weight="600" stroke="none" fill="#000">Overview</text>
|
||||
<line x1="16" y1="48" x2="104" y2="48" stroke-width="3" />
|
||||
<!-- inactive tabs -->
|
||||
<text x="136" y="32" font-size="14" stroke="none" fill="#666">Activity</text>
|
||||
<text x="232" y="32" font-size="14" stroke="none" fill="#666">Members</text>
|
||||
<text x="328" y="32" font-size="14" stroke="none" fill="#666">Settings</text>
|
||||
</g>
|
||||
```
|
||||
|
||||
### Breadcrumb
|
||||
|
||||
```svg
|
||||
<g transform="translate(0,0)">
|
||||
<text x="0" y="14" font-size="14" stroke="none" fill="#666">Workspace</text>
|
||||
<text x="80" y="14" font-size="14" stroke="none" fill="#666">/</text>
|
||||
<text x="96" y="14" font-size="14" stroke="none" fill="#666">Projects</text>
|
||||
<text x="160" y="14" font-size="14" stroke="none" fill="#666">/</text>
|
||||
<text x="176" y="14" font-size="14" font-weight="600" stroke="none" fill="#000">Project name</text>
|
||||
</g>
|
||||
```
|
||||
|
||||
### Pagination
|
||||
|
||||
```svg
|
||||
<g transform="translate(0,0)">
|
||||
<rect width="32" height="32" />
|
||||
<text x="16" y="21" font-size="14" text-anchor="middle" stroke="none" fill="#666">‹</text>
|
||||
<rect x="40" width="32" height="32" fill="#000" />
|
||||
<text x="56" y="21" font-size="14" text-anchor="middle" stroke="none" fill="#fff">1</text>
|
||||
<rect x="80" width="32" height="32" />
|
||||
<text x="96" y="21" font-size="14" text-anchor="middle" stroke="none" fill="#000">2</text>
|
||||
<rect x="120" width="32" height="32" />
|
||||
<text x="136" y="21" font-size="14" text-anchor="middle" stroke="none" fill="#000">3</text>
|
||||
<rect x="160" width="32" height="32" />
|
||||
<text x="176" y="21" font-size="14" text-anchor="middle" stroke="none" fill="#666">›</text>
|
||||
</g>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Content
|
||||
|
||||
### Heading
|
||||
|
||||
```svg
|
||||
<text x="0" y="28" font-size="28" font-weight="700" stroke="none" fill="#000">Page title</text>
|
||||
```
|
||||
|
||||
### Section heading
|
||||
|
||||
```svg
|
||||
<text x="0" y="20" font-size="20" font-weight="600" stroke="none" fill="#000">Section</text>
|
||||
```
|
||||
|
||||
### Paragraph block (placeholder lines)
|
||||
|
||||
```svg
|
||||
<g transform="translate(0,0)">
|
||||
<line x1="0" y1="8" x2="480" y2="8" stroke="#666" />
|
||||
<line x1="0" y1="24" x2="480" y2="24" stroke="#666" />
|
||||
<line x1="0" y1="40" x2="320" y2="40" stroke="#666" />
|
||||
</g>
|
||||
```
|
||||
|
||||
### List row (avatar + title + subtitle + chevron)
|
||||
|
||||
```svg
|
||||
<g transform="translate(0,0)">
|
||||
<rect width="800" height="56" />
|
||||
<circle cx="32" cy="28" r="16" fill="#e6e6e6" />
|
||||
<text x="64" y="24" font-size="14" font-weight="600" stroke="none" fill="#000">Primary text</text>
|
||||
<text x="64" y="40" font-size="12" stroke="none" fill="#666">Secondary text</text>
|
||||
<polyline points="772,20 780,28 772,36" fill="none" />
|
||||
</g>
|
||||
```
|
||||
|
||||
### Table
|
||||
|
||||
```svg
|
||||
<g transform="translate(0,0)">
|
||||
<!-- header -->
|
||||
<rect width="800" height="48" fill="#f4f4f4" />
|
||||
<text x="16" y="30" font-size="12" font-weight="600" stroke="none" fill="#000">Name</text>
|
||||
<text x="280" y="30" font-size="12" font-weight="600" stroke="none" fill="#000">Status</text>
|
||||
<text x="480" y="30" font-size="12" font-weight="600" stroke="none" fill="#000">Updated</text>
|
||||
<text x="680" y="30" font-size="12" font-weight="600" stroke="none" fill="#000">Owner</text>
|
||||
|
||||
<!-- rows -->
|
||||
<g transform="translate(0,48)">
|
||||
<rect width="800" height="48" />
|
||||
<text x="16" y="30" font-size="14" stroke="none" fill="#000">Row title 1</text>
|
||||
<text x="280" y="30" font-size="14" stroke="none" fill="#666">Active</text>
|
||||
<text x="480" y="30" font-size="14" stroke="none" fill="#666">2h ago</text>
|
||||
<text x="680" y="30" font-size="14" stroke="none" fill="#666">Alex</text>
|
||||
</g>
|
||||
<g transform="translate(0,96)">
|
||||
<rect width="800" height="48" />
|
||||
<text x="16" y="30" font-size="14" stroke="none" fill="#000">Row title 2</text>
|
||||
<text x="280" y="30" font-size="14" stroke="none" fill="#666">Pending</text>
|
||||
<text x="480" y="30" font-size="14" stroke="none" fill="#666">5h ago</text>
|
||||
<text x="680" y="30" font-size="14" stroke="none" fill="#666">Sam</text>
|
||||
</g>
|
||||
</g>
|
||||
```
|
||||
|
||||
### Key-value pair
|
||||
|
||||
```svg
|
||||
<g transform="translate(0,0)">
|
||||
<text x="0" y="14" font-size="12" stroke="none" fill="#666">Created</text>
|
||||
<text x="0" y="34" font-size="14" stroke="none" fill="#000">Mar 12, 2026</text>
|
||||
</g>
|
||||
```
|
||||
|
||||
### Metric tile
|
||||
|
||||
```svg
|
||||
<g transform="translate(0,0)">
|
||||
<rect width="240" height="120" rx="6" />
|
||||
<text x="24" y="40" font-size="12" stroke="none" fill="#666">Active users</text>
|
||||
<text x="24" y="80" font-size="28" font-weight="700" stroke="none" fill="#000">1,284</text>
|
||||
<text x="24" y="104" font-size="12" stroke="none" fill="#666">+12% vs last week</text>
|
||||
</g>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Media
|
||||
|
||||
### Image placeholder (with diagonal cross)
|
||||
|
||||
```svg
|
||||
<g transform="translate(0,0)">
|
||||
<rect width="240" height="160" fill="#e6e6e6" />
|
||||
<line x1="0" y1="0" x2="240" y2="160" stroke="#666" />
|
||||
<line x1="240" y1="0" x2="0" y2="160" stroke="#666" />
|
||||
</g>
|
||||
```
|
||||
|
||||
### Avatar (circular, with cross)
|
||||
|
||||
```svg
|
||||
<g transform="translate(0,0)">
|
||||
<circle cx="24" cy="24" r="24" fill="#e6e6e6" />
|
||||
<line x1="7" y1="7" x2="41" y2="41" stroke="#666" />
|
||||
<line x1="41" y1="7" x2="7" y2="41" stroke="#666" />
|
||||
</g>
|
||||
```
|
||||
|
||||
### Video placeholder (image + play triangle)
|
||||
|
||||
```svg
|
||||
<g transform="translate(0,0)">
|
||||
<rect width="320" height="180" fill="#e6e6e6" />
|
||||
<line x1="0" y1="0" x2="320" y2="180" stroke="#666" />
|
||||
<line x1="320" y1="0" x2="0" y2="180" stroke="#666" />
|
||||
<circle cx="160" cy="90" r="32" fill="#fff" />
|
||||
<polygon points="150,76 150,104 178,90" fill="#000" stroke="none" />
|
||||
</g>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Overlay
|
||||
|
||||
### Modal (with backdrop)
|
||||
|
||||
```svg
|
||||
<!-- backdrop dims the canvas; render this inside an SVG that already has the underlying screen -->
|
||||
<rect x="0" y="0" width="1280" height="800" fill="#000" fill-opacity="0.4" stroke="none" />
|
||||
|
||||
<g transform="translate(320, 200)">
|
||||
<rect width="640" height="400" rx="6" />
|
||||
<text x="24" y="48" font-size="20" font-weight="600" stroke="none" fill="#000">Confirm action</text>
|
||||
<line x1="0" y1="72" x2="640" y2="72" />
|
||||
<text x="24" y="112" font-size="14" stroke="none" fill="#000">Body copy goes here describing what's about to happen.</text>
|
||||
<line x1="0" y1="328" x2="640" y2="328" />
|
||||
<!-- footer actions -->
|
||||
<g transform="translate(384, 348)">
|
||||
<rect width="120" height="40" rx="4" />
|
||||
<text x="60" y="25" font-size="14" text-anchor="middle" stroke="none" fill="#000">Cancel</text>
|
||||
</g>
|
||||
<g transform="translate(512, 348)">
|
||||
<rect width="104" height="40" rx="4" fill="#000" />
|
||||
<text x="52" y="25" font-size="14" text-anchor="middle" stroke="none" fill="#fff">Confirm</text>
|
||||
</g>
|
||||
</g>
|
||||
```
|
||||
|
||||
### Toast
|
||||
|
||||
```svg
|
||||
<g transform="translate(0,0)">
|
||||
<rect width="320" height="56" rx="6" fill="#000" />
|
||||
<text x="24" y="34" font-size="14" stroke="none" fill="#fff">Saved successfully</text>
|
||||
<text x="280" y="34" font-size="14" stroke="none" fill="#fff">×</text>
|
||||
</g>
|
||||
```
|
||||
|
||||
### Tooltip
|
||||
|
||||
```svg
|
||||
<g transform="translate(0,0)">
|
||||
<rect width="160" height="32" rx="4" fill="#000" />
|
||||
<text x="80" y="20" font-size="12" text-anchor="middle" stroke="none" fill="#fff">Helpful explanation</text>
|
||||
<polygon points="76,32 80,40 84,32" fill="#000" stroke="none" />
|
||||
</g>
|
||||
```
|
||||
|
||||
### Dropdown menu (open state)
|
||||
|
||||
```svg
|
||||
<g transform="translate(0,0)">
|
||||
<rect width="200" height="160" rx="4" />
|
||||
<text x="16" y="28" font-size="14" stroke="none" fill="#000">Menu item one</text>
|
||||
<text x="16" y="60" font-size="14" stroke="none" fill="#000">Menu item two</text>
|
||||
<text x="16" y="92" font-size="14" stroke="none" fill="#000">Menu item three</text>
|
||||
<line x1="0" y1="108" x2="200" y2="108" />
|
||||
<text x="16" y="136" font-size="14" stroke="none" fill="#000">Sign out</text>
|
||||
</g>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Annotation layer
|
||||
|
||||
Use these for callouts and reviewer notes. Render them in a final `<g data-region="annotations">` so reviewers can hide them by toggling the group.
|
||||
|
||||
### Numbered callout
|
||||
|
||||
```svg
|
||||
<g transform="translate(0,0)">
|
||||
<circle cx="0" cy="0" r="12" fill="#fff" stroke="#d33" stroke-dasharray="4 2" />
|
||||
<text x="0" y="4" font-size="12" font-weight="700" text-anchor="middle" stroke="none" fill="#d33">1</text>
|
||||
</g>
|
||||
```
|
||||
|
||||
### Dashed region highlight
|
||||
|
||||
```svg
|
||||
<rect x="0" y="0" width="240" height="120" fill="none" stroke="#d33" stroke-dasharray="6 3" />
|
||||
```
|
||||
|
||||
### Arrow connector (between two screens)
|
||||
|
||||
```svg
|
||||
<g transform="translate(0,0)">
|
||||
<line x1="0" y1="0" x2="120" y2="0" stroke="#000" />
|
||||
<polygon points="120,0 110,-6 110,6" fill="#000" stroke="none" />
|
||||
</g>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Common composition mistakes (avoid)
|
||||
|
||||
- **Text with halo:** forgetting `stroke="none"` on `<text>`. The text inherits the parent stroke.
|
||||
- **Off-grid coordinates:** values like `x="37"` instead of `x="40"`. Snap everything to multiples of 8.
|
||||
- **Solid fills sneaking in:** anything other than `#fff`, `#e6e6e6`, `#f4f4f4`, or `#000` is a mistake.
|
||||
- **Multiple typefaces:** stick to one font-family across the whole file.
|
||||
- **Annotation colour bleeding into UI:** `#d33` only ever appears inside the annotation `<g>`.
|
||||
- **Missing `viewBox`:** without it, the SVG won't scale when embedded in different containers.
|
||||
|
|
@ -0,0 +1,362 @@
|
|||
# Worked examples
|
||||
|
||||
Four complete wireframes you can copy and modify. Each one is a valid standalone SVG file. The annotation list under each example is what you should reproduce in your reply when emitting a wireframe.
|
||||
|
||||
---
|
||||
|
||||
## 1. Login screen (mobile, 375×812)
|
||||
|
||||
```svg
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="375" height="812" viewBox="0 0 375 812"
|
||||
font-family="-apple-system, system-ui, sans-serif" fill="#fff" stroke="#000" stroke-width="1.5">
|
||||
<rect x="0" y="0" width="375" height="812" />
|
||||
|
||||
<!-- 1: status bar (placeholder) -->
|
||||
<g transform="translate(16, 16)">
|
||||
<text x="0" y="14" font-size="12" stroke="none" fill="#666">9:41</text>
|
||||
<text x="343" y="14" font-size="12" text-anchor="end" stroke="none" fill="#666">100%</text>
|
||||
</g>
|
||||
|
||||
<!-- 2: brand mark -->
|
||||
<g transform="translate(159, 120)">
|
||||
<rect width="56" height="56" rx="8" fill="#e6e6e6" />
|
||||
</g>
|
||||
|
||||
<!-- 3: title -->
|
||||
<text x="187" y="216" font-size="28" font-weight="700" text-anchor="middle" stroke="none" fill="#000">Welcome back</text>
|
||||
<text x="187" y="248" font-size="14" text-anchor="middle" stroke="none" fill="#666">Sign in to continue</text>
|
||||
|
||||
<!-- 4: email field -->
|
||||
<g transform="translate(16, 296)">
|
||||
<text x="0" y="14" font-size="14" font-weight="600" stroke="none" fill="#000">Email</text>
|
||||
<g transform="translate(0, 24)">
|
||||
<rect width="343" height="48" rx="4" />
|
||||
<text x="12" y="30" font-size="14" stroke="none" fill="#666">you@example.com</text>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
<!-- 5: password field -->
|
||||
<g transform="translate(16, 400)">
|
||||
<text x="0" y="14" font-size="14" font-weight="600" stroke="none" fill="#000">Password</text>
|
||||
<g transform="translate(0, 24)">
|
||||
<rect width="343" height="48" rx="4" />
|
||||
<text x="12" y="30" font-size="14" stroke="none" fill="#666">••••••••</text>
|
||||
<text x="331" y="30" font-size="12" text-anchor="end" stroke="none" fill="#666">show</text>
|
||||
</g>
|
||||
<text x="343" y="92" font-size="12" text-anchor="end" stroke="none" fill="#666">Forgot password?</text>
|
||||
</g>
|
||||
|
||||
<!-- 6: primary CTA -->
|
||||
<g transform="translate(16, 528)">
|
||||
<rect width="343" height="48" rx="4" fill="#000" />
|
||||
<text x="171" y="30" font-size="14" font-weight="600" text-anchor="middle" stroke="none" fill="#fff">Sign in</text>
|
||||
</g>
|
||||
|
||||
<!-- 7: divider -->
|
||||
<g transform="translate(16, 600)">
|
||||
<line x1="0" y1="8" x2="140" y2="8" stroke="#666" />
|
||||
<text x="171" y="12" font-size="12" text-anchor="middle" stroke="none" fill="#666">or</text>
|
||||
<line x1="203" y1="8" x2="343" y2="8" stroke="#666" />
|
||||
</g>
|
||||
|
||||
<!-- 8: secondary CTA -->
|
||||
<g transform="translate(16, 632)">
|
||||
<rect width="343" height="48" rx="4" />
|
||||
<text x="171" y="30" font-size="14" text-anchor="middle" stroke="none" fill="#000">Continue with SSO</text>
|
||||
</g>
|
||||
|
||||
<!-- 9: footer -->
|
||||
<text x="187" y="744" font-size="14" text-anchor="middle" stroke="none" fill="#000">New here? <tspan font-weight="600">Create an account</tspan></text>
|
||||
</svg>
|
||||
```
|
||||
|
||||
**Annotations:**
|
||||
1. Status bar placeholder
|
||||
2. Brand mark
|
||||
3. Page title + subtitle
|
||||
4. Email input
|
||||
5. Password input + reveal control + forgot link
|
||||
6. Primary CTA (sign in)
|
||||
7. SSO divider
|
||||
8. Secondary CTA (SSO)
|
||||
9. Sign-up link
|
||||
|
||||
---
|
||||
|
||||
## 2. Admin dashboard (desktop, 1280×800)
|
||||
|
||||
```svg
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1280" height="800" viewBox="0 0 1280 800"
|
||||
font-family="-apple-system, system-ui, sans-serif" fill="#fff" stroke="#000" stroke-width="1.5">
|
||||
<rect x="0" y="0" width="1280" height="800" />
|
||||
|
||||
<!-- 1: sidebar -->
|
||||
<g transform="translate(0, 0)" data-region="sidebar">
|
||||
<rect width="240" height="800" />
|
||||
<text x="24" y="40" font-size="20" font-weight="600" stroke="none" fill="#000">Acme</text>
|
||||
<rect x="0" y="80" width="240" height="40" fill="#e6e6e6" />
|
||||
<text x="24" y="105" font-size="14" stroke="none" fill="#000">Dashboard</text>
|
||||
<text x="24" y="153" font-size="14" stroke="none" fill="#666">Projects</text>
|
||||
<text x="24" y="201" font-size="14" stroke="none" fill="#666">Reports</text>
|
||||
<text x="24" y="249" font-size="14" stroke="none" fill="#666">Team</text>
|
||||
<text x="24" y="297" font-size="14" stroke="none" fill="#666">Settings</text>
|
||||
</g>
|
||||
|
||||
<!-- 2: top bar -->
|
||||
<g transform="translate(240, 0)" data-region="topbar">
|
||||
<rect width="1040" height="64" />
|
||||
<g transform="translate(24, 12)">
|
||||
<rect width="400" height="40" rx="20" />
|
||||
<circle cx="20" cy="20" r="6" />
|
||||
<line x1="24" y1="24" x2="30" y2="30" />
|
||||
<text x="40" y="25" font-size="14" stroke="none" fill="#666">Search…</text>
|
||||
</g>
|
||||
<circle cx="1000" cy="32" r="16" fill="#e6e6e6" />
|
||||
</g>
|
||||
|
||||
<!-- 3: page header -->
|
||||
<g transform="translate(264, 96)" data-region="header">
|
||||
<text x="0" y="28" font-size="28" font-weight="700" stroke="none" fill="#000">Dashboard</text>
|
||||
<text x="0" y="56" font-size="14" stroke="none" fill="#666">Overview of activity for the last 7 days</text>
|
||||
<g transform="translate(872, 0)">
|
||||
<rect width="120" height="40" rx="4" fill="#000" />
|
||||
<text x="60" y="25" font-size="14" font-weight="600" text-anchor="middle" stroke="none" fill="#fff">New project</text>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
<!-- 4: metric tiles -->
|
||||
<g transform="translate(264, 184)" data-region="metrics">
|
||||
<g transform="translate(0, 0)">
|
||||
<rect width="232" height="120" rx="6" />
|
||||
<text x="24" y="40" font-size="12" stroke="none" fill="#666">Active users</text>
|
||||
<text x="24" y="80" font-size="28" font-weight="700" stroke="none" fill="#000">1,284</text>
|
||||
<text x="24" y="104" font-size="12" stroke="none" fill="#666">+12% vs last week</text>
|
||||
</g>
|
||||
<g transform="translate(256, 0)">
|
||||
<rect width="232" height="120" rx="6" />
|
||||
<text x="24" y="40" font-size="12" stroke="none" fill="#666">New signups</text>
|
||||
<text x="24" y="80" font-size="28" font-weight="700" stroke="none" fill="#000">312</text>
|
||||
<text x="24" y="104" font-size="12" stroke="none" fill="#666">+4%</text>
|
||||
</g>
|
||||
<g transform="translate(512, 0)">
|
||||
<rect width="232" height="120" rx="6" />
|
||||
<text x="24" y="40" font-size="12" stroke="none" fill="#666">Revenue</text>
|
||||
<text x="24" y="80" font-size="28" font-weight="700" stroke="none" fill="#000">$24.1k</text>
|
||||
<text x="24" y="104" font-size="12" stroke="none" fill="#666">+8%</text>
|
||||
</g>
|
||||
<g transform="translate(768, 0)">
|
||||
<rect width="232" height="120" rx="6" />
|
||||
<text x="24" y="40" font-size="12" stroke="none" fill="#666">Churn</text>
|
||||
<text x="24" y="80" font-size="28" font-weight="700" stroke="none" fill="#000">1.4%</text>
|
||||
<text x="24" y="104" font-size="12" stroke="none" fill="#666">−0.3%</text>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
<!-- 5: chart placeholder -->
|
||||
<g transform="translate(264, 328)" data-region="chart">
|
||||
<rect width="640" height="320" rx="6" />
|
||||
<text x="24" y="40" font-size="20" font-weight="600" stroke="none" fill="#000">Activity</text>
|
||||
<rect x="24" y="64" width="592" height="232" fill="#e6e6e6" />
|
||||
<line x1="24" y1="64" x2="616" y2="296" stroke="#666" />
|
||||
<line x1="616" y1="64" x2="24" y2="296" stroke="#666" />
|
||||
</g>
|
||||
|
||||
<!-- 6: recent items list -->
|
||||
<g transform="translate(920, 328)" data-region="recent">
|
||||
<rect width="336" height="320" rx="6" />
|
||||
<text x="24" y="40" font-size="20" font-weight="600" stroke="none" fill="#000">Recent</text>
|
||||
<g transform="translate(0, 64)">
|
||||
<line x1="0" y1="0" x2="336" y2="0" stroke="#666" />
|
||||
<circle cx="32" cy="28" r="12" fill="#e6e6e6" />
|
||||
<text x="56" y="24" font-size="14" font-weight="600" stroke="none" fill="#000">Item one</text>
|
||||
<text x="56" y="40" font-size="12" stroke="none" fill="#666">2h ago</text>
|
||||
</g>
|
||||
<g transform="translate(0, 120)">
|
||||
<line x1="0" y1="0" x2="336" y2="0" stroke="#666" />
|
||||
<circle cx="32" cy="28" r="12" fill="#e6e6e6" />
|
||||
<text x="56" y="24" font-size="14" font-weight="600" stroke="none" fill="#000">Item two</text>
|
||||
<text x="56" y="40" font-size="12" stroke="none" fill="#666">5h ago</text>
|
||||
</g>
|
||||
<g transform="translate(0, 176)">
|
||||
<line x1="0" y1="0" x2="336" y2="0" stroke="#666" />
|
||||
<circle cx="32" cy="28" r="12" fill="#e6e6e6" />
|
||||
<text x="56" y="24" font-size="14" font-weight="600" stroke="none" fill="#000">Item three</text>
|
||||
<text x="56" y="40" font-size="12" stroke="none" fill="#666">1d ago</text>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
```
|
||||
|
||||
**Annotations:**
|
||||
1. Sidebar nav with active "Dashboard"
|
||||
2. Top bar with global search and account menu
|
||||
3. Page header with title, subtitle, and primary CTA
|
||||
4. Four KPI metric tiles
|
||||
5. Activity chart panel (chart area shown as placeholder)
|
||||
6. Recent items list (right rail)
|
||||
|
||||
---
|
||||
|
||||
## 3. Settings page with form (desktop, 1280×800)
|
||||
|
||||
```svg
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1280" height="800" viewBox="0 0 1280 800"
|
||||
font-family="-apple-system, system-ui, sans-serif" fill="#fff" stroke="#000" stroke-width="1.5">
|
||||
<rect x="0" y="0" width="1280" height="800" />
|
||||
|
||||
<!-- 1: top bar -->
|
||||
<g transform="translate(0, 0)">
|
||||
<rect width="1280" height="64" />
|
||||
<text x="24" y="40" font-size="20" font-weight="600" stroke="none" fill="#000">Settings</text>
|
||||
<circle cx="1240" cy="32" r="16" fill="#e6e6e6" />
|
||||
</g>
|
||||
|
||||
<!-- 2: settings nav (left rail) -->
|
||||
<g transform="translate(24, 96)">
|
||||
<text x="0" y="14" font-size="12" font-weight="600" stroke="none" fill="#666">PERSONAL</text>
|
||||
<rect x="-8" y="32" width="216" height="32" fill="#e6e6e6" />
|
||||
<text x="0" y="52" font-size="14" stroke="none" fill="#000">Account</text>
|
||||
<text x="0" y="84" font-size="14" stroke="none" fill="#666">Notifications</text>
|
||||
<text x="0" y="116" font-size="14" stroke="none" fill="#666">Sessions</text>
|
||||
<text x="0" y="160" font-size="12" font-weight="600" stroke="none" fill="#666">WORKSPACE</text>
|
||||
<text x="0" y="200" font-size="14" stroke="none" fill="#666">Members</text>
|
||||
<text x="0" y="232" font-size="14" stroke="none" fill="#666">Billing</text>
|
||||
<text x="0" y="264" font-size="14" stroke="none" fill="#666">Integrations</text>
|
||||
</g>
|
||||
|
||||
<!-- 3: form content -->
|
||||
<g transform="translate(264, 96)">
|
||||
<text x="0" y="28" font-size="28" font-weight="700" stroke="none" fill="#000">Account</text>
|
||||
<text x="0" y="56" font-size="14" stroke="none" fill="#666">Manage your personal account details.</text>
|
||||
|
||||
<!-- avatar field -->
|
||||
<g transform="translate(0, 96)">
|
||||
<text x="0" y="14" font-size="14" font-weight="600" stroke="none" fill="#000">Profile photo</text>
|
||||
<g transform="translate(0, 24)">
|
||||
<circle cx="32" cy="32" r="32" fill="#e6e6e6" />
|
||||
<line x1="9" y1="9" x2="55" y2="55" stroke="#666" />
|
||||
<line x1="55" y1="9" x2="9" y2="55" stroke="#666" />
|
||||
</g>
|
||||
<g transform="translate(80, 36)">
|
||||
<rect width="120" height="40" rx="4" />
|
||||
<text x="60" y="25" font-size="14" text-anchor="middle" stroke="none" fill="#000">Upload</text>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
<!-- name field -->
|
||||
<g transform="translate(0, 216)">
|
||||
<text x="0" y="14" font-size="14" font-weight="600" stroke="none" fill="#000">Display name</text>
|
||||
<g transform="translate(0, 24)">
|
||||
<rect width="480" height="40" rx="4" />
|
||||
<text x="12" y="25" font-size="14" stroke="none" fill="#000">Alex Morgan</text>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
<!-- email field -->
|
||||
<g transform="translate(0, 312)">
|
||||
<text x="0" y="14" font-size="14" font-weight="600" stroke="none" fill="#000">Email</text>
|
||||
<g transform="translate(0, 24)">
|
||||
<rect width="480" height="40" rx="4" />
|
||||
<text x="12" y="25" font-size="14" stroke="none" fill="#000">alex@acme.com</text>
|
||||
</g>
|
||||
<text x="0" y="84" font-size="12" stroke="none" fill="#666">Used for sign-in and notifications.</text>
|
||||
</g>
|
||||
|
||||
<!-- role dropdown -->
|
||||
<g transform="translate(0, 432)">
|
||||
<text x="0" y="14" font-size="14" font-weight="600" stroke="none" fill="#000">Role</text>
|
||||
<g transform="translate(0, 24)">
|
||||
<rect width="200" height="40" rx="4" />
|
||||
<text x="12" y="25" font-size="14" stroke="none" fill="#000">Admin</text>
|
||||
<polyline points="180,17 188,25 180,33" fill="none" />
|
||||
</g>
|
||||
</g>
|
||||
|
||||
<!-- footer actions -->
|
||||
<g transform="translate(0, 552)">
|
||||
<line x1="0" y1="0" x2="800" y2="0" stroke="#666" />
|
||||
<g transform="translate(560, 24)">
|
||||
<rect width="120" height="40" rx="4" />
|
||||
<text x="60" y="25" font-size="14" text-anchor="middle" stroke="none" fill="#000">Cancel</text>
|
||||
</g>
|
||||
<g transform="translate(688, 24)">
|
||||
<rect width="112" height="40" rx="4" fill="#000" />
|
||||
<text x="56" y="25" font-size="14" font-weight="600" text-anchor="middle" stroke="none" fill="#fff">Save</text>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
```
|
||||
|
||||
**Annotations:**
|
||||
1. Top bar with section name
|
||||
2. Settings sub-nav (Personal / Workspace groups)
|
||||
3. Form: avatar with upload, display name, email + help text, role dropdown, save / cancel actions
|
||||
|
||||
---
|
||||
|
||||
## 4. Modal confirmation overlay (desktop, 1280×800)
|
||||
|
||||
```svg
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1280" height="800" viewBox="0 0 1280 800"
|
||||
font-family="-apple-system, system-ui, sans-serif" fill="#fff" stroke="#000" stroke-width="1.5">
|
||||
<rect x="0" y="0" width="1280" height="800" />
|
||||
|
||||
<!-- underlying screen (faded) -->
|
||||
<g transform="translate(0,0)" opacity="0.5">
|
||||
<rect width="1280" height="64" />
|
||||
<text x="24" y="40" font-size="20" font-weight="600" stroke="none" fill="#000">Projects</text>
|
||||
</g>
|
||||
|
||||
<!-- 1: backdrop -->
|
||||
<rect x="0" y="0" width="1280" height="800" fill="#000" fill-opacity="0.4" stroke="none" />
|
||||
|
||||
<!-- 2: modal -->
|
||||
<g transform="translate(320, 240)" data-region="modal">
|
||||
<rect width="640" height="320" rx="6" />
|
||||
<text x="24" y="48" font-size="20" font-weight="600" stroke="none" fill="#000">Delete project?</text>
|
||||
<line x1="0" y1="72" x2="640" y2="72" />
|
||||
|
||||
<text x="24" y="112" font-size="14" stroke="none" fill="#000">This permanently deletes the project and all its data.</text>
|
||||
<text x="24" y="136" font-size="14" stroke="none" fill="#000">This action can't be undone.</text>
|
||||
|
||||
<!-- confirm input -->
|
||||
<g transform="translate(24, 168)">
|
||||
<text x="0" y="14" font-size="12" stroke="none" fill="#666">Type the project name to confirm</text>
|
||||
<g transform="translate(0, 24)">
|
||||
<rect width="592" height="40" rx="4" />
|
||||
<text x="12" y="25" font-size="14" stroke="none" fill="#666">acme-prod</text>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
<!-- footer -->
|
||||
<line x1="0" y1="248" x2="640" y2="248" />
|
||||
<g transform="translate(384, 268)">
|
||||
<rect width="120" height="40" rx="4" />
|
||||
<text x="60" y="25" font-size="14" text-anchor="middle" stroke="none" fill="#000">Cancel</text>
|
||||
</g>
|
||||
<g transform="translate(512, 268)">
|
||||
<rect width="104" height="40" rx="4" fill="#000" />
|
||||
<text x="52" y="25" font-size="14" font-weight="600" text-anchor="middle" stroke="none" fill="#fff">Delete</text>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
<!-- 3: annotation -->
|
||||
<g data-region="annotations">
|
||||
<circle cx="976" cy="556" r="12" fill="#fff" stroke="#d33" stroke-dasharray="4 2" />
|
||||
<text x="976" y="560" font-size="12" font-weight="700" text-anchor="middle" stroke="none" fill="#d33">1</text>
|
||||
<text x="996" y="564" font-size="12" stroke="none" fill="#d33">Disable until input matches project name</text>
|
||||
</g>
|
||||
</svg>
|
||||
```
|
||||
|
||||
**Annotations:**
|
||||
1. Backdrop dims the underlying page
|
||||
2. Confirmation modal: title, body copy, type-to-confirm field, Cancel + destructive Confirm
|
||||
3. Reviewer note (red dashed): the destructive button must remain disabled until the typed input matches
|
||||
|
||||
---
|
||||
|
||||
## Multi-screen flow
|
||||
|
||||
When emitting a flow, render each screen as its own `<g transform="translate(x,0)">` inside one SVG, separated by 80px gutters and connected with arrow primitives from `components.md`. Or emit one SVG per screen and a `flow.svg` summary that arranges thumbnails (scaled with `transform="scale(0.25)"`) left-to-right. Either is acceptable; pick whichever the reviewer can scan faster.
|
||||
|
|
@ -0,0 +1,107 @@
|
|||
# Grid, palette, and type scale
|
||||
|
||||
These are the only values you may use. Do not introduce new colours, sizes, or grid units.
|
||||
|
||||
## Canvas presets
|
||||
|
||||
| Viewport | Width × Height | Use for |
|
||||
| -------- | -------------- | ----------------------------- |
|
||||
| Desktop | 1280 × 800 | Default for web app screens |
|
||||
| Wide | 1440 × 900 | Marketing landing pages |
|
||||
| Tablet | 768 × 1024 | iPad-class screens |
|
||||
| Mobile | 375 × 812 | iPhone-class screens |
|
||||
|
||||
Always include `viewBox="0 0 W H"` matching the canvas so it scales when embedded.
|
||||
|
||||
## Grid
|
||||
|
||||
- Base unit: **8px**. All `x`, `y`, `width`, `height` values must be multiples of 8.
|
||||
- Outer page margin: **24px** on desktop/tablet, **16px** on mobile.
|
||||
- Column gutter: **24px** desktop, **16px** mobile.
|
||||
- Vertical rhythm: **24px** between sibling components.
|
||||
|
||||
### Desktop 12-column grid
|
||||
|
||||
- Total width: 1280
|
||||
- Outer margin (each side): 48
|
||||
- Inner content width: 1184
|
||||
- Column width: 88, gutter 8 → 12 × (88 + 8) − 8 = 1144 + 40 = 1184 ✓
|
||||
|
||||
In practice, snap to common widths:
|
||||
- Sidebar: 240
|
||||
- Content max: 944 (after sidebar)
|
||||
- Card grid: 3 × 384 with 24 gutters or 4 × 280 with 24 gutters
|
||||
- Modal width: 480 (small), 640 (default), 800 (wide)
|
||||
|
||||
### Mobile single column
|
||||
|
||||
- Total width: 375
|
||||
- Outer margin: 16 each side → content 343
|
||||
- Tap targets: 44 minimum height (snap to 48)
|
||||
|
||||
## Palette (the only colours allowed)
|
||||
|
||||
| Name | Hex | Use |
|
||||
| ---------------- | ---------- | -------------------------------------------------------- |
|
||||
| Ink | `#000` | Strokes, primary text |
|
||||
| Paper | `#fff` | Default fill |
|
||||
| Mute text | `#666` | Placeholder text inside inputs, secondary labels |
|
||||
| Placeholder grey | `#e6e6e6` | Image/avatar/empty-state regions |
|
||||
| Subtle grey | `#f4f4f4` | Optional zebra rows in tables; nothing else |
|
||||
| Annotation red | `#d33` | Annotation layer ONLY — dashed borders, callout numbers |
|
||||
|
||||
That's the entire palette. No hover states, no focus rings, no brand colours.
|
||||
|
||||
## Type scale
|
||||
|
||||
Single typeface: `font-family="-apple-system, system-ui, sans-serif"`.
|
||||
|
||||
| Role | Size | Weight | Use |
|
||||
| -------- | ---- | ------ | -------------------------------- |
|
||||
| Caption | 12 | 400 | Help text, metadata, table footnotes |
|
||||
| Body | 14 | 400 | Default text, button labels, list rows |
|
||||
| Heading | 20 | 600 | Section headings, card titles |
|
||||
| Title | 28 | 700 | Page title (one per screen) |
|
||||
|
||||
Font-weight is the only typographic variation allowed beyond size. No italics, no underline (except links — see below).
|
||||
|
||||
### Link convention
|
||||
|
||||
For text links, render as body 14, with `text-decoration="underline"`. No colour change.
|
||||
|
||||
### Strokes on text
|
||||
|
||||
Always set `stroke="none"` on `<text>` elements. The wireframe SVG sets a default stroke at the `<svg>` root for boxes; text inherits it as an unwanted halo unless overridden.
|
||||
|
||||
## Standard component sizes
|
||||
|
||||
These appear so often you should memorise them.
|
||||
|
||||
| Component | Size (W × H) |
|
||||
| ----------------- | ------------ |
|
||||
| Button (default) | 120 × 40 |
|
||||
| Button (small) | 80 × 32 |
|
||||
| Button (icon) | 40 × 40 |
|
||||
| Text input | 320 × 40 |
|
||||
| Text input (full) | 100% × 40 |
|
||||
| Search input | 480 × 40 |
|
||||
| Dropdown | 200 × 40 |
|
||||
| Checkbox / radio | 20 × 20 |
|
||||
| Avatar (small) | 32 × 32 circle |
|
||||
| Avatar (medium) | 48 × 48 circle |
|
||||
| Navbar | 100% × 64 |
|
||||
| Tab | (auto) × 48 |
|
||||
| List row | 100% × 56 |
|
||||
| Table row | 100% × 48 |
|
||||
| Card padding | 24 inside |
|
||||
| Modal | 480 / 640 / 800 wide, height auto |
|
||||
|
||||
## Coordinate conventions
|
||||
|
||||
- Place every primitive inside a `<g transform="translate(x, y)">` so its internal coordinates start at `(0, 0)`. This makes primitives copy-pastable across screens.
|
||||
- Use comments above each primitive: `<!-- 1: nav -->`, `<!-- 2: search -->` matching the annotation list you write below the SVG.
|
||||
- Group related primitives under a parent `<g>` with a `data-region="..."` attribute for searchability.
|
||||
|
||||
## Negative space
|
||||
|
||||
Empty space is part of the design. Do not fill the canvas. A wireframe with one card centered in the viewport is a valid wireframe if that's the screen's intent.
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
"schemaVersion": 1,
|
||||
"packageName": "@paperclipai/skills-catalog",
|
||||
"packageVersion": "0.3.1",
|
||||
"generatedAt": "2026-05-28T03:02:49.579Z",
|
||||
"generatedAt": "2026-05-31T18:06:11.126Z",
|
||||
"skills": [
|
||||
{
|
||||
"id": "paperclipai:bundled:docs:doc-maintenance",
|
||||
|
|
@ -108,6 +108,78 @@
|
|||
],
|
||||
"contentHash": "sha256:4fb46a4bcefad4fd46fae48c433ee497112509a8e19fb8a7745ead44d219b498"
|
||||
},
|
||||
{
|
||||
"id": "paperclipai:bundled:product:wireframe",
|
||||
"key": "paperclipai/bundled/product/wireframe",
|
||||
"kind": "bundled",
|
||||
"category": "product",
|
||||
"slug": "wireframe",
|
||||
"name": "wireframe",
|
||||
"description": "Produce low-fidelity black-and-white UI wireframes as standalone SVG files, optionally bundled into a single-page HTML viewer and published via the here-now skill. Use when the user asks to \"wireframe X\", \"sketch a screen for\", \"draft a layout\", \"low-fi mockup\", \"rough mock\", \"make a page to view the wireframes\", \"build a viewer for these screens\", or to \"deploy / publish / host the wireframes\". Do NOT use when the user wants production UI code, branded designs, hi-fi mockups, or animated/interactive prototypes — use frontend-design or similar instead.",
|
||||
"path": "catalog/bundled/product/wireframe",
|
||||
"entrypoint": "SKILL.md",
|
||||
"trustLevel": "assets",
|
||||
"compatibility": "compatible",
|
||||
"defaultInstall": false,
|
||||
"recommendedForRoles": [
|
||||
"designer",
|
||||
"product",
|
||||
"engineer"
|
||||
],
|
||||
"requires": [],
|
||||
"tags": [
|
||||
"design",
|
||||
"wireframe",
|
||||
"ux",
|
||||
"prototyping",
|
||||
"svg"
|
||||
],
|
||||
"files": [
|
||||
{
|
||||
"path": "SKILL.md",
|
||||
"kind": "skill",
|
||||
"sizeBytes": 11887,
|
||||
"sha256": "a910d038d8cdd13615b82f44a2195b781ebccf01c49f9c58babd43b0701fe644"
|
||||
},
|
||||
{
|
||||
"path": "assets/site-template.html",
|
||||
"kind": "asset",
|
||||
"sizeBytes": 14791,
|
||||
"sha256": "8886ad77c386c17b0457f11624aab136c33db7f28dcd6f10fc18e9d833d0950f"
|
||||
},
|
||||
{
|
||||
"path": "assets/template-mobile.svg",
|
||||
"kind": "asset",
|
||||
"sizeBytes": 925,
|
||||
"sha256": "dc32af257df1f986b87a7b30a6104de825057dc4d6f12b381f5a28308f0002f0"
|
||||
},
|
||||
{
|
||||
"path": "assets/template.svg",
|
||||
"kind": "asset",
|
||||
"sizeBytes": 1262,
|
||||
"sha256": "63f8adb8eb4b21dea6fd442b9e9312704c419535eb577d4ef0bfb54410fd576b"
|
||||
},
|
||||
{
|
||||
"path": "references/components.md",
|
||||
"kind": "reference",
|
||||
"sizeBytes": 14806,
|
||||
"sha256": "4ce18f418d78b13cdfa6fe8070173044a631576cd2b999571e6703f58894496c"
|
||||
},
|
||||
{
|
||||
"path": "references/examples.md",
|
||||
"kind": "reference",
|
||||
"sizeBytes": 16108,
|
||||
"sha256": "18f486e95a578a50cf6c9f93394b82534392771320692e4876edf17054bfae55"
|
||||
},
|
||||
{
|
||||
"path": "references/grid-system.md",
|
||||
"kind": "reference",
|
||||
"sizeBytes": 4581,
|
||||
"sha256": "017260d2216859d57e5fa869e0efc6b88c7ed4d8a8a4f5af04fe89a3ef2445bd"
|
||||
}
|
||||
],
|
||||
"contentHash": "sha256:0bd9a9fdc656d529e3f97c00cd504dcf72d3a4fecb8b0504ca2fe3e00d63287f"
|
||||
},
|
||||
{
|
||||
"id": "paperclipai:bundled:quality:qa-acceptance",
|
||||
"key": "paperclipai/bundled/quality/qa-acceptance",
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ const EXPECTED_BUNDLED_KEYS = [
|
|||
"paperclipai/bundled/docs/doc-maintenance",
|
||||
"paperclipai/bundled/paperclip-operations/issue-triage",
|
||||
"paperclipai/bundled/paperclip-operations/task-planning",
|
||||
"paperclipai/bundled/product/wireframe",
|
||||
"paperclipai/bundled/quality/qa-acceptance",
|
||||
"paperclipai/bundled/software-development/github-pr-workflow",
|
||||
];
|
||||
|
|
@ -31,8 +32,11 @@ describe("shipped skills catalog", () => {
|
|||
expect(optionalKeys).toEqual(EXPECTED_OPTIONAL_KEYS);
|
||||
});
|
||||
|
||||
it("keeps every shipped skill markdown-only until a script-bearing skill clears security review", () => {
|
||||
const scriptBearing = catalogSkills.filter((skill) => skill.trustLevel !== "markdown_only");
|
||||
it("keeps every shipped skill free of executable scripts until script-bearing skills clear security review", () => {
|
||||
// The real install-time security boundary (server assertCatalogSkillInstallable) blocks
|
||||
// only "scripts_executables". Static assets (svg/html templates, e.g. the wireframe skill)
|
||||
// carry the "assets" trust level and are installable, so they are allowed in the catalog.
|
||||
const scriptBearing = catalogSkills.filter((skill) => skill.trustLevel === "scripts_executables");
|
||||
expect(scriptBearing, formatViolations("script-bearing skills require security review", scriptBearing)).toEqual([]);
|
||||
});
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue