Fumadocs + Code Hike
Frontend

Bootstrap

Framework CSS para desenvolvimento responsivo e mobile-first

O que é Bootstrap?

Bootstrap é o framework CSS mais popular do mundo para desenvolvimento de sites responsivos e mobile-first. Oferece um sistema de grid flexível, componentes pré-construídos e utilitários CSS para acelerar o desenvolvimento.

Por que utilizamos Bootstrap na IngenioLab?

  • Desenvolvimento rápido: Componentes prontos para uso
  • Responsivo: Mobile-first por padrão
  • Customizável: Temas e variáveis SASS
  • Compatibilidade: Funciona em todos os browsers
  • Comunidade: Vasta documentação e recursos
  • Integração React: Funciona perfeitamente com React

Instalação

# Via NPM
npm install bootstrap
# Com React Bootstrap (recomendado para React)
npm install react-bootstrap bootstrap
# Apenas CSS via CDN
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">

Configuração IngenioLab

1. Setup básico com Vite:

// src/main.tsx
import 'bootstrap/dist/css/bootstrap.min.css'
import 'bootstrap/dist/js/bootstrap.bundle.min.js'
import './index.css' // Seus estilos customizados
import { createRoot } from 'react-dom/client'
import App from './App'
createRoot(document.getElementById('root')!).render(<App />)

2. Com React Bootstrap:

// src/main.tsx
import 'bootstrap/dist/css/bootstrap.min.css'
import { createRoot } from 'react-dom/client'
import App from './App'
createRoot(document.getElementById('root')!).render(<App />)

3. Customização SASS:

