fix padding
This commit is contained in:
+689
@@ -0,0 +1,689 @@
|
||||
# UI Design Improvement Guide for YvCode
|
||||
|
||||
> A comprehensive analysis and recommendations to modernize and enhance the user interface of YvCode - a lightweight code canvas for social media images.
|
||||
|
||||
---
|
||||
|
||||
## 📊 Current State Analysis
|
||||
|
||||
### Strengths
|
||||
- ✅ Clean dark theme foundation with good contrast
|
||||
- ✅ Modern glassmorphism effects on the toolbar
|
||||
- ✅ Well-organized component structure (TopBar, Layers, Canvas, Inspector)
|
||||
- ✅ Good use of subtle borders (`border-white/5`) and backgrounds (`bg-white/5`)
|
||||
- ✅ Proper iconography with consistent stroke-based SVGs
|
||||
- ✅ Keyboard shortcuts implemented throughout
|
||||
|
||||
### Areas for Improvement
|
||||
- ⚠️ Limited visual hierarchy and depth
|
||||
- ⚠️ Inconsistent spacing and component sizing
|
||||
- ⚠️ Missing micro-interactions and animations
|
||||
- ⚠️ Outdated form control styling
|
||||
- ⚠️ No onboarding or empty states
|
||||
- ⚠️ Missing visual feedback for user actions
|
||||
|
||||
---
|
||||
|
||||
## 🎨 Design System Recommendations
|
||||
|
||||
### 1. Color Palette Enhancement
|
||||
|
||||
**Current:** Basic neutral grays with blue accents
|
||||
|
||||
**Recommended:** Expanded semantic color palette
|
||||
|
||||
```css
|
||||
/* Primary Colors */
|
||||
--primary-50: #eff6ff;
|
||||
--primary-100: #dbeafe;
|
||||
--primary-400: #60a5fa;
|
||||
--primary-500: #3b82f6;
|
||||
--primary-600: #2563eb;
|
||||
|
||||
/* Surface Colors (for layered depth) */
|
||||
--surface-0: #09090b; /* Deepest - sidebars */
|
||||
--surface-1: #0f0f12; /* Base canvas area */
|
||||
--surface-2: #18181b; /* Elevated cards */
|
||||
--surface-3: #27272a; /* Interactive elements */
|
||||
|
||||
/* Semantic Colors */
|
||||
--success: #22c55e;
|
||||
--warning: #f59e0b;
|
||||
--error: #ef4444;
|
||||
--info: #06b6d4;
|
||||
|
||||
/* Glass Effects */
|
||||
--glass-bg: rgba(255, 255, 255, 0.03);
|
||||
--glass-border: rgba(255, 255, 255, 0.06);
|
||||
--glass-highlight: rgba(255, 255, 255, 0.08);
|
||||
```
|
||||
|
||||
### 2. Typography Scale
|
||||
|
||||
**Recommended Font System:**
|
||||
|
||||
```css
|
||||
/* Headings */
|
||||
--text-2xl: 1.5rem; /* Panel titles */
|
||||
--text-xl: 1.25rem; /* Section headers */
|
||||
--text-lg: 1.125rem; /* Subheadings */
|
||||
|
||||
/* Body */
|
||||
--text-base: 0.875rem; /* Default UI text */
|
||||
--text-sm: 0.8125rem; /* Secondary text */
|
||||
--text-xs: 0.75rem; /* Labels, hints */
|
||||
|
||||
/* Line Heights */
|
||||
--leading-tight: 1.25;
|
||||
--leading-normal: 1.5;
|
||||
--leading-relaxed: 1.75;
|
||||
```
|
||||
|
||||
### 3. Spacing System
|
||||
|
||||
Adopt an 8px grid system for consistent spacing:
|
||||
|
||||
```css
|
||||
--space-1: 4px;
|
||||
--space-2: 8px;
|
||||
--space-3: 12px;
|
||||
--space-4: 16px;
|
||||
--space-5: 20px;
|
||||
--space-6: 24px;
|
||||
--space-8: 32px;
|
||||
--space-10: 40px;
|
||||
--space-12: 48px;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🧩 Component-Specific Improvements
|
||||
|
||||
### TopBar
|
||||
|
||||
**Current Issues:**
|
||||
- Fixed positioning with `h-16` may feel cramped
|
||||
- Logo and title area lacks breathing room
|
||||
- Export dropdown uses hover instead of click
|
||||
|
||||
**Recommendations:**
|
||||
|
||||
1. **Increase Height & Visual Weight**
|
||||
```tsx
|
||||
// Change from h-16 to h-14 with better internal spacing
|
||||
<div className="h-14 bg-surface-0/80 backdrop-blur-xl border-b border-white/[0.04]">
|
||||
```
|
||||
|
||||
2. **Add Breadcrumb Navigation**
|
||||
```tsx
|
||||
<div className="flex items-center gap-2 text-sm">
|
||||
<span className="text-neutral-500">Projects</span>
|
||||
<ChevronRight className="w-3 h-3 text-neutral-600" />
|
||||
<span className="text-white font-medium">{snap.meta.title}</span>
|
||||
</div>
|
||||
```
|
||||
|
||||
3. **Convert Export to Click-Triggered Dropdown with Animation**
|
||||
- Use Radix UI or Headless UI for accessible dropdowns
|
||||
- Add scale and fade animations on open/close
|
||||
|
||||
4. **Add Status Indicator**
|
||||
```tsx
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="w-1.5 h-1.5 rounded-full bg-green-500 animate-pulse" />
|
||||
<span className="text-xs text-neutral-500">Auto-saved</span>
|
||||
</div>
|
||||
```
|
||||
|
||||
### Toolbar (Floating)
|
||||
|
||||
**Current Issues:**
|
||||
- Good glassmorphism base but could be more refined
|
||||
- Tool icons lack visual distinction when inactive
|
||||
- Zoom controls feel disconnected
|
||||
|
||||
**Recommendations:**
|
||||
|
||||
1. **Enhanced Glass Effect**
|
||||
```tsx
|
||||
<div className="
|
||||
absolute bottom-6 left-1/2 -translate-x-1/2
|
||||
flex items-center gap-3 px-4 py-3
|
||||
rounded-2xl
|
||||
bg-gradient-to-b from-white/[0.08] to-white/[0.04]
|
||||
backdrop-blur-2xl
|
||||
border border-white/[0.08]
|
||||
shadow-[0_8px_32px_rgba(0,0,0,0.4),_inset_0_1px_0_rgba(255,255,255,0.1)]
|
||||
">
|
||||
```
|
||||
|
||||
2. **Animated Tool Selection**
|
||||
```tsx
|
||||
// Add spring animation for tool selection indicator
|
||||
<motion.div
|
||||
layoutId="tool-indicator"
|
||||
className="absolute inset-0 bg-primary-500 rounded-xl"
|
||||
transition={{ type: "spring", stiffness: 500, damping: 30 }}
|
||||
/>
|
||||
```
|
||||
|
||||
3. **Tooltips with Shortcuts**
|
||||
- Add tooltips that show on hover with keyboard shortcut badges
|
||||
- Use consistent tooltip styling across the app
|
||||
|
||||
4. **Group Visual Separators**
|
||||
- Use subtle vertical dividers with gradient fade
|
||||
|
||||
### Layers Panel
|
||||
|
||||
**Current Issues:**
|
||||
- Minimal visual hierarchy
|
||||
- Layer items lack drag handles or reordering indication
|
||||
- Empty state is too plain
|
||||
|
||||
**Recommendations:**
|
||||
|
||||
1. **Add Drag & Drop Reordering**
|
||||
- Use `@dnd-kit/sortable` for accessible drag-and-drop
|
||||
- Add ghost element preview during drag
|
||||
- Show insertion indicator line
|
||||
|
||||
2. **Enhanced Layer Item Design**
|
||||
```tsx
|
||||
<div className="
|
||||
group relative
|
||||
flex items-center gap-3 px-3 py-2.5
|
||||
rounded-lg
|
||||
bg-gradient-to-r from-transparent to-transparent
|
||||
hover:from-white/[0.03] hover:to-transparent
|
||||
border border-transparent
|
||||
hover:border-white/[0.06]
|
||||
cursor-pointer
|
||||
transition-all duration-200
|
||||
">
|
||||
{/* Drag handle - visible on hover */}
|
||||
<div className="
|
||||
opacity-0 group-hover:opacity-100
|
||||
cursor-grab active:cursor-grabbing
|
||||
text-neutral-600 hover:text-neutral-400
|
||||
">
|
||||
<GripVertical className="w-3 h-3" />
|
||||
</div>
|
||||
|
||||
{/* Layer thumbnail preview */}
|
||||
<div className="w-8 h-8 rounded bg-white/5 flex items-center justify-center">
|
||||
{/* Mini preview of element */}
|
||||
</div>
|
||||
|
||||
{/* Content */}
|
||||
<div className="flex-1 min-w-0">
|
||||
<span className="text-sm text-neutral-200 truncate block">
|
||||
{element.name}
|
||||
</span>
|
||||
<span className="text-xs text-neutral-500">
|
||||
{element.type}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
3. **Beautiful Empty State**
|
||||
```tsx
|
||||
<div className="flex flex-col items-center justify-center py-12 px-4">
|
||||
<div className="w-16 h-16 rounded-2xl bg-gradient-to-br from-white/[0.06] to-white/[0.02] flex items-center justify-center mb-4">
|
||||
<Layers className="w-8 h-8 text-neutral-600" />
|
||||
</div>
|
||||
<h4 className="text-sm font-medium text-neutral-400 mb-1">No layers yet</h4>
|
||||
<p className="text-xs text-neutral-600 text-center">
|
||||
Click on the canvas or use<br />
|
||||
<kbd className="px-1.5 py-0.5 rounded bg-white/5 text-neutral-400">C</kbd>
|
||||
<kbd className="px-1.5 py-0.5 rounded bg-white/5 text-neutral-400 mx-1">T</kbd>
|
||||
<kbd className="px-1.5 py-0.5 rounded bg-white/5 text-neutral-400">A</kbd>
|
||||
to add elements
|
||||
</p>
|
||||
</div>
|
||||
```
|
||||
|
||||
### Inspector Panel
|
||||
|
||||
**Current Issues:**
|
||||
- Dense information without clear sections
|
||||
- Form controls need modernization
|
||||
- No collapsible sections
|
||||
|
||||
**Recommendations:**
|
||||
|
||||
1. **Collapsible Accordion Sections**
|
||||
```tsx
|
||||
<Accordion type="multiple" defaultValue={['appearance', 'style']}>
|
||||
<AccordionItem value="appearance">
|
||||
<AccordionTrigger className="
|
||||
flex items-center justify-between w-full py-3
|
||||
text-xs font-semibold uppercase tracking-wider text-neutral-500
|
||||
hover:text-neutral-300
|
||||
">
|
||||
Appearance
|
||||
<ChevronDown className="w-4 h-4 transition-transform duration-200" />
|
||||
</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
{/* Content */}
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
</Accordion>
|
||||
```
|
||||
|
||||
2. **Modern Input Fields**
|
||||
```tsx
|
||||
// Enhanced number input with stepper
|
||||
<div className="
|
||||
flex items-center
|
||||
bg-white/[0.03] hover:bg-white/[0.05]
|
||||
border border-white/[0.06] hover:border-white/[0.1]
|
||||
rounded-lg
|
||||
overflow-hidden
|
||||
focus-within:ring-2 focus-within:ring-primary-500/30 focus-within:border-primary-500/50
|
||||
transition-all
|
||||
">
|
||||
<input
|
||||
type="number"
|
||||
className="flex-1 bg-transparent px-3 py-2 text-sm text-white outline-none"
|
||||
/>
|
||||
<div className="flex flex-col border-l border-white/[0.06]">
|
||||
<button className="px-2 py-1 hover:bg-white/[0.05]">
|
||||
<ChevronUp className="w-3 h-3" />
|
||||
</button>
|
||||
<button className="px-2 py-1 hover:bg-white/[0.05] border-t border-white/[0.06]">
|
||||
<ChevronDown className="w-3 h-3" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
3. **Enhanced Color Picker**
|
||||
```tsx
|
||||
// Modern color picker with swatches and opacity
|
||||
<div className="space-y-3">
|
||||
<div className="flex gap-2">
|
||||
<div className="
|
||||
relative w-10 h-10 rounded-lg overflow-hidden
|
||||
border-2 border-white/10
|
||||
shadow-lg shadow-black/20
|
||||
">
|
||||
{/* Checkerboard background for transparency */}
|
||||
<div className="absolute inset-0 bg-checkerboard" />
|
||||
<input type="color" className="absolute inset-0 w-full h-full cursor-pointer opacity-0" />
|
||||
<div className="absolute inset-0" style={{ backgroundColor: color }} />
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<input
|
||||
type="text"
|
||||
value={color}
|
||||
className="w-full bg-white/[0.03] border border-white/[0.06] rounded-lg px-3 py-2 text-sm font-mono"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Recent colors */}
|
||||
<div className="flex gap-1.5">
|
||||
{recentColors.map(c => (
|
||||
<button
|
||||
key={c}
|
||||
className="w-6 h-6 rounded-md border border-white/10 hover:scale-110 transition-transform"
|
||||
style={{ backgroundColor: c }}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
4. **Slider with Value Display**
|
||||
```tsx
|
||||
<div className="space-y-2">
|
||||
<div className="flex justify-between">
|
||||
<label className="text-xs font-medium text-neutral-500">Border Radius</label>
|
||||
<span className="text-xs text-neutral-400 tabular-nums">{value}px</span>
|
||||
</div>
|
||||
<input
|
||||
type="range"
|
||||
className="
|
||||
w-full h-1.5 rounded-full
|
||||
bg-white/[0.06]
|
||||
appearance-none
|
||||
[&::-webkit-slider-thumb]:appearance-none
|
||||
[&::-webkit-slider-thumb]:w-4
|
||||
[&::-webkit-slider-thumb]:h-4
|
||||
[&::-webkit-slider-thumb]:rounded-full
|
||||
[&::-webkit-slider-thumb]:bg-white
|
||||
[&::-webkit-slider-thumb]:shadow-lg
|
||||
[&::-webkit-slider-thumb]:cursor-pointer
|
||||
[&::-webkit-slider-thumb]:hover:scale-110
|
||||
[&::-webkit-slider-thumb]:transition-transform
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
```
|
||||
|
||||
### Canvas Area
|
||||
|
||||
**Current Issues:**
|
||||
- Basic checkered or solid background
|
||||
- No visual indicators for safe zones
|
||||
- Selection handles could be more refined
|
||||
|
||||
**Recommendations:**
|
||||
|
||||
1. **Subtle Canvas Background Pattern**
|
||||
```tsx
|
||||
// Add subtle dot grid pattern to canvas workspace
|
||||
<div className="
|
||||
absolute inset-0
|
||||
bg-[radial-gradient(circle_at_1px_1px,_rgba(255,255,255,0.03)_1px,_transparent_1px)]
|
||||
bg-[size:24px_24px]
|
||||
" />
|
||||
```
|
||||
|
||||
2. **Enhanced Selection Handles**
|
||||
```tsx
|
||||
// Modern corner handles
|
||||
<div className="
|
||||
absolute w-3 h-3
|
||||
bg-white
|
||||
rounded-full
|
||||
border-2 border-primary-500
|
||||
shadow-lg shadow-primary-500/30
|
||||
cursor-nwse-resize
|
||||
hover:scale-125
|
||||
transition-transform
|
||||
" />
|
||||
```
|
||||
|
||||
3. **Canvas Info Badge**
|
||||
```tsx
|
||||
// Show canvas size in corner
|
||||
<div className="
|
||||
absolute bottom-4 right-4
|
||||
px-2 py-1
|
||||
bg-black/50 backdrop-blur-sm
|
||||
rounded-md
|
||||
text-xs text-neutral-400
|
||||
font-mono
|
||||
">
|
||||
{width} × {height}
|
||||
</div>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ✨ Micro-Interactions & Animations
|
||||
|
||||
### 1. Button Press Effects
|
||||
|
||||
```tsx
|
||||
// Add satisfying press feedback
|
||||
<button className="
|
||||
transform
|
||||
active:scale-95
|
||||
transition-transform duration-75
|
||||
">
|
||||
```
|
||||
|
||||
### 2. Panel Transitions
|
||||
|
||||
```tsx
|
||||
// Smooth panel content transitions
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 10 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
exit={{ opacity: 0, y: -10 }}
|
||||
transition={{ duration: 0.15 }}
|
||||
>
|
||||
```
|
||||
|
||||
### 3. Loading States
|
||||
|
||||
```tsx
|
||||
// Skeleton loading for async operations
|
||||
<div className="
|
||||
animate-pulse
|
||||
bg-gradient-to-r from-white/[0.03] via-white/[0.06] to-white/[0.03]
|
||||
bg-[length:200%_100%]
|
||||
animate-shimmer
|
||||
" />
|
||||
```
|
||||
|
||||
### 4. Success Feedback
|
||||
|
||||
```tsx
|
||||
// Toast notification for exports
|
||||
<div className="
|
||||
fixed bottom-6 right-6
|
||||
flex items-center gap-3
|
||||
px-4 py-3
|
||||
bg-green-500/10 backdrop-blur-xl
|
||||
border border-green-500/20
|
||||
rounded-xl
|
||||
shadow-2xl
|
||||
">
|
||||
<CheckCircle className="w-5 h-5 text-green-400" />
|
||||
<span className="text-sm text-green-200">Image exported successfully!</span>
|
||||
</div>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Feature Enhancements
|
||||
|
||||
### 1. Onboarding Experience
|
||||
|
||||
**First-time user tooltip tour:**
|
||||
- Highlight each major area with spotlight effect
|
||||
- Short, contextual tips
|
||||
- Skip option
|
||||
|
||||
```tsx
|
||||
<div className="
|
||||
absolute inset-0
|
||||
bg-black/60 backdrop-blur-sm
|
||||
z-50
|
||||
">
|
||||
{/* Spotlight cutout */}
|
||||
<div className="
|
||||
absolute
|
||||
w-64 h-16
|
||||
bg-transparent
|
||||
rounded-2xl
|
||||
ring-4 ring-primary-500
|
||||
shadow-[0_0_0_9999px_rgba(0,0,0,0.6)]
|
||||
" style={{ top: 0, left: 100 }}>
|
||||
{/* Tooltip */}
|
||||
<div className="absolute top-full mt-4 left-0 w-64 p-4 bg-surface-2 rounded-xl">
|
||||
<h4 className="font-semibold text-white mb-1">Create & Export</h4>
|
||||
<p className="text-sm text-neutral-400">Start a new project or export your creation in multiple formats.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
### 2. Command Palette (⌘K)
|
||||
|
||||
Add a spotlight-style command palette for power users:
|
||||
|
||||
```tsx
|
||||
<div className="
|
||||
fixed inset-0
|
||||
flex items-start justify-center
|
||||
pt-[20vh]
|
||||
bg-black/50 backdrop-blur-sm
|
||||
z-50
|
||||
">
|
||||
<div className="
|
||||
w-full max-w-lg
|
||||
bg-surface-1
|
||||
border border-white/10
|
||||
rounded-2xl
|
||||
shadow-2xl
|
||||
overflow-hidden
|
||||
">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Type a command or search..."
|
||||
className="w-full px-5 py-4 bg-transparent text-white placeholder-neutral-500 outline-none"
|
||||
/>
|
||||
<div className="border-t border-white/5 max-h-80 overflow-auto">
|
||||
{/* Command results */}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
### 3. Quick Actions Contextual Menu
|
||||
|
||||
Right-click context menus with modern styling:
|
||||
|
||||
```tsx
|
||||
<div className="
|
||||
min-w-[200px]
|
||||
bg-surface-2/95 backdrop-blur-xl
|
||||
border border-white/10
|
||||
rounded-xl
|
||||
shadow-2xl
|
||||
py-1
|
||||
overflow-hidden
|
||||
">
|
||||
<button className="
|
||||
w-full px-3 py-2
|
||||
flex items-center gap-3
|
||||
text-sm text-neutral-300
|
||||
hover:bg-white/5 hover:text-white
|
||||
transition-colors
|
||||
">
|
||||
<Copy className="w-4 h-4" />
|
||||
Duplicate
|
||||
<span className="ml-auto text-xs text-neutral-500">⌘D</span>
|
||||
</button>
|
||||
</div>
|
||||
```
|
||||
|
||||
### 4. Preview Mode
|
||||
|
||||
Full-screen distraction-free preview:
|
||||
|
||||
```tsx
|
||||
<div className="
|
||||
fixed inset-0
|
||||
bg-black
|
||||
flex items-center justify-center
|
||||
z-50
|
||||
">
|
||||
<button className="
|
||||
absolute top-6 right-6
|
||||
p-2
|
||||
rounded-full
|
||||
bg-white/10 hover:bg-white/20
|
||||
text-white
|
||||
transition-colors
|
||||
">
|
||||
<X className="w-5 h-5" />
|
||||
</button>
|
||||
|
||||
{/* Canvas preview at actual size */}
|
||||
<div className="shadow-2xl rounded-lg overflow-hidden">
|
||||
{/* Rendered canvas */}
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📱 Responsive Considerations
|
||||
|
||||
### Breakpoint Strategy
|
||||
|
||||
```css
|
||||
/* Collapse side panels to bottom sheets on tablet */
|
||||
@media (max-width: 1024px) {
|
||||
.layers-panel,
|
||||
.inspector-panel {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 40vh;
|
||||
border-radius: 24px 24px 0 0;
|
||||
transform: translateY(calc(100% - 48px));
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
.panel-open {
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Hide panels completely on mobile - show as modals */
|
||||
@media (max-width: 768px) {
|
||||
.side-panels {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Implementation Priority
|
||||
|
||||
### Phase 1: Quick Wins (1-2 days)
|
||||
1. ✅ Update color palette and CSS variables
|
||||
2. ✅ Enhance button/input hover states
|
||||
3. ✅ Add micro-animations (scale, transitions)
|
||||
4. ✅ Improve empty states
|
||||
|
||||
### Phase 2: Component Polish (3-5 days)
|
||||
1. 🔲 Redesign Inspector panel with collapsible sections
|
||||
2. 🔲 Enhance Layers panel with drag-and-drop
|
||||
3. 🔲 Modernize all form controls
|
||||
4. 🔲 Add toast notifications
|
||||
|
||||
### Phase 3: Advanced Features (1-2 weeks)
|
||||
1. 🔲 Command palette (⌘K)
|
||||
2. 🔲 Onboarding tour
|
||||
3. 🔲 Context menus
|
||||
4. 🔲 Preview mode
|
||||
5. 🔲 Responsive adaptations
|
||||
|
||||
---
|
||||
|
||||
## 🛠️ Recommended Libraries
|
||||
|
||||
| Purpose | Library | Why |
|
||||
|---------|---------|-----|
|
||||
| Animations | `framer-motion` | Smooth, spring-based animations |
|
||||
| UI Primitives | `@radix-ui/react-*` | Accessible, unstyled components |
|
||||
| Drag & Drop | `@dnd-kit/core` | Modern, accessible DnD |
|
||||
| Icons | `lucide-react` | Consistent, customizable icons |
|
||||
| Tooltips | `@radix-ui/react-tooltip` | Accessible tooltips |
|
||||
| Toasts | `sonner` | Beautiful toast notifications |
|
||||
| Command Palette | `cmdk` | ⌘K style command menu |
|
||||
|
||||
---
|
||||
|
||||
## 📐 Visual Reference
|
||||
|
||||
### Inspiration Sources
|
||||
- [Figma](https://figma.com) - Panel organization, inspector design
|
||||
- [Linear](https://linear.app) - Command palette, animations
|
||||
- [Raycast](https://raycast.com) - Glass effects, dark theme
|
||||
- [Arc Browser](https://arc.net) - Sidebar design, animations
|
||||
- [Vercel Dashboard](https://vercel.com) - Cards, buttons, clean typography
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
The current YvCode UI has a solid foundation with its dark theme and component organization. The key improvements focus on:
|
||||
|
||||
1. **Visual Polish** - Enhanced glass effects, refined colors, better shadows
|
||||
2. **Interaction Design** - Micro-animations, better feedback, modern controls
|
||||
3. **Information Architecture** - Collapsible sections, better empty states, contextual help
|
||||
4. **Power User Features** - Command palette, keyboard shortcuts display, context menus
|
||||
|
||||
Implementing these changes will transform YvCode into a professional-grade design tool that feels modern, responsive, and delightful to use.
|
||||
+98
-37
@@ -11,7 +11,9 @@ interface TopBarProps {
|
||||
|
||||
const TopBar: React.FC<TopBarProps> = ({ stageRef }) => {
|
||||
const [showRecentSnaps, setShowRecentSnaps] = useState(false);
|
||||
const [showExportMenu, setShowExportMenu] = useState(false);
|
||||
const recentButtonRef = useRef<HTMLButtonElement>(null);
|
||||
const exportButtonRef = useRef<HTMLButtonElement>(null);
|
||||
|
||||
const {
|
||||
snap,
|
||||
@@ -76,7 +78,9 @@ const TopBar: React.FC<TopBarProps> = ({ stageRef }) => {
|
||||
link.download = `${snap.meta.title || 'canvas'}.${format}`;
|
||||
link.href = dataUrl;
|
||||
link.click();
|
||||
}, [stageRef, snap.meta]);
|
||||
setShowExportMenu(false);
|
||||
addRecentSnap(snap);
|
||||
}, [stageRef, snap, addRecentSnap]);
|
||||
|
||||
const handleExportJSON = useCallback(() => {
|
||||
const json = exportSnap();
|
||||
@@ -88,6 +92,7 @@ const TopBar: React.FC<TopBarProps> = ({ stageRef }) => {
|
||||
link.click();
|
||||
URL.revokeObjectURL(url);
|
||||
|
||||
setShowExportMenu(false);
|
||||
// Save to recent snaps when exporting
|
||||
addRecentSnap(snap);
|
||||
}, [exportSnap, snap, addRecentSnap]);
|
||||
@@ -120,24 +125,33 @@ const TopBar: React.FC<TopBarProps> = ({ stageRef }) => {
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="h-16 bg-[#09090b]/80 backdrop-blur-md border-b border-white/5 flex items-center justify-between px-6 z-40 fixed top-0 w-full">
|
||||
<>
|
||||
{/* Overlay for closing export menu */}
|
||||
{showExportMenu && (
|
||||
<div
|
||||
className="fixed inset-0 z-40 bg-transparent"
|
||||
onClick={() => setShowExportMenu(false)}
|
||||
/>
|
||||
)}
|
||||
|
||||
<div className="h-14 bg-[#09090b] border-b border-white/[0.08] flex items-center justify-between px-5 z-40 relative select-none">
|
||||
{/* Left section: Logo & Actions */}
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="flex items-center gap-2 pr-4 border-r border-white/5">
|
||||
<div className="w-8 h-8 rounded-xl bg-gradient-to-br from-blue-600 to-blue-700 shadow-lg shadow-blue-900/20 flex items-center justify-center">
|
||||
<div className="flex items-center gap-5">
|
||||
<div className="flex items-center gap-3 pr-5 border-r border-white/[0.08]">
|
||||
<div className="w-8 h-8 rounded-lg bg-gradient-to-br from-blue-600 to-blue-700 shadow-lg shadow-blue-500/20 flex items-center justify-center">
|
||||
<svg className="w-5 h-5 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2.5} d="M13 10V3L4 14h7v7l9-11h-7z" />
|
||||
</svg>
|
||||
</div>
|
||||
<span className="font-bold text-lg tracking-tight bg-clip-text text-transparent bg-gradient-to-r from-white to-white/60">
|
||||
<span className="font-bold text-base tracking-tight text-white">
|
||||
YvCode
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-1">
|
||||
<div className="flex items-center gap-1.5">
|
||||
<button
|
||||
onClick={handleNewSnap}
|
||||
className="p-2 rounded-lg text-neutral-400 hover:text-white hover:bg-white/5 transition-all active:scale-95"
|
||||
className="w-8 h-8 flex items-center justify-center rounded-lg text-neutral-400 hover:text-white hover:bg-white/5 transition-all"
|
||||
title="New (⌘N)"
|
||||
>
|
||||
<svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
@@ -146,7 +160,7 @@ const TopBar: React.FC<TopBarProps> = ({ stageRef }) => {
|
||||
</button>
|
||||
<button
|
||||
onClick={handleImportJSON}
|
||||
className="p-2 rounded-lg text-neutral-400 hover:text-white hover:bg-white/5 transition-all active:scale-95"
|
||||
className="w-8 h-8 flex items-center justify-center rounded-lg text-neutral-400 hover:text-white hover:bg-white/5 transition-all"
|
||||
title="Open File (⌘O)"
|
||||
>
|
||||
<svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
@@ -154,12 +168,11 @@ const TopBar: React.FC<TopBarProps> = ({ stageRef }) => {
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
{/* Recent Snaps Button */}
|
||||
<div className="relative">
|
||||
<button
|
||||
ref={recentButtonRef}
|
||||
onClick={toggleRecentSnaps}
|
||||
className={`p-2 rounded-lg transition-all active:scale-95 ${
|
||||
className={`w-8 h-8 flex items-center justify-center rounded-lg transition-all ${
|
||||
showRecentSnaps
|
||||
? 'text-white bg-white/10'
|
||||
: 'text-neutral-400 hover:text-white hover:bg-white/5'
|
||||
@@ -180,21 +193,20 @@ const TopBar: React.FC<TopBarProps> = ({ stageRef }) => {
|
||||
</div>
|
||||
|
||||
{/* Center section: Title & Tools */}
|
||||
<div className="absolute left-1/2 -translate-x-1/2 flex items-center">
|
||||
<div className="flex flex-col items-center">
|
||||
<div className="absolute left-1/2 -translate-x-1/2 flex items-center gap-4">
|
||||
<input
|
||||
type="text"
|
||||
value={snap.meta.title}
|
||||
onChange={(e) => updateMeta({ title: e.target.value })}
|
||||
className="bg-transparent text-sm font-medium text-center text-neutral-200 focus:text-white px-2 py-1 outline-none rounded hover:bg-white/5 focus:bg-white/10 transition-colors placeholder-neutral-600 w-48"
|
||||
className="bg-transparent text-sm font-medium text-center text-neutral-200 focus:text-white px-3 py-1.5 outline-none rounded-md hover:bg-white/5 focus:bg-white/10 transition-colors placeholder-neutral-600 w-48 border border-transparent focus:border-white/10"
|
||||
placeholder="Untitled Project"
|
||||
/>
|
||||
<div className="flex items-center gap-2 mt-0.5">
|
||||
<span className="w-1.5 h-1.5 rounded-full bg-neutral-600" />
|
||||
<div className="h-4 w-px bg-white/10" />
|
||||
<div className="relative group/aspect">
|
||||
<select
|
||||
value={snap.meta.aspect}
|
||||
onChange={handleAspectChange}
|
||||
className="bg-transparent text-[10px] items-center uppercase tracking-wider font-semibold text-neutral-500 hover:text-neutral-300 outline-none cursor-pointer appearance-none text-center"
|
||||
className="bg-transparent text-xs font-medium text-neutral-400 hover:text-white px-2 py-1 outline-none cursor-pointer appearance-none text-center transition-colors"
|
||||
>
|
||||
{ASPECT_RATIOS.map((ratio) => (
|
||||
<option key={ratio.name} value={ratio.name}>
|
||||
@@ -204,28 +216,27 @@ const TopBar: React.FC<TopBarProps> = ({ stageRef }) => {
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Right section: History & Export */}
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="flex items-center gap-0.5 p-1 bg-white/5 rounded-lg border border-white/5">
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="flex items-center gap-1">
|
||||
<button
|
||||
onClick={undo}
|
||||
disabled={history.past.length === 0}
|
||||
className="p-1.5 rounded text-neutral-400 hover:text-white hover:bg-white/10 transition-colors disabled:opacity-30 disabled:hover:bg-transparent"
|
||||
className="w-8 h-8 flex items-center justify-center rounded-lg text-neutral-400 hover:text-white hover:bg-white/10 transition-all disabled:opacity-30 disabled:hover:bg-transparent active:scale-95"
|
||||
title="Undo (⌘Z)"
|
||||
>
|
||||
<svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 10h10a8 8 0 018 8v2M3 10l6 6m-6-6l6-6" />
|
||||
</svg>
|
||||
</button>
|
||||
<button
|
||||
onClick={redo}
|
||||
disabled={history.future.length === 0}
|
||||
className="p-1.5 rounded text-neutral-400 hover:text-white hover:bg-white/10 transition-colors disabled:opacity-30 disabled:hover:bg-transparent"
|
||||
className="w-8 h-8 flex items-center justify-center rounded-lg text-neutral-400 hover:text-white hover:bg-white/10 transition-all disabled:opacity-30 disabled:hover:bg-transparent active:scale-95"
|
||||
title="Redo (⇧⌘Z)"
|
||||
>
|
||||
<svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M21 10h-10a8 8 0 00-8 8v2M21 10l-6 6m6-6l-6-6" />
|
||||
</svg>
|
||||
</button>
|
||||
@@ -233,42 +244,92 @@ const TopBar: React.FC<TopBarProps> = ({ stageRef }) => {
|
||||
|
||||
<div className="h-6 w-px bg-white/10" />
|
||||
|
||||
<div className="relative group">
|
||||
<div className="relative z-50">
|
||||
<button
|
||||
className="flex items-center gap-2 px-4 py-2 bg-white text-black hover:bg-blue-50 text-sm font-semibold rounded-lg shadow-lg shadow-white/5 transition-all active:scale-95"
|
||||
ref={exportButtonRef}
|
||||
onClick={() => setShowExportMenu(!showExportMenu)}
|
||||
className={`flex items-center gap-3 px-4 py-2 text-sm font-medium rounded-lg transition-all duration-200 border group ${
|
||||
showExportMenu
|
||||
? 'bg-blue-600/10 text-blue-400 border-blue-500/50 shadow-[0_0_20px_rgba(37,99,235,0.3)]'
|
||||
: 'bg-white/5 text-neutral-300 hover:text-white hover:bg-white/10 border-white/5 hover:border-white/10'
|
||||
}`}
|
||||
>
|
||||
<span>Export</span>
|
||||
<svg className="w-4 h-4 text-neutral-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<div className={`p-0.5 rounded-md transition-all duration-200 ${showExportMenu ? 'bg-blue-500/20 text-blue-400' : 'bg-white/5 text-neutral-400 group-hover:text-white'}`}>
|
||||
<svg className={`w-3.5 h-3.5 transition-transform duration-200 ${showExportMenu ? 'rotate-180' : ''}`} fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
|
||||
</svg>
|
||||
</div>
|
||||
</button>
|
||||
|
||||
<div className="absolute right-0 top-full mt-2 w-48 bg-[#09090b] border border-white/10 rounded-xl shadow-2xl p-1 opacity-0 invisible group-hover:opacity-100 group-hover:visible transition-all z-50 transform origin-top-right">
|
||||
<div className="px-3 py-2 text-xs font-semibold text-neutral-500 uppercase tracking-wider">Format</div>
|
||||
{showExportMenu && (
|
||||
<div className="absolute right-0 top-full mt-3 w-72 bg-[#09090b]/95 backdrop-blur-2xl border border-white/10 rounded-xl shadow-2xl shadow-black/50 p-2 overflow-hidden animate-in fade-in zoom-in-95 duration-150 origin-top-right ring-1 ring-white/5 z-50">
|
||||
<div className="px-3 py-2">
|
||||
<span className="text-[10px] font-bold text-neutral-500 uppercase tracking-widest pl-1">Download Image</span>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 gap-1.5 px-1">
|
||||
<button
|
||||
onClick={() => handleExportImage('png', 2)}
|
||||
className="w-full text-left px-3 py-2 text-sm text-neutral-300 hover:text-white hover:bg-white/10 rounded-lg transition-colors flex justify-between group/item"
|
||||
className="w-full flex items-center justify-between px-4 py-3 rounded-xl bg-white/[0.03] hover:bg-white/[0.08] border border-white/[0.02] hover:border-white/10 transition-all group active:scale-[0.98]"
|
||||
>
|
||||
<span>PNG Image</span>
|
||||
<span className="bg-white/10 px-1.5 py-0.5 rounded text-[10px] text-neutral-400 group-hover/item:text-white">2x</span>
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="w-10 h-10 rounded-lg bg-gradient-to-br from-blue-500/10 to-blue-600/10 border border-blue-500/20 text-blue-400 flex items-center justify-center group-hover:scale-110 transition-transform duration-300">
|
||||
<span className="font-bold text-[10px]">PNG</span>
|
||||
</div>
|
||||
<div className="flex flex-col items-start gap-0.5">
|
||||
<span className="text-sm font-medium text-neutral-200 group-hover:text-white transition-colors">PNG Image</span>
|
||||
<span className="text-[10px] text-neutral-500">High quality (2x)</span>
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
|
||||
<button
|
||||
onClick={() => handleExportImage('jpeg', 2)}
|
||||
className="w-full text-left px-3 py-2 text-sm text-neutral-300 hover:text-white hover:bg-white/10 rounded-lg transition-colors"
|
||||
className="w-full flex items-center justify-between px-4 py-3 rounded-xl bg-white/[0.03] hover:bg-white/[0.08] border border-white/[0.02] hover:border-white/10 transition-all group active:scale-[0.98]"
|
||||
>
|
||||
JPEG Image
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="w-10 h-10 rounded-lg bg-gradient-to-br from-purple-500/10 to-purple-600/10 border border-purple-500/20 text-purple-400 flex items-center justify-center group-hover:scale-110 transition-transform duration-300">
|
||||
<span className="font-bold text-[10px]">JPG</span>
|
||||
</div>
|
||||
<div className="flex flex-col items-start gap-0.5">
|
||||
<span className="text-sm font-medium text-neutral-200 group-hover:text-white transition-colors">JPEG Image</span>
|
||||
<span className="text-[10px] text-neutral-500">Standard quality</span>
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
<div className="h-px bg-white/5 my-1" />
|
||||
</div>
|
||||
|
||||
<div className="h-px bg-gradient-to-r from-transparent via-white/10 to-transparent my-2" />
|
||||
|
||||
<div className="px-3 py-1.5">
|
||||
<span className="text-[10px] font-bold text-neutral-500 uppercase tracking-widest pl-1">Project File</span>
|
||||
</div>
|
||||
|
||||
<div className="px-1 pb-1">
|
||||
<button
|
||||
onClick={handleExportJSON}
|
||||
className="w-full text-left px-3 py-2 text-sm text-neutral-300 hover:text-white hover:bg-white/10 rounded-lg transition-colors"
|
||||
className="w-full flex items-center justify-between px-4 py-3 rounded-xl bg-white/[0.03] hover:bg-white/[0.08] border border-white/[0.02] hover:border-white/10 transition-all group active:scale-[0.98]"
|
||||
>
|
||||
Save Project JSON
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="w-10 h-10 rounded-lg bg-gradient-to-br from-yellow-500/10 to-yellow-600/10 border border-yellow-500/20 text-yellow-400 flex items-center justify-center group-hover:scale-110 transition-transform duration-300">
|
||||
<svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4" />
|
||||
</svg>
|
||||
</div>
|
||||
<div className="flex flex-col items-start gap-0.5">
|
||||
<span className="text-sm font-medium text-neutral-200 group-hover:text-white transition-colors">Save Project</span>
|
||||
<span className="text-[10px] text-neutral-500">Edit later (.json)</span>
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -3,12 +3,6 @@
|
||||
|
||||
@import "tailwindcss";
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html, body, #root {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
|
||||
Reference in New Issue
Block a user