Skip to content

v5.2.0

Choose a tag to compare

@github-actions github-actions released this 04 Oct 19:19
· 228 commits to master since this release

A PDF is the page, printed. The PDF and slide writers are replaced by one
paged layout engine: the program is rendered to the HTML and CSS a
template renders, and the compiler lays that out on paper itself — the
real cascade, block, flex and grid layout, shaped and bidirectional text,
pagination, and a PDF with embedded font subsets, an outline and links. No
browser, no external tool. A design that works on the web works on paper.

The engine

  • CSS, as the page has it. The engine's rules for every built-in, the
    theme's tokens, the project's .css files and every style { } block,
    through a cascade with combinators, attribute selectors,
    :first-child/:nth-child, specificity, inheritance, var(), em,
    rem, % and ::before/::after. @media print applies.
  • Layout. Block, flex and grid, absolute positioning, gap,
    min-/max- sizes, aspect-ratio, inline-blocks on a line, tables
    sized by their content, list markers.
  • Text. Shaped (ligatures, kerning, Arabic joining), bidirectional,
    broken at Unicode's opportunities, justified. A right-to-left document
    mirrors its rows, grids, tables and list markers.
  • Paint. Gradients with any number of stops, background pictures,
    per-side borders with radii, shadows, opacity, clipping, rotation; PNG,
    JPEG, WebP, GIF and SVG (kept vector).
  • Pagination. Lines kept two to a side, tables broken between rows with
    their header repeated, side-by-side columns broken each on its own,
    headings kept with what follows, break-inside: avoid, PageBreak and
    break-before/break-after.

New elements

  • Header(on:), Footer(on:) with page and pages in scope;
    Background(on:), drawn edge to edge behind each page;
    Watermark("DRAFT").
  • TableOfContents(levels:, title:) — headings with the page each
    lands on, dotted leaders, linked.
  • Chart(kind: .bar | .line | .area | .pie | .donut, …) and
    QrCode(value), vector graphics in a PDF, and SVG on a web page
    where what they read is known at build time.
  • Document(size:, landscape, margin:, title:, author:, subject:, keywords:, lang:) names the paper and the file's metadata.

Fonts

Liberation Sans, Serif and Mono are built into wf, so Helvetica,
Arial, sans-serif, Times and monospace resolve with nothing
installed. .ttf/.otf files under fonts/ (or src/fonts/,
public/fonts/, pdf.fonts) and local @font-face rules are used by
their family name, variable fonts at the asked weight. A character no
named face has is drawn from one that has it; the build names any family
taken from the machine, and "system_fonts": false forbids it.

Decks

  • A Presentation holds for, if and slide components: a deck
    writes its openers and footers once and makes a slide per item of its
    data.
  • A page's state and derived values have their values on paper —
    and in any template render — so a slide shows a figure worked out from a
    data file. Data handed to a render wins over either.
  • :is() and :where() in the paged cascade. The engine's slide
    heading sizes use :where, and the slide margin is a default, so a
    deck's own classes always win.

Templates

  • render_pdf_report and render_slides_report return a PdfReport:
    the bytes, the page count, each page's text in reading order and the
    notes. A template reads fonts and pictures from beside its files.

The editor

  • The language server knows what the project writes. In a pdf or
    slides project, completion offers only what paper draws — no
    Button, no on click, no resource — a Presentation is offered its
    slides and the project's slide components, and hover says when an
    element is not drawn in this output and why. page and pages are names
    inside a Header, Footer or Background. On a web project the
    paper-only elements are offered last.

Fixes

  • Right to left, a flex row runs from the right. taffy already
    mirrors a row under direction: rtl; the engine reversed it again, so
    rows in an Arabic PDF came out left to right.
  • A keyed list no longer draws a row twice when a handler that runs as
    a row leaves — an input's blur — writes the list while it is being
    drawn.
  • A Link whose to has a query (/?show=open) is active only when
    the address has those values too; a row of filter links no longer marks
    every one as the current page.
  • Attributes on a component's call reach its root — class:,
    aria-*, data-*, id, role, title, hidden — on the live page
    and in the static paint; before, they were dropped without a word. A
    component that declares a prop of that name still receives it.
  • A Bool prop given a condition — outlined: theme != "light" —
    follows it, instead of becoming an attribute.
  • The static paint draws what the live page does: a component's
    derived that reads a store, a class: list with one entry only the
    browser knows, hidden:.
  • On a page with a layout, the skip link jumps past the layout's
    chrome
    to where the page starts.
  • wf test: expect reads the text a reader sees, not the markup; a
    date or money literal draws; data: seeds stores in a test that clicks;
    wf test tests/x.wf runs that file in its project.
  • A validate rule's message may be t("key"), and a rule's argument
    that reads state is read when it checks.
  • xs.filter(x => x != null) is a list of what is there ([T?] to
    [T]).
  • wf fmt leaves arguments laid out over lines as written.
  • The breadcrumb JSON-LD lists only pages a reader can open; a page's
    head { } counts as reading its values (no false U02); a store
    action called in the store's own initial value is named for what it is.
  • Host(title:) is the title attribute.

Diagnostics

  • V11: a component of the project's own named like a built-in
    (component Chart), which shadows it.
  • E109 now refuses only what a page means only in a browser;
    everything static draws.

Examples

examples/documents/ holds three reference documents: a nine-page
Halyard platform report (dark cover, contents, charts, a 64-row table that
repeats its header, incidents, a QR code), an invoice (band, watermark,
repeated header, totals kept whole), and an Arabic résumé laid out right to
left.

examples/decks/ holds three slide decks: WebFluent itself in the docs
site's design system, Halyard's quarterly review drawn from the report's
data (a slide per incident from a for), and an Arabic workshop.

Tutorials

The cookbook is now Tutorials: three projects built step by step, each
complete under examples/tutorials/ and none needing a server — Todos
(easy), Field Notes, a statically built notes site (medium), and
Pocket, a budget tracker with forms, i18n and offline (hard). A test
holds every file the chapter shows to the project it comes from.

Editors

The tree-sitter grammars and the VS Code grammar highlight the new
elements.

Full Changelog: v5.1.1...v5.2.0