Add shared footer.js — one-line cross-subdomain wayfinding
Self-contained Shadow-DOM component so no subdomain is a dead end. Loaded via <script src="https://theradicalparty.com/footer.js" defer>. Two modes: full bar (default) and data-mode="pill" for full-screen apps. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
a851ef28df
commit
7a3b684203
1 changed files with 120 additions and 0 deletions
120
footer.js
Normal file
120
footer.js
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
/* ═══════════════════════════════════════════════════════════════════════
|
||||
The Radical Party — shared footer / wayfinding component.
|
||||
|
||||
ONE script, loaded on every subdomain, so no island is a dead end:
|
||||
<script src="https://theradicalparty.com/footer.js" defer></script>
|
||||
|
||||
- Self-contained: injects itself into <body>, styles live in a Shadow DOM
|
||||
so nothing leaks in or out of the host page's CSS.
|
||||
- Central: edit this one file, every site updates. No per-repo redeploy.
|
||||
- Two modes, chosen via a data attribute on the script tag:
|
||||
(default) full footer bar at the end of the page.
|
||||
data-mode="pill" compact fixed corner badge — for full-screen apps
|
||||
(maps, games) where a block footer would be hidden.
|
||||
Optional: data-accent="#ff0099" to match a site's own accent.
|
||||
═══════════════════════════════════════════════════════════════════════ */
|
||||
(function () {
|
||||
if (window.__radicalFooter) return;
|
||||
window.__radicalFooter = 1;
|
||||
|
||||
// Read config off our own <script> tag (currentScript is null under defer).
|
||||
var self =
|
||||
document.currentScript ||
|
||||
document.querySelector('script[src*="footer.js"]');
|
||||
var ds = (self && self.dataset) || {};
|
||||
var mode = ds.mode === 'pill' ? 'pill' : 'bar';
|
||||
var accent = ds.accent || '#ff2d55';
|
||||
var here = location.hostname; // don't link a site back to itself
|
||||
|
||||
// Wayfinding + a curated set of mission-aligned flagships (not all 25).
|
||||
var HOME = 'https://theradicalparty.com';
|
||||
var HUB = 'https://links.theradicalparty.com';
|
||||
var FLAGSHIPS = [
|
||||
['Ghost', 'https://ghost.theradicalparty.com'],
|
||||
['Watermelon', 'https://watermelon.theradicalparty.com'],
|
||||
['Search', 'https://search.theradicalparty.com'],
|
||||
['Cipher', 'https://cipher.theradicalparty.com'],
|
||||
['I Am the Law', 'https://iamthelaw.theradicalparty.com'],
|
||||
].filter(function (l) {
|
||||
return here === '' || l[1].indexOf('//' + here) === -1;
|
||||
});
|
||||
|
||||
var host = document.createElement('div');
|
||||
host.id = 'radical-footer-root';
|
||||
var shadow = host.attachShadow({ mode: 'open' });
|
||||
|
||||
var flagLinks = FLAGSHIPS.map(function (l) {
|
||||
return '<a href="' + l[1] + '">' + l[0] + '</a>';
|
||||
}).join('');
|
||||
|
||||
var barCSS =
|
||||
':host{all:initial}' +
|
||||
'*{margin:0;padding:0;box-sizing:border-box;font-family:"IBM Plex Mono",ui-monospace,SFMono-Regular,Menlo,monospace}' +
|
||||
'.rf{background:#0B0B09;color:#8b8b85;border-top:1px solid rgba(255,255,255,.08);' +
|
||||
'padding:34px 22px;box-shadow:0 -1px 0 ' + accent + '22}' +
|
||||
'.rf-in{max-width:1040px;margin:0 auto;display:flex;flex-wrap:wrap;gap:24px 40px;align-items:flex-start;justify-content:space-between}' +
|
||||
'.rf-brand{display:flex;flex-direction:column;gap:8px;min-width:200px}' +
|
||||
'.rf-logo{display:inline-flex;align-items:center;gap:9px;text-decoration:none}' +
|
||||
'.rf-dot{width:11px;height:11px;border-radius:50%;background:' + accent + ';box-shadow:0 0 12px ' + accent + '99;flex:none}' +
|
||||
'.rf-word{font-family:"Syne","IBM Plex Mono",monospace;font-weight:800;font-size:16px;letter-spacing:.14em;' +
|
||||
'color:#F0EDE6;text-transform:uppercase}' +
|
||||
'.rf-vvv{font-size:10px;letter-spacing:.32em;text-transform:uppercase;color:' + accent + '}' +
|
||||
'.rf-tag{font-size:11px;line-height:1.6;color:#6f6f69;max-width:280px}' +
|
||||
'.rf-cols{display:flex;gap:52px;flex-wrap:wrap}' +
|
||||
'.rf-col{display:flex;flex-direction:column;gap:11px}' +
|
||||
'.rf-h{font-size:9px;letter-spacing:.28em;text-transform:uppercase;color:#55554f;margin-bottom:3px}' +
|
||||
'.rf a{color:#b7b7b0;text-decoration:none;font-size:12.5px;letter-spacing:.02em;transition:color .15s}' +
|
||||
'.rf a:hover{color:' + accent + '}' +
|
||||
'.rf-logo:hover .rf-word{color:' + accent + '}' +
|
||||
'.rf-btm{max-width:1040px;margin:26px auto 0;padding-top:16px;border-top:1px solid rgba(255,255,255,.06);' +
|
||||
'display:flex;justify-content:space-between;flex-wrap:wrap;gap:8px;font-size:9px;letter-spacing:.2em;text-transform:uppercase;color:#55554f}' +
|
||||
'@media(max-width:640px){.rf-cols{gap:32px}.rf{padding:28px 18px}}';
|
||||
|
||||
var barHTML =
|
||||
'<div class="rf"><div class="rf-in">' +
|
||||
'<div class="rf-brand">' +
|
||||
'<a class="rf-logo" href="' + HOME + '"><span class="rf-dot"></span><span class="rf-word">Radical</span></a>' +
|
||||
'<span class="rf-vvv">Vibe · Vote · Veto</span>' +
|
||||
'<span class="rf-tag">Australia’s direct-democracy movement — and everything it builds in the open.</span>' +
|
||||
'</div>' +
|
||||
'<div class="rf-cols">' +
|
||||
'<div class="rf-col"><span class="rf-h">Movement</span>' +
|
||||
'<a href="' + HOME + '">The Platform</a>' +
|
||||
'<a href="' + HOME + '#about">One Law</a>' +
|
||||
'<a href="' + HOME + '#join">Join</a>' +
|
||||
'<a href="' + HUB + '">All Projects</a>' +
|
||||
'</div>' +
|
||||
(flagLinks ? '<div class="rf-col"><span class="rf-h">We Build</span>' + flagLinks + '</div>' : '') +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'<div class="rf-btm"><span>© 2026 The Radical Party</span><span>Radical by default</span></div>' +
|
||||
'</div>';
|
||||
|
||||
var pillCSS =
|
||||
':host{all:initial}' +
|
||||
'*{margin:0;padding:0;box-sizing:border-box;font-family:"IBM Plex Mono",ui-monospace,monospace}' +
|
||||
'.rf-pill{position:fixed;right:14px;bottom:14px;z-index:2147483000;display:inline-flex;align-items:center;gap:8px;' +
|
||||
'background:rgba(11,11,9,.92);backdrop-filter:blur(8px);border:1px solid rgba(255,255,255,.12);' +
|
||||
'border-radius:999px;padding:8px 14px;text-decoration:none;box-shadow:0 6px 20px rgba(0,0,0,.4);transition:border-color .15s}' +
|
||||
'.rf-pill:hover{border-color:' + accent + '}' +
|
||||
'.rf-dot{width:9px;height:9px;border-radius:50%;background:' + accent + ';box-shadow:0 0 10px ' + accent + '99}' +
|
||||
'.rf-txt{font-size:11px;letter-spacing:.06em;color:#e7e4dd}.rf-txt b{color:#fff;font-weight:700}' +
|
||||
'.rf-arrow{color:' + accent + ';font-size:12px}';
|
||||
|
||||
var pillHTML =
|
||||
'<a class="rf-pill" href="' + HUB + '" title="Explore The Radical Party">' +
|
||||
'<span class="rf-dot"></span>' +
|
||||
'<span class="rf-txt">Part of <b>The Radical Party</b></span>' +
|
||||
'<span class="rf-arrow">↗</span></a>';
|
||||
|
||||
shadow.innerHTML =
|
||||
'<style>' + (mode === 'pill' ? pillCSS : barCSS) + '</style>' +
|
||||
(mode === 'pill' ? pillHTML : barHTML);
|
||||
|
||||
function mount() {
|
||||
if (document.getElementById('radical-footer-root')) return;
|
||||
document.body.appendChild(host);
|
||||
}
|
||||
if (document.body) mount();
|
||||
else document.addEventListener('DOMContentLoaded', mount);
|
||||
})();
|
||||
Loading…
Add table
Reference in a new issue