Skip to main content

Collapse

Overview

The Collapse component provides an expandable/collapsible content panel.

It's perfect for accordions, FAQs, settings panels, and any content that should be hidden by default. Supports both controlled and uncontrolled modes, with smooth animations and full accessibility support.


Import

import { Collapse } from '@allxsmith/bestax-bulma';

Usage

Basic Collapse

A simple uncontrolled collapse.

function example() {
  return (
    <Collapse
      trigger={
        <Block
          p="3"
          bgColor="white-ter"
          cursor="pointer"
          style={{ borderRadius: '4px' }}
        >
          <Strong>Click to expand</Strong>
        </Block>
      }
    >
      <Block p="3">
        <Paragraph>
          This is the collapsible content. It can contain any React elements.
        </Paragraph>
        <Paragraph>Click the header again to collapse.</Paragraph>
      </Block>
    </Collapse>
  );
}


Default Open

Collapse that starts open by default.

function example() {
  return (
    <Collapse
      defaultOpen
      trigger={
        <Block
          p="3"
          bgColor="white-ter"
          cursor="pointer"
          style={{ borderRadius: '4px' }}
        >
          <Strong>This starts open</Strong>
        </Block>
      }
    >
      <Block p="3">
        <Paragraph>This content is visible by default.</Paragraph>
      </Block>
    </Collapse>
  );
}


Without Animation

Collapse that toggles instantly without animation.

function example() {
  return (
    <Collapse
      animation={false}
      trigger={
        <Block
          p="3"
          bgColor="white-ter"
          cursor="pointer"
          style={{ borderRadius: '4px' }}
        >
          <Strong>No animation</Strong>
        </Block>
      }
    >
      <Block p="3">
        <Paragraph>This collapse has animation disabled.</Paragraph>
      </Block>
    </Collapse>
  );
}


Controlled Mode

Collapse with external state management.

function example() {
  const [isOpen, setIsOpen] = useState(false);

  return (
    <Block>
      <Block mb="4">
        <Button color="primary" onClick={() => setIsOpen(!isOpen)}>
          {isOpen ? 'Close' : 'Open'} Collapse
        </Button>
        <Span ml="4">State: {isOpen ? 'Open' : 'Closed'}</Span>
      </Block>
      <Collapse
        open={isOpen}
        trigger={
          <Block p="3" bgColor="white-ter" style={{ borderRadius: '4px' }}>
            Controlled collapse (use button above)
          </Block>
        }
      >
        <Block p="3">
          <Paragraph>This collapse is controlled by external state.</Paragraph>
        </Block>
      </Collapse>
    </Block>
  );
}


Accordion

Multiple collapses working together as an accordion.

function example() {
  const [openIndex, setOpenIndex] = useState(0);

  const items = [
    { title: 'Section 1', content: 'Content for section 1.' },
    { title: 'Section 2', content: 'Content for section 2.' },
    { title: 'Section 3', content: 'Content for section 3.' },
  ];

  return (
    <Block display="flex" flexDirection="column" gap="2">
      {items.map((item, index) => (
        <Collapse
          key={index}
          className="collapse is-bordered"
          open={openIndex === index}
          trigger={
            <Block
              p="4"
              onClick={() => setOpenIndex(openIndex === index ? -1 : index)}
              display="flex"
              justifyContent="space-between"
              alignItems="center"
            >
              <Strong>{item.title}</Strong>
              <Span>{openIndex === index ? '−' : '+'}</Span>
            </Block>
          }
        >
          <Paragraph p="4">{item.content}</Paragraph>
        </Collapse>
      ))}
    </Block>
  );
}


Card Style

Collapse with card styling.

function example() {
  return (
    <Collapse
      className="collapse is-card"
      defaultOpen
      trigger={
        <Block className="collapse-trigger-header">
          <Span>Card Collapse</Span>
          <Icon name="fas fa-chevron-down" />
        </Block>
      }
    >
      <Paragraph>
        This collapse is styled like a card with shadow and padding.
      </Paragraph>
      <Paragraph>Perfect for FAQ sections or settings panels.</Paragraph>
    </Collapse>
  );
}


FAQ Example

A common FAQ pattern with multiple collapses.

function example() {
  const faqs = [
    {
      q: 'What is Bestax?',
      a: 'Bestax is a React component library built on Bulma.',
    },
    { q: 'How do I install it?', a: 'pnpm add @allxsmith/bestax-bulma' },
    { q: 'Is it free?', a: 'Yes, Bestax is open source and free to use.' },
  ];

  const [openStates, setOpenStates] = useState(faqs.map(() => false));

  const toggle = index => {
    setOpenStates(prev =>
      prev.map((state, i) => (i === index ? !state : state))
    );
  };

  return (
    <Block>
      <Title size="5" mb="4">
        FAQ
      </Title>
      <Block display="flex" flexDirection="column" gap="2">
        {faqs.map((faq, index) => (
          <Collapse
            key={index}
            className="collapse is-bordered"
            trigger={
              <Block
                p="4"
                onClick={() => toggle(index)}
                display="flex"
                justifyContent="space-between"
              >
                <Strong>{faq.q}</Strong>
                <Span>{openStates[index] ? '▲' : '▼'}</Span>
              </Block>
            }
            open={openStates[index]}
          >
            <Paragraph p="4" textColor="grey-dark">
              {faq.a}
            </Paragraph>
          </Collapse>
        ))}
      </Block>
    </Block>
  );
}


CSS Classes

The Collapse component supports these additional CSS classes:

ClassDescription
is-activeApplied when the collapse is open
is-cardCard-style with shadow and padding
is-borderedBordered style with rounded corners

Accessibility

  • Trigger has role="button" and tabIndex="0" for keyboard access
  • Trigger has aria-expanded to indicate open/closed state
  • Trigger has aria-controls pointing to the content element
  • Content has aria-hidden matching the collapsed state
  • Supports keyboard navigation (Enter and Space to toggle)

  • Tabs - Tabbed content panels
  • Panel - Bulma panel component

Props

PropTypeDefaultDescription
openbooleanControlled open state. If provided, component is controlled.
defaultOpenbooleanfalseInitial open state for uncontrolled usage.
onOpen() => voidCallback when collapse opens.
onClose() => voidCallback when collapse closes.
triggerReact.ReactNodeThe clickable trigger element (header/button).
animation'fade' | 'slide' | false'fade'Animation style, or false to disable.
position'top' | 'bottom''top'Position of the trigger relative to content.
ariaIdstringautoCustom aria id for accessibility.
borderedbooleanfalseAdds a border around the collapse.
triggerClassNamestringAdditional classes for the trigger wrapper.
contentClassNamestringAdditional classes for the content wrapper.
childrenReact.ReactNodeThe collapsible content.
classNamestringAdditional CSS classes.
...All standard <div> attributes and Bulma helper propsSee Helper Props

CSS & Sass Variables

Collapse registers these variables on its own .collapse element. Override them there (or via className) — a value set on an ancestor is only inherited, and loses to the component-level declaration. See Theme.

CSS VariableSass VariableDefault
--bulma-collapse-margin-bottom$collapse-margin-bottom0.5rem
--bulma-collapse-border-color$collapse-border-colorvar(--bulma-border)
--bulma-collapse-radius$collapse-radiusvar(--bulma-radius)
--bulma-collapse-trigger-icon-size$collapse-trigger-icon-size1.5em
--bulma-collapse-trigger-icon-margin$collapse-trigger-icon-margin0.5em
--bulma-collapse-header-weight$collapse-header-weightvar(--bulma-weight-semibold)
--bulma-collapse-group-gap$collapse-group-gap0.5rem