From d492e2ce196ff47d7720626f0c2db5d0266406ca Mon Sep 17 00:00:00 2001 From: Jacob Zweifel Date: Wed, 26 Aug 2026 22:38:41 -0400 Subject: [PATCH] Write the login page on the letterhead too The sign-in page was the last bare doorway into the dressed admin. The palette and control skin now live in one class-scoped stylesheet, lib/styles/letterhead.css, imported by both the admin layout and the login page instead of being duplicated per surface. The login page gets the airmail stripe, wordmark masthead, and the shared form skin, with the handle label in the mono system voice the landing lookup uses, plus aria-invalid/aria-describedby wiring on a failed submit. Co-Authored-By: Claude Fable 5 --- apps/web/src/lib/styles/letterhead.css | 203 ++++++++++++++++++++++ apps/web/src/routes/admin/+layout.svelte | 205 +---------------------- apps/web/src/routes/login/+page.svelte | 85 ++++++---- 3 files changed, 260 insertions(+), 233 deletions(-) create mode 100644 apps/web/src/lib/styles/letterhead.css diff --git a/apps/web/src/lib/styles/letterhead.css b/apps/web/src/lib/styles/letterhead.css new file mode 100644 index 0000000..54eebd0 --- /dev/null +++ b/apps/web/src/lib/styles/letterhead.css @@ -0,0 +1,203 @@ +/* The letterhead: palette, type, and control skin for the app's own pages + (admin, login). Mirrors the site theme (SiteLayout.svelte). Structural + layout stays with each page; this file owns the look of the paper and + everything written on it. */ + +.letterhead { + --paper: #f7f3ea; + --paper-sunk: #efe9dc; + --ink: #161d2b; + --muted: #6f6757; + --accent: #9c3218; + --accent-ink: #fdfbf6; + --rule: #e0d8c8; + --airmail-blue: #1c3f6e; + --warn: #7c2144; + --ok: #256c3f; + /* The stripe's pale bands stay paper-white in both schemes; on the dark + scheme's page color the pole stops reading as airmail. */ + --stripe-paper: #f7f3ea; + + --font-serif: 'Iowan Old Style', 'Hoefler Text', 'Palatino Linotype', Palatino, + 'Book Antiqua', Georgia, serif; + --font-mono: ui-monospace, 'SF Mono', SFMono-Regular, 'Cascadia Mono', Menlo, Consolas, + monospace; + + background: var(--paper); + color: var(--ink); + font-family: var(--font-serif); + font-size: 1.05rem; + line-height: 1.6; + min-height: 100vh; + display: flex; + flex-direction: column; + background-image: repeating-linear-gradient( + 45deg, + color-mix(in srgb, var(--ink) 1.4%, transparent) 0 1px, + transparent 1px 4px + ); +} + +@media (prefers-color-scheme: dark) { + .letterhead { + --paper: #101520; + --paper-sunk: #161c29; + --ink: #e9e3d6; + --muted: #948b78; + --accent: #d4714f; + --accent-ink: #101520; + --rule: #242c3a; + --airmail-blue: #3a6ea8; + --warn: #e39aae; + --ok: #86c294; + } +} + +.letterhead .airmail { + height: 6px; + background: repeating-linear-gradient( + -45deg, + var(--accent) 0 10px, + var(--stripe-paper) 10px 20px, + var(--airmail-blue) 20px 30px, + var(--stripe-paper) 30px 40px + ); +} + +.letterhead .wordmark { + font-size: 1.15rem; + font-weight: 600; + letter-spacing: 0.01em; + color: var(--accent); + text-decoration: none; +} + +.letterhead h1 { + margin: 0 0 0.75rem; + font-size: 1.7rem; + font-weight: 600; + line-height: 1.2; + letter-spacing: -0.01em; + text-wrap: balance; +} + +.letterhead h2 { + margin: 2.25rem 0 0.75rem; + font-family: var(--font-mono); + font-size: 0.72rem; + font-weight: 500; + letter-spacing: 0.1em; + text-transform: uppercase; + color: color-mix(in srgb, var(--airmail-blue) 75%, var(--ink)); + border-bottom: 1px solid var(--rule); + padding-bottom: 0.4rem; +} + +.letterhead a { + color: var(--accent); + text-underline-offset: 0.18em; +} + +.letterhead code { + font-family: var(--font-mono); + font-size: 0.82em; + background: var(--paper-sunk); + border: 1px solid var(--rule); + border-radius: 2px; + padding: 0.05em 0.3em; + overflow-wrap: anywhere; +} + +.letterhead small, +.letterhead .meta { + color: var(--muted); + font-size: 0.88rem; +} + +.letterhead input:not([type='checkbox']), +.letterhead select, +.letterhead textarea { + box-sizing: border-box; + padding: 0.5rem 0.7rem; + font-family: var(--font-mono); + /* 16px floor: anything smaller makes iOS Safari zoom on focus. */ + font-size: 1rem; + color: var(--ink); + background: var(--paper); + border: 1px solid color-mix(in srgb, var(--muted) 80%, var(--rule)); + border-radius: 2px; +} + +.letterhead input::placeholder, +.letterhead textarea::placeholder { + color: var(--muted); + opacity: 1; +} + +.letterhead input[type='checkbox'] { + accent-color: var(--accent); + width: 1rem; + height: 1rem; +} + +.letterhead button, +.letterhead a.btn { + box-sizing: border-box; + padding: 0.5rem 1.1rem; + font-family: var(--font-mono); + font-size: 0.8rem; + letter-spacing: 0.04em; + color: var(--accent-ink); + background: var(--accent); + border: 1px solid var(--accent); + border-radius: 2px; + cursor: pointer; + transition: filter 160ms ease; +} + +.letterhead a.btn { + display: inline-block; + text-decoration: none; +} + +.letterhead button:hover, +.letterhead button:focus-visible, +.letterhead a.btn:hover, +.letterhead a.btn:focus-visible { + filter: brightness(1.12); +} + +.letterhead button.quiet { + color: var(--muted); + background: transparent; + border-color: color-mix(in srgb, var(--muted) 55%, var(--rule)); + transition: + color 160ms ease, + border-color 160ms ease; +} + +.letterhead button.quiet:hover, +.letterhead button.quiet:focus-visible { + filter: none; + color: var(--warn); + border-color: var(--warn); +} + +.letterhead .notice { + color: var(--ok); +} + +.letterhead .error, +.letterhead [role='alert'] { + color: var(--warn); +} + +.letterhead :focus-visible { + outline: 2px solid var(--accent); + outline-offset: 2px; +} + +.letterhead ::selection { + background: var(--accent); + color: var(--accent-ink); +} diff --git a/apps/web/src/routes/admin/+layout.svelte b/apps/web/src/routes/admin/+layout.svelte index 58aab06..d9acfcb 100644 --- a/apps/web/src/routes/admin/+layout.svelte +++ b/apps/web/src/routes/admin/+layout.svelte @@ -1,5 +1,6 @@ -
+
@@ -35,67 +36,6 @@
diff --git a/apps/web/src/routes/login/+page.svelte b/apps/web/src/routes/login/+page.svelte index 5655005..03e196e 100644 --- a/apps/web/src/routes/login/+page.svelte +++ b/apps/web/src/routes/login/+page.svelte @@ -1,4 +1,5 @@