Title: CSS Borders and Box Decorations Module Level 4 Shortname: css-borders Level: 4 Status: WD Prepare for TR: yes Date: 2025-12-16 Work Status: Exploring Group: csswg ED: https://drafts.csswg.org/css-borders-4/ TR: https://www.w3.org/TR/css-borders-4/ Editor: Elika J. Etemad / fantasai, Apple, http://fantasai.inkedblade.net/contact, w3cid 35400 Editor: Lea Verou, Invited Expert, http://lea.verou.me/about/, w3cid 52258 Editor: Sebastian Zartner, Invited Expert, sebastianzartner@gmail.com, w3cid 64937 Editor: Noam Rosenthal, Google, w3cid 121539 Former Editor: Bert Bos, W3C, bert@w3.org, w3cid 3343 Abstract: This module contains the features of CSS relating to the borders and decorations of boxes on the page. Ignored Terms: border-*-*-radius, border-*-clip, box-shadow-*, corner-*-shape, corner-*-*-shape, corner-*-*, corner-* Ignored Vars: block, inline, Li, Ltop, Lbottom, Lleft, Lright, spread, total width, W, Wside, Wleft, Wright, Wtop, Wbottom Repository: w3c/csswg-drafts WPT Path Prefix: css/css-borders/ WPT Display: open
spec:css-overflow-3; type:value; for:overflow; text:visible spec:css-shapes-2; type:function; text:path() type:property; text:shape-inside spec:css-text-4; type:value; text:collapse spec:dom; type: dfn; text: element; spec:geometry-1; type: dfn; text: height dimension text: width dimension text: rectangle text: x coordinate; for: rectangle text: y coordinate; for: rectangle spec:css-sizing-3; type:dfn; text:height text:size text:width
Name: border-top-color, border-right-color, border-bottom-color, border-left-color, border-block-start-color, border-block-end-color, border-inline-start-color, border-inline-end-color Value: <These properties set the foreground color of the [=border=] specified by the 'border-style' properties. The stripes defined by <> | < > Initial: currentcolor Applies to: all elements except [=ruby base containers=] and [=ruby annotation containers=] Inherited: no Logical property group: border-color Percentages: N/A Computed Value: the computed color and/or a one-dimensional image function Animation type: see prose
.foo {
border: 30px solid;
border-color: stripes(dodgerblue, skyblue) stripes(yellow, gold) stripes(lightgreen, limegreen) stripes(indianred, orange);
}
Sample rendering:
The same border colors with ''border-style: dotted'':
Name: border-color Value: [ <'border-color' is a [=shorthand property=] for the four physical 'border-*-color' properties. The four values set the top, right, bottom and left border, respectively. A missing left is the same as right, a missing bottom is the same as top, and a missing right is also the same as top. This is resolved individually for each list item. The [=flow-relative=] properties 'border-block-start-color', 'border-block-end-color', 'border-inline-start-color', and 'border-inline-end-color' correspond to the [=physical=] properties 'border-top-color', 'border-bottom-color', 'border-left-color', and 'border-right-color'. The mapping depends on the element’s 'writing-mode', 'direction', and 'text-orientation'.> | < > ]{1,4}
Name: border-block-color, border-inline-color
Value: <<'border-top-color'>>{1,2}
These two shorthand properties set the
'border-block-start-color' & 'border-block-end-color'
and
'border-inline-start-color' & 'border-inline-end-color',
respectively.
The first value represents the start side color,
and the second value represents the end side color.
If only one value is given,
it applies to both the start and end sides.
Name: border-top-style, border-right-style, border-bottom-style, border-left-style, border-block-start-style, border-block-end-style, border-inline-start-style, border-inline-end-style Value: <These properties control whether a [=border=] appears, and if it does what style it's drawn in (if it is not overridden by a border image). The [=flow-relative=] properties 'border-block-start-style', 'border-block-end-style', 'border-inline-start-style', and 'border-inline-end-style' correspond to the [=physical=] properties 'border-top-style', 'border-bottom-style', 'border-left-style', and 'border-right-style'. The mapping depends on the element’s 'writing-mode', 'direction', and 'text-orientation'.> Initial: none Applies to: all elements except [=ruby base containers=] and [=ruby annotation containers=] Inherited: no Logical property group: border-style Percentages: N/A Computed value: specified keyword Animation type: discrete
Name: border-block-style, border-inline-style
Value: <<'border-top-style'>>{1,2}
These two shorthand properties set the
'border-block-start-style' & 'border-block-end-style'
and
'border-inline-start-style' & 'border-inline-end-style',
respectively.
The first value represents the start side style,
and the second value represents the end side style.
If only one value is given,
it applies to both the start and end sides.
Name: border-style
Value: <<'border-top-style'>>{1,4}
'border-style' is a [=shorthand property=] for the four physical 'border-*-style' properties.
The four values set the top, right, bottom and left border, respectively.
A missing left is the same as right,
a missing bottom is the same as top,
and a missing right is also the same as top.
This is resolved individually for each list item.
The style is specified as a <<Values have the following meanings:> = none | hidden | dotted | dashed | solid | double | groove | ridge | inset | outset
Name: border-top-width, border-right-width, border-bottom-width, border-left-width, border-block-start-width, border-block-end-width, border-inline-start-width, border-inline-end-width Value: <These properties specify the thickness of the [=border=], i.e. the border width. Where> Initial: medium Applies to: all elements except [=ruby base containers=] and [=ruby annotation containers=] Inherited: no Logical property group: border-width Percentages: N/A Computed value: absolute length, [=snapped as a border width=] Animation Type: by computed value
<> = <> | hairline | thin | medium | thick
Negative values are invalid. The thin, medium, and thick keywords are equivalent to ''1px'', ''3px'', and ''5px'', respectively. The hairline keyword is a UA-defined length, less than or equal to ''1px'' and equal to an integer number of device pixels at the default page zoom, which represents a "just visible" line. (While it can be as large as ''1px'', on many devices it will be approximately ''0.3px'' to ''0.5px''.) The [=resolved value=] for the 'border-width' properties is the [=used value=]. If the 'border-style' corresponding to a given 'border-width' is ''border-style/none'' or ''border-style/hidden'', then the [=used value|used=] width is 0. The [=flow-relative=] properties 'border-block-start-width', 'border-block-end-width', 'border-inline-start-width', and 'border-inline-end-width' correspond to the [=physical=] properties 'border-top-width', 'border-bottom-width', 'border-left-width', and 'border-right-width'. The mapping depends on the element’s 'writing-mode', 'direction', and 'text-orientation'.
Name: border-block-width, border-inline-width
Value: <<'border-top-width'>>{1,2}
These two shorthand properties set the
'border-block-start-width' & 'border-block-end-width'
and
'border-inline-start-width' & 'border-inline-end-width',
respectively.
The first value represents the start side width,
and the second value represents the end side width.
If only one value is given,
it applies to both the start and end sides.
Name: border-width
Value: <<'border-top-width'>>{1,4}
'border-width' is a [=shorthand property=] for the four physical 'border-*-width' properties.
The four values set the top, right, bottom and left border, respectively.
A missing left is the same as right,
a missing bottom is the same as top,
and a missing right is also the same as top.
This is resolved individually for each list item.
Note: Although the [=initial value|initial=] width is ''medium'',
the [=initial value|initial=] style is ''border-style/none'';
therefore the [=used value|used=] initial width is 0.
Name: border-top, border-right, border-bottom, border-left, border-block-start, border-block-end, border-inline-start, border-inline-end Value: <These [=shorthand properties=] set the 'border-width', 'border-color', and 'border-style' of one side of the [=borders=] of a box. Omitted values are set to their [=initial values=]. The [=flow-relative=] properties 'border-block-start', 'border-block-end', 'border-inline-start', and 'border-inline-end' correspond to the [=physical=] properties 'border-top', 'border-bottom', 'border-left', and 'border-right'. The mapping depends on the element’s 'writing-mode', 'direction', and 'text-orientation'.> || < > || < > Initial: See individual properties Applies to: all elements except [=ruby base containers=] and [=ruby annotation containers=] Inherited: no Percentages: N/A Computed value: see individual properties Animation Type: see individual properties
Name: border-block, border-inline Value: <<'border-block-start'>>These two shorthand properties set the 'border-block-start' & 'border-block-end' or 'border-inline-start' & 'border-inline-end', respectively, both to the same style.
Name: border Value: <> || < > || < >
p { border: solid red }
p {
border-top: solid red;
border-right: solid red;
border-bottom: solid red;
border-left: solid red;
border-image: none;
}
blockquote {
border-color: red;
border-left: double;
color: black
}
In the above example,
the color of the left border is black, while the other borders are red.
This is due to 'border-left' setting the width, style, and color.
Since the color value is not given by the 'border-left' property,
it will be taken from the 'color' property.
The fact that the 'color' property is set after the 'border-left' property
is not relevant.
p { width: 70px; height: 70px; border: solid 30px;
border-color: orange orange silver silver;
border-top-right-radius: 100%; }
|
|
2 * min(|radius|'s [=width=] / |edge|'s [=width=], |radius|'s [=height=] / |edge|'s [=height=]).
1. Let |adustedRadiusWidth| be the [=adjusted radius dimension=] given |coverage|, |radius|'s [=width=], and |outset|'s [=width=].
1. Let |adustedRadiusHeight| be the [=adjusted radius dimension=] given |coverage|, |radius|'s [=height=], and |outset|'s [=height=].
1. Return (|adustedRadiusWidth|, |adustedRadiusHeight|).
To compute the adjusted radius dimension given numbers |coverage|, |radius|, and |outset|:
1. If |radius| is greater than |spread|, or if |coverage| is greater than 1, then return |radius| + |outset|.
1. Let |ratio| be |radius| / |outset|.
1. Return |radius| + |outset| * (1 - (1 - |ratio|)3 * (1 - |coverage|3)).
Note: this algorithm is designed to reduce the effect of the |outset| (or spread) on the shape of the corner.
The |coverage| factor makes this reduction more pronounced for rectangular shapes (where the [=border radius=] is close to 0),
and less pronounced for elliptical shapes (where the [=border radius=] is close to 50%).
DIV {
background: black;
color: white;
border-radius: 1em;
padding: 1em }
box-sizing: border-box; width: 6em; height: 2.5em; border-radius: 0.5em 2em 0.5em 2emThe height (2.5em) is enough for the specified radii (0.5em plus 2.0em). However, if the height is only 2em,
box-sizing: border-box; width: 6em; height: 2em; border-radius: 0.5em 2em 0.5em 2emall corners need to be reduced by a factor 0.8 to make them fit. The used border radii thus are 0.4em (instead of 0.5em) and 1.6em (instead of 2em). See borders B in the figure.
Name: border-top-left-radius, border-top-right-radius, border-bottom-right-radius, border-bottom-left-radius, border-start-start-radius, border-start-end-radius, border-end-start-radius, border-end-end-radius Value: <The radius is specified as a <> Initial: 0 Applies to: all elements (but see prose) Inherited: no Logical property group: border-radius Percentages: Refer to corresponding dimension of the border box. Computed value: pair of computed < > values Animation Type: by computed value
<The two <> = <> | < > < > = <> [ / < > ]? < > = <>{1,2}
DIV.standout {
width: 13em;
height: 8em;
border: solid black 1em;
border-radius: 7.5em 5em }
Name: border-top-radius, border-right-radius, border-bottom-radius, border-left-radius, border-block-start-radius, border-block-end-radius, border-inline-start-radius, border-inline-end-radius Value: <>{1,2} [ / < >{1,2} ]? Initial: 0 Applies to: all elements (but see prose) Inherited: no Percentages: Refer to corresponding dimension of the border box. Computed value: see individual properties Animation type: see individual properties
The 'border-*-radius' shorthands set the two 'border-*-*-radius'
longhand properties of the related side. If values are given before
and after the slash, then the values before the slash set the
horizontal radius and the values after the slash set the vertical radius.
If there is no slash, then the values set both radii equally.
The two values for the radii are given in the order
top-left, top-right for 'border-top-radius',
top-right, bottom-right for 'border-right-radius',
bottom-left, bottom-right for 'border-bottom-radius',
top-left, bottom-left for 'border-left-radius',
start-start, start-end for 'border-block-start-radius',
end-start, end-end for 'border-block-end-radius'
start-start, end-start for 'border-inline-start-radius',
and start-end, end-end for 'border-inline-end-radius'.
If the second value is omitted it is copied from the first.
Name: border-radius Value: <The 'border-radius' shorthand sets all four 'border-*-radius' properties. If values are given before and after the slash, then the values before the slash set the horizontal radii and the values after the slash set the vertical radii. If there is no slash, then the values set the radii in both axes equally. The four values for each radii are given in the order top-left, top-right, bottom-right, bottom-left. If bottom-left is omitted it is the same as top-right. If bottom-right is omitted it is the same as top-left. If top-right is omitted it is the same as top-left.>{1,4} [ / < >{1,4} ]?
border-radius: 4em;is equivalent to
border-top-left-radius: 4em; border-top-right-radius: 4em; border-bottom-right-radius: 4em; border-bottom-left-radius: 4em;and
border-radius: 2em 1em 4em / 0.5em 3em;is equivalent to
border-top-left-radius: 2em 0.5em; border-top-right-radius: 1em 3em; border-bottom-right-radius: 4em 0.5em; border-bottom-left-radius: 1em 3em;
Name: corner-top-left-shape, corner-top-right-shape, corner-bottom-right-shape, corner-bottom-left-shape, corner-start-start-shape, corner-start-end-shape, corner-end-start-shape, corner-end-end-shape Value: <The 'corner-*-*-shape' longhand properties set the corner shape for the given corner. The [=flow-relative=] longhands ('corner-start-start-shape', etc.) correspond to the [=physical=] longhands ('corner-top-left-shape', etc.) depending on the element’s 'writing-mode', 'direction', and 'text-orientation'. The first> Initial: round Applies to: all elements where 'border-radius' can apply Inherited: no Logical property group: corner-shape Computed value: the corresponding ''superellipse()'' value Animation Type: see [=superellipse interpolation=]
<> =''corner-shape/round'' |''corner-shape/scoop'' |''corner-shape/bevel'' |''corner-shape/notch'' |''corner-shape/square'' |''corner-shape/squircle'' | <> superellipse() = superellipse(< > | infinity | -infinity)
Name: corner-top-shape, corner-right-shape, corner-bottom-shape, corner-left-shape,
corner-block-start-shape, corner-block-end-shape, corner-inline-start-shape, corner-inline-end-shape
Value: <<'corner-top-left-shape'>>{1,2}
The 'corner-*-shape' shorthands set the two 'corner-*-*-shape'
properties of the related side.
If only one value is given,
the second value defaults to the same value.
For the physical shorthands ('corner-top-shape', etc.),
the values are either in left/right order, or top/bottom order,
whichever axis is meaningful for the property.
That is, ''corner-top-shape: round square''
sets ''corner-top-left-shape: round; corner-top-right-shape: square;''.
For the logical shorthands ('corner-block-start-shape', etc.),
the values are always in start/end order in the other axis.
That is, ''corner-block-start-shape: round square''
sets ''corner-start-start-shape: round; corner-start-end-shape: square;''.
Name: corner-shape
Value: <<'corner-top-left-shape'>>{1,4}
Initial: round
Applies to: all elements where 'border-radius' can apply
Inherited: no
Animation type: see individual properties
The 'corner-shape' property specifies the shape of the box's corners,
within the region specified by 'border-radius'.
The four values set the top, right, bottom and left shape, respectively.
A missing left is the same as right,
a missing bottom is the same as top,
and a missing right is also the same as top.
Name: corner-top-left, corner-top-right, corner-bottom-left, corner-bottom-right, corner-start-start, corner-start-end, corner-end-start, corner-end-end Value: <<'border-top-left-radius'>> || <<'corner-top-left-shape'>> Initial: 0 Applies to: all elements (but see prose) Inherited: no Percentages: Refer to corresponding dimension of the border box. Computed value: see individual properties Animation type: see individual propertiesThe 'corner-*-*' shorthands set the two 'corner-*-*-shape' and 'border-*-*-radius' longhand properties of the related side. See the corresponding 'corner-*-*-shape' and 'border-*-*-radius' properties for further details.
Name: corner-top, corner-right, corner-bottom, corner-left, corner-block-start, corner-block-end, corner-inline-start, corner-inline-end Value: <<'border-top-radius'>> || <<'corner-top-shape'>> Initial: 0 Applies to: all elements (but see prose) Inherited: no Percentages: Refer to corresponding dimension of the border box. Computed value: see individual properties Animation type: see individual propertiesThe 'corner-*' shorthands set the two 'corner-*-shape' longhand properties and two 'border-*-radius' longhand properties of the related side. See the corresponding 'corner-*-shape' and 'border-*-radius' properties for further details.
Name: corner Value: <<'border-radius'>> || <<'corner-shape'>> Initial: 0 Applies to: all elements (but see prose) Inherited: no Percentages: Refer to corresponding dimension of the border box. Computed value: see individual properties Animation type: see individual propertiesThe 'corner' shorthand sets the 'corner-*-shape' and 'border-*-radius' longhands all together.
-|outset| in all directions.
(|v|[0] ⋅ |factor|, |v|[1] ⋅ |factor|).
hypot(|v|[0], |v|[1]).
1. If |l| is 0, return |v|.
1. Return (|v|[0] / |l|, |v|[1] / |l|).
(-|v|[1], |v|[0]).
The vector between two points, given {{DOMPoint}}s |a| and |b|, is (|b|'s {{DOMPointReadOnly/x}} - |a|'s {{DOMPointReadOnly/x}}, |b|'s {{DOMPointReadOnly/y}} - |a|'s {{DOMPointReadOnly/y}}).
(|radius|'s [=width=] ⋅ |scaleFactor|, |radius|'s [=height=] ⋅ |scaleFactor|).
1. Let |adjustedRadiusInOutsetCoordinates| be the [=outset-adjusted border radius=] given |borderRect|'s size, |radius|, and (-|insetX|, -|insetY|).
1. Return (|adjustedRadiusInOutsetCoordinates|'s [=width=] - |insetX|, |adjustedRadiusInOutsetCoordinates|'s [=height=] - |insetY|).
1. Let |adjustedTopRightRadius| be the |adjustedRadius| given 'border-top-right-radius', |insetRight|, and |insetTop|.
1. Let |adjustedBottomRightRadius| be the |adjustedRadius| given 'border-bottom-right-radius', |insetRight|, and |insetBottom|.
1. Let |adjustedBottomLeftRadius| be the |adjustedRadius| given 'border-bottom-left-radius', |insetLeft|, and |insetBottom|.
1. Let |adjustedTopLeftRadius| be the |adjustedRadius| given 'border-top-left-radius', |insetLeft|, and |insetTop|.
1. Clip out from |path|, the [=border-aligned corner clip-out path=] given
|borderRect|'s right, |borderRect|'s top,
(-|adjustedTopRightRadius|[0], 0), (0, |adjustedTopRightRadius|[1]),
|element|'s [=computed value|computed=] 'corner-top-right-shape',
|topInset|, and |rightInset|.
1. Clip out from |path|, the [=border-aligned corner clip-out path=] given
|borderRect|'s right, |borderRect|'s bottom,
(0, -|adjustedBottomRightRadius|[1]), (-|adjustedBottomRightRadius|[0], 0),
|element|'s [=computed value|computed=] 'corner-bottom-right-shape',
|rightInset|, and |bottomInset|.
1. Clip out from |path|, the [=border-aligned corner clip-out path=] given
|borderRect|'s left, |borderRect|'s bottom,
(|adjustedBottomLeftRadius|[0], 0), (0, -|adjustedBottomLeftRadius|[1]),
|element|'s [=computed value|computed=] 'corner-bottom-left-shape',
|bottomInset|, and |leftInset|.
1. Clip out from |path|, the [=border-aligned corner clip-out path=] given
|borderRect|'s left, |borderRect|'s top,
(0, |adjustedTopLeftRadius|[1]), (|adjustedTopLeftRadius|[0], 0),
|element|'s [=computed value|computed=] 'corner-top-left-shape',
|leftInset|, and |topInset|.
1. Return |path|.
To get the border-aligned corner clip-out path given a {{DOMPointReadOnly}} |originalCornerOuter|, a [=two-dimensional vector=] |vectorTowardsStart|, a [=two-dimensional vector=] |vectorTowardsEnd|, a [=superellipse parameter=] |curvature|, and numbers |startInset| and |endInset|:
1. If |curvature| is ∞, then return an empty path.
1. Let |clampedHalfCorner| be the [=normalized superellipse half corner=] given clamp(|curvature|, -1, 1).
1. Let |originalCornerStart| be |originalCornerOuter|, [=point extended by vectors|extended by=] « |vectorTowardsStart| ».
1. Let |originalCornerEnd| be |originalCornerOuter|, [=point extended by vectors|extended by=] « |vectorTowardsEnd| ».
1. Let |originalCornerCenter| be |originalCornerOuter|, [=point extended by vectors|extended by=] « |vectorTowardsStart|, |vectorTowardsEnd| ».
1. Let |extendStart| be a [=vector between two points|vector between=] |originalCornerStart| and |originalCornerCenter|, [=two-dimensional vector/normalize|normalized=] and [=two-dimensional vector/scale|scaled by=] |startInset|.
1. Let |extendEnd| be a [=vector between two points|vector between=] |originalCornerEnd| and |originalCornerCenter|, [=two-dimensional vector/normalize|normalized=] and [=two-dimensional vector/scale|scaled by=] |endInset|.
1. Let |clipStart| be |originalCornerStart|, [=point extended by vectors|extended by=] « |extendStart| ».
1. Let |clipEnd| be |originalCornerEnd|, [=point extended by vectors|extended by=] « |extendEnd| ».
1. Let |clipOuter| be |originalCornerOuter|, [=point extended by vectors|extended by=] « |extendStart|, |extendEnd| ».
1. Let |vectorFromStartToControlPoint| be the the [=two-dimensional vector=] (2 ⋅ |clampedHalfCorner| - 0.5, 1.5 - 2 ⋅ |clampedHalfCorner|).
1. Let |singlePixelVectorFromStartToControlPoint| be the |vectorFromStartToControlPoint|, [=two-dimensional vector/normalize|normalized=].
1. Let |strokeA|, |strokeB| be the [=two-dimensional vector/perpendicular=] of |singlePixelVectorFromStartToControlPoint|.
1. Let |offset1| be [=vector between two points|the vector between=] |originalCornerStart| and |outerCorner|, [=two-dimensional vector/normalize|normalized=] and [=two-dimensional vector/scale|scaled by=] |startInset| ⋅ |strokeA|.
1. Let |offset2| be [=vector between two points|the vector between=] |outerCorner| and |originalCornerEnd|, [=two-dimensional vector/normalize|normalized=] and [=two-dimensional vector/scale|scaled by=] |startInset| ⋅ |strokeB|.
1. Let |offset3| be [=vector between two points|the vector between=] |originalCornerEnd| and |originalCornerCenter|, [=two-dimensional vector/normalize|normalized=] and [=two-dimensional vector/scale|scaled by=] |endInset| ⋅ |strokeB|.
1. Let |offset4| be [=vector between two points|the vector between=] |originalCornerCenter| and |originalCornerStart|, [=two-dimensional vector/normalize|normalized=] and [=two-dimensional vector/scale|scaled by=] |endInset| ⋅ |strokeA|.
1. Let |adjustedCornerStart| be |originalCornerStart|, [=point extended by vectors|extended by=] « |offset1|, |offset2| ».
1. Let |adjustedCornerEnd| be |originalCornerEnd|, [=point extended by vectors|extended by=] « |offset3|, |offset4| ».
1. Let |adjustedCornerCenter| be |originalCornerCenter|, [=point extended by vectors|extended by=] « |offset4|, |offset1| ».
1. Let |adjustedCornerOuter| be |originalCornerOuter|, [=point extended by vectors|extended by=] « |offset2|, |offset3| ».
1. Let |curveCenter| be |adjustedCornerOuter| if |curvature| is less than 0, |adjustedCornerCenter| otherwise.
1. Let |mapPointToCorner| be the following steps: given numbers |x| and |y|:
1. Let |v1| be [=vector between two points|the vector between=] |curveCenter| and |adjustedCornerEnd|, [=two-dimensional vector/scale|scaled by=] |x|.
1. Let |v2| be [=vector between two points|the vector between=] |curveCenter| and |adjustedCornerStart|, [=two-dimensional vector/scale|scaled by=] |y|.
1. Return the {{DOMPointReadOnly}} at (|x|, |y|), [=point extended by vectors|extended by=] « |v1|, |v2| ».
1. Let |controlPoint| be the result of calling |mapPointToCorner| given |vectorFromStartToControlPoint|'s {{DOMPointReadOnly/y}} and 1 - |unitVectorFromStartToControlPoint|'s {{DOMPointReadOnly/x}}.
1. Let |axisAlignedCornerStart| be the intersection between the lines (|adjustedCornerStart|, |controlPoint|) and (|clipStart|, |clipOuter|). If the lines are parallel, let it be adjustedCornerStart.
1. Let |axisAlignedCornerEnd| be the intersection between the lines (|adjustedCornerEnd|, |controlPoint|) and (|clipEnd|, |clipOuter|). If the lines are parallel, let it be adjustedCornerEnd.
Note: |axisAlignedCornerStart| and |axisAlignedCornerEnd| act as "miters" when rendering an outset.
They are a straight extension of the curve's tangent, intersecting with the unshaped target rect.
1. Let |path| be a path, starting at |axisAlignedCornerStart|.
1. If |curvature| is -∞:
1. Extend |path| to |adjustedCornerStart|.
1. Extend |path| to |adjustedCornerCenter|.
1. Extend |path| to |adjustedCornerEnd|.
1. Otherwise:
1. Let |K| be 0.5-abs(|curvature|).
1. For every |T| between 0 and 1, in an [=implementation-approximated=] manner,
extend |path| to the result of calling |mapPointToCorner| given |T||K| and (1 - |T|)|K|.
1. Extend |path| to |axisAlignedCornerEnd|.
1. Extend |path| to |clipOuter|.
1. Return |path|.
The following example would create overlapping corners if not constrained.
div {
corner-shape: scoop;
border-top-left-radius: 80%;
border-bottom-right-radius: 80%;
}
min(1, |scaleFactorA|, |scaleFactorB|).
log2, interpolating it linearly would result in an effect where concave corners interpolate at a much higher velocity than convex corners.
To balance that, the superellipse interpolation formula describes how a [=superellipse parameter=] is converted to a value between 0 and 1, and vice versa:
0.5abs(|s|).
1. Let |convexHalfCorner| be 0.51/|k|.
1. If |s| is less than 0, return 1 - |convexHalfCorner|.
1. Return |convexHalfCorner|.
(1, 0) and (1, 1).
1. Let |axisLineB| be a line between (0, 1) and (1, 1).
1. Let |normalizedHalfCorner| be the [=normalized superellipse half corner=] given |curvature|.
1. Let |halfCornerPoint| be (|normalizedHalfCorner|, 1 - |normalizedHalfCorner|).
1. Let |lineFromCenterToHalfCorner| be a line between (0, 0) and |halfCornerPoint|.
1. Let |tangentLine| be the line perpendicular to |lineFromCenterToHalfCorner|, at |halfCornerPoint|.
1. Let |intersectionA| be the intersection between |axisLineA| and |tangentLine|.
1. Let |intersectionB| be the intersection between |axisLineB| and |tangentLine|.
1. Return a pentagon between the points « (1, 1), (1, 0), |intersectionA|, |intersectionB|, (0, 1), (1, 1) ».
ln(0.5) / ln(|convexHalfCorner|).
1. Let |s| be log2(|k|).
1. If |interpolationValue| is less than 0.5, return -|s|.
1. Return |s|.
The image is 81 by 81 pixels and has to be divided into 9 equal parts.
The style rules could thus be as follows:
DIV {
border: double orange 1em;
border-image: url("border.png") 27 round stretch;
}
The result, when applied to a DIV of 12 by 5em,
will be similar to this:
.notebox {
border: double orange;
/* must set 'border' shorthand first, otherwise it erases 'border-image' */
border-image: url("border.png") 30 round;
/* but other 'border' properties can be set after */
border-width: thin thick;
}
...
.sidebar .notebox {
box-shadow: 0 0 5px gray;
border-radius: 5px;
border: none; /* turn off all borders */
/* 'border' shorthand resets 'border-image' */
}
Name: border-image-source Value: none | <> Initial: none Applies to: All elements, except internal table elements when 'border-collapse' is ''border-collapse/collapse'' Inherited: no Percentages: N/A Computed value: the keyword ''border-image-source/none'' or the computed < > Animation type: discrete
Name: border-image-slice Value: [<> | < >]{1,4} && fill? Initial: 100% Applies to: All elements, except internal table elements when 'border-collapse' is ''border-collapse/collapse'' Inherited: no Percentages: refer to size of the border image Computed value: four values, each either a number or percentage; plus a ''fill'' keyword if specified Animation type: by computed value
Name: border-image-width Value: [ <> | < > | auto ]{1,4} Initial: 1 Applies to: All elements, except internal table elements when 'border-collapse' is ''border-collapse/collapse'' Inherited: no Percentages: Relative to width/height of the [=border image area=] Computed value: four values, each either a number, the keyword ''border-image-width/auto'', or a computed < > value Animation type: by computed value
Name: border-image-outset Value: [ <> | < > ]{1,4} Initial: 0 Applies to: All elements, except internal table elements when 'border-collapse' is ''border-collapse/collapse'' Inherited: no Percentages: N/A Computed value: four values, each a number or absolute length Animation type: by computed value
Name: border-image-repeat
Value: [ stretch | repeat | round | space ]{1,2}
Initial: stretch
Applies to: All elements, except internal table elements when 'border-collapse' is ''border-collapse/collapse''
Inherited: no
Percentages: N/A
Computed value: two keywords, one per axis
Animation type: discrete
Name: border-image Value: <<'border-image-source'>> || <<'border-image-slice'>> [ / <<'border-image-width'>> | / <<'border-image-width'>>? / <<'border-image-outset'>> ]? || <<'border-image-repeat'>> Initial: See individual properties Applies to: See individual properties Inherited: no Percentages: N/A Computed value: See individual properties Animation Type: See individual properties
This section is not yet ready for implementation. It exists in this repository to record the ideas and promote discussion.
Before attempting to implement anything of this section, please contact the CSSWG at www-style@w3.org.
Name: border-limit Value: all | [ sides | corners ] <>? | [ top | right | bottom | left ] < > Initial: all Applies to: all elements, except table element when 'border-collapse' is ''collapse'' Inherited: no Percentages: relative to border-box Animation type: discrete
By default, the entire border is drawn. However, border rendering can be limited to only part of a border. The keyword specifies which part, and the length or percentage specifies how much.
The following example draws only the middle 50% of the sides.
div {
border: solid;
border-limit: sides 50%;
}
The following example draws only the curved parts of the corners.
div {
border: solid;
border-radius: 1em 2em;
border-limit: corners;
}
The following example draws only the left 4em of the top border.
div {
border-top: solid;
border-limit: left 4em;
}
The following example draws only the first 10px of each corner:
div {
border: solid;
border-limit: corners 10px;
}
The following example draws the curved part of the corner plus 5px along the sides:
div {
border: solid;
border-radius: 5px;
border-limit: corners 5px;
}
The following example draws the curved part of the corner and all of the side except the middle 40%.
div {
border: solid;
border-radius: 5px;
border-limit: corners 30%;
}
Name: border-top-clip, border-right-clip, border-bottom-clip, border-left-clip, border-block-start-clip, border-block-end-clip, border-inline-start-clip, border-inline-end-clip Value: none | [ <> | < > ]+ Initial: none Inherited: no Logical property group: border-clip Percentages: refer to length of border-edge side Computed value: ''border-top-clip/none'', or a list consisting of absolute lengths, or percentages as specified Animation type: by computed value
These properties split their respective borders into parts along the border edge. The first part is visible, the second is invisible, the third part is visible, etc. Parts can be specified with lengths, percentages, or flexible lengths (expressed by the ''fr'' unit, as per [[CSS3GRID]]). The ''border-top-clip/none'' value means that the border is not split, but shown normally. The [=flow-relative=] longhands ('border-block-start-clip', etc.) correspond to the [=physical=] longhands ('border-top-clip', etc.) depending on the element’s 'writing-mode', 'direction', and 'text-orientation'.
Name: border-block-clip, border-inline-clip Value: <<'border-top-clip'>>These two [=shorthand properties=] set the 'border-block-start-clip' & 'border-block-end-clip' and 'border-inline-start-clip' & 'border-inline-end-clip', respectively.
Name: border-clip Value: <<'border-top-clip'>>'border-clip' is a [=shorthand property=] for the longhand properties, setting all four sides to the same value. If the listed parts are shorter than the border, any remaining border is split proportionally between the specified flexible lengths. If there are no flexible lengths, the behavior is as if ''1fr'' had been specified at the end of the list. If the listed parts are longer than the border, the specified parts will be shown in full until the end of the border. In this case, all flexible lengths will be zero. For horizontal borders, parts are listed from left to right. For vertical borders, parts are listed from top to bottom. The exact border parts are determined by laying out the specified border parts with all flexible lengths initially set to zero. Any remaining border is split proportionally between the flexible lengths specified.
border-clip: 10px 1fr 10px;
border-top-clip: 10px 1fr 10px; border-bottom-clip: 10px 1fr 10px; border-right-clip: 5px 1fr 5px; border-left-clip: 5px 1fr 5px;
By making the first part have zero length, the inverse border of the previous example can easily be created:
border-top-clip: 0 10px 1fr 10px; border-bottom-clip: 0 10px 1fr 10px; border-right-clip: 0 5px 1fr 5px; border-left-clip: 0 5px 1fr 5px;
border: thin solid black; border-clip: 0 1fr; /* hide borders */ border-top-clip: 10px 1fr 10px; /* make certain borders visible */ border-bottom-clip: 10px 1fr 10px;
border-top: thin solid black; border-bottom: thin solid black; border-top-clip: 10px; border-bottom-clip: 10px;
border-top: thin solid black; border-clip: 10px;
This rendering:
A sentence consists of words¹.
¹ Most often.
@footnote {
border-top: thin solid black;
border-clip: 4em;
}
border: 4px solid black; border-top-clip: 40px 20px 0 1fr 20px 20px 0 1fr 40px;
In this example, there will be a visible 40px border part on each end of the top border. Inside the 40px border parts, there will be an invisible border part of at least 20px. Inside these invisible border parts, there will be visible border parts, each 20px long with 20px invisible border parts between them.
The fragments are shown in red for illustrative purposes; they should not be visible in compliant UAs.
border: 4px solid black; border-top-clip: 3fr 10px 2fr 10px 1fr 10px 10px 10px 1fr 10px 2fr 10px 3fr;
All but one of the visible border parts are represented as flexible lengths in this example. The length of these border parts will change when the width of the element changes. Here is one rendering where 1fr ends up being 10px:
Here is another rendering where 1fr ends up being 30px:
The fragments are shown in red for illustrative purposes; they should be black in compliant UAs.
Name: box-shadow-color Value: <># Initial: currentcolor Applies to: all elements Inherited: no Percentages: N/A Computed value: list, each item a computed color Animation type: by computed value
The 'box-shadow-color' property defines one or more drop shadow colors. The property accepts a comma-separated list of shadow colors.
See the section [[css-backgrounds-3#shadow-layers|“Layering, Layout, and
Other Details”]] for how 'box-shadow-color' interacts with other
comma-separated drop shadow properties to form each drop shadow
layer.
Name: box-shadow-offset Value: [ none | <>{1,2} ]# Initial: none Applies to: all elements Inherited: no Percentages: N/A Computed value: list, each item either ''box-shadow-offset/none'' or a pair of offsets (horizontal and vertical) from the element‘s box Animation type: by computed value, treating ''box-shadow-offset/none'' as ''0 0'' when interpolated with non-''box-shadow-offset/none'' values.
The 'box-shadow-offset' property defines one or more drop shadow offsets.
The property accepts a comma-separated list.
Each item in that list can either be the ''box-shadow-offset/none'' value,
which indicates no shadow,
a pair of < See the section [[css-backgrounds-3#shadow-layers|“Layering, Layout, and
Other Details”]] for how 'box-shadow-offset' interacts with other
comma-separated drop shadow properties to form each drop shadow
layer.
The 'box-shadow-blur' property defines one or more blur radii for drop shadows.
The property accepts a comma-separated list of < Negative values are invalid.
If the blur value is zero, the shadow’s edge is sharp.
Otherwise, the larger the value, the more the shadow’s edge is blurred.
See [[css-backgrounds-3#shadow-blur|Shadow Blurring]], below.
See the section [[css-backgrounds-3#shadow-layers|“Layering, Layout, and
Other Details”]] for how 'box-shadow-blur' interacts with other
comma-separated drop shadow properties to form each drop shadow
layer.
The 'box-shadow-spread' property defines one or more spread distances for drop shadows.
The property accepts a comma-separated list of < Positive values cause the shadow to expand in all directions by the specified radius.
Negative values cause the shadow to contract.
See [[css-backgrounds-3#shadow-shape|Shadow Shape]], below.
Note that for inner shadows,
expanding the shadow (creating more shadow area)
means contracting the shadow’s perimeter shape.
See the section [[css-backgrounds-3#shadow-layers|“Layering, Layout, and
Other Details”]] for how 'box-shadow-spread' interacts with other
comma-separated drop shadow properties to form each drop shadow
layer.
The 'box-shadow-position' property defines one or more drop shadow positions.
The property accepts a comma-separated list of ''box-shadow-position/outset'' and ''box-shadow-position/inset'' keywords.
See the section [[css-backgrounds-3#shadow-layers|“Layering, Layout, and
Other Details”]] for how 'box-shadow-position' interacts with other
comma-separated drop shadow properties to form each drop shadow
layer.
The 'box-shadow' property attaches one or more drop-shadows to the box.
The property accepts a comma-separated list of shadows,
ordered front to back.
Each shadow is given as a < In addition to the many contributors to the [[CSS1]], [[CSS2]],
and [[CSS3BG]] predecessors to this module,
the editors would like to thank
Tab Atkins,
Noam Rosenthal,
Håkon Wium Lie,
Oriol Brufau,
and Guillaume Lebas
for their suggestions and feedback specifically for this Level 4.
Blurring shadows: the 'box-shadow-blur' property
Name: box-shadow-blur
Value: <
Spreading shadows: the 'box-shadow-spread' property
Name: box-shadow-spread
Value: <
Spreading shadows: the 'box-shadow-position' property
Name: box-shadow-position
Value: [ outset | inset ]#
Initial: outset
Applies to: all elements
Inherited: no
Percentages: N/A
Computed value: list, each item one of the keywords
Animation type: by computed value
Drop Shadows Shorthand: the 'box-shadow' property
Name: box-shadow
Value: <
<
width: 100px; height: 100px;
border: 12px solid blue; background-color: orange;
border-top-left-radius: 60px 90px;
border-bottom-right-radius: 60px 90px;
box-shadow: 64px 64px 12px 40px rgba(0,0,0,0.4),
12px 12px 0px 8px rgba(0,0,0,0.4) inset;
Shadow Shape, Spread, and Knockout
An [=outer box-shadow=] casts a shadow
as if the border-box of the element were opaque.
Assuming a spread distance of zero, its perimeter has
the exact same size and shape as the border box.
The shadow is drawn outside the border edge only:
it is clipped inside the border-box of the element.
An [=inner box-shadow=] casts a shadow
as if everything outside the padding edge were opaque.
Assuming a spread distance of zero, its perimeter has
the exact same size and shape as the padding box.
The shadow is drawn inside the padding edge only:
it is clipped outside the padding box of the element.
If a [=spread distance=] is defined,
the shadow perimeter defined above
is expanded outward (for [=outer box-shadows=])
or contracted inward (for [=inner box-shadows=])
by outsetting (insetting, for inner shadows)
the shadow's straight edges by the [=spread distance=]
(and flooring the resulting width/height at zero).
border:5px solid blue;
background-color:orange;
width: 144px;
height: 144px;
border-radius: 20px;
border-radius: 0;
box-shadow:
rgba(0,0,0,0.4)
10px 10px;
box-shadow:
rgba(0,0,0,0.4)
10px 10px
inset
box-shadow:
rgba(0,0,0,0.4)
10px 10px 0
10px /* spread */
box-shadow:
rgba(0,0,0,0.4)
10px 10px 0
10px /* spread */
inset
Blurring Shadow Edges
A non-zero [=blur radius=] indicates
that the resulting shadow should be blurred,
such as by a Gaussian filter.
The exact algorithm is not defined;
however the resulting shadow must approximate
(with each pixel being within 5% of its expected value)
the image that would be generated by applying to the shadow
a Gaussian blur with a standard deviation equal to half the blur radius.
Note: This means for a long, straight shadow edge,
the blur radius will create a visibly apparent color transition
approximately the twice length of the blur radius
that is perpendicular to and centered on the shadow's edge,
and that ranges
from almost the full shadow color at the endpoint inside the shadow
to almost fully transparent at the endpoint outside it.
Layering, Layout, and Other Details
Drop shadows are declared in the [=coordinated value list=]
constructed from the 'box-shadow-*' properties,
which form a [=coordinating list property group=]
with 'box-shadow-offset' as the [=coordinating list base property=].
See [[css-values-4#linked-properties]].
The shadow effects are applied front-to-back:
the first shadow is on top and the others are layered behind.
Shadows do not influence layout and may overlap (or be overlapped by)
other boxes and text or their shadows.
In terms of stacking contexts and the painting order,
the outer box-shadows of an element are drawn immediately below the background of that element,
and the inner shadows of an element are drawn immediately above the background of that element
(below the borders and border image, if any).
Unless otherwise specified, drop shadows are only applied to the [=principal box=].
If the affected box has multiple fragments,
the shadows are applied as specified in 'box-decoration-break'.
Shadows do not trigger scrolling or increase the size of the scrollable area.
Outer shadows have no effect on internal table elements in the collapsing border model.
If a shadow is defined for single border edge in the collapsing border model
that has multiple border thicknesses
(e.g. an outer shadow on a table where one row has thicker borders than the others,
or an inner shadow on a rowspanning table cell that adjoins cells with different border thicknesses),
the exact position and rendering of its shadows are undefined.
Border Shaping
While 'corner-shape' and 'border-radius' allow some expressiveness to styling a border,
they still work with the assumption that the border is rectangular.
The 'border-shape' property augments these capabilities,
by enabling the author to use any [=basic shape=] to specify the path of the border.
The 'border-shape' property
Name: border-shape
Value: none | [ <
The 'border-shape' property defines custom shapes for element borders
using one or two [=basic shape=] values.
Syntax and Usage Modes
The 'border-shape' property accepts either a single <
Geometry Box References
When a <
Examples
.circular-button {
width: 100px;
height: 100px;
border: 5px solid blue;
border-shape: circle(50%);
}
This creates a circular border with a 5px stroke width.
.diamond {
width: 200px;
height: 200px;
border: 10px solid crimson;
border-shape:
polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%)
polygon(50% 10%, 90% 50%, 50% 90%, 10% 50%);
}
This creates a diamond border by defining outer and inner polygons.
.custom-shape {
border: 8px solid green;
border-shape: path('M 10,30 A 20,20 0,0,1 50,30 A 20,20 0,0,1 90,30 Q 90,60 50,90 Q 10,60 10,30 z');
}
This creates a heart-shaped border using an SVG path.
.fancy-box {
width: 100px;
height: 100px;
border-top-width: 10px;
border-right-width: 5px;
border-bottom-width: 10px;
border-left-width: 5px;
border-style: solid;
border-color: purple;
border-shape: ellipse(50% 40%);
}
The [=relevant side for border shape=] determines which border width is used for the stroke.
Interactions with Other Properties
Interaction with 'border-radius' and 'corner-shape'
The 'border-shape' property is not compatible with 'border-radius' and 'corner-shape'.
When an element's [=computed value=] of 'border-shape' is not
Interaction with 'box-shadow'
An [=outer box-shadow=] is cast as if the shape defined by the outer path were opaque.
An [=inner box-shadow=] is cast as if everything outside the shape defined by the inner path were opaque.
For both inner and outer shadows, the shadow shape is expanded or contracted by the spread distance,
blurred by the blur radius, and then clipped by the 'border-shape', adhering to the standard 'box-shadow' painting rules.
.shaped-shadow {
border-shape: circle(50%);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
}
The shadow follows the circular shape rather than a rectangular box.
Interaction with Overflow and Clipping
The inner 'border-shape' clips the [=overflow=] content of the element,
in the same manner as 'border-radius',
as described in corner clipping.
Issue: how should this affect clipping replaced elements?
Interaction with Layout
'border-shape' does not affect geometry or layout,
which is still computed using the existing 'border-width' properties.
The shaped border is purely a visual effect.
'border-shape' does not affect the flow of content inside the box.
Note: An author can use 'border-shape' in tandem with 'shape-inside'
to create effects that both decorate the box and control its text flow.
Determining the Relevant Side
Privacy Considerations
No new privacy considerations have been reported on this specification.
Security Considerations
No new security considerations have been reported on this specification.
Changes
Changes since the
First Public Working Draft of 22 July 2025
* Added 'corner-*' shorthands
* Renamed corners to 'corner'
* Added Web Platform Tests coverage
* Incorporated full text of [[CSS3BG]] related to borders and shadows
* Renamed 'border-clip-*' properties to 'border-*-clip' and added logical longhands and shorthands
* Renamed
Additions since [[CSS3BG]]
* <
Acknowledgments