Skip to content

test(ui): den Küchen-Breiten-Guard am Effekt messen statt an der Form - #644

Merged
ulsklyc merged 4 commits into
mainfrom
claude/kitchen-guard-followup
Aug 2, 2026
Merged

test(ui): den Küchen-Breiten-Guard am Effekt messen statt an der Form#644
ulsklyc merged 4 commits into
mainfrom
claude/kitchen-guard-followup

Conversation

@ulsklyc

@ulsklyc ulsklyc commented Aug 2, 2026

Copy link
Copy Markdown
Owner

Nachlauf zu #643. Codex hat seine Bewertung des letzten Commits erst nach dem Merge abgegeben - sieben Befunde, alle nachvollzogen, alle umgesetzt.

Zwei davon waren Fehlalarme

Die schwerere Kategorie: der Guard hätte korrekten Code blockiert.

Fall Warum korrekt Guard sagte
.recipes-list { max-width: none } lässt den Scroller ausdrücklich unbeschränkt rot
Kappung in @layer components { … } Cascade Layers gelten auf jedem Viewport rot

Beide hatten dieselbe Ursache: geprüft wurde die Anwesenheit einer Eigenschaft bzw. das @ am Präludium, nicht die Wirkung. Jetzt zählt der Wert, und At-Rules werden nach Bedeutung unterschieden - einschränkend sind @media, @supports, @container, @scope; @layer ordnet nur die Kaskade.

Fünf echte Lücken

  • Natives Nesting. .recipes-list { & { max-width: 20rem } } wurde still übersprungen: der flache Scanner nahm die erste schließende Klammer als Rumpfende. Dieselbe Klasse wie der @import-Fehler aus der letzten Runde - der Guard prüfte weniger, als er behauptet. Er balanciert jetzt und liest verschachtelte Regeln mit aufgelöstem Selektor mit.
  • --content-max-width-narrow: none. Die Existenzprüfung nahm jeden Wert. Der Token muss eine Breite sein, sonst lösen beide Kind-Deklarationen auf nichts auf.
  • .shopping-page .kitchen-rows. Erfüllte die Kind-Zusage, ohne die Rezeptliste je zu treffen. Der erfüllende Selektor darf das Element weder über einen Vorfahren noch über einen Zustand einschränken.
  • Kappung und overflow in getrennten Regeln. Der Browser sammelt die Deklarationen aller passenden Regeln, bevor er den Wert bestimmt. Eine Prüfung pro Block sah in keinem von beiden ein gekapptes, clippendes Element - und genau das war es.
  • margin-inline-end: 20rem. Als gestrecktes Flex-Item zieht eine Inline-Marge direkt von der Randbox ab, ganz ohne Breitenangabe. Der margin-Shorthand zählt mit.

Verifikation

61 Mutationen, jede mit erwartetem Ausgang, 0 Abweichungen. Davon zehn, die grün bleiben müssen - sie belegen, dass der Guard korrekte Arbeit nicht blockiert: :not(), :has(), Pseudo-Elemente, Kind-Selektoren, width: 100%, align-self: stretch, max-width: none, width: auto, @layer, harmlose Custom Properties.

npm test vollständig: 2660 grün, 0 rot. Kein CSS geändert.

Bekannte Grenzen

Unverändert die zwei aus #643, beide brauchen Kaskade oder DOM: ein struktureller Selektor auf ein geschütztes Element (.recipes-list > ul) und eine Ausrichtung, die von einem Vorfahren kommt (align-items am Container).

Nachlauf zu #643, das dort Gefundene nach dem Merge. Zwei der sieben
Befunde waren Fehlalarme - der Guard haette korrekte Arbeit blockiert:

- `.recipes-list { max-width: none }` und `width: auto` galten als
  Kappung. Sie lassen den Scroller ausdruecklich unbeschraenkt. Geprueft
  wird jetzt der WERT, nicht die Anwesenheit der Eigenschaft.
