Initial commit: IndeeHub decentralized streaming platform
Built a complete Netflix-style streaming interface for IndeeHub's decentralized media platform with real film content. Features: - Vue 3 + TypeScript + Vite setup with hot module reloading - Netflix-inspired UI with hero section and horizontal scrolling content rows - Glass morphism design system with custom Tailwind configuration - 20+ real IndeeHub films organized into 6 categories (Bitcoin, Documentaries, Drama, etc.) - Full-featured video player component with custom controls - Mobile-responsive design with bottom navigation - Nostr integration ready (nostr-tools, relay pool, NIP-71 support) - Pinia state management for content - MCP tools configured (Filesystem, Memory, Nostr, Puppeteer) Components: - Browse.vue: Main streaming interface with hero and content rows - ContentRow.vue: Horizontal scrolling film cards with navigation arrows - VideoPlayer.vue: Custom video player with play/pause, seek, volume, fullscreen - MobileNav.vue: Bottom tab navigation for mobile devices Tech Stack: - Frontend: Vue 3 (Composition API), TypeScript - Build: Vite 7 - Styling: Tailwind CSS with custom theme - State: Pinia 3 - Router: Vue Router 4.6 - Protocol: Nostr (nostr-tools 2.22) Design: - 4px grid spacing system - Glass morphism UI components - Netflix-style hero section with featured content - Smooth animations and hover effects - Mobile-first responsive breakpoints - Dark theme with custom color palette Content: - 20+ IndeeHub films with titles, descriptions, categories - Bitcoin documentaries: God Bless Bitcoin, Dirty Coin, Searching for Satoshi - Independent films and documentaries - Working Unsplash CDN images for thumbnails and backdrops Ready for deployment to Umbrel, Start9, and Archy nodes. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
37
.cursor/mcp.json
Normal file
37
.cursor/mcp.json
Normal file
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"mcpServers": {
|
||||
"filesystem": {
|
||||
"command": "npx",
|
||||
"args": [
|
||||
"-y",
|
||||
"@modelcontextprotocol/server-filesystem@2026.1.14",
|
||||
"/Users/dorian/Projects"
|
||||
]
|
||||
},
|
||||
"memory": {
|
||||
"command": "npx",
|
||||
"args": [
|
||||
"-y",
|
||||
"@modelcontextprotocol/server-memory@2026.1.26"
|
||||
]
|
||||
},
|
||||
"nostr": {
|
||||
"command": "npx",
|
||||
"args": [
|
||||
"-y",
|
||||
"nostr-mcp-server@2.1.0"
|
||||
],
|
||||
"env": {
|
||||
"NOSTR_NSEC_KEY": "nsec1tzud8sr2m4nl49762yuqzgmanf93e23e4d0d3euraaqc2t677unqawmzec",
|
||||
"NOSTR_RELAYS": "wss://relay.damus.io,wss://nos.lol,wss://relay.nostr.band"
|
||||
}
|
||||
},
|
||||
"puppeteer": {
|
||||
"command": "npx",
|
||||
"args": [
|
||||
"-y",
|
||||
"@modelcontextprotocol/server-puppeteer@2025.5.12"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
281
.cursor/rules/accessibility.mdc
Normal file
281
.cursor/rules/accessibility.mdc
Normal file
@@ -0,0 +1,281 @@
|
||||
---
|
||||
description: WCAG AA accessibility standards and patterns
|
||||
alwaysApply: false
|
||||
globs: **/*.{vue,html,jsx,tsx}
|
||||
---
|
||||
|
||||
# Accessibility Standards (A11y)
|
||||
|
||||
## Philosophy
|
||||
|
||||
Accessible design is not optional. It's a fundamental requirement for building inclusive, usable applications.
|
||||
|
||||
**Target: WCAG AA Compliance**
|
||||
|
||||
## Color & Contrast
|
||||
|
||||
### Contrast Ratios (WCAG AA)
|
||||
- **Normal text**: 4.5:1 minimum
|
||||
- **Large text** (18pt+): 3:1 minimum
|
||||
- **UI components**: 3:1 minimum
|
||||
|
||||
```html
|
||||
<!-- ✅ Good - High contrast -->
|
||||
<div class="bg-white text-gray-900">
|
||||
Readable text with 21:1 ratio
|
||||
</div>
|
||||
|
||||
<!-- ❌ Bad - Low contrast -->
|
||||
<div class="bg-gray-300 text-gray-400">
|
||||
Hard to read - only 1.5:1 ratio
|
||||
</div>
|
||||
```
|
||||
|
||||
### Don't Rely on Color Alone
|
||||
|
||||
```html
|
||||
<!-- ✅ Good - Icon + color + text -->
|
||||
<span class="text-red-500 flex items-center gap-2">
|
||||
<svg class="w-4 h-4"><!-- Error icon --></svg>
|
||||
Error: Invalid input
|
||||
</span>
|
||||
```
|
||||
|
||||
## Semantic HTML
|
||||
|
||||
### Use the Right Elements
|
||||
|
||||
```html
|
||||
<!-- ✅ Good - Semantic -->
|
||||
<header>
|
||||
<nav>
|
||||
<ul>
|
||||
<li><a href="/">Home</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
<article>
|
||||
<h1>Article Title</h1>
|
||||
<p>Content...</p>
|
||||
</article>
|
||||
</main>
|
||||
|
||||
<!-- ❌ Bad - Divitis -->
|
||||
<div class="header">
|
||||
<div class="nav">...</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
### Heading Hierarchy
|
||||
|
||||
```html
|
||||
<!-- ✅ Good - Proper hierarchy -->
|
||||
<h1>Page Title</h1>
|
||||
<h2>Section Title</h2>
|
||||
<h3>Subsection Title</h3>
|
||||
```
|
||||
|
||||
## Keyboard Navigation
|
||||
|
||||
### Focus Indicators
|
||||
|
||||
**Always show visible focus states.**
|
||||
|
||||
```html
|
||||
<button class="focus:outline-none focus:ring-2 focus:ring-primary focus:ring-offset-2">
|
||||
Click Me
|
||||
</button>
|
||||
|
||||
<input class="focus:border-primary focus:ring-2 focus:ring-primary-focus" />
|
||||
```
|
||||
|
||||
### Skip Links
|
||||
|
||||
```html
|
||||
<a href="#main-content" class="sr-only focus:not-sr-only focus:absolute focus:top-4 focus:z-50">
|
||||
Skip to main content
|
||||
</a>
|
||||
|
||||
<main id="main-content">
|
||||
<!-- Main content -->
|
||||
</main>
|
||||
```
|
||||
|
||||
### Test Keyboard Navigation
|
||||
- Tab through entire page
|
||||
- Shift+Tab to go backwards
|
||||
- Enter/Space to activate buttons
|
||||
- Escape to close modals/dropdowns
|
||||
|
||||
## ARIA (Use Sparingly)
|
||||
|
||||
**First rule of ARIA: Don't use ARIA if you can use semantic HTML instead.**
|
||||
|
||||
### ARIA Labels
|
||||
|
||||
```html
|
||||
<!-- Button with only icon -->
|
||||
<button aria-label="Close modal">
|
||||
<svg><!-- X icon --></svg>
|
||||
</button>
|
||||
|
||||
<!-- Decorative image -->
|
||||
<img src="decoration.svg" alt="" aria-hidden="true" />
|
||||
```
|
||||
|
||||
### ARIA Live Regions
|
||||
|
||||
```html
|
||||
<!-- Announce status updates -->
|
||||
<div
|
||||
role="status"
|
||||
aria-live="polite"
|
||||
aria-atomic="true"
|
||||
class="sr-only"
|
||||
>
|
||||
{{ statusMessage }}
|
||||
</div>
|
||||
```
|
||||
|
||||
## Forms Accessibility
|
||||
|
||||
### Label Every Input
|
||||
|
||||
```html
|
||||
<!-- ✅ Good - Explicit label -->
|
||||
<label for="email">Email Address</label>
|
||||
<input id="email" type="email" name="email" />
|
||||
|
||||
<!-- ❌ Bad - No label -->
|
||||
<input type="email" placeholder="Email" />
|
||||
```
|
||||
|
||||
### Required Fields
|
||||
|
||||
```html
|
||||
<label for="email">
|
||||
Email <span class="text-red-500" aria-label="required">*</span>
|
||||
</label>
|
||||
<input
|
||||
id="email"
|
||||
type="email"
|
||||
required
|
||||
aria-required="true"
|
||||
/>
|
||||
```
|
||||
|
||||
### Error Messages
|
||||
|
||||
```html
|
||||
<input
|
||||
id="email"
|
||||
:aria-invalid="hasError"
|
||||
:aria-describedby="hasError ? 'email-error' : undefined"
|
||||
/>
|
||||
|
||||
<div
|
||||
v-if="hasError"
|
||||
id="email-error"
|
||||
role="alert"
|
||||
class="text-red-500"
|
||||
>
|
||||
Please enter a valid email
|
||||
</div>
|
||||
```
|
||||
|
||||
## Touch Targets & Mobile
|
||||
|
||||
### Minimum Touch Target Size
|
||||
|
||||
**WCAG Guideline: 44x44px minimum**
|
||||
|
||||
```html
|
||||
<!-- ✅ Good - Large enough -->
|
||||
<button class="p-4">
|
||||
<svg class="w-6 h-6"><!-- Icon --></svg>
|
||||
</button>
|
||||
|
||||
<!-- ❌ Bad - Too small -->
|
||||
<button class="p-1">
|
||||
<svg class="w-3 h-3"><!-- Icon --></svg>
|
||||
</button>
|
||||
```
|
||||
|
||||
### Spacing Between Targets
|
||||
|
||||
```html
|
||||
<!-- ✅ Good - Adequate spacing -->
|
||||
<nav class="flex flex-col gap-6 md:gap-2">
|
||||
<a href="/home" class="py-3 md:py-2">Home</a>
|
||||
</nav>
|
||||
```
|
||||
|
||||
## Motion & Animation
|
||||
|
||||
### Respect Prefers-Reduced-Motion
|
||||
|
||||
```css
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
* {
|
||||
animation-duration: 0.01ms !important;
|
||||
transition-duration: 0.01ms !important;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Screen Reader Support
|
||||
|
||||
### Alt Text for Images
|
||||
|
||||
```html
|
||||
<!-- ✅ Informative image -->
|
||||
<img src="chart.png" alt="Bar chart showing 25% revenue increase" />
|
||||
|
||||
<!-- ✅ Decorative image -->
|
||||
<img src="decoration.svg" alt="" />
|
||||
```
|
||||
|
||||
### Screen Reader Only Text
|
||||
|
||||
```css
|
||||
.sr-only {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
padding: 0;
|
||||
margin: -1px;
|
||||
overflow: hidden;
|
||||
clip: rect(0, 0, 0, 0);
|
||||
white-space: nowrap;
|
||||
border-width: 0;
|
||||
}
|
||||
```
|
||||
|
||||
## Accessibility Checklist
|
||||
|
||||
### Perceivable
|
||||
- ✅ Text has sufficient contrast (4.5:1)
|
||||
- ✅ Images have alt text
|
||||
- ✅ Color is not the only indicator
|
||||
- ✅ Content works at 200% zoom
|
||||
|
||||
### Operable
|
||||
- ✅ All functionality keyboard accessible
|
||||
- ✅ Focus indicators visible
|
||||
- ✅ No keyboard traps
|
||||
- ✅ Touch targets at least 44x44px
|
||||
|
||||
### Understandable
|
||||
- ✅ Clear, simple language
|
||||
- ✅ Labels for all form inputs
|
||||
- ✅ Error messages are clear
|
||||
- ✅ Consistent navigation
|
||||
|
||||
### Robust
|
||||
- ✅ Semantic HTML
|
||||
- ✅ Valid ARIA attributes
|
||||
- ✅ Works with assistive technologies
|
||||
|
||||
**Remember: Accessibility is not a checklist—it's an ongoing commitment to inclusive design.**
|
||||
69
.cursor/rules/animation-principles.mdc
Normal file
69
.cursor/rules/animation-principles.mdc
Normal file
@@ -0,0 +1,69 @@
|
||||
---
|
||||
description: Animation timing, easing, stagger patterns, and motion design principles
|
||||
alwaysApply: false
|
||||
globs: **/*.{ts,tsx,js,jsx,vue,css,scss}
|
||||
---
|
||||
|
||||
# Animation Principles & Motion Design
|
||||
|
||||
## Core Philosophy
|
||||
|
||||
Every animation should serve one of these purposes:
|
||||
- **Guide Attention**: Direct user focus to important elements
|
||||
- **Provide Feedback**: Confirm actions and state changes
|
||||
- **Show Relationships**: Reveal spatial and hierarchical connections
|
||||
- **Enhance Perception**: Make loading and transitions feel faster
|
||||
- **Add Delight**: Create memorable micro-interactions
|
||||
|
||||
## Duration Guidelines
|
||||
|
||||
```javascript
|
||||
const durations = {
|
||||
instant: 100, // Micro-feedback (hover states)
|
||||
fast: 200, // Small elements (tooltips, dropdowns)
|
||||
moderate: 300, // Standard UI transitions (modals, cards)
|
||||
normal: 500, // Page sections, complex components
|
||||
slow: 600, // Hero animations, page transitions
|
||||
};
|
||||
```
|
||||
|
||||
## Easing Functions
|
||||
|
||||
- **ease-out**: 90% of entrance animations (fade in, slide in)
|
||||
- **ease-in**: Exit animations (fade out, slide out)
|
||||
- **ease-in-out**: Position changes, transforms
|
||||
- **spring**: Playful interactions, emphasis
|
||||
- **linear**: Progress bars, loading spinners
|
||||
|
||||
## Staggered Animation Pattern
|
||||
|
||||
```javascript
|
||||
// Delay calculation
|
||||
const staggerDelay = (index, baseDelay = 100) => index * baseDelay;
|
||||
```
|
||||
|
||||
Guidelines:
|
||||
- Delay increment: 50-150ms between items
|
||||
- Max items: Limit to 6-8 visible items
|
||||
- Direction: Top-to-bottom or left-to-right
|
||||
|
||||
## Reduced Motion Accessibility
|
||||
|
||||
Always respect `prefers-reduced-motion` settings:
|
||||
|
||||
```css
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
* {
|
||||
animation-duration: 0.01ms !important;
|
||||
animation-iteration-count: 1 !important;
|
||||
transition-duration: 0.01ms !important;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Performance
|
||||
|
||||
- ✅ Animate `transform` and `opacity` only
|
||||
- ✅ Use `will-change` sparingly
|
||||
- ✅ Limit simultaneous animations
|
||||
- ❌ Don't animate `width`, `height`, `top`, `left`
|
||||
239
.cursor/rules/code-quality.mdc
Normal file
239
.cursor/rules/code-quality.mdc
Normal file
@@ -0,0 +1,239 @@
|
||||
---
|
||||
description: Code quality principles and clean code practices
|
||||
alwaysApply: true
|
||||
---
|
||||
|
||||
# Code Quality & Clean Code Standards
|
||||
|
||||
## Core Principles
|
||||
|
||||
### 1. Readability First
|
||||
|
||||
Code is read far more often than it's written.
|
||||
|
||||
```javascript
|
||||
// ❌ Bad - Unclear, cryptic
|
||||
const d = new Date()
|
||||
const y = d.getFullYear()
|
||||
|
||||
// ✅ Good - Clear, self-documenting
|
||||
const today = new Date()
|
||||
const year = today.getFullYear()
|
||||
```
|
||||
|
||||
### 2. Single Responsibility Principle
|
||||
|
||||
Each function/component should do one thing well.
|
||||
|
||||
```javascript
|
||||
// ✅ Good - Single responsibility
|
||||
function validateEmail(email) {
|
||||
return email && email.length > 0
|
||||
}
|
||||
|
||||
function formatEmail(email) {
|
||||
return email.toLowerCase().trim()
|
||||
}
|
||||
```
|
||||
|
||||
### 3. DRY (Don't Repeat Yourself)
|
||||
|
||||
Extract repeated logic into reusable functions.
|
||||
|
||||
### 4. Meaningful Names
|
||||
|
||||
Names should reveal intent.
|
||||
|
||||
```javascript
|
||||
// ❌ Bad
|
||||
const d = 86400000
|
||||
const arr = []
|
||||
|
||||
// ✅ Good
|
||||
const MILLISECONDS_PER_DAY = 86400000
|
||||
const activeProjects = []
|
||||
```
|
||||
|
||||
### 5. Keep It Simple (KISS)
|
||||
|
||||
Simplicity is the ultimate sophistication.
|
||||
|
||||
## Error Handling
|
||||
|
||||
### Always Handle Errors
|
||||
|
||||
```javascript
|
||||
// ✅ Good - Comprehensive error handling
|
||||
async function fetchProjects() {
|
||||
const error = ref(null)
|
||||
const isLoading = ref(true)
|
||||
const data = ref(null)
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/projects')
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`)
|
||||
}
|
||||
|
||||
data.value = await response.json()
|
||||
} catch (err) {
|
||||
error.value = err.message
|
||||
console.error('Failed to fetch projects:', err)
|
||||
} finally {
|
||||
isLoading.value = false
|
||||
}
|
||||
|
||||
return { data, error, isLoading }
|
||||
}
|
||||
```
|
||||
|
||||
### User-Friendly Error Messages
|
||||
|
||||
```javascript
|
||||
function getErrorMessage(error) {
|
||||
const messages = {
|
||||
'ERR_CONNECTION_REFUSED': 'Unable to connect. Please check your internet.',
|
||||
'ERR_TIMEOUT': 'Request timed out. Please try again.',
|
||||
}
|
||||
|
||||
return messages[error.code] || 'An unexpected error occurred.'
|
||||
}
|
||||
```
|
||||
|
||||
## Comments & Documentation
|
||||
|
||||
### When to Comment
|
||||
|
||||
#### ✅ DO Comment:
|
||||
- **Why** something is done (not what)
|
||||
- Complex algorithms
|
||||
- Non-obvious decisions
|
||||
- Workarounds for bugs
|
||||
|
||||
```javascript
|
||||
// ✅ Good - Explains WHY
|
||||
// We use a 300ms debounce to avoid overwhelming the API
|
||||
// with requests while the user is still typing
|
||||
const debouncedSearch = debounce(searchProjects, 300)
|
||||
```
|
||||
|
||||
#### ❌ DON'T Comment:
|
||||
- Obvious code
|
||||
- Outdated information
|
||||
- Commented-out code (delete it instead)
|
||||
|
||||
### JSDoc for Public APIs
|
||||
|
||||
```javascript
|
||||
/**
|
||||
* Formats a date into human-readable format
|
||||
*
|
||||
* @param {string|Date} date - The date to format
|
||||
* @param {Object} options - Formatting options
|
||||
* @returns {string} Formatted date string
|
||||
*
|
||||
* @example
|
||||
* formatDate('2026-02-02', { format: 'long' })
|
||||
* // Returns: "February 2, 2026"
|
||||
*/
|
||||
export function formatDate(date, options = {}) {
|
||||
// Implementation
|
||||
}
|
||||
```
|
||||
|
||||
## Import Order
|
||||
|
||||
```javascript
|
||||
// 1. External libraries
|
||||
import { ref, computed } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
// 2. Internal modules
|
||||
import { useAuth } from '@/composables/useAuth'
|
||||
import { formatDate } from '@/utils/date'
|
||||
|
||||
// 3. Components
|
||||
import Button from '@/components/atoms/Button.vue'
|
||||
|
||||
// 4. Styles
|
||||
import './styles.css'
|
||||
```
|
||||
|
||||
## Code Review Checklist
|
||||
|
||||
### Before Submitting PR
|
||||
- ✅ Code runs without errors
|
||||
- ✅ Tests pass
|
||||
- ✅ No console errors or warnings
|
||||
- ✅ Linter passes
|
||||
- ✅ Code follows conventions
|
||||
- ✅ Complex logic is commented
|
||||
- ✅ No commented-out code
|
||||
- ✅ Mobile responsive
|
||||
- ✅ Accessibility checked
|
||||
|
||||
### What to Look For in Review
|
||||
- **Correctness**: Does it work as intended?
|
||||
- **Readability**: Is it easy to understand?
|
||||
- **Maintainability**: Can it be easily modified?
|
||||
- **Performance**: Any obvious bottlenecks?
|
||||
- **Security**: Any vulnerabilities?
|
||||
- **Testing**: Adequate test coverage?
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Guard Clauses
|
||||
|
||||
```javascript
|
||||
// ✅ Good - Early returns
|
||||
function processUser(user) {
|
||||
if (!user) return null
|
||||
if (!user.email) return null
|
||||
if (!user.isActive) return null
|
||||
|
||||
// Main logic here
|
||||
return processedUser
|
||||
}
|
||||
```
|
||||
|
||||
### Avoid Nested Ifs
|
||||
|
||||
```javascript
|
||||
// ❌ Bad
|
||||
if (user) {
|
||||
if (user.isActive) {
|
||||
if (user.hasPermission) {
|
||||
// Do something
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ✅ Good
|
||||
if (!user) return
|
||||
if (!user.isActive) return
|
||||
if (!user.hasPermission) return
|
||||
|
||||
// Do something
|
||||
```
|
||||
|
||||
## Summary Checklist
|
||||
|
||||
### Code
|
||||
- ✅ Readable and self-documenting
|
||||
- ✅ Single responsibility
|
||||
- ✅ DRY (no repetition)
|
||||
- ✅ Meaningful names
|
||||
- ✅ Simple and clear
|
||||
|
||||
### Error Handling
|
||||
- ✅ All errors caught
|
||||
- ✅ User-friendly messages
|
||||
- ✅ Logged appropriately
|
||||
|
||||
### Documentation
|
||||
- ✅ Complex logic commented
|
||||
- ✅ Public APIs documented
|
||||
- ✅ No outdated comments
|
||||
|
||||
**Remember: Quality is not an act, it's a habit.**
|
||||
79
.cursor/rules/component-architecture.mdc
Normal file
79
.cursor/rules/component-architecture.mdc
Normal file
@@ -0,0 +1,79 @@
|
||||
---
|
||||
description: Component composition patterns and architecture principles
|
||||
alwaysApply: false
|
||||
globs: **/*.{ts,tsx,js,jsx,vue,svelte}
|
||||
---
|
||||
|
||||
# Component Architecture & Composition Patterns
|
||||
|
||||
## Core Philosophy
|
||||
|
||||
**Composition Over Configuration**
|
||||
|
||||
Build complex UIs from simple, focused components that compose well together.
|
||||
|
||||
### Key Tenets
|
||||
|
||||
- **Single Responsibility**: Each component does one thing well
|
||||
- **Composition Slots**: Use children/slots instead of complex prop APIs
|
||||
- **Default Props**: Provide sensible defaults for easy use
|
||||
- **Prop Interfaces**: Clear contracts with TypeScript/PropTypes
|
||||
- **Minimal State**: Keep component state local and minimal
|
||||
|
||||
## Anti-patterns to Avoid
|
||||
|
||||
- ❌ God components that do everything
|
||||
- ❌ Prop drilling through many layers
|
||||
- ❌ Hard-coded values instead of props
|
||||
- ❌ Component logic mixed with layout
|
||||
- ❌ Tight coupling between components
|
||||
|
||||
## Template Pattern
|
||||
|
||||
Create base templates that handle common layouts and let content be injected.
|
||||
|
||||
```jsx
|
||||
const PageTemplate = ({ title, children }) => (
|
||||
<div className="page-container">
|
||||
<Header />
|
||||
<main className="page-content">
|
||||
{title && <h1>{title}</h1>}
|
||||
{children}
|
||||
</main>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
```
|
||||
|
||||
## Container/Presenter Pattern
|
||||
|
||||
Split components into smart (containers) and dumb (presenters) components.
|
||||
|
||||
- **Container**: Handles data and logic
|
||||
- **Presenter**: Pure presentation, receives data via props
|
||||
|
||||
## Prop Interface Design
|
||||
|
||||
```typescript
|
||||
interface BaseComponentProps {
|
||||
className?: string;
|
||||
children?: React.ReactNode;
|
||||
testId?: string;
|
||||
}
|
||||
|
||||
interface ButtonProps extends BaseComponentProps {
|
||||
variant?: 'primary' | 'secondary' | 'ghost';
|
||||
size?: 'sm' | 'md' | 'lg';
|
||||
disabled?: boolean;
|
||||
loading?: boolean;
|
||||
onClick?: (event: React.MouseEvent) => void;
|
||||
}
|
||||
```
|
||||
|
||||
### Prop Naming Conventions
|
||||
|
||||
- Boolean props: `isOpen`, `hasError`, `disabled`
|
||||
- Handler props: `onClick`, `onClose`, `onChange`
|
||||
- Render props: `renderHeader`, `renderItem`
|
||||
- Data props: `items`, `data`, `value`
|
||||
- Config props: `variant`, `size`, `theme`
|
||||
172
.cursor/rules/git-workflow.mdc
Normal file
172
.cursor/rules/git-workflow.mdc
Normal file
@@ -0,0 +1,172 @@
|
||||
---
|
||||
description: Git workflow and commit message conventions
|
||||
alwaysApply: false
|
||||
globs: .git/**
|
||||
---
|
||||
|
||||
# Git Workflow & Version Control
|
||||
|
||||
## Commit Message Format
|
||||
|
||||
```
|
||||
<type>(<scope>): <subject>
|
||||
|
||||
<body>
|
||||
|
||||
<footer>
|
||||
```
|
||||
|
||||
### Types
|
||||
- **feat**: New feature
|
||||
- **fix**: Bug fix
|
||||
- **docs**: Documentation only
|
||||
- **style**: Code style (formatting)
|
||||
- **refactor**: Code restructuring
|
||||
- **perf**: Performance improvement
|
||||
- **test**: Adding tests
|
||||
- **chore**: Build process, dependencies
|
||||
|
||||
### Examples
|
||||
|
||||
```bash
|
||||
# ✅ Good commit messages
|
||||
feat(auth): add password reset functionality
|
||||
fix(button): resolve hover state in dark mode
|
||||
docs(readme): update installation instructions
|
||||
refactor(utils): extract date formatting logic
|
||||
perf(images): lazy load project thumbnails
|
||||
|
||||
# ❌ Bad commit messages
|
||||
fix bug
|
||||
update stuff
|
||||
changes
|
||||
WIP
|
||||
```
|
||||
|
||||
## Branching Strategy
|
||||
|
||||
### Branch Naming
|
||||
|
||||
```
|
||||
<type>/<short-description>
|
||||
|
||||
Examples:
|
||||
feature/user-authentication
|
||||
fix/mobile-navigation-bug
|
||||
refactor/theme-system
|
||||
docs/api-documentation
|
||||
```
|
||||
|
||||
## Feature Development Workflow
|
||||
|
||||
```bash
|
||||
# 1. Start from main
|
||||
git checkout main
|
||||
git pull origin main
|
||||
|
||||
# 2. Create feature branch
|
||||
git checkout -b feature/user-profile
|
||||
|
||||
# 3. Make changes and commit
|
||||
git add src/components/UserProfile.vue
|
||||
git commit -m "feat(profile): add user profile component"
|
||||
|
||||
# 4. Push branch
|
||||
git push -u origin feature/user-profile
|
||||
|
||||
# 5. Create Pull Request
|
||||
```
|
||||
|
||||
## Pull Request Guidelines
|
||||
|
||||
### PR Title Format
|
||||
|
||||
```
|
||||
<type>(<scope>): <description>
|
||||
|
||||
Examples:
|
||||
feat(theme): add dark mode toggle
|
||||
fix(navigation): resolve mobile menu closing issue
|
||||
```
|
||||
|
||||
### PR Checklist
|
||||
- ✅ Code follows project conventions
|
||||
- ✅ Tests added/updated
|
||||
- ✅ Documentation updated
|
||||
- ✅ No merge conflicts
|
||||
- ✅ CI/CD passes
|
||||
- ✅ Mobile responsive
|
||||
- ✅ Accessibility checked
|
||||
|
||||
## Code Review Process
|
||||
|
||||
### As a Reviewer
|
||||
- **Be kind and constructive**
|
||||
- **Explain why, not just what**
|
||||
- **Ask questions, don't make demands**
|
||||
- **Praise good work**
|
||||
|
||||
```markdown
|
||||
<!-- ❌ Bad review comment -->
|
||||
This is wrong.
|
||||
|
||||
<!-- ✅ Good review comment -->
|
||||
This approach might cause performance issues with large datasets.
|
||||
Consider using pagination instead. What do you think?
|
||||
```
|
||||
|
||||
## Useful Git Commands
|
||||
|
||||
### Daily Commands
|
||||
|
||||
```bash
|
||||
# Check status
|
||||
git status
|
||||
|
||||
# View changes
|
||||
git diff
|
||||
|
||||
# View commit history
|
||||
git log --oneline --graph --all
|
||||
|
||||
# Stash changes
|
||||
git stash
|
||||
|
||||
# Apply stash
|
||||
git stash pop
|
||||
```
|
||||
|
||||
### Undoing Changes
|
||||
|
||||
```bash
|
||||
# Discard changes to file
|
||||
git restore src/App.vue
|
||||
|
||||
# Unstage file
|
||||
git restore --staged src/App.vue
|
||||
|
||||
# Undo last commit, keep changes
|
||||
git reset --soft HEAD~1
|
||||
```
|
||||
|
||||
## Git Best Practices Summary
|
||||
|
||||
### Commits
|
||||
- ✅ Small, focused commits
|
||||
- ✅ Clear, descriptive messages
|
||||
- ✅ Commit often
|
||||
- ✅ One logical change per commit
|
||||
|
||||
### Branches
|
||||
- ✅ Descriptive branch names
|
||||
- ✅ Keep branches short-lived
|
||||
- ✅ Delete merged branches
|
||||
- ✅ Pull frequently
|
||||
|
||||
### Pull Requests
|
||||
- ✅ Clear title and description
|
||||
- ✅ Link to relevant issues
|
||||
- ✅ Request reviews
|
||||
- ✅ Address feedback promptly
|
||||
|
||||
**Remember: Git is a tool for collaboration. Use it to make your team more effective.**
|
||||
109
.cursor/rules/master-philosophy.mdc
Normal file
109
.cursor/rules/master-philosophy.mdc
Normal file
@@ -0,0 +1,109 @@
|
||||
---
|
||||
description: Core front-end development philosophy and principles
|
||||
alwaysApply: true
|
||||
---
|
||||
|
||||
# Master Front-End Development Philosophy
|
||||
|
||||
## Core Mission
|
||||
|
||||
Build the best front-end code ever made by combining modern frameworks, utility-first CSS, systematic design thinking, and flawless execution.
|
||||
|
||||
## Philosophical Pillars
|
||||
|
||||
### 1. Mobile-First, Everywhere-Perfect
|
||||
- Every component works flawlessly on mobile, tablet, and desktop
|
||||
- Mobile is the foundation, not an afterthought
|
||||
- Touch targets, viewport management, and safe areas are first-class citizens
|
||||
|
||||
### 2. Consistency is Sacred
|
||||
- Mobile and desktop versions show **identical content and functionality** unless explicitly designed otherwise
|
||||
- Design tokens ensure visual consistency across all breakpoints
|
||||
- User experience never diverges accidentally between screen sizes
|
||||
|
||||
**Verification Before Shipping:**
|
||||
- ✅ Same navigation items on mobile and desktop
|
||||
- ✅ Same content sections visible
|
||||
- ✅ Same buttons and CTAs available
|
||||
- ✅ Only layout and presentation differ
|
||||
|
||||
### 3. Theme-First Architecture
|
||||
- Applications are built with theming as a core architectural decision from day one
|
||||
- Themes are CSS-based with reactive state management
|
||||
- Dark mode and light mode are equals, not primary/secondary
|
||||
|
||||
**CRITICAL THEME RULE**: Header, sidebar, and main background must use the same fill color.
|
||||
|
||||
### 4. Utility-First, Component-Second
|
||||
- Tailwind CSS utilities in templates for maximum flexibility
|
||||
- Component classes only for truly reusable patterns
|
||||
- Extract components when you repeat, not before
|
||||
|
||||
### 5. Performance as a Feature
|
||||
- **Bundle Size**: < 200KB gzipped
|
||||
- **LCP**: < 2.5s
|
||||
- **FID**: < 100ms
|
||||
- CSS transforms for GPU acceleration
|
||||
- Lazy loading and code splitting by default
|
||||
|
||||
### 6. Design System as Foundation
|
||||
- Typography scale, spacing scale, and color palettes defined upfront
|
||||
- **4px base grid system** for mathematical harmony
|
||||
- Consistent component patterns documented
|
||||
|
||||
### 7. Accessibility is Non-Negotiable
|
||||
- **WCAG AA compliance minimum**
|
||||
- Keyboard navigation works everywhere
|
||||
- Screen reader friendly
|
||||
- **4.5:1 contrast ratio** for normal text
|
||||
- **44x44px minimum touch targets**
|
||||
|
||||
### 8. Real Users, Real Devices
|
||||
- Test on actual phones and tablets
|
||||
- Consider network conditions (3G, 4G, WiFi)
|
||||
- Handle offline states gracefully
|
||||
- Progressive enhancement over graceful degradation
|
||||
|
||||
## Success Metrics
|
||||
|
||||
### Code Quality
|
||||
- ✅ Can a new developer understand the codebase in under 1 hour?
|
||||
- ✅ Does the app work perfectly on a 5-year-old phone?
|
||||
- ✅ Is mobile/desktop consistency maintained automatically?
|
||||
|
||||
### User Experience
|
||||
- ✅ Does it feel native on every device?
|
||||
- ✅ Does the interface respond instantly to user input?
|
||||
- ✅ Load in under 2s on desktop, under 5s on mobile 3G
|
||||
|
||||
## Anti-Patterns to Avoid
|
||||
|
||||
### ❌ Desktop-First Thinking
|
||||
Never build desktop first and "make it responsive later"
|
||||
|
||||
### ❌ Inconsistent Experiences
|
||||
Mobile users should never get a degraded experience
|
||||
|
||||
### ❌ Hardcoded Values
|
||||
Use design tokens, variables, and systematic scales
|
||||
|
||||
### ❌ Ignoring Performance
|
||||
Every kilobyte matters, every millisecond counts
|
||||
|
||||
### ❌ Accessibility as Afterthought
|
||||
Build accessible from day one, not as a retrofit
|
||||
|
||||
### ❌ Skipping Documentation
|
||||
Code without documentation is technical debt
|
||||
|
||||
## The Ultimate Goal
|
||||
|
||||
**When someone uses your application:**
|
||||
- "This feels incredibly polished"
|
||||
- "Everything just works"
|
||||
- "This looks beautiful on my device"
|
||||
|
||||
**When a developer reads your code:**
|
||||
- "This is so well organized"
|
||||
- "I understand exactly what's happening"
|
||||
- "Adding features will be straightforward"
|
||||
118
.cursor/rules/mobile-ux-patterns.mdc
Normal file
118
.cursor/rules/mobile-ux-patterns.mdc
Normal file
@@ -0,0 +1,118 @@
|
||||
---
|
||||
description: Mobile-first UX patterns and touch interaction design
|
||||
alwaysApply: false
|
||||
globs: **/*.{ts,tsx,js,jsx,vue,css,scss}
|
||||
---
|
||||
|
||||
# Mobile UX Patterns & Touch Interactions
|
||||
|
||||
## Mobile-First Philosophy
|
||||
|
||||
Start with mobile constraints, then progressively enhance for larger screens.
|
||||
|
||||
### Design Process
|
||||
|
||||
1. **Mobile (320-480px)**: Core functionality only
|
||||
2. **Tablet (768-1024px)**: Add supporting features
|
||||
3. **Desktop (1280px+)**: Enhanced experience
|
||||
|
||||
## Touch Target Sizing
|
||||
|
||||
### Minimum 44x44px Touch Targets
|
||||
|
||||
```css
|
||||
.touch-target {
|
||||
min-width: 44px;
|
||||
min-height: 44px;
|
||||
/* Apple HIG recommendation */
|
||||
}
|
||||
|
||||
.touch-target-optimal {
|
||||
min-width: 48px;
|
||||
min-height: 48px;
|
||||
/* Material Design recommendation */
|
||||
}
|
||||
```
|
||||
|
||||
### Spacing Between Targets
|
||||
|
||||
```css
|
||||
.button-group {
|
||||
display: flex;
|
||||
gap: 16px; /* Comfortable spacing */
|
||||
}
|
||||
```
|
||||
|
||||
## Thumb-Friendly Zones
|
||||
|
||||
```
|
||||
┌─────────────────┐
|
||||
│ Hard to reach │ ← Top 20%
|
||||
├─────────────────┤
|
||||
│ Easy reach │ ← Middle 60%
|
||||
├─────────────────┤
|
||||
│ Natural thumb │ ← Bottom 20%
|
||||
│ zone │ (place primary actions here)
|
||||
└─────────────────┘
|
||||
```
|
||||
|
||||
Primary actions should be placed in the bottom 20% for one-handed use.
|
||||
|
||||
## Dynamic Viewport Height
|
||||
|
||||
```css
|
||||
/* Use dvh instead of vh for mobile */
|
||||
.hero {
|
||||
height: 100dvh; /* Dynamic viewport height */
|
||||
}
|
||||
|
||||
/* Fallback for older browsers */
|
||||
.hero {
|
||||
height: 100vh;
|
||||
height: 100dvh;
|
||||
}
|
||||
```
|
||||
|
||||
## Safe Area Insets (iOS)
|
||||
|
||||
```css
|
||||
.header {
|
||||
padding-top: env(safe-area-inset-top);
|
||||
padding-left: env(safe-area-inset-left);
|
||||
padding-right: env(safe-area-inset-right);
|
||||
}
|
||||
|
||||
.footer {
|
||||
padding-bottom: env(safe-area-inset-bottom);
|
||||
}
|
||||
```
|
||||
|
||||
## Form Input Optimization
|
||||
|
||||
```html
|
||||
<!-- Use correct input types for mobile keyboards -->
|
||||
<input type="email" inputmode="email">
|
||||
<input type="number" inputmode="numeric">
|
||||
<input type="tel" inputmode="tel">
|
||||
<input type="url" inputmode="url">
|
||||
```
|
||||
|
||||
```css
|
||||
/* Prevent iOS zoom on focus */
|
||||
input, select, textarea {
|
||||
font-size: 16px; /* Minimum to prevent zoom */
|
||||
}
|
||||
```
|
||||
|
||||
## Touch Feedback
|
||||
|
||||
```css
|
||||
.button {
|
||||
transition: transform 0.1s ease, opacity 0.1s ease;
|
||||
}
|
||||
|
||||
.button:active {
|
||||
transform: scale(0.95);
|
||||
opacity: 0.8;
|
||||
}
|
||||
```
|
||||
224
.cursor/rules/performance-optimization.mdc
Normal file
224
.cursor/rules/performance-optimization.mdc
Normal file
@@ -0,0 +1,224 @@
|
||||
---
|
||||
description: Performance optimization and bundle size management
|
||||
alwaysApply: false
|
||||
globs: **/*.{js,ts,vue,jsx,tsx}
|
||||
---
|
||||
|
||||
# Performance & Optimization
|
||||
|
||||
## Performance Targets
|
||||
|
||||
- **Bundle Size**: < 200KB gzipped
|
||||
- **LCP** (Largest Contentful Paint): < 2.5s
|
||||
- **FID** (First Input Delay): < 100ms
|
||||
- **Mobile 3G**: Load in under 5s
|
||||
- **Desktop**: Load in under 2s
|
||||
- **Animation Frame Rate**: 60fps (16.67ms per frame)
|
||||
|
||||
## Bundle Size Optimization
|
||||
|
||||
### 1. Code Splitting by Route
|
||||
|
||||
```javascript
|
||||
// Vue Router with lazy loading
|
||||
const routes = [
|
||||
{
|
||||
path: '/',
|
||||
component: HomePage // Eager loaded
|
||||
},
|
||||
{
|
||||
path: '/projects/:id',
|
||||
component: () => import('./views/ProjectDetail.vue') // Lazy loaded
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
### 2. Lazy Load Heavy Components
|
||||
|
||||
```javascript
|
||||
import { defineAsyncComponent } from 'vue'
|
||||
|
||||
const HeavyChart = defineAsyncComponent(() =>
|
||||
import('./components/HeavyChart.vue')
|
||||
)
|
||||
```
|
||||
|
||||
### 3. Tree Shaking
|
||||
|
||||
```javascript
|
||||
// ✅ Good - Named imports (tree-shakeable)
|
||||
import { ref, computed } from 'vue'
|
||||
|
||||
// ❌ Bad - Default import (not tree-shakeable)
|
||||
import Vue from 'vue'
|
||||
```
|
||||
|
||||
## Image Optimization
|
||||
|
||||
### 1. Use Modern Formats
|
||||
- **WebP**: 30% smaller than JPEG
|
||||
- **AVIF**: Even smaller
|
||||
|
||||
```html
|
||||
<picture>
|
||||
<source srcset="image.avif" type="image/avif">
|
||||
<source srcset="image.webp" type="image/webp">
|
||||
<img src="image.jpg" alt="Fallback">
|
||||
</picture>
|
||||
```
|
||||
|
||||
### 2. Lazy Loading
|
||||
|
||||
```html
|
||||
<img src="image.jpg" loading="lazy" alt="Lazy loaded" />
|
||||
```
|
||||
|
||||
### 3. Responsive Images
|
||||
|
||||
```html
|
||||
<img
|
||||
srcset="
|
||||
image-320.jpg 320w,
|
||||
image-640.jpg 640w,
|
||||
image-1024.jpg 1024w
|
||||
"
|
||||
sizes="(max-width: 640px) 100vw, 50vw"
|
||||
src="image-640.jpg"
|
||||
alt="Responsive image"
|
||||
/>
|
||||
```
|
||||
|
||||
## Font Optimization
|
||||
|
||||
### 1. Preload Critical Fonts
|
||||
|
||||
```html
|
||||
<link
|
||||
rel="preload"
|
||||
href="/fonts/ProximaNova-Bold.woff2"
|
||||
as="font"
|
||||
type="font/woff2"
|
||||
crossorigin
|
||||
>
|
||||
```
|
||||
|
||||
### 2. Font Display Strategy
|
||||
|
||||
```css
|
||||
@font-face {
|
||||
font-family: 'Proxima Nova';
|
||||
src: url('/fonts/proximanova.woff2') format('woff2');
|
||||
font-display: swap; /* Show fallback immediately */
|
||||
}
|
||||
```
|
||||
|
||||
## JavaScript Performance
|
||||
|
||||
### 1. Debounce Expensive Operations
|
||||
|
||||
```javascript
|
||||
let timeout
|
||||
const handleSearch = (event) => {
|
||||
clearTimeout(timeout)
|
||||
timeout = setTimeout(() => {
|
||||
// Perform search
|
||||
}, 300)
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Memoization
|
||||
|
||||
```javascript
|
||||
import { computed } from 'vue'
|
||||
|
||||
const filteredProjects = computed(() => {
|
||||
return projects.value.filter(p =>
|
||||
p.title.includes(searchQuery.value)
|
||||
)
|
||||
})
|
||||
```
|
||||
|
||||
## Rendering Performance
|
||||
|
||||
### 1. Use CSS Transforms for Animations
|
||||
|
||||
```css
|
||||
/* ❌ Bad - Triggers layout */
|
||||
.element {
|
||||
transition: top 0.3s;
|
||||
}
|
||||
|
||||
/* ✅ Good - GPU accelerated */
|
||||
.element {
|
||||
transition: transform 0.3s;
|
||||
}
|
||||
.element:hover {
|
||||
transform: translateY(10px);
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Animate Only Transform and Opacity
|
||||
|
||||
- ✅ Animate `transform` and `opacity` only
|
||||
- ❌ Don't animate `width`, `height`, `top`, `left`
|
||||
|
||||
## Vue-Specific Optimizations
|
||||
|
||||
### Use `v-once` for Static Content
|
||||
|
||||
```vue
|
||||
<div v-once>
|
||||
<h1>{{ staticTitle }}</h1>
|
||||
</div>
|
||||
```
|
||||
|
||||
### Shallow Reactive for Large Objects
|
||||
|
||||
```javascript
|
||||
import { shallowRef } from 'vue'
|
||||
const projects = shallowRef([...largeArray])
|
||||
```
|
||||
|
||||
### Keep Components Small
|
||||
|
||||
Split large components into smaller, focused ones.
|
||||
|
||||
## Mobile Performance
|
||||
|
||||
### 1. Use Passive Event Listeners
|
||||
|
||||
```javascript
|
||||
element.addEventListener('touchstart', handler, { passive: true })
|
||||
```
|
||||
|
||||
### 2. Test on Real Devices
|
||||
|
||||
- Use slow 3G throttling
|
||||
- Test on mid-range Android devices
|
||||
- Use Chrome DevTools CPU throttling (4x slowdown)
|
||||
|
||||
## Performance Checklist
|
||||
|
||||
### Build Time
|
||||
- ✅ Code split by route
|
||||
- ✅ Lazy load heavy components
|
||||
- ✅ Tree shake unused code
|
||||
- ✅ Minify and compress assets
|
||||
- ✅ Optimize images (WebP, compression)
|
||||
|
||||
### Runtime
|
||||
- ✅ Debounce expensive operations
|
||||
- ✅ Memoize computed values
|
||||
- ✅ Use CSS transforms for animations
|
||||
- ✅ Avoid layout thrashing
|
||||
|
||||
### Network
|
||||
- ✅ Enable compression (gzip/brotli)
|
||||
- ✅ Set appropriate cache headers
|
||||
- ✅ Preload/prefetch resources
|
||||
|
||||
### Fonts
|
||||
- ✅ Preload critical fonts
|
||||
- ✅ Use font-display: swap
|
||||
|
||||
**Remember: Performance is not a one-time task—it's an ongoing practice.**
|
||||
183
.cursor/rules/quick-reference.mdc
Normal file
183
.cursor/rules/quick-reference.mdc
Normal file
@@ -0,0 +1,183 @@
|
||||
---
|
||||
description: Quick reference for common patterns and commands
|
||||
alwaysApply: false
|
||||
globs: **/*.{vue,js,ts,html}
|
||||
---
|
||||
|
||||
# Quick Reference Guide
|
||||
|
||||
## Vue 3 Composition API Template
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<div>{{ message }}</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
|
||||
// Props
|
||||
const props = defineProps({
|
||||
title: String
|
||||
})
|
||||
|
||||
// Emits
|
||||
const emit = defineEmits(['update'])
|
||||
|
||||
// State
|
||||
const message = ref('Hello')
|
||||
|
||||
// Computed
|
||||
const displayMessage = computed(() => message.value.toUpperCase())
|
||||
|
||||
// Methods
|
||||
const handleClick = () => {
|
||||
emit('update', { value: message.value })
|
||||
}
|
||||
|
||||
// Lifecycle
|
||||
onMounted(() => {
|
||||
console.log('Component mounted')
|
||||
})
|
||||
</script>
|
||||
```
|
||||
|
||||
## Tailwind Quick Classes
|
||||
|
||||
### Layout
|
||||
```html
|
||||
<div class="flex items-center justify-between gap-4">
|
||||
<div class="grid grid-cols-3 gap-6">
|
||||
<div class="max-w-7xl mx-auto px-4">
|
||||
```
|
||||
|
||||
### Spacing (4px grid)
|
||||
```html
|
||||
<div class="p-4"> <!-- 16px all sides -->
|
||||
<div class="px-6 py-3"> <!-- 24px H, 12px V -->
|
||||
<div class="mb-6"> <!-- 24px bottom -->
|
||||
```
|
||||
|
||||
### Typography
|
||||
```html
|
||||
<p class="text-sm"> <!-- 14px -->
|
||||
<p class="text-base"> <!-- 16px -->
|
||||
<p class="text-xl"> <!-- 20px -->
|
||||
<p class="font-semibold"> <!-- 600 -->
|
||||
<p class="font-bold"> <!-- 700 -->
|
||||
```
|
||||
|
||||
### Responsive
|
||||
```html
|
||||
<div class="text-base md:text-lg lg:text-xl">
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3">
|
||||
<div class="hidden md:block"> <!-- Desktop only -->
|
||||
<div class="block md:hidden"> <!-- Mobile only -->
|
||||
```
|
||||
|
||||
## Common Component Patterns
|
||||
|
||||
### Button
|
||||
```html
|
||||
<button class="px-6 py-3 bg-primary text-white rounded-lg font-semibold
|
||||
hover:bg-primary-dark transition-colors">
|
||||
Click Me
|
||||
</button>
|
||||
```
|
||||
|
||||
### Input
|
||||
```html
|
||||
<input
|
||||
type="text"
|
||||
class="w-full px-4 py-2.5 border border-gray-300 rounded-lg
|
||||
focus:border-primary focus:ring-2 focus:ring-primary-focus"
|
||||
/>
|
||||
```
|
||||
|
||||
### Card
|
||||
```html
|
||||
<div class="bg-white rounded-lg shadow-md p-6 hover:shadow-lg transition-shadow">
|
||||
<h3 class="text-xl font-semibold mb-2">Title</h3>
|
||||
<p class="text-gray-600">Content</p>
|
||||
</div>
|
||||
```
|
||||
|
||||
## Error Handling Pattern
|
||||
|
||||
```javascript
|
||||
async function fetchData() {
|
||||
const error = ref(null)
|
||||
const isLoading = ref(true)
|
||||
const data = ref(null)
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/data')
|
||||
if (!response.ok) throw new Error(`HTTP ${response.status}`)
|
||||
data.value = await response.json()
|
||||
} catch (err) {
|
||||
error.value = err.message
|
||||
} finally {
|
||||
isLoading.value = false
|
||||
}
|
||||
|
||||
return { data, error, isLoading }
|
||||
}
|
||||
```
|
||||
|
||||
## Git Commands
|
||||
|
||||
```bash
|
||||
# Daily workflow
|
||||
git status
|
||||
git checkout -b feature/new-feature
|
||||
git add .
|
||||
git commit -m "feat: add feature"
|
||||
git push origin feature/new-feature
|
||||
|
||||
# Undo changes
|
||||
git restore .
|
||||
|
||||
# Stash
|
||||
git stash
|
||||
git stash pop
|
||||
```
|
||||
|
||||
## Performance Targets
|
||||
|
||||
```
|
||||
LCP (Largest Contentful Paint): < 2.5s
|
||||
FID (First Input Delay): < 100ms
|
||||
CLS (Cumulative Layout Shift): < 0.1
|
||||
Bundle Size: < 200KB gzipped
|
||||
```
|
||||
|
||||
## Accessibility Quick Checks
|
||||
|
||||
- [ ] Images have alt text
|
||||
- [ ] Color contrast ≥ 4.5:1
|
||||
- [ ] Keyboard navigation works
|
||||
- [ ] Focus indicators visible
|
||||
- [ ] Form labels present
|
||||
- [ ] Touch targets ≥ 44x44px
|
||||
|
||||
## Responsive Breakpoints
|
||||
|
||||
```
|
||||
sm: 640px // Tablet
|
||||
md: 768px // Desktop
|
||||
lg: 1024px // Large
|
||||
xl: 1280px // XL
|
||||
```
|
||||
|
||||
## Common Regex Patterns
|
||||
|
||||
```javascript
|
||||
// Email
|
||||
/^[^\s@]+@[^\s@]+\.[^\s@]+$/
|
||||
|
||||
// URL
|
||||
/^https?:\/\/.+/
|
||||
|
||||
// Hex color
|
||||
/^#[0-9A-Fa-f]{6}$/
|
||||
```
|
||||
111
.cursor/rules/scroll-navigation.mdc
Normal file
111
.cursor/rules/scroll-navigation.mdc
Normal file
@@ -0,0 +1,111 @@
|
||||
---
|
||||
description: Scroll behavior and navigation patterns
|
||||
alwaysApply: false
|
||||
globs: **/*.{ts,tsx,js,jsx,vue,css,scss}
|
||||
---
|
||||
|
||||
# Scroll & Navigation Patterns
|
||||
|
||||
## Scroll Philosophy
|
||||
|
||||
**Default to Natural Scrolling**
|
||||
|
||||
Natural, uncontrolled scrolling should be your default. Only implement controlled scroll behaviors when they serve a clear purpose.
|
||||
|
||||
### When to Control Scroll
|
||||
|
||||
- ✅ Full-page sections (snap scrolling) - ONLY for portfolios/showcases
|
||||
- ✅ Carousels and galleries
|
||||
- ✅ Onboarding flows
|
||||
- ✅ Modal/overlay open states
|
||||
|
||||
### When to Allow Natural Scroll (Most Cases)
|
||||
|
||||
- ✅ Long-form content
|
||||
- ✅ Blog posts and articles
|
||||
- ✅ List views
|
||||
- ✅ Traditional navigation
|
||||
- ✅ E-commerce sites
|
||||
- ✅ Documentation
|
||||
- ✅ Applications and dashboards
|
||||
|
||||
## Snap Scrolling (Use Sparingly)
|
||||
|
||||
**⚠️ CAVEAT**: Snap scrolling is NOT always appropriate. Use only for specific cases like portfolios and presentations.
|
||||
|
||||
```css
|
||||
/* Container with snap behavior */
|
||||
html {
|
||||
scroll-snap-type: y mandatory;
|
||||
scroll-padding-top: 4rem;
|
||||
}
|
||||
|
||||
.snap-section {
|
||||
scroll-snap-align: start;
|
||||
scroll-snap-stop: always;
|
||||
min-height: 100vh;
|
||||
min-height: 100dvh;
|
||||
}
|
||||
```
|
||||
|
||||
## Smooth Scrolling
|
||||
|
||||
```css
|
||||
html {
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
/* Respect user preferences */
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
html {
|
||||
scroll-behavior: auto;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Scroll Lock (Modal Open)
|
||||
|
||||
```javascript
|
||||
const lockScroll = () => {
|
||||
const scrollbarWidth = window.innerWidth - document.documentElement.clientWidth;
|
||||
document.body.style.overflow = 'hidden';
|
||||
document.body.style.paddingRight = `${scrollbarWidth}px`;
|
||||
};
|
||||
|
||||
const unlockScroll = () => {
|
||||
document.body.style.overflow = '';
|
||||
document.body.style.paddingRight = '';
|
||||
};
|
||||
```
|
||||
|
||||
## Scroll Position Persistence
|
||||
|
||||
```javascript
|
||||
// Save before navigation
|
||||
const saveScrollPosition = () => {
|
||||
sessionStorage.setItem('scrollPosition', window.scrollY.toString());
|
||||
};
|
||||
|
||||
// Restore on page load
|
||||
const restoreScrollPosition = () => {
|
||||
const savedPosition = sessionStorage.getItem('scrollPosition');
|
||||
if (savedPosition) {
|
||||
window.scrollTo(0, parseInt(savedPosition, 10));
|
||||
sessionStorage.removeItem('scrollPosition');
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
## Overscroll Behavior
|
||||
|
||||
```css
|
||||
/* Disable bounce on body */
|
||||
body {
|
||||
overscroll-behavior: none;
|
||||
}
|
||||
|
||||
/* Contain overscroll to element */
|
||||
.modal {
|
||||
overscroll-behavior: contain;
|
||||
}
|
||||
```
|
||||
112
.cursor/rules/seo-meta-strategy.mdc
Normal file
112
.cursor/rules/seo-meta-strategy.mdc
Normal file
@@ -0,0 +1,112 @@
|
||||
---
|
||||
description: SEO optimization and social meta tag strategy
|
||||
alwaysApply: false
|
||||
globs: **/*.{html,tsx,jsx,vue,astro}
|
||||
---
|
||||
|
||||
# SEO & Meta Tag Strategy
|
||||
|
||||
## Core Philosophy
|
||||
|
||||
SEO combines three pillars:
|
||||
1. **Content SEO**: Quality, relevance, keywords
|
||||
2. **Technical SEO**: Structure, performance, crawlability
|
||||
3. **Social SEO**: Open Graph, Twitter Cards, sharing optimization
|
||||
|
||||
## Page Title Optimization
|
||||
|
||||
**Formula**: `[Primary Keyword] - [Secondary Keyword] | [Brand Name]`
|
||||
|
||||
**Guidelines**:
|
||||
- Optimal: 50-60 characters
|
||||
- Maximum: 70 characters
|
||||
- Mobile: 55 characters
|
||||
|
||||
```html
|
||||
<title>Bitcoin UX Design - Lightning Network Development | PROUX</title>
|
||||
```
|
||||
|
||||
## Meta Descriptions
|
||||
|
||||
**Formula**: `[Hook/Benefit] + [Key Details] + [Call-to-Action]`
|
||||
|
||||
**Guidelines**:
|
||||
- Optimal: 150-160 characters
|
||||
- Mobile: 120 characters
|
||||
- Minimum: 120 characters
|
||||
|
||||
```html
|
||||
<meta name="description" content="Transform your Bitcoin app's UX. 10+ years designing for Lightning Network. 98% user satisfaction. Explore projects →">
|
||||
```
|
||||
|
||||
## Open Graph Tags
|
||||
|
||||
**Essential OG Tags**:
|
||||
|
||||
```html
|
||||
<meta property="og:title" content="Page Title">
|
||||
<meta property="og:description" content="Description">
|
||||
<meta property="og:image" content="https://example.com/og-image.png">
|
||||
<meta property="og:url" content="https://example.com/page">
|
||||
<meta property="og:type" content="website">
|
||||
<meta property="og:site_name" content="Brand">
|
||||
```
|
||||
|
||||
### OG Image Specifications
|
||||
|
||||
- Dimensions: 1200x630px (1.91:1 ratio)
|
||||
- Format: PNG or JPG
|
||||
- File Size: < 5MB (ideally < 300KB)
|
||||
- Safe Zone: Keep text/logos in center 1200x600px
|
||||
|
||||
## Twitter Card Tags
|
||||
|
||||
```html
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:site" content="@yourusername">
|
||||
<meta name="twitter:title" content="Page Title">
|
||||
<meta name="twitter:description" content="Description">
|
||||
<meta name="twitter:image" content="https://example.com/image.jpg">
|
||||
```
|
||||
|
||||
## Structured Data (Schema.org)
|
||||
|
||||
```html
|
||||
<script type="application/ld+json">
|
||||
{
|
||||
"@context": "https://schema.org",
|
||||
"@type": "Organization",
|
||||
"name": "Company Name",
|
||||
"description": "Company description",
|
||||
"url": "https://example.com",
|
||||
"logo": "https://example.com/logo.png"
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
## Canonical URLs
|
||||
|
||||
```html
|
||||
<link rel="canonical" href="https://example.com/page">
|
||||
```
|
||||
|
||||
Use canonical URLs to:
|
||||
- Prevent duplicate content issues
|
||||
- Handle URL parameters
|
||||
- Indicate preferred URL version
|
||||
|
||||
## Performance for SEO
|
||||
|
||||
Core Web Vitals affect rankings:
|
||||
- **LCP** (Largest Contentful Paint): < 2.5s
|
||||
- **FID** (First Input Delay): < 100ms
|
||||
- **CLS** (Cumulative Layout Shift): < 0.1
|
||||
|
||||
```html
|
||||
<!-- Preload critical resources -->
|
||||
<link rel="preload" href="hero.jpg" as="image">
|
||||
<link rel="preload" href="font.woff2" as="font" crossorigin>
|
||||
|
||||
<!-- Lazy load images -->
|
||||
<img src="image.jpg" loading="lazy" alt="Description">
|
||||
```
|
||||
156
.cursor/rules/tailwind-mastery.mdc
Normal file
156
.cursor/rules/tailwind-mastery.mdc
Normal file
@@ -0,0 +1,156 @@
|
||||
---
|
||||
description: Tailwind CSS utility-first design system patterns
|
||||
alwaysApply: false
|
||||
globs: **/*.{vue,html,jsx,tsx}
|
||||
---
|
||||
|
||||
# Tailwind CSS Mastery
|
||||
|
||||
## Core Principles
|
||||
|
||||
### 1. Embrace Utility Classes
|
||||
Don't fight against utilities - they're your superpower.
|
||||
|
||||
### 2. Extract Components When You Repeat
|
||||
Create component classes only after repeating a pattern **3+ times**.
|
||||
|
||||
### 3. Mobile-First Responsive Design
|
||||
Always start mobile, progressively enhance for larger screens.
|
||||
|
||||
```html
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
<!-- Content scales beautifully -->
|
||||
</div>
|
||||
```
|
||||
|
||||
### 4. Use Design Tokens
|
||||
Extend Tailwind's config with your design system.
|
||||
|
||||
```javascript
|
||||
// tailwind.config.js
|
||||
module.exports = {
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
primary: {
|
||||
DEFAULT: '#606060',
|
||||
light: '#808080',
|
||||
dark: '#404040',
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Spacing System (4px Grid)
|
||||
|
||||
**The 4px Base Grid Rule**: All spacing follows a 4px base grid for mathematical harmony.
|
||||
|
||||
```
|
||||
4px = p-1, m-1, gap-1
|
||||
8px = p-2, m-2, gap-2
|
||||
16px = p-4, m-4, gap-4
|
||||
24px = p-6, m-6, gap-6
|
||||
32px = p-8, m-8, gap-8
|
||||
```
|
||||
|
||||
## Responsive Breakpoints
|
||||
|
||||
```javascript
|
||||
sm: 640px // Mobile landscape
|
||||
md: 768px // Tablets
|
||||
lg: 1024px // Desktop
|
||||
xl: 1280px // Large desktop
|
||||
```
|
||||
|
||||
### Mobile-First Pattern
|
||||
|
||||
```html
|
||||
<!-- Base = Mobile, then enhance -->
|
||||
<div class="text-base md:text-lg lg:text-xl">
|
||||
<!-- 16px → 18px → 20px -->
|
||||
</div>
|
||||
|
||||
<div class="p-4 md:p-6 lg:p-8">
|
||||
<!-- 16px → 24px → 32px padding -->
|
||||
</div>
|
||||
```
|
||||
|
||||
## Component Patterns
|
||||
|
||||
### Buttons
|
||||
|
||||
```html
|
||||
<!-- Primary -->
|
||||
<button class="px-6 py-3 bg-primary text-white rounded-lg font-semibold
|
||||
hover:bg-primary-dark transition-colors duration-200
|
||||
focus:outline-none focus:ring-2 focus:ring-primary-focus">
|
||||
Primary Action
|
||||
</button>
|
||||
|
||||
<!-- Secondary -->
|
||||
<button class="px-6 py-3 border-2 border-primary text-primary rounded-lg
|
||||
hover:bg-primary hover:text-white transition-all">
|
||||
Secondary Action
|
||||
</button>
|
||||
```
|
||||
|
||||
### Cards
|
||||
|
||||
```html
|
||||
<div class="bg-white rounded-lg shadow-md p-6 mb-6
|
||||
hover:shadow-lg transition-shadow duration-300">
|
||||
<h3 class="text-xl font-semibold mb-2">Card Title</h3>
|
||||
<p class="text-gray-600">Content</p>
|
||||
</div>
|
||||
```
|
||||
|
||||
### Form Elements
|
||||
|
||||
```html
|
||||
<input
|
||||
type="text"
|
||||
class="w-full px-4 py-2.5 border border-gray-300 rounded-lg
|
||||
focus:border-primary focus:ring-2 focus:ring-primary-focus
|
||||
transition-colors"
|
||||
placeholder="Enter text"
|
||||
/>
|
||||
```
|
||||
|
||||
## Color Contrast for Accessibility
|
||||
|
||||
Always ensure **4.5:1 contrast ratio** minimum (WCAG AA).
|
||||
|
||||
```html
|
||||
<!-- Good - High contrast -->
|
||||
<p class="text-gray-900 bg-white">Dark text on light</p>
|
||||
|
||||
<!-- Bad - Low contrast (avoid) -->
|
||||
<p class="text-gray-400 bg-gray-300">Hard to read!</p>
|
||||
```
|
||||
|
||||
## Custom Component Classes
|
||||
|
||||
Only extract after 3+ repetitions:
|
||||
|
||||
```css
|
||||
@layer components {
|
||||
.btn-primary {
|
||||
@apply px-6 py-3 bg-primary text-white rounded-lg font-semibold;
|
||||
@apply hover:bg-primary-dark transition-colors duration-200;
|
||||
@apply focus:outline-none focus:ring-2 focus:ring-primary-focus;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Summary Checklist
|
||||
|
||||
- ✅ Start mobile-first, enhance for larger screens
|
||||
- ✅ Use design tokens (extend Tailwind config)
|
||||
- ✅ Follow 4px spacing grid
|
||||
- ✅ Extract components after 3+ repetitions
|
||||
- ✅ Keep utility classes in HTML
|
||||
- ✅ Ensure color contrast for accessibility
|
||||
- ✅ Use transitions for smooth interactions
|
||||
- ✅ Configure PurgeCSS for production
|
||||
152
.cursor/rules/theme-architecture.mdc
Normal file
152
.cursor/rules/theme-architecture.mdc
Normal file
@@ -0,0 +1,152 @@
|
||||
---
|
||||
description: Theme system architecture with CSS-based theming
|
||||
alwaysApply: false
|
||||
globs: **/*.{css,vue,jsx,tsx}
|
||||
---
|
||||
|
||||
# Theme System Architecture
|
||||
|
||||
## Core Principle
|
||||
|
||||
Build applications with theming as a **core architectural decision from day one**.
|
||||
|
||||
## Theme Architecture Rules
|
||||
|
||||
### 1. CSS-Based Themes with Reactive State
|
||||
- Themes are pure CSS (performance)
|
||||
- State management is reactive (Vue/React)
|
||||
- No JavaScript for style calculations
|
||||
|
||||
### 2. Unified Background System ⚠️ CRITICAL
|
||||
|
||||
**Header, sidebar, and main background MUST use the same fill color.**
|
||||
|
||||
```css
|
||||
/* ✅ Good - Unified background */
|
||||
.theme-name {
|
||||
background-color: #0a0a0a;
|
||||
}
|
||||
|
||||
.theme-name aside,
|
||||
.theme-name header {
|
||||
background: #0a0a0a; /* Same as root */
|
||||
}
|
||||
|
||||
/* ❌ Bad - Clashing backgrounds */
|
||||
.theme-name aside {
|
||||
background: #1a1a1a; /* Different - creates visual clash */
|
||||
}
|
||||
```
|
||||
|
||||
### 3. No Separator Borders
|
||||
|
||||
Do not add borders between sidebar/content or header/content. Let backgrounds blend seamlessly.
|
||||
|
||||
## Implementation
|
||||
|
||||
### Theme State Management (Vue)
|
||||
|
||||
```javascript
|
||||
// composables/useTheme.js
|
||||
import { ref } from 'vue'
|
||||
|
||||
const currentTheme = ref('ultra-modern-light')
|
||||
|
||||
export function useTheme() {
|
||||
const setTheme = (themeName) => {
|
||||
currentTheme.value = themeName
|
||||
localStorage.setItem('theme', themeName)
|
||||
document.documentElement.className = themeName
|
||||
}
|
||||
|
||||
const initTheme = () => {
|
||||
const saved = localStorage.getItem('theme')
|
||||
if (saved) currentTheme.value = saved
|
||||
}
|
||||
|
||||
return { currentTheme, setTheme, initTheme }
|
||||
}
|
||||
```
|
||||
|
||||
### Theme CSS Structure
|
||||
|
||||
```css
|
||||
/* ===== THEME: Theme Name ===== */
|
||||
|
||||
/* Root Background */
|
||||
.theme-name {
|
||||
background-color: #color !important;
|
||||
}
|
||||
|
||||
/* Sidebar - MUST match root */
|
||||
.theme-name aside {
|
||||
background: #color !important;
|
||||
}
|
||||
|
||||
/* Header - MUST match root */
|
||||
.theme-name header {
|
||||
background: #color !important;
|
||||
}
|
||||
|
||||
/* Content Cards */
|
||||
.theme-name .content-card {
|
||||
background: linear-gradient(...) !important;
|
||||
border: 1px solid #color;
|
||||
box-shadow: 0 8px 24px rgba(...);
|
||||
border-radius: 16px;
|
||||
}
|
||||
|
||||
/* Buttons */
|
||||
.theme-name .btn-primary {
|
||||
background: linear-gradient(...);
|
||||
color: #fff;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* Navigation */
|
||||
.theme-name nav a.active {
|
||||
background: linear-gradient(...) !important;
|
||||
color: #color !important;
|
||||
}
|
||||
```
|
||||
|
||||
## Theme Testing Checklist
|
||||
|
||||
Before shipping a new theme:
|
||||
|
||||
- ✅ Header, sidebar, and background match
|
||||
- ✅ No visual borders/separation
|
||||
- ✅ Text is readable (sufficient contrast)
|
||||
- ✅ Buttons have clear hover states
|
||||
- ✅ Active navigation item is distinct
|
||||
- ✅ Icons are visible
|
||||
- ✅ Theme persists after page reload
|
||||
- ✅ Works on mobile and desktop
|
||||
|
||||
## Dark Mode Support
|
||||
|
||||
```css
|
||||
/* Light Theme */
|
||||
.theme-light {
|
||||
background-color: #faf9f6;
|
||||
}
|
||||
|
||||
/* Dark Theme */
|
||||
.theme-dark {
|
||||
background-color: #0a0a0a;
|
||||
}
|
||||
```
|
||||
|
||||
## Theme Best Practices
|
||||
|
||||
### ✅ DO:
|
||||
- Match header, sidebar, and root backgrounds
|
||||
- Use `!important` for theme overrides (necessary)
|
||||
- Test themes in browser before considering complete
|
||||
- Group theme styles together
|
||||
|
||||
### ❌ DON'T:
|
||||
- Add borders between sidebar and content
|
||||
- Use different background colors for header/sidebar/root
|
||||
- Create themes without testing visual harmony
|
||||
- Use inline styles that override themes
|
||||
166
.cursor/rules/visual-design-system.mdc
Normal file
166
.cursor/rules/visual-design-system.mdc
Normal file
@@ -0,0 +1,166 @@
|
||||
---
|
||||
description:
|
||||
alwaysApply: true
|
||||
---
|
||||
|
||||
# Visual Design System Principles
|
||||
|
||||
## Typography Philosophy
|
||||
|
||||
### Bold, Statement Typography
|
||||
|
||||
Large, confident typography that dominates visual hierarchy. Text is a design element, not just content.
|
||||
|
||||
**Scale Guidelines**:
|
||||
- Hero Text: 6-9rem (96-144px)
|
||||
- Page Titles: 3-4rem (48-64px)
|
||||
- Section Headers: 2-3rem (32-48px)
|
||||
- Body Text: 1-1.125rem (16-18px)
|
||||
- Small Text: 0.875rem (14px)
|
||||
|
||||
```css
|
||||
.hero-text {
|
||||
font-size: clamp(4rem, 10vw, 9rem);
|
||||
font-weight: 700;
|
||||
line-height: 1.1;
|
||||
}
|
||||
```
|
||||
|
||||
## Gradient-Based Visual Hierarchy
|
||||
|
||||
Use gradients strategically to create visual interest without clutter.
|
||||
|
||||
```css
|
||||
/* Text gradient */
|
||||
.gradient-heading {
|
||||
background: linear-gradient(to right, #ffffff, #9ca3af);
|
||||
-webkit-background-clip: text;
|
||||
background-clip: text;
|
||||
color: transparent;
|
||||
}
|
||||
```
|
||||
|
||||
## Glass Morphism
|
||||
|
||||
Create depth through semi-transparent layers with backdrop blur.
|
||||
|
||||
```css
|
||||
.glass-card {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
backdrop-filter: blur(12px);
|
||||
-webkit-backdrop-filter: blur(12px);
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
```
|
||||
|
||||
### Usage Guidelines
|
||||
|
||||
- ✅ Cards and containers over complex backgrounds
|
||||
- ✅ Navigation bars with fixed positioning
|
||||
- ✅ Modal overlays and dialogs
|
||||
- ❌ Body text containers (readability issues)
|
||||
- ❌ Form inputs (confusing UX)
|
||||
|
||||
## Color System Architecture
|
||||
|
||||
**Semantic Color Tokens** - Define colors by purpose, not appearance:
|
||||
|
||||
```javascript
|
||||
const colors = {
|
||||
brand: {
|
||||
primary: '#F7931A',
|
||||
secondary: '#8E44AD',
|
||||
},
|
||||
neutral: {
|
||||
50: '#FAFAFA',
|
||||
900: '#171717',
|
||||
},
|
||||
semantic: {
|
||||
success: '#22C55E',
|
||||
error: '#EF4444',
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
## Shadow & Glow Systems
|
||||
|
||||
Use shadows sparingly for depth perception:
|
||||
|
||||
```css
|
||||
.shadow-card {
|
||||
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1),
|
||||
0 2px 4px -1px rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
.glow-on-hover:hover {
|
||||
box-shadow: 0 0 20px rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
```
|
||||
|
||||
## Spacing System
|
||||
|
||||
**8px Base Grid** - All spacing follows 8px increments:
|
||||
|
||||
```javascript
|
||||
const spacing = {
|
||||
1: '0.25rem', // 4px
|
||||
2: '0.5rem', // 8px
|
||||
4: '1rem', // 16px
|
||||
8: '2rem', // 32px
|
||||
16: '4rem', // 64px
|
||||
32: '8rem', // 128px
|
||||
};
|
||||
```
|
||||
|
||||
## Border Radius System
|
||||
|
||||
```css
|
||||
rounded-lg /* 8px - buttons */
|
||||
rounded-2xl /* 16px - cards */
|
||||
rounded-3xl /* 24px - modals */
|
||||
rounded-full /* 9999px - circles/pills */
|
||||
```
|
||||
|
||||
## Responsive Breakpoints
|
||||
|
||||
Mobile-first progressive enhancement:
|
||||
|
||||
```javascript
|
||||
const breakpoints = {
|
||||
sm: '640px', // Mobile landscape
|
||||
md: '768px', // Tablets
|
||||
lg: '1024px', // Laptop
|
||||
xl: '1280px', // Desktop
|
||||
};
|
||||
```
|
||||
|
||||
## Dark Mode
|
||||
|
||||
Design with both modes from the start:
|
||||
|
||||
```css
|
||||
:root {
|
||||
--bg-primary: #ffffff;
|
||||
--text-primary: #171717;
|
||||
}
|
||||
|
||||
[data-theme="dark"] {
|
||||
--bg-primary: #171717;
|
||||
--text-primary: #fafafa;
|
||||
}
|
||||
```
|
||||
|
||||
## Hover State Patterns
|
||||
|
||||
All interactive elements provide immediate feedback:
|
||||
|
||||
```css
|
||||
.interactive {
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.interactive:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
```
|
||||
200
.cursor/rules/vue-conventions.mdc
Normal file
200
.cursor/rules/vue-conventions.mdc
Normal file
@@ -0,0 +1,200 @@
|
||||
---
|
||||
description: Vue 3 Composition API conventions and best practices
|
||||
alwaysApply: false
|
||||
globs: **/*.vue
|
||||
---
|
||||
|
||||
# Vue.js Conventions & Best Practices
|
||||
|
||||
## Component Architecture
|
||||
|
||||
### Use Composition API with `<script setup>`
|
||||
|
||||
```vue
|
||||
<script setup>
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
|
||||
// Props
|
||||
const props = defineProps({
|
||||
userId: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
})
|
||||
|
||||
// State
|
||||
const user = ref(null)
|
||||
const isLoading = ref(false)
|
||||
|
||||
// Computed
|
||||
const displayName = computed(() => {
|
||||
return user.value ? `${user.value.firstName} ${user.value.lastName}` : ''
|
||||
})
|
||||
|
||||
// Methods
|
||||
const fetchUser = async () => {
|
||||
isLoading.value = true
|
||||
try {
|
||||
// Fetch logic
|
||||
} finally {
|
||||
isLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// Lifecycle
|
||||
onMounted(() => {
|
||||
fetchUser()
|
||||
})
|
||||
</script>
|
||||
```
|
||||
|
||||
### Component Organization Order
|
||||
|
||||
1. **Imports** - External, then internal
|
||||
2. **Props** - TypeScript-style validation
|
||||
3. **Emits** - Explicitly defined
|
||||
4. **State** (refs and reactive)
|
||||
5. **Computed** - Derived values
|
||||
6. **Watchers** - Side effects
|
||||
7. **Methods** - Business logic
|
||||
8. **Lifecycle hooks** - Ordered by execution
|
||||
9. **Expose** - Public API (if needed)
|
||||
|
||||
## Props Best Practices
|
||||
|
||||
### Always Validate Props
|
||||
|
||||
```javascript
|
||||
defineProps({
|
||||
title: {
|
||||
type: String,
|
||||
required: true,
|
||||
validator: (value) => value.length > 0
|
||||
},
|
||||
status: {
|
||||
type: String,
|
||||
default: 'pending',
|
||||
validator: (value) => ['pending', 'active', 'complete'].includes(value)
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### Prop Naming
|
||||
- Use descriptive names: `isLoading` not `loading`
|
||||
- Boolean props: `is`, `has`, `can`, `should`
|
||||
- Avoid abbreviations: `projectData` not `projData`
|
||||
|
||||
## Reactive State
|
||||
|
||||
### When to Use ref vs reactive
|
||||
|
||||
**Use `ref` for:**
|
||||
- Primitives (string, number, boolean)
|
||||
- Single values
|
||||
- When you need `.value` explicit access
|
||||
|
||||
**Use `reactive` for:**
|
||||
- Objects with multiple properties
|
||||
- Complex nested data structures
|
||||
|
||||
## Event Handling
|
||||
|
||||
### Define Emits Explicitly
|
||||
|
||||
```javascript
|
||||
const emit = defineEmits(['update', 'delete', 'close'])
|
||||
|
||||
const handleUpdate = () => {
|
||||
emit('update', { id: 1, name: 'Updated' })
|
||||
}
|
||||
```
|
||||
|
||||
## Template Best Practices
|
||||
|
||||
### Keep Templates Clean
|
||||
|
||||
```vue
|
||||
<!-- Good - Logic in script -->
|
||||
<template>
|
||||
<div class="project-card" :class="cardClasses">
|
||||
<h3>{{ project.title }}</h3>
|
||||
<p v-if="hasDescription">{{ project.description }}</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const cardClasses = computed(() => ({
|
||||
'is-featured': project.featured,
|
||||
'is-complete': project.status === 'complete'
|
||||
}))
|
||||
|
||||
const hasDescription = computed(() => {
|
||||
return project.description && project.description.length > 0
|
||||
})
|
||||
</script>
|
||||
```
|
||||
|
||||
## Performance Optimization
|
||||
|
||||
### Lazy Loading Components
|
||||
|
||||
```javascript
|
||||
// Router lazy loading
|
||||
const ProjectDetail = () => import('@/pages/ProjectDetail.vue')
|
||||
|
||||
// Component lazy loading
|
||||
const HeavyChart = defineAsyncComponent(() =>
|
||||
import('@/components/HeavyChart.vue')
|
||||
)
|
||||
```
|
||||
|
||||
### Use `v-once` for Static Content
|
||||
|
||||
```vue
|
||||
<div v-once>
|
||||
<h1>{{ staticTitle }}</h1>
|
||||
</div>
|
||||
```
|
||||
|
||||
### Shallow Reactive for Large Objects
|
||||
|
||||
```javascript
|
||||
import { shallowRef } from 'vue'
|
||||
const projects = shallowRef([...largeArray])
|
||||
```
|
||||
|
||||
## Common Pitfalls to Avoid
|
||||
|
||||
### ❌ Mutating Props
|
||||
Never mutate props directly - emit events instead
|
||||
|
||||
### ❌ Forgetting .value in script
|
||||
```javascript
|
||||
const count = ref(0)
|
||||
if (count.value === 0) { } // ✅ Correct
|
||||
```
|
||||
|
||||
### ❌ Creating Objects in Template
|
||||
```vue
|
||||
<!-- Bad - Creates new object every render -->
|
||||
<Child :config="{ theme: 'dark' }" />
|
||||
|
||||
<!-- Good - Stable reference -->
|
||||
<script setup>
|
||||
const config = { theme: 'dark' }
|
||||
</script>
|
||||
<Child :config="config" />
|
||||
```
|
||||
|
||||
## Summary Checklist
|
||||
|
||||
- ✅ Use Composition API with `<script setup>`
|
||||
- ✅ Validate all props with types and defaults
|
||||
- ✅ Define emits explicitly
|
||||
- ✅ Keep templates clean - move logic to script
|
||||
- ✅ Use computed for derived state
|
||||
- ✅ Always provide `:key` for lists
|
||||
- ✅ Handle errors gracefully
|
||||
- ✅ Lazy load heavy components
|
||||
- ✅ Never mutate props
|
||||
- ✅ Remember `.value` for refs in script
|
||||
Reference in New Issue
Block a user