Files
simplex-chat/website/src/badges.html
T

213 lines
13 KiB
HTML

---
permalink: /badges/
title: "Support SimpleX"
eleventyExcludeFromCollections: true
templateEngineOverride: njk
---
<!doctype html>
<html lang="{{ page.url | getlang }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{ title }}</title>
<!-- Open in the site's theme so a dark-mode buyer is not flashed a light page. -->
{% include "dark-mode.html" %}
<link href="/css/tailwind.css" rel="stylesheet" />
<link href="/css/style.css" rel="stylesheet" />
<style>
:root { --nav-color: #001796; }
html.dark { --nav-color: #ffffff; }
html, body { margin: 0; }
/* A fixed bar at the site's height and spacing; the frame fills the rest of the screen and
the site footer sits below it. This is the only navbar — the app hides its own when embedded. */
.b-nav {
position: fixed; top: 0; left: 0; width: 100%; height: 54px; z-index: 50;
/* Centre the links on the page; the logo and toggle are taken out of flow below, so
neither their width nor the logo image loading can nudge the centred links. This is
how the site's own navbar keeps its menu centred. */
box-sizing: border-box; display: flex; align-items: center; justify-content: center;
/* The site's own frosted bar: translucent over the content that scrolls under it. */
background: rgba(243, 250, 255, 0.72);
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
}
html.dark .b-nav { background: rgba(0, 8, 50, 0.72); }
.b-logo { position: absolute; left: 0; padding-left: 30px; display: flex; align-items: center; height: 100%; }
.b-logo img { height: 40px; width: auto; }
.b-logo .dark { display: none; }
html.dark .b-logo .light { display: none; }
html.dark .b-logo .dark { display: block; }
.b-links { display: flex; align-items: center; gap: 60px; }
.b-links a {
font-family: "Manrope", sans-serif;
color: var(--nav-color); text-decoration: none; font-size: 18px; font-weight: 500;
position: relative;
}
.b-links a::before {
content: ""; position: absolute; width: 0; height: 1px; bottom: -2px; right: 0;
background-color: var(--nav-color); transition: width .25s ease-out;
}
.b-links a:hover::before { width: 100%; left: 0; right: auto; }
.b-theme { display: flex; align-items: center; justify-content: center; padding: 0; border: 0; background: none; cursor: pointer; }
.b-theme-top { position: absolute; right: 0; margin-right: 20px; }
.b-theme-inline { display: none; }
.b-theme svg { width: 20px; height: 20px; }
.b-theme svg path { fill: var(--nav-color); }
.b-theme .sun { display: none; }
html.dark .b-theme .sun { display: block; }
html.dark .b-theme .moon { display: none; }
/* The hamburger shows only on phones; there the links collapse into a dropdown it toggles. */
.b-burger { display: none; position: absolute; right: 0; margin-right: 16px; padding: 0; border: 0; background: none; cursor: pointer; }
.b-burger svg { width: 24px; height: 24px; }
.b-burger svg path { fill: var(--nav-color); }
.b-theme-label { font-family: "Manrope", sans-serif; color: var(--nav-color); font-size: 16px; font-weight: 500; }
/* The frame sits under the fixed navbar (the app pads its own top to clear it) and is sized
to its content by the app's height messages, so the page — not the frame — scrolls, and
the footer follows naturally. 100vh is only the size before the first message arrives. */
#badges-frame {
display: block; width: 100%; height: 100vh; border: 0;
/* An empty frame paints transparent-dark before its document loads, and the app's own
ground flashes before the site hands in its colour. Give the frame the page's own
background so any gap reads as the site, and fade the app in once it is sized and
themed, so neither flash is seen. */
background: #F3FAFF;
opacity: 0; transition: opacity .25s ease;
}
html.dark #badges-frame { background: #000832; }
@media (max-width: 640px) {
.b-burger { display: flex; align-items: center; }
.b-theme-top { display: none; }
/* The links become a frosted dropdown pinned under the hamburger, shown only when open. */
.b-links {
position: absolute; top: 54px; right: 8px;
flex-direction: column; align-items: stretch; gap: 2px;
min-width: 190px; padding: 8px;
/* Solid, and a shade off the page — the surfaces the site's own mobile menu uses — so
the panel reads as a distinct card, not see-through against the matching ground. */
background: #fff; border: 1px solid rgba(0, 23, 150, .12);
border-radius: 12px; box-shadow: 0 10px 30px rgba(0, 8, 50, .18);
display: none;
}
html.dark .b-links { background: #0a0f2b; border-color: rgba(255, 255, 255, .12); }
.b-nav.open .b-links { display: flex; }
.b-links a { padding: 10px 12px; border-radius: 8px; font-size: 16px; }
.b-links a::before { display: none; }
.b-links a:hover { background: rgba(0, 23, 150, .06); }
html.dark .b-links a:hover { background: rgba(255, 255, 255, .06); }
.b-theme-inline { display: flex; justify-content: flex-start; gap: 8px; padding: 10px 12px; }
}
</style>
</head>
<body class="bg-[#F3FAFF] dark:bg-[#000832]">
<header class="b-nav">
<a class="b-logo" href="/" aria-label="SimpleX">
<img class="light" src="/img/new/logo-light.png" alt="SimpleX">
<img class="dark" src="/img/new/logo-dark.png" alt="SimpleX">
</a>
<nav class="b-links" id="b-links" aria-label="Badges">
<!-- Home leaves for the site; Buy a code and Your codes are hash links, and the hash is
what propagates the route into the frame. -->
<a href="/">Home</a>
<a href="#/tier">Buy a code</a>
<a href="#/codes">Your codes</a>
<!-- The theme toggle rides inside the dropdown on phones; a second copy (below) sits at
the bar's right on wider screens. One `js-theme` class wires both to the handler. -->
<button class="b-theme b-theme-inline js-theme" type="button" aria-label="Toggle light or dark theme">
<svg class="moon" viewBox="5 5 20 23" xmlns="http://www.w3.org/2000/svg"><path d="M10.895 7.574c0 7.55 5.179 13.67 11.567 13.67 1.588 0 3.101-0.38 4.479-1.063-1.695 4.46-5.996 7.636-11.051 7.636-6.533 0-11.83-5.297-11.83-11.83 0-4.82 2.888-8.959 7.023-10.803-0.116 0.778-0.188 1.573-0.188 2.39z"/></svg>
<svg class="sun" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M12 17a5 5 0 110-10 5 5 0 010 10zm0-15a1 1 0 011 1v1a1 1 0 11-2 0V3a1 1 0 011-1zm0 17a1 1 0 011 1v1a1 1 0 11-2 0v-1a1 1 0 011-1zM3 11h1a1 1 0 110 2H3a1 1 0 110-2zm17 0h1a1 1 0 110 2h-1a1 1 0 110-2zM5.6 5.6a1 1 0 011.4 0l.7.7A1 1 0 116.3 7.7l-.7-.7a1 1 0 010-1.4zm11 11a1 1 0 011.4 0l.7.7a1 1 0 01-1.4 1.4l-.7-.7a1 1 0 010-1.4zM18.4 5.6a1 1 0 010 1.4l-.7.7A1 1 0 1116.3 6.3l.7-.7a1 1 0 011.4 0zM7 16.6a1 1 0 010 1.4l-.7.7a1 1 0 01-1.4-1.4l.7-.7a1 1 0 011.4 0z"/></svg>
<span class="b-theme-label">Theme</span>
</button>
</nav>
<button class="b-burger" id="b-burger" type="button" aria-label="Open menu" aria-expanded="false" aria-controls="b-links">
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M3 6h18v2H3zM3 11h18v2H3zM3 16h18v2H3z"/></svg>
</button>
<button class="b-theme b-theme-top js-theme" id="b-theme" type="button" aria-label="Toggle light or dark theme">
<svg class="moon" viewBox="5 5 20 23" xmlns="http://www.w3.org/2000/svg"><path d="M10.895 7.574c0 7.55 5.179 13.67 11.567 13.67 1.588 0 3.101-0.38 4.479-1.063-1.695 4.46-5.996 7.636-11.051 7.636-6.533 0-11.83-5.297-11.83-11.83 0-4.82 2.888-8.959 7.023-10.803-0.116 0.778-0.188 1.573-0.188 2.39z"/></svg>
<svg class="sun" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M12 17a5 5 0 110-10 5 5 0 010 10zm0-15a1 1 0 011 1v1a1 1 0 11-2 0V3a1 1 0 011-1zm0 17a1 1 0 011 1v1a1 1 0 11-2 0v-1a1 1 0 011-1zM3 11h1a1 1 0 110 2H3a1 1 0 110-2zm17 0h1a1 1 0 110 2h-1a1 1 0 110-2zM5.6 5.6a1 1 0 011.4 0l.7.7A1 1 0 116.3 7.7l-.7-.7a1 1 0 010-1.4zm11 11a1 1 0 011.4 0l.7.7a1 1 0 01-1.4 1.4l-.7-.7a1 1 0 010-1.4zM18.4 5.6a1 1 0 010 1.4l-.7.7A1 1 0 1116.3 6.3l.7-.7a1 1 0 011.4 0zM7 16.6a1 1 0 010 1.4l-.7.7a1 1 0 01-1.4-1.4l.7-.7a1 1 0 011.4 0z"/></svg>
</button>
</header>
<iframe id="badges-frame" title="Support SimpleX" src="https://badges.simplex.chat"></iframe>
{% include "footer.html" %}
<script>
(function () {
// Must match the iframe's origin exactly: messages are only sent to it and only trusted
// from it, so neither channel can be driven by another frame.
var APP = "https://badges.simplex.chat";
var frame = document.getElementById("badges-frame");
function siteTheme() {
try { return localStorage.getItem("theme") || "system"; } catch (e) { return "system"; }
}
function isDark() {
return document.documentElement.classList.contains("dark");
}
function siteBg() { return isDark() ? "#000832" : "#F3FAFF"; }
function applySiteTheme(theme) {
var dark = theme === "dark" || (theme === "system" && window.matchMedia("(prefers-color-scheme: dark)").matches);
document.documentElement.classList.toggle("dark", dark);
try { if (theme === "system") localStorage.removeItem("theme"); else localStorage.setItem("theme", theme); } catch (e) {}
}
function post(msg) { try { frame.contentWindow.postMessage(msg, APP); } catch (e) { /* not ready */ } }
function postRoute() { post({ type: "simplex-route", hash: location.hash }); }
function postColors() { post({ type: "simplex-colors", bg: siteBg() }); }
function reveal() { frame.style.opacity = "1"; }
// Fallback: reveal even if the app never posts a height (an older deploy), so the frame
// can never stay invisible. The first height message reveals it sooner, once themed.
frame.addEventListener("load", function () { setTimeout(reveal, 400); });
// Buy a code / Your codes are hash links; changing the hash — from a click or from the
// address bar — is what carries the route into the frame.
window.addEventListener("hashchange", postRoute);
window.addEventListener("message", function (e) {
if (e.origin !== APP || !e.data) return;
// The frame announces itself on load; hand it the site's theme, background and route.
if (e.data.type === "simplex-embed-ready") {
post({ type: "simplex-theme", theme: siteTheme() });
postColors();
postRoute();
}
// The buyer used the theme switch inside the frame; keep the site in step.
if (e.data.type === "simplex-theme") { applySiteTheme(e.data.theme); postColors(); }
// The frame reports its content height; size it so the site's page scrolls, not the frame.
if (e.data.type === "simplex-height" && typeof e.data.height === "number") {
frame.style.height = Math.max(0, e.data.height) + "px";
reveal();
}
});
// Both theme toggles (the bar's on wide screens, the dropdown's on phones) share the class.
document.querySelectorAll(".js-theme").forEach(function (btn) {
btn.addEventListener("click", function () {
var next = isDark() ? "light" : "dark";
applySiteTheme(next);
post({ type: "simplex-theme", theme: next });
postColors();
});
});
// The phone dropdown: the hamburger toggles it; a link tap or an outside tap closes it.
var nav = document.querySelector(".b-nav");
var burger = document.getElementById("b-burger");
function setMenu(open) {
nav.classList.toggle("open", open);
burger.setAttribute("aria-expanded", open ? "true" : "false");
}
burger.addEventListener("click", function (e) {
e.stopPropagation();
setMenu(!nav.classList.contains("open"));
});
nav.querySelectorAll(".b-links a").forEach(function (a) {
a.addEventListener("click", function () { setMenu(false); });
});
document.addEventListener("click", function (e) {
if (!nav.contains(e.target)) setMenu(false);
});
})();
</script>
</body>
</html>