Skip to content

Scope: In-Scope Source Trees

This skill applies to every file that renders DraftForge UI in the frontend. The brand is one — feature-specific or page-specific exemptions don't exist.

In-Scope

Tree Purpose
frontend/app/components/ Shared components — ui/, user/, events/, etc. The shadcn primitives in components/ui/*.tsx are review targets when callers misuse them.
frontend/app/components/ui/buttons/ Brand button library (PrimaryButton, SecondaryButton, SubmitButton, ConfirmButton, EditButton, NavButton, DestructiveButton, CancelButton, plus icons/ and domain folders). Adding a new button MUST follow the README convention.
frontend/app/routes/ React Router route components. Top-level page UI.
frontend/app/features/ Feature-scoped components and hooks. Page-specific buttons, dialogs, forms.
frontend/app/app.css Token SSOT (CSS variables, --primary, --base-*, gradients, glows). Token additions must be reflected in THEMING-GUIDE.md.
frontend/tailwind.config.js Tailwind theme extension (custom colors, bg-base-* scale, --ring). Changes here ripple through every component.

Out of Scope

  • frontend/app/components/ui/*.tsx — the bare shadcn primitives (button.tsx, dialog.tsx, etc.). These define the brand contract; review here is for callers. Use the ui-styling or shadcn skills if changing the primitives themselves.
  • Build tooling: vite.config.ts, tsconfig.*.json, eslint.config.js, playwright.config.ts.
  • Generated bindings, type stubs.
  • E2E tests under frontend/tests/ — selectors are the contract, not the styling. (Exception: if a test asserts a specific brand class, that's a brand contract; review the assertion.)
  • Backend Python (backend/), Django templates, Daphne/Channels routing.
  • Docs (docs/), nginx config, Docker config.

Special Cases

Mandatory components

Two components MUST be used wherever applicable; raw markup is a block:

Use case Required component Why
Showing a user's avatar <UserAvatar> from ~/components/user/UserAvatar Handles Discord CDN, fallback initials, online indicator, captain ring, memoization. Raw <img> with AvatarUrl() is a known regression source.
Detail-page breadcrumb on org/league/event/series/tournament/rollcall <EntityBreadcrumb> from ~/components/ui/entity-breadcrumb Renders the typed segment labels ("ORGANIZATION" above "DTX") and disables the last segment automatically. Manual breadcrumb nav is a block.

See THEMING-GUIDE.md §"Component Patterns" for the full mandatory list.

Button policy

Per THEMING-GUIDE.md §"Button Policy": all user-facing actions must use a brand button wrapper, not raw <Button> from ~/components/ui/button. Raw <Button> is reserved for structural uses only — dropdown triggers, combobox triggers, shadcn internals.

A raw <Button> with onClick that performs a user-facing action is a block.

Dialog / AlertDialog surfaces

Dialog and AlertDialog content automatically include the brandBg overlay (see THEMING-GUIDE.md §"Brand Surface Background"). Adding bg-gradient-to-* to a DialogContent would silently strip bg-background (tailwind-merge group collision) — do not do it. Use [background-image:var(--brand-bg)] arbitrary property if a custom overlay is needed.

Width convention (see THEMING-GUIDE.md §"Dialog Widths"):

Dialog kind DialogContent width Examples
Form-heavy (edit/signup/create with multiple sections) sm:max-w-2xl EditProfileModal, EventSignupModal, CreateEventModal
Decision (single question, two buttons; or destructive with typed-name gate) default (sm:max-w-md) — no override ConfirmDialog, DeleteDialog. DeleteDialog composes ConfirmDialog and inherits the width.
Wide picker / table / chart (rare) sm:max-w-3xl or larger, only with a justification comment bracket pickers, large team rosters

A form-heavy DialogContent that ships with no sm:max-w-* override defaults to the shadcn sm:max-w-lg — too narrow for multi-field forms. The content then wraps awkwardly or spawns a horizontal scrollbar inside the modal. Reviewers should flag this as warn.

Long forms inside DialogContent MUST scroll via <ScrollArea> (from ~/components/ui/scroll-area), not raw overflow-y-auto — the brand-styled thin scrollbar replaces the chunky OS default and matches the rest of the app.

Token vs literal

The brand uses semantic tokens (bg-primary, bg-base-900, text-foreground, text-success) that map to oklch values in app.css / tailwind.config.js. Raw Tailwind color classes (bg-violet-500, text-slate-100) bypass the token layer.

Exception: error surface containers explicitly use raw red-900 / violet-900 per THEMING-GUIDE.md §"Status Colors" — that's intentional, not drift. The semantic --error token (rose-500) is for inline accents only.