Merge branch 'AngelJumbo:main' into main

This commit is contained in:
Topplehawk
2026-03-15 18:54:45 -06:00
committed by GitHub
326 changed files with 708 additions and 148 deletions
+1
View File
@@ -1,2 +1,3 @@
*.html
thumbnails/
*.js
+33 -1
View File
@@ -5,10 +5,11 @@ A place where to find gruvbox theme wallpapers.
1. Fork the project.
2. Add the wallpapers that you have in their respective folder.
3. For light variants: Place the light-themed wallpapers in a light/ subfolder within the category.
3. Make a pull request.
> [!CAUTION]
> - Use files under 25mg
> - Use files under 25mb
> - Avoid spaces in names
> [!TIP]
@@ -16,6 +17,37 @@ A place where to find gruvbox theme wallpapers.
I tend to accept any contributions, but let's keep the site with images that match or look good with the gruvbox's color scheme :').
## Nix package
1. **Use [nix flakes](https://wiki.nixos.org/wiki/Flakes)**:
2. Add the following to your `flake.nix` file:
```nix
inputs = {
gruvbox-wallpapers.url = "github:AngelJumbo/gruvbox-wallpapers";
# ...
};
```
3. Then, in your Home Manager configuration:
```nix
{
inputs,
pkgs,
...
}: {
home.file = {
"path/to/dir" = {
source = inputs.gruvbox-wallpapers.packages."${pkgs.stdenv.hostPlatform.system}".default;
recursive = true;
};
};
}
```
## Disclaimer
Most of the images shared here are community contributions, and their original sources are often unknown. If you are the rightful owner of any image and would like it removed, please contact me by opening an issue. For any use beyond personal scope, I recommend performing a reverse image search to verify the origin.
-60
View File
@@ -1,60 +0,0 @@
function loadPage(section, page) {
let buttons = document.getElementById(section).getElementsByTagName("button");
let currSel;
for (let i in buttons) {
if (buttons[i].classList?.contains("selected")) {
if (page - 1 == i) return;
else {
currSel = buttons[i];
break;
}
}
}
fetch(section + "_page" + page + ".html")
.then((response) => response.text())
.then((data) => {
document.getElementById(section + "-content").innerHTML = data;
if (currSel) {
currSel.classList.remove("selected");
}
document.getElementById(section).getElementsByTagName("button")[page - 1].classList.add("selected");
});
}
function activeSection(section) {
hideAll();
const targetSection = document.getElementById(section);
targetSection.focus();
targetSection.classList.add("selected");
setTimeout(() => {
targetSection.scrollIntoView({ block: "start", behavior: "smooth" });
}, 100);
}
function hideAll() {
document.querySelectorAll(".section").forEach((s) => {
s.classList.remove("selected");
});
}
const HIDDEN_CLASS = "hidden";
function hideThemeSwitcher(currentTheme) {
if (currentTheme == "dark") {
document.getElementById("dark-icon")?.classList.add(HIDDEN_CLASS);
document.getElementById("light-icon")?.classList.remove(HIDDEN_CLASS);
} else {
document.getElementById("light-icon")?.classList.add(HIDDEN_CLASS);
document.getElementById("dark-icon")?.classList.remove(HIDDEN_CLASS);
}
}
function setTheme(newTheme) {
document.documentElement.style.setProperty("color-scheme", newTheme);
hideThemeSwitcher(newTheme);
}
function switchTheme() {
setTheme(document.documentElement.style.getPropertyValue("color-scheme")=="dark"?"light":"dark");
}
+420 -86
View File
@@ -26,147 +26,481 @@ generate_thumbnails() {
rm -rf thumbnails/*
# Count total number of images
total_images=$(find ./wallpapers -type f | wc -l)
total_images=$(find ./wallpapers -type f \( -iname "*.jpg" -o -iname "*.jpeg" -o -iname "*.png" \) | wc -l)
current_image=0
for section_dir in ./wallpapers/*; do
section_name="${section_dir##*/}"
mkdir -p "thumbnails/$section_name"
# Process dark images (base directory)
for img in "$section_dir"/*; do
[ -f "$img" ] || continue
case "$img" in
*.jpg|*.jpeg|*.png|*.JPG|*.JPEG|*.PNG) ;;
*) continue ;;
esac
current_image=$((current_image + 1))
local img_filename="${img##*/}"
local thumbnail="thumbnails/$section_name/$img_filename"
echo "($current_image/$total_images): $img -> $thumbnail"
convert "$img" -resize 300x300 "$thumbnail"
done
# Process light images if light folder exists
if [ -d "$section_dir/light" ]; then
mkdir -p "thumbnails/$section_name/light"
for img in "$section_dir/light"/*; do
[ -f "$img" ] || continue
case "$img" in
*.jpg|*.jpeg|*.png|*.JPG|*.JPEG|*.PNG) ;;
*) continue ;;
esac
current_image=$((current_image + 1))
local img_filename="${img##*/}"
local thumbnail="thumbnails/$section_name/light/$img_filename"
echo "($current_image/$total_images): $img -> $thumbnail"
convert "$img" -resize 300x300 "$thumbnail"
done
fi
done
echo "Thumbnail generation complete: $total_images images processed"
}
write_section_header() {
echo "write section header ..."
echo "<h2 class='s$1 clickable' onclick='activeSection(\"$2\")' >" >>$3
echo "$2" | tr a-z A-Z >>$3
echo "</h2>" >>$3
check_has_light_folder() {
local section_dir=$1
[ -d "$section_dir/light" ] && echo "true" || echo "false"
}
write_img() {
echo "write image ..."
# Remove leading './' from path if present
local img_path="${1#./}"
local section_name=$(echo "$img_path" | cut -d'/' -f2)
local img_filename="${1##*/}"
# Use web-friendly paths (no leading ./)
local thumbnail_path="thumbnails/$section_name/$img_filename"
echo " <a target='_blank' href='$img_path'>
<img loading='lazy' src='$thumbnail_path' alt='$img_filename' width='200'></a>" >>$2
count_images_in_dir() {
local dir=$1
find "$dir" -maxdepth 1 -type f \( -iname "*.jpg" -o -iname "*.jpeg" -o -iname "*.png" \) | wc -l
}
rm *.html
create_section_data() {
local section=$1
local subdir=$2
local maxPerPage=8
local has_light=$(check_has_light_folder "$subdir")
echo "Creating data for section: $section"
# Create JSON data file for the section
local data_file="${section}_data.js"
cat > "$data_file" << EOF
// Data for $section section
window.sectionData = window.sectionData || {};
window.sectionData['$section'] = {
hasLight: $has_light,
maxPerPage: $maxPerPage,
dark: [
EOF
touch ./index.html
# Generate dark images data
for wallpaper in "$subdir"/*; do
[ -f "$wallpaper" ] || continue
case "$wallpaper" in
*.jpg|*.jpeg|*.png|*.JPG|*.JPEG|*.PNG) ;;
*) continue ;;
esac
local img_path="${wallpaper#./}"
local img_filename="${wallpaper##*/}"
local thumbnail_path="thumbnails/$section/$img_filename"
echo " { src: '$img_path', thumb: '$thumbnail_path', alt: '$img_filename' }," >> "$data_file"
done
echo "<!DOCTYPE html>
# Generate light images data if exists
echo " ]," >> "$data_file"
if [ "$has_light" = "true" ]; then
echo " light: [" >> "$data_file"
for wallpaper in "$subdir/light"/*; do
[ -f "$wallpaper" ] || continue
case "$wallpaper" in
*.jpg|*.jpeg|*.png|*.JPG|*.JPEG|*.PNG) ;;
*) continue ;;
esac
local img_path="${wallpaper#./}"
local img_filename="${wallpaper##*/}"
local thumbnail_path="thumbnails/$section/light/$img_filename"
echo " { src: '$img_path', thumb: '$thumbnail_path', alt: '$img_filename' }," >> "$data_file"
done
echo " ]" >> "$data_file"
else
echo " light: []" >> "$data_file"
fi
echo "};" >> "$data_file"
}
# Clean up old files
rm -f *.html *.js
# Generate thumbnails
generate_thumbnails
# Create main index.html
cat > index.html << 'EOF'
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='utf-8'>
<meta name='viewport' content='width=device-width, initial-scale=1.0'>
<link rel='stylesheet' type='text/css' href='style.css'>
<title>Gruvbox wallpapers</title>
<script src='app.js' defer></script>
<script src='https://kit.fontawesome.com/13865d7982.js' crossorigin='anonymous' defer></script>
<style>
.section-content {
margin: 20px 0;
opacity: 0;
visibility: hidden;
transition: all 0.3s ease-in-out;
transform: translateY(10px);
}
.section.selected .section-content {
opacity: 1;
visibility: visible;
transform: translateY(0);
}
/* Theme switcher styles */
.section-theme-switcher {
text-align: center;
margin: 15px 0;
opacity: 0;
visibility: hidden;
transition: all 0.3s ease-in-out;
transform: translateY(-10px);
}
.section.selected .section-theme-switcher {
opacity: 1;
visibility: visible;
transform: translateY(0);
}
.theme-toggle {
display: inline-flex;
gap: 0;
}
.theme-btn {
padding: 10px 20px;
background-color: var(--bg-color-reverse);
color: var(--fg-color-reverse);
border: 2px solid transparent;
cursor: pointer;
transition: border-color 0.3s ease;
font-weight: 600;
font-size: 14px;
argin: 0 5px 0 5px;
}
.theme-btn:hover {
background-color: var(--bg-color-reverse);
}
.theme-btn.active {
border: 4px solid var(--fg-color);
background-color: var(--bg-color);
color: var(--fg-color);
}
.subsection-content {
display: none;
}
.subsection-content.active {
display: block;
}
.loading {
text-align: center;
padding: 40px;
color: var(--fg-color);
}
</style>
</head>
<body>
<div class='float-btns'>
<a href='https://github.com/AngelJumbo/gruvbox-wallpapers' target='_blank' class='btn float-btn' title='Source code' >
<a href='https://github.com/AngelJumbo/gruvbox-wallpapers' target='_blank' class='btn float-btn' title='Source code'>
<span>
<i class='fa-brands fa-github'></i>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-github-icon lucide-github"><path d="M15 22v-4a4.8 4.8 0 0 0-1-3.5c3 0 6-2 6-5.5.08-1.25-.27-2.48-1-3.5.28-1.15.28-2.35 0-3.5 0 0-1 0-3 1.5-2.64-.5-5.36-.5-8 0C6 2 5 2 5 2c-.3 1.15-.3 2.35 0 3.5A5.403 5.403 0 0 0 4 9c0 3.5 3 5.5 6 5.5-.39.49-.68 1.05-.85 1.65-.17.6-.22 1.23-.15 1.85v4"/><path d="M9 18c-4.51 2-5-2-7-2"/></svg>
</span>
</a>
<button onclick='switchTheme()' class='btn float-btn' title='Switch theme'>
<button onclick='switchMainTheme()' class='btn float-btn' title='Switch theme'>
<span>
<i id='light-icon' class='fa-solid fa-sun'></i>
<i id='dark-icon' class='fa-solid fa-moon'></i>
<svg id="light-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-sun-icon lucide-sun"><circle cx="12" cy="12" r="4"/><path d="M12 2v2"/><path d="M12 20v2"/><path d="m4.93 4.93 1.41 1.41"/><path d="m17.66 17.66 1.41 1.41"/><path d="M2 12h2"/><path d="M20 12h2"/><path d="m6.34 17.66-1.41 1.41"/><path d="m19.07 4.93-1.41 1.41"/></svg>
<svg id="dark-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-moon-icon lucide-moon"><path d="M20.985 12.486a9 9 0 1 1-9.473-9.472c.405-.022.617.46.402.803a6 6 0 0 0 8.268 8.268c.344-.215.825-.004.803.401"/></svg>
</span>
</button>
</div>
<main>
<h1>Gruvbox Wallpapers</h1>" >./index.html
<h1>Gruvbox Wallpapers</h1>
EOF
# Generate sections
color=1
maxPerPage=8
declare -a sections
generate_thumbnails
declare -a sections_with_light
for subdir in ./wallpapers/*; do
section="${subdir##*/}"
sections+=("$section")
write_section_header $color "$section" ./index.html
page=1
img_count=0
subhtml="${section}_page${page}.html"
touch ./$subhtml
echo "<div class='section' id='$section'>" >>./index.html
echo "<div class='pager'>" >>./index.html
countImgs=$(find "$subdir" -type f | wc -l)
countImgs=$((countImgs - 1))
for i in $(seq 1 $((($countImgs / $maxPerPage) + 1))); do
echo "<button class='btn pager-btn' onclick='loadPage(\"$section\", $i)'>$i</button>" >>./index.html
done
echo "</div>" >>./index.html
echo "<div id='$section-content'>" >>./index.html
echo "<div class='c'>" >>./$subhtml
for wallpaper in ${subdir}/*; do
if [ "$img_count" -ge $maxPerPage ]; then
echo "</div>" >>./$subhtml
page=$((page + 1))
subhtml="${section}_page${page}.html"
touch ./$subhtml
echo "<div class='c'>" >>./$subhtml
img_count=0
fi
write_img "$wallpaper" "./$subhtml"
img_count=$((img_count + 1))
done
echo "</div>" >>./$subhtml
echo "</div>" >>./index.html
echo "</div>" >>./index.html
has_light=$(check_has_light_folder "$subdir")
if [ "$has_light" = "true" ]; then
sections_with_light+=("$section")
fi
# Create the data file
create_section_data "$section" "$subdir"
# Add section to main page
echo " <h2 class='s$color clickable' onclick='activeSection(\"$section\")'>" >> index.html
echo " $(echo "$section" | tr a-z A-Z)" >> index.html
echo " </h2>" >> index.html
echo " <div class='section' id='$section'>" >> index.html
# Add theme switcher if section has light variant
if [ "$has_light" = "true" ]; then
echo " <div class='section-theme-switcher'>" >> index.html
echo " <div class='theme-toggle'>" >> index.html
echo " <button class='theme-btn active' onclick='switchSectionTheme(\"$section\", \"dark\")' id='$section-dark-btn'>Dark</button>" >> index.html
echo " <button class='theme-btn' onclick='switchSectionTheme(\"$section\", \"light\")' id='$section-light-btn'>Light</button>" >> index.html
echo " </div>" >> index.html
echo " </div>" >> index.html
fi
echo " <div class='section-content' id='$section-content'>" >> index.html
echo " <div class='loading'>Loading...</div>" >> index.html
echo " </div>" >> index.html
echo " </div>" >> index.html
color=$((color + 1))
if [ "$color" -eq 8 ]; then
color=1
fi
done
echo "</main>" >>./index.html
echo "<script>
window.onload = () => {" >>./index.html
# Add all section data scripts
for section in "${sections[@]}"; do
echo " loadPage('$section', 1);" >>./index.html
echo " <script src='${section}_data.js'></script>" >> index.html
done
echo "
activeSection('${sections[0]}');
if (window.matchMedia('(prefers-color-scheme: dark)').matches){
setTheme('dark');
}else{
setTheme('light');
}
}
</script>" >>./index.html
# Add main JavaScript
cat >> index.html << 'EOF'
<script>
// Global state
let activeSectionName = null;
let sectionStates = {};
echo "</body>
</html>" >>./index.html
// Initialize section state
function initSectionState(section) {
if (!sectionStates[section]) {
sectionStates[section] = {
currentTheme: 'dark',
currentPages: { dark: 1, light: 1 },
loaded: false
};
}
}
function activeSection(section) {
hideAllSections();
const targetSection = document.getElementById(section);
targetSection.classList.add('selected');
activeSectionName = section;
initSectionState(section);
// Load content if not already loaded
if (!sectionStates[section].loaded) {
loadSectionContent(section);
}
// Reset to dark theme
if (window.sectionData[section].hasLight) {
switchSectionTheme(section, 'dark');
}
setTimeout(() => {
targetSection.scrollIntoView({ block: 'start', behavior: 'smooth' });
}, 100);
}
function hideAllSections() {
document.querySelectorAll('.section').forEach(s => {
s.classList.remove('selected');
});
activeSectionName = null;
}
function loadSectionContent(section) {
const contentDiv = document.getElementById(section + '-content');
const data = window.sectionData[section];
if (!data) {
contentDiv.innerHTML = '<div class="loading">Error: Section data not found</div>';
return;
}
let html = '';
// Dark content
if (data.dark.length > 0) {
html += `<div class="subsection-content active" id="${section}-dark">`;
// Dark pagination
const darkPages = Math.ceil(data.dark.length / data.maxPerPage);
if (darkPages > 1) {
html += '<div class="pager">';
for (let i = 1; i <= darkPages; i++) {
const selectedClass = i === 1 ? ' selected' : '';
html += `<button class="btn pager-btn${selectedClass}" onclick="loadPage('${section}', 'dark', ${i})">${i}</button>`;
}
html += '</div>';
}
html += `<div id="${section}-dark-content">`;
html += generateImageGrid(data.dark.slice(0, data.maxPerPage));
html += '</div>';
html += '</div>';
}
// Light content
if (data.hasLight && data.light.length > 0) {
html += `<div class="subsection-content" id="${section}-light">`;
// Light pagination
const lightPages = Math.ceil(data.light.length / data.maxPerPage);
if (lightPages > 1) {
html += '<div class="pager">';
for (let i = 1; i <= lightPages; i++) {
const selectedClass = i === 1 ? ' selected' : '';
html += `<button class="btn pager-btn${selectedClass}" onclick="loadPage('${section}', 'light', ${i})">${i}</button>`;
}
html += '</div>';
}
html += `<div id="${section}-light-content">`;
html += generateImageGrid(data.light.slice(0, data.maxPerPage));
html += '</div>';
html += '</div>';
}
contentDiv.innerHTML = html;
sectionStates[section].loaded = true;
}
function generateImageGrid(images) {
let html = '<div class="c">';
images.forEach(img => {
html += `<a target="_blank" href="${img.src}">
<img loading="lazy" src="${img.thumb}" alt="${img.alt}" width="200">
</a>`;
});
html += '</div>';
return html;
}
function switchSectionTheme(section, theme) {
const darkBtn = document.getElementById(section + '-dark-btn');
const lightBtn = document.getElementById(section + '-light-btn');
const darkContent = document.getElementById(section + '-dark');
const lightContent = document.getElementById(section + '-light');
initSectionState(section);
sectionStates[section].currentTheme = theme;
// Update button states
if (darkBtn && lightBtn) {
darkBtn.classList.toggle('active', theme === 'dark');
lightBtn.classList.toggle('active', theme === 'light');
}
// Switch content visibility
if (darkContent && lightContent) {
darkContent.classList.toggle('active', theme === 'dark');
lightContent.classList.toggle('active', theme === 'light');
}
// Load first page of selected theme
loadPage(section, theme, 1);
}
function loadPage(section, theme, page) {
const data = window.sectionData[section];
const images = data[theme];
const startIdx = (page - 1) * data.maxPerPage;
const endIdx = Math.min(startIdx + data.maxPerPage, images.length);
const pageImages = images.slice(startIdx, endIdx);
const contentDiv = document.getElementById(`${section}-${theme}-content`);
if (contentDiv) {
contentDiv.innerHTML = generateImageGrid(pageImages);
}
// Update pagination buttons
const pagerButtons = document.getElementById(`${section}-${theme}`)?.getElementsByClassName('pager-btn');
if (pagerButtons) {
for (let i = 0; i < pagerButtons.length; i++) {
pagerButtons[i].classList.toggle('selected', i === page - 1);
}
}
initSectionState(section);
sectionStates[section].currentPages[theme] = page;
}
// Main theme functions
const HIDDEN_CLASS = 'hidden';
function hideThemeSwitcher(currentTheme) {
if (currentTheme === 'dark') {
document.getElementById('dark-icon')?.classList.add(HIDDEN_CLASS);
document.getElementById('light-icon')?.classList.remove(HIDDEN_CLASS);
} else {
document.getElementById('light-icon')?.classList.add(HIDDEN_CLASS);
document.getElementById('dark-icon')?.classList.remove(HIDDEN_CLASS);
}
}
function setMainTheme(newTheme) {
document.documentElement.style.setProperty('color-scheme', newTheme);
hideThemeSwitcher(newTheme);
}
function switchMainTheme() {
const current = document.documentElement.style.getPropertyValue('color-scheme');
setMainTheme(current === 'dark' ? 'light' : 'dark');
}
// Initialize
document.addEventListener('DOMContentLoaded', function() {
EOF
echo " activeSection('${sections[0]}');" >> index.html
cat >> index.html << 'EOF'
if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
setMainTheme('dark');
} else {
setMainTheme('light');
}
});
</script>
</body>
</html>
EOF
echo "Build complete! Generated $(echo ${#sections[@]}) sections with dynamic content loading."
echo "Sections with light variants: ${sections_with_light[*]}"
Generated
+27
View File
@@ -0,0 +1,27 @@
{
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1761907660,
"narHash": "sha256-kJ8lIZsiPOmbkJypG+B5sReDXSD1KGu2VEPNqhRa/ew=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "2fb006b87f04c4d3bdf08cfdbc7fab9c13d94a15",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"nixpkgs": "nixpkgs"
}
}
},
"root": "root",
"version": 7
}
+48
View File
@@ -0,0 +1,48 @@
{
description = "Gruvbox-inspired wallpaper collection";
inputs.nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
outputs = {
self,
nixpkgs,
}: let
systems = ["x86_64-linux" "aarch64-linux" "x86_64-darwin" "aarch64-darwin"];
forAllSystems = f: nixpkgs.lib.genAttrs systems (system: f (import nixpkgs {inherit system;}));
mkWallpapersPkg = pkgs: name: path:
pkgs.stdenv.mkDerivation {
pname = "gruvbox-wallpapers-${name}";
version = self.shortRev or "dev";
src = ./.;
installPhase = ''
mkdir -p $out
${
if name == "default"
then "find wallpapers -type f -exec cp {} $out/ \\;"
else "cp -r ${path}/* $out/"
}
'';
};
subdirs = {
default = "wallpapers";
anime = "wallpapers/anime";
brands = "wallpapers/brands";
game = "wallpapers/game";
minimalistic = "wallpapers/minimalistic";
mix = "wallpapers/mix";
painting = "wallpapers/painting";
photography = "wallpapers/photography";
pixelart = "wallpapers/pixelart";
renders = "wallpapers/renders";
};
in {
formatter = forAllSystems (pkgs: pkgs.alejandra);
packages = forAllSystems (
pkgs:
nixpkgs.lib.mapAttrs (name: path: mkWallpapersPkg pkgs name path) subdirs
);
};
}
-1
View File
@@ -1,4 +1,3 @@
main {
display: flex;
flex-direction: column;
+179
View File
@@ -137,6 +137,7 @@ img:hover {
}
.float-btns {
z-index: 10;
position: fixed;
bottom: 0;
right: 0;
@@ -161,3 +162,181 @@ img:hover {
.hidden {
display: none !important;
}
/* Add these styles to your existing style.css */
.subsection-theme-switch {
text-align: center;
margin: 10px 0;
}
.btn.subsection-theme-btn {
padding: 8px 16px;
font-size: 14px;
font-weight: 700;
margin: 0 4px;
border-radius: 4px;
&.selected {
background-color: var(--bg-color);
color: var(--fg-color);
border: 2px solid var(--fg-color);
}
&:not(.selected) {
opacity: 0.7;
}
}
.subsection-content {
transition: opacity 0.3s ease-in-out;
}
/* Ensure subsection content containers are properly spaced */
.section .subsection-content + .subsection-content {
margin-top: 0;
}
/* Enhanced theme switch styles - Add to your style.css */
.subsection-theme-switch {
text-align: center;
margin: 15px 0 20px 0;
opacity: 0;
visibility: hidden;
transition: all 0.3s ease-in-out;
transform: translateY(-10px);
}
/* Show theme switch only when section is selected */
.section.selected .subsection-theme-switch {
opacity: 1;
visibility: visible;
transform: translateY(0);
}
/* Theme switch container styled like a toggle */
.theme-toggle-container {
display: inline-flex;
background-color: var(--bg-color-3);
border-radius: 25px;
padding: 4px;
box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.2);
border: 2px solid var(--fg-color);
position: relative;
overflow: hidden;
}
.theme-toggle-container::before {
content: '';
position: absolute;
top: 4px;
left: 4px;
width: calc(50% - 4px);
height: calc(100% - 8px);
background: linear-gradient(135deg, var(--bg-color-reverse), var(--bg-color-3-switch));
border-radius: 20px;
transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
z-index: 1;
}
.theme-toggle-container.light-active::before {
transform: translateX(100%);
}
.btn.subsection-theme-btn {
padding: 12px 24px;
font-size: 14px;
font-weight: 700;
border: none;
background: transparent;
color: var(--fg-color);
cursor: pointer;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
position: relative;
z-index: 2;
border-radius: 20px;
min-width: 80px;
text-transform: uppercase;
letter-spacing: 0.5px;
&:hover {
background: transparent;
}
&.selected {
color: var(--fg-color-reverse);
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
}
&:not(.selected) {
opacity: 0.7;
}
/* Add subtle glow effect for selected button */
&.selected {
position: relative;
}
&.selected::after {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
border-radius: 20px;
background: rgba(255, 255, 255, 0.1);
pointer-events: none;
}
}
/* Animation for button text */
.btn.subsection-theme-btn {
position: relative;
overflow: hidden;
}
.btn.subsection-theme-btn::before {
content: attr(data-text);
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
transition: transform 0.3s ease;
opacity: 0;
}
.btn.subsection-theme-btn.selected::before {
opacity: 1;
transform: translate(-50%, -50%) scale(1.05);
}
svg {
stroke: var(--fg-color-reverse) !important;
}
/* Responsive adjustments */
@media screen and (max-width: 640px) {
.btn.subsection-theme-btn {
padding: 10px 20px;
font-size: 12px;
min-width: 70px;
}
.theme-toggle-container {
padding: 3px;
}
}
/* Dark mode specific adjustments */
@media (prefers-color-scheme: dark) {
.theme-toggle-container {
box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.4);
}
.theme-toggle-container::before {
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.5);
}
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 932 KiB

