css

package
v0.8.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 18, 2026 License: BSD-3-Clause Imports: 7 Imported by: 1

Documentation

Overview

Package css implements a deliberately small but real subset of CSS: a value model, a tokenizer/parser for stylesheets and declaration blocks, tag/class/ id selectors with specificity, and a cascade with inheritance over a dom tree. It targets the handful of properties Phase 0 needs — display, color, background-color, font-size, font-weight, font-family, margin, padding, width, text-align — plus a user-agent default stylesheet for common tags.

Index

Constants

View Source
const (
	Screen = "screen"
	Print  = "print"
)

Media types a Media.Type can name. Any other type (speech, tv, …) never matches.

View Source
const DefaultViewportWidth = 1024

DefaultViewportWidth is the viewport width (CSS px) used to evaluate @media width queries when no explicit width is supplied. It matches a desktop render.

Variables

View Source
var (
	// Sans is the sans-serif bucket with no named family.
	Sans = FontFamily{Generic: GenericSans}
	// Serif is the serif bucket with no named family.
	Serif = FontFamily{Generic: GenericSerif}
	// Mono is the monospace bucket with no named family.
	Mono = FontFamily{Generic: GenericMono}
)

The three generic families, for the places that mean the bucket itself: the UA default, and the bundled faces the font layer registers. Values rather than constants because Go has no struct constant; do not assign to them.

View Source
var Transparent = Color{}

Transparent is the fully-transparent colour (the initial background-color).

Functions

func ImportURLs

func ImportURLs(sheet string) (urls []string, medias []string)

ImportURLs extracts the targets of leading @import at-rules from a stylesheet, in order. Per the CSS spec @import rules must precede all other rules (except @charset), so only the leading run is honoured; the first non-@import, non-@charset, non-comment token stops the scan. Both @import "url" and @import url(...) forms are supported, with an optional trailing media query (returned via the paired media slice, "" when absent).

func InlineStyleText added in v0.5.10

func InlineStyleText(root *dom.Node) string

InlineStyleText returns the document's embedded stylesheet: the text of every <style> element under root, in document order. It is what the cascade itself reads besides the linked sheets, exported so a consumer of the OTHER at-rules a stylesheet carries (the engine's @font-face loading) sees the same CSS the cascade does.

func MediaApplies

func MediaApplies(media string, vw float64) bool

MediaApplies reports whether a media query (from a <link media> attribute or an @import) applies at viewport width vw. It reuses the same simplified evaluation as @media blocks: an empty query always applies, "print" never does, and min-width/max-width pixel features are honoured against vw. A comma-separated media list matches if ANY component matches.

func MediaAppliesTo added in v0.4.0

func MediaAppliesTo(media string, m Media) bool

MediaAppliesTo is MediaApplies against a Media: a <link media="print"> applies under Media{Type: Print} and nowhere else, "screen" the reverse.

Types

type AlignContent

type AlignContent uint8

AlignContent distributes flex lines (or grid tracks) along the cross axis when there is spare cross-axis space (multi-line flex / align-content).

const (
	// AlignContentStretch stretches lines to fill the cross axis (initial).
	AlignContentStretch AlignContent = iota
	// AlignContentStart packs lines at the cross-start edge.
	AlignContentStart
	// AlignContentEnd packs lines at the cross-end edge.
	AlignContentEnd
	// AlignContentCenter centres the lines as a group.
	AlignContentCenter
	// AlignContentSpaceBetween spreads lines with the ends flush.
	AlignContentSpaceBetween
	// AlignContentSpaceAround spreads lines with half-gaps at the ends.
	AlignContentSpaceAround
	// AlignContentSpaceEvenly spreads lines with equal gaps incl. the ends.
	AlignContentSpaceEvenly
)

type AlignItems

type AlignItems uint8

AlignItems is the cross-axis alignment (align-items). In a grid container it is also reused for the block-axis (align-items) and, via JustifyItems, the inline-axis (justify-items) alignment of items within their cells.

const (
	// AlignStretch stretches items to fill the cross axis (initial value).
	AlignStretch AlignItems = iota
	// AlignFlexStart aligns items at the cross-start edge.
	AlignFlexStart
	// AlignFlexEnd aligns items at the cross-end edge.
	AlignFlexEnd
	// AlignCenterItems centres items on the cross axis.
	AlignCenterItems
)

type AlignSelf

type AlignSelf uint8

AlignSelf overrides a single item's cross-axis alignment. Auto (the initial value) defers to the container's align-items.

const (
	// AlignSelfAuto uses the container's align-items value (initial).
	AlignSelfAuto AlignSelf = iota
	// AlignSelfStretch stretches this item on the cross axis.
	AlignSelfStretch
	// AlignSelfStart aligns this item at the cross-start edge.
	AlignSelfStart
	// AlignSelfEnd aligns this item at the cross-end edge.
	AlignSelfEnd
	// AlignSelfCenter centres this item on the cross axis.
	AlignSelfCenter
)

func (AlignSelf) Resolve

func (a AlignSelf) Resolve(container AlignItems) AlignItems

resolve returns the effective AlignItems for an item, falling back to the container's align-items when the item's align-self is auto.

type BgImage

type BgImage struct {
	Kind BgImageKind
	URL  string    // raw url() argument (BgURL); resolved by the engine
	Grad *Gradient // gradient spec (BgGradient)
}

BgImage is one background-image layer: a url() bitmap or a gradient. Layers are stored first-listed-first; the first layer paints on top (CSS order).

type BgImageKind

type BgImageKind uint8

BgImageKind identifies the kind of a single background-image layer.

const (
	// BgNone is an empty layer (the keyword `none`).
	BgNone BgImageKind = iota
	// BgURL is a url(...) bitmap layer.
	BgURL
	// BgGradient is a linear/radial gradient layer.
	BgGradient
)

type BgPosition

type BgPosition struct {
	X, Y Length
}

BgPosition is a resolved background-position (percent/px per axis). The initial value is the top-left corner (0%, 0%).

type BgRepeat

type BgRepeat uint8

BgRepeat is the background-repeat value.

const (
	// RepeatBoth tiles on both axes (initial value).
	RepeatBoth BgRepeat = iota
	// RepeatX tiles horizontally only.
	RepeatX
	// RepeatY tiles vertically only.
	RepeatY
	// NoRepeat paints the image once.
	NoRepeat
)

type BgSize

type BgSize struct {
	Kind BgSizeKind
	W    Length // meaningful when SizeExplicit
	H    Length // meaningful when SizeExplicit; Auto keeps aspect ratio
}

BgSize is a resolved background-size value.

type BgSizeKind

type BgSizeKind uint8

BgSizeKind selects how a background image is scaled.

const (
	// SizeAuto uses the image's intrinsic size (gradients fill the box).
	SizeAuto BgSizeKind = iota
	// SizeCover scales to cover the whole box (may crop).
	SizeCover
	// SizeContain scales to fit inside the box (may letterbox).
	SizeContain
	// SizeExplicit uses the given width/height lengths (a length or auto each).
	SizeExplicit
)

type BorderSide

type BorderSide struct {
	Width float64
	Style BorderStyle
	Color Color
}

BorderSide is one edge's border: its width, line style and colour.

type BorderStyle

type BorderStyle uint8

BorderStyle is the subset of border-style the engine paints. Any non-none, non-hidden line style renders as a solid line (dashed/dotted/etc. collapse to solid at this fidelity).

const (
	// BorderNone is the initial value: no border line (even if width > 0).
	BorderNone BorderStyle = iota
	// BorderSolid renders a solid line of the border colour.
	BorderSolid
)

type Borders

type Borders struct{ Top, Right, Bottom, Left BorderSide }

Borders is the four border edges of a box.

func (Borders) Widths

func (b Borders) Widths() Edges

Widths returns the four border widths as Edges (0 when the style is none, so layout only reserves space for painted borders — matching a border-style:none edge contributing no width even if border-width is set).

type BoxShadow

type BoxShadow struct {
	OffsetX, OffsetY float64
	Blur, Spread     float64
	Color            Color
	Inset            bool
}

BoxShadow is one box-shadow layer.

type BoxSizing

type BoxSizing uint8

BoxSizing selects whether width/height apply to the content box or the border box.

const (
	// ContentBox is the initial value: width is the content width.
	ContentBox BoxSizing = iota
	// BorderBox: width includes padding and border.
	BorderBox
)

type Break added in v0.4.0

type Break uint8

Break is a break-before / break-after value (CSS Fragmentation Level 3) as it matters to paged media: a forced page break (page, left, right), an avoided one, or neither. The column- and region-only values (column, avoid-column, region, avoid-region) say nothing about pages and map to BreakAuto. This engine's own screen layout never fragments; the value is carried on Style for a paginating consumer (go-pdfkit/html2pdf and the paginate package) to read.

const (
	// BreakAuto is the initial value: neither force nor forbid a break.
	BreakAuto Break = iota
	// BreakAvoid avoids a page break at this edge (avoid, avoid-page).
	BreakAvoid
	// BreakPage forces a page break: page, the legacy `always`, all, recto
	// and verso — any forced break that does not also choose a page side.
	BreakPage
	// BreakLeft forces one or two page breaks so the next page is a left page.
	BreakLeft
	// BreakRight forces one or two page breaks so the next page is a right
	// page.
	BreakRight
)

type BreakInside added in v0.4.0

type BreakInside uint8

BreakInside is a break-inside value for paged media.

const (
	// BreakInsideAuto is the initial value: a page break may fall inside the
	// box.
	BreakInsideAuto BreakInside = iota
	// BreakInsideAvoid asks that no page break fall inside the box (avoid,
	// avoid-page). avoid-column / avoid-region say nothing about pages and
	// map to BreakInsideAuto.
	BreakInsideAvoid
)

type Clear

type Clear uint8

Clear is the subset of the clear property the engine understands.

const (
	// ClearNone is the initial value.
	ClearNone Clear = iota
	// ClearLeft clears past left floats.
	ClearLeft
	// ClearRight clears past right floats.
	ClearRight
	// ClearBoth clears past floats on both sides.
	ClearBoth
)

