Reskin the whole app via design tokens in tailwind-theme.css (remapped color ramps) plus a define-once component layer in lib/styles/components.css (.btn, .card, .input, .chip, .badge, .sheet, …). Buttons are muted/outlined gold rather than flat fills. Self-host Inter + Fraunces (woff2) under the existing font-src 'self' CSP. Restyle the shared components and the account, admin, host, join, recover and upload screens against the new tokens. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
58 lines
1.9 KiB
Svelte
58 lines
1.9 KiB
Svelte
<script lang="ts">
|
|
import { focusTrap } from '$lib/actions/focus-trap';
|
|
import { scrollLock } from '$lib/actions/scroll-lock';
|
|
import { modalInert } from '$lib/actions/modal-inert';
|
|
import type { Snippet } from 'svelte';
|
|
|
|
// Accessible name is REQUIRED. Pass `titleId` when the dialog renders its own
|
|
// visible heading (the common case — link aria-labelledby to that heading's id),
|
|
// or `ariaLabel` for dialogs without a visible title (e.g. an image preview).
|
|
// Failing to provide either leaves the dialog without an accessible name,
|
|
// which screen readers announce as just "dialog" — useless. The runtime
|
|
// warning below catches missed cases during dev.
|
|
interface Props {
|
|
open: boolean;
|
|
titleId?: string;
|
|
ariaLabel?: string;
|
|
onClose: () => void;
|
|
closeOnBackdrop?: boolean;
|
|
children: Snippet;
|
|
}
|
|
|
|
let { open, titleId, ariaLabel, onClose, closeOnBackdrop = true, children }: Props = $props();
|
|
|
|
$effect(() => {
|
|
if (open && !titleId && !ariaLabel && typeof console !== 'undefined') {
|
|
console.warn('<Modal> opened without titleId or ariaLabel — dialog has no accessible name.');
|
|
}
|
|
});
|
|
</script>
|
|
|
|
{#if open}
|
|
<!-- display:contents wrapper: keeps the backdrop + dialog visually unchanged
|
|
while giving `modalInert` a single node whose siblings (the background
|
|
page, nav, etc.) get inerted for assistive tech. -->
|
|
<div class="contents" use:modalInert>
|
|
<button
|
|
type="button"
|
|
class="fixed inset-0 z-50 bg-black/50"
|
|
aria-label="Schließen"
|
|
tabindex="-1"
|
|
onclick={closeOnBackdrop ? onClose : () => {}}
|
|
></button>
|
|
<div
|
|
class="pointer-events-none fixed inset-0 z-50 flex items-center justify-center p-4"
|
|
role="dialog"
|
|
aria-modal="true"
|
|
aria-labelledby={titleId}
|
|
aria-label={titleId ? undefined : ariaLabel}
|
|
use:focusTrap={{ onclose: onClose }}
|
|
use:scrollLock
|
|
>
|
|
<div class="card pointer-events-auto w-full max-w-sm p-6 shadow-xl">
|
|
{@render children()}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{/if}
|