uxonfly-mcp 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +205 -0
- package/build/index.js +159 -0
- package/package.json +57 -0
- package/uxonfly.md +651 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Phanindhra Kondru
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
# UXonFly
|
|
2
|
+
|
|
3
|
+
> An open-source design system for AI coding sessions.
|
|
4
|
+
> Strong opinions. MIT licensed.
|
|
5
|
+
|
|
6
|
+
[](LICENSE)
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## What this is
|
|
11
|
+
|
|
12
|
+
Every AI coding session starts blind. Generic shadcn components. Default
|
|
13
|
+
Tailwind colors. Modal-inside-a-modal. Empty states with no CTA. Errors in
|
|
14
|
+
toasts that should be inline. Destructive actions with single-click delete.
|
|
15
|
+
|
|
16
|
+
UXonFly is a curated set of design and UX rules — tokens, components, UX
|
|
17
|
+
patterns, flows, copy rules, and principles — that your AI reads before
|
|
18
|
+
generating UI. The rules live in one markdown file: `uxonfly.md`.
|
|
19
|
+
|
|
20
|
+
**Two ways to use it. Same content, same rules:**
|
|
21
|
+
|
|
22
|
+
1. **As a file** — copy `uxonfly.md` into your project. Your AI reads it natively every session. Zero install.
|
|
23
|
+
2. **As an MCP server** — run `uxonfly-mcp` and your AI invokes 7 tools (`get_component`, `get_ux_pattern`, …) on demand.
|
|
24
|
+
|
|
25
|
+
The file install is the recommended starting point. The MCP server is the
|
|
26
|
+
upgrade for power users who want tool-call precision.
|
|
27
|
+
|
|
28
|
+
MIT licensed. Fork it. Improve it. Send a PR.
|
|
29
|
+
|
|
30
|
+
## What's in it
|
|
31
|
+
|
|
32
|
+
- **5 token categories** — colors, typography, spacing, shape, shadows
|
|
33
|
+
- **6 components** — button, input, card, badge, modal, toast
|
|
34
|
+
- **9 UX patterns** — navigation, modals, forms, loading, empty states, errors, destructive actions, data tables, notifications
|
|
35
|
+
- **3 flows** — empty-state, onboarding, auth
|
|
36
|
+
- **Copy rules** — voice, tense, errors, CTAs, tooltips, placeholders, labels
|
|
37
|
+
- **7 principles** — accessibility first, one primary action, error prevention, progressive disclosure, copy-driven, keyboard navigable, trust the system
|
|
38
|
+
|
|
39
|
+
Strong opinions. Border-first. Modern SaaS aesthetic (Linear / Stripe / Vercel-aligned).
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## Install option 1 — file (recommended, 30 seconds)
|
|
44
|
+
|
|
45
|
+
This is the only step you need.
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
# Download the rulebook
|
|
49
|
+
curl -O https://raw.githubusercontent.com/Phanikondru/uxonfly-mcp/main/uxonfly.md
|
|
50
|
+
|
|
51
|
+
# For Claude Code:
|
|
52
|
+
mv uxonfly.md CLAUDE.md
|
|
53
|
+
|
|
54
|
+
# OR for Cursor:
|
|
55
|
+
mv uxonfly.md .cursorrules
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Open your AI tool and start prompting. It will read the file before generating
|
|
59
|
+
UI on every session.
|
|
60
|
+
|
|
61
|
+
That's it. No npm install. No config. No login.
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## Install option 2 — MCP server (power users)
|
|
66
|
+
|
|
67
|
+
If you want your AI to invoke specific tools (`get_component`,
|
|
68
|
+
`get_ux_pattern`, `get_tokens`, …) on demand instead of reading the whole
|
|
69
|
+
file every prompt, run UXonFly as a local MCP server.
|
|
70
|
+
|
|
71
|
+
> **Status**: `uxonfly-mcp` is not yet published to npm. The one-line `npx`
|
|
72
|
+
> install will work after the first publish — until then, use the local-build
|
|
73
|
+
> path below.
|
|
74
|
+
|
|
75
|
+
### Install today (local build)
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
git clone https://github.com/Phanikondru/uxonfly-mcp.git
|
|
79
|
+
cd uxonfly-mcp
|
|
80
|
+
npm install
|
|
81
|
+
npm run build
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Then point your AI tool at the compiled binary using its absolute path.
|
|
85
|
+
|
|
86
|
+
**Claude Code:**
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
claude mcp add uxonfly -- node /absolute/path/to/uxonfly-mcp/build/index.js
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
**Cursor** — add to `~/.cursor/mcp.json`:
|
|
93
|
+
|
|
94
|
+
```json
|
|
95
|
+
{
|
|
96
|
+
"mcpServers": {
|
|
97
|
+
"uxonfly": {
|
|
98
|
+
"command": "node",
|
|
99
|
+
"args": ["/absolute/path/to/uxonfly-mcp/build/index.js"]
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### After the first npm publish (coming soon)
|
|
106
|
+
|
|
107
|
+
Once `uxonfly-mcp` is on the npm registry, the install collapses to one command.
|
|
108
|
+
|
|
109
|
+
**Claude Code:**
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
claude mcp add uxonfly -- npx -y uxonfly-mcp
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
**Cursor** — `~/.cursor/mcp.json`:
|
|
116
|
+
|
|
117
|
+
```json
|
|
118
|
+
{
|
|
119
|
+
"mcpServers": {
|
|
120
|
+
"uxonfly": {
|
|
121
|
+
"command": "npx",
|
|
122
|
+
"args": ["-y", "uxonfly-mcp"]
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Available tools
|
|
129
|
+
|
|
130
|
+
| Tool | When AI calls it | Returns |
|
|
131
|
+
| --- | --- | --- |
|
|
132
|
+
| `get_design_system` | Before any UI task | Full system |
|
|
133
|
+
| `get_component` | Before generating a component | Component spec |
|
|
134
|
+
| `get_tokens` | Before writing CSS / Tailwind | Tokens |
|
|
135
|
+
| `get_ux_pattern` | Before any UX decision | Pattern |
|
|
136
|
+
| `get_flow` | Before any multi-step flow | Flow spec |
|
|
137
|
+
| `get_copy_rules` | Before writing interface copy | Copy rules |
|
|
138
|
+
| `get_rules` | Before layout / IA decisions | Principles |
|
|
139
|
+
|
|
140
|
+
Each tool description begins with `ALWAYS call…` so your AI invokes them
|
|
141
|
+
proactively before generating UI code (the trick: the tool description
|
|
142
|
+
itself is the instruction).
|
|
143
|
+
|
|
144
|
+
### Custom rulebook
|
|
145
|
+
|
|
146
|
+
Point the MCP server at your own version of `uxonfly.md` via the
|
|
147
|
+
`UXONFLY_MD_PATH` environment variable:
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
# Local build (works today):
|
|
151
|
+
UXONFLY_MD_PATH=/absolute/path/to/your/uxonfly.md \
|
|
152
|
+
node /absolute/path/to/uxonfly-mcp/build/index.js
|
|
153
|
+
|
|
154
|
+
# After npm publish:
|
|
155
|
+
UXONFLY_MD_PATH=/absolute/path/to/your/uxonfly.md npx uxonfly-mcp
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
## Local development
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
git clone https://github.com/Phanikondru/uxonfly-mcp.git
|
|
164
|
+
cd uxonfly-mcp
|
|
165
|
+
npm install
|
|
166
|
+
npm run dev # run with tsx
|
|
167
|
+
npm run build # compile to ./build
|
|
168
|
+
npm start # run the compiled server
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
---
|
|
172
|
+
|
|
173
|
+
## Contributing
|
|
174
|
+
|
|
175
|
+
The whole point of this project: one designer's opinions become a shared
|
|
176
|
+
standard the community keeps improving. PRs welcome.
|
|
177
|
+
|
|
178
|
+
How to help:
|
|
179
|
+
|
|
180
|
+
- Open an issue if you think a rule is wrong, missing, or unclear
|
|
181
|
+
- Send a PR for new components, patterns, or principles you've battle-tested
|
|
182
|
+
- Add real-world usage examples (apps you've built with `uxonfly.md`)
|
|
183
|
+
|
|
184
|
+
Goal: make UXonFly *better* than any one designer's taste alone.
|
|
185
|
+
|
|
186
|
+
---
|
|
187
|
+
|
|
188
|
+
## About
|
|
189
|
+
|
|
190
|
+
Built by [phanikondru](https://x.com/Phanikondru) — designer with 4 years
|
|
191
|
+
of production UX experience, building in public.
|
|
192
|
+
|
|
193
|
+
Just trying to build good things. **Design + AI + Code.**
|
|
194
|
+
|
|
195
|
+
UXonFly was built with Gemini, Cursor, and the rules in `uxonfly.md`
|
|
196
|
+
itself — the same system that ships with this package. The tool eats its
|
|
197
|
+
own dog food.
|
|
198
|
+
|
|
199
|
+
Follow the project: [@Phanikondru](https://x.com/Phanikondru)
|
|
200
|
+
|
|
201
|
+
---
|
|
202
|
+
|
|
203
|
+
## License
|
|
204
|
+
|
|
205
|
+
MIT. Use it commercially. Fork it. Modify it. Just keep the LICENSE file.
|
package/build/index.js
ADDED
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* uxonfly-mcp — Designer intelligence for every AI coding session.
|
|
4
|
+
*
|
|
5
|
+
* A Model Context Protocol server exposing the UXonFly design system,
|
|
6
|
+
* UX patterns, and product principles to any MCP-compatible AI tool
|
|
7
|
+
* (Cursor, Claude Code, Windsurf, VS Code, Zed).
|
|
8
|
+
*
|
|
9
|
+
* Each tool description begins with "ALWAYS call..." so the AI invokes
|
|
10
|
+
* them proactively before writing UI code — the tool description is the
|
|
11
|
+
* instruction (see PRD §4.3).
|
|
12
|
+
*/
|
|
13
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
14
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
15
|
+
import { z } from "zod";
|
|
16
|
+
import { readFileSync } from "node:fs";
|
|
17
|
+
import { fileURLToPath } from "node:url";
|
|
18
|
+
import { dirname, resolve } from "node:path";
|
|
19
|
+
// ─── Load the base system ────────────────────────────────────────────
|
|
20
|
+
const DEFAULT_MD_PATH = resolve(dirname(fileURLToPath(import.meta.url)), "..", "uxonfly.md");
|
|
21
|
+
function loadSystem() {
|
|
22
|
+
const path = process.env.UXONFLY_MD_PATH ?? DEFAULT_MD_PATH;
|
|
23
|
+
try {
|
|
24
|
+
return readFileSync(path, "utf8");
|
|
25
|
+
}
|
|
26
|
+
catch (err) {
|
|
27
|
+
console.error(`[uxonfly-mcp] failed to read ${path}:`, err);
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
const SYSTEM = loadSystem();
|
|
32
|
+
// ─── Markdown section slicing ────────────────────────────────────────
|
|
33
|
+
// Extracts content under a heading until the next heading of equal or
|
|
34
|
+
// shallower depth. Keeps the MCP server dependency-free (no markdown lib).
|
|
35
|
+
function getSection(md, heading, level = 2) {
|
|
36
|
+
const marker = "#".repeat(level) + " ";
|
|
37
|
+
const lines = md.split("\n");
|
|
38
|
+
let start = -1;
|
|
39
|
+
for (let i = 0; i < lines.length; i++) {
|
|
40
|
+
if (lines[i].trim() === (marker + heading).trim()) {
|
|
41
|
+
start = i + 1;
|
|
42
|
+
break;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
if (start === -1)
|
|
46
|
+
return null;
|
|
47
|
+
let end = lines.length;
|
|
48
|
+
for (let i = start; i < lines.length; i++) {
|
|
49
|
+
const line = lines[i].trim();
|
|
50
|
+
if (line.startsWith("#")) {
|
|
51
|
+
const depth = line.match(/^#+/)?.[0].length ?? 0;
|
|
52
|
+
if (depth <= level) {
|
|
53
|
+
end = i;
|
|
54
|
+
break;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return lines.slice(start, end).join("\n").trim();
|
|
59
|
+
}
|
|
60
|
+
const asText = (text) => ({
|
|
61
|
+
content: [{ type: "text", text }],
|
|
62
|
+
});
|
|
63
|
+
const notFound = (what) => asText(`[uxonfly] not found: ${what}. Check uxonfly.md for available sections.`);
|
|
64
|
+
// ─── Server ──────────────────────────────────────────────────────────
|
|
65
|
+
const server = new McpServer({
|
|
66
|
+
name: "uxonfly-mcp",
|
|
67
|
+
version: "0.1.0",
|
|
68
|
+
});
|
|
69
|
+
// Tool 1 — complete design system
|
|
70
|
+
server.registerTool("get_design_system", {
|
|
71
|
+
title: "Get UXonFly Design System",
|
|
72
|
+
description: "ALWAYS call this tool before writing any UI code. Returns the complete 3-layer UXonFly design system for this project: visual language (tokens, components, layout), UX patterns (navigation, modals, forms, loading, empty states, errors, destructive actions, tables, notifications), and product principles. Do not invent design decisions — follow what this tool returns.",
|
|
73
|
+
inputSchema: {},
|
|
74
|
+
}, async () => asText(SYSTEM));
|
|
75
|
+
// Tool 2 — specific component
|
|
76
|
+
server.registerTool("get_component", {
|
|
77
|
+
title: "Get Component Spec",
|
|
78
|
+
description: "ALWAYS call this before generating any UI component (button, input, card, badge, modal, toast, etc.). Returns the full component spec: sizes, variants, states, and UX rules. Never improvise component specs.",
|
|
79
|
+
inputSchema: {
|
|
80
|
+
name: z
|
|
81
|
+
.string()
|
|
82
|
+
.describe("Component name, lowercase. E.g. 'button', 'input', 'card', 'badge', 'modal', 'toast'."),
|
|
83
|
+
},
|
|
84
|
+
}, async ({ name }) => {
|
|
85
|
+
const components = getSection(SYSTEM, "Components", 2);
|
|
86
|
+
if (!components)
|
|
87
|
+
return notFound("Components section");
|
|
88
|
+
const spec = getSection(components, name.toLowerCase(), 3);
|
|
89
|
+
return spec ? asText(spec) : notFound(`component '${name}'`);
|
|
90
|
+
});
|
|
91
|
+
// Tool 3 — design tokens
|
|
92
|
+
server.registerTool("get_tokens", {
|
|
93
|
+
title: "Get Design Tokens",
|
|
94
|
+
description: "ALWAYS call this before writing CSS, Tailwind config, CSS variables, or any styling code. Returns the complete token set: colors (with all semantic variants), typography scale, spacing scale, border radius, and shadows. Never use values outside these tokens.",
|
|
95
|
+
inputSchema: {},
|
|
96
|
+
}, async () => {
|
|
97
|
+
const tokens = getSection(SYSTEM, "Tokens", 2);
|
|
98
|
+
return tokens ? asText(tokens) : notFound("Tokens section");
|
|
99
|
+
});
|
|
100
|
+
// Tool 4 — UX pattern for a context
|
|
101
|
+
server.registerTool("get_ux_pattern", {
|
|
102
|
+
title: "Get UX Pattern",
|
|
103
|
+
description: "ALWAYS call this before making any UX decision. Covers destructive actions, forms, modals, loading states, empty states, errors, navigation, data tables, and notifications. Returns the correct pattern plus implementation guidance. Never guess at UX decisions.",
|
|
104
|
+
inputSchema: {
|
|
105
|
+
context: z
|
|
106
|
+
.string()
|
|
107
|
+
.describe("UX context. E.g. 'destructive-actions', 'forms', 'empty-states', 'errors', 'loading', 'modals', 'navigation', 'data-tables', 'notifications'."),
|
|
108
|
+
},
|
|
109
|
+
}, async ({ context }) => {
|
|
110
|
+
const patterns = getSection(SYSTEM, "UX Patterns", 2);
|
|
111
|
+
if (!patterns)
|
|
112
|
+
return notFound("UX Patterns section");
|
|
113
|
+
const match = getSection(patterns, context.toLowerCase(), 3);
|
|
114
|
+
return match ? asText(match) : notFound(`pattern '${context}'`);
|
|
115
|
+
});
|
|
116
|
+
// Tool 5 — multi-step flow
|
|
117
|
+
server.registerTool("get_flow", {
|
|
118
|
+
title: "Get UX Flow",
|
|
119
|
+
description: "ALWAYS call this before building any multi-step flow. Returns a complete flow spec: all screens, states, transitions, success and error handling. Never design flows from scratch.",
|
|
120
|
+
inputSchema: {
|
|
121
|
+
flow_name: z
|
|
122
|
+
.string()
|
|
123
|
+
.describe("Flow name. E.g. 'empty-state', 'onboarding', 'auth'."),
|
|
124
|
+
},
|
|
125
|
+
}, async ({ flow_name }) => {
|
|
126
|
+
const flows = getSection(SYSTEM, "Flows", 2);
|
|
127
|
+
if (!flows)
|
|
128
|
+
return notFound("Flows section");
|
|
129
|
+
const flow = getSection(flows, flow_name.toLowerCase(), 3);
|
|
130
|
+
return flow ? asText(flow) : notFound(`flow '${flow_name}'`);
|
|
131
|
+
});
|
|
132
|
+
// Tool 6 — copy rules
|
|
133
|
+
server.registerTool("get_copy_rules", {
|
|
134
|
+
title: "Get Copy Rules",
|
|
135
|
+
description: "ALWAYS call this before writing any interface copy — error messages, CTAs, empty-state text, tooltips, placeholders, labels. Returns tone, voice, tense, and format rules. Never write copy without checking these.",
|
|
136
|
+
inputSchema: {},
|
|
137
|
+
}, async () => {
|
|
138
|
+
const copy = getSection(SYSTEM, "Copy Rules", 2);
|
|
139
|
+
return copy ? asText(copy) : notFound("Copy Rules section");
|
|
140
|
+
});
|
|
141
|
+
// Tool 7 — product and design principles
|
|
142
|
+
server.registerTool("get_rules", {
|
|
143
|
+
title: "Get Product Principles",
|
|
144
|
+
description: "ALWAYS call this before making layout or information architecture decisions. Returns all product and design principles for this project: progressive disclosure, primary-action rules, error prevention, copy-driven UI, keyboard navigation, and accessibility.",
|
|
145
|
+
inputSchema: {},
|
|
146
|
+
}, async () => {
|
|
147
|
+
const rules = getSection(SYSTEM, "Principles", 2);
|
|
148
|
+
return rules ? asText(rules) : notFound("Principles section");
|
|
149
|
+
});
|
|
150
|
+
// ─── Start ───────────────────────────────────────────────────────────
|
|
151
|
+
async function main() {
|
|
152
|
+
const transport = new StdioServerTransport();
|
|
153
|
+
await server.connect(transport);
|
|
154
|
+
console.error("[uxonfly-mcp] running on stdio");
|
|
155
|
+
}
|
|
156
|
+
main().catch((err) => {
|
|
157
|
+
console.error("[uxonfly-mcp] fatal:", err);
|
|
158
|
+
process.exit(1);
|
|
159
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "uxonfly-mcp",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Designer intelligence for every AI coding session. Production-grade UX. On the fly.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"uxonfly-mcp": "build/index.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"build",
|
|
11
|
+
"uxonfly.md",
|
|
12
|
+
"README.md",
|
|
13
|
+
"LICENSE"
|
|
14
|
+
],
|
|
15
|
+
"scripts": {
|
|
16
|
+
"build": "tsc && node -e \"require('fs').chmodSync('build/index.js', '755')\"",
|
|
17
|
+
"dev": "tsx src/index.ts",
|
|
18
|
+
"start": "node build/index.js",
|
|
19
|
+
"prepublishOnly": "npm run build"
|
|
20
|
+
},
|
|
21
|
+
"keywords": [
|
|
22
|
+
"mcp",
|
|
23
|
+
"model-context-protocol",
|
|
24
|
+
"design-system",
|
|
25
|
+
"ux",
|
|
26
|
+
"ai",
|
|
27
|
+
"cursor",
|
|
28
|
+
"claude-code",
|
|
29
|
+
"windsurf",
|
|
30
|
+
"vibe-coding"
|
|
31
|
+
],
|
|
32
|
+
"author": {
|
|
33
|
+
"name": "phanikondru",
|
|
34
|
+
"url": "https://x.com/Phanikondru"
|
|
35
|
+
},
|
|
36
|
+
"license": "MIT",
|
|
37
|
+
"homepage": "https://uxonfly.dev",
|
|
38
|
+
"repository": {
|
|
39
|
+
"type": "git",
|
|
40
|
+
"url": "git+https://github.com/Phanikondru/uxonfly-mcp.git"
|
|
41
|
+
},
|
|
42
|
+
"bugs": {
|
|
43
|
+
"url": "https://github.com/Phanikondru/uxonfly-mcp/issues"
|
|
44
|
+
},
|
|
45
|
+
"dependencies": {
|
|
46
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
47
|
+
"zod": "^3.23.8"
|
|
48
|
+
},
|
|
49
|
+
"devDependencies": {
|
|
50
|
+
"@types/node": "^22.19.2",
|
|
51
|
+
"tsx": "^4.19.2",
|
|
52
|
+
"typescript": "^5.9.3"
|
|
53
|
+
},
|
|
54
|
+
"engines": {
|
|
55
|
+
"node": ">=18"
|
|
56
|
+
}
|
|
57
|
+
}
|
package/uxonfly.md
ADDED
|
@@ -0,0 +1,651 @@
|
|
|
1
|
+
# UXonFly — Design System
|
|
2
|
+
|
|
3
|
+
> Production-grade design and UX rules for AI coding sessions.
|
|
4
|
+
>
|
|
5
|
+
> Drop this file in your project root as `CLAUDE.md` (Claude Code) or
|
|
6
|
+
> `.cursorrules` (Cursor). Your AI will read it on every prompt and follow
|
|
7
|
+
> these rules instead of generating generic UI.
|
|
8
|
+
>
|
|
9
|
+
> Strong opinions. Border-first. Modern SaaS aesthetic
|
|
10
|
+
> (Linear / Stripe / Vercel-aligned). MIT licensed.
|
|
11
|
+
> Improvements welcome via PRs at github.com/Phanikondru/uxonfly-mcp.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Tokens
|
|
16
|
+
|
|
17
|
+
### Colors
|
|
18
|
+
|
|
19
|
+
Brand color: `#6366F1` (indigo). Replace with your own brand hex — every
|
|
20
|
+
other color is derived from it.
|
|
21
|
+
|
|
22
|
+
Semantic palette:
|
|
23
|
+
|
|
24
|
+
- `--color-primary`: #6366F1
|
|
25
|
+
- `--color-primary-hover`: #4F46E5
|
|
26
|
+
- `--color-primary-active`: #4338CA
|
|
27
|
+
- `--color-primary-subtle`: #EEF2FF
|
|
28
|
+
- `--color-text`: #0F172A (slate-900 — never pure black)
|
|
29
|
+
- `--color-text-muted`: #64748B (slate-500)
|
|
30
|
+
- `--color-text-subtle`: #94A3B8 (slate-400)
|
|
31
|
+
- `--color-surface`: #FFFFFF
|
|
32
|
+
- `--color-surface-subtle`: #F8FAFC (slate-50)
|
|
33
|
+
- `--color-surface-sunken`: #F1F5F9 (slate-100)
|
|
34
|
+
- `--color-border`: #E2E8F0 (slate-200)
|
|
35
|
+
- `--color-border-strong`: #CBD5E1 (slate-300)
|
|
36
|
+
- `--color-success`: #10B981 (emerald-500)
|
|
37
|
+
- `--color-warning`: #F59E0B (amber-500)
|
|
38
|
+
- `--color-danger`: #EF4444 (red-500)
|
|
39
|
+
- `--color-info`: #3B82F6 (blue-500)
|
|
40
|
+
|
|
41
|
+
Rules:
|
|
42
|
+
|
|
43
|
+
- Never use pure black (`#000`) for text. Use slate-900 (`#0F172A`).
|
|
44
|
+
- Never use pure white surfaces against muted backgrounds. Use slate-50 for muted areas.
|
|
45
|
+
- The primary brand color is for ONE primary action per screen. Don't decorate with it.
|
|
46
|
+
- Semantic colors (success / warning / danger) appear only on status indicators — never as decoration.
|
|
47
|
+
|
|
48
|
+
Dark mode: invert surface and text. Brand color stays the same hue.
|
|
49
|
+
|
|
50
|
+
- `--color-surface` → `#0F172A` (slate-900)
|
|
51
|
+
- `--color-surface-subtle` → `#1E293B` (slate-800)
|
|
52
|
+
- `--color-text` → `#F8FAFC` (slate-50)
|
|
53
|
+
- `--color-text-muted` → `#94A3B8` (slate-400)
|
|
54
|
+
- `--color-border` → `#1E293B` (slate-800)
|
|
55
|
+
|
|
56
|
+
### Typography
|
|
57
|
+
|
|
58
|
+
- Display: **Cal Sans** (fallback: Inter Display)
|
|
59
|
+
- Body: **Inter**
|
|
60
|
+
- Mono: **JetBrains Mono**
|
|
61
|
+
|
|
62
|
+
Type scale (px):
|
|
63
|
+
|
|
64
|
+
- `xs`: 12 / 16 line-height
|
|
65
|
+
- `sm`: 14 / 20
|
|
66
|
+
- `base`: 16 / 24 (default body)
|
|
67
|
+
- `lg`: 18 / 28
|
|
68
|
+
- `xl`: 20 / 28
|
|
69
|
+
- `2xl`: 24 / 32
|
|
70
|
+
- `3xl`: 32 / 40
|
|
71
|
+
- `4xl`: 48 / 52
|
|
72
|
+
|
|
73
|
+
Weights:
|
|
74
|
+
|
|
75
|
+
- 400 (regular) — body text
|
|
76
|
+
- 500 (medium) — UI labels, buttons
|
|
77
|
+
- 600 (semibold) — headings, emphasis
|
|
78
|
+
- 700 (bold) — top-level page titles only, used sparingly
|
|
79
|
+
|
|
80
|
+
Rules:
|
|
81
|
+
|
|
82
|
+
- Body text is 16px minimum. Never smaller for readable content.
|
|
83
|
+
- Line height: body 1.5, headings 1.2.
|
|
84
|
+
- Never use more than 3 distinct font sizes on a single screen.
|
|
85
|
+
|
|
86
|
+
### Spacing
|
|
87
|
+
|
|
88
|
+
8-stop scale (pixels): `4 / 8 / 12 / 16 / 24 / 32 / 48 / 64`
|
|
89
|
+
|
|
90
|
+
Rules:
|
|
91
|
+
|
|
92
|
+
- Use only these values. No 7px, no 20px, no 50px.
|
|
93
|
+
- Default gap between major sections: 32px.
|
|
94
|
+
- Default gap between fields in a form: 16px.
|
|
95
|
+
- Default page padding: 24px desktop, 16px mobile.
|
|
96
|
+
- Compact UI (toolbars, dense tables): 8px gaps.
|
|
97
|
+
|
|
98
|
+
### Shape
|
|
99
|
+
|
|
100
|
+
Border radius scale:
|
|
101
|
+
|
|
102
|
+
- `sm`: 4px (badges, small chips)
|
|
103
|
+
- `md`: 8px (default — buttons, inputs)
|
|
104
|
+
- `lg`: 12px (cards, modals)
|
|
105
|
+
- `xl`: 16px (hero sections, large containers)
|
|
106
|
+
- `full`: 9999px (avatars, pill buttons)
|
|
107
|
+
|
|
108
|
+
Rules:
|
|
109
|
+
|
|
110
|
+
- Default radius is `md` (8px). Use it unless there's a reason not to.
|
|
111
|
+
- Never mix radius sizes inside the same component.
|
|
112
|
+
- Never use square corners (radius 0) on interactive elements.
|
|
113
|
+
|
|
114
|
+
### Shadows
|
|
115
|
+
|
|
116
|
+
**Border-first philosophy**: separation comes from borders, not shadows.
|
|
117
|
+
|
|
118
|
+
One shadow only — for genuinely elevated things:
|
|
119
|
+
|
|
120
|
+
```
|
|
121
|
+
shadow-elevated: 0 1px 3px rgba(15, 23, 42, 0.08), 0 1px 2px rgba(15, 23, 42, 0.06)
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Use for:
|
|
125
|
+
|
|
126
|
+
- Modals (backdrop creates the elevation, shadow makes it feel held)
|
|
127
|
+
- Popovers and dropdowns
|
|
128
|
+
- Toasts
|
|
129
|
+
- Floating UI (FABs, command palettes)
|
|
130
|
+
|
|
131
|
+
Never use for:
|
|
132
|
+
|
|
133
|
+
- Cards in a list (use a border instead)
|
|
134
|
+
- Buttons (use the primary color, not a shadow, to indicate elevation)
|
|
135
|
+
- Inputs (use a focus ring, not a shadow)
|
|
136
|
+
- Headers, sidebars, navigation (use a `border-bottom`)
|
|
137
|
+
|
|
138
|
+
If you find yourself reaching for a shadow on a card, you want a border.
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
## Components
|
|
143
|
+
|
|
144
|
+
### button
|
|
145
|
+
|
|
146
|
+
Sizes:
|
|
147
|
+
|
|
148
|
+
- `sm`: 32px height, 12px horizontal padding, 14px text
|
|
149
|
+
- `md`: 40px height, 16px horizontal padding, 14px text (default)
|
|
150
|
+
- `lg`: 48px height, 24px horizontal padding, 16px text
|
|
151
|
+
|
|
152
|
+
Variants:
|
|
153
|
+
|
|
154
|
+
- `primary` — brand color background, white text. ONE per screen, max.
|
|
155
|
+
- `secondary` — surface background, border, text color. Default for everything that isn't the primary action.
|
|
156
|
+
- `ghost` — transparent background, no border, text color. For tertiary actions in toolbars and inline contexts.
|
|
157
|
+
- `danger` — red background, white text. For destructive actions only. Always paired with a 2-step confirmation.
|
|
158
|
+
|
|
159
|
+
States (every variant):
|
|
160
|
+
|
|
161
|
+
- `default` — base styling
|
|
162
|
+
- `hover` — slightly darker background (use the `-hover` token)
|
|
163
|
+
- `active` — even darker background (use the `-active` token)
|
|
164
|
+
- `focus-visible` — 2px ring in primary color at 50% opacity, 2px offset
|
|
165
|
+
- `disabled` — 50% opacity, `cursor: not-allowed`, no hover
|
|
166
|
+
- `loading` — spinner replaces label, button width stays the same
|
|
167
|
+
|
|
168
|
+
Rules:
|
|
169
|
+
|
|
170
|
+
- Maximum ONE `primary` button per screen. If two actions are equally important, redesign the screen.
|
|
171
|
+
- Destructive actions ALWAYS use the `danger` variant. Never red-color a primary button.
|
|
172
|
+
- Loading state: spinner replaces the label text but the button width does not change.
|
|
173
|
+
- Never use buttons for navigation. Navigation is for `<a>` tags. Buttons are for actions.
|
|
174
|
+
- Icon-only buttons require an `aria-label`.
|
|
175
|
+
|
|
176
|
+
### input
|
|
177
|
+
|
|
178
|
+
Default size: 40px height, 12px horizontal padding, 14px text.
|
|
179
|
+
Border: 1px solid `--color-border`, radius `md`.
|
|
180
|
+
Focus: 2px ring in `--color-primary` at 50% opacity, 2px offset.
|
|
181
|
+
|
|
182
|
+
Rules:
|
|
183
|
+
|
|
184
|
+
- Label ALWAYS above the field, always visible. Never use placeholder-as-label.
|
|
185
|
+
- Mark optional fields with `(optional)`. Never mark required fields with `*`.
|
|
186
|
+
- Validation runs on `blur`, never on every keystroke (one exception: password strength meters).
|
|
187
|
+
- Error messages appear below the field, in `--color-danger`, prefixed with an icon.
|
|
188
|
+
- Placeholders are example values (`jane@example.com`), never instructions ("Enter your email").
|
|
189
|
+
- Help text and error text share the same vertical slot — no layout shift between states.
|
|
190
|
+
|
|
191
|
+
### card
|
|
192
|
+
|
|
193
|
+
Background: `--color-surface`.
|
|
194
|
+
Border: 1px solid `--color-border` (NOT shadow).
|
|
195
|
+
Radius: `lg` (12px).
|
|
196
|
+
Padding: 24px.
|
|
197
|
+
|
|
198
|
+
Rules:
|
|
199
|
+
|
|
200
|
+
- Cards group related content. Don't use a card for a single piece of content.
|
|
201
|
+
- Cards in a list are separated by 16px vertical gaps. No additional shadows or dividers.
|
|
202
|
+
- Hoverable cards get `--color-border-strong` on hover, no shadow.
|
|
203
|
+
- Never nest cards more than one level deep.
|
|
204
|
+
|
|
205
|
+
### badge
|
|
206
|
+
|
|
207
|
+
Height: 24px.
|
|
208
|
+
Padding: 0 8px.
|
|
209
|
+
Radius: `full`.
|
|
210
|
+
Text: 12px, weight 500.
|
|
211
|
+
|
|
212
|
+
Variants:
|
|
213
|
+
|
|
214
|
+
- `neutral` — slate-100 background, slate-700 text
|
|
215
|
+
- `primary` — primary-subtle background, primary text
|
|
216
|
+
- `success` — emerald-50 background, emerald-700 text
|
|
217
|
+
- `warning` — amber-50 background, amber-700 text
|
|
218
|
+
- `danger` — red-50 background, red-700 text
|
|
219
|
+
- `info` — blue-50 background, blue-700 text
|
|
220
|
+
|
|
221
|
+
Rules:
|
|
222
|
+
|
|
223
|
+
- Badges are for status, counts, or labels. Never for navigation, never for actions.
|
|
224
|
+
- Never use a badge as a button. If it's clickable, it's a button.
|
|
225
|
+
- Never put more than one badge on a single piece of content unless they encode different information types (status + count is fine; two statuses is not).
|
|
226
|
+
|
|
227
|
+
### modal
|
|
228
|
+
|
|
229
|
+
Default max-width: 480px.
|
|
230
|
+
Wide variant: 640px.
|
|
231
|
+
Full variant: 90vw (only for large media or complex flows).
|
|
232
|
+
Backdrop: `rgba(15, 23, 42, 0.5)`.
|
|
233
|
+
Padding: 24px.
|
|
234
|
+
Radius: `lg`.
|
|
235
|
+
Shadow: `shadow-elevated`.
|
|
236
|
+
|
|
237
|
+
Required close affordances (ALL THREE):
|
|
238
|
+
|
|
239
|
+
- Close button (X) in the top-right
|
|
240
|
+
- Click on backdrop dismisses
|
|
241
|
+
- `Esc` key dismisses
|
|
242
|
+
|
|
243
|
+
Rules:
|
|
244
|
+
|
|
245
|
+
- Modals interrupt the user — only use them for short, focused tasks.
|
|
246
|
+
- NEVER nest modals inside modals. Redesign the flow.
|
|
247
|
+
- NEVER require horizontal scroll inside a modal.
|
|
248
|
+
- Modal title is required. Plain language, sentence case.
|
|
249
|
+
- Primary action is bottom-right. Cancel is bottom-left or the top-right X.
|
|
250
|
+
- Destructive modals follow the pattern in `### destructive-actions`.
|
|
251
|
+
|
|
252
|
+
### toast
|
|
253
|
+
|
|
254
|
+
Position: bottom-right on desktop, top-center on mobile.
|
|
255
|
+
Width: 360px max.
|
|
256
|
+
Padding: 16px.
|
|
257
|
+
Radius: `md`.
|
|
258
|
+
Shadow: `shadow-elevated`.
|
|
259
|
+
Stack: max 3 visible at once. Older toasts dismiss when the 4th appears.
|
|
260
|
+
|
|
261
|
+
Auto-dismiss timing:
|
|
262
|
+
|
|
263
|
+
- Success: 4 seconds
|
|
264
|
+
- Info: 5 seconds
|
|
265
|
+
- Warning: 7 seconds
|
|
266
|
+
- Error: 10 seconds
|
|
267
|
+
- With required action (e.g. "Undo"): never auto-dismiss until clicked
|
|
268
|
+
|
|
269
|
+
Rules:
|
|
270
|
+
|
|
271
|
+
- Toasts are for confirmations and non-critical errors. Never for blocking information.
|
|
272
|
+
- Toasts always include an icon matching the variant.
|
|
273
|
+
- Toasts NEVER contain "view more" links — the message must stand alone.
|
|
274
|
+
- Errors that need user action go inline or as a banner — NOT as a toast.
|
|
275
|
+
|
|
276
|
+
---
|
|
277
|
+
|
|
278
|
+
## UX Patterns
|
|
279
|
+
|
|
280
|
+
### navigation
|
|
281
|
+
|
|
282
|
+
- 5+ top-level sections → sidebar (left, fixed, 240px wide)
|
|
283
|
+
- 2–4 views → tabs (top of content, underline style)
|
|
284
|
+
- Hierarchy beyond 2 levels deep → breadcrumbs (above the page title)
|
|
285
|
+
|
|
286
|
+
Rules:
|
|
287
|
+
|
|
288
|
+
- Hamburger menus are FORBIDDEN on screens ≥1024px wide.
|
|
289
|
+
- Sidebar nav items are vertical icons + text. Never icon-only on desktop.
|
|
290
|
+
- Active state: brand color text + brand color subtle background. NOT a colored bar.
|
|
291
|
+
- Tabs: underline only, no background fill. Active tab gets brand color underline + text.
|
|
292
|
+
- Breadcrumbs: separator is `/`, never `>`. Last item is the current page (no link).
|
|
293
|
+
|
|
294
|
+
### modals
|
|
295
|
+
|
|
296
|
+
Use a modal when:
|
|
297
|
+
|
|
298
|
+
- The task is short (under 30 seconds)
|
|
299
|
+
- The task is focused (single clear outcome)
|
|
300
|
+
- The task interrupts the current flow intentionally (confirmation, quick edit)
|
|
301
|
+
|
|
302
|
+
Use a new page when:
|
|
303
|
+
|
|
304
|
+
- The task has multiple steps
|
|
305
|
+
- The task requires reference to other content on the page
|
|
306
|
+
- The task IS the primary flow (creating the main resource of the app)
|
|
307
|
+
|
|
308
|
+
Forbidden:
|
|
309
|
+
|
|
310
|
+
- Modal nested inside a modal
|
|
311
|
+
- Modal that opens another modal on success
|
|
312
|
+
- Modal taller than the viewport with internal scroll AND a sticky footer
|
|
313
|
+
- Modal without a close X button
|
|
314
|
+
|
|
315
|
+
### forms
|
|
316
|
+
|
|
317
|
+
Layout:
|
|
318
|
+
|
|
319
|
+
- Single column by default, ALWAYS. Two-column only when fields are paired (first/last name, city/zip).
|
|
320
|
+
- Field gap: 16px vertical.
|
|
321
|
+
- Section gap (within a form): 32px.
|
|
322
|
+
|
|
323
|
+
Labels:
|
|
324
|
+
|
|
325
|
+
- Above the field, always visible. Sentence case. Singular ("Project name", not "Project Names:").
|
|
326
|
+
- No colon at the end.
|
|
327
|
+
|
|
328
|
+
Optional vs required:
|
|
329
|
+
|
|
330
|
+
- Mark OPTIONAL fields with `(optional)` after the label.
|
|
331
|
+
- Never mark required fields with `*` or "required".
|
|
332
|
+
|
|
333
|
+
Validation:
|
|
334
|
+
|
|
335
|
+
- On `blur`, NEVER on keystroke (one exception: password strength meters).
|
|
336
|
+
- On submit if the user has not yet blurred the field.
|
|
337
|
+
- Error message below the field, in danger color, prefixed with icon.
|
|
338
|
+
|
|
339
|
+
Submit button:
|
|
340
|
+
|
|
341
|
+
- Bottom-right by default.
|
|
342
|
+
- Cancel button to its left, secondary or ghost variant.
|
|
343
|
+
- Submit label is always verb + object: "Create project", not "Submit" or "OK".
|
|
344
|
+
|
|
345
|
+
Forbidden:
|
|
346
|
+
|
|
347
|
+
- "Reset" buttons. Use Cancel instead.
|
|
348
|
+
- Submit disabled until the form is valid (the user can't tell what's missing).
|
|
349
|
+
- Multiple submit buttons.
|
|
350
|
+
|
|
351
|
+
### loading
|
|
352
|
+
|
|
353
|
+
Apply by perceived task duration:
|
|
354
|
+
|
|
355
|
+
| Estimated time | Treatment |
|
|
356
|
+
| --- | --- |
|
|
357
|
+
| < 100ms | Nothing. Feels instant. |
|
|
358
|
+
| 100ms – 1s | Nothing visible, OR a brief shimmer if the user might wonder. |
|
|
359
|
+
| 1s – 3s | Inline spinner OR skeleton if the shape is known. |
|
|
360
|
+
| 3s+ | Progress bar + descriptive text ("Importing 1,200 rows…") |
|
|
361
|
+
| Unknown duration | Skeleton if shape known, indeterminate spinner with text otherwise. |
|
|
362
|
+
|
|
363
|
+
Rules:
|
|
364
|
+
|
|
365
|
+
- Skeleton vs spinner: skeleton if you know the shape (a card grid, a table, a profile). Spinner if you don't.
|
|
366
|
+
- NEVER show a spinner AND a skeleton on the same content at the same time.
|
|
367
|
+
- Loading replaces the content. Don't overlay a spinner on stale content unless the action is a refresh.
|
|
368
|
+
- Loading on a button: spinner replaces the label inline; button width stays the same.
|
|
369
|
+
|
|
370
|
+
### empty-states
|
|
371
|
+
|
|
372
|
+
ALWAYS four elements, in this order:
|
|
373
|
+
|
|
374
|
+
1. **Illustration** — small (max 120px tall), monochrome or low-contrast, never a photo.
|
|
375
|
+
2. **Heading** — what the user is seeing, in 1 short sentence. "No projects yet."
|
|
376
|
+
3. **Subtext** — what they can do about it, in 1 sentence. "Create your first project to start tracking work."
|
|
377
|
+
4. **Primary CTA button** — verb + object. "Create project."
|
|
378
|
+
|
|
379
|
+
Forbidden:
|
|
380
|
+
|
|
381
|
+
- Empty area with no content
|
|
382
|
+
- Generic "No data" string
|
|
383
|
+
- Disabled CTA
|
|
384
|
+
- More than one CTA (no "Learn more" + "Create" — pick one)
|
|
385
|
+
- Apologetic tone ("Sorry, no items found")
|
|
386
|
+
|
|
387
|
+
### errors
|
|
388
|
+
|
|
389
|
+
Choose by error type:
|
|
390
|
+
|
|
391
|
+
| Error | Treatment |
|
|
392
|
+
| --- | --- |
|
|
393
|
+
| Form validation | Inline, below the field |
|
|
394
|
+
| Action failure (e.g. save failed) | Inline at the action point, OR top-of-form banner |
|
|
395
|
+
| Page-level (5xx, network) | Top-of-page banner with retry button |
|
|
396
|
+
| Critical, blocks all action | Full-page error state with retry CTA |
|
|
397
|
+
|
|
398
|
+
Forbidden:
|
|
399
|
+
|
|
400
|
+
- Errors as toasts (toasts auto-dismiss; the user might miss critical info)
|
|
401
|
+
- Raw error codes ("Error 500") — translate to plain language
|
|
402
|
+
- Error messages without next steps ("Something went wrong" — say WHAT and what to do)
|
|
403
|
+
|
|
404
|
+
Plain language rule:
|
|
405
|
+
|
|
406
|
+
- Always tell the user (a) what failed, (b) what to do next.
|
|
407
|
+
- Bad: `Error: ENOENT no such file or directory`
|
|
408
|
+
- Good: `Couldn't find the file. It may have been moved. Refresh the page or upload it again.`
|
|
409
|
+
|
|
410
|
+
### destructive-actions
|
|
411
|
+
|
|
412
|
+
Minimum 2 steps: trigger → confirmation.
|
|
413
|
+
|
|
414
|
+
Confirmation modal copy format:
|
|
415
|
+
|
|
416
|
+
```
|
|
417
|
+
Title: Delete [resource type]?
|
|
418
|
+
Body: This will permanently delete [resource name]. This cannot be undone.
|
|
419
|
+
Cancel: Cancel
|
|
420
|
+
Danger: Delete [resource type]
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
For HIGH-VALUE destructive actions (deleting a workspace, an account, paid data):
|
|
424
|
+
|
|
425
|
+
- Require typed confirmation: "Type [resource name] to confirm"
|
|
426
|
+
- Disable the danger button until the typed input matches exactly
|
|
427
|
+
|
|
428
|
+
After success:
|
|
429
|
+
|
|
430
|
+
- If reversible within a window: show an undo toast for 10 seconds.
|
|
431
|
+
- If irreversible: show a success toast confirming what was deleted, no undo.
|
|
432
|
+
|
|
433
|
+
Forbidden:
|
|
434
|
+
|
|
435
|
+
- Single-click delete with no confirmation
|
|
436
|
+
- "Are you sure?" with an OK button (vague + non-destructive button color)
|
|
437
|
+
- Confirmations where the danger and cancel buttons look similar
|
|
438
|
+
|
|
439
|
+
### data-tables
|
|
440
|
+
|
|
441
|
+
Pagination:
|
|
442
|
+
|
|
443
|
+
- Show pagination at 25+ rows.
|
|
444
|
+
- Default page size: 25. Allowed: 25 / 50 / 100.
|
|
445
|
+
- Page navigation: bottom-right. Total count: bottom-left.
|
|
446
|
+
|
|
447
|
+
Sorting:
|
|
448
|
+
|
|
449
|
+
- Click the column header to sort. Click again to reverse. Click a third time to clear.
|
|
450
|
+
- Sort indicator: arrow up/down icon next to the column label.
|
|
451
|
+
|
|
452
|
+
Filtering:
|
|
453
|
+
|
|
454
|
+
- Filters live ABOVE the table, left-aligned.
|
|
455
|
+
- Filter chips show active filters; X to remove.
|
|
456
|
+
- "Clear all filters" appears when 2+ filters are active.
|
|
457
|
+
|
|
458
|
+
Bulk actions:
|
|
459
|
+
|
|
460
|
+
- Row selection via leading checkbox.
|
|
461
|
+
- Bulk action bar appears above the table when 1+ rows selected.
|
|
462
|
+
- Bulk action bar shows: count selected, available actions, "deselect all".
|
|
463
|
+
|
|
464
|
+
Column behavior:
|
|
465
|
+
|
|
466
|
+
- Sticky first column when horizontal scroll is needed.
|
|
467
|
+
- Truncate long values with `…` and a tooltip showing the full value on hover.
|
|
468
|
+
- Right-align numbers, left-align text and dates.
|
|
469
|
+
|
|
470
|
+
Forbidden:
|
|
471
|
+
|
|
472
|
+
- Horizontal scroll without sticky first column
|
|
473
|
+
- Pagination AND infinite scroll on the same table
|
|
474
|
+
- Sortable columns without a visible sort indicator
|
|
475
|
+
|
|
476
|
+
### notifications
|
|
477
|
+
|
|
478
|
+
Three types — never use the wrong one.
|
|
479
|
+
|
|
480
|
+
| Type | Use for | Position | Persistence |
|
|
481
|
+
| --- | --- | --- | --- |
|
|
482
|
+
| **Toast** | Transient confirmation (saved, sent, copied) | Bottom-right | Auto-dismiss |
|
|
483
|
+
| **Banner** | Persistent state info (trial ending, system maintenance) | Top of page | Dismissible by user |
|
|
484
|
+
| **Inline** | Field-level feedback (validation error, status near a control) | Adjacent to the control | Persists until resolved |
|
|
485
|
+
|
|
486
|
+
Rules:
|
|
487
|
+
|
|
488
|
+
- Critical errors NEVER use toasts. Use a banner or inline error.
|
|
489
|
+
- Banners stack at the top in priority order — most critical first.
|
|
490
|
+
- Inline notifications never auto-dismiss.
|
|
491
|
+
|
|
492
|
+
---
|
|
493
|
+
|
|
494
|
+
## Flows
|
|
495
|
+
|
|
496
|
+
### empty-state
|
|
497
|
+
|
|
498
|
+
Steps:
|
|
499
|
+
|
|
500
|
+
1. Route loads. Detect empty data.
|
|
501
|
+
2. Render the empty state component (illustration + heading + subtext + CTA — see UX Patterns → empty-states).
|
|
502
|
+
3. CTA leads DIRECTLY to the create flow. No intermediate "welcome" screen.
|
|
503
|
+
4. After successful creation, route back to the populated view with a success toast: "[Resource] created."
|
|
504
|
+
|
|
505
|
+
Anti-patterns:
|
|
506
|
+
|
|
507
|
+
- Empty state that just says "Loading…" for a second before content appears (use a skeleton instead)
|
|
508
|
+
- Empty state with multiple CTAs ("Create" AND "Learn more")
|
|
509
|
+
- Empty state that disables the CTA until the user completes onboarding
|
|
510
|
+
|
|
511
|
+
### onboarding
|
|
512
|
+
|
|
513
|
+
Philosophy: minimum viable onboarding. Skip is ALWAYS allowed.
|
|
514
|
+
|
|
515
|
+
Steps:
|
|
516
|
+
|
|
517
|
+
1. **Welcome screen** — one sentence about what the product does. One primary CTA: "Get started." One secondary: "Skip."
|
|
518
|
+
2. **Minimum collection** — name + primary use case. NO MORE THAN 2 INPUTS. If you need 5 fields, your onboarding is wrong.
|
|
519
|
+
3. **One contextual highlight** — when the user lands on the main view, point at the most important thing with a small popover. Just one. Dismissible.
|
|
520
|
+
4. **Mark as onboarded** — never show the onboarding again, even if the user clears state.
|
|
521
|
+
|
|
522
|
+
Forbidden:
|
|
523
|
+
|
|
524
|
+
- Multi-step wizards with 5+ screens
|
|
525
|
+
- Required onboarding (no skip allowed)
|
|
526
|
+
- Tutorial overlays that grey out the screen and block interaction
|
|
527
|
+
- Asking for payment, phone number, or company size in onboarding
|
|
528
|
+
|
|
529
|
+
### auth
|
|
530
|
+
|
|
531
|
+
Steps:
|
|
532
|
+
|
|
533
|
+
1. **Single screen, sign-in OR sign-up** — one email field. The button auto-detects: "Sign in" if account exists, "Create account" if not.
|
|
534
|
+
2. **Authentication method** — magic link OR password OR OAuth. NEVER show all three side by side. Pick one primary, others as smaller text-link alternatives.
|
|
535
|
+
3. **After auth** — route to the user's last intended destination. Default: dashboard.
|
|
536
|
+
4. **Errors are inline** — never in a toast. "Wrong password" appears below the password field.
|
|
537
|
+
|
|
538
|
+
Forbidden:
|
|
539
|
+
|
|
540
|
+
- Separate "Sign in" and "Sign up" pages with identical fields
|
|
541
|
+
- Forced password complexity requirements visible before the user has typed anything
|
|
542
|
+
- "Remember me" checkboxes (assume yes — provide a sign-out instead)
|
|
543
|
+
- CAPTCHA on the first login attempt
|
|
544
|
+
|
|
545
|
+
---
|
|
546
|
+
|
|
547
|
+
## Copy Rules
|
|
548
|
+
|
|
549
|
+
**Voice**: direct, human, never cute. Closest reference: Linear, Stripe, Vercel.
|
|
550
|
+
**Tense**: present.
|
|
551
|
+
**Person**: second person (you / your).
|
|
552
|
+
|
|
553
|
+
### Errors
|
|
554
|
+
|
|
555
|
+
- Tell the user (a) what failed, (b) what to do next.
|
|
556
|
+
- Never show error codes ("Error 500", "ENOENT", etc.). Translate.
|
|
557
|
+
- Bad: `Failed to fetch.`
|
|
558
|
+
- Good: `Couldn't load your projects. Check your connection and try again.`
|
|
559
|
+
|
|
560
|
+
### CTAs
|
|
561
|
+
|
|
562
|
+
- Format: verb + object. "Create project", "Send invite", "Delete account."
|
|
563
|
+
- Forbidden: "Submit", "OK", "Click here", "Continue" (when the action is destructive or specific).
|
|
564
|
+
|
|
565
|
+
### Empty states
|
|
566
|
+
|
|
567
|
+
- Encouraging, not apologetic.
|
|
568
|
+
- Bad: `Sorry, no projects found.`
|
|
569
|
+
- Good: `No projects yet. Create your first to get started.`
|
|
570
|
+
|
|
571
|
+
### Tooltips
|
|
572
|
+
|
|
573
|
+
- One sentence max.
|
|
574
|
+
- No trailing punctuation.
|
|
575
|
+
- Bad: `This will permanently delete the selected items. This action cannot be undone.`
|
|
576
|
+
- Good: `Delete the selected items`
|
|
577
|
+
|
|
578
|
+
### Placeholders
|
|
579
|
+
|
|
580
|
+
- Example values, never instructions.
|
|
581
|
+
- Bad: `Enter your email`
|
|
582
|
+
- Good: `jane@example.com`
|
|
583
|
+
|
|
584
|
+
### Labels
|
|
585
|
+
|
|
586
|
+
- Sentence case, singular.
|
|
587
|
+
- No colons at the end.
|
|
588
|
+
- Bad: `PROJECT NAMES:`
|
|
589
|
+
- Good: `Project name`
|
|
590
|
+
|
|
591
|
+
### Headings
|
|
592
|
+
|
|
593
|
+
- Title case for H1 page titles. Sentence case for H2/H3 section headings.
|
|
594
|
+
- Maximum 5 words for H1.
|
|
595
|
+
|
|
596
|
+
### Numbers
|
|
597
|
+
|
|
598
|
+
- Spell out one through nine in body copy. Use numerals for 10+.
|
|
599
|
+
- Always use numerals in UI labels, table cells, and stats.
|
|
600
|
+
|
|
601
|
+
---
|
|
602
|
+
|
|
603
|
+
## Principles
|
|
604
|
+
|
|
605
|
+
These are the north stars. When two rules conflict, the one closer to the top wins.
|
|
606
|
+
|
|
607
|
+
### 1. Accessibility is not a feature
|
|
608
|
+
|
|
609
|
+
WCAG AA minimum, no exceptions. Contrast enforced at the token level. ARIA
|
|
610
|
+
landmarks on every route. Keyboard reachable for every interactive element.
|
|
611
|
+
Focus-visible rings on everything that can be tabbed to. If your design fails
|
|
612
|
+
accessibility, it fails — period.
|
|
613
|
+
|
|
614
|
+
### 2. One primary action per screen
|
|
615
|
+
|
|
616
|
+
If the user lands on a screen and there are two equally important things to
|
|
617
|
+
do, redesign the screen. Secondary actions are visually subordinate. No
|
|
618
|
+
competing CTAs. The user should never have to ask "what's the main thing here?"
|
|
619
|
+
|
|
620
|
+
### 3. Error prevention over recovery
|
|
621
|
+
|
|
622
|
+
Design so mistakes are hard to make in the first place. Default to safe
|
|
623
|
+
inputs. Make destructive actions require deliberate effort (typed confirmation
|
|
624
|
+
for high-value cases). Surface validation early enough that the user can fix
|
|
625
|
+
it before they commit.
|
|
626
|
+
|
|
627
|
+
### 4. Progressive disclosure
|
|
628
|
+
|
|
629
|
+
Show what the user needs RIGHT NOW. Reveal more on demand. The default view
|
|
630
|
+
is the most common case. Power features live one click deeper. A user should
|
|
631
|
+
be able to ignore 80% of the interface 80% of the time.
|
|
632
|
+
|
|
633
|
+
### 5. Copy drives decisions
|
|
634
|
+
|
|
635
|
+
Write the copy before designing the UI. If the copy is vague, the UI is wrong.
|
|
636
|
+
If the heading takes 3 seconds to understand, no font choice will save it.
|
|
637
|
+
Test by reading aloud — if it sounds like a robot, rewrite it.
|
|
638
|
+
|
|
639
|
+
### 6. Keyboard navigable by default
|
|
640
|
+
|
|
641
|
+
Every interactive element is reachable via Tab in logical order. Every
|
|
642
|
+
interactive element shows a visible focus ring when focused. Esc dismisses
|
|
643
|
+
overlays (modals, popovers, dropdowns). Enter submits the focused form.
|
|
644
|
+
Arrow keys navigate within components (tabs, menus, listboxes).
|
|
645
|
+
|
|
646
|
+
### 7. Trust the system
|
|
647
|
+
|
|
648
|
+
Don't second-guess these rules to "make it pop" or "match the brand." The
|
|
649
|
+
brand is your color and your typography. Everything else is consistent across
|
|
650
|
+
all UXonFly-built apps so your AI doesn't drift between sessions. If a rule
|
|
651
|
+
is wrong, fix it in this file — don't override it case by case.
|