You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Every wait in the interface looks the same: a line of grey text, or nothing at all. The page holds still, gives no sign of what is coming, and then the whole layout arrives at once. The wait may be short and still feel long, because nothing on screen says the shape of what is being fetched.
A skeleton is not decoration. It is the interface answering "this is what is coming, and where" while it fetches — and the reading surface of this product is a stable, predictable page (§13.2, the note follows the metrics of the default Obsidian theme). A page that pops into existence from a single line of text contradicts that on every navigation.
Origin
Internal, requested by the owner.
Type
Gap, the product does not do it and will.
The state today, surface by surface
There are exactly two ways this product renders a wait, and neither says anything.
A blank screen.RequireSession renders <div className="loading-screen" /> while the session resolves (memorysmith-frontend/src/app/RootLayout.tsx:53), and the CSS for it is min-height: 60vh and nothing else (styles.css:1900). This is what every page load and every deep link shows first: an empty area of viewport, no brand, no motion, no text. It is also what the sign-out path renders on the way to the provider (:76).
A line of grey text..status is padding: 2rem; color: var(--text-soft) (styles.css:127), and it is the whole of what these render:
Where
What is replaced by one line of text
features/structure/VaultLayout.tsx:57
The entire vault layout: sidebar, brand, search box, navigation, breadcrumb and content column
features/note/NotePage.tsx:52
The note: title, properties, body
features/dashboard/LiveDashboard.tsx:55
The KPI tiles and every facet chart
features/graph/GraphPage.tsx:843
The graph canvas
features/structure/TemplatesPage.tsx:89
Each template card
shared/components/Transclusion.tsx:48
An embedded block, mid-paragraph, inside otherwise rendered prose
VaultLayout is the worst of them, because the wait is not scoped to what is loading: the query is for the structure, and the sidebar that will hold it, the header, the search box and the breadcrumb are all withheld until it answers, then appear together. The frame of that screen is known before the request leaves.
And one surface with no wait state at all.DashboardPage.tsx:25 renders vaults?.map(…) straight into the carousel, so while the vaults load the catalogue is simply an empty strip. It is the first thing anybody sees after signing in, and it says nothing whatsoever — not even "loading". A skeleton row of vault cards is the whole fix there.
Nothing in styles.css defines a skeleton, a shimmer or a placeholder block today: there is no @keyframes for one and no class to reuse.
What this must not become
A skeleton for a request that has already failed is worse than the text it replaces.#53 records exactly that defect: LiveDashboard decides with isLoading || !data, so an errored query takes the loading branch and the dashboard says "Loading…" forever. Shipping skeletons over that logic replaces a permanent line of text with a permanent shimmer — a page that looks alive and is dead. #53 lands first, or this issue carries the same fix.
The same rule applies to the three states in general: a skeleton belongs to isPending alone. A background refetch must never blank out content already on screen, and an error must reach the error branch.
A skeleton is not a spinner in a different shape. It has to occupy the size and position of the content that will replace it, or it trades one layout shift for two. That is a per-surface design job — a vault card, a note body, a KPI tile and a facet chart have different frames — and not one generic grey box used everywhere.
It must not talk over the screen reader. Today .status at least announces the word "Loading". Shapes announce nothing, so the placeholder needs aria-busy on the region and the text kept for assistive technology, not deleted along with the visible line.
It must respect prefers-reduced-motion. The project already honours it (styles.css:2173) and a shimmer animation has to join that block, resolving to a static placeholder.
Business rules created or changed
None. This adds a bullet to §13.2, which holds interface rules in prose and reserves no RN code, and it changes nothing in any bounded context. The bullet: a screen waiting for data shows the shape of what is coming, in the position it will occupy, and never a blank area or a bare line of text.
Documents this delivery will update
docs/software-vision.md, §13.2, for the interface rule above.
None of the others: this changes no technical decision, no domain fact and nothing about what whoever arrives installs or operates.
Definition of done
The blank loading-screen is gone. A session resolving shows the brand and the frame of the application, not 60vh of nothing.
VaultLayout renders its own frame immediately — sidebar, header, search box, breadcrumb — and skeletons only the part the query fills. The frame never moves when the data arrives.
The vault catalogue shows skeleton cards instead of an empty strip.
The note, the KPI tiles, the facet charts, the template cards and a transcluded block each have a placeholder matched to their real dimensions, and none of them shifts the page when it resolves.
One reusable skeleton primitive in styles.css, used by all of them. Not six hand-rolled grey boxes.
The shimmer stops under prefers-reduced-motion: reduce, and every skeleton region carries aria-busy with the loading text still available to a screen reader.
What is declaredly left out
Making anything actually faster. This addresses the perceived wait, not the wait. There is no declared latency target for the web surface at all — software-vision.md §14 points at architecture-guide.md §15 for performance targets, and §15 is the error taxonomy; the only p95 numbers in the repository are for the MCP tools (§19). A skeleton is not a substitute for that budget, and it should not be allowed to hide its absence.
Optimistic rendering, prefetch on hover, or any change to the staleTime: Infinity caching policy in app/query-client.ts.
Progress indication with a percentage, and any change to the export flow, which has its own long-running shape.
The empty state, which is a different message: "there is nothing here" is not "this is coming".
The need, distilled
Every wait in the interface looks the same: a line of grey text, or nothing at all. The page holds still, gives no sign of what is coming, and then the whole layout arrives at once. The wait may be short and still feel long, because nothing on screen says the shape of what is being fetched.
A skeleton is not decoration. It is the interface answering "this is what is coming, and where" while it fetches — and the reading surface of this product is a stable, predictable page (§13.2, the note follows the metrics of the default Obsidian theme). A page that pops into existence from a single line of text contradicts that on every navigation.
Origin
Internal, requested by the owner.
Type
Gap, the product does not do it and will.
The state today, surface by surface
There are exactly two ways this product renders a wait, and neither says anything.
A blank screen.
RequireSessionrenders<div className="loading-screen" />while the session resolves (memorysmith-frontend/src/app/RootLayout.tsx:53), and the CSS for it ismin-height: 60vhand nothing else (styles.css:1900). This is what every page load and every deep link shows first: an empty area of viewport, no brand, no motion, no text. It is also what the sign-out path renders on the way to the provider (:76).A line of grey text.
.statusispadding: 2rem; color: var(--text-soft)(styles.css:127), and it is the whole of what these render:features/structure/VaultLayout.tsx:57features/note/NotePage.tsx:52features/dashboard/LiveDashboard.tsx:55features/graph/GraphPage.tsx:843features/structure/TemplatesPage.tsx:89shared/components/Transclusion.tsx:48VaultLayoutis the worst of them, because the wait is not scoped to what is loading: the query is for the structure, and the sidebar that will hold it, the header, the search box and the breadcrumb are all withheld until it answers, then appear together. The frame of that screen is known before the request leaves.And one surface with no wait state at all.
DashboardPage.tsx:25rendersvaults?.map(…)straight into the carousel, so while the vaults load the catalogue is simply an empty strip. It is the first thing anybody sees after signing in, and it says nothing whatsoever — not even "loading". A skeleton row of vault cards is the whole fix there.Nothing in
styles.cssdefines a skeleton, a shimmer or a placeholder block today: there is no@keyframesfor one and no class to reuse.What this must not become
A skeleton for a request that has already failed is worse than the text it replaces. #53 records exactly that defect:
LiveDashboarddecides withisLoading || !data, so an errored query takes the loading branch and the dashboard says "Loading…" forever. Shipping skeletons over that logic replaces a permanent line of text with a permanent shimmer — a page that looks alive and is dead. #53 lands first, or this issue carries the same fix.The same rule applies to the three states in general: a skeleton belongs to
isPendingalone. A background refetch must never blank out content already on screen, and an error must reach the error branch.A skeleton is not a spinner in a different shape. It has to occupy the size and position of the content that will replace it, or it trades one layout shift for two. That is a per-surface design job — a vault card, a note body, a KPI tile and a facet chart have different frames — and not one generic grey box used everywhere.
It must not talk over the screen reader. Today
.statusat least announces the word "Loading". Shapes announce nothing, so the placeholder needsaria-busyon the region and the text kept for assistive technology, not deleted along with the visible line.It must respect
prefers-reduced-motion. The project already honours it (styles.css:2173) and a shimmer animation has to join that block, resolving to a static placeholder.Business rules created or changed
None. This adds a bullet to §13.2, which holds interface rules in prose and reserves no
RNcode, and it changes nothing in any bounded context. The bullet: a screen waiting for data shows the shape of what is coming, in the position it will occupy, and never a blank area or a bare line of text.Documents this delivery will update
docs/software-vision.md, §13.2, for the interface rule above.Definition of done
loading-screenis gone. A session resolving shows the brand and the frame of the application, not 60vh of nothing.VaultLayoutrenders its own frame immediately — sidebar, header, search box, breadcrumb — and skeletons only the part the query fills. The frame never moves when the data arrives.styles.css, used by all of them. Not six hand-rolled grey boxes.isPending. An errored query reaches an error state, and a background refetch leaves rendered content in place. A session that cannot be refreshed leaves the interface loading forever, and only signing out gets out of it #53 is closed, or its fix is part of this.prefers-reduced-motion: reduce, and every skeleton region carriesaria-busywith the loading text still available to a screen reader.What is declaredly left out
software-vision.md§14 points atarchitecture-guide.md§15 for performance targets, and §15 is the error taxonomy; the only p95 numbers in the repository are for the MCP tools (§19). A skeleton is not a substitute for that budget, and it should not be allowed to hide its absence.staleTime: Infinitycaching policy inapp/query-client.ts.