Skip to contentWolf-Rayet

Decision records

ADR-0040 A collapsed panel is absent rather than exempt, and a demand may not be the thing that is hidden

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

#Context

Nineteen components are built. Every one of them renders everything it is given: an Arbiter draws its children, an Emitter draws its string, and a Scope draws what it hosts. The accordion is the first component in the roster whose children can be *in the markup and not on the screen*, and that is a state no record in this system has had to price.

The engine's answer today is one predicate, and it was written for a different question. packages/budget/src/measure.js computes

rendered: rect.width > 0.5 && rect.height > 0.5
  && cs.visibility !== 'hidden' && cs.display !== 'none'
  && parseFloat(cs.opacity) > 0.001

and packages/budget/src/engine.js spends it twice. A Scope's members — the set its emission load, its attribution and its dominance contest are computed over — is filtered on n.rendered. So is the View's demand count:

const viewDemands = dom.nodes.filter((n) => n.rendered && n.level === DEMAND_LEVEL && !n.inExemptSlot);

So the behaviour with a Demanded element inside a collapsed panel is already determined and has never been chosen. Under display: none the element's border box is 0×0 and its display is none, so rendered is false on three of the five clauses at once: the element leaves members, contributes no emission, owns no pixels in the attribution map, enters no dominance contest, and — the half that matters — leaves viewDemands and therefore counts against neither SCOPE_DEMAND_CAP nor VIEW_DEMAND_CAP. A demand inside a closed panel is free today. No record says it should be, and the next person to hit it reads a predicate rather than a decision, which is the failure mode ADR-0037 named in this exact position and refused to leave standing.

The predicate also has a second answer, which is worse and is reachable by an ordinary implementation choice. Collapse written as height: 0; overflow: hidden clips the panel without changing the child's own border box. The child's rect stays at its natural size, so rendered stays true: the demand counts against both caps while painting nothing, and ownershipMap in engine.js — which fills the owner map from node.rect and consults no clip — credits that child with every pixel inside its rect, which is whatever is actually drawn there. That is not a policy disagreement. It is an attribution the receipt would report as fact and which is not true of the frame. Two implementations of one visual behaviour therefore give opposite answers to the salience question and one of them corrupts the emission numbers of an unrelated element, and nothing in the system currently prefers either.

What breaks if this stays undecided is not the accordion; it collapses either way. What breaks is that the system's most repeated refusal — you may not make emission or demand disappear by declaring something about it — acquires an undocumented exception whose key is a CSS property.

#Decision

A collapsed panel's children are absent from the frame, not exempted from the budget. They carry no emission and count against no cap, and that is the engine's existing rule rather than a new grant: this budget is computed over a rendered frame, which is the same sentence ADR-0026 used to answer transience — "an unsummoned tooltip costs nothing because it is not in the frame." n.rendered is correct as written and is not amended.

Because absence is free, an accordion may not collapse a panel that holds a Demanded element. A demand inside a closed panel is a demand with no representative on the screen: the reader is asked for nothing, told nothing, and given no mark to act on, while the product believes it has escalated. The demand is lifted to the header, which is on screen, or the panel is open. This is enforced rather than encouraged — a declared level-4 node that is present in the DOM and absent from the frame is a build failure naming the element, its Scope, the View and this record, under a new check kind, Salience — hidden. It reads declarations and no pixels, so it is a static property of the scene exactly as the two cap checks are.

The exemption is reported whether or not it is exercised. Every receipt carries the declared-but-unrendered demand set beside the counted one, so a scene measuring one demand says which of "one demand" and "one demand and one you cannot see" it means. A silent exemption and no exemption are indistinguishable in a receipt, which is the property ADR-0034 refuses of a CI step and ADR-0029 refuses of a first record.

Collapse is display: none, reached through the hidden attribute, and never height: 0; overflow: hidden. The mechanism is part of this decision because the decision is only true under one of the two: the clipped form leaves the child's border box at natural size, so rendered stays true, the demand is counted while invisible, and the ownership map credits the hidden child with pixels drawn by whatever is really there. packages/react/src/accordion.css carries no overflow: hidden and no zero size on the panel, Accordion.tsx writes the hidden attribute, and the discipline test asserts both.

