A divergence is not a bug report and not a wish. It is a place where building straight from
the code would produce something the design language does not want, or where the language
describes something the code does not yet provide. Each entry names both sides and says
which to follow today.
Two kinds of disagreement land here, and they resolve differently. When code and the design
library each state a value, code is authoritative and the design library has drifted — the
library is an emission target and is being rebuilt from code. When the design library states a
composition — how regions nest, what a template is made of, which slots a page has — code is
usually not disagreeing but silent, because the frame has no way to say it. There the design
library carries the intent and code is the side that has to grow.
TITAN-DIV-16 is the second
kind; TITAN-DIV-01 is the first.
Cite the ID. When a divergence drives a decision, name it — “chip tint is Tier-3 by
design per TITAN-DIV-01.” An agent or reviewer can then check the reasoning against the
system instead of against taste. IDs are stable and never renumbered.
How to use this page
- Build to design intent, not to what the code happens to do.
- When they conflict, follow the Follow today row — it is the safe answer right now.
- Cite the divergence ID in the PR or the design rationale.
- If a divergence is resolved, the entry stays and gains a resolution note. Deleting it loses the reason the code looks the way it does.
TITAN-DIV-01
Tier-3 tokens alias primitives, never semantics.
The stated rationale for “never skip semantic” is that a component token pointing at a
primitive cannot resolve correctly when the theme changes. That rationale does not hold
here — every one of the 26 resolves correctly, because each declares its own values. The
real cost is different, and smaller: each token re-decides independently what the semantic
tier already decided, and its relationship to the token it was derived from exists only in
prose.
TITAN-DIV-02
border-focus is specified and was never implemented.
This is the most consequential gap in the token layer, and it is not an open question.
TITAN-A11Y-04 requires a visible focus indicator;
the specification supplies the token to do it with; the code does not have it. Every component
deciding focus independently is the cost of that one missing implementation.
TITAN-DIV-03
Status colors fail contrast on light surfaces.
The status tokens invert their safety between modes:
text-success fails badly in light
(2.41:1) and passes comfortably in dark (6.80:1). A component verified in one mode carries
an undetected failure in the other. Measured values are in
the contrast tables.
TITAN-DIV-04
Three typography surfaces, three different counts.TITAN-DIV-05
The SCSS token surface is frozen at 82 names.TITAN-DIV-06
Two tokens for zero.The recorded reason does not hold.
spacing-none is described as being for resets that
must stay 0 regardless of context — but spacing-0 is also always 0. The distinction exists
in prose and nowhere in the values.TITAN-DIV-07
No surface layering model.TITAN-DIV-08
No grid specification.TITAN-DIV-09
The naming specification and the shipped tokens have substantially diverged.Both sides are legitimate. The specification is the design baseline, confirmed by its
owner. The shipped tokens are what renders. Neither is drift from the other in a simple
sense — they grew apart, and 54 of 244 overlapping is the measure of how far.This is why the grammar is documented separately from the inventory. The grammar transferred
intact; the token list did not.
TITAN-DIV-10
Documentation groups by concept; the package exports by unit.
The documentation was already doing this implicitly. 19 of 39 component pages in the
navigation have no matching Titan folder. Most are simple, harmless aliases — no second export
competes for the design-vocabulary name: Table is
DataGrid, Switch is Toggle, Tag is
Chip, Input is TextField, FileUpload is Dropzone, Spinner is CircularProgress,
ProgressBar is LinearProgress.
Two are not simple aliases and do not belong in that list. Dialog and Link each collide
with a second, real, unrelated export that also compiles under the design-vocabulary name —
importing that second export silently produces something dangerously incomplete (Dialog) or
entirely different (Link). These are dual-export collisions, not naming aliases: see
TITAN-DIV-27 for Dialog/Modal
and TITAN-DIV-25 for
Link/TextLink.
Switch/Toggle confirmed, not inferred, as of this pass. The Figma library’s own
component is named
Toggle, and Titan’s component tokens use the toggle-* prefix
(toggle-track-width, toggle-thumb-size, etc.) — both independent of this documentation’s
earlier guess. Unlike the general pattern this entry describes, this is not “the design
library’s word standing in front of code’s”: the design library and the token layer agree
with each other, and “Switch” is this documentation’s own choice — closer to the
Table/DataGrid case than to the rest of this list. Kept as “Switch” regardless, as a
plain English word at least as common as “Toggle” — see
Switch.TITAN-DIV-11
The design vocabulary names prominence; the code names the treatment.Prominence survives a visual change; treatment does not. “Primary” names the decision —
this is the action the view exists to enable. “Contained” names one way of drawing it. If
primary buttons stopped being filled tomorrow,
TITAN-BTN-01 would still say the right thing, and a
rule written against
contained would not.TITAN-DIV-12
The frame’s regions carry wrong or missing landmark roles.TITAN-DIV-13
A sticky header and a sticky footer sit 1090 stacking steps apart.The asymmetry is probably not deliberate. Nothing records a reason for the footer needing to
outrank a modal scrim, and the two regions are otherwise treated as a pair throughout the frame.
Recorded as a divergence rather than a defect because “both on the sticky plane” is a design
decision to confirm, not an obvious bug fix.
TITAN-DIV-14
TabPanel names a tab that nothing exported produces, with ids that collide.
TITAN-DIV-15
TabPanel wraps whatever it is given in a text element, and discards the inactive section.
Unmounting is a data decision disguised as a rendering one. A half-filled form in the tab you
just left is gone, and nothing warned the person who left it. That is the same class of loss the
frame protects against elsewhere — which is why it is recorded here rather than treated as a
performance choice.
TITAN-DIV-16
The header has no tab-bar slot, so a tabbed page composes one outside it.What the workaround costs is ownership. Once the bar is a sibling, the header’s bottom
boundary is produced by whatever the page put next to it, which is why the gutter has to be
re-supplied by hand — and why a page that forgets to looks identical to a page that chose not to.
TITAN-DIV-17
The page divider belongs to no region.This is a composition claim, not a measurement. The gap sizes in the current design library
are not the argument and will be restated when it is rebuilt from code. The argument is which
region owns the rule: a region can swap its own bottom edge for a tab bar, and cannot swap a rule
that belongs to neither side of it (TITAN-DIV-16).
TITAN-DIV-18
Filters are a header row in design and a separate region in code.The exclusivity is the part worth keeping. Whatever happens to the API, the design’s claim is
that a page does not get both a tab bar and a header filter row — recorded as
TITAN-VIEW-17. The table page template is the worked example: it
has tabs in the header, so its filters sit in the body with the data they filter.
TITAN-DIV-19
The selection bar and its select-all menu are designed and absent from code.This answers a question the view tier had left open. List view recorded
three plausible homes for bulk actions — the page header,
Controls, or a bar that appears on
selection. The design library answers: a bar that appears on selection, in the table’s own
toolbar band.TITAN-DIV-20
A hovered row and a selected row are given the same fill.The cost lands exactly where selection matters. On a table with bulk actions, the reader
answering “which rows did I select” is holding a pointer over the table while they do it, which is
the one condition under which the answer is unavailable.
TITAN-DIV-21
Only the standard Alert variant is colour-complete; filled and outlined fail contrast.
Computed from the shipped token values, formula validated against published reference ratios:
TITAN-DIV-22
EmptyState renders its supporting line as a heading.The visual size is already independent of the level. The title is
h3 carrying a bold body
type, so the heading level is doing no visual work — which means correcting it costs nothing on
screen and fixes the outline for every page that uses the component.TITAN-DIV-23
The theme’s naive every-word capitalization doesn’t implement title case’s function-word exceptions.TITAN-DIV-24
Input’s error focus ring’s light-mode value exactly matchesborder-error’s dark-mode value.
TITAN-DIV-25
The package’sLink export is not the design vocabulary’s Link.
TITAN-DIV-26
The package’s export for FileUpload is namedDropzone.
TITAN-DIV-27
The package’sDialog export is not the design vocabulary’s Dialog.
TITAN-DIV-28
The package’s export for Panel is namedTile.
TITAN-DIV-29
Popover ships with no Titan-specific decision recorded anywhere.TITAN-DIV-30
List’s disableBorder, disableHover, and bulleted props render no visual change.
TITAN-DIV-31
Avatar ships with zero Titan decisions.
TITAN-DIV-32
Code ships a bareAppBar; design’s page template depicts a composed site header.