Shadow DOM

Huh? What?

Light DOM vs Shadow DOM

Light DOM is what you use today.

Shadow DOM is what you will be using in the future.

Shadow DOM visually replaces existing Elements.

Hello World

<h3>Hello World</h3>
var heading = currentSlide.querySelector('h3') var root = heading.createShadowRoot()

Shadow DOM is a sub-tree inside a DOM node

Like a mini-iframe that replaces an Element.

Light DOM

<h3>Light DOM</h3>
var heading = currentSlide.querySelector('h3') var root = heading.createShadowRoot() root.innerHTML = 'Shadow DOM'

Shadow DOM = view

HTML = model

You can inject your original content into the Shadow DOM

Light DOM

<a href="#" id="anchor">Light DOM</a>
var anchor = currentSlide.querySelector('#anchor') var root = anchor.createShadowRoot() root.innerHTML = '\ Shadow DOM\ '

How is this useful?

Remember the idea of semantic markup?

Decouple content from presentation.

HEADING
<font size="30px">HEADING</font>
<!-- bad -->

CSS

Great on small scale.

Terrible on big scale.

<!-- vertically centered content -->
<div class="Center-Container is-Table">
  <div class="Table-Cell">
    <div class="Center-Block">
    <!-- content -->
    </div>
  </div>
</div>
.Center-Container.is-Table { display: table; }
.is-Table .Table-Cell {
  display: table-cell;
  vertical-align: middle;
}
.is-Table .Center-Block {
  width: 50%;
  margin: 0 auto;
}

But are divs & classes really 'semantic'?

divs + class abuse is about as good as we get in HTML4

<!-- Bootstrap 2 Panels -->
<div class="panel panel-default">
  <div class="panel-heading">
    <h3 class="panel-title">Panel title</h3>
  </div>
  <div class="panel-body">
    Panel content
  </div>
</div>

Things are getting better

Progress on both ends of the spectrum

HTML5 gives us a lot more semantic elements to work with

<heading>Heading</heading>
<main>Content</main>
<aside>Sidebar</aside>
<footer>Footer</footer>

Reality: HTML5 can't save you from CSS

<div class="page-wrapper">
  <div class="page-wrapper-inner">
    <heading><span>Heading</span></heading>
    <div class="main-wrapper">
      <main>
        <div class="wrapper">
          Content
        </div>
      </main>
    </div>
    <aside>Sidebar</aside>
    <footer><span>Footer</span></footer>
  </div>
</div>
<!-- div spam -->

CSS Flexbox makes CSS more powerful for layouts

Solved by Flexbox

<body class="HolyGrail">
  <header>...</header>
  <div class="HolyGrail-body">
    <main class="HolyGrail-content">...</main>
    <nav class="HolyGrail-nav">...</nav>
    <aside class="HolyGrail-ads">...</aside>
  </div>
  <footer>...</footer>
</body>
.HolyGrail {
  display: flex;
  min-height: 100vh;
  flex-direction: column;
}

.HolyGrail-body {
  display: flex;
  flex: 1;
}

.HolyGrail-content {
  flex: 1;
}

.HolyGrail-nav, .HolyGrail-ads {
  /* 12em is the width of the columns */
  flex: 0 0 12em;
}

.HolyGrail-nav {
  /* put the nav on the left */
  order: -1;
}

When you need a div you need a div

Pseudo Elements :before :after

#example:before {
   content: "";
   display: block;
   width: 100px;
   height: 100px;
}

Shadow DOM

Hide non-semantic DOM in the Shadow DOM

Main Content

<main>Main Content</main>
// wrap main in a div var main = currentSlide.querySelector('main') var root = main.createShadowRoot() root.innerHTML = '\
\ \
'

Use templates to keep things tidy

Main Content

<main>Main Content</main>
<template>
  <div style="border: 1px solid;">
    <content></content>
  </div>
</template>
// wrap main in a div var main = currentSlide.querySelector('main') var template = currentSlide.querySelector('template') var root = main.createShadowRoot() root.appendChild(template.content)

Wrap different content

Heading
Content

<div class="body">
  <heading>Heading</heading>
  <main class="content">Content</main>
</div>
<template>
  <style>
    .green {
      color: green;
    }
    .blue {
      color: blue;
    }
  </style>
  <div class="green">
    <content select="heading"></content>
  </div>
  <div class="blue">
    <content select=".content"></content>
  </div>
</template>
var body = currentSlide.querySelector('.body') var template = currentSlide.querySelector('template') var root = body.createShadowRoot() root.appendChild(template.content)

Hide style in the Shadow DOM

Specificity wars suck

Main Content

<main>Main Content</main>

/* someone else's CSS */
.slide div main * {
  color: blue;
}

/* need highly specific selector */
/* to override */
body .slide div main * {
  color: red;
}

Ignore the rest of the DOM

Main Content

<main>Main Content</main>
<template>
  <style>
    * {
      color: red;
    }
  </style>
  <content></content>
</template>
var main = currentSlide.querySelector('main') var template = currentSlide.querySelector('template') var root = main.createShadowRoot() root.appendChild(template.content)

Shadow DOM elements are unaffected by existing page styles

Light DOM

<a href="#" id="anchor">Light DOM</a>
<template>
  <a href="#">Shadow DOM</a>
</template>
a {
  color: #B22D1A; /* red */
  text-decoration: none;
}
var anchor = currentSlide.querySelector('#anchor') var template = currentSlide.querySelector('template') var root = anchor.createShadowRoot() root.appendChild(template.content)

Applying page styles

Main Content

<main>Main Content</main>
<template>
  <a href="#"><content></content></a>
</template>
var main = currentSlide.querySelector('main') var template = currentSlide.querySelector('template') root = main.createShadowRoot() root.appendChild(template.content) /* Toggle apply page styles */ root.applyAuthorStyles = !root.applyAuthorStyles

Inheriting Styles

Main Content

<main>Main Content</main>
<template>
  <a href="#"><content></content></a>
</template>
var main = currentSlide.querySelector('main') var template = currentSlide.querySelector('template') root = main.createShadowRoot() root.appendChild(template.content) /* Toggle reset inherited styles */ root.resetStyleInheritance = !root.resetStyleInheritance

Allowing external styling

Main Content

<main id="content">Main Content</main>
<style>
#content::x-thumb {
  background: green;
  width: 10px;
  height: 10px;
  display: block;
}
</style>
var main = currentSlide.querySelector('main') var template = currentSlide.querySelector('template') var root = main.createShadowRoot() var thumb = document.createElement('div'); thumb.part = 'x-thumb'; root.appendChild(thumb);

Styling Native Elements

<progress value="35" max="100"></progress>
/* important: disable native styling */
progress.custom {
  -webkit-appearance: none;
}

/* style pseudo-elements */
progress.custom::-webkit-progress-bar {
  background: burlywood;
}

progress.custom::-webkit-progress-value {
  background: coral;
}
progress = currentSlide.querySelector('progress') progress.value += 5 progress.value %= 100 progress.classList.add('custom')

Styling native elements is crucial

Rebuilding native elements is awful.

You will get it wrong.

Many elements starting to expose their Shadow DOM

https://gist.github.com/afabbro/3759334

Browser Support

Evergreen Browsers, kinda.

Shadow DOM is what you will be using in the future.

Keep up or be left behind.

Questions?