Authoring Surfaces
The mental model
A surface is a low-fidelity wireframe β a structural sketch of a UI screen or component authored directly in your spec using a restricted Pug DSL. It answers: βWhat does this screen contain, how is it laid out, and what can the user interact with?β
Surfaces are intentionally lo-fi. They communicate structure and interaction points, not visual polish. Think of them as a whiteboard sketch β precise enough to implement from, abstract enough to leave room for design decisions.
The Pug DSL
Surfaces use a subset of Pug compiled into themed HTML components. The vocabulary covers:
- Layout:
stack,row,grid,card,section,sidebar - Forms:
input,select,checkbox,radio,textarea,switch - Actions:
button,link; Navigation:tabs,nav,breadcrumb - Display:
table,list,badge,avatar,icon; Typography:heading,text
No mixins, includes, or JavaScript logic are allowed. The DSL is deliberately constrained to keep wireframes focused on structure.
Linking surfaces to features
The feature attribute connects a surface to the feature or flow it visualizes:
{% surface id="add-bookmark-form" title="Add Bookmark" feature="feature/add-bookmark" %}This creates bidirectional cross-references: the feature page links to its surfaces, and the surface page links back to its feature.
Surface types
The type attribute classifies the surface:
- screen β a full page layout
- widget β a self-contained component embedded in larger screens
- atomic β a primitive component (button variant, input state)
Choose the type that matches the scope of what you are designing. Most authored surfaces are screens or widgets.
Grouping with tabs
When a feature has multiple related views (e.g., desktop vs. mobile, empty state vs. populated), use the group attribute to render them together in a tabbed view:
{% surface id="bookmarks-empty" group="bookmarks-list" label="Empty State" ... %}{% surface id="bookmarks-populated" group="bookmarks-list" label="With Items" ... %}Designing effective wireframes
Start with content hierarchy. What is the most important thing on this screen? Place it first in the structure.
Use real-ish content. Instead of βLorem ipsumβ, use plausible values: βhttps://example.com/articleβ, βMeeting Notes - Q2 Planningβ. This reveals layout issues that placeholder text hides.
Show states. A form has empty, filled, error, and success states. Create surfaces for each meaningful state, grouped together.
Keep it structural. Resist the urge to specify colors, fonts, or pixel dimensions. The Pug DSL intentionally omits these β they are design system concerns, not specification concerns.
Common mistakes
| Mistake | Why it hurts |
|---|---|
| High-fidelity design | Surfaces are wireframes, not mockups. Specifying exact colors, spacing in pixels, or font weights defeats the purpose. |
| Orphaned surfaces | A surface without a feature attribute is disconnected from the spec graph. Always link to the feature or flow it serves. |
| JavaScript logic in Pug | The DSL is static structure only. Conditionals and loops are not supported β show different states as separate grouped surfaces. |
| Duplicating flow sequence | A surface shows one screen state, not a multi-step flow. Use multiple grouped surfaces for multi-step UIs, one per step. |
| Ignoring component vocabulary | The DSL has specific component names. Using HTML tags (div, span) will not compile. Learn the vocabulary from the Component Gallery. |
| Skipping error states | Showing only the happy-path UI leaves developers guessing about error presentation. Add surfaces for validation errors and empty states. |
The Component Gallery
All available DSL components with live interactive examples are in the Component Gallery at /showroom. Consult it for the exact attribute vocabulary of each component.
Evolution
Surfaces evolve alongside features. When requirements change, update surfaces to reflect new interaction points. Because surfaces are low-fidelity, changes are lightweight β restructuring a wireframe is minutes of work, not hours.