Per-Instance Overrides for .astro Components Through Inline Custom Properties Without Extra Variants
Per-Instance Overrides via CSS Variables in Astro
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/astro/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.
--- // src/pages/index.astro import { ThemedBox } from "../components/themed-box.css"; --- <div style="--box-bg: navy; --box-text: white; --box-radius: 8px;"> <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.
!importantwins the round but creates a worse override fight the next time someone needs a one-off.- The
styleprop 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/astro/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
- Overrides → CSS Custom Properties — the broader pattern.
- Overrides → Typed prop tokens — when to upgrade to a typed
css-*prop.