Search code examples
htmlcssbootstrap-4

CSS-only Solution to Align Button-Collapse Behavior


I have arranged two (actually several) buttons next to each other in a Bootstrap template. When a button is pressed, a collapse opens below it. I have demonstrated the desired behavior in the first line with button Collapse 1 and 2.

For good reasons (tab order for accessibility), however, I cannot implement the HTML as in the first line. Instead, I would like to implement the HTML as in the second line (button and collapse are grouped).

Is there a way to transfer the appearance and behavior from line 1 to line 2 with CSS only? Unfortunately, I haven't managed it. :-(

<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.9.3/dist/umd/popper.min.js"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>

<div class="container mt-5">

  <div class="row">
    <div class="col-md-12 text-center">
      <button
        class="btn btn-primary"
        type="button"
        data-toggle="collapse"
        data-target="#collapseText1"
        aria-expanded="false"
        aria-controls="collapseText1">Collapse 1</button>
      <button
        class="btn btn-secondary"
        type="button"
        data-toggle="collapse"
        data-target="#collapseText2"
        aria-expanded="false"
        aria-controls="collapseText2">Collapse 2</button>
    </div>
  </div>
  
  <div class="row mt-3">
    <div class="col-md-12">
      <div class="collapse mb-3" id="collapseText1">
        <div class="card card-body">Text for Collapse 1.</div>
      </div>
      <div class="collapse mb-3" id="collapseText2">
        <div class="card card-body">Text for Collapse 2.</div>
      </div>
    </div>
  </div>
  
  <div class="row mt-5">
    <div class="col-md-6 text-center">
      <button
        class="btn btn-primary"
        type="button"
        data-toggle="collapse"
        data-target="#collapseText3"
        aria-expanded="false"
        aria-controls="collapseText3">Collapse 3</button>
      <div class="collapse mt-3 mb-3" id="collapseText3">
        <div class="card card-body">Text for Collapse 3.</div>
      </div>
    </div>
    <div class="col-md-6 text-center">
      <button
        class="btn btn-secondary"
        type="button"
        data-toggle="collapse"
        data-target="#collapseText4"
        aria-expanded="false"
        aria-controls="collapseText4">Collapse 4</button>
      <div class="collapse mt-3 mb-3" id="collapseText4">
        <div class="card card-body">Text for Collapse 4.</div>
      </div>
    </div>
  </div>

</div>


Solution

  • Flex is your friend.

    Using CSS flexbox you can change sorting order of elements, so my idea is to keep elements all in one container, then changing their position in CSS.

    Element structure will be like this:

    Container
      Button 1
      Content 1
      Button 2
      Content 2
      Button 3
      Content 3
    

    But element rendered order will be like this:

      Container
        Button 1 Button 2 Button 3
        Content 1
        Content 2
        Content 3
    

    That can be accomplished with CSS only, look at the following example

    .collapse-flex-container {
        display: flex;
        flex-wrap: wrap;
        justify-content: center;
    }
    .collapse-flex-container > div {
      width: 100%;
      order: 5;
    }
    .collapse-flex-container > button {
      margin-right: 5px;
    }
    <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet"/>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.9.3/dist/umd/popper.min.js"></script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>
    
    <div class="container mt-5">
      <div class="row mt-5">
        <div class="col-md-12 collapse-flex-container">
          <!-- BUTTON 1 -->
          <button class="btn btn-primary" type="button" data-toggle="collapse" data-target="#collapseText1" aria-expanded="false" aria-controls="collapseText1">Collapse 1</button>
          <!-- CONTENT 1 -->
          <div class="collapse mt-3 mb-3 collapse-content" id="collapseText1">
            <div class="card card-body">Text for Collapse 1.</div>
          </div>
          
          <!-- BUTTON 2 -->
          <button class="btn btn-secondary" type="button" data-toggle="collapse" data-target="#collapseText2" aria-expanded="false" aria-controls="collapseText2">Collapse 2</button>
          <!-- CONTENT 2 -->
          <div class="collapse mt-3 mb-3 collapse-content" id="collapseText2">
            <div class="card card-body">Text for Collapse 2.</div>
          </div>
        </div>
      </div>
    </div>