skills/archive/ui-developer/SKILL.md
Frontend UI development for Beam Studio V2 with Tailwind and design tokens. Load when user needs Beam Studio UI, components, or Tailwind layout work.
npx skillsauth add beam-ai-team/beam-next-skills ui-developerInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Archived skill. Kept for reference only; use current Prism UI rules and app-specific frontend guidance instead.
You are a frontend developer specialized in UI development for Beam Studio V2. Follow these rules strictly.
Before editing UI files, adding components, or changing Tailwind/design-token usage, show the route/component, target files, expected visual change, and verification plan. Require explicit user approval in the current turn before writing files. Reviewing code, proposing UI changes, and explaining component patterns do not require approval.
style={{}})cn() utility from @/lib/utils for conditional class merging// ✅ Correct
<div className={cn('flex items-center gap-2', isActive && 'bg-primary')}>
// ❌ Wrong
<div style={{ display: 'flex', alignItems: 'center' }}>
Use ONLY colors defined in tailwind.config.ts. Never use arbitrary colors.
Allowed Semantic Colors:
primary, primary-foreground
secondary, secondary-foreground
destructive, destructive-foreground
success, success-foreground
warning, warning-foreground
muted, muted-foreground
accent, accent-foreground
background, foreground
border, input, ring
Allowed Color Variants:
primary-100, primary-90, primary-80, primary-50, primary-20, primary-10
muted-100, muted-90, muted-80, muted-50, muted-40, muted-20, muted-10
// ✅ Correct
<div className="bg-primary text-primary-foreground">
<span className="text-muted-foreground">
<div className="border-border bg-muted-40">
// ❌ Wrong
<div className="bg-blue-500">
<span className="text-[#333333]">
<div style={{ backgroundColor: 'red' }}>
src/components/
├── ui/ # Base shadcn components - NEVER MODIFY
├── wrapper/ # Enhanced components with business logic
├── shared/ # Reusable cross-feature components
└── sections/ # Feature-specific components
UI Layer (/components/ui/)
Wrapper Layer (/components/wrapper/)
ComponentName.tsx, ComponentName.stories.tsx, index.tsShared Layer (/components/shared/)
Sections Layer (/components/sections/)
Need basic UI element?
├── Check /components/ui/ first
├── Found? → DO NOT modify. Create wrapper in /wrapper/
└── Not found? → Check shadcn docs, add via CLI
Need enhanced functionality?
├── Add loading/form/icons → Create in /wrapper/
└── Feature-specific logic → Create in /sections/
Reusable across features?
└── Place in /shared/
ComponentName/
├── ComponentName.tsx # Main implementation
├── ComponentName.stories.tsx # Storybook stories
└── index.ts # Default export
import React, { forwardRef } from 'react';
import { cn } from '@/lib/utils';
interface ComponentNameProps {
children: React.ReactNode;
className?: string;
// ... other props
}
/**
* Brief description of what this component does.
* @param children - Child elements to render
* @param className - Additional CSS classes
*/
const ComponentName = forwardRef<HTMLDivElement, ComponentNameProps>(
({ children, className, ...props }, ref) => {
return (
<div ref={ref} className={cn('base-classes', className)} {...props}>
{children}
</div>
);
}
);
ComponentName.displayName = 'ComponentName';
export default ComponentName;
import React, { forwardRef } from 'react';
import { BaseComponent } from '@/components/ui/base-component';
import { cn } from '@/lib/utils';
interface WrapperProps extends BaseComponentProps {
isLoading?: boolean;
LeadingIcon?: React.ReactNode;
TrailingIcon?: React.ReactNode;
}
/**
* Enhanced BaseComponent with loading state and icon support.
* @param isLoading - Shows loading animation when true
* @param LeadingIcon - Icon displayed before content
* @param TrailingIcon - Icon displayed after content
*/
const ComponentWrapper = forwardRef<HTMLElement, WrapperProps>(
({ isLoading = false, LeadingIcon, TrailingIcon, className, ...props }, ref) => {
if (isLoading) {
return <LoadingState />;
}
return (
<div className="relative">
{LeadingIcon && <span className="absolute left-3">{LeadingIcon}</span>}
<BaseComponent
ref={ref}
className={cn(LeadingIcon && 'pl-8', TrailingIcon && 'pr-8', className)}
{...props}
/>
{TrailingIcon && <span className="absolute right-3">{TrailingIcon}</span>}
</div>
);
}
);
export default ComponentWrapper;
Each component should do ONE thing well.
// ✅ Correct - separate concerns
const UserAvatar = ({ src, name }) => (
<Avatar src={src} alt={name} />
);
const UserName = ({ name }) => (
<span className="font-medium">{name}</span>
);
const UserCard = ({ user }) => (
<div className="flex items-center gap-2">
<UserAvatar src={user.avatar} name={user.name} />
<UserName name={user.name} />
</div>
);
// ❌ Wrong - doing too much
const UserCard = ({ user, onEdit, onDelete, showActions, isLoading, error }) => {
// 200+ lines of mixed concerns
};
Before creating a component:
/components/shared/ for existing components/components/wrapper/ for base wrappers// ✅ Reuse existing
import { Card } from '@/components/shared/Card';
import { EmptyState } from '@/components/shared/EmptyState';
// ❌ Don't recreate
const MyCustomCard = () => { /* same as Card */ };
tailwind.config.ts unless explicitly asked/components/ui/ files// ✅ Correct - use existing variant
<Button variant="destructive" size="sm">Delete</Button>
// ❌ Wrong - creating new component for styling
const RedSmallButton = ({ children }) => (
<button className="bg-red-500 text-sm">{children}</button>
);
Maximum 500 lines per component file.
If exceeding, break into smaller components:
// ✅ Correct - split into parts
// AgentDashboard/AgentHeader.tsx
// AgentDashboard/AgentMetrics.tsx
// AgentDashboard/AgentActions.tsx
// AgentDashboard/AgentDashboard.tsx (composes above)
// ❌ Wrong - 800 line monolith
// AgentDashboard.tsx with everything inline
Components used in 2+ places go in /components/shared/.
src/components/shared/
├── Card/
├── EmptyState/
├── Dot/
├── LogoImage/
├── StatusIcon/
└── [YourComponent]/
Every component/page needs these states:
interface ComponentProps {
data: Data[];
isLoading: boolean;
error: Error | null;
}
const MyComponent = ({ data, isLoading, error }: ComponentProps) => {
// Loading State
if (isLoading) {
return <Skeleton className="h-32 w-full" />;
}
// Error State
if (error) {
return (
<div className="text-destructive">
<AlertCircle className="h-4 w-4" />
<span>{error.message}</span>
</div>
);
}
// Empty State
if (data.length === 0) {
return (
<EmptyState
text="No items found"
content="Create your first item to get started"
/>
);
}
// Success State with hover states in JSX
return (
<div className="group hover:bg-muted-40 transition-colors">
{/* Content */}
</div>
);
};
Prioritize readable JSX over clever logic.
// ✅ Correct - clean JSX
const getStatusColor = (status: string): string => {
const colors: Record<string, string> = {
active: 'text-success',
pending: 'text-warning',
failed: 'text-destructive'
};
return colors[status] ?? 'text-muted-foreground';
};
return (
<span className={getStatusColor(status)}>
{status}
</span>
);
// ❌ Wrong - inline logic mess
return (
<span className={status === 'active' ? 'text-success' : status === 'pending' ? 'text-warning' : status === 'failed' ? 'text-destructive' : 'text-muted-foreground'}>
{status}
</span>
);
Always mobile-first, always responsive.
xs: 375px
sm: 375px-768px
md: 768px-1024px
lg: 1024px-1280px
xl: 1280px-1400px
2xl: 1400px+
// Mobile-first responsive
<div className="
flex flex-col sm:flex-row
gap-2 sm:gap-4 md:gap-6
p-4 sm:p-6 md:p-8
w-full sm:w-1/2 md:w-1/3 lg:w-1/4
">
// Use hook for JS logic
const { xs, sm, md } = useScreenSize();
return xs ? <MobileView /> : <DesktopView />;
// ✅ Correct - vertical spacing
<div className="space-y-6">
<Section1 />
<Section2 />
</div>
// ❌ Avoid - border separation
<div className="border-b pb-4 mb-4">
<Section1 />
</div>
// ✅ Correct - constrained width
<div className="mx-auto max-w-4xl px-4">
<Content />
</div>
// ❌ Avoid - full width on desktop
<div className="w-full">
<Content />
</div>
// ✅ Correct - prevent overflow
<p className="max-w-prose truncate">Long text...</p>
<h2 className="line-clamp-2">Multi-line title that may be very long...</h2>
// ✅ Correct - subtle shadow
<div className="rounded-lg bg-background shadow-sm">
// ❌ Avoid - hard outline
<div className="rounded-lg border-2 border-border">
NEVER write API integrations. Use hardcoded JSON data.
// src/mocks/agents.json
{
"agents": [
{ "id": "1", "name": "Agent 1", "status": "active" },
{ "id": "2", "name": "Agent 2", "status": "pending" }
]
}
// Component
import mockAgents from '@/mocks/agents.json';
const AgentList = () => {
const agents = mockAgents.agents;
return <List items={agents} />;
};
Extract logic into named functions.
// ✅ Correct
const handleCardClick = (id: string) => {
setSelectedId(id);
trackAnalytics('card_click', { id });
};
const formatDate = (date: string): string => {
return new Date(date).toLocaleDateString();
};
return (
<Card onClick={() => handleCardClick(item.id)}>
<span>{formatDate(item.createdAt)}</span>
</Card>
);
// ❌ Wrong
return (
<Card onClick={() => {
setSelectedId(item.id);
trackAnalytics('card_click', { id: item.id });
}}>
<span>{new Date(item.createdAt).toLocaleDateString()}</span>
</Card>
);
Create constants for repeated values.
// src/lib/constants.ts
export const STATUS = {
ACTIVE: 'active',
PENDING: 'pending',
FAILED: 'failed'
} as const;
export const STATUS_COLORS: Record<string, string> = {
[STATUS.ACTIVE]: 'text-success',
[STATUS.PENDING]: 'text-warning',
[STATUS.FAILED]: 'text-destructive'
};
export enum ViewMode {
GRID = 'grid',
LIST = 'list'
}
// Usage
import { STATUS, STATUS_COLORS, ViewMode } from '@/lib/constants';
NEVER install new packages unless explicitly requested.
Use existing:
lucide-react for icons@radix-ui/* for primitivesclass-variance-authority for variantsclsx + tailwind-merge via cn()// ✅ Use existing
import { CheckIcon, XIcon } from 'lucide-react';
// ❌ Don't add
import { FaCheck } from 'react-icons/fa'; // NO!
Add JSDoc to all components and functions.
/**
* Displays a status badge with appropriate color coding.
* @param status - Current status value ('active' | 'pending' | 'failed')
* @param showIcon - Whether to display status icon (default: true)
* @returns Styled badge element
* @example
* <StatusBadge status="active" />
* <StatusBadge status="failed" showIcon={false} />
*/
const StatusBadge = ({ status, showIcon = true }: StatusBadgeProps) => {
// implementation
};
/**
* Formats a timestamp into human-readable relative time.
* @param timestamp - ISO date string
* @returns Formatted string like "2 hours ago"
*/
const formatRelativeTime = (timestamp: string): string => {
// implementation
};
<Button isLoading={isSubmitting}>Submit</Button>
<Input
name="email"
label="Email Address"
LeadingIcon={<MailIcon className="h-4 w-4" />}
/>
<Card id={item.id} onSelect={handleSelect}>
{({ isSelected }) => (
<>
<Card.Header>
<Card.IconGroup icons={item.icons} />
<Card.MoreButton options={menuOptions} />
</Card.Header>
<Card.TextContent title={item.name} description={item.description} />
<Card.Footer>
<Card.BadgeGroup badges={item.tags} />
</Card.Footer>
</>
)}
</Card>
<EmptyState
text="No results found"
content={<Button onClick={handleCreate}>Create New</Button>}
/>
<Modal
open={isOpen}
onOpenChange={setIsOpen}
title="Confirm Action"
content={<ModalContent />}
footer={<ModalActions />}
size="md"
/>
tools
Build a Palantir-shape, PDF-native use-case proposal document for a sophisticated enterprise account: research-grounded use cases (each with description, challenge, impact, value), an operating-graph ontology page, a recommended PoC with a week-by-week plan, and a closing page that asks for one decision. Load when a client asks us to 'propose high-impact use cases', requests a use-case presentation/catalog for a function (finance, HR, ops), or when a technical evaluation team will review candidates to pick a PoC. NOT for single-account cold outreach (use prospect-brief), full process diagnostics (use operating-diagnostic), or priced proposals (use proposal-creation).
development
Convert Beam Figma slide designs into high-fidelity, editable HTML presentation decks. Use when Codex is asked to audit Figma slides, extract slide templates, rebuild Beam slides as HTML decks, decide whether Figma imagery should be exported or rebuilt in HTML/CSS, create Beam/Prism-compatible deck templates, or improve fidelity of existing Beam HTML slide rebuilds.
development
Use the Beam AI reusable slide library: individual HTML slide templates extracted from Beam Figma rebuilds, kept separate from deck themes and full deck templates. Load when the user asks for a slide library, specific Beam slide patterns, reusable Figma-inspired slides, Prism slide-library items, or slide-level HTML templates.
development
Use Beam AI deck and report design packs, HTML templates, and curated examples to create sales decks, customer intro decks, RPO decks, and DIN A4 use-case proposal reports. Load when the user asks for Beam-branded presentation templates, Prism-compatible deck templates, Beam report templates, customer intro decks, commercial proposals, or reusable HTML deck/report examples.