// src/styles/custom-bootstrap.scss
// Importar funções e variáveis do Bootstrap
@import "bootstrap/scss/functions";
@import "bootstrap/scss/variables";
// Customizar variáveis
$primary: #007bff;
$secondary: #6c757d;
$success: #28a745;
$danger: #dc3545;
// Customizar breakpoints
$grid-breakpoints: (
xs: 0,
sm: 576px,
md: 768px,
lg: 992px,
xl: 1200px,
xxl: 1400px
);
// Importar o resto do Bootstrap
@import "bootstrap/scss/bootstrap";
// Estilos customizados IngenioLab
.ingeniolab-card {
border-radius: 12px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
.ingeniolab-btn {
border-radius: 8px;
font-weight: 600;
}

Sistema de Grid

1. Grid básico:

// src/components/GridExample.tsx
export const GridExample = () => {
return (
<div className="container">
<div className="row">
<div className="col-12 col-md-6 col-lg-4">
<h3>Coluna 1</h3>
<p>Conteúdo responsivo</p>
</div>
<div className="col-12 col-md-6 col-lg-4">
<h3>Coluna 2</h3>
<p>Adapta automaticamente</p>
</div>
<div className="col-12 col-md-12 col-lg-4">
<h3>Coluna 3</h3>
<p>Mobile-first design</p>
</div>
</div>
</div>
)
}

2. Grid avançado com React Bootstrap:

// src/components/Dashboard.tsx
import { Container, Row, Col, Card } from 'react-bootstrap'
export const Dashboard = () => {
return (
<Container fluid>
<Row className="g-4">
<Col xs={12} md={6} lg={3}>
<Card className="h-100">
<Card.Body>
<Card.Title>Total Users</Card.Title>
<Card.Text className="display-6">1,234</Card.Text>
</Card.Body>
</Card>
</Col>
<Col xs={12} md={6} lg={3}>
<Card className="h-100">
<Card.Body>
<Card.Title>Revenue</Card.Title>
<Card.Text className="display-6">$45,678</Card.Text>
</Card.Body>
</Card>
</Col>
<Col xs={12} lg={6}>
<Card className="h-100">
<Card.Body>
<Card.Title>Recent Activity</Card.Title>
<Card.Text>Lista de atividades...</Card.Text>
</Card.Body>
</Card>
</Col>
</Row>
</Container>
)
}

Componentes com React Bootstrap

1. Navegação:

// src/components/Navigation.tsx
import { Navbar, Nav, Container, NavDropdown } from 'react-bootstrap'
import { LinkContainer } from 'react-router-bootstrap'
export const Navigation = () => {
return (
<Navbar bg="primary" variant="dark" expand="lg" sticky="top">
<Container>
<LinkContainer to="/">
<Navbar.Brand>IngenioLab</Navbar.Brand>
</LinkContainer>
<Navbar.Toggle aria-controls="basic-navbar-nav" />
<Navbar.Collapse id="basic-navbar-nav">
<Nav className="me-auto">
<LinkContainer to="/">
<Nav.Link>Home</Nav.Link>
</LinkContainer>
<LinkContainer to="/users">
<Nav.Link>Users</Nav.Link>
</LinkContainer>
<NavDropdown title="Dashboard" id="basic-nav-dropdown">
<LinkContainer to="/dashboard/analytics">
<NavDropdown.Item>Analytics</NavDropdown.Item>
</LinkContainer>
<LinkContainer to="/dashboard/reports">
<NavDropdown.Item>Reports</NavDropdown.Item>
</LinkContainer>
<NavDropdown.Divider />
<LinkContainer to="/dashboard/settings">
<NavDropdown.Item>Settings</NavDropdown.Item>
</LinkContainer>
</NavDropdown>
</Nav>
<Nav>
<Nav.Link href="/profile">Profile</Nav.Link>
<Nav.Link href="/logout">Logout</Nav.Link>
</Nav>
</Navbar.Collapse>
</Container>
</Navbar>
)
}

2. Formulários:

// src/components/UserForm.tsx
import { useState } from 'react'
import { Form, Button, Alert, Row, Col, Card } from 'react-bootstrap'
interface UserFormData {
name: string
email: string
role: string
isActive: boolean
}
export const UserForm = () => {
const [formData, setFormData] = useState<UserFormData>({
name: '',
email: '',
role: 'user',
isActive: true
})
const [validated, setValidated] = useState(false)
const [error, setError] = useState<string | null>(null)
const [success, setSuccess] = useState(false)
const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
const form = event.currentTarget
event.preventDefault()
event.stopPropagation()
if (form.checkValidity()) {
// Processar formulário
console.log('Form data:', formData)
setSuccess(true)
setError(null)
}
setValidated(true)
}
return (
<Card>
<Card.Header>
<h4>Criar Usuário</h4>
</Card.Header>
<Card.Body>
{error && <Alert variant="danger">{error}</Alert>}
{success && <Alert variant="success">Usuário criado com sucesso!</Alert>}
<Form noValidate validated={validated} onSubmit={handleSubmit}>
<Row>
<Col md={6}>
<Form.Group className="mb-3">
<Form.Label>Nome *</Form.Label>
<Form.Control
required
type="text"
value={formData.name}
onChange={(e) => setFormData(prev => ({ ...prev, name: e.target.value }))}
placeholder="Nome completo"
/>
<Form.Control.Feedback type="invalid">
Por favor, insira um nome válido.
</Form.Control.Feedback>
</Form.Group>
</Col>
<Col md={6}>
<Form.Group className="mb-3">
<Form.Label>Email *</Form.Label>
<Form.Control
required
type="email"
value={formData.email}
onChange={(e) => setFormData(prev => ({ ...prev, email: e.target.value }))}
placeholder="usuario@exemplo.com"
/>
<Form.Control.Feedback type="invalid">
Por favor, insira um email válido.
</Form.Control.Feedback>
</Form.Group>
</Col>
</Row>
<Row>
<Col md={6}>
<Form.Group className="mb-3">
<Form.Label>Role</Form.Label>
<Form.Select
value={formData.role}
onChange={(e) => setFormData(prev => ({ ...prev, role: e.target.value }))}
>
<option value="user">User</option>
<option value="admin">Admin</option>
<option value="moderator">Moderator</option>
</Form.Select>
</Form.Group>
</Col>
<Col md={6}>
<Form.Group className="mb-3 pt-4">
<Form.Check
type="checkbox"
id="isActive"
label="Usuário ativo"
checked={formData.isActive}
onChange={(e) => setFormData(prev => ({ ...prev, isActive: e.target.checked }))}
/>
</Form.Group>
</Col>
</Row>
<div className="d-grid gap-2 d-md-flex justify-content-md-end">
<Button variant="secondary" type="button">
Cancelar
</Button>
<Button variant="primary" type="submit">
Criar Usuário
</Button>
</div>
</Form>
</Card.Body>
</Card>
)
}

