TYPOGRAPHY_ENGINE_RFC Phase 1. Additive on TypographyPrimitiveSet, behind the frozen
token contract (--font-size-X keeps its name; only the value formula changes, like
color --scale-* hex -> oklch()).
- type-scale.ts (pure, isomorphic, no canvas): the Utopia clamp() formula. fluidClamp /
resolveTypeSize / isFluidSize. rem-based (a11y: scales with browser font-zoom).
- config-types: TextMetric.size accepts `string | FluidSize` ({min,max,minVw?,maxVw?}).
Plain length strings still valid -> backward-compatible.
- render-css appendTypographyDeclarations: emits calc(resolveTypeSize(size) * --scaling)
-> a fixed rem or a fluid clamp; the --scaling axis composes on top.
- config.ts: validateSizeValue accepts a FluidSize (validates min/max/minVw/maxVw) so
the base config validates (was the cascade root — FluidSize objects failed the
string-only CSS-value check).
- typography.ts: sizes in rem; headings (lg/xl/xxl/xxxl) fluid (min @480px -> max
@1280px, max = previous fixed px so desktop is unchanged); body (md) fixed. hero/h1/h2
drop the manual { base, md } responsive sizes — the clamp covers the viewport.
- generated/base.css regenerated. type-scale.test.ts (6 tests). 2 config-test assertions
updated to the new rem/clamp values.
Verified in browser: --font-size-xxxl 40px @480 -> 80px @1280; xxl 32->48; lg 18->20;
md 16 fixed. check 0 errors; eidos suite green (3 pre-existing words failures only).
canvas-text/<SText> unaffected (reads getComputedStyle real font, measures the clamp).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
active-uix
parent
b273ecfb69
commit
667aa49331
@ -0,0 +1,41 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { fluidClamp, isFluidSize, resolveTypeSize } from './type-scale';
|
||||
|
||||
describe('type-scale (fluid)', () => {
|
||||
it('isFluidSize discriminates fluid descriptors from fixed strings', () => {
|
||||
expect(isFluidSize({ min: '1rem', max: '2rem' })).toBe(true);
|
||||
expect(isFluidSize('16px')).toBe(false);
|
||||
expect(isFluidSize(null)).toBe(false);
|
||||
expect(isFluidSize({ min: '1rem' })).toBe(false);
|
||||
});
|
||||
|
||||
it('computes the Utopia clamp (rem) for the default viewports', () => {
|
||||
// 40px @480 → 80px @1280: slope 0.05, slopeVw 5, intercept 16px = 1rem.
|
||||
expect(fluidClamp({ min: '2.5rem', max: '5rem' })).toBe(
|
||||
'clamp(2.5rem, calc(1rem + 5vw), 5rem)'
|
||||
);
|
||||
});
|
||||
|
||||
it('accepts px inputs and emits rem', () => {
|
||||
expect(fluidClamp({ min: '40px', max: '80px' })).toBe('clamp(2.5rem, calc(1rem + 5vw), 5rem)');
|
||||
});
|
||||
|
||||
it('honours custom viewports', () => {
|
||||
// 16px @320 → 24px @1024: slope ≈ 0.011364, slopeVw ≈ 1.1364,
|
||||
// intercept ≈ 12.3636px ≈ 0.7727rem.
|
||||
expect(fluidClamp({ min: '1rem', max: '1.5rem', minVw: '320px', maxVw: '1024px' })).toMatch(
|
||||
/^clamp\(1rem, calc\(0\.77\d*rem \+ 1\.13\d*vw\), 1\.5rem\)$/
|
||||
);
|
||||
});
|
||||
|
||||
it('collapses degenerate inputs to a fixed rem', () => {
|
||||
expect(fluidClamp({ min: '1.25rem', max: '1.25rem' })).toBe('1.25rem');
|
||||
});
|
||||
|
||||
it('resolveTypeSize: fixed passes through, fluid → clamp', () => {
|
||||
expect(resolveTypeSize('1rem')).toBe('1rem');
|
||||
expect(resolveTypeSize({ min: '2.5rem', max: '5rem' })).toBe(
|
||||
'clamp(2.5rem, calc(1rem + 5vw), 5rem)'
|
||||
);
|
||||
});
|
||||
});
|
||||
Loading…
Reference in new issue