And the attribute is not self-executing, which was found by measurement rather than by reading. hidden takes effect through the UA stylesheet's [hidden] { display: none }, a presentational hint at the lowest priority in the cascade. A panel that lays its children out sets display on a class selector, which outranks it. The first build of this component did exactly that — .wr-accordion__panel { display: flex }, the attribute written, no clipping anywhere — and 41-accordion-hidden-demand.html, the scene declared must-fail by this record, passed: the receipt reported #seal-pressure rendered at 266×46 and counted it one of one against the View. Nothing was wrong with the engine, the attribute or the argument; the stylesheet had quietly cancelled the mechanism the argument rests on. So the rule is stated as a rule about the stylesheet: a component that discloses by removing must carry an explicit [hidden] { display: none } on the element it removes whenever that element sets a display of its own. The two checks written before this was discovered — "does not clip" and "writes the attribute" — were both green on the broken file, which is why a third one exists that reads the cascade instead of the intent, and it was proved to fire before it was made to pass.

#Why this is not the laundering the other three records refuse, argued rather than asserted

ADR-0009 refuses it at the content boundary, ADR-0026 at the portal boundary, ADR-0037 at the scrim. The three share one structure: the thing is still on the screen, and a declaration is used to make the accounting for it disappear. Bright chrome wrapped in a content slot still emits into the reader's eye and the mask deletes the count. A tooltip portalled to the body still occupies a region, and the region pays nothing while the budget it is charged to is not the one it is visible in. A demand under a scrim is still legible — ADR-0014's own words are that the covered scene "remains visible, dimmed" — and exempting it would leave the reader looking at two demands while the check reports one.

The test each of those fails is the same: the check and the reader disagree, and the check is the one that is wrong. That is what makes an exemption a laundering device. It is not the mere fact that a declaration reduced a number.

Collapse passes that test rather than evading it. The reader sees one demand. The check sees one demand. There is no second thing on the screen whose cost has been moved, dimmed or masked, because there is no second thing on the screen. Excluding a display: none element from a count of rendered demands is not an exemption granted to collapse — it is the count being arithmetically correct about the frame it is a count of. The laundering shape needs a discrepancy to hide in, and this one has none.

#Whether ADR-0037's reasoning transfers, and where it stops

It does not transfer, and the unit's own distinction is the reason: a scrim leaves the covered thing visible and dimmed, and collapse removes it from the render entirely. That difference is decisive in both directions.

It decides the exemption question against ADR-0037's answer. That record's rule — "the scrim dims light, it does not lower a declaration" — is a statement about an element that is still being drawn. Its whole mechanism depends on that: the covered demand is *suspended*, re-declared at Directed for the overlay's lifetime, which means level-3 roles, level-3 tone and level-3 emission. Suspension is a change of level, and a change of level is a change to what is drawn. Applied to a collapsed panel there is nothing to re-declare, because there is nothing being drawn to declare anything about; the machinery does not produce a wrong answer here so much as it has no object. Where ADR-0037 could say "keep counting it, it is still there", this record cannot, because it is not.

It decides the refusal question with ADR-0037, and by an argument ADR-0037 did not need. That record could refuse the exemption and stop, because refusing it left the covered demand counted and the screen honest. Refusing an exemption is not available here, so the refusal has to move: what is forbidden is not the exemption but the act that would exploit it. A screen may not become compliant by closing a panel over its second alarm, and the way that is stopped is that a demand may not be inside a closed panel at all. Same refusal, opposite mechanism, and it is reached from the difference between dimming and removal rather than from ADR-0037's conclusion.

The one thing carried across unaltered is ADR-0037's form of enforcement, which is a general property of a system with no runtime rather than anything about scrims: there is nothing in a fixture to open a panel, so a scene drawn with a demand inside a closed one is wrong at the point it was written, and it fails the build there.

#Rejected options

Collapse does not exempt: a hidden child keeps its declared level and keeps counting, exactly as under a scrim. Merit, and it is real: it is ADR-0037 transferred whole, it needs no new check, it closes the laundering route by construction rather than by a rule about where demands may sit, and it is the answer that never has to explain why this system's most-repeated refusal has an exception. It lost on what the check would then be measuring. Salience — view would fail a View on an element with no pixels, no position and no reader, which is a verdict about product state rather than about a screen — and this engine has never claimed to audit product state. It lost a second time on a line it cannot draw. Every Emitter in this set is already conditionally present, and ADR-0026 ratified that: an alert that has not fired is not in the DOM at all, and it costs nothing for exactly the reason a collapsed one would be made to cost something. A rule that charged for display: none and not for absence from the markup would be charging for the author's choice of how to not-show something, which is the same defect this record's own mechanism clause exists to remove, arriving from the other side.

Exempt, change nothing, and let the predicate keep answering. Merit: it is the behaviour on disk today, it is arguably already correct on the laundering question — the argument above is the argument — and shipping the accordion under it costs one component and no records. It lost for the reason ADR-0037 gave when refusing the identical option: the modal's own required scene had no defined verdict under it, and the accordion's does not either. A screen carrying two alarms, one of them behind a closed panel, would pass or fail according to whether the author reached for hidden or for height: 0, and which of those happens would have been decided by two lines in measure.js rather than by anyone. It also leaves the exemption silent, and a demand that becomes free the moment a panel closes needs a check standing next to it, not a paragraph.

