# Frontend Subagent Guide

This guide covers frontend-focused subagents:

- **frontend-specialist**: UI components, Neo-Brutalism, Core Web Vitals, accessibility
- **uiux-designer**: UI/UX decisions, component design, user experience

## Quick Reference

| Aspect            | frontend-specialist                            | uiux-designer                          |
| ----------------- | ---------------------------------------------- | -------------------------------------- |
| **Primary Use**   | React components, performance, accessibility   | Design decisions, user flows           |
| **Key Files**     | `src/components/`, `src/features/`             | Design specs, mockups                  |
| **Key ADRs**      | ADR-012 (Design System), ADR-044 (Feature Org) | ADR-012 (Design System)                |
| **Design System** | Neo-Brutalism (bold borders, high contrast)    | Same                                   |
| **State Mgmt**    | TanStack Query (server), Zustand (client)      | N/A                                    |
| **Delegate To**   | `coder` (backend), `tester` (test coverage)    | `frontend-specialist` (implementation) |

## The frontend-specialist Subagent

### When to Use

Use the **frontend-specialist** subagent when you need to:

- Build new React components
- Fix CSS/styling issues
- Improve Core Web Vitals performance
- Implement accessibility features
- Debug React rendering issues
- Optimize bundle size

### What frontend-specialist Knows

The frontend-specialist subagent understands:

- React 18+ patterns and hooks
- TanStack Query for server state
- Zustand for client state
- Tailwind CSS with custom design tokens
- Neo-Brutalism design system
- Accessibility standards (WCAG)
- Performance optimization

## Design System: Neo-Brutalism

The project uses a Neo-Brutalism design aesthetic characterized by:

- Bold, black borders
- High contrast colors
- Shadow offsets for depth
- Raw, honest UI elements
- Playful but functional

### Design Tokens

Located in `src/styles/` and documented in `docs/DESIGN_TOKENS.md`:

```css
/* Core colors */
--color-primary: #ff6b35;
--color-secondary: #004e89;
--color-accent: #f7c548;
--color-background: #fffdf7;
--color-text: #1a1a1a;

/* Borders */
--border-width: 3px;
--border-color: #1a1a1a;

/* Shadows (offset style) */
--shadow-sm: 2px 2px 0 0 #1a1a1a;
--shadow-md: 4px 4px 0 0 #1a1a1a;
--shadow-lg: 6px 6px 0 0 #1a1a1a;
```

### Component Patterns

**Brutal Card:**

```tsx
<div className="border-3 border-black bg-white p-4 shadow-[4px_4px_0_0_#1A1A1A] hover:shadow-[6px_6px_0_0_#1A1A1A] hover:translate-x-[-2px] hover:translate-y-[-2px] transition-all">
  {children}
</div>
```

**Brutal Button:**

```tsx
<button className="border-3 border-black bg-primary px-4 py-2 font-bold shadow-[4px_4px_0_0_#1A1A1A] hover:shadow-[2px_2px_0_0_#1A1A1A] hover:translate-x-[2px] hover:translate-y-[2px] active:shadow-none active:translate-x-[4px] active:translate-y-[4px] transition-all">
  Click Me
</button>
```

## Example Requests

### Building New Components

```
"Use frontend-specialist to create a PriceTag component that
displays the current price and original price (if discounted)
in the Neo-Brutalism style with a 'SALE' badge when applicable."
```

### Performance Optimization

```
"Use frontend-specialist to optimize the deals list page.
It's showing poor Largest Contentful Paint scores and the
initial load feels sluggish."
```

### Accessibility Fix

```
"Use frontend-specialist to audit and fix accessibility issues
on the shopping list page. Screen reader users report that
the checkbox states aren't being announced correctly."
```

### Responsive Design

```
"Use frontend-specialist to make the store search component
work better on mobile. The dropdown menu is getting cut off
on smaller screens."
```

## State Management

### Server State (TanStack Query)

```tsx
// Fetching data with caching
const {
  data: deals,
  isLoading,
  error,
} = useQuery({
  queryKey: ['deals', storeId],
  queryFn: () => dealsApi.getByStore(storeId),
  staleTime: 5 * 60 * 1000, // 5 minutes
});

// Mutations with optimistic updates
const mutation = useMutation({
  mutationFn: dealsApi.favorite,
  onMutate: async (dealId) => {
    await queryClient.cancelQueries(['deals']);
    const previous = queryClient.getQueryData(['deals']);
    queryClient.setQueryData(['deals'], (old) =>
      old.map((d) => (d.id === dealId ? { ...d, isFavorite: true } : d)),
    );
    return { previous };
  },
  onError: (err, dealId, context) => {
    queryClient.setQueryData(['deals'], context.previous);
  },
});
```

### Client State (Zustand)

```tsx
// Simple client-only state
const useUIStore = create((set) => ({
  sidebarOpen: false,
  toggleSidebar: () => set((s) => ({ sidebarOpen: !s.sidebarOpen })),
}));
```

## The uiux-designer Subagent

### When to Use

Use the **uiux-designer** subagent when you need to:

- Make design decisions for new features
- Improve user flows
- Design component layouts
- Choose appropriate UI patterns
- Plan information architecture

### Example Requests

**Design new feature:**

```
"Use uiux-designer to design the user flow for adding items
to a shopping list from the deals page. Consider both desktop
and mobile experiences."
```

**Improve existing UX:**

```
"Use uiux-designer to improve the flyer upload experience.
Users are confused about which file types are supported and
don't understand the processing status."
```

**Component design:**

```
"Use uiux-designer to design a price comparison component
that shows the same item across multiple stores."
```

## Component Structure

### Feature-Based Organization

