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;
}
/* Mobile Stacking for Legacy Mode (max-width: 999px) */
/* Mobile sidebar (max-width: 999px): sidebar fixed, capped to viewport width */
@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 {
display: flex;
flex-direction: column;
@@ -2130,39 +2222,34 @@ body.layout-modern .global-sidebar-right {
.item-layout-container .item-main-content {
order: 1;
/* Main content (video) on top */
/* padding: 10px; */
}
.item-sidebar-left {
order: 2;
/* Comments/Tags on bottom */
/* padding: 10px; */
z-index: 1;
}
/* Mobile Overrides if necessary — most styles now universal */
.item-sidebar-right,
.index-sidebar-right,
.global-sidebar-right {
width: 280px !important;
.index-layout-wrapper,
.item-layout-container,
.pagewrapper,
body.sidebar-right-hidden .index-layout-wrapper,
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,
body.sidebar-right-hidden .pagination-container-fluid {
right: 0 !important;
transition: none !important;
}
#sidebar-right-toggle {
top: 80% !important;
transform: none !important;
/* All sidebars hide/show instantly at this breakpoint — no slide animation */
.item-sidebar-right,
.index-sidebar-right,
.global-sidebar-right {
transition: none !important;
}
}
@@ -2958,7 +3045,8 @@ body.layout-legacy .scroll-nav-wrapper {
right: -45px;
width: 40px;
pointer-events: none;
z-index: 1000;
z-index: 0;
display: none;
}
body.layout-legacy .scroll-to-bottom {
@@ -4720,11 +4808,12 @@ body.layout-modern .pagination-container-fluid {
min-width: 0;
}
@media (min-width: 1200px) {
@media (min-width: 1000px) {
/* Reserve space for the fixed sidebar so content doesn't flow behind it */
.index-layout-wrapper {
padding-right: 300px;
transition: padding-right 0.3s ease-in-out;
}
/* Modern layout: wider sidebar */
@@ -7703,6 +7792,7 @@ div.posts>a.thumb > p {
bottom: 5px;
left: 5px;
gap: 2px;
align-items: center;
}
/* 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 {
font-size: 0.85em;
}
@@ -9924,9 +10028,10 @@ html[theme="f0ck95"] .embed-responsive.embed-responsive-16by9 {
position: relative;
}
@media (min-width: 1200px) {
@media (min-width: 1000px) {
.pagewrapper {
padding-right: 300px;
transition: padding-right 0.3s ease-in-out;
}
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,
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(.item-layout-container),
@@ -11071,8 +11176,7 @@ div.posts>a::after {
.pin-indicator {
position: absolute;
top: 5px;
position: relative;
left: 5px;
font-size: 13px;
fill: var(--accent);

View File

@@ -2307,9 +2307,49 @@ window.cancelAnimFrame = (function () {
hidden = hiddenValue === 'true';
}
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();
// 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 = {}) => {
if (isNavigating) return;
@@ -6281,8 +6321,10 @@ window.cancelAnimFrame = (function () {
if (settled) return;
settled = true;
// Freeze all transitions (override CSS !important) so the class toggle
// doesn't trigger an additional right-property animation.
// 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');
if (stateChanging) {
@@ -6300,6 +6342,11 @@ window.cancelAnimFrame = (function () {
// Restore CSS-controlled transitions on the next frame
requestAnimationFrame(() => {
sidebar.style.removeProperty('transition');
wrappers.forEach(w => {
w.style.removeProperty('transition');
w.style.removeProperty('padding-right');
w.style.removeProperty('right');
});
swipeRT.isDraggingSidebar = false;
});
};
@@ -6537,6 +6584,10 @@ window.cancelAnimFrame = (function () {
const settle = () => {
if (settled) return;
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');
if (stateChanging) {
@@ -6549,6 +6600,11 @@ window.cancelAnimFrame = (function () {
sidebar.style.transform = '';
requestAnimationFrame(() => {
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 });
// 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 => {
// Only if it was a clean tap (no significant swipe movement)
if (Math.abs(swipeRT.xDiff || 0) < 15 && Math.abs(swipeRT.yDiff || 0) < 15) {
e.preventDefault();
edgeZone.dispatchEvent(new Event('click'));
e.preventDefault(); // always prevent 300ms synthetic click
const touch = e.changedTouches[0];
if (touch && isWithinPillArea(touch.clientX, touch.clientY)) {
window.toggleSidebarRight();
}
}
}, { 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
window.toggleSidebarRight = () => {
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 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.transform = `translateX(${targetTranslate}px)`;
@@ -6641,13 +6752,22 @@ window.cancelAnimFrame = (function () {
const settle = () => {
if (settled) return;
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');
const toHidden = !isHidden;
document.body.classList.toggle('sidebar-right-hidden', toHidden);
localStorage.setItem('sidebarRightHidden', toHidden);
void sidebar.offsetWidth;
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 => {
@@ -6659,9 +6779,11 @@ window.cancelAnimFrame = (function () {
setTimeout(settle, 350);
};
// Click on edgeZone: smoothly toggle sidebar open/closed
edgeZone.addEventListener('click', () => {
// Click on edgeZone: only toggle when the click lands on the pill
edgeZone.addEventListener('click', (e) => {
if (isWithinPillArea(e.clientX, e.clientY)) {
window.toggleSidebarRight();
}
});
// </mouse-sidebar-drag>