Signature Library · Indian
Kolam Divider
One rice-flour line looped round every dot, edged in kaavi red. It draws itself when seen, and on hover.
Label is read by screen readers. Odd dot counts keep it one unbroken line.
States
Default
Hover
Active
Focus
Code
kolam-divider.html
<div class="sig-kolam-divider" role="separator" aria-label="Section divider">
<svg class="sig-kolam-divider__art" viewBox="0 0 262 92" aria-hidden="true">
<path class="sig-kolam-divider__kaavi" d="M46 29L63 46L80 63Q97 87.65 114 63L131 46L148 29Q165 4.35 182 29L199 46L216 63C233 87.65 257.65 63 233 46L216 29Q199 4.35 182 29L165 46L148 63Q131 87.65 114 63L97 46L80 29Q63 4.35 46 29L29 46C4.35 63 29 87.65 46 63L63 46L80 29Q97 4.35 114 29L131 46L148 63Q165 87.65 182 63L199 46L216 29C233 4.35 257.65 29 233 46L216 63Q199 87.65 182 63L165 46L148 29Q131 4.35 114 29L97 46L80 63Q63 87.65 46 63L29 46C4.35 29 29 4.35 46 29Z" pathLength="1"></path>
<path class="sig-kolam-divider__line" d="M46 29L63 46L80 63Q97 87.65 114 63L131 46L148 29Q165 4.35 182 29L199 46L216 63C233 87.65 257.65 63 233 46L216 29Q199 4.35 182 29L165 46L148 63Q131 87.65 114 63L97 46L80 29Q63 4.35 46 29L29 46C4.35 63 29 87.65 46 63L63 46L80 29Q97 4.35 114 29L131 46L148 63Q165 87.65 182 63L199 46L216 29C233 4.35 257.65 29 233 46L216 63Q199 87.65 182 63L165 46L148 29Q131 4.35 114 29L97 46L80 63Q63 87.65 46 63L29 46C4.35 29 29 4.35 46 29Z" pathLength="1"></path>
<circle class="sig-kolam-divider__dot" cx="29" cy="29" r="2.8"></circle>
<circle class="sig-kolam-divider__dot" cx="63" cy="29" r="2.8"></circle>
<circle class="sig-kolam-divider__dot" cx="97" cy="29" r="2.8"></circle>
<circle class="sig-kolam-divider__dot" cx="131" cy="29" r="2.8"></circle>
<circle class="sig-kolam-divider__dot" cx="165" cy="29" r="2.8"></circle>
<circle class="sig-kolam-divider__dot" cx="199" cy="29" r="2.8"></circle>
<circle class="sig-kolam-divider__dot" cx="233" cy="29" r="2.8"></circle>
<circle class="sig-kolam-divider__dot" cx="29" cy="63" r="2.8"></circle>
<circle class="sig-kolam-divider__dot" cx="63" cy="63" r="2.8"></circle>
<circle class="sig-kolam-divider__dot" cx="97" cy="63" r="2.8"></circle>
<circle class="sig-kolam-divider__dot" cx="131" cy="63" r="2.8"></circle>
<circle class="sig-kolam-divider__dot" cx="165" cy="63" r="2.8"></circle>
<circle class="sig-kolam-divider__dot" cx="199" cy="63" r="2.8"></circle>
<circle class="sig-kolam-divider__dot" cx="233" cy="63" r="2.8"></circle>
</svg>
</div>
kolam-divider.css
.sig-kolam-divider {
--sig-flour: #f8f1e3;
--sig-kaavi: #b5402a;
--sig-w: 280px;
--sig-speed: 1800ms;
}
.sig-kolam-divider {
position: relative;
box-sizing: border-box;
width: min(100%, var(--sig-w));
--off: 0;
}
.sig-kolam-divider[data-state=ready] {
--off: 1;
}
.sig-kolam-divider[data-state=drawn] {
--ease: stroke-dashoffset var(--sig-speed) ease-in-out;
}
.sig-kolam-divider__art {
display: block;
width: 100%;
overflow: visible;
}
.sig-kolam-divider__dot {
fill: var(--sig-flour);
}
.sig-kolam-divider__kaavi {
fill: none;
stroke-linecap: round;
stroke-linejoin: round;
stroke-dasharray: 1;
stroke-dashoffset: var(--off);
transition: var(--ease, none);
stroke: var(--sig-kaavi);
stroke-width: 7;
}
.sig-kolam-divider__line {
fill: none;
stroke-linecap: round;
stroke-linejoin: round;
stroke-dasharray: 1;
stroke-dashoffset: var(--off);
transition: var(--ease, none);
stroke: var(--sig-flour);
stroke-width: 2.8;
}
@media (prefers-reduced-motion: reduce) {
.sig-kolam-divider__kaavi,
.sig-kolam-divider__line {
animation: none !important;
transition: none !important;
}
}
kolam-divider.js
document.querySelectorAll(".sig-kolam-divider").forEach((el) => {
const draw = () => {
el.dataset.state = "ready";
void el.offsetWidth;
el.dataset.state = "drawn";
};
const seen = new IntersectionObserver((list) => {
if (!list[0].isIntersecting) return;
seen.disconnect();
draw();
});
el.dataset.state = "ready";
seen.observe(el);
el.addEventListener("pointerenter", draw);
return () => {
seen.disconnect();
el.removeEventListener("pointerenter", draw);
};
});
kolam-divider.tsx
import { useEffect, useRef } from "react";
import type React from "react";
import "./kolam-divider.css"; // the CSS tab
function attach(el: HTMLElement) {
const draw = () => {
el.dataset.state = "ready";
void el.offsetWidth;
el.dataset.state = "drawn";
};
const seen = new IntersectionObserver((list) => {
if (!list[0].isIntersecting) return;
seen.disconnect();
draw();
});
el.dataset.state = "ready";
seen.observe(el);
el.addEventListener("pointerenter", draw);
return () => {
seen.disconnect();
el.removeEventListener("pointerenter", draw);
};
}
export function SigKolamDivider(props: React.ComponentProps<"div">) {
const ref = useRef<HTMLDivElement>(null);
useEffect(() => attach(ref.current!), []);
return (
<div className="sig-kolam-divider" role="separator" aria-label="Section divider" ref={ref} {...props}>
<svg className="sig-kolam-divider__art" viewBox="0 0 262 92" aria-hidden="true">
<path className="sig-kolam-divider__kaavi" d="M46 29L63 46L80 63Q97 87.65 114 63L131 46L148 29Q165 4.35 182 29L199 46L216 63C233 87.65 257.65 63 233 46L216 29Q199 4.35 182 29L165 46L148 63Q131 87.65 114 63L97 46L80 29Q63 4.35 46 29L29 46C4.35 63 29 87.65 46 63L63 46L80 29Q97 4.35 114 29L131 46L148 63Q165 87.65 182 63L199 46L216 29C233 4.35 257.65 29 233 46L216 63Q199 87.65 182 63L165 46L148 29Q131 4.35 114 29L97 46L80 63Q63 87.65 46 63L29 46C4.35 29 29 4.35 46 29Z" pathLength="1" />
<path className="sig-kolam-divider__line" d="M46 29L63 46L80 63Q97 87.65 114 63L131 46L148 29Q165 4.35 182 29L199 46L216 63C233 87.65 257.65 63 233 46L216 29Q199 4.35 182 29L165 46L148 63Q131 87.65 114 63L97 46L80 29Q63 4.35 46 29L29 46C4.35 63 29 87.65 46 63L63 46L80 29Q97 4.35 114 29L131 46L148 63Q165 87.65 182 63L199 46L216 29C233 4.35 257.65 29 233 46L216 63Q199 87.65 182 63L165 46L148 29Q131 4.35 114 29L97 46L80 63Q63 87.65 46 63L29 46C4.35 29 29 4.35 46 29Z" pathLength="1" />
<circle className="sig-kolam-divider__dot" cx="29" cy="29" r="2.8" />
<circle className="sig-kolam-divider__dot" cx="63" cy="29" r="2.8" />
<circle className="sig-kolam-divider__dot" cx="97" cy="29" r="2.8" />
<circle className="sig-kolam-divider__dot" cx="131" cy="29" r="2.8" />
<circle className="sig-kolam-divider__dot" cx="165" cy="29" r="2.8" />
<circle className="sig-kolam-divider__dot" cx="199" cy="29" r="2.8" />
<circle className="sig-kolam-divider__dot" cx="233" cy="29" r="2.8" />
<circle className="sig-kolam-divider__dot" cx="29" cy="63" r="2.8" />
<circle className="sig-kolam-divider__dot" cx="63" cy="63" r="2.8" />
<circle className="sig-kolam-divider__dot" cx="97" cy="63" r="2.8" />
<circle className="sig-kolam-divider__dot" cx="131" cy="63" r="2.8" />
<circle className="sig-kolam-divider__dot" cx="165" cy="63" r="2.8" />
<circle className="sig-kolam-divider__dot" cx="199" cy="63" r="2.8" />
<circle className="sig-kolam-divider__dot" cx="233" cy="63" r="2.8" />
</svg>
</div>
);
}
kolam-divider.html
<div class="sig-kolam-divider relative [box-sizing:border-box] w-[min(100%,_280px)] [--off:0] [&[data-state=ready]]:[--off:1] [&[data-state=drawn]]:[--ease:stroke-dashoffset_1800ms_ease-in-out]" role="separator" aria-label="Section divider">
<svg class="[display:block] w-[100%] [overflow:visible]" viewBox="0 0 262 92" aria-hidden="true">
<path class="[fill:none] [stroke-linecap:round] [stroke-linejoin:round] [stroke-dasharray:1] [stroke-dashoffset:var(--off)] [transition:var(--ease,_none)] [stroke:#b5402a] [stroke-width:7] motion-reduce:[transition:none!important]" d="M46 29L63 46L80 63Q97 87.65 114 63L131 46L148 29Q165 4.35 182 29L199 46L216 63C233 87.65 257.65 63 233 46L216 29Q199 4.35 182 29L165 46L148 63Q131 87.65 114 63L97 46L80 29Q63 4.35 46 29L29 46C4.35 63 29 87.65 46 63L63 46L80 29Q97 4.35 114 29L131 46L148 63Q165 87.65 182 63L199 46L216 29C233 4.35 257.65 29 233 46L216 63Q199 87.65 182 63L165 46L148 29Q131 4.35 114 29L97 46L80 63Q63 87.65 46 63L29 46C4.35 29 29 4.35 46 29Z" pathLength="1"></path>
<path class="[fill:none] [stroke-linecap:round] [stroke-linejoin:round] [stroke-dasharray:1] [stroke-dashoffset:var(--off)] [transition:var(--ease,_none)] [stroke:#f8f1e3] [stroke-width:2.8] motion-reduce:[transition:none!important]" d="M46 29L63 46L80 63Q97 87.65 114 63L131 46L148 29Q165 4.35 182 29L199 46L216 63C233 87.65 257.65 63 233 46L216 29Q199 4.35 182 29L165 46L148 63Q131 87.65 114 63L97 46L80 29Q63 4.35 46 29L29 46C4.35 63 29 87.65 46 63L63 46L80 29Q97 4.35 114 29L131 46L148 63Q165 87.65 182 63L199 46L216 29C233 4.35 257.65 29 233 46L216 63Q199 87.65 182 63L165 46L148 29Q131 4.35 114 29L97 46L80 63Q63 87.65 46 63L29 46C4.35 29 29 4.35 46 29Z" pathLength="1"></path>
<circle class="[fill:#f8f1e3]" cx="29" cy="29" r="2.8"></circle>
<circle class="[fill:#f8f1e3]" cx="63" cy="29" r="2.8"></circle>
<circle class="[fill:#f8f1e3]" cx="97" cy="29" r="2.8"></circle>
<circle class="[fill:#f8f1e3]" cx="131" cy="29" r="2.8"></circle>
<circle class="[fill:#f8f1e3]" cx="165" cy="29" r="2.8"></circle>
<circle class="[fill:#f8f1e3]" cx="199" cy="29" r="2.8"></circle>
<circle class="[fill:#f8f1e3]" cx="233" cy="29" r="2.8"></circle>
<circle class="[fill:#f8f1e3]" cx="29" cy="63" r="2.8"></circle>
<circle class="[fill:#f8f1e3]" cx="63" cy="63" r="2.8"></circle>
<circle class="[fill:#f8f1e3]" cx="97" cy="63" r="2.8"></circle>
<circle class="[fill:#f8f1e3]" cx="131" cy="63" r="2.8"></circle>
<circle class="[fill:#f8f1e3]" cx="165" cy="63" r="2.8"></circle>
<circle class="[fill:#f8f1e3]" cx="199" cy="63" r="2.8"></circle>
<circle class="[fill:#f8f1e3]" cx="233" cy="63" r="2.8"></circle>
</svg>
</div>
<!-- Needs the script from the JS tab. -->