<style>
  /**
   * Hide the menu during the 'no-js' state 
   * to solve Cumulative Layout Shift.
   */
  [data-state='no-js'] .disclosure__content {
    display: none;
  }
</style>

<div class="disclosure js-disclosure" data-state="no-js">
  <button aria-expanded="false" class="disclosure__button js-disclosure-btn" aria-controls="content" disabled title="This button becomes functional once JavaScript is active">
    Disclosure trigger
    <svg class="disclosure__icon" focusable="false" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10 6"><polygon points="8.97 0 5.01 3.9 1 0.03 0 1.03 4.96 6 10 1.08 8.97 0"></polygon></svg>
  </button>
  <div class="disclosure__content" id="content">
    <!-- Additional content goes here -->
    <p>Revealed content!</p>
  </div>
</div>


<noscript>
  <!-- Show all content if no JS (avoids CLS) -->
  <style>
    [data-state='no-js'] .disclosure__content {
      display: block;
    }
  </style>
</noscript>
// Base styles
body {
  margin: 0;
  padding: 2rem;
  font-size: 1.5rem;
  font-family: sans-serif;
}


//-----------------
// Disclosure styles

.disclosure {
  border: 1px solid #ccc;
  padding: 10px;
  max-width: 50rem;
}

.disclosure__content {
  margin-top: 10px;
}

.disclosure__icon {
  height: 1em;
  width: 1em;
  fill: currentcolor;
  transition: transform 0.3s ease-out;
}

.disclosure__button {
  background-color: #222;
  padding: 0.25em 0.5em;
  color: #fff;
  border: 0;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1ch;
  
  &:focus-visible {
    outline: solid orange 3px;
    outline-offset: 3px;
  }
  
  &[aria-expanded="false"] {
    + .disclosure__content {
      display: none;
    }
  }
  
  &[aria-expanded="true"] {
    + .disclosure__content {
      display: block;
    }
    
    .disclosure__icon {
      transform: rotate(-180deg);
    }
  }
}

View Compiled
/** Technical Requirements
  * 1 * Must do a null check for component's presence on the page
  * 2 * Must update the data-state variable and button attributes on load  
  * 3 * Must toggle aria-expanded boolean on click
  * 4 * No requirement for closing other disclosures in a set when one is open
*/

const disclosures = document.querySelectorAll('.js-disclosure');

function toggleDisclosure() {
  const isExpanded = this.getAttribute('aria-expanded') === 'true';
  this.setAttribute('aria-expanded', !isExpanded);
}

function init() {
  // early return if no disclosures present on page
  if (!disclosures.length) {
    return
  }
  
  // update state not JS has loaded
  disclosures.forEach(component => {
    component.dataset.state = 'ready';
    
    const disclosureButtons = component.querySelectorAll('.js-disclosure-btn');
    
    disclosureButtons.forEach(btn => {
      // initialise button attributes now JS has loaded
      btn.removeAttribute('title');
      btn.removeAttribute('disabled');

      // listen for clicks
      btn.addEventListener('click', toggleDisclosure);
    });
  }); 
  
  
}

document.addEventListener(
  'DOMContentLoaded',
  () => {
    init();
  });

External CSS

  1. https://cdn.jsdelivr.net/npm/modern-css-reset@1.4.0/dist/reset.min.css

External JavaScript

This Pen doesn't use any external JavaScript resources.