Introduction to Exo UI

Welcome to the official documentation for Exo UI — a beautifully crafted, fully accessible, and deeply customizable dashboard template system designed for modern web applications.

Exo UI is built from the ground up to provide teams with a robust starting point for creating analytics dashboards, SaaS admin panels, and complex data-driven layouts. By leveraging the utility-first power of Tailwind CSS, every single component from layout wrappers to fine-grained input controls can be adjusted to match your exact brand guidelines.

Whether you are spinning up a slick prototype or constructing an enterprise-scale architecture, this template delivers a meticulously organized codebase, built-in global dark mode support, fluid responsive design, and seamlessly integrated component libraries. This guide will walk you through the core development paradigms, theming architecture, and layout strategies you need to master Exo UI.

Installation

To get started with the template, install the dependencies and start the development server.

Terminal
# Install project dependencies
npm install

# Start the optimized Vite development server
ng serve

By default, the application will be available at http://localhost:4200.

Terminal
npm install
npm run dev
Terminal
npm install
npm run dev

Advanced Theming & Customization

Exo UI features a robust styling engine powered by Tailwind CSS v4 and native CSS variables. This creates a flexible system where updating a few core variables instantly trickles down to hundreds of UI components intuitively.

The Design Token Architecture

In src/styles.css, you will find `@layer base` defining standard CSS variables under the `:root` selector. These variables map directly to dynamic Tailwind utility classes like bg-primary or text-foreground.

src/styles.css
@layer base {
  :root {
    --background: #ffffff;
    --foreground: #09090b;
    --primary: #18181b;
    --primary-foreground: #fafafa;
    /* Adjust these to drastically recalculate the template's look! */
  }

  /* Seamless Dark Mode Inverses */
  .dark {
    --background: #09090b;
    --foreground: #fafafa;
    --primary: #fafafa;
    --primary-foreground: #18181b;
  }
}

Creating Architectural Themes

Exo UI ships with four color variants out of the box (`zinc`, `blue`, `rose`, `green`). Creating a brand new layout theme implies declaring a class globally overriding the core UI tokens.

/* Create a Custom Purple Theme */
.theme-purple {
  --primary: #9333ea;
  --ring: #9333ea;
}
.theme-purple.dark {
  --primary: #c084fc;
  --ring: #c084fc;
}

Central state management of the UI theme lives in src/app/core/services/theme.service.ts. It uses native Signals to maintain absolute reactive sync with the browser's DOM & memory.

Layouts & Architecture

Functional Standalone Routing

Exo UI relies entirely on modern Angular Standalone Components—meaning absolutely zero legacy ngModule overhead.

Open src/app/app.routes.ts to securely view and modify the primary routing tree structure. Application layouts are safely segregated at the structural root so authentication views are totally isolated from the logged-in dashboard logic.

src/app/app.routes.ts
export const routes: Routes = [
  {
    path: '', 
    component: AppShellComponent,
    children: [
      {
        path: 'dashboard',
        loadComponent: () => import('./features/dashboard/dashboard.component').then(c => c.DashboardComponent)
      }
    ]
  },
  {
    path: 'login',
    component: AuthLayoutComponent // Completely unattached to main AppShell
  }
];

Component Sandboxing & Signal Forms API

  • All generic presentational frontend components sit inside src/app/shared/components/. They strictly rely on `input()` API implementations over old `@Input()` decorators.
  • Exo UI leverages the bleeding-edge Angular Signal forms APIs combined with `Zod` validation schemas for user input payloads. Forms are strongly typed, fully reactive, and completely boilerplate-free natively.

React Router Architecture

In React, routing is configured using modern react-router-dom strategies leveraging nested outlet paths.

Vue Router Architecture

In Vue, view structures are managed cleanly utilizing standard nested routing strategies.

Typography

Exo UI relies on a standard system font stack to ensure zero-latency layout loading and maximum OS-level text crispness. We utilize utility classes like `text-sm`, `font-semibold`, and `tracking-tight` carefully to map directly back to Tailwind.

Heading 1

class="scroll-m-20 text-4xl font-extrabold tracking-tight lg:text-5xl"

Heading 2

class="scroll-m-20 border-b pb-2 text-3xl font-semibold tracking-tight first:mt-0"

Iconography

