The future of responsive web design

Web component queries

Nikos Zinas

Chapter 1

Web components

Web Components are a set of technologies, aimed at giving the developers the tools to create and share custom HTML tags, which fully encapsulate their functionality and style.

Custom Elements

Twitter Bootstrap

<div class="modal fade">
  <div class="modal-dialog">
    <div class="modal-content">
      <div class="modal-header">
        <button type="button" class="close" data-dismiss="modal" aria-label="Close">
			<span aria-hidden="true">×</span>
		</button>
        <h4 class="modal-title">Modal title</h4>
      </div>
      <div class="modal-body">
        <p>One fine body…</p>
      </div>
      <div class="modal-footer">
        <button type="button" class="btn btn-default" data-dismiss="modal">
            Close
        </button>
        <button type="button" class="btn btn-primary">Save changes</button>
      </div>
    </div><!-- /.modal-content -->
  </div><!-- /.modal-dialog -->
</div><!-- /.modal -->

Web components

<twbs-modal animation="fade">
	<twbs-modal-header>
		<button is="twbs-modal-button-close">Close<button>
		<h4>Modal title</h4>
	</twbs-modal-header>
	<p>One fine body…</p>
	<twbs-modal-footer>
		<button is="twbs-modal-button-close" class="btn btn-default">
                    Close
                </button>
		<button class="btn btn-primary">Save changes</button>
	</twbs-modal-footer>
</twbs-modal>

Registering elements

// Create an object based on the HTMLElement prototype
var modal = Object.create(HTMLElement.prototype);

// Register the element
document.register('twbs-modal', {
    prototype: modal
});

// You can also extend existing objects
document.register('twbs-button', {
    prototype: Object.create(HTMLButtonElement.prototype),
    extends: 'button'
});

Per-element APIs

// Create an object based on the HTMLElement prototype
var modal = Object.create(HTMLElement.prototype);

// Define your own functions
modal.hide = function () {
    // do stuff...
};

// Register the element
document.register('twbs-modal', {
    prototype: modal
});
// Assuming <twbs-modal id="login-window"></twbs-modal>
document.getElementById('login-window').hide();

Lifecycle callbacks

// Fires when an instance of the element is created
modal.createdCallback = function() {};

// Fires when an instance was inserted into the document
modal.attachedCallback = function() {};

// Fires when an instance was removed from the document
modal.detachedCallback = function() {};

// Fires when an attribute was added, removed, or updated
modal.attributeChangedCallback = function(attr, oldVal, newVal) {};

Templates

<template></template>
<script type="text/template">
    -- Some HTML content --
</script>
<template id="profile-template">
    <div class="profile">
        <img 
            src="assets/default.png" 
            alt="a faceless profile icon" 
            title="Default Avatar" />
        <h3 class="profile__name"></h3>
        <a href="" class="profile__email"></a>
    </div>
</template>

Content is inert

var content = document.getElementById('profile-template').content;
var node = document.importNode(content, true);

// treat clone as any other node
// eg. document.body.appendChild(node);
<template id="profile-template">
    <div class="profile">
        <img 
            src="assets/default.png" 
            alt="a faceless profile icon" 
            title="Default Avatar" />
        <h3 class="profile__name"></h3>
        <a href="" class="profile__email"></a>
    </div>
</template>

Content is not part of the document

var content = document.getElementById('profile-template').content;
var node = document.importNode(content, true);

// treat clone as any other node
// eg. document.body.appendChild(node);

Shadow DOM

DOM Tree encapsulation

<div id="host-element"></div>

<script>
// Create your shadow root
var root = document.querySelector('#host-element').createShadowRoot();

// add content like in any other node
root.innerHTML = '...';
</script>
<nav class="navbar navbar-default">
  <div class="container-fluid">
    <div class="navbar-header">
      <a class="navbar-brand" href="#">
        <img alt="Brand" src="...">
      </a>
    </div>
  </div>
</nav>
<nav class="navbar navbar-default">
    <a class="navbar-brand" href="#">
        <img alt="Brand" src="...">
    </a>
</nav>
<template id="navbar-template">
    <div class="container-fluid">
        <div class="navbar-header">
            <content></content>
        </div>
    </div>
</template>
var shadowRoot, templateContent, node;

// Handle template
templateContent = document.querySelector('#navbar-template').content;
node = document.importNode(templateContent, true);

// Add to Shadow DOM
shadowRoot = document.querySelector('.navbar').createShadowRoot();
shadowRoot.appendChild(node);

HTML Imports

<link 
    rel="import" 
    href="twbs-modal.html"
    id="twbs-modal-import"
    onload="handleSuccess()"
    onerror="handleError()"
    async>
var content = document.getElementById('twbs-modal-import').import;

// treat content like any other document. eg.
content.querySelector('...');
  • HTML is fetched but not inserted in the importing document
  • Requests are de-duped
  • Scripts are executing in the context of the importing document
  • Styles are applied to the importing document

HTML Imports

The modular web

Chapter 2

Responsive Web Design

Responsive web design (RWD) is a web design approach aimed at crafting sites to provide an optimal viewing experience—easy reading and navigation with a minimum of resizing, panning, and scrolling—across a wide range of devices (from mobile phones to desktop computer monitors).

A bit of history...

Cameron Adams - Resolution dependent layout

2004

Ethan Marcotte (A List Apart) - Responsive web design

2010

Fluid grids

Flexible images

Media queries

Chapter 3

Element Queries

?

Element query is the method of adapting an element's styling, based on the size of its container and not the size of the viewport

Conceptual example

.profile .avatar {
    float: left;
    width: 180px;
}

.profile ( max-width: 320px ) .avatar {
    display: block;
    float: none;
    width: auto;
}

Don't get too excited...

Element queries do not exist

Circular dependency

.container {
    display: inline-block;
}

.container .block {
    width: 200px;
}

.container ( min-width: 150px ) .block {
    width: 100px;
}

Can I do anything today?

eq.js

.profile .avatar {
    float: left;
    width: 180px;
}

.profile[data-eq-state$="small"] .avatar {
    display: block;
    float: none;
    width: auto;
}
<div class="profile" data-eq-pts="small: 300, medium: 600, large: 900">
    <div class="avatar"></div>
</div>

css-element-queries

.profile .avatar {
    float: left;
    width: 180px;
}

.profile[max-width="200px"] .avatar {
    display: block;
    float: none;
    width: auto;
}

elementQuery

.profile .avatar {
    float: left;
    width: 180px;
}

.profile[max-width~="200px"] .avatar {
    display: block;
    float: none;
    width: auto;
}

boomqueries

.profile .avatar {
    display: block;
}

.profile--large .avatar {
    float: left;
    width: 180px;
}
boomQueries.add('.profile', [
    [180, 'profile--large']
]);

Resources

Ian Storm Taylor - Media Queries are a Hack

Think in components

Think responsively

Thank you

Nikos Zinas - @nzinas

The future of responsive web design - web component queries

By Nikos Zinas

The future of responsive web design - web component queries

  • 1,595