Smithery Logo
MCPsSkillsDocsPricing
Login
Smithery Logo

Accelerating the Agent Economy

Resources

DocumentationPrivacy PolicySystem Status

Company

PricingAboutBlog

Connect

© 2026 Smithery. All rights reserved.

    petrilahdelma

    frontend-development

    petrilahdelma/frontend-development
    Coding
    1
    3 installs

    About

    SKILL.md

    Install

    Install via Skills CLI

    or add to your agent
    • Claude Code
      Claude Code
    • Codex
      Codex
    • OpenClaw
      OpenClaw
    • Cursor
      Cursor
    • Amp
      Amp
    • GitHub Copilot
      GitHub Copilot
    • Gemini CLI
      Gemini CLI
    • Kilo Code
      Kilo Code
    • Junie
      Junie
    • Replit
      Replit
    • Windsurf
      Windsurf
    • Cline
      Cline
    • Continue
      Continue
    • OpenCode
      OpenCode
    • OpenHands
      OpenHands
    • Roo Code
      Roo Code
    • Augment
      Augment
    • Goose
      Goose
    • Trae
      Trae
    • Zencoder
      Zencoder
    • Antigravity
      Antigravity
    ├─
    ├─
    └─

    About

    Expert guidance for React 18+, Next.js 15, TypeScript, CSS Modules, and modern frontend architecture...

    SKILL.md

    Frontend Development Expert

    Core Principles

    When building frontend features, always follow these principles:

    1. Component-First Architecture: Break UI into small, reusable, testable components
    2. Type Safety: Use TypeScript strict mode with proper interfaces and no any
    3. Accessibility by Default: WCAG 2.1 AA compliance minimum
    4. Performance Optimization: Code splitting, lazy loading, memoization
    5. Progressive Enhancement: Works without JS, enhanced with JS

    Technology Stack (Digitaltableteur Project)

    Core Framework

    • React 18 with concurrent features
    • Next.js 15.5.6 with App Router (server components)
    • TypeScript 5.8 in strict mode

    Styling

    • CSS Modules for scoped styling (never inline styles except dynamic backgroundImage)
    • Design tokens from shared/styles/variables.css
    • Logical properties (margin-inline, padding-block)

    State & Data

    • React hooks (useState, useEffect, useContext, useMemo, useCallback)
    • Server components for data fetching in Next.js
    • Client components ("use client") for interactivity

    Internationalization

    • i18next + react-i18next for translations
    • 3 languages: EN (default), FI, SV
    • Cookie-based persistence for language preference

    Testing

    • Vitest for unit tests
    • Testing Library for component testing
    • axe-core for accessibility testing
    • Playwright for E2E via Storybook

    Component Development Workflow

    1. Before Creating ANY Component

    CRITICAL: Always read these files first:

    • docs/LLM_COMPONENT_GENERATION_RULES.md (12,000+ words, comprehensive guide)
    • docs/LLM-CRITICAL-REASONING-AND-PLANNING-INSTRUCTIONS.md (planning requirements)

    These documents are mandatory and cover:

    • Folder structure (ComponentName/ComponentName.tsx, .module.css, .test.tsx, .stories.tsx, index.ts)
    • CSS Modules patterns and design token usage
    • Accessibility requirements and testing
    • i18n integration (all text must be translatable)
    • TypeScript interfaces and props patterns
    • Storybook documentation with WIP badge system

    2. Component Folder Structure

    shared/components/ComponentName/
    ├── ComponentName.tsx          # Main component
    ├── ComponentName.module.css   # Scoped styles
    ├── ComponentName.test.tsx     # Unit + a11y tests
    ├── ComponentName.stories.tsx  # Storybook documentation
    └── index.ts                   # Export barrel
    

    Never create standalone component files.

    3. Component Template

    // ComponentName.tsx
    import React from 'react';
    import { useTranslation } from 'react-i18next';
    import styles from './ComponentName.module.css';
    
    export interface ComponentNameProps {
      /** Brief description of what this prop does */
      propName: string;
      /** Optional prop with default value */
      variant?: 'primary' | 'secondary';
      /** Children elements */
      children?: React.ReactNode;
    }
    
    /**
     * Component description: what it does and when to use it.
     *
     * @example
     * ```tsx
     * <ComponentName propName="value" variant="primary">
     *   Content
     * </ComponentName>
     * ```
     */
    export const ComponentName: React.FC<ComponentNameProps> = ({
      propName,
      variant = 'primary',
      children,
    }) => {
      const { t } = useTranslation();
    
      return (
        <div className={`${styles.container} ${styles[variant]}`}>
          <h2>{t('componentName.title')}</h2>
          {children}
        </div>
      );
    };
    

    4. CSS Modules Pattern

    /* ComponentName.module.css */
    
    /* Base styles - use design tokens */
    .container {
      padding: var(--space-layout-24);
      background-color: var(--color-background);
      color: var(--color-text);
    
      /* Use logical properties */
      margin-inline: auto;
      padding-block: var(--space-layout-16);
    
      /* Responsive with fluid typography */
      font-size: clamp(1rem, 2vw, 1.25rem);
    }
    
    /* Variants */
    .primary {
      background-color: var(--color-primary);
      color: var(--color-white);
    }
    
    .secondary {
      background-color: var(--color-secondary);
      color: var(--color-white);
    }
    
    /* States */
    .container:hover {
      transform: scale(1.02);
      transition: transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
    }
    
    /* Responsive */
    @media (max-width: 768px) {
      .container {
        padding: var(--space-layout-16);
      }
    }
    
    /* Accessibility - reduced motion */
    @media (prefers-reduced-motion: reduce) {
      .container:hover {
        transform: none;
      }
    }
    

    5. Required Tests

    // ComponentName.test.tsx
    import { describe, it, expect } from 'vitest';
    import { render, screen } from '@testing-library/react';
    import { axe, toHaveNoViolations } from 'jest-axe';
    import { ComponentName } from './ComponentName';
    
    expect.extend(toHaveNoViolations);
    
    describe('ComponentName', () => {
      it('renders without crashing', () => {
        render(<ComponentName propName="test" />);
        expect(screen.getByText(/test/i)).toBeInTheDocument();
      });
    
      it('has no accessibility violations', async () => {
        const { container } = render(<ComponentName propName="test" />);
        const results = await axe(container);
        expect(results).toHaveNoViolations();
      });
    
      it('applies variant styles correctly', () => {
        const { container } = render(
          <ComponentName propName="test" variant="secondary" />
        );
        expect(container.firstChild).toHaveClass('secondary');
      });
    });
    

    6. Storybook Story

    // ComponentName.stories.tsx
    import type { Meta, StoryObj } from '@storybook/react';
    import { ComponentName } from './ComponentName';
    
    const meta: Meta<typeof ComponentName> = {
      title: 'Components/ComponentName',
      component: ComponentName,
      parameters: {
        docs: {
          description: {
            component: 'Description of what this component does.',
          },
        },
      },
      tags: ['autodocs', 'wip'], // Add 'wip' badge until fully tested
      argTypes: {
        variant: {
          control: 'select',
          options: ['primary', 'secondary'],
        },
      },
    };
    
    export default meta;
    type Story = StoryObj<typeof ComponentName>;
    
    export const Primary: Story = {
      args: {
        propName: 'Example text',
        variant: 'primary',
        children: 'Content here',
      },
    };
    
    export const Secondary: Story = {
      args: {
        propName: 'Example text',
        variant: 'secondary',
        children: 'Content here',
      },
    };
    

    Next.js 15 App Router Patterns

    Server Components (Default)

    // app/page.tsx
    import { ComponentName } from '@/shared/components/ComponentName';
    
    // Server component - can fetch data directly
    export default async function Page() {
      const data = await fetch('https://api.example.com/data');
      const json = await data.json();
    
      return (
        <main>
          <ComponentName propName={json.title} />
        </main>
      );
    }
    

    Client Components (Interactive)

    // app/components/InteractiveWidget.tsx
    "use client";
    
    import { useState } from 'react';
    
    export function InteractiveWidget() {
      const [count, setCount] = useState(0);
    
      return (
        <button onClick={() => setCount(count + 1)}>
          Count: {count}
        </button>
      );
    }
    

    Metadata for SEO

    // app/about/page.tsx
    import type { Metadata } from 'next';
    
    export async function generateMetadata(): Promise<Metadata> {
      return {
        title: 'About | Digitaltableteur',
        description: 'Learn about our design systems expertise',
        openGraph: {
          title: 'About | Digitaltableteur',
          description: 'Learn about our design systems expertise',
          images: ['/og-image.png'],
          type: 'website',
        },
        twitter: {
          card: 'summary_large_image',
          title: 'About | Digitaltableteur',
          description: 'Learn about our design systems expertise',
          images: ['/og-image.png'],
        },
      };
    }
    
    export default function AboutPage() {
      return <div>About content</div>;
    }
    

    Performance Optimization

    Code Splitting

    // Lazy load heavy components
    import dynamic from 'next/dynamic';
    
    const HeavyChart = dynamic(() => import('./HeavyChart'), {
      loading: () => <p>Loading chart...</p>,
      ssr: false, // Disable SSR for client-only components
    });
    

    Memoization

    import { useMemo, useCallback } from 'react';
    
    function ExpensiveComponent({ items }: { items: Item[] }) {
      // Memoize expensive calculations
      const sortedItems = useMemo(() => {
        return items.sort((a, b) => a.name.localeCompare(b.name));
      }, [items]);
    
      // Memoize callbacks
      const handleClick = useCallback((id: string) => {
        console.log('Clicked:', id);
      }, []);
    
      return <div>{/* Use sortedItems */}</div>;
    }
    

    Image Optimization

    import Image from 'next/image';
    
    <Image
      src="/hero-image.jpg"
      alt="Descriptive alt text"
      width={1200}
      height={630}
      priority // For above-the-fold images
      quality={85}
    />
    

    Accessibility Checklist

    Before marking any component complete, verify:

    • Semantic HTML (<button>, <nav>, <main>, <article>)
    • ARIA labels where needed (aria-label, aria-describedby)
    • Keyboard navigation (Tab, Enter, Escape)
    • Focus indicators visible and clear
    • Color contrast ≥ 4.5:1 for text
    • Screen reader testing (run with VoiceOver/NVDA)
    • No accessibility violations in axe-core tests

    Internationalization (i18n)

    Adding Translation Keys

    // shared/locales/en/translation.json
    {
      "componentName": {
        "title": "Component Title",
        "description": "Component description text",
        "button": {
          "submit": "Submit",
          "cancel": "Cancel"
        }
      }
    }
    

    Using Translations

    import { useTranslation } from 'react-i18next';
    
    function MyComponent() {
      const { t } = useTranslation();
    
      return (
        <div>
          <h1>{t('componentName.title')}</h1>
          <p>{t('componentName.description')}</p>
          <button>{t('componentName.button.submit')}</button>
        </div>
      );
    }
    

    MUST: Ensure 100% translation coverage in EN, FI, SV.


    Common Patterns

    Form Handling

    "use client";
    import { useState } from 'react';
    
    export function ContactForm() {
      const [formData, setFormData] = useState({ name: '', email: '' });
      const [loading, setLoading] = useState(false);
    
      const handleSubmit = async (e: React.FormEvent) => {
        e.preventDefault();
        setLoading(true);
    
        try {
          const response = await fetch('/api/contact', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify(formData),
          });
    
          if (!response.ok) throw new Error('Submission failed');
    
          // Handle success
        } catch (error) {
          console.error(error);
        } finally {
          setLoading(false);
        }
      };
    
      return (
        <form onSubmit={handleSubmit}>
          <input
            type="text"
            value={formData.name}
            onChange={(e) => setFormData({ ...formData, name: e.target.value })}
            required
          />
          <button type="submit" disabled={loading}>
            {loading ? 'Sending...' : 'Submit'}
          </button>
        </form>
      );
    }
    

    Context for Global State

    // contexts/ThemeContext.tsx
    "use client";
    import { createContext, useContext, useState } from 'react';
    
    type Theme = 'light' | 'dark';
    
    const ThemeContext = createContext<{
      theme: Theme;
      setTheme: (theme: Theme) => void;
    }>({ theme: 'light', setTheme: () => {} });
    
    export function ThemeProvider({ children }: { children: React.ReactNode }) {
      const [theme, setTheme] = useState<Theme>('light');
    
      return (
        <ThemeContext.Provider value={{ theme, setTheme }}>
          {children}
        </ThemeContext.Provider>
      );
    }
    
    export const useTheme = () => useContext(ThemeContext);
    

    Anti-Patterns to Avoid

    ❌ DON'T:

    • Use @ts-ignore or any types
    • Create inline styles (except dynamic backgroundImage)
    • Hardcode colors (use CSS custom properties)
    • Bypass ESLint errors
    • Skip accessibility testing
    • Forget responsive design
    • Create standalone component files
    • Use class components (use functional components)

    ✅ DO:

    • Use TypeScript strict mode
    • Follow folder structure pattern
    • Write tests for all components
    • Use design tokens from variables.css
    • Test with reduced motion preferences
    • Ensure keyboard navigation
    • Keep functions under 50 lines
    • Extract complex logic to custom hooks

    Quality Gates (Run Before Commit)

    # Type checking
    npm run typecheck
    
    # Linting
    npm run lint
    
    # Tests
    npm test
    
    # Build verification
    npm run build
    
    # All together
    npm run typecheck && npm run lint && npm test && npm run build
    

    Additional Resources

    For detailed component generation rules, always refer to:

    • docs/LLM_COMPONENT_GENERATION_RULES.md - Complete component architecture guide
    • shared/components/CLAUDE.md - Component library specific instructions
    • app/CLAUDE.md - Next.js App Router patterns

    For critical planning steps:

    • docs/LLM-CRITICAL-REASONING-AND-PLANNING-INSTRUCTIONS.md

    When to Use This Skill

    Activate this skill when:

    • Building React components
    • Implementing Next.js features
    • Working with TypeScript interfaces
    • Styling with CSS Modules
    • Adding i18n translations
    • Optimizing performance
    • Debugging frontend issues
    • Writing component tests
    • Creating Storybook stories
    • Implementing accessibility features

    Remember: Always read the component generation rules before creating new components.

    Recommended Servers
    Svelte
    Svelte
    Vercel
    Vercel
    Cloudflare
    Cloudflare
    Repository
    petrilahdelma/digitaltableteur
    Files