Case 358/980: the gate that measured the box
A CSS rule the browser drops without a word, two thirds of every slide clipped on a phone, and eight green gates — because all eight measured the box
🕵️ Show the full case file → 📋 Session protocol →
Case File · Code Homicide Dept.
The slide was clipped on a phone: two thirds of each one, off the screen. The rule that scaled it was written, legible, and had been sitting there for days: <code>transform: scale(calc(100cqw / 980))</code>. The browser reads it, does not understand it, and drops it — no exception, no warning, not one line in the console. It looked right because the article's column is about 980 px and the slide fitted there anyway. And the eight gates watching this work stayed green, all of them, the whole time. Not one lied: they measured the box, and the box was exactly what it was supposed to be. The content, inside it, was still 980.
Show glossary
- invalid declaration
- A CSS rule the browser does not understand does not break the sheet: it drops that single declaration and reads on. It is a design decision of the language, and a good one — that is how a modern sheet fails to kill an old browser. Its price is this case: no exception, no warning, no line in the console. The rule you wrote simply does not exist. And everything that rule was going to change stays exactly as it was.
- proxy for the capability
- A gate measures one thing and concludes another. It measures that the box has the right width and concludes that the slide is visible. It measures that there are 16 nodes in the DOM and concludes that there are 16 legible slides. What is measured and what is asserted are neighbours, not the same thing — and a whole defect fits in the gap. ADR-072 says it in one line: a check that measures a proxy for the capability is a check that can be GREEN while the page is wrong.
- innerText / textContent
- Two ways of pulling the text out of an element, and they do not answer the same question.
textContentreturns everything in the tree, visible or not.innerTextreturns what the browser has PAINTED. On this served page the difference is exact: 1,927 words are in the DOM; 19 reach the reader. Neither figure is false. The trap is picking the one that answers the question you did not ask. - specificity
- When two CSS rules paint the same element, the more specific one wins, and specificity is counted as a three-digit number: (ids, classes, elements). It matters here for a property that surprises almost everyone:
:not(a, b)INHERITS the specificity of its heaviest argument, so every class you add to an exemption list bumps the rule's weight up a notch.:not(:where(a, b))contributes zero: the list can grow without moving the weight. - projection
- An artifact generated from a declared source. Drift is fixed by re-running the generator, never by editing the served copy (ADR-057). The deck in this case IS a projection: the source is the talk's markdown, and the site page is captured from the renderer that already knows how to paint it. Slidev's markdown is never parsed — not in Nushell, not in Rust: that would be a second implementation, and a second implementation drifts.
The protocol to declare, version and verify this → ontoref.dev
The double ledger — what it cost, and what it left
The figures on the left are the body; the ones on the right are what took its place. And the proportion presiding over the case is the one in its number: 358 and 980. The box, correct. The content, intact inside it, measuring nearly three times its width. The two figures coexisted for days, measured, green and published. Neither was false. Nobody simply divided them.
What the crime cost
- The declaration holding up the scale of every slide scale(calc(100cqw / 980))
- CSS.supports() on that declaration, in the browser false
- What the CSSOM stores when you assign it "" (empty string)
- Errors, warnings or console lines it produced 0
- Width of the slide's box on a 390 px phone 358 px
- Native width of that same slide's content 980 px
- Share of every slide clipped if the scale does not apply 63.5 %
- Gates in the suite that already existed: A·B·C·D·E·F·H·I 8
- …that compared the CONTENT against its box 0
- Slides counting the STRING in the served HTML (grep -o slidev-page) 32
- Slides counting the ELEMENT in the DOM (querySelectorAll) 16
- Words Ctrl+F reaches as served (innerText) 19
- Words present in the DOM, always indexable (textContent) 1,927
- Words the reader reaches in "Read all" 2,152
- Words before this work: the deck was a carousel of PNGs 0
What the case left behind
- The rule measure the thing you assert, not the one next to it
- Gate J compares the CONTENT against its box, across 4 viewports
- What gate J measures today 0.994 · 0.997 · 0.998 · 0.998 (content fill)
- The scale a ResizeObserver computes the ratio as a NUMBER in --deck-k
- The guard :not(:where(…)) contributes zero and pins the weight at (0,1,0)
- The suite, run against the served page 10 gates, all green
- What gate F found in passing /public/r/glosario-en.json → 404
The point of abandonment — what the table doesn't show
The point of abandonment was not failing to measure: it was measuring, and being satisfied. Every one of this session’s seven traps was found by running — the CSS asked of the browser instead of picked by hand, the rendered DOM instead of the markdown, the served page instead of the generated file. That instinct was right and it is why the work landed. But measuring is not the same as measuring what you assert, and I crossed that line eight times in a row without noticing, because on all eight the number came back correct. There is no sensation of error in reading a correct number. It feels exactly like being right. What broke the tie was not a better idea: it was opening the deck on a phone.
The suspects — the false leads
| The site's CSS leaks into the deck and crushes the scale | “"The site's CSS leaks into the deck and crushes the scale." It is the first hypothesis and the most natural one: the deck lives embedded in a site article, surrounded by the site's styles, and a clipped slide looks exactly like that. Ruled out by gate C, which exists precisely to answer this question: the site's rules do not reach inside the deck. The gate was green and it was right. Nothing was getting in. And that is the first warning of what happens here: a green gate telling the truth does not bring you one step closer to the defect, if it is answering a different question.” | ruled out — gate C was right, and it was no help |
| Slidev's <style scoped> wins on specificity | “"Slidev's <style scoped> wins on specificity." This one was TRUE, and it was not the cause. The <style scoped> compiles to .print-slide-container[data-v-HASH], which weighs (0,3,0) and pushes the whole transform stack to identity. A real problem, found along the way, fixed by binding the rule to [data-js] to reach (0,4,0). And with that, the clipping was still intact: you can win the specificity war as hard as you like — an invalid declaration still does not exist after you have won it.” | ruled out — it was true, and it was not the cause |
| The box does not have the width it should — it is a layout problem | “"The box does not have the width it should." It is what you conclude on seeing content spill out of a box: if something overflows a box, the box is wrong. Ruled out by measuring it: 358 px on a 390 px phone. Correct. Exact. What it was supposed to be. The box was right the WHOLE time, and that is why eight gates looked at it and saw nothing: there was nothing to see in it. The body was inside.” | ruled out — the box was right the whole time |
| "If the rule is written in the sheet, the browser applies it" | “"If the rule is written in the sheet, the browser applies it." GUILTY. And it is nobody's: it is the default belief. CSS does not validate, does not throw, does not write to the console: it drops the declaration it does not understand and reads on — and that is a virtue of the language, because that is how a modern sheet fails to kill an old browser. scale() demands a number and calc() cannot divide one length by another, so scale(calc(100cqw / 980)) is not CSS. Checked in the browser, with nothing in between: CSS.supports(...) returns false, and on assignment the CSSOM stores an empty string. The rule was written, it was legible, and it did not exist. And everything it was going to change — the content — stayed exactly as it was, while everything the gates were measuring — the box — remained impeccable.” | GUILTY — and it is everyone's belief |
The weapon — A declaration the browser reads, fails to understand, and drops — no exception, no warning, not one line in the console
/* The rule holding up the scale of all 16 slides. It looks reasonable.
The browser reads it, does not understand it, and DROPS it. No exception, no warning. */
.deck-slide .print-slide-container {
transform: scale(calc(100cqw / 980)); /* ← invalid: scale() demands a NUMBER,
and calc() cannot divide length by length */
}
/* Verified in this session, in the browser, with nothing in between:
CSS.supports('transform', 'scale(calc(100cqw / 980))') → false
div.style.transform = 'scale(calc(100cqw / 980))'
div.style.transform → "" (it stored nothing)
It looked right because the article's column is ~980 px wide: unscaled, the slide fitted
anyway. On a 390 px phone the box is 358 px — and the content was still 980.
1 − 358/980 = 63.5 % of every slide, clipped.
And the eight gates that already existed stayed GREEN, because all eight measured THE BOX:
the box was 358 px, which is exactly what the box was supposed to be. Every one of them
told the truth. Not one of them was looking at the content. */
And here is the part that makes this a case file and not a slip. This work was watched by eight gates —containment, fidelity, no inbound leak, glossary, slides, fonts, navigation, zoom— and all eight were green the whole time. Not one lied. The gate that counted slides counted 16 nodes and there were 16. The one that measured the box measured 358 px and the box was 358 px. The one checking that no foreign styles got in was right: none did. Eight truths, and not one of them was the assertion I believed I was checking — that a reader can read the slide. No gate compared the content against its box. The one division that was needed —980 inside 358— was being done by nobody.
The turn — Compute the ratio as a number, and add the one gate that looks at the content
// 1. The ratio CSS cannot express — box width / native width — is computed by whoever
// CAN measure it, and left written down as a plain number.
var W = 980;
var ro = new ResizeObserver(function (entries) {
for (var e of entries) {
var k = e.contentRect.width / W; // ← a NUMBER, not a length
e.target.style.setProperty('--deck-k', k);
}
});
.deck-slide .print-slide-container[data-js] {
transform: scale(var(--deck-k, 1)); // ← (0,4,0): above Slidev's
} // [data-v-HASH], which is (0,3,0)
// 2. And the gate that did not exist — the only one that looks at the CONTENT, not the box.
// It does not fix this defect: it fixes the whole class.
const m = await p.evaluate(() => {
const s = document.querySelector('.deck-slide[data-active]')
const c = s.querySelector('.print-slide-container')
const sb = s.getBoundingClientRect(), cb = c.getBoundingClientRect()
return { boxW: sb.width, cW: cb.width } // ← the box AND the content
})
const fill = m.cW / m.boxW // ← and the ratio between them
if (!(fill > 0.97 && fill < 1.03))
fail('J_escala', `content at ${(fill*100).toFixed(0)} % of its box (clipped)`)
// Across 4 viewports — 1920, 1440, 820 and 390 px. The box is still measured.
// Nothing is CONCLUDED from it any more.
The box is still measured. That is worth saying, because this case’s lesson is told wrong if it is told as “measuring the box is bad”. Measuring the box is fine. What was wrong was concluding from the box something the box does not say. The eight gates are still there, green, useful, and untouched: a ninth has been added to them, doing the one division none of them did. And a debt remains, stated here and not buried in the last paragraph: the ten gates are still not in just check. They are green and they are outside the chain, which is precisely the pathology of case 0/318 — an impeccable validator nobody runs. This case has not closed it. It has named it.
The verdict
The series asks: with ontoref, would it have gone like this? And this case answers with a very specific discomfort, because the mechanism that would have caught it was already written, and it has a number. ADR-072 says, unambiguously, that a check measuring a proxy for the capability is a check that can be green while the page is wrong; this series’ own howto repeats it in capitals —“verify the RENDERED CARD, not a number in the HTML”—; and case 3/0 is literally the case of a check that only ever verified what EXISTED and never what SHOULD exist. All of that was in the repository before this deck was ever projected. And still, eight new gates were born measuring the box. Because the rule, written, is not enough: you have to ask each gate what it asserts, and then look at whether that is what it measures — and there is nothing more uncomfortable to look at than a gate of your own, green, telling the truth. Across the seven traps of this session the failure was always the same: the box instead of the content, DOM nodes instead of what rendered, innerText instead of textContent, the string in the HTML instead of the element in the tree. Four variants of one error, and not one of the four ever produced an error message. Measuring the thing next door does not fail. That is the entire problem.
| A gate that measures a proxy for the capability | The gate declares WHAT it asserts and measures exactly that. "The slides are visible" is not proven by measuring the box: it is proven by comparing the content against the box. Gate J does it across four viewports, and today it measures 0.994–0.998. It is one single division more than the other eight were doing — and it is the difference between a gate and an ornament. |
| A language that fails silently by design | CSS drops what it does not understand and will never say so. That cannot be repaired from here: it is CONTAINED by measuring the effect instead of trusting the cause. The rule was written, it was legible, and it did not exist. The only thing that could give it away was looking at what the rule was supposed to have changed. Containing is not curing, and the case says so instead of glossing over it. |
| A number that comes from counting the string instead of the element | The slide count came out at 32 against 16 real ones, because grep -o slidev-page counts the STRING — and that class appears twice per slide. querySelectorAll('.slidev-page').length counts the ELEMENT, and returns 16. The same trap one level down: a correct number about the wrong question is indistinguishable from a correct number. |
| Picking the method that answers a different question | textContent says what is in the tree; innerText says what has been painted. On this page: 1,927 and 19. Both true, and only one supports the assertion "the reader reaches the text". Gate E prints BOTH, always, instead of picking the convenient one: a figure you put on display cannot be used to fool yourself. |
| An exemption list that raises the rule's weight as it grows | :not(a, b) inherits the specificity of its argument, so the boundary contract got heavier with every class added to it — and at two exemptions it was already stepping on the slides' scale. :not(:where(a, b)) contributes zero. The list can grow without moving the weight: the mechanism stops expiring from being used. |
| A suite of gates nobody runs | The ten gates exist and today they are green against the served page. They are NOT in just check yet. A generator outside the chain is a generator that does not run, and a gate outside the chain is a gate that does not watch: it is the very pathology of case 0/318, and this case declares it open rather than assuming it closed. |
Case law — what enforces the lesson today
- ✓A gate declares what it claims and measures exactly that — never the box next door
adr:072/a-check-measures-the-capability-not-a-proxy - ✓A set of gates nobody runs is not coverage: the gate enters the chain
adr:071/the-gate-is-in-the-chain
One thing remains to be said, and it files this case next to the others in the series. Case 1/1852 verified the file and not the page. Case 3/0 checked what existed and never what should exist. Case 69/58 looked at the tree nobody serves. Case 0/318 wrote an impeccable validator and never ran it. All four have the same skeleton as this one: an artifact asserts something about itself, and what gets checked is the thing next door. Change the neighbour —the file instead of the page, the tree instead of the served one, the box instead of the content— and nothing else changes. That is why this case does not add a gate and fall silent: it adds the question you have to put to any gate before believing it. What do you assert? And is that what you measure? If the two answers are not the same sentence, the gate is green and decorative, and the day the page breaks you will hear it from a reader with a phone in their hand.
Glosario
Sin coincidencias.