doc: add styleguide

This commit is contained in:
Marcel
2026-03-17 18:06:50 +00:00
parent ed32e1728d
commit 3b04f4cafe

390
docs/STYLEGUIDE.md Normal file
View File

@@ -0,0 +1,390 @@
# Familienarchiv — Design Styleguide
This document defines the visual language for the Familienarchiv frontend. All UI work should follow these conventions to stay consistent with the De Gruyter Brill corporate identity.
---
## Brand Identity
The design is based on the **De Gruyter Brill** brand identity (unveiled at Frankfurt Book Fair 2024). Key characteristics:
- Clean white backgrounds, high contrast
- Strong typographic hierarchy (uppercase labels, serif body text)
- Academic publisher aesthetic: authoritative, clear, uncluttered
- The Melrose purple (`#B4B9FF`) quotation-mark logo is the primary brand signature
---
## Colors
Defined in `src/routes/layout.css` as `@theme` variables. All generate Tailwind utilities automatically (`bg-*`, `text-*`, `border-*`).
| Token | Hex | Usage |
|---|---|---|
| `brand-navy` | `#012851` | Primary text, headings, buttons, active states — Prussian Blue |
| `brand-mint` | `#A1DCD8` | Accent color, icon tints, hover underlines — Aqua Island |
| `brand-purple` | `#B4B9FF` | Logo, nav active state highlight, top accent strip — Melrose |
| `brand-sand` | `#F0EFE9` | Subtle card backgrounds, borders, hover backgrounds — paper tone |
| `brand-white` | `#FFFFFF` | Page background, card surfaces |
| `brand-dark` | `#0D0D0D` | Near-black text when maximum contrast is needed |
### Color usage rules
- **Never** use raw hex values in components — always use token utilities.
- Page and card backgrounds are **white**. Use `bg-brand-sand` only for subtle inset areas (e.g. `bg-brand-sand/30`).
- `brand-navy` is the workhorse: headings, body text, borders, primary buttons.
- `brand-mint` is an accent only — never use it as primary text color on white (contrast too low).
- `brand-purple` is reserved for the logo and the single top accent strip in the header.
---
## Typography
### Fonts
| Role | Font | Tailwind | Notes |
|---|---|---|---|
| Body / Serif | **Tinos** (Times substitute) | `font-serif` | Loaded from Google Fonts. Used for document titles, names, body copy, dates. Matches DGB's use of Times. |
| UI / Sans | **Montserrat** (Gotham substitute) | `font-sans` | Loaded from Google Fonts. Used for labels, navigation, buttons, metadata, form elements. Matches DGB's use of Gotham. |
### Type scale and usage
| Element | Classes | Example |
|---|---|---|
| Page title | `font-serif text-3xl text-brand-navy` | `<h1>` |
| Card section heading | `font-sans text-xs font-bold uppercase tracking-widest text-gray-400` | Section labels |
| Document / item title | `font-serif text-xl font-medium text-brand-navy` | List items |
| Metadata / label | `font-sans text-xs font-bold uppercase tracking-widest text-gray-500` | Field labels |
| Body text | `font-serif text-sm text-brand-navy` | Descriptions, summaries |
| Navigation | `font-sans text-xs font-bold uppercase tracking-widest` | Nav links |
### Rules
- **Labels are always uppercase + tracked**: `text-xs font-bold uppercase tracking-widest`
- **Headings** use `font-sans` (Montserrat), set in CSS globally.
- **Content** (document titles, person names, summaries) uses `font-serif` (Tinos).
- Never use `font-serif` for UI chrome (buttons, labels, nav).
---
## Icons
### Library
686 SVG icons in `frontend/static/degruyter-icons/`. Two families:
- **Simple** — single-color, action-oriented. Use for all UI icons. Available in 4 sizes.
- **Complex** — multi-color illustrative icons. Use sparingly for empty states or section headers.
### Simple icon sizes
| Size | Pixels | Path segment | Use |
|---|---|---|---|
| XS | 12px | `X-Small-12px` | Inline text hints, badges |
| SM | 16px | `Small-16px` | Compact UI, table cells |
| **MD** | **24px** | **`Medium-24px`** | **Standard UI icon — default choice** |
| LG | 32px | `Large-32px` | Feature headers, empty states |
### URL pattern
```
/degruyter-icons/Simple/{Size}/SVG/{Category}/{Name}-{Size-Code}.svg
```
**Size codes:** `XS`, `SM`, `MD`, `LG`
### Usage as `<img>` (recommended for static icons)
SVG fills are hardcoded to `#000000`. Use CSS to tint/size them:
```svelte
<!-- Standard icon -->
<img src="/degruyter-icons/Simple/Medium-24px/SVG/Action/Edit-Content-MD.svg"
alt="" aria-hidden="true" class="w-6 h-6" />
<!-- Muted/secondary icon -->
<img src="/degruyter-icons/Simple/Medium-24px/SVG/Action/Edit-Content-MD.svg"
alt="" aria-hidden="true" class="w-6 h-6 opacity-40" />
<!-- Colored via CSS filter (navy tint) -->
<img src="/degruyter-icons/Simple/Medium-24px/SVG/Action/Edit-Content-MD.svg"
alt="" aria-hidden="true"
class="w-6 h-6"
style="filter: invert(11%) sepia(58%) saturate(1200%) hue-rotate(192deg) brightness(95%) contrast(101%)" />
```
> **Note:** Always include `alt=""` and `aria-hidden="true"` for decorative icons. For meaningful icons (no visible label next to them), use a descriptive `alt` text instead.
### Key icons for this app
| Use case | Icon path |
|---|---|
| Edit / Bearbeiten | `Action/Edit-Content-MD.svg` |
| Search / Suche | `Action/Mag-Glass-MD.svg` |
| New document | `Action/Add/Add-General-MD.svg` |
| Download | `Action/Download-MD.svg` |
| Upload | `Action/Upload-MD.svg` |
| Filter | `Action/Filter/Filter-Outline-MD.svg` |
| Calendar / date | `Action/Calendar/Calendar-Add-MD.svg` |
| Location | `Action/Location-MD.svg` |
| Person / account | `Action/Account-MD.svg` |
| Chat / conversation | `Action/Chat-MD.svg` |
| Tag / bookmark | `Action/Bookmark/Bookmark-Outline-MD.svg` |
| Close / dismiss | `Action/Close-MD.svg` |
| Back / left arrow | `Action/Arrow/Arrow-Left-MD.svg` |
| Settings / admin | `Action/Settings-MD.svg` |
| Document / PDF | `Action/PDF-Document-MD.svg` |
| Mail | `Action/Mail-MD.svg` |
| Delete | `Action/Remove/Remove-General-MD.svg` |
| Info | `Action/Info/Block/Info-Block-Border-MD.svg` |
---
## Spacing
Based on Tailwind's 4pt grid. Prefer multiples of 4 for all spacing.
| Scale | Value | Use |
|---|---|---|
| `p-1` / `gap-1` | 4px | Tight inline spacing |
| `p-2` | 8px | Small padding (badges, chips) |
| `p-3` | 12px | Compact buttons |
| `p-4` | 16px | Default section padding |
| `p-6` | 24px | Card inner padding (default) |
| `p-8` | 32px | Large card padding |
| `p-10` | 40px | Page vertical padding |
| `gap-6` | 24px | Grid/list gaps |
| `mb-6` | 24px | Standard spacing between sections |
| `mb-10` | 40px | Large spacing between card sections |
---
## Layout
### Page wrapper
All content pages use:
```svelte
<div class="max-w-7xl mx-auto py-8 px-4 sm:px-6 lg:px-8">
```
Narrower pages (forms, detail views):
```svelte
<div class="max-w-4xl mx-auto py-10 px-4">
```
### Header
The global sticky header in `+layout.svelte`:
- Height: **68px** (4px purple accent strip + 64px nav bar)
- Background: `bg-white`
- Bottom border: `border-b border-gray-100`
- Z-index: `z-50`
### Full-screen views
Document detail (`/documents/[id]`) uses a full-viewport split layout:
```svelte
<div class="h-screen flex flex-col bg-white">
<!-- top bar -->
<!-- content: sidebar + preview -->
</div>
```
---
## Components
### Card
Standard content card:
```svelte
<div class="bg-white shadow-sm border border-brand-sand rounded-sm p-6">
<h2 class="text-xs font-bold uppercase tracking-widest text-gray-400 mb-5">
Section Title
</h2>
<!-- content -->
</div>
```
Card with colored accent bar (person/document detail):
```svelte
<div class="bg-white shadow-sm border border-brand-sand rounded-sm overflow-hidden">
<div class="h-2 bg-brand-navy w-full"></div>
<div class="p-8 md:p-10">
<!-- content -->
</div>
</div>
```
### Buttons
Primary button:
```svelte
<button class="bg-brand-navy text-white px-5 py-2 text-xs font-bold uppercase tracking-widest font-sans hover:bg-brand-navy/90 transition-colors">
Speichern
</button>
```
Secondary / outline button:
```svelte
<button class="border border-gray-300 text-gray-600 px-5 py-2 text-xs font-bold uppercase tracking-widest font-sans hover:bg-gray-50 transition-colors rounded-sm">
Abbrechen
</button>
```
Ghost / text button (inline actions):
```svelte
<button class="text-brand-navy/60 hover:text-brand-navy text-sm font-medium font-sans transition-colors">
Aktion
</button>
```
Destructive button:
```svelte
<button class="border border-red-300 text-red-600 px-4 py-2 text-xs font-bold uppercase tracking-widest font-sans hover:bg-red-50 transition-colors rounded-sm">
Löschen
</button>
```
Button with DGB icon:
```svelte
<button class="inline-flex items-center gap-2 bg-brand-navy text-white px-4 py-2 text-xs font-bold uppercase tracking-widest font-sans hover:bg-brand-navy/90 transition-colors">
<img src="/degruyter-icons/Simple/Small-16px/SVG/Action/Edit-Content-SM.svg"
alt="" aria-hidden="true" class="w-4 h-4 invert" />
Bearbeiten
</button>
```
> Use `class="invert"` on icons inside dark (navy) buttons to make the black SVG white.
### Form inputs
Label + input pair:
```svelte
<div>
<label for="field" class="block text-xs font-bold uppercase tracking-widest text-gray-500 mb-1.5 font-sans">
Feldname *
</label>
<input
id="field" name="field" type="text"
class="block w-full border border-gray-300 py-2.5 px-3 text-sm font-serif text-brand-navy placeholder-gray-400 focus:border-brand-navy focus:ring-1 focus:ring-brand-navy focus:outline-none"
/>
</div>
```
Search input:
```svelte
<div class="relative">
<input type="text" placeholder="Suchen..."
class="block w-full border border-gray-300 py-2.5 pr-10 pl-3 font-sans text-sm text-brand-navy placeholder-gray-400 focus:border-brand-navy focus:ring-1 focus:ring-brand-navy focus:outline-none" />
<div class="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-3">
<img src="/degruyter-icons/Simple/Medium-24px/SVG/Action/Mag-Glass-MD.svg"
alt="" aria-hidden="true" class="w-4 h-4 opacity-40" />
</div>
</div>
```
### Status badges
```svelte
<!-- Mint (uploaded/active) -->
<span class="inline-flex items-center rounded-full border border-brand-mint/50 bg-brand-mint/20 text-brand-navy px-2.5 py-0.5 text-[10px] font-bold tracking-wide uppercase font-sans">
UPLOADED
</span>
<!-- Yellow (placeholder/pending) -->
<span class="inline-flex items-center rounded-full border border-yellow-200 bg-yellow-50 text-yellow-700 px-2.5 py-0.5 text-[10px] font-bold tracking-wide uppercase font-sans">
PLACEHOLDER
</span>
```
### Tag chips
```svelte
<button class="inline-flex items-center rounded bg-brand-sand/30 px-2 py-1 text-[10px] font-bold tracking-widest text-brand-navy uppercase font-sans transition-colors hover:bg-brand-navy hover:text-white">
Schlagwort
</button>
```
### Back link
```svelte
<a href="/persons" class="inline-flex items-center text-xs font-bold uppercase tracking-widest text-gray-500 hover:text-brand-navy transition-colors group mb-4 font-sans">
<img src="/degruyter-icons/Simple/Medium-24px/SVG/Action/Arrow/Arrow-Left-MD.svg"
alt="" aria-hidden="true"
class="w-4 h-4 mr-2 opacity-40 group-hover:opacity-100 transition-opacity" />
Zurück zur Übersicht
</a>
```
### Subtle "new item" link
```svelte
<a href="/documents/new" class="inline-flex items-center gap-1 text-sm font-medium text-brand-navy/60 hover:text-brand-navy transition-colors font-sans">
<img src="/degruyter-icons/Simple/Medium-24px/SVG/Action/Add/Add-General-MD.svg"
alt="" aria-hidden="true" class="w-4 h-4 opacity-60" />
Neues Dokument
</a>
```
### Empty state
```svelte
<div class="p-16 text-center">
<div class="mx-auto mb-4 w-16 h-16 flex items-center justify-center">
<img src="/degruyter-icons/Simple/Large-32px/SVG/Action/Mag-Glass-LG.svg"
alt="" aria-hidden="true" class="w-10 h-10 opacity-20" />
</div>
<h3 class="font-serif text-lg font-medium text-brand-navy">Keine Dokumente gefunden</h3>
<p class="mt-1 font-sans text-sm text-gray-500">Versuchen Sie, die Filter anzupassen.</p>
</div>
```
### Nav active state
Current page nav link:
```svelte
class="text-brand-navy bg-brand-purple/15 rounded"
```
Inactive nav link:
```svelte
class="text-gray-500 hover:text-brand-navy hover:bg-brand-sand/60 rounded"
```
Both share the base: `inline-flex items-center px-3 py-1.5 text-xs font-bold uppercase tracking-widest font-sans transition-colors`
### Save bar (long forms)
Sticky full-bleed (document edit):
```svelte
<div class="sticky bottom-0 z-10 -mx-4 px-6 py-4 bg-white border-t border-brand-sand shadow-[0_-2px_8px_rgba(0,0,0,0.06)] flex items-center justify-between">
<a href="..." class="text-xs font-bold uppercase tracking-widest text-gray-500 hover:text-brand-navy font-sans transition-colors">
Abbrechen
</a>
<button type="submit" class="bg-brand-navy text-white px-6 py-2.5 text-xs font-bold uppercase tracking-widest font-sans hover:bg-brand-navy/90 transition-colors">
Speichern
</button>
</div>
```
Card-style (short forms):
```svelte
<div class="mt-4 flex items-center justify-between rounded-sm border border-brand-sand bg-white px-6 py-4 shadow-sm">
```
---
## Do / Don't
| Do | Don't |
|---|---|
| Use `font-sans` for all UI labels, buttons, nav | Use `font-serif` for buttons or labels |
| Use `uppercase tracking-widest` for labels | Use sentence case for field labels |
| Use `brand-navy` for primary actions | Use `brand-mint` as primary action color |
| Use `opacity-*` to create icon tints | Change icon fill color inline |
| Use `invert` class on icons inside `bg-brand-navy` buttons | Use colored icon files for button icons |
| Use `rounded-sm` for cards and buttons (subtle) | Use `rounded-full` for non-pill elements |
| Use `shadow-sm` for card elevation | Use large shadows |
| Keep borders `border-gray-100` or `border-brand-sand` | Use dark borders |