Before

Width:  |  Height:  |  Size: 2.1 MiB

After

Width:  |  Height:  |  Size: 2.1 MiB

Before

Width:  |  Height:  |  Size: 11 MiB

After

Width:  |  Height:  |  Size: 11 MiB

Before

Width:  |  Height:  |  Size: 8.3 MiB

After

Width:  |  Height:  |  Size: 8.3 MiB

Before

Width:  |  Height:  |  Size: 2.2 MiB

After

Width:  |  Height:  |  Size: 2.2 MiB

Before

Width:  |  Height:  |  Size: 4.4 MiB

After

Width:  |  Height:  |  Size: 4.4 MiB

Before

Width:  |  Height:  |  Size: 1.7 MiB

After

Width:  |  Height:  |  Size: 1.7 MiB

Before

Width:  |  Height:  |  Size: 12 MiB

After

Width:  |  Height:  |  Size: 12 MiB

Before

Width:  |  Height:  |  Size: 3.3 MiB

After

Width:  |  Height:  |  Size: 3.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 MiB

Before

Width:  |  Height:  |  Size: 4.0 MiB

After

Width:  |  Height:  |  Size: 4.0 MiB

Before

Width:  |  Height:  |  Size: 256 KiB

After

Width:  |  Height:  |  Size: 256 KiB

