Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 31 additions & 6 deletions css/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,26 @@
--ground: #f5f8fb;
--panel: #ffffff;
--line: #dde4ec;
--img-bg: #ffffff;
--bg-blue-tint: #eef4f9;
--bg-green-tint: #f5f9ec;
--bg-error: #faf0ed;
--border-error: #c0563c;
}
[data-theme="dark"] {
--blue: #5b9fd4;
--blue-dark: #4a87bb;
--green: #a8cf35;
--ink: #e4edf5;
--dim: #8fa0b3;
--ground: #0f1923;
--panel: #192636;
--line: #283a4e;
--img-bg: #1e2e3e;
--bg-blue-tint: #0a1e2e;
--bg-green-tint: #0c1a06;
--bg-error: #1e0e0a;
--border-error: #d06550;
}
* { box-sizing: border-box; }
body {
Expand All @@ -19,8 +39,13 @@ a { color: var(--blue); }
padding: 14px 24px; background: var(--panel); border-bottom: 1px solid var(--line);
}
.brand { display: flex; align-items: center; gap: 10px; font-weight: 700; text-decoration: none; color: var(--blue); font-size: 1.05rem; }
.site-header nav { display: flex; gap: 18px; }
.site-header nav { display: flex; gap: 18px; align-items: center; }
.site-header nav a { color: var(--dim); text-decoration: none; }
.theme-btn {
background: none; border: none; cursor: pointer; color: var(--dim);
font-size: 1rem; padding: 0; line-height: 1; visibility: hidden;
}
.theme-btn:hover { color: var(--ink); }
main { max-width: 1080px; margin: 0 auto; padding: 32px 24px 64px; }
.site-footer { text-align: center; color: var(--dim); font-size: .85rem; padding: 24px; border-top: 1px solid var(--line); }
.loading { color: var(--dim); }
Expand All @@ -40,15 +65,15 @@ main { max-width: 1080px; margin: 0 auto; padding: 32px 24px 64px; }
padding: 14px; text-decoration: none; color: inherit; transition: border-color .15s, box-shadow .15s;
}
.device-card:hover { border-color: var(--blue); box-shadow: 0 4px 14px -8px rgba(65,122,171,.5); }
.device-card img { width: 100%; aspect-ratio: 1; object-fit: contain; border-radius: 6px; background: #fff; }
.device-card img { width: 100%; aspect-ratio: 1; object-fit: contain; border-radius: 6px; background: var(--img-bg); }
.device-card h3 { margin: 10px 0 2px; font-size: 1rem; }
.device-card p { margin: 0; color: var(--dim); font-size: .82rem; }
.device-card .go { color: var(--blue); font-weight: 600; font-size: .85rem; margin-top: 8px; display: inline-block; }

/* Device wizard */
.device-page .back { display: inline-block; margin-bottom: 16px; text-decoration: none; color: var(--dim); }
.device-head { display: flex; gap: 20px; align-items: center; margin-bottom: 20px; }
.device-head img { width: 110px; height: 110px; object-fit: contain; background: #fff; border: 1px solid var(--line); border-radius: 10px; }
.device-head img { width: 110px; height: 110px; object-fit: contain; background: var(--img-bg); border: 1px solid var(--line); border-radius: 10px; }
.device-head h1 { margin: 0; font-size: 1.6rem; }
.device-head p { margin: 2px 0 6px; color: var(--dim); }
.device-head .links { font-size: .85rem; }
Expand All @@ -67,10 +92,10 @@ esp-web-install-button button, .install-btn {
background: var(--blue); color: #fff; border: 0; border-radius: 8px;
padding: 10px 22px; font-size: .95rem; font-weight: 700; cursor: pointer;
}
.fallback { border-left: 3px solid var(--blue); background: #eef4f9; border-radius: 0 8px 8px 0; padding: 12px 16px; }
.fallback { border-left: 3px solid var(--blue); background: var(--bg-blue-tint); border-radius: 0 8px 8px 0; padding: 12px 16px; }
.fallback ul { margin: 8px 0 0; padding-left: 20px; }
.release-notes { border-left: 3px solid var(--green); background: #f5f9ec; border-radius: 0 8px 8px 0; padding: 12px 16px; margin-top: 14px; font-size: .88rem; }
.release-notes { border-left: 3px solid var(--green); background: var(--bg-green-tint); border-radius: 0 8px 8px 0; padding: 12px 16px; margin-top: 14px; font-size: .88rem; }
.release-notes summary { cursor: pointer; font-weight: 600; }
.release-notes pre { white-space: pre-wrap; font-family: inherit; margin: 8px 0; }
.done-check { color: var(--green); font-weight: 700; }
.error-box { border-left: 3px solid #c0563c; background: #faf0ed; border-radius: 0 8px 8px 0; padding: 12px 16px; }
.error-box { border-left: 3px solid var(--border-error); background: var(--bg-error); border-radius: 0 8px 8px 0; padding: 12px 16px; }
11 changes: 11 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,16 @@
<title>Apollo Installer</title>
<meta name="description" content="Flash your Apollo Automation device from the browser.">
<link rel="icon" href="assets/brand/favicon.png">
<script>
(function () {
try {
var t = localStorage.getItem('theme');
if (t === 'dark' || (!t && matchMedia('(prefers-color-scheme: dark)').matches)) {
document.documentElement.setAttribute('data-theme', 'dark');
}
} catch (e) {}
}());
</script>
<link rel="stylesheet" href="css/style.css">
</head>
<body>
Expand All @@ -18,6 +28,7 @@
<a href="https://wiki.apolloautomation.com">Wiki</a>
<a href="https://apolloautomation.com">Shop</a>
<a href="https://dsc.gg/ApolloAutomation">Discord</a>
<button id="theme-toggle" class="theme-btn" title="Toggle theme" aria-label="Toggle theme">🌙</button>
</nav>
</header>
<main id="app"><p class="loading">Loading devices…</p></main>
Expand Down
2 changes: 2 additions & 0 deletions js/app.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { loadRegistry } from './registry.js';
import { renderHub } from './views/hub.js';
import { renderDevice } from './views/device.js';
import { initThemeToggle } from './theme.js';

const app = document.getElementById('app');

Expand Down Expand Up @@ -29,3 +30,4 @@ async function route() {

window.addEventListener('hashchange', route);
route();
initThemeToggle();
63 changes: 63 additions & 0 deletions js/theme.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// js/theme.js — theme toggle: System / Dark / Light (cycles on click)
const THEMES = ['system', 'dark', 'light'];
const ICONS = { system: '⬤', dark: '🌙', light: '☀' };
const LABELS = { system: 'System theme', dark: 'Dark theme', light: 'Light theme' };
const NEXT_LABEL = {
system: 'Switch to dark theme',
dark: 'Switch to light theme',
light: 'Switch to system theme',
};

function storedPref() {
try { return localStorage.getItem('theme') || 'system'; } catch (e) { return 'system'; }
}

function savePref(pref) {
try {
if (pref === 'system') localStorage.removeItem('theme');
else localStorage.setItem('theme', pref);
} catch (e) {}
}

function applyPref(pref) {
const root = document.documentElement;
const isDark =
pref === 'dark' ||
(pref === 'system' && matchMedia('(prefers-color-scheme: dark)').matches);
if (isDark) root.setAttribute('data-theme', 'dark');
else root.removeAttribute('data-theme');
}

export function initThemeToggle() {
const btn = document.getElementById('theme-toggle');
if (!btn) return;

const mq = matchMedia('(prefers-color-scheme: dark)');

function updateBtn(pref) {
btn.textContent = ICONS[pref];
btn.setAttribute('aria-label', LABELS[pref]);
btn.title = NEXT_LABEL[pref];
}

function setPref(pref) {
savePref(pref);
applyPref(pref);
updateBtn(pref);
}

// Keep system mode in sync when OS preference changes
mq.addEventListener('change', () => {
if (storedPref() === 'system') applyPref('system');
});

btn.addEventListener('click', () => {
const current = storedPref();
const next = THEMES[(THEMES.indexOf(current) + 1) % THEMES.length];
setPref(next);
});

// Initialise button state (makes it visible)
updateBtn(storedPref());
btn.style.visibility = '';
}
Loading