plugins/web-and-frontend/skills/frontend-developer/SKILL.md
Build React components with hooks, responsive layouts, and accessible UI. Use when creating React components, implementing state management, styling with Tailwind or CSS-in-JS, or ensuring accessibility compliance.
npx skillsauth add arosenkranz/claude-code-config frontend-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.
Guidelines for modern React development with accessibility and performance.
interface ButtonProps {
variant?: 'primary' | 'secondary' | 'danger';
size?: 'sm' | 'md' | 'lg';
isLoading?: boolean;
disabled?: boolean;
onClick?: () => void;
children: React.ReactNode;
}
export function Button({
variant = 'primary',
size = 'md',
isLoading = false,
disabled = false,
onClick,
children,
}: ButtonProps) {
return (
<button
className={cn(
'rounded-md font-medium transition-colors',
variants[variant],
sizes[size],
(disabled || isLoading) && 'opacity-50 cursor-not-allowed'
)}
disabled={disabled || isLoading}
onClick={onClick}
aria-busy={isLoading}
>
{isLoading ? <Spinner /> : children}
</button>
);
}
interface TabsContextType {
activeTab: string;
setActiveTab: (id: string) => void;
}
const TabsContext = createContext<TabsContextType | null>(null);
function Tabs({ children, defaultValue }: TabsProps) {
const [activeTab, setActiveTab] = useState(defaultValue);
return (
<TabsContext.Provider value={{ activeTab, setActiveTab }}>
<div role="tablist">{children}</div>
</TabsContext.Provider>
);
}
function TabTrigger({ value, children }: TabTriggerProps) {
const ctx = useContext(TabsContext);
if (!ctx) throw new Error('TabTrigger must be inside Tabs');
return (
<button
role="tab"
aria-selected={ctx.activeTab === value}
onClick={() => ctx.setActiveTab(value)}
>
{children}
</button>
);
}
Tabs.Trigger = TabTrigger;
Tabs.Content = TabContent;
function useFetch<T>(url: string) {
const [state, setState] = useState<{
data: T | null;
isLoading: boolean;
error: Error | null;
}>({
data: null,
isLoading: true,
error: null,
});
useEffect(() => {
const controller = new AbortController();
async function fetchData() {
try {
const res = await fetch(url, { signal: controller.signal });
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const data = await res.json();
setState({ data, isLoading: false, error: null });
} catch (err) {
if (err instanceof Error && err.name !== 'AbortError') {
setState({ data: null, isLoading: false, error: err });
}
}
}
fetchData();
return () => controller.abort();
}, [url]);
return state;
}
function useDebounce<T>(value: T, delay: number): T {
const [debouncedValue, setDebouncedValue] = useState(value);
useEffect(() => {
const timer = setTimeout(() => setDebouncedValue(value), delay);
return () => clearTimeout(timer);
}, [value, delay]);
return debouncedValue;
}
function useLocalStorage<T>(key: string, initialValue: T) {
const [storedValue, setStoredValue] = useState<T>(() => {
try {
const item = window.localStorage.getItem(key);
return item ? JSON.parse(item) : initialValue;
} catch {
return initialValue;
}
});
const setValue = (value: T | ((val: T) => T)) => {
const valueToStore = value instanceof Function ? value(storedValue) : value;
setStoredValue(valueToStore);
window.localStorage.setItem(key, JSON.stringify(valueToStore));
};
return [storedValue, setValue] as const;
}
<button> for actions, <a> for navigationh1 -> h2 -> h3)<nav>, <main>, <aside>, <footer> landmarks// Announce dynamic content
<div aria-live="polite" aria-atomic="true">
{statusMessage}
</div>
// Label interactive elements
<button aria-label="Close dialog" aria-describedby="dialog-desc">
<XIcon />
</button>
// Indicate states
<button aria-pressed={isActive} aria-expanded={isOpen}>
Menu
</button>
function handleKeyDown(e: KeyboardEvent) {
switch (e.key) {
case 'ArrowDown':
focusNext();
break;
case 'ArrowUp':
focusPrev();
break;
case 'Escape':
close();
break;
case 'Enter':
case ' ':
select();
break;
}
}
// Focus trap in modals
useEffect(() => {
if (isOpen) {
const previousFocus = document.activeElement as HTMLElement;
firstFocusableRef.current?.focus();
return () => previousFocus?.focus();
}
}, [isOpen]);
<div className="
grid
grid-cols-1
sm:grid-cols-2
lg:grid-cols-3
gap-4
p-4
sm:p-6
lg:p-8
">
{items.map(item => <Card key={item.id} {...item} />)}
</div>
import { cva, type VariantProps } from 'class-variance-authority';
const buttonVariants = cva(
'inline-flex items-center justify-center rounded-md font-medium transition-colors',
{
variants: {
variant: {
primary: 'bg-blue-600 text-white hover:bg-blue-700',
secondary: 'bg-gray-100 text-gray-900 hover:bg-gray-200',
ghost: 'hover:bg-gray-100',
},
size: {
sm: 'h-8 px-3 text-sm',
md: 'h-10 px-4',
lg: 'h-12 px-6 text-lg',
},
},
defaultVariants: {
variant: 'primary',
size: 'md',
},
}
);
type ButtonProps = VariantProps<typeof buttonVariants> & {
children: React.ReactNode;
};
const HeavyComponent = lazy(() => import('./HeavyComponent'));
function App() {
return (
<Suspense fallback={<Skeleton />}>
<HeavyComponent />
</Suspense>
);
}
// Memoize expensive calculations
const sortedItems = useMemo(
() => items.sort((a, b) => a.name.localeCompare(b.name)),
[items]
);
// Memoize callbacks passed to children
const handleClick = useCallback(() => {
doSomething(id);
}, [id]);
// Memoize components that receive stable props
const MemoizedChild = memo(function Child({ data }: Props) {
return <div>{data.name}</div>;
});
<img
src={imageSrc}
alt={imageAlt}
loading="lazy"
decoding="async"
width={400}
height={300}
/>
type Action =
| { type: 'ADD_ITEM'; payload: Item }
| { type: 'REMOVE_ITEM'; payload: string }
| { type: 'CLEAR' };
function cartReducer(state: CartState, action: Action): CartState {
switch (action.type) {
case 'ADD_ITEM':
return { ...state, items: [...state.items, action.payload] };
case 'REMOVE_ITEM':
return { ...state, items: state.items.filter(i => i.id !== action.payload) };
case 'CLEAR':
return { ...state, items: [] };
}
}
const CartContext = createContext<{
state: CartState;
dispatch: Dispatch<Action>;
} | null>(null);
tools
Lightweight orchestrator for spec-before-plan workflow. Use when starting a feature with ambiguous requirements. Walks SPEC.md → PLAN.md → execute, delegating to /superpowers:writing-plans and /superpowers:executing-plans. Invoke when asked to "spec this out", "spec-first", "spec and plan for X", or when feature requirements are vague.
tools
Problem Statement Co-Authoring Skill
development
Structure and maintain professional brag documents with clear templates for accomplishments, projects, and growth tracking. Use when documenting achievements, creating brag document entries, formatting accomplishments, or tracking career progress.
development
Analyze technical documentation for clarity, conciseness, and effectiveness using Google Technical Writing principles. Use when reviewing documentation, checking writing quality, improving docs, or providing writing feedback.