Exo UI defaults to the Lucide Icons set. Being exclusively SVG based, they are highly accessible, perform crisp scaling, and bypass the payload weight required by large icon fonts.

Button

A flexible button component with multiple variants.

<app-button variant="default">
  Default
</app-button>

<app-button variant="destructive">
  Destructive  
</app-button>
<Button variant="default">
  Default
</Button>

<Button variant="destructive">
  Destructive
</Button>
<Button variant="default">
  Default
</Button>

<Button variant="destructive">
  Destructive
</Button>

Card

A versatile container element.

Project Summary

Monitor your deployments.

Your total deployments ran successfully.
<app-card class="w-full max-w-sm">
  <app-card-header>
    <h3 class="font-semibold leading-none">Project Summary</h3>
    <p class="text-sm text-gray-500">Monitor your deployments.</p>
  </app-card-header>
  <app-card-content class="text-sm text-gray-600">
    Your total deployments ran successfully.
  </app-card-content>
</app-card>
<Card className="w-full max-w-sm">
  <CardHeader>
    <CardTitle>Project Summary</CardTitle>
    <CardDescription>Monitor your deployments.</CardDescription>
  </CardHeader>
  <CardContent className="text-sm text-gray-600">
    Your total deployments ran successfully.
  </CardContent>
</Card>
<Card class="w-full max-w-sm">
  <CardHeader>
    <CardTitle>Project Summary</CardTitle>
    <CardDescription>Monitor your deployments.</CardDescription>
  </CardHeader>
  <CardContent class="text-sm text-gray-600">
    Your total deployments ran successfully.
  </CardContent>
</Card>

Badge

Highlights status or labels.

Default
Error
<app-badge variant="default">Default</app-badge>
<app-badge variant="destructive">Error</app-badge>
<Badge variant="default">Default</Badge>
<Badge variant="destructive">Error</Badge>
<Badge variant="default">Default</Badge>
<Badge variant="destructive">Error</Badge>

Alerts

Display a callout for user attention.

Information
A new update is available.
Error
Action failed.
<div class="relative w-full rounded-lg border p-4 text-gray-900 border-gray-200 pl-11">
  <svg class="h-4 w-4 absolute left-4 top-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
    <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0" />
  </svg>
  <h5 class="mb-1 font-medium leading-none">Information</h5>
  <div class="text-sm text-gray-500">A new update is available.</div>
</div>
<Alert>
  <InfoIcon className="h-4 w-4" />
  <AlertTitle>Information</AlertTitle>
  <AlertDescription>
    A new update is available.
  </AlertDescription>
</Alert>
<Alert>
  <InfoIcon class="h-4 w-4" />
  <AlertTitle>Information</AlertTitle>
  <AlertDescription>
    A new update is available.
  </AlertDescription>
</Alert>

Avatar

An image element with a fallback for representing the user.

AF
Avatar
<div class="inline-flex items-center justify-center bg-gray-200 h-10 w-10 text-sm rounded-full font-medium text-gray-600">
  AF
</div>

<img src="https://i.pravatar.cc/150?u=v" class="h-10 w-10 rounded-full object-cover bg-gray-100" alt="Avatar" />
<Avatar>
  <AvatarImage src="https://i.pravatar.cc/150?u=v" alt="Avatar" />
  <AvatarFallback>AF</AvatarFallback>
</Avatar>
<Avatar>
  <AvatarImage src="https://i.pravatar.cc/150?u=v" alt="Avatar" />
  <AvatarFallback>AF</AvatarFallback>
</Avatar>

Progress

Displays an indicator showing the completion progress.

Loading... 60%
<div class="w-full max-w-sm">
  <div class="mb-1 flex justify-between text-sm">
    <span class="font-medium">Loading...</span>
    <span class="text-gray-500">60%</span>
  </div>
  <div class="relative h-2 w-full overflow-hidden rounded-full bg-gray-200">
    <div class="h-full bg-gray-900 transition-all" [style.width]="'60%'"></div>
  </div>
</div>
<Progress value={60} className="w-full max-w-sm" />
<Progress :value="60" class="w-full max-w-sm" />

Accordion

A vertically stacked set of interactive headings.

