Communitygithub.com

Frontend Designer

Construye componentes frontend accesibles, responsivos y de alto rendimiento siguiendo las mejores prácticas de sistemas de diseño, CSS moderno y patrones agnósticos de frameworks.

¿Qué es Frontend Designer?

Frontend Designer es una habilidad integral para diseñadores y desarrolladores frontend que desean crear interfaces de usuario hermosas, accesibles y eficientes siguiendo las mejores prácticas modernas. Útil en plataformas como Claude Code, Cursor y Codex, esta habilidad guía la implementación de componentes reutilizables, sistemas de diseño basados en tokens, diseño responsivo mobile-first, accesibilidad WCAG 2.1 AA, y optimización del rendimiento. Incluye patrones concretos para componentes como botones, tarjetas e inputs de formulario, con ejemplos en React, TypeScript y CSS moderno (Flexbox, Grid, propiedades personalizadas, container queries). También cubre técnicas avanzadas como carga perezosa de imágenes, división de código, enlaces de salto, gestión de foco, y theming claro/oscuro. La habilidad proporciona scripts para generar componentes, auditar accesibilidad y configurar sistemas de diseño, asegurando consistencia, mantenibilidad y velocidad en el desarrollo frontend.

Compatible con✓Claude Code~Codex CLI✓Cursor
npx skills add https://github.com/Samueladewole/claude-skills/tree/HEAD/frontend-designer

Preguntar en tu IA favorita

Abre un nuevo chat con esta habilidad de agente ya precargada.

Documentación

¿Qué hace Frontend Designer?

Una habilidad integral para diseñadores y desarrolladores frontend que desean construir interfaces de usuario hermosas, accesibles y eficientes siguiendo las mejores prácticas modernas.

Lo que esta habilidad hace

Ayuda a diseñadores/desarrolladores frontend con:

  • Diseño y Desarrollo de Componentes - Construye componentes reutilizables y accesibles
  • Sistemas de Diseño - Implementa tokens, patrones y documentación
  • Diseño Responsivo - Layouts fluidos mobile-first
  • Accesibilidad (WCAG 2.1) - Patrones de diseño inclusivo
  • CSS Moderno - Flexbox, Grid, propiedades personalizadas
  • Optimización de Rendimiento - Frontends rápidos y eficientes
  • Patrones de Framework - Mejores prácticas para React, Vue, Svelte
  • Diseño a Código - Flujos de Figma a producción

Por qué esta habilidad importa

Sin un enfoque sistemático:

  • Implementaciones de componentes inconsistentes
  • Problemas de accesibilidad
  • Comportamiento responsivo deficiente
  • Código y estilos duplicados
  • Difícil de mantener
  • Problemas de rendimiento

Con esta habilidad:

  • Componentes consistentes y reutilizables
  • Cumplimiento WCAG AA por defecto
  • Diseño responsivo mobile-first
  • Alineado con el sistema de diseño
  • Código base mantenible
  • Entrega rápida y optimizada

Principios Fundamentales

1. Accesibilidad Primero

  • Mínimo WCAG 2.1 AA
  • HTML semántico
  • Navegación por teclado
  • Soporte para lectores de pantalla
  • Gestión de foco
  • Contraste de color

2. Responsivo Mobile-First

  • Comenzar con móvil (320px)
  • Mejora progresiva
  • Tipografía fluida
  • Layouts flexibles
  • Objetivos táctiles amigables

3. Rendimiento por Defecto

  • CSS/JS mínimo
  • Carga perezosa
  • Imágenes optimizadas
  • CSS crítico
  • Tree shaking

4. Orientado a Componentes

  • Metodología de diseño atómico
  • Componentes reutilizables
  • Personalización basada en props
  • Composición sobre herencia

5. Alineado con el Sistema de Diseño

  • Tokens de diseño
  • Espaciado consistente
  • Escala tipográfica
  • Paleta de colores
  • Biblioteca de componentes

Patrones de Componentes

Componente Botón

Patrón de botón accesible y flexible:

// Ejemplo React
interface ButtonProps {
  variant?: 'primary' | 'secondary' | 'ghost';
  size?: 'sm' | 'md' | 'lg';
  disabled?: boolean;
  loading?: boolean;
  children: React.ReactNode;
  onClick?: () => void;
}

export const Button: React.FC<ButtonProps> = ({
  variant = 'primary',
  size = 'md',
  disabled = false,
  loading = false,
  children,
  onClick,
  ...props
}) => {
  return (
    <button
      className={`btn btn--${variant} btn--${size}`}
      disabled={disabled || loading}
      onClick={onClick}
      aria-busy={loading}
      {...props}
    >
      {loading ? <Spinner /> : children}
    </button>
  );
};

CSS (con tokens de diseño):

