fix(design): stop synthesizing bold on Hedvig display headings (#1555)

* fix(design): stop synthesizing bold on Hedvig display headings

Hedvig Letters Serif ships weight 400 only, but the h1-h3 base rule
forced font-weight 500 and DialogTitle/SheetTitle stacked font-semibold
on top, so every display heading rendered browser-synthesized bold: the
smudged heavy look on dialog titles and page headings. Drop the base
rule to 400, remove the weight utilities from the title primitives, and
sweep the 41 files that hand-set font-medium/semibold/bold on serif
headings (a pattern design.md already forbids). Headings that opt into
font-sans keep their weight.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(design): drop empty className left by the weight sweep

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Jakob Wennberg <311770904+jakobwennberg-oss@users.noreply.github.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Jakob Wennberg
2026-08-13 11:27:25 +02:00
committed by GitHub
co-authored by Claude Fable 5 Jakob Wennberg
parent e51a2c8102
commit 314efe8b22
41 changed files with 75 additions and 74 deletions
+2 -2
View File
@@ -377,7 +377,7 @@ export function LoginClient({ initialMethod }: { initialMethod: LoginMethod | nu
</div>
<div className="text-center space-y-2">
<h1 className="text-2xl font-medium tracking-tight">{tAuth('email_sent_title')}</h1>
<h1 className="text-2xl tracking-tight">{tAuth('email_sent_title')}</h1>
<p className="text-muted-foreground text-sm leading-relaxed">
{showResetPassword
? tAuth.rich('email_sent_body_reset', {
@@ -436,7 +436,7 @@ export function LoginClient({ initialMethod }: { initialMethod: LoginMethod | nu
<KeyRound className="h-7 w-7 text-primary" />
</div>
</div>
<h1 className="text-2xl font-medium tracking-tight">{tAuth('reset_title')}</h1>
<h1 className="text-2xl tracking-tight">{tAuth('reset_title')}</h1>
<p className="text-muted-foreground text-sm mt-2">
{tAuth('reset_subtitle')}
</p>
+2 -2
View File
@@ -183,7 +183,7 @@ function MfaEnrollContent() {
<ShieldCheck className="h-7 w-7 text-primary" />
</div>
</div>
<h1 className="text-2xl font-medium tracking-tight">Aktivera tvåfaktorsautentisering</h1>
<h1 className="text-2xl tracking-tight">Aktivera tvåfaktorsautentisering</h1>
<p className="text-muted-foreground text-sm mt-2">
Skydda ditt konto med en autentiseringsapp som Google Authenticator eller Authy
</p>
@@ -237,7 +237,7 @@ function MfaEnrollContent() {
<ShieldCheck className="h-7 w-7 text-primary" />
</div>
</div>
<h1 className="text-2xl font-medium tracking-tight">Skanna QR-koden</h1>
<h1 className="text-2xl tracking-tight">Skanna QR-koden</h1>
<p className="text-muted-foreground text-sm mt-2">
Öppna din autentiseringsapp och skanna koden nedan
</p>
+1 -1
View File
@@ -178,7 +178,7 @@ function MfaVerifyContent() {
<ShieldCheck className="h-7 w-7 text-primary" />
</div>
</div>
<h1 className="text-2xl font-medium tracking-tight">{t('verify_title')}</h1>
<h1 className="text-2xl tracking-tight">{t('verify_title')}</h1>
<p className="text-muted-foreground text-sm mt-2">
{t('verify_subtitle_full')}
</p>
+2 -2
View File
@@ -393,7 +393,7 @@ function RegisterPageContent() {
</div>
<div className="text-center space-y-2">
<h1 className="text-2xl font-medium tracking-tight">{t('duplicate_title')}</h1>
<h1 className="text-2xl tracking-tight">{t('duplicate_title')}</h1>
<p className="text-muted-foreground text-sm leading-relaxed">
{t('duplicate_body_prefix')}{' '}
<span className="font-medium text-foreground">{duplicateEmail}</span>.
@@ -446,7 +446,7 @@ function RegisterPageContent() {
</div>
<div className="text-center space-y-2">
<h1 className="text-2xl font-medium tracking-tight">{t('confirm_email_title')}</h1>
<h1 className="text-2xl tracking-tight">{t('confirm_email_title')}</h1>
<p className="text-muted-foreground text-sm leading-relaxed">
{t.rich('confirm_email_body', {
email,
+1 -1
View File
@@ -216,7 +216,7 @@ function ResetPasswordInner() {
<KeyRound className="h-7 w-7 text-primary" />
</div>
</div>
<h1 className="text-2xl font-medium tracking-tight">{t('title')}</h1>
<h1 className="text-2xl tracking-tight">{t('title')}</h1>
<p className="text-muted-foreground text-sm mt-2">{subtitle}</p>
</div>
+1 -1
View File
@@ -17,7 +17,7 @@ export default function DashboardError({
return (
<div className="flex flex-col items-center justify-center min-h-[50vh] gap-4">
<h2 className="text-xl font-semibold">Något gick fel</h2>
<h2 className="text-xl">Något gick fel</h2>
<p className="text-muted-foreground text-sm max-w-md text-center">
Ett oväntat fel uppstod. Försök igen eller{' '}
<SupportLink variant="inline" subject="Oväntat fel">kontakta support</SupportLink>{' '}
@@ -95,14 +95,14 @@ export default async function ExtensionDetailPage({
{/* Details */}
<div className="space-y-6">
<div>
<h2 className="text-sm font-medium uppercase tracking-wider text-muted-foreground mb-2">{t('description_heading')}</h2>
<h2 className="text-sm uppercase tracking-wider text-muted-foreground mb-2">{t('description_heading')}</h2>
<p className="text-sm text-muted-foreground leading-relaxed">
{extensionLongDescription}
</p>
</div>
<div>
<h2 className="text-sm font-medium uppercase tracking-wider text-muted-foreground mb-2">{t('data_source_heading')}</h2>
<h2 className="text-sm uppercase tracking-wider text-muted-foreground mb-2">{t('data_source_heading')}</h2>
<p className="text-sm text-muted-foreground">
{dataPatternLabels[definition.dataPattern]}
</p>
+2 -2
View File
@@ -25,7 +25,7 @@ export default async function ExtensionsPage() {
{/* General extensions */}
{generalSector && (
<section>
<h2 className="text-sm font-medium uppercase tracking-wider text-muted-foreground mb-4">
<h2 className="text-sm uppercase tracking-wider text-muted-foreground mb-4">
{generalSectorName}
</h2>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 stagger-enter">
@@ -39,7 +39,7 @@ export default async function ExtensionsPage() {
{/* Industry sectors (hidden while no industry sector ships extensions) */}
{industrySectors.length > 0 && (
<section>
<h2 className="text-sm font-medium uppercase tracking-wider text-muted-foreground mb-4">
<h2 className="text-sm uppercase tracking-wider text-muted-foreground mb-4">
{t('industry_tools')}
</h2>
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-4 stagger-enter">
+1 -1
View File
@@ -237,7 +237,7 @@ function TermCard({ term, isExpanded, onToggle }: { term: GlossaryTerm; isExpand
</div>
<div>
<div className="flex items-center gap-2 flex-wrap">
<h3 className="font-medium">{term.term}</h3>
<h3>{term.term}</h3>
{term.simpleTerm && (
<span className="text-sm text-muted-foreground font-normal">
{term.simpleTerm}
@@ -241,7 +241,7 @@ export function KassaflodesanalysClient() {
{/* Section 1: Löpande verksamhet */}
<Card>
<CardHeader>
<h2 className="text-sm font-medium uppercase tracking-wider text-muted-foreground">
<h2 className="text-sm uppercase tracking-wider text-muted-foreground">
Den löpande verksamheten
</h2>
<CardTitle className="text-base">
@@ -281,7 +281,7 @@ export function KassaflodesanalysClient() {
{/* Section 2: Investeringsverksamhet */}
<Card>
<CardHeader>
<h2 className="text-sm font-medium uppercase tracking-wider text-muted-foreground">
<h2 className="text-sm uppercase tracking-wider text-muted-foreground">
Investeringsverksamheten
</h2>
<CardTitle className="text-base">
@@ -307,7 +307,7 @@ export function KassaflodesanalysClient() {
{/* Section 3: Finansieringsverksamhet */}
<Card>
<CardHeader>
<h2 className="text-sm font-medium uppercase tracking-wider text-muted-foreground">
<h2 className="text-sm uppercase tracking-wider text-muted-foreground">
Finansieringsverksamheten
</h2>
<CardTitle className="text-base">
+3 -3
View File
@@ -117,7 +117,7 @@ export default function InvoiceActionPage({ params }: { params: Promise<{ token:
<Card className="max-w-md w-full">
<CardContent className="pt-6 text-center">
<AlertCircle className="h-12 w-12 text-destructive mx-auto mb-4" />
<h2 className="text-lg font-semibold mb-2">Ogiltig länk</h2>
<h2 className="text-lg mb-2">Ogiltig länk</h2>
<p className="text-muted-foreground">{error}</p>
</CardContent>
</Card>
@@ -136,7 +136,7 @@ export default function InvoiceActionPage({ params }: { params: Promise<{ token:
<Card className="max-w-md w-full">
<CardContent className="pt-6 text-center">
<CheckCircle className="h-12 w-12 text-success mx-auto mb-4" />
<h2 className="text-lg font-semibold mb-2">Tack för ditt svar!</h2>
<h2 className="text-lg mb-2">Tack för ditt svar!</h2>
<p className="text-muted-foreground mb-4">
{successMessage || (
invoice.previousResponse === 'marked_paid'
@@ -170,7 +170,7 @@ export default function InvoiceActionPage({ params }: { params: Promise<{ token:
<div className="max-w-lg mx-auto">
{/* Header */}
<div className="text-center mb-8">
<h1 className="text-2xl font-bold text-foreground mb-2">
<h1 className="text-2xl text-foreground mb-2">
Betalningspåminnelse
</h1>
<p className="text-muted-foreground">
+1 -1
View File
@@ -15,7 +15,7 @@ export default function PrivacyPolicyPage() {
<div className="min-h-dvh bg-background py-12 px-4">
<div className="max-w-3xl mx-auto space-y-6">
<div className="text-center mb-8">
<h1 className="text-3xl font-bold text-foreground mb-2">
<h1 className="text-3xl text-foreground mb-2">
Integritetspolicy
</h1>
<p className="text-muted-foreground">
+1 -1
View File
@@ -65,7 +65,7 @@ export default function GlobalError({
{phase === "fallback" ? (
<div className="flex min-h-dvh items-center justify-center p-8">
<div className="text-center space-y-4">
<h2 className="text-xl font-semibold">Något gick fel</h2>
<h2 className="text-xl">Något gick fel</h2>
<p className="text-muted-foreground">
Ett oväntat fel inträffade. Försök igen eller{" "}
<a
+4 -3
View File
@@ -414,18 +414,19 @@ summary,
}
}
/* Display typography - Fraunces */
/* Display typography - Hedvig Letters Serif */
.font-display {
font-family: var(--font-display);
font-optical-sizing: auto;
}
/* Layered so utility classes (e.g. font-sans on a pane title) can override:
unlayered element rules would beat Tailwind's layered utilities. */
unlayered element rules would beat Tailwind's layered utilities.
Hedvig ships weight 400 only; anything heavier is browser-synthesized. */
@layer base {
h1, h2, h3 {
font-family: var(--font-display);
font-weight: 500;
font-weight: 400;
letter-spacing: -0.015em;
font-optical-sizing: auto;
}
+2 -2
View File
@@ -86,7 +86,7 @@ export default function SandboxPage() {
</div>
<div className="rounded-xl border bg-card p-6" style={{ boxShadow: 'var(--shadow-md)' }}>
<h1 className="text-lg font-medium tracking-tight text-center mb-2">
<h1 className="text-lg tracking-tight text-center mb-2">
Du är redan inloggad
</h1>
<p className="text-sm text-muted-foreground text-center leading-relaxed">
@@ -111,7 +111,7 @@ export default function SandboxPage() {
<div className="w-full max-w-sm animate-slide-up">
<div className="text-center mb-10">
<BrandWordmark size="hero" className="mb-2" />
<h1 className="text-xl font-medium tracking-tight mt-3">
<h1 className="text-xl tracking-tight mt-3">
Testa {branding.appName.toLowerCase()} utan att registrera dig
</h1>
<p className="text-muted-foreground text-sm mt-2 leading-relaxed">