Problem
Composed slot content now resolves primitive layers to the design system's own components (ADR-074). A glyph layer becomes the library's icon component, carrying the concepts the binding maps — colour and name.
It does not carry size, and for an icon component that sizes itself from a prop, the result renders wrong.
In a Card's default example, the badge's icon is authored at 16×16. It emits as:
<Icon appearance="On positive" name="add" style={{ width: '<sizing token>', height: '16px' }} />
The icon component has two independent size channels:
- Its root box, which the inline style shrinks to 16px
- Its inner glyph element, sized only by a
data-size attribute driven by a size prop
size is never passed, so it stays at its default and the inner glyph renders 24×24 inside a 16×16 root. The root clips its content, so the icon comes back cropped.
Before the binding existed, a composed glyph emitted a self-contained masked span carrying its own dimensions inline, and rendered correctly at any size.
Why it is not simply an oversight
ADR-075 deliberately excluded size from the glyph concept set. The reasoning was that sizing should travel as sizing — the spec records width and height, and every platform already understands those — rather than becoming a per-library enum that every other design system then has to model.
That reasoning holds right up until the component ignores its own box, which is what an icon component with a size prop does.
The information needed is already present and unambiguous: the composed width and the component's own extra-small variant resolve to the same sizing token. Nothing has to be guessed; it has to be matched.
Options
- Map
size as a glyph concept, matched on token identity. Compare the composed dimension against the sizing tokens the bound component's own variants use, and pass the variant that matches. Renders correctly, needs no hand-maintained table, and stays one concept. It reopens ADR-075's decision to keep size off glyphs and Decision 4's rule that conventions map prop names and never values.
- Map
size with a declared value table in conventions. Explicit and simple to read, but it is the unbounded, drift-prone mapping ADR-075 Decision 4 rejected: it grows with the design system and silently rots when a token is renamed.
- Do not bind a glyph that carries explicit sizing. Fall back to the self-contained masked span, which is correct at any size. Principled — never bind when the bound component cannot express what the composition asks for — but it unbinds most composed icons, because authored examples nearly always set an explicit size.
- Accept it. Composed icons render at the component's default size unless a design happens to use that default. Cheapest, and visibly wrong wherever it is not.
The same question will apply to any concept where a component overrides its own box from a prop. Icons are simply where it shows first.
Acceptance criteria
Workspace
Found in a design system library workspace while reviewing composed slot content in Storybook, after enabling container, text and glyph primitive bindings for the first time.
Impacted code
packages/react-from-specs/src/Spec/primitives.ts — the glyph concept set and its attribute emission
packages/webcomponents-from-specs/src/Spec/primitives.ts — the same, for custom elements
packages/react-from-specs/src/Spec/composition.ts and its Web Components counterpart — where composed slot content resolves a primitive
adr/075-primitive-style-prop-mapping.md — the concept set and the no-value-mapping decision
Text fixtures
- A card whose slot content includes a badge containing an icon at a non-default size — the reported case, and the smallest reproduction
- A component whose composed icon uses the design system's default size, confirming the fallback still renders
- A composed icon inside a nested instance, checking the rule survives more than one level of composition
Notes
Related to ADR-074 (primitives resolve at emit time), ADR-075 (the concept set and value mapping), and ADR-076 (the root constraint on container bindings).
Implementation details are tracked internally.
Problem
Composed slot content now resolves primitive layers to the design system's own components (ADR-074). A glyph layer becomes the library's icon component, carrying the concepts the binding maps — colour and name.
It does not carry size, and for an icon component that sizes itself from a prop, the result renders wrong.
In a Card's default example, the badge's icon is authored at 16×16. It emits as:
The icon component has two independent size channels:
data-sizeattribute driven by asizepropsizeis never passed, so it stays at its default and the inner glyph renders 24×24 inside a 16×16 root. The root clips its content, so the icon comes back cropped.Before the binding existed, a composed glyph emitted a self-contained masked span carrying its own dimensions inline, and rendered correctly at any size.
Why it is not simply an oversight
ADR-075 deliberately excluded
sizefrom the glyph concept set. The reasoning was that sizing should travel as sizing — the spec records width and height, and every platform already understands those — rather than becoming a per-library enum that every other design system then has to model.That reasoning holds right up until the component ignores its own box, which is what an icon component with a
sizeprop does.The information needed is already present and unambiguous: the composed width and the component's own extra-small variant resolve to the same sizing token. Nothing has to be guessed; it has to be matched.
Options
sizeas a glyph concept, matched on token identity. Compare the composed dimension against the sizing tokens the bound component's own variants use, and pass the variant that matches. Renders correctly, needs no hand-maintained table, and stays one concept. It reopens ADR-075's decision to keepsizeoff glyphs and Decision 4's rule that conventions map prop names and never values.sizewith a declared value table in conventions. Explicit and simple to read, but it is the unbounded, drift-prone mapping ADR-075 Decision 4 rejected: it grows with the design system and silently rots when a token is renamed.The same question will apply to any concept where a component overrides its own box from a prop. Icons are simply where it shows first.
Acceptance criteria
Workspace
Found in a design system library workspace while reviewing composed slot content in Storybook, after enabling container, text and glyph primitive bindings for the first time.
Impacted code
packages/react-from-specs/src/Spec/primitives.ts— the glyph concept set and its attribute emissionpackages/webcomponents-from-specs/src/Spec/primitives.ts— the same, for custom elementspackages/react-from-specs/src/Spec/composition.tsand its Web Components counterpart — where composed slot content resolves a primitiveadr/075-primitive-style-prop-mapping.md— the concept set and the no-value-mapping decisionText fixtures
Notes
Related to ADR-074 (primitives resolve at emit time), ADR-075 (the concept set and value mapping), and ADR-076 (the root constraint on container bindings).