AnimationsSimpleApril 8, 2026
Divider Width Expand
Horizontal line expands from 0% to 100% width when a section is opened. Used as a visual separator animation for accordion-style toggles.
View Full Demo →Preview
Source
demo.jsx
import DividerWidthExpand from "./index.jsx";
import { dividerWidthExpand } from "@/content/animations/demo-data.js";
import styles from "./demo.module.css";
export default function DividerWidthExpandDemo() {
return (
<div className={styles.demo}>
<div className={styles.inner}>
<div className={styles.left}>
<p className={styles.label}>What we do</p>
<h2 className={styles.heading}>Services</h2>
</div>
<div className={styles.right}>
<DividerWidthExpand {...dividerWidthExpand} />
</div>
</div>
</div>
);
}
index.jsx
"use client";
import { useState } from "react";
import styles from "./styles.module.css";
export default function DividerWidthExpand({ sections = [] }) {
const [openIndex, setOpenIndex] = useState(null);
return (
<div className={styles.list}>
{sections.map((section, i) => {
const isOpen = openIndex === i;
return (
<div key={section.title} className={styles.item}>
<button
className={styles.trigger}
onClick={() => setOpenIndex(isOpen ? null : i)}
aria-expanded={isOpen}
>
<span>{section.title}</span>
<span className={styles.indicator}>{isOpen ? "−" : "+"}</span>
</button>
<div className={`${styles.divider} ${isOpen ? styles.expanded : ""}`} />
{isOpen && (
<p className={styles.content}>{section.content}</p>
)}
</div>
);
})}
</div>
);
}
demo.module.css
.demo {
min-height: 100vh;
background: #f8f8f6;
display: flex;
align-items: center;
justify-content: center;
padding: 5rem 3rem;
}
.inner {
display: grid;
grid-template-columns: 1fr 2fr;
gap: 5rem;
width: 100%;
max-width: 1000px;
align-items: start;
}
.left {
padding-top: 2rem;
display: flex;
flex-direction: column;
gap: 0.75rem;
position: sticky;
top: 5rem;
}
.label {
font-size: 0.75rem;
font-weight: 500;
letter-spacing: 0.14em;
text-transform: uppercase;
color: #9b9b9b;
}
.heading {
font-size: clamp(2.5rem, 5vw, 4.5rem);
font-weight: 500;
letter-spacing: -0.03em;
line-height: 0.95;
color: #1a1a1a;
}
.right {
width: 100%;
}
/* Let the component fill full width in demo */
.right :global(.list) {
max-width: 100%;
padding: 0;
margin: 0;
}
@media (max-width: 767px) {
.inner {
grid-template-columns: 1fr;
gap: 2rem;
}
.left {
position: static;
}
}
styles.module.css
.list {
max-width: 600px;
margin: 0 auto;
padding: 2rem;
}
.item {
border-top: 1px solid #e5e5e5;
}
.item:last-child {
border-bottom: 1px solid #e5e5e5;
}
.trigger {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
padding: 1.25rem 0;
font-size: 1rem;
font-weight: 500;
font-family: inherit;
letter-spacing: -0.01em;
color: #1a1a1a;
background: none;
border: none;
cursor: pointer;
text-align: left;
}
.indicator {
font-size: 1.25rem;
font-weight: 300;
color: #9b9b9b;
}
.divider {
height: 1px;
background: #1a1a1a;
width: 0%;
transition: width 400ms cubic-bezier(0.16, 1, 0.3, 1);
}
.divider.expanded {
width: 100%;
}
.content {
padding: 0.75rem 0 1.5rem;
font-size: 0.9375rem;
color: #6b6b6b;
line-height: 1.65;
}