1
0
Fork 0
polaris-task-force/DESIGN.md
Z8MB1E 16a44d3fc5 feat(poker): redesign felt as diegetic oval with toolbar and viewer cards
The table becomes a full oval racetrack with a center deck and pot plaque, perimeter seats as compact pills with stack-first readouts that flash on change, betting actions mounted on the felt near the viewer, your own cards dealt onto the bottom rail at community-card size, directional popups, and gated start and waiting-room polish.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-09-11 17:07:28 -04:00

197 lines
15 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.

# Polaris Task Force Design System
## 1. Atmosphere & Identity
Polaris is a dark operations console: focused, quiet, and information-dense without feeling cramped. The signature is a neutral charcoal surface hierarchy with restrained white controls and small, purposeful state accents.
## 2. Color
### Palette
| Role | Token | Usage |
| --------------------- | --------------------------------------- | ------------------------------------ |
| Surface / page | `bg-background` | Frontend page background |
| Surface / card | `bg-card` | Elevated content sections |
| Text / primary | `text-foreground` | Headings and primary labels |
| Text / secondary | `text-muted-foreground` | Descriptions and supporting copy |
| Interactive / primary | `bg-primary`, `text-primary-foreground` | Primary actions and checked controls |
| Interactive / focus | `ring-ring` | Keyboard focus indicators |
| Border | `border-border` | Card and control boundaries |
| Status / error | `text-destructive` | Inline validation and failure copy |
| Status / success | `text-emerald-500` | Existing save confirmations |
### Rules
- Use semantic Tailwind tokens backed by `src/app/(frontend)/styles.css`; do not add raw colors in frontend components.
- Preserve the dark neutral palette and use accent color only for interactive state.
## 3. Typography
### Scale
| Level | Token | Usage |
| ---------- | ------------------------------- | ---------------------------- |
| Page title | `text-lg font-semibold` | Route heading |
| Card title | `font-semibold` | Section heading |
| Body | inherited `text-base` | Primary content |
| Supporting | `text-sm text-muted-foreground` | Descriptions and helper text |
| Compact | `text-xs` | Secondary metadata only |
### Font Stack
- Primary: Geist, sans-serif
- Mono: Geist Mono, monospace
## 4. Spacing & Layout
### Base Unit
Spacing follows Tailwind's 4px scale. Existing frontend stacks use `gap-1`, `gap-2`, `gap-3`, `gap-4`, and `gap-6`.
### Rules
- Use `flex flex-col gap-*` for vertical stacks; do not use `space-y-*`.
- Account content is constrained with `max-w-2xl` and uses `gap-6` between cards.
- Preference sections use `CardHeader` and `CardContent`; controls use `gap-4`.
- Preference sub-options are indented with `pl-6` and remain one readable column on narrow screens.
## 5. Components
### Card sections
- **Structure**: `Card` → `CardHeader` → `CardTitle` + `CardDescription`; `CardContent` body.
- **Variants**: default dark card surface.
- **Spacing**: built-in card padding; internal control stack uses `gap-4`.
- **States**: default, focus/error states belong to child controls.
- **Accessibility**: headings and descriptions provide section context.
- **Motion**: no section-level motion.
- **Layout**: vertical stack.
### Poker table surface
- **Purpose**: The poker table is a diegetic game surface. The velvet felt carries the board, the dealer station, opponent arc, and the player's action console without floating control overlays.
- **Material**: Preserve the existing velvet felt gradient, dark inset rim, and restrained gold trim. The action console is a darker inset panel with the same trim language, while chips use dark, clay, and gold-trim variants for action hierarchy.
- **Primitives**: `poker-felt-table`, `poker-dealer-station`, `poker-seat-arc-*`, `poker-action-console`, `poker-hand-placard`, and `poker-chip`.
- **States**: betting and waiting console states, acting-seat highlight, folded and eliminated players, showdown result, game-complete placement, open and occupied waiting seats, and reduced-motion deal presentation.
- **Accessibility**: The cosmetic dealer station is labelled as a station and is not included in player or seat data. Action controls remain native buttons and inputs with visible focus states. Card labels continue to describe face-up and face-down cards.
- **Motion**: Cards originate visually at the dealer shoe using transform and opacity with a 60ms stagger. Reduced motion removes the deal animation while retaining static card tilts and highlight states.
- **Layout**: The felt is a flat-bottom semicircle. Opponents occupy count-aware upper arc slots, the dealer is top-center, community cards sit in the middle, and the user's console is pinned to the flat bottom edge. Fullscreen increases felt depth without moving the console out of the viewport canvas.
### Locker inventory surface
- **Purpose**: The authenticated locker is a Polaris operations surface: a dense, readable inventory rather than a literal game replica. The grid is the focal region and selection context is kept adjacent to it.
- **Palette**: Use the existing semantic neutral tokens: `bg-background` for the page, `bg-card`/`bg-muted` for framed regions, `border-border` for structure, `text-foreground` and `text-muted-foreground` for hierarchy, and `bg-primary`/`ring-ring` for active selection and primary actions. Amber utility accents are reserved for existing equipment/skin meaning; they do not become a second surface palette.
- **Typography**: Keep Geist for interface copy and Geist Mono with `tabular-nums` for coordinates, counts, dimensions, and other inventory metadata. Compact labels use the existing `text-xs` level, never ad-hoc display sizes.
- **Primitives**:
- **`locker-shell`**: one responsive page region with a compact control band and a single-column fallback below `xl`.
- **`inventory-frame`**: a strong rectangular border around the existing two-dimensional grid. The grid may scroll only inside this bounded region when its footprint exceeds the available inline size; the page itself must not gain a horizontal scrollbar.
- **`selection-rail`**: selected-item context and actions sit beside the inventory at `xl` and later, then become an inline section below the grid at narrower widths. It remains readable when no item is selected.
- **`metadata-strip`**: a compact cluster for occupancy, dimensions, and drag/drop guidance, using 4px spacing and semantic muted text.
- **`wardrobe-slot-board`**: a body-ordered rectangular board: head/face spans the top, torso and pack occupy the middle, and weapons/attachments plus sidearm finish the lower row. The primary weapon owns an indented attachment subgroup for optic, muzzle, and bipod slots. Each slot is a labeled semantic button with distinct filled and empty states; the board is driven by `WARDROBE_SLOT_LAYOUT` and never relies on a character image as its control surface.
- **States**: default, hover, focus-visible, selected, search-highlight, valid drop, invalid drop, attachment drop, empty, error, disabled, and reduced-motion. Selected items use a visible semantic ring; placement feedback stays inside the grid cells; empty and error states retain the existing user/manager-specific copy.
- **Accessibility**: Tabs remain native shadcn tab semantics. Every icon-only button has an accessible label, every meaningful thumbnail keeps descriptive alt text, draggable items retain dnd-kit keyboard attributes, and all actions keep visible `focus-visible` rings. The responsive rail must not reorder focus away from the selected item context.
- **Motion**: Preserve the existing dnd-kit activation threshold and absolute-position/ResizeObserver mechanics. Interaction polish is limited to existing shadcn color transitions, transform/opacity drag feedback, and the existing search highlight; `prefers-reduced-motion` disables highlight animation and nonessential transitions. No decorative looping motion is introduced.
- **Responsive behavior**: At 375px the shell becomes one readable column with no viewport-level horizontal overflow. Only the `inventory-frame` may expose bounded two-dimensional scrolling when required by the configured grid; the selection rail follows the grid inline, while the tab/search control cluster wraps before it can overflow. The inventory and selection rail use the side-by-side layout only at `xl` and above so the grid remains dominant and rail labels remain readable. The wardrobe slot board keeps its body order at `lg` and collapses regions to one readable column below that threshold.
### Mission card
- **Structure**: linked `Item` with `ItemHeader`, `ItemMedia`, `ItemTitle`, `ItemContent`, and `ItemDescription` primitives.
- **Variants**: `default` uses the neutral outline surface; `image` adds an optional cover image with a translucent gradient overlay. The contextual default is `image` when a cover image URL is available.
- **Metadata**: compact cards show operation/status, date/time, map, player slots, and campaign; full cards additionally show estimated duration.
- **States**: default, image, hover, focus-visible, and reduced-content/full-detail display.
- **Accessibility**: the entire card is one keyboard-reachable link; decorative cover images use empty alt text and are hidden from assistive technology.
- **Motion**: existing 200ms scale, border, and foreground transitions communicate the linked-card affordance; no decorative animation.
- **Layout**: responsive metadata wraps on narrow screens while the image layer stays absolutely positioned behind content.
### Preference row
- **Structure**: `label.flex.items-center.justify-between` with text on the left and a shadcn control on the right.
- **Variants**: primary setting and indented notification setting.
- **Spacing**: `gap-3` for label copy; `pl-6` for dependent settings.
- **States**: default, checked, unchecked, disabled, focus-visible.
- **Accessibility**: the label owns the control relationship; disabled dependent rows use both disabled controls and reduced-opacity presentation.
- **Motion**: shadcn control transition only.
- **Layout**: responsive cluster with a single-column text block.
### Switch
- **Structure**: shadcn `Switch` with an associated label and optional description.
- **Variants**: primary and dependent/disabled.
- **Spacing**: control row uses `gap-4`.
- **States**: unchecked, checked, hover, active, focus-visible, disabled.
- **Accessibility**: keyboard reachable with visible focus and native switch semantics.
- **Motion**: 100–150ms control transition.
- **Layout**: inline control at the row edge.
### RadioGroup
- **Structure**: shadcn `RadioGroup` with `RadioGroupItem` and `Label` pairs.
- **Variants**: two-option roster label selection.
- **Spacing**: option cluster uses `gap-4`.
- **States**: selected, unselected, hover, active, focus-visible.
- **Accessibility**: grouped options retain native radio semantics and labels.
- **Motion**: control transition only.
- **Layout**: wraps on narrow screens without horizontal overflow.
### Transfer workflow surface
- **Structure**: the account `AssignmentCard` owns current-assignment context and opens the
`RequestTransferDialog`; `/transfers` uses a `stack` of `Card` sections containing request,
approval, and escalated-resolution cards.
- **Variants**: request cards, leader approval cards, and superuser resolution cards share the
neutral card surface; status meaning is carried by the existing `Badge` variants.
- **States**: loading, empty, pending, approved, rejected, appealed, escalated, completed,
cancelled, denied, error, and success feedback.
- **Accessibility**: every dialog has a visible title and description, every textarea has a label,
rejection requires a non-empty reason, and destructive actions remain keyboard reachable with
the existing focus ring.
- **Motion**: use the existing shadcn dialog, button, and toast transitions only; no decorative
section animation. Async buttons expose their pending state and disable competing actions.
- **Layout**: the transfer list is a single document scroll region inside the authenticated shell;
cards use an intrinsic one-column-to-two-column grid and remain readable at narrow widths.
### Helpdesk ticket controls
- **Structure**: ticket list and detail surfaces use the existing `Item`, `Badge`, `Button`, and
`Dialog` primitives. The author-only edit dialog contains labeled title, category, priority, and
description controls; vote state uses one `Button` with a visible count.
- **States**: edit controls expose default, pending, success, and error states. Vote controls expose
unvoted, voted (`aria-pressed`), pending, and disabled states; canceled tickets disable voting.
- **Accessibility**: edit dialogs have a title and description, every field has an associated label,
and vote buttons announce both the action and count without relying on iconography.
- **Motion**: use existing shadcn dialog and button transitions only.
- **Layout**: controls wrap on narrow screens and never turn a card-wide navigation link into a
nested interactive target.
## 6. Motion & Interaction
### Timing
| Type | Duration | Usage |
| -------- | --------- | ---------------------------------- |
| Micro | 100–150ms | Switch and radio state feedback |
| Standard | 200–300ms | Button hover and focus transitions |
### Rules
- Keep motion in existing shadcn controls and buttons; do not add decorative animation to the account form.
- Preserve visible keyboard focus and `prefers-reduced-motion` behavior from the component primitives.
## 7. Depth & Surface
The frontend uses a mixed strategy: card borders plus the existing shadcn card shadow, with tonal changes for muted controls. New preference sections should compose the existing `Card` primitive rather than introduce custom shadows or radii.
## 8. Accessibility Constraints & Accepted Debt
### Constraints
- Target WCAG 2.2 AA.
- Every interactive control must be keyboard reachable with a visible focus state.
- Dependent notification controls must expose their disabled state through the actual control, not only visual opacity.
- The form must reflow to one readable column at mobile widths without horizontal scrolling.
### Accepted Debt
| Item | Location | Why accepted | Owner / Exit |
| ---------------------------------------------- | ------------- | -------------------------------------------------------------- | --------------------------------------------------------------- |
| Existing success color uses `text-emerald-500` | Account forms | Preserve established feedback styling in this focused redesign | Consolidate account feedback tokens in a future account UI pass |