@flairjs/client 0.0.1-beta.6 → 0.0.1
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 +341 -0
- package/package.json +2 -2
package/README.md
ADDED
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
# @flairjs/client
|
|
2
|
+
|
|
3
|
+
Client-side utilities and types for Flair CSS-in-JSX.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @flairjs/client
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Exports
|
|
12
|
+
|
|
13
|
+
### Styling Functions
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
import { flair, css, c, cn } from '@flairjs/client'
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
#### `flair(styles)`
|
|
20
|
+
|
|
21
|
+
Create component styles using CSS-in-JS object syntax:
|
|
22
|
+
|
|
23
|
+
```jsx
|
|
24
|
+
import { flair } from '@flairjs/client'
|
|
25
|
+
|
|
26
|
+
const Button = () => <button className="button">Click me</button>
|
|
27
|
+
|
|
28
|
+
Button.flair = flair({
|
|
29
|
+
".button": {
|
|
30
|
+
backgroundColor: 'blue',
|
|
31
|
+
color: 'white',
|
|
32
|
+
padding: '12px 24px',
|
|
33
|
+
'&:hover': {
|
|
34
|
+
backgroundColor: 'darkblue'
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
})
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
#### `css` Template Literal
|
|
41
|
+
|
|
42
|
+
Write CSS using template literal syntax:
|
|
43
|
+
|
|
44
|
+
```jsx
|
|
45
|
+
import { css } from '@flairjs/client'
|
|
46
|
+
|
|
47
|
+
const Button = () => <button className="button">Click me</button>
|
|
48
|
+
|
|
49
|
+
Button.flair = css`
|
|
50
|
+
.button {
|
|
51
|
+
background-color: blue;
|
|
52
|
+
color: white;
|
|
53
|
+
padding: 12px 24px;
|
|
54
|
+
|
|
55
|
+
&:hover {
|
|
56
|
+
background-color: darkblue;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
`
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
#### `c()` and `cn()` Class Name Utilities
|
|
63
|
+
|
|
64
|
+
Simple pass-through functions that help Flair's static analyzer find class names in your code.
|
|
65
|
+
|
|
66
|
+
**Important**: `c()` and `cn()` are **not** like `clsx` or `classnames`. They don't merge or conditionally apply classes - they simply return whatever you pass to them.
|
|
67
|
+
|
|
68
|
+
```jsx
|
|
69
|
+
import { c, cn } from '@flairjs/client'
|
|
70
|
+
|
|
71
|
+
// Both c() and cn() are identical - they just return their input
|
|
72
|
+
// Their purpose is to signal to Flair's build-time analyzer which class names to include
|
|
73
|
+
|
|
74
|
+
function getButtonClass() {
|
|
75
|
+
// ✅ Signal to Flair that 'btn' and 'btn-primary' should be included
|
|
76
|
+
return c('btn btn-primary')
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const Button = () => {
|
|
80
|
+
return <button className={getButtonClass()}>Click me</button>
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
Button.flair = flair({
|
|
84
|
+
'.btn': { padding: '12px 24px' },
|
|
85
|
+
'.btn-primary': { backgroundColor: 'blue' }
|
|
86
|
+
})
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
**Why use them?** Since Flair is a build-time library, it needs to statically analyze your code to find class names. When you return a class name from a function like `<button className={someFunction()}>`, Flair can't infer what class name will be used. Wrapping the class name in `c()` or `cn()` inside the function signals to Flair which classes to include.
|
|
90
|
+
|
|
91
|
+
**Runtime behavior**: Zero overhead - they literally just return their input:
|
|
92
|
+
```js
|
|
93
|
+
c('foo') === 'foo' // true
|
|
94
|
+
cn('bar') === 'bar' // true
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### Theme System
|
|
98
|
+
|
|
99
|
+
```typescript
|
|
100
|
+
import { defineConfig } from '@flairjs/client'
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
**Setup**: To enable theming, import the theme CSS in your top-level file (e.g., `main.tsx`, `App.tsx`, or `index.tsx`):
|
|
104
|
+
|
|
105
|
+
```jsx
|
|
106
|
+
import '@flairjs/client/theme.css'
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
#### `defineConfig(config)`
|
|
110
|
+
|
|
111
|
+
Define your project's theme configuration:
|
|
112
|
+
|
|
113
|
+
```typescript
|
|
114
|
+
// flair.theme.ts
|
|
115
|
+
import { defineConfig } from '@flairjs/client'
|
|
116
|
+
|
|
117
|
+
const theme = defineConfig({
|
|
118
|
+
prefix: 'my-app',
|
|
119
|
+
selector: 'body',
|
|
120
|
+
tokens: {
|
|
121
|
+
colors: {
|
|
122
|
+
primary: '#3b82f6',
|
|
123
|
+
secondary: '#64748b'
|
|
124
|
+
},
|
|
125
|
+
fonts: {
|
|
126
|
+
family: "'Inter', sans-serif",
|
|
127
|
+
size: {
|
|
128
|
+
sm: '14px',
|
|
129
|
+
md: '16px',
|
|
130
|
+
lg: '18px'
|
|
131
|
+
}
|
|
132
|
+
},
|
|
133
|
+
space: {
|
|
134
|
+
1: '4px',
|
|
135
|
+
2: '8px',
|
|
136
|
+
3: '12px',
|
|
137
|
+
4: '16px'
|
|
138
|
+
}
|
|
139
|
+
},
|
|
140
|
+
breakpoints: {
|
|
141
|
+
sm: '640px',
|
|
142
|
+
md: '768px',
|
|
143
|
+
lg: '1024px'
|
|
144
|
+
}
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
export default theme
|
|
148
|
+
export type Theme = typeof theme
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### Component Libraries
|
|
152
|
+
|
|
153
|
+
#### React
|
|
154
|
+
|
|
155
|
+
```jsx
|
|
156
|
+
import { Style } from '@flairjs/client/react'
|
|
157
|
+
|
|
158
|
+
const MyComponent = () => {
|
|
159
|
+
return (
|
|
160
|
+
<>
|
|
161
|
+
<div className="my-class">Styled content</div>
|
|
162
|
+
<Style>{`
|
|
163
|
+
.my-class {
|
|
164
|
+
color: red;
|
|
165
|
+
}
|
|
166
|
+
`}</Style>
|
|
167
|
+
</>
|
|
168
|
+
)
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Global styles
|
|
172
|
+
const App = () => {
|
|
173
|
+
return (
|
|
174
|
+
<>
|
|
175
|
+
<Style global>{`
|
|
176
|
+
body {
|
|
177
|
+
margin: 0;
|
|
178
|
+
font-family: sans-serif;
|
|
179
|
+
}
|
|
180
|
+
`}</Style>
|
|
181
|
+
{/* App content */}
|
|
182
|
+
</>
|
|
183
|
+
)
|
|
184
|
+
}
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
#### Preact
|
|
188
|
+
|
|
189
|
+
```jsx
|
|
190
|
+
import { Style } from '@flairjs/client/preact'
|
|
191
|
+
|
|
192
|
+
// Usage is identical to React
|
|
193
|
+
const MyComponent = () => {
|
|
194
|
+
return (
|
|
195
|
+
<>
|
|
196
|
+
<div className="my-class">Styled content</div>
|
|
197
|
+
<Style>{`
|
|
198
|
+
.my-class { color: red; }
|
|
199
|
+
`}</Style>
|
|
200
|
+
</>
|
|
201
|
+
)
|
|
202
|
+
}
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
#### SolidJS
|
|
206
|
+
|
|
207
|
+
```jsx
|
|
208
|
+
import { Style } from '@flairjs/client/solidjs'
|
|
209
|
+
|
|
210
|
+
const MyComponent = () => {
|
|
211
|
+
return (
|
|
212
|
+
<>
|
|
213
|
+
<div class="my-class">Styled content</div>
|
|
214
|
+
<Style>{`
|
|
215
|
+
.my-class { color: red; }
|
|
216
|
+
`}</Style>
|
|
217
|
+
</>
|
|
218
|
+
)
|
|
219
|
+
}
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
## TypeScript Support
|
|
223
|
+
|
|
224
|
+
### Theme Intellisense
|
|
225
|
+
|
|
226
|
+
Extend the `FlairTheme` interface for theme token autocomplete:
|
|
227
|
+
|
|
228
|
+
```typescript
|
|
229
|
+
// types/flair.d.ts
|
|
230
|
+
import { Theme } from '../flair.theme'
|
|
231
|
+
|
|
232
|
+
declare module '@flairjs/client' {
|
|
233
|
+
export interface FlairTheme extends Theme {}
|
|
234
|
+
}
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
After this setup, you'll get autocomplete for theme tokens:
|
|
238
|
+
|
|
239
|
+
```jsx
|
|
240
|
+
Button.flair = flair({
|
|
241
|
+
".selector": {
|
|
242
|
+
backgroundColor: '$colors.primary', // ← Autocomplete available
|
|
243
|
+
padding: '$space.3 $space.4', // ← Autocomplete available
|
|
244
|
+
fontSize: '$fonts.size.md' // ← Autocomplete available
|
|
245
|
+
}
|
|
246
|
+
})
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
### Type Definitions
|
|
250
|
+
|
|
251
|
+
```typescript
|
|
252
|
+
// Theme token types
|
|
253
|
+
type ThemeTokens<T extends FlairTheme = FlairTheme> =
|
|
254
|
+
T extends { tokens: any } ? TokensOf<T['tokens']> : never
|
|
255
|
+
|
|
256
|
+
type BreakPointTokens<T extends FlairTheme = FlairTheme> =
|
|
257
|
+
T extends { breakpoints: any } ? `$screen ${keyof T['breakpoints']}` : never
|
|
258
|
+
|
|
259
|
+
// CSS object with theme support
|
|
260
|
+
type FlairCSS = {
|
|
261
|
+
[K in string]?: FlairObject | FlairCSS
|
|
262
|
+
} & {
|
|
263
|
+
[K in BreakPointTokens]?: FlairObject | FlairCSS
|
|
264
|
+
}
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
## Usage Patterns
|
|
268
|
+
|
|
269
|
+
### Responsive Design
|
|
270
|
+
|
|
271
|
+
```jsx
|
|
272
|
+
const Card = () => <div className="card">Content</div>
|
|
273
|
+
|
|
274
|
+
Card.flair = flair({
|
|
275
|
+
'.card': {
|
|
276
|
+
padding: '$space.2',
|
|
277
|
+
fontSize: '$fonts.size.sm',
|
|
278
|
+
|
|
279
|
+
// Responsive breakpoints
|
|
280
|
+
'$screen md': {
|
|
281
|
+
padding: '$space.4',
|
|
282
|
+
fontSize: '$fonts.size.md'
|
|
283
|
+
},
|
|
284
|
+
|
|
285
|
+
'$screen lg': {
|
|
286
|
+
padding: '$space.6',
|
|
287
|
+
fontSize: '$fonts.size.lg'
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
})
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
### Nested Selectors
|
|
294
|
+
|
|
295
|
+
```jsx
|
|
296
|
+
Card.flair = flair({
|
|
297
|
+
'.card': {
|
|
298
|
+
backgroundColor: 'white',
|
|
299
|
+
|
|
300
|
+
'&:hover': {
|
|
301
|
+
backgroundColor: '#f9f9f9'
|
|
302
|
+
},
|
|
303
|
+
|
|
304
|
+
'&.active': {
|
|
305
|
+
borderColor: '$colors.primary'
|
|
306
|
+
},
|
|
307
|
+
|
|
308
|
+
'& .title': {
|
|
309
|
+
fontSize: '$fonts.size.lg',
|
|
310
|
+
fontWeight: 'bold'
|
|
311
|
+
},
|
|
312
|
+
|
|
313
|
+
'& > .content': {
|
|
314
|
+
padding: '$space.4'
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
})
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
### Global Styles
|
|
321
|
+
|
|
322
|
+
```jsx
|
|
323
|
+
// Using globalFlair property
|
|
324
|
+
const App = () => <div>App content</div>
|
|
325
|
+
|
|
326
|
+
App.globalFlair = css`
|
|
327
|
+
* {
|
|
328
|
+
box-sizing: border-box;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
body {
|
|
332
|
+
margin: 0;
|
|
333
|
+
font-family: '$fonts.family';
|
|
334
|
+
background-color: '$colors.background';
|
|
335
|
+
}
|
|
336
|
+
`
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
## License
|
|
340
|
+
|
|
341
|
+
MIT
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flairjs/client",
|
|
3
|
-
"version": "0.0.1
|
|
3
|
+
"version": "0.0.1",
|
|
4
4
|
"module": "./dist/esm/index.js",
|
|
5
5
|
"main": "./dist/cjs/index.js",
|
|
6
6
|
"types": "./dist/types/index.d.ts",
|
|
@@ -67,7 +67,7 @@
|
|
|
67
67
|
"csstype": "^3.1.3",
|
|
68
68
|
"rolldown": "1.0.0-beta.37",
|
|
69
69
|
"typescript": "^5.8.3",
|
|
70
|
-
"@flairjs/core": "0.0.1
|
|
70
|
+
"@flairjs/core": "0.0.1"
|
|
71
71
|
},
|
|
72
72
|
"scripts": {
|
|
73
73
|
"build": "rolldown -c",
|