The variant pipeline is async, so right after upload all four URLs in
profile.avatar_urls are still null. The first wiring attempt called
pickAvatarUrl() which returned null, and nothing visible changed even
though the upload + PATCH succeeded.
Fixes:
- pickAvatarUrl: use the actual backend keys (small/medium/large/
original — there's no "thumbnail").
- After upload, when no variant URL is ready, fetch the raw object
via /api/v1/digital_objects/:id/content as a blob URL for immediate
display. Persist that URL to localStorage so the appbar's
useProfile() picks it up via the storage event.
- ProfileBootstrap: detect stale blob: URLs cached from previous
sessions, clear them, and refetch a fresh blob URL when variants
still aren't ready. Eventually the persistent variant URLs land
and overwrite.
- Force-remount AvatarImage via key={src} in the profile page and
appbar — base-ui's Avatar.Image keeps internal load state that
doesn't always reset on src change.
- Diagnostic logs in fetchDigitalObjectAsBlobUrl + the upload flow
to make next debug round one step easier (kept; cheap).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
components/
Component layers in this project.
ui/ shadcn primitives — token-driven, reskinnable per design system
forms/ composed form widgets
data/ data display (tables, filters, empty states)
layout/ app shell, page chrome, navigation wrappers
marketing/ landing and marketing blocks
[system]/ design-system-specific components (e.g. m3/, apple/)
The one rule
Custom components import from ui/, never the reverse.
ui/ is the primitive layer. It must stay reskinnable by swapping tokens in
app/themes/*.css alone. If a component can't be expressed that way (M3
ripple, Apple segmented control, etc.), it belongs in a system-specific
folder — not ui/ and not the shared folders above.
Tokens, not values
Every custom component should reference semantic tokens:
- Colors:
bg-primary,text-muted-foreground,border-border - Radius:
rounded-md,rounded-lg - Fonts:
font-sans,font-heading
Hardcoded hex, oklch, or px values are a bug — they break theming.