genesis+worker: design system Genesis ทั้งชุด + patch pdf.worker แก้ fake-space ไทย + ปิด review design/responsive/quality รอบ 2026-09-24 — (1) Genesis: ฟอนต์ General Sans self-host + DM Sans + Sarabun, AppNav 56px backdrop-blur แทน SessionBar, DotGrid หน้า login, palette/รัศมี/เงา/focus ตาม docs/DESIGN.md ครบทุกหน้า (2) postinstall scripts/patch-pdf-worker.mjs ถอด if (scaledDim) รอบ prevTransform ให้อัปเดตทุก glyph — กัน addFakeSpaces แทรก space กลางคำไทยใน PDF ที่ justify ด้วย letter-spacing + test กัน regression (tests/scripts/ + fixture thai_letterspacing.pdf, invariant #6) (3) ฟอร์มอัปโหลด + ช่องกลุ่ม typeId ทางเลือก — normDocumentGroup ที่ utils/external-file.ts เป็น single source ร่วมกับ N8 (4) token fixes: focus ring ปุ่ม pin + แถว bookmark ตามหน้า, ปุ่ม action ใน popup/composer/dialog เป็น rounded-md ทั้งคู่, selection preview เป็น tint ของ primary แทน blue-500 ดิบ, default สี shape/highlight รวมที่ utils/annotation-display, loading animate-pulse + จดข้อยกเว้น (เงา pin, font weights ของ viewer) ลง DESIGN.md (5) responsive: Export PDF max-lg:order-first ให้เห็นบน iPad, แถวบน header แถวเดียวเลื่อนได้บนจอแคบ, re-fit ความกว้างตอน toggle sidebar เฉพาะเมื่อยังไม่ซูมเอง, pin popover flip ขึ้นบนเมื่อใกล้ท้ายหน้า, toast hint ตอนสลับ tool กลางเอกสาร, ป้าย "ชนิด · ผู้ทำ" ของรายการผู้อื่นสำหรับจอสัมผัส (6) คุณภาพ: @layer ครอบ @media กันกฎ touch รั่ยไป desktop (Tailwind v3 ยก @layer ออกจาก media ทิ้ง wrapper — ยืนยันด้วย build จริง + วัดบนจอ), กฎ M5 ครอบ contenteditable กัน Safari zoom ตอนแก้ไข TEXTBOX บน iPad, COMMENT ใช้ box มาตราฐานเดียวตาม pin px จริง (render/scroll target/move-clamp/bookmark), ตัดหัวการ์ดซ้ำกับ h2 หน้ารายการ — พร้อมไฟล์ untracked ที่ postinstall/โค้ดอ้างถึงครบชุด (app/fonts/, AppNav, DotGrid, tests/) กัน fresh clone fail ที่ npm install (คำขอผู้ใช้ 2026-09-24)

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
Your Name 2026-09-24 11:46:02 +07:00
parent e254b935ce
commit c5c6b65bd2
42 changed files with 1429 additions and 745 deletions

78
docs/DESIGN.md Normal file
View file

@ -0,0 +1,78 @@
# Genesis
## Overview
An editorial precision interface for **Judge's Note** — a Thai court PDF annotation app (judes-note) used daily by judges on desktops and iPads. The aesthetic is quietly confident — bold display typography, generous spacing, and gallery-frame card surfaces. The mood is professional and modern without being sterile. High information density balanced by breathing room. (Text is primarily Thai; Latin display type carries the brand voice in headings and numbers.)
## Colors
- **Primary** (rgb(0 51 161 / <alpha-value>) / #0033A1): CTAs, active states, links, focus indicators, interactive highlights — royal blue
- **Primary Hover** (#002780): Darker blue for hover states on primary elements
- **Neutral** (#9C9C9C): Placeholders, disabled states — decorative metadata only (see `ink` families for readable text)
- **Background** (#FAFAFA): Page background, light warm gray
- **Surface** (#FFFFFF): Cards, panels, modals, nav backdrop
- **Text Primary** (#0A0A0A): Headings, body text, primary labels — near-black
- **Text Secondary** (#6B6B6B): Descriptions, metadata, secondary labels — passes 4.5:1 on white; use for meaningful small text (dates, counts, authors)
- **Border** (#E8E8EC): Card borders, dividers, input borders — subtle and recessive
- **Success** (#10B981): Positive status — borders and tinted backgrounds (`success/10`–`/15`)
- **Warning** (#F59E0B): Pending status, caution — borders and tinted backgrounds (`warning/10`–`/15`)
- **Error** (#EF4444): Destructive actions, validation errors — borders and tinted backgrounds (`error/10`–`/15`)
- **Success Ink** (#047857), **Warning Ink** (#B45309), **Error Ink** (#B91C1C): Text- and icon-safe variants of the status colors (≥5:1 on white). Any status *text or icon* on a light surface uses the `-ink` token; the base 500 tokens are for fills, borders, and tints only. Never reach for raw Tailwind `amber-*`/`emerald-*`/`red-*` shades.
Note: the template's Secondary green (#20970B) has no role in this app and is not registered as a token. Code font (JetBrains Mono) is likewise unused — no code UI in this app.
## Typography
- **Display Font**: General Sans — loaded from Fontshare
- **Body Font**: DM Sans — loaded from Google Fonts
- **Code Font**: JetBrains Mono — loaded from Google Fonts
Display and heading text uses General Sans at bold weight with tight letter spacing (-0.03em to -0.04em). Body and UI text uses DM Sans at regular and medium weights. The contrast between the geometric display font and the humanist body font creates a refined editorial feel. Code blocks, API keys, and CLI commands use JetBrains Mono at regular weight.
Type scale: Display 72px, Headline 60px, Section heading 32px, Subhead 24px, Body 15px, Small 13px, Caption 12px, Overline 11px uppercase.
## Elevation
This design uses minimal shadows. Cards rest flat with a 1px border and gain a subtle shadow on hover (0 8px 30px rgba(0,0,0,0.08)) combined with a -2px vertical lift. Primary buttons gain a tinted glow shadow on hover (0 4px 12px rgba(0,51,161,0.35)). The nav uses backdrop-blur rather than a shadow to convey elevation. Dropdowns and popovers use shadow-lg. Focus indicators use a 2px solid royal-blue outline with a 2px offset (≥3:1 against adjacent surfaces, WCAG 1.4.11/2.4.13) — a translucent halo alone is too faint to qualify.
## Components
- **Buttons**: Primary uses blue fill with white text, 6px radius, medium weight. Secondary uses transparent bg with 1px border, same radius. Ghost has no border or bg, just text color change on hover. Destructive uses red text with red border. All buttons shift up 1px on hover. Sizes: small (32px), medium (38px), large (44px).
- **Cards**: White surface, 1px subtle border, 12px radius, overflow hidden. Content cards (file cards, form cards) stack a content area with title, metadata, and actions. Hover lifts the card 2px and increases shadow. Transition duration 200ms.
- **Inputs**: 1px subtle border, surface background, 6px radius, 8px vertical and 14px horizontal padding, 14px font size (16px on touch devices — iOS zooms on focus below 16px). Focus: border turns blue with the 2px outlined focus indicator. Error: border turns red. Placeholder text uses muted color.
- **Chips**: Tag chips use rounded-full (pill shape), gray-100 background, gray-600 text, 4px vertical and 12px horizontal padding, 12px font size. Active state: blue background with white text. Status chips follow the same shape but use semantic colors (green for published, yellow for pending, red for rejected).
- **Lists**: Stacked rows with 1px dividers between items. Each row is flex with space-between, 12px vertical and 16px horizontal padding. Hover: subtle background change.
- **Checkboxes**: 20px size, rounded-full, gray-200 unchecked, blue checked with white checkmark. Used as toggle switches for preferences.
- **Tooltips**: Native browser tooltips via title attributes. No custom tooltip component currently.
- **Navigation**: Sticky top nav with backdrop-blur, 56px height, 1px bottom border. Logo left, links center (desktop) or hamburger drawer (mobile), user avatar dropdown right. Nav links: 14px medium weight, hover shows bg-alt background.
- **Search**: Global search triggered by ⌘K. Rendered as a rounded-xl bar with magnifying glass icon and keyboard shortcut badge.
## Spacing
- Base unit: 4px
- Scale: 4, 8, 12, 16, 20, 24, 32, 40, 48, 64, 80, 96px
- Component padding: small 8×12, medium 10×16, large 12×24
- Section spacing: 32px mobile, 48px tablet, 64px desktop
- Container max width: 1280px with 24px horizontal padding
- Card grid gap: 20-24px
## Border Radius
- 4px: Tags, chips, badges, inline code — and menu/list rows inside dropdowns, panels, and popovers (a row belongs to its container, not a standalone button)
- 6px: Buttons, inputs, selects — including small action buttons inside popups, composers, and dialogs; a cancel/save pair must always share the same radius
- 8px: Metadata cards, dropdowns, panels
- 12px: Kit preview cards, search bar, featured sections
- 9999px: Avatars, status dots, pill badges
## Do's and Don'ts
- Do use blue (rgb(0 51 161 / <alpha-value>)) only for interactive elements — never for decoration or static text. Exceptions (brand marks, agreed 2026-09-23): the JN logo tile, and the blue header band of the summary page. Static numbers (e.g. folio numbers) use `ink`.
- Do maintain the 4px spacing grid for all padding, margins, and gaps
- Do use General Sans for headings and DM Sans for body — never swap them
- Do keep kit cards at 12px radius and buttons/inputs at 6px — don't mix these values
- Do provide sufficient contrast in both light and dark modes — test both
- Do use the `-ink` status tokens for status text/icons and the 500 tokens for fills, borders, and tints
- Don't use raw Tailwind palette shades (slate, amber, emerald, red…) — only the tokens registered in tailwind.config.ts
- Don't use pure black (#000000) or pure white (#FFFFFF) for text — use the defined palette values
- Don't add decorative gradients or illustrations — the interactive dot grid is the only decorative element
- Don't use shadows on static elements — reserve shadow elevation for hover and focus states (floating layers and document pins excepted — see App-specific exceptions)
- Don't use more than two font weights on a single screen (dense working-surface screens such as the document viewer are excepted — see App-specific exceptions)
- Don't place more than one primary (filled blue) button in the same view section
## App-specific exceptions
- The document viewer header is a solid white bar (not the nav's backdrop-blur): it hosts a dense tool row where blur would smear the scrolled document beneath. Only the site nav (AppNav) blurs.
- Comment pins on the document page carry a small static shadow: they are floating markers that must lift off the paper layer, in the same spirit as dropdown/popover elevation (agreed 2026-09-24).
- The document viewer (working surface) uses three font weights on one screen — 400 body, 500 labels/buttons, 600 panel/file titles. The two-weight rule still holds for content pages (agreed 2026-09-24).
- The text-selection preview in the viewer (drag-preview tint of the select/comment/textbox/stamp tools) is a translucent tint of primary (`rgb(0 51 161 / 0.3)`), keeping every blue on the screen in the brand hue (agreed 2026-09-24).