The HTML target
Intent
@quario/html serialises the event stream into a fragment you drop into your
own page. It’s not a document. There is no <html>, no <head>, and no
presentation beyond what a definition declared. The fragment goes into a
template, an email body or a server-rendered response.
Design decisions
This target is a fragment, not a painter, and not a flow target. It has no pages of its own to break or to hand to a reader’s application, which is why page bands go unread below. Painters, flow targets, and a fragment describes the family.
The fragment carries no pagination, and leaves page bands unread. Page
furniture belongs to the host’s print CSS. A group’s break maps to a class
instead of vanishing, so your print stylesheet can act on it. q-break marks
an instance whose leading edge turns, and q-break-after one whose trailing
edge does.
This target escapes everything a definition or its data contributes.
Escaping happens here, at the markup edge, because the event stream carries
unescaped data. The target escapes literal template text and header cells as
well as interpolated values. A definition’s own <h2> reaches the reader as the
characters it’s made of.
It escapes generated attributes too. Data attributes, inline styles,
src, alt and colspan all carry data. Class names are constants this
target owns.
The q-* selectors are the contract, and the stylesheet carries no look of
its own. It holds the band-role defaults, the table’s structure, and the honor
and pagination rules. They’re ordinary rules a later host sheet overrides on
source order. Since 0.7.0 it draws no hairline under a table’s header row, and
no rule or bold weight on its total rows. It draws no rule above the report
footer either, because no other target ever drew them. A report that declares no
borders now reads the same on screen as in the PDF and the worksheet.
Styled runs render as spans. One <span style="…"> per styled run,
carrying its whole resolved inline style. Unstyled runs stay bare, so a cell
without runs emits the markup it always did.
A fragment without the sheet still carries the classes and the text. It doesn’t occupy, break or paginate until some stylesheet says so.
The report default rides the root as an inline style. That’s the one place the rest of the document can inherit it from.
The unlicensed marking sits outside the report root. Inside it, an author’s
declared size would reach it. It’s the fragment’s first element and the
root’s sibling, holding only the escaped wording.
API walkthrough
Rendering
import { html } from '@quario/html'
const fragment = await report.render(html(), data)Rendering is asynchronous and returns control to the event loop between batches, so a large report doesn’t block the host. Compilation stays synchronous, and render errors reject with their location attached.
The same definition, data, and options produce an identical fragment. The target
stamps nothing, and an unformatted Date writes as ISO 8601 UTC rather than in
the host’s zone.
Styling the output
@import '@quario/html/style.css';Two rules in that sheet are worth knowing, because a fragment without them
behaves differently. .q-item carries min-height: 1lh and white-space: pre-line, so a visible text item occupies a line and a literal newline is a
break. Table cells carry pre-line too, and empty cells have no min-height,
so they don’t occupy.
The classes are yours to restyle. Override on source order, the way you would any vendor sheet.
A host that wants the look the sheet carried before 0.7.0 adds three rules after the shipped one.
.q-table thead th { border-bottom: 0.5pt solid #000; }
.q-table tfoot td { border-top: 0.5pt solid #000; font-weight: bold; }
.q-item.q-report-footer { border-top: 1pt solid #000; padding-top: 4pt; }The report header’s own rule is a factor rather than a point size:
.q-item.q-report-header is font-size: 1.4em. An unstyled fragment renders
its headline at 1.4× the host page’s body type rather than at a fixed 14pt. A
host whose body isn’t 10pt sees that move. A host rule on the class still
wins, the way it always did.
Linking a run
The target wraps a run carrying an href in an <a> inside its own span, so
the style the run declared paints the link. It’s a real anchor rather than a
styled span, so a keyboard reaches it the way it reaches any link. The target
escapes the URL at the one markup edge like every other interpolated value. The
engine admitted it before it crossed, so this target decides nothing about it.
A cell’s own href links the cell’s text. The target asks the style a run
wears, which is the cell’s wherever the run declares none. A run declaring its
own replaces the cell’s rather than nesting inside it.
Mapping a font family
import { html } from '@quario/html'
const target = html({ fonts: { Inter: 'var(--font-inter)' } })A declared family name maps to a CSS font-family value. Names match
case-insensitively, and a mapping may cover the generics like any other name. A
value containing ; or } throws from html() rather than reaching the page.
This mapping resolves names a report declared, and never the face of text that declared nothing. That baseline lives in the reference stylesheet, which is where a host moves it.
Mapping output back to the definition
import { html } from '@quario/html'
const target = html({ paths: true })Each element whose event carries a schema path gets data-q-path. It’s off
by default, and it’s the selection seam the editor builds on.
Printing
A fragment plus a print stylesheet is a legitimate route to paper, and it’s the one the browser already knows how to drive. The trade is that pagination becomes the browser’s, so page bands, repeated table headers, and deterministic bytes aren’t part of the bargain. Where those matter, render through the PDF target instead, from the same definition.
A document that declares its paper gets one <style> in the fragment. It
holds an @page rule with that size and each margin, so the print dialog opens
on the right sheet. It’s the only <style> this target writes.
A note on Content Security Policy
Conditional formatting emits inline style attributes, so a restrictive
style-src has to account for them. An image item embeds bytes as a data:
URI (data:image/svg+xml for an SVG), which needs img-src data:. Neither is a
hole, and neither affects the no-string-to-code guarantee, which covers script
execution.
Trust and CSP has the whole picture.