sketch
Static mockups are barely better than screenshots. Every interactive element in a sketch must respond to interaction.
SKILL.md_os/.os-toolboxes/studio/interface_design/skills/sketch/SKILL.md
GitHubGenerated by: PROP-EXT-OM-04-INTERFACE-DESIGN
Making Sketches Feel Alive
Static mockups are barely better than screenshots. Every interactive element in a sketch must respond to interaction.
Required Interactivity
| Element | Must Have |
|---|---|
| Buttons | Click handler with visible feedback (state change, animation, toast) |
| Forms | Input validation on blur, submit handler that shows success state |
| Lists | Add/remove items, empty state, populated state |
| Toggles/switches | Working toggle with visible state change |
| Tabs/nav | Click to switch content |
| Modals/drawers | Open/close with transition |
| Hover states | Every clickable element needs a hover effect |
| Dropdowns | Open/close, item selection |
Transitions
Add transition: all 0.15s ease as a baseline to interactive elements. Subtle motion makes the sketch feel real and helps judge whether the interaction pattern works.
Fake the Backend
If the sketch shows a "Save" button, clicking it should show a brief loading state then a success message. If it shows a search bar, typing should filter hardcoded results. The goal is to feel the full interaction loop, not just see the resting state.
State Cycling
If the sketch has multiple states (empty, loading, populated, error), include buttons to cycle through them. Label each state clearly. This lets the user experience how the design handles different data conditions.
Implementation
Use vanilla JS in inline <script> tags. No frameworks, no build step. Keep it simple:
<script>
// Toggle a panel
document.querySelector('.panel-toggle').addEventListener('click', (e) => {
e.target.closest('.panel').classList.toggle('collapsed');
});
</script>
Shared Theme System
All sketches share a CSS variable theme so design decisions compound across sketches.
Setup
On the first sketch, create .planning/sketches/themes/ with a default theme:
.planning/sketches/
themes/
default.css <- all sketches link to this
001-dashboard-layout/
index.html <- links to ../themes/default.css
Theme File Structure
Each theme defines CSS custom properties only — no component styles, no layout rules. Just the visual vocabulary:
:root {
/* Colors */
--color-bg: #fafafa;
--color-surface: #ffffff;
--color-border: #e5e5e5;
--color-text: #1a1a1a;
--color-text-muted: #6b6b6b;
--color-primary: #2563eb;
--color-primary-hover: #1d4ed8;
--color-accent: #f59e0b;
--color-danger: #ef4444;
--color-success: #22c55e;
/* Typography */
--font-sans: 'Inter', system-ui, sans-serif;
--font-mono: 'JetBrains Mono', monospace;
--text-xs: 0.75rem;
--text-sm: 0.875rem;
--text-base: 1rem;
--text-lg: 1.125rem;
--text-xl: 1.25rem;
--text-2xl: 1.5rem;
--text-3xl: 1.875rem;
/* Spacing */
--space-1: 4px;
--space-2: 8px;
--space-3: 12px;
--space-4: 16px;
--space-6: 24px;
--space-8: 32px;
--space-12: 48px;
/* Shapes */
--radius-sm: 4px;
--radius-md: 8px;
--radius-lg: 12px;
--radius-full: 9999px;
/* Shadows */
--shadow-sm: 0 1px 2px rgba(0,0,0,0.05);
--shadow-md: 0 4px 6px rgba(0,0,0,0.07);
--shadow-lg: 0 10px 15px rgba(0,0,0,0.1);
}
Adapt the default theme to match the mood/direction established during intake. The values above are a starting point — change colors, fonts, spacing, and shapes to match the agreed aesthetic.
Linking
Every sketch links to the theme:
<link rel="stylesheet" href="../themes/default.css">
Creating New Themes
When a sketch reveals an aesthetic fork ("should this feel clinical or warm?"), create both as theme files rather than arguing about it. The user can switch and feel the difference.
Name themes descriptively: midnight.css, warm-minimal.css, brutalist.css.
Theme Switcher
Include in every sketch (part of the sketch toolbar):
<select id="theme-switcher" onchange="document.querySelector('link[href*=themes]').href='../themes/'+this.value+'.css'">
<option value="default">Default</option>
</select>
Dynamically populate options by listing available theme files, or hardcode the known themes.
Sketch Toolbar
Include a small floating toolbar in every sketch. It provides utilities without competing with the actual design.
Implementation
A small <div> fixed to the bottom-right, semi-transparent, expands on hover:
<div id="sketch-tools" style="position:fixed;bottom:12px;right:12px;z-index:9999;font-family:system-ui;font-size:12px;background:rgba(0,0,0,0.7);color:white;padding:8px 12px;border-radius:8px;opacity:0.4;transition:opacity 0.2s;" onmouseenter="this.style.opacity='1'" onmouseleave="this.style.opacity='0.4'">
<!-- Theme switcher -->
<!-- Viewport buttons -->
<!-- Annotation toggle -->
</div>
Components
Theme Switcher
A dropdown that swaps the theme CSS file at runtime:
<select onchange="document.querySelector('link[href*=themes]').href='../themes/'+this.value+'.css'">
<option value="default">Default</option>
</select>
Viewport Preview
Three buttons that constrain the sketch content area to standard widths:
- Phone: 375px
- Tablet: 768px
- Desktop: 1280px (or full width)
Implemented by wrapping sketch content in a container and adjusting its max-width.
Annotation Mode
A toggle that overlays spacing values, color hex codes, and font sizes on hover. Implemented as a JS snippet that reads computed styles and shows them in a tooltip. Helps understand visual decisions without opening dev tools.
Styling
The toolbar should be unobtrusive — small, dark, semi-transparent. It should never compete with the sketch visually. Style it independently of the theme (hardcoded dark background, white text).
Multi-Variant HTML Patterns
Every sketch produces 2-3 variants in the same HTML file. The user switches between them to compare.
Tab-Based Variants
The standard approach: a tab bar at the top of the page, each tab shows a different variant.
<div id="variant-nav" style="position:fixed;top:0;left:0;right:0;z-index:9998;background:var(--color-surface, #fff);border-bottom:1px solid var(--color-border, #e5e5e5);padding:8px 16px;display:flex;gap:8px;font-family:system-ui;">
<button class="variant-tab active" onclick="showVariant('a')">A: Sidebar Layout</button>
<button class="variant-tab" onclick="showVariant('b')">B: Top Nav</button>
<button class="variant-tab" onclick="showVariant('c')">C: Floating Panels</button>
</div>
<div id="variant-a" class="variant active">
<!-- Variant A content -->
</div>
<div id="variant-b" class="variant" style="display:none">
<!-- Variant B content -->
</div>
<div id="variant-c" class="variant" style="display:none">
<!-- Variant C content -->
</div>
<script>
function showVariant(id) {
document.querySelectorAll('.variant').forEach(v => v.style.display = 'none');
document.querySelectorAll('.variant-tab').forEach(t => t.classList.remove('active'));
document.getElementById('variant-' + id).style.display = 'block';
event.target.classList.add('active');
}
</script>
Add padding-top to the body to account for the fixed tab bar.
Marking the Winner
After the user picks a direction, add a visual indicator to the winning tab:
<button class="variant-tab active">A: Sidebar Layout ★ Selected</button>
Keep all variants visible and navigable — the winner is highlighted, not the only option.
Side-by-Side (for small variants)
When comparing small elements (button styles, card layouts, icon treatments), render them next to each other with labels rather than using tabs:
<div style="display:grid;grid-template-columns:repeat(3,1fr);gap:24px;padding:24px;">
<div>
<h3>A: Rounded</h3>
<!-- variant content -->
</div>
<div>
<h3>B: Sharp</h3>
<!-- variant content -->
</div>
<div>
<h3>C: Pill</h3>
<!-- variant content -->
</div>
</div>
Variant Count
- First round (dramatic): 2-3 meaningfully different approaches
- Refinement rounds: 2-3 subtle variations within the chosen direction
- Never more than 4 — more than that overwhelms. If there are 5+ options, narrow before showing.
Synthesis Variants
When the user cherry-picks elements across variants, create a new variant tab labeled descriptively:
<button class="variant-tab" onclick="showVariant('synth1')">Synthesis: A's layout + C's palette</button>