A comprehensive event calendar application built with Vue 3, Nuxt 3/4, and ShadCN Vue components. This is a Vue/Nuxt implementation inspired by the React/Next.js ShadCN component found at https://github.com/origin-space/event-calendar.
- Month View: Traditional monthly calendar grid with event display
- Week View: 7-day view with time slots and drag-and-drop support
- Day View: Single day detailed view with hourly time slots
- Agenda View: List-based view of upcoming events
- Event Management: Create, edit, and delete events with a modal interface
- Enhanced Drag & Drop: Move events between time slots with visual feedback
- Drag events within WeekView and DayView with precise time slot targeting
- Visual drop zone highlighting during drag operations
- Support for both all-day and timed event conversions
- Cross-view dragging between different calendar views
- All-day & Timed Events: Support for both event types with seamless conversion
- Event Colors: Color-coded events with predefined color schemes
- Event Status Management: Visual feedback for different event states
- Past Events: Automatic detection with reduced opacity and time indicators
- Cancelled Events: Strikethrough text with reduced saturation and cancel icons
- Tentative Events: Dashed borders with question mark indicators
- Confirmed Events: Standard appearance (default state)
- Multi-day Event Support: Visual continuity for events spanning multiple days
- Connected visual styling across day boundaries
- Rounded borders that flow from start to end day
- Information display only on event start day
- Visual connection indicators for continuation
- Location Support: Add and display event locations with tooltips and icons
- Compact location display for small event cards
- Hover tooltips showing full location details
- Location icons with customizable sizing
- Loading States: Visual feedback during async operations with loading indicators
- Error Handling: Comprehensive error handling with toast notifications and validation
Quick view switching with keyboard shortcuts:
M
- Switch to Month viewW
- Switch to Week viewD
- Switch to Day viewA
- Switch to Agenda view
Note: Keyboard shortcuts are disabled when typing in input fields or when the event modal is open.
- Mobile-friendly interface with adaptive layouts
- Touch-friendly interactions for mobile devices
- Responsive event cards and time slot sizing
- Dark/Light Mode: Seamless theme switching with smooth transitions
- Auto-detects system preference
- Manual toggle with animated sun/moon icon
- Persistent theme selection via localStorage
- Smooth color transitions across all components
Advanced recurring event support with intelligent validation:
- Daily Recurrence: Events that repeat every N days
- Weekly Recurrence: Events that repeat on specific days of the week
- Auto-selects start date's day of week when switching to weekly
- Multiple days per week selection with toggle interface
- Monthly Recurrence: Events that repeat on specific day of month
- Auto-selects start date's day when switching to monthly
- Handles month-end edge cases (e.g., January 31st)
- Yearly Recurrence: Events that repeat annually on specific date
- Auto-selects start date's month and day when switching to yearly
- Flexible End Conditions:
- Never: Events continue indefinitely with
endsNever
flag - After X occurrences: Limited repetitions with count validation
- On specific date: End on a particular date with date validation
- Never: Events continue indefinitely with
- Smart Form Behavior: Automatic field population based on event start date
- Comprehensive Validation: Prevents invalid recurring patterns with helpful error messages
Enterprise-grade security implementation:
- Input Sanitization: XSS protection with HTML sanitization
- Content Security Policy: Strict CSP headers to prevent injection attacks
- Rate Limiting: API endpoint protection with configurable limits
- Validation Framework: Multi-layer validation with error recovery
- Security Headers: HSTS, X-Frame-Options, X-Content-Type-Options
- Secret Management: Secure handling of API keys and tokens
- Error Boundaries: Graceful error handling without information leakage
Built-in observability and performance tracking:
- Performance Monitoring: Real-time metrics collection and reporting
- Error Tracking: Comprehensive error logging with context preservation
- User Analytics: Privacy-focused usage analytics
- Health Checks: Application health monitoring with detailed status
- Resilient Error Handling: Circuit breaker patterns and error recovery
- Memory Management: Performance cache with LRU eviction
- Debug Logging: Detailed logging for development and troubleshooting
Global-ready timezone and locale support:
- Automatic Timezone Detection: Browser-based timezone detection
- Timezone Conversion: Event display in user's local timezone
- DST Handling: Daylight saving time transition support
- Multiple Timezone Support: Display events from different timezones
- Timezone Validation: Comprehensive timezone data validation
- Recurring Event Timezone Consistency: Proper timezone handling for recurring events
Mobile-first design with accessibility compliance:
- Touch Gestures: Enhanced touch interactions for mobile devices
- Mobile Drag & Drop: Touch-optimized event manipulation
- Screen Reader Support: Full ARIA compliance for accessibility
- Keyboard Navigation: Complete keyboard-only operation support
- Responsive Breakpoints: Adaptive layouts for all screen sizes
- High Contrast Mode: Support for accessibility color preferences
Seamless integration with popular calendar services:
- Google Calendar: Import and sync with Google Calendar
- Microsoft Outlook: Exchange and Outlook.com integration
- Apple Calendar: iCal format support
- CalDAV Protocol: Standard calendar protocol support
- Conflict Resolution: Smart handling of sync conflicts
- Selective Sync: Choose which calendars to sync
Advanced performance features for large-scale deployments:
- Event Virtualization: Efficient rendering of large event datasets
- Smart Caching: LRU cache with configurable TTL
- Service Worker: Offline support and background sync
- Code Splitting: Lazy loading of calendar views and features
- Memory Management: Automatic cleanup and garbage collection
- Bundle Optimization: Tree-shaking and chunk optimization
- Equal Grid Sizing: Month view uses consistent cell dimensions for uniform layout
- Header Alignment: Week view headers perfectly align with time grid columns
- Multi-day Visual Connection: Events spanning multiple days show visual continuity
- Status Indicators: Clear visual feedback for event states with icons and styling
- Improved Spacing: Better component spacing and padding throughout the interface
- Nuxt 3/4 - Vue.js meta-framework with SSR/SSG capabilities
- Vue 3 - Composition API with TypeScript support
- TypeScript - Strict typing throughout the application
- ShadCN Vue - Component library (New York style)
- Tailwind CSS v4 - Utility-first CSS framework
- Reka UI - Headless UI components for Vue
- Iconify - Icon library with Lucide icons
- @vueuse/core - Vue composition utilities (including keyboard shortcuts and dark mode)
- date-fns - Date manipulation and formatting
- @vue-dnd-kit/core - Modern Vue 3 drag and drop functionality
- vue-sonner - Toast notifications
- @tanstack/vue-form - Form management with validation
- uuid - Unique identifier generation for events
Make sure to install dependencies using Bun (recommended):
bun install
Start the development server on http://localhost:3000
:
bun run dev
Build the application for production:
bun run build
Locally preview production build:
bun run preview
Generate static site:
bun run generate
Run tests with Vitest:
bun run test
Run tests with UI:
bun run test:ui
Run tests once:
bun run test:run
Run tests with coverage:
bun run test:coverage
components/
├── event-calendar/ # Core calendar components
│ ├── EventCalendar.vue # Main calendar container
│ ├── MonthView.vue # Monthly calendar view with equal grid sizing
│ ├── WeekView.vue # Weekly calendar view with aligned headers
│ ├── DayView.vue # Daily calendar view with drag-and-drop
│ ├── AgendaView.vue # Agenda list view with month navigation
│ ├── EventModal.vue # Event creation/editing modal with recurring support
│ ├── LocationDisplay.vue # Location display component with tooltips
│ ├── ColorManager.vue # Color management and conflict detection
│ ├── ExternalCalendarSettings.vue # External calendar integration settings
│ ├── DayEventsOverflowPopup.vue # Event overflow handling
│ ├── DragDropVisualFeedback.vue # Visual feedback component
│ ├── EventResizeHandle.vue # Event resizing handles
│ ├── types.ts # TypeScript interfaces and types
│ ├── utils.ts # Calendar utility functions
│ ├── constants.ts # Calendar constants
│ ├── __tests__/ # Test files
│ │ ├── EventCalendar.test.ts # Main component tests
│ │ ├── useColorManager.test.ts # Color manager tests
│ │ └── useEventStatus.test.ts # Event status tests
│ └── composables/ # Vue composables
│ ├── useCalendarUtils.ts # Calendar utility functions
│ ├── useColorManager.ts # Color management and conflict detection
│ ├── useDragAndDrop.ts # Enhanced drag-and-drop system
│ ├── useErrorHandling.ts # Error handling and validation
│ ├── useEventFiltering.ts # Event filtering and search
│ ├── useEventStatus.ts # Event status management
│ ├── useEventResize.ts # Event resizing functionality
│ ├── useMultiDayLayout.ts # Multi-day event layout
│ ├── useRecurringEvents.ts # Recurring event processing
│ ├── useExternalCalendar.ts # External calendar integration
│ ├── useTimezone.ts # Timezone handling and conversion
│ ├── useVirtualization.ts # Event virtualization for performance
│ ├── useCompatibility.ts # Browser compatibility checks
│ ├── useMobileEnhancement.ts # Mobile-specific optimizations
│ ├── useSecurity.ts # Security validation and sanitization
│ ├── useResilientErrorHandling.ts # Advanced error recovery
│ ├── useMonitoring.ts # Performance monitoring and analytics
│ ├── useEventAPI.ts # Event API management
│ ├── useEventStore.ts # Event state management
│ ├── usePerformanceCache.ts # Caching and performance optimization
│ ├── useAdvancedValidation.ts # Comprehensive validation framework
│ └── useKeyboardNavigation.ts # Keyboard navigation composable
└── ui/ # ShadCN UI components
├── DarkModeToggle.vue # Dark/Light mode toggle component
└── [other ShadCN components] # Button, Dialog, Input, etc.
server/
└── api/ # Server API endpoints
├── health.get.ts # Health check endpoint
├── analytics.post.ts # Analytics data collection
└── errors.post.ts # Error reporting endpoint
composables/
├── useDarkMode.ts # Global dark mode management
└── useServiceWorker.ts # Service worker management
This Event Calendar can be used as a ShadCN-style component/block in your Vue/Nuxt applications. It follows the ShadCN design principles with proper TypeScript types, accessible markup, and Tailwind CSS styling.
- Copy the component files from
components/event-calendar/
to your project - Install required dependencies:
bun install @vueuse/core date-fns @date-fns/tz @vue-dnd-kit/core @tanstack/vue-form uuid vue-sonner
- Ensure you have the required ShadCN components installed:
bunx --bun shadcn-vue@latest add button dialog input label textarea select checkbox radio-group popover calendar
- Add to your Nuxt configuration (nuxt.config.ts):
export default defineNuxtConfig({ modules: ["@vueuse/nuxt", "@nuxtjs/tailwindcss", "shadcn-nuxt"], shadcn: { prefix: "", componentDir: "./components/ui", }, })
components/event-calendar/
├── EventCalendar.vue # Main component - drop this into any page
├── types.ts # TypeScript interfaces
├── composables/ # Reusable Vue composables
│ ├── useCalendarUtils.ts # Calendar utilities
│ ├── useColorManager.ts # Event color management
│ ├── useDragAndDrop.ts # Enhanced drag-and-drop
│ ├── useErrorHandling.ts # Error handling system
│ ├── useEventFiltering.ts # Event filtering logic
│ ├── useEventStatus.ts # Event status management
│ ├── useExternalCalendar.ts # External calendar integration
│ ├── useKeyboardNavigation.ts # Keyboard shortcuts
│ ├── useMultiDayLayout.ts # Multi-day event layout
│ ├── useRecurringEvents.ts # Recurring event support
│ └── useTimezone.ts # Timezone handling
└── [view-components].vue # Month, Week, Day, Agenda views
<template>
<div class="p-6">
<EventCalendar
:events="events"
:initial-view="'month'"
@event-add="handleEventAdd"
@event-update="handleEventUpdate"
@event-delete="handleEventDelete"
/>
</div>
</template>
<script setup lang="ts">
import { ref } from "vue"
import type { CalendarEvent } from "@/components/event-calendar/types"
import { toast } from "vue-sonner"
// Event management
const events = ref<CalendarEvent[]>([
{
id: "1",
title: "Team Meeting",
description: "Weekly sync with the team",
startDate: new Date(2024, 0, 15, 10, 0),
endDate: new Date(2024, 0, 15, 11, 0),
allDay: false,
color: "sky",
location: "Conference Room A",
status: "confirmed",
},
])
const handleEventAdd = (event: CalendarEvent) => {
events.value.push(event)
toast.success(`Event "${event.title}" created`)
}
const handleEventUpdate = (event: CalendarEvent) => {
const index = events.value.findIndex(e => e.id === event.id)
if (index !== -1) {
events.value[index] = event
toast.success(`Event "${event.title}" updated`)
}
}
const handleEventDelete = (eventId: string) => {
events.value = events.value.filter(e => e.id !== eventId)
toast.success("Event deleted")
}
</script>
<template>
<EventCalendar
:events="events"
:initial-view="view"
@event-add="handleEventAdd"
@event-update="handleEventUpdate"
@event-delete="handleEventDelete"
class="h-screen"
/>
</template>
<script setup lang="ts">
import { useCompatibility } from "@/components/ui/event-calendar/composables/useCompatibility"
// import { useCompatibility } from "@/blocks/event-calendar/composables/useCompatibility"
const { ensureUUID } = useCompatibility()
// Full configuration with advanced features
const events = ref<CalendarEvent[]>([
{
id: ensureUUID(),
title: "Multi-day Conference",
description: "Annual tech conference",
startDate: new Date(2024, 0, 15, 9, 0),
endDate: new Date(2024, 0, 17, 17, 0),
allDay: false,
color: "violet",
location: "Convention Center",
status: "confirmed",
timezone: "America/New_York",
isRecurring: false,
},
])
</script>
Available colors: sky
, amber
, violet
, rose
, emerald
, orange
confirmed
- Default appearancetentative
- Dashed border with question markcancelled
- Strikethrough with reduced opacitypast
- Auto-detected, reduced opacity
month
- Traditional calendar gridweek
- 7-day view with time slotsday
- Single day detailed viewagenda
- List view of events
M
- Switch to Month viewW
- Switch to Week viewD
- Switch to Day viewA
- Switch to Agenda view
The component uses Tailwind CSS classes and CSS custom properties. Override styles by:
<template>
<EventCalendar :events="events" class="custom-calendar" />
</template>
<style scoped>
.custom-calendar {
/* Override default styles */
--calendar-border-color: theme("colors.gray.300");
--event-border-radius: theme("borderRadius.lg");
}
/* Custom event styling */
.custom-calendar :deep(.event-content) {
@apply shadow-lg hover:shadow-xl;
}
</style>
// api/events.ts
export interface EventAPI {
fetchEvents: (start: Date, end: Date) => Promise<CalendarEvent[]>
createEvent: (event: Omit<CalendarEvent, "id">) => Promise<CalendarEvent>
updateEvent: (id: string, event: Partial<CalendarEvent>) => Promise<CalendarEvent>
deleteEvent: (id: string) => Promise<void>
}
// composables/useEventAPI.ts
export const useEventAPI = () => {
const events = ref<CalendarEvent[]>([])
const isLoading = ref(false)
const loadEvents = async (start: Date, end: Date) => {
isLoading.value = true
try {
events.value = await $fetch("/api/events", {
query: { start: start.toISOString(), end: end.toISOString() },
})
} finally {
isLoading.value = false
}
}
return { events: readonly(events), isLoading: readonly(isLoading), loadEvents }
}
GET /api/health
Response:
{
"status": "healthy" | "unhealthy",
"uptime": number, // seconds
"timestamp": string,
"checks": {
"server": "healthy" | "unhealthy",
"memory": "healthy" | "unhealthy"
},
"version": string
}
POST /api/analytics
Body:
{
"session": string,
"timestamp": string,
"metricsCount": number,
"errorsCount": number,
"summary": {
"totalMetrics": number,
"totalErrors": number,
"averagePageLoadTime": number,
"errorRate": number
}
}
POST /api/errors
Body:
{
"session": string,
"timestamp": string,
"error": {
"message": string,
"stack": string,
"type": string,
"context": Record<string, any>
}
}
- Uses
shallowRef
for large event collections - Memoized computed properties for expensive operations
- Virtual scrolling for large time ranges (agenda view)
- Optimized drag-and-drop with throttled updates
- SSR-compatible with client-side hydration
<script setup lang="ts">
import { ref, onMounted } from "vue"
import type { CalendarEvent } from "@/components/event-calendar/types"
import { toast } from "vue-sonner"
const events = ref<CalendarEvent[]>([])
const handleEventAdd = async (newEvent: CalendarEvent) => {
events.value.push(newEvent)
toast.success(`Event "${newEvent.title}" created successfully`)
}
const handleEventUpdate = async (updatedEvent: CalendarEvent) => {
const index = events.value.findIndex(event => event.id === updatedEvent.id)
if (index !== -1) {
events.value[index] = updatedEvent
toast.success(`Event "${updatedEvent.title}" updated successfully`)
}
}
const handleEventDelete = async (eventId: string) => {
const eventIndex = events.value.findIndex(event => event.id === eventId)
if (eventIndex !== -1) {
const deletedEvent = events.value[eventIndex]
events.value.splice(eventIndex, 1)
toast.success(`Event "${deletedEvent.title}" deleted successfully`)
}
}
onMounted(() => {
events.value = [
{
id: "1",
title: "Team Meeting",
description: "Weekly team sync",
startDate: new Date(),
endDate: new Date(),
allDay: false,
color: "sky",
location: "Conference Room A",
},
]
})
</script>
<template>
<div>
<EventCalendar
:events="events"
@event-add="handleEventAdd"
@event-update="handleEventUpdate"
@event-delete="handleEventDelete"
/>
</div>
</template>
interface CalendarEvent {
id: string
title: string
description?: string
startDate: Date
endDate: Date
startTime?: string
endTime?: string
allDay?: boolean
color?: EventColor
location?: string
status?: EventStatus
isRecurring?: boolean
recurringPattern?: RecurringPattern
recurringId?: string
}
type EventColor = "sky" | "amber" | "violet" | "rose" | "emerald" | "orange"
type EventStatus = "confirmed" | "cancelled" | "tentative" | "past"
type ViewMode = "month" | "week" | "day" | "agenda"
interface RecurringPattern {
type: "daily" | "weekly" | "monthly" | "yearly"
interval: number // Every N days/weeks/months/years
endDate?: Date
count?: number // Number of occurrences
endsNever?: boolean // For events that never end
daysOfWeek?: number[] // For weekly: 0=Sunday, 1=Monday, etc.
dayOfMonth?: number // For monthly: 1-31
monthOfYear?: number // For yearly: 1-12
}
// Location Display Props
interface LocationDisplayProps {
location?: string
showIcon?: boolean
showTooltip?: boolean
compact?: boolean
iconSize?: number
}
interface EventCalendarProps {
events: CalendarEvent[]
initialView?: ViewMode
}
// Event handlers
@eventAdd(event: CalendarEvent): void
@eventUpdate(event: CalendarEvent): void
@eventDelete(eventId: string): void
The calendar includes advanced drag-and-drop functionality powered by @vue-dnd-kit/core
:
- Global Drag State: Unified drag-and-drop system that works across all calendar views
- Cross-View Dragging: Move events between different calendar views (Month, Week, Day)
- Smart Event Conversion: Automatically converts between all-day and timed events based on drop target
- Precise Time Targeting: Drop events on specific time slots with pixel-perfect positioning
- Visual Feedback: Drop zones are highlighted during drag operations with color-coded indicators
- Duration Preservation: Event duration is maintained when moving between time slots
The enhanced system provides flexible configuration for different drop zones:
interface DragDropConfig {
enabled: boolean
crossViewEnabled: boolean
visualFeedback: boolean
snapToGrid: boolean
allowTimeChange: boolean
allowDateChange: boolean
}
interface DropZoneInfo {
id: string
type: "time-slot" | "all-day" | "day-cell"
date: Date
time?: string
isValid: boolean
}
interface DragState {
isDragging: boolean
draggedEvent?: CalendarEvent
validDropZones: DropZoneInfo[]
currentDropZone?: DropZoneInfo
dragPreview?: { x: number; y: number; event: CalendarEvent }
}
The useDragAndDropSystem
composable provides:
const {
globalDragState, // Global drag state management
createDragConfig, // Create drag configuration for zones
registerDropZone, // Register valid drop zones
unregisterDropZone, // Unregister drop zones
isValidDropZone, // Check if zone accepts drops
getCurrentDropZone, // Get current drop target
createDraggableEvent, // Create draggable event handlers
createDroppableZone, // Create droppable zone handlers
} = useDragAndDropSystem()
- All-day to Timed: Dragging from all-day zones to time slots converts events to timed events
- Timed to All-day: Dragging from time slots to all-day zones converts events to all-day events
- Date Changes: Moving events between different days preserves time but updates the date
- Time Precision: Mouse position determines exact drop time within time slots
The calendar includes comprehensive error handling and loading state management:
The useErrorHandling
composable provides centralized error management:
interface CalendarError {
id: string
type: "validation" | "network" | "drag-drop" | "general"
message: string
timestamp: Date
context?: Record<string, any>
}
const {
errors, // Reactive array of errors
isLoading, // Global loading state
handleError, // Handle and display errors
clearError, // Clear specific error
clearAllErrors, // Clear all errors
withErrorHandling, // Wrap async operations
validateEvent, // Validate event data
} = useErrorHandling()
- Global Loading State: Unified loading indicator across all operations
- Async Operation Feedback: Visual feedback during event creation, updates, and deletions
- Simulated Network Delays: Realistic UX with simulated async operations
- Loading Indicators: Toast notifications and UI feedback during operations
Comprehensive validation for event data:
- Required Fields: Title validation
- Date Validation: Start/end date consistency checks
- Time Logic: End time must be after start time for timed events
- Real-time Feedback: Immediate validation feedback in forms
- Graceful Degradation: Operations fail gracefully without breaking the UI
- User Feedback: Clear error messages via toast notifications
- Development Logging: Detailed error logging in development mode
- Context Preservation: Error context for debugging and recovery
The LocationDisplay
component provides flexible location rendering:
<LocationDisplay :location="event.location" :compact="true" :show-tooltip="true" :icon-size="12" />
Props:
location
- Location text to displayshowIcon
- Show/hide map pin icon (default: true)showTooltip
- Enable hover tooltip with full location (default: true)compact
- Compact mode for small spaces (default: false)iconSize
- Icon size in pixels (default: 12)
The project uses several configuration files:
nuxt.config.ts
- Nuxt configuration with modules and pluginscomponents.json
- ShadCN component configurationtailwind.config.js
- Tailwind CSS configurationtsconfig.json
- TypeScript configuration
The calendar includes comprehensive event status management with visual feedback:
- Confirmed (default): Standard event appearance
- Tentative: Events with uncertain confirmation status
- Cancelled: Events that have been cancelled but remain visible
- Past: Events that have already ended (auto-detected)
Each status type provides distinct visual indicators:
// Status styling examples
confirmed: "" // No additional styling (default)
tentative: "opacity-80 border-dashed" // Dashed border with reduced opacity
cancelled: "opacity-50 line-through saturate-50" // Strikethrough with reduced saturation
past: "opacity-70 saturate-75" // Reduced opacity and saturation
- Cancelled: ✕ (cross mark)
- Past: ⏰ (clock icon)
- Tentative: ? (question mark)
- Confirmed: No indicator
The system automatically detects past events:
- Timed Events: Marked as past when end time has passed
- All-day Events: Marked as past after the day ends
- Real-time Updates: Status updates automatically as time progresses
The useEventStatus
composable provides:
const {
getEventStatus, // Get current event status
getEventStatusClasses, // Get CSS classes for status styling
getEventStatusIndicator, // Get status indicator icon
getEventStatusTooltip, // Get status tooltip text
isEventInPast, // Check if event is past
isEventCancelled, // Check if event is cancelled
processEventsWithStatus, // Process events with computed status
} = useEventStatus(events)
Enhanced visual continuity for events spanning multiple days:
- Connected Borders: Rounded borders that flow from start to end day
- Visual Indicators: Subtle connection lines for continuation
- Information Display: Event details shown only on start day
- Smart Rendering: Optimized rendering for long-duration events
Events automatically adapt their appearance based on position:
// Border radius classes based on event position
startDay: "rounded-l rounded-r-none" // Left rounded only
middleDay: "rounded-none" // No rounding
endDay: "rounded-r rounded-l-none" // Right rounded only
singleDay: "rounded" // Full rounding
The system provides utilities for multi-day event handling:
const {
isMultiDayEvent, // Check if event spans multiple days
isEventStart, // Check if current day is event start
isEventEnd, // Check if current day is event end
getMultiDayEventClasses, // Get appropriate CSS classes
} = useMultiDayEvents()
Comprehensive dark mode implementation with smooth transitions:
- Auto-detection: Respects system preference on first visit
- Manual Toggle: Animated sun/moon icon toggle button
- Persistence: Theme choice saved to localStorage
- Smooth Transitions: All components transition smoothly between themes
Uses VueUse's useDark
composable:
export const useDarkMode = () => {
const isDark = useDark({
selector: "html",
attribute: "class",
valueDark: "dark",
valueLight: "light",
storageKey: "event-calendar-theme",
})
const toggle = useToggle(isDark)
return { isDark: readonly(isDark), toggle }
}
Smooth color transitions applied globally:
* {
transition: colors 300ms ease-in-out;
}
- Bun (recommended) or Node.js 18+
- TypeScript knowledge for customization
- Basic understanding of Vue 3 Composition API
- Familiarity with Nuxt 3/4 for advanced features
# Core development
bun run dev # Start development server
bun run build # Build for production
bun run preview # Preview production build
bun run generate # Generate static site
# Code quality
bun run lint # Run ESLint
bun run lint:fix # Fix ESLint issues automatically
bun run format # Format code with Prettier
bun run typecheck # Run TypeScript type checking
# Testing
bun run test # Run unit tests
bun run test:ui # Run tests with UI
bun run test:coverage # Run tests with coverage
bun run test:e2e # Run end-to-end tests
bun run test:e2e:ui # Run E2E tests with UI
# Performance & Security
bun run performance:audit # Run Lighthouse audit
bun run security:audit # Run security audit
bun run security:fix # Fix security vulnerabilities
# Docker operations
bun run docker:build # Build Docker image
bun run docker:run # Start with Docker Compose
bun run docker:stop # Stop Docker services
bun run docker:logs # View Docker logs
Key configuration files and their purposes:
nuxt.config.ts
: Main Nuxt configuration with modules, optimization, and security settingscomponents.json
: ShadCN component configuration and styling preferencestsconfig.json
: TypeScript configuration with relaxed strictness for easier developmentvitest.config.ts
: Unit testing configurationplaywright.config.ts
: End-to-end testing configurationeslint.config.mjs
: ESLint configuration with security and accessibility rules
-
TypeScript Errors
# The project uses relaxed TypeScript settings for easier development # If you encounter type errors, check tsconfig.json configuration bun run typecheck
-
Recurring Events Validation
# Weekly events require at least one day selected # Monthly events require a valid day of month (1-31) # Yearly events require a valid month (1-12) and day
-
SSR Hydration Issues
# Some composables use client-side only features # Check for typeof window !== 'undefined' checks
-
Performance Issues
# Enable performance monitoring in development # Check browser devtools for performance insights bun run performance:audit
-
Memory Issues
# Increase Node.js memory limit if needed NODE_OPTIONS="--max-old-space-size=4096" bun run build
-
Dependency Conflicts
# Clear node_modules and reinstall rm -rf node_modules bun.lock bun install
-
Fork and Clone
git clone https://github.com/AnoRebel/event-calendar.git cd event-calendar bun install
-
Create Feature Branch
git checkout -b feature/your-feature-name
-
Development Workflow
# Start development server bun run dev # Make your changes # Run tests bun run test # Check code quality bun run lint bun run typecheck
-
Testing Requirements
- Add unit tests for new composables
- Add component tests for UI changes
- Update E2E tests for user-facing features
- Ensure all tests pass before submitting
-
Code Standards
- Follow existing code style and patterns
- Use TypeScript for type safety
- Add JSDoc comments for public APIs
- Follow Vue 3 Composition API best practices
-
Security Considerations
- Sanitize user inputs
- Validate all data at boundaries
- Follow security best practices
- Run security audit before submitting
-
Submit Pull Request
- Write clear commit messages
- Include tests for new features
- Update documentation as needed
- Reference related issues
This project is open source and available under the MIT License.
Check out the Nuxt deployment documentation for more information on deployment options.