{"id":171586,"date":"2014-06-02T08:52:05","date_gmt":"2014-06-02T15:52:05","guid":{"rendered":"http:\/\/css-tricks.com\/?p=171586"},"modified":"2014-10-24T18:58:43","modified_gmt":"2014-10-25T01:58:43","slug":"using-encapsulation-semantic-markup","status":"publish","type":"post","link":"https:\/\/css-tricks.com\/using-encapsulation-semantic-markup\/","title":{"rendered":"Using Encapsulation for Semantic Markup"},"content":{"rendered":"

The following is a guest article by Chris Scott<\/a>. Chris takes us through a great use case for the Shadow DOM. As designers, we may want to style something in a certain way, but sometimes end up having to go to war with HTML, CSS, and JS to get it done. And even then, the results can be weighty, hacky, and unsemantic. Shadow DOM might be able to save us from that, giving us a fresh place to use whatever HTML we need (need 20 empty elements? No problem!) without exposing that mess to the actual DOM (which would suck for accessibility, semantics, etc.)<\/em><\/p>\n

<\/p>\n

File inputs are notoriously hard to style. Let’s say you want to use an SVG icon instead<\/em> of the default button-and-filename styling.<\/p>\n

\"onjective\"<\/figure>\n

That is not a trivial styling change. Input elements<\/a> are “no content” elements, i.e. an element that doesn’t have a closing tag. Thus, there is nowhere to put an SVG element “inside” of it. So how might you go about this?<\/p>\n

Well, let’s take a progressive enhancement approach and start with the basic functionality:<\/p>\n

<form>\r\n  <input type=\"file\"><\/input>\r\n<\/form><\/code><\/pre>\n

What do we get out of the box? Semantically, it’s very descriptive: there’s an element that allows the user to input a file. It’s also functional: if you click on it a file system dialogue opens.<\/p>\n

Now let’s think about adding the icon. You could put the image near the input, perhaps using z-indexes and transparent inputs to retain the functionality of the input (like this<\/a>). This approach is okay semantically (you have an input and<\/em> an image) but it definitely feels hacky and is more difficult to test. Alternatively, you could use an <img><\/code> tag and drop the <input><\/code> altogether, but then you have all the issues of replicating the file input’s functionality and you have lost a bunch of meaning from the markup.<\/p>\n

(There are other ways you could go about this as well. For instance a label-with-hidden-input, but for the sake of getting to the point here, let’s move on.)<\/p>\n

Shadow DOM<\/h3>\n

A better approach, in my opinion, is using Shadow DOM. Shadow DOM is one of the ingredients to Web Components, and you can read up on all that here<\/a>. I just want to talk about its semantic benefits, for now. <\/p>\n

Shadow DOM is described in the W3C draft spec<\/a> as functional encapsulation<\/q> or functional boundaries<\/q>. That means we can encapsulate<\/em> some design and pin it to an existing node on the DOM tree (called the Shadow Root). Returning to our example:<\/p>\n

\n
var button = document.querySelector(&#x27;input[type=&quot;file&quot;]&#x27;);\r\nvar shadowDom = button.webkitCreateShadowRoot();\r\nshadowDom.innerHTML = &quot;&lt;div&gt;Hello from the other side&lt;\/div&gt;&quot;;<\/code><\/pre>\n

See the Pen gHEei<\/a> by chrismichaelscott (@chrismichaelscott<\/a>) on CodePen<\/a>.<\/p>\n<\/div>\n

Take a look at the result in the Pen above. This may look like a long-winded way of changing some HTML but that’s not actually what happened. If you were to “inspect element” in your browser you would still see the original file input – no sign of a div<\/code> at all. More to the point, if you click on the string the browser will pop up a file system dialogue. This is the crux of encapsulation: from the outside the input is still an input<\/code> but if you traverse the functional boundary<\/q> then it’s not an input<\/code> at all, it’s a string. That means that we can make an input (or any other tag, for that matter) render in any way want. We can set the inner HTML of the Shadow Root to be any valid HTML and it will render as such. Including, of course, adding SVG.<\/p>\n

\n
var button = document.querySelector(&#x27;input[type=&quot;file&quot;]&#x27;);\r\nvar shadowDom = button.webkitCreateShadowRoot();\r\nshadowDom.innerHTML = &quot;&lt;img src=\\&quot;https:\/\/s3-eu-west-1.amazonaws.com\/chrisscott\/codepen\/iconmonstr-archive-7-icon.svg\\&quot; alt=\\&quot;Select file\\&quot;&gt;&lt;\/img&gt;&quot;;<\/code><\/pre>\n

See the Pen jiqoh<\/a> by chrismichaelscott (@chrismichaelscott<\/a>) on CodePen<\/a>.<\/p>\n<\/div>\n

Clearly that isn’t a particularly good looking alternative, but it’s a start. And the HTML is simple and semantically correct. To take this further let’s look at another tool in the HTML5 Web Components toolbox that’s useful for this kind of thing: template<\/code> tags<\/a>.<\/p>\n

Templates<\/h3>\n

Template tags are basically a way of building HTML that is not rendered. The cool thing about templates is that you can use JavaScript to inject the DOM from a template into an element elsewhere in the document. In this case, we can define a template for our fancy vector image file input and use that to define the Shadow DOM for actual input<\/code> tags. Here’s a suitable template:<\/p>\n

<template id=\"file-button-template\">\r\n  <style>\r\n    img {\r\n      padding: 6px;\r\n      border: 1px solid grey;\r\n      border-radius: 4px;\r\n      box-shadow: 1px 1px 4px grey;\r\n      background-color: lightgrey;\r\n      width: 30px;\r\n      cursor: pointer;\r\n    }\r\n    img:hover {\r\n      background-color: whitesmoke;\r\n    }\r\n  <\/style>\r\n  <img src=\"https:\/\/s3-eu-west-1.amazonaws.com\/chrisscott\/codepen\/iconmonstr-archive-7-icon.svg\" alt=\"Select file\"><\/img>\r\n<\/template><\/code><\/pre>\n

Bringing it all together<\/h3>\n

The template<\/code> element can be placed into the head<\/code> of a document, and I think that’s a nice place for it. The following Pen has the template we just defined in the head<\/code>. If you open it on CodePen.io<\/a> you can see the template by clicking the cog in the top left of the HTML panel.<\/p>\n

See the Pen jghes<\/a> by chrismichaelscott (@chrismichaelscott<\/a>) on CodePen<\/a>.<\/p>\n

Pretty good, I reckon. Semantically, it’s exactly the same as the vanilla HTML but it is visually completely bespoke.<\/p>\n

Browser support, polyfills and graceful degradation<\/h3>\n

There’s some bad news (then more bad news and then a little, tiny glimmer of good news) in terms of support for Shadow DOM in browsers.<\/p>\n

The first piece of bad news is that editing Shadow DOM is, currently, only supported in WebKit\/Blink browsers, using the prefixed element.webkitCreateShadowRoot()<\/code> method. Firefox 30 should support Shadow DOM<\/a> as well, though.<\/p>\n

Bad news number two: Polyfils may not support this use case – certainly Polymer<\/a> doesn’t. In the case of the Polymer platform, to understand why the polyfill doesn’t behave as the native implementation does one needs to understand the mechanics of the polyfill<\/a>. Essentially, the Polymer platform performs it’s encapsulation by replacing native DOM nodes with wrappers<\/em>. These wrappers emulate the behaviour of actual DOM nodes; for example, you can set the innerHTML<\/code> of the wrapper as you would a native node. The wrappers maintain the so-called light DOM and shadow DOM. In terms of encapsulation, this works fairly well as the wrapper can intercept calls on the light side (such as the children<\/code> getter) and hide the shadow DOM.<\/p>\n

\n
var light = document.querySelector(&quot;div&quot;);\r\nvar template = document.querySelector(&quot;template&quot;);\r\n\r\nlight.createShadowRoot().innerHTML = template.innerHTML;\r\n\r\ndocument.querySelector(&quot;code&quot;).innerHTML = \r\n  light.innerHTML<\/code><\/pre>\n

See the Pen FCknf<\/a> by chrismichaelscott (@chrismichaelscott<\/a>) on CodePen<\/a>.<\/p>\n<\/div>\n

In this example, the retieved inner HTML of the “light” DOM show the encapsulation working (there is no h1<\/code> tag). However, the polyfill has not used actual Shadow DOM (otherwise it wouldn’t be a polyfill!) and you can verify that if you inspect the first div from the results. In the DOM, you will see, there is an h1<\/code> tag – not hidden in the Shadow DOM, just there. That’s because, underneath the wrapper, the Polymer platform is just manipulating the regular DOM tree as there is no shadow root.<\/p>\n

In terms of the file input SVG icon that is not good. If one were to run the demo of the file input with the Polymer polyfill, as with the previous example, it would merely insert the img<\/code> tag as a child of the input. If you recall, that is not allowed. An input is a void<\/i> element and has no permitted child elements, so the browser will just ignore the img<\/code> and style<\/code> elements as invalid children. Here is a fork of the ‘pen illustrating the issue<\/a>; again, inspect the element and you can verify that the polyfill has added the template as children of the input.<\/p>\n

And so, to the promised “tiny glimmer of good news”… Even though support is limited at the moment, I still think that it is justifiable to start using Shadow DOM to do things like this, now. Why? If your user’s browser doesn’t support Shadow DOM it will just render a normal file input. That’s not a bad thing and considering the complex (read: hacky<\/em>) alternatives I think Shadow DOM should be considered a plausible choice.<\/p>\n

Notes on Shadow DOM, Web Components, etc<\/h3>\n

There are a huge number of use-cases for Shadow DOM and the related Web Components. This article has completely overlooked the advantages of encapsulation in terms of developing frameworks and widgets. For those interested it’s worth reading the specs<\/a> and getting to grips with Polymer<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"

The following is a guest article by Chris Scott. Chris takes us through a great use case for the Shadow DOM. As designers, we may want to style something in a certain way, but sometimes end up having to go to war with HTML, CSS, and JS to get it done. And even then, the […]<\/p>\n","protected":false},"author":3,"featured_media":0,"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":"","jetpack_is_tweetstorm":false,"jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":false,"jetpack_social_options":[]},"categories":[4],"tags":[469],"jetpack_publicize_connections":[],"acf":[],"jetpack_featured_media_url":"","jetpack-related-posts":[{"id":178717,"url":"https:\/\/css-tricks.com\/swapping-svg-icons\/","url_meta":{"origin":171586,"position":0},"title":"Swapping Out SVG Icons","date":"August 12, 2014","format":false,"excerpt":"Let's say you're using inline SVG and want to change the SVG icon that is displayed in an element on state change, like changing a class or on :hover\/:focus. There are a number of ways you can approach that. Technique #1: Hide\/Show SVG Element Include both icons: Hide one by\u2026","rel":"","context":"In "Article"","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":305838,"url":"https:\/\/css-tricks.com\/styling-in-the-shadow-dom-with-css-shadow-parts\/","url_meta":{"origin":171586,"position":1},"title":"Styling in the Shadow DOM With CSS Shadow Parts\u00a0","date":"April 13, 2020","format":false,"excerpt":"Safari 13.1 just shipped support for CSS Shadow Parts. That means the ::part() selector is now supported in Chrome, Edge, Opera, Safari, and Firefox. We\u2019ll see why it\u2019s useful, but first a recap on shadow DOM encapsulation\u2026 The benefits of shadow DOM encapsulation I work at giffgaff where we have\u2026","rel":"","context":"In "Article"","img":{"alt_text":"","src":"https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2020\/03\/intersecting-parts.png?fit=1200%2C600&ssl=1&resize=350%2C200","width":350,"height":200},"classes":[]},{"id":333234,"url":"https:\/\/css-tricks.com\/styling-web-components\/","url_meta":{"origin":171586,"position":2},"title":"Styling Web Components","date":"January 29, 2021","format":false,"excerpt":"Nolan Lawson has a little emoji-picker-element that is awfully handy and incredibly easy to use. But considering you'd probably be using it within your own app, it should be style-able so it can incorporated nicely anywhere. How to allow that styling isn't exactly obvious: What wasn\u2019t obvious to me, though,\u2026","rel":"","context":"In "Article"","img":{"alt_text":"","src":"https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2021\/01\/web-blocks.jpg?fit=1200%2C600&ssl=1&resize=350%2C200","width":350,"height":200},"classes":[]},{"id":351915,"url":"https:\/\/css-tricks.com\/shadow-roots-and-inheritance\/","url_meta":{"origin":171586,"position":3},"title":"Shadow Roots and Inheritance","date":"September 16, 2021","format":false,"excerpt":"There is a helluva gotcha with styling a

element, as documented here by Kitty Giraudel. It's obscure enough that you might never run into it, but if you do, I could see it being very confusing (it would confuse me, at least). Perhaps you're aware of the shadow DOM?\u2026","rel":"","context":"In "Link"","img":{"alt_text":"","src":"https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2021\/09\/Screen-Shot-2021-09-15-at-3.57.50-PM.png?fit=1200%2C717&ssl=1&resize=350%2C200","width":350,"height":200},"classes":[]},{"id":151172,"url":"https:\/\/css-tricks.com\/custom-controls-in-html5-video-full-screen\/","url_meta":{"origin":171586,"position":4},"title":"Hiding Native HTML5 Video Controls in Full-Screen Mode","date":"September 26, 2013","format":false,"excerpt":"The following is a guest post by Sara Soueidan. I know Sara through all her excellent work on CodePen. She was working on some custom HTML5 video controls and noticed that the customizations were lost when the video went into full screen mode (example of that happening). Digging into the\u2026","rel":"","context":"In "Article"","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":359938,"url":"https:\/\/css-tricks.com\/on-yak-shaving-and-md-block-a-new-html-element-for-markdown\/","url_meta":{"origin":171586,"position":5},"title":"On Yak Shaving and md-block, a new HTML element for Markdown","date":"December 29, 2021","format":false,"excerpt":"Lea Verou made a Web Component for processing Markdown. Looks like there were a couple of others out there already, but I agree with Lea in that this is a good use case for the light DOM (as opposed to the shadow DOM that is normally quite useful for web\u2026","rel":"","context":"In "Link"","img":{"alt_text":"","src":"https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2021\/12\/MD-BLOCK.png?fit=1200%2C600&ssl=1&resize=350%2C200","width":350,"height":200},"classes":[]}],"featured_media_src_url":null,"_links":{"self":[{"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/posts\/171586"}],"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\/3"}],"replies":[{"embeddable":true,"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/comments?post=171586"}],"version-history":[{"count":2,"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/posts\/171586\/revisions"}],"predecessor-version":[{"id":171592,"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/posts\/171586\/revisions\/171592"}],"wp:attachment":[{"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/media?parent=171586"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/categories?post=171586"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/tags?post=171586"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}