Releases: storybook-community/addon-css-user-preferences
Release list
v1.0.0
Major Changes
-
#1
498ae6bThanks @unional! - Support Storybook 10, and ship the addon as ESM only.The package is now published as
@storybook-community/addon-css-user-preferences
under the storybook-community org. It requires Storybook 10, builds with tsdown,
and drops the CommonJS build:dist/cjs,dist/esmanddist/tsare gone, and
./previewand./managerare reached through the exports map instead.The
prefers-contrastglobal andcssUserPrefsparameter key was misspelled
prefers-constrast. Any story that set the misspelled key must be updated,
though it never had an effect.The addon-kit
HeaderandPageexample components have been removed, along
with the panel and tab they were demonstrated in. Only the toolbar addon remains.Adds a CSF Next entry point with typed story parameters, and renders the toolbar
icon at a visible size.
Minor Changes
-
#5
27bcc1bThanks @unional! - Emulate preferences forwindow.matchMedia, not just for CSS.The addon rewrote
@mediaconditions in the CSSOM and nothing else, so a
component that read a preference in JavaScript — auseMediaQueryhook, a theme
library resolving its initial theme, a reduced-motion check gating an animation
— kept seeing the real operating system value while the CSS around it was
emulated, and the story rendered half emulated.While a preference is being emulated,
window.matchMedianow answers for it. A
query naming an emulated feature is rewritten to carry the emulated outcome and
handed to the real implementation, so an unset preference, a viewport condition,
and the rest of a compound query are still the browser's own answer, and a query
shape the rewrite cannot express falls back to the real value rather than
guessing.The returned
MediaQueryListdispatches achangeevent when the toolbar
changes a preference, throughaddEventListener('change'),onchange, and the
deprecatedaddListener, so a subscriber follows the toolbar instead of reading
a correct first value and then going stale.Returning every preference to its system default puts the browser's own
window.matchMediaback, so a Storybook that is not emulating anything is left
untouched. -
#4
58197bdThanks @unional! - Apply preferences to stylesheets a story mounts as it renders.The rewrite ran from a Storybook
useEffect, which fires after the play
function, so a story shipping its own<style>could render with un-emulated
CSS and its interaction tests could assert before the rewrite landed. The
decorator now processes during render and observes the document for sheets
that arrive later.The toolbar dropdowns follow the manager theme instead of the operating
system's, which previously left dark controls inside a light manager.A preference the story pins in its own
globalsis now shown read-only.
Storybook lets story globals win, so changing it from the toolbar was
silently discarded.
Patch Changes
-
#10
be11101Thanks @unional! - Emulate preferences in stylesheets that arrive via<link>.A
<link rel="stylesheet">is in the document before its sheet is:link.sheet
staysnulluntil the request resolves, anddocument.styleSheetsdoes not list
the sheet before then. The rewrite triggered by the element's insertion therefore
ran against a collection that did not yet contain the sheet that triggered it,
and because loading is not a DOM mutation, nothing re-fired the observer when the
sheet did arrive — so that stylesheet was never rewritten.A later re-render or any toolbar change picked the sheet up, which hid the
problem while clicking through the UI. What it did not hide is the case the
observer exists for: a play function runs before Storybook's effects, so an
interaction test asserting on a rewritten condition raced the network and passed
or failed on timing.The addon now waits for a pending
<link>to load and rewrites once its sheet is
there, so a linked stylesheet is emulated like an inline one. -
#9
5b19bf6Thanks @unional! - Skip stylesheets the browser will not let the addon read, instead of failing the render.processCSSread.cssRuleson every sheet indocument.styleSheets. For a sheet that is not origin-clean the CSSOM getter throwsSecurityError, and the"cssRules" in targetguard did not help —cssRulesis an accessor onCSSStyleSheet.prototype, soinis true for every sheet and the throw came one line later. SinceprocessAll()runs on the render path, one cross-origin<link>— an ordinary Google Fonts or CDN stylesheet withoutcrossorigin— took every story down.The read is now guarded: an unreadable sheet is skipped and reported once with a
console.warnnaming itshref, and the remaining sheets are still rewritten.