8.0 KiB
8.0 KiB
Coding Conventions
Analysis Date: 2026-04-07
Naming Patterns
Files:
- Vue components: PascalCase (e.g.,
AppHeader.vue,ProjectCard.vue) - Composables: camelCase with
useprefix (e.g.,useTheme.ts,useProjects.ts) - Utility/config files: camelCase (e.g.,
site.ts,techstack.ts) - Data files: camelCase (e.g.,
testimonials.ts,faq.ts) - Type definitions: camelCase in
types/index.ts
Functions:
- All functions use camelCase (e.g.,
toggleTheme,openGallery,getImageUrl) - Composables are named with
useprefix:useTheme(),useGallery(),useSeo() - Getter functions use
getprefix:getTheme(),getImageUrl() - Boolean functions/computed use
is/hasprefix:isDark,hasNext,isOpen - Handler functions use verb +
Handler:toggleTheme,openGallery,closeGallery
Variables:
- Refs and computed properties: camelCase (e.g.,
isDark,currentIndex,isOpen) - Interfaces and types: PascalCase (e.g.,
Props,SeoOptions,Theme) - Constants: UPPER_SNAKE_CASE for config constants (not extensively used in codebase)
- Private/module state: camelCase prefixed with
_if truly private
Types:
- Type aliases: PascalCase (e.g.,
type Theme = 'light' | 'dark') - Interface names: PascalCase (e.g.,
interface Props,interface SeoOptions) - Props interfaces: Always named
Props(e.g., in<script setup lang="ts">components) - Generic types from Vue use their original names (e.g.,
Ref<boolean>,Computed<string>)
Code Style
Formatting:
- Tool: Prettier 3.5.3
- Semi-colons: disabled (
semi: false) - Quotes: single quotes (
singleQuote: true) - Print width: 100 characters (
printWidth: 100)
Linting:
- Tool: ESLint 9.22.0 with Vue support
- Config:
eslint.config.tsusing flat config format - Plugins:
@vue/eslint-config-typescript- TypeScript supporteslint-plugin-vuev10.0.0 - Vue 3 rules@vue/eslint-config-prettier/skip-formatting- Prettier integration (skip-formatting enabled)
Import Organization
Order:
- Vue and framework imports (
vue,vue-router,pinia,vue-i18n) - Type imports (use
import typefor TypeScript types) - Local components (
@/components/...) - Composables (
@/composables/...) - Utilities and helpers
- Data and configuration files
- Styles (scoped CSS imported at end of
<style>)
Path Aliases:
@/maps to./src/(configured intsconfig.app.json)- Always use
@/prefix for imports from src directory - Examples:
import AppHeader from '@/components/layout/AppHeader.vue'import { useTheme } from '@/composables/useTheme'import type { Project } from '@/types'import { techStack } from '@/data/techstack'
Error Handling
Patterns:
- Try-catch blocks wrap risky operations (e.g., dynamic imports, DOM manipulation)
- Fallback values provided when operations fail:
- In
useAssets(): returns placeholder image URL if asset fails to load - In
useSeo(): gracefully handles missing meta elements by creating them
- In
- Console warnings for non-critical failures:
console.warn('message')for warnings during execution- Error objects logged with context:
console.warn('Failed to load image: ${path}', error)
- Silent failures with fallbacks preferred over throwing errors for UI operations
Examples from codebase:
// In useAssets.ts - graceful fallback
if (!path || path.trim() === '') {
console.warn('getImageUrl called with empty or undefined path')
return `https://via.placeholder.com/400x300/f3f4f6/9ca3af?text=${encodeURIComponent('No image')}`
}
// In useSeo.ts - create if missing
let meta = document.querySelector(`meta[${property ? 'property' : 'name'}="${name}"]`)
if (!meta) {
meta = document.createElement('meta')
// ... setup ...
document.head.appendChild(meta)
}
Logging
Framework: console object (no dedicated logging library)
Patterns:
console.warn()for warnings (missing assets, invalid input)- Logging only in composables for utility functions
- No console.log() in production code (only development/debugging)
- Error context included:
console.warn('context', error)
Comments
When to Comment:
- JSDoc comments for composable functions (exported functions)
- Inline comments for non-obvious logic (especially SEO handling in router)
- Comments explaining why (not what the code does)
- TODO comments for known issues:
// TODO: page 404insrc/router/index.ts
JSDoc/TSDoc:
- Composables include JSDoc for exported functions
- Example from
useAssets.ts:/** * Get image URL from assets folder * @param path - Path like '@/assets/images/filename.webp' or 'filename.webp' * @returns string - The image URL */ const getImageUrl = (path: string | undefined): string => { ... } - Not consistently applied across all files; use when function signature isn't obvious
Function Design
Size: Composables are typically 50-100 lines; keep focused on single responsibility
Parameters:
- Props interfaces always named
Propsin components - Use destructuring in setup:
const { t } = useI18n() - Optional config objects in composables (e.g.,
SeoOptionswith defaults) - Explicit typing on all parameters
Return Values:
- Composables return object with all exposed functions and reactive state
- Always return computed versions of reactive state when exposing refs:
return { isOpen, // reactive ref currentImage, // computed from ref openGallery, // function closeGallery // function } - Functions return early on validation failures with fallbacks
Module Design
Exports:
- Composables export single named function:
export function useTheme() { ... } - Config files export named constants:
export const siteConfig: SiteConfig = { ... } - Type definitions export interfaces and types:
export interface Project { ... } - Data files export arrays or objects:
export const techStack: TechStack = { ... }
Barrel Files:
- Not extensively used; direct imports preferred
- Only
src/types/index.tsserves as barrel export for type definitions - Components use direct imports:
import AppHeader from '@/components/layout/AppHeader.vue'
Component Structure (Vue SFC):
<script setup lang="ts">for all components (Vue 3 Composition API)- Props validated with TypeScript interfaces
- Composables called at top of setup
- Computed properties for derived state
- Functions defined after setup calls
<template>uses semantic HTML and accessibility attributes- Scoped styles at bottom with
@importfor external stylesheets
Example pattern from AppHeader.vue:
<script setup lang="ts">
import { ref, computed } from 'vue'
// Composables first
const { getImageUrl } = useAssets()
const { t } = useI18n()
// State
const isMenuOpen = ref(false)
// Computed
const navigation = computed(() => [ ... ])
// Functions
const toggleMenu = () => { ... }
</script>
Type Safety
TypeScript Configuration:
- Version: ~5.8.0
- DOM-focused (
tsconfig.dom.jsonfrom @vue/tsconfig) - Path alias
@/*points to./src/* - Type checking enabled in build:
npm run type-checkrunsvue-tsc --build
Type Usage Patterns:
- All component Props use interface definitions
- Composable return values typed explicitly
- Function parameters and return types annotated
- Type imports use
import typesyntax - Avoid
anytype; use proper interfaces/generics
Vue 3 Specific
Composition API:
<script setup>syntax exclusively used- No Options API in codebase
- Composables follow Composition API patterns
Lifecycle Hooks:
onMounted()for initialization (theme loading, SEO setup)onUnmounted()for cleanup (removing DOM elements in useSeo)watch()for reactive side effects (theme changes)
Reactivity:
ref()for primitive statecomputed()for derived state- Avoid unnecessary reactivity; use constants when possible
- Return computed versions of refs from composables
Convention analysis: 2026-04-07