npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

create-salty-css

v0.4.5

Published

NPM create CLI package for Salty CSS

Readme

Salty CSS Banner

Salty CSS — CSS-in-JS that compiles away

Salty CSS is a build-time, type-safe CSS-in-JS library. You author styles in .css.ts files; the compiler emits real CSS, your runtime ships zero styling logic.

Get started · API index · Docs · Discord · GitHub · NPM

Features

  • Build-time compilation — no runtime style injection, no FOUC, no client bundle cost.
  • Framework support — Next.js (App + Pages, React Server Components, Webpack & Turbopack), React + Vite, React + Webpack, Astro.
  • Type safety — TypeScript-first authoring, generated token types, an ESLint plugin.
  • Design tokens & theming — static, responsive (media-bound), and conditional (data-attribute / class-bound) variables in one place.
  • Templates — reusable style bundles with their own variants.
  • Modifiers — custom value transformers ('space:3''12px') defined in config.
  • Variants, compound variants, anyOf variants, default variants out of the box.

Get started

npx salty-css init

init detects your framework, installs the right packages, creates salty.config.ts, and wires the bundler plugin. Per-framework setup:

React + Webpack (without Next.js) is also supported via @salty-css/webpack.

CLI

| Command | Alias | What it does | | ----------------------------------- | ----- | ------------------------------------------------------------------------------------------------------------------------------ | | npx salty-css init [directory] | — | Detect framework, install packages, create salty.config.ts, wire the plugin. | | npx salty-css generate [filePath] | g | Scaffold a new Salty component file. Flags: --name, --className, --tag. | | npx salty-css build [directory] | b | Compile *.css.ts files into saltygen/index.css. Not needed when the bundler plugin is running. Flags: --watch, --mode. | | npx salty-css update [version] | up | Update all @salty-css/* packages. Defaults to latest. Flags: --dir, --yes. | | npx salty-css --version | — | Print CLI version. |

Full reference: salty-css.dev/docs/cli.

Good to know

  1. File extensions matter. styled, className, keyframes, and every defineX(...) call must live in a file ending .css.ts, .css.tsx, .salty.ts, .styles.ts, or .styled.ts. The compiler ignores everything else.
  2. Extending non-Salty components is finestyled(NextLink, { ... }) — as long as the wrapped component forwards className to a DOM element.
  3. Async values & functions are allowed (base: { color: async () => 'red' }), but heavy imports inside *.css.ts slow the build.
  4. React Server Components are supported via @salty-css/next — no 'use client' needed for styled output.

ESLint

Salty CSS ships a small ESLint plugin and matching shareable config. Two rules, both error by default, both autofixable, both scoped to Salty files (.css.ts, .css.tsx, .salty.ts, .styles.ts, .styled.ts):

  • @salty-css/core/must-be-exported — every styled, className, keyframes, and defineX* call must be exported; the compiler ignores anything else.
  • @salty-css/core/no-variants-in-basevariants must be a sibling of base, not nested inside it.
npm i -D @salty-css/eslint-plugin-core @salty-css/eslint-config-core

Flat config (ESLint 9+):

// eslint.config.mjs
import saltyConfig from '@salty-css/eslint-config-core/flat';

export default [saltyConfig];

Legacy (.eslintrc):

module.exports = { extends: ['@salty-css/eslint-config-core'] };

Full reference → salty-css.dev/docs/eslint.

API index

| Symbol | Import | One-liner | Docs | | ----------------------------------------------- | ------------------------------ | ----------------------------------------------------------------------------- | ----------------------------------------------------------------------- | | styled | @salty-css/react/styled | React component factory with variants & extension. | api/styled | | className | @salty-css/react/class-name | CSS class string with .variant() chaining. | api/classname | | Variants | — | variants, compoundVariants, anyOfVariants, defaultVariants semantics. | variants | | Overrides | — | Extend components, swap element, override per-instance via style. | overrides | | ESLint plugin | @salty-css/eslint-config-core | Two rules: enforce export and correct variants placement on Salty files. | eslint | | defineConfig | @salty-css/core/config | Top-level project config. | api/config | | defineVariables | @salty-css/core/factories | Static, responsive, and conditional CSS variables (tokens). | variables | | Theming | — | data-theme switcher built on conditional variables. | theming | | defineGlobalStyles | @salty-css/core/factories | Global rules (html, body, etc.). | api/define-factories | | defineMediaQuery | @salty-css/core/factories | Named, reusable media queries. | media-queries | | defineTemplates | @salty-css/core/factories | Reusable style bundles, optionally with variants. | templates | | defineFont | @salty-css/core/factories | @font-face (or @import) + CSS variable in one. | fonts | | defineImport | @salty-css/core/factories | Pull external CSS into Salty's imports layer. | imports | | keyframes | @salty-css/react/keyframes | Typed @keyframes with params and initial-state injection. | animations | | defineViewportClamp | @salty-css/core/helpers | Fluid clamp() values that scale with the viewport. | viewport-clamp | | color | @salty-css/core/helpers | Color manipulation (alpha, darken, …). | color-function | | Modifiers | (on defineConfig) | Custom value transformers, e.g. 'space:3''12px'. | modifiers | | withSaltyCss | @salty-css/next | Next.js config wrapper (Webpack + Turbopack). | installation | | saltyPlugin (Vite) | @salty-css/vite | Vite plugin. | installation | | saltyPlugin (Webpack) | @salty-css/webpack | Webpack loader + plugin. | installation | | saltyIntegration | @salty-css/astro/integration | Astro integration. | installation |


styled

// components/button.css.ts
import { styled } from '@salty-css/react/styled';

export const Button = styled('button', {
  className: 'btn', // optional custom class
  element: 'button', // optional element override
  base: {
    padding: '0.6em 1.2em',
    border: '1px solid currentColor',
    '&:hover': { background: 'black', color: 'white' },
  },
  variants: {
    variant: {
      outlined: {},
      solid: { background: 'black', color: 'white' },
    },
  },
  compoundVariants: [
    /* { variant: 'solid', size: 'lg', css: { ... } } */
  ],
  defaultVariants: { variant: 'outlined' },
  defaultProps: { type: 'button' },
  passProps: false, // pass variant props through to the rendered element
  priority: undefined, // 0–8, higher wins; usually leave to auto
});
<Button variant="solid">Save</Button>

