feat(interface-design): add comprehensive interface design skill documentation and references

- Introduced SKILL.md outlining the principles and processes for effective interface design.
- Added critique.md for evaluating design outputs and ensuring craft over correctness.
- Created example.md to illustrate the application of subtle layering principles in design decisions.
- Developed principles.md detailing core craft principles for consistent design quality.
- Implemented validation.md for memory management and pattern reuse in the design system.
- Established system.md for the Playzer design system, defining direction, domain concepts, and design decisions.
- Added StatusBadge component for displaying booking statuses with appropriate styles and labels.
- Updated skills-lock.json to include the new interface-design skill.
This commit is contained in:
Jose Selesan
2026-04-24 11:23:45 -03:00
parent 7bfa3b390e
commit 503c29613b
13 changed files with 1200 additions and 32 deletions

186
.interface-design/system.md Normal file
View File

@@ -0,0 +1,186 @@
# Design System - Playzer
## Direction
**Feel:** Technical but warm. Like a well-organized sports facility — functional, clean, but with life. Not cold like a trading terminal, not playful like a toy app.
**Users:** Owners and employees managing sports complex bookings. They're juggling schedules, phone calls, and walk-ins. The interface should stay out of the way while they're working.
---
## Domain
**Concepts:**
- Turno (booking slot) — A scheduled time block
- Cancha (court) — Sports court/pitch
- Horario — Time slot
- Complex — The sports facility
- Estado — Booking status (confirmed, cancelled, etc.)
**Color world:**
- Court green (grass/tennis)
- Clay tan (paddle court)
- Pool blue (swimming)
- Wood grain (locker rooms)
- Concrete gray (tech floor)
**Signature:**
- Status pills use subtle borders + background tint, not bold badges
- Status communicates without demanding attention — whisper-level hierarchy
---
## Decisions
### Depth
**Approach:** Borders-only with surface elevation shifts
- No drop shadows
- Higher elevation = slightly lighter surface (in dark: lighter = higher)
- Subtle rgba borders define edges quietly
### Spacing
**Base unit:** 4px
- Micro: 2-4px (icon gaps)
- Component: 8-12px (inside buttons, badges)
- Section: 16-24px (between groups)
- Major: 32-40px (between distinct areas)
### Typography
**Font:** Geist Variable
**Scale:**
- Display: 28px / semibold / -0.02em tracking
- Heading: 20px / semibold / -0.01em tracking
- Body: 14px / normal / 0 tracking
- Label: 12px / medium / 0 tracking
- Caption: 11px / normal / 0.02em tracking
### Border Radius
**Scale:**
- `sm`: 4px (inputs, small badges)
- `md`: 6px (buttons, cards)
- `lg`: 10px (dialogs, large cards)
- `xl`: 14px (modals)
### Borders
**Progression:**
- Subtle: `oklch(1 0 0 / 8%)` (background edges)
- Default: `oklch(1 0 0 / 12%)` (card borders)
- Emphasis: `oklch(1 0 0 / 20%)` (active states)
### Status Semantic Colors
**Light mode:**
- Confirmed: `bg-emerald-50 border-emerald-200 text-emerald-700`
- Pending: `bg-amber-50 border-amber-200 text-amber-700`
- Cancelled: `bg-rose-50 border-rose-200 text-rose-700`
- No-show: `bg-orange-50 border-orange-200 text-orange-700`
- Completed: `bg-sky-50 border-sky-200 text-sky-700`
**Dark mode:**
- Slight desaturation, lower opacity backgrounds
---
## Patterns
### Card
- Background: `bg-card`
- Border: `border` token
- Radius: `md`
- Padding: `p-4` (compact) / `p-5` (default)
### Status Badge
```tsx
<span className="inline-flex items-center gap-1.5 rounded-full border px-2 py-0.5 text-xs font-medium">
{icon}
{label}
</span>
```
### Input
- Background: `bg-input` (darker than surface)
- No heavy borders by default
- Focus ring via `ring-3 ring-ring/50`
### Layout
- Sidebar: Same background as main, subtle border separation
- Content: Max-width 6xl, centered
- Padding: `px-4 sm:px-6`
---
## TODO
- [x] Status tokens in CSS variables
- [x] Refactor status badges to use semantic classes
- [x] StatusBadge component created
- [x] Home panel redesign with metrics + timeline
---
## Home Page Patterns
### Structure
- Header: Title + DatePicker chip + Action button (3-column)
- Metrics Grid: 4-column stats with semantic colors
- Next Booking: Prominent card with emerald accent
- Timeline: Grouped by date with "now" indicator
### DatePicker Chip
```tsx
<div className="flex items-center gap-1 rounded-md border bg-card px-2 py-1">
<Field>
<DatePicker value={...} onChange={...} placeholder="" />
</Field>
{isToday && (
<span className="text-xs font-medium text-emerald-600">Hoy</span>
)}
</div>
```
### Metrics Grid
```tsx
<div className="grid gap-3 sm:grid-cols-2 lg:grid-cols-4">
<div className="rounded-lg border bg-card p-3">
<p className="text-xs font-medium text-muted-foreground">Total</p>
<p className="mt-1 text-2xl font-semibold tracking-tight">{value}</p>
</div>
// ... confirmed (emerald), completed (sky), no-show (orange)
</div>
```
### Timeline Item
```tsx
<div className={[
'group flex items-center justify-between gap-3 rounded-md border p-2.5 transition-colors hover:bg-muted/50',
isPast && 'opacity-60',
isNow && 'border-l-2 border-l-emerald-500 bg-emerald-50/30',
].join(' ')}>
<div className="flex min-w-0 flex-1 items-center gap-3">
<div className="flex w-16 shrink-0 flex-col items-center">
<span className="text-sm font-medium tabular-nums">{startTime}</span>
<span className="text-xs text-muted-foreground">{endTime}</span>
</div>
<ChevronRight className="size-4 shrink-0 text-muted-foreground/30" />
<div className="min-w-0 flex-1">
<p className="truncate text-sm font-medium">{courtName}</p>
<p className="truncate text-xs text-muted-foreground">{customer}</p>
</div>
</div>
<div className="flex shrink-0 items-center gap-2">
<StatusBadge status={...} />
<div className="flex gap-1 opacity-0 transition-opacity group-hover:opacity-100">
<Button size="icon-xs" variant="ghost">...</Button>
</div>
</div>
</div>
```
### Actions Hover Pattern
```tsx
<div className="flex gap-1 opacity-0 transition-opacity group-hover:opacity-100">
<Button size="icon-xs" variant="ghost" title="Marcar cumplida">
<Check className="size-3.5" />
</Button>
<Button size="icon-xs" variant="ghost" title="Cancelar">×</Button>
</div>
```