- Eine Kappung in `@layer components { … }` galt als bedingt. Cascade
  Layers ordnen nur die Kaskade und gelten ueberall; einschraenkend sind
  @media, @supports, @container und @scope. Der Parser unterscheidet die
  At-Rules jetzt nach Bedeutung statt nach ihrem '@'.

Dazu fuenf echte Luecken, eine davon wieder im Scanner:

- Natives Nesting (`.recipes-list { & { max-width: 20rem } }`) wurde
  still uebersprungen: der flache Scanner nahm die erste schliessende
  Klammer als Rumpfende. Er balanciert jetzt und liest verschachtelte
  Regeln mit aufgeloestem Selektor mit.
- `--content-max-width-narrow: none`. Die Existenzpruefung nahm jeden
  Wert; jetzt muss der Token eine Breite SEIN.
- `.shopping-page .kitchen-rows` erfuellte die Kind-Zusage, ohne die
  Rezeptliste zu treffen. Der erfuellende Selektor darf das Element
  weder ueber einen Vorfahren noch ueber einen Zustand einschraenken.
- Kappung und `overflow` in zwei getrennten Regeln. Der Browser sammelt
  die Deklarationen aller passenden Regeln, bevor er den Wert bestimmt -
  der Guard tut das jetzt auch, statt jeden Block einzeln zu lesen.
- `margin-inline-end: 20rem` am Scroller. Als gestrecktes Flex-Item zieht
  eine Inline-Marge direkt von der Randbox ab, ganz ohne Breitenangabe.
  Der margin-Shorthand zaehlt mit.

61 Mutationen belegen den Stand, davon zehn, die gruen bleiben muessen.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: bbebd4f885

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread test/test-frontend-audit.js Outdated
Comment thread test/test-frontend-audit.js Outdated
Comment thread test/test-frontend-audit.js Outdated
Comment thread test/test-frontend-audit.js Outdated
Comment thread test/test-frontend-audit.js Outdated
Comment thread test/test-frontend-audit.js
Comment thread test/test-frontend-audit.js Outdated
Comment thread test/test-frontend-audit.js
Codex-Nachlauf zu #644. Vier der acht Befunde sind Folgen der Fixes im
vorigen Commit - der wichtigste ein Loch, das beim Beheben eines
Fehlalarms entstand:

- `max-width: 20rem; width: 100%` kam durch. `width` und `max-width`
  konkurrieren nicht, sie beschraenken gemeinsam; als eine Liste gelesen
  gewann das erlaubte `100%` und die Kappung stand ungeprueft daneben.
  Jetzt wird jede Achsen-Gruppe einzeln ausgewertet.
- `FILLS` war eingefuehrt, aber nirgends benutzt: beide Ausrichtungs-
  Zusicherungen lasen weiter eine hartkodierte Liste ohne `revert`, und
  `align-self: revert` wurde faelschlich abgelehnt.
- `:is(.shopping-page .kitchen-rows)` galt als schlichter Selektor, weil
  die funktionale Pseudoklasse samt Inhalt entfernt wurde. `:is()` und
  `:where()` gehoeren zum Subjekt - ihr Inhalt wird jetzt mitgelesen,
  `:not()` und `:has()` fallen weiter weg.
- Die Regel-Zusammenfassung gruppierte nach Selektortext. `.kitchen-rows`
  und `ul.kitchen-rows` treffen dasselbe Element, standen aber als zwei
  unvollstaendige Eintraege da. Gruppiert wird jetzt nach den Merkmalen
  im Subjekt.

Dazu vier eigenstaendige Luecken:

- `@starting-style` galt als unbedingt. Es liefert Uebergangs-Startwerte,
  nicht die normale Darstellung; datepicker.css nutzt es bereits.
- Eine At-Rule INNERHALB einer Style-Regel trug Deklarationen fuer den
  Elternselektor, die der Parser verwarf.
- Die kanonische Token-Pruefung nahm die erste :root-Deklaration; eine
  spaetere `--content-max-width-narrow: none` schlaegt sie.
- `list.style.marginInlineEnd` blieb ungeprueft - der Inline-Scan kannte
  Breiten und Ausrichtung, aber keine Margen.

69 Mutationen belegen den Stand, davon elf, die gruen bleiben muessen.
@ulsklyc

ulsklyc commented Aug 2, 2026

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e3586bd4f4

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread test/test-frontend-audit.js Outdated
Comment thread test/test-frontend-audit.js
Comment thread test/test-frontend-audit.js Outdated
Comment thread test/test-frontend-audit.js Outdated
Comment thread test/test-frontend-audit.js Outdated
Comment thread test/test-frontend-audit.js
Codex-Nachlauf zu #644, dritte Runde. Ein Fehlalarm, fuenf Luecken:

- `margin-inline-end: 20rem; margin: 0` wurde als Verstoss gemeldet. Der
  Shorthand setzt die Langform zurueck, die Marge ist null. Die Margen
  werden jetzt in Deklarationsreihenfolge aufgeloest statt gesammelt.
- `.kitchen-rows:not(.uncapped)` galt als unbedingte Kappung. In
  targets() muessen :not() und :has() wegfallen - sie sagen dort nur,
  dass die Klasse nicht das Subjekt ist. Fuer die Frage, ob eine Kappung
  IMMER gilt, sagen dieselben Pseudoklassen das Gegenteil: sie knuepfen
  sie an eine Bedingung. Sie bleiben hier jetzt stehen.
- `--content-max-width-narrow: 720px bogus` kam durch: die Pruefung war
  auf ein Praefix statt auf den ganzen Wert verankert.
- Ein frueheres `:root { --content-max-width-narrow: none !important }`
  schlug den gueltigen Wert dahinter. `!important` gilt auch zwischen
  Regeln, nicht nur innerhalb einer; die kanonischen Rumpfe werden jetzt
  zusammen ausgewertet.
- `list.style.setProperty('margin-inline-end', …)` blieb ungeprueft.
- Beim Zusammenfassen ging der Vorfahren-Kontext verloren, wodurch
  `.context-a .kitchen-rows` und `.context-b .kitchen-rows` in einen Topf
  fielen - der Guard hielt das Clipping fuer ausgeglichen, das in
  Kontext A besteht. Kontextfreie Regeln zaehlen weiter ueberall.

75 Mutationen belegen den Stand, davon 15, die gruen bleiben muessen.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1ccdf96aa7

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread test/test-frontend-audit.js
Comment thread test/test-frontend-audit.js
Comment thread test/test-frontend-audit.js
Comment thread test/test-frontend-audit.js
Comment thread test/test-frontend-audit.js
Comment thread test/test-frontend-audit.js
Codex-Nachlauf zu #644, vierte Runde. Beide Befunde sind Folgen der
Fixes im vorigen Commit:

- `margin-inline-end: 20rem !important; margin: 0`. Die neue
  Margen-Kaskade las die Wichtigkeit nicht mit, also loeschte der
  gewoehnliche Shorthand eine wichtige Langform, die im Browser stehen
  bleibt. Jede Achse merkt sich jetzt, ob ihr Wert wichtig gesetzt wurde.
- `.kitchen-rows:hover { align-self: start }` glich eine Luecke aus, die
  im Ruhezustand besteht. Neben dem Vorfahren-Kontext gehoert auch der
  Zustand des Subjekts zum Schluessel; zustandsfreie Regeln zaehlen
  weiter ueberall, zustandsgebundene nur in ihrem eigenen.

77 Mutationen belegen den Stand, davon 15, die gruen bleiben muessen.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a25928bb57

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +2087 to +2088
assert.doesNotMatch(src, new RegExp(`\\b${name}\\.style\\.margin(?:Inline|Left|Right)?[A-Za-z]*\\s*=`),
`${page}.js setzt eine Inline-Marge am Scroller - die zieht als gestrecktes Flex-Item direkt von seiner Breite ab`);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject bracket-notation inline margins

Fresh evidence after the setProperty fix is that bracket notation still bypasses every inline-margin pattern. On a JavaScript-built scroller, list.style['marginInlineEnd'] = '20rem' shortens the stretched element's wheel-hit area just like the dot-property form, but the audit remains green because this regex requires .style.margin...; include equivalent bracket-property assignments in the scan.

Useful? React with 👍 / 👎.

const tokenValue = declaredValue(canonicalBodies, '--content-max-width-narrow');
assert.ok(tokenValue !== null,
'tokens.css muss --content-max-width-narrow unbedingt in :root definieren - ohne die Deklaration löst var(…) auf nichts auf und die Kappung entfällt');
assert.match(tokenValue, /^(?:\d+(?:\.\d+)?(?:px|rem|em|ch|ex|vw|vmin|vmax|%)|(?:min|max|clamp|calc)\(.*\))$/,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Validate function bodies in the canonical width token

Fresh evidence beyond the previously fixed prefix match is that the anchored regex accepts arbitrary contents inside supported function names. For example, --content-max-width-narrow: calc(1px +) passes this assertion, but substitution produces an invalid max-width, so the browser falls back to an uncapped list while the audit stays green; validate the function grammar rather than only its outer name and parentheses.

Useful? React with 👍 / 👎.

.replace(/:(?:not|has)\([^)]*\)/g, '')
.replace(/:(?:is|where)\(([^)]*)\)/g, '$1')
.trim().split(/[\s>+~]+/).pop() ?? '';
return new Set(subject.match(/[.#][\w-]+/g) ?? []);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve element types when combining clipping rules

Fresh evidence beyond the ancestor/state fixes is that subjectKeys() still discards the subject's element type. If the base .kitchen-rows rule keeps its cap and overflow: hidden but the alignment fix moves to div.kitchen-rows { align-self: start; }, this combiner applies that fix to the same key and the audit passes; however, the recipe list is an ul.kitchen-rows, so it remains stretched and clips its rows. Keep type selectors in the element identity while still allowing an untyped rule to match every type.

Useful? React with 👍 / 👎.

Comment on lines +2266 to +2269
const sharedRules = scopedRules(shared)
.flatMap(({ selectors, body }) => selectors.map((sel) => ({
keys: subjectKeys(sel), context: contextOf(sel), state: stateOf(sel), sel, body,
})))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve conditional contexts when combining clipping rules

Fresh evidence after the at-rule parsing fixes is that this projection drops each rule's conditional flag before declarations are combined. Removing the base align-self and placing .kitchen-rows { align-self: start; } inside @media (max-width: 1px) leaves the audit green because the conditional declaration is merged with the unconditional cap and overflow, although at every normal viewport the list remains stretched and clipped. Conditional rules must not satisfy the unconditional clipping fix.

Useful? React with 👍 / 👎.

Comment on lines +2152 to +2155
const canonicalBodies = allRules
.filter(({ file, selectors, conditional }) => file === 'tokens.css' && !conditional
&& selectors.some((sel) => /^:root\b/.test(sel)))
.map(({ body }) => body).join(';');

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Honor selector specificity for canonical token rules

Fresh evidence after the cross-rule !important fix is that concatenating rule bodies still reduces the cascade to source order and discards selector specificity. With an earlier :root:not(.unused) { --content-max-width-narrow: none; } followed by the normal :root declaration, the more-specific earlier rule wins in the browser and leaves the lists uncapped, while declaredValue() selects the later 720px body and the audit passes. Either carry selector specificity into winner selection or accept only the exact canonical :root selector.

Useful? React with 👍 / 👎.

@ulsklyc
ulsklyc merged commit c7035c6 into main Aug 2, 2026
15 checks passed
@ulsklyc
ulsklyc deleted the claude/kitchen-guard-followup branch August 2, 2026 20:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant