Add dark mode support with logo_dark and repo_icon_dark variants

Introduces logo_dark and repo_icon_dark config options that serve alternate
images to dark-OS browsers via HTML <picture> elements, and adds a CSS
prefers-color-scheme dark block to the default header template.
This commit is contained in:
James Coleman 2026-06-05 12:30:09 -05:00
parent 81be7ddd2e
commit 782c24e9f5
4 changed files with 59 additions and 7 deletions

View file

@ -460,6 +460,9 @@ A title for the repo to show instead of the directory name.
### repo_icon ### repo_icon
The repo icon, will default to tux if not defined. The icon can be defined as an http(s) link, file path, a file stored in the template directory, or png image name from [Dashboard Icons](https://github.com/walkxcode/dashboard-icons/tree/main/png). The script will automatically make a copy or download the icon to the image folder. The repo icon, will default to tux if not defined. The icon can be defined as an http(s) link, file path, a file stored in the template directory, or png image name from [Dashboard Icons](https://github.com/walkxcode/dashboard-icons/tree/main/png). The script will automatically make a copy or download the icon to the image folder.
### repo_icon_dark
The dark-mode variant of the repo icon. Accepts the same sources as `repo_icon`. When defined, browsers that detect a dark OS theme will display this icon instead. If omitted, the light icon is used in all themes.
### repo_description ### repo_description
A description to show at the bottom of the repo card. A description to show at the bottom of the repo card.
@ -481,6 +484,7 @@ If you have a repo that is not synced via the mirror-sync, but want to customize
* section * section
* repo_title * repo_title
* repo_icon * repo_icon
* repo_icon_dark
* repo_description * repo_description
* repo_skip * repo_skip
* disable_size_calc * disable_size_calc
@ -514,6 +518,9 @@ A title for the mirror, defaults to the name if unset.
### logo ### logo
The logo, will default to tux if not defined. The logo can be defined as an http(s) link, file path, a file stored in the template directory, or png image name from [Dashboard Icons](https://github.com/walkxcode/dashboard-icons/tree/main/png). The script will automatically make a copy or download the icon to the image folder. The logo, will default to tux if not defined. The logo can be defined as an http(s) link, file path, a file stored in the template directory, or png image name from [Dashboard Icons](https://github.com/walkxcode/dashboard-icons/tree/main/png). The script will automatically make a copy or download the icon to the image folder.
### logo_dark
The dark-mode variant of the mirror logo. Accepts the same sources as `logo`. When defined, browsers that detect a dark OS theme will display this logo instead. If omitted, the light logo is used in all themes.
### description ### description
A description to place below the logo that can be HTML formatted. A description to place below the logo that can be HTML formatted.
@ -530,6 +537,7 @@ MIRRORS="mirror_example"
mirror_example_path="/home/mirror/mirror_docroot" mirror_example_path="/home/mirror/mirror_docroot"
mirror_example_title="My company" mirror_example_title="My company"
mirror_example_logo="http://example.com/logo.png" mirror_example_logo="http://example.com/logo.png"
mirror_example_logo_dark="http://example.com/logo-dark.png"
mirror_example_description="A public mirror provided by this cool company." mirror_example_description="A public mirror provided by this cool company."
mirror_example_provider_site="http://www.example.com/" mirror_example_provider_site="http://www.example.com/"
mirror_example_provider_name="Company" mirror_example_provider_name="Company"
@ -548,6 +556,8 @@ Default templates:
* footer.html - The footer of the index. * footer.html - The footer of the index.
* footer.txt - Template for the global footer file. * footer.txt - Template for the global footer file.
The default `header.html` template includes a `@media (prefers-color-scheme: dark)` CSS block that automatically switches the page to a dark theme when the OS reports a dark preference. The logo and repo icon templates use the HTML `<picture>` element so that a dark variant image is served to dark-mode browsers when `logo_dark` or `repo_icon_dark` is configured.
## Configurations of general defaults. ## Configurations of general defaults.
### index_generate ### index_generate

View file

@ -10,7 +10,7 @@ PATH="/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:$HOME/.local/
# Variables about this program. # Variables about this program.
PROGRAM="mirror-file-generator" PROGRAM="mirror-file-generator"
VERSION="20260602" VERSION="20260605"
PIDPATH="/tmp" PIDPATH="/tmp"
PIDFILE="${PIDPATH}/${PROGRAM}.pid" PIDFILE="${PIDPATH}/${PROGRAM}.pid"
LOGFILE="/var/log/mirror-sync/$PROGRAM.log" LOGFILE="/var/log/mirror-sync/$PROGRAM.log"
@ -169,6 +169,7 @@ read_config() {
eval section="\${${MODULE}_section:-}" eval section="\${${MODULE}_section:-}"
eval repo_title="\${${MODULE}_repo_title:-}" eval repo_title="\${${MODULE}_repo_title:-}"
eval icon="\${${MODULE}_repo_icon:-}" eval icon="\${${MODULE}_repo_icon:-}"
eval icon_dark="\${${MODULE}_repo_icon_dark:-}"
eval repo_description="\${${MODULE}_repo_description:-}" eval repo_description="\${${MODULE}_repo_description:-}"
eval disable_size_calc="\${${MODULE}_disable_size_calc:-0}" eval disable_size_calc="\${${MODULE}_disable_size_calc:-0}"
eval repo_skip="\${${MODULE}_repo_skip:-0}" eval repo_skip="\${${MODULE}_repo_skip:-0}"
@ -312,6 +313,7 @@ for ((i=0; i<${#selected_mirrors[@]}; i++)); do
title=$(html_encode "$title") title=$(html_encode "$title")
export title export title
eval logo="\${${mirror}_logo:-}" eval logo="\${${mirror}_logo:-}"
eval logo_dark="\${${mirror}_logo_dark:-}"
eval description="\${${mirror}_description:-}" eval description="\${${mirror}_description:-}"
export description export description
eval provider_site="\${${mirror}_provider_site:-}" eval provider_site="\${${mirror}_provider_site:-}"
@ -330,6 +332,15 @@ for ((i=0; i<${#selected_mirrors[@]}; i++)); do
logo_relative=$(html_encode "$(image_copy "${logo:-$icons_default_img}" logo)") logo_relative=$(html_encode "$(image_copy "${logo:-$icons_default_img}" logo)")
export logo_relative export logo_relative
# If a dark logo is configured, copy it and export a <source> tag; otherwise export empty.
if [[ -n ${logo_dark:-} ]]; then
logo_dark_relative=$(html_encode "$(image_copy "$logo_dark" logo_dark)")
logo_dark_source="<source srcset=\"$logo_dark_relative\" media=\"(prefers-color-scheme: dark)\" />"
else
logo_dark_source=""
fi
export logo_dark_source
# Default index file path. # Default index file path.
index_file_path="$path/$index_file_name" index_file_path="$path/$index_file_name"
index_file_temp="$index_file_path.build" index_file_temp="$index_file_path.build"
@ -480,9 +491,10 @@ for ((i=0; i<${#selected_mirrors[@]}; i++)); do
# If we should skip this repo, continue to the next. # If we should skip this repo, continue to the next.
if ((${repo_skip:-0})); then if ((${repo_skip:-0})); then
# Unset all vars for next repo. # Unset all vars for next repo.
unset repo_path repo_icon repo_title repo_size repo_size_kb \ unset repo_path repo_icon repo_icon_dark repo_icon_dark_source \
repo_title repo_size repo_size_kb \
repo_sync_time repo_description timestamp dusum section \ repo_sync_time repo_description timestamp dusum section \
icon repo_skip disable_size_calc timestamp_file_stat icon icon_dark repo_skip disable_size_calc timestamp_file_stat
continue continue
fi fi
@ -516,6 +528,15 @@ for ((i=0; i<${#selected_mirrors[@]}; i++)); do
repo_icon=$(html_encode "$(image_copy "${icon:-$icons_default_img}" "$dir_name")") repo_icon=$(html_encode "$(image_copy "${icon:-$icons_default_img}" "$dir_name")")
export repo_icon export repo_icon
# If a dark icon is configured, copy it and export a <source> tag; otherwise export empty.
if [[ -n ${icon_dark:-} ]]; then
repo_icon_dark=$(html_encode "$(image_copy "$icon_dark" "${dir_name}_dark")")
repo_icon_dark_source="<source srcset=\"$repo_icon_dark\" media=\"(prefers-color-scheme: dark)\" />"
else
repo_icon_dark_source=""
fi
export repo_icon_dark_source
# If repo size is undefined, check if an unknown repo directory size exists. # If repo size is undefined, check if an unknown repo directory size exists.
if [[ -z ${repo_size:-} ]] && ((${disable_size_calc:-0} == 0)); then if [[ -z ${repo_size:-} ]] && ((${disable_size_calc:-0} == 0)); then
unknown_path="$dir_sizes_unknown_path/$mirror/$dir_name" unknown_path="$dir_sizes_unknown_path/$mirror/$dir_name"
@ -572,9 +593,10 @@ for ((i=0; i<${#selected_mirrors[@]}; i++)); do
fi fi
# Unset all vars for next repo. # Unset all vars for next repo.
unset repo_path repo_icon repo_title repo_size repo_size_kb \ unset repo_path repo_icon repo_icon_dark repo_icon_dark_source \
repo_title repo_size repo_size_kb \
repo_sync_time repo_description timestamp dusum section \ repo_sync_time repo_description timestamp dusum section \
icon repo_skip disable_size_calc timestamp_file_stat icon icon_dark repo_skip disable_size_calc timestamp_file_stat
done done
# If the index should be generated, add each section and footer. # If the index should be generated, add each section and footer.

View file

@ -87,11 +87,28 @@
width: auto; width: auto;
} }
} }
@media (prefers-color-scheme: dark) {
body {
background-color: #1e1e1e;
color: #e0e0e0;
}
.repo-list a:link {
color: #e0e0e0;
}
.repo {
background-color: #2d2d2d;
color: #e0e0e0;
}
}
</style> </style>
</head> </head>
<body> <body>
<div class="header"> <div class="header">
<img id="logo" src="${logo_relative}" alt="${title}" /> <picture>
${logo_dark_source}
<img id="logo" src="${logo_relative}" alt="${title}" />
</picture>
<div class="mirror-description"> <div class="mirror-description">
${description} ${description}
</div> </div>

View file

@ -1,6 +1,9 @@
<a href="${repo_path}"> <a href="${repo_path}">
<div class="repo"> <div class="repo">
<img class="repo-icon" src="${repo_icon}" alt="${repo_title}" /> <picture>
${repo_icon_dark_source}
<img class="repo-icon" src="${repo_icon}" alt="${repo_title}" />
</picture>
<div class="repo-body"> <div class="repo-body">
<div class="repo-title">${repo_title}</div> <div class="repo-title">${repo_title}</div>
<div class="repo-size">Size: ${repo_size}</div> <div class="repo-size">Size: ${repo_size}</div>