Building a Pull-Quote Custom Element with Polymer

Pankaj Parashar
Share

Custom elements allow developers to define their own HTML elements with custom tag names that closely resemble their design components. The W3C defines custom elements as the specification that:

Enables authors to define and use new types of DOM elements in a document.

The definition looks quite simple in theory, but in practice the process of creating and using a custom element involves:

  1. A decent amount of JavaScript
  2. Knowledge of the JavaScript API(s) to create the custom element
  3. Handling cross-browser inconsistencies.

If you manage to survive through these challenges, then you still have to worry about those browsers that do not support custom elements. Thankfully, Polymer saves the day by providing an easy-to-use framework to create and use custom elements.

If you need a quick refresher on Polymer and Web components then I would strongly recommend reading my previous article on SitePoint that introduces these concepts in depth.

In this article, we will take a very basic example of creating a custom element with Polymer to render pull quotes. The idea originated from this article by Louis Lazaris where he sparked a debate on the semantic value of using blockquote vs aside to create pull quotes in your markup.

Marking up pull-quotes

It’s fairly common to see blockquote, aside, or even a plain div element being used to mark up pull quotes. It’s time we put an end to this conundrum by creating a new custom element called <pull-quote> that can accurately represent our content. By the end of this article you will be able to create something like this:

Custom pull-quote element

Setting Up the Custom Element

Before we begin with our custom element, we will quickly set up the project folder by installing Polymer and all the dependencies via Bower.

$ bower install --save Polymer/polymer

Typically, creating a new custom element involves the following steps:

  1. Create an HTML file with the name of the custom element.
  2. Import the polymer.html file from the bower_components folder using <link>.
  3. Declare your custom element using <polymer-element>.
  4. Add the styles and the markup needed to define your custom element using the <template> tag.
  5. If your custom element involves JavaScript, call the Polymer() constructor to register the element in the DOM.

The basic approach is to first decide how you are going to use the custom element in your markup and then work your way backwards to create the template for the custom element. In this case, we plan to use the following markup every time we intend to use pull quotes in our document:

<pull-quote>Sample text and can even contain other HTML tags.</pull-quote>

We’ll start by creating a file called pull-quote.html in the root directory. Edit the file and import the polymer.html file that can be found in the bower_components folder:

<link rel="import" href="bower_components/polymer/polymer.html">

We’ll declare our new <pull-quote> element using the name attribute of <polymer-element> in the following way:

<polymer-element name="pull-quote" noscript>
    <!-- More to come -->
</polymer-element>

Couple of points to note:

  1. The name of the custom element must contain a hyphen (U+002D HYPHEN-MINUS) character to allow the HTML parsers to differentiate custom elements from regular HTML elements.
  2. The noscript attribute indicates that this element doesn’t need any JavaScript to function and hence can be immediately registered in the DOM.

Adding the Content

The content that makes up the custom element is defined inside the <template> tag. The <template> tag provides separation of presentation from content. In our case, anything we write inside the <pull-quote> tag would make up the content of the custom element and the styles applied to it would form the presentation.

In nutshell, there are three types of DOM elements that we are dealing with:

  1. Light DOM – This includes what we write inside the custom element. Hence, the user of the custom element supplies the Light DOM.
  2. Shadow DOM – This includes everything that we define within the <template> tag. It is internal to the element and encompasses everything that is required to make the custom element work.
  3. Composed DOM – This is what the browser actually renders. For rendering, the light DOM is distributed into the shadow DOM to produce the composed DOM.

Types of DOM elements in a Custom Element

The <content> tag represents the insertion point where the Light DOM content would be injected into the Shadow DOM. The <content> tag has much more advanced usage in selecting only specific content that you write inside the custom element. However, for the sake of this example we’ll stick to the basic usage of this tag.

<polymer-element name="pull-quote" noscript>
    <template>
        <content></content>
    </template>
</polymer-element>

Styling the Custom Element

