ShapeDivider
svgで境界線を描画するコンポーネントです。
Overview
Lorem ipsum dolor sit amet. Consectetur adipiscing elit, …
<Stack> <p class="-p:30">Lorem ipsum dolor sit amet. Consectetur adipiscing elit, ...</p> <ShapeDivider viewBox="0 0 100 10"> <path d="M100 6C89.3 3.3 82.7 9 70 9S48.4 3 38 3 24.5 6 17 6C7.4 6 0 0 0 0v10h100V6z" /> </ShapeDivider></Stack><div class="l--stack"> <p class="-p:30">Lorem ipsum dolor sit amet. Consectetur adipiscing elit, ...</p> <div class="b--shapeDivider -max-sz:full" aria-hidden="true"> <div class="b--shapeDivider_inner"> <svg class="b--shapeDivider_svg" viewBox="0 0 100 10" width="100%" height="100%" fill="currentColor" focusable="false" preserveAspectRatio="none"> <path d="M100 6C89.3 3.3 82.7 9 70 9S48.4 3 38 3 24.5 6 17 6C7.4 6 0 0 0 0v10h100V6z"></path> </svg> </div> </div></div>Styles
ShapeDivider のベーススタイルは、以下のCSSで定義されています。
/* Note: scale 反転は、__inner に対して行う。shapeDivider自身を反転させると、X方向に隙間ができてしまう。 */
@layer lism-block {
.b--shapeDivider {
--level: 5;
/* svg配置調整用 */
--inner-offset: 0px;
--inner-stretch: 1;
/* flip がオンなら -1 にセット */
--_flipX: 1;
--_flipY: 1;
/* 500px時点を最小値, 1200px時点を最大値にして、clampで高さを変動させる */
height: clamp(calc(5px * var(--level)), calc(1cqw * var(--level)), calc(12px * var(--level)));
overflow: visible;
}
.b--shapeDivider:where([data-flip^='X']) {
--_flipX: -1;
}
.b--shapeDivider:where([data-flip$='Y']) {
--_flipY: -1;
}
.b--shapeDivider_inner {
/* 隙間ができるのを防ぐ */
--offsetY: 0.5px;
--offsetX: -0.5px;
height: 100%;
width: calc(100% + 1px);
scale: calc(var(--_flipX) * var(--inner-stretch)) var(--_flipY);
translate: calc(var(--offsetX) + var(--inner-offset)) calc(var(--offsetY) * var(--_flipY));
}
.b--shapeDivider_svg {
/* 位置ずれ防止 */
display: block;
}
/* 左右にスライドするアニメーション */
:where([data-has-animation='true']) .b--shapeDivider_svg {
/* ↓ X 100% にしてるのは translateXのcalc計算で * -1 をしなくていいように。*/
transform-origin: 100% 0;
animation: lism--slideShape 12s infinite alternate linear;
}
}
@keyframes lism--slideShape {
0% {
transform: scaleX(4);
}
100% {
transform: scaleX(4) translateX(calc(100% - 25%));
}
} ソースコード全体は GitHub で公開しています。
How to use
@lism-css/ui パッケージでShapeDivider(b--shapeDivider)として提供しています。
Import
import { ShapeDivider } from '@lism-css/ui/react'; import { ShapeDivider } from '@lism-css/ui/astro'; <link href="https://cdn.jsdelivr.net/npm/@lism-css/ui@0.30.0/dist/style.css" rel="stylesheet" /> Props
| プロパティ | 説明 |
|---|---|
viewBox |
出力するsvgのviewBox。 |
level |
シェイプの高さレベルを正の数で指定できます。デフォルトは5です。 |
offset |
シェイプを水平方向にずらすための変数を出力します。 |
stretch |
シェイプを水平方向に引き伸ばして表示するための変数を出力します。 |
flip |
"X", "Y", "XY" でシェイプの反転方向を指定できます。 |
isEmpty |
シェイプを表示せず、Spacerのように使用することができます。 |
isAnimation |
アニメーションをONにします。data-has-animation="true"が出力されます。 |
Examples
シェイプをセクションの上下に配置する
flipを指定して反転させることで、同じsvgパスを使って上下に利用できます。(パス自体を上下用に分けて用意してももちろんOKです)
Lorem ipsum dolor sit amet. Consectetur adipiscing elit, sed do eiusmod tempor Incididunt ut. Labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut.
Lorem ipsum dolor sit amet. Consectetur adipiscing elit, sed do eiusmod tempor Incididunt ut. Labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut. Aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint.
Lorem ipsum dolor sit amet. Consectetur adipiscing elit, sed do eiusmod tempor Incididunt ut. Labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut.
<Wrapper hasGutter py="40"> <DummyText /></Wrapper><ShapeDivider viewBox="0 0 10 10" level="4"> <path d="M10,10V0L0,9.5V10L10,10z"></path></ShapeDivider><Wrapper hasGutter bgc="text" c="base"> <Box py="40"> <DummyText length="l" /> </Box></Wrapper><ShapeDivider viewBox="0 0 10 10" level="4" flip="XY"> <path d="M10,10V0L0,9.5V10L10,10z"></path></ShapeDivider><Wrapper hasGutter py="40"> <DummyText /></Wrapper><div class="is--wrapper has--gutter -py:40"> <p>...Contents...</p></div><div class="b--shapeDivider -max-sz:full" style="--level:4" aria-hidden="true"> <div class="b--shapeDivider_inner"> <svg class="b--shapeDivider_svg" viewBox="0 0 10 10" width="100%" height="100%" fill="currentColor" focusable="false" preserveAspectRatio="none"> <path d="M10,10V0L0,9.5V10L10,10z"></path> </svg> </div></div><div class="is--wrapper has--gutter -bgc:text -c:base"> <div class="l--box -py:40"> <p>...Contents...</p> </div></div><div class="b--shapeDivider -max-sz:full" style="--level:4" aria-hidden="true" data-flip="XY"> <div class="b--shapeDivider_inner"> <svg class="b--shapeDivider_svg" viewBox="0 0 10 10" width="100%" height="100%" fill="currentColor" focusable="false" preserveAspectRatio="none"> <path d="M10,10V0L0,9.5V10L10,10z"></path> </svg> </div></div><div class="is--wrapper has--gutter -py:40"> <p>...Contents...</p></div>空のShapeDividerをスペーサーとして利用する
pathを用意せず、isEmptyを指定してスペーサーとしても利用できます。
空の場合でも、levelで高さをコントロールできるので、上下片方にシェイプを配置した時の逆側の余白コントロールに便利です。
Lorem ipsum dolor sit amet. Consectetur adipiscing elit, sed do eiusmod tempor Incididunt ut. Labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut. Aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint.
Lorem ipsum dolor sit amet. Consectetur adipiscing elit, sed do eiusmod tempor Incididunt ut. Labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut. Aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint.
<Wrapper hasGutter bgc="base"> <ShapeDivider isEmpty level="5" /> <Box py="20"> <DummyText length="l" /> </Box> <ShapeDivider viewBox="0 0 10 10" level="5" > <path d="M10,10V0L0,9.5V10L10,10z"></path> </ShapeDivider></Wrapper><Wrapper hasGutter bgc="text" c="base"> <Box py="40"> <DummyText length="l" /> </Box></Wrapper><div class="is--wrapper has--gutter -bgc:base"> <div class="b--shapeDivider -max-sz:full" style="--level:5" aria-hidden="true"></div> <div class="l--box -py:20"> <p>...Contents...</p> </div> <div class="b--shapeDivider -max-sz:full" style="--level:5" aria-hidden="true"> <div class="b--shapeDivider_inner"> <svg class="b--shapeDivider_svg" viewBox="0 0 10 10" width="100%" height="100%" fill="currentColor" focusable="false" preserveAspectRatio="none"> <path d="M10,10V0L0,9.5V10L10,10z"></path> </svg> </div> </div></div><div class="is--wrapper has--gutter -bgc:text -c:base"> <div class="l--box -py:40"> <p>...Contents...</p> </div></div>アニメーションをオンにする例
isAnimationでアニメーションをONにした時の例です。

Lorem ipsum dolor sit amet. Consectetur adipiscing elit, sed do eiusmod tempor Incididunt ut. Labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut. Aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint.
Lorem ipsum dolor sit amet. Consectetur adipiscing elit, sed do eiusmod tempor Incididunt ut. Labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut. Aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint.
<Container pos="relative" hasGutter> <ShapeDivider isEmpty level="7" /> <Frame isLayer z="-1" max-w="100%"> <Media src="https://cdn.lism-css.com/img/KRQPW50jXaLu.webp" alt="" width="960" height="640" /> <Layer bgc="black:50%" /> </Frame> <Wrapper contentSize="s" py="40" c="white"> <DummyText length="l" /> </Wrapper> <ShapeDivider viewBox="0 0 100 10" isAnimation c="base" level="7"> <path opacity=".3" d="M100,5.5C91.5,3,79.5,7.5,71.5,8C58,9,49,0.5,36,2c-9,1-13.5,3.5-20.5,3.5C8.5,6,0,2,0,2v8h100V5.5z"></path> <path opacity=".3" d="M100,1.5C87.8,6.6,79.3,2.1,67.8,2.6S53,6.5,42.5,5.5S23,0,15,0S1,3,0,4v6h100V1.5z"></path> <path d="M100,7.5C91.5,5,77.1,11.8,54,8C34.6,4.8,14.4,4.1,0,8.5V10h100V7.5z"></path> </ShapeDivider></Container><Container isWrapper="s" hasGutter bgc="base" py="40"> <DummyText length="l" /></Container><div class="is--container has--gutter -pos:relative"> <div class="b--shapeDivider -max-sz:full" style="--level:7" aria-hidden="true"></div> <div class="l--frame is--layer -z:-1 -max-w:100%"> <img src="https://cdn.lism-css.com/img/KRQPW50jXaLu.webp" alt="" width="960" height="640" /> <div class="is--layer -bgc" style="--bgc:color-mix(in srgb, var(--black) 50%, transparent)"></div> </div> <div class="is--wrapper -contentSize:s -py:40 -c" style="--c:var(--white)"> <p>...Contents...</p> </div> <div class="b--shapeDivider -max-sz:full -c:base" style="--level:7" aria-hidden="true" data-has-animation="true"> <div class="b--shapeDivider_inner"> <svg class="b--shapeDivider_svg" viewBox="0 0 100 10" width="100%" height="100%" fill="currentColor" focusable="false" preserveAspectRatio="none"> <path opacity=".3" d="M100,5.5C91.5,3,79.5,7.5,71.5,8C58,9,49,0.5,36,2c-9,1-13.5,3.5-20.5,3.5C8.5,6,0,2,0,2v8h100V5.5z"></path> <path opacity=".3" d="M100,1.5C87.8,6.6,79.3,2.1,67.8,2.6S53,6.5,42.5,5.5S23,0,15,0S1,3,0,4v6h100V1.5z"></path> <path d="M100,7.5C91.5,5,77.1,11.8,54,8C34.6,4.8,14.4,4.1,0,8.5V10h100V7.5z"></path> </svg> </div> </div></div><div class="is--container is--wrapper has--gutter -bgc:base -py:40 -contentSize:s"> <p>...Contents...</p></div>内部のSVGをずらして配置する例
上下に同じシェイプを使いまわす時、そのまま反転させると少し違和感がでてくるかもしれません。
そういう場合は、stretch,offsetでsvgをずらすことで、同じシェイプでも自然な表示にすることができます。
stretch,offsetの活用Lorem ipsum dolor sit amet. Consectetur adipiscing elit, sed do eiusmod tempor Incididunt ut. Labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut. Aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint.
Lorem ipsum dolor sit amet. Consectetur adipiscing elit, sed do eiusmod tempor Incididunt ut. Labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut. Aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint. Occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Sed ut perspiciatis undeomnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam.
Lorem ipsum dolor sit amet. Consectetur adipiscing elit, sed do eiusmod tempor Incididunt ut. Labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut. Aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint.
<Container isWrapper="s" hasGutter bgc="base" py="40"> <ShapeDivider isEmpty level="4" /> <DummyText length="l" /></Container><Container pos="relative" hasGutter c="base" bgc="text"> <ShapeDivider viewBox="0 0 100 10" c="base" flip="Y" level="8" stretch="1.1" offset="-5%"> <path d="M100 6C89.3 3.3 82.7 9 70 9S48.4 3 38 3 24.5 6 17 6C7.4 6 0 0 0 0v10h100V6z"></path> </ShapeDivider> <Wrapper contentSize="s" py="40"> <DummyText length="xl" /> </Wrapper> <ShapeDivider viewBox="0 0 100 10" c="base" level="8" stretch="1.1"> <path d="M100 6C89.3 3.3 82.7 9 70 9S48.4 3 38 3 24.5 6 17 6C7.4 6 0 0 0 0v10h100V6z"></path> </ShapeDivider></Container><Container isWrapper="s" hasGutter bgc="base" py="40"> <DummyText length="l" /> <ShapeDivider isEmpty level="4" /></Container><div class="is--container is--wrapper has--gutter -bgc:base -py:40 -contentSize:s"> <div class="b--shapeDivider -max-sz:full" style="--level:4" aria-hidden="true"></div> <p>...Contents...</p></div><div class="is--container has--gutter -pos:relative -c:base -bgc:text"> <div class="b--shapeDivider -max-sz:full -c:base" style="--level:8;--inner-offset:-5%;--inner-stretch:1.1" aria-hidden="true" data-flip="Y"> <div class="b--shapeDivider_inner"> <svg class="b--shapeDivider_svg" viewBox="0 0 100 10" width="100%" height="100%" fill="currentColor" focusable="false" preserveAspectRatio="none"> <path d="M100 6C89.3 3.3 82.7 9 70 9S48.4 3 38 3 24.5 6 17 6C7.4 6 0 0 0 0v10h100V6z"></path> </svg> </div> </div> <div class="is--wrapper -contentSize:s -py:40"> <p>...Contents...</p> </div> <div class="b--shapeDivider -max-sz:full -c:base" style="--level:8;--inner-stretch:1.1" aria-hidden="true"> <div class="b--shapeDivider_inner"> <svg class="b--shapeDivider_svg" viewBox="0 0 100 10" width="100%" height="100%" fill="currentColor" focusable="false" preserveAspectRatio="none"> <path d="M100 6C89.3 3.3 82.7 9 70 9S48.4 3 38 3 24.5 6 17 6C7.4 6 0 0 0 0v10h100V6z"></path> </svg> </div> </div></div><div class="is--container is--wrapper has--gutter -bgc:base -py:40 -contentSize:s"> <p>...Contents...</p> <div class="b--shapeDivider -max-sz:full" style="--level:4" aria-hidden="true"></div></div>