Skip to content
Merged
Changes from 1 commit
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
Next Next commit
[css-shapes-2] Add detailed example for
  • Loading branch information
noamr committed Jan 21, 2025
commit 10049d8010ae4f05f753e9156d4aa38785eb2001
123 changes: 122 additions & 1 deletion css-shapes-2/Overview.bs
Original file line number Diff line number Diff line change
Expand Up @@ -318,7 +318,7 @@ The ''shape()'' Function</h4>
the command's starting point, the command's end point, or the [=reference box=], respectively.
If such component is not provided, the <<coordinate-pair>> is relative to the segment's start.

<dt><dfn><<arc-command>></dfn> = <dfn value>arc</dfn> <<command-end-point>> [[of <<length-percentage>>{1,2}] && <<arc-sweep>>? && <<arc-size>>? && rotate <<angle>>? ]
<dt><dfn><<arc-command>></dfn> = <dfn value>arc</dfn> <<command-end-point>> [[of <<length-percentage>>{1,2}] && <<arc-sweep>>? && <<arc-size>>?|| rotate <<angle>>? ]
<dd>
Add an <a href="https://www.w3.org/TR/SVG/paths.html#PathDataEllipticalArcCommands">elliptical arc</a> command
to the list of path data commands,
Expand Down Expand Up @@ -406,6 +406,127 @@ The ''shape()'' Function</h4>
which affects how line-joins and line-caps are rendered.
</dl>

<h5 id=shape-examples>Using ''shape()'' to create responsive, parametric speech bubble</h5>

<div class=example>
The ''shape()'' function enables shapes that are responsive, rather than scalable.
While the ''polygon()'' shape is also responsive, it doesn't support curves.
Copy link
Contributor

@yisibl yisibl Jan 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great example!

it doesn't support curves.

polygon adds the round keyword, which I think may not be accurately described here.
https://drafts.csswg.org/css-shapes-1/#funcdef-basic-shape-polygon

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True, I'll update!


To demonstrate, let's start with a speech bubble, such as the following:

<img src="images/bubble.svg" width=300 style="background: unset">

Using this shape with a ''clip-path'' can be done by using the ''path()'' function:

<pre highlight="css">
.bubble { clip-path: path("m 5 0 H 95 Q 100 0 100 5 V 92 Q 100 97 95 97 H 70 l -2 3 l -2 -3 H 5 Q 0 97 0 92 V 5 Q 0 0 5 0") };
</pre>

Altohugh this path can easily scale, the scaled results are not always desirable. e.g. when scaled to a small balloon, the arrow and corners are scaled to become almost invisible:

<img src="images/bubble.svg" width=100 style="background: unset">

To construct this shape using the ''shape()'' function, let's start by turning all the pixel values from the ''path'' function to percentages.
Note that the ''shape()'' function begins with <css>from</css>:

<pre highlight="css">
.bubble { clip-path: shape( from 5% 0%,
hline to 95%,
curve to 100% 5% with 100% 0%,
vline to 92%,
curve to 95% 97% with 100% 97%,
hline to 70%,
line by -2% 3%,
line by -2% -3%,
hline to 5%,
curve to 0% 92% with 0% 97%,
vline to 5%,
curve to 5% 0% with 0% 0%); }
</pre>

To make this path responsive, as in, respond well to size changes, we will convert some of its units to ''px'' values,
specifically the ones the control the curves and arrows:

<pre highlight="css">
.bubble { clip-path: shape( from 5px 0%,
hline to calc(100% - 5px),
curve to 100% 5px with 100% 0%,
vline to calc(100% - 8px),
curve to calc(100% - 5px) calc(100% - 3px) with 100% calc(100% - 3px),
hline to 70%,
line by -2px 3px,
line by -2px -3px,
hline to 5px,
curve to 0% calc(100% - 8px) with 0% calc(100% - 3px),
vline to 5px,
curve to 5px 0% with 0% 0%); }
</pre>

When applied as ''clip-path'', it would looks like the following:
<p>
<img src="images/bubble-50.svg" width=150 style="background: unset">
</p>

The whole speech bubble is scaled to the reference box, while the curves and arrows stay more constant.

Since ''shape()'' uses CSS units, we can replace some of the edges with ''position'' values:

<pre highlight="css">
.bubble { clip-path: shape(from 5px 0,
hline to calc(100% - 5px),
curve to right 5px with right top,
vline to calc(100% - 8px),
curve to calc(100% - 5px) calc(100% - 3px) with right calc(100% - 3px),
hline to 70%,
line by -2px 3px,
line by -2px -3px,
hline to 5px,
curve to left calc(100% - 8px) with left calc(100% - 3px),
vline to 5px,
curve to 5px top with left top); }
</pre>

Another useful feature of ''shape()'' is that it can be used alongside CSS properties. In this case,
we can make the arrow and radius parametric, and even animate them:

<pre highlight="css">

@keyframes bubble {
from {
--radius: 30px;
--arrow-position: 20%;
}
}

:root {
--radius: 5px;
--arrow-length: 3px;
--arrow-half-width: 2px;
--arrow-position: 70%;
--arrow-bottom-offset: calc(100% - var(--radius) - var(--arrow-length));
}

.bubble {
animation: bubble 100ms;
clip-path: shape(from var(---radius) top,
hline to calc(100% - var(---radius)),
curve to right var(---radius) with right top,
vline to var(---arrow-bottom-offset),
curve to calc(100% - var(---radius)) calc(100% - var(---arrow-length))
with right calc(100% - var(---arrow-length)),
hline to var(---arrow-position),
line by var(---arrow-half-width) var(---arrow-length),
line by var(---arrow-half-width) calc(0px - var(---arrow-length)),
hline to var(---radius),
curve to left var(---arrow-bottom-offset) with left calc(100% - var(---arrow-length)),
vline to var(---radius),
curve to var(---radius) top with left top); }
</pre>



</div>

<h5 id=interpolating-shape>
Interpolating the ''shape()'' Function</h5>

Expand Down