Problem
A focus ring captured as a Figma layer becomes a real DOM element with hand-computed geometry. In deButton:
<span className="de-button__focus-ring" data-element="focusRing"></span>
with CSS that pins its size per variant:
.de-button__focus-ring { height: 36px; border-radius: 8px; }
.de-button[data-size="Medium"]:focus-visible .de-button__focus-ring { height: 44px; }
The button is 28px and the ring is 36px — the difference is a constant someone measured, not a relationship. Three consequences:
- Retheme the button's height and the ring stops matching
- Add a size variant and the ring must be hand-extended to cover it
border-radius: 8px is hardcoded while the root's radius is a token, so retheming the corner breaks the pairing
There is also an empty element in the markup that draws nothing on its own, and (until recently) it was a <div> inside a <button> — invalid content model.
Why this is filed rather than fixed
The obvious fix — have the transform recognise a focus-ring layer and emit CSS instead — is pattern inference, which this pipeline has been deliberately removing. It would mean guessing from an element's name or its styling that it is "a focus ring," which is exactly the class of heuristic that produced the glyph-mask box bug (isGlyphLike treating an instance with a name prop as a glyph) and the enum-label size parser (glyphConfigSize reading "20x20" as pixels). Both were deleted rather than relocated.
So the transform should not be taught to detect this shape.
Options
Upstream (preferred). Don't draw focus rings as layers. A focus indicator is a platform affordance, and the roles docs already recommend leaving focus unclassified and using the platform default, which meets contrast requirements and matches user expectation. This makes the whole problem disappear at the source.
Declared. If a library genuinely wants a specced ring, it should say so — an annotation, or a states classification the transform can act on without guessing. That fits the declared-not-inferred rule, but it is new vocabulary for a narrow case.
Derived geometry. Keep the element but emit inset: -4px and an inherited radius rather than per-size pixel constants. This does not need inference — it is a better emission of the same captured data — and it removes the per-variant patching even if the element stays.
The third is worth doing regardless of the first two, because it is a strict improvement with no new signal required.
Notes
Surfaced by an independent review of generated output for DE Button and DE Favorite button. Related: the @layer specs wrapping and the UA-reset block both landed already.
Problem
A focus ring captured as a Figma layer becomes a real DOM element with hand-computed geometry. In
deButton:with CSS that pins its size per variant:
The button is 28px and the ring is 36px — the difference is a constant someone measured, not a relationship. Three consequences:
border-radius: 8pxis hardcoded while the root's radius is a token, so retheming the corner breaks the pairingThere is also an empty element in the markup that draws nothing on its own, and (until recently) it was a
<div>inside a<button>— invalid content model.Why this is filed rather than fixed
The obvious fix — have the transform recognise a focus-ring layer and emit CSS instead — is pattern inference, which this pipeline has been deliberately removing. It would mean guessing from an element's name or its styling that it is "a focus ring," which is exactly the class of heuristic that produced the glyph-mask box bug (
isGlyphLiketreating an instance with anameprop as a glyph) and the enum-label size parser (glyphConfigSizereading"20x20"as pixels). Both were deleted rather than relocated.So the transform should not be taught to detect this shape.
Options
Upstream (preferred). Don't draw focus rings as layers. A focus indicator is a platform affordance, and the roles docs already recommend leaving
focusunclassified and using the platform default, which meets contrast requirements and matches user expectation. This makes the whole problem disappear at the source.Declared. If a library genuinely wants a specced ring, it should say so — an annotation, or a states classification the transform can act on without guessing. That fits the declared-not-inferred rule, but it is new vocabulary for a narrow case.
Derived geometry. Keep the element but emit
inset: -4pxand an inherited radius rather than per-size pixel constants. This does not need inference — it is a better emission of the same captured data — and it removes the per-variant patching even if the element stays.The third is worth doing regardless of the first two, because it is a strict improvement with no new signal required.
Notes
Surfaced by an independent review of generated output for DE Button and DE Favorite button. Related: the
@layer specswrapping and the UA-reset block both landed already.