@smicolon/fasla-ui 0.2.4 → 0.3.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 +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +91 -23
- package/dist/registry/index.js +31 -2
- package/dist/tokens/index.d.ts +75 -6
- package/dist/tokens/index.js +59 -20
- package/package.json +4 -1
- package/registry/blocks/app-shell/AppShell.tsx +1 -1
- package/registry/blocks/data-table/DataTable.test.tsx +61 -0
- package/registry/blocks/data-table/DataTable.tsx +53 -16
- package/registry/blocks/data-table/index.ts +1 -0
- package/registry/blocks/empty-state/EmptyState.test.tsx +12 -0
- package/registry/blocks/empty-state/EmptyState.tsx +22 -31
- package/registry/blocks/form-section/FormSection.tsx +1 -1
- package/registry/blocks/navbar/Navbar.test.tsx +12 -0
- package/registry/blocks/navbar/Navbar.tsx +8 -23
- package/registry/blocks/page-header/PageHeader.tsx +5 -2
- package/registry/blocks/sidebar/Sidebar.tsx +9 -16
- package/registry/blocks/stats-card/StatsCard.tsx +9 -27
- package/registry/effects/animated-gradient/animated-gradient.tsx +23 -8
- package/registry/effects/border-beam/border-beam.tsx +77 -53
- package/registry/effects/effects.test.tsx +266 -0
- package/registry/effects/glow-card/glow-card.tsx +68 -43
- package/registry/effects/shimmer-button/shimmer-button.tsx +62 -23
- package/registry/effects/spotlight/spotlight.tsx +73 -29
- package/registry/effects/text-reveal/index.ts +1 -0
- package/registry/effects/text-reveal/text-reveal.test.tsx +66 -0
- package/registry/effects/text-reveal/text-reveal.tsx +68 -21
- package/registry/effects/typewriter-text/typewriter-text.tsx +39 -20
- package/registry/text-props-undefined.test.tsx +73 -0
- package/registry/ui/avatar/avatar.test.tsx +316 -0
- package/registry/ui/avatar/avatar.tsx +235 -108
- package/registry/ui/avatar/index.ts +1 -1
- package/registry/ui/badge/badge.test.tsx +267 -32
- package/registry/ui/badge/badge.tsx +233 -21
- package/registry/ui/button/button.test.tsx +6 -0
- package/registry/ui/button/button.tsx +6 -23
- package/registry/ui/card/card.tsx +1 -1
- package/registry/ui/checkbox/checkbox.tsx +13 -26
- package/registry/ui/combobox/combobox.test.tsx +43 -0
- package/registry/ui/combobox/combobox.tsx +27 -55
- package/registry/ui/input/input.test.tsx +2 -2
- package/registry/ui/input/input.tsx +4 -4
- package/registry/ui/radio/index.ts +1 -0
- package/registry/ui/radio/radio.test.tsx +248 -0
- package/registry/ui/radio/radio.tsx +208 -0
- package/registry/ui/select/select.tsx +4 -14
- package/registry/ui/skeleton/skeleton.tsx +1 -1
- package/registry/ui/status-indicator/index.ts +6 -0
- package/registry/ui/status-indicator/status-indicator.test.tsx +148 -0
- package/registry/ui/status-indicator/status-indicator.tsx +87 -0
- package/registry/ui/switch/index.ts +1 -1
- package/registry/ui/switch/switch.test.tsx +323 -0
- package/registry/ui/switch/switch.tsx +254 -57
- package/registry/ui/textarea/textarea.tsx +1 -1
- package/registry.json +78 -15
- package/tailwind-preset.ts +143 -0
package/registry.json
CHANGED
|
@@ -47,11 +47,14 @@
|
|
|
47
47
|
"name": "avatar",
|
|
48
48
|
"type": "registry:ui",
|
|
49
49
|
"title": "Avatar",
|
|
50
|
-
"description": "
|
|
50
|
+
"description": "A user's photo, initials or icon, in three sizes and two radii, with an optional border and presence dot. Falls back from photo to initials to icon on its own.",
|
|
51
51
|
"dependencies": [
|
|
52
|
-
"class-variance-authority"
|
|
52
|
+
"class-variance-authority",
|
|
53
|
+
"lucide-react"
|
|
54
|
+
],
|
|
55
|
+
"registryDependencies": [
|
|
56
|
+
"status-indicator"
|
|
53
57
|
],
|
|
54
|
-
"registryDependencies": [],
|
|
55
58
|
"files": [
|
|
56
59
|
{
|
|
57
60
|
"path": "registry/ui/avatar/avatar.tsx",
|
|
@@ -68,9 +71,10 @@
|
|
|
68
71
|
"name": "badge",
|
|
69
72
|
"type": "registry:ui",
|
|
70
73
|
"title": "Badge",
|
|
71
|
-
"description": "A
|
|
74
|
+
"description": "A compact inline label for status, category or metadata: solid, soft and outline types in six semantic tones, with an optional icon, avatar and close button.",
|
|
72
75
|
"dependencies": [
|
|
73
|
-
"class-variance-authority"
|
|
76
|
+
"class-variance-authority",
|
|
77
|
+
"lucide-react"
|
|
74
78
|
],
|
|
75
79
|
"registryDependencies": [],
|
|
76
80
|
"files": [
|
|
@@ -113,7 +117,8 @@
|
|
|
113
117
|
"description": "A button component with multiple variants, sizes, and loading state.",
|
|
114
118
|
"dependencies": [
|
|
115
119
|
"@radix-ui/react-slot",
|
|
116
|
-
"class-variance-authority"
|
|
120
|
+
"class-variance-authority",
|
|
121
|
+
"lucide-react"
|
|
117
122
|
],
|
|
118
123
|
"registryDependencies": [],
|
|
119
124
|
"files": [
|
|
@@ -152,7 +157,9 @@
|
|
|
152
157
|
"type": "registry:ui",
|
|
153
158
|
"title": "Checkbox",
|
|
154
159
|
"description": "An accessible checkbox for binary selections with controlled state.",
|
|
155
|
-
"dependencies": [
|
|
160
|
+
"dependencies": [
|
|
161
|
+
"lucide-react"
|
|
162
|
+
],
|
|
156
163
|
"registryDependencies": [],
|
|
157
164
|
"files": [
|
|
158
165
|
{
|
|
@@ -166,12 +173,35 @@
|
|
|
166
173
|
"forms"
|
|
167
174
|
]
|
|
168
175
|
},
|
|
176
|
+
{
|
|
177
|
+
"name": "radio",
|
|
178
|
+
"type": "registry:ui",
|
|
179
|
+
"title": "Radio",
|
|
180
|
+
"description": "An accessible radio for choosing one option from a set, with a plain and a bordered layout variant.",
|
|
181
|
+
"dependencies": [
|
|
182
|
+
"class-variance-authority"
|
|
183
|
+
],
|
|
184
|
+
"registryDependencies": [],
|
|
185
|
+
"files": [
|
|
186
|
+
{
|
|
187
|
+
"path": "registry/ui/radio/radio.tsx",
|
|
188
|
+
"type": "registry:ui",
|
|
189
|
+
"target": "components/ui/radio.tsx"
|
|
190
|
+
}
|
|
191
|
+
],
|
|
192
|
+
"categories": [
|
|
193
|
+
"primitives",
|
|
194
|
+
"forms"
|
|
195
|
+
]
|
|
196
|
+
},
|
|
169
197
|
{
|
|
170
198
|
"name": "combobox",
|
|
171
199
|
"type": "registry:ui",
|
|
172
200
|
"title": "Combobox",
|
|
173
201
|
"description": "A searchable combobox with single or multiple selection.",
|
|
174
|
-
"dependencies": [
|
|
202
|
+
"dependencies": [
|
|
203
|
+
"lucide-react"
|
|
204
|
+
],
|
|
175
205
|
"registryDependencies": [],
|
|
176
206
|
"files": [
|
|
177
207
|
{
|
|
@@ -209,7 +239,9 @@
|
|
|
209
239
|
"type": "registry:block",
|
|
210
240
|
"title": "Empty State",
|
|
211
241
|
"description": "Empty state component with icon, title, description, and actions.",
|
|
212
|
-
"dependencies": [
|
|
242
|
+
"dependencies": [
|
|
243
|
+
"lucide-react"
|
|
244
|
+
],
|
|
213
245
|
"registryDependencies": [],
|
|
214
246
|
"files": [
|
|
215
247
|
{
|
|
@@ -287,7 +319,9 @@
|
|
|
287
319
|
"type": "registry:block",
|
|
288
320
|
"title": "Navbar",
|
|
289
321
|
"description": "Responsive navigation bar with desktop links and mobile menu.",
|
|
290
|
-
"dependencies": [
|
|
322
|
+
"dependencies": [
|
|
323
|
+
"lucide-react"
|
|
324
|
+
],
|
|
291
325
|
"registryDependencies": [],
|
|
292
326
|
"files": [
|
|
293
327
|
{
|
|
@@ -325,7 +359,9 @@
|
|
|
325
359
|
"type": "registry:ui",
|
|
326
360
|
"title": "Select",
|
|
327
361
|
"description": "A select control for choosing one option from a list.",
|
|
328
|
-
"dependencies": [
|
|
362
|
+
"dependencies": [
|
|
363
|
+
"lucide-react"
|
|
364
|
+
],
|
|
329
365
|
"registryDependencies": [],
|
|
330
366
|
"files": [
|
|
331
367
|
{
|
|
@@ -365,7 +401,9 @@
|
|
|
365
401
|
"type": "registry:block",
|
|
366
402
|
"title": "Sidebar",
|
|
367
403
|
"description": "Responsive sidebar navigation with collapsible structure.",
|
|
368
|
-
"dependencies": [
|
|
404
|
+
"dependencies": [
|
|
405
|
+
"lucide-react"
|
|
406
|
+
],
|
|
369
407
|
"registryDependencies": [],
|
|
370
408
|
"files": [
|
|
371
409
|
{
|
|
@@ -424,7 +462,9 @@
|
|
|
424
462
|
"type": "registry:block",
|
|
425
463
|
"title": "Stats Card",
|
|
426
464
|
"description": "Display a metric with trend, icon, and supporting label.",
|
|
427
|
-
"dependencies": [
|
|
465
|
+
"dependencies": [
|
|
466
|
+
"lucide-react"
|
|
467
|
+
],
|
|
428
468
|
"registryDependencies": [],
|
|
429
469
|
"files": [
|
|
430
470
|
{
|
|
@@ -438,12 +478,35 @@
|
|
|
438
478
|
"application"
|
|
439
479
|
]
|
|
440
480
|
},
|
|
481
|
+
{
|
|
482
|
+
"name": "status-indicator",
|
|
483
|
+
"type": "registry:ui",
|
|
484
|
+
"title": "Status Indicator",
|
|
485
|
+
"description": "A presence dot — online, away, busy or offline — with an accessible name in the page's language, so colour never carries the status alone.",
|
|
486
|
+
"dependencies": [
|
|
487
|
+
"class-variance-authority"
|
|
488
|
+
],
|
|
489
|
+
"registryDependencies": [],
|
|
490
|
+
"files": [
|
|
491
|
+
{
|
|
492
|
+
"path": "registry/ui/status-indicator/status-indicator.tsx",
|
|
493
|
+
"type": "registry:ui",
|
|
494
|
+
"target": "components/ui/status-indicator.tsx"
|
|
495
|
+
}
|
|
496
|
+
],
|
|
497
|
+
"categories": [
|
|
498
|
+
"primitives",
|
|
499
|
+
"data-display"
|
|
500
|
+
]
|
|
501
|
+
},
|
|
441
502
|
{
|
|
442
503
|
"name": "switch",
|
|
443
504
|
"type": "registry:ui",
|
|
444
505
|
"title": "Switch",
|
|
445
|
-
"description": "An accessible switch for on-off settings.",
|
|
446
|
-
"dependencies": [
|
|
506
|
+
"description": "An accessible switch for on-off settings: solid and outline styles in three sizes, with the track before or after its label.",
|
|
507
|
+
"dependencies": [
|
|
508
|
+
"class-variance-authority"
|
|
509
|
+
],
|
|
447
510
|
"registryDependencies": [],
|
|
448
511
|
"files": [
|
|
449
512
|
{
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import plugin from "tailwindcss/plugin"
|
|
2
|
+
import type { Config } from "tailwindcss"
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Shared typography preset — the single place the Fasla type system lives.
|
|
6
|
+
*
|
|
7
|
+
* Every value here is read from the `Tailwind En/*` and `Tailwind AR/*` text
|
|
8
|
+
* styles in the Fasla Figma file. Figma is the source of truth: nothing in this
|
|
9
|
+
* file is rounded, averaged or invented. If a value looks wrong, fix the style
|
|
10
|
+
* in Figma and mirror it here — never the other way round.
|
|
11
|
+
*
|
|
12
|
+
* Geist carries LTR, Cairo carries RTL. The two ramps are 1:1 by size and
|
|
13
|
+
* deliberately not 1:1 by line-height — Cairo needs more vertical room for
|
|
14
|
+
* descenders and diacritics.
|
|
15
|
+
*
|
|
16
|
+
* Line-height is delivered as a CSS variable per rung, so `[dir="rtl"]`
|
|
17
|
+
* re-points the whole ramp at once. That is what makes every `text-*` utility
|
|
18
|
+
* direction-aware without a single component knowing about it.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Line-height as Figma states it: a px value against a px size.
|
|
23
|
+
*
|
|
24
|
+
* It is emitted as `calc(lineHeight / fontSize)` — a unitless ratio, so it
|
|
25
|
+
* stays correct when a rung is used at a responsive size, and exact, because
|
|
26
|
+
* CSS does the division at full precision. Eight of these ratios are
|
|
27
|
+
* non-terminating (18→26 is 1.444…), so writing them as decimals would mean
|
|
28
|
+
* rounding a source-of-truth value. calc() avoids that entirely.
|
|
29
|
+
*
|
|
30
|
+
* `null` means Figma has the rung on AUTO — the font's own metrics, which CSS
|
|
31
|
+
* spells `normal`. No rung is currently on AUTO: all 144 styles are bound, in
|
|
32
|
+
* both scripts, at every size. The mechanism stays because AUTO is a legitimate
|
|
33
|
+
* choice Figma can express, and a rung may return to it.
|
|
34
|
+
*/
|
|
35
|
+
const LEADING = {
|
|
36
|
+
// size En AR
|
|
37
|
+
// XXS is the one rung below Tailwind's stock scale. Figma binds En to
|
|
38
|
+
// `typography/line-height/L-neg-0,5` (14) and AR to `L-0` (16).
|
|
39
|
+
xxs: /* 10 */ [/* 14 */ 14, /* 16 */ 16],
|
|
40
|
+
xs: /* 12 */ [/* 16 */ 16, /* 20 */ 20],
|
|
41
|
+
sm: /* 14 */ [/* 20 */ 20, /* 24 */ 24],
|
|
42
|
+
base: /* 16 */ [/* 24 */ 24, /* 28 */ 28],
|
|
43
|
+
lg: /* 18 */ [/* 28 */ 28, /* 32 */ 32],
|
|
44
|
+
xl: /* 20 */ [/* 28 */ 28, /* 36 */ 36],
|
|
45
|
+
"2xl": /* 24 */ [/* 32 */ 32, /* 40 */ 40],
|
|
46
|
+
"3xl": /* 30 */ [/* 40 */ 40, /* 50 */ 50],
|
|
47
|
+
"4xl": /* 36 */ [/* 44 */ 44, /* 60 */ 60],
|
|
48
|
+
"5xl": /* 48 */ [/* 58 */ 58, /* 72 */ 72],
|
|
49
|
+
"6xl": /* 60 */ [/* 72 */ 72, /* 90 */ 90],
|
|
50
|
+
"7xl": /* 72 */ [/* 90 */ 90, /* 108 */ 108],
|
|
51
|
+
"8xl": /* 96 */ [/* 108 */ 108, /* 144 */ 144],
|
|
52
|
+
"9xl": /* 128 */ [/* 144 */ 144, /* 192 */ 192],
|
|
53
|
+
// `Extra/*` — two styles that sit outside the XXS–9XL ramp but are part of it.
|
|
54
|
+
// Weight, underline and case are left to the ordinary utilities: `Extra/Link`
|
|
55
|
+
// is 400 + underline, `Extra/List Header` is 500 + uppercase. Uppercase is
|
|
56
|
+
// English-only; Arabic has no uppercase and must never be given one.
|
|
57
|
+
link: /* 16 */ [/* 24 */ 24, /* 24 */ 24],
|
|
58
|
+
"list-header": /* 14 */ [/* 16 */ 16, /* 16 */ 16],
|
|
59
|
+
} as const satisfies Record<string, readonly [number | null, number | null]>
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Figma's size scale. From XS up it matches Tailwind's own scale 1:1; XXS
|
|
63
|
+
* (10px, `typography/font-size/xxs`) is Fasla's addition below it.
|
|
64
|
+
*/
|
|
65
|
+
const SIZE_PX = {
|
|
66
|
+
xxs: 10,
|
|
67
|
+
xs: 12,
|
|
68
|
+
sm: 14,
|
|
69
|
+
base: 16,
|
|
70
|
+
lg: 18,
|
|
71
|
+
xl: 20,
|
|
72
|
+
"2xl": 24,
|
|
73
|
+
"3xl": 30,
|
|
74
|
+
"4xl": 36,
|
|
75
|
+
"5xl": 48,
|
|
76
|
+
"6xl": 60,
|
|
77
|
+
"7xl": 72,
|
|
78
|
+
"8xl": 96,
|
|
79
|
+
"9xl": 128,
|
|
80
|
+
link: 16,
|
|
81
|
+
"list-header": 14,
|
|
82
|
+
} as const
|
|
83
|
+
|
|
84
|
+
type Rung = keyof typeof LEADING
|
|
85
|
+
|
|
86
|
+
const rungs = Object.keys(LEADING) as Rung[]
|
|
87
|
+
|
|
88
|
+
const rem = (px: number) => `${px / 16}rem`
|
|
89
|
+
const cssVar = (rung: Rung) => `--leading-${rung}`
|
|
90
|
+
|
|
91
|
+
/** Exact, unitless, and never rounded — CSS divides at full precision. */
|
|
92
|
+
const ratio = (leadingPx: number, sizePx: number) => `calc(${leadingPx} / ${sizePx})`
|
|
93
|
+
|
|
94
|
+
/** `text-xl` resolves its leading through the variable, never a literal. */
|
|
95
|
+
const fontSize = Object.fromEntries(
|
|
96
|
+
rungs.map((rung) => [rung, [rem(SIZE_PX[rung]), `var(${cssVar(rung)})`]])
|
|
97
|
+
) as Record<Rung, [string, string]>
|
|
98
|
+
|
|
99
|
+
const leadingFor = (index: 0 | 1) =>
|
|
100
|
+
Object.fromEntries(
|
|
101
|
+
rungs.map((rung) => {
|
|
102
|
+
const px = LEADING[rung][index]
|
|
103
|
+
return [cssVar(rung), px === null ? "normal" : ratio(px, SIZE_PX[rung])]
|
|
104
|
+
})
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Figma sets `letterSpacing` to 0% on all 144 text styles, both scripts, every
|
|
109
|
+
* weight — so the ramp declares no tracking and components should set none.
|
|
110
|
+
* Tailwind's stock `tracking-*` scale is left in place for the one sanctioned
|
|
111
|
+
* exception, the letter-spaced eyebrow that design/DESIGN.md documents as
|
|
112
|
+
* deliberately detached. Reach for it only for that.
|
|
113
|
+
*/
|
|
114
|
+
|
|
115
|
+
const preset = {
|
|
116
|
+
content: [],
|
|
117
|
+
theme: {
|
|
118
|
+
extend: {
|
|
119
|
+
fontFamily: {
|
|
120
|
+
// Brand V2.5 §14 — product tier.
|
|
121
|
+
sans: ["var(--font-geist-sans)", "system-ui", "sans-serif"],
|
|
122
|
+
mono: ["var(--font-geist-mono)", "ui-monospace", "monospace"],
|
|
123
|
+
// Brand V2.5 §14 — the Arabic face. Geist has no Arabic coverage.
|
|
124
|
+
arabic: ["var(--font-cairo)", "system-ui", "sans-serif"],
|
|
125
|
+
},
|
|
126
|
+
fontSize,
|
|
127
|
+
},
|
|
128
|
+
},
|
|
129
|
+
plugins: [
|
|
130
|
+
plugin(({ addBase }) => {
|
|
131
|
+
addBase({
|
|
132
|
+
":root": leadingFor(0),
|
|
133
|
+
// Source order matters: same specificity as `:root`, so this must win by
|
|
134
|
+
// coming second. `dir` is on a wrapper element, not <html>, in both apps —
|
|
135
|
+
// the variables cascade down from wherever it sits.
|
|
136
|
+
'[dir="rtl"]': leadingFor(1),
|
|
137
|
+
})
|
|
138
|
+
}),
|
|
139
|
+
],
|
|
140
|
+
} satisfies Config
|
|
141
|
+
|
|
142
|
+
export { LEADING, SIZE_PX }
|
|
143
|
+
export default preset
|