Frontend Agent Skill
⚠️ PRE-GENERATION CHECKLIST (MANDATORY)
Before creating ANY UI component, complete this checklist:
PRE_GENERATION_CHECKLIST:
1. READ_DESIGN_SYSTEM:
- [ ] Read /DESIGN-SYSTEM.md for forbidden patterns
- [ ] Check /src/app/globals.css @theme block for tokens
- [ ] Note: accent-500 = #ff6b35 (orange)
2. CHECK_EXISTING_COMPONENTS:
- [ ] Look in /src/components/ui/ first (48 components)
- [ ] Check components.json for shadcn configuration
- [ ] Review existing patterns in landing page
3. REFERENCE_UI_LIBRARIES:
- [ ] See /docs/UI-LIBRARY-INDEX.md for premium components
- [ ] Priority: Project → StyleUI/KokonutUI/Cult UI → shadcn base
- [ ] NEVER use shadcn defaults without customization
4. VERIFY_NO_FORBIDDEN_PATTERNS:
- [ ] No bg-white, text-gray-600, or generic hover states
- [ ] No uniform grid-cols-3 gap-4 layouts
- [ ] No unstyled <Card className="p-6">
- [ ] No icons without brand colors
FORBIDDEN CODE PATTERNS:
// ❌ NEVER GENERATE THESE
className="bg-white rounded-lg shadow p-4" // Generic card
className="grid grid-cols-3 gap-4" // Uniform grid
className="text-gray-600" // Default muted
className="hover:bg-gray-100" // Generic hover
<Card className="p-6"> // Unstyled shadcn
REQUIRED PATTERNS:
// ✅ ALWAYS USE DESIGN TOKENS
className="bg-bg-card border border-border-base hover:border-accent-500"
className="text-text-primary"
className="text-text-secondary"
className="bg-accent-500 hover:bg-accent-400"
Overview
The Frontend Agent is responsible for all UI/UX work in the Unite-Hub Next.js application:
- React 19 / Next.js 16 development with App Router
- shadcn/ui component implementation and customization
- Tailwind CSS styling and responsive design
- Route creation and breadcrumb setup
- Client-side state management (React Context, hooks)
- Accessibility and performance optimization
How to Use This Agent
Trigger
User says: "Fix dashboard layout", "Add new contact page", "Update navigation", "Create modal component"
What the Agent Does
1. Understand the Request
Questions to Ask:
- Which page/component needs work?
- What's the desired behavior?
- Are there design references (screenshots, wireframes)?
- What's the priority (P0/P1/P2)?
2. Analyze Current Implementation
Step A: Locate Files
# Find the component or page
find src/app -name "*.tsx" | grep -i "contacts"
find src/components -name "*.tsx" | grep -i "hotleads"
Step B: Read Current Code
// Use text_editor tool
text_editor.view("src/app/dashboard/contacts/page.tsx")
Step C: Identify Dependencies
- What shadcn/ui components are used?
- What contexts are consumed (AuthContext, etc.)?
- What API routes are called?
- What database queries are made?
3. Implement Changes
Step A: Component Updates
For existing components:
// src/components/HotLeadsPanel.tsx
import { Button } from "@/components/ui/button";
import { Card } from "@/components/ui/card";
import { useAuth } from "@/contexts/AuthContext";
export function HotLeadsPanel({ workspaceId }: { workspaceId: string }) {
const { currentOrganization } = useAuth();
// Fetch hot leads
const [leads, setLeads] = useState([]);
useEffect(() => {
async function fetchLeads() {
const res = await fetch("/api/agents/contact-intelligence", {
method: "POST",
body: JSON.stringify({ action: "get_hot_leads", workspaceId }),
});
const data = await res.json();
setLeads(data.leads || []);
}
if (workspaceId) fetchLeads();
}, [workspaceId]);
return (
<Card>
{/* UI implementation */}
</Card>
);
}
Step B: Route Creation
For new pages:
// src/app/dashboard/new-page/page.tsx
import { Metadata } from "next";
export const metadata: Metadata = {
title: "New Page | Unite Hub",
description: "Description of new page"
};
export default async function NewPage() {
return (
<div className="container mx-auto py-8">
<h1 className="text-3xl font-bold">New Page</h1>
{/* Content */}
</div>
);
}
Step C: shadcn/ui Components
Install new components if needed:
npx shadcn@latest add dialog
npx shadcn@latest add dropdown-menu
npx shadcn@latest add toast
Use components following shadcn patterns:
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
<Dialog>
<DialogTrigger asChild>
<Button>Open Dialog</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>Title</DialogTitle>
<DialogDescription>Description</DialogDescription>
</DialogHeader>
{/* Content */}
</DialogContent>
</Dialog>
4. Add Workspace Filtering (CRITICAL for V1)
All database queries MUST filter by workspace:
// ❌ BAD - Shows data from all workspaces
const { data: contacts } = await supabase
.from("contacts")
.select("*");
// ✅ GOOD - Only shows data from user's workspace
const { data: contacts } = await supabase
.from("contacts")
.select("*")
.eq("workspace_id", workspaceId);
Required for these tables:
contacts-.eq("workspace_id", workspaceId)campaigns-.eq("workspace_id", workspaceId)drip_campaigns-.eq("workspace_id", workspaceId)emails-.eq("workspace_id", workspaceId)generatedContent-.eq("workspace_id", workspaceId)
5. Handle Loading and Error States
Loading State:
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
async function fetchData() {
try {
setIsLoading(true);
const data = await fetch("...");
setData(data);
} catch (err) {
setError(err.message);
} finally {
setIsLoading(false);
}
}
fetchData();
}, []);
if (isLoading) return <Spinner />;
if (error) return <ErrorBanner message={error} />;
return <DataDisplay data={data} />;
6. Responsive Design
Tailwind Breakpoints:
<div className="
grid grid-cols-1 // Mobile: 1 column
md:grid-cols-2 // Tablet: 2 columns
lg:grid-cols-3 // Desktop: 3 columns
gap-4
">
{/* Cards */}
</div>
Mobile-First Approach:
- Start with mobile layout (default classes)
- Add
md:classes for tablet - Add
lg:andxl:for desktop
7. Test Changes
Step A: Visual Testing
# Start dev server
npm run dev
# Navigate to page in browser
# Test on mobile viewport (DevTools)
# Test dark theme
Step B: Accessibility
// Check for:
// - Proper ARIA labels
// - Keyboard navigation
// - Focus states
// - Screen reader support
<button aria-label="Close dialog">×</button>
<input aria-describedby="email-help" />
<div role="alert" aria-live="polite">{error}</div>
Step C: Performance
// Use React.memo for expensive components
import { memo } from "react";
export const ExpensiveComponent = memo(function ExpensiveComponent({ data }) {
return <div>{/* Render */}</div>;
});
// Use dynamic imports for heavy components
import dynamic from "next/dynamic";
const HeavyChart = dynamic(() => import("@/components/HeavyChart"), {
loading: () => <Spinner />,
ssr: false
});
Common Tasks
Task 1: Fix Missing Workspace Filter
Example: Dashboard Overview page showing all contacts
Steps:
- Read
src/app/dashboard/overview/page.tsx - Find all Supabase queries
- Add
.eq("workspace_id", workspaceId)to each - Add null check for workspaceId before querying
- Test with multiple workspaces
Code:
// Before
const { data: contacts } = a