I'm trying to find the best method for building unique border shapes in HTML/CSS, for example:
example of border shape I'm attempting to acheive
The goal is to be able to have this responsive so width/height can change to anything, but have a fixed size for the parts of the shape that are marked red, like the cut corners and notch on the left side.
I attempted to use clip-path
with an inner div to create a border, but this falls apart with more complex shapes, like the notch on the left side. clip-path
also doesn't allow for rounding without using an SVG filter that introduces other artifacts to more complex shapes, and doesn't allow for precise control of the corner radius.
The cleanest reproductible way is to dive into how the path are drawn: read this first Paths.
svg
;width
and height
, example below.const { useRef, useState, useEffect } = React;
function App() {
const boxRef = useRef(null)
const [w, setW] = useState(200)
const [h, setH] = useState(200)
const [path, setPath] = useState("")
const computeX = x => {
const cur = w / 2 - (520 / 2 - x)
return cur
}
const computeXend = x => {
const cur = w - 520 + x
return cur
}
const computeY = y => {
const cur = h - (679 - y)
return cur
}
const resize = () => {
setPath(
`M0 ${computeY(664.576)}C0 ${computeY(667.098)} 0.953274 ${computeY(
669.528
)} 2.66879 ${computeY(671.377)}L6.77293 ${computeY(
675.801
)}C8.66521 ${computeY(677.841)} 11.3218 ${computeY(
679
)} 14.1041 ${computeY(679)}H${computeX(215.778)}C` +
`${computeX(218.323)} ${computeY(679)} ${computeX(
220.773
)} ${computeY(678.029)} ${computeX(222.628)} ${computeY(
676.286
)}L${computeX(227.49)} ${computeY(671.714)}C${computeX(
229.345
)} ${computeY(669.971)} ${computeX(231.795)} ${computeY(
669
)} ${computeX(234.34)} ${computeY(669)}H${computeX(
285.78
)}C${computeX(288.478)} ${computeY(669)} ${computeX(
291.061
)} ${computeY(670.09)} ${computeX(292.944)} ${computeY(
672.022
)}L${computeX(296.797)} ${computeY(675.978)}C` +
`${computeX(298.679)} ${computeY(677.91)} ${computeX(
301.262
)} ${computeY(679)} ${computeX(303.96)} ${computeY(
679
)}H${computeXend(505.896)}C${computeXend(508.678)} ${computeY(
679
)} ${computeXend(511.334)} ${computeY(677.841)} ${computeXend(
513.227
)} ${computeY(675.801)}L${computeXend(517.331)} ${computeY(
671.377
)}C${computeXend(519.047)} ${computeY(669.528)} ${computeXend(
520
)} ${computeY(667.098)} ${computeXend(520)} ${computeY(
664.576
)}V14.4242C` +
`${computeXend(520)} 11.9017 ${computeXend(
519.047
)} 9.47246 ${computeXend(517.331)} 7.62318L${computeXend(
513.227
)} 3.19905C${computeXend(511.335)} 1.15925 ${computeXend(
508.678
)} 4.3237e-05 ${computeXend(505.896)} 4.29938e-05L${computeX(
304.222
)} 2.53629e-05C` +
`${computeX(301.677)} 2.51404e-05 ${computeX(
299.227
)} 0.970698 ${computeX(297.372)} 2.71423L${computeX(
292.51
)} 7.28582C${computeX(290.655)} 9.02935 ${computeX(
288.206
)} 10 ${computeX(285.66)} 10L${computeX(234.22)} 10C${computeX(
231.522
)} 10 ${computeX(228.939)} 8.91008 ${computeX(
227.056
)} 6.97768L${computeX(223.203)} 3.02236C` +
`${computeX(221.321)} 1.08995 ${computeX(
218.738
)} 1.78896e-05 ${computeX(
216.04
)} 1.76538e-05L14.1044 0C11.3221 -2.43238e-07 8.66562 1.15915 6.77334 3.19888L2.66891 7.62311C` +
`0.953336 9.47238 2.80713e-05 11.9017 2.78508e-05 14.4242L0 ${computeY(
664.576
)}Z`
)
}
useEffect(() => {
resize();
}, [])
return ([
<input
type="number"
value={w}
onChange={
e => {
setW(e.target.value)
setH(e.target.value)
resize()
}
}
/>,
<svg
className={"w-full h-full absolute left-0 bot-0"}
width={w}
height={h}
viewBox={`0 0 ${w} ${h}`}
fill="none"
xmlns="http://www.w3.org/2000/svg"
overflow={"visible"}
>
<path
d={path}
fill="#ffeeef"
stroke={"black"}
/>
</svg>
])
}
ReactDOM.render(<App />,
document.getElementById("root"))
#root {
width: 100vw;
height: 100vh;
display: flex;
align-items: center;
justify-content: center;
}
input {
position: absolute;
top: 4px;
left: 4px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.production.min.js"></script>
<div id="root"></div>