Is there a way to inherit :host element css styles into shadow DOM?
The reason is if we start developing web components, each web component style must be consistent on a page.
The page can have global css, and this global css styles can be inherited to shadow DOM. There was ::shadow and /deep/, but it's deprecated now.
Or, is this against pattern? If so, why?
I found this Q/A, but seems outdated for me.
Can Shadow DOM elements inherit CSS?
http://plnkr.co/edit/qNSlM0?p=preview
const el = document.querySelector('my-element');
el.attachShadow({mode: 'open'}).innerHTML = `
<!-- SEE THIS 'red' is not red -->
<p class="red">This is defined in Shadow DOM. I want this red with class="red"</p>
<slot></slot>
`;
.red {
padding: 10px;
background: red;
font-size: 25px;
text-transform: uppercase;
color: white;
}
<!DOCTYPE html>
<html>
<head>
<!-- web components polyfills -->
<script src="//unpkg.com/#webcomponents/custom-elements"></script>
<script src="//unpkg.com/#webcomponents/webcomponentsjs"></script>
<script src="//unpkg.com/#webcomponents/shadydom"></script>
<script src="//unpkg.com/#webcomponents/shadycss#1.0.6/apply-shim.min.js"></script>
</head>
<body>
<div>
<p class="red">I'm outside the element (big/white)</p>
<my-element>
<p class="red">Light DOM content is also affected.</p>
</my-element>
<p class="red">I'm outside the element (big/white)</p>
</div>
</body>
</html>
As supersharp pointed out it's very simple but not obvious from the examples you can find on the internet. Take this base class as an example. Alternatively, you could make two different ones (e.g. Component and ShadowComponent). There is also the option to use adoptedStyleSheets or the ::part selector.
class HtmlComponent extends HTMLElement {
static ModeShadowRoot = 0;
static ModeRoot = 1;
static styleSheets = [];
static mode = HtmlComponent.ModeShadowRoot;
#root = null;
constructor() {
super();
if (this.constructor.mode === HtmlComponent.ModeShadowRoot) {
const shadowRoot = this.attachShadow({ mode: 'closed' });
shadowRoot.adoptedStyleSheets = this.constructor.styleSheets;
this.#root = shadowRoot;
} else {
this.#root = this;
}
}
get root() {
return this.#root;
}
init() {
this.root.innerHTML = this.render();
}
render() {
return '';
}
}
class Test extends HtmlComponent {
static mode = HtmlComponent.ModeRoot;
constructor() {
super();
super.init();
}
render() {
return `
<div>
<x-nested-component></x-nested-component>
</div>
`;
}
}
One of the features of Shadow DOM is to isolate CSS styles.
So if you want your Custom Elements to inherit styles from the main page, don't use Shadow DOM. It's not mandatory.
Related
I am using a shadow DOM for CSS isolation. I want the rem font size to use the HTML element font size in the shadow DOM.
The font in <div class="shadow-div">shadow DOM font (should be 100px)</div> should be 100px but the rem size is still 16px.
Here is a small code snippet demonstrating what I want to do.
<style>
html {
font-size: 16px;
}
</style>
<div>light dom font</div>
<div id="root"></div>
<script>
const root = document.getElementById('root');
root.attachShadow({ mode: 'open' });
const html = document.createElement('html')
html.innerHTML = `
<head>
<style>
html {
font-size: 100px;
}
.shadow-div {
font-size: 1rem;
}
</style>
</head>
<body>
<div class="shadow-div">shadow dom font (should be 100px)</div>
</body>
`;
root.shadowRoot.appendChild(html);
</script>
shadowRoots are not Documents, so don't have <html><head><body> markup;
shadowRoots are more like DocumentFragments
You style shadow content based from the :host selector; and since shadow DOM is encapsulated, there is less need for classes to target that one DIV
REMs are always based on the html definition; use em instead inside the Component
Also note how inheritable properties, like color 'trickle down' into Components and do style content inside shadowDOM (font-size also, but you overload it in the Component in this code)
see: https://lamplightdev.com/blog/2019/03/26/why-is-my-web-component-inheriting-styles/
That simplifies your code to:
<style>
html { font-size: 10px }
body { font-size: 3rem; color:green }
</style>
<div>3rem body font-size = 3 * html font-size</div>
<div id="root"></div>
<script>
document.getElementById('root')
.attachShadow({ mode: 'open' })
.innerHTML = `<style>
:host { font-size: 50px }
div { font-size: 2rem }
p { font-size: .5em }
</style>
50px Component font-size
<div>2rem = 2 * html font-size = 20px</div>
<p>.5em = .5 * :host font-size = 25px</p>`;
</script>
Traditionally, you can add CSS in three ways:
External CSS via <link rel="stylesheet" href="foo.css">
Internal CSS via <style> h1 { ... } in the <head> element
Inline CSS via the style="..." attribute on specific elements
Inline CSS has the drawback that I can't use CSS classes, which is something I need to do. Is there a way to define internal CSS (e.g. a <style></style> fragment in the <body> element?
This would be much easier for me because I could create a self-contained HTML snippet with a method in my server code. This kind of method is necessary because I don't control the <head> section. It is owned by a commercial product. I can only insert content inside the <body>.
Example:
<div>
<style>
.myclass {...}
</style>
<div class="myclass">...</div>
</div>
Related: https://htmx.org/essays/locality-of-behaviour/
I have seen other websites (like https://amazon.com) where they appear to have several style tags inside the <body>.
There is a huge gap between theory and practice. Many sites use <style> in the body.
The editors decided against it. But maybe there will be a change in the future: https://github.com/whatwg/html/issues/1605
Under the premise, you don't care about invalid HTML in relation of <style> inside <body> (which is not that uncommon), you can assign unique identifier i.e.:
<style>
.my-class-1 {
color: gold;
}
</style>
<div class="my-class-1">Fragment Content</div>
<style>
.my-class-2 {
color: tomato;
}
</style>
<div class="my-class-2">Fragment Content</div>
<div class="my-fragment-1">
<style>
.my-fragment-1 .my-class {
color: teal;
}
</style>
<div class="my-class">Fragment Content</div>
</div>
<div class="my-fragment-2">
<style>
.my-fragment-2 .my-class {
color: hotpink;
}
</style>
<div class="my-class">Fragment Content</div>
</div>
<style id="my-style-1">
#my-style-1 + div {
color: orangered;
}
</style>
<div>Fragment Content</div>
<style id="my-style-2">
#my-style-2 + div {
color: indigo;
}
</style>
<div>Fragment Content</div>
the simpler answer to your question is "Yes" and I'll elaborate on this with several examples below. A <style> tag will work wherever you place it within either the <head> or the <body>.
A style tag placed in the <body> tag technically does violate HTML syntax rules, it's surprisingly common in practice, even among some larger corporations.
There are several different methods for including <body>-level <style> tags in your project.
1. Pure HTML <style> tags (the static method)
If you have all the styles you need already written up and there are no dynamic pieces needed, you can simply write those styles into a <style> tag statically and include those in the code, as seen in this example below:
<html>
<head></head>
<body>
<div class="custom-widget">
<h1>This is a title</h1>
<p>This is some text.</p>
<style>
.custom-widget {
display: block;
padding: 20px;
border: 5px double red;
}
.custom-widget h1 {
text-transform: uppercase;
}
.custom-widget h1::first-letter {
font-size: 150%;
}
.custom-widget p {
font-style: italic;
}
</style>
</div>
</body>
</html>
2. Writing the styles into a <style> tag as text using JavaScript
If you need to load the styles into your <style> tag dynamically and you simply need plain text styles that you will not need to change much after creating them. You can create the <style> block and then inject the CSS styles as plain text as desired, as seen in this example below:
const counter = document.getElementById('counter');
let count = +counter.dataset.count;
const customWidgetStyle = document.querySelector('.custom-widget style'),
countdown = setInterval(() => {
if (count--) {
counter.innerText = `Importing CSS in ${count}…`;
} else {
clearInterval(countdown);
counter.remove();
customWidgetStyle.innerHTML = `
.custom-widget {
display: block;
padding: 20px;
border: 5px double red;
}
.custom-widget h1 {
text-transform: uppercase;
}
.custom-widget h1::first-letter {
font-size: 150%;
}
.custom-widget p {
font-style: italic;
}
`;
}
}, 1000);
<html>
<head></head>
<body>
<div class="custom-widget">
<h1>This is a title</h1>
<p>This is some text.</p>
<style></style>
</div>
<span id="counter" data-count="3">Importing CSS in 3…</span>
</body>
</html>
3. Creating cssRules styles into a <style> tag using the JavaScript CSSStyleSheet.insertRule() method
If you need even more flexibility with how you add your styles, you can use the CSSStyleSheet.insertRule() (MDN docs), which will dynamically allow you to add and manage the styles more granularly. This may be overkill for your specific need but there's a lot of power, flexibility, and control when working with the CSSOM.
Here is an example of this method, in which I use an addStylesheetRules function example defined on the MDN docs page for insertRule under the heading Examples, here:
const addStylesheetRules = (rules, stylesheet) => {
if (stylesheet === undefined) {
const style = document.createElement('style');
stylesheet = style.sheet;
document.head.appendChild(style);
}
for (let i = 0; i < rules.length; i++) {
let j = 1,
propStr = '';
rule = rules[i];
const selector = rule[0];
if (Array.isArray(rule[1][0])) {
rule = rule[1];
j = 0;
}
for (let pl = rule.length; j < pl; j++) {
const prop = rule[j];
propStr += prop[0] + ': ' + prop[1] + (prop[2] ? ' !important' : '') + ';\n';
}
stylesheet.insertRule(selector + '{' + propStr + '}', stylesheet.cssRules.length);
}
}
const customWidget = document.querySelector('.custom-widget'),
customWidgetStyleTag = document.createElement('style');
customWidget.appendChild(customWidgetStyleTag);
const customWidgetStylesheet = customWidgetStyleTag.sheet;
addStylesheetRules([
['.custom-widget',
['display', 'block'],
['padding', '20px'],
['border', '5px double red']
],
['.custom-widget h1',
['text-transform', 'uppercase']
],
['.custom-widget h1::first-letter',
['font-size', '150%']
],
['.custom-widget p',
['font-style', 'italic']
]
], customWidgetStylesheet);
<html>
<head></head>
<body>
<div class="custom-widget">
<h1>This is a title</h1>
<p>This is some text.</p>
</div>
</body>
</html>
Please let me know if there is any more context I can add to better answer your question.
It is going to work in html5, even if it's regarded as invalid in html4.
I have an example from where I work now.
We are adding a slideshow for some books in a library catalogue and because this is done with a plugin the only possible way to style this is to include a <style> block with the html, as this plugin doesn't and shouldn't have access to <head> of the CMS it is designed for.
However, this solution is a last resort because of limitations of how the CMS is built and should be avoided.
Couldn't you target the head element with Javascript and insert a style programmatically?
<script>
var head = document.querySelector('head')[0];
var css = 'div { background: red; }',
var style = document.createElement('style');
style.appendChild(document.createTextNode(css));
head.appendChild(style) ;
</script>
As far as I can understand from the description you gave, you don't have the access to the <head>...</head> element, but you are free to edit the body. Also, you want to use CSS3 Classes, but with inline CSS, you can't.
I can't find a way in pure HTML/CSS, so I suggest you to use JQuery.
<script async src="https://cdn.statically.io/libs/jquery/3.6.0/jquery.min.js" />
<script>$('head').append('<style>/*Your styles here*/</style></script>');
Now, add classes to the html elements and edit the content between the <style /> tag. Also, place this script at the end of <body> so that you can avoid the probable weird problems caused by placing this in between. XD
But remember, this will change the head after the user has loaded the page. So, theoretically, the users will first see an ugly html page without styles, then styles will be loaded and they'd see the page with styles.For more explanation, check out the official documentation: https://api.jquery.com/append/
Your example should work. I work with WordPress using custom html where all the custom code goes into a <body> tag, and styling like so should work within each page (added more divs just to show an example that one style tag can hold classes for all divs within a div):
<div>
<style>
.className { ... }
.classNameTwo{ ... }
.classNameThree{ ... }
</style>
<div class="className"></div>
<div class="classNameTwo">
<div class="classNameThree"></div>
</div>
</div>
I try to define a custom field, containing either a SVG or a Canvas. But my example shows some strange rendering. I expect two boxes 400 pixel wide and 300 pixel high. But both boxes seem to collapse in different ways. How can I fix this?
class TestSvg extends HTMLElement
{
constructor() {
super();
const shadow = this.attachShadow({mode: 'open'});
const container = document.createElement('svg');
container.setAttribute('width', '400');
container.setAttribute('height', '300');
shadow.appendChild (container);
}
}
class TestCanvas extends HTMLElement
{
constructor() {
super();
const shadow = this.attachShadow({mode: 'open'});
const container = document.createElement('canvas');
container.setAttribute('width', '400');
container.setAttribute('height', '300');
shadow.appendChild (container);
}
}
customElements.define ('test-svg', TestSvg);
customElements.define ('test-canvas', TestCanvas);
test-svg, test-canvas {
border: 1px solid black;
}
svg
<test-svg>
</test-svg>
canvas
<test-canvas>
</test-canvas>
end
Same without custom elements:
svg, canvas {
border: 1px solid black;
}
svg
<svg width="400" height="300"></svg>
canvas
<canvas width="400" height="300"></canvas>
end
Why is there a difference between the version with custom elements and the version without custom elements?
Your SVG element is not being created correctly. It needs to be in the correct SVG namespace. Change it to this:
const container = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
By default, your custom elements will be display: inline. Set them to block or inline-block depending on your need.
test-svg, test-canvas {
display: inline-block;
border: 1px solid black;
}
Updated test:
class TestSvg extends HTMLElement
{
constructor() {
super();
const shadow = this.attachShadow({mode: 'open'});
const container = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
container.setAttribute('width', '400');
container.setAttribute('height', '300');
shadow.appendChild (container);
}
}
class TestCanvas extends HTMLElement
{
constructor() {
super();
const shadow = this.attachShadow({mode: 'open'});
const container = document.createElement('canvas');
container.setAttribute('width', '400');
container.setAttribute('height', '300');
shadow.appendChild (container);
}
}
customElements.define ('test-svg', TestSvg);
customElements.define ('test-canvas', TestCanvas);
test-svg, test-canvas {
display: inline-block;
border: 1px solid black;
}
svg
<test-svg>
</test-svg>
canvas
<test-canvas>
</test-canvas>
end
Good to see more people are combining Custom Elements and SVG, they are a good match
Your core problem was the SVG NameSpace, so Paul his answer is correct.
Some additional comments:
Your constructor can be optimized:
constructor() {
super();
const shadow = this.attachShadow({mode: 'open'});
const container = document.createElementNS('http://www.w3.org/2000/svg','svg');
container.setAttribute('width', '400');
container.setAttribute('height', '300');
shadow.appendChild (container);
}
super() returns the element scope
Google documentation that says to use super() first is wrong,
they mean: Call super() before you can use the 'this' scope reference.
attachShadow() boths sets AND returns this.shadowRoot for free
.appendChild() returns the created element
So you can chain everything:
constructor() {
const container = super()
.attachShadow({mode:'open'})
.appendChild (document.createElementNS('http://www.w3.org/2000/svg','svg'));
container.setAttribute('width', '400');
container.setAttribute('height', '300');
}
If you do not do anything special with this in memory Custom Element,
you can scrap the whole constructor and create the element with HTML in the connectedCallback
connectedCallback(){
this.innerHTML = `<svg width='400' height='300'
xmlns='http://www.w3.org/2000/svg'
viewBox='0 0 20 20'></svg>`;
}
Note: I also ditched shadowDOM above; if you do want shadowDOM its:
connectedCallback(){
this.attachShadow({mode:"open"})
.innerHTML = `<svg width='400' height='300'
xmlns='http://www.w3.org/2000/svg'
viewBox='0 0 20 20'></svg>`;
}
There is another sizing problem
Depending on your Font family and size there will be a gutter below your inline-block SVG Custom Element (see RED below) to allow for pgjq characters that stick out below the base line.
To counter that you have to set vertical-align: top on the SVG element:
<style>
body { font-size: 3em }
svg-circle { background: RED }
svg-circle svg {
background: grey;
display: inline-block;
width: 80px;
}
#correct svg { vertical-align: top }
</style>
<div>
<svg-circle radius="40%" color="green"></svg-circle>
<svg-circle x="50%" y="100%" color="blue"></svg-circle>
<svg-circle></svg-circle>
</div>
<div id="correct">
<svg-circle radius="40%" color="green"></svg-circle>
<svg-circle x="50%" y="100%" color="blue"></svg-circle>
<svg-circle></svg-circle>
</div>
<script>
customElements.define("svg-circle", class extends HTMLElement {
static get observedAttributes() { return ["x", "y", "radius", "color"] }
connectedCallback() { this.render() }
attributeChangedCallback() { this.render() }
render() {
let [x = "50%",y = "50%",radius = "50%", color = "rebeccapurple"] =
this.constructor.observedAttributes.map(x => this.getAttribute(x) || undefined);
this.innerHTML = `<svg viewBox='0 0 96 96'>
<circle cx='${x}' cy='${y}' r='${radius}' fill='${color}'/></svg>`;
}
});
</script>
Note: There is no shadowDOM attached to <svg-circle>, so the SVG inside can be styled with global CSS
Make it an IMG
If you do not want any CSS bleeding, and want to work with the SVG as if it is an image,
without pointer-events issues, then create an IMG:
this.innerHTML = `<img src="data:image/svg+xml,<svg viewBox='0 0 96 96'>
<circle cx='${x}' cy='${y}' r='${radius}' fill='${color}'/>
</svg>">`.replace(/#/g, "%23");
Note: The # is the only character that needs to be escaped here. In CSS url() usage you also need to escape the < and >
Add a grid
If you are going to create an Icon Toolbar or Chessboard like layout with SVGs, add a grid:
#correct {
display: grid;
grid-template-columns: repeat(3, 80px);
}
svg-circle svg {
background: grey;
display: inline-block;
width: 100%;
height: 100%;
}
In the first custom element TestSvg, change the element type from svg to canvas
const container = document.createElement('canvas');
HTML elements reference
https://developer.mozilla.org/en-US/docs/Web/HTML/Element
'canvas' is an HTML element.
where as 'svg' is not. 'svg' is Container element, structure element
I have a custom element (without shadow DOM) that I'd like to be able to use anywhere, even inside another custom element that might use shadow DOM. However, I'm not sure how to get the styles working in both places.
For example, lets say I create a simple fancy-button element:
class fancyButton extends HTMLElement {
constructor() {
super();
this.innerHTML = `
<style>
fancy-button button {
padding: 10px 15px;
background: rgb(62,118,194);
color: white;
border: none;
border-radius: 4px
}
</style>
<button>Click Me</button>`;
}
}
customElements.define('fancy-button', fancyButton);
<fancy-button></fancy-button>
Inside a shadow DOM element, the inserted style tag will allow the fancy-button styles to work. However, if this component gets used outside of a shadow DOM element, the style tag will be duplicated every time the element is used.
If instead I add the style tag as part of the html import file, then the styles only work outside of the shadow DOM but at least they are only declared once.
<!-- fancy-button.html -->
<style>
fancy-button button {
padding: 10px 15px;
background: rgb(62,118,194);
color: white;
border: none;
border-radius: 4px
}
</style>
<script>
class fancyButton extends HTMLElement {
constructor() {
super();
this.innerHTML = `<button>Click Me</button>`;
}
}
customElements.define('fancy-button', fancyButton);
</script>
What's the best way to add custom element styles that handles both being used inside and outside the shadow DOM?
So I was able to find a solution thanks to Supersharp suggestions about checking if we're in the shadow DOM.
First you add the styles as part of the import file so that the styles apply outside of the shadow DOM by default. Then when element is added to the DOM, we check getRootNode() to see if it's been added to a ShadowRoot node. If it has, and the styles haven't already been injected into the root, then we can inject the styles manually.
var div = document.createElement('div');
var shadow = div.attachShadow({mode: 'open'});
shadow.innerHTML = '<fancy-button></fancy-button>';
document.body.appendChild(div);
<style data-fs-dialog>
fancy-button button {
padding: 10px 15px;
background: rgb(62,118,194);
color: white;
border: none;
border-radius: 4px
}
</style>
<script>
class fancyButton extends HTMLElement {
constructor() {
super();
}
connectedCallback() {
this.innerHTML = `<button>Click Me</button>`;
var root = this.getRootNode();
// In polyfilled browsers there is no shadow DOM so global styles still style
// the "fake" shadow DOM. We need to test for truly native support so we know
// when to inject styles into the shadow dom. The best way I've found to do that
// is to test the toString output of a shadowroot since `instanceof ShadowRoot`
// returns true when it's just a document-fragment in polyfilled browsers
if (root.toString() === '[object ShadowRoot]' && !root.querySelector('style[data-fs-dialog]')) {
var styles = document.querySelector('style[data-fs-dialog]').cloneNode(true);
root.appendChild(styles);
}
}
}
customElements.define('fancy-button', fancyButton);
</script>
<fancy-button></fancy-button>
When all browsers support <link rel=stylesheet> in the shadow DOM, then the inline script can turn into an external stylesheet as robdodson suggested, and the code is a bit cleaner.
You'll probably want to put the styles in a separate CSS file that you vend along with your element's JS. But as you've pointed out, if you put the element inside the Shadow DOM of another element then the styles won't work in that scope. For this reason it's usually best to just create a shadow root and pop your styles in there. Any reason why you wouldn't want to do that?
I have created my first web application and have .html, .dart, and .css files. I want to create a modal page that will a bit smaller than my page and be centered on it. I don't really want any visible borders. The function of this page is to allow the user to display, using clickable elements, 'Help' and 'About' pages and a page that allow the user to see a list of the data files that have been collected.
I've found a couple of examples of modal pages but they are old. One appears to be easy to understand but the Dart editor flags a couple of errors and has a line that I don't understand at the head of the .dart file.
#import('dart:html'); // OK just remove the '#"
#resource('modal.css'); // ???
This example is in a blog DartBlog that does not appear to be active and did not allow me to leave a comment.
I would appreciate an help understanding the example or pointing me to working examples.
This import statement is outdated Dart syntax.
use instead
import 'dart:html';
I never saw the #resource thing and I'm sure this is not available anymore as well.
You can either put a style tag to your HTML file like
<html>
<head>
<style>
.black_overlay{
display: block;
position: absolute;
top: 0%;
left: 0%;
width: 100%;
height: 100%;
background-color: black;
z-index:1001;
-moz-opacity: 0.8;
opacity:.80;
filter: alpha(opacity=80);
}
.white_content {
display: block;
position: absolute;
top: 25%;
left: 25%;
width: 50%;
height: 50%;
padding: 16px;
border: 16px solid orange;
background-color: white;
z-index:1002;
overflow: auto;
}
</style>
</head>
<body>
</body>
</html>
or put the CSS content in a file like styles.css and import it to your HTML
<html>
<head>
<link rel='stylesheet' href='styles.css'>
</head>
<body>
</body>
</html>
I tried to update the code to current syntax (not tested though) and I added some comments
import 'dart:html';
void main() {
//setup the screen elements...
ButtonElement button = new ButtonElement();
button.text = "click me";
//add an event handler
button.onclick.listen((event) {
ModalDialog dialog = new ModalDialog("This is a <strong>message</strong>
with html formatting");
dialog.show();
});
//add the button to the screen
document.body.append(button);
//add the modal dialog stylesheet
document.head.append(getStylesheet());
}
/**
* Our modal dialog class
*/
class ModalDialog {
final DivElement _content;
final DivElement _blackOverlay;
final ButtonElement _button;
//Constructor
ModalDialog(String message) :
//constructor pre-init
_content = new DivElement(),
_blackOverlay = new DivElement(),
_button = new ButtonElement()
{
//constructor body
_content.id = "modalContent";
_content.classes.add("white_content"); //set the class for CSS
_blackOverlay.id = "modalOverlay";
_blackOverlay.classes.add("black_overlay"); //set the class for CSS
//Our message will go inside this span
SpanElement span = new SpanElement();
span.innerHTML = message;
_content.append(span);
//This is the button that will "clear" the dialog
_button.text = "Ok";
_button.onClick.listen((event) {
hide();
});
_content.append(_button);
}
//remove the modal dialog div's from the dom.
hide() {
//find the element and remove it.
//there is no list.remove(x) statement at present,
// so we have to do it manually. - UPDATE: now there is
_content.remove();
_blackOverlay.remove();
}
//add the modal dialog div's to the dom
show() {
document.body.append(_content);
document.body.append(_blackOverlay);
}
}
/**
* Utility method to get a stylesheet object
*/
getStylesheet() {
LinkElement styleSheet = new LinkElement(); // maybe
styleSheet.rel = "stylesheet";
styleSheet.type="text/css";
styleSheet.href="modal.css"; // UPDATE: you don't need to add your CSS to your HTML as shown above because it's done in this code
return styleSheet;
}