Load essential styles to shadow-dom - javascript

I love the idea of shadow dom styles encapsulation, but I'd like to include base styles to each shadow dom (reset, typography, etc).
<head>
<link rel="stylesheet" href="core.css">
...
</head>
<my-component></my-component>
<script>
customElements.define('my-component', class MyComponent extends HTMLElement {
...
connectedCallback() {
this.shadow = this.attachShadow({mode: 'open'});
const linkElem = document.createElement('link');
linkElem.setAttribute('rel', 'stylesheet');
linkElem.setAttribute('href', 'core.css');
// applying exiting "core.css" to current shadow dom
this.shadow.appendChild(linkElem);
}
});
</script>
Since core.css was called (linked) twice will it affect the performance?

You could try use Constructable Stylesheet Objects
With this approach you can define global styles and then use them with shadowRoot.adoptedStylesheets like the following:
import {
resetSheet,
headlinesSheet,
coreSheet,
} from '/style-system.js';
import {
myComponentStyles,
} from './styles.js';
// ...
connectedCallback() {
// Only compose styles once
if (!this.shadowRoot.adoptedStyleSheets.length) {
this.shadowRoot.adoptedStyleSheet = [
// global stylesheets
resetSheet,
headlinesSheet,
coreSheet,
// specific sheet for this component
myComponentStyles
]
}
}
The main advantages with this approach in comparison with the one you're using (creating link elements to each component) are:
You can share global styles to be used across multiple components defining them once
You only load the styles being used by that component being rendered and this scales performance because it lazily loads stylesheets
You can change those global styles dynamically (as they're a JS component) without the need of applying changes in multiple elements, making more decoupled changes
As this is a quite new approach, I'd recommend you read the following articles in order to create a more solid knowledge about Constructable Stylesheet Objects
Constructible Stylesheets
Why Would Anyone Use Constructible Stylesheets, Anyways?
Adopt a Design System inside your Web Components with Constructable Stylesheets

The browser will cache the request for core.css so there's not really a performance penalty, but since the stylesheet will be loaded asynchronously you might get a flash of unstyled content (FOUC) while the browser fetches the css for the first time.
One possible way to get around this is to preload the css file in your document <head>, so that it will (probably, as preloads are only 'hints' to the browser) be available by the time your Shadow DOM is parsed:
<link rel="preload" href="core.css" as="style">

Related

Generating a class in javascript to match existing sass class name

I am using webpack to generate a single bundle.js file containing everything I need in a single file, and the webpack compiles and seems to contain what I expect. I am dynamically generating DOM elements in javascript that I would like to be formatted with existing sass class specifications as follows:
index.scss:
.classname {
position: 'absolute';
font-family: 'Roboto', sans-serif;
color: blue;
}
index.js:
import styles from './css/index.scss';
console.log(styles);
let aDiv = document.createElement("div");
aDiv.classList.add("classname");
aDiv.style.top = "10px";
document.body.append(aDiv);
let bDiv = document.createElement("div");
bDiv.classList.add(styles.classname);
aDiv.style.top = "30px";
document.body.append(bDiv);
In Firefox's developer tools, from the console, I can see that styles is an object with a property for each of many css rules, one of which is:
"classname": "_2mpyBRPOMJ9D3LKNxOIvrs"
In Firefox's Inspector tool, aDiv's class is "classname", but no styles are applied from that class. bDiv's class is "undefined", and no styles are applied there either. I cannot figure out how to get the "_2mpyBRPOMJ9D3LKNxOIvrs" class name, which is different each time I compile, assigned as a class. But even when I do this manually from Firefox, the styles are never applied, and neither "classname" or "_2mpyBRPOMJ9D3LKNxOIvrs" exist in Firefox's style editor.
Is it possible to dynamically create an element that inherits styles from scss via its class name? Or is there a nicer cleaner way to do this that I'm missing? I feel I must have failed to grasp a central tenet of webpacking css into js.
This is because the minification plugin changes your class names to new references when your scss gets merged as css into your code. You can disable this behavior by configuring webpack. Thia answer tells you how to do it:
How to disable webpack minification for classes names

What happens when i use inline style and css files in React?

I am refering to a situation in which i have something like this:
import "./myCss.css"
const BeautyButton = (props) =>{
return (
<a style={{backgroundColor:"red"}} className="blue-background" >hello</a>
);
}
Which property prevails? Is there a general rule for all the properties (fontSize, width, etc)?
There is nothing different between how CSS works in React and with regular HTML. To understand which rules take effect, you'll want to study the concept of Specificity in CSS.
For your specific question, inline styles are considered more specific than styles in external stylesheets, so those styles will prevail.

Vue.js detach styles from template

I use a template with a <style> block which must be near its div for CMS reasons.
When I run Vue.js, it seems to remove the style block, saying...
- Templates should only be responsible for mapping the state to the UI.
Avoid placing tags with side-effects in your templates, such as <style>,
as they will not be parsed.
What can I do?
var app = new Vue({
el: '#app'
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.2.0/vue.js"></script>
<div id="app">
<style>
#div_123 {
background: http://placehold.it/850x150;
}
#media screen and (max-width: 640px) {
#div_123 {
background: http://placehold.it/350x150;
}
}
</style>
<div id="div_123">
Test
</div>
</div>
The Issue
In Vue 2 the root instance is treated more like a component than it was in Vue 1.
This means when you bind the Vue instance to #app it digests everything in #app as a vue template. This means tags are invalid and they'll be removed from the template. This is just the way things work in Vue 2.
Recreation
I recreated the issue in a codepen here
https://codepen.io/Fusty/pen/gqXavm?editors=1010
The <style> tag nested within the tag Vue is bound to. It should style the background red and the text color green. However, we see only a flash of this (depending on how fast your browser fires up Vue) and eventually vue removes these style tags as it digest #app as a template and then updates the DOM with what it thinks should be there (without <style> tags).
Better Recreation
Thanks to user #joestrouth1#6053 on the Vue-Land discord, we also have this fork of my recreation of the issue.
https://codepen.io/joestrouth1/pen/WPXrbg?editors=1011
Check out the console. It reads . . .
"[Vue warn]: Error compiling template:
Templates should only be responsible for mapping the state to the UI. Avoid placing tags with side-effects in your templates, such as <style>, as they will not be parsed.
1 | <div>
2 | <style>
| ^^^^^^^
... etc ...
Complaining about the style tags in a template.
This zeroes in on the actual issue. It is good to note this doesn't occur in Vue 1. Probably because it treats the root instance more uniquely than components, but I am not 100% sure on this topic.
Solution (Hack, not best practice or especially recommended)
The <style> tags are still in the DOM during the created lifecycle hook for the Vue instance and they are removed by the time the mountedlifecycle hook fires. Let's just query for all of the style tags within the #app element, save them, and then append them back to the #app element after Vue has digested the template.
Adding the following to your root Vue instance will take any <style> tags within whatever element your Vue instance is bound to (via el: 'someSelector') and append them (possibly relocating them) to the element your Vue instance is bound to.
created: function() {
this.styleTagNodeList = document.querySelector(this.$options.el).querySelectorAll('style');
},
mounted: function() {
for(var i = 0; i < this.styleTagNodeList.length; ++i)
this.$el.appendChild(this.styleTagNodeList[i]);
}
NOTE: This is definitely a hack which likely has unintended consequences I have not run into yet and cannot specifically disclaim. USE AT YOUR OWN RISK.
This works for my specific situation where I allow the users to store a string of CSS and then I need to render it on specific pages - ei: preview page.
The context here is css is saved as string in database, fetched and rendered within an Vue component.
# html
<html>
<head>
<style id="app_style"></style>
</head>
<body>
<div id="app"></div>
</body>
</html>
# app.vue
data() {
return {
dynamic_css: ''
}
},
created() {
// fetch css from database, set as `this.dynamic_css`
},
watch {
dynamic_css: function(newValue) {
document.getElementById('app_style').innerHTML = newValue
}
}

