Pen Settings

HTML

CSS

CSS Base

Vendor Prefixing

Add External Stylesheets/Pens

Any URLs added here will be added as <link>s in order, and before the CSS in the editor. You can use the CSS from another Pen by using its URL and the proper URL extension.

+ 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

Auto Save

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="wrapper">
  <section class="details-group">
    <details class="details" open>
      <summary class="details__summary">
        &lt;details&gt; and &lt;summary&gt;
      </summary>
      <div class="details__content">
        <p>The &lt;details&gt; and &lt;summary&gt; elements are used to allow common accordion-style functionality without relying on JavaScript.</p>
      </div>
    </details>

    <details class="details">
      <summary class="details__summary">Making it better</summary>
      <div class="details__content">
        <p>With a little help from JavaScript, we can supplement the default behavior to make an accordion that is super smooth and nicely-styled, but still degrades gracefully.</p>
        <p>This accordion uses JS to calculate heights so we can use CSS transitions without any max-height hacks. It also sets a timeout before removing the <code>open</code> attribute, so the content of the <code>&lt;details&gt;</code> element will stay visible until the height transition is finished.</p>
        <p>The cool part is that since we&rsquo;re using these modern HTML5 elements, the accordion should work just fine even with JavaScript disabled. <strong><em>Gasp!</em> A simple web component in 2018 that doesn&rsquo;t completely fail without JS?! Sound the alarm!</strong>
      </div>
    </details>

    <details class="details">
      <summary class="details__summary">Settings</summary>
      <div class="details__content">
        <ul>
          <li>
            <strong><code>speed</code></strong> <em>(default: <code>300</code>)</em> Speed of the transition in milliseconds. Setting this in the plugin will override the value specified in the CSS.
          </li>
          <li>
            <strong><code>one_visible</code></strong> <em>(default: <code>false</code>)</em> Determines whether details can be toggled independently, or if only one can be visible at a time.
          </li>
        </ul>
      </div>
    </details>

    <details class="details">
      <summary class="details__summary">Shameless Plug</summary>
      <div class="details__content">
        <p>If you liked this pen, check out <a target="_blank" href="https://keithpickering.github.io">my portfolio site</a>. I&rsquo;m accepting new projects all the time, so drop me a line.</p>
      </div>
    </details>
  </section>
</div>
              
            
!

CSS

              
                @import url('https://fonts.googleapis.com/css?family=Oswald:300|Roboto:300,500');

$spacing: 24px;
$plus-size: 16px;
$plus-thickness: 2px;
$speed: 300ms;
$easing: ease-in-out;
$gray-dark: #546E7A;
$gray: #CFD8DC;
$gray-light: #ECEFF1;
$primary: #00ACC1;

html, body {
  min-height: 100%;
}

html {
  overflow-y: scroll;
  font-size: 18px;
}

body {
  font-size: 1rem;
  font-weight: 300;
  font-family: Roboto, sans-serif;
  line-height: 1.4;
  color: $gray-dark;
  background-color: $gray-light;
}

p, ul, li {
  margin: 0;
  padding: 0;
  margin-bottom: $spacing;
  
  &:last-child {
    margin-bottom: 0;
  }
}

a { color: $primary; }

code { background: $gray-light; }

.wrapper {
  max-width: 640px;
  margin-left: auto;
  margin-right: auto;
  padding: $spacing*2;
}

.details-group {
  border: 1px solid $gray;
  border-radius: 5px;
  background-color: white;
}

