Owned styles
Own the look. Depend on the behavior. A primitive's stylesheet can be copied into the application and edited there; the accessibility-sensitive behavior stays a maintained package. This is the third styling mode, and today it is a manual copy.
On this page
The boundary
Copying source is attractive for what you want to change and a liability for what you do not. The boundary is drawn along that line: the look is yours to copy, the behavior engine is not.
| Owner | What | Why |
|---|---|---|
| Package | Event router, behavior implementations, keyboard algorithms, focus utilities, the build-time scanner, the table engine | A focus or keyboard bug fixed upstream reaches every project through an npm update, without reconciling copied runtime code. |
| You, once copied | Primitive stylesheets, visual recipes, theme definitions, composition wrappers and example components | Visual source is meant to be edited; a copy has no version lock, no unused CSS and no specificity surprises. |
The reason behavior stays package-managed is arithmetic: a thousand projects that copy the tabs keyboard code own a thousand stale implementations the day a focus bug is fixed upstream. For CSS the same copy is a feature. This is a deliberate difference from a pure source-copy library (ADR 0005).
Status
Warning The slean add command does not exist yet. There is no package to install and no registry;
every mention of it in the architecture documents describes a plan. What works today is the manual
copy below, and it is the path this page documents.
Copying a stylesheet
The package publishes plain files, so a copy is a copy. Take the primitive file you want to own
and keep importing tokens.css and base.css from the package, or copy those
too when the tokens themselves should be yours.
# Copy the stylesheet you want to own. tokens.css stays the shared contract the copy depends on;
# copy it as well when you want to own the tokens.
mkdir -p src/lib/slean
cp node_modules/@svelte-lean/styles/css/tabs.css src/lib/slean/tabs.cssimport '@svelte-lean/styles/tokens.css'; // the token contract the copied file reads
import '@svelte-lean/styles/base.css'; // layer order, scoped reset, shared focus ring
import '$lib/slean/tabs.css'; // yours: edit it, the package never touches it againFrom here on the file is application source. The package's future versions do not change it, and
the package's static checks no longer run on it. The tabs behavior keeps writing aria-selected, tabindex and hidden to the markup exactly as
before, because it never knew which stylesheet was reading them.
Editing the copy
Change values, add variants, remove what the application does not use. The copied file keeps the
layer statement and the :where() wrappers; keeping them means the file behaves like the
package file it came from, and removing them is allowed once it is yours.
/* src/lib/slean/tabs.css after an edit. The selectors on protocol attributes and ARIA state
* stay as they are: the behavior writes aria-selected and hidden, the stylesheet reads them. */
@layer slean.reset, slean.tokens, slean.base, slean.components, slean.compositions, slean.utilities;
@layer slean.components {
:where([data-slean='tabs'] [data-slean-part='trigger'][aria-selected='true']) {
border-color: var(--slean-fg); /* was var(--slean-accent) */
font-weight: var(--slean-font-weight-semibold);
}
}What to keep
- Selectors on protocol attributes and ARIA state (
[data-slean-part='trigger'],[aria-selected='true'],[hidden]): the behavior contract writes them. - The
:focus-visiblering frombase.css, or an equivalent of your own; a visible focus indicator is part of the accessibility contract, not of the theme. - The
forced-colorsrules and the coarse-pointer hit areas, unless you replace them with rules that serve the same users.
What is safe to change
- Every color, radius, spacing and duration, directly or through the tokens.
- Variant values and their looks, sizes, the indicator style, transitions.
- The layer names, when the application has a layer scheme of its own.
Updating a copy
A copied file does not update itself. After upgrading the package, compare the shipped file with yours and carry over what you want; there is no diff-first tool yet, so this is a manual step.
# No diff-first update exists yet. After upgrading the package, compare by hand.
diff node_modules/@svelte-lean/styles/css/tabs.css src/lib/slean/tabs.cssThe planned command
The blueprint describes a command that copies a stylesheet into the project, records where the copy came from, explains the runtime cost of the primitive it belongs to, and updates copies diff-first. It is listed here so the plan is visible, and marked as not available so nobody waits for it.
# Not available. The planned command copies the stylesheet, records its provenance and updates
# it diff-first; until it ships, the manual steps above are the documented path.
npx slean add tabsUntil it exists, the manual copy above is the whole of mode C. The behavior engine will stay package-managed when the command ships; the command changes how a stylesheet arrives, not what is owned.
Escape hatches
Every layer of this model has a lower one. Owned styles fall back to writing CSS from scratch against the protocol (mode 1); package tokens fall back to your own custom properties; the package stylesheet falls back to an unlayered override (Themes). None of them changes the behavior contract.
Source: packages/styles/css and the distribution decision in ADR 0005.