.btn {
  /* Estilos base */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);

  font-family: var(--font-sans);
  font-weight: 600;
  text-decoration: none;

  border: none;
  border-radius: var(--radius-md);
  cursor: pointer;

  transition: all 0.2s ease;

  /* Accesibilidad */
  min-height: 44px; /* Objetivo táctil WCAG */

  &:focus-visible {
    outline: 2px solid var(--color-focus);
    outline-offset: 2px;
  }

  &:disabled {
    opacity: 0.5;
    cursor: not-allowed;
  }
}

/* Variantes */
.btn--primary {
  background: var(--color-primary);
  color: var(--color-on-primary);

  &:hover:not(:disabled) {
    background: var(--color-primary-hover);
  }
}

.btn--secondary {
  background: var(--color-secondary);
  color: var(--color-on-secondary);
}

.btn--ghost {
  background: transparent;
  color: var(--color-primary);
  border: 1px solid currentColor;
}

/* Tamaños */
.btn--sm {
  padding: var(--space-2) var(--space-3);
  font-size: var(--text-sm);
}

.btn--md {
  padding: var(--space-3) var(--space-4);
  font-size: var(--text-base);
}

.btn--lg {
  padding: var(--space-4) var(--space-6);
  font-size: var(--text-lg);
}

Componente Tarjeta

Tarjeta flexible y accesible:

interface CardProps {
  variant?: 'elevated' | 'outlined' | 'filled';
  padding?: 'none' | 'sm' | 'md' | 'lg';
  interactive?: boolean;
  children: React.ReactNode;
}

export const Card: React.FC<CardProps> = ({
  variant = 'elevated',
  padding = 'md',
  interactive = false,
  children,
}) => {
  const Component = interactive ? 'button' : 'div';

  return (
    <Component
      className={`
        card
        card--${variant}
        card--padding-${padding}
        ${interactive ? 'card--interactive' : ''}
      `}
      {...(interactive && { role: 'button', tabIndex: 0 })}
    >
      {children}
    </Component>
  );
};

CSS:

.card {
  border-radius: var(--radius-lg);
  background: var(--color-surface);
}

.card--elevated {
  box-shadow: var(--shadow-md);
}

.card--outlined {
  border: 1px solid var(--color-border);
}

.card--filled {
  background: var(--color-surface-variant);
}

.card--interactive {
  cursor: pointer;
  transition: transform 0.2s, box-shadow 0.2s;

  &:hover {
    transform: translateY(-2px);
    box-shadow: var(--shadow-lg);
  }

  &:focus-visible {
    outline: 2px solid var(--color-focus);
    outline-offset: 2px;
  }
}

.card--padding-sm { padding: var(--space-3); }
.card--padding-md { padding: var(--space-4); }
.card--padding-lg { padding: var(--space-6); }

Componente Input de Formulario

Input de formulario accesible:

interface InputProps {
  label: string;
  error?: string;
  hint?: string;
  required?: boolean;
  type?: 'text' | 'email' | 'password' | 'number';
}

export const Input: React.FC<InputProps> = ({
  label,
  error,
  hint,
  required = false,
  type = 'text',
  ...props
}) => {
  const id = useId();
  const hintId = `${id}-hint`;
  const errorId = `${id}-error`;

  return (
    <div className="input-wrapper">
      <label htmlFor={id} className="input-label">
        {label}
        {required && <span aria-label="required">*</span>}
      </label>

      {hint && (
        <p id={hintId} className="input-hint">
          {hint}
        </p>
      )}

      <input
        id={id}
        type={type}
        className={`input ${error ? 'input--error' : ''}`}
        aria-required={required}
        aria-invalid={!!error}
        aria-describedby={error ? errorId : hint ? hintId : undefined}
        {...props}
      />

      {error && (
        <p id={errorId} className="input-error" role="alert">
          {error}
        </p>
      )}
    </div>
  );
};

Tokens de Diseño

Propiedades personalizadas CSS para el sistema de diseño:

