Skip to main content

Architecture

BodyWorks uses a layered component architecture that combines Shadcn UI components with custom fitness-specific components and Aceternity UI for advanced animations.

Component Layers

The component system is organized into three distinct layers:
  1. Base UI Layer - Shadcn UI components (New York style)
  2. Custom Component Layer - Fitness-specific components
  3. Animation Layer - Aceternity UI components
// Component configuration from components.json
{
  "style": "new-york",
  "rsc": true,
  "tsx": true,
  "tailwind": {
    "baseColor": "neutral",
    "cssVariables": true,
    "prefix": ""
  },
  "aliases": {
    "components": "@/components",
    "utils": "@/lib/utils",
    "ui": "@/components/ui",
    "lib": "@/lib",
    "hooks": "@/hooks"
  },
  "iconLibrary": "lucide"
}

Shadcn UI Integration

BodyWorks uses Shadcn UI as the foundation for its component library, configured with the New York style variant.

Configuration

  • Style: New York variant
  • RSC: React Server Components enabled
  • TypeScript: Full TypeScript support
  • Base Color: Neutral palette
  • CSS Variables: Enabled for theming
@/components  → components/
@/utilslib/utils
@/ui          → components/ui/
@/lib         → lib/
@/hooks       → hooks/

Custom Component Structure

Custom components are built on top of base UI components and follow a consistent pattern:

Component Patterns

Card Components

  • exercise-card.tsx
  • routine-card.tsx
  • descripted-card.tsx
  • testimonial-card.tsx

Interactive Components

  • search-bar.tsx
  • navbar.tsx
  • footer-navbar.tsx
  • mode-toggle.tsx

Data Display

  • workout-summary-table.tsx
  • pagination-providor.tsx
  • data-loading-skeleton.tsx

Utility Components

  • theme-provider.tsx
  • hint.tsx
  • custom-spinner.tsx

Naming Conventions

Components follow these naming patterns:
  • UI Components: kebab-case (e.g., button.tsx, 3d-card.tsx)
  • Custom Components: kebab-case with descriptive names (e.g., exercise-card.tsx)
  • React Components: PascalCase exports (e.g., ExerciseCard, SearchBar)

Styling Approach

BodyWorks uses Tailwind CSS with a custom configuration and CSS variable-based theming.

Tailwind Configuration

// Using class variance authority for component variants
import { cva } from "class-variance-authority";
import { cn } from "@/lib/utils";

const buttonVariants = cva(
  "inline-flex items-center justify-center gap-2...",
  {
    variants: {
      variant: {
        default: "bg-primary text-primary-foreground...",
        destructive: "bg-destructive text-white...",
        outline: "border bg-background...",
      },
      size: {
        default: "h-9 px-4 py-2",
        sm: "h-8 rounded-md gap-1.5 px-3",
        lg: "h-10 rounded-md px-6",
      },
    },
  }
);

Utility Function

The cn() utility merges Tailwind classes with proper precedence:
import { cn } from "@/lib/utils";

<div className={cn(
  "base-classes",
  condition && "conditional-classes",
  className // Allow prop override
)} />

Theme System

:root {
  --primary: 0 0% 9%;
  --secondary: 0 0% 96.1%;
  --accent: 0 0% 96.1%;
  --destructive: 0 84.2% 60.2%;
  --border: 0 0% 89.8%;
  --ring: 0 0% 63.9%;
}

Component Composition

BodyWorks components are highly composable, allowing for flexible layouts:
import { CardContainer, CardBody, CardItem } from "@/components/ui/3d-card";
import { Button } from "@/components/ui/button";
import Image from "next/image";

export function ExerciseCard({ name, image }: Props) {
  return (
    <CardContainer>
      <CardBody className="group/card ...">
        <CardItem translateZ="100">
          <Image src={image} alt={name} />
        </CardItem>
        <CardItem className="text-xl font-bold">
          {name}
        </CardItem>
      </CardBody>
    </CardContainer>
  );
}

Accessibility

All components follow accessibility best practices:
  • Semantic HTML elements
  • ARIA attributes where needed
  • Keyboard navigation support
  • Focus management
  • Screen reader compatibility
// Example: Button with proper ARIA attributes
<button
  data-slot="button"
  aria-invalid={isInvalid}
  className={cn(buttonVariants({ variant, size }))}
>
  {children}
</button>

Next Steps

UI Library

Explore available UI components and their usage

React Hooks

Learn about custom hooks for data fetching and utilities

Build docs developers (and LLMs) love