CSSはどんどん進化している! border-shapeプロパティを使うとボーダーをあらゆる形状に実装できます
Post on:2026年6月29日
sponsorsr
CSSで2つの円を線でつなぐボーダーを実装できると思いますか?
可能になりました!
Chrome 147+のアップデートでサポートされたborder-shapeプロパティを使用すると、ボーダーにあらゆる形状(多角形・円など)を設定できます。

まずは、実際の動作をご覧ください。ドラッグの操作にはJavaScriptが使用されていますが、自由に変形するボーダーはCSSで実装されています。
※Chrome 147+, Edge 147+でご覧ください。
See the Pen
CSSのborder-shapeで直線を曲げる 1 by coliss (@coliss)
on CodePen.
CSSのborder-shapeプロパティは、ボーダーに任意の形状(多角形・円など)を設定できます。clip-pathと同じ形状を作成できますが、clip-pathのマスクとは異なり、border-shapeはボックス自体を再定義するため、その新しい形状に沿って背景や境界線などがすべてが従います。
では、その仕組みを解説します。
まずは、2つの円を「直線」で繋いでみます。JavaScriptは必要ありません、CSSのAnchor Positioning、attr()関数、コンテナクエリ、shape()関数、三角関数を使用します。
HTMLは非常にシンプルです。2つの円と直線をdiv要素で用意します。
|
1 2 3 4 5 6 |
<div class="circle" name="--a" size="150px"></div> <div class="circle" name="--b" size="100px"></div> <div class="arrow" x="--a" y="--b" size_x="150px" size_y="100px"> ... </div> |
動的に変化する位置を制御するのは、CSSで可能です。さらに円間の距離に応じて形状も調整することができます。さらに円が重なった際には直線は消えます。ピュアCSSによる衝突検出です!
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
.arrow { /* arrow size */ --a: 17px; --b: 20px; /* */ --c: 12px; /* control line height */ --r: 12px; /* radius of the small cirles */ /**/ --g: 10px; /* gap between the arrow and circles */ pointer-events: none; --x: attr(x type(<custom-ident>)); --y: attr(y type(<custom-ident>)); --r1: calc(attr(size_x type(<length>))/2); --r2: calc(attr(size_y type(<length>))/2); } .arrow :is(a,b,c,d) { position: absolute; --_x: calc(anchor(var(--x) inside) + anchor-size(var(--x))/2 - .1px); --_y: calc(anchor(var(--y) inside) + anchor-size(var(--y))/2); container-type: size; } .arrow :is(a,b) {top: var(--_x); bottom: var(--_y)} .arrow :is(a,c) {left: var(--_x); right: var(--_y)} .arrow :is(c,d) {top: var(--_y); bottom: var(--_x)} .arrow :is(b,d) {left: var(--_y); right: var(--_x)} .arrow :is(a,b,c,d):before { content: ""; position: absolute; inset: calc(100% - var(--a)) 0 calc(-1*var(--a)) calc(100% - hypot(100cqh,100cqw)); rotate: atan(100cqh/100cqw); border-image: conic-gradient(#556270) fill 0//999px; transform-origin: right; --_m0: sign(hypot(100cqh,100cqw) - var(--r1) - var(--r2) - 3*(var(--b) + var(--g))); --_m1: sign(hypot(100cqh,100cqw) - var(--r1) - var(--r2)); padding-inline: calc((var(--r1) + var(--g))*var(--_m0)) calc((var(--r2) + var(--g))*var(--_m0)); opacity: calc(sign(1cqw)*sign(1cqh)*var(--_m1)); --_d: calc(var(--r)*cos(asin(var(--c)/(2*var(--r))))); clip-path: if(style(--_m0 = 1): polygon(var(--b) 0,var(--b) var(--c),calc(100% - var(--b)) var(--c),calc(100% - var(--b)) 0,100% 50%,calc(100% - var(--b)) 100%,calc(100% - var(--b)) calc(100% - var(--c)),var(--b) calc(100% - var(--c)),var(--b) 100%,0 50%) content-box; else: shape(from var(--_d) var(--c), arc to var(--_d) calc(100% - var(--c)) of var(--r) large, hline to calc(100% - var(--_d)), arc to calc(100% - var(--_d)) var(--c) of var(--r) large );); } .arrow b {scale: -1 1} .arrow c {scale: 1 -1} .arrow d {scale: -1 -1} .circle { position: absolute; left: 10%; top: 10%; width: calc(attr(size type(<length>))); aspect-ratio: 1; background: #45ADA8; border-radius: 50%; anchor-name: attr(name type(<custom-ident>)); } .circle + .circle { background: #FA6900; left: 72%; top: 40%; } |
実際の動作は、デモページでご覧ください。
円(グリーンとオレンジも)をドラッグすると移動できます。移動に合わせて直線の長さや向きも変化し、円が重なった際には線は消えます。
See the Pen
CSSのborder-shapeで直線を曲げる by coliss (@coliss)
on CodePen.
これで、円を直線で繋げることができました。続いて、この直線を円間の距離に応じて変化する線を実装します。円に近づくと線は曲がり、遠ざかると伸びて細くなります。もちろん、これもピュアCSSで実装できます!
上記の実装に加えて、border-shapeプロパティとif()関数を使用します。HTMLは上記と同様に、シンプルです。
|
1 2 3 4 5 6 7 8 9 |
<div class="circle" name="--a" ></div> <div class="circle" name="--b" ></div> <div class="link" x="--a" y="--b"> <a></a> <b></b> <c></c> <d></d> </div> |
CSSは下記の通りです。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
.link { --b: 10px; /* line thickness */ --l: 500px; /* the wanted line length */ pointer-events: none; --x: attr(x type(<custom-ident>)); --y: attr(y type(<custom-ident>)); } .link * { position: absolute; --_x: calc(anchor(var(--x) inside) + anchor-size(var(--x))/2 - .1px); --_y: calc(anchor(var(--y) inside) + anchor-size(var(--y))/2); container-type: size; } .link :is(a,b) {top: var(--_x); bottom: var(--_y)} .link :is(a,c) {left: var(--_x); right: var(--_y)} .link :is(c,d) {top: var(--_y); bottom: var(--_x)} .link :is(b,d) {left: var(--_y); right: var(--_x)} .link *:before { content: ""; position: absolute; right: 0; bottom: 0; width: hypot(100cqh,100cqw); /*--e: 1.5; height: calc((2/var(--n))*pow(pow(var(--l)/1px,var(--e)) - pow(hypot(100cqh,100cqw)/1px,var(--e)),1/var(--e))*1px);*/ --n: 4; height: calc((2/var(--n))*(var(--l) - (pow(hypot(100cqh,100cqw)/1px,2)/(var(--l)/1px))*1px)); --_b: max(6px,var(--b)*min(1,1.2*var(--l)/hypot(100cqh,100cqw))); transform: translate(0,50%) rotate(atan(100cqh/100cqw)) translate(0,calc(var(--_b)/2)); border-bottom: var(--_b) dashed #556270; transform-origin: right; display: if(style(calc(sign(1cqw)*sign(1cqh)) = 0):none); border-shape: shape(from 0 50%,curve to 25% 50% with 12.5% 0,smooth to 50% 50%,smooth to 75% 50%,smooth to 100% 50%) padding-box; } .link b {scale: -1 1} .link c {scale: 1 -1} .link d {scale: -1 -1} .link :is(b,c):before, .link :is(b,c):after { transform: translate(0,50%) rotate(atan(100cqh/100cqw)) translate(0,calc(var(--_b)/-2)) scale(1,-1); } .circle { position: absolute; left: 10%; top: 10%; width: 120px; aspect-ratio: 1; background: radial-gradient(#556270 12px,#0000 13px) #45ADA8; border-radius: 50%; anchor-name: attr(name type(<custom-ident>)); } .circle + .circle { background-color: #FA6900; left: 72%; top: 40%; } |
実際の動作は、デモページでご覧ください。
See the Pen
CSSのborder-shapeで直線を曲げる 1 by coliss (@coliss)
on CodePen.
要素の数はいくつでも設定でき、接続設定(線の長さや曲線の数)も調整可能です。
See the Pen
CSSのborder-shapeで直線を曲げる 2 by coliss (@coliss)
on CodePen.
元ネタは、下記ポストより
Working on another type of connection 👀
I think I have found a CSS cheating code "border-shape" 🤩 https://t.co/PVm4rCDok2 pic.twitter.com/2oJD6vWAIZ
— CSS by T. Afif (@ChallengesCss) June 10, 2026
sponsors













