DESIGN.md · amontapdum.org
Nagi Glass 凪硝子
สเปกดีไซน์ของทั้งโดเมน — สี ตัวอักษร ระยะ และชิ้นส่วนที่ใช้จริง ทุกค่าตรงกับ CSS ที่รันอยู่ และ export ไปใช้กับ agent ได้
amontapdum.org is one portal that hosts several self-run systems: KHLANG (shop back office and POS), JERNEY (student activity registration), MUSUBI (link hub with click stats) and the shared account app. The design language is Nagi Glass: quiet Japanese calm expressed through a single soft motion curve. Native disclosure controls on the design document animate open and closed without changing their native keyboard semantics.
The window metaphor matters: every page is wrapped in a rounded frame with an even inset on all four sides, so the whole domain reads as one application window, not a set of loose pages.
The mood references rain, still water, hydrangea and evening lantern light. Japanese atmosphere comes from spacing, restraint and light — never from sakura, torii or decorative kanji.
Color palette
สีทั้งหมด · ค่า HEX และคอนทราสต์ที่วัดจากของจริง
primary
ปุ่มหลัก แท็บที่เลือก โฟกัส
15.8:1 บน surface · AAA
on-primary
ตัวอักษรบนพื้น primary
15.8:1 บน primary · AAA
secondary
ลิงก์เงียบ · เน้นรอง
4.6:1 บน surface · AA
tertiary
ไฮเดรนเยีย — ไล่เฉด/ภาพประกอบ
3.1:1 บน surface · AA-large
neutral
พื้นหน้า · พื้นช่องกรอก
พื้นผิว / เส้น — ไม่ใช้กับข้อความ
surface
แผงทึบ (เมนู, โมดัล)
พื้นผิว / เส้น — ไม่ใช้กับข้อความ
ink
ตัวอักษรหลัก
18.9:1 บน surface · AAA
muted
คำอธิบายรอง · เมตาดาตา
5.6:1 บน surface · AA
line
เส้นคั่นบาง
พื้นผิว / เส้น — ไม่ใช้กับข้อความ
desk
พื้นโต๊ะรอบกรอบหน้าต่าง
พื้นผิว / เส้น — ไม่ใช้กับข้อความ
glass
พื้นการ์ดโปร่งขุ่น · ใช้ที่ 64% + blur
พื้นผิว / เส้น — ไม่ใช้กับข้อความ
brand-jerney
สีแบรนด์ JERNEY
6.8:1 บน surface · AA
success
—
5.3:1 บน surface · AA
warning
—
5.4:1 บน surface · AA
danger
—
6.5:1 บน surface · AA
- Primary (#042249): deep night navy. The single interaction color for the hub and KHLANG —
active tab fills, primary buttons, focus rings. 15.8:1 on white.
- On-primary (#FFFFFF): text and icons placed on a primary fill. It stays white even when the
device asks for a dark scheme, because the fill stays navy.
- Secondary (#2E7D9E): still-water teal for quiet links and secondary emphasis.
- Tertiary (#7D8FD9): hydrangea blue-purple used for accents, gradients and illustration.
- Neutral (#E9EEF5): the page wash — cool paper that keeps glass cards readable.
- Surface (#FFFFFF): opaque panel color for menus and modals. Panels must be opaque in front
of text; only cards may be translucent.
- Ink (#0A0E2D): body text. Muted (#5D687D): captions, metadata, secondary labels.
- Line (#E3E8F0): hairline dividers.
- Desk (#FFFFFF): the surface around the window frame. White in light mode,
#03060Cin dark. - Success (#1B7A4B), Warning (#9A5B00), Danger (#B3261E): semantic fills for confirm, caution and
destructive actions. All three clear AA on white (5.3, 5.4 and 6.5:1) and carry white text. They keep these light-content values in both device schemes.
- Glass (#FAFBFF): the base of every card. Cards are drawn at ~64% alpha over a tinted wash and
blurred, so the value reads as a cool frosted white, never a flat panel.
- Brand JERNEY (#6D3AC6): JERNEY keeps its violet identity on the light surface. A lighter
#B79BF0 remains reserved for text placed on a dark photograph or overlay, never for a dark page.
Measured contrast (WCAG AA needs 4.5:1 for text)
| Pair | Ratio | Verdict |
|---|---|---|
ink #0A0E2D on surface #FFFFFF | 18.9:1 | AAA |
primary #042249 on #FFFFFF | 15.8:1 | AAA |
on-primary #FFFFFF on primary #042249 | 15.8:1 | AAA |
muted #5D687D on #FFFFFF | 5.6:1 | AA |
secondary #2E7D9E on #FFFFFF | 4.6:1 | AA |
brand-jerney #6D3AC6 on #FFFFFF | 6.8:1 | AA |
Verify these with lint; the CLI checks every component's textColor against its backgroundColor and warns below 4.5:1.
Dark mode
Content is light in every scheme. There is no dark content palette: pages, cards, dialogs, menus, tables and form controls keep the values above whether the device asks for light or dark. color-scheme: light is declared on :root so the user agent renders scrollbars, form widgets and autofill light as well.
Only the window reacts to the device theme — the desk around the frame and its hairline rim:
| Role | Light scheme | Dark scheme |
|---|---|---|
desk (--ng-desk) | #FFFFFF | #03060C |
rim (--ng-rim) | rgba(10,14,45,.16) | rgba(255,255,255,.18) |
The dark media query therefore contains exactly those two tokens and nothing else. Adding a content rule inside it is a defect: an app that painted itself dark in the dark scheme (KHLANG, JERNEY, MUSUBI, ACCOUNT) has been returned to the light values.
Typography
ตัวอักษร 6 ระดับ · Prompt สำหรับหัวข้อ (ไทย + ละติน) · Noto Sans Thai Looped สำหรับเนื้อหา · JetBrains Mono สำหรับโค้ด
Layout · spacing & radius
ระยะห่าง 7 ขั้น และรัศมีมุม 4 ระดับ
xs4pxsm8pxmd12pxlg16pxxl20px2xl24px3xl32pxsm12pxmd16pxlg28pxpill999pxA 4px base scale, used in this order: 4 · 8 · 12 · 16 · 20 · 24 · 32. The window frame uses a symmetric inset (7px desktop, 4px at ≤880px) — the same value on all four sides, never a different value to dodge a scrollbar.
Content columns stay narrow enough to keep line length comfortable; cards breathe rather than pack. Negative space is the primary luxury signal.
sm12px — inputs on small screens, small tiles.md16px — window frame, medium panels.lg28px — cards and modals.pill999px — every button, tab, chip and text field. Capsules are the identity.
Radii shrink with the element: a frame inset of 7px cannot carry a 26px radius without looking like a pill, so the frame radius is 16px.
Components
ชิ้นส่วนที่ใช้จริง พร้อม markup ตัวอย่าง
ระบบหลังร้านสำหรับร้านค้า จัดการสินค้าและสต็อก บันทึกการขาย ออกใบเสร็จ
เปิดใช้งาน →- window-frame — fixed rounded overlay around every page, symmetric inset (7px desktop, 4px at
≤880px), desk ring masking scrolled content; never a border on <body>.
- nav-shell — floating capsule bar holding the wordmark and link pills; horizontally snug,
collapsing to one menu button at ≤880px.
- nav-tab-active — filled brand-primary capsule with on-primary text in both device schemes.
- button-primary — the single high-emphasis action per view. JERNEY swaps in its own violet.
- button-secondary — white surface, 1px
lineborder,inklabel: the "look closer" action that
sits next to a primary. Never give it the primary fill — two filled buttons read as two equal choices.
- button-danger —
dangerfill, white label, used only for destructive actions (delete, revoke,
wipe). Pair it with a confirmation; never place it first in a row.
- chip-success / chip-warning — status pills; the dot or fill carries the meaning, the label stays
short ("พร้อมใช้งาน", "ใกล้เต็ม"). Red is reserved for failure and destructive intent, amber for attention, green for healthy — never reuse these three as decoration.
- card-glass — translucent card, one shadow, 28px radius, 24px padding.
- chip — neutral capsule for tags and status; neutral means "no judgement", semantic status uses
the success/warning/danger tokens instead.
- input-capsule — 48px tall pill field with 18px horizontal padding; background always set
explicitly and color-scheme: light keeps native fields consistent in both device schemes.
- link-quiet — teal text link, no underline until hover.
- divider-line — hairline separator in
line, paired with a muted caption.
Component previews sit directly on the section card — one layer, never a card inside a card. Groups are separated by a small uppercase label with a hairline under it, not by another bordered box. A control nested two cards deep loses its edge and the page starts to look like a mockup of itself.
The /designmd page keeps the main Download DESIGN.md action prominent, with copy and the visual/source switch beside it. Content and previews are locked to light; the dark-preview control is disabled. Only the outer window desk/rim follows the device scheme. Keep a single clear home navigation path, mark the current page with aria-current="page", and collapse technical metadata and agent-use details on narrow screens. Its navigation is for moving between pages only; do not add authentication or permission behavior to this document view.
Native disclosures
The design document's details controls animate both expansion and collapse using measured panel height and opacity. Keep native details/summary semantics so Enter and Space toggle them; preserve focus on the summary and keep closed content out of keyboard navigation. Use a visible +/− state, consistent hover, pressed, and focus-visible feedback, and allow panel height to follow content (never clamp or use a fixed maximum). Rapid toggles must cancel/finish the previous transition without stale callbacks. Re-measure segmented thumbs after disclosure layout settles. Under prefers-reduced-motion: reduce, remove animation while retaining instant native state changes. Copy feedback must reset; disabled demo controls must be semantically disabled; dialogs must restore focus to their trigger on close.
Motion
อนิเมชั่นชุดเดียวกับ musubi · เข้าและออกครบคู่
- การ์ดใบแรก+0ms
- การ์ดใบที่สอง+60ms
- การ์ดใบที่สาม+120ms
- การ์ดใบที่สี่+180ms
Motion is lifted whole from musubi: one enter curve, one exit curve, and every popup animates both ways. Nothing appears by cutting in, and nothing disappears by vanishing — the close is what makes the open feel deliberate.
| Token | Value | Use |
|---|---|---|
--ng-ease-out | cubic-bezier(.16,1,.3,1) | everything entering |
--ng-ease-in | cubic-bezier(.4,0,.7,1) | everything leaving |
--ng-dur | 300ms | popup / dialog enter |
--ng-dur-scrim | 240ms | backdrop fade in |
--ng-dur-fast | 180ms | inner rows leaving |
--ng-dur-slow | 440ms | the QR block / hero scale pop |
--ng-exit | 220ms | popup leave |
Enter: translateY(14px) scale(.965) → none, opacity 0 → 1, .30s on ease-out. Exit: none → translateY(10px) scale(.97), opacity → 0, .22s on ease-in. Inner content rises 8px on enter (.28s ease, staggered 0 / 60 / 130ms) and drops 4px on exit (.18s ease), so the panel and its contents move as one object instead of two.
Lists and grids: rise .36s on ease-out, staggered --d at 38–60ms per item, capped near 260ms. Past ten items the stagger stops adding delay — a long list should not take two seconds to appear.
Closing is a state, not an instant. Add .closing (or .ng-closing), let the exit animation run, then call dialog.close() on animationend with a setTimeout fallback. Calling close() first removes ::backdrop immediately and the dialog blinks out with no exit at all.
Usage. Markup carries the classes, the shared script carries the timing:
<button data-ng-open="#share">แชร์</button> <dialog class="ng-dialog" id="share" data-ng-dialog> <div class="ng-dialog-head">…</div> <div class="ng-dialog-body">…</div> <div class="ng-dialog-foot ng-seq"><button>ยกเลิก</button><button class="primary">คัดลอก</button></div> </dialog>
NagiGlass.dialog.open(el) / .close(el) handle showModal, the .ng-closing state, the animationend wait and the cancel (Esc) event. data-ng-open / data-ng-close bind themselves.
Reduced motion: every animation above is disabled under prefers-reduced-motion: reduce; the classes and the JS helper both check it. Never gate content behind an animation that a user turned off.
Elevation & depth
ความลึกมาจากความโปร่งใส + เงาชั้นเดียว
Depth comes from translucency plus one soft shadow — never from stacked borders or bevels.
- Card:
0 0 0 1px rgba(10,14,45,.05), 0 12px 34px rgba(86,96,160,.13)on a 64% white fill with
backdrop-filter: blur(22px) saturate(1.45).
- Lifted (popovers, submenus):
0 10px 26px rgba(10,14,45,.12). - Window frame: a 1px
rgba(10,14,45,.16)hairline plus a soft inset shadow. On a white desk
the rim must stay a faint ink hairline — a white ring disappears entirely.
Anti-neumorphism rule: no inset shadows, no dual light/dark bevels. A surface is either glass or paper; it never pretends to be carved.
Do's & Don'ts
กฎที่ยึด เพื่อไม่ให้ดีไซน์เพี้ยนระหว่างทาง
ควรทำ Do
- Keep content tokens light and change only outer desk/rim tokens with the device scheme.
- Keep panels that sit in front of text fully opaque.
- Measure the rendered result (
getComputedStyle,getBoundingClientRect) before calling a layout correct.
ไม่ควรทำ Don't
- Hardcode foregrounds on accent fills; use on-accent so brand and semantic pairings stay readable.
- Add dark content overrides to brand classes; the only device-dependent colours are the outer desk/rim.
- Add sakura, torii, red suns or decorative kanji to signal "Japanese".
- Animate more than one property family at a time; one curve, one distance.
- Inline arbitrary brand hex values into a component demo; demos use
var(--primary)and related shared tokens. - Nest cards. One card per level: the section is the card, everything inside it is content. If a group needs separation, use a label and a hairline — not a second border.
Application UI verification
กติกา KHLANG / JERNEY · ผลตรวจจริงและขอบเขตที่ยังไม่ทดสอบ
KHLANG: geometry and interaction
- Mobile bottom sheets retain 16px left/right gutters and a zero external bottom gap. Internal padding accommodates
safe-area-inset-bottom. Rounded top corners follow the 28px surface token; the lower edge is docked, not a floating rounded card. - Centered settings dialogs and desktop modals retain their own placement. Do not force all overlays into one alignment.
- All modal overlays escape animated ancestors through a body Teleport. The scrim blurs by 8px and the content panel is opaque white. The outer frame remains above overlays and never captures pointer events.
- Product category rails reserve at least 56px and use
flex-shrink:0; long product lists scroll independently without squeezing the rail, search field or heading. - Page-entry motion uses backwards fill, not persistent transform fill. After entry, the parent transform must be
none, and a fixed POS action bar must measure bottom gap zero at 360, 390, 768 and 1280px widths. - Mobile table labels use the Thai body font even if their barcode or numeric value is monospace. Apply the body font directly to the label pseudo-element; inheriting from the data cell can render Thai as missing glyphs.
- Async modal openers must preserve their return-focus target before loading disables the trigger. Escape/cancel restores focus after the modal closes and the loading state settles.
KHLANG audit coverage
The source router inventory contains 22 page route patterns, including login, shop pages, administrator pages, parameterized shop administrator pages and the not-found page. Public-production observations include 42 authenticated/not-found page-and-viewport rows at 390 and 1280px, plus four anonymous login/theme rows. The owner role was reviewed separately from the platform administrator role: a platform administrator with no shop memberships is not proof that owner pages have been reviewed with shop data.
The completed visual-layout sweep found no document horizontal overflow or body/text theme switching in those measured rows. Additional safe interaction checks opened/cancelled product, supplier, purchase-order, shop and stock-issue forms. During this round, fixed POS action-bar positioning, purchase-order opener focus restoration, and Thai mobile table-label fonts were corrected and rechecked on public production.
The receipt was rendered with synthetic component props in a local harness, with print CSS and event emission checked. This is not a completed production sale or proof of printer integration. No real sale, payment, purchase-order commit, inventory mutation, permission change, print job or export was submitted. Record-dependent tracking/receive/cancel workflows and every possible data state are not certified by this read-only UI review. A route visit, source check or empty state must never be labelled a fully tested workflow.
JERNEY: geometry and interaction
- QR, certificate and confirmation dialogs use the shared AppModal lifecycle. The panel is opaque white, radius 28px; backdrop blur is 8px and modal z-index is 100, below the persistent frame at 200.
- Enter panel motion is 300ms, leave panel motion 220ms, and backdrop lifecycle 240ms. Keep the Teleport outside the Transition and return focus only after leave finishes.
- Escape must be tested from the focused element so the real bubbling path reaches the modal keyboard handler. Dispatching a keyboard event directly on window does not exercise a handler on the modal ancestor.
- Event creation/editing and organization membership editors are inline forms, not popups. Do not report a missing modal as a defect for these controls. Never click submit while auditing an opener.
JERNEY audit coverage
Public-production read-only audit: 2026-10-20 UTC. The source router contains 15 route patterns, including the legacy ticket/feedback aliases. Fourteen patterns were visited at 390 and 1280px (28 page-and-viewport rows), with anonymous staff login separately checked in both device schemes. Measured document overflow was zero and body foreground/background stayed light in those rows. Admin, organizer dashboard and staff check-in were actually authenticated, not counted from a login redirect.
The public event page, calendar list/grid controls, empty personal ticket/history pages, administrator event editor, organization membership editor, QR and certificate dialogs were inspected without submitting writes. QR and certificate panels were white, within the viewport, with Escape closing and opener focus restored. The QR panel was tested by real touch at 390 x 500: scrollTop changed from 0 to 200 while the underlying page stayed at scrollY 0. The previously missing backdrop blur was added and the modal layer moved beneath the persistent frame. Panel enter/leave was measured as 300/220ms.
Coverage limits: certificate-detail, valid-ticket and feedback-success layouts require an owned valid registration/check-in. Only invalid-identifier and permission/error states were exercised on those parameterized routes; this is not success-state verification. No registration, check-in, certificate issue/upload, account/organization change, event publication/deletion, email, native share or file download was submitted. Real camera hardware and offline ticket recovery are not certified by this round. Empty dashboard/guest datasets do not certify populated tables or charts. These limits must remain visible beside the audit result.
Verification evidence and completion rule
The visual audit and contract tests are separate evidence. KHLANG web contract tests: 12 pass, 0 fail; JERNEY web contract tests: 11 pass, 0 fail. Both web typechecks and production builds completed. These are UI regression tests, not a claim that every API/business acceptance suite ran. Targeted deploys uploaded hashed assets before the HTML shell and verified uploaded content by read-back hashes.
A completed UI review means the stated read-only matrix has been recorded, issues found in that matrix corrected and retested, and unexecuted states named. It must never be shortened to "every workflow passed". Keep personal data, session cookies, bearer tokens, private ticket URLs and reviewer identities out of this public document.