OKroot | Come sano, vive libre
Ecosistema de OKroot | Come sano, vive libre, para personas con celiaquía, diabetes o intolerancia a la lactosa. Reúne la app con escáner de etiquetas por IA y modo sin conexión, la página de lista de espera con recetario y la documentación oficial del producto.
Por Valentina Ramírez
Publicado: 16 de junio de 2026 · Actualizado: 5 de octubre de 2026
- Problema
- Comer con celiaquía, diabetes o intolerancia a la lactosa obliga a leer cada etiqueta y cruzar varias restricciones a mano.
- Solución
- El usuario fotografía la etiqueta y recibe en segundos un veredicto con la razón, según su perfil.
- Resultado
- En validación con clientes reales. Núcleo de la app en producción, en early access y con lista de espera abierta.
01 App
PWA con scanner de etiquetas por IA y cola de sincronización offline. Ver detalleOcultar
- React
- TypeScript
- Vite
- Tailwind CSS
- Dexie
- Django
- DRF
- PostgreSQL
- Claude API
- PWA
El problema
Comer con celiaquía, diabetes o intolerancia a la lactosa implica leer cada etiqueta, descifrar ingredientes escondidos bajo otros nombres y cruzar varias restricciones a la vez, todo a mano. Las apps que existen suelen validar una condición y dependen de una base de datos fija de productos.
Cada etiqueta, a mano
Comprar o comer fuera implica leer el reverso de cada producto, una y otra vez.
Ingredientes con disfraz
La lactosa, el gluten o el azúcar aparecen bajo nombres técnicos que pocos reconocen.
Restricciones que se acumulan
Las soluciones existentes validan una condición a la vez y dependen de una lista fija de productos.
Resultados
- Scanner en producción: foto de la etiqueta, veredicto con la razón concreta y soporte para las tres condiciones a la vez.
- Núcleo completo en producción: perfil, scanner, diario con bitácora de salud, recetas, insights y mercado semanal sobre Django, PostgreSQL y React.
- Cola de sync offline operativa para escaneos, comidas y lista de compras, con envío por lotes idempotente cuando vuelve la red.
Ver detalle técnico AbrirOcultar detalle técnico
Qué construí
- 01
Frontend en React y TypeScript estricto, con Vite, Tailwind CSS, React Router y rutas por feature cargadas bajo demanda. Zustand guarda la sesión, TanStack Query maneja los datos del servidor, y react-hook-form con zod valida los formularios. Una capa de fetch propia, sin Axios.
- 02
Backend en Django y Django REST Framework sobre PostgreSQL, organizado por dominios (usuarios, scanner, diario, recetas, insights, mercado, pagos y sync). La lógica vive en services y las vistas solo orquestan. Todas las respuestas usan el mismo envoltorio de datos y de error.
- 03
Scanner en el servidor: el cliente y el servidor validan la imagen (tipo, tamaño y resolución mínima). El servidor llama a Claude con un prompt que conoce las condiciones del usuario y sus ingredientes a evitar, y parsea una respuesta por líneas a un veredicto, riesgo de trazas, ingredientes problemáticos y macros.
- 04
La imagen no se guarda: va al modelo y se descarta. Si la etiqueta es ilegible, el servidor reemplaza el texto del modelo por una indicación concreta (borrosa, poca luz, reflejo, texto cortado, sin lista de ingredientes) en lugar de adivinar.
- 05
Perfil de salud como estado persistente: tres condiciones combinables, objetivo, datos físicos con cálculo de gasto calórico (Mifflin-St Jeor) e ingredientes a evitar. Un ingrediente marcado por el usuario fuerza el veredicto de rechazo en código, sin depender de que el modelo lo recuerde.
- 06
Diario con comidas y macros, resumen semanal y bitácora de salud (energía, piel y glucosa). Insights con alertas, racha, vaso de agua y una estimación glucémica simple para diabéticos. Mercado semanal armado desde las recetas favoritas y lo registrado en el diario.
- 07
Offline: el service worker (Workbox) precachea la app y las consultas a la API usan primero la red con respaldo en caché. Los escaneos, las comidas y los cambios de la lista de compras sin conexión quedan en una cola de IndexedDB (Dexie) y se envían por lotes cuando vuelve la red.
- 08
Sync con idempotencia: cada operación lleva una clave UUID generada en el cliente y el servidor devuelve la entidad existente si la reconoce, así un reintento nunca duplica. El lote se envía en orden, con reintentos espaciados y el servidor como fuente de verdad.
- 09
Cuota de escaneos reservada de forma atómica en la base antes de llamar al modelo y devuelta si la llamada falla, para que peticiones simultáneas no superen el tope. Suscripciones con checkout alojado de Stripe y MercadoPago y webhooks con firma verificada e idempotentes.
- 10
Seguridad: autenticación con tokens de corta duración y rotación, inicio de sesión con Google verificado en el servidor, protección contra intentos repetidos y cabeceras de seguridad.
- 11
Calidad y entrega: linter, comprobación de migraciones, más de 500 pruebas de backend y más de 250 de frontend, y escaneo de secretos y de vulnerabilidades en cada entrega. Despliegue reproducible.
Scanner de etiquetas
Foto de la etiqueta y veredicto en cuatro niveles: apto, cuidado, no apto y sin datos. Muestra el ingrediente que lo decide, el riesgo de contaminación cruzada, una fila por condición activa y los macros leídos de la tabla nutricional.
Perfil e ingredientes a evitar
Onboarding en cuatro pasos con las condiciones (combinables), el objetivo, los datos físicos y los ingredientes que el usuario no quiere ver. Se edita después desde el perfil.
Diario y bitácora de salud
Comidas por momento del día con calorías y macros, resumen semanal y registro diario de energía, piel y glucosa. Registrar comidas funciona sin conexión.
Recetas
50 recetas curadas, todas sin gluten, sin lactosa y sin azúcar. Se filtran por momento, tiempo, dificultad y favoritas, y las que contienen un ingrediente a evitar quedan fuera. Se pueden agregar al diario.
Insights y mercado semanal
Resumen semanal con alertas, racha de registro y vasos de agua. Un mercado semanal se arma con los ingredientes de las recetas favoritas y lo que se registró en el diario.
Una foto, una respuesta. Esto pasa por debajo.
- 01 La app valida la foto: tipo, tamaño y resolución mínima
- 02 El servidor la revisa de nuevo y reserva un escaneo de la cuota antes de llamar al modelo
- 03 Claude lee los ingredientes con tus condiciones y tus ingredientes a evitar en el prompt
- 04 El servidor parsea la respuesta, aplica tus ingredientes prohibidos como regla dura y guarda el resultado
- 05 Respuesta en pantalla: puedes comerlo, o el ingrediente exacto que lo descarta
Diseño
- 01
El diseño parte de un prototipo navegable, accesible desde el botón Ver diseño.
- 02
Sistema de diseño propio, versión 2.0, con tokens en un solo archivo CSS. Violeta como color primario sobre neutros cálidos y colores propios para los veredictos apto, cuidado y no apto.
- 03
Bricolage Grotesque para titulares y Plus Jakarta Sans para texto, ambas autoalojadas. Íconos de Lucide con trazo uniforme.
- 04
El veredicto no depende solo del color: lleva una marca (visto, exclamación o equis) y una palabra grande, y se anuncia a lectores de pantalla con regiones en vivo.
- 05
Pensada para el teléfono: la interfaz vive dentro de un marco móvil con barra de navegación inferior de cuatro pestañas más una quinta de plan o de mercado.
- 06
Animación reducida a nada cuando el usuario lo pide, objetivos táctiles de 44 px en varios controles y anillos de foco visibles.
- 07
Todo el texto está en español de Colombia y en tuteo. Hoy la interfaz es solo clara, sin modo oscuro.
Decisiones de arquitectura
01 Nicho médico real, no wellness genérico VerCerrar
- Contexto
- Existen muchas apps de comer sano. Las personas con celiaquía, diabetes o intolerancia tienen necesidades concretas y consecuencias reales si se equivocan.
- Trade-off
- Un producto más amplio alcanza más usuarios pero diluye la propuesta y baja el estándar de validación.
- Decisión
- OKroot está pensada para quien ya tiene la condición. Eso define el prompt, los veredictos y la regla de que una duda se muestra como duda, nunca como un apto.
02 Condiciones combinables como estado, no como contexto VerCerrar
- Contexto
- Un usuario puede tener las tres condiciones a la vez, y repetirlas en cada petición es una fuente de olvidos y de respuestas inconsistentes.
- Trade-off
- Validar una restricción es simple. Validar tres a la vez exige modelar el perfil y construir el prompt desde él.
- Decisión
- Las condiciones y los ingredientes a evitar viven en la base de datos. Cada escaneo arma su prompt desde ese perfil y valida contra todas las condiciones activas.
03 Claude API en vez de una lista de ingredientes VerCerrar
- Contexto
- Un ingrediente prohibido aparece bajo decenas de nombres: la lactosa como suero, caseína o lactosuero; el gluten como malta, sémola o espelta. Una lista fija envejece y no cubre todos los casos.
- Trade-off
- Una lista es predecible y barata, pero exige mantenimiento y falla ante nombres nuevos. El modelo generaliza, pero puede equivocarse y cuesta por llamada.
- Decisión
- El modelo analiza la etiqueta real con una lista de nombres técnicos en el prompt. Lo crítico no se le delega: el ingrediente que el usuario prohibió fuerza el rechazo en código, y una imagen ilegible recibe una indicación fija, no una conjetura.
04 Una respuesta por líneas, parseada con reglas VerCerrar
- Contexto
- El resultado alimenta una pantalla con veredicto, condiciones, ingredientes y macros, y no puede romperse si el modelo cambia de redacción.
- Trade-off
- Un formato por líneas con expresiones regulares es más frágil que un esquema estricto, pero es simple y fácil de probar.
- Decisión
- El servidor parsea cada campo por separado: un veredicto que no entiende queda como sin datos y un macro que no puede leer se descarta en lugar de inventarse.
05 La cuota se reserva antes de llamar al modelo VerCerrar
- Contexto
- Cada escaneo cuesta dinero, y dos peticiones simultáneas pueden pasar una comprobación hecha antes de que la otra descuente.
- Trade-off
- Reservar antes obliga a devolver el escaneo si la llamada falla, y un reembolso mal hecho deja al usuario sin escaneos.
- Decisión
- La reserva bloquea la fila de suscripción dentro de una transacción. Si la llamada al modelo falla, el escaneo se devuelve y el usuario recibe un resultado sin datos en lugar de un cobro perdido.
06 PWA con escritura offline, no una app nativa VerCerrar
- Contexto
- El registro de comidas ocurre en el momento, no siempre con buena señal, y el scanner necesita un servidor porque la IA no corre en el dispositivo.
- Trade-off
- Una web tradicional falla sin conexión. Una app nativa exige publicar en stores y mantener dos bases de código.
- Decisión
- Service worker que precachea la app y una cola de IndexedDB para los escaneos, las comidas y la lista de compras. Un escaneo sin red se procesa cuando vuelve la señal, no al instante. Se instala desde el navegador.
07 Sync por lotes con idempotencia, sin fusionar en el cliente VerCerrar
- Contexto
- Una cola offline que reintenta puede enviar la misma operación dos veces, y fusionar cambios en el cliente multiplica los casos raros.
- Trade-off
- Sin fusión en el cliente, el servidor tiene la última palabra. A cambio, el modelo es simple y los reintentos son seguros.
- Decisión
- Cada operación lleva una clave UUID y el servidor devuelve la entidad ya creada si la reconoce. Un fallo de una operación no frena el lote, y el cliente reintenta con esperas crecientes.
Aprendizajes
Un modelo de lenguaje decide bien lo ambiguo, pero lo crítico se asegura en código: el ingrediente que el usuario prohibió se aplica como regla dura, y una imagen ilegible recibe una respuesta fija en vez de una conjetura.
Reservar la cuota antes de llamar al modelo y devolverla si falla evita tanto el sobreuso concurrente como el cobro de un escaneo que no llegó.
Decir con precisión qué funciona sin conexión importa. El scanner necesita servidor, así que un escaneo sin red se encola y se procesa al volver la señal.
Roadmap
Ahora
- PWA en producción en early access, con scanner, diario, recetas, insights, mercado semanal y suscripciones.
Siguiente
- Leer el diario y las recetas desde el almacenamiento local sin conexión: hoy solo el historial de escaneos se lee de la caché.
Después
- Buscador de recetas según lo que hay en la nevera, planificado y aún sin implementar.
02 Landing
Landing de lista de espera en Astro con recetario y analítica con consentimiento. Ver detalleOcultar
- Astro
- TypeScript
- Tailwind CSS
- Vercel Functions
- Supabase
- GA4
- SEO
- A11y
El problema
Una app con varias restricciones dietéticas tiene que decir con precisión a quién sirve y qué hace antes de que alguien se anote. Y como trata temas de salud, no puede prometer lo que no es: ni afirmaciones médicas ni nombres de tecnología que no ayudan al usuario.
Leer cada etiqueta, una por una
El súper se vuelve una revisión de ingredientes, y 'almidón modificado' a las 8 de la noche no ayuda a nadie.
Cruzar varias condiciones en la misma comida
Celiaquía y diabetes a la vez, más la lactosa, y cada app resuelve una sola.
Recetas que parecían seguras
Descartar a posteriori lo que no lo era, y contar a mano los carbohidratos de cada plato.
Una promesa de salud que no se puede sostener
En un tema médico, un claim de más resta confianza y expone. Hay que decir qué hace la herramienta y qué no.
Resultados
- Landing en producción con la lista de espera guardando solicitudes, recetario y páginas legales.
- Analítica con consentimiento y SEO técnico con datos estructurados desde el lanzamiento.
Ver detalle técnico AbrirOcultar detalle técnico
Qué construí
- 01
Astro con salida estática y Tailwind CSS con la configuración en CSS. Sin adaptador ni framework de interfaz: el JavaScript es vanilla, un script por componente. Los íconos de Lucide se incrustan al compilar como SVG, sin JavaScript en el navegador.
- 02
Una página de aterrizaje de nueve secciones en orden fijo: Hero, Dolor, Cómo funciona, Comparación, Funciones, Casos de éxito, Precios, Lista de espera y Preguntas. Además hay páginas de Sobre mí, Contacto y Preguntas, y las cuatro legales (privacidad, términos, habeas data y cookies).
- 03
Recetario como colección de contenido: cada receta es un archivo Markdown validado en el build con un esquema de zod. Una receta que no lo cumple rompe la compilación y no llega a producción. El índice y una página por receta se generan en estático, con JSON-LD de receta y migas de pan.
- 04
Los datos de la sección de precios y el cupo de acceso anticipado viven en un solo archivo de constantes, y el contador de cupos admite solo un número real: con cero, la línea de escasez desaparece.
- 05
Lista de espera con nombre, correo y casilla de consentimiento obligatoria. Envía JSON a una función de servidor que valida y normaliza, y guarda en una tabla compartida de Supabase. El navegador nunca habla con Supabase.
- 06
La función guarda únicamente nombre, correo, producto, fecha de consentimiento y la ruta de la página. No recibe ni guarda datos de salud ni la IP, y descarta cualquier campo extra. Un correo repetido responde 'ya estás dentro' sin crear otra fila, y solo guarda en producción: en preview valida y avisa que el almacenamiento está desactivado.
- 07
Analítica: GA4 con consentimiento. No se carga nunca por defecto; un banner de cookies guarda la preferencia y, solo si el visitante acepta, activa la analítica. Un page_view manual cubre la navegación con transiciones.
- 08
Navegación con View Transitions como mejora progresiva: cada ruta sigue siendo un HTML real, y los scripts se vuelven a ejecutar en cada carga de página.
- 09
SEO técnico: title, description, canonical, Open Graph y Twitter, mapa del sitio automático y JSON-LD con Organization, WebSite, SoftwareApplication y FAQPage. Las preguntas frecuentes alimentan el acordeón y el esquema desde la misma fuente.
- 10
Seguridad: cabeceras estrictas (CSP, HSTS, anti-framing y permisos restringidos) y caché inmutable en los archivos con hash.
- 11
Calidad: la verificación es astro check (TypeScript y plantillas) más el build, que valida además el esquema de las recetas. CI corre ambos en cada pull request, con un audit de dependencias que informa pero no bloquea. No hay pruebas automatizadas de interfaz.
Hero con una promesa concreta
Titular 'Fotografía la etiqueta. Sabe en segundos si puedes comerla', las tres condiciones nombradas, un botón a la lista de espera y un mock del scanner animado.
Dolor, en seis escenas
Carrusel con las seis molestias cotidianas: swipe horizontal en móvil y desplazamiento anclado en escritorio.
Cómo funciona y comparación
Tres pasos (fotografía, análisis con IA y respuesta con la razón) y una tabla frente a otras apps: restricciones juntas, etiqueta real y alcance más allá del scanner.
Funciones y fundadora
Perfil persistente, recetas curadas y diario con soporte sin conexión, y una sección que presenta a la creadora como primera usuaria, con enlaces a su portafolio y redes.
Lista de espera y preguntas
Formulario con consentimiento explícito y nueve preguntas frecuentes que responden con límites claros: no es un reemplazo del médico y hoy solo lee etiquetas de ingredientes.
Recetario
16 recetas sin gluten con tiempos, ingredientes y notas. Los beneficios solo se publican si citan una fuente verificable.
Diseño
- 01
El diseño parte de un prototipo navegable, accesible desde el botón Ver diseño.
- 02
Violeta como color primario sobre fondos crema, en la misma familia visual que la app, con un hero en tarjeta de gradiente suave.
- 03
Fuentes autoalojadas y servidas desde el propio sitio, sin pedir nada a terceros.
- 04
Secciones con un eyebrow editorial, animaciones de aparición con IntersectionObserver y CSS puro, sin una librería de animación.
- 05
Iconografía en SVG incrustado: nunca flechas de texto en los botones ni rayas largas en el copy.
- 06
Un scanner animado en el hero que muestra el escaneo y el veredicto sin cargar el producto real.
- 07
Todo el contenido está en español y las rutas también (preguntas, contacto, sobre-mi, recetas).
Decisiones de arquitectura
01 Acción antes que lista de condiciones VerCerrar
- Contexto
- Hay tres condiciones y un solo botón. Enumerarlas todas en el primer pantallazo diluye la acción.
- Trade-off
- Nombrar las condiciones da claridad. Ponerlas en un botón lo satura.
- Decisión
- El titular promete la acción ('fotografía la etiqueta') y nombra las tres condiciones una sola vez, debajo. Todos los botones llevan al mismo destino: la lista de espera.
02 Copy sin stack ni claims médicos VerCerrar
- Contexto
- El usuario no necesita saber con qué se construyó, y en salud, palabras como diagnóstico o tratamiento prometen lo que la herramienta no es.
- Trade-off
- Omitir el stack quita credibilidad técnica. Omitir los claims médicos limita el discurso.
- Decisión
- El texto describe el beneficio y nunca la tecnología. Las respuestas aclaran que es una ayuda para decidir y no reemplaza al médico, y las recetas solo publican beneficios con fuente.
03 Una fundadora que es la primera usuaria VerCerrar
- Contexto
- Un producto de salud sin prueba social pierde confianza, pero inventar casos la destruye.
- Trade-off
- Un solo caso es poca evidencia. Uno real pesa más que varios de relleno.
- Decisión
- La sección de casos de éxito cuenta la historia real de la creadora y deja un carrusel listo para sumar casos cuando existan, sin inventarlos.
04 Consentimiento antes de la analítica VerCerrar
- Contexto
- La landing habla de salud, y cargar un script de seguimiento por defecto contradice lo que promete.
- Trade-off
- GA4 detrás de un banner pierde parte de las visitas, pero la política de cookies puede ser cierta.
- Decisión
- GA4 solo se carga si el visitante acepta, y se apaga si no hay ID. El banner aclara que no se recopilan datos del perfil de salud.
05 La lista de espera no pide datos de salud VerCerrar
- Contexto
- Preguntar la condición al anotarse daría datos útiles, pero convertiría una lista de contactos en datos sensibles.
- Trade-off
- Sin la condición no se puede segmentar. A cambio, no hay datos de salud que proteger.
- Decisión
- El formulario pide nombre y correo, y la función guarda solo columnas de una lista blanca. Cualquier campo extra se descarta en el servidor.
06 Estática, con una sola función de servidor VerCerrar
- Contexto
- Una landing no necesita un backend propio, pero el formulario tiene que guardar sin exponer llaves.
- Trade-off
- La función de Vercel no corre bajo el servidor de desarrollo de Astro, y el preview no tiene base de datos.
- Decisión
- El sitio es estático y el formulario habla con una función de servidor que guarda los datos. Fuera de producción valida y no guarda.
07 Una tabla de contactos compartida VerCerrar
- Contexto
- Cada producto de Lúmina W tiene su lista de espera, y mantener una base por producto multiplica el trabajo.
- Trade-off
- Una tabla compartida exige que cada función ponga su propio producto y que el script de la tabla sea idéntico en todos los repositorios.
- Decisión
- Las listas de espera comparten una tabla, con una clave única por producto y correo. El servidor define a qué producto pertenece cada registro.
Aprendizajes
Comunicar tres restricciones en un solo llamado exige priorizar la acción sobre la lista de condiciones: fotografiar la etiqueta se entiende más rápido que enumerar lo que se tiene.
En un tema de salud, lo que no se promete vale tanto como lo que sí. Sin claims médicos, sin stack y sin casos inventados, cada frase se puede defender.
Una analítica que espera el consentimiento y un formulario que no pide datos de salud son decisiones de producto, no solo de cumplimiento.
Roadmap
Ahora
- Landing en producción captando la lista de espera del acceso anticipado.
Siguiente
- Sumar más casos de éxito: hoy la landing cuenta solo el de la fundadora.
Después
- Más idiomas y mercados, y más condiciones y combinaciones, según las preguntas frecuentes.
03 Documentación
Documentación oficial del producto: uso, características, primeros pasos y soporte. Ver detalleOcultar
- Astro
- MDX
- TypeScript
- Lucide
- Vitest
- Playwright
- Vercel
Ver detalle técnico AbrirOcultar detalle técnico
Qué construí
- 01
Astro con salida estática, sin React ni adaptador, desplegado en Vercel. El JavaScript es TypeScript vanilla: búsqueda, estado del menú lateral y tabla de contenido.
- 02
Contenido en MDX con colecciones de contenido de Astro: una página por archivo, con título, resumen e ícono tipados por el esquema de la colección.
- 03
Una sola fuente de verdad para la navegación: la lista de grupos y páginas define el menú lateral, la hoja de navegación móvil, la portada y el paginador anterior y siguiente, en orden de lectura, con una prueba que la contrasta con los archivos.
- 04
Bloques de redacción propios, disponibles en cada página sin importarlos: avisos, pasos numerados, campos, capturas, tarjetas, insignias y tablas.
- 05
Diseño atómico con alias de importación por capa (átomos, moléculas, organismos, plantillas y layouts), con los componentes del sistema de diseño del producto recreados en Astro.
- 06
Búsqueda del lado del cliente: el índice se genera en el build como un archivo JSON estático y el navegador lo consulta, sin servidor ni servicios de terceros.
- 07
SEO técnico: sitemap generado, robots.txt, manifest e idioma declarado en el documento.
- 08
Seguridad por cabeceras, con una CSP restrictiva sin orígenes de terceros y cabeceras anti-framing, anti-sniffing, de referrer y de permisos.
- 09
Calidad: ESLint, Prettier y verificación de tipos con astro check, pruebas unitarias con Vitest y pruebas de punta a punta con Playwright, con CI en cada PR.
Diseño
- 01
Los tokens de diseño provienen del sistema de diseño del producto y no se redefinen en el sitio.
- 02
Íconos Lucide con un trazo uniforme, a través de un único componente.
- 03
Barra lateral en escritorio; en móvil, barra superior con una hoja inferior para navegar.