Code Style
Conventions and patterns used across the Rarebox codebase. Follow these when contributing to keep things consistent.
Vue Components
Single-File Components
All components use Vue 3 <script setup> syntax with the Composition API. No Options API.
<script setup>
import { ref, computed } from 'vue'
import { usePortfolioStore } from '@/stores/portfolio'
const store = usePortfolioStore()
const searchQuery = ref('')
const filteredItems = computed(() =>
store.currentPortfolio.items.filter(item =>
item.name.toLowerCase().includes(searchQuery.value.toLowerCase())
)
)
</script>
<template>
<!-- template here -->
</template>
<style scoped>
/* styles here */
</style>File Naming
- Components: PascalCase —
CardGrid.vue,AddItemModal.vue,BottomSheet.vue - Views: PascalCase with
Viewsuffix —SearchView.vue,SetsView.vue,SettingsView.vue - Stores: camelCase —
portfolio.js,deck.js - Composables: camelCase with
useprefix —useCardSearch.js,usePriceHistory.js
Component Organization
Within a .vue file, order sections as:
<script setup>— imports, props, emits, reactive state, computed, methods, lifecycle hooks<template>— markup<style scoped>— styles (always scoped unless intentionally global)
JavaScript
General
- ES modules (
import/export), no CommonJS constby default,letwhen reassignment is needed, nevervar- Arrow functions for callbacks and computed values
- Template literals over string concatenation
- Destructuring for object/array access
- Optional chaining (
?.) and nullish coalescing (??) where appropriate
Async
async/awaitover raw Promises and.then()chains- Always wrap external API calls in try/catch
- Use the existing retry-with-backoff pattern for API requests
Naming
- Variables/functions: camelCase —
fetchCardPrice,isStale,lastRefreshed - Constants: UPPER_SNAKE_CASE for true constants —
MAX_CONCURRENT_REQUESTS,SNAPSHOT_RETENTION_DAYS - Boolean variables: prefix with
is,has,should—isLoading,hasNeverPriced,shouldRetry
CSS
Scoped Styles
Always use <style scoped> unless you have a specific reason for global styles. This prevents style leakage between components.
Responsive Design
- Mobile-first: base styles target mobile, use
@mediaqueries to add desktop enhancements - Touch detection:
@media (hover: none)for touch-specific behaviors - Breakpoints used in the project:
/* Mobile → Tablet */
@media (max-width: 768px) { }
/* Touch devices (persistent overlays, bottom sheets) */
@media (hover: none) { }
/* iOS PWA safe areas */
@supports (padding-top: env(safe-area-inset-top)) { }The primary breakpoint is 768px — everything below is mobile, everything above is desktop. There's no explicit tablet breakpoint; the app uses a two-tier responsive system. Touch detection via (hover: none) is used alongside the width breakpoint for mobile-specific behaviors like bottom sheets and persistent overlay buttons.
Color and Theme
- Use CSS custom properties (variables) for colors and spacing
- Dark mode support via CSS variables — components shouldn't hard-code color values
Store Patterns
Mutations Must Persist
Every Pinia store action that changes persistent state must call this.persist() at the end. For critical operations, use this.persistNow() for an immediate flush.
// Good
addItem(portfolioId, item) {
const portfolio = this.portfolios.find(p => p.id === portfolioId)
portfolio.items.push(item)
this.persist() // debounced write to IDB
}
// Good — critical operation
resetAll() {
this.portfolios = [createDefaultPortfolio()]
this.snapshots = {}
this.persistNow() // immediate write
}API Calls in Stores
API fetching logic lives in store actions, not in components. Components call store actions and read reactive state — they don't make API calls directly.
Commit Messages
Use clear, descriptive commit messages. No strict format enforced, but prefer:
feat: add Japanese set price history charts
fix: prevent duplicate snapshots on rapid page reload
refactor: extract price fetching into composable
docs: add API integration documentationPrefixes: feat, fix, refactor, docs, style, perf, test, chore