Skip to main content

Shared Components

Moved to UI Library

The shared UI components have been moved to the @elcto/ui package. See the UI Library documentation for complete component reference.

Both the Backend Dashboard and Heimdall ID use the shared @elcto/ui component library located at shared/ui/.

Quick Reference

Importing Components

// Import from the shared library
import { Button, Modal, Alert, Dropdown } from "@elcto/ui/components";
import { cn } from "@elcto/ui/utils";

// Or from local re-exports
import { Button, Modal, Alert } from "@/components/ui";

Available Components

ComponentDescriptionDocs
AlertStatus messages with variantsView
AvatarUser avatar with fallbackView
BadgeStatus badges and labelsView
BannerFull-width notification bannersView
BlurredTextPrivacy-aware text displayView
ButtonInteractive buttonsView
CardContent containersView
CodeInputOTP/verification inputView
DropdownDropdown menusView
FloatingInputInputs with floating labelsView
LoadingSpinner, SpinnerLoading indicatorsView
ModalDialog windowsView
TableData tablesView
TooltipHover tooltipsView

Platform Icons

import {
TwitchIcon,
DiscordIcon,
YouTubeIcon,
KickIcon,
SteamIcon,
GitHubIcon,
} from "@elcto/ui/components";

<TwitchIcon className="w-5 h-5 text-[var(--twitch)]" />

See Icons documentation for all available icons.

Auth Components

ProtectedRoute

Wrap pages that require authentication.

import { ProtectedRoute } from "@/components/auth/ProtectedRoute";

export default function DashboardPage() {
return (
<ProtectedRoute>
<Dashboard />
</ProtectedRoute>
);
}

// With required permissions
<ProtectedRoute requiredPermissions={["gps:write", "users:read"]}>
<AdminPanel />
</ProtectedRoute>

// With custom fallback
<ProtectedRoute fallback={<CustomLoginPrompt />}>
<Content />
</ProtectedRoute>

Props:

PropTypeDefaultDescription
childrenReactNode-Protected content
requiredPermissionsstring[]-Required permissions
fallbackReactNode<LoginRedirect />Fallback component

ProtectedContent

Conditionally render content based on auth state.

import { ProtectedContent } from "@/components/auth/ProtectedContent";

<ProtectedContent>
{/* Only shown when authenticated */}
<UserMenu />
</ProtectedContent>

<ProtectedContent fallback={<LoginButton />}>
<LogoutButton />
</ProtectedContent>

<ProtectedContent requiredPermissions={["admin"]}>
<AdminTools />
</ProtectedContent>

Props:

PropTypeDefaultDescription
childrenReactNode-Protected content
fallbackReactNodenullFallback when not authenticated
requiredPermissionsstring[]-Required permissions

UserButton

Display user info with dropdown menu.

import { UserButton } from "@/components/auth/UserButton";

// Basic usage
<UserButton />

// With custom menu items
<UserButton
menuItems={[
{ label: "Settings", href: "/settings" },
{ label: "Help", href: "/help" },
]}
/>

Props:

PropTypeDefaultDescription
menuItemsMenuItem[]-Additional menu items
showEmailbooleantrueShow email in dropdown

Layout Components

DashboardLayout

The main layout for authenticated dashboard pages.

import { DashboardLayout } from "@/components/layouts/DashboardLayout";

export default function Page() {
return (
<DashboardLayout
title="GPS Data"
breadcrumbs={[
{ label: "Dashboard", href: "/dashboard" },
{ label: "GPS Data" }
]}
>
<GpsDataContent />
</DashboardLayout>
);
}

Props:

PropTypeDefaultDescription
childrenReactNode-Page content
titlestring-Page title
breadcrumbsBreadcrumb[]-Breadcrumb navigation

OverlayLayout

Full-screen overlay layout for modals and overlays.

import { OverlayLayout } from "@/layouts/OverlayLayout";

export default function ModalPage() {
return (
<OverlayLayout onClose={() => router.back()}>
<div className="bg-white rounded-lg p-6 max-w-md w-full">
<h2>Modal Content</h2>
{/* ... */}
</div>
</OverlayLayout>
);
}

The site footer component.

import { Footer } from "@/components/layout/Footer";

export default function Layout({ children }) {
return (
<>
<main>{children}</main>
<Footer />
</>
);
}

Providers

SessionProvider

Wrap your app with the session provider for auth state.

// src/app/layout.tsx
import { SessionProvider } from "@/components/providers/SessionProvider";

export default function RootLayout({ children }) {
return (
<html>
<body>
<SessionProvider>
{children}
</SessionProvider>
</body>
</html>
);
}

ApolloWrapper

GraphQL client provider (Backend Dashboard only).

// src/app/layout.tsx
import { ApolloWrapper } from "@/components/providers/ApolloWrapper";

export default function RootLayout({ children }) {
return (
<html>
<body>
<SessionProvider>
<ApolloWrapper>
{children}
</ApolloWrapper>
</SessionProvider>
</body>
</html>
);
}

Styling

See Design Tokens for the complete styling reference.

CSS Variables

/* Use CSS variables from @elcto/ui */
background-color: var(--bg-card);
color: var(--text-primary);
border-color: var(--border-subtle);

Utility Classes

/* Button variants */
.btn-brand /* Primary brand button */
.btn-secondary /* Secondary button */

/* Card styles */
.card /* Elevated card */
.card-flat /* Flat card */

/* Text colors */
.text-primary /* Primary text */
.text-secondary /* Secondary text */
.text-muted /* Muted text */

Best Practices

Loading States

import { LoadingSpinner, Alert } from "@/components/ui";

function DataTable() {
const { data, isLoading, error } = useQuery(...);

if (isLoading) return <LoadingSpinner />;
if (error) return <Alert variant="error">{error.message}</Alert>;

return <Table data={data} />;
}

Component Composition

import { Card, CardHeader, CardContent } from "@/components/ui";

<Card>
<CardHeader>
<h3>Title</h3>
</CardHeader>
<CardContent>
Content here
</CardContent>
</Card>

Next Steps