:root {
  /* Colores - Primarios */
  --color-primary: #0066FF;
  --color-primary-hover: #0052CC;
  --color-on-primary: #FFFFFF;

  /* Colores - Superficie */
  --color-surface: #FFFFFF;
  --color-surface-variant: #F5F5F5;
  --color-on-surface: #1A1A1A;

  /* Colores - Bordes */
  --color-border: #E0E0E0;
  --color-border-hover: #BDBDBD;

  /* Colores - Semánticos */
  --color-error: #D32F2F;
  --color-success: #388E3C;
  --color-warning: #F57C00;
  --color-info: #1976D2;

  /* Escala de Espaciado (base 8px) */
  --space-1: 0.25rem;  /* 4px */
  --space-2: 0.5rem;   /* 8px */
  --space-3: 0.75rem;  /* 12px */
  --space-4: 1rem;     /* 16px */
  --space-5: 1.5rem;   /* 24px */
  --space-6: 2rem;     /* 32px */
  --space-8: 3rem;     /* 48px */
  --space-10: 4rem;    /* 64px */

  /* Escala Tipográfica */
  --text-xs: 0.75rem;    /* 12px */
  --text-sm: 0.875rem;   /* 14px */
  --text-base: 1rem;     /* 16px */
  --text-lg: 1.125rem;   /* 18px */
  --text-xl: 1.25rem;    /* 20px */
  --text-2xl: 1.5rem;    /* 24px */
  --text-3xl: 1.875rem;  /* 30px */
  --text-4xl: 2.25rem;   /* 36px */

  /* Familias de Fuentes */
  --font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  --font-mono: "SF Mono", Monaco, "Cascadia Code", monospace;

  /* Alturas de Línea */
  --leading-tight: 1.25;
  --leading-normal: 1.5;
  --leading-relaxed: 1.75;

  /* Radio de Borde */
  --radius-sm: 0.25rem;  /* 4px */
  --radius-md: 0.5rem;   /* 8px */
  --radius-lg: 1rem;     /* 16px */
  --radius-full: 9999px;

  /* Sombras */
  --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05);
  --shadow-md: 0 4px 6px rgba(0, 0, 0, 0.1);
  --shadow-lg: 0 10px 15px rgba(0, 0, 0, 0.1);
  --shadow-xl: 0 20px 25px rgba(0, 0, 0, 0.15);

  /* Anillo de Foco */
  --color-focus: #0066FF;

  /* Transiciones */
  --transition-fast: 150ms ease;
  --transition-base: 200ms ease;
  --transition-slow: 300ms ease;
}

/* Modo oscuro */
@media (prefers-color-scheme: dark) {
  :root {
    --color-surface: #1A1A1A;
    --color-surface-variant: #2A2A2A;
    --color-on-surface: #FFFFFF;
    --color-border: #3A3A3A;
  }
}

Patrones de Diseño Responsivo

Puntos de Corte Mobile-First

/* Enfoque mobile-first */
.container {
  padding: var(--space-4);

  /* Tablet: 768px y superior */
  @media (min-width: 48rem) {
    padding: var(--space-6);
  }

  /* Desktop: 1024px y superior */
  @media (min-width: 64rem) {
    padding: var(--space-8);
    max-width: 1200px;
    margin: 0 auto;
  }
}

Tipografía Fluida

/* Tipografía responsiva */
h1 {
  font-size: clamp(2rem, 5vw, 3.5rem);
  line-height: var(--leading-tight);
}

h2 {
  font-size: clamp(1.5rem, 4vw, 2.5rem);
}

p {
  font-size: clamp(1rem, 2vw, 1.125rem);
  line-height: var(--leading-normal);
}

Layouts con Grid

/* Cuadrícula responsiva */
.grid {
  display: grid;
  gap: var(--space-4);

  /* Columnas auto-ajustables (mín 280px) */
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
}

/* Sistema de cuadrícula de 12 columnas */
.grid-12 {
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  gap: var(--space-4);
}

.col-span-4 {
  grid-column: span 4;
}

/* Apilar en móvil */
@media (max-width: 48rem) {
  .col-span-4 {
    grid-column: span 12;
  }
}

Patrones de Accesibilidad

Enlaces de Salto

export const SkipLink = () => (
  <a href="#main-content" className="skip-link">
    Saltar al contenido principal
  </a>
);
.skip-link {
  position: absolute;
  top: -40px;
  left: 0;
  background: var(--color-primary);
  color: var(--color-on-primary);
  padding: var(--space-2) var(--space-4);
  text-decoration: none;
  z-index: 100;

  &:focus {
    top: 0;
  }
}

Gestión de Foco

// Modal con trampa de foco
export const Modal = ({ isOpen, onClose, children }) => {
  const modalRef = useRef(null);

  useEffect(() => {
    if (isOpen) {
      // Guardar elemento actualmente enfocado
      const previouslyFocused = document.activeElement;

      // Enfocar primer elemento enfocable en el modal
      const firstFocusable = modalRef.current?.querySelector(
        'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
      );
      firstFocusable?.focus();

      // Restaurar foco al cerrar
      return () => previouslyFocused?.focus();
    }
  }, [isOpen]);

  if (!isOpen) return null;

  return (
    <div
      ref={modalRef}
      role="dialog"
      aria-modal="true"
      className="modal-overlay"
    >
      <div className="modal">
        {children}
      </div>
    </div>
  );
};

Etiquetas ARIA

// Botón de icono con etiqueta accesible
export const IconButton = ({ icon, label, ...props }) => (
  <button
    aria-label={label}
    className="icon-button"
    {...props}
  >
    <span aria-hidden="true">{icon}</span>
  </button>
);

