navegacion: al abrir, la actividad; los modulos en la pestaña Menu

docs/ESTUDIO_NAVEGACION.md compara cuatro formas de moverse por la app
(lanzador en portada, boton de menu arriba, pestaña Menu abajo, hamburguesa)
y se queda con la pestaña de abajo, el patron Mas de Android y de las apps
grandes: al alcance del pulgar y en todas las pantallas.

- la portada es la actividad; el lanzador ya no se pinta alli
- quinta pestaña fija en la barra de abajo, Menu, que abre desde abajo una
  hoja con todos los modulos por secciones; se cierra con la X, tocando fuera
  o volviendo a tocar Menu. Sin JS: una casilla y CSS
- textos en los 11 idiomas; caben cinco pestañas a 360px
- capturas, portada y esquema de la interfaz al dia
This commit is contained in:
hacklab 2026-10-19 14:23:43 +02:00
parent 6eafe5ee18
commit 382d903bc5
18 changed files with 192 additions and 34 deletions

View file

@ -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. 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 ## Lo que es nuestro
Aquí se puede hacer lo que haga falta. Aquí se puede hacer lo que haga falta.

View file

@ -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 - 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 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. 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, - 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. 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. - 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
<img src="docs/capturas/07-oasismobile.png" width="200"> <img src="docs/capturas/07-oasismobile.png" width="200">
</p> </p>
Lanzador, tribus, una votación de semáforo, módulos, ajustes, la barra de atajos, las La actividad al abrir, tribus, una votación de semáforo, módulos, ajustes, la barra de
salas del dev y el mismo lanzador con el tema OasisMobile del dev. 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 Casi sesenta módulos no caben en la pantalla de un teléfono. Al abrir la app se ve la
secciones en una rejilla de iconos, como el cajón de apps del móvil. Abajo queda una actividad, lo que ha pasado en la red. Abajo hay cuatro atajos que eliges tú y una
barra de cuatro atajos que eliges tú. Dentro de cada módulo los filtros van en fichas quinta pestaña fija, Menú, que abre desde abajo una hoja con todos los módulos por
pequeñas, para que el contenido se vea sin tener que bajar. 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 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, acampada deja lo justo para organizarse. En el móvil un workflow cambia los módulos,

View file

@ -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.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 280 KiB

After

Width:  |  Height:  |  Size: 305 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 99 KiB

After

Width:  |  Height:  |  Size: 134 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 159 KiB

After

Width:  |  Height:  |  Size: 160 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 93 KiB

After

Width:  |  Height:  |  Size: 94 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 132 KiB

After

Width:  |  Height:  |  Size: 133 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 109 KiB

After

Width:  |  Height:  |  Size: 110 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 91 KiB

After

Width:  |  Height:  |  Size: 92 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 104 KiB

After

Width:  |  Height:  |  Size: 105 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 76 KiB

After

Width:  |  Height:  |  Size: 78 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

After

Width:  |  Height:  |  Size: 61 KiB

Before After
Before After

View file

