ng-prism 22.8 is out. If you haven't come across it yet: ng-prism is a component lab for Angular libraries, a Storybook alternative without story files. You put a @Showcase decorator on a component, the TypeScript compiler picks up inputs, outputs and types at build time, and the components render in the same document as the styleguide, without an iframe. The full tour has the details.
Voice: elevenlabs.io
This release is mostly about the conditions a component is seen under. The canvas can simulate a light or dark color scheme, reduced motion, forced colors and colour vision deficiencies now, without a change to your CSS. Besides that, showcases can hold real Date values, and the canvas no longer changes along with the styleguide's theme.
Dark mode, reduced motion and forced colors in the canvas
Checking a component in dark mode meant the OS settings or the Rendering tab in DevTools so far. Both switch the whole page, the styleguide around the component included, and neither is something you want to do for every one of 40 components in a review.
In 22.8 the tools menu has a Simulate group:
- Motion: System or Reduce
- Color scheme: System, Light or Dark
- Forced colors: Off or Active
- Vision: Protanopia, Deuteranopia, Tritanopia, Achromatopsia or Blurred
It reaches the component in the Playground, every cell of the Overview and the CDK overlays the component opens, so dialogs, menus and tooltips too. The styleguide around it stays as it is. A pill on the canvas says what's simulated, the setting stays when you switch components, and it's part of the URL, so you can send somebody a link to your component in dark mode.
The same card under Light on the left and Dark on the right. Its own prefers-color-scheme rule does the work.
Your styles need no change, and that was the interesting part to build. Without an iframe, the browser can't emulate a media feature for a single element. So ng-prism copies every @media rule that tests an emulated feature, scopes the copy to the component and switches the original off there. Specificity and cascade order stay the same as under the real condition, and a combined query like (prefers-color-scheme: dark) and (min-width: 600px) still follows the window width. matchMedia answers from the simulation as well and fires change on the lists a component holds.
If your library's dark mode hangs on a class instead of the media query, the config can name it:
export default defineConfig({ colorScheme: { dark: { class: 'dark' }, }, }); Under Dark, the component's host and the CDK overlay container get the class. A color scheme and forced colors also change the colours axe measures, so the live a11y audit runs again under them and says which condition its score belongs to.
The new tools menu. Every choice shows what it does: a swatch per background, a small page in the colours of each scheme, a colour stripe through each vision filter.
Try it in the demo: the Card in dark mode.
Colour vision
Some components carry their meaning in colour. The alerts in the demo are green for success and red for error. Under Deuteranopia, a form of red-green colour blindness, the two come out nearly the same:
On top as is, below under Deuteranopia. Info stays blue, success and error end up in the same olive.
The text still says what happened, so these alerts get away with it. A status dot without a label wouldn't. The filters follow Machado, Oliveira and Fernandes (2009) at full severity, Achromatopsia shows brightness only, and Blurred stands for low vision. They change what you see, not what the component computes, so the a11y audit doesn't run again for them.
The Alert under Deuteranopia in the demo.
Forced colors, checked against Chromium
Forced colors is the mode behind the contrast themes in Windows: the browser drops most of your colours and paints with a small system palette. A page can't put the browser into that mode, so the canvas approximates it.
The first version of that approximation was too kind. Against a real component library, a toggle, a checkbox and a radio button looked fine in the simulation, while in Chromium's real mode you couldn't tell whether they were checked. A simulation that hides bugs is worse than none, so it got rebuilt before the release:
- It uses the palettes of Chrome's forced colors emulation, the light or the dark one depending on the color scheme.
- Chromium forces colours after the cascade, so even your own
@media (forced-colors: active)rules lose every colour that isn't a system colour. The canvas writes a twin of each of your rules right behind it, with the same selector, the same specificity and the forced values, so your rules still win and lose against each other as before. - 114 tests render the same fixtures in Chromium's real mode and under the simulation, in both palettes, and compare the computed colours. On that component library, 30 of 30 screenshot pairs came out pixel equal afterwards, and the three controls show up as broken, which they are.
The Button under forced colors. Filled, Elevated and Text lose their shape, since backgrounds and shadows go. Only Outlined keeps a border.
A transparent border is the usual fix, because the mode paints borders in the text colour. It's still an approximation: Chrome paints a backplate behind every line of text, which CSS can't reproduce, so the pill and the audit say "approx.", and for the exact mode DevTools stays the reference.
Dates in showcases
A date picker couldn't have a variant with a value until now. The scanner dropped every new Date(…) as a runtime value, and Date inputs ended up in the JSON editor. In 22.8 a Date is a showcase value like any other:
@Showcase<DateFieldComponent>({ title: 'Date Field', category: 'Forms', variants: [ { name: 'Empty', inputs: { label: 'Start' } }, { name: 'With value', inputs: { label: 'Start', value: new Date(2026, 3, 28) } }, { name: 'Min and max', inputs: { label: 'Start', value: new Date(2026, 3, 28), min: new Date(2026, 3, 1), max: new Date(2026, 3, 30) }, }, ], }) The generated manifest is TypeScript, so it writes the Date the way your source does, and the browser creates it when the styleguide loads, in its own time zone. A timestamp computed at build time would tie the day to the build machine: the same showcase built in CI and on a laptop in Berlin could show different days.
One trap is the same as in plain JavaScript: new Date('2026-04-28') is midnight in UTC, which west of UTC is still the 27th. For a day without a time, write new Date(2026, 3, 28) or new Date('2026-04-28T00:00'). And new Date() without arguments is dropped with a warning, because it's the current time and would differ in every build and every visual regression run.
The day comes from new Date(2026, 3, 28). The Controls panel has a date field for each Date input.
Date inputs get a date field in the Controls panel, and the Events panel shows a date instead of {}. Building this turned up an older gap: a component that sets its own model(), as a date picker does when you click a day, went unheard. Now the Events panel logs it as valueChange, the control shows the new value, and Reset appears.
The Date Field in the demo.
The canvas stays when the theme changes
The styleguide has a dark and a light theme, and until now that leaked into the canvas. The checkerboard of a transparent background was dark under the dark theme. A component that inherits its text colour got the styleguide's, which is white on a light ground under the dark theme. Libraries worked around that with a global rule on .demo-wrap.
In 22.8 the theme styles the styleguide, not your component. light, dark and transparent set the text colour a component inherits, dark ink on light and transparent and light ink on dark, and the checkerboard is light in both themes.
Dark theme on the left, light theme on the right. The cells don't change.
If all your components are designed for a light surface, there's a new option, so you don't have to write bg: 'light' on every showcase:
export default defineConfig({ showcaseDefaults: { bg: 'light' }, }); It only fills in where a showcase declares no background.
Smaller things
- On a phone, component pages keep a 16 px edge instead of 24 px, and the title lines up with the page.
- Plugins get four more icons (
mouse-pointer,mouse-pointer-click,scan,keyboard) andformatDateValue(), which shows aDatethe way the styleguide does. - The overlays and the measurement on a fixed ground read theme tokens of their own now, so a project whose ground is a mid-tone can make them readable on it.
Changes to check when you update
A few things change in 22.8, mostly for visual regression baselines and plugins:
-
bg: 'checker'is gone. It was deprecated since 22.2 and goes now instead of in 23.ng updaterewrites it to'transparent', which is a light checkerboard in both themes and see-through in a capture, so baselines recorded oncheckerneed a new recording. - A component takes the text colour of its ground. Baselines of components that inherit it, an icon in
currentColorfor example, may need a new recording. A global.demo-wrap { color }workaround stops working and can go. -
Dateinputs aretype: 'date'inInputMetainstead of'object'. A plugin control that matched'object'doesn't get them anymore. -
model()changes reach the panels. They show up in the Events panel and count as a changed input. - The tools menu has new names. That only matters for end-to-end tests that find its groups or buttons by name.
Deprecated, and removed in 23.0.0: data-theme on <html>. The styleguide marks its theme as data-prism-theme now, because a library whose own theme hangs on data-theme followed the styleguide's theme through it. An attribute can't warn, so search the styles of your styleguide app for data-theme.
Upgrading
ng update @ng-prism/core Update the plugins you use at the same time. Visual regression, JSDoc and box model changed along with the core.
- Demo: ng-prism.github.io/ng-prism/demo
- Simulate in the docs: Simulate, Under a simulation
- Dates: Dates in variants
- Everything that changed: Upgrade notes
- Release: github.com/ng-prism/ng-prism/releases/tag/v22.8.0
- Repo: github.com/ng-prism/ng-prism
Which conditions do you check your components under that the canvas can't simulate yet? prefers-contrast, 200 % zoom, right to left? I'd like to plan the next step around what you actually check, so let me know in the comments or open an issue.






