mirror of
https://github.com/sean2077/obsidian-dynamic-theme-background.git
synced 2026-07-22 06:44:57 +00:00
801 lines
46 KiB
HTML
801 lines
46 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>Obsidian DTB - Dynamic Theme Background</title>
|
||
<meta name="description"
|
||
content="Dynamic Theme Background (DTB) - Multi-source dynamic wallpaper plugin for Obsidian: Unsplash, Pexels, Pixabay, Wallhaven, 360 Wallpaper, custom & local images.">
|
||
<meta name="keywords"
|
||
content="Obsidian, wallpaper, background, plugin, Unsplash, Pexels, Pixabay, Wallhaven, 360 Wallpaper, dynamic, theme background">
|
||
<meta name="author" content="sean2077">
|
||
<meta name="robots" content="index,follow">
|
||
<meta name="theme-color" content="#0a0a0a">
|
||
<link rel="canonical" href="https://obsidian-dynamic-theme-background.pages.dev">
|
||
<meta property="og:type" content="website">
|
||
<meta property="og:title" content="Dynamic Theme Background - Obsidian Plugin">
|
||
<meta property="og:description"
|
||
content="Multi-source wallpapers, smart scheduling & visual tuning for an inspired Obsidian workspace.">
|
||
<meta property="og:url" content="https://obsidian-dynamic-theme-background.pages.dev">
|
||
<meta property="og:image"
|
||
content="https://raw.githubusercontent.com/sean2077/obsidian-dynamic-theme-background/master/docs/assets/dtb-status-bar.jpg">
|
||
<meta property="og:image:type" content="image/jpeg">
|
||
<meta property="og:image:width" content="1200">
|
||
<meta property="og:image:height" content="630">
|
||
<meta property="og:site_name" content="Dynamic Theme Background">
|
||
<meta property="og:locale" content="en_US">
|
||
<meta name="msapplication-TileColor" content="#667eea">
|
||
<meta name="mobile-web-app-capable" content="yes">
|
||
<link rel="manifest" href="manifest.json">
|
||
<!-- Preconnect for faster image fetch -->
|
||
<link rel="preconnect" href="https://raw.githubusercontent.com" crossorigin>
|
||
<link rel="preconnect" href="https://api.github.com" crossorigin>
|
||
<link rel="dns-prefetch" href="https://github.com">
|
||
<!-- Favicon -->
|
||
<link rel="icon"
|
||
href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>🌈</text></svg>">
|
||
<link rel="mask-icon"
|
||
href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>🌈</text></svg>"
|
||
color="#667eea">
|
||
<!-- Structured Data -->
|
||
<script type="application/ld+json" id="schema-dtb">{
|
||
"@context": "https://schema.org",
|
||
"@type": "SoftwareApplication",
|
||
"name": "Dynamic Theme Background",
|
||
"applicationCategory": "ProductivityApplication",
|
||
"operatingSystem": "Windows, macOS, Linux, iOS, Android",
|
||
"abstract": "Dynamic multi-source wallpaper management for Obsidian.",
|
||
"description": "Obsidian plugin providing dynamic multi-source wallpapers with smart scheduling, visual tuning, caching, time-based rotation and custom sources.",
|
||
"author": {"@type": "Person", "name": "sean2077"},
|
||
"publisher": {"@type": "Organization", "name": "Obsidian Community"},
|
||
"license": "https://opensource.org/licenses/MIT",
|
||
"keywords": ["Obsidian","wallpaper","background","unsplash","pexels","pixabay","wallhaven","dynamic","plugin"],
|
||
"url": "https://github.com/sean2077/obsidian-dynamic-theme-background",
|
||
"sourceCode": "https://github.com/sean2077/obsidian-dynamic-theme-background",
|
||
"downloadUrl": "https://github.com/sean2077/obsidian-dynamic-theme-background/releases",
|
||
"image": "https://raw.githubusercontent.com/sean2077/obsidian-dynamic-theme-background/master/docs/assets/dtb-status-bar.jpg",
|
||
"featureList": [
|
||
"Multiple providers (Unsplash, Pexels, Pixabay, Wallhaven, custom)",
|
||
"Local + remote mixing",
|
||
"Time-based rotation & interval scheduling",
|
||
"Visual adjustments (blur, brightness, saturation, hue)",
|
||
"Caching & performance optimization",
|
||
"Command palette quick actions",
|
||
"Custom API support"
|
||
],
|
||
"sameAs": ["https://obsidian.md/plugins","https://github.com/sean2077"],
|
||
"offers": {"@type": "Offer", "price": "0", "priceCurrency": "USD"}
|
||
}</script>
|
||
<!-- External CSS -->
|
||
<link rel="stylesheet" href="landing.css?v=1733500901">
|
||
<!-- Early performance & iOS detection (must be inline for immediate execution) -->
|
||
<script>
|
||
(function () {
|
||
var ua = navigator.userAgent;
|
||
var isIOS = /iPhone|iPad|iPod/i.test(ua) || (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1);
|
||
var isLowPerf = false;
|
||
try {
|
||
isLowPerf = navigator.hardwareConcurrency <= 2 ||
|
||
navigator.deviceMemory <= 2 ||
|
||
/Android|iPhone|iPad|iPod/i.test(ua) ||
|
||
window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
||
} catch (e) { }
|
||
if (isLowPerf) document.documentElement.classList.add('dtb-perf-low');
|
||
if (isIOS) document.documentElement.classList.add('dtb-ios');
|
||
})();
|
||
</script>
|
||
</head>
|
||
|
||
<body>
|
||
<a href="#main" class="skip-link" data-i18n="accessibility.skipToContent">Skip to content</a>
|
||
<div class="dynamic-bg" aria-hidden="true"></div>
|
||
<div class="wallpaper-overlay"></div>
|
||
<div class="floating-particles" aria-hidden="true"></div>
|
||
|
||
<header class="header">
|
||
<nav class="nav container">
|
||
<a href="#top" class="logo" aria-label="Back to top / Home" data-i18n-title="nav.backToTop">
|
||
<span aria-hidden="true">🌈</span> <span data-i18n="nav.brandName">Obsidian DTB</span>
|
||
<span class="version-badge" id="dtb-version-header" data-i18n-title="nav.currentVersion"
|
||
data-fallback="v?" data-i18n="nav.versionLoading">loading…</span>
|
||
</a>
|
||
<div class="nav-links">
|
||
<a href="#demo" data-i18n="nav.preview">Preview</a>
|
||
<a href="#features" data-i18n="nav.features">Features</a>
|
||
<a href="#installation" data-i18n="nav.install">Install</a>
|
||
<a href="#quickstart" data-i18n="nav.quickstart">Quick Start</a>
|
||
<a href="#sources" data-i18n="nav.sources">Sources</a>
|
||
<a href="#faq" data-i18n="nav.faq">FAQ</a>
|
||
<a class="icon-btn" href="https://github.com/sean2077/obsidian-dynamic-theme-background" target="_blank"
|
||
rel="noopener noreferrer" data-i18n-aria-label="nav.github" data-i18n-title="nav.github">
|
||
<svg viewBox="0 0 16 16" aria-hidden="true" role="img" focusable="false">
|
||
<path
|
||
d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.012 8.012 0 0016 8c0-4.42-3.58-8-8-8Z" />
|
||
</svg>
|
||
</a>
|
||
<button class="icon-btn lang-toggle" id="lang-toggle-btn" data-i18n-aria-label="nav.langSwitch"
|
||
data-i18n-title="nav.langSwitchTitle">
|
||
<span class="ico" aria-hidden="true">🌐</span>
|
||
<span class="lang-text" aria-hidden="true">中</span>
|
||
</button>
|
||
</div>
|
||
</nav>
|
||
</header>
|
||
|
||
<main id="main" tabindex="-1">
|
||
<!-- Hero Section -->
|
||
<section class="hero" id="top">
|
||
<div class="hero-overlay"></div>
|
||
<div class="container hero-container">
|
||
<div class="hero-badges">
|
||
<a href="https://obsidian.md" target="_blank" rel="noopener noreferrer" class="hero-badge"
|
||
data-i18n="hero.badge">Obsidian Plugin</a>
|
||
<span class="hero-badge hero-badge-alt" data-i18n="hero.desc">Multi-source Dynamic Wallpaper</span>
|
||
</div>
|
||
<h1 class="hero-title">
|
||
<span class="hero-title-text">Dynamic Theme</span>
|
||
<span class="hero-title-text hero-title-animated">Background</span>
|
||
</h1>
|
||
<p class="hero-slogan" data-i18n="hero.slogan">Build Your Own Wallpaper Library in Obsidian!</p>
|
||
<p class="hero-tagline" data-i18n="hero.tagline">Make every note-taking experience visually inspiring
|
||
</p>
|
||
|
||
<div class="hero-stats">
|
||
<div class="hero-stat">
|
||
<span class="hero-stat-number">7+</span>
|
||
<span class="hero-stat-label" data-i18n="hero.statSources">Wallpaper Sources</span>
|
||
</div>
|
||
<div class="hero-stat-divider"></div>
|
||
<div class="hero-stat">
|
||
<span class="hero-stat-number">∞</span>
|
||
<span class="hero-stat-label" data-i18n="hero.statImages">HD Wallpapers</span>
|
||
</div>
|
||
<div class="hero-stat-divider"></div>
|
||
<div class="hero-stat">
|
||
<span class="hero-stat-number">100%</span>
|
||
<span class="hero-stat-label" data-i18n="hero.statFree">Free & Open Source</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="cta-buttons">
|
||
<a href="#installation" class="btn btn-primary"><span>⚡</span><span
|
||
data-i18n="hero.installBtn">Install Now</span></a>
|
||
<a href="#demo" class="btn btn-ghost"><span>▶</span><span data-i18n="hero.watchDemo">Watch
|
||
Demo</span></a>
|
||
</div>
|
||
<div class="hero-scroll-hint">
|
||
<span data-i18n="hero.scrollHint">Scroll to explore</span>
|
||
<div class="hero-scroll-arrow"></div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- Demo Section -->
|
||
<section class="demo full-section" id="demo">
|
||
<div class="container">
|
||
<h2 class="fade-in">🎬 <span class="heading-text" data-i18n="demo.title">Preview</span></h2>
|
||
<div class="demo-gallery fade-in">
|
||
<div class="demo-item">
|
||
<div class="demo-img-wrap">
|
||
<img src="assets/dtb-demo2.gif" alt="Wallpaper switching preview" class="demo-img"
|
||
width="800" height="450" loading="lazy" decoding="async">
|
||
<span class="demo-img-label" data-i18n="demo.switchingLabel">Switching</span>
|
||
</div>
|
||
<h3 data-i18n="demo.switchingTitle">Multi‑source Library</h3>
|
||
<ul class="demo-list" data-demo-list="switching"></ul>
|
||
</div>
|
||
<div class="demo-item">
|
||
<div class="demo-img-wrap">
|
||
<img src="assets/dtb-demo.gif" alt="Image backgrounds and gradient backgrounds"
|
||
class="demo-img" width="800" height="450" loading="lazy" decoding="async">
|
||
<span class="demo-img-label" data-i18n="demo.backgroundsLabel">Backgrounds</span>
|
||
</div>
|
||
<h3 data-i18n="demo.backgroundsTitle">Image & Gradient Backgrounds</h3>
|
||
<ul class="demo-list" data-demo-list="backgrounds"></ul>
|
||
</div>
|
||
<div class="demo-item">
|
||
<div class="demo-img-wrap">
|
||
<img src="assets/dtb-demo3.gif" alt="Feature-rich settings panel" class="demo-img"
|
||
width="800" height="450" loading="lazy" decoding="async">
|
||
<span class="demo-img-label" data-i18n="demo.settingsLabel">Settings</span>
|
||
</div>
|
||
<h3 data-i18n="demo.settingsTitle">Feature-rich Settings</h3>
|
||
<ul class="demo-list" data-demo-list="settings"></ul>
|
||
</div>
|
||
</div>
|
||
<div class="summary-pills fade-in">
|
||
<div class="summary-pill"><span class="ico">🌐</span><span
|
||
data-i18n="demo.pills.multiSourceMix">Multi-Source Mix</span></div>
|
||
<div class="summary-pill"><span class="ico">🎚️</span><span data-i18n="demo.pills.liveTuning">Live
|
||
Tuning</span></div>
|
||
<div class="summary-pill"><span class="ico">🧠</span><span
|
||
data-i18n="demo.pills.cachingPrefetch">Caching & Prefetch</span></div>
|
||
<div class="summary-pill"><span class="ico">⌨️</span><span
|
||
data-i18n="demo.pills.commandPalette">Command Palette</span></div>
|
||
<div class="summary-pill"><span class="ico">🔧</span><span data-i18n="demo.pills.customApi">Custom
|
||
API</span></div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- Features Section -->
|
||
<section class="features full-section" id="features">
|
||
<div class="container">
|
||
<h2 class="fade-in section-title">✨ <span class="heading-text" data-i18n="features.title">Key
|
||
Features</span></h2>
|
||
<p class="features-subtitle fade-in" data-i18n="features.subtitle">An elevated wallpaper experience
|
||
crafted for Obsidian</p>
|
||
<div class="features-grid">
|
||
<div class="feature-card fade-in" style="--accent:#667eea;">
|
||
<h3><span class="feature-icon">🖼️</span><span
|
||
data-i18n="features.multiSource.title">Multi-source Library</span></h3>
|
||
<p data-i18n="features.multiSource.desc">Unsplash, Pixabay, Pexels, Wallhaven + custom API &
|
||
local folders—flexibly combine and curate.</p>
|
||
</div>
|
||
<div class="feature-card fade-in" style="--accent:#14b8a6;">
|
||
<h3><span class="feature-icon">🎨</span><span data-i18n="features.visualTuning.title">Visual
|
||
Tuning</span></h3>
|
||
<p data-i18n="features.visualTuning.desc">Adjust blur, brightness, saturation, hue &
|
||
more—theme-friendly and dark/light aware.</p>
|
||
</div>
|
||
<div class="feature-card fade-in" style="--accent:#f59e0b;">
|
||
<h3><span class="feature-icon">⚡</span><span data-i18n="features.smartRotation.title">Smart
|
||
Rotation</span></h3>
|
||
<p data-i18n="features.smartRotation.desc">Time windows, interval auto-change, command palette
|
||
quick switch, minute-level scheduling.</p>
|
||
</div>
|
||
<div class="feature-card fade-in" style="--accent:#8b5cf6;">
|
||
<h3><span class="feature-icon">🤝</span><span data-i18n="features.greatUx.title">Great UX</span>
|
||
</h3>
|
||
<p data-i18n="features.greatUx.desc">Async loading, caching, default presets, batch ops &
|
||
shortcut commands for fast workflows.</p>
|
||
</div>
|
||
<div class="feature-card fade-in" style="--accent:#f43f5e;">
|
||
<h3><span class="feature-icon">🚀</span><span data-i18n="features.performance.title">Performance
|
||
Minded</span></h3>
|
||
<p data-i18n="features.performance.desc">Optimized rendering & caching for minimal impact on
|
||
editor responsiveness.</p>
|
||
</div>
|
||
<div class="feature-card fade-in" style="--accent:#06b6d4;">
|
||
<h3><span class="feature-icon">📦</span><span data-i18n="features.roadmap.title">Roadmap</span>
|
||
</h3>
|
||
<p data-i18n="features.roadmap.desc">Weather integration, theme packs, video / audio / AI
|
||
backgrounds, mobile refinements.</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- Installation Section -->
|
||
<section class="installation full-section" id="installation">
|
||
<div class="container">
|
||
<h2 class="fade-in section-title">⬇️ <span class="heading-text"
|
||
data-i18n="installation.title">Installation</span></h2>
|
||
<p class="features-subtitle fade-in" data-i18n="installation.subtitle">
|
||
Pick a method below. Once enabled, jump to Quick Start to configure rotation & sources.</p>
|
||
<div class="install-tabs fade-in" id="install-tabs" data-default="market">
|
||
<div class="tab-list" role="tablist" aria-label="Installation methods">
|
||
<button class="tab-btn" role="tab" aria-selected="true" data-tab="market" id="tab-market">🛒
|
||
<span data-i18n="installation.tabs.marketplace">Marketplace</span></button>
|
||
<button class="tab-btn" role="tab" aria-selected="false" data-tab="brat" id="tab-brat">🧪 <span
|
||
data-i18n="installation.tabs.brat">BRAT</span></button>
|
||
<button class="tab-btn" role="tab" aria-selected="false" data-tab="manual" id="tab-manual">📦
|
||
<span data-i18n="installation.tabs.manual">Manual</span></button>
|
||
</div>
|
||
<div class="tab-panels">
|
||
<div class="tab-panel is-active" role="tabpanel" aria-labelledby="tab-market" id="panel-market">
|
||
<div class="install-method">
|
||
<h3>
|
||
<span>🛒</span>
|
||
<span data-i18n="installation.marketplace.title">Community Marketplace</span>
|
||
<span class="install-status status-available"
|
||
data-i18n="installation.marketplace.status">Available</span>
|
||
</h3>
|
||
<ul class="install-steps" data-install-method="marketplace"></ul>
|
||
<div class="install-note-small" data-i18n="installation.marketplace.note">Available
|
||
from the official Obsidian Community Plugins catalog. <a
|
||
href="https://community.obsidian.md/plugins/dynamic-theme-background"
|
||
target="_blank" rel="noopener noreferrer">View listing</a>.</div>
|
||
</div>
|
||
</div>
|
||
<div class="tab-panel" role="tabpanel" aria-labelledby="tab-brat" id="panel-brat" hidden>
|
||
<div class="install-method">
|
||
<h3>
|
||
<span>🧪</span>
|
||
<span data-i18n="installation.brat.title">BRAT (Beta)</span>
|
||
<span class="install-status status-beta"
|
||
data-i18n="installation.brat.status">Beta</span>
|
||
</h3>
|
||
<ul class="install-steps" data-install-method="brat"></ul>
|
||
<div class="install-note-small" data-i18n="installation.brat.note">Use BRAT to test
|
||
beta or prerelease builds before they reach the Marketplace.</div>
|
||
</div>
|
||
</div>
|
||
<div class="tab-panel" role="tabpanel" aria-labelledby="tab-manual" id="panel-manual" hidden>
|
||
<div class="install-method">
|
||
<h3>
|
||
<span>📦</span>
|
||
<span data-i18n="installation.manual.title">Manual Install</span>
|
||
<span class="install-status status-available"
|
||
data-i18n="installation.manual.status">Advanced</span>
|
||
</h3>
|
||
<ul class="install-steps" data-install-method="manual"></ul>
|
||
<div class="install-note-small" data-i18n="installation.manual.note">Use for pinned
|
||
versions, air-gapped envs, or manual audits.</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="install-help-text" data-i18n="installation.helpText">Need
|
||
help? See the <a href="https://github.com/sean2077/obsidian-dynamic-theme-background#readme"
|
||
target="_blank" rel="noopener noreferrer">README</a> or <a
|
||
href="https://github.com/sean2077/obsidian-dynamic-theme-background/issues" target="_blank"
|
||
rel="noopener noreferrer">report an issue</a>.</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- Quick Start Section -->
|
||
<section class="quickstart full-section" id="quickstart">
|
||
<div class="container">
|
||
<h2 class="fade-in section-title">🚀 <span class="heading-text" data-i18n="quickstart.title">Quick
|
||
Start</span></h2>
|
||
<p class="section-subtitle fade-in" data-i18n="quickstart.subtitle">Already installed? In three short
|
||
steps you'll have smart rotation running.</p>
|
||
|
||
<div class="quickstart-content fade-in">
|
||
<div class="quickstart-steps">
|
||
<div class="quickstart-step">
|
||
<span class="step-number" data-i18n="quickstart.step1.label">STEP 1</span>
|
||
<h3 data-i18n="quickstart.step1.title">Add Sources</h3>
|
||
<p data-i18n="quickstart.step1.desc">Add local images or enable API providers (Unsplash /
|
||
Pixabay / Pexels / Wallhaven / Custom).</p>
|
||
</div>
|
||
<div class="quickstart-step">
|
||
<span class="step-number" data-i18n="quickstart.step2.label">STEP 2</span>
|
||
<h3 data-i18n="quickstart.step2.title">Set Rotation</h3>
|
||
<p data-i18n="quickstart.step2.desc">Choose interval + optional time windows (morning / day
|
||
/ night) for contextual change.</p>
|
||
</div>
|
||
<div class="quickstart-step">
|
||
<span class="step-number" data-i18n="quickstart.step3.label">STEP 3</span>
|
||
<h3 data-i18n="quickstart.step3.title">Control & Enjoy</h3>
|
||
<p data-i18n="quickstart.step3.desc">Use status bar clicks or Command Palette
|
||
(<code>dtb</code>) to switch, save & fetch.</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="statusbar-demo">
|
||
<figure class="statusbar-figure">
|
||
<img src="assets/dtb-status-bar.jpg" alt="Status bar controls" width="800" height="450"
|
||
loading="lazy" decoding="async" />
|
||
<figcaption data-i18n="quickstart.statusbar.caption">Status Bar Snapshot</figcaption>
|
||
</figure>
|
||
<div class="statusbar-actions">
|
||
<h4 data-i18n="quickstart.statusbar.title">Status Bar Quick Control</h4>
|
||
<div class="statusbar-action">
|
||
<span class="action-ico">🖱️</span>
|
||
<span data-i18n="quickstart.statusbar.leftClick"><strong>Left Click</strong> Random
|
||
wallpaper</span>
|
||
</div>
|
||
<div class="statusbar-action">
|
||
<span class="action-ico">🖱️</span>
|
||
<span data-i18n="quickstart.statusbar.middleClick"><strong>Middle Click</strong> Open
|
||
settings tab</span>
|
||
</div>
|
||
<div class="statusbar-action">
|
||
<span class="action-ico">🖱️</span>
|
||
<span data-i18n="quickstart.statusbar.rightClick"><strong>Right Click</strong> Save
|
||
current background</span>
|
||
</div>
|
||
<p class="statusbar-hint" data-i18n="quickstart.statusbar.hint">Daily interaction happens
|
||
here or via the command palette.</p>
|
||
<p class="statusbar-tip" data-i18n="quickstart.statusbar.tip">Tip: assign hotkeys for even
|
||
faster switching</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- Sources Section -->
|
||
<section class="api-sources full-section" id="sources">
|
||
<div class="container">
|
||
<h2 class="fade-in section-title">🌐 <span class="heading-text" data-i18n="sources.title">Supported
|
||
Sources</span></h2>
|
||
<p class="fade-in sources-subtitle" data-i18n="sources.subtitle">High quality
|
||
wallpaper platforms + custom endpoints</p>
|
||
<div class="api-logos">
|
||
<a class="api-logo fade-in" style="--accent:#9aa0ae;" href="https://unsplash.com/" target="_blank"
|
||
rel="noopener noreferrer">
|
||
<div>📸</div>
|
||
<div class="api-logo-name">Unsplash</div>
|
||
<p data-i18n="sources.unsplash">Photography</p>
|
||
</a>
|
||
<a class="api-logo fade-in" style="--accent:#28c76f;" href="https://pixabay.com/" target="_blank"
|
||
rel="noopener noreferrer">
|
||
<div>🖼️</div>
|
||
<div class="api-logo-name">Pixabay</div>
|
||
<p data-i18n="sources.pixabay">Free images</p>
|
||
</a>
|
||
<a class="api-logo fade-in" style="--accent:#14b8a6;" href="https://www.pexels.com/" target="_blank"
|
||
rel="noopener noreferrer">
|
||
<div>🎨</div>
|
||
<div class="api-logo-name">Pexels</div>
|
||
<p data-i18n="sources.pexels">Curated shots</p>
|
||
</a>
|
||
<a class="api-logo fade-in" style="--accent:#8b5cf6;" href="https://wallhaven.cc/" target="_blank"
|
||
rel="noopener noreferrer">
|
||
<div>🏞️</div>
|
||
<div class="api-logo-name">Wallhaven</div>
|
||
<p data-i18n="sources.wallhaven">Wallpaper hub</p>
|
||
</a>
|
||
<a class="api-logo fade-in" style="--accent:#00d26a;" href="https://wallpaper.apc.360.cn/"
|
||
target="_blank" rel="noopener noreferrer">
|
||
<div>🎯</div>
|
||
<div class="api-logo-name">360 Wallpaper</div>
|
||
<p data-i18n="sources.qihoo360">HD categories</p>
|
||
</a>
|
||
<button type="button" class="api-logo fade-in" style="--accent:#ff9800;" id="custom-api-btn">
|
||
<div>🔧</div>
|
||
<div class="api-logo-name">Custom API</div>
|
||
<p data-i18n="sources.custom">Bring your own</p>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- FAQ Section -->
|
||
<section class="faq full-section" id="faq">
|
||
<div class="container">
|
||
<h2 class="fade-in section-title">❓ <span class="heading-text" data-i18n="faq.title">FAQ</span></h2>
|
||
<div class="faq-grid">
|
||
<div class="faq-item fade-in">
|
||
<div class="faq-question">Which wallpaper sources are supported?</div>
|
||
<div class="faq-answer">Unsplash, Pixabay, Pexels, Wallhaven plus custom APIs; more incoming.
|
||
</div>
|
||
</div>
|
||
<div class="faq-item fade-in">
|
||
<div class="faq-question">How do I set rotation rules?</div>
|
||
<div class="faq-answer">Configure time slots, interval auto-rotate or trigger manually via the
|
||
command palette.</div>
|
||
</div>
|
||
<div class="faq-item fade-in">
|
||
<div class="faq-question">Theme & mobile support?</div>
|
||
<div class="faq-answer">Yes—responsive layout and theme variable integration for dark & light
|
||
modes.</div>
|
||
</div>
|
||
<div class="faq-item fade-in">
|
||
<div class="faq-question">Performance impact?</div>
|
||
<div class="faq-answer">Async loading + smart caching keeps the workspace responsive.</div>
|
||
</div>
|
||
<div class="faq-item fade-in">
|
||
<div class="faq-question">Feedback & issues?</div>
|
||
<div class="faq-answer">Report bugs via <a
|
||
href="https://github.com/sean2077/obsidian-dynamic-theme-background/issues/new?assignees=&labels=&projects=&template=bug_report.md&title="
|
||
target="_blank" rel="noopener noreferrer">Bug Report</a> or suggest features via <a
|
||
href="https://github.com/sean2077/obsidian-dynamic-theme-background/issues/new?assignees=&labels=&projects=&template=feature_request.md&title="
|
||
target="_blank" rel="noopener noreferrer">Feature Request</a>.</div>
|
||
</div>
|
||
<div class="faq-item fade-in">
|
||
<div class="faq-question">Where is configuration stored?</div>
|
||
<div class="faq-answer">Inside your vault
|
||
<code>.obsidian/plugins/obsidian-dynamic-theme-background/</code> folder (standard Obsidian
|
||
plugin pattern).
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="faq-disclaimer fade-in" data-i18n="faq.disclaimer">API
|
||
usage must comply with each provider's Terms of Service. Respect rate limits & attribution policies
|
||
where required.</div>
|
||
</div>
|
||
</section>
|
||
</main>
|
||
|
||
<footer class="footer">
|
||
<div class="container">
|
||
<div class="footer-content">
|
||
<div class="footer-section">
|
||
<h3 data-i18n="footer.aboutTitle">About DTB</h3>
|
||
<div data-i18n="footer.aboutDesc">Dynamic Theme Background enhances Obsidian with dynamic, smart
|
||
wallpapers for inspiration.</div>
|
||
</div>
|
||
<div class="footer-section">
|
||
<h3 data-i18n="footer.linksTitle">Links</h3>
|
||
<div class="footer-links">
|
||
<a href="https://github.com/sean2077/obsidian-dynamic-theme-background" target="_blank"
|
||
rel="noopener noreferrer" data-i18n="footer.github">GitHub Repo</a>
|
||
<a href="https://github.com/sean2077/obsidian-dynamic-theme-background#readme" target="_blank"
|
||
rel="noopener noreferrer" data-i18n="footer.docs">Documentation</a>
|
||
<a href="#faq" data-i18n="footer.faq">FAQ</a>
|
||
</div>
|
||
</div>
|
||
<div class="footer-section">
|
||
<h3 data-i18n="footer.contactTitle">Contact</h3>
|
||
<div data-i18n="footer.contactDesc">Need help or custom work? Contact via GitHub Issues.</div>
|
||
</div>
|
||
</div>
|
||
<div class="badges">
|
||
<span class="badge" data-i18n="footer.license">MIT License</span>
|
||
<span class="badge" data-i18n="footer.madeBy">Made with ❤️ by sean2077</span>
|
||
<span class="badge" data-i18n="footer.community">Obsidian Community</span>
|
||
<a class="badge" id="dtb-version-footer" data-fallback="v?"
|
||
href="https://github.com/sean2077/obsidian-dynamic-theme-background/releases" target="_blank"
|
||
rel="noopener noreferrer">version…</a>
|
||
</div>
|
||
<div class="footer-copyright"><span data-i18n="footer.copyright">© 2025 Dynamic Theme
|
||
Background. All rights reserved.</span></div>
|
||
</div>
|
||
</footer>
|
||
|
||
<!-- Fixed Bottom Right Buttons -->
|
||
<div class="fixed-buttons">
|
||
<button id="back-to-top" data-i18n-aria-label="nav.backToTop" data-i18n-title="nav.backToTop">
|
||
<span class="back-icon">
|
||
<span class="back-arrow">▲</span>
|
||
<span class="back-line"></span>
|
||
</span>
|
||
</button>
|
||
<div class="dtb-btn-wrapper">
|
||
<div class="dtb-arrow-hint" id="dtb-arrow-hint">
|
||
<span class="arrow-text" data-i18n="hero.trySwitch">Try switching →</span>
|
||
</div>
|
||
<button id="dtb-switch-btn" title="Click to switch background" aria-label="Switch background">
|
||
<span class="dtb-icon">🌈</span>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
(function () {
|
||
// Time-based dynamic background
|
||
(function () {
|
||
var bgEl = document.querySelector('.dynamic-bg');
|
||
if (!bgEl) return;
|
||
|
||
var timeClasses = ['time-dawn', 'time-morning', 'time-noon', 'time-afternoon', 'time-dusk', 'time-night'];
|
||
var timeLabels = ['🌅 Dawn', '☀️ Morning', '🌞 Noon', '🌤️ Afternoon', '🌇 Dusk', '🌙 Night'];
|
||
var currentIndex = 0;
|
||
|
||
// Demo mode: cycle through all times every 10 seconds
|
||
var demoMode = new URLSearchParams(location.search).has('demo');
|
||
|
||
function getTimeClass() {
|
||
var hour = new Date().getHours();
|
||
if (hour >= 5 && hour < 8) return 'time-dawn';
|
||
if (hour >= 8 && hour < 11) return 'time-morning';
|
||
if (hour >= 11 && hour < 14) return 'time-noon';
|
||
if (hour >= 14 && hour < 17) return 'time-afternoon';
|
||
if (hour >= 17 && hour < 19) return 'time-dusk';
|
||
return 'time-night';
|
||
}
|
||
|
||
function getTimeIndex() {
|
||
var cls = getTimeClass();
|
||
return timeClasses.indexOf(cls);
|
||
}
|
||
|
||
function updateBackground(index) {
|
||
if (typeof index === 'number') {
|
||
currentIndex = index;
|
||
}
|
||
var cls = timeClasses[currentIndex];
|
||
bgEl.className = 'dynamic-bg ' + cls;
|
||
|
||
// Update DTB button tooltip
|
||
var dtbBtn = document.getElementById('dtb-switch-btn');
|
||
if (dtbBtn) {
|
||
dtbBtn.title = timeLabels[currentIndex] + ' - Click to switch';
|
||
}
|
||
}
|
||
|
||
// Initialize with current time
|
||
currentIndex = getTimeIndex();
|
||
updateBackground();
|
||
|
||
// DTB button click handler
|
||
var dtbBtn = document.getElementById('dtb-switch-btn');
|
||
var arrowHint = document.getElementById('dtb-arrow-hint');
|
||
|
||
if (dtbBtn) {
|
||
dtbBtn.addEventListener('click', function () {
|
||
currentIndex = (currentIndex + 1) % timeClasses.length;
|
||
updateBackground();
|
||
// Add click animation
|
||
dtbBtn.classList.add('clicked');
|
||
setTimeout(function () { dtbBtn.classList.remove('clicked'); }, 200);
|
||
// Hide arrow hint after first click
|
||
if (arrowHint) {
|
||
arrowHint.classList.add('hidden');
|
||
}
|
||
});
|
||
}
|
||
|
||
// Auto-hide arrow hint after 8 seconds
|
||
if (arrowHint) {
|
||
setTimeout(function () {
|
||
arrowHint.classList.add('hidden');
|
||
}, 10000);
|
||
}
|
||
|
||
if (demoMode) {
|
||
console.log('🎬 Demo mode enabled - backgrounds will cycle every 10 seconds');
|
||
setInterval(function () {
|
||
currentIndex = (currentIndex + 1) % timeClasses.length;
|
||
updateBackground();
|
||
}, 10000);
|
||
} else {
|
||
// Production: update every minute based on real time
|
||
setInterval(function () {
|
||
currentIndex = getTimeIndex();
|
||
updateBackground();
|
||
}, 60000);
|
||
}
|
||
})();
|
||
|
||
// Skip particles on low-perf mode
|
||
if (document.documentElement.classList.contains('dtb-perf-low')) return;
|
||
|
||
// Lazy particles - reduced count for better performance
|
||
var particles = document.querySelector('.floating-particles');
|
||
var particleCount = 10;
|
||
var particlesSpawned = false;
|
||
|
||
function spawnParticles() {
|
||
if (particlesSpawned || !particles) return;
|
||
particlesSpawned = true;
|
||
for (var i = 0; i < particleCount; i++) {
|
||
var p = document.createElement('div');
|
||
p.className = 'particle';
|
||
var s = Math.random() * 28 + 10;
|
||
p.style.cssText = 'width:' + s + 'px;height:' + s + 'px;left:' + (Math.random() * 100) + '%;top:' + (Math.random() * 100) + '%;animation-duration:' + (Math.random() * 18 + 14) + 's;background:rgba(255,255,255,' + (Math.random() * 0.1 + 0.03) + ')';
|
||
particles.appendChild(p);
|
||
}
|
||
}
|
||
|
||
window.addEventListener('scroll', spawnParticles, { once: true, passive: true });
|
||
setTimeout(spawnParticles, 1500);
|
||
|
||
// IntersectionObserver fade-in (skip on iOS/low-perf for immediate visibility)
|
||
var skipFadeIn = document.documentElement.classList.contains('dtb-ios') ||
|
||
document.documentElement.classList.contains('dtb-perf-low');
|
||
var io = (!skipFadeIn && 'IntersectionObserver' in window) ? new IntersectionObserver(function (es) {
|
||
es.forEach(function (e) {
|
||
if (e.isIntersecting) { e.target.classList.add('visible'); io.unobserve(e.target); }
|
||
});
|
||
}, { rootMargin: '0px 0px -80px 0px', threshold: 0.1 }) : null;
|
||
|
||
document.querySelectorAll('.fade-in').forEach(function (el) {
|
||
if (io) io.observe(el); else el.classList.add('visible');
|
||
});
|
||
|
||
// Smooth anchors
|
||
document.querySelectorAll('a[href^="#"]').forEach(function (a) {
|
||
a.addEventListener('click', function (e) {
|
||
if (a.classList.contains('logo')) {
|
||
e.preventDefault();
|
||
window.scrollTo({ top: 0, behavior: 'smooth' });
|
||
return;
|
||
}
|
||
var id = a.getAttribute('href').slice(1);
|
||
var t = document.getElementById(id);
|
||
if (t) { e.preventDefault(); t.scrollIntoView({ behavior: 'smooth', block: 'start' }); }
|
||
});
|
||
});
|
||
|
||
// Back to top
|
||
var back = document.getElementById('back-to-top');
|
||
function threshold() { return window.innerHeight * 0.65; }
|
||
function toggleBack() { back.classList.toggle('visible', window.scrollY > threshold()); }
|
||
window.addEventListener('scroll', toggleBack, { passive: true });
|
||
toggleBack();
|
||
back.addEventListener('click', function () { window.scrollTo({ top: 0, behavior: 'smooth' }); });
|
||
|
||
// Dynamic version fetch
|
||
(function () {
|
||
var headerEl = document.getElementById('dtb-version-header');
|
||
var footerEl = document.getElementById('dtb-version-footer');
|
||
|
||
function setVersion(v, url) {
|
||
if (headerEl) { headerEl.textContent = v; headerEl.dataset.releaseUrl = url || ''; headerEl.style.cursor = 'pointer'; headerEl.title = 'Open release ' + v; }
|
||
if (footerEl) { footerEl.textContent = v; footerEl.dataset.releaseUrl = url || ''; footerEl.title = 'Open release ' + v; if (footerEl.tagName === 'A' && url) footerEl.setAttribute('href', url); }
|
||
}
|
||
|
||
var params = new URLSearchParams(location.search);
|
||
var skipApi = params.has('noapi') || params.get('noapi') === '1';
|
||
|
||
fetch('https://api.github.com/repos/sean2077/obsidian-dynamic-theme-background/releases/latest', {
|
||
headers: { 'Accept': 'application/vnd.github+json' }
|
||
}).then(function (res) {
|
||
if (skipApi || !res.ok) throw new Error('skip');
|
||
return res.json();
|
||
}).then(function (json) {
|
||
var tag = json.tag_name || json.name || '';
|
||
if (tag && !/^v/i.test(tag)) tag = 'v' + tag;
|
||
if (!tag) tag = headerEl?.dataset.fallback || 'v?';
|
||
var releaseUrl = 'https://github.com/sean2077/obsidian-dynamic-theme-background/releases/tag/' + tag.replace(/^v/i, '');
|
||
setVersion(tag, releaseUrl);
|
||
|
||
// Update JSON-LD
|
||
var schema = document.getElementById('schema-dtb');
|
||
var ver = tag.replace(/^v/, '');
|
||
if (schema) {
|
||
try {
|
||
var data = JSON.parse(schema.textContent || '{}');
|
||
data.softwareVersion = ver;
|
||
schema.textContent = JSON.stringify(data);
|
||
} catch (err) { }
|
||
}
|
||
}).catch(function () {
|
||
var fallback = headerEl?.dataset.fallback || 'v?';
|
||
var fallbackUrl = 'https://github.com/sean2077/obsidian-dynamic-theme-background/releases';
|
||
setVersion(fallback, fallbackUrl);
|
||
if (footerEl && footerEl.tagName === 'A') footerEl.setAttribute('href', fallbackUrl);
|
||
});
|
||
|
||
if (headerEl) {
|
||
headerEl.addEventListener('click', function (e) {
|
||
var u = headerEl.dataset.releaseUrl;
|
||
if (u) { e.preventDefault(); e.stopPropagation(); location.assign(u); }
|
||
});
|
||
}
|
||
})();
|
||
|
||
// Installation tabs
|
||
(function () {
|
||
var container = document.getElementById('install-tabs');
|
||
if (!container) return;
|
||
var buttons = Array.from(container.querySelectorAll('.tab-btn'));
|
||
var panels = Array.from(container.querySelectorAll('.tab-panel'));
|
||
|
||
buttons.forEach(function (b, i) {
|
||
var id = b.dataset.tab;
|
||
b.id = b.id || ('tab-' + id);
|
||
var pnl = container.querySelector('#panel-' + id);
|
||
if (pnl) pnl.setAttribute('aria-labelledby', b.id);
|
||
b.setAttribute('tabindex', i === 0 ? '0' : '-1');
|
||
});
|
||
|
||
function activate(id, focus, updateHash) {
|
||
buttons.forEach(function (b) {
|
||
var sel = b.dataset.tab === id;
|
||
b.classList.toggle('is-active', sel);
|
||
b.setAttribute('aria-selected', sel ? 'true' : 'false');
|
||
b.setAttribute('tabindex', sel ? '0' : '-1');
|
||
if (sel && focus) b.focus();
|
||
});
|
||
panels.forEach(function (p) {
|
||
var show = p.id === 'panel-' + id;
|
||
p.classList.toggle('is-active', show);
|
||
if (show) p.removeAttribute('hidden'); else p.setAttribute('hidden', '');
|
||
});
|
||
if (updateHash) history.replaceState(null, '', '#install-' + id);
|
||
}
|
||
|
||
buttons.forEach(function (btn) {
|
||
btn.addEventListener('click', function () { activate(btn.dataset.tab, true, true); });
|
||
});
|
||
|
||
container.addEventListener('keydown', function (e) {
|
||
var current = buttons.findIndex(function (b) { return b.getAttribute('aria-selected') === 'true'; });
|
||
if (['ArrowRight', 'ArrowLeft', 'Home', 'End'].indexOf(e.key) >= 0) {
|
||
e.preventDefault();
|
||
var ni = current;
|
||
if (e.key === 'ArrowRight') ni = (current + 1) % buttons.length;
|
||
else if (e.key === 'ArrowLeft') ni = (current - 1 + buttons.length) % buttons.length;
|
||
else if (e.key === 'Home') ni = 0;
|
||
else if (e.key === 'End') ni = buttons.length - 1;
|
||
activate(buttons[ni].dataset.tab, true, true);
|
||
}
|
||
});
|
||
|
||
var match = location.hash.match(/^#install-(market|brat|manual)$/);
|
||
if (match) activate(match[1], false, false); else activate(buttons[0].dataset.tab, false, false);
|
||
})();
|
||
})();
|
||
</script>
|
||
<!-- i18n support -->
|
||
<script src="i18n.js" defer></script>
|
||
</body>
|
||
|
||
</html>
|