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
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
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
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
const STYLE_NAMES: readonly string[]
const RUN_STYLE_NAMES: readonly string[]
const REPORT_STYLE_NAMES: readonly string[]
function isBoxName(name: string): booleanThe 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.