This is the reference of record for the Causomic interface. Each component below is the live component from the bundle, not a picture of one. Props are the shipped API.
Where the system is silent — dark mode, modals, toasts, breakpoints beyond the 720px switch — it is silent on purpose. Ask before inventing; new patterns come back into this file.
Import the React components, or style your own markup against the token variables. Both read from the same CSS custom properties, so they can coexist in one page.
import { Button, Kicker } from '@causomic/ds';
import '@causomic/ds/styles.css';
<Button href="/book">Book a call</Button>
.cta {
background: var(--ink);
color: var(--cta-fg);
padding: var(--btn-pad);
border-radius: var(--radius);
}
Load in this order. styles.css imports the token files, so importing it alone is enough in a bundler.
Nine rules that keep the system one visual world. A pull request that breaks one of these is a bug, not a variation.
A committed light theme. Two grounds, two text tones, two rule weights, one accent. There is no third text tier and no dark variant — do not add either.
Prefer these in product code. They let the palette move without a find-and-replace.
Two families. Switzer carries everything a person reads; Geist Mono carries everything the machine measured — labels, doses, counts, caveats. Sizes are exact values, not a modular scale.
Three of them need developer attention, because they live in the HTML rather than the CMS.
There is no 4/8 scale. Six named constants and one breakpoint. If a value is not on this page, it should not be in the CSS.
This is the layout primitive. A 1px gap over a --rule background, inside a --rule border. Cells share their hairlines, so a sequence reads as one object. Never nest a bordered frame inside a cell.
.cells {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
gap: var(--grid-gap);
background: var(--rule);
border: 1px solid var(--rule);
}
.cells > * { background: var(--paper); padding: 24px; }
Three files. Two drawn marks and one photograph. There is no icon set and no illustration library — figures are drawn from data at runtime.
The palette was chosen to clear AA at every combination that ships. Keep it that way: the checks below are the ones a review should run.
Five primitives that appear on every surface. Import path components/core/.
The one CTA object, and the only solid-ink element on a light page. Every one of them books the same 30-minute call, so there is never a second button competing with it.
<Button href="/book">Book a call</Button> <Button tone="bone" href="/book">Book a call</Button>
The labelling voice: section kickers, figure straps, legend keys, ledger numbers. If a piece of text names something rather than saying something, it is a Kicker.
<Kicker>Simulated cohort</Kicker> <Kicker color="var(--amber)">Top-ranked design</Kicker>
The caveat under a figure. Mandatory wherever a simulated number appears — a chart or stat that ships without one is a defect, not a style choice.
<FigNote>Illustrative simulation.</FigNote>
A 9px ruler strip. It exists to frame the ink footer band, above and below. Do not scatter it as decoration elsewhere.
Renders either SVG through a CSS mask, so one file serves both ink and bone contexts. See Assets for sizes and clear space.
<Logo src="/assets/causomic-logo.svg" height={18} color="var(--ink)" />
<Logo src="/assets/causomic-mark.svg" height={24} ratio={1} />
Where simulation output is presented. Both panels carry their own qualifier because both are screenshot-ready — they leave the page in decks and emails.
Paper ground, plus-grid texture, hairline border, mono strap with square legend swatches. The figure itself is yours to draw — canvas or SVG, ink weights for the series, amber for the one.
<VizPanel
strap="Simulated trial designs"
legend={[
{ label: 'Simulated designs', color: 'var(--grid-mid)' },
{ label: 'Top-ranked', color: 'var(--amber)' }
]}
>
<canvas ref={ref} />
</VizPanel>
<FigNote>Illustrative simulation.</FigNote>
The readout for a single design. Amber belongs to the winner only — the quiet variant is what an inspected-but-not-winning design looks like, and both states appear in the same view.
<Verdict
kicker="Top-ranked design"
title="300 mg q3w"
sub="PD-L1 ≥ 50% · combination arm"
stats={[{ label: 'Median PFS', value: '9.4 mo' }]}
note="Illustrative simulation."
/>
Two ways to present an ordered sequence. Both render as one object with shared hairlines — if you find yourself reaching for a list of cards, one of these is the answer.
Numbered ruled rows. Use it when the items are read in order and can run long — process steps, commitments, what is and is not included.
<Ledger items={[
{ n: '01', label: 'Scope', body: 'Compound, eligibility, endpoint.' },
{ n: '02', label: 'Calibration', body: 'We fit the model to your prior data.' }
]} />
Three or four short steps across the page, arrows straddling the seams. Keep each step to a title and one line — anything longer belongs in a Ledger.
<StepFlow steps={[
{ n: '01', title: 'Define', say: 'Compound, arms, endpoint' },
{ n: '02', title: 'Simulate', say: '240 protocols' },
{ n: '03', title: 'Rank', say: 'Three worth running' }
]} />
Product-surface components. The marketing site defines none of these — they extend its vocabulary (square geometry, hairline borders, ink fills, mono labels) to dashboard views.
Filters and applied constraints. Selected fills ink; nothing else changes size, so a row of chips does not reflow as the user picks.
Square swatch plus mono label. Status, not decoration. Three tones, and the amber one appears at most once per view.
A row on a hairline; the active tab gets full ink and a 1px underline. Controlled or uncontrolled — click through the example.
Mono uppercase headers, hairline rows, tabular numerals. Set mono on any column holding a measured value and align: 'right' on numbers that get compared.
<Table
columns={[
{ key: 'design', label: 'Design', mono: true, strong: true },
{ key: 'pfs', label: 'Median PFS', mono: true, align: 'right' }
]}
rows={rows}
onRowClick={(row) => select(row)}
/>
Mono label over a 27px tabular value. Lay them out in a cell grid, not as separate boxes. accent is the winner’s only.
All six are live below — type in them, tab through them, toggle them. Controls sit on bone with a rule-2 border, and every label is mono microtype above the field.
Field owns the label and hint; the control goes inside it. Set mono on inputs that hold identifiers, doses or counts — the data voice is a real signal, not styling.
<Field label="Dose" hint="mg per administration">
<Input mono value={dose} onChange={e => setDose(e.target.value)} />
</Field>
<Field label="Indication">
<Select options={['NSCLC', 'Melanoma']} value={ind} onChange={onInd} />
</Field>
Checkbox and switch are square and fill ink when on. The radio ring is the one curve in the system — it earns it, because roundness is what tells a reader only one can be chosen.
<Checkbox label="Include combination arm" checked={inc} onChange={setInc} />
<Radio options={['Monotherapy', 'Combination arm']} value={arm} onChange={setArm} />
<Switch label="Log scale" checked={log} onChange={setLog} />
Nav and footer are full-bleed: they ignore the page gutter and run to the viewport edge. Both are shown here inside a bordered box, which is the one place in this document they are framed.
Lockup, two links, one solid-ink CTA cell running to the bar edge. Two links is the rule — a third is a third thing to read before the one action. Omit the CTA on the page it points at.
<NavBar
logoSrc="/assets/causomic-logo.svg"
links={[{ label: 'The model', href: '/#model' }, { label: 'Design partners', href: '/#partners' }]}
cta={{ label: 'Book a call', href: '/book' }}
/>
Ticks, then the ink band with the bone lockup, then ticks, then the mono meta row. This band and the closing band on the roles page are the only dark surfaces in the system.