From 804220b929822d2d8ade0bf84e2055e4e48b3df5 Mon Sep 17 00:00:00 2001 From: Nolan Lawson Date: Wed, 8 Feb 2023 13:57:49 -0800 Subject: [PATCH 1/6] RFC: Dual light/shadow components --- text/0000-dual-light-shadow.md | 256 +++++++++++++++++++++++++++++++++ 1 file changed, 256 insertions(+) create mode 100644 text/0000-dual-light-shadow.md diff --git a/text/0000-dual-light-shadow.md b/text/0000-dual-light-shadow.md new file mode 100644 index 00000000..a23e2237 --- /dev/null +++ b/text/0000-dual-light-shadow.md @@ -0,0 +1,256 @@ +--- +title: Dual light/shadow components +status: DRAFTED +created_at: 2022-02-08 +updated_at: 2022-02-08 +champion: Nolan Lawson (nolanlawson) +pr: https://github.com/salesforce/lwc-rfcs/pull/77 +--- + +# Dual light/shadow components + +## Summary + +[Light DOM components](https://rfcs.lwc.dev/rfcs/lwc/0115-light-dom) are a useful feature in certain contexts. +However, LWC currently requires a component to be either light or shadow – it cannot be both at once. + +This RFC proposes a "dual" mode where the same component can run in either light DOM mode or shadow DOM mode. + +## Basic example + +A component can declare its `lwc:render-mode` to be `"dual"`: + +```html + + +``` + +```js +// component.js +export default class extends LightningElement { + static renderMode = 'dual'; +} +``` + +Then, a parent component can use `lwc:render-mode` to choose to render the child as either light DOM or shadow DOM: + +```html + +``` + +```html + +``` + +This will result in `` being rendered as either light DOM: + +```html + +

Hello world

+
+``` + +or shadow DOM: + +```html + + #shadow-root +

Hello world

+
+``` + +## Motivation + +Light DOM and shadow DOM have tradeoffs relative to each other. Each may be useful in certain cases. + +Also, the design of LWC lends itself to largely shadow-DOM-neutral component authoring. For instance, LWC supports scoped styles, light DOM ``s, and `lwc:ref` – all of which work roughly the same between shadow components and light components. So there is not a strong reason for a component to declare upfront whether it is light or shadow. + +So naturally, a request we've heard from component authors is that they would like to be able to author a single component and have it render as either light or shadow DOM. + +To be fair, there is already a workaround today: composition. I.e., a component could be authored in light DOM, and then a wrapper component could be authored in shadow DOM. + +However, this workaround has several downsides: + +1. All properties have to be reflected between the shadow parent and light child. +2. All slots have to be propagated from the parent to the child. +3. An extra wrapper component is required, which may be undesired for common base components (e.g. buttons, icons). +4. The two components have different external tag names (e.g. `my-button` and `my-button-shadow`). + +This RFC has none of these downsides. + +## Detailed design + +Currently the `lwc:render-mode` directive / `static renderMode` property only allows two values: `"light"` and `"shadow"`. + +This proposal adds a third value: `"dual"`. + +```html + + +``` + +```js +// component.js +export default class extends LightningElement { + static renderMode = 'dual'; +} +``` + +Currently, `lwc:render-mode` can only be applied to the top-level ` ``` -If a dual-mode component is referenced _without_ an explicit `lwc:render-mode`, then it is considered a shadow component: +### No explicit `lwc:render-mode` + +If a dual-mode component is referenced _without_ an explicit `lwc:render-mode`, then it is considered to be a shadow component: ```html ``` +### Non-dual-mode components + +If `lwc:render-mode` is applied to a component that is _not_ a dual-mode component, then a runtime error is thrown: + +```html + +``` + +```js +// notDual.js +export default class extends LightningElement { +} +``` + +(This error is thrown regardless of whether the component would have rendered in shadow DOM or light mode.) + +### Dynamic components + +`lwc:render-mode` is also supported on [dynamic components](https://github.com/salesforce/lwc-rfcs/pull/71), since it +is already known in advance that the dynamic component is an LWC component. + +```html + +``` + +However, if a non-dual-mode component is rendered, then a runtime error is thrown. + +### Shadow DOM mixed mode + A dual-mode component can also use the `static shadowSupportMode` property to control whether it renders in native or synthetic shadow. This only applies when it is running in shadow mode. @@ -161,7 +210,7 @@ In short: 1. Any restrictions that apply to either light DOM components or shadow DOM components also apply to dual components. 2. Component authors are responsible for handling runtime differences, e.g. `this.querySelector` vs `this.template.querySelector`. -### Light DOM restrictions that apply +#### Light DOM restrictions that apply Following [light DOM restrictions](https://rfcs.lwc.dev/rfcs/lwc/0115-light-dom), a dual-mode component cannot have a `slotchange` event listener on a ``. This throws a compile-time error: @@ -175,7 +224,7 @@ a `slotchange` event listener on a ``. This throws a compile-time error: However, unlike light DOM components, a dual-mode component can use `lwc:dom="manual"` (see next section). -### Shadow DOM restrictions that apply +#### Shadow DOM restrictions that apply `lwc:dom="manual"` must be used for manual DOM operations if a dual-mode component is running in synthetic shadow mode. If not, an error will be logged (as is currently the case with normal shadow components). @@ -184,7 +233,7 @@ an error will be logged (as is currently the case with normal shadow components) shadow DOM components. If a component author uses `lwc:slot-bind` in a dual-mode template, then a compile-time error is thrown (the same as with normal shadow components). -### Behavior that differs at runtime between light and shadow +#### Behavior that differs at runtime between light and shadow Some behavior will differ at runtime between light DOM mode and shadow DOM mode. Dual-mode components are responsible for handling these differences themselves. @@ -196,15 +245,7 @@ A non-exhaustive list: - Slots are lazy in light DOM and eager in native shadow DOM. - Scoped and non-scoped stylesheets will both behave differently in light versus shadow mode. -### Coherence - -If a template is declared to be `lwc:render-mode="dual"`, then the corresponding component must also have -`static renderMode = 'dual'`, and vice versa. - -If not, an error will be thrown at runtime. (This is the same as what currently happens if `"shadow"` and `"light"` -are mixed between the two.) - -### Runtime modifications +#### Runtime modifications A component cannot dynamically modify its `static renderMode`; changing it after instantiation has no effect. From 7839d3ab71fe5df828ca7f90405d5991dad022cc Mon Sep 17 00:00:00 2001 From: Nolan Lawson Date: Wed, 8 Feb 2023 15:25:49 -0800 Subject: [PATCH 3/6] fix: fix typo --- text/0000-dual-light-shadow.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-dual-light-shadow.md b/text/0000-dual-light-shadow.md index 269fedd1..df357c7a 100644 --- a/text/0000-dual-light-shadow.md +++ b/text/0000-dual-light-shadow.md @@ -108,7 +108,7 @@ export default class extends LightningElement { ### Switching between light and shadow mode -Currently, `lwc:render-mode` can only be applied to the top-level ` ``` +Only three values are accepted in this context: `"light"`, `"shadow"`, or `"inherit"`. Any other value will throw a compile-time error. + +### Inheritance + +`"inherit"` is a special value of `lwc:render-mode` that can only be used in the context of a child LWC component: + +```html + +``` + +This instructs the `` to render using whatever mode the parent component is using – either shadow or light. If `` is not a dual-mode component, then a runtime error will be thrown. + +For the parent component, however, `"inherit"` can be used in non-dual-mode components as well as dual-mode components: + +```html + +``` + +In this case, the same rules apply – the child component uses the mode inherited from its parent. + +The above example is equivalent to: + +```html + +``` + +For the purposes of inheritance, `` contents are considered to be children of the slotting component, not the +slottable component. + +```html + +``` + +### Coherence + If a template is declared to be `lwc:render-mode="dual"`, then the corresponding component must also have `static renderMode = 'dual'`, and vice versa.