@salty-css/core 0.2.2-refactor-vite-8.5 → 0.2.2-refactor-vite-8.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +535 -0
- package/package.json +1 -1
package/README.md
ADDED
|
@@ -0,0 +1,535 @@
|
|
|
1
|
+

|
|
2
|
+
|
|
3
|
+
# Salty CSS — CSS-in-JS that compiles away
|
|
4
|
+
|
|
5
|
+
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.
|
|
6
|
+
|
|
7
|
+
[Get started](#get-started) · [API index](#api-index) · [Docs](https://salty-css.dev/docs) · [Discord](https://discord.gg/R6kr4KxMhP) · [GitHub](https://github.com/margarita-form/salty-css) · [NPM](https://www.npmjs.com/package/@salty-css/core)
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
- **Build-time compilation** — no runtime style injection, no FOUC, no client bundle cost.
|
|
12
|
+
- **Framework support** — Next.js (App + Pages, React Server Components, Webpack & Turbopack), React + Vite, React + Webpack, Astro.
|
|
13
|
+
- **Type safety** — TypeScript-first authoring, generated token types, an [ESLint plugin](https://www.npmjs.com/package/@salty-css/eslint-plugin-core).
|
|
14
|
+
- **Design tokens & theming** — static, responsive (media-bound), and conditional (data-attribute / class-bound) variables in one place.
|
|
15
|
+
- **Templates** — reusable style bundles with their own variants.
|
|
16
|
+
- **Modifiers** — custom value transformers (`'space:3'` → `'12px'`) defined in config.
|
|
17
|
+
- **Variants, compound variants, anyOf variants, default variants** out of the box.
|
|
18
|
+
|
|
19
|
+
## Get started
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npx salty-css init
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
`init` detects your framework, installs the right packages, creates `salty.config.ts`, and wires the bundler plugin. Per-framework setup:
|
|
26
|
+
|
|
27
|
+
- **Next.js** → [salty-css.dev/docs/installation](https://salty-css.dev/docs/installation) — `withSaltyCss(nextConfig)` in `next.config.ts`. Auto-detects Webpack vs Turbopack; React Server Components supported.
|
|
28
|
+
- **React + Vite** → [salty-css.dev/docs/installation](https://salty-css.dev/docs/installation) — `saltyPlugin(__dirname)` in `vite.config.ts`.
|
|
29
|
+
- **Astro** → [salty-css.dev/docs/installation](https://salty-css.dev/docs/installation) — `saltyIntegration()` in `astro.config.mjs`.
|
|
30
|
+
|
|
31
|
+
React + Webpack (without Next.js) is also supported via `@salty-css/webpack`.
|
|
32
|
+
|
|
33
|
+
## CLI
|
|
34
|
+
|
|
35
|
+
| Command | Alias | What it does |
|
|
36
|
+
| ----------------------------------- | ----- | ------------------------------------------------------------------------------------------------------------------------------ |
|
|
37
|
+
| `npx salty-css init [directory]` | — | Detect framework, install packages, create `salty.config.ts`, wire the plugin. |
|
|
38
|
+
| `npx salty-css generate [filePath]` | `g` | Scaffold a new Salty component file. Flags: `--name`, `--className`, `--tag`. |
|
|
39
|
+
| `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`. |
|
|
40
|
+
| `npx salty-css update [version]` | `up` | Update all `@salty-css/*` packages. Defaults to `latest`. Flags: `--dir`, `--yes`. |
|
|
41
|
+
| `npx salty-css --version` | — | Print CLI version. |
|
|
42
|
+
|
|
43
|
+
Full reference: [salty-css.dev/docs/cli](https://salty-css.dev/docs/cli).
|
|
44
|
+
|
|
45
|
+
## Good to know
|
|
46
|
+
|
|
47
|
+
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.
|
|
48
|
+
2. **Extending non-Salty components is fine** — `styled(NextLink, { ... })` — as long as the wrapped component forwards `className` to a DOM element.
|
|
49
|
+
3. **Async values & functions are allowed** (`base: { color: async () => 'red' }`), but heavy imports inside `*.css.ts` slow the build.
|
|
50
|
+
4. **React Server Components are supported** via `@salty-css/next` — no `'use client'` needed for styled output.
|
|
51
|
+
|
|
52
|
+
## ESLint
|
|
53
|
+
|
|
54
|
+
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`):
|
|
55
|
+
|
|
56
|
+
- **`@salty-css/core/must-be-exported`** — every `styled`, `className`, `keyframes`, and `defineX*` call must be exported; the compiler ignores anything else.
|
|
57
|
+
- **`@salty-css/core/no-variants-in-base`** — `variants` must be a sibling of `base`, not nested inside it.
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
npm i -D @salty-css/eslint-plugin-core @salty-css/eslint-config-core
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
**Flat config (ESLint 9+):**
|
|
64
|
+
|
|
65
|
+
```js
|
|
66
|
+
// eslint.config.mjs
|
|
67
|
+
import saltyConfig from '@salty-css/eslint-config-core/flat';
|
|
68
|
+
|
|
69
|
+
export default [saltyConfig];
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
**Legacy (`.eslintrc`):**
|
|
73
|
+
|
|
74
|
+
```js
|
|
75
|
+
module.exports = { extends: ['@salty-css/eslint-config-core'] };
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Full reference → [salty-css.dev/docs/eslint](https://salty-css.dev/docs/eslint).
|
|
79
|
+
|
|
80
|
+
## API index
|
|
81
|
+
|
|
82
|
+
| Symbol | Import | One-liner | Docs |
|
|
83
|
+
| ----------------------------------------------- | ------------------------------- | ----------------------------------------------------------------------------- | ----------------------------------------------------------------------- |
|
|
84
|
+
| [`styled`](#styled) | `@salty-css/react/styled` | React component factory with variants & extension. | [api/styled](https://salty-css.dev/docs/api/styled) |
|
|
85
|
+
| [`className`](#classname) | `@salty-css/react/class-name` | CSS class string with `.variant()` chaining. | [api/classname](https://salty-css.dev/docs/api/classname) |
|
|
86
|
+
| [Variants](#variants) | — | `variants`, `compoundVariants`, `anyOfVariants`, `defaultVariants` semantics. | [variants](https://salty-css.dev/docs/variants) |
|
|
87
|
+
| [Overrides](#overrides) | — | Extend components, swap element, override per-instance via `style`. | [overrides](https://salty-css.dev/docs/overrides) |
|
|
88
|
+
| [ESLint plugin](#eslint) | `@salty-css/eslint-config-core` | Two rules: enforce `export` and correct `variants` placement on Salty files. | [eslint](https://salty-css.dev/docs/eslint) |
|
|
89
|
+
| [`defineConfig`](#defineconfig) | `@salty-css/core/config` | Top-level project config. | [api/config](https://salty-css.dev/docs/api/config) |
|
|
90
|
+
| [`defineVariables`](#definevariables) | `@salty-css/core/factories` | Static, responsive, and conditional CSS variables (tokens). | [variables](https://salty-css.dev/docs/variables) |
|
|
91
|
+
| [Theming](#theming) | — | `data-theme` switcher built on conditional variables. | [theming](https://salty-css.dev/docs/theming) |
|
|
92
|
+
| [`defineGlobalStyles`](#defineglobalstyles) | `@salty-css/core/factories` | Global rules (`html`, `body`, etc.). | [api/define-factories](https://salty-css.dev/docs/api/define-factories) |
|
|
93
|
+
| [`defineMediaQuery`](#definemediaquery) | `@salty-css/core/factories` | Named, reusable media queries. | [media-queries](https://salty-css.dev/docs/media-queries) |
|
|
94
|
+
| [`defineTemplates`](#definetemplates) | `@salty-css/core/factories` | Reusable style bundles, optionally with variants. | [templates](https://salty-css.dev/docs/templates) |
|
|
95
|
+
| [`defineFont`](#definefont) | `@salty-css/core/factories` | `@font-face` (or `@import`) + CSS variable in one. | [fonts](https://salty-css.dev/docs/fonts) |
|
|
96
|
+
| [`defineImport`](#defineimport) | `@salty-css/core/factories` | Pull external CSS into Salty's `imports` layer. | [imports](https://salty-css.dev/docs/imports) |
|
|
97
|
+
| [`keyframes`](#keyframes) | `@salty-css/react/keyframes` | Typed `@keyframes` with params and initial-state injection. | [animations](https://salty-css.dev/docs/animations) |
|
|
98
|
+
| [`defineViewportClamp`](#defineviewportclamp) | `@salty-css/core/helpers` | Fluid `clamp()` values that scale with the viewport. | [viewport-clamp](https://salty-css.dev/docs/viewport-clamp) |
|
|
99
|
+
| [`color`](#color) | `@salty-css/core/helpers` | Color manipulation (`alpha`, `darken`, …). | [color-function](https://salty-css.dev/docs/color-function) |
|
|
100
|
+
| [Modifiers](#modifiers) | (on `defineConfig`) | Custom value transformers, e.g. `'space:3'` → `'12px'`. | [modifiers](https://salty-css.dev/docs/modifiers) |
|
|
101
|
+
| [`withSaltyCss`](#withsaltycss-nextjs) | `@salty-css/next` | Next.js config wrapper (Webpack + Turbopack). | [installation](https://salty-css.dev/docs/installation) |
|
|
102
|
+
| [`saltyPlugin` (Vite)](#saltyplugin-vite) | `@salty-css/vite` | Vite plugin. | [installation](https://salty-css.dev/docs/installation) |
|
|
103
|
+
| [`saltyPlugin` (Webpack)](#saltyplugin-webpack) | `@salty-css/webpack` | Webpack loader + plugin. | [installation](https://salty-css.dev/docs/installation) |
|
|
104
|
+
| [`saltyIntegration`](#saltyintegration-astro) | `@salty-css/astro/integration` | Astro integration. | [installation](https://salty-css.dev/docs/installation) |
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
## `styled`
|
|
109
|
+
|
|
110
|
+
```ts
|
|
111
|
+
// components/button.css.ts
|
|
112
|
+
import { styled } from '@salty-css/react/styled';
|
|
113
|
+
|
|
114
|
+
export const Button = styled('button', {
|
|
115
|
+
className: 'btn', // optional custom class
|
|
116
|
+
element: 'button', // optional element override
|
|
117
|
+
base: {
|
|
118
|
+
padding: '0.6em 1.2em',
|
|
119
|
+
border: '1px solid currentColor',
|
|
120
|
+
'&:hover': { background: 'black', color: 'white' },
|
|
121
|
+
},
|
|
122
|
+
variants: {
|
|
123
|
+
variant: {
|
|
124
|
+
outlined: {},
|
|
125
|
+
solid: { background: 'black', color: 'white' },
|
|
126
|
+
},
|
|
127
|
+
},
|
|
128
|
+
compoundVariants: [
|
|
129
|
+
/* { variant: 'solid', size: 'lg', css: { ... } } */
|
|
130
|
+
],
|
|
131
|
+
defaultVariants: { variant: 'outlined' },
|
|
132
|
+
defaultProps: { type: 'button' },
|
|
133
|
+
passProps: false, // pass variant props through to the rendered element
|
|
134
|
+
priority: undefined, // 0–8, higher wins; usually leave to auto
|
|
135
|
+
});
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
```tsx
|
|
139
|
+
<Button variant="solid">Save</Button>
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
See full reference → [salty-css.dev/docs/api/styled](https://salty-css.dev/docs/api/styled).
|
|
143
|
+
|
|
144
|
+
## `className`
|
|
145
|
+
|
|
146
|
+
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.
|
|
147
|
+
|
|
148
|
+
```ts
|
|
149
|
+
// components/card.css.ts
|
|
150
|
+
import { className } from '@salty-css/react/class-name';
|
|
151
|
+
|
|
152
|
+
export const card = className({
|
|
153
|
+
className: 'card',
|
|
154
|
+
base: {
|
|
155
|
+
padding: '1rem',
|
|
156
|
+
borderRadius: '8px',
|
|
157
|
+
},
|
|
158
|
+
variants: {
|
|
159
|
+
tone: {
|
|
160
|
+
neutral: { background: '#f6f6f6' },
|
|
161
|
+
brand: { background: '{colors.brand.main}' },
|
|
162
|
+
},
|
|
163
|
+
},
|
|
164
|
+
});
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
```tsx
|
|
168
|
+
<div className={`${card.variant('tone', 'brand')}`}>Hello</div>
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
See full reference → [salty-css.dev/docs/api/classname](https://salty-css.dev/docs/api/classname).
|
|
172
|
+
|
|
173
|
+
## Variants
|
|
174
|
+
|
|
175
|
+
`styled` and `className` share the same variant shape:
|
|
176
|
+
|
|
177
|
+
- **`variants`** — named axes (e.g. `size: { sm, md, lg }`). Each axis becomes a prop.
|
|
178
|
+
- **`compoundVariants`** — array; styles applied only when **all** listed axes match.
|
|
179
|
+
- **`anyOfVariants`** — array; styles applied when **any** listed axis matches.
|
|
180
|
+
- **`defaultVariants`** — values used when the prop is omitted at the call site.
|
|
181
|
+
|
|
182
|
+
```ts
|
|
183
|
+
styled('a', {
|
|
184
|
+
base: { textDecoration: 'none' },
|
|
185
|
+
variants: {
|
|
186
|
+
size: { sm: { fontSize: 14 }, lg: { fontSize: 18 } },
|
|
187
|
+
underline: { true: { textDecoration: 'underline' } },
|
|
188
|
+
},
|
|
189
|
+
compoundVariants: [{ size: 'lg', underline: true, css: { textUnderlineOffset: 4 } }],
|
|
190
|
+
defaultVariants: { size: 'sm' },
|
|
191
|
+
});
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
Boolean variants accept `true`/`false` keys; pass them as `<Link underline />` shorthand. See [salty-css.dev/docs/variants](https://salty-css.dev/docs/variants).
|
|
195
|
+
|
|
196
|
+
## Overrides
|
|
197
|
+
|
|
198
|
+
- **Extend any component:** `styled(ExistingComponent, { ... })`. The wrapped component must accept `className`.
|
|
199
|
+
- **Swap the rendered element** for one instance: `<Button as="a" href="/x" />` (use `passProps` if you want variant props to reach the underlying element).
|
|
200
|
+
- **Per-instance style overrides:** `<Box style={{ ... }} />` and `<Box css={{ ... }} />` accept regular React `style` and Salty's `css` prop (overrides apply at the highest priority).
|
|
201
|
+
|
|
202
|
+
See [salty-css.dev/docs/overrides](https://salty-css.dev/docs/overrides) for `as`, `style`, CSS-variable overrides, and ref forwarding.
|
|
203
|
+
|
|
204
|
+
## `defineConfig`
|
|
205
|
+
|
|
206
|
+
```ts
|
|
207
|
+
// salty.config.ts
|
|
208
|
+
import { defineConfig } from '@salty-css/core/config';
|
|
209
|
+
|
|
210
|
+
export const config = defineConfig({
|
|
211
|
+
importStrategy: 'root', // 'root' | 'component'
|
|
212
|
+
variables: {
|
|
213
|
+
/* see defineVariables */
|
|
214
|
+
},
|
|
215
|
+
global: {
|
|
216
|
+
/* see defineGlobalStyles */
|
|
217
|
+
},
|
|
218
|
+
templates: {
|
|
219
|
+
/* see defineTemplates */
|
|
220
|
+
},
|
|
221
|
+
modifiers: {
|
|
222
|
+
/* see Modifiers */
|
|
223
|
+
},
|
|
224
|
+
reset: 'default', // 'default' | 'none' | GlobalStyles
|
|
225
|
+
externalModules: ['react', 'react-dom'],
|
|
226
|
+
strict: true, // true | 'warn' | false
|
|
227
|
+
defaultUnit: 'px', // px, rem, em, vh, vw, % …
|
|
228
|
+
});
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
Full reference → [salty-css.dev/docs/api/config](https://salty-css.dev/docs/api/config).
|
|
232
|
+
|
|
233
|
+
## `defineVariables`
|
|
234
|
+
|
|
235
|
+
Tokens come in three flavours — static, responsive (media-bound), conditional (class/data-attribute-bound):
|
|
236
|
+
|
|
237
|
+
```ts
|
|
238
|
+
// styles/variables.css.ts
|
|
239
|
+
import { defineVariables } from '@salty-css/core/factories';
|
|
240
|
+
|
|
241
|
+
export default defineVariables({
|
|
242
|
+
colors: {
|
|
243
|
+
dark: '#111',
|
|
244
|
+
light: '#fefefe',
|
|
245
|
+
brand: { main: '#0070f3', highlight: '#ff4081' },
|
|
246
|
+
},
|
|
247
|
+
responsive: {
|
|
248
|
+
base: { fontSize: { heading: '48px', body: '16px' } },
|
|
249
|
+
'@largeMobileDown': { fontSize: { heading: '32px', body: '14px' } },
|
|
250
|
+
},
|
|
251
|
+
conditional: {
|
|
252
|
+
theme: {
|
|
253
|
+
dark: { backgroundColor: '{colors.dark}', textColor: '{colors.light}' },
|
|
254
|
+
light: { backgroundColor: '{colors.light}', textColor: '{colors.dark}' },
|
|
255
|
+
},
|
|
256
|
+
},
|
|
257
|
+
});
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
Use as string references: `{colors.brand.main}`, `{fontSize.heading}`, `{theme.textColor}`.
|
|
261
|
+
|
|
262
|
+
See [salty-css.dev/docs/variables](https://salty-css.dev/docs/variables).
|
|
263
|
+
|
|
264
|
+
## Theming
|
|
265
|
+
|
|
266
|
+
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:
|
|
267
|
+
|
|
268
|
+
```html
|
|
269
|
+
<html data-theme="dark">
|
|
270
|
+
<!-- '{theme.textColor}' resolves to '{colors.light}' -->
|
|
271
|
+
</html>
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
See the dark-mode walkthrough (with SSR flash fix) → [salty-css.dev/docs/theming](https://salty-css.dev/docs/theming).
|
|
275
|
+
|
|
276
|
+
## `defineGlobalStyles`
|
|
277
|
+
|
|
278
|
+
```ts
|
|
279
|
+
// styles/global.css.ts
|
|
280
|
+
import { defineGlobalStyles } from '@salty-css/core/factories';
|
|
281
|
+
|
|
282
|
+
export default defineGlobalStyles({
|
|
283
|
+
html: { fontFamily: 'Inter, system-ui, sans-serif' },
|
|
284
|
+
body: { margin: 0, background: '#fff' },
|
|
285
|
+
});
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
## `defineMediaQuery`
|
|
289
|
+
|
|
290
|
+
```ts
|
|
291
|
+
// styles/media.css.ts
|
|
292
|
+
import { defineMediaQuery } from '@salty-css/core/factories';
|
|
293
|
+
|
|
294
|
+
export const largeMobileDown = defineMediaQuery((media) => media.maxWidth(600));
|
|
295
|
+
export const darkMode = defineMediaQuery((media) => media.prefersColorScheme('dark'));
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
```ts
|
|
299
|
+
styled('span', { base: { fontSize: 64, '@largeMobileDown': { fontSize: 32 } } });
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
See [salty-css.dev/docs/media-queries](https://salty-css.dev/docs/media-queries).
|
|
303
|
+
|
|
304
|
+
## `defineTemplates`
|
|
305
|
+
|
|
306
|
+
Reusable style bundles. Static templates pick values by dot-path; function templates take a parameter.
|
|
307
|
+
|
|
308
|
+
```ts
|
|
309
|
+
// styles/templates.css.ts
|
|
310
|
+
import { defineTemplates } from '@salty-css/core/factories';
|
|
311
|
+
|
|
312
|
+
export default defineTemplates({
|
|
313
|
+
textStyle: {
|
|
314
|
+
heading: {
|
|
315
|
+
base: { fontFamily: '{fontFamily.heading}', lineHeight: 1.1 },
|
|
316
|
+
variants: {
|
|
317
|
+
weight: { regular: { fontWeight: 500 }, heavy: { fontWeight: 800 } },
|
|
318
|
+
},
|
|
319
|
+
large: { fontSize: '{fontSize.heading.large}' },
|
|
320
|
+
small: { fontSize: '{fontSize.heading.small}' },
|
|
321
|
+
},
|
|
322
|
+
},
|
|
323
|
+
card: (padding: string) => ({
|
|
324
|
+
padding,
|
|
325
|
+
borderRadius: 8,
|
|
326
|
+
boxShadow: '0 0 10px rgba(0,0,0,0.1)',
|
|
327
|
+
}),
|
|
328
|
+
});
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
```ts
|
|
332
|
+
styled('h1', { base: { textStyle: 'heading.large@weight=heavy', card: '20px' } });
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
Rich-node variants, inheritance, and the `compoundVariants`/`anyOfVariants` rules: [salty-css.dev/docs/templates](https://salty-css.dev/docs/templates).
|
|
336
|
+
|
|
337
|
+
## `defineFont`
|
|
338
|
+
|
|
339
|
+
```ts
|
|
340
|
+
// styles/fonts.css.ts
|
|
341
|
+
import { defineFont } from '@salty-css/core/factories';
|
|
342
|
+
|
|
343
|
+
export const Inter = defineFont({
|
|
344
|
+
name: 'Inter',
|
|
345
|
+
variable: '--font-inter',
|
|
346
|
+
display: 'swap',
|
|
347
|
+
fallback: 'system-ui, sans-serif',
|
|
348
|
+
variants: [
|
|
349
|
+
{ weight: 400, style: 'normal', src: '/fonts/inter-400.woff2' },
|
|
350
|
+
{ weight: 700, style: 'normal', src: ['/fonts/inter-700.woff2', '/fonts/inter-700.ttf'] },
|
|
351
|
+
],
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
// Or pull a remote stylesheet (e.g. Google Fonts) — emits @import + variable.
|
|
355
|
+
export const InterCdn = defineFont({
|
|
356
|
+
name: 'Inter',
|
|
357
|
+
variable: '--font-inter',
|
|
358
|
+
import: 'https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap',
|
|
359
|
+
});
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
`Inter.className`, `Inter.variable`, `Inter.fontFamily`, and `Inter.style` are available for explicit usage. See [salty-css.dev/docs/fonts](https://salty-css.dev/docs/fonts).
|
|
363
|
+
|
|
364
|
+
## `defineImport`
|
|
365
|
+
|
|
366
|
+
Pull external CSS into Salty's `imports` cascade layer (which sits **before** `reset`, `global`, `templates`, and your component layers — so your styles always win).
|
|
367
|
+
|
|
368
|
+
```ts
|
|
369
|
+
// styles/imports.css.ts
|
|
370
|
+
import { defineImport } from '@salty-css/core/factories';
|
|
371
|
+
|
|
372
|
+
export default defineImport(
|
|
373
|
+
'./reset.css', // relative
|
|
374
|
+
'modern-normalize/modern-normalize.css', // node_modules
|
|
375
|
+
'~normalize.css/normalize.css', // node_modules (~ form)
|
|
376
|
+
'/fonts/inter.css', // public/ folder
|
|
377
|
+
'https://fonts.googleapis.com/css2?family=Inter', // URL
|
|
378
|
+
{ url: './print.css', media: 'print' }, // media-conditional
|
|
379
|
+
{ url: './p3.css', supports: 'color(display-p3 1 1 1)' }, // supports-conditional
|
|
380
|
+
);
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
Layer order: `@layer imports, reset, global, templates, fonts, l0…l8;`. See [salty-css.dev/docs/imports](https://salty-css.dev/docs/imports).
|
|
384
|
+
|
|
385
|
+
## `keyframes`
|
|
386
|
+
|
|
387
|
+
```ts
|
|
388
|
+
// styles/animations.css.ts
|
|
389
|
+
import { keyframes } from '@salty-css/react/keyframes';
|
|
390
|
+
|
|
391
|
+
export const fadeIn = keyframes({
|
|
392
|
+
animationName: 'fadeIn',
|
|
393
|
+
appendInitialStyles: true, // injects `from`/`0%` as base styles on the component
|
|
394
|
+
params: { delay: '250ms', fillMode: 'forwards' },
|
|
395
|
+
from: { opacity: 0 },
|
|
396
|
+
to: { opacity: 1 },
|
|
397
|
+
});
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
```ts
|
|
401
|
+
styled('div', { base: { animation: fadeIn } });
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
See [salty-css.dev/docs/animations](https://salty-css.dev/docs/animations).
|
|
405
|
+
|
|
406
|
+
## `defineViewportClamp`
|
|
407
|
+
|
|
408
|
+
```ts
|
|
409
|
+
// styles/clamp.css.ts
|
|
410
|
+
import { defineViewportClamp } from '@salty-css/core/helpers';
|
|
411
|
+
|
|
412
|
+
export const fhdClamp = defineViewportClamp({ screenSize: 1920 });
|
|
413
|
+
export const mobileClamp = defineViewportClamp({ screenSize: 640, axis: 'horizontal' });
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
```ts
|
|
417
|
+
styled('span', { base: { fontSize: fhdClamp(96), '@largeMobileDown': { fontSize: mobileClamp(48) } } });
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
Options: `screenSize`, `axis` (`'horizontal' | 'vertical'`), `minMultiplier`, `maxMultiplier`, `minMaxUnit`. See [salty-css.dev/docs/viewport-clamp](https://salty-css.dev/docs/viewport-clamp).
|
|
421
|
+
|
|
422
|
+
## `color`
|
|
423
|
+
|
|
424
|
+
```ts
|
|
425
|
+
import { color } from '@salty-css/core/helpers';
|
|
426
|
+
import { styled } from '@salty-css/react/styled';
|
|
427
|
+
|
|
428
|
+
export const Tint = styled('span', {
|
|
429
|
+
base: { backgroundColor: color('#000').alpha(0.5) },
|
|
430
|
+
});
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
Backed by the [Qix-/color](https://github.com/Qix-/color) library — `.alpha()`, `.darken()`, `.lighten()`, `.mix()`, etc. See [salty-css.dev/docs/color-function](https://salty-css.dev/docs/color-function).
|
|
434
|
+
|
|
435
|
+
## Modifiers
|
|
436
|
+
|
|
437
|
+
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`).
|
|
438
|
+
|
|
439
|
+
```ts
|
|
440
|
+
// salty.config.ts
|
|
441
|
+
import { defineConfig } from '@salty-css/core/config';
|
|
442
|
+
|
|
443
|
+
export const config = defineConfig({
|
|
444
|
+
modifiers: {
|
|
445
|
+
spaceShorthand: {
|
|
446
|
+
pattern: /^space:(\d+)$/,
|
|
447
|
+
transform: (match) => {
|
|
448
|
+
const n = Number(match.replace('space:', ''));
|
|
449
|
+
return { value: `${n * 4}px` };
|
|
450
|
+
},
|
|
451
|
+
},
|
|
452
|
+
},
|
|
453
|
+
});
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
```ts
|
|
457
|
+
styled('div', { base: { padding: 'space:3' } }); // → padding: 12px
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
See [salty-css.dev/docs/modifiers](https://salty-css.dev/docs/modifiers).
|
|
461
|
+
|
|
462
|
+
## `withSaltyCss` (Next.js)
|
|
463
|
+
|
|
464
|
+
```ts
|
|
465
|
+
// next.config.ts
|
|
466
|
+
import { withSaltyCss } from '@salty-css/next';
|
|
467
|
+
|
|
468
|
+
const nextConfig = {
|
|
469
|
+
/* your config */
|
|
470
|
+
};
|
|
471
|
+
export default withSaltyCss(nextConfig);
|
|
472
|
+
// Or pin a bundler: withSaltyCss(nextConfig, { bundler: 'webpack' })
|
|
473
|
+
```
|
|
474
|
+
|
|
475
|
+
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()`).
|
|
476
|
+
|
|
477
|
+
Next.js 14 CommonJS:
|
|
478
|
+
|
|
479
|
+
```js
|
|
480
|
+
const { withSaltyCss } = require('@salty-css/next');
|
|
481
|
+
module.exports = withSaltyCss(nextConfig);
|
|
482
|
+
```
|
|
483
|
+
|
|
484
|
+
See [salty-css.dev/docs/installation](https://salty-css.dev/docs/installation).
|
|
485
|
+
|
|
486
|
+
## `saltyPlugin` (Vite)
|
|
487
|
+
|
|
488
|
+
```ts
|
|
489
|
+
// vite.config.ts
|
|
490
|
+
import { defineConfig } from 'vite';
|
|
491
|
+
import { saltyPlugin } from '@salty-css/vite';
|
|
492
|
+
|
|
493
|
+
export default defineConfig({
|
|
494
|
+
plugins: [saltyPlugin(__dirname)],
|
|
495
|
+
});
|
|
496
|
+
```
|
|
497
|
+
|
|
498
|
+
Options: `{ mode }`.
|
|
499
|
+
|
|
500
|
+
## `saltyPlugin` (Webpack)
|
|
501
|
+
|
|
502
|
+
```js
|
|
503
|
+
// webpack.config.js
|
|
504
|
+
const { saltyPlugin } = require('@salty-css/webpack');
|
|
505
|
+
|
|
506
|
+
module.exports = (env, argv) => {
|
|
507
|
+
const config = {
|
|
508
|
+
/* … */
|
|
509
|
+
};
|
|
510
|
+
saltyPlugin(config, __dirname);
|
|
511
|
+
return config;
|
|
512
|
+
};
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
Signature: `saltyPlugin(config, dir, isServer?, cjs?, { mode? })`.
|
|
516
|
+
|
|
517
|
+
## `saltyIntegration` (Astro)
|
|
518
|
+
|
|
519
|
+
```ts
|
|
520
|
+
// astro.config.mjs
|
|
521
|
+
import { defineConfig } from 'astro/config';
|
|
522
|
+
import saltyIntegration from '@salty-css/astro/integration';
|
|
523
|
+
|
|
524
|
+
export default defineConfig({
|
|
525
|
+
integrations: [saltyIntegration()],
|
|
526
|
+
});
|
|
527
|
+
```
|
|
528
|
+
|
|
529
|
+
Options: `srcDir` (defaults to `'src'`), `rootDir` (defaults to the Astro config root).
|
|
530
|
+
|
|
531
|
+
---
|
|
532
|
+
|
|
533
|
+
## Support
|
|
534
|
+
|
|
535
|
+
Help, questions, or feedback → [Join the Salty CSS Discord](https://discord.gg/R6kr4KxMhP). Bug reports → [GitHub issues](https://github.com/margarita-form/salty-css/issues).
|