Skip to content

Commit

Permalink
Add support for styles with a shady-unscoped attribute
Browse files Browse the repository at this point in the history
Since `/deep/` has been removed from Shady DOM v0, users must share styles between elements via style modules (e.g. `<style include="shared-style">`).

Under ShadyDOM, these styles are scoped and duplicated between elements. If these styles are intended to replace global styling done with `html /deep/ ...` this is inefficient since the styles are needlessly copied to each element style.

Therefore, this adds support for a `shady-unscoped` attribute. If this attribute is placed on a style and native Shadow DOM is not in use, then the style is copied once to the document and are left unscoped.

Please note, css custom properties are not supported in `shady-unscoped` styles.
  • Loading branch information
Steven Orvell committed Oct 18, 2017
1 parent 40058ae commit 90697bf
Show file tree
Hide file tree
Showing 5 changed files with 181 additions and 3 deletions.
28 changes: 26 additions & 2 deletions src/lib/style-util.html
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
var settings = Polymer.Settings;

return {
unscopedStyleImports: new WeakMap(),
SHADY_UNSCOPED_ATTR: 'shady-unscoped',
// chrome 49 has semi-working css vars, check if box-shadow works
// safari 9.1 has a recalc bug: https://bugs.webkit.org/show_bug.cgi?id=155782
NATIVE_VARIABLES: Polymer.Settings.useNativeCSSProperties,
Expand Down Expand Up @@ -211,12 +213,34 @@
// get style element applied to main doc via HTMLImports polyfill
e = e.__appliedElement || e;
e.parentNode.removeChild(e);
cssText += this.resolveCss(e.textContent, element.ownerDocument);
var css = this.resolveCss(e.textContent, element.ownerDocument);
// support: unscoped styles
if (!settings.useNativeShadow &&
e.hasAttribute(this.SHADY_UNSCOPED_ATTR)) {
e.textContent = css;
document.head.insertBefore(e, document.head.firstChild);
} else {
cssText += css;
}
// it's an import, assume this is a text file of css content.
// TODO(sorvell): plan is to deprecate this way to get styles;
// remember to add deprecation warning when this is done.
} else if (e.import && e.import.body) {
cssText += this.resolveCss(e.import.body.textContent, e.import);
var importCss = this.resolveCss(e.import.body.textContent, e.import);
// support: unscoped styles
// record imports in a WeakMap so they are de-duped if unscoped > 1x.
if (!settings.useNativeShadow &&
e.hasAttribute(this.SHADY_UNSCOPED_ATTR)) {
if (!this.unscopedStyleImports.has(e.import)) {
this.unscopedStyleImports.set(e.import, true);
var importStyle = document.createElement('style');
importStyle.setAttribute(this.SHADY_UNSCOPED_ATTR, '');
importStyle.textContent = importCss;
document.head.insertBefore(importStyle, document.head.firstChild);
}
} else {
cssText += importCss;
}
}
}
}
Expand Down
6 changes: 5 additions & 1 deletion test/runner.html
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,11 @@
'unit/custom-style-transformed.html',
'unit/custom-style-transformed.html?dom=shadow',
'unit/custom-style-transformed.html?lazyRegister=true&useNativeCSSProperties=true',
'unit/custom-style-transformed.html?lazyRegister=true&useNativeCSSProperties=true&dom=shadow'
'unit/custom-style-transformed.html?lazyRegister=true&useNativeCSSProperties=true&dom=shadow',
'unit/shady-unscoped-style.html',
'unit/shady-unscoped-style.html?dom=shadow',
'unit/shady-unscoped-style.html?lazyRegister=true&useNativeCSSProperties=true',
'unit/shady-unscoped-style.html?lazyRegister=true&useNativeCSSProperties=true&dom=shadow'
];

if ('import' in document.createElement('link') && (window.customElements || document.registerElement)) {
Expand Down
3 changes: 3 additions & 0 deletions test/unit/shady-unscoped-style-import-css.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.import {
border: 2px solid yellow;
}
26 changes: 26 additions & 0 deletions test/unit/shady-unscoped-style-import.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<dom-module id="global-shared1">
<link rel="import" type="css" shady-unscoped href="shady-unscoped-style-import-css.html">
<template>
<style shady-unscoped>
:root {
--zug: margin: 10px;
}

.happy {
@apply --zug;
border: 1px solid green;
}
</style>

<style>
.normal {
border: 3px solid orange;
}
</style>
</template>
</dom-module>

<dom-module id="global-shared2">
<link rel="import" type="css" shady-unscoped href="shady-unscoped-style-import-css.html">
</dom-module>

121 changes: 121 additions & 0 deletions test/unit/shady-unscoped-style.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
<!doctype html>
<html>
<head>
<script src="../../../webcomponentsjs/webcomponents-lite.js"></script>
<script src="../../../web-component-tester/browser.js"></script>
<link rel="import" href="../../polymer.html">
<link rel="import" href="shady-unscoped-style-import.html">
</head>
<body>

<custom-style>
<style is="custom-style">
html {
--foo: {
padding: 10px;
}
}
</style>
</custom-style>

<dom-module id="my-element">

<template>
<style include="global-shared1 global-shared2">
:host {
display: block;
}

.happy {
@apply --foo;
}
</style>
<div id="fromStyle" class="happy">Happy: green</div>
<div id="fromImport" class="import">Happy: yellow</div>
<div id="normal" class="normal">Happy: orange</div>
</template>

<script>
HTMLImports.whenReady(function() {
Polymer({
is: 'my-element'
});
});
</script>

</dom-module>

<dom-module id="my-element2">

<template>
<style include="global-shared1">
:host {
display: block;
}

</style>
<div id="fromStyle" class="happy">Happy: green</div>
<div id="fromImport" class="import">Happy: yellow</div>
<div id="normal" class="normal">Happy: orange</div>
</template>

<script>
HTMLImports.whenReady(function() {
Polymer({ is: 'my-element2'});
});
</script>

</dom-module>

<my-element></my-element>
<my-element2></my-element2>

<script>
suite('shady-unscoped styles', function() {

function assertComputed(element, value, property, pseudo) {
var computed = getComputedStyle(element, pseudo);
property = property || 'border-top-width';
if (Array.isArray(value)) {
assert.oneOf(computed[property], value, 'computed style incorrect for ' + property);
} else {
assert.equal(computed[property], value, 'computed style incorrect for ' + property);
}
}

var el1 = document.querySelector('my-element');
var el2 = document.querySelector('my-element2');

test('unscoped styles apply', function() {
assertComputed(el1.$.fromStyle, '1px');
assertComputed(el1.$.fromImport, '2px');
assertComputed(el2.$.fromStyle, '1px');
assertComputed(el2.$.fromImport, '2px');
});

test('shared and @apply apply when used with unscoped styles', function() {
assertComputed(el1.$.fromStyle, '10px', 'padding');
assertComputed(el1.$.normal, '3px');
assertComputed(el2.$.normal, '3px');
})

test('unscoped styling de-duped in ShadyDOM', function() {
if (Polymer.Settings.useNativeShadow) {
this.skip();
}
assert.equal(document.head.querySelectorAll('style[shady-unscoped]').length, 2);
});

test('@apply does not apply under ShadyDOM for shady-unscoped styles', function() {
if (Polymer.Settings.useNativeShadow) {
this.skip();
}
assertComputed(el1.$.fromStyle, '0px', 'margin');
assertComputed(el2.$.fromStyle, '0px', 'margin');
})


});
</script>
</body>
</html>

0 comments on commit 90697bf

Please sign in to comment.