// Estado de carga
export const LoadingButton = ({ loading, children, ...props }) => (
  <button
    aria-busy={loading}
    aria-live="polite"
    disabled={loading}
    {...props}
  >
    {loading ? 'Cargando...' : children}
  </button>
);

Optimización de Rendimiento

CSS Crítico

<!-- CSS crítico en línea -->
<style>
  /* Estilos above-the-fold */
  body { margin: 0; font-family: var(--font-sans); }
  .header { /* estilos críticos del encabezado */ }
  .hero { /* estilos críticos del héroe */ }
</style>

<!-- Cargar hoja de estilos completa de forma asíncrona -->
<link rel="stylesheet" href="styles.css" media="print" onload="this.media='all'">

Carga Perezosa de Imágenes

export const LazyImage = ({ src, alt, ...props }) => (
  <img
    src={src}
    alt={alt}
    loading="lazy"
    decoding="async"
    {...props}
  />
);

División de Código

// Carga perezosa en React
const Dashboard = lazy(() => import('./Dashboard'));

function App() {
  return (
    <Suspense fallback={<Loading />}>
      <Dashboard />
    </Suspense>
  );
}

Técnicas Modernas de CSS

Container Queries

.card {
  container-type: inline-size;
}

.card-content {
  display: flex;
  flex-direction: column;

  /* Cambiar a layout de fila cuando el contenedor > 400px */
  @container (min-width: 400px) {
    flex-direction: row;
  }
}

Auto-Fit con CSS Grid

.gallery {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
  gap: var(--space-4);
}

Propiedades Personalizadas para Temas

/* Tema claro (por defecto) */
:root {
  --bg: #ffffff;
  --text: #000000;
}

/* Tema oscuro */
[data-theme="dark"] {
  --bg: #000000;
  --text: #ffffff;
}

body {
  background: var(--bg);
  color: var(--text);
}

Estructura de Biblioteca de Componentes

src/
├── components/
│   ├── Button/
│   │   ├── Button.tsx
│   │   ├── Button.css
│   │   ├── Button.test.tsx
│   │   └── Button.stories.tsx
│   ├── Card/
│   ├── Input/
│   └── index.ts
├── tokens/
│   ├── colors.css
│   ├── spacing.css
│   ├── typography.css
│   └── index.css
├── utils/
│   ├── a11y.ts
│   └── responsive.ts
└── index.ts

Cómo Usar Esta Habilidad

Generar Componente

./scripts/generate_component.sh Button

Crea un componente con:

  • Archivo TypeScript/JSX
  • Módulo CSS
  • Archivo de prueba
  • Historia de Storybook
  • Verificaciones de accesibilidad

Configuración del Sistema de Diseño

./scripts/setup_design_system.sh

Crea:

  • Tokens de diseño (propiedades personalizadas CSS)
  • Estilos base
  • Plantillas de componentes
  • Estructura de documentación

Auditoría de Accesibilidad

./scripts/audit_accessibility.sh

Verifica:

  • Relaciones de contraste de color
  • Navegación por teclado
  • Atributos ARIA
  • HTML semántico
  • Gestión de foco

Mejores Prácticas

Diseño de Componentes

✅ HACER:

  • Usar HTML semántico
  • Hacer que los componentes sean accesibles por teclado
  • Proporcionar etiquetas ARIA
  • Soporte para modo claro y oscuro

Individual skills in this repo

This repo contains 5 individual skills — each has its own dedicated page.

Samueladewole/claude-skills

Create API handoff documentation for frontend developers. Use when backend work is complete and needs to be documented for frontend integration, or user says 'create handoff', 'document API', 'frontend handoff', or 'API documentation'.

Samueladewole/claude-skills

Set up Tailwind CSS v4 in Expo with react-native-css and NativeWind v5 for universal styling

Samueladewole/claude-skills

Document frontend data needs for backend developers. Use when frontend needs to communicate API requirements to backend, or user says 'backend requirements', 'what data do I need', 'API requirements', or is describing data needs for a UI.

Samueladewole/claude-skills

Master Next.js 14+ App Router with Server Components, streaming, parallel routes, and advanced data fetching. Use when building Next.js applications, implementing SSR/SSG, or optimizing React Server Components.

Samueladewole/claude-skills

Analyze a short-form video (TikTok, Instagram Reel, YouTube Short, or any MP4) to extract its full transcript, understand why it performed, and generate concrete repurposing ideas. Use this skill whenever the user asks to: analyze a video, understand why something went viral, reverse-engineer a creator's content, extract a transcript from a video, figure out what makes a video perform, or get repurposing ideas from a video. Also trigger when the user says things like "why did this perform", "what's the hook here", "can I repurpose this", "analyze this TikTok/Reel", or drops a video file path or URL.

Skills relacionados