Skip to main content

HTML+CSS Embed Renderer

Renders HTML+CSS to a Skia Picture for opaque embedding on the canvas (HTMLEmbedNode).

Source: crates/grida-canvas/src/htmlcss/


Architecture

Three-phase pipeline inspired by Chromium's Style → Layout → Paint separation. All Skia object construction is deferred to phase 3 because Stylo's global DOM slot corrupts Skia objects built during borrow_data() traversal.

Phase 1: Collect (collect.rs)     Phase 2: Layout (layout.rs)     Phase 3: Paint (paint.rs)
┌──────────────────────┐ tree ┌──────────────────────┐ tree ┌──────────────────────┐
│ csscascade (Stylo) │ ──────► │ Taffy (block/flex/ │ ────► │ Skia PictureRecorder │
│ ComputedValues → │ Styled │ grid layout) │ Layout │ Canvas draw ops │
│ StyledElement tree │ Element │ + MeasureFunc for │ Box │ Paragraph, Paint, │
│ (plain Rust, no Skia)│ │ text (Skia Para) │ │ RRect, Shader │
└──────────────────────┘ └──────────────────────┘ └──────────────────────┘

Module structure:

FilePurpose
mod.rsPublic API: render(), measure_content_height()
types.rsCSS-specific enums (Display, Position, Overflow, FlexDirection, ...)
style.rsStyledElement IR, reusing cg primitives where aligned
collect.rsStylo DOM → StyledElement tree (no Skia objects)
layout.rsStyledElement → Taffy → LayoutBox tree (positioned)
paint.rsLayoutBox → Skia Picture (backgrounds, borders, text, gradients)

CG type reuse

Types from cg::prelude reused where they 100% align with CSS semantics:

cg typeCSS property
CGColorcolor, background-color, border-color
EdgeInsetspadding (resolved px)
BlendModemix-blend-mode
TextAligntext-align
FontWeightfont-weight
TextTransformtext-transform
TextDecorationStyletext-decoration-style

CSS Property Support

Status key: ✅ supported | ⚠️ partial | ❌ not yet

Display & Box Generation

CSS PropertyStatusNotes
display: blockVia Taffy Display::Block with margin collapsing
display: inlineMerged into parent's Paragraph as InlineRunItem
display: inline-block⚠️Treated as inline
display: noneSubtree skipped
display: flexVia Taffy — direction, wrap, align, justify, gap
display: inline-flex
display: gridVia Taffy Display::Grid — full property support
display: inline-grid
display: list-itemMarker text generated (bullet/number)
display: table⚠️Falls back to block flow (no column grid)
display: table-row⚠️Falls back to flex (faux-table)
display: table-cell⚠️Falls back to flex item
display: contents
display: flow-root

Box Model

CSS PropertyStatusNotes
width, heightpx and auto
width, height (%)⚠️% not resolved against parent
min-width, max-widthVia Taffy
min-height, max-heightVia Taffy
aspect-ratio
inline-size, block-sizeLogical sizing not mapped
min-inline-size, etc.Logical sizing not mapped
padding (all sides)px values
margin (all sides)px, auto; collapsing via Taffy block flow
box-sizingVia Taffy
overflowhidden/clip via canvas clip_rect
overflow-x, overflow-y⚠️Mapped to single overflow axis
overflow-clip-margin
overflow-wrap / word-wrap
resize

Positioning

CSS PropertyStatusNotes
position: staticDefault
position: relativeVia Taffy
position: absoluteVia Taffy
position: fixed
position: sticky
top, right, bottom, left⚠️Stub in collect.rs, returns defaults
inset (shorthand)
inset-block, inset-inlineLogical insets not mapped
z-index⚠️Stored but not used for paint order
floatRecognized in collect, no layout effect
clearRecognized in collect, no layout effect

Flexbox

CSS PropertyStatusNotes
flex-directionVia Taffy
flex-wrapVia Taffy
flex-flowShorthand; direction + wrap
align-itemsVia Taffy
align-selfVia Taffy
align-contentVia Taffy
justify-contentVia Taffy
justify-items
justify-self
place-contentShorthand
place-itemsShorthand
place-selfShorthand
flex-growVia Taffy
flex-shrinkVia Taffy
flex-basisVia Taffy
flexShorthand
gapVia Taffy
row-gapVia Taffy
column-gapVia Taffy
order

Grid

CSS PropertyStatusNotes
grid-template-columnspx, %, fr, minmax(), fit-content(), repeat()
grid-template-rowspx, %, fr, minmax(), fit-content(), repeat()
grid-template-areasNot collected from Stylo (named areas not mapped)
grid-templateShorthand
grid-auto-columnsImplicit track sizing
grid-auto-rowsImplicit track sizing
grid-auto-flowrow, column, dense
grid-column-start/endLine numbers, span
grid-row-start/endLine numbers, span
grid-columnShorthand
grid-rowShorthand
grid-areaNamed area placement not supported
gridShorthand
repeat(auto-fill, ...)Via Taffy
repeat(auto-fit, ...)Via Taffy
Named grid linesLine names ignored; numeric placement only
subgridTaffy does not support subgrid

