Awesome-omni-skill frontend-dev

Frontend development skill for React, Next.js, Tailwind CSS, and TypeScript. Use when implementing web frontend features.

install
source · Clone the upstream repo
git clone https://github.com/diegosouzapw/awesome-omni-skill
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/diegosouzapw/awesome-omni-skill "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/development/frontend-dev" ~/.claude/skills/diegosouzapw-awesome-omni-skill-frontend-dev && rm -rf "$T"
manifest: skills/development/frontend-dev/SKILL.md
source content

Frontend Development Skill

Platform-specific knowledge for web frontend development.

Tech Stack

ComponentTechnology
FrameworkNext.js 14+ (App Router)
LanguageTypeScript (strict mode)
StylingTailwind CSS
StateReact hooks, Zustand (if needed)
TestingVitest, Playwright
DeploymentCloudflare Pages

Coding Standards

Naming

  • Components:
    PascalCase
  • Files:
    kebab-case.tsx
    or
    PascalCase.tsx
    (follow project)
  • Functions/Variables:
    camelCase
  • Constants:
    SCREAMING_SNAKE_CASE
  • Language: English

File Organization

// 1. External imports
import { useState } from 'react'
import { clsx } from 'clsx'

// 2. Internal imports
import { Button } from '@/components/ui/button'
import { useUser } from '@/hooks/use-user'

// 3. Types
interface Props {
  title: string
  onSubmit: () => void
}

// 4. Component
export function FeatureCard({ title, onSubmit }: Props) {
  const [isOpen, setIsOpen] = useState(false)

  return (
    // ...
  )
}

Component Patterns

// Prefer composition over props drilling
export function Card({ children }: { children: React.ReactNode }) {
  return <div className="rounded-lg border p-4">{children}</div>
}

Card.Header = function CardHeader({ children }: { children: React.ReactNode }) {
  return <div className="font-bold">{children}</div>
}

Card.Body = function CardBody({ children }: { children: React.ReactNode }) {
  return <div className="mt-2">{children}</div>
}

Tailwind Patterns

// Use clsx for conditional classes
import { clsx } from 'clsx'

<button
  className={clsx(
    'rounded px-4 py-2',
    isActive ? 'bg-blue-500 text-white' : 'bg-gray-200'
  )}
/>

// Extract common patterns
const buttonVariants = {
  primary: 'bg-blue-500 text-white hover:bg-blue-600',
  secondary: 'bg-gray-200 text-gray-800 hover:bg-gray-300',
}

Build Commands

# Install dependencies
pnpm install  # or npm install

# Development
pnpm dev

# Type check
pnpm typecheck  # or tsc --noEmit

# Lint
pnpm lint

# Build
pnpm build

# Test
pnpm test

Next.js App Router

Route Structure

app/
├── layout.tsx        # Root layout
├── page.tsx          # Home page
├── (marketing)/      # Route group (no URL segment)
│   ├── about/
│   │   └── page.tsx
│   └── pricing/
│       └── page.tsx
├── dashboard/
│   ├── layout.tsx    # Dashboard layout
│   └── page.tsx
└── api/
    └── route.ts      # API route

Server vs Client Components

// Server Component (default) - no 'use client'
async function ServerComponent() {
  const data = await fetch('...')  // Can fetch directly
  return <div>{data}</div>
}

// Client Component - needs interactivity
'use client'

import { useState } from 'react'

function ClientComponent() {
  const [count, setCount] = useState(0)
  return <button onClick={() => setCount(c => c + 1)}>{count}</button>
}

Data Fetching

// Server Component
async function Page() {
  const data = await fetch('https://api.example.com/data', {
    next: { revalidate: 60 }  // ISR: revalidate every 60s
  })
  return <div>{data}</div>
}

// Client Component with SWR
'use client'
import useSWR from 'swr'

function ClientPage() {
  const { data, error } = useSWR('/api/data', fetcher)
  if (error) return <div>Error</div>
  if (!data) return <div>Loading...</div>
  return <div>{data}</div>
}

Testing

Vitest Unit Test

import { describe, it, expect } from 'vitest'
import { render, screen } from '@testing-library/react'
import { Button } from './button'

describe('Button', () => {
  it('renders children', () => {
    render(<Button>Click me</Button>)
    expect(screen.getByText('Click me')).toBeInTheDocument()
  })
})

Playwright E2E

import { test, expect } from '@playwright/test'

test('home page', async ({ page }) => {
  await page.goto('/')
  await expect(page.getByRole('heading', { name: 'Welcome' })).toBeVisible()
})

Common Issues

Hydration Mismatch

  • Use
    suppressHydrationWarning
    for dynamic content
  • Check for client-only code in Server Components

"Module not found"

  • Check tsconfig paths
  • Verify import aliases in next.config.js

Tailwind not applying

  • Check
    content
    paths in tailwind.config.js
  • Restart dev server after config changes