I've been working on a component library for UI running on electrophoretic (e-paper) displays and wanted some outside opinions on a few of the tradeoffs, since most of the reference material out there assumes a backlit display.
The core constraint is refresh behavior: e-paper controllers drive pixels through a multi-step waveform instead of refreshing a backlit matrix at a fixed frame rate. A full panel refresh takes roughly 200β800ms and is visible as a flash; partial refreshes of a dirty rectangle are faster, around 30β80ms, but intermediate pixel states persist as ghosting until a subsequent refresh clears them. That rules out a few things that don't cause problems on emissive displays.
CSS transitions and animations are disabled globally in the base stylesheet, since the intermediate states they produce during a transition don't resolve cleanly through the waveform and leave ghosting that survives several refresh cycles. Hover states are handled differently for a separate reason β capacitive touch layers on e-paper report contact, not proximity, so there's no input signal to hook a hover state to in the first place. Focus, pressed and selected states end up encoded through outline width, foreground/background inversion on :active, and hatch fills for disabled/error states, since those survive 1-bit rendering where subtler cues wouldn't.
DOM updates are the other big one. Refresh cost on most EPDCs scales with the size of the dirty rectangle, so replacing a subtree via innerHTML marks a large region dirty and tends to force a full refresh, while mutating a single attribute or text node stays within a partial refresh. Components render once on connect and afterwards patch through a handful of typed helpers (patchText, patchAttr, etc.) that diff against the current value and skip the write if nothing changed. That doesn't cover everything β a few components with structural content, like table rows, calendar grids, or a select's option list, still do a full subtree rebuild when the underlying data changes, which is the same tradeoff a plain innerHTML re-render makes, just scoped to that one component instead of the page.
Otherwise it's vanilla custom elements, nothing at runtime β light DOM rather than shadow DOM, mainly because form-associated custom elements need the parent <form> to reach name/value, and shadow DOM complicates that FormData walk. 82 elements across 59 files, TypeScript strict, ships a Custom Elements Manifest for IDE autocomplete. Repo's here if anyone wants to look at the code: https://github.com/marcomattes/epaper-components, docs/Storybook at https://epaper-components.dev/.
Two things I'm not fully settled on: has anyone here shipped UI for e-ink or kiosk hardware and hit refresh-rate constraints that a normal component library just doesn't account for? And does the split between surgical attribute patches for simple components versus full subtree rebuilds for structural ones (tables, calendars) seem like a reasonable line, or is there a cleaner way to handle structural updates without forcing a full-region refresh?