{ "schemaVersion": 2, "generatedAt": "2026-08-29T00:00:00Z", "title": "Design System: Media Centre", "extensions": { "colorMeta": { "tt-black": { "role": "neutral", "displayName": "Pure Black", "canonical": "#000000", "purpose": "The ground, everywhere and unconditionally. There is no second surface colour, because there are no surfaces.", "tonalRamp": ["#000000", "#0d0d0d", "#1a1a1a", "#262626", "#333333", "#404040", "#4d4d4d", "#595959"] }, "tt-white": { "role": "neutral", "displayName": "Transmission White", "canonical": "#ffffff", "purpose": "Body text, row titles, table row headers. The default reading colour.", "tonalRamp": ["#262626", "#404040", "#595959", "#737373", "#8c8c8c", "#a6a6a6", "#d9d9d9", "#ffffff"] }, "tt-yellow": { "role": "primary", "displayName": "Service Yellow", "canonical": "#ffff00", "purpose": "The service speaking in its own voice: mastheads, page numbers, section-band titles, focus reversal, and the warn tier on any threshold-coloured value.", "tonalRamp": ["#333300", "#4d4d00", "#666600", "#999900", "#cccc00", "#ffff00", "#ffff66", "#ffffcc"] }, "tt-cyan": { "role": "primary", "displayName": "Transmission Cyan", "canonical": "#00ffff", "purpose": "Live values and now-showing: the header clock, active figures, links, the currently-playing title. Cyan means this number is moving.", "tonalRamp": ["#003333", "#004d4d", "#006666", "#009999", "#00cccc", "#00ffff", "#66ffff", "#ccffff"] }, "tt-green": { "role": "secondary", "displayName": "Signal Green", "canonical": "#00ff00", "purpose": "Running, healthy, available. ON AIR rows, usage under 75%, healthy progress bars, the confirmation toast.", "tonalRamp": ["#003300", "#004d00", "#006600", "#009900", "#00cc00", "#00ff00", "#66ff66", "#ccffcc"] }, "tt-red": { "role": "secondary", "displayName": "Off-Air Red", "canonical": "#ff0000", "purpose": "Off air, failed, danger. Degraded-service copy, OFF AIR rows, usage at or above 90%, disk temperature at or above 55C, the armed state of a destructive button.", "tonalRamp": ["#330000", "#4d0000", "#660000", "#990000", "#cc0000", "#ff0000", "#ff6666", "#ffcccc"] }, "tt-magenta": { "role": "secondary", "displayName": "Request Magenta", "canonical": "#ff00ff", "purpose": "Waiting on a human. Ombi's pending count and requested events, and nothing else. Its rarity is the point.", "tonalRamp": ["#330033", "#4d004d", "#660066", "#990099", "#cc00cc", "#ff00ff", "#ff66ff", "#ffccff"] }, "tt-blue": { "role": "tertiary", "displayName": "Field Blue", "canonical": "#0000ff", "purpose": "The only colour used as a background rather than as ink. Draws the full-bleed section band and the table-row hover. It never sets text.", "tonalRamp": ["#000033", "#00004d", "#000066", "#000099", "#0000cc", "#0000ff", "#6666ff", "#ccccff"] }, "ink-dim": { "role": "neutral", "displayName": "Dim Ink", "canonical": "color-mix(in srgb, #ffffff 58%, #000000)", "purpose": "The only dimmed value permitted to carry text; at 58% it clears 4.5:1 on black. Secondary row copy, labels, timestamps, the flat trend arrow.", "tonalRamp": ["#1f1f1f", "#333333", "#4d4d4d", "#666666", "#808080", "#949494", "#b3b3b3", "#d9d9d9"] }, "ink-dimmer": { "role": "neutral", "displayName": "Dither Grey", "canonical": "color-mix(in srgb, #ffffff 30%, #000000)", "purpose": "Graphics only. Bar troughs, the mosaic separator, the empty-artwork field, the disabled-button fill, scrollbar thumbs. At 30% it fails text contrast outright.", "tonalRamp": ["#0d0d0d", "#1a1a1a", "#262626", "#333333", "#404040", "#4d4d4d", "#737373", "#a6a6a6"] } }, "typographyMeta": { "double-height": { "displayName": "Double Height", "purpose": "The only size other than body text. Page mastheads, the now-showing title, and the big figures where the number is the content. Set in DotGothic16." }, "double-height-mobile": { "displayName": "Double Height (phone)", "purpose": "Below 760px the double-height row steps to 1.375rem, because a true 30px row eats a phone viewport." }, "body": { "displayName": "Body", "purpose": "Everything else, at one size and one weight, with tabular figures so columns line up on the character grid." }, "label": { "displayName": "Label", "purpose": "Row names, figure captions, buttons, fastext keys, table headers. Letterspacing and case do the work a smaller size would do elsewhere." }, "field-title": { "displayName": "Field Title", "purpose": "The widest tracking in the system (0.22em), reserved for the section colour band and the header row's service name." }, "input": { "displayName": "Input", "purpose": "The one hardcoded pixel size in the codebase. 16px keeps iOS Safari from zooming the page on focus, which would break the character grid mid-interaction." } }, "shadows": [], "shadowPhilosophy": "There are no shadows. The codebase contains zero box-shadow declarations and zero border-radius declarations; the grep returning nothing is the audit test for the whole system. Depth is emissive, not cast: full-saturation colour on pure black reads as lit, a region comes forward by reversing (coloured ground, black text) rather than by lifting, and the single text-shadow in the system is a phosphor bloom at 55% of the element's own colour, spent only on mastheads and the now-showing title.", "motion": [ { "name": "tt-transmit", "value": "tt-in 0.18s steps(2, end) both", "purpose": "Page transmission. Sections fade in in sequence on load, in stepped rather than smooth opacity, because a teletext page did not appear, it was painted in a few rows at a time." }, { "name": "tt-transmit-stagger", "value": "0.05s per section, capped at 0.35s from the eighth child on", "purpose": "The delay ramp across .tt-page children that produces the row-by-row paint." }, { "name": "toast-slide", "value": "opacity 0.15s, transform 0.15s", "purpose": "The only other authored motion: the bottom confirmation strip slides up 100% and fades in." }, { "name": "reduced-motion", "value": "@media (prefers-reduced-motion: reduce) { .tt-page > * { animation: none } * { transition-duration: 0.01ms !important } }", "purpose": "Both moves are removed outright under reduced motion; content is visible by default, so nothing is lost." } ], "breakpoints": [ { "name": "page", "value": "92ch", "purpose": "The measure. A teletext page was 40 columns; a screen this wide gets double that." }, { "name": "md", "value": "760px", "purpose": "Double-height steps to 1.375rem, fastext goes to two columns, the bar ruler narrows from 20 cells to 14, the gutter goes to 1rem." }, { "name": "sm", "value": "560px", "purpose": "A list row wraps its description to a full-width third line; the header clock takes its own line; search stacks." } ] }, "components": [ { "name": "Primary Button", "kind": "button", "refersTo": "button-primary", "description": "A reversed row of cells. The entire button vocabulary: no fill variants, no radius, no border.", "html": "", "css": ".ds-btn { display: inline-flex; align-items: center; justify-content: center; min-height: 44px; padding: 0 1.5ch; background: var(--voice, #ffff00); color: var(--tt-black, #000); border: none; border-radius: 0; font-family: var(--font, 'Chivo Mono', ui-monospace, Menlo, monospace); font-size: var(--fs, 0.9375rem); line-height: 1.45; letter-spacing: 0.16em; text-transform: uppercase; cursor: pointer; } .ds-btn:hover { background: var(--live, #00ffff); } .ds-btn:focus-visible { outline: none; background: var(--live, #00ffff); color: var(--tt-black, #000); } .ds-btn:active { background: var(--ok, #00ff00); } .ds-btn:disabled { background: var(--ink-dimmer, #4d4d4d); color: var(--ink-dim, #949494); cursor: default; }" }, { "name": "Armed Button (two-tap confirm)", "kind": "button", "refersTo": "button-primary-armed", "description": "A destructive action arms on first press and states its own stage rather than opening a dialog. Red armed, green when done.", "html": " ", "css": ".ds-btn-armed { display: inline-flex; align-items: center; justify-content: center; min-height: 44px; padding: 0 1.5ch; background: var(--alert, #ff0000); color: var(--tt-white, #fff); border: none; border-radius: 0; font-family: var(--font, 'Chivo Mono', ui-monospace, Menlo, monospace); font-size: var(--fs, 0.9375rem); line-height: 1.45; letter-spacing: 0.16em; text-transform: uppercase; cursor: pointer; } .ds-btn-armed:hover { background: var(--live, #00ffff); color: var(--tt-black, #000); } .ds-btn-armed:focus-visible { outline: none; background: var(--voice, #ffff00); color: var(--tt-black, #000); } .ds-btn-armed.ds-done { background: transparent; color: var(--ok, #00ff00); cursor: default; }" }, { "name": "Quiet Button", "kind": "button", "refersTo": "button-quiet", "description": "The secondary action. Transparent until touched, then it reverses like everything else.", "html": "", "css": ".ds-btn-quiet { display: inline-flex; align-items: center; justify-content: center; min-height: 44px; padding: 0 1ch; background: transparent; color: var(--live, #00ffff); border: none; border-radius: 0; font-family: var(--font, 'Chivo Mono', ui-monospace, Menlo, monospace); font-size: var(--fs, 0.9375rem); line-height: 1.45; letter-spacing: 0.16em; text-transform: uppercase; cursor: pointer; } .ds-btn-quiet:hover { background: var(--live, #00ffff); color: var(--tt-black, #000); } .ds-btn-quiet:focus-visible { outline: none; background: var(--voice, #ffff00); color: var(--tt-black, #000); }" }, { "name": "Text Field", "kind": "input", "refersTo": "input-field", "description": "No border, no box. The field is marked by an underscore run pinned to its bottom edge, which turns yellow on focus. 16px type stops iOS zooming on focus.", "html": "", "css": ".ds-label { display: block; color: var(--ink-dim, #949494); font-family: var(--font, 'Chivo Mono', ui-monospace, Menlo, monospace); font-size: var(--fs, 0.9375rem); line-height: 1.45; letter-spacing: 0.16em; text-transform: uppercase; } .ds-input { display: block; width: 100%; min-height: 44px; padding: 0 1ch; background-color: var(--tt-black, #000); color: var(--voice, #ffff00); border: none; border-radius: 0; font-family: var(--font, 'Chivo Mono', ui-monospace, Menlo, monospace); font-size: 16px; outline: none; background-image: repeating-linear-gradient(90deg, var(--ink-dimmer, #4d4d4d) 0 0.6em, transparent 0.6em 0.9em); background-size: 100% 2px; background-repeat: no-repeat; background-position: 0 100%; caret-color: var(--voice, #ffff00); } .ds-input::placeholder { color: var(--ink-dim, #949494); } .ds-input:focus, .ds-input:focus-visible { background-image: repeating-linear-gradient(90deg, var(--voice, #ffff00) 0 0.6em, transparent 0.6em 0.9em); background-size: 100% 2px; background-repeat: no-repeat; background-position: 0 100%; }" }, { "name": "Colour Field Band", "kind": "card", "refersTo": "field-band", "description": "A section header as a full-bleed band of colour with the title reversed out of it. This replaces every card header and every 1px border. Goes yellow when something wants attention, red when a region is down.", "html": "
ConnectionRefusedError: [Errno 61] Connection refused