You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
description: Nuxt application conventions. Use whenever the task touches a Nuxt app — pages, layouts, Nuxt plugins, middleware, app composables, Nitro server routes, `useFetch`, `useAsyncData`, `useRequestFetch`, `$fetch`, `definePageMeta`, runtime config, or Nuxt-specific typing. Make sure to use this skill for any work on Nuxt code, Nitro handlers, or Nuxt-aware data fetching, even when the user only mentions Vue, SSR, typed responses, or `$fetch` without naming Nuxt explicitly.
4
+
---
5
+
6
+
# Nuxt Conventions
7
+
8
+
This skill collects conventions for building Nuxt applications. It covers Nuxt runtime behavior, Nitro server routes, data fetching, and typing patterns that rely on Nuxt's build-time integration.
9
+
10
+
Treat it as the home for Nuxt-specific rules. Non-Nuxt Vue component conventions — component structure, props, emits, styling — belong elsewhere.
11
+
12
+
## Conventions
13
+
14
+
Each convention below is a self-contained rule. Apply the rules that match the task. When new Nuxt-specific patterns become stable, add them here as additional sections.
15
+
16
+
### Import Nuxt APIs from `#imports`
17
+
18
+
When a Nuxt file needs framework APIs, import them explicitly from `#imports`, never from `#app`.
19
+
20
+
Rules:
21
+
22
+
- With auto-imports disabled, every Nuxt composable still needs an explicit import.
23
+
- Import Nuxt runtime APIs such as `useRoute`, `useFetch`, `navigateTo`, `definePageMeta`, `useState`, and `useCookie` from `#imports`.
24
+
- Do not move this guidance into the Vue component skill. It is Nuxt-specific and belongs here.
25
+
26
+
### Type internal fetch through the Nitro handler
27
+
28
+
Nuxt infers response types for its own `/api/...` routes from the Nitro handler's return type. Use that inference instead of duplicating generics at the call site.
29
+
30
+
Rules:
31
+
32
+
- Annotate every Nitro handler with an explicit return type, usually `Promise<YourResponse>`.
33
+
- Call internal routes with `useFetch('/api/...')` without a response generic.
34
+
- When the task needs an imperative internal request, obtain request-aware `$fetch` with `const $fetch = useRequestFetch()` and call it without a response generic.
35
+
- Do not import `$fetch` from `ofetch` for internal app-to-app requests.
36
+
37
+
Why it matters:
38
+
39
+
- A typed handler gives one source of truth for the response shape. A generic at the call site can drift away from the real payload without any compile-time complaint.
40
+
-`useRequestFetch()` forwards the current request context, including headers and cookies, into the app's own routes during SSR. A plain `$fetch` from `ofetch` does not carry that context, so protected internal routes can lose session state.
This convention applies to the app's own internal routes only. External HTTP targets do not participate in Nuxt route inference and do not need `useRequestFetch()` to forward cookies into the app's own API. For them, use the client that fits the integration and type the response at the call site when needed.
72
+
73
+
```ts
74
+
import { $fetch } from'ofetch'
75
+
76
+
interfaceExternalUserResponse {
77
+
id:string;
78
+
}
79
+
80
+
const user =await$fetch<ExternalUserResponse>('https://example.com/api/user')
Copy file name to clipboardExpand all lines: .agents/skills/vue-components/SKILL.md
+52-12Lines changed: 52 additions & 12 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,6 +1,6 @@
1
1
---
2
2
name: vue-components
3
-
description: Vue component conventions and patterns for the Perd project. Use when creating new Vue components, editing existing .vue files, reviewing Vue code, adding styles to components, defining props/emits/slots, working with CSS Modules, writing media queries, or when the user mentions Vue components, component styling, CSS Modules, props, emits, or any .vue file changes. Apply these rules during code generation, code review, and refactoring of Vue components.
3
+
description: Vue component conventions and patterns for the Perd project. Use when creating new Vue components, editing existing .vue files, reviewing Vue code, adding styles to components, defining props/emits/slots, working with CSS Modules, writing media queries, or when the user mentions Vue components, component styling, CSS Modules, props, emits, or any .vue file changes. Excludes framework-owned routing, pages/layouts, app composables, and app-level data-fetching patterns.
4
4
---
5
5
6
6
# Vue Component Conventions
@@ -29,20 +29,16 @@ Type exports that other components need go in a separate non-setup script block
29
29
30
30
Auto-imports are disabled. Every import must be explicit.
31
31
32
-
- Nuxt composables (`useRoute`, `useFetch`, `navigateTo`, `definePageMeta`, `useState`, `useCookie`, etc.) come from `#imports` — never from `#app`.
33
-
-`$fetch` comes from `'ofetch'` — it is not available through `#imports` when auto-imports are disabled.
34
32
- Vue APIs (`ref`, `computed`, `onMounted`, etc.) come from `'vue'`.
35
33
- VueUse composables come from `'@vueuse/core'`.
36
34
- Shared project code comes from `#shared/...`.
37
-
-Components use relative `~/components/...` paths.
35
+
-Component imports should follow the path style already established by the owning feature instead of introducing alias-specific rules here.
The project uses SSR (server-side rendering). Never access browser globals (`document`, `window`, `navigator`, etc.) directly in component code — it will crash on the server.
102
+
Template bindings should be simple: a prop, a reactive ref, or a computed reference. When a binding requires any expression — `||`, `&&`, a ternary, a negation, or anything beyond a plain identifier — move that logic into a `computed` in the script section and bind the computed instead.
107
103
108
-
Use VueUse composables for DOM interactions instead. They handle SSR automatically by no-oping on the server:
104
+
The reason is that templates are meant to be declarative: they should describe *what* to render, not *how* to compute it. Keeping expressions out of the template makes the logic independently readable, ensures TypeScript can properly infer the bound type, and makes it easy to reason about each binding without tracing expression chains in the markup.
105
+
106
+
```ts
107
+
// In <script setup> — logic lives here
108
+
const ariaBusy =computed(() =>loading||undefined)
109
+
```
110
+
111
+
```html
112
+
<!-- In <template> — binding stays clean -->
113
+
:aria-busy="ariaBusy"
114
+
```
115
+
116
+
### Browser APIs
117
+
118
+
Never access browser globals (`document`, `window`, `navigator`, etc.) directly during component setup. Prefer VueUse composables for DOM interactions because they degrade safely when the DOM is unavailable:
109
119
110
120
-`useEventListener` — instead of `document.addEventListener` / `element.addEventListener`
111
121
-`onClickOutside` — instead of manual pointerdown + contains checks
112
122
- Other `@vueuse/core` composables as needed
113
123
114
124
```ts
115
-
// Correct — SSR-safe
125
+
// Correct
116
126
import { useEventListener } from'@vueuse/core'
117
127
118
128
useEventListener(dialogRef, 'close', () => {
119
129
isOpened.value=false
120
130
})
121
131
122
-
// Wrong — crashes during SSR
132
+
// Wrong
123
133
document.addEventListener('pointerdown', handler)
124
134
```
125
135
@@ -154,6 +164,30 @@ Every component uses `<style module>` — never `<style scoped>`. The root class
154
164
</style>
155
165
```
156
166
167
+
Use CSS module classes directly in templates with `$style`. Do not import `useCssModule()` just to compose class lists in script. Keep class names in the template and move only the logic into computed values:
Avoid inline conditional logic in class bindings. Do not write route comparisons, ternaries, negations, or boolean expressions directly inside template class objects; compute them in script first.
190
+
157
191
### Modifier Pattern with :global()
158
192
159
193
State-based class modifiers use plain string classes in the template combined with `&:global(.modifier)` in CSS. This is the project's established convention for CSS Modules — it is intentional and correct:
@@ -178,6 +212,12 @@ State-based class modifiers use plain string classes in the template combined wi
178
212
179
213
The modifier string (e.g., `visible`, `active`, `small`) is a plain class name — not a `$style` reference. The `&:global(.modifier)` selector escapes the CSS Module hashing so it matches the plain class.
180
214
215
+
### Naming And Selectors
216
+
217
+
Use full, readable names for component prop values and CSS/state variants. Prefer `medium`, `small`, `icon-only`, and `icon-small` over abbreviations like `md`, `sm`, or `icon-sm`.
218
+
219
+
Style owned markup through explicit classes on the element being styled. Avoid nested tag selectors such as `& em`, `& span`, or `& strong` when the element can be given its own class.
220
+
181
221
### CSS Features (Baseline 2025)
182
222
183
223
The project targets modern browsers only. Use these freely:
Copy file name to clipboardExpand all lines: AGENTS.md
+22Lines changed: 22 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -18,10 +18,24 @@
18
18
- Public reference-data read routes use `slug`; admin mutations use stable `id` params.
19
19
- All tables live in `server/database/schema.ts`, organized by Auth, Equipment catalog, and User data sections.
20
20
21
+
## Visual design prototype
22
+
23
+
The designer's visual prototype of the final application lives in `new-design-assets/`. It is the source of truth for UI layout, component appearance, color tokens, typography, spacing, and interaction patterns.
24
+
25
+
When creating or updating Vue components, page layouts, or any visual styles, consult `new-design-assets/` first:
-`new-design-assets/styles/base.css` and `components.css` — global resets and component-level styles.
29
+
-`new-design-assets/scripts/` — JSX view files that show per-screen layout and component structure (`view-dashboard.jsx`, `view-catalog.jsx`, `view-detail.jsx`, etc.).
30
+
-`new-design-assets/index.html` — runnable prototype; open in a browser to inspect the full interactive design.
31
+
32
+
Do not invent visual decisions (colors, spacing values, component shapes) that contradict the prototype. If the prototype does not cover a case, match the nearest existing pattern from it.
33
+
21
34
## Local skills
22
35
23
36
Before any task, check whether a local skill matches the domain and follow it.
24
37
38
+
-`nuxt-app` for Nuxt pages, layouts, app composables, internal API fetch typing, request-aware `useRequestFetch()`, and Nitro handler return types that drive internal route inference.
25
39
-`vue-components` for `.vue` component structure, styling, props/emits, and SSR-safe frontend patterns.
26
40
-`equipment-backend` for equipment/catalog backend work in `server/api`, validation schemas, Drizzle write paths, schema changes, and equipment API tests.
27
41
-`planning-docs` for roadmap files in `plan/`, completed work notes, architecture docs, and iteration planning tasks.
@@ -30,6 +44,14 @@ Before any task, check whether a local skill matches the domain and follow it.
30
44
31
45
- Files imported by standalone Node or `tsx` scripts, including `tools/*.ts`, migrations, seeds, and their transitive dependencies, must not rely on Nuxt-only aliases like `~/` or `@@/`. If they use `#shared/*` or `#server/*`, keep those aliases backed by `package.json#imports`.
32
46
- Do not implement fixes, features, compatibility branches, or refactors unless they address a reproducible problem, an explicitly requested behavior, or a currently supported project scenario. Treat hypothetical improvements and broader compatibility ideas as follow-up work, not as justification for changing the current behavior.
47
+
- Async action areas must remain structurally stable while state is loading or mutating. Keep the same interactive control mounted and change its state instead of swapping it out for unrelated text blocks.
48
+
- Related navigation and actions must be grouped by user meaning. Do not tuck inventory actions or similar workflow controls into unrelated content sections such as property lists.
49
+
- Plan each "next iteration" as a sequence of minimal self-contained tasks. Every task must state its intended result, scope boundaries, and required verification.
50
+
- When one iteration spans multiple tasks, each task must be safe to complete, commit, and validate independently without hidden follow-up decisions.
51
+
- New Playwright scenarios should be run against their individual spec file before the full `pnpm run test:e2e:ci` suite.
52
+
- Playwright `context.route(...)` matchers must account for query strings whenever the real endpoint is requested with query parameters.
53
+
- E2E flows that rely on mocked auth should not use `page.goto()` after login unless the test also establishes a real server-side session cookie.
54
+
- If browser tests run through `wrangler dev`, the E2E preview path must keep Wrangler state, logs, and config under a writable temp directory instead of the user's default `~/.config`.
33
55
- After any architecture, route-convention, data-model, or test-strategy change, update the relevant docs in the same change. This can include `AGENTS.md`, `plan/PLAN.md`, `plan/completed.md`, or the detailed roadmap file that changed.
A web app for those who love hanging out in the forest but always forget to bring something important. With this app, you can easily create a checklist of things you must take on your adventure.
5
+
Perd is an outdoor equipment companion for people who plan trips, hikes, and other outdoor outings and do not want to forget important gear.
6
6
7
-
## Similar applications
7
+
The product direction is built around one practical loop:
0 commit comments