Sizing Keywords

CSS PropertyStatusNotes
min-content, max-contentIntrinsic sizing keywords
fit-content
contain-intrinsic-size

Background

CSS PropertyStatusNotes
background-colorSolid color with border-radius
background-image: url()
linear-gradient()All directions + angles, multi-stop
radial-gradient()Circle/ellipse
conic-gradient()Sweep gradient
Multi-layer backgroundsStacked gradient + solid layers
background-position
background-size
background-repeat
background-origin
background-clip
background-attachment
background-blend-modeDifferent from mix-blend-mode
background (shorthand)⚠️Color and gradient layers only

Border

CSS PropertyStatusNotes
border-width (all sides)
border-color (all sides)
border-style (all sides)solid/dashed/dotted painted; rest fallback
border-style: grooveEnum defined, paint falls back to solid
border-style: ridgeEnum defined, paint falls back to solid
border-style: insetEnum defined, paint falls back to solid
border-style: outsetEnum defined, paint falls back to solid
border-style: doubleEnum defined, paint falls back to solid
border-radiusPer-corner elliptical (separate rx/ry)
border (shorthand)
border-image
border-image-outset
border-image-repeat
border-image-slice
border-image-source
border-image-width
border-collapse
border-spacing
Logical border propertiesborder-block-*, border-inline-*

Outline

CSS PropertyStatusNotes
outline
outline-color
outline-style
outline-width
outline-offset

Box Shadow

CSS PropertyStatusNotes
box-shadow (outer)blur, spread, offset, border-radius
box-shadow: insetclip + EvenOdd frame via PathBuilder
Multiple shadowsAll shadows stacked in order

Color

CSS PropertyStatusNotes
colorInherited
opacityVia canvas save_layer
color-scheme
accent-color
forced-color-adjust
print-color-adjust

Font

CSS PropertyStatusNotes
font (shorthand)
font-familyGeneric families mapped to platform names
font-sizeComputed px
font-weight100–900
font-styleitalic
font-stretch
font-size-adjust
font-kerning
font-optical-sizing
font-synthesisShorthand
font-synthesis-weight
font-synthesis-style
font-synthesis-small-caps
font-variant (shorthand)
font-variant-ligatures
font-variant-caps
font-variant-numeric
font-variant-east-asian
font-variant-alternates
font-variant-position
font-variant-emoji
font-feature-settings
font-variation-settings
font-language-override
font-palette

Text Layout

CSS PropertyStatusNotes
line-heightnormal, number, length
letter-spacing
word-spacing
text-alignleft, right, center, justify
text-align-last
text-justify
text-indentField defined in FontProps, not extracted
text-transformuppercase, lowercase, capitalize
white-spacenormal, pre, pre-wrap, pre-line, nowrap
white-space-collapse
word-break
overflow-wrap / word-wrap
line-break
hyphens
hyphenate-character
hyphenate-limit-chars
tab-size
text-overflowEnum defined (Clip/Ellipsis), not extracted
text-wrap
text-wrap-mode
text-wrap-style
hanging-punctuation
text-spacing-trim
text-autospace
widows
orphans

Text Decoration

CSS PropertyStatusNotes
text-decoration (shorthand)underline, line-through, overline (bitfield — simultaneous)
text-decoration-line
text-decoration-style⚠️Field defined, not extracted from Stylo
text-decoration-color⚠️Field defined, not extracted from Stylo
text-decoration-thickness
text-decoration-skip-ink
text-underline-position
text-underline-offset

Text Shadow & Emphasis

CSS PropertyStatusNotes
text-shadowNot in type schema
text-emphasis
text-emphasis-style
text-emphasis-color
text-emphasis-position

Writing Modes & BiDi

CSS PropertyStatusNotes
direction
writing-mode
unicode-bidi
text-orientation
text-combine-upright

Inline Layout & Alignment

CSS PropertyStatusNotes
vertical-alignEnum defined, not extracted
dominant-baseline
alignment-baseline
baseline-shift
initial-letter

Ruby

CSS PropertyStatusNotes
ruby-position
ruby-align
ruby-overhang

Lists