type Color

type Color struct{ R, G, B, A uint8 }

Color is an 8-bit-per-channel RGBA colour. A==0 is treated as transparent.

type ColorScheme added in v0.5.10

type ColorScheme uint8

ColorScheme is the computed value of the inherited `color-scheme` property (CSS Color Adjust 1 §2.2) reduced to what this engine acts on: which of the two UA-supported schemes the element declares support for. The zero value is `normal` (no scheme declared). Custom-ident schemes are ignored, and so is `only` (it forbids UA overrides such as forced colours, which this engine never applies).

const (
	ColorSchemeLight ColorScheme = 1 << iota // `light` listed
	ColorSchemeDark                          // `dark` listed
)

func (ColorScheme) UsedDark added in v0.5.10

func (cs ColorScheme) UsedDark() bool

UsedDark reports whether the element's used colour scheme is dark, per the "determine the used color scheme" algorithm of CSS Color Adjust 1 §2.3 with this engine's fixed preference: when an element supports BOTH schemes the preferred one is used, which here is dark — the same fixed answer js/window.go's matchMedia gives for `prefers-color-scheme` (the bench suite's reference Chromium runs with a dark appearance). One supported scheme is used as is. With none (`normal`) the UA default scheme applies, which is light: Gecko's LookAndFeel::ColorSchemeForStyle ("Otherwise default content to light"), and Chromium likewise renders a page that never opts in light.

type ColorStop

type ColorStop struct {
	Color  Color
	Pos    Length
	HasPos bool
}

ColorStop is one gradient colour stop. Pos is the parsed position (a percent or px length); HasPos is false when the stop had no explicit position and its fraction is interpolated during normalisation.

type ContainerCondition added in v0.4.0

type ContainerCondition struct {
	// Name filters which ancestor container this condition may match against
	// ("" means: the nearest ancestor container of ANY name).
	Name string
	// Cond is the lowercased, trimmed size-feature condition text (e.g.
	// "(min-width: 400px)"), or "" for a name-only condition with no size
	// feature (matches unconditionally once a qualifying container exists).
	Cond string
}

ContainerCondition is an `@container` rule's condition, attached to every Rule nested (directly or via @media/@layer) inside the at-rule. Unlike a `@media` condition — known before any element is visited, since it only depends on the viewport — a `@container` condition depends on an ANCESTOR ELEMENT's actual laid-out size, which is per-matched-element (different elements matching the same selector can have different nearest containers) and only known once at least one layout pass has run. So, unlike @media, this is not resolved away at parse time: it rides along on the Rule and is evaluated during cascade, per candidate element, against whatever container sizes the caller supplies (nil before the first layout pass, which makes every @container rule inactive until real geometry is available).

type ContainerSize added in v0.4.0

type ContainerSize struct {
	InlineSize float64
	BlockSize  float64
}

ContainerSize is a query container's resolved size, gathered from a layout pass and fed back into the cascade so `@container` conditions can be evaluated against it. InlineSize is the container's border-box width; BlockSize (border-box height) is only populated — and only ever consulted — when the container has size containment on both axes (ContainerType == ContainerTypeSize): querying the block axis of an inline-size-only container is invalid per spec and always fails to match here, the same way it does in a browser.

type ContainerType added in v0.4.0

type ContainerType uint8

ContainerType is the computed value of the `container-type` property: it controls whether an element establishes a query container that descendant `@container` rules can condition on, and which of its axes are available to query. It is NOT inherited (an element resets to ContainerNormal unless its own declarations set it).

const (
	// ContainerNormal is the initial value: the element establishes no query
	// container (but may still be a size/layout containment target for other
	// reasons this engine does not model).
	ContainerNormal ContainerType = iota
	// ContainerInlineSize establishes a query container queryable on its
	// inline axis (width, in this engine's horizontal writing mode) only.
	ContainerInlineSize
	// ContainerTypeSize establishes a query container queryable on BOTH axes
	// (inline and block / width and height). Named ContainerTypeSize (rather
	// than the more obvious ContainerSize) because that name is already taken
	// by the ContainerSize struct just below, for a container's RESOLVED size
	// — a different, unrelated concept that also needed the name.
	ContainerTypeSize
)

type Declaration

type Declaration struct {
	Property  string
	Value     string
	Important bool
}

Declaration is a single property: value pair. Important marks a trailing `!important` on the original declaration; it is consulted by the cascade, never by a property's value parser (Value never carries the marker).

func ParseDeclarations

func ParseDeclarations(body string) []Declaration

ParseDeclarations parses a declaration block body ("a: b; c: d") into declarations, lowercasing property names and trimming values.

type Display

type Display uint8

Display is the subset of the display property the engine understands.

const (
	// DisplayInline is the default for unknown/inline elements.
	DisplayInline Display = iota
	// DisplayBlock stacks the box vertically in block flow.
	DisplayBlock
	// DisplayNone removes the element (and subtree) from layout.
	DisplayNone
	// DisplayInlineBlock is an atomic inline-level block (laid out as a block
	// but participating inline; Phase 1 treats it as block for simplicity).
	DisplayInlineBlock
	// DisplayFlex is a block-level flex container.
	DisplayFlex
	// DisplayTable is a block-level table box.
	DisplayTable
	// DisplayTableRow is a table row box.
	DisplayTableRow
	// DisplayTableCell is a table cell box.
	DisplayTableCell
	// DisplayTableRowGroup is a thead/tbody/tfoot grouping box (transparent to
	// the table's row collection).
	DisplayTableRowGroup
	// DisplayGrid is a block-level CSS grid container.
	DisplayGrid
	// DisplayContents makes the element generate no box of its own — its
	// children behave as if they were direct children of ITS parent instead
	// (the element itself becomes fully transparent to layout, though it still
	// exists in the DOM/cascade). See layout.renderedChildren, the single
	// chokepoint every layout algorithm (block, inline, flex, grid, table)
	// walks a container's children through, which is where this is resolved —
	// so a DisplayContents element never itself reaches box placement.
	DisplayContents
	// DisplayInlineFlex is a flex container that is itself an INLINE-level
	// box in its parent's flow (unlike DisplayFlex, which is block-level) —
	// distinct from DisplayFlex specifically so isBlockLevel and the inline-
	// collection walk can tell them apart; internally it lays out its
	// children with the identical flex algorithm. Confirmed live on
	// pkg.go.dev: `.go-Breadcrumb li{display:inline-flex}` stacked each
	// breadcrumb item on its own line instead of flowing in a row, because
	// this engine previously parsed `inline-flex` down to the SAME
	// DisplayFlex value as `flex`, losing the "inline" qualifier entirely and
	// making every such element block-level. See
	// layout.appendElementInline's own handling for how the atomic
	// inline-level box is produced.
	DisplayInlineFlex
)

type Edges

type Edges struct{ Top, Right, Bottom, Left float64 }

Edges holds the four sides of a margin or padding box, in pixels.

type Filter added in v0.4.0

type Filter struct {
	Kind   FilterKind
	Amount float64 // multiplier / fraction / blur sigma (px) / hue radians

	// Drop-shadow parameters (only meaningful when Kind == FilterDropShadow).
	OffsetX, OffsetY float64
	Blur             float64
	Color            Color
	UseCurrentColor  bool // resolve Color from the element's `color` at paint time
}

Filter is one parsed `filter` function.

type FilterKind added in v0.4.0

type FilterKind int

FilterKind identifies one CSS `filter` function.

const (
	// FilterBlur is blur(<length>): a Gaussian blur whose standard deviation is
	// Amount pixels.
	FilterBlur FilterKind = iota
	// FilterBrightness is brightness(<number|percentage>): a per-channel linear
	// multiply by Amount (Amount>=0, may exceed 1).
	FilterBrightness
	// FilterContrast is contrast(<number|percentage>): scales each channel about
	// the 0.5 mid-point by Amount.
	FilterContrast
	// FilterSaturate is saturate(<number|percentage>): a saturation matrix with
	// Amount as the saturation factor (may exceed 1).
	FilterSaturate
	// FilterGrayscale is grayscale(<number|percentage>): interpolates towards
	// luminance by Amount in [0,1].
	FilterGrayscale
	// FilterSepia is sepia(<number|percentage>): interpolates towards a sepia
	// tone by Amount in [0,1].
	FilterSepia
	// FilterHueRotate is hue-rotate(<angle>): rotates hue by Amount radians.
	FilterHueRotate
	// FilterInvert is invert(<number|percentage>): interpolates towards the
	// negative by Amount in [0,1].
	FilterInvert
	// FilterDropShadow is drop-shadow(<offset-x> <offset-y> <blur>? <color>?):
	// a blurred, offset, coloured copy of the element's alpha painted behind it.
	FilterDropShadow
)

type FlexDirection

type FlexDirection uint8

FlexDirection is the main-axis direction of a flex container.

const (
	// FlexRow lays items along the inline (horizontal) axis.
	FlexRow FlexDirection = iota
	// FlexColumn lays items along the block (vertical) axis.
	FlexColumn
)

type FlexWrap

type FlexWrap uint8

FlexWrap controls whether flex items wrap onto multiple lines.

const (
	// FlexNoWrap keeps all items on a single line (initial value).
	FlexNoWrap FlexWrap = iota
	// FlexWrapOn wraps items onto new lines toward the cross-end.
	FlexWrapOn
	// FlexWrapReverse wraps items with the cross axis reversed.
	FlexWrapReverse
)

type Float

type Float uint8

Float is the subset of the float property the engine understands.

const (
	// FloatNone is the initial value (no float).
	FloatNone Float = iota
	// FloatLeft floats the box to the left of its container.
	FloatLeft
	// FloatRight floats the box to the right of its container.
	FloatRight
)

type FontFace added in v0.4.0

