A colophon is the note at the back of a book naming its typefaces, materials, and makers. This is that — for your app's UI.
Colophon gives AI coding agents a shared, living design system for every repo — seed it, edit it in a live canvas, prototype flows, and turn approved designs into production code.
Colophon ships two halves in one plugin:
- a skill (
skills/colophon/) that discovers DESIGN.md or the legacy design system, guides seeding, and builds UI from its tokens, components, and rationale; and - a canvas extension (
extensions/colophon/) with two canvases:- Colophon canvas: that renders and edits the defined design system live, and
- Prototype canvas: that renders device-framed,
click-through mockups, including
colophon/prototypetools and hooks so the agent has an initialized design system in context.
In a repository you want to design or prototype:
- Install the plugin:
copilot plugin install karkarl/colophon- Reload or restart Copilot so it discovers the plugin.
- Open the Colophon canvas. If the repository has no design system yet, choose Start fresh, Import tokens, or Scan codebase.
- Save the proposal to create
DESIGN.md, companion patterns in.agents/design/, and anAGENTS.mdpointer. Colophon supplies that shared design context when you ask Copilot to build or change UI.
Open the Prototype canvas when you are ready to create or preview a click-through
flow. Commit DESIGN.md, AGENTS.md, and .agents/design/ so the rest of the team
works from the same system.
| Command | What it does |
|---|---|
/colophon |
Opens the Colophon design-system canvas. |
/prototype |
Opens the Prototype canvas for the repository's click-through flow. |
/design-validate |
Validates the design system and reports every error and warning without modifying files. |
/design-scan |
Scans existing UI sources and reports an unsaved design-system proposal with its evidence. |
/prototype-validate |
Validates prototype parsing, navigation, component, and design-token references without modifying files. |
/prototype-export |
Validates and writes a standalone interactive prototype export, or reports why export was blocked. |
The five workflow commands are declarative plugin commands in commands/, so hosts can discover them consistently. /colophon is supplied by the Colophon canvas.
New seeds use root DESIGN.md, companion patterns in .agents/design/, and an
AGENTS.md pointer. Commit all three. Both Markdown documents explain how to use
the optional Colophon extension; reading or editing the design does not require
the plugin.
DESIGN.md combines YAML front matter (colors, named typography, spacing,
and rounded) with authored Markdown rationale. Colophon seeds the standard
Overview, Colors, Typography, Layout, Elevation & Depth, Shapes, Components, and
Do's and Don'ts sections. Token saves preserve the Markdown body, unknown
front-matter fields, and YAML comments; no-op saves preserve the file exactly.
Brace-form token references are resolved in supported token fields and component
style tokens. Placeholders such as {project-name} in names, descriptions, and
custom metadata remain literal text unless explicitly referenced by a token.
Edit prose directly in DESIGN.md; the canvas displays it alongside the tokens.
Colophon-specific metadata lives under x-colophon: { version: 1, tokens: ... }:
brand voice, theme/resource mappings, authority/port targets, shadows, principles,
font-role metadata, and custom canvas pages. Portable token values remain in the
standard groups. This metadata is not part of the upstream standard. Detailed
component trees and prototype flows remain companion JSONC files.
Existing .agents/design/design.json systems keep loading and saving in that
format. Initialization never overwrites or migrates an existing system. If both
DESIGN.md and legacy design.json exist, Colophon reports a conflict and blocks
token saves until the user chooses one authority. Invalid documents are reported
without substituting starter values. A prose-only DESIGN.md is readable guidance,
not permission to invent preview tokens. Canvas saves reject stale document
revisions rather than overwriting external edits.
The YAML parser is bundled with the extension, so installed plugins work offline
without npm install. For development, run npm ci, npm run build:yaml, and
npm test. The pinned parser and its license ship in extensions/colophon/vendor/.
Compatibility targets the upstream alpha format; this is not an implementation
of the upstream CLI's complete lint, import/export, or migration workflow.
The existing internal token contract and legacy files are described below. DESIGN.md-backed systems project to the same canvas model in memory, without writing a second design.json. Inspector JSON paths address this normalized model, not literal YAML source paths.
The component registry and page previews are independent. Open Components in the canvas, hover over or keyboard-focus the Edit pencil beside its title, then activate it to reveal Re-organize components. That button switches to organization mode using the existing Pages sidebar, not a modal. Editing alone does not show the component lists. The pencil is always visible on touch devices. Custom pages use the same edit mode; their title field, description, notes, and Delete page action are also hidden until editing.
The Finish organizing checkmark returns to page editing without losing
changes. Escape cancels an active drag without leaving organization mode.
The page's Finish editing page checkmark hides its editing controls without
discarding changes. Page navigation stays in organization mode; Brand, reload,
and Inspect leave it. Adding a page leaves Inspect and focuses its title field.
Changes appear immediately; Save to repo remains available to persist them.
Saving keeps the current editing mode, including Unassigned; its file-change
notification does not close the organizer.
Definitions stay in .agents/design/components.jsonc for reuse.
The organizer shows the selected page's ordered components. The existing left sidebar lists Components, every custom page, and Unassigned, with counts. Coverage counts show how many registered components have a page, how many are unassigned, and how many have multiple homes. Missing references are also labeled.
- Drag a component onto a different page in the left sidebar to move it, not copy it. The move appends it to that page and keeps the source page selected for further moves. A cross-page move removes that component's other standalone placements; nested uses are unchanged.
- Select a page, then drag above or below a row to set its position. Up and Down offer the same ordering without dragging; Move to works with keyboard and touch.
- Existing duplicates are flagged, not automatically migrated. Choose Keep only on [page] to resolve one in place. Reordering within a page preserves its existing placements elsewhere.
- Move to Unassigned to remove all standalone placements without deleting the definition. New pages start empty. Deleting a page leaves its components unassigned if they have no other page.
The organizer creates or updates the entry whose id is components in
x-colophon.tokens.pages. You can also edit it directly:
x-colophon:
version: 1
tokens:
pages:
- id: components
name: Components
components: [Button, Field, Badge]
- id: publishing
name: Publishing
components: [ExampleScreen, SubtlePicker]Merge these entries into the existing pages array rather than replacing other
metadata or custom pages. Legacy .agents/design/design.json uses the same
pages array at the token document's top level.
The components entry configures the existing built-in page; it does not create
a second page or change its title. Use name: Components to satisfy the existing
page contract. The built-in page has the shared organizer, but not custom-page
title, notes, or deletion controls. Merely viewing it does not create configuration
or mark the document as changed. A move preserves unrelated selections and their
order. Only affected default-all selections are made explicit; there is no
automatic migration when opening the organizer.
- No entry, or an entry without
components: show every registered definition. components: [Name, ...]: show only those previews, in the listed order.components: []: show no previews.
Selections must be arrays of unique, non-empty component names; null is not an
empty selection. Validation rejects malformed selections, duplicate page IDs,
and the reserved brand ID, which cannot be a preview destination,
and warns about names absent from the registry. Missing names also appear as a
warning on the page. Unselected definitions remain available to custom pages,
nested component references, flyouts, Inspect, and editing.
While inspecting, Components temporarily renders every definition for visual
selection and resizing. Leaving Inspect restores the configured previews.
Token saves and reloads preserve the selection in the active document format. Standalone prototype exports (including the artifact used by Publish) preserve the page metadata and full registry; they render prototype screens, not the Colophon page gallery. Filtering the gallery never filters a prototype's components.
| File | What it is |
|---|---|
DESIGN.md (repo root) |
New-system authority: portable tokens, design rationale, and Colophon metadata |
design.json (legacy) |
Tokens: authority, brand, colors, typography, spacing, radii, shadows, principles |
components.jsonc |
A structured element-tree with token-bound Auto Layout — the component patterns your team has agreed on |
principles.md (legacy) |
Prose voice / information hierarchy / do & don't |
These are plain files. Commit them, review them in PRs, edit them by hand or in the canvas.
design.json is the durable contract between people, the canvas, and agents: it names the visual system and tells an agent how that system becomes production UI.
The core design fields—brand, colors, typography, spacing, radii,shadows, and principles—are framework-agnostic design intent. Agents use their names and usage guidance rather than inventing ad-hoc values. components.jsoncdescribes reusable patterns for canvas preview and implementation guidance; it is
not automatically shipping code.
The authority block is the production hand-off. It removes the ambiguity that normally exists when a design-system preview and the app's implementation use different technologies:
| Field | What it tells people and agents |
|---|---|
authority.designSource |
Who owns the design. "self" (the default) means .agents/design/ is the design source of truth. |
authority.port |
The app-wide default production target. Leave it null when no separate implementation needs a port from the design contract. |
authority.port.authoritySource |
What the UI actually ships as, such as Native WinUI 3 / C# or SwiftUI. That implementation wins when it differs from the canvas preview. |
authority.port.syncSource |
The skill, repository, or reference an agent must use to port the design into the production target—for example, microsoft/win-dev-skills. |
authority.port.helperAgent |
An optional specialist agent or skill that performs the port. |
authority.owner / authority.port.owner |
The team or person responsible for keeping the canonical implementation aligned with the design. |
authority.syncProcess |
How the preview examples and production implementation stay in sync, such as a documented XAML-to-design update process. |
authority.portOverrides[] |
Exceptions for a particular area or \components[]\\. An override uses the same port fields, so a chat surface can ship with Reactor while the rest of an app ships natively. |
For example, a native app can keep Colophon as its shared design source while explicitly directing agents to the implementation that ships:
{
"authority": {
"designSource": "self",
"owner": "@contoso/design-systems",
"syncProcess": "Review preview and WinUI changes together in every UI PR.",
"port": {
"authoritySource": "Native WinUI 3 / C#",
"syncSource": "microsoft/win-dev-skills",
"helperAgent": "win-dev-skills",
"owner": "@contoso/windows-ui"
},
"portOverrides": []
}
}With no port or overrides, design.json and components.jsonc remain the framework-agnostic design contract and an agent implements that contract in the repository's chosen production technology. With a port target, the skill, injected context, and managed AGENTS.md pointer tell agents what is canonical, what reference to use, and what not to copy verbatim. This keeps the canvas useful without turning components.jsonc into a competing source of production code.
Open the Design System canvas to see the system rendered live:
- Design system - Brand board, color palette, type scale, spacing/radii/shadows, principles.
- Live component previews —
components.jsoncis rendered by a small pure JSON→DOM interpreter (no framework runtime, works offline) using the system's own tokens, so you see real UI, not just code. Format v3 keeps every object layer relational with a stableid, supports semantic Auto Layout, and adds explicit parent-relative freeform positioning. - Pages sidebar — Brand, Components, and custom pages use the host's generic editor theme: soft gray selected/hover backplates, a small ink selection pill, and ink focus rings, independent of sample or authored preview colors. The page list scrolls independently and keeps the active page visible after navigation and updates; narrow windows use a compact index above the content.
- Inline editing — brand and authority text is read-only until you hover or focus its edit icon and activate it. Touch devices always show the icons. Inspect mode hides these icons; select the text and use Properties or JSON instead. Leaving the field returns to read-only text; Enter (Ctrl/Cmd+Enter for multiline text) or Escape also finishes editing without discarding changes. Change a color/font/brand text and Save to repo writes the active DESIGN.md or legacy design.json. File edits stream back into the canvas via SSE.
- Exact inspection — toggle Inspect to select brand, color, typography, spacing,
radius, shadow, principle, component, or individual rendered component layer by its
exact JSON Pointer path. Edit the object inline, save it to
design.json/components.jsonc, or attach it to chat. - Visual component editing — Inspect opens a synchronized three-pane workspace: component layers on the left, the live canvas in the center, and Properties / JSON on the right. Select layers from the tree or rendered preview, then edit direction, gap, padding, margin, alignment, justification, wrapping, growth, grid columns, and fill/hug sizing through token-backed controls. Spacing combo boxes step through the design-system scale by default; Snapped switches them to free pixel values when a composition needs an intentional exception. Typography pickers preview each design-system text style and family in its own typeface. Color pickers expose the full token palette, an explicit None / transparent choice, and a native spectrum control for a custom override. Appearance controls inherit from the parent/class by default and write sparse overrides for typography, colors, radius, shadow, and text alignment. Drag layers before, after, or inside another element; duplicate/delete layers; and undo/redo before saving. In Inspect mode, drag an absolutely positioned child directly on a freeform preview to update its parent-relative X/Y coordinates live; one undo step is recorded when it is dropped.
- Direct manipulation in Inspect — the floating bottom toolbar contains Move (V), Frame (F), Shape tools (Rectangle R, Ellipse O, Line L, Arrow Shift+L), Text (T), and Add components. The component dropdown searches the current design system and inserts a reusable instance into the selection. Editor-selected buttons stay neutral black; the sample design's accent applies only to the rendered sample. Choose a creation tool, then click on a preview layer for default sizing or drag to draw its bounds; Shift draws a square/circle or snaps a line/arrow to 45-degree increments. Arrows point toward release. Enter inserts into the selected container (or after a selected leaf). Creation returns to Move; Text focuses its content field. Escape cancels. Component references remain in the sidebar's Components picker: click to insert or drag onto a preview. Drag existing layers from the tree: blue destination bounds mean inside, while a line means before/after. Invalid containers, recursive references, cycles, and cross-component moves are rejected before a valid destination is shown. Moving into a flow layout removes obsolete absolute positioning. Selection handles resize width, height, or both; hold Shift on the corner to preserve aspect ratio. W/H fields remain the keyboard alternative. Pending property edits are committed before measuring a resize; invalid edits block the gesture. Text and resize remain separate undo steps. Scale geometry accepts 0.1-10x and changes fixed dimensions and descendant positions, not shared typography, spacing, radius, or effect tokens. Zoom (50-200%) only changes the preview. Properties on left swaps the sidebars; narrow views stack them in one rail. Parent (or Shift+Enter) navigates up the layer tree. Escape cancels a drag or resize, and completed gestures remain draft edits until Save to repo.
The Inspect interaction proposal
documents the Figma research, the implemented scope, and deliberate differences
from a full illustration editor. Editor geometry cues use a selection color
token when supplied by the design system, falling back to its existing accent.
components.jsonc v3 uses stable IDs and token names for semantic layout, with
fixed pixel dimensions reserved for intentional freeform composition:
{
"id": "card-root",
"el": "article",
"class": "ds-card",
"layout": {
"mode": "vertical",
"gap": "2",
"padding": "5",
"align": "stretch",
"width": "fill"
},
"margin": { "top": "2" },
"appearance": {
"textStyle": "heading",
"color": "ink",
"background": "surface",
"radius": "lg"
},
"children": []
}layout.mode accepts vertical, horizontal, grid, freeform, or none. Layout also
supports gap, padding, align, justify, wrap, grow, columns, width,
and height; margin lives on the node. Width and height accept fill, hug, or
non-negative pixel numbers in v3. Spacing values reference keys in
design.json (plus 0 and auto) by default. Unsnapped non-negative numbers are
explicit pixel values. Format v1 remains readable, while v2 and later require
unique stable IDs within each component so canvas selections and future layer moves
remain durable.
Compatible component-reference and default text insertions preserve the document
version. Creating fixed geometry, resizing, or scaling requires an explicit v3
upgrade; these tools never automatically migrate unrelated legacy definitions.
A freeform parent establishes a local coordinate system without flattening the relational tree. Direct children opt into arbitrary placement with finite pixel coordinates:
{
"id": "board",
"el": "section",
"layout": { "mode": "freeform", "width": 640, "height": 400 },
"children": [
{
"id": "card",
"component": "Card",
"position": { "mode": "absolute", "x": 120, "y": 48 },
"layout": { "width": 240, "height": 160 }
}
]
}Omitting position keeps a child in normal flow. Auto Layout drag operations still
reorder or reparent children; freeform movement updates only the child node's
parent-relative x and y.
Both component and prototype nodes support shape: "rectangle" | "ellipse" | "line" | "arrow" for
primitive leaves. Ellipse uses a geometric 50% radius at any aspect ratio,
overriding appearance radii; it is not a pill-radius token. Shape nodes cannot
contain children. Use a frame for grouping.
Lines/arrows store normalized endpoints: { start: { x, y }, end: { x, y } }
in fixed positive-pixel width/height bounds. Stroke color/thickness use
appearance.borderColor / borderWidth. Shared SVG rendering preserves their
direction in both previews, standalone exports, and generated React.
Component-instance appearance overrides update the resolved shape: ellipses keep
their geometric radius, and line/arrow stroke overrides style the SVG rather
than adding a rectangular border. Referenced definitions remain unchanged.
Visual values inherit through normal component classes and the element hierarchy.
An omitted appearance key means inherit; selecting a different value in
Properties writes only that token override. Supported overrides are fontFamily,
textStyle, color, background, borderColor, borderWidth, radius, shadow, and
textAlign. Color properties may also contain an explicit six-digit hex value.
The reserved $none value produces transparent colors, a square radius, or no
shadow without colliding with design-token names.
Border thickness (px) in both component and prototype Properties writes
appearance.borderWidth, a non-negative finite pixel number (fractions allowed).
An explicit thickness draws a solid border using the selected border color, or
the existing CSS color; 0 removes it. Clear the field to remove the width/style
override and restore class or component styling. The same property is supported
in component interaction states and standalone exports.
The thickness field uses the same numberbox and increment/decrement buttons as
Margin, but always edits pixels rather than snapping to spacing tokens. Arrow
keys step by one pixel. Property dropdowns share a single down-chevron icon.
Element and component-reference nodes can optionally declare states and on:
{
"id": "picker",
"el": "button",
"attrs": { "type": "button" },
"appearance": { "background": "$none", "color": "ink" },
"states": {
"hover": { "background": "line" },
"pressed": { "background": "paper" },
"hoverPressed": { "background": "surface" },
"disabled": { "color": "muted" }
},
"control": {
"chrome": "none",
"focus": { "kind": "outline", "color": "accent" }
},
"on": { "click": { "open": "PickerOptions" } },
"children": ["Format"]
}Each state is a sparse appearance override with the same validation, including
existing six-digit hex and $none support. The precedence is base, hover, pressed,
then hoverPressed when both apply. Disabled is exclusive (base plus disabled).
Omitted properties restore their previous inline/class/inherited values. On a
component reference, states merge by state and property over the component root;
an explicit on replaces the root's action, and control merges by field.
Existing definitions without these fields retain their styling.
Interaction metadata (states, on, control, state, when, geometry, and
transitions) is supported only outside SVG
subtrees. Validation rejects it on SVG roots and descendants, including expanded
component references. Put interactions on an HTML button wrapping an SVG icon.
Defining states.disabled does not disable a control. Use attrs.disabled: true
for native controls or attrs["aria-disabled"]: "true". The gallery's State
selector offers Live, Rest, Hover, Pressed, Hover + pressed, and Disabled for
screenshots. It applies to state-bearing nodes in that preview only, never saves
to JSON, and cannot re-enable an authored disabled control. Live follows primary
pointer and button keyboard input. Inspect mode selects/edits layers instead of
opening flyouts.
Entering Inspect clears forced preview states and disables the State selector so
temporary disabled previews do not block layer selection.
Persistent controls and moving parts. Declare literal boolean defaults on a
node with state: { "checked": false } and activate it with
on: { "click": { "toggle": "checked" } }. State is local to that rendered
subtree; a nested declaration starts a new scope. Two instances never share
values, and clicks never modify the JSON. State survives hover, press, forced
preview states, and transient interaction resets. Disposing/re-rendering starts
from defaults, including the gallery's first Inspect entry when it renders the
initial layer selection. Inspect itself never activates a control.
References merge state defaults by name. open and toggle are mutually
exclusive actions, not executable expressions.
Toggle targets are buttons, divs, or spans with a switch (default) or checkbox
role. Supply an accessible label. The runtime binds aria-checked to the named
boolean, makes custom controls keyboard-focusable, and activates on click or
Space release (Enter is also supported). Repeated keydown events do not toggle
repeatedly. Disabled/inert and Inspect controls do not activate. Pointer cancel,
lost capture, removal, focus loss, and window blur clear transient presses
without changing the boolean or replacing the focused DOM element.
Toggle keyboard shortcuts apply only when the toggle itself has focus; editable
or focusable descendants keep their own keyboard behavior.
Children can follow the owner's input and state with sparse conditional rules:
{
"id": "thumb-carrier",
"el": "span",
"geometry": { "translateX": 0 },
"transitions": [
{ "properties": ["translate"], "duration": 367, "easing": [0.1, 0.9, 0.2, 1] }
],
"when": [
{ "state": { "checked": true }, "style": { "geometry": { "translateX": 20 } } },
{ "state": { "checked": false }, "interaction": "pressed", "style": { "background": "muted" } }
]
}when rules require all named booleans to match. Omit interaction for persistent
styling; otherwise use hover, pressed, hoverPressed, or disabled. Precedence is
base, persistent rules, then each active interaction in the existing order with
its matching conditional rules applied immediately after it. Rules at the same
level run in array order; reference rules append to the component's rules.
Disabled keeps persistent styling but excludes hover and press.
Both states entries and when[].style accept the existing flat appearance
fields plus geometry and transitions. Geometry supports finite pixel
translateX/translateY (a translation pair, omitted axes are zero), and
non-negative pixel width/height. CSS's individual translate property keeps
movement independent of resize and editor transforms. The explicit transition
array replaces the legacy 160ms color transitions; an empty array disables them.
Each entry requires properties, non-negative duration in milliseconds, and
easing (linear, ease, ease-in, ease-out, ease-in-out, or a cubic Bezier tuple).
Allowed properties are color, background-color, border-color, border-width,
border-radius, box-shadow, width, height, and translate; no arbitrary CSS or
JavaScript. A state transition array replaces the entire list until that state
ends. Reduced motion suppresses even inline authored transitions.
Passive parts follow their scope's current toggle input; nested interactive
controls retain their own hover and press states. Input cancellation is resolved
for the entire scope before styles are applied, independent of child order.
Inspect's Scale geometry also scales base, state, and conditional pixel
geometry, including translations, but never transition durations or easing.
Width/height controls and resize handles show the effective base geometry size;
editing it stores the new size in layout and removes any competing base
geometry size override. Conditional sizes remain authored overrides unless the
whole component is scaled.
The bundled ToggleSwitch demonstrates 20px travel, 12x12 / 14x14 / 17x14
thumb sizes, 83ms resizing with [0,0,0,1], and 250ms disabled resizing.
Its separate 367ms slide uses [0.1,0.9,0.2,1]: the
WinUI reposition reference
records 3.67s with the test's
0.1 speed ratio.
This is a declarative browser reproduction of the platform reference, not a
runtime dependency on Windows theme APIs.
on.click.open is a literal, exact component name, not a node ID or interpolated
prop. The target renders with its own defaults and design tokens in a non-clipping
flyout. Only one flyout opens at a time; nested flyouts are not supported.
Escape restores trigger focus; outside click and focus leaving dismiss without
stealing focus. Native radio changes select and dismiss. Radio groups are isolated
per component render. Selection is transient: reopening restores defaults and does
not update the trigger's label. These interactions work in the gallery, Prototype
canvas, and self-contained exports; they do not define production framework behavior.
Run node --test extensions/colophon/*.test.mjs for regression coverage. The browser
tests use installed Chrome/Edge without an npm dependency; set COLOPHON_BROWSER
to a Chromium executable on other hosts (otherwise those tests report a skip).
Set COLOPHON_BROWSER_CASES to a comma-separated list such as
gallery,prototype,export,export-reduced-motion to focus the shell integration
test without running unrelated editor scenarios.
control is an opt-in browser chrome reset/focus treatment for native buttons,
textual inputs, textareas, and contenteditable elements. chrome: "none" removes
the browser bevel/border and textarea resize handle, and requires a visible
focus replacement. Focus kind is outline or underline; color must name a
design color token (not a literal color or $none). Text entry uses focus; buttons
use keyboard focus visibility. Forced-colors mode retains a visible focus cue.
Control eligibility is checked after resolving component defaults and authored
reference props, including interpolated input types and contenteditable values.
Overrides that resolve to unsupported controls are rejected.
No global Windows styling or raw-CSS escape hatch is introduced; ordinary Field
and Button patterns are unchanged. Edit these optional fields in the JSON inspector.
If a repo has no .agents/design/ yet, the canvas shows a bundled starter system plus a 3-way onboarding panel (below).
When there's no .agents/design/, choose how to start; refine everything in the canvas after.
| Mode | What it does |
|---|---|
| Start fresh | Bundled starter (the "Northlight" system) or a blank skeleton (grayscale + one accent, system fonts). Writes immediately. |
| Import tokens | Point at a repo-relative .json path or paste token JSON. Adapts our schema, flat {name:hex}, nested Tailwind / Style-Dictionary (colors/fontFamily/spacing/borderRadius), and W3C {$value} tokens. Loads as a proposal to refine, then Save. |
| Scan codebase | Walks the repo's CSS/JSX/styles and extracts colors (prefers named CSS vars), fonts, spacing, radii, shadows — classifying unnamed colors into ink/paper/accent. Loads as a proposal to refine, then Save. |
Starter/scratch seed root DESIGN.md; JSON import/scan load an unsaved proposal with a review bar (Save to repo / Discard). Any first save also scaffolds .agents/design/components.jsonc and adds an idempotent AGENTS.md pointer at the repo root. Existing DESIGN.md files load directly; Markdown import/export and automatic legacy migration are not included.
When it seeds a repository, Colophon idempotently adds a managed AGENTS.md block—creating the file if needed—so agents read DESIGN.md (or the existing legacy system) before UI work. The block explains the optional Colophon workflow and direct-file fallback.
colophonskill — discovers the design authority before UI work, guides safe seeding, and generates UI from its tokens, component patterns, and rationale rather than ad-hoc styles.colophontool — the agent calls it to get the system as text before UI work.init=truescaffolds.agents/design/from the starter;scan=trueproposes one from the repo's existing UI when none exists yet.- Hooks — when a repository contains DESIGN.md or legacy
.agents/design/design.json,onSessionStartannounces it andonUserPromptSubmittedinjects its guidance for UI-related prompts. Parse errors and conflicting sources are surfaced explicitly. Repositories without a system receive no injected design context; use the canvas orcolophontool to seed one.
A second canvas turns the design system into click-through prototypes — so a team can shape a flow by talking to Copilot instead of redlining in Figma, review it visually, then convert a screen to code.
The Screens menu on the left lists screens in document order and highlights the current screen. Choose a screen to navigate, or use Back to return; the menu also follows click-through navigation. It uses the toolbar's neutral styling, independent of the prototype's design tokens and preview theme. Standalone exports use the same menu.
Use the + beside Screens to name a new screen. With no sections, the Sections subtitle is hidden and the section dropdown offers Ungrouped and Add section. Choose Add section to create a group and assign the selected screen to it. The new-screen dialog offers the same option with a section-name field, creating the screen and group together. Once a section exists, the Sections subtitle and its + appear. Use Selected screen's section to move a screen into an existing group or back to Ungrouped. Delete screen asks for confirmation and removes links to that screen and flows that start there. Changes remain drafts until Save in the sidebar or inspector; save errors leave the draft intact. You can delete the last screen and start again. Exports preserve the groups but omit authoring controls.
Groups are stored in prototypes.jsonc as optional sections: [{ "id": "onboarding", "name": "Onboarding" }]; each grouped screen has a sectionId. Section order follows
the array, with ungrouped screens first and document order preserved within each
group. Existing files without sections keep their flat list. Agent patches can use
upsertSection with a section object, then upsertScreen with screen.sectionId.
- Format — prototypes live at
.agents/design/prototypes.jsonc: a framework-agnostic scene graph (layout primitives + references to yourcomponents.jsoncby name + navigation as data), never shipping code. It's pure data, so it renders safely and Copilot can patch a single node by id without rewriting the file. Every save re-emits a stable, key-ordered file plus a Markdown flow outline for painless PR review. - Device frames — preview each screen in web breakpoints, desktop-app windows (Windows/WinUI, macOS), mobile (iPhone/Android), and tablet — selectable, rotatable, with custom sizes and a zoom-to-fit — like Chrome DevTools' device toolbar, but including native app chrome.
- Interactions (v1) — navigate between screens, simple state (toggles, tabs), open/close modals, and visibility bound to state. Click through it live in the canvas, rendered with your real tokens + components in Light/Dark/High-contrast.
- Structured properties — toggle Inspect to select a layer in the preview or
Layers tree. Layers and its history/actions occupy the left sidebar, with
Screens available in a collapsible section; Properties / JSON stay on the
right of the preview. Properties is
the default tab: edit its stable ID, text, image,
component-instance content, layout, dimensions, padding, margin, and sparse
appearance overrides. Typography previews, palette swatches, explicit None,
custom colors, and snapped/free spacing reuse Colophon's token-backed controls.
Instance content changes never modify a component definition. JSON remains
available for exact edits, including navigation and complex props.
Editor buttons and field focus retain neutral chrome. Geometry cues use the
same
selectiontoken as Colophon; preview components and palette swatches retain the design system's own colors. - Shared direct manipulation — Prototype uses the same bottom tool modes, click/drag creation, component picker, validated blue destination outlines/insertion lines, resize handles, geometry scaling, parent navigation, and properties docking as Colophon. Insert frames, rectangles, ellipses, lines, arrows, text, and component instances; drag tree layers into a preview or use the tree for reparenting. A component remains an instance, never a detached definition. W/H resize is distinct from Scale geometry, which changes fixed dimensions and descendant positions while preserving typography, spacing, effects, props, and navigation. Device Fit and 50-200% zoom are view-only. Narrow editors stack the rails. Cancel on Escape, pointer cancellation, lost capture, blur, reload, or leaving Inspect; completed gestures join the existing undo/save history.
- Live local draft — typing previews valid changes immediately. Finishing an
input commits one local edit; Escape cancels its preview. Invalid values show
an error instead of entering the saved document. Changes stay local until
Save writes
prototypes.jsonc; save failures preserve the draft. - History and movement — undo/redo applies to property edits, JSON edits, and layer operations. Duplicate creates fresh IDs; delete removes the selected layer. Drop before/after a tree row to reorder, or inside a layout to reparent. In a freeform layout, drag a directly positioned child in the preview: its parent-relative pixel coordinates change without flattening the tree. Each completed drag is one undoable edit, and Escape cancels an active drag.
- Send to chat — selected elements can be sent to Copilot as context for the next request, including the source, screen, exact JSON Pointer path, draft state, and element payload.
- Convert to code — a first-pass
codegenaction turns the JSONC scene graph and component intent into code for the configured production target. The current web target emits React/JSX usingds-*conventions; a native port target emits a hand-off scaffold and porting notes for WinUI/SwiftUI through the same authority mechanism. prototypetool — Copilot authors and reads prototypes from conversation:actionofread(flow outline),validate(dangling navigation / unknown components or tokens),patch(surgical scene-graph ops),codegen(convert a screen),export(standalone browser artifact), orpublish(explicit GitHub Pages deployment).
| Audience | Share this | What they need |
|---|---|---|
| Designers and developers | Commit .agents/design/ and open a pull request |
Repository access; they can open the Colophon and Prototype canvases in Copilot. |
| Someone continuing the agent work | Share the Copilot agent session from the repository's Agents view | Repository access. A session is useful for context and hand-off, not as a public presentation. |
| Stakeholders and reviewers | Use Export in the Prototype canvas | The generated .agents/design/prototype-export/index.html is a self-contained, interactive file that opens in any modern browser. |
| A broad browser audience | Use Publish in the Prototype canvas | GitHub authentication with repository admin access. Colophon writes only its generated file to gh-pages and opens the resulting GitHub Pages URL. |
| A team adopting the canvas extension | Share the extension as a private GitHub gist | The recipient can install the gist through Copilot, then open it in their own workspace. |
Export is the safe default: it preserves screens, device frames, themes, click-through interactions, and component rendering without a Copilot session, a loopback server, or an internet connection. Publish is intentionally separate and asks for confirmation. It uses the authenticated GitHub CLI/API, never stages or commits the active working tree, and refuses to overwrite a GitHub Pages configuration that is not already based on gh-pages.
Colophon canvas:
inspect_selection— return the exact selecteddesign.jsonorcomponents.jsoncobject and its JSON Pointer path.attach_selection— send that object to chat as context for the user's next request.read— return the current system as a text summary.init— scaffold.agents/design/(non-destructive);mode: "starter" | "scratch".scan— scan existing UI and return a proposed system (text + evidence); writes nothing.validate— schema/parse + component checks; writes nothing.refresh— tell the open canvas to reload from disk.
Prototype canvas:
read/outline— return the Markdown flow outline (screens, nodes, navigation).inspect_selection— return the exact selected JSON element and its JSON Pointer path.attach_selection— send that selection to chat as context for the user's next request.patch— apply surgical scene-graph ops (upsertScreen,setNode,patchNode,setNav, …) and save.validate— dangling navigation targets, unknown component/token references.codegen— convert a screen to code for the configured port target.export— write a self-contained interactiveprototype-export/index.htmlbelow.agents/design/.publish— explicitly export and publish to the repository'sgh-pagesbranch via the authenticated GitHub CLI/API, without touching the active working tree.refresh— tell the open canvas to reload from disk.
plugin.json plugin manifest (skills + extensions)
.github/plugin/marketplace.json makes this repo its own plugin marketplace
skills/colophon/SKILL.md design discovery, seeding, and on-system UI guidance
extensions/colophon/ the canvas extension:
extension.mjs wiring: canvases + tools + hooks + loopback server + file IO
designio.mjs discover / seed / save DESIGN.md or legacy JSON; AGENTS.md pointer; token → CSS vars
designmd.mjs portable front matter adapter; preserve rationale and custom YAML
vendor/ bundled YAML parser and license (no runtime install)
context.mjs UI-intent detection + the summary/context text Copilot receives
sources.mjs seed generators: scratch skeleton, token importer, codebase scanner
renderer.mjs tiny iframe shell (design canvas)
client.js the in-canvas inspector app (onboarding, render, edit, live previews)
property-controls.js / property-controls.css shared token-backed inspector controls
editor-workspace.js / editor-workspace-renderer.mjs shared tools, gestures, component picker and overlays
shape-geometry.js shared shape validation and SVG geometry for rendering/export/codegen
editor-geometry.js shared creation templates and atomic, token-preserving geometry scaling
inspect-workspace.js / proto-workspace.js component and prototype document adapters
inspect-workspace.css shared editor cues, docking and narrow layouts
styles.css canvas chrome + the ds-* component runtime (from tokens)
prototypeio.mjs load / save / surgically patch / validate prototypes.jsonc (scene graph)
proto-render.js in-canvas JSON→DOM interpreter + interaction/state runtime
components-runtime.js browser-only component runtime used by standalone exports
proto-client.js the Prototype canvas app (device frames, inspect/edit/layers, click-through)
proto-properties.js structured properties with caller-owned preview/commit transactions
proto-layout.js shared flat layout, appearance validation, and styling
proto-editor-model.js validated insertion/reparenting for tree and preview drops
proto-renderer.mjs / proto.css prototype iframe shell + device-frame styles
proto-outline.mjs Markdown flow-outline generator
protocodegen.mjs convert a screen to code for the port target
prototypeexport.mjs standalone HTML export writer
pagespublish.mjs explicit GitHub Pages publisher
sample/ bundled starter design system + sample prototypes.jsonc
```
## Notes & limitations (experimental)
- Component and prototype previews render with a pure in-canvas JSON→DOM interpreter, so
they work fully offline — no CDN, no React/Babel.
- Design-system inspection supports token editing plus visual Auto Layout controls,
drag/reparent, duplicate/delete, undo/redo, and exact JSON fallback for
`components.jsonc` layers. Fixed dimensions and parent-relative absolute
positioning are supported; min/max dimensions, responsive variants, and
multi-selection are not yet part of the component schema.
- Prototype layers support structured properties and JSON editing, local live
drafts, undo/redo, duplicate/delete, tree reordering/reparenting, and direct
freeform movement, click/drag creation tools, resize handles, and geometry scaling. The model
stays flat: `layout` is a discriminator such as
`"stack"`, `"row"`, `"grid"`, `"scroll"`, `"freeform"`, or `"none"`; dimensions,
spacing, margin, and `position` live on the node, not a nested layout object.
Standalone exports include rendering and navigation, not authoring controls.
Native `codegen` remains a best-effort hand-off scaffold; the web/React target is deterministic.
- Canvas APIs are an experimental SDK surface and may change.
## Installation and team setup
Colophon is a Copilot plugin **and** its own marketplace, so there are a few ways in.
**Install it as a plugin (recommended — includes the skill and both canvases):**
```bash
copilot plugin install karkarl/colophon
```
**Or register Colophon's marketplace, then install by name:**
```bash
copilot plugin marketplace add karkarl/colophon
copilot plugin install colophon@colophon
```
Restart or reload Copilot after installation, then open the **Colophon** or
**Prototype** canvas in the workspace.
**Configure it for a team:** declare the plugin in the repository's
`.github/copilot/settings.json` through the `enabledPlugins` field and commit that
file. Each teammate also needs repository access and a Copilot environment that
supports plugins. Use `~/.copilot/settings.json` instead to enable it across all of
your own repositories.
**Install only the canvas extension (no skill):** the extension lives in
`extensions/colophon/`. Install that subdirectory into
`~/.copilot/extensions/colophon/`, or point the `install_extension` tool at
`https://github.com/karkarl/colophon/tree/main/extensions/colophon`.
The standalone extension is useful for canvas-only evaluation. Prefer the plugin for
normal use because it also provides the UI-design skill and automatic design-system
context.
## License
MIT — see [LICENSE](./LICENSE).