Pen Settings

HTML

CSS

CSS Base

Vendor Prefixing

Add External Stylesheets/Pens

Any URL's added here will be added as <link>s in order, and before the CSS in the editor. If you link to another Pen, it will include the CSS from that Pen. If the preprocessor matches, it will attempt to combine them before processing.

+ add another resource

JavaScript

Babel includes JSX processing.

Add External Scripts/Pens

Any URL's added here will be added as <script>s in order, and run before the JavaScript in the editor. You can use the URL of any other Pen and it will include the JavaScript from that Pen.

+ add another resource

Packages

Add Packages

Search for and use JavaScript packages from npm here. By selecting a package, an import statement will be added to the top of the JavaScript editor for this package.

Behavior

Save Automatically?

If active, Pens will autosave every 30 seconds after being saved once.

Auto-Updating Preview

If enabled, the preview panel updates automatically as you code. If disabled, use the "Run" button to update.

Format on Save

If enabled, your code will be formatted when you actively save your Pen. Note: your code becomes un-folded during formatting.

Editor Settings

Code Indentation

Want to change your Syntax Highlighting theme, Fonts and more?

Visit your global Editor Settings.

HTML

              
                <div class="space-y-8">
  <h1 class="text-3xl font-bold mb-5">Accessible smooth collapse experiment</h1>
  
  <p>Primis ullamcorper laoreet morbi ex sem id, sapien molestie torquent aptent vivamus, felis dapibus diam lectus donec. <a href="https://wolstenhol.me" target="_top">Elit aenean eu eleifend vestibulum</a> sem quis commodo pretium sed rutrum aptent, proin cursus nunc sollicitudin vehicula dapibus iaculis est malesuada lobortis ante eget, nostra molestie morbi natoque luctus tortor bibendum taciti amet tristique.</p>

  <div 
    x-data="collapse"
    class="collapse border py-3 px-5 space-y-3"
    @resize.window.debounce="updateHeight"
  >
    <h2>
      <button 
        class="flex items-center w-full space-x-3 font-bold text-xl select-none text-left" 
        @click="toggle" 
        id="collapse-1-button" 
        aria-controls="collapse-1-content" 
        :aria-expanded="expanded ? 'true' : 'false'"
      >
        <svg
          class="flex-shrink-0 w-6 h-6"
          aria-hidden="true"
          focusable="false"
          xmlns="http://www.w3.org/2000/svg"
          class="h-6 w-6"
          fill="none"
          viewBox="0 0 24 24"
          stroke="currentColor"
        >
          <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" /> </svg>
        <span>Expandable region heading</span>
      </button>
    </h2>
    <section 
      class="space-y-2 overflow-hidden transition-all duration-300 ease-out" 
      x-ref="content" 
      aria-labelledby="collapse-1-button"
    >
      <p>Congue interdum nibh at lobortis habitasse vehicula facilisis auctor, convallis nascetur aenean mattis egestas neque ante molestie penatibus, cras efficitur cursus class magnis quam tempus.</p>
      <p>
        <a href="https://wolstenhol.me" target="_top">I am a link that should only be focusable when the element is open</a>.
      </p>
      <p>Vehicula cursus velit elit placerat porta urna sit risus etiam, ullamcorper turpis praesent non tempus volutpat mus. Blandit magna hac auctor nec aliquet consequat orci maximus, rhoncus litora ultricies taciti tempor class. A mus consectetur nisl tellus cras blandit quisque consequat, duis aliquet rutrum aptent fermentum metus ligula, sapien penatibus venenatis potenti nisi eu eros. Turpis quam vestibulum praesent imperdiet amet curae hendrerit semper, ullamcorper metus lacinia cras lacus senectus sed. Dictumst torquent integer interdum mus consectetur efficitur natoque ultricies nec sodales auctor suspendisse orci cubilia, parturient egestas ligula est metus non scelerisque netus lacus imperdiet blandit pellentesque inceptos. Duis ad justo gravida turpis quam dui consequat dictumst, metus curabitur nisl magna vestibulum ex tempus sollicitudin morbi, interdum montes lacus vel sit facilisis sociosqu.</p>
    </section>
  </div>


  <p>Platea ligula justo auctor elit cubilia velit laoreet viverra, mollis feugiat amet dignissim phasellus aenean sed dui, tellus convallis at erat tortor donec efficitur. Litora aliquam habitasse ultrices dictumst torquent <a href="https://wolstenhol.me" target="_top">magna malesuada et urna magnis</a>, odio tristique praesent eu facilisi vel sed lacinia non, pulvinar tellus aliquet in ullamcorper consectetur lobortis metus imperdiet.</p>
</div>
              
            
!

CSS

              
                .collapse__content {
  // We set the max-height using the --collapse-height
  // CSS custom property. If the custom property is 
  // missing then the fallback value of 0 is used.
  // By toggling the value of the custom property with
  // JavaScript we can animate the max-height.
  max-height: var(--collapse-height, 0);
}

// Unrelated to component - just for presentation of example content.
a {
  color: rgb(109, 40, 217);
  text-decoration: underline;

  &:hover,
  &:focus {
    color: rgb(91, 33, 182);
  }
}

              
            
!

JS

              
                document.addEventListener('alpine:init', () => {
  Alpine.data('collapse', () => ({
    expanded: null,
    init() {
      const elem = this.$refs.content;

      // We add the hidden attribute and CSS class via Alpine's init function
      // so that content is not hidden if the JS fails to execute. The downside
      // of this is a layout-shift issue and a flash of content appearing then
      // disappearing as the page loads then the JS executes. The balance
      // between progressive enhancement and layout-shift issues is tricky!
      elem.hidden = true;
      elem.classList.add('collapse__content');
    },
    toggle() {
      const elem = this.$refs.content;

      if (this.expanded) {
        // Remove the --collapse-height custom property so the browser uses
        // the fallback value of 0.
        elem.style.removeProperty('--collapse-height');
        elem.addEventListener(
          'transitionend',
          (e) => {
            // We need to make sure the event hasn't come from a child element
            // and bubbled up to our element.
            if (e.target === elem) {
              // Mark the element as hidden so its contents will be
              // hidden from assistive tech like screen readers or
              // keyboard navigation.
              elem.hidden = true;
              this.expanded = false;
            }
          },
          {
            once: true,
          }
        );
      } else {
        // Unhide our element so we can calculate its dimensions.
        // It will still be visually hidden because of the maxHeight
        // of 0.
        elem.hidden = false;
        // Set a --collapse-height property that matches the elements height.
        // This will cause the browser to animate the opening of the
        // element.
        elem.style.setProperty('--collapse-height', `${elem.scrollHeight}px`);
        elem.addEventListener(
          'transitionend',
          (e) => {
            // We need to make sure the event hasn't come from a child element
            // and bubbled up to our element.
            if (e.target === elem) {
              this.expanded = true;
            }
          },
          {
            once: true,
          }
        );
      }
    },
    updateHeight() {
      // A function to update the custom property on window resize, to avoid
      // clipping content if the scrollHeight value of the element changes.
      if (this.expanded) {
        const elem = this.$refs['content'];
        elem.style.setProperty('--collapse-height', `${elem.scrollHeight}px`);
      }
    },
  }));
});

              
            
!
999px

Console