Before

Width:  |  Height:  |  Size: 291 KiB

After

Width:  |  Height:  |  Size: 291 KiB

Before

Width:  |  Height:  |  Size: 435 KiB

After

Width:  |  Height:  |  Size: 435 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1022 KiB

Before

Width:  |  Height:  |  Size: 14 MiB

After

Width:  |  Height:  |  Size: 14 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

Before

Width:  |  Height:  |  Size: 277 KiB

After

Width:  |  Height:  |  Size: 277 KiB

Before

Width:  |  Height:  |  Size: 277 KiB

After

Width:  |  Height:  |  Size: 277 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 365 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Before

Width:  |  Height:  |  Size: 1.2 MiB

After

Width:  |  Height:  |  Size: 1.2 MiB

Before

Width:  |  Height:  |  Size: 781 KiB

After

Width:  |  Height:  |  Size: 781 KiB

Before

Width:  |  Height:  |  Size: 6.3 KiB

After

Width:  |  Height:  |  Size: 6.3 KiB

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 MiB

Before

Width:  |  Height:  |  Size: 167 KiB

After

Width:  |  Height:  |  Size: 167 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Before

Width:  |  Height:  |  Size: 78 KiB

After

Width:  |  Height:  |  Size: 78 KiB

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 179 KiB

