yems-ui 1.1.1 → 1.1.5

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 CHANGED
@@ -1,66 +1,58 @@
1
- # @yems-ui/core
1
+ # yems-ui
2
2
 
3
- A beautiful React component library with **glassmorphism effects**, **premium animations**, and **modern design patterns**.
3
+ A React component library with **glassmorphism effects**, **premium micro-animations**, and a fully themeable design system built on Tailwind CSS v4.
4
4
 
5
- ## ✨ Features
5
+ [![npm version](https://img.shields.io/npm/v/yems-ui)](https://www.npmjs.com/package/yems-ui)
6
+ [![npm downloads](https://img.shields.io/npm/dm/yems-ui)](https://www.npmjs.com/package/yems-ui)
7
+ [![license](https://img.shields.io/npm/l/yems-ui)](./LICENSE)
6
8
 
7
- - 🎨 **Glassmorphism Design** - Beautiful frosted glass effects with liquid aesthetics
8
- - ⚡ **Premium Animations** - Smooth micro-interactions using Motion.dev (Framer Motion)
9
- - 🎯 **TypeScript First** - Full type safety with exported types
10
- - 🎭 **Radix UI Primitives** - Fully accessible components
11
- - 🎨 **Tailwind CSS v4** - Modern utility-first styling
12
- - 🌓 **Dark/Light Mode** - Theme-aware with CSS variables
13
- - 🚀 **Tree-shakeable** - Import only what you need
9
+ **[Live Demo →](https://yem-ui.vercel.app)** See all components in action with light/dark mode toggle.
14
10
 
15
- ## 📦 Installation
11
+ ---
16
12
 
17
- ### Via NPM (Recommended)
13
+ ## Table of Contents
18
14
 
19
- ```bash
20
- npm install @yems-ui/core
21
- ```
15
+ - [Installation](#installation)
16
+ - [Setup](#setup)
17
+ - [Theming](#theming)
18
+ - [Components](#components)
19
+ - [Dark Mode](#dark-mode)
20
+ - [TypeScript](#typescript)
22
21
 
23
- ### Peer Dependencies
22
+ ---
24
23
 
25
- Make sure you have React 18+ installed:
24
+ ## Installation
26
25
 
27
26
  ```bash
28
- npm install react react-dom
27
+ npm install yems-ui
28
+ # or
29
+ pnpm add yems-ui
30
+ # or
31
+ yarn add yems-ui
29
32
  ```
30
33
 
31
- ### Required: Tailwind CSS v4
32
-
33
- This library requires Tailwind CSS v4:
34
+ **Peer dependencies** (install if not already present):
34
35
 
35
36
  ```bash
36
- npm install tailwindcss@next
37
+ npm install react react-dom
37
38
  ```
38
39
 
39
40
  ---
40
41
 
41
- ## 🚨 IMPORTANT: Tailwind CSS v4 Setup
42
+ ## Setup
42
43
 
43
- This library uses Tailwind CSS v4 and requires **two critical steps** in your project:
44
+ yems-ui requires **Tailwind CSS v4** and the `@tailwindcss/vite` plugin.
44
45
 
45
- ### Step 1: Add the `@source` directive
46
+ ### 1. Install Tailwind CSS v4
46
47
 
47
- In your main CSS file (e.g., `src/styles.css` or `src/index.css`), add:
48
-
49
- ```css
50
- @import "tailwindcss";
51
- @source "../node_modules/@yems-ui/core/dist"; /* <-- REQUIRED! */
52
- @import "@yems-ui/core/styles.css";
53
-
54
- /* Your custom styles below... */
48
+ ```bash
49
+ npm install tailwindcss @tailwindcss/vite
55
50
  ```
56
51
 
57
- > ⚠️ **Without the `@source` directive, the library's utility classes (like `bg-true-azure`, `glass-card`, etc.) will NOT be generated!**
58
-
59
- ### Step 2: Ensure Vite + Tailwind v4 plugin
60
-
61
- Make sure your `vite.config.ts` includes the Tailwind plugin:
52
+ ### 2. Configure Vite
62
53
 
63
54
  ```ts
55
+ // vite.config.ts
64
56
  import { defineConfig } from "vite";
65
57
  import react from "@vitejs/plugin-react";
66
58
  import tailwindcss from "@tailwindcss/vite";
@@ -70,43 +62,51 @@ export default defineConfig({
70
62
  });
71
63
  ```
72
64
 
73
- ---
65
+ ### 3. Import the styles
74
66
 
75
- ## Framework Integration
67
+ In your app's entry CSS file (e.g. `src/index.css`):
76
68
 
77
- This library works with any React framework or setup:
69
+ ```css
70
+ @import "tailwindcss";
78
71
 
79
- - **Next.js** - Works with App Router and Pages Router
80
- - **Vite** - Full support with HMR
81
- - **Create React App** - Compatible
82
- - **Remix** - Works as expected
83
- - **Astro + React** - Fully compatible
84
- - **Gatsby** - Supported
85
- - **Any custom webpack/rollup setup** - Should work fine
72
+ /* Tell Tailwind to scan yems-ui's dist folder for class names */
73
+ @source "../node_modules/yems-ui/dist";
86
74
 
87
- > **Note**: Mobile support is built-in. Components are touch-friendly and work on React Native Web setups.
75
+ /* Import the design system tokens and base styles */
76
+ @import "yems-ui/styles.css";
77
+ ```
88
78
 
89
- ---
79
+ > ⚠️ The `@source` directive is required. Without it, Tailwind won't generate the utility classes used by yems-ui components.
90
80
 
91
- ## �🚀 Usage
81
+ ### 4. Import styles in your entry file
92
82
 
93
83
  ```tsx
94
- import {
95
- Button,
96
- Card,
97
- CardHeader,
98
- CardTitle,
99
- CardContent,
100
- } from "@yems-ui/core";
84
+ // src/main.tsx
85
+ import "./index.css";
86
+ import React from "react";
87
+ import ReactDOM from "react-dom/client";
88
+ import App from "./App";
89
+
90
+ ReactDOM.createRoot(document.getElementById("root")!).render(
91
+ <React.StrictMode>
92
+ <App />
93
+ </React.StrictMode>,
94
+ );
95
+ ```
96
+
97
+ ### 5. Use components
98
+
99
+ ```tsx
100
+ import { Button, Card, CardHeader, CardTitle, CardContent } from "yems-ui";
101
101
 
102
102
  function App() {
103
103
  return (
104
- <Card hover>
104
+ <Card>
105
105
  <CardHeader>
106
- <CardTitle>Welcome</CardTitle>
106
+ <CardTitle>Hello World</CardTitle>
107
107
  </CardHeader>
108
108
  <CardContent>
109
- <Button variant="primary">Click me</Button>
109
+ <Button variant="primary">Get Started</Button>
110
110
  </CardContent>
111
111
  </Card>
112
112
  );
@@ -115,52 +115,663 @@ function App() {
115
115
 
116
116
  ---
117
117
 
118
- ## 🎨 Components
118
+ ## Theming
119
+
120
+ yems-ui uses a **two-layer CSS variable system** built into Tailwind v4's `@theme`. You can override any part of it in your own CSS.
121
+
122
+ ### How it works
123
+
124
+ ```
125
+ Layer 1 — Raw palette → Layer 2 — Semantic tokens → Components
126
+ --brand-500: #5000ab → --color-primary: var(--brand-500) → bg-primary
127
+ --accent-500: #e3b23c → --color-accent: var(--accent-500) → bg-accent
128
+ ```
129
+
130
+ To retheme the library, you only need to override **Layer 1** values. Everything cascades automatically.
131
+
132
+ ---
133
+
134
+ ### Changing the primary color
135
+
136
+ Add this to your CSS file after importing yems-ui styles:
137
+
138
+ ```css
139
+ @import "tailwindcss";
140
+ @source "../node_modules/yems-ui/dist";
141
+ @import "yems-ui/styles.css";
142
+
143
+ /* Override just what you want to change */
144
+ :root {
145
+ --brand-500: #0066ff; /* new primary — all buttons, links, rings update */
146
+ --brand-900: #001a66; /* new secondary / dark shade */
147
+ --accent-500: #f59e0b; /* new accent color */
148
+ }
149
+ ```
150
+
151
+ ### All themeable tokens
152
+
153
+ #### Brand palette (Layer 1)
154
+
155
+ | Token | Default | Purpose |
156
+ | -------------- | --------- | ---------------------- |
157
+ | `--brand-50` | `#ede8f7` | Lightest brand tint |
158
+ | `--brand-100` | `#d4c5f0` | Light brand tint |
159
+ | `--brand-200` | `#a98cdf` | |
160
+ | `--brand-300` | `#7e53ce` | |
161
+ | `--brand-400` | `#6529be` | |
162
+ | `--brand-500` | `#5000ab` | **Main primary color** |
163
+ | `--brand-600` | `#40008a` | |
164
+ | `--brand-700` | `#300069` | |
165
+ | `--brand-800` | `#200048` | |
166
+ | `--brand-900` | `#1c0636` | Secondary / dark shade |
167
+ | `--accent-500` | `#e3b23c` | **Main accent color** |
168
+ | `--accent-700` | `#bb4d00` | Ember / dark accent |
169
+
170
+ #### Semantic tokens (Layer 2)
171
+
172
+ These are what components use internally. You can override these individually if you want to rewire which color plays which role:
173
+
174
+ | Token | Default maps to | Used for |
175
+ | ---------------------------- | --------------- | ----------------------------------- |
176
+ | `--color-primary` | `--brand-500` | Primary buttons, links, focus rings |
177
+ | `--color-primary-foreground` | `--neutral-50` | Text on primary backgrounds |
178
+ | `--color-secondary` | `--brand-900` | Secondary buttons |
179
+ | `--color-accent` | `--accent-500` | Accent buttons, highlights |
180
+ | `--color-ember` | `--accent-700` | Ember variant |
181
+ | `--color-destructive` | `--color-error` | Destructive actions |
182
+ | `--color-background` | `--neutral-100` | Page background |
183
+ | `--color-foreground` | `--neutral-900` | Body text |
184
+ | `--color-muted` | `--neutral-100` | Subtle backgrounds |
185
+ | `--color-muted-foreground` | `--neutral-500` | Placeholder / hint text |
186
+ | `--color-border` | `--neutral-200` | Borders and dividers |
187
+ | `--color-ring` | `--brand-500` | Focus ring color |
188
+
189
+ #### Shape & spacing
190
+
191
+ | Token | Default | Purpose |
192
+ | ---------- | ------- | ------------------------------------- |
193
+ | `--radius` | `12px` | Base border radius for all components |
194
+
195
+ ```css
196
+ :root {
197
+ --radius: 8px; /* more angular */
198
+ /* or */
199
+ --radius: 20px; /* more pill-shaped */
200
+ }
201
+ ```
202
+
203
+ #### Shadows
204
+
205
+ | Token | Default | Purpose |
206
+ | ------------------ | ------------- | -------------------- |
207
+ | `--shadow-sm` | subtle | Small elevation |
208
+ | `--shadow-md` | medium | Default cards |
209
+ | `--shadow-lg` | prominent | Dropdowns, modals |
210
+ | `--shadow-xl` | strong | Tooltips, popovers |
211
+ | `--shadow-primary` | brand-tinted | Primary button hover |
212
+ | `--shadow-accent` | accent-tinted | Accent button hover |
213
+
214
+ ```css
215
+ :root {
216
+ /* Softer shadows for a flatter look */
217
+ --shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.08);
218
+ --shadow-md: 0 2px 8px rgba(0, 0, 0, 0.1);
219
+ --shadow-lg: 0 4px 16px rgba(0, 0, 0, 0.12);
220
+ }
221
+ ```
222
+
223
+ #### Glassmorphism
224
+
225
+ | Token | Light default | Purpose |
226
+ | ------------------- | ------------------------ | --------------------- |
227
+ | `--glass-bg` | `rgba(255,255,255,0.65)` | Glass background |
228
+ | `--glass-bg-strong` | `rgba(255,255,255,0.85)` | Strong glass |
229
+ | `--glass-border` | `rgba(0,0,0,0.08)` | Glass border |
230
+ | `--glass-blur` | `16px` | Backdrop blur amount |
231
+ | `--glass-card-bg` | `rgba(255,255,255,0.75)` | Card glass background |
232
+ | `--glass-shadow` | subtle blue-tinted | Glass drop shadow |
233
+
234
+ ```css
235
+ :root {
236
+ /* Heavier blur for more prominent glass effect */
237
+ --glass-blur: 24px;
238
+ --glass-bg: rgba(255, 255, 255, 0.5);
239
+
240
+ /* Or minimal glass — nearly transparent */
241
+ --glass-blur: 8px;
242
+ --glass-bg: rgba(255, 255, 255, 0.9);
243
+ }
244
+ ```
245
+
246
+ #### Typography
247
+
248
+ | Token | Default | Purpose |
249
+ | ---------------- | ---------------------- | -------------------- |
250
+ | `--font-sans` | `"Poppins", system-ui` | Body font |
251
+ | `--font-display` | `"Otama EP", Georgia` | Display/heading font |
252
+
253
+ ```css
254
+ :root {
255
+ --font-sans: "Inter", ui-sans-serif, system-ui, sans-serif;
256
+ }
257
+ ```
258
+
259
+ #### Transitions
260
+
261
+ | Token | Default | Purpose |
262
+ | --------------------- | ------- | ---------------- |
263
+ | `--transition-fast` | `150ms` | Hover states |
264
+ | `--transition-normal` | `250ms` | Most animations |
265
+ | `--transition-slow` | `350ms` | Page transitions |
266
+
267
+ ```css
268
+ :root {
269
+ /* Snappier animations */
270
+ --transition-fast: 100ms;
271
+ --transition-normal: 180ms;
272
+
273
+ /* Or disable motion for accessibility */
274
+ --transition-fast: 0ms;
275
+ --transition-normal: 0ms;
276
+ }
277
+ ```
278
+
279
+ ### Complete theme example
280
+
281
+ ```css
282
+ @import "tailwindcss";
283
+ @source "../node_modules/yems-ui/dist";
284
+ @import "yems-ui/styles.css";
285
+
286
+ /* A blue/teal theme with softer radius and minimal glass */
287
+ :root {
288
+ /* Brand */
289
+ --brand-500: #0ea5e9;
290
+ --brand-900: #0c4a6e;
291
+ --accent-500: #f59e0b;
292
+ --accent-700: #d97706;
293
+
294
+ /* Shape */
295
+ --radius: 8px;
296
+
297
+ /* Glass — subtle */
298
+ --glass-blur: 8px;
299
+ --glass-bg: rgba(255, 255, 255, 0.8);
300
+ --glass-card-bg: rgba(255, 255, 255, 0.9);
301
+
302
+ /* Shadows — flatter */
303
+ --shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.06);
304
+ --shadow-md: 0 2px 8px rgba(0, 0, 0, 0.08);
305
+ --shadow-lg: 0 4px 16px rgba(0, 0, 0, 0.1);
306
+ }
307
+ ```
308
+
309
+ ### Per-component override via className
310
+
311
+ Every component accepts a `className` prop for one-off overrides without touching theme variables:
312
+
313
+ ```tsx
314
+ <Button variant="primary" className="rounded-full px-8">
315
+ Pill Button
316
+ </Button>
317
+
318
+ <Card className="border-2 border-primary/30 shadow-xl">
319
+ Custom Card
320
+ </Card>
321
+
322
+ <Input className="h-14 text-lg" placeholder="Large input" />
323
+ ```
324
+
325
+ ---
326
+
327
+ ## Components
328
+
329
+ ### Button
330
+
331
+ ```tsx
332
+ import { Button } from "yems-ui";
333
+
334
+ // Variants
335
+ <Button variant="primary">Primary</Button>
336
+ <Button variant="secondary">Secondary</Button>
337
+ <Button variant="accent">Accent</Button>
338
+ <Button variant="ember">Ember</Button>
339
+ <Button variant="destructive">Delete</Button>
340
+ <Button variant="ghost">Ghost</Button>
341
+ <Button variant="outline">Outline</Button>
342
+ <Button variant="link">Link</Button>
343
+
344
+ // Outline variants
345
+ <Button variant="outline-primary">Outline Primary</Button>
346
+ <Button variant="outline-secondary">Outline Secondary</Button>
347
+ <Button variant="outline-accent">Outline Accent</Button>
348
+ <Button variant="outline-ember">Outline Ember</Button>
349
+ <Button variant="outline-destructive">Outline Destructive</Button>
350
+
351
+ // Ghost variants
352
+ <Button variant="ghost-primary">Ghost Primary</Button>
353
+ <Button variant="ghost-secondary">Ghost Secondary</Button>
354
+
355
+ // Sizes
356
+ <Button size="sm">Small</Button>
357
+ <Button size="default">Default</Button>
358
+ <Button size="lg">Large</Button>
359
+ <Button size="xl">Extra Large</Button>
360
+ <Button size="icon"><SearchIcon /></Button>
361
+
362
+ // With icons
363
+ <Button leftIcon={<PlusIcon />}>Add Item</Button>
364
+ <Button rightIcon={<ArrowRight />}>Continue</Button>
365
+
366
+ // Loading state
367
+ <Button isLoading>Saving...</Button>
368
+
369
+ // As a link (renders as child element)
370
+ <Button asChild>
371
+ <a href="/dashboard">Dashboard</a>
372
+ </Button>
373
+ ```
374
+
375
+ **Props:**
376
+
377
+ | Prop | Type | Default | Description |
378
+ | ----------- | --------------------------------------------------------- | --------- | ------------------------------------- |
379
+ | `variant` | see above | `primary` | Visual style |
380
+ | `size` | `sm \| default \| lg \| xl \| icon \| icon-sm \| icon-lg` | `default` | Size |
381
+ | `isLoading` | `boolean` | `false` | Shows spinner, disables interaction |
382
+ | `leftIcon` | `ReactNode` | — | Icon before label |
383
+ | `rightIcon` | `ReactNode` | — | Icon after label |
384
+ | `asChild` | `boolean` | `false` | Renders as child element (e.g. `<a>`) |
385
+
386
+ ---
387
+
388
+ ### Card
389
+
390
+ ```tsx
391
+ import { Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter, StatCard } from "yems-ui";
392
+
393
+ <Card>
394
+ <CardHeader>
395
+ <CardTitle>Card Title</CardTitle>
396
+ <CardDescription>A short description.</CardDescription>
397
+ </CardHeader>
398
+ <CardContent>
399
+ Content goes here.
400
+ </CardContent>
401
+ <CardFooter>
402
+ <Button variant="outline-primary">Action</Button>
403
+ </CardFooter>
404
+ </Card>
405
+
406
+ // With hover animation
407
+ <Card hover>
408
+ <CardContent>Lifts on hover</CardContent>
409
+ </Card>
410
+
411
+ // Stats card
412
+ <StatCard
413
+ title="Total Revenue"
414
+ value="$12,340"
415
+ icon={<DollarSign />}
416
+ trend={{ value: 12.5, isPositive: true }}
417
+ description="vs last month"
418
+ />
419
+ ```
420
+
421
+ **StatCard props:**
119
422
 
120
- | Component | Description |
121
- | ----------------- | --------------------------------------------------------------------------------- |
122
- | **Button** | Multiple variants (primary, secondary, accent, ghost, outline) with glassmorphism |
123
- | **Card** | Glass card with hover lift and StatCard variant |
124
- | **Dialog** | Liquid glass modal with smooth animations |
125
- | **Input** | Floating label inputs with variants |
126
- | **Radio Group** | With explosion animation effect |
127
- | **Switch** | Glassmorphism toggle |
128
- | **Checkbox** | Animated checkbox |
129
- | **Alert** | 5 variants (default, info, success, warning, error) |
130
- | **Accordion** | Collapsible content sections |
131
- | **Popover** | Contextual overlays |
132
- | **Tabs** | Tabbed content with glass effect |
133
- | **Select** | Dropdown select with glass styling |
134
- | **Tooltip** | Hover tooltips |
135
- | **Dropdown Menu** | Context menus |
136
- | **Table** | Data tables with hover effects |
137
- | **Badge** | Status badges and indicators |
138
- | **Avatar** | User avatars with fallback |
139
- | **Progress** | Progress bars |
140
- | **Separator** | Dividers |
141
- | **Pagination** | Smart page navigation |
142
- | **Breadcrumbs** | Navigation hierarchy |
143
- | **Skeleton** | Loading states (text, card, avatar, table) |
144
- | **Empty State** | No data displays |
145
- | **Toast** | Notification toasts |
423
+ | Prop | Type | Description |
424
+ | ------------- | ---------------------------------------- | --------------------------- |
425
+ | `title` | `string` | Label above the value |
426
+ | `value` | `string \| number` | The main figure |
427
+ | `icon` | `ReactNode` | Icon shown top-right |
428
+ | `trend` | `{ value: number, isPositive: boolean }` | Percentage change indicator |
429
+ | `description` | `string` | Sub-label below the value |
146
430
 
147
431
  ---
148
432
 
149
- ## 🎨 Brand Colors
433
+ ### Input
434
+
435
+ ```tsx
436
+ import { Input, Label, FormField, Textarea } from "yems-ui";
437
+ import { Search } from "lucide-react";
438
+
439
+ // Variants
440
+ <Input placeholder="Default" />
441
+ <Input placeholder="Filled" variant="filled" />
442
+ <Input placeholder="Ghost" variant="ghost" />
443
+
444
+ // Sizes
445
+ <Input placeholder="Small" inputSize="sm" />
446
+ <Input placeholder="Large" inputSize="lg" />
447
+
448
+ // With icons and addons
449
+ <Input leftIcon={<Search className="h-4 w-4" />} placeholder="Search..." />
450
+ <Input leftAddon="https://" placeholder="yoursite.com" />
451
+ <Input rightAddon=".com" placeholder="domain" />
452
+
453
+ // Validation states
454
+ <Input state="error" error="This field is required" />
455
+ <Input state="success" hint="Looks good!" />
456
+
457
+ // Label + FormField (handles layout, label, error, hint together)
458
+ <FormField label="Email" htmlFor="email" required error="Invalid email">
459
+ <Input id="email" type="email" />
460
+ </FormField>
461
+
462
+ // Textarea
463
+ <Textarea placeholder="Write something..." rows={4} />
464
+ <Textarea variant="filled" state="error" error="Required" />
465
+ ```
466
+
467
+ ---
468
+
469
+ ### Alert
470
+
471
+ ```tsx
472
+ import { Alert, AlertTitle, AlertDescription } from "yems-ui";
473
+ import { InfoIcon } from "lucide-react";
474
+
475
+ <Alert variant="info">
476
+ <InfoIcon className="h-4 w-4" />
477
+ <AlertTitle>Heads up</AlertTitle>
478
+ <AlertDescription>Something you should know.</AlertDescription>
479
+ </Alert>
480
+
481
+ // Variants: default | info | success | warning | error
482
+ <Alert variant="success">...</Alert>
483
+ <Alert variant="warning">...</Alert>
484
+ <Alert variant="error">...</Alert>
485
+
486
+ // Dismissible
487
+ <Alert variant="info" dismissible onDismiss={() => console.log("dismissed")}>
488
+ <AlertTitle>Dismissible</AlertTitle>
489
+ </Alert>
490
+ ```
491
+
492
+ ---
493
+
494
+ ### Badge
495
+
496
+ ```tsx
497
+ import { Badge, StatusBadge } from "yems-ui";
498
+
499
+ // Filled
500
+ <Badge variant="primary">Primary</Badge>
501
+ <Badge variant="success">Active</Badge>
502
+ <Badge variant="error">Failed</Badge>
503
+
504
+ // Soft (light background, colored text)
505
+ <Badge variant="soft-success">Paid</Badge>
506
+ <Badge variant="soft-warning">Pending</Badge>
507
+ <Badge variant="soft-error">Unpaid</Badge>
508
+
509
+ // Outline
510
+ <Badge variant="outline-primary">Draft</Badge>
511
+
512
+ // With dot indicator
513
+ <Badge variant="soft-success" dot>Online</Badge>
514
+
515
+ // Sizes
516
+ <Badge size="sm">Small</Badge>
517
+ <Badge size="lg">Large</Badge>
518
+
519
+ // Preset status badge
520
+ <StatusBadge status="active" />
521
+ <StatusBadge status="pending" />
522
+ <StatusBadge status="inactive" />
523
+ ```
524
+
525
+ ---
526
+
527
+ ### Dialog
528
+
529
+ ```tsx
530
+ import {
531
+ Dialog,
532
+ DialogTrigger,
533
+ DialogContent,
534
+ DialogHeader,
535
+ DialogTitle,
536
+ DialogDescription,
537
+ DialogFooter,
538
+ } from "yems-ui";
539
+
540
+ <Dialog>
541
+ <DialogTrigger asChild>
542
+ <Button>Open Dialog</Button>
543
+ </DialogTrigger>
544
+ <DialogContent>
545
+ <DialogHeader>
546
+ <DialogTitle>Edit Profile</DialogTitle>
547
+ <DialogDescription>Make changes to your profile here.</DialogDescription>
548
+ </DialogHeader>
549
+ <div className="py-4">
550
+ <Input placeholder="Name" />
551
+ </div>
552
+ <DialogFooter>
553
+ <Button variant="primary">Save</Button>
554
+ </DialogFooter>
555
+ </DialogContent>
556
+ </Dialog>;
557
+ ```
558
+
559
+ ---
560
+
561
+ ### Select
562
+
563
+ ```tsx
564
+ import {
565
+ Select,
566
+ SelectTrigger,
567
+ SelectValue,
568
+ SelectContent,
569
+ SelectItem,
570
+ } from "yems-ui";
571
+
572
+ const [value, setValue] = useState("");
573
+
574
+ <Select value={value} onValueChange={setValue}>
575
+ <SelectTrigger className="w-48">
576
+ <SelectValue placeholder="Choose one" />
577
+ </SelectTrigger>
578
+ <SelectContent>
579
+ <SelectItem value="option1">Option 1</SelectItem>
580
+ <SelectItem value="option2">Option 2</SelectItem>
581
+ <SelectItem value="option3">Option 3</SelectItem>
582
+ </SelectContent>
583
+ </Select>;
584
+ ```
585
+
586
+ ---
587
+
588
+ ### Tabs
589
+
590
+ ```tsx
591
+ import { Tabs, TabsList, TabsTrigger, TabsContent } from "yems-ui";
592
+
593
+ <Tabs defaultValue="overview">
594
+ <TabsList>
595
+ <TabsTrigger value="overview">Overview</TabsTrigger>
596
+ <TabsTrigger value="analytics">Analytics</TabsTrigger>
597
+ <TabsTrigger value="settings">Settings</TabsTrigger>
598
+ </TabsList>
599
+ <TabsContent value="overview">Overview content</TabsContent>
600
+ <TabsContent value="analytics">Analytics content</TabsContent>
601
+ <TabsContent value="settings">Settings content</TabsContent>
602
+ </Tabs>;
603
+ ```
604
+
605
+ ---
606
+
607
+ ### Toast
608
+
609
+ ```tsx
610
+ import { Toaster, useToast } from "yems-ui";
611
+
612
+ // Add <Toaster /> once near the root of your app
613
+ function App() {
614
+ return (
615
+ <>
616
+ <YourApp />
617
+ <Toaster />
618
+ </>
619
+ );
620
+ }
621
+
622
+ // Use the hook anywhere
623
+ function MyComponent() {
624
+ const { toast } = useToast();
625
+
626
+ return (
627
+ <Button
628
+ onClick={() =>
629
+ toast({ title: "Saved!", description: "Your changes have been saved." })
630
+ }
631
+ >
632
+ Save
633
+ </Button>
634
+ );
635
+ }
636
+
637
+ // Destructive variant
638
+ toast({
639
+ variant: "destructive",
640
+ title: "Error",
641
+ description: "Something went wrong.",
642
+ });
643
+ ```
644
+
645
+ ---
646
+
647
+ ### Skeleton
648
+
649
+ ```tsx
650
+ import { Skeleton, SkeletonCard, SkeletonText, SkeletonAvatar, SkeletonTable } from "yems-ui";
651
+
652
+ // Basic
653
+ <Skeleton className="h-4 w-48" />
654
+ <Skeleton variant="circular" className="h-10 w-10" />
655
+
656
+ // Animation variants
657
+ <Skeleton animation="pulse" className="h-4 w-full" /> // default
658
+ <Skeleton animation="wave" className="h-4 w-full" />
659
+ <Skeleton animation="none" className="h-4 w-full" />
660
+
661
+ // Preset layouts
662
+ <SkeletonCard />
663
+ <SkeletonText lines={4} />
664
+ <SkeletonAvatar size="lg" />
665
+ <SkeletonTable rows={5} columns={4} />
666
+ ```
667
+
668
+ ---
669
+
670
+ ### Other components
671
+
672
+ | Component | Import | Notes |
673
+ | -------------- | ----------------------------------------------------------------------------- | -------------------------------------------------- |
674
+ | `Accordion` | `AccordionItem, AccordionTrigger, AccordionContent` | Radix-based, animated |
675
+ | `Avatar` | `Avatar, AvatarImage, AvatarFallback` | With image fallback |
676
+ | `Breadcrumbs` | `Breadcrumbs` | Accepts `items: { label, href? }[]` |
677
+ | `Checkbox` | `Checkbox` | Controlled via `checked` + `onCheckedChange` |
678
+ | `DropdownMenu` | `DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem` | Radix-based |
679
+ | `EmptyState` | `EmptyState` | Props: `title`, `description`, `icon`, `action` |
680
+ | `Pagination` | `Pagination` | Props: `currentPage`, `totalPages`, `onPageChange` |
681
+ | `Popover` | `Popover, PopoverTrigger, PopoverContent` | Radix-based |
682
+ | `Progress` | `Progress` | Accepts `value` (0–100) |
683
+ | `RadioGroup` | `RadioGroup, RadioGroupItem` | Controlled via `value` + `onValueChange` |
684
+ | `Separator` | `Separator` | Horizontal or vertical divider |
685
+ | `Switch` | `Switch` | Controlled via `checked` + `onCheckedChange` |
686
+ | `Table` | `Table, TableHeader, TableBody, TableRow, TableHead, TableCell, TableCaption` | Standard table layout |
687
+ | `Tooltip` | `TooltipProvider, Tooltip, TooltipTrigger, TooltipContent` | Wrap app in `TooltipProvider` |
688
+
689
+ ---
690
+
691
+ ## Dark Mode
692
+
693
+ yems-ui supports dark mode via a `.dark` class on the `<html>` element. Add or remove it to switch themes:
694
+
695
+ ```tsx
696
+ // Toggle dark mode
697
+ document.documentElement.classList.toggle("dark");
698
+ ```
699
+
700
+ A simple React hook to manage this:
701
+
702
+ ```tsx
703
+ function useDarkMode() {
704
+ const [isDark, setIsDark] = useState(() =>
705
+ document.documentElement.classList.contains("dark"),
706
+ );
707
+
708
+ const toggle = () => {
709
+ document.documentElement.classList.toggle("dark");
710
+ setIsDark((prev) => !prev);
711
+ localStorage.setItem("theme", isDark ? "light" : "dark");
712
+ };
713
+
714
+ // Restore on mount
715
+ useEffect(() => {
716
+ const saved = localStorage.getItem("theme");
717
+ const prefersDark = window.matchMedia(
718
+ "(prefers-color-scheme: dark)",
719
+ ).matches;
720
+ if (saved === "dark" || (!saved && prefersDark)) {
721
+ document.documentElement.classList.add("dark");
722
+ setIsDark(true);
723
+ }
724
+ }, []);
725
+
726
+ return { isDark, toggle };
727
+ }
728
+
729
+ // Usage
730
+ function Header() {
731
+ const { isDark, toggle } = useDarkMode();
732
+ return (
733
+ <Button variant="ghost" size="icon" onClick={toggle}>
734
+ {isDark ? <Sun /> : <Moon />}
735
+ </Button>
736
+ );
737
+ }
738
+ ```
739
+
740
+ Dark mode overrides the glass variables and all semantic tokens automatically — no extra setup needed.
741
+
742
+ ---
743
+
744
+ ## TypeScript
745
+
746
+ All components are fully typed. Props interfaces are exported:
747
+
748
+ ```tsx
749
+ import type {
750
+ ButtonProps,
751
+ CardProps,
752
+ InputProps,
753
+ BadgeProps,
754
+ StatCardProps,
755
+ EmptyStateProps,
756
+ PaginationProps,
757
+ SkeletonProps,
758
+ } from "yems-ui";
759
+ ```
760
+
761
+ ---
150
762
 
151
- The library includes these brand colors as CSS variables:
763
+ ## Framework notes
152
764
 
153
- | Variable | Color | Hex |
154
- | ------------------------ | --------- | --------- |
155
- | `--color-true-azure` | Primary | `#5000ab` |
156
- | `--color-dark-amethyst` | Secondary | `#1c0636` |
157
- | `--color-sunflower-gold` | Accent | `#e3b23c` |
158
- | `--color-autumn-ember` | Ember | `#bb4d00` |
159
- | `--color-fresh-sky` | Sky | `#4ea5d9` |
160
- | `--color-seasalt` | Light | `#fafafa` |
765
+ | Framework | Notes |
766
+ | ------------------------ | --------------------------------------------------------------------------------- |
767
+ | **Vite** | Full support. See setup above. |
768
+ | **Next.js App Router** | Add `"use client"` to files using hooks or event handlers. CSS setup is the same. |
769
+ | **Next.js Pages Router** | Import styles in `_app.tsx`. Works without `"use client"`. |
770
+ | **Remix** | Import styles in `root.tsx` links export. |
771
+ | **Astro** | Use inside `.tsx` components with `client:load` or `client:visible`. |
161
772
 
162
773
  ---
163
774
 
164
- ## 📝 License
775
+ ## License
165
776
 
166
- MIT © Sodiq Ogundairo
777
+ MIT © [Sodiq Ogundairo](https://github.com/SodiqOgundairo)