Your calm window into the Atmosphere. morgen.blue
rss atproto
3
fork

Configure Feed

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

chore: add morgenblau-designer skill from feat-auth

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

+262
+262
.claude/skills/morgenblau-designer/SKILL.md
··· 1 + --- 2 + name: morgenblau-designer 3 + description: Morgenblau's complete design language for all UI work — surface layers, control tokens, color rules, typography, radius, the Window primitive, Caveat annotations, motion, voice, icons, anti-patterns. Use this skill whenever designing or building Morgenblau UI — new components, new screens, layout changes, or any edit that touches how Morgenblau looks, feels, sounds, or moves. Invoke before choosing classNames, sizes, colors, radii, or writing UI copy for Morgenblau. Also invoke when reviewing a Morgenblau PR for visual or tonal consistency. 4 + --- 5 + 6 + # Morgenblau designer 7 + 8 + This skill encodes the design language for Morgenblau — a calm content platform powered by RSS and ATProto, delivering daily digests instead of infinite feeds. The goal of every design decision is the product's core promise: **intentionality without deprivation.** 9 + 10 + This is **design language only**. Implementation details (file paths, CSS tokens, React primitives) live in `src/` and in `plans/morgenblau-design-implementation.md`. If you need to know _how_ to realize a rule, look there — not here. 11 + 12 + --- 13 + 14 + ## North star 15 + 16 + - **80 / 20.** 80 % of the surface area should feel clean, minimal, straight-to-the-point. The remaining 20 % is personality — delivered through copy, Caveat annotations, and subtle motion. Never through louder color or extra decoration. 17 + - **Texture: crisp morning.** The terrace on a clear morning, not the candlelit cafe. Clean sans-serifs, cool blues, precise transitions, generous whitespace. 18 + - **Craft reference cluster** (taste, not concrete examples): Linear's precision, Family / Benji Taylor's warmth, Emil Kowalski's restraint, Josh Puckett's animation care, Dieter Rams' "less but better." 19 + - **Motion metaphor: ripples settling on water, not springs.** Ease-out dominant. Overshoot and bounce are reserved for rare delight moments. 20 + - **The window, and the newspaper.** Two metaphors. The Window is something you choose to look through, then step away from. The newspaper is the feeling of a finite object with a start and an end — a ritual, not a habit. Never an infinite feed, never an inbox, never an "unread count." 21 + - **Identity.** The user isn't managing subscriptions. They're the **editor of their own daily publication.** 22 + 23 + If a design decision doesn't clearly serve one of these, simplify it away. 24 + 25 + --- 26 + 27 + ## Surface layers — closer = _lighter_ 28 + 29 + Morgenblau inverts the common "shadows signal elevation" pattern. Closeness to the user is expressed by **luminance**, not shadow. Surfaces rise toward the user by getting lighter. 30 + 31 + | Level | Light surface | Light border | Dark surface | Dark border | Example | 32 + | ---------------------- | ------------- | ------------ | ------------ | ----------- | -------------------------------------------------------------- | 33 + | **0** — base | gray-100 | — | gray-950 | — | the page background, behind everything | 34 + | **1** — one above base | gray-50 | gray-200 | gray-900 | gray-800 | the Window. A card sitting on bare base (e.g. the login card). | 35 + | **2** — two above base | white | gray-100 | gray-800 | gray-700 | a card inside the Window (e.g. a digest card). | 36 + 37 + **Principle:** the same direction in both modes — closer is lighter. In light mode the ladder climbs toward white. In dark mode it climbs from near-black toward mid-gray. 38 + 39 + **Level is a property of the element, not its container.** A surface primitive takes its level explicitly. Default is `2` (the common case — a card inside the Window). A card placed directly on the base gets level `1`. This makes surfaces predictable and keeps the same primitive working in Window-framed and bare-base contexts. 40 + 41 + **Do not** stack deeper than two levels above base. If you find yourself needing a level 3, you've composed too many cards — restructure. 42 + 43 + --- 44 + 45 + ## Controls invert the layer system 46 + 47 + Surfaces lighten as they come forward. **Controls do the opposite: they step forward by contrasting _against_ the surface brightness trend.** Two axes, opposite directions. 48 + 49 + | Role | Rule | L0 light | L1 light | L2 light | L0 dark | L1 dark | L2 dark | 50 + | ----------------------- | ---------------------------------- | ------------------------------------------------------------------------------------------------ | -------- | -------- | -------- | -------- | -------- | 51 + | **Input bg** | one step past surface | gray-50 | gray-100 | gray-50 | gray-700 | gray-800 | gray-700 | 52 + | **Input border** | two steps past | gray-200 | gray-200 | gray-100 | gray-600 | gray-700 | gray-600 | 53 + | **Secondary button bg** | two steps past | gray-100 | gray-200 | gray-100 | gray-600 | gray-700 | gray-600 | 54 + | **Primary button** | color-defined, not level-dependent | soft atmosphere-blue tint in both modes (filled tint, tinted border, atmosphere-blue foreground) | | | | | | 55 + 56 + **In light mode controls go darker; in dark mode they go lighter.** The rule holds: controls always stand _against_ the surface trend, so they visually step forward. 57 + 58 + **Exception — level 0 (base) inverts the inversion.** On the darkest surface, there is no darker step available in light mode (and no lighter step in dark mode), so the rule flips: on L0, controls go **lighter** than the surface, not darker. On L0 the input **bg** matches the L2 bg, but the **border goes a step darker than L2's** (gray-200 in light mode) so the edge still reads against the base; dark mode reuses the L2 border (gray-600) unchanged because it already stands out against gray-950. 59 + 60 + **Focus state — a 1 px outline on the perceived element.** Morgenblau draws its own outline so the indicator lands on the element the user actually perceives — not always the natively focusable one. Pattern: `outline-none` base, then `focus-visible:outline-solid focus-visible:outline-1 focus-visible:outline-offset-2 focus-visible:outline-ring` on controls, and `focus-within:…` on compound containers (e.g. `InputGroup`, where the native input sits inside a wrapper with an addon — the outline belongs on the wrapper, not the inner input). `outline-solid` is explicit — Tailwind v4 has no bare `outline` utility, so the style has to be set by name. The result looks native: thin, solid, 2 px offset, atmosphere-blue. No box-shadow ring, no border-color swap, no halo. Error/invalid states (`aria-invalid`) keep their own softer ring so focus and validation stay distinguishable. 61 + 62 + **Two button variants only: primary (soft blue) and secondary (gray).** There is no solid / dark / black variant. When a button needs to feel more critical, the answer is **copy and placement**, not a louder color. A confirmation word, a more prominent position, a Caveat hint — but never a third button variant. 63 + 64 + --- 65 + 66 + ## Color 67 + 68 + Morgenblau's palette is almost entirely monochrome, with two exceptions: one **brand accent** and four **category markers**. 69 + 70 + | Token | Role | 71 + | ----------------- | --------------------------------------------------------------------------------------- | 72 + | `atmosphere-blue` | the one non-monochrome accent — primary actions, focus rings, highlights. Nothing else. | 73 + | `leaf-green` | longform / blogpost category | 74 + | `sunset-orange` | micropost category | 75 + | `coral-red` | video category | 76 + | `aurora-purple` | podcast category | 77 + | `sand-brown` | **sunrise-horizon only** — gradient endpoints. Never text. Never surfaces. | 78 + 79 + **Harmony to preserve:** `atmosphere-blue`, `leaf-green`, and `sunset-orange` share lightness and chroma (only the hue rotates). This is intentional — they sit side by side in a digest without one dominating. `coral-red` and `aurora-purple` are slightly punchier so video and podcast, which are more attention-seeking formats, earn a bit more visual pull. Do not break this harmony when introducing new colors; extend the same chroma + lightness pattern if a future category arrives. 80 + 81 + **Rules of use:** 82 + 83 + - `atmosphere-blue` is for intent: the primary action, a focus ring, a moment of discovery. Not for decoration, not for chrome, not for backgrounds. 84 + - Category colors appear **only on their content type**. A leaf-green swatch on anything that isn't longform breaks the system. 85 + - `sand-brown` appears **only** as the bottom endpoint of a sunrise gradient (e.g. the login panel). Not as a surface tone, not as a text color, not as a border. 86 + - Grays do all the structural work: surfaces, borders, controls, typography. 87 + 88 + --- 89 + 90 + ## Typography 91 + 92 + **Geist** for almost everything. **Caveat** for annotations — and only annotations (see the annotation section). 93 + 94 + **Two font colors, ever:** 95 + 96 + - **Primary** (darker) — headings, body text 97 + - **Secondary** (lighter, `muted-foreground`) — hints, metadata, all Caveat 98 + 99 + Scale is **calm, not dramatic**. h1 is not 40 px. Hierarchy is built more with weight than size. 100 + 101 + **Three weights, ever.** No bold, no semibold. Hierarchy is carried by the delta between weights plus size plus tracking, not by heavy strokes. 102 + 103 + | Weight | Role | 104 + | ------------- | ------------------------------------------ | 105 + | 300 — light | paragraphs, body text, hints, descriptions | 106 + | 400 — regular | labels, metadata, UI chrome text | 107 + | 500 — medium | headings (h1–h6), card titles | 108 + 109 + These defaults are wired into base styles (`h1–h6 → font-medium`, `p → font-light`), so plain tags pick them up automatically. Only override when the semantic tag doesn't match the role (e.g. a `<span>` acting as a heading). 110 + 111 + | Token | Size | Weight | Tracking | Use | 112 + | ----------- | --------- | ------ | ------------------ | --------------------------- | 113 + | `text-xs` | 0.75 rem | 300 | normal | tiny meta (timestamps) | 114 + | `text-sm` | 0.875 rem | 300 | normal | secondary text, hints | 115 + | `text-sm` | 0.875 rem | 400 | normal | labels | 116 + | `text-base` | 1 rem | 300 | normal | body | 117 + | `text-lg` | 1.125 rem | 500 | `tracking-tight` | small headings, card titles | 118 + | `text-xl` | 1.25 rem | 500 | `tracking-tight` | section headings (h3) | 119 + | `text-2xl` | 1.5 rem | 500 | `tracking-tighter` | page headings (h1 / h2) | 120 + 121 + **Rules:** 122 + 123 + - h1 caps at 1.5 rem (about 1.5× body). Whispered, not shouted. 124 + - **Tracking tightens as size grows** — body is normal, mid-size headings are `tracking-tight` (-0.025em), large headings are `tracking-tighter` (-0.05em). Tight tracking at scale is a craft signature; it reads as intentional. 125 + - In long-form text (article reader), titles and paragraphs differ mainly by **weight**, not size. Titles go medium (500), paragraphs stay light (300) — the delta reads as hierarchy without size jumps. 126 + - Caveat renders visually smaller than Geist at the same nominal size — bump it up one step when placed beside Geist body. Caveat's usable weights are 400–700; let it pick the nearest when used inside `<p>` (it reads as regular either way). 127 + - **Caveat is always secondary color.** Never primary. Never atmosphere-blue. Never a category color. 128 + 129 + --- 130 + 131 + ## Radius — ladder scaled to element size 132 + 133 + | Size | Use | 134 + | -------------------------- | ----------------------------------- | 135 + | `rounded-sm` (0.125 rem) | tags, chips | 136 + | `rounded-lg` (0.5 rem) | small badges, icon wells | 137 + | `rounded-xl` (0.75 rem) | **buttons, inputs** | 138 + | `rounded-2xl` (1 rem) | tight containers | 139 + | `rounded-3xl` (1.5 rem) | secondary cards | 140 + | `rounded-4xl` (2 rem) | **primary cards** (e.g. login card) | 141 + | asymmetric 4 rem / 0.5 rem | **Window only** | 142 + 143 + **Principle: radius scales with element size.** A 2 rem radius on a button makes it a lozenge; a 0.75 rem radius on a hero card makes it uptight. Match the roundness to the surface area. 144 + 145 + The overall UI should feel generously rounded — never square, never pill-shaped by default. The Window's asymmetric radii are the one exception; everything else stays on the symmetric ladder. 146 + 147 + --- 148 + 149 + ## The Window 150 + 151 + Morgenblau's core layout metaphor. Two invariants. 152 + 153 + **1. Asymmetric radii.** Top ~4 rem (opens skyward); bottom ~0.5 rem (grounded). Symmetric radii read as a modal. The asymmetry is the point — the Window sits on something, and opens toward something. 154 + 155 + **2. One per screen.** Either: 156 + 157 + - framing primary content (e.g. the digest), or 158 + - serving as a decorative anchor (e.g. the sunrise sky panel on the login screen). 159 + 160 + Never both on the same screen. Never two Windows stacked or side by side. When an article opens, it **replaces** (sheets over) the current Window rather than layering on top — the metaphor stays singular. 161 + 162 + The Window's insets, positioning, and scroll behavior are **consumer-level** concerns and vary by context. They do not belong in this skill; treat them as an implementation detail of whatever layout is hosting the Window. 163 + 164 + --- 165 + 166 + ## Caveat annotations — the reader's pencil 167 + 168 + Caveat is a small, warm hand laid on top of a crisp page. It's the reader's pencil mark, the editor's note in the margin. To keep it from becoming decoration, Caveat is allowed in exactly four places and forbidden everywhere else. 169 + 170 + **Allowed uses (always secondary color):** 171 + 172 + 1. **Input hints** — one line below a form field. Short. Example: _"New here? Enter your handle without `@`."_ 173 + 2. **Content meta** — time-to-read, relative date, source name next to a card. Example: _"4 min · 2 days ago"_ next to a Geist title. 174 + 3. **Margin asides** on long-form reading surfaces only (article reader, settings pages, docs) — a short pencil-mark thought in the margin. Never on digest rows, cards, or forms. 175 + 4. **Empty-state personality** — the second line of an empty-state block can be Caveat. Example: _"Nothing new this morning."_ (Geist body) / _"Enjoy your coffee."_ (Caveat, smaller, secondary). 176 + 177 + **Forbidden uses:** 178 + 179 + - As a button label 180 + - As a heading or section title 181 + - Inside buttons, cards, or any interactive surface label 182 + - For navigation, tabs, or menu items 183 + - As the primary copy in any block — Caveat always accompanies Geist body that carries the information; it never carries the information alone 184 + 185 + **Never:** hand-drawn decorations (circles, arrows, underlines) outside long-form reading surfaces. They belong to marketing and long-form contexts, not to the app chrome or digest. 186 + 187 + --- 188 + 189 + ## Motion 190 + 191 + **Defer to the `web-animation-design` skill for the fundamentals** — `ease-out` for enter/exit, `ease-in-out` for on-screen morphing, `ease` for hovers, `transform` + `opacity` only, `prefers-reduced-motion` on every animation, under 300 ms for UI, no animation for 100×/day actions. That skill holds the base rules; don't re-derive them here. 192 + 193 + **Morgenblau-specific rules that layer on top:** 194 + 195 + 1. **Ripple, not spring.** Default: no bounce, no overshoot. Stricter than the baseline — springs are actively avoided on product surfaces. 196 + 2. **Springs are reserved for exactly two situations:** 197 + - **Social affirmations** — like, save, follow. Subtle Apple-style `{ duration: 0.4, bounce: 0.2 }` maximum. The one place delight lands in motion. 198 + - **New edition arrival** — when the morning, lunch, or evening fetch lands, the first card of the new edition settles in with a whisper of spring. **Once per edition**, not per card. 199 + 3. **Two duration tokens:** 200 + - **UI duration (~180 ms)** — hovers, button press, dropdowns, tooltips, input focus, card hover lift. 201 + - **Scene duration (~320 ms)** — Window → sheet (article open), edition switch, day change, auth → home transition. 202 + 4. **Window sheet-replacement.** When an article opens, a new Window sheets up from below with `ease-out-quint`, ~320 ms, `translateY(100%) → 0`. Dismissal is faster (~260 ms) with `ease-out-cubic`. Exits are always faster than entrances. 203 + 5. **What never animates:** 204 + - Calendar strip horizontal scroll when driven by arrow keys (used daily; must feel instant) 205 + - Day number re-render inside a cell 206 + - Focus ring appearance (must be visible immediately) 207 + 6. **Anti-patterns:** 208 + - **Staggered card entrances on digest load.** Digest rows appear together — no waterfall reveal. Reinforces "a finite object, all arrived at once." 209 + - **Parallax or scroll-driven effects on cards.** Content is the point; decoration isn't. 210 + - **Pulsing skeleton screens.** Use a soft fade-in when content resolves, not a pulse. 211 + 212 + --- 213 + 214 + ## Voice & tone 215 + 216 + **Register:** calm and direct, like a thoughtful newspaper editor. No corporate voice. No cheerleader voice. No LLM voice ("I'll help you..."). One step warmer than Linear, one step cooler than Superhuman. 217 + 218 + **Rules:** 219 + 220 + 1. **Short sentences.** Most copy fits in one line. Two lines only if the second is a gentle afterthought (the Caveat line). 221 + 2. **Second person is default.** _"You curated this"_ — not _"Users can curate..."_ 222 + 3. **No exclamation marks. Ever.** The product is calm; the copy mirrors it. 223 + 4. **No emoji.** Ever. 224 + 5. **No "unread," no counts, no urgency language.** _"Just 3 left to read!"_ is anti-Morgenblau. 225 + 6. **Empty states are features.** Name the calm, don't apologize for it. 226 + 7. **Errors are matter-of-fact.** _"Couldn't reach that feed. We'll try again at lunch."_ No "Oops," "Uh oh," or "Something went wrong." 227 + 8. **Prefer concrete over abstract.** _"Morning edition"_ beats _"New content digest."_ 228 + 9. **First-party language.** _"Your publication"_ — not _"your account."_ _"Sources"_ — not _"subscriptions."_ The user is the editor, not a subscriber. 229 + 230 + **The voice test:** _Would a calm editor with an espresso say this at 7 am without performing?_ If no, rewrite. 231 + 232 + --- 233 + 234 + ## Icons 235 + 236 + - **Library:** Hugeicons (React). 237 + - **Variant: stroke.** Solid and two-tone feel heavy next to the calm typography. 238 + - **Sizes:** 1 rem (tight inline, e.g. inside small buttons or chip adornments), 1.125 rem (standard inline with body text), 1.25 rem (primary navigation, Window chrome). 239 + - **Stroke weight:** Hugeicons' stroke default (1.5 px). Do not override. 240 + - **Color: `currentColor`.** Icons inherit the text color of their surrounding context — primary, secondary, or atmosphere-blue depending on where they sit. Icons almost never need their own color token. 241 + - **Pairing with text:** `inline-flex items-center gap-[0.5em]`. 242 + 243 + --- 244 + 245 + ## Anti-patterns (the list) 246 + 247 + If a Morgenblau design exhibits any of these, something has gone wrong: 248 + 249 + - **Pure-white body background.** The base is `gray-100`; white belongs to level-2 cards. 250 + - **Symmetric Window corners.** The top/bottom asymmetry is the metaphor. Equal radii = modal, not Window. 251 + - **More than one Window per screen.** The metaphor is singular. 252 + - **Dramatic type scale** (h1 at 2+ rem, big jumps between levels). Everything above 1.5 rem reads as shouting. 253 + - **Atmosphere-blue used decoratively** (backgrounds, chrome, non-functional surfaces). Blue is for intent; grays do structure. 254 + - **Category colors outside their content type.** A leaf-green swatch on a video card breaks the system. 255 + - **`sand-brown` as a text or surface color.** It's the sunrise-horizon endpoint — nothing else. 256 + - **Spring physics by default on UI motion.** The motion metaphor is ripples settling, not springs bouncing. 257 + - **Staggered card entrances on digest load.** All content arrives together. 258 + - **Hand-drawn decoration on app chrome, digest rows, or forms.** Those belong to marketing and long-form surfaces only. 259 + - **A third button variant** (solid, black, dark). Two variants carry every action. Critical actions earn emphasis through copy and placement, not louder buttons. 260 + - **Caveat as primary copy, button label, heading, or nav item.** Caveat accompanies Geist; it never carries meaning alone. 261 + - **Unread counts, progress indicators, "X items left" badges.** These are anti-Morgenblau. 262 + - **Exclamation marks or emoji in UI copy.** Breaks the voice.