Before

Width:  |  Height:  |  Size: 79 KiB

After

Width:  |  Height:  |  Size: 79 KiB

Before

Width:  |  Height:  |  Size: 270 KiB

After

Width:  |  Height:  |  Size: 270 KiB

Before

Width:  |  Height:  |  Size: 343 KiB

After

Width:  |  Height:  |  Size: 343 KiB

Before

Width:  |  Height:  |  Size: 287 KiB

After

Width:  |  Height:  |  Size: 287 KiB

Before

Width:  |  Height:  |  Size: 283 KiB

After

Width:  |  Height:  |  Size: 283 KiB

Before

Width:  |  Height:  |  Size: 74 KiB

After

Width:  |  Height:  |  Size: 74 KiB

Before

Width:  |  Height:  |  Size: 286 KiB

After

Width:  |  Height:  |  Size: 286 KiB

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Before

Width:  |  Height:  |  Size: 56 KiB

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 MiB

Before

Width:  |  Height:  |  Size: 88 KiB

After

Width:  |  Height:  |  Size: 88 KiB

Before

Width:  |  Height:  |  Size: 100 KiB

After

Width:  |  Height:  |  Size: 100 KiB

Before

Width:  |  Height:  |  Size: 169 KiB

After

Width:  |  Height:  |  Size: 169 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 175 KiB

Before

Width:  |  Height:  |  Size: 71 KiB

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 MiB

Before

Width:  |  Height:  |  Size: 71 KiB

After

Width:  |  Height:  |  Size: 71 KiB

Before

Width:  |  Height:  |  Size: 59 KiB

After

Width:  |  Height:  |  Size: 59 KiB

Before

Width:  |  Height:  |  Size: 56 KiB

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 MiB

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Before

Width:  |  Height:  |  Size: 90 KiB

After

Width:  |  Height:  |  Size: 90 KiB

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 25 KiB

Before

Width:  |  Height:  |  Size: 56 KiB

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 734 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 574 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 344 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 184 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 MiB

Before

Width:  |  Height:  |  Size: 622 KiB

After

Width:  |  Height:  |  Size: 622 KiB

Before

Width:  |  Height:  |  Size: 7.8 MiB

After

Width:  |  Height:  |  Size: 7.8 MiB

Before

Width:  |  Height:  |  Size: 59 KiB

After

Width:  |  Height:  |  Size: 59 KiB

Before

Width:  |  Height:  |  Size: 1.4 MiB

After

Width:  |  Height:  |  Size: 1.4 MiB

Some files were not shown because too many files have changed in this diff Show More