Imporved the site
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
export class AssetsConstants {
|
||||
|
||||
static readonly ME = '/assets/me.webp';
|
||||
static readonly LOGO = '/assets/favicon.ico';
|
||||
static readonly LOGO = '/assets/logos/logo-wolf.png';
|
||||
static readonly FLAG_DE = '/assets/flags/de.svg';
|
||||
static readonly FLAG_EN = '/assets/flags/gb.svg';
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<mat-toolbar class="flex! items-center! p-[clamp(0.5rem,1vw,1rem)]! backdrop-blur-sm! backdrop-saturate-[1.1]! bg-white/80! dark:bg-[#313131]/80! border-b! border-black/8!" color="primary" (keydown)="onKeydown($event)">
|
||||
<a class="flex items-center gap-[clamp(0.4rem,1vw,0.6rem)] text-inherit no-underline" routerLink="/">
|
||||
<img class="w-[clamp(36px,10vw,48px)] h-[clamp(36px,10vw,48px)] rounded-full" src="{{AssetsConstants.LOGO}}" alt="" aria-hidden="true" draggable="false"
|
||||
<img class="w-[clamp(36px,10vw,48px)] h-auto rounded-full" src="{{AssetsConstants.LOGO}}" width="117" height="128" alt="" aria-hidden="true" draggable="false"
|
||||
oncontextmenu="return false;">
|
||||
<span class="font-semibold tracking-[0.2px] text-[clamp(1rem,3vw,1.2rem)]">{{ 'APP.TITLE' | translate }}</span>
|
||||
</a>
|
||||
|
||||
@@ -40,4 +40,15 @@ describe('LanguageService', () => {
|
||||
expect(translateSpy.use).toHaveBeenCalledWith('de');
|
||||
expect(localStorage.getItem(LocalStoreConstants.LANGUAGE_KEY)).toBe('de');
|
||||
});
|
||||
|
||||
it('marks the document with the active language so screen readers switch voice', () => {
|
||||
localStorage.setItem(LocalStoreConstants.LANGUAGE_KEY, 'en');
|
||||
const service = createService();
|
||||
|
||||
expect(document.documentElement.lang).toBe('en');
|
||||
|
||||
service.use('de');
|
||||
|
||||
expect(document.documentElement.lang).toBe('de');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Injectable, inject, signal } from '@angular/core';
|
||||
import { DOCUMENT } from '@angular/common';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import {LocalStoreConstants} from '../constants/LocalStoreConstants';
|
||||
|
||||
@@ -7,20 +8,32 @@ type Lang = 'de' | 'en';
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class LanguageService {
|
||||
private readonly translate = inject(TranslateService);
|
||||
private readonly document = inject(DOCUMENT);
|
||||
readonly lang = signal<Lang>(this.getInitial());
|
||||
|
||||
constructor() {
|
||||
// translate service lang and fallback are already configured via provideTranslateService in app.config
|
||||
// just ensure the stored preference is active on startup
|
||||
this.translate.use(this.lang());
|
||||
this.markDocumentLanguage(this.lang());
|
||||
}
|
||||
|
||||
use(l: Lang) {
|
||||
this.lang.set(l);
|
||||
this.translate.use(l);
|
||||
this.markDocumentLanguage(l);
|
||||
try { localStorage.setItem(LocalStoreConstants.LANGUAGE_KEY, l); } catch (e) { void e; }
|
||||
}
|
||||
|
||||
/**
|
||||
* index.html ships a static lang="en". Without this the document keeps
|
||||
* claiming English after a switch, so a screen reader reads the German
|
||||
* content with an English voice.
|
||||
*/
|
||||
private markDocumentLanguage(l: Lang): void {
|
||||
this.document.documentElement.lang = l;
|
||||
}
|
||||
|
||||
private getInitial(): Lang {
|
||||
try {
|
||||
const stored = localStorage.getItem(LocalStoreConstants.LANGUAGE_KEY) as Lang | null;
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
"PROFILE": "Profilfoto von Andreas Dahm"
|
||||
},
|
||||
"HELLO": "Hallo, ich bin Andreas.",
|
||||
"LEAD": "Senior Softwareentwickler und Architekt mit über 15 Jahren Erfahrung. Meine Arbeit bewegt sich zwischen zwei Bereichen, die selten zusammentreffen: Echtzeit-3D-Simulation — Stoffphysik, Kollisionserkennung, GPU-Shader — und verteilte Cloud-Architektur für Unternehmensplattformen. Unterwegs habe ich ein Entwicklungsteam geleitet, die Migration eines großen Legacy-Monolithen zu Microservices geplant und ein Spiel auf Steam veröffentlicht. In letzter Zeit beschäftigt mich vor allem die Engineering-Seite der KI-gestützten Entwicklung: die Harnesses, Leitplanken und Quality Gates, die generierten Code überprüfbar machen. Privat entwickle ich Spiele und beschäftige mich mit algorithmischen Themen — vieles davon findet sich irgendwo auf dieser Seite wieder.",
|
||||
"LEAD": "Ich arbeite seit 2010 als Softwareentwickler. Die meiste Zeit davon in zwei ziemlich unterschiedlichen Ecken. Die eine ist Echtzeit-3D-Simulation: Stoffphysik, Kollisionserkennung, GPU-Shader. Die andere ist Cloud-Architektur für größere Unternehmensplattformen. Ein paar Jahre habe ich ein kleines Backend-Team geleitet, den Umbau eines großen Legacy-Monolithen Richtung Microservices geplant und zusammen mit meinem Bruder ein Spiel auf Steam veröffentlicht. In letzter Zeit geht viel Zeit in Tooling rund um KI-gestützte Entwicklung, vor allem darum, das Ergebnis überprüfbar zu machen und nicht nur schnell. Privat baue ich Spiele und probiere Sachen mit Algorithmen aus. Das meiste davon landet irgendwann auf dieser Seite.",
|
||||
"ROLE": "Senior Software Entwickler / Full-Stack Entwickler / Softwarearchitekt",
|
||||
"LOCATION": "München · Remote",
|
||||
"VIEW_PROJECTS": "Projekte ansehen",
|
||||
@@ -187,12 +187,12 @@
|
||||
"PROJECT": {
|
||||
"CLAUDE_BOX": {
|
||||
"TITLE": "Claude-in-a-Box",
|
||||
"DESCRIPTION": "Eine containerisierte Entwicklungsumgebung, die einen KI-Coding-Agenten zu einem überprüfbaren Teil der Toolchain macht. Statt spontaner Prompts führt die Box den Agenten über Regeln pro Stack, Architekturprofile pro Repository und Commit-Hooks, die keinen roten Build durchlassen.",
|
||||
"DESCRIPTION": "Eine Entwicklungsumgebung im Container, in der ein KI-Coding-Agent nach festen Regeln arbeitet statt nach spontanen Prompts. Er zieht sich die Regeln für den Stack, den er im Repository vorfindet, und ein Git-Hook stoppt den Commit, wenn Build oder Tests rot sind.",
|
||||
"LINK_INTERNAL": "Projektdetails",
|
||||
"HIGHLIGHTS": {
|
||||
"P1": "Deterministisches Context-Routing: Der Agent lädt die Regeln des Stacks, den er im Repository tatsächlich vorfindet.",
|
||||
"P2": "Commit-Gates, die bei fehlgeschlagenem Build oder Test blockieren — generierter Code kann die Qualitätsschwelle nicht umgehen.",
|
||||
"P3": "MCP-Integrationen für Code-Intelligence und lesenden Ticket-Zugriff, dazu Subagenten, die den Diff prüfen."
|
||||
"P1": "Der Agent lädt die Regeln für den Stack, den er im Repo vorfindet. Ein Java-Service bekommt also andere als ein Frontend.",
|
||||
"P2": "Ein Commit-Hook führt Build und Tests aus und blockiert den Commit, wenn sie fehlschlagen.",
|
||||
"P3": "MCP-Server für Codesuche und lesenden Jira-Zugriff, dazu eigene Review-Agenten, die den Diff durchgehen."
|
||||
}
|
||||
},
|
||||
"P2": {
|
||||
@@ -364,17 +364,17 @@
|
||||
},
|
||||
"CLAUDE_BOX": {
|
||||
"TITLE": "Claude-in-a-Box",
|
||||
"SHORT_DESCRIPTION": "Eine containerisierte, regelgetriebene Umgebung für die Arbeit mit einem KI-Coding-Agenten.",
|
||||
"INTRODUCTION": "Mit einem KI-Coding-Agenten zu arbeiten ist leicht angefangen und schwer ehrlich gehalten. Das Ergebnis sieht plausibel aus, also sind die teuren Fehler genau die, die durch das Review kommen. Claude-in-a-Box ist meine Antwort darauf: eine Docker-basierte Entwicklungsumgebung, in der der Agent unter denselben Bedingungen arbeitet wie eine neue Kollegin oder ein neuer Kollege — nach den Architekturregeln des Repositories, in dem er sich befindet, und mit einem Build, der grün bleiben muss, bevor irgendetwas committet werden kann. Das interessante Engineering steckt nicht in den Prompts, sondern im Harness darum herum.",
|
||||
"BULLET_1": "Containerisierter Workspace mit fixierten Toolchains — über SDKMAN verwaltete JDKs und Maven, Node und ein persistentes Home-Volume, das einen Container-Rebuild überlebt.",
|
||||
"BULLET_2": "Context-Routing per Erkennung: Ein Repository mit pom.xml lädt die Java/Spring-Regeln, eines mit package.json die TypeScript-Regeln, und ein Repository mit eigenem Architekturprofil lädt zusätzlich dieses.",
|
||||
"BULLET_3": "Ein Commit-Hook, der den Stack selbst erkennt, das passende Build- und Test-Gate ausführt und den Commit bei Fehlschlag blockiert.",
|
||||
"BULLET_4": "MCP-Server für Code-Graph-Abfragen und lesenden Ticket-Zugriff, dazu eigens gebaute Review-Subagenten, die einen fertigen Diff prüfen, bevor er als erledigt gemeldet wird.",
|
||||
"CHALLENGE_1": "Das Regelwerk klein genug für den Context zu halten und trotzdem verbindlich zu machen — allgemeine Ratschläge werden ignoriert, also mussten die Regeln in Stack-Skills und Repository-Profile aufgeteilt werden, die nur bei Bedarf laden.",
|
||||
"CHALLENGE_2": "Das Quality Gate unumgehbar machen. Die Prüfung läuft als Hook außerhalb des Agenten, sodass ein fehlgeschlagener Build den Commit blockiert — unabhängig davon, was der Agent über seine eigene Arbeit glaubt.",
|
||||
"CHALLENGE_3": "Toolchain-Isolation in einem Container ohne Root: Builds, Browser und Sprach-Runtimes mussten alle in Volumes installiert werden, die dem Entwickler-User gehören.",
|
||||
"LEARNING_1": "Die Qualität KI-gestützter Arbeit hängt vor allem am Harness, nicht am Prompt. Deterministische Gates schlagen sorgfältige Formulierungen jedes Mal.",
|
||||
"LEARNING_2": "Den Agenten als nicht vertrauenswürdigen Mitwirkenden zu behandeln — reviewed, gegated und nur mit dem nötigen Context versorgt — ist dieselbe Disziplin, die auch menschliches Code-Review funktionieren lässt."
|
||||
"SHORT_DESCRIPTION": "Ein Container-Setup, um mit einem KI-Coding-Agenten nach festen Regeln zu arbeiten.",
|
||||
"INTRODUCTION": "Einen KI-Coding-Agenten dazu zu bringen, irgendetwas zu produzieren, ist einfach. Ihn dazu zu bringen, etwas zu produzieren, das man auch wirklich mergen würde, ist eine andere Sache, weil das Ergebnis auf den ersten Blick meistens ganz vernünftig aussieht. Also habe ich ein Docker-Setup gebaut, in dem der Agent wie jeder andere Mitwirkende arbeitet. Er bekommt die Architekturregeln des Repositories, in dem er gerade ist, und der Build muss grün sein, bevor überhaupt etwas committet werden kann. Die meiste Arbeit steckt im Setup rund um den Agenten und nicht in den Prompts.",
|
||||
"BULLET_1": "Container mit festen Toolchains: JDKs und Maven über SDKMAN, Node und ein Home-Volume, das einen Rebuild übersteht.",
|
||||
"BULLET_2": "Die Regeln werden per Erkennung geladen. Ein Repo mit pom.xml bekommt die Java- und Spring-Regeln, eines mit package.json die TypeScript-Regeln, und Repos mit eigenen Architekturnotizen bekommen diese zusätzlich.",
|
||||
"BULLET_3": "Ein Commit-Hook ermittelt den Stack, führt den passenden Build und die Tests aus und verweigert den Commit, wenn sie fehlschlagen.",
|
||||
"BULLET_4": "MCP-Server für Code-Graph-Abfragen und lesende Jira-Zugriffe, dazu Review-Agenten, die einen fertigen Diff prüfen, bevor er als erledigt gilt.",
|
||||
"CHALLENGE_1": "Die Regeln kurz genug zu halten, dass sie noch etwas bringen. Lange allgemeine Richtlinien werden einfach ignoriert, also sind sie in Dateien pro Stack und pro Repo aufgeteilt, die nur bei Bedarf geladen werden.",
|
||||
"CHALLENGE_2": "Das Quality Gate so zu bauen, dass der Agent sich nicht daran vorbeireden kann. Die Prüfung läuft als Git-Hook außerhalb des Agenten, ein fehlgeschlagener Build blockiert den Commit also unabhängig davon, was der Agent über seine eigene Arbeit denkt.",
|
||||
"CHALLENGE_3": "Die Toolchains in einem Container ohne Root installiert zu bekommen. Builds, Browser und Sprach-Runtimes mussten alle in Volumes, die dem Entwickler-User gehören.",
|
||||
"LEARNING_1": "Wie gut das Ganze funktioniert, hängt viel stärker am Setup rund um den Agenten als an der Formulierung der Prompts.",
|
||||
"LEARNING_2": "Den Output des Agenten genauso zu reviewen und zu gaten wie den Pull Request einer neuen Kollegin hat sich als die nützlichste Regel überhaupt herausgestellt."
|
||||
}
|
||||
},
|
||||
"IMPRINT": {
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
"PROFILE": "Profile photo of Andreas Dahm"
|
||||
},
|
||||
"HELLO": "Hello, I’m Andreas.",
|
||||
"LEAD": "Senior software developer and architect, 15+ years in the field. My work sits across two areas that rarely meet: real-time 3D simulation — cloth physics, collision detection, GPU shaders — and distributed cloud architecture for enterprise platforms. Along the way I have led a development team, planned the migration of a large legacy monolith to microservices, and shipped a game on Steam. More recently I have been working on the engineering side of AI-assisted development: the harnesses, guardrails and quality gates that make generated code reviewable. Outside work I build games and dig into algorithmic problems — most of which end up somewhere on this site.",
|
||||
"LEAD": "I have been working as a software developer since 2010. Most of that time went into two fairly different corners. One is real-time 3D simulation: cloth physics, collision detection, GPU shaders. The other is cloud architecture for larger enterprise platforms. I led a small backend team for a few years, planned the move of a big legacy monolith towards microservices, and released a game on Steam together with my brother. Lately a lot of my time goes into tooling around AI-assisted development, mostly into making the output reviewable instead of just fast. In my free time I build games and mess around with algorithms. Most of that ends up somewhere on this site.",
|
||||
"ROLE": "Senior Software Developer / Full-Stack Developer / Software Architect",
|
||||
"LOCATION": "Munich · Remote",
|
||||
"VIEW_PROJECTS": "View projects",
|
||||
@@ -187,12 +187,12 @@
|
||||
"PROJECT": {
|
||||
"CLAUDE_BOX": {
|
||||
"TITLE": "Claude-in-a-Box",
|
||||
"DESCRIPTION": "A containerised development environment that turns an AI coding agent into a reviewable part of the toolchain. Instead of prompting ad hoc, the box routes the agent through per-stack rules, per-repository architecture profiles and commit hooks that refuse to let a red build through.",
|
||||
"DESCRIPTION": "A development setup in a container where an AI coding agent works under fixed rules instead of ad hoc prompting. It picks up the rules for whatever stack it finds in the repository, and a git hook stops the commit if the build or the tests are red.",
|
||||
"LINK_INTERNAL": "Project details",
|
||||
"HIGHLIGHTS": {
|
||||
"P1": "Deterministic context routing: the agent loads the rules for the stack it actually detects in the repository.",
|
||||
"P2": "Commit gates that block on a failing build or test run, so generated code cannot bypass the quality bar.",
|
||||
"P3": "MCP integrations for code intelligence and read-only ticket access, plus subagents that review the diff."
|
||||
"P1": "The agent loads the rules for the stack it finds in the repo, so a Java service and a frontend get different ones.",
|
||||
"P2": "A commit hook runs the build and the tests and blocks the commit when they fail.",
|
||||
"P3": "MCP servers for code search and read-only Jira access, plus separate review agents that go over the diff."
|
||||
}
|
||||
},
|
||||
"P2": {
|
||||
@@ -364,17 +364,17 @@
|
||||
},
|
||||
"CLAUDE_BOX": {
|
||||
"TITLE": "Claude-in-a-Box",
|
||||
"SHORT_DESCRIPTION": "A containerised, rule-driven environment for working with an AI coding agent.",
|
||||
"INTRODUCTION": "Working with an AI coding agent is easy to start and hard to keep honest. The output looks plausible, so the expensive failures are the ones that pass review. Claude-in-a-Box is my answer to that: a Docker-based development environment where the agent operates under the same constraints a new colleague would — the architecture rules of the repository it is in, and a build that has to stay green before anything can be committed. The interesting engineering is not in the prompts; it is in the harness around them.",
|
||||
"BULLET_1": "Containerised workspace with pinned toolchains — SDKMAN-managed JDKs and Maven, Node, and a persistent home volume that survives a container rebuild.",
|
||||
"BULLET_2": "Context routing by detection: a repository with a pom.xml loads the Java/Spring rules, one with a package.json loads the TypeScript rules, and a repository with its own architecture profile loads that too.",
|
||||
"BULLET_3": "A commit hook that detects the stack itself, runs the matching build and test gate, and blocks the commit when it fails.",
|
||||
"BULLET_4": "MCP servers for code-graph queries and read-only ticket lookup, plus purpose-built review subagents that grill a finished diff before it is reported as done.",
|
||||
"CHALLENGE_1": "Keeping the rule set small enough to stay in context but specific enough to be binding — general advice gets ignored, so the rules had to be split into stack skills and repository profiles that load only when relevant.",
|
||||
"CHALLENGE_2": "Making the quality gate impossible to talk around. The check runs as a hook outside the agent, so a failing build blocks the commit regardless of what the agent believes about its own work.",
|
||||
"CHALLENGE_3": "Toolchain isolation in a container that has no root: builds, browsers and language runtimes all had to be installed into volumes owned by the development user.",
|
||||
"LEARNING_1": "The quality of AI-assisted work is mostly a function of the harness, not the prompt. Deterministic gates beat careful wording every time.",
|
||||
"LEARNING_2": "Treating the agent as an untrusted contributor — reviewed, gated, and given only the context it needs — turns out to be the same discipline that makes human code review work."
|
||||
"SHORT_DESCRIPTION": "A container setup for working with an AI coding agent under fixed rules.",
|
||||
"INTRODUCTION": "Getting an AI coding agent to produce something is easy. Getting it to produce something you would actually merge is a different problem, because the output usually looks fine at first glance. So I built a Docker setup where the agent works like any other contributor. It gets the architecture rules of the repository it is in, and the build has to be green before anything can be committed. Most of the work went into the setup around the agent rather than into the prompts.",
|
||||
"BULLET_1": "Container with fixed toolchains: JDKs and Maven through SDKMAN, Node, and a home volume that survives a rebuild.",
|
||||
"BULLET_2": "Rules are loaded by detection. A repo with a pom.xml gets the Java and Spring rules, one with a package.json gets the TypeScript rules, and repos with their own architecture notes get those on top.",
|
||||
"BULLET_3": "A commit hook works out the stack, runs the matching build and tests, and refuses the commit if they fail.",
|
||||
"BULLET_4": "MCP servers for code graph queries and read-only Jira lookups, plus review agents that check a finished diff before it counts as done.",
|
||||
"CHALLENGE_1": "Keeping the rules short enough to stay useful. Long general guidelines just get ignored, so they are split into per-stack and per-repo files that only load when they apply.",
|
||||
"CHALLENGE_2": "Making the quality gate something the agent cannot talk its way past. The check runs as a git hook outside the agent, so a failing build blocks the commit no matter what the agent believes about its own work.",
|
||||
"CHALLENGE_3": "Getting the toolchains installed in a container that has no root. Builds, browsers and language runtimes all had to go into volumes owned by the development user.",
|
||||
"LEARNING_1": "How well this works depends far more on the setup around the agent than on how the prompt is worded.",
|
||||
"LEARNING_2": "Reviewing and gating the agent's output the same way you would a new colleague's pull request turned out to be the most useful rule of the lot."
|
||||
}
|
||||
},
|
||||
"IMPRINT": {
|
||||
|
||||
BIN
src/assets/logos/logo-wolf.png
Normal file
BIN
src/assets/logos/logo-wolf.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
@@ -27,9 +27,18 @@
|
||||
<meta name="twitter:image" content="https://andreas-dahm.eu/assets/me.webp">
|
||||
|
||||
<link rel="icon" type="image/x-icon" href="assets/favicon.ico">
|
||||
|
||||
<!--
|
||||
The LCP image lives in a lazily loaded route chunk, so the browser cannot
|
||||
discover it until Angular has booted. Preloading it here is what makes it
|
||||
fetch in parallel with the bundle instead of after it.
|
||||
-->
|
||||
<link rel="preload" as="image" href="assets/me.webp" fetchpriority="high">
|
||||
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500&display=swap" rel="stylesheet">
|
||||
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined&display=swap" rel="stylesheet">
|
||||
<link href="https://fonts.googleapis.com/icon?family=Material+Icons&display=swap" rel="stylesheet">
|
||||
</head>
|
||||
<body>
|
||||
<app-root class="flex flex-col min-h-screen"></app-root>
|
||||
|
||||
@@ -31,8 +31,13 @@ $dark-theme: mat.define-theme((color: (theme-type: dark, primary: mat.$cyan-pale
|
||||
--card-border-inset: rgba(255, 255, 255, 0.35);
|
||||
--card-border-inset-dark: rgba(0, 0, 0, 0.35);
|
||||
|
||||
--link-color: #38a7ff;
|
||||
--link-color-hover: #66bfff;
|
||||
/*
|
||||
Chosen to clear WCAG AA *after* compositing: several link containers carry
|
||||
opacity-85/opacity-80, which blends the text toward the surface. Worst case
|
||||
(footer, 80% over #f1f4f3) is 4.73:1; a plain link on a card is 8.03:1.
|
||||
*/
|
||||
--link-color: #0f4f8a;
|
||||
--link-color-hover: #0a3a66;
|
||||
}
|
||||
|
||||
.dark {
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
--container-app: var(--app-maxWidth);
|
||||
--radius-card: var(--card-radius);
|
||||
|
||||
--font-sans: Inter, Roboto, Arial, sans-serif;
|
||||
--font-sans: Roboto, Arial, sans-serif;
|
||||
|
||||
--spacing-fluid-sm: clamp(0.5rem, 2vw, 1rem);
|
||||
--spacing-fluid-md: clamp(1rem, 3vw, 1.5rem);
|
||||
@@ -88,7 +88,7 @@
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: Inter, Roboto, Arial, sans-serif;
|
||||
font-family: Roboto, Arial, sans-serif;
|
||||
background-color: var(--app-bg);
|
||||
color: var(--app-fg);
|
||||
transition: background-color 220ms ease, color 220ms ease;
|
||||
@@ -98,10 +98,6 @@
|
||||
@apply text-link no-underline font-medium hover:text-link-hover hover:underline;
|
||||
}
|
||||
|
||||
.material-symbols-outlined {
|
||||
font-variation-settings: "FILL" 0, "wght" 400, "GRAD" 0, "opsz" 24;
|
||||
}
|
||||
|
||||
.dark body {
|
||||
background: radial-gradient(ellipse at 50% 0%, #1e2530 0%, #1a1a1a 65%);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user