Yes, it adapts flawlessly to mobile screens.
<div class="w-full max-w-lg border-b border-t border-gray-200">
  <div class="px-4 py-2 hover:bg-gray-50 transition-colors">
    <button (click)="isOpen = !isOpen" class="flex w-full items-center justify-between font-medium py-3 text-left">
      Is the layout responsive?
      <svg [class.rotate-180]="isOpen" class="h-4 w-4 shrink-0 transition-transform">...</svg>
    </button>
    <div *ngIf="isOpen" class="text-sm pb-4 text-gray-600 transition-all">
      Yes, it adapts flawlessly to mobile screens.
    </div>
  </div>
</div>
<Accordion type="single" collapsible className="w-full max-w-lg">
  <AccordionItem value="item-1">
    <AccordionTrigger>Is the layout responsive?</AccordionTrigger>
    <AccordionContent>
      Yes, it adapts flawlessly to mobile screens.
    </AccordionContent>
  </AccordionItem>
</Accordion>
<Accordion type="single" collapsible class="w-full max-w-lg">
  <AccordionItem value="item-1">
    <AccordionTrigger>Is the layout responsive?</AccordionTrigger>
    <AccordionContent>
      Yes, it adapts flawlessly to mobile screens.
    </AccordionContent>
  </AccordionItem>
</Accordion>

Timeline

A linear chronological activity feed.

Deployed Successfully
<div class="relative space-y-6 before:absolute before:inset-0 before:ml-5 before:w-0.5 before:bg-gray-200 w-full max-w-sm">
  <div class="relative flex items-center">
    <div class="flex items-center justify-center w-10 h-10 rounded-full bg-gray-900 text-white border-2 border-white shrink-0 z-10 shadow-sm">
      <svg class="h-4 w-4">...</svg>
    </div>
    <div class="w-full ml-4 p-3 rounded border border-gray-200 bg-white shadow-sm">
      <div class="font-medium text-sm">Deployed Successfully</div>
      <time class="text-xs text-gray-500">10:24 AM</time>
    </div>
  </div>
</div>
<Timeline>
  <TimelineItem>
    <TimelineSeparator>
      <TimelineDot />
      <TimelineConnector />
    </TimelineSeparator>
    <TimelineContent>
      <div className="font-medium text-sm">Deployed Successfully</div>
      <time className="text-xs text-gray-500">10:24 AM</time>
    </TimelineContent>
  </TimelineItem>
</Timeline>
<Timeline>
  <TimelineItem>
    <TimelineSeparator>
      <TimelineDot />
      <TimelineConnector />
    </TimelineSeparator>
    <TimelineContent>
      <div class="font-medium text-sm">Deployed Successfully</div>
      <time class="text-xs text-gray-500">10:24 AM</time>
    </TimelineContent>
  </TimelineItem>
</Timeline>

Forms

Input and form group element definitions.

<form class="space-y-4 w-full max-w-sm p-4 border border-gray-200 rounded-md">
  <div class="space-y-2">
    <label class="text-sm font-medium">Email</label>
    <input type="email" 
           class="flex h-9 w-full rounded-md border border-gray-200 bg-transparent px-3 py-1 text-sm shadow-sm focus:outline-none focus:ring-1 focus:ring-blue-600" 
           placeholder="user@example.com" />
  </div>
  <button type="submit" class="inline-flex items-center justify-center rounded-md text-sm font-medium h-9 px-4 bg-gray-900 text-white shadow">
    Save Profile
  </button>
</form>
<form className="space-y-4 w-full max-w-sm p-4 border rounded-md">
  <div className="space-y-2">
    <Label htmlFor="email">Email</Label>
    <Input type="email" id="email" placeholder="user@example.com" />
  </div>
  <Button type="submit">Save Profile</Button>
</form>
<form class="space-y-4 w-full max-w-sm p-4 border rounded-md">
  <div class="space-y-2">
    <Label for="email">Email</Label>
    <Input type="email" id="email" placeholder="user@example.com" />
  </div>
  <Button type="submit">Save Profile</Button>
</form>

Dashboard Layout

The primary AppShellComponent creates a scaffold housing the sticky top-navigation header, collapsible left sidebar, and dynamically rendered Router Outlet nested content area.

Sticky Header
Sidebar Nav
<router-outlet> Main Module

Auth Layout

For Login, Registration, and Forgotten Password pages, Exo UI provides an isolated AuthLayoutComponent wrapper. It purposefully ships completely stripped of the sidebar and internal navigations.