CINXE.COM

Introducing Slot-Based Shadow DOM API | WebKit

<!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html;charset=utf-8"> <meta name="robots" content="noodp"> <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=yes, viewport-fit=cover"> <meta name="theme-color" content="hsl(203.6, 100%, 12%)"> <title> Introducing Slot-Based Shadow DOM API | WebKit</title> <meta name="application-name" content="WebKit"> <link rel="stylesheet" type="text/css" href="https://webkit.org/wp-content/themes/webkit/style.css?2022100501" media="all"> <link rel="stylesheet" href="https://www.apple.com/wss/fonts?families=SF+Pro,v1" type="text/css"> <link rel="stylesheet" href="https://www.apple.com/wss/fonts?families=SF+Mono,v2" type="text/css"> <meta name="supported-color-schemes" content="light dark"> <noscript> <img src="https://shynet.webkit.org/ingress/561b9e53-fb8c-4297-ae4d-bde05e8daa59/pixel.gif"> </noscript> <script defer src="https://shynet.webkit.org/ingress/561b9e53-fb8c-4297-ae4d-bde05e8daa59/script.js"></script> <link rel="alternate" type="application/rss+xml" title="RSS 2.0" href="https://webkit.org/feed/"> <link rel="alternate" type="text/xml" title="RSS .92" href="https://webkit.org/feed/rss/"> <link rel="alternate" type="application/atom+xml" title="Atom 0.3" href="https://webkit.org/feed/atom/"> <link rel="pingback" href="https://webkit.org/wp/xmlrpc.php"> <link rel="shortcut icon" sizes="32x32" type="image/x-icon" href="/favicon.ico"> <meta name='robots' content='max-image-preview:large' /> <link rel='stylesheet' id='wp-block-library-css' href='https://webkit.org/wp/wp-includes/css/dist/block-library/style.min.css?ver=6.7' type='text/css' media='all' /> <style id='classic-theme-styles-inline-css' type='text/css'> /*! This file is auto-generated */ .wp-block-button__link{color:#fff;background-color:#32373c;border-radius:9999px;box-shadow:none;text-decoration:none;padding:calc(.667em + 2px) calc(1.333em + 2px);font-size:1.125em}.wp-block-file__button{background:#32373c;color:#fff;text-decoration:none} </style> <style id='global-styles-inline-css' type='text/css'> :root{--wp--preset--aspect-ratio--square: 1;--wp--preset--aspect-ratio--4-3: 4/3;--wp--preset--aspect-ratio--3-4: 3/4;--wp--preset--aspect-ratio--3-2: 3/2;--wp--preset--aspect-ratio--2-3: 2/3;--wp--preset--aspect-ratio--16-9: 16/9;--wp--preset--aspect-ratio--9-16: 9/16;--wp--preset--color--black: #000000;--wp--preset--color--cyan-bluish-gray: #abb8c3;--wp--preset--color--white: #ffffff;--wp--preset--color--pale-pink: #f78da7;--wp--preset--color--vivid-red: #cf2e2e;--wp--preset--color--luminous-vivid-orange: #ff6900;--wp--preset--color--luminous-vivid-amber: #fcb900;--wp--preset--color--light-green-cyan: #7bdcb5;--wp--preset--color--vivid-green-cyan: #00d084;--wp--preset--color--pale-cyan-blue: #8ed1fc;--wp--preset--color--vivid-cyan-blue: #0693e3;--wp--preset--color--vivid-purple: #9b51e0;--wp--preset--gradient--vivid-cyan-blue-to-vivid-purple: linear-gradient(135deg,rgba(6,147,227,1) 0%,rgb(155,81,224) 100%);--wp--preset--gradient--light-green-cyan-to-vivid-green-cyan: linear-gradient(135deg,rgb(122,220,180) 0%,rgb(0,208,130) 100%);--wp--preset--gradient--luminous-vivid-amber-to-luminous-vivid-orange: linear-gradient(135deg,rgba(252,185,0,1) 0%,rgba(255,105,0,1) 100%);--wp--preset--gradient--luminous-vivid-orange-to-vivid-red: linear-gradient(135deg,rgba(255,105,0,1) 0%,rgb(207,46,46) 100%);--wp--preset--gradient--very-light-gray-to-cyan-bluish-gray: linear-gradient(135deg,rgb(238,238,238) 0%,rgb(169,184,195) 100%);--wp--preset--gradient--cool-to-warm-spectrum: linear-gradient(135deg,rgb(74,234,220) 0%,rgb(151,120,209) 20%,rgb(207,42,186) 40%,rgb(238,44,130) 60%,rgb(251,105,98) 80%,rgb(254,248,76) 100%);--wp--preset--gradient--blush-light-purple: linear-gradient(135deg,rgb(255,206,236) 0%,rgb(152,150,240) 100%);--wp--preset--gradient--blush-bordeaux: linear-gradient(135deg,rgb(254,205,165) 0%,rgb(254,45,45) 50%,rgb(107,0,62) 100%);--wp--preset--gradient--luminous-dusk: linear-gradient(135deg,rgb(255,203,112) 0%,rgb(199,81,192) 50%,rgb(65,88,208) 100%);--wp--preset--gradient--pale-ocean: linear-gradient(135deg,rgb(255,245,203) 0%,rgb(182,227,212) 50%,rgb(51,167,181) 100%);--wp--preset--gradient--electric-grass: linear-gradient(135deg,rgb(202,248,128) 0%,rgb(113,206,126) 100%);--wp--preset--gradient--midnight: linear-gradient(135deg,rgb(2,3,129) 0%,rgb(40,116,252) 100%);--wp--preset--font-size--small: 13px;--wp--preset--font-size--medium: 20px;--wp--preset--font-size--large: 36px;--wp--preset--font-size--x-large: 42px;--wp--preset--spacing--20: 0.44rem;--wp--preset--spacing--30: 0.67rem;--wp--preset--spacing--40: 1rem;--wp--preset--spacing--50: 1.5rem;--wp--preset--spacing--60: 2.25rem;--wp--preset--spacing--70: 3.38rem;--wp--preset--spacing--80: 5.06rem;--wp--preset--shadow--natural: 6px 6px 9px rgba(0, 0, 0, 0.2);--wp--preset--shadow--deep: 12px 12px 50px rgba(0, 0, 0, 0.4);--wp--preset--shadow--sharp: 6px 6px 0px rgba(0, 0, 0, 0.2);--wp--preset--shadow--outlined: 6px 6px 0px -3px rgba(255, 255, 255, 1), 6px 6px rgba(0, 0, 0, 1);--wp--preset--shadow--crisp: 6px 6px 0px rgba(0, 0, 0, 1);}:where(.is-layout-flex){gap: 0.5em;}:where(.is-layout-grid){gap: 0.5em;}body .is-layout-flex{display: flex;}.is-layout-flex{flex-wrap: wrap;align-items: center;}.is-layout-flex > :is(*, div){margin: 0;}body .is-layout-grid{display: grid;}.is-layout-grid > :is(*, div){margin: 0;}:where(.wp-block-columns.is-layout-flex){gap: 2em;}:where(.wp-block-columns.is-layout-grid){gap: 2em;}:where(.wp-block-post-template.is-layout-flex){gap: 1.25em;}:where(.wp-block-post-template.is-layout-grid){gap: 1.25em;}.has-black-color{color: var(--wp--preset--color--black) !important;}.has-cyan-bluish-gray-color{color: var(--wp--preset--color--cyan-bluish-gray) !important;}.has-white-color{color: var(--wp--preset--color--white) !important;}.has-pale-pink-color{color: var(--wp--preset--color--pale-pink) !important;}.has-vivid-red-color{color: var(--wp--preset--color--vivid-red) !important;}.has-luminous-vivid-orange-color{color: var(--wp--preset--color--luminous-vivid-orange) !important;}.has-luminous-vivid-amber-color{color: var(--wp--preset--color--luminous-vivid-amber) !important;}.has-light-green-cyan-color{color: var(--wp--preset--color--light-green-cyan) !important;}.has-vivid-green-cyan-color{color: var(--wp--preset--color--vivid-green-cyan) !important;}.has-pale-cyan-blue-color{color: var(--wp--preset--color--pale-cyan-blue) !important;}.has-vivid-cyan-blue-color{color: var(--wp--preset--color--vivid-cyan-blue) !important;}.has-vivid-purple-color{color: var(--wp--preset--color--vivid-purple) !important;}.has-black-background-color{background-color: var(--wp--preset--color--black) !important;}.has-cyan-bluish-gray-background-color{background-color: var(--wp--preset--color--cyan-bluish-gray) !important;}.has-white-background-color{background-color: var(--wp--preset--color--white) !important;}.has-pale-pink-background-color{background-color: var(--wp--preset--color--pale-pink) !important;}.has-vivid-red-background-color{background-color: var(--wp--preset--color--vivid-red) !important;}.has-luminous-vivid-orange-background-color{background-color: var(--wp--preset--color--luminous-vivid-orange) !important;}.has-luminous-vivid-amber-background-color{background-color: var(--wp--preset--color--luminous-vivid-amber) !important;}.has-light-green-cyan-background-color{background-color: var(--wp--preset--color--light-green-cyan) !important;}.has-vivid-green-cyan-background-color{background-color: var(--wp--preset--color--vivid-green-cyan) !important;}.has-pale-cyan-blue-background-color{background-color: var(--wp--preset--color--pale-cyan-blue) !important;}.has-vivid-cyan-blue-background-color{background-color: var(--wp--preset--color--vivid-cyan-blue) !important;}.has-vivid-purple-background-color{background-color: var(--wp--preset--color--vivid-purple) !important;}.has-black-border-color{border-color: var(--wp--preset--color--black) !important;}.has-cyan-bluish-gray-border-color{border-color: var(--wp--preset--color--cyan-bluish-gray) !important;}.has-white-border-color{border-color: var(--wp--preset--color--white) !important;}.has-pale-pink-border-color{border-color: var(--wp--preset--color--pale-pink) !important;}.has-vivid-red-border-color{border-color: var(--wp--preset--color--vivid-red) !important;}.has-luminous-vivid-orange-border-color{border-color: var(--wp--preset--color--luminous-vivid-orange) !important;}.has-luminous-vivid-amber-border-color{border-color: var(--wp--preset--color--luminous-vivid-amber) !important;}.has-light-green-cyan-border-color{border-color: var(--wp--preset--color--light-green-cyan) !important;}.has-vivid-green-cyan-border-color{border-color: var(--wp--preset--color--vivid-green-cyan) !important;}.has-pale-cyan-blue-border-color{border-color: var(--wp--preset--color--pale-cyan-blue) !important;}.has-vivid-cyan-blue-border-color{border-color: var(--wp--preset--color--vivid-cyan-blue) !important;}.has-vivid-purple-border-color{border-color: var(--wp--preset--color--vivid-purple) !important;}.has-vivid-cyan-blue-to-vivid-purple-gradient-background{background: var(--wp--preset--gradient--vivid-cyan-blue-to-vivid-purple) !important;}.has-light-green-cyan-to-vivid-green-cyan-gradient-background{background: var(--wp--preset--gradient--light-green-cyan-to-vivid-green-cyan) !important;}.has-luminous-vivid-amber-to-luminous-vivid-orange-gradient-background{background: var(--wp--preset--gradient--luminous-vivid-amber-to-luminous-vivid-orange) !important;}.has-luminous-vivid-orange-to-vivid-red-gradient-background{background: var(--wp--preset--gradient--luminous-vivid-orange-to-vivid-red) !important;}.has-very-light-gray-to-cyan-bluish-gray-gradient-background{background: var(--wp--preset--gradient--very-light-gray-to-cyan-bluish-gray) !important;}.has-cool-to-warm-spectrum-gradient-background{background: var(--wp--preset--gradient--cool-to-warm-spectrum) !important;}.has-blush-light-purple-gradient-background{background: var(--wp--preset--gradient--blush-light-purple) !important;}.has-blush-bordeaux-gradient-background{background: var(--wp--preset--gradient--blush-bordeaux) !important;}.has-luminous-dusk-gradient-background{background: var(--wp--preset--gradient--luminous-dusk) !important;}.has-pale-ocean-gradient-background{background: var(--wp--preset--gradient--pale-ocean) !important;}.has-electric-grass-gradient-background{background: var(--wp--preset--gradient--electric-grass) !important;}.has-midnight-gradient-background{background: var(--wp--preset--gradient--midnight) !important;}.has-small-font-size{font-size: var(--wp--preset--font-size--small) !important;}.has-medium-font-size{font-size: var(--wp--preset--font-size--medium) !important;}.has-large-font-size{font-size: var(--wp--preset--font-size--large) !important;}.has-x-large-font-size{font-size: var(--wp--preset--font-size--x-large) !important;} :where(.wp-block-post-template.is-layout-flex){gap: 1.25em;}:where(.wp-block-post-template.is-layout-grid){gap: 1.25em;} :where(.wp-block-columns.is-layout-flex){gap: 2em;}:where(.wp-block-columns.is-layout-grid){gap: 2em;} :root :where(.wp-block-pullquote){font-size: 1.5em;line-height: 1.6;} </style> <link rel="EditURI" type="application/rsd+xml" title="RSD" href="https://webkit.org/wp/xmlrpc.php?rsd" /> <meta name="generator" content="WordPress 6.7" /> <link rel="canonical" href="https://webkit.org/blog/4096/introducing-shadow-dom-api/" /> <link rel='shortlink' href='https://webkit.org/?p=4096' /> <link rel="alternate" title="oEmbed (JSON)" type="application/json+oembed" href="https://webkit.org/wp-json/oembed/1.0/embed?url=https%3A%2F%2Fwebkit.org%2Fblog%2F4096%2Fintroducing-shadow-dom-api%2F" /> <link rel="alternate" title="oEmbed (XML)" type="text/xml+oembed" href="https://webkit.org/wp-json/oembed/1.0/embed?url=https%3A%2F%2Fwebkit.org%2Fblog%2F4096%2Fintroducing-shadow-dom-api%2F&#038;format=xml" /> <!-- Schema.org markup --> <meta itemprop="name" content="Introducing Slot-Based Shadow DOM API"> <meta itemprop="description" content="We鈥檙e pleased to announce that basic support for the new slot-based shadow DOM API is now available in the nightly builds."> <meta itemprop="image" content="https://webkit.org/blog-files/introducing-shadow-dom/progress-bar.png"> <!-- Twitter Card data --> <meta name="twitter:card" content="summary_large_image"> <meta name="twitter:image:src" content="https://webkit.org/blog-files/introducing-shadow-dom/progress-bar.png"> <meta name="twitter:site" content="@webkit"> <meta name="twitter:title" content="Introducing Slot-Based Shadow DOM API"> <meta name="twitter:description" content="We鈥檙e pleased to announce that basic support for the new slot-based shadow DOM API is now available in the nightly builds."> <meta name="twitter:creator" content="@rniwa_dev"> <!-- Open Graph data --> <meta property="og:title" content="Introducing Slot-Based Shadow DOM API"> <meta property="og:type" content="article"> <meta property="og:url" content="https://webkit.org/blog/4096/introducing-shadow-dom-api/"> <meta property="og:image" content="https://webkit.org/blog-files/introducing-shadow-dom/progress-bar.png"> <meta property="og:description" content="We鈥檙e pleased to announce that basic support for the new slot-based shadow DOM API is now available in the nightly builds."> <meta property="og:site_name" content="WebKit"> <meta property="article:published_time" content="2015-10-26T07:00:42-07:00"> <meta property="article:modified_time" content="2015-11-20T14:50:50-08:00"> <meta property="article:section" content="Standards"> <meta property="fb:admins" content="1085088865"> </head> <body class="post-template-default single single-post postid-4096 single-format-standard"> <!-- Copyright 漏 2020 Apple Inc. All rights reserved. --> <svg xmlns="http://www.w3.org/2000/svg"> <style> svg { display: block; width: 0; height: 0; } </style> <filter id="invertLightness" x="0" y="0" style="color-interpolation-filters: sRGB"> <feColorMatrix type="matrix" in="SourceGraphic" result="red" values="1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 0 0 0 0 1" /> <feColorMatrix type="matrix" in="SourceGraphic" result="green" values="0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 0 0 0 1" /> <feColorMatrix type="matrix" in="SourceGraphic" result="blue" values="0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 0 0 1" /> <feBlend in="red" in2="green" mode="lighten" result="maxyellow" /> <feBlend in="maxyellow" in2="blue" mode="lighten" result="max" /> <feBlend in="red" in2="green" mode="darken" result="minyellow" /> <feBlend in="minyellow" in2="blue" mode="darken" result="min" /> <feComponentTransfer result="adjustment" in="min"> <feFuncR type="linear" intercept="1" slope="-1" /> <feFuncG type="linear" intercept="1" slope="-1" /> <feFuncB type="linear" intercept="1" slope="-1" /> </feComponentTransfer> <feComposite operator="arithmetic" in="SourceGraphic" in2="adjustment" k1="0" k2="1" k3="1" k4="-1" result="channelAdjustment" /> <feComposite operator="arithmetic" in="channelAdjustment" in2="max" k1="0" k2="1" k3="-1" k4="1" result="finalColors" /> <feComposite operator="in" in="finalColors" in2="SourceAlpha" /> </filter> </svg> <header aria-label="WebKit.org Header" id="header"> <div class="page-width"> <a href="/"><div id="logo" class="site-logo">WebKit</div></a> <nav id="site-nav" aria-label="Site Menu"> <div class="menu-main-menu-container"><input type="checkbox" id="menu-main-menu-toggle" class="menu-toggle" /><label for="menu-main-menu-toggle" class="label-toggle main-menu" data-open="Main Menu" data-close="Close Menu"></label><ul id="menu-main-menu" class="menu" role="menubar"><li id="menu-item-6091" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-6091"><a href="https://webkit.org/downloads/" role="menuitem">Downloads</a></li> <li id="menu-item-4272" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-has-children menu-item-4272"><input type="checkbox" id="toggle-4272" class="menu-toggle" /><a href="#nav-sub-menu" role="menuitem" aria-haspopup="true" aria-owns="sub-menu-for-4272" aria-controls="sub-menu-for-4272" aria-expanded="true"><label for="toggle-4272" class="label-toggle">Feature Status</label></a> <ul class="sub-menu sub-menu-layer" role="menu" id="sub-menu-for-4272"> <li id="menu-item-13052" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-13052"><a href="https://webkit.org/css-status/" role="menuitem">CSS Features</a></li> <li id="menu-item-14388" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-14388"><a href="https://webkit.org/standards-positions/" role="menuitem">Standards Positions</a></li> </ul> </li> <li id="menu-item-9988" class="menu-item menu-item-type-custom menu-item-object-custom menu-item-has-children menu-item-9988"><input type="checkbox" id="toggle-9988" class="menu-toggle" /><a href="#nav-sub-menu" role="menuitem" aria-haspopup="true" aria-owns="sub-menu-for-9988" aria-controls="sub-menu-for-9988" aria-expanded="true"><label for="toggle-9988" class="label-toggle">Documentation</label></a> <ul class="sub-menu sub-menu-layer" role="menu" id="sub-menu-for-9988"> <li id="menu-item-9989" class="menu-item menu-item-type-custom menu-item-object-custom menu-item-9989"><a href="/web-inspector" role="menuitem">Web Inspector</a></li> <li id="menu-item-10868" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-10868"><a href="https://webkit.org/tracking-prevention/" role="menuitem">Tracking Prevention</a></li> </ul> </li> <li id="menu-item-4282" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-has-children menu-item-4282"><input type="checkbox" id="toggle-4282" class="menu-toggle" /><a href="#nav-sub-menu" role="menuitem" aria-haspopup="true" aria-owns="sub-menu-for-4282" aria-controls="sub-menu-for-4282" aria-expanded="true"><label for="toggle-4282" class="label-toggle">Policies</label></a> <ul class="sub-menu sub-menu-layer" role="menu" id="sub-menu-for-4282"> <li id="menu-item-10037" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-10037"><a href="https://webkit.org/project/" role="menuitem">Project Goals</a></li> <li id="menu-item-13077" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-13077"><a href="https://webkit.org/bug-prioritization/" role="menuitem">Bug Prioritization</a></li> <li id="menu-item-13076" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-13076"><a href="https://webkit.org/bug-report-guidelines/" role="menuitem">Bug Report Guidelines</a></li> <li id="menu-item-13075" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-13075"><a href="https://webkit.org/code-style-guidelines/" role="menuitem">Code Style Guidelines</a></li> <li id="menu-item-13074" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-13074"><a href="https://webkit.org/commit-and-review-policy/" role="menuitem">Commit and Review Policy</a></li> <li id="menu-item-13073" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-13073"><a href="https://webkit.org/feature-policy/" role="menuitem">Feature Policy</a></li> <li id="menu-item-13072" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-13072"><a href="https://webkit.org/security-policy/" role="menuitem">Security Policy</a></li> <li id="menu-item-13071" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-13071"><a href="https://webkit.org/tracking-prevention-policy/" role="menuitem">Tracking Prevention Policy</a></li> </ul> </li> <li id="menu-item-4274" class="menu-item menu-item-type-custom menu-item-object-custom menu-item-has-children menu-item-4274"><input type="checkbox" id="toggle-4274" class="menu-toggle" /><a href="#nav-sub-menu" role="menuitem" aria-haspopup="true" aria-owns="sub-menu-for-4274" aria-controls="sub-menu-for-4274" aria-expanded="true"><label for="toggle-4274" class="label-toggle">Contribute</label></a> <ul class="sub-menu sub-menu-layer" role="menu" id="sub-menu-for-4274"> <li id="menu-item-4277" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-4277"><a href="https://webkit.org/getting-started/" role="menuitem">Getting Started</a></li> <li id="menu-item-4284" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-4284"><a href="https://webkit.org/contributing-code/" role="menuitem">Contributing Code</a></li> <li id="menu-item-4281" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-4281"><a href="https://webkit.org/testing-contributions/" role="menuitem">Testing Contributions</a></li> <li id="menu-item-4273" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-4273"><a href="https://webkit.org/reporting-bugs/" role="menuitem">How to Report Bugs</a></li> <li id="menu-item-4278" class="menu-item menu-item-type-custom menu-item-object-custom menu-item-4278"><a href="https://github.com/WebKit/WebKit" role="menuitem">GitHub Repository</a></li> </ul> </li> <li id="menu-item-4270" class="menu-item menu-item-type-post_type menu-item-object-page current_page_parent menu-item-has-children menu-item-4270"><input type="checkbox" id="toggle-4270" class="menu-toggle" /><a href="#nav-sub-menu" role="menuitem" aria-haspopup="true" aria-owns="sub-menu-for-4270" aria-controls="sub-menu-for-4270" aria-expanded="true"><label for="toggle-4270" class="label-toggle">Blog</label></a> <ul class="sub-menu sub-menu-layer" role="menu" id="sub-menu-for-4270"> <li id="menu-item-13057" class="menu-item menu-item-type-taxonomy menu-item-object-category menu-item-13057"><a href="https://webkit.org/blog/category/news/" role="menuitem">News Posts</a></li> <li id="menu-item-13058" class="menu-item menu-item-type-taxonomy menu-item-object-category menu-item-13058"><a href="https://webkit.org/blog/category/css/" role="menuitem">CSS Posts</a></li> <li id="menu-item-13063" class="menu-item menu-item-type-taxonomy menu-item-object-category menu-item-13063"><a href="https://webkit.org/blog/category/contributing/" role="menuitem">Contributing Posts</a></li> <li id="menu-item-13062" class="menu-item menu-item-type-taxonomy menu-item-object-category menu-item-13062"><a href="https://webkit.org/blog/category/privacy/" role="menuitem">Privacy Posts</a></li> <li id="menu-item-13060" class="menu-item menu-item-type-taxonomy menu-item-object-category menu-item-13060"><a href="https://webkit.org/blog/category/performance/" role="menuitem">Performance Posts</a></li> <li id="menu-item-13061" class="menu-item menu-item-type-taxonomy menu-item-object-category menu-item-13061"><a href="https://webkit.org/blog/category/javascript/" role="menuitem">JavaScript Posts</a></li> <li id="menu-item-13056" class="menu-item menu-item-type-taxonomy menu-item-object-category current-post-ancestor current-menu-parent current-post-parent menu-item-13056"><a href="https://webkit.org/blog/category/standards/" role="menuitem">Standards Posts</a></li> <li id="menu-item-13059" class="menu-item menu-item-type-taxonomy menu-item-object-category menu-item-13059"><a href="https://webkit.org/blog/category/web-inspector/" role="menuitem">Web Inspector Posts</a></li> <li id="menu-item-13055" class="menu-item menu-item-type-taxonomy menu-item-object-category menu-item-13055"><a href="https://webkit.org/blog/category/safari-technology-preview/" role="menuitem">Safari Technology Preview Posts</a></li> </ul> </li> <li><form action="/" method="get"><input type="search" name="s" class="search-input" value=""></form></li></ul></div></nav> </div> </header> <main id="content"> <div class="page-width"> <article class="post-4096 post type-post status-publish format-standard hentry category-standards" id="post-4096"> <h1><a href="https://webkit.org/blog/4096/introducing-shadow-dom-api/" rel="bookmark" title="Permanent Link: Introducing Slot-Based Shadow DOM API">Introducing Slot-Based Shadow DOM API</a></h1> <div class="byline"> <p class="date">Oct 26, 2015</p> <p class="author">by <span>Ryosuke Niwa</span></p> <p class="twitter"><a href="https://twitter.com/rniwa_dev" target="_blank">@rniwa_dev</a></p> </div> <div class="bodycopy"> <p>We&#8217;re pleased to announce that basic support for the new <a href="https://lists.w3.org/Archives/Public/public-webapps/2015AprJun/0184.html">slot-based shadow DOM API</a> we proposed in April is now available in the <a href="/nightly">nightly builds of WebKit</a> after <a href="http://trac.webkit.org/changeset/190680">r190680</a>. Shadow DOM is a part of Web Components, a set of specifications that were initially proposed by Google to enable the creation of reusable widgets and components on the Web. Shadow DOM, in particular, provides a lightweight encapsulation for DOM trees by allowing a creation of a parallel tree on an element called a &#8220;shadow tree&#8221; that replaces the rendering of the element without modifying the underlying DOM tree. Because a shadow tree is not an ordinary child of the &#8220;host&#8221; element to which it is attached, users of components cannot accidentally poke into it. Style rules are also scoped, meaning that CSS rules defined outside of a shadow tree do not apply to elements inside the shadow tree and rules defined inside the shadow tree do not apply to elements outside of it.</p> <h2>Style Isolation</h2> <p>One major benefit of using shadow DOM is style isolation. To see how, let&#8217;s say we want to create a custom progress bar. We can use two nested div&#8217;s to show the bar and another div with the text to show the percentage as follows:</p> <pre><code class="html"><span class="tag">&lt;<span class="keyword">style</span>&gt;</span> .progress { position: relative; border: solid 1px #000; padding: 1px; width: 100px; height: 1rem; } .progress &gt; .bar { background: #9cf; height: 100%; } .progress &gt; .label { position: absolute; top: 0; left: 0; width: 100%; text-align: center; font-size: 0.8rem; line-height: 1.1rem; } <span class="tag">&lt;/<span class="keyword">style</span>&gt;</span> <span class="tag">&lt;<span class="keyword">template</span> <span class="keyword attribute">id</span>=<span class="attribute value string">"progress-bar-template"</span>&gt;</span> <span class="tag">&lt;<span class="keyword">div</span> <span class="keyword attribute">class</span>=<span class="attribute value string">"progress"</span> <span class="keyword attribute">role</span>=<span class="attribute value string">"progressbar"</span> <span class="keyword attribute">aria-valuenow</span>=<span class="attribute value string">"0"</span> <span class="keyword attribute">aria-valuemin</span>=<span class="attribute value string">"0"</span> <span class="keyword attribute">aria-valuemax</span>=<span class="attribute value string">"100"</span>&gt;</span> <span class="tag">&lt;<span class="keyword">div</span> <span class="keyword attribute">class</span>=<span class="attribute value string">"bar"</span>&gt;</span><span class="tag">&lt;/<span class="keyword">div</span>&gt;</span> <span class="tag">&lt;<span class="keyword">div</span> <span class="keyword attribute">class</span>=<span class="attribute value string">"label"</span>&gt;</span>0%<span class="tag">&lt;/<span class="keyword">div</span>&gt;</span> <span class="tag">&lt;/<span class="keyword">div</span>&gt;</span> <span class="tag">&lt;/<span class="keyword">template</span>&gt;</span> <span class="tag">&lt;<span class="keyword">script</span>&gt;</span> function createProgressBar() { var fragment = document.getElementById('progress-bar-template').content.cloneNode(true); var progressBar = fragment.querySelector('div'); progressBar.updateProgress = function (newPercentage) { this.setAttribute('aria-valuenow', newPercentage); this.querySelector('.label').textContent = newPercentage + '%'; this.querySelector('.bar').style.width = newPercentage + '%'; } return progressBar; } <span class="tag">&lt;/<span class="keyword">script</span>&gt;</span> </code></pre> <p>Note the use of the <a href="https://html.spec.whatwg.org/multipage/scripting.html#the-template-element">template element</a>, which allows authors to include a snippet of HTML that can be instantiated later by cloning the content. This was the first feature of Web Components we implemented in WebKit that later got merged into the <a href="https://html.spec.whatwg.org/">HTML5 specification</a>. A template element can appear anywhere in a document (e.g. between <code>table</code> and <code>tr</code> elements), and content inside template elements is <em><a href="https://html.spec.whatwg.org/multipage/scripting.html#associated-inert-template-document">inert</a></em> and does not run scripts or load images and other types of subresources. Then the user of this custom progress bar can instantiate it and update the progress as follows:</p> <pre><code class="js"><span class="keyword type">var</span> <span class="identifier">progressBar</span> <span class="operator">=</span> <span class="identifier">createProgressBar</span>(); <span class="identifier">container</span>.<span class="identifier">appendChild</span>(<span class="identifier">progressBar</span>); ... <span class="identifier">progressBar</span>.<span class="identifier">updateProgress</span>(<span class="number">10</span>); </code></pre> <figure class="widescreen mattewhite"><img decoding="async" src="/blog-files/introducing-shadow-dom/progress-bar.png" title="Progress Bar Example" width="216" height="48"/></figure> <p>The problem with this progress bar implementation is that its two internal divs are freely accessible to its users and its style rules are not scoped to the progress bar. For example, the style rules defined for the progress bar will apply to content outside the progress bar with the class name <code>progress</code>:</p> <pre><code class="html"><span class="tag">&lt;<span class="keyword">section</span> <span class="keyword attribute">class</span>=<span class="attribute value string">"project"</span>&gt;</span> <span class="tag">&lt;<span class="keyword">p</span> <span class="keyword attribute">class</span>=<span class="attribute value string">"progress"</span>&gt;</span>Pending an approval<span class="tag">&lt;/<span class="keyword">p</span>&gt;</span> <span class="tag">&lt;/<span class="keyword">section</span>&gt;</span> </code></pre> <p>Similarly, style rules defined for other elements could override rules in the progress bar:</p> <pre><code class="css">&lt;<span class="keyword">style</span>&gt; <span class="keyword builtin">.label</span> { <span class="attribute">font-weight</span>: <span class="identifier">bold</span>; } &lt;/<span class="keyword">style</span>&gt; </code></pre> <p>While we could work around these problems by using a custom element name such as <code>custom-progressbar</code> to scope rules and then initialize all other properties by <code>all: initial</code>, Shadow DOM provides a much more elegant solution. The idea here is to introduce an encapsulation layer at the outer div so that users of the progress bar don&#8217;t see its internals (such as divs created for the label and the bar) and styles defined for the progress bar don&#8217;t interfere with the rest of the page and vice versa. To do that, we first create a <code>ShadowRoot</code> on the progress bar by calling <code>attachShadow({mode: 'closed'})</code>, and then append various nodes needed for its implementation under it. Let&#8217;s say we&#8217;re still using a div to &#8220;host&#8221; this shadow root, then we can create a new div and attach a shadow root as follows:</p> <pre><code class="html"><span class="tag">&lt;<span class="keyword">template</span> <span class="keyword attribute">id</span>=<span class="attribute value string">"progress-bar-template"</span>&gt;</span> <span class="tag">&lt;<span class="keyword">style</span>&gt;</span> .progress { position: relative; border: solid 1px #000; padding: 1px; width: 100px; height: 1rem; } .progress &gt; .bar { background: #9cf; height: 100%; } .progress &gt; .label { position: absolute; top: 0; left: 0; width: 100%; text-align: center; font-size: 0.8rem; line-height: 1.1rem; } <span class="tag">&lt;/<span class="keyword">style</span>&gt;</span> <span class="tag">&lt;<span class="keyword">div</span> <span class="keyword attribute">class</span>=<span class="attribute value string">"progress"</span> <span class="keyword attribute">role</span>=<span class="attribute value string">"progressbar"</span> <span class="keyword attribute">aria-valuenow</span>=<span class="attribute value string">"0"</span> <span class="keyword attribute">aria-valuemin</span>=<span class="attribute value string">"0"</span> <span class="keyword attribute">aria-valuemax</span>=<span class="attribute value string">"100"</span>&gt;</span> <span class="tag">&lt;<span class="keyword">div</span> <span class="keyword attribute">class</span>=<span class="attribute value string">"bar"</span>&gt;</span><span class="tag">&lt;/<span class="keyword">div</span>&gt;</span> <span class="tag">&lt;<span class="keyword">div</span> <span class="keyword attribute">class</span>=<span class="attribute value string">"label"</span>&gt;</span>0%<span class="tag">&lt;/<span class="keyword">div</span>&gt;</span> <span class="tag">&lt;/<span class="keyword">div</span>&gt;</span> <span class="tag">&lt;/<span class="keyword">template</span>&gt;</span> <span class="tag">&lt;<span class="keyword">script</span>&gt;</span> function createProgressBar() { var progressBar = document.createElement('div'); var shadowRoot = progressBar.attachShadow({mode: 'closed'}); shadowRoot.appendChild(document.getElementById('progress-bar-template').content.cloneNode(true)); progressBar.updateProgress = function (newPercentage) { shadowRoot.querySelector('.progress').setAttribute('aria-valuenow', newPercentage); shadowRoot.querySelector('.label').textContent = newPercentage + '%'; shadowRoot.querySelector('.bar').style.width = newPercentage + '%'; } return progressBar; } <span class="tag">&lt;/<span class="keyword">script</span>&gt;</span> </code></pre> <p>Notice that the style element is inside the template element and cloned into the shadow root along with the divs. This allows the style rules defined inside the shadow root to be scoped. Style rules defined outside a shadow root do not apply to elements inside the shadow root either. <strong>Tip</strong>: while debugging your code, you may find it helpful to use shadow DOM&#8217;s <code>open</code> mode so that you can access the newly created shadow root via the <code>shadowRoot</code> property of the host element. e.g. <code>{mode: DEBUG ? 'open' : 'closed'}</code></p> <h2>Composition with Slots</h2> <p>At this point, you might be wondering why this had to be done in DOM instead of CSS. Styling is a presentational concept, so why should we add new elements to the DOM? In fact, <a href="http://www.w3.org/TR/2014/WD-css-scoping-1-20140403/">the first public working draft of the CSS Scoping Module Level 1</a> defines the <a href="http://www.w3.org/TR/2014/WD-css-scoping-1-20140403/#scope-atrule">@scope</a> rule, which enables exactly that. So why did we need to add another mechanism to isolate styles? One motivation was to allow elements used in the implementation of components to be hidden from node traversal APIs such as <code>querySelectorAll</code> and <code>getElementsByTagName</code>. Because nodes inside a shadow root are not found by these APIs by default, users of components that utilize shadow DOM do not need to worry about how each component is implemented. Each component is presented as an opaque element whose implementation details are encapsulated in its shadow DOM. <strong>Note that shadow DOM does not provide a cross-origin iframe-like security boundary. Scripts can easily bypass the shadow DOM boundary if needed.</strong> Another reason we need a DOM-based solution is for composition. Let&#8217;s say we have a list of contacts:</p> <pre><code class="html"><span class="tag">&lt;<span class="keyword">ul</span> <span class="keyword attribute">id</span>=<span class="attribute value string">"contacts"</span>&gt;</span> <span class="tag">&lt;<span class="keyword">li</span>&gt;</span> Commit Queue (<span class="tag">&lt;<span class="keyword">a</span> <span class="keyword attribute">href</span>=<span class="attribute value string">"mailto:commit-queue@webkit.org"</span>&gt;</span>commit-queue@webkit.org<span class="tag">&lt;/<span class="keyword">a</span>&gt;</span>)<span class="tag">&lt;<span class="keyword">br</span>&gt;</span> One Infinite Loop, Cupertino, CA 95014 <span class="tag">&lt;/<span class="keyword">li</span>&gt;</span> <span class="tag">&lt;<span class="keyword">li</span>&gt;</span> Niwa, Ryosuke (<span class="tag">&lt;<span class="keyword">a</span> <span class="keyword attribute">href</span>=<span class="attribute value string">"mailto:rniwa@webkit.org"</span>&gt;</span>rniwa@webkit.org<span class="tag">&lt;/<span class="keyword">a</span>&gt;</span>)<span class="tag">&lt;<span class="keyword">br</span>&gt;</span> Two Infinite Loop, Cupertino, CA 95014 <span class="tag">&lt;/<span class="keyword">li</span>&gt;</span> <span class="tag">&lt;/<span class="keyword">ul</span>&gt;</span> </code></pre> <p>and we would like to add a fancy UI for each contact&#8217;s information in the list when scripts are enabled:</p> <figure class="widescreen mattewhite"> <img fetchpriority="high" decoding="async" src="/blog-files/introducing-shadow-dom/commit-queue-contact.png" title="Commit Queue Contact" width="794" height="160" /></figure> <p>Instead of copying all of this text over to our own shadow DOM, we can use named slots to render the text elsewhere in our shadow DOM without modifying the DOM as follows:</p> <pre><code class="html"><span class="tag">&lt;<span class="keyword">template</span> <span class="keyword attribute">id</span>=<span class="attribute value string">"contact-template"</span>&gt;</span> <span class="tag">&lt;<span class="keyword">style</span>&gt;</span> :host { border: solid 1px #ccc; border-radius: 0.5rem; padding: 0.5rem; margin: 0.5rem; } b { display: inline-block; width: 5rem; } <span class="tag">&lt;/<span class="keyword">style</span>&gt;</span> <span class="tag">&lt;<span class="keyword">b</span>&gt;</span>Name<span class="tag">&lt;/<span class="keyword">b</span>&gt;</span>: <span class="tag">&lt;<span class="keyword">slot</span> <span class="keyword attribute">name</span>=<span class="attribute value string">"fullName"</span>&gt;</span><span class="tag">&lt;<span class="keyword">slot</span> <span class="keyword attribute">name</span>=<span class="attribute value string">"firstName"</span>&gt;</span><span class="tag">&lt;/<span class="keyword">slot</span>&gt;</span> <span class="tag">&lt;<span class="keyword">slot</span> <span class="keyword attribute">name</span>=<span class="attribute value string">"lastName"</span>&gt;</span><span class="tag">&lt;/<span class="keyword">slot</span>&gt;</span><span class="tag">&lt;/<span class="keyword">slot</span>&gt;</span><span class="tag">&lt;<span class="keyword">br</span>&gt;</span> <span class="tag">&lt;<span class="keyword">b</span>&gt;</span>Email<span class="tag">&lt;/<span class="keyword">b</span>&gt;</span>: <span class="tag">&lt;<span class="keyword">slot</span> <span class="keyword attribute">name</span>=<span class="attribute value string">"email"</span>&gt;</span>Unknown<span class="tag">&lt;/<span class="keyword">slot</span>&gt;</span><span class="tag">&lt;<span class="keyword">br</span>&gt;</span> <span class="tag">&lt;<span class="keyword">b</span>&gt;</span>Address<span class="tag">&lt;/<span class="keyword">b</span>&gt;</span>: <span class="tag">&lt;<span class="keyword">slot</span> <span class="keyword attribute">name</span>=<span class="attribute value string">"address"</span>&gt;</span>Unknown<span class="tag">&lt;/<span class="keyword">slot</span>&gt;</span> <span class="tag">&lt;/<span class="keyword">template</span>&gt;</span> <span class="tag">&lt;<span class="keyword">script</span>&gt;</span> window.addEventListener('DOMContentLoaded', function () { var contacts = document.getElementById('contacts').children; var template = document.getElementById('contact-template').content; for (var i = 0; i <span class="tag">&lt; <span class="keyword attribute">contacts</span>.<span class="keyword attribute">length</span>; <span class="keyword attribute">i</span>++) <span class="keyword attribute">contacts</span>[<span class="keyword attribute">i</span>].<span class="keyword attribute">attachShadow</span>({<span class="keyword attribute">mode:</span> <span class="attribute value string">'closed'</span>}).<span class="keyword attribute">appendChild</span>(<span class="keyword attribute">template</span>.<span class="keyword attribute">cloneNode</span>(<span class="keyword attribute">true</span>)); }); &lt;/<span class="keyword">script</span>&gt;</span> </code></pre> <p>Conceptually, slots are holes in a shadow DOM that will be filled by children of its host element. Each element can be assigned into a slot of a specific name by the <code>slot</code> attribute as follows:</p> <pre><code class="html"><span class="tag">&lt;<span class="keyword">ul</span> <span class="keyword attribute">id</span>=<span class="attribute value string">"contacts"</span>&gt;</span> <span class="tag">&lt;<span class="keyword">li</span>&gt;</span> <span class="tag">&lt;<span class="keyword">span</span> <span class="keyword attribute">slot</span>=<span class="attribute value string">"fullName"</span>&gt;</span>Commit Queue<span class="tag">&lt;/<span class="keyword">span</span>&gt;</span> (<span class="tag">&lt;<span class="keyword">a</span> <span class="keyword attribute">slot</span>=<span class="attribute value string">"email"</span> <span class="keyword attribute">href</span>=<span class="attribute value string">"mailto:commit-queue@webkit.org"</span>&gt;</span>commit-queue@webkit.org<span class="tag">&lt;/<span class="keyword">a</span>&gt;</span>)<span class="tag">&lt;<span class="keyword">br</span>&gt;</span> <span class="tag">&lt;<span class="keyword">span</span> <span class="keyword attribute">slot</span>=<span class="attribute value string">"address"</span>&gt;</span>One Infinite Loop, Cupertino, CA 95014<span class="tag">&lt;/<span class="keyword">span</span>&gt;</span> <span class="tag">&lt;/<span class="keyword">li</span>&gt;</span> <span class="tag">&lt;/<span class="keyword">ul</span>&gt;</span> </code></pre> <p>Here, we&#8217;re attaching a shadow root to the li, and each span with a <code>slot</code> attribute is assigned to the slot of the same name inside the shadow DOM. Let&#8217;s take a closer look at the shadow DOM template:</p> <pre><code class="html"><span class="tag">&lt;<span class="keyword">b</span>&gt;</span>Name<span class="tag">&lt;/<span class="keyword">b</span>&gt;</span>: <span class="tag">&lt;<span class="keyword">slot</span> <span class="keyword attribute">name</span>=<span class="attribute value string">"fullName"</span>&gt;</span> <span class="tag">&lt;<span class="keyword">slot</span> <span class="keyword attribute">name</span>=<span class="attribute value string">"firstName"</span>&gt;</span><span class="tag">&lt;/<span class="keyword">slot</span>&gt;</span> <span class="tag">&lt;<span class="keyword">slot</span> <span class="keyword attribute">name</span>=<span class="attribute value string">"lastName"</span>&gt;</span><span class="tag">&lt;/<span class="keyword">slot</span>&gt;</span> <span class="tag">&lt;/<span class="keyword">slot</span>&gt;</span><span class="tag">&lt;<span class="keyword">br</span>&gt;</span> <span class="tag">&lt;<span class="keyword">b</span>&gt;</span>Email<span class="tag">&lt;/<span class="keyword">b</span>&gt;</span>: <span class="tag">&lt;<span class="keyword">slot</span> <span class="keyword attribute">name</span>=<span class="attribute value string">"email"</span>&gt;</span>Unknown<span class="tag">&lt;/<span class="keyword">slot</span>&gt;</span><span class="tag">&lt;<span class="keyword">br</span>&gt;</span> <span class="tag">&lt;<span class="keyword">b</span>&gt;</span>Address<span class="tag">&lt;/<span class="keyword">b</span>&gt;</span>: <span class="tag">&lt;<span class="keyword">slot</span> <span class="keyword attribute">name</span>=<span class="attribute value string">"address"</span>&gt;</span>Unknown<span class="tag">&lt;/<span class="keyword">slot</span>&gt;</span> </code></pre> <p>In this template, we have slots named <code>fullName</code>, which contains two other slots named <code>firstName</code> and <code>lastName</code>, and two additional slots named <code>email</code> and <code>address</code>. The <code>fullName</code> slot is taking the advantage of fallback content, and showing <code>firstName</code> and <code>lastName</code> only if there were no nodes assigned to the <code>fullName</code> slot. Even though there is exactly one node assigned to each slot in this example, multiple elements with the same <code>slot</code> attribute value can be assigned to a single slot, and they will appear in the order they appeared as the children of the host element. You can also use an unnamed default slot that will be filled by all of the host&#8217;s children that don&#8217;t have a <code>slot</code> attribute specified. When a Web browser renders this content, the content of the li element is replaced by the shadow DOM, and slots inside of it are replaced by their assigned node as if rendering the following DOM instead:</p> <pre><code class="html"><span class="tag">&lt;<span class="keyword">ul</span> <span class="keyword attribute">id</span>=<span class="attribute value string">"contacts"</span>&gt;</span> <span class="tag">&lt;<span class="keyword">li</span>&gt;</span> <span class="comment">&lt;!--shadow-root-start--&gt;</span> <span class="tag">&lt;<span class="keyword">b</span>&gt;</span>Name<span class="tag">&lt;/<span class="keyword">b</span>&gt;</span>: <span class="tag">&lt;<span class="keyword">slot</span> <span class="keyword attribute">name</span>=<span class="attribute value string">"fullName"</span>&gt;</span> <span class="comment">&lt;!--slot-content-start--&gt;</span> <span class="tag">&lt;<span class="keyword">span</span> <span class="keyword attribute">slot</span>=<span class="attribute value string">"fullName"</span>&gt;</span>Commit Queue<span class="tag">&lt;/<span class="keyword">span</span>&gt;</span> <span class="comment">&lt;!--slot-content-end--&gt;</span> <span class="tag">&lt;/<span class="keyword">slot</span>&gt;</span><span class="tag">&lt;<span class="keyword">br</span>&gt;</span> <span class="tag">&lt;<span class="keyword">b</span>&gt;</span>Email<span class="tag">&lt;/<span class="keyword">b</span>&gt;</span>: <span class="tag">&lt;<span class="keyword">slot</span> <span class="keyword attribute">name</span>=<span class="attribute value string">"email"</span>&gt;</span> <span class="comment">&lt;!--slot-content-start--&gt;</span> <span class="tag">&lt;<span class="keyword">a</span> <span class="keyword attribute">slot</span>=<span class="attribute value string">"email"</span> <span class="keyword attribute">href</span>=<span class="attribute value string">"mailto:commit-queue@webkit.org"</span>&gt;</span>commit-queue@webkit.org<span class="tag">&lt;/<span class="keyword">a</span>&gt;</span> <span class="comment">&lt;!--slot-content-end--&gt;</span> <span class="tag">&lt;/<span class="keyword">slot</span>&gt;</span><span class="tag">&lt;<span class="keyword">br</span>&gt;</span> <span class="tag">&lt;<span class="keyword">b</span>&gt;</span>Address<span class="tag">&lt;/<span class="keyword">b</span>&gt;</span>: <span class="tag">&lt;<span class="keyword">slot</span> <span class="keyword attribute">name</span>=<span class="attribute value string">"address"</span>&gt;</span> <span class="comment">&lt;!--slot-content-start--&gt;</span> <span class="tag">&lt;<span class="keyword">span</span> <span class="keyword attribute">slot</span>=<span class="attribute value string">"address"</span>&gt;</span>One Infinite Loop, Cupertino, CA 95014<span class="tag">&lt;/<span class="keyword">span</span>&gt;</span> <span class="comment">&lt;!--slot-content-end--&gt;</span> <span class="tag">&lt;/<span class="keyword">slot</span>&gt;</span> <span class="comment">&lt;!--shadow-root-end--&gt;</span> <span class="tag">&lt;/<span class="keyword">li</span>&gt;</span> <span class="tag">&lt;/<span class="keyword">ul</span>&gt;</span> </code></pre> <p>As you can see, slot-based composition is a powerful tool that allows widgets to pull in the page content without cloning or modifying the DOM. With it, widgets can respond to changes made to its child nodes without <a href="https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver">MutationObservers</a> or an explicit notification via script. In essence, composition turns the DOM into a communication medium between components.</p> <h2>Styling the Host Element</h2> <p>There is one more thing to note in the previous example, which had a mysterious pseudo-class <code>:host</code>:</p> <pre><code class="html"><span class="tag">&lt;<span class="keyword">template</span> <span class="keyword attribute">id</span>=<span class="attribute value string">"contact-template"</span>&gt;</span> <span class="tag">&lt;<span class="keyword">style</span>&gt;</span> :host { border: solid 1px #ccc; border-radius: 0.5rem; padding: 0.5rem; margin: 0.5rem; } b { display: inline-block; width: 5rem; } <span class="tag">&lt;/<span class="keyword">style</span>&gt;</span> ... <span class="tag">&lt;/<span class="keyword">template</span>&gt;</span> </code></pre> <p>This pseudo class, as its name suggests, matches the host element of the shadow DOM in which this rule appears. By default, author style rules defined outside the shadow DOM have a higher precedence over rules defined in the shadow DOM. This allows a component to define its &#8220;default style&#8221;, and let users of the component override as needed. In addition, a component can use <code>!important</code> to force a certain style, such as width and <code>display</code> type, without which it cannot function properly with. Any <code>!important</code> rules defined inside a shadow DOM have a higher precedence over regular and <code>!important</code> rules defined outside the shadow DOM.</p> <h2>Future Work</h2> <p>There is still a lot of work left for Web Components. For styling, we would like to allow styling nodes assigned to a slot inside a shadow DOM. There is also a desire for components to respond to the document theme as well as exposing a stylable part to their users like CSS pseudo elements. In the longer term, we would like to see an imperative DOM API to manipulate slot assignments as <a href="https://lists.w3.org/Archives/Public/public-webapps/2014JanMar/0376.html">we proposed a while ago</a>. To complement shadow DOM, we&#8217;re also interested in <a href="http://w3c.github.io/webcomponents/spec/custom/">custom elements</a>. The custom elements API allows authors to associate a JavaScript class with a particular element name in HTML documents, and it&#8217;s a great way to attach shadow DOM and other custom behaviors idiomatically. Unfortunately, there are a <a href="https://github.com/w3c/webcomponents/blob/gh-pages/proposals/Optional-Upgrades-Optional-Constructors.md">few</a> <a href="https://github.com/w3c/webcomponents/blob/gh-pages/proposals/Type-Extensions.md">conflicting</a> <a href="https://github.com/w3c/webcomponents/blob/gh-pages/proposals/Parser-Created-Constructors.md">proposals</a> on when and how to create a custom element. To help steer the discussion in W3C, we&#8217;re planning to prototype it in WebKit. For packaging and delivering Web Components, we&#8217;ve <a href="http://trac.webkit.org/changeset/187890">been</a> <a href="https://trac.webkit.org/r189431">working</a> <a href="https://trac.webkit.org/r190272">on</a> <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import">ES6 modules</a>. Like <a href="https://hacks.mozilla.org/2014/12/mozilla-and-web-components/">Mozilla</a>, we believe modules will radically change the way authors strcuture their pages. We would also like to eventually design an API to create a fully isolated web component with an iframe-like security boundary on top of shadow DOM and custom elements. To conclude, we&#8217;re really excited about bringing a major feature of Web Components to WebKit, and we&#8217;ll keep you posted about more features coming your way. If you have any questions, please feel free to contact <a href="https://twitter.com/ryosukeniwa">myself</a>, <a href="https://twitter.com/webkit">@WebKit</a>, or <a href="https://twitter.com/jonathandavis">Jon Davis</a>.</p> </div> </article> <aside class="nextrouter" aria-label="Next/Previous posts"> <div class="bodycopy"> <a class="page-numbers next-post" href="https://webkit.org/blog/4062/targeting-domains-with-content-blockers/" rel="next"><div class="nextrouter-copy"><span class="label">Next</span><span class="title">Targeting Domains with Content Blockers</span><span class="link">Learn more</span></div></a> </div> </aside> <aside class="nextrouter previous" aria-label="Next/Previous posts"> <div class="bodycopy"> <a class="page-numbers prev-post" href="https://webkit.org/blog/4054/es6-in-webkit/" rel="prev"><div class="nextrouter-copy"><span class="label">Previously</span><span class="title">ECMAScript 6 in WebKit</span><span class="link">Learn more</span></div></a> </div> </aside> </div><!--.page-width--> </main><!-- #content --> <footer> <div class="page-width"> <nav id="footer-nav" aria-label="Footer menu"><div class="menu-footer-menu-container"><ul id="menu-footer-menu" class="menu"><li id="menu-item-7617" class="menu-item menu-item-type-custom menu-item-object-custom menu-item-7617"><a rel="me" href="https://front-end.social/@webkit">@webkit@front-end.social</a></li> <li id="menu-item-5365" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-5365"><a href="https://webkit.org/sitemap/">Site Map</a></li> <li id="menu-item-4185" class="menu-item menu-item-type-custom menu-item-object-custom menu-item-4185"><a href="http://www.apple.com/legal/privacy/">Privacy Policy</a></li> <li id="menu-item-4287" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-4287"><a href="https://webkit.org/licensing-webkit/">Licensing WebKit</a></li> <li id="menu-item-4187" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-4187"><a href="https://webkit.org/terms-of-use/">WebKit and the WebKit logo are trademarks of Apple Inc.</a></li> </ul></div></nav> </div> </footer> <script type="text/javascript" src="https://webkit.org/wp-content/themes/webkit/scripts/global.js?ver=1.0" id="theme-global-js"></script> </body> </html> <!-- Dynamic page generated in 0.320 seconds. --> <!-- Cached page generated by WP-Super-Cache on 2024-11-26 23:37:43 --> <!-- super cache -->

Pages: 1 2 3 4 5 6 7 8 9 10