# ThrillWiki Nuxt 4 Frontend Standards Rules for developing the ThrillWiki frontend with Nuxt 4. ## Project Structure ``` frontend/ ├── app.vue # Root component ├── nuxt.config.ts # Nuxt configuration ├── pages/ # File-based routing │ ├── index.vue # Homepage (/) │ ├── parks/ │ │ ├── index.vue # /parks │ │ ├── nearby.vue # /parks/nearby │ │ └── [slug].vue # /parks/:slug │ └── ... ├── components/ │ ├── layout/ # Header, Footer, Sidebar │ ├── ui/ # Base components (Button, Card, Input) │ ├── entity/ # ParkCard, RideCard, ReviewCard │ └── forms/ # Form components ├── composables/ # Shared logic (useAuth, useApi, useUnits) ├── stores/ # Pinia stores ├── types/ # TypeScript interfaces └── assets/ └── css/ # Global styles, Tailwind config ``` ## Component Conventions ### Naming - Use PascalCase for component files: `ParkCard.vue`, `SearchAutocomplete.vue` - Use kebab-case in templates: ``, `` - Prefix base components with `Base`: `BaseButton.vue`, `BaseInput.vue` ### Component Structure ```vue ``` ### TypeScript - Enable strict mode - Define interfaces for all data structures in `types/` - Use `defineProps()` with TypeScript generics - No `any` types without explicit justification ## Routing ### File-Based Routes Follow Nuxt 4 file-based routing conventions: - `pages/index.vue` → `/` - `pages/parks/index.vue` → `/parks` - `pages/parks/[slug].vue` → `/parks/:slug` - `pages/parks/[park]/rides/[ride].vue` → `/parks/:park/rides/:ride` ### Navigation ```typescript // Use navigateTo for programmatic navigation await navigateTo('/parks/cedar-point') // Use NuxtLink for declarative navigation All Parks ``` ## Data Fetching ### Use Composables ```typescript // composables/useParks.ts export function useParks() { const { data, pending, error, refresh } = useFetch('/api/parks/') return { parks: data, loading: pending, error, refresh } } ``` ### In Components ```typescript // Use useAsyncData for page-level data const { data: park } = await useAsyncData( `park-${route.params.slug}`, () => $fetch(`/api/parks/${route.params.slug}/`) ) ``` ## State Management (Pinia) ### Store Structure ```typescript // stores/auth.ts export const useAuthStore = defineStore('auth', () => { const user = ref(null) const isAuthenticated = computed(() => !!user.value) async function login(credentials: LoginCredentials) { // Implementation } function logout() { user.value = null } return { user, isAuthenticated, login, logout } }) ``` ### Using Stores ```typescript const authStore = useAuthStore() const { user, isAuthenticated } = storeToRefs(authStore) ``` ## API Integration ### Base API Composable ```typescript // composables/useApi.ts export function useApi() { const config = useRuntimeConfig() const authStore = useAuthStore() return $fetch.create({ baseURL: config.public.apiBase, headers: { ...(authStore.token && { Authorization: `Bearer ${authStore.token}` }) } }) } ``` ## Error Handling ### Page Errors ```typescript // In page components const { data, error } = await useAsyncData(...) if (error.value) { throw createError({ statusCode: error.value.statusCode || 500, message: error.value.message }) } ``` ### Form Errors - Display validation errors inline with form fields - Use toast notifications for API errors - Provide clear user feedback ## Accessibility Requirements - All interactive elements must be keyboard accessible - Provide proper ARIA labels - Ensure color contrast meets WCAG AA standards - Support `prefers-reduced-motion` - Use semantic HTML elements