Compare commits
8 Commits
816d4b89ce
...
0ebf75b24e
| Author | SHA1 | Date | |
|---|---|---|---|
| 0ebf75b24e | |||
| 7b46e06f8b | |||
| 0737db69a9 | |||
| 64b4a65e7b | |||
| 7f0d2b54e0 | |||
| 5b1a1d0b0a | |||
| 0562b94b03 | |||
| ef08512986 |
@@ -91,7 +91,6 @@
|
|||||||
--space-4xl: 4rem;
|
--space-4xl: 4rem;
|
||||||
|
|
||||||
/* Typography Scale */
|
/* Typography Scale */
|
||||||
--text-2xs: 0.625rem;
|
|
||||||
--text-xs: 0.75rem;
|
--text-xs: 0.75rem;
|
||||||
--text-sm: 0.875rem;
|
--text-sm: 0.875rem;
|
||||||
--text-base: 1rem;
|
--text-base: 1rem;
|
||||||
@@ -205,6 +204,14 @@
|
|||||||
--font-mono: 'Space Mono', monospace;
|
--font-mono: 'Space Mono', monospace;
|
||||||
--font-primary: 'Space Grotesk', system-ui, -apple-system, 'Segoe UI', Inter, Roboto, Arial, sans-serif;
|
--font-primary: 'Space Grotesk', system-ui, -apple-system, 'Segoe UI', Inter, Roboto, Arial, sans-serif;
|
||||||
--font-secondary: 'Inter', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif;
|
--font-secondary: 'Inter', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif;
|
||||||
|
|
||||||
|
/* Micro typography scale — extends Tailwind's text-xs (0.75rem) downward */
|
||||||
|
--font-size-5xs: 0.4375rem;
|
||||||
|
--font-size-4xs: 0.5rem;
|
||||||
|
--font-size-3xs: 0.5625rem;
|
||||||
|
--font-size-2xs: 0.625rem;
|
||||||
|
/* Monospace label tracking — used in Loader and Footnote */
|
||||||
|
--tracking-wider-mono: 0.2em;
|
||||||
}
|
}
|
||||||
|
|
||||||
@layer base {
|
@layer base {
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ const stats = $derived([
|
|||||||
>
|
>
|
||||||
<!-- Left: index · name · type badge · provider badge -->
|
<!-- Left: index · name · type badge · provider badge -->
|
||||||
<div class="flex items-center gap-2 sm:gap-4 min-w-0 shrink-0">
|
<div class="flex items-center gap-2 sm:gap-4 min-w-0 shrink-0">
|
||||||
<span class="font-mono text-[0.625rem] tracking-widest text-neutral-400 uppercase leading-none shrink-0">
|
<span class="font-mono text-2xs tracking-widest text-neutral-400 uppercase leading-none shrink-0">
|
||||||
{String(index + 1).padStart(2, '0')}
|
{String(index + 1).padStart(2, '0')}
|
||||||
</span>
|
</span>
|
||||||
<Divider orientation="vertical" class="h-3 shrink-0" />
|
<Divider orientation="vertical" class="h-3 shrink-0" />
|
||||||
@@ -94,14 +94,14 @@ const stats = $derived([
|
|||||||
</span>
|
</span>
|
||||||
|
|
||||||
{#if fontType}
|
{#if fontType}
|
||||||
<Badge size="xs" variant="default" class="text-nowrap font-mono">
|
<Badge size="xs" variant="default" nowrap>
|
||||||
{fontType}
|
{fontType}
|
||||||
</Badge>
|
</Badge>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<!-- Provider badge -->
|
<!-- Provider badge -->
|
||||||
{#if providerBadge}
|
{#if providerBadge}
|
||||||
<Badge size="xs" variant="default" class="text-nowrap font-mono" data-provider={font.provider}>
|
<Badge size="xs" variant="default" nowrap data-provider={font.provider}>
|
||||||
{providerBadge}
|
{providerBadge}
|
||||||
</Badge>
|
</Badge>
|
||||||
{/if}
|
{/if}
|
||||||
@@ -147,7 +147,7 @@ const stats = $derived([
|
|||||||
<!-- ── Mobile stats footer (md:hidden — header stats take over above) -->
|
<!-- ── Mobile stats footer (md:hidden — header stats take over above) -->
|
||||||
<div class="md:hidden px-4 sm:px-5 py-1.5 sm:py-2 border-t border-subtle flex gap-2 sm:gap-4 bg-paper dark:bg-dark-card mt-auto">
|
<div class="md:hidden px-4 sm:px-5 py-1.5 sm:py-2 border-t border-subtle flex gap-2 sm:gap-4 bg-paper dark:bg-dark-card mt-auto">
|
||||||
{#each stats as stat, i}
|
{#each stats as stat, i}
|
||||||
<Footnote class="text-[0.4375rem] sm:text-[0.5rem] tracking-wider {i === 0 ? 'ml-auto' : ''}">
|
<Footnote class="text-5xs sm:text-4xs tracking-wider {i === 0 ? 'ml-auto' : ''}">
|
||||||
{stat.label}:{stat.value}
|
{stat.label}:{stat.value}
|
||||||
</Footnote>
|
</Footnote>
|
||||||
{#if i < stats.length - 1}
|
{#if i < stats.length - 1}
|
||||||
|
|||||||
@@ -61,13 +61,10 @@ function handleReset() {
|
|||||||
{#each SORT_OPTIONS as option}
|
{#each SORT_OPTIONS as option}
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size={isMobileOrTabletPortrait ? 'sm' : 'md'}
|
size={isMobileOrTabletPortrait ? 'xs' : 'sm'}
|
||||||
active={sortStore.value === option}
|
active={sortStore.value === option}
|
||||||
onclick={() => sortStore.set(option)}
|
onclick={() => sortStore.set(option)}
|
||||||
class={cn(
|
class="tracking-wide px-0"
|
||||||
'font-bold uppercase tracking-wide font-primary, px-0',
|
|
||||||
isMobileOrTabletPortrait ? 'text-[0.5625rem]' : 'text-[0.625rem]',
|
|
||||||
)}
|
|
||||||
>
|
>
|
||||||
{option}
|
{option}
|
||||||
</Button>
|
</Button>
|
||||||
@@ -78,12 +75,9 @@ function handleReset() {
|
|||||||
<!-- Reset_Filters -->
|
<!-- Reset_Filters -->
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size={isMobileOrTabletPortrait ? 'sm' : 'md'}
|
size={isMobileOrTabletPortrait ? 'xs' : 'sm'}
|
||||||
onclick={handleReset}
|
onclick={handleReset}
|
||||||
class={cn(
|
class={cn('group font-mono tracking-widest text-neutral-400', isMobileOrTabletPortrait && 'px-0')}
|
||||||
'group text-[0.5625rem] md:text-[0.625rem] font-mono font-bold uppercase tracking-widest text-neutral-400',
|
|
||||||
isMobileOrTabletPortrait && 'px-0',
|
|
||||||
)}
|
|
||||||
iconPosition="left"
|
iconPosition="left"
|
||||||
>
|
>
|
||||||
{#snippet icon()}
|
{#snippet icon()}
|
||||||
|
|||||||
@@ -113,7 +113,7 @@ $effect(() => {
|
|||||||
<div class="flex items-center gap-1.5">
|
<div class="flex items-center gap-1.5">
|
||||||
<Settings2Icon size={12} class="text-swiss-red" />
|
<Settings2Icon size={12} class="text-swiss-red" />
|
||||||
<span
|
<span
|
||||||
class="text-[0.5625rem] font-mono uppercase tracking-widest font-bold text-swiss-black dark:text-neutral-200"
|
class="text-3xs font-mono uppercase tracking-widest font-bold text-swiss-black dark:text-neutral-200"
|
||||||
>
|
>
|
||||||
CONTROLS
|
CONTROLS
|
||||||
</span>
|
</span>
|
||||||
@@ -166,7 +166,7 @@ $effect(() => {
|
|||||||
class="text-swiss-red"
|
class="text-swiss-red"
|
||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
class="text-[0.5625rem] md:text-[0.625rem] font-mono uppercase tracking-widest font-bold hidden sm:inline whitespace-nowrap"
|
class="text-3xs md:text-2xs font-mono uppercase tracking-widest font-bold hidden sm:inline whitespace-nowrap"
|
||||||
>
|
>
|
||||||
GLOBAL_CONTROLS
|
GLOBAL_CONTROLS
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -37,6 +37,11 @@ interface Props extends HTMLAttributes<HTMLSpanElement> {
|
|||||||
* @default false
|
* @default false
|
||||||
*/
|
*/
|
||||||
dot?: boolean;
|
dot?: boolean;
|
||||||
|
/**
|
||||||
|
* Prevent text wrapping
|
||||||
|
* @default false
|
||||||
|
*/
|
||||||
|
nowrap?: boolean;
|
||||||
/**
|
/**
|
||||||
* Content snippet
|
* Content snippet
|
||||||
*/
|
*/
|
||||||
@@ -51,6 +56,7 @@ let {
|
|||||||
variant = 'default',
|
variant = 'default',
|
||||||
size = 'xs',
|
size = 'xs',
|
||||||
dot = false,
|
dot = false,
|
||||||
|
nowrap = false,
|
||||||
children,
|
children,
|
||||||
class: className,
|
class: className,
|
||||||
...rest
|
...rest
|
||||||
@@ -63,6 +69,7 @@ let {
|
|||||||
'font-mono uppercase tracking-wide',
|
'font-mono uppercase tracking-wide',
|
||||||
labelSizeConfig[size],
|
labelSizeConfig[size],
|
||||||
badgeVariantConfig[variant],
|
badgeVariantConfig[variant],
|
||||||
|
nowrap && 'text-nowrap',
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
{...rest}
|
{...rest}
|
||||||
|
|||||||
@@ -150,11 +150,11 @@ const variantStyles: Record<ButtonVariant, string> = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const sizeStyles: Record<ButtonSize, string> = {
|
const sizeStyles: Record<ButtonSize, string> = {
|
||||||
xs: 'h-6 px-2 text-[9px] gap-1',
|
xs: 'h-6 px-2 text-3xs gap-1',
|
||||||
sm: 'h-8 px-3 text-[10px] gap-1.5',
|
sm: 'h-8 px-3 text-2xs gap-1.5',
|
||||||
md: 'h-10 px-4 text-[11px] gap-2',
|
md: 'h-10 px-4 text-xs gap-2',
|
||||||
lg: 'h-12 px-6 text-[12px] gap-2',
|
lg: 'h-12 px-6 text-xs gap-2',
|
||||||
xl: 'h-14 px-8 text-[13px] gap-2.5',
|
xl: 'h-14 px-8 text-sm gap-2.5',
|
||||||
};
|
};
|
||||||
|
|
||||||
// Square padding for icon-only mode
|
// Square padding for icon-only mode
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ const displayLabel = $derived(label ?? controlLabel ?? '');
|
|||||||
step={control.step}
|
step={control.step}
|
||||||
orientation="horizontal"
|
orientation="horizontal"
|
||||||
/>
|
/>
|
||||||
<span class="font-mono text-[0.6875rem] text-secondary tabular-nums w-10 text-right shrink-0">
|
<span class="font-mono text-xs text-secondary tabular-nums w-10 text-right shrink-0">
|
||||||
{formattedValue()}
|
{formattedValue()}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -136,7 +136,7 @@ const displayLabel = $derived(label ?? controlLabel ?? '');
|
|||||||
{#if displayLabel}
|
{#if displayLabel}
|
||||||
<span
|
<span
|
||||||
class="
|
class="
|
||||||
text-[0.5625rem] font-primary font-bold tracking-tight uppercase
|
text-3xs font-primary font-bold tracking-tight uppercase
|
||||||
text-neutral-900 dark:text-neutral-100
|
text-neutral-900 dark:text-neutral-100
|
||||||
mb-0.5 leading-none
|
mb-0.5 leading-none
|
||||||
"
|
"
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ const { label, children, class: className }: Props = $props();
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class={cn('flex flex-col gap-3 py-6 border-b border-subtle last:border-0', className)}>
|
<div class={cn('flex flex-col gap-3 py-6 border-b border-subtle last:border-0', className)}>
|
||||||
<div class="flex justify-between items-center text-[0.6875rem] font-primary font-bold tracking-tight text-neutral-900 dark:text-neutral-100 uppercase leading-none">
|
<div class="flex justify-between items-center text-xs font-primary font-bold tracking-tight text-neutral-900 dark:text-neutral-100 uppercase leading-none">
|
||||||
{label}
|
{label}
|
||||||
</div>
|
</div>
|
||||||
{@render children?.()}
|
{@render children?.()}
|
||||||
|
|||||||
@@ -27,14 +27,14 @@ const { children, class: className, render }: Props = $props();
|
|||||||
{#if render}
|
{#if render}
|
||||||
{@render render({
|
{@render render({
|
||||||
class: cn(
|
class: cn(
|
||||||
'font-mono text-[0.5625rem] sm:text-[0.625rem] lowercase tracking-[0.2em] text-text-soft',
|
'font-mono text-3xs sm:text-2xs lowercase tracking-wider-mono text-text-soft',
|
||||||
className,
|
className,
|
||||||
),
|
),
|
||||||
})}
|
})}
|
||||||
{:else if children}
|
{:else if children}
|
||||||
<span
|
<span
|
||||||
class={cn(
|
class={cn(
|
||||||
'font-mono text-[0.5625rem] sm:text-[0.625rem] lowercase tracking-[0.2em] text-text-soft',
|
'font-mono text-3xs sm:text-2xs lowercase tracking-wider-mono text-text-soft',
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -148,7 +148,7 @@ const inputClasses = $derived(cn(
|
|||||||
{#if helperText}
|
{#if helperText}
|
||||||
<span
|
<span
|
||||||
class={cn(
|
class={cn(
|
||||||
'text-[0.625rem] font-mono tracking-wide px-1',
|
'text-2xs font-mono tracking-wide px-1',
|
||||||
error ? 'text-brand ' : 'text-secondary',
|
error ? 'text-brand ' : 'text-secondary',
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
import { cn } from '$shared/shadcn/utils/shadcn-utils';
|
import { cn } from '$shared/shadcn/utils/shadcn-utils';
|
||||||
import type { Snippet } from 'svelte';
|
import type { Snippet } from 'svelte';
|
||||||
import {
|
import {
|
||||||
|
type LabelFont,
|
||||||
type LabelSize,
|
type LabelSize,
|
||||||
type LabelVariant,
|
type LabelVariant,
|
||||||
labelSizeConfig,
|
labelSizeConfig,
|
||||||
@@ -28,6 +29,11 @@ interface Props {
|
|||||||
* @default true
|
* @default true
|
||||||
*/
|
*/
|
||||||
uppercase?: boolean;
|
uppercase?: boolean;
|
||||||
|
/**
|
||||||
|
* Font family
|
||||||
|
* @default 'mono'
|
||||||
|
*/
|
||||||
|
font?: LabelFont;
|
||||||
/**
|
/**
|
||||||
* Bold text
|
* Bold text
|
||||||
* @default false
|
* @default false
|
||||||
@@ -55,6 +61,7 @@ interface Props {
|
|||||||
let {
|
let {
|
||||||
variant = 'default',
|
variant = 'default',
|
||||||
size = 'sm',
|
size = 'sm',
|
||||||
|
font = 'mono',
|
||||||
uppercase = true,
|
uppercase = true,
|
||||||
bold = false,
|
bold = false,
|
||||||
icon,
|
icon,
|
||||||
@@ -68,6 +75,7 @@ let {
|
|||||||
class={cn(
|
class={cn(
|
||||||
'font-mono tracking-widest leading-none',
|
'font-mono tracking-widest leading-none',
|
||||||
'inline-flex items-center gap-1.5',
|
'inline-flex items-center gap-1.5',
|
||||||
|
font === 'primary' && 'font-primary tracking-tight',
|
||||||
labelSizeConfig[size],
|
labelSizeConfig[size],
|
||||||
labelVariantConfig[variant],
|
labelVariantConfig[variant],
|
||||||
uppercase && 'uppercase',
|
uppercase && 'uppercase',
|
||||||
|
|||||||
@@ -3,6 +3,8 @@
|
|||||||
* Import from here in each component to keep maps DRY.
|
* Import from here in each component to keep maps DRY.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
export type LabelFont = 'mono' | 'primary';
|
||||||
|
|
||||||
export type LabelVariant =
|
export type LabelVariant =
|
||||||
| 'default'
|
| 'default'
|
||||||
| 'accent'
|
| 'accent'
|
||||||
@@ -14,10 +16,10 @@ export type LabelVariant =
|
|||||||
export type LabelSize = 'xs' | 'sm' | 'md' | 'lg';
|
export type LabelSize = 'xs' | 'sm' | 'md' | 'lg';
|
||||||
|
|
||||||
export const labelSizeConfig: Record<LabelSize, string> = {
|
export const labelSizeConfig: Record<LabelSize, string> = {
|
||||||
xs: 'text-[0.5rem]',
|
xs: 'text-4xs',
|
||||||
sm: 'text-[0.5625rem] md:text-[0.625rem]',
|
sm: 'text-3xs md:text-2xs',
|
||||||
md: 'text-[0.625rem] md:text-[0.6875rem]',
|
md: 'text-2xs md:text-xs',
|
||||||
lg: 'text-[0.8rem] md:text-[0.875rem]',
|
lg: 'text-sm',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const labelVariantConfig: Record<LabelVariant, string> = {
|
export const labelVariantConfig: Record<LabelVariant, string> = {
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ let { size = 20, class: className = '', message = 'analyzing_data' }: Props = $p
|
|||||||
<div class="w-px h-3 bg-text-muted/50"></div>
|
<div class="w-px h-3 bg-text-muted/50"></div>
|
||||||
|
|
||||||
<!-- Message -->
|
<!-- Message -->
|
||||||
<span class="font-mono text-[10px] uppercase tracking-[0.2em] text-text-subtle font-medium">
|
<span class="font-mono text-2xs uppercase tracking-wider-mono text-text-subtle font-medium">
|
||||||
{message}
|
{message}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ const indexStr = $derived(String(index).padStart(2, '0'));
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{#if subtitle}
|
{#if subtitle}
|
||||||
<span class="text-neutral-300 dark:text-neutral-700 text-[0.625rem] hidden md:inline">/</span>
|
<span class="text-neutral-300 dark:text-neutral-700 text-2xs hidden md:inline">/</span>
|
||||||
<Label variant="muted" size="sm">{subtitle}</Label>
|
<Label variant="muted" size="sm">{subtitle}</Label>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ interface Props {
|
|||||||
const { text }: Props = $props();
|
const { text }: Props = $props();
|
||||||
</script>
|
</script>
|
||||||
{#if text}
|
{#if text}
|
||||||
<h2 class="text-3xl md:text-4xl lg:text-5xl font-['Space_Grotesk'] font-bold text-swiss-black dark:text-neutral-200 tracking-tight">
|
<h2 class="text-3xl md:text-4xl lg:text-5xl font-primary font-bold text-swiss-black dark:text-neutral-200 tracking-tight">
|
||||||
{text}
|
{text}
|
||||||
</h2>
|
</h2>
|
||||||
{/if}
|
{/if}
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ let {
|
|||||||
|
|
||||||
const isVertical = $derived(orientation === 'vertical');
|
const isVertical = $derived(orientation === 'vertical');
|
||||||
|
|
||||||
const labelClasses = `font-mono text-[0.625rem] tabular-nums shrink-0
|
const labelClasses = `font-mono text-2xs tabular-nums shrink-0
|
||||||
text-secondary
|
text-secondary
|
||||||
group-hover:text-neutral-700 dark:group-hover:text-neutral-300
|
group-hover:text-neutral-700 dark:group-hover:text-neutral-300
|
||||||
transition-colors`;
|
transition-colors`;
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ $effect(() => {
|
|||||||
<div class="flex-1 min-h-0 h-full">
|
<div class="flex-1 min-h-0 h-full">
|
||||||
<div class="py-2 relative flex flex-col min-h-0 h-full">
|
<div class="py-2 relative flex flex-col min-h-0 h-full">
|
||||||
<div class="py-2 mx-6 sticky border-b border-subtle">
|
<div class="py-2 mx-6 sticky border-b border-subtle">
|
||||||
<Label class="font-primary text-neutral-400" bold variant="default" size="sm" uppercase>
|
<Label font="primary" variant="muted" bold size="sm" uppercase>
|
||||||
Typeface Selection
|
Typeface Selection
|
||||||
</Label>
|
</Label>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -70,19 +70,21 @@ let {
|
|||||||
-->
|
-->
|
||||||
<ButtonGroup>
|
<ButtonGroup>
|
||||||
<ToggleButton
|
<ToggleButton
|
||||||
|
size="sm"
|
||||||
active={comparisonStore.side === 'A'}
|
active={comparisonStore.side === 'A'}
|
||||||
onclick={() => comparisonStore.side = 'A'}
|
onclick={() => comparisonStore.side = 'A'}
|
||||||
class="flex-1 tracking-wide font-bold uppercase text-[0.625rem]"
|
class="flex-1 tracking-wide font-bold uppercase"
|
||||||
>
|
>
|
||||||
<span>Left Font</span>
|
<span>Left Font</span>
|
||||||
</ToggleButton>
|
</ToggleButton>
|
||||||
|
|
||||||
<ToggleButton
|
<ToggleButton
|
||||||
class="flex-1 tracking-wide font-bold uppercase text-[0.625rem]"
|
size="sm"
|
||||||
|
class="flex-1 tracking-wide font-bold uppercase"
|
||||||
active={comparisonStore.side === 'B'}
|
active={comparisonStore.side === 'B'}
|
||||||
onclick={() => comparisonStore.side = 'B'}
|
onclick={() => comparisonStore.side = 'B'}
|
||||||
>
|
>
|
||||||
<span class="uppercase">Right Font</span>
|
<span>Right Font</span>
|
||||||
</ToggleButton>
|
</ToggleButton>
|
||||||
</ButtonGroup>
|
</ButtonGroup>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user