# "The Design Layer" _Path: en/frontend/design-layer_ > "How to place frontend styles and components in the theme, a shared design package, or an individual module." ## Table of Contents - The Design Layer ## Content # The Design Layer This page is a design-ownership decision guide. Its CSS and component snippets are partial patterns that assume an existing Wippy frontend package and build. A Wippy frontend can contain many independently published modules in one application. The **theme** reaches every surface, while each **module** owns its local presentation. A **shared design layer** covers the narrower case where several modules share a concept that the theme does not provide. ## The layers | Layer | Reaches | Owns | |---|---|---| | **Theme** | *Every* surface, including modules you do not own | PrimeVue components, the shared semantic tokens, documented classes | | **Shared design layer** | Only the modules that opt in | Vocabulary those modules share that has no themed component behind it | | **Module** | Itself | What is genuinely specific to one surface | ### The theme is universal, and that is the constraint The theme styles markup **you do not own**. Any module — including a third-party plugin written by someone who has never seen your app — renders into the same host and is painted by the same theme. That is what makes the theme the universal layer, and it cuts both ways: **Nothing app-specific may go into the theme**, because it would be imposed on every module that never asked for it. **A module may not depend on anything app-specific being in the theme.** The contract is *PrimeVue components + the shared Wippy semantic tokens + documented classes* — nothing an application added on top. Note that PrimeVue's own presets are not the contract either: Wippy runs PrimeVue with `theme: 'none'`, so it is the Wippy semantic tokens you rely on. ```css /* GOOD — shared Wippy semantic tokens, present for every module */ .my-panel { color: var(--p-text-color); background: var(--p-content-background); border: 1px solid var(--p-content-border-color); } /* BAD — an application-specific token. Your module now only works inside one app, and silently loses the declaration anywhere else: an undefined custom property makes the declaration invalid at computed-value time, so it drops and the element quietly inherits instead. */ .my-panel { background: var(--kx-surface-2); } ``` This is also the answer to *"can I put our shared vocabulary in the facade?"* Only if it must genuinely reach arbitrary, unowned markup. If it is scoped to *your* set of modules, it does not belong in the theme — it belongs in the layer below. ### The backbone, and when a component may opt out PrimeVue and Tailwind, as shipped by the host, are the recommended backbone for any component. A component **may** opt out — but the opt-out narrows the moment it renders anything conventional, and the ladder only goes one way: | The component… | Then it must load | |---|---| | is presentation-neutral — canvas, SVG, a chart with no controls, no tokens, no utilities, no scrolling | nothing: `hostCssKeys: []` | | consumes semantic tokens or dark mode | `themeConfigUrl` | | can scroll | `iframeCssUrl` | | renders markdown | `markdownCssUrl` | | chooses Tailwind utilities for routine layout or spacing | `primeVueCssUrl` (the Host bundles Tailwind with this asset) | | renders anything **PrimeVue** ships a component for — button, input, form, table, dialog, menu, tag, tooltip, any feedback control | `primeVueCssUrl` **and** `PrimeVuePlugin` | A chart on a canvas is the archetypal legitimate opt-out: it has no classic UI, so it needs none of the backbone. Give that same chart a toolbar and it is no longer presentation-neutral — the button is a PrimeVue button, and the whole integration comes with it. Note the coupling: **Tailwind utilities are delivered with `primeVueCssUrl`.** There is no separate Tailwind host CSS key, so in practice a component that chooses Tailwind is loading the PrimeVue asset too. Prefer utilities for ordinary layout and spacing when they keep the component clear, but portable module-owned CSS remains valid when a utility is not the best expression of the design. (`preflightCssUrl` is not part of the key union; if Tailwind preflight is genuinely required inside the shadow root, load it imperatively — rarely needed.) The practical consequence for this page: **most of what a module wants already exists in the backbone.** The shared design layer is a narrow band above it, not a place to re-do what PrimeVue and Tailwind already cover. See [CSS Injection](./web-host/css-injection.md) for the mechanics. ### The shared design layer Some ideas recur across a known set of modules and have no application-level contract in the theme: a domain-specific match summary, a surface header row, an empty state, or a project-specific tag-sizing vocabulary. These concepts belong in the shared design layer. They ship as a **published package**, materialized into each consumer at build time. It must be a package rather than a path alias because consumers live in different repositories. A module in another repository, with no path access to the producer, must be able to consume the vocabulary and build. The producing module declares the package as a **build-time artifact** and each consumer materializes it into its own tree. See [Build-time Artifacts](../guides/artifacts.md) for the declaration, the `node-package` format, what the runtime reconciles for you, and the glue a build still has to supply itself. ### The module Everything else, plus every deliberate divergence from the shared vocabulary. ## Deciding where something belongs Ask in order. First yes wins. 1. **Is it a value?** Colour, radius, spacing, elevation, severity. → **Theme.** Read a semantic token. Never a literal. 2. **Does the theme already ship a component for this?** Button, Dialog, Select, Tag. → **Theme.** Use the component. Style it by putting a class *on* it — never rebuild it. 3. **Do two or more of your modules need this same concept, with no themed component behind it?** → **Shared design layer.** 4. Otherwise → **Module.** ## Worked examples The examples use the `kx-` prefix for application-specific classes and stylesheet names. The placement rules apply to any Wippy application. ### Never rebuild a themed component PrimeVue ships `Button`. Replacing it with `.kx-btn` on a native `