diff --git a/GUIA_DE_DESARROLLO.md b/GUIA_DE_DESARROLLO.md index 3ca2aee..887bea1 100644 --- a/GUIA_DE_DESARROLLO.md +++ b/GUIA_DE_DESARROLLO.md @@ -69,6 +69,15 @@ Lo mismo vale para cualquier módulo: antes de hacer uno propio, mirad si el dev tiene o lo está haciendo. +## Navegación + +Al abrir se ve la actividad. Los módulos están en la quinta pestaña de abajo, Menú, +que abre una hoja con el lanzador (`hive_views.js`, sin JavaScript: una casilla y +CSS). La barra tiene siempre cuatro atajos elegidos por cada cual más Menú. Por qué +así: `docs/ESTUDIO_NAVEGACION.md`. Si se cambia, que sea con una prueba que lo +justifique, no por gusto. + + ## Lo que es nuestro Aquí se puede hacer lo que haga falta. diff --git a/README.md b/README.md index 9c27fc3..f958763 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,8 @@ Android 7 o superior, 64 bits · se instala junto al Oasis oficial sin pisarlo - La APK se monta sobre la oficial: se coge la del dev, se le cambia el backend por el de la rama y se le da otro nombre de paquete. Así trae todo lo nativo del dev (el puente de audio y cámara, las notificaciones de llamada) y convive con la oficial. +- Al abrir se ve la actividad, no un menú. Los módulos están en la quinta pestaña de + abajo, Menú, que abre una hoja desde abajo en cualquier pantalla. - Votaciones como en asamblea: al crear una se elige Sí/No, semáforo (verde, amarillo, rojo) o termómetro (del 1 al 5). Los resultados salen en barras. - Borrar pide confirmación. Antes la X de una tribu la borraba de un toque. @@ -137,13 +139,14 @@ En la barra de arriba, junto al avatar, está el botón de *No molestar* del Tel

