Skip to content

Browser support ​

wmkit ships ES2022 (target: es2022 in tsup), ESM + CJS, no polyfills and no runtime dependencies. The floor is set by the platform APIs each layer touches, not by the syntax — so the answer differs depending on how much of the package you actually import.

Supported baselines ​

What you importChrome / EdgeSafariFirefoxSet by
core state machine, without named layouts9415.493the ES2022 build target
core with saveLayout / getLayout / setLayout9815.494structuredClone; older engines copy layouts through JSON, see below
attachDesktop (drag, resize, snap, keyboard, a11y)9815.494ES2022 and structuredClone still dominate
themes/glass.css, themes/light.css11116.2113color-mix() in the snap preview
themes/retro.css9815.494plain borders and gradients only
@surdeddd/wmkit/persist9815.494same as core; a missing or throwing localStorage makes it a no-op
@surdeddd/wmkit/popout9815.494a window.open popup everywhere; picture-in-picture needs Chrome/Edge 116+ or Firefox 151+
@surdeddd/wmkit/dock9815.494same as attachDesktop; MutationObserver marks late windows

The adapters add whatever their framework requires and nothing else: React >= 18, Vue >= 3.3, Svelte >= 4, Solid >= 1.8, Angular >= 16.

The DOM APIs the controller needs — pointer capture, ResizeObserver, requestAnimationFrame — all landed years before the ES2022 target, so they never set the floor in practice.

Practical summary: the library works in every browser from early 2022 onward; importing a bundled glass theme raises that to spring 2023. Style the windows yourself and the theme row disappears entirely.

Platform APIs, and what happens without them ​

APIUsed byMissing behaviour
Pointer Events + setPointerCapturesrc/dom/drag.ts, src/dom/resize.ts, dock separatorsno drag or resize; keyboard control and the core still work
ResizeObserverattachDesktop viewport trackingfeature-detected — the desktop is measured once at attach; call wm.setViewport() yourself when it resizes
requestAnimationFramepointer batching in drag and resizerequired
structuredClonenamed layouts in src/core/manager.tslayouts are copied through JSON instead: functions in meta are dropped, Dates become strings and Infinity becomes null; every other core method is unaffected
Element.animateFLIP minimize and restore ghostsfeature-detected — the animation is skipped, state still changes
Element.moveBeforeskin rebuilds in src/dom/controller.tsfeature-detected — content moves with append, so an iframe inside a rebuilt window reloads (Chromium 133+ keeps it running)
matchMediacoarse-pointer and reduced-motion detectionfeature-detected — falls back to fine-pointer defaults
localStoragepersist default storageread inside a try/catch; the plugin becomes a no-op, or pass your own storage
reportErrorpersist errors when there is no onErrorfeature-detected — the error is dropped silently
CSSStyleSheet.replaceSync (constructable stylesheets)<wm-desktop>.styles, shadow skins from @surdeddd/wmkit/chrome<wm-desktop>.styles skips CSS strings (Safari before 16.4); a shadow skin falls back to a <style> element
document.adoptedStyleSheetspopout style copyingguarded — only document.styleSheets are copied
documentPictureInPicturepopout (target: 'pip', and 'auto' when present)'auto' falls back to a popup; isPopoutSupported('pip') returns false
window.openpopout (target: 'window')isPopoutSupported() returns false when neither kind exists; popout() rejects with a clear error
getScreenDetails (Window Management API)popout screen option, listScreens()feature-detected — the popup opens at the browser's default position and listScreens() resolves null; the same happens when the permission is denied

Nothing in src/core touches window or document, so the manager can be created, mutated, serialized and hydrated during SSR. Only attachDesktop and the adapters' bind helpers need a document — call them after mount.

CSS features in the bundled themes ​

FeatureWhereFallback
color-mix()snap preview fill and border (glass.css, light.css)the preview renders unstyled; override [data-wm-snap-preview] with a static colour
backdrop-filterwindow and titlebar blurdegrades to a flat translucent panel
contain: layout stylewindow elementa perf hint only
individual translate / scale propertiesopen and modal-blocked keyframesthe animation is skipped
prefers-reduced-motionevery themefewer animations, never more

All themes are pure custom properties over data-wm-* attributes, so replacing a theme with your own CSS removes every requirement in this table.

Tested matrix ​

CI runs the full end-to-end suite on every push against:

  • Chromium (Playwright Desktop Chrome)
  • Firefox (Playwright Desktop Firefox)
  • WebKit (Playwright Desktop Safari)
  • Mobile Chromium (Playwright Pixel 7, touch and coarse pointer)

Unit tests run in Node, with jsdom for the adapter and DOM smoke tests.

Touch and pointer ​

Coarse pointers (pointer: coarse) automatically get larger resize hit areas (16 px edges, 24 px corners instead of 8 and 12), a wider snap threshold (20 px instead of 12), bigger snap corners (96 px instead of 64) and a 12 px magnetism threshold instead of 8. Override any of it through hitAreas, snap and magnetism in attachDesktop.

Titlebar context menus fire on right click and on long press, so onTitlebarContextMenu works the same on both input types.

Known gaps ​

  • popout opens a popup in every engine; picture-in-picture exists in Chrome and Edge 116+ and Firefox 151+, on desktop only. Check isPopoutSupported() before showing the control, since a browser can disable both.
  • Without structuredClone named layouts go through a JSON copy, which drops functions and turns Dates into strings. Keep meta and extensions JSON-safe, ship a polyfill, or store serialize() output yourself.
  • Visual regression baselines are generated on macOS and skipped in CI, so rendering differences are caught by hand, not by the pipeline.