Remove .claude directory from repository tracking
Local-only Claude Code context — not part of the UMBRA application. Files remain on disk, just untracked per .gitignore. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
e1e546c50c
commit
937118a528
@ -1,165 +0,0 @@
|
||||
# CLAUDE.md - UMBRA
|
||||
|
||||
## CRUCIAL CONTEXT EFFICIENCY
|
||||
|
||||
### Subagent Discipline
|
||||
- Prefer inline work for tasks under ~5 tool calls. Subagents have overhead — don't delegate trivially.
|
||||
- When using subagents, include output rules: "Final response under 2000 characters. List outcomes, not process."
|
||||
- Never call TaskOutput twice for the same subagent. If it times out, increase the timeout — don't re-read.
|
||||
|
||||
### File Reading
|
||||
- Read files with purpose. Before reading a file, know what you're looking for.
|
||||
- Use Grep to locate relevant sections before reading entire large files.
|
||||
- Never re-read a file you've already read in this session.
|
||||
- For files over 500 lines, use offset/limit to read only the relevant section.
|
||||
|
||||
### Responses
|
||||
- Don't echo back file contents you just read — the user can see them.
|
||||
- Don't narrate tool calls ("Let me read the file..." / "Now I'll edit..."). Just do it.
|
||||
- Keep explanations proportional to complexity. Simple changes need one sentence, not three paragraphs.
|
||||
|
||||
## Deployment & Access
|
||||
|
||||
- **UMBRA is hosted on a remote machine** at `http://10.0.69.35` (not localhost).
|
||||
- **Login PIN:** `1993`
|
||||
- **Rebuilds are manual.** Do not run `docker-compose` commands — the user will rebuild on the remote machine when needed.
|
||||
- **Browser automation** can access the live app at `http://10.0.69.35`.
|
||||
|
||||
## Operational Procedures
|
||||
|
||||
- Before making any changes or edits to code, create a branch. Once work is completed, commit and push to the branch automatically, then merge into main and push.
|
||||
- When required: For backend work invoke the 'backend-engineer' subagent, for work on the front end, invoke the 'frontend-engineer' subagent. To review work use the 'senior-code-reviewer' subagent and for any research use the 'research-analyst' subagent.
|
||||
- For any frontend UI related work you MUST use the frontend design skill AND reference the [stylesheet](.claude/context/stylesheet.md) to ensure visual consistency. The stylesheet defines all colors, typography, spacing, component patterns, and design principles for UMBRA. Do not invent new patterns.
|
||||
|
||||
## Future Roadmap
|
||||
|
||||
After the UI refresh is complete, the next major phases are:
|
||||
- **Multi-user authentication** — replace single PIN auth with per-user accounts
|
||||
- **Backend restructure** — add user_id foreign keys to all models, scope all queries per-user
|
||||
- **Always build for scale.** Even though UMBRA is currently single-user, design features, indexes, validations, and state machines with multi-user in mind. Cutting corners now means rework later.
|
||||
|
||||
## Key Features & Patterns
|
||||
|
||||
- **Reminder alerts**: Real-time polling (30s) via `AlertsProvider` context. Dashboard shows `AlertBanner`, other pages get Sonner toasts (max 3 + summary). Snooze/dismiss with `client_now` for Docker UTC offset.
|
||||
- **Two-click delete**: `useConfirmAction` hook in `hooks/useConfirmAction.ts` — first click shows "Sure?", auto-resets after 4s, second click executes. Used by TodoItem and ReminderItem.
|
||||
- **Naive datetime contract**: All datetimes are naive (no timezone). Frontend sends `toLocalDatetime()` from `lib/date-utils.ts` when the backend needs "now". Docker container runs UTC; `client_now` bridges the gap.
|
||||
|
||||
## Known Issues
|
||||
|
||||
- **Git push auth flake.** The first `git push` to the Gitea remote will fail with an authentication error. Simply retry the same push command — the second attempt succeeds.
|
||||
|
||||
## Hard Rules
|
||||
|
||||
- **Naive datetimes only.** The DB uses `TIMESTAMP WITHOUT TIME ZONE`. Never send timezone-aware strings (no `Z` suffix, no `.toISOString()`). Use local datetime formatting helpers instead.
|
||||
- **Eager load relationships in async SQLAlchemy.** Any `response_model` that includes a relationship (e.g. `ProjectResponse.tasks`) must use `selectinload()` when querying. Lazy loading raises `MissingGreenlet` in async context.
|
||||
- **Never shadow SQLAlchemy names.** Model columns must not be named `relationship`, `column`, `metadata`, or other SQLAlchemy reserved names. The `person.py` model aliases the import as `sa_relationship` for this reason.
|
||||
- **Frontend date inputs require exact formats.** `<input type="date">` needs `YYYY-MM-DD`, `<input type="datetime-local">` needs `YYYY-MM-DDThh:mm`. Backend may return `2026-02-15T00:00:00` which must be sliced/converted before binding.
|
||||
- **All API routes are prefixed with `/api`.** Frontend axios base URL is `/api`. Nginx proxies `/api/` to backend:8000. Never duplicate the prefix.
|
||||
|
||||
## Tech Stack
|
||||
|
||||
### Backend
|
||||
- **Python 3.12** (slim Docker image - no curl, use `urllib` for healthchecks)
|
||||
- **FastAPI** with async lifespan, Pydantic v2 (`model_dump()`, `ConfigDict(from_attributes=True)`)
|
||||
- **SQLAlchemy 2.0** async with `Mapped[]` type hints, `mapped_column()`, `async_sessionmaker`
|
||||
- **PostgreSQL 16** (Alpine) via `asyncpg`
|
||||
- **Alembic** for migrations
|
||||
- **Auth:** PIN-based with bcrypt + itsdangerous signed cookies (not JWT)
|
||||
|
||||
### Frontend
|
||||
- **React 18** + TypeScript + Vite 6
|
||||
- **TanStack Query v5** for server state (`useQuery`, `useMutation`, `invalidateQueries`)
|
||||
- **FullCalendar 6** (dayGrid, timeGrid, interaction plugins)
|
||||
- **Tailwind CSS 3** with custom dark theme + accent color CSS variables
|
||||
- **Sonner** for toast notifications
|
||||
- **Lucide React** for icons
|
||||
- Custom shadcn/ui-style components in `frontend/src/components/ui/`
|
||||
|
||||
### Infrastructure
|
||||
- **Docker Compose** - 3 services: `db`, `backend`, `frontend`
|
||||
- **Nginx** (Alpine) serves frontend SPA, proxies `/api/` to backend
|
||||
- Frontend served on port **80**, backend on port **8000**
|
||||
|
||||
## Authority Links
|
||||
|
||||
- [ui_refresh.md](.claude/projects/ui_refresh.md) - UI overhaul project plan, audit findings, implementation stages
|
||||
- [stylesheet.md](.claude/context/stylesheet.md) - Design system & visual reference for all frontend work
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
backend/app/
|
||||
main.py # FastAPI app, router registration, health endpoint
|
||||
config.py # Pydantic BaseSettings (DATABASE_URL, SECRET_KEY)
|
||||
database.py # Async engine, session factory, get_db dependency
|
||||
models/ # SQLAlchemy 2.0 models (Mapped[] style)
|
||||
schemas/ # Pydantic v2 request/response schemas
|
||||
routers/ # One router per feature (auth, todos, events, etc.)
|
||||
|
||||
frontend/src/
|
||||
App.tsx # Routes + ProtectedRoute wrapper
|
||||
lib/api.ts # Axios instance + getErrorMessage helper
|
||||
hooks/ # useAuth, useSettings, useTheme, useCalendars
|
||||
types/index.ts # TypeScript interfaces matching backend schemas
|
||||
components/
|
||||
ui/ # 16 base components (Button, Dialog, Sheet, Card, Input, Select, etc.)
|
||||
layout/ # AppLayout + Sidebar
|
||||
auth/ # LockScreen (PIN setup/login)
|
||||
dashboard/ # DashboardPage + widgets (Stats, Calendar, Todo, Upcoming, Countdown, etc.)
|
||||
calendar/ # CalendarPage, CalendarSidebar, CalendarForm, EventForm, TemplateForm
|
||||
todos/ # TodosPage + TodoList + TodoItem + TodoForm
|
||||
reminders/ # RemindersPage + ReminderList + ReminderForm
|
||||
projects/ # ProjectsPage, ProjectCard, ProjectDetail, ProjectForm, KanbanBoard, TaskRow, TaskForm, TaskDetailPanel
|
||||
people/ # PeoplePage + PersonCard + PersonForm
|
||||
locations/ # LocationsPage + LocationCard + LocationForm
|
||||
settings/ # SettingsPage (accent color, PIN change, first day of week)
|
||||
```
|
||||
|
||||
## Essential Commands
|
||||
|
||||
```bash
|
||||
# Build and run all services
|
||||
docker-compose up --build
|
||||
|
||||
# Rebuild single service after changes
|
||||
docker-compose up --build backend
|
||||
docker-compose up --build frontend
|
||||
|
||||
# View logs
|
||||
docker-compose logs -f
|
||||
docker-compose logs -f backend
|
||||
|
||||
# Reset database (destroys all data)
|
||||
docker-compose down -v && docker-compose up --build
|
||||
|
||||
# Stop everything
|
||||
docker-compose down
|
||||
```
|
||||
|
||||
## API Routes
|
||||
|
||||
All routes require auth (signed cookie) except `/api/auth/*` and `/health`.
|
||||
|
||||
| Prefix | Resource |
|
||||
|----------------------|-----------------|
|
||||
| `/api/auth` | PIN setup/login/logout/status |
|
||||
| `/api/todos` | Todos CRUD + toggle |
|
||||
| `/api/events` | Calendar events CRUD (incl. recurring) |
|
||||
| `/api/event-templates` | Event templates CRUD |
|
||||
| `/api/calendars` | User calendars CRUD + visibility |
|
||||
| `/api/reminders` | Reminders CRUD + dismiss |
|
||||
| `/api/projects` | Projects + nested tasks CRUD |
|
||||
| `/api/people` | People CRUD |
|
||||
| `/api/locations` | Locations CRUD + search |
|
||||
| `/api/settings` | Settings + PIN change |
|
||||
| `/api/dashboard` | Dashboard aggregation |
|
||||
| `/api/upcoming` | Unified upcoming items |
|
||||
| `/api/weather` | Weather data proxy |
|
||||
|
||||
## Stop Conditions
|
||||
|
||||
- **Do not** add timezone info to datetime strings sent to the backend
|
||||
- **Do not** use `datetime.utcnow()` - use `datetime.now(timezone.utc)` instead (deprecated in 3.12)
|
||||
- **Do not** return relationships from async endpoints without `selectinload()`
|
||||
- **Do not** use `curl` in backend Docker healthchecks (not available in python:slim)
|
||||
- **Do not** use `git push --force` or destructive git operations without explicit approval
|
||||
@ -1,359 +0,0 @@
|
||||
# UMBRA Design System & Stylesheet
|
||||
|
||||
> This document defines the visual language for every page in UMBRA.
|
||||
> All frontend work MUST conform to these patterns. Do not invent new colors, spacing scales, or component styles.
|
||||
|
||||
---
|
||||
|
||||
## Visual Reference
|
||||
|
||||
The dashboard establishes the canonical look: a near-black canvas with thin-bordered cards, cyan accent highlights, the Sora/DM Sans type pairing, and subtle glow effects on interactive elements. Every page should feel like a natural extension of this dashboard.
|
||||
|
||||
**Key visual characteristics:**
|
||||
- Ultra-dark background (~#0a0a0a) with very subtle card elevation (#0d0d0d cards)
|
||||
- Thin 1px borders in dark gray (#262626) - never heavy or prominent
|
||||
- Accent color (default cyan) used sparingly: active nav, stat icons, today highlights, hover glows
|
||||
- High-contrast white text on dark, with muted gray (#a3a3a3) for secondary content
|
||||
- Cards have a gentle hover glow (`shadow-accent/5`) - not dramatic, just a hint of life
|
||||
- Clean data-dense layouts with compact spacing - no wasted space
|
||||
- Type badges and priority pills use color-coded backgrounds at 10% opacity (e.g. `bg-blue-500/10 text-blue-400`)
|
||||
|
||||
---
|
||||
|
||||
## 1. Color Tokens (CSS Custom Properties)
|
||||
|
||||
All colors use HSL format via CSS variables. Never hardcode hex values.
|
||||
|
||||
### Surface Colors
|
||||
| Token | HSL | Approx Hex | Usage |
|
||||
|---|---|---|---|
|
||||
| `--background` | `0 0% 3.9%` | `#0a0a0a` | Page background |
|
||||
| `--card` | `0 0% 5%` | `#0d0d0d` | Card/panel surfaces |
|
||||
| `--card-elevated` | `0 0% 7%` | `#121212` | Hover states, elevated surfaces |
|
||||
| `--secondary` | `0 0% 10%` | `#1a1a1a` | Secondary backgrounds, muted surfaces |
|
||||
| `--muted` | `0 0% 10%` | `#1a1a1a` | Disabled/muted backgrounds |
|
||||
|
||||
### Text Colors
|
||||
| Token | HSL | Approx Hex | Usage |
|
||||
|---|---|---|---|
|
||||
| `--foreground` | `0 0% 98%` | `#fafafa` | Primary text |
|
||||
| `--muted-foreground` | `0 0% 63.9%` | `#a3a3a3` | Secondary/subtle text |
|
||||
|
||||
### Border & Input
|
||||
| Token | HSL | Approx Hex | Usage |
|
||||
|---|---|---|---|
|
||||
| `--border` | `0 0% 14.9%` | `#262626` | All borders, dividers |
|
||||
| `--input` | `0 0% 14.9%` | `#262626` | Input borders |
|
||||
|
||||
### Semantic Colors
|
||||
| Token | HSL | Usage |
|
||||
|---|---|---|
|
||||
| `--destructive` | `0 62.8% 30.6%` | Delete actions, errors |
|
||||
| `--primary` / `--accent-color` / `--ring` | Dynamic (accent HSL) | Primary actions, focus rings |
|
||||
|
||||
### Accent System (Dynamic)
|
||||
|
||||
The accent is composed of three root variables set at runtime by `useTheme`:
|
||||
- `--accent-h` (hue)
|
||||
- `--accent-s` (saturation)
|
||||
- `--accent-l` (lightness)
|
||||
|
||||
These feed into `--primary`, `--ring`, and `--accent-color`.
|
||||
|
||||
**Presets:**
|
||||
| Name | H | S | L | Approx Color |
|
||||
|---|---|---|---|---|
|
||||
| `cyan` (default) | 187 | 85.7% | 53.3% | Teal-cyan |
|
||||
| `blue` | 217 | 91.2% | 59.8% | Bright blue |
|
||||
| `purple` | 258 | 89.5% | 66.3% | Violet |
|
||||
| `orange` | 21 | 94.6% | 53.3% | Warm orange |
|
||||
| `green` | 142 | 70.6% | 45.3% | Emerald |
|
||||
|
||||
### Semantic Color Assignments (Fixed per feature)
|
||||
|
||||
These are NOT theme-dependent. They provide visual consistency for item types:
|
||||
|
||||
| Feature | Text | Background | Usage |
|
||||
|---|---|---|---|
|
||||
| Todos | `text-blue-400` | `bg-blue-500/10` | Todo badges, type indicators |
|
||||
| Events | `text-purple-400` | `bg-purple-500/10` | Event badges, calendar items |
|
||||
| Reminders | `text-orange-400` | `bg-orange-500/10` | Reminder badges |
|
||||
| Projects | `text-blue-400` | `bg-blue-500/10` | Project counts |
|
||||
| In Progress | `text-purple-400` | `bg-purple-500/10` | Status indicators |
|
||||
| Open Todos | `text-teal-400` | `bg-teal-500/10` | Stat cards |
|
||||
| Weather | `text-amber-400` | `bg-amber-500/10` | Weather widget |
|
||||
| High priority | `text-red-400` | `bg-red-500/10` | Priority pills |
|
||||
| Medium priority | `text-yellow-400` | `bg-yellow-500/10` | Priority pills |
|
||||
| Low priority | `text-green-400` | `bg-green-500/10` | Priority pills |
|
||||
|
||||
---
|
||||
|
||||
## 2. Typography
|
||||
|
||||
### Font Families
|
||||
- **Headings:** `Sora` (`font-heading`) — all h1-h6, card titles, stat values, logo
|
||||
- **Body:** `DM Sans` (`font-body`) — everything else
|
||||
|
||||
### Type Scale (as used)
|
||||
| Class | Usage |
|
||||
|---|---|
|
||||
| `text-[9px]` | Micro labels (priority badges, type tags) |
|
||||
| `text-[10px] tracking-wider uppercase` | Stat labels |
|
||||
| `text-[11px]` | Small metadata |
|
||||
| `text-xs` | Timestamps, secondary metadata |
|
||||
| `text-sm` | Body text, list items, form inputs, nav items |
|
||||
| `text-lg font-semibold` | Card titles (with `font-heading`) |
|
||||
| `text-xl font-bold tabular-nums` | Stat values (with `font-heading`) |
|
||||
| `text-3xl font-bold tracking-tight` | Page greeting/hero (with `font-heading`) |
|
||||
|
||||
### Font Rendering
|
||||
```css
|
||||
font-feature-settings: "rlig" 1, "calt" 1;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. Spacing & Layout
|
||||
|
||||
### Border Radius
|
||||
| Token | Value | Usage |
|
||||
|---|---|---|
|
||||
| `rounded-lg` | `0.5rem` (8px) | Cards, panels |
|
||||
| `rounded-md` | `~6px` | Buttons, inputs |
|
||||
| `rounded-sm` | `~4px` | Small elements |
|
||||
| `rounded-full` | Pill | Badges, dots, icon containers |
|
||||
|
||||
### Page Layout
|
||||
```
|
||||
App Shell: flex h-screen overflow-hidden bg-background
|
||||
├── Sidebar: w-64 (expanded) / w-16 (collapsed), border-r, bg-card
|
||||
│ transition-all duration-300
|
||||
└── Content: flex-1 flex flex-col overflow-hidden
|
||||
├── MobileHeader: md:hidden, h-14, border-b, bg-card
|
||||
└── <main>: flex-1 overflow-y-auto
|
||||
└── Page content: px-6 py-6
|
||||
```
|
||||
|
||||
### Grid Patterns
|
||||
| Pattern | Classes | Usage |
|
||||
|---|---|---|
|
||||
| Stat row | `grid gap-2.5 grid-cols-2 lg:grid-cols-4` | Dashboard stats |
|
||||
| Main + sidebar | `grid gap-5 lg:grid-cols-5` → `lg:col-span-3` + `lg:col-span-2` | Dashboard layout |
|
||||
| Card grid | `grid gap-4 md:grid-cols-2 lg:grid-cols-3` | List pages (people, locations, projects) |
|
||||
|
||||
### Standard Spacing
|
||||
- Page padding: `px-6 py-6`
|
||||
- Card padding: `p-5` (header), `p-5 pt-0` (content/footer)
|
||||
- Card gap in grid: `gap-4` to `gap-5`
|
||||
- List row padding: `py-1.5 px-2`
|
||||
- Section gap: `space-y-4` to `space-y-6`
|
||||
|
||||
---
|
||||
|
||||
## 4. Component Patterns
|
||||
|
||||
### Card
|
||||
```
|
||||
Base: rounded-lg border bg-card text-card-foreground shadow-sm
|
||||
Hover: hover:shadow-lg hover:shadow-accent/5 hover:border-accent/20
|
||||
Motion: transition-all duration-200
|
||||
Header: flex flex-col space-y-1.5 p-5
|
||||
Title: font-heading text-lg font-semibold leading-none tracking-tight
|
||||
Content: p-5 pt-0
|
||||
Footer: flex items-center p-5 pt-0
|
||||
```
|
||||
|
||||
### Button (CVA variants)
|
||||
```
|
||||
Base: inline-flex items-center justify-center gap-2 rounded-md text-sm font-medium
|
||||
transition-colors focus-visible:ring-2 focus-visible:ring-ring
|
||||
Variants:
|
||||
default: bg-accent text-accent-foreground hover:bg-accent/90
|
||||
destructive: bg-destructive text-destructive-foreground hover:bg-destructive/90
|
||||
outline: border border-input bg-background hover:bg-accent/10 hover:text-accent
|
||||
secondary: bg-secondary text-secondary-foreground hover:bg-secondary/80
|
||||
ghost: hover:bg-accent/10 hover:text-accent
|
||||
link: text-accent underline-offset-4 hover:underline
|
||||
Sizes:
|
||||
default: h-10 px-4 py-2
|
||||
sm: h-9 px-3
|
||||
lg: h-11 px-8
|
||||
icon: h-10 w-10
|
||||
```
|
||||
|
||||
### Badge
|
||||
```
|
||||
Base: inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold
|
||||
Variants:
|
||||
default: border-transparent bg-accent text-accent-foreground
|
||||
secondary: border-transparent bg-secondary text-secondary-foreground
|
||||
destructive: border-transparent bg-destructive text-destructive-foreground
|
||||
outline: text-foreground
|
||||
```
|
||||
|
||||
### Input / Select / Textarea
|
||||
```
|
||||
h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm
|
||||
placeholder:text-muted-foreground
|
||||
focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2
|
||||
disabled:cursor-not-allowed disabled:opacity-50
|
||||
```
|
||||
|
||||
### Dialog
|
||||
```
|
||||
Overlay: fixed inset-0 bg-background/80 backdrop-blur-sm
|
||||
Content: border bg-card p-6 shadow-lg rounded-lg max-w-xl
|
||||
Header: flex flex-col space-y-1.5
|
||||
Title: text-lg font-semibold leading-none tracking-tight
|
||||
Footer: flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2
|
||||
Close: absolute right-4 top-4, X icon h-4 w-4
|
||||
```
|
||||
|
||||
### Empty State
|
||||
```
|
||||
Container: flex flex-col items-center justify-center py-16 px-4
|
||||
Icon: rounded-full bg-muted p-4 mb-4, icon h-8 w-8 text-muted-foreground
|
||||
Title: text-lg font-semibold mb-1
|
||||
Desc: text-sm text-muted-foreground text-center max-w-sm mb-4
|
||||
Action: <Button> with Plus icon
|
||||
```
|
||||
|
||||
### Skeleton
|
||||
```
|
||||
Base: animate-pulse rounded-md bg-muted
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Widget & Data Patterns
|
||||
|
||||
### Stat Card
|
||||
```
|
||||
Container: Card with bg-gradient-to-br from-accent/[0.03] to-transparent
|
||||
Icon: p-1.5 rounded-md bg-{color}/10
|
||||
Label: text-[10px] tracking-wider uppercase text-muted-foreground
|
||||
Value: font-heading text-xl font-bold tabular-nums
|
||||
```
|
||||
|
||||
### List Row (full-page lists: todos, reminders, etc.)
|
||||
```
|
||||
Container: space-y-0.5 (tight stacking, no card borders)
|
||||
Row: flex items-center gap-3 px-3 py-2 rounded-md
|
||||
hover:bg-card-elevated transition-colors duration-150
|
||||
Layout: Everything on ONE line. Left-to-right:
|
||||
[interactive control] [title truncate flex-1] [pills/badges] [metadata] [actions]
|
||||
Title: text-sm font-medium truncate flex-1 min-w-0
|
||||
Metadata: text-[11px] shrink-0, right-aligned before actions
|
||||
Actions: ghost icon buttons h-7 w-7, last in row
|
||||
```
|
||||
|
||||
**List view vs card view:** If a page shows items as a *list* (todos, upcoming),
|
||||
use single-line rows with `hover:bg-card-elevated` — no borders, no shadow,
|
||||
no multi-line content. If a page shows items as a *grid of cards* (projects,
|
||||
people, locations), use the Card component with `p-5`, multi-line content, and
|
||||
`hover:shadow-lg hover:shadow-accent/5`.
|
||||
|
||||
### Widget List Row (dashboard widgets — more compact)
|
||||
```
|
||||
flex items-center gap-2 py-1.5 px-2 rounded-md
|
||||
hover:bg-card-elevated transition-colors duration-150
|
||||
```
|
||||
|
||||
### Section Header (widget title)
|
||||
```
|
||||
Icon wrapped in p-1.5 rounded-md bg-{color}/10
|
||||
alongside CardTitle
|
||||
"View all" or count label: text-xs text-muted-foreground
|
||||
```
|
||||
|
||||
### Priority Pills
|
||||
```
|
||||
text-[9px] px-1.5 py-0.5 rounded-full font-medium
|
||||
High: bg-red-500/20 text-red-400
|
||||
Medium: bg-yellow-500/20 text-yellow-400
|
||||
Low: bg-green-500/20 text-green-400
|
||||
```
|
||||
|
||||
### Type Tags
|
||||
```
|
||||
text-[9px] px-1.5 py-0.5 rounded font-medium uppercase tracking-wide
|
||||
Todo: bg-blue-500/15 text-blue-400
|
||||
Event: bg-purple-500/15 text-purple-400
|
||||
Reminder: bg-orange-500/15 text-orange-400
|
||||
```
|
||||
|
||||
### Week Timeline (today highlight)
|
||||
```
|
||||
bg-accent/10 border border-accent/30 rounded-lg
|
||||
shadow-[0_0_12px_hsl(var(--accent-color)/0.15)]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Navigation
|
||||
|
||||
### Sidebar Nav Link
|
||||
```
|
||||
Base: flex items-center gap-3 rounded-lg px-3 py-2 text-sm font-medium
|
||||
transition-all duration-200 border-l-2
|
||||
Active: bg-accent/15 text-accent border-accent
|
||||
Inactive: text-muted-foreground hover:bg-accent/10 hover:text-accent border-transparent
|
||||
Icon: h-5 w-5 shrink-0
|
||||
```
|
||||
|
||||
### Logo
|
||||
```
|
||||
font-heading text-xl font-bold tracking-tight text-accent
|
||||
```
|
||||
|
||||
### Logout
|
||||
```
|
||||
hover:bg-destructive/10 hover:text-destructive
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. Animations & Transitions
|
||||
|
||||
### Keyframes
|
||||
| Name | Effect | Duration | Easing |
|
||||
|---|---|---|---|
|
||||
| `fade-in` | opacity 0 → 1 | 300ms | ease-out |
|
||||
| `slide-up` | opacity 0→1 + Y(8px→0) | 400ms | ease-out |
|
||||
|
||||
### Staggered Entry (dashboard sections)
|
||||
```
|
||||
animationDelay: '50ms' / '100ms' / '150ms'
|
||||
animationFillMode: 'backwards'
|
||||
```
|
||||
|
||||
### Standard Transitions
|
||||
| Usage | Classes |
|
||||
|---|---|
|
||||
| Interactive elements | `transition-all duration-200` |
|
||||
| Color changes | `transition-colors duration-150` |
|
||||
| Sidebar collapse | `transition-all duration-300` |
|
||||
|
||||
---
|
||||
|
||||
## 8. Scrollbar
|
||||
```css
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: hsl(0 0% 20%) transparent; /* #333 thumb on transparent */
|
||||
::-webkit-scrollbar { width: 6px; height: 6px; }
|
||||
::-webkit-scrollbar-thumb:hover { background: hsl(var(--accent-color) / 0.5); }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. Design Principles
|
||||
|
||||
1. **Dark-first:** Everything is designed for the dark palette. There is no light mode.
|
||||
2. **Accent as highlight, not flood:** The accent color draws attention to interactive/important elements. It should never dominate a section.
|
||||
3. **10% opacity backgrounds:** Feature colors (blue, purple, orange, etc.) are always at 10-20% opacity for backgrounds, with 400-level text on top. Never use full-saturation backgrounds.
|
||||
4. **Data density:** Compact rows, small text for metadata, efficient use of space. This is a productivity tool, not a marketing page.
|
||||
5. **Consistent card pattern:** Every distinct content section is a Card. Cards have uniform padding (p-5), title styling, and hover behavior.
|
||||
6. **Typography hierarchy:** Sora for headings/numbers, DM Sans for body. This distinction creates visual rhythm without extra decoration.
|
||||
7. **Subtle motion:** Hover glows, fade-ins, and slide-ups. No bouncing, spinning, or attention-grabbing animations.
|
||||
8. **Icon consistency:** All icons from Lucide React, sized at `h-5 w-5` for nav/actions, `h-4 w-4` for inline, `h-8 w-8` for empty states.
|
||||
@ -1,296 +0,0 @@
|
||||
# UMBRA UI Refresh — Planning Document
|
||||
|
||||
## Current UI Audit (2026-02-20)
|
||||
|
||||
### Global Observations
|
||||
- **Theme:** Dark background (~#0a0a0a), dark card surfaces (~#1a1a2e), blue/cyan accent
|
||||
- **Sidebar:** Fixed left ~200px, solid blue highlight on active item, Lucide icons + labels, collapse chevron
|
||||
- **Typography:** System sans-serif, white headings, muted gray subtext — functional but lacks personality
|
||||
- **Cards:** Rounded borders with subtle gray stroke, no shadows, no hover effects, no transitions
|
||||
- **Layout:** Full-width content area with no max-width constraint — cards stretch awkwardly on wide displays
|
||||
- **Action buttons:** Consistent "+ Add X" blue filled buttons top-right on every page — good pattern
|
||||
- **Empty states:** Centered icon + message — functional but lifeless
|
||||
- **Scrollbars:** Default browser scrollbars — clash with dark theme
|
||||
|
||||
### Page-by-Page Notes
|
||||
|
||||
#### Login
|
||||
- Centered dark card, lock icon, PIN input, blue "Unlock" button
|
||||
- Clean and minimal — works well structurally
|
||||
- Missing UMBRA branding/identity — feels like a generic auth screen
|
||||
- No ambient atmosphere (no gradients, no glow, no texture)
|
||||
|
||||
#### Dashboard
|
||||
- 4 stat cards across top (Total Projects, In Progress, People, Locations) with colored Lucide icons
|
||||
- "Upcoming (7 days)" section — flat list of events with "Event" badge right-aligned
|
||||
- Bottom row: "Upcoming Todos" + "Today's Events" side by side
|
||||
- **Issues:**
|
||||
- Stat cards are flat rectangles with label/number/icon — no visual weight or hierarchy
|
||||
- Icon colors (purple, purple, green, orange) feel arbitrary — no semantic meaning
|
||||
- Upcoming list wastes horizontal space — event name far left, badge far right, nothing in between
|
||||
- Bottom widgets have massive empty space when few/no items
|
||||
- No week summary or schedule preview — missed opportunity for at-a-glance context
|
||||
- "Welcome back. Here's your overview." is generic placeholder text
|
||||
- No time-of-day awareness or contextual greeting
|
||||
|
||||
#### Todos
|
||||
- Search bar + "All Priorities" dropdown + category filter + "Show completed" checkbox
|
||||
- All controls on single line — functional but cramped and visually flat
|
||||
- Empty state: centered checkbox icon + message
|
||||
- **Issues:** Filter controls are unstyled native elements mixed with custom — inconsistent
|
||||
|
||||
#### Calendar (Month)
|
||||
- FullCalendar month view with colored event dots/pills
|
||||
- Color coding: blue (default), purple, green (multi-day), orange, yellow
|
||||
- **Issues:**
|
||||
- Prev/next navigation arrows are tiny unstyled squares (~16px) — nearly invisible
|
||||
- "today" button is plain text with no visual emphasis
|
||||
- Event text truncates on small cells with no tooltip or expand affordance
|
||||
- Multi-day events (green/orange bars) visually dominate and obscure day cells
|
||||
- No visual distinction for today's date in month grid
|
||||
|
||||
#### Calendar (Week)
|
||||
- Time grid with events as colored blocks — time + title visible
|
||||
- Today column has yellowish-brown tint overlay
|
||||
- **Issues:**
|
||||
- The "today" tint is murky (#8B8000-ish overlay) — looks dirty rather than highlighted
|
||||
- Past-time columns also have this tint, making it confusing
|
||||
- No current-time indicator line
|
||||
|
||||
#### Calendar (Day)
|
||||
- Full-day column with same murky brown-yellow background
|
||||
- **Issues:** When empty, entire content area is just a brown column — unappealing
|
||||
- No visual distinction between past hours and future hours
|
||||
|
||||
#### Reminders
|
||||
- Tab bar: Active / Dismissed / All — clean blue active state
|
||||
- Empty state similar to Todos
|
||||
- Very sparse — large empty space below tabs
|
||||
|
||||
#### Projects
|
||||
- 3-column card grid
|
||||
- Each card: ALL-CAPS name, status badge pill, "Progress" label with thin bar, task count, calendar icon + due date
|
||||
- **Issues:**
|
||||
- ALL-CAPS names are aggressive and hard to scan
|
||||
- Status badges ("not started" / "in progress") are small outlined pills — low contrast
|
||||
- Progress bars are extremely thin (~2px) and low-contrast against dark surface
|
||||
- "Progress" label + "0/3 tasks" on same line wastes vertical space
|
||||
- Cards have uneven internal spacing — description shows on some, not others
|
||||
- No hover state or click affordance — cards don't feel interactive
|
||||
|
||||
#### People
|
||||
- 3-column card grid
|
||||
- Each card: name, category badge (Friends = blue, Work = gray), birthday, edit/delete icons top-right
|
||||
- **Issues:**
|
||||
- Cards feel sparse — just 3 lines of info in a large rectangle
|
||||
- No avatar placeholder — missed opportunity for visual anchoring
|
||||
- Edit (pencil) and delete (trash) icons are small and far from the name
|
||||
- Category badge colors don't match Locations page (Work = gray here, purple there)
|
||||
|
||||
#### Locations
|
||||
- 3-column card grid with location pin icon beside name
|
||||
- Category badges: home = blue, work = purple
|
||||
- Address shown as subtitle
|
||||
- **Issues:** Badge color system inconsistent with People page
|
||||
|
||||
#### Settings
|
||||
- Three stacked sections: Appearance, Dashboard, Security
|
||||
- Left-aligned at ~50% width, entire right half is empty
|
||||
- Accent color picker: 5 colored circles (cyan, blue, purple, orange, green)
|
||||
- **Issues:**
|
||||
- Massive dead space on right — feels incomplete
|
||||
- Color circles have no labels or tooltips
|
||||
- Sections could use visual separation beyond just spacing
|
||||
|
||||
---
|
||||
|
||||
## Design Critique (Frontend Design Review)
|
||||
|
||||
### 1. Surface & Depth — "Everything lives on the same plane"
|
||||
The biggest visual issue is that every element — cards, sidebar, content area, stat widgets — exists at the same depth. There are no shadows, no layering, no elevation changes. Modern dark UIs use subtle elevation to create hierarchy:
|
||||
- Cards should float slightly above the background with soft box-shadows (`0 1px 3px rgba(0,0,0,0.3)`)
|
||||
- The sidebar should feel like a separate layer with a subtle border or shadow on its right edge
|
||||
- Hover states should lift cards slightly (`translateY(-1px)` + shadow increase)
|
||||
- Active/selected states should have a gentle inner glow or border luminance shift
|
||||
|
||||
### 2. Typography — "Functional but forgettable"
|
||||
The current type system is plain system sans-serif with two weights (bold headings, normal body). For a personal life manager named "UMBRA," typography should reinforce brand identity:
|
||||
- Consider a distinctive heading font (geometric sans like Outfit, Sora, or General Sans)
|
||||
- Body text needs slightly more line-height for readability on dark backgrounds (1.6 minimum)
|
||||
- The ALL-CAPS project names are too aggressive — use sentence case with semibold weight instead
|
||||
- Stat card numbers should be larger and use tabular figures for alignment
|
||||
- Muted text (#6b7280-range) is too low-contrast on dark backgrounds — bump to #9ca3af minimum
|
||||
|
||||
### 3. Color System — "Arbitrary accents without semantic meaning"
|
||||
Colors are applied without a clear system:
|
||||
- Stat card icons use purple/purple/green/orange — why purple twice? No semantic connection
|
||||
- Category badges are inconsistent: Work is gray on People, purple on Locations
|
||||
- The calendar today overlay (#8B8000-ish) clashes with the cool-toned theme
|
||||
- **Recommendation:** Establish a semantic color palette:
|
||||
- Accent (cyan/blue): interactive elements, active states, links
|
||||
- Success (emerald): completed, on-track
|
||||
- Warning (amber): upcoming, due soon
|
||||
- Danger (rose): overdue, delete
|
||||
- Neutral (slate): secondary text, borders, disabled
|
||||
- Category colors should be consistent app-wide (same color for "Work" everywhere)
|
||||
|
||||
### 4. Spacing & Density — "Too much air, not enough content"
|
||||
- Dashboard stat cards have generous internal padding but minimal content — lots of dead space
|
||||
- The Upcoming list is a single-column spanning the full width with text far-left and badge far-right
|
||||
- Settings page uses only half the viewport width
|
||||
- Cards on People/Locations show 3 lines of info in ~150px tall containers
|
||||
- **Recommendation:** Use a tighter grid system. Dashboard should use a 2-column or asymmetric layout below stats. Cards should breathe but not float in empty space. Consider max-width containers (1200-1400px).
|
||||
|
||||
### 5. Interactivity — "Static pages, not a living app"
|
||||
Zero motion, zero feedback:
|
||||
- No hover states on any cards or interactive elements
|
||||
- No transitions on page changes
|
||||
- No loading states or skeleton screens
|
||||
- No micro-interactions (checkbox toggle, card expand, badge pulse)
|
||||
- Scrollbars are default browser chrome — jarring in dark theme
|
||||
- **Recommendation:** Add CSS transitions on all interactive elements (150-200ms ease). Custom scrollbar styling with `scrollbar-color` and `::-webkit-scrollbar`. Subtle scale/shadow on card hover.
|
||||
|
||||
### 6. Dashboard Specific — "A bulletin board, not a command center"
|
||||
The dashboard is the first thing you see and it should feel alive:
|
||||
- Missing: week-at-a-glance schedule/timeline
|
||||
- Missing: contextual greeting (time of day + name)
|
||||
- Missing: quick actions or shortcuts
|
||||
- Stat cards don't link context — "5 Projects" but which ones matter right now?
|
||||
- Bottom widgets (Upcoming Todos / Today's Events) are equal-width but Today's Events is more urgent
|
||||
- **Recommendation:** Redesign dashboard as a "morning briefing" — today's schedule, this week's priorities, active project progress, quick-add shortcuts
|
||||
|
||||
### 7. Component Quality — "Default HTML with Tailwind"
|
||||
Several components feel like unstyled defaults:
|
||||
- Calendar nav arrows are default FullCalendar squares — need custom styled buttons
|
||||
- Filter dropdowns on Todos look like native `<select>` elements
|
||||
- Progress bars are 2px thin lines — should be thicker (6-8px) with rounded ends and gradient fills
|
||||
- Badge pills lack padding consistency and weight
|
||||
|
||||
---
|
||||
|
||||
## Implementation Stages
|
||||
|
||||
### Stage 1: Dashboard Overhaul — COMPLETED
|
||||
- [x] Redesign dashboard layout: greeting, week summary, stat cards, upcoming events, today's schedule
|
||||
- [x] Establish new card component with shadows, hover states, transitions
|
||||
- [x] Add custom scrollbar styling globally
|
||||
- [x] Set up CSS custom properties for the refined color system
|
||||
- [x] **Deliverable:** Polished dashboard + `stylesheet.md` capturing the new design language
|
||||
- [x] Typography: Sora headings + DM Sans body
|
||||
- [x] Contextual greeting (time-of-day aware)
|
||||
- [x] Week-at-a-glance timeline with today highlight + event dots
|
||||
- [x] Stat cards with gradient backgrounds, semantic icon colors, tabular numbers
|
||||
- [x] Upcoming widget with type badges + priority pills
|
||||
- [x] Today's Events + Upcoming Todos + Active Reminders sidebar widgets
|
||||
- [x] Weather widget with location search
|
||||
- [x] Staggered fade-in/slide-up animations
|
||||
- [x] Smart nudge bar (contextual tips below week timeline)
|
||||
|
||||
### Stage 2: Global Shell & Navigation — COMPLETED
|
||||
- [x] Sidebar refinement: accent-colored hover states, border-left active indicator, collapse animation
|
||||
- [x] Separator between nav items and utility items (Settings, Logout)
|
||||
- [x] Global transitions and loading states (skeleton screens)
|
||||
- [x] Custom scrollbar applied everywhere (accent-tinted on hover)
|
||||
|
||||
### Stage 3: Calendar Redesign & Improvements — COMPLETED
|
||||
- [x] Multi-calendar backend: user-created calendars with color, virtual birthday calendar from People
|
||||
- [x] Calendar sidebar with collapsible calendar list, visibility toggles, color indicators
|
||||
- [x] Custom-styled toolbar: prev/next/today buttons, segmented Month/Week/Day view switcher
|
||||
- [x] Calendar header alignment with global UMBRA header (h-16), no border wrapper
|
||||
- [x] ResizeObserver for sidebar collapse → calendar auto-resizes
|
||||
- [x] Sheet component (`sheet.tsx`): slide-in panel replacing Dialog for all forms
|
||||
- [x] All 4 forms migrated to Sheet: EventForm, TodoForm, ReminderForm, LocationForm
|
||||
- [x] Form layout improvements: 2-column grids for related fields, wider textareas
|
||||
- [x] All-day event fixes: slim CSS bars, exclusive end-date offset (display ↔ save)
|
||||
- [x] Materialized recurring events backend: migration, model, service, router
|
||||
- [x] Recurrence UI: structured picker (every N days, weekly, monthly nth weekday, monthly date)
|
||||
- [x] Edit/delete scope dialog for recurring events ("This event only" / "This and all future")
|
||||
- [x] Drag-drop/resize guards for recurring events (reverts with info toast)
|
||||
- [x] LocationPicker component with OSM Nominatim integration + local DB search
|
||||
- [x] LocationPicker integrated into EventForm and LocationForm
|
||||
- [x] First day of week setting (Sunday/Monday) in Settings with calendar sync
|
||||
- [x] FullCalendar reactivity fix: key prop for firstDay remount
|
||||
- [x] Dashboard/upcoming queries: parent template exclusion filter (no duplicates)
|
||||
- [x] DayBriefing: null guards on end_datetime, reminders in night briefing, pre-5AM today awareness
|
||||
- [x] Recurrence crash fixes: null field stripping, _rule_int helper, first-occurrence generation
|
||||
- [x] Event templates: removed unused duration_minutes field, added Alembic migration
|
||||
- [x] TemplateForm converted from Dialog to Sheet side panel (matches EventForm)
|
||||
- [x] EventForm auto-prefills start=now, end=now+1h for new events (no calendar selection needed)
|
||||
- [x] EventForm distinguishes template-based creation ("Create Event from X Template") from editing
|
||||
- [x] LocationPicker mount guard: prevents auto-open dropdown on pre-filled forms
|
||||
|
||||
### Stage 4: CRUD Pages (Todos, Reminders, Projects)
|
||||
|
||||
#### Projects — COMPLETED
|
||||
- [x] Refined filter bar components
|
||||
- [x] Improved card designs (progress bars, status badges)
|
||||
- [x] Better empty states with contextual illustrations or suggestions
|
||||
- [x] Consistent hover/click affordances
|
||||
|
||||
#### Todos — COMPLETED
|
||||
- [x] Compact h-16 header with segmented priority filter, search, category dropdown, show-completed toggle
|
||||
- [x] Summary stat cards (Open, Completed, Overdue)
|
||||
- [x] Single-line compact row design (list-view convention: no card borders, hover:bg-card-elevated)
|
||||
- [x] Priority pills, category badges, recurrence badges inline
|
||||
- [x] Overdue/today date coloring, due time display
|
||||
- [x] Grouped sections (Overdue → Today → Upcoming → No Due Date → Completed)
|
||||
- [x] Empty state with "Add Todo" action button
|
||||
- [x] Recurrence logic: auto-reset scheduling (daily/weekly/monthly), reset info display
|
||||
- [x] Optional due time field, fixed date not being truly optional
|
||||
|
||||
#### Reminders — COMPLETED
|
||||
- [x] Compact h-16 header with segmented status filter (Active/Dismissed/All), search input
|
||||
- [x] Summary stat cards (Active, Overdue, Dismissed) with semantic icon colors
|
||||
- [x] Single-line compact row design (ReminderItem) matching TodoItem pattern
|
||||
- [x] Grouped sections (Overdue → Today → Upcoming → No Date → Dismissed)
|
||||
- [x] Recurrence badge inline, date coloring (red overdue, yellow today, muted future)
|
||||
- [x] Dismiss button, edit button, 4-second confirm delete with "Sure?" label
|
||||
- [x] Optimistic delete with rollback
|
||||
- [x] Empty state with "Add Reminder" action button
|
||||
- [x] ReminderForm uses single datetime-local input (matches EventForm) with recurrence alongside
|
||||
|
||||
#### Reminder Alerts — COMPLETED
|
||||
- [x] Backend: `snoozed_until` column + Alembic migration 017 with composite index
|
||||
- [x] Backend: `GET /api/reminders/due` endpoint (overdue, non-dismissed, non-recurring, snooze-aware)
|
||||
- [x] Backend: `PATCH /api/reminders/{id}/snooze` with `Literal[5, 10, 15]` validation + state guards
|
||||
- [x] Backend: Snooze/due endpoints accept `client_now` from frontend (fixes Docker UTC vs local time)
|
||||
- [x] Backend: Dismiss clears `snoozed_until`; updating `remind_at` reactivates dismissed reminders
|
||||
- [x] Frontend: `AlertsProvider` context in AppLayout — single polling instance, no duplicate toasts
|
||||
- [x] Frontend: `useAlerts` hook polls `/reminders/due` every 30s with client_now
|
||||
- [x] Frontend: Sonner custom toasts on non-dashboard pages (max 3 + overflow summary, `duration: Infinity`)
|
||||
- [x] Frontend: `AlertBanner` on dashboard below stats row (orange left accent, compact rows)
|
||||
- [x] Frontend: `SnoozeDropdown` component — clock icon + "Snooze" label, opens dropdown with 5/10/15 min options
|
||||
- [x] Frontend: Toast/banner dismiss button with X icon + "Dismiss" label
|
||||
- [x] Frontend: Route-aware display — toasts dismissed on dashboard entry, fired on exit
|
||||
- [x] Frontend: Dashboard Active Reminders card filters out items already in AlertBanner
|
||||
- [x] Frontend: Shared `getRelativeTime` + `toLocalDatetime` utilities in `lib/date-utils.ts`
|
||||
- [x] Accessibility: aria-labels on all snooze/dismiss buttons
|
||||
|
||||
### Stage 5: Entity Pages (People, Locations)
|
||||
- Avatar placeholders for People cards
|
||||
- Consistent category badge colors app-wide
|
||||
- Denser card layout with more visible info
|
||||
|
||||
### Stage 6: Settings & Login
|
||||
- Settings: full-width responsive layout, labeled color picker, section cards
|
||||
- Login: UMBRA branding, ambient background effect
|
||||
|
||||
### Stage 7: Final Polish
|
||||
- Cross-page consistency audit
|
||||
- Animation timing review
|
||||
- Accessibility pass (contrast ratios, focus states)
|
||||
- Performance review (no layout shifts, smooth transitions)
|
||||
|
||||
---
|
||||
|
||||
## User Requirements
|
||||
- Maintain dark minimalist theme with accent colors
|
||||
- More modern, softer, cleaner aesthetic
|
||||
- Less dead space in UI elements
|
||||
- Themed scrollbars matching the dark UI
|
||||
- Dashboard: include a summary of the current week
|
||||
- Incremental implementation — dashboard first, then style guide, then remaining pages
|
||||
|
||||
## Refresh Scope
|
||||
|
||||
**Current status:** Stages 1-4 complete, plus Reminder Alerts feature. Next up: Stage 5 Entity Pages (People, Locations).
|
||||
Loading…
x
Reference in New Issue
Block a user