Escape

Style declarations

One closed vocabulary, target-neutral. Each render target maps it to its own model. An unknown name is a definition error. Every value may be a literal or an = expression.

StyleDeclarations

Declaration Value Meaning
family "sans", "serif", "mono", or an embedded family name typeface family
size number font size in points
bold boolean weight
italic boolean slant
underline boolean underline decoration
strikethrough boolean strikethrough decoration
uppercase boolean draw the text in capitals
color #rgb or #rrggbb text colour, hex only
background #rgb or #rrggbb background colour, hex only
align "left", "center", "right" horizontal alignment in the cell
valign "top", "middle", "bottom" vertical alignment in a box taller than its content
format a kind, or { kind, digits, negative, zero } / { kind, form } how a number or date presents
currency ISO 4217 code the denomination of a currency cell
href a URL, or # and a group’s label where the text points
spaceBefore / spaceAfter number >= 0 blank space before or after a band item, in points

Plus the box below.

valign is legal only where a box has height it didn’t ask for, which means table cells and split slots. On a stacked text item, a band image, or the report default it’s a definition error.

BoxDeclarations

Padding and border, per side. <Side> is Top, Right, Bottom or Left.

Declaration Value Meaning
padding<Side> number >= 0 inset in points on that side
border<Side>Width number >= 0 stroke width in points, 0 draws nothing
border<Side>Style "solid", "dashed", "dotted" line style of that side
border<Side>Color #rgb or #rrggbb stroke colour of that side

Border-box, no collapse. A border side is width, style and colour together, or none. An incomplete literal is a definition error, while an incomplete expression result contributes nothing at render.

FormatDeclaration

signature
type FormatKind = 'number' | 'currency' | 'percent' | 'date'
type DateForm = 'short' | 'medium' | 'long' | 'full'

type NegativeReading = 'minus' | 'parens'
type ZeroReading = 'number' | 'dash'

type FormatDeclaration =
  | FormatKind
  | {
      kind: 'number' | 'currency' | 'percent'
      digits?: number
      negative?: NegativeReading
      zero?: ZeroReading
    }
  | { kind: 'date'; form?: DateForm }

digits is a whole number from 0 to 20. negative chooses -1,204.00 or (1,204.00), and zero chooses 0.00 or -. Both default to the first, so no existing definition moves, and date takes neither. A modifier a kind doesn’t understand is a definition error. Only the whole declaration may be an expression, never a modifier inside it.

format and currency are legal wherever a node has a cell value, which means text items, column cells, headers, and total cells. Both are definition errors on an image, on a row’s style, and on the report default. Both are legal on a styled run, which carries the inline half of the vocabulary.

ResolvedFormat

signature
type ResolvedFormat =
  | {
      kind: 'number' | 'currency' | 'percent'
      digits?: number
      negative: NegativeReading
      zero: ZeroReading
    }
  | { kind: 'date'; form: DateForm }

What crosses the event stream. Never the author’s shorthand, always the object with the answer filled in.

Narrowed sets

signature
type ReportStyleDeclarations = Pick<StyleDeclarations, 'family' | 'size'>

type ImageStyleDeclarations = Pick<
  StyleDeclarations,
  'background' | 'align' | 'spaceBefore' | 'spaceAfter' | keyof BoxDeclarations
>

type StackedStyleDeclarations = Omit<StyleDeclarations, 'valign'>

type CellStyleDeclarations = Omit<StyleDeclarations, 'spaceBefore' | 'spaceAfter'>

type RowStyleDeclarations = Omit<
  StyleDeclarations,
  'format' | 'currency' | 'href' | 'spaceBefore' | 'spaceAfter'
>

type SlotStyleDeclarations = Omit<StyleDeclarations, 'spaceBefore' | 'spaceAfter'>

type RunStyleDeclarations = Pick<
  StyleDeclarations,
  | 'family' | 'size' | 'bold' | 'italic' | 'underline' | 'strikethrough'
  | 'color' | 'background' | 'uppercase' | 'format' | 'currency' | 'href'
>

Each set states a narrowing the engine has always applied, so a node’s style type refuses what a render would have refused. The report default carries family and size only, and an image carries no text declarations. A stacked item drops valign: its box is its own height, so there is nothing to align against. A slot’s box is the split’s height, which is why a slot keeps the name. A table cell and a split slot drop flow spacing, which describes a band item rather than a cell. A row box drops that too, and with it the three names that present a value. A box wraps cells and holds none of its own, so format, currency and href have nothing to act on. A styled run carries the inline half. The box and flow spacing describe a block, and align describes a line. Any of them on a run is a definition error naming the run.

Layering

Five layers, outermost in: the target’s own baseline, the report default, then the band-role default a target supplies for headline roles. Then the node’s own style, and last a styled run’s. A split’s style is a node’s own for every slot under it, so it outranks the band-role default. The report header’s band-role size is a factor over the document’s, 1.4×, so a document declaring size: 20 renders its headline at 28.

format layers whole. A cell that names it restates the entire declaration.

STYLE_NAMES

signature
const STYLE_NAMES: readonly string[]
const RUN_STYLE_NAMES: readonly string[]
const REPORT_STYLE_NAMES: readonly string[]
function isBoxName(name: string): boolean

The vocabulary as data, in the order this page lists it. Import it rather than keeping a copy. RUN_STYLE_NAMES is the run’s subset, in the same order, and REPORT_STYLE_NAMES is the report default’s two names. isBoxName answers whether a name is part of the box, a padding or a border side. That’s the partition that defines a table row’s box. A target that removes the box from a style asks it rather than matching the two prefixes itself.