steid

@jamesgill /

docs: settle the profile page on flat navigation

Decided after looking at the built page with real data rather than imagining it.
It had three equal-weight sections, two of them empty boxes, and repositories
sorted alphabetically so dotfiles outranked steid — a third of the page was
content and two thirds advertised incompleteness.

The replacement is a tab bar rather than stacked sections, which turns empty
sections from visible failures into destinations that happen to be empty, and
makes Writing shippable later without redesigning around it. One lead item gets
real typographic weight, then a compact list; that lead is the editorial layer
portfolio-first requires, and ordering alone cannot carry it. Hairlines and
hover states, never bordered containers.

This deliberately breaks ui.md's own density rules, so the file now says why:
those rules govern the tool surfaces where code is the hero, and the profile is
the portfolio surface whose job is to make an impression rather than be
operated. A third kind of surface should pick one, not split the difference.

Also recorded, because it cost a wrong mockup: Tailwind only ships classes the
app already uses, since build.rs scans the real sources. A mockup written
against the served stylesheet silently loses any new utility — gap-7 collapsed a
nav into one run-together string with no error anywhere. Throwaway design
iteration has to be plain CSS against the theme's custom properties, and dark
mode is a .dark class rather than prefers-color-scheme.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JZwc7URWKVhkAuRTWiDmjA
JamesPatrickGill authored 7 days agoparent4ef3945Browse filesf0afae5e0f3c91983096350491069c0ed95925b5

2 files changed+84 −0

CLAUDE.md+6 −0View file
@@ -124,6 +124,12 @@ Working knowledge that is easy to get wrong and slow to rediscover:
124124 `*` is not part of the name. The whole tail arrives as one percent-decoded string.
125125 Matching happens on the *raw* path, which is why `%2F` inside a `{rev}` segment
126126 survives as a single segment and decodes to a slash.
127+- **Tailwind only ships classes the app already uses.** `build.rs` scans the real
128+ sources, so a class that appears nowhere in `src/` is absent from the built CSS and
129+ fails silently — spacing collapses, nothing errors. A throwaway mockup must therefore
130+ be written in plain CSS against the theme's custom properties, not in Tailwind against
131+ the served stylesheet. Dark mode is a `.dark` class on an ancestor, not
132+ `prefers-color-scheme`.
127133 - **UI components reference theme tokens, never raw colours** — see `styles.css`. A
128134 hardcoded colour follows neither a palette change nor the colour scheme. Registry
129135 components are copied in by `topcoat ui add`, not depended on.
plans/ui.md+78 −0View file
@@ -29,6 +29,84 @@ Steid is portfolio-first, not a Gitea clone. The profile page is the product —
2929 are one kind of thing on it, alongside writing and projects. Any layout inherited from
3030 a GitHub-shaped forge needs checking against that before it's copied.
3131
32+## The profile page
33+
34+Settled 2026-08-29, after looking at the built page with real data rather than
35+imagining it. Three sections of equal weight — Repositories, Writing, Projects — two of
36+them empty boxes, repositories sorted alphabetically so `dotfiles` outranked `steid`.
37+A third of the page was content and two thirds advertised incompleteness.
38+
39+**Flat, app-style navigation. No cards, no boxes.**
40+
41+```
42+James Gill
43+@jamesgill
44+<bio>
45+<links>
46+─────────────────────────────────────────────
47+Overview Repositories 3 Writing
48+─────────────────────────────────────────────
49+CURRENTLY BUILDING
50+steid Rust
51+A self-hostable gitforge where the profile is the product.
52+421 tests · updated 2 hours ago
53+
54+REPOSITORIES All 3 →
55+──────────────────────────────────────────
56+topcoat-notes 3d
57+Notes on building with Topcoat 0.5.
58+──────────────────────────────────────────
59+dotfiles 2w
60+```
61+
62+- **A tab bar, not stacked sections.** Empty sections stop being visible failures and
63+ become destinations that simply have nothing in them yet. It is also what makes
64+ Writing shippable without redesigning the page around it.
65+- **Tabs are links.** No JavaScript, and each tab is a real URL that can be shared.
66+- **One lead item with genuine typographic weight**, then a compact list. This is the
67+ editorial layer portfolio-first requires: without it the page has no opinion about
68+ what matters, and ordering alone cannot carry that.
69+- **Hairlines and hover states, never bordered containers.** The row is the unit.
70+- **Recency, not alphabetical.** Alphabetical is a filing rule; a portfolio needs an
71+ editorial one.
72+- **Sections with nothing in them are hidden from visitors** and shown to the owner as
73+ an affordance.
74+
75+### This deliberately breaks the density rules above
76+
77+[Philosophy](#philosophy) says small type and nothing large outside page titles. That
78+still holds — for the **tool** surfaces, where code is the hero and density wins:
79+browsing a tree, reading a blob, the log, settings. The **profile** is the portfolio
80+surface, and it is the one page whose job is to make an impression rather than to be
81+operated. The lead item gets editorial weight there and nowhere else.
82+
83+If a third kind of surface appears, decide which of the two it is rather than
84+splitting the difference.
85+
86+### What it needs that does not exist yet
87+
88+- **`updated_at` on a repository.** The design promises recency and nothing stores it.
89+ Asking git costs one fork per repository — a twelve-repo profile would be ~150ms of
90+ `execve` before rendering. So it is a column, touched when a push is authorized.
91+ Slightly wrong if a push then fails; the alternative is wrong more expensively.
92+- **A `/{handle}/repos` index.** The Repositories tab needs a destination; only
93+ `/{handle}/repos/{name}` exists.
94+- **A way to choose the lead.** A `pinned` flag on the repository, set from the settings
95+ page that now exists. No pin, no lead section — automatic "most recent" would put a
96+ dotfiles tweak at the top of a portfolio.
97+
98+### Mocking is harder than it looks here
99+
100+**Tailwind classes the app does not already use are not in the built stylesheet.**
101+`build.rs` scans the real sources, so a mockup written against the served CSS silently
102+loses any new utility — `gap-7` and `tracking-widest` collapsed a nav into
103+`OverviewRepositories3WritingProjects` before this was understood. Iterate either in the
104+app itself or, for throwaway exploration, in plain CSS against the theme's custom
105+properties (`--background`, `--foreground`, `--muted-foreground`, `--border`, `--surface`).
106+
107+Dark mode is a `.dark` class on an ancestor, not `prefers-color-scheme`; a mockup
108+without it renders light.
109+
32110 ## Stack
33111
34112 Settled in [0005](decisions/0005-tailwind-and-copied-components.md).