β οΈ IMPORTANT NOTE FOR SELF-HOSTERS
This is a static site generator - it does NOT include:
- Automatic image upload functionality
- Image management UI or admin panel
- Database or backend services
You must manually:
- Add images to
public/images/original/- Create markdown metadata files in
src/content/photos/- Run the thumbnail generation script
- Rebuild the site
If you need automated workflows, you'll need to build your own scripts or CI/CD pipelines. See Automation Ideas for inspiration.
A sleek, high-performance photo gallery built with Astro, TypeScript, and vanilla CSS/JS.
Showcase your photos with modern design, automatic sorting, and seamless dark mode β no heavy frameworks, just pure speed.
- π Blazing-fast static generation with Astro for instant loading
- β‘ Performance-first β Lazy loading images, efficient CSS, and WebP thumbnails
- πΌοΈ Markdown-driven photo management β Add metadata like dates for automatic sorting
- π
Smart sorting β Photos sorted by
date(newest first, precise to the millisecond), name, or random - π Infinite Scroll β Loads more images as you scroll, with animated transitions and loader
- π Instant dark mode β Flicker-free theme switching with local storage and CSS variables
- π± Fully responsive β Optimized for desktop, tablet, and mobile with CSS Grid
- βΏ Accessible lightbox β Powered by GLightbox for smooth image viewing and screen reader support
- π Unique IDs for Images β Each image uses a unique ID for reliable sharing and direct linking
- π€ GLightbox Share & View Original Buttons β Share direct links or open originals in a new tab
- πΌοΈ Automatic thumbnail generation β Node script using Sharp for optimized WebP images
- π§ Config-driven site β Control navigation, meta-tags, hero text, and more via
src/config/ - π₯ Optional external downloads β Automatically fetch markdown files and images from internal/external URLs during build (configurable via environment variables)
- π Refresh Button β Instantly reloads all thumbnails and resources without cache, so you always see the latest images after updates.
- π¨βπΌ Staff Badges β Highlight staff member images in the grid
- ποΈ Custom Sorting Selector β Sort by newest, oldest, name, or random order
- πΌοΈ Animated grid transitions β Each batch of images animates in with a smooth fade and slide
- π§© Minimal dependencies β No heavy frameworks, just Astro, Preact, and vanilla CSS/JS
pnpm installKey dependencies: Astro, Preact, GLightbox (for lightbox), Sharp (for thumbnails), dotenv (for environment variables), and TypeScript.
Place original images in:
public/images/original/
Example: public/images/original/photo.jpg
Run the script to create optimized WebP thumbnails:
pnpm run gen:thumbsThumbnails will be saved in public/images/thumbs/ (sizes: 200px, 400px, and 800px for high-resolution).
Create Markdown files in:
src/content/photos/
Example: src/content/photos/photo.md
Each file needs frontmatter like this:
---
id: "unique-image-id" # Unique ID for the image (used for sharing and GLightbox features)
title: "My Photo"
slug: "my-photo"
date: "2023-10-01T12:34:56" # ISO format for sorting (precise to seconds)
fullsizePath: "/images/original/photo.webp"
thumbPath: "/images/thumbs/photo-400.webp"
width: 1600
height: 900
caption: "A beautiful moment"
---If you want to automatically download markdown files and images from external sources during the build process:
-
Set environment variables in your
.envfile or build environment:EXT_DL_URL_MARKDOWN=http://your-internal-server/markdown/ EXT_DL_URL_ORIGINAL=http://your-internal-server/original/ EXT_DL_URL_MARKDOWN_EXTERNAL=http://your-external-server/markdown/ # Fallback EXT_DL_URL_ORIGINAL_EXTERNAL=http://your-external-server/original/ # Fallback -
Configure the download behavior in
src/config/externaldownload.cjs:require('dotenv').config(); module.exports = { enableCopyMdFiles: true, enableCopyOriginalImages: true, mdSourceUrlInternal: process.env.EXT_DL_URL_MARKDOWN, mdSourceUrlExternal: process.env.EXT_DL_URL_MARKDOWN_EXTERNAL, originalSourceUrlInternal: process.env.EXT_DL_URL_ORIGINAL, originalSourceUrlExternal: process.env.EXT_DL_URL_ORIGINAL_EXTERNAL, };
-
The build scripts will attempt to download from internal URLs first, falling back to external URLs if needed. If no URLs are set, the build proceeds without downloads.
pnpm run devOpen http://localhost:4321 to see your gallery.
βββ public/
β βββ images/
β β βββ original/ # Your full-size images (or downloaded from external sources)
β β βββ thumbs/ # Auto-generated WebP thumbnails (200px, 400px, 800px)
βββ src/
β βββ components/
β β βββ Header.astro # Sticky header with nav, centered logo, mobile menu, theme toggle, and refresh button
β β βββ PhotoGrid.astro # Server-rendered wrapper for PhotoGridClient
β β βββ PhotoGridClient.tsx # Client-side grid with GLightbox, infinite scroll, and custom buttons
β β βββ ThemeToggle.jsx # Modern theme switcher with CSS animations
β β βββ RefreshButton.astro # Button to reload thumbnails/resources without cache
β βββ config/
β β βββ externaldownload.cjs # Config for external downloads (enable/disable, URLs via env vars)
β β βββ navigation.js # Config for nav links (Portal, Wiki, Blog, Forum, Discord, Map)
β β βββ site.js # Config for site metadata (name, description, URLs, hero text, fonts, etc.)
β βββ content/
β β βββ photos/ # Markdown files with photo metadata (date, paths, etc.)
β βββ pages/
β β βββ index.astro # Main page with hero, sorting, and gallery
β βββ styles/
β βββ variables.css # CSS variables and dark mode
β βββ base.css # Reset, typography, links
β βββ layout.css # Container, grid, background effects, Flexbox for footer
β βββ components.css # Photo, header, footer, theme toggle, header buttons, GLightbox fixes
β βββ hero.css # Hero section styles with min-height for Flexbox compatibility
β βββ responsive.css # Media queries
β βββ accessibility.css # Focus and accessibility
βββ scripts/
β βββ copy-md-files.cjs # Script to download markdown files from internal/external URLs
β βββ copy-original-images.cjs # Script to download images from internal/external URLs
β βββ generate-thumbs.js # Sharp-based thumbnail generator for WebP
β βββ generate-images.js # Generates images.json from originals and markdown
β βββ refresh-resources.cjs # Script to reload thumbnails/resources without cache
βββ package.json # Dependencies: Astro, Preact, GLightbox, Sharp, dotenv
βββ README.md
Edit the config files in src/config/ to customize the site:
Edit src/config/navigation.js to add or remove nav links. Current links:
export const navigationLinks = [
{ label: 'Portal', href: 'https://portal.voidtales.win' },
{ label: 'Wiki', href: 'https://wiki.voidtales.win' },
{ label: 'Blog', href: 'https://blog.voidtales.win' },
{ label: 'Forum', href: 'https://forum.voidtales.win' },
{ label: 'Discord', href: 'https://discord.gg/QEMQsFect6' },
{ label: 'Map', href: 'https://dynmap.voidtales.win' },
];Edit src/config/site.js for site-wide settings like meta-tags, hero text, fonts, and URLs:
export const siteConfig = {
name: 'Void Tales Gallery',
description: 'A sleek, high-performance photo gallery built with Astro, TypeScript, and vanilla CSS/JS. Showcase your photos with modern design, automatic sorting, and seamless dark mode.',
url: 'https://gallery.voidtales.win',
ogImage: '/images/og-image.webp',
author: 'inventory69',
keywords: ['photo gallery', 'Astro', 'VoidTales', 'images', 'modern web'],
fonts: [
'https://fonts.googleapis.com/css2?family=Macondo&family=Macondo+Swash+Caps&display=swap',
'https://fonts.googleapis.com/css2?family=Asul:wght@400;700&display=swap',
'https://fonts.googleapis.com/css2?family=Cinzel+Decorative:wght@400;700;900&display=swap'
],
fontFamilyHead: "'Cinzel Decorative', serif",
fontFamily: "'Asul', sans-serif",
manifest: '/manifest.json',
favicon: '/favicon.ico',
hero: {
title: 'Void Tales Gallery',
subtitle: 'The latest images from the world of VoidTales β sorted by date.',
cta: 'To the images',
},
footer: {
copyright: 'VoidTales',
},
defaultSort: 'date-desc',
staffAuthors: [
"shinsnowly",
"Shin Snowly",
],
};- Infinite Scroll: Loads more images as you scroll, with animated transitions and loader.
- Refresh Button: Instantly reloads all thumbnails and resources without cache.
- Unique IDs for Images: Each image uses a unique ID for reliable sharing in GLightbox.
- GLightbox Share & View Original Buttons: The lightbox now includes a share button and a "view original" button.
- Staff Badges: Highlight staff member images in the grid.
- Animated grid transitions: Each batch of images animates in with a smooth fade and slide.
- Header Buttons: Improved styling and consistency for header action buttons.
- Project-wide TypeScript compatibility: Config imports are now type-safe via
.d.tsfiles.
We welcome contributions to improve VoidTales Gallery! Whether you're fixing bugs, adding features, or enhancing documentation, your help is appreciated.
- Visit https://github.com/inventory69/voidtales-gallery and click "Fork".
- Clone your fork locally:
git clone https://github.com/your-username/voidtales-gallery.git.
- Install dependencies:
pnpm install. - Generate thumbnails if adding images:
pnpm run gen:thumbs. - Start the dev server:
pnpm run devand visit http://localhost:4321.
- Create a feature branch:
git checkout -b feature/your-feature-name. - Edit code, add images, or update docs.
- Test thoroughly: Ensure thumbnails are generated, run
pnpm run dev, and check responsiveness. - Commit changes:
git add . && git commit -m 'feat: Add your feature description'.
- Push your branch:
git push origin feature/your-feature-name. - Open a PR on GitHub with a clear title and description (e.g., "Add Discord link to navigation").
- Reference any related issues.
- Automated preview deployments are available for PRs with collaboration rights β test your changes live via the provided link.
- Address reviewer feedback promptly to expedite merging.
- Follow TypeScript and Astro best practices.
- Keep commits descriptive and atomic.
- Update README/docs for new features.
- Ensure accessibility and performance are maintained.
- No heavy frameworks; stick to vanilla CSS/JS where possible.
For questions, join our Discord or open an issue. Thanks for contributing! π
Since this is a static site, here are some ways to automate image management if you're self-hosting:
- Upload images via GitHub web interface or git push
- Automatically generate thumbnails on push
- Auto-commit generated files and metadata
- Trigger rebuild and deployment
Example workflow:
name: Process New Images
on:
push:
paths:
- 'public/images/original/**'
jobs:
process:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Generate thumbnails
run: pnpm run gen:thumbs
- name: Commit generated files
run: |
git config user.name github-actions
git add public/images/thumbs
git commit -m "chore: generate thumbnails"
git push- Build a simple Node.js/Python script
- Extract metadata from EXIF data automatically
- Create markdown files with proper frontmatter
- Commit and push changes programmatically
Example concept:
// upload.js - Run locally to add images
const sharp = require('sharp');
const fs = require('fs');
const path = require('path');
async function processImage(imagePath) {
const metadata = await sharp(imagePath).metadata();
const id = path.basename(imagePath, path.extname(imagePath));
// Generate thumbnails
await generateThumbnails(imagePath, id);
// Create markdown file
const mdContent = `---
id: "${id}"
title: "${id}"
slug: "${id}"
date: "${new Date().toISOString()}"
fullsizePath: "/images/original/${path.basename(imagePath)}"
thumbPath: "/images/thumbs/${id}-400.webp"
width: ${metadata.width}
height: ${metadata.height}
---`;
fs.writeFileSync(`src/content/photos/${id}.md`, mdContent);
}- Use a headless CMS (Strapi, Directus, Sanity)
- Fetch images and metadata during Astro build process
- Keep the static site benefits with dynamic content source
- Use the existing external download feature as a starting point
- Mount a volume for images
- Add images via SFTP/FTP
- Use inotify to watch for new files
- Automatically trigger rebuild
This gallery does NOT provide these features out of the box. You need to implement them yourself based on your infrastructure and requirements.
- Sorting: Photos are automatically sorted by
date(newest first, precise to milliseconds), name, or random. - Hero Section: Customizable intro area in
index.astro. - Lightbox: Uses GLightbox for accessibility and performance.
- Thumbnails: Generated via Sharp in WebP format.
- Dark Mode: Flicker-free, stored in localStorage, uses CSS variables.
- Performance: Images lazy-load; CSS is optimized.
- Header: Transparent, sticky, with blur and mobile menu.
- Background Effects: Blurred images with overlay for Light/Dark Mode to create an elegant look.
- Minimal Frameworks: Pure Astro + TypeScript + CSS/JS, Preact only for lightweight client interactions.
- Build: Includes thumbnail generation in
buildscript for production. - External Downloads: Optional feature for fetching content from internal/external servers. Configure via environment variables and
src/config/externaldownload.cjs. If disabled or no URLs set, the build runs normally without downloads.
MIT License
Copyright (c) 2025 inventory69 & Hyphonical
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
