# DESIGN.md — Afilmory Web Design System **Scope:** `apps/web` (the gallery SPA) and `packages/ui` (its primitives). **Out of scope:** `be/apps/dashboard` (a linear, data-first admin aesthetic — a deliberately different system, see `be/apps/dashboard/AGENTS.md`), `apps/mobile` (native Swift/UIKit, localized and styled natively), `apps/site` (Astro marketing site). **Authority.** This document is derived from the shipped code, not from intent. It is normative for new code. A handful of existing files predate it and contradict it — those are enumerated in [§13 Known Deviations](#13-known-deviations). Do not copy a deviation just because it is nearby in the tree. If you find code that contradicts this document and is *not* listed in §13, the document is stale: fix the document in the same PR. --- ## 1. Core Principle > **The photograph is the only light source. Everything else is glass in front of it.** Chrome is dark, semi-transparent, and made of *material* that the content shows through. Depth comes from stacking translucent layers, not from drop shadows. The accent colour is not the brand's — it is derived from the current photo. Every control that is not needed right now recedes to zero opacity. This is Apple's platform language ported to the web: UIKit semantic colours, `UIVisualEffectView` materials, and SwiftUI's `spring(duration:bounce:)` motion model. --- ## 2. Colour ### 2.1 Everything is a semantic token The palette comes from `tailwindcss-uikit-colors/v4/macos.css` (imported in `apps/web/src/styles/tailwind.css`). There are four families, and hierarchy is expressed by walking *down a family*, never by picking a different hue. | Family | Use for | Ladder | | --- | --- | --- | | `text-text-*` | Foreground text | `text-text` → `-secondary` → `-tertiary` → `-quaternary` → `-quinary` | | `bg-fill-*` / `border-fill-*` | Opaque-ish control and surface fills, hairlines | `-fill` → `-secondary` → `-tertiary` → `-quaternary` → `-quinary` | | `bg-material-*` | Translucent surfaces sitting over content (see §3) | `-ultra-thin` → `-thin` → `-medium` → `-thick` → `-ultra-thick` → `-opaque` | | System hues (`red`, `green`, `blue`, `orange`…) | Semantic status only | — | **Rules** - Never write a raw hex, `rgb()`, or a Tailwind neutral ramp (`gray-*`, `zinc-*`, `slate-*`) for UI chrome. The only legitimate raw hex is a third-party brand mark (e.g. the Google `G` in `SignInPanel`). - Never hand-roll a light/dark pair (`text-gray-900 dark:text-gray-50`). UIKit tokens already resolve per scheme; writing the pair yourself means you picked the wrong token. - `white/N` and `black/N` are allowed **only** on top of a photograph, where the backdrop is an unknown image rather than an app surface — e.g. the viewer's chrome buttons (`text-white`, `hover:bg-black/40`) and `GlassButton` (`border-white/10`). ### 2.2 Accent is content-derived, in two stages 1. **Site level.** `siteConfig.accentColor` is injected as `--color-accent` / `--color-primary` / `--color-secondary` by an inline `