Treat collapse as an occlusion case and reuse ADR-0037's suspension: re-declare the hidden demand at Directed while the panel is closed. Merit: one mechanism covering both cases, no new check kind, and the record is already written and ratified. It lost because suspension is a change of level and there is no element to change. Its consequences — "level-3 roles, level-3 tone, level-3 emission" — are all statements about what gets drawn, and the panel draws nothing; a suspended-but-absent element is a declaration with no rendering, which is precisely the pairing the layer model exists to prevent. It would also make the receipt report a level for something that has no pixels, which is a number a reader cannot check against anything.

Forbid Demanded anywhere inside an accordion, open or closed. Merit: the simplest rule in this set by a distance. It requires no reasoning about rendered at all, it removes the exploit instead of checking for it, and it has a real argument behind it — disclosure is a claim that the contents can wait, and something that demands attention cannot. It lost because it forbids the case the component exists for. A settings section whose expanded panel holds a field that failed validation is the ordinary reason to open a panel, and a rule making it illegal would push the demand out to a sibling, where it says less about what needs fixing and where the reader has to reconstruct the link the panel already drew. It also makes the accordion the only Arbiter with a demand cap of its own, which is new machinery bought to avoid a decision rather than to enforce one — ADR-0037's objection to the overlay demand cap of zero, unchanged.

Count a hidden demand against its Scope but not against the View. Merit: it splits the difference on a real intuition — the panel's own region knows the demand is there even though the screen does not — and it keeps the local check strict while keeping the global one about pixels. It lost because the two caps are one rule read at two ranges. ADR-0010's two-tier ration counts the same objects at Scope and at View, and a demand that is real at one range and unreal at the other is two answers to one question, with nothing to say which range is telling the truth.

Report the hidden demand as a warning rather than a failure. Merit: it surfaces the thing without failing a build over a state a runtime will legitimately produce the moment a reader closes a panel, and it is honest that a *static* fixture is the only place this can be judged. It lost to ADR-0028's finding about the same trade: a check nobody fails on is a comment in an unusual place. It also mistakes what is being judged. The failure is not that a panel is closed — it is that a scene was *authored* with a demand behind a closed panel, which is a decision someone made in a file, not a state a reader wandered into.

#Consequences

What this makes easier. The accordion's scenes have declared verdicts that come from a record: a calm accordion passes, an accordion with an expanded panel holding a demand passes, and an accordion with that same demand inside a *collapsed* panel fails on Salience — hidden. The third is the must-fail sibling this decision requires, and it is the first scene in the set that fails on a node the emission half of the engine never sees. It also settles the remaining disclosure components before they are built — tabs, content-switcher, tree-view and progress-indicator all hide children, and each is now answered: absent is free, and a demand may not be what is absent.

What this makes harder, stated plainly. A View can pass while the product holds two things it would call urgent, one of them behind a panel that happens to be closed. That is a real consequence of measuring frames and it is not softened here. What this record buys is that no *accordion* can be the thing closing over it: inside this component the case is a build failure. Outside one, the system says what it has always said — an element that is not on the screen was not measured, and a design that hides an alarm has made a disclosure mistake that no budget engine is positioned to catch.

A second obligation on the runtime, beside ADR-0037's. That record left suspension to a runtime that does not exist. This one leaves the same shape: a real accordion collapsing over a demand that arrives *after* the panel was closed must lift it to the header, and there is nothing to do that yet. What ships is the static half — the contract, the failing scene, the check — which is what stops the first component with a runtime inventing its own rule.

A check may be green about the wrong thing, and this record has an instance of it. Both mechanism checks passed on a component whose disclosure did nothing, because each asserted an absence — no clipping, no missing attribute — and an absence is satisfied by a component that discloses nothing at all. The lesson is not "write more checks"; it is that a check asserting the absence of the wrong implementation is not a check that the right one works. The scene caught it, which is the argument for keeping a must-fail sibling for every rule rather than only for rules that seemed likely to break.

Enforcement table. Adds one check kind, Salience — hidden, alongside Salience — scope and Salience — view; it reads declarations and no pixels. Adds one receipt field, the declared-but-unrendered demand set, reported on every scene. Emission load is untouched: an absent element emits nothing and there is no cost to move, which is the one place this record is simpler than ADR-0038. measure.js's rendered predicate is unchanged and is now load-bearing on the record rather than by accident.