Die Leiste zieht jetzt um, statt zu springen

Nachgebaut nach der App: die Spalte klappt nach oben weg und kommt von
oben zurück, die Insel schiebt sich seitwärts hinaus und herein. Der
Inhalt rückt gleichzeitig nach — ohne das bliebe links ein Loch stehen,
bis die Leiste angekommen ist.

Dazwischen eine Pause von gut zwei Zehnteln: ohne sie tauscht der
Browser die Leiste in einem Bild aus, und vom Umzug ist nichts zu sehen.

Dazu gestaffelt einblendende Reihen und Kacheln, die sich beim
Überfahren leicht anheben. Wer "Bewegung reduzieren" eingeschaltet hat,
bekommt von alledem nichts.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Scarriffle
2026-09-14 16:42:33 +02:00
co-authored by Claude Opus 5
parent baf8003445
commit 2874a89a6e
+53 -2
View File
@@ -255,6 +255,25 @@ img { display: block; }
#app.side .island button { text-align: left; border-radius: var(--r-control); }
#app.side .island .sep { width: auto; height: 1px; margin: var(--s-snug); }
#app.side #content { margin-left: calc(var(--sidebar-w) + 24px); }
/* ---- Der Umzug der Leiste -----------------------------------------------
Nachgebaut nach der App: die Spalte klappt nach **oben** weg und kommt von
oben zurück, die Insel schiebt sich **seitwärts** hinaus und herein.
Zusammen sieht es aus, als zöge dieselbe Leiste um — erst zusammenklappen,
dann hinüberschieben.
Der Inhalt rückt gleichzeitig nach; ohne das bliebe links ein Loch stehen,
bis die Leiste angekommen ist. */
.island { transition: transform .3s cubic-bezier(.32,.72,0,1), opacity .22s ease; }
#content { transition: margin-left .32s cubic-bezier(.32,.72,0,1); }
#chrome.moving .island { opacity: 0; transform: translateX(-115%); }
#app.side #chrome.moving .island { opacity: 0; transform: translateY(-115%); }
/* Wer Bewegung nicht mag, bekommt keine. */
@media (prefers-reduced-motion: reduce) {
.island, #content, #chrome, .card .art, .shot { transition: none !important; }
}
#app.side .pad-top { padding-top: var(--s-loose); }
.island .brand {
@@ -298,6 +317,20 @@ input[type=range] { width: 100%; accent-color: var(--accent); }
/* ---- Zustände ------------------------------------------------------------ */
#content { padding-bottom: var(--s-section); }
/* Ein Seitenwechsel soll nicht schlagen. Kurz und flach — was länger dauert,
fühlt sich nach Warten an statt nach Bewegung. */
@keyframes rise { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }
.row, .grid, .settings, #hero { animation: rise .32s cubic-bezier(.32,.72,0,1) both; }
/* Die Reihen kommen gestaffelt — die erste zuerst, dann nach unten weg. */
.row:nth-of-type(2) { animation-delay: .04s; }
.row:nth-of-type(3) { animation-delay: .08s; }
.row:nth-of-type(4) { animation-delay: .12s; }
.row:nth-of-type(n+5) { animation-delay: .16s; }
/* Kacheln heben sich beim Überfahren leicht an. */
.card .art { transition: transform .2s cubic-bezier(.32,.72,0,1), box-shadow .2s ease; }
.card:hover .art { transform: scale(1.04); box-shadow: 0 10px 24px rgba(0,0,0,.5); }
.pad-top { padding-top: calc(84px + env(safe-area-inset-top)); }
.spinner {
width: 26px; height: 26px; border: 2px solid var(--separator);
@@ -533,10 +566,28 @@ function buildIsland() {
? '<svg viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.6"><rect x="2" y="3.5" width="16" height="13" rx="2.5"/><path d="M2 8h16" /><rect x="2" y="3.5" width="16" height="4.5" rx="2.5" fill="currentColor" stroke="none" opacity=".9"/></svg>'
// Rechteck mit Spalte links — die Leiste zieht an die Seite.
: '<svg viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.6"><rect x="2" y="3.5" width="16" height="13" rx="2.5"/><path d="M7.5 3.5v13"/><rect x="2" y="3.5" width="5.5" height="13" rx="2.5" fill="currentColor" stroke="none" opacity=".9"/></svg>';
nav.onclick = () => {
// Erst wegklappen, dann umziehen, dann zurückkommen. Ohne die Pause
// dazwischen tauscht der Browser die Leiste in einem Bild aus, und vom
// Umzug ist nichts zu sehen.
nav.onclick = async () => {
const chrome = el('chrome');
const quick = matchMedia('(prefers-reduced-motion: reduce)').matches;
if (!quick) {
chrome.classList.add('moving');
await new Promise((r) => setTimeout(r, 210));
}
setPref('nav', P.nav === 'side' ? 'top' : 'side');
buildIsland();
go(current);
if (!quick) {
// Ein Bild abwarten, damit die neue Form ihre Ausgangsstellung
// übernimmt, bevor sie hereinfährt — sonst gibt es keinen Weg zu gehen.
requestAnimationFrame(() => requestAnimationFrame(() => {
el('chrome').classList.remove('moving');
}));
}
};
isl.appendChild(nav);