diff --git a/src/compiler/compile/Component.ts b/src/compiler/compile/Component.ts
index 9822529ece61..3dfb4fd275cf 100644
--- a/src/compiler/compile/Component.ts
+++ b/src/compiler/compile/Component.ts
@@ -14,7 +14,7 @@ import Stylesheet from './css/Stylesheet';
import { test } from '../config';
import Fragment from './nodes/Fragment';
import internal_exports from './internal_exports';
-import { Ast, CompileOptions, Var, Warning, CssResult } from '../interfaces';
+import { Ast, CompileOptions, Var, Warning, CssResult, ShadowDomMode } from '../interfaces';
import error from '../utils/error';
import get_code_frame from '../utils/get_code_frame';
import flatten_reference from './utils/flatten_reference';
@@ -30,6 +30,7 @@ import check_graph_for_cycles from './utils/check_graph_for_cycles';
import { print, x, b } from 'code-red';
interface ComponentOptions {
+ shadowdom?: ShadowDomMode;
namespace?: string;
tag?: string;
immutable?: boolean;
@@ -158,6 +159,7 @@ export default class Component {
});
}
this.tag = this.component_options.tag || compile_options.tag;
+ this.compile_options.shadowDom = this.component_options.shadowdom || "open";
} else {
this.tag = this.name.name;
}
@@ -170,7 +172,7 @@ export default class Component {
this.walk_instance_js_post_template();
- if (!compile_options.customElement) this.stylesheet.reify();
+ if (!compile_options.customElement || compile_options.shadowDom=="none") this.stylesheet.reify();
this.stylesheet.warn_on_unused_selectors(this);
}
@@ -1414,7 +1416,16 @@ function process_component_options(component: Component, nodes) {
component_options[name] = value;
break;
}
+ case 'shadowdom': {
+ const code = 'invalid-shadowdom-attribute';
+ const message = `'shadowdom' must be set to 'open', 'closed or 'none'`;
+ const value = get_value(attribute, code, message);
+ if (value != "open" && value != "none" && value != "closed")
+ component.error(attribute, { code, message });
+ component_options[name] = value;
+ break;
+ }
default:
component.error(attribute, {
code: `invalid-options-attribute`,
diff --git a/src/compiler/compile/index.ts b/src/compiler/compile/index.ts
index 12b161aeeb0e..5fe11fd2f5eb 100644
--- a/src/compiler/compile/index.ts
+++ b/src/compiler/compile/index.ts
@@ -22,6 +22,7 @@ const valid_options = [
'hydratable',
'legacy',
'customElement',
+ 'shadowDom',
'tag',
'css',
'loopGuardTimeout',
diff --git a/src/compiler/compile/render_dom/index.ts b/src/compiler/compile/render_dom/index.ts
index 26fa4a70f81f..0a3857ee1aea 100644
--- a/src/compiler/compile/render_dom/index.ts
+++ b/src/compiler/compile/render_dom/index.ts
@@ -20,7 +20,7 @@ export default function dom(
block.has_outro_method = true;
// prevent fragment being created twice (#1063)
- if (options.customElement) block.chunks.create.push(b`this.c = @noop;`);
+ if (options.customElement && options.shadowDom !== "none") block.chunks.create.push(b`this.c = @noop;`);
const body = [];
@@ -29,7 +29,7 @@ export default function dom(
body.push(b`const ${renderer.file_var} = ${file};`);
}
- const css = component.stylesheet.render(options.filename, !options.customElement);
+ const css = component.stylesheet.render(options.filename, (!options.customElement || options.shadowDom === "none"));
const styles = component.stylesheet.has_styles && options.dev
? `${css.code}\n/*# sourceMappingURL=${css.map.toUrl()} */`
: css.code;
@@ -37,9 +37,9 @@ export default function dom(
const add_css = component.get_unique_name('add_css');
const should_add_css = (
- !options.customElement &&
+ (!options.customElement &&
!!styles &&
- options.css !== false
+ options.css !== false ) || options.shadowDom === "none"
);
if (should_add_css) {
@@ -437,14 +437,19 @@ export default function dom(
}
if (options.customElement) {
+ const lightDom = options.shadowDom === 'none';
const declaration = b`
class ${name} extends @SvelteElement {
constructor(options) {
super();
+ ${!lightDom && b`
+ this._root =this.attachShadow({ mode: '${options.shadowDom}' });
+ `}
- ${css.code && b`this.shadowRoot.innerHTML = \`\`;`}
+ ${css.code && !lightDom && b`this._root.innerHTML = \`\`;`}
+ ${should_add_css && lightDom && b`if (!@_document.getElementById("${component.stylesheet.id}-style")) ${add_css}();`}
- @init(this, { target: this.shadowRoot }, ${definition}, ${has_create_fragment ? 'create_fragment': 'null'}, ${not_equal}, ${prop_indexes}, ${dirty});
+ @init(this, { target: ${lightDom ? 'this' : 'this._root'} }, ${definition}, ${has_create_fragment ? 'create_fragment': 'null'}, ${not_equal}, ${prop_indexes}, ${dirty});
${dev_props_check}
diff --git a/src/compiler/interfaces.ts b/src/compiler/interfaces.ts
index a5e286462ff3..a7932615fa33 100644
--- a/src/compiler/interfaces.ts
+++ b/src/compiler/interfaces.ts
@@ -120,6 +120,7 @@ export interface CompileOptions {
hydratable?: boolean;
legacy?: boolean;
customElement?: boolean;
+ shadowDom?: ShadowDomMode;
tag?: string;
css?: boolean;
loopGuardTimeout?: number;
@@ -166,4 +167,8 @@ export interface Var {
export interface CssResult {
code: string;
map: SourceMap;
-}
\ No newline at end of file
+}
+
+export type ShadowDomMode = 'none'
+ | 'open'
+ | 'closed'
\ No newline at end of file
diff --git a/src/runtime/internal/Component.ts b/src/runtime/internal/Component.ts
index 10588a08046b..f8fbcf8ced2d 100644
--- a/src/runtime/internal/Component.ts
+++ b/src/runtime/internal/Component.ts
@@ -167,7 +167,6 @@ if (typeof HTMLElement === 'function') {
$$: T$$;
constructor() {
super();
- this.attachShadow({ mode: 'open' });
}
connectedCallback() {
diff --git a/test/custom-elements/samples/shadowdom-closed/main.svelte b/test/custom-elements/samples/shadowdom-closed/main.svelte
new file mode 100644
index 000000000000..43c2300bc04f
--- /dev/null
+++ b/test/custom-elements/samples/shadowdom-closed/main.svelte
@@ -0,0 +1,8 @@
+