Skip to contentWolf-Rayet

Decision records

ADR-0076 A toggletip is a popover and a button, and the hosting question answers them the same way

Accepted2026-09-06Phase 4, reopened

#Context

popover and toggletip are the last two rows of ADR-0021 section E. They were audited together before either was built, in ADR-0061's manner, and the material to audit them with was already in the repository.

Section E gives the two rows one sentence between them, shared with a third:

modal, popover and toggletip declare the overlay stacking context and ground on the scrim composite per ADR-0015 and ADR-0016.

That is the shape ADR-0057 named and removed three rows for: *"three components that arrive as a group are three nobody compared."* Neither row has ever carried an argument of its own.

And ADR-0026 already set the test they were to be judged by. That record made tooltip an Emitter, and its Consequences left an instruction for exactly this pass:

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.

#The hosting question answers them identically

modal answered it and was built: it hosts the content of a dialogue that blocks, and it is the one Scope in the set that reaches [3, 4] — ADR-0037's occlusion rule and ADR-0038's charge for what it covers are about that.

popover and toggletip answer it with the same word. Both host arbitrary content anchored to something else on the screen. accepts is AcceptedBy<'scope'> for both, unnarrowed, because the slot contract belongs to the layer; there is no second answer available for one of them to give.

Put to the eight dimensions the test now has, they agree on all eight: Scope, one rung, non-interactive, a name for what they hold, no disclosure, no vouching, no acknowledgement, and neither outlives its route. They agree on accepts, and they agree on the one thing ADR-0073 added for Scopes — they are the same place, anchored above what they cover.

What Carbon separates them on is the trigger, and this system puts triggers outside. A toggletip is a popover that a control opens. The control is a Button, and that is not a preference here — it is the reporting-and-acting split this system has now made four times: ADR-0050 for acknowledgement (*"the control that performs an acknowledgement is a Button"*), ADR-0062 for a process, ADR-0066 for pagination, where core's nesting table made it structural rather than stylistic. A fifth component whose only distinguishing feature is the control beside it would be the same finding refused.

And the second thing Carbon separates them on is whether the content is interactive, which is a claim about what a caller puts inside a container. AcceptedBy<'scope'> admits Emitters, and a Button is an Emitter. A Scope that permitted interactive children and one that forbade them would be two components; two Scopes that both permit them, distinguished by what someone typically supplies, are one.

#toggletip is removed, and popover is the name that survives

Which of the two names survives is the part worth arguing, because the collapse is symmetric on the dimensions and is not symmetric on the naming.

This system names Scopes for where they are — §3's own examples are *"Panel, sidebar, page region, modal, drawer"*, and ADR-0073 made that a rule: a Scope is a place. popover names a place, which is above and anchored. toggletip names a trigger model, and the trigger is a Button that sits outside the component.

So the row that survives is the one whose name describes the thing that remains after the control is extracted, and that is popover. Roster 49 → 48; section E 6 → 5; the Carbon term 36 → 35, dropped 9 → 10.

#ADR-0026's sentence is corrected where it stands

That record's argument for tooltip being an Emitter leans on this pair:

a tooltip has nothing to distribute to — it carries a string and hosts no children. That is exactly the line between it and toggletip, which stays a Scope: a toggletip is invoked, holds interactive content, and therefore has somewhere to allocate.

The argument survives and the name in it does not. What that sentence needs is *a Scope on the other side of the line that hosts what a tooltip cannot*, and popover is that Scope. The correction is a name, not a finding, and it is made in ADR-0026 rather than left for a reader to reconcile.

One clause of it was already false and this audit found it. Carbon builds its tooltip *on* its popover; this system's does not and cannot. ADR-0026's own load-bearing consequence is that an Emitter declares no stacking context, so *"this system's tooltip renders inside its anchor's Scope and is never portalled to the document body."* A popover here therefore has one consumer rather than three, and a base with one consumer is not a base — it is the component. That is a second, independent reason the pair collapses to one row rather than to a primitive and a product.

#Rejected options

Keep both, on Carbon's own separation. Carbon publishes two pages and its own guidance separates them on interactivity and trigger. Rejected on ADR-0021's founding decision, which refused to inherit Carbon's grouping because *"Carbon groups by form and this system groups by authority over attention"* — and on ADR-0026's instruction, which said these rows answer the hosting question, not the summoning one. Transience and summoning were already ruled out as layer questions by that record: *"the layer model deliberately has no word for them."*

Keep toggletip and drop popover, on the grounds that Carbon calls popover a container for building other components. Genuinely tempting, and it is the reading that would follow from Carbon's own documentation. Rejected because the premise is false here: this system's tooltip is an Emitter that renders inside its anchor's Scope, so a popover has one consumer, not three — and because toggletip names a trigger this system puts outside the component, so the surviving row would be named for the half that was extracted.

Build both and let the difference emerge. The option that avoids deciding. Rejected on §11's own word: a component that differs from another in its name is scenery, and the way to find out whether these two differ is to ask the question ADR-0026 left, which this record does.

Add a dimension for whether a Scope's content is interactive. It would separate them, and it would be the third dimension recovered in six passes. Rejected because it is not a property of the component: AcceptedBy<'scope'> admits Emitters for every Scope in the set, and a dimension whose value depends on what a caller happens to supply is a dimension about callers. ADR-0071 recovered vouches and acknowledges because the *tier* declared them and the test had not looked; there is nothing here to recover.

#Consequences

Roster 49 → 48, built 36, unchanged. Twelve rows remain — §C form 6, §D navigation 3, §E surfaces 1, §G 2. The identity roster:check derives moves to 10 dropped + 33 distinct Carbon slugs carried = 43.

Section E has one row left, popover, and it is the last Scope on the roster. When it is built the Scope tier is complete, and ADR-0026's rule for occluding components will have been applied to all three of the components it was written for.

ADR-0026 is amended in two places — the name on the far side of the tooltip line, and the clause about what a popover is a base for. Neither changes that record's decision.

Nothing built, no token, scene or baseline moved. A record, a table row, an exclusion row and two corrected sentences in another record.

#Measured

  • Section E's shared sentence: one argument across three rows, none of the three carrying one of its own before this record — *disk, this pass*.
  • ADR-0026's instruction, quoted above, and its tooltip argument naming toggletip — *disk, this pass*.
  • The eight dimensions applied: agreement on all eight, plus accepts and the Scope place ADR-0073 added.
  • Roster 49 → 48; section E 6 → 5; Carbon rows 36 → 35; dropped 9 → 10; the identity 10 + 33 = 43.
  • Built 36, unchanged. pnpm battery — the verdict this pass is measured by.