@typeset/adapter-web
@typeset/adapter-web translates adapter-facing results from @typeset/typeset into trusted HTML and the CSS required to express them in a browser. It owns the internal markup; framework integrations should treat the returned HTML as an opaque paragraph result and replace it as a whole.
Measured Composition
import { renderLayoutResultToHtml, serializeWebStyleRules } from "@typeset/adapter-web";
const output = renderLayoutResultToHtml(layoutResult);
const style = document.createElement("style");
style.textContent = serializeWebStyleRules(output.styleRules);
host.before(style);
host.innerHTML = output.html;
Measured lines are authoritative and do not wrap again in the browser. Each solved line is a full-width inline-block span; the returned HTML is a fragment without an additional paragraph wrapper. Set the host element’s inline size to layoutResult.availableWidth so the line spans form visual rows without introducing block formatting boundaries between them. Source text is always HTML-escaped. The caller must render with the same font context and layout unit used for measurement and composition.
Adjacent graphemes with the same final rendering configuration are coalesced across token boundaries. Spacing changes, conditional spacing, soft line-start compensation, optional annotations, and Static Flow structural wrappers still create explicit inline-run boundaries.
Pass resolveCompositeFont when the result carries Core composite-font runs. This option is conditionally required: rendering throws when font runs are present without it, while ordinary single-font results do not need it. The resolver maps adapter-neutral candidates to browser family aliases and CSS face properties; Core-owned relativeSize and baselineShift are rendered automatically:
renderLayoutResultToHtml(layoutResult, {
resolveCompositeFont: (font) => ({
families: font.candidates.map(({ family }) => family),
style: "normal",
weight: 400,
}),
});
Composite-font declarations use content-addressed typeset-composite-font-* and typeset-composite-font-adjustment-* classes. Each output returns the required dynamic rules in styleRules; the adapter does not create or place a style element. Callers may combine rules from any number of paragraph outputs and pass the complete collection to serializeWebStyleRules, which deduplicates identical class rules in first-seen order.
The serializer keeps spacing and baseline shift on a base-size outer wrapper and applies relative font size to an inner span. Consequently a Core 0.25em spacing decision remains one quarter of the composition em even when the text run is rendered at 80%. A bundled internal compositeFontRun class gives the inner font span zero line height and baseline alignment, so a composite-font face or relative size changes glyph rendering without contributing a different height to the browser line box. This fixed adapter behavior is not repeated in styleRules. Baseline shift uses composition em and visual relative positioning, so it does not enlarge the browser line box. Composite-font runs therefore do not independently determine the surrounding line height; measured empty lines emit an invisible zero-width strut in the inherited font context.
Static Flow
import { renderStaticFlowToHtml } from "@typeset/adapter-web";
const output = renderStaticFlowToHtml(staticFlowResult, {
alignment: "justify",
});
host.innerHTML = output.html;
Insert the serialized output.styleRules as shown for Measured Composition when the Static Flow result contains composite-font runs.
Static Flow uses the same resolveCompositeFont option as Measured Composition. The resolver must return the final CSS family names that will exist in the consuming page. The adapter neither loads fonts nor emits @font-face; server-side callers may prepare those declarations independently and pass their family aliases through the resolver. If a system or browser-provided family is sufficient, return that family name directly.
Static Flow keeps browser soft wrapping while rendering hard segments, fixed and conditional spacing, known line-edge spacing, and unbreakable runs. Pass alignment: "start" | "justify" to express paragraph alignment with CSS; justification follows browser line breaking and leaves the final line unexpanded. The option is optional: when omitted, the adapter emits no alignment class and the host CSS cascade controls text alignment. The output reports effects that the current Web strategy cannot express exactly:
- Negative conditional spacing uses the existing negative-margin and adjacent soft-break approximation.
- Positive soft line-start spacing is reported as unsupported and is not rendered.
- Width-dependent effects already reported by core Static Flow are forwarded as unsupported diagnostics.
Optional annotations
The adapter can add semantic data-typeset-* annotations for inspection tools without owning their visual presentation:
const output = renderLayoutResultToHtml(layoutResult, {
annotations: { lines: true, hanging: true, spacing: true },
});
Annotations are omitted by default. Consumers such as the Solid Playground may target them from their own CSS to visualize overfull lines, hanging punctuation, spacing, or break constraints. The adapter’s CSS only implements required browser layout semantics.
Pretext measurement
The browser-only Pretext provider has a separate entry so Node and build-time Static Flow consumers do not load it:
import { createPretextMeasureToken } from "@typeset/adapter-web/pretext";
It measures a token’s natural advance in CSS pixels, normalizes it to composition em, and caches repeated text for the lifetime of the returned function. For a relatively sized run, pass the actual run size as fontSizePx and the base size as compositionEmPx. It does not manage Web Font loading or font fallback identity.
Security
The serializer escapes all source text and only emits internal tags, CSS Module classes, content-addressed dynamic classes, validated numeric styles, accessibility attributes, and explicitly requested data-typeset-* annotations. Dynamic CSS strings escape raw-text delimiters before they enter styleRules. The adapter does not accept raw HTML.