first commit
This commit is contained in:
@@ -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.
|
||||
Reference in New Issue
Block a user