Refactor
@@ -1,2 +1,3 @@
|
||||
*.html
|
||||
thumbnails/
|
||||
*.js
|
||||
@@ -5,6 +5,7 @@ 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]
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -26,59 +26,206 @@ 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")
|
||||
|
||||
touch ./index.html
|
||||
echo "Creating data for section: $section"
|
||||
|
||||
echo "<!DOCTYPE html>
|
||||
# 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
|
||||
|
||||
# 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
|
||||
|
||||
# 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'>
|
||||
@@ -87,7 +234,7 @@ echo "<!DOCTYPE html>
|
||||
<i class='fa-brands fa-github'></i>
|
||||
</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>
|
||||
@@ -95,78 +242,264 @@ echo "<!DOCTYPE html>
|
||||
</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
|
||||
has_light=$(check_has_light_folder "$subdir")
|
||||
|
||||
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
|
||||
if [ "$has_light" = "true" ]; then
|
||||
sections_with_light+=("$section")
|
||||
fi
|
||||
|
||||
write_img "$wallpaper" "./$subhtml"
|
||||
img_count=$((img_count + 1))
|
||||
done
|
||||
# Create the data file
|
||||
create_section_data "$section" "$subdir"
|
||||
|
||||
echo "</div>" >>./$subhtml
|
||||
# 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
|
||||
|
||||
echo "</div>" >>./index.html
|
||||
echo "</div>" >>./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[*]}"
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
main {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
@@ -137,6 +137,7 @@ img:hover {
|
||||
}
|
||||
|
||||
.float-btns {
|
||||
z-index: 10;
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
@@ -161,3 +162,177 @@ 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);
|
||||
}
|
||||
|
||||
/* 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
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: 2.5 MiB After Width: | Height: | Size: 2.5 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 |
|
Before Width: | Height: | Size: 932 KiB After Width: | Height: | Size: 932 KiB |
|
Before Width: | Height: | Size: 365 KiB After Width: | Height: | Size: 365 KiB |
|
Before Width: | Height: | Size: 14 MiB After Width: | Height: | Size: 14 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 |
|
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 |
|
After Width: | Height: | Size: 7.9 MiB |
|
After Width: | Height: | Size: 7.9 MiB |
|
After Width: | Height: | Size: 7.9 MiB |
|
After Width: | Height: | Size: 7.9 MiB |
|
Before Width: | Height: | Size: 167 KiB After Width: | Height: | Size: 167 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 |
|
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 |
|
After Width: | Height: | Size: 7.9 MiB |
|
After Width: | Height: | Size: 7.9 MiB |
|
After Width: | Height: | Size: 7.9 MiB |
|
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 |
|
Before Width: | Height: | Size: 71 KiB After Width: | Height: | Size: 71 KiB |
|
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 |
|
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 |
|
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 |
|
Before Width: | Height: | Size: 304 KiB After Width: | Height: | Size: 304 KiB |
|
Before Width: | Height: | Size: 618 KiB After Width: | Height: | Size: 618 KiB |
|
Before Width: | Height: | Size: 694 KiB After Width: | Height: | Size: 694 KiB |
|
Before Width: | Height: | Size: 9.2 MiB After Width: | Height: | Size: 9.2 MiB |
|
Before Width: | Height: | Size: 6.8 KiB After Width: | Height: | Size: 6.8 KiB |
|
Before Width: | Height: | Size: 1.4 MiB After Width: | Height: | Size: 1.4 MiB |
|
Before Width: | Height: | Size: 53 KiB After Width: | Height: | Size: 53 KiB |
|
Before Width: | Height: | Size: 183 KiB After Width: | Height: | Size: 183 KiB |
|
Before Width: | Height: | Size: 449 KiB After Width: | Height: | Size: 449 KiB |
|
Before Width: | Height: | Size: 5.6 MiB After Width: | Height: | Size: 5.6 MiB |
|
Before Width: | Height: | Size: 779 KiB After Width: | Height: | Size: 779 KiB |
|
Before Width: | Height: | Size: 2.5 MiB After Width: | Height: | Size: 2.5 MiB |
|
Before Width: | Height: | Size: 1.9 MiB After Width: | Height: | Size: 1.9 MiB |
|
Before Width: | Height: | Size: 11 MiB After Width: | Height: | Size: 11 MiB |
|
Before Width: | Height: | Size: 3.6 MiB After Width: | Height: | Size: 3.6 MiB |
|
Before Width: | Height: | Size: 6.1 MiB After Width: | Height: | Size: 6.1 MiB |
|
Before Width: | Height: | Size: 1.7 MiB After Width: | Height: | Size: 1.7 MiB |
|
Before Width: | Height: | Size: 3.6 MiB After Width: | Height: | Size: 3.6 MiB |
|
Before Width: | Height: | Size: 1.3 MiB After Width: | Height: | Size: 1.3 MiB |
|
Before Width: | Height: | Size: 2.3 MiB After Width: | Height: | Size: 2.3 MiB |
|
Before Width: | Height: | Size: 768 KiB After Width: | Height: | Size: 768 KiB |
|
Before Width: | Height: | Size: 2.1 MiB After Width: | Height: | Size: 2.1 MiB |
|
Before Width: | Height: | Size: 6.1 MiB After Width: | Height: | Size: 6.1 MiB |
|
Before Width: | Height: | Size: 1.3 MiB After Width: | Height: | Size: 1.3 MiB |
|
Before Width: | Height: | Size: 3.7 MiB After Width: | Height: | Size: 3.7 MiB |
|
Before Width: | Height: | Size: 2.0 MiB After Width: | Height: | Size: 2.0 MiB |
|
Before Width: | Height: | Size: 2.3 MiB After Width: | Height: | Size: 2.3 MiB |
|
Before Width: | Height: | Size: 518 KiB After Width: | Height: | Size: 518 KiB |
|
Before Width: | Height: | Size: 3.0 MiB After Width: | Height: | Size: 3.0 MiB |
|
Before Width: | Height: | Size: 166 KiB After Width: | Height: | Size: 166 KiB |
|
Before Width: | Height: | Size: 3.8 MiB After Width: | Height: | Size: 3.8 MiB |
|
Before Width: | Height: | Size: 160 KiB After Width: | Height: | Size: 160 KiB |
|
Before Width: | Height: | Size: 4.2 MiB After Width: | Height: | Size: 4.2 MiB |
|
Before Width: | Height: | Size: 10 MiB After Width: | Height: | Size: 10 MiB |
|
Before Width: | Height: | Size: 439 KiB After Width: | Height: | Size: 439 KiB |
|
Before Width: | Height: | Size: 860 KiB After Width: | Height: | Size: 860 KiB |
|
Before Width: | Height: | Size: 80 KiB After Width: | Height: | Size: 80 KiB |
|
Before Width: | Height: | Size: 2.2 MiB After Width: | Height: | Size: 2.2 MiB |