Claude Code Plugins

Community-maintained marketplace

Feedback

Guia implementação de breadcrumb de navegação hierárquica seguindo padrões de acessibilidade WCAG e design system do Plataforma B2B de treinamento técnico corporativo

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name breadcrumb-impl
description Guia implementação de breadcrumb de navegação hierárquica seguindo padrões de acessibilidade WCAG e design system do Plataforma B2B de treinamento técnico corporativo
allowed-tools Read, Edit, Write

Breadcrumb Implementation Skill - Plataforma B2B de treinamento técnico corporativo

Objetivo

Esta skill ativa automaticamente ao implementar sistema de breadcrumb (navegação hierárquica) no Plataforma B2B de treinamento técnico corporativo, garantindo:

  • Estrutura semântica correta (HTML5 + ARIA)
  • Acessibilidade WCAG 2.1 AA
  • Design responsivo
  • Integração com React Router (quando implementado)

Especificação (US-061)

User Story: Implementar Sistema de Breadcrumb Complexidade: 8 pontos Sprint: 2.4 Prioridade: 🟠 P1

Critérios de Aceite

✅ Posicionado no topo da página (abaixo do header) ✅ Formato: Hub > Curso de Bash > Aula 1.1 ✅ Cada item é clicável (exceto o atual) ✅ Item atual em negrito ✅ Separador: > ou / ✅ Responsivo: colapsa em mobile para ... > Aula 1.1 ✅ Acessibilidade: aria-label="Breadcrumb", aria-current="page"

Estrutura HTML Semântica

<nav aria-label="Breadcrumb" className="breadcrumb-container">
  <ol className="breadcrumb-list">
    <li className="breadcrumb-item">
      <a href="#" onClick={handleHome}>
        🏠 Hub
      </a>
    </li>
    <li className="breadcrumb-separator" aria-hidden="true">
      <span>></span>
    </li>
    <li className="breadcrumb-item">
      <a href="#" onClick={handleCourse}>
        📖 Curso de Bash
      </a>
    </li>
    <li className="breadcrumb-separator" aria-hidden="true">
      <span>></span>
    </li>
    <li className="breadcrumb-item">
      <span aria-current="page" className="current">
        📝 Aula 1.1
      </span>
    </li>
  </ol>
</nav>

Componente React

Criar: src/components/shared/Breadcrumb.jsx

import React from 'react'
import { ChevronRight } from 'lucide-react'

/**
 * Breadcrumb - Navegação hierárquica acessível
 *
 * @param {Array} items - Array de objetos: [{label, icon, onClick, current}]
 * @param {string} separator - Separador visual (default: ">")
 * @param {boolean} collapse - Colapsar em mobile (default: true)
 */
export function Breadcrumb({ items, separator = '>', collapse = true }) {
  if (!items || items.length === 0) return null

  return (
    <nav
      aria-label="Breadcrumb"
      className="px-6 py-3 bg-white/80 backdrop-blur-sm border-b border-slate-200"
    >
      <ol className="flex items-center flex-wrap gap-2 text-sm">
        {items.map((item, index) => {
          const isLast = index === items.length - 1
          const isFirst = index === 0

          // Mobile: Mostrar apenas último item se collapse ativo
          const hiddenOnMobile = collapse && !isLast && items.length > 2

          return (
            <React.Fragment key={index}>
              <li
                className={`
                  breadcrumb-item
                  ${hiddenOnMobile ? 'hidden md:flex' : 'flex'}
                  items-center gap-2
                `}
              >
                {isLast ? (
                  // Último item: não clicável, em negrito
                  <span
                    aria-current="page"
                    className="font-semibold text-slate-900"
                  >
                    {item.icon && <span className="inline-block mr-1">{item.icon}</span>}
                    {item.label}
                  </span>
                ) : (
                  // Item clicável
                  <button
                    onClick={item.onClick}
                    className="
                      flex items-center gap-1
                      text-slate-600 hover:text-purple-600
                      transition-colors duration-200
                      hover:underline
                      focus:outline-none focus:ring-2 focus:ring-purple-500 focus:ring-offset-2
                      rounded-sm px-1
                    "
                    aria-label={`Navegar para ${item.label}`}
                  >
                    {item.icon && <span>{item.icon}</span>}
                    <span>{item.label}</span>
                  </button>
                )}
              </li>

              {/* Separador */}
              {!isLast && (
                <li
                  aria-hidden="true"
                  className={`
                    text-slate-400
                    ${hiddenOnMobile ? 'hidden md:block' : 'block'}
                  `}
                >
                  {typeof separator === 'string' ? (
                    <span className="text-sm">{separator}</span>
                  ) : (
                    <ChevronRight className="w-4 h-4" />
                  )}
                </li>
              )}

              {/* Mobile: Mostrar "..." antes do último item */}
              {collapse && isFirst && items.length > 2 && (
                <li className="md:hidden text-slate-400" aria-hidden="true">
                  <span>...</span>
                </li>
              )}
            </React.Fragment>
          )
        })}
      </ol>
    </nav>
  )
}

