### Fase 19 — Emoji avatars expansion (food + animal categories) **v1.0 · 3/4.** Tercera fase del ciclo v1.0. **Estado: 📋 Planificada.** Petición concreta: el avatar picker actual (`/settings`, también en colective-onboarding y `CreateCollectiveModal`) ofrece 8 emojis hardcoded. Ampliar con **todos los emojis oficiales** de food + animals según Unicode CLDR. **Objetivo: agrupar emojis en categorías navegables (no scroll infinito flat), añadir food + animals completos (~270 glyphs combinados según Unicode 15), mantener performance del picker.** **Decisiones clave:** - Fuente de verdad: lista hardcoded en `apps/web/src/lib/data/emoji-catalog.ts`, no fetched. Generada una sola vez desde Unicode CLDR; commit del JSON. Tamaño ~5–10KB. - Categorías visibles: **Caras** (mantiene los 8 actuales + ~30 más comunes), **Comida** (todos los food/drink), **Animales** (todos los animal/nature), **Objetos** (un pequeño set útil). El picker abre por defecto en "Caras"; pestañas en la cabecera. - Sin emoji search en MVP de la fase — solo navegación por pestaña. Search si crece. - Variantes de skin tone parked. --- #### 19.1 Catálogo **Fichero:** nuevo `apps/web/src/lib/data/emoji-catalog.ts`. ```ts export type EmojiCategory = 'faces' | 'food' | 'animals' | 'objects'; export const EMOJI_CATALOG: Record = { faces: ['😀', '😎', '🥳', '🤓', '👻', '🤖', '👽', '🥸', /* ~30 más */], food: ['🍎', '🍐', '🍊', '🍋', '🍌', '🍉', '🍇', '🍓', /* ~85 más, todo Unicode "Food & Drink" */], animals: ['🐶', '🐱', '🐭', '🐹', '🐰', '🦊', '🐻', '🐼', /* ~150 más, todo "Animals & Nature" */], objects: ['🏠', '🏡', '🌳', '🌻', '⭐', '🔥', '🎁', '🎈', /* ~20 más */], } as const; export const CATEGORY_LABELS: Record = { faces: 'Caras', food: 'Comida', animals: 'Animales', objects: 'Objetos', }; ``` **Tareas:** - [ ] **19.1.1** Generar los arrays. Fuente recomendada: [unicode-emoji-json](https://github.com/muan/unicode-emoji-json) `data-by-group.json`. Copiar el bloque "Food & Drink" + "Animals & Nature". Filtrar variantes (eliminar items con ZWJ secuencias raras como `🐕‍🦺` si Vite no las renderiza consistente). Mantener Unicode 15 baseline (sin Unicode 16 que iOS aún no incluye). - [ ] **19.1.2** Tamaño final esperado: ~280 strings, ~10KB del bundle. Verificar. - [ ] **19.1.3** Generador deterministic — un script `scripts/build-emoji-catalog.mjs` ejecutable manualmente que rebuild el `.ts` desde una copia local del JSON. No corre en CI; solo cuando Unicode se actualiza. --- #### 19.2 Componente EmojiPicker **Fichero:** nuevo `apps/web/src/lib/components/EmojiPicker.svelte`. - [ ] **19.2.1** Props: `selected: string | null`, `onSelect: (emoji: string) => void`, `onClose?: () => void`. - [ ] **19.2.2** Layout: header con pestañas (4 botones), body grid `grid-cols-8` (responsive `sm:grid-cols-10`), cada celda 32×32 con `text-2xl`. Click selecciona + dispara `onSelect`. - [ ] **19.2.3** Categoría seleccionada en `$state`, default `'faces'`. Switch instantáneo (no animación). - [ ] **19.2.4** Lazy render: solo monta el grid de la categoría activa (no las 4 al mismo tiempo) — evita coste de ~280 nodos vacíos. - [ ] **19.2.5** A11y: pestañas son `role="tab"`, grid `role="grid"`, cada celda `role="gridcell"` + aria-label con el codepoint name. Keyboard nav (arrow keys) entre celdas. --- #### 19.3 Integraciones **Ficheros:** - `apps/web/src/routes/(app)/settings/+page.svelte` (avatar del usuario). - `apps/web/src/routes/(app)/onboarding/+page.svelte` (emoji inicial del colectivo). - `apps/web/src/lib/components/CreateCollectiveModal.svelte` (emoji al crear). - `apps/web/src/routes/(app)/collective/manage/+page.svelte` (emoji al renombrar — Fase 10.2). - [ ] **19.3.1** Reemplazar los selectores hardcoded de emoji por ``. Mantener el mismo padding/border del contenedor para no romper layouts. - [ ] **19.3.2** Verificar que `users.avatar_emoji` y `collectives.avatar_emoji` siguen aceptando los nuevos glyphs (text column — sí). --- #### 19.4 Tests - [ ] **19.4.1** Vitest unit `apps/web/src/lib/components/EmojiPicker.test.ts`: - EP-U-01 4 pestañas presentes. - EP-U-02 default category = 'faces'. - EP-U-03 click en una celda dispara `onSelect` con el emoji correcto. - EP-U-04 cambiar pestaña re-renderiza el grid con la categoría correcta. - EP-U-05 arrow keys mueven foco entre celdas. - [ ] **19.4.2** Vitest unit `emoji-catalog.test.ts`: - EC-U-01 `EMOJI_CATALOG.food.length >= 85`. - EC-U-02 `EMOJI_CATALOG.animals.length >= 130`. - EC-U-03 cada categoría no contiene duplicados. - EC-U-04 no overlap entre categorías (un emoji solo en una). - [ ] **19.4.3** Playwright `tests/e2e/emoji-picker.test.ts`: - **EP-E-01** Ana en `/settings`, abre el picker, cambia a "Comida", selecciona 🍎, guarda → `users.avatar_emoji` actualizado, avatar renderiza 🍎. - **EP-E-02** Onboarding: Eva (nuevo usuario) crea colectivo con emoji 🐱 desde "Animales". --- #### 19.Z Verificación + cierre - [ ] **19.Z.1** `just test-all` verde. Suma esperada: +9 unit + 2 e2e = +11. - [ ] **19.Z.2** Smoke en iOS Safari + Android Chrome — verificar que TODOS los emojis del catálogo renderizan (no tofu □). Si algunos no, eliminar de la lista. - [ ] **19.Z.3** Bundle-size check: bundle gz total no debe subir más de ~10KB respecto al baseline. - [ ] **19.Z.4** Entrada en CHANGELOG: `[new] Emoji picker expanded with full food + animals categories (Fase 19).` - [ ] **19.Z.5** Documentar en `docs/history/fase-19-emoji-avatars.md`. --- ### Riesgos / notas - **iOS vs Android emoji rendering**: algunos Unicode 15 codepoints (🩷, 🩵, 🪿) aún no están en iOS 16 (sí en 17.4+). Filtrar a Unicode 14 si la base de usuarios incluye iOS < 17. Documentar la decisión. - **CLDR shortName as aria-label**: para a11y, necesitamos el nombre del emoji ("red apple"). Si añadir el JSON completo de nombres infla el bundle, fallback a `aria-label={emoji}` (lee como "ÄPFel" en VoiceOver — feo pero funcional). - **ZWJ sequences**: 👨‍👩‍👧 (familia) o 🏳️‍🌈 (rainbow flag) son secuencias múltiples. Los excluimos del catálogo en MVP de la fase (variantes de skin tone tampoco). - **Performance del grid grande**: ~280 nodos en el DOM si renderizamos todas las pestañas. Por eso lazy-render por pestaña activa. ### Scope explícito fuera - Buscar emoji por nombre. - Skin tone variants. - Recently-used (tracking de los últimos emojis del usuario). - Custom emojis subidos por el colectivo. - Sincronización del emoji con el sistema operativo del usuario.