You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
48 lines
7.1 KiB
48 lines
7.1 KiB
# Audit: announce
|
|
audit-version: 1
|
|
audited-at: 2026-06-26
|
|
scope:
|
|
method: adversarially-verified workflow; HIGH lead-verified. B7 ground-truth: Toolbar A35 + Form A36 incidents STAYED FIXED (untrack present); toast/clipboard/drag-drop use uix.timers (no A6 leak); button is clean (its archetype finding was inert — Spinner not runtime-registered); data-size/data-shape are eidos visual attrs (not contract violations).
|
|
provider: src/uix/soma/components/announce/announce-provider.svelte.ts
|
|
cleanup-audit (A6/A35/A36): Line 75-77: Single $effect with cleanup return (clearTimers()). Disposes: politeTimer and assertiveTimer via cancel() if present (lines 149-161). Timers are scheduled via soma.uix.timers.schedule() (A6 compliant). Global createAnnouncer (global.svelte.ts): timers stored in RegionPair.timer and cance
|
|
|
|
## Summary
|
|
Counts (post-verification): CRITICAL 0 · HIGH 0 · MEDIUM 2 · LOW 0.
|
|
|
|
## Findings
|
|
### MEDIUM: eidos CSS selectors must match morfo-declared or runtime-auto-generated attributes. A decl — announce-001 <!-- id: announce-001 -->
|
|
- dimension: E-bis: Theming / Selector drift
|
|
- rule: eidos CSS selectors must match morfo-declared or runtime-auto-generated attributes. A declared prop like `visuallyHidden` must have a corresponding data attribute or inline style to drive styling.
|
|
- location: src/uix/eidos/components/announce/announce.css:23
|
|
- evidence: Line 23 uses selector `[data-announce-region]:not([data-visually-hidden])` to style visible regions. However, `data-visually-hidden` attribute is never written by soma. The provider (announce-provider.svelte.ts:246) writes inline `style` when hidden, but never writes a `data-*` attribute to signal visibility state to eidos.
|
|
- impact: The CSS selector `:not([data-visually-hidden])` always matches because the attribute is never present. This means card styling (display, padding, border, etc.) is incorrectly applied to ALL regions, including those where `visuallyHidden=true` (which should be sr-only with no card chrome).
|
|
- repro: Mount an Announce.Region with `visuallyHidden={false}`. The region will receive card styling (padding, border, etc.). Mount another with `visuallyHidden={true}` (default). It will ALSO receive card styling, breaking the sr-only case.
|
|
- proposed-fix: Option A: Modify AnnounceRegionProvider.props (line 242-248) to write `data-visually-hidden: hidden ? '' : undefined` so the CSS `:not([data-visually-hidden])` selector works correctly. Option B: Change the CSS selector to check for inline style presence (not reliable) or add a separate `data-visible` attribute when visuallyHidden is false. Recommend Option A with a morfo data declaration for the attribute.
|
|
- verify: [downgraded] MECHANISM CONFIRMED, IMPACT REFUTED. Confirmed: `data-visually-hidden` is NEVER written anywhere. Morfo (announce.ts:88-99) declares only `data-role`+`data-live` on the `region` part; the provider (announce-provider.svelte.ts:243-247) drives `visuallyHidden` ONLY via inline `style: hidden ? VISUALLY_HIDDEN_STYLE : undefined`; grep across soma+eidos finds no writer of the attr (announce.css:23 is the sole reference). So `[data-announce-region]:not([data-visually-hidden])` (announce.css:23) ALWAYS matches — the negation is dead/non-functional CSS; the 'visible regions only' guard the author intended never excludes the sr-only case. THAT part is a real selector-drift defect (E-bis), MEDIUM stands. BUT the candidate's IMPACT ('incorrectly applied to ALL regions ... breaking the sr-only case') is REFUTED: when visuallyHidden=true, soma sets the inline sr-only style (`position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0,0,0,0)`) which has higher specificity than the stylesheet rule and overrides `padding:0`/`border:0` inline (lines 12-22). The leftover `display:block`/`background`/`border-radius`/`font-size`/`line-height` from the CSS block are visually inert on a 1x1px clipped box — the region stays hidden. So the sr-only case is NOT broken; the defect is dead CSS / a non-functional visible-vs-hidden distinction, not an a11y/visual regression. Keeping MEDIUM but correcting the rationale/impact.
|
|
- fix-status: open
|
|
|
|
### MEDIUM: eidos CSS selectors must key off attributes the morfo declares or soma actually writes; a — announce-101 <!-- id: announce-101 -->
|
|
- dimension: E-bis: Theming / selector drift, SYS-1-adjacent
|
|
- rule: eidos CSS selectors must key off attributes the morfo declares or soma actually writes; a `:not([attr])` guard against an attribute that is never emitted is dead CSS that silently never differentiates state.
|
|
- location: src/uix/eidos/components/announce/announce.css:23
|
|
- evidence: `[data-announce-region]:not([data-visually-hidden]) { display:block; padding:...; border:...; background:...; }` — but `data-visually-hidden` is never written: announce.ts:88-99 declares only `data-role` and `data-live` on the region part, and announce-provider.svelte.ts:243-247 drives `visuallyHidden` exclusively via inline `style` (`hidden ? VISUALLY_HIDDEN_STYLE : undefined`), never a data attribute. Grep across soma+eidos confirms no writer.
|
|
- impact: The intended 'visible regions only' branch is non-functional: the `:not()` negation always passes, so the visible-card rule applies to every region. It is masked in the default (visuallyHidden=true) case because the higher-specificity inline sr-only style clips the box, so there is no user-visible regression today — but the CSS no longer expresses the visible-vs-hidden intent and will silently mis-style if the inline style ever changes. Dead-code distinction.
|
|
- repro: Mount `<Announce.Region visuallyHidden={false}>` and `<Announce.Region visuallyHidden={true}>`; inspect computed styles — the visible-card rule (display:block/background/font-size) resolves on BOTH because `:not([data-visually-hidden])` matches both; only the inline sr-only clip differentiates them, not the CSS branch as intended.
|
|
- proposed-fix: Either (a) project the state as a real attribute the CSS can key on — e.g. declare `data-visually-hidden` in the region part's morfo `data` and have soma write it from `opts.visuallyHidden`, then keep `:not([data-visually-hidden])`; or (b) gate the visible-card chrome on an opt-in attribute the consumer sets (e.g. `data-variant='visible'`) rather than a negation of a never-present attr. Option (a) keeps the existing selector working.
|
|
- verify: [verifier-added] added by adversarial verify pass
|
|
- fix-status: open
|
|
|
|
## No-findings dimensions
|
|
A: Contract (morfo), B: Behavior (A35/A36/A6/A15/live-regions/A30/A33/A31), C: DOM-selector, D: Frontier, E: TSC, F: Tests, G: Redundancy
|
|
|
|
## Theming facts (E-bis)
|
|
- magic z-index: none
|
|
- magic literals: none
|
|
- undeclared parts: none
|
|
- roles clean: true · variants clean: true
|
|
|
|
## Tests (F)
|
|
- exists: true · env: jsdom via vitest
|
|
- covers: timer scheduling and cancellation via soma.uix.timers; A/B toggle for repeated announcements; role/aria-live derivation; declarative region attrs sync; standalone region without provider; provider context context injection
|
|
- untested: visible region CSS application (visuallyHidden=false styling); async timer edge cases under network throttle; cross-document announcer (createAnnouncer in iframe/shadow DOM)
|