Version 0.1.0 just released! Check out the release notes from GitHub Releases

Tune One Card's Spacing or Radius From the Call Site Without Adding a Variant or Forking the React Component

Per-Instance Overrides via CSS Variables in React

Sooner or later a designer asks for "the same card, but with a bigger radius — just on this one page." If you add a radius variant for the one-off, the API grows; if you reach for !important, the next override fight is even harder. Salty CSS's preferred answer is to expose the knob as a CSS custom property in the component and override it at the call site via the framework-native style prop.

The component declares the knob

// /components/themed-box.css.ts
import { styled } from "@salty-css/react/styled";

export const ThemedBox = styled("div", {
  base: {
    backgroundColor: "var(--box-bg, white)",
    color: "var(--box-text, black)",
    padding: "var(--box-padding, 1rem)",
    borderRadius: "var(--box-radius, 4px)",
  },
});

The component reads each knob through var(--name, fallback), so consumers can override one knob, all of them, or none — the fallback keeps untouched knobs working.

Override at the call site

The framework-native style (or style="…" in Astro) attribute on the element itself — or any ancestor — sets the variable. Inline declarations always win the cascade, so the override lands even against a higher-priority Salty layer rule.

import { ThemedBox } from "./themed-box.css";

export const ThemeExample = () => {
  return (
    <div
      style={
        {
          "--box-bg": "navy",
          "--box-text": "white",
          "--box-radius": "8px",
        } as React.CSSProperties
      }
    >
      <ThemedBox>This box uses the parent's custom properties</ThemedBox>
    </div>
  );
};

Why this beats the obvious alternatives

  • A new variant for every knob balloons the component's prop API and makes refactoring painful.
  • !important wins the round but creates a worse override fight the next time someone needs a one-off.
  • The style prop on the component itself also works (style=), but it only overrides on that element. Setting a CSS variable on a parent lets a whole subtree of components inherit the override.

Typed prop tokens — when you want the API to be discoverable

If consumers shouldn't have to remember the variable name, declare a prop token instead. Reference the value as {props.X} in your styles and Salty CSS exposes a typed css-X prop on the rendered component:

import { styled } from "@salty-css/react/styled";

export const Box = styled("div", {
  base: {
    padding: "1rem",
    color: "{props.color}",
    backgroundColor: "{props.bgColor}",
  },
});

// Usage: `<Box css-color="white" css-bg-color="#222" />`

Reach for prop tokens when the component owns the contract; stick with style= when the variable name is the contract (e.g. shared theming variables a parent wrapper sets for several descendants).

What it produces

The component's CSS uses var(--…) references against a --… declaration that comes from the consumer's inline style. There is no extra Salty CSS rule emitted per call site — the inline declaration is the override.

See also