diff options
| -rw-r--r-- | Dockerfile | 6 | ||||
| -rw-r--r-- | static/style.css | 56 | ||||
| -rw-r--r-- | templates/gallery.html | 24 |
3 files changed, 60 insertions, 26 deletions
@@ -2,10 +2,12 @@ FROM alpine RUN apk add --no-cache python3 uv WORKDIR /app +COPY pyproject.toml .python-version uv.lock /app/ +RUN uv sync + COPY static /app/static COPY templates /app/templates -COPY app.py pyproject.toml .python-version uv.lock /app/ -RUN uv sync +COPY app.py /app/ EXPOSE 8000 diff --git a/static/style.css b/static/style.css index 1d412c1..223c62a 100644 --- a/static/style.css +++ b/static/style.css @@ -3,8 +3,8 @@ body { font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; background-color: #121212; color: #e0e0e0; - margin: auto; max-width: 1000px; + margin: auto; padding: 20px; } @@ -22,7 +22,7 @@ h2 { padding-bottom: 8px; } -/* Flexible, wrap-aligned image gallery */ +/* Flex-based gallery layout */ .gallery { display: flex; flex-wrap: wrap; @@ -30,49 +30,65 @@ h2 { align-items: flex-start; } -/* Gallery item constrains to image width */ +/* Each image container wraps to its image's width */ .gallery-item { display: inline-block; margin: 0; + padding: 0; box-sizing: border-box; overflow: hidden; vertical-align: top; } -/* Image: fixed height, natural width */ +/* Image styling with placeholder skeleton */ .gallery-item img { - display: block; height: 200px; width: auto; max-width: 100vw; object-fit: cover; + display: block; transition: transform 0.2s ease-in-out; } -/* Description: match image width */ +.gallery-item img.loading { + background: linear-gradient(90deg, #222 25%, #333 50%, #222 75%); + background-size: 200% 100%; + animation: shimmer 1.5s infinite; + min-width: 200px; + min-height: 200px; +} + +/* Shimmer animation for loading placeholder */ +@keyframes shimmer { + 0% { + background-position: -200% 0; + } + 100% { + background-position: 200% 0; + } +} + +/* Description styling */ .gallery-item p { margin: 8px 0 0; font-size: 0.85rem; color: #aaaaaa; line-height: 1.4; - max-width: 100%; - white-space: normal; + text-align: left; word-break: break-word; - overflow-wrap: break-word; - display: block; -} - -/* Hover effect */ -.gallery-item img:hover { - transform: scale(1.03); + max-width: 100%; } -/* Mobile: full width per row */ +/* Responsive layout for mobile */ @media (max-width: 600px) { body { padding: 12px; } + h1 { + font-size: 1.5rem; + } + h2 { font-size: 1.2rem; } @@ -89,10 +105,16 @@ h2 { .gallery-item img { width: 100%; height: auto; - //max-height: 300px; + max-height: 300px; } .gallery-item p { padding: 0 8px 8px; } } + +/* Instagram icon link */ +.ig-link { + margin-left: 12px; + vertical-align: middle; +} diff --git a/templates/gallery.html b/templates/gallery.html index b5447e2..f33c422 100644 --- a/templates/gallery.html +++ b/templates/gallery.html @@ -2,10 +2,6 @@ <html lang="en"> <head> <meta charset="UTF-8" /> - <link - rel="stylesheet" - href="{{ url_for('static', filename='style.css') }}" - /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="description" @@ -21,6 +17,13 @@ <meta property="og:image" content="{{ cover_img_url }}" /> <meta property="og:url" content="{{ base_url }}" /> <meta property="og:type" content="website" /> + + <title>Leo Lalla</title> + <link + rel="stylesheet" + href="{{ url_for('static', filename='style.css') }}" + /> + <script> function layout() { const isMobile = @@ -30,8 +33,15 @@ const img = item.querySelector("img"); if (!img) return; + const cleanup = () => img.classList.remove("loading"); + + if (img.complete) { + cleanup(); + } else { + img.addEventListener("load", cleanup, { once: true }); + } + if (isMobile) { - // On mobile, reset any previously set width item.style.width = ""; return; } @@ -52,6 +62,7 @@ window.addEventListener("resize", layout); </script> </head> + <body> <h1> {{ author }} {% if ig_link %} @@ -66,7 +77,6 @@ {% endif %} </h1> - <!-- --> {% for date in dates %} <h2>{{ date }}</h2> <div class="gallery"> @@ -78,8 +88,8 @@ > <img src="{{ url_for('serve_thumbnail', filename=f.thumbnail.split('/')[-1]) }}" - alt="{{ f.name }}" loading="lazy" + class="loading" /> </a> {% if f.description %} |