For styling our custom element, Polymer provides a bunch of useful selectors. For example:

  1. :host – Targets the custom element itself.
  2. :host(<pseudo-class>) – Targets the various states of the custom element like :host(:hover), :host(:active) etc.
  3. :host(<class-name>) – Targets only those custom elements that have the supplied class name.
  4. :host-context(<selector>) – This pseudo class targets the host element if it or any of its ancestors matches <selector>. This is particularly useful for theming when you apply theme-related classes on <html> or <body> tags.

Pro tip – If you simply use selectors (like, p, #id, or .class) without the :host then they would automatically become descendent selectors. For example, to target a paragraph tag inside the <pull-quote> we can simply use, p { ... } and then it will automatically be expanded as pull-quote p { ... }.

We can either choose to style our custom element using the <style> tag:

<template>
    <style>
        :host {
            display: inline-block;
            font-style: italic;
            text-align: justify;
            width: 325px;
            line-height: 30px;
        }
    </style>
    <content></content>
</template>

Or we can write our CSS in a new file like pull-quote.css and then import that file into our pull-quote.html file using <link>:

<link rel="stylesheet" href="pull-quote.css">

A couple of points to note:

  1. By default, all custom elements are set as display: inline. Hence, we have to explicitly declare display: inline-block.
  2. The :host selector has the lowest specificity. Hence, users can override your styles from outside the custom element.

At the time of writing, using pseudo elements (::before and ::after) on the :host selector like :host::before {...} does not work as expected in Blink browsers (Chrome and Opera). This appears to be a known bug that has been fixed and will be shipped soon with Chrome 38. But this shouldn’t stop us from using them altogether.

To surround our pull-quotes with actual quotes, we’ve got three options.

Adding Quotes with :host Pseudo-Elements

The first option is to use :host::before and :host::after pseudo-elements from within the custom element to add double-quotes:

:host::before {
  content: '\201C';
}
:host::after {
  content: '\201D';
}

As described above, this would work only in non-Blink browsers. If you could afford to wait till the other browsers fix the bug then stick with this method.

Adding Quotes with Regular Pseudo-Elements

You can also use pull-quote::before and pull-quote::after pseudo-elements to add double-quotes from outside the custom element.

pull-quote::before {
  content: '\201C';
}
pull-quote::after {
  content: '\201D';
}

This works in all browsers but may not be the most ideal solution keeping in mind that the styles for the same custom element are now fragmented in two different files.

Adding Quotes with Extra HTML

The last option is to use HTML to add double quotes in the template itself:

<template>
    <span>&quot;</span>
    <content></content>
    <span>&quot;</span>
</template>

This works in all browsers too! Additionally, you could also style the quotes with CSS that can then sit inside the <template> tag.

Using Our Finished pull-quote Element

Make sure you add the platform.js polyfill in the <head> of your document. This polyfill will ensure that your custom elements continue to work as expected even in browsers that do not support them.

<script src="bower_components/platform/platform.js"></script>

To use the custom elements, we do exactly what I described in my previous article:

  1. Download the Custom Element package via Bower.
  2. Import the corresponding .html file in your document.
  3. Use the custom element markup anywhere in your document.

    <html>
        <head>
            <script src="bower_components/platform/platform.js"></script>
            <link rel="import" href="pull-quote.html">
        </head>
        <body>
            <pull-quote>Sample text inside pull quote</pull-quote>
    
            <pull-quote>
                <p>Can also contain other tags</p>
            </pull-quote>
        </body>
    </html>

Check out the complete code along-with the demo on Plunker. I’ve also setup a project repository on Github for the pull-quote custom element. And if you want more, CustomElements.io, a project by Zeno Rocha has a useful collection of custom elements created using Polymer, X-Tag, etc.

The Polymer library provides a vast amount of powerful features and APIs to interact with custom elements and other web components. We’ve barely touched the topic and still have a lot of features to explore. However, I am hopeful that this introduction would be simple enough for everyone to understand and serve as a springboard to build many more new custom elements.