From bdc4992d16c7fa182af533e88651dde684d65145 Mon Sep 17 00:00:00 2001 From: lenafx Date: Mon, 24 Nov 2025 20:59:50 +0100 Subject: [PATCH] responsive design and better image handling --- src/content/image-handler.js | 80 +++++++-- views/styles/books.css | 306 +++++++++++++++++++++++++++++------ views/styles/home.css | 291 +++++++++++++++++++++++++-------- 3 files changed, 550 insertions(+), 127 deletions(-) diff --git a/src/content/image-handler.js b/src/content/image-handler.js index 974242a..80273b7 100644 --- a/src/content/image-handler.js +++ b/src/content/image-handler.js @@ -1,22 +1,48 @@ +let masonryTimer = null; +function scheduleMasonryRelayout(grid) { + clearTimeout(masonryTimer); + masonryTimer = setTimeout(() => { + relayoutMasonry(grid); + }, 50); +} + export function createImageCard(id, tags, imageUrl, thumbnailUrl, type, options = {}) { - const { - showMessage, - showTagModal, - favoriteIds = new Set() + const { + showMessage, + showTagModal, + favoriteIds = new Set() } = options; const card = document.createElement('div'); - card.className = 'image-entry'; - card.dataset.id = id; + card.className = 'image-entry loading newly-added'; + card.dataset.id = id; card.dataset.type = type; - card.title = tags.join(', '); + card.title = tags.join(', '); const img = document.createElement('img'); img.src = thumbnailUrl || imageUrl; img.loading = 'lazy'; img.alt = tags.join(' '); - img.onload = () => img.classList.add('loaded'); - + + img.onload = () => { + img.classList.add('loaded'); + card.classList.remove('loading'); + + if (type !== 'book') { + setTimeout(() => resizeMasonryItem(card), 20); + scheduleMasonryRelayout(card.parentElement); + } + + setTimeout(() => { + card.classList.remove('newly-added'); + }, 400); + }; + + img.onerror = () => { + card.classList.remove('loading'); + img.classList.add('loaded'); + }; + card.appendChild(img); if (type === 'book') { @@ -30,7 +56,6 @@ export function createImageCard(id, tags, imageUrl, thumbnailUrl, type, options Click To Read `; card.appendChild(readOverlay); - return card; } @@ -40,12 +65,12 @@ export function createImageCard(id, tags, imageUrl, thumbnailUrl, type, options const favBtn = document.createElement('button'); favBtn.className = 'heart-button'; favBtn.dataset.id = id; - + const isFavorited = favoriteIds.has(String(id)); updateHeartIcon(favBtn, isFavorited); favBtn.onclick = async (e) => { - e.stopPropagation(); + e.stopPropagation(); e.preventDefault(); const currentlyFavorited = favoriteIds.has(String(id)); @@ -99,6 +124,32 @@ export function createImageCard(id, tags, imageUrl, thumbnailUrl, type, options return card; } +function resizeMasonryItem(item) { + if (item.dataset.type === 'book') return; + + const grid = item.parentElement; + const rowHeight = parseInt(getComputedStyle(grid).gridAutoRows); + const rowGap = parseInt(getComputedStyle(grid).gap); + + const img = item.querySelector("img"); + + if (!img) return; + + if (!img.complete || img.naturalWidth === 0) { + return; + } + + const width = img.clientWidth; + const imageHeight = (img.naturalHeight / img.naturalWidth) * width; + + const extra = 2; + const totalHeight = imageHeight + extra; + const rowSpan = Math.ceil((totalHeight + rowGap) / (rowHeight + rowGap)); + item.style.gridRowEnd = "span " + rowSpan; + + item.style.height = totalHeight + "px"; +} + function updateHeartIcon(btn, isFavorited) { if (isFavorited) { btn.innerHTML = ``; @@ -109,6 +160,11 @@ function updateHeartIcon(btn, isFavorited) { } } +function relayoutMasonry(grid) { + const items = grid.querySelectorAll('.image-entry:not([data-type="book"])'); + items.forEach(item => resizeMasonryItem(item)); +} + export function populateTagModal(container, tags) { container.innerHTML = ''; if (!tags || tags.length === 0) { diff --git a/views/styles/books.css b/views/styles/books.css index 65bd9cf..4462058 100644 --- a/views/styles/books.css +++ b/views/styles/books.css @@ -27,46 +27,96 @@ padding: 0.5rem 1rem; border-radius: var(--radius-md); } + .back-btn-large:hover { color: var(--text-primary); background: var(--bg-surface-hover); } +@media (max-width: 767px) { + .back-btn-large { + font-size: 0.95rem; + gap: 0.5rem; + padding: 0.4rem 0.8rem; + } +} + .book-layout-grid { display: grid; - grid-template-columns: 300px 1fr; + grid-template-columns: 300px 1fr; gap: 3rem; align-items: start; } +@media (max-width: 1024px) { + .book-layout-grid { + grid-template-columns: 250px 1fr; + gap: 2rem; + } +} + +@media (max-width: 767px) { + .book-layout-grid { + grid-template-columns: 1fr; + gap: 1.5rem; + } +} + .book-left-col { display: flex; flex-direction: column; gap: 1.5rem; - position: sticky; - top: 20px; +} + +@media (min-width: 768px) { + .book-left-col { + position: sticky; + top: 20px; + } +} + +@media (max-width: 767px) { + .book-left-col { + flex-direction: row; + align-items: flex-start; + gap: 1rem; + } } .book-poster-large { width: 100%; border-radius: var(--radius-lg); - box-shadow: 0 15px 40px rgba(0,0,0,0.6); - aspect-ratio: 2/3; - object-fit: cover; + box-shadow: 0 15px 40px rgba(0, 0, 0, 0.6); + aspect-ratio: 2 / 3; + object-fit: cover; border: 1px solid var(--border); - background: #111; + background: #111; +} + +@media (max-width: 767px) { + .book-poster-large { + width: 120px; + flex-shrink: 0; + } } .book-title-sidebar { - font-size: 1.5rem; + font-size: 1.5rem; font-weight: 700; line-height: 1.3; margin: 0; color: var(--text-primary); - text-align: center; + text-align: center; word-wrap: break-word; } +@media (max-width: 767px) { + .book-title-sidebar { + font-size: 1.2rem; + text-align: left; + } +} + .book-chapters-column { display: flex; flex-direction: column; @@ -90,11 +140,43 @@ cursor: pointer; transition: background 0.2s; } -.chapter-row:last-child { border-bottom: none; } -.chapter-row:hover { background: var(--bg-surface-hover); } -.chapter-main-text { font-weight: 600; font-size: 1rem; color: var(--text-primary); } -.chapter-sub-text { font-size: 0.9rem; color: var(--text-tertiary); } +.chapter-row:last-child { + border-bottom: none; +} + +.chapter-row:hover { + background: var(--bg-surface-hover); +} + +@media (max-width: 767px) { + .chapter-row { + padding: 0.75rem 1rem; + flex-direction: column; + align-items: flex-start; + gap: 0.25rem; + } +} + +.chapter-main-text { + font-weight: 600; + font-size: 1rem; + color: var(--text-primary); +} + +.chapter-sub-text { + font-size: 0.9rem; + color: var(--text-tertiary); +} + +@media (max-width: 767px) { + .chapter-main-text { + font-size: 0.9rem; + } + .chapter-sub-text { + font-size: 0.8rem; + } +} .pagination-bar { display: flex; @@ -103,7 +185,9 @@ gap: 1.5rem; margin-top: 1rem; padding-top: 1rem; + flex-wrap: wrap; } + .page-btn { background: var(--bg-surface); border: 1px solid var(--border); @@ -114,16 +198,46 @@ font-weight: 500; transition: 0.2s; } -.page-btn:hover:not(:disabled) { background: var(--bg-surface-hover); border-color: var(--accent); } -.page-btn:disabled { opacity: 0.4; cursor: not-allowed; } -#reader-view { - position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; - z-index: 200; background: #0d0d0d; overflow-y: auto; - display: flex; flex-direction: column; align-items: center; padding-top: 60px; +.page-btn:hover:not(:disabled) { + background: var(--bg-surface-hover); + border-color: var(--accent); } -.reader-page-img { max-width: 100%; width: auto; display: block; margin-bottom: 0; box-shadow: 0 0 20px rgba(0,0,0,0.5); } +.page-btn:disabled { + opacity: 0.4; + cursor: not-allowed; +} + +@media (max-width: 767px) { + .page-btn { + padding: 0.4rem 1rem; + font-size: 0.9rem; + } +} + +#reader-view { + position: fixed; + top: 0; + left: 0; + width: 100vw; + height: 100vh; + z-index: 200; + background: #0d0d0d; + overflow-y: auto; + display: flex; + flex-direction: column; + align-items: center; + padding-top: 60px; +} + +.reader-page-img { + max-width: 100%; + width: auto; + display: block; + margin-bottom: 0; + box-shadow: 0 0 20px rgba(0, 0, 0, 0.5); +} .reader-text-content { max-width: 900px; @@ -132,44 +246,68 @@ font-size: 1.1rem; line-height: 1.8; padding: 2rem; - background: #18181b; + background: #18181b; margin-bottom: 4rem; border-radius: 8px; - box-shadow: 0 4px 20px rgba(0,0,0,0.5); + box-shadow: 0 4px 20px rgba(0, 0, 0, 0.5); } + .reader-text-content p { margin-bottom: 1.5rem; } +@media (max-width: 767px) { + .reader-text-content { + font-size: 1rem; + line-height: 1.7; + padding: 1.5rem; + width: 100%; + border-radius: 0; + } +} + .reader-close-btn { - position: fixed; top: 20px; left: 20px; z-index: 210; - background: rgba(0,0,0,0.8); color: white; border: 1px solid rgba(255,255,255,0.2); - padding: 10px 20px; border-radius: 8px; cursor: pointer; display: flex; align-items: center; gap: 8px; font-weight: 600; backdrop-filter: blur(4px); -} -.reader-close-btn:hover { background: var(--accent); border-color: var(--accent); } - -.loading-state { text-align: center; padding: 4rem; color: var(--text-tertiary); } - -.image-entry[data-type="book"] { - aspect-ratio: 2/3; - background: #1a1a1a; - display: block; - position: relative; + position: fixed; + top: 20px; + left: 20px; + z-index: 210; + background: rgba(0, 0, 0, 0.8); + color: white; + border: 1px solid rgba(255, 255, 255, 0.2); + padding: 10px 20px; + border-radius: 8px; cursor: pointer; - overflow: hidden; + display: flex; + align-items: center; + gap: 8px; + font-weight: 600; + backdrop-filter: blur(4px); } -.image-entry[data-type="book"] img { - width: 100%; - height: 100%; - object-fit: cover; - object-position: center top; - transition: filter 0.3s ease, transform 0.3s ease; +.reader-close-btn:hover { + background: var(--accent); + border-color: var(--accent); } -.image-entry[data-type="book"]:hover img { - filter: blur(4px) brightness(0.7); - transform: scale(1.05); +@media (max-width: 767px) { + .reader-close-btn { + top: 10px; + left: 10px; + padding: 8px 16px; + font-size: 0.9rem; + } +} + +.loading-state { + text-align: center; + padding: 4rem; + color: var(--text-tertiary); +} + +@media (max-width: 767px) { + .loading-state { + padding: 2rem 1rem; + } } .book-read-overlay { @@ -182,7 +320,7 @@ gap: 0.5rem; opacity: 0; transition: opacity 0.3s ease; - pointer-events: none; + pointer-events: none; color: white; z-index: 10; } @@ -196,11 +334,11 @@ font-size: 1.1rem; text-transform: uppercase; letter-spacing: 1px; - text-shadow: 0 2px 4px rgba(0,0,0,0.8); + text-shadow: 0 2px 4px rgba(0, 0, 0, 0.8); } .book-read-overlay svg { - filter: drop-shadow(0 2px 4px rgba(0,0,0,0.8)); + filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.8)); color: var(--accent); } @@ -208,7 +346,77 @@ content: ""; position: absolute; inset: 0; - border: 1px solid rgba(255,255,255,0.1); + border: 1px solid rgba(255, 255, 255, 0.1); border-radius: inherit; pointer-events: none; +} + +.gallery-masonry .image-entry[data-type="book"] { + grid-row-end: span 1 !important; + aspect-ratio: 2 / 3 !important; + position: relative !important; + contain: layout style !important; +} + +.gallery-masonry .image-entry[data-type="book"]::before { + content: ""; + display: block; + padding-top: 150% !important; +} + +.gallery-masonry .image-entry[data-type="book"] img { + position: absolute !important; + inset: 0 !important; + width: 100% !important; + height: 100% !important; + object-fit: cover !important; +} +.gallery-masonry:has(.image-entry[data-type="book"]) { + grid-auto-rows: 1fr !important; +} + +.gallery-masonry.books-only .image-entry[data-type="book"], +.gallery-masonry .image-entry[data-type="book"] { + aspect-ratio: 2 / 3; + height: auto; + grid-row-end: span 1 !important; + contain: layout style; + background: var(--bg-surface); + border-radius: var(--radius-md); + overflow: hidden; + position: relative; + cursor: pointer; + transition: transform 0.2s; +} + +.gallery-masonry .image-entry[data-type="book"]::before { + content: ""; + display: block; + padding-top: 150%; +} + +.gallery-masonry .image-entry[data-type="book"] img { + position: absolute; + top: 0; left: 0; + width: 100%; + height: 100%; + object-fit: cover; + object-position: center top; + transition: all 0.3s ease; +} + +.gallery-masonry .image-entry[data-type="book"] img:not(.loaded) { + background: linear-gradient(90deg, #18181b 0%, #27272a 50%, #18181b 100%); + background-size: 200% 100%; + animation: shimmer 1.8s infinite; + opacity: 1; +} + +.gallery-masonry .image-entry[data-type="book"]:hover img { + filter: brightness(0.7) blur(2px); + transform: scale(1.08); +} + +.gallery-masonry .image-entry[data-type="book"]:hover .book-read-overlay { + opacity: 1; } \ No newline at end of file diff --git a/views/styles/home.css b/views/styles/home.css index f983b96..8187a78 100644 --- a/views/styles/home.css +++ b/views/styles/home.css @@ -1,28 +1,19 @@ :root { --bg-base: #09090b; - --bg-sidebar: #101012; - --bg-surface: #18181b; - --bg-surface-hover: #27272a; - --accent: #8b5cf6; - --accent-glow: rgba(139, 92, 246, 0.3); --accent-gradient: linear-gradient(135deg, #8b5cf6, #6366f1); - --text-primary: #f4f4f5; --text-secondary: #a1a1aa; --text-tertiary: #52525b; - --border: #27272a; --border-hover: #3f3f46; - --radius-md: 8px; --radius-lg: 16px; --radius-full: 9999px; - --sidebar-width-collapsed: 72px; --sidebar-width-expanded: 240px; --header-height: 70px; @@ -40,13 +31,12 @@ body { padding: 0; background-color: var(--bg-base); color: var(--text-primary); - font-family: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; + font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; height: 100vh; display: flex; overflow: hidden; } - .sidebar { width: var(--sidebar-width-collapsed); background: var(--bg-sidebar); @@ -64,15 +54,22 @@ body { width: var(--sidebar-width-expanded); } +@media (max-width: 767px) { + .sidebar { + width: 60px; + padding: 1rem 0.5rem; + } +} + .main-wrapper { flex: 1; display: flex; flex-direction: column; height: 100%; position: relative; + min-width: 0; } - .brand-logo { height: 48px; display: flex; @@ -102,7 +99,6 @@ body { } .nav-button { - display: flex; align-items: center; padding: 0.85rem; @@ -128,7 +124,6 @@ body { color: var(--accent); } - .nav-button svg { min-width: 24px; width: 24px; @@ -136,7 +131,22 @@ body { margin-right: 1rem; } -a, a:visited, a:hover, a:active { +@media (max-width: 767px) { + .nav-button { + padding: 0.7rem; + } + .nav-button svg { + min-width: 20px; + width: 20px; + height: 20px; + margin-right: 0; + } +} + +a, +a:visited, +a:hover, +a:active { text-decoration: none; } @@ -151,7 +161,6 @@ a, a:visited, a:hover, a:active { transform: translateX(0); } - .top-header { height: var(--header-height); display: flex; @@ -163,12 +172,17 @@ a, a:visited, a:hover, a:active { z-index: 40; } +@media (max-width: 767px) { + .top-header { + height: 60px; + padding: 0 1rem; + } +} .search-box { display: contents; } - #search-input { background: var(--bg-surface); border: 1px solid var(--border); @@ -185,6 +199,20 @@ a, a:visited, a:hover, a:active { box-shadow: 0 0 0 2px var(--accent-glow); } +@media (max-width: 767px) { + #search-input { + width: 100%; + max-width: 350px; + font-size: 0.85rem; + } +} + +@media (max-width: 480px) { + #search-input { + width: calc(100vw - 80px); + max-width: 300px; + } +} .content-view { flex: 1; @@ -192,8 +220,13 @@ a, a:visited, a:hover, a:active { padding: 0 2rem 2rem 2rem; } -.page { +@media (max-width: 767px) { + .content-view { + padding: 0 1rem 2rem 1rem; + } +} +.page { max-width: 1600px; margin: 0 auto; animation: fadeIn 0.3s ease-out; @@ -204,56 +237,64 @@ a, a:visited, a:hover, a:active { opacity: 0; transform: translateY(10px); } - to { opacity: 1; transform: translateY(0); } } - - - #source-list { display: flex; gap: 1rem; - overflow-x: auto; padding-bottom: 1rem; scrollbar-width: none; - align-items: center; flex-direction: row; - + scroll-behavior: smooth; + overflow-x: auto; + -webkit-overflow-scrolling: touch; } #source-list::-webkit-scrollbar { - display: none; + height: 6px; +} + +#source-list::-webkit-scrollbar-track { + background: transparent; +} + +#source-list::-webkit-scrollbar-thumb { + background: var(--border); + border-radius: 3px; +} + +#source-list::-webkit-scrollbar-thumb:hover { + background: var(--border-hover); +} + +@media (max-width: 767px) { + #source-list { + gap: 0.75rem; + } } .source-button { background: var(--bg-surface); border: 1px solid var(--border); border-radius: var(--radius-md); - - display: flex; align-items: center; gap: 1rem; - - padding: 0.75rem 1rem; min-width: 200px; - width: auto; - height: auto; - - cursor: pointer; transition: all 0.2s; position: relative; overflow: hidden; text-align: left; + flex-shrink: 0; } .source-button:hover { @@ -269,6 +310,13 @@ a, a:visited, a:hover, a:active { color: var(--text-primary); } +@media (max-width: 767px) { + .source-button { + min-width: 160px; + padding: 0.6rem 0.8rem; + gap: 0.75rem; + } +} .source-button img, .source-button .brand-icon { @@ -277,9 +325,15 @@ a, a:visited, a:hover, a:active { border-radius: 6px; object-fit: cover; flex-shrink: 0; - } +@media (max-width: 767px) { + .source-button img, + .source-button .brand-icon { + width: 28px; + height: 28px; + } +} .source-text-wrapper { display: flex; @@ -301,38 +355,56 @@ a, a:visited, a:hover, a:active { .source-url { font-size: 0.75rem; color: var(--text-tertiary); - white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } +@media (max-width: 767px) { + .source-name { + font-size: 0.85rem; + } + .source-url { + font-size: 0.7rem; + } +} .gallery-masonry { - column-count: 2; - column-gap: 1rem; + display: grid; + grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); + gap: 1rem; +} + +.gallery-masonry:not(.books-only) { + grid-auto-rows: 8px; } @media (min-width: 768px) { .gallery-masonry { - column-count: 3; + grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); } } @media (min-width: 1024px) { .gallery-masonry { - column-count: 4; + grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); } } @media (min-width: 1400px) { .gallery-masonry { - column-count: 5; + grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); } } -.image-entry { - margin-bottom: 1rem; +@media (max-width: 480px) { + .gallery-masonry { + grid-template-columns: 1fr; + } +} + +.image-entry:not([data-type="book"]) { + margin-bottom: 0; border-radius: var(--radius-md); overflow: hidden; position: relative; @@ -342,34 +414,30 @@ a, a:visited, a:hover, a:active { cursor: zoom-in; display: inline-block; width: 100%; - } -.image-entry:hover { +.image-entry:not([data-type="book"]):hover { transform: scale(1.02); z-index: 2; box-shadow: 0 10px 25px rgba(0, 0, 0, 0.3); } +@media (max-width: 767px) { + .image-entry:hover { + transform: none; + } +} + .image-entry img { width: 100%; height: auto; - display: block; - opacity: 0; - transition: opacity 0.3s; + aspect-ratio: auto; } .image-entry img.loaded { opacity: 1; } - -.image-entry img:not(.loaded) { - opacity: 1; -} - - - .image-buttons { position: absolute; bottom: 0; @@ -383,17 +451,20 @@ a, a:visited, a:hover, a:active { opacity: 0; transition: 0.2s; top: auto; - right: auto; - flex-direction: row; - } .image-entry:hover .image-buttons { opacity: 1; } +@media (max-width: 767px) { + .image-buttons { + opacity: 0.6; + padding: 1.5rem 0.75rem 0.75rem 0.75rem; + } +} .image-buttons button { width: 32px; @@ -409,7 +480,6 @@ a, a:visited, a:hover, a:active { cursor: pointer; transition: 0.2s; padding: 0; - } .image-buttons button:hover { @@ -422,6 +492,16 @@ a, a:visited, a:hover, a:active { height: 16px; } +@media (max-width: 767px) { + .image-buttons button { + width: 36px; + height: 36px; + } + .image-buttons button svg { + width: 18px; + height: 18px; + } +} .settings-grid { display: grid; @@ -429,6 +509,13 @@ a, a:visited, a:hover, a:active { gap: 1.5rem; } +@media (max-width: 640px) { + .settings-grid { + grid-template-columns: 1fr; + gap: 1rem; + } +} + .settings-card { background: var(--bg-surface); border: 1px solid var(--border); @@ -436,6 +523,12 @@ a, a:visited, a:hover, a:active { padding: 1.5rem; } +@media (max-width: 767px) { + .settings-card { + padding: 1rem; + } +} + .settings-card h3 { margin-top: 0; font-size: 1.1rem; @@ -443,7 +536,6 @@ a, a:visited, a:hover, a:active { color: var(--text-primary); } - fieldset { border: none; padding: 0; @@ -473,7 +565,6 @@ input[type="radio"] { height: 1.2em; } - .hidden { display: none !important; } @@ -488,7 +579,11 @@ input[type="radio"] { gap: 1rem; } - +@media (max-width: 767px) { + .loading-state { + padding: 2rem 1rem; + } +} #tag-info-modal { position: fixed; @@ -499,14 +594,14 @@ input[type="radio"] { display: flex; align-items: center; justify-content: center; + padding: 1rem; } #tag-info-modal.hidden { display: none; } -#tag-info-modal>div { - +#tag-info-modal > div { background: var(--bg-surface); border: 1px solid var(--border); padding: 2rem; @@ -515,6 +610,15 @@ input[type="radio"] { max-width: 500px; position: relative; box-shadow: 0 20px 50px rgba(0, 0, 0, 0.5); + max-height: 80vh; + overflow-y: auto; +} + +@media (max-width: 767px) { + #tag-info-modal > div { + padding: 1.5rem; + width: 95%; + } } #tag-info-close-button { @@ -525,6 +629,12 @@ input[type="radio"] { border: none; color: var(--text-tertiary); cursor: pointer; + font-size: 1.5rem; + width: 32px; + height: 32px; + display: flex; + align-items: center; + justify-content: center; } .tag-cloud { @@ -534,7 +644,6 @@ input[type="radio"] { margin-top: 1rem; } - #tag-info-content span { background: var(--bg-surface-hover); border: 1px solid var(--border); @@ -544,7 +653,6 @@ input[type="radio"] { font-size: 0.8rem; } - .toast { position: fixed; bottom: 2rem; @@ -561,6 +669,18 @@ input[type="radio"] { opacity: 0; pointer-events: none; transition: 0.3s; + max-width: calc(100vw - 4rem); +} + +@media (max-width: 767px) { + .toast { + bottom: 1rem; + right: 1rem; + left: 1rem; + max-width: none; + padding: 0.75rem 1rem; + font-size: 0.9rem; + } } .toast:not(.hidden) { @@ -573,4 +693,43 @@ input[type="radio"] { transform: translateY(0); opacity: 1; pointer-events: all; +} + +#gallery-placeholder { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + width: 100%; + padding: 4rem 1rem; + break-inside: avoid; + column-span: all; +} + +#gallery-placeholder p { + max-width: 300px; + text-align: center; + white-space: normal; + margin: 0; +} + +.image-entry img.loaded { + opacity: 1; + transition: opacity 0.3s ease-in; + min-height: unset; +} + +@keyframes fadeInUp { + from { + opacity: 0; + transform: translateY(20px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +.image-entry.newly-added { + animation: fadeInUp 0.4s ease-out; } \ No newline at end of file