2ff06ee7ae
Major Features:
- Internationalization (i18n) with auto-detection for ES/EN/PT
- Mobile-first responsive design for Studio and Experience
- Multi-model AI configuration (llama3.2:3b, qwen3.5:9b, gpt-oss:latest)
- Course language configuration (auto-detect or fixed per course)
Backend Changes:
- shared/common: ModelType enum for intelligent model selection
- LMS: log_ai_usage function migration (fix chat tutor 500 error)
- LMS/CMS: course language config fields (language_setting, fixed_language)
- LMS: /courses/{id}/language-config endpoint for language detection
Frontend Changes:
- Experience: Enhanced i18n with browser language detection
- Experience: Audio recording with HTTPS check and error handling
- Studio: Memory game with unique pair IDs and debug logging
- Studio: Expanded translations (250+ keys for ES, EN, PT)
- Both: Language selector in headers (mobile responsive)
Documentation:
- AI_MODELS_CONFIG.md: Multi-model configuration guide
- RESPONSIVIDAD_GUIA.md: Mobile-first design patterns
- I18N_RESPONSIVIDAD_IMPLEMENTACION.md: Implementation details
- DEBUG_AUDIO_RECORDING.md: Audio troubleshooting guide
- DEBUG_MEMORY_GAME.md: Memory game debugging steps
Bug Fixes:
- Fix chat tutor 500 error (missing log_ai_usage function)
- Fix audio recording (HTTPS check, browser compatibility)
- Fix memory game pair IDs (unique ID generation)
- Fix HotspotBlock TypeScript errors
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
337 lines
6.2 KiB
Markdown
337 lines
6.2 KiB
Markdown
# Guía de Responsividad - OpenCCB
|
|
|
|
## Principios Mobile-First
|
|
|
|
### Breakpoints (Tailwind CSS)
|
|
|
|
```css
|
|
/* Mobile: Default (0-639px) */
|
|
/* sm: 640px+ */
|
|
/* md: 768px+ */
|
|
/* lg: 1024px+ */
|
|
/* xl: 1280px+ */
|
|
/* 2xl: 1536px+ */
|
|
```
|
|
|
|
### Jerarquía de Diseño
|
|
|
|
1. **Mobile (< 640px)**: Diseño de una sola columna, menú hamburguesa
|
|
2. **Tablet (640px - 1024px)**: 2 columnas, navegación visible
|
|
3. **Desktop (1024px+)**: Layout completo, todas las características
|
|
|
|
---
|
|
|
|
## Componentes Responsivos
|
|
|
|
### Layout Principal
|
|
|
|
```tsx
|
|
// Mobile-first container
|
|
<div className="min-h-screen flex flex-col">
|
|
{/* Header responsivo */}
|
|
<header className="h-16 px-4 md:px-6">
|
|
{/* Logo y navegación */}
|
|
</header>
|
|
|
|
{/* Contenido principal */}
|
|
<main className="flex-1 px-4 md:px-6 py-4 md:py-8">
|
|
{children}
|
|
</main>
|
|
</div>
|
|
```
|
|
|
|
### Navegación
|
|
|
|
**Mobile:**
|
|
- Menú hamburguesa (ícono)
|
|
- Sidebar deslizante desde la derecha
|
|
- Overlay con backdrop blur
|
|
- Items de navegación en columna
|
|
|
|
**Desktop:**
|
|
- Navegación horizontal visible
|
|
- Dropdowns al hacer hover/click
|
|
- Items en fila con espaciado
|
|
|
|
### Tarjetas de Cursos
|
|
|
|
```tsx
|
|
// Grid responsivo
|
|
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4 md:gap-6">
|
|
{courses.map(course => (
|
|
<CourseCard key={course.id} course={course} />
|
|
))}
|
|
</div>
|
|
```
|
|
|
|
### Tablas de Datos
|
|
|
|
**Mobile:**
|
|
- Scroll horizontal
|
|
- O tarjetas apiladas en lugar de tabla
|
|
|
|
**Desktop:**
|
|
- Tabla completa visible
|
|
|
|
```tsx
|
|
<div className="overflow-x-auto">
|
|
<table className="min-w-full">
|
|
{/* tabla */}
|
|
</table>
|
|
</div>
|
|
```
|
|
|
|
---
|
|
|
|
## Tipografía Fluida
|
|
|
|
```tsx
|
|
// Títulos responsivos
|
|
<h1 className="text-2xl sm:text-3xl md:text-4xl lg:text-5xl font-bold">
|
|
Título
|
|
</h1>
|
|
|
|
// Texto de párrafo
|
|
<p className="text-sm md:text-base lg:text-lg">
|
|
Contenido
|
|
</p>
|
|
|
|
// Texto pequeño
|
|
<span className="text-xs md:text-sm">
|
|
Metadata
|
|
</span>
|
|
```
|
|
|
|
---
|
|
|
|
## Espaciado Responsivo
|
|
|
|
```tsx
|
|
// Padding responsivo
|
|
<div className="px-4 md:px-6 lg:px-8 py-4 md:py-6 lg:py-8">
|
|
{/* contenido */}
|
|
</div>
|
|
|
|
// Gap responsivo
|
|
<div className="flex flex-col md:flex-row gap-4 md:gap-6 lg:gap-8">
|
|
{/* items */}
|
|
</div>
|
|
```
|
|
|
|
---
|
|
|
|
## Componentes Específicos
|
|
|
|
### AppHeader (Experience)
|
|
|
|
**Mobile (< 768px):**
|
|
- Logo compacto
|
|
- Íconos de notificación y tema
|
|
- Botón de menú hamburguesa
|
|
- Sidebar deslizante con:
|
|
- Navegación completa
|
|
- Selector de idioma
|
|
- Toggle de tema
|
|
- Perfil de usuario
|
|
- Botón de logout
|
|
|
|
**Desktop (≥ 768px):**
|
|
- Logo completo
|
|
- Navegación horizontal visible
|
|
- Íconos de notificación, idioma, tema
|
|
- Perfil de usuario visible
|
|
- Botón de logout
|
|
|
|
### Navbar (Studio)
|
|
|
|
**Mobile (< 768px):**
|
|
- Logo compacto
|
|
- Dropdowns colapsados
|
|
- Toggle de tema
|
|
- Selector de idioma
|
|
- Botón de menú hamburguesa
|
|
- Sidebar deslizante
|
|
|
|
**Desktop (≥ 768px):**
|
|
- Logo completo
|
|
- Dropdowns visibles
|
|
- Toggle de tema
|
|
- Selector de idioma
|
|
- Información de usuario visible
|
|
|
|
### Reproductor de Lecciones
|
|
|
|
**Mobile:**
|
|
- Video en ancho completo
|
|
- Controles simplificados
|
|
- Pestañas colapsadas (acordeón)
|
|
- Navegación entre lecciones (botones)
|
|
|
|
**Desktop:**
|
|
- Video con tamaño fijo
|
|
- Sidebar con navegación de lecciones
|
|
- Pestañas visibles
|
|
- Panel de transcripción/resumen
|
|
|
|
---
|
|
|
|
## Imágenes y Multimedia
|
|
|
|
```tsx
|
|
// Imágenes responsivas
|
|
<Image
|
|
src={src}
|
|
alt={alt}
|
|
fill
|
|
sizes="(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 25vw"
|
|
className="object-cover"
|
|
/>
|
|
|
|
// Video responsivo
|
|
<div className="aspect-video w-full">
|
|
<video controls className="w-full h-full">
|
|
{/* sources */}
|
|
</video>
|
|
</div>
|
|
```
|
|
|
|
---
|
|
|
|
## Accesibilidad
|
|
|
|
### Navegación por Teclado
|
|
|
|
- Todos los elementos interactivos deben ser focusables
|
|
- Orden de tab lógico
|
|
- Indicadores de focus visibles
|
|
|
|
### Screen Readers
|
|
|
|
- Labels descriptivos en botones e íconos
|
|
- `aria-label` en íconos sin texto
|
|
- `aria-expanded` en elementos colapsables
|
|
- `aria-modal` en diálogos
|
|
|
|
### Contraste
|
|
|
|
- Relación de contraste mínima 4.5:1
|
|
- Texto grande: 3:1 mínimo
|
|
|
|
---
|
|
|
|
## Pruebas de Responsividad
|
|
|
|
### Dispositivos de Prueba
|
|
|
|
**Chrome DevTools:**
|
|
- iPhone SE (375x667)
|
|
- iPhone 12 Pro (390x844)
|
|
- iPad Air (820x1180)
|
|
- iPad Pro (1024x1366)
|
|
- Desktop (1920x1080)
|
|
|
|
**Herramientas:**
|
|
- Chrome DevTools Device Mode
|
|
- Firefox Responsive Design Mode
|
|
- BrowserStack (dispositivos reales)
|
|
|
|
### Checklist de Pruebas
|
|
|
|
- [ ] Navegación funciona en mobile
|
|
- [ ] Menús desplegables accesibles
|
|
- [ ] Formularios usables en pantallas pequeñas
|
|
- [ ] Tablas con scroll horizontal o versión mobile
|
|
- [ ] Imágenes se escalan correctamente
|
|
- [ ] Texto legible sin zoom
|
|
- [ ] Botones táctiles (mínimo 44x44px)
|
|
- [ ] Sin overflow horizontal no intencional
|
|
- [ ] Layout no se rompe en tamaños extremos
|
|
|
|
---
|
|
|
|
## Patrones Comunes
|
|
|
|
### Mobile: Navegación Inferior
|
|
|
|
```tsx
|
|
<nav className="fixed bottom-0 left-0 right-0 bg-white dark:bg-gray-900 border-t md:hidden">
|
|
<div className="flex justify-around items-center h-16">
|
|
{/* íconos de navegación */}
|
|
</div>
|
|
</nav>
|
|
```
|
|
|
|
### Desktop: Sidebar Fija
|
|
|
|
```tsx
|
|
<aside className="hidden md:block w-64 fixed left-0 top-16 bottom-0 overflow-y-auto">
|
|
{/* navegación lateral */}
|
|
</aside>
|
|
```
|
|
|
|
### Tablas Responsivas
|
|
|
|
```tsx
|
|
// Opción 1: Scroll horizontal
|
|
<div className="overflow-x-auto">
|
|
<table className="min-w-full divide-y divide-gray-200">
|
|
{/* tabla completa */}
|
|
</table>
|
|
</div>
|
|
|
|
// Opción 2: Tarjetas en mobile
|
|
<div className="block md:hidden">
|
|
{items.map(item => (
|
|
<Card key={item.id} item={item} />
|
|
))}
|
|
</div>
|
|
<div className="hidden md:block">
|
|
<table>
|
|
{/* tabla completa */}
|
|
</table>
|
|
</div>
|
|
```
|
|
|
|
---
|
|
|
|
## Rendimiento
|
|
|
|
### Lazy Loading
|
|
|
|
```tsx
|
|
// Imágenes
|
|
<Image
|
|
src={src}
|
|
alt={alt}
|
|
loading="lazy"
|
|
width={400}
|
|
height={300}
|
|
/>
|
|
|
|
// Componentes pesados
|
|
const HeavyComponent = dynamic(() => import('./HeavyComponent'), {
|
|
loading: () => <p>Cargando...</p>,
|
|
ssr: false,
|
|
})
|
|
```
|
|
|
|
### Code Splitting
|
|
|
|
```tsx
|
|
// Carga diferida por ruta
|
|
const CourseDetail = dynamic(() => import('@/components/CourseDetail'))
|
|
```
|
|
|
|
---
|
|
|
|
## Referencias
|
|
|
|
- [Tailwind CSS Responsive Design](https://tailwindcss.com/docs/responsive-design)
|
|
- [MDN Responsive Design](https://developer.mozilla.org/en-US/docs/Learn/CSS/CSS_layout/Responsive_Design)
|
|
- [Web.dev Responsive Design](https://web.dev/responsive-web-design-basics/)
|
|
|
|
---
|
|
|
|
**Última actualización**: 2026-03-20
|
|
**Versión**: 1.0
|