App-tabbalk met glijdende markering Nieuw
Tabbalk onderin voor een mobiele webapp, hier van een fysiotherapiepraktijk: een oranje streep glijdt naar de actieve tab, het icoon wipt kort omhoog en aria-current markeert de pagina. Een teller toont openstaande afspraken. Scroll je in de inhoud naar beneden, dan zakt de balk weg en bij terugscrollen komt hij terug. Pijltjes links en rechts lopen door de tabs. In productie zet je de balk met position:fixed en env(safe-area-inset-bottom) onderin het scherm.
Volgende afspraak
Dinsdag 14 oktober om 08:30 bij Ruben Smit, Fysio Oost in Leiden.
Neem je sportschoenen en een handdoek mee. Parkeren kan gratis achter de praktijk.
Verzetten kan tot 24 uur van tevoren, daarna rekenen we € 39 voor de gemiste afspraak.
Vragen over je oefeningen? Stuur Ruben een bericht, hij antwoordt op werkdagen binnen een dag.
Oefeningen deze week
Brug: 3 keer 12, twee keer per dag.
Kuitstretch tegen de muur: 3 keer 30 seconden per been.
Fietsen op lage weerstand: 15 minuten.
Afspraken
Di 14 okt, 08:30, behandeling knie.
Do 23 okt, 17:15, controle en nieuwe oefeningen.
Berichten
Ruben: Lukt de brug zonder pijn? Dan bouwen we volgende week op naar 15 herhalingen.
Profiel
Lotte Jansen, verzekerd bij Zilveren Kruis. Nog 6 behandelingen vergoed dit jaar.
Zo gebruik je het
- Plak de HTML op de plek waar het onderdeel moet komen.
- Zet de CSS in je stylesheet. Alle klassen hebben een eigen voorvoegsel, dus niets botst met je eigen opmaak.
- Voeg de JavaScript toe en roep init() aan met het element waar de HTML in staat.
<!-- App-tabbalk met glijdende markering · NedDev · eigen werk. -->
<div class="jouw-container">
<div class="atm">
<div class="atm__view" tabindex="0" aria-label="Inhoud">
<section class="atm__p is-on" data-p="0"><h3 class="atm__h">Volgende afspraak</h3><p class="atm__t">Dinsdag 14 oktober om 08:30 bij Ruben Smit, Fysio Oost in Leiden.</p><p class="atm__t">Neem je sportschoenen en een handdoek mee. Parkeren kan gratis achter de praktijk.</p><p class="atm__t">Verzetten kan tot 24 uur van tevoren, daarna rekenen we € 39 voor de gemiste afspraak.</p><p class="atm__t">Vragen over je oefeningen? Stuur Ruben een bericht, hij antwoordt op werkdagen binnen een dag.</p></section>
<section class="atm__p" data-p="1"><h3 class="atm__h">Oefeningen deze week</h3><p class="atm__t">Brug: 3 keer 12, twee keer per dag.</p><p class="atm__t">Kuitstretch tegen de muur: 3 keer 30 seconden per been.</p><p class="atm__t">Fietsen op lage weerstand: 15 minuten.</p></section>
<section class="atm__p" data-p="2"><h3 class="atm__h">Afspraken</h3><p class="atm__t">Di 14 okt, 08:30, behandeling knie.</p><p class="atm__t">Do 23 okt, 17:15, controle en nieuwe oefeningen.</p></section>
<section class="atm__p" data-p="3"><h3 class="atm__h">Berichten</h3><p class="atm__t">Ruben: Lukt de brug zonder pijn? Dan bouwen we volgende week op naar 15 herhalingen.</p></section>
<section class="atm__p" data-p="4"><h3 class="atm__h">Profiel</h3><p class="atm__t">Lotte Jansen, verzekerd bij Zilveren Kruis. Nog 6 behandelingen vergoed dit jaar.</p></section>
</div>
<nav class="atm__bar" aria-label="App">
<span class="atm__mark" aria-hidden="true"></span>
<a class="atm__tab" href="#atm-home" aria-current="page"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M4 11l8-6 8 6v8a1 1 0 0 1-1 1h-4v-6h-6v6H5a1 1 0 0 1-1-1z"/></svg><span>Home</span></a>
<a class="atm__tab" href="#atm-oefeningen"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M3 10v4M6 8v8M18 8v8M21 10v4M6 12h12"/></svg><span>Oefenen</span></a>
<a class="atm__tab" href="#atm-afspraken"><svg viewBox="0 0 24 24" aria-hidden="true"><rect x="4" y="5" width="16" height="15" rx="2"/><path d="M4 10h16M9 3v4M15 3v4"/></svg><span>Afspraken</span><b class="atm__badge" aria-hidden="true">2</b><span class="atm__vh">, 2 gepland</span></a>
<a class="atm__tab" href="#atm-berichten"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M4 6h16v10H9l-5 4z"/></svg><span>Berichten</span></a>
<a class="atm__tab" href="#atm-profiel"><svg viewBox="0 0 24 24" aria-hidden="true"><circle cx="12" cy="8" r="4"/><path d="M4 21c1-4 4.5-6 8-6s7 2 8 6"/></svg><span>Profiel</span></a>
</nav>
</div>
</div>/* App-tabbalk met glijdende markering · NedDev · eigen werk. */
.atm{position:absolute;inset:0;background:#fff;color:#0a0a0a;font-family:'Switzer',sans-serif;text-align:left;overflow:hidden}
.atm *,.atm *::before,.atm *::after{box-sizing:border-box}
.atm__view{position:absolute;inset:0;overflow:auto;padding:16px 18px 76px;outline:none;scrollbar-width:thin}
.atm__view:focus-visible{box-shadow:inset 0 0 0 2px #F5480F}
.atm__p{display:none}
.atm__p.is-on{display:block;animation:atmIn .24s ease}
@keyframes atmIn{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:none}}
.atm__h{margin:0 0 6px;font:600 18px/1.2 'Switzer',sans-serif;letter-spacing:-.01em}
.atm__t{margin:0 0 8px;font:400 14px/1.45 'Switzer',sans-serif;color:#3a3733;max-width:38em}
.atm__bar{position:absolute;left:0;right:0;bottom:0;display:flex;height:60px;background:#0a0a0a;transition:transform .28s cubic-bezier(.2,.7,.2,1)}
.atm.is-hidden .atm__bar{transform:translateY(100%)}
.atm__mark{position:absolute;top:0;left:0;width:28px;height:3px;border-radius:0 0 3px 3px;background:#F5480F;transform:translateX(var(--x,0));transition:transform .32s cubic-bezier(.3,.7,.2,1)}
.atm__tab{position:relative;flex:1;min-width:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:4px;color:#b9b5ae;text-decoration:none;font:500 12px/1 'Switzer',sans-serif;outline:none;-webkit-tap-highlight-color:transparent}
.atm__tab svg{width:22px;height:22px;fill:none;stroke:currentColor;stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round}
.atm__tab span{max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.atm__tab:hover{color:#fff}
.atm__tab[aria-current="page"]{color:#fff;font-weight:600}
.atm__tab[aria-current="page"] svg{stroke:#F5480F}
.atm__tab.is-pop svg{animation:atmPop .34s cubic-bezier(.3,.7,.3,1.4)}
@keyframes atmPop{40%{transform:translateY(-4px) scale(1.08)}100%{transform:none}}
.atm__tab:focus-visible{box-shadow:inset 0 0 0 2px #F5480F}
.atm__vh{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}
.atm__badge{position:absolute;top:7px;left:calc(50% + 6px);min-width:18px;height:18px;padding:0 4px;border-radius:9px;background:#F5480F;color:#0a0a0a;font:700 12px/18px 'Switzer',sans-serif;text-align:center}
@media (max-width:420px){.atm__tab{font-size:12px}.atm__tab span{max-width:62px}}
@media (prefers-reduced-motion:reduce){.atm__bar,.atm__mark{transition:none}.atm__p.is-on,.atm__tab.is-pop svg{animation:none}}// App-tabbalk met glijdende markering · NedDev · eigen werk.
// root = het element waarin de HTML staat
function init(root){
var w=root.querySelector('.atm');if(!w)return;
var tabs=[].slice.call(w.querySelectorAll('.atm__tab')),ps=[].slice.call(w.querySelectorAll('.atm__p')),mark=w.querySelector('.atm__mark'),view=w.querySelector('.atm__view');
function place(){var t=w.querySelector('.atm__tab[aria-current="page"]');if(!t)return;mark.style.setProperty('--x',(t.offsetLeft+t.offsetWidth/2-14)+'px');}
function act(i,focus){tabs.forEach(function(t,k){if(k===i)t.setAttribute('aria-current','page');else t.removeAttribute('aria-current');});
ps.forEach(function(p,k){p.classList.toggle('is-on',k===i);});var t=tabs[i];t.classList.remove('is-pop');void t.offsetWidth;t.classList.add('is-pop');
view.scrollTop=0;w.classList.remove('is-hidden');place();if(focus)t.focus();}
tabs.forEach(function(t,i){t.addEventListener('click',function(e){e.preventDefault();act(i);});
t.addEventListener('keydown',function(e){var j=null;if(e.key==='ArrowRight')j=(i+1)%tabs.length;else if(e.key==='ArrowLeft')j=(i-1+tabs.length)%tabs.length;if(j!==null){e.preventDefault();tabs[j].focus();}});});
var last=0;view.addEventListener('scroll',function(){var y=view.scrollTop,max=view.scrollHeight-view.clientHeight;if(y>last+6&&y>24)w.classList.add('is-hidden');else if(y<last-6||y<=4||y>=max-2&&y<last)w.classList.remove('is-hidden');last=y;},{passive:true});
w.querySelector('.atm__bar').addEventListener('focusin',function(){w.classList.remove('is-hidden');});
if('ResizeObserver' in window)new ResizeObserver(place).observe(w);
place();
}
init(document.querySelector('.jouw-container'));
Meer uit navigatie & menu's
Iets gezien dat je mooi vindt?
Zet het in je selectie en stuur hem op. Dan bouwen we je site eromheen, in jouw huisstijl.
Neem contact op →