Skip to contentWolf-Rayet

Decision records

ADR-0026 A tooltip is an Emitter; transience is a presence property, not a layer

Accepted2026-09-01Phase 4, reopened by ADR-0021

#Context

Eleven components have now declared a layer, and ten of them declared it the way §3 intends: read the component's own behaviour, answer the authority question, write down the answer. None of the ten had a rival answer with a case behind it. ADR-0021's table lists tooltip as an Emitter at levels 1 to 2, and that entry was written while surveying forty-three components in an afternoon — it is provenance, not a decision, and §3's claim about itself is what makes the difference matter.

§3 says the authority question "has exactly one answer per component and no ambiguous cases." ADR-0021 reopened the roster specifically to test that claim against components this system did not design. A tooltip is the first one on the list where the claim is under genuine pressure, and taking the table's word for it would be spending the test to save an argument.

The pressure is real and comes from three directions at once.

It is transient. Every other component in the set is resident: an indicator, a row, a field, a tag are on screen because the screen has them. A tooltip is on screen because someone pointed at something, and it leaves when they stop. Nothing in the layer model has a word for that.

It is summoned. The others are placed by the product. This one is produced by an act of the reader, which makes it the first component whose existence is a response rather than a statement.

It occludes. Alone in the set, it sits on top of its neighbours rather than beside them. The components that do that elsewhere in the roster — modal, popover, toggletip — are all Scopes in ADR-0021's own table, declaring the overlay stacking context and grounding on the scrim composite per ADR-0015 and ADR-0016. A tooltip that is an Emitter is the one occluding component in the roster that is not a Scope, and that asymmetry has to be justified rather than noticed later.

What breaks if this stays undecided is not the component — a tooltip renders either way. What breaks is the enforcement. COMPONENT_LAYERS feeds wr/nesting, the slot contract is AcceptedBy<'emitter'> or AcceptedBy<'scope'>, and the two answers permit different children and different grounds. A layer chosen by inertia is a lint rule enforcing a decision nobody made.

#Decision

A tooltip is an Emitter. Its legal levels are Ambient through Marked. Transience and summoning are properties of a component's presence, not of its authority over attention, and the layer model deliberately has no word for them.

Each rival answer fails on §3's own question rather than on taste. It is not a Scope: a Scope "owns a budget and distributes it", and a tooltip has nothing to distribute to — it carries a string and hosts no children. That is exactly the line between it and popover, which stays a Scope: a popover is invoked, holds content, and therefore has somewhere to allocate. This sentence named toggletip until ADR-0076, and the name is the only part of it that changed — that record found a toggletip is a popover and the Button that opens it, so what stands on the far side of this line is the surface rather than the trigger. It is not an Arbiter: it ranks nothing. It is not Substrate: Substrate cannot emit, and a tooltip paints above what it covers. It is not dropped: ADR-0021 drops a component whose premise this system has no position on, and §3's own Emitter examples name a label.

Transience is answered by what the engine measures. The budget is computed over a rendered frame: an unsummoned tooltip costs nothing because it is not in the frame, and a summoned one costs exactly what it emits. Every Emitter in the set is already conditionally present — an alert appears when something failed, an inline loader while something is pending — so this component differs in *what summons it*, which is not a question §3 asks.

The consequence that makes this load-bearing. Only a Scope declares a stacking context (ADR-0013) and only a stacking context declares a ground (ADR-0015, ADR-0016). An Emitter declares neither, so this system's tooltip renders inside its anchor's Scope and is never portalled to the document body. A portalled tooltip would emit into a region whose budget it does not belong to while the region it visually occupies paid nothing for it — the substrate laundering ADR-0009 refuses at the content boundary, arriving through the stacking context instead. Being an Emitter is what forbids it, and packages/react/src/tooltip.css carries no z-index and no fixed position as a result.

The ceiling, with a reason ADR-0021 does not give. That record stops the tooltip at Marked because "a label attached to a control describes it and does not direct anyone to it," and §2's Directed is "clearly the local focus" — the anchor is the local focus and this is the anchor's description. The second reason is independent and stronger: a tooltip is summoned by an attention act that has already succeeded. The reader is already pointing at the control. Emission above Marked spends budget attracting attention that, by construction, has already arrived.

The floor stays at Ambient, and the argument for raising it is recorded because it is the best one against this record. A tooltip occludes, and an occluding element at "minimal deviation from the substrate" is one nobody can tell from what it covers — which would make Ambient an unreadable state and the floor a lie. It fails against the tag's own precedent. A tooltip is a bounded chip lifted off its ground by surface-raised, which ADR-0025's component established as "a step in position rather than a fourth thing drawn," and which works with no hue to spend. Findability comes from the ground. The ground is not the level.

