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 togglethe 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;
}

Links