Style book
Every reusable piece of Bytestream, rendered once, in the tokens the build uses. The rules behind each one live in design/patterns.md; this page is the picture. Match these before inventing.
Tokens
Six colours and nothing else. Every translucent value is one of void, now or paper pulled back. No purple, no third accent, no hex codes in the interface.
src/ui/tokens.ts · nowAlpha() paperAlpha() voidAlpha() are functions on the palette, not new colours.
Type
Two faces. Barlow Condensed 600 is the brand voice: wordmark, on-picture names, tile names, section labels. It carries Latin only, so anything set in it answers for names it cannot spell. Inter for everything else. Times in tabular numerals.
Spacing & radius
A 4-point scale. Screen gutter 12 phone, 24 desktop. Rail card gap 12. Radii: pill 4, card 6, frame 8, stage 10, panel 12.
Motion & focus
Sparingly. Tab switches cut. Rail selection border fades 120 ms. Sheets settle in 220 ms ease-out-cubic. The tuning pulse (1.2 s) is the only loop. No scroll reveals. Focus ring: 2px now, offset 2, keyboard only; text fields warm their border instead.
Sidebar & tab bar
One product, two frames. Desktop (≥768): a 248 sidebar, wordmark, then Search with its ⌘K hint, then Watch / Guide / Atlas / Saved. Active row: now label and a 3px bar on the left edge. Phone: a 56 tab bar, glyph over label, now with a 2px underline.
src/ui/Chrome.tsx · Wordmark.tsx
Destination glyphs
16px in both frames. Saved is the one glyph that changes shape when you stand in it, because it is the save control: the heart fills when the channel is yours. Sidebar, tab bar and the overlay's save toggle are one symbol in two states.
Screen tabs
Tabs across the top of a screen with more than one face (the Studio's Channel / Control room). now label with a 2px underline on the active one, paperDim otherwise, a hairline under the row.
Frame & overlay
16:9, radius 8, studio when there is no picture. Controls sit on the picture in a bottom-anchored overlay over a soft void gradient: channel name in the condensed face, then Programme 21:00–21:30 · Country · Category · Next … with the LIVE pill right-aligned, then the progress line, then the control row. One shape, two scales, everywhere. Auto-hides; a pointer move brings it back.
src/ui/Overlay.tsx · Transport.tsx · ProgressBar.tsx · LivePill.tsx
Transport
Left cluster: previous channel, back 15, the amber play disc, forward 15, next channel. Right: save heart (desktop), speaker, 72px volume line (desktop), CC, masks, lamp, fullscreen. Desktop glyphs 18 with a 28 disc; phone 20 with a 36 disc. Mute is on every layout. No control is ever removed to make something fit.
Masks amber = theater on. Lamp filled = ambience on, never a second amber.
State patterns
Tuning: the frame stays studio, a 2px now hairline pulses across the bottom of the picture, inside the frame, in front of the video, out of the layout flow. Off air and unplayable: one true line in paperDim, centred. Empty catalog: a title and one retry link in now. The progress line goes bare in all three.
Pills
LIVE: live fill, paper text, 12/700, radius 4, 4×8, only while the stream is playing. Rewound more than 10 s it becomes the outline Back to live. The amber outline pill marks a state in now (Looping, recall). A hairline pill is quiet fact (Locked, No listings).
Rail card
176 desktop / 140 phone. 16:9 tile in studio with the logo centred at 60% width, or the name in the brand face when there is no logo (then no caption: the name is already the label). Selected: 2px now border, name in now, and on web the logo swaps for a captured live frame. Hover: name in paper.
src/ui/RailCard.tsx · ChannelTile.tsx · ChannelNameMark.tsx
Guide row
Tile, channel name, programme title, time range in tabular numerals, amber programme progress. The playing row on desktop fills now with void text. A channel the guide does not carry (only while a query narrows the screen) shows No listings, no time range and no bar: the absence is the distinction.
src/ui/ChannelRow.tsx · MetaLine.tsx
Row highlight
One row language for Spotlight results and picker options. Rows bleed to the panel's edges. Hover is rowHover (neutral); active is rowActive with the title in now (warm). Counts sit in a fixed 56px right column, tabular.
List handle
One 36×4 pill, fully round, paper .28, no chevron, no label. Three rests and nothing between them: default, up, down. Drag walks neighbours; release snaps; tap steps. Hover paper .6 on pointer. 220 ms ease-out-cubic on every breakpoint. e / Shift+E.
rest · hover · src/ui/ListHandle.tsx
Inputs & chips
One field pattern, variants. field wears its box always; inline (the trailing filter on a chip row, desktop) is bare at rest and puts the chip's clothes on only while focused or holding text. Focus warms the border to focus; no ring on text fields. Chips are 36 tall; a set chip turns now and grows a clear button that never opens the picker.
src/ui/SearchField.tsx · FilterChip.tsx · FilterPanel.tsx · PickerSheet.tsx
Switch
The one non-text control of the input family. now track on, hairline off, paper thumb, no hover response. Same three colours, no fourth.
Poster tile (Studio)
16:9, radius 8, the encode's still. A one-line state under the caption: amber dot for on air and for anything in motion (encoding, uploading, pulsing), paper dot for in review, none for rejected. In-motion tiles keep the still behind a void veil with the state on top and the meter along the bottom edge. Rejected sits at half opacity.
or choose a file
Readouts & meters
A readout is a value you copy, not type: void box, hairline, tabular, with its one action in now at the right; long values ellipsise, the action never wraps. A big number is the condensed face at 44 with its unit in paperDim; the 2px meter under it fills now.
Timeline sheet · parked 2026-09-22
Built for the Studio channel home and taken off it the same day; kept to return to. A bottom-docked sheet on the list handle carrying the loop strip: every segment of the N00bstream loop sized to runtime with its still, the creator's own outlined in now, a now-line at wall-clock, ghost segments for videos still in review or encoding, a dashed amber slot for the next upload, a tabular axis, and the unlock meter beneath. Headline Your timeline. Working component with the rests live: component-timeline-sheet.html.
video here
Open question for its return: a visitor's strip carries titles and creators only; yours, starts, the lock and the slot are owner facts.
Usage
The style book measured against Laws of UX (Jon Yablonski), fetched 2026-09-22. For each law: what Bytestream already does, then the rule to keep or the place it falls short. Cognitive Bias is the umbrella the others sit under and is not a rule of its own here.
| Law | In Bytestream | Keep / fix |
|---|---|---|
| Aesthetic-Usability Effect | A dark room with film grain, one warm light, real stills in every tile. The player is the product and everything else is a rail under it. | Grain and warmth earn trust only while the picture plays. An empty studio tile with a type-only name is where the effect runs out; keep stills first, logos second, type last. |
| Choice Overload | 9,461 playable channels never appear as one list. Shelves cut by category, country and language; Spotlight answers a query; the Guide shows only what is on now. | The Categories index and Atlas are the two places a viewer meets the whole catalog at once. Both need a first row that decides for them (starred countries, seeded channels). |
| Chunking | Four destinations. The overlay is three lines: name, meta, controls. The timeline strip breaks a 41-minute loop into nine titled segments sized to runtime. | A shelf row is the chunk; a shelf longer than a screen should end with a Show-all card, not run on. |
| Cognitive Load | No hex codes, no jargon in the interface. Times as 21:00–21:30. One control surface for the player, not two. One row language for every list. | The OBS server and key readouts are the heaviest thing in the Studio; they stay in a box with a Copy action so nothing has to be read, only pressed. |
| Doherty Threshold | Tune-in is measured, not guessed: one YouTube player reused, HLS remembers the working stream, the next host is preconnected. Sheets settle in 220 ms. The tuning pulse says something is happening within the first frame. | Anything over 400 ms shows a state, never a blank. Paused holds the last frame. A reload is not a stream failure. |
| Fitts's Law | The list handle draws 36×4 but its target is 48×36. Phone transport glyphs are 20 with a 36 disc and 8 gaps, inside a 320 handset. Chips are 36 tall. | Desktop transport is ten 18 px glyphs at 14 px gaps; the save heart gets 28 px of clearance from the speaker so a mute press cannot catch it. Any new control on that row must keep that gap. |
| Flow | Theater clears the chrome. The overlay hides itself; a pointer move brings it back. Arrow keys walk the rail; Enter selects; Space toggles play. Channel changes never re-mount the picture. | A route change exits theater and the stream never re-tunes on a layout change. Keep it that way: every Watch state is a layout state, never a route. |
| Goal-Gradient Effect | The Studio's unlock meter: 17:40 of 30:00 approved, with the remainder spelled out and the reward named (bytestream.tv/jake). The loop strip's dashed slot reads Your next video here. | The meter moves only on approval, so a creator with three videos in review sees no progress. Show the pending minutes on the meter in a ghost fill. |
| Hick's Law | Four destinations, one search. The picker is a searchable list, not a grid of every country. A tap on a rail card tunes; there is no second confirm. | The desktop transport row is the one place with ten choices side by side. It survives because every glyph is a standard: no invented icon lives there. |
| Jakob's Law | ⌘K opens search. A 16:9 frame with a bottom overlay and a crimson LIVE pill is what every player looks like. The Studio is a channel home with tabs, like the sites creators already use. Stream server + key is the OBS convention. | The comedy/tragedy masks for theater are ours alone. They read only because they sit where the widescreen icon usually sits and turn amber when on. |
| Law of Common Region | Panels: the overlay's void gradient, the studio-toned cards, the stage band under the picture, the popover with its own border. Rows bleed to the panel's edges so a highlight is a row, not a stripe. | Spotlight's field has no box of its own because the panel's border already draws the region. Never put a box inside a box. |
| Law of Proximity | The LIVE pill sits on the programme line, above the progress bar it describes. The save heart rides the title line on a phone, away from mute. The clear button lives inside its chip. | The unlock meter sits directly under the number it measures; move one and move both. |
| Law of Prägnanz | The handle is a bare pill: it goes both ways, so it carries no arrow. A progress line with nothing to report draws the empty track, not a made-up position. The wordmark is the word alone. | The chevron on the handle lasted one afternoon. Resist adding a glyph to say what the shape already says. |
| Law of Similarity | One row language for Spotlight and the picker. One rail card. Sidebar row, tab bar item and overlay save toggle share the heart in the same two states. Every field is one pattern with variants. | A new list must reuse ChannelRow or the row highlight; a new switch must be the SettingsSheet switch. |
| Law of Uniform Connectedness | The progress line and its head are one amber stroke. The tuning hairline follows the frame's own radius. The now-line on the timeline runs from the axis through the strip. | The desktop playing Guide row fills amber end to end, tile to time: one connected object, not a highlighted name. |
| Mental Model | It is a television, not a dashboard: channels, tonight, LIVE, off air. The N00bstream is a channel you tune into, so a creator's video 'airs' rather than 'publishes'. | The word 'Off air' is a claim about the broadcast, so the Guide says 'No listings' when it is the guide that is empty. Keep the two vocabularies apart. |
| Miller's Law | Four destinations, one search. Nine segments on a loop strip. The transport's left cluster is five. | The desktop transport's right cluster is seven; it holds because it is two groups (sound, picture) with a gap between. |
| Occam's Razor | Theater and the phone lost their own transport layouts; there is one. The desktop sheet and the phone sheet share one clock. The lamp is web-only rather than present-and-inert. | The Studio channel home and control room both carry upload and go-live. One of them should own the action and the other should link to it. |
| Paradox of the Active User | No onboarding. 'Tap for sound' appears where the tap is needed and dismisses on first tap. The OBS box says exactly where to paste. Any common video format is accepted. | Every keyboard shortcut also has a visible control; keys are a bonus, never the only door. |
| Pareto Principle | 329 channels carry a programme guide against 9,461 playable, and they lead every shelf. The starred countries lead Atlas. Recommendations open with Top picks. | When a query narrows the Guide, rows with a programme still come first and the quiet rows gather in a tail. |
| Parkinson's Law | A run of the pipeline is bounded: encoding 'takes a few minutes', a reviewer 'then' puts it on air, and the state line says which step it is on. | Give every waiting state a time shape, even a rough one; an unbounded 'Processing' invites the creator to keep refreshing. |
| Peak-End Rule | The peak is the first frame of a channel: the tuning pulse, then the picture, then LIVE lights. The end is never a black rectangle: paused holds a frame, off air says so and moves on after a second. | The Studio's peak is the first approval. Make it visible: the video's state turns amber and the loop strip gains a segment. |
| Postel's Law | Search matches on name, country, category and language. Uploads accept any common format and normalise through Mux. The relay answers for sources that will not play in a browser with the true reason. | The condensed face is Latin-only, so a name it cannot spell falls to Inter in the tile. Accept every script; send one clean line back. |
| Selective Attention | Amber means now and appears once per screen. Crimson means LIVE and appears once. Everything else is paper, dim or hairline, so the eye finds the one warm thing. | A second amber on a screen is a bug. The lamp is filled paper when on precisely so it is never mistaken for the masks. |
| Serial Position Effect | Search leads the sidebar because it is how you get anywhere in 9,000 channels; Saved ends it. Seeded channels lead a shelf; the Show-all card ends it. The playing channel's row leads the Guide. | The loop strip's first segment is the one a visitor reads; the creator's next-video slot is last on purpose, it is the invitation. |
| Tesler's Law | Going live from OBS needs a server and a key; that cannot be reduced, so it is presented as two copyable readouts and one sentence. Rights attestation on upload is one screen, once. | Do not hide irreducible steps behind 'advanced'. Put them in a box, label them plainly, give each one action. |
| Von Restorff Effect | LIVE is the only crimson thing in the app. The selected rail card is the only amber border. The creator's own segments are the only outlined ones on the timeline strip. | Because these are the only differences, they carry meaning. A decorative border or a third accent would dilute all three at once. |
| Working Memory | The recall pill remembers the last channel. The channel picker keeps its scroll. A tuned channel survives a reload. Filter chips show what is set without opening the picker. | A creator's state lines (In review, Encoding, Uploading 412 MB of 660 MB) are the memory; never collapse them to a single 'Processing'. |
| Zeigarnik Effect | Uploading and encoding tiles keep the still behind a veil with the state on top and a meter along the edge. The unlock meter is unfinished by design. The dashed slot on the timeline is an open task. | The activity log in the control room closes the loop: every started thing gets a finished line, so nothing stays open in the creator's head. |
Five to act on: the desktop transport row is at the edge of Fitts and Hick; pending minutes should show on the unlock meter (Goal-Gradient); upload and go-live live in two places (Occam); waiting states need a time shape (Parkinson); the first approval needs a visible peak (Peak-End).