Available since v0.1.0
fs-assert-stable
StableThe temporal inverse of updated. Passes when the matched element's subtree is not mutated during the timeout window. Any mutation within the window fails the assertion.
Syntax
fs-assert-stable="<selector>"
When to use it
- Flicker detection — "after I add to cart, the price should not update again for 500ms"
- Settlement checks — "after the modal opens, the form shouldn't re-render during the transition"
- Perpetual "never mutate" guards on immutable UI (legal notice, static footer)
Example — flicker detection (OOB + stable)
The most useful pattern. Pair stable with OOB so the stability window begins after the primary mutation:
<!-- Primary: add to cart -->
<button fs-assert="cart/add-item" fs-trigger="click"
fs-assert-updated="#cart-total">
Add to Cart
</button>
<!-- OOB: verify price doesn't flicker after the cart update -->
<div
fs-assert="cart/price-stable"
fs-assert-oob="cart/add-item"
fs-assert-stable="#cart-total"
fs-assert-timeout="500">
</div>
Example — perpetual immutability
<div id="legal-notice"
fs-assert="layout/legal-stable"
fs-trigger="invariant"
fs-assert-stable="#legal-notice">
</div>
This fails if #legal-notice or any descendant is ever mutated.
Timeout semantics
stable is unique: it commits on the first mutation (failure) rather than waiting for a match. Without fs-assert-timeout, stable passes via the GC sweep (default 5s) — use an explicit timeout for tighter stability windows.
- With OOB: the timeout window starts when the parent assertion passes, not at page load.
- With invariant: runs perpetually for the page lifetime. Any mutation, any time, is a failure.
- With user triggers: window starts at trigger fire. Rare — OOB is almost always the right pairing.
Pairs well with
- OOB — the canonical pairing for flicker detection
fs-trigger="invariant"— perpetual immutability guardsfs-assert-timeout— explicit stability window
Gotchas
- Scope matters.
fs-assert-stable="#foo"fails on ANY mutation in#foo's subtree. If#foois inside your app's root swap target, every re-render mutates it. Place stability sentinels OUTSIDE swap targets, or narrow the selector to an element that isn't part of routine re-renders. - Framework transitions. CSS animations via class toggles count as mutations. If your UI animates into place, the animation class toggle will fail a
stablecheck targeting the same element. Target the inner stable content instead. - Not a "nothing happened" check.
stableonly cares about the selected subtree. Events, network calls, and mutations elsewhere in the DOM are ignored.
See also
updated— the temporal complementfs-assert-oob— the canonical pairing- Patterns cookbook #12
- Assertions index