feat: skeleton loading UIs to replace text-based loading states (closes #198)#321
Merged
feat: skeleton loading UIs to replace text-based loading states (closes #198)#321
Conversation
Add @Keyframes shimmer using a background-position sweep (from -200% to 200%) with linear easing for a smooth, continuous wave effect. Register --animate-shimmer: shimmer 1.5s linear infinite in @theme inline. All gradient colours reference var(--color-surface-*) tokens — no hardcoded hex.
Reusable shimmer placeholder primitive with: - variant: 'text' | 'rect' | 'circle' (controls shape and defaults) - width / height: CSS value overrides - count: renders N stacked items via the Stack primitive - aria-hidden on every root element (purely visual, screen-reader silent) - Shimmer applied via animate-shimmer class + inline gradient style using var(--color-surface-elevated/hover) tokens for full dark-theme compatibility
Co-located skeleton template matching the ModelsListContent row layout: 6 rows of py-md px-base border-b with a name line and three badge rects. Replaces 'Loading models...' text in Phase 3.
Co-located skeleton template matching the conversation list item layout: 5 items of border border-border rounded-base with a title line (70%) and a relative-timestamp line (35%, 0.75em). Replaces 'Loading conversations...' text in Phase 3.
Co-located skeleton template matching the ModelCard layout: rounded-xl card with model name, id line, param badge, and download/like stats. Used in Phase 3 to replace the hardcoded animate-spin spinner during HF search.
Co-located skeleton template matching the inspector panel layout: - Header bar: model name rect + three circle icon-button placeholders - Metadata section: section heading + 4 label:value rows - Tags section: section heading + 3 rounded-full tag pill placeholders - Actions row: two button-height rects Shown in Phase 3 when loading and no model is selected yet (initial fetch).
Co-located skeleton template matching the ChatPage two-column layout: - Left panel (30%): header approximation (tabs + model name + search) + ConversationListSkeleton - Right panel (70%): 4 alternating message rects (self-start / self-end) Used in Phase 3 as the Suspense fallback while the ChatPage JS chunk loads.
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.
Closes #198
Summary
Replaces all text-based loading states (
"Loading models...","Loading conversations…", etc.) with polished shimmer skeleton UIs that mirror the real content layout, eliminating layout shift and making the app feel faster.This PR covers Phase 1 (foundation) and Phase 2 (co-located templates). Phase 3 (wiring the skeletons into the live loading states) follows in a separate PR once this is reviewed.
Changes
Phase 1 — Foundation
src/styles/tailwind.css@keyframes shimmer: background-position sweep (-200% 0→200% 0) withlineareasing for a smooth, continuous wave (not ease-in-out, which causes jarring acceleration)--animate-shimmer: shimmer 1.5s linear infinitein@theme inlinevar(--color-surface-elevated)/var(--color-surface-hover)— zero hardcoded hexsrc/components/primitives/Skeleton.tsx(new)variant(text | rect | circle),width,height,className,countaria-hidden="true"on every root element — screen readers skip purely visual placeholdersanimate-shimmerclass +style={{ background: gradient, backgroundSize: '200% 100%' }}count > 1wraps N copies in the existingStackprimitiveprimitives/index.tsPhase 2 — Co-located Skeleton Templates
Each template lives beside the feature it mirrors (no central
skeletons/directory). All useStack/Rowprimitives — no raw flex div soup.src/components/ModelLibraryPanel/ModelListSkeleton.tsxModelsListContentrowspy-md px-base border-b+ name line (55%) + 3 badge rectssrc/components/ConversationListPanel/ConversationListSkeleton.tsxborder rounded-base+ title (70%) + timestamp (35%)src/pages/ChatPageSkeleton.tsxConversationListSkeleton) + right 70% (4 alternating message rects)src/components/HuggingFaceBrowser/ModelCardSkeleton.tsxModelCardrounded-xlcard: name + id line + param badge + statssrc/components/ModelInspectorPanel/ModelInspectorSkeleton.tsxModelInspectorPanelDesign decisions
lineareasing:ease-in-outmakes the wave visibly speed up/slow down on each loop —lineargives the continuous, even flow of a real shimmeraria-hidden="true": skeletons are purely visual; parent containers will usearia-busyin Phase 3 if neededSkeletoninprimitives/(notui/): it is a layout-level primitive, consistent withCard,Stack,RowVerification
tsc --noEmitexits clean (EXIT:0) with all 7 commits.