From e3cedf74a5934938166a8a653785810730891130 Mon Sep 17 00:00:00 2001 From: def670 Date: Sun, 22 Mar 2026 17:19:34 -0400 Subject: [PATCH] otb-shared-brand v0.3.0: add shared theme toggle and js deploy support --- README.md | 7 ++ VERSION | 2 +- brand.css | 191 +++++++++++++++++++++++++++++------------- brand.js | 24 ++++++ deploy-mintme.sh | 24 +++--- deploy-monitor.sh | 21 ++--- deploy-otb-tracker.sh | 37 ++++---- deploy-portal.sh | 19 +++-- footer.html | 2 + header.html | 33 +++++--- 10 files changed, 239 insertions(+), 121 deletions(-) create mode 100644 brand.js diff --git a/README.md b/README.md index 05705bf..9f00bd4 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,10 @@ +## v0.3.0 - 2026-03-22 + +- Added shared theme toggle markup and behavior via brand.js. +- Updated shared header to include theme toggle. +- Added deploy scripts that also deploy/inject brand.js where needed. +- Updated tracker deploy to inject shared CSS and shared JS. + ## v0.2.0 - 2026-03-22 - Added shared brand.css, header.html, and footer.html as the canonical OTB branding source. diff --git a/VERSION b/VERSION index 1474d00..268b033 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -v0.2.0 +v0.3.0 diff --git a/brand.css b/brand.css index 68edbd3..b80d516 100644 --- a/brand.css +++ b/brand.css @@ -1,16 +1,36 @@ /* ===== OTB shared branding ===== */ +html[data-theme="dark"]{ + --otb-bg:#081225; + --otb-bg2:#09172d; + --otb-text:#e8eefc; + --otb-muted:#aab6d6; + --otb-panel:rgba(18,24,37,.98); + --otb-panel-soft:rgba(18,24,37,.78); + --otb-line:rgba(255,255,255,.08); +} + +html[data-theme="light"]{ + --otb-bg:#f4f7fb; + --otb-bg2:#eef3f9; + --otb-text:#0f172a; + --otb-muted:#475569; + --otb-panel:rgba(255,255,255,.98); + --otb-panel-soft:rgba(255,255,255,.88); + --otb-line:rgba(15,23,42,.10); +} + body{ - padding-bottom: 56px; + padding-bottom:56px; } .site-container{ - max-width: 1100px; - margin: 0 auto; - padding: 20px 18px 0 18px; + max-width:1100px; + margin:0 auto; + padding:20px 18px 0 18px; } .site-header{ - width: 100%; + width:100%; } .site-nav{ @@ -34,10 +54,10 @@ body{ width:auto; display:block; object-fit:contain; - background: rgba(255,255,255,0.92); - padding: 6px 12px; - border-radius: 999px; - box-shadow: 0 8px 24px rgba(0,0,0,0.35); + background:rgba(255,255,255,0.92); + padding:6px 12px; + border-radius:999px; + box-shadow:0 8px 24px rgba(0,0,0,0.35); } .site-title{ @@ -48,15 +68,23 @@ body{ .site-title strong{ letter-spacing:.2px; - color: var(--text, #e8eefc); + color:var(--otb-text); } .site-title span{ - color: var(--muted, #aab6d6); + color:var(--otb-muted); font-size:13px; margin-top:2px; } +.site-nav-right{ + display:flex; + align-items:center; + gap:14px; + flex-wrap:wrap; + justify-content:flex-end; +} + .site-navlinks{ display:flex; gap:12px; @@ -70,15 +98,15 @@ body{ text-decoration:none; padding:8px 10px; border-radius:12px; - color: var(--muted, #aab6d6); + color:var(--otb-muted); border:1px solid transparent; } .site-navlinks > a:hover, .dropdown-toggle:hover{ - color: var(--text, #e8eefc); - border-color: rgba(255,255,255,.08); - background: rgba(255,255,255,.03); + color:var(--otb-text); + border-color:var(--otb-line); + background:rgba(255,255,255,.03); } .dropdown{ @@ -99,8 +127,8 @@ body{ display:none; padding:10px; border-radius:14px; - background:rgba(18,24,37,.98); - border:1px solid rgba(255,255,255,.08); + background:var(--otb-panel); + border:1px solid var(--otb-line); box-shadow:0 16px 40px rgba(0,0,0,.35); z-index:9999; } @@ -114,7 +142,7 @@ body{ display:block; padding:9px 10px; border-radius:10px; - color:var(--muted, #aab6d6); + color:var(--otb-muted); text-decoration:none; white-space:nowrap; margin:0; @@ -125,63 +153,103 @@ body{ } .dropdown-menu a:hover{ - color:var(--text, #e8eefc); + color:var(--otb-text); background:rgba(255,255,255,.04); } +.otb-theme-switch{ + position:relative; + display:inline-block; + width:54px; + height:30px; + flex:0 0 auto; +} + +.otb-theme-switch input{ + opacity:0; + width:0; + height:0; +} + +.otb-theme-slider{ + position:absolute; + inset:0; + cursor:pointer; + background:rgba(255,255,255,.10); + border:1px solid var(--otb-line); + transition:.2s; + border-radius:999px; +} + +.otb-theme-slider:before{ + content:""; + position:absolute; + height:22px; + width:22px; + left:3px; + top:3px; + background:var(--otb-text); + transition:.2s; + border-radius:50%; +} + +.otb-theme-switch input:checked + .otb-theme-slider:before{ + transform:translateX(24px); +} + .otb-statusbar{ - position: fixed; - left: 0; - right: 0; - bottom: 0; - z-index: 9999; - display: flex; - align-items: center; - justify-content: center; - min-height: 42px; - padding: 8px 14px; - background: rgba(8, 16, 32, 0.94); - border-top: 1px solid rgba(255,255,255,.10); - backdrop-filter: blur(8px); - box-shadow: 0 -8px 24px rgba(0,0,0,.28); + position:fixed; + left:0; + right:0; + bottom:0; + z-index:9999; + display:flex; + align-items:center; + justify-content:center; + min-height:42px; + padding:8px 14px; + background:var(--otb-panel); + border-top:1px solid var(--otb-line); + backdrop-filter:blur(8px); + box-shadow:0 -8px 24px rgba(0,0,0,.28); } .otb-statusbar-inner{ - width: 100%; - max-width: 1100px; - display: flex; - gap: 10px; - align-items: center; - justify-content: center; - flex-wrap: wrap; - text-align: center; - color: var(--muted, #aab6d6); - font-size: 12px; - line-height: 1.35; + width:100%; + max-width:1100px; + display:flex; + gap:10px; + align-items:center; + justify-content:center; + flex-wrap:wrap; + text-align:center; + color:var(--otb-muted); + font-size:12px; + line-height:1.35; } .otb-statusbar strong{ - color: var(--text, #e8eefc); - font-weight: 700; + color:var(--otb-text); + font-weight:700; } .otb-statusbar a{ - color: #62e6b7; - text-decoration: none; - font-weight: 600; + color:#62e6b7; + text-decoration:none; + font-weight:600; } .otb-statusbar a:hover{ - text-decoration: underline; + text-decoration:underline; } .otb-dot{ - width: 6px; - height: 6px; - border-radius: 999px; - display: inline-block; - background: rgba(255,255,255,.25); - flex: 0 0 auto; + width:6px; + height:6px; + border-radius:999px; + display:inline-block; + background:rgba(255,255,255,.25); + flex:0 0 auto; } @media (max-width: 900px){ @@ -190,6 +258,11 @@ body{ flex-direction:column; } + .site-nav-right{ + width:100%; + justify-content:space-between; + } + .site-navlinks{ justify-content:flex-start; } @@ -217,11 +290,11 @@ body{ @media (max-width: 700px){ body{ - padding-bottom: 72px; + padding-bottom:72px; } .otb-statusbar-inner{ - font-size: 11px; - line-height: 1.25; + font-size:11px; + line-height:1.25; } } diff --git a/brand.js b/brand.js new file mode 100644 index 0000000..728dc30 --- /dev/null +++ b/brand.js @@ -0,0 +1,24 @@ +(function () { + function applyTheme(theme) { + document.documentElement.setAttribute("data-theme", theme); + const toggle = document.getElementById("otbThemeToggle"); + if (toggle) toggle.checked = theme === "light"; + } + + function savedTheme() { + return localStorage.getItem("otb_theme") || "dark"; + } + + window.addEventListener("DOMContentLoaded", function () { + applyTheme(savedTheme()); + + const toggle = document.getElementById("otbThemeToggle"); + if (!toggle) return; + + toggle.addEventListener("change", function () { + const theme = toggle.checked ? "light" : "dark"; + localStorage.setItem("otb_theme", theme); + applyTheme(theme); + }); + }); +})(); diff --git a/deploy-mintme.sh b/deploy-mintme.sh index 2dc60c3..d1ed9c4 100755 --- a/deploy-mintme.sh +++ b/deploy-mintme.sh @@ -9,16 +9,18 @@ mkdir -p "$BKDIR" cp -av index.html pricing.html terms.html contact.html assets/style.css "$BKDIR"/ HEADER="$(cat /home/def/otb-shared-brand/header.html)" -FOOTER="$(cat /home/def/otb-shared-brand/footer.html)" +FOOTER="$(cat /home/def/otb-shared-brand/footer.html | sed 's|__OTB_BRAND_JS__|/assets/brand.js|g')" BRANDCSS="$(cat /home/def/otb-shared-brand/brand.css)" -python3 - <' in text: text = re.sub(r'
.*?
', header, text, count=1, flags=re.S) else: - text = text.replace('', '\\n' + header, 1) + text = text.replace('', '\n' + header, 1) if '
' in text: - text = re.sub(r'
.*?
\\s*
', footer, text, count=1, flags=re.S) + text = re.sub(r'
.*?', footer, text, count=1, flags=re.S) else: - text = text.replace('', footer + '\\n', 1) + text = text.replace('', footer + '\n', 1) p.write_text(text, encoding="utf-8") cssp = Path("assets/style.css") css = cssp.read_text(encoding="utf-8") if "/* ===== OTB shared branding ===== */" in css: - css = re.sub(r'/\\* ===== OTB shared branding ===== \\*/.*', brandcss, css, flags=re.S) + css = re.sub(r'/\* ===== OTB shared branding ===== \*/.*', brandcss, css, flags=re.S) else: - css += "\\n\\n" + brandcss + css += "\n\n" + brandcss cssp.write_text(css, encoding="utf-8") print("mintme deploy complete") -PY +PY2 diff --git a/deploy-monitor.sh b/deploy-monitor.sh index 2e794ab..65875a9 100755 --- a/deploy-monitor.sh +++ b/deploy-monitor.sh @@ -9,9 +9,11 @@ mkdir -p "$BKDIR" cp -av frontend/index.html frontend/styles.css "$BKDIR"/ || true HEADER="$(cat /home/def/otb-shared-brand/header.html)" -FOOTER="$(cat /home/def/otb-shared-brand/footer.html)" +FOOTER="$(cat /home/def/otb-shared-brand/footer.html | sed 's|__OTB_BRAND_JS__|/brand.js|g')" BRANDCSS="$(cat /home/def/otb-shared-brand/brand.css)" +cp -av /home/def/otb-shared-brand/brand.js frontend/brand.js + cat > frontend/index.html < @@ -34,11 +36,6 @@ $HEADER
Loading…
- -
@@ -48,24 +45,22 @@ $HEADER
$FOOTER - - HTML -python3 - < static/brand.js + +python3 - <\n{brandcss}\n" + +text = re.sub(r'', '', text, flags=re.S) -# inject header if header not in text: - text = text.replace("", "\\n" + header) + text = text.replace("", "\n" + header) -# inject footer if footer not in text: - text = text.replace("", footer + "\\n") + text = text.replace("", footer + "\n") + +if style_block not in text: + text = text.replace("", style_block + "\n") app.write_text(text, encoding="utf-8") print("otb-tracker branding injected") -PY - -echo "==== restarting service ====" -sudo systemctl restart otb_tracker.service || true +PY2 -echo "==== done ====" +sudo systemctl restart otb-tracker.service || true diff --git a/deploy-portal.sh b/deploy-portal.sh index 2bf9c52..e79ea24 100755 --- a/deploy-portal.sh +++ b/deploy-portal.sh @@ -9,33 +9,34 @@ mkdir -p "$BKDIR" cp -av templates/includes/site_nav.html static/css/style.css templates/portal_*.html "$BKDIR"/ cp -av /home/def/otb-shared-brand/header.html templates/includes/site_nav.html +cp -av /home/def/otb-shared-brand/brand.js static/brand.js -FOOTER="$(cat /home/def/otb-shared-brand/footer.html)" +FOOTER="$(cat /home/def/otb-shared-brand/footer.html | sed 's|__OTB_BRAND_JS__|/static/brand.js|g')" BRANDCSS="$(cat /home/def/otb-shared-brand/brand.css)" -python3 - <' in text: - text = re.sub(r'
.*?
\\s*', footer, text, count=1, flags=re.S) + text = re.sub(r'
.*?', footer, text, count=1, flags=re.S) else: - text = text.replace('{% include "footer.html" %}', footer + '\\n\\n {% include "footer.html" %}') + text = text.replace('{% include "footer.html" %}', footer + '\n\n {% include "footer.html" %}') p.write_text(text, encoding="utf-8") cssp = Path("static/css/style.css") css = cssp.read_text(encoding="utf-8") if "/* ===== OTB shared branding ===== */" in css: - css = re.sub(r'/\\* ===== OTB shared branding ===== \\*/.*', brandcss, css, flags=re.S) + css = re.sub(r'/\* ===== OTB shared branding ===== \*/.*', brandcss, css, flags=re.S) else: - css += "\\n\\n" + brandcss + css += "\n\n" + brandcss cssp.write_text(css, encoding="utf-8") print("portal deploy complete") -PY +PY2 sudo systemctl restart otb_billing.service diff --git a/footer.html b/footer.html index c13121d..9cd515b 100644 --- a/footer.html +++ b/footer.html @@ -12,3 +12,5 @@
+ + diff --git a/header.html b/header.html index 06ea196..7177302 100644 --- a/header.html +++ b/header.html @@ -9,20 +9,27 @@ - + Portal + + + +