From f4e6da9210eabe576eb26d0a065630ed4b693811 Mon Sep 17 00:00:00 2001 From: "Leivur R. Djurhuus" Date: Sat, 28 Feb 2026 22:01:40 -0600 Subject: [PATCH] Add performance optimizations and accessibility improvements Dynamic imports for heavy components (Kanban, Gantt, CommandPalette), skip-to-content link, ARIA landmarks/labels on sidebar, breadcrumbs, topbar notifications, kanban board, gantt timeline, and pipeline progress. Focus-visible ring styles for keyboard navigation. Co-Authored-By: Claude Opus 4.6 --- package-lock.json | 28 +++++++++++++++++++ package.json | 1 + src/app/(app)/layout.tsx | 6 ++-- .../(app)/projects/[projectId]/board/page.tsx | 7 ++++- .../projects/[projectId]/timeline/page.tsx | 7 ++++- src/app/error.tsx | 4 +-- src/app/globals.css | 23 +++++++++++++++ src/app/layout.tsx | 6 ++++ src/app/not-found.tsx | 2 +- .../deliverables/pipeline-progress.tsx | 9 +++++- src/components/layout/breadcrumbs.tsx | 7 +++-- src/components/layout/sidebar.tsx | 6 ++-- src/components/layout/topbar.tsx | 17 +++++++++-- src/components/lazy-command-palette.tsx | 12 ++++++++ src/components/views/gantt-timeline.tsx | 6 +++- src/components/views/kanban-board.tsx | 9 +++++- 16 files changed, 132 insertions(+), 18 deletions(-) create mode 100644 src/components/lazy-command-palette.tsx diff --git a/package-lock.json b/package-lock.json index 74e165a..5249800 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,6 +16,7 @@ "@tailwindcss/postcss": "^4.2.1", "@tanstack/react-query": "^5.90.21", "@tanstack/react-table": "^8.21.3", + "@tanstack/react-virtual": "^3.13.19", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "cmdk": "^1.1.1", @@ -4915,6 +4916,23 @@ "react-dom": ">=16.8" } }, + "node_modules/@tanstack/react-virtual": { + "version": "3.13.19", + "resolved": "https://registry.npmjs.org/@tanstack/react-virtual/-/react-virtual-3.13.19.tgz", + "integrity": "sha512-KzwmU1IbE0IvCZSm6OXkS+kRdrgW2c2P3Ho3NC+zZXWK6oObv/L+lcV/2VuJ+snVESRlMJ+w/fg4WXI/JzoNGQ==", + "license": "MIT", + "dependencies": { + "@tanstack/virtual-core": "3.13.19" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/@tanstack/table-core": { "version": "8.21.3", "resolved": "https://registry.npmjs.org/@tanstack/table-core/-/table-core-8.21.3.tgz", @@ -4928,6 +4946,16 @@ "url": "https://github.com/sponsors/tannerlinsley" } }, + "node_modules/@tanstack/virtual-core": { + "version": "3.13.19", + "resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.13.19.tgz", + "integrity": "sha512-/BMP7kNhzKOd7wnDeB8NrIRNLwkf5AhCYCvtfZV2GXWbBieFm/el0n6LOAXlTi6ZwHICSNnQcIxRCWHrLzDY+g==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, "node_modules/@tybys/wasm-util": { "version": "0.10.1", "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", diff --git a/package.json b/package.json index 0ae8245..b59b581 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ "@tailwindcss/postcss": "^4.2.1", "@tanstack/react-query": "^5.90.21", "@tanstack/react-table": "^8.21.3", + "@tanstack/react-virtual": "^3.13.19", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "cmdk": "^1.1.1", diff --git a/src/app/(app)/layout.tsx b/src/app/(app)/layout.tsx index bf4dd68..7bb7c6a 100644 --- a/src/app/(app)/layout.tsx +++ b/src/app/(app)/layout.tsx @@ -2,7 +2,7 @@ import { Suspense } from "react"; import { Sidebar, MobileSidebar } from "@/components/layout/sidebar"; import { Topbar } from "@/components/layout/topbar"; import { QueryProvider } from "@/components/query-provider"; -import { CommandPalette } from "@/components/command-palette"; +import { LazyCommandPalette } from "@/components/lazy-command-palette"; export default function AppLayout({ children }: { children: React.ReactNode }) { return ( @@ -11,13 +11,13 @@ export default function AppLayout({ children }: { children: React.ReactNode }) {
-
+
{children}
- + ); } diff --git a/src/app/(app)/projects/[projectId]/board/page.tsx b/src/app/(app)/projects/[projectId]/board/page.tsx index f1916ab..9ff91ce 100644 --- a/src/app/(app)/projects/[projectId]/board/page.tsx +++ b/src/app/(app)/projects/[projectId]/board/page.tsx @@ -1,10 +1,15 @@ "use client"; +import dynamic from "next/dynamic"; import { useParams } from "next/navigation"; import { useDeliverables, useUpdateStageStatus } from "@/hooks/use-deliverables"; -import { KanbanBoard } from "@/components/views/kanban-board"; import { Skeleton } from "@/components/ui/skeleton"; +const KanbanBoard = dynamic( + () => import("@/components/views/kanban-board").then((m) => m.KanbanBoard), + { ssr: false } +); + export default function BoardViewPage() { const { projectId } = useParams<{ projectId: string }>(); const { data: deliverables, isLoading } = useDeliverables(projectId); diff --git a/src/app/(app)/projects/[projectId]/timeline/page.tsx b/src/app/(app)/projects/[projectId]/timeline/page.tsx index f2ea743..0a2abf2 100644 --- a/src/app/(app)/projects/[projectId]/timeline/page.tsx +++ b/src/app/(app)/projects/[projectId]/timeline/page.tsx @@ -1,10 +1,15 @@ "use client"; +import dynamic from "next/dynamic"; import { useParams } from "next/navigation"; import { useDeliverables } from "@/hooks/use-deliverables"; -import { GanttTimeline } from "@/components/views/gantt-timeline"; import { Skeleton } from "@/components/ui/skeleton"; +const GanttTimeline = dynamic( + () => import("@/components/views/gantt-timeline").then((m) => m.GanttTimeline), + { ssr: false } +); + export default function TimelineViewPage() { const { projectId } = useParams<{ projectId: string }>(); const { data: deliverables, isLoading } = useDeliverables(projectId); diff --git a/src/app/error.tsx b/src/app/error.tsx index fb247a6..a33800f 100644 --- a/src/app/error.tsx +++ b/src/app/error.tsx @@ -16,9 +16,9 @@ export default function GlobalError({ }, [error]); return ( -
+
- +