Lay the Almanacka design foundation: tokens, type, shell

New token system in global.css (near-white surfaces, one ink-blue
accent, no sub-12px type), self-hosted Schibsted Grotesk, rewritten
primitives (buttons, fields, modal shell, segmented, disclosure,
skeleton), a borderless 56px header with an SV | EN language toggle,
an Icon component replacing glyphs, page skeletons that keep the
layout during loading, and leave-type colours moved out of TS into
CSS variables. Feature blocks keep their structure for later phases.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
2026-09-19 18:25:04 +02:00
parent 95444d3bf4
commit d14f86478a
44 changed files with 1358 additions and 485 deletions
@@ -1,16 +1,31 @@
import { ReactNode } from 'react'
import { useTranslation } from '@/i18n'
import './loading-spinner.css'
interface LoadingSpinnerProps {
message?: string
ariaLabel?: string
message?: string | undefined
ariaLabel?: string | undefined
}
export function LoadingSpinner({ message = 'Loading...', ariaLabel = 'Loading' }: LoadingSpinnerProps): ReactNode {
/**
* A ring spinner with a message. Defaults come from the i18n bundle so a
* bare `<LoadingSpinner />` reads correctly in either language; callers
* with a more specific message ("Laddar kalender…") pass it in.
*/
export function LoadingSpinner({ message, ariaLabel }: LoadingSpinnerProps): ReactNode {
const { t } = useTranslation()
return (
<div className="loading-spinner" role="status" aria-label={ariaLabel} aria-busy={true} data-testid="loading-spinner">
<div className="loading-spinner__icon" data-testid="spinner-icon" role="presentation">⊙</div>
<p className="loading-spinner__message" data-testid="spinner-message">{message}</p>
<div
className="loading-spinner"
role="status"
aria-label={ariaLabel ?? t('common.loading')}
aria-busy={true}
data-testid="loading-spinner"
>
<div className="loading-spinner__icon" data-testid="spinner-icon" role="presentation" />
<p className="loading-spinner__message" data-testid="spinner-message">
{message ?? t('common.loadingEllipsis')}
</p>
</div>
)
}