@ -48,8 +48,8 @@ def portada():
d.rectangle((80, bottom + 12, 420, bottom + 20), fill=FG) 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", 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) font=font(SANS, 27), fill=MUTED)
shots = [("01-portada.png", "Lanzador de módulos"), ("02-tribus.png", "Tribus en filas"), shots = [("01-portada.png", "Actividad al abrir"), ("02-tribus.png", "Tribus en filas"),
("03-votacion.png", "Votar en semáforo"), ("07-oasismobile.png", "O el tema del dev")] ("03-votacion.png", "Votar en semáforo"), ("07-oasismobile.png", "El menú, tema del dev")]
pw, gap, top = 320, 50, 270 pw, gap, top = 320, 50, 270
x = (W - (4 * (pw + 20) + 3 * gap)) // 2 x = (W - (4 * (pw + 20) + 3 * gap)) // 2
cap = font(ANTON, 34) 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) 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] 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.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) font=font(SANS, 18), fill=MUTED)
# el movil esquematico # 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) d.rounded_rectangle((px0 + 16, py0 + 20, px1 - 16, py0 + 84), 14, fill=BG, outline=FG, width=3)
s = font(SANS, 15) s = font(SANS, 15)
d.text((px0 + 46, py0 + 42), "logo · no molestar · avatar", font=s, fill=FG) 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 y = py0 + 110
for nombre, n in secciones: d.text((px0 + 40, y), "ACTIVIDAD", font=font(ANTON, 22), fill=FG)
d.text((px0 + 40, y), nombre, font=font(ANTON, 17), fill=FG) d.line((px0 + 40, y + 32, px1 - 40, y + 32), fill=FG, width=3)
d.line((px0 + 40, y + 26, px1 - 40, y + 26), fill=FG, width=3) y += 52
y += 38 for i in range(4):
for i in range(n): d.rounded_rectangle((px0 + 40, y, px1 - 40, y + 86), 12, fill=BG, outline=FG, width=3)
cx = px0 + 48 + (i % 4) * 76 d.rectangle((px0 + 56, y + 18, px0 + 150, y + 30), fill=FG)
if i and i % 4 == 0: d.rectangle((px0 + 56, y + 44, px1 - 70, y + 52), fill=MUTED)
y += 62 d.rectangle((px0 + 56, y + 62, px1 - 120, y + 70), fill=MUTED)
d.rounded_rectangle((cx, y + 4, cx + 48, y + 52), 12, fill=FG) y += 104
d.rounded_rectangle((cx, y, cx + 48, y + 48), 12, fill=BG, outline=FG, width=3)
y += 72
d.rounded_rectangle((px0 + 16, py1 - 84, px1 - 16, py1 - 20), 14, fill=NEON, outline=FG, width=3) 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 bx0, bx1 = 560, 1320
cajas = [ cajas = [
@ -111,15 +109,14 @@ def esquema():
["El logo vuelve al inicio y el avatar abre tu perfil.", ["El logo vuelve al inicio y el avatar abre tu perfil.",
"En medio, el botón de No molestar del Teléfono.", "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), "Con muchos módulos activos salen también los filtros Personal y Social."], BG),
((bx0, 375, bx1, 565), "EN MEDIO — EL LANZADOR", ((bx0, 375, bx1, 565), "EN MEDIO — LA ACTIVIDAD",
["Los módulos activos en una rejilla de iconos, por secciones,", ["Al abrir la app se ve lo que ha pasado en la red, no un menú.",
"como el cajón de apps del móvil. Los nombres son los del Oasis", "Dentro de cada módulo, los filtros van en fichas pequeñas",
"oficial: Comunidad, Biblioteca, Gobernanza…", "y el botón principal (crear tribu, crear sala…) siempre igual."], BG),
"Solo en la pantalla de inicio; dentro de cada módulo, filtros en fichas."], BG), ((bx0, 600, bx1, 750), "ABAJO — LOS ATAJOS Y EL MENÚ",
((bx0, 600, bx1, 750), "ABAJO — LOS ATAJOS", ["Cuatro atajos a tu elección y una quinta pestaña fija, Menú,",
["Cuatro módulos a tu elección: ni uno más, que es para el pulgar.", "que abre desde abajo una hoja con todos los módulos por secciones",
"Se cambian en Herramientas → Tu barra. La elección se guarda", "(Comunidad, Biblioteca, Gobernanza…). Está en todas las pantallas."], NEON),
"en tu nodo, así que sigue ahí después de cerrar la aplicación."], NEON),
] ]
flechas = [((px1 + 10, py0 + 52), (bx0 - 12, 262)), ((px1 + 10, 440), (bx0 - 12, 470)), flechas = [((px1 + 10, py0 + 52), (bx0 - 12, 262)), ((px1 + 10, 440), (bx0 - 12, 470)),
((px1 + 10, py1 - 52), (bx0 - 12, 675))] ((px1 + 10, py1 - 52), (bx0 - 12, 675))]

View file

@ -2,7 +2,7 @@
// Lo que la interfaz de la rama necesita del backend, fuera de backend.js. // 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 // 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 // monta el router propio (fork_routes.js) delante de las rutas del dev. Nada de
// esto corre en escritorio. Ver GUIA_DE_DESARROLLO.md. // esto corre en escritorio. Ver GUIA_DE_DESARROLLO.md.
// //
@ -30,7 +30,9 @@ module.exports = ({ getViewerId, about }) => {
return async (ctx, next) => { return async (ctx, next) => {
if (!isMobile() || !isPage(ctx.path)) return 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_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(); if (!avatarAt) await refreshAvatar();
else if (Date.now() - avatarAt > 60000) refreshAvatar(); else if (Date.now() - avatarAt > 60000) refreshAvatar();
return next(); return next();

View file

@ -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; 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; 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)); }

View file

@ -18,6 +18,8 @@
module.exports = { module.exports = {
en: { en: {
// usabilidad (19/10/2026): votaciones, borrar, modulos, pie // usabilidad (19/10/2026): votaciones, borrar, modulos, pie
bbMenu: "Menu",
bbMenuClose: "Close",
workflow_acampada: "Camp", workflow_acampada: "Camp",
tribeSectionsMore: "More", tribeSectionsMore: "More",
tribeSectionsLabel: "Sections", tribeSectionsLabel: "Sections",
@ -96,6 +98,8 @@ module.exports = {
}, },
es: { es: {
// usabilidad (19/10/2026): votaciones, borrar, modulos, pie // usabilidad (19/10/2026): votaciones, borrar, modulos, pie
bbMenu: "Menú",
bbMenuClose: "Cerrar",
workflow_acampada: "Acampada", workflow_acampada: "Acampada",
tribeSectionsMore: "Más", tribeSectionsMore: "Más",
tribeSectionsLabel: "Secciones", tribeSectionsLabel: "Secciones",
@ -180,6 +184,8 @@ module.exports = {
}, },
fr: { fr: {
// usabilidad (19/10/2026): votaciones, borrar, modulos, pie // usabilidad (19/10/2026): votaciones, borrar, modulos, pie
bbMenu: "Menu",
bbMenuClose: "Fermer",
workflow_acampada: "Campement", workflow_acampada: "Campement",
tribeSectionsMore: "Plus", tribeSectionsMore: "Plus",
tribeSectionsLabel: "Sections", tribeSectionsLabel: "Sections",
@ -258,6 +264,8 @@ module.exports = {
}, },
eu: { eu: {
// usabilidad (19/10/2026): votaciones, borrar, modulos, pie // usabilidad (19/10/2026): votaciones, borrar, modulos, pie
bbMenu: "Menua",
bbMenuClose: "Itxi",
workflow_acampada: "Kanpaldia", workflow_acampada: "Kanpaldia",
tribeSectionsMore: "Gehiago", tribeSectionsMore: "Gehiago",
tribeSectionsLabel: "Atalak", tribeSectionsLabel: "Atalak",
@ -336,6 +344,8 @@ module.exports = {
}, },
de: { de: {
// usabilidad (19/10/2026): votaciones, borrar, modulos, pie // usabilidad (19/10/2026): votaciones, borrar, modulos, pie
bbMenu: "Menü",
bbMenuClose: "Schließen",
workflow_acampada: "Camp", workflow_acampada: "Camp",
tribeSectionsMore: "Mehr", tribeSectionsMore: "Mehr",
tribeSectionsLabel: "Bereiche", tribeSectionsLabel: "Bereiche",
@ -414,6 +424,8 @@ module.exports = {
}, },
it: { it: {
// usabilidad (19/10/2026): votaciones, borrar, modulos, pie // usabilidad (19/10/2026): votaciones, borrar, modulos, pie
bbMenu: "Menu",
bbMenuClose: "Chiudi",
workflow_acampada: "Accampata", workflow_acampada: "Accampata",
tribeSectionsMore: "Altro", tribeSectionsMore: "Altro",
tribeSectionsLabel: "Sezioni", tribeSectionsLabel: "Sezioni",
@ -492,6 +504,8 @@ module.exports = {
}, },
pt: { pt: {
// usabilidad (19/10/2026): votaciones, borrar, modulos, pie // usabilidad (19/10/2026): votaciones, borrar, modulos, pie
bbMenu: "Menu",
bbMenuClose: "Fechar",
workflow_acampada: "Acampada", workflow_acampada: "Acampada",
tribeSectionsMore: "Mais", tribeSectionsMore: "Mais",
tribeSectionsLabel: "Secções", tribeSectionsLabel: "Secções",
@ -570,6 +584,8 @@ module.exports = {
}, },
zh: { zh: {
// usabilidad (19/10/2026): votaciones, borrar, modulos, pie // usabilidad (19/10/2026): votaciones, borrar, modulos, pie
bbMenu: "菜单",
bbMenuClose: "关闭",
workflow_acampada: "营地", workflow_acampada: "营地",
tribeSectionsMore: "更多", tribeSectionsMore: "更多",
tribeSectionsLabel: "板块", tribeSectionsLabel: "板块",
@ -648,6 +664,8 @@ module.exports = {
}, },
ar: { ar: {
// usabilidad (19/10/2026): votaciones, borrar, modulos, pie // usabilidad (19/10/2026): votaciones, borrar, modulos, pie
bbMenu: "القائمة",
bbMenuClose: "إغلاق",
workflow_acampada: "مخيم", workflow_acampada: "مخيم",
tribeSectionsMore: "المزيد", tribeSectionsMore: "المزيد",
tribeSectionsLabel: "الأقسام", tribeSectionsLabel: "الأقسام",
@ -726,6 +744,8 @@ module.exports = {
}, },
hi: { hi: {
// usabilidad (19/10/2026): votaciones, borrar, modulos, pie // usabilidad (19/10/2026): votaciones, borrar, modulos, pie
bbMenu: "मेनू",
bbMenuClose: "बंद करें",
workflow_acampada: "शिविर", workflow_acampada: "शिविर",
tribeSectionsMore: "और", tribeSectionsMore: "और",
tribeSectionsLabel: "खंड", tribeSectionsLabel: "खंड",
@ -804,6 +824,8 @@ module.exports = {
}, },
ru: { ru: {
// usabilidad (19/10/2026): votaciones, borrar, modulos, pie // usabilidad (19/10/2026): votaciones, borrar, modulos, pie
bbMenu: "Меню",
bbMenuClose: "Закрыть",
workflow_acampada: "Лагерь", workflow_acampada: "Лагерь",
tribeSectionsMore: "Ещё", tribeSectionsMore: "Ещё",
tribeSectionsLabel: "Разделы", tribeSectionsLabel: "Разделы",

View file

@ -114,9 +114,26 @@ module.exports = ({ i18n }) => {
const slots = pins.map(m => bbLink( const slots = pins.map(m => bbLink(
m.href, m.glyph, i18n[m.labelKey] || m.key, m.key === 'inbox' ? inboxN : 0 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( return nav(
{ class: "oasis-bottombar", attrs: { "aria-label": i18n.bbQuickActions || "Quick actions" } }, { 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()
)
); );
}; };