@empathyds/skills 1.0.2

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 ADDED
@@ -0,0 +1,51 @@
1
+ # @empathyds/skills
2
+
3
+ Install the Empathy DS skill for Claude — build beautiful Vue 3 apps with AI.
4
+
5
+ ## Quick Start
6
+
7
+ ```bash
8
+ npx @empathyds/skills
9
+ ```
10
+
11
+ This launches an interactive installer that lets you:
12
+
13
+ 1. **Install skill files locally** — for Claude Code or local development
14
+ 2. **Export a .skill package** — for uploading to claude.ai
15
+ 3. **Setup MCP server config** — for live component docs access
16
+
17
+ ## Commands
18
+
19
+ ```bash
20
+ # Interactive mode
21
+ npx @empathyds/skills
22
+
23
+ # Install skill files to a directory (default: .agent/skills/empathy-ds)
24
+ npx @empathyds/skills install
25
+ npx @empathyds/skills install ./my-skills/empathy
26
+
27
+ # Generate MCP config
28
+ npx @empathyds/skills mcp
29
+
30
+ # Help
31
+ npx @empathyds/skills help
32
+ ```
33
+
34
+ ## What's Included
35
+
36
+ The skill teaches Claude to build beautiful Vue 3 applications using the Empathy DS component library:
37
+
38
+ - **Design thinking** — Claude picks an aesthetic direction before coding
39
+ - **60+ components** — Full API reference for all Empathy DS components
40
+ - **Page patterns** — Pre-built blocks for login, dashboard, settings, landing pages, and more
41
+ - **Visual polish** — Font pairings, spacing systems, hover states, transitions
42
+
43
+ ## Skills vs MCP
44
+
45
+ | | Skill | MCP |
46
+ |---|---|---|
47
+ | **What** | Static knowledge & patterns | Live data & tools |
48
+ | **Use for** | Design principles, templates, aesthetic guidance | Fetching latest docs, validating usage, searching components |
49
+ | **Updates** | Re-install skill file | Automatic (server-side) |
50
+
51
+ For the best experience, use both together.
package/bin/cli.js ADDED
@@ -0,0 +1,267 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { existsSync, mkdirSync, copyFileSync, readFileSync, writeFileSync, readdirSync } from "fs";
4
+ import { resolve, join, dirname } from "path";
5
+ import { fileURLToPath } from "url";
6
+ import { createInterface } from "readline";
7
+ import { execSync } from "child_process";
8
+
9
+ const __filename = fileURLToPath(import.meta.url);
10
+ const __dirname = dirname(__filename);
11
+ const SKILL_DIR = resolve(__dirname, "..", "skill");
12
+
13
+ // ── Colors ──────────────────────────────────────────
14
+ const c = {
15
+ reset: "\x1b[0m",
16
+ bold: "\x1b[1m",
17
+ dim: "\x1b[2m",
18
+ green: "\x1b[32m",
19
+ cyan: "\x1b[36m",
20
+ yellow: "\x1b[33m",
21
+ magenta: "\x1b[35m",
22
+ red: "\x1b[31m",
23
+ white: "\x1b[37m",
24
+ bgCyan: "\x1b[46m",
25
+ };
26
+
27
+ function log(msg = "") { console.log(msg); }
28
+ function success(msg) { log(`${c.green}✔${c.reset} ${msg}`); }
29
+ function info(msg) { log(`${c.cyan}ℹ${c.reset} ${msg}`); }
30
+ function warn(msg) { log(`${c.yellow}⚠${c.reset} ${msg}`); }
31
+ function error(msg) { log(`${c.red}✖${c.reset} ${msg}`); }
32
+
33
+ // ── Readline helper ─────────────────────────────────
34
+ function ask(question) {
35
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
36
+ return new Promise((res) => {
37
+ rl.question(question, (answer) => {
38
+ rl.close();
39
+ res(answer.trim());
40
+ });
41
+ });
42
+ }
43
+
44
+ // ── Copy directory recursively ──────────────────────
45
+ function copyDirSync(src, dest) {
46
+ if (!existsSync(dest)) mkdirSync(dest, { recursive: true });
47
+ for (const entry of readdirSync(src, { withFileTypes: true })) {
48
+ const srcPath = join(src, entry.name);
49
+ const destPath = join(dest, entry.name);
50
+ if (entry.isDirectory()) {
51
+ copyDirSync(srcPath, destPath);
52
+ } else {
53
+ copyFileSync(srcPath, destPath);
54
+ success(` ${destPath}`);
55
+ }
56
+ }
57
+ }
58
+
59
+ // ── Create .skill zip ───────────────────────────────
60
+ function createSkillPackage(outputDir) {
61
+ const outputPath = join(outputDir, "empathy-ds.skill");
62
+ try {
63
+ execSync(`cd "${resolve(SKILL_DIR, "..")}" && zip -r "${resolve(outputPath)}" skill/ -x "*.DS_Store"`, {
64
+ stdio: "pipe",
65
+ });
66
+ // Rename internal folder structure
67
+ } catch {
68
+ // Fallback: just copy the directory
69
+ warn("zip not available, copying raw skill files instead");
70
+ copyDirSync(SKILL_DIR, join(outputDir, "empathy-ds"));
71
+ return join(outputDir, "empathy-ds");
72
+ }
73
+ return outputPath;
74
+ }
75
+
76
+ // ── MCP Server config ───────────────────────────────
77
+ function generateMCPConfig(skillPath) {
78
+ return {
79
+ mcpServers: {
80
+ "empathy-ds-docs": {
81
+ command: "npx",
82
+ args: ["-y", "@anthropic-ai/mcp-server-filesystem", skillPath],
83
+ description: "Serves Empathy DS component docs and skill files to Claude"
84
+ }
85
+ }
86
+ };
87
+ }
88
+
89
+ // ── Main ────────────────────────────────────────────
90
+ async function main() {
91
+ const args = process.argv.slice(2);
92
+ const command = args[0];
93
+
94
+ log();
95
+ log(`${c.bold}${c.magenta} ✦ Empathy DS — Claude Skill Installer${c.reset}`);
96
+ log(`${c.dim} Build beautiful Vue 3 apps with AI${c.reset}`);
97
+ log();
98
+
99
+ // Direct command mode
100
+ if (command === "install" || command === "i") {
101
+ await installSkill(args[1]);
102
+ return;
103
+ }
104
+ if (command === "mcp") {
105
+ await setupMCP(args[1]);
106
+ return;
107
+ }
108
+ if (command === "help" || command === "--help" || command === "-h") {
109
+ showHelp();
110
+ return;
111
+ }
112
+
113
+ // Interactive mode
114
+ log(` ${c.bold}What would you like to do?${c.reset}`);
115
+ log();
116
+ log(` ${c.cyan}1${c.reset} Install skill files locally ${c.dim}(for Claude Code / local dev)${c.reset}`);
117
+ log(` ${c.cyan}2${c.reset} Export .skill package ${c.dim}(for uploading to claude.ai)${c.reset}`);
118
+ log(` ${c.cyan}3${c.reset} Setup MCP server config ${c.dim}(for live component docs)${c.reset}`);
119
+ log(` ${c.cyan}4${c.reset} All of the above`);
120
+ log();
121
+
122
+ const choice = await ask(` ${c.bold}Choose [1-4]:${c.reset} `);
123
+ log();
124
+
125
+ switch (choice) {
126
+ case "1":
127
+ await installSkill();
128
+ break;
129
+ case "2":
130
+ await exportPackage();
131
+ break;
132
+ case "3":
133
+ await setupMCP();
134
+ break;
135
+ case "4":
136
+ await installSkill();
137
+ log();
138
+ await exportPackage();
139
+ log();
140
+ await setupMCP();
141
+ break;
142
+ default:
143
+ error("Invalid choice. Run again and pick 1-4.");
144
+ process.exit(1);
145
+ }
146
+
147
+ log();
148
+ log(`${c.dim} ─────────────────────────────────────────${c.reset}`);
149
+ log(` ${c.green}${c.bold}Done!${c.reset} Happy building ✦`);
150
+ log();
151
+ }
152
+
153
+ // ── Install skill files to a directory ──────────────
154
+ async function installSkill(targetDir) {
155
+ if (!targetDir) {
156
+ targetDir = await ask(` ${c.bold}Install to directory${c.reset} ${c.dim}[.agent/skills/empathy-ds]:${c.reset} `);
157
+ if (!targetDir) targetDir = ".agent/skills/empathy-ds";
158
+ }
159
+
160
+ const dest = resolve(process.cwd(), targetDir);
161
+
162
+ if (existsSync(dest)) {
163
+ const overwrite = await ask(` ${c.yellow}Directory exists. Overwrite? [y/N]:${c.reset} `);
164
+ if (overwrite.toLowerCase() !== "y") {
165
+ info("Skipped installation.");
166
+ return;
167
+ }
168
+ }
169
+
170
+ log(` ${c.bold}Installing skill files...${c.reset}`);
171
+ copyDirSync(SKILL_DIR, dest);
172
+ log();
173
+ success(`Skill installed to ${c.cyan}${dest}${c.reset}`);
174
+ log();
175
+ info(`To use with Claude Code, point your skill config to this directory.`);
176
+ info(`SKILL.md + references/ are ready to go.`);
177
+ }
178
+
179
+ // ── Export .skill package ───────────────────────────
180
+ async function exportPackage() {
181
+ let outputDir = await ask(` ${c.bold}Export .skill file to${c.reset} ${c.dim}[.]:${c.reset} `);
182
+ if (!outputDir) outputDir = ".";
183
+
184
+ const dest = resolve(process.cwd(), outputDir);
185
+ if (!existsSync(dest)) mkdirSync(dest, { recursive: true });
186
+
187
+ log(` ${c.bold}Creating .skill package...${c.reset}`);
188
+ const outputPath = createSkillPackage(dest);
189
+ log();
190
+ success(`Package created: ${c.cyan}${outputPath}${c.reset}`);
191
+ log();
192
+ info(`Upload this file to ${c.bold}claude.ai → Settings → Skills${c.reset}`);
193
+ }
194
+
195
+ // ── Setup MCP ───────────────────────────────────────
196
+ async function setupMCP(skillPath) {
197
+ log(` ${c.bold}MCP Server Setup${c.reset}`);
198
+ log();
199
+ info("An MCP server can serve live component docs to Claude,");
200
+ info("so it always has access to the latest API reference.");
201
+ log();
202
+
203
+ if (!skillPath) {
204
+ skillPath = await ask(` ${c.bold}Path to skill directory${c.reset} ${c.dim}[.agent/skills/empathy-ds]:${c.reset} `);
205
+ if (!skillPath) skillPath = ".agent/skills/empathy-ds";
206
+ }
207
+
208
+ const resolvedPath = resolve(process.cwd(), skillPath);
209
+ const config = generateMCPConfig(resolvedPath);
210
+
211
+ log();
212
+ log(` ${c.bold}Add this to your Claude MCP config:${c.reset}`);
213
+ log();
214
+ log(c.dim + " ┌──────────────────────────────────────────────────");
215
+ const configStr = JSON.stringify(config, null, 2);
216
+ for (const line of configStr.split("\n")) {
217
+ log(`${c.dim} │${c.reset} ${c.cyan}${line}${c.reset}`);
218
+ }
219
+ log(c.dim + " └──────────────────────────────────────────────────" + c.reset);
220
+ log();
221
+
222
+ const save = await ask(` ${c.bold}Save config to file? [y/N]:${c.reset} `);
223
+ if (save.toLowerCase() === "y") {
224
+ const configPath = resolve(process.cwd(), "mcp-config.json");
225
+ writeFileSync(configPath, configStr + "\n");
226
+ success(`Config saved to ${c.cyan}${configPath}${c.reset}`);
227
+ log();
228
+ info(`Merge this into your Claude Desktop / Claude Code MCP config file.`);
229
+ info(`Typically at: ${c.dim}~/.claude/mcp_servers.json${c.reset} or ${c.dim}claude_desktop_config.json${c.reset}`);
230
+ }
231
+
232
+ log();
233
+ info(`${c.bold}For a richer MCP setup${c.reset}, consider building a custom MCP server that:`);
234
+ log(`${c.dim} • Searches components by name, category, or prop${c.reset}`);
235
+ log(`${c.dim} • Fetches live docs from empathyds.com${c.reset}`);
236
+ log(`${c.dim} • Validates component usage in Vue templates${c.reset}`);
237
+ log(`${c.dim} • Serves design tokens dynamically${c.reset}`);
238
+ }
239
+
240
+ // ── Help ────────────────────────────────────────────
241
+ function showHelp() {
242
+ log(` ${c.bold}Usage:${c.reset}`);
243
+ log();
244
+ log(` ${c.cyan}npx @empathyds/skills${c.reset} ${c.dim}Interactive mode${c.reset}`);
245
+ log(` ${c.cyan}npx @empathyds/skills install${c.reset} ${c.dim}Install skill files locally${c.reset}`);
246
+ log(` ${c.cyan}npx @empathyds/skills mcp${c.reset} ${c.dim}Setup MCP server config${c.reset}`);
247
+ log(` ${c.cyan}npx @empathyds/skills help${c.reset} ${c.dim}Show this help${c.reset}`);
248
+ log();
249
+ log(` ${c.bold}Options:${c.reset}`);
250
+ log();
251
+ log(` ${c.cyan}install [dir]${c.reset} Install skill files to a directory (default: .agent/skills/empathy-ds)`);
252
+ log(` ${c.cyan}mcp [dir]${c.reset} Generate MCP config pointing to skill directory`);
253
+ log();
254
+ log(` ${c.bold}Examples:${c.reset}`);
255
+ log();
256
+ log(` ${c.dim}# Interactive — choose what to do${c.reset}`);
257
+ log(` ${c.cyan}npx @empathyds/skills${c.reset}`);
258
+ log();
259
+ log(` ${c.dim}# Quick install to custom path${c.reset}`);
260
+ log(` ${c.cyan}npx @empathyds/skills install ./my-skills/empathy${c.reset}`);
261
+ log();
262
+ }
263
+
264
+ main().catch((err) => {
265
+ error(err.message);
266
+ process.exit(1);
267
+ });
package/package.json ADDED
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "@empathyds/skills",
3
+ "version": "1.0.2",
4
+ "description": "Install the Empathy DS skill for Claude — build beautiful Vue 3 apps with AI",
5
+ "type": "module",
6
+ "bin": {
7
+ "empathyds-skills": "./bin/cli.js"
8
+ },
9
+ "files": [
10
+ "bin/",
11
+ "skill/",
12
+ "README.md"
13
+ ],
14
+ "engines": {
15
+ "node": ">=16.0.0"
16
+ },
17
+ "keywords": [
18
+ "empathy-ds",
19
+ "claude",
20
+ "skill",
21
+ "design-system",
22
+ "vue3",
23
+ "mcp"
24
+ ],
25
+ "license": "MIT"
26
+ }
package/skill/SKILL.md ADDED
@@ -0,0 +1,184 @@
1
+ ---
2
+ name: empathy-ds
3
+ description: Build beautiful, accessible Vue 3 web applications using the Empathy DS design system. Use this skill when users want to create websites, web apps, UI components, or interfaces using Vue 3 with the Empathy DS component library. Triggers include requests to build Vue apps, create modern UIs, design web pages, build dashboards, landing pages, forms, admin panels, or use the organization's design system. Also triggers when styling, beautifying, or improving the look of any Vue 3 web UI using Empathy DS components. Do NOT use for React, plain HTML, or non-Vue projects unless specifically asked to use Empathy DS.
4
+ ---
5
+
6
+ # Empathy DS Skill
7
+
8
+ Build professional, visually striking Vue 3 applications using the Empathy DS design system — an opinionated, accessible component library with 60+ production-ready components.
9
+
10
+ ## Design Thinking (Do This First)
11
+
12
+ Before writing any code, commit to a clear aesthetic direction. Empathy DS provides the components — **you provide the creative vision**.
13
+
14
+ ### 1. Understand the Context
15
+ - **Purpose**: What problem does this interface solve? Who is the audience?
16
+ - **Tone**: Professional/corporate, playful/creative, minimal/editorial, warm/organic, bold/modern?
17
+ - **Density**: Data-heavy dashboard or spacious marketing page?
18
+
19
+ ### 2. Choose an Aesthetic Direction
20
+ Pick a cohesive visual identity and execute it with intention:
21
+ - **Color strategy**: Use 1 dominant color + 1-2 accents. Empathy DS tokens (`bg-primary`, `text-muted-foreground`) are available as Tailwind utilities.
22
+ - **Typography**: Pair fonts thoughtfully — a distinctive display font with a clean body font. Import from Google Fonts via `<link>` or `@import`.
23
+ - **Spacing**: Generous whitespace for marketing/editorial; tighter spacing for data-heavy UIs. Use consistent rhythm (multiples of 4px or 8px).
24
+ - **Layout**: Choose asymmetric, grid-breaking, or structured grid layouts depending on context. CSS Grid and Flexbox are your primary tools.
25
+
26
+ ### 3. Avoid Generic "AI Slop"
27
+ - Never default to the same fonts, colors, or layouts for every project
28
+ - Avoid: purple-gradient-on-white, Inter/Roboto everywhere, identical card grids
29
+ - Do: Match aesthetic to context, vary between light/dark themes, surprise with layout choices
30
+
31
+ ## Setup
32
+
33
+ ```ts
34
+ // main.ts
35
+ import { createApp } from "vue";
36
+ import App from "./App.vue";
37
+ import "@empathyds/vue/style.css"; // Required — always import first
38
+
39
+ createApp(App).mount("#app");
40
+ ```
41
+
42
+ For monorepo setups with bundler resolution issues, add Vite alias:
43
+ ```ts
44
+ // vite.config.ts
45
+ import { defineConfig } from "vite";
46
+ import path from "path";
47
+ export default defineConfig({
48
+ resolve: {
49
+ alias: {
50
+ "@empathyds/vue": path.resolve(__dirname, "node_modules/@empathyds/vue/dist/empathy-ds.es.js"),
51
+ },
52
+ },
53
+ });
54
+ ```
55
+
56
+ ## Component Library Overview
57
+
58
+ All imports come from `@empathyds/vue`. The library provides:
59
+
60
+ | Category | Components |
61
+ |---|---|
62
+ | **Forms** | Input, Select, Checkbox, RadioGroup, Switch, Textarea, Form, Field, Label, NumberField, PinInput, TagsInput, InputOtp, InputGroup |
63
+ | **Layout** | Card (+ CardHeader, CardTitle, CardDescription, CardContent, CardFooter), Separator, Sidebar, AspectRatio, Resizable, ScrollArea, Sheet |
64
+ | **Navigation** | Breadcrumb, NavigationMenu, Menubar, Tabs, Stepper, Pagination |
65
+ | **Feedback** | Alert, AlertDialog, Dialog, Drawer, Toast (Sonner), Spinner, Progress, Skeleton, Empty |
66
+ | **Data Display** | Table (+ TableHeader, TableBody, TableRow, TableHead, TableCell), Avatar, Badge, Chart, Carousel, Calendar, RangeCalendar |
67
+ | **Overlay** | Popover, Tooltip, HoverCard, ContextMenu, DropdownMenu, Command |
68
+ | **Interactive** | Button, ButtonGroup, Toggle, ToggleGroup, Slider, Collapsible, Accordion |
69
+
70
+ For detailed component props, slots, and variants: see `references/component-api.md`
71
+ For pre-built page patterns and blocks: see `references/blocks.md`
72
+
73
+ ## Critical Rules
74
+
75
+ 1. **Import `@empathyds/vue/style.css`** in main entry file — without it, nothing is styled.
76
+
77
+ 2. **Use `<script setup>`** syntax (Vue 3 Composition API).
78
+
79
+ 3. **Import every component explicitly** — no global registration:
80
+ ```vue
81
+ <script setup>
82
+ import { Button, Card, CardContent, CardHeader, CardTitle } from "@empathyds/vue";
83
+ </script>
84
+ ```
85
+
86
+ 4. **Use Tailwind classes for all styling** — never use inline `style=""` attributes. Empathy DS design tokens are available as Tailwind utilities (`bg-primary`, `text-muted-foreground`, `border-input`, etc.).
87
+
88
+ ## Layout & Styling Patterns
89
+
90
+ ### Page Shell Pattern
91
+ Every page needs a shell:
92
+
93
+ ```vue
94
+ <template>
95
+ <div class="min-h-screen bg-background text-foreground font-sans">
96
+ <header class="px-8 py-4 border-b border-border">
97
+ <slot name="header" />
98
+ </header>
99
+ <main class="max-w-7xl mx-auto p-8">
100
+ <slot />
101
+ </main>
102
+ </div>
103
+ </template>
104
+ ```
105
+
106
+ ### Card Grid Pattern
107
+ Responsive columns with Tailwind grid:
108
+ ```vue
109
+ <div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
110
+ <Card v-for="item in items" :key="item.id">...</Card>
111
+ </div>
112
+ ```
113
+
114
+ ### Dashboard Layout Pattern
115
+ Sidebar + content area:
116
+ ```vue
117
+ <div class="grid grid-cols-[260px_1fr] min-h-screen">
118
+ <aside class="border-r border-border p-6">
119
+ <!-- Navigation -->
120
+ </aside>
121
+ <div class="p-8 overflow-y-auto">
122
+ <!-- Content -->
123
+ </div>
124
+ </div>
125
+ ```
126
+
127
+ ### Spacing System
128
+ Use Tailwind's spacing scale consistently: `gap-1` (0.25rem), `gap-2` (0.5rem), `gap-3` (0.75rem), `gap-4` (1rem), `gap-6` (1.5rem), `gap-8` (2rem), `gap-12` (3rem), `gap-16` (4rem). Apply via Tailwind utility classes (`p-`, `m-`, `gap-`, `space-y-`, etc.).
129
+
130
+ ### Font Loading
131
+ Import distinctive fonts to avoid generic output:
132
+ ```html
133
+ <link href="https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;700&family=DM+Serif+Display&display=swap" rel="stylesheet">
134
+ ```
135
+ Then apply via CSS:
136
+ ```css
137
+ :root {
138
+ --font-heading: 'DM Serif Display', serif;
139
+ --font-body: 'DM Sans', sans-serif;
140
+ }
141
+ ```
142
+
143
+ **Vary fonts per project.** Good pairings to rotate between:
144
+ - DM Serif Display + DM Sans (warm editorial)
145
+ - Playfair Display + Source Sans 3 (classic elegance)
146
+ - Space Grotesk + Inter (modern tech)
147
+ - Fraunces + Work Sans (friendly organic)
148
+ - Instrument Serif + Instrument Sans (refined minimal)
149
+ - Sora + Nunito (soft modern)
150
+ - Bebas Neue + Lato (bold impact)
151
+
152
+ ## Visual Polish Checklist
153
+
154
+ Before delivering any output, verify:
155
+
156
+ - [ ] Custom fonts loaded (not just system defaults)
157
+ - [ ] Consistent color palette (2-4 colors max, using Tailwind + DS tokens)
158
+ - [ ] All styling uses Tailwind classes (no inline `style=""` attributes)
159
+ - [ ] Spacing is consistent and rhythmic
160
+ - [ ] Hover states on interactive elements (`hover:` utilities)
161
+ - [ ] Transitions on state changes (`transition-all duration-200 ease-in-out`)
162
+ - [ ] Visual hierarchy clear (headings > subheadings > body > muted text)
163
+ - [ ] Empty states handled gracefully (use `Empty` component)
164
+ - [ ] Responsive — works on mobile widths too
165
+
166
+ ## Tailwind Integration
167
+
168
+ Tailwind is the default styling approach. Always use Tailwind utility classes instead of inline styles or scoped CSS.
169
+
170
+ ```ts
171
+ // main.ts — import order matters
172
+ import "@empathyds/vue/style.css"; // First
173
+ import "./assets/main.css"; // Tailwind second
174
+ ```
175
+
176
+ Empathy DS design tokens (`bg-primary`, `text-muted-foreground`, `border-input`, etc.) are available as Tailwind utilities. Use them for consistent theming.
177
+
178
+ ## Additional Resources
179
+
180
+ - Full docs: https://empathyds.com/getting-started
181
+ - Components: https://empathyds.com/components/button
182
+ - Pre-built blocks: https://empathyds.com/blocks/login-form
183
+ - Design tokens: https://empathyds.com/tokens/colors
184
+ - LLM context: https://empathyds.com/llms.txt
@@ -0,0 +1,451 @@
1
+ # Pre-Built Blocks & Page Patterns
2
+
3
+ Copy and adapt these patterns as starting points. Always customize colors, fonts, and spacing to match the project's aesthetic direction. Use Tailwind utility classes for all styling.
4
+
5
+ ## Table of Contents
6
+ - [Login / Auth Form](#login--auth-form)
7
+ - [Dashboard Layout](#dashboard-layout)
8
+ - [Data Table Page](#data-table-page)
9
+ - [Settings Page](#settings-page)
10
+ - [Landing / Marketing Page](#landing--marketing-page)
11
+ - [Profile / Account Page](#profile--account-page)
12
+
13
+ ---
14
+
15
+ ## Login / Auth Form
16
+
17
+ Centered card with logo, form fields, and social auth options.
18
+
19
+ ```vue
20
+ <script setup>
21
+ import { ref } from "vue";
22
+ import {
23
+ Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter,
24
+ Button, Input, Label, Field, Form, Separator, Checkbox
25
+ } from "@empathyds/vue";
26
+
27
+ const email = ref("");
28
+ const password = ref("");
29
+ const remember = ref(false);
30
+ </script>
31
+
32
+ <template>
33
+ <div class="min-h-screen flex items-center justify-center bg-muted p-4">
34
+ <Card class="w-full max-w-[420px]">
35
+ <CardHeader class="text-center">
36
+ <!-- Replace with your logo/brand -->
37
+ <div class="text-2xl font-bold mb-2">✦ Brand</div>
38
+ <CardTitle class="text-xl">Welcome back</CardTitle>
39
+ <CardDescription>Enter your credentials to continue</CardDescription>
40
+ </CardHeader>
41
+ <CardContent>
42
+ <Form @submit.prevent="handleLogin" class="flex flex-col gap-4">
43
+ <Field>
44
+ <Label for="email">Email</Label>
45
+ <Input id="email" v-model="email" type="email" placeholder="you@company.com" />
46
+ </Field>
47
+ <Field>
48
+ <div class="flex justify-between items-center">
49
+ <Label for="password">Password</Label>
50
+ <a href="#" class="text-xs text-primary hover:underline">Forgot?</a>
51
+ </div>
52
+ <Input id="password" v-model="password" type="password" />
53
+ </Field>
54
+ <Field class="flex items-center gap-2">
55
+ <Checkbox id="remember" v-model:checked="remember" />
56
+ <Label for="remember" class="text-sm">Remember me</Label>
57
+ </Field>
58
+ <Button type="submit" class="w-full">Sign in</Button>
59
+ </Form>
60
+
61
+ <div class="flex items-center gap-4 my-6">
62
+ <Separator class="flex-1" />
63
+ <span class="text-xs text-muted-foreground">OR</span>
64
+ <Separator class="flex-1" />
65
+ </div>
66
+
67
+ <Button variant="outline" class="w-full">Continue with Google</Button>
68
+ </CardContent>
69
+ <CardFooter class="justify-center">
70
+ <p class="text-xs text-muted-foreground">
71
+ Don't have an account? <a href="#" class="text-primary font-medium hover:underline">Sign up</a>
72
+ </p>
73
+ </CardFooter>
74
+ </Card>
75
+ </div>
76
+ </template>
77
+ ```
78
+
79
+ ---
80
+
81
+ ## Dashboard Layout
82
+
83
+ Sidebar navigation + header + content area with stat cards and data table.
84
+
85
+ ```vue
86
+ <script setup>
87
+ import { ref } from "vue";
88
+ import {
89
+ Card, CardContent,
90
+ Button, Avatar, AvatarFallback, Badge,
91
+ Table, TableHeader, TableBody, TableRow, TableHead, TableCell,
92
+ DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem,
93
+ Tabs, TabsList, TabsTrigger, TabsContent,
94
+ } from "@empathyds/vue";
95
+
96
+ const navItems = [
97
+ { label: "Dashboard", icon: "📊", active: true },
98
+ { label: "Projects", icon: "📁", active: false },
99
+ { label: "Team", icon: "👥", active: false },
100
+ { label: "Settings", icon: "⚙️", active: false },
101
+ ];
102
+ </script>
103
+
104
+ <template>
105
+ <div class="grid grid-cols-[260px_1fr] min-h-screen font-sans">
106
+ <!-- Sidebar -->
107
+ <aside class="border-r border-border p-6 flex flex-col gap-1">
108
+ <div class="font-bold text-lg px-2 mb-4">✦ AppName</div>
109
+ <button
110
+ v-for="item in navItems"
111
+ :key="item.label"
112
+ class="flex items-center gap-3 px-3 py-2.5 rounded-lg text-sm text-left w-full transition-colors"
113
+ :class="item.active ? 'bg-primary text-primary-foreground' : 'text-foreground hover:bg-muted'"
114
+ >
115
+ <span>{{ item.icon }}</span>
116
+ {{ item.label }}
117
+ </button>
118
+ </aside>
119
+
120
+ <!-- Main Content -->
121
+ <div>
122
+ <!-- Top Header -->
123
+ <header class="flex justify-between items-center px-8 py-4 border-b border-border">
124
+ <h1 class="text-xl font-semibold">Dashboard</h1>
125
+ <Avatar>
126
+ <AvatarFallback>JD</AvatarFallback>
127
+ </Avatar>
128
+ </header>
129
+
130
+ <!-- Dashboard Content -->
131
+ <div class="p-8">
132
+ <!-- Stat Cards Row -->
133
+ <div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4 mb-8">
134
+ <Card v-for="stat in stats" :key="stat.label">
135
+ <CardContent>
136
+ <div class="text-xs text-muted-foreground mb-1">{{ stat.label }}</div>
137
+ <div class="text-3xl font-bold">{{ stat.value }}</div>
138
+ <div
139
+ class="text-xs mt-1"
140
+ :class="stat.trend > 0 ? 'text-green-600' : 'text-red-600'"
141
+ >
142
+ {{ stat.trend > 0 ? '↑' : '↓' }} {{ Math.abs(stat.trend) }}% from last month
143
+ </div>
144
+ </CardContent>
145
+ </Card>
146
+ </div>
147
+
148
+ <!-- Tabs with Table -->
149
+ <Tabs default-value="recent">
150
+ <TabsList>
151
+ <TabsTrigger value="recent">Recent</TabsTrigger>
152
+ <TabsTrigger value="popular">Popular</TabsTrigger>
153
+ </TabsList>
154
+ <TabsContent value="recent">
155
+ <!-- Insert Table component here -->
156
+ </TabsContent>
157
+ </Tabs>
158
+ </div>
159
+ </div>
160
+ </div>
161
+ </template>
162
+ ```
163
+
164
+ ---
165
+
166
+ ## Data Table Page
167
+
168
+ Full-featured data table with search, filters, and pagination.
169
+
170
+ ```vue
171
+ <script setup>
172
+ import { ref, computed } from "vue";
173
+ import {
174
+ Card, CardContent,
175
+ Button, Input, Badge,
176
+ Table, TableHeader, TableBody, TableRow, TableHead, TableCell,
177
+ DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem,
178
+ Select, SelectTrigger, SelectValue, SelectContent, SelectItem,
179
+ } from "@empathyds/vue";
180
+
181
+ const search = ref("");
182
+ const statusFilter = ref("all");
183
+ </script>
184
+
185
+ <template>
186
+ <div class="p-8 max-w-7xl mx-auto">
187
+ <div class="flex justify-between items-center mb-6">
188
+ <h1 class="text-2xl font-bold">Orders</h1>
189
+ <Button>+ New Order</Button>
190
+ </div>
191
+
192
+ <!-- Filters Row -->
193
+ <div class="flex gap-3 mb-4">
194
+ <Input v-model="search" placeholder="Search orders..." class="max-w-xs" />
195
+ <Select v-model="statusFilter">
196
+ <SelectTrigger class="w-40">
197
+ <SelectValue placeholder="Status" />
198
+ </SelectTrigger>
199
+ <SelectContent>
200
+ <SelectItem value="all">All Status</SelectItem>
201
+ <SelectItem value="active">Active</SelectItem>
202
+ <SelectItem value="completed">Completed</SelectItem>
203
+ <SelectItem value="cancelled">Cancelled</SelectItem>
204
+ </SelectContent>
205
+ </Select>
206
+ </div>
207
+
208
+ <!-- Table -->
209
+ <Card>
210
+ <CardContent class="p-0">
211
+ <Table>
212
+ <TableHeader>
213
+ <TableRow>
214
+ <TableHead>Order</TableHead>
215
+ <TableHead>Customer</TableHead>
216
+ <TableHead>Status</TableHead>
217
+ <TableHead class="text-right">Amount</TableHead>
218
+ <TableHead class="w-12"></TableHead>
219
+ </TableRow>
220
+ </TableHeader>
221
+ <TableBody>
222
+ <TableRow v-for="order in filteredOrders" :key="order.id">
223
+ <TableCell class="font-medium">#{{ order.id }}</TableCell>
224
+ <TableCell>{{ order.customer }}</TableCell>
225
+ <TableCell>
226
+ <Badge :variant="order.status === 'active' ? 'default' : order.status === 'completed' ? 'secondary' : 'destructive'">
227
+ {{ order.status }}
228
+ </Badge>
229
+ </TableCell>
230
+ <TableCell class="text-right">{{ order.amount }}</TableCell>
231
+ <TableCell>
232
+ <DropdownMenu>
233
+ <DropdownMenuTrigger as-child>
234
+ <Button variant="ghost" size="icon">⋯</Button>
235
+ </DropdownMenuTrigger>
236
+ <DropdownMenuContent>
237
+ <DropdownMenuItem>View</DropdownMenuItem>
238
+ <DropdownMenuItem>Edit</DropdownMenuItem>
239
+ </DropdownMenuContent>
240
+ </DropdownMenu>
241
+ </TableCell>
242
+ </TableRow>
243
+ </TableBody>
244
+ </Table>
245
+ </CardContent>
246
+ </Card>
247
+ </div>
248
+ </template>
249
+ ```
250
+
251
+ ---
252
+
253
+ ## Settings Page
254
+
255
+ Sectioned form layout with cards for each settings group.
256
+
257
+ ```vue
258
+ <script setup>
259
+ import { ref } from "vue";
260
+ import {
261
+ Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter,
262
+ Button, Input, Textarea, Label, Field, Form, Separator, Switch,
263
+ } from "@empathyds/vue";
264
+
265
+ const name = ref("");
266
+ const bio = ref("");
267
+ const emailNotifs = ref(true);
268
+ const pushNotifs = ref(false);
269
+ </script>
270
+
271
+ <template>
272
+ <div class="max-w-2xl mx-auto p-8">
273
+ <h1 class="text-2xl font-bold mb-2">Settings</h1>
274
+ <p class="text-muted-foreground mb-8">Manage your account preferences.</p>
275
+
276
+ <div class="flex flex-col gap-6">
277
+ <!-- Profile Section -->
278
+ <Card>
279
+ <CardHeader>
280
+ <CardTitle class="text-base">Profile</CardTitle>
281
+ <CardDescription>Your public-facing information.</CardDescription>
282
+ </CardHeader>
283
+ <CardContent class="flex flex-col gap-4">
284
+ <Field>
285
+ <Label for="name">Display name</Label>
286
+ <Input id="name" v-model="name" />
287
+ </Field>
288
+ <Field>
289
+ <Label for="bio">Bio</Label>
290
+ <Textarea id="bio" v-model="bio" rows="3" />
291
+ </Field>
292
+ </CardContent>
293
+ <CardFooter class="border-t border-border justify-end">
294
+ <Button>Save changes</Button>
295
+ </CardFooter>
296
+ </Card>
297
+
298
+ <!-- Notifications Section -->
299
+ <Card>
300
+ <CardHeader>
301
+ <CardTitle class="text-base">Notifications</CardTitle>
302
+ </CardHeader>
303
+ <CardContent class="flex flex-col gap-3">
304
+ <div class="flex justify-between items-center">
305
+ <div>
306
+ <div class="text-sm font-medium">Email notifications</div>
307
+ <div class="text-xs text-muted-foreground">Receive updates via email</div>
308
+ </div>
309
+ <Switch v-model:checked="emailNotifs" />
310
+ </div>
311
+ <Separator />
312
+ <div class="flex justify-between items-center">
313
+ <div>
314
+ <div class="text-sm font-medium">Push notifications</div>
315
+ <div class="text-xs text-muted-foreground">Browser push alerts</div>
316
+ </div>
317
+ <Switch v-model:checked="pushNotifs" />
318
+ </div>
319
+ </CardContent>
320
+ </Card>
321
+ </div>
322
+ </div>
323
+ </template>
324
+ ```
325
+
326
+ ---
327
+
328
+ ## Landing / Marketing Page
329
+
330
+ Hero section + feature grid + CTA. Use bold typography and generous spacing.
331
+
332
+ ```vue
333
+ <script setup>
334
+ import {
335
+ Card, CardContent, Button, Badge, Separator,
336
+ } from "@empathyds/vue";
337
+
338
+ const features = [
339
+ { icon: "⚡", title: "Lightning Fast", description: "Built for speed with optimized rendering and lazy loading." },
340
+ { icon: "🎨", title: "Beautiful by Default", description: "Opinionated design tokens ensure consistency across your app." },
341
+ { icon: "♿", title: "Accessible", description: "WCAG compliant components that work for everyone." },
342
+ ];
343
+ </script>
344
+
345
+ <template>
346
+ <div class="font-sans">
347
+ <!-- Hero -->
348
+ <section class="text-center py-24 px-8 max-w-3xl mx-auto">
349
+ <Badge variant="secondary" class="mb-6">Now in Beta</Badge>
350
+ <h1 class="font-heading text-5xl lg:text-7xl leading-tight font-bold mb-6">
351
+ Build something<br>people remember
352
+ </h1>
353
+ <p class="text-lg text-muted-foreground max-w-xl mx-auto mb-8 leading-relaxed">
354
+ A short, compelling description of what the product does and why it matters.
355
+ </p>
356
+ <div class="flex gap-3 justify-center">
357
+ <Button size="lg">Get Started</Button>
358
+ <Button size="lg" variant="outline">Learn More</Button>
359
+ </div>
360
+ </section>
361
+
362
+ <Separator />
363
+
364
+ <!-- Features Grid -->
365
+ <section class="py-16 px-8 max-w-5xl mx-auto">
366
+ <div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
367
+ <Card v-for="feature in features" :key="feature.title">
368
+ <CardContent>
369
+ <div class="text-3xl mb-3">{{ feature.icon }}</div>
370
+ <h3 class="font-semibold mb-2">{{ feature.title }}</h3>
371
+ <p class="text-muted-foreground text-sm leading-relaxed">
372
+ {{ feature.description }}
373
+ </p>
374
+ </CardContent>
375
+ </Card>
376
+ </div>
377
+ </section>
378
+ </div>
379
+ </template>
380
+ ```
381
+
382
+ ---
383
+
384
+ ## Profile / Account Page
385
+
386
+ Header with avatar + tabs for different sections.
387
+
388
+ ```vue
389
+ <script setup>
390
+ import { ref } from "vue";
391
+ import {
392
+ Avatar, AvatarImage, AvatarFallback, Button,
393
+ Tabs, TabsList, TabsTrigger, TabsContent,
394
+ } from "@empathyds/vue";
395
+
396
+ const user = ref({
397
+ name: "Jane Doe",
398
+ email: "jane@company.com",
399
+ initials: "JD",
400
+ avatar: "",
401
+ });
402
+ </script>
403
+
404
+ <template>
405
+ <div class="max-w-3xl mx-auto p-8">
406
+ <!-- Profile Header -->
407
+ <div class="flex items-center gap-6 mb-8">
408
+ <Avatar class="w-20 h-20 text-2xl">
409
+ <AvatarImage :src="user.avatar" />
410
+ <AvatarFallback>{{ user.initials }}</AvatarFallback>
411
+ </Avatar>
412
+ <div>
413
+ <h1 class="text-2xl font-bold">{{ user.name }}</h1>
414
+ <p class="text-muted-foreground">{{ user.email }}</p>
415
+ </div>
416
+ <Button variant="outline" class="ml-auto">Edit Profile</Button>
417
+ </div>
418
+
419
+ <!-- Tabbed Content -->
420
+ <Tabs default-value="overview">
421
+ <TabsList>
422
+ <TabsTrigger value="overview">Overview</TabsTrigger>
423
+ <TabsTrigger value="activity">Activity</TabsTrigger>
424
+ <TabsTrigger value="billing">Billing</TabsTrigger>
425
+ </TabsList>
426
+ <TabsContent value="overview" class="mt-6">
427
+ <!-- Overview cards -->
428
+ </TabsContent>
429
+ <TabsContent value="activity" class="mt-6">
430
+ <!-- Activity feed -->
431
+ </TabsContent>
432
+ <TabsContent value="billing" class="mt-6">
433
+ <!-- Billing info -->
434
+ </TabsContent>
435
+ </Tabs>
436
+ </div>
437
+ </template>
438
+ ```
439
+
440
+ ---
441
+
442
+ ## Tips for Beautiful Output
443
+
444
+ 1. **Custom fonts matter.** Load a distinctive font pair via Google Fonts and apply with Tailwind's `font-heading` / `font-sans` classes.
445
+ 2. **Color consistency.** Use Empathy DS design tokens (`bg-primary`, `text-muted-foreground`, `border-border`) throughout.
446
+ 3. **Whitespace is design.** When in doubt, add more `p-`, `m-`, and `gap-` values.
447
+ 4. **Transitions everywhere.** Add `transition-all duration-150 ease-in-out` to interactive elements.
448
+ 5. **Hover states.** Every clickable element needs `hover:` utilities for visual feedback.
449
+ 6. **Border radius consistency.** Pick one and use it (`rounded-lg` or `rounded-xl`).
450
+ 7. **Muted text for secondary info.** Use `text-muted-foreground` liberally.
451
+ 8. **Never use inline styles.** Always use Tailwind utility classes.
@@ -0,0 +1,374 @@
1
+ # Component API Reference
2
+
3
+ Quick-reference for Empathy DS component props, slots, and usage. All imports from `@empathyds/vue`.
4
+
5
+ ## Table of Contents
6
+ - [Button](#button)
7
+ - [Card](#card)
8
+ - [Input & Forms](#input--forms)
9
+ - [Select](#select)
10
+ - [Table](#table)
11
+ - [Dialog & Drawer](#dialog--drawer)
12
+ - [Tabs](#tabs)
13
+ - [Alert](#alert)
14
+ - [Badge](#badge)
15
+ - [Avatar](#avatar)
16
+ - [Navigation Menu](#navigation-menu)
17
+ - [Sidebar](#sidebar)
18
+ - [Accordion](#accordion)
19
+ - [Toast (Sonner)](#toast-sonner)
20
+ - [Tooltip & Popover](#tooltip--popover)
21
+ - [Progress & Skeleton](#progress--skeleton)
22
+ - [Chart](#chart)
23
+
24
+ ---
25
+
26
+ ## Button
27
+
28
+ ```vue
29
+ <script setup>
30
+ import { Button } from "@empathyds/vue";
31
+ </script>
32
+ <template>
33
+ <Button variant="default" size="default">Click me</Button>
34
+ </template>
35
+ ```
36
+
37
+ | Prop | Values | Default |
38
+ |------|--------|---------|
39
+ | `variant` | `default`, `destructive`, `outline`, `secondary`, `ghost`, `link` | `default` |
40
+ | `size` | `default`, `sm`, `lg`, `icon` | `default` |
41
+ | `disabled` | `boolean` | `false` |
42
+ | `as-child` | `boolean` | `false` |
43
+
44
+ **ButtonGroup**: Wrap multiple Buttons in `<ButtonGroup>` for connected button sets.
45
+
46
+ ## Card
47
+
48
+ ```vue
49
+ <script setup>
50
+ import { Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter } from "@empathyds/vue";
51
+ </script>
52
+ <template>
53
+ <Card>
54
+ <CardHeader>
55
+ <CardTitle>Title</CardTitle>
56
+ <CardDescription>Optional description text</CardDescription>
57
+ </CardHeader>
58
+ <CardContent>
59
+ Content goes here
60
+ </CardContent>
61
+ <CardFooter>
62
+ <Button>Action</Button>
63
+ </CardFooter>
64
+ </Card>
65
+ </template>
66
+ ```
67
+
68
+ Sub-components: `CardHeader`, `CardTitle`, `CardDescription`, `CardContent`, `CardFooter`.
69
+
70
+ ## Input & Forms
71
+
72
+ ```vue
73
+ <script setup>
74
+ import { ref } from "vue";
75
+ import { Form, Field, Label, Input, Textarea, Checkbox, Switch, RadioGroup, RadioGroupItem } from "@empathyds/vue";
76
+
77
+ const email = ref("");
78
+ const agreed = ref(false);
79
+ </script>
80
+ <template>
81
+ <Form @submit.prevent="handleSubmit">
82
+ <Field>
83
+ <Label for="email">Email</Label>
84
+ <Input id="email" v-model="email" type="email" placeholder="you@example.com" />
85
+ </Field>
86
+
87
+ <Field class="flex items-center gap-2">
88
+ <Checkbox id="terms" v-model:checked="agreed" />
89
+ <Label for="terms">I agree to terms</Label>
90
+ </Field>
91
+
92
+ <Button type="submit">Submit</Button>
93
+ </Form>
94
+ </template>
95
+ ```
96
+
97
+ **Input props**: `type`, `placeholder`, `disabled`, `modelValue` (use `v-model`)
98
+ **Textarea props**: `placeholder`, `rows`, `disabled`, `modelValue`
99
+ **NumberField**: Numeric input with increment/decrement controls
100
+ **PinInput**: OTP-style segmented input
101
+ **TagsInput**: Multi-value tag entry
102
+ **InputGroup**: Group an Input with prefix/suffix elements
103
+
104
+ ## Select
105
+
106
+ ```vue
107
+ <script setup>
108
+ import { ref } from "vue";
109
+ import {
110
+ Select, SelectTrigger, SelectValue, SelectContent, SelectGroup,
111
+ SelectLabel, SelectItem, SelectSeparator
112
+ } from "@empathyds/vue";
113
+
114
+ const fruit = ref("");
115
+ </script>
116
+ <template>
117
+ <Select v-model="fruit">
118
+ <SelectTrigger class="w-[200px]">
119
+ <SelectValue placeholder="Pick a fruit" />
120
+ </SelectTrigger>
121
+ <SelectContent>
122
+ <SelectGroup>
123
+ <SelectLabel>Fruits</SelectLabel>
124
+ <SelectItem value="apple">Apple</SelectItem>
125
+ <SelectItem value="banana">Banana</SelectItem>
126
+ <SelectItem value="cherry">Cherry</SelectItem>
127
+ </SelectGroup>
128
+ </SelectContent>
129
+ </Select>
130
+ </template>
131
+ ```
132
+
133
+ ## Table
134
+
135
+ ```vue
136
+ <script setup>
137
+ import { Table, TableHeader, TableBody, TableRow, TableHead, TableCell } from "@empathyds/vue";
138
+ </script>
139
+ <template>
140
+ <Table>
141
+ <TableHeader>
142
+ <TableRow>
143
+ <TableHead>Name</TableHead>
144
+ <TableHead>Status</TableHead>
145
+ <TableHead class="text-right">Amount</TableHead>
146
+ </TableRow>
147
+ </TableHeader>
148
+ <TableBody>
149
+ <TableRow v-for="item in items" :key="item.id">
150
+ <TableCell class="font-medium">{{ item.name }}</TableCell>
151
+ <TableCell>{{ item.status }}</TableCell>
152
+ <TableCell class="text-right">{{ item.amount }}</TableCell>
153
+ </TableRow>
154
+ </TableBody>
155
+ </Table>
156
+ </template>
157
+ ```
158
+
159
+ ## Dialog & Drawer
160
+
161
+ ```vue
162
+ <script setup>
163
+ import {
164
+ Dialog, DialogTrigger, DialogContent, DialogHeader,
165
+ DialogTitle, DialogDescription, DialogFooter, DialogClose
166
+ } from "@empathyds/vue";
167
+ </script>
168
+ <template>
169
+ <Dialog>
170
+ <DialogTrigger as-child>
171
+ <Button>Open Dialog</Button>
172
+ </DialogTrigger>
173
+ <DialogContent>
174
+ <DialogHeader>
175
+ <DialogTitle>Are you sure?</DialogTitle>
176
+ <DialogDescription>This action cannot be undone.</DialogDescription>
177
+ </DialogHeader>
178
+ <DialogFooter>
179
+ <DialogClose as-child>
180
+ <Button variant="outline">Cancel</Button>
181
+ </DialogClose>
182
+ <Button>Confirm</Button>
183
+ </DialogFooter>
184
+ </DialogContent>
185
+ </Dialog>
186
+ </template>
187
+ ```
188
+
189
+ **Drawer** follows same pattern: `Drawer`, `DrawerTrigger`, `DrawerContent`, `DrawerHeader`, `DrawerTitle`, `DrawerDescription`, `DrawerFooter`, `DrawerClose`. Slides in from bottom/side.
190
+
191
+ **Sheet** is similar to Drawer: `Sheet`, `SheetTrigger`, `SheetContent`, `SheetHeader`, `SheetTitle`, `SheetDescription`, `SheetFooter`, `SheetClose`. Use `side` prop on SheetContent: `"top"`, `"right"`, `"bottom"`, `"left"`.
192
+
193
+ ## Tabs
194
+
195
+ ```vue
196
+ <script setup>
197
+ import { Tabs, TabsList, TabsTrigger, TabsContent } from "@empathyds/vue";
198
+ </script>
199
+ <template>
200
+ <Tabs default-value="overview">
201
+ <TabsList>
202
+ <TabsTrigger value="overview">Overview</TabsTrigger>
203
+ <TabsTrigger value="analytics">Analytics</TabsTrigger>
204
+ <TabsTrigger value="settings">Settings</TabsTrigger>
205
+ </TabsList>
206
+ <TabsContent value="overview">Overview content</TabsContent>
207
+ <TabsContent value="analytics">Analytics content</TabsContent>
208
+ <TabsContent value="settings">Settings content</TabsContent>
209
+ </Tabs>
210
+ </template>
211
+ ```
212
+
213
+ ## Alert
214
+
215
+ ```vue
216
+ <script setup>
217
+ import { Alert, AlertTitle, AlertDescription } from "@empathyds/vue";
218
+ </script>
219
+ <template>
220
+ <Alert variant="destructive">
221
+ <AlertTitle>Error</AlertTitle>
222
+ <AlertDescription>Something went wrong.</AlertDescription>
223
+ </Alert>
224
+ </template>
225
+ ```
226
+
227
+ | Prop | Values |
228
+ |------|--------|
229
+ | `variant` | `default`, `destructive` |
230
+
231
+ **AlertDialog**: Modal confirmation dialog. Uses `AlertDialog`, `AlertDialogTrigger`, `AlertDialogContent`, `AlertDialogHeader`, `AlertDialogTitle`, `AlertDialogDescription`, `AlertDialogFooter`, `AlertDialogAction`, `AlertDialogCancel`.
232
+
233
+ ## Badge
234
+
235
+ ```vue
236
+ <script setup>
237
+ import { Badge } from "@empathyds/vue";
238
+ </script>
239
+ <template>
240
+ <Badge variant="default">Active</Badge>
241
+ <Badge variant="secondary">Pending</Badge>
242
+ <Badge variant="destructive">Failed</Badge>
243
+ <Badge variant="outline">Draft</Badge>
244
+ </template>
245
+ ```
246
+
247
+ ## Avatar
248
+
249
+ ```vue
250
+ <script setup>
251
+ import { Avatar, AvatarImage, AvatarFallback } from "@empathyds/vue";
252
+ </script>
253
+ <template>
254
+ <Avatar>
255
+ <AvatarImage src="https://example.com/photo.jpg" alt="User" />
256
+ <AvatarFallback>JD</AvatarFallback>
257
+ </Avatar>
258
+ </template>
259
+ ```
260
+
261
+ ## Navigation Menu
262
+
263
+ ```vue
264
+ <script setup>
265
+ import {
266
+ NavigationMenu, NavigationMenuList, NavigationMenuItem,
267
+ NavigationMenuTrigger, NavigationMenuContent, NavigationMenuLink
268
+ } from "@empathyds/vue";
269
+ </script>
270
+ <template>
271
+ <NavigationMenu>
272
+ <NavigationMenuList>
273
+ <NavigationMenuItem>
274
+ <NavigationMenuTrigger>Getting Started</NavigationMenuTrigger>
275
+ <NavigationMenuContent>
276
+ <NavigationMenuLink href="/docs">Documentation</NavigationMenuLink>
277
+ </NavigationMenuContent>
278
+ </NavigationMenuItem>
279
+ </NavigationMenuList>
280
+ </NavigationMenu>
281
+ </template>
282
+ ```
283
+
284
+ ## Sidebar
285
+
286
+ Use `Sidebar`, `SidebarHeader`, `SidebarContent`, `SidebarFooter`, `SidebarGroup`, `SidebarGroupLabel`, `SidebarGroupContent`, `SidebarMenu`, `SidebarMenuItem`, `SidebarMenuButton`.
287
+
288
+ ## Accordion
289
+
290
+ ```vue
291
+ <script setup>
292
+ import { Accordion, AccordionItem, AccordionTrigger, AccordionContent } from "@empathyds/vue";
293
+ </script>
294
+ <template>
295
+ <Accordion type="single" collapsible>
296
+ <AccordionItem value="item-1">
297
+ <AccordionTrigger>Section 1</AccordionTrigger>
298
+ <AccordionContent>Content for section 1</AccordionContent>
299
+ </AccordionItem>
300
+ </Accordion>
301
+ </template>
302
+ ```
303
+
304
+ ## Toast (Sonner)
305
+
306
+ ```vue
307
+ <script setup>
308
+ import { Toaster } from "@empathyds/vue";
309
+ import { toast } from "sonner";
310
+
311
+ function showToast() {
312
+ toast.success("Changes saved!");
313
+ }
314
+ </script>
315
+ <template>
316
+ <Toaster />
317
+ <Button @click="showToast">Save</Button>
318
+ </template>
319
+ ```
320
+
321
+ Place `<Toaster />` once at root level. Use `toast()`, `toast.success()`, `toast.error()`, `toast.warning()`, `toast.info()`.
322
+
323
+ ## Tooltip & Popover
324
+
325
+ ```vue
326
+ <script setup>
327
+ import { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } from "@empathyds/vue";
328
+ </script>
329
+ <template>
330
+ <TooltipProvider>
331
+ <Tooltip>
332
+ <TooltipTrigger as-child>
333
+ <Button variant="ghost">Hover me</Button>
334
+ </TooltipTrigger>
335
+ <TooltipContent>Helpful tooltip text</TooltipContent>
336
+ </Tooltip>
337
+ </TooltipProvider>
338
+ </template>
339
+ ```
340
+
341
+ **Popover**: Same pattern with `Popover`, `PopoverTrigger`, `PopoverContent`. Stays open on click.
342
+ **HoverCard**: `HoverCard`, `HoverCardTrigger`, `HoverCardContent`. Shows on hover with richer content.
343
+
344
+ ## Progress & Skeleton
345
+
346
+ ```vue
347
+ <Progress :model-value="66" />
348
+
349
+ <Skeleton class="w-[200px] h-4" />
350
+ <Skeleton class="w-full h-16 rounded-lg" />
351
+ ```
352
+
353
+ ## Chart
354
+
355
+ Chart component wraps a charting library. Import `Chart` and pass data/config props. Refer to https://empathyds.com/components/chart for detailed configuration options.
356
+
357
+ ---
358
+
359
+ ## Other Components
360
+
361
+ - **Breadcrumb**: `Breadcrumb`, `BreadcrumbList`, `BreadcrumbItem`, `BreadcrumbLink`, `BreadcrumbSeparator`, `BreadcrumbPage`
362
+ - **Carousel**: `Carousel`, `CarouselContent`, `CarouselItem`, `CarouselPrevious`, `CarouselNext`
363
+ - **Calendar**: `Calendar`, `RangeCalendar` — date picking components
364
+ - **Collapsible**: `Collapsible`, `CollapsibleTrigger`, `CollapsibleContent`
365
+ - **Command**: `Command`, `CommandInput`, `CommandList`, `CommandEmpty`, `CommandGroup`, `CommandItem` — command palette / search
366
+ - **ContextMenu**: `ContextMenu`, `ContextMenuTrigger`, `ContextMenuContent`, `ContextMenuItem`
367
+ - **DropdownMenu**: `DropdownMenu`, `DropdownMenuTrigger`, `DropdownMenuContent`, `DropdownMenuItem`, `DropdownMenuSeparator`, `DropdownMenuLabel`
368
+ - **Menubar**: Full menu bar component system
369
+ - **Pagination**: `Pagination`, `PaginationList`, `PaginationPrev`, `PaginationNext`
370
+ - **Resizable**: `ResizablePanelGroup`, `ResizablePanel`, `ResizableHandle`
371
+ - **ScrollArea**: `ScrollArea`, `ScrollBar` — custom scrollbars
372
+ - **Slider**: Range slider input
373
+ - **Stepper**: Multi-step wizard component
374
+ - **Toggle / ToggleGroup**: On/off toggle buttons