{"id":284470,"date":"2019-03-18T05:47:46","date_gmt":"2019-03-18T12:47:46","guid":{"rendered":"http:\/\/css-tricks.com\/?p=284470"},"modified":"2019-03-22T05:28:03","modified_gmt":"2019-03-22T12:28:03","slug":"an-introduction-to-web-components","status":"publish","type":"post","link":"https:\/\/css-tricks.com\/an-introduction-to-web-components\/","title":{"rendered":"An Introduction to Web Components"},"content":{"rendered":"

Front-end development moves at a break-neck pace. This is made evident by the myriad articles, tutorials, and Twitter threads bemoaning the state of what once was a fairly simple tech stack. In this article, I\u2019ll discuss why Web Components are a great tool to deliver high-quality user experiences without complicated frameworks or build steps and that don\u2019t run the risk of becoming obsolete. In subsequent articles of this five-part series, we will dive deeper into each of the specifications.<\/p>\n

This series assumes a basic understanding of HTML, CSS, and JavaScript. If you feel weak in one of those areas, don\u2019t worry, building a custom element actually simplifies many complexities in front-end development.<\/p>\n

<\/p>\n

\n

Article Series:<\/h4>\n
    \n
  1. An Introduction to Web Components (This post<\/em>)<\/li>\n
  2. Crafting Reusable HTML Templates<\/a><\/li>\n
  3. Creating a Custom Element from Scratch<\/a><\/li>\n
  4. Encapsulating Style and Structure with Shadow DOM<\/a><\/li>\n
  5. Advanced Tooling for Web Components<\/a><\/li>\n<\/ol>\n<\/div>\n
    \n

    What are Web Components, anyway?<\/h3>\n

    Web Components consist of three separate technologies that are used together:<\/p>\n

      \n
    1. Custom Elements.<\/strong> Quite simply, these are fully-valid HTML elements with custom templates, behaviors and tag names (e.g. <one-dialog><\/code>) made with a set of JavaScript APIs. Custom Elements are defined in the HTML Living Standard specification<\/a>.<\/li>\n
    2. Shadow DOM.<\/strong> Capable of isolating CSS and JavaScript, almost like an <iframe><\/code>. This is defined in the Living Standard DOM specification<\/a>.<\/li>\n
    3. HTML templates.<\/strong> User-defined templates in HTML that aren\u2019t rendered until called upon. The <template><\/code> tag is defined in the HTML Living Standard specification<\/a>.<\/li>\n<\/ol>\n

      These are what make up the Web Components specification.<\/p>\n

      HTML Modules<\/a> is likely to be the fourth technology in the stack, but it has yet to be implemented in any of the big four browsers. The Chrome team has announced it an intent to implement them in a future release<\/a>.<\/p>\n

      Web Components are generally available in all of the major browsers with the exception of Microsoft Edge and Internet Explorer 11, but polyfills exist to fill in those gaps<\/a>.<\/p>\n

      Referring to any of these as Web Components is technically accurate because the term itself is a bit overloaded. As a result, each of the technologies can be used independently or combined with any of the others. In other words, they are not mutually exclusive.<\/p>\n

      Let\u2019s take a quick<\/em> look at each of those first three. We\u2019ll dive deeper into them in other articles in this series.<\/p>\n

      Custom elements<\/h3>\n

      As the name implies, custom elements<\/strong> are HTML elements, like <div><\/code>, <section><\/code> or <article><\/code>, but something we can name ourselves that are defined via a browser API. Custom elements are just like those standard HTML elements \u2014 names in angle brackets \u2014 except they always<\/em> have a dash in them, like <news-slider><\/code> or <bacon-cheeseburger><\/code>. Going forward, browser vendors have committed not to create new built-in elements containing a dash in their names to prevent conflicts.<\/p>\n

      Custom elements contain their own semantics, behaviors, markup and can be shared across frameworks and browsers. <\/p>\n

      class MyComponent extends HTMLElement {\r\n  connectedCallback() {\r\n    this.innerHTML = `<h1>Hello world<\/h1>`;\r\n  }\r\n}\r\n    \r\ncustomElements.define('my-component', MyComponent);<\/code><\/pre>\n

      \nSee the Pen
      \nCustom elements demo<\/a> by Caleb Williams (
      @calebdwilliams<\/a>)
      \non
      CodePen<\/a>.<\/span>\n<\/p>\n

      In this example, we define <my-component><\/code>, our very own HTML element. Admittedly, it doesn\u2019t do much, however this is the basic building block of a custom element. All custom elements must in some way extend an HTMLElement<\/code> in order to be registered with the browser. <\/p>\n

      Custom elements exist without third-party frameworks and the browser vendors are dedicated to the continued backward compatibility of the spec, all but guaranteeing that components written according to the specifications will not suffer from breaking API changes. What\u2019s more, these components can generally be used out-of-the-box with today\u2019s most popular frameworks<\/a>, including Angular, React, Vue, and others with minimal effort.<\/p>\n

      Shadow DOM<\/h3>\n

      The shadow DOM<\/strong> is an encapsulated version of the DOM. This allows authors to effectively isolate DOM fragments from one another, including anything that could be used as a CSS selector and the styles associated with them. Generally, any content inside of the document\u2019s scope is referred to as the light DOM,<\/strong> and anything inside a shadow root is referred to as the shadow DOM. <\/p>\n

      When using the light DOM, an element can be selected by using document.querySelector('selector')<\/code> or by targeting any element\u2019s children by using element.querySelector('selector'<\/code>); in the same way, a shadow root\u2019s children can be targeted by calling shadowRoot.querySelector<\/code> where shadowRoot<\/code> is a reference to the document fragment \u2014 the difference being that the shadow root\u2019s children will not be select-able from the light DOM. For example, If we have a shadow root with a <button><\/code> inside of it, calling shadowRoot.querySelector('button')<\/code> would return our button, but no invocation of the document\u2019s query selector will return that element because it belongs to a different DocumentOrShadowRoot<\/code> instance. Style selectors work in the same way. <\/p>\n

      In this respect, the shadow DOM works sort of like an <iframe><\/code> where the content is cut off from the rest of the document; however, when we create a shadow root, we still have total control over that part of our page, but scoped to a context. This is what we call encapsulation<\/strong>. <\/p>\n

      If you\u2019ve ever written a component that reuses the same id<\/code> or relies on either CSS-in-JS tools or CSS naming strategies (like BEM<\/a>), shadow DOM has the potential to improve your developer experience.<\/p>\n

      Imagine the following scenario:<\/p>\n

      <div>\r\n  <div id=\"example\">\r\n    <!-- Pseudo-code used to designate a shadow root -->\r\n    <#shadow-root>\r\n      <style>\r\n      button {\r\n        background: tomato;\r\n        color: white;\r\n      }\r\n      <\/style>\r\n      <button id=\"button\">This will use the CSS background tomato<\/button>\r\n    <\/#shadow-root>\r\n  <\/div>\r\n  <button id=\"button\">Not tomato<\/button>\r\n<\/div><\/code><\/pre>\n

      Aside from the pseudo-code of <#shadow-root><\/code> (which is used here to demarcate the shadow boundary which has no HTML element), the HTML is fully valid. To attach a shadow root to the node above, we would run something like:<\/p>\n

      const shadowRoot = document.getElementById('example').attachShadow({ mode: 'open' });\r\nshadowRoot.innerHTML = `<style>\r\nbutton {\r\n  color: tomato;\r\n}\r\n<\/style>\r\n<button id=\"button\">This will use the CSS color tomato <slot><\/slot><\/button>`;<\/code><\/pre>\n

      A shadow root can also include content from its containing document by using the <slot><\/code> element. Using a slot will drop user content from the outer document at a designated spot in your shadow root. <\/p>\n

      \nSee the Pen
      \nShadow DOM style encapsulation demo<\/a> by Caleb Williams (
      @calebdwilliams<\/a>)
      \non
      CodePen<\/a>.<\/span>\n<\/p>\n

      HTML templates<\/h3>\n

      The aptly-named HTML <template><\/code> element allows us to stamp out re-usable templates of code inside a normal HTML flow that won\u2019t be immediately rendered, but can be used at a later time. <\/p>\n

      <template id=\"book-template\">\r\n  <li><span class=\"title\"><\/span> &mdash; <span class=\"author\"><\/span><\/li>\r\n<\/template>\r\n\r\n<ul id=\"books\"><\/ul><\/code><\/pre>\n

      The example above wouldn\u2019t render any content until a script has consumed the template, instantiated the code and told the browser what to do with it.<\/p>\n

      const fragment = document.getElementById('book-template');\r\nconst books = [\r\n  { title: 'The Great Gatsby', author: 'F. Scott Fitzgerald' },\r\n  { title: 'A Farewell to Arms', author: 'Ernest Hemingway' },\r\n  { title: 'Catch 22', author: 'Joseph Heller' }\r\n];\r\n\r\nbooks.forEach(book => {\r\n  \/\/ Create an instance of the template content\r\n  const instance = document.importNode(fragment.content, true);\r\n  \/\/ Add relevant content to the template\r\n  instance.querySelector('.title').innerHTML = book.title;\r\n  instance.querySelector('.author').innerHTML = book.author;\r\n  \/\/ Append the instance ot the DOM\r\n  document.getElementById('books').appendChild(instance);\r\n});<\/code><\/pre>\n

      Notice that this example creates a template (<template id=\"book-template\"><\/code>) without any other Web Components technology, illustrating again that the three technologies in the stack can be used independently or collectively.<\/p>\n

      Ostensibly, the consumer of a service that utilizes the template API could write a template of any shape or structure that could be created at a later time. Another page on a site might use the same service, but structure the template this way:<\/p>\n

      <template id=\"book-template\">\r\n  <li><span class=\"author\"><\/span>'s classic novel <span class=\"title\"><\/span><\/li>\r\n<\/template>\r\n\r\n<ul id=\"books\"><\/ul><\/code><\/pre>\n

      \nSee the Pen
      \nTemplate example<\/a> by Caleb Williams (
      @calebdwilliams<\/a>)
      \non
      CodePen<\/a>.<\/span>\n<\/p>\n

      That wraps up our introduction to Web Components<\/h3>\n

      As web development continues to become more and more complicated, it will begin to make sense for developers like us to begin deferring more and more development to the web platform itself which has continued to mature. The Web Components specifications are a set of low-level APIs that will continue to grow and evolve as our needs as developers evolve.<\/p>\n

      In the next article, we will take a deeper look at the HTML templates part of this. Then, we\u2019ll follow that up with a discussion of custom elements and shadow DOM. Finally, we\u2019ll wrap it all up by looking at higher-level tooling and incorporation with today\u2019s popular libraries and frameworks.<\/p>\n

      \n

      Article Series:<\/h4>\n
        \n
      1. An Introduction to Web Components (This post<\/em>)<\/li>\n
      2. Crafting Reusable HTML Templates<\/a><\/li>\n
      3. Creating a Custom Element from Scratch<\/a><\/li>\n
      4. Encapsulating Style and Structure with Shadow DOM<\/a><\/li>\n
      5. Advanced Tooling for Web Components<\/a><\/li>\n<\/ol>\n<\/div>\n","protected":false},"excerpt":{"rendered":"

        Front-end development moves at a break-neck pace. This is made evident by the myriad articles, tutorials, and Twitter threads bemoaning the state of what once was a fairly simple tech stack. In this article, I\u2019ll discuss why Web Components are a great tool to deliver high-quality user experiences without complicated frameworks or build steps and […]<\/p>\n","protected":false},"author":38145,"featured_media":279570,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_bbp_topic_count":0,"_bbp_reply_count":0,"_bbp_total_topic_count":0,"_bbp_total_reply_count":0,"_bbp_voice_count":0,"_bbp_anonymous_reply_count":0,"_bbp_topic_count_hidden":0,"_bbp_reply_count_hidden":0,"_bbp_forum_subforum_count":0,"sig_custom_text":"","sig_image_type":"featured-image","sig_custom_image":0,"sig_is_disabled":false,"inline_featured_image":false,"c2c_always_allow_admin_comments":false,"footnotes":"","jetpack_publicize_message":"We're kicking off a 5-part (!!) series that digs into Web Components, from the concept to each API.","jetpack_is_tweetstorm":false,"jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":[]},"categories":[4],"tags":[1536,1619,1317,803],"jetpack_publicize_connections":[],"acf":[],"jetpack_featured_media_url":"https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2018\/11\/toolbox-frameworks-scaled.jpg?fit=2560%2C1280&ssl=1","jetpack-related-posts":[],"featured_media_src_url":"https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2018\/11\/toolbox-frameworks-scaled.jpg?fit=1024%2C512&ssl=1","_links":{"self":[{"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/posts\/284470"}],"collection":[{"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/users\/38145"}],"replies":[{"embeddable":true,"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/comments?post=284470"}],"version-history":[{"count":20,"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/posts\/284470\/revisions"}],"predecessor-version":[{"id":285242,"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/posts\/284470\/revisions\/285242"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/media\/279570"}],"wp:attachment":[{"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/media?parent=284470"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/categories?post=284470"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/tags?post=284470"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}