-
Notifications
You must be signed in to change notification settings - Fork 2.2k
chore: output scoped-elements build folder for base class elements with scoped subcomponents
#22145
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
annawen1
wants to merge
9
commits into
carbon-design-system:main
from
annawen1:chore/scoped-elements-build
Closed
Changes from all commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
0b1d19d
chore: create scoped-elements build
annawen1 78c26d2
chore: scope sub components
annawen1 8dc6621
chore: minimize changes
annawen1 b677936
chore: plugin function descriptions
annawen1 609127d
Merge branch 'main' into chore/scoped-elements-build
annawen1 68eb77b
chore: add documentation
annawen1 53e5bb5
Merge branch 'main' into chore/scoped-elements-build
annawen1 7114961
chore: add example for stackblitz
annawen1 675853e
chore: fix license date and title for example
annawen1 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,6 +4,7 @@ packages/*/build/ | |
| packages/*/examples/*/build/ | ||
| es | ||
| es-custom | ||
| scoped-elements | ||
| lib | ||
| dist | ||
| umd | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,169 @@ | ||
| # Scoped custom elements | ||
|
|
||
| ## Overview | ||
|
|
||
| The `scoped-elements` build output provides a version of Carbon web components | ||
| that use | ||
| [scoped custom element registries](https://github.com/WICG/webcomponents/blob/gh-pages/proposals/Scoped-Custom-Element-Registries.md) | ||
| instead of the global custom element registry. This allows multiple versions of | ||
| Carbon web components to coexist on the same page without naming conflicts. | ||
|
|
||
| ## When to use scoped elements | ||
|
|
||
| Use the `scoped-elements` build output when: | ||
|
|
||
| - **Multiple versions**: You need to use different versions of Carbon web | ||
| components on the same page (e.g., during a gradual migration) | ||
| - **Micro-frontends**: Your application uses a micro-frontend architecture where | ||
| different teams may use different versions of Carbon | ||
| - **Third-party integration**: You're integrating with third-party code that may | ||
| also use Carbon web components | ||
| - **Avoiding conflicts**: You want to prevent custom element naming conflicts in | ||
| complex applications | ||
|
|
||
| ## How it works | ||
|
|
||
| ### Standard build (`es/`) | ||
|
|
||
| The standard build uses the `@customElement` decorator to register components | ||
| globally: | ||
|
|
||
| ```typescript | ||
| @customElement('cds-button') | ||
| class CDSButton extends LitElement { | ||
| // Component implementation | ||
| } | ||
| ``` | ||
|
|
||
| This registers the component in the global `customElements` registry, which | ||
| means only one version of `cds-button` can exist on a page. | ||
|
|
||
| ### Scoped elements build (`scoped-elements/`) | ||
|
|
||
| The `scoped-elements` build transforms components to: | ||
|
|
||
| 1. **Remove global registration**: The `@customElement` decorator is stripped | ||
| out, so components are not automatically registered globally | ||
| 2. **Add scoped registries**: Each component that uses child components gets its | ||
| own private custom element registry | ||
| 3. **Isolate dependencies**: Child components are registered only within their | ||
| parent's shadow DOM scope | ||
|
|
||
| This transformation is performed by the `scopedElementsDecoratorStripPlugin` | ||
| during the build process. | ||
|
|
||
| ## Installation | ||
|
|
||
| The `scoped-elements` build is included in the `@carbon/web-components` package: | ||
|
|
||
| ```bash | ||
| npm install @carbon/web-components | ||
| ``` | ||
|
|
||
| ## Usage | ||
|
|
||
| ### Basic usage | ||
|
|
||
| Import components from the `scoped-elements` directory instead of `es`: | ||
|
|
||
| ```javascript | ||
| // Instead of: | ||
| // import '@carbon/web-components/es/components/button/button.js'; | ||
|
|
||
| // Use: | ||
| import CDSButton from '@carbon/web-components/scoped-elements/components/button/button.js'; | ||
| ``` | ||
|
|
||
| ### Scoped subcomponents | ||
|
|
||
| For components with child dependencies, the scoped build automatically creates | ||
| private registries: | ||
|
|
||
| ```javascript | ||
| import CDSDropdown from '@carbon/web-components/scoped-elements/components/dropdown/dropdown.js'; | ||
|
|
||
| // The dropdown component will automatically register its child components | ||
| // (like dropdown-item) in its own scoped registry | ||
| customElements.define('cds-dropdown', CDSDropdown); | ||
| ``` | ||
|
|
||
| ## Complete example with the accordion component | ||
|
|
||
| ```html | ||
| <div data-scoped-accordion-root> | ||
| <cds-accordion-skeleton count="3" open></cds-accordion-skeleton> | ||
| <cds-accordion> | ||
| <cds-accordion-item title="Section 1"> | ||
| <p>Rendered from a scoped registry.</p> | ||
| </cds-accordion-item> | ||
| <cds-accordion-item title="Section 2"> | ||
| <p>Not defined in the global registry.</p> | ||
| </cds-accordion-item> | ||
| </cds-accordion> | ||
| </div> | ||
| ``` | ||
|
|
||
| ```js | ||
| import CDSAccordion from '@carbon/web-components/scoped-elements/components/accordion/accordion.js'; | ||
| import CDSAccordionItem from '@carbon/web-components/scoped-elements/components/accordion/accordion-item.js'; | ||
| import CDSAccordionSkeleton from '@carbon/web-components/scoped-elements/components/accordion/accordion-skeleton.js'; | ||
|
|
||
| /** | ||
| * | ||
| * When using the scoped-elements build, components are not automatically | ||
| * registered globally. This array contains the accordion component family | ||
| * (accordion, accordion-item, and accordion-skeleton) with their tag names | ||
| * and class constructors for manual registration. | ||
| * | ||
| */ | ||
| const elements = [ | ||
| ['cds-accordion', CDSAccordion], | ||
| ['cds-accordion-item', CDSAccordionItem], | ||
| ['cds-accordion-skeleton', CDSAccordionSkeleton], | ||
| ]; | ||
|
|
||
| const defineElements = (registry) => { | ||
| elements.forEach(([tagName, elementClass]) => { | ||
| if (!registry.get(tagName)) { | ||
| registry.define(tagName, elementClass); | ||
| } | ||
| }); | ||
| }; | ||
|
|
||
| const root = document.querySelector('[data-scoped-accordion-root]'); | ||
|
|
||
| /** | ||
| * | ||
| * Wraps the root element in a shadow DOM with a scoped custom element registry. | ||
| * | ||
| */ | ||
| if (root) { | ||
| const host = document.createElement('div'); | ||
| const markup = root.outerHTML; | ||
| const fallbackRoot = root; | ||
|
|
||
| root.replaceWith(host); | ||
|
|
||
| try { | ||
| const registry = new CustomElementRegistry(); | ||
| defineElements(registry); | ||
|
|
||
| // Attach shadow DOM with the scoped registry | ||
| // Components inside this shadow root will use the scoped registry | ||
| // instead of the global customElements registry. | ||
| const shadowRoot = host.attachShadow({ | ||
| mode: 'open', | ||
| customElementRegistry: registry, | ||
| }); | ||
|
|
||
| shadowRoot.innerHTML = markup; | ||
|
|
||
| if (typeof registry.upgrade === 'function') { | ||
| registry.upgrade(shadowRoot); | ||
| } | ||
| } catch (error) { | ||
| host.replaceWith(fallbackRoot); | ||
| throw error; | ||
| } | ||
| } | ||
| ``` |
22 changes: 22 additions & 0 deletions
22
packages/web-components/examples/scoped-custom-registry/.gitignore
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| # See https://help.github.com/ignore-files/ for more about ignoring files. | ||
|
|
||
| # dependencies | ||
| /node_modules | ||
|
|
||
| # testing | ||
| /coverage | ||
|
|
||
| # production | ||
| /build | ||
|
|
||
| # misc | ||
| .DS_Store | ||
| .cache | ||
| .env.local | ||
| .env.development.local | ||
| .env.test.local | ||
| .env.production.local | ||
|
|
||
| npm-debug.log* | ||
| yarn-debug.log* | ||
| yarn-error.log* |
6 changes: 6 additions & 0 deletions
6
packages/web-components/examples/scoped-custom-registry/.sassrc
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| { | ||
| "includePaths": [ | ||
| "node_modules", | ||
| "../../node_modules" | ||
| ] | ||
| } |
53 changes: 53 additions & 0 deletions
53
packages/web-components/examples/scoped-custom-registry/index.html
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,53 @@ | ||
| <!-- | ||
| Copyright IBM Corp. 2026 | ||
|
|
||
| This source code is licensed under the Apache-2.0 license found in the | ||
| LICENSE file in the root directory of this source tree. | ||
| --> | ||
|
|
||
| <html> | ||
| <head> | ||
| <title>carbon-web-components scoped custom registry example</title> | ||
| <meta charset="UTF-8" /> | ||
| <link rel="stylesheet" | ||
| href="https://1.www.s81c.com/common/carbon-for-ibm-dotcom/tag/v1/latest/plex.css"/> | ||
|
|
||
| <link rel="stylesheet" href="src/styles.scss" /> | ||
| <style> | ||
| /* Suppress custom element until styles are loaded */ | ||
| cds-accordion:not(:defined) { | ||
| display: none; | ||
| } | ||
|
|
||
| body { | ||
| font-family: 'IBM Plex Sans', Arial, sans-serif; | ||
| margin: 2rem; | ||
| } | ||
|
|
||
| section + section { | ||
| margin-top: 2rem; | ||
| } | ||
| </style> | ||
| <script type="module" src="src/index.js"></script> | ||
| </head> | ||
| <body> | ||
| <section> | ||
| <div data-scoped-accordion-root> | ||
| <cds-accordion-skeleton count="3" open></cds-accordion-skeleton> | ||
| <cds-accordion> | ||
| <cds-accordion-item title="Scoped section 1 title"> | ||
| <p> | ||
| This accordion is created with a scoped CustomElementRegistry from | ||
| src/index.js. | ||
| </p> | ||
| </cds-accordion-item> | ||
| <cds-accordion-item title="Scoped section 2 title"> | ||
| <p> | ||
| It does not rely on the global customElements registry. | ||
| </p> | ||
| </cds-accordion-item> | ||
| </cds-accordion> | ||
| </div> | ||
| </section> | ||
| </body> | ||
| </html> |
22 changes: 22 additions & 0 deletions
22
packages/web-components/examples/scoped-custom-registry/package.json
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| { | ||
| "name": "carbon-web-components-scoped-registry-example", | ||
| "version": "0.1.0", | ||
| "private": true, | ||
| "description": "Sample project for getting started with the Web Components from Carbon using a scoped registry.", | ||
| "license": "Apache-2", | ||
| "main": "index.html", | ||
| "scripts": { | ||
| "build": "vite build", | ||
| "clean": "rimraf node_modules dist .cache", | ||
| "dev": "vite" | ||
| }, | ||
| "dependencies": { | ||
| "@carbon/styles": "^1.34.0", | ||
| "@carbon/web-components": "latest", | ||
| "sass": "^1.64.1" | ||
| }, | ||
| "devDependencies": { | ||
| "vite": "^7.1.2", | ||
| "rimraf": "^6.0.1" | ||
| } | ||
| } |
3 changes: 3 additions & 0 deletions
3
packages/web-components/examples/scoped-custom-registry/sandbox.config.json
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| { | ||
| "template": "node" | ||
| } |
75 changes: 75 additions & 0 deletions
75
packages/web-components/examples/scoped-custom-registry/src/index.js
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,75 @@ | ||
| /** | ||
| * Copyright IBM Corp. 2026 | ||
| * | ||
| * This source code is licensed under the Apache-2.0 license found in the | ||
| * LICENSE file in the root directory of this source tree. | ||
| */ | ||
| import CDSAccordion from '@carbon/web-components/scoped-elements/components/accordion/accordion.js'; | ||
| import CDSAccordionItem from '@carbon/web-components/scoped-elements/components/accordion/accordion-item.js'; | ||
| import CDSAccordionSkeleton from '@carbon/web-components/scoped-elements/components/accordion/accordion-skeleton.js'; | ||
|
|
||
| const accordionElements = [ | ||
| ['cds-accordion', CDSAccordion], | ||
| ['cds-accordion-item', CDSAccordionItem], | ||
| ['cds-accordion-skeleton', CDSAccordionSkeleton], | ||
| ]; | ||
|
|
||
| const defineAccordionElements = (registry) => { | ||
| accordionElements.forEach(([tagName, elementClass]) => { | ||
| if (!registry.get(tagName)) { | ||
| registry.define(tagName, elementClass); | ||
| } | ||
| }); | ||
| }; | ||
|
|
||
| const createScopedRegistry = () => { | ||
| if (typeof CustomElementRegistry === 'undefined') { | ||
| return undefined; | ||
| } | ||
|
|
||
| try { | ||
| return new CustomElementRegistry(); | ||
| } catch { | ||
| return undefined; | ||
| } | ||
| }; | ||
|
|
||
| const scopedAccordionRoot = document.querySelector('[data-scoped-accordion-root]'); | ||
|
|
||
| if (scopedAccordionRoot) { | ||
| const host = document.createElement('div'); | ||
| const markup = scopedAccordionRoot.outerHTML; | ||
| const fallbackRoot = scopedAccordionRoot; | ||
|
|
||
| scopedAccordionRoot.replaceWith(host); | ||
|
|
||
| try { | ||
| const scopedRegistry = createScopedRegistry(); | ||
| if (!scopedRegistry) { | ||
| host.replaceWith(fallbackRoot); | ||
| } else { | ||
| defineAccordionElements(scopedRegistry); | ||
|
|
||
| const shadowRoot = host.attachShadow({ | ||
| mode: 'open', | ||
| customElementRegistry: scopedRegistry, | ||
| }); | ||
|
|
||
| shadowRoot.innerHTML = markup; | ||
| if (typeof scopedRegistry.upgrade === 'function') { | ||
| scopedRegistry.upgrade(shadowRoot); | ||
| } | ||
|
|
||
| if (!shadowRoot.querySelector('cds-accordion')) { | ||
| throw new Error( | ||
| 'Accordion markup was not rendered in scoped shadow root.' | ||
| ); | ||
| } | ||
| } | ||
| } catch (error) { | ||
| host.replaceWith(fallbackRoot); | ||
| const message = | ||
| error instanceof Error ? error.message : 'Unknown scoped registry error.'; | ||
| throw new Error(`Scoped registry initialization failed: ${message}`); | ||
| } | ||
| } | ||
9 changes: 9 additions & 0 deletions
9
packages/web-components/examples/scoped-custom-registry/src/styles.scss
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| @use '@carbon/styles/scss/reset'; | ||
| @use '@carbon/styles/scss/theme'; | ||
| @use '@carbon/styles/scss/themes'; | ||
|
|
||
| :root { | ||
| @include theme.theme(themes.$white); | ||
| background-color: var(--cds-background); | ||
| color: var(--cds-text-primary); | ||
| } |
12 changes: 12 additions & 0 deletions
12
packages/web-components/examples/scoped-custom-registry/vite.config.js
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| import { resolve } from 'path'; | ||
| import { defineConfig } from 'vite'; | ||
|
|
||
| export default defineConfig({ | ||
| build: { | ||
| rollupOptions: { | ||
| input: { | ||
| main: resolve(__dirname, 'index.html'), | ||
| }, | ||
| }, | ||
| }, | ||
| }); |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This fallback restores the
cds-accordion*code but doesn't keep a scoped registry attached or register those tags withcustomElements.define(). Will browsers withoutCustomElementRegistrysupport (support appears to be incomplete: https://developer.mozilla.org/en-US/docs/Web/API/CustomElementRegistry#browser_compatibility) leave the accordion elements unupgraded here?Unless I'm misunderstanding, MDN says custom elements have to be registered to be available: https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements#registering_a_custom_element