API reference

Look up component props, hook options and their defaults. Start with Lighting and Lighting.Surface; use the hooks when you need to connect an existing host.

Lighting

Wrap the part of your interface that should share a light. Lighting adds no DOM element. Each surface uses its nearest owner, so nested owners create independent lights.

Lighting signature
<Lighting motion="system" radius={480} intensity={0.75}>
{/* Your surfaces */}
</Lighting>
Lighting props
Prop / optionTypeDefaultDescription
motion'system' | 'dynamic' | 'static''system'System respects reduced-motion and device preferences. Dynamic follows mouse or pen input, even with reduced motion enabled. Static keeps a fixed direction. Touch does not drive the light.
staticDirectionnumber | 'random'315Direction in degrees for fixed lighting. Random chooses one shared direction per mount and keeps it across ordinary rerenders. Numbers must be finite.
radiusnumber480Distance over which dynamic lighting fades outside a surface, in viewport CSS pixels. Must be positive and finite.
intensitynumber0.75Shared light strength. Must be finite and between 0 and 1; invalid values throw a RangeError.
childrenReactNodeRequiredThe area that shares this light. Ordinary children stay unchanged; only explicit surfaces respond.
refRef<Light>UnsetOptional stable light handle. Call its refresh() method after position-only layout changes.

Pointer input comes from the document. The scope chooses which surfaces respond; it does not clip input to a wrapper rectangle. staticDirection applies whenever fixed lighting is active, including when motion="system" respects reduced motion.

Lighting.Surface

Mark an element that should respond to its nearest light. It renders one HTML host and preserves your native attributes and event handlers.

Lighting.Surface signature
<Lighting.Surface as="article" edgeStyle="raised">
Your content
</Lighting.Surface>
Lighting.Surface props
Prop / optionTypeDefaultDescription
pseudoElement'after' | 'before''after'Decoration slot. Choose before to preserve an existing ::after. If both slots are occupied, or the host is a native input, use a wrapper. Invalid values throw a RangeError.
edgeStyle'raised' | 'recessed' | 'matte''raised'How the edge responds to light. All three styles preserve your component appearance.
colorstringcurrentColorHighlight color. Follows the host text color by default. Accepts any CSS color string, including a CSS variable.
shadowColorstring#0008Color on the opposite edge. Accepts any CSS color string; use transparent to remove this shadow on a light surface.
borderWidthnumber1Decorative edge thickness in pixels; it does not change the host border. Negative values clamp to 0. Non-finite values use the default.
intensitynumber1Multiplier for the shared light strength. Finite values clamp to 0–1; non-finite values use the default.
asHTML tag name'div'The HTML element to render. Native attributes and the ref follow the chosen tag; custom React components are not accepted here.
childrenReactNodeUnsetContent rendered inside the host. Use a wrapper for inputs and other elements that cannot display the decoration themselves.
refRef<HTMLElement for the chosen tag>UnsetForwarded to the rendered element alongside the internal registration ref. React 19 callback-ref cleanup is preserved.
classNamestringUnsetYour classes, combined with the decoration classes.
styleCSSPropertiesUnsetYour inline styles, merged after the decoration bindings. Keep Edge-owned CSS variables intact unless deliberately overriding them.

Place every Lighting.Surface inside a Lighting owner; rendering it outside one throws an error. For native inputs and occupied pseudo-elements, see Integration.

Existing host borders are measured so the decoration aligns with their outside edge. borderWidth changes the decoration thickness, not the host border. For a custom component that already exposes its host, use useSurface below.

useLight(options?)

Create a light when you need to connect existing components through hooks. The returned Light handle stays stable; changing options updates the existing light.

useLight signature
function useLight(options?: LightOptions): Light;
useLight options
Prop / optionTypeDefaultDescription
motion'system' | 'dynamic' | 'static''system'System respects reduced-motion and device preferences. Dynamic follows mouse or pen input, even with reduced motion enabled. Static keeps a fixed direction. Touch does not drive the light.
staticDirectionnumber | 'random'315Direction in degrees for fixed lighting. Random chooses one shared direction per mount and keeps it across ordinary rerenders. Numbers must be finite.
radiusnumber480Distance over which dynamic lighting fades outside a surface, in viewport CSS pixels. Must be positive and finite.
intensitynumber0.75Shared light strength. Must be finite and between 0 and 1; invalid values throw a RangeError.

Omitted options use their defaults. radius controls distance attenuation for dynamic lighting. Fixed lighting uses staticDirection and does not fade because an element is far from an arbitrary origin.

light.refresh()

Call light.refresh() after your application moves an element or changes its host border thickness without a size notification. It remeasures geometry on the next frame, including while the pointer is stationary.

refresh signature
interface Light {
refresh(): void;
}

Call it after React commits the new layout. It schedules an update without replacing the light handle or requiring pointer movement.

useSurface(light, options?)

Connect one existing HTML host to a light. Attach the returned ref. Edge applies and removes its own decoration; keep your existing className and style props.

useSurface signature
const ref = useSurface<HTMLElement>(light, options);
Share one light with existing components
function Area() {
const light = useLight();
return <><ExistingCard light={light} /><ExistingCard light={light} /></>;
}
function ExistingCard({ light }: { light: Light }) {
const ref = useSurface<HTMLDivElement>(light);
return <Card ref={ref}>Your existing component</Card>;
}
useSurface options
Prop / optionTypeDefaultDescription
pseudoElement'after' | 'before''after'Decoration slot. Choose before to preserve an existing ::after. If both slots are occupied, or the host is a native input, use a wrapper. Invalid values throw a RangeError.
edgeStyle'raised' | 'recessed' | 'matte''raised'How the edge responds to light. All three styles preserve your component appearance.
colorstringcurrentColorHighlight color. Follows the host text color by default. Accepts any CSS color string, including a CSS variable.
shadowColorstring#0008Color on the opposite edge. Accepts any CSS color string; use transparent to remove this shadow on a light surface.
borderWidthnumber1Decorative edge thickness in pixels; it does not change the host border. Negative values clamp to 0. Non-finite values use the default.
intensitynumber1Multiplier for the shared light strength. Finite values clamp to 0–1; non-finite values use the default.

An element can belong to only one light controller. Connect the returned ref to the element that actually displays the border, not to a parent whose dimensions differ.

edgeStyle changes update the existing registration. Appearance options update without replacing the host. Hook decoration is attached on the client; its server markup contains only your own styles. Use Lighting.Surface when you need the decorative CSS in server output too.

Rendering and cleanup

Hooks can create handles and render markup on the server without accessing the DOM. Client effects activate the light after hydration. Cleanup restores controller-owned inline variables, removes browser subscriptions and cancels pending frames. Setup can restart during React StrictMode checks.

The React adapter requires React 19. Tests cover React’s server renderer and hydration APIs; they do not establish integration with every SSR framework.

bolerish/edge · React