sunshine daisy

This commit is contained in:
2026-07-19 22:49:13 +02:00
parent 9c6070afff
commit 5529724982
2 changed files with 262 additions and 36 deletions

View File

@@ -2119,8 +2119,100 @@ body.layout-modern .global-sidebar-right {
font-size: 1.5em; font-size: 1.5em;
} }
/* Mobile Stacking for Legacy Mode (max-width: 999px) */ /* Mobile sidebar (max-width: 999px): sidebar fixed, capped to viewport width */
@media (max-width: 999px) { @media (max-width: 999px) {
/* Sidebar itself: cap width to viewport, always right-anchored */
.item-sidebar-right,
.index-sidebar-right,
.global-sidebar-right {
width: min(280px, 80vw) !important;
right: 0 !important;
}
body.sidebar-right-hidden .item-sidebar-right,
body.sidebar-right-hidden .index-sidebar-right,
body.sidebar-right-hidden .global-sidebar-right {
right: calc(-1 * min(280px, 80vw)) !important;
}
/* Mobile: pagination centered in available space (left of sidebar) */
.pagination-container-fluid {
right: min(280px, 80vw) !important;
transition: right 0.3s ease-in-out !important;
}
/* Sidebar hidden: pagination expands to full viewport width */
body.sidebar-right-hidden .pagination-container-fluid {
right: 0 !important;
}
#sidebar-right-toggle {
top: 80% !important;
transform: none !important;
}
}
/* 600999px: sidebar is attached to the grid, same as desktop but with mobile width.
Grid reserves space for the sidebar so content is never hidden behind it. */
@media (min-width: 600px) and (max-width: 999px) {
.index-layout-wrapper {
padding-right: min(280px, 80vw);
}
body.sidebar-right-hidden .index-layout-wrapper {
padding-right: 0;
}
/* Generic pagewrapper (non-index, non-item pages) also reserves space */
.pagewrapper:not(:has(.index-layout-wrapper)):not(:has(.item-layout-container)) {
padding-right: min(280px, 80vw);
}
body.sidebar-right-hidden
.pagewrapper:not(:has(.index-layout-wrapper)):not(:has(.item-layout-container)) {
padding-right: 0;
}
/* Item page: extend the desktop grid layout to 600px so the fixed sidebar
aligns naturally — col3 width = sidebar width, so content never overlaps. */
body.layout-modern .item-layout-container {
display: grid;
grid-template-columns: min(260px, 26vw) minmax(0, 1fr) min(280px, 80vw);
height: calc(100vh - var(--navbar-h, 50px));
width: 100%;
overflow: hidden;
}
body.layout-modern.sidebar-right-hidden .item-layout-container {
grid-template-columns: min(260px, 26vw) minmax(0, 1fr) 0px;
}
body.layout-modern .item-layout-container .item-main-content {
grid-column: 2;
}
body.layout-modern .item-sidebar-left {
grid-column: 1;
}
/* Legacy layout: 2-column grid at this range */
body.layout-legacy .item-layout-container {
display: grid;
grid-template-columns: minmax(0, 1fr) min(280px, 80vw);
height: auto !important;
overflow: visible !important;
}
body.layout-legacy.sidebar-right-hidden .item-layout-container {
grid-template-columns: minmax(0, 1fr) 0px;
}
}
/* Very small screens (≤599px): sidebar is always a pure overlay — never reserve space for it.
Wrappers stay at full width regardless of sidebar state; no transitions needed here.
Item view also switches to stacked layout here (not at 999px). */
@media (max-width: 599px) {
/* Item view: stack content above comments/sidebar-left */
.item-layout-container { .item-layout-container {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@@ -2130,39 +2222,34 @@ body.layout-modern .global-sidebar-right {
.item-layout-container .item-main-content { .item-layout-container .item-main-content {
order: 1; order: 1;
/* Main content (video) on top */
/* padding: 10px; */
} }
.item-sidebar-left { .item-sidebar-left {
order: 2; order: 2;
/* Comments/Tags on bottom */
/* padding: 10px; */
z-index: 1; z-index: 1;
} }
/* Mobile Overrides if necessary — most styles now universal */ .index-layout-wrapper,
.item-sidebar-right, .item-layout-container,
.index-sidebar-right, .pagewrapper,
.global-sidebar-right { body.sidebar-right-hidden .index-layout-wrapper,
width: 280px !important; body.sidebar-right-hidden .item-layout-container,
body.sidebar-right-hidden .pagewrapper {
padding-right: 5px !important;
transition: none !important;
} }
body.sidebar-right-hidden .item-sidebar-right,
body.sidebar-right-hidden .index-sidebar-right,
body.sidebar-right-hidden .global-sidebar-right {
right: -280px !important;
}
/* Mobile: sidebar is an overlay, so pagination always spans full viewport */
.pagination-container-fluid, .pagination-container-fluid,
body.sidebar-right-hidden .pagination-container-fluid { body.sidebar-right-hidden .pagination-container-fluid {
right: 0 !important; right: 0 !important;
transition: none !important;
} }
#sidebar-right-toggle { /* All sidebars hide/show instantly at this breakpoint — no slide animation */
top: 80% !important; .item-sidebar-right,
transform: none !important; .index-sidebar-right,
.global-sidebar-right {
transition: none !important;
} }
} }
@@ -2958,7 +3045,8 @@ body.layout-legacy .scroll-nav-wrapper {
right: -45px; right: -45px;
width: 40px; width: 40px;
pointer-events: none; pointer-events: none;
z-index: 1000; z-index: 0;
display: none;
} }
body.layout-legacy .scroll-to-bottom { body.layout-legacy .scroll-to-bottom {
@@ -4720,11 +4808,12 @@ body.layout-modern .pagination-container-fluid {
min-width: 0; min-width: 0;
} }
@media (min-width: 1200px) { @media (min-width: 1000px) {
/* Reserve space for the fixed sidebar so content doesn't flow behind it */ /* Reserve space for the fixed sidebar so content doesn't flow behind it */
.index-layout-wrapper { .index-layout-wrapper {
padding-right: 300px; padding-right: 300px;
transition: padding-right 0.3s ease-in-out;
} }
/* Modern layout: wider sidebar */ /* Modern layout: wider sidebar */
@@ -7703,6 +7792,7 @@ div.posts>a.thumb > p {
bottom: 5px; bottom: 5px;
left: 5px; left: 5px;
gap: 2px; gap: 2px;
align-items: center;
} }
/* Make individual item entry subtler and faster for infinite scroll */ /* Make individual item entry subtler and faster for infinite scroll */
@@ -9748,6 +9838,20 @@ html[theme="f0ck95d"] .badge-dark {
} }
} }
/* At 600999px: center the button in the free space left of the sidebar.
Shift left by half the sidebar width so transform:translateX(-50%) lands
at the center of the available area rather than the full viewport. */
@media (min-width: 600px) and (max-width: 999px) {
.mobile-scroll-to-top {
left: calc(50% - min(140px, 40vw));
transition: left 0.3s ease-in-out;
}
body.sidebar-right-hidden .mobile-scroll-to-top {
left: 50%;
}
}
.steuerung.steuerung-icon .fa-solid { .steuerung.steuerung-icon .fa-solid {
font-size: 0.85em; font-size: 0.85em;
} }
@@ -9924,9 +10028,10 @@ html[theme="f0ck95"] .embed-responsive.embed-responsive-16by9 {
position: relative; position: relative;
} }
@media (min-width: 1200px) { @media (min-width: 1000px) {
.pagewrapper { .pagewrapper {
padding-right: 300px; padding-right: 300px;
transition: padding-right 0.3s ease-in-out;
} }
body.layout-modern .pagewrapper { body.layout-modern .pagewrapper {
@@ -9942,7 +10047,7 @@ html[theme="f0ck95"] .embed-responsive.embed-responsive-16by9 {
Since Index and Item pages have their own layout wrappers that handle space reservation, Since Index and Item pages have their own layout wrappers that handle space reservation,
we disable the generic pagewrapper padding on those pages to avoid "double padding" or broken expansion. we disable the generic pagewrapper padding on those pages to avoid "double padding" or broken expansion.
*/ */
@media (min-width: 1200px) { @media (min-width: 1000px) {
.pagewrapper:has(.index-layout-wrapper), .pagewrapper:has(.index-layout-wrapper),
.pagewrapper:has(.item-layout-container), .pagewrapper:has(.item-layout-container),
@@ -11071,8 +11176,7 @@ div.posts>a::after {
.pin-indicator { .pin-indicator {
position: absolute; position: relative;
top: 5px;
left: 5px; left: 5px;
font-size: 13px; font-size: 13px;
fill: var(--accent); fill: var(--accent);

View File

@@ -2307,9 +2307,49 @@ window.cancelAnimFrame = (function () {
hidden = hiddenValue === 'true'; hidden = hiddenValue === 'true';
} }
document.body.classList.toggle('sidebar-right-hidden', hidden); document.body.classList.toggle('sidebar-right-hidden', hidden);
// Always force-hide on very small screens regardless of saved preference
if (window.innerWidth <= 599) {
document.body.classList.add('sidebar-right-hidden');
}
}; };
window.initSidebarRightToggle(); window.initSidebarRightToggle();
// Auto-hide sidebar on very small screens (≤599px), restore on wider
(() => {
const mq = window.matchMedia('(max-width: 599px)');
const applyBreakpoint = (e) => {
// Freeze transitions so the class change is instant (no sliding during resize)
const sidebar = document.querySelector('.global-sidebar-right, .item-sidebar-right, .index-sidebar-right');
const wrappers = document.querySelectorAll('.index-layout-wrapper, .item-layout-container, .pagination-container-fluid, .pagewrapper');
if (sidebar) sidebar.style.setProperty('transition', 'none', 'important');
wrappers.forEach(w => w.style.setProperty('transition', 'none', 'important'));
if (e.matches) {
// Entering narrow: force-hide without touching localStorage
document.body.classList.add('sidebar-right-hidden');
} else {
// Leaving narrow: restore from saved preference
const saved = localStorage.getItem('sidebarRightHidden');
const hidden = saved === 'true';
document.body.classList.toggle('sidebar-right-hidden', hidden);
}
// Force reflow then restore transitions
if (sidebar) void sidebar.offsetWidth;
requestAnimationFrame(() => {
if (sidebar) sidebar.style.removeProperty('transition');
wrappers.forEach(w => {
w.style.removeProperty('transition');
w.style.removeProperty('padding-right');
w.style.removeProperty('right');
});
});
};
mq.addEventListener('change', applyBreakpoint);
applyBreakpoint(mq); // run once on init
})();
const loadPageAjax = async (url, replace = true, options = {}) => { const loadPageAjax = async (url, replace = true, options = {}) => {
if (isNavigating) return; if (isNavigating) return;
@@ -6281,8 +6321,10 @@ window.cancelAnimFrame = (function () {
if (settled) return; if (settled) return;
settled = true; settled = true;
// Freeze all transitions (override CSS !important) so the class toggle // Freeze sidebar AND wrapper transitions so the class toggle
// doesn't trigger an additional right-property animation. // doesn't trigger a second padding-right animation.
const wrappers = getSidebarWrappers();
wrappers.forEach(w => w.style.setProperty('transition', 'none', 'important'));
sidebar.style.setProperty('transition', 'none', 'important'); sidebar.style.setProperty('transition', 'none', 'important');
if (stateChanging) { if (stateChanging) {
@@ -6300,6 +6342,11 @@ window.cancelAnimFrame = (function () {
// Restore CSS-controlled transitions on the next frame // Restore CSS-controlled transitions on the next frame
requestAnimationFrame(() => { requestAnimationFrame(() => {
sidebar.style.removeProperty('transition'); sidebar.style.removeProperty('transition');
wrappers.forEach(w => {
w.style.removeProperty('transition');
w.style.removeProperty('padding-right');
w.style.removeProperty('right');
});
swipeRT.isDraggingSidebar = false; swipeRT.isDraggingSidebar = false;
}); });
}; };
@@ -6537,6 +6584,10 @@ window.cancelAnimFrame = (function () {
const settle = () => { const settle = () => {
if (settled) return; if (settled) return;
settled = true; settled = true;
// Freeze sidebar AND wrapper transitions so the class toggle
// doesn't trigger a second padding-right animation.
const wrappers = getSidebarWrappers();
wrappers.forEach(w => w.style.setProperty('transition', 'none', 'important'));
sidebar.style.setProperty('transition', 'none', 'important'); sidebar.style.setProperty('transition', 'none', 'important');
if (stateChanging) { if (stateChanging) {
@@ -6549,6 +6600,11 @@ window.cancelAnimFrame = (function () {
sidebar.style.transform = ''; sidebar.style.transform = '';
requestAnimationFrame(() => { requestAnimationFrame(() => {
sidebar.style.removeProperty('transition'); sidebar.style.removeProperty('transition');
wrappers.forEach(w => {
w.style.removeProperty('transition');
w.style.removeProperty('padding-right');
w.style.removeProperty('right');
});
}); });
}; };
@@ -6616,15 +6672,53 @@ window.cancelAnimFrame = (function () {
}); });
window.addEventListener('resize', syncEdgeZone, { passive: true }); window.addEventListener('resize', syncEdgeZone, { passive: true });
// Touchend on edgeZone: instant tap-to-toggle (no 300ms synthetic click delay) // Returns true if (clientX, clientY) is within the pill's hit area.
// The pill is #sidebar-drag-zone::after — 4×40px, centered on pointer:fine,
// right-aligned with padding-right:6px on pointer:coarse.
const isWithinPillArea = (clientX, clientY) => {
const rect = edgeZone.getBoundingClientRect();
const pillH = 40;
const hitPad = 12; // generous padding so it's easy to hit
// Vertical: pill is always centered in the drag zone
const pillCenterY = rect.top + rect.height / 2;
if (clientY < pillCenterY - pillH / 2 - hitPad || clientY > pillCenterY + pillH / 2 + hitPad) return false;
// Horizontal: right-aligned on touch, centered on mouse
const isCoarse = window.matchMedia('(pointer: coarse)').matches;
const pillCenterX = isCoarse ? rect.right - 6 - 2 : rect.left + rect.width / 2;
return clientX >= pillCenterX - 2 - hitPad && clientX <= pillCenterX + 2 + hitPad;
};
// Touchend: tap on the pill toggles sidebar; rest of drag zone only responds to swipe
edgeZone.addEventListener('touchend', e => { edgeZone.addEventListener('touchend', e => {
// Only if it was a clean tap (no significant swipe movement)
if (Math.abs(swipeRT.xDiff || 0) < 15 && Math.abs(swipeRT.yDiff || 0) < 15) { if (Math.abs(swipeRT.xDiff || 0) < 15 && Math.abs(swipeRT.yDiff || 0) < 15) {
e.preventDefault(); e.preventDefault(); // always prevent 300ms synthetic click
edgeZone.dispatchEvent(new Event('click')); const touch = e.changedTouches[0];
if (touch && isWithinPillArea(touch.clientX, touch.clientY)) {
window.toggleSidebarRight();
}
} }
}, { passive: false }); }, { passive: false });
// Helper: all layout wrappers that have padding-right animated by sidebar state
const getSidebarWrappers = () => {
// At ≤999px: sidebar is a pure overlay — only pagination tracks it.
if (window.innerWidth <= 599) {
return document.querySelectorAll('.pagination-container-fluid');
}
// At ≥600px: exclude pagewrapper when a dedicated layout wrapper is present.
// CSS already zeroes pagewrapper.padding-right via :has on those pages,
// so including it here causes double-padding (pagewrapper + index-layout-wrapper both
// get 300px snapped, making the grid 600px too narrow, then jumping back on settle).
const hasDedicatedWrapper = !!(
document.querySelector('.index-layout-wrapper') ||
document.querySelector('.item-layout-container')
);
const sel = hasDedicatedWrapper
? '.index-layout-wrapper, .pagination-container-fluid'
: '.pagination-container-fluid, .pagewrapper';
return document.querySelectorAll(sel);
};
// Smoothly toggle sidebar open/closed // Smoothly toggle sidebar open/closed
window.toggleSidebarRight = () => { window.toggleSidebarRight = () => {
const sidebar = document.querySelector('.global-sidebar-right, .item-sidebar-right, .index-sidebar-right'); const sidebar = document.querySelector('.global-sidebar-right, .item-sidebar-right, .index-sidebar-right');
@@ -6634,6 +6728,23 @@ window.cancelAnimFrame = (function () {
const sidebarWidth = sidebar.offsetWidth || 300; const sidebarWidth = sidebar.offsetWidth || 300;
const targetTranslate = isHidden ? -sidebarWidth : sidebarWidth; const targetTranslate = isHidden ? -sidebarWidth : sidebarWidth;
// At ≤599px the sidebar is a pure overlay — never animate wrapper padding
const animateWrappers = window.innerWidth > 599;
// Grid/pagewrapper: snap instantly in both directions (no animated reflow).
// Pagination always animates — it tracks sidebar position with no reflow cost.
const wrappers = getSidebarWrappers();
if (animateWrappers) {
wrappers.forEach(w => {
const isPagination = w.classList.contains('pagination-container-fluid');
const prop = isPagination ? 'right' : 'padding-right';
const targetVal = isHidden ? sidebarWidth + 'px' : '0px';
w.style.setProperty('transition', isPagination ? `${prop} 0.3s ease-in-out` : 'none', 'important');
w.style.setProperty(prop, targetVal, 'important');
});
void sidebar.offsetWidth; // settle layout before sidebar transform begins
}
sidebar.style.setProperty('transition', 'transform 0.3s ease-in-out', 'important'); sidebar.style.setProperty('transition', 'transform 0.3s ease-in-out', 'important');
sidebar.style.transform = `translateX(${targetTranslate}px)`; sidebar.style.transform = `translateX(${targetTranslate}px)`;
@@ -6641,13 +6752,22 @@ window.cancelAnimFrame = (function () {
const settle = () => { const settle = () => {
if (settled) return; if (settled) return;
settled = true; settled = true;
// Freeze wrapper transitions so the class commit doesn't re-trigger them
wrappers.forEach(w => w.style.setProperty('transition', 'none', 'important'));
sidebar.style.setProperty('transition', 'none', 'important'); sidebar.style.setProperty('transition', 'none', 'important');
const toHidden = !isHidden; const toHidden = !isHidden;
document.body.classList.toggle('sidebar-right-hidden', toHidden); document.body.classList.toggle('sidebar-right-hidden', toHidden);
localStorage.setItem('sidebarRightHidden', toHidden); localStorage.setItem('sidebarRightHidden', toHidden);
void sidebar.offsetWidth; void sidebar.offsetWidth;
sidebar.style.transform = ''; sidebar.style.transform = '';
requestAnimationFrame(() => sidebar.style.removeProperty('transition')); requestAnimationFrame(() => {
sidebar.style.removeProperty('transition');
wrappers.forEach(w => {
w.style.removeProperty('transition');
w.style.removeProperty('padding-right');
w.style.removeProperty('right');
});
});
}; };
const onEnd = ev => { const onEnd = ev => {
@@ -6659,9 +6779,11 @@ window.cancelAnimFrame = (function () {
setTimeout(settle, 350); setTimeout(settle, 350);
}; };
// Click on edgeZone: smoothly toggle sidebar open/closed // Click on edgeZone: only toggle when the click lands on the pill
edgeZone.addEventListener('click', () => { edgeZone.addEventListener('click', (e) => {
if (isWithinPillArea(e.clientX, e.clientY)) {
window.toggleSidebarRight(); window.toggleSidebarRight();
}
}); });
// </mouse-sidebar-drag> // </mouse-sidebar-drag>