JavaScript: (How) can one retrieve external CSS rules in Chrome's DevTools?

I'm working on a little Chrome extension that extends its DevTools. For this, I need to acquire all of the defined CSS selectors for the currently selected element ($0).
I know each item within document.styleSheets exposes all of the necessary data through cssRules. This would be perfect, but unfortunately CORS seems to throw a spanner in the works. For external stylesheets, cssRules returns null.
Is it possible to access this data without resorting to a hackish solution, e.g. downloading the stylesheet and inserting it into a style tag? I ask because Chrome itself seems to do so in its Styles sidebar panel, but I can't find much information on the matter.
Thanks!
I think I've figured it out. It just took a bit more digging in the documentation.
The inspectedWindow API exposes getResources, allowing you to fetch all of the resources within the inspected window. This includes type and functionality for fetching its content.
Injecting this content into a style tag allows you to access the CSS rules via document.styleSheets. This is ideal because my sidebar pane is encapsulated within a shadow DOM, allowing me to know exactly which stylesheets have been injected.
chrome.devtools.inspectedWindow.getResources(function(resources) {
for (var i = 0; i < resources.length; i++) {
if (resources[i].type != 'stylesheet') {
continue;
}
// inject the resource into the shadow DOM
// this allows us to freely access all CSS rules CORS-free
resources[i].getContent(function(content) {
var style = document.createElement('style');
style.textContent = content;
document.body.appendChild(style);
});
}
});

Modulizing CSS Stylesheets with RequireJS

I have created a template like so:
// template.tpl
<div>
<input id="an_input"></input>
</div>
and some CSS:
// stylesheet.css
input {
background: #000000;
}
Finally this is a slimmed down module:
define([
'jquery',
'text!template.tpl',
'text!styleshet.css'
], function($, html, css){
var view = $('#sample_div');
view.append($(html));
var regex = /^([^\s\}])/gm;
var styles = css.replace(regex, '#'+view.attr('id')+' $1');
var style = $('<style>\n'+styles+'\n</style>');
view.prepend(style);
});
What is essentially happening, is the template is being loaded and put into the #sample_div. Shortly after the CSS file is being loaded as text, then every item is prefixed with the ID of the view.
Once the CSS is prefixed, the style tag is created and placed inside the view.
Now, this works perfectly, OK it isn't pretty, nor does it leave much margin for error. However I wrote this code to help demonstrate what I need.
I need to be able to load templates with view specific stylesheets, where the styles in the sheet will only ever apply to the view and will only override global styles.
The problem with the above example is that it is a hack, a regex against the CSS, and the building of a new style tag, this is not how I want to do it. I have been looking into javascript CSS parsers for a cleaner solution, and although JSCSSP caught my eye, it put to many functions into the global namespace, and jquery.parsecss only seems to work with styles already within the document.
Does anyone have any experience with what I am trying to achieve?
Most loaders out there have CSS plugins that handle the insertion for you:
RequireJS CSS plugin
https://github.com/tyt2y3/requirejs-css-plugin
CurlJS CSS plugin is bundled with the main distribution:
https://github.com/cujojs/curl/tree/master/dist

Categories

Resources