A calm place to write long-form, and publish it to the open social web. skypress.blog/
0

Configure Feed

Select the types of activity you want to include in your feed.

Add SP6 brand identity + apply it

Deliver the brand identity (brief §7) and apply it across the app.

Direction: "the open sky meets the typesetter's bench" — editorial letterpress
+ altitude blue. Warm paper, ink near-black, a confident sky-blue accent; a
characterful old-style serif (Fraunces) for display, a reading serif
(Newsreader) for body/UI, and IBM Plex Mono for code + eyebrow labels. All
freely licensed and self-hosted via Fontsource.

- src/styles/global.css: CSS-variable design tokens + base typography + .eyebrow
/ .btn / .sky-wash utilities + first-class light/dark (prefers-color-scheme).
- Logo.astro + favicon.svg: a type-block framing a sun over a horizon (sky ∧
press), monochrome-safe and favicon-ready.
- Applied to the landing (editorial hero), reading page (Fraunces title +
Newsreader prose, styled quotes/code/images), publication homepage, and the
editor chrome / login / publish / your-articles (tokenized for dark mode).
- docs/brand/brand-identity.md documents the full system (concept, logo,
palette, type, voice, tagline, directions).

Verified: astro check clean, build green, 37 tests; landing + reading pages
screenshot-checked.

+572 -115
+2 -2
README.md
··· 63 63 | SP3 | Image/blob pipeline (`mediaUpload` → `uploadBlob`) | ✅ Complete | 64 64 | SP4 | Public renderer (`/@<handle>/<rkey>`, link tags, edge SSR, sanitisation) | ✅ Complete | 65 65 | SP5 | Edit flow (the "puppy problem") + unpublish | ✅ Complete | 66 - | SP6 | Brand identity | Next | 67 - | SP7 | Deploy to a free host | | 66 + | SP6 | Brand identity | ✅ Complete | 67 + | SP7 | Deploy to a free host | Next | 68 68 69 69 ## License 70 70
+80
docs/brand/brand-identity.md
··· 1 + # SkyPress — Brand Identity 2 + 3 + > The deliverable for brief §7. The system is implemented in `src/styles/global.css` 4 + > (tokens + type), `src/components/Logo.astro`, and `public/favicon.svg`. 5 + 6 + ## Concept 7 + 8 + **The open sky meets the typesetter's bench.** Airy + crafted, modern + timeless. SkyPress 9 + is a calm, editorial writing studio for the open social web — indie-web and human, never 10 + corporate-SaaS. The "sky" is the AT Protocol's open air; the "press" is movable type, ink, 11 + and paper. The design lives in that collision. 12 + 13 + ### Directions explored (then refined) 14 + 1. **Bauhaus-sky** — geometric, primary blue, grotesque type. Rejected: too cool/corporate. 15 + 2. **Soft cloud / pastel** — rounded, airy, gradients. Rejected: drifts toward AI-slop. 16 + 3. **Editorial letterpress + altitude blue** ✅ — warm paper, ink, a confident sky-blue, a 17 + characterful old-style serif. Crafted and reading-first; this is the chosen direction. 18 + 19 + ## Logo 20 + 21 + A **type block** (the page / letterpress tile) framing a **sun rising over a horizon**, 22 + with two short "lines of set type" beneath — sky ∧ press in one mark. Single-colour 23 + (`currentColor`), so it works in monochrome (GitHub README) and as a 16px favicon; the app 24 + renders the mark in sky-blue. See `Logo.astro` / `favicon.svg`. 25 + 26 + - **Wordmark:** "SkyPress" — one word, set in Fraunces (display) with a touch of `SOFT`/`WONK` 27 + for letterpress warmth. The mark sits left of the word. 28 + 29 + ## Color 30 + 31 + | Token | Light | Dark | Use | 32 + |---|---|---|---| 33 + | `--paper` | `#faf6ef` | `#111319` | page background (warm paper / deep night sky) | 34 + | `--paper-raised` | `#fffdf9` | `#181b23` | cards, inputs | 35 + | `--panel` | `#f1eadc` | `#1b1f2a` | bars, code chips | 36 + | `--ink` | `#1c1a16` | `#ece6da` | primary text (warm near-black / warm off-white) | 37 + | `--ink-soft` | `#46413a` | `#c3bcad` | lede / secondary text | 38 + | `--muted` | `#756d5f` | `#938b7d` | meta, captions | 39 + | `--line` | `#e5ddcd` | `#292d39` | hairlines | 40 + | `--sky` | `#2a5fd0` | `#7aa2ff` | primary accent (altitude blue) | 41 + | `--sky-tint` | `#e9f0fc` | `#1a2235` | accent washes | 42 + | `--ember` | `#bb5a36` | `#e08a63` | rare warm counterweight / errors | 43 + 44 + Light + dark are both first-class (auto via `prefers-color-scheme`). The palette is a 45 + considered sky-blue + warm ink/paper neutrals — deliberately not default tech-blue on white. 46 + 47 + ## Typography (all freely licensed, self-hosted via Fontsource) 48 + 49 + - **Display / wordmark / headings — Fraunces** (variable; `opsz`, `SOFT`, `WONK`). An 50 + old-style serif with optical sizing and characterful, slightly "wonky" details — 51 + letterpress warmth. 52 + - **Reading & UI — Newsreader** (variable). A serif designed for on-screen long-form; 53 + calm and editorial. Used for body and most UI text to keep the studio cohesive. 54 + - **Mono — IBM Plex Mono.** Code blocks, and uppercase letter-spaced "eyebrow" labels 55 + (`.eyebrow`) — the indie-press voice. 56 + 57 + ## Iconography & UI texture 58 + 59 + - "Blocks of type" show up as the editor's curated block set; the mark echoes stacked 60 + lines of type. Hairline rules (`--line`) and a soft horizon gradient (`.sky-wash`) give 61 + atmosphere without clip-art clouds. 62 + - Buttons are mono-labelled, understated; the primary action is sky-blue. 63 + 64 + ## Voice & tone 65 + 66 + Calm, encouraging, plainspoken, a little literary. 67 + - **Empty state:** "A blank page, and the open sky." 68 + - **Primary buttons:** "Start writing", "Publish", "Update". 69 + - **Reassurance:** "Your data, your server, the open web." 70 + 71 + ## Tagline / positioning 72 + 73 + **"Write things worth keeping."** 74 + Alternates: "Long-form for the open sky." · "The open sky meets the typesetter's bench." · 75 + "Your words, your server, the open web." 76 + 77 + ## Constraints met 78 + 79 + Tiny favicon ✓ · large hero mark ✓ · monochrome ✓ · light + dark first-class ✓ · freely 80 + licensable fonts, reproducible assets ✓.
+33
docs/specs/sp6-brand.md
··· 1 + # SP6 — Brand identity 2 + 3 + - **Date:** 2026-06-08 4 + - **Status:** ✅ Complete — system built + applied + screenshot-verified 5 + - **Goal (brief §7):** Deliver a complete brand identity and apply it across the editor + 6 + reading experience. 7 + 8 + The brand deliverable (concept, logo, palette, type, voice, tagline, the 2–3 directions + 9 + the chosen one) is **[docs/brand/brand-identity.md](../brand/brand-identity.md)**. 10 + 11 + ## What was built 12 + 13 + - **Design system** (`src/styles/global.css`): CSS-variable tokens (warm paper, ink, 14 + altitude-blue sky, neutrals), self-hosted freely-licensed type (Fraunces / Newsreader / 15 + IBM Plex Mono via Fontsource), base typography, `.eyebrow` / `.btn` / `.sky-wash` 16 + utilities, and **first-class light + dark** via `prefers-color-scheme`. 17 + - **Logo + favicon** (`Logo.astro`, `favicon.svg`): a type-block framing a sun over a 18 + horizon — sky ∧ press — monochrome-safe and favicon-ready. 19 + - **Applied** across the landing (editorial hero with sky-wash), the reading page 20 + (editorial article: Fraunces title, Newsreader prose, styled quotes/code/images, the 21 + SP5 "updated" marker), the publication homepage, and the editor chrome / login / publish 22 + / your-articles (tokenized for dark mode). 23 + 24 + ## Verified 25 + 26 + - `astro check` clean; build green; 37 tests green. 27 + - Screenshots of the landing and reading pages confirm the editorial-letterpress + sky 28 + direction renders cohesively (in dark mode here; light mode shares the token set). 29 + 30 + ## Out of scope 31 + 32 + A manual theme toggle (auto via OS for now), illustration/marketing assets beyond the mark, 33 + and per-publication theming (`site.standard.theme.*`).
+30
package-lock.json
··· 15 15 "@atproto/common-web": "^0.5.0", 16 16 "@atproto/oauth-client-browser": "^0.4.1", 17 17 "@automattic/isolated-block-editor": "2.30.0", 18 + "@fontsource-variable/fraunces": "^5.2.9", 19 + "@fontsource-variable/newsreader": "^5.2.10", 20 + "@fontsource/ibm-plex-mono": "^5.2.7", 18 21 "@wordpress/block-library": "9.24.0", 19 22 "@wordpress/blocks": "14.13.0", 20 23 "@wordpress/element": "6.24.0", ··· 2123 2126 "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.11.tgz", 2124 2127 "integrity": "sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==", 2125 2128 "license": "MIT" 2129 + }, 2130 + "node_modules/@fontsource-variable/fraunces": { 2131 + "version": "5.2.9", 2132 + "resolved": "https://registry.npmjs.org/@fontsource-variable/fraunces/-/fraunces-5.2.9.tgz", 2133 + "integrity": "sha512-Y6IjunlN9Ni723np+GIgAaKzCDBrPRrqNi01TZxHs5wtHYROWFM9W6yiT+/gGwSjWIRD18oX17kD/BRWekc/Lw==", 2134 + "license": "OFL-1.1", 2135 + "funding": { 2136 + "url": "https://github.com/sponsors/ayuhito" 2137 + } 2138 + }, 2139 + "node_modules/@fontsource-variable/newsreader": { 2140 + "version": "5.2.10", 2141 + "resolved": "https://registry.npmjs.org/@fontsource-variable/newsreader/-/newsreader-5.2.10.tgz", 2142 + "integrity": "sha512-MdI2iRwrqWpMOU/2aV2BgfZ4dJNlj/XlYaY8Zb7t87mWqHskYW0XiUANkt1cyOCiEfW2VQ0bQ5vZgGPp6B2B4w==", 2143 + "license": "OFL-1.1", 2144 + "funding": { 2145 + "url": "https://github.com/sponsors/ayuhito" 2146 + } 2147 + }, 2148 + "node_modules/@fontsource/ibm-plex-mono": { 2149 + "version": "5.2.7", 2150 + "resolved": "https://registry.npmjs.org/@fontsource/ibm-plex-mono/-/ibm-plex-mono-5.2.7.tgz", 2151 + "integrity": "sha512-MKAb8qV+CaiMQn2B0dIi1OV3565NYzp3WN5b4oT6LTkk+F0jR6j0ZN+5BKJiIhffDC3rtBULsYZE65+0018z9w==", 2152 + "license": "OFL-1.1", 2153 + "funding": { 2154 + "url": "https://github.com/sponsors/ayuhito" 2155 + } 2126 2156 }, 2127 2157 "node_modules/@img/colour": { 2128 2158 "version": "1.1.0",
+3
package.json
··· 23 23 "@atproto/common-web": "^0.5.0", 24 24 "@atproto/oauth-client-browser": "^0.4.1", 25 25 "@automattic/isolated-block-editor": "2.30.0", 26 + "@fontsource-variable/fraunces": "^5.2.9", 27 + "@fontsource-variable/newsreader": "^5.2.10", 28 + "@fontsource/ibm-plex-mono": "^5.2.7", 26 29 "@wordpress/block-library": "9.24.0", 27 30 "@wordpress/blocks": "14.13.0", 28 31 "@wordpress/element": "6.24.0",
+9
public/favicon.svg
··· 1 + <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" fill="none"> 2 + <rect width="32" height="32" rx="8" fill="#faf6ef"/> 3 + <g stroke="#2a5fd0"> 4 + <rect x="2.5" y="2.5" width="27" height="27" rx="7.5" stroke-width="2.2"/> 5 + <path d="M7 18.5h18" stroke-width="2.2" stroke-linecap="round"/> 6 + <path d="M9 22.8h14M9 25.6h9" stroke-width="1.7" stroke-linecap="round" opacity="0.55"/> 7 + </g> 8 + <circle cx="16" cy="12.5" r="3.4" fill="#2a5fd0"/> 9 + </svg>
+49
src/components/Logo.astro
··· 1 + --- 2 + interface Props { 3 + size?: number; 4 + wordmark?: boolean; 5 + } 6 + const { size = 28, wordmark = true } = Astro.props; 7 + --- 8 + 9 + <span class="logo"> 10 + <svg 11 + class="logo__mark" 12 + width={size} 13 + height={size} 14 + viewBox="0 0 32 32" 15 + fill="none" 16 + aria-hidden="true" 17 + > 18 + <!-- the type block / page --> 19 + <rect x="2.5" y="2.5" width="27" height="27" rx="7.5" stroke="currentColor" stroke-width="2.2" /> 20 + <!-- sun over the horizon (sky) --> 21 + <circle cx="16" cy="12.5" r="3.4" fill="currentColor" /> 22 + <path d="M7 18.5h18" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" /> 23 + <!-- lines of set type (press) --> 24 + <path d="M9 22.8h14M9 25.6h9" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" opacity="0.55" /> 25 + </svg> 26 + {wordmark && <span class="logo__word">SkyPress</span>} 27 + </span> 28 + 29 + <style> 30 + .logo { 31 + display: inline-flex; 32 + align-items: center; 33 + gap: 0.55rem; 34 + color: var(--ink); 35 + text-decoration: none; 36 + } 37 + .logo__mark { 38 + color: var(--sky); 39 + flex: none; 40 + } 41 + .logo__word { 42 + font-family: var(--font-display); 43 + font-weight: 600; 44 + font-size: 1.18rem; 45 + font-variation-settings: 'opsz' 30, 'SOFT' 50, 'WONK' 1; 46 + letter-spacing: -0.015em; 47 + color: var(--ink); 48 + } 49 + </style>
+3 -18
src/layouts/Base.astro
··· 1 1 --- 2 + import '../styles/global.css'; 3 + 2 4 interface Props { 3 5 title: string; 4 6 description?: string; ··· 12 14 <meta charset="utf-8" /> 13 15 <meta name="viewport" content="width=device-width, initial-scale=1" /> 14 16 <meta name="generator" content={Astro.generator} /> 17 + <link rel="icon" href="/favicon.svg" type="image/svg+xml" /> 15 18 {description && <meta name="description" content={description} />} 16 19 <title>{title}</title> 17 20 <slot name="head" /> ··· 20 23 <slot /> 21 24 </body> 22 25 </html> 23 - 24 - <style is:global> 25 - :root { 26 - --ink: #16181d; 27 - --paper: #fbfaf7; 28 - --sky: #2f7de1; 29 - --muted: #5b6470; 30 - font-family: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, 31 - sans-serif; 32 - } 33 - html { 34 - background: var(--paper); 35 - color: var(--ink); 36 - } 37 - body { 38 - margin: 0; 39 - } 40 - </style>
+68 -20
src/pages/[author]/[rkey].astro
··· 1 1 --- 2 2 import Base from '../../layouts/Base.astro'; 3 + import Logo from '../../components/Logo.astro'; 3 4 import { resolveAuthor } from '../../lib/reader/identity'; 4 5 import { getRecord } from '../../lib/reader/records'; 5 6 import { resolveBlobImageUrls } from '../../lib/media/blob'; ··· 74 75 <meta property="og:url" content={canonical} /> 75 76 </Fragment> 76 77 78 + <header class="masthead"> 79 + <a href="/"><Logo /></a> 80 + </header> 77 81 <main class="reader"> 78 - <p class="reader__meta"> 82 + <p class="reader__meta eyebrow"> 79 83 <a class="reader__author" href={`/@${ handle }`}>@{handle}</a> 80 84 {publishedLabel && <> · {publishedLabel}</>} 81 85 {updatedLabel && <> · updated {updatedLabel}</>} ··· 83 87 </p> 84 88 <h1 class="reader__title">{title}</h1> 85 89 <article class="reader__article" set:html={html} /> 90 + <footer class="reader__foot"> 91 + <a class="reader__author" href={`/@${ handle }`}>More from @{handle}</a> 92 + </footer> 86 93 </main> 87 94 </Base> 88 95 89 96 <style> 97 + .masthead { 98 + padding: 1.5rem clamp(1.25rem, 5vw, 4rem); 99 + border-bottom: 1px solid var(--line); 100 + } 101 + .masthead a { 102 + text-decoration: none; 103 + } 90 104 .reader { 91 - max-width: 42rem; 105 + max-width: 40rem; 92 106 margin: 0 auto; 93 - padding: 3rem 1.5rem 6rem; 107 + padding: clamp(2.5rem, 6vw, 4.5rem) 1.5rem 6rem; 94 108 } 95 109 .reader__meta { 96 - text-transform: uppercase; 97 - letter-spacing: 0.08em; 98 - font-size: 0.72rem; 99 - color: var(--muted); 100 - margin-bottom: 1.5rem; 110 + margin-bottom: 1.25rem; 101 111 } 102 112 .reader__author { 103 113 color: var(--sky); 104 114 text-decoration: none; 105 - font-weight: 600; 106 115 } 107 116 .reader__title { 108 - font-size: clamp( 2rem, 5vw, 3rem ); 109 - line-height: 1.1; 110 - margin: 0 0 2rem; 117 + font-size: clamp(2.2rem, 5.5vw, 3.4rem); 118 + font-variation-settings: 'opsz' 144, 'SOFT' 40, 'WONK' 1; 119 + line-height: 1.04; 120 + margin: 0 0 2.25rem; 111 121 } 112 122 .reader__article { 113 - font-size: 1.15rem; 114 - line-height: 1.7; 123 + font-family: var(--font-body); 124 + font-size: 1.22rem; 125 + line-height: 1.72; 126 + color: var(--ink); 127 + } 128 + .reader__article :global(h2), 129 + .reader__article :global(h3) { 130 + margin: 2.4rem 0 0.8rem; 131 + } 132 + .reader__article :global(h2) { 133 + font-size: 1.7rem; 134 + } 135 + .reader__article :global(p) { 136 + margin: 1.2rem 0; 137 + } 138 + .reader__article :global(a) { 139 + color: var(--sky); 140 + } 141 + /* First paragraph: a quiet editorial lead-in */ 142 + .reader__article :global(p:first-of-type) { 143 + font-size: 1.32rem; 144 + color: var(--ink-soft); 145 + } 146 + .reader__article :global(blockquote) { 147 + margin: 1.8rem 0; 148 + padding-left: 1.25rem; 149 + border-left: 3px solid var(--sky); 150 + font-style: italic; 151 + color: var(--ink-soft); 115 152 } 116 153 .reader__article :global(img) { 117 154 max-width: 100%; 118 155 height: auto; 119 - border-radius: 8px; 156 + border-radius: var(--radius); 157 + box-shadow: var(--shadow); 120 158 } 121 159 .reader__article :global(pre) { 122 - background: #16181d; 123 - color: #f5f3ee; 124 - padding: 1rem; 125 - border-radius: 8px; 160 + background: var(--ink); 161 + color: var(--paper); 162 + font-family: var(--font-mono); 163 + padding: 1.1rem 1.25rem; 164 + border-radius: var(--radius); 126 165 overflow: auto; 127 - font-size: 0.95rem; 166 + font-size: 0.92rem; 167 + line-height: 1.5; 168 + } 169 + .reader__article :global(code) { 170 + font-family: var(--font-mono); 171 + } 172 + .reader__foot { 173 + margin-top: 3.5rem; 174 + padding-top: 1.5rem; 175 + border-top: 1px solid var(--line); 128 176 } 129 177 </style>
+33 -15
src/pages/[author]/index.astro
··· 1 1 --- 2 2 import Base from '../../layouts/Base.astro'; 3 + import Logo from '../../components/Logo.astro'; 3 4 import { resolveAuthor } from '../../lib/reader/identity'; 4 5 import { listRecords } from '../../lib/reader/records'; 5 6 import { publicationHomeUrl } from '../../lib/publish/records'; ··· 64 65 {pubUri && <link rel="site.standard.publication" href={pubUri} />} 65 66 </Fragment> 66 67 68 + <header class="masthead"> 69 + <a href="/"><Logo /></a> 70 + </header> 67 71 <main class="home"> 68 - <p class="home__handle">@{handle}</p> 72 + <p class="home__handle eyebrow">@{handle}</p> 69 73 <h1 class="home__title">{pubName}</h1> 70 74 {publication?.value.description && ( 71 75 <p class="home__lede">{publication.value.description}</p> ··· 90 94 </Base> 91 95 92 96 <style> 97 + .masthead { 98 + padding: 1.5rem clamp(1.25rem, 5vw, 4rem); 99 + border-bottom: 1px solid var(--line); 100 + } 101 + .masthead a { 102 + text-decoration: none; 103 + } 93 104 .home { 94 105 max-width: 42rem; 95 106 margin: 0 auto; 96 - padding: 4rem 1.5rem 6rem; 107 + padding: clamp(2.5rem, 6vw, 4.5rem) 1.5rem 6rem; 97 108 } 98 109 .home__handle { 99 - text-transform: uppercase; 100 - letter-spacing: 0.1em; 101 - font-size: 0.75rem; 102 - color: var(--muted); 110 + margin: 0; 103 111 } 104 112 .home__title { 105 - font-size: clamp( 2rem, 5vw, 3rem ); 106 - margin: 0.25rem 0 1rem; 113 + font-size: clamp(2.2rem, 6vw, 3.6rem); 114 + font-variation-settings: 'opsz' 144, 'SOFT' 40, 'WONK' 1; 115 + margin: 0.4rem 0 1rem; 107 116 } 108 117 .home__lede { 118 + color: var(--ink-soft); 119 + font-size: 1.2rem; 120 + max-width: 42ch; 121 + } 122 + .home__empty { 109 123 color: var(--muted); 110 - font-size: 1.15rem; 111 124 } 112 125 .home__list { 113 126 list-style: none; ··· 115 128 margin: 2.5rem 0 0; 116 129 } 117 130 .home__item { 118 - padding: 1.25rem 0; 119 - border-top: 1px solid #e7e3da; 131 + padding: 1.35rem 0; 132 + border-top: 1px solid var(--line); 120 133 } 121 134 .home__item a { 122 - font-size: 1.3rem; 123 - font-weight: 600; 135 + font-family: var(--font-display); 136 + font-size: 1.4rem; 137 + font-weight: 560; 124 138 color: var(--ink); 125 139 text-decoration: none; 126 140 } 141 + .home__item a:hover { 142 + color: var(--sky); 143 + } 127 144 .home__date { 128 145 color: var(--muted); 129 - font-size: 0.8rem; 146 + font-family: var(--font-mono); 147 + font-size: 0.72rem; 130 148 margin-left: 0.75rem; 131 149 } 132 150 .home__desc { 133 - color: var(--muted); 151 + color: var(--ink-soft); 134 152 margin: 0.4rem 0 0; 135 153 } 136 154 </style>
+23 -24
src/pages/editor.astro
··· 1 1 --- 2 2 import Base from '../layouts/Base.astro'; 3 + import Logo from '../components/Logo.astro'; 3 4 import Studio from '../components/Studio.tsx'; 4 5 --- 5 6 6 7 <Base title="Write — SkyPress"> 7 8 <main class="editor-shell"> 8 9 <header class="editor-shell__bar"> 9 - <a class="editor-shell__home" href="/">SkyPress</a> 10 - <span class="editor-shell__hint" 11 - >Sign in with your AT Protocol identity to write.</span 12 - > 10 + <a class="editor-shell__home" href="/"><Logo size={24} /></a> 11 + <span class="editor-shell__hint eyebrow">The studio</span> 13 12 </header> 14 13 <!-- client:only — auth + editor run only in the browser; their bundle never 15 14 reaches reading pages (Decisions 0001 & 0004). --> ··· 25 24 align-items: baseline; 26 25 gap: 1rem; 27 26 padding: 0.75rem 1.25rem; 28 - border-bottom: 1px solid #e7e3da; 27 + border-bottom: 1px solid var(--line); 29 28 flex-wrap: wrap; 30 29 } 31 30 .editor-shell__home { ··· 50 49 justify-content: space-between; 51 50 gap: 1rem; 52 51 padding: 0.5rem 1.25rem; 53 - background: #f1eee7; 54 - border-bottom: 1px solid #e7e3da; 52 + background: var(--panel); 53 + border-bottom: 1px solid var(--line); 55 54 font-size: 0.9rem; 56 55 flex-wrap: wrap; 57 56 } 58 57 .studio__signout { 59 - border: 1px solid #d6d0c4; 60 - background: #fff; 58 + border: 1px solid var(--line-strong); 59 + background: var(--paper-raised); 61 60 border-radius: 6px; 62 61 padding: 0.3rem 0.7rem; 63 62 cursor: pointer; ··· 72 71 } 73 72 .studio__error, 74 73 .login__error { 75 - color: #b3261e; 74 + color: var(--ember); 76 75 font-size: 0.9rem; 77 76 } 78 77 .login__title { ··· 93 92 width: 100%; 94 93 box-sizing: border-box; 95 94 padding: 0.6rem 0.7rem; 96 - border: 1px solid #d6d0c4; 95 + border: 1px solid var(--line-strong); 97 96 border-radius: 8px; 98 97 font: inherit; 99 98 margin-bottom: 0.85rem; ··· 122 121 align-items: center; 123 122 gap: 0.75rem; 124 123 padding: 0.75rem 1.25rem; 125 - border-bottom: 1px solid #e7e3da; 124 + border-bottom: 1px solid var(--line); 126 125 } 127 126 .publish__title { 128 127 flex: 1 1 18rem; 129 128 padding: 0.5rem 0.7rem; 130 - border: 1px solid #d6d0c4; 129 + border: 1px solid var(--line-strong); 131 130 border-radius: 8px; 132 131 font: inherit; 133 132 font-size: 1.05rem; ··· 148 147 } 149 148 .publish__cancel { 150 149 padding: 0.5rem 0.9rem; 151 - border: 1px solid #d6d0c4; 152 - background: #fff; 150 + border: 1px solid var(--line-strong); 151 + background: var(--paper-raised); 153 152 border-radius: 8px; 154 153 font: inherit; 155 154 cursor: pointer; 156 155 } 157 156 .publish__confirm { 158 157 flex: 1 1 100%; 159 - background: #fff7e6; 160 - border: 1px solid #f0d9a8; 158 + background: var(--sky-tint); 159 + border: 1px solid var(--line-strong); 161 160 border-radius: 10px; 162 161 padding: 0.85rem 1rem; 163 162 } ··· 177 176 } 178 177 .publish__result code { 179 178 word-break: break-all; 180 - background: #f1eee7; 179 + background: var(--panel); 181 180 padding: 0.1rem 0.3rem; 182 181 border-radius: 4px; 183 182 } 184 183 .publish__error { 185 - color: #b3261e; 184 + color: var(--ember); 186 185 } 187 186 188 187 /* Your articles + mode bar */ 189 188 .myarticles { 190 189 padding: 1rem 1.25rem; 191 - border-bottom: 1px solid #e7e3da; 190 + border-bottom: 1px solid var(--line); 192 191 } 193 192 .myarticles__heading { 194 193 font-size: 0.75rem; ··· 224 223 gap: 0.5rem; 225 224 } 226 225 .myarticles__actions button { 227 - border: 1px solid #d6d0c4; 228 - background: #fff; 226 + border: 1px solid var(--line-strong); 227 + background: var(--paper-raised); 229 228 border-radius: 6px; 230 229 padding: 0.25rem 0.6rem; 231 230 font: inherit; ··· 242 241 color: var(--muted); 243 242 } 244 243 .studio__mode button { 245 - border: 1px solid #d6d0c4; 246 - background: #fff; 244 + border: 1px solid var(--line-strong); 245 + background: var(--paper-raised); 247 246 border-radius: 6px; 248 247 padding: 0.25rem 0.7rem; 249 248 font: inherit;
+85 -36
src/pages/index.astro
··· 1 1 --- 2 2 import Base from '../layouts/Base.astro'; 3 + import Logo from '../components/Logo.astro'; 3 4 --- 4 5 5 6 <Base 6 7 title="SkyPress — a writing studio for the open social web" 7 - description="A standalone long-form writing studio for the AT Protocol." 8 + description="A standalone, long-form writing studio for the AT Protocol. Write in blocks; your words are saved to your own server." 8 9 > 9 - <main class="home"> 10 - <p class="home__kicker">SkyPress · SP0 spike</p> 11 - <h1 class="home__title">The open sky meets the typesetter's bench.</h1> 12 - <p class="home__lede"> 13 - A standalone, long-form writing studio for the AT Protocol. Write in 14 - blocks; your words are saved to your own server. 15 - </p> 16 - <nav class="home__nav"> 17 - <a href="/editor">Open the editor →</a> 18 - <a href="/preview">See a rendered article →</a> 19 - </nav> 20 - </main> 10 + <div class="page sky-wash"> 11 + <header class="masthead"> 12 + <Logo /> 13 + <a class="btn btn--ghost" href="/editor">Open the studio</a> 14 + </header> 15 + 16 + <main class="hero"> 17 + <p class="eyebrow">Long-form for the AT Protocol</p> 18 + <h1 class="hero__title"> 19 + The open sky meets the<br /><span class="hero__em">typesetter's bench.</span> 20 + </h1> 21 + <p class="hero__lede"> 22 + A calm, block-based writing studio for the open social web. Compose rich 23 + long-form pieces and publish them straight to <em>your own</em> server — SkyPress 24 + is just the studio you write in. 25 + </p> 26 + <div class="hero__actions"> 27 + <a class="btn btn--primary" href="/editor">Start writing</a> 28 + <a class="btn btn--ghost" href="/preview">Read a sample</a> 29 + </div> 30 + <p class="hero__note"> 31 + Sign in with your existing Bluesky / AT&nbsp;Protocol identity. No new account. 32 + </p> 33 + </main> 34 + 35 + <footer class="footer"> 36 + <span>Your data, your server, the open web.</span> 37 + <span class="footer__mark">GPL-2.0 · built on Gutenberg</span> 38 + </footer> 39 + </div> 21 40 </Base> 22 41 23 42 <style> 24 - .home { 25 - max-width: 42rem; 43 + .page { 44 + min-height: 100vh; 45 + display: flex; 46 + flex-direction: column; 47 + } 48 + .masthead { 49 + display: flex; 50 + align-items: center; 51 + justify-content: space-between; 52 + padding: 1.5rem clamp(1.25rem, 5vw, 4rem); 53 + } 54 + .hero { 55 + flex: 1; 56 + max-width: 52rem; 26 57 margin: 0 auto; 27 - padding: 6rem 1.5rem; 58 + padding: clamp(3rem, 10vh, 7rem) clamp(1.25rem, 5vw, 4rem); 59 + display: flex; 60 + flex-direction: column; 61 + align-items: flex-start; 28 62 } 29 - .home__kicker { 30 - text-transform: uppercase; 31 - letter-spacing: 0.12em; 32 - font-size: 0.75rem; 33 - color: var(--muted); 63 + .hero__title { 64 + font-size: clamp(2.6rem, 8vw, 5rem); 65 + font-weight: 560; 66 + font-variation-settings: 'opsz' 144, 'SOFT' 40, 'WONK' 1; 67 + line-height: 1.02; 68 + margin: 1rem 0 0; 34 69 } 35 - .home__title { 36 - font-size: clamp(2rem, 6vw, 3.25rem); 37 - line-height: 1.05; 38 - margin: 0.25rem 0 1rem; 70 + .hero__em { 71 + font-style: italic; 72 + color: var(--sky); 73 + font-variation-settings: 'opsz' 144, 'SOFT' 60, 'WONK' 1; 39 74 } 40 - .home__lede { 41 - font-size: 1.2rem; 42 - color: var(--muted); 43 - max-width: 34rem; 75 + .hero__lede { 76 + font-size: clamp(1.15rem, 2.4vw, 1.4rem); 77 + line-height: 1.55; 78 + color: var(--ink-soft); 79 + max-width: 38ch; 80 + margin: 1.75rem 0 0; 44 81 } 45 - .home__nav { 82 + .hero__actions { 46 83 display: flex; 47 - gap: 1.5rem; 48 - margin-top: 2rem; 84 + gap: 0.85rem; 85 + margin: 2.25rem 0 0; 49 86 flex-wrap: wrap; 50 87 } 51 - .home__nav a { 52 - color: var(--sky); 53 - text-decoration: none; 54 - font-weight: 600; 88 + .hero__note { 89 + color: var(--muted); 90 + font-size: 0.95rem; 91 + margin-top: 1.5rem; 92 + } 93 + .footer { 94 + display: flex; 95 + justify-content: space-between; 96 + gap: 1rem; 97 + flex-wrap: wrap; 98 + padding: 1.5rem clamp(1.25rem, 5vw, 4rem); 99 + border-top: 1px solid var(--line); 100 + color: var(--muted); 101 + font-family: var(--font-mono); 102 + font-size: 0.72rem; 103 + letter-spacing: 0.04em; 55 104 } 56 105 </style>
+154
src/styles/global.css
··· 1 + /** 2 + * SkyPress design system (SP6, Decision 0009). 3 + * "The open sky meets the typesetter's bench" — editorial letterpress + open air. 4 + * Self-hosted, freely-licensed fonts; light + dark first-class. 5 + */ 6 + @import '@fontsource-variable/fraunces'; 7 + @import '@fontsource-variable/newsreader'; 8 + @import '@fontsource/ibm-plex-mono/400.css'; 9 + @import '@fontsource/ibm-plex-mono/500.css'; 10 + 11 + :root { 12 + /* Type */ 13 + --font-display: 'Fraunces Variable', Georgia, 'Times New Roman', serif; 14 + --font-body: 'Newsreader Variable', Georgia, serif; 15 + --font-mono: 'IBM Plex Mono', ui-monospace, 'SFMono-Regular', monospace; 16 + 17 + /* Palette — light (warm paper, ink, altitude blue) */ 18 + --paper: #faf6ef; 19 + --paper-raised: #fffdf9; 20 + --panel: #f1eadc; 21 + --ink: #1c1a16; 22 + --ink-soft: #46413a; 23 + --muted: #756d5f; 24 + --line: #e5ddcd; 25 + --line-strong: #d8cdb8; 26 + --sky: #2a5fd0; 27 + --sky-strong: #1d418f; 28 + --sky-tint: #e9f0fc; 29 + --ember: #bb5a36; /* rare warm accent (the ink/press counterweight) */ 30 + 31 + --radius: 12px; 32 + --radius-sm: 8px; 33 + --shadow: 0 1px 2px rgba(28, 26, 22, 0.06), 0 10px 30px -12px rgba(28, 26, 22, 0.18); 34 + --measure: 66ch; 35 + } 36 + 37 + @media (prefers-color-scheme: dark) { 38 + :root { 39 + --paper: #111319; 40 + --paper-raised: #181b23; 41 + --panel: #1b1f2a; 42 + --ink: #ece6da; 43 + --ink-soft: #c3bcad; 44 + --muted: #938b7d; 45 + --line: #292d39; 46 + --line-strong: #353a49; 47 + --sky: #7aa2ff; 48 + --sky-strong: #a9c2ff; 49 + --sky-tint: #1a2235; 50 + --ember: #e08a63; 51 + --shadow: 0 1px 2px rgba(0, 0, 0, 0.4), 0 18px 40px -16px rgba(0, 0, 0, 0.6); 52 + } 53 + } 54 + 55 + *, 56 + *::before, 57 + *::after { 58 + box-sizing: border-box; 59 + } 60 + 61 + html { 62 + background: var(--paper); 63 + color: var(--ink); 64 + font-family: var(--font-body); 65 + font-size: 18px; 66 + line-height: 1.6; 67 + -webkit-font-smoothing: antialiased; 68 + text-rendering: optimizeLegibility; 69 + color-scheme: light dark; 70 + } 71 + 72 + body { 73 + margin: 0; 74 + min-height: 100vh; 75 + } 76 + 77 + h1, h2, h3, h4 { 78 + font-family: var(--font-display); 79 + font-weight: 540; 80 + font-variation-settings: 'opsz' 40, 'SOFT' 30, 'WONK' 0; 81 + line-height: 1.1; 82 + letter-spacing: -0.01em; 83 + color: var(--ink); 84 + } 85 + 86 + a { 87 + color: var(--sky); 88 + text-underline-offset: 0.18em; 89 + text-decoration-thickness: 1px; 90 + } 91 + a:hover { 92 + color: var(--sky-strong); 93 + } 94 + 95 + ::selection { 96 + background: var(--sky-tint); 97 + } 98 + 99 + /* Mono "eyebrow" / kicker — the indie-press label voice */ 100 + .eyebrow { 101 + font-family: var(--font-mono); 102 + font-weight: 500; 103 + font-size: 0.7rem; 104 + letter-spacing: 0.18em; 105 + text-transform: uppercase; 106 + color: var(--muted); 107 + } 108 + 109 + /* Buttons */ 110 + .btn { 111 + display: inline-flex; 112 + align-items: center; 113 + gap: 0.5rem; 114 + font-family: var(--font-mono); 115 + font-size: 0.82rem; 116 + letter-spacing: 0.04em; 117 + padding: 0.6rem 1.1rem; 118 + border-radius: var(--radius-sm); 119 + border: 1px solid transparent; 120 + cursor: pointer; 121 + text-decoration: none; 122 + transition: transform 0.12s ease, background 0.12s ease, border-color 0.12s ease; 123 + } 124 + .btn:active { 125 + transform: translateY(1px); 126 + } 127 + .btn--primary { 128 + background: var(--sky); 129 + color: #fff; 130 + } 131 + .btn--primary:hover { 132 + background: var(--sky-strong); 133 + color: #fff; 134 + } 135 + .btn--ghost { 136 + background: var(--paper-raised); 137 + color: var(--ink); 138 + border-color: var(--line-strong); 139 + } 140 + .btn--ghost:hover { 141 + border-color: var(--sky); 142 + color: var(--sky); 143 + } 144 + .btn:disabled { 145 + opacity: 0.45; 146 + cursor: not-allowed; 147 + } 148 + 149 + /* Sky atmosphere: a soft horizon gradient for hero/empty surfaces */ 150 + .sky-wash { 151 + background: 152 + radial-gradient(120% 80% at 50% -20%, var(--sky-tint) 0%, transparent 60%), 153 + var(--paper); 154 + }