DOM Type โ Light vs Shadow โ
Every mono-* component ships in two flavors: a light-DOM build and a shadow-DOM build. Most of the time you don't think about it โ you just import a component and use it. This page explains the difference in plain terms and which one to pick.
What is light vs shadow DOM? โ
Think of a component as a small box of HTML + CSS that you drop onto your page.
- Light DOM โ the box is open. Its markup lives directly in the normal page, and it's styled by the page's global stylesheet (mono-helper's CSS). Your page can see and reach into it. Simple and shared.
- Shadow DOM โ the box is sealed. The component keeps its own markup and styles inside a private bubble (a shadow root). Outside CSS can't leak in and its internal styles can't leak out โ it's self-contained and isolated.
Both render the exact same component; they only differ in where the markup/styles live and how isolated they are.
The two builds mono ships โ
| Light (default) | Shadow | |
|---|---|---|
| Import | import 'mono-helper/ui/button' | import 'mono-helper/ui/shadow/button' |
| Tag you write | <mono-button> | <mono-shadow-button> |
| Styles come from | the global mono-helper CSS | the component's own scoped styles |
| Built for | client-side apps (SPA) | server-side rendering (@lit-labs/ssr) |
Theming works on both
CSS custom properties inherit through the shadow boundary, so your --mono-* overrides (see Theme and each component's CSS Variables section) apply to the light and shadow builds the same way.
Which should I use? โ
The right flavor depends on whether your app renders on the server.
Vue app (host or remote) โ use light (the default). A Vue SPA renders in the browser, so there's no server paint to optimize โ light DOM is the simplest, smallest path. Just
import 'mono-helper/ui/<name>'and use<mono-<name>>.Nuxt host (SSR) โ use shadow. Because Nuxt renders on the server, the shadow build lets the server send the fully-styled component in the first HTML response (as Declarative Shadow DOM). The result: no skeleton delay, no unstyled flash, and no reflow when the page hydrates โ a smoother initial load and reload.
In a Nuxt host you don't hand-write
<mono-shadow-*>or wrap anything: justimport 'mono-helper/ui/shadow/<name>'and themono-helper/nuxtmodule auto-wraps it for SSR โ you keep using the component normally.
Rule of thumb: Vue โ light ยท Nuxt host โ shadow. A Vue remote stays light even under a Nuxt host โ it's still a client-rendered island; the shadow build is for the Nuxt host's own server-rendered shell and pages.
See it in the docs โ
Every component's live demo on this site has a Shadow tab next to Vue and CSS. Toggle it to see the same component rendered through its shadow build โ it looks identical, because theming (--mono-* variables) and ::part cross the shadow boundary by design.
Quick comparison โ
| Light | Shadow | |
|---|---|---|
| Import | mono-helper/ui/<name> | mono-helper/ui/shadow/<name> |
| Tag | <mono-<name>> | <mono-shadow-<name>> |
| Style source | global mono-helper CSS | encapsulated per-component styles |
| First paint (SSR) | needs client hydration first | painted on the server, no flash |
| Best for | Vue apps (SPA) | Nuxt host (SSR) |
--mono-* theming | โ | โ (vars pierce the boundary) |
New here? Start with Getting Started and Theme. If you're the AI maintaining a mono template, this choice is codified as Rule 11 ("Component flavor") in the Template Rules.