# Salty CSS for React > Sprinkle Salty CSS on your React app — build-time CSS-in-TS that ships zero runtime. This file contains the Salty CSS documentation for the React framework, extracted for LLM ingestion. Includes framework-specific docs, shared concepts, and React-compatible recipes. Source: https://salty-css.dev/llms-full-react.txt — Generated: 2026-06-16 Full file (all frameworks): https://salty-css.dev/llms-full.txt Companion index: https://salty-css.dev/llms.txt --- # Salty CSS for React Source: https://salty-css.dev/react/ > Sprinkle Salty CSS on your React app — build-time CSS-in-TS that ships zero runtime. Build-time CSS-in-TS that ships zero runtime — and feels right at home next to your components. ### Get started ```bash npx salty-css init ``` Then read the [Quick Start](/docs/react/quick-start/) or jump straight to the [Installation guide](/docs/react/installation/). ### What you get **The styled API you remember** — Components over class names, with variants, compound variants, and typed prop tokens — the Stitches DX, served zero-runtime. **Zero-runtime extraction** — esbuild executes your `.css.ts` files at build time. Only a tiny class mapper ships to the client — the styling engine never does. **Cascade-layer scoping** — Hash-based isolation plus auto-bumped layers when you extend — predictable overrides, no specificity wars. ### A minimal component ```ts // components/button.css.ts import { styled } from "@salty-css/react/styled"; export const Button = styled("button", { base: { padding: "0.75rem 1.25rem", borderRadius: "6px", background: "{theme.color}", color: "white", }, variants: { tone: { primary: { background: "{theme.color}" }, ghost: { background: "transparent", border: "1px solid currentColor" }, }, }, }); ``` That's the whole runtime. Really. ### Where to go next - [Quick Start](/docs/react/quick-start/) — Zero to a styled component in five minutes. - [Installation](/docs/react/installation/) — Wire up the Vite or Webpack plugin. - [Component styles](/docs/react/basics/) — The full styled API and how it composes. - [Variants](/docs/react/variants/) — Prop-driven styles and compound variants. - [Templates](/docs/react/templates/) — Reusable style recipes with defineTemplates. --- # Documentation — React Source: https://salty-css.dev/docs/react/ Salty CSS is a build-time CSS-in-**TS** library for React, Next.js and Astro. You author styles in `.css.ts` files, the compiler turns them into real CSS, and your runtime ships with no styling engine attached. Meow. It's a good fit when you want **typed styles, real design tokens, and theming that doesn't need a React provider** — without giving up on the developer experience of writing CSS next to your component. ### What you get - **Typed styles** — `styled("button", { ... })` returns a typed component; variants become typed props. - **Design tokens** — `defineVariables` for static, **responsive**, and **conditional** tokens (the conditional ones are how theming works). - **Theming without a provider** — flip a `data-theme` attribute on `` and every consumer of `{theme.color}` updates. - **Templates** — `defineTemplates` for reusable style bundles with their own variants. - **Media queries that read like English** — `media.minWidth(720).and.dark`. - **Fluid sizing** — `defineViewportClamp` for `clamp()`-based values that scale with the viewport. - **Type-safe class names** — `className({ ... })` when you want the styles without the component wrapper. - **A CLI** — `npx salty-css init` / `generate` / `build` / `up` for the boring bits. ### TL;DR — install it Inside any React, Next.js or Astro project: ```bash npx salty-css init ``` Then create a component in a `*.css.ts` file: ```ts // /components/my-button.css.ts import { styled } from "@salty-css/react/styled"; export const Button = styled("button", { base: { padding: "0.75rem 1.25rem", borderRadius: "6px", background: "{theme.color}", color: "{theme.background}", }, }); ``` …and use it like any other . ### Where to go next - **New here?** → [Quick Start](/docs/quick-start/) walks you from `init` to a themed component with variants in about 15 minutes. - **Adding it to a project?** → [Installation](/docs/installation/) covers the per-framework wiring. - **Need the reference?** → [`styled`](/docs/api/styled/), [`className`](/docs/api/classname/), [`defineConfig`](/docs/api/config/). ### Search the docs Hit `Ctrl + K` (or `⌘ + K` on macOS) anywhere in the docs to open the search modal — or click **Search** at the top of the sidebar. Matching is fuzzy: partial terms like `variant`, `media` or `clamp` are usually enough. ### What's in the docs #### Getting Started - [Quick Start](/docs/quick-start/) — install, your first component, variants, theming, fonts. - [Installation](/docs/installation/) — per-framework setup (Next.js, Vite, Webpack, Astro). - [Usage](/docs/usage/) — file suffixes, dev-time naming, DevTools. - [Troubleshooting](/docs/troubleshooting/) — the fix list for the most common issues. - [CLI](/docs/cli/) — use Salty CSS from the command line. - [FAQ](/docs/faq/) — short answers to the things people ask first. #### Styling - [Component styles](/docs/basics/) — `styled`, `className`, and where each one fits. - [Variables](/docs/variables/) — design tokens with `defineVariables` (static, responsive, conditional). - [Theming](/docs/theming/) — dark mode and multi-theme without a provider. - [Fonts](/docs/fonts/) — `defineFont` for local files and remote stylesheets. - [Imports](/docs/imports/) — pull in external CSS with `defineImport`. - [Class styles](/docs/classnames/) — `className` for plain class-based styles. - [Variants](/docs/variants/) — prop-driven variants, compound, and `anyOfVariants`. - [Overrides](/docs/overrides/) — extend components, swap elements, forward props with `passProps`. - [Media queries](/docs/media-queries/) — media queries, container queries, breakpoints. - [Animations](/docs/animations/) — keyframes, stagger, pause/resume. - [Templates](/docs/templates/) — reusable styles with `defineTemplates`. #### Utilities - [Viewport clamp](/docs/viewport-clamp/) — fluid responsive sizing. - [Color function](/docs/color-function/) — manipulate colors at build time. - [Modifiers](/docs/modifiers/) — custom value transformers. #### API reference - [`styled` function](/docs/api/styled/) — full API for the `styled` component factory. - [`className` function](/docs/api/classname/) — full API for class-based styles. - [`defineConfig`](/docs/api/config/) — every option that lives in `salty.config.ts`. - [`define*` factories index](/docs/api/define-factories/) — one-page index of every factory. ### Get support Join the [Salty CSS Discord server](https://discord.gg/R6kr4KxMhP) for help, or check the [GitHub repository](https://github.com/margarita-form/salty-css) for source and issues. --- # Getting Started — React Source: https://salty-css.dev/docs/react/getting-started/ Everything you need to get a Salty CSS project off the ground. Start with the **Quick Start** for a fifteen-minute end-to-end walkthrough, or jump into **Installation** if you'd rather wire the build plugin into an existing app. The remaining pages cover day-to-day **Usage**, the **CLI**, **ESLint** support, common **Troubleshooting**, and **FAQ** answers. --- # Quick Start — React Source: https://salty-css.dev/docs/react/quick-start/ Goal: by the end of this page you have Salty CSS installed, a typed component on screen, prop-driven variants, dark mode that flips without a provider, and a custom font registered. Budget about 15 minutes. ### 1. Install Inside any React, Next.js, Vite, or Astro project, run: ```bash npx salty-css init ``` The CLI detects your framework, adds the right plugin (`@salty-css/next`, `@salty-css/vite`, `@salty-css/webpack`, or `@salty-css/astro`), drops a `salty.config.ts` in the project root, and wires up the build. If you'd rather wire it up yourself, see [Installation](/docs/installation/). ### 2. Your first component All Salty definitions live in files matching `*.css.ts`, `*.css.tsx`, `*.salty.ts`, `*.styled.ts`, or `*.styles.ts` — the suffix is how the compiler picks them up at build time. ```ts // /components/card.css.ts import { styled } from "@salty-css/react/styled"; export const Card = styled("section", { base: { padding: "1.5rem", borderRadius: "12px", background: "{theme.background}", color: "{theme.color}", boxShadow: "0 1px 2px rgba(0, 0, 0, 0.06)", }, }); ``` Use it like any other : ```tsx import { Component } from "./my-component.css"; const MyPage = () => { return ( This is a Salty CSS component ); }; export default MyPage; ``` Note the `{theme.background}` / `{theme.color}` tokens — we'll define those in step 4. ### 3. Add variants Variants turn props into typed style branches. Add a `size` axis and a `tone` axis: ```ts // /components/card.css.ts import { styled } from "@salty-css/react/styled"; export const Card = styled("section", { defaultVariants: { size: "medium", tone: "neutral" }, base: { padding: "1.5rem", borderRadius: "12px", background: "{theme.background}", color: "{theme.color}", }, variants: { size: { small: { padding: "1rem", borderRadius: "8px" }, medium: { padding: "1.5rem" }, large: { padding: "2.5rem", borderRadius: "16px" }, }, tone: { neutral: {}, brand: { background: "{theme.highlight}", color: "{theme.background}" }, muted: { background: "{theme.altBackground}" }, }, }, compoundVariants: [ { size: "large", tone: "brand", css: { fontWeight: 600 } }, ], }); ``` `size` and `tone` are now typed props. Render with: ```tsx import { Button } from "./button/button.css"; export const MyComponent = () => { return (
); }; ``` For more on variants — including `anyOfVariants` for "any of these branches matches" rules — see [Variants](/docs/variants/). ### 4. Add design tokens and dark mode Salty themes are CSS variables under the hood. You declare two (or more) value sets under `conditional.theme`, and the active set is picked by an attribute on an ancestor — usually ``. No provider, no context. ```ts // /styles/themes.css.ts import { defineVariables } from "@salty-css/core/factories"; export const palette = defineVariables({ colors: { ink: "#0d1117", paper: "#ffffff", sand: "#f5f1e8", coral: "#ff6b5a", }, }); export const themes = defineVariables({ conditional: { theme: { light: { background: "{colors.paper}", altBackground: "{colors.sand}", color: "{colors.ink}", highlight: "{colors.coral}", }, dark: { background: "{colors.ink}", altBackground: "#1c2128", color: "{colors.paper}", highlight: "{colors.coral}", }, }, }, }); ``` Activate a theme by setting the attribute on any ancestor — usually the root: ```html ... ``` That's the whole switch. Every `{theme.background}` / `{theme.color}` reference in your styles now reads the dark values. Flip the attribute back to `light` and it all updates again. For a working toggle that persists the choice in `localStorage` and avoids the first-paint flash: ```tsx // /components/theme-toggle.tsx "use client"; import { useEffect, useState } from "react"; type Theme = "dark" | "light"; const STORAGE_KEY = "theme"; const readInitial = (): Theme => { if (typeof window === "undefined") return "light"; const saved = window.localStorage.getItem(STORAGE_KEY) as Theme | null; if (saved === "dark" || saved === "light") return saved; return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light"; }; export const ThemeToggle = () => { const [theme, setTheme] = useState("light"); useEffect(() => { const initial = readInitial(); setTheme(initial); document.documentElement.dataset.theme = initial; }, []); const toggle = () => { const next: Theme = theme === "dark" ? "light" : "dark"; setTheme(next); document.documentElement.dataset.theme = next; window.localStorage.setItem(STORAGE_KEY, next); }; return ( ); }; ``` To avoid a flash of the wrong theme on first paint in Next.js, inline a tiny pre-hydration script in `app/layout.tsx` (or `_document.tsx` for the Pages Router) so the attribute is set before React hydrates: ```tsx // /app/layout.tsx const setThemeScript = ` try { var t = localStorage.getItem("theme"); if (t !== "dark" && t !== "light") { t = matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light"; } document.documentElement.dataset.theme = t; } catch (e) {} `; export default function RootLayout({ children }) { return (