From 45186647519248ebdbf672dc29ca50b0681a2a3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julio=20C=C3=A9sar=20Su=C3=A1stegui?= Date: Tue, 25 Aug 2026 10:33:29 -0600 Subject: [PATCH 1/2] fix(linter): keep YAML numeric lineHeight as a unitless multiplier --- packages/cli/src/linter/model/handler.test.ts | 12 ++++++++++++ packages/cli/src/linter/model/handler.ts | 19 +++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/packages/cli/src/linter/model/handler.test.ts b/packages/cli/src/linter/model/handler.test.ts index e402307..9758e38 100644 --- a/packages/cli/src/linter/model/handler.test.ts +++ b/packages/cli/src/linter/model/handler.test.ts @@ -465,6 +465,18 @@ describe('ModelHandler', () => { expect(result.findings[0]!.severity).toBe('error'); }); + it('keeps unquoted YAML numeric lineHeight as a unitless multiplier', () => { + const result = handler.execute(makeParsed({ + typography: { + 'body-md': { fontFamily: 'Inter', fontSize: '16px', fontWeight: 400, lineHeight: 1.6 }, + }, + })); + const body = result.designSystem.typography.get('body-md'); + expect(body?.lineHeight?.value).toBe(1.6); + expect(body?.lineHeight?.unit).toBe(''); + expect(result.findings.filter((f) => f.path.includes('lineHeight'))).toHaveLength(0); + }); + it('accepts string representations of numbers for fontWeight', () => { const result = handler.execute(makeParsed({ typography: { diff --git a/packages/cli/src/linter/model/handler.ts b/packages/cli/src/linter/model/handler.ts index 9ea1193..1ac6b29 100644 --- a/packages/cli/src/linter/model/handler.ts +++ b/packages/cli/src/linter/model/handler.ts @@ -377,6 +377,25 @@ function parseTypography(props: Record, path: string, f const dimensionProps = ['fontSize', 'lineHeight', 'letterSpacing'] as const; for (const prop of dimensionProps) { const raw = props[prop]; + if (typeof raw === 'number' && Number.isFinite(raw)) { + // YAML parses unquoted unitless values as numbers. lineHeight is a + // unitless multiplier in both DESIGN.md and DTCG; other dimension + // properties still require an explicit unit. + if (prop === 'lineHeight') { + result[prop] = { + type: 'dimension', + value: raw, + unit: '', + }; + } else { + findings.push({ + severity: 'error', + path: `${path}.${prop}`, + message: `'${raw}' is not a valid dimension. Include a unit (px, rem, or em).`, + }); + } + continue; + } if (typeof raw === 'string') { if (isParseableDimension(raw)) { const parsed = parseDimension(raw); From 552358b6bad34db392f0536838f9fcd5041ce6ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julio=20C=C3=A9sar=20Su=C3=A1stegui?= Date: Tue, 25 Aug 2026 10:38:32 -0600 Subject: [PATCH 2/2] test: narrow optional finding.path in lineHeight assertion --- packages/cli/src/linter/model/handler.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cli/src/linter/model/handler.test.ts b/packages/cli/src/linter/model/handler.test.ts index 9758e38..89ded11 100644 --- a/packages/cli/src/linter/model/handler.test.ts +++ b/packages/cli/src/linter/model/handler.test.ts @@ -474,7 +474,7 @@ describe('ModelHandler', () => { const body = result.designSystem.typography.get('body-md'); expect(body?.lineHeight?.value).toBe(1.6); expect(body?.lineHeight?.unit).toBe(''); - expect(result.findings.filter((f) => f.path.includes('lineHeight'))).toHaveLength(0); + expect(result.findings.filter((f) => f.path?.includes('lineHeight') === true)).toHaveLength(0); }); it('accepts string representations of numbers for fontWeight', () => {