Runtime API
Two runtime entry points ship with the package: the CSS helpers under
@oddsquad/vite-plugin-lit/css.js, and the timeline API behind
virtual:lit-plugin/timeline. Both are dependency-free and safe to import from
component code.
urlSheet()
Section titled “urlSheet()”function urlSheet(url: string): UrlSheet;Builds one constructed CSSStyleSheet from a ?url-imported CSS asset. Adopt
it from any number of shadow roots through adoptedStyleSheets, and an edit to
the source CSS re-fetches and replaceSync()s that one object. Every root that
adopted it restyles, with no component re-render and no page reload.
import {urlSheet} from '@oddsquad/vite-plugin-lit/css.js';import sheetUrl from './utils.css?url';
const {sheet, onHotUpdate} = urlSheet(sheetUrl);
import.meta.hot?.accept('./utils.css?url', onHotUpdate);
export default sheet;Register onHotUpdate in the module that imports the CSS, with the same literal
specifier. Vite resolves accepted dependencies by static analysis, so the string
has to appear in your source. Overlapping fetches from rapid edits resolve in
issue order, so a slow earlier request cannot clobber a newer one.
UrlSheet
Section titled “UrlSheet”interface UrlSheet { sheet: CSSStyleSheet; onHotUpdate: (mod: Record<string, unknown> | undefined) => void;}sheet is empty until the first fetch resolves, so the page shows a brief flash
of unstyled content on initial load. That is the cost of keeping the CSS a real
asset instead of inlining it into the JS bundle. onHotUpdate is typed to match
Vite’s accept callback, so it drops straight into import.meta.hot?.accept().
devCacheBust()
Section titled “devCacheBust()”function devCacheBust(url: string): string;Appends a cache-busting query to a ?url-imported CSS file URL in dev. Each HMR
re-execution then yields a fresh href, and the browser refetches the changed
stylesheet. Production builds already serve a content-hashed asset, so the input
comes back unchanged.
import {LitElement, html} from 'lit';import {customElement} from 'lit/decorators.js';import {devCacheBust} from '@oddsquad/vite-plugin-lit/css.js';import cssUrl from './my-element.css?url';
const href = devCacheBust(cssUrl);
@customElement('my-element')export class MyElement extends LitElement { override render() { return html`<link rel="stylesheet" href=${href} /> <p>Styled by a linked sheet.</p>`; }}addTimelineLayer()
Section titled “addTimelineLayer()”function addTimelineLayer(layer: TimelineLayer): void;
interface TimelineLayer { id: string; label: string; /** 0xRRGGBB */ color: number;}Registers a layer, one category of recorded timeline events with its own colour and toggle. The panel appends it to the toggle strip after the built-in layers. Duplicate ids are ignored, so calling it on every module evaluation is safe.
addTimelineEvent()
Section titled “addTimelineEvent()”function addTimelineEvent(event: TimelineEventInput): void;
interface TimelineEventInput<TData = unknown> { layerId: string; /** A `performance.now()` value. Leave it out to stamp the event now. */ time?: number; data: TData; title?: string; subtitle?: string; /** Pairs a start event with its matching end event. */ groupId?: number | string; logType?: 'default' | 'warning' | 'error'; meta?: { elementId?: number; tagName?: string; source?: {file: string; line: number}; };}Emits one event onto a custom layer or onto a built-in id: 'lit-lifecycle',
'lit-render', 'lit-render-verbose', 'mouse', 'keyboard', 'custom-events',
or 'lit-warnings'.
import {addTimelineEvent, addTimelineLayer} from 'virtual:lit-plugin/timeline';
addTimelineLayer({id: 'fetch', label: 'Fetch', color: 0x22d3ee});
export const recordFetch = (url: string, ms: number): void => { addTimelineEvent({ layerId: 'fetch', time: performance.now(), data: {url, ms}, title: url, subtitle: `${ms.toFixed(1)} ms`, });};color is a number, not a CSS string. data is serialized before it reaches
the panel, so keep it to plain values. meta is filled in by the plugin’s own
capture layers and custom events have no reason to set it. The panel shows
every event on the recording’s clock, milliseconds since Record; the
plugin moves a custom event’s performance.now() time onto it, so the event
lines up with the Lit updates around it.
The virtual module resolves to a no-op stub in any vite build, including a
build with timeline enabled, because the events only ever travel over the dev
server’s HMR channel. It is also a stub in dev when timeline is off. Imports
are safe to leave in component code. The ambient types ship in
@oddsquad/vite-plugin-lit/client, the same declarations that cover the CSS
queries.