Design system
One page for the whole platform. Edit the tokens behind it (or a shared @orgdevkit/ui component) and the change flows to every app on its next build.
Unified elements
Shared across every app via @orgdevkit/design tokens and @orgdevkit/ui components. These are the defaults to reuse.
Neutrals
Page and surface backgrounds, hairlines, nested panels.
Ink
Text colours, strongest first.
Accent — teal
The brand action colour and its tints.
Stance — load-bearing
Agree / question / disagree / resting. Kept exact across the platform.
Stance ink — on-tint text
Slightly deepened stance inks for TEXT sitting on the stance-bg tints (pf workshop chips). The first three have no dark override — a minting gap for R-next; --colour-resting-ink was minted with one.
Notice / caution
Info banners + demo strips. Minted in R3.2 (was the orphan .demo-banner palette).
Danger
Destructive-action surfaces (pf admin). Light-only so far.
Utility borders & control greys
The cool-grey control vocabulary minted from the reference app. Light-only so far (no dark overrides) — a designer-visible gap.
Companion / chat surfaces
The the reference app companion vocabulary (chat dock, callouts). Light-only so far.
Steer — guided revision
The pf "steer" prompt panel (the facilitator nudge). Light-only so far.
Warm panels & dark CTA
Warm parchment panel pair + the hover of a dark (ink-filled) CTA. Light-only so far.
Library categories
Per-category ink for library/reading-list sources (pf companion). Light-only so far.
Document status (portal app · additive)
Lifecycle of a registry template. current/draft now alias the platform palette.
Document tier (portal app · additive)
Derived from the platform palette (no foreign colours).
Diff paint (governance ceremony)
The one diff card — additions and deletions.
Type scale
Headings + reading set in the serif; UI in the sans. Sizes are the --t-* tokens.
Spacing & layout
Today only layout/measure tokens exist — there is no --space-* ramp yet (R1.1 flag; R3 adds the two-tier scale).
Radii
Shadows
Base atoms & focus
The global class atoms from the @orgdevkit/design base layer — .eyebrow, .ref, .rule, .glyph — and the shared focus treatment. These need no import: any element in any app can wear them.
.rule — the registry divider on --hairline-inkKeyboard focus everywhere gets the same treatment: a 2px canvas gap + the teal --accent-tint-2 ring, applied globally via :focus-visible in the base layer (mouse clicks don’t paint it).
System gap: no motion/easing tokens are minted yet — transition durations and curves are hardcoded per component (and the website keeps its own --ease). A designer restyling motion currently has no single dial.
Primitives
The live @orgdevkit/ui components, in the reference look. Labelled with their export name.
Eyebrow · kicker
Display XL
Display — section opener
Display M — sub-section title
Title — a card or pane heading
Lede — the serif standfirst that introduces a passage, set a touch larger and in the secondary ink.
Body L — the wide reading column, one step up from the default body.
Body — the reading set. Members read document passages and reflections in this serif face at a comfortable measure.
Small — muted meta, captions, and footnotes.
REF · CATALOGUE-0042Shorter sleep tracks with slower recovery312. The reader’s own diary shows a comparable patterne2.
Begeleide krachttraining, twee keer per week, laat in drie RCT’s een klein maar consistent effect op vermoeidheid zien.
The state pill above and the reaction below are two axes. Neither ever tints the other. Nothing here is marked unread.
de opbouw en begeleiding zijn bepalend voor het effect
slaapkwaliteit hangt samen met het beloop van klachten
extra vitamine D boven de dagelijkse aanbeveling
Dit komt uit het onderzoeksmateriaal van deze persoon en is geen oordeel over wat jij zegt. Ben je het er niet mee eens, schrijf dat gerust op.
- Kernvraag van het onderzoek
- Situatie en verloop tot nu
- Wat het meest in de weg zit
- Wat al geprobeerd is
- Beslisstijl (samen met arts)
- Wat een goede uitkomst zou zijn
- Grenzen aan het onderzoek
- Wie je erbij wilt betrekken
- Hoe vaak je wilt afstemmen
Vult zichzelf tijdens het gesprek. Dit is geen formulier: je kunt hier zelf niets aanvinken.
Een leeg profiel staat aan het begin, niet aan het eind.
- Kernvraag van het onderzoek
- Situatie en verloop tot nu
- Wat het meest in de weg zit
- Wat al geprobeerd is
- Beslisstijl (samen met arts)
- Wat een goede uitkomst zou zijn
- Grenzen aan het onderzoek
- Wie je erbij wilt betrekken
- Hoe vaak je wilt afstemmen
Counts are “looked at / total”, never a score. Every mark in the collapsed strip carries its chapter’s full name, so the strip is a shortcut and not a riddle. The reviewer rail is the same component with two entries and no figures.
The panel opens over this column. Nothing here moves when it does — the chrome keeps its 72px footprint either way.
Hover Doing or Areas: the panel opens over the page after a short grace, and that section takes a grey bar while the one you are actually on keeps the ink one. Clicking always lands somewhere — Updates has no second level, so it navigates and closes whatever was open. Unpinning asks first, and the × has no path to the answer.
The fifth slot holds a pill with a count of zero, and it is empty: a mark reading 0 would say “there is nothing here”, which is the one thing a mark on a nav entry must never mean.
The area’s own pages sit beside them, not in the chrome. The rail can name Wealth and — because a shell nav resolves one fixed set of destinations — it cannot name anything inside it.
Archive is drawn and is not a control — no link, no href, nothing to focus — and it does not count towards whether there is a menu at all: an area with one live page and eight not-yet ones has nowhere to go, so nothing is drawn. The figure on a row is a figure (meta), never the rail’s open-count pill: there are no counts down here worth interrupting for.
The fifth slot holds a mark with no word, and it is empty — swatch included. A coloured square on its own says “this belongs somewhere” and names nowhere, and a person who cannot see colour gets nothing from it at all. There is no title on any of them: the detail behind a pin belongs in a popover, which is what the mock’s native tooltip was replaced by.
Doing
Updates
Receive-only. Everything you watch, newest first. You never write anything back from here.
Transactions
PoolCOMING SOON
Ideas, notes, resources, people and lists \u2014 capture first, decide later.
Nothing pressed yet.
Four pages, one header. The black button is in the same corner on all four and says the same word, because it is the only ink the header can draw — Import a file is the page’s own verb and takes the area’s accent instead. A subtitle turns the one-line shape into the two-line one and the cluster tops out with the title rather than falling into the gap under it. The C chip is drawn because this host says the key is bound; a host that has not bound it leaves it out rather than advertising a shortcut that does nothing.
Doing
Reflect
Step back from the list — close the day, look across the week, and think out loud when something is stuck.
CADENCES
- Daily closeClean up the left-overs and set the day.
- WeeklyEmpty the inbox, look across the projects, tidy what is left.
- QuarterlyStep back across the whole quarter.Not available yet
VISION
A calm life where the systems carry the load — present for the people, building things that outlast the builder.
Last revised in June · revisit quarterlyLAST REFLECTION
Week 32 — “Calmer week. Said no twice and it held.”
BRAINSTORM ROOMS
Reflect
Step back from the list — close the day, look across the week, and think out loud when something is stuck.
CADENCES
- Daily closeClean up the left-overs and set the day.
Nothing pressed yet.
Three stages: the menu that gets here from Doing, the page with everything, and the same page on an account that has none of it. The design draws four blocks and three of them read from stores the ODK does not have — so the page draws what it is handed and nothing where it is handed nothing, rather than a Resume nobody can resume and an archive link into nowhere. The third cadence is drawn here and cannot be chosen, and the owner’s rule (2026-08-19) is that nothing in this product is drawn as absent — so it wears the same SOON badge the rail’s not-yet entries wear, in both lists, with no verb and no menu item behind it. ⚠ The reason is the ELEMENT rather than the cadence, and the two used to be the same reason: quarterly has six step ids and no runner was true until U.7b built the screen, and U.7e2 gave the last two steps their panels. This fixture keeps it soon because it is the only drawing of that state anywhere in the tree — flip it and the badge, the note, the missing verb and the missing menu row are on no screen a screenshot pass can open, and five sabotage rows lose the fixture they read.
Not one of these passes an orbit count. The 24px and 18px marks drop to two paths on their own, because a third one at that size crosses the type beside it — the design’s minimum-size rule as a default rather than as a prop somebody has to remember. The fourth plays once and stops; the fifth is the same mark with its paths off. On the ink band the dots are the surface colour rather than white, which is why they are still right after the theme flips. Under prefers-reduced-motion every one of them rests on one dot with its paths drawn.
Top row, 44 down to 12px: below 15px the dot grows and the gap opens, so the mark never reads as a full stop before the word. Second row, the same lockup twice — once with its dot, once with a 40px loader in the same slot. The slot is the dot’s box whatever is in it, so the word is in exactly the same place in both. Underneath, the mark alone: the app icon and the avatar, drawn from the geometry a composition generates its real icon files from.
Hover a corner mark: after a short grace the circles fly out from under it, outside first, each filling with its glyph only once it has landed — and the dot on the trigger shrinks, which is the loader’s launch beat borrowed as an interaction. Leaving closes it again; a click keeps it. The middle fan holds five, so its ring stands 15px further out — the arc grows to fit, and the limit of five is the store’s rule, not the geometry’s. The third has nothing in it and therefore draws nothing: no trigger, no scrim, because a control that opens an empty ring is a dead control.
Doing
| typed | call the notary back tomorrow, 10m |
|---|---|
| the sentence | call the notary back |
| read out of it | tomorrow → due · 10m → estimate |
The header’s Capture, the fan’s Capture and the C key are one state — the line above says which door was used, and it is the only difference between them. Inside, one line is read for a day and an estimate: the table shows what the parser makes of the placeholder’s own example, which the harvested parser could read neither half of. Picking a chip moves where it lands; picking it again puts it back. And the Esc chip is not decoration — this panel is a Modal, so Escape closes it and Tab stays inside, which the surface this was harvested from does neither of.
| Maand | Stand | Verschil |
|---|---|---|
| jan 2026 | €118.900 | |
| feb 2026 | €120.400 | +€1.500 |
| mar 2026 | €121.100 | +€700 |
| apr 2026 | €119.800 | −€1.300 |
| may 2026 | €122.600 | +€2.800 |
| jun 2026 | €123.900 | +€1.300 |
| jul 2026 | €124.200 | +€300 |
| aug 2026 | €122.100 | −€2.100 |
| sep 2026 | €125.400 | +€3.300 |
| oct 2026 | €126.800 | +€1.400 |
| nov 2026 | €127.100 | +€300 |
| dec 2026 | €128.437 | +€1.337 |
De doorgetrokken lijn is wat er gemeten is; de gestippelde lijnen zijn rekenwerk.
- Laatste 7 dagen · €6,10 per dag · nog 12 dagen
- Elke dag vol · €10,00 per dag · nog 8 dagen
- Alles op pauze · €0,00 per dag · zo raakt het niet op
- 25-7 · €3.80 · jij en de assistent €3.80
- 26-7 · €5.20 · jij en de assistent €4.40 · het team ’s nachts €0.80
- 27-7 · €4.10 · jij en de assistent €4.10
- 28-7 · niets gebruikt
- 29-7 · €6.70 · jij en de assistent €5.90 · je Support team €0.40 · het team ’s nachts €0.40
- 30-7 · €5.10 · jij en de assistent €5.10
- 31-7 · €4.40 · jij en de assistent €3.70 · je Support team €0.70
- 1-8 · €7.10 · jij en de assistent €6.10 · het team ’s nachts €1.00
- 2-8 · €3.20 · jij en de assistent €3.20
- 3-8 · €5.60 · jij en de assistent €4.90 · je Support team €0.70
- 4-8 · €8.40 · jij en de assistent €7.20 · het team ’s nachts €1.20
- 5-8 · €2.90 · jij en de assistent €2.90
- 6-8 · €6.10 · jij en de assistent €5.20 · je Support team €0.90
- vandaag · €6.20 · jij en de assistent €5.00 · je Support team €1.20
- Jij en de assistent
- Je Support team
- Het team · ’s nachts
- Eenmalige inrichting
Een lege dag is een dag zonder verbruik, geen storing. De gestippelde kolom bij vandaag is wat er nog over is \u2014 en die verschijnt niet op een dag die er al overheen is.
In je documenten staat dit als “Hb”, “hemoglobine” en één keer als “haemoglobin”. Één stof, één grafiek.
De band is het bereik uit het laatste rapport. Die band is een hulplijn bij het kijken. Of een waarde erbuiten valt, staat per meting: elke meting wordt vergeleken met het bereik van het rapport waar zij zelf uit komt.
11 november 2024 · 7,6 mmol/L · onder het bereik van het rapport waar deze meting uit komt (7,8 tot 10,5 mmol/L)
| Datum | Waarde | Status | Uit |
|---|---|---|---|
| 3 mei 2024 | 8,1 mmol/L | binnen het bereik | [e2] |
| 11 november 2024 | 7,6 mmol/L | onder het bereik | [e2] |
| 4 april 2025 | 8,0 mmol/L | binnen het bereik | [e2] |
| 14 mei 2026 | 8,3 mmol/L | binnen het bereik | [e2] |
One measurement never becomes a chart: a line through a single point draws a course nobody measured. Each reading is compared with the range of the report it came from — so the last row, which came without one, carries no verdict at all.
One state, on purpose: no hover, no disabled, no size. The href is the caller’s URL byte for byte — there is no redirect to count a click through, so there is nothing to render differently. A relative href has no fifth specimen because it has no appearance: it throws at compose time rather than mail a dead button.
|
The strip at the foot is not a state and cannot be switched off: a blank disclaimer throws at compose time rather than render, so there is no specimen of a mail without one. The preheader is here too and is deliberately invisible — it is the line a client shows beside the subject, and the only hidden text in the output. What this specimen proves is that it renders in a browser; no mail client is represented here, and Outlook least of all.
|
Hoi Anne, Elke zes weken staat er één gesprek voor je klaar over de richting van je onderzoek. Het gaat niet over de inhoud van je plan; het gaat over de vraag of het team nog het goede voor je uitzoekt. Het duurt ongeveer een kwartier. Het is een gesprek en geen formulier, dus je kunt gewoon opschrijven wat er sinds de vorige keer veranderd is. Open het gesprek: https://example.test/gesprek/9f2c De link brengt je naar het gesprek in je eigen omgeving. Je hoeft niets voor te bereiden. Laat je deze mail liggen, dan gebeurt er niets. Er volgt geen herinnering en er verandert niets aan je onderzoek. Het gesprek blijft staan tot je het opent. Het team Je krijgt dit bericht omdat je zelf een onderzoek hebt lopen. In je eigen omgeving zet je deze uitnodiging uit; dat stopt de mail en niet het onderzoek. BELANGRIJK Dit is je eigen onderzoek om samen met je arts te kijken wat voor jou het beste is. Het is geen medisch advies en geen diagnose. AI analyses kunnen fouten maken. Jij bepaalt samen met je arts welk onderzoek nodig is en hoe de uitkomsten uit dit platform te interpreteren voor jouw specifieke situatie.
The greeting takes the first name from the intake. There is no specimen without one because there is nothing to see: no name means no greeting line at all — not Hoi , and not an invented name.
|
Beste auteur, Wij werken als team aan het persoonlijke onderzoek van iemand met een chronische aandoening. Wie die persoon is, staat hier niet, en dat blijft ook zo. Wij vragen de volledige tekst van “Sleep duration and inflammatory markers in a prospective cohort” (2025). De samenvatting hebben we; die is niet genoeg om vast te leggen hoe zeker het bewijs is. Wat er met je artikel gebeurt: - Het komt in de privé-bibliotheek van één persoon, en van de mensen die zij zelf laat meelezen. - Het team leest het en legt de bewijskracht vast in GRADE, de maat voor de zekerheid van bewijs. Dat gebeurt woordelijk zoals de publicatie het zelf beschrijft, niet in onze eigen woorden. - Wordt het gebruikt, dan staat het als genummerde bron onder een punt in haar plan, met titel en jaar. - Het wordt niet gepubliceerd, niet doorgestuurd naar anderen en niet in een openbare database gezet. Deze vier regels staan woordelijk zo op de pagina achter de link. Het zijn dezelfde sleutels; er bestaat geen tweede formulering. Deel de publicatie: https://example.test/doneren/7b41 De link is drie weken geldig en werkt één keer. Er wordt geen account voor je gemaakt en er is geen wachtwoord. Wat je hier niet voor terugkrijgt: - Geen account en geen inlog. Deze pagina is alles wat je van ons ziet. - Geen bericht of het artikel gebruikt is. Wij versturen aan niemand notificaties, ook niet aan jou. - Geen inzage in het plan, in de gegevens of in wie de persoon is. - Geen vergoeding, en geen vermelding buiten de bronnenlijst van dit ene onderzoek. Ook deze vier regels zijn de sleutels van de doneerpagina, woordelijk. Één formulering, twee plekken. Zeg je nee, dan horen wij niets terug en dat is prima. Er volgt geen tweede verzoek. Het team Je krijgt dit bericht eenmalig, omdat je publicatie in het onderzoek van deze persoon opdook. Je adres staat bij ons in de bronvragenlijst en gaat nergens anders heen. BELANGRIJK Dit is je eigen onderzoek om samen met je arts te kijken wat voor jou het beste is. Het is geen medisch advies en geen diagnose. AI analyses kunnen fouten maken. Jij bepaalt samen met je arts welk onderzoek nodig is en hoe de uitkomsten uit dit platform te interpreteren voor jouw specifieke situatie.
The two panels are the donate page’s own keys rendered a second time, never a second wording — which is why they arrive as data. The mail names no person, carries one link, and that link works once.
|
Goedemorgen, Vier dingen weken gisteren af van de verwachting. 6 van de 6 onderzoeken liepen. circle-02 liep gisteren en leverde niets op, en de engine gaf niet aan waarom. circle-02 liep gisteren en gaf niets uit. Of er is niet gewerkt, of het werk bereikte geen model. circle-04 heeft 160 bronnen die op indexeren wachten en daar bewoog er gisteren geen van. circle-06 stopte bij een grens: er is geen tegoed meer. Elk onderzoek, en wat het gisteren deed: Onderzoek · Lid · Staat · Bronnen · Geïndexeerd · Bevindingen · Rondes circle-01 · member-01 · Loopt · 12 · 11 · 6 · 1 circle-02 · member-02 · Loopt · 0 · 0 · 0 · 0 circle-03 · member-03 · Loopt · 0 · 0 · 0 · 0 circle-04 · member-04 · Loopt · 12 · 0 · 6 · 1 circle-05 · member-05 · Loopt · 12 · 0 · 6 · 1 circle-06 · member-06 · Loopt · 12 · 0 · 6 · 1 Gisteren is $9,20 van de $24,00 aan dagbudgetten gebruikt, verdeeld over 6 onderzoeken. Open het beheer: https://example.test/beheer Alles in dit bericht staat ook op Kosten en op Trajecten, met dezelfde cijfers. Dit bericht rekent niets uit wat daar niet staat. Leek een probleem en was het niet: - circle-03 · leverde niets op — de engine gaf aan dat hij pas op de plaats maakte - circle-06 · gaf niets uit — er is geen tegoed meer, en dat is een staat en geen stilte Kon niet gemeten worden: - circle-05 · een achterstand die niet bewoog — er kon niets bij die naad bewegen, dus er is geen gezonde meting om mee te vergelijken Je krijgt dit bericht omdat je de enige beheerder bent. Het gaat naar niemand anders, en het gaat over het platform en niet over wat mensen lezen of doen. Het platform Wil je dit bericht niet meer, dan zet je het uit op Kosten. De cijfers blijven daar staan. BELANGRIJK Dit is je eigen onderzoek om samen met je arts te kijken wat voor jou het beste is. Het is geen medisch advies en geen diagnose. AI analyses kunnen fouten maken. Jij bepaalt samen met je arts welk onderzoek nodig is en hoe de uitkomsten uit dit platform te interpreteren voor jouw specifieke situatie.
Every figure here arrived as a string. The mail computes nothing, so it cannot disagree with the screens it reports on — and it carries no chart and no percentage without its denominator. The progress table is the one exception to no table, added by the owner on 2026-08-15; it names circles and members by id, never by initial.
|
Goedemorgen, Er week gisteren niets af van de verwachting. 2 van de 2 onderzoeken liepen. Elk onderzoek, en wat het gisteren deed: Onderzoek · Lid · Staat · Bronnen · Geïndexeerd · Bevindingen · Rondes circle-01 · member-01 · Loopt · 12 · 11 · 6 · 1 circle-03 · member-03 · Loopt · 9 · 9 · 4 · 0 Gisteren is $3,68 van de $8,00 aan dagbudgetten gebruikt, verdeeld over 2 onderzoeken. Open het beheer: https://example.test/beheer Alles in dit bericht staat ook op Kosten en op Trajecten, met dezelfde cijfers. Dit bericht rekent niets uit wat daar niet staat. Je krijgt dit bericht omdat je de enige beheerder bent. Het gaat naar niemand anders, en het gaat over het platform en niet over wat mensen lezen of doen. Het platform Wil je dit bericht niet meer, dan zet je het uit op Kosten. De cijfers blijven daar staan. BELANGRIJK Dit is je eigen onderzoek om samen met je arts te kijken wat voor jou het beste is. Het is geen medisch advies en geen diagnose. AI analyses kunnen fouten maken. Jij bepaalt samen met je arts welk onderzoek nodig is en hoe de uitkomsten uit dit platform te interpreteren voor jouw specifieke situatie.
The state that is easiest to lose and hardest to win back. Nothing warned, so there are no warning paragraphs and no quiet blocks — and the mail still has a headline, the table and a way in, because an email that never arrives is indistinguishable from a cron that never ran.
Canvas + hairline + soft shadow.
Hover lift for clickable surfaces.
Open / selected: deeper shadow + accent border.
With refCode — the leading archive-signature cell (fixed-width accent tabular-nums) the Brand catalogue rows carry.
The float-label input family the CRM record forms use (e.g. crm AddPersonForm). Controlled by the consumer; the label floats on focus/fill.
The three states every control must render: resting, invalid (the disagree-red border + aria-invalid), and disabled.
The generic searchable record table (the CRM/people directory list). Columns, selection, search, and decrypt/empty states are all driven by props.
| Name |
|---|
| Ada Lovelace |
| Grace Hopper |
| Katherine Johnson |
The first-load decrypting state (loading + initialLoad) — rows are field-decrypted via @orgdevkit/crypto, so the initial fetch shows the DecryptSpinner instead of a generic loader.
The two thin helpers around the crm drawer’s view⇄edit toggle: the quiet pencil that enters edit mode, and the sticky cancel/save bar shown while editing (the consumer owns the editing flag). Shown here inline; in-app the bar is the drawer’s sticky footer.
The shared top bar every app wears — brand + context tag, the role-aware three-circles nav, and a right-side actions slot. Pass role (+ currentPath) and the chrome derives that circle’s nav via circlesNav; each role lands on roleHome(role). Pass a linkComponent for client-side routing (here a plain anchor).
Portal look — wordmark (serif brand after the logo) + navVariant="pills" (taller bar, centred pill nav, active = accent on accent-tint)
Sign in
This place works by invitation. There is no sign-up form and you cannot make an account here.
You can change the language at any time.
The sign-in surface of a product nobody can join from the outside. The element holds no strings and no transport: the host resolves the copy through the locale runtime and calls requestMagicLink() from @orgdevkit/auth/magic-link, which is where the neutrality is decided. Switch between a member, not a member and rate-limited and the screen does not change — that is the point, and it is why the panel talks about the request rather than promising a mail. Refused link is the ?denied=1 state, which says one thing whether the link expired, was already used, or was never that person’s.
On an app surface this person is shown the member chrome.
Their own research, their own six destinations.
The frame every surface sits in: chrome, content column, footer. Which chrome — or none — comes from resolveShellRoute() in shellAccess, which reads the session subject and nothing on the URL, so no route param can widen what a person is shown. The nav is empty while onboarding is unfinished and the gate is what moves them; a shell told to redirect paints nothing rather than a half-second of another room. The disclaimer strip is required by the footer’s own props — one string per chrome (§6), never interpolated, never shortened — which is how “on every surface” stays true without anyone remembering it.
Settings
Your account
Every row here is an app-shell capability — the address a magic link goes to, the session, the locale runtime, the theme store, chrome-level display preferences — and the mockup’s own evidence that the screen is the shell’s is that it has three kickers, one per chrome. The notation options are labelled BY the formatter each one selects (notation.ts), so a translation cannot promise a shape the code does not produce; the appearance row is the only place a person can hand the choice back to their device, because system is the absence of a stored value rather than a third one. A confirmation of a new address is a state of the ROW: the old address stays on screen, because nothing has changed until a link is opened. What the design draws that is not the shell’s — a copy of your data, deleting an account — arrives through panels.
The hairline-top page footer — serif brand mark (+ optional accent glyph) and a muted sans caption. Generalised from the portal app footer.
A kit ships one language and no runtime; the host merges a catalogue over it per locale. A missing key falls back rather than showing machinery to a reader, so the gap is invisible by construction — which is why copyGaps() exists and is meant to run in a test, not in a render. Locale comes from the account, never from the browser.
Per-locale translation state for a document/section. Four states: filled = in-sync; amber = stale (the source changed after this translation); dashed = unverified (machine-drafted, awaiting a human check); ghost = off (that language isn’t available yet — any locale absent from the map).
The two remaining shared @orgdevkit/ui composites — the CRM “link a member to organisations / events” pickers. They drive an async loader and a full set of localised labels, so they live on the real crm screens rather than a synthetic demo; listed here so the catalogue is complete. Both are built from the primitives above (Modal, FloatText, Button, Badge) on the shared tokens.
Feature-specific
Genuinely bespoke, app-local surfaces — the allowed exceptions to reuse-first (Decision 13), catalogued from the CSS the apps actually ship today (name + what it is + the CSS that styles it). Anything here that turns out to be almost shared gets promoted into @orgdevkit/ui rather than forked again.
Specialised shared — the governance diff
Shared in @orgdevkit/ui but specialised: the one diff card the revision ceremony uses. Rendered live.
removedadded
The same diff core in the stance tone — the reference app workshop palette (agree-green / disagree-red on the shared stance tokens), adopted by ProposedEditCard in R3.4.
removedadded
The composed DiffCard — caption bar + its own inline⇄split toggle + legend in one labelled card. This is the headline “one diff card” every change surface reuses.
removedadded
PatchDiffCard — the same card fed a raw unified-diff hunk (GitHub compare’s FileChange.patch): reconstructed to before/after, then word-diffed. The bridge to real compare output.
removedadded
ReconcileSplit — the AI-reconcile view: a canonical change folded into a country’s copy. The country’s own wording that was kept is underlined; the folded-in change reads as additions (no struck deletions in the draft).