-Lanzador, tribus, una votación de semáforo, módulos, ajustes, la barra de atajos, las -salas del dev y el mismo lanzador con el tema OasisMobile del dev. +La actividad al abrir, tribus, una votación de semáforo, módulos, ajustes, la barra de +atajos, las salas del dev y el menú con el tema OasisMobile del dev. -Casi sesenta módulos no caben en la pantalla de un teléfono: la portada los ordena por -secciones en una rejilla de iconos, como el cajón de apps del móvil. Abajo queda una -barra de cuatro atajos que eliges tú. Dentro de cada módulo los filtros van en fichas -pequeñas, para que el contenido se vea sin tener que bajar. +Casi sesenta módulos no caben en la pantalla de un teléfono. Al abrir la app se ve la +actividad, lo que ha pasado en la red. Abajo hay cuatro atajos que eliges tú y una +quinta pestaña fija, Menú, que abre desde abajo una hoja con todos los módulos por +secciones, como el cajón de apps del móvil. Está en todas las pantallas. Por qué así, +y no un menú arriba o una hamburguesa: [docs/ESTUDIO_NAVEGACION.md](docs/ESTUDIO_NAVEGACION.md). Solo aparecen los módulos activos. En *Ajustes → Workflows* se eligen de golpe: el de acampada deja lo justo para organizarse. En el móvil un workflow cambia los módulos, diff --git a/docs/ESTUDIO_NAVEGACION.md b/docs/ESTUDIO_NAVEGACION.md new file mode 100644 index 0000000..3b6db73 --- /dev/null +++ b/docs/ESTUDIO_NAVEGACION.md @@ -0,0 +1,69 @@ +# Cómo moverse por Oasis en el móvil + +Estudio para decidir la navegación de la rama. 19/10/2026. + +## La pregunta + +Oasis tiene casi sesenta módulos. En un escritorio caben en un menú lateral; en un +móvil no. ¿Qué tiene que ver alguien al abrir la app, y cómo llega al resto? + +## Lo que hay ahora + +Medido en el emulador (Pixel 9, 412 px de ancho) con el workflow acampada (16 módulos): + +- Al abrir, la portada es el lanzador: una rejilla de iconos por secciones que ocupa + unos 1.300 px, casi pantalla y media. La actividad, que es lo que ha pasado en la + red, empieza debajo y no se ve sin bajar. +- Abajo hay una barra de cuatro atajos que elige cada cual (Actividad, Chats, + Publicar, Buzón de serie). +- Para ir a un módulo que no está en la barra hay que volver a la portada y buscarlo + en la rejilla. Desde dentro de un módulo no hay acceso directo al resto. + +O sea: la portada es un menú, no un sitio donde pase algo, y el menú solo está en la +portada. + +## Cómo lo hacen otras + +- Instagram, TikTok, X: el contenido es la portada. Abajo, cinco destinos fijos + (inicio, buscar, crear, vídeos, perfil). Lo demás vive en el perfil o en ajustes. +- WhatsApp, Signal, Telegram: pestañas con las tres o cuatro cosas que se usan + siempre. Lo demás, en un menú de tres puntos. +- Google Maps, Gmail y la guía de Material Design: una barra abajo con entre tres y + cinco destinos; si hay más, el último es "Más" y abre una hoja con el resto. +- El menú hamburguesa arriba a la izquierda (muy usado en 2012-2016) se ha ido + abandonando: lo que no se ve se usa menos, y arriba queda lejos del pulgar. + +## Qué pedimos a la navegación + +1. Al abrir, ver qué pasa (actividad), no un menú. +2. Llegar a cualquier módulo desde cualquier pantalla, en dos toques como mucho. +3. Que el sitio para hacerlo se vea sin explicarlo. +4. Que se alcance con el pulgar con el móvil en una mano. +5. No tapar contenido ni quitar sitio de pantalla. + +## Opciones + +| | Portada | Llegar a un módulo | Pulgar | Se ve | Comentario | +|---|---|---|---|---|---| +| A. Como ahora | Lanzador | Volver a portada y buscar | Medio | Sí, en portada | La actividad queda enterrada | +| B. Botón "Menú" bajo la barra de arriba | Actividad | 2 toques | Lejos (arriba) | Sí | La idea inicial; ocupa una fila arriba | +| C. "Menú" como quinta pestaña de abajo | Actividad | 2 toques | Cerca | Sí | Patrón "Más" de Android y de las apps grandes | +| D. Hamburguesa arriba | Actividad | 2 toques | Lejos | Poco | Lo que se esconde se usa menos | + +## Recomendación + +C: la portada pasa a ser la actividad, y la barra de abajo tiene siempre una quinta +pestaña, "Menú", que abre desde abajo una hoja con todos los módulos agrupados (el +mismo lanzador de ahora). Se cierra tocando fuera, con la X o volviendo a tocar Menú. + +Es la idea de B, un botón que despliega las opciones, pero puesto donde llega el +pulgar y donde está el resto de la navegación. Funciona igual en todas las pantallas, +y se hace sin JavaScript, solo con HTML y CSS (una casilla que abre la hoja). + +## Qué mirar en la prueba + +- Desde la portada, cuántos toques hacen falta para llegar a Tribus, a Votaciones y a + Ajustes, y desde dentro de un foro. +- Si alguien que no conoce la app encuentra el menú sin que se lo digan. +- Si al volver atrás la hoja se queda abierta (no debería). +- Que la barra de cinco quepa a 360 px de ancho. diff --git a/docs/capturas/00-portada.png b/docs/capturas/00-portada.png index 2b4a052..a32f942 100644 Binary files a/docs/capturas/00-portada.png and b/docs/capturas/00-portada.png differ diff --git a/docs/capturas/01-portada.png b/docs/capturas/01-portada.png index 8f6e7d2..30830b0 100644 Binary files a/docs/capturas/01-portada.png and b/docs/capturas/01-portada.png differ diff --git a/docs/capturas/02-tribus.png b/docs/capturas/02-tribus.png index 43874dc..1c54684 100644 Binary files a/docs/capturas/02-tribus.png and b/docs/capturas/02-tribus.png differ diff --git a/docs/capturas/03-votacion.png b/docs/capturas/03-votacion.png index cea3076..5dbb2e4 100644 Binary files a/docs/capturas/03-votacion.png and b/docs/capturas/03-votacion.png differ diff --git a/docs/capturas/04-modulos.png b/docs/capturas/04-modulos.png index a7cb348..2277275 100644 Binary files a/docs/capturas/04-modulos.png and b/docs/capturas/04-modulos.png differ diff --git a/docs/capturas/05-ajustes.png b/docs/capturas/05-ajustes.png index 3a2278f..1bb169a 100644 Binary files a/docs/capturas/05-ajustes.png and b/docs/capturas/05-ajustes.png differ diff --git a/docs/capturas/06-barra.png b/docs/capturas/06-barra.png index d65b309..9693e29 100644 Binary files a/docs/capturas/06-barra.png and b/docs/capturas/06-barra.png differ diff --git a/docs/capturas/07-oasismobile.png b/docs/capturas/07-oasismobile.png index 383609a..1b32edb 100644 Binary files a/docs/capturas/07-oasismobile.png and b/docs/capturas/07-oasismobile.png differ diff --git a/docs/capturas/08-salas.png b/docs/capturas/08-salas.png index f309c89..dc314cb 100644 Binary files a/docs/capturas/08-salas.png and b/docs/capturas/08-salas.png differ diff --git a/docs/capturas/esquema-interfaz.png b/docs/capturas/esquema-interfaz.png index 7813bd9..46c713b 100644 Binary files a/docs/capturas/esquema-interfaz.png and b/docs/capturas/esquema-interfaz.png differ diff --git a/docs/capturas/generar_portada.py b/docs/capturas/generar_portada.py index 4973ba7..f2142ca 100644 --- a/docs/capturas/generar_portada.py +++ b/docs/capturas/generar_portada.py @@ -48,8 +48,8 @@ def portada(): d.rectangle((80, bottom + 12, 420, bottom + 20), fill=FG) d.text((80, 196), "Rama PRUEBAS del hacklab · base Oasis 1.3.3 · tema Acampada, pensado para el pulgar", font=font(SANS, 27), fill=MUTED) - shots = [("01-portada.png", "Lanzador de módulos"), ("02-tribus.png", "Tribus en filas"), - ("03-votacion.png", "Votar en semáforo"), ("07-oasismobile.png", "O el tema del dev")] + shots = [("01-portada.png", "Actividad al abrir"), ("02-tribus.png", "Tribus en filas"), + ("03-votacion.png", "Votar en semáforo"), ("07-oasismobile.png", "El menú, tema del dev")] pw, gap, top = 320, 50, 270 x = (W - (4 * (pw + 20) + 3 * gap)) // 2 cap = font(ANTON, 34) @@ -80,7 +80,7 @@ def esquema(): d.text((46, 26), "LA INTERFAZ DE MÓVIL, POR DENTRO", font=font(ANTON, 46), fill=FG) bottom = d.textbbox((46, 26), "LA INTERFAZ DE MÓVIL, POR DENTRO", font=font(ANTON, 46))[3] d.rectangle((46, bottom + 10, 260, bottom + 16), fill=FG) - d.text((46, bottom + 30), "Casi sesenta módulos no caben en una pantalla de teléfono. Se reparten en tres piezas.", + d.text((46, bottom + 30), "Casi sesenta módulos no caben en una pantalla de teléfono. Lo primero es lo que pasa; el resto, en el menú.", font=font(SANS, 18), fill=MUTED) # el movil esquematico @@ -89,21 +89,19 @@ def esquema(): d.rounded_rectangle((px0 + 16, py0 + 20, px1 - 16, py0 + 84), 14, fill=BG, outline=FG, width=3) s = font(SANS, 15) d.text((px0 + 46, py0 + 42), "logo · no molestar · avatar", font=s, fill=FG) - secciones = [("COMUNIDAD", 8), ("BIBLIOTECA", 2), ("GOBERNANZA", 2)] + # en medio, la actividad: lo que ha pasado en la red y = py0 + 110 - for nombre, n in secciones: - d.text((px0 + 40, y), nombre, font=font(ANTON, 17), fill=FG) - d.line((px0 + 40, y + 26, px1 - 40, y + 26), fill=FG, width=3) - y += 38 - for i in range(n): - cx = px0 + 48 + (i % 4) * 76 - if i and i % 4 == 0: - y += 62 - d.rounded_rectangle((cx, y + 4, cx + 48, y + 52), 12, fill=FG) - d.rounded_rectangle((cx, y, cx + 48, y + 48), 12, fill=BG, outline=FG, width=3) - y += 72 + d.text((px0 + 40, y), "ACTIVIDAD", font=font(ANTON, 22), fill=FG) + d.line((px0 + 40, y + 32, px1 - 40, y + 32), fill=FG, width=3) + y += 52 + for i in range(4): + d.rounded_rectangle((px0 + 40, y, px1 - 40, y + 86), 12, fill=BG, outline=FG, width=3) + d.rectangle((px0 + 56, y + 18, px0 + 150, y + 30), fill=FG) + d.rectangle((px0 + 56, y + 44, px1 - 70, y + 52), fill=MUTED) + d.rectangle((px0 + 56, y + 62, px1 - 120, y + 70), fill=MUTED) + y += 104 d.rounded_rectangle((px0 + 16, py1 - 84, px1 - 16, py1 - 20), 14, fill=NEON, outline=FG, width=3) - d.text((px0 + 92, py1 - 62), "barra: 4 atajos que eliges tú", font=s, fill=FG) + d.text((px0 + 62, py1 - 62), "4 atajos que eliges tú · MENÚ", font=s, fill=FG) bx0, bx1 = 560, 1320 cajas = [ @@ -111,15 +109,14 @@ def esquema(): ["El logo vuelve al inicio y el avatar abre tu perfil.", "En medio, el botón de No molestar del Teléfono.", "Con muchos módulos activos salen también los filtros Personal y Social."], BG), - ((bx0, 375, bx1, 565), "EN MEDIO — EL LANZADOR", - ["Los módulos activos en una rejilla de iconos, por secciones,", - "como el cajón de apps del móvil. Los nombres son los del Oasis", - "oficial: Comunidad, Biblioteca, Gobernanza…", - "Solo en la pantalla de inicio; dentro de cada módulo, filtros en fichas."], BG), - ((bx0, 600, bx1, 750), "ABAJO — LOS ATAJOS", - ["Cuatro módulos a tu elección: ni uno más, que es para el pulgar.", - "Se cambian en Herramientas → Tu barra. La elección se guarda", - "en tu nodo, así que sigue ahí después de cerrar la aplicación."], NEON), + ((bx0, 375, bx1, 565), "EN MEDIO — LA ACTIVIDAD", + ["Al abrir la app se ve lo que ha pasado en la red, no un menú.", + "Dentro de cada módulo, los filtros van en fichas pequeñas", + "y el botón principal (crear tribu, crear sala…) siempre igual."], BG), + ((bx0, 600, bx1, 750), "ABAJO — LOS ATAJOS Y EL MENÚ", + ["Cuatro atajos a tu elección y una quinta pestaña fija, Menú,", + "que abre desde abajo una hoja con todos los módulos por secciones", + "(Comunidad, Biblioteca, Gobernanza…). Está en todas las pantallas."], NEON), ] flechas = [((px1 + 10, py0 + 52), (bx0 - 12, 262)), ((px1 + 10, 440), (bx0 - 12, 470)), ((px1 + 10, py1 - 52), (bx0 - 12, 675))] diff --git a/src/backend/mobile_layer.js b/src/backend/mobile_layer.js index 3fc3f18..611e3c7 100644 --- a/src/backend/mobile_layer.js +++ b/src/backend/mobile_layer.js @@ -2,7 +2,7 @@ // Lo que la interfaz de la rama necesita del backend, fuera de backend.js. // // Prepara lo que leen la barra de arriba y el lanzador de hive_views.js: el filtro -// Personal/Social, si el lanzador se ensena en esta pagina y el avatar propio. Lo +// Personal/Social, que el lanzador no va en la portada y el avatar propio. Lo // monta el router propio (fork_routes.js) delante de las rutas del dev. Nada de // esto corre en escritorio. Ver GUIA_DE_DESARROLLO.md. // @@ -30,7 +30,9 @@ module.exports = ({ getViewerId, about }) => { return async (ctx, next) => { if (!isMobile() || !isPage(ctx.path)) return next(); global.__OASIS_HIVE_FILTER__ = (ctx.path === '/activity' && (ctx.query.hive === 'personal' || ctx.query.hive === 'community')) ? ctx.query.hive : ''; - global.__OASIS_SHOW_HIVE__ = (ctx.path === '/activity' || ctx.path === '/'); + // el lanzador ya no va en la portada: la portada es la actividad y los modulos + // estan en la hoja de la pestaña Menu (docs/ESTUDIO_NAVEGACION.md) + global.__OASIS_SHOW_HIVE__ = false; if (!avatarAt) await refreshAvatar(); else if (Date.now() - avatarAt > 60000) refreshAvatar(); return next(); diff --git a/src/client/assets/styles/mobile_ux.css b/src/client/assets/styles/mobile_ux.css index 0325922..aeb35cd 100644 --- a/src/client/assets/styles/mobile_ux.css +++ b/src/client/assets/styles/mobile_ux.css @@ -531,3 +531,42 @@ body > .welcome-banner { order: -1; margin-top: 10px !important; } background: var(--ux-bg) !important; color: var(--ux-fg) !important; box-shadow: none !important; font-family: var(--ux-text) !important; font-weight: 700 !important; font-size: 15px !important; text-transform: uppercase; text-decoration: none !important; } + +/* ---------- pestaña Menu y su hoja con todos los modulos ---------- */ +/* la portada es la actividad; los modulos se abren desde abajo, como el "Mas" de + Android (docs/ESTUDIO_NAVEGACION.md). Una casilla oculta abre y cierra la hoja */ +.oasis-bottombar input.menu-toggle { position: absolute; opacity: 0; width: 0 !important; height: 0 !important; min-height: 0 !important; pointer-events: none; } +.oasis-bottombar label.bb-menu { cursor: pointer; } +.oasis-bottombar .menu-toggle:checked ~ label.bb-menu .bb-ico.has-svg { background: var(--ux-accent) !important; color: var(--ux-on-accent) !important; } +.oasis-bottombar .menu-sheet, .oasis-bottombar .menu-sheet-backdrop { display: none; } +.oasis-bottombar .menu-toggle:checked ~ .menu-sheet-backdrop { + display: block; position: fixed; inset: 0; z-index: 1002; background: rgba(0, 0, 0, .45); +} +.oasis-bottombar .menu-toggle:checked ~ .menu-sheet { + display: block; position: fixed; left: 0; right: 0; z-index: 1003; + bottom: 0; max-height: 82dvh; overflow-y: auto; overscroll-behavior: contain; + padding: 8px 14px calc(18px + env(safe-area-inset-bottom, 0px)); + background: var(--ux-bg); color: var(--ux-fg); + border-top: 3px solid var(--ux-line); border-radius: 22px 22px 0 0; + box-shadow: 0 -6px 24px rgba(0, 0, 0, .25); +} +.menu-sheet-head { + position: sticky; top: -8px; z-index: 1; display: flex; align-items: center; justify-content: space-between; + margin: 0 -14px 8px; padding: 12px 14px 10px; background: var(--ux-bg); border-bottom: 2px solid var(--ux-line); +} +.menu-sheet-head::before { + content: ""; position: absolute; top: 4px; left: 50%; width: 44px; height: 5px; margin-left: -22px; + border-radius: 3px; background: var(--ux-muted); +} +.menu-sheet-title { font-family: var(--ux-title); font-size: 26px; text-transform: uppercase; line-height: 1; } +.menu-sheet-close { + display: inline-flex; align-items: center; justify-content: center; width: 44px; height: 44px; cursor: pointer; + border: 2px solid var(--ux-line); border-radius: 50%; font-size: 20px; background: var(--ux-bg); +} +.menu-sheet .launcher { margin: 0 !important; padding: 0 !important; } +/* cinco pestañas: que quepan a 360px */ +.oasis-bottombar .bb-item { flex: 1 1 0 !important; min-width: 0 !important; } +@media (max-width: 380px) { .oasis-bottombar .bb-lbl { font-size: 11px !important; } } +.oasis-bottombar .menu-toggle:checked ~ div.menu-sheet { background: var(--ux-bg) !important; bottom: calc(66px + env(safe-area-inset-bottom, 0px)); border-radius: 22px 22px 0 0 !important; border: 0 !important; border-top: 3px solid var(--ux-line) !important; } +.oasis-bottombar div.menu-sheet-head { background: var(--ux-bg) !important; border: 0 !important; border-bottom: 2px solid var(--ux-line) !important; } +.oasis-bottombar .menu-toggle:checked ~ .menu-sheet-backdrop { bottom: calc(66px + env(safe-area-inset-bottom, 0px)); } diff --git a/src/client/assets/translations/i18n_fork.js b/src/client/assets/translations/i18n_fork.js index 122c053..567fcd6 100644 --- a/src/client/assets/translations/i18n_fork.js +++ b/src/client/assets/translations/i18n_fork.js @@ -18,6 +18,8 @@ module.exports = { en: { // usabilidad (19/10/2026): votaciones, borrar, modulos, pie + bbMenu: "Menu", + bbMenuClose: "Close", workflow_acampada: "Camp", tribeSectionsMore: "More", tribeSectionsLabel: "Sections", @@ -96,6 +98,8 @@ module.exports = { }, es: { // usabilidad (19/10/2026): votaciones, borrar, modulos, pie + bbMenu: "Menú", + bbMenuClose: "Cerrar", workflow_acampada: "Acampada", tribeSectionsMore: "Más", tribeSectionsLabel: "Secciones", @@ -180,6 +184,8 @@ module.exports = { }, fr: { // usabilidad (19/10/2026): votaciones, borrar, modulos, pie + bbMenu: "Menu", + bbMenuClose: "Fermer", workflow_acampada: "Campement", tribeSectionsMore: "Plus", tribeSectionsLabel: "Sections", @@ -258,6 +264,8 @@ module.exports = { }, eu: { // usabilidad (19/10/2026): votaciones, borrar, modulos, pie + bbMenu: "Menua", + bbMenuClose: "Itxi", workflow_acampada: "Kanpaldia", tribeSectionsMore: "Gehiago", tribeSectionsLabel: "Atalak", @@ -336,6 +344,8 @@ module.exports = { }, de: { // usabilidad (19/10/2026): votaciones, borrar, modulos, pie + bbMenu: "Menü", + bbMenuClose: "Schließen", workflow_acampada: "Camp", tribeSectionsMore: "Mehr", tribeSectionsLabel: "Bereiche", @@ -414,6 +424,8 @@ module.exports = { }, it: { // usabilidad (19/10/2026): votaciones, borrar, modulos, pie + bbMenu: "Menu", + bbMenuClose: "Chiudi", workflow_acampada: "Accampata", tribeSectionsMore: "Altro", tribeSectionsLabel: "Sezioni", @@ -492,6 +504,8 @@ module.exports = { }, pt: { // usabilidad (19/10/2026): votaciones, borrar, modulos, pie + bbMenu: "Menu", + bbMenuClose: "Fechar", workflow_acampada: "Acampada", tribeSectionsMore: "Mais", tribeSectionsLabel: "Secções", @@ -570,6 +584,8 @@ module.exports = { }, zh: { // usabilidad (19/10/2026): votaciones, borrar, modulos, pie + bbMenu: "菜单", + bbMenuClose: "关闭", workflow_acampada: "营地", tribeSectionsMore: "更多", tribeSectionsLabel: "板块", @@ -648,6 +664,8 @@ module.exports = { }, ar: { // usabilidad (19/10/2026): votaciones, borrar, modulos, pie + bbMenu: "القائمة", + bbMenuClose: "إغلاق", workflow_acampada: "مخيم", tribeSectionsMore: "المزيد", tribeSectionsLabel: "الأقسام", @@ -726,6 +744,8 @@ module.exports = { }, hi: { // usabilidad (19/10/2026): votaciones, borrar, modulos, pie + bbMenu: "मेनू", + bbMenuClose: "बंद करें", workflow_acampada: "शिविर", tribeSectionsMore: "और", tribeSectionsLabel: "खंड", @@ -804,6 +824,8 @@ module.exports = { }, ru: { // usabilidad (19/10/2026): votaciones, borrar, modulos, pie + bbMenu: "Меню", + bbMenuClose: "Закрыть", workflow_acampada: "Лагерь", tribeSectionsMore: "Ещё", tribeSectionsLabel: "Разделы", diff --git a/src/views/hive_views.js b/src/views/hive_views.js index e3e8a3d..fc0923a 100644 --- a/src/views/hive_views.js +++ b/src/views/hive_views.js @@ -114,9 +114,26 @@ module.exports = ({ i18n }) => { const slots = pins.map(m => bbLink( m.href, m.glyph, i18n[m.labelKey] || m.key, m.key === 'inbox' ? inboxN : 0 )); + // quinta pestaña fija: Menu abre desde abajo una hoja con todos los modulos (el + // lanzador). Sin JS: una casilla y CSS. autocomplete=off para que al volver atras + // el navegador no la deje abierta. Ver docs/ESTUDIO_NAVEGACION.md + const menuLabel = i18n.bbMenu || 'Menu'; return nav( { class: "oasis-bottombar", attrs: { "aria-label": i18n.bbQuickActions || "Quick actions" } }, - ...slots + input({ type: 'checkbox', id: 'oasis-menu-toggle', class: 'menu-toggle', autocomplete: 'off', 'aria-label': menuLabel }), + ...slots, + label({ class: 'bb-item bb-menu', for: 'oasis-menu-toggle' }, + iconNode('bb-ico', 'modules', '▦'), + span({ class: 'bb-lbl' }, menuLabel) + ), + label({ class: 'menu-sheet-backdrop', for: 'oasis-menu-toggle', 'aria-hidden': 'true' }), + div({ class: 'menu-sheet', role: 'dialog', 'aria-label': menuLabel }, + div({ class: 'menu-sheet-head' }, + span({ class: 'menu-sheet-title' }, menuLabel), + label({ class: 'menu-sheet-close', for: 'oasis-menu-toggle', 'aria-label': i18n.bbMenuClose || 'Close' }, '✕') + ), + renderHiveNav() + ) ); };