Accordion

A vertically stacked set of interactive headings that each reveal an associated section of content.

Yes. It adheres to the WAI-ARIA design pattern.

View source
import * as Accordion from "@danielfrg/solid-ui/accordion"
import styles from "./index.module.css"

export function DemoAccordionHero() {
  return (
    <Accordion.Root class={styles.accordion} defaultValue={["item-1"]}>
      <Accordion.Item value="item-1" class={styles.item}>
        <Accordion.Header class={styles.header}>
          <Accordion.Trigger class={styles.trigger}>
            <span>Is it accessible?</span>
            <PlusIcon class={styles.triggerIcon} />
          </Accordion.Trigger>
        </Accordion.Header>
        <Accordion.Content class={styles.panel}>
          <div class={styles.content}>Yes. It adheres to the WAI-ARIA design pattern.</div>
        </Accordion.Content>
      </Accordion.Item>

      <Accordion.Item value="item-2" class={styles.item}>
        <Accordion.Header class={styles.header}>
          <Accordion.Trigger class={styles.trigger}>
            <span>Is it unstyled?</span>
            <PlusIcon class={styles.triggerIcon} />
          </Accordion.Trigger>
        </Accordion.Header>
        <Accordion.Content class={styles.panel}>
          <div class={styles.content}>Yes. It's unstyled by default, giving you freedom over the look and feel.</div>
        </Accordion.Content>
      </Accordion.Item>

      <Accordion.Item value="item-3" class={styles.item}>
        <Accordion.Header class={styles.header}>
          <Accordion.Trigger class={styles.trigger}>
            <span>Can it be animated?</span>
            <PlusIcon class={styles.triggerIcon} />
          </Accordion.Trigger>
        </Accordion.Header>
        <Accordion.Content class={styles.panel}>
          <div class={styles.content}>Yes! You can animate the Accordion with CSS or JavaScript.</div>
        </Accordion.Content>
      </Accordion.Item>
    </Accordion.Root>
  )
}

function PlusIcon(props: Record<string, any>) {
  return (
    <svg viewBox="0 0 12 12" fill="currentcolor" {...props}>
      <path d="M6.75 0H5.25V5.25H0V6.75L5.25 6.75V12H6.75V6.75L12 6.75V5.25H6.75V0Z" />
    </svg>
  )
}
.accordion {
  box-sizing: border-box;
  display: flex;
  width: 24rem;
  max-width: calc(100vw - 8rem);
  flex-direction: column;
  justify-content: center;
  color: var(--color-gray-900);
}

.item {
  border-bottom: 1px solid var(--color-gray-200);
}

.trigger {
  box-sizing: border-box;
  position: relative;
  display: flex;
  width: 100%;
  gap: 1rem;
  align-items: baseline;
  justify-content: space-between;
  padding-block: 0.5rem;
  padding-inline: 0.75rem 0.25rem;
  color: var(--color-gray-900);
  font-family: inherit;
  font-weight: 500;
  font-size: 1rem;
  line-height: 1.5rem;
  background: var(--color-gray-50);
  border: none;
  outline: none;
  text-align: left;
  cursor: pointer;
}

@media (hover: hover) {
  .trigger:hover {
    background-color: var(--color-gray-100);
  }
}

.trigger:focus-visible {
  outline: 2px solid var(--color-blue);
  z-index: 1;
}

.triggerIcon {
  box-sizing: border-box;
  flex-shrink: 0;
  width: 0.75rem;
  height: 0.75rem;
  margin-right: 0.5rem;
  transition: transform 150ms ease-out;
}

.trigger[data-expanded] .triggerIcon {
  transform: rotate(45deg) scale(1.1);
}

.panel {
  box-sizing: border-box;
  overflow: hidden;
  color: var(--color-gray-600);
  font-size: 1rem;
  line-height: 1.5rem;
  animation: slideUp 200ms ease-out;
}

.panel[data-expanded] {
  animation: slideDown 200ms ease-out;
}

@keyframes slideDown {
  from {
    height: 0;
  }
  to {
    height: var(--kb-accordion-content-height);
  }
}

@keyframes slideUp {
  from {
    height: var(--kb-accordion-content-height);
  }
  to {
    height: 0;
  }
}

.content {
  padding: 0.75rem;
}

.status {
  margin: 1rem 0 0;
  font-size: 0.875rem;
  line-height: 1.25rem;
  color: var(--color-gray-600);
}
:root {
  --color-blue: oklch(45% 50% 264deg);
  --color-red: oklch(50% 55% 31deg);
  --color-gray-50: oklch(98% 0.25% 264deg);
  --color-gray-100: oklch(12% 9.5% 264deg / 5%);
  --color-gray-200: oklch(12% 9% 264deg / 7%);
  --color-gray-300: oklch(12% 8.5% 264deg / 17%);
  --color-gray-400: oklch(12% 8% 264deg / 38%);
  --color-gray-500: oklch(12% 7.5% 264deg / 50%);
  --color-gray-600: oklch(12% 7% 264deg / 67%);
  --color-gray-700: oklch(12% 6% 264deg / 77%);
  --color-gray-800: oklch(12% 5% 264deg / 85%);
  --color-gray-900: oklch(12% 5% 264deg / 90%);
  --color-gray-950: oklch(12% 5% 264deg / 95%);
}

@media (prefers-color-scheme: dark) {
  :root {
    --color-blue: oklch(69% 50% 264deg);
    --color-red: oklch(80% 55% 31deg);
    --color-gray-50: oklch(17% 0.25% 264deg);
    --color-gray-100: oklch(28% 0.75% 264deg / 65%);
    --color-gray-200: oklch(29% 0.75% 264deg / 80%);
    --color-gray-300: oklch(35% 0.75% 264deg / 80%);
    --color-gray-400: oklch(47% 0.875% 264deg / 80%);
    --color-gray-500: oklch(64% 1% 264deg / 80%);
    --color-gray-600: oklch(82% 1% 264deg / 80%);
    --color-gray-700: oklch(92% 1.125% 264deg / 80%);
    --color-gray-800: oklch(93% 0.875% 264deg / 85%);
    --color-gray-900: oklch(95% 0.5% 264deg / 90%);
    --color-gray-950: oklch(94% 0.375% 264deg / 95%);
  }
}

Examples

Multiple open items

Use the multiple prop to allow multiple panels to be open simultaneously.

Yes. It adheres to the WAI-ARIA design pattern.

Yes. It's unstyled by default, giving you freedom over the look and feel.

View source
import * as Accordion from "@danielfrg/solid-ui/accordion"
import styles from "./index.module.css"

export function DemoAccordionMultiple() {
  return (
    <Accordion.Root class={styles.accordion} multiple defaultValue={["item-1", "item-2"]}>
      <Accordion.Item value="item-1" class={styles.item}>
        <Accordion.Header class={styles.header}>
          <Accordion.Trigger class={styles.trigger}>
            <span>Is it accessible?</span>
            <PlusIcon class={styles.triggerIcon} />
          </Accordion.Trigger>
        </Accordion.Header>
        <Accordion.Content class={styles.panel}>
          <div class={styles.content}>Yes. It adheres to the WAI-ARIA design pattern.</div>
        </Accordion.Content>
      </Accordion.Item>

      <Accordion.Item value="item-2" class={styles.item}>
        <Accordion.Header class={styles.header}>
          <Accordion.Trigger class={styles.trigger}>
            <span>Is it unstyled?</span>
            <PlusIcon class={styles.triggerIcon} />
          </Accordion.Trigger>
        </Accordion.Header>
        <Accordion.Content class={styles.panel}>
          <div class={styles.content}>Yes. It's unstyled by default, giving you freedom over the look and feel.</div>
        </Accordion.Content>
      </Accordion.Item>

      <Accordion.Item value="item-3" class={styles.item}>
        <Accordion.Header class={styles.header}>
          <Accordion.Trigger class={styles.trigger}>
            <span>Can it be animated?</span>
            <PlusIcon class={styles.triggerIcon} />
          </Accordion.Trigger>
        </Accordion.Header>
        <Accordion.Content class={styles.panel}>
          <div class={styles.content}>Yes! You can animate the Accordion with CSS or JavaScript.</div>
        </Accordion.Content>
      </Accordion.Item>
    </Accordion.Root>
  )
}

function PlusIcon(props: Record<string, any>) {
  return (
    <svg viewBox="0 0 12 12" fill="currentcolor" {...props}>
      <path d="M6.75 0H5.25V5.25H0V6.75L5.25 6.75V12H6.75V6.75L12 6.75V5.25H6.75V0Z" />
    </svg>
  )
}
.accordion {
  box-sizing: border-box;
  display: flex;
  width: 24rem;
  max-width: calc(100vw - 8rem);
  flex-direction: column;
  justify-content: center;
  color: var(--color-gray-900);
}

.item {
  border-bottom: 1px solid var(--color-gray-200);
}

.trigger {
  box-sizing: border-box;
  position: relative;
  display: flex;
  width: 100%;
  gap: 1rem;
  align-items: baseline;
  justify-content: space-between;
  padding-block: 0.5rem;
  padding-inline: 0.75rem 0.25rem;
  color: var(--color-gray-900);
  font-family: inherit;
  font-weight: 500;
  font-size: 1rem;
  line-height: 1.5rem;
  background: var(--color-gray-50);
  border: none;
  outline: none;
  text-align: left;
  cursor: pointer;
}

@media (hover: hover) {
  .trigger:hover {
    background-color: var(--color-gray-100);
  }
}

.trigger:focus-visible {
  outline: 2px solid var(--color-blue);
  z-index: 1;
}

.triggerIcon {
  box-sizing: border-box;
  flex-shrink: 0;
  width: 0.75rem;
  height: 0.75rem;
  margin-right: 0.5rem;
  transition: transform 150ms ease-out;
}

.trigger[data-expanded] .triggerIcon {
  transform: rotate(45deg) scale(1.1);
}

.panel {
  box-sizing: border-box;
  overflow: hidden;
  color: var(--color-gray-600);
  font-size: 1rem;
  line-height: 1.5rem;
  animation: slideUp 200ms ease-out;
}

.panel[data-expanded] {
  animation: slideDown 200ms ease-out;
}

@keyframes slideDown {
  from {
    height: 0;
  }
  to {
    height: var(--kb-accordion-content-height);
  }
}

@keyframes slideUp {
  from {
    height: var(--kb-accordion-content-height);
  }
  to {
    height: 0;
  }
}

.content {
  padding: 0.75rem;
}

.status {
  margin: 1rem 0 0;
  font-size: 0.875rem;
  line-height: 1.25rem;
  color: var(--color-gray-600);
}
:root {
  --color-blue: oklch(45% 50% 264deg);
  --color-red: oklch(50% 55% 31deg);
  --color-gray-50: oklch(98% 0.25% 264deg);
  --color-gray-100: oklch(12% 9.5% 264deg / 5%);
  --color-gray-200: oklch(12% 9% 264deg / 7%);
  --color-gray-300: oklch(12% 8.5% 264deg / 17%);
  --color-gray-400: oklch(12% 8% 264deg / 38%);
  --color-gray-500: oklch(12% 7.5% 264deg / 50%);
  --color-gray-600: oklch(12% 7% 264deg / 67%);
  --color-gray-700: oklch(12% 6% 264deg / 77%);
  --color-gray-800: oklch(12% 5% 264deg / 85%);
  --color-gray-900: oklch(12% 5% 264deg / 90%);
  --color-gray-950: oklch(12% 5% 264deg / 95%);
}

@media (prefers-color-scheme: dark) {
  :root {
    --color-blue: oklch(69% 50% 264deg);
    --color-red: oklch(80% 55% 31deg);
    --color-gray-50: oklch(17% 0.25% 264deg);
    --color-gray-100: oklch(28% 0.75% 264deg / 65%);
    --color-gray-200: oklch(29% 0.75% 264deg / 80%);
    --color-gray-300: oklch(35% 0.75% 264deg / 80%);
    --color-gray-400: oklch(47% 0.875% 264deg / 80%);
    --color-gray-500: oklch(64% 1% 264deg / 80%);
    --color-gray-600: oklch(82% 1% 264deg / 80%);
    --color-gray-700: oklch(92% 1.125% 264deg / 80%);
    --color-gray-800: oklch(93% 0.875% 264deg / 85%);
    --color-gray-900: oklch(95% 0.5% 264deg / 90%);
    --color-gray-950: oklch(94% 0.375% 264deg / 95%);
  }
}

Default value

Use defaultValue to set which panel is open by default without controlling the state.

Yes. It's unstyled by default, giving you freedom over the look and feel.

View source
import * as Accordion from "@danielfrg/solid-ui/accordion"
import styles from "./index.module.css"

export function DemoAccordionDefaultValue() {
  return (
    <Accordion.Root class={styles.accordion} defaultValue={["item-2"]}>
      <Accordion.Item value="item-1" class={styles.item}>
        <Accordion.Header class={styles.header}>
          <Accordion.Trigger class={styles.trigger}>
            <span>Is it accessible?</span>
            <PlusIcon class={styles.triggerIcon} />
          </Accordion.Trigger>
        </Accordion.Header>
        <Accordion.Content class={styles.panel}>
          <div class={styles.content}>Yes. It adheres to the WAI-ARIA design pattern.</div>
        </Accordion.Content>
      </Accordion.Item>

      <Accordion.Item value="item-2" class={styles.item}>
        <Accordion.Header class={styles.header}>
          <Accordion.Trigger class={styles.trigger}>
            <span>Is it unstyled?</span>
            <PlusIcon class={styles.triggerIcon} />
          </Accordion.Trigger>
        </Accordion.Header>
        <Accordion.Content class={styles.panel}>
          <div class={styles.content}>Yes. It's unstyled by default, giving you freedom over the look and feel.</div>
        </Accordion.Content>
      </Accordion.Item>

      <Accordion.Item value="item-3" class={styles.item}>
        <Accordion.Header class={styles.header}>
          <Accordion.Trigger class={styles.trigger}>
            <span>Can it be animated?</span>
            <PlusIcon class={styles.triggerIcon} />
          </Accordion.Trigger>
        </Accordion.Header>
        <Accordion.Content class={styles.panel}>
          <div class={styles.content}>Yes! You can animate the Accordion with CSS or JavaScript.</div>
        </Accordion.Content>
      </Accordion.Item>
    </Accordion.Root>
  )
}

function PlusIcon(props: Record<string, any>) {
  return (
    <svg viewBox="0 0 12 12" fill="currentcolor" {...props}>
      <path d="M6.75 0H5.25V5.25H0V6.75L5.25 6.75V12H6.75V6.75L12 6.75V5.25H6.75V0Z" />
    </svg>
  )
}
.accordion {
  box-sizing: border-box;
  display: flex;
  width: 24rem;
  max-width: calc(100vw - 8rem);
  flex-direction: column;
  justify-content: center;
  color: var(--color-gray-900);
}

.item {
  border-bottom: 1px solid var(--color-gray-200);
}

.trigger {
  box-sizing: border-box;
  position: relative;
  display: flex;
  width: 100%;
  gap: 1rem;
  align-items: baseline;
  justify-content: space-between;
  padding-block: 0.5rem;
  padding-inline: 0.75rem 0.25rem;
  color: var(--color-gray-900);
  font-family: inherit;
  font-weight: 500;
  font-size: 1rem;
  line-height: 1.5rem;
  background: var(--color-gray-50);
  border: none;
  outline: none;
  text-align: left;
  cursor: pointer;
}

@media (hover: hover) {
  .trigger:hover {
    background-color: var(--color-gray-100);
  }
}

.trigger:focus-visible {
  outline: 2px solid var(--color-blue);
  z-index: 1;
}

.triggerIcon {
  box-sizing: border-box;
  flex-shrink: 0;
  width: 0.75rem;
  height: 0.75rem;
  margin-right: 0.5rem;
  transition: transform 150ms ease-out;
}

.trigger[data-expanded] .triggerIcon {
  transform: rotate(45deg) scale(1.1);
}

.panel {
  box-sizing: border-box;
  overflow: hidden;
  color: var(--color-gray-600);
  font-size: 1rem;
  line-height: 1.5rem;
  animation: slideUp 200ms ease-out;
}

.panel[data-expanded] {
  animation: slideDown 200ms ease-out;
}

@keyframes slideDown {
  from {
    height: 0;
  }
  to {
    height: var(--kb-accordion-content-height);
  }
}

@keyframes slideUp {
  from {
    height: var(--kb-accordion-content-height);
  }
  to {
    height: 0;
  }
}

.content {
  padding: 0.75rem;
}

.status {
  margin: 1rem 0 0;
  font-size: 0.875rem;
  line-height: 1.25rem;
  color: var(--color-gray-600);
}
:root {
  --color-blue: oklch(45% 50% 264deg);
  --color-red: oklch(50% 55% 31deg);
  --color-gray-50: oklch(98% 0.25% 264deg);
  --color-gray-100: oklch(12% 9.5% 264deg / 5%);
  --color-gray-200: oklch(12% 9% 264deg / 7%);
  --color-gray-300: oklch(12% 8.5% 264deg / 17%);
  --color-gray-400: oklch(12% 8% 264deg / 38%);
  --color-gray-500: oklch(12% 7.5% 264deg / 50%);
  --color-gray-600: oklch(12% 7% 264deg / 67%);
  --color-gray-700: oklch(12% 6% 264deg / 77%);
  --color-gray-800: oklch(12% 5% 264deg / 85%);
  --color-gray-900: oklch(12% 5% 264deg / 90%);
  --color-gray-950: oklch(12% 5% 264deg / 95%);
}

@media (prefers-color-scheme: dark) {
  :root {
    --color-blue: oklch(69% 50% 264deg);
    --color-red: oklch(80% 55% 31deg);
    --color-gray-50: oklch(17% 0.25% 264deg);
    --color-gray-100: oklch(28% 0.75% 264deg / 65%);
    --color-gray-200: oklch(29% 0.75% 264deg / 80%);
    --color-gray-300: oklch(35% 0.75% 264deg / 80%);
    --color-gray-400: oklch(47% 0.875% 264deg / 80%);
    --color-gray-500: oklch(64% 1% 264deg / 80%);
    --color-gray-600: oklch(82% 1% 264deg / 80%);
    --color-gray-700: oklch(92% 1.125% 264deg / 80%);
    --color-gray-800: oklch(93% 0.875% 264deg / 85%);
    --color-gray-900: oklch(95% 0.5% 264deg / 90%);
    --color-gray-950: oklch(94% 0.375% 264deg / 95%);
  }
}

Controlled

Use value and onChange to control the open state externally.

Yes. It adheres to the WAI-ARIA design pattern.

Open: item-1

View source
import { createSignal } from "solid-js"
import * as Accordion from "@danielfrg/solid-ui/accordion"
import styles from "./index.module.css"

export function DemoAccordionControlled() {
  const [value, setValue] = createSignal<string[]>(["item-1"])

  return (
    <div>
      <Accordion.Root class={styles.accordion} value={value()} onChange={setValue}>
        <Accordion.Item value="item-1" class={styles.item}>
          <Accordion.Header class={styles.header}>
            <Accordion.Trigger class={styles.trigger}>
              <span>Is it accessible?</span>
              <PlusIcon class={styles.triggerIcon} />
            </Accordion.Trigger>
          </Accordion.Header>
          <Accordion.Content class={styles.panel}>
            <div class={styles.content}>Yes. It adheres to the WAI-ARIA design pattern.</div>
          </Accordion.Content>
        </Accordion.Item>

        <Accordion.Item value="item-2" class={styles.item}>
          <Accordion.Header class={styles.header}>
            <Accordion.Trigger class={styles.trigger}>
              <span>Is it unstyled?</span>
              <PlusIcon class={styles.triggerIcon} />
            </Accordion.Trigger>
          </Accordion.Header>
          <Accordion.Content class={styles.panel}>
            <div class={styles.content}>Yes. It's unstyled by default, giving you freedom over the look and feel.</div>
          </Accordion.Content>
        </Accordion.Item>

        <Accordion.Item value="item-3" class={styles.item}>
          <Accordion.Header class={styles.header}>
            <Accordion.Trigger class={styles.trigger}>
              <span>Can it be animated?</span>
              <PlusIcon class={styles.triggerIcon} />
            </Accordion.Trigger>
          </Accordion.Header>
          <Accordion.Content class={styles.panel}>
            <div class={styles.content}>Yes! You can animate the Accordion with CSS or JavaScript.</div>
          </Accordion.Content>
        </Accordion.Item>
      </Accordion.Root>
      <p class={styles.status}>Open: {value().length > 0 ? value().join(", ") : "none"}</p>
    </div>
  )
}

function PlusIcon(props: Record<string, any>) {
  return (
    <svg viewBox="0 0 12 12" fill="currentcolor" {...props}>
      <path d="M6.75 0H5.25V5.25H0V6.75L5.25 6.75V12H6.75V6.75L12 6.75V5.25H6.75V0Z" />
    </svg>
  )
}
.accordion {
  box-sizing: border-box;
  display: flex;
  width: 24rem;
  max-width: calc(100vw - 8rem);
  flex-direction: column;
  justify-content: center;
  color: var(--color-gray-900);
}

.item {
  border-bottom: 1px solid var(--color-gray-200);
}

.trigger {
  box-sizing: border-box;
  position: relative;
  display: flex;
  width: 100%;
  gap: 1rem;
  align-items: baseline;
  justify-content: space-between;
  padding-block: 0.5rem;
  padding-inline: 0.75rem 0.25rem;
  color: var(--color-gray-900);
  font-family: inherit;
  font-weight: 500;
  font-size: 1rem;
  line-height: 1.5rem;
  background: var(--color-gray-50);
  border: none;
  outline: none;
  text-align: left;
  cursor: pointer;
}

@media (hover: hover) {
  .trigger:hover {
    background-color: var(--color-gray-100);
  }
}

.trigger:focus-visible {
  outline: 2px solid var(--color-blue);
  z-index: 1;
}

.triggerIcon {
  box-sizing: border-box;
  flex-shrink: 0;
  width: 0.75rem;
  height: 0.75rem;
  margin-right: 0.5rem;
  transition: transform 150ms ease-out;
}

.trigger[data-expanded] .triggerIcon {
  transform: rotate(45deg) scale(1.1);
}

.panel {
  box-sizing: border-box;
  overflow: hidden;
  color: var(--color-gray-600);
  font-size: 1rem;
  line-height: 1.5rem;
  animation: slideUp 200ms ease-out;
}

.panel[data-expanded] {
  animation: slideDown 200ms ease-out;
}

@keyframes slideDown {
  from {
    height: 0;
  }
  to {
    height: var(--kb-accordion-content-height);
  }
}

@keyframes slideUp {
  from {
    height: var(--kb-accordion-content-height);
  }
  to {
    height: 0;
  }
}

.content {
  padding: 0.75rem;
}

.status {
  margin: 1rem 0 0;
  font-size: 0.875rem;
  line-height: 1.25rem;
  color: var(--color-gray-600);
}
:root {
  --color-blue: oklch(45% 50% 264deg);
  --color-red: oklch(50% 55% 31deg);
  --color-gray-50: oklch(98% 0.25% 264deg);
  --color-gray-100: oklch(12% 9.5% 264deg / 5%);
  --color-gray-200: oklch(12% 9% 264deg / 7%);
  --color-gray-300: oklch(12% 8.5% 264deg / 17%);
  --color-gray-400: oklch(12% 8% 264deg / 38%);
  --color-gray-500: oklch(12% 7.5% 264deg / 50%);
  --color-gray-600: oklch(12% 7% 264deg / 67%);
  --color-gray-700: oklch(12% 6% 264deg / 77%);
  --color-gray-800: oklch(12% 5% 264deg / 85%);
  --color-gray-900: oklch(12% 5% 264deg / 90%);
  --color-gray-950: oklch(12% 5% 264deg / 95%);
}

@media (prefers-color-scheme: dark) {
  :root {
    --color-blue: oklch(69% 50% 264deg);
    --color-red: oklch(80% 55% 31deg);
    --color-gray-50: oklch(17% 0.25% 264deg);
    --color-gray-100: oklch(28% 0.75% 264deg / 65%);
    --color-gray-200: oklch(29% 0.75% 264deg / 80%);
    --color-gray-300: oklch(35% 0.75% 264deg / 80%);
    --color-gray-400: oklch(47% 0.875% 264deg / 80%);
    --color-gray-500: oklch(64% 1% 264deg / 80%);
    --color-gray-600: oklch(82% 1% 264deg / 80%);
    --color-gray-700: oklch(92% 1.125% 264deg / 80%);
    --color-gray-800: oklch(93% 0.875% 264deg / 85%);
    --color-gray-900: oklch(95% 0.5% 264deg / 90%);
    --color-gray-950: oklch(94% 0.375% 264deg / 95%);
  }
}

Collapsible

Use the collapsible prop to allow the active panel to be closed by clicking its trigger again.

Yes. It adheres to the WAI-ARIA design pattern.

View source
import * as Accordion from "@danielfrg/solid-ui/accordion"
import styles from "./index.module.css"

export function DemoAccordionCollapsible() {
  return (
    <Accordion.Root class={styles.accordion} defaultValue={["item-1"]} collapsible>
      <Accordion.Item value="item-1" class={styles.item}>
        <Accordion.Header class={styles.header}>
          <Accordion.Trigger class={styles.trigger}>
            <span>Is it accessible?</span>
            <PlusIcon class={styles.triggerIcon} />
          </Accordion.Trigger>
        </Accordion.Header>
        <Accordion.Content class={styles.panel}>
          <div class={styles.content}>Yes. It adheres to the WAI-ARIA design pattern.</div>
        </Accordion.Content>
      </Accordion.Item>

      <Accordion.Item value="item-2" class={styles.item}>
        <Accordion.Header class={styles.header}>
          <Accordion.Trigger class={styles.trigger}>
            <span>Is it unstyled?</span>
            <PlusIcon class={styles.triggerIcon} />
          </Accordion.Trigger>
        </Accordion.Header>
        <Accordion.Content class={styles.panel}>
          <div class={styles.content}>Yes. It's unstyled by default, giving you freedom over the look and feel.</div>
        </Accordion.Content>
      </Accordion.Item>

      <Accordion.Item value="item-3" class={styles.item}>
        <Accordion.Header class={styles.header}>
          <Accordion.Trigger class={styles.trigger}>
            <span>Can it be animated?</span>
            <PlusIcon class={styles.triggerIcon} />
          </Accordion.Trigger>
        </Accordion.Header>
        <Accordion.Content class={styles.panel}>
          <div class={styles.content}>Yes! You can animate the Accordion with CSS or JavaScript.</div>
        </Accordion.Content>
      </Accordion.Item>
    </Accordion.Root>
  )
}

function PlusIcon(props: Record<string, any>) {
  return (
    <svg viewBox="0 0 12 12" fill="currentcolor" {...props}>
      <path d="M6.75 0H5.25V5.25H0V6.75L5.25 6.75V12H6.75V6.75L12 6.75V5.25H6.75V0Z" />
    </svg>
  )
}
.accordion {
  box-sizing: border-box;
  display: flex;
  width: 24rem;
  max-width: calc(100vw - 8rem);
  flex-direction: column;
  justify-content: center;
  color: var(--color-gray-900);
}

.item {
  border-bottom: 1px solid var(--color-gray-200);
}

.trigger {
  box-sizing: border-box;
  position: relative;
  display: flex;
  width: 100%;
  gap: 1rem;
  align-items: baseline;
  justify-content: space-between;
  padding-block: 0.5rem;
  padding-inline: 0.75rem 0.25rem;
  color: var(--color-gray-900);
  font-family: inherit;
  font-weight: 500;
  font-size: 1rem;
  line-height: 1.5rem;
  background: var(--color-gray-50);
  border: none;
  outline: none;
  text-align: left;
  cursor: pointer;
}

@media (hover: hover) {
  .trigger:hover {
    background-color: var(--color-gray-100);
  }
}

.trigger:focus-visible {
  outline: 2px solid var(--color-blue);
  z-index: 1;
}

.triggerIcon {
  box-sizing: border-box;
  flex-shrink: 0;
  width: 0.75rem;
  height: 0.75rem;
  margin-right: 0.5rem;
  transition: transform 150ms ease-out;
}

.trigger[data-expanded] .triggerIcon {
  transform: rotate(45deg) scale(1.1);
}

.panel {
  box-sizing: border-box;
  overflow: hidden;
  color: var(--color-gray-600);
  font-size: 1rem;
  line-height: 1.5rem;
  animation: slideUp 200ms ease-out;
}

.panel[data-expanded] {
  animation: slideDown 200ms ease-out;
}

@keyframes slideDown {
  from {
    height: 0;
  }
  to {
    height: var(--kb-accordion-content-height);
  }
}

@keyframes slideUp {
  from {
    height: var(--kb-accordion-content-height);
  }
  to {
    height: 0;
  }
}

.content {
  padding: 0.75rem;
}

.status {
  margin: 1rem 0 0;
  font-size: 0.875rem;
  line-height: 1.25rem;
  color: var(--color-gray-600);
}
:root {
  --color-blue: oklch(45% 50% 264deg);
  --color-red: oklch(50% 55% 31deg);
  --color-gray-50: oklch(98% 0.25% 264deg);
  --color-gray-100: oklch(12% 9.5% 264deg / 5%);
  --color-gray-200: oklch(12% 9% 264deg / 7%);
  --color-gray-300: oklch(12% 8.5% 264deg / 17%);
  --color-gray-400: oklch(12% 8% 264deg / 38%);
  --color-gray-500: oklch(12% 7.5% 264deg / 50%);
  --color-gray-600: oklch(12% 7% 264deg / 67%);
  --color-gray-700: oklch(12% 6% 264deg / 77%);
  --color-gray-800: oklch(12% 5% 264deg / 85%);
  --color-gray-900: oklch(12% 5% 264deg / 90%);
  --color-gray-950: oklch(12% 5% 264deg / 95%);
}

@media (prefers-color-scheme: dark) {
  :root {
    --color-blue: oklch(69% 50% 264deg);
    --color-red: oklch(80% 55% 31deg);
    --color-gray-50: oklch(17% 0.25% 264deg);
    --color-gray-100: oklch(28% 0.75% 264deg / 65%);
    --color-gray-200: oklch(29% 0.75% 264deg / 80%);
    --color-gray-300: oklch(35% 0.75% 264deg / 80%);
    --color-gray-400: oklch(47% 0.875% 264deg / 80%);
    --color-gray-500: oklch(64% 1% 264deg / 80%);
    --color-gray-600: oklch(82% 1% 264deg / 80%);
    --color-gray-700: oklch(92% 1.125% 264deg / 80%);
    --color-gray-800: oklch(93% 0.875% 264deg / 85%);
    --color-gray-900: oklch(95% 0.5% 264deg / 90%);
    --color-gray-950: oklch(94% 0.375% 264deg / 95%);
  }
}