You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
svelte-kit-vice/web/routes/uix/icon-scale-study/+page.svelte

247 lines
6.5 KiB

<script lang="ts">
// Icon ↔ typography relationship study. For each step of the type scale we
// pair the REAL text (at its font-size) with an icon sized by each candidate
// model, so the optical balance can be judged by eye — not by formula.
//
// Fluid steps (lg/xl/xxl/xxxl) use their DESKTOP max here.
type Step = { name: string; font: number; lh: number };
const steps: Step[] = [
{ name: 'xxs', font: 10, lh: 15 },
{ name: 'xs', font: 12, lh: 18 },
{ name: 'sm', font: 14, lh: 20 },
{ name: 'md', font: 16, lh: 23 },
{ name: 'lg', font: 20, lh: 27 },
{ name: 'xl', font: 28, lh: 34 },
{ name: 'xxl', font: 48, lh: 50 }
];
// Today's icon scale (what ships now).
const CURRENT: Record<string, number> = {
xxs: 12,
xs: 14,
sm: 16,
md: 18,
lg: 20,
xl: 24,
xxl: 48
};
const grid4 = (n: number) => Math.round(n / 4) * 4;
type Model = { key: string; label: string; px: (s: Step) => number };
const models: Model[] = [
{ key: 'today', label: 'today', px: (s) => CURRENT[s.name] },
{ key: 'plus2', label: 'font + 2', px: (s) => s.font + 2 },
{ key: 'plus4', label: 'font + 4', px: (s) => s.font + 4 },
{ key: 'lh', label: 'line-height → grid-4', px: (s) => grid4(s.lh) }
];
let icon = $state<'house' | 'bell' | 'gear'>('house');
const icons = ['house', 'bell', 'gear'] as const;
const paths: Record<string, string> = {
house: 'M3 9.5 12 3l9 6.5V21a1 1 0 0 1-1 1h-5v-7H9v7H4a1 1 0 0 1-1-1z',
bell: 'M6 8a6 6 0 0 1 12 0c0 7 3 9 3 9H3s3-2 3-9 M9.5 21a2.5 2.5 0 0 0 5 0',
gear: 'M12 15a3 3 0 1 0 0-6 3 3 0 0 0 0 6z M19.4 13a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-2.82 1.17V22a2 2 0 1 1-4 0v-.09A1.65 1.65 0 0 0 7 20.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 3.6 15a1.65 1.65 0 0 0-1.51-1H2a2 2 0 1 1 0-4h.09A1.65 1.65 0 0 0 3.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.6a1.65 1.65 0 0 0 1-1.51V3a2 2 0 1 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9c.2.61.78 1 1.42 1H22a2 2 0 1 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z'
};
</script>
{#snippet glyph(px: number)}
<svg
width={px}
height={px}
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
style="flex: 0 0 auto;"
>
<path d={paths[icon]} />
</svg>
{/snippet}
<div class="study">
<header>
<h1>Icon ↔ type relationship study</h1>
<p>
Each row pairs the real text (at its <code>font-size</code>) with the icon sized by each
model. Judge by eye: at every size, which icon sits in balance with the text — neither
shrunken nor overpowering? The answer may differ small vs large (e.g. <code>+2</code> low,
<code>line-height</code> high).
</p>
<div class="picker">
<span>glyph</span>
{#each icons as i}
<button class:active={icon === i} onclick={() => (icon = i)}>{i}</button>
{/each}
</div>
<div class="legend">
{#each models as m}<span class="chip {m.key}">{m.label}</span>{/each}
</div>
</header>
{#each steps as s}
<section class="step">
<div class="meta">
<strong>{s.name}</strong>
<span>font {s.font}px</span>
<span>line-height {s.lh}px</span>
</div>
<div class="cells">
{#each models as m}
{@const px = m.px(s)}
<div class="cell {m.key}">
<div class="sample" style="font-size: {s.font}px;">
{@render glyph(px)}
<span>Texto</span>
</div>
<div class="caption"><span class="dot {m.key}"></span>{m.label} · {px}px</div>
</div>
{/each}
</div>
</section>
{/each}
</div>
<style>
.study {
max-inline-size: 1100px;
margin-inline: auto;
padding: 2rem 1.5rem 5rem;
color: var(--color-content-primary);
}
header h1 {
font-size: 1.75rem;
font-weight: 600;
margin: 0 0 0.5rem;
}
header p {
color: var(--color-content-subtle);
max-inline-size: 70ch;
line-height: 1.55;
margin: 0 0 1rem;
}
code {
font-family: var(--font-mono, monospace);
font-size: 0.85em;
background: var(--color-surface-muted);
padding: 0.1em 0.35em;
border-radius: 4px;
}
.picker {
display: inline-flex;
gap: 0.35rem;
align-items: center;
margin-bottom: 0.75rem;
font-size: 0.8rem;
color: var(--color-content-subtle);
}
.picker button {
font: inherit;
padding: 0.2rem 0.6rem;
border-radius: 6px;
border: 1px solid var(--color-border-default);
background: var(--color-surface-default);
color: var(--color-content-subtle);
cursor: pointer;
}
.picker button.active {
background: var(--color-primary-solid);
color: var(--color-primary-contrast);
border-color: transparent;
}
.legend {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
font-size: 0.75rem;
margin-bottom: 1.5rem;
}
.chip {
padding: 0.15rem 0.5rem;
border-radius: 999px;
border: 1px solid var(--color-border-subtle);
color: var(--color-content-subtle);
}
.step {
border-top: 1px solid var(--color-border-subtle);
padding: 1.5rem 0;
}
.meta {
display: flex;
gap: 1rem;
align-items: baseline;
font-size: 0.8rem;
color: var(--color-content-subtle);
margin-bottom: 1rem;
font-family: var(--font-mono, monospace);
}
.meta strong {
font-size: 1rem;
color: var(--color-content-primary);
}
.cells {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
gap: 1rem;
align-items: end;
}
.cell {
background: var(--color-surface-raised);
border: 1px solid var(--color-border-subtle);
border-radius: 10px;
padding: 1rem;
overflow: hidden;
}
.sample {
display: flex;
align-items: center;
gap: 0.5em;
line-height: 1.2;
color: var(--color-content-primary);
white-space: nowrap;
}
.caption {
display: flex;
align-items: center;
gap: 0.4rem;
margin-top: 0.75rem;
font-size: 0.72rem;
color: var(--color-content-subtle);
font-family: var(--font-mono, monospace);
}
.dot {
inline-size: 8px;
block-size: 8px;
border-radius: 999px;
flex: 0 0 auto;
}
.dot.today {
background: var(--color-neutral-solid);
}
.dot.plus2 {
background: var(--color-affirm-solid);
}
.dot.plus4 {
background: var(--color-primary-solid);
}
.dot.lh {
background: var(--color-risk-solid);
}
.cell.today {
border-color: color-mix(in srgb, var(--color-neutral-solid) 35%, transparent);
}
.cell.plus2 {
border-color: color-mix(in srgb, var(--color-affirm-solid) 35%, transparent);
}
.cell.plus4 {
border-color: color-mix(in srgb, var(--color-primary-solid) 35%, transparent);
}
.cell.lh {
border-color: color-mix(in srgb, var(--color-risk-solid) 35%, transparent);
}
</style>

Powered by TurnKey Linux.