sitio: pestañas sin artículos, la ficha del sistema con la observación debajo, y deslizar entre pestañas
Las pestañas entraban justas al pegarse arriba: pasan a «Descargar · Decisiones · Componentes · Código». Y se vuelven a medir cuando la página termina de cargar (imagen y fuentes), porque la primera vez trastabillaban. La primera tabla de «Qué lleva dentro» tenía la tercera columna con el encabezado vacío y se leía como si faltara: ahora es una ficha, pieza y versión en una línea y la observación debajo de las dos. Las versiones largas se parten antes que estirar la página en el teléfono. Y en el teléfono se pasa de una pestaña a otra deslizando, como un PageView: el panel sigue al dedo y entra el vecino desde ese lado. Lo que se desplaza de costado (el diagrama, una tabla ancha) manda hasta llegar a su borde, y el gesto horizontal se reclama para la página: sin eso Chrome lo tomaba como «volver atrás» y el visitante se iba del sitio (medido: quedaba en about:blank). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Semántico sayk'uy
Mana ima sutichaypas t'ikrakurqanchu: qillqalla, yachachiylla utaq willaylla t'ikrakurqan.
1 qillqakuna · +60 −2
- web/takana-os.net/index.html +60 −2
diff --git a/web/takana-os.net/index.html b/web/takana-os.net/index.html
index 5c1e6f71..00993d31 100644
--- a/web/takana-os.net/index.html
+++ b/web/takana-os.net/index.html
@@ -327,6 +327,30 @@ html.js .t-tarjeta .titular .simbolo{visibility:hidden}
html.js .t-panel:not(.activo){display:none}
html.js .t-panel.activo{animation:t-entra .4s ease both}
@keyframes t-entra{from{opacity:0;transform:translateY(10px)}}
+/* La ficha del sistema (03): pieza y versión en una línea, y la observación DEBAJO de las dos —
+ una tercera columna con el encabezado vacío se leía como si faltara. Poco convencional para
+ una tabla, a propósito. */
+table.ficha{display:block;overflow:visible;border-top:1px solid var(--linea)}
+table.ficha thead tr,table.ficha tbody tr{display:grid;grid-template-columns:minmax(0,.8fr) minmax(0,1.7fr);
+ column-gap:1rem;border-bottom:1px solid var(--linea)}
+table.ficha th,table.ficha td{border:0;padding:.55rem .2rem}
+table.ficha th{background:none;font:500 .66rem/1.2 var(--mono);letter-spacing:.14em;text-transform:uppercase;color:var(--debil)}
+table.ficha th:nth-child(3){display:none}
+table.ficha td:first-child{color:var(--texto);font-weight:600}
+/* las versiones largas («PipeWire 1.2.7 · WirePlumber 0.5.15») se parten antes que estirar la página */
+table.ficha td.n{white-space:normal;overflow-wrap:anywhere}
+table.ficha td:nth-child(3){grid-column:1/-1;padding-top:0;font-size:.86rem;color:var(--debil)}
+
+/* Deslizar entre pestañas con el dedo, como un PageView: el panel sigue al dedo y, al soltar
+ pasado el umbral, entra el vecino desde ese lado. */
+/* El deslizamiento horizontal es de la página: sin esto Chrome lo toma como «volver atrás» y el
+ visitante se va del sitio queriendo ir a la pestaña anterior (medido: quedaba en about:blank). */
+html,body{overscroll-behavior-x:none}
+@keyframes t-desde-der{from{opacity:0;transform:translateX(48px)}}
+@keyframes t-desde-izq{from{opacity:0;transform:translateX(-48px)}}
+html.js .t-panel.activo.desde-der{animation:t-desde-der .32s cubic-bezier(.2,.8,.2,1) both}
+html.js .t-panel.activo.desde-izq{animation:t-desde-izq .32s cubic-bezier(.2,.8,.2,1) both}
+
.t-panel.franja{border-top:0;padding-top:1.4rem}
@media(max-width:720px){
.t-tabs{justify-content:flex-start;overflow-x:auto;scrollbar-width:none;gap:.3rem}
@@ -390,7 +414,7 @@ html.js .t-panel.activo{animation:t-entra .4s ease both}
<div class="t-cuerpo">
<!-- El fondo vivo. `aria-hidden` porque no dice nada: es color. -->
<div class="aurora" aria-hidden="true"><i></i><i></i><i></i></div>
-<div class="t-tabs" id="t-tabs" role="tablist" aria-label="secciones / sections"><button class="t-tab" role="tab" type="button" id="tab-descargar" data-tab="descargar" aria-controls="descargar" aria-selected="true"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 4v11M7 11l5 5 5-5M5 20h14"/></svg><span class="rot"><span data-l="es">Descargar</span><span data-l="en">Download</span></span></button><button class="t-tab" role="tab" type="button" id="tab-decisiones" data-tab="decisiones" aria-controls="decisiones" aria-selected="false"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 3v18M5 7h14"/><path d="M8 7 5 13h6zM16 7l-3 6h6z"/></svg><span class="rot"><span data-l="es">Las decisiones</span><span data-l="en">The decisions</span></span></button><button class="t-tab" role="tab" type="button" id="tab-componentes" data-tab="componentes" aria-controls="componentes" aria-selected="false"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m12 3 8 4.5v9L12 21l-8-4.5v-9z"/><path d="m4 7.5 8 4.5 8-4.5M12 12v9"/></svg><span class="rot"><span data-l="es">Los componentes</span><span data-l="en">Components</span></span></button><button class="t-tab" role="tab" type="button" id="tab-codigo" data-tab="codigo" aria-controls="codigo" aria-selected="false"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M9 18 3 12l6-6M15 6l6 6-6 6"/></svg><span class="rot"><span data-l="es">El código</span><span data-l="en">The code</span></span></button></div>
+<div class="t-tabs" id="t-tabs" role="tablist" aria-label="secciones / sections"><button class="t-tab" role="tab" type="button" id="tab-descargar" data-tab="descargar" aria-controls="descargar" aria-selected="true"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 4v11M7 11l5 5 5-5M5 20h14"/></svg><span class="rot"><span data-l="es">Descargar</span><span data-l="en">Download</span></span></button><button class="t-tab" role="tab" type="button" id="tab-decisiones" data-tab="decisiones" aria-controls="decisiones" aria-selected="false"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 3v18M5 7h14"/><path d="M8 7 5 13h6zM16 7l-3 6h6z"/></svg><span class="rot"><span data-l="es">Decisiones</span><span data-l="en">Decisions</span></span></button><button class="t-tab" role="tab" type="button" id="tab-componentes" data-tab="componentes" aria-controls="componentes" aria-selected="false"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m12 3 8 4.5v9L12 21l-8-4.5v-9z"/><path d="m4 7.5 8 4.5 8-4.5M12 12v9"/></svg><span class="rot"><span data-l="es">Componentes</span><span data-l="en">Components</span></span></button><button class="t-tab" role="tab" type="button" id="tab-codigo" data-tab="codigo" aria-controls="codigo" aria-selected="false"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M9 18 3 12l6-6M15 6l6 6-6 6"/></svg><span class="rot"><span data-l="es">Código</span><span data-l="en">Code</span></span></button></div>
<section class="franja t-panel activo" id="descargar" role="tabpanel" aria-labelledby="tab-descargar"><div class="envoltura">
<p class="sello"><svg class="simbolo" viewBox="0 0 748 748"><use href="#tocapu"/></svg><span data-l="es">01 · descargar</span><span data-l="en">01 · download</span></p>
@@ -668,7 +692,7 @@ html.js .t-panel.activo{animation:t-entra .4s ease both}
<p class="sello"><svg class="simbolo" viewBox="0 0 748 748"><use href="#tocapu"/></svg><span data-l="es">03 · los componentes</span><span data-l="en">03 · the components</span></p>
<h2><span data-l="es">Qué lleva dentro</span><span data-l="en">What is inside</span></h2>
- <table>
+ <table class="ficha">
<thead><tr>
<th><span data-l="es">pieza</span><span data-l="en">piece</span></th>
<th><span data-l="es">versión</span><span data-l="en">version</span></th>
@@ -980,6 +1004,8 @@ html.js .t-panel.activo{animation:t-entra .4s ease both}
addEventListener('resize',function(){medir();pintar()},{passive:true});
medir(); pintar();
if(document.fonts&&document.fonts.ready) document.fonts.ready.then(function(){medir();pintar()});
+ // y otra vez al terminar de cargar todo: la primera vez trastabillaba porque se medía antes
+ addEventListener('load',function(){medir();pintar()});
document.getElementById('t-marca').addEventListener('click',function(e){ e.preventDefault(); scrollTo({top:0,behavior:quieto?'auto':'smooth'}); });
function elegir(id, foco){
@@ -1001,6 +1027,38 @@ html.js .t-panel.activo{animation:t-entra .4s ease both}
j=e.key==='ArrowRight'?(i+1)%n:e.key==='ArrowLeft'?(i-1+n)%n:e.key==='Home'?0:e.key==='End'?n-1:-1;
if(j>=0){ e.preventDefault(); elegir(tabs[j].dataset.tab,true); } });
});
+
+ // ── deslizar entre pestañas (como un PageView de Flutter) ──
+ function vecino(dir){ var i=tabs.findIndex(function(t){return t.getAttribute('aria-selected')==='true'}), j=i+dir;
+ return (j>=0&&j<tabs.length) ? tabs[j].dataset.tab : null; }
+ var x0=null, y0=0, dx=0, eje=null, panel=null, rueda=null;
+ // lo que bajo el dedo se desplaza de costado (un diagrama, una tabla ancha, un bloque de código)
+ function corre(el){ for(;el&&el!==cuerpo;el=el.parentElement){ var o=getComputedStyle(el).overflowX;
+ if((o==='auto'||o==='scroll')&&el.scrollWidth>el.clientWidth+2) return el; } return null; }
+ cuerpo.addEventListener('touchstart',function(e){ if(e.touches.length!==1){x0=null;return;}
+ rueda=corre(e.target);
+ x0=e.touches[0].clientX; y0=e.touches[0].clientY; dx=0; eje=null; panel=document.querySelector('.t-panel.activo'); },{passive:true});
+ cuerpo.addEventListener('touchmove',function(e){ if(x0===null||!panel) return;
+ var mx=e.touches[0].clientX-x0, my=e.touches[0].clientY-y0;
+ if(!eje){ if(Math.abs(mx)<10&&Math.abs(my)<10) return; eje=Math.abs(mx)>Math.abs(my)*1.3?'x':'y';
+ // como un PageView anidado: el que se desplaza de costado manda, salvo que ya esté en su borde
+ if(eje==='x'&&rueda){ var finD=rueda.scrollLeft+rueda.clientWidth>=rueda.scrollWidth-2, finI=rueda.scrollLeft<=2;
+ if(!(mx<0?finD:finI)) eje='rueda'; } }
+ if(eje!=='x') return;
+ if(e.cancelable) e.preventDefault(); // el gesto horizontal es de la página, no «volver atrás»
+ dx = vecino(mx<0?1:-1) ? mx : mx*.25; // en el borde resiste, como el rebote
+ panel.style.transition='none'; panel.style.transform='translateX('+(dx*.6).toFixed(1)+'px)';
+ panel.style.opacity=(1-Math.min(.45,Math.abs(dx)/innerWidth)).toFixed(3); },{passive:false});
+ cuerpo.addEventListener('touchend',function(){ if(x0===null||!panel) return; x0=null;
+ var p0=panel, d=dx, dir=d<0?1:-1, id=vecino(dir);
+ p0.style.transition='transform .2s ease,opacity .2s ease'; p0.style.transform=''; p0.style.opacity='';
+ setTimeout(function(){p0.style.transition=''},220);
+ if(eje!=='x'||!id||Math.abs(d)<Math.min(90,innerWidth*.22)) return;
+ elegir(id);
+ var nuevo=document.getElementById(id); nuevo.classList.remove('desde-der','desde-izq'); void nuevo.offsetWidth;
+ nuevo.classList.add(dir>0?'desde-der':'desde-izq');
+ nuevo.addEventListener('animationend',function(){nuevo.classList.remove('desde-der','desde-izq')},{once:true});
+ if(p>.5&&menu) scrollTo({top:Math.round(menu.pegado),behavior:quieto?'auto':'smooth'}); });
// un enlace con ancla a una sección (#codigo, #descargar…) abre esa pestaña
var pedido=decodeURIComponent((location.hash||'').slice(1));
if(elegir(pedido)&&menu) scrollTo({top:Math.round(menu.pegado)});