CSS Fill and Stroke Module Level 3

W3C First Public Working Draft,

More details about this document
This version:
https://www.w3.org/TR/2017/WD-fill-stroke-3-20170404/
Latest published version:
https://www.w3.org/TR/fill-stroke-3/
Editor's Draft:
https://drafts.fxtf.org/fill-stroke/
History:
https://www.w3.org/standards/history/fill-stroke-3/
Feedback:
Inline In Spec
Editors:
Elika J. Etemad / fantasai (Invited Expert)
Tab Atkins-Bittner (Google)
Former Editor:
(Mozilla Corporation)
Issue Tracking:
GitHub Issues
Suggest an Edit for this Spec:
GitHub Editor

Abstract

This module contains the features of CSS relating to filling and stroking text and SVG shapes.

Status of this document

This section describes the status of this document at the time of its publication. A list of current W3C publications and the latest revision of this technical report can be found in the W3C technical reports index at https://www.w3.org/TR/.

This document is a First Public Working Draft.

Publication as a First Public Working Draft does not imply endorsement by W3C and its Members. This is a draft document and may be updated, replaced or obsoleted by other documents at any time. It is inappropriate to cite this document as other than work in progress.

GitHub Issues are preferred for discussion of this specification. When filing an issue, please put the text “fill-stroke” in the title, preferably like this: “[fill-stroke] …summary of comment…”. All issues and comments are archived, and there is also a historical archive.

This document was produced by the CSS Working Group.

This document was produced by a group operating under the W3C Patent Policy. W3C maintains a public list of any patent disclosures made in connection with the deliverables of the group; that page also includes instructions for disclosing a patent. An individual who has actual knowledge of a patent which the individual believes contains Essential Claim(s) must disclose the information in accordance with section 6 of the W3C Patent Policy.

This document is governed by the 03 November 2023 W3C Process Document.

1. Introduction

Text and SVG graphical elements that define a shape—path elements, basic shapes such as rectare rendered by being filled, which is painting the interior of the object, and stroked, which is painting along the outline of the object.

This specification describes how text and SVG graphical elements are filled and stroked, by defining a number of properties that control the appearance and shape of an element’s fill and stroke.

Insert Specifying Paint from SVG2.

Deal with text decoration. See issue and other issue.

2. Paint

Paint is what makes abstract geometries visible. It consists of colors, patterns, images, gradients, and other 2D graphics. The <color> type represents 0-dimensional paint; it is defined in [CSS3-COLOR]. The <paint> type represents 2-dimensional paint, and its syntax is:

<paint> = none | <image> | <svg-paint>
none

Indicates no paint is applied.

<image>

Defined in [CSS3-IMAGES] and includes image references and gradients.

<svg-paint>

References SVG paint servers, and is defined in § 2.1 SVG-Specific Paints. A <url> value here is treated as an ambiguous image URL.

2.1. SVG-Specific Paints

<svg-paint> = child | child( <integer> )
child

Refers to the last child paint server element of the element being painted.

If there is no such child, behaves as none.

child(<integer>)

This functional notation, which accepts a positive integer as its argument, refers to the nth child paint server element (1-indexed, as for ::nth-child()) of the element being painted.

If there is no such child, behaves as none.

Arguments less than 1 are invalid.

w3c/csswg-drafts/383[css-images][css-masking][paint] Ambiguities in handling url()

context-fill and context-stroke need to be worked into the spec, but they belong as keywords on each individual stroke and fill sub-property, not as a <paint> value. Needs more WG discussion, and confirmation that this is an SVG2 feature that will be kept, before we do the relatively substantial edits for it.

CSS Images 4 § 2.7.1 Paint Sources defines how to handle SVG paint server coordinate spaces. Copy, ref, or something else here?

3. Fills

A fill paints within the outlines of an SVG shape or the glyphs of an inline (or SVG text) box. Similar to backgrounds [CSS3BG], fills can be a solid color (fill-color) and/or an image pattern (fill-image etc.).

It is strongly recommended to use the fill shorthand when setting fills. The individual painting longhands should only be used when it’s needed to alter one individual aspect.

Add a box-break property that is a shorthand for box-decoration-break, fill-break, and stroke-break?

3.1. Layering Multiple Fills

The fill of a box can have multiple layers. The number of layers is determined by the number of comma-separated values for the fill-image property. A value of none still creates a layer.

List-valued properties interact exactly as for background images, defined in CSS Backgrounds 3 § 2.1 Layering Multiple Background Images.

3.2. Fill Geometry

3.2.1. Winding Rule for SVG shapes: the fill-rule property

Name: fill-rule
Value: nonzero | evenodd
Initial: nonzero
Applies to: SVG shapes
Inherited: yes
Percentages: N/A
Computed value: as specified
Canonical order: per grammar
Animation type: discrete
Media: visual

The fill-rule property indicates the rule used to determine what parts of the canvas are included inside the shape. For a simple, non-intersecting path, it is intuitively clear what region lies “inside”; however, for a more complex path, such as a path that intersects itself or where one subpath encloses another, the interpretation of “inside” is not so obvious.

The fill-rule property provides two options for how the inside of a shape is determined:

nonzero
This rule determines the “insideness” of a point on the canvas by drawing a ray from that point to infinity in any direction and then examining the places where a segment of the shape crosses the ray. Starting with a count of zero, add one each time a path segment crosses the ray from left to right and subtract one each time a path segment crosses the ray from right to left. After counting the crossings, if the result is zero then the point is outside the path. Otherwise, it is inside.
The effect of a nonzero fill rule on paths with self-intersections and enclosed subpaths.
evenodd
This rule determines the “insideness” of a point on the canvas by drawing a ray from that point to infinity in any direction and counting the number of path segments from the given shape that the ray crosses. If this number is odd, the point is inside; if even, the point is outside.
The effect of an evenodd fill rule on paths with self-intersections and enclosed subpaths.

Note: The above descriptions do not specify what to do if a path segment coincides with or is tangent to the ray. Since any ray will do, one may simply choose a different ray that does not have such problem intersections.

This property does not apply to text (whether SVG or CSS), as the “inside” of a text glyph is intrinsically defined.

Should this be layerized?

3.2.2. Fragmented Fills: the fill-break property

Name: fill-break
Value: bounding-box | slice | clone
Initial: bounding-box
Applies to: all elements
Inherited: yes?
Percentages: N/A
Computed value: as specified
Canonical order: per grammar
Animation type: discrete
Media: visual

This property specifies how the geometry of a fragmented box is treated for fills.

Values have the following meanings: COPY FROM FRAGMENTATION

3.3. Fill Paint

3.3.1. Fill Color: the fill-color property

Name: fill-color
Value: <color>
Initial: currentcolor
Applies to: text and SVG shapes
Inherited: yes
Percentages: N/A
Computed value: the computed color
Canonical order: per grammar
Animation type: by computed value
Media: visual

This property sets the fill color of an element. This color is drawn behind any fill images.

It’s recommended that authors use the color property to set the color of text, rather than fill-color.

Unlike background-color, the fill color must only be drawn if the final layer of fill-image is none or an invalid image. If it’s a valid image, the fill color must not be drawn.

Note: This is required to match the legacy behavior of SVG’s fill property, which built in a "fallback color" to the single-layer image syntax. In CSS this is better achieved with the image() function, which makes fallback explicit.

fill-color should be layerized, like stroke-color, so the two sets of properties are maximally consistent.

SVG user agents must include the following rules (or their equivalent) in their user agent style sheet:

@namespace svg "http://www.w3.org/2000/svg";
svg:svg:root, *|*:not(svg|*) > svg:svg {
  fill-color: black;
}

3.3.2. Fill Image Sources: the fill-image property

Name: fill-image
Value: <paint>#
Initial: none
Applies to: text and SVG shapes
Inherited: yes
Percentages: N/A
Computed value: as specified, with any <image> computed
Canonical order: per grammar
Animation type: repeatable list
Media: visual

This property sets the fill images of an element. Images are drawn with the first specified one on top (closest to the user) and each subsequent image behind the previous one. Values are interpreted identically to background-image, mutatis mutandi.

3.3.3. Fill Positioning Area: the fill-origin property

Name: fill-origin
Value: match-parent | fill-box | stroke-box | content-box | padding-box | border-box
Initial: match-parent
Applies to: all elements
Inherited: no
Percentages: N/A
Computed value: as specified
Canonical order: per grammar
Animation type: discrete
Media: visual

This property specifies the coordinate system of the fill, setting the fill positioning area. Values have the following meanings:

match-parent
Use the same fill positioning area as the parent. If it has no parent, use the initial containing block.
content-box
padding-box
border-box
Use the box’s own content-box/padding-box/border-box as the fill positioning area. For SVG shapes, content-box and padding-box are treated as fill-box, while border-box is treated as stroke-box.
fill-box
For SVG shapes or SVG text, use the object bounding box of the element. For CSS boxes, use the bounding box of the text glyph outlines of the element and all in-flow or floated descendants.
stroke-box
For SVG shapes or SVG text, use the stroke bounding box of the element. For CSS boxes, use the bounding box of the text glyph stroke outlines of the element and all in-flow or floated descendants.

The SVG UA style sheet is amended to include the following rules:

svg:svg {
  fill-origin: content-box;
}

The fill painting area is infinite in size. When painting, the fill color/images are intersected with the glyph areas of the affected text, or the fill geometry of the affected SVG shape.

SVG paint servers carry around their own originator information in *Units attributes, but maybe SVGWG can add a new value that makes them pay attention to the CSS originator. Alternately, we can add an auto initial value here, which defers to the paint server’s information if you’re referring to one, and otherwise is match-parent.

3.3.4. Positioning Fill Images: the fill-position property

Name: fill-position
Value: <position>#
Initial: 0% 0%
Applies to: text and SVG shapes
Inherited: yes
Percentages: n/a
Computed value: A list, each item consisting of: a pair of offsets (horizontal and vertical) from the top left origin each given as a combination of an absolute length and a percentage
Canonical order: per grammar
Animation type: repeatable list
Media: visual

If fill images have been specified, this property specifies their initial position (after any resizing) within their corresponding fill positioning area. Values are interpreted identically to background-position, mutatis mutandi.

3.3.5. Sizing Fill Images: the fill-size property

Name: fill-size
Value: <bg-size>#
Initial: auto
Applies to: text and SVG shapes
Inherited: yes
Percentages: n/a
Computed value: as specified, but with lengths made absolute and omitted auto keywords filled in
Canonical order: per grammar
Animation type: repeatable list
Media: visual

Specifies the size of the fill images. Values are interpreted identically to background-size, mutatis mutandi.

3.3.6. Tiling Fill Images: the fill-repeat property

Name: fill-repeat
Value: <repeat-style>#
Initial: repeat
Applies to: text and SVG shapes
Inherited: yes
Percentages: n/a
Computed value: A list, each item consisting of: two keywords, one per dimension
Canonical order: per grammar
Animation type: discrete
Media: visual

Specifies how fill images are tiled after they have been sized and positioned. Values are interpreted identically to background-repeat, mutatis mutandi.

3.3.7. Fill Shorthand: the fill property

Name: fill
Value: <'background'> with modifications
Initial: See individual properties
Applies to: See individual properties
Inherited: See individual properties
Percentages: N/A
Computed value: See individual properties
Canonical order: per grammar
Animation type: See individual properties
Media: visual

This property is a shorthand that sets all of the fill painting properties—fill-color, fill-image, fill-origin, fill-position, fill-size, and fill-repeatin one declaration. Omitted values are set to their initial value, except that an omitted fill-color is set to transparent, and an omitted fill-origin is set to fill-box.

What should be the default value for fill-origin when set via this shorthand? content-box or fill-box? fill-box works better for fancy/overflowing fonts like Zapfino, but it’s much more expensive to calculate than content-box.

SVG has special color fallback syntax—a color following an image is only drawn if the image fails. This conflicts with our desire to match background, because fill: url(#foo) red; does *not* fill with red and then draw #foo. Compromise: fill-color is a fallback color, but both none and invalid images trigger the fallback. So fill: url(#foo) red; will only draw the red if there is no #foo (like SVG), but fill: url(#foo), red (equivalent to none red on the last layer) will draw the red and the #foo (like background).

3.4. Fill Transparency

3.4.1. Fill Opacity: the fill-opacity property

Name: fill-opacity
Value: <'opacity'>
Initial: 1
Applies to: text and SVG shapes
Inherited: yes
Percentages: N/A
Computed value: the specified value converted to a <number>, clamped to the range [0,1]
Canonical order: per grammar
Animation type: by computed value
Media: visual

The fill-opacity property specifies the opacity of the painting operation used to fill the element. As with opacity, a value of 0 or 0% means fully transparent, and a value of 1 or 100% means fully opaque.

4. Strokes (Outlines)

A stroke draws a border along the outlines of an SVG shape or the glyphs of an inline (or SVG text) box. The resulting area can be filled similar to the fill area. Strokes can be a solid color (stroke-color) and/or an image pattern (stroke-image etc.).

It is strongly recommended to use the stroke shorthand when setting strokes. The individual painting longhands should only be used when it’s needed to alter one individual aspect.

By default, strokes are drawn on top of fills; the drawing order is controlled by the paint-order property.

4.1. Layering Multiple Strokes

The stroke of a box can have multiple layers. The number of layers is determined by the larger of the number of comma-separated values for the stroke-image property and the number of comma-separated values for the stroke-color property. A value of none still creates a layer.

If the two properties have different lengths, the values are aligned from the end, with missing front values for stroke-color defaulting to transparent, and missing front values for stroke-image defaulting to none.

List-valued properties interact exactly as for background images, defined in CSS Backgrounds 3 § 2.1 Layering Multiple Background Images.

stroke-color must front-fill with transparent, otherwise multiple image layers will randomly fall back to solid colors if the image fails to load. Do we want to have strokes and fills front-fill with initial values in general, or should the rest of these properties repeat their lists as they do for backgrounds?

4.2. Stroke Geometry

We currently layerize the stroke paint props, and stroke-width, per SVGWG resolution. Should we layerize the other geometry properties? They have similar use-cases, albeit somewhat more niche, and consistency in API is important. Worth the implementation/testing effort? Hard case: stroke-dasharray, because it has "commas anywhere lol" syntax.

4.2.1. Stroke Thickness: the stroke-width property

Name: stroke-width
Value: <length-percentage>#
Initial: 1px
Applies to: text and SVG shapes
Inherited: yes
Percentages: relative to the scaled viewport size
Computed value: the absolute length, or percentage
Canonical order: per grammar
Animation type: by computed value
Media: visual

This property specifies the width of the strokes on the outline. A zero value causes no stroke to be painted for that layer. Negative values are invalid.

The scaled viewport size is the geometric mean of the viewport width and height.

4.2.2. Stroke Positioning: the stroke-align property

Name: stroke-align
Value: center | inset | outset
Initial: center
Applies to: text and SVG shapes
Inherited: yes
Percentages: N/A
Computed value: as specified
Canonical order: per grammar
Animation type: discrete
Media: visual

This property allows the author to align a stroke along the outline.

center
The stroke for each subpath is centered on the outline.
inset
The stroke for each subpath lies on the “inside” of the outline (into the fill area).

The stroke-linejoin property must be ignored.

Why is stroke-linejoin ignored? It’s still needed for interior corners.

outset
The stroke for each subpath lies on the “outside” of the outline (outside the fill area).
Text is easy, as are simple closed, non-self-intersecting paths, but the other categories exist and need good definitions.

How does this apply to open path segments? One suggestion is to alias outset to left and inset to right for open paths. How are end caps handled?

  1. Stroke with stroke-align center value.

  2. Fill region.

  3. Stroke with stroke-align inset value per spec (? stroke only paints inside fill region).

  4. Stroke with stroke-align outset value (implemented by blocking out fill region).

  5. Stroke with stroke-align inset value, alternative interpretation.

  6. Stroke with stroke-align outset value per spec (?).

  7. Stroke with stroke-align alternative left value; pink shows round line-cap.

How does this apply to paths with loops? Is the region inside the red circle in the below figure part of the stroked (as shown)? Are internal edges stroked if the fill rule is nonzero; if so how? (Shown without stroking below.)

  • Top row: Normal stroke. Stroke left. Stroke right.

  • Middle row: Fill rule nonzero, no stroke. Stroke outside. Stroke inside.

  • Bottom row: Fill rule evenodd, no stroke. Stroke outside. Stroke inside.

How are dashes handled? Are they based on original path?

  1. Normal dash pattern.

  2. Dash pattern based on dash pattern or original path. Note light-gray region which is from inside dash part (simply using clipping path does not yield proper result).

  3. Dash pattern based on center of offset path. Light gray represents dash pattern based on center of inset path.

4.2.3. Stroke End Shapes: the stroke-linecap property

Name: stroke-linecap
Value: butt | round | square
Initial: butt
Applies to: text and SVG shapes
Inherited: yes
Percentages: N/A
Computed value: as specified
Canonical order: per grammar
Animation type: discrete
Media: visual

stroke-linecap specifies the shape to be used at the end of open subpaths (such as the segments of a dashed stroke, or an unclosed path element) when they are stroked. The possible values are:

butt
The stroke for each subpath does not extend beyond its two endpoints. (A zero length subpath will therefore not have any stroke.)
round
At each end of each subpath, the stroke is extended by a half circle with a radius equal to half the stroke width. (The stroke for a zero-length subpath is a full circle centered at the subpath’s point.)
square
At the end of each subpath, the stroke is extended by a rectangle with the same width as the stroke width and whose length is half of the stroke width. (The stroke for a zero-length subpath is a square with side length equal to the stroke width, centered at the subpath’s point, and oriented such that two of its sides are parallel to the effective tangent at that subpath’s point. See § 4.6 Computing the Shape of the Stroke for details on how to determine the tangent at a zero-length subpath.)

Adding a rectangle to the end of dashes on a curved outline looks bad. It should just extend the dash by stroke-width/2, following the outline.

The three types of line caps.

See the definition of the cap shape below for a more precise description of the shape a line cap will have.

4.2.4. Stroke Corner Shapes: the stroke-linejoin property

Name: stroke-linejoin
Value: [ crop | arcs | miter ] || [ bevel | round | fallback ]
Initial: miter
Applies to: text and SVG shapes
Inherited: yes
Percentages: N/A
Computed value: as specified
Canonical order: per grammar
Animation type: discrete
Media: visual

stroke-linejoin specifies the shape to be used at the corners of paths or basic shapes when they are stroked. It has two parts: [ crop | arcs | miter ] specifies whether or not, and how, to extend the corner of the stroke; [ bevel | round | fallback ] specifies how to render the "cap" of the corner when it’s limited in length by stroke-miterlimit.

crop
The stroke extends the minimal amount past the corner necessary to form a convex corner.

This acts identically to miter, but forces stroke-miterlimit to behave as if it had its minimum value of 1.

miter
A sharp corner is used to join path segments. The corner is formed by extending the outer edges of the stroke at the tangents of the path segments until they intersect.
arcs
An arcs corner is used to join path segments. The arcs shape is formed by extending the outer edges of the stroke at the join point with arcs that have the same curvature as the outer edges at the join point.
bevel
The corner is cropped at the stroke-miterlimit by the perpendicular to its diagonal.
round
The corner is cropped as for bevel, and a filled arc tangent to the truncated stroke edges is appended to round the corner.
fallback
When the stroke-miterlimit is exceeded, this behaves as if crop bevel were specified.

Can we just remove fallback? It’s a stupid value, useful only because we didn’t have the clip-at-miterlimit behavior in SVG1. Question is if people are mostly just *accidentally* getting the bevel behavior right now, and would be okay with their joins extending up to the miterlimit and only getting the excess corners clipped, or if they actually wanted the discontinuous behavior currently specified. The breakpoint is between 29 and 30 degrees.

If [ crop | arcs | miter ] is omitted, it defaults to crop. If [ bevel | round | fallback ] is omitted, it defaults to fallback.

Four types of line joins
A more detailed look at precisely how each type of join is shaped. For each, the dashed white line is the path being stroked, and the black is the stroke itself, before applying the linejoin. The pink area is the additional stroke added at the corner for bevel, round, and miter linejoins.

4.2.5. Stroke Corner Limits: the stroke-miterlimit property

Name: stroke-miterlimit
Value: <number>
Initial: 4
Applies to: text and SVG shapes
Inherited: yes
Percentages: N/A
Computed value: a number
Canonical order: per grammar
Animation type: discrete
Media: visual

This property specifies the maximum size of a miter or arcs join when two stroke segments meet at a corner. (The apparent “size” of the join—the distance between the inner and outer corner—approaches infinity as the angle decreases.)

This property has no effect when stroke-align is inset.

Values have the following meanings:

<number>
Specifies the limit on the join’s size as a ratio of its diagonal to the stroke-width. Values less than 1 are invalid (and make the declaration invalid).

For a miter linejoin, the length of the diagonal is calculated from the angle between the two segments as stroke-width / sin(θ/2). The linejoin is clipped perpendicular to the line bisecting the angle between the two path segments.

For an arcs linejoin, the length of the diagonal is calculated along a circular arc that is tangent to the line bisecting the angle between the two segments at the point the two segments intersect and passes through the end point of the join. The linejoin is clipped perpendicular to this arc.

Should we add angles? The miterlimit number has physical justification, but it’s difficult to understand without experimentation.

4.2.6. Fragmented Strokes: the stroke-break property

Name: stroke-break
Value: bounding-box | slice | clone
Initial: bounding-box
Applies to: all elements
Inherited: ?
Percentages: N/A
Computed value: as specified
Canonical order: per grammar
Animation type: discrete
Media: visual

This property specifies how the geometry of a fragmented box is treated for strokes.

Values have the following meanings: COPY FROM FRAGMENTATION

4.3. Stroke Dashing

4.3.1. Stroke Dash Patterns: the stroke-dasharray property

Name: stroke-dasharray
Value: none | <length-percentage>+#
Initial: none
Applies to: text and SVG shapes
Inherited: yes
Percentages: relative to the scaled viewport size
Computed value: as specified
Canonical order: per grammar
Animation type: repeatable list
Media: visual

This property controls the pattern of dashes and gaps used to stroke paths.

none
No dashing: the stroke is drawn continuously.
<length-percentage>+#
Specifies a dashing pattern to use. Each <length-percentage> value represents the length of the next dash or gap (beginning with the first dash and alternating from there) of the stroke. The pattern repeats over the length of the stroke. (If the number of values is odd, the pattern behaves as if it was duplicated to yield an even number of values.) The dashing pattern is reset and begins anew at the start of each subpath.

Negative values are invalid. If all values are zero, it is treated as none.

SVG allows comma separation. Do we need to allow this in CSS for back-compat? (Please say no.)

Need a way to specify dash lengths relative to the width of the stroke. (For instance, to do square dashes.)

Need a way to specify dash lengths relative to the length of the path. (For instance, to do the "self-drawing SVG" thing without script.)

A dashed stroke. The dashing pattern is 20 10. The red line shows the actual path that is stroked.

4.3.2. Stroke Dash Start Position: the stroke-dashoffset property

Name: stroke-dashoffset
Value: <length-percentage>
Initial: 0
Applies to: text and SVG shapes
Inherited: yes
Percentages: relative to the scaled viewport size
Computed value: as specified
Canonical order: per grammar
Animation type: repeatable list
Media: visual

This property specifies the distance into the repeated dash pattern to start dashing at the beginning of the path. Values can be negative.

A dashed stroke with a non-zero dash offset. The dashing pattern is 20 10 and the dash offset is 15. The red line shows the actual path that is stroked.

See § 4.6.3 Dash Positions for a more precise description of positions along a path that dashes will be placed.

4.3.3. Corner Control: the stroke-dash-corner and stroke-dash-justify properties

Name: stroke-dash-corner
Value: none | <length>
Initial: none
Applies to: text and SVG shapes
Inherited: yes
Percentages: N/A
Computed value: specified value, with lengths made absolute
Canonical order: per grammar
Animation type: by computed value if <length>, otherwise discrete
Media: visual

The stroke-dash-corner property controls whether a dash is always painted at the vertices of a stroked shape. The points at which a dash corner is painted include the start and end points of every segment within the shape’s equivalent path.

none

This property has no special effect on dashing: the dashes are positioned/sized continuously along the path.

<length>

A dash of the given <length> must be painted at each vertex of the shape. For an open shape, the first corner dash is positioned so that it begins at the start of the path, and the last corner dash so that it ends at the end of the path. The other corner dashes of an open shape, and all corner dashes of a closed shape, are positioned so that they are centered on their vertex.

Should the corner dash at the first and last vertex of an open shape be half the length of the others? Should this be author controllable?

Should there be a way to specify a padding, so that any dash pattern between the corner dashes does not not run up against them?

The corner needs to take over the role of the first dash in the dash pattern, so the "interior" of each segment starts and ends with a gap. Automatically skip the first dash in the pattern in each segment?

Keyword to use the length of the first dash as the corner.

When stroke-dash-corner is not none, dash patterns (as specified by stroke-dasharray) are repeated separately on each path segment, in the space between the segment’s corner dashes.

Need to define what happens when corner dashes would overlap.

Should we auto-trigger justification when this property is active? Without it, you’ll get some dumb-looking results by default.

Image showing a rectangle at each corner and short dashes in between.
A rectangle painted with a dashed stroke and with corner dashes, set with stroke-dasharray: 0 8px; stroke-dash-corner: 32px;.
Name: stroke-dash-justify
Value: none | [ stretch | compress ] || [ dashes || gaps ]
Initial: none
Applies to: text and SVG shapes
Inherited: yes
Percentages: N/A
Computed value: specified value, with lengths made absolute
Canonical order: per grammar
Animation type: discrete
Media: visual

The stroke-dash-justify property specifies whether and how a stroke’s dash pattern will be adjusted so that it is repeated a whole number of times along each of an element’s subpaths.

none

No adjustment of the dash pattern is made: it’s clipped at the end of the subpath with however much progress it made to that point.

stretch

Indicates that when the dash pattern does not fit into a subpath a whole number times, the dashes or gaps (or both) will be lengthened so that it does.

compress

Indicates that when the dash pattern does not fit into a subpath a whole number times, the dashes or gaps (or both) will be shortened so that it does.

dashes

Indicates that when a dash pattern is to be stretched or compressed, the length of the dashes will be adjusted.

gaps

Indicates that when a dash pattern is to be stretched or compressed, the length of the gaps will be adjusted.

If neither stretch nor compress is specified, it defaults to whichever would result in less adjustment.

If neither dashes nor gaps is specified, it defaults to ???.

Default should probably be to adjust the gaps only.

The target length that a dash pattern will be adjusted to depends on the value of the stroke-dash-corner property:

The adjustment of dash and gap length in a dash pattern is done by scaling the lengths by a factor, which is the number closest to 1 that will result in the dash pattern fitting in the target length a whole number of times. If stretch is used, the factor is a number between 1 and 2, while if compress is used, the factor is a number between 0 and 1.

If stretch is specified, but the dash pattern is longer than the target length, no adjustment is performed.

Should it center the segment when it’s too long?

If compress is specified, but the adjustable parts of the dash pattern can’t be compressed enough to fit the dash pattern into the target length, the compressible parts must be set to zero.

Do we want to allow control of whether the stroke ends with a dash or with a gap? For a closed path, you probably want a gap at the end, while for an open path, a dash at the end is probably better. Maybe omit control for this, and just do it automatically based on whether the path is closed?

The same dash stroke used on three different paths, adjusted to fit a whole number of times on each.

4.4. Stroke Paint

4.4.1. Stroke Color: the stroke-color property

Name: stroke-color
Value: <color>#
Initial: transparent
Applies to: text and SVG shapes
Inherited: yes
Percentages: N/A
Computed value: the computed color
Canonical order: per grammar
Animation type: by computed value
Media: visual

This property sets the stroke colors of an element. Like stroke-image, stroke colors are drawn with the first specified one on top (closest to the user) and each subsequent color behind the previous one.

Stroke colors are only drawn if the stroke-image on the corresponding layer is none or an invalid image. (In other words, each layer draws either an image or a color, with images winning if both are specified.)

Note: This is required to match the legacy behavior of SVG’s stroke property, which built in a "fallback color" to the single-layer image syntax. In CSS this is better achieved with the image() function, which makes fallback explicit.

4.4.2. Stroke Image Sources: the stroke-image property

Name: stroke-image
Value: <paint>#
Initial: none
Applies to: text and SVG shapes
Inherited: yes
Percentages: N/A
Computed value: as specified, with any <image> computed
Canonical order: per grammar
Animation type: repeatable list
Media: visual

This property sets the stroke images of an element. Images are drawn with the first specified one on top (closest to the user) and each subsequent image behind the previous one. Values are interpreted identically to background-image, mutatis mutandi.

4.4.3. Stroke Positioning Area: the stroke-origin property

Name: stroke-origin
Value: match-parent | fill-box | stroke-box | content-box | padding-box | border-box
Initial: match-parent
Applies to: all elements
Inherited: no
Percentages: N/A
Computed value: as specified
Canonical order: per grammar
Animation type: discrete
Media: visual

This property specifies the coordinate system of the stroke, setting the stroke positioning area. Values have the following meanings:

match-parent
Use the same stroke positioning area as the parent. If it has no parent, use the initial containing block.
content-box
padding-box
border-box
Use the box’s own content-box/padding-box/border-box as the stroke positioning area. For SVG shapes, content-box and padding-box are treated as fill-box, while border-box is treated as stroke-box.
fill-box
For SVG shapes or SVG text, use the object bounding box of the element. For CSS boxes, use the bounding box of the text glyph outlines of the element and all in-flow or floated descendants.
stroke-box
For SVG shapes or SVG text, use the stroke bounding box of the element. For CSS boxes, use the bounding box of the text glyph stroke outlines of the element and all in-flow or floated descendants.

The SVG UA style sheet is amended to include the following rules:

svg:svg {
  stroke-origin: content-box;
}

The stroke painting area is infinite in size. When painting, the stroke color/images are intersected with the glyph strokes of the affected text, or the stroke geometry of the affected SVG shape.

SVG paint servers carry around their own originator information in *Units attributes, but maybe SVGWG can add a new value that makes them pay attention to the CSS originator. Alternately, we can add an auto initial value here, which defers to the paint server’s information if you’re referring to one, and otherwise is match-parent.

4.4.4. Positioning Stroke Images: the stroke-position property

Name: stroke-position
Value: <position>#
Initial: 0% 0%
Applies to: text and SVG shapes
Inherited: yes
Percentages: n/a
Computed value: A list, each item consisting of: a pair of offsets (horizontal and vertical) from the top left origin each given as a combination of an absolute length and a percentage
Canonical order: per grammar
Animation type: repeatable list
Media: visual

If stroke images have been specified, this property specifies their initial position (after any resizing) within their corresponding stroke positioning area. Values are interpreted identically to background-position, mutatis mutandi.

4.4.5. Sizing Stroke Images: the stroke-size property

Name: stroke-size
Value: <bg-size>#
Initial: auto
Applies to: text and SVG shapes
Inherited: yes
Percentages: n/a
Computed value: as specified, but with lengths made absolute and omitted auto keywords filled in
Canonical order: per grammar
Animation type: repeatable list
Media: visual

Specifies the size of the stroke images. Values are interpreted identically to background-size, mutatis mutandi.

4.4.6. Tiling Stroke Images: the stroke-repeat property

Name: stroke-repeat
Value: <repeat-style>#
Initial: repeat
Applies to: text and SVG shapes
Inherited: yes
Percentages: n/a
Computed value: A list, each item consisting of: two keywords, one per dimension
Canonical order: per grammar
Animation type: discrete
Media: visual

Specifies how stroke fill images are tiled after they have been sized and positioned. Values are interpreted identically to background-repeat, mutatis mutandi.

4.4.7. Stroke Shorthand: the stroke property

Name: stroke
Value: <'background'> with modifications
Initial: See individual properties
Applies to: See individual properties
Inherited: See individual properties
Percentages: N/A
Computed value: See individual properties
Canonical order: per grammar
Animation type: See individual properties
Media: visual

This property is a shorthand that sets all of the stroke painting properties—stroke-color, stroke-image, stroke-origin, stroke-position, stroke-size, and stroke-repeatin one declaration. Omitted values are set to their initial value, except that an omitted stroke-color is set to transparent, and an omitted stroke-origin is set to stroke-box.

4.5. Stroke Transparency

4.5.1. Stroke Opacity: the stroke-opacity property

Name: stroke-opacity
Value: <'opacity'>
Initial: 1
Applies to: text and SVG shapes
Inherited: yes
Percentages: N/A
Computed value: the specified value converted to a <number>, clamped to the range [0,1]
Canonical order: per grammar
Animation type: by computed value
Media: visual

The stroke-opacity property specifies the opacity of the painting operation used to stroke the current object. As with opacity, a value of 0 or 0% means fully transparent, and a value of 1 or 100% means fully opaque.

4.6. Computing the Shape of the Stroke

4.6.1. Stroke Paths

In all cases, all stroking properties which are affected by directionality, such as those having to do with dash patterns, must be rendered such that the stroke operation starts at the same point at which the graphics element starts. In particular, for path elements, the start of the path is the first point of the initial “moveto” command.

For stroking properties such as dash patterns whose computations are dependent on progress along the outline of the graphics element, distance calculations are required to utilize the SVG user agent’s standard Distance along a path algorithms.

When stroking is performed using a complex paint server, such as a gradient or a pattern, the stroke operation must be identical to the result that would have occurred if the geometric shape defined by the geometry of the current graphics element and its associated stroking properties were converted to an equivalent path element and then filled using the given paint server.

A subpath consisting of a single “moveto” shall not be stroked. Any zero length subpath shall not be stroked if the stroke-linecap property has a value of butt but shall be stroked if the stroke-linecap property has a value of round or square, producing respectively a circle or a square centered at the given point. Examples of zero-length subpaths include 'M 10,10 L 10,10', 'M 20,20 h 0', 'M 30,30 z', and 'M 40,40 c 0,0 0,0 0,0'.

This should be redundant with the stroke shape computation requirements below. In this section, we should phrase the requirements descriptively rather than normatively.

4.6.2. Stroke Shape

The stroke shape of an element is the shape that is filled by the stroke property. The following algorithm describes what the stroke shape of text, a path, or basic shape is, taking into account the stroking properties above:
  1. Let shape be an empty shape.

  2. Let path be the equivalent path of the element.

  3. For each subpath of path:

    1. Let positions be the dash positions for the subpath.

    2. For each pair (start, end) in positions:

      1. Let dash be the shape that includes, for all distances between start and end along the subpath, all points that lie on the line perpendicular to the subpath at that distance and which are within distance stroke-width of the point on the subpath at that position.

      2. Set dash to be the union of dash and the starting cap shape for the subpath at position start.

      3. Set dash to be the union of dash and the ending cap shape for the subpath at position end.

      4. Let index and last be the indexes of the path segments in the subpath at distance start and end along the subpath.

        Note: It does not matter whether any zero length segments are included when choosing index and last.

      5. While index < last:

        1. Set dash to be the union of dash and the line join shape for the subpath at segment index index.

        2. Set index to index + 1.

      6. Set shape to be the union of shape and dash.

  4. Return shape.

4.6.3. Dash Positions

This section doesn’t handle stroke-dash-corner and stroke-dash-justify yet.

The dash positions for a given subpath of the equivalent path of a path or basic shape is a sequence of pairs of values, which represent the starting and ending distance along the subpath for each of the dashes that form the subpath’s stroke. It is determined as follows:
  1. Let pathlength be the length of the subpath.

  2. Let dashes be the list of values of stroke-dasharray on the element, repeated if necessary so that it has an even number of elements; if the property has the value none, then the list has a single value 0.

  3. Let count be the number of values in dashes.

  4. Let sum be the sum of the values in dashes.

  5. If sum = 0, then return a sequence with the single pair (0, pathlength).

  6. Let positions be an empty sequence.

  7. Let offset be the value of the stroke-dashoffset property on the element.

  8. If offset is negative, then set offset to sum − abs(offset).

  9. Set offset to offset mod sum.

  10. Let index be the smallest integer such that sum(dashesi, 0 ≤ i ≤ index) ≥ offset.

  11. Let dashlength be min(sum(dashesi, 0 ≤ i ≤ index) − offset, pathlength).

  12. If index mod 2 = 0, then append to positions the pair (0, dashlength).

  13. Let position be dashlength.

  14. While position < pathlength:

    1. Set index to (index + 1) mod count.

    2. Let dashlength be min(dashesindex, pathlengthposition).

    3. If index mod 2 = 0, then append to positions the pair (position, position + dashlength).

    4. Set position to position + dashlength.

  15. Return positions.

4.6.4. Cap Shapes

The starting and ending cap shapes at a given position along a subpath are determined as follows:
  1. If stroke-linecap is butt, then return an empty shape.

  2. Otherwise, if stroke-linecap is round, then:

    1. If this is a starting cap, then return a semicircle of radius stroke-width positioned such that:

      • Its straight edge is parallel to the line perpendicular to the subpath at distance position along it.

      • The midpoint of its straight edge is at the point that is along the subpath at distance position.

      • The direction from the midpoint of its arc to the midpoint of its straight edge is the same as the direction of the subpath at distance position along it.

    2. Otherwise, this is an ending cap. Return a semicircle of radius stroke-width positioned such that:

      • Its straight edge is parallel to the line perpendicular to the subpath at distance position along it.

      • The midpoint of its straight edge is at the point that is along the subpath at distance position.

      • The direction from the midpoint of its straight edge to the midpoint of its arc is the same as the direction of the subpath at distance position along it.

  3. Otherwise, stroke-linecap is square:

    1. If this is a starting cap, then return a rectangle with side lengths stroke-width and stroke-width / 2 positioned such that:

      • Its longer edges, A and B, are parallel to the line perpendicular to the subpath at distance position along it.

      • The midpoint of A is at start.

      • The direction from the midpoint of B to the midpoint of A is the same as the direction of the subpath at distance position along it.

    2. Otherwise, this is an ending cap. Return a rectangle with side lengths stroke-width and stroke-width / 2 positioned such that:

      • Its longer edges, A and B, are parallel to the line perpendicular to the subpath at distance position along it.

      • The midpoint of A is at end.

      • The direction from the midpoint of A to the midpoint of B is the same as the direction of the subpath at distance position along it.

The three different stroke-linecap values used on paths with a single, non-zero length subpath. The white line is the path itself and the thick gray area is the stroke.

On the top row, the green lines indicate the perpendicular to the tangent at the path endpoints and the pink areas are the caps. The bottom row shows the stroke without the perpendicular and cap highlighting.

4.6.5. Line Join Shape

The line join shape for a given segment of a subpath is determined as follows:
  1. Let P be the point at the end of the segment.

  2. Let A be the line parallel to the tangent at the end of the segment.

  3. Let B be the line parallel to the tangent at the start of the following segment.

  4. If A and B are the same line, then return an empty shape.

  5. Let |Aleft| and |Aright| be lines parallel to A at a distance of stroke-width / 2 to the left and to the right of A relative to the subpath direction, respectively.

  6. Let |Bleft| and |Bright| be lines parallel to B at a distance of stroke-width / 2 to the left and to the right of B relative to the subpath direction, respectively.

  7. Let |P1|, |P2| and |P3| be points determined as follows:

    1. If the smaller angle between A and B is on the right of these lines, considering the direction of the subpath, then |P1| and |P2| are the points on |Aleft| and |Bleft| closest to P, and |P3| is the intersection of |Aleft| and |Bleft|.

    2. Otherwise, |P1| and |P2| are the points on |Aright| and |Bright| closest to P, and |P3| is the intersection of |Aright| and |Bright|.

  8. Let bevel be the triangle formed from the three points P, |P1| and |P2|.

  9. If stroke-linejoin is round, then return the union of bevel and a circular sector of radius stroke-width, centered on P, and which has |P1| and |P2| as the two endpoints of the arc.

  10. If stroke-linejoin is arcs, then find the circles that are tangent to the stroke edges at |P1| and |P2| with the same curvature as the edges at those points (see below). If both curvatures are zero, fall through to miter-clip.

    Extend the stroke edges using these circles (or a line, in the case of zero curvature):

    • If the two circles (or circle and line) do not intersect, fall through to miter-clip.

    • If the two circles (or circle and line) intersect, the line join region is defined by the lines that connect P with |P1| and |P2| and the arcs defined by the circles (or arc and line) between the closest intersection point to P, and |P1| and |P2|.

      Next, calculate the miter limit as defined in § 4.2.5 Stroke Corner Limits: the stroke-miterlimit property. Clip any part of the line join region that extends past the miter limit. Return the resulting region.

      Note: Note that the curvatures are calculated in user-space before any transforms are applied.

  11. If stroke-linejoin is miter or miter-clip then the line join region is the union of bevel and the triangle formed from the three points |P1|, |P2| and |P3|.

  12. Let θ be the angle between A and B. If 1 / sin(θ / 2) ≤ stroke-miterlimit, then return the line join region.

  13. If stroke-linejoin is miter-clip, then clip any part of the line join region that extends past the miter limit and return this region.

  14. Return bevel.

Construction of a round line join shape, shown in pink. The white line is the original path, which has two segments that come to a point, and the gray region is the stroke.
Construction of an arcs line join shape, shown in pink. The white line is the original path, which has two segments that come to a point, and the gray region is the stroke. The dashed lines show circles that are tangent to and have the curvature of the segments at the join. The olive-green circles (concentric with the dashed circles) define the join shape.

4.6.6. Arcs Linejoin

The arcs linejoin requires finding circles that are both tangent to and have the same curvatures as the outer stroke edges at the ends of path segments. To find one of these circles, first calculate the curvature κ of the path segment at its end (see below). Next, find the radius of a circle corresponding to this curvature: r = 1/κ. Increase or decrease the radius by stroke-width / 2 to account for the stroke: rc = r ± stroke-width/2. The center of the circle will be on a line normal to the path end a distance of rc away from the outer stroke edge at the end.
For a line:
The curvature is infinite. Extend the outer stroke edge by a line.
For an elliptical arc:

where:

The parameter θ at the beginning or end of an arc segment can be found by using the formulas in the Elliptical arc implementation notes. (Note, some renderers convert elliptical arcs to cubic Béziers prior to rendering so the equations here may not be needed.)

For a quadratic Bézier:

Where κ(0) and κ(1) are the signed curvatures at the start and end of the path segment respectively, and the P’s are the three points that define the quadratic Bézier.

For a cubic Bézier:

Where κ(0) and κ(1) are the signed curvatures at the start and end of the path segment respectively, and the P’s are the four points that define the cubic Bézier. Note, if P0 and P1, or P2 and P3, are degenerate, the curvature will be infinite and a line should be used in constructing the join.

4.7. Perfect-World Syntax

The stroke-* properties and values from SVG inherit SVG’s somewhat incoherent naming schemes. We’d name them differently if they were being designed today. We should see how much we can move toward this, perhaps with alias/shorthands.

Old Syntax New Syntax
stroke stroke-paint
stroke shorthand
stroke-alignment stroke-align: inset | outset | center
stroke-opacity no change
stroke-width no change
stroke-linecap stroke-cap: none | round | square
stroke-linejoin stroke-corner-shape: bevel | round | [ arc | miter ] [ bevel | round ]?
stroke-miterlimit stroke-corner-limit
stroke-dasharray stroke-dash-array
stroke-dashoffset stroke-dash-offset
stroke-dashcorner stroke-dash-corner
stroke-dashadjust stroke-dash-justify: none | [ stretch | compress ] || [ dashes | gaps ]
stroke-dash shorthand

5. Text Decoration Fills and Strokes

For handling fills and strokes on text decorations, two sets of fill and stroke properties are defined: one prefixed with text-decoration-* for line decorations and one prefixed with text-emphasis-* for emphasis marks. These properties are exactly analogous to the fill and stroke properties above, except that:

These should definitely be at-risk, possibly deferred to the next level.

6. Privacy and Security Considerations

This specification introduces no new privacy or security considerations.

Conformance

Document conventions

Conformance requirements are expressed with a combination of descriptive assertions and RFC 2119 terminology. The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in the normative parts of this document are to be interpreted as described in RFC 2119. However, for readability, these words do not appear in all uppercase letters in this specification.

All of the text of this specification is normative except sections explicitly marked as non-normative, examples, and notes. [RFC2119]

Examples in this specification are introduced with the words “for example” or are set apart from the normative text with class="example", like this:

This is an example of an informative example.

Informative notes begin with the word “Note” and are set apart from the normative text with class="note", like this:

Note, this is an informative note.

Advisements are normative sections styled to evoke special attention and are set apart from other normative text with <strong class="advisement">, like this: UAs MUST provide an accessible alternative.

Conformance classes

Conformance to this specification is defined for three conformance classes:

style sheet
A CSS style sheet.
renderer
A UA that interprets the semantics of a style sheet and renders documents that use them.
authoring tool
A UA that writes a style sheet.

A style sheet is conformant to this specification if all of its statements that use syntax defined in this module are valid according to the generic CSS grammar and the individual grammars of each feature defined in this module.

A renderer is conformant to this specification if, in addition to interpreting the style sheet as defined by the appropriate specifications, it supports all the features defined by this specification by parsing them correctly and rendering the document accordingly. However, the inability of a UA to correctly render a document due to limitations of the device does not make the UA non-conformant. (For example, a UA is not required to render color on a monochrome monitor.)

An authoring tool is conformant to this specification if it writes style sheets that are syntactically correct according to the generic CSS grammar and the individual grammars of each feature in this module, and meet all other conformance requirements of style sheets as described in this module.

Partial implementations

So that authors can exploit the forward-compatible parsing rules to assign fallback values, CSS renderers must treat as invalid (and ignore as appropriate) any at-rules, properties, property values, keywords, and other syntactic constructs for which they have no usable level of support. In particular, user agents must not selectively ignore unsupported component values and honor supported values in a single multi-value property declaration: if any value is considered invalid (as unsupported values must be), CSS requires that the entire declaration be ignored.

Implementations of Unstable and Proprietary Features

To avoid clashes with future stable CSS features, the CSSWG recommends following best practices for the implementation of unstable features and proprietary extensions to CSS.

Non-experimental implementations

Once a specification reaches the Candidate Recommendation stage, non-experimental implementations are possible, and implementors should release an unprefixed implementation of any CR-level feature they can demonstrate to be correctly implemented according to spec.

To establish and maintain the interoperability of CSS across implementations, the CSS Working Group requests that non-experimental CSS renderers submit an implementation report (and, if necessary, the testcases used for that implementation report) to the W3C before releasing an unprefixed implementation of any CSS features. Testcases submitted to W3C are subject to review and correction by the CSS Working Group.

Further information on submitting testcases and implementation reports can be found from on the CSS Working Group’s website at http://www.w3.org/Style/CSS/Test/. Questions should be directed to the [email protected] mailing list.

Index

Terms defined by this specification

Terms defined by reference

References

Normative References

[CSS-BREAK-3]
Rossen Atanassov; Elika Etemad. CSS Fragmentation Module Level 3. 4 December 2018. CR. URL: https://www.w3.org/TR/css-break-3/
[CSS-BREAK-4]
Rossen Atanassov; Elika Etemad. CSS Fragmentation Module Level 4. 18 December 2018. WD. URL: https://www.w3.org/TR/css-break-4/
[CSS-CASCADE-5]
Elika Etemad; Miriam Suzanne; Tab Atkins Jr.. CSS Cascading and Inheritance Level 5. 13 January 2022. CR. URL: https://www.w3.org/TR/css-cascade-5/
[CSS-COLOR-4]
Chris Lilley; Tab Atkins Jr.; Lea Verou. CSS Color Module Level 4. 13 February 2024. CR. URL: https://www.w3.org/TR/css-color-4/
[CSS-COLOR-5]
Chris Lilley; et al. CSS Color Module Level 5. 29 February 2024. WD. URL: https://www.w3.org/TR/css-color-5/
[CSS-DISPLAY-3]
Elika Etemad; Tab Atkins Jr.. CSS Display Module Level 3. 30 March 2023. CR. URL: https://www.w3.org/TR/css-display-3/
[CSS-IMAGES-4]
Tab Atkins Jr.; Elika Etemad; Lea Verou. CSS Images Module Level 4. 17 February 2023. WD. URL: https://www.w3.org/TR/css-images-4/
[CSS-SYNTAX-3]
Tab Atkins Jr.; Simon Sapin. CSS Syntax Module Level 3. 24 December 2021. CR. URL: https://www.w3.org/TR/css-syntax-3/
[CSS-TEXT-DECOR-3]
Elika Etemad; Koji Ishii. CSS Text Decoration Module Level 3. 5 May 2022. CR. URL: https://www.w3.org/TR/css-text-decor-3/
[CSS-TEXT-DECOR-4]
Elika Etemad; Koji Ishii. CSS Text Decoration Module Level 4. 4 May 2022. WD. URL: https://www.w3.org/TR/css-text-decor-4/
[CSS-VALUES-4]
Tab Atkins Jr.; Elika Etemad. CSS Values and Units Module Level 4. 12 March 2024. WD. URL: https://www.w3.org/TR/css-values-4/
[CSS3-COLOR]
Tantek Çelik; Chris Lilley; David Baron. CSS Color Module Level 3. 18 January 2022. REC. URL: https://www.w3.org/TR/css-color-3/
[CSS3-IMAGES]
Tab Atkins Jr.; Elika Etemad; Lea Verou. CSS Images Module Level 3. 18 December 2023. CR. URL: https://www.w3.org/TR/css-images-3/
[CSS3BG]
Elika Etemad; Brad Kemper. CSS Backgrounds and Borders Module Level 3. 11 March 2024. CR. URL: https://www.w3.org/TR/css-backgrounds-3/
[RFC2119]
S. Bradner. Key words for use in RFCs to Indicate Requirement Levels. March 1997. Best Current Practice. URL: https://datatracker.ietf.org/doc/html/rfc2119
[SVG2]
Amelia Bellamy-Royds; et al. Scalable Vector Graphics (SVG) 2. 4 October 2018. CR. URL: https://www.w3.org/TR/SVG2/

Property Index

Name Value Initial Applies to Inh. %ages Anim­ation type Canonical order Com­puted value Media
fill <'background'> with modifications See individual properties See individual properties See individual properties N/A See individual properties per grammar See individual properties visual
fill-break bounding-box | slice | clone bounding-box all elements yes? N/A discrete per grammar as specified visual
fill-color <color> currentcolor text and SVG shapes yes N/A by computed value per grammar the computed color visual
fill-image <paint># none text and SVG shapes yes N/A repeatable list per grammar as specified, with any <image> computed visual
fill-opacity <'opacity'> 1 text and SVG shapes yes N/A by computed value per grammar the specified value converted to a <number>, clamped to the range [0,1] visual
fill-origin match-parent | fill-box | stroke-box | content-box | padding-box | border-box match-parent all elements no N/A discrete per grammar as specified visual
fill-position <position># 0% 0% text and SVG shapes yes n/a repeatable list per grammar A list, each item consisting of: a pair of offsets (horizontal and vertical) from the top left origin each given as a combination of an absolute length and a percentage visual
fill-repeat <repeat-style># repeat text and SVG shapes yes n/a discrete per grammar A list, each item consisting of: two keywords, one per dimension visual
fill-rule nonzero | evenodd nonzero SVG shapes yes N/A discrete per grammar as specified visual
fill-size <bg-size># auto text and SVG shapes yes n/a repeatable list per grammar as specified, but with lengths made absolute and omitted auto keywords filled in visual
stroke <'background'> with modifications See individual properties See individual properties See individual properties N/A See individual properties per grammar See individual properties visual
stroke-align center | inset | outset center text and SVG shapes yes N/A discrete per grammar as specified visual
stroke-break bounding-box | slice | clone bounding-box all elements ? N/A discrete per grammar as specified visual
stroke-color <color># transparent text and SVG shapes yes N/A by computed value per grammar the computed color visual
stroke-dash-corner none | <length> none text and SVG shapes yes N/A by computed value if <length>, otherwise discrete per grammar specified value, with lengths made absolute visual
stroke-dash-justify none | [ stretch | compress ] || [ dashes || gaps ] none text and SVG shapes yes N/A discrete per grammar specified value, with lengths made absolute visual
stroke-dasharray none | <length-percentage>+# none text and SVG shapes yes relative to the scaled viewport size repeatable list per grammar as specified visual
stroke-dashoffset <length-percentage> 0 text and SVG shapes yes relative to the scaled viewport size repeatable list per grammar as specified visual
stroke-image <paint># none text and SVG shapes yes N/A repeatable list per grammar as specified, with any <image> computed visual
stroke-linecap butt | round | square butt text and SVG shapes yes N/A discrete per grammar as specified visual
stroke-linejoin [ crop | arcs | miter ] || [ bevel | round | fallback ] miter text and SVG shapes yes N/A discrete per grammar as specified visual
stroke-miterlimit <number> 4 text and SVG shapes yes N/A discrete per grammar a number visual
stroke-opacity <'opacity'> 1 text and SVG shapes yes N/A by computed value per grammar the specified value converted to a <number>, clamped to the range [0,1] visual
stroke-origin match-parent | fill-box | stroke-box | content-box | padding-box | border-box match-parent all elements no N/A discrete per grammar as specified visual
stroke-position <position># 0% 0% text and SVG shapes yes n/a repeatable list per grammar A list, each item consisting of: a pair of offsets (horizontal and vertical) from the top left origin each given as a combination of an absolute length and a percentage visual
stroke-repeat <repeat-style># repeat text and SVG shapes yes n/a discrete per grammar A list, each item consisting of: two keywords, one per dimension visual
stroke-size <bg-size># auto text and SVG shapes yes n/a repeatable list per grammar as specified, but with lengths made absolute and omitted auto keywords filled in visual
stroke-width <length-percentage># 1px text and SVG shapes yes relative to the scaled viewport size by computed value per grammar the absolute length, or percentage visual

Issues Index

Insert Specifying Paint from SVG2.
Deal with text decoration. See issue and other issue.
w3c/csswg-drafts/383[css-images][css-masking][paint] Ambiguities in handling url()
context-fill and context-stroke need to be worked into the spec, but they belong as keywords on each individual stroke and fill sub-property, not as a <paint> value. Needs more WG discussion, and confirmation that this is an SVG2 feature that will be kept, before we do the relatively substantial edits for it.
CSS Images 4 § 2.7.1 Paint Sources defines how to handle SVG paint server coordinate spaces. Copy, ref, or something else here?
Add a box-break property that is a shorthand for box-decoration-break, fill-break, and stroke-break?
Should this be layerized?
fill-color should be layerized, like stroke-color, so the two sets of properties are maximally consistent.
SVG paint servers carry around their own originator information in *Units attributes, but maybe SVGWG can add a new value that makes them pay attention to the CSS originator. Alternately, we can add an auto initial value here, which defers to the paint server’s information if you’re referring to one, and otherwise is match-parent.
What should be the default value for fill-origin when set via this shorthand? content-box or fill-box? fill-box works better for fancy/overflowing fonts like Zapfino, but it’s much more expensive to calculate than content-box.
SVG has special color fallback syntax—a color following an image is only drawn if the image fails. This conflicts with our desire to match background, because fill: url(#foo) red; does *not* fill with red and then draw #foo. Compromise: fill-color is a fallback color, but both none and invalid images trigger the fallback. So fill: url(#foo) red; will only draw the red if there is no #foo (like SVG), but fill: url(#foo), red (equivalent to none red on the last layer) will draw the red and the #foo (like background).
stroke-color must front-fill with transparent, otherwise multiple image layers will randomly fall back to solid colors if the image fails to load. Do we want to have strokes and fills front-fill with initial values in general, or should the rest of these properties repeat their lists as they do for backgrounds?
We currently layerize the stroke paint props, and stroke-width, per SVGWG resolution. Should we layerize the other geometry properties? They have similar use-cases, albeit somewhat more niche, and consistency in API is important. Worth the implementation/testing effort? Hard case: stroke-dasharray, because it has "commas anywhere lol" syntax.
Why is stroke-linejoin ignored? It’s still needed for interior corners.
Text is easy, as are simple closed, non-self-intersecting paths, but the other categories exist and need good definitions.

How does this apply to open path segments? One suggestion is to alias outset to left and inset to right for open paths. How are end caps handled?

  1. Stroke with stroke-align center value.

  2. Fill region.

  3. Stroke with stroke-align inset value per spec (? stroke only paints inside fill region).

  4. Stroke with stroke-align outset value (implemented by blocking out fill region).

  5. Stroke with stroke-align inset value, alternative interpretation.

  6. Stroke with stroke-align outset value per spec (?).

  7. Stroke with stroke-align alternative left value; pink shows round line-cap.

How does this apply to paths with loops? Is the region inside the red circle in the below figure part of the stroked (as shown)? Are internal edges stroked if the fill rule is nonzero; if so how? (Shown without stroking below.)

  • Top row: Normal stroke. Stroke left. Stroke right.

  • Middle row: Fill rule nonzero, no stroke. Stroke outside. Stroke inside.

  • Bottom row: Fill rule evenodd, no stroke. Stroke outside. Stroke inside.

How are dashes handled? Are they based on original path?

  1. Normal dash pattern.

  2. Dash pattern based on dash pattern or original path. Note light-gray region which is from inside dash part (simply using clipping path does not yield proper result).

  3. Dash pattern based on center of offset path. Light gray represents dash pattern based on center of inset path.

Adding a rectangle to the end of dashes on a curved outline looks bad. It should just extend the dash by stroke-width/2, following the outline.
Can we just remove fallback? It’s a stupid value, useful only because we didn’t have the clip-at-miterlimit behavior in SVG1. Question is if people are mostly just *accidentally* getting the bevel behavior right now, and would be okay with their joins extending up to the miterlimit and only getting the excess corners clipped, or if they actually wanted the discontinuous behavior currently specified. The breakpoint is between 29 and 30 degrees.
Should we add angles? The miterlimit number has physical justification, but it’s difficult to understand without experimentation.
SVG allows comma separation. Do we need to allow this in CSS for back-compat? (Please say no.)
Need a way to specify dash lengths relative to the width of the stroke. (For instance, to do square dashes.)
Need a way to specify dash lengths relative to the length of the path. (For instance, to do the "self-drawing SVG" thing without script.)
Should the corner dash at the first and last vertex of an open shape be half the length of the others? Should this be author controllable?
Should there be a way to specify a padding, so that any dash pattern between the corner dashes does not not run up against them?
The corner needs to take over the role of the first dash in the dash pattern, so the "interior" of each segment starts and ends with a gap. Automatically skip the first dash in the pattern in each segment?
Keyword to use the length of the first dash as the corner.
Need to define what happens when corner dashes would overlap.
Should we auto-trigger justification when this property is active? Without it, you’ll get some dumb-looking results by default.
Default should probably be to adjust the gaps only.
Should it center the segment when it’s too long?
Do we want to allow control of whether the stroke ends with a dash or with a gap? For a closed path, you probably want a gap at the end, while for an open path, a dash at the end is probably better. Maybe omit control for this, and just do it automatically based on whether the path is closed?
SVG paint servers carry around their own originator information in *Units attributes, but maybe SVGWG can add a new value that makes them pay attention to the CSS originator. Alternately, we can add an auto initial value here, which defers to the paint server’s information if you’re referring to one, and otherwise is match-parent.
This should be redundant with the stroke shape computation requirements below. In this section, we should phrase the requirements descriptively rather than normatively.
This section doesn’t handle stroke-dash-corner and stroke-dash-justify yet.
These should definitely be at-risk, possibly deferred to the next level.