Skip to content

sort-filter-lens leaks 61 unnamespaced classes — .error/.row/.muted collide with 16/19/14 other surfaces today #103

Open

Description

@mpstaton

Context: The-Sort-Filter-Lens-Containment-Breach.md

F3 was written because of this member. It is still true. Measured 2026-09-13:

apps/sort-filter-lens/src/app.css   511 lines, 98 top-level rules
distinct leaked leftmost classes    61
scoped <style> blocks                0
.svelte files                        1  (App.svelte, 729 lines)

Registered root_class is .sort-filter-lens. One rule honours it. app.css is imported as plain global CSS from both mount.ts and index.ts.

This is live, not latent

Leaked class Surfaces rendering it
.row 19
.error 16
.muted 14
.empty 13

The demonstrable case. chat declares three properties:

.chat-app .bubble.error { background: …; border: …; color: …; }

sort-filter-lens declares five, three of which chat does not — margin, padding, border-radius.

Specificity decides border and color (chat wins at 0,3,0). But specificity never arises for the other three, because only one rule declares them. So every chat error bubble gains 0.8rem 1rem margin, 0.5rem 0.7rem padding and a 6px radius whenever sort-filter-lens happens to be mounted, and loses them when it isn't.

A member's appearance depends on which sibling is on screen. Under independent deploys that is not debuggable by the team that owns the broken surface.

Read the debt rating carefully

This member is rated debt: critical, and the instinct is "badly built". The sweep found the opposite. It has the best a11y labelling of its cluster (5 aria-label, role="toolbar", role="menu", aria-expanded, a real <label for>) and owns the most capable single control in the product — a persisted 3-key sort with rank badges, direction toggles and a grouped column picker.

Packaging failure, not craft failure. The sort toolbar is the organ most worth promoting out of it — not the member most worth rewriting.

Work

  1. Contain — prefix all 61 or wrap under .sort-filter-lens. Mechanical. Verify: F3 goes 100 → 0 findings. Screenshot chat's error bubble with the lens mounted and unmounted, before and after — the bug is invisible unless two specific remotes are on screen together, which is why it survived.
  2. Decompose — 729 lines, zero components, so there was nowhere for scoped <style> to live. Extract the sort toolbar first.
  3. Phantom tokens — carries all four. Blocked on The federal layer never shipped --space-*, --radius-* or --z-* — 170 declarations reference tokens that don't exist #102.
  4. @keyframes pending-spin runs infinite with no prefers-reduced-motion guard; <span role="button" tabindex="0"> nested inside a <button>.

Bounded

The other four members in the sweep are fully contained — search-and-add 47/47, search-results 76/76, org-workbench 131/131, person-enrichment 110/110. Zero leaks. One member, one file.

Activity

  1. self-assigned this
    on Sep 13, 2026
  2. added theissue type on Sep 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions