Files
playzer/.interface-design/system.md
Jose Selesan 503c29613b 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.
2026-04-24 11:23:45 -03:00

5.2 KiB
Raw Blame History

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

<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

  • Status tokens in CSS variables
  • Refactor status badges to use semantic classes
  • StatusBadge component created
  • 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

<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

<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

<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

<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>