name: embryo-design-system description: "Apply the Embryo V4 design system when building or modifying HTML/CSS UI. Use this skill whenever the user wants to build a UI, add a component, style a page, or asks about visual design for Embryo, beatviz, or any ZUN Labs project. Also use when the user mentions drum pad, fx slider, glitch effect, monochrome UI, retro UI, or wants the visual style to match the existing apps. Covers: CSS tokens, drum-pad component, fx-slider, header layout, glitch animation, typography (Press Start 2P + monospace), and black-and-white aesthetic with magenta/cyan accents."
Embryo V4 Design System Skill
You are building or modifying a UI that must follow the Embryo V4 design system — a technical, monochromatic aesthetic with retro 8-bit typography, hard edges, and chromatic aberration accents (magenta + cyan).
The reference file is at: C:\embryo-player-v4\design-system\index.html
Read it for the full component showcase before making changes.
Design Philosophy
- Mono-chroma first: black (
#000) and white (#fff) for all structure - Hard edges: minimal border-radius (4px max), strong 1–2px black borders
- Instantaneous feedback: transitions max 0.05s — feel mechanical, not soft
- Retro-technical: monospace for data/labels, Press Start 2P for titles/logos
- Accents only for glitch: magenta (
#ff00c1) and cyan (#00fff9) are reserved for the glitch animation — not for general UI color
CSS Tokens — Always Use These Variables
Paste this :root block in every new HTML file or stylesheet:
@import url('https://fonts.googleapis.com/css2?family=Press+Start+2P&display=swap');
:root {
/* Colors */
--color-bg: #ffffff;
--color-fg: #000000;
--color-accent-1: #ff00c1; /* Magenta — glitch only */
--color-accent-2: #00fff9; /* Cyan — glitch only */
--color-border: #000000;
/* Typography */
--font-main: monospace;
--font-accent: 'Press Start 2P', cursive;
/* Spacing scale */
--space-xs: 4px;
--space-s: 8px;
--space-m: 16px;
--space-l: 32px;
}
body {
background: var(--color-bg);
color: var(--color-fg);
font-family: var(--font-main);
margin: 0;
padding: var(--space-l);
line-height: 1.6;
}
Components
Drum Pad
The core interactive element — 80×80px square, instant feedback on press.
<!-- Empty pad -->
<div class="drum-pad">01</div>
<!-- Pad with sample loaded — shows dot indicator -->
<div class="drum-pad loaded">02</div>
.drum-pad {
width: 80px;
height: 80px;
background: white;
border: 1px solid black;
border-radius: 4px;
display: flex;
justify-content: center;
align-items: center;
font-weight: bold;
cursor: pointer;
transition: all 0.05s;
user-select: none;
}
.drum-pad:active {
background: black;
color: white;
transform: scale(0.95);
}
/* Sample loaded state — thick border + dot indicator */
.drum-pad.loaded {
border-width: 3px;
position: relative;
}
.drum-pad.loaded::after {
content: '';
display: block;
width: 6px;
height: 6px;
background: black;
border-radius: 50%;
position: absolute;
top: 6px;
right: 6px;
}
States to implement in JS:
- Add
.loadedwhen a sample is assigned to the pad - Trigger
:activevisually via CSS — no JS needed for press feedback - For programmatic trigger (MIDI), briefly add/remove a
.triggeredclass with the same inverted style
FX Slider
Horizontal range input styled as a minimal hardware fader.
<div class="fx-panel">
<div class="fx-group">
<label class="fx-label">FLT</label>
<input type="range" class="fx-slider" min="0" max="100" value="70">
</div>
<div class="fx-group">
<label class="fx-label">RES</label>
<input type="range" class="fx-slider" min="0" max="100" value="40">
</div>
<div class="fx-group">
<label class="fx-label">DRV</label>
<input type="range" class="fx-slider" min="0" max="100" value="30">
</div>
<div class="fx-group">
<label class="fx-label">VOL</label>
<input type="range" class="fx-slider" min="0" max="100" value="80">
</div>
</div>
.fx-panel {
display: flex;
justify-content: space-between;
padding: 8px 10px;
border-bottom: 2px solid black;
background: white;
gap: 10px;
}
.fx-group {
display: flex;
flex-direction: column;
align-items: center;
flex: 1;
}
.fx-label {
font-size: 0.8rem;
font-weight: bold;
margin-bottom: 4px;
text-transform: uppercase;
}
.fx-slider {
-webkit-appearance: none;
appearance: none;
width: 100%;
height: 20px;
background: transparent;
cursor: pointer;
}
.fx-slider:focus { outline: none; }
.fx-slider::-webkit-slider-runnable-track {
width: 100%;
height: 4px;
background: #ccc;
border: 1px solid black;
}
.fx-slider::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
height: 16px;
width: 8px;
background: black;
margin-top: -7px;
}
/* Firefox */
.fx-slider::-moz-range-track {
height: 4px;
background: #ccc;
border: 1px solid black;
}
.fx-slider::-moz-range-thumb {
height: 16px;
width: 8px;
background: black;
border: none;
border-radius: 0;
}
Header / Transport Bar
Top bar with load button, BPM counter, and transport controls.
<header class="app-header">
<button class="btn">LOAD</button>
<div class="transport">
<button class="btn btn--square">-</button>
<span class="bpm-display">120</span>
<button class="btn btn--square">+</button>
</div>
<button class="btn">PLAY</button>
</header>
.app-header {
border: 2px solid black;
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px;
background: white;
}
.transport {
display: flex;
gap: 10px;
align-items: center;
}
.bpm-display {
font-weight: bold;
min-width: 3ch;
text-align: center;
}
.btn {
background: white;
border: 1px solid black;
height: 44px;
padding: 0 15px;
font-weight: bold;
font-family: var(--font-main);
cursor: pointer;
transition: all 0.05s;
}
.btn:active {
background: black;
color: white;
}
.btn--square {
width: 44px;
padding: 0;
}
Section / Layout Scaffolding
/* Section container */
.section {
margin-bottom: var(--space-l);
}
/* Section heading — left accent bar */
h2 {
border-left: 4px solid var(--color-fg);
padding-left: var(--space-s);
margin-top: var(--space-l);
text-transform: uppercase;
font-family: var(--font-main);
}
/* Title / logo — accent font */
h1 {
font-family: var(--font-accent);
font-size: 1.5rem;
margin: 0;
}
/* Header divider */
header {
border-bottom: 2px solid var(--color-fg);
margin-bottom: var(--space-l);
padding-bottom: var(--space-m);
}
Glitch Animation
For titles, logos, or any text that needs the chromatic aberration effect.
The element text must be duplicated in content: 'TEXT' in the pseudo-elements.
<div class="glitch-wrapper" data-text="EMBRYO">
<span>EMBRYO</span>
</div>
.glitch-wrapper {
position: relative;
display: inline-block;
}
.glitch-wrapper::before,
.glitch-wrapper::after {
content: attr(data-text); /* reads from data-text attribute */
position: absolute;
top: 0; left: 0;
width: 100%;
height: 100%;
font-weight: bold;
font-size: inherit;
}
.glitch-wrapper::before {
left: 2px;
text-shadow: -2px 0 var(--color-accent-1); /* magenta */
clip-path: inset(44% 0 56% 0);
animation: glitch-anim 5s infinite linear alternate-reverse;
}
.glitch-wrapper::after {
left: -2px;
text-shadow: -2px 0 var(--color-accent-2); /* cyan */
animation: glitch-anim2 1s infinite linear alternate-reverse;
}
@keyframes glitch-anim {
0% { clip-path: inset(80% 0 0 0); transform: translate(-5px, -2px); }
50% { clip-path: inset(15% 0 80% 0); transform: translate(5px, -2px); }
100% { clip-path: inset(62% 0 35% 0); transform: translate(-2px, -2px); }
}
@keyframes glitch-anim2 {
0% { clip-path: inset(10% 0 85% 0); transform: translate(5px, 2px); }
100% { clip-path: inset(15% 0 80% 0); transform: translate(5px, -2px); }
}
Use
data-textattribute instead of hardcodingcontent: 'TEXT'— this way the animation stays in sync if the text changes dynamically.
Typography Rules
| Usage | Font | Size | Weight |
|---|---|---|---|
| App title / logo | Press Start 2P |
1.5rem | normal (font is bold by design) |
| Section headings (h2) | monospace |
default | bold |
| Labels, buttons, BPM | monospace |
default–0.8rem | bold |
| Body / data | monospace |
default | normal |
| Code snippets | monospace |
0.85rem | normal |
Spacing Rules
Always use the token variables, never raw pixel values:
| Token | Value | Use for |
|---|---|---|
--space-xs |
4px | Tight gaps between related items (dot indicator offset) |
--space-s |
8px | Padding inside small elements, label margins |
--space-m |
16px | Standard padding, grid gaps, section spacing |
--space-l |
32px | Section margins, page padding |
New Component Checklist
When adding a new component that doesn't exist in the design system yet:
- Use only
--color-bg/--color-fg/--color-borderfor color — no new colors - Border:
1px solid blackstandard,2pxfor containers/headers - Transition:
all 0.05s— never longer - Active/pressed state: invert bg/fg (
background: black; color: white) - Font:
var(--font-main)for all UI text;var(--font-accent)only for titles - No box-shadow, no gradients, no blur — flat and hard only
When to Read Reference Files
- For the full component showcase in browser: open
C:\embryo-player-v4\design-system\index.html - For TypeScript integration (attaching CSS classes from JS): read
references/ts-integration.md - For a 4x4 pad grid layout: read
references/pad-grid.md