3. Tabelas e Listas:

// src/components/UserTable.tsx
import { Table, Badge, Button, ButtonGroup } from 'react-bootstrap'
interface User {
id: string
name: string
email: string
role: string
isActive: boolean
}
interface UserTableProps {
users: User[]
onEdit: (user: User) => void
onDelete: (userId: string) => void
}
export const UserTable = ({ users, onEdit, onDelete }: UserTableProps) => {
const getRoleBadgeVariant = (role: string) => {
switch (role) {
case 'admin': return 'danger'
case 'moderator': return 'warning'
default: return 'primary'
}
}
return (
<Table responsive striped hover>
<thead>
<tr>
<th>Nome</th>
<th>Email</th>
<th>Role</th>
<th>Status</th>
<th width="150">Ações</th>
</tr>
</thead>
<tbody>
{users.map(user => (
<tr key={user.id}>
<td>{user.name}</td>
<td>{user.email}</td>
<td>
<Badge bg={getRoleBadgeVariant(user.role)}>
{user.role}
</Badge>
</td>
<td>
<Badge bg={user.isActive ? 'success' : 'secondary'}>
{user.isActive ? 'Ativo' : 'Inativo'}
</Badge>
</td>
<td>
<ButtonGroup size="sm">
<Button
variant="outline-primary"
onClick={() => onEdit(user)}
>
Edit
</Button>
<Button
variant="outline-danger"
onClick={() => onDelete(user.id)}
>
Delete
</Button>
</ButtonGroup>
</td>
</tr>
))}
</tbody>
</Table>
)
}

4. Modais:

// src/components/DeleteConfirmModal.tsx
import { Modal, Button } from 'react-bootstrap'
interface DeleteConfirmModalProps {
show: boolean
onHide: () => void
onConfirm: () => void
title: string
message: string
loading?: boolean
}
export const DeleteConfirmModal = ({
show,
onHide,
onConfirm,
title,
message,
loading = false
}: DeleteConfirmModalProps) => {
return (
<Modal show={show} onHide={onHide} centered>
<Modal.Header closeButton>
<Modal.Title>{title}</Modal.Title>
</Modal.Header>
<Modal.Body>
<p>{message}</p>
</Modal.Body>
<Modal.Footer>
<Button variant="secondary" onClick={onHide} disabled={loading}>
Cancelar
</Button>
<Button variant="danger" onClick={onConfirm} disabled={loading}>
{loading ? 'Deletando...' : 'Confirmar'}
</Button>
</Modal.Footer>
</Modal>
)
}

Utilitários CSS

1. Spacing e Layout:

// src/components/UtilityExample.tsx
export const UtilityExample = () => {
return (
<div className="container-fluid">
{/* Margin e Padding */}
<div className="p-4 m-2 bg-light">
<h3 className="mb-3">Título com margin bottom</h3>
<p className="px-2 py-1">Parágrafo com padding horizontal</p>
</div>
{/* Flexbox */}
<div className="d-flex justify-content-between align-items-center p-3 bg-primary text-white">
<span>Left</span>
<span>Center</span>
<span>Right</span>
</div>
{/* Text utilities */}
<div className="text-center p-3">
<h4 className="text-primary">Título Azul</h4>
<p className="text-muted fw-light">Texto secundário</p>
<small className="text-uppercase fw-bold">Texto em maiúscula</small>
</div>
{/* Display utilities */}
<div className="d-none d-md-block">Visível apenas em desktop</div>
<div className="d-block d-md-none">Visível apenas em mobile</div>
</div>
)
}

2. Responsividade:

