@salty-css/eslint-plugin-core 0.1.0-refactor-add-additional-paths-to-config-cache.1 → 0.1.0
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 +355 -374
- package/index.cjs +1 -1
- package/index.js +25 -25
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -1,554 +1,535 @@
|
|
|
1
1
|

|
|
2
2
|
|
|
3
|
-
# Salty CSS
|
|
3
|
+
# Salty CSS — CSS-in-JS that compiles away
|
|
4
4
|
|
|
5
|
-
|
|
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
6
|
|
|
7
|
-
[Get started](#get-started)
|
|
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
8
|
|
|
9
9
|
## Features
|
|
10
10
|
|
|
11
|
-
- Build
|
|
12
|
-
- Next.js, React Server Components,
|
|
13
|
-
- Type safety
|
|
14
|
-
-
|
|
15
|
-
-
|
|
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.
|
|
16
18
|
|
|
17
19
|
## Get started
|
|
18
20
|
|
|
19
|
-
Fastest way to get started with any framework is
|
|
20
|
-
|
|
21
21
|
```bash
|
|
22
22
|
npx salty-css init
|
|
23
23
|
```
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
`init` detects your framework, installs the right packages, creates `salty.config.ts`, and wires the bundler plugin. Per-framework setup:
|
|
26
26
|
|
|
27
|
-
- Next.js → [
|
|
28
|
-
- React + Vite → [
|
|
29
|
-
-
|
|
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
30
|
|
|
31
|
-
|
|
31
|
+
React + Webpack (without Next.js) is also supported via `@salty-css/webpack`.
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
- Build: `npx salty-css build [directory]`
|
|
35
|
-
- Update Salty CSS packages: `npx salty-css up`
|
|
33
|
+
## CLI
|
|
36
34
|
|
|
37
|
-
|
|
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. |
|
|
38
42
|
|
|
39
|
-
|
|
40
|
-
2. Salty CSS components created with styled function can extend non Salty CSS components (`export const CustomLink = styled(NextJSLink, { ... });`) but those components must take in `className` prop for styles to apply.
|
|
41
|
-
3. Among common types like `string` and `number`, CSS-in-JS properties in Salty CSS do support `functions` and `promises` as values (`styled('span', { base: { color: async () => 'red' } });`) but running asynchronous tasks or importing heavy 3rd party libraries into `*.css.ts` or `*.css.tsx` files can cause longer build times.
|
|
43
|
+
Full reference: [salty-css.dev/docs/cli](https://salty-css.dev/docs/cli).
|
|
42
44
|
|
|
43
|
-
##
|
|
45
|
+
## Good to know
|
|
44
46
|
|
|
45
|
-
|
|
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.
|
|
46
51
|
|
|
47
|
-
##
|
|
52
|
+
## ESLint
|
|
48
53
|
|
|
49
|
-
|
|
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`):
|
|
50
55
|
|
|
51
|
-
-
|
|
52
|
-
-
|
|
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.
|
|
53
58
|
|
|
54
|
-
|
|
59
|
+
```bash
|
|
60
|
+
npm i -D @salty-css/eslint-plugin-core @salty-css/eslint-config-core
|
|
61
|
+
```
|
|
55
62
|
|
|
56
|
-
|
|
57
|
-
- [defineVariables](#variables) - create CSS variables (tokens) that can be used in any styling function
|
|
58
|
-
- [defineMediaQuery](#media-queries) - create CSS media queries and use them in any styling function
|
|
59
|
-
- [defineTemplates](#templates) - create reusable templates that can be applied when same styles are used over and over again
|
|
60
|
-
- [keyframes](#keyframes-animations) - create CSS keyframes animation that can be used and imported in any styling function
|
|
63
|
+
**Flat config (ESLint 9+):**
|
|
61
64
|
|
|
62
|
-
|
|
65
|
+
```js
|
|
66
|
+
// eslint.config.mjs
|
|
67
|
+
import saltyConfig from '@salty-css/eslint-config-core/flat';
|
|
63
68
|
|
|
64
|
-
|
|
65
|
-
|
|
69
|
+
export default [saltyConfig];
|
|
70
|
+
```
|
|
66
71
|
|
|
67
|
-
|
|
72
|
+
**Legacy (`.eslintrc`):**
|
|
68
73
|
|
|
69
|
-
|
|
74
|
+
```js
|
|
75
|
+
module.exports = { extends: ['@salty-css/eslint-config-core'] };
|
|
76
|
+
```
|
|
70
77
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
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) |
|
|
74
105
|
|
|
75
|
-
|
|
106
|
+
---
|
|
76
107
|
|
|
77
|
-
|
|
108
|
+
## `styled`
|
|
78
109
|
|
|
79
110
|
```ts
|
|
80
|
-
//
|
|
111
|
+
// components/button.css.ts
|
|
81
112
|
import { styled } from '@salty-css/react/styled';
|
|
82
113
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
element: 'section', // Override the html element that will be rendered for this component
|
|
114
|
+
export const Button = styled('button', {
|
|
115
|
+
className: 'btn', // optional custom class
|
|
116
|
+
element: 'button', // optional element override
|
|
87
117
|
base: {
|
|
88
|
-
|
|
118
|
+
padding: '0.6em 1.2em',
|
|
119
|
+
border: '1px solid currentColor',
|
|
120
|
+
'&:hover': { background: 'black', color: 'white' },
|
|
89
121
|
},
|
|
90
122
|
variants: {
|
|
91
|
-
|
|
123
|
+
variant: {
|
|
124
|
+
outlined: {},
|
|
125
|
+
solid: { background: 'black', color: 'white' },
|
|
126
|
+
},
|
|
92
127
|
},
|
|
93
128
|
compoundVariants: [
|
|
94
|
-
|
|
129
|
+
/* { variant: 'solid', size: 'lg', css: { ... } } */
|
|
95
130
|
],
|
|
96
|
-
defaultVariants: {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
// Add additional default props for the component (eg, id and other html element attributes)
|
|
101
|
-
},
|
|
102
|
-
passProps: true, // Pass variant props to the rendered element / parent component (default: false)
|
|
103
|
-
priority: 1, // Override automatic priotity layer with a custom value (0-8), higher is considered more important
|
|
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
|
|
104
135
|
});
|
|
105
136
|
```
|
|
106
137
|
|
|
107
|
-
Example usage:
|
|
108
|
-
|
|
109
138
|
```tsx
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
export const Page = () => {
|
|
113
|
-
return <Component>Hello world</Component>;
|
|
114
|
-
};
|
|
139
|
+
<Button variant="solid">Save</Button>
|
|
115
140
|
```
|
|
116
141
|
|
|
117
|
-
|
|
142
|
+
See full reference → [salty-css.dev/docs/api/styled](https://salty-css.dev/docs/api/styled).
|
|
118
143
|
|
|
119
|
-
|
|
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.
|
|
120
147
|
|
|
121
148
|
```ts
|
|
122
|
-
//
|
|
149
|
+
// components/card.css.ts
|
|
123
150
|
import { className } from '@salty-css/react/class-name';
|
|
124
151
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
className: 'wrapper', // Define optional custom class name that will be included to the scope
|
|
152
|
+
export const card = className({
|
|
153
|
+
className: 'card',
|
|
128
154
|
base: {
|
|
129
|
-
|
|
155
|
+
padding: '1rem',
|
|
156
|
+
borderRadius: '8px',
|
|
157
|
+
},
|
|
158
|
+
variants: {
|
|
159
|
+
tone: {
|
|
160
|
+
neutral: { background: '#f6f6f6' },
|
|
161
|
+
brand: { background: '{colors.brand.main}' },
|
|
162
|
+
},
|
|
130
163
|
},
|
|
131
164
|
});
|
|
132
165
|
```
|
|
133
166
|
|
|
134
|
-
Example usage:
|
|
135
|
-
|
|
136
167
|
```tsx
|
|
137
|
-
|
|
168
|
+
<div className={`${card.variant('tone', 'brand')}`}>Hello</div>
|
|
169
|
+
```
|
|
138
170
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
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
|
+
});
|
|
142
192
|
```
|
|
143
193
|
|
|
144
|
-
|
|
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`
|
|
145
205
|
|
|
146
206
|
```ts
|
|
147
|
-
//
|
|
148
|
-
import {
|
|
207
|
+
// salty.config.ts
|
|
208
|
+
import { defineConfig } from '@salty-css/core/config';
|
|
149
209
|
|
|
150
|
-
export
|
|
151
|
-
|
|
152
|
-
|
|
210
|
+
export const config = defineConfig({
|
|
211
|
+
importStrategy: 'root', // 'root' | 'component'
|
|
212
|
+
variables: {
|
|
213
|
+
/* see defineVariables */
|
|
214
|
+
},
|
|
215
|
+
global: {
|
|
216
|
+
/* see defineGlobalStyles */
|
|
153
217
|
},
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
margin: 0,
|
|
218
|
+
templates: {
|
|
219
|
+
/* see defineTemplates */
|
|
157
220
|
},
|
|
158
|
-
|
|
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, % …
|
|
159
228
|
});
|
|
160
229
|
```
|
|
161
230
|
|
|
162
|
-
|
|
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):
|
|
163
236
|
|
|
164
237
|
```ts
|
|
165
|
-
//
|
|
238
|
+
// styles/variables.css.ts
|
|
166
239
|
import { defineVariables } from '@salty-css/core/factories';
|
|
167
240
|
|
|
168
241
|
export default defineVariables({
|
|
169
|
-
/*
|
|
170
|
-
Define static variable token (like colors, font sizes, etc.). and use them in your styles (e.g. color: '{colors.brand.highlight}').
|
|
171
|
-
Variables can be nested (colors.brand.main) and can reference other variables.
|
|
172
|
-
*/
|
|
173
242
|
colors: {
|
|
174
243
|
dark: '#111',
|
|
175
244
|
light: '#fefefe',
|
|
176
|
-
brand: {
|
|
177
|
-
main: '#0070f3',
|
|
178
|
-
highlight: '#ff4081',
|
|
179
|
-
},
|
|
180
|
-
},
|
|
181
|
-
fontFamily: {
|
|
182
|
-
heading: 'Arial, sans-serif',
|
|
183
|
-
body: 'Georgia, serif',
|
|
245
|
+
brand: { main: '#0070f3', highlight: '#ff4081' },
|
|
184
246
|
},
|
|
185
|
-
|
|
186
|
-
/*
|
|
187
|
-
Define variables that are responsive to a media query (defined in media.css.ts) asn use them in your styles as normal (e.g. font-size: '{fontSize.heading.regular}').
|
|
188
|
-
These variables will be automatically updated when the media query is matched. Base values are used when no media query is matched.
|
|
189
|
-
*/
|
|
190
247
|
responsive: {
|
|
191
|
-
base: {
|
|
192
|
-
|
|
193
|
-
heading: {
|
|
194
|
-
small: '32px',
|
|
195
|
-
regular: '48px',
|
|
196
|
-
large: '64px',
|
|
197
|
-
},
|
|
198
|
-
body: {
|
|
199
|
-
small: '16px',
|
|
200
|
-
regular: '20px',
|
|
201
|
-
large: '24px',
|
|
202
|
-
},
|
|
203
|
-
},
|
|
204
|
-
},
|
|
205
|
-
'@largeMobileDown': {
|
|
206
|
-
fontSize: {
|
|
207
|
-
heading: {
|
|
208
|
-
small: '20px',
|
|
209
|
-
regular: '32px',
|
|
210
|
-
large: '48px',
|
|
211
|
-
},
|
|
212
|
-
body: {
|
|
213
|
-
small: '14px',
|
|
214
|
-
regular: '16px',
|
|
215
|
-
large: '20px',
|
|
216
|
-
},
|
|
217
|
-
},
|
|
218
|
-
},
|
|
248
|
+
base: { fontSize: { heading: '48px', body: '16px' } },
|
|
249
|
+
'@largeMobileDown': { fontSize: { heading: '32px', body: '14px' } },
|
|
219
250
|
},
|
|
220
|
-
|
|
221
|
-
/*
|
|
222
|
-
Conditional variables are used to define styles that depend on a class name (e.g. <div className="theme-dark">). or data-attribute (e.g. <div data-theme="dark">). Names for these variables will be "{theme.backgroundColor}" and "{theme.textColor}".
|
|
223
|
-
*/
|
|
224
251
|
conditional: {
|
|
225
252
|
theme: {
|
|
226
|
-
dark: {
|
|
227
|
-
|
|
228
|
-
textColor: '{colors.light}',
|
|
229
|
-
},
|
|
230
|
-
light: {
|
|
231
|
-
backgroundColor: '{colors.light}',
|
|
232
|
-
textColor: '{colors.dark}',
|
|
233
|
-
},
|
|
253
|
+
dark: { backgroundColor: '{colors.dark}', textColor: '{colors.light}' },
|
|
254
|
+
light: { backgroundColor: '{colors.light}', textColor: '{colors.dark}' },
|
|
234
255
|
},
|
|
235
256
|
},
|
|
236
257
|
});
|
|
237
258
|
```
|
|
238
259
|
|
|
239
|
-
|
|
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`
|
|
240
277
|
|
|
241
278
|
```ts
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
// Use of conditional theme text color variable
|
|
249
|
-
color: '{theme.textColor}',
|
|
250
|
-
},
|
|
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' },
|
|
251
285
|
});
|
|
252
286
|
```
|
|
253
287
|
|
|
254
|
-
##
|
|
255
|
-
|
|
256
|
-
Create global media queries that can be either used directly as a scope (e.g. `'@MEDIA_QUERY_NAME': { color: 'blue' }`) or imported to be used in JS.
|
|
288
|
+
## `defineMediaQuery`
|
|
257
289
|
|
|
258
290
|
```ts
|
|
259
|
-
//
|
|
260
|
-
import { defineMediaQuery } from '@salty-css/
|
|
291
|
+
// styles/media.css.ts
|
|
292
|
+
import { defineMediaQuery } from '@salty-css/core/factories';
|
|
261
293
|
|
|
262
|
-
export const largePortraitUp = defineMediaQuery((media) => media.minWidth(600));
|
|
263
294
|
export const largeMobileDown = defineMediaQuery((media) => media.maxWidth(600));
|
|
295
|
+
export const darkMode = defineMediaQuery((media) => media.prefersColorScheme('dark'));
|
|
264
296
|
```
|
|
265
297
|
|
|
266
|
-
Example usage:
|
|
267
|
-
|
|
268
298
|
```ts
|
|
269
|
-
styled('span', { base: { fontSize:
|
|
299
|
+
styled('span', { base: { fontSize: 64, '@largeMobileDown': { fontSize: 32 } } });
|
|
270
300
|
```
|
|
271
301
|
|
|
272
|
-
|
|
302
|
+
See [salty-css.dev/docs/media-queries](https://salty-css.dev/docs/media-queries).
|
|
273
303
|
|
|
274
|
-
|
|
304
|
+
## `defineTemplates`
|
|
305
|
+
|
|
306
|
+
Reusable style bundles. Static templates pick values by dot-path; function templates take a parameter.
|
|
275
307
|
|
|
276
308
|
```ts
|
|
277
|
-
//
|
|
309
|
+
// styles/templates.css.ts
|
|
278
310
|
import { defineTemplates } from '@salty-css/core/factories';
|
|
279
311
|
|
|
280
312
|
export default defineTemplates({
|
|
281
|
-
// Static templates for text styles.
|
|
282
313
|
textStyle: {
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
regular: {
|
|
288
|
-
fontSize: '{fontSize.heading.regular}',
|
|
289
|
-
},
|
|
290
|
-
large: {
|
|
291
|
-
fontSize: '{fontSize.heading.large}',
|
|
314
|
+
heading: {
|
|
315
|
+
base: { fontFamily: '{fontFamily.heading}', lineHeight: 1.1 },
|
|
316
|
+
variants: {
|
|
317
|
+
weight: { regular: { fontWeight: 500 }, heavy: { fontWeight: 800 } },
|
|
292
318
|
},
|
|
319
|
+
large: { fontSize: '{fontSize.heading.large}' },
|
|
320
|
+
small: { fontSize: '{fontSize.heading.small}' },
|
|
293
321
|
},
|
|
294
|
-
body: {
|
|
295
|
-
small: {
|
|
296
|
-
fontSize: '{fontSize.body.small}',
|
|
297
|
-
lineHeight: '1.5em',
|
|
298
|
-
},
|
|
299
|
-
regular: {
|
|
300
|
-
fontSize: '{fontSize.body.regular}',
|
|
301
|
-
lineHeight: '1.33em',
|
|
302
|
-
},
|
|
303
|
-
},
|
|
304
|
-
},
|
|
305
|
-
// Dynamic function templates for card styles.
|
|
306
|
-
card: (value: string) => {
|
|
307
|
-
return {
|
|
308
|
-
padding: value,
|
|
309
|
-
borderRadius: '8px',
|
|
310
|
-
boxShadow: '0 0 10px rgba(0, 0, 0, 0.1)',
|
|
311
|
-
};
|
|
312
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',
|
|
313
359
|
});
|
|
314
360
|
```
|
|
315
361
|
|
|
316
|
-
|
|
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).
|
|
317
367
|
|
|
318
368
|
```ts
|
|
319
|
-
|
|
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
|
+
);
|
|
320
381
|
```
|
|
321
382
|
|
|
322
|
-
|
|
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`
|
|
323
386
|
|
|
324
387
|
```ts
|
|
325
|
-
//
|
|
388
|
+
// styles/animations.css.ts
|
|
326
389
|
import { keyframes } from '@salty-css/react/keyframes';
|
|
327
390
|
|
|
328
391
|
export const fadeIn = keyframes({
|
|
329
|
-
// Name of the animation in final CSS
|
|
330
392
|
animationName: 'fadeIn',
|
|
331
|
-
//
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
delay: '250ms',
|
|
336
|
-
fillMode: 'forwards',
|
|
337
|
-
},
|
|
338
|
-
// Rest is animation timeline
|
|
339
|
-
from: {
|
|
340
|
-
opacity: 0,
|
|
341
|
-
},
|
|
342
|
-
to: {
|
|
343
|
-
opacity: 1,
|
|
344
|
-
},
|
|
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 },
|
|
345
397
|
});
|
|
346
398
|
```
|
|
347
399
|
|
|
348
|
-
Example usage:
|
|
349
|
-
|
|
350
400
|
```ts
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
export const Wrapper = styled('div', { base: { animation: fadeIn } });
|
|
401
|
+
styled('div', { base: { animation: fadeIn } });
|
|
354
402
|
```
|
|
355
403
|
|
|
356
|
-
|
|
404
|
+
See [salty-css.dev/docs/animations](https://salty-css.dev/docs/animations).
|
|
357
405
|
|
|
358
|
-
|
|
406
|
+
## `defineViewportClamp`
|
|
359
407
|
|
|
360
408
|
```ts
|
|
361
|
-
//
|
|
362
|
-
import { defineViewportClamp } from '@salty-css/
|
|
409
|
+
// styles/clamp.css.ts
|
|
410
|
+
import { defineViewportClamp } from '@salty-css/core/helpers';
|
|
363
411
|
|
|
364
412
|
export const fhdClamp = defineViewportClamp({ screenSize: 1920 });
|
|
365
|
-
export const mobileClamp = defineViewportClamp({ screenSize: 640 });
|
|
413
|
+
export const mobileClamp = defineViewportClamp({ screenSize: 640, axis: 'horizontal' });
|
|
366
414
|
```
|
|
367
415
|
|
|
368
|
-
Example usage:
|
|
369
|
-
|
|
370
416
|
```ts
|
|
371
417
|
styled('span', { base: { fontSize: fhdClamp(96), '@largeMobileDown': { fontSize: mobileClamp(48) } } });
|
|
372
418
|
```
|
|
373
419
|
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
Modify any color easily, add opacity, darken...
|
|
420
|
+
Options: `screenSize`, `axis` (`'horizontal' | 'vertical'`), `minMultiplier`, `maxMultiplier`, `minMaxUnit`. See [salty-css.dev/docs/viewport-clamp](https://salty-css.dev/docs/viewport-clamp).
|
|
377
421
|
|
|
378
|
-
|
|
422
|
+
## `color`
|
|
379
423
|
|
|
380
424
|
```ts
|
|
381
425
|
import { color } from '@salty-css/core/helpers';
|
|
426
|
+
import { styled } from '@salty-css/react/styled';
|
|
382
427
|
|
|
383
|
-
export const
|
|
428
|
+
export const Tint = styled('span', {
|
|
429
|
+
base: { backgroundColor: color('#000').alpha(0.5) },
|
|
430
|
+
});
|
|
384
431
|
```
|
|
385
432
|
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
### Next.js
|
|
389
|
-
|
|
390
|
-

|
|
391
|
-
|
|
392
|
-
Salty CSS provides Next.js App & Pages router support with full React Server Components support.
|
|
393
|
-
|
|
394
|
-
### Add Salty CSS to Next.js
|
|
395
|
-
|
|
396
|
-
1. In your existing Next.js repository you can run `npx salty-css init` to automatically configure Salty CSS.
|
|
397
|
-
2. Create your first Salty CSS component with `npx salty-css generate [filePath]` (e.g. src/custom-wrapper)
|
|
398
|
-
3. Import your component for example to `page.tsx` and see it working!
|
|
399
|
-
|
|
400
|
-
And note: steps 2 & 3 are just to show how get new components up and running, step 1 does all of the important stuff 🤯
|
|
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).
|
|
401
434
|
|
|
402
|
-
|
|
435
|
+
## Modifiers
|
|
403
436
|
|
|
404
|
-
|
|
405
|
-
2. Create `salty.config.ts` to your app directory
|
|
406
|
-
3. Add Salty CSS plugin to next.js config
|
|
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`).
|
|
407
438
|
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
4. Make sure that `salty.config.ts` and `next.config.ts` are in the same folder!
|
|
412
|
-
5. Build `saltygen` directory by running your app once or with cli `npx salty-css build [directory]`
|
|
413
|
-
6. Import global styles from `saltygen/index.css` to some global css file with `@import 'insert_path_to_index_css';`.
|
|
414
|
-
|
|
415
|
-
[Check out Next.js demo project](https://github.com/margarita-form/salty-css-website) or [react example code](#code-examples)
|
|
416
|
-
|
|
417
|
-
---
|
|
418
|
-
|
|
419
|
-
### React + Vite
|
|
420
|
-
|
|
421
|
-

|
|
422
|
-
|
|
423
|
-
### Add Salty CSS to your React + Vite app
|
|
424
|
-
|
|
425
|
-
1. In your existing Vite repository you can run `npx salty-css init` to automatically configure Salty CSS.
|
|
426
|
-
2. Create your first Salty CSS component with `npx salty-css generate [filePath]` (e.g. src/custom-wrapper)
|
|
427
|
-
3. Import your component for example to `main.tsx` and see it working!
|
|
428
|
-
|
|
429
|
-
And note: steps 2 & 3 are just to show how get new components up and running, step 1 does all of the important stuff 🤯
|
|
430
|
-
|
|
431
|
-
### Test it out
|
|
439
|
+
```ts
|
|
440
|
+
// salty.config.ts
|
|
441
|
+
import { defineConfig } from '@salty-css/core/config';
|
|
432
442
|
|
|
433
|
-
|
|
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
|
+
```
|
|
434
455
|
|
|
435
|
-
|
|
456
|
+
```ts
|
|
457
|
+
styled('div', { base: { padding: 'space:3' } }); // → padding: 12px
|
|
458
|
+
```
|
|
436
459
|
|
|
437
|
-
|
|
460
|
+
See [salty-css.dev/docs/modifiers](https://salty-css.dev/docs/modifiers).
|
|
438
461
|
|
|
439
|
-
|
|
440
|
-
2. In `vite.config` add import for salty plugin `import { saltyPlugin } from '@salty-css/vite';` and then add `saltyPlugin(__dirname)` to your vite configuration plugins
|
|
441
|
-
3. Make sure that `salty.config.ts` and `vite.config.ts` are in the same folder!
|
|
442
|
-
4. Build `saltygen` directory by running your app once or with cli `npx salty-css build [directory]`
|
|
443
|
-
5. Import global styles from `saltygen/index.css` to some global css file with `@import 'insert_path_to_index_css';`.
|
|
462
|
+
## `withSaltyCss` (Next.js)
|
|
444
463
|
|
|
445
|
-
|
|
464
|
+
```ts
|
|
465
|
+
// next.config.ts
|
|
466
|
+
import { withSaltyCss } from '@salty-css/next';
|
|
446
467
|
|
|
447
|
-
|
|
468
|
+
const nextConfig = {
|
|
469
|
+
/* your config */
|
|
470
|
+
};
|
|
471
|
+
export default withSaltyCss(nextConfig);
|
|
472
|
+
// Or pin a bundler: withSaltyCss(nextConfig, { bundler: 'webpack' })
|
|
473
|
+
```
|
|
448
474
|
|
|
449
|
-
|
|
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()`).
|
|
450
476
|
|
|
451
|
-
|
|
477
|
+
Next.js 14 CommonJS:
|
|
452
478
|
|
|
453
|
-
|
|
479
|
+
```js
|
|
480
|
+
const { withSaltyCss } = require('@salty-css/next');
|
|
481
|
+
module.exports = withSaltyCss(nextConfig);
|
|
482
|
+
```
|
|
454
483
|
|
|
455
|
-
|
|
484
|
+
See [salty-css.dev/docs/installation](https://salty-css.dev/docs/installation).
|
|
456
485
|
|
|
457
|
-
|
|
486
|
+
## `saltyPlugin` (Vite)
|
|
458
487
|
|
|
459
|
-
```
|
|
460
|
-
|
|
488
|
+
```ts
|
|
489
|
+
// vite.config.ts
|
|
490
|
+
import { defineConfig } from 'vite';
|
|
491
|
+
import { saltyPlugin } from '@salty-css/vite';
|
|
461
492
|
|
|
462
|
-
export
|
|
463
|
-
|
|
464
|
-
colors: {
|
|
465
|
-
brand: '#111',
|
|
466
|
-
highlight: 'yellow',
|
|
467
|
-
},
|
|
468
|
-
},
|
|
469
|
-
global: {
|
|
470
|
-
html: {
|
|
471
|
-
backgroundColor: '#f8f8f8',
|
|
472
|
-
},
|
|
473
|
-
},
|
|
493
|
+
export default defineConfig({
|
|
494
|
+
plugins: [saltyPlugin(__dirname)],
|
|
474
495
|
});
|
|
475
496
|
```
|
|
476
497
|
|
|
477
|
-
|
|
498
|
+
Options: `{ mode }`.
|
|
478
499
|
|
|
479
|
-
|
|
480
|
-
import { styled } from '@salty-css/react/styled';
|
|
500
|
+
## `saltyPlugin` (Webpack)
|
|
481
501
|
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
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
|
+
};
|
|
488
513
|
```
|
|
489
514
|
|
|
490
|
-
|
|
515
|
+
Signature: `saltyPlugin(config, dir, isServer?, cjs?, { mode? })`.
|
|
491
516
|
|
|
492
|
-
|
|
493
|
-
import { styled } from '@salty-css/react/styled';
|
|
517
|
+
## `saltyIntegration` (Astro)
|
|
494
518
|
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
cursor: 'pointer',
|
|
503
|
-
transition: '200ms',
|
|
504
|
-
textDecoration: 'none',
|
|
505
|
-
'&:hover': {
|
|
506
|
-
background: 'black',
|
|
507
|
-
borderColor: 'black',
|
|
508
|
-
color: 'white',
|
|
509
|
-
},
|
|
510
|
-
'&:disabled': {
|
|
511
|
-
opacity: 0.25,
|
|
512
|
-
pointerEvents: 'none',
|
|
513
|
-
},
|
|
514
|
-
},
|
|
515
|
-
variants: {
|
|
516
|
-
variant: {
|
|
517
|
-
outlined: {
|
|
518
|
-
// same as default styles
|
|
519
|
-
},
|
|
520
|
-
solid: {
|
|
521
|
-
'&:not(:hover)': {
|
|
522
|
-
background: 'black',
|
|
523
|
-
borderColor: 'black',
|
|
524
|
-
color: 'white',
|
|
525
|
-
},
|
|
526
|
-
'&:hover': {
|
|
527
|
-
background: 'transparent',
|
|
528
|
-
borderColor: 'currentColor',
|
|
529
|
-
color: 'currentColor',
|
|
530
|
-
},
|
|
531
|
-
},
|
|
532
|
-
},
|
|
533
|
-
},
|
|
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()],
|
|
534
526
|
});
|
|
535
527
|
```
|
|
536
528
|
|
|
537
|
-
|
|
529
|
+
Options: `srcDir` (defaults to `'src'`), `rootDir` (defaults to the Astro config root).
|
|
538
530
|
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
export const IndexPage = () => {
|
|
544
|
-
return (
|
|
545
|
-
<Wrapper>
|
|
546
|
-
<Button variant="solid" onClick={() => alert('It is a button.')}>
|
|
547
|
-
Outlined
|
|
548
|
-
</Button>
|
|
549
|
-
</Wrapper>
|
|
550
|
-
);
|
|
551
|
-
};
|
|
552
|
-
```
|
|
531
|
+
---
|
|
532
|
+
|
|
533
|
+
## Support
|
|
553
534
|
|
|
554
|
-
|
|
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).
|
package/index.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";const m=require("@salty-css/core/compiler/helpers"),
|
|
1
|
+
"use strict";const m=require("@salty-css/core/compiler/helpers"),I={meta:{type:"problem",fixable:"code",docs:{description:"Ensure `variants` are not nested inside `base` in styled calls",category:"Errors",recommended:!0},messages:{nestedVariants:"`variants` should not be nested inside `base`."}},create(n){return m.isSaltyFile(n.filename)?{CallExpression:o=>{try{const{callee:a,arguments:l}=o;if(!(a.type==="Identifier"&&a.name==="styled"))return;const t=l[1];if(!t||!(t.type==="ObjectExpression"))return;const i=t.properties.find(r=>r.type==="Property"&&r.key.type==="Identifier"&&r.key.name==="base");if(!i)return;const{value:u}=i;if(!(u.type==="ObjectExpression"))return;const s=u.properties.find(r=>r.type==="Property"&&r.key.type==="Identifier"&&r.key.name==="variants");if(!s)return;n.report({node:s,messageId:"nestedVariants",fix:r=>{if(!s.range)return null;const{sourceCode:y}=n,x=y.getText(s),b=t.properties.indexOf(i),f=t.properties[b];if(!f.range)return null;const S=[f.range[1],f.range[1]],p=[s.range[0]-1,s.range[1]];if(u.properties.at(-1)===s){const d=y.text.slice(s.range[1]).match(/^\s*,/);d&&(p[1]+=d[0].length)}return[r.removeRange(p),r.insertTextAfterRange(S,`, ${x}`)]}})}catch(a){console.log("ESlint error",a);return}}}:{}}},h={meta:{type:"problem",fixable:"code",docs:{description:"Salty CSS related function calls or other values must be exported",category:"Errors",recommended:!0},messages:{mustBeExported:"Salty CSS function calls (styled, className, keyframes, defineX) must be exported — the compiler only picks up exports from .css.ts / .salty.ts / .styles.ts / .styled.ts files."}},create(n){if(!m.isSaltyFile(n.filename))return{};function o(e){if(!e)return!1;if((e==null?void 0:e.type)!=="CallExpression")return;const t=["styled","keyframes","className"];if(e.callee.type==="Identifier"&&t.includes(e.callee.name))return!0;const c=/^define[A-Z]/;return!!(e.callee.type==="Identifier"&&c.test(e.callee.name))}function a(e){if(e.type==="VariableDeclaration"){const t=e.declarations[0];return t.type!=="VariableDeclarator"?void 0:o(t.init)}return!1}function l(e){const t=e.parent;return t.type==="ExportNamedDeclaration"||t.type==="ExportDefaultDeclaration"}return{VariableDeclaration:e=>{a(e)&&(l(e)||n.report({node:e,messageId:"mustBeExported",fix:t=>t.insertTextBefore(e,"export ")}))},ExpressionStatement(e){e.type!=="ExpressionStatement"||!o(e.expression)||!(e.parent.type==="Program")||n.report({node:e,messageId:"mustBeExported",fix:i=>i.insertTextBefore(e,"export default ")})}}}},v={rules:{"no-variants-in-base":I,"must-be-exported":h}};module.exports=v;
|
package/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { isSaltyFile as d } from "@salty-css/core/compiler/helpers";
|
|
2
|
-
const
|
|
2
|
+
const I = {
|
|
3
3
|
meta: {
|
|
4
4
|
type: "problem",
|
|
5
5
|
fixable: "code",
|
|
@@ -16,45 +16,45 @@ const v = {
|
|
|
16
16
|
return d(a.filename) ? {
|
|
17
17
|
CallExpression: (o) => {
|
|
18
18
|
try {
|
|
19
|
-
const { callee:
|
|
20
|
-
if (!(
|
|
19
|
+
const { callee: n, arguments: l } = o;
|
|
20
|
+
if (!(n.type === "Identifier" && n.name === "styled")) return;
|
|
21
21
|
const t = l[1];
|
|
22
22
|
if (!t || !(t.type === "ObjectExpression")) return;
|
|
23
23
|
const i = t.properties.find((r) => r.type === "Property" && r.key.type === "Identifier" && r.key.name === "base");
|
|
24
24
|
if (!i) return;
|
|
25
25
|
const { value: u } = i;
|
|
26
26
|
if (!(u.type === "ObjectExpression")) return;
|
|
27
|
-
const
|
|
27
|
+
const s = u.properties.find(
|
|
28
28
|
(r) => r.type === "Property" && r.key.type === "Identifier" && r.key.name === "variants"
|
|
29
29
|
);
|
|
30
|
-
if (!
|
|
30
|
+
if (!s) return;
|
|
31
31
|
a.report({
|
|
32
|
-
node:
|
|
32
|
+
node: s,
|
|
33
33
|
messageId: "nestedVariants",
|
|
34
34
|
fix: (r) => {
|
|
35
|
-
if (!
|
|
36
|
-
const { sourceCode:
|
|
35
|
+
if (!s.range) return null;
|
|
36
|
+
const { sourceCode: y } = a, x = y.getText(s), b = t.properties.indexOf(i), f = t.properties[b];
|
|
37
37
|
if (!f.range) return null;
|
|
38
|
-
const
|
|
39
|
-
|
|
38
|
+
const S = [f.range[1], f.range[1]], p = [
|
|
39
|
+
s.range[0] - 1,
|
|
40
40
|
// Include the preceding comma (if any)
|
|
41
|
-
|
|
41
|
+
s.range[1]
|
|
42
42
|
];
|
|
43
|
-
if (u.properties.at(-1) ===
|
|
44
|
-
const m =
|
|
45
|
-
m && (
|
|
43
|
+
if (u.properties.at(-1) === s) {
|
|
44
|
+
const m = y.text.slice(s.range[1]).match(/^\s*,/);
|
|
45
|
+
m && (p[1] += m[0].length);
|
|
46
46
|
}
|
|
47
|
-
return [r.removeRange(
|
|
47
|
+
return [r.removeRange(p), r.insertTextAfterRange(S, `, ${x}`)];
|
|
48
48
|
}
|
|
49
49
|
});
|
|
50
|
-
} catch (
|
|
51
|
-
console.log("ESlint error",
|
|
50
|
+
} catch (n) {
|
|
51
|
+
console.log("ESlint error", n);
|
|
52
52
|
return;
|
|
53
53
|
}
|
|
54
54
|
}
|
|
55
55
|
} : {};
|
|
56
56
|
}
|
|
57
|
-
},
|
|
57
|
+
}, v = {
|
|
58
58
|
meta: {
|
|
59
59
|
type: "problem",
|
|
60
60
|
fixable: "code",
|
|
@@ -64,7 +64,7 @@ const v = {
|
|
|
64
64
|
recommended: !0
|
|
65
65
|
},
|
|
66
66
|
messages: {
|
|
67
|
-
mustBeExported: "
|
|
67
|
+
mustBeExported: "Salty CSS function calls (styled, className, keyframes, defineX) must be exported — the compiler only picks up exports from .css.ts / .salty.ts / .styles.ts / .styled.ts files."
|
|
68
68
|
}
|
|
69
69
|
},
|
|
70
70
|
create(a) {
|
|
@@ -77,7 +77,7 @@ const v = {
|
|
|
77
77
|
const c = /^define[A-Z]/;
|
|
78
78
|
return !!(e.callee.type === "Identifier" && c.test(e.callee.name));
|
|
79
79
|
}
|
|
80
|
-
function
|
|
80
|
+
function n(e) {
|
|
81
81
|
if (e.type === "VariableDeclaration") {
|
|
82
82
|
const t = e.declarations[0];
|
|
83
83
|
return t.type !== "VariableDeclarator" ? void 0 : o(t.init);
|
|
@@ -90,7 +90,7 @@ const v = {
|
|
|
90
90
|
}
|
|
91
91
|
return {
|
|
92
92
|
VariableDeclaration: (e) => {
|
|
93
|
-
|
|
93
|
+
n(e) && (l(e) || a.report({
|
|
94
94
|
node: e,
|
|
95
95
|
messageId: "mustBeExported",
|
|
96
96
|
fix: (t) => t.insertTextBefore(e, "export ")
|
|
@@ -105,12 +105,12 @@ const v = {
|
|
|
105
105
|
}
|
|
106
106
|
};
|
|
107
107
|
}
|
|
108
|
-
},
|
|
108
|
+
}, k = {
|
|
109
109
|
rules: {
|
|
110
|
-
"no-variants-in-base":
|
|
111
|
-
"must-be-exported":
|
|
110
|
+
"no-variants-in-base": I,
|
|
111
|
+
"must-be-exported": v
|
|
112
112
|
}
|
|
113
113
|
};
|
|
114
114
|
export {
|
|
115
|
-
|
|
115
|
+
k as default
|
|
116
116
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@salty-css/eslint-plugin-core",
|
|
3
|
-
"version": "0.1.0
|
|
3
|
+
"version": "0.1.0",
|
|
4
4
|
"main": "./dist/index.js",
|
|
5
5
|
"module": "./dist/index.mjs",
|
|
6
6
|
"typings": "./dist/index.d.ts",
|
|
@@ -34,7 +34,8 @@
|
|
|
34
34
|
}
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@salty-css/core": "
|
|
37
|
+
"@salty-css/core": "0.1.0",
|
|
38
|
+
"@types/estree": "1.0.8",
|
|
38
39
|
"eslint": ">=9.x || >=8.x || >=7.x"
|
|
39
40
|
}
|
|
40
41
|
}
|