Uso nos Componentes

Nível 2: Curso (Learning System)

// BashLearningSystem.jsx
import { Breadcrumb } from '../shared/Breadcrumb'

export function BashLearningSystem({ onBack }) {
  const breadcrumbItems = [
    {
      label: 'Hub',
      icon: '🏠',
      onClick: onBack
    },
    {
      label: 'Curso de Bash',
      icon: '📖',
      current: true
    }
  ]

  return (
    <div>
      <Breadcrumb items={breadcrumbItems} />
      {/* Resto do componente */}
    </div>
  )
}

Nível 3: Aula (Notes View)

// BashNotesView.jsx
import { Breadcrumb } from '../shared/Breadcrumb'

export function BashNotesView({
  moduleTitle,
  onBackToCourse,
  onBackToHub
}) {
  const breadcrumbItems = [
    {
      label: 'Hub',
      icon: '🏠',
      onClick: onBackToHub
    },
    {
      label: 'Curso de Bash',
      icon: '📖',
      onClick: onBackToCourse
    },
    {
      label: moduleTitle || 'Aula',
      icon: '📝',
      current: true
    }
  ]

  return (
    <div>
      <Breadcrumb items={breadcrumbItems} />
      {/* Resto do componente */}
    </div>
  )
}

Nível 4: Modal Flash Cards

// FlashcardModal.jsx
import { Breadcrumb } from '../shared/Breadcrumb'

export function FlashcardModal({
  technology,
  section,
  onClose
}) {
  const breadcrumbItems = [
    {
      label: 'Hub',
      icon: '🏠',
      onClick: () => {} // Desabilitado em modal
    },
    {
      label: `Curso de ${technology}`,
      icon: '📖',
      onClick: () => {} // Desabilitado em modal
    },
    {
      label: `Praticando: ${section}`,
      icon: '💡',
      current: true
    }
  ]

  return (
    <div className="modal-overlay">
      <div className="modal-content">
        <Breadcrumb
          items={breadcrumbItems}
          collapse={true}
        />
        {/* Cards */}
      </div>
    </div>
  )
}

Variações de Design

Com Ícone Lucide React

import { Home, BookOpen, FileText } from 'lucide-react'

const breadcrumbItems = [
  { label: 'Hub', icon: <Home className="w-4 h-4" />, onClick: handleHome },
  { label: 'Curso', icon: <BookOpen className="w-4 h-4" />, onClick: handleCourse },
  { label: 'Aula', icon: <FileText className="w-4 h-4" />, current: true }
]

Com Separador Customizado

<Breadcrumb
  items={breadcrumbItems}
  separator={<ChevronRight className="w-3 h-3 text-slate-400" />}
/>

Sem Colapso Mobile

<Breadcrumb
  items={breadcrumbItems}
  collapse={false}
/>

Acessibilidade (WCAG 2.1 AA)

Estrutura Semântica

