Files
playzer/.interface-design/system.md

211 lines
6.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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 (v2)
### Structure
- Header + URL Card: 2-column grid (lg:)
- Main: Timeline de reservas
- Sidebar: Stats grid consolidado (sticky)
### URL Card (destacada)
```tsx
<div className="group relative overflow-hidden rounded-2xl border border-blue-200/60 bg-blue-50/50 p-5">
<div className="absolute inset-0 bg-gradient-to-br from-blue-50/80 to-blue-100/30" />
<div className="relative flex items-center justify-between gap-4">
<div className="min-w-0 flex-1">
<p className="text-[11px] font-semibold uppercase tracking-[0.28em] text-blue-600/70">
Reserva online
</p>
<a href={url} className="mt-1 block truncate text-sm font-medium text-blue-700 underline-offset-2 hover:underline">
{url}
</a>
</div>
<button className="relative flex shrink-0 items-center gap-2 rounded-lg bg-blue-500/10 px-3 py-2 text-sm font-medium text-blue-700 transition-colors hover:bg-blue-500/20">
<Copy className="size-4" />
<span>Copiar</span>
</button>
</div>
</div>
```
### Stats Pills (header)
```tsx
<span className="rounded-full border border-emerald-200/60 bg-emerald-50/60 px-3 py-1 text-xs font-medium text-emerald-700">
{count} hoy
</span>
```
### Stats Sidebar (grid 2x2)
```tsx
<section className="space-y-3 rounded-2xl border border-border/70 bg-card p-4">
<h2 className="text-xs font-semibold uppercase tracking-[0.28em] text-muted-foreground">
Resumen
</h2>
<div className="grid grid-cols-2 gap-3">
<div className="rounded-xl border border-border/50 bg-background/80 p-3">
<p className="text-xs text-muted-foreground">Total</p>
<p className="mt-1 text-xl font-semibold tracking-tight">{value}</p>
</div>
<div className="rounded-xl border border-emerald-200/40 bg-emerald-50/40 p-3">
<p className="text-xs text-emerald-700/80">Hoy</p>
<p className="mt-1 text-xl font-semibold tracking-tight text-emerald-700">{value}</p>
</div>
{/* ... confirmed (blue), completed (sky) */}
</div>
</section>
```
### Timeline Item (v2 - mejorado)
```tsx
<div className={[
'group flex items-center justify-between gap-3 rounded-xl border border-border/70 bg-card/60 p-3.5 transition-all hover:bg-muted/50',
isPast && 'opacity-50',
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-14 flex-col items-center">
<span className="text-sm font-medium tabular-nums">{startTime}</span>
<span className="text-[11px] text-muted-foreground">{endTime}</span>
</div>
<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" title="Marcar cumplida"><Check className="size-3.5" /></Button>
<Button size="icon-xs" variant="ghost" title="Cancelar">×</Button>
</div>
</div>
</div>
```
### Date Group Header
```tsx
<div className="flex items-center gap-3">
<h2 className="text-sm font-semibold capitalize">{dateLabel}</h2>
<div className="h-px flex-1 border-t border-dashed border-border/40" />
<span className="shrink-0 rounded-full border border-border/70 bg-background px-2.5 py-0.5 text-[11px] font-medium text-muted-foreground">
{count}
</span>
</div>
```