Placement is a second axis, not a variant. above and below select no mark, because no ramp in this system resolves one differently from the other. The table row's alignments is the precedent.

#Rejected options

A tooltip is a Scope. The strongest alternative, and the one the roster's shape argues for: modal, popover and toggletip all occlude and are all Scopes, so a tooltip filed anywhere else makes the roster's occluding components inconsistent. It would also get the ground right by construction, compositing on the scrim per ADR-0016 rather than inheriting whatever it happens to sit in. It lost on the definition. A Scope owns a budget *and distributes it*, and distribution requires children; a tooltip's entire payload is one string, so a Scope contract here would declare an allocation with nowhere to send it. The consistency argument also inverts on inspection: what those three share is not occlusion but hosting. A modal hosts a form, a popover hosts controls, a toggletip hosts interactive content a reader can click into. A tooltip hosts nothing, and grouping it with them by the visual property rather than the structural one is precisely the failure §3 derives its own tiers to avoid.

Add a sixth layer for transient components. It would name the real difference instead of arguing it away, and it would give modal, popover, toggletip and this component one home. It lost twice. §3's tiers are derived from a single question — what authority does this have over attention? — and "how long is it on screen" is not an answer to that question, so a tier holding it would be the first one in the model borrowed from somewhere else, which is the exact error §3 opens by naming in atomic design. And it would be a tier with no enforcement behind it: the engine measures frames, the lint reads nesting, and neither has anything to say about duration. ADR-0002 is closed at five names, and reopening it to add a name nothing checks would make the model larger and no more decidable.

Take ADR-0021's table as the decision and build it. Free, already written, and it arrives at the same answer this record does. It lost because arriving at the right answer by inertia leaves nothing to appeal to when the next occluding component is filed, and because ADR-0021 reopened the roster *specifically* to test §3's claim of no ambiguous cases against components this system did not design. A test whose first hard case is settled by citing the table being tested is not a test. The answer is the same; the difference is that TOOLTIP_RENDERS_IN_PLACE and the absent z-index now exist, and neither would have.

Drop it, as ai-label and menu-buttons were dropped. Defensible on a narrow reading — if the layer is genuinely ambiguous, ADR-0021's own remedy is to drop by name with the reason. It lost because the premise is not the problem. ai-label was dropped because this system has no position on encoding provenance; it has a clear position on a label describing a control, and §3's example list names one. Dropping a component this system *can* answer, because answering it took an argument, would be the coverage claim ADR-0021 refuses.

#Consequences

§3's claim survives its first hard case, and is narrower than it looked. "Exactly one answer per component and no ambiguous cases" holds here — but only after separating the authority question from two properties that travel with it in ordinary design vocabulary. The claim is about authority, and it is decidable about authority. It was never a claim that a component's layer is *obvious*.

A rule for the roster's remaining occluding components. modal, popover and toggletip are Scopes because they host, not because they occlude. When each is built it answers the hosting question rather than re-litigating this one, and a future occluding component that hosts nothing is an Emitter by this record.

ADR-0076 applied that instruction and it removed one of the three. popover and toggletip answer the hosting question with the same word — both host arbitrary content anchored to something else — so the rule did the work it was written for, and toggletip is a popover and a Button. Two of the three are now decided: modal was built under ADR-0037, and popover is the roster's last Scope. And a clause of this record was already false about them. Carbon builds its tooltip *on* its popover; the paragraph above forbids that here, because an Emitter declares no stacking context and this system's tooltip therefore renders inside its anchor's Scope. A popover here has one consumer rather than three, which is the second reason that pair collapses to one row rather than to a primitive and a product.

One new name in core. MARKED_LEVEL and MarkedLevel, derived and named for the reason AMBIENT_LEVEL was: a component whose ceiling is Marked has to say which rungs it excludes, and saying them as 3 and 4 would put level numbers outside attributes.ts. This is the first component whose ceiling landed there, so there was nothing to derive until now.

The enforcement surface does not grow. As ADR-0021 predicts, the pairing matrix stays at 194 cells across 10 dimensions — it is keyed on layers and levels, never on component names, and this record adds neither. What it adds is one fixture scene, 25-tooltip-calm.html, whose controlled pair prices a summoned tooltip against a dismissed one. That pair is the record's own claim under measurement: transience is free only if an unsummoned tooltip is genuinely absent from the frame, and a tooltip faded with opacity: 0 would still paint, still be priced, and look identical in a screenshot.

The component is harder to use than the industry's. It does not portal, it does not decide its own visibility, and it needs an anchor that establishes a containing block. All three are this record's consequences rather than gaps, and the contract says so in TOOLTIP_RENDERS_IN_PLACE rather than leaving the next reader to find out from a stylesheet.