<nav> com aria-label="Breadcrumb": Identifica região de navegação ✅ <ol> ao invés de <ul>: Lista ordenada (sequência importa) ✅ <li> para cada item: Estrutura de lista semântica ✅ aria-current="page": Marca item atual ✅ aria-hidden="true" nos separadores: Esconde de screen readers

Navegação por Teclado

Tab: Navega entre items clicáveis ✅ Enter/Space: Ativa link ✅ Focus visível: Ring de foco em botões

Screen Readers

NVDA/JAWS leitura esperada:

"Breadcrumb navigation
Link: Home
Link: Curso de Bash
Current page: Aula 1.1"

Responsividade

Desktop (≥768px)

🏠 Hub > 📖 Curso de Bash > 📝 Aula 1.1: Introdução ao Shell

Tablet (≥640px)

🏠 Hub > 📖 Bash > 📝 Aula 1.1

Mobile (<640px)

... > 📝 Aula 1.1

Testes

Testes Unitários

// Breadcrumb.test.jsx
import { render, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { Breadcrumb } from './Breadcrumb'

describe('Breadcrumb', () => {
  const mockItems = [
    { label: 'Home', icon: '🏠', onClick: jest.fn() },
    { label: 'Curso', icon: '📖', onClick: jest.fn() },
    { label: 'Aula', icon: '📝', current: true }
  ]

  it('renderiza todos os items', () => {
    render(<Breadcrumb items={mockItems} />)
    expect(screen.getByText('Home')).toBeInTheDocument()
    expect(screen.getByText('Curso')).toBeInTheDocument()
    expect(screen.getByText('Aula')).toBeInTheDocument()
  })

  it('marca último item com aria-current', () => {
    render(<Breadcrumb items={mockItems} />)
    const currentItem = screen.getByText('Aula')
    expect(currentItem).toHaveAttribute('aria-current', 'page')
  })

  it('chama onClick ao clicar em item', async () => {
    const user = userEvent.setup()
    render(<Breadcrumb items={mockItems} />)

    const homeButton = screen.getByRole('button', { name: /Home/i })
    await user.click(homeButton)

    expect(mockItems[0].onClick).toHaveBeenCalledTimes(1)
  })

  it('item atual não é clicável', () => {
    render(<Breadcrumb items={mockItems} />)
    const currentItem = screen.getByText('Aula')
    expect(currentItem.tagName).toBe('SPAN')
  })

  it('exibe separadores entre items', () => {
    render(<Breadcrumb items={mockItems} separator=">" />)
    const separators = screen.getAllByText('>')
    expect(separators).toHaveLength(2) // Entre 3 items
  })
})

Teste E2E com Playwright

// breadcrumb.spec.js
test('breadcrumb navigation', async ({ page }) => {
  await page.goto('http://localhost:3000')

  // Navegar para curso
  await page.click('text=Bash')
  await expect(page.locator('nav[aria-label="Breadcrumb"]')).toBeVisible()
  await expect(page.locator('text=Hub')).toBeVisible()

  // Clicar em breadcrumb para voltar
  await page.click('nav[aria-label="Breadcrumb"] >> text=Hub')
  await expect(page).toHaveURL('http://localhost:3000')
})

Integração com React Router (Futuro)

Quando US-040 (React Router) for implementado:

import { Link, useLocation } from 'react-router-dom'

export function Breadcrumb({ items }) {
  return (
    <nav aria-label="Breadcrumb">
      <ol>
        {items.map((item, index) => (
          <li key={index}>
            {item.current ? (
              <span aria-current="page">{item.label}</span>
            ) : (
              <Link to={item.path}>{item.label}</Link>
            )}
          </li>
        ))}
      </ol>
    </nav>
  )
}

Referências

Ativação Automática

Esta skill ativa quando você:

  • Implementa US-061 (Sistema de Breadcrumb)
  • Trabalha com navegação hierárquica
  • Cria componente Breadcrumb.jsx
  • Adiciona breadcrumb a Learning Systems
  • Testa acessibilidade de navegação