first commit

This commit is contained in:
2026-05-29 08:20:56 +01:00
commit 949299f58d
6368 changed files with 527176 additions and 0 deletions

View File

@@ -0,0 +1,88 @@
# `SurfaceSkinProvider`
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
`<SurfaceSkinProvider>` is the single React boundary that activates a skin for
every atomic control inside its subtree. It writes the resolved palette of
CSS custom properties (`--cs-*`) onto a wrapping `<div>`'s inline `style`
attribute; every atomic control reads its colours through `var(--cs-…)`
declarations, so changing skin is purely a CSS-variable swap with no
React-side dispatch.
This is the only path through which a skin influences rendering. The schema
spec is explicit on this point: there is no JS-level skin registry, no
`React.useContext` consumed by controls, and no themed component variants.
Keeping skin enforcement on the CSS side enables a single physical surface
to host two side-by-side skinned subtrees without forking the React tree.
## Features
- Closed `SurfaceSkinVarName` enumeration of canonical variables.
- Frozen `DEFAULT_SURFACE_SKIN_VARS` palette.
- Pure `buildSkinStyle(vars)` helper for unit testing.
- Caller-supplied `className` appended to the default `rcs-skin` class.
- `data-skin-id` attribute on the wrapper for integration-test selectors.
## Usage: Basic
```tsx
import { SurfaceSkinProvider } from "@suaveplan/react-control-surface/skin";
export function App(): React.ReactNode {
return (
<SurfaceSkinProvider id="dark">
<Surface />
</SurfaceSkinProvider>
);
}
```
## Usage: Advanced
```tsx
import { SurfaceSkinProvider } from "@suaveplan/react-control-surface/skin";
const orangeKnobOverride = {
"--cs-knob-track-color": "#ff8800",
"--cs-knob-indicator-color": "#ffe0b2",
} as const;
export function NkiSurface(): React.ReactNode {
return (
<SurfaceSkinProvider
id="native-instruments"
vars={orangeKnobOverride}
className="device-frame"
>
<Surface />
</SurfaceSkinProvider>
);
}
```
## API Reference
- `SurfaceSkinProvider(props): React.ReactNode`
- `id: string` — written to `data-skin-id` on the wrapper.
- `vars?: SurfaceSkinVars` — partial override map merged on top of the
defaults; entries whose value is `undefined` keep the default.
- `className?: string` — appended to the `"rcs-skin"` base class.
- `children?: React.ReactNode`
- `buildSkinStyle(vars?: SurfaceSkinVars): CSSProperties` — pure merge helper.
- `DEFAULT_SURFACE_SKIN_VARS`, `SURFACE_SKIN_VAR_NAMES` — re-exported defaults
and the canonical CSS variable name list.
## Implementation Notes
The provider intentionally renders a `<div>` rather than a fragment because
CSS variables only cascade through real DOM ancestors. Inline styles are
used (instead of injecting a stylesheet) so server-side rendering remains
deterministic and consumers see exactly the colours they configured without
load-order pitfalls. Overrides are processed as a string-keyed loop so
arbitrary `--cs-foo` properties — useful for one-off device-specific themes
— pass straight through to the DOM without requiring an addition to the
closed `SurfaceSkinVarName` union.