.details {
  overflow: hidden;
  border-bottom: 1px solid $gray;
  transition: height $speed $easing;
  
  &:last-child {
    border-bottom: 0;
  }
  
  &__summary,
  &__content {
    padding: $spacing;
  }
  
  &__summary {
    position: relative;
    list-style: none; // Hide the marker (proper method)
    padding-left: $spacing*2;
    outline: 0;
    cursor: pointer;
    font-size: 1.4rem;
    font-family: Oswald;
    text-transform: uppercase;
    transition: color $speed $easing;
    
    [open] > & {
      color: $primary;
    }
    
    // Hide the marker in Webkit
    &::-webkit-details-marker {
      display: none;
    }
    
    // Our replacement marker
    &:before,
    &:after {
      content: "";
      position: absolute;
    }
    
    &:before {
      left: $spacing/2 + $plus-size/2;
      top: 50%;
      height: $plus-thickness;
      margin-top: -$plus-thickness/2;
      width: $plus-size;
      background: $primary;
    }
    
    &:after {
      left: $spacing/2 + $plus-size;
      top: 50%;
      height: $plus-size;
      margin-top: -$plus-size/2;
      width: $plus-thickness;
      margin-left: -$plus-thickness/2;
      background: $primary;
      transition: all $speed $easing;
    }
    
    [open] &:after {
      opacity: 0;
      transform: translateY(25%);
    }
  }
  
  &__content {
    padding-top: 0;
    padding-left: $spacing*2; 
  }
}
              
            
!

JS

              
                class Details {
  constructor(el, settings) {
    this.group    = el;
    this.details  = this.group.getElementsByClassName("details");
    this.toggles  = this.group.getElementsByClassName("details__summary");
    this.contents = this.group.getElementsByClassName("details__content");    
    
    // Set default settings if necessary
    this.settings = Object.assign({
      speed: 300,
      one_visible: false
    }, settings);
    
    // Setup inital positions
    for (let i = 0; i < this.details.length; i++) {
      const detail  = this.details[i];
      const toggle  = this.toggles[i];
      const content = this.contents[i];
      
      // Set transition-duration to match JS setting
      detail.style.transitionDuration = this.settings.speed + "ms";
      
      // Set initial height to transition from
      if (!detail.hasAttribute("open")) {
        detail.style.height = toggle.clientHeight + "px";
      } else {
        detail.style.height = toggle.clientHeight + content.clientHeight + "px";
      }
    }
    
    // Setup click handler
    this.group.addEventListener("click", (e) => {      
      if (e.target.classList.contains("details__summary")) {
        e.preventDefault();
        
        let num = 0;
        for (let i = 0; i < this.toggles.length; i++) {
          if (this.toggles[i] === e.target) {
            num = i;
            break;
          }
        }
        
        if (!e.target.parentNode.hasAttribute("open")) {
          this.open(num);
        } else {
          this.close(num);
        }
      }
    });
  }
  
  open(i) {
    const detail = this.details[i];
    const toggle = this.toggles[i];
    const content = this.contents[i];
    
    // If applicable, hide all the other details first
    if (this.settings.one_visible) {
      for (let a = 0; a < this.toggles.length; a++) {
        if (i !== a) this.close(a);
      }
    }
    
    // Update class
    detail.classList.remove("is-closing");
    
    // Get height of toggle
    const toggle_height = toggle.clientHeight;
    
    // Momentarily show the contents just to get the height
    detail.setAttribute("open", true);
    const content_height = content.clientHeight;
    detail.removeAttribute("open");
    
    // Set the correct height and let CSS transition it
    detail.style.height = toggle_height + content_height + "px";
    
    // Finally set the open attr
    detail.setAttribute("open", true);
  }
  
  close(i) {
    const detail = this.details[i];
    const toggle = this.toggles[i];
    
    // Update class
    detail.classList.add("is-closing");
    
    // Get height of toggle
    const toggle_height = toggle.clientHeight;
    
    // Set the height so only the toggle is visible
    detail.style.height = toggle_height + "px";
    
    setTimeout(() => {
      // Check if still closing
      if (detail.classList.contains("is-closing"))
        detail.removeAttribute("open");
        detail.classList.remove("is-closing");
    }, this.settings.speed);
  }
}

(() => {
  const els = document.getElementsByClassName("details-group");
  
  for (let i = 0; i < els.length; i++) {
    const details = new Details(els[i], {
      speed: 500,
      one_visible: true
    });
  }
})();
              
            
!
999px

Console