web: the muted text that was never muted, and the stylesheet fetched every time
Every text-[color:var(--ink)]/50, border-[color:var(--ink)]/10 and
bg-[color:var(--ink)]/5 in the templates compiled to nothing. Tailwind v3
cannot put an opacity on an arbitrary var() colour, so all 538 of them were
dropped from output.css without a word. Meta text rendered at full ink, card
borders fell back to Tailwind's default grey (bright white rings at night),
and the washes, backdrops and hover pills simply weren't there, on the news
site, the casino and the adventure pages alike.
The palette is now written once per phase and casino room as bare rgb
channels (--ink-rgb and friends), the plain vars are built from them, and
tailwind.config.js exposes ink, muted, accent, warn and card as real colours.
The classes are text-ink/80, border-ink/10, bg-accent/20 now, and they compile.
Restoring the intended opacities as-is would have traded flat for illegible:
ink at 50% measures 2.7 to 2.9:1 on the lit phases. So secondary text gets
its own colour, --muted, per phase and per casino room, taken as faint as it
can go while still clearing 4.5:1 on both the card and the page. Everything
that was ink at 35 to 70% is text-muted. The 20-30% ones are separators and
empty-cell dashes and stay faint.
The bg-theme-* chips carry white text everywhere they appear, and eight of
them measured 2.5 to 3.2:1. Same hues, taken just dark enough to pass (4.6 to
4.7:1). eu, lego and adventure already passed and are unchanged, and the
underline and border colours keep the old pastels since they carry no text.
Static files came out of the embedded FS with no Last-Modified, no ETag and
no Cache-Control, so every page view downloaded the stylesheet and every
script in full, and the service worker's "cheap 304" never happened. Each
file is hashed once now. Templates link through {{asset}}, which appends
?v=<hash>; a request naming the current hash is served immutable for a year,
and everything else gets the hash as its ETag and revalidates to a 304. The
service worker serves versioned URLs cache-first, drops older versions of the
same file as new ones land, and moves to cache v6.
This commit is contained in:
@@ -19,10 +19,10 @@
|
||||
has happened to since their last visit. Above the Siege because everything
|
||||
below this line is the realm's news and this is the reader's own. */}}
|
||||
{{if .Away.Has}}
|
||||
<section class="mb-6 rounded-3xl bg-[color:var(--card)] border-2 border-theme-adventure/30 p-5 sm:p-6 shadow-pete">
|
||||
<section class="mb-6 rounded-3xl bg-card border-2 border-theme-adventure/30 p-5 sm:p-6 shadow-pete">
|
||||
<div class="flex items-baseline justify-between gap-3 flex-wrap">
|
||||
<h2 class="font-display text-xl font-bold">While you were away</h2>
|
||||
<span class="text-xs uppercase tracking-wider text-[color:var(--ink)]/45">{{.Away.Name}} · past {{.Away.Since}}</span>
|
||||
<span class="text-xs uppercase tracking-wider text-muted">{{.Away.Name}} · past {{.Away.Since}}</span>
|
||||
</div>
|
||||
<ul class="mt-3 space-y-2">
|
||||
{{range .Away.Lines}}
|
||||
@@ -31,9 +31,9 @@
|
||||
<span class="shrink-0" aria-hidden="true">{{.Emoji}}</span>
|
||||
<span class="flex-1 text-sm">
|
||||
<span class="font-semibold group-hover:text-theme-adventure group-hover:underline">{{.Label}}</span>
|
||||
{{if .Line}}<span class="text-[color:var(--ink)]/60"> — {{.Line}}</span>{{end}}
|
||||
{{if .Line}}<span class="text-muted"> — {{.Line}}</span>{{end}}
|
||||
</span>
|
||||
<span class="text-xs text-[color:var(--ink)]/40 shrink-0 tabular-nums">{{.When}}</span>
|
||||
<span class="text-xs text-muted shrink-0 tabular-nums">{{.When}}</span>
|
||||
</a>
|
||||
</li>
|
||||
{{end}}
|
||||
@@ -64,22 +64,22 @@
|
||||
</div>
|
||||
</a>
|
||||
{{else if .Siege.History}}
|
||||
<a href="/adventure/siege" class="flex items-center gap-3 mb-6 rounded-2xl bg-[color:var(--card)] border-2 border-[color:var(--ink)]/10 px-5 py-3 shadow-pete hover:border-theme-adventure/40 transition">
|
||||
<a href="/adventure/siege" class="flex items-center gap-3 mb-6 rounded-2xl bg-card border-2 border-ink/10 px-5 py-3 shadow-pete hover:border-theme-adventure/40 transition">
|
||||
<span class="text-lg" aria-hidden="true">🏰</span>
|
||||
<span class="text-sm text-[color:var(--ink)]/70">Nothing camped outside town right now — <span class="font-semibold text-theme-adventure">the sieges we've fought</span></span>
|
||||
<span class="text-sm text-muted">Nothing camped outside town right now — <span class="font-semibold text-theme-adventure">the sieges we've fought</span></span>
|
||||
</a>
|
||||
{{end}}
|
||||
|
||||
<section class="mb-10" id="roster" data-stale="{{.RosterStale}}">
|
||||
<div class="flex items-baseline justify-between mb-3">
|
||||
<h2 class="font-display text-2xl font-bold">Out there right now</h2>
|
||||
<p class="text-xs uppercase tracking-wider text-[color:var(--ink)]/50" id="roster-status">
|
||||
<p class="text-xs uppercase tracking-wider text-muted" id="roster-status">
|
||||
{{if .RosterStale}}last known — the wire's gone quiet{{else}}live{{end}}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="rounded-3xl bg-[color:var(--card)] border-2 border-[color:var(--ink)]/10 shadow-pete overflow-hidden {{if .RosterStale}}opacity-60{{end}}" id="roster-card">
|
||||
<ul class="divide-y divide-[color:var(--ink)]/10" id="roster-list">
|
||||
<div class="rounded-3xl bg-card border-2 border-ink/10 shadow-pete overflow-hidden {{if .RosterStale}}opacity-60{{end}}" id="roster-card">
|
||||
<ul class="divide-y divide-ink/10" id="roster-list">
|
||||
{{range .Roster}}
|
||||
{{/* The layout is .roster-row, not utilities: this markup has a twin in the
|
||||
script below that re-renders the same list on every poll, and the two
|
||||
@@ -87,22 +87,22 @@
|
||||
<li class="roster-row" data-token="{{.Token}}" data-name="{{.Name}}">
|
||||
<span class="roster-row-icon" aria-hidden="true">{{if .OnRun}}⚔{{else}}🏠{{end}}</span>
|
||||
<a href="/adventure/who/{{.Token}}" class="roster-row-name font-semibold truncate hover:text-theme-adventure hover:underline">{{.Name}}</a>
|
||||
<span class="roster-row-meta text-sm text-[color:var(--ink)]/60">lv {{.Level}} {{.ClassRace}}</span>
|
||||
<span class="roster-row-where text-sm {{if .OnRun}}font-semibold{{else}}text-[color:var(--ink)]/60{{end}}">
|
||||
{{.Where}}{{if .Idle}} <span class="text-[color:var(--ink)]/45">· {{.Idle}}</span>{{end}}
|
||||
<span class="roster-row-meta text-sm text-muted">lv {{.Level}} {{.ClassRace}}</span>
|
||||
<span class="roster-row-where text-sm {{if .OnRun}}font-semibold{{else}}text-muted{{end}}">
|
||||
{{.Where}}{{if .Idle}} <span class="text-muted">· {{.Idle}}</span>{{end}}
|
||||
</span>
|
||||
{{if and $.User .OnRun}}
|
||||
<button type="button" class="mischief-send roster-row-act shrink-0 rounded-full bg-[color:var(--ink)]/5 hover:bg-red-500 hover:text-white border border-[color:var(--ink)]/15 px-3 py-1 text-xs font-semibold transition" data-token="{{.Token}}" data-name="{{.Name}}">send trouble</button>
|
||||
<button type="button" class="mischief-send roster-row-act shrink-0 rounded-full bg-ink/5 hover:bg-red-500 hover:text-white border border-ink/15 px-3 py-1 text-xs font-semibold transition" data-token="{{.Token}}" data-name="{{.Name}}">send trouble</button>
|
||||
{{end}}
|
||||
</li>
|
||||
{{else}}
|
||||
<li class="px-5 py-8 text-center text-[color:var(--ink)]/60">Nobody's out at the moment. Quiet week in the realm.</li>
|
||||
<li class="px-5 py-8 text-center text-muted">Nobody's out at the moment. Quiet week in the realm.</li>
|
||||
{{end}}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{{if not .User}}
|
||||
<p class="mt-3 text-xs text-[color:var(--ink)]/50">
|
||||
<p class="mt-3 text-xs text-muted">
|
||||
<a href="/auth/login?next={{.Path}}" class="underline hover:text-red-500">Sign in</a> to send something unpleasant their way.
|
||||
</p>
|
||||
{{end}}
|
||||
@@ -111,30 +111,30 @@
|
||||
<!-- Your open contracts. Rendered from filed facts, never from live game state:
|
||||
Pete only knows what gogobee told it happened. -->
|
||||
<div id="mischief-orders" class="mt-4 hidden">
|
||||
<h3 class="text-xs uppercase tracking-wider text-[color:var(--ink)]/50 mb-2">Trouble you've paid for</h3>
|
||||
<h3 class="text-xs uppercase tracking-wider text-muted mb-2">Trouble you've paid for</h3>
|
||||
<ul id="mischief-orders-list" class="space-y-2 text-sm"></ul>
|
||||
</div>
|
||||
|
||||
<!-- The buy dialog. Prices are gogobee's, pulled live from /api/mischief/catalog;
|
||||
nothing here asserts a number of its own. -->
|
||||
<div id="mischief-modal" class="fixed inset-0 z-50 hidden items-center justify-center bg-black/50 p-4">
|
||||
<div class="w-full max-w-md rounded-3xl bg-[color:var(--card)] border-2 border-[color:var(--ink)]/10 shadow-pete overflow-hidden">
|
||||
<div class="flex items-center justify-between px-5 py-4 border-b border-[color:var(--ink)]/10">
|
||||
<div class="w-full max-w-md rounded-3xl bg-card border-2 border-ink/10 shadow-pete overflow-hidden">
|
||||
<div class="flex items-center justify-between px-5 py-4 border-b border-ink/10">
|
||||
<h3 class="font-display text-xl font-bold">Send trouble to <span id="mischief-target">—</span></h3>
|
||||
<button type="button" id="mischief-close" class="text-2xl leading-none text-[color:var(--ink)]/50 hover:text-[color:var(--ink)]" aria-label="close">×</button>
|
||||
<button type="button" id="mischief-close" class="text-2xl leading-none text-muted hover:text-ink" aria-label="close">×</button>
|
||||
</div>
|
||||
<div class="px-5 py-4 space-y-3">
|
||||
<p class="text-sm text-[color:var(--ink)]/60">Pick how bad. They won't know who sent it — unless you sign it, or they live to read the receipt.</p>
|
||||
<p class="text-sm text-muted">Pick how bad. They won't know who sent it — unless you sign it, or they live to read the receipt.</p>
|
||||
<div id="mischief-tiers" class="space-y-2"></div>
|
||||
<label class="flex items-center gap-2 text-sm">
|
||||
<input type="checkbox" id="mischief-signed" class="rounded border-[color:var(--ink)]/30">
|
||||
<span>Sign my name to it <span class="text-[color:var(--ink)]/45">(+25%, and they'll know)</span></span>
|
||||
<input type="checkbox" id="mischief-signed" class="rounded border-ink/30">
|
||||
<span>Sign my name to it <span class="text-muted">(+25%, and they'll know)</span></span>
|
||||
</label>
|
||||
<p id="mischief-balance" class="text-xs text-[color:var(--ink)]/50"></p>
|
||||
<p id="mischief-balance" class="text-xs text-muted"></p>
|
||||
<p id="mischief-result" class="text-sm hidden"></p>
|
||||
</div>
|
||||
<div class="flex items-center justify-end gap-2 px-5 py-4 border-t border-[color:var(--ink)]/10">
|
||||
<button type="button" id="mischief-cancel" class="rounded-full px-4 py-2 text-sm font-semibold text-[color:var(--ink)]/60 hover:text-[color:var(--ink)]">Never mind</button>
|
||||
<div class="flex items-center justify-end gap-2 px-5 py-4 border-t border-ink/10">
|
||||
<button type="button" id="mischief-cancel" class="rounded-full px-4 py-2 text-sm font-semibold text-muted hover:text-ink">Never mind</button>
|
||||
<button type="button" id="mischief-confirm" class="rounded-full bg-red-500 text-white px-5 py-2 text-sm font-semibold shadow-pete hover:-translate-y-0.5 transition disabled:opacity-40 disabled:translate-y-0" disabled>Put the word out</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -160,17 +160,17 @@
|
||||
}
|
||||
|
||||
function row(a) {
|
||||
var idle = a.Idle ? ' <span class="text-[color:var(--ink)]/45">· ' + esc(a.Idle) + '</span>' : '';
|
||||
var idle = a.Idle ? ' <span class="text-muted">· ' + esc(a.Idle) + '</span>' : '';
|
||||
// The server-rendered twin of this row is above, in the {{"{{range .Roster}}"}}
|
||||
// block. Keep the class names identical: the layout is all in .roster-row.
|
||||
var button = (signedIn && a.OnRun)
|
||||
? '<button type="button" class="mischief-send roster-row-act shrink-0 rounded-full bg-[color:var(--ink)]/5 hover:bg-red-500 hover:text-white border border-[color:var(--ink)]/15 px-3 py-1 text-xs font-semibold transition" data-token="' + esc(a.Token) + '" data-name="' + esc(a.Name) + '">send trouble</button>'
|
||||
? '<button type="button" class="mischief-send roster-row-act shrink-0 rounded-full bg-ink/5 hover:bg-red-500 hover:text-white border border-ink/15 px-3 py-1 text-xs font-semibold transition" data-token="' + esc(a.Token) + '" data-name="' + esc(a.Name) + '">send trouble</button>'
|
||||
: '';
|
||||
return '<li class="roster-row" data-token="' + esc(a.Token) + '" data-name="' + esc(a.Name) + '">' +
|
||||
'<span class="roster-row-icon" aria-hidden="true">' + (a.OnRun ? '⚔' : '🏠') + '</span>' +
|
||||
'<a href="/adventure/who/' + esc(a.Token) + '" class="roster-row-name font-semibold truncate hover:text-theme-adventure hover:underline">' + esc(a.Name) + '</a>' +
|
||||
'<span class="roster-row-meta text-sm text-[color:var(--ink)]/60">lv ' + esc(a.Level) + ' ' + esc(a.ClassRace) + '</span>' +
|
||||
'<span class="roster-row-where text-sm ' + (a.OnRun ? 'font-semibold' : 'text-[color:var(--ink)]/60') + '">' +
|
||||
'<span class="roster-row-meta text-sm text-muted">lv ' + esc(a.Level) + ' ' + esc(a.ClassRace) + '</span>' +
|
||||
'<span class="roster-row-where text-sm ' + (a.OnRun ? 'font-semibold' : 'text-muted') + '">' +
|
||||
esc(a.Where) + idle + '</span>' + button + '</li>';
|
||||
}
|
||||
|
||||
@@ -182,7 +182,7 @@
|
||||
var rows = data.adventurers || [];
|
||||
list.innerHTML = rows.length
|
||||
? rows.map(row).join('')
|
||||
: '<li class="px-5 py-8 text-center text-[color:var(--ink)]/60">Nobody\'s out at the moment. Quiet week in the realm.</li>';
|
||||
: '<li class="px-5 py-8 text-center text-muted">Nobody\'s out at the moment. Quiet week in the realm.</li>';
|
||||
status.textContent = data.stale ? "last known — the wire's gone quiet" : 'live';
|
||||
card.classList.toggle('opacity-60', !!data.stale);
|
||||
})
|
||||
@@ -226,7 +226,7 @@ function initMischief(list, esc) {
|
||||
|
||||
function drawTiers() {
|
||||
if (!catalog || !catalog.tiers || !catalog.tiers.length) {
|
||||
tiersEl.innerHTML = '<p class="text-sm text-[color:var(--ink)]/50">The catalogue is between snapshots. Try again in a moment.</p>';
|
||||
tiersEl.innerHTML = '<p class="text-sm text-muted">The catalogue is between snapshots. Try again in a moment.</p>';
|
||||
return;
|
||||
}
|
||||
var signed = signedEl.checked;
|
||||
@@ -236,11 +236,11 @@ function initMischief(list, esc) {
|
||||
var price = signed ? t.signed_fee : t.fee;
|
||||
var tooDear = known && euro < price;
|
||||
return '<button type="button" class="mischief-tier w-full text-left rounded-2xl border-2 px-4 py-2 transition ' +
|
||||
(chosen === t.key ? 'border-red-500 bg-red-500/10' : 'border-[color:var(--ink)]/10 hover:border-[color:var(--ink)]/25') +
|
||||
(chosen === t.key ? 'border-red-500 bg-red-500/10' : 'border-ink/10 hover:border-ink/25') +
|
||||
(tooDear ? ' opacity-40 cursor-not-allowed' : '') + '" data-key="' + esc(t.key) + '"' + (tooDear ? ' disabled' : '') + '>' +
|
||||
'<span class="flex items-baseline justify-between"><span class="font-semibold">' + esc(t.display) + '</span>' +
|
||||
'<span class="font-semibold">' + euros(price) + '</span></span>' +
|
||||
(t.blurb ? '<span class="block text-xs text-[color:var(--ink)]/55 mt-0.5">' + esc(t.blurb) + '</span>' : '') +
|
||||
(t.blurb ? '<span class="block text-xs text-muted mt-0.5">' + esc(t.blurb) + '</span>' : '') +
|
||||
'</button>';
|
||||
}).join('');
|
||||
balanceEl.textContent = known ? 'You have about ' + euros(euro) + ' to spend.' : '';
|
||||
@@ -275,10 +275,10 @@ function initMischief(list, esc) {
|
||||
var map = {
|
||||
pending: ['on its way', 'bg-amber-500/15 text-amber-700'],
|
||||
placed: ['out for delivery', 'bg-red-500/15 text-red-600'],
|
||||
bounced_funds: ["couldn't afford it", 'bg-[color:var(--ink)]/10 text-[color:var(--ink)]/60'],
|
||||
bounced_ineligible: ['no longer a mark', 'bg-[color:var(--ink)]/10 text-[color:var(--ink)]/60']
|
||||
bounced_funds: ["couldn't afford it", 'bg-ink/10 text-muted'],
|
||||
bounced_ineligible: ['no longer a mark', 'bg-ink/10 text-muted']
|
||||
};
|
||||
var m = map[statusText] || [statusText, 'bg-[color:var(--ink)]/10 text-[color:var(--ink)]/60'];
|
||||
var m = map[statusText] || [statusText, 'bg-ink/10 text-muted'];
|
||||
return '<span class="rounded-full px-2 py-0.5 text-xs font-semibold ' + m[1] + '">' + esc(m[0]) + '</span>';
|
||||
}
|
||||
|
||||
@@ -289,10 +289,10 @@ function initMischief(list, esc) {
|
||||
if (!orders || !orders.length) { ordersWrap.classList.add('hidden'); return; }
|
||||
ordersWrap.classList.remove('hidden');
|
||||
ordersList.innerHTML = orders.map(function (o) {
|
||||
var detail = o.detail ? ' <span class="text-[color:var(--ink)]/45">· ' + esc(o.detail) + '</span>' : '';
|
||||
var detail = o.detail ? ' <span class="text-muted">· ' + esc(o.detail) + '</span>' : '';
|
||||
return '<li class="flex items-center gap-3">' +
|
||||
'<span class="font-semibold">' + esc(o.target_name) + '</span>' +
|
||||
'<span class="text-[color:var(--ink)]/55">' + esc(o.tier) + (o.signed ? ', signed' : '') + '</span>' +
|
||||
'<span class="text-muted">' + esc(o.tier) + (o.signed ? ', signed' : '') + '</span>' +
|
||||
'<span class="ml-auto">' + badge(o.status) + '</span>' + detail + '</li>';
|
||||
}).join('');
|
||||
})
|
||||
@@ -328,14 +328,14 @@ function initMischief(list, esc) {
|
||||
loadOrders();
|
||||
setTimeout(close, 1200);
|
||||
} else {
|
||||
resultEl.className = 'text-sm text-[color:var(--ink)]/70';
|
||||
resultEl.className = 'text-sm text-muted';
|
||||
resultEl.textContent = (res.body && res.body.error) || 'That didn\'t take. Try again.';
|
||||
confirmEl.disabled = false;
|
||||
}
|
||||
})
|
||||
.catch(function () {
|
||||
resultEl.classList.remove('hidden');
|
||||
resultEl.className = 'text-sm text-[color:var(--ink)]/70';
|
||||
resultEl.className = 'text-sm text-muted';
|
||||
resultEl.textContent = 'The line dropped. Try again.';
|
||||
confirmEl.disabled = false;
|
||||
});
|
||||
@@ -356,15 +356,15 @@ function initMischief(list, esc) {
|
||||
|
||||
<nav class="mt-10 flex items-center justify-between">
|
||||
{{if .HasPrev}}
|
||||
<a href="{{.PrevURL}}" class="rounded-full bg-[color:var(--card)] border-2 border-[color:var(--ink)]/10 px-5 py-2 font-semibold shadow-pete hover:-translate-y-0.5 transition">← newer</a>
|
||||
<a href="{{.PrevURL}}" class="rounded-full bg-card border-2 border-ink/10 px-5 py-2 font-semibold shadow-pete hover:-translate-y-0.5 transition">← newer</a>
|
||||
{{else}}<span></span>{{end}}
|
||||
<span class="text-sm text-[color:var(--ink)]/60">page {{.Page}}</span>
|
||||
<span class="text-sm text-muted">page {{.Page}}</span>
|
||||
{{if .HasNext}}
|
||||
<a href="{{.NextURL}}" class="rounded-full bg-theme-{{.Channel.Theme}} text-white px-5 py-2 font-semibold shadow-pete hover:-translate-y-0.5 transition">older →</a>
|
||||
{{else}}<span></span>{{end}}
|
||||
</nav>
|
||||
{{else}}
|
||||
<div class="rounded-3xl bg-[color:var(--card)] border-2 border-dashed border-[color:var(--ink)]/15 p-10 text-center text-[color:var(--ink)]/60">
|
||||
<div class="rounded-3xl bg-card border-2 border-dashed border-ink/15 p-10 text-center text-muted">
|
||||
Pete hasn't catalogued anything for {{.Channel.Title}} yet.
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
Reference in New Issue
Block a user