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
555by key fingerprint, not the SSH username (everyone connects as `git@`);555by key fingerprint, not the SSH username (everyone connects as `git@`);
556`channel_open_session` must explicitly `reply.accept()` in 0.62 or the channel is556`channel_open_session` must explicitly `reply.accept()` in 0.62 or the channel is
557rejected; exec output streams through the `Clone + Send` `Handle` from a task.557rejected; 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
562brand 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
564changes. Split `/` into a signed-in dashboard and a public `/explore` landing (the
565anonymous 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
571presence means "serve the file". A NULL falls back to a deterministic FNV-derived
5725×5 identicon SVG generated in-process.
573
574**Alternatives:** an icon font or sprite sheet (extra asset, worse a11y); storing
575avatar 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
577a 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
580request, and keeps the "no CDN / no build step" rule. Carbon is a documented, freely
581usable palette and the swap touches only theme token values, proving the
582structure-only `base.css` contract. On-disk avatars keep the schema in the portable
583subset. Uploads are limited to raster types (PNG/JPEG/GIF/WebP, ≤ 1 MiB); SVG upload
584is refused so no user-supplied markup is ever served, while our own identicons are
585SVG. Both mutating routes (`POST /settings`, `POST /settings/avatar`) verify the
586double-submit CSRF token; `/settings` redirects anonymous visitors to `/login`.