type FontFace struct {
	// Family is the family name, lowercased: `font-family` inside the rule,
	// which is what a `font-family` declaration elsewhere has to name to
	// reach this face.
	Family string
	// Srcs are the `src` entries in declaration order — a consumer takes the
	// first whose Format it can decode. `local()` entries are dropped: there
	// is no system font database here to look a face up in.
	Srcs []FontSrc
	// Weight is the weight the files carry, 400 when the rule gives none. A
	// range ("font-weight: 100 900", a variable font) collapses to its first
	// value, since only a static instance can be loaded.
	Weight int
	// Italic is set for `font-style: italic` or `oblique`.
	Italic bool
}

FontFace is one `@font-face` rule: a named family, the files it can be loaded from in preference order, and which weight and slant of that family the files carry.

A page that wants a typeface the engine does not bundle says so here and nowhere else, so this is the only route by which a document's own fonts can reach it. Before this existed, `@font-face` was skipped as an unrecognised at-rule and every run was set in one of the three bundled families — so a poster asking for IBM Plex Sans and Spectral was typeset in Inter and Lora, silently and at different metrics.

func ParseFontFaces added in v0.4.0

func ParseFontFaces(src string, m Media) []FontFace

ParseFontFaces returns every `@font-face` rule in a stylesheet source, in document order, including those nested in an `@media` block whose condition matches m. A rule with no family or no usable src is dropped.

It is a separate pass from ParseStylesheetMedia rather than another Rule variant: an @font-face rule has no selector and takes no part in the cascade, so nothing that walks rules should have to learn to skip it.

type FontFamily

type FontFamily struct {
	// Names are the non-generic families named, lowercased and joined with
	// commas in declaration order. Empty when the declaration named only
	// generic keywords.
	Names string
	// Generic is the bucket to use when no named family is available — the
	// generic keyword the declaration ended with, or the one implied by a
	// name the engine recognises ("helvetica" means sans).
	Generic Generic
}

FontFamily is a resolved `font-family`: the families the declaration named, in order, and the generic bucket to fall back on when none of them can be loaded.

It used to be the bucket alone, which meant a document could never ask for a typeface by name: a poster written for IBM Plex Sans and Spectral was typeset in the bundled Inter and Lora, silently and at different metrics. Names is what an @font-face rule (see FontFace) is matched against.

It stays comparable — a struct of a string and a byte — because it is a map key in the font layer, which caches a parsed face per (family, weight, slant).

func (FontFamily) NamedFamilies added in v0.4.0

func (f FontFamily) NamedFamilies() []string

NamedFamilies returns the families this declaration named, in the order it named them — what a consumer walks to find the first face it holds.

type FontSrc added in v0.4.0

type FontSrc struct {
	// URL is the url() as written — relative to the stylesheet it came from,
	// which is the caller's to resolve.
	URL string
	// Format is the format() hint, lowercased and unquoted ("truetype",
	// "woff2", "opentype", …), or empty when the rule gave none. It is a
	// HINT: a consumer that cannot decode a format should skip the entry,
	// but an entry without one may still be anything.
	Format string
}

FontSrc is one entry of an `@font-face` rule's `src`.

type Generic added in v0.4.0

type Generic uint8

Generic is one of CSS's generic font families — the bucket a run falls into when none of the families it named is available.

const (
	// GenericSans is the default sans-serif bucket.
	GenericSans Generic = iota
	// GenericSerif is the serif bucket.
	GenericSerif
	// GenericMono is the monospace bucket.
	GenericMono
)

type Gradient

type Gradient struct {
	Radial bool

	// Linear direction. Corner is 0 for an explicit angle (AngleDeg), else a
	// corner code 1..4 (top-right/bottom-right/bottom-left/top-left) whose angle
	// depends on the box size and is resolved in the sampler.
	AngleDeg float64
	Corner   uint8

	// Radial geometry.
	Shape            RadialShape
	Extent           RadialExtent
	RadiusX, RadiusY Length // meaningful when Extent==ExtentExplicit
	PosX, PosY       Length // centre; default 50% 50%

	Stops []ColorStop
}

Gradient is a parsed linear or radial gradient.

func (*Gradient) Sampler

func (g *Gradient) Sampler(w, h float64) GradientSampler

Sampler builds a GradientSampler for a box of the given size (W×H, pixels).

type GradientSampler

type GradientSampler struct {
	// contains filtered or unexported fields
}

GradientSampler evaluates a gradient's colour at any point of a W×H box. It is built once per box (Gradient.Sampler) with its geometry and normalised stops precomputed, then queried per pixel with At.

func (GradientSampler) At

func (s GradientSampler) At(px, py float64) Color

At returns the gradient colour at box-local point (px, py).

type GridFlow

type GridFlow uint8

GridFlow is the auto-placement direction of a grid container.

const (
	// GridFlowRow fills each row before moving to the next (initial).
	GridFlowRow GridFlow = iota
	// GridFlowColumn fills each column before moving to the next.
	GridFlowColumn
)

type GridLine

type GridLine struct {
	Auto bool
	Span bool
	N    int
}

GridLine is one endpoint of a grid item's placement. Auto means the endpoint is chosen by auto-placement; Span means the endpoint is "span N" tracks from the opposite edge; otherwise N is a 1-based grid line number.

type ImageRendering added in v0.4.0

type ImageRendering uint8

ImageRendering selects the resampling filter used to scale a raster image (an <img> or a background-image) to a size other than its intrinsic one. It inherits (per CSS Images 3).

const (
	// IRAuto lets the engine pick a high-quality (smooth) filter — a bicubic
	// resample. It is the initial value and also covers `smooth` / `high-quality`.
	IRAuto ImageRendering = iota
	// IRPixelated preserves hard pixel edges by nearest-neighbour sampling, the
	// behaviour pixel art wants (`image-rendering: pixelated`). `crisp-edges`
	// maps here too: both ask the engine not to smooth the image.
	IRPixelated
)

type Justify

type Justify uint8

Justify is the main-axis distribution (justify-content).

const (
	// JustifyStart packs items at the main-start edge (initial value).
	JustifyStart Justify = iota
	// JustifyEnd packs items at the main-end edge.
	JustifyEnd
	// JustifyCenter centres items on the main axis.
	JustifyCenter
	// JustifySpaceBetween distributes free space between items.
	JustifySpaceBetween
	// JustifySpaceAround distributes free space around items.
	JustifySpaceAround
	// JustifySpaceEvenly distributes free space evenly incl. the ends.
	JustifySpaceEvenly
)

type Length

type Length struct {
	Px        float64
	Percent   float64 // 0..1; only meaningful when IsPercent
	IsPercent bool
	Auto      bool
}

Length is a resolved CSS length. Percentages are kept separately so the layout can resolve them against the containing block's width.

func (Length) Resolve

func (l Length) Resolve(containing float64) float64

Resolve returns the length in pixels against a containing-block width.

type LineHeight

type LineHeight struct {
	Px     float64
	Factor float64 // unitless multiplier of the element's own font-size (0 = none)
	Normal bool
}

LineHeight is a computed line-height. Normal means "use the font's own line height". Otherwise it is EITHER a fixed pixel height (Px, from a length or percentage value) OR a unitless Factor of the element's own font-size.

The distinction is load-bearing for inheritance (CSS 2.1 §10.8.1): a length or percentage line-height computes to a fixed pixel value that descendants inherit unchanged, whereas a unitless number computes to the number itself and inherits AS the number, so each descendant re-multiplies by its OWN font-size. Collapsing a unitless line-height to pixels at the declaring element (as an earlier version did) leaks the ancestor's font-size onto larger/smaller descendants, making their line boxes too short and letting glyphs from adjacent lines overlap.

func (LineHeight) Resolve

func (h LineHeight) Resolve(fontSize float64) (float64, bool)

