This document outlines the coding standards and style guidelines for the Nuxtware project.
- Indentation: Use 4 spaces for indentation (not tabs)
- Quotes: Prefer single quotes for JavaScript/TypeScript strings
- Semicolons: Don't use semicolons at the end of statements
- Line Length: Aim to keep lines under 100 characters
- Use
<script setup lang="ts">syntax for Vue components - Component names should use PascalCase
- Props should be properly typed with TypeScript interfaces
- Use composition API instead of options API
- Use self-closing tags for HTML elements that don't have content (e.g.,
<slot />) - Rely on Nuxt's auto-imports for Vue functions instead of explicit imports
- Avoid inline CSS in SFCs - use external CSS files in the assets/styles directory
- Avoid dynamic Tailwind class names (e.g.,
bg-${color}-500) as they won't be detected by Tailwind's purging process - Components must follow these requirements:
- Zero dependencies (base deps only e.g.: Vue, VueUse, Tailwind, CSS, HTML5)
- Configurable / Reusable
- Fully typed (TypeScript)
- "Theme"-able (uses the existing design system)
- Translatable (all text elements use i18n)
- Accessible:
- Semantically correct HTML
- Screen reader support
- Keyboard support
- User feedback
- Responsive design
- Target Size (24px × 24px for links and 44px × 44px for buttons)
- SEO (if relevant):
- Semantically correct HTML
- SSR support
When building complex templates, follow these guidelines to ensure maintainability and performance:
-
Use
<template>tags withv-forwhen conditional rendering is needed within loops:<!-- Preferred --> <template v-for="(item, index) in items" :key="item.id"> <div v-if="shouldRenderItem(item)" class="item"> {{ item.name }} </div> </template> <!-- Avoid --> <div v-for="(item, index) in items" :key="item.id" v-if="shouldRenderItem(item)" class="item"> {{ item.name }} </div>
-
Group related attributes together in a consistent order:
- Key/ref attributes (
:key,ref) - Structural directives (
v-if,v-else,v-for) - Other v-directives (
v-model,v-bind) - Standard HTML attributes (
class,id, etc.) - Aria attributes (
aria-*,role) - Event handlers (
@click, etc.)
- Key/ref attributes (
-
For complex conditional rendering, use computed properties instead of complex expressions:
<!-- Preferred --> <div v-if="isItemVisible">{{ item.name }}</div> <!-- Avoid --> <div v-if="item.isActive && !item.isHidden && currentFilter === 'all'"> {{ item.name }} </div>
When working with Vue's reactivity system, be consistent in how you access reactive values:
-
Always use
.valueto access values fromref()andcomputed()properties:// Correct const count = ref(0) function increment() { count.value++ } // Also correct - accessing value from composable if (getInitialFilters.value.length > 0) { // Do something } // Incorrect - missing .value function decrement() { count-- // This won't work! }
-
Remember that properties from composables are often refs or computed values:
// Composable usage const { items, isLoading } = useMyComposable() // Correct if (!isLoading.value && items.value.length > 0) { processItems() }
-
For reactive objects created with
reactive(), do not use.value:// Correct const state = reactive({ count: 0 }) function increment() { state.count++ }
-
Be consistent with destructuring - prefer using
toRefsinstead of manual destructuring:// Preferred const state = reactive({ count: 0, name: 'Example' }) const { count, name } = toRefs(state) // Now you can use count.value and name.value while maintaining reactivity
- Use component-specific composables (e.g.,
useCart()) instead of direct API calls - Handle errors as arrays to display multiple error messages when needed
- Manage state through composables rather than component-local state where appropriate
- Use
getFormattedPrice()fromusePrice()for all price displays
- Use self-closing HTML tags for void elements (e.g.,
<img>not<img />) - Maintain consistent spacing in templates and script sections
- Use Foundation component props instead of custom styling when available:
- Use
squareattribute instead of custom styling for square buttons - Use
size="small"instead of custom width/height classes - Set
aria-hidden="true"on decorative icons
- Use
- Use theme tokens for colors (e.g.,
border-borderinstead ofborder-gray-200) - Follow icon size standards (typically
w-2 h-2for small icons) - Use refined responsive width patterns (e.g.,
w-[90%] md:w-[400px]) - Apply consistent spacing from the design system
- Use standard icons from the system
- Use translated ARIA labels with
$t()(e.g.,:aria-label="$t('cart.remove')") - Ensure appropriate sizing for interactive elements
- Apply appropriate button variants in different contexts
- Use
useLocalePath()andformatLink()for proper i18n of navigation paths - Ensure all user-facing text uses translation keys
Avoid these common issues to maintain code quality and prevent bugs:
-
Remove unused parameters from functions:
// Incorrect function shouldRenderFilter(filter: any, index: number): boolean { // index is never used return filter.isActive } // Correct function shouldRenderFilter(filter: any): boolean { return filter.isActive }
-
Declare types for all variables and parameters:
// Incorrect let resizeTimer; // Correct let resizeTimer: number;
-
Always check if reactive properties are defined before accessing nested properties:
// Incorrect if (items.value.length > 0) { ... } // Correct if (items.value && items.value.length > 0) { ... }
-
Use optional chaining for safer property access:
// Preferred const itemName = items.value?.[0]?.name
-
Always check if DOM references exist before accessing their properties:
// Incorrect const width = containerRef.value.offsetWidth // Correct const width = containerRef.value?.offsetWidth || 0
-
Clean up event listeners and observers to prevent memory leaks:
// Set up listeners onMounted(() => { window.addEventListener('resize', handleResize) }) // Clean up onBeforeUnmount(() => { window.removeEventListener('resize', handleResize) })
- Use explicit type annotations where possible
- Use interfaces for object shapes
- Prefix interface names with appropriate context (e.g.,
WidgetSearchInputProps) - Use semicolons within type definitions
- JSON files should be properly formatted with 4-space indentation
- Each key should appear only once at each level
- Nested properties should maintain consistent indentation
- Use utility classes where appropriate
- Custom CSS should follow BEM naming convention when needed
- Keys should be organized hierarchically by feature/component
- Keys should be descriptive of their purpose
- Translation files should mirror each other in structure between languages
- Always use
$t()for translatable text, avoid hardcoded strings