CSS support

CSS support

htmlbag uses csshtml to match stylesheet rules against the DOM. CSS applies through three sources, in order of increasing specificity:

  1. <style> blocks in <head>.
  2. <link rel="stylesheet" href="…"> external files.
  3. Inline style="…" attributes.

Recognised properties

Box model

width, height, margin, margin-{top,right,bottom,left}, padding, padding-{top,right,bottom,left}, border, border-{top,right,bottom,left}, border-width, border-style, border-color, border-radius, box-sizing.

Typography

font-family, font-size, font-weight, font-style, text-align, text-indent, text-decoration, line-height, color, letter-spacing, word-spacing, text-transform, white-space.

Backgrounds

background-color, background-image, background.

Page-level layout

@page rules support size (named like A4 or explicit <width> <height>) and margin / margin-{top,right,bottom,left}.

Insert positioning

float: top | before | bottom | after — see inserts. The standard CSS float: left | right (side floats with text wrap) is not supported.

Display

display: none hides an element entirely.

display: block and display: inline override the element’s default formatting context. A <span class="title"> with display: block becomes a block-level container that starts on its own line; a <div class="badge"> with display: inline flows inline with its surrounding text. The override is read from the resolved style, so selector-driven and class-driven rules work the same as inline styles.

Lists and markers

list-style-type (disc, decimal, lower-roman, upper-alpha, …) controls the visible marker on <ol> / <ul> items.

list-style-position: outside (default) places the marker in the padding band to the left of the item’s content, so multi-line items keep their text aligned at a single left edge. list-style-position: inside reflows the marker as part of the item’s first line.

The marker itself can be styled via the ::marker pseudo-element:

li::marker {
    color: darkslateblue;
    font-weight: bold;
    font-family: monospace;
}

li.note::marker {
    content: "→ ";
}

The content property on ::marker replaces the default list-style-type glyph for that item. See the marker-pseudo example.

Generated content and counters

::before and ::after pseudo-elements render generated content via the content property. Strings, attribute values (attr(name)), counter functions and the leader(…) token are supported:

h2::before {
    content: counter(section) ". ";
}
figcaption::before {
    content: "Figure " counter(figure) ": ";
}

CSS Counters work as in CSS Lists 3:

Property Effect
counter-reset: name [value] (Re)set a named counter on entering this element.
counter-increment: name [step] Bump the counter by step (default 1) when this element matches.
counter(name) The current value of the counter as a string.
counters(name, sep) Joined values up the ancestor stack (for nested numbering like 1.2.3).

Counters are scoped by the element tree: a counter-reset on a section boundary clears nested counters, exactly as the spec prescribes.

body { counter-reset: section; }
h2   { counter-reset: subsection; counter-increment: section; }
h3   { counter-increment: subsection; }
h2::before { content: counter(section) " "; }
h3::before { content: counter(section) "." counter(subsection) " "; }

See the numbered-sections-counters example for a full document with hierarchical numbering.

boxesandglue extensions

Vendor-prefixed (-bag-…) properties expose Knuth-Plass line-breaker knobs that have no direct CSS-spec equivalent. They take part in the normal CSS cascade — set them on body for a document-wide default, on a class for per-element tuning, override with !important.

Property Type Default Effect
-bag-linebreak-tolerance float 4 Knuth-Plass badness ceiling (TeX \tolerance). Higher values let the line breaker accept looser lines. TeX uses 200 for \fussy and 10000 for \sloppy.
-bag-linebreak-hyphen-penalty int 50 Demerits added at a hyphenation breakpoint (TeX \hyphenpenalty). Lower values encourage hyphenation; useful for German compound words where the default prefers a slightly overfull line to a hyphenated loose one.
/* Document-wide: relaxed line breaker, hyphenate eagerly. */
body {
	-bag-linebreak-tolerance: 200;
	-bag-linebreak-hyphen-penalty: 5;
}

/* Narrow column: even more permissive. */
.sidenote {
	-bag-linebreak-tolerance: 1000;
}

Hyphenation patterns themselves are selected by the standard lang="…" HTML attribute (resolved through CSS Text 3 §6 hyphens), or — for whole documents — by the front-matter lang: key in the Markdown frontend. Tags without TeX patterns (Arabic, Hebrew, CJK, unknown) resolve to a no-op hyphenator and never produce break points; the tunables above only matter when patterns are present.

Selector support

The full CSS3 selector grammar works: type, class, id, attribute, descendant, child and sibling combinators, plus structural pseudo-classes like :first-child, :last-child, :nth-child(n), :nth-of-type(n), :not(...). A common use case is alternating-row styling on tables — see the zebra-table example.

Box model details

padding-left and margin-left on a block container shift child boxes to the right and reduce their content width accordingly, exactly as CSS prescribes. So ul { padding-left: 20pt } indents list items, blockquote { margin-left: 20pt } indents the quote body, and nested containers stack their shifts.

What’s resolved when

CSS is fully resolved before htmlbag walks the DOM — by the time CSSBuilder.OutputPages runs, every element carries its computed properties in HTMLItem.Styles (a map[string]string). This means selector matches and class-driven rules are picked up; you do not need to inline styles to make them visible to htmlbag’s detection.