UI refinement - #32
Merged
Merged
Conversation
Responsive: the records type-nav grid and the overview sidebar grid had no breakpoint variants, so both overflowed on phones. They now stack, with the type nav becoming a horizontally scrolling row. The collection tab bar and version info bar scroll/wrap rather than overflow. Tables: wrappers on the landing page, owner page, record and schema detail, and version compare used overflow-hidden, which clipped wide content instead of scrolling it. All now use overflow-x-auto (pre blocks left alone — they scroll internally). Accessibility: add a prefers-reduced-motion block, and a shared useDismissable hook so UserMenu, CreateMenu, and the version picker all close on Escape and return focus to their trigger. Share dialogs close on Escape and carry role="dialog"/aria-modal. Share tokens: the /schemas/:id loader fetched without forwarding ?token, so a shared-link viewer following a schema hash out of a private collection hit a 404. It now uses apiUrlBuilder like its sibling routes. llms.txt: document the web URL scheme (version prefix + view segment, bare semver) so agents can link people to a view, not just fetch from the API.
The primitives added earlier only covered the pages rebuilt at the time, so
roughly two thirds of the app still carried the original drift. This applies
them across the rest: landing, explore, owner profile, protocol, invitations,
collection surfaces, record and schema detail, and the steward-only admin
tools.
Radius is now fully tokenized: 99 bare rounded/rounded-sm/rounded-lg classes
are gone, leaving rounded-control (4px) on things people touch,
rounded-surface (2px) on containers, and rounded-full only on avatars, status
dots, and progress bars. MirrorAdmin's rounded-lg panels were the app's only
instance of that radius.
Buttons collapse from 16 hand-inlined copies and 5 competing hover treatments
to the shared variants; several gained real disabled states they were missing
despite being disabled. Alerts, inputs, selects, textareas, tables, badges,
and empty states move onto the primitives. Semantic colors stay where they
carry meaning: diff added/updated/removed headers, expired-key row tints, and
API response status.
Primitives gained what the sweep proved they needed: an lg button size for
landing CTAs, a success variant for approve actions (three sites were
hand-rolling green buttons), and a resize prop on Textarea.
Fixes found along the way: the active pagination link, the active record-type
pill, and the "Manage account" button all lost their light text once visited,
because the global a:visited{color:inherit} rule outranks .text-parchment.
QueryExplorer's sticky table header lost its bottom rule when the body
scrolled, since a border on a tr does not travel with a sticky header. The
landing hero's requestAnimationFrame animation now honors prefers-reduced-
motion, which CSS alone cannot stop. Docs code blocks and the prose styles use
the radius tokens instead of hardcoded 3px/4px.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Design system. New
src/components/ui.tsx(Button, ButtonLink, Input/Select/Textarea/Checkbox/Field, Alert, Table, Tabs, Badge, EmptyState, SectionHeading) plus radius tokens:--radius-control(4px) on things people touch,--radius-surface(2px) on structural containers. Barerounded/rounded-sm/rounded-lgwent from 99 occurrences to zero;rounded-fullsurvives only on avatars, status dots, and progress bars. Inline primary buttons went 16 → 3 (a toast, a pagination marker, an active pill — none are buttons), and five competing hover treatments collapsed to one.Settings. Three surfaces (user, org, collection) had three different navigation idioms and a tab bar copy-pasted into seven files with hardcoded active states. They now share one
SettingsLayoutwith a sticky left rail — route-driven for user/org, scroll-tracked anchors for collection settings. Auth moved torequireAuthmiddleware for org and collection settings, which previously did a render-timewindow.locationredirect./settings/avatarwas an orphan route with zero inbound links; its working upload is folded into Profile.Collection URLs. Version is now a path prefix, views are path segments, and omitting the prefix means latest:
/:owner/:slug/:owner/:slug/records/schemas/files/:owner/:slug/v/1.2.0[/records|/schemas|/files]/:owner/:slug/versionsand/versions/compareThis replaces
?tab=files,?tab=metadata,/schemas?version=,/diff, and the double-v/v/v1.0.0form — all of which now 302 to their new homes, so no existing link breaks. Page semver is bare (/v/1.2.0) since the API normalizes both forms.Confirmed the wire protocol does not constrain page URLs: negotiate/push/pull and manifests are
/api/*plus content hashes, webhooks carry{owner, slug, semver, hash}, and ARK identifiers are minted from NAAN + semver. The only server-side dependency was the ARK resolver (src/api/ark.ts), updated.Information architecture. The collection header splits into a collection-wide top row (breadcrumb, visibility, Versions, Settings) and a version-scoped tab row carrying a version picker that preserves your current view when you switch. Previously the "version tab" was a non-clickable
<span>that appeared and disappeared. Schemas honors per-version pinning (the data model always did; the loader just never passed?version=). One overview layout now serves latest and pinned versions alike.Dashboard. Rebuilt on a server loader over
GET /api/collections?mine=true— one request instead of one per organization, which also removes a false "No collections yet" state that showed while ~15 requests resolved. The org sidebar became a facet rail with counts; rows gained description, semver, record count, and last-push time.Fixes found along the way
Responses need a route-levelErrorBoundary, which no route defined (a class error boundary can't catch them). HTTP status was already correct; only the UI was unwired.invisible group-hover:visible) at the far right of a scrolling table. Now a sticky right-edge column. Two rendering bugs behind it: withborder-collapse, borders don't travel with sticky cells, andsticky right-0can leave a subpixel seam.a:visited { color: inherit }outranks Tailwind text colors on specificity, so solid-dark links lost their light text once visited — hit the active pagination link, the active record-type pill, and the "Manage account" button.(collection, version)rows as collections — "appears in 11 collections" for a record in one collection across 11 versions.versions.lengthas the total, so any collection past 100 read "100 versions."Accessibility and responsive
focus-visiblehad zero occurrences app-wide; there's now a universal focus ring. Addedprefers-reduced-motion(including an explicit guard for the landing hero'srequestAnimationFrameloop, which CSS can't stop) and a shareduseDismissablehook so dropdowns close on Escape and return focus. The records and overview grids had no breakpoint variants and overflowed on phones; eight table wrappers clipped instead of scrolling.Notes for review
llms.txtgained a "Web URLs" section; it documented the API exhaustively but never told an agent how to link a human to a view.Verification
pnpm typecheck,pnpm lint,pnpm test(97 passing), andpnpm buildall pass. Every surface was checked in the browser, including legacy-URL redirects (curl -Ion each old shape) and the sticky-column behavior mid-scroll.