ui-ux-consultant-cli 1.0.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/assets/ui-ux-consultant/SKILL.md +844 -0
- package/assets/ui-ux-consultant/references/accessibility.md +175 -0
- package/assets/ui-ux-consultant/references/alt-libraries.md +90 -0
- package/assets/ui-ux-consultant/references/animations.md +448 -0
- package/assets/ui-ux-consultant/references/catalog/colors.md +91 -0
- package/assets/ui-ux-consultant/references/catalog/fonts.md +363 -0
- package/assets/ui-ux-consultant/references/catalog/products.md +340 -0
- package/assets/ui-ux-consultant/references/catalog/styles.md +165 -0
- package/assets/ui-ux-consultant/references/components.md +1116 -0
- package/assets/ui-ux-consultant/references/patterns.md +600 -0
- package/assets/ui-ux-consultant/references/performance.md +198 -0
- package/assets/ui-ux-consultant/references/stacks/astro.md +382 -0
- package/assets/ui-ux-consultant/references/stacks/flutter.md +308 -0
- package/assets/ui-ux-consultant/references/stacks/html-tailwind.md +415 -0
- package/assets/ui-ux-consultant/references/stacks/jetpack-compose.md +333 -0
- package/assets/ui-ux-consultant/references/stacks/laravel.md +521 -0
- package/assets/ui-ux-consultant/references/stacks/nextjs.md +275 -0
- package/assets/ui-ux-consultant/references/stacks/nuxt-ui.md +384 -0
- package/assets/ui-ux-consultant/references/stacks/nuxtjs.md +264 -0
- package/assets/ui-ux-consultant/references/stacks/react-native.md +346 -0
- package/assets/ui-ux-consultant/references/stacks/react.md +268 -0
- package/assets/ui-ux-consultant/references/stacks/shadcn.md +485 -0
- package/assets/ui-ux-consultant/references/stacks/svelte.md +429 -0
- package/assets/ui-ux-consultant/references/stacks/swiftui.md +336 -0
- package/assets/ui-ux-consultant/references/stacks/threejs.md +366 -0
- package/assets/ui-ux-consultant/references/stacks/vue.md +272 -0
- package/assets/ui-ux-consultant/references/theming.md +701 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +130 -0
- package/package.json +51 -0
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
# Vue UI/UX Guidelines
|
|
2
|
+
|
|
3
|
+
## When to read this
|
|
4
|
+
Read this file when building UI with Vue 3 and the Composition API. Covers reactivity, composables, Pinia state, routing, forms, and component patterns for modern Vue apps.
|
|
5
|
+
|
|
6
|
+
## Recommended UI Libraries
|
|
7
|
+
|
|
8
|
+
| Library | Best for | Install |
|
|
9
|
+
|---|---|---|
|
|
10
|
+
| Vuetify 3 | Material Design components | `npm install vuetify` |
|
|
11
|
+
| PrimeVue | Rich component set | `npm install primevue` |
|
|
12
|
+
| Element Plus | Enterprise UI | `npm install element-plus` |
|
|
13
|
+
| Pinia | State management | `npm install pinia` |
|
|
14
|
+
| VueUse | Composable utilities | `npm install @vueuse/core` |
|
|
15
|
+
| Vee-Validate | Form validation | `npm install vee-validate` |
|
|
16
|
+
|
|
17
|
+
## Style Recommendations by App Type
|
|
18
|
+
|
|
19
|
+
- **Enterprise/admin:** Element Plus + minimal custom style
|
|
20
|
+
- **Consumer product:** Vuetify 3 + Material Design tokens
|
|
21
|
+
- **Custom brand:** PrimeVue + custom design tokens via CSS variables
|
|
22
|
+
- **Documentation/content:** Headless + custom Tailwind
|
|
23
|
+
|
|
24
|
+
## Top UX Patterns
|
|
25
|
+
|
|
26
|
+
### 1. Async Data with Loading and Error States
|
|
27
|
+
```vue
|
|
28
|
+
<script setup lang="ts">
|
|
29
|
+
const { data, pending, error } = await useFetch('/api/users');
|
|
30
|
+
</script>
|
|
31
|
+
|
|
32
|
+
<template>
|
|
33
|
+
<div v-if="pending">Loading...</div>
|
|
34
|
+
<div v-else-if="error">{{ error.message }}</div>
|
|
35
|
+
<UserList v-else :users="data" />
|
|
36
|
+
</template>
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### 2. Pinia Store with Composition API Style
|
|
40
|
+
```typescript
|
|
41
|
+
// stores/user.ts
|
|
42
|
+
import { defineStore } from 'pinia';
|
|
43
|
+
import { ref, computed } from 'vue';
|
|
44
|
+
|
|
45
|
+
export const useUserStore = defineStore('user', () => {
|
|
46
|
+
const user = ref<User | null>(null);
|
|
47
|
+
const isLoggedIn = computed(() => !!user.value);
|
|
48
|
+
|
|
49
|
+
async function login(creds: Credentials) {
|
|
50
|
+
user.value = await api.login(creds);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function logout() {
|
|
54
|
+
user.value = null;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return { user, isLoggedIn, login, logout };
|
|
58
|
+
});
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### 3. Composable for Reusable Logic
|
|
62
|
+
```typescript
|
|
63
|
+
// composables/useSearch.ts
|
|
64
|
+
import { ref, computed } from 'vue';
|
|
65
|
+
import type { Ref } from 'vue';
|
|
66
|
+
|
|
67
|
+
export function useSearch<T>(items: Ref<T[]>, key: keyof T) {
|
|
68
|
+
const query = ref('');
|
|
69
|
+
const filtered = computed(() =>
|
|
70
|
+
query.value
|
|
71
|
+
? items.value.filter(i => String(i[key]).toLowerCase().includes(query.value.toLowerCase()))
|
|
72
|
+
: items.value
|
|
73
|
+
);
|
|
74
|
+
return { query, filtered };
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Usage in component
|
|
78
|
+
const { query, filtered } = useSearch(products, 'name');
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### 4. Optimistic Update with Rollback
|
|
82
|
+
```typescript
|
|
83
|
+
async function toggleFavorite(id: string) {
|
|
84
|
+
const store = useProductStore();
|
|
85
|
+
const prev = [...store.products];
|
|
86
|
+
store.products = store.products.map(p =>
|
|
87
|
+
p.id === id ? { ...p, favorite: !p.favorite } : p
|
|
88
|
+
);
|
|
89
|
+
try {
|
|
90
|
+
await api.toggleFavorite(id);
|
|
91
|
+
} catch {
|
|
92
|
+
store.products = prev;
|
|
93
|
+
toast.error('Update failed');
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### 5. Template Ref and Lifecycle
|
|
99
|
+
```vue
|
|
100
|
+
<script setup lang="ts">
|
|
101
|
+
import { ref, onMounted } from 'vue';
|
|
102
|
+
|
|
103
|
+
const inputRef = ref<HTMLInputElement | null>(null);
|
|
104
|
+
|
|
105
|
+
onMounted(() => {
|
|
106
|
+
inputRef.value?.focus();
|
|
107
|
+
});
|
|
108
|
+
</script>
|
|
109
|
+
|
|
110
|
+
<template>
|
|
111
|
+
<input ref="inputRef" placeholder="Auto-focused on mount" />
|
|
112
|
+
</template>
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### 6. Controlled Form with Vee-Validate and Zod
|
|
116
|
+
```vue
|
|
117
|
+
<script setup lang="ts">
|
|
118
|
+
import { useForm } from 'vee-validate';
|
|
119
|
+
import { toTypedSchema } from '@vee-validate/zod';
|
|
120
|
+
import { z } from 'zod';
|
|
121
|
+
|
|
122
|
+
const schema = toTypedSchema(z.object({
|
|
123
|
+
email: z.string().email(),
|
|
124
|
+
password: z.string().min(8),
|
|
125
|
+
}));
|
|
126
|
+
|
|
127
|
+
const { defineField, handleSubmit, errors } = useForm({ validationSchema: schema });
|
|
128
|
+
const [email, emailAttrs] = defineField('email');
|
|
129
|
+
const [password, passwordAttrs] = defineField('password');
|
|
130
|
+
|
|
131
|
+
const onSubmit = handleSubmit(values => console.log(values));
|
|
132
|
+
</script>
|
|
133
|
+
|
|
134
|
+
<template>
|
|
135
|
+
<form @submit="onSubmit">
|
|
136
|
+
<input v-bind="emailAttrs" v-model="email" type="email" />
|
|
137
|
+
<span>{{ errors.email }}</span>
|
|
138
|
+
<input v-bind="passwordAttrs" v-model="password" type="password" />
|
|
139
|
+
<span>{{ errors.password }}</span>
|
|
140
|
+
<button type="submit">Login</button>
|
|
141
|
+
</form>
|
|
142
|
+
</template>
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### 7. Dynamic Component with defineAsyncComponent
|
|
146
|
+
```typescript
|
|
147
|
+
import { defineAsyncComponent } from 'vue';
|
|
148
|
+
|
|
149
|
+
const HeavyChart = defineAsyncComponent({
|
|
150
|
+
loader: () => import('./HeavyChart.vue'),
|
|
151
|
+
loadingComponent: ChartSkeleton,
|
|
152
|
+
errorComponent: ErrorFallback,
|
|
153
|
+
delay: 200,
|
|
154
|
+
});
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### 8. Provide / Inject for Deep Prop Passing
|
|
158
|
+
```typescript
|
|
159
|
+
// Parent component
|
|
160
|
+
import { provide, ref } from 'vue';
|
|
161
|
+
|
|
162
|
+
const theme = ref('light');
|
|
163
|
+
provide('theme', theme); // Provide reactive value
|
|
164
|
+
|
|
165
|
+
// Deep child component
|
|
166
|
+
import { inject } from 'vue';
|
|
167
|
+
|
|
168
|
+
const theme = inject<Ref<string>>('theme', ref('light')); // With default
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### 9. Watch with Cleanup
|
|
172
|
+
```typescript
|
|
173
|
+
import { watch, ref } from 'vue';
|
|
174
|
+
|
|
175
|
+
const query = ref('');
|
|
176
|
+
|
|
177
|
+
watch(query, async (newQuery, _, onCleanup) => {
|
|
178
|
+
const controller = new AbortController();
|
|
179
|
+
onCleanup(() => controller.abort()); // Cancel previous fetch on change
|
|
180
|
+
results.value = await fetch(`/api/search?q=${newQuery}`, {
|
|
181
|
+
signal: controller.signal,
|
|
182
|
+
}).then(r => r.json());
|
|
183
|
+
});
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### 10. Transition for Animated List
|
|
187
|
+
```vue
|
|
188
|
+
<template>
|
|
189
|
+
<TransitionGroup name="list" tag="ul">
|
|
190
|
+
<li v-for="item in items" :key="item.id">{{ item.name }}</li>
|
|
191
|
+
</TransitionGroup>
|
|
192
|
+
</template>
|
|
193
|
+
|
|
194
|
+
<style>
|
|
195
|
+
.list-enter-active, .list-leave-active { transition: all 0.3s ease; }
|
|
196
|
+
.list-enter-from, .list-leave-to { opacity: 0; transform: translateX(30px); }
|
|
197
|
+
</style>
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
## Best Practices by Category
|
|
201
|
+
|
|
202
|
+
### State
|
|
203
|
+
- `ref()` for primitives and single values
|
|
204
|
+
- `reactive()` for complex objects — never destructure (loses reactivity)
|
|
205
|
+
- `computed()` for derived state — do not duplicate in `ref`
|
|
206
|
+
- `watch()` for side effects triggered by specific reactive sources
|
|
207
|
+
- `watchEffect()` for effects that auto-track all used reactive values
|
|
208
|
+
- Pinia for shared application state — use `storeToRefs()` when destructuring
|
|
209
|
+
|
|
210
|
+
### Components
|
|
211
|
+
- `<script setup>` for all new components — less boilerplate, better TS support
|
|
212
|
+
- Composition API exclusively in new code — not Options API
|
|
213
|
+
- TypeScript with `defineProps<Props>()` and `defineEmits<Emits>()`
|
|
214
|
+
- Single-file `.vue` components — PascalCase names
|
|
215
|
+
- Composables over mixins for reusable logic
|
|
216
|
+
- `defineExpose()` only for intentional public component APIs
|
|
217
|
+
|
|
218
|
+
### Routing
|
|
219
|
+
- `useRouter()` and `useRoute()` composables — not `this.$router`
|
|
220
|
+
- Lazy-load all routes: `component: () => import('./Page.vue')`
|
|
221
|
+
- Navigation guards for authentication — `router.beforeEach`
|
|
222
|
+
- Named routes for programmatic navigation — avoid hardcoded path strings
|
|
223
|
+
|
|
224
|
+
### Performance
|
|
225
|
+
- `v-once` for static content that truly never changes
|
|
226
|
+
- `v-memo` for expensive list items with stable deps
|
|
227
|
+
- `shallowReactive()` / `shallowRef()` for large objects that don't need deep reactivity
|
|
228
|
+
- `defineAsyncComponent()` for heavy components not needed on initial render
|
|
229
|
+
- `:key="item.id"` on all `v-for` — never use index as key for dynamic lists
|
|
230
|
+
|
|
231
|
+
### Forms
|
|
232
|
+
- `v-model` for two-way binding on all inputs
|
|
233
|
+
- `@submit.prevent` on form element — prevents full page reload
|
|
234
|
+
- Vee-Validate + Zod for schema-driven validation
|
|
235
|
+
- Separate composable for complex form logic
|
|
236
|
+
|
|
237
|
+
### Accessibility
|
|
238
|
+
- Use native HTML elements: `<button>`, `<a>`, `<input>`, `<select>`
|
|
239
|
+
- `aria-live="polite"` for dynamically updated regions
|
|
240
|
+
- `:key` on all `v-for` for correct DOM reconciliation
|
|
241
|
+
- Focus management after route changes and modal close
|
|
242
|
+
|
|
243
|
+
### Props and Emits
|
|
244
|
+
- `defineProps` with TypeScript generics for type safety
|
|
245
|
+
- Never mutate props — emit an event or use a local `ref` copy
|
|
246
|
+
- Use `v-model` pattern for two-way component bindings: emit `update:modelValue`
|
|
247
|
+
|
|
248
|
+
## Common Anti-Patterns
|
|
249
|
+
|
|
250
|
+
1. Destructuring `reactive()` object — loses reactivity: `const { name } = reactive(user)` breaks
|
|
251
|
+
2. Mutating props directly — always emit or create a local copy with `ref`
|
|
252
|
+
3. Options API in new Vue 3 code — use Composition API with `<script setup>`
|
|
253
|
+
4. `this.$refs` in Composition API — use template `ref()` instead
|
|
254
|
+
5. Large single-file components over 300 lines — split into composables and sub-components
|
|
255
|
+
6. `:key="index"` on `v-for` with dynamic lists — breaks reconciliation on reorder
|
|
256
|
+
7. Not using `storeToRefs()` when destructuring Pinia store — loses reactivity
|
|
257
|
+
8. `v-if` and `v-for` on the same element — always put `v-if` on a parent wrapper
|
|
258
|
+
9. Storing non-reactive data in `reactive()` — use plain variables for constants
|
|
259
|
+
10. Missing `onUnmounted` cleanup for event listeners and timers
|
|
260
|
+
|
|
261
|
+
## Performance Checklist
|
|
262
|
+
|
|
263
|
+
- [ ] `v-once` on static content that never changes after first render
|
|
264
|
+
- [ ] `v-memo` for expensive list items where deps rarely change
|
|
265
|
+
- [ ] `shallowReactive()` for large flat objects that don't need deep reactivity
|
|
266
|
+
- [ ] `defineAsyncComponent()` for heavy third-party components
|
|
267
|
+
- [ ] `storeToRefs()` when destructuring any Pinia store
|
|
268
|
+
- [ ] `:key="item.id"` on all `v-for` loops — no index keys on mutable lists
|
|
269
|
+
- [ ] Route-level lazy loading for all page components
|
|
270
|
+
- [ ] `watch` with `{ lazy: true }` when you don't need immediate execution
|
|
271
|
+
- [ ] Bundle analysis with Vite's `rollup-plugin-visualizer`
|
|
272
|
+
- [ ] Avoid large watcher chains — prefer `computed` for derived values
|