This commit is contained in:
@@ -12,9 +12,7 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
# Fuld historik: astro.config.mjs udleder sitemap-lastmod fra hver sides
|
||||
# sidste commit, og IndexNow-steppet differ pushet. Begge er værdiløse
|
||||
# med den default shallow clone (fetch-depth: 1).
|
||||
# Fuld historik til sitemap-lastmod og IndexNow
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup Node
|
||||
@@ -22,10 +20,7 @@ jobs:
|
||||
with:
|
||||
node-version: 22
|
||||
|
||||
# The runner forces omit of optional deps, dropping @img/sharp-linux-x64
|
||||
# (sharp's platform binary) -> astro:assets MissingSharp. Clear the omit
|
||||
# env and force-install the linux/glibc/x64 sharp binary per sharp's
|
||||
# official cross-platform install, then verify it loads.
|
||||
# Runneren udelader optional deps, så sharp-binæren installeres eksplicit
|
||||
- name: Install dependencies
|
||||
env:
|
||||
npm_config_omit: ""
|
||||
@@ -35,7 +30,6 @@ jobs:
|
||||
npm install --include=optional --os=linux --libc=glibc --cpu=x64 sharp
|
||||
node -e "console.log('sharp', require('sharp').versions.sharp)"
|
||||
|
||||
# Intet indlæg bygges uden godkendelse; se scripts/verify-approvals.mjs.
|
||||
- name: Verify blog approvals
|
||||
env:
|
||||
PUBLISH_KEY: ${{ secrets.PUBLISH_KEY }}
|
||||
@@ -44,11 +38,7 @@ jobs:
|
||||
- name: Build site
|
||||
run: npm run build
|
||||
|
||||
# tar over SSH (the runner image has no rsync). Build is unpacked into
|
||||
# a .new dir on the web host and atomically swapped in, so the live web
|
||||
# root is never left half-written. Host and path are repository secrets,
|
||||
# not variables: run logs of a public repo are public, and Gitea prints
|
||||
# variables in them but masks secrets.
|
||||
# tar over SSH med atomisk swap (runner-imaget har ikke rsync)
|
||||
- name: Deploy dist/ over SSH
|
||||
env:
|
||||
DEPLOY_HOST: ${{ secrets.DEPLOY_HOST }}
|
||||
@@ -72,9 +62,6 @@ jobs:
|
||||
&& mv $DEPLOY_PATH.new $DEPLOY_PATH \
|
||||
&& rm -rf $DEPLOY_PATH.old"
|
||||
|
||||
# Fortæl Bing/Yandex m.fl. med det samme hvilke sider der ændrede sig.
|
||||
# Kører EFTER deploy, så nøglefilen og siderne er live når de kommer og tjekker.
|
||||
# Google understøtter ikke IndexNow. Må aldrig vælte et ellers vellykket deploy.
|
||||
- name: Ping IndexNow
|
||||
continue-on-error: true
|
||||
env:
|
||||
|
||||
+5
-28
@@ -9,13 +9,8 @@ import { fileURLToPath } from 'node:url';
|
||||
|
||||
const ROOT = path.dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
// ── lastmod til sitemap ────────────────────────────────────────────────────────
|
||||
// Google bruger kun lastmod hvis den er troværdig, så vi gætter aldrig: statiske
|
||||
// sider får datoen for deres SIDSTE COMMIT, blogindlæg får updated/date fra
|
||||
// frontmatter, og alt vi ikke kan datere præcist får slet ingen lastmod.
|
||||
// NB: kræver fuld git-historik — deploy-workflowen bruger fetch-depth: 0.
|
||||
// Er historikken shallow (eller mangler .git), falder vi stille tilbage til
|
||||
// "ingen lastmod" i stedet for at udgive en forkert dato.
|
||||
// lastmod til sitemap: sidste commit for statiske sider, frontmatter for blogindlæg.
|
||||
// Kræver fuld git-historik (fetch-depth: 0); ellers ingen lastmod.
|
||||
|
||||
const gitLastModified = (file) => {
|
||||
try {
|
||||
@@ -30,8 +25,6 @@ const gitLastModified = (file) => {
|
||||
}
|
||||
};
|
||||
|
||||
// Shallow clone => hver fil ser ud til at være ændret i den ene commit vi har.
|
||||
// Så er datoerne værdiløse, og vi dropper lastmod helt.
|
||||
const gitHistoryIsUsable = (() => {
|
||||
try {
|
||||
const n = execFileSync('git', ['rev-list', '--count', 'HEAD'], {
|
||||
@@ -145,14 +138,8 @@ export default defineConfig({
|
||||
sitemap({
|
||||
changefreq: 'weekly',
|
||||
priority: 0.7,
|
||||
// /tarballz/ er ikke en Astro-side (genereres på webhosten), men oversigten må
|
||||
// gerne indekseres; selve pakkefilerne er noindex der.
|
||||
customPages: ['https://asger.xn--weirse-fya.dk/tarballz/'],
|
||||
// Tag-siderne er noindex (tynde lister), så de hører heller ikke i sitemap'et.
|
||||
// /peppol er kun en omdirigering til /nemhandel og skal ikke indekseres.
|
||||
// /cookies, /status og /chat er hjælpesider uden søgeværdi; de er noindex
|
||||
// og står derfor heller ikke i sitemap'et (ellers melder Search Console
|
||||
// "indsendt URL markeret som noindex").
|
||||
// noindex-sider og omdirigeringer
|
||||
filter: (page) => {
|
||||
const p = new URL(page).pathname.replace(/\/$/, '');
|
||||
return !p.startsWith('/tags') && !p.startsWith('/peppol')
|
||||
@@ -163,18 +150,11 @@ export default defineConfig({
|
||||
if (pathname === '/') item.priority = 1.0;
|
||||
const lastmod = lastmodByRoute.get(pathname);
|
||||
if (lastmod) item.lastmod = lastmod;
|
||||
// Samme kanoniske form som <link rel="canonical"> og de interne links:
|
||||
// uden afsluttende skråstreg. Slås lastmod op FØR strippet, fordi
|
||||
// lastmodByRoute er nøglet på Astros egne ruter (som har skråstregen).
|
||||
// Undtagen /tarballz/: den er ikke en Astro-side, og webhosten serverer
|
||||
// den kun med skråstreg (uden skråstreg giver den en 301).
|
||||
// Kanonisk form uden afsluttende skråstreg (undtagen /tarballz/)
|
||||
if (pathname !== '/' && pathname !== '/tarballz/') item.url = item.url.replace(/\/$/, '');
|
||||
return item;
|
||||
},
|
||||
}),
|
||||
// Service worker → offline support + installable PWA. autoUpdate + outdated-cache
|
||||
// cleanup so a redeploy never strands a visitor on a stale version. Reuses the
|
||||
// existing public/site.webmanifest (manifest: false = don't generate another).
|
||||
AstroPWA({
|
||||
registerType: 'autoUpdate',
|
||||
manifest: false,
|
||||
@@ -184,10 +164,7 @@ export default defineConfig({
|
||||
cleanupOutdatedCaches: true,
|
||||
clientsClaim: true,
|
||||
skipWaiting: true,
|
||||
// No SPA fallback. The default answers EVERY navigation it has not
|
||||
// precached with the cached front page, so /tarballz/ (served by nginx
|
||||
// outside the Astro build) and unknown URLs showed the home page instead
|
||||
// of the real page or the 404. Every Astro page is precached anyway.
|
||||
// Ingen fallback til forsiden, så /tarballz/ og 404 virker
|
||||
navigateFallback: null,
|
||||
},
|
||||
devOptions: { enabled: false },
|
||||
|
||||
+2
-11
@@ -1,11 +1,5 @@
|
||||
// Pinger IndexNow (Bing/Yandex/Seznam m.fl.) med de sider dette push faktisk ændrede.
|
||||
//
|
||||
// Vi sender bevidst IKKE hele sitemappet hver gang: IndexNow er til "det her ændrede sig
|
||||
// nu", og gentagne fuld-lister ser ud som støj. Kan vi ikke regne diffen ud, springer vi
|
||||
// over frem for at gætte.
|
||||
//
|
||||
// Kør efter `astro build` — dist/sitemap-0.xml bruges som facitliste over gyldige URL'er.
|
||||
// Google understøtter ikke IndexNow; det her hjælper kun de øvrige motorer.
|
||||
// Pinger IndexNow med de sider der er ændret i dette push.
|
||||
// Kør efter `astro build` (bruger dist/sitemap-0.xml).
|
||||
|
||||
import { execFileSync } from 'node:child_process';
|
||||
import fs from 'node:fs';
|
||||
@@ -62,8 +56,6 @@ const urls = [...new Set(files.map(routeForFile).filter(Boolean).map((r) => ORIG
|
||||
valid.has(u),
|
||||
);
|
||||
|
||||
// Layout/CSS/config-ændringer rammer hver side, men det er ikke en indholdsændring
|
||||
// værd at pinge om. Kun ruter med eget ændret kildefil ryger afsted.
|
||||
if (!urls.length) {
|
||||
console.log('IndexNow: ingen sider med ændret indhold i dette push — springer over.');
|
||||
process.exit(0);
|
||||
@@ -91,7 +83,6 @@ const res = await fetch('https://api.indexnow.org/indexnow', {
|
||||
body: JSON.stringify(body),
|
||||
});
|
||||
|
||||
// 200 = modtaget, 202 = modtaget men nøglen valideres stadig. Begge er fine.
|
||||
console.log(`IndexNow: HTTP ${res.status} ${res.statusText}`);
|
||||
if (![200, 202].includes(res.status)) {
|
||||
console.log(await res.text().catch(() => ''));
|
||||
|
||||
@@ -1,14 +1,5 @@
|
||||
// Spærre før build: intet blogindlæg går live uden at være godkendt.
|
||||
//
|
||||
// Hvert indlæg i src/content/blog skal bære et `godkendt:`-stempel i frontmatter.
|
||||
// Stemplet er en HMAC-SHA256 over filens sti + indhold (uden selve stempel-linjen)
|
||||
// + sha256 af hvert billede i indlæggets mappe (src/content/blog/<slug>/),
|
||||
// lavet med en nøgle som kun godkendelsestjenesten og denne workflow kender. Et
|
||||
// indlæg der er lagt direkte i repoet, eller ændret efter godkendelse, har derfor
|
||||
// intet eller et forkert stempel, og så stopper buildet før noget bliver deployet.
|
||||
//
|
||||
// Udkast hører ikke hjemme her: repoet er offentligt, så `draft: true` ville stadig
|
||||
// udgive teksten via git. Udkast ligger i et privat repo indtil de er godkendt.
|
||||
// Stopper buildet hvis et blogindlæg mangler et gyldigt `godkendt:`-stempel
|
||||
// (HMAC-SHA256 over sti, indhold og billeder).
|
||||
//
|
||||
// PUBLISH_KEY=<hex> node scripts/verify-approvals.mjs
|
||||
import crypto from 'node:crypto';
|
||||
@@ -21,8 +12,7 @@ if (!key) {
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Skal matche godkendelsestjenesten byte for byte. Et indlæg uden billeder har
|
||||
// ingen @asset-linjer, så dets stempel er det samme som før billeder fandtes.
|
||||
// Skal matche godkendelsestjenesten byte for byte.
|
||||
const canonical = (relPath, text, assets = []) => {
|
||||
const lines = text.replace(/\r\n/g, '\n').split('\n');
|
||||
if (lines[0] === '---') {
|
||||
|
||||
@@ -32,7 +32,6 @@ const t = lang === 'en' ? {
|
||||
function esc(s) {
|
||||
return s.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
||||
}
|
||||
// Escape FØRST, linkificér så: rene URL'er og e-mails bliver klikbare, intet HTML slipper igennem.
|
||||
function linkify(text) {
|
||||
var html = esc(text);
|
||||
html = html.replace(/\b(https?:\/\/[^\s<]+[^\s<.,!?:;)])/g,
|
||||
@@ -41,8 +40,6 @@ const t = lang === 'en' ? {
|
||||
'<a href="mailto:$1" class="text-accent underline">$1</a>');
|
||||
return html;
|
||||
}
|
||||
// Track spørgsmålet i Matomo FRA browseren, så det knyttes til den besøgendes
|
||||
// egen visitor-profil (cookie/visitor-ID, IP, browser) i stedet for en server-IP.
|
||||
function track(action, q) {
|
||||
try { (window._paq = window._paq || []).push(['trackEvent', 'Chat', action, q]); } catch (_) {}
|
||||
}
|
||||
|
||||
@@ -11,8 +11,7 @@ interface Props {
|
||||
modifiedTime?: string;
|
||||
tags?: string[];
|
||||
jsonLd?: unknown[];
|
||||
// Tynde oversigtssider (fx tags med 1-2 indlæg) skal ikke i Googles indeks,
|
||||
// men links fra dem skal stadig følges.
|
||||
// noindex, follow
|
||||
noindex?: boolean;
|
||||
// Sidens sprog (standard: dansk) og dens oversættelser, som hreflang-links.
|
||||
lang?: 'da' | 'en';
|
||||
@@ -28,9 +27,7 @@ const xDefault = hreflang.find((a) => a.lang === 'da');
|
||||
|
||||
const pageTitle = title ? `${title} · ${SITE.name}` : `${SITE.title} · Datalog & Technical Solution Architect`;
|
||||
const desc = description ?? SITE.description;
|
||||
// Kanonisk form er UDEN afsluttende skråstreg. Origin serverer begge former med
|
||||
// 200, og de interne links peger alle på no-slash, så canonical og sitemap skal
|
||||
// gøre det samme, ellers opdager Google to adskilte sæt af de samme sider.
|
||||
// Kanonisk form er uden afsluttende skråstreg, som i sitemap og interne links.
|
||||
const canonicalPath = Astro.url.pathname.replace(/\/$/, '') || '/';
|
||||
const canonical = new URL(canonicalPath, SITE.url).href;
|
||||
const ogImage = new URL(image ?? '/og.png', SITE.url).href;
|
||||
@@ -76,20 +73,15 @@ const graphs = [baseGraph, ...jsonLd];
|
||||
<link rel="manifest" href="/site.webmanifest" />
|
||||
<link rel="sitemap" href="/sitemap-index.xml" />
|
||||
<link rel="alternate" type="application/rss+xml" title={SITE.name} href="/rss.xml" />
|
||||
<!-- Mastodon-verifikation: usynligt rel=me i head (kræver ingen JS) -->
|
||||
<link rel="me" href="https://mastodon.social/@asgerweirsoee" />
|
||||
|
||||
{graphs.map((g) => <script type="application/ld+json" set:html={JSON.stringify(g)} />)}
|
||||
|
||||
<!-- Matomo (site 2) via first-party proxy /_a/: neutrale navne, så content-blockers ikke matcher "matomo".
|
||||
Lagt direkte i kilden fordi HTML-minify dropper det valgfrie </head> som NPMplus' sub_filter ellers injicerede på. -->
|
||||
<script is:inline>
|
||||
var _paq = (window._paq = window._paq || []);
|
||||
_paq.push(['setDomains', ['*.weirsoe.dk', '*.xn--weirse-fya.dk']]);
|
||||
_paq.push(['setVisitorCookieTimeout', 31536000]); // visitor-cookie lever 1 år (sekunder)
|
||||
_paq.push(['setVisitorCookieTimeout', 31536000]);
|
||||
_paq.push(['enableLinkTracking']);
|
||||
// Enhedsmodel (Custom Dimension 2): grov enheds-kohorte ud fra skaerm (logiske pt) + DPR.
|
||||
// Apple maskerer GPU-strengen, saa 6,1-tommer-generationen kan ikke splittes finere end gruppe.
|
||||
(function () {
|
||||
try {
|
||||
var w = screen.width, h = screen.height, dpr = Math.round(window.devicePixelRatio || 1);
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
---
|
||||
import { NAV, NAV_EN, SITE } from '../consts';
|
||||
const { lang, alternates = [] } = Astro.props as { lang?: 'da' | 'en'; alternates?: { lang: 'da' | 'en'; path: string }[] };
|
||||
// Sprogskiftet bor KUN her: findes siden på det andet sprog (sidens alternates), linkes der til den.
|
||||
const andet = alternates.find((a) => a.lang !== (lang ?? 'da'));
|
||||
const nav = lang === 'en' ? NAV_EN : NAV;
|
||||
const home = lang === 'en' ? '/en' : '/';
|
||||
|
||||
@@ -25,8 +25,7 @@ const { title, description, image, type, publishedTime, modifiedTime, tags, json
|
||||
<!doctype html>
|
||||
<html lang={lang ?? SITE.lang}>
|
||||
<head>
|
||||
{/* Hilsenen skal stå INDE i <head>. Stod den før <html>, smed Astros compiler
|
||||
både <html> og <head> væk, og sitet mistede lang-attributten (juni-okt. 2026). */}
|
||||
{/* Skal stå i <head>, ellers dropper Astro <html> og <head>. */}
|
||||
<Fragment set:html={`<!--
|
||||
_ ____ ____ _____ ____
|
||||
/ \\ / ___| / ___| ____| _ \\
|
||||
@@ -58,8 +57,6 @@ const { title, description, image, type, publishedTime, modifiedTime, tags, json
|
||||
</script>
|
||||
<Head {title} {description} {image} {type} {publishedTime} {modifiedTime} {tags} {jsonLd} noindex={noindex || PREVIEW} {lang} {alternates} />
|
||||
<ClientRouter />
|
||||
<!-- Registrér service worker'en (PWA/offline). Genereret af @vite-pwa i build;
|
||||
is:inline + public-sti så Astro ikke bundler den. 404 i dev = harmløst. -->
|
||||
{!PREVIEW && <script is:inline src="/registerSW.js"></script>}
|
||||
</head>
|
||||
<body class="min-h-screen flex flex-col antialiased">
|
||||
@@ -76,7 +73,7 @@ const { title, description, image, type, publishedTime, modifiedTime, tags, json
|
||||
<Palette />
|
||||
<CookieBanner {lang} />
|
||||
<script is:inline>
|
||||
// Konami-koden: ↑↑↓↓←→←→BA → terminal-tema + en lille rulle. En klassiker skal æres.
|
||||
// Konami-kode → terminal-tema
|
||||
(() => {
|
||||
if (window.__konami) return; window.__konami = true;
|
||||
const seq = ['ArrowUp','ArrowUp','ArrowDown','ArrowDown','ArrowLeft','ArrowRight','ArrowLeft','ArrowRight','b','a'];
|
||||
|
||||
@@ -16,8 +16,6 @@ const highlights = [
|
||||
{ title: 'Selvhosting, AI & fuld-stack', body: 'Et Proxmox-homelab med ~50 LXC/VM’er bag SSO (Authentik), og projekter fra Java/Go/Python til TypeScript og LLM-drevne workflows.' },
|
||||
];
|
||||
|
||||
// Spørgsmålene står i tredje person med navnet (det er det folk og LLM'er søger på,
|
||||
// og det fodrer FAQ-schema); svarene er i første person, så det er mig der skriver.
|
||||
const faqs = [
|
||||
{
|
||||
q: 'Hvem er Asger Weirsøe?',
|
||||
|
||||
+2
-4
@@ -2,15 +2,13 @@
|
||||
import Base from '../layouts/Base.astro';
|
||||
import { SITE } from '../consts';
|
||||
|
||||
// Hvad jeg roder med lige nu, opdateres manuelt, det er hele pointen med en /now-side.
|
||||
const focus = [
|
||||
'Elektronisk fakturering hos Digisense: Peppol, NemHandel og løsningsarkitektur',
|
||||
'HD 1. del i Erhvervsøkonomi på AAU (eksamener til sommer)',
|
||||
'Homelab: crawlkit, denne side, og alt for mange LXC-containere',
|
||||
];
|
||||
|
||||
// Build-time: aktivitets-heatmap fra min Gitea (offentligt API). Fejler stille,
|
||||
// siden bygger fint uden graf hvis Gitea er nede under build.
|
||||
// Aktivitets-heatmap fra Gitea ved build; ingen graf hvis Gitea er nede.
|
||||
type Beat = { timestamp: number; contributions: number };
|
||||
let perDay = new Map<string, number>();
|
||||
try {
|
||||
@@ -19,7 +17,7 @@ try {
|
||||
const d = new Date(b.timestamp * 1000).toISOString().slice(0, 10);
|
||||
perDay.set(d, (perDay.get(d) || 0) + b.contributions);
|
||||
}
|
||||
} catch { /* ingen graf, ingen drama */ }
|
||||
} catch {}
|
||||
|
||||
// 26 uger bagud, uger som kolonner (man-søn), niveau 0-4
|
||||
const WEEKS = 26;
|
||||
|
||||
-13
@@ -3,7 +3,6 @@ import { SITE } from './consts';
|
||||
export const personId = `${SITE.url}/#person`;
|
||||
export const websiteId = `${SITE.url}/#website`;
|
||||
|
||||
// Canonical Person entity — referenced by @id from every other entity.
|
||||
export const person = {
|
||||
'@type': 'Person',
|
||||
'@id': personId,
|
||||
@@ -38,8 +37,6 @@ export const person = {
|
||||
dateCreated: '2020',
|
||||
},
|
||||
],
|
||||
// schema.org/callSign: kaldesignalet er en entydig identifikator på linje med
|
||||
// sameAs-profilerne og knytter OZ0AGW til personen.
|
||||
callSign: 'OZ0AGW',
|
||||
worksFor: { '@type': 'Organization', name: 'Digisense A/S' },
|
||||
alumniOf: { '@type': 'CollegeOrUniversity', name: 'Aalborg Universitet' },
|
||||
@@ -79,11 +76,8 @@ export const website = {
|
||||
publisher: { '@id': personId },
|
||||
};
|
||||
|
||||
// Default graph present on every page (Google de-dupes by @id).
|
||||
export const baseGraph = { '@context': 'https://schema.org', '@graph': [website, person] };
|
||||
|
||||
// Marks the front page as a ProfilePage about the Person — a strong "this site is
|
||||
// about this human" signal that helps Google build a knowledge-panel entity.
|
||||
export const profilePage = {
|
||||
'@context': 'https://schema.org',
|
||||
'@type': 'ProfilePage',
|
||||
@@ -95,7 +89,6 @@ export const profilePage = {
|
||||
inLanguage: 'da-DK',
|
||||
};
|
||||
|
||||
// FAQ pairs — natural-language Q&A that search engines and LLMs can lift verbatim.
|
||||
export function faqPage(items: { q: string; a: string }[]) {
|
||||
return {
|
||||
'@context': 'https://schema.org',
|
||||
@@ -134,8 +127,6 @@ export function blogPosting(opts: {
|
||||
};
|
||||
}
|
||||
|
||||
// BreadcrumbList — the highest-ROI rich result for content sites: a human-readable
|
||||
// path (Forside › Blog › …) in the SERP instead of the raw URL. Needs ≥2 items.
|
||||
export function breadcrumb(items: { name: string; url: string }[]) {
|
||||
return {
|
||||
'@context': 'https://schema.org',
|
||||
@@ -146,8 +137,6 @@ export function breadcrumb(items: { name: string; url: string }[]) {
|
||||
};
|
||||
}
|
||||
|
||||
// CollectionPage — tells search/AI that a listing page (the blog index, a tag) is a
|
||||
// curated set authored by the Person, not a thin/duplicate page.
|
||||
export function collectionPage(opts: { url: string; name: string; description: string }) {
|
||||
return {
|
||||
'@context': 'https://schema.org',
|
||||
@@ -162,8 +151,6 @@ export function collectionPage(opts: { url: string; name: string; description: s
|
||||
};
|
||||
}
|
||||
|
||||
// SoftwareSourceCode — a project page for a tool the Person wrote and maintains.
|
||||
// codeRepository points at Gitea; sameAs lists the package pages (AUR) for the same tool.
|
||||
export function softwareSourceCode(opts: {
|
||||
url: string; name: string; description: string; codeRepository: string;
|
||||
language: string; license: string; platforms: string[]; sameAs: string[];
|
||||
|
||||
@@ -1,23 +1,15 @@
|
||||
@import "tailwindcss";
|
||||
@import "@fontsource-variable/inter";
|
||||
|
||||
/* Shared design system (SSOT: agw/Weircon-wind, vendored as a submodule).
|
||||
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, 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. */
|
||||
/* Fælles tema (Weircon-wind) */
|
||||
@import "../../vendor/weirsoe-theme/adapters/tailwind.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 {
|
||||
--font-sans: "Inter Variable", ui-sans-serif, system-ui, -apple-system, sans-serif;
|
||||
}
|
||||
html[data-theme="terminal"] {
|
||||
--font-sans: ui-monospace, "Cascadia Code", "JetBrains Mono", Menlo, Consolas, monospace;
|
||||
}
|
||||
/* make the theme's body font follow asger's --font-sans (Inter / mono) */
|
||||
:root { --wt-font-sans: var(--font-sans); }
|
||||
@layer base { a { color: inherit; } }
|
||||
|
||||
Reference in New Issue
Block a user