@hydra-tv/ui
v0.3.2
Published
Generic, domain-agnostic React components for the @hydra-tv design system (buttons, inputs, dialogs, data grids, etc.). No broadcast-specific semantics — see @hydra-tv/broadcast for those.
Downloads
898
Maintainers
Readme
@hydra-tv/ui
Generic, domain-agnostic React components — buttons, inputs, dialogs, data grids, charts, and the like. No domain semantics live here: for broadcast (tally lamps, timecode, transport controls, …) see @hydra-tv/broadcast, and for sports (scoreboards, box scores, shot charts, …) see @hydra-tv/sports.
Visual language: dense, dark-only, "tactile hardware" — beveled controls, recessed LED-style wells, IBM Plex Mono. See @hydra-tv/tokens for the full token catalog these components are built from.
Install & setup
Within this monorepo, add it as a workspace dependency:
{ "dependencies": { "@hydra-tv/ui": "0.3.0", "@hydra-tv/tokens": "0.2.0" } }In another repo, point at this one (path/version depend on how you're consuming it — see the root README's "Using this in another app" section), then in your app:
import "@hydra-tv/tokens"; // once, at your app's entry point
import { Button, Panel } from "@hydra-tv/ui";react and react-dom (>=18) are peer dependencies — this package doesn't bundle its own React.
Component catalog
Every component below is <Name>.tsx (implementation, with an exported <Name>Props TypeScript interface) + <Name>.md (usage doc) in src/components/. Read the .md first — it has a working example — before opening the source.
Ported from the original design handoff
| Component | What it is |
|---|---|
| Button | Beveled push-button. Variants incl. take (reserve for high-consequence actions) and armed. |
| Input | Recessed text/number field with optional label + unit suffix. |
| Select | Styled native dropdown. |
| Checkbox | Recessed checkbox (✕ mark, not a checkmark icon). |
| Switch | Two-position rocker with text captions on both positions. |
| Badge | Small status tag; some kinds use tally colors, see its doc. |
| Tabs | Tab strip (renders the strip only, not panels). |
| Panel | The base layout container/"card" of the system. |
| Dialog | Modal confirmation dialog (no portal — absolutely positioned). |
| DataGrid | Dense table with sticky header and optional row highlight states. |
| LogConsole | Scrolling, monospace, auto-following event log. |
| FieldRow | Single label+control row (used internally by PropertyEditor; usable standalone). |
| PropertyEditor | Declarative sectioned form built from FieldRows. |
Added to fill gaps for general use
None of these existed in the original handoff (which was scoped to broadcast control-room screens); they're built in the same inline-style/token idiom as the ported set.
| Component | What it is |
|---|---|
| Divider | Hairline separator, horizontal or vertical. |
| Spinner | Indeterminate loading ring. |
| ProgressBar | Determinate or indeterminate progress in a recessed well. |
| RadioGroup | Single-select-from-visible-list control. |
| Slider | Numeric drag/range input. |
| Tooltip | Hover/focus popover label. |
| Accordion | Collapsible sections. |
| Menu | Click-triggered dropdown/action menu (distinct from Select). |
| Toast / ToastProvider / useToast | Transient status notifications with a context-based API. |
| Breadcrumb | Uppercase path trail showing the current location. |
| NavBar | Horizontal app-level navigation bar (brand · links · actions). |
| SideNav | Vertical navigation rail with active-item state. |
| LauncherTile | Large icon+label tile for home/app launchers. |
| Combobox | Searchable single-select with a custom listbox (distinct from native Select). |
Data visualization
Hand-rolled SVG and flex layout — no charting dependency, same inline-style/token idiom as everything else. Series colors default to the --ch-* data-viz tokens; never plot with --tally-pgm/--tally-pvw.
| Component | What it is |
|---|---|
| Stat | Headline figure in a recessed well, with label, unit and delta. |
| Sparkline | Inline trend glyph, small enough for a table cell. |
| BarChart | Categorical bars, horizontal or vertical, optionally stacked. |
| LineChart | Multi-series x/y lines with axes, reference line and band. |
| ScatterPlot | x/y point cloud over a pluggable background layer drawn in domain units. |
| HeatGrid | Labelled matrix shaded by value; sequential or diverging. |
| PercentileBar | Where one value sits in a distribution. |
Conventions (read this before adding a component)
- No CSS-in-JS, no external UI dependency. Every component is plain React + inline
styleobjects that reference@hydra-tv/tokensCSS custom properties (var(--bg-2),var(--ctl-h), etc.). The one exception is components that need CSS thestyleprop can't express — pseudo-elements (Slider's thumb) or@keyframes(Spinner,ProgressBar's indeterminate mode) — those inject one small scoped<style>tag from within the component. Don't reach for a CSS-in-JS library or a new build step for this; it hasn't been needed yet. - Controlled/uncontrolled pattern. Stateful inputs accept both
value/checked(controlled) anddefaultValue/defaultChecked(uncontrolled, via internaluseState) — seeCheckbox.tsxorSlider.tsxfor the pattern (const current = value !== undefined ? value : internal). - Every prop type is exported.
export interface ButtonProps { ... }alongsideexport function Button(...), so consumers (and other components — seeDialog.tsximportingButtonProps["variant"]) get real type-checking. - A
style?: CSSPropertiesescape hatch on (almost) every component, spread last so it can override any computed style. - Font roles, not faces. Set
fontFamilyto--font-ui/--font-data/--font-copy/--font-labelon the element that owns the text — never--font-mono/--font-sansand never a face name. Roles alias--font-monoby default; consumers mix typefaces by remapping a role. - Shared non-component code lives in
src/internal/and is deliberately not re-exported from the barrel (scale/tick math and the chart width-measuring hook live there). Keep the public API to components and their prop types. - Every
.tsxfile has a matching.mdwith a one-line description, a props table, and a runnable example. When you add a component, add its doc in the same commit — an agent picking this library up should never have to read implementation code just to learn the API.
