Styling

This pack authors CSS with Distillate, Elastic's typed CSS engine with render-driven style collection. React renderers call cls(context, ...handles) so class names resolve through the HTML adapter's resolveClassName. src/pack.ts passes styleAdapter: createDistillateHtmlStyleAdapter(slideDistillery) to definePrimitivePack: it records those handles and renderStyles emits the Distillate stylesheet.

Used rules are discovered while rendering, via resolveClassName. The adapter is part of the pack, so a host supplies nothing:

import { createIsomerRuntime } from '@elastic/isomer-runtime';
import { slideDeckFrame, slidesPack } from '@elastic/isomer-primitives-slides';

const runtime = createIsomerRuntime({
  packs: [slidesPack],
  frames: { slide: slideDeckFrame },
});
		

A runtime loading this pack alongside another styled pack emits both stylesheets. The adapter declares ownsHandle, so its rules are routed to its own collector rather than merged into a neighbour's.

This pack declares no styleCollector, and should not: every handle reaches the collector through resolveClassName, so no primitive here declares a collectStyles hook and there is no collector shape to protect. A pack that does write such hooks declares the tag, and a runtime then refuses an adapter that builds a different collector.

Pass styleAdapter on the runtime to replace every pack's adapter at once — including this one — which is how a host renders HTML with no CSS at all.

The React surface returns content, not a document. The stylesheet belongs to the host. The pack exports slideStylesheet for exactly this purpose:

import { slideStylesheet } from '@elastic/isomer-primitives-slides';
import { renderToStaticMarkup } from 'react-dom/server';

const node = runtime.surfaces.react.render(composition);
const body = renderToStaticMarkup(node);
// Host wraps `body` in a section with `data-theme`, includes `slideStylesheet()`.
		

For a single node rendered outside a frame (e.g., in Storybook), StandaloneSlideNode inlines the stylesheet:

import {
  StandaloneSlideNode,
  type SlideCodeNode,
} from '@elastic/isomer-primitives-slides';

const node: SlideCodeNode = {
  type: 'slideCode',
  label: 'Composition',
  language: 'json',
  code: '{ "type": "view", "body": [] }',
};

<StandaloneSlideNode node={node} />
		

This is a React convenience wrapper, not a surface — it applies slideStylesheet() via dangerouslySetInnerHTML. The HTML surface does not use it.

A browser resolves Inter and Roboto Mono from the page; an image backend resolves nothing it was not handed, and an unregistered family falls back to the backend's built-in face. So a host rasterizing this pack registers both families itself.

SLIDE_THEME.font.weight declares 500, 600, 700, and 800, and anything that does not set a weight inherits the document default of 400. That is the set to register, per family — src/examples/deck/fonts.ts derives it from the theme rather than listing it, so a new weight token cannot silently fall back to the nearest registered face.

@fontsource/inter and @fontsource/roboto-mono ship woff2, which takumi decodes natively, so no conversion step is needed. Roboto Mono stops at 700; a weight the family does not ship is dropped rather than substituted.