Documentation Index
Fetch the complete documentation index at: https://grantmaster.dev/llms.txt
Use this file to discover all available pages before exploring further.
GrantMaster Global UI Refactor Notes
Phase 0 Inventory
Canonical shared surfaces
- App shell:
src/Layout.tsx - Main chrome:
src/components/layout/Header.tsx,src/components/layout/Sidebar.tsx - Canonical page wrappers:
src/components/layout/PageLayout.tsx,src/components/layout/PageHeader.tsx,src/components/layout/PageShell.tsx - Canonical page tabs:
src/components/ui/PageTabs.tsx - Core cards:
src/components/ui/card.tsx,src/components/widgets/infowidgets/KpiCard.tsx - Widget/dashboard wrappers:
src/components/widgets/WidgetContainer.tsx,src/components/widgets/WidgetDashboardBase.tsx - Page templates:
src/components/page-templates/BentoDashboardTemplate.tsx,src/components/page-templates/RegistryPageTemplate.tsx,src/components/page-templates/FeedPageTemplate.tsx - Overlay primitives:
src/components/ui/modal-system/AppModal.tsx,src/components/ui/modal-system/AppDrawer.tsx,src/components/modals/ModalRenderer.tsx - Table/list primitives:
src/components/ui/data-table/DataTable.tsx,src/components/ui/SearchFilterBar.tsx - Empty/data states:
src/components/ui/EmptyState.tsx,src/components/layout/PageStatePanel.tsx,src/components/ui/DataStateWrapper.tsx
Duplication and drift
PageLayoutexists in bothsrc/components/layout/PageLayout.tsxandsrc/components/ui/PageLayout.tsx. Theuiexport is a thin wrapper, but max-width and padding mappings are duplicated.PageHeaderexists in bothsrc/components/layout/PageHeader.tsxandsrc/components/ui/PageHeader.tsx. Theuifile is only a re-export, but the split still creates ambiguity.- Card styling is fragmented across
ui/card.tsx,ui/CardVariants.tsx,widgets/infowidgets/KpiCard.tsx,ui/widget-system.tsx, and many feature-local wrappers. - Empty states are fragmented across
ui/EmptyState.tsx,PageStatePanel.tsx,DataStateWrapper.tsx, widget empty states, and feature-local placeholders. - List/table wrappers are split between
RegistryPageTemplate,FeedPageTemplate,SearchFilterBar,DataTable, and many feature-local toolbars. - Modal patterns are split between
AppModal,AppDrawer, rawDialogContent, rawSheetContent, and several legacy custom modal shells.
Repeated one-off layout patterns
- Repeated
space-y-6,gap-6,p-5,p-6,p-8,max-w-*,min-w-*, and hardcoded modal/table widths acrosssrc/featuresandsrc/components. - Many dashboard and people pages use local KPI wrappers instead of the shared KPI surface.
- Several feature pages still compose
PageLayout+PageTabs+ conditional rendering manually instead of usingPageShell.
Rendering hotspots
- Many tabbed pages still use
activeTab === ... && <Component />, which remounts tab content on every switch. DashboardTabs.tsxintentionally hydrates visited tabs, which is a good direction and should become the standard for heavy overview surfaces.Layout.tsxowns sidebar width, collapse state, assistant state, mobile menu state, and shell rendering in one component, so page-local route changes still pass through a large shell.PageShell.tsxrecreates action nodes and tab arrays on every render. Not critical by itself, but it sits on a common path.WidgetDashboardBase.tsxalready lazy-loads widgets and delays hydration, so later phases should align chart-heavy pages with that pattern instead of re-solving it page by page.DataTable.tsxalready supports virtualization, but many feature tables still use custom table markup and bypass that path entirely.
Shared map for phased replacement
- Shared layout components:
LayoutPageLayoutPageHeaderPageShellPageTabs
- Page shell components:
PageLayoutPageHeaderPageStatePanelSearchFilterBar
- Dashboard/widget primitives:
WidgetContainerWidgetDashboardBaseKpiCardMetricCardSurfaceMetricGridSurfaceSection
- Modal primitives:
AppModalAppDrawerModalRenderer
- Table/list wrappers:
DataTableRegistryPageTemplateFeedPageTemplateSearchFilterBar
- Chart wrappers:
- Widget dashboard surfaces under
src/components/widgets - Chart-heavy overview tabs in
src/features/dashboard,src/features/grants, andsrc/features/users
- Widget dashboard surfaces under
Batch Plan
Phase 1
- Normalize shared layout and spacing tokens in
src/index.css - Introduce canonical page shell primitives for container, section, grids, KPI rows, and split layouts
- Introduce a unified card system with KPI, data, action, and empty-state variants
- Replace the existing decorative empty-state primitive with a stable shared empty-state surface while keeping backwards compatibility for current callers
- Update canonical wrappers first:
PageLayout,PageHeader,PageTabs,ui/card,EmptyState
Phase 2+
- Move route-level tab pages from manual
PageLayout+PageTabscomposition towardPageShell - Convert overview/dashboard families to the shared grid and KPI row primitives before touching denser data modules
- Migrate data-heavy modules onto shared table/filter wrappers instead of adding local toolbar/table shells
- Collapse remaining raw
DialogContentandSheetContentusage into the shared modal and drawer primitives - Standardize keep-mounted tab strategies for heavy dashboards, charts, and editors where remount costs are visible
Phase 4 Notes
BentoDashboardTemplateis now the canonical overview/dashboard composition layer for KPI rows and 12-column analytics rows.- Overview widgets should prefer shared widget empty states over zero-value shells so sparse data still reads as intentional.
SurfaceSectionis the shared executive dashboard card wrapper; row height, side-panel density, and title/meta spacing should be solved there first.- Quick actions belong inside a subdued side-panel card, not as a competing primary surface.
Phase 5 Notes
RegistryPageTemplateandFeedPageTemplateare now the canonical Phase 5 seams for data-dense module cleanup. Shared toolbar, selection, and empty-state decisions should land there before touching individual tabs.SurfaceToolbaris the standard filter and result-count wrapper for list, feed, and registry views. Page-level actions stay in the page header; list-local actions belong in the toolbar.SurfaceSelectionBaris the standard sticky batch-action surface for list and table selection. Batch-selection styling should no longer be rebuilt per module.- Data-heavy wrappers should render inside
DataCardsurfaces so table shells, feed shells, and no-data/loading states inherit the same spacing, radius, and border language. - Later Phase 5 follow-ups should target row-density behavior, long-list virtualization adoption, and eventual migration of custom registry tables toward the shared
DataTablepath where it is safe.
Phase 6 Notes
AppModalandAppDrawernow share the same close-behavior contract: non-dismissible overlays block escape, pointer-dismiss, drag-dismiss, and the inline close button.AppDrawernow owns a real drawer sizing system (sm/md/lg/xl/full) instead of forcing width through localSheetContentclasses.DrawerFooteris now the canonical drawer action row and intentionally mirrorsModalFooterso button placement stays consistent between dialogs and drawers.- Shared form primitives (
FormSection,FormField,FormActions,FormGrid) now carry the tokenized spacing and text rhythm for overlay forms. Local modal forms should lean on those instead of re-specifying label/help/error spacing. ConfigDrawerTemplateis the canonical settings/configuration overlay wrapper for field-heavy drawers.ApprovalDomainFormandTemplateEditorModalwere moved onto the shared drawer/modal system as Phase 6 reference migrations. Additional rawDialogContentandSheetContentusages should be collapsed into this path incrementally in later batches.
Phase 7 Notes
SurfaceMetricSummaryRowis now the standard framing layer for analytical cards that need supporting metrics above charts. Avoid dropping isolated values or percentages into chart cards without that summary context.WidgetDonutandWidgetSparklinenow lazy-load theirrechartspayloads behind stable placeholders so heavy chart code is deferred until those widgets actually mount.- Shared chart widgets must preserve stable minimum heights in both empty and loaded states to avoid card jump and legend drift.
WidgetDonutnow treats input data as immutable; legend ranking should always sort a copied array instead of mutating caller-provided data.- The representative analytics tabs in Grants Tracker, Post-Award Financials, and Applications Overview now use the summary-row + chart structure that later analytics pages should follow.
Phase 8 Notes
- Module-local wrappers should collapse toward shared shell and surface primitives before page-level redesign work. This batch moved Settings, Support, Documents, Compliance Policies, and Grantors further onto that path without touching business logic.
SettingsPageHeadernow rides on the shared card/header system, so settings pages inherit the same spacing, radius, and typography rhythm as the rest of the app instead of carrying a bespoke banner treatment.- Support sub-tabs should not nest
PageLayoutinsideSupportPage. Local subpage intros now useSurfacePageHeader, and content cards should use sharedDataCard,SurfaceToolbar,SurfaceSection, andSurfaceEmptyStateprimitives. CoverageTabandSmartSearchTabare the reference pattern for routed sub-tabs that need local framing inside an already-mounted page shell: use a local intro card plus shared analytical sections, not another page shell.- Grantors should keep using the module-local helpers, but those helpers now need to delegate to the shared surfaces (
SurfaceInsightList,DataCard,SurfaceSection,SurfaceToolbar) instead of rebuilding near-duplicate cards and table wrappers. DocumentBrowserempty states now flow through the sharedEmptyStateprimitive; future document module cleanup should continue removing local empty/list placeholders in favor of the shared state surfaces.
Phase 9 Notes
- Default page content padding now compresses responsively (
p-4->p-5->p-6) so smaller desktop widths keep more usable horizontal space without changing page structure. PageHeaderaction areas now prefer wrapping over horizontal scrolling, which keeps title/action rows readable when local action sets grow on narrower desktops.PageTabsnow reserves side padding for scroll affordances only when the tab rail actually overflows. Non-overflowing tab bars no longer waste width on smaller desktop screens.SearchFilterBarand the toolbar surfaces that depend on it now stay stacked untilxl, with the search field allowed to shrink fully. Filters and actions wrap instead of forcing cramped inline layouts at mid-desktop widths.BentoDashboardTemplatenow keeps KPI rows and filter capsules stacked untilxl, and the 12-column dashboard rows also wait untilxlbefore splitting. This avoids thin, panic-width widgets on small desktop windows.PageGrid12-column layouts now collapse to a single column belowlg, and registry tables now use a consistent minimum width inside controlled horizontal scroll containers instead of collapsing headers and cells into unreadable widths.
Phase 10 Notes
KeepAliveTabPanelsis now the shared visited-tab primitive for route-driven tab pages that should preserve mounted state after first visit. Use it when remounting tabs causes noticeable panel churn or lazy-loading flicker.- Workflow, Documents, and Policies route tabs now keep visited panels mounted instead of remounting the subtree on every tab switch.
PageTabsno longer uses extra mount-time state to discover the tablist id or right-fade visibility; those values are now derived so the shared tab shell commits less during initial page mount.PageShellnow memoizes mapped actions, breadcrumbs, and tab models so route-driven shell pages avoid rebuilding common chrome inputs unless the resolved config actually changes.- The route perf budget regression for workflow tab switches now passes again, so the shared tab-shell path is back within the expected commit envelope.
Phase 11 Notes
- Shared surface depth is intentionally calmer now:
--shadow-cardand--shadow-card-hoverwere reduced, and the canonicalcardSurfaceClassNameno longer swaps to a heavier dark-mode-only shadow. New surfaces should not reintroduce louder one-off shadow stacks. - Shared action sizing now runs through the
--action-height-*tokens inButton, including icon buttons. Page actions, toolbar actions, and overflow triggers should inherit those sizes instead of hardcoding local heights. Badgenow has a standardized minimum height, line-height, icon sizing, and text rhythm so count pills and metadata chips align consistently across cards, tables, and headers.SurfaceEmptyStateno longer nests aCardaroundEmptyStateCard, which removes a duplicated border/shadow layer from no-data views. Future empty states should avoid wrapping the shared empty-state primitive in another card unless there is a genuine compositional reason.SurfacePageHeader,SurfaceNotice,SurfaceActionBar, and metric summary surfaces now lean on the same title, radius, and padding tokens as the rest of the app. Final polish work should continue collapsing executive surfaces toward those primitives instead of reintroducing bespoke hero/header treatments.