web-frontend
Guidelines for building minimal, professional, and concise frontend components in the /web project using TypeScript, React Router v7, Tailwind CSS, and the CVA pattern.
When & Why to Use This Skill
This Claude skill provides a comprehensive framework and standardized guidelines for developing modern, professional frontend components. It leverages a robust tech stack including React Router v7, Tailwind CSS, and CVA (Class Variance Authority) to ensure type safety, consistent design language, and efficient component composition. By defining clear patterns for layouts, interactive elements, and data fetching, it streamlines the development of scalable web applications and PWAs.
Use Cases
- Generating type-safe React components with complex variants using the CVA pattern and TypeScript.
- Implementing consistent page layouts and navigation structures using React Router v7 file-based routing.
- Applying a unified design system with Radix Colors and Tailwind CSS to ensure professional dark mode support and smooth transitions.
- Standardizing data loading patterns and SEO meta-tag management across various application routes.
- Building accessible and responsive UI modules like InfoCards, InfoPills, and interactive headers following strict design principles.
| name | web-frontend |
|---|---|
| description | Guidelines for building minimal, professional, and concise frontend components in the /web project using TypeScript, React Router v7, Tailwind CSS, and the CVA pattern. |
Web Frontend Development Skill
This skill provides guidance for developing frontend components in the /web project for CRAN/E, a PWA that searches CRAN packages and authors.
Tech Stack
- TypeScript for type safety
- React Router v7 (Vite build) with Remix-style file-based routes
- Tailwind CSS with Radix Colors for styling
- CVA (Class Variance Authority) for component variants
- Remix Icons (
@remixicon/react) for icons - Supabase for data fetching via typed client
Design Principles
Minimal & Professional
The design follows a clean, minimal aesthetic with:
- Gradient accents (iris, ruby, jade, bronze, sand, amethyst, opal)
- Subtle hover effects with slow transitions (duration-700)
- Rounded corners (rounded-full for pills, rounded-xl for cards)
- Consistent spacing using Tailwind's gap utilities
- Dark mode support via Radix Colors
Component Structure
All UI components follow these patterns:
- Props Definition: Use
PropsWithChildrenwhen accepting children, combine withVariantProps<typeof twBase>from CVA - CVA Styling: Define variants using
cva()for reusable, type-safe class composition - Display Name: Always set
Component.displayNamefor debugging - Type Safety: Leverage TypeScript for props, Zod schemas for validation
Core Component Patterns
Layout Components
Header (app/modules/header.tsx):
- Gradient backgrounds with variants
- Headline + optional subline structure
- Optional ornament slot for additional content
PageContent (app/modules/page-content.tsx):
- Full-width wrapper with consistent padding
full-widthclass for spanning viewport width- Inner flex column with gap-16 for section spacing
PageContentSection (app/modules/page-content-section.tsx):
- Headline, subline, and fragment ID for anchor navigation
- Prose variant for long-form text styling
- Gap-16 for consistent vertical rhythm
Interactive Components
InfoPill (app/modules/info-pill.tsx):
- Rounded-full border with gradient hover effects
- Optional label slot (left-aligned, gray-dim)
- Size variants: xs, sm, md, lg
- Gradient variants matching design system
- Hover opacity transition on gradient overlay
InfoCard (app/modules/info-card.tsx):
- Rounded-xl border with gradient hover effects
- Min height for consistent card sizing
- Icon indicators (external/internal) with wiggle animation on hover
- Group hover states for coordinated animations
Tag (app/modules/tag.tsx):
- Small, uppercase, rounded-full labels
- Border gradients (iris, jade) for semantic meaning
- Size variants: xs, sm
Utility Components
Separator (app/modules/separator.tsx):
- Simple
<hr>with opacity-20 for subtle dividers
Prose (app/modules/prose.tsx):
- Typography component for long-form content
- Supports dangerouslySetInnerHTML for rendered HTML
- Text-xl, font-light, leading-normal baseline
Anchors & AnchorLink (app/modules/anchors.tsx):
- Navigation for in-page sections
- Fragment-based routing
Color System
Use Radix Colors with semantic variants:
- iris: Primary/action color
- ruby: Error/destructive actions
- jade: Success/positive states
- bronze: Neutral/informational
- sand/gold: Warm highlights
- amethyst: Secondary accent (plum + iris mix)
- opal: Cool accent (iris + sky mix)
Text colors:
text-gray-normal: Body texttext-gray-dim: Secondary/muted texttext-gray-12/text-gray-4: Prose (dark/light mode)
Page Structure Pattern
Reference pages follow this structure:
- Header with gradient and title
- Anchors navigation (if multiple sections)
- PageContent wrapper
- PageContentSection for each major section
- Separator between sections
Example from _page.privacy._index.tsx:
<Header gradient="sand" headline="Privacy" subline="..." />
<Anchors anchorIds={...}>...</Anchors>
<PageContent>
<PageContentSection headline="General" fragment="general">
<p>...</p>
</PageContentSection>
<Separator />
<PageContentSection headline="..." fragment="...">
...
</PageContentSection>
</PageContent>
Route Patterns
_page.*files: Public pages with layout wrapper_page.package.$packageName.tsx: Package detail pages_page.author.$authorName.tsx: Author profile pages_page.privacy._index.tsx: Static content pagesapi.*: API endpoints (search, MCP)*.og.*: Open Graph image generation
Data Loading
Use React Router's loader pattern:
export const loader: LoaderFunction = async ({ params }) => {
const data = await Service.fetchData(params.id);
if (!data) throw data(null, { status: 404 });
return data(data);
};
Access via useLoaderData<LoaderData>().
Meta Tags
Use mergeMeta() helper for combining meta tags:
export const meta = mergeMeta(({ data }) => {
const title = `${data.name} | CRAN/E`;
return [
{ title },
{ name: "description", content: data.description },
{ property: "og:title", content: title },
{ property: "og:image", content: `${url}/og` },
];
});
Styling Best Practices
- Use CVA for variants: Don't hardcode conditional classes
- Prefer Tailwind utilities: Avoid custom CSS when possible
- Dark mode: Use Radix Colors with
dark:variants - Transitions: Use duration-700 for smooth, professional animations
- Responsive: Use
md:breakpoint for desktop adjustments - Spacing: Use gap utilities for flex/grid spacing instead of margins
Accessibility
- Use semantic HTML elements
- Provide meaningful alt text for images
- Ensure keyboard navigation works
- Test with screen readers when adding interactive elements
Icon Usage
Import from @remixicon/react:
import { RiArrowRightSLine, RiExternalLinkLine } from "@remixicon/react";
Size prop for consistency (typically 16 or 20).
Component File Structure
import { cva, VariantProps } from "cva";
import { PropsWithChildren, ReactNode } from "react";
type Props = PropsWithChildren<
VariantProps<typeof twBase> & {
// Custom props
}
>;
const twBase = cva({
base: "...",
variants: {
size: { ... },
},
defaultVariants: { ... },
});
export function Component(props: Props) {
const { children, ...rest } = props;
return <div className={twBase(rest)}>{children}</div>;
}
Component.displayName = "Component";
Testing Strategy
- Lint:
npm run lintfrom/web - Typecheck:
npm run typecheckfrom/web - Build:
npm run buildfrom/web - No unit tests; validation relies on lint/typecheck/build
Common Patterns to Follow
Example: Package Detail Page (_page.package.$packageName.tsx)
- Header with package name and gradient
- Anchors for section navigation (Synopsis, Documentation, Team, Insights, Binaries, Dependencies)
- Multiple PageContentSection components
- InfoPill for metadata (version, license, etc.)
- InfoCard for related packages/authors
- ExternalLink components for CRAN URLs
- Lazy-loaded components for performance (e.g., PackageDependencySearch)
Example: Author Page (_page.author.$authorName.tsx)
- Header with author name
- List of packages by author using InfoCard grid
- Team section showing collaborators
- Similar structure to package page
File Organization
- Routes:
/web/app/routes/*.tsx - Components:
/web/app/modules/*.tsx - Data services:
/web/app/data/*.ts - Types:
/web/app/data/*.shape.ts(Zod schemas),*.types.generated.ts(Supabase)
Environment
- Node >= 20 (v23.3.0 recommended via
.nvmrc) - npm (with
legacy-peer-deps = truein.npmrc) - Required env vars: See
.env.example