```
src/
├── components/           # Shared UI components
│   ├── ui/              # Basic UI primitives
│   │   ├── Button.tsx
│   │   ├── Card.tsx
│   │   └── Input.tsx
│   ├── layout/          # Layout components
│   │   ├── Header.tsx
│   │   └── Sidebar.tsx
│   └── shared/          # Complex shared components
│       └── PriceDisplay.tsx
├── features/            # Feature-specific components
│   ├── deals/
│   │   ├── components/
│   │   ├── hooks/
│   │   └── api/
│   └── shopping-list/
│       ├── components/
│       ├── hooks/
│       └── api/
└── pages/               # Route page components
    ├── DealsPage.tsx
    └── ShoppingListPage.tsx
```

### Component Pattern

```tsx
// src/components/PriceTag.tsx
import { cn } from '@/utils/cn';

interface PriceTagProps {
  currentPrice: number;
  originalPrice?: number;
  currency?: string;
  className?: string;
}

export function PriceTag({
  currentPrice,
  originalPrice,
  currency = '$',
  className,
}: PriceTagProps) {
  const isOnSale = originalPrice && originalPrice > currentPrice;
  const discount = isOnSale ? Math.round((1 - currentPrice / originalPrice) * 100) : 0;

  return (
    <div className={cn('flex items-baseline gap-2', className)}>
      <span className="text-2xl font-bold text-primary">
        {currency}
        {currentPrice.toFixed(2)}
      </span>
      {isOnSale && (
        <>
          <span className="text-sm text-gray-500 line-through">
            {currency}
            {originalPrice.toFixed(2)}
          </span>
          <span className="border-2 border-black bg-accent px-1 text-xs font-bold">
            -{discount}%
          </span>
        </>
      )}
    </div>
  );
}
```

## Testing React Components

### Component Test Pattern

```tsx
import { describe, it, expect, vi } from 'vitest';
import { renderWithProviders, screen } from '@/tests/utils/renderWithProviders';
import userEvent from '@testing-library/user-event';
import { PriceTag } from './PriceTag';

describe('PriceTag', () => {
  it('displays current price', () => {
    renderWithProviders(<PriceTag currentPrice={9.99} />);
    expect(screen.getByText('$9.99')).toBeInTheDocument();
  });

  it('shows discount when original price is higher', () => {
    renderWithProviders(<PriceTag currentPrice={7.99} originalPrice={9.99} />);
    expect(screen.getByText('$7.99')).toBeInTheDocument();
    expect(screen.getByText('$9.99')).toBeInTheDocument();
    expect(screen.getByText('-20%')).toBeInTheDocument();
  });
});
```

### Hook Test Pattern

```tsx
import { renderHook, waitFor } from '@testing-library/react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { useDeals } from './useDeals';

describe('useDeals', () => {
  it('fetches deals for store', async () => {
    const queryClient = new QueryClient({
      defaultOptions: { queries: { retry: false } },
    });

    const { result } = renderHook(() => useDeals('store-123'), {
      wrapper: ({ children }) => (
        <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
      ),
    });

    await waitFor(() => expect(result.current.isSuccess).toBe(true));
    expect(result.current.data).toHaveLength(10);
  });
});
```

## Accessibility Guidelines

### ARIA Patterns

```tsx
// Proper button with loading state
<button
  aria-busy={isLoading}
  aria-label={isLoading ? 'Loading...' : 'Add to cart'}
  disabled={isLoading}
>
  {isLoading ? <Spinner /> : 'Add to Cart'}
</button>

// Proper form field
<label htmlFor="email">Email Address</label>
<input
  id="email"
  type="email"
  aria-describedby="email-error"
  aria-invalid={!!errors.email}
/>
{errors.email && (
  <span id="email-error" role="alert">
    {errors.email}
  </span>
)}
```

### Keyboard Navigation

- All interactive elements must be focusable
- Focus order should be logical
- Focus traps for modals
- Skip links for main content

### Color Contrast

- Normal text: minimum 4.5:1 contrast ratio
- Large text: minimum 3:1 contrast ratio
- Use the Neo-Brutalism palette which is designed for high contrast

## Performance Optimization

### Code Splitting

```tsx
// Lazy load heavy components
const PdfViewer = lazy(() => import('./PdfViewer'));

function FlyerPage() {
  return (
    <Suspense fallback={<LoadingSpinner />}>
      <PdfViewer />
    </Suspense>
  );
}
```

### Image Optimization

```tsx
// Use appropriate sizes and formats
<img
  src={imageUrl}
  srcSet={`${imageUrl}?w=400 400w, ${imageUrl}?w=800 800w`}
  sizes="(max-width: 600px) 400px, 800px"
  loading="lazy"
  alt={itemName}
/>
```

### Memoization

```tsx
// Memoize expensive computations
const sortedDeals = useMemo(() => deals.slice().sort((a, b) => a.price - b.price), [deals]);

// Memoize callbacks passed to children
const handleSelect = useCallback((id: string) => {
  setSelectedId(id);
}, []);
```

## Related Documentation

- [OVERVIEW.md](./OVERVIEW.md) - Subagent system overview
- [CODER-GUIDE.md](./CODER-GUIDE.md) - For implementing features
- [TESTER-GUIDE.md](./TESTER-GUIDE.md) - Component testing patterns
- [../development/DESIGN_TOKENS.md](../development/DESIGN_TOKENS.md) - Design token reference
- [../adr/0012-frontend-component-library-and-design-system.md](../adr/0012-frontend-component-library-and-design-system.md) - Design system ADR
- [../adr/0005-frontend-state-management-and-server-cache-strategy.md](../adr/0005-frontend-state-management-and-server-cache-strategy.md) - State management ADR
- [../adr/0044-frontend-feature-organization.md](../adr/0044-frontend-feature-organization.md) - Feature organization
