CSS property support table
- Display and visibility
- Box model
- Borders, rounded corners, outlines, and shadows
- Placement: positioning, float, and transform
- Fonts and text
- Backgrounds
- Tables
- Lists
- Generated content and counters
- Page breaking (CSS Fragmentation)
- Flexbox
- Grid
- Replaced elements: images
- List of unsupported properties
Display and visibility
| Property | Supported | Notes |
|---|---|---|
display | ⚠️ | block, inline, inline-block, list-item, table, table-row, table-cell, table-caption, flex, and none only. grid is supported; see the Grid section. inline-flex, inline-grid, the internal table values such as table-row-group, and flow-root are not. <thead>, <tbody>, and <tfoot> stay block in the UA stylesheet, but row collection sees through them, so tables still work |
visibility | ⚠️ | visible, hidden, and collapse. collapse is treated as hidden; table row and column sizes are not recomputed. Inherited |
overflow | ⚠️ | Anything other than visible, that is hidden, scroll, and auto, clips alike; they are not told apart. There is no notion of a scrollbar. overflow-x and overflow-y are not supported |
opacity | ⚠️ | <number> or <percentage>, clamped to 0 through 1. Implemented with a PDF transparency group and an ExtGState. Compositing is per element; blending such as mix-blend-mode is not supported |
z-index | ⚠️ | auto or <integer>. It only affects position: relative elements, whereas the specification applies it to every positioned element. It controls the paint order among siblings of the same parent only; separate stacking contexts are not supported. Absolutely positioned elements always paint above the normal flow |
box-sizing | ⚠️ | content-box and border-box. The non-standard padding-box is not supported |
Box model
| Property | Supported | Notes |
|---|---|---|
width, height | ⚠️ | auto, <length>, <percentage>, and calc(). min-content, max-content, and fit-content are not supported. A percentage height is ignored, since the containing block’s height is treated as indefinite |
min-width, min-height | ⚠️ | <length>, <percentage>, and calc(); 0 by default. Keywords such as auto and min-content are not supported. A percentage min-height is ignored |
max-width, max-height | ⚠️ | none, <length>, <percentage>, and calc(); none by default. When min exceeds max, min wins, as the specification says. A percentage max-height is ignored |
aspect-ratio | ⚠️ | auto | <ratio> | auto <ratio>. The normal direction is width first, height derived. Height first, width derived only happens in shrink-to-fit contexts: floats, inline-block, absolute positioning, and <img>. A normal-flow block with width: auto still stretches, as the specification says. When min-* or max-* clamping breaks the ratio, nothing is recomputed |
margin | ✅ | The one to four value shorthand. auto, for centring, and negative values are supported |
margin-top, -right, -bottom, -left | ✅ | Margin collapsing between adjacent siblings and between parent and child is supported |
padding | ✅ | The one to four value shorthand |
padding-top, -right, -bottom, -left | ✅ | Percentages resolve against the containing block’s width, as the specification says |
margin-inline, margin-block, margin-inline-start and so on | ⚠️ | Logical properties. The only writing mode supported is horizontal-tb LTR, so they map to fixed physical sides: inline-start is left, inline-end is right, block-start is top and block-end is bottom (writing-mode and direction are not supported). The two-side shorthands take one or two values, start then end. auto passes through, so margin-inline: auto centres a block |
padding-inline, padding-block, padding-inline-start and so on | ⚠️ | Logical properties, mapped the same way as margin-inline |
Borders, rounded corners, outlines, and shadows
| Property | Supported | Notes |
|---|---|---|
border | ✅ | Accepts <width> || <style> || <color> in any order with any part omitted, and applies it to all four sides |
border-top, -right, -bottom, -left | ✅ | The per-side shorthands; the value syntax is the same as border |
border-inline, border-block, border-inline-start and so on, including the -width, -style and -color longhands | ⚠️ | Logical properties, mapped the same way as margin-inline |
border-width, border-style, border-color | ✅ | The one to four value shorthand |
border-*-width | ⚠️ | <length> only. The thin, medium, and thick keywords are not supported |
border-*-style | ⚠️ | none, hidden, solid, dashed, dotted, double, groove, ridge, inset, and outset. hidden is treated as none, including when resolving table border conflicts. groove, ridge, inset, and outset are drawn with two shades derived from border-color |
border-*-color | ✅ | currentcolor by default |
border-radius | ⚠️ | One to four values, plus the elliptical syntax with /. Percentages are not supported; <length> only. When the four sides do not share the same width, style, and colour, the rounding is abandoned and four straight sides are drawn instead. Combining it with groove, ridge, inset, or outset falls back in the same way |
border-top-left-radius and the other three corners | ⚠️ | <length>{1,2}, the horizontal and vertical radii. The same restrictions as border-radius |
border-start-start-radius and the other three corners | ⚠️ | Logical property. The first keyword is the block axis and the second the inline axis (border-start-end-radius is the top-right corner). Mapped the same way as margin-inline |
outline | ✅ | <width> || <style> || <color>. Drawn outside the border box, with no effect on layout |
outline-width, outline-style, outline-color | ⚠️ | outline-style takes the same values as border-style. The UA-defined auto is not supported |
outline-offset | ❌ | Always 0 |
box-shadow | ⚠️ | none | <shadow>#; several may be given separated by commas, the first being frontmost. inset parses but is not drawn. The blur is approximated with four concentric rectangles |
Placement: positioning, float, and transform
| Property | Supported | Notes |
|---|---|---|
float | ✅ | none, left, and right. Shrink-to-fit for width: auto is supported |
clear | ✅ | none, left, right, and both |
position | ⚠️ | static, relative, absolute, and fixed. sticky is not supported, and absolute and fixed carry the restrictions listed below |
top, right, bottom, left | ⚠️ | With absolute and fixed, aligning to the bottom by giving bottom alone is not supported; everything resolves from top, to avoid a circular dependency on the height. With relative they act as offsets that move the background and border together with the content (text, images, child boxes). Percentages in top/bottom on a relative element count as 0 |
inset, the shorthand | ✅ | The one to four value shorthand, expanded the same way as margin |
inset-inline, inset-block, inset-inline-start and so on | ⚠️ | Logical properties, mapped the same way as margin-inline |
transform | ⚠️ | translate, translateX, translateY, scale, scaleX, scaleY, rotate, skew, skewX, skewY, and matrix. The 3D functions such as translate3d, rotate3d, and perspective() are not supported. It is implemented as a PDF CTM transform, so the transformed content has no bearing on where the pages break |
transform-origin | ⚠️ | The same syntax as background-position: one or two values from keywords, lengths, and percentages. 50% 50% by default. A third value for the Z axis is not supported |
Known restrictions on position: absolute and fixed:
- An absolutely positioned element is taken out of the normal flow and laid down afterwards as an overlay on a page that has already been settled
- Only a positioned ancestor that fits on a single page, or the page area itself, can act as the containing block
- An
absoluteplaced in the middle of text splits the text before and after it onto separate lines, instead of keeping it on one line the way a browser does. - Splitting an absolutely positioned element across pages is not supported; it is placed on one page as best it can be
- In streaming mode,
Mode::Streaming, absolute positioning is ignored
Fonts and text
| Property | Supported | Notes |
|---|---|---|
font-family | ⚠️ | A comma separated list. Of the generic families, serif, sans-serif, and monospace are resolved against the system fonts; cursive and fantasy are not. What sans-serif resolves to can be pinned with the CLI’s --gothic-font |
font-size | ⚠️ | <length> only, in px, em, or rem. Keywords such as smaller, larger, and medium, and percentages, are not supported |
font-weight | ⚠️ | normal, bold, and 100 through 900. Numbers are reduced to two states, 600 and above counting as bold. Without a bold face, a synthetic bold is drawn by filling and stroking |
font-style | ⚠️ | normal, italic, and oblique; oblique is treated as italic and no angle may be given. Without italic glyphs, a synthetic italic is produced by shearing the text matrix |
font, the shorthand | ❌ | Use the individual longhands |
color | ✅ | Inherited. See Selectors, values, and at-rules for the colour notations that may be used |
line-height | ✅ | normal, <number>, <length>, and <percentage>. normal is derived from the font’s own recommended line spacing (ascent + descent + line gap), so its value depends on the font |
text-align | ⚠️ | left, right, center, and justify. justify distributes the slack between words on every line but the last. start and end are not supported, since direction itself is not |
text-indent | ⚠️ | <length> and <percentage>. hanging and each-line are not supported |
text-transform | ⚠️ | none, uppercase, lowercase, and capitalize, which changes the first letter of each word only. full-width and full-size-kana are not supported |
text-decoration, text-decoration-line | ⚠️ | none, underline, and line-through, which may be combined. overline and blink are not supported, nor are the text-decoration-color, -style, and -thickness parts of the shorthand. Propagation from ancestor to descendant is simplified by treating it as an inherited property |
text-shadow | ⚠️ | none | <shadow>#, each being <offset-x> <offset-y> <blur>? <color>?. PDF has no blur filter, so the blur is approximated by drawing several times at reduced alpha. Inherited |
text-overflow | ⚠️ | clip and ellipsis, in effect only when overflow is something other than visible. It applies to lines that overflow horizontally; overflow of the block as a whole is not handled. A <string> value is not supported |
word-break | ✅ | normal, which allows a break only where CJK characters meet, plus break-all and keep-all. The deprecated break-word is not supported |
overflow-wrap, word-wrap | ⚠️ | normal, break-word, and anywhere, the last treated as break-word. It adds no break opportunities; it splits between characters only for a word that would not fit even at the start of a line |
hyphens | ⚠️ | none, manual, and auto. Words break only at a soft hyphen, U+00AD, and a hyphen is shown at the end of the line when they do. There is no dictionary, so auto behaves like manual and never hyphenates automatically |
text-emphasis, -style, -color, -position | ⚠️ | dot, circle, double-circle, triangle, and sesame, each filled or open, plus <string>. The keyword marks are drawn as PDF paths and so do not depend on the font having the glyph, whereas a <string> is drawn as a glyph and disappears if the font lacks it. position takes over and under only; right and left are skipped. The line grows taller to make room for the marks. text-emphasis-skip, which skips punctuation, is not supported |
letter-spacing | ⚠️ | normal and <length>. Percentages are not supported |
word-spacing | ⚠️ | normal and <length> |
white-space | ⚠️ | normal, nowrap, and pre. pre-wrap, pre-line, and break-spaces are not supported |
vertical-align | ✅ | baseline, sub, super, text-top, text-bottom, top, middle, bottom, <length>, and <percentage>. In a table cell, top, middle, bottom, and baseline are the meaningful ones |
quotes | ⚠️ | none, or repeated "open" "close" pairs. Used together with open-quote and close-quote in content. Inherited |
Backgrounds
| Property | Supported | Notes |
|---|---|---|
background, the shorthand | ⚠️ | Accepts <color>, <image>, <repeat>, <attachment>, and <position>[ / <size>] in any order. Longhands left out are reset to their initial values, as the specification says. Note that including background-clip or -origin, that is a keyword such as padding-box, is a parse error and throws the whole declaration away |
background-color | ✅ | Colours with an alpha channel are drawn transparently through an ExtGState |
background-image | ⚠️ | none | url(...) only. Gradient functions such as linear-gradient(), and several comma separated backgrounds, are not supported. By default the image is tiled at its intrinsic size |
background-position | ✅ | One or two values from the keywords left, center, right, top, and bottom, plus lengths and percentages. The three and four value syntax, such as right 10px bottom 20px, is not supported |
background-size | ✅ | cover, contain, or one or two values of <length-percentage> | auto |
background-repeat | ⚠️ | repeat, repeat-x, repeat-y, and no-repeat. The CSS3 round and space, and the two value syntax, are not supported |
background-attachment | ⚠️ | scroll and fixed; since there is no scrolling, fixed is treated as scroll |
background-clip, background-origin, background-blend-mode | ❌ | Not implemented. Backgrounds are drawn against the border box |
When border-radius and background-image are used together, the image is not clipped to the rounded corners; the rounding applies to the background colour fill only.
Tables
| Property | Supported | Notes |
|---|---|---|
table-layout | ✅ | auto and fixed. With auto, column widths come from measuring the natural width of the cell contents; measuring the natural width of a nested table or a flex container is not supported and counts as 0 |
border-collapse | ⚠️ | separate and collapse. collapse merges the borders visually only, simplifying the conflict resolution of CSS 2.1 §17.6.2 to “the thicker one wins, and at equal width the style order decides”. Inherited |
border-spacing | ✅ | <length>{1,2}. Treated as 0 under border-collapse: collapse. Inherited |
caption-side | ⚠️ | top and bottom. The left and right values, which are for vertical writing, are not supported |
empty-cells | ✅ | show and hide, meaningful only under border-collapse: separate. Inherited |
vertical-align on a cell | ✅ | See Fonts and text above |
Column widths from the width attribute or CSS width on <colgroup> and <col>, rowspan and colspan, and repeating <thead> across pages are all supported. rowspan="0" counts as 1.
Anonymous table boxes are generated (CSS 2.1 §17.2.1, rules 2.1 and 2.2). A display: table-cell placed directly inside a display: table is gathered into an anonymous row, and a child that is neither a row nor a cell is wrapped in an anonymous cell. A display: table plus display: table-cell column layout written without any rows works as it is.
min-width and max-width on a cell feed into the column width algorithm. Under table-layout: auto they clamp the natural width of the column, so min-width is no longer guaranteed once the table has been scaled down proportionally to fit the paper. Under table-layout: fixed they clamp the width stated on the first row’s cells, and a cell with width: auto and only a min-width uses that value as the column width.
Lists
| Property | Supported | Notes |
|---|---|---|
list-style, the shorthand | ✅ | Accepts type, position, and image in any order with any part omitted |
list-style-type | ⚠️ | disc, circle, square, decimal, decimal-leading-zero, lower-roman, upper-roman, lower-alpha (lower-latin), upper-alpha (upper-latin), and none. cjk-*, hiragana, katakana, and the like are not supported. Inherited |
list-style-position | ✅ | outside and inside. Inherited |
list-style-image | ⚠️ | none | url(...) parses but is never drawn; it always falls back to the text marker from list-style-type |
Generated content and counters
| Property | Supported | Notes |
|---|---|---|
content | ⚠️ | For ::before, ::after, ::first-letter, and the @page margin boxes. String literals, attr(), counter() and counters(), and open-quote, close-quote, no-open-quote, and no-close-quote can be concatenated. Inserting an image with url() is not supported. As a simplification, ::before and ::after are not generated on an element that has block children |
counter-reset | ✅ | none, or repeated name [<integer>] |
counter-increment | ✅ | none, or repeated name [<integer>], the value being 1 when omitted |
counter-set | ❌ | Not implemented |
Page numbers through counter(page) and counter(pages) can be used inside the @page margin boxes. counter(pages) is an error in streaming mode, where the total page count is never settled.
Page breaking (CSS Fragmentation)
| Property | Supported | Notes |
|---|---|---|
break-before, break-after | ⚠️ | auto, avoid (avoid-page and avoid-column mean the same), and always (page means the same). left, right, recto, and verso, which control spreads, and the multi-column values are not supported |
break-inside | ⚠️ | auto and avoid (avoid-page and avoid-column mean the same) |
page-break-before, page-break-after, page-break-inside | ✅ | Aliases for the break-* properties above, for moving existing wkhtmltopdf and wicked_pdf work across |
orphans, widows | ✅ | An integer of 1 or more; 2 by default |
page, named pages | ❌ | Named pages themselves, such as @page intro, are not supported |
The HTML attribute data-page-break="before|after|avoid" is available as syntactic sugar.
Flexbox
Laying out display: flex is delegated to taffy. A flex container that fits on a page is atomic as far as page breaking goes: it is never split part way through, and it moves to the next page when it does not fit. A container taller than one page is split in units of items that do not overlap vertically (see Page breaking for the details).
| Property | Supported | Notes |
|---|---|---|
flex-direction | ✅ | row, row-reverse, column, and column-reverse |
flex-wrap | ✅ | nowrap, wrap, and wrap-reverse |
justify-content | ⚠️ | normal (the initial value), flex-start (start), flex-end (end), center, space-between, space-around, and space-evenly. The safe and unsafe overflow keywords are not supported |
align-items | ✅ | flex-start (start), flex-end (end), center, baseline, and stretch |
align-content | ✅ | flex-start (start), flex-end (end), center, stretch, space-between, space-around, and space-evenly |
align-self | ✅ | auto, flex-start (start), flex-end (end), center, baseline, and stretch |
flex-grow, flex-shrink | ✅ | A non-negative <number>; a negative value makes the declaration invalid and it is ignored |
flex-basis | ⚠️ | auto, content, and <length-percentage>. content is treated as auto |
flex, the shorthand | ✅ | none, and <grow> [<shrink>] [<basis>]. The specification’s defaulting rules are reproduced: the basis of flex: 1 is 0%, and the grow and shrink of flex: <width> are 1 |
gap, row-gap, column-gap | ⚠️ | In effect on flex containers only; it does not act as the multi-column column-gap |
order | ❌ | The taffy 0.12 series does not support it |
place-content, place-items, place-self, the shorthands | ❌ | Not implemented. Use the individual longhands |
justify-items, justify-self | — | They do not apply to flex items; see below |
justify-items and justify-self having no effect in flex is by design
In CSS Box Alignment, justify-items and justify-self are properties for Grid and block layout and do not apply to flex items; the design is that placing an individual item along the main axis is expressed with justify-content and margin: auto. Browsers ignore them too. Even in taffy, to which layout is delegated, only the Grid algorithm looks at them; the flexbox algorithm never does.
Parsing them here would therefore change nothing on the page, so they are deliberately left unimplemented. To push a particular flex item to one side, use margin: auto, which is supported:
.item { margin-left: auto; } /* the equivalent of justify-self: end, pushing to the end of the main axis */
.item { margin-left: auto; margin-right: auto; } /* the equivalent of justify-self: center */
Grid
Laying out display: grid is delegated to taffy, as flexbox is. A grid that does not fit on one page is broken between rows, the same approach as tables; no break is taken at a boundary crossed by an item that spans several rows.
| Property | Supported | Notes |
|---|---|---|
grid-template-columns, grid-template-rows | ✅ | none, <length>, <percentage>, fr, auto, min-content, max-content, minmax(), fit-content(), repeat(<integer>|auto-fill|auto-fit), and [name] for line names. calc() is not supported in a track size |
grid-template-areas | ✅ | A matrix of strings. A mismatched column count or a non-rectangular area makes the value invalid and the whole declaration is ignored, as the specification says. A . is an unnamed cell |
grid-auto-columns, grid-auto-rows | ✅ | <track-size>+ |
grid-auto-flow | ✅ | row, column, and dense, which may be combined |
grid-row-start, -end, grid-column-start, -end | ✅ | auto, <integer>, span <integer>, <custom-ident>, and span <custom-ident> |
grid-row, grid-column, grid-area | ✅ | The shorthands, separated by /. grid-area: <name> names an area |
justify-items, justify-self | ✅ | Meaningful in Grid only; they do not apply to flex items, as noted above |
align-items, align-self, justify-content, align-content, gap | ✅ | Ranges |
grid, grid-template, the shorthands | ❌ | Use the individual longhands. These pack track and area definitions into one syntax, and that grammar is too involved to support |
display: inline-grid | ❌ | Not supported, for the same reason as inline-flex |
| subgrid, masonry | ❌ | Not implemented |
When a grid built only from auto tracks has an explicit container width, the leftover width is distributed to those auto tracks, but not in the proportion CSS prescribes (an equal share to each auto track). Use fr or a length if the column widths need to be exact.
Replaced elements: images
| Property | Supported | Notes |
|---|---|---|
object-fit | ✅ | fill, contain, cover, none, and scale-down. Meaningful on <img> only |
object-position | ✅ | The same syntax as background-position. 50% 50% by default |
<img> supports inline placement and sizing through the width and height attributes or CSS. The formats supported are PNG, JPEG, and WebP. When CSS gives only one of width and height, the other is derived from the intrinsic aspect ratio (see aspect-ratio).
List of unsupported properties
The following are parse errors and the whole declaration is ignored. They are simply unimplemented, not all deliberately ruled out forever. See the ❌ rows in the tables above as well.
- Shorthands:
font,place-content,place-items,place-self, and the colour and line-style parts oftext-decoration - The sizing logical properties:
inline-size,block-size,min-inline-size,max-block-sizeand so on (themargin,padding,insetandborderlogical properties are supported) - Writing direction:
direction,unicode-bidi,writing-mode,text-orientation,text-combine-upright - Text details:
text-decoration-color,-style,-thickness,text-underline-offset,text-emphasis-skip,tab-size,ruby-*,text-justify,line-break - Font details:
font-variant,font-stretch,font-feature-settings,font-variation-settings,font-kerning,font-display - Multi-column:
columns,column-count,column-width,column-rule,column-span,column-fill - Visual effects:
filter,backdrop-filter,mix-blend-mode,background-blend-mode,clip,clip-path,mask,isolation - 3D and animation:
perspective,transform-style,backface-visibility, the individualtranslate,rotate, andscaleproperties,transition-*,animation-*,will-change - Border and background extensions:
border-image-*,background-clip,background-origin,outline-offset - Overflow:
overflow-x,overflow-y,resize,scroll-*,overscroll-behavior - UI and interaction:
cursor,pointer-events,user-select,caret-color,accent-color,appearance - Other:
all,content-visibility,counter-set,pagefor named pages, the aural media properties such asspeak, andzoom