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

              
                <main>
  <o-h>What am I looking at?</o-h>
  <p>For a number of years, in fact, since the very beginning of HTML we have had these 6 weird tags (h1-h6) that we kind of knew weren't quite right but wound up in HTML for legacy reasons.  Yes, I realize it is confusing that you can have "legacy reasons" in things that are brand new, but I describe all of this in my article <a href="http://bkardell.com/blog/On-Headings.html">Headings and the Seinfeld Pitch</a>, but if you want a quick rundown of where all of this stands you can read this <a href="https://bkardell.com/blog/Whats-The-Deal-With-That.html">quick summary</a>..
  </p>
  <section>
    <o-h>In a nutshell?</o-h>
    <p>Ok, in a nutshell when you whiteboard a design for a page you want to talk about stuff.  Not tiny little bits, but stuff that you can draw a box around (even if you don't) and refer back to.  We have to, that's really the only way we can talk about it.</p>
  <p>When we write HTML we put in structures that contain those things, and we give them names so that we can talk about them in terms of style and script.</p>
    <p>But the <em>headings</em> are historically weird.  Headings aren't about containers with names - they're just "in there somewhere and have a level".  We can kind of say "there is a Clients section" and "here's where it starts... kinda" but it's very hard to say "ok, read that section to me" because their relationship to a "section" has been historically largely 'implied'. Most of us don't notice because we've named all the bits we care about - in code, visually, we can make sense of it.  But this is impossibly hard for machines that don't have any of that information available.  We should really fix that.  We could.</p>  

  <p>Given that same kind of information, we could make another kind of tree: A document outline.  That's not a DOM tree, DOM trees are full of all kinds of noise - an outline is a kind of higher level version of that... The sort of level you would use in discussions when designing the <em>outline</em> of it.  The stuff same you refer to in your designs and CSS classes already.  What we want, really, is just for that to be communicated further.</p>   
    <p>This is an attempt to provide a way to describe such an outline, expose it, make it paletteable and encourage good use.  It is not the only way.  It is very potentially not the best way.  A good way to find out is to try it.</p>
    <section class="warning">
	    <o-h non-display>Warning</o-h>
	    <p>What you shouldn't do is read this as a "I should use this because this is what the future will look like."</p>
	    <p>We have no certain promise what the future will look like.</p>
	    <p>Rather, use it because it may help solve problems today. In doing so, it will also give you
	      an idea what that might feel like to solve problems this way. This, in turn, allows you to feed back into the process with more interesting observations and insights, and it can fairly easily fork new experiments which try other variations. Or,
	      just use it as discussion fodder to understand the problem. Either is fine.</p>
	  </section>
    
    <section>
      <o-h>What's different / How does it work?</o-h>
      <p>This approach attempts to keep things very "simple":  There are regions of a page that belong in an outline - major landmarks and sections.  However, to be useful, I think those things need a name.  Sometimes, the name is only there for "machines" (including AT) and sometimes they <em>also</em> play the traditional, more visually oriented role.  An outline heading (<code>&lt;o-h&gt;</code>) has to be the first child of sectioning content - just as in elements like summary/details or fieldset/legend.  Also, just like those others, if you don't do this, it is not just going to invisibly "mean something different" it is going to be (by default) visually apparent.</p>
      
      <p>In the text so far, "What am I looking at?" "In a nutshell?" and "What's different / How does it work?" are all first-children <code>&lt;o-h&gt;</code> of "sectioning elements".  They look different by default, and they mean something in the "document outline".  The text in red that begins "what you shouldn't do" is also a section.  It also has an outline heading as its first child, but this is marked with the attribute <code>non-display</code> which retains its meaning, but simply doesn't show it visually.  <o-h>This sentence is also an  <code>&lt;o-h&gt;</code> in code</o-h>.  It isn't a first child of sectioning content though, there's no way to make sense of it and be meaningful in an outline, so it's just text, and it doesn't look special either.</p>
    </section>
  
  
    <section>
      <o-h>And some DOM...</o-h>
      <p>This also exposes a property on the document object <code>document._getOutlineHeadings()</code> which returns 
      an a tree-like object representing the outline.  The outline 'section nodes' have 3 properties: 
        <dl>
          <dt>.owningSection</dt>
          <dd>Points to the sectioning HTMLElement which is owns this subsection, or null if it is the root.</dd>
          
          <dt>.subSectons</dt>
          <dd>An array of outline section nodes that this section owns</dd>
          
          <dt>.level</dt>
          <dd>A quick reference to the level this outline section node represents in the document outline</dd>
    
      </dl>
    </section>
  </section>
  
</main>
              
            
!

CSS

              
                /* Ha, you thought this would apply to all of them, but it only aplies the ones with meaning!
   yes, it means you need higher specificity to style a particular "level" of contextual heading.  oh friggin well?  Imagine you have a CMS and you create some kind of reusable section.  Do you want it to look the same everywhere?  Then say *that* instead of something about levels.  That's totally fine. 
*/
o-h {
  padding-left: 0.5rem;
  color: #5555cc;
  background-color: #eeeeee;
}

body, main, article, aside, nav, section { 
  border: 1px dotted gray; 
  margin: 0.5rem; 
  padding: 1rem; 
}

.warning {
   font-style: italic;
   color: red; 
}

dl { margin: 2rem; }
dt { font-weight: bold; margin-top: 1rem; margin-bottom: 0.5rem; font-family: monospace; }

body { 
  line-height: 1.5rem;
}
              
            
!

JS

              
                var ct = 0,
  injectDefaultStyles = function(cssString) {
    var styleEl = document.createElement('style');
    styleEl.innerHTML = cssString;
    document.head.insertBefore(styleEl, document.head.firstElementChild);
  };

injectDefaultStyles(`
  [non-display] {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0,0,0,0);
    border: 0;
  }
 o-h:not([role=heading]) { 
     /* this needs a full reset here, if only everyone supported it.  */
     all: initial !important;
     font-weight: normal !important;
     font-size: 1rem !important;
     color: black; 
}

 o-h { 
  display: block;
  font-weight: bold;
  line-height: normal;
}
 o-h[aria-level='1'] {
     font-size: 3em;
 }

o-h[aria-level='2'] {
     font-size: 2.25em;
 }

o-h[aria-level='3'] {
     font-size: 1.75em;
 }

o-h[aria-level='4'] {
     font-size: 1.5em;
 }

o-h[aria-level='5'] {
     font-size: 1.25em;
 }

o-h[aria-level='6'] {
     font-size: 1.15em;
 }


o-h[aria-level='7'] {
     font-size: 1em;
 }

`);

var sectioningSelector = 'body, main, article, aside, nav, section';

document._getOutlineHeadings = function() {
  var headings = Array.prototype.slice.call(document.querySelectorAll('[outline-level]>[role="heading"]')),
    root = {
      owningSection: null,
      subSections: [],
      level: 0
    },
    ctx = root

  headings.forEach(function(item, i) {
    var tmp = ctx,
      itemLevel = item._level
    if (i === 0) {
      //root.heading = item;
    }
    if (itemLevel > ctx.level) {
      tmp = {
        owningSection: ctx,
        subSections: [],
        level: ctx.level + 1,
        heading: item,
        section: item.parentElement
      }
      ctx.subSections.push(tmp)
      ctx = tmp
    } else if (itemLevel === ctx.level) {
      ctx.owningSection.subSections.push({
        owningSection: ctx,
        subSections: [],
        level: ctx.level,
        heading: item,
        section: item.parentElement
      })
    } else if (itemLevel < ctx.level) {
      console.log('this happened...')
    }
  })

  return root
}

class OulineHeadingElement extends HTMLElement {

  get _level() {
    return parseInt(this.getAttribute('aria-level', -1), 10)
  }

  disconnectedCallback() {
    var parent = this.__parentElement,
      next = parent.firstElementChild,
      curLevel = this._level;

    if (curLevel) {
      if (next && next.tagName === 'O-H') {
        next.setAttribute('role', 'heading');
        next.setAttribute('aria-level', curLevel);
      } else {
        parent.removeAttribute('outline-level');
        parent.hasHeading = false;
      }
    }
    delete this.__parentElement;
  }

  connectedCallback() {
    var isParentSectioning = this.parentElement.matches(sectioningSelector)

    if (isParentSectioning && !this.parentElement.hasHeading) {
      // find the closest thing with an outline-level
      var closest = this.parentElement.closest('[outline-level]'),
        // the current closest level is that or 0
        closestLevel = ((closest && closest.getAttribute('outline-level')) || 0)

      closestLevel++;

      console.log('closest ', closest)
      console.log('closestLevel ', closestLevel)

      // set the outline level
      this.parentElement.setAttribute('outline-level', closestLevel)
      console.log('marked heading for ', this.parentElement)

      this.setAttribute('role', 'heading')
      this.setAttribute('aria-level', closestLevel)

      this.parentElement.hasHeading = true
    }
  }
}

customElements.define('o-h', OulineHeadingElement)
              
            
!
999px

Console