// src/styles/responsive.scss
.ingeniolab-hero {
padding: 2rem 1rem;
@include media-breakpoint-up(md) {
padding: 4rem 2rem;
}
@include media-breakpoint-up(lg) {
padding: 6rem 3rem;
}
}
.ingeniolab-card {
margin-bottom: 1rem;
@include media-breakpoint-up(lg) {
margin-bottom: 2rem;
}
}

Temas e Customização

1. Dark Theme:

// src/styles/themes.scss
[data-bs-theme="dark"] {
.ingeniolab-card {
background: var(--bs-dark);
border-color: var(--bs-gray-700);
}
.ingeniolab-sidebar {
background: var(--bs-gray-900);
}
}
// Componente React
export const ThemeToggle = () => {
const [theme, setTheme] = useState('light')
useEffect(() => {
document.documentElement.setAttribute('data-bs-theme', theme)
}, [theme])
return (
<Button
variant={theme === 'light' ? 'dark' : 'light'}
onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}
>
{theme === 'light' ? '🌙' : '☀️'}
</Button>
)
}

2. Variáveis customizadas:

// src/styles/variables.scss
:root {
--ingeniolab-primary: #007bff;
--ingeniolab-secondary: #6c757d;
--ingeniolab-success: #28a745;
--ingeniolab-border-radius: 8px;
--ingeniolab-box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
.ingeniolab-btn {
border-radius: var(--ingeniolab-border-radius);
box-shadow: var(--ingeniolab-box-shadow);
}

Performance

1. Import seletivo:

// src/main.tsx - Import apenas componentes necessários
import 'bootstrap/scss/functions';
import 'bootstrap/scss/variables';
import 'bootstrap/scss/mixins';
import 'bootstrap/scss/bootstrap-grid';
import 'bootstrap/scss/bootstrap-utilities';
// Para React Bootstrap
import { Button } from 'react-bootstrap/Button'
import { Card } from 'react-bootstrap/Card'
// Ao invés de: import { Button, Card } from 'react-bootstrap'

2. Bundle optimization:

// vite.config.ts
export default defineConfig({
build: {
rollupOptions: {
output: {
manualChunks: {
bootstrap: ['bootstrap', 'react-bootstrap']
}
}
}
}
})

Acessibilidade

1. ARIA e semântica:

// src/components/AccessibleComponents.tsx
import { Button, Alert, Modal } from 'react-bootstrap'
export const AccessibleForm = () => {
return (
<form role="form" aria-label="User registration form">
<div className="mb-3">
<label htmlFor="username" className="form-label">
Username
</label>
<input
id="username"
type="text"
className="form-control"
aria-describedby="username-help"
required
/>
<div id="username-help" className="form-text">
Choose a unique username
</div>
</div>
<Button type="submit" aria-label="Submit registration form">
Register
</Button>
</form>
)
}

Integração com Formulários

1. Com React Hook Form:

// src/components/HookFormExample.tsx
import { useForm } from 'react-hook-form'
import { Form, Button, Alert } from 'react-bootstrap'
interface FormData {
name: string
email: string
}
export const HookFormExample = () => {
const { register, handleSubmit, formState: { errors } } = useForm<FormData>()
const onSubmit = (data: FormData) => {
console.log(data)
}
return (
<Form onSubmit={handleSubmit(onSubmit)}>
<Form.Group className="mb-3">
<Form.Label>Name</Form.Label>
<Form.Control
type="text"
{...register('name', { required: 'Name is required' })}
isInvalid={!!errors.name}
/>
<Form.Control.Feedback type="invalid">
{errors.name?.message}
</Form.Control.Feedback>
</Form.Group>
<Form.Group className="mb-3">
<Form.Label>Email</Form.Label>
<Form.Control
type="email"
{...register('email', { required: 'Email is required' })}
isInvalid={!!errors.email}
/>
<Form.Control.Feedback type="invalid">
{errors.email?.message}
</Form.Control.Feedback>
</Form.Group>
<Button type="submit">Submit</Button>
</Form>
)
}