Resolve returns the used line-height in pixels for an element whose computed font-size is fontSize, and whether an explicit height applies. It returns (0, false) for `normal` (defer to the font's natural metrics) and for a non-positive height. A unitless Factor is multiplied by fontSize; a fixed Px is returned unchanged.

type LinkRef

type LinkRef struct {
	Href  string // the raw href attribute value (unresolved)
	Media string // the raw media attribute value ("" when absent)
}

LinkRef is a reference to an external stylesheet declared by a <link rel="stylesheet" href="..."> element, with its media attribute.

func StylesheetLinks(root *dom.Node) []LinkRef

StylesheetLinks walks the DOM in document order and returns every <link rel="stylesheet"> href, preserving order (which fixes cascade precedence). Links whose rel does not contain the "stylesheet" token, or that carry the "alternate" token, or that have an empty href, are skipped.

type ListStylePosition

type ListStylePosition uint8

ListStylePosition is where the marker sits relative to the item's content. It inherits.

const (
	// ListOutside places the marker in the indent to the left of the content box
	// (the initial value).
	ListOutside ListStylePosition = iota
	// ListInside places the marker inside the content box, before the first line.
	ListInside
)

type ListStyleType

type ListStyleType uint8

ListStyleType is the marker style of a display:list-item box. It inherits.

const (
	// ListDisc is a filled circle (the initial value / <ul> default).
	ListDisc ListStyleType = iota
	// ListCircle is a hollow (stroked) circle.
	ListCircle
	// ListSquare is a filled square.
	ListSquare
	// ListDecimal is an ascending decimal number ("1.", "2.", …) — the <ol> default.
	ListDecimal
	// ListNone paints no marker.
	ListNone
)

type Media added in v0.4.0

type Media struct {
	Type  string  // Screen ("" or "screen") or Print; compared case-insensitively
	Width float64 // viewport width, CSS px
	// Scale is the device pixel ratio: device pixels per CSS px (CSSOM View
	// devicePixelRatio). 0 means 1. It is what the `resolution` media feature
	// reports (Media Queries 4 §5.1, in dppx), and a cascade run against it
	// computes every absolute length in device pixels (see scaleLengths).
	Scale float64
}

Media is what a stylesheet is evaluated against: the media TYPE — screen (the zero value) or print — that "@media print", "@media screen" and a <link media="print"> select on, and the viewport width that min-width / max-width features compare with. A browser's screen render and its print preview of the same page differ by exactly this value: sites hide their navigation, sidebars and footers under @media print, and a print-only stylesheet is skipped on screen. Every Cascade*/ParseStylesheet* entry point that takes only a viewport width evaluates for screen.

type ObjectFit added in v0.4.0

type ObjectFit uint8

ObjectFit selects how a replaced element's (img/video) own content is scaled to its box, when that box's size (from width/height/aspect-ratio) doesn't match the content's own intrinsic aspect ratio — the equivalent of background-size for a foreground element rather than a background layer. Confirmed live (round 89) as `object-fit:cover`, the standard Tailwind `object-cover` utility: react.dev's own avatar/video-thumbnail images and tailwindcss.com's own gallery images. `object-position` is not modelled — no confirmed non-centered real use — so cover/contain always centre.

const (
	// ObjectFitFill stretches the content to exactly fill the box, distorting
	// its aspect ratio if the box's doesn't match — the initial value, and
	// this engine's own prior behaviour for every image before this existed.
	ObjectFitFill ObjectFit = iota
	// ObjectFitCover scales the content, preserving its aspect ratio, to
	// cover the whole box, cropping any overflow — same mechanism as
	// background-size:cover, applied to a foreground element.
	ObjectFitCover
	// ObjectFitContain scales the content, preserving its aspect ratio, to
	// fit entirely inside the box, letterboxing any remainder.
	ObjectFitContain
)

type Overflow added in v0.2.0

type Overflow uint8

Overflow is the computed overflow of one axis. For the engine's headless paint every non-visible value (hidden/clip/scroll/auto) clips descendant painting to the box's padding box — there is no interactive scrolling, so a scroll/auto container renders exactly its visible window, matching the first paint a user would see. This is what keeps the universal `sr-only` / visually-hidden pattern (position:absolute;width:1px;height:1px; overflow:hidden;clip) from painting its screen-reader text at full size.

const (
	// OverflowVisible is the initial value: content is not clipped.
	OverflowVisible Overflow = iota
	OverflowHidden
	OverflowClip
	OverflowScroll
	OverflowAuto
)

func (Overflow) Clips added in v0.2.0

func (o Overflow) Clips() bool

Clips reports whether this overflow value clips descendant painting.

type PageRule added in v0.4.0

type PageRule struct {
	Selector     string
	Declarations []Declaration
}

PageRule is one @page block as written (CSS Paged Media Level 3), with its selector — "" for the unqualified rule, ":first", ":left", ":right", ":blank", or a named page ("cover") — and its declarations in order. A selector list (`@page :left, :right { … }`) yields one PageRule per selector, sharing the declarations. Margin boxes nested inside (`@top-center { content: counter(page) }`) are skipped — they are not modelled yet — and the declarations around them are kept.

func ParsePageRules added in v0.4.0

func ParsePageRules(src string, m Media) []PageRule

ParsePageRules returns the @page rules of a stylesheet, in order, honouring an enclosing @media (a @page inside `@media print` applies under Print only) and unwrapping @layer exactly as ParseStylesheetMedia does for style rules. Style rules and every other at-rule are passed over: the page context is read separately from the element cascade, which never sees a @page block (parseRules skips it as an unknown at-rule).

type PageSpec added in v0.4.0

type PageSpec struct {
	Width, Height float64
	Margin        [4]float64 // top, right, bottom, left, CSS px
	MarginSet     [4]bool
}

PageSpec is a resolved page: the page box size and margins in CSS px. A zero Width/Height means size was not given (auto — the consumer's default page; `size: landscape` alone, an orientation for that default, also leaves them zero). MarginSet says which margins a rule set; an unset margin is the consumer's default too.

func DocumentPage added in v0.4.0

func DocumentPage(root *dom.Node, externalSheets []string, m Media, selector string) PageSpec

DocumentPage gathers the @page rules of every <style> in root (document order) and of externalSheets (external sheets first, as in the cascade: a <link> stylesheet loads in the head, before the document's own <style>), evaluated against m, and resolves selector — "" for the document's default page. externalSheets should come from the same Media-aware fetch the cascade uses (engine.LoadStylesheets), so a <link media="print"> sheet's @page is seen exactly when its style rules are.

func ResolvePage added in v0.4.0

func ResolvePage(rules []PageRule, selector string) PageSpec

ResolvePage applies, in source order, every rule whose selector is "" and then every rule whose selector equals selector, so a ":first" page inherits the unqualified rule's values and overrides what it sets. Understood: size (auto | one or two lengths — one length is a square page | a named size and/or portrait|landscape, in either order) and margin / margin-top|right|bottom|left (absolute lengths, em/rem at 16px; a percentage or auto is ignored). Anything else — margin boxes, page, bleed, marks — is ignored.

type Position

type Position uint8

Position is the subset of the position property the engine understands.

const (
	// PositionStatic is the initial value: the box is in normal flow with no
	// top/right/bottom/left offset applied.
	PositionStatic Position = iota
	// PositionRelative keeps the box in normal flow (it still reserves space) but
	// paints it shifted by its top/left/right/bottom offset.
	PositionRelative
	// PositionAbsolute removes the box from normal flow and positions it against
	// the padding box of the nearest positioned ancestor (else the initial
	// containing block).
	PositionAbsolute
	// PositionFixed removes the box from normal flow and positions it against the
	// initial containing block (the viewport). For a full-page static render it is
	// resolved to document coordinates so it paints once at its place.
	PositionFixed
	// PositionSticky is approximated as relative for a full-page static shot.
	PositionSticky
)

func (Position) OutOfFlow

func (p Position) OutOfFlow() bool

OutOfFlow reports whether a position value takes the box out of normal flow (so it reserves no space in its parent's block/inline formatting context).

func (Position) Positioned

func (p Position) Positioned() bool

Positioned reports whether a position value makes the box a containing block for absolutely-positioned descendants (anything other than static).

type RadialExtent

type RadialExtent uint8

RadialExtent is the sizing keyword of a radial gradient's ending shape.

const (
	// ExtentFarthestCorner is the default extent.
	ExtentFarthestCorner RadialExtent = iota
	// ExtentClosestSide sizes to the nearest edge.
	ExtentClosestSide
	// ExtentClosestCorner sizes to the nearest corner.
	ExtentClosestCorner
	// ExtentFarthestSide sizes to the farthest edge.
	ExtentFarthestSide
	// ExtentExplicit uses explicit RadiusX/RadiusY lengths.
	ExtentExplicit
)

type RadialShape

type RadialShape uint8

RadialShape selects a circular or elliptical radial gradient.

const (
	// RadialEllipse is the default radial shape.
	RadialEllipse RadialShape = iota
	// RadialCircle forces equal radii.
	RadialCircle
)

type Rule

type Rule struct {
	Selectors    []Selector
	Declarations []Declaration

	// Container is non-nil when this rule came from (directly, or via nested
	// @media/@layer) an `@container` at-rule: its selectors only take effect
	// for a matched element when the condition holds against that element's
	// nearest qualifying ancestor container, evaluated at cascade time (see
	// container.go — this is deferred, unlike @media, because it depends on
	// per-element ancestor geometry rather than a single known viewport
	// width).
	Container *ContainerCondition
}

Rule is a parsed style rule: a list of selectors sharing a declaration block.

func ParseStylesheet

func ParseStylesheet(src string) []Rule

ParseStylesheet parses a full stylesheet into rules at the default viewport width. See ParseStylesheetVW to control @media evaluation.

func ParseStylesheetMedia added in v0.4.0

func ParseStylesheetMedia(src string, m Media) []Rule

ParseStylesheetMedia is ParseStylesheetVW evaluated against a Media — the entry point that makes "@media print" blocks apply.

func ParseStylesheetVW

func ParseStylesheetVW(src string, vw float64) []Rule

ParseStylesheetVW parses a full stylesheet into rules, evaluating @media blocks against viewport width vw: a matching @media block's inner rules are included, a non-matching one is skipped. @layer blocks are unwrapped (their rules included as if the layer boundary were not there — this engine does not model cross-layer cascade priority, but that is far less wrong than dropping the content: Tailwind v4's default output, among many other frameworks, puts nearly all of its CSS inside @layer). @container blocks are always included, with their condition attached to each inner rule for the cascade to evaluate per-element once real layout geometry is available (see container.go) — @container style(...) queries are not supported and are skipped wholesale, like any other unrecognised at-rule (@font-face, @keyframes, @supports, @import, ...). Malformed rules are skipped defensively.

type Selector

type Selector struct {
	// contains filtered or unexported fields
}

Selector is a full complex selector: a chain of compound selectors joined by combinators. parts is in source order (left to right); parts[len-1] is the key selector matched against the candidate element, and each preceding part constrains an ancestor or sibling via combs[i] (the combinator between parts[i] and parts[i+1]).

func ParseSelectorList

func ParseSelectorList(s string) []Selector

ParseSelectorList parses a comma-separated selector list, skipping empty and unparseable entries. Commas inside functional pseudo-classes (`:is(a, b)`) are respected (not treated as list separators), and `:is()` / `:where()` / `:matches()` wrappers are expanded into plain selectors — the form modern Tailwind emits for its dark-mode and variant rules (`:is(.dark .dark\:bg-wash-dark)`).

func (Selector) Matches

func (s Selector) Matches(n *dom.Node) bool

Matches reports whether the selector matches element n, evaluating the combinator chain from the key selector leftwards. It is exactly MatchesHost(n, nil) — outside any shadow tree there is no host to bind ":host" to, so a Host compound anywhere in the selector always fails.

func (Selector) MatchesHost added in v0.4.0

func (s Selector) MatchesHost(n, host *dom.Node) bool

MatchesHost is Matches with host bound as the enclosing shadow tree's host element (see cascade.go's shadow-scoped cascade walk), so ":host" and ":host(<selector>)" compounds — the shadow tree's one sanctioned way to style, or reach past, the host from inside its own scoped stylesheet — can match. It is the ONLY way a Host compound ever matches; every other compound is unaffected by host.

func (Selector) Specificity

func (s Selector) Specificity() int

Specificity returns the (a, b, c) specificity packed into a single int, with a = id count, b = class count, c = tag count, summed over all compounds.

type Style

type Style struct {
	Display    Display
	Visibility Visibility // inherited; see the Visibility doc comment
	Color      Color
	Background Color
	FontSize   float64 // px
	FontWeight int     // 400 = normal, 700 = bold
	FontFamily FontFamily
	Italic     bool // font-style: italic|oblique (inherited)
	// Underline is set by `text-decoration`/`text-decoration-line: underline`,
	// cleared by any other explicit line value (including "none"). Real CSS
	// text-decoration-line is NOT an inherited property — instead, a line set
	// on a block/inline CONTAINER visually propagates through its descendant
	// inline boxes unless a descendant sets its own line explicitly. This
	// engine approximates that (real) propagation the same way an actually-
	// inherited property would: copied from parent to child, then
	// overwritten by an explicit declaration on the child itself — correct
	// for the overwhelming majority of real content (an <a> or a heading
	// carries `text-decoration:underline`; its own descendant <strong>/<code>
	// inherits the line via this field exactly as it should), and only wrong
	// for the rare case of a descendant deliberately opting OUT with its own
	// `text-decoration-line:none` while an ancestor's line is meant to still
	// show through — not modelled here. Only the "underline" line is tracked
	// (line-through/overline/blink are not) and text-decoration-color/style/
	// thickness are not modelled: the rendered line is always solid, in the
	// text's own current colour — deliberately narrow, matching the
	// overwhelmingly common real-world "a hyperlink is underlined" pattern
	// this was found missing for entirely: text-decoration had NO Style
	// field, no parser case and no paint code at all before this — every
	// underline on every page this engine has ever rendered was silently
	// dropped, confirmed live on developer.mozilla.org's own in-article
	// links (`:is(.content-section a):not([href^="#"])
	// {text-decoration:underline}`, present in real Chrome, absent here).
	Underline bool

	// Fill/Stroke are the SVG paint properties (inherited, like Color).
	// FillSet/StrokeSet distinguish "CSS resolved a concrete colour" from
	// "left to the SVG document's own presentation attribute" — svg.go's
	// serializeSVG is the only reader: unset changes nothing (the element's
	// own `fill`/`stroke` XML attribute, if any, survives untouched), set
	// overrides it with the CSS value. FillNone/StrokeNone models
	// `fill:none`/`stroke:none` (paint suppressed) separately, since Color{}
	// (transparent black) is also a legitimate real colour value. Confirmed
	// load-bearing live: tailwindcss.com's nav icons (search glyph, version-
	// badge chevron, logo mark) and CTA underline are coloured entirely via
	// Tailwind's `fill-*`/`stroke-*` utility classes, never an XML `fill=`
	// attribute — before this, every such icon rasterised with SVG's initial
	// fill (black), often invisible against a dark background.
	Fill       Color
	FillSet    bool
	FillNone   bool
	Stroke     Color
	StrokeSet  bool
	StrokeNone bool

	Margin    Edges
	Padding   Edges
	Border    Borders
	Width     Length // Auto by default
	MinWidth  Length // Auto (== none) by default
	MaxWidth  Length // Auto (== none) by default
	Height    Length // Auto by default
	MinHeight Length // Auto (== none) by default
	MaxHeight Length // Auto (== none) by default
	// AspectRatio is the preferred width/height ratio from `aspect-ratio:
	// <W>/<H>` (0 means "auto", i.e. unset — no ratio constrains sizing).
	// Confirmed load-bearing live (round 86): Next.js's own auto-generated
	// image-placeholder wrapper divs (tailwindcss.com's blog/changelog cover
	// images) size themselves ENTIRELY via `aspect-ratio` plus a resolved
	// width, with no explicit height at all — reserving the correct space
	// before the image itself loads, to avoid a layout shift. Only the
	// width-known/height-auto direction is resolved (see usedHeight); the
	// reverse (height known, width auto) has no confirmed real caller and is
	// not attempted.
	AspectRatio float64
	// ObjectFit controls how a replaced element's OWN content (the loaded
	// image bitmap) is scaled into its box when the two aspect ratios don't
	// match — see ObjectFit's own doc comment. Fill (the zero value) is this
	// engine's own pre-existing behaviour (stretch to exactly fill the box).
	ObjectFit     ObjectFit
	BoxSizing     BoxSizing
	TextAlign     TextAlign
	WhiteSpace    WhiteSpace
	TextTransform TextTransform
	LineHeight    LineHeight

	// LetterSpacing is `letter-spacing`'s resolved value in px (0 = `normal`,
	// the initial value), added after EVERY character of a text run
	// (including its last, matching real browsers — this is why a run's
	// measured width grows even for a single-character string). Inherited,
	// per spec. Only the line-wrapping width (layout.appendWords, the
	// confirmed real path — tailwindcss.com's own large `tracking-tighter`
	// hero heading and `tracking-widest` sidebar labels) and painting
	// (paint.drawText) apply it; the shrink-to-fit/min-content intrinsic-
	// sizing paths (preferredWidth, minContentWidth, flex/table sizing) do
	// not add it to their own measurements — a documented, narrower scope
	// than every possible consumer of Measure, matching this engine's
	// established practice of shipping the confirmed-reachable path first.
	LetterSpacing float64

	// BorderSpacingH/V are `border-spacing`'s horizontal/vertical values in
	// px (both 0 = the initial value, no gap). Inherited, per spec (one of a
	// handful of table-specific properties CSS inherits despite table
	// layout itself not otherwise being inherited). Confirmed real on
	// en.wikipedia.org's own infobox (`.infobox{border-spacing:3px}`, cells
	// styled with only a `border-bottom` — the gap this property adds
	// between rows IS the infobox's visible row-to-row breathing room, not
	// merely a cosmetic nicety). layout.table applies it unconditionally,
	// without checking `border-collapse` (not modelled at all yet, so every
	// table is implicitly always "separate", the default) — spec says
	// border-spacing has no effect under `border-collapse:collapse`, but
	// both of this engine's own confirmed real cases are unaffected by that
	// simplification: Wikipedia's infobox never sets border-collapse at all
	// (stays at the default 'separate' this engine already assumes), and
	// pkg.go.dev's own `table{border-collapse:collapse;border-spacing:0}`
	// reset sets spacing to zero anyway, so applying it unconditionally
	// there is a no-op regardless.
	BorderSpacingH, BorderSpacingV float64

	// CenterAsBlock is TextAlign == AlignCenterBlocks for every element,
	// EXCEPT it stays true for a <table> whose PARENT has TextAlign ==
	// AlignCenterBlocks even in quirks mode, where the table's OWN TextAlign
	// is separately reset to AlignLeft (see css/ua.go's quirks-mode table
	// rule) so its cells' text is correctly left-, not centre-, aligned.
	// Layout's "centre a definite-width block within its container" rule
	// (the legacy <center>/align=center effect) reads THIS field rather than
	// TextAlign directly, because real browsers keep the two questions
	// (should MY OWN text be centred vs. should I, as a block, be centred
	// within my parent) independent for exactly this one quirks-mode case —
	// confirmed against the HTML standard, whose quirks-mode reset is scoped
	// to the `table` selector alone. Every other element (including a
	// <center> with its own definite width, which must centre ITSELF using
	// its own AlignCenterBlocks default regardless of its parent) is
	// unaffected: CenterAsBlock and TextAlign==AlignCenterBlocks always
	// agree for it.
	CenterAsBlock bool

	// ImageRendering selects the scaling filter for raster images (inherited).
	// The initial value IRAuto means high-quality (bicubic); IRPixelated asks
	// for nearest-neighbour (pixel art).
	ImageRendering ImageRendering

	// ListItem marks a display:list-item box (it generates a marker). It is not
	// inherited. ListStyleType and ListStylePosition are inherited and select the
	// marker glyph and its placement.
	ListItem          bool
	ListStyleType     ListStyleType
	ListStylePosition ListStylePosition

	// BorderRadius is the corner radius applied to the border box when painting
	// the background and border. It is a single (uniform) radius: the common real
	// case (Tailwind `rounded-*`, pills, circles) sets all four corners equal.
	// Differing per-corner radii are approximated by the last-applied value
	// (documented in FIDELITY.md). A px length is used as-is; a percentage
	// resolves against the box's smaller side at paint time. Zero (the initial
	// value) means square corners.
	BorderRadius Length

	// Auto-margin flags: a margin explicitly set to `auto` centres or pushes the
	// box; distinct from a 0 margin.
	MarginLeftAuto  bool
	MarginRightAuto bool
	// MarginLeft/RightIsPercent + MarginLeft/RightPercent (0..1) record a
	// percentage margin-left/margin-right — resolved against the containing
	// block's WIDTH at layout time in resolveWidths, the one place that
	// already has cw available for exactly this purpose (mirroring how Width/
	// Height keep percentages unresolved in a Length rather than eagerly
	// computing a wrong value against an unknown containing block). Margin is
	// a plain Edges (float64), not Length, everywhere else, so this is a
	// narrower, side-channel addition rather than a wholesale type change.
	// Scoped to margin-LEFT/RIGHT only — the confirmed real need (Wikipedia's
	// own `.ambox{margin:0 10%}`, `@media(min-width:720px)`-gated, centring
	// article cleanup-notice boxes within their column) only ever needs
	// horizontal percentages; margin-top/margin-bottom percentages (also
	// resolved against the containing block's WIDTH per spec, a common CSS
	// quirk) have no confirmed real caller and are not modelled — a
	// documented scope limit, not a silent gap for the case that matters.
	MarginLeftIsPercent  bool
	MarginLeftPercent    float64
	MarginRightIsPercent bool
	MarginRightPercent   float64

	// Overflow per axis (not inherited; initial value visible). Any non-visible
	// value clips descendant painting to this box's padding box.
	OverflowX Overflow
	OverflowY Overflow

	// HasClip/ClipRect model the legacy `clip: rect(top, right, bottom, left)`
	// property — deprecated in favour of clip-path, but still a live, real-
	// world pattern: confirmed load-bearing on pkg.go.dev's own "skip to main
	// content" link (`clip:rect(0 0 0 0)`, the CSS2-era screen-reader-only
	// idiom, predating the now-common `width:1px;height:1px;overflow:hidden`
	// version this engine already honours via Overflow above — that one has
	// no effect here since this element sets no explicit small width/height,
	// only `clip`). Per spec this only applies when Position is absolute or
	// fixed, and all four edges are distances from the box's own top-left
	// BORDER edge (not the CSS shorthand's usual top/right/bottom/left box
	// edges) — `rect(0,0,0,0)` is therefore a zero-size rectangle at the
	// box's own corner, clipping it and its content to nothing while it
	// still occupies its normal layout position (unlike display:none). Only
	// the common case (all four values explicit lengths) is modelled; a
	// `clip: auto` per edge, or any other value this engine's length parsing
	// does not resolve, leaves HasClip false — clip:rect() with a genuinely
	// variable edge is rare in practice (the sr-only idiom always hard-codes
	// all four to 0), so this is not a real-world loss.
	HasClip  bool
	ClipRect Edges // Top, Right, Bottom, Left offsets from the box's own corner

	// Positioning that affects normal flow.
	Float Float
	Clear Clear

	// CSS position and the box-offset properties. Top/Right/Bottom/Left are Auto
	// by default (the initial value of each offset). ZIndex is meaningful only
	// when ZIndexAuto is false; the initial value is auto (paint in tree order).
	Position   Position
	Top        Length // Auto by default
	Right      Length // Auto by default
	Bottom     Length // Auto by default
	Left       Length // Auto by default
	ZIndex     int
	ZIndexAuto bool // true == "auto" (the initial value)

	// TranslateX/TranslateY and RotateDeg are the TWO subsets of the
	// `transform` property this engine understands, EACH ONLY on its own —
	// a value naming any other function (scale, skew, matrix, 3D), or
	// mixing more than one of these two together (`translate(...)
	// rotate(...)` on the same element), is unsupported and leaves every
	// transform field at zero, same as before either was understood at all
	// — see FIDELITY.md's Known gaps. Translate (see parseTransformTranslate
	// in parse.go) is a paint-time offset applied like a relative position
	// shift, not a layout input; the initial/reset value (Length's Go zero
	// value) is exactly "no translation", so unlike most other reset-not-
	// inherited fields there is nothing to list explicitly in inheritFrom.
	//
	// RotateDeg (`rotate()`/`rotatez()`, see parseTransformRotate) is the
	// angle in degrees to rotate the element's own border-box rectangle by,
	// clockwise for a positive value — CSS's own convention. go-images'
	// own Rotate is the OPPOSITE sign convention (its own doc comment:
	// "rotated by angle degrees counter-clockwise", matching scikit-image),
	// so paint.paintRotated negates the angle it passes in; get this backward
	// and a page with any asymmetric rotated content (readable text/icons,
	// not just tailwindcss.com's own near-symmetric card shapes) would spin
	// the visibly wrong way, caught by checking the sign convention against
	// the dependency's own doc comment before writing any code, not
	// discovered after the fact. Unlike translate, this DOES need real
	// coordinate-transform machinery: paint.paintRotated renders the box's
	// own border-box rectangle to an offscreen buffer and rotates the
	// PIXELS with go-images' Rotate (bilinear, growing the buffer to fit the
	// rotated corners), rather than transforming any drawing coordinates —
	// so it does not compose with `filter`/opacity/`mask-image` on the same
	// element (paintBox only takes this path when none of those apply): no
	// confirmed real trigger combines rotate with any of them, and doing so
	// correctly needs the two offscreen-buffer mechanisms unified, a larger
	// change than this one evidenced case justifies. transform-origin is
	// not modelled either — always the default 50% 50% (the box's own
	// centre), which is what every confirmed trigger already uses.
	//
	// This field backs BOTH `transform: rotate()`/`rotateZ()` (parsed by
	// parseTransformRotate) and the newer, independent CSS Transforms Level
	// 2 `rotate` property (parsed directly in `case "rotate"`, a bare
	// angle with no function-call syntax at all — `rotate:90deg`,
	// `rotate:none`) — two different spellings of the identical visual
	// capability. Confirmed live (round 145): modern Tailwind (v4) compiles
	// its `rotate-*` utilities to the STANDALONE property, not the
	// `transform` function — `.rotate-(--angle){rotate:var(--angle)}` on
	// tailwindcss.com's own "P3 colors" section, a grid of diagonal colour-
	// name labels ("red", "orange", …) tilted -45°, confirmed directly from
	// its compiled CSS bundle rather than assumed from the class name alone
	// (an assumption that would have targeted the wrong property entirely
	// and shipped a fix that silently did nothing on the one confirmed real
	// page). `transform: rotate()` itself has no independently confirmed
	// trigger of its own in this session's corpus — kept anyway since it is
	// the same underlying, now-evidenced capability under CSS's older
	// syntax, not a distinct speculative addition.
	//
	// Both spellings write the SAME field and so cannot compose with each
	// other, or with `transform: translate()`/the (unimplemented) standalone
	// `translate`/`scale` properties, correctly per spec — whichever this
	// engine's cascade applies LAST simply overwrites the field. No
	// confirmed trigger combines any of these, so this is disclosed rather
	// than engineered around.
	TranslateX Length
	TranslateY Length
	RotateDeg  float64

	// AppearanceNone is `appearance: none` (or `-webkit-appearance: none`),
	// which suppresses a form control's native OS chrome so the author's own
	// background/background-image/border/border-radius paint instead. Not
	// inherited; the zero value (false) is the initial `auto`, matching every
	// other form control this engine already renders with its own hardcoded
	// UA-default look (see paintFormControl/paintCheckboxLike). Confirmed
	// load-bearing live: developer.mozilla.org's own `<mdn-switch>` custom
	// toggle-switch component styles a real `<input type=checkbox>` with
	// exactly `appearance:none;background-color:...;background-image:radial-
	// gradient(...);border-radius:9999px` UNCONDITIONALLY (not gated behind
	// `:checked` — only the knob's position/colour REFINES on `:checked`), so
	// a checkbox that sets this must stop taking this engine's own generic
	// checkbox square and instead paint like any other styled box. Only the
	// bare `none`/`auto` keywords are modelled; the CSS Basic UI vendor-
	// specific appearance keywords (`textfield`, `menulist-button`, etc.) have
	// no confirmed trigger and are left unsupported (equivalent to `auto`).
	AppearanceNone bool

	// Flex container properties (meaningful when Display == DisplayFlex).
	FlexDirection  FlexDirection
	FlexWrap       FlexWrap
	JustifyContent Justify
	AlignItems     AlignItems
	AlignContent   AlignContent

	// Gaps between flex lines/items and between grid tracks. RowGap is the
	// cross-line / block-axis gap; ColumnGap is the main-axis / inline-axis gap.
	RowGap    Length
	ColumnGap Length

	// Flex/grid item properties (meaningful for a child of a flex/grid container).
	FlexGrow   float64
	FlexShrink float64
	FlexBasis  Length // Auto == "auto" (use the item's width/content)
	Order      int    // reorders items within a line (ascending)
	AlignSelf  AlignSelf

	// Grid container properties (meaningful when Display == DisplayGrid).
	GridTemplateColumns []TrackSize
	GridTemplateRows    []TrackSize
	GridAutoRows        TrackSize
	GridAutoColumns     TrackSize
	GridAutoFlow        GridFlow
	// GridAutoFlowDense is the "dense" keyword on grid-auto-flow: the
	// auto-placement cursor restarts from the very first grid cell before
	// placing EACH item (potentially reordering items visually, out of DOM
	// order, to backfill an earlier gap a bigger item left open) instead of
	// only ever advancing forward from wherever the previous item landed
	// (the default, "sparse", packing). See layout.placeItems.
	GridAutoFlowDense bool
	GridTemplateAreas [][]string // row-major grid of area names ("" == empty)
	JustifyItems      AlignItems // inline-axis alignment of items in their cell

	// Grid item placement (meaningful for a child of a grid container).
	GridColumnStart GridLine
	GridColumnEnd   GridLine
	GridRowStart    GridLine
	GridRowEnd      GridLine
	GridArea        string // named area this item is placed into (via grid-area)
	JustifySelf     AlignSelf

	// CustomProps holds the element's resolved CSS custom properties (--name ->
	// raw value). It is inherited from the parent and overridden by matched
	// rules; var() references consult it at computed-value time. Nil until an
	// element (or an ancestor) defines a custom property.
	CustomProps map[string]string

	// ColorScheme is the inherited `color-scheme` (CSS Color Adjust 1 §2.2);
	// its UsedDark picks light-dark()'s branch. Zero value: `normal`.
	ColorScheme ColorScheme

	// Background image layers (gradients and url() bitmaps) and their paint
	// parameters. Each list is indexed per layer (first-listed paints on top);
	// a shorter size/position/repeat list repeats its last value. All nil == no
	// background image (only the solid Background colour paints).
	BackgroundImages   []BgImage
	BackgroundSize     []BgSize
	BackgroundPosition []BgPosition
	BackgroundRepeat   []BgRepeat

	// MaskImage is the resolved absolute URL of a `mask-image`/
	// `-webkit-mask-image: url(...)` — empty means no mask (the common case).
	// Unlike background-image, a mask does not paint its own pixels: it is an
	// alpha stencil applied over everything the ELEMENT ITSELF paints (colour,
	// background, borders, text, children), stretched to fill the element's
	// border box (this engine's one deliberate simplification — the real
	// mask-size/mask-position/mask-repeat grammar is not modelled, matching
	// how far this engine narrowly scoped `transform: translate()`). Confirmed
	// load-bearing live: modern MediaWiki (Wikipedia's Vector-2022 skin, and
	// most of its UI icon systems generally) renders EVERY toolbar icon —
	// hamburger menu, search, language switcher — as an empty, solid-coloured
	// <span> cut into shape by exactly this mechanism, so the icon recolours
	// for dark mode without needing a second image asset. A gradient or other
	// non-url() mask value is not modelled and leaves this empty.
	MaskImage string

	// BoxShadows are the element's box-shadow layers (first-listed paints on top).
	BoxShadows []BoxShadow

	// Opacity is the element's group opacity in [0,1]; HasOpacity distinguishes a
	// genuine opacity from the zero value (an unset Style is fully opaque).
	Opacity    float64
	HasOpacity bool

	// Filters is the element's `filter` function chain, applied in order to the
	// element's rendered output (its box plus subtree) as a group. Nil == no
	// filter. Not inherited (`filter` is a non-inherited property).
	Filters []Filter
	// BackdropFilters is `backdrop-filter`'s function chain (same grammar and
	// Filter type as Filters — see parseFilterList) applied to whatever has
	// already been painted BEHIND this box's own border-box, before the box's
	// own background/border/content paint on top — the standard "frosted
	// glass" idiom (a translucent bar over blurred page content). Confirmed
	// live on react.dev's own sticky nav bar and code-sandbox title bars
	// (`backdrop-filter:blur(16px) saturate(2)` via Tailwind's `backdrop-blur-
	// lg backdrop-saturate-200`, composed through CSS custom properties). Not
	// inherited, matching Filters.
	BackdropFilters []Filter

	// ContainerType and ContainerName are `container-type`/`container-name`
	// (or the `container` shorthand): whether this element establishes a
	// query container for descendant `@container` rules, and under what
	// name. Neither is inherited; the initial value is ContainerNormal / "".
	ContainerType ContainerType
	ContainerName string

	// Fragmentation (CSS Fragmentation Level 3), as far as paged media is
	// concerned — this engine's own screen layout ignores all five; a
	// paginating consumer (the paginate package, go-pdfkit/html2pdf) reads
	// them. break-before / break-after / break-inside are NOT inherited
	// (initial auto); the legacy page-break-before/after/inside properties
	// are aliases (always → page). orphans / widows ARE inherited (initial
	// 2; only a positive integer is a valid value, anything else leaves the
	// declaration ignored).
	BreakBefore, BreakAfter Break
	BreakInside             BreakInside
	Orphans, Widows         int

	// Multi-column layout (CSS Multi-column Layout Level 1): ColumnCount (0 =
	// not set) and ColumnWidth (Auto = not set) are NOT inherited — a plain
	// block box unless one or the other is set, exactly like `columns`'s own
	// two longhands. The column gap itself is the SAME `column-gap`/`gap`
	// property flex/grid already use (ColumnGap, declared with RowGap
	// above) — real CSS defines it once, in the Box Alignment module, for
	// all three layout modes, not a per-mode property. Its shared "unset"
	// default is Length{Auto:true} (see initialStyle/inheritFrom), which
	// flex/grid's own gapLen/gapPxCSS already treat identically to an
	// explicit 0 — safe to give the field this default without touching
	// their behaviour at all. Multi-column layout alone resolves Auto
	// differently: to 1em (see layoutMultiCol), matching every browser's
	// implementation of a value the spec itself only describes as "a
	// reasonable value comparable to the width of a typical typeface's
	// space character" — unlike flex/grid, where `normal` is defined to
	// compute to a plain 0.
	//
	// Confirmed live (round 143/144): pkg.go.dev's own `.UnitFiles-fileList{
	// columns:12.5rem 5;...}` — its package-documentation "Source Files"
	// list, real Chrome laying ~29 short filenames out in up to 5 narrow
	// columns instead of one long single column.
	//
	// What layout.layoutMultiCol actually models, deliberately narrower than
	// the full spec, is exactly what that one confirmed trigger needs: N
	// columns of even width, each in-flow ELEMENT child of the container
	// (never a bare text node — no confirmed trigger mixes one into a
	// columns container) placed as a single ATOMIC unit into whichever
	// column a simple cumulative-height greedy pass assigns it to, never
	// split across two columns even when a single child is themselves taller
	// than a whole column (the same "atomic child, no fragmentation"
	// simplification this engine already applies to a replaced element or a
	// table row elsewhere). NOT modelled: column-span (spec keyword `all`,
	// letting one child cross every column — no confirmed trigger),
	// column-rule (the vertical divider line between columns — pure
	// decoration, not modelled since the plain column gap alone is already
	// the confirmed visual difference from a single column), TRUE column
	// balancing (the real spec's iterative algorithm that minimises the
	// tallest column's own height while respecting break-inside/break-before
	// avoidance — this engine's single greedy pass, target = total÷count,
	// approximates it closely for a roughly-uniform list like the confirmed
	// trigger, but can leave a visibly taller last column for adversarial,
	// very unevenly-sized content), and fragmenting a SINGLE child's own
	// internal content (its own child elements, or its own wrapped text
	// lines) across two columns — a browser's real balancing can split one
	// long paragraph's lines between columns; this engine cannot, matching
	// its own "atomic child" scope decision above.
	ColumnCount int
	ColumnWidth Length

	// TabSize (CSS Text Module Level 3) is INHERITED, initial 8 — a preserved
	// tab (white-space:pre/pre-wrap) advances to the next multiple of this
	// many space-widths, counted as columns from the line's own start (see
	// layout's own expandTabs). Confirmed live (round 91): pkg.go.dev's own
	// `pre,textarea.code{tab-size:4}` on its real, tab-indented Go source
	// samples — this engine's tab expansion already existed but read a
	// hardcoded 8 regardless of this property, over-indenting by 2x. Only a
	// bare, non-negative `<number>` is parsed; the sibling `<length>` form is
	// spec-flagged "at risk" (may be dropped) and has no confirmed real use
	// here, so an unparseable/negative value leaves the inherited value in
	// place rather than resetting to some sentinel (0 is itself a valid,
	// meaningful value: "don't render tabs").
	TabSize int

	// TextWrapBalance (CSS Text Module Level 4, `text-wrap`/`text-wrap-style`)
	// is INHERITED, initial false ("auto", ordinary greedy wrapping) — `true`
	// requests the `balance` value: redistribute a SHORT block's own line
	// breaks so each line's leftover space is more even (a real browser's own
	// algorithm is UA-defined; layout's own balanceLines approximates it by
	// finding the narrowest width that still wraps to the SAME line count a
	// plain greedy wrap already produces, then re-breaking at that width —
	// the spec's own explicit invariant: balancing must never change the line
	// count for 5 lines or fewer, and a UA "may" give up and fall back to
	// `auto` past 10). Confirmed live (round 93): tailwindcss.com's own hero
	// `<h1 class="... text-balance ...">` heading. Only `balance` itself is
	// modelled — `pretty`/`stable`/`avoid-short-last-line` have no confirmed
	// real use in this engine's own corpus and fall back to ordinary
	// wrapping, same as the property's own initial `auto`.
	TextWrapBalance bool

	// TextWrapNowrap (CSS Text 4 `text-wrap`/`text-wrap-mode`) is INHERITED,
	// initial false ("wrap") — `true` requests `nowrap`: per spec, "lines
	// only break at forced line breaks; content that does not fit overflows"
	// — exactly this engine's EXISTING white-space:nowrap wrapping behaviour
	// (see layout.layoutInline's own `nowrap` parameter), but WITHOUT
	// white-space's OTHER effect of collapsing runs of whitespace: the spec
	// is explicit that text-wrap-mode affects line-breaking opportunities
	// only, never whitespace collapsing/preservation. Confirmed live (round
	// 93, flagged then as a follow-up rather than folded into that round's
	// own `balance` fix): tailwindcss.com's own `.text-nowrap{text-wrap:
	// nowrap}` on its code-toolbar filename labels.
	TextWrapNowrap bool

	// WordBreakAll is `word-break: break-all`, INHERITED, initial false. Per
	// spec it makes any character boundary a break opportunity for a line
	// that would otherwise overflow — this engine models only the ONE
	// visible effect that has a confirmed real-world trigger: a single
	// unbreakable "word" (one InlineItem — see appendWords) WIDER than the
	// whole line gets split at a rune boundary instead of overflowing its
	// container (layoutInline's splitOverlongWord). The stronger, more
	// aggressive part of the real spec — breaking mid-word EAGERLY even when
	// the word would otherwise have fit on its own line, purely for more
	// even line lengths — is not modelled: no corpus page's own confirmed
	// trigger (see OverflowWrapAnywhere below) needs it, and it would also
	// change ordinary greedy-wrap line counts for text that already fits,
	// a much larger blast radius for an unevidenced refinement. `keep-all`
	// (suppress even CJK's own default inter-character breaks) is not
	// modelled either — this engine does not implement CJK line-breaking in
	// the first place, so there is nothing for it to suppress.
	//
	// See the Style method BreaksOverlongWords, which OR-combines this with
	// OverflowWrapAnywhere: the two properties are independent (either one
	// requests the same one effect this engine models), so they are kept as
	// separate fields rather than one shared boolean — collapsing them at
	// parse time would let an unrelated `word-break: normal` on a LATER
	// declaration silently cancel an EARLIER `overflow-wrap: break-word`'s
	// own effect, which real CSS's cascade would not do (they are different
	// properties; each keeps its own computed value independently).
	WordBreakAll bool

	// OverflowWrapAnywhere is `overflow-wrap`/`word-wrap: break-word` (the
	// unprefixed and legacy-alias property names for the same property) or
	// `anywhere`, and also the deprecated `word-break: break-word` value
	// (per spec, an alias for `word-break: normal; overflow-wrap: anywhere`,
	// so it sets THIS field, not WordBreakAll). INHERITED, initial false.
	// The spec distinguishes `break-word` (breaks only as an overflow-
	// avoidance last resort, like this engine's one modelled effect) from
	// `anywhere` (also affects min-/max-content intrinsic-size calculations,
	// letting a container shrink narrower than its longest unbreakable
	// word) — that intrinsic-sizing distinction is not modelled, since
	// every confirmed real trigger (see BreaksOverlongWords) only needs the
	// line-breaking effect the two values already share.
	//
	// Confirmed live: developer.mozilla.org sets `overflow-wrap:break-word`
	// on `html` itself (inherited page-wide); pkg.go.dev's own package-doc
	// pages set `word-break:break-all` on `.UnitFiles-fileList` (a `<ul>` of
	// long bare filenames with no natural break points) and `word-break:
	// break-word` on `.UnitDirectories td` (a subdirectory-listing table
	// cell); news.ycombinator.com sets `word-break:break-word` on `.title a`
	// (every story headline link, which occasionally IS a long bare URL).
	// Before this, a real production page like any of these would overflow
	// its own container with unbroken text exactly where a real browser
	// wraps mid-word instead.
	OverflowWrapAnywhere bool

	// LineClamp is `-webkit-line-clamp`/`line-clamp`'s value: the maximum
	// number of lines an element's inline content renders before being cut
	// off, 0 meaning "not set" (unclamped — the CSS `none` keyword resolves
	// to this too). Not inherited, matching `overflow` and `display`, the two
	// properties `-webkit-line-clamp` is always paired with in real CSS (the
	// "-webkit-box" multi-line-truncation idiom: `display:-webkit-box;
	// -webkit-box-orient:vertical;-webkit-line-clamp:N;overflow:hidden`) —
	// this engine models the actual clamping this idiom exists for without
	// requiring the paired display/overflow declarations to also be present,
	// since the confirmed real-world callers (see layout's own consumer)
	// always clamp regardless of exactly which vendor ceremony wraps it.
	// Truncating to N lines by DROPPING the rest is modelled; the trailing
	// ellipsis glyph real browsers additionally insert on the last kept line
	// is not — a documented, narrower scope than the full feature, matching
	// the confirmed need (tailwindcss.com's own `line-clamp-2` on a `<p>`,
	// which primarily needs the container to stop growing past two lines,
	// not the exact glyph at the cut point).
	LineClamp int

	// VerticalAlign is `vertical-align`'s keyword value; VAlignBaseline (the
	// zero value) is the default. Only VAlignTextBottom currently shifts an
	// inline-level replaced element's position in its line — the sole
	// confirmed real need (github.com's own octicon SVG icons, styled
	// `vertical-align:text-bottom` throughout its Primer design system to
	// align an icon's bottom edge with the surrounding text's font, not its
	// baseline). The other keyword values are recognised here (so they don't
	// silently fall through to some unrelated case) but not yet given
	// positional effect — a narrower scope than the full property, matching
	// this engine's established practice of shipping the confirmed-reachable
	// value first. Not inherited, per spec.
	VerticalAlign VerticalAlign

	// TextOverflowEllipsis is `text-overflow:ellipsis` (vs. the default
	// `clip`, false). It only has a visible effect combined with
	// `white-space:nowrap` and a clipping `overflow-x` — the real, confirmed
	// idiom (Tailwind's own `.truncate` utility: `overflow:hidden;
	// text-overflow:ellipsis;white-space:nowrap`, found live on
	// tailwindcss.com's own team-member role labels). `overflow:hidden`
	// alone already clips the raw overflowing text with no code here at
	// all; this field's only job is swapping that raw clip for a "…"-
	// terminated one. Scoped to a line made ENTIRELY of plain text items (no
	// nested inline element, image or forced break) — the confirmed real
	// shape — a line mixing in any of those is left as plain overflow, a
	// documented narrower scope than the full property (which also allows a
	// custom replacement string, not modelled). Not inherited, per spec.
	TextOverflowEllipsis bool
}

Style is the fully-computed style of an element, after cascade + inheritance.

func (*Style) Bold

func (s *Style) Bold() bool

Bold reports whether the weight renders as bold.

func (*Style) BreaksOverlongWords added in v0.4.0

func (s *Style) BreaksOverlongWords() bool

BreaksOverlongWords reports whether a single unbreakable "word" wider than its own line may be split at a rune boundary instead of overflowing — see WordBreakAll and OverflowWrapAnywhere's own doc comments for exactly which property values request this and why they are two independent fields rather than one.

type StyleMap

type StyleMap map[*dom.Node]*Style

StyleMap maps each element node to its fully-computed style.

func Cascade

func Cascade(root *dom.Node) StyleMap

Cascade computes styles at the default viewport width. See CascadeVW.

func CascadeMedia added in v0.4.0

func CascadeMedia(root *dom.Node, m Media, externalSheets []string) StyleMap

CascadeMedia is CascadeVW evaluated against a Media rather than a bare screen width: with Media{Type: Print, Width: w} the page's "@media print" rules apply and its "@media screen" rules do not — a print preview's cascade. externalSheets should then come from a fetch that selected <link media> the same way (engine.LoadStylesheets with the same Media).

func CascadeMediaContainers added in v0.4.0

func CascadeMediaContainers(root *dom.Node, m Media, externalSheets []string, containers map[*dom.Node]ContainerSize) StyleMap

CascadeMediaContainers is CascadeVWContainers against a Media — see CascadeMedia.

func CascadeVW

func CascadeVW(root *dom.Node, vw float64, externalSheets []string) StyleMap

CascadeVW computes a style for every element in the tree rooted at root, applying the user-agent stylesheet, author rules from every <style> element plus any externalSheets (already-fetched CSS text, e.g. <link> stylesheets), and inline style="" attributes, with proper specificity and inheritance. @media width queries are evaluated against viewport width vw. Every @container rule is treated as not matching (see CascadeVWContainers) since no container sizes are known yet — the caller re-cascades with CascadeVWContainers once a layout pass has measured them.

func CascadeVWContainers added in v0.4.0

func CascadeVWContainers(root *dom.Node, vw float64, externalSheets []string, containers map[*dom.Node]ContainerSize) StyleMap

CascadeVWContainers is CascadeVW extended with container-query support: containers maps a query-container element (one whose computed style sets container-type) to its size as measured by the most recent layout pass. A nil/empty containers (as CascadeVW passes) makes every @container rule evaluate to "not matching" — the correct, conservative behaviour before any layout has run, and the reason this is a separate entry point rather than a parameter CascadeVW always threads through: most callers (tests, the first cascade of a render) have no layout yet and want exactly today's CascadeVW behaviour with zero conceptual overhead.

Resolving an @container condition against real geometry is inherently a multi-pass affair — this single call only ever consults the containers map it is given; iterating cascade→layout→re-measure to a fixpoint is the caller's job (see engine's layoutWithContainers, which plays the same role for container queries that dynamic.go's settle loop plays for JavaScript: same shape, bounded passes, deterministic termination).

type TextAlign

type TextAlign uint8

TextAlign is the horizontal alignment of inline content in a block.

const (
	// AlignLeft is the initial value.
	AlignLeft TextAlign = iota
	// AlignCenter centres each line.
	AlignCenter
	// AlignRight right-aligns each line.
	AlignRight
	// AlignCenterBlocks centres inline content like AlignCenter and additionally
	// centres a definite-width block/table child within its container — the legacy
	// behaviour of the <center> element and the align="center" attribute (browsers'
	// -moz-center / -webkit-center). A plain text-align:center author rule never
	// produces it, so standards-mode blocks are unaffected.
	AlignCenterBlocks
)

type TextTransform added in v0.4.0

type TextTransform uint8

TextTransform is `text-transform`: the capitalisation a run of text is RENDERED with, leaving the document's own characters untouched. It is applied where layout turns a text node into items, so measurement and painting see the same string — a label written "Partners" and styled uppercase must be measured as "PARTNERS", which is wider.

full-width and full-size-kana are not implemented; they parse as TTNone.

const (
	// TTNone renders the text as the document wrote it.
	TTNone TextTransform = iota
	// TTUppercase renders every character in upper case.
	TTUppercase
	// TTLowercase renders every character in lower case.
	TTLowercase
	// TTCapitalize upper-cases the first letter of each word.
	TTCapitalize
)

type TrackKind

type TrackKind uint8

TrackKind is the sizing function of a single grid track.

const (
	// TrackAuto sizes the track to its content (initial for implicit tracks).
	TrackAuto TrackKind = iota
	// TrackPx is a fixed pixel size.
	TrackPx
	// TrackPercent is a percentage of the container's content size on that axis.
	TrackPercent
	// TrackFr is a flexible fraction of the leftover space.
	TrackFr
	// TrackMinMax is a minmax(min,max) range; Min and Max are non-nil.
	TrackMinMax
)

type TrackSize

type TrackSize struct {
	Kind     TrackKind
	Px       float64    // TrackPx
	Percent  float64    // TrackPercent (0..1)
	Fr       float64    // TrackFr
	Min, Max *TrackSize // TrackMinMax bounds
}

TrackSize is one column or row sizing function of a grid template.

type VerticalAlign added in v0.4.0

type VerticalAlign uint8

VerticalAlign is the `vertical-align` property's keyword value.

const (
	VAlignBaseline VerticalAlign = iota
	VAlignTop
	VAlignBottom
	VAlignTextTop
	VAlignTextBottom
	VAlignMiddle
	VAlignSub
	VAlignSuper
)

type Visibility added in v0.4.0

type Visibility uint8

Visibility is the visibility property: unlike Display, it is INHERITED, and hiding an ancestor still reserves its layout space and does not stop a descendant that resets `visibility:visible` from painting. `collapse` (its real effect is table-row-specific: the row's space is reclaimed) is treated identically to VisibilityHidden — a documented simplification, not a bug.

const (
	// VisibilityVisible is the initial value.
	VisibilityVisible Visibility = iota
	// VisibilityHidden paints nothing for this box (background, border,
	// shadows, inline content) while still occupying its layout space; a
	// descendant box may re-show itself with its own `visibility:visible`.
	VisibilityHidden
	// VisibilityCollapse is treated the same as VisibilityHidden (see above).
	VisibilityCollapse
)

type WhiteSpace

type WhiteSpace uint8

WhiteSpace controls collapsing of whitespace and wrapping.

const (
	// WSNormal collapses runs of whitespace and wraps at the block width.
	WSNormal WhiteSpace = iota
	// WSPre preserves spaces and newlines and does not wrap (as in <pre>).
	WSPre
	// WSNoWrap collapses whitespace like WSNormal but never wraps: the
	// block's content stays on one line, however wide.
	WSNoWrap
)

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL