Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
196 changes: 164 additions & 32 deletions css-borders-4/Overview.bs
Original file line number Diff line number Diff line change
Expand Up @@ -3148,7 +3148,7 @@ Border Shaping</h2>
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' function augments these capabilities,
The 'border-shape' property augments these capabilities,
by enabling the author to use any [=basic shape=] to specify the path of the border.

<h3 id="border-shape-func">
Expand All @@ -3165,49 +3165,181 @@ The 'border-shape' property</h3>
Animation type: by computed value
</pre>

The 'border-shape' property is provided with either a single <<basic-shape>> or two <<basic-shape>>s,
resulting in one or two paths, respectively.
The 'border-shape' property defines custom shapes for element borders
using one or two [=basic shape=] values.

When two <<basic-shape>> values are given, the border is rendered as the shape between the two paths.
When only a single <<basic-shape>> is given, the border is rendered as a stroke with the
[=relevant side for border shape|relevant side=]'s [=computed value|computed=] [=border width=] as the stroke width.
<h3 id="border-shape-syntax">
Syntax and Usage Modes</h3>

The fill color of the border is the [=relevant side for border shape|relevant side=]'s [=computed value|computed=]'border-color'.
The 'border-shape' property accepts either a single <<basic-shape>> or two <<basic-shape>>s:

When a <<geometry-box>> is not given, the default computation of percentage differs based on the number of given <<basic-shape>> values.
: Single <<basic-shape>> (Stroke mode)
:: The border is rendered as a stroke along the shape's path,
with the stroke width determined by the
[=relevant side for border shape|relevant side=]'s [=computed value|computed=] [=border width=].
This mode is useful for creating outlined shapes.

When two <<basic-shape>> values are given, the first (outer) one defaults to the [=border box=] and the second (inner) one defaults to the [=padding box=].
This allows using the different 'border-width' properties to affect the final shape.
: Two <<basic-shape>>s (Fill mode)
:: The border is rendered as the area between the two paths.
The first shape defines the outer boundary,
and the second shape defines the inner boundary.
This mode provides precise control over the border region's geometry.
The fill color of the border is the [=relevant side for border shape|relevant side=]'s [=computed value|computed=] 'border-color'.

When a single <<basic-shape>> value is given, the <<geometry-box>> defaults to the ''half-border-box'' value, which allows stroking in a way that matches the default border behavior.
<h3 id="border-shape-geometry-box">
Geometry Box References</h3>

The 'border-shape' property is not compatible with 'border-radius' and 'corner-shape'.
When an element's [=computed value=] of 'border-shape' is not <css>none</css>,
its 'border-radius' is ignored, as if it was set to 0.
'corner-shape' is implicitly ignored, as it can only work in tandem with 'border-radius'.
When a <<geometry-box>> is not explicitly provided,
the default reference box depends on the number of <<basic-shape>> values:

An [=outer box-shadow=] follows the outside of the outer path, and an [=inner box-shadow=] follows the inside inner path.
Both are rendered as a stroke, with a stroke width of <code>spread * 2</code>, clipped by the border shape.
: Single <<basic-shape>>
:: The <<geometry-box>> defaults to the ''half-border-box'' value,
which positions the stroke to match the default border behavior,
with the border extending equally inward and outward from the shape path.

: Two <<basic-shape>>s
:: The first (outer) shape defaults to the [=border box=],
and the second (inner) shape defaults to the [=padding box=].
This allows the 'border-width' properties to naturally affect the final shape.

'border-shape' does not affect geometry or layout,
which is still computed using the existing 'border-width' properties.
<h3 id="border-shape-examples">
Examples</h3>

'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 decorate the box and control its text flow at the same time.
<div class="example">
Creating a circular border using stroke mode:

The inner 'border-shape' clips the [=overflow=] content of the element, in the same manner as 'border-radius',
as described in <a href="https://drafts.csswg.org/css-backgrounds-3/#corner-clipping">corner clipping</a>.
<pre class=lang-css>
.circular-button {
width: 100px;
height: 100px;
border: 5px solid blue;
border-shape: circle(50%);
}
</pre>

Issue: how should this affect clipping replaced elements?
This creates a circular border with a 5px stroke width.
</div>

<div algorithm="choose-relevant-side-for-border-shape">
An element's <dfn>relevant side for border shape</dfn> is the first side (in the order [=block-start=], [=inline-start=], [=block-end=], and [=inline-end=]) that has a non-''border-style/none'' [=border style=], or [=block-start=] if they're all ''border-style/none''.
1. If |element|'s [=computed value|computed=] 'border-block-start-style' is not ''border-style/none'', then return [=block-start=].
1. If |element|'s [=computed value|computed=] 'border-inline-start-style' is not ''border-style/none'', then return [=inline-start=].
1. If |element|'s [=computed value|computed=] 'border-block-end-style' is not ''border-style/none'', then return [=block-end=].
1. If |element|'s [=computed value|computed=] 'border-inline-end-style' is not ''border-style/none'', then return [=inline-end=].
1. Return [=block-start=].
</div>
<div class="example">
Creating a diamond-shaped border using fill mode:

<pre class=lang-css>
.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%);
}
</pre>

This creates a diamond border by defining outer and inner polygons.
</div>

<div class="example">
Using a custom path shape:

<pre class=lang-css>
.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');
}
</pre>

This creates a heart-shaped border using an SVG path.
</div>

<div class="example">
Combining 'border-shape' with different border widths:

<pre class=lang-css>
.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%);
}
</pre>

The [=relevant side for border shape=] determines which border width is used for the stroke.
</div>

<h3 id="border-shape-interactions">
Interactions with Other Properties</h3>

<h4 id="border-shape-radius-interaction">
Interaction with 'border-radius' and 'corner-shape'</h4>

The 'border-shape' property is not compatible with 'border-radius' and 'corner-shape'.
When an element's [=computed value=] of 'border-shape' is not <css>none</css>,
its 'border-radius' is ignored, as if it was set to 0.
'corner-shape' is implicitly ignored, as it can only work in tandem with 'border-radius'.

Note: Authors should use either 'border-shape' for custom shapes
or 'border-radius'/'corner-shape' for rectangular boxes with styled corners,
but not both simultaneously.

<h4 id="border-shape-shadow-interaction">
Interaction with 'box-shadow'</h4>

An [=outer box-shadow=] follows the outside of the outer path,
and an [=inner box-shadow=] follows the inside of the inner path.
Both are rendered as a stroke, with a stroke width of <code>spread * 2</code>,
clipped by the border shape.

<div class="example">
<pre class=lang-css>
.shaped-shadow {
border-shape: circle(50%);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
}
</pre>

The shadow follows the circular shape rather than a rectangular box.
</div>

<h4 id="border-shape-overflow-interaction">
Interaction with Overflow and Clipping</h4>

The inner 'border-shape' clips the [=overflow=] content of the element,
in the same manner as 'border-radius',
as described in <a href="https://drafts.csswg.org/css-backgrounds-3/#corner-clipping">corner clipping</a>.

Issue: how should this affect clipping replaced elements?

<h4 id="border-shape-layout-interaction">
Interaction with Layout</h4>

'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.

<h3 id="border-shape-relevant-side">
Determining the Relevant Side</h3>

<div algorithm="choose-relevant-side-for-border-shape">
An element's <dfn>relevant side for border shape</dfn> is the first side (in the order [=block-start=], [=inline-start=], [=block-end=], and [=inline-end=]) that has a non-''border-style/none'' [=border style=], or [=block-start=] if they're all ''border-style/none''.
1. If |element|'s [=computed value|computed=] 'border-block-start-style' is not ''border-style/none'', then return [=block-start=].
1. If |element|'s [=computed value|computed=] 'border-inline-start-style' is not ''border-style/none'', then return [=inline-start=].
1. If |element|'s [=computed value|computed=] 'border-block-end-style' is not ''border-style/none'', then return [=block-end=].
1. If |element|'s [=computed value|computed=] 'border-inline-end-style' is not ''border-style/none'', then return [=inline-end=].
1. Return [=block-start=].
</div>

Note: This algorithm ensures consistent behavior when multiple border sides have different styles or widths.
The first non-none border side (in logical order) determines the border characteristics
used for the shaped border.


<wpt>
Expand Down