My frontend conventions
Skill AGilbertDev/agilbertdev-recipes/skills/my-frontend-conventions
My reusable coding-agent conventions and a curated skill set, pulled into any project like a versioned package.
npx -y skills add AGilbertDev/agilbertdev-recipes --skill my-frontend-conventionsAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
- 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
AGilbertDev's frontend conventions for Nuxt/Vue projects — component and composable choices, solution priority, and icons. Use when building UI, components, or pages in a personal Nuxt project. Pairs with my-styling-conventions for Tailwind and theming.
SKILL.md
5.1 KB, as published. Nobody here has run it
Frontend conventions (Nuxt / Vue)
Solution priority
Reach for solutions in this order, and stop at the first that fits:
- Nuxt UI components and composables
- Nuxt core features
- Custom Vue
- Tailwind utilities for styling gaps
Look up the official docs and explain the reasoning rather than guessing. When naming a component or API, name it exactly (for example UFormField with a UInput inside) so it is easy to look up.
Components
- Prefer Nuxt UI primitives (
UButton,UCard,UForm,UModal,UTable,UInput, and so on) before building custom. - Vue 3 Composition API with
<script setup>. Keep components small and composable; pull shared logic into composables.
Data mutations and cache invalidation
Always invalidate the client cache after a mutation. Any write that changes server state (a $fetch POST, PATCH, or DELETE) must be followed by refreshing whatever client-side cache reads that state, so the UI reflects the change without a full page reload.
- Data loaded with
useFetchoruseAsyncData: call the returnedrefresh()after the write, orrefreshNuxtData(key)for a shared key. - State that lives in the
nuxt-auth-utilssession (anything read offuser): callfetch()fromuseUserSession()after the write souserre-reads. - State derived from the session once does not re-derive when
userre-reads. AuseStateseeded fromuser(for example theuseThemelight and dark ids) and the active i18n locale keep their first value, so re-apply them by hand in the same success handler by setting theuseThemestate and callingsetLocale. Refreshing the session alone leaves these looking reverted. - An optimistic local update is fine for responsiveness, but the authoritative refetch still has to run so the cache and the server agree.
Never rely on the next navigation or reload to pick up a change. A stale client cache after a mutation is a bug.
Server state with TanStack Query
Use TanStack Query (@tanstack/vue-query) for reading and writing server state, layered on Nuxt's data fetching. Register it once in a Nuxt plugin with SSR hydration, dehydrating on the app:rendered hook and hydrating on the client.
- Query keys live in one factory file,
app/queries/keys.ts, exported as aqueryKeysobject with a function per key. A key is never hand-typed at a call site, so the keys a mutation invalidates always match the queries that produced them. - Query and mutation composables live in
app/composables/, auto-imported, nameduseXxxQueryanduseXxxMutation. A page reads and writes server state through these composables rather than a bare$fetch. - Every mutation invalidates the affected query keys in
onSuccesswithqueryClient.invalidateQueries. Session-backed state is not in the query cache, so it is still refreshed throughuseUserSession().fetch()in the sameonSuccess, following the mutations section above.
Loading state on submit
Every form submit shows a loading state on its submit control while the write is in flight. Bind the submit UButton's :loading to the mutation's isPending, or to the local in-flight flag when the write is not a TanStack mutation, and keep the control disabled until it settles. A slow write is then never mistaken for a dead button and cannot be double-submitted.
Icons
- Phosphor is the default set, via the Nuxt UI icon prop (
i-ph-*). Match the icon weight to the text it sits with: use the-boldvariants next to bold or large text so the glyph does not look thin, and scale the icon up as the text scales. Pick one weight family per project and stay consistent. - Simple Icons for brand and logo marks only (
i-simple-icons-*).
Page composition
- Build a long landing or portfolio page as one route that scrolls through sections, each its own component under
components/home/(hero, about, experience, and so on), assembled in the page. Keep the page file a thin list of those sections. - A small reusable
SectionHeadercomponent (a monotext-primarykicker plus the sectionh2) keeps headers consistent. Exactly oneh1(the hero), oneh2per section, in order. - For in-page anchor nav, keep the section ids in one composable (for example
useSectionId) so the nav links and each<section :id>always agree. On a bilingual site make the ids locale-aware there (#a-propos/#about), and translate the current hash when toggling locale so the toggle stays on the same section.
Scroll reveal
- Reveal sections on scroll with a small client plugin: add a
jsclass to<html>, hide[data-reveal]elements only when that class is present (so a no-JS render still shows everything), then add anis-inclass through anIntersectionObserveron mount and after each navigation. Stagger children with a--reveal-icustom property. Gate the whole effect behindprefers-reduced-motion.