FeatureStatusNotes
<ul> with disc/circle/squareMarker text prepended to list item content
<ol> with decimal numberingAuto-incrementing counter
lower-alpha, upper-alpha
lower-roman, upper-romanStylo servo-mode limitation (servo/stylo#349)
list-style-type: none
list-style-image
list-style-position
list-style (shorthand)⚠️type only
Nested listsIndependent counters per list

Visual Effects

CSS PropertyStatusNotes
opacityVia canvas save_layer
visibilityhidden/collapse skips painting
mix-blend-modeAll CSS blend modes
isolation

Transform

CSS PropertyStatusNotes
transform2D: translate, rotate, scale, skew, matrix
transform-originPercentage-based origins (default 50% 50%)
transform-box
transform-style
translateIndividual property (use transform: instead)
rotateIndividual property (use transform: instead)
scaleIndividual property (use transform: instead)
perspective
perspective-origin
backface-visibility

Filter & Effects

CSS PropertyStatusNotes
filter
backdrop-filter
clip-path
clip-rule
mask
mask-image
mask-clip
mask-composite
mask-mode
mask-origin
mask-position
mask-repeat
mask-size
mask-type

CSS Motion Path (Offset)

CSS PropertyStatusNotes
offset
offset-path
offset-distance
offset-rotate
offset-anchor
offset-position

Multi-column Layout

CSS PropertyStatusNotes
columns
column-count
column-width
column-gapVia Taffy gap (flex/grid only)
column-rule
column-span
column-fill
break-before
break-after
break-inside

Table Layout

CSS PropertyStatusNotes
table-layout
border-collapse
border-spacing
caption-side
empty-cells

Generated Content & Counters

CSS PropertyStatusNotes
content (::before/after)Pseudo-elements not supported
counter-resetInternal counters for <ol> only
counter-increment
counter-set
quotes

Scroll Snap

CSS PropertyStatusNotes
scroll-snap-typeStatic render only
scroll-snap-alignStatic render only
scroll-snap-stopStatic render only
scroll-padding
scroll-margin
scroll-behavior
overscroll-behavior

Scrollbar

CSS PropertyStatusNotes
scrollbar-width
scrollbar-color
scrollbar-gutter

Contain & Content Visibility

CSS PropertyStatusNotes
contain
content-visibility
will-change

Image Rendering

CSS PropertyStatusNotes
image-rendering
image-orientation
object-fit
object-position
object-view-box

Shape (Floats)

CSS PropertyStatusNotes
shape-outside
shape-margin
shape-image-threshold

SVG Presentation Attributes

CSS PropertyStatusNotes
fillSVG elements not rendered
fill-opacity
fill-rule
stroke
stroke-width
stroke-opacity
stroke-dasharray
stroke-dashoffset
stroke-linecap
stroke-linejoin
stroke-miterlimit
paint-order
vector-effect
marker, marker-*
clip-rule
color-interpolation
flood-color
flood-opacity
stop-color
stop-opacity
lighting-color
text-anchor
<svg> inline

Replaced Elements

CSS PropertyStatusNotes
<img> renderingImages not loaded/painted
<video>, <canvas>

Interaction & UI

CSS PropertyStatusNotes
cursorNot relevant for canvas embed
pointer-eventsNot relevant for canvas embed
user-selectNot relevant for canvas embed
touch-actionNot relevant for canvas embed
caret-color
appearance

Animation & Transition

CSS PropertyStatusNotes
transitionStatic render only
transition-propertyStatic render only
transition-durationStatic render only
transition-delayStatic render only
transition-timing-functionStatic render only
animationStatic render only
animation-nameStatic render only
animation-durationStatic render only
animation-delayStatic render only
animation-iteration-countStatic render only
animation-directionStatic render only
animation-timing-functionStatic render only
animation-fill-modeStatic render only
animation-play-stateStatic render only
animation-timelineStatic render only

CSS Variables & Functions

FeatureStatusNotes
Custom properties (var())var() not resolved
calc()⚠️Stylo resolves to px
clamp(), min(), max()⚠️Stylo resolves to px
env()

Inline Elements (HTML)

FeatureStatusNotes
<strong>, <em>, <b>, <i>Bold/italic via font properties
<u>, <ins>Underline decoration
<s>, <del>, <strike>Line-through decoration
<small>Smaller font size
<code>, <kbd>, <mark>Background, border, border-radius, padding via InlineBoxDecoration
Inline box padding as layout spaceSkia placeholders at OpenBox/CloseBox boundaries
Text wrapping with inline boxesTaffy MeasureFunc with Skia Paragraph

Key Design Decisions

Subpixel layout

Taffy rounding is disabled (taffy.disable_rounding()) to match Chromium's subpixel layout precision. Text intrinsic width is ceiled to prevent subpixel-induced wrapping.

Inline box model

Follows Chromium's kOpenTag/kCloseTag model. InlineRunItem::OpenBox and CloseBox inject Skia placeholders that consume inline space matching padding + border width. Decoration rects are painted using Paragraph::get_rects_for_range().

Root margin stripping

<html> and <body> margins are zeroed in the collector since the embed container provides its own bounds. Author padding is preserved.

Whitespace collapsing

Inter-element whitespace (newlines/spaces between block elements) is detected and dropped during inline group flushing to prevent empty 24px-tall blocks.