Commit graph

2 commits

Author SHA1 Message Date
doxa-maps-88655c70
1652559e06 Teach the synced-CSS gate what a selector is, and turn it on
The gate reported 14 conflicts across two files. Thirteen were false and it was
blind to real ones, and both came from a single cause: it keyed a rule on the
last class before the brace, so any qualified rule was filed under its own base
rule. A dark-mode override, a .dsm-dark variant and a :host(:fullscreen) rule all
merged into the unconditional rules they were meant to override, and the pair was
then reported as the file disagreeing with itself. In every case both copies were
identical and the code correct.

Merging is last-write-wins, so the same bug also HID conflicts. Poisoning
.dsm-root inset from 0 to 7px in the shadow copy produced no finding at all,
because the fullscreen rule's own inset:0 was merged in afterwards and restored
it. A property could be shared, genuinely divergent, and invisible.

The key is now the full selector, and at-rules are part of it. The header already
claimed media-query variants were treated as distinct rules; nothing implemented
it, and PeopleGroupDetail's 18px name with a 16px @media override read as the
file contradicting itself when the shadow copy simply has no mobile rule -- which
this gate's own contract explicitly allows. At-rule bodies are brace-matched
rather than regex-matched, since a lazy pattern stops at the first inner brace.

The shadow literal is also sliced AFTER its opening backtick. Slicing from the
anchor left useShadowStyles(` at the head of the CSS, which a full-selector
pattern absorbs into the first rule -- .dsm-root was captured as
"useShadowStyles(` .dsm-root" and silently dropped.

That leaves one true finding, and it was real: .action-buttons padding-top was
12px as a Vue child and 4px in a shadow root. Both values date from the initial
commit, so there is no later intent to infer; aligned to the shadow copy's 4px
because that is the copy that reaches the shipped bundles, so it preserves what
users have been seeing rather than changing it.

check:synced-css:self-test poisons three properties in memory and requires each
to be caught. inset is there deliberately -- it is the one the old implementation
missed, so it is the case that proves the fix. Now in build:unlocked, which it
could never be while it was red.
2026-09-23 01:22:36 +00:00
doxa-maps-88655c70
fd41b4a207 Check that a component's two CSS copies agree
Some checks are pending
build-gate / build (push) Waiting to run
A shared component declares the same selectors twice: once in a scoped <style>
block for when it is imported as a Vue child, and once in a useShadowStyles
literal for when it is mounted inside a shadow root, where scoped CSS does not
reach. Both are load-bearing and the standing rule is that they stay
identical. Nothing enforced it.

library/components/PeopleGroupDetail.vue disagreed with itself on eleven
properties, and the shadow copy is a coherent DARK theme under a light-only
rule: .detail-header background white vs #16213e, .image-container #f5f5f5 vs
#2d2d4e, and .people-group-name color #333 vs #fff -- white text, legible only
on the dark background, so fixing the background alone would make the name
invisible. Six importers; the dark hex reached four shipped bundles.

It was invisible because it is a two-host bug: the component looks correct in
whichever host you tested. Fourth of that shape this week.

Three findings while building this, each of which had the gate reporting
success about something it never compared:

- a loose /<style[^>]*>/ matched the words "<style scoped>" inside a JS
  comment 43 characters before useShadowStyles( in SemanticTreeLegend.vue, then
  ran to the next real </style> and swallowed the whole shadow literal into the
  <style> text. Both maps read the same bytes, so that file could not fail on
  any of its 51 apparent shared selectors. Anchored to line start, since an SFC
  block starts at column 0. That also raised PeopleGroupDetail 8 -> 11.
- assigning m[sel] = props let the last declaration win, discarding every
  property that appeared only in a base rule when a media query redeclared the
  selector. Rules are merged now.
- comparing raw text reported comma-spacing as divergence: 5 findings for
  doxa-simple-map of which 2 were formatting. Compared as property maps, so the
  rule is about meaning.

Negative-tested by change rather than by exit code, which with pre-existing
failures proves nothing: fixing .detail-header takes PeopleGroupDetail 11 -> 10
and restoring it returns 11.

Standalone rather than in build:unlocked, because it fails on live defects
today.
2026-09-22 18:40:55 +00:00