See full reference → salty-css.dev/docs/api/styled.

className

Framework-agnostic class-string factory. The return value is a string you can drop in className=, plus a .variant(name, value) method for chaining variant classes.

// components/card.css.ts
import { className } from '@salty-css/react/class-name';

export const card = className({
  className: 'card',
  base: {
    padding: '1rem',
    borderRadius: '8px',
  },
  variants: {
    tone: {
      neutral: { background: '#f6f6f6' },
      brand: { background: '{colors.brand.main}' },
    },
  },
});
<div className={`${card.variant('tone', 'brand')}`}>Hello</div>

See full reference → salty-css.dev/docs/api/classname.

Variants

styled and className share the same variant shape:

  • variants — named axes (e.g. size: { sm, md, lg }). Each axis becomes a prop.
  • compoundVariants — array; styles applied only when all listed axes match.
  • anyOfVariants — array; styles applied when any listed axis matches.
  • defaultVariants — values used when the prop is omitted at the call site.
styled('a', {
  base: { textDecoration: 'none' },
  variants: {
    size: { sm: { fontSize: 14 }, lg: { fontSize: 18 } },
    underline: { true: { textDecoration: 'underline' } },
  },
  compoundVariants: [{ size: 'lg', underline: true, css: { textUnderlineOffset: 4 } }],
  defaultVariants: { size: 'sm' },
});

Boolean variants accept true/false keys; pass them as <Link underline /> shorthand. See salty-css.dev/docs/variants.

Overrides

  • Extend any component: styled(ExistingComponent, { ... }). The wrapped component must accept className.
  • Swap the rendered element for one instance: <Button as="a" href="/x" /> (use passProps if you want variant props to reach the underlying element).
  • Per-instance style overrides: <Box style={{ ... }} /> and <Box css={{ ... }} /> accept regular React style and Salty's css prop (overrides apply at the highest priority).

See salty-css.dev/docs/overrides for as, style, CSS-variable overrides, and ref forwarding.

defineConfig

// salty.config.ts
import { defineConfig } from '@salty-css/core/config';

export const config = defineConfig({
  importStrategy: 'root', // 'root' | 'component'
  variables: {
    /* see defineVariables */
  },
  global: {
    /* see defineGlobalStyles */
  },
  templates: {
    /* see defineTemplates */
  },
  modifiers: {
    /* see Modifiers */
  },
  reset: 'default', // 'default' | 'none' | GlobalStyles
  externalModules: ['react', 'react-dom'],
  strict: true, // true | 'warn' | false
  defaultUnit: 'px', // px, rem, em, vh, vw, % …
});

Full reference → salty-css.dev/docs/api/config.

defineVariables

Tokens come in three flavours — static, responsive (media-bound), conditional (class/data-attribute-bound):

// styles/variables.css.ts
import { defineVariables } from '@salty-css/core/factories';

export default defineVariables({
  colors: {
    dark: '#111',
    light: '#fefefe',
    brand: { main: '#0070f3', highlight: '#ff4081' },
  },
  responsive: {
    base: { fontSize: { heading: '48px', body: '16px' } },
    '@largeMobileDown': { fontSize: { heading: '32px', body: '14px' } },
  },
  conditional: {
    theme: {
      dark: { backgroundColor: '{colors.dark}', textColor: '{colors.light}' },
      light: { backgroundColor: '{colors.light}', textColor: '{colors.dark}' },
    },
  },
});

Use as string references: {colors.brand.main}, {fontSize.heading}, {theme.textColor}.

See salty-css.dev/docs/variables.

Theming

The conditional bucket above wires up data-attribute / class-based themes with zero providers. Toggle a data-theme attribute on <html> (or any ancestor) and conditional variables resolve to the matching branch:

<html data-theme="dark">
  <!-- '{theme.textColor}' resolves to '{colors.light}' -->
</html>

See the dark-mode walkthrough (with SSR flash fix) → salty-css.dev/docs/theming.

defineGlobalStyles

// styles/global.css.ts
import { defineGlobalStyles } from '@salty-css/core/factories';

export default defineGlobalStyles({
  html: { fontFamily: 'Inter, system-ui, sans-serif' },
  body: { margin: 0, background: '#fff' },
});

defineMediaQuery

// styles/media.css.ts
import { defineMediaQuery } from '@salty-css/core/factories';

export const largeMobileDown = defineMediaQuery((media) => media.maxWidth(600));
export const darkMode = defineMediaQuery((media) => media.prefersColorScheme('dark'));
styled('span', { base: { fontSize: 64, '@largeMobileDown': { fontSize: 32 } } });

See salty-css.dev/docs/media-queries.

defineTemplates

Reusable style bundles. Static templates pick values by dot-path; function templates take a parameter.

// styles/templates.css.ts
import { defineTemplates } from '@salty-css/core/factories';

export default defineTemplates({
  textStyle: {
    heading: {
      base: { fontFamily: '{fontFamily.heading}', lineHeight: 1.1 },
      variants: {
        weight: { regular: { fontWeight: 500 }, heavy: { fontWeight: 800 } },
      },
      large: { fontSize: '{fontSize.heading.large}' },
      small: { fontSize: '{fontSize.heading.small}' },
    },
  },
  card: (padding: string) => ({
    padding,
    borderRadius: 8,
    boxShadow: '0 0 10px rgba(0,0,0,0.1)',
  }),
});
styled('h1', { base: { textStyle: 'heading.large@weight=heavy', card: '20px' } });

Rich-node variants, inheritance, and the compoundVariants/anyOfVariants rules: salty-css.dev/docs/templates.

defineFont

// styles/fonts.css.ts
import { defineFont } from '@salty-css/core/factories';

export const Inter = defineFont({
  name: 'Inter',
  variable: '--font-inter',
  display: 'swap',
  fallback: 'system-ui, sans-serif',
  variants: [
    { weight: 400, style: 'normal', src: '/fonts/inter-400.woff2' },
    { weight: 700, style: 'normal', src: ['/fonts/inter-700.woff2', '/fonts/inter-700.ttf'] },
  ],
});

// Or pull a remote stylesheet (e.g. Google Fonts) — emits @import + variable.
export const InterCdn = defineFont({
  name: 'Inter',
  variable: '--font-inter',
  import: 'https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap',
});

Inter.className, Inter.variable, Inter.fontFamily, and Inter.style are available for explicit usage. See salty-css.dev/docs/fonts.

defineImport

Pull external CSS into Salty's imports cascade layer (which sits before reset, global, templates, and your component layers — so your styles always win).

// styles/imports.css.ts
import { defineImport } from '@salty-css/core/factories';

export default defineImport(
  './reset.css', // relative
  'modern-normalize/modern-normalize.css', // node_modules
  '~normalize.css/normalize.css', // node_modules (~ form)
  '/fonts/inter.css', // public/ folder
  'https://fonts.googleapis.com/css2?family=Inter', // URL
  { url: './print.css', media: 'print' }, // media-conditional
  { url: './p3.css', supports: 'color(display-p3 1 1 1)' } // supports-conditional
);

Layer order: @layer imports, reset, global, templates, fonts, l0…l8;. See salty-css.dev/docs/imports.

keyframes

// styles/animations.css.ts
import { keyframes } from '@salty-css/react/keyframes';

export const fadeIn = keyframes({
  animationName: 'fadeIn',
  appendInitialStyles: true, // injects `from`/`0%` as base styles on the component
  params: { delay: '250ms', fillMode: 'forwards' },
  from: { opacity: 0 },
  to: { opacity: 1 },
});
styled('div', { base: { animation: fadeIn } });

See salty-css.dev/docs/animations.

defineViewportClamp

// styles/clamp.css.ts
import { defineViewportClamp } from '@salty-css/core/helpers';

export const fhdClamp = defineViewportClamp({ screenSize: 1920 });
export const mobileClamp = defineViewportClamp({ screenSize: 640, axis: 'horizontal' });
styled('span', { base: { fontSize: fhdClamp(96), '@largeMobileDown': { fontSize: mobileClamp(48) } } });

Options: screenSize, axis ('horizontal' | 'vertical'), minMultiplier, maxMultiplier, minMaxUnit. See salty-css.dev/docs/viewport-clamp.

color

import { color } from '@salty-css/core/helpers';
import { styled } from '@salty-css/react/styled';

export const Tint = styled('span', {
  base: { backgroundColor: color('#000').alpha(0.5) },
});

Backed by the Qix-/color library — .alpha(), .darken(), .lighten(), .mix(), etc. See salty-css.dev/docs/color-function.

Modifiers

Custom value transformers registered on defineConfig. Each modifier is a { pattern, transform } pair: when Salty sees a string value matching pattern, it replaces it with transform(match).value (and optionally emits extra CSS via transform(match).css).

// salty.config.ts
import { defineConfig } from '@salty-css/core/config';

export const config = defineConfig({
  modifiers: {
    spaceShorthand: {
      pattern: /^space:(\d+)$/,
      transform: (match) => {
        const n = Number(match.replace('space:', ''));
        return { value: `${n * 4}px` };
      },
    },
  },
});
styled('div', { base: { padding: 'space:3' } }); // → padding: 12px

See salty-css.dev/docs/modifiers.

withSaltyCss (Next.js)

// next.config.ts
import { withSaltyCss } from '@salty-css/next';

const nextConfig = {
  /* your config */
};
export default withSaltyCss(nextConfig);
// Or pin a bundler: withSaltyCss(nextConfig, { bundler: 'webpack' })

Options: mode (build mode override), bundler ('auto' | 'webpack' | 'turbopack', defaults to auto-detect via process.env.TURBOPACK), dir (project root for Turbopack — defaults to nextConfig.turbopack.root or process.cwd()).

Next.js 14 CommonJS:

const { withSaltyCss } = require('@salty-css/next');
module.exports = withSaltyCss(nextConfig);

See salty-css.dev/docs/installation.

saltyPlugin (Vite)

// vite.config.ts
import { defineConfig } from 'vite';
import { saltyPlugin } from '@salty-css/vite';

export default defineConfig({
  plugins: [saltyPlugin(__dirname)],
});

Options: { mode }.

saltyPlugin (Webpack)

// webpack.config.js
const { saltyPlugin } = require('@salty-css/webpack');

module.exports = (env, argv) => {
  const config = {
    /* … */
  };
  saltyPlugin(config, __dirname);
  return config;
};

Signature: saltyPlugin(config, dir, isServer?, cjs?, { mode? }).

saltyIntegration (Astro)

// astro.config.mjs
import { defineConfig } from 'astro/config';
import saltyIntegration from '@salty-css/astro/integration';

export default defineConfig({
  integrations: [saltyIntegration()],
});

Options: srcDir (defaults to 'src'), rootDir (defaults to the Astro config root).


Support

Help, questions, or feedback → Join the Salty CSS Discord. Bug reports → GitHub issues.