Frontend Overview
CruzJS ships a frontend stack built on React (via React Router v7), Chakra UI for complex interactive components, and Tailwind CSS for utility-first styling. The result is a flexible system where you reach for the right tool depending on the complexity of what you are building.
Technology Stack
Section titled “Technology Stack”| Layer | Technology | Purpose |
|---|---|---|
| Routing & SSR | React Router v7 | File-based routes, loaders, actions, server-side rendering |
| Data fetching | tRPC + React Query | Type-safe API calls, caching, optimistic updates |
| Complex components | Chakra UI | Modals, toasts, form controls, accessibility primitives |
| Utility styling | Tailwind CSS | Layout, spacing, typography, color, responsive design |
| Component library | @cruzjs/ui | Pre-built cards, headers, state components |
Design Philosophy
Section titled “Design Philosophy”CruzJS follows a dual-styling approach rather than committing exclusively to one CSS strategy:
- Tailwind CSS handles the majority of styling: layout, spacing, typography, colors, borders, and responsive breakpoints. Every component uses Tailwind utility classes directly in JSX.
- Chakra UI is used selectively for components that benefit from built-in accessibility, animation, and state management: modals, toasts, spinners, form controls, and disclosure patterns.
This means you will rarely write custom CSS files. Instead, you compose styles inline:
// Tailwind for layout and visual styling<div className="flex items-center gap-4 rounded-xl border border-slate-200 bg-white p-6"> <h3 className="text-sm font-semibold text-slate-700 uppercase tracking-wide"> Section Title </h3></div>
// Chakra for interactive components with built-in accessibilityimport { Modal, ModalOverlay, ModalContent, useDisclosure } from '@chakra-ui/react';
const { isOpen, onOpen, onClose } = useDisclosure();Component Organization
Section titled “Component Organization”Frontend code lives in two main locations:
packages/ui/src/components/
Section titled “packages/ui/src/components/”The @cruzjs/ui package exports reusable, framework-level components used across the dashboard, org pages, and settings views:
packages/ui/src/components/ StatCard.tsx # Metric display card with icon and color PageHeader.tsx # Page title with optional action button SectionCard.tsx # Grouped content card with optional header DetailRow.tsx # Label-value row with icon ConfirmModal.tsx # Confirmation dialog (Chakra-based) StateComponents.tsx # LoadingState, EmptyState, PermissionDenied TabNavigation.tsx # Horizontal tab bar OrgHeader.tsx # Organization banner with avatar and stats ActionItem.tsx # Clickable list item with icon and descriptionImport them from the @cruzjs/ui package:
import { StatCard, PageHeader, SectionCard, LoadingState } from '@cruzjs/ui';apps/web/src/
Section titled “apps/web/src/”Application-specific pages and feature components live in the main app:
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 filesStyling Conventions
Section titled “Styling Conventions”Tailwind Patterns
Section titled “Tailwind Patterns”CruzJS uses a consistent set of Tailwind patterns across all components:
// Cards and containers<div className="rounded-xl border border-slate-200 bg-white p-6">
// Section headings<h3 className="text-sm font-semibold text-slate-700 uppercase tracking-wide">
// Body text<p className="text-base text-slate-600">
// Muted/secondary text<p className="text-xs text-slate-500 font-medium">
// Primary action buttons<button className="px-4 py-2 bg-[#003DCC] text-white font-medium rounded-lg hover:bg-[#0031A3] transition-colors">
// Danger variant<button className="px-4 py-2 bg-red-500 text-white font-medium rounded-lg hover:bg-red-600 transition-colors">Color System
Section titled “Color System”The primary brand color is #003DCC (a deep blue). The UI uses the Tailwind slate palette for neutral grays. Accent colors come from Tailwind’s built-in palettes:
| Usage | Color |
|---|---|
| Primary / brand | #003DCC |
| Success | emerald-500 |
| Warning | amber-500 |
| Danger | red-500 |
| Info | cyan-500 |
| Neutral backgrounds | slate-50 through slate-100 |
| Text | slate-900 (primary), slate-500 (secondary) |
Responsive Design
Section titled “Responsive Design”Use Tailwind responsive prefixes for adaptive layouts:
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4"> <StatCard icon={<UsersIcon />} label="Members" value={42} color="primary" /> <StatCard icon={<ActivityIcon />} label="Active" value={38} color="emerald" /></div>Data Flow
Section titled “Data Flow”Every page follows the same data flow pattern:
- The component calls a tRPC procedure via React Query hooks (
useQueryoruseMutation). - The tRPC client sends an HTTP request to
/api/trpc/*with auth and org headers attached automatically. - The server resolves the procedure, runs middleware, executes business logic, and returns data.
- React Query caches the response and triggers a re-render with the new data.
- The component renders using
@cruzjs/uicomponents and Tailwind styling.
import { trpc } from '~/trpc/client';import { StatCard, PageHeader, LoadingState } from '@cruzjs/ui';
export default function DashboardPage() { const { data, isLoading } = trpc.dashboard.stats.useQuery();
if (isLoading) return <LoadingState size="xl" text="Loading dashboard..." />;
return ( <div> <PageHeader title="Dashboard" description="Your project at a glance" /> <div className="grid grid-cols-1 md:grid-cols-3 gap-4 mt-6"> <StatCard icon={<UsersIcon />} label="Users" value={data.userCount} color="primary" /> <StatCard icon={<ChartIcon />} label="Revenue" value={`$${data.revenue}`} color="emerald" /> </div> </div> );}Next Steps
Section titled “Next Steps”- Components — the full
@cruzjs/uicomponent library - tRPC Client — fetching and mutating data
- Layouts — dashboard, org, and public layout system
- Forms — form patterns with Zod validation