Use the theme's cards, buttons, inputs and filter buttons everywhere
Build & Deploy / deploy (push) Successful in 2m48s

Every hand-written card, button, input, the profiler's filter buttons, the
upload area, the chat bubbles, the nav links and the shortcut key now use
Weircon-wind components. The theme is imported into Tailwind's components
layer so utilities on the same element still apply.

Claude-Session: https://claude.ai/code/session_012LCTmU2ewo8tnM1dByYAoG
This commit is contained in:
Asger Geel Weirsøe
2026-10-29 18:52:00 +01:00
parent a352b32fac
commit 490835e6ae
21 changed files with 77 additions and 96 deletions
+3 -4
View File
@@ -19,8 +19,8 @@ const t = lang === 'en' ? {
<form id="chat-form" class="mt-6 flex gap-3" data-thinking={t.thinking} data-offline={t.offline} data-unreachable={t.unreachable}>
<input id="chat-input" type="text" required maxlength="500" autocomplete="off"
placeholder={t.placeholder}
class="flex-1 rounded-xl border border-border bg-surface/60 px-4 py-2.5 outline-none focus:border-accent" />
<button type="submit" class="rounded-xl bg-accent px-5 py-2.5 font-medium text-bg transition-transform hover:-translate-y-0.5">Send</button>
class="flex-1 wt-input wt-input--lg" />
<button type="submit" class="wt-btn wt-btn--primary wt-btn--lg">Send</button>
</form>
<p class="mt-3 text-xs text-muted/80">{t.disclaimer}</p>
@@ -49,8 +49,7 @@ const t = lang === 'en' ? {
function bubble(role, text) {
var log = document.getElementById('chat-log');
var d = document.createElement('div');
d.className = 'rounded-2xl border border-border p-4 text-sm whitespace-pre-wrap ' +
(role === 'user' ? 'bg-surface-2 ml-8' : 'bg-surface/60 mr-8');
d.className = 'wt-card text-sm whitespace-pre-wrap ' + (role === 'user' ? 'wt-card--alt ml-8' : 'mr-8');
if (role === 'user') d.textContent = text; else d.innerHTML = linkify(text);
log.appendChild(d);
d.scrollIntoView({ behavior: 'smooth', block: 'end' });
+2 -2
View File
@@ -12,14 +12,14 @@ const en = lang === 'en';
aria-live="polite"
aria-label={en ? 'Cookie notice' : 'Cookie-besked'}
>
<div class="mx-auto flex max-w-3xl flex-col gap-3 rounded-2xl border border-border bg-surface/95 p-4 shadow-xl backdrop-blur sm:flex-row sm:items-center sm:justify-between">
<div class="mx-auto flex max-w-3xl flex-col gap-3 wt-card wt-card--raised sm:flex-row sm:items-center sm:justify-between">
<p class="text-sm text-muted">
{en ? 'This site uses cookies to improve your experience and keep anonymous visitor statistics. By using the site you accept this.' : 'Denne side bruger cookies til at forbedre din oplevelse og lave anonym statistik over besøg. Ved at bruge siden accepterer du det.'}
<a href="/cookies" class="text-accent underline">{en ? 'Read more (Danish)' : 'Læs mere'}</a>.
</p>
<button
id="cookie-ok"
class="shrink-0 self-start rounded-xl bg-accent px-5 py-2 text-sm font-medium text-bg transition-transform hover:-translate-y-0.5 sm:self-auto"
class="shrink-0 self-start wt-btn wt-btn--primary sm:self-auto"
>OK</button>
</div>
</div>
+2 -4
View File
@@ -17,10 +17,8 @@ const isActive = (href: string) => (href === '/' || href === '/en' ? path === hr
<li>
<a
href={item.href}
class:list={[
'rounded-lg px-3 py-1.5 transition-colors hover:text-text',
isActive(item.href) ? 'text-text bg-surface-2' : 'text-muted',
]}
class="wt-nav-link"
aria-current={isActive(item.href) ? 'page' : undefined}
>{item.label}</a>
</li>
))}
+1 -1
View File
@@ -3,7 +3,7 @@ interface Props { href: string; title: string; date: Date; description?: string;
const { href, title, date, description, tags = [] } = Astro.props;
const d = date.toLocaleDateString('da-DK', { year: 'numeric', month: 'long', day: 'numeric' });
---
<article class="group rounded-2xl border border-border bg-surface/60 p-5 transition-colors hover:border-accent/50">
<article class="group wt-card wt-card--hover">
<a href={href} class="block">
<div class="text-xs text-muted">{d}</div>
<h3 class="mt-1 text-lg font-semibold tracking-tight group-hover:text-accent">{title}</h3>
+1 -1
View File
@@ -5,6 +5,6 @@ import Base from '../layouts/Base.astro';
<div class="py-20 text-center">
<h1 class="text-6xl font-bold tracking-tight">404</h1>
<p class="mt-4 text-muted">Siden findes ikke.</p>
<a href="/" class="mt-8 inline-block rounded-xl bg-accent px-5 py-2.5 font-medium text-bg">Til forsiden</a>
<a href="/" class="mt-8 wt-btn wt-btn--primary wt-btn--lg">Til forsiden</a>
</div>
</Base>
+1 -1
View File
@@ -22,7 +22,7 @@ const stack = [
ingen tredjeparts-fonts, ingen eksterne trackere.. bare en statisk side der gør hvad den får besked på.
</p>
<div class="mt-8 overflow-hidden rounded-2xl border border-border">
<div class="mt-8 wt-card wt-card--flush">
<table class="w-full text-sm">
<tbody>
{stack.map((row) => (
+2 -2
View File
@@ -11,7 +11,7 @@ import Base from '../layouts/Base.astro';
<h2 class="mt-10 text-2xl font-bold tracking-tight">Hvad bruger vi dem til?</h2>
<div class="mt-5 grid gap-4">
<div class="rounded-2xl border border-border bg-surface/60 p-5">
<div class="wt-card">
<h3 class="font-semibold tracking-tight">Statistik &amp; analyse</h3>
<p class="mt-2 text-sm text-muted">
Jeg bruger <strong class="text-text">Matomo</strong> til at se hvor mange der besøger siden,
@@ -25,7 +25,7 @@ import Base from '../layouts/Base.astro';
</ul>
</div>
<div class="rounded-2xl border border-border bg-surface/60 p-5">
<div class="wt-card">
<h3 class="font-semibold tracking-tight">Funktionelt</h3>
<p class="mt-2 text-sm text-muted">
Husker dine valg på siden. Det er ikke sporing, og det forlader aldrig din browser.
+7 -7
View File
@@ -126,7 +126,7 @@ const sprog = ['Dansk (modersmål)', 'Engelsk (flydende i tale og skrift)'];
<h2 class="text-sm font-semibold uppercase tracking-widest text-accent">Erhvervserfaring</h2>
<div class="mt-4 space-y-4">
{erhverv.map((e) => (
<div class="rounded-2xl border border-border bg-surface/60 p-5">
<div class="wt-card">
<h3 class="font-semibold">{e.org} <span class="font-normal text-muted">· {e.sted}</span></h3>
<div class="mt-3 space-y-3">
{e.roller.map((r) => (
@@ -150,7 +150,7 @@ const sprog = ['Dansk (modersmål)', 'Engelsk (flydende i tale og skrift)'];
<h2 class="text-sm font-semibold uppercase tracking-widest text-accent">Uddannelse</h2>
<div class="mt-4 space-y-4">
{uddannelse.map((u) => (
<div class="rounded-2xl border border-border bg-surface/60 p-5">
<div class="wt-card">
<div class="flex flex-wrap items-baseline justify-between gap-2">
<h3 class="font-semibold">{u.hvad}</h3><span class="text-xs text-muted">{u.naar}</span>
</div>
@@ -165,7 +165,7 @@ const sprog = ['Dansk (modersmål)', 'Engelsk (flydende i tale og skrift)'];
<h2 class="text-sm font-semibold uppercase tracking-widest text-accent">Licenser</h2>
<div class="mt-4 space-y-4">
{licenser.map((l) => (
<div class="rounded-2xl border border-border bg-surface/60 p-5">
<div class="wt-card">
<div class="flex flex-wrap items-baseline justify-between gap-2">
<h3 class="font-semibold">{l.hvad}</h3><span class="text-xs text-muted">{l.naar}</span>
</div>
@@ -178,7 +178,7 @@ const sprog = ['Dansk (modersmål)', 'Engelsk (flydende i tale og skrift)'];
<section class="mt-10">
<h2 class="text-sm font-semibold uppercase tracking-widest text-accent">Publikationer</h2>
<div class="mt-4 rounded-2xl border border-border bg-surface/60 p-5">
<div class="mt-4 wt-card">
<a href="https://arxiv.org/abs/2310.00999" target="_blank" rel="noopener" class="font-medium hover:text-accent">
CGAAL: Distributed On-The-Fly ATL Model Checker with Heuristics
</a>
@@ -193,7 +193,7 @@ const sprog = ['Dansk (modersmål)', 'Engelsk (flydende i tale og skrift)'];
<h2 class="text-sm font-semibold uppercase tracking-widest text-accent">Frivilligt arbejde</h2>
<div class="mt-4 space-y-4">
{frivilligt.map((f) => (
<div class="rounded-2xl border border-border bg-surface/60 p-5">
<div class="wt-card">
<div class="flex flex-wrap items-baseline justify-between gap-2">
<h3 class="font-semibold">{f.hvad}</h3><span class="text-xs text-muted">{f.naar}</span>
</div>
@@ -217,7 +217,7 @@ const sprog = ['Dansk (modersmål)', 'Engelsk (flydende i tale og skrift)'];
<div>
<h2 class="text-sm font-semibold uppercase tracking-widest text-accent">Sprog</h2>
<ul class="mt-4 space-y-2 text-sm">
{sprog.map((s) => <li class="rounded-xl border border-border bg-surface/60 px-3 py-2">{s}</li>)}
{sprog.map((s) => <li class="wt-card wt-card--compact">{s}</li>)}
</ul>
<h2 class="mt-8 text-sm font-semibold uppercase tracking-widest text-accent">Interesser</h2>
<p class="mt-4 text-sm text-muted">
@@ -236,7 +236,7 @@ const sprog = ['Dansk (modersmål)', 'Engelsk (flydende i tale og skrift)'];
<section class="mt-10">
<h2 class="text-sm font-semibold uppercase tracking-widest text-accent">Natur og jagt</h2>
<div class="mt-4 rounded-2xl border border-border bg-surface/60 p-5">
<div class="mt-4 wt-card">
<p class="text-muted">
Jeg har jagttegn, men jagten er mest anledningen. Det jeg tager ud efter, er at stå op
længe før resten af landet og træde ud i den stilhed, der kun findes tidligt om morgenen,
+5 -5
View File
@@ -98,7 +98,7 @@ const skills = [
<h2 class="text-sm font-semibold uppercase tracking-widest text-accent">Experience</h2>
<div class="mt-4 space-y-4">
{work.map((e) => (
<div class="rounded-2xl border border-border bg-surface/60 p-5">
<div class="wt-card">
<h3 class="font-semibold">{e.org} <span class="font-normal text-muted">· {e.place}</span></h3>
<div class="mt-3 space-y-3">
{e.roles.map((r) => (
@@ -122,7 +122,7 @@ const skills = [
<h2 class="text-sm font-semibold uppercase tracking-widest text-accent">Education</h2>
<div class="mt-4 space-y-4">
{education.map((u) => (
<div class="rounded-2xl border border-border bg-surface/60 p-5">
<div class="wt-card">
<div class="flex flex-wrap items-baseline justify-between gap-2">
<h3 class="font-semibold">{u.what}</h3><span class="text-xs text-muted">{u.when}</span>
</div>
@@ -135,7 +135,7 @@ const skills = [
<section class="mt-10">
<h2 class="text-sm font-semibold uppercase tracking-widest text-accent">Publication</h2>
<div class="mt-4 rounded-2xl border border-border bg-surface/60 p-5">
<div class="mt-4 wt-card">
<a href="https://arxiv.org/abs/2310.00999" target="_blank" rel="noopener" class="font-medium hover:text-accent">
CGAAL: Distributed On-The-Fly ATL Model Checker with Heuristics
</a>
@@ -164,8 +164,8 @@ const skills = [
<div>
<h2 class="text-sm font-semibold uppercase tracking-widest text-accent">Languages</h2>
<ul class="mt-4 space-y-2 text-sm">
<li class="rounded-xl border border-border bg-surface/60 px-3 py-2">Danish (native)</li>
<li class="rounded-xl border border-border bg-surface/60 px-3 py-2">English (fluent, spoken and written)</li>
<li class="wt-card wt-card--compact">Danish (native)</li>
<li class="wt-card wt-card--compact">English (fluent, spoken and written)</li>
</ul>
</div>
<div>
+7 -7
View File
@@ -43,9 +43,9 @@ const faqs = [
the air as a radio amateur (OZ0AGW).
</p>
<div class="mt-7 flex flex-wrap gap-3">
<a href="/en/cv" class="rounded-xl bg-accent px-5 py-2.5 font-medium text-bg transition-transform hover:-translate-y-0.5">See my CV</a>
<a href="/en/chat" class="rounded-xl border border-border px-5 py-2.5 font-medium transition-colors hover:border-accent">Ask my CV</a>
<a href="/en/projects" class="rounded-xl border border-border px-5 py-2.5 font-medium transition-colors hover:border-accent">Projects</a>
<a href="/en/cv" class="wt-btn wt-btn--primary wt-btn--lg">See my CV</a>
<a href="/en/chat" class="wt-btn wt-btn--lg">Ask my CV</a>
<a href="/en/projects" class="wt-btn wt-btn--lg">Projects</a>
</div>
</div>
<Image
@@ -60,7 +60,7 @@ const faqs = [
<section class="mt-14 grid gap-4 sm:grid-cols-3">
{highlights.map((h) => (
<div class="rounded-2xl border border-border bg-surface/60 p-5">
<div class="wt-card">
<h2 class="font-semibold tracking-tight">{h.title}</h2>
<p class="mt-2 text-sm text-muted">{h.body}</p>
</div>
@@ -76,20 +76,20 @@ const faqs = [
{ href: '/ubl', title: 'UBL check (Danish)', desc: 'Drop in an e-invoice and get its profile, syntax and EN 16931 status decoded. It all runs in the browser, the file never leaves your machine.' },
{ href: '/en/projects', title: 'Projects', desc: 'Tools I maintain and publish on the AUR, Homebrew and Scoop.' },
].map((c) => (
<a href={c.href} class="group rounded-2xl border border-border bg-surface/60 p-5 transition-colors hover:border-accent">
<a href={c.href} class="group wt-card wt-card--hover">
<h3 class="font-semibold tracking-tight group-hover:text-accent">{c.title} →</h3>
<p class="mt-2 text-sm text-muted">{c.desc}</p>
</a>
))}
</div>
<p class="mt-3 text-xs text-muted/80">Pro tip: press <kbd class="rounded border border-border bg-surface-2 px-1.5 py-0.5 font-mono text-[0.7rem]">Ctrl/⌘ K</kbd> to jump anywhere, or run <code class="rounded bg-surface-2 px-1.5 py-0.5">curl asger.weirsøe.dk</code> in a terminal.</p>
<p class="mt-3 text-xs text-muted/80">Pro tip: press <kbd class="wt-kbd">Ctrl/⌘ K</kbd> to jump anywhere, or run <code class="rounded bg-surface-2 px-1.5 py-0.5">curl asger.weirsøe.dk</code> in a terminal.</p>
</section>
<section class="mt-16">
<h2 class="text-2xl font-bold tracking-tight">Frequently asked questions</h2>
<div class="mt-5 grid gap-3">
{faqs.map((f) => (
<details class="group rounded-2xl border border-border bg-surface/60 p-5">
<details class="group wt-card">
<summary class="cursor-pointer list-none font-semibold tracking-tight marker:hidden">{f.q}</summary>
<p class="mt-3 text-sm text-muted">{f.a}</p>
</details>
+1 -1
View File
@@ -18,7 +18,7 @@ const ld = collectionPage({
<div class="mt-8 grid gap-4 sm:grid-cols-2">
{sider.map((p) => (
<a href={`/en/projects/${p.id.replace(/^en\//, '')}`}
class="group rounded-2xl border border-border bg-surface/60 p-5 transition-colors hover:border-accent/50">
class="group wt-card wt-card--hover">
<div class="flex items-center justify-between">
<h3 class="font-semibold tracking-tight group-hover:text-accent">{p.data.title}</h3>
{p.data.aur.length > 0 && <span class="text-xs text-muted">AUR</span>}
+7 -7
View File
@@ -56,9 +56,9 @@ const jsonLd = [profilePage, faqPage(faqs)];
Proxmox-homelab, roder med AI-projekter og går på luften som radioamatør (OZ0AGW).
</p>
<div class="mt-7 flex flex-wrap gap-3">
<a href="/cv" class="rounded-xl bg-accent px-5 py-2.5 font-medium text-bg transition-transform hover:-translate-y-0.5">Se mit CV</a>
<a href="/projekter" class="rounded-xl border border-border px-5 py-2.5 font-medium transition-colors hover:border-accent">Projekter</a>
<a href="/blog" class="rounded-xl border border-border px-5 py-2.5 font-medium transition-colors hover:border-accent">Blog</a>
<a href="/cv" class="wt-btn wt-btn--primary wt-btn--lg">Se mit CV</a>
<a href="/projekter" class="wt-btn wt-btn--lg">Projekter</a>
<a href="/blog" class="wt-btn wt-btn--lg">Blog</a>
</div>
</div>
<Image
@@ -73,7 +73,7 @@ const jsonLd = [profilePage, faqPage(faqs)];
<section class="mt-14 grid gap-4 sm:grid-cols-3">
{highlights.map((h) => (
<div class="rounded-2xl border border-border bg-surface/60 p-5">
<div class="wt-card">
<h2 class="font-semibold tracking-tight">{h.title}</h2>
<p class="mt-2 text-sm text-muted">{h.body}</p>
</div>
@@ -91,20 +91,20 @@ const jsonLd = [profilePage, faqPage(faqs)];
{ href: '/profiler', title: 'Profil-opslag', desc: 'Søgbart opslag over Nemhandel OIOUBL- og Peppol BIS/PINT-profiler: id\'er, dokumenter og typisk brug.' },
{ href: '/ubl', title: 'UBL-tjek', desc: 'Træk en faktura ind og få profil, syntaks og EN 16931-status afkodet. Alt sker i browseren, filen forlader aldrig din maskine.' },
].map((c) => (
<a href={c.href} class="group rounded-2xl border border-border bg-surface/60 p-5 transition-colors hover:border-accent">
<a href={c.href} class="group wt-card wt-card--hover">
<h3 class="font-semibold tracking-tight group-hover:text-accent">{c.title} →</h3>
<p class="mt-2 text-sm text-muted">{c.desc}</p>
</a>
))}
</div>
<p class="mt-3 text-xs text-muted/80">Pro-tip: tryk <kbd class="rounded border border-border bg-surface-2 px-1.5 py-0.5 font-mono text-[0.7rem]">Ctrl/⌘ K</kbd> og hop direkte til alt, eller <code class="rounded bg-surface-2 px-1.5 py-0.5">curl asger.weirsøe.dk</code> i en terminal.</p>
<p class="mt-3 text-xs text-muted/80">Pro-tip: tryk <kbd class="wt-kbd">Ctrl/⌘ K</kbd> og hop direkte til alt, eller <code class="rounded bg-surface-2 px-1.5 py-0.5">curl asger.weirsøe.dk</code> i en terminal.</p>
</section>
<section class="mt-16">
<h2 class="text-2xl font-bold tracking-tight">Ofte stillede spørgsmål</h2>
<div class="mt-5 grid gap-3">
{faqs.map((f) => (
<details class="group rounded-2xl border border-border bg-surface/60 p-5">
<details class="group wt-card">
<summary class="cursor-pointer list-none font-semibold tracking-tight marker:hidden">
{f.q}
</summary>
+11 -12
View File
@@ -12,15 +12,15 @@ import Base from '../layouts/Base.astro';
<form id="pep-form" class="mt-8 flex gap-3">
<input id="pep-cvr" type="text" inputmode="numeric" pattern="[0-9]{8}" maxlength="8" required
placeholder="CVR (8 cifre)" autocomplete="off"
class="w-44 rounded-xl border border-border bg-surface/60 px-4 py-2.5 outline-none focus:border-accent" />
<button type="submit" class="rounded-xl bg-accent px-5 py-2.5 font-medium text-bg transition-transform hover:-translate-y-0.5">Slå op</button>
class="w-44 wt-input wt-input--lg" />
<button type="submit" class="wt-btn wt-btn--primary wt-btn--lg">Slå op</button>
</form>
<div id="pep-result" class="mt-8" hidden>
<div class="rounded-2xl border border-border bg-surface/60 p-5">
<div class="wt-card">
<div class="flex flex-wrap items-baseline justify-between gap-2">
<h2 id="pep-name" class="text-xl font-semibold tracking-tight"></h2>
<span id="pep-badge" class="rounded-full px-3 py-1 text-sm font-medium"></span>
<span id="pep-badge" class="wt-tag"></span>
</div>
<p id="pep-cvr-line" class="mt-1 text-sm text-muted"></p>
<div id="pep-caps" class="mt-4"></div>
@@ -46,29 +46,29 @@ import Base from '../layouts/Base.astro';
};
async function loadPeppol(cvr) {
var box = document.getElementById('peppol-net'); box.hidden = false;
box.innerHTML = '<div class="rounded-2xl border border-border bg-surface/60 p-5"><p class="text-sm text-muted">Slår op i Peppol-netværket…</p></div>';
box.innerHTML = '<div class="wt-card"><p class="text-sm text-muted">Slår op i Peppol-netværket…</p></div>';
try {
var r = await fetch('/api/peppol/smp/' + encodeURIComponent(cvr));
var d = await r.json();
if (!r.ok) { box.innerHTML = '<div class="rounded-2xl border border-border bg-surface/60 p-5"><p class="text-sm text-muted">Peppol-opslag ikke tilgængeligt lige nu.</p></div>'; return; }
if (!r.ok) { box.innerHTML = '<div class="wt-card"><p class="text-sm text-muted">Peppol-opslag ikke tilgængeligt lige nu.</p></div>'; return; }
if (!d.on_peppol) {
box.innerHTML = '<div class="rounded-2xl border border-border bg-surface/60 p-5"><div class="flex items-baseline justify-between gap-2"><h2 class="text-xl font-semibold tracking-tight">Peppol-netværk</h2><span class="wt-tag wt-muted">Ikke fundet</span></div><p class="mt-2 text-sm text-muted">Virksomheden blev ikke fundet i Peppol Directory via CVR-opslag (kan stadig være på Peppol under et andet identifikator-scheme).</p></div>';
box.innerHTML = '<div class="wt-card"><div class="flex items-baseline justify-between gap-2"><h2 class="text-xl font-semibold tracking-tight">Peppol-netværk</h2><span class="wt-tag wt-muted">Ikke fundet</span></div><p class="mt-2 text-sm text-muted">Virksomheden blev ikke fundet i Peppol Directory via CVR-opslag (kan stadig være på Peppol under et andet identifikator-scheme).</p></div>';
return;
}
var docs = (d.doctypes || []).map(function (dt) {
var label = PROF[dt.customization] || dt.customization;
return '<span class="wt-tag">' + esc(dt.document) + ' · ' + esc(label) + '</span>';
}).join('');
box.innerHTML = '<div class="rounded-2xl border border-border bg-surface/60 p-5">' +
box.innerHTML = '<div class="wt-card">' +
'<div class="flex flex-wrap items-baseline justify-between gap-2"><h2 class="text-xl font-semibold tracking-tight">Peppol-netværk</h2>' +
'<span class="rounded-full bg-accent/15 px-3 py-1 text-sm font-medium text-accent">På Peppol</span></div>' +
'<span class="wt-tag wt-tag--accent">På Peppol</span></div>' +
'<p class="mt-1 text-sm text-muted">' + esc(d.name || '') + (d.country ? ' · ' + esc(d.country) : '') +
' · <span class="font-mono text-xs">' + esc(d.participant_id || '') + '</span>' + (d.reg_date ? ' · reg. ' + esc(d.reg_date) : '') + '</p>' +
'<p class="mt-4 text-xs font-semibold uppercase tracking-widest text-accent">Kan modtage</p>' +
'<div class="mt-2 flex flex-wrap gap-2">' + (docs || '<span class="text-sm text-muted">ingen dokumenttyper</span>') + '</div>' +
'<p class="mt-3 text-xs text-muted/80">Live fra Peppol Directory via egen proxy, intet gemt.</p></div>';
} catch (_) {
box.innerHTML = '<div class="rounded-2xl border border-border bg-surface/60 p-5"><p class="text-sm text-muted">Kunne ikke nå Peppol-opslaget.</p></div>';
box.innerHTML = '<div class="wt-card"><p class="text-sm text-muted">Kunne ikke nå Peppol-opslaget.</p></div>';
}
}
form.addEventListener('submit', async function (e) {
@@ -86,8 +86,7 @@ import Base from '../layouts/Base.astro';
document.getElementById('pep-cvr-line').textContent = 'CVR ' + data.cvr;
var badge = document.getElementById('pep-badge');
badge.textContent = data.registered ? 'Registreret i NemHandel' : 'Ikke registreret';
badge.className = 'rounded-full px-3 py-1 text-sm font-medium ' +
(data.registered ? 'bg-accent/15 text-accent' : 'border border-border text-muted');
badge.className = 'wt-tag ' + (data.registered ? 'wt-tag--accent' : 'wt-muted');
var caps = document.getElementById('pep-caps');
if (data.registered && (data.profiles.length || data.networks.length)) {
caps.innerHTML =
+2 -2
View File
@@ -48,7 +48,7 @@ const updated = today.toLocaleDateString('da-DK', { year: 'numeric', month: 'lon
<h2 class="text-sm font-semibold uppercase tracking-widest text-accent">Lige nu</h2>
<ul class="mt-4 space-y-3">
{focus.map((f) => (
<li class="rounded-2xl border border-border bg-surface/60 p-4 text-sm">{f}</li>
<li class="wt-card text-sm">{f}</li>
))}
</ul>
</section>
@@ -57,7 +57,7 @@ const updated = today.toLocaleDateString('da-DK', { year: 'numeric', month: 'lon
<section class="mt-10">
<h2 class="text-sm font-semibold uppercase tracking-widest text-accent">Git-aktivitet</h2>
<p class="mt-2 text-sm text-muted">{total} bidrag i mit eget Gitea det seneste år, de seneste {WEEKS} uger:</p>
<div class="mt-4 overflow-x-auto rounded-2xl border border-border bg-surface/60 p-4">
<div class="mt-4 overflow-x-auto wt-card">
<div class="heatmap" role="img" aria-label="Aktivitets-heatmap over git-bidrag">
{weeks.map((w) => (
<div class="hm-col">
+8 -24
View File
@@ -51,22 +51,22 @@ const kilde = (p: (typeof profiles)[number]) => {
placeholder="Søg profil, id, dokument…"
autocomplete="off"
aria-label="Søg profiler"
class="w-full rounded-xl border border-border bg-surface/60 py-2.5 pl-9 pr-4 outline-none focus:border-accent"
class="w-full wt-input wt-input--lg pl-9"
/>
</div>
<span id="p-count" class="text-sm text-muted">{profiles.length} profiler</span>
</div>
<div class="mt-3 flex flex-wrap gap-2">
<button type="button" class="p-chip" data-std="" aria-pressed="true">Alle</button>
{standarder.map((s) => <button type="button" class="p-chip" data-std={s} aria-pressed="false">{s}</button>)}
<button type="button" class="wt-btn wt-btn--ghost wt-btn--sm" data-std="" aria-pressed="true">Alle</button>
{standarder.map((s) => <button type="button" class="wt-btn wt-btn--ghost wt-btn--sm" data-std={s} aria-pressed="false">{s}</button>)}
</div>
<div class="mt-3">
<span class="mr-2 text-xs text-muted">Nemhandelsregistrets kategori:</span>
<span class="mt-1 inline-flex flex-wrap gap-2 align-middle">
<button type="button" class="p-chip" data-kat="" aria-pressed="true">Alle</button>
{kategorier.map((k) => <button type="button" class="p-chip" data-kat={k} aria-pressed="false">{k}</button>)}
<button type="button" class="wt-btn wt-btn--ghost wt-btn--sm" data-kat="" aria-pressed="true">Alle</button>
{kategorier.map((k) => <button type="button" class="wt-btn wt-btn--ghost wt-btn--sm" data-kat={k} aria-pressed="false">{k}</button>)}
</span>
</div>
@@ -78,7 +78,7 @@ const kilde = (p: (typeof profiles)[number]) => {
data-standard={p.standard}
data-kategori={p.kategori ?? ''}
data-search={searchString(p)}
class="rounded-2xl border border-border bg-surface/60 p-5 transition-colors hover:border-accent/50"
class="wt-card wt-card--hover"
>
<div class="flex flex-wrap items-center gap-2">
<span class="wt-tag wt-tag--accent">
@@ -136,22 +136,6 @@ const kilde = (p: (typeof profiles)[number]) => {
</p>
<style is:inline>
.p-chip {
border: 1px solid var(--color-border);
color: var(--color-muted);
border-radius: 0.6rem;
padding: 0.35rem 0.8rem;
font-size: 0.85rem;
cursor: pointer;
background: transparent;
transition: color 0.15s, background-color 0.15s, border-color 0.15s;
}
.p-chip:hover { color: var(--color-text); }
.p-chip[aria-pressed="true"] {
background: var(--color-surface-2);
color: var(--color-text);
border-color: color-mix(in srgb, var(--color-accent) 40%, var(--color-border));
}
.copy { transition: color 0.15s; }
.copy:hover { color: var(--color-accent); text-decoration: underline; text-underline-offset: 3px; }
</style>
@@ -160,8 +144,8 @@ const kilde = (p: (typeof profiles)[number]) => {
(function () {
var cards = [].slice.call(document.querySelectorAll('[data-profile]'));
var search = document.getElementById('p-search');
var stdChips = [].slice.call(document.querySelectorAll('.p-chip[data-std]'));
var katChips = [].slice.call(document.querySelectorAll('.p-chip[data-kat]'));
var stdChips = [].slice.call(document.querySelectorAll('button[data-std]'));
var katChips = [].slice.call(document.querySelectorAll('button[data-kat]'));
var count = document.getElementById('p-count');
var none = document.getElementById('p-none');
var q = '', std = '', kat = '';
+3 -3
View File
@@ -38,14 +38,14 @@ try {
<h1 class="text-4xl font-bold tracking-tight">Projekter</h1>
<p class="mt-3 text-muted">Hentet live fra min <a class="text-accent hover:underline" href={`${SITE.giteaBase}/${SITE.giteaUser}`}>Gitea</a>.</p>
{error && <p class="mt-8 rounded-xl border border-border bg-surface/60 p-5 text-muted">Kunne ikke hente repositories lige nu.</p>}
{error && <p class="mt-8 wt-card text-muted">Kunne ikke hente repositories lige nu.</p>}
<div class="mt-8 grid gap-4 sm:grid-cols-2">
{repos.map((r) => {
const side = sider.get(r.name.toLowerCase());
return side ? (
<a href={`/projekter/${side.id}`}
class="group rounded-2xl border border-border bg-surface/60 p-5 transition-colors hover:border-accent/50">
class="group wt-card wt-card--hover">
<div class="flex items-center justify-between">
<h3 class="font-semibold tracking-tight group-hover:text-accent">{side.data.title}</h3>
{side.data.aur.length > 0 && <span class="text-xs text-muted">AUR</span>}
@@ -55,7 +55,7 @@ try {
</a>
) : (
<a href={r.html_url} target="_blank" rel="noopener"
class="group rounded-2xl border border-border bg-surface/60 p-5 transition-colors hover:border-accent/50">
class="group wt-card wt-card--hover">
<div class="flex items-center justify-between">
<h3 class="font-semibold tracking-tight group-hover:text-accent">{r.name}</h3>
{r.stars_count > 0 && <span class="text-xs text-muted">★ {r.stars_count}</span>}
+1 -1
View File
@@ -10,7 +10,7 @@ const STATUS_URL = 'https://status.weircon.dk/status/weircon';
Når man drifter ~50 containere selv, er det rart at vide hvornår man har smadret noget.
</p>
<div class="mt-8 overflow-hidden rounded-2xl border border-border bg-surface/60">
<div class="mt-8 wt-card wt-card--flush">
<iframe src={STATUS_URL} title="Uptime Kuma statusside" loading="lazy"
class="h-[70vh] w-full" style="border:0"></iframe>
</div>
+7 -7
View File
@@ -28,7 +28,7 @@ const idx = profiles.map((p) => ({
også på Windows og macOS.
</p>
<div id="drop" class="mt-8 rounded-2xl border border-dashed border-border bg-surface/40 p-8 text-center transition-colors">
<div id="drop" class="mt-8 wt-dropzone">
<p class="text-muted">Træk en <code class="rounded bg-surface-2 px-1.5 py-0.5">.xml</code>-fil hertil, eller
<label class="cursor-pointer text-accent hover:underline">vælg en fil<input id="file" type="file" accept=".xml,text/xml,application/xml" class="hidden" /></label>.
</p>
@@ -37,8 +37,8 @@ const idx = profiles.map((p) => ({
<details class="mt-3">
<summary class="cursor-pointer text-sm text-muted">…eller indsæt XML / en identifikator manuelt</summary>
<textarea id="paste" rows="6" placeholder="Indsæt UBL-XML, eller bare en CustomizationID/ProfileID…"
class="mt-2 w-full rounded-xl border border-border bg-surface/60 px-4 py-3 font-mono text-xs outline-none focus:border-accent"></textarea>
<p class="mt-2"><button id="analyze" type="button" class="rounded-xl bg-accent px-4 py-2 text-sm font-medium text-bg">Afkod</button></p>
class="mt-2 w-full wt-input wt-input--lg font-mono text-xs"></textarea>
<p class="mt-2"><button id="analyze" type="button" class="wt-btn wt-btn--primary">Afkod</button></p>
</details>
<div id="result" class="mt-6" hidden></div>
@@ -140,7 +140,7 @@ const idx = profiles.map((p) => ({
(dec.standard ? ' <span class="text-muted text-base font-normal">· ' + esc(dec.standard) + '</span>' : '') + '</h2>';
result.innerHTML =
'<div class="rounded-2xl border border-border bg-surface/60 p-5">' + head +
'<div class="wt-card">' + head +
'<dl class="mt-3 space-y-1 text-xs">' +
row('Dokumenttype', '<span class="font-semibold">' + esc(docType) + '</span> (UBL ' + (esc(ublv) || '2.x') + ')') +
row('ID', esc(id)) +
@@ -157,7 +157,7 @@ const idx = profiles.map((p) => ({
function renderId(s) {
var dec = decode(s, s);
result.innerHTML = '<div class="rounded-2xl border border-border bg-surface/60 p-5">' +
result.innerHTML = '<div class="wt-card">' +
(dec.match ? '<div class="flex flex-wrap items-center gap-2">' + badge(dec.match.standard, 'ok') +
'</div><h2 class="mt-2 text-xl font-semibold tracking-tight">' + esc(dec.match.navn) + '</h2>'
: '<h2 class="text-xl font-semibold tracking-tight">Identifikator</h2>') +
@@ -187,9 +187,9 @@ const idx = profiles.map((p) => ({
function readFile(f) { var r = new FileReader(); r.onload = function () { analyze(String(r.result)); }; r.readAsText(f); }
fileInput.addEventListener('change', function () { if (fileInput.files[0]) readFile(fileInput.files[0]); });
['dragenter', 'dragover'].forEach(function (ev) { drop.addEventListener(ev, function (e) {
e.preventDefault(); drop.classList.add('border-accent'); }); });
e.preventDefault(); drop.classList.add('is-over'); }); });
['dragleave', 'drop'].forEach(function (ev) { drop.addEventListener(ev, function (e) {
e.preventDefault(); drop.classList.remove('border-accent'); }); });
e.preventDefault(); drop.classList.remove('is-over'); }); });
drop.addEventListener('drop', function (e) { var f = e.dataTransfer && e.dataTransfer.files[0]; if (f) readFile(f); });
document.getElementById('analyze').addEventListener('click', function () { analyze(paste.value); });
paste.addEventListener('keydown', function (e) { if ((e.metaKey || e.ctrlKey) && e.key === 'Enter') analyze(paste.value); });
+1 -1
View File
@@ -43,7 +43,7 @@ const sections = [
<h2 class="text-sm font-semibold uppercase tracking-widest text-accent">{s.title}</h2>
<div class="mt-4 space-y-3">
{s.items.map((it) => (
<div class="rounded-2xl border border-border bg-surface/60 p-4">
<div class="wt-card">
<span class="font-semibold">{it.name}</span>
<p class="mt-1 text-sm text-muted">{it.note}</p>
</div>
+4 -3
View File
@@ -5,10 +5,11 @@
adapters/tailwind.css feeds the colour tokens into Tailwind's @theme so the
bg-surface / text-muted / border-border / text-accent utilities are generated
from the shared palette; weirsoe-theme.css adds the base glow, the sun⇄moon
toggle, .prose and the .wt-* components. Replaces the tokens/toggle/prose/glow
toggle, .prose and the .wt-* components, in Tailwind's components layer so a
utility on the same element (pl-9, font-mono) still wins. Replaces the tokens/toggle/prose/glow
that used to live here by hand. */
@import "../../vendor/weirsoe-theme/adapters/tailwind.css";
@import "../../vendor/weirsoe-theme/weirsoe-theme.css";
@import "../../vendor/weirsoe-theme/weirsoe-theme.css" layer(components);
/* asger keeps Inter as its UI font (the theme defaults to a system stack). */
@theme {
@@ -19,4 +20,4 @@ html[data-theme="terminal"] {
}
/* make the theme's body font follow asger's --font-sans (Inter / mono) */
:root { --wt-font-sans: var(--font-sans); }
a { color: inherit; }
@layer base { a { color: inherit; } }