fabrica

hanna/fabrica

docs: log the UI refresh decisions

015cda1 · hanna committed on 2026-07-25

Record the inline-icon vendoring, IBM Carbon theme rebase, dashboard/explore
split, and the profile/avatar design (on-disk bytes + portable avatar_mime,
identicon fallback, no remote gravatar).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed by hannaSSH key fingerprint: SHA256:4g9cWhkAAw8gwqhJUcVbSnGEvdGwklW+1Aa/fMUu59k
1 files changed · +29 −0UnifiedSplit
docs/decisions.md +29 −0
@@ -555,3 +555,32 @@ re-exports `ssh-key` at a patch level distinct from the workspace's, so the two
555555 by key fingerprint, not the SSH username (everyone connects as `git@`);
556556 `channel_open_session` must explicitly `reply.accept()` in 0.62 or the channel is
557557 rejected; exec output streams through the `Clone + Send` `Handle` from a task.
558+
559+## 2026-07-25 — UI refresh: inline icons, Carbon themes, dashboard/explore split, profiles
560+
561+**Decision:** Vendor UI glyphs as an inline-SVG `icons` module (Lucide, ISC) with
562+brand marks from Simple Icons (CC0), replacing the emoji glyphs. Rebase the default
563+`dark`/`light` themes on IBM Carbon (Gray 100 / White) as pure `--fb-*` token
564+changes. Split `/` into a signed-in dashboard and a public `/explore` landing (the
565+anonymous landing carries no in-content sign-in button). Give users a profile
566+(display name, pronouns, bio, location, website, GitHub/Mastodon handles) edited at
567+`/settings`, and avatars served from `/avatar/{username}`.
568+
569+**Avatar storage:** the bytes live on disk at `{storage.data_dir}/avatars/{id}`;
570+`users.avatar_mime` (nullable TEXT, migration 0002) records the content type and its
571+presence means "serve the file". A NULL falls back to a deterministic FNV-derived
572+5×5 identicon SVG generated in-process.
573+
574+**Alternatives:** an icon font or sprite sheet (extra asset, worse a11y); storing
575+avatar bytes in the DB (BLOB vs BYTEA is not portable across SQLite/Postgres, which
576+§ store portability forbids); Gravatar or any remote avatar (a network request from
577+a page, forbidden by §9.5); a single combined home page (the prior behaviour).
578+
579+**Rationale:** inline SVG inherits `currentColor`, scales to `1em`, needs no network
580+request, and keeps the "no CDN / no build step" rule. Carbon is a documented, freely
581+usable palette and the swap touches only theme token values, proving the
582+structure-only `base.css` contract. On-disk avatars keep the schema in the portable
583+subset. Uploads are limited to raster types (PNG/JPEG/GIF/WebP, ≤ 1 MiB); SVG upload
584+is refused so no user-supplied markup is ever served, while our own identicons are
585+SVG. Both mutating routes (`POST /settings`, `POST /settings/avatar`) verify the
586+double-submit CSRF token; `/settings` redirects anonymous visitors to `/login`.