Add scripts/check-tokens.mjs (npm run lint:tokens, wired into lint) as
a guardrail: fails if src/**/*.svelte reintroduces arbitrary values
outside the B.4 allowlist, factory rounded-*, h-N w-N pairs (should
be size-N), quarter-step fractional spacing, or literal hex/rgba()
colors.
Cleans up the repo until the script reports 0 violations: ~40 h-N w-N
pairs -> size-N (mostly icons, via a one-off script), ~26 quarter-step
fractional spacings rounded to the nearest integer (delta <=1px, e.g.
py-2.75->py-3, gap-1.75->gap-2), stray factory radii (rounded-sm/lg,
bare rounded) migrated to the matching tokens, and two
tracking-[-0.02em] that matched the existing tracking-display token
exactly but had never been migrated. Removes the
stroke-thin/regular/bold recipes (added earlier, zero consumers) —
decision made, not blocking: strokeWidth={n} (13 values, across
almost every component) isn't migrated to CSS because there's no way
to verify without Docker that lucide-svelte honors stroke-width by
class instead of the prop, and a regression there would be silent
across the whole app; the script documents this gap explicitly
instead of pretending full coverage.
Orphan detector: 0 components with no importers. Removes
docs/design-system-refactor-plan.md (earlier plan, superseded by
frontend-refactor-plan.md). web-player/CLAUDE.md gains a "Styles"
section with the B.4 allowlist and the base components to reuse.
Recalculates the baseline table (A.4) and logs it: 61->47 components,
44->11 arbitrary values (the remaining 11 on the allowlist), 13->8
distinct radii, 0 lint:tokens violations.
Verified with npm run check (0 errors), npm run lint:tokens (0
violations) and npm run build. Smoke-tested /login with no console
errors attributable to this change.
38 lines
1.0 KiB
Svelte
38 lines
1.0 KiB
Svelte
<script lang="ts">
|
|
import { page } from '$app/state';
|
|
import { isSectionActive, appNavLinks } from '$lib/navigation.svelte';
|
|
import type { SessionUser } from '$lib/types';
|
|
|
|
interface Props {
|
|
user: SessionUser | null;
|
|
class?: string;
|
|
}
|
|
|
|
let { user, class: klass = '' }: Props = $props();
|
|
|
|
const links = $derived(appNavLinks(user).filter((link) => link.primary));
|
|
|
|
function isActive(href: string) {
|
|
return isSectionActive(href, page.url.pathname, page.data.section);
|
|
}
|
|
</script>
|
|
|
|
<nav
|
|
class="pointer-events-auto flex items-center justify-around glass-bar px-4 py-2 lg:hidden {klass}"
|
|
style="padding-bottom:calc(0.4375rem + var(--mf-safe-b))"
|
|
>
|
|
{#each links as link (link.href)}
|
|
{@const active = isActive(link.href)}
|
|
<a
|
|
href={link.href}
|
|
aria-current={active ? 'page' : undefined}
|
|
class="flex flex-col items-center gap-1 rounded-control px-2.5 py-1.5 {active
|
|
? 'text-fg'
|
|
: 'text-fg-2'}"
|
|
>
|
|
<link.icon class="size-4" strokeWidth={1.6} />
|
|
<span class="text-xs font-medium">{link.label}</span>
|
|
</a>
|
|
{/each}
|
|
</nav>
|