PNG window skins
Start with a radius ("22px", "50%", or "9999px") or SVG path shape and
background: "transparent". Use a PNG when you need a hole in the window or an
outline those shapes cannot describe. PNG skins are fixed-size and cannot resize.
A washer with a transparent center is a useful first test.
Create the mask asset
Section titled “Create the mask asset”Add an RGBA PNG under assets/. Its pixel dimensions must be exactly 1× or 2× the
window’s logical width and height. Alpha defines both the native backing and where the
window accepts pointer input. Import it in slop.ts and use the skin window:
import skin from "./assets/window-mask.png";
// Inside defineSlop({ ... })window: { kind: "skin", width: 480, height: 480, image: skin },A skin window contains kind, width, height, and image. It cannot
also specify shape, background, or resizable, and the native window stays
at the skin’s fixed size.
Align the WebView to the skin
Section titled “Align the WebView to the skin”The host draws the PNG behind the page, supplies window sizing, and overrides
ordinary html/body backgrounds with transparency. Avoid overriding that
transparency with more-specific or !important page backgrounds. The generated Svelte entry provides the app root automatically;
mark a framework-neutral root with data-hitslop-root; its mount ancestors
fill the window too. No sizing/transparency reset or duplicated pixel constants
are needed; use 100% or --slop-window-width/--slop-window-height. Align controls with opaque areas of the PNG using ordinary CSS.
The native host exposes initial dimensions as --slop-window-width and --slop-window-height, and
sets data-slop-presentation="skin". These layout rules are disabled for capture.
Pixels below 10% alpha are click-through. Feathered edge pixels may still receive or reject clicks depending on their alpha, so keep important controls well inside opaque regions. Transparent holes can intentionally expose and click the content behind the window.
Test the physical behavior
Section titled “Test the physical behavior”- Confirm the PNG is RGBA and is exactly 1× or 2× the logical window dimensions.
- Test clicks around curved edges, transparent holes, and soft shadows.
- Move the window with its native toolbar handle; guest controls do not drag it.
- Check that focus rings and menus stay inside the usable silhouette.
- Preview at native size; a skin is not a responsive window treatment.
Vite includes the imported PNG as an immutable content-addressed asset, even if no
component renders it. Rust validates the PNG stream and infers its scale; the native
mask uses that scale for crisp rendering. Document images imported by a user are separate
attachments served by app.attachments.url(id).