Skip to content

Frontend Overview

CruzJS ships a frontend stack built on React (via React Router v7), @cruzjs/ui for a complete component library, and Tailwind CSS v4 for utility-first styling. The component library has zero external dependencies — no Chakra UI, no Radix, no Headless UI, no Floating UI. Every component is built from React, CSS, and Web APIs.

LayerTechnologyPurpose
Routing and SSRReact Router v7File-based routes, loaders, actions, server-side rendering
Data fetchingtRPC + React QueryType-safe API calls, caching, optimistic updates
Component library@cruzjs/ui124+ production-grade components with zero external deps
Utility stylingTailwind CSS v4Layout, spacing, typography, color, responsive design
Design systemCSS variablesThemeable color tokens, dark mode, brand customization

@cruzjs/ui provides 124 components organized into 9 categories: Primitives, Forms and Inputs, Layout, Overlays and Feedback, Navigation, Data Display, Application Blocks, Marketing Blocks, and Documentation Blocks. Every component is built from scratch using React and CSS — there are no third-party UI dependencies to manage, version, or bundle.

import { Button, Modal, Avatar, Badge, DataTable, CommandPalette } from '@cruzjs/ui';

All components consume CSS variables from the CruzJS design system. No hardcoded colors — everything themes automatically when you change a variable.

TokenUsage
--color-primaryBrand color (default: indigo)
--color-primary-lightLighter brand variant
--color-primary-darkDarker brand variant
--color-successPositive actions, online status
--color-warningCaution states
--color-dangerDestructive actions, errors
--color-infoInformational highlights
--color-surfaceBackground surfaces
--color-textPrimary text
--color-text-secondarySecondary text
--color-text-mutedMuted/disabled text

Override CSS variables to apply your brand across the entire library:

:root {
--color-primary: #6366f1;
--color-primary-light: #818cf8;
--color-primary-dark: #4f46e5;
--color-surface: #ffffff;
--color-text: #18181b;
}

All 124 components immediately reflect these overrides. No prop drilling, no theme provider wrappers — just CSS.

Components use useIsMobile() to adapt behavior by platform:

  • Dropdowns/Selects become bottom sheets on mobile
  • Modals become full-screen sheets on mobile
  • Tooltips are not rendered on mobile (info shown inline)
  • Sidebars become full-screen overlays on mobile
  • Touch targets are minimum 44px on mobile

Every component ships with:

  • Correct ARIA roles, states, and properties
  • Full keyboard navigation (Tab, Enter, Space, Escape, Arrow keys)
  • Focus management (trap in modals, restore on close)
  • Screen reader announcements
  • prefers-reduced-motion support
  • WCAG compliance verified via Storybook a11y addon

No window/document access at module load time. All browser APIs are guarded behind useEffect or runtime checks. useIsMobile() returns false on the server, ensuring server-rendered markup is valid and hydration is clean.

@cruzjs/ui is included in all CruzJS projects by default. To add it manually:

Terminal window
pnpm add @cruzjs/ui

Peer dependency: react >= 18.0.0

All components are exported from the package root:

import {
AppShell,
Navbar,
Sidebar,
PageShell,
Modal,
DataTable,
CommandPalette,
Input,
Select,
Button,
} from '@cruzjs/ui';

Frontend code lives in two locations:

The @cruzjs/ui package exports 124 reusable components organized into 9 categories:

CategoryCountExamples
Primitives11Alert, Avatar, Badge, Card, Spinner, Tooltip
Forms and Inputs20Input, Select, DatePicker, RichTextEditor, FileUploadZone
Layout8AppShell, Sidebar, Navbar, PageShell, SettingsLayout
Overlays and Feedback11Modal, Drawer, CommandPalette, Notification, Progress
Navigation7Tabs, Accordion, Stepper, Pagination, SegmentedControl
Data Display17DataTable, Timeline, StatsGrid, Tree, DragAndDropList
Application Blocks22LoginBlock, OrgSwitcher, TeamRoster, PricingCards, WebhookManager
Marketing Blocks14HeroSection, FeatureGrid, TestimonialCarousel, Footer
Documentation Blocks6DocSidebar, CodeBlock, ExamplePreview, Changelog

Application-specific pages and feature components:

apps/web/src/
routes/ # React Router route modules (pages)
features/ # Feature-specific components and logic
trpc/ # tRPC client setup and router type
database/ # Drizzle schema and seed files

Many components accept a renderLink prop for framework-specific routing:

import { Link } from 'react-router';
<Sidebar
groups={groups}
renderLink={({ href, children, className }) => (
<Link to={href} className={className}>{children}</Link>
)}
/>
import { AppShell, Navbar, Sidebar } from '@cruzjs/ui';
import { Link, Outlet, useLocation } from 'react-router';
function Layout() {
const location = useLocation();
return (
<AppShell
header={
<Navbar
items={navItems}
activeId={location.pathname}
renderLink={({ href, children, className }) => (
<Link to={href} className={className}>{children}</Link>
)}
/>
}
sidebar={
<Sidebar
groups={sidebarGroups}
activeId={location.pathname}
renderLink={({ href, children, className }) => (
<Link to={href} className={className}>{children}</Link>
)}
/>
}
>
<Outlet />
</AppShell>
);
}

Every page follows the same pattern:

  1. The component calls a tRPC procedure via React Query hooks.
  2. The tRPC client sends an HTTP request to /api/trpc/* with auth and org headers attached automatically.
  3. The server resolves the procedure, runs middleware, executes business logic, and returns data.
  4. React Query caches the response and triggers a re-render.
  5. The component renders using @cruzjs/ui components and Tailwind styling.
import { trpc } from '~/trpc/client';
import { PageShell, StatsGrid, Spinner } from '@cruzjs/ui';
export default function DashboardPage() {
const { data, isLoading } = trpc.dashboard.stats.useQuery();
if (isLoading) return <Spinner size="xl" />;
return (
<PageShell title="Dashboard" description="Your project at a glance">
<StatsGrid stats={[
{ label: 'Users', value: data.userCount, trend: '+12%' },
{ label: 'Revenue', value: `$${data.revenue}`, trend: '+8%' },
]} />
</PageShell>
);
}

Every component ships with Storybook stories covering all variants, sizes, states, and mobile viewports. Run Storybook locally:

Terminal window
pnpm --filter @cruzjs/ui storybook

See the Storybook guide for details.

The component library includes 2,095 tests across 118 test files:

Terminal window
pnpm --filter @cruzjs/ui test