| name | SampleByte | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| colors |
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| typography |
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| rounded |
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| spacing |
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| components |
|
SampleByte uses a macOS-first visual language and should feel like a first-party desktop utility, not a web app that happens to run on the desktop. Read this before touching any UI code.
The reference points are Music.app and Xcode: dark, high-information-density, every control sized and spaced per Apple's HIG. When in doubt, open one of those apps and copy what they do.
Defined in src/index.css @theme {}. Every color used in the app must come from one of these tokens — no raw hex or rgba() values except where a token genuinely does not cover the case (e.g. a one-off rgba(255,255,255,0.015) stripe).
| Token | Value | When to use |
|---|---|---|
base |
#1c1c1e |
Window background. Main content area. |
surface |
#242427 |
Elevated surfaces: toolbar, sidebar, column headers, card headers. One step above base. |
raised |
#2e2e31 |
Controls resting on surface: input fields, button hover fill, segmented control background. |
overlay |
#38383c |
Highest elevation: popovers, context menus, active input fill. |
border |
rgba(255,255,255,0.08) |
Default separator. Sidebar edge, row dividers, input border at rest. |
border-bright |
rgba(255,255,255,0.16) |
Emphasized separator. Active/focused control border. |
ink |
rgba(255,255,255,0.87) |
Primary text. Selected item label, headings, active state text. |
muted |
rgba(255,255,255,0.55) |
Secondary text. Default sidebar item label, placeholder-like info. |
faint |
rgba(255,255,255,0.28) |
Tertiary text. Column headers, kbd hints, timestamps at rest. |
accent |
#FF5500 |
Brand orange. Selection, primary button, drop target, action affordances. |
accent-bright |
#FF7733 |
Hover state for accent elements. |
live |
#FFB300 |
Amber — only what is currently sounding: the playing pad's backlight glow, the playing library row's marks and readouts. Semantically distinct from accent (which means selection/action). One sound plays at a time, so it reads as a single "now playing" signal. |
Selected/active state: use bg-accent/15 for list row selections. This reads as a warm orange tint without being heavy.
Avoid ad hoc text-white or bg-black; use tokens or documented component variants. The primary button is the only current white-text exception and should stay centralized in the button component/token.
Font stack: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', Helvetica, sans-serif (resolves to SF Pro on macOS). Set via --font-family-ui and --font-family-brand (currently identical — no custom web fonts).
Monospace: 'SF Mono', 'Menlo', 'Monaco', 'Courier New', monospace via --font-family-mono. Use for file format badges and any raw mono need.
Readout type (font-readout) — the LCD-style treatment for musical metadata: BPM, key, duration, bars, pad time, pad numbers, pack count. It's mono + tabular-nums + 0.02em tracking, so numbers read like an instrument panel rather than body text. Prefer font-readout over a bare font-mono for these values; it's what gives the data surfaces their identity. When the value belongs to something currently playing, tint it text-live (amber).
| Use | Size class | Weight |
|---|---|---|
| Section header (sidebar) | text-[11px] |
font-semibold |
| Body / list item label | text-[12px] or text-[13px] |
font-normal |
| Column header | text-[11px] |
font-medium |
| Metadata / timestamps | text-[11px] or text-[12px] font-mono |
font-normal |
| Dialog title | text-[13px] |
font-semibold |
| Toolbar brand | text-[13px] |
font-semibold |
| Keyboard hints | text-[10px] font-mono |
font-normal |
Avoid text-xs (Tailwind's 12px) as a shorthand — be explicit with text-[12px] so sizes are intentional and searchable.
Base unit: 4px (Tailwind's default). Standard increments: 4, 8, 12, 16, 20, 24.
| Control | Height |
|---|---|
| Toolbar | h-11 (44px) |
| Small button / segmented segment | h-[26px] |
| Default button | h-[28px] |
| Large button | h-[32px] |
| Search field / small input | h-[26px] |
| Default input | h-8 (32px) |
| Sidebar source list row | h-[28px] |
| Library table row | h-[34px] |
| Column header row | h-8 (32px) |
| Context | Radius |
|---|---|
| Buttons, inputs, tags | rounded-md (6px) |
| Sidebar row selection | rounded-md (6px) |
| Segmented control outer | rounded-[8px] |
| Segmented control inner segment | rounded-[5px] |
| Dialogs / sheets | rounded-xl (12px) |
| Pad grid cells | rounded-lg (8px) |
| Icon containers | rounded-lg (8px) |
src/components/Toolbar.tsx. The entire bar is -webkit-app-region: drag so the window can be moved by dragging anywhere on it. Every interactive element inside must override this with -webkit-app-region: no-drag.
type ElectronStyle = React.CSSProperties & { WebkitAppRegion?: 'drag' | 'no-drag' }
// Drag zone
<div style={{ WebkitAppRegion: 'drag' } as ElectronStyle}>
// Interactive child
<button style={{ WebkitAppRegion: 'no-drag' } as ElectronStyle}>…</button>
</div>Left zone: 72px padding for native traffic lights (set via trafficLightPosition: { x: 16, y: 14 } in electron/main/index.ts). Do not put content in this zone.
Used for Chop/Library/Packs navigation and for any 2–3 option exclusive choice (e.g. source filter: All/Local/Free, pad audition mode, chop method). One primitive — src/components/ui/Segmented.tsx — backs every instance; do not hand-roll a new pill group.
<Segmented value={view} onChange={setView} options={[
{ value: 'chop', label: 'Chop' },
{ value: 'library', label: 'Library' },
]} />- Sizes:
md(26px, top-level tabs — the default) andsm(22px, dense toolbars and sidebar filters). - Tones:
neutral(white-alpha active fill — view/mode toggles, the default) andaccent(orange-tint active — filter-style choices like Freesound Sort/Duration). fullWidthstretches segments to equal parts across a column (the sidebar source filter).- For an option needing custom children (e.g. a ★ marker on the suggested loop length, the dynamic stems list), compose
SegmentedTrack+Pilldirectly instead ofSegmented.
Never use underline tabs or radio-button groups where a segmented control fits.
src/components/AppSidebar.tsx. Background is bg-surface (one step above base). Slides to w-0 opacity-0 when hidden — no collapsed icon-only state.
Section headers:
<p className="px-3 pt-3 pb-1 text-[11px] font-semibold text-faint select-none tracking-wide">
Label
</p>Row items: 28px tall, rounded-md, px-2. Selection: bg-accent/15. No left-border indicator.
Actions on rows (rename, delete, etc.) are revealed on hover via opacity-0 group-hover:opacity-100. Keep them to the right inside the row; use 20×20px touch targets.
| Variant | When to use |
|---|---|
primary |
Main call to action per context. Orange fill, white text. |
outline |
Secondary actions alongside a primary button. |
ghost |
Tertiary/cancel actions. No background until hover. |
danger |
Destructive confirmation. Red text, subtle hover fill. |
Default size is md (28px). Use sm (26px) in dense areas like sidebar footers and toolbar.
Centered sheet (fixed left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2), rounded-xl, max-w-sm. Always include a close button in the top-right corner of the content (not the Radix default — it's been removed from DialogContent). Actions row at the bottom, right-aligned: Cancel (ghost) + Confirm (primary or danger).
Column grid constant: grid-cols-[1fr_64px_52px_44px_140px_52px] — Name (flex), Duration, BPM, Key, Project, Actions. If columns change, update this constant. Column headers in bg-surface, rows alternate with a rgba(255,255,255,0.015) stripe on odd rows. Selected row gets bg-accent/[0.07]; the playing row gets bg-live/[0.07] with amber (text-live / fill-live) play marks and mini-waveform.
src/components/CommandPalette.tsx, mounted once in App.tsx. Opens on ⌘K (or the toolbar ⌘K affordance, via the samplebyte:open-command-palette window event). Keyboard-first: type to filter, ↑/↓ to move, Enter to run, Esc to close. Scope is deliberately small — view navigation plus the file entry points (open audio, import folder). Add a command by appending to the commands array; keep each run a single store/IPC call.
These two carry the hardware-instrument identity; keep them the most crafted objects on screen. The waveform (useWaveSurfer.ts) uses vertical gradients — quiet warm-grey for the unplayed wave, brighter for the played (progress) side. A filled pad rests with a subtle top-light gradient (backlit rubber feel); when it sounds it lights up amber (bg-live/20 + an amber glow shadow). The amber glow is the one bold moment on the pad — keep everything else around it quiet.
titleBarStyle: 'hiddenInset'— native traffic lights overlay the app. Set inelectron/main/index.ts. Do not setframe: false.backgroundColor: '#1c1c1e'— prevents white flash during load.trafficLightPosition: { x: 16, y: 14 }— centers lights in the 44px toolbar.user-select: noneonbody— prevents accidental text selection when clicking around the UI. Re-enabled oninput,textarea,[contenteditable].- Scrollbars — 6px,
rgba(255,255,255,0.12)thumb, no track. Matches macOS overlay scrollbar aesthetic. - Keyboard focus — a global
:focus-visibleoutline (accent, 2px) inindex.csscovers controls that have no other focus affordance. Inputs keep theirfocus:border-accenttreatment. Don't addoutline-noneto a button without giving it another visible focus state. - Reduced motion —
index.cssneutralizes animations and transitions underprefers-reduced-motion: reduce(catches the pad-title marquee, spinners, pad press scale). New motion is covered automatically; no per-component work needed.
- Custom web fonts — DM Sans, Chakra Petch, JetBrains Mono have been removed. System font only.
- ALL CAPS labels — used in the old design. Replaced with mixed-case +
font-semiboldper macOS 13+ conventions. - Left-border active indicators on list rows — replaced with full-row accent fill.
- Card grid layouts for collections — replaced with table/list views. Cards are a web pattern; lists are the macOS pattern.
- Heavy drop shadows — use
shadow-2xl shadow-black/60only on dialogs. No shadows on inline components. - Orange-tinted borders — the old
rgba(255,180,100,0.08)border color. All borders are now neutral white-alpha.