Accordion
Build an accordion/dropdown where sections expand or collapse when their headers are clicked.
Solution
Drop Down One
- A
- B
- C
Explanation
The trick
With this component we want to be able to show and hide the list. That's the key!
So it becomes more of a question of how to do that in an aesthetically pleasing way?
If you toggle the display
property the content will jump as you change the HTML structure.
Instead, we use the transform
property. This property doesn't affect the other elements in the DOM and stops you getting that jump effect.
The code
In this simple example, the container
section has a height
that we will match later on. And it has overflow
set to hidden
.
But why?
Now the click handler
will toggle
the showStyle
and the hideStyle
. Look closely and you will see that the translate
value shifts up in the y-coordinate by exactly the same height that we set the container to.
You can of course style this better and use different effects, but the main takeaway is to use a non-layout-effecting
property. We've used transform
but you can also use properties such as opacity
and filter.
Code
import { useState, CSSProperties } from "react";
import styles from "./Solution.module.css";
const Solution = () => {
const [showOne, toggleShowOne] = useState(false);
const handleClick = () => {
toggleShowOne((prev) => !prev);
};
const showStyle: CSSProperties = {
transform: "translate(0, 0)",
};
const hideStyle: CSSProperties = {
transform: "translate(0, -150px)",
};
const visibilityStyle = showOne ? showStyle : hideStyle;
return (
<div>
<section className={styles.section}>
<h2 onClick={handleClick} className={styles.heading}>
Drop Down One
</h2>
<ul style={visibilityStyle} className={styles.list}>
<li className={styles.listItem}>A</li>
<li className={styles.listItem}>B</li>
<li className={styles.listItem}>C</li>
</ul>
</section>
</div>
);
};
export default Solution;
Styling
.section {
border: 1px solid black;
padding: 5px;
height: 150px;
overflow: hidden;
}
.heading {
cursor: pointer;
}
.list {
display: flex;
flex-direction: column;
gap: 5px;
}
.listItem {
padding: 5px;
border: 1px solid black;
width: 250px;
}