diff --git a/RELEASE-QUALIFICATION.md b/RELEASE-QUALIFICATION.md index c8b65d6..2e00b35 100644 --- a/RELEASE-QUALIFICATION.md +++ b/RELEASE-QUALIFICATION.md @@ -18,6 +18,16 @@ This milestone qualifies the public kit and its private workbench consumer for r - Hosted evidence covers the full current qualification request, authentication, and same-origin font loading. A single-widget adapter check is insufficient. - Published versions, Git commits, artifact checksums, remote CI, and local CLI version are reported separately. +## Current mathematical baseline + +The audited layout model records each component's declared text behavior: single line, wrapping, wrapping anywhere, or managed scrolling. It retains the full single-line estimate and checks every requested width. Wrapping controls use their authored minimum block size; an explicit height or line cap still applies when declared. + +The built CLI passes all 180 fixtures for `qualification/original-project.json` and all 720 fixtures across the 152 component-theme matrix runs. Both strict commands exit successfully. `qualification/reviewed-math-policy.json` pins the exact case inventory, width verdicts, layout assumptions, run results, and totals. The release guard rejects new failures, changed case inputs, incomplete evidence, or an exit status that disagrees with the artifact. + +The previous 17 project and 45 matrix findings remain in `qualification/reviewed-math-policy-20261004.json` as historical results from the earlier single-line model. Current passing mathematical estimates do not establish rendered behavior or accessibility conformance. + +Run `pnpm check:release:math` after building the CLI. Run the direct guard regressions with `node --test scripts/assess-release-math.test.mjs` against the same build. + ## Ownership - CLI review: public math, fixture contracts, receipt contracts, and CLI. diff --git a/docs/guides/proof-driven-design.md b/docs/guides/proof-driven-design.md index ed3e634..95f8f53 100644 --- a/docs/guides/proof-driven-design.md +++ b/docs/guides/proof-driven-design.md @@ -33,6 +33,8 @@ dk audit --css app.css --json --strict > audit.json Component fixtures evaluate each requested layout width and declared color-distinctness check. Coverage reports declared, evaluated, and unsupported proof kinds. A passing wide layout does not cancel a failing narrow layout. +Layout defaults to a single line. Recipes can declare `textBehavior` as `wrap`, `wrap-anywhere`, or `scroll` when the component supports that behavior. The results retain the full single-line estimate and report the minimum inline space required after padding, icons, and other reserved controls. Wrapped estimates also report line count and height at each width. Explicit `heights` and `maxLines` budgets can still fail; a control with a minimum height and natural block growth can omit those limits. Scrolling checks the text viewport, not full value visibility. + ## Interpret the result Mathematical evidence includes the following results: diff --git a/examples/sveltekit-starter/tests/desk.spec.js b/examples/sveltekit-starter/tests/desk.spec.js index 34bf825..4155a35 100644 --- a/examples/sveltekit-starter/tests/desk.spec.js +++ b/examples/sveltekit-starter/tests/desk.spec.js @@ -6,12 +6,34 @@ test('public components hydrate and complete release and theme flows', async ({ page.on('pageerror', (error) => errors.push(error.message)); await page.goto('/'); await expect(page.getByRole('heading', { name: 'Releases', exact: true })).toBeVisible(); - await page.getByRole('button', { name: 'Create release' }).click(); - await expect(page.getByText('Correct the marked fields.')).toBeVisible(); - await page.getByRole('textbox', { name: 'Release name', exact: true }).fill('Browser release'); - await page.getByRole('textbox', { name: 'Release owner', exact: true }).fill('Rafi'); - await page.getByRole('button', { name: 'Create release' }).click(); + const releaseName = page.getByRole('textbox', { name: 'Release name', exact: true }); + const releaseOwner = page.getByRole('textbox', { name: 'Release owner', exact: true }); + const createRelease = page.getByRole('button', { name: 'Create release' }); + const initialRows = await page.locator('tbody tr').allTextContents(); + /** @type {string[]} */ + const createRequests = []; + page.on('request', (request) => { + if (request.method() === 'POST' && new URL(request.url()).search === '?/create') { + createRequests.push(request.url()); + } + }); + await createRelease.click(); + await expect(releaseName).toBeFocused(); + expect(await releaseName.evaluate((input) => input instanceof HTMLInputElement && input.validity.valueMissing)).toBe(true); + expect(await releaseOwner.evaluate((input) => input instanceof HTMLInputElement && input.validity.valueMissing)).toBe(true); + expect(createRequests).toEqual([]); + expect(await page.locator('tbody tr').allTextContents()).toEqual(initialRows); + await releaseName.fill('Browser release'); + await createRelease.click(); + await expect(releaseOwner).toBeFocused(); + await expect(releaseName).toHaveValue('Browser release'); + expect(await releaseOwner.evaluate((input) => input instanceof HTMLInputElement && input.validity.valueMissing)).toBe(true); + expect(createRequests).toEqual([]); + expect(await page.locator('tbody tr').allTextContents()).toEqual(initialRows); + await releaseOwner.fill('Rafi'); + await createRelease.click(); await expect(page.getByRole('status')).toHaveText('Created Browser release.'); + expect(createRequests).toHaveLength(1); await page.getByRole('row').filter({ hasText: 'Browser release' }).getByRole('checkbox').check(); await page.getByRole('button', { name: 'Review release' }).click(); await expect(page.getByRole('dialog')).toBeVisible(); diff --git a/package.json b/package.json index 1471271..79233b6 100644 --- a/package.json +++ b/package.json @@ -92,7 +92,7 @@ "preflight": "pnpm lint && pnpm check:strict && pnpm test && pnpm test:coverage", "verify": "pnpm release:verify", "check:component-targets": "node ./scripts/check-component-targets.mjs && node ./scripts/check-component-narrow-layout.mjs", - "check:release:math": "node ./scripts/assess-release-math.mjs" + "check:release:math": "node --test ./scripts/assess-release-math.test.mjs && node ./scripts/assess-release-math.mjs" }, "devDependencies": { "@changesets/cli": "^2.29.6", diff --git a/packages/components/src/lib/accordion/accordion.recipe.test.ts b/packages/components/src/lib/accordion/accordion.recipe.test.ts index 0836df1..5896d63 100644 --- a/packages/components/src/lib/accordion/accordion.recipe.test.ts +++ b/packages/components/src/lib/accordion/accordion.recipe.test.ts @@ -3,10 +3,10 @@ import { describe, expect, it } from 'vitest'; import { createTheme } from '@dkcli/tokens'; import { createAccordionRegistration, getAccordionRecipeCase } from './accordion.recipe.js'; -import { expectKnownLayoutFailures } from '../test-utils/proof-expectations.js'; +import { DECLARED_LAYOUT_BEHAVIORS, expectDeclaredLayoutBehavior } from '../test-utils/proof-expectations.js'; describe('accordion recipe', () => { - it('compiles light and dark recipe cases and reports the known narrow-width estimate', () => { + it('compiles light and dark cases with declared panel wrapping and height budget', () => { const light = createTheme({ name: 'accordion-light', seed: { @@ -31,7 +31,7 @@ describe('accordion recipe', () => { for (const theme of [light, dark]) { const registration = createAccordionRegistration(theme); expect(getAccordionRecipeCase(registration.recipe, { size: 'md' }).caseKey).toContain('size=md'); - expectKnownLayoutFailures(registration.recipe.proofFixtures, 2, [{ name: 'accordion-md-open', widths: [320] }]); + expectDeclaredLayoutBehavior(registration.recipe.proofFixtures, 2, DECLARED_LAYOUT_BEHAVIORS.accordion); } }); }); diff --git a/packages/components/src/lib/accordion/accordion.spec.ts b/packages/components/src/lib/accordion/accordion.spec.ts index 2c6f38a..94ed636 100644 --- a/packages/components/src/lib/accordion/accordion.spec.ts +++ b/packages/components/src/lib/accordion/accordion.spec.ts @@ -135,11 +135,11 @@ export const accordionSpec: ComponentSpec = createComponentSpec({ widths: [320, 420], heights: [160], noOverflow: true, + textBehavior: 'wrap-anywhere', + lineHeight: 1.6, blockSize: literal('128px'), inlinePadding: slotVar('panel', '--dk-accordion-panel-padding'), - gap: slotVar('trigger', '--dk-accordion-trigger-gap'), - labelFontSize: slotVar('trigger', '--dk-accordion-trigger-size'), - iconSize: slotVar('indicator', '--dk-accordion-indicator-size') + labelFontSize: slotVar('panel', '--dk-accordion-panel-size') } }, proofCases: [ diff --git a/packages/components/src/lib/badge/badge.recipe.test.ts b/packages/components/src/lib/badge/badge.recipe.test.ts index c52000b..cba5d3c 100644 --- a/packages/components/src/lib/badge/badge.recipe.test.ts +++ b/packages/components/src/lib/badge/badge.recipe.test.ts @@ -3,7 +3,7 @@ import { describe, expect, it } from 'vitest'; import { createTheme } from '@dkcli/tokens'; import { createBadgeRegistration, getBadgeRecipeCase } from './badge.recipe.js'; -import { BADGE_LAYOUT_FAILURES, expectKnownLayoutFailures } from '../test-utils/proof-expectations.js'; +import { DECLARED_LAYOUT_BEHAVIORS, expectDeclaredLayoutBehavior } from '../test-utils/proof-expectations.js'; describe('badge recipe', () => { it('compiles all tone, emphasis, and size cases across light and dark themes', () => { @@ -42,7 +42,7 @@ describe('badge recipe', () => { expect(compiledCase.slots.root.baseVars['--dk-badge-bg']).toMatch(/^#/); expect(compiledCase.slots.label.baseVars['--dk-badge-label-size']).toContain('clamp'); for (const registration of [lightRegistration, darkRegistration]) { - expectKnownLayoutFailures(registration.recipe.proofFixtures, 15, BADGE_LAYOUT_FAILURES); + expectDeclaredLayoutBehavior(registration.recipe.proofFixtures, 15, DECLARED_LAYOUT_BEHAVIORS.badge); } }); }); diff --git a/packages/components/src/lib/badge/badge.spec.ts b/packages/components/src/lib/badge/badge.spec.ts index 00a9012..8aff186 100644 --- a/packages/components/src/lib/badge/badge.spec.ts +++ b/packages/components/src/lib/badge/badge.spec.ts @@ -177,8 +177,10 @@ export const badgeSpec: ComponentSpec = createComponentSpec({ layout: { target: 'root', widths: [120, 200], - heights: [30], + // The badge has a minimum block size and grows when its label wraps. noOverflow: true, + textBehavior: 'wrap-anywhere', + lineHeight: 1, blockSize: slotVar('root', '--dk-badge-block-size'), inlinePadding: slotVar('root', '--dk-badge-inline-padding'), gap: slotVar('root', '--dk-badge-gap'), diff --git a/packages/components/src/lib/breadcrumbs/breadcrumbs.recipe.test.ts b/packages/components/src/lib/breadcrumbs/breadcrumbs.recipe.test.ts index d830aa9..4f27d83 100644 --- a/packages/components/src/lib/breadcrumbs/breadcrumbs.recipe.test.ts +++ b/packages/components/src/lib/breadcrumbs/breadcrumbs.recipe.test.ts @@ -3,10 +3,10 @@ import { describe, expect, it } from 'vitest'; import { createTheme } from '@dkcli/tokens'; import { createBreadcrumbsRegistration, getBreadcrumbsRecipeCase } from './breadcrumbs.recipe.js'; -import { expectKnownLayoutFailures } from '../test-utils/proof-expectations.js'; +import { DECLARED_LAYOUT_BEHAVIORS, expectDeclaredLayoutBehavior } from '../test-utils/proof-expectations.js'; describe('breadcrumbs recipe', () => { - it('compiles light and dark breadcrumb cases and reports the known narrow-width estimate', () => { + it('compiles light and dark cases with declared wrapping across breadcrumb items', () => { const themes = [ createTheme({ name: 'breadcrumbs-light', @@ -33,7 +33,7 @@ describe('breadcrumbs recipe', () => { for (const theme of themes) { const registration = createBreadcrumbsRegistration(theme); expect(getBreadcrumbsRecipeCase(registration.recipe, { size: 'md' }).caseKey).toContain('size=md'); - expectKnownLayoutFailures(registration.recipe.proofFixtures, 1, [{ name: 'breadcrumbs-md', widths: [220] }]); + expectDeclaredLayoutBehavior(registration.recipe.proofFixtures, 1, DECLARED_LAYOUT_BEHAVIORS.breadcrumbs); } }); }); diff --git a/packages/components/src/lib/breadcrumbs/breadcrumbs.spec.ts b/packages/components/src/lib/breadcrumbs/breadcrumbs.spec.ts index e3c9aa3..e3ab4f5 100644 --- a/packages/components/src/lib/breadcrumbs/breadcrumbs.spec.ts +++ b/packages/components/src/lib/breadcrumbs/breadcrumbs.spec.ts @@ -89,8 +89,11 @@ export const breadcrumbsSpec: ComponentSpec = createComponentSpec({ layout: { target: 'root', widths: [220, 320], - heights: [48], + // The item row wraps naturally; its minimum block size is not a height cap. noOverflow: true, + textBehavior: 'wrap', + // Two delivered separators reserve at most 12px each in the declared sample. + reservedInlineSize: literal(24), blockSize: slotVar('item', '--dk-breadcrumbs-item-block-size'), inlinePadding: slotVar('item', '--dk-breadcrumbs-item-inline-padding'), gap: slotVar('root', '--dk-breadcrumbs-gap'), diff --git a/packages/components/src/lib/button/button.recipe.test.ts b/packages/components/src/lib/button/button.recipe.test.ts index 4d20f12..0c38f08 100644 --- a/packages/components/src/lib/button/button.recipe.test.ts +++ b/packages/components/src/lib/button/button.recipe.test.ts @@ -3,7 +3,7 @@ import { describe, expect, it } from 'vitest'; import { createTheme } from '@dkcli/tokens'; import { createButtonRegistration, getButtonRecipeCase } from './button.recipe.js'; -import { BUTTON_LAYOUT_FAILURES, expectKnownLayoutFailures } from '../test-utils/proof-expectations.js'; +import { DECLARED_LAYOUT_BEHAVIORS, expectDeclaredLayoutBehavior } from '../test-utils/proof-expectations.js'; describe('button recipe', () => { it('compiles the full button case matrix for light and dark themes', () => { @@ -51,12 +51,12 @@ describe('button recipe', () => { expect(darkCase.slots.root.stateVars.hover?.['--dk-button-bg']).toMatch(/^#/); }); - it('preserves curated fixtures and reports the two narrow-width mathematical failures', () => { + it('preserves curated fixtures with declared label wrapping and natural block growth', () => { const registration = createButtonRegistration(); expect(registration.recipe.proofFixtures.length).toBeGreaterThan(0); expect(registration.recipe.proofFixtures.every((fixture) => fixture.resolved)).toBe(true); - expectKnownLayoutFailures(registration.recipe.proofFixtures, 13, BUTTON_LAYOUT_FAILURES); + expectDeclaredLayoutBehavior(registration.recipe.proofFixtures, 13, DECLARED_LAYOUT_BEHAVIORS.button); expect(registration.recipe.proofFixtures.some((fixture) => fixture.props.as === 'a')).toBe(true); expect(registration.recipe.proofFixtures.some((fixture) => fixture.states.includes('loading'))).toBe(true); }); diff --git a/packages/components/src/lib/button/button.spec.ts b/packages/components/src/lib/button/button.spec.ts index 4b4f63e..b461433 100644 --- a/packages/components/src/lib/button/button.spec.ts +++ b/packages/components/src/lib/button/button.spec.ts @@ -354,8 +354,10 @@ export const buttonSpec: ComponentSpec = createComponentSpec({ layout: { target: 'root', widths: [180, 240, 320], - heights: [44, 48, 52], + // Size presets define minimum block sizes; complete labels may add lines. noOverflow: true, + textBehavior: 'wrap-anywhere', + lineHeight: 1.1, inlinePadding: slotVar('root', '--dk-button-inline-padding'), gap: slotVar('root', '--dk-button-gap'), labelFontSize: slotVar('label', '--dk-button-label-font-size'), diff --git a/packages/components/src/lib/checkbox/Checkbox.svelte b/packages/components/src/lib/checkbox/Checkbox.svelte index 9bc640f..4ce413b 100644 --- a/packages/components/src/lib/checkbox/Checkbox.svelte +++ b/packages/components/src/lib/checkbox/Checkbox.svelte @@ -85,6 +85,7 @@ {name} id={fieldId} bind:checked + {required} {disabled} aria-label={label} aria-checked={indeterminate ? 'mixed' : checked ? 'true' : 'false'} diff --git a/packages/components/src/lib/checkbox/checkbox.recipe.test.ts b/packages/components/src/lib/checkbox/checkbox.recipe.test.ts index 3728f05..4deeadc 100644 --- a/packages/components/src/lib/checkbox/checkbox.recipe.test.ts +++ b/packages/components/src/lib/checkbox/checkbox.recipe.test.ts @@ -1,7 +1,7 @@ import { describe, expect, it } from 'vitest'; import { createCheckboxRegistration, getCheckboxRecipeCase } from './checkbox.recipe.js'; -import { CHECKBOX_LAYOUT_FAILURES, expectKnownLayoutFailures } from '../test-utils/proof-expectations.js'; +import { DECLARED_LAYOUT_BEHAVIORS, expectDeclaredLayoutBehavior } from '../test-utils/proof-expectations.js'; describe('checkbox recipe', () => { it('compiles checkbox cases and proof fixtures', () => { @@ -10,6 +10,6 @@ describe('checkbox recipe', () => { expect(Object.keys(registration.recipe.cases)).toHaveLength(3); const compiledCase = getCheckboxRecipeCase(registration.recipe, { size: 'md' }); expect(compiledCase.slots.control.baseVars['--dk-checkbox-bg']).toMatch(/^#/); - expectKnownLayoutFailures(registration.recipe.proofFixtures, 9, CHECKBOX_LAYOUT_FAILURES); + expectDeclaredLayoutBehavior(registration.recipe.proofFixtures, 9, DECLARED_LAYOUT_BEHAVIORS.checkbox); }); }); diff --git a/packages/components/src/lib/checkbox/checkbox.spec.ts b/packages/components/src/lib/checkbox/checkbox.spec.ts index 630b598..c66d9d4 100644 --- a/packages/components/src/lib/checkbox/checkbox.spec.ts +++ b/packages/components/src/lib/checkbox/checkbox.spec.ts @@ -140,8 +140,12 @@ export const checkboxSpec: ComponentSpec = createComponentSpec({ layout: { target: 'root', widths: [240, 320], - heights: [44], + // The 44px hit-size minimum does not cap a multiline label's height. noOverflow: true, + textBehavior: 'wrap', + lineHeight: 1.35, + // The largest 22px control plus both 1.5px borders is persistent chrome. + reservedInlineSize: literal(25), blockSize: slotVar('root', '--dk-checkbox-hit-size'), gap: slotVar('root', '--dk-checkbox-gap'), labelFontSize: slotVar('label', '--dk-checkbox-label-size') diff --git a/packages/components/src/lib/combobox/Combobox.svelte b/packages/components/src/lib/combobox/Combobox.svelte index 31306c1..17f137d 100644 --- a/packages/components/src/lib/combobox/Combobox.svelte +++ b/packages/components/src/lib/combobox/Combobox.svelte @@ -20,6 +20,7 @@ type Placement } from '../internal/behavior/index.js'; import { FieldFrame } from '../primitives/index.js'; + import { requiredSelection } from '../internal/behavior/required-selection.js'; import { DEFAULT_COMBOBOX_THEME, createComboboxRegistration, @@ -91,6 +92,12 @@ if (disabled) internalOpen = false; }); + $effect(() => { + if (internalOpen && (!filteredItems[highlightIndex] || filteredItems[highlightIndex].disabled)) { + highlightIndex = firstEnabledIndex(filteredItems); + } + }); + async function syncPosition(): Promise { await tick(); if (!internalOpen || !inputEl || !surfaceEl) return; @@ -204,6 +211,7 @@
document.querySelectorAll('[data-test-outside]').forEach((element) => element.remove())); describe('Combobox', () => { + it('updates the active option when asynchronous results replace the list', async () => { + const onChange = vi.fn(); + const { rerender } = render(Combobox, { props: { items: [{ value: 'one', label: 'One' }, { value: 'two', label: 'Two' }], onChange } }); + const input = screen.getByRole('combobox'); + input.focus(); + await fireEvent.keyDown(input, { key: 'End' }); + await rerender({ items: [{ value: 'three', label: 'Three' }] }); + const option = screen.getByRole('option', { name: 'Three' }); + expect(input.getAttribute('aria-activedescendant')).toBe(option.id); + await fireEvent.keyDown(input, { key: 'Enter' }); + expect(onChange).toHaveBeenCalledExactlyOnceWith({ value: 'three' }); + }); + const items = [ { value: 'staging', label: 'Staging' }, { value: 'production', label: 'Production' } diff --git a/packages/components/src/lib/command-palette/Combobox.svelte.test.ts b/packages/components/src/lib/command-palette/Combobox.svelte.test.ts index 86c1d6e..dd71cda 100644 --- a/packages/components/src/lib/command-palette/Combobox.svelte.test.ts +++ b/packages/components/src/lib/command-palette/Combobox.svelte.test.ts @@ -1,11 +1,71 @@ import { fireEvent, render, screen } from '@testing-library/svelte'; -import { describe, expect, it, vi } from 'vitest'; +import { afterEach, describe, expect, it, vi } from 'vitest'; import { tick } from 'svelte'; import CommandPalette from './CommandPalette.svelte'; import CommandPaletteFocusHarness from './CommandPaletteFocusHarness.svelte'; +afterEach(() => { + document.querySelectorAll('[data-command-outside]').forEach((element) => element.remove()); + vi.restoreAllMocks(); +}); + describe('CommandPalette', () => { + it('keeps a usable highlighted command after a controlled query narrows results', async () => { + const onAction = vi.fn(); + const { rerender } = render(CommandPalette, { props: { open: true, items: [{ id: 'one', label: 'One' }, { id: 'two', label: 'Two' }], onAction } }); + await tick(); + const input = screen.getByRole('combobox'); + await fireEvent.keyDown(input, { key: 'End' }); + await rerender({ query: 'One' }); + input.focus(); + await fireEvent.keyDown(input, { key: 'Enter' }); + expect(onAction).toHaveBeenCalledExactlyOnceWith({ id: 'one' }); + }); + + it('focuses an initially open palette and traps Tab inside its modal surface', async () => { + render(CommandPalette, { props: { open: true, items: [{ id: 'first', label: 'First' }, { id: 'last', label: 'Last' }] } }); + await tick(); + const input = screen.getByRole('combobox'); + expect(document.activeElement).toBe(input); + await fireEvent.keyDown(input, { key: 'Tab', shiftKey: true }); + const last = screen.getByRole('option', { name: 'Last' }); + expect(document.activeElement).toBe(last); + await fireEvent.keyDown(last, { key: 'Tab' }); + expect(document.activeElement).toBe(input); + }); + + it('dismisses after an outside click without stealing its new focus', async () => { + let now = 0; + vi.spyOn(performance, 'now').mockImplementation(() => now); + render(CommandPaletteFocusHarness); + const opener = screen.getByRole('button', { name: 'Open commands' }); + opener.focus(); + await fireEvent.click(opener); + await tick(); + now = 200; + const outside = document.createElement('button'); + outside.dataset.commandOutside = 'true'; + document.body.append(outside); + outside.focus(); + await fireEvent.click(outside); + await tick(); + expect(screen.queryByRole('dialog')).toBeNull(); + expect(document.activeElement).toBe(outside); + }); + + it('keeps Tab navigation inside an open modal palette', async () => { + render(CommandPalette, { props: { open: true, items: [{ id: 'last', label: 'Last' }] } }); + await tick(); + const input = screen.getByRole('combobox'); + input.focus(); + await fireEvent.keyDown(input, { key: 'Tab', shiftKey: true }); + const last = screen.getByRole('option', { name: 'Last' }); + expect(document.activeElement).toBe(last); + await fireEvent.keyDown(last, { key: 'Tab' }); + expect(document.activeElement).toBe(input); + }); + it('returns focus to the external opener after Escape closes a bound palette', async () => { render(CommandPaletteFocusHarness); diff --git a/packages/components/src/lib/command-palette/CommandPalette.svelte b/packages/components/src/lib/command-palette/CommandPalette.svelte index 8016704..90c6756 100644 --- a/packages/components/src/lib/command-palette/CommandPalette.svelte +++ b/packages/components/src/lib/command-palette/CommandPalette.svelte @@ -11,7 +11,7 @@ @@ -260,7 +266,8 @@ descriptionStyle={slotStyles.description} errorStyle={slotStyles.error} > - + + - + +
{:else} updateDraft((event.currentTarget as HTMLInputElement).value)} - onblur={commitValue} + onblur={() => commitValue()} onkeydown={handleInputKeydown} /> {/if} diff --git a/packages/components/src/lib/inline-edit/TextField.svelte.test.ts b/packages/components/src/lib/inline-edit/TextField.svelte.test.ts index 81aa88f..9c15f31 100644 --- a/packages/components/src/lib/inline-edit/TextField.svelte.test.ts +++ b/packages/components/src/lib/inline-edit/TextField.svelte.test.ts @@ -4,6 +4,25 @@ import { describe, expect, it, vi } from 'vitest'; import InlineEdit from './InlineEdit.svelte'; describe('InlineEdit', () => { + it('disables editing actions when a multiline edit becomes disabled', async () => { + const onCommit = vi.fn(); + const onCancel = vi.fn(); + const { rerender } = render(InlineEdit, { props: { value: 'Original', multiline: true, onCommit, onCancel } }); + await fireEvent.click(screen.getByRole('button', { name: 'Original' })); + await fireEvent.input(screen.getByRole('textbox'), { target: { value: 'Draft' } }); + await rerender({ disabled: true }); + expect((screen.getByRole('button', { name: 'Save' }) as HTMLButtonElement).disabled).toBe(true); + expect((screen.getByRole('button', { name: 'Cancel' }) as HTMLButtonElement).disabled).toBe(true); + await fireEvent.click(screen.getByRole('button', { name: 'Save' })); + expect(onCommit).not.toHaveBeenCalled(); + expect(onCancel).not.toHaveBeenCalled(); + expect((screen.getByRole('textbox') as HTMLTextAreaElement).value).toBe('Draft'); + await rerender({ disabled: false }); + await fireEvent.click(screen.getByRole('button', { name: 'Save' })); + expect(onCommit).toHaveBeenCalledExactlyOnceWith({ value: 'Draft' }); + expect(document.activeElement).toBe(screen.getByRole('button', { name: 'Draft' })); + }); + it('enters edit mode and commits a changed value', async () => { const onCommit = vi.fn(); render(InlineEdit, { diff --git a/packages/components/src/lib/internal/behavior/required-selection.ts b/packages/components/src/lib/internal/behavior/required-selection.ts new file mode 100644 index 0000000..8182465 --- /dev/null +++ b/packages/components/src/lib/internal/behavior/required-selection.ts @@ -0,0 +1,17 @@ +import type { Action } from 'svelte/action'; + +type RequiredSelectionState = { + required: boolean; + disabled: boolean; + value: string | undefined; +}; + +/** Validates the committed value, independently of editable search text. */ +export const requiredSelection: Action = (input, state) => { + function update(next: RequiredSelectionState): void { + input.setCustomValidity(next.required && !next.disabled && !next.value ? 'Select an option.' : ''); + } + + update(state); + return { update, destroy: () => input.setCustomValidity('') }; +}; diff --git a/packages/components/src/lib/layout-estimates.browser.test.mjs b/packages/components/src/lib/layout-estimates.browser.test.mjs new file mode 100644 index 0000000..5ef0529 --- /dev/null +++ b/packages/components/src/lib/layout-estimates.browser.test.mjs @@ -0,0 +1,135 @@ +import assert from 'node:assert/strict'; +import { mkdtemp, readFile, rm, writeFile } from 'node:fs/promises'; +import { tmpdir } from 'node:os'; +import { join } from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { test } from 'node:test'; +import { chromium } from '@playwright/test'; +import { svelte } from '@sveltejs/vite-plugin-svelte'; +import { createServer } from 'vite'; + +const repository = fileURLToPath(new URL('../../../../', import.meta.url)); +const harnessId = join(repository, '__northstar-layout-probe.svelte'); +const investigatedCases = JSON.parse(await readFile(join(repository, 'qualification/layout-render-cases.json'), 'utf8')).cases.map((entry) => entry.componentId + ':' + entry.id); +const source = ` + + +
+{#each fixtures as fixture} + {#each fixture.layout[0].widths as width} +
+ {#if fixture.componentId === 'accordion'} + + {:else if fixture.componentId === 'badge'} + {fixture.sampleText} + {:else if fixture.componentId === 'breadcrumbs'} + + {:else if fixture.componentId === 'button'} + + {:else if fixture.componentId === 'checkbox'} + + {:else if fixture.componentId === 'text-field'} + + {/if} +
+ {/each} +{/each} +
+ +`; + +test('Northstar conservative layout samples retain complete content at their declared widths', async () => { + const cache = await mkdtemp(join(tmpdir(), 'dk-northstar-layout-')); + const server = await createServer({ + configFile: false, + root: repository, + cacheDir: cache, + plugins: [{ + name: 'northstar-layout-probe', + enforce: 'pre', + resolveId(id) { if (id === 'virtual:northstar-layout.svelte') return harnessId; }, + load(id) { if (id === harnessId) return source; }, + configureServer(server) { + server.middlewares.use('/northstar-layout', async (_request, response, next) => { + try { + const html = await server.transformIndexHtml('/northstar-layout', '
'); + response.setHeader('Content-Type', 'text/html'); + response.end(html); + } catch (error) { next(error); } + }); + } + }, svelte({ configFile: false, hot: false })], + resolve: { alias: { '@dkcli/core': join(repository, 'packages/core/src/index.ts'), '@dkcli/tokens': join(repository, 'packages/tokens/src/index.ts') } }, + server: { host: '127.0.0.1', port: 0 } + }); + let browser; + try { + await server.listen(); + const address = server.httpServer.address(); + assert.ok(address && typeof address === 'object'); + browser = await chromium.launch({ headless: true }); + const page = await browser.newPage({ viewport: { width: 1440, height: 960 } }); + page.setDefaultTimeout(15_000); + const errors = []; + page.on('pageerror', (error) => errors.push(error.message)); + await page.goto(`http://127.0.0.1:${address.port}/northstar-layout`); + await page.locator('[data-ready=true]').waitFor(); + await page.evaluate(() => document.fonts.ready); + const observations = []; + for (const viewportWidth of [1440, 320]) { + await page.setViewportSize({ width: viewportWidth, height: 960 }); + observations.push(...await page.locator('[data-fixture]').evaluateAll((rows, viewportWidth) => rows.map((row) => { + const bounds = row.getBoundingClientRect(); + const candidates = [...row.querySelectorAll('*')].filter((element) => !element.matches('input,.sr-only') && getComputedStyle(element).position !== 'absolute'); + const nodes = candidates.map((element) => { + const rect = element.getBoundingClientRect(); + const css = getComputedStyle(element); + const directText = [...element.childNodes].filter((node) => node.nodeType === Node.TEXT_NODE && node.textContent.trim()); + const ranges = directText.flatMap((node) => { const range = document.createRange(); range.selectNodeContents(node); return [...range.getClientRects()].map((rect) => ({ left: rect.left, right: rect.right })); }); + return { selector: element.className || element.tagName, text: directText.map((node) => node.textContent).join('').trim(), left: rect.left, right: rect.right, width: rect.width, height: rect.height, clientWidth: element.clientWidth, scrollWidth: element.scrollWidth, fontSize: css.fontSize, ranges }; + }); + const input = row.querySelector('input:not([type=checkbox])'); + const inputBounds = input?.getBoundingClientRect(); + return { viewportWidth, component: row.dataset.component, fixtureId: row.dataset.fixture, width: Number(row.dataset.width), clientWidth: row.clientWidth, scrollWidth: row.scrollWidth, left: bounds.left, right: bounds.right, text: row.textContent.replace(/\\s+/g, ' ').trim(), input: input ? { value: input.value, clientWidth: input.clientWidth, scrollWidth: input.scrollWidth, width: inputBounds.width, height: inputBounds.height, fontSize: getComputedStyle(input).fontSize } : null, nodes }; + }), viewportWidth)); + } + const failures = observations.flatMap((row) => row.nodes.filter((node) => node.scrollWidth > node.clientWidth + 1 || node.left < row.left - 1 || node.right > row.right + 1 || node.ranges.some((range) => range.left < row.left - 1 || range.right > row.right + 1)).map((node) => ({ component: row.component, fixtureId: row.fixtureId, viewportWidth: row.viewportWidth, containerWidth: row.width, node }))); + const fixtures = await page.evaluate(() => window.__northstarFixtures.map((fixture) => ({ component: fixture.componentId, id: fixture.id, sample: fixture.sampleText, layout: fixture.layout }))); + const receipt = { theme: 'Northstar', fonts: 'system-ui, sans-serif', fixtures, observations, failures, mapping: ['Accordion sample populates both trigger and panel; proof target panel uses trigger font token.', 'Breadcrumbs uses the three declared labels with delivered separators, not literal slash sample.', 'TextField sample populates both label and value; native value scrolling is recorded separately.'] }; + await writeFile('/tmp/dk-northstar-layout-observations.json', JSON.stringify(receipt, null, 2)); + assert.equal(fixtures.length, 17, 'The exact seventeen conservative fixture failures must be inspected.'); + assert.deepEqual(errors, [], 'The source sample render must have no browser errors.'); + for (const row of observations) { + const fixture = fixtures.find((fixture) => fixture.component === row.component && fixture.id === row.fixtureId); + assert.ok(fixture, 'Every rendered row must bind a declared fixture.'); + if (row.component === 'breadcrumbs') for (const segment of ['Workspace', 'Release', 'Production']) assert.ok(row.text.includes(segment), 'Complete Breadcrumb item labels must be retained.'); + else assert.ok(row.text.includes(fixture.sample), 'The complete declared sample must remain in the rendered row.'); + if (row.component === 'text-field') { + assert.equal(row.input.value, fixture.sample, 'The modeled TextField input must retain the full sample value.'); + assert.ok(row.input.clientWidth > 0, 'Native input scrolling needs an actual allocated text viewport.'); + } + } + assert.deepEqual(failures, [], 'Complete source samples must fit their allocated rows without painted or native container overflow.'); + } finally { + await browser?.close(); + await server.close(); + await rm(cache, { recursive: true, force: true }); + } +}); diff --git a/packages/components/src/lib/layout-text-behavior.test.ts b/packages/components/src/lib/layout-text-behavior.test.ts new file mode 100644 index 0000000..d8937ac --- /dev/null +++ b/packages/components/src/lib/layout-text-behavior.test.ts @@ -0,0 +1,53 @@ +import { describe, expect, it } from 'vitest'; +import { createProjectTheme } from '@dkcli/tokens'; +import { createAccordionRegistration } from './accordion/accordion.recipe.js'; +import { createBadgeRegistration } from './badge/badge.recipe.js'; +import { createBreadcrumbsRegistration } from './breadcrumbs/breadcrumbs.recipe.js'; +import { createButtonRegistration } from './button/button.recipe.js'; +import { createCheckboxRegistration } from './checkbox/checkbox.recipe.js'; +import { createTextFieldRegistration } from './text-field/text-field.recipe.js'; + +const theme = createProjectTheme({ + name: 'Northstar', + seed: { color: '#c44724', ratio: 'perfect-fourth', mode: 'light', density: 'comfortable', motion: 'snappy' }, + fonts: { body: 'system-ui, sans-serif', display: 'Georgia, serif', mono: 'ui-monospace, monospace' } +}); + +describe('delivered component text behavior in mathematical layout fixtures', () => { + const cases = [ + { name: 'Accordion panel', registration: createAccordionRegistration, textBehavior: 'wrap-anywhere', lineHeight: 1.6 }, + { name: 'Badge label', registration: createBadgeRegistration, textBehavior: 'wrap-anywhere', lineHeight: 1 }, + { name: 'Breadcrumb item rows', registration: createBreadcrumbsRegistration, textBehavior: 'wrap' }, + { name: 'Button label', registration: createButtonRegistration, textBehavior: 'wrap-anywhere', lineHeight: 1.1 }, + { name: 'Checkbox label', registration: createCheckboxRegistration, textBehavior: 'wrap', lineHeight: 1.35 }, + { name: 'TextField native input', registration: createTextFieldRegistration, textBehavior: 'scroll', lineHeight: 1.2 } + ]; + + it.each(cases)('$name exposes its delivered flow instead of treating all text as a single line', ({ registration, textBehavior, lineHeight }) => { + const component = registration(theme); + const layouts = component.recipe.proofFixtures.flatMap((fixture) => fixture.layout); + expect(layouts.length).toBeGreaterThan(0); + for (const layout of layouts) { + expect(layout).toMatchObject({ textBehavior }); + if (lineHeight !== undefined) expect(component.spec.proofs.layout).toMatchObject({ lineHeight }); + expect(layout.estimatedInlinePx).toBeGreaterThan(0); + expect(layout.widthChecks.map((check) => check.width)).toEqual(layout.widths); + } + }); + + it.each([createBadgeRegistration, createBreadcrumbsRegistration, createButtonRegistration, createCheckboxRegistration])('does not misclassify a growing min-height control as a fixed-height container', (registration) => { + expect(registration(theme).spec.proofs.layout).not.toHaveProperty('heights'); + }); + + it('binds the Accordion panel sample to the panel typography instead of trigger chrome', () => { + const layout = createAccordionRegistration(theme).spec.proofs.layout; + expect(layout).toMatchObject({ target: 'panel', labelFontSize: { slotVar: { slot: 'panel', name: '--dk-accordion-panel-size' } }, heights: [160] }); + expect(layout).not.toHaveProperty('gap'); + expect(layout).not.toHaveProperty('iconSize'); + }); + + it('reserves the complete Checkbox control and the delivered Breadcrumb separators', () => { + expect(createCheckboxRegistration(theme).spec.proofs.layout).toMatchObject({ reservedInlineSize: { literal: 25 } }); + expect(createBreadcrumbsRegistration(theme).spec.proofs.layout).toMatchObject({ reservedInlineSize: { literal: 24 } }); + }); +}); diff --git a/packages/components/src/lib/menu/Menu.svelte b/packages/components/src/lib/menu/Menu.svelte index 17bc7a7..5a9745f 100644 --- a/packages/components/src/lib/menu/Menu.svelte +++ b/packages/components/src/lib/menu/Menu.svelte @@ -12,7 +12,7 @@ import { createEventDispatcher, tick } from 'svelte'; import type { ThemeContract } from '@dkcli/core'; - import { computeAnchoredPosition, firstEnabledIndex, isEventOutside, nextListIndex, type Placement } from '../internal/behavior/index.js'; + import { computeAnchoredPosition, firstEnabledIndex, lastEnabledIndex, isEventOutside, nextListIndex, type Placement } from '../internal/behavior/index.js'; import { DEFAULT_MENU_THEME, createMenuRegistration, @@ -57,7 +57,7 @@ $: compiledCase = getMenuRecipeCase(registration.recipe, { size }); $: slotStyles = serializeMenuSlotStyles(compiledCase); $: if (internalOpen) { - highlightIndex = Math.max(0, firstEnabledIndex(items)); + if (!items[highlightIndex] || items[highlightIndex].disabled) highlightIndex = firstEnabledIndex(items); void syncPosition(); } @@ -92,6 +92,7 @@ internalOpen = nextOpen; open = nextOpen; previousOpen = nextOpen; + if (nextOpen) highlightIndex = firstEnabledIndex(items); onOpenChange?.({ open: nextOpen }); dispatch('openchange', { open: nextOpen }); if (!nextOpen && restoreFocus) { @@ -153,6 +154,14 @@ event.preventDefault(); } } + + function handleTriggerKeydown(event: KeyboardEvent): void { + if (internalOpen || (event.key !== 'ArrowDown' && event.key !== 'ArrowUp')) return; + setOpen(true); + highlightIndex = event.key === 'ArrowUp' ? lastEnabledIndex(items) : firstEnabledIndex(items); + event.preventDefault(); + event.stopPropagation(); + } @@ -163,6 +172,7 @@ class="menu-trigger" aria-haspopup="menu" aria-expanded={internalOpen ? 'true' : 'false'} + onkeydown={handleTriggerKeydown} onclick={() => { setOpen(!internalOpen); }} diff --git a/packages/components/src/lib/menu/Menu.svelte.test.ts b/packages/components/src/lib/menu/Menu.svelte.test.ts index b181c3c..76d984b 100644 --- a/packages/components/src/lib/menu/Menu.svelte.test.ts +++ b/packages/components/src/lib/menu/Menu.svelte.test.ts @@ -8,6 +8,16 @@ import MenuHarness from './MenuHarness.svelte'; afterEach(() => document.querySelectorAll('[data-test-outside]').forEach((element) => element.remove())); describe('Menu', () => { + it.each([['ArrowDown', 'First'], ['ArrowUp', 'Last']])('opens from %s and focuses %s', async (key, label) => { + render(Menu, { props: { items: [{ value: 'first', label: 'First' }, { value: 'last', label: 'Last' }] } }); + const trigger = screen.getByRole('button', { name: 'Open menu' }); + trigger.focus(); + await fireEvent.keyDown(trigger, { key }); + await tick(); + expect(screen.getByRole('menu')).toBeTruthy(); + expect(document.activeElement).toBe(screen.getByRole('menuitem', { name: label })); + }); + it('closes when focus leaves without stealing outside focus or choosing an action', async () => { const onAction = vi.fn(); render(Menu, { props: { items: [{ value: 'rename', label: 'Rename' }], onAction } }); diff --git a/packages/components/src/lib/native-form-behavior.browser.test.mjs b/packages/components/src/lib/native-form-behavior.browser.test.mjs new file mode 100644 index 0000000..c4b3c4f --- /dev/null +++ b/packages/components/src/lib/native-form-behavior.browser.test.mjs @@ -0,0 +1,123 @@ +import assert from 'node:assert/strict'; +import { execFileSync } from 'node:child_process'; +import { mkdtemp, rm, writeFile } from 'node:fs/promises'; +import { tmpdir } from 'node:os'; +import { join, relative } from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { test } from 'node:test'; +import { chromium } from '@playwright/test'; +import { svelte } from '@sveltejs/vite-plugin-svelte'; +import { createServer } from 'vite'; + +const repository = fileURLToPath(new URL('../../../../', import.meta.url)); +const harnessId = join(repository, '__native-form-probe.svelte'); +const baseline = process.env.DK_FORM_BASELINE === '1'; +const baselineFiles = new Set([ + 'select/Select.svelte', 'combobox/Combobox.svelte', + 'date-picker/DatePicker.svelte', 'range-date-picker/RangeDatePicker.svelte' +].map((path) => join(repository, 'packages/components/src/lib', path))); +const source = ` + +
+
submit(event, 'select')}> + { + event.preventDefault(); + focusTarget?.focus(); + }} + /> +{/if} + + diff --git a/packages/components/src/lib/proof-matrix.test.ts b/packages/components/src/lib/proof-matrix.test.ts index b6ba674..5e80675 100644 --- a/packages/components/src/lib/proof-matrix.test.ts +++ b/packages/components/src/lib/proof-matrix.test.ts @@ -2,13 +2,7 @@ import { describe, expect, it } from 'vitest'; import { createTheme } from '@dkcli/tokens'; -import { - BADGE_LAYOUT_FAILURES, - BUTTON_LAYOUT_FAILURES, - CHECKBOX_LAYOUT_FAILURES, - expectKnownLayoutFailures, - type KnownLayoutFailure -} from './test-utils/proof-expectations.js'; +import { DECLARED_LAYOUT_BEHAVIORS, expectDeclaredLayoutBehavior } from './test-utils/proof-expectations.js'; import { COMPONENT_VERIFICATION_REGISTRY, DK_COMPONENT_THEME_PRESETS } from './verification.js'; const FIXTURE_COUNTS: Record = { @@ -21,20 +15,6 @@ const FIXTURE_COUNTS: Record = { 'text-field': 6, textarea: 6, toast: 2, tooltip: 1, 'tree-view': 2 }; -// These single-line width estimates are known limits of the current recipes. -// Keep failures visible until rendered evidence or an intentional recipe change resolves them. -function expectedLayoutFailures(slug: string, themeId: string): KnownLayoutFailure[] { - switch (slug) { - case 'accordion': return [{ name: 'accordion-md-open', widths: [320] }]; - case 'breadcrumbs': return [{ name: 'breadcrumbs-md', widths: [220] }]; - case 'button': return themeId === 'linen' ? [BUTTON_LAYOUT_FAILURES[1]] : BUTTON_LAYOUT_FAILURES; - case 'badge': return themeId === 'sage' ? [] : BADGE_LAYOUT_FAILURES; - case 'checkbox': return ['cobalt', 'ember'].includes(themeId) ? CHECKBOX_LAYOUT_FAILURES : []; - case 'text-field': return themeId === 'linen' ? [] : [{ name: 'sizes (size=lg)', widths: [240] }]; - default: return []; - } -} - describe('component mathematical proof matrix', () => { it('keeps explicit fixture-count coverage for every shipped component', () => { expect(COMPONENT_VERIFICATION_REGISTRY.map((entry) => entry.slug)).toEqual(Object.keys(FIXTURE_COUNTS)); @@ -44,12 +24,12 @@ describe('component mathematical proof matrix', () => { const theme = createTheme(themeDef); describe(`theme: ${themeDef.name}`, () => { for (const comp of COMPONENT_VERIFICATION_REGISTRY) { - it(`${comp.name} — preserves all fixtures and reports known narrow-width failures`, () => { + it(`${comp.name} — preserves all fixtures and declared layout behavior`, () => { const registration = comp.createRegistration(theme); - expectKnownLayoutFailures( + expectDeclaredLayoutBehavior( registration.recipe.proofFixtures, FIXTURE_COUNTS[comp.slug], - expectedLayoutFailures(comp.slug, themeDef.id) + DECLARED_LAYOUT_BEHAVIORS[comp.slug] ); }); } diff --git a/packages/components/src/lib/radio-group/RadioGroup.svelte b/packages/components/src/lib/radio-group/RadioGroup.svelte index f8fc668..b086910 100644 --- a/packages/components/src/lib/radio-group/RadioGroup.svelte +++ b/packages/components/src/lib/radio-group/RadioGroup.svelte @@ -108,6 +108,7 @@ {name} value={item.value} bind:group={currentValue} + {required} disabled={disabled || item.disabled} onchange={() => handleValueChange(item.value)} /> diff --git a/packages/components/src/lib/range-date-picker/DatePicker.svelte.test.ts b/packages/components/src/lib/range-date-picker/DatePicker.svelte.test.ts index f25e907..232b3c6 100644 --- a/packages/components/src/lib/range-date-picker/DatePicker.svelte.test.ts +++ b/packages/components/src/lib/range-date-picker/DatePicker.svelte.test.ts @@ -12,6 +12,61 @@ describe('RangeDatePicker', () => { afterEach(() => { vi.useRealTimers(); + document.querySelectorAll('[data-test-calendar-external]').forEach((element) => element.remove()); + }); + + it('excludes a disabled range from native form submission', async () => { + const form = document.createElement('form'); + form.dataset.testCalendarExternal = 'true'; + document.body.append(form); + const { rerender } = render(RangeDatePicker, { target: form, props: { label: 'Launch window', name: 'window', value: { start: '2026-04-15', end: '2026-04-18' } } }); + expect([...new FormData(form).entries()]).toEqual([['window[start]', '2026-04-15'], ['window[end]', '2026-04-18']]); + await rerender({ disabled: true }); + + expect([...new FormData(form).entries()]).toEqual([]); + }); + + it('closes an open calendar when disabled without changing the range', async () => { + const onChange = vi.fn(); + const { rerender } = render(RangeDatePicker, { props: { label: 'Launch window', value: { start: '2026-04-15', end: '2026-04-18' }, onChange } }); + await fireEvent.click(screen.getByRole('button', { name: 'Launch window' })); + await rerender({ disabled: true }); + const remainingDay = screen.queryByRole('button', { name: /Apr 16, 2026/i }); + if (remainingDay) await fireEvent.click(remainingDay); + + expect(onChange).not.toHaveBeenCalled(); + expect(screen.queryByRole('dialog', { name: /Choose date range/i })).toBeNull(); + expect(screen.getByRole('button', { name: 'Launch window' }).textContent).toContain('Apr 15, 2026 – Apr 18, 2026'); + }); + + it('keeps focus on an outside input after its click dismisses the calendar', async () => { + render(RangeDatePicker, { props: { label: 'Launch window', value: { start: '2026-04-15', end: '2026-04-18' } } }); + await fireEvent.click(screen.getByRole('button', { name: 'Launch window' })); + const outside = document.createElement('input'); + outside.dataset.testCalendarExternal = 'true'; + document.body.append(outside); + outside.focus(); + await fireEvent.click(outside); + await tick(); + + expect(screen.queryByRole('dialog', { name: /Choose date range/i })).toBeNull(); + expect(document.activeElement).toBe(outside); + }); + + it('moves backward across month boundaries to the current-month date with one day tab stop', async () => { + const onChange = vi.fn(); + render(RangeDatePicker, { props: { label: 'Launch window', open: true, value: { start: '2026-04-15', end: '2026-05-01' }, onChange } }); + const april = screen.getByRole('grid', { name: 'April 2026' }); + const may = screen.getByRole('grid', { name: 'May 2026' }); + const currentDay = within(may).getByRole('button', { name: /May 1, 2026/i }); + currentDay.focus(); + await fireEvent.keyDown(currentDay, { key: 'ArrowLeft' }); + await tick(); + + const aprilDay = within(april).getByRole('button', { name: /Apr 30, 2026/i }); + expect(document.activeElement).toBe(aprilDay); + expect(screen.getByRole('dialog', { name: /Choose date range/i }).querySelectorAll('.range-day[tabindex="0"]')).toHaveLength(1); + expect(onChange).not.toHaveBeenCalled(); }); it('selects a start and end date and emits the completed range', async () => { diff --git a/packages/components/src/lib/range-date-picker/RangeDatePicker.svelte b/packages/components/src/lib/range-date-picker/RangeDatePicker.svelte index e9acb48..da6a958 100644 --- a/packages/components/src/lib/range-date-picker/RangeDatePicker.svelte +++ b/packages/components/src/lib/range-date-picker/RangeDatePicker.svelte @@ -25,6 +25,7 @@ type DateRangeValue as InternalDateRangeValue } from '../internal/behavior/index.js'; import { FieldFrame } from '../primitives/index.js'; + import RequiredSelection from '../primitives/RequiredSelection.svelte'; import { DEFAULT_RANGE_DATE_PICKER_THEME, createRangeDatePickerRegistration, @@ -92,7 +93,7 @@ let internalValue = $state(untrack(() => normalizeRange(value))); let previousValue = $state(untrack(() => JSON.stringify(value ?? {}))); - let internalOpen = $state(untrack(() => open)); + let internalOpen = $state(untrack(() => open && !disabled)); let previousOpen = $state(untrack(() => open)); let visibleMonth = $state(untrack(() => monthStartIso(value?.start ?? todayIso()))); @@ -100,13 +101,16 @@ let triggerEl: HTMLButtonElement | null = $state(null); let surfaceEl: HTMLDivElement | null = $state(null); let restoreFocusEl: HTMLElement | null = null; - let dayRefs: Record = {}; + let dayRefs: Record | undefined> = {}; function registerDay(node: HTMLButtonElement, iso: string) { - dayRefs[iso] = node; + const refs = dayRefs[iso] ?? new Set(); + refs.add(node); + dayRefs[iso] = refs; return { destroy() { - delete dayRefs[iso]; + refs.delete(node); + if (refs.size === 0 && dayRefs[iso] === refs) delete dayRefs[iso]; } }; } @@ -129,7 +133,9 @@ async function syncFocus(): Promise { await tick(); - dayRefs[focusedDate]?.focus(); + const candidates = Array.from(dayRefs[focusedDate] ?? []); + const preferred = candidates.find((node) => node.dataset.outside !== 'true') ?? candidates[0]; + preferred?.focus(); } function openCalendar(): void { @@ -145,18 +151,20 @@ void syncFocus(); } - function closeCalendar(): void { + function closeCalendar(restoreFocus = true): void { const focusTarget = restoreFocusEl ?? triggerEl; emitOpen(false); - focusTarget?.focus(); - void tick().then(() => { + if (restoreFocus) { focusTarget?.focus(); - }); + void tick().then(() => { + focusTarget?.focus(); + }); + } } function chooseDate(iso: string): void { if ( - isDateDisabled({ + disabled || isDateDisabled({ value: iso, min, max, @@ -233,7 +241,7 @@ return; } if (isEventOutside(surfaceEl, event.target) && isEventOutside(triggerEl, event.target)) { - closeCalendar(); + closeCalendar(Boolean(surfaceEl?.contains(document.activeElement))); } } @@ -272,13 +280,16 @@ }); run(() => { if (open !== previousOpen) { - internalOpen = open; + internalOpen = open && !disabled; previousOpen = open; if (internalOpen) { void syncFocus(); } } }); + run(() => { + if (disabled && internalOpen) emitOpen(false); + }); let describedBy = $derived(error ? `${fieldId}-error` : description ? `${fieldId}-description` : undefined); let calendarPair = $derived(buildRangeCalendarPair({ visibleMonth, @@ -306,9 +317,15 @@ descriptionStyle={slotStyles.description} errorStyle={slotStyles.error} > + {#if name} - - + + {/if} diff --git a/packages/components/src/lib/tree-view/Accordion.svelte.test.ts b/packages/components/src/lib/tree-view/Accordion.svelte.test.ts index 38c8a3f..3a825a1 100644 --- a/packages/components/src/lib/tree-view/Accordion.svelte.test.ts +++ b/packages/components/src/lib/tree-view/Accordion.svelte.test.ts @@ -4,6 +4,44 @@ import { describe, expect, it, vi } from 'vitest'; import TreeView from './TreeView.svelte'; describe('TreeView', () => { + it('has one tree tab stop and keeps focus navigation separate from selection', async () => { + const onChange = vi.fn(); + render(TreeView, { props: { items: [{ id: 'workspace', label: 'Workspace', children: [{ id: 'ready', label: 'Ready' }] }], expandedIds: ['workspace'], onChange } }); + const buttons = screen.getAllByRole('button'); + expect(buttons.filter((button) => button.tabIndex === 0)).toHaveLength(1); + const parent = screen.getByRole('button', { name: 'Workspace' }); + parent.focus(); + await fireEvent.keyDown(parent, { key: 'ArrowDown' }); + const child = screen.getByRole('button', { name: 'Ready' }); + expect(document.activeElement).toBe(child); + expect(child.tabIndex).toBe(0); + expect(parent.tabIndex).toBe(-1); + expect(onChange).not.toHaveBeenCalled(); + await fireEvent.keyDown(child, { key: 'ArrowLeft' }); + expect(document.activeElement).toBe(parent); + }); + + it('skips a disabled child during tree keyboard navigation', async () => { + const onChange = vi.fn(); + render(TreeView, { props: { items: [{ id: 'workspace', label: 'Workspace', children: [{ id: 'blocked', label: 'Blocked', disabled: true }, { id: 'ready', label: 'Ready' }] }], expandedIds: ['workspace'], onChange } }); + const parent = screen.getByRole('button', { name: 'Workspace' }); + parent.focus(); + await fireEvent.keyDown(parent, { key: 'ArrowDown' }); + const ready = screen.getByRole('button', { name: 'Ready' }); + expect(document.activeElement).toBe(ready); + expect(onChange).not.toHaveBeenCalled(); + await fireEvent.keyDown(ready, { key: 'Enter' }); + expect(onChange).toHaveBeenCalledExactlyOnceWith({ value: 'ready' }); + }); + + it('enters an expanded branch through its first enabled child', async () => { + render(TreeView, { props: { items: [{ id: 'workspace', label: 'Workspace', children: [{ id: 'blocked', label: 'Blocked', disabled: true }, { id: 'ready', label: 'Ready' }] }], expandedIds: ['workspace'] } }); + const parent = screen.getByRole('button', { name: 'Workspace' }); + parent.focus(); + await fireEvent.keyDown(parent, { key: 'ArrowRight' }); + expect(document.activeElement).toBe(screen.getByRole('button', { name: 'Ready' })); + }); + it('expands branches and emits selection changes', async () => { const onChange = vi.fn(); diff --git a/packages/components/src/lib/tree-view/TreeView.svelte b/packages/components/src/lib/tree-view/TreeView.svelte index fb973ce..4575749 100644 --- a/packages/components/src/lib/tree-view/TreeView.svelte +++ b/packages/components/src/lib/tree-view/TreeView.svelte @@ -14,7 +14,7 @@ import { flattenHierarchy, - nextVisibleHierarchyIndex, + nextListIndex, toggleExpandedIds, type FlatHierarchyItem, type HierarchyItem @@ -48,6 +48,7 @@ let previousValue = value; let previousExpandedKey = expandedIds.join('|'); let itemRefs: HTMLButtonElement[] = []; + let focusedId = value; $: registration = theme.name === DEFAULT_TREE_VIEW_THEME.name ? defaultRegistration : createTreeViewRegistration(theme); @@ -56,6 +57,7 @@ $: if (value !== previousValue) { internalValue = value; previousValue = value; + focusedId = value; } $: nextExpandedKey = expandedIds.join('|'); $: if (nextExpandedKey !== previousExpandedKey) { @@ -63,7 +65,8 @@ previousExpandedKey = nextExpandedKey; } $: visibleItems = flattenHierarchy(items as HierarchyItem[], internalExpandedIds, 0); - $: activeIndex = Math.max(0, visibleItems.findIndex((entry) => entry.id === internalValue)); + $: focusedIndex = visibleItems.findIndex((entry) => entry.id === focusedId && !entry.item.disabled); + $: tabStopIndex = focusedIndex >= 0 ? focusedIndex : visibleItems.findIndex((entry) => !entry.item.disabled); function emitExpanded(ids: string[]): void { internalExpandedIds = ids; @@ -77,6 +80,7 @@ internalValue = nextValue; value = nextValue; previousValue = nextValue; + focusedId = nextValue; onChange?.({ value: nextValue }); dispatch('change', { value: nextValue }); } @@ -89,6 +93,8 @@ } function focusVisible(index: number): void { + if (!visibleItems[index] || visibleItems[index].item.disabled) return; + focusedId = visibleItems[index].id; void tick().then(() => { itemRefs[index]?.focus(); }); @@ -97,7 +103,7 @@ function handleKeydown(event: KeyboardEvent, entry: FlatHierarchyItem, index: number): void { if (entry.item.disabled) return; if (event.key === 'ArrowUp' || event.key === 'ArrowDown' || event.key === 'Home' || event.key === 'End') { - const nextIndex = nextVisibleHierarchyIndex(visibleItems, index, event.key); + const nextIndex = nextListIndex(visibleItems.map((item) => item.item), index, event.key, { orientation: 'vertical' }); focusVisible(nextIndex); event.preventDefault(); return; @@ -106,8 +112,9 @@ if (event.key === 'ArrowRight') { if (entry.hasChildren && !entry.expanded) { toggleBranch(entry.item as TreeViewItem); - } else if (entry.hasChildren && visibleItems[index + 1]) { - focusVisible(index + 1); + } else if (entry.hasChildren) { + const childIndex = visibleItems.findIndex((item) => item.parentId === entry.id && !item.item.disabled); + if (childIndex >= 0) focusVisible(childIndex); } event.preventDefault(); return; @@ -151,6 +158,7 @@ style={slotStyles.branch} type="button" disabled={entry.item.disabled} + tabindex="-1" aria-label={entry.expanded ? `Collapse ${entry.item.label}` : `Expand ${entry.item.label}`} onclick={(event) => { event.stopPropagation(); @@ -168,6 +176,8 @@ type="button" data-selected={internalValue === entry.id ? 'true' : 'false'} disabled={entry.item.disabled} + tabindex={index === tabStopIndex && !entry.item.disabled ? 0 : -1} + onfocus={() => { focusedId = entry.id; }} onclick={() => emitChange(entry.id)} onkeydown={(event) => handleKeydown(event, entry, index)} > diff --git a/packages/core/src/component-compiler.d.ts b/packages/core/src/component-compiler.d.ts index 317c8eb..65cab28 100644 --- a/packages/core/src/component-compiler.d.ts +++ b/packages/core/src/component-compiler.d.ts @@ -1,4 +1,4 @@ -import { type ComponentCase, type ComponentSpec, type ComponentStateName, type TokenExpr } from './component-spec.ts'; +import { type ComponentCase, type ComponentSpec, type ComponentStateName, type LayoutProofSpec, type TokenExpr } from './component-spec.ts'; import { type DistinctnessReport } from './perception.ts'; import type { ThemeContract } from './theme-contract.ts'; type ResolvedSlotVars = Record; @@ -43,6 +43,10 @@ export type ResolvedLayoutWidthCheck = { width: number; fitsWidth: boolean; fitsHeight: boolean; + availableTextPx?: number; + estimatedLineCount?: number; + estimatedBlockPx?: number; + fitsLines?: boolean; pass: boolean; }; /** Lists declared, evaluated, and unsupported proof categories. */ @@ -52,12 +56,15 @@ export type ProofCoverage = { unsupported: string[]; complete: boolean; }; -/** Reports conservative single-line estimates using maximum token values. */ +/** Reports estimates for the declared text behavior using maximum token values. */ export type ResolvedLayoutCheck = { target: string; widths: number[]; heights: number[]; estimatedInlinePx: number; + minimumInlinePx?: number; + textBehavior?: NonNullable; + maxLines?: number; requiredBlockPx: number; widthChecks: ResolvedLayoutWidthCheck[]; assumptions: string[]; diff --git a/packages/core/src/component-compiler.ts b/packages/core/src/component-compiler.ts index cf5d804..10b4350 100644 --- a/packages/core/src/component-compiler.ts +++ b/packages/core/src/component-compiler.ts @@ -66,6 +66,10 @@ export type ResolvedLayoutWidthCheck = { width: number; fitsWidth: boolean; fitsHeight: boolean; + availableTextPx?: number; + estimatedLineCount?: number; + estimatedBlockPx?: number; + fitsLines?: boolean; pass: boolean; }; @@ -77,12 +81,15 @@ export type ProofCoverage = { complete: boolean; }; -/** Reports conservative single-line estimates using maximum token values. */ +/** Reports estimates for the declared text behavior using maximum token values. */ export type ResolvedLayoutCheck = { target: string; widths: number[]; heights: number[]; estimatedInlinePx: number; + minimumInlinePx?: number; + textBehavior?: NonNullable; + maxLines?: number; requiredBlockPx: number; widthChecks: ResolvedLayoutWidthCheck[]; assumptions: string[]; @@ -222,13 +229,34 @@ function toPx(value: { numeric: number; unit: string }): { numeric: number; unit } function multiplyNumberish(value: string | number, factor: number): string | number { + if (!Number.isFinite(factor)) throw new Error('Token multiplier must be finite.'); + const multiply = (numeric: number): number => { + const result = numeric * factor; + if (!Number.isFinite(result)) throw new Error('Token multiplication exceeds finite numeric bounds.'); + return Number(result.toFixed(3)); + }; + if (typeof value === 'string' && /^clamp\(/i.test(value.trim())) { + const clamp = value.trim().match(/^clamp\(\s*(-?\d*\.?\d+)(px|rem|em)\s*,\s*(.+)\s*,\s*(-?\d*\.?\d+)(px|rem|em)\s*\)$/i); + if (!clamp) throw new Error(`Cannot multiply unresolved token "${value}".`); + const center = clamp[3].trim().replace(/^calc\((.*)\)$/i, '$1').trim(); + if (!/^-?\d*\.?\d+(?:px|rem|em|vw|vh|%)(?:\s+[+-]\s+\d*\.?\d+(?:px|rem|em|vw|vh|%))*$|^0$/i.test(center)) { + throw new Error(`Cannot multiply unresolved token "${value}".`); + } + const bounds = [`${multiply(Number(clamp[1]))}${clamp[2].toLowerCase()}`, `${multiply(Number(clamp[4]))}${clamp[5].toLowerCase()}`]; + if (factor < 0) bounds.reverse(); + const terms = [...center.matchAll(/([+-]?\s*\d*\.?\d+)(px|rem|em|vw|vh|%)/gi)].map((term) => ({ + numeric: multiply(Number(term[1].replace(/\s/g, ''))), unit: term[2].toLowerCase() + })); + const scaledCenter = terms.map((term, index) => index === 0 ? `${term.numeric}${term.unit}` : `${term.numeric < 0 ? '-' : '+'} ${Math.abs(term.numeric)}${term.unit}`).join(' ') || '0'; + return `clamp(${bounds[0]}, ${scaledCenter}, ${bounds[1]})`; + } const parsed = parseNumberish(value); if (!parsed) { throw new Error(`Cannot multiply unresolved token "${String(value)}".`); } - const multiplied = parsed.numeric * factor; - return parsed.unit ? `${Number(multiplied.toFixed(3))}${parsed.unit}` : Number(multiplied.toFixed(3)); + const multiplied = multiply(parsed.numeric); + return parsed.unit ? `${multiplied}${parsed.unit}` : multiplied; } function resolveAlias(theme: ThemeContract, alias: string, seen: Set): string | number { @@ -237,7 +265,7 @@ function resolveAlias(theme: ThemeContract, alias: string, seen: Set): s } const resolved = theme.aliases[alias]; - if (!resolved) { + if (!Object.hasOwn(theme.aliases, alias) || !resolved) { throw new Error(`Unknown theme alias "${alias}".`); } @@ -250,13 +278,15 @@ function resolveThemeToken(theme: ThemeContract, ref: string, seen: Set return resolveAlias(theme, ref, seen); } - const [familyName, tokenName] = ref.split('.', 2) as [keyof ThemeContract['families'], string]; + const parts = ref.split('.'); + if (parts.length !== 2 || !parts[0] || !parts[1]) throw new Error(`Invalid theme token reference "${ref}".`); + const [familyName, tokenName] = parts as [keyof ThemeContract['families'], string]; const family = theme.families[familyName]; - if (!family) { + if (!Object.hasOwn(theme.families, familyName) || !family) { throw new Error(`Unknown theme family "${familyName}" in token ref "${ref}".`); } - if (!(tokenName in family)) { + if (!Object.hasOwn(family, tokenName)) { throw new Error(`Unknown theme token "${ref}".`); } @@ -315,7 +345,11 @@ function resolveExprToPx(theme: ThemeContract, expr: TokenExpr | number, context if (!parsed || !['', 'px', 'rem', 'em'].includes(parsed.unit)) { throw new Error(`Token "${JSON.stringify(expr)}" did not resolve to a numeric length.`); } - return Number(toPx(parsed).numeric.toFixed(2)); + const pixels = toPx(parsed).numeric; + if (!Number.isFinite(pixels)) { + throw new Error(`Token "${JSON.stringify(expr)}" did not resolve to a finite length.`); + } + return Number(pixels.toFixed(2)); } function resolveExprToMs(theme: ThemeContract, expr: TokenExpr | number, context: ResolveContext = {}): number { @@ -324,7 +358,11 @@ function resolveExprToMs(theme: ThemeContract, expr: TokenExpr | number, context if (!parsed || !['', 'ms', 's'].includes(parsed.unit) || !Number.isFinite(parsed.numeric) || parsed.numeric < 0) { throw new Error(`Token "${JSON.stringify(expr)}" did not resolve to a numeric duration.`); } - return Number((parsed.numeric * (parsed.unit === 's' ? 1000 : 1)).toFixed(2)); + const milliseconds = parsed.numeric * (parsed.unit === 's' ? 1000 : 1); + if (!Number.isFinite(milliseconds)) { + throw new Error(`Token "${JSON.stringify(expr)}" did not resolve to a finite duration.`); + } + return Number(milliseconds.toFixed(2)); } function estimateInlineContentWidth( @@ -333,18 +371,25 @@ function estimateInlineContentWidth( layoutProof: LayoutProofSpec, theme: ThemeContract, context: ResolveContext -): number { +): { fullInlinePx: number; fixedInlinePx: number; fontSizePx: number; text: string } { const paddingInline = layoutProof.inlinePadding ? resolveExprToPx(theme, layoutProof.inlinePadding, context) : 0; const gap = layoutProof.gap ? resolveExprToPx(theme, layoutProof.gap, context) : 0; const fontSize = layoutProof.labelFontSize ? resolveExprToPx(theme, layoutProof.labelFontSize, context) : 16; const iconSize = layoutProof.iconSize ? resolveExprToPx(theme, layoutProof.iconSize, context) : 0; + const reservedInline = layoutProof.reservedInlineSize !== undefined + ? resolveExprToPx(theme, layoutProof.reservedInlineSize, context) + : 0; + if (!Number.isFinite(reservedInline) || reservedInline < 0) { + throw new Error('Reserved inline size must be finite and non-negative.'); + } const hasText = axes.content !== 'icon-only' && Boolean(sampleText); const textWidth = hasText ? sampleText!.length * fontSize * 0.56 : 0; const hasLeading = axes.content === 'leading' || axes.content === 'leading-trailing'; const hasTrailing = axes.content === 'trailing' || axes.content === 'leading-trailing'; const hasIconOnly = axes.content === 'icon-only'; - let inline = paddingInline * 2 + textWidth; + let inline = paddingInline * 2 + textWidth + reservedInline; + if (reservedInline > 0 && hasText) inline += gap; if (hasIconOnly) { inline += iconSize; } else { @@ -356,7 +401,41 @@ function estimateInlineContentWidth( } } - return Number(inline.toFixed(2)); + return { + fullInlinePx: Number(inline.toFixed(2)), + fixedInlinePx: inline - textWidth, + fontSizePx: fontSize, + text: hasText ? sampleText! : '' + }; +} + +const graphemeSegmenter = new Intl.Segmenter(undefined, { granularity: 'grapheme' }); + +function textGraphemes(text: string): string[] { + return Array.from(graphemeSegmenter.segment(text), ({ segment }) => segment); +} + +function estimateTextLines(text: string, availablePx: number, glyphPx: number, anywhere: boolean): number { + if (!text) return 1; + const words = text.trim().split(/\s+/).map((word) => textGraphemes(word).length); + const capacity = Math.max(1, Math.floor((availablePx + 0.000001) / glyphPx)); + let lines = 1; + let used = 0; + for (const length of words) { + const gap = used > 0 ? 1 : 0; + if (used + gap + length <= capacity) { + used += gap + length; + continue; + } + if (used > 0) lines += 1; + if (anywhere && length > capacity) { + lines += Math.ceil(length / capacity) - 1; + used = ((length - 1) % capacity) + 1; + } else { + used = length; + } + } + return lines; } function buildContrastProofs( @@ -434,17 +513,43 @@ function buildLayoutProofs( context: ResolveContext ): ResolvedLayoutCheck[] { return (proofs.layout ? [proofs.layout] : []).map((proof) => { - const estimatedInlinePx = estimateInlineContentWidth(componentCase.axes, proofCase.sampleText, proof, theme, context); + const content = estimateInlineContentWidth(componentCase.axes, proofCase.sampleText, proof, theme, context); + const estimatedInlinePx = content.fullInlinePx; + const textBehavior = proof.textBehavior ?? 'single-line'; + const glyphPx = content.fontSizePx * 0.56; + const longestWord = Math.max(0, ...content.text.trim().split(/\s+/).map((word) => textGraphemes(word).length)); + const minimumTextPx = textBehavior === 'wrap' + ? longestWord * glyphPx + : content.text ? glyphPx : 0; + const minimumInlinePx = textBehavior === 'single-line' + ? estimatedInlinePx + : Number((content.fixedInlinePx + minimumTextPx).toFixed(2)); const requiredBlockPx = resolveExprToPx( theme, proof.blockSize ?? { slotVar: { slot: proof.target, name: `--dk-${componentId}-block-size` } }, context ); - const heights = proof.heights ?? [requiredBlockPx]; - const fitsHeight = requiredBlockPx <= Math.max(...heights); + const heights = proof.heights ?? (textBehavior === 'single-line' ? [requiredBlockPx] : []); const widthChecks = proof.widths.map((width) => { - const fitsWidth = !proof.noOverflow || estimatedInlinePx <= width; - return { width, fitsWidth, fitsHeight, pass: fitsWidth && fitsHeight }; + const fitsWidth = !proof.noOverflow || minimumInlinePx <= width; + if (textBehavior === 'single-line') { + const fitsHeight = requiredBlockPx <= Math.max(...heights); + return { width, fitsWidth, fitsHeight, pass: fitsWidth && fitsHeight }; + } + const availableTextPx = Number(Math.max(0, width - content.fixedInlinePx).toFixed(2)); + const estimatedLineCount = textBehavior === 'scroll' + ? 1 + : estimateTextLines(content.text, availableTextPx, glyphPx, textBehavior === 'wrap-anywhere'); + const estimatedBlockPx = Number(Math.max( + requiredBlockPx, + content.text ? estimatedLineCount * content.fontSizePx * (proof.lineHeight ?? 1.4) : 0 + ).toFixed(2)); + const fitsHeight = heights.length === 0 || estimatedBlockPx <= Math.max(...heights); + const fitsLines = proof.maxLines === undefined || estimatedLineCount <= proof.maxLines; + return { + width, fitsWidth, fitsHeight, availableTextPx, estimatedLineCount, estimatedBlockPx, + fitsLines, pass: fitsWidth && fitsHeight && fitsLines + }; }); const pass = widthChecks.every((check) => check.pass); @@ -453,11 +558,20 @@ function buildLayoutProofs( widths: proof.widths, heights, estimatedInlinePx, + minimumInlinePx, + textBehavior, + ...(proof.maxLines !== undefined ? { maxLines: proof.maxLines } : {}), requiredBlockPx, widthChecks, assumptions: [ - 'Single-line content uses a 0.56em glyph-advance estimate and maximum length token values.', - 'Font shaping, wrapping, and rendered container geometry are not measured.' + 'The full single-line estimate uses a 0.56em glyph advance and maximum length token values.', + ...(textBehavior === 'wrap' || textBehavior === 'wrap-anywhere' ? [ + `Text uses ${textBehavior === 'wrap' ? 'whitespace word boundaries' : 'word boundaries and grapheme-safe breaks'} for estimated wrapping.`, + ...(proof.lineHeight === undefined ? ['Wrapping uses the default line height of 1.4.'] : []), + ...(proof.heights === undefined ? ['Block size is a minimum; no maximum height is declared.'] : []) + ] : []), + ...(textBehavior === 'scroll' ? ['The native text viewport scrolls horizontally; full text visibility is not verified.'] : []), + 'Font shaping, vertical padding, and rendered container geometry are not measured.' ], pass }; diff --git a/packages/core/src/component-duration-overflow.test.ts b/packages/core/src/component-duration-overflow.test.ts new file mode 100644 index 0000000..fda99e2 --- /dev/null +++ b/packages/core/src/component-duration-overflow.test.ts @@ -0,0 +1,39 @@ +import { describe, expect, it } from 'vitest'; + +import { compileComponentRecipe } from './component-compiler.ts'; +import type { ComponentSpec } from './component-spec.ts'; +import type { ThemeContract } from './theme-contract.ts'; + +const theme: ThemeContract = { + name: 'duration-regression', + seed: { color: '#295dff', ratio: 'perfect-fourth', mode: 'light', density: 'comfortable', motion: 'snappy' }, + meta: { density: 'comfortable', mode: 'light', optimizedSeed: '#295dff', paletteScore: 1, ratioName: 'perfect fourth', ratioValue: 1.333 }, + families: { color: {}, elevation: {}, motion: {}, radius: {}, space: {}, state: {}, type: {} }, + aliases: {} +}; + +function durationSpec(duration: string): ComponentSpec { + return { + id: 'duration', + slots: [{ name: 'root', kind: 'container', required: true }], axes: [], states: ['rest'], + recipe: { root: [{ style: { '--dk-motion-duration': { literal: duration } } }] }, + proofs: { motion: [{ target: 'root', durationMaxMs: Number.MAX_VALUE }] }, + a11y: { role: 'group' } + }; +} + +describe('component duration unit conversion', () => { + it('rejects finite seconds when conversion would emit an infinite millisecond proof', () => { + const seconds = `1${'0'.repeat(306)}`; + expect(Number.isFinite(Number(seconds))).toBe(true); + expect(Number.isFinite(Number(seconds) * 1000)).toBe(false); + expect(() => compileComponentRecipe(durationSpec(`${seconds}s`), theme)).toThrow(/duration/i); + }); + + it('retains very large durations whose converted milliseconds remain finite', () => { + const [fixture] = compileComponentRecipe(durationSpec(`1${'0'.repeat(305)}s`), theme).proofFixtures; + expect(fixture.motion[0].durationMs).toBe(1e308); + expect(Number.isFinite(fixture.motion[0].durationMs)).toBe(true); + expect(fixture.motion[0].pass).toBe(true); + }); +}); diff --git a/packages/core/src/component-length-overflow.test.ts b/packages/core/src/component-length-overflow.test.ts new file mode 100644 index 0000000..a04af9c --- /dev/null +++ b/packages/core/src/component-length-overflow.test.ts @@ -0,0 +1,61 @@ +import { describe, expect, it } from 'vitest'; + +import { compileComponentRecipe } from './component-compiler.ts'; +import type { ComponentSpec } from './component-spec.ts'; +import type { ThemeContract } from './theme-contract.ts'; + +const theme: ThemeContract = { + name: 'length-regression', + seed: { color: '#295dff', ratio: 'perfect-fourth', mode: 'light', density: 'comfortable', motion: 'snappy' }, + meta: { density: 'comfortable', mode: 'light', optimizedSeed: '#295dff', paletteScore: 1, ratioName: 'perfect fourth', ratioValue: 1.333 }, + families: { color: {}, elevation: {}, motion: {}, radius: {}, space: {}, state: {}, type: {} }, + aliases: {} +}; + +function lengthSpec(length: string | number, field: 'minSize' | 'actualSize' = 'actualSize'): ComponentSpec { + return { + id: 'length', + slots: [{ name: 'root', kind: 'container', required: true }], axes: [], states: ['rest'], + recipe: { root: [{ style: { '--dk-length-block-size': { literal: '44px' } } }] }, + proofs: { target: [{ target: 'root', minSize: 1, actualSize: 44, modality: 'touch', [field]: { literal: length } }] }, + a11y: { role: 'group' } + }; +} + +const finiteOverflow = `1${'0'.repeat(308)}`; +const nonfinitePixels = `${'9'.repeat(400)}px`; + +describe('component length unit conversion', () => { + it.each(['rem', 'em'])('rejects finite %s values that overflow during pixel conversion', (unit) => { + expect(Number.isFinite(Number(finiteOverflow))).toBe(true); + expect(Number.isFinite(Number(finiteOverflow) * 16)).toBe(false); + expect(() => compileComponentRecipe(lengthSpec(`${finiteOverflow}${unit}`), theme)).toThrow(/finite|numeric length/i); + }); + + it('rejects pixel literals that parse to infinity before conversion', () => { + expect(Number.isFinite(Number(nonfinitePixels.slice(0, -2)))).toBe(false); + expect(() => compileComponentRecipe(lengthSpec(nonfinitePixels), theme)).toThrow(/finite|numeric length/i); + }); + + it.each([Infinity, NaN])('rejects nonfinite numeric target lengths (%s)', (length) => { + expect(() => compileComponentRecipe(lengthSpec(length), theme)).toThrow(/finite|numeric length/i); + }); + + it('also rejects an overflowing minimum target size', () => { + expect(() => compileComponentRecipe(lengthSpec(`${finiteOverflow}rem`, 'minSize'), theme)).toThrow(/finite|numeric length/i); + }); + + it.each([ + ['44px', 44], + ['2rem', 32], + ['1.5em', 24], + [`1${'0'.repeat(300)}px`, 1e300], + [`1${'0'.repeat(300)}rem`, 1.6e301], + [`1${'0'.repeat(300)}em`, 1.6e301] + ] as const)('preserves finite length %s', (length, expected) => { + const [fixture] = compileComponentRecipe(lengthSpec(length), theme).proofFixtures; + expect(fixture.target[0].actualSizePx).toBe(expected); + expect(Number.isFinite(fixture.target[0].actualSizePx)).toBe(true); + expect(fixture.target[0].pass).toBe(true); + }); +}); diff --git a/packages/core/src/component-spec.d.ts b/packages/core/src/component-spec.d.ts index 9ccba2f..1bb1796 100644 --- a/packages/core/src/component-spec.d.ts +++ b/packages/core/src/component-spec.d.ts @@ -81,6 +81,14 @@ export type LayoutProofSpec = { gap?: TokenExpr | number; labelFontSize?: TokenExpr | number; iconSize?: TokenExpr | number; + /** Defaults to a single line. Set only when the rendered text supports this behavior. */ + textBehavior?: 'single-line' | 'wrap' | 'wrap-anywhere' | 'scroll'; + /** Additional persistent chrome, excluding padding. The gap is added once when text is present. */ + reservedInlineSize?: TokenExpr | number; + /** Unitless text line height. Wrapping estimates default to 1.4. */ + lineHeight?: number; + /** An explicit line budget; growing controls can omit it. */ + maxLines?: number; }; export type MotionProofSpec = { target: string; diff --git a/packages/core/src/component-spec.ts b/packages/core/src/component-spec.ts index 783ec4b..c5d9160 100644 --- a/packages/core/src/component-spec.ts +++ b/packages/core/src/component-spec.ts @@ -93,6 +93,14 @@ export type LayoutProofSpec = { gap?: TokenExpr | number; labelFontSize?: TokenExpr | number; iconSize?: TokenExpr | number; + /** Defaults to a single line. Set only when the rendered text supports this behavior. */ + textBehavior?: 'single-line' | 'wrap' | 'wrap-anywhere' | 'scroll'; + /** Additional persistent chrome, excluding padding. The gap is added once when text is present. */ + reservedInlineSize?: TokenExpr | number; + /** Unitless text line height. Wrapping estimates default to 1.4. */ + lineHeight?: number; + /** An explicit line budget; growing controls can omit it. */ + maxLines?: number; }; export type MotionProofSpec = { @@ -251,6 +259,18 @@ export function validateComponentSpec(spec: ComponentSpec): ComponentSpec { if (layout?.heights && (layout.heights.length === 0 || layout.heights.some((height) => !Number.isFinite(height) || height <= 0))) { throw new Error('Layout proof heights must be finite positive values.'); } + if (layout?.textBehavior !== undefined && !['single-line', 'wrap', 'wrap-anywhere', 'scroll'].includes(layout.textBehavior)) { + throw new Error('Unsupported layout text behavior.'); + } + if (layout?.lineHeight !== undefined && (!Number.isFinite(layout.lineHeight) || layout.lineHeight <= 0)) { + throw new Error('Layout line height must be finite and positive.'); + } + if (layout?.maxLines !== undefined && (!Number.isInteger(layout.maxLines) || layout.maxLines <= 0)) { + throw new Error('Layout maxLines must be a positive integer.'); + } + if (typeof layout?.reservedInlineSize === 'number' && (!Number.isFinite(layout.reservedInlineSize) || layout.reservedInlineSize < 0)) { + throw new Error('Reserved inline size must be finite and non-negative.'); + } return spec; } diff --git a/packages/core/src/compose.ts b/packages/core/src/compose.ts index 2c651f7..951b2fe 100644 --- a/packages/core/src/compose.ts +++ b/packages/core/src/compose.ts @@ -3,6 +3,8 @@ import type { DesignDocument } from './design.ts'; import { analyzeImportance, type ImportanceReport } from './saliency.ts'; import { area, center, round } from './types.ts'; +import { assertDesignDocument, assertFrame, assertRectangles } from './geometry-validation.ts'; +import { assertFiniteOutput } from './numeric-validation.ts'; export type Rect = { id: string; @@ -38,6 +40,7 @@ function clamp01(value: number): number { } function normalizeScore(value: number): number { + assertFiniteOutput(value, 'Composition score'); return parseFloat(clamp01(value).toFixed(3)); } @@ -69,6 +72,8 @@ function gapSeries(values: number[]): number[] { /** Scores geometric composition heuristics for rectangles within a frame. */ export function scoreComposition(rects: Rect[], frame: Frame): CompositionScore { + assertFrame(frame); + assertRectangles(rects); const centerX = frame.width / 2; const centerY = frame.height / 2; const totalArea = rects.reduce((sum, rect) => sum + rect.width * rect.height, 0); @@ -188,6 +193,7 @@ export function scoreDesignComposition( document: DesignDocument, importanceReport?: ImportanceReport ): AdvancedCompositionScore { + assertDesignDocument(document); const rects = document.elements.map((element) => ({ id: element.id, x: element.x, diff --git a/packages/core/src/design-input-boundaries.test.ts b/packages/core/src/design-input-boundaries.test.ts new file mode 100644 index 0000000..b235060 --- /dev/null +++ b/packages/core/src/design-input-boundaries.test.ts @@ -0,0 +1,23 @@ +import { describe, expect, it } from 'vitest'; +import { analyzeImportance } from './saliency.ts'; +import { solveDesignLayout } from './layout.ts'; +import { scoreDesignComposition } from './compose.ts'; +import type { DesignDocument, DesignRole } from './design.ts'; + +function document(): DesignDocument { + return { frame: { width: 960, height: 640 }, elements: [{ id: 'title', kind: 'text', role: 'title', x: 32, y: 32, width: 220, height: 48 }] }; +} + +describe('design-document admission', () => { + it.each([ + { frame: { width: Number.NaN, height: 640 } }, + { frame: { width: 960, height: 640, columns: Number.NaN } }, + { elements: [{ ...document().elements[0], importance: Number.NaN }] }, + { elements: [{ ...document().elements[0], role: 'toString' as DesignRole }] } + ])('rejects malformed geometry or scoring inputs before output %j', (change) => { + const input = { ...document(), ...change }; + expect(() => analyzeImportance(input)).toThrow(); + expect(() => solveDesignLayout(input)).toThrow(); + expect(() => scoreDesignComposition(input)).toThrow(); + }); +}); diff --git a/packages/core/src/ease.d.ts b/packages/core/src/ease.d.ts index 99e658e..a8d03f2 100644 --- a/packages/core/src/ease.d.ts +++ b/packages/core/src/ease.d.ts @@ -11,6 +11,7 @@ export type SpringResult = { css: string; }; export declare const SPRING_PRESETS: Record; +/** Samples a positive, settling spring with 1 to 10,000 intervals. */ export declare function generateSpring(params: SpringParams, sampleCount?: number): SpringResult; export declare function cubicBezierToLinear(x1: number, y1: number, x2: number, y2: number, sampleCount?: number): { samples: number[]; diff --git a/packages/core/src/ease.ts b/packages/core/src/ease.ts index 7743dab..9d0ec94 100644 --- a/packages/core/src/ease.ts +++ b/packages/core/src/ease.ts @@ -1,5 +1,7 @@ // Spring physics and easing curve generation. +import { assertFiniteOutput, assertGeneratedCount, assertPositiveFinite } from './numeric-validation.ts'; + export type SpringParams = { mass: number; stiffness: number; damping: number }; export type SpringResult = { @@ -17,11 +19,19 @@ export const SPRING_PRESETS: Record = { wobbly: { mass: 1, stiffness: 180, damping: 12 }, }; +/** Samples a positive, settling spring with 1 to 10,000 intervals. */ export function generateSpring(params: SpringParams, sampleCount: number = 50): SpringResult { const { mass, stiffness, damping } = params; - const omega = Math.sqrt(stiffness / mass); - const zeta = damping / (2 * Math.sqrt(stiffness * mass)); + assertPositiveFinite(mass, 'Spring mass'); + assertPositiveFinite(stiffness, 'Spring stiffness'); + assertPositiveFinite(damping, 'Spring damping'); + assertGeneratedCount(sampleCount, 'Spring samples', 1); + const omega = Math.sqrt(stiffness) / Math.sqrt(mass); + const zeta = damping / (2 * Math.sqrt(stiffness) * Math.sqrt(mass)); + assertPositiveFinite(omega, 'Spring frequency'); + assertPositiveFinite(zeta, 'Spring damping ratio'); const omegaD = omega * Math.sqrt(Math.abs(1 - zeta * zeta)); + const overdampedRoot = zeta > 1 ? Math.sqrt(zeta - 1) * Math.sqrt(zeta + 1) : 0; // Duration estimation: time for the dominant decay mode to reach < 0.1% of its amplitude. // For underdamped/critically damped (zeta <= 1), the envelope is exp(-zeta*omega*t). @@ -29,12 +39,15 @@ export function generateSpring(params: SpringParams, sampleCount: number = 50): // s2 = -omega*(zeta - sqrt(zeta^2 - 1)), so the time constant is 1/|s2|. let decayRate: number; if (zeta > 1) { - decayRate = omega * (zeta - Math.sqrt(zeta * zeta - 1)); + decayRate = omega / (zeta + overdampedRoot); } else { decayRate = zeta * omega; } // exp(-decayRate * t) < 0.001 => t > -ln(0.001) / decayRate - const duration = decayRate > 0 ? -Math.log(0.001) / decayRate : 1; + assertPositiveFinite(decayRate, 'Spring decay rate'); + const duration = -Math.log(0.001) / decayRate; + assertPositiveFinite(duration, 'Spring settling duration'); + const reportedDuration = duration < 0.001 ? duration : parseFloat(duration.toFixed(3)); const samples: number[] = []; for (let i = 0; i <= sampleCount; i++) { @@ -47,24 +60,30 @@ export function generateSpring(params: SpringParams, sampleCount: number = 50): } else if (zeta === 1) { x = 1 - Math.exp(-omega * t) * (1 + omega * t); } else { - const s1 = -omega * (zeta + Math.sqrt(zeta * zeta - 1)); - const s2 = -omega * (zeta - Math.sqrt(zeta * zeta - 1)); + const s1 = -omega * (zeta + overdampedRoot); + const s2 = -decayRate; + assertFiniteOutput(s1, 'Spring fast eigenmode'); x = 1 - (s2 * Math.exp(s1 * t) - s1 * Math.exp(s2 * t)) / (s2 - s1); } + assertFiniteOutput(x, 'Spring sample'); samples.push(parseFloat(x.toFixed(3))); } samples[samples.length - 1] = 1; const linearValues = samples.map(v => v.toFixed(3)).join(',\n '); const linear = `linear(\n ${linearValues}\n)`; - const css = `/* spring(mass: ${mass}, stiffness: ${stiffness}, damping: ${damping}) */\n/* Duration: ${duration.toFixed(3)}s */\ntransition-timing-function: ${linear};`; + const css = `/* spring(mass: ${mass}, stiffness: ${stiffness}, damping: ${damping}) */\n/* Duration: ${duration < 0.001 ? String(duration) : duration.toFixed(3)}s */\ntransition-timing-function: ${linear};`; - return { params, duration: parseFloat(duration.toFixed(3)), samples, linear, css }; + return { params, duration: reportedDuration, samples, linear, css }; } export function cubicBezierToLinear( x1: number, y1: number, x2: number, y2: number, sampleCount: number = 50 ): { samples: number[]; linear: string } { + assertGeneratedCount(sampleCount, 'Bezier samples', 1); + if (![x1, y1, x2, y2].every(Number.isFinite) || x1 < 0 || x1 > 1 || x2 < 0 || x2 > 1) { + throw new Error('Bezier coordinates must be finite, with x coordinates from 0 to 1.'); + } const samples: number[] = []; for (let i = 0; i <= sampleCount; i++) { const x = i / sampleCount; @@ -77,6 +96,7 @@ export function cubicBezierToLinear( t = Math.max(0, Math.min(1, t)); } const by = 3 * (1 - t) * (1 - t) * t * y1 + 3 * (1 - t) * t * t * y2 + t * t * t; + assertFiniteOutput(by, 'Bezier sample'); samples.push(parseFloat(by.toFixed(3))); } const linearValues = samples.map(v => v.toFixed(3)).join(',\n '); diff --git a/packages/core/src/embedding-boundaries.test.ts b/packages/core/src/embedding-boundaries.test.ts new file mode 100644 index 0000000..52ab949 --- /dev/null +++ b/packages/core/src/embedding-boundaries.test.ts @@ -0,0 +1,17 @@ +import { describe, expect, it } from 'vitest'; +import { analyzeEmbeddingTopology, buildSimilarityMatrix, cosineSimilarity } from './future.ts'; + +describe('embedding shape and numeric integrity', () => { + it.each([1e200, 1e-200])('keeps finite cosine similarity for vectors scaled by %s', (scale) => { + expect(cosineSimilarity([scale, scale], [scale, scale])).toBe(1); + expect(cosineSimilarity([scale, 0], [0, scale])).toBe(0); + }); + it('rejects mismatched dimensions and nonfinite vector coordinates instead of truncating or emitting null', () => { + expect(() => cosineSimilarity([1, 0], [1])).toThrow(); + expect(() => cosineSimilarity([Number.NaN], [1])).toThrow(); + expect(() => buildSimilarityMatrix([[Number.NaN]])).toThrow(); + }); + it('rejects missing item embeddings rather than returning an empty topology for actual content', () => { + expect(() => analyzeEmbeddingTopology({ items: [{ id: 'a', role: 'title', label: 'A', text: 'Content' }], itemEmbeddings: [], queryEmbedding: [1], model: 'heuristic-hash', mode: 'heuristic', query: 'Content' })).toThrow(); + }); +}); diff --git a/packages/core/src/fit.ts b/packages/core/src/fit.ts index 9b8e503..962232f 100644 --- a/packages/core/src/fit.ts +++ b/packages/core/src/fit.ts @@ -1,5 +1,8 @@ // Verifies supplied render measurements for containment and layout drift. +import { assertFrame, assertRectangles } from './geometry-validation.ts'; +import { assertFiniteOutput, assertNonnegativeFinite, assertPositiveFinite } from './numeric-validation.ts'; + export type FitRect = { id: string; x: number; @@ -100,6 +103,9 @@ export function verifyContainment( options: ContainmentOptions = {} ): ContainmentReport { const tolerance = options.overflowTolerance ?? 0; + assertFrame(frame); + assertRectangles(items); + assertNonnegativeFinite(tolerance, 'Overflow tolerance'); const evaluated = items.map((item) => { const overflowLeft = Math.max(0, 0 - item.x); @@ -124,6 +130,7 @@ export function verifyContainment( const maxOverflowX = Math.max(0, ...evaluated.map((item) => item.overflowX)); const maxOverflowY = Math.max(0, ...evaluated.map((item) => item.overflowY)); const totalOverflow = evaluated.reduce((sum, item) => sum + item.overflowX + item.overflowY, 0); + assertFiniteOutput(totalOverflow, 'Total rectangle overflow'); const normalizer = Math.max(frame.width + frame.height, 1) * Math.max(evaluated.length, 1); const score = Math.round(clamp01(1 - totalOverflow / normalizer) * 100); @@ -148,6 +155,10 @@ export function verifyPlanFit( ): PlanFitReport { const positionTolerance = options.positionTolerance ?? 2; const sizeTolerance = options.sizeTolerance ?? 2; + assertRectangles(expected); + assertRectangles(actual); + assertNonnegativeFinite(positionTolerance, 'Position tolerance'); + assertNonnegativeFinite(sizeTolerance, 'Size tolerance'); const actualById = new Map(actual.map((item) => [item.id, item])); const missing: string[] = []; @@ -162,7 +173,8 @@ export function verifyPlanFit( const deltaY = measured.y - target.y; const deltaWidth = measured.width - target.width; const deltaHeight = measured.height - target.height; - const drift = Math.sqrt(deltaX ** 2 + deltaY ** 2 + deltaWidth ** 2 + deltaHeight ** 2); + const drift = Math.hypot(deltaX, deltaY, deltaWidth, deltaHeight); + assertFiniteOutput(drift, 'Rectangle drift'); const withinTolerance = Math.abs(deltaX) <= positionTolerance && Math.abs(deltaY) <= positionTolerance && @@ -186,6 +198,7 @@ export function verifyPlanFit( const mismatchCount = items.filter((item) => !item.withinTolerance).length; const totalDrift = items.reduce((sum, item) => sum + item.drift, 0); + assertFiniteOutput(totalDrift, 'Total rectangle drift'); const meanDrift = items.length > 0 ? totalDrift / items.length : 0; const maxDrift = Math.max(0, ...items.map((item) => item.drift)); const maxSpan = Math.max( @@ -219,16 +232,15 @@ export function recommendMetricGrid( const gap = options.gap ?? 12; const minCellWidth = options.minCellWidth ?? 112; const compactThreshold = options.compactThreshold ?? 132; - const maxColumns = Math.max(1, Math.min(options.maxColumns ?? itemCount, itemCount)); - - let columns = maxColumns; - while (columns > 1) { - const width = (containerWidth - gap * (columns - 1)) / columns; - if (width >= minCellWidth) { - break; - } - columns -= 1; - } + assertPositiveFinite(containerWidth, 'Grid container width'); + assertNonnegativeFinite(gap, 'Grid gap'); + assertPositiveFinite(minCellWidth, 'Grid minimum cell width'); + assertNonnegativeFinite(compactThreshold, 'Grid compact threshold'); + if (!Number.isSafeInteger(itemCount) || itemCount < 0) throw new Error('Grid item count must be a nonnegative safe integer.'); + const requestedColumns = options.maxColumns ?? Math.max(itemCount, 1); + if (!Number.isSafeInteger(requestedColumns) || requestedColumns < 1) throw new Error('Grid maximum columns must be a positive safe integer.'); + const maxColumns = Math.max(1, Math.min(requestedColumns, itemCount)); + const columns = Math.min(maxColumns, Math.floor((containerWidth + gap) / (minCellWidth + gap))); const safeColumns = Math.max(1, columns); const cellWidth = Math.max(0, (containerWidth - gap * (safeColumns - 1)) / safeColumns); diff --git a/packages/core/src/future.ts b/packages/core/src/future.ts index 5458a33..4952248 100644 --- a/packages/core/src/future.ts +++ b/packages/core/src/future.ts @@ -1,6 +1,7 @@ import { round } from './types.ts'; import { escapeCssComment, escapeCssString } from './css-safety.ts'; import type { AuditReport } from './audit.ts'; +import { assertGeneratedCount } from './numeric-validation.ts'; export const FUTURE_EMBEDDING_MODELS = [ '@cf/baai/bge-small-en-v1.5', @@ -108,11 +109,14 @@ function hashToken(token: string, seed: number): number { } function normalizeVector(values: number[]): number[] { - const magnitude = Math.sqrt(values.reduce((sum, value) => sum + value * value, 0)); - if (magnitude === 0) { + if (!Array.from(values).every(Number.isFinite)) throw new Error('Embedding coordinates must be finite numbers.'); + const scale = values.reduce((max, value) => Math.max(max, Math.abs(value)), 0); + if (scale === 0) { return values.map(() => 0); } - return values.map((value) => value / magnitude); + const scaled = values.map((value) => value / scale); + const magnitude = Math.sqrt(scaled.reduce((sum, value) => sum + value * value, 0)); + return scaled.map((value) => value / magnitude); } function roleWeight(role: string): number { @@ -128,6 +132,7 @@ export function composeFutureTopologyText(item: FutureTopologyItem): string { } export function createHeuristicEmbedding(text: string, dimensions: number = HASH_DIMENSIONS): number[] { + assertGeneratedCount(dimensions, 'Embedding dimensions', 1); const vector = Array(dimensions).fill(0); const tokens = tokenize(text); const enriched = [...tokens]; @@ -150,27 +155,27 @@ export function createHeuristicEmbeddings(texts: string[]): number[][] { } export function cosineSimilarity(left: number[], right: number[]): number { - const dimensions = Math.min(left.length, right.length); + if (left.length !== right.length) throw new Error('Embedding dimensions must match.'); + const normalizedLeft = normalizeVector(left); + const normalizedRight = normalizeVector(right); + const dimensions = left.length; if (dimensions === 0) { return 0; } let dot = 0; - let leftMagnitude = 0; - let rightMagnitude = 0; for (let index = 0; index < dimensions; index += 1) { - const leftValue = left[index] ?? 0; - const rightValue = right[index] ?? 0; + const leftValue = normalizedLeft[index]; + const rightValue = normalizedRight[index]; dot += leftValue * rightValue; - leftMagnitude += leftValue * leftValue; - rightMagnitude += rightValue * rightValue; } - if (leftMagnitude === 0 || rightMagnitude === 0) { - return 0; - } - return round(dot / Math.sqrt(leftMagnitude * rightMagnitude), 4); + return round(Math.min(1, Math.max(-1, dot)), 4); } export function buildSimilarityMatrix(vectors: number[][]): number[][] { + for (const vector of vectors) { + if (vector.length !== vectors[0].length) throw new Error('Embedding dimensions must match.'); + normalizeVector(vector); + } return vectors.map((left, leftIndex) => vectors.map((right, rightIndex) => leftIndex === rightIndex ? 1 : cosineSimilarity(left, right) @@ -309,7 +314,9 @@ export function analyzeEmbeddingTopology(options: { mode: 'heuristic' | 'ml'; warnings?: string[]; }): FutureTopologyReport { - if (options.items.length === 0 || options.itemEmbeddings.length === 0) { + if (options.items.length !== options.itemEmbeddings.length) throw new Error('Provide one embedding for each topology item.'); + if (options.items.length > 0 && options.queryEmbedding.length === 0) throw new Error('Provide a nonempty query embedding.'); + if (options.items.length === 0) { return { mode: options.mode, model: options.model, diff --git a/packages/core/src/geometry-boundaries.test.ts b/packages/core/src/geometry-boundaries.test.ts new file mode 100644 index 0000000..632fa30 --- /dev/null +++ b/packages/core/src/geometry-boundaries.test.ts @@ -0,0 +1,63 @@ +import { describe, expect, it } from 'vitest'; +import { verifyContainment, verifyPlanFit, recommendMetricGrid } from './fit.ts'; +import { solveStackLayout } from './layout.ts'; +import { scoreComposition } from './compose.ts'; +import { analyzeTargetAcquisition, predictFittsTime, predictHickTime, predictSteeringTime } from './interaction.ts'; +import { getCorrections } from './optical.ts'; +import { generateGlassCss } from './glass.ts'; + +describe('geometry inputs', () => { + it('rejects finite coordinates whose drift or composition moments overflow', () => { + const left = { id: 'box', x: Number.MAX_VALUE, y: 0, width: 1, height: 1 }; + const right = { ...left, x: -Number.MAX_VALUE }; + expect(() => verifyPlanFit([left], [right])).toThrow(); + expect(() => scoreComposition([{ ...left, x: 1e154, width: 1e154, height: 1e154 }], { width: 1e154, height: 1e154 })).toThrow(); + }); + it.each([Number.NaN, Number.POSITIVE_INFINITY, -1])('rejects invalid rectangle dimensions %s instead of generating a fit score', (width) => { + const item = { id: 'box', x: 0, y: 0, width, height: 40 }; + expect(() => verifyContainment({ width: 320, height: 200 }, [item])).toThrow(); + expect(() => verifyPlanFit([item], [item])).toThrow(); + expect(() => scoreComposition([item], { width: 320, height: 200 })).toThrow(); + }); + + it('rejects duplicate measurement identities instead of selecting the last matching rectangle', () => { + const item = { id: 'box', x: 0, y: 0, width: 40, height: 40 }; + expect(() => verifyPlanFit([item], [item, { ...item, x: 100 }])).toThrow(); + }); + + it.each([ + { min: 100, preferred: 120, max: 20 }, + { min: 0, preferred: 120, grow: Number.NaN }, + { min: 0, preferred: 120, shrink: -1 }, + { min: -1, preferred: 120 } + ])('rejects inconsistent stack constraints %j', (item) => { + expect(() => solveStackLayout([{ id: 'box', ...item }], { container: 320 })).toThrow(); + }); + + it('keeps clamping a preferred size and supports an omitted maximum', () => { + const result = solveStackLayout([{ id: 'box', min: 40, preferred: 20 }], { container: 100 }); + expect(result.items[0].size).toBe(100); + expect(Number.isFinite(result.metrics.used)).toBe(true); + }); + + it('rejects invalid grid counts without iterating through an unbounded count', () => { + expect(() => recommendMetricGrid(320, Number.NaN)).toThrow(); + expect(() => recommendMetricGrid(320, -1)).toThrow(); + expect(recommendMetricGrid(320, 1e9).columns).toBe(2); + }); +}); + +describe('physical CSS and interaction inputs', () => { + it.each([0, -1, Number.NaN, Number.POSITIVE_INFINITY])('rejects invalid optical sizes %s', (size) => { + expect(() => getCorrections('circle', size)).toThrow(); + }); + it.each([{ blur: -1 }, { radius: -1 }, { opacity: Number.NaN }, { layers: 1.5 }, { blur: Number.MAX_VALUE, layers: 3 }])('rejects invalid glass parameters %j', (params) => { + expect(() => generateGlassCss(params)).toThrow(); + }); + it('rejects physically invalid targeting inputs before touch width correction can hide them', () => { + expect(() => analyzeTargetAcquisition({ distance: 100, width: -10, choices: 3, modality: 'touch' })).toThrow(); + expect(() => predictFittsTime({ distance: -100, width: 40 })).toThrow(); + expect(() => predictHickTime({ choices: 1.5 })).toThrow(); + expect(() => predictSteeringTime({ length: 100, width: 0 })).toThrow(); + }); +}); diff --git a/packages/core/src/geometry-validation.d.ts b/packages/core/src/geometry-validation.d.ts new file mode 100644 index 0000000..7f1c899 --- /dev/null +++ b/packages/core/src/geometry-validation.d.ts @@ -0,0 +1,13 @@ +import type { DesignDocument } from './design.ts'; +export declare function assertFrame(frame: { + width: number; + height: number; +}): void; +export declare function assertRectangles(items: Array<{ + id: string; + x: number; + y: number; + width: number; + height: number; +}>): void; +export declare function assertDesignDocument(document: DesignDocument): void; diff --git a/packages/core/src/geometry-validation.ts b/packages/core/src/geometry-validation.ts new file mode 100644 index 0000000..05347a2 --- /dev/null +++ b/packages/core/src/geometry-validation.ts @@ -0,0 +1,47 @@ +import { assertFiniteOutput, assertNonnegativeFinite, assertPositiveFinite } from './numeric-validation.ts'; +import type { DesignDocument } from './design.ts'; + +export function assertFrame(frame: { width: number; height: number }): void { + assertPositiveFinite(frame.width, 'Frame width'); + assertPositiveFinite(frame.height, 'Frame height'); + assertFiniteOutput(frame.width * frame.height, 'Frame area'); +} + +export function assertRectangles(items: Array<{ id: string; x: number; y: number; width: number; height: number }>): void { + const ids = new Set(); + for (const item of items) { + if (!item.id || ids.has(item.id)) throw new Error('Rectangle IDs must be nonempty and distinct.'); + ids.add(item.id); + assertFiniteOutput(item.x, 'Rectangle x'); + assertFiniteOutput(item.y, 'Rectangle y'); + assertNonnegativeFinite(item.width, 'Rectangle width'); + assertNonnegativeFinite(item.height, 'Rectangle height'); + assertFiniteOutput(item.x + item.width, 'Rectangle right edge'); + assertFiniteOutput(item.y + item.height, 'Rectangle bottom edge'); + assertFiniteOutput(item.width * item.height, 'Rectangle area'); + } +} + +export function assertDesignDocument(document: DesignDocument): void { + assertFrame(document.frame); + assertRectangles(document.elements); + assertNonnegativeFinite(document.frame.padding ?? 32, 'Document padding'); + assertNonnegativeFinite(document.frame.gap ?? 20, 'Document gap'); + const columns = document.frame.columns ?? 12; + if (!Number.isSafeInteger(columns) || columns < 1) throw new Error('Document columns must be a positive safe integer.'); + if (document.frame.mode !== undefined && document.frame.mode !== 'flow' && document.frame.mode !== 'app-shell') throw new Error('Document mode must be flow or app-shell.'); + const roles = ['hero', 'title', 'body', 'caption', 'cta', 'support', 'meta', 'eyebrow', 'image', 'data']; + for (const element of document.elements) { + if (element.role !== undefined && !roles.includes(element.role)) throw new Error(`Unknown document role "${element.role}".`); + if (!['text', 'image', 'shape', 'group'].includes(element.kind)) throw new Error(`Unknown document element kind "${element.kind}".`); + for (const key of ['minWidth', 'preferredWidth', 'maxWidth', 'minHeight', 'preferredHeight', 'maxHeight'] as const) { + if (element[key] !== undefined) assertNonnegativeFinite(element[key], `Document ${key}`); + } + if (element.fontSize !== undefined) assertPositiveFinite(element.fontSize, 'Document font size'); + if (element.fontWeight !== undefined) assertPositiveFinite(element.fontWeight, 'Document font weight'); + if (element.importance !== undefined) assertFiniteOutput(element.importance, 'Document importance'); + if (element.priority !== undefined) assertFiniteOutput(element.priority, 'Document priority'); + } + const regions = [...document.background?.safeRegions ?? [], ...(document.background?.subjectRegion ? [document.background.subjectRegion] : [])]; + assertRectangles(regions.map((region, index) => ({ ...region, id: `region-${index}` }))); +} diff --git a/packages/core/src/glass.ts b/packages/core/src/glass.ts index f26aec5..0b8ac69 100644 --- a/packages/core/src/glass.ts +++ b/packages/core/src/glass.ts @@ -1,6 +1,7 @@ // Generates CSS for layered glass effects. import { hexToSrgb } from './color.ts'; +import { assertFiniteOutput, assertNonnegativeFinite } from './numeric-validation.ts'; export type GlassParams = { blur?: number; @@ -16,11 +17,13 @@ export type GlassParams = { }; export function noiseDataUri(intensity: number): string { + assertOpacity(intensity, 'Noise opacity'); const svg = ``; return `url("data:image/svg+xml;base64,${btoa(svg)}")`; } export function hexToRgba(hex: string, alpha: number): string { + assertOpacity(alpha, 'Color opacity'); const [r, g, b] = hexToSrgb(hex); return `rgba(${Math.round(r * 255)}, ${Math.round(g * 255)}, ${Math.round(b * 255)}, ${alpha})`; } @@ -30,12 +33,19 @@ export function generateGlassCss(params: GlassParams = {}): string { const opacity = params.opacity ?? 0.08; const mode = params.mode ?? 'light'; const tint = params.tint ?? (mode === 'light' ? '#ffffff' : '#000000'); - const layers = Math.min(3, Math.max(1, params.layers ?? 1)); + const layers = params.layers ?? 1; const borderOpacity = params.borderOpacity ?? 0.15; const saturation = params.saturation ?? 120; const noise = params.noise ?? 0; const selector = params.selector ?? '.glass'; const radius = params.radius ?? 16; + assertNonnegativeFinite(blur, 'Glass blur'); + assertNonnegativeFinite(saturation, 'Glass saturation'); + assertNonnegativeFinite(radius, 'Glass radius'); + assertOpacity(opacity, 'Glass opacity'); + assertOpacity(borderOpacity, 'Glass border opacity'); + assertOpacity(noise, 'Glass noise'); + if (!Number.isInteger(layers) || layers < 1 || layers > 3) throw new Error('Glass layers must be an integer from 1 to 3.'); const lines: string[] = []; const bg = hexToRgba(tint, opacity); @@ -61,6 +71,7 @@ export function generateGlassCss(params: GlassParams = {}): string { const pseudos = ['::before', '::after']; for (let i = 1; i < layers; i++) { const layerBlur = blur * (1 + i * 0.5); + assertFiniteOutput(layerBlur, 'Glass layer blur'); const layerOpacity = parseFloat((opacity * (1 - i * 0.3)).toFixed(3)); lines.push(''); lines.push(`${selector}${pseudos[i - 1]} {`); @@ -77,3 +88,8 @@ export function generateGlassCss(params: GlassParams = {}): string { return lines.join('\n'); } + +function assertOpacity(value: number, label: string): void { + assertNonnegativeFinite(value, label); + if (value > 1) throw new Error(`${label} must be at most 1.`); +} diff --git a/packages/core/src/interaction.ts b/packages/core/src/interaction.ts index be5bd65..63cc411 100644 --- a/packages/core/src/interaction.ts +++ b/packages/core/src/interaction.ts @@ -1,6 +1,7 @@ // Estimates targeting, choice, steering, and touch occlusion costs. import type { TargetModality } from './types.ts'; +import { assertFiniteOutput, assertNonnegativeFinite, assertPositiveFinite } from './numeric-validation.ts'; export type FittsInput = { distance: number; @@ -34,25 +35,30 @@ export type TargetReport = { }; export function fittsIndexOfDifficulty(distance: number, width: number): number { + assertNonnegativeFinite(distance, 'Target distance'); + assertPositiveFinite(width, 'Target width'); return parseFloat(Math.log2(distance / Math.max(width, 1e-6) + 1).toFixed(3)); } export function predictFittsTime(input: FittsInput): number { const intercept = input.intercept ?? 50; const slope = input.slope ?? 150; - return parseFloat((intercept + slope * fittsIndexOfDifficulty(input.distance, input.width)).toFixed(1)); + return prediction(intercept, slope, fittsIndexOfDifficulty(input.distance, input.width)); } export function predictHickTime(input: HickInput): number { const intercept = input.intercept ?? 80; const slope = input.slope ?? 120; - return parseFloat((intercept + slope * Math.log2(Math.max(input.choices, 1) + 1)).toFixed(1)); + if (!Number.isSafeInteger(input.choices) || input.choices < 1) throw new Error('Choices must be a positive safe integer.'); + return prediction(intercept, slope, Math.log2(input.choices + 1)); } export function predictSteeringTime(input: SteeringInput): number { const intercept = input.intercept ?? 0; const slope = input.slope ?? 110; - return parseFloat((intercept + slope * (input.length / Math.max(input.width, 1e-6))).toFixed(1)); + assertNonnegativeFinite(input.length, 'Path length'); + assertPositiveFinite(input.width, 'Path width'); + return prediction(intercept, slope, input.length / input.width); } export function analyzeTargetAcquisition(options: { @@ -64,6 +70,9 @@ export function analyzeTargetAcquisition(options: { modality?: TargetModality; }): TargetReport { const modality = options.modality ?? 'mouse'; + assertPositiveFinite(options.width, 'Target width'); + if (options.pathWidth !== undefined) assertPositiveFinite(options.pathWidth, 'Path width'); + if (options.pathLength !== undefined) assertNonnegativeFinite(options.pathLength, 'Path length'); const effectiveWidth = modality === 'touch' ? Math.max(12, options.width - Math.min(options.width * 0.32, 14)) @@ -96,3 +105,11 @@ export function analyzeTargetAcquisition(options: { effectiveWidth: parseFloat(effectiveWidth.toFixed(1)) }; } + +function prediction(intercept: number, slope: number, difficulty: number): number { + assertNonnegativeFinite(intercept, 'Prediction intercept'); + assertNonnegativeFinite(slope, 'Prediction slope'); + const result = intercept + slope * difficulty; + assertFiniteOutput(result, 'Predicted time'); + return parseFloat(result.toFixed(1)); +} diff --git a/packages/core/src/jerk.d.ts b/packages/core/src/jerk.d.ts index 511c77a..cb44da5 100644 --- a/packages/core/src/jerk.d.ts +++ b/packages/core/src/jerk.d.ts @@ -12,4 +12,5 @@ export type MinimumJerkResult = { css: string; }; export declare function minimumJerkPosition(t: number): number; +/** Generates a positive-duration curve with 1 to 10,000 sample intervals. */ export declare function generateMinimumJerk(duration?: number, sampleCount?: number): MinimumJerkResult; diff --git a/packages/core/src/jerk.ts b/packages/core/src/jerk.ts index d4bd183..c6c0757 100644 --- a/packages/core/src/jerk.ts +++ b/packages/core/src/jerk.ts @@ -1,5 +1,7 @@ // Minimum-jerk trajectory generation. +import { assertGeneratedCount, assertPositiveFinite } from './numeric-validation.ts'; + export type MinimumJerkSample = { t: number; x: number; @@ -24,7 +26,11 @@ export function minimumJerkPosition(t: number): number { return 10 * clamped ** 3 - 15 * clamped ** 4 + 6 * clamped ** 5; } +/** Generates a positive-duration curve with 1 to 10,000 sample intervals. */ export function generateMinimumJerk(duration: number = 0.6, sampleCount: number = 40): MinimumJerkResult { + assertPositiveFinite(duration, 'Minimum-jerk duration'); + assertGeneratedCount(sampleCount, 'Minimum-jerk samples', 1); + const reportedDuration = duration < 0.001 ? duration : fmt(duration); const samples: MinimumJerkSample[] = []; for (let index = 0; index <= sampleCount; index += 1) { @@ -44,9 +50,9 @@ export function generateMinimumJerk(duration: number = 0.6, sampleCount: number const linear = `linear(\n ${samples.map((sample) => sample.x.toFixed(3)).join(',\n ')}\n)`; return { - duration: fmt(duration), + duration: reportedDuration, samples, linear, - css: `/* minimum-jerk */\ntransition-duration: ${fmt(duration)}s;\ntransition-timing-function: ${linear};` + css: `/* minimum-jerk */\ntransition-duration: ${reportedDuration}s;\ntransition-timing-function: ${linear};` }; } diff --git a/packages/core/src/layout-text-behavior.test.ts b/packages/core/src/layout-text-behavior.test.ts new file mode 100644 index 0000000..612627d --- /dev/null +++ b/packages/core/src/layout-text-behavior.test.ts @@ -0,0 +1,132 @@ +import { describe, expect, it } from 'vitest'; + +import { compileComponentRecipe, type ResolvedLayoutCheck } from './component-compiler.ts'; +import type { ComponentSpec, LayoutProofSpec, TokenExpr } from './component-spec.ts'; +import type { ThemeContract } from './theme-contract.ts'; + +// Structural extension keeps the regression executable before the public contract lands. +type TextLayout = LayoutProofSpec & { + textBehavior?: 'single-line' | 'wrap' | 'wrap-anywhere' | 'scroll'; + reservedInlineSize?: TokenExpr | number; + lineHeight?: number; + maxLines?: number; +}; + +const theme: ThemeContract = { + name: 'text-layout-regression', + seed: { color: '#295dff', ratio: 'perfect-fourth', mode: 'light', density: 'comfortable', motion: 'snappy' }, + meta: { density: 'comfortable', mode: 'light', optimizedSeed: '#295dff', paletteScore: 1, ratioName: 'perfect fourth', ratioValue: 1.333 }, + families: { color: {}, elevation: {}, motion: {}, radius: {}, space: {}, state: {}, type: {} }, + aliases: {} +}; + +function compileLayout(overrides: Partial = {}, sampleText = 'alpha beta gamma'): ResolvedLayoutCheck { + const layout: TextLayout = { + target: 'root', widths: [96, 192], noOverflow: true, + blockSize: 24, inlinePadding: 8, labelFontSize: 16, lineHeight: 1.25, + ...overrides + }; + const spec: ComponentSpec = { + id: 'text-layout', slots: [{ name: 'root', kind: 'container', required: true }], + axes: [], states: ['rest'], recipe: { root: [] }, + proofs: { layout }, proofCases: [{ name: 'label', sampleText }], a11y: { role: 'group' } + }; + return compileComponentRecipe(spec, theme).proofFixtures[0].layout[0]; +} + +describe('declared layout text behavior', () => { + it('preserves the legacy single-line verdict at every width', () => { + const proof = compileLayout(); + expect(proof.estimatedInlinePx).toBe(159.36); + expect(proof.widthChecks.map(({ width, fitsWidth, pass }) => ({ width, fitsWidth, pass }))).toEqual([ + { width: 96, fitsWidth: false, pass: false }, + { width: 192, fitsWidth: true, pass: true } + ]); + expect(proof.pass).toBe(false); + }); + + it('fits wrapping words without discarding the full single-line estimate', () => { + const proof = compileLayout({ textBehavior: 'wrap' }); + expect(proof).toMatchObject({ textBehavior: 'wrap', estimatedInlinePx: 159.36, minimumInlinePx: 60.8, pass: true }); + expect(proof.widthChecks).toMatchObject([ + { width: 96, availableTextPx: 80, estimatedLineCount: 3, estimatedBlockPx: 60, fitsWidth: true, fitsHeight: true, pass: true }, + { width: 192, availableTextPx: 176, estimatedLineCount: 1, estimatedBlockPx: 24, pass: true } + ]); + }); + + it('keeps an unbreakable word failing under ordinary wrapping', () => { + const proof = compileLayout({ textBehavior: 'wrap' }, 'abcdefghijklmno'); + expect(proof).toMatchObject({ minimumInlinePx: 150.4, pass: false }); + expect(proof.widthChecks).toMatchObject([{ width: 96, fitsWidth: false, pass: false }, { width: 192, fitsWidth: true, pass: true }]); + }); + + it('allows arbitrary wrapping only when declared and never splits a grapheme cluster', () => { + const proof = compileLayout({ textBehavior: 'wrap-anywhere', widths: [8, 9, 18], inlinePadding: 0 }, 'e\u0301e\u0301e\u0301'); + expect(proof).toMatchObject({ textBehavior: 'wrap-anywhere', minimumInlinePx: 8.96 }); + expect(proof.widthChecks).toMatchObject([ + { width: 8, fitsWidth: false, pass: false }, + { width: 9, estimatedLineCount: 3, fitsWidth: true, pass: true }, + { width: 18, estimatedLineCount: 2, fitsWidth: true, pass: true } + ]); + }); + + it('reserves persistent chrome before assigning width to wrapping text', () => { + const proof = compileLayout({ textBehavior: 'wrap', reservedInlineSize: 24, widths: [80, 96] }); + expect(proof).toMatchObject({ estimatedInlinePx: 183.36, minimumInlinePx: 84.8, pass: false }); + expect(proof.widthChecks).toMatchObject([ + { width: 80, availableTextPx: 40, fitsWidth: false, pass: false }, + { width: 96, availableTextPx: 56, fitsWidth: true, pass: true } + ]); + }); + + it('checks wrapping height against an explicit height budget at each width', () => { + const proof = compileLayout({ textBehavior: 'wrap', heights: [40] }); + expect(proof.widthChecks).toMatchObject([ + { width: 96, estimatedLineCount: 3, estimatedBlockPx: 60, fitsWidth: true, fitsHeight: false, pass: false }, + { width: 192, estimatedBlockPx: 24, fitsHeight: true, pass: true } + ]); + expect(proof.pass).toBe(false); + }); + + it('rejects a wrapped label exceeding the declared line budget', () => { + const proof = compileLayout({ textBehavior: 'wrap', maxLines: 2 }); + expect(proof.widthChecks).toMatchObject([{ width: 96, estimatedLineCount: 3, pass: false }, { width: 192, estimatedLineCount: 1, pass: true }]); + expect(proof.pass).toBe(false); + }); + + it('records a default line-height assumption when the author omits it', () => { + const proof = compileLayout({ textBehavior: 'wrap', lineHeight: undefined }); + expect(proof.assumptions.join(' ')).toMatch(/default.*line.height|line.height.*default/i); + }); + + it('models a native scrolling viewport without claiming that all text is visible', () => { + const proof = compileLayout({ textBehavior: 'scroll', widths: [12, 96] }); + expect(proof).toMatchObject({ textBehavior: 'scroll', estimatedInlinePx: 159.36, pass: false }); + expect(proof.widthChecks).toMatchObject([ + { width: 12, fitsWidth: false, pass: false }, + { width: 96, availableTextPx: 80, estimatedLineCount: 1, fitsWidth: true, pass: true } + ]); + expect(proof.assumptions.join(' ')).toMatch(/scroll/i); + expect(proof.assumptions.join(' ')).toMatch(/(?:full|all).*text.*(?:visibility|visible).*(?:not|unverified)|(?:not|no|without|does not).*(?:full|all).*text.*(?:visibility|visible)/i); + }); + + it.each([ + { textBehavior: 'unrestricted' }, + { textBehavior: ['wrap'] }, + { reservedInlineSize: -1 }, + { reservedInlineSize: Number.POSITIVE_INFINITY }, + { reservedInlineSize: Number.NaN }, + { lineHeight: 0 }, + { lineHeight: -1 }, + { lineHeight: Number.POSITIVE_INFINITY }, + { lineHeight: Number.NaN }, + { maxLines: 0 }, + { maxLines: -1 }, + { maxLines: 1.5 }, + { maxLines: Number.POSITIVE_INFINITY }, + { maxLines: Number.NaN } + ])('rejects a malformed text behavior or budget: %j', (value) => { + const malformed = value as Partial; + expect(() => compileLayout(malformed)).toThrow(); + }); +}); diff --git a/packages/core/src/layout.ts b/packages/core/src/layout.ts index 166d58a..22ee8e9 100644 --- a/packages/core/src/layout.ts +++ b/packages/core/src/layout.ts @@ -3,6 +3,8 @@ import { analyzeImportance, type ImportanceReport } from './saliency.ts'; import type { DesignDocument, LayoutObjectiveElement, LayoutObjectiveReport } from './design.ts'; import { area, center, clamp01, intersectionArea, intersects, round, type RectLike } from './types.ts'; +import { assertFiniteOutput, assertNonnegativeFinite, assertPositiveFinite } from './numeric-validation.ts'; +import { assertDesignDocument } from './geometry-validation.ts'; export type LayoutItem = { id: string; @@ -109,6 +111,23 @@ export function solveStackLayout(items: LayoutItem[], options: LayoutSolveOption const gap = options.gap ?? 0; const padding = options.padding ?? 0; const align = options.align ?? 'start'; + assertPositiveFinite(options.container, 'Layout container'); + assertNonnegativeFinite(gap, 'Layout gap'); + assertNonnegativeFinite(padding, 'Layout padding'); + if (!['start', 'center', 'end'].includes(align)) throw new Error('Layout alignment must be start, center, or end.'); + const ids = new Set(); + for (const item of items) { + if (!item.id || ids.has(item.id)) throw new Error('Layout IDs must be nonempty and distinct.'); + ids.add(item.id); + assertNonnegativeFinite(item.min, 'Layout minimum'); + assertNonnegativeFinite(item.preferred, 'Layout preferred size'); + if (item.max !== undefined) { + assertNonnegativeFinite(item.max, 'Layout maximum'); + if (item.max < item.min) throw new Error('Layout maximum must be at least its minimum.'); + } + assertNonnegativeFinite(item.grow ?? 1, 'Layout growth weight'); + assertNonnegativeFinite(item.shrink ?? 1, 'Layout shrink weight'); + } const available = Math.max(0, options.container - padding * 2 - Math.max(0, items.length - 1) * gap); const solved: SolvedLayoutItem[] = items.map((item) => { @@ -125,6 +144,9 @@ export function solveStackLayout(items: LayoutItem[], options: LayoutSolveOption }); const preferredTotal = solved.reduce((sum, item) => sum + item.size, 0); + assertFiniteOutput(preferredTotal, 'Layout preferred total'); + assertFiniteOutput(items.reduce((sum, item) => sum + (item.grow ?? 1), 0), 'Layout total growth weight'); + assertFiniteOutput(items.reduce((sum, item) => sum + (item.shrink ?? 1), 0), 'Layout total shrink weight'); if (preferredTotal < available) { distributeExtra(solved, available - preferredTotal); } else if (preferredTotal > available) { @@ -133,6 +155,7 @@ export function solveStackLayout(items: LayoutItem[], options: LayoutSolveOption const contentUsed = solved.reduce((sum, item) => sum + item.size, 0); const totalUsed = contentUsed + padding * 2 + Math.max(0, items.length - 1) * gap; + assertFiniteOutput(totalUsed, 'Layout total size'); const free = Math.max(0, options.container - totalUsed); const overflow = Math.max(0, totalUsed - options.container); @@ -213,6 +236,7 @@ export function solveDesignLayout( preservePositions?: boolean; } = {} ): LayoutObjectiveReport { + assertDesignDocument(document); const padding = document.frame.padding ?? 32; const gap = document.frame.gap ?? 20; const columns = Math.max(1, document.frame.columns ?? 12); diff --git a/packages/core/src/linebreak-overflow.test.ts b/packages/core/src/linebreak-overflow.test.ts new file mode 100644 index 0000000..eb69ff2 --- /dev/null +++ b/packages/core/src/linebreak-overflow.test.ts @@ -0,0 +1,45 @@ +import { describe, expect, it } from 'vitest'; +import { balanceLines, greedyBreak } from './linebreak.ts'; +import { typesetParagraph } from './typeset.ts'; +import { recommendTypography } from './typography.ts'; + +describe('line breaking around an unbreakable word', () => { + it('rejects finite measures and font sizes whose derived estimates overflow', () => { + expect(() => balanceLines('A paragraph', Number.MAX_VALUE)).toThrow(); + expect(() => typesetParagraph({ text: 'A paragraph', widthPx: 200, fontSize: Number.MAX_VALUE })).toThrow(); + }); + it('isolates an overlong word and continues wrapping all following words with finite badness', () => { + const result = balanceLines('unbreakablelongword a few words', 8); + expect(result.lines[0]).toBe('unbreakablelongword'); + expect(result.lines.slice(1).every((line) => line.length <= 8)).toBe(true); + expect(result.lines.join(' ')).toBe('unbreakablelongword a few words'); + expect(Number.isFinite(result.badness)).toBe(true); + expect(result.badness).toBeGreaterThan(0); + }); + + it('preserves width overflow evidence without giving the whole paragraph zero badness', () => { + const result = typesetParagraph({ text: 'unbreakablelongword a few words', widthPx: 60, fontSize: 16 }); + expect(result.lines[0].text).toBe('unbreakablelongword'); + expect(result.lines.slice(1).every((line) => line.width <= 60 * 1.08)).toBe(true); + expect(result.lines.map((line) => line.text).join(' ')).toBe('unbreakablelongword a few words'); + expect(result.maxLineWidth).toBeGreaterThan(60); + expect(Number.isFinite(result.badness)).toBe(true); + expect(result.badness).toBeGreaterThan(0); + }); + + it('represents an empty paragraph without a fabricated blank line or penalty', () => { + expect(balanceLines(' ', 8)).toEqual({ lines: [], badness: 0 }); + expect(greedyBreak(' ', 8)).toEqual({ lines: [], badness: 0 }); + }); + + it.each([0, -1, Number.NaN, Number.POSITIVE_INFINITY])('rejects invalid measures %s', (width) => { + expect(() => balanceLines('A paragraph', width)).toThrow(); + expect(() => greedyBreak('A paragraph', width)).toThrow(); + expect(() => typesetParagraph({ text: 'A paragraph', widthPx: width, fontSize: 16 })).toThrow(); + }); + + it.each([0, -16, Number.NaN, Number.POSITIVE_INFINITY])('rejects invalid font sizes %s', (fontSize) => { + expect(() => typesetParagraph({ text: 'A paragraph', widthPx: 200, fontSize })).toThrow(); + expect(() => recommendTypography({ containerWidth: 200, fontSize })).toThrow(); + }); +}); diff --git a/packages/core/src/linebreak.ts b/packages/core/src/linebreak.ts index ee618e3..d6f6270 100644 --- a/packages/core/src/linebreak.ts +++ b/packages/core/src/linebreak.ts @@ -11,6 +11,7 @@ import { type TypesetResult } from './typeset.ts'; import { round } from './types.ts'; +import { assertFiniteOutput, assertIntegerCount, assertPositiveFinite } from './numeric-validation.ts'; export type LineBreakResult = { lines: string[]; @@ -54,6 +55,8 @@ function lineLength(words: string[], start: number, end: number): number { } export function greedyBreak(text: string, maxChars: number): LineBreakResult { + assertPositiveFinite(maxChars, 'Line character measure'); + if (!text.trim()) return { lines: [], badness: 0 }; const words = text.trim().split(/\s+/); const lines: string[] = []; let current: string[] = []; @@ -72,10 +75,14 @@ export function greedyBreak(text: string, maxChars: number): LineBreakResult { } const badness = lines.reduce((sum, line) => sum + (maxChars - line.length) ** 2, 0); + assertFiniteOutput(badness, 'Line badness'); return { lines, badness }; } export function balanceLines(text: string, maxChars: number, targetLines?: number): LineBreakResult { + assertPositiveFinite(maxChars, 'Line character measure'); + if (targetLines !== undefined) assertIntegerCount(targetLines, 'Target lines', 1); + if (!text.trim()) return { lines: [], badness: 0 }; const words = text.trim().split(/\s+/); const count = words.length; const costs = Array.from({ length: count + 1 }, () => Number.POSITIVE_INFINITY); @@ -88,7 +95,7 @@ export function balanceLines(text: string, maxChars: number, targetLines?: numbe } for (let end = start + 1; end <= count; end += 1) { const length = lineLength(words, start, end); - if (length > maxChars) { + if (length > maxChars && end > start + 1) { break; } const remainder = maxChars - length; @@ -117,6 +124,7 @@ export function balanceLines(text: string, maxChars: number, targetLines?: numbe if (targetLines !== undefined) { badness += Math.abs(lines.length - targetLines) * maxChars * 2; } + assertFiniteOutput(badness, 'Line badness'); return { lines, diff --git a/packages/core/src/motion-boundaries.test.ts b/packages/core/src/motion-boundaries.test.ts new file mode 100644 index 0000000..0945349 --- /dev/null +++ b/packages/core/src/motion-boundaries.test.ts @@ -0,0 +1,48 @@ +import { describe, expect, it } from 'vitest'; +import { cubicBezierToLinear, generateSpring, SPRING_PRESETS } from './ease.ts'; +import { generateMinimumJerk } from './jerk.ts'; + +describe('motion generation boundaries', () => { + it('preserves positive sub-millisecond durations rather than rounding them to zero', () => { + expect(generateMinimumJerk(0.0001).duration).toBe(0.0001); + expect(generateMinimumJerk(0.0001).css).toContain('transition-duration: 0.0001s;'); + const spring = generateSpring({ mass: 1, stiffness: 1e16, damping: 2e8 }); + expect(spring.duration).toBeGreaterThan(0); + expect(spring.css).not.toContain('Duration: 0.000s'); + }); + it.each([ + { mass: 0, stiffness: 180, damping: 12 }, + { mass: 1, stiffness: -180, damping: 12 }, + { mass: 1, stiffness: 180, damping: 0 }, + { mass: Number.POSITIVE_INFINITY, stiffness: 180, damping: 12 }, + { mass: 1, stiffness: Number.NaN, damping: 12 } + ])('rejects a spring without a finite settling model %j', (params) => { + expect(() => generateSpring(params)).toThrow(); + }); + + it.each([0, -1, 1.5, Number.NaN, Number.POSITIVE_INFINITY, 10_001])('rejects invalid sample intervals %s', (samples) => { + expect(() => generateSpring(SPRING_PRESETS.snappy, samples)).toThrow(); + expect(() => generateMinimumJerk(0.6, samples)).toThrow(); + expect(() => cubicBezierToLinear(0.25, 0.1, 0.25, 1, samples)).toThrow(); + }); + + it.each([0, -1, Number.NaN, Number.POSITIVE_INFINITY])('rejects invalid minimum-jerk duration %s', (duration) => { + expect(() => generateMinimumJerk(duration)).toThrow(); + }); + + it('retains a finite slow settling trajectory for a strongly overdamped spring', () => { + const result = generateSpring({ mass: 1, stiffness: 1, damping: 1e9 }, 20); + expect(result.duration).toBeGreaterThan(1e9); + expect(result.samples[0]).toBe(0); + expect(result.samples[10]).toBeGreaterThan(0.9); + expect(result.samples.every(Number.isFinite)).toBe(true); + expect(result.samples.at(-1)).toBe(1); + }); + + it('rejects invalid CSS bezier x coordinates while preserving y overshoot', () => { + expect(() => cubicBezierToLinear(-0.1, 0, 1, 1)).toThrow(); + expect(() => cubicBezierToLinear(0, 0, 1.1, 1)).toThrow(); + expect(() => cubicBezierToLinear(0, Number.NaN, 1, 1)).toThrow(); + expect(cubicBezierToLinear(0.3, -0.3, 0.7, 1.3).samples.every(Number.isFinite)).toBe(true); + }); +}); diff --git a/packages/core/src/numeric-validation.d.ts b/packages/core/src/numeric-validation.d.ts new file mode 100644 index 0000000..07e3c6d --- /dev/null +++ b/packages/core/src/numeric-validation.d.ts @@ -0,0 +1,5 @@ +export declare function assertPositiveFinite(value: number, label: string): void; +export declare function assertNonnegativeFinite(value: number, label: string): void; +export declare function assertGeneratedCount(value: number, label: string, minimum?: number): void; +export declare function assertIntegerCount(value: number, label: string, minimum?: number): void; +export declare function assertFiniteOutput(value: number, label: string): void; diff --git a/packages/core/src/numeric-validation.ts b/packages/core/src/numeric-validation.ts new file mode 100644 index 0000000..c26245b --- /dev/null +++ b/packages/core/src/numeric-validation.ts @@ -0,0 +1,24 @@ +/** Bounds generated arrays while retaining substantially more precision than tool defaults. */ +const MAX_GENERATED_INTERVALS = 10_000; + +export function assertPositiveFinite(value: number, label: string): void { + if (!Number.isFinite(value) || value <= 0) throw new Error(`${label} must be a positive finite number.`); +} + +export function assertNonnegativeFinite(value: number, label: string): void { + if (!Number.isFinite(value) || value < 0) throw new Error(`${label} must be a nonnegative finite number.`); +} + +export function assertGeneratedCount(value: number, label: string, minimum = 0): void { + if (!Number.isInteger(value) || value < minimum || value > MAX_GENERATED_INTERVALS) { + throw new Error(`${label} must be an integer from ${minimum} to ${MAX_GENERATED_INTERVALS}.`); + } +} + +export function assertIntegerCount(value: number, label: string, minimum = 0): void { + if (!Number.isSafeInteger(value) || value < minimum) throw new Error(`${label} must be a safe integer no less than ${minimum}.`); +} + +export function assertFiniteOutput(value: number, label: string): void { + if (!Number.isFinite(value)) throw new Error(`${label} exceeds finite numeric bounds.`); +} diff --git a/packages/core/src/optical.ts b/packages/core/src/optical.ts index 997cb87..0ae3876 100644 --- a/packages/core/src/optical.ts +++ b/packages/core/src/optical.ts @@ -1,5 +1,7 @@ // Optical correction presets for UI elements. +import { assertPositiveFinite } from './numeric-validation.ts'; + export type Correction = { property: string; value: string; @@ -104,6 +106,7 @@ export const OPTICAL = { } satisfies Record; export function getCorrections(type: string, size: number = 48): CorrectionResult { + assertPositiveFinite(size, 'Optical size'); if (!Object.hasOwn(OPTICAL, type)) { throw new Error( `Unknown optical type: ${type}. Available types: ${Object.keys(OPTICAL).join(', ')}` diff --git a/packages/core/src/project-serialization.test.ts b/packages/core/src/project-serialization.test.ts new file mode 100644 index 0000000..f8d00f8 --- /dev/null +++ b/packages/core/src/project-serialization.test.ts @@ -0,0 +1,68 @@ +import { describe, expect, it } from 'vitest'; +import { applyThemeProjectPatch, canonicalProjectJson, createThemeProject, hashProjectValue, projectIdentity, validateProjectTheme, validateThemeProject, validateThemeProjectPatch, type ProjectTheme } from './project.ts'; + +const theme: ProjectTheme = { + name: 'Portable', seed: { color: '#295dff', ratio: 'perfect-fourth', mode: 'light', density: 'comfortable', motion: 'snappy' }, + fonts: { body: 'sans-serif', display: 'serif', mono: 'monospace' } +}; + +describe('portable project serialization', () => { + it('applies a patch to the validated snapshot whose identity was checked before the digest await', async () => { + const project = createThemeProject({ theme }); + const identity = await projectIdentity(project); + const pending = applyThemeProjectPatch(project, { schemaVersion: 1, id: 'radius', title: 'Radius', projectIdentity: identity, changes: [{ family: 'radius', token: 'md', before: null, after: '8px' }] }); + project.theme.seed.color = '#c44724'; + project.viewports[0] = 360; + const updated = await pending; + expect(updated.theme.seed.color).toBe('#295dff'); + expect(updated.viewports[0]).toBe(320); + expect(updated.history[0].theme.seed.color).toBe('#295dff'); + expect(updated.theme.overrides?.radius?.md).toBe('8px'); + expect(project.theme.overrides).toBeUndefined(); + }); + + it('returns an invalid patch verdict for uncloneable metadata', () => { + const patch = { schemaVersion: 1, id: 'radius', title: 'Radius', projectIdentity: 'a'.repeat(64), changes: [{ family: 'radius', token: 'md', before: null, after: '8px' }], callback: (): void => undefined }; + expect(() => validateThemeProjectPatch(patch)).not.toThrow(); + expect(validateThemeProjectPatch(patch).valid).toBe(false); + }); + it('canonicalizes sparse arrays according to their actual exported JSON representation', async () => { + const value = { extra: Array(2) }; + const roundTrip: unknown = JSON.parse(JSON.stringify(value)); + expect(canonicalProjectJson(value)).toBe('{"extra":[null,null]}'); + expect(await hashProjectValue(value)).toBe(await hashProjectValue(roundTrip)); + }); + + it('keeps serializable nested metadata identity stable after a JSON export/import', async () => { + const project = createThemeProject({ theme: { ...theme, metadata: { savedAt: new Date('2026-10-04T12:00:00Z') } } as ProjectTheme }); + const imported = validateThemeProject(JSON.parse(JSON.stringify(project))); + expect(imported.valid).toBe(true); + if (!imported.valid) throw new Error('Expected a serializable project.'); + expect(await projectIdentity(project)).toBe(await projectIdentity(imported.project)); + }); + + it('rejects a sparse viewport array rather than certifying nonexistent widths', () => { + const project = createThemeProject({ theme }); + expect(validateThemeProject({ ...project, viewports: Array(1) }).valid).toBe(false); + }); + + it('reports uncloneable nested input as invalid instead of throwing from a validation API', () => { + const value = { ...theme, metadata: { callback: (): string => 'not portable' } }; + expect(() => validateProjectTheme(value)).not.toThrow(); + expect(validateProjectTheme(value).valid).toBe(false); + const project = createThemeProject({ theme }); + expect(() => validateThemeProject({ ...project, metadata: value.metadata })).not.toThrow(); + expect(validateThemeProject({ ...project, metadata: value.metadata }).valid).toBe(false); + }); + + it('hashes a snapshot before asynchronous digest work and keeps validation copies independent', async () => { + const project = createThemeProject({ theme }); + const before = structuredClone(project); + const pending = projectIdentity(project); + project.theme.fonts.body = 'serif'; + project.viewports[0] = 360; + expect(await pending).toBe(await projectIdentity(before)); + expect(await projectIdentity(project)).not.toBe(await pending); + expect(theme.fonts.body).toBe('sans-serif'); + }); +}); diff --git a/packages/core/src/project.test.ts b/packages/core/src/project.test.ts index 8f0aac8..f36488a 100644 --- a/packages/core/src/project.test.ts +++ b/packages/core/src/project.test.ts @@ -5,6 +5,21 @@ const theme: ProjectTheme = { name: 'Northstar', seed: { color: '#c44724', ratio const now = '2026-10-04T12:00:00.000Z'; describe('portable theme projects', () => { + it.each(['"Inter', "'Inter", 'Inter,, sans-serif', 'Inter,', '123font, sans-serif', 'serif !important', 'Arial "Inter"', 'inherit, serif', 'initial Inter', 'default'])('rejects malformed font stack %s on project import', (body) => { + const project = createThemeProject({ id: 'northstar', theme }); + const checked = validateThemeProject({ ...project, theme: { ...theme, fonts: { ...theme.fonts, body } } }); + expect(checked.valid).toBe(false); + if (checked.valid) throw new Error('Expected invalid font stack.'); + expect(checked.errors['fonts.body']).toMatch(/font stack/i); + expect(project.theme.fonts.body).toBe(theme.fonts.body); + }); + + it.each(['"Inter, Display", sans-serif', "'Noto Sans', ui-sans-serif", '日本語, sans-serif', 'IBM Plex Sans, monospace', '"inherit", serif'])('retains valid quoted and unquoted font stack %s', (body) => { + const project = createThemeProject({ id: 'northstar', theme: { ...theme, fonts: { ...theme.fonts, body } } }); + expect(project.theme.fonts.body).toBe(body); + expect(validateThemeProject(project).valid).toBe(true); + }); + it.each(['dk-button-default', 'DK-range-date-picker-default'])('rejects reserved recipe theme name %s on project import', (name) => { const project = createThemeProject({ id: 'northstar', theme }); expect(validateThemeProject({ ...project, theme: { ...theme, name } }).valid).toBe(false); @@ -56,7 +71,8 @@ describe('portable theme projects', () => { ['review', { reviews: [{ verdict: 'up' }] }], ['qualification', { qualification: { status: 'passed' } }], ['unsafe font', { theme: { ...theme, fonts: { ...theme.fonts, body: 'serif; color:red' } } }], ['unsafe override', { theme: { ...theme, overrides: { color: { primary: 'red;}body{display:none}' } } } }], - ['bad ratio', { theme: { ...theme, seed: { ...theme.seed, ratio: 'unknown' } } }] + ['bad ratio', { theme: { ...theme, seed: { ...theme.seed, ratio: 'unknown' } } }], + ['empty ratio', { theme: { ...theme, seed: { ...theme.seed, ratio: '' } } }] ])('rejects malformed %s without changing the current project', (_label, change) => { const project = createThemeProject({ theme }); const original = JSON.stringify(project); diff --git a/packages/core/src/project.ts b/packages/core/src/project.ts index 0877683..c9caa06 100644 --- a/packages/core/src/project.ts +++ b/packages/core/src/project.ts @@ -69,7 +69,7 @@ function timestamp(value: unknown): value is string { } function widths(value: unknown): value is number[] { return Array.isArray(value) && value.length > 0 && value.length <= 6 && - value.every((width) => Number.isInteger(width) && width >= 320 && width <= 2000) && + Array.from(value).every((width) => Number.isInteger(width) && width >= 320 && width <= 2000) && new Set(value).size === value.length; } function safeValue(value: unknown): value is ThemeTokenValue { @@ -78,6 +78,30 @@ function safeValue(value: unknown): value is ThemeTokenValue { if (typeof value === 'string' && (value.length === 0 || value.length > 512)) return false; try { assertSafeCssValue(value); return true; } catch { return false; } } + +function fontStack(value: string): boolean { + const families: string[] = []; + let quote = ''; + let start = 0; + for (let index = 0; index < value.length; index += 1) { + const char = value[index]; + if (quote) { + if (char === quote) quote = ''; + } else if (char === '"' || char === "'") quote = char; + else if (char === ',') { + families.push(value.slice(start, index).trim()); + start = index + 1; + } + } + if (quote) return false; + families.push(value.slice(start).trim()); + const identifier = /^(?:--|-?[_a-zA-Z\u0080-\u{10FFFF}])[_a-zA-Z0-9\u0080-\u{10FFFF}-]*$/u; + return families.every((family) => { + if (/^(?:"[^"]*"|'[^']*')$/.test(family)) return true; + return family.length > 0 && family.split(/\s+/).every((name) => identifier.test(name) && !/^(?:inherit|initial|unset|revert|revert-layer|default)$/i.test(name)); + }); +} + export function validateProjectTheme(value: unknown): { valid: true; theme: ProjectTheme } | { valid: false; errors: Record } { const errors: Record = {}; if (!record(value)) return { valid: false, errors: { theme: 'Use a theme object.' } }; @@ -90,7 +114,7 @@ export function validateProjectTheme(value: unknown): { valid: true; theme: Proj if (seed.mode !== 'light' && seed.mode !== 'dark') errors.mode = 'Choose light or dark mode.'; if (seed.density !== 'comfortable' && seed.density !== 'compact') errors.density = 'Choose comfortable or compact density.'; try { - if ((typeof seed.ratio !== 'string' && typeof seed.ratio !== 'number') || (typeof seed.ratio === 'number' && !Number.isFinite(seed.ratio))) throw new Error('ratio'); + if ((typeof seed.ratio !== 'string' && typeof seed.ratio !== 'number') || (typeof seed.ratio === 'string' && !seed.ratio.trim()) || (typeof seed.ratio === 'number' && !Number.isFinite(seed.ratio))) throw new Error('ratio'); const ratio = resolveRatio(String(seed.ratio)); if (!Number.isFinite(ratio.value) || ratio.value <= 1 || ratio.value > 3) throw new Error('ratio'); } catch { errors.ratio = 'Use a supported ratio greater than 1 and no greater than 3.'; } @@ -100,7 +124,7 @@ export function validateProjectTheme(value: unknown): { valid: true; theme: Proj if (!record(value.fonts)) errors.fonts = 'Provide body, display, and mono font stacks.'; else for (const key of ['body', 'display', 'mono']) { const font = value.fonts[key]; - if (typeof font !== 'string' || font.trim().length === 0 || font.length > 200 || !safeValue(font) || /[()\\]/.test(font)) errors[`fonts.${key}`] = 'Use a CSS font stack with at most 200 characters.'; + if (typeof font !== 'string' || font.trim().length === 0 || font.length > 200 || !safeValue(font) || /[()\\]/.test(font) || !fontStack(font)) errors[`fonts.${key}`] = 'Use a CSS font stack with at most 200 characters.'; } if (value.overrides !== undefined) { if (!record(value.overrides)) errors.overrides = 'Use token family objects.'; @@ -113,7 +137,9 @@ export function validateProjectTheme(value: unknown): { valid: true; theme: Proj } } } - return Object.keys(errors).length ? { valid: false, errors } : { valid: true, theme: structuredClone(value) as ProjectTheme }; + if (Object.keys(errors).length) return { valid: false, errors }; + try { return { valid: true, theme: structuredClone(value) as ProjectTheme }; } + catch { return { valid: false, errors: { theme: 'Use serializable theme inputs.' } }; } } /** Validates portable inputs without discarding historical evidence. */ @@ -153,7 +179,8 @@ export function validateThemeProject(value: unknown): ThemeProjectValidation { } } if (Object.keys(errors).length) return { valid: false, errors }; - return { valid: true, project: structuredClone(value) as ThemeProject, errors: {} }; + try { return { valid: true, project: structuredClone(value) as ThemeProject, errors: {} }; } + catch { return { valid: false, errors: { project: 'Use serializable project inputs and evidence.' } }; } } export function createThemeProject(input: { id?: string; theme: ProjectTheme; viewports?: number[] }, _now = new Date().toISOString()): ThemeProject { @@ -178,13 +205,20 @@ export function restoreThemeProjectRevision(project: ThemeProject, revision: num } /** Produces deterministic JSON independently of object insertion order. */ export function canonicalProjectJson(value: unknown): string { + const serialized = JSON.stringify(value); + if (serialized === undefined) throw new Error('Canonical JSON requires serializable values.'); + const snapshot: unknown = JSON.parse(serialized); + return canonicalJsonSnapshot(snapshot); +} + +function canonicalJsonSnapshot(value: unknown): string { if (value === null || typeof value !== 'object') { const result = JSON.stringify(value); if (result === undefined) throw new Error('Canonical JSON requires serializable values.'); return result; } - if (Array.isArray(value)) return `[${value.map(canonicalProjectJson).join(',')}]`; - return `{${Object.entries(value).filter(([, item]) => item !== undefined).sort(([left], [right]) => left.localeCompare(right, 'en')).map(([key, item]) => `${JSON.stringify(key)}:${canonicalProjectJson(item)}`).join(',')}}`; + if (Array.isArray(value)) return `[${value.map(canonicalJsonSnapshot).join(',')}]`; + return `{${Object.entries(value).sort(([left], [right]) => left.localeCompare(right, 'en')).map(([key, item]) => `${JSON.stringify(key)}:${canonicalJsonSnapshot(item)}`).join(',')}}`; } export async function hashProjectValue(value: unknown): Promise { const bytes = new TextEncoder().encode(canonicalProjectJson(value)); @@ -216,14 +250,19 @@ export function validateThemeProjectPatch(value: unknown): { valid: true; patch: seen.add(key); } } - return errors.length ? { valid: false, errors } : { valid: true, patch: structuredClone(value) as ThemeProjectPatch }; + if (errors.length) return { valid: false, errors }; + try { return { valid: true, patch: structuredClone(value) as ThemeProjectPatch }; } + catch { return { valid: false, errors: ['Use serializable patch inputs.'] }; } } export async function applyThemeProjectPatch(project: ThemeProject, value: unknown, now = new Date().toISOString()): Promise { + const checked = validateThemeProject(project); + if (!checked.valid) throw new Error(Object.values(checked.errors).join(' ')); + const snapshot = checked.project; const result = validateThemeProjectPatch(value); if (!result.valid) throw new Error(result.errors.join(' ')); const patch = result.patch; - if (await projectIdentity(project) !== patch.projectIdentity) throw new Error('The patch belongs to a different theme revision.'); - const theme = structuredClone(project.theme); + if (await projectIdentity(snapshot) !== patch.projectIdentity) throw new Error('The patch belongs to a different theme revision.'); + const theme = structuredClone(snapshot.theme); theme.overrides ??= {}; for (const change of patch.changes) { let family = theme.overrides[change.family] ?? {}; @@ -232,5 +271,5 @@ export async function applyThemeProjectPatch(project: ThemeProject, value: unkno else family[change.token] = change.after; theme.overrides[change.family] = family; } - return reviseThemeProject(project, { theme }, patch.title, now); + return reviseThemeProject(snapshot, { theme }, patch.title, now); } diff --git a/packages/core/src/saliency.ts b/packages/core/src/saliency.ts index c5c2d1d..9bc25aa 100644 --- a/packages/core/src/saliency.ts +++ b/packages/core/src/saliency.ts @@ -1,4 +1,5 @@ import type { DesignDocument, DesignElement, DesignRole } from './design.ts'; +import { assertDesignDocument } from './geometry-validation.ts'; import { area, center, @@ -92,6 +93,7 @@ export function analyzeImportance( document: DesignDocument, mode: ImportanceMode = 'heuristic' ): ImportanceReport { + assertDesignDocument(document); const frameArea = Math.max(document.frame.width * document.frame.height, 1); const focusRegions: RectLike[] = []; if (document.background?.subjectRegion) { diff --git a/packages/core/src/scale-boundaries.test.ts b/packages/core/src/scale-boundaries.test.ts new file mode 100644 index 0000000..0af6567 --- /dev/null +++ b/packages/core/src/scale-boundaries.test.ts @@ -0,0 +1,39 @@ +import { describe, expect, it } from 'vitest'; +import { generateFibonacciScale, generateFluidScale, generateScale, resolveRatio } from './scale.ts'; + +describe('scale input and output boundaries', () => { + it.each(['toString', 'constructor', '__proto__', 'Infinity'])('rejects a nonnumeric ratio %s', (ratio) => { + expect(() => resolveRatio(ratio)).toThrow(); + }); + + it.each([Number.NaN, Number.POSITIVE_INFINITY, -16, 0])('rejects an invalid base %s in both static generators', (base) => { + expect(() => generateScale({ base })).toThrow(); + expect(() => generateFibonacciScale({ base })).toThrow(); + }); + + it.each([{ steps: -1 }, { steps: 1.5 }, { steps: Number.NaN }, { steps: Number.POSITIVE_INFINITY }, { steps: 10_001 }, { down: -1 }, { down: 1.5 }])('rejects invalid step counts %j', (options) => { + expect(() => generateScale(options)).toThrow(); + expect(() => generateFibonacciScale(options)).toThrow(); + expect(() => generateFluidScale(options)).toThrow(); + }); + + it.each([{ unit: 'cm' }, { naming: 'typo' }, { prefix: 'space; color:red' }])('rejects misleading or invalid token options %j', (options) => { + expect(() => generateScale(options)).toThrow(); + expect(() => generateFibonacciScale(options)).toThrow(); + }); + + it.each([{ baseMin: 20, baseMax: 10 }, { baseMin: 0 }, { baseMax: Number.POSITIVE_INFINITY }])('rejects invalid fluid sizes %j', (options) => { + expect(() => generateFluidScale(options)).toThrow(); + }); + + it('rejects finite inputs whose output overflows rather than returning invalid CSS', () => { + expect(() => generateScale({ base: Number.MAX_VALUE, ratio: '2', down: 0, steps: 1 })).toThrow(); + expect(() => generateFluidScale({ baseMin: Number.MAX_VALUE / 2, baseMax: Number.MAX_VALUE, ratio: '2', down: 0, steps: 1 })).toThrow(); + }); + + it('continues Fibonacci growth beyond the precomputed lookup instead of repeating its last value', () => { + const result = generateFibonacciScale({ base: 16, down: 0, steps: 9, unit: 'px' }); + expect(result.scale.slice(-3).map((step) => step.px)).toEqual([466, 754, 1220]); + expect(result.scale.every((step) => Number.isFinite(step.px))).toBe(true); + }); +}); diff --git a/packages/core/src/scale.d.ts b/packages/core/src/scale.d.ts index da43ebd..1ac533c 100644 --- a/packages/core/src/scale.d.ts +++ b/packages/core/src/scale.d.ts @@ -21,6 +21,7 @@ export declare function resolveRatio(val?: string): { value: number; }; export declare function stepName(step: number, naming: string): string; +/** Generates finite tokens with integer steps/down from 0 to 10,000 and px or rem units. */ export declare function generateScale(options: { base?: number; ratio?: string; @@ -34,6 +35,7 @@ export declare function generateScale(options: { scale: ScaleStep[]; }; export declare const FIBONACCI: number[]; +/** Extends the Fibonacci recurrence, rejecting overflow and counts outside 0 to 10,000. */ export declare function generateFibonacciScale(options?: { base?: number; steps?: number; @@ -56,7 +58,7 @@ export type FluidScaleMeta = ScaleMeta & { vwMin: number; vwMax: number; }; -/** Generates CSS clamp() values between finite viewport widths with 0 <= vwMin < vwMax. */ +/** Generates CSS clamp() values for ordered finite sizes/viewports, with 0 to 10,000 steps/down. */ export declare function generateFluidScale(options?: { baseMin?: number; baseMax?: number; diff --git a/packages/core/src/scale.ts b/packages/core/src/scale.ts index fbdbb09..3f90a04 100644 --- a/packages/core/src/scale.ts +++ b/packages/core/src/scale.ts @@ -1,3 +1,6 @@ +import { assertSafeCssCustomPropertyBody } from './css-safety.ts'; +import { assertFiniteOutput, assertGeneratedCount, assertPositiveFinite } from './numeric-validation.ts'; + export const RATIOS: Record = { 'minor-second': 16 / 15, 'major-second': 9 / 8, @@ -33,9 +36,9 @@ export type ScaleMeta = { export function resolveRatio(val?: string): { name: string; value: number } { if (!val || val === 'golden') return { name: 'golden', value: RATIOS.golden }; - if (RATIOS[val]) return { name: val, value: RATIOS[val] }; + if (Object.hasOwn(RATIOS, val)) return { name: val, value: RATIOS[val] }; const n = Number(val); - if (n > 1) return { name: 'custom', value: n }; + if (Number.isFinite(n) && n > 1) return { name: 'custom', value: n }; throw new Error( `Unknown ratio: ${val}. Use a name (${Object.keys(RATIOS).join(', ')}) or a number > 1.` ); @@ -59,6 +62,16 @@ function fmtPx(px: number): number { return parseFloat(px.toFixed(1)); } +function validateScaleOptions(base: number, steps: number, down: number, unit: string, prefix: string, naming: string): void { + assertPositiveFinite(base, 'Scale base'); + assertGeneratedCount(steps, 'Scale steps'); + assertGeneratedCount(down, 'Scale down'); + if (unit !== 'rem' && unit !== 'px') throw new Error('Scale unit must be rem or px.'); + if (naming !== 'natural' && naming !== 'signed') throw new Error('Scale naming must be natural or signed.'); + assertSafeCssCustomPropertyBody(prefix, 'scale prefix'); +} + +/** Generates finite tokens with integer steps/down from 0 to 10,000 and px or rem units. */ export function generateScale(options: { base?: number; ratio?: string; @@ -76,9 +89,12 @@ export function generateScale(options: { const prefix = options.prefix ?? 'space'; const naming = options.naming ?? 'natural'; + validateScaleOptions(base, steps, down, unit, prefix, naming); + const scale: ScaleStep[] = []; for (let i = -down; i <= steps; i++) { const px = base * Math.pow(ratio, i); + assertFiniteOutput(px, 'Scale step'); const name = stepName(i, naming); const token = `--${prefix}-${name}`; const value = unit === 'rem' ? `${fmtRem(px)}rem` : `${fmtPx(px)}px`; @@ -93,6 +109,7 @@ export function generateScale(options: { export const FIBONACCI = [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233]; +/** Extends the Fibonacci recurrence, rejecting overflow and counts outside 0 to 10,000. */ export function generateFibonacciScale(options: { base?: number; steps?: number; down?: number; unit?: string; prefix?: string; naming?: string; } = {}): { meta: ScaleMeta; scale: ScaleStep[] } { @@ -104,11 +121,20 @@ export function generateFibonacciScale(options: { const naming = options.naming ?? 'natural'; const centerIdx = 5; + validateScaleOptions(base, steps, down, unit, prefix, naming); + const fibonacci = [...FIBONACCI]; + while (fibonacci.length <= centerIdx + steps) { + const next = fibonacci[fibonacci.length - 1] + fibonacci[fibonacci.length - 2]; + assertFiniteOutput(next, 'Fibonacci step'); + fibonacci.push(next); + } + const scale: ScaleStep[] = []; for (let i = -down; i <= steps; i++) { const fibIdx = Math.max(0, centerIdx + i); - const fibVal = fibIdx >= 0 && fibIdx < FIBONACCI.length ? FIBONACCI[fibIdx] : FIBONACCI[FIBONACCI.length - 1]; - const px = base * fibVal / FIBONACCI[centerIdx]; + const fibVal = fibonacci[fibIdx]; + const px = base * (fibVal / FIBONACCI[centerIdx]); + assertFiniteOutput(px, 'Fibonacci scale step'); const name = stepName(i, naming); const token = `--${prefix}-${name}`; const value = unit === 'rem' ? `${fmtRem(px)}rem` : `${fmtPx(px)}px`; @@ -120,7 +146,7 @@ export function generateFibonacciScale(options: { export type FluidScaleStep = ScaleStep & { clamp: string; pxMin: number; pxMax: number }; export type FluidScaleMeta = ScaleMeta & { baseMin: number; baseMax: number; vwMin: number; vwMax: number }; -/** Generates CSS clamp() values between finite viewport widths with 0 <= vwMin < vwMax. */ +/** Generates CSS clamp() values for ordered finite sizes/viewports, with 0 to 10,000 steps/down. */ export function generateFluidScale(options: { baseMin?: number; baseMax?: number; ratio?: string; steps?: number; down?: number; prefix?: string; naming?: string; vwMin?: number; vwMax?: number; @@ -135,6 +161,10 @@ export function generateFluidScale(options: { const vwMin = options.vwMin ?? 320; const vwMax = options.vwMax ?? 1440; + validateScaleOptions(baseMin, steps, down, 'rem', prefix, naming); + assertPositiveFinite(baseMax, 'Fluid maximum base'); + if (baseMax < baseMin) throw new Error('Fluid maximum base must be at least the minimum base.'); + if (!Number.isFinite(vwMin) || !Number.isFinite(vwMax) || vwMin < 0 || vwMax <= vwMin) { throw new Error('Fluid viewport widths must be finite, with vw-min >= 0 and vw-max > vw-min.'); } @@ -143,6 +173,8 @@ export function generateFluidScale(options: { for (let i = -down; i <= steps; i++) { const pxMin = baseMin * Math.pow(ratio, i); const pxMax = baseMax * Math.pow(ratio, i); + assertFiniteOutput(pxMin, 'Fluid minimum step'); + assertFiniteOutput(pxMax, 'Fluid maximum step'); const remMin = fmtRem(pxMin); const remMax = fmtRem(pxMax); const name = stepName(i, naming); @@ -151,6 +183,8 @@ export function generateFluidScale(options: { const intercept = pxMin - slope * vwMin; const interceptRem = parseFloat((intercept / 16).toFixed(4)); const slopeVw = parseFloat((slope * 100).toFixed(3)); + assertFiniteOutput(interceptRem, 'Fluid intercept'); + assertFiniteOutput(slopeVw, 'Fluid slope'); const clamp = `clamp(${remMin}rem, ${interceptRem}rem + ${slopeVw}vw, ${remMax}rem)`; scale.push({ step: i, name, token, value: clamp, px: fmtPx(pxMax), rem: remMax, clamp, pxMin: fmtPx(pxMin), pxMax: fmtPx(pxMax) }); } diff --git a/packages/core/src/token-expression-boundaries.test.ts b/packages/core/src/token-expression-boundaries.test.ts new file mode 100644 index 0000000..0054fb3 --- /dev/null +++ b/packages/core/src/token-expression-boundaries.test.ts @@ -0,0 +1,32 @@ +import { describe, expect, it } from 'vitest'; +import { resolveTokenExpr } from './component-compiler.ts'; +import type { ThemeContract } from './theme-contract.ts'; + +function theme(): ThemeContract { + return { + name: 'Fluid', seed: { color: '#295dff', ratio: 'perfect-fourth', mode: 'light', density: 'comfortable', motion: 'snappy' }, + meta: { optimizedSeed: '#295dff', paletteScore: 1, mode: 'light', density: 'comfortable', ratioName: 'perfect-fourth', ratioValue: 4 / 3 }, + families: { color: {}, space: { base: 'clamp(1rem, 0.5rem + 1vw, 2rem)' }, type: {}, radius: {}, elevation: {}, motion: {}, state: {} }, + aliases: { spacing: 'space.base', a: 'b', b: 'a' } + }; +} + +describe('theme expression boundaries', () => { + it('scales the complete responsive length rather than freezing its maximum bound', () => { + expect(resolveTokenExpr(theme(), { mul: [{ alias: 'spacing' }, 0.5] })).toBe('clamp(0.5rem, 0.25rem + 0.5vw, 1rem)'); + }); + + it('preserves affine units and reverses bounds when a responsive length is multiplied negatively', () => { + expect(resolveTokenExpr(theme(), { mul: [{ literal: 'clamp(8px, calc(4px + 1vw - 0.5vh), 16px)' }, -0.5] })).toBe('clamp(-8px, -2px - 0.5vw + 0.25vh, -4px)'); + }); + + it.each(['space.toString', 'constructor.foo', 'space.base.extra', 'toString'])('rejects malformed or inherited reference %s', (ref) => { + expect(() => resolveTokenExpr(theme(), { ref })).toThrow(); + }); + + it('reports genuine alias cycles and rejects nonfinite scaling before CSS emission', () => { + expect(() => resolveTokenExpr(theme(), { alias: 'a' })).toThrow(/Circular/); + expect(() => resolveTokenExpr(theme(), { mul: [{ literal: '10px' }, Number.POSITIVE_INFINITY] })).toThrow(); + expect(() => resolveTokenExpr(theme(), { mul: [{ literal: '10px' }, Number.MAX_VALUE] })).toThrow(); + }); +}); diff --git a/packages/core/src/typeset-discretionary-hyphen.test.ts b/packages/core/src/typeset-discretionary-hyphen.test.ts new file mode 100644 index 0000000..beaae9b --- /dev/null +++ b/packages/core/src/typeset-discretionary-hyphen.test.ts @@ -0,0 +1,44 @@ +import { describe, expect, it } from 'vitest'; +import { flowPreparedLinesByWidth } from './linebreak.ts'; +import { layoutPreparedNextLine, prepareTypesetParagraph, typesetParagraph } from './typeset.ts'; + +const recovery = 'Flow recovers all words.'; + +describe('discretionary hyphen reconstruction', () => { + it.each(['normal', 'pre-wrap'] as const)('preserves a complete word on one line in %s mode', (whiteSpace) => { + const options = { text: recovery, fontSize: 12, lineHeight: 1.5, language: 'en', whiteSpace, engine: 'advanced' as const }; + const result = typesetParagraph({ ...options, widthPx: 280, hyphenate: true }); + const unhyphenated = typesetParagraph({ ...options, widthPx: 280, hyphenate: false }); + expect(result.lines.map((line) => line.text)).toEqual([recovery]); + expect(result.lines[0].width).toBe(unhyphenated.lines[0].width); + expect(result.lines[0].paintWidth).toBe(unhyphenated.lines[0].paintWidth); + expect(result.usedHyphenation).toBe(false); + }); + + it('preserves the exact recovery text through prepared lead/body flow', () => { + const prepared = prepareTypesetParagraph({ text: recovery, fontSize: 12, lineHeight: 1.5, language: 'en', hyphenate: true, whiteSpace: 'pre-wrap', engine: 'advanced' }); + expect(layoutPreparedNextLine(prepared, { chunkIndex: 0, segmentIndex: 0 }, 160)?.text).toBe(recovery); + const flow = flowPreparedLinesByWidth(prepared, [{ label: 'lead', widthPx: 160, maxLines: 2 }, { label: 'body', widthPx: 220 }]); + expect(flow.lines.map((line) => line.text)).toEqual([recovery]); + expect(flow.usedAllText).toBe(true); + }); + + it('adds and measures a discretionary hyphen only when the word actually crosses lines', () => { + const options = { text: 'recovers', fontSize: 12, language: 'en', hyphenate: true, whiteSpace: 'pre-wrap' as const }; + const result = typesetParagraph({ ...options, widthPx: 33 }); + expect(result.lines.map((line) => line.text)).toEqual(['reco-', 'vers']); + expect(result.lines[0].width).toBeCloseTo((4 * 0.56 + 0.36) * 12, 2); + expect(result.usedHyphenation).toBe(true); + const prepared = prepareTypesetParagraph(options); + const flow = flowPreparedLinesByWidth(prepared, [{ widthPx: 33, maxLines: 1 }, { widthPx: 220 }]); + expect(flow.lines.map((line) => line.text)).toEqual(['reco-', 'vers']); + expect(flow.usedAllText).toBe(true); + }); + + it('keeps authored hyphens and whitespace without claiming discretionary hyphenation', () => { + const text = ' recovers\twith real-hyphens - '; + const result = typesetParagraph({ text, widthPx: 400, fontSize: 12, language: 'en', hyphenate: true, whiteSpace: 'pre-wrap' }); + expect(result.lines.map((line) => line.text)).toEqual([text]); + expect(result.usedHyphenation).toBe(false); + }); +}); diff --git a/packages/core/src/typeset.d.ts b/packages/core/src/typeset.d.ts index 4304668..db5d954 100644 --- a/packages/core/src/typeset.d.ts +++ b/packages/core/src/typeset.d.ts @@ -19,6 +19,8 @@ export type PreparedTypesetSegment = { text: string; width: number; canHang: boolean; + /** Width of the hyphen rendered only when a line ends after this segment. */ + discretionaryHyphenWidth?: number; }; export type PreparedTypesetChunk = { text: string; diff --git a/packages/core/src/typeset.ts b/packages/core/src/typeset.ts index 38c8af9..db3531f 100644 --- a/packages/core/src/typeset.ts +++ b/packages/core/src/typeset.ts @@ -1,4 +1,5 @@ import { clamp01, round, type EngineMode, type WhiteSpaceMode } from './types.ts'; +import { assertFiniteOutput, assertIntegerCount, assertPositiveFinite } from './numeric-validation.ts'; const TAB_SIZE = 8; const ENGLISH_VOWEL_PATTERN = /[aeiouy]/i; @@ -25,6 +26,8 @@ export type PreparedTypesetSegment = { text: string; width: number; canHang: boolean; + /** Width of the hyphen rendered only when a line ends after this segment. */ + discretionaryHyphenWidth?: number; }; export type PreparedTypesetChunk = { @@ -108,6 +111,7 @@ export type TypesetResult = { type TypesetChunkResult = { lines: TypesetLine[]; badness: number; + usedHyphenation: boolean; }; function glyphFactor(char: string): number { @@ -138,7 +142,9 @@ function glyphFactor(char: string): number { function measure(text: string, fontSize: number, opticalSizing: boolean): number { const optical = opticalSizing ? 0.985 : 1; const units = Array.from(text).reduce((sum, char) => sum + glyphFactor(char), 0); - return units * fontSize * optical; + const width = units * fontSize * optical; + assertFiniteOutput(width, 'Typeset text width'); + return width; } function isEnglishVowel(char: string): boolean { @@ -198,6 +204,8 @@ function hyphenateWord(word: string, language: string): string[] { } function normalizePreparationOptions(options: PrepareTypesetOptions): PreparedTypesetParagraph { + assertPositiveFinite(options.fontSize, 'Typeset font size'); + assertPositiveFinite(options.lineHeight ?? 1.5, 'Typeset line height'); return { engine: options.engine ?? 'advanced', whiteSpace: options.whiteSpace ?? 'normal', @@ -222,11 +230,12 @@ function pushWordSegments( for (let index = 0; index < pieces.length; index += 1) { const isLastPiece = index === pieces.length - 1; - const text = isLastPiece ? pieces[index] : `${pieces[index]}-`; + const text = pieces[index]; segments.push({ text, width: measure(text, prepared.fontSize, prepared.opticalSizing), - canHang: false + canHang: false, + ...(isLastPiece ? {} : { discretionaryHyphenWidth: measure('-', prepared.fontSize, prepared.opticalSizing) }) }); } } @@ -303,7 +312,8 @@ function lineText( .slice(start, end) .map((segment) => segment.text) .join(''); - return whiteSpace === 'pre-wrap' ? text : text.trim(); + const visibleText = whiteSpace === 'pre-wrap' ? text : text.trim(); + return segments[end - 1]?.discretionaryHyphenWidth !== undefined ? `${visibleText}-` : visibleText; } function lineWidths( @@ -313,7 +323,8 @@ function lineWidths( whiteSpace: WhiteSpaceMode ): { fitWidth: number; paintWidth: number } { const slice = segments.slice(start, end); - const paintWidth = slice.reduce((sum, segment) => sum + segment.width, 0); + const hyphenWidth = segments[end - 1]?.discretionaryHyphenWidth ?? 0; + const paintWidth = slice.reduce((sum, segment) => sum + segment.width, 0) + hyphenWidth; let fitEnd = slice.length; if (whiteSpace === 'pre-wrap') { @@ -323,7 +334,7 @@ function lineWidths( } return { - fitWidth: slice.slice(0, fitEnd).reduce((sum, segment) => sum + segment.width, 0), + fitWidth: slice.slice(0, fitEnd).reduce((sum, segment) => sum + segment.width, 0) + hyphenWidth, paintWidth }; } @@ -355,7 +366,8 @@ function typesetPreparedChunk( if (segments.length === 0) { return { lines: [createLine('', 0, 0, widthPx)], - badness: 0 + badness: 0, + usedHyphenation: false }; } @@ -375,13 +387,14 @@ function typesetPreparedChunk( if (!text && fitWidth === 0) { continue; } - if (fitWidth > limit) { + if (fitWidth > limit && end > start + 1) { break; } const leftover = Math.max(0, widthPx - fitWidth); const isLastLine = end === segments.length; - const ragPenalty = isLastLine && targetLastChunk ? leftover * leftover * 0.16 : leftover * leftover; + const overflow = Math.max(0, fitWidth - widthPx); + const ragPenalty = overflow * overflow + (isLastLine && targetLastChunk ? leftover * leftover * 0.16 : leftover * leftover); const visibleText = whiteSpace === 'pre-wrap' ? text.replace(/[ \t]+$/g, '').trim() : text; const lengthPenalty = visibleText.length > 0 && visibleText.length <= 2 ? 1200 : 0; const hyphenPenalty = visibleText.endsWith('-') ? 120 : 0; @@ -399,6 +412,7 @@ function typesetPreparedChunk( targetLastChunk }) ?? 0; const nextCost = costs[start] + ragPenalty + lengthPenalty + hyphenPenalty + customPenalty; + assertFiniteOutput(nextCost, 'Typeset line badness'); if (nextCost < costs[end]) { costs[end] = nextCost; @@ -408,12 +422,14 @@ function typesetPreparedChunk( } const lines: TypesetLine[] = []; + let usedHyphenation = false; let cursor = segments.length; while (cursor > 0 && breaks[cursor] >= 0) { const start = breaks[cursor]; const text = lineText(segments, start, cursor, whiteSpace); const { fitWidth, paintWidth } = lineWidths(segments, start, cursor, whiteSpace); lines.unshift(createLine(text, fitWidth, paintWidth, widthPx)); + usedHyphenation ||= segments[cursor - 1]?.discretionaryHyphenWidth !== undefined; cursor = start; } @@ -421,11 +437,13 @@ function typesetPreparedChunk( const text = lineText(segments, 0, cursor, whiteSpace); const { fitWidth, paintWidth } = lineWidths(segments, 0, cursor, whiteSpace); lines.unshift(createLine(text, fitWidth, paintWidth, widthPx)); + usedHyphenation ||= segments[cursor - 1]?.discretionaryHyphenWidth !== undefined; } return { lines, - badness: Number.isFinite(costs[segments.length]) ? costs[segments.length] : 0 + badness: Number.isFinite(costs[segments.length]) ? costs[segments.length] : 0, + usedHyphenation }; } @@ -526,6 +544,8 @@ export function layoutPreparedParagraphWithPenalty( linePenalty?: TypesetLinePenaltyFn; } = {} ): TypesetResult { + assertPositiveFinite(widthPx, 'Typeset measure'); + if (options.targetLines !== undefined) assertIntegerCount(options.targetLines, 'Target lines', 1); if (prepared.chunks.length === 0) { return { engine: prepared.engine, @@ -545,6 +565,7 @@ export function layoutPreparedParagraphWithPenalty( const lines: TypesetLine[] = []; let chunkBadness = 0; + let usedHyphenation = false; for (const [index, chunk] of prepared.chunks.entries()) { const result = typesetPreparedChunk( @@ -558,6 +579,7 @@ export function layoutPreparedParagraphWithPenalty( ); lines.push(...result.lines); chunkBadness += result.badness; + usedHyphenation ||= result.usedHyphenation; } const widths = lines.map((line) => line.width); @@ -566,6 +588,8 @@ export function layoutPreparedParagraphWithPenalty( widths.reduce((sum, value) => sum + (value - averageWidth) ** 2, 0) / Math.max(widths.length, 1); const targetPenalty = options.targetLines ? Math.abs(lines.length - options.targetLines) * widthPx * 2.2 : 0; const maxLineWidth = round(Math.max(0, ...widths)); + assertFiniteOutput(chunkBadness + targetPenalty + variance * 0.08, 'Typeset paragraph badness'); + assertFiniteOutput(lines.length * prepared.fontSize * prepared.lineHeight, 'Typeset paragraph height'); return { engine: prepared.engine, @@ -574,7 +598,7 @@ export function layoutPreparedParagraphWithPenalty( badness: Math.round(chunkBadness + targetPenalty + variance * 0.08), averageWidth: round(averageWidth), variance: round(variance), - usedHyphenation: lines.some((line) => line.text.replace(/[ \t]+$/g, '').endsWith('-')), + usedHyphenation, lineCount: lines.length, maxLineWidth, heightPx: round(lines.length * prepared.fontSize * prepared.lineHeight), @@ -588,6 +612,7 @@ export function layoutPreparedNextLine( start: TypesetCursor, widthPx: number ): PreparedTypesetLine | null { + assertPositiveFinite(widthPx, 'Typeset measure'); let chunkIndex = start.chunkIndex; let segmentIndex = start.segmentIndex; const limit = widthPx * 1.08; diff --git a/packages/core/src/typography.ts b/packages/core/src/typography.ts index a92d3df..d620277 100644 --- a/packages/core/src/typography.ts +++ b/packages/core/src/typography.ts @@ -2,6 +2,7 @@ import { typesetParagraph, type TypesetOptions, type TypesetResult } from './typeset.ts'; import type { EngineMode, WhiteSpaceMode } from './types.ts'; +import { assertFiniteOutput, assertPositiveFinite } from './numeric-validation.ts'; export type TypographyProfile = 'default' | 'low-vision'; @@ -47,11 +48,14 @@ function scoreCrowdingRisk( } export function recommendTypography(input: TypographyInput): TypographyRecommendation { + assertPositiveFinite(input.fontSize, 'Typography font size'); + assertPositiveFinite(input.containerWidth, 'Typography container width'); const engine = input.engine ?? 'basic'; const profile = input.profile ?? 'default'; const contrastLc = input.contrastLc ?? 75; const averageGlyphWidth = input.fontSize * 0.52; const charactersPerLine = parseFloat((input.containerWidth / averageGlyphWidth).toFixed(1)); + assertFiniteOutput(charactersPerLine, 'Characters per line'); let lineHeight = 1.45; let letterSpacingEm = 0; diff --git a/packages/tokens/src/create-project-theme.test.ts b/packages/tokens/src/create-project-theme.test.ts index 1a8e750..27e6130 100644 --- a/packages/tokens/src/create-project-theme.test.ts +++ b/packages/tokens/src/create-project-theme.test.ts @@ -53,3 +53,7 @@ it.each(['transparent', 'rgba(0, 0, 0, 0)', '#0000', 'rgba(0, 0, 0, 0.9999)', 'r it('rejects a static CSS length that converts to infinity', () => { expect(() => createProjectTheme({ ...config, overrides: { space: { md: `${'9'.repeat(400)}px` } } })).toThrow(/length/i); }); + +it.each([`${'9'.repeat(400)}ms`, `1${'0'.repeat(306)}s`])('rejects a duration that the mathematical motion proof cannot resolve to finite milliseconds', (normal) => { + expect(() => createProjectTheme({ ...config, overrides: { motion: { normal } } })).toThrow(/duration/i); +}); diff --git a/packages/tokens/src/create-project-theme.ts b/packages/tokens/src/create-project-theme.ts index 2c4ef99..d63c45b 100644 --- a/packages/tokens/src/create-project-theme.ts +++ b/packages/tokens/src/create-project-theme.ts @@ -8,6 +8,7 @@ function supportedLength(text: string): boolean { if (!clamp) return false; const center = clamp[3].trim().replace(/^calc\((.*)\)$/i, '$1'); if (!/^(?:0|-?\d*\.?\d+(?:px|rem|em|vw|vh|%)(?:\s+[+-]\s+\d*\.?\d+(?:px|rem|em|vw|vh|%))*)$/i.test(center)) return false; + if (![...center.matchAll(/-?\d*\.?\d+/g)].every((coefficient) => Number.isFinite(Number(coefficient[0])))) return false; const min = Number(clamp[1]) * (clamp[2].toLowerCase() === 'px' ? 1 : 16); const max = Number(clamp[4]) * (clamp[5].toLowerCase() === 'px' ? 1 : 16); return Number.isFinite(min) && Number.isFinite(max) && min <= max; @@ -28,7 +29,8 @@ function validateToken(family: ProjectTokenFamily, token: string, value: string if (family === 'type' && token.startsWith('font-')) throw new Error('Edit font stacks through the fonts configuration.'); if (family === 'motion') { if (token === 'preset') throw new Error('Edit the motion preset through the theme seed.'); - if (!DURATION.test(text)) throw new Error(`Use a nonnegative CSS duration for ${family}.${token}.`); + const milliseconds = Number.parseFloat(text) * (text.endsWith('ms') ? 1 : 1000); + if (!DURATION.test(text) || !Number.isFinite(milliseconds)) throw new Error(`Use a finite nonnegative CSS duration for ${family}.${token}.`); } if (family === 'state') throw new Error('Edit state tokens through the theme seed.'); } @@ -42,14 +44,6 @@ export function createProjectTheme(input: ProjectTheme): ThemeContract { theme.families.type['font-body'] = config.fonts.body; theme.families.type['font-display'] = config.fonts.display; theme.families.type['font-mono'] = config.fonts.mono; - const durations: Record = { - snappy: ['120ms', '200ms', '320ms'], - calm: ['180ms', '300ms', '480ms'], - expressive: ['160ms', '280ms', '440ms'], - reduced: ['0ms', '0ms', '0ms'] - }; - const [fast, normal, slow] = durations[config.seed.motion]; - Object.assign(theme.families.motion, { fast, normal, slow }); for (const [family, values] of Object.entries(config.overrides ?? {})) { const key = family as ProjectTokenFamily; for (const [token, value] of Object.entries(values)) { diff --git a/packages/tokens/src/create-theme.d.ts b/packages/tokens/src/create-theme.d.ts index 0e64079..396ccaa 100644 --- a/packages/tokens/src/create-theme.d.ts +++ b/packages/tokens/src/create-theme.d.ts @@ -5,4 +5,4 @@ export type CreateThemeOptions = { seed: ThemeSeed; }; /** Compiles token families and semantic aliases from a theme seed. */ -export declare function createTheme({ name, seed }: CreateThemeOptions): ThemeContract; +export declare function createTheme({ name, seed: inputSeed }: CreateThemeOptions): ThemeContract; diff --git a/packages/tokens/src/create-theme.test.ts b/packages/tokens/src/create-theme.test.ts index 9abfd8d..80663e7 100644 --- a/packages/tokens/src/create-theme.test.ts +++ b/packages/tokens/src/create-theme.test.ts @@ -4,6 +4,44 @@ import { createTheme } from './create-theme.ts'; import { emitThemeCss } from './emit-css.ts'; describe('@dkcli/tokens createTheme', () => { + it.each([ + ['snappy', ['120ms', '200ms', '320ms']], + ['reduced', ['0ms', '0ms', '0ms']], + ['calm', ['180ms', '300ms', '480ms']], + ['expressive', ['160ms', '280ms', '440ms']] + ] as const)('exports the authored %s motion preset', (motion, durations) => { + const contract = createTheme({ + name: 'Motion', + seed: { color: '#295dff', ratio: 'perfect-fourth', mode: 'light', density: 'comfortable', motion } + }); + const css = emitThemeCss(contract); + expect([contract.families.motion.fast, contract.families.motion.normal, contract.families.motion.slow]).toEqual(durations); + expect(contract.seed.motion).toBe(motion); + expect(contract.families.motion.preset).toBe(motion); + expect(css).toContain(`--motion-normal: ${durations[1]};`); + }); + + it.each(['smooth', 'custom-motion', 'toString', '__proto__'])('retains complete fallback durations and metadata for legacy preset %s', (motion) => { + const contract = createTheme({ + name: 'Legacy motion', + seed: { color: '#295dff', ratio: 'perfect-fourth', mode: 'light', density: 'comfortable', motion } + }); + expect([contract.families.motion.fast, contract.families.motion.normal, contract.families.motion.slow]).toEqual(['120ms', '200ms', '320ms']); + expect(contract.seed.motion).toBe(motion); + expect(contract.families.motion.preset).toBe(motion); + expect(emitThemeCss(contract)).toContain('--motion-normal: 200ms;'); + }); + + it('keeps compiled seed metadata independent of later caller edits', () => { + const seed = { color: '#295dff', ratio: 'perfect-fourth', mode: 'light', density: 'comfortable', motion: 'snappy' } as const; + const input = { ...seed }; + const contract = createTheme({ name: 'Snapshot', seed: input }); + Object.assign(input, { color: '#c44724', mode: 'dark', motion: 'reduced' }); + expect(contract.seed).toEqual(seed); + expect(contract.seed.mode).toBe(contract.meta.mode); + expect(contract.seed.motion).toBe(contract.families.motion.preset); + }); + it('compiles actual palette and scale families from the seed', () => { const contract = createTheme({ name: 'Ocean', diff --git a/packages/tokens/src/create-theme.ts b/packages/tokens/src/create-theme.ts index b315169..91da8cf 100644 --- a/packages/tokens/src/create-theme.ts +++ b/packages/tokens/src/create-theme.ts @@ -15,6 +15,13 @@ export type CreateThemeOptions = { seed: ThemeSeed; }; +const MOTION_DURATIONS: Record = { + snappy: { fast: '120ms', normal: '200ms', slow: '320ms' }, + calm: { fast: '180ms', normal: '300ms', slow: '480ms' }, + expressive: { fast: '160ms', normal: '280ms', slow: '440ms' }, + reduced: { fast: '0ms', normal: '0ms', slow: '0ms' } +}; + function colorHex(value: string | { hex: string }): string { return typeof value === 'string' ? value : value.hex; } @@ -28,7 +35,11 @@ function scaleValue(scale: FluidScaleStep[], name: string, fallback: string): st } /** Compiles token families and semantic aliases from a theme seed. */ -export function createTheme({ name, seed }: CreateThemeOptions): ThemeContract { +export function createTheme({ name, seed: inputSeed }: CreateThemeOptions): ThemeContract { + const seed = { ...inputSeed }; + const motionDurations = Object.hasOwn(MOTION_DURATIONS, seed.motion) + ? MOTION_DURATIONS[seed.motion] + : MOTION_DURATIONS.snappy; const optimized = optimizePalette(seed.color, { engine: 'advanced', goal: 'ui', @@ -101,9 +112,7 @@ export function createTheme({ name, seed }: CreateThemeOptions): ThemeContract { }, motion: { preset: seed.motion, - fast: '120ms', - normal: '200ms', - slow: '320ms' + ...motionDurations }, state: { mode: seed.mode, diff --git a/packages/tokens/src/emit-css.ts b/packages/tokens/src/emit-css.ts index f0e0671..aaff709 100644 --- a/packages/tokens/src/emit-css.ts +++ b/packages/tokens/src/emit-css.ts @@ -5,6 +5,7 @@ import { escapeCssComment, type ThemeContract } from '@dkcli/core'; +import { assertThemeExportable } from './validate-export.ts'; function emitFamily(prefix: string, family: Record): string[] { return Object.entries(family).map(([name, value]) => { @@ -31,6 +32,7 @@ function resolveAlias(value: string, aliases: ThemeContract['aliases']): string /** Serializes theme families and aliases as CSS custom properties in a `:root` rule. */ export function emitThemeCss(contract: ThemeContract): string { + assertThemeExportable(contract); const lines = [ `/* ${escapeCssComment(contract.name)} */`, `/* optimized seed ${escapeCssComment(contract.meta.optimizedSeed)} ratio ${escapeCssComment(contract.meta.ratioName)} */`, diff --git a/packages/tokens/src/emit-json.ts b/packages/tokens/src/emit-json.ts index ef2e617..73d9eda 100644 --- a/packages/tokens/src/emit-json.ts +++ b/packages/tokens/src/emit-json.ts @@ -1,6 +1,8 @@ import type { ThemeContract } from '@dkcli/core'; +import { assertThemeExportable } from './validate-export.ts'; /** Serializes a DesignKit `ThemeContract` as JSON. */ export function emitThemeJson(contract: ThemeContract): string { + assertThemeExportable(contract); return JSON.stringify(contract, null, 2); } diff --git a/packages/tokens/src/export-boundaries.test.ts b/packages/tokens/src/export-boundaries.test.ts new file mode 100644 index 0000000..8ae7d40 --- /dev/null +++ b/packages/tokens/src/export-boundaries.test.ts @@ -0,0 +1,28 @@ +import { describe, expect, it } from 'vitest'; +import { createTheme } from './create-theme.ts'; +import { createProjectTheme } from './create-project-theme.ts'; +import { emitThemeCss } from './emit-css.ts'; +import { emitThemeJson } from './emit-json.ts'; + +function theme(): ReturnType { + return createTheme({ name: 'Export', seed: { color: '#295dff', ratio: 'perfect-fourth', mode: 'light', density: 'comfortable', motion: 'snappy' } }); +} + +describe('theme export integrity', () => { + it('rejects a nonfinite token instead of exporting Infinity CSS or null JSON', () => { + const contract = theme(); + contract.families.space.base = Number.POSITIVE_INFINITY; + expect(() => emitThemeCss(contract)).toThrow(); + expect(() => emitThemeJson(contract)).toThrow(); + }); + it.each>([{ a: 'b', b: 'a' }, { a: 'space.missing' }])('rejects unresolved aliases %j on both export paths', (aliases) => { + const contract = theme(); + contract.aliases = aliases; + expect(() => emitThemeCss(contract)).toThrow(); + expect(() => emitThemeJson(contract)).toThrow(); + }); + it('rejects a fluid override with a nonfinite affine coefficient despite finite clamp bounds', () => { + const contract = theme(); + expect(() => createProjectTheme({ name: 'Export', seed: contract.seed, fonts: { body: 'sans-serif', display: 'serif', mono: 'monospace' }, overrides: { space: { base: `clamp(1rem, 1rem + ${'1'.repeat(400)}vw, 2rem)` } } })).toThrow(); + }); +}); diff --git a/packages/tokens/src/validate-export.d.ts b/packages/tokens/src/validate-export.d.ts new file mode 100644 index 0000000..81f2ad5 --- /dev/null +++ b/packages/tokens/src/validate-export.d.ts @@ -0,0 +1,3 @@ +import { type ThemeContract } from '@dkcli/core'; +/** Rejects token data that cannot produce the same resolvable CSS and JSON theme. */ +export declare function assertThemeExportable(contract: ThemeContract): void; diff --git a/packages/tokens/src/validate-export.ts b/packages/tokens/src/validate-export.ts new file mode 100644 index 0000000..88d6974 --- /dev/null +++ b/packages/tokens/src/validate-export.ts @@ -0,0 +1,29 @@ +import { assertSafeCssValue, type ThemeContract } from '@dkcli/core'; + +/** Rejects token data that cannot produce the same resolvable CSS and JSON theme. */ +export function assertThemeExportable(contract: ThemeContract): void { + for (const [family, values] of Object.entries(contract.families)) { + for (const [token, value] of Object.entries(values)) { + if ((typeof value !== 'string' && typeof value !== 'number') || (typeof value === 'number' && !Number.isFinite(value))) { + throw new Error(`Use a finite number or CSS string for ${family}.${token}.`); + } + assertSafeCssValue(value, `${family}.${token}`); + } + } + for (const name of Object.keys(contract.aliases)) { + let value = name; + const seen = new Set(); + while (Object.hasOwn(contract.aliases, value)) { + if (seen.has(value)) throw new Error(`Circular theme alias detected for "${name}".`); + seen.add(value); + value = contract.aliases[value]; + } + const reference = value.match(/^([a-z-]+)\.([a-z0-9_-]+)$/i); + if (reference) { + const family = contract.families[reference[1] as keyof ThemeContract['families']]; + if (!Object.hasOwn(contract.families, reference[1]) || !family || !Object.hasOwn(family, reference[2])) { + throw new Error(`Unknown theme token "${value}" in alias "${name}".`); + } + } else assertSafeCssValue(value, `alias ${name}`); + } +} diff --git a/qualification/reviewed-math-policy-20261004.json b/qualification/reviewed-math-policy-20261004.json new file mode 100644 index 0000000..3f428dd --- /dev/null +++ b/qualification/reviewed-math-policy-20261004.json @@ -0,0 +1,8666 @@ +{ + "schemaVersion": 1, + "evidence": { + "kind": "mathematical", + "rendered": "not-collected" + }, + "assumptions": [ + "This review applies only to the original Browser test project content and recorded widths.", + "Reviewed findings remain failed conservative single-line/max-token estimates; they are not rendered passes.", + "No other failure category or unsupported proof is waived." + ], + "project": { + "inputSha256": "f26c9f3b852c7e0a2a764146671a27869296f75f1bfcd13bd2a9049a29aa1fd6", + "projectIdentity": "336ee2707a33d648d9ff9a754a6712062034e2ba55443c5c00235e8723376050", + "projectId": "browser-qualification", + "revision": 1, + "viewports": [ + 320, + 768, + 1280 + ], + "fixtureCount": 180, + "reviewedFailures": [ + { + "componentId": "accordion", + "id": "size=md__open", + "name": "accordion-md-open", + "caseKey": "size=md", + "axes": { + "size": "md" + }, + "states": [ + "open" + ], + "props": {}, + "sampleText": "Deployment timeline and stakeholder notes", + "layout": [ + { + "target": "panel", + "widths": [ + 320, + 420, + 768, + 1280 + ], + "heights": [ + 160 + ], + "estimatedInlinePx": 399.36, + "requiredBlockPx": 128, + "widthChecks": [ + { + "width": 320, + "fitsWidth": false, + "fitsHeight": true, + "pass": false + }, + { + "width": 420, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + } + ], + "pass": false + } + ] + }, + { + "componentId": "badge", + "id": "emphasis=soft|size=md|tone=success__rest", + "name": "success-soft", + "caseKey": "emphasis=soft|size=md|tone=success", + "axes": { + "tone": "success", + "emphasis": "soft", + "size": "md" + }, + "states": [ + "rest" + ], + "props": {}, + "sampleText": "Healthy", + "layout": [ + { + "target": "root", + "widths": [ + 120, + 200, + 320, + 768, + 1280 + ], + "heights": [ + 30 + ], + "estimatedInlinePx": 134.57, + "requiredBlockPx": 30, + "widthChecks": [ + { + "width": 120, + "fitsWidth": false, + "fitsHeight": true, + "pass": false + }, + { + "width": 200, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + } + ], + "pass": false + } + ] + }, + { + "componentId": "badge", + "id": "emphasis=solid|size=md|tone=success__rest", + "name": "success-solid", + "caseKey": "emphasis=solid|size=md|tone=success", + "axes": { + "tone": "success", + "emphasis": "solid", + "size": "md" + }, + "states": [ + "rest" + ], + "props": {}, + "sampleText": "Healthy", + "layout": [ + { + "target": "root", + "widths": [ + 120, + 200, + 320, + 768, + 1280 + ], + "heights": [ + 30 + ], + "estimatedInlinePx": 134.57, + "requiredBlockPx": 30, + "widthChecks": [ + { + "width": 120, + "fitsWidth": false, + "fitsHeight": true, + "pass": false + }, + { + "width": 200, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + } + ], + "pass": false + } + ] + }, + { + "componentId": "badge", + "id": "emphasis=outline|size=md|tone=success__rest", + "name": "success-outline", + "caseKey": "emphasis=outline|size=md|tone=success", + "axes": { + "tone": "success", + "emphasis": "outline", + "size": "md" + }, + "states": [ + "rest" + ], + "props": {}, + "sampleText": "Healthy", + "layout": [ + { + "target": "root", + "widths": [ + 120, + 200, + 320, + 768, + 1280 + ], + "heights": [ + 30 + ], + "estimatedInlinePx": 134.57, + "requiredBlockPx": 30, + "widthChecks": [ + { + "width": 120, + "fitsWidth": false, + "fitsHeight": true, + "pass": false + }, + { + "width": 200, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + } + ], + "pass": false + } + ] + }, + { + "componentId": "breadcrumbs", + "id": "size=md__rest", + "name": "breadcrumbs-md", + "caseKey": "size=md", + "axes": { + "size": "md" + }, + "states": [ + "rest" + ], + "props": {}, + "sampleText": "Workspace / Release / Production", + "layout": [ + { + "target": "root", + "widths": [ + 220, + 320, + 768, + 1280 + ], + "heights": [ + 48 + ], + "estimatedInlinePx": 266.88, + "requiredBlockPx": 44, + "widthChecks": [ + { + "width": 220, + "fitsWidth": false, + "fitsHeight": true, + "pass": false + }, + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + } + ], + "pass": false + } + ] + }, + { + "componentId": "button", + "id": "content=label|size=lg|variant=solid__rest", + "name": "solid-sizes (content=label|size=lg|variant=solid)", + "caseKey": "content=label|size=lg|variant=solid", + "axes": { + "variant": "solid", + "size": "lg", + "content": "label" + }, + "states": [ + "rest" + ], + "props": {}, + "sampleText": "Continue", + "layout": [ + { + "target": "root", + "widths": [ + 180, + 240, + 320, + 768, + 1280 + ], + "heights": [ + 44, + 48, + 52 + ], + "estimatedInlinePx": 230.36, + "requiredBlockPx": 52, + "widthChecks": [ + { + "width": 180, + "fitsWidth": false, + "fitsHeight": true, + "pass": false + }, + { + "width": 240, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + } + ], + "pass": false + } + ] + }, + { + "componentId": "button", + "id": "content=label|size=md|variant=link__rest__as=a|href=#", + "name": "link-anchor", + "caseKey": "content=label|size=md|variant=link", + "axes": { + "variant": "link", + "size": "md", + "content": "label" + }, + "states": [ + "rest" + ], + "props": { + "as": "a", + "href": "#" + }, + "sampleText": "Open details", + "layout": [ + { + "target": "root", + "widths": [ + 180, + 240, + 320, + 768, + 1280 + ], + "heights": [ + 44, + 48, + 52 + ], + "estimatedInlinePx": 232.56, + "requiredBlockPx": 48, + "widthChecks": [ + { + "width": 180, + "fitsWidth": false, + "fitsHeight": true, + "pass": false + }, + { + "width": 240, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + } + ], + "pass": false + } + ] + }, + { + "componentId": "checkbox", + "id": "size=sm__rest", + "name": "default (size=sm)", + "caseKey": "size=sm", + "axes": { + "size": "sm" + }, + "states": [ + "rest" + ], + "props": {}, + "sampleText": "Send release notes", + "layout": [ + { + "target": "root", + "widths": [ + 240, + 320, + 768, + 1280 + ], + "heights": [ + 44 + ], + "estimatedInlinePx": 268.83, + "requiredBlockPx": 44, + "widthChecks": [ + { + "width": 240, + "fitsWidth": false, + "fitsHeight": true, + "pass": false + }, + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + } + ], + "pass": false + } + ] + }, + { + "componentId": "checkbox", + "id": "size=md__rest", + "name": "default (size=md)", + "caseKey": "size=md", + "axes": { + "size": "md" + }, + "states": [ + "rest" + ], + "props": {}, + "sampleText": "Send release notes", + "layout": [ + { + "target": "root", + "widths": [ + 240, + 320, + 768, + 1280 + ], + "heights": [ + 44 + ], + "estimatedInlinePx": 268.83, + "requiredBlockPx": 44, + "widthChecks": [ + { + "width": 240, + "fitsWidth": false, + "fitsHeight": true, + "pass": false + }, + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + } + ], + "pass": false + } + ] + }, + { + "componentId": "checkbox", + "id": "size=lg__rest", + "name": "default (size=lg)", + "caseKey": "size=lg", + "axes": { + "size": "lg" + }, + "states": [ + "rest" + ], + "props": {}, + "sampleText": "Send release notes", + "layout": [ + { + "target": "root", + "widths": [ + 240, + 320, + 768, + 1280 + ], + "heights": [ + 44 + ], + "estimatedInlinePx": 268.83, + "requiredBlockPx": 44, + "widthChecks": [ + { + "width": 240, + "fitsWidth": false, + "fitsHeight": true, + "pass": false + }, + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + } + ], + "pass": false + } + ] + }, + { + "componentId": "checkbox", + "id": "size=sm__checked", + "name": "checked (size=sm)", + "caseKey": "size=sm", + "axes": { + "size": "sm" + }, + "states": [ + "checked" + ], + "props": {}, + "sampleText": "Send release notes", + "layout": [ + { + "target": "root", + "widths": [ + 240, + 320, + 768, + 1280 + ], + "heights": [ + 44 + ], + "estimatedInlinePx": 268.83, + "requiredBlockPx": 44, + "widthChecks": [ + { + "width": 240, + "fitsWidth": false, + "fitsHeight": true, + "pass": false + }, + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + } + ], + "pass": false + } + ] + }, + { + "componentId": "checkbox", + "id": "size=md__checked", + "name": "checked (size=md)", + "caseKey": "size=md", + "axes": { + "size": "md" + }, + "states": [ + "checked" + ], + "props": {}, + "sampleText": "Send release notes", + "layout": [ + { + "target": "root", + "widths": [ + 240, + 320, + 768, + 1280 + ], + "heights": [ + 44 + ], + "estimatedInlinePx": 268.83, + "requiredBlockPx": 44, + "widthChecks": [ + { + "width": 240, + "fitsWidth": false, + "fitsHeight": true, + "pass": false + }, + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + } + ], + "pass": false + } + ] + }, + { + "componentId": "checkbox", + "id": "size=lg__checked", + "name": "checked (size=lg)", + "caseKey": "size=lg", + "axes": { + "size": "lg" + }, + "states": [ + "checked" + ], + "props": {}, + "sampleText": "Send release notes", + "layout": [ + { + "target": "root", + "widths": [ + 240, + 320, + 768, + 1280 + ], + "heights": [ + 44 + ], + "estimatedInlinePx": 268.83, + "requiredBlockPx": 44, + "widthChecks": [ + { + "width": 240, + "fitsWidth": false, + "fitsHeight": true, + "pass": false + }, + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + } + ], + "pass": false + } + ] + }, + { + "componentId": "checkbox", + "id": "size=sm__indeterminate", + "name": "indeterminate (size=sm)", + "caseKey": "size=sm", + "axes": { + "size": "sm" + }, + "states": [ + "indeterminate" + ], + "props": {}, + "sampleText": "Send release notes", + "layout": [ + { + "target": "root", + "widths": [ + 240, + 320, + 768, + 1280 + ], + "heights": [ + 44 + ], + "estimatedInlinePx": 268.83, + "requiredBlockPx": 44, + "widthChecks": [ + { + "width": 240, + "fitsWidth": false, + "fitsHeight": true, + "pass": false + }, + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + } + ], + "pass": false + } + ] + }, + { + "componentId": "checkbox", + "id": "size=md__indeterminate", + "name": "indeterminate (size=md)", + "caseKey": "size=md", + "axes": { + "size": "md" + }, + "states": [ + "indeterminate" + ], + "props": {}, + "sampleText": "Send release notes", + "layout": [ + { + "target": "root", + "widths": [ + 240, + 320, + 768, + 1280 + ], + "heights": [ + 44 + ], + "estimatedInlinePx": 268.83, + "requiredBlockPx": 44, + "widthChecks": [ + { + "width": 240, + "fitsWidth": false, + "fitsHeight": true, + "pass": false + }, + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + } + ], + "pass": false + } + ] + }, + { + "componentId": "checkbox", + "id": "size=lg__indeterminate", + "name": "indeterminate (size=lg)", + "caseKey": "size=lg", + "axes": { + "size": "lg" + }, + "states": [ + "indeterminate" + ], + "props": {}, + "sampleText": "Send release notes", + "layout": [ + { + "target": "root", + "widths": [ + 240, + 320, + 768, + 1280 + ], + "heights": [ + 44 + ], + "estimatedInlinePx": 268.83, + "requiredBlockPx": 44, + "widthChecks": [ + { + "width": 240, + "fitsWidth": false, + "fitsHeight": true, + "pass": false + }, + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + } + ], + "pass": false + } + ] + }, + { + "componentId": "text-field", + "id": "size=lg__rest", + "name": "sizes (size=lg)", + "caseKey": "size=lg", + "axes": { + "size": "lg" + }, + "states": [ + "rest" + ], + "props": {}, + "sampleText": "Project name", + "layout": [ + { + "target": "field", + "widths": [ + 240, + 320, + 420, + 768, + 1280 + ], + "heights": [ + 44, + 48, + 52 + ], + "estimatedInlinePx": 310, + "requiredBlockPx": 52, + "widthChecks": [ + { + "width": 240, + "fitsWidth": false, + "fitsHeight": true, + "pass": false + }, + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 420, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + } + ], + "pass": false + } + ] + } + ], + "fixtureInventory": [ + { + "componentId": "accordion", + "id": "size=sm__rest", + "name": "accordion-sm", + "caseKey": "size=sm", + "axes": { + "size": "sm" + }, + "states": [ + "rest" + ], + "props": {}, + "sampleText": "Overview and rollout checklist", + "declared": [ + "contrast", + "target", + "layout" + ], + "proofCounts": { + "contrast": 2, + "distinctness": 0, + "target": 1, + "layout": 1, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "accordion", + "id": "size=md__open", + "name": "accordion-md-open", + "caseKey": "size=md", + "axes": { + "size": "md" + }, + "states": [ + "open" + ], + "props": {}, + "sampleText": "Deployment timeline and stakeholder notes", + "declared": [ + "contrast", + "target", + "layout" + ], + "proofCounts": { + "contrast": 2, + "distinctness": 0, + "target": 1, + "layout": 1, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "alert", + "id": "tone=neutral__rest", + "name": "neutral", + "caseKey": "tone=neutral", + "axes": { + "tone": "neutral" + }, + "states": [ + "rest" + ], + "props": {}, + "declared": [ + "contrast", + "target", + "layout", + "helperText" + ], + "proofCounts": { + "contrast": 2, + "distinctness": 0, + "target": 1, + "layout": 1, + "helperText": 1, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "alert", + "id": "tone=brand__rest", + "name": "brand", + "caseKey": "tone=brand", + "axes": { + "tone": "brand" + }, + "states": [ + "rest" + ], + "props": {}, + "declared": [ + "contrast", + "target", + "layout", + "helperText" + ], + "proofCounts": { + "contrast": 2, + "distinctness": 0, + "target": 1, + "layout": 1, + "helperText": 1, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "alert", + "id": "tone=success__rest", + "name": "success", + "caseKey": "tone=success", + "axes": { + "tone": "success" + }, + "states": [ + "rest" + ], + "props": {}, + "declared": [ + "contrast", + "target", + "layout", + "helperText" + ], + "proofCounts": { + "contrast": 2, + "distinctness": 0, + "target": 1, + "layout": 1, + "helperText": 1, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "alert", + "id": "tone=warning__rest", + "name": "warning", + "caseKey": "tone=warning", + "axes": { + "tone": "warning" + }, + "states": [ + "rest" + ], + "props": {}, + "declared": [ + "contrast", + "target", + "layout", + "helperText" + ], + "proofCounts": { + "contrast": 2, + "distinctness": 0, + "target": 1, + "layout": 1, + "helperText": 1, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "alert", + "id": "tone=danger__rest", + "name": "danger", + "caseKey": "tone=danger", + "axes": { + "tone": "danger" + }, + "states": [ + "rest" + ], + "props": {}, + "declared": [ + "contrast", + "target", + "layout", + "helperText" + ], + "proofCounts": { + "contrast": 2, + "distinctness": 0, + "target": 1, + "layout": 1, + "helperText": 1, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "avatar", + "id": "shape=circle|size=sm__rest", + "name": "small-circle", + "caseKey": "shape=circle|size=sm", + "axes": { + "size": "sm", + "shape": "circle" + }, + "states": [ + "rest" + ], + "props": {}, + "declared": [ + "contrast", + "target" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 0, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "avatar", + "id": "shape=circle|size=md__rest", + "name": "medium-circle", + "caseKey": "shape=circle|size=md", + "axes": { + "size": "md", + "shape": "circle" + }, + "states": [ + "rest" + ], + "props": {}, + "declared": [ + "contrast", + "target" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 0, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "avatar", + "id": "shape=rounded|size=lg__rest", + "name": "large-rounded", + "caseKey": "shape=rounded|size=lg", + "axes": { + "size": "lg", + "shape": "rounded" + }, + "states": [ + "rest" + ], + "props": {}, + "declared": [ + "contrast", + "target" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 0, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "badge", + "id": "emphasis=soft|size=md|tone=neutral__rest", + "name": "neutral-soft", + "caseKey": "emphasis=soft|size=md|tone=neutral", + "axes": { + "tone": "neutral", + "emphasis": "soft", + "size": "md" + }, + "states": [ + "rest" + ], + "props": {}, + "sampleText": "Draft", + "declared": [ + "contrast", + "layout" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 0, + "layout": 1, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "badge", + "id": "emphasis=solid|size=md|tone=neutral__rest", + "name": "neutral-solid", + "caseKey": "emphasis=solid|size=md|tone=neutral", + "axes": { + "tone": "neutral", + "emphasis": "solid", + "size": "md" + }, + "states": [ + "rest" + ], + "props": {}, + "sampleText": "Draft", + "declared": [ + "contrast", + "layout" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 0, + "layout": 1, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "badge", + "id": "emphasis=outline|size=md|tone=neutral__rest", + "name": "neutral-outline", + "caseKey": "emphasis=outline|size=md|tone=neutral", + "axes": { + "tone": "neutral", + "emphasis": "outline", + "size": "md" + }, + "states": [ + "rest" + ], + "props": {}, + "sampleText": "Draft", + "declared": [ + "contrast", + "layout" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 0, + "layout": 1, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "badge", + "id": "emphasis=soft|size=md|tone=brand__rest", + "name": "brand-soft", + "caseKey": "emphasis=soft|size=md|tone=brand", + "axes": { + "tone": "brand", + "emphasis": "soft", + "size": "md" + }, + "states": [ + "rest" + ], + "props": {}, + "sampleText": "Launch", + "declared": [ + "contrast", + "layout" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 0, + "layout": 1, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "badge", + "id": "emphasis=solid|size=md|tone=brand__rest", + "name": "brand-solid", + "caseKey": "emphasis=solid|size=md|tone=brand", + "axes": { + "tone": "brand", + "emphasis": "solid", + "size": "md" + }, + "states": [ + "rest" + ], + "props": {}, + "sampleText": "Launch", + "declared": [ + "contrast", + "layout" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 0, + "layout": 1, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "badge", + "id": "emphasis=outline|size=md|tone=brand__rest", + "name": "brand-outline", + "caseKey": "emphasis=outline|size=md|tone=brand", + "axes": { + "tone": "brand", + "emphasis": "outline", + "size": "md" + }, + "states": [ + "rest" + ], + "props": {}, + "sampleText": "Launch", + "declared": [ + "contrast", + "layout" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 0, + "layout": 1, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "badge", + "id": "emphasis=soft|size=md|tone=success__rest", + "name": "success-soft", + "caseKey": "emphasis=soft|size=md|tone=success", + "axes": { + "tone": "success", + "emphasis": "soft", + "size": "md" + }, + "states": [ + "rest" + ], + "props": {}, + "sampleText": "Healthy", + "declared": [ + "contrast", + "layout" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 0, + "layout": 1, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "badge", + "id": "emphasis=solid|size=md|tone=success__rest", + "name": "success-solid", + "caseKey": "emphasis=solid|size=md|tone=success", + "axes": { + "tone": "success", + "emphasis": "solid", + "size": "md" + }, + "states": [ + "rest" + ], + "props": {}, + "sampleText": "Healthy", + "declared": [ + "contrast", + "layout" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 0, + "layout": 1, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "badge", + "id": "emphasis=outline|size=md|tone=success__rest", + "name": "success-outline", + "caseKey": "emphasis=outline|size=md|tone=success", + "axes": { + "tone": "success", + "emphasis": "outline", + "size": "md" + }, + "states": [ + "rest" + ], + "props": {}, + "sampleText": "Healthy", + "declared": [ + "contrast", + "layout" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 0, + "layout": 1, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "badge", + "id": "emphasis=soft|size=md|tone=warning__rest", + "name": "warning-soft", + "caseKey": "emphasis=soft|size=md|tone=warning", + "axes": { + "tone": "warning", + "emphasis": "soft", + "size": "md" + }, + "states": [ + "rest" + ], + "props": {}, + "sampleText": "Review", + "declared": [ + "contrast", + "layout" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 0, + "layout": 1, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "badge", + "id": "emphasis=solid|size=md|tone=warning__rest", + "name": "warning-solid", + "caseKey": "emphasis=solid|size=md|tone=warning", + "axes": { + "tone": "warning", + "emphasis": "solid", + "size": "md" + }, + "states": [ + "rest" + ], + "props": {}, + "sampleText": "Review", + "declared": [ + "contrast", + "layout" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 0, + "layout": 1, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "badge", + "id": "emphasis=outline|size=md|tone=warning__rest", + "name": "warning-outline", + "caseKey": "emphasis=outline|size=md|tone=warning", + "axes": { + "tone": "warning", + "emphasis": "outline", + "size": "md" + }, + "states": [ + "rest" + ], + "props": {}, + "sampleText": "Review", + "declared": [ + "contrast", + "layout" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 0, + "layout": 1, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "badge", + "id": "emphasis=soft|size=md|tone=danger__rest", + "name": "danger-soft", + "caseKey": "emphasis=soft|size=md|tone=danger", + "axes": { + "tone": "danger", + "emphasis": "soft", + "size": "md" + }, + "states": [ + "rest" + ], + "props": {}, + "sampleText": "Failed", + "declared": [ + "contrast", + "layout" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 0, + "layout": 1, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "badge", + "id": "emphasis=solid|size=md|tone=danger__rest", + "name": "danger-solid", + "caseKey": "emphasis=solid|size=md|tone=danger", + "axes": { + "tone": "danger", + "emphasis": "solid", + "size": "md" + }, + "states": [ + "rest" + ], + "props": {}, + "sampleText": "Failed", + "declared": [ + "contrast", + "layout" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 0, + "layout": 1, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "badge", + "id": "emphasis=outline|size=md|tone=danger__rest", + "name": "danger-outline", + "caseKey": "emphasis=outline|size=md|tone=danger", + "axes": { + "tone": "danger", + "emphasis": "outline", + "size": "md" + }, + "states": [ + "rest" + ], + "props": {}, + "sampleText": "Failed", + "declared": [ + "contrast", + "layout" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 0, + "layout": 1, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "breadcrumbs", + "id": "size=md__rest", + "name": "breadcrumbs-md", + "caseKey": "size=md", + "axes": { + "size": "md" + }, + "states": [ + "rest" + ], + "props": {}, + "sampleText": "Workspace / Release / Production", + "declared": [ + "contrast", + "target", + "layout" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 1, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "button", + "id": "content=label|size=md|variant=solid__rest", + "name": "solid-sizes (content=label|size=md|variant=solid)", + "caseKey": "content=label|size=md|variant=solid", + "axes": { + "variant": "solid", + "size": "md", + "content": "label" + }, + "states": [ + "rest" + ], + "props": {}, + "sampleText": "Continue", + "declared": [ + "contrast", + "target", + "layout" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 1, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "button", + "id": "content=label|size=md|variant=soft__rest", + "name": "variants-md-label (content=label|size=md|variant=soft)", + "caseKey": "content=label|size=md|variant=soft", + "axes": { + "variant": "soft", + "size": "md", + "content": "label" + }, + "states": [ + "rest" + ], + "props": {}, + "sampleText": "Continue", + "declared": [ + "contrast", + "target", + "layout" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 1, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "button", + "id": "content=label|size=md|variant=outline__rest", + "name": "variants-md-label (content=label|size=md|variant=outline)", + "caseKey": "content=label|size=md|variant=outline", + "axes": { + "variant": "outline", + "size": "md", + "content": "label" + }, + "states": [ + "rest" + ], + "props": {}, + "sampleText": "Continue", + "declared": [ + "contrast", + "target", + "layout" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 1, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "button", + "id": "content=label|size=md|variant=ghost__rest", + "name": "variants-md-label (content=label|size=md|variant=ghost)", + "caseKey": "content=label|size=md|variant=ghost", + "axes": { + "variant": "ghost", + "size": "md", + "content": "label" + }, + "states": [ + "rest" + ], + "props": {}, + "sampleText": "Continue", + "declared": [ + "contrast", + "target", + "layout" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 1, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "button", + "id": "content=label|size=md|variant=link__rest", + "name": "variants-md-label (content=label|size=md|variant=link)", + "caseKey": "content=label|size=md|variant=link", + "axes": { + "variant": "link", + "size": "md", + "content": "label" + }, + "states": [ + "rest" + ], + "props": {}, + "sampleText": "Continue", + "declared": [ + "contrast", + "target", + "layout" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 1, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "button", + "id": "content=label|size=md|variant=destructive__rest", + "name": "variants-md-label (content=label|size=md|variant=destructive)", + "caseKey": "content=label|size=md|variant=destructive", + "axes": { + "variant": "destructive", + "size": "md", + "content": "label" + }, + "states": [ + "rest" + ], + "props": {}, + "sampleText": "Continue", + "declared": [ + "contrast", + "target", + "layout" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 1, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "button", + "id": "content=label|size=xs|variant=solid__rest", + "name": "solid-sizes (content=label|size=xs|variant=solid)", + "caseKey": "content=label|size=xs|variant=solid", + "axes": { + "variant": "solid", + "size": "xs", + "content": "label" + }, + "states": [ + "rest" + ], + "props": {}, + "sampleText": "Continue", + "declared": [ + "contrast", + "target", + "layout" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 1, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "button", + "id": "content=label|size=sm|variant=solid__rest", + "name": "solid-sizes (content=label|size=sm|variant=solid)", + "caseKey": "content=label|size=sm|variant=solid", + "axes": { + "variant": "solid", + "size": "sm", + "content": "label" + }, + "states": [ + "rest" + ], + "props": {}, + "sampleText": "Continue", + "declared": [ + "contrast", + "target", + "layout" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 1, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "button", + "id": "content=label|size=lg|variant=solid__rest", + "name": "solid-sizes (content=label|size=lg|variant=solid)", + "caseKey": "content=label|size=lg|variant=solid", + "axes": { + "variant": "solid", + "size": "lg", + "content": "label" + }, + "states": [ + "rest" + ], + "props": {}, + "sampleText": "Continue", + "declared": [ + "contrast", + "target", + "layout" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 1, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "button", + "id": "content=icon-only|size=md|variant=solid__rest", + "name": "icon-md", + "caseKey": "content=icon-only|size=md|variant=solid", + "axes": { + "variant": "solid", + "size": "md", + "content": "icon-only" + }, + "states": [ + "rest" + ], + "props": {}, + "declared": [ + "contrast", + "target", + "layout" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 1, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "button", + "id": "content=icon-only|size=lg|variant=solid__rest", + "name": "icon-lg", + "caseKey": "content=icon-only|size=lg|variant=solid", + "axes": { + "variant": "solid", + "size": "lg", + "content": "icon-only" + }, + "states": [ + "rest" + ], + "props": {}, + "declared": [ + "contrast", + "target", + "layout" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 1, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "button", + "id": "content=label|size=md|variant=destructive__loading", + "name": "destructive-loading", + "caseKey": "content=label|size=md|variant=destructive", + "axes": { + "variant": "destructive", + "size": "md", + "content": "label" + }, + "states": [ + "loading" + ], + "props": {}, + "sampleText": "Delete", + "declared": [ + "contrast", + "target", + "layout" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 1, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "button", + "id": "content=label|size=md|variant=link__rest__as=a|href=#", + "name": "link-anchor", + "caseKey": "content=label|size=md|variant=link", + "axes": { + "variant": "link", + "size": "md", + "content": "label" + }, + "states": [ + "rest" + ], + "props": { + "as": "a", + "href": "#" + }, + "sampleText": "Open details", + "declared": [ + "contrast", + "target", + "layout" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 1, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "card", + "id": "padding=md|surface=default__rest", + "name": "default", + "caseKey": "padding=md|surface=default", + "axes": { + "padding": "md", + "surface": "default" + }, + "states": [ + "rest" + ], + "props": {}, + "declared": [ + "contrast", + "layout" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 0, + "layout": 1, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "card", + "id": "padding=md|surface=raised__rest", + "name": "raised", + "caseKey": "padding=md|surface=raised", + "axes": { + "padding": "md", + "surface": "raised" + }, + "states": [ + "rest" + ], + "props": {}, + "declared": [ + "contrast", + "layout" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 0, + "layout": 1, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "card", + "id": "padding=md|surface=outlined__rest", + "name": "outlined", + "caseKey": "padding=md|surface=outlined", + "axes": { + "padding": "md", + "surface": "outlined" + }, + "states": [ + "rest" + ], + "props": {}, + "declared": [ + "contrast", + "layout" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 0, + "layout": 1, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "checkbox", + "id": "size=sm__rest", + "name": "default (size=sm)", + "caseKey": "size=sm", + "axes": { + "size": "sm" + }, + "states": [ + "rest" + ], + "props": {}, + "sampleText": "Send release notes", + "declared": [ + "contrast", + "target", + "layout", + "helperText" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 1, + "helperText": 1, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "checkbox", + "id": "size=md__rest", + "name": "default (size=md)", + "caseKey": "size=md", + "axes": { + "size": "md" + }, + "states": [ + "rest" + ], + "props": {}, + "sampleText": "Send release notes", + "declared": [ + "contrast", + "target", + "layout", + "helperText" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 1, + "helperText": 1, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "checkbox", + "id": "size=lg__rest", + "name": "default (size=lg)", + "caseKey": "size=lg", + "axes": { + "size": "lg" + }, + "states": [ + "rest" + ], + "props": {}, + "sampleText": "Send release notes", + "declared": [ + "contrast", + "target", + "layout", + "helperText" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 1, + "helperText": 1, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "checkbox", + "id": "size=sm__checked", + "name": "checked (size=sm)", + "caseKey": "size=sm", + "axes": { + "size": "sm" + }, + "states": [ + "checked" + ], + "props": {}, + "sampleText": "Send release notes", + "declared": [ + "contrast", + "target", + "layout", + "helperText" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 1, + "helperText": 1, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "checkbox", + "id": "size=md__checked", + "name": "checked (size=md)", + "caseKey": "size=md", + "axes": { + "size": "md" + }, + "states": [ + "checked" + ], + "props": {}, + "sampleText": "Send release notes", + "declared": [ + "contrast", + "target", + "layout", + "helperText" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 1, + "helperText": 1, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "checkbox", + "id": "size=lg__checked", + "name": "checked (size=lg)", + "caseKey": "size=lg", + "axes": { + "size": "lg" + }, + "states": [ + "checked" + ], + "props": {}, + "sampleText": "Send release notes", + "declared": [ + "contrast", + "target", + "layout", + "helperText" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 1, + "helperText": 1, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "checkbox", + "id": "size=sm__indeterminate", + "name": "indeterminate (size=sm)", + "caseKey": "size=sm", + "axes": { + "size": "sm" + }, + "states": [ + "indeterminate" + ], + "props": {}, + "sampleText": "Send release notes", + "declared": [ + "contrast", + "target", + "layout", + "helperText" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 1, + "helperText": 1, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "checkbox", + "id": "size=md__indeterminate", + "name": "indeterminate (size=md)", + "caseKey": "size=md", + "axes": { + "size": "md" + }, + "states": [ + "indeterminate" + ], + "props": {}, + "sampleText": "Send release notes", + "declared": [ + "contrast", + "target", + "layout", + "helperText" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 1, + "helperText": 1, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "checkbox", + "id": "size=lg__indeterminate", + "name": "indeterminate (size=lg)", + "caseKey": "size=lg", + "axes": { + "size": "lg" + }, + "states": [ + "indeterminate" + ], + "props": {}, + "sampleText": "Send release notes", + "declared": [ + "contrast", + "target", + "layout", + "helperText" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 1, + "helperText": 1, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "chip", + "id": "size=md|tone=neutral__rest", + "name": "neutral", + "caseKey": "size=md|tone=neutral", + "axes": { + "tone": "neutral", + "size": "md" + }, + "states": [ + "rest" + ], + "props": {}, + "declared": [ + "contrast", + "target", + "layout" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 1, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "chip", + "id": "size=md|tone=brand__selected", + "name": "brand-selected", + "caseKey": "size=md|tone=brand", + "axes": { + "tone": "brand", + "size": "md" + }, + "states": [ + "selected" + ], + "props": {}, + "declared": [ + "contrast", + "target", + "layout" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 1, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "chip", + "id": "size=sm|tone=danger__rest", + "name": "danger", + "caseKey": "size=sm|tone=danger", + "axes": { + "tone": "danger", + "size": "sm" + }, + "states": [ + "rest" + ], + "props": {}, + "declared": [ + "contrast", + "target", + "layout" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 1, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "combobox", + "id": "size=sm__rest", + "name": "default (size=sm)", + "caseKey": "size=sm", + "axes": { + "size": "sm" + }, + "states": [ + "rest" + ], + "props": {}, + "declared": [ + "contrast", + "target", + "optionRow", + "anchoredSurface" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 0, + "helperText": 0, + "optionRow": 1, + "anchoredSurface": 3, + "motion": 0 + } + }, + { + "componentId": "combobox", + "id": "size=md__rest", + "name": "default (size=md)", + "caseKey": "size=md", + "axes": { + "size": "md" + }, + "states": [ + "rest" + ], + "props": {}, + "declared": [ + "contrast", + "target", + "optionRow", + "anchoredSurface" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 0, + "helperText": 0, + "optionRow": 1, + "anchoredSurface": 3, + "motion": 0 + } + }, + { + "componentId": "combobox", + "id": "size=lg__rest", + "name": "default (size=lg)", + "caseKey": "size=lg", + "axes": { + "size": "lg" + }, + "states": [ + "rest" + ], + "props": {}, + "declared": [ + "contrast", + "target", + "optionRow", + "anchoredSurface" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 0, + "helperText": 0, + "optionRow": 1, + "anchoredSurface": 3, + "motion": 0 + } + }, + { + "componentId": "combobox", + "id": "size=sm__selected", + "name": "selected (size=sm)", + "caseKey": "size=sm", + "axes": { + "size": "sm" + }, + "states": [ + "selected" + ], + "props": {}, + "declared": [ + "contrast", + "target", + "optionRow", + "anchoredSurface" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 0, + "helperText": 0, + "optionRow": 1, + "anchoredSurface": 3, + "motion": 0 + } + }, + { + "componentId": "combobox", + "id": "size=md__selected", + "name": "selected (size=md)", + "caseKey": "size=md", + "axes": { + "size": "md" + }, + "states": [ + "selected" + ], + "props": {}, + "declared": [ + "contrast", + "target", + "optionRow", + "anchoredSurface" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 0, + "helperText": 0, + "optionRow": 1, + "anchoredSurface": 3, + "motion": 0 + } + }, + { + "componentId": "combobox", + "id": "size=lg__selected", + "name": "selected (size=lg)", + "caseKey": "size=lg", + "axes": { + "size": "lg" + }, + "states": [ + "selected" + ], + "props": {}, + "declared": [ + "contrast", + "target", + "optionRow", + "anchoredSurface" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 0, + "helperText": 0, + "optionRow": 1, + "anchoredSurface": 3, + "motion": 0 + } + }, + { + "componentId": "combobox", + "id": "size=sm__invalid", + "name": "invalid (size=sm)", + "caseKey": "size=sm", + "axes": { + "size": "sm" + }, + "states": [ + "invalid" + ], + "props": {}, + "declared": [ + "contrast", + "target", + "optionRow", + "anchoredSurface" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 0, + "helperText": 0, + "optionRow": 1, + "anchoredSurface": 3, + "motion": 0 + } + }, + { + "componentId": "combobox", + "id": "size=md__invalid", + "name": "invalid (size=md)", + "caseKey": "size=md", + "axes": { + "size": "md" + }, + "states": [ + "invalid" + ], + "props": {}, + "declared": [ + "contrast", + "target", + "optionRow", + "anchoredSurface" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 0, + "helperText": 0, + "optionRow": 1, + "anchoredSurface": 3, + "motion": 0 + } + }, + { + "componentId": "combobox", + "id": "size=lg__invalid", + "name": "invalid (size=lg)", + "caseKey": "size=lg", + "axes": { + "size": "lg" + }, + "states": [ + "invalid" + ], + "props": {}, + "declared": [ + "contrast", + "target", + "optionRow", + "anchoredSurface" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 0, + "helperText": 0, + "optionRow": 1, + "anchoredSurface": 3, + "motion": 0 + } + }, + { + "componentId": "command-palette", + "id": "rest", + "name": "default", + "caseKey": "", + "axes": {}, + "states": [ + "rest" + ], + "props": {}, + "declared": [ + "contrast", + "target", + "optionRow", + "anchoredSurface" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 0, + "helperText": 0, + "optionRow": 1, + "anchoredSurface": 3, + "motion": 0 + } + }, + { + "componentId": "command-palette", + "id": "selected", + "name": "selected", + "caseKey": "", + "axes": {}, + "states": [ + "selected" + ], + "props": {}, + "declared": [ + "contrast", + "target", + "optionRow", + "anchoredSurface" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 0, + "helperText": 0, + "optionRow": 1, + "anchoredSurface": 3, + "motion": 0 + } + }, + { + "componentId": "data-chart", + "id": "type=line__rest", + "name": "line", + "caseKey": "type=line", + "axes": { + "type": "line" + }, + "states": [ + "rest" + ], + "props": {}, + "declared": [ + "contrast", + "layout" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 0, + "layout": 1, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "data-chart", + "id": "type=bar__rest", + "name": "bar", + "caseKey": "type=bar", + "axes": { + "type": "bar" + }, + "states": [ + "rest" + ], + "props": {}, + "declared": [ + "contrast", + "layout" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 0, + "layout": 1, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "data-chart", + "id": "type=area__rest", + "name": "area", + "caseKey": "type=area", + "axes": { + "type": "area" + }, + "states": [ + "rest" + ], + "props": {}, + "declared": [ + "contrast", + "layout" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 0, + "layout": 1, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "data-grid-lite", + "id": "size=md__rest", + "name": "default-md", + "caseKey": "size=md", + "axes": { + "size": "md" + }, + "states": [ + "rest" + ], + "props": {}, + "declared": [ + "contrast", + "target", + "layout" + ], + "proofCounts": { + "contrast": 2, + "distinctness": 0, + "target": 1, + "layout": 1, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "data-grid-lite", + "id": "size=sm__rest", + "name": "dense-sm", + "caseKey": "size=sm", + "axes": { + "size": "sm" + }, + "states": [ + "rest" + ], + "props": {}, + "declared": [ + "contrast", + "target", + "layout" + ], + "proofCounts": { + "contrast": 2, + "distinctness": 0, + "target": 1, + "layout": 1, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "data-grid-lite", + "id": "size=md__selected", + "name": "active-md", + "caseKey": "size=md", + "axes": { + "size": "md" + }, + "states": [ + "selected" + ], + "props": {}, + "declared": [ + "contrast", + "target", + "layout" + ], + "proofCounts": { + "contrast": 2, + "distinctness": 0, + "target": 1, + "layout": 1, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "date-picker", + "id": "size=md__rest", + "name": "default-md", + "caseKey": "size=md", + "axes": { + "size": "md" + }, + "states": [ + "rest" + ], + "props": {}, + "declared": [ + "contrast", + "target", + "anchoredSurface", + "layout" + ], + "proofCounts": { + "contrast": 2, + "distinctness": 0, + "target": 2, + "layout": 1, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 3, + "motion": 0 + } + }, + { + "componentId": "date-picker", + "id": "size=lg__selected", + "name": "selected-lg", + "caseKey": "size=lg", + "axes": { + "size": "lg" + }, + "states": [ + "selected" + ], + "props": {}, + "declared": [ + "contrast", + "target", + "anchoredSurface", + "layout" + ], + "proofCounts": { + "contrast": 2, + "distinctness": 0, + "target": 2, + "layout": 1, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 3, + "motion": 0 + } + }, + { + "componentId": "date-picker", + "id": "size=sm__disabled", + "name": "disabled-sm", + "caseKey": "size=sm", + "axes": { + "size": "sm" + }, + "states": [ + "disabled" + ], + "props": {}, + "declared": [ + "contrast", + "target", + "anchoredSurface", + "layout" + ], + "proofCounts": { + "contrast": 2, + "distinctness": 0, + "target": 2, + "layout": 1, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 3, + "motion": 0 + } + }, + { + "componentId": "dialog", + "id": "size=sm__rest", + "name": "default (size=sm)", + "caseKey": "size=sm", + "axes": { + "size": "sm" + }, + "states": [ + "rest" + ], + "props": {}, + "declared": [ + "contrast", + "layout" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 0, + "layout": 1, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "dialog", + "id": "size=md__rest", + "name": "default (size=md)", + "caseKey": "size=md", + "axes": { + "size": "md" + }, + "states": [ + "rest" + ], + "props": {}, + "declared": [ + "contrast", + "layout" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 0, + "layout": 1, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "dialog", + "id": "size=lg__rest", + "name": "large", + "caseKey": "size=lg", + "axes": { + "size": "lg" + }, + "states": [ + "rest" + ], + "props": {}, + "declared": [ + "contrast", + "layout" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 0, + "layout": 1, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "drawer", + "id": "side=left|size=sm__rest", + "name": "default-right (side=left|size=sm)", + "caseKey": "side=left|size=sm", + "axes": { + "size": "sm", + "side": "left" + }, + "states": [ + "rest" + ], + "props": {}, + "declared": [ + "contrast", + "layout" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 0, + "layout": 1, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "drawer", + "id": "side=right|size=sm__rest", + "name": "default-right (side=right|size=sm)", + "caseKey": "side=right|size=sm", + "axes": { + "size": "sm", + "side": "right" + }, + "states": [ + "rest" + ], + "props": {}, + "declared": [ + "contrast", + "layout" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 0, + "layout": 1, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "drawer", + "id": "side=left|size=md__rest", + "name": "default-right (side=left|size=md)", + "caseKey": "side=left|size=md", + "axes": { + "size": "md", + "side": "left" + }, + "states": [ + "rest" + ], + "props": {}, + "declared": [ + "contrast", + "layout" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 0, + "layout": 1, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "drawer", + "id": "side=right|size=md__rest", + "name": "default-right (side=right|size=md)", + "caseKey": "side=right|size=md", + "axes": { + "size": "md", + "side": "right" + }, + "states": [ + "rest" + ], + "props": {}, + "declared": [ + "contrast", + "layout" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 0, + "layout": 1, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "drawer", + "id": "side=left|size=lg__rest", + "name": "large-left", + "caseKey": "side=left|size=lg", + "axes": { + "size": "lg", + "side": "left" + }, + "states": [ + "rest" + ], + "props": {}, + "declared": [ + "contrast", + "layout" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 0, + "layout": 1, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "drawer", + "id": "side=right|size=lg__rest", + "name": "default-right (side=right|size=lg)", + "caseKey": "side=right|size=lg", + "axes": { + "size": "lg", + "side": "right" + }, + "states": [ + "rest" + ], + "props": {}, + "declared": [ + "contrast", + "layout" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 0, + "layout": 1, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "empty-state", + "id": "size=md|tone=neutral__rest", + "name": "neutral-md", + "caseKey": "size=md|tone=neutral", + "axes": { + "tone": "neutral", + "size": "md" + }, + "states": [ + "rest" + ], + "props": {}, + "declared": [ + "contrast", + "layout" + ], + "proofCounts": { + "contrast": 2, + "distinctness": 0, + "target": 0, + "layout": 1, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "empty-state", + "id": "size=md|tone=brand__rest", + "name": "brand-md", + "caseKey": "size=md|tone=brand", + "axes": { + "tone": "brand", + "size": "md" + }, + "states": [ + "rest" + ], + "props": {}, + "declared": [ + "contrast", + "layout" + ], + "proofCounts": { + "contrast": 2, + "distinctness": 0, + "target": 0, + "layout": 1, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "empty-state", + "id": "size=lg|tone=danger__rest", + "name": "danger-lg", + "caseKey": "size=lg|tone=danger", + "axes": { + "tone": "danger", + "size": "lg" + }, + "states": [ + "rest" + ], + "props": {}, + "declared": [ + "contrast", + "layout" + ], + "proofCounts": { + "contrast": 2, + "distinctness": 0, + "target": 0, + "layout": 1, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "file-upload", + "id": "size=sm__rest", + "name": "default (size=sm)", + "caseKey": "size=sm", + "axes": { + "size": "sm" + }, + "states": [ + "rest" + ], + "props": {}, + "declared": [ + "contrast", + "target", + "layout" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 1, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "file-upload", + "id": "size=md__rest", + "name": "default (size=md)", + "caseKey": "size=md", + "axes": { + "size": "md" + }, + "states": [ + "rest" + ], + "props": {}, + "declared": [ + "contrast", + "target", + "layout" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 1, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "file-upload", + "id": "size=lg__rest", + "name": "large", + "caseKey": "size=lg", + "axes": { + "size": "lg" + }, + "states": [ + "rest" + ], + "props": {}, + "declared": [ + "contrast", + "target", + "layout" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 1, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "file-upload", + "id": "size=sm__invalid", + "name": "invalid (size=sm)", + "caseKey": "size=sm", + "axes": { + "size": "sm" + }, + "states": [ + "invalid" + ], + "props": {}, + "declared": [ + "contrast", + "target", + "layout" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 1, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "file-upload", + "id": "size=md__invalid", + "name": "invalid (size=md)", + "caseKey": "size=md", + "axes": { + "size": "md" + }, + "states": [ + "invalid" + ], + "props": {}, + "declared": [ + "contrast", + "target", + "layout" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 1, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "file-upload", + "id": "size=lg__invalid", + "name": "invalid (size=lg)", + "caseKey": "size=lg", + "axes": { + "size": "lg" + }, + "states": [ + "invalid" + ], + "props": {}, + "declared": [ + "contrast", + "target", + "layout" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 1, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "inline-edit", + "id": "size=md__rest", + "name": "default-md", + "caseKey": "size=md", + "axes": { + "size": "md" + }, + "states": [ + "rest" + ], + "props": {}, + "declared": [ + "contrast", + "target" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 0, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "menu", + "id": "size=sm__rest", + "name": "default (size=sm)", + "caseKey": "size=sm", + "axes": { + "size": "sm" + }, + "states": [ + "rest" + ], + "props": {}, + "declared": [ + "contrast", + "optionRow", + "anchoredSurface" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 0, + "layout": 0, + "helperText": 0, + "optionRow": 1, + "anchoredSurface": 3, + "motion": 0 + } + }, + { + "componentId": "menu", + "id": "size=md__rest", + "name": "default (size=md)", + "caseKey": "size=md", + "axes": { + "size": "md" + }, + "states": [ + "rest" + ], + "props": {}, + "declared": [ + "contrast", + "optionRow", + "anchoredSurface" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 0, + "layout": 0, + "helperText": 0, + "optionRow": 1, + "anchoredSurface": 3, + "motion": 0 + } + }, + { + "componentId": "menu", + "id": "size=lg__rest", + "name": "default (size=lg)", + "caseKey": "size=lg", + "axes": { + "size": "lg" + }, + "states": [ + "rest" + ], + "props": {}, + "declared": [ + "contrast", + "optionRow", + "anchoredSurface" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 0, + "layout": 0, + "helperText": 0, + "optionRow": 1, + "anchoredSurface": 3, + "motion": 0 + } + }, + { + "componentId": "menu", + "id": "size=sm__selected", + "name": "selected (size=sm)", + "caseKey": "size=sm", + "axes": { + "size": "sm" + }, + "states": [ + "selected" + ], + "props": {}, + "declared": [ + "contrast", + "optionRow", + "anchoredSurface" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 0, + "layout": 0, + "helperText": 0, + "optionRow": 1, + "anchoredSurface": 3, + "motion": 0 + } + }, + { + "componentId": "menu", + "id": "size=md__selected", + "name": "selected (size=md)", + "caseKey": "size=md", + "axes": { + "size": "md" + }, + "states": [ + "selected" + ], + "props": {}, + "declared": [ + "contrast", + "optionRow", + "anchoredSurface" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 0, + "layout": 0, + "helperText": 0, + "optionRow": 1, + "anchoredSurface": 3, + "motion": 0 + } + }, + { + "componentId": "menu", + "id": "size=lg__selected", + "name": "selected (size=lg)", + "caseKey": "size=lg", + "axes": { + "size": "lg" + }, + "states": [ + "selected" + ], + "props": {}, + "declared": [ + "contrast", + "optionRow", + "anchoredSurface" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 0, + "layout": 0, + "helperText": 0, + "optionRow": 1, + "anchoredSurface": 3, + "motion": 0 + } + }, + { + "componentId": "pagination", + "id": "size=md__rest", + "name": "pagination-md", + "caseKey": "size=md", + "axes": { + "size": "md" + }, + "states": [ + "rest" + ], + "props": {}, + "sampleText": "1 2 3 4 5", + "declared": [ + "contrast", + "target", + "layout" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 1, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "popover", + "id": "size=sm__rest", + "name": "default (size=sm)", + "caseKey": "size=sm", + "axes": { + "size": "sm" + }, + "states": [ + "rest" + ], + "props": {}, + "declared": [ + "contrast", + "anchoredSurface" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 0, + "layout": 0, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 3, + "motion": 0 + } + }, + { + "componentId": "popover", + "id": "size=md__rest", + "name": "default (size=md)", + "caseKey": "size=md", + "axes": { + "size": "md" + }, + "states": [ + "rest" + ], + "props": {}, + "declared": [ + "contrast", + "anchoredSurface" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 0, + "layout": 0, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 3, + "motion": 0 + } + }, + { + "componentId": "popover", + "id": "size=lg__rest", + "name": "large", + "caseKey": "size=lg", + "axes": { + "size": "lg" + }, + "states": [ + "rest" + ], + "props": {}, + "declared": [ + "contrast", + "anchoredSurface" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 0, + "layout": 0, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 3, + "motion": 0 + } + }, + { + "componentId": "progress", + "id": "size=md|tone=brand__rest", + "name": "brand-md", + "caseKey": "size=md|tone=brand", + "axes": { + "tone": "brand", + "size": "md" + }, + "states": [ + "rest" + ], + "props": {}, + "declared": [ + "contrast", + "layout" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 0, + "layout": 1, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "progress", + "id": "size=sm|tone=success__rest", + "name": "success-sm", + "caseKey": "size=sm|tone=success", + "axes": { + "tone": "success", + "size": "sm" + }, + "states": [ + "rest" + ], + "props": {}, + "declared": [ + "contrast", + "layout" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 0, + "layout": 1, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "progress", + "id": "size=md|tone=danger__rest", + "name": "danger-md", + "caseKey": "size=md|tone=danger", + "axes": { + "tone": "danger", + "size": "md" + }, + "states": [ + "rest" + ], + "props": {}, + "declared": [ + "contrast", + "layout" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 0, + "layout": 1, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "radio-group", + "id": "orientation=vertical|size=sm__rest", + "name": "default (orientation=vertical|size=sm)", + "caseKey": "orientation=vertical|size=sm", + "axes": { + "size": "sm", + "orientation": "vertical" + }, + "states": [ + "rest" + ], + "props": {}, + "declared": [ + "contrast", + "target", + "optionRow" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 0, + "helperText": 0, + "optionRow": 1, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "radio-group", + "id": "orientation=horizontal|size=sm__rest", + "name": "horizontal (orientation=horizontal|size=sm)", + "caseKey": "orientation=horizontal|size=sm", + "axes": { + "size": "sm", + "orientation": "horizontal" + }, + "states": [ + "rest" + ], + "props": {}, + "declared": [ + "contrast", + "target", + "optionRow" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 0, + "helperText": 0, + "optionRow": 1, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "radio-group", + "id": "orientation=vertical|size=md__rest", + "name": "default (orientation=vertical|size=md)", + "caseKey": "orientation=vertical|size=md", + "axes": { + "size": "md", + "orientation": "vertical" + }, + "states": [ + "rest" + ], + "props": {}, + "declared": [ + "contrast", + "target", + "optionRow" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 0, + "helperText": 0, + "optionRow": 1, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "radio-group", + "id": "orientation=horizontal|size=md__rest", + "name": "horizontal (orientation=horizontal|size=md)", + "caseKey": "orientation=horizontal|size=md", + "axes": { + "size": "md", + "orientation": "horizontal" + }, + "states": [ + "rest" + ], + "props": {}, + "declared": [ + "contrast", + "target", + "optionRow" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 0, + "helperText": 0, + "optionRow": 1, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "radio-group", + "id": "orientation=vertical|size=lg__rest", + "name": "default (orientation=vertical|size=lg)", + "caseKey": "orientation=vertical|size=lg", + "axes": { + "size": "lg", + "orientation": "vertical" + }, + "states": [ + "rest" + ], + "props": {}, + "declared": [ + "contrast", + "target", + "optionRow" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 0, + "helperText": 0, + "optionRow": 1, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "radio-group", + "id": "orientation=horizontal|size=lg__rest", + "name": "horizontal (orientation=horizontal|size=lg)", + "caseKey": "orientation=horizontal|size=lg", + "axes": { + "size": "lg", + "orientation": "horizontal" + }, + "states": [ + "rest" + ], + "props": {}, + "declared": [ + "contrast", + "target", + "optionRow" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 0, + "helperText": 0, + "optionRow": 1, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "radio-group", + "id": "orientation=vertical|size=sm__selected", + "name": "selected (orientation=vertical|size=sm)", + "caseKey": "orientation=vertical|size=sm", + "axes": { + "size": "sm", + "orientation": "vertical" + }, + "states": [ + "selected" + ], + "props": {}, + "declared": [ + "contrast", + "target", + "optionRow" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 0, + "helperText": 0, + "optionRow": 1, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "radio-group", + "id": "orientation=horizontal|size=sm__selected", + "name": "selected (orientation=horizontal|size=sm)", + "caseKey": "orientation=horizontal|size=sm", + "axes": { + "size": "sm", + "orientation": "horizontal" + }, + "states": [ + "selected" + ], + "props": {}, + "declared": [ + "contrast", + "target", + "optionRow" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 0, + "helperText": 0, + "optionRow": 1, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "radio-group", + "id": "orientation=vertical|size=md__selected", + "name": "selected (orientation=vertical|size=md)", + "caseKey": "orientation=vertical|size=md", + "axes": { + "size": "md", + "orientation": "vertical" + }, + "states": [ + "selected" + ], + "props": {}, + "declared": [ + "contrast", + "target", + "optionRow" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 0, + "helperText": 0, + "optionRow": 1, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "radio-group", + "id": "orientation=horizontal|size=md__selected", + "name": "selected (orientation=horizontal|size=md)", + "caseKey": "orientation=horizontal|size=md", + "axes": { + "size": "md", + "orientation": "horizontal" + }, + "states": [ + "selected" + ], + "props": {}, + "declared": [ + "contrast", + "target", + "optionRow" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 0, + "helperText": 0, + "optionRow": 1, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "radio-group", + "id": "orientation=vertical|size=lg__selected", + "name": "selected (orientation=vertical|size=lg)", + "caseKey": "orientation=vertical|size=lg", + "axes": { + "size": "lg", + "orientation": "vertical" + }, + "states": [ + "selected" + ], + "props": {}, + "declared": [ + "contrast", + "target", + "optionRow" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 0, + "helperText": 0, + "optionRow": 1, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "radio-group", + "id": "orientation=horizontal|size=lg__selected", + "name": "selected (orientation=horizontal|size=lg)", + "caseKey": "orientation=horizontal|size=lg", + "axes": { + "size": "lg", + "orientation": "horizontal" + }, + "states": [ + "selected" + ], + "props": {}, + "declared": [ + "contrast", + "target", + "optionRow" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 0, + "helperText": 0, + "optionRow": 1, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "range-date-picker", + "id": "size=sm__rest", + "name": "default-md (size=sm)", + "caseKey": "size=sm", + "axes": { + "size": "sm" + }, + "states": [ + "rest" + ], + "props": {}, + "declared": [ + "contrast", + "target", + "anchoredSurface", + "layout" + ], + "proofCounts": { + "contrast": 2, + "distinctness": 0, + "target": 2, + "layout": 1, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 3, + "motion": 0 + } + }, + { + "componentId": "range-date-picker", + "id": "size=md__rest", + "name": "default-md (size=md)", + "caseKey": "size=md", + "axes": { + "size": "md" + }, + "states": [ + "rest" + ], + "props": {}, + "declared": [ + "contrast", + "target", + "anchoredSurface", + "layout" + ], + "proofCounts": { + "contrast": 2, + "distinctness": 0, + "target": 2, + "layout": 1, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 3, + "motion": 0 + } + }, + { + "componentId": "range-date-picker", + "id": "size=lg__rest", + "name": "lg", + "caseKey": "size=lg", + "axes": { + "size": "lg" + }, + "states": [ + "rest" + ], + "props": {}, + "declared": [ + "contrast", + "target", + "anchoredSurface", + "layout" + ], + "proofCounts": { + "contrast": 2, + "distinctness": 0, + "target": 2, + "layout": 1, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 3, + "motion": 0 + } + }, + { + "componentId": "segmented-control", + "id": "size=sm__rest", + "name": "default (size=sm)", + "caseKey": "size=sm", + "axes": { + "size": "sm" + }, + "states": [ + "rest" + ], + "props": {}, + "declared": [ + "contrast", + "target" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 0, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "segmented-control", + "id": "size=md__rest", + "name": "default (size=md)", + "caseKey": "size=md", + "axes": { + "size": "md" + }, + "states": [ + "rest" + ], + "props": {}, + "declared": [ + "contrast", + "target" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 0, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "segmented-control", + "id": "size=sm__selected", + "name": "selected (size=sm)", + "caseKey": "size=sm", + "axes": { + "size": "sm" + }, + "states": [ + "selected" + ], + "props": {}, + "declared": [ + "contrast", + "target" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 0, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "segmented-control", + "id": "size=md__selected", + "name": "selected (size=md)", + "caseKey": "size=md", + "axes": { + "size": "md" + }, + "states": [ + "selected" + ], + "props": {}, + "declared": [ + "contrast", + "target" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 0, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "select", + "id": "size=sm__rest", + "name": "default (size=sm)", + "caseKey": "size=sm", + "axes": { + "size": "sm" + }, + "states": [ + "rest" + ], + "props": {}, + "declared": [ + "contrast", + "target", + "optionRow", + "anchoredSurface" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 0, + "helperText": 0, + "optionRow": 1, + "anchoredSurface": 3, + "motion": 0 + } + }, + { + "componentId": "select", + "id": "size=md__rest", + "name": "default (size=md)", + "caseKey": "size=md", + "axes": { + "size": "md" + }, + "states": [ + "rest" + ], + "props": {}, + "declared": [ + "contrast", + "target", + "optionRow", + "anchoredSurface" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 0, + "helperText": 0, + "optionRow": 1, + "anchoredSurface": 3, + "motion": 0 + } + }, + { + "componentId": "select", + "id": "size=lg__rest", + "name": "default (size=lg)", + "caseKey": "size=lg", + "axes": { + "size": "lg" + }, + "states": [ + "rest" + ], + "props": {}, + "declared": [ + "contrast", + "target", + "optionRow", + "anchoredSurface" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 0, + "helperText": 0, + "optionRow": 1, + "anchoredSurface": 3, + "motion": 0 + } + }, + { + "componentId": "select", + "id": "size=sm__selected", + "name": "selected (size=sm)", + "caseKey": "size=sm", + "axes": { + "size": "sm" + }, + "states": [ + "selected" + ], + "props": {}, + "declared": [ + "contrast", + "target", + "optionRow", + "anchoredSurface" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 0, + "helperText": 0, + "optionRow": 1, + "anchoredSurface": 3, + "motion": 0 + } + }, + { + "componentId": "select", + "id": "size=md__selected", + "name": "selected (size=md)", + "caseKey": "size=md", + "axes": { + "size": "md" + }, + "states": [ + "selected" + ], + "props": {}, + "declared": [ + "contrast", + "target", + "optionRow", + "anchoredSurface" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 0, + "helperText": 0, + "optionRow": 1, + "anchoredSurface": 3, + "motion": 0 + } + }, + { + "componentId": "select", + "id": "size=lg__selected", + "name": "selected (size=lg)", + "caseKey": "size=lg", + "axes": { + "size": "lg" + }, + "states": [ + "selected" + ], + "props": {}, + "declared": [ + "contrast", + "target", + "optionRow", + "anchoredSurface" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 0, + "helperText": 0, + "optionRow": 1, + "anchoredSurface": 3, + "motion": 0 + } + }, + { + "componentId": "select", + "id": "size=sm__invalid", + "name": "invalid (size=sm)", + "caseKey": "size=sm", + "axes": { + "size": "sm" + }, + "states": [ + "invalid" + ], + "props": {}, + "declared": [ + "contrast", + "target", + "optionRow", + "anchoredSurface" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 0, + "helperText": 0, + "optionRow": 1, + "anchoredSurface": 3, + "motion": 0 + } + }, + { + "componentId": "select", + "id": "size=md__invalid", + "name": "invalid (size=md)", + "caseKey": "size=md", + "axes": { + "size": "md" + }, + "states": [ + "invalid" + ], + "props": {}, + "declared": [ + "contrast", + "target", + "optionRow", + "anchoredSurface" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 0, + "helperText": 0, + "optionRow": 1, + "anchoredSurface": 3, + "motion": 0 + } + }, + { + "componentId": "select", + "id": "size=lg__invalid", + "name": "invalid (size=lg)", + "caseKey": "size=lg", + "axes": { + "size": "lg" + }, + "states": [ + "invalid" + ], + "props": {}, + "declared": [ + "contrast", + "target", + "optionRow", + "anchoredSurface" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 0, + "helperText": 0, + "optionRow": 1, + "anchoredSurface": 3, + "motion": 0 + } + }, + { + "componentId": "side-nav", + "id": "rest", + "name": "default", + "caseKey": "", + "axes": {}, + "states": [ + "rest" + ], + "props": {}, + "declared": [ + "contrast", + "target", + "layout" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 1, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "side-nav", + "id": "selected", + "name": "selected", + "caseKey": "", + "axes": {}, + "states": [ + "selected" + ], + "props": {}, + "declared": [ + "contrast", + "target", + "layout" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 1, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "skeleton", + "id": "size=md|variant=text__rest", + "name": "text-md", + "caseKey": "size=md|variant=text", + "axes": { + "variant": "text", + "size": "md" + }, + "states": [ + "rest" + ], + "props": {}, + "declared": [ + "layout" + ], + "proofCounts": { + "contrast": 0, + "distinctness": 0, + "target": 0, + "layout": 1, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "skeleton", + "id": "size=md|variant=block__rest", + "name": "block-md", + "caseKey": "size=md|variant=block", + "axes": { + "variant": "block", + "size": "md" + }, + "states": [ + "rest" + ], + "props": {}, + "declared": [ + "layout" + ], + "proofCounts": { + "contrast": 0, + "distinctness": 0, + "target": 0, + "layout": 1, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "skeleton", + "id": "size=lg|variant=avatar__rest", + "name": "avatar-lg", + "caseKey": "size=lg|variant=avatar", + "axes": { + "variant": "avatar", + "size": "lg" + }, + "states": [ + "rest" + ], + "props": {}, + "declared": [ + "layout" + ], + "proofCounts": { + "contrast": 0, + "distinctness": 0, + "target": 0, + "layout": 1, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "stepper", + "id": "orientation=horizontal|size=md__rest", + "name": "horizontal-md", + "caseKey": "orientation=horizontal|size=md", + "axes": { + "size": "md", + "orientation": "horizontal" + }, + "states": [ + "rest" + ], + "props": {}, + "declared": [ + "contrast", + "target" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 0, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "stepper", + "id": "orientation=vertical|size=sm__rest", + "name": "vertical-sm", + "caseKey": "orientation=vertical|size=sm", + "axes": { + "size": "sm", + "orientation": "vertical" + }, + "states": [ + "rest" + ], + "props": {}, + "declared": [ + "contrast", + "target" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 0, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "stepper", + "id": "orientation=horizontal|size=md__selected", + "name": "selected-md", + "caseKey": "orientation=horizontal|size=md", + "axes": { + "size": "md", + "orientation": "horizontal" + }, + "states": [ + "selected" + ], + "props": {}, + "declared": [ + "contrast", + "target" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 0, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "switch", + "id": "size=sm__rest", + "name": "default (size=sm)", + "caseKey": "size=sm", + "axes": { + "size": "sm" + }, + "states": [ + "rest" + ], + "props": {}, + "declared": [ + "contrast", + "target", + "motion" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 0, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 1 + } + }, + { + "componentId": "switch", + "id": "size=md__rest", + "name": "default (size=md)", + "caseKey": "size=md", + "axes": { + "size": "md" + }, + "states": [ + "rest" + ], + "props": {}, + "declared": [ + "contrast", + "target", + "motion" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 0, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 1 + } + }, + { + "componentId": "switch", + "id": "size=lg__rest", + "name": "default (size=lg)", + "caseKey": "size=lg", + "axes": { + "size": "lg" + }, + "states": [ + "rest" + ], + "props": {}, + "declared": [ + "contrast", + "target", + "motion" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 0, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 1 + } + }, + { + "componentId": "switch", + "id": "size=sm__checked", + "name": "checked (size=sm)", + "caseKey": "size=sm", + "axes": { + "size": "sm" + }, + "states": [ + "checked" + ], + "props": {}, + "declared": [ + "contrast", + "target", + "motion" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 0, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 1 + } + }, + { + "componentId": "switch", + "id": "size=md__checked", + "name": "checked (size=md)", + "caseKey": "size=md", + "axes": { + "size": "md" + }, + "states": [ + "checked" + ], + "props": {}, + "declared": [ + "contrast", + "target", + "motion" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 0, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 1 + } + }, + { + "componentId": "switch", + "id": "size=lg__checked", + "name": "checked (size=lg)", + "caseKey": "size=lg", + "axes": { + "size": "lg" + }, + "states": [ + "checked" + ], + "props": {}, + "declared": [ + "contrast", + "target", + "motion" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 0, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 1 + } + }, + { + "componentId": "table", + "id": "size=md__rest", + "name": "default-md", + "caseKey": "size=md", + "axes": { + "size": "md" + }, + "states": [ + "rest" + ], + "props": {}, + "declared": [ + "contrast", + "target", + "layout" + ], + "proofCounts": { + "contrast": 2, + "distinctness": 0, + "target": 2, + "layout": 1, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "table", + "id": "size=sm__rest", + "name": "dense-sm", + "caseKey": "size=sm", + "axes": { + "size": "sm" + }, + "states": [ + "rest" + ], + "props": {}, + "declared": [ + "contrast", + "target", + "layout" + ], + "proofCounts": { + "contrast": 2, + "distinctness": 0, + "target": 2, + "layout": 1, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "table", + "id": "size=md__selected", + "name": "selected-md", + "caseKey": "size=md", + "axes": { + "size": "md" + }, + "states": [ + "selected" + ], + "props": {}, + "declared": [ + "contrast", + "target", + "layout" + ], + "proofCounts": { + "contrast": 2, + "distinctness": 0, + "target": 2, + "layout": 1, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "tabs", + "id": "orientation=horizontal|size=sm__rest", + "name": "horizontal-default (orientation=horizontal|size=sm)", + "caseKey": "orientation=horizontal|size=sm", + "axes": { + "size": "sm", + "orientation": "horizontal" + }, + "states": [ + "rest" + ], + "props": {}, + "declared": [ + "contrast", + "target" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 0, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "tabs", + "id": "orientation=vertical|size=sm__rest", + "name": "vertical-default (orientation=vertical|size=sm)", + "caseKey": "orientation=vertical|size=sm", + "axes": { + "size": "sm", + "orientation": "vertical" + }, + "states": [ + "rest" + ], + "props": {}, + "declared": [ + "contrast", + "target" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 0, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "tabs", + "id": "orientation=horizontal|size=md__rest", + "name": "horizontal-default (orientation=horizontal|size=md)", + "caseKey": "orientation=horizontal|size=md", + "axes": { + "size": "md", + "orientation": "horizontal" + }, + "states": [ + "rest" + ], + "props": {}, + "declared": [ + "contrast", + "target" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 0, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "tabs", + "id": "orientation=vertical|size=md__rest", + "name": "vertical-default (orientation=vertical|size=md)", + "caseKey": "orientation=vertical|size=md", + "axes": { + "size": "md", + "orientation": "vertical" + }, + "states": [ + "rest" + ], + "props": {}, + "declared": [ + "contrast", + "target" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 0, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "tabs", + "id": "orientation=horizontal|size=lg__rest", + "name": "horizontal-default (orientation=horizontal|size=lg)", + "caseKey": "orientation=horizontal|size=lg", + "axes": { + "size": "lg", + "orientation": "horizontal" + }, + "states": [ + "rest" + ], + "props": {}, + "declared": [ + "contrast", + "target" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 0, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "tabs", + "id": "orientation=vertical|size=lg__rest", + "name": "vertical-default (orientation=vertical|size=lg)", + "caseKey": "orientation=vertical|size=lg", + "axes": { + "size": "lg", + "orientation": "vertical" + }, + "states": [ + "rest" + ], + "props": {}, + "declared": [ + "contrast", + "target" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 0, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "tabs", + "id": "orientation=horizontal|size=sm__selected", + "name": "selected (orientation=horizontal|size=sm)", + "caseKey": "orientation=horizontal|size=sm", + "axes": { + "size": "sm", + "orientation": "horizontal" + }, + "states": [ + "selected" + ], + "props": {}, + "declared": [ + "contrast", + "target" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 0, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "tabs", + "id": "orientation=vertical|size=sm__selected", + "name": "selected (orientation=vertical|size=sm)", + "caseKey": "orientation=vertical|size=sm", + "axes": { + "size": "sm", + "orientation": "vertical" + }, + "states": [ + "selected" + ], + "props": {}, + "declared": [ + "contrast", + "target" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 0, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "tabs", + "id": "orientation=horizontal|size=md__selected", + "name": "selected (orientation=horizontal|size=md)", + "caseKey": "orientation=horizontal|size=md", + "axes": { + "size": "md", + "orientation": "horizontal" + }, + "states": [ + "selected" + ], + "props": {}, + "declared": [ + "contrast", + "target" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 0, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "tabs", + "id": "orientation=vertical|size=md__selected", + "name": "selected (orientation=vertical|size=md)", + "caseKey": "orientation=vertical|size=md", + "axes": { + "size": "md", + "orientation": "vertical" + }, + "states": [ + "selected" + ], + "props": {}, + "declared": [ + "contrast", + "target" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 0, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "tabs", + "id": "orientation=horizontal|size=lg__selected", + "name": "selected (orientation=horizontal|size=lg)", + "caseKey": "orientation=horizontal|size=lg", + "axes": { + "size": "lg", + "orientation": "horizontal" + }, + "states": [ + "selected" + ], + "props": {}, + "declared": [ + "contrast", + "target" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 0, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "tabs", + "id": "orientation=vertical|size=lg__selected", + "name": "selected (orientation=vertical|size=lg)", + "caseKey": "orientation=vertical|size=lg", + "axes": { + "size": "lg", + "orientation": "vertical" + }, + "states": [ + "selected" + ], + "props": {}, + "declared": [ + "contrast", + "target" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 0, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "text-field", + "id": "size=xs__rest", + "name": "sizes (size=xs)", + "caseKey": "size=xs", + "axes": { + "size": "xs" + }, + "states": [ + "rest" + ], + "props": {}, + "sampleText": "Project name", + "declared": [ + "contrast", + "target", + "layout", + "helperText" + ], + "proofCounts": { + "contrast": 2, + "distinctness": 0, + "target": 1, + "layout": 1, + "helperText": 2, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "text-field", + "id": "size=sm__rest", + "name": "sizes (size=sm)", + "caseKey": "size=sm", + "axes": { + "size": "sm" + }, + "states": [ + "rest" + ], + "props": {}, + "sampleText": "Project name", + "declared": [ + "contrast", + "target", + "layout", + "helperText" + ], + "proofCounts": { + "contrast": 2, + "distinctness": 0, + "target": 1, + "layout": 1, + "helperText": 2, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "text-field", + "id": "size=md__rest", + "name": "sizes (size=md)", + "caseKey": "size=md", + "axes": { + "size": "md" + }, + "states": [ + "rest" + ], + "props": {}, + "sampleText": "Project name", + "declared": [ + "contrast", + "target", + "layout", + "helperText" + ], + "proofCounts": { + "contrast": 2, + "distinctness": 0, + "target": 1, + "layout": 1, + "helperText": 2, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "text-field", + "id": "size=lg__rest", + "name": "sizes (size=lg)", + "caseKey": "size=lg", + "axes": { + "size": "lg" + }, + "states": [ + "rest" + ], + "props": {}, + "sampleText": "Project name", + "declared": [ + "contrast", + "target", + "layout", + "helperText" + ], + "proofCounts": { + "contrast": 2, + "distinctness": 0, + "target": 1, + "layout": 1, + "helperText": 2, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "text-field", + "id": "size=md__invalid", + "name": "invalid-md", + "caseKey": "size=md", + "axes": { + "size": "md" + }, + "states": [ + "invalid" + ], + "props": {}, + "sampleText": "Project name", + "declared": [ + "contrast", + "target", + "layout", + "helperText" + ], + "proofCounts": { + "contrast": 2, + "distinctness": 0, + "target": 1, + "layout": 1, + "helperText": 2, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "text-field", + "id": "size=sm__disabled", + "name": "disabled-sm", + "caseKey": "size=sm", + "axes": { + "size": "sm" + }, + "states": [ + "disabled" + ], + "props": {}, + "sampleText": "Project name", + "declared": [ + "contrast", + "target", + "layout", + "helperText" + ], + "proofCounts": { + "contrast": 2, + "distinctness": 0, + "target": 1, + "layout": 1, + "helperText": 2, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "textarea", + "id": "size=sm__rest", + "name": "default (size=sm)", + "caseKey": "size=sm", + "axes": { + "size": "sm" + }, + "states": [ + "rest" + ], + "props": {}, + "sampleText": "Outline the decisions this release needs.", + "declared": [ + "contrast", + "target", + "layout", + "helperText" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 1, + "helperText": 1, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "textarea", + "id": "size=md__rest", + "name": "default (size=md)", + "caseKey": "size=md", + "axes": { + "size": "md" + }, + "states": [ + "rest" + ], + "props": {}, + "sampleText": "Outline the decisions this release needs.", + "declared": [ + "contrast", + "target", + "layout", + "helperText" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 1, + "helperText": 1, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "textarea", + "id": "size=lg__rest", + "name": "default (size=lg)", + "caseKey": "size=lg", + "axes": { + "size": "lg" + }, + "states": [ + "rest" + ], + "props": {}, + "sampleText": "Outline the decisions this release needs.", + "declared": [ + "contrast", + "target", + "layout", + "helperText" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 1, + "helperText": 1, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "textarea", + "id": "size=sm__invalid", + "name": "invalid (size=sm)", + "caseKey": "size=sm", + "axes": { + "size": "sm" + }, + "states": [ + "invalid" + ], + "props": {}, + "sampleText": "Outline the decisions this release needs.", + "declared": [ + "contrast", + "target", + "layout", + "helperText" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 1, + "helperText": 1, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "textarea", + "id": "size=md__invalid", + "name": "invalid (size=md)", + "caseKey": "size=md", + "axes": { + "size": "md" + }, + "states": [ + "invalid" + ], + "props": {}, + "sampleText": "Outline the decisions this release needs.", + "declared": [ + "contrast", + "target", + "layout", + "helperText" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 1, + "helperText": 1, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "textarea", + "id": "size=lg__invalid", + "name": "invalid (size=lg)", + "caseKey": "size=lg", + "axes": { + "size": "lg" + }, + "states": [ + "invalid" + ], + "props": {}, + "sampleText": "Outline the decisions this release needs.", + "declared": [ + "contrast", + "target", + "layout", + "helperText" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 1, + "helperText": 1, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "toast", + "id": "placement=bottom-right__rest", + "name": "toast-bottom-right", + "caseKey": "placement=bottom-right", + "axes": { + "placement": "bottom-right" + }, + "states": [ + "rest" + ], + "props": {}, + "declared": [ + "contrast", + "target", + "anchoredSurface", + "motion" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 0, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 3, + "motion": 1 + } + }, + { + "componentId": "toast", + "id": "placement=top-left__rest", + "name": "toast-top-left", + "caseKey": "placement=top-left", + "axes": { + "placement": "top-left" + }, + "states": [ + "rest" + ], + "props": {}, + "declared": [ + "contrast", + "target", + "anchoredSurface", + "motion" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 0, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 3, + "motion": 1 + } + }, + { + "componentId": "tooltip", + "id": "open", + "name": "tooltip-default", + "caseKey": "", + "axes": {}, + "states": [ + "open" + ], + "props": {}, + "sampleText": "Tooltip copy", + "declared": [ + "contrast", + "anchoredSurface", + "motion" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 0, + "layout": 0, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 3, + "motion": 1 + } + }, + { + "componentId": "tree-view", + "id": "rest", + "name": "default", + "caseKey": "", + "axes": {}, + "states": [ + "rest" + ], + "props": {}, + "declared": [ + "contrast", + "target", + "layout" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 1, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + }, + { + "componentId": "tree-view", + "id": "selected", + "name": "selected", + "caseKey": "", + "axes": {}, + "states": [ + "selected" + ], + "props": {}, + "declared": [ + "contrast", + "target", + "layout" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 1, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + } + } + ], + "runs": [ + { + "slug": "accordion", + "name": "Accordion", + "themeId": "browser-qualification", + "themeName": "Browser test", + "caseCount": 2, + "fixtureCount": 2, + "pass": false, + "failures": [ + { + "fixtureName": "Browser test / Accordion / accordion-md-open", + "caseKey": "size=md", + "reasons": [ + "layout overflow at 320px" + ] + } + ] + }, + { + "slug": "alert", + "name": "Alert", + "themeId": "browser-qualification", + "themeName": "Browser test", + "caseCount": 5, + "fixtureCount": 5, + "pass": true, + "failures": [] + }, + { + "slug": "avatar", + "name": "Avatar", + "themeId": "browser-qualification", + "themeName": "Browser test", + "caseCount": 6, + "fixtureCount": 3, + "pass": true, + "failures": [] + }, + { + "slug": "badge", + "name": "Badge", + "themeId": "browser-qualification", + "themeName": "Browser test", + "caseCount": 30, + "fixtureCount": 15, + "pass": false, + "failures": [ + { + "fixtureName": "Browser test / Badge / success-soft", + "caseKey": "emphasis=soft|size=md|tone=success", + "reasons": [ + "layout overflow at 120px" + ] + }, + { + "fixtureName": "Browser test / Badge / success-solid", + "caseKey": "emphasis=solid|size=md|tone=success", + "reasons": [ + "layout overflow at 120px" + ] + }, + { + "fixtureName": "Browser test / Badge / success-outline", + "caseKey": "emphasis=outline|size=md|tone=success", + "reasons": [ + "layout overflow at 120px" + ] + } + ] + }, + { + "slug": "breadcrumbs", + "name": "Breadcrumbs", + "themeId": "browser-qualification", + "themeName": "Browser test", + "caseCount": 2, + "fixtureCount": 1, + "pass": false, + "failures": [ + { + "fixtureName": "Browser test / Breadcrumbs / breadcrumbs-md", + "caseKey": "size=md", + "reasons": [ + "layout overflow at 220px" + ] + } + ] + }, + { + "slug": "button", + "name": "Button", + "themeId": "browser-qualification", + "themeName": "Browser test", + "caseCount": 120, + "fixtureCount": 13, + "pass": false, + "failures": [ + { + "fixtureName": "Browser test / Button / solid-sizes (content=label|size=lg|variant=solid)", + "caseKey": "content=label|size=lg|variant=solid", + "reasons": [ + "layout overflow at 180px" + ] + }, + { + "fixtureName": "Browser test / Button / link-anchor", + "caseKey": "content=label|size=md|variant=link", + "reasons": [ + "layout overflow at 180px" + ] + } + ] + }, + { + "slug": "card", + "name": "Card", + "themeId": "browser-qualification", + "themeName": "Browser test", + "caseCount": 9, + "fixtureCount": 3, + "pass": true, + "failures": [] + }, + { + "slug": "checkbox", + "name": "Checkbox", + "themeId": "browser-qualification", + "themeName": "Browser test", + "caseCount": 3, + "fixtureCount": 9, + "pass": false, + "failures": [ + { + "fixtureName": "Browser test / Checkbox / default (size=sm)", + "caseKey": "size=sm", + "reasons": [ + "layout overflow at 240px" + ] + }, + { + "fixtureName": "Browser test / Checkbox / default (size=md)", + "caseKey": "size=md", + "reasons": [ + "layout overflow at 240px" + ] + }, + { + "fixtureName": "Browser test / Checkbox / default (size=lg)", + "caseKey": "size=lg", + "reasons": [ + "layout overflow at 240px" + ] + }, + { + "fixtureName": "Browser test / Checkbox / checked (size=sm)", + "caseKey": "size=sm", + "reasons": [ + "layout overflow at 240px" + ] + }, + { + "fixtureName": "Browser test / Checkbox / checked (size=md)", + "caseKey": "size=md", + "reasons": [ + "layout overflow at 240px" + ] + }, + { + "fixtureName": "Browser test / Checkbox / checked (size=lg)", + "caseKey": "size=lg", + "reasons": [ + "layout overflow at 240px" + ] + }, + { + "fixtureName": "Browser test / Checkbox / indeterminate (size=sm)", + "caseKey": "size=sm", + "reasons": [ + "layout overflow at 240px" + ] + }, + { + "fixtureName": "Browser test / Checkbox / indeterminate (size=md)", + "caseKey": "size=md", + "reasons": [ + "layout overflow at 240px" + ] + }, + { + "fixtureName": "Browser test / Checkbox / indeterminate (size=lg)", + "caseKey": "size=lg", + "reasons": [ + "layout overflow at 240px" + ] + } + ] + }, + { + "slug": "chip", + "name": "Chip", + "themeId": "browser-qualification", + "themeName": "Browser test", + "caseCount": 10, + "fixtureCount": 3, + "pass": true, + "failures": [] + }, + { + "slug": "combobox", + "name": "Combobox", + "themeId": "browser-qualification", + "themeName": "Browser test", + "caseCount": 3, + "fixtureCount": 9, + "pass": true, + "failures": [] + }, + { + "slug": "command-palette", + "name": "CommandPalette", + "themeId": "browser-qualification", + "themeName": "Browser test", + "caseCount": 1, + "fixtureCount": 2, + "pass": true, + "failures": [] + }, + { + "slug": "data-chart", + "name": "DataChart", + "themeId": "browser-qualification", + "themeName": "Browser test", + "caseCount": 3, + "fixtureCount": 3, + "pass": true, + "failures": [] + }, + { + "slug": "data-grid-lite", + "name": "DataGridLite", + "themeId": "browser-qualification", + "themeName": "Browser test", + "caseCount": 2, + "fixtureCount": 3, + "pass": true, + "failures": [] + }, + { + "slug": "date-picker", + "name": "DatePicker", + "themeId": "browser-qualification", + "themeName": "Browser test", + "caseCount": 3, + "fixtureCount": 3, + "pass": true, + "failures": [] + }, + { + "slug": "dialog", + "name": "Dialog", + "themeId": "browser-qualification", + "themeName": "Browser test", + "caseCount": 3, + "fixtureCount": 3, + "pass": true, + "failures": [] + }, + { + "slug": "drawer", + "name": "Drawer", + "themeId": "browser-qualification", + "themeName": "Browser test", + "caseCount": 6, + "fixtureCount": 6, + "pass": true, + "failures": [] + }, + { + "slug": "empty-state", + "name": "EmptyState", + "themeId": "browser-qualification", + "themeName": "Browser test", + "caseCount": 15, + "fixtureCount": 3, + "pass": true, + "failures": [] + }, + { + "slug": "file-upload", + "name": "FileUpload", + "themeId": "browser-qualification", + "themeName": "Browser test", + "caseCount": 3, + "fixtureCount": 6, + "pass": true, + "failures": [] + }, + { + "slug": "inline-edit", + "name": "InlineEdit", + "themeId": "browser-qualification", + "themeName": "Browser test", + "caseCount": 3, + "fixtureCount": 1, + "pass": true, + "failures": [] + }, + { + "slug": "menu", + "name": "Menu", + "themeId": "browser-qualification", + "themeName": "Browser test", + "caseCount": 3, + "fixtureCount": 6, + "pass": true, + "failures": [] + }, + { + "slug": "pagination", + "name": "Pagination", + "themeId": "browser-qualification", + "themeName": "Browser test", + "caseCount": 2, + "fixtureCount": 1, + "pass": true, + "failures": [] + }, + { + "slug": "popover", + "name": "Popover", + "themeId": "browser-qualification", + "themeName": "Browser test", + "caseCount": 3, + "fixtureCount": 3, + "pass": true, + "failures": [] + }, + { + "slug": "progress", + "name": "Progress", + "themeId": "browser-qualification", + "themeName": "Browser test", + "caseCount": 10, + "fixtureCount": 3, + "pass": true, + "failures": [] + }, + { + "slug": "radio-group", + "name": "RadioGroup", + "themeId": "browser-qualification", + "themeName": "Browser test", + "caseCount": 6, + "fixtureCount": 12, + "pass": true, + "failures": [] + }, + { + "slug": "range-date-picker", + "name": "RangeDatePicker", + "themeId": "browser-qualification", + "themeName": "Browser test", + "caseCount": 3, + "fixtureCount": 3, + "pass": true, + "failures": [] + }, + { + "slug": "segmented-control", + "name": "SegmentedControl", + "themeId": "browser-qualification", + "themeName": "Browser test", + "caseCount": 2, + "fixtureCount": 4, + "pass": true, + "failures": [] + }, + { + "slug": "select", + "name": "Select", + "themeId": "browser-qualification", + "themeName": "Browser test", + "caseCount": 3, + "fixtureCount": 9, + "pass": true, + "failures": [] + }, + { + "slug": "side-nav", + "name": "SideNav", + "themeId": "browser-qualification", + "themeName": "Browser test", + "caseCount": 1, + "fixtureCount": 2, + "pass": true, + "failures": [] + }, + { + "slug": "skeleton", + "name": "Skeleton", + "themeId": "browser-qualification", + "themeName": "Browser test", + "caseCount": 9, + "fixtureCount": 3, + "pass": true, + "failures": [] + }, + { + "slug": "stepper", + "name": "Stepper", + "themeId": "browser-qualification", + "themeName": "Browser test", + "caseCount": 4, + "fixtureCount": 3, + "pass": true, + "failures": [] + }, + { + "slug": "switch", + "name": "Switch", + "themeId": "browser-qualification", + "themeName": "Browser test", + "caseCount": 3, + "fixtureCount": 6, + "pass": true, + "failures": [] + }, + { + "slug": "table", + "name": "Table", + "themeId": "browser-qualification", + "themeName": "Browser test", + "caseCount": 2, + "fixtureCount": 3, + "pass": true, + "failures": [] + }, + { + "slug": "tabs", + "name": "Tabs", + "themeId": "browser-qualification", + "themeName": "Browser test", + "caseCount": 6, + "fixtureCount": 12, + "pass": true, + "failures": [] + }, + { + "slug": "text-field", + "name": "TextField", + "themeId": "browser-qualification", + "themeName": "Browser test", + "caseCount": 4, + "fixtureCount": 6, + "pass": false, + "failures": [ + { + "fixtureName": "Browser test / TextField / sizes (size=lg)", + "caseKey": "size=lg", + "reasons": [ + "layout overflow at 240px" + ] + } + ] + }, + { + "slug": "textarea", + "name": "Textarea", + "themeId": "browser-qualification", + "themeName": "Browser test", + "caseCount": 3, + "fixtureCount": 6, + "pass": true, + "failures": [] + }, + { + "slug": "toast", + "name": "Toast", + "themeId": "browser-qualification", + "themeName": "Browser test", + "caseCount": 4, + "fixtureCount": 2, + "pass": true, + "failures": [] + }, + { + "slug": "tooltip", + "name": "Tooltip", + "themeId": "browser-qualification", + "themeName": "Browser test", + "caseCount": 1, + "fixtureCount": 1, + "pass": true, + "failures": [] + }, + { + "slug": "tree-view", + "name": "TreeView", + "themeId": "browser-qualification", + "themeName": "Browser test", + "caseCount": 1, + "fixtureCount": 2, + "pass": true, + "failures": [] + } + ], + "summary": { + "componentCount": 38, + "themeCount": 1, + "runCount": 38, + "passedRuns": 32, + "fixtureCount": 180, + "failedFixtureCount": 17, + "pass": false + } + }, + "matrix": { + "evidence": { + "kind": "mathematical", + "rendered": "not-collected" + }, + "components": [ + "accordion", + "alert", + "avatar", + "badge", + "breadcrumbs", + "button", + "card", + "checkbox", + "chip", + "combobox", + "command-palette", + "data-chart", + "data-grid-lite", + "date-picker", + "dialog", + "drawer", + "empty-state", + "file-upload", + "inline-edit", + "menu", + "pagination", + "popover", + "progress", + "radio-group", + "range-date-picker", + "segmented-control", + "select", + "side-nav", + "skeleton", + "stepper", + "switch", + "table", + "tabs", + "text-field", + "textarea", + "toast", + "tooltip", + "tree-view" + ], + "themes": [ + "cobalt", + "sage", + "ember", + "linen" + ], + "summary": { + "componentCount": 38, + "themeCount": 4, + "runCount": 152, + "passedRuns": 132, + "fixtureCount": 720, + "failedFixtureCount": 45, + "pass": false + }, + "runs": [ + { + "slug": "accordion", + "name": "Accordion", + "themeId": "cobalt", + "themeName": "Cobalt", + "caseCount": 2, + "fixtureCount": 2, + "pass": false, + "failures": [ + { + "fixtureName": "Cobalt / Accordion / accordion-md-open", + "caseKey": "size=md", + "reasons": [ + "layout overflow at 320px" + ] + } + ] + }, + { + "slug": "alert", + "name": "Alert", + "themeId": "cobalt", + "themeName": "Cobalt", + "caseCount": 5, + "fixtureCount": 5, + "pass": true, + "failures": [] + }, + { + "slug": "avatar", + "name": "Avatar", + "themeId": "cobalt", + "themeName": "Cobalt", + "caseCount": 6, + "fixtureCount": 3, + "pass": true, + "failures": [] + }, + { + "slug": "badge", + "name": "Badge", + "themeId": "cobalt", + "themeName": "Cobalt", + "caseCount": 30, + "fixtureCount": 15, + "pass": false, + "failures": [ + { + "fixtureName": "Cobalt / Badge / success-soft", + "caseKey": "emphasis=soft|size=md|tone=success", + "reasons": [ + "layout overflow at 120px" + ] + }, + { + "fixtureName": "Cobalt / Badge / success-solid", + "caseKey": "emphasis=solid|size=md|tone=success", + "reasons": [ + "layout overflow at 120px" + ] + }, + { + "fixtureName": "Cobalt / Badge / success-outline", + "caseKey": "emphasis=outline|size=md|tone=success", + "reasons": [ + "layout overflow at 120px" + ] + } + ] + }, + { + "slug": "breadcrumbs", + "name": "Breadcrumbs", + "themeId": "cobalt", + "themeName": "Cobalt", + "caseCount": 2, + "fixtureCount": 1, + "pass": false, + "failures": [ + { + "fixtureName": "Cobalt / Breadcrumbs / breadcrumbs-md", + "caseKey": "size=md", + "reasons": [ + "layout overflow at 220px" + ] + } + ] + }, + { + "slug": "button", + "name": "Button", + "themeId": "cobalt", + "themeName": "Cobalt", + "caseCount": 120, + "fixtureCount": 13, + "pass": false, + "failures": [ + { + "fixtureName": "Cobalt / Button / solid-sizes (content=label|size=lg|variant=solid)", + "caseKey": "content=label|size=lg|variant=solid", + "reasons": [ + "layout overflow at 180px" + ] + }, + { + "fixtureName": "Cobalt / Button / link-anchor", + "caseKey": "content=label|size=md|variant=link", + "reasons": [ + "layout overflow at 180px" + ] + } + ] + }, + { + "slug": "card", + "name": "Card", + "themeId": "cobalt", + "themeName": "Cobalt", + "caseCount": 9, + "fixtureCount": 3, + "pass": true, + "failures": [] + }, + { + "slug": "checkbox", + "name": "Checkbox", + "themeId": "cobalt", + "themeName": "Cobalt", + "caseCount": 3, + "fixtureCount": 9, + "pass": false, + "failures": [ + { + "fixtureName": "Cobalt / Checkbox / default (size=sm)", + "caseKey": "size=sm", + "reasons": [ + "layout overflow at 240px" + ] + }, + { + "fixtureName": "Cobalt / Checkbox / default (size=md)", + "caseKey": "size=md", + "reasons": [ + "layout overflow at 240px" + ] + }, + { + "fixtureName": "Cobalt / Checkbox / default (size=lg)", + "caseKey": "size=lg", + "reasons": [ + "layout overflow at 240px" + ] + }, + { + "fixtureName": "Cobalt / Checkbox / checked (size=sm)", + "caseKey": "size=sm", + "reasons": [ + "layout overflow at 240px" + ] + }, + { + "fixtureName": "Cobalt / Checkbox / checked (size=md)", + "caseKey": "size=md", + "reasons": [ + "layout overflow at 240px" + ] + }, + { + "fixtureName": "Cobalt / Checkbox / checked (size=lg)", + "caseKey": "size=lg", + "reasons": [ + "layout overflow at 240px" + ] + }, + { + "fixtureName": "Cobalt / Checkbox / indeterminate (size=sm)", + "caseKey": "size=sm", + "reasons": [ + "layout overflow at 240px" + ] + }, + { + "fixtureName": "Cobalt / Checkbox / indeterminate (size=md)", + "caseKey": "size=md", + "reasons": [ + "layout overflow at 240px" + ] + }, + { + "fixtureName": "Cobalt / Checkbox / indeterminate (size=lg)", + "caseKey": "size=lg", + "reasons": [ + "layout overflow at 240px" + ] + } + ] + }, + { + "slug": "chip", + "name": "Chip", + "themeId": "cobalt", + "themeName": "Cobalt", + "caseCount": 10, + "fixtureCount": 3, + "pass": true, + "failures": [] + }, + { + "slug": "combobox", + "name": "Combobox", + "themeId": "cobalt", + "themeName": "Cobalt", + "caseCount": 3, + "fixtureCount": 9, + "pass": true, + "failures": [] + }, + { + "slug": "command-palette", + "name": "CommandPalette", + "themeId": "cobalt", + "themeName": "Cobalt", + "caseCount": 1, + "fixtureCount": 2, + "pass": true, + "failures": [] + }, + { + "slug": "data-chart", + "name": "DataChart", + "themeId": "cobalt", + "themeName": "Cobalt", + "caseCount": 3, + "fixtureCount": 3, + "pass": true, + "failures": [] + }, + { + "slug": "data-grid-lite", + "name": "DataGridLite", + "themeId": "cobalt", + "themeName": "Cobalt", + "caseCount": 2, + "fixtureCount": 3, + "pass": true, + "failures": [] + }, + { + "slug": "date-picker", + "name": "DatePicker", + "themeId": "cobalt", + "themeName": "Cobalt", + "caseCount": 3, + "fixtureCount": 3, + "pass": true, + "failures": [] + }, + { + "slug": "dialog", + "name": "Dialog", + "themeId": "cobalt", + "themeName": "Cobalt", + "caseCount": 3, + "fixtureCount": 3, + "pass": true, + "failures": [] + }, + { + "slug": "drawer", + "name": "Drawer", + "themeId": "cobalt", + "themeName": "Cobalt", + "caseCount": 6, + "fixtureCount": 6, + "pass": true, + "failures": [] + }, + { + "slug": "empty-state", + "name": "EmptyState", + "themeId": "cobalt", + "themeName": "Cobalt", + "caseCount": 15, + "fixtureCount": 3, + "pass": true, + "failures": [] + }, + { + "slug": "file-upload", + "name": "FileUpload", + "themeId": "cobalt", + "themeName": "Cobalt", + "caseCount": 3, + "fixtureCount": 6, + "pass": true, + "failures": [] + }, + { + "slug": "inline-edit", + "name": "InlineEdit", + "themeId": "cobalt", + "themeName": "Cobalt", + "caseCount": 3, + "fixtureCount": 1, + "pass": true, + "failures": [] + }, + { + "slug": "menu", + "name": "Menu", + "themeId": "cobalt", + "themeName": "Cobalt", + "caseCount": 3, + "fixtureCount": 6, + "pass": true, + "failures": [] + }, + { + "slug": "pagination", + "name": "Pagination", + "themeId": "cobalt", + "themeName": "Cobalt", + "caseCount": 2, + "fixtureCount": 1, + "pass": true, + "failures": [] + }, + { + "slug": "popover", + "name": "Popover", + "themeId": "cobalt", + "themeName": "Cobalt", + "caseCount": 3, + "fixtureCount": 3, + "pass": true, + "failures": [] + }, + { + "slug": "progress", + "name": "Progress", + "themeId": "cobalt", + "themeName": "Cobalt", + "caseCount": 10, + "fixtureCount": 3, + "pass": true, + "failures": [] + }, + { + "slug": "radio-group", + "name": "RadioGroup", + "themeId": "cobalt", + "themeName": "Cobalt", + "caseCount": 6, + "fixtureCount": 12, + "pass": true, + "failures": [] + }, + { + "slug": "range-date-picker", + "name": "RangeDatePicker", + "themeId": "cobalt", + "themeName": "Cobalt", + "caseCount": 3, + "fixtureCount": 3, + "pass": true, + "failures": [] + }, + { + "slug": "segmented-control", + "name": "SegmentedControl", + "themeId": "cobalt", + "themeName": "Cobalt", + "caseCount": 2, + "fixtureCount": 4, + "pass": true, + "failures": [] + }, + { + "slug": "select", + "name": "Select", + "themeId": "cobalt", + "themeName": "Cobalt", + "caseCount": 3, + "fixtureCount": 9, + "pass": true, + "failures": [] + }, + { + "slug": "side-nav", + "name": "SideNav", + "themeId": "cobalt", + "themeName": "Cobalt", + "caseCount": 1, + "fixtureCount": 2, + "pass": true, + "failures": [] + }, + { + "slug": "skeleton", + "name": "Skeleton", + "themeId": "cobalt", + "themeName": "Cobalt", + "caseCount": 9, + "fixtureCount": 3, + "pass": true, + "failures": [] + }, + { + "slug": "stepper", + "name": "Stepper", + "themeId": "cobalt", + "themeName": "Cobalt", + "caseCount": 4, + "fixtureCount": 3, + "pass": true, + "failures": [] + }, + { + "slug": "switch", + "name": "Switch", + "themeId": "cobalt", + "themeName": "Cobalt", + "caseCount": 3, + "fixtureCount": 6, + "pass": true, + "failures": [] + }, + { + "slug": "table", + "name": "Table", + "themeId": "cobalt", + "themeName": "Cobalt", + "caseCount": 2, + "fixtureCount": 3, + "pass": true, + "failures": [] + }, + { + "slug": "tabs", + "name": "Tabs", + "themeId": "cobalt", + "themeName": "Cobalt", + "caseCount": 6, + "fixtureCount": 12, + "pass": true, + "failures": [] + }, + { + "slug": "text-field", + "name": "TextField", + "themeId": "cobalt", + "themeName": "Cobalt", + "caseCount": 4, + "fixtureCount": 6, + "pass": false, + "failures": [ + { + "fixtureName": "Cobalt / TextField / sizes (size=lg)", + "caseKey": "size=lg", + "reasons": [ + "layout overflow at 240px" + ] + } + ] + }, + { + "slug": "textarea", + "name": "Textarea", + "themeId": "cobalt", + "themeName": "Cobalt", + "caseCount": 3, + "fixtureCount": 6, + "pass": true, + "failures": [] + }, + { + "slug": "toast", + "name": "Toast", + "themeId": "cobalt", + "themeName": "Cobalt", + "caseCount": 4, + "fixtureCount": 2, + "pass": true, + "failures": [] + }, + { + "slug": "tooltip", + "name": "Tooltip", + "themeId": "cobalt", + "themeName": "Cobalt", + "caseCount": 1, + "fixtureCount": 1, + "pass": true, + "failures": [] + }, + { + "slug": "tree-view", + "name": "TreeView", + "themeId": "cobalt", + "themeName": "Cobalt", + "caseCount": 1, + "fixtureCount": 2, + "pass": true, + "failures": [] + }, + { + "slug": "accordion", + "name": "Accordion", + "themeId": "sage", + "themeName": "Sage", + "caseCount": 2, + "fixtureCount": 2, + "pass": false, + "failures": [ + { + "fixtureName": "Sage / Accordion / accordion-md-open", + "caseKey": "size=md", + "reasons": [ + "layout overflow at 320px" + ] + } + ] + }, + { + "slug": "alert", + "name": "Alert", + "themeId": "sage", + "themeName": "Sage", + "caseCount": 5, + "fixtureCount": 5, + "pass": true, + "failures": [] + }, + { + "slug": "avatar", + "name": "Avatar", + "themeId": "sage", + "themeName": "Sage", + "caseCount": 6, + "fixtureCount": 3, + "pass": true, + "failures": [] + }, + { + "slug": "badge", + "name": "Badge", + "themeId": "sage", + "themeName": "Sage", + "caseCount": 30, + "fixtureCount": 15, + "pass": true, + "failures": [] + }, + { + "slug": "breadcrumbs", + "name": "Breadcrumbs", + "themeId": "sage", + "themeName": "Sage", + "caseCount": 2, + "fixtureCount": 1, + "pass": false, + "failures": [ + { + "fixtureName": "Sage / Breadcrumbs / breadcrumbs-md", + "caseKey": "size=md", + "reasons": [ + "layout overflow at 220px" + ] + } + ] + }, + { + "slug": "button", + "name": "Button", + "themeId": "sage", + "themeName": "Sage", + "caseCount": 120, + "fixtureCount": 13, + "pass": false, + "failures": [ + { + "fixtureName": "Sage / Button / solid-sizes (content=label|size=lg|variant=solid)", + "caseKey": "content=label|size=lg|variant=solid", + "reasons": [ + "layout overflow at 180px" + ] + }, + { + "fixtureName": "Sage / Button / link-anchor", + "caseKey": "content=label|size=md|variant=link", + "reasons": [ + "layout overflow at 180px" + ] + } + ] + }, + { + "slug": "card", + "name": "Card", + "themeId": "sage", + "themeName": "Sage", + "caseCount": 9, + "fixtureCount": 3, + "pass": true, + "failures": [] + }, + { + "slug": "checkbox", + "name": "Checkbox", + "themeId": "sage", + "themeName": "Sage", + "caseCount": 3, + "fixtureCount": 9, + "pass": true, + "failures": [] + }, + { + "slug": "chip", + "name": "Chip", + "themeId": "sage", + "themeName": "Sage", + "caseCount": 10, + "fixtureCount": 3, + "pass": true, + "failures": [] + }, + { + "slug": "combobox", + "name": "Combobox", + "themeId": "sage", + "themeName": "Sage", + "caseCount": 3, + "fixtureCount": 9, + "pass": true, + "failures": [] + }, + { + "slug": "command-palette", + "name": "CommandPalette", + "themeId": "sage", + "themeName": "Sage", + "caseCount": 1, + "fixtureCount": 2, + "pass": true, + "failures": [] + }, + { + "slug": "data-chart", + "name": "DataChart", + "themeId": "sage", + "themeName": "Sage", + "caseCount": 3, + "fixtureCount": 3, + "pass": true, + "failures": [] + }, + { + "slug": "data-grid-lite", + "name": "DataGridLite", + "themeId": "sage", + "themeName": "Sage", + "caseCount": 2, + "fixtureCount": 3, + "pass": true, + "failures": [] + }, + { + "slug": "date-picker", + "name": "DatePicker", + "themeId": "sage", + "themeName": "Sage", + "caseCount": 3, + "fixtureCount": 3, + "pass": true, + "failures": [] + }, + { + "slug": "dialog", + "name": "Dialog", + "themeId": "sage", + "themeName": "Sage", + "caseCount": 3, + "fixtureCount": 3, + "pass": true, + "failures": [] + }, + { + "slug": "drawer", + "name": "Drawer", + "themeId": "sage", + "themeName": "Sage", + "caseCount": 6, + "fixtureCount": 6, + "pass": true, + "failures": [] + }, + { + "slug": "empty-state", + "name": "EmptyState", + "themeId": "sage", + "themeName": "Sage", + "caseCount": 15, + "fixtureCount": 3, + "pass": true, + "failures": [] + }, + { + "slug": "file-upload", + "name": "FileUpload", + "themeId": "sage", + "themeName": "Sage", + "caseCount": 3, + "fixtureCount": 6, + "pass": true, + "failures": [] + }, + { + "slug": "inline-edit", + "name": "InlineEdit", + "themeId": "sage", + "themeName": "Sage", + "caseCount": 3, + "fixtureCount": 1, + "pass": true, + "failures": [] + }, + { + "slug": "menu", + "name": "Menu", + "themeId": "sage", + "themeName": "Sage", + "caseCount": 3, + "fixtureCount": 6, + "pass": true, + "failures": [] + }, + { + "slug": "pagination", + "name": "Pagination", + "themeId": "sage", + "themeName": "Sage", + "caseCount": 2, + "fixtureCount": 1, + "pass": true, + "failures": [] + }, + { + "slug": "popover", + "name": "Popover", + "themeId": "sage", + "themeName": "Sage", + "caseCount": 3, + "fixtureCount": 3, + "pass": true, + "failures": [] + }, + { + "slug": "progress", + "name": "Progress", + "themeId": "sage", + "themeName": "Sage", + "caseCount": 10, + "fixtureCount": 3, + "pass": true, + "failures": [] + }, + { + "slug": "radio-group", + "name": "RadioGroup", + "themeId": "sage", + "themeName": "Sage", + "caseCount": 6, + "fixtureCount": 12, + "pass": true, + "failures": [] + }, + { + "slug": "range-date-picker", + "name": "RangeDatePicker", + "themeId": "sage", + "themeName": "Sage", + "caseCount": 3, + "fixtureCount": 3, + "pass": true, + "failures": [] + }, + { + "slug": "segmented-control", + "name": "SegmentedControl", + "themeId": "sage", + "themeName": "Sage", + "caseCount": 2, + "fixtureCount": 4, + "pass": true, + "failures": [] + }, + { + "slug": "select", + "name": "Select", + "themeId": "sage", + "themeName": "Sage", + "caseCount": 3, + "fixtureCount": 9, + "pass": true, + "failures": [] + }, + { + "slug": "side-nav", + "name": "SideNav", + "themeId": "sage", + "themeName": "Sage", + "caseCount": 1, + "fixtureCount": 2, + "pass": true, + "failures": [] + }, + { + "slug": "skeleton", + "name": "Skeleton", + "themeId": "sage", + "themeName": "Sage", + "caseCount": 9, + "fixtureCount": 3, + "pass": true, + "failures": [] + }, + { + "slug": "stepper", + "name": "Stepper", + "themeId": "sage", + "themeName": "Sage", + "caseCount": 4, + "fixtureCount": 3, + "pass": true, + "failures": [] + }, + { + "slug": "switch", + "name": "Switch", + "themeId": "sage", + "themeName": "Sage", + "caseCount": 3, + "fixtureCount": 6, + "pass": true, + "failures": [] + }, + { + "slug": "table", + "name": "Table", + "themeId": "sage", + "themeName": "Sage", + "caseCount": 2, + "fixtureCount": 3, + "pass": true, + "failures": [] + }, + { + "slug": "tabs", + "name": "Tabs", + "themeId": "sage", + "themeName": "Sage", + "caseCount": 6, + "fixtureCount": 12, + "pass": true, + "failures": [] + }, + { + "slug": "text-field", + "name": "TextField", + "themeId": "sage", + "themeName": "Sage", + "caseCount": 4, + "fixtureCount": 6, + "pass": false, + "failures": [ + { + "fixtureName": "Sage / TextField / sizes (size=lg)", + "caseKey": "size=lg", + "reasons": [ + "layout overflow at 240px" + ] + } + ] + }, + { + "slug": "textarea", + "name": "Textarea", + "themeId": "sage", + "themeName": "Sage", + "caseCount": 3, + "fixtureCount": 6, + "pass": true, + "failures": [] + }, + { + "slug": "toast", + "name": "Toast", + "themeId": "sage", + "themeName": "Sage", + "caseCount": 4, + "fixtureCount": 2, + "pass": true, + "failures": [] + }, + { + "slug": "tooltip", + "name": "Tooltip", + "themeId": "sage", + "themeName": "Sage", + "caseCount": 1, + "fixtureCount": 1, + "pass": true, + "failures": [] + }, + { + "slug": "tree-view", + "name": "TreeView", + "themeId": "sage", + "themeName": "Sage", + "caseCount": 1, + "fixtureCount": 2, + "pass": true, + "failures": [] + }, + { + "slug": "accordion", + "name": "Accordion", + "themeId": "ember", + "themeName": "Ember", + "caseCount": 2, + "fixtureCount": 2, + "pass": false, + "failures": [ + { + "fixtureName": "Ember / Accordion / accordion-md-open", + "caseKey": "size=md", + "reasons": [ + "layout overflow at 320px" + ] + } + ] + }, + { + "slug": "alert", + "name": "Alert", + "themeId": "ember", + "themeName": "Ember", + "caseCount": 5, + "fixtureCount": 5, + "pass": true, + "failures": [] + }, + { + "slug": "avatar", + "name": "Avatar", + "themeId": "ember", + "themeName": "Ember", + "caseCount": 6, + "fixtureCount": 3, + "pass": true, + "failures": [] + }, + { + "slug": "badge", + "name": "Badge", + "themeId": "ember", + "themeName": "Ember", + "caseCount": 30, + "fixtureCount": 15, + "pass": false, + "failures": [ + { + "fixtureName": "Ember / Badge / success-soft", + "caseKey": "emphasis=soft|size=md|tone=success", + "reasons": [ + "layout overflow at 120px" + ] + }, + { + "fixtureName": "Ember / Badge / success-solid", + "caseKey": "emphasis=solid|size=md|tone=success", + "reasons": [ + "layout overflow at 120px" + ] + }, + { + "fixtureName": "Ember / Badge / success-outline", + "caseKey": "emphasis=outline|size=md|tone=success", + "reasons": [ + "layout overflow at 120px" + ] + } + ] + }, + { + "slug": "breadcrumbs", + "name": "Breadcrumbs", + "themeId": "ember", + "themeName": "Ember", + "caseCount": 2, + "fixtureCount": 1, + "pass": false, + "failures": [ + { + "fixtureName": "Ember / Breadcrumbs / breadcrumbs-md", + "caseKey": "size=md", + "reasons": [ + "layout overflow at 220px" + ] + } + ] + }, + { + "slug": "button", + "name": "Button", + "themeId": "ember", + "themeName": "Ember", + "caseCount": 120, + "fixtureCount": 13, + "pass": false, + "failures": [ + { + "fixtureName": "Ember / Button / solid-sizes (content=label|size=lg|variant=solid)", + "caseKey": "content=label|size=lg|variant=solid", + "reasons": [ + "layout overflow at 180px" + ] + }, + { + "fixtureName": "Ember / Button / link-anchor", + "caseKey": "content=label|size=md|variant=link", + "reasons": [ + "layout overflow at 180px" + ] + } + ] + }, + { + "slug": "card", + "name": "Card", + "themeId": "ember", + "themeName": "Ember", + "caseCount": 9, + "fixtureCount": 3, + "pass": true, + "failures": [] + }, + { + "slug": "checkbox", + "name": "Checkbox", + "themeId": "ember", + "themeName": "Ember", + "caseCount": 3, + "fixtureCount": 9, + "pass": false, + "failures": [ + { + "fixtureName": "Ember / Checkbox / default (size=sm)", + "caseKey": "size=sm", + "reasons": [ + "layout overflow at 240px" + ] + }, + { + "fixtureName": "Ember / Checkbox / default (size=md)", + "caseKey": "size=md", + "reasons": [ + "layout overflow at 240px" + ] + }, + { + "fixtureName": "Ember / Checkbox / default (size=lg)", + "caseKey": "size=lg", + "reasons": [ + "layout overflow at 240px" + ] + }, + { + "fixtureName": "Ember / Checkbox / checked (size=sm)", + "caseKey": "size=sm", + "reasons": [ + "layout overflow at 240px" + ] + }, + { + "fixtureName": "Ember / Checkbox / checked (size=md)", + "caseKey": "size=md", + "reasons": [ + "layout overflow at 240px" + ] + }, + { + "fixtureName": "Ember / Checkbox / checked (size=lg)", + "caseKey": "size=lg", + "reasons": [ + "layout overflow at 240px" + ] + }, + { + "fixtureName": "Ember / Checkbox / indeterminate (size=sm)", + "caseKey": "size=sm", + "reasons": [ + "layout overflow at 240px" + ] + }, + { + "fixtureName": "Ember / Checkbox / indeterminate (size=md)", + "caseKey": "size=md", + "reasons": [ + "layout overflow at 240px" + ] + }, + { + "fixtureName": "Ember / Checkbox / indeterminate (size=lg)", + "caseKey": "size=lg", + "reasons": [ + "layout overflow at 240px" + ] + } + ] + }, + { + "slug": "chip", + "name": "Chip", + "themeId": "ember", + "themeName": "Ember", + "caseCount": 10, + "fixtureCount": 3, + "pass": true, + "failures": [] + }, + { + "slug": "combobox", + "name": "Combobox", + "themeId": "ember", + "themeName": "Ember", + "caseCount": 3, + "fixtureCount": 9, + "pass": true, + "failures": [] + }, + { + "slug": "command-palette", + "name": "CommandPalette", + "themeId": "ember", + "themeName": "Ember", + "caseCount": 1, + "fixtureCount": 2, + "pass": true, + "failures": [] + }, + { + "slug": "data-chart", + "name": "DataChart", + "themeId": "ember", + "themeName": "Ember", + "caseCount": 3, + "fixtureCount": 3, + "pass": true, + "failures": [] + }, + { + "slug": "data-grid-lite", + "name": "DataGridLite", + "themeId": "ember", + "themeName": "Ember", + "caseCount": 2, + "fixtureCount": 3, + "pass": true, + "failures": [] + }, + { + "slug": "date-picker", + "name": "DatePicker", + "themeId": "ember", + "themeName": "Ember", + "caseCount": 3, + "fixtureCount": 3, + "pass": true, + "failures": [] + }, + { + "slug": "dialog", + "name": "Dialog", + "themeId": "ember", + "themeName": "Ember", + "caseCount": 3, + "fixtureCount": 3, + "pass": true, + "failures": [] + }, + { + "slug": "drawer", + "name": "Drawer", + "themeId": "ember", + "themeName": "Ember", + "caseCount": 6, + "fixtureCount": 6, + "pass": true, + "failures": [] + }, + { + "slug": "empty-state", + "name": "EmptyState", + "themeId": "ember", + "themeName": "Ember", + "caseCount": 15, + "fixtureCount": 3, + "pass": true, + "failures": [] + }, + { + "slug": "file-upload", + "name": "FileUpload", + "themeId": "ember", + "themeName": "Ember", + "caseCount": 3, + "fixtureCount": 6, + "pass": true, + "failures": [] + }, + { + "slug": "inline-edit", + "name": "InlineEdit", + "themeId": "ember", + "themeName": "Ember", + "caseCount": 3, + "fixtureCount": 1, + "pass": true, + "failures": [] + }, + { + "slug": "menu", + "name": "Menu", + "themeId": "ember", + "themeName": "Ember", + "caseCount": 3, + "fixtureCount": 6, + "pass": true, + "failures": [] + }, + { + "slug": "pagination", + "name": "Pagination", + "themeId": "ember", + "themeName": "Ember", + "caseCount": 2, + "fixtureCount": 1, + "pass": true, + "failures": [] + }, + { + "slug": "popover", + "name": "Popover", + "themeId": "ember", + "themeName": "Ember", + "caseCount": 3, + "fixtureCount": 3, + "pass": true, + "failures": [] + }, + { + "slug": "progress", + "name": "Progress", + "themeId": "ember", + "themeName": "Ember", + "caseCount": 10, + "fixtureCount": 3, + "pass": true, + "failures": [] + }, + { + "slug": "radio-group", + "name": "RadioGroup", + "themeId": "ember", + "themeName": "Ember", + "caseCount": 6, + "fixtureCount": 12, + "pass": true, + "failures": [] + }, + { + "slug": "range-date-picker", + "name": "RangeDatePicker", + "themeId": "ember", + "themeName": "Ember", + "caseCount": 3, + "fixtureCount": 3, + "pass": true, + "failures": [] + }, + { + "slug": "segmented-control", + "name": "SegmentedControl", + "themeId": "ember", + "themeName": "Ember", + "caseCount": 2, + "fixtureCount": 4, + "pass": true, + "failures": [] + }, + { + "slug": "select", + "name": "Select", + "themeId": "ember", + "themeName": "Ember", + "caseCount": 3, + "fixtureCount": 9, + "pass": true, + "failures": [] + }, + { + "slug": "side-nav", + "name": "SideNav", + "themeId": "ember", + "themeName": "Ember", + "caseCount": 1, + "fixtureCount": 2, + "pass": true, + "failures": [] + }, + { + "slug": "skeleton", + "name": "Skeleton", + "themeId": "ember", + "themeName": "Ember", + "caseCount": 9, + "fixtureCount": 3, + "pass": true, + "failures": [] + }, + { + "slug": "stepper", + "name": "Stepper", + "themeId": "ember", + "themeName": "Ember", + "caseCount": 4, + "fixtureCount": 3, + "pass": true, + "failures": [] + }, + { + "slug": "switch", + "name": "Switch", + "themeId": "ember", + "themeName": "Ember", + "caseCount": 3, + "fixtureCount": 6, + "pass": true, + "failures": [] + }, + { + "slug": "table", + "name": "Table", + "themeId": "ember", + "themeName": "Ember", + "caseCount": 2, + "fixtureCount": 3, + "pass": true, + "failures": [] + }, + { + "slug": "tabs", + "name": "Tabs", + "themeId": "ember", + "themeName": "Ember", + "caseCount": 6, + "fixtureCount": 12, + "pass": true, + "failures": [] + }, + { + "slug": "text-field", + "name": "TextField", + "themeId": "ember", + "themeName": "Ember", + "caseCount": 4, + "fixtureCount": 6, + "pass": false, + "failures": [ + { + "fixtureName": "Ember / TextField / sizes (size=lg)", + "caseKey": "size=lg", + "reasons": [ + "layout overflow at 240px" + ] + } + ] + }, + { + "slug": "textarea", + "name": "Textarea", + "themeId": "ember", + "themeName": "Ember", + "caseCount": 3, + "fixtureCount": 6, + "pass": true, + "failures": [] + }, + { + "slug": "toast", + "name": "Toast", + "themeId": "ember", + "themeName": "Ember", + "caseCount": 4, + "fixtureCount": 2, + "pass": true, + "failures": [] + }, + { + "slug": "tooltip", + "name": "Tooltip", + "themeId": "ember", + "themeName": "Ember", + "caseCount": 1, + "fixtureCount": 1, + "pass": true, + "failures": [] + }, + { + "slug": "tree-view", + "name": "TreeView", + "themeId": "ember", + "themeName": "Ember", + "caseCount": 1, + "fixtureCount": 2, + "pass": true, + "failures": [] + }, + { + "slug": "accordion", + "name": "Accordion", + "themeId": "linen", + "themeName": "Linen", + "caseCount": 2, + "fixtureCount": 2, + "pass": false, + "failures": [ + { + "fixtureName": "Linen / Accordion / accordion-md-open", + "caseKey": "size=md", + "reasons": [ + "layout overflow at 320px" + ] + } + ] + }, + { + "slug": "alert", + "name": "Alert", + "themeId": "linen", + "themeName": "Linen", + "caseCount": 5, + "fixtureCount": 5, + "pass": true, + "failures": [] + }, + { + "slug": "avatar", + "name": "Avatar", + "themeId": "linen", + "themeName": "Linen", + "caseCount": 6, + "fixtureCount": 3, + "pass": true, + "failures": [] + }, + { + "slug": "badge", + "name": "Badge", + "themeId": "linen", + "themeName": "Linen", + "caseCount": 30, + "fixtureCount": 15, + "pass": false, + "failures": [ + { + "fixtureName": "Linen / Badge / success-soft", + "caseKey": "emphasis=soft|size=md|tone=success", + "reasons": [ + "layout overflow at 120px" + ] + }, + { + "fixtureName": "Linen / Badge / success-solid", + "caseKey": "emphasis=solid|size=md|tone=success", + "reasons": [ + "layout overflow at 120px" + ] + }, + { + "fixtureName": "Linen / Badge / success-outline", + "caseKey": "emphasis=outline|size=md|tone=success", + "reasons": [ + "layout overflow at 120px" + ] + } + ] + }, + { + "slug": "breadcrumbs", + "name": "Breadcrumbs", + "themeId": "linen", + "themeName": "Linen", + "caseCount": 2, + "fixtureCount": 1, + "pass": false, + "failures": [ + { + "fixtureName": "Linen / Breadcrumbs / breadcrumbs-md", + "caseKey": "size=md", + "reasons": [ + "layout overflow at 220px" + ] + } + ] + }, + { + "slug": "button", + "name": "Button", + "themeId": "linen", + "themeName": "Linen", + "caseCount": 120, + "fixtureCount": 13, + "pass": false, + "failures": [ + { + "fixtureName": "Linen / Button / link-anchor", + "caseKey": "content=label|size=md|variant=link", + "reasons": [ + "layout overflow at 180px" + ] + } + ] + }, + { + "slug": "card", + "name": "Card", + "themeId": "linen", + "themeName": "Linen", + "caseCount": 9, + "fixtureCount": 3, + "pass": true, + "failures": [] + }, + { + "slug": "checkbox", + "name": "Checkbox", + "themeId": "linen", + "themeName": "Linen", + "caseCount": 3, + "fixtureCount": 9, + "pass": true, + "failures": [] + }, + { + "slug": "chip", + "name": "Chip", + "themeId": "linen", + "themeName": "Linen", + "caseCount": 10, + "fixtureCount": 3, + "pass": true, + "failures": [] + }, + { + "slug": "combobox", + "name": "Combobox", + "themeId": "linen", + "themeName": "Linen", + "caseCount": 3, + "fixtureCount": 9, + "pass": true, + "failures": [] + }, + { + "slug": "command-palette", + "name": "CommandPalette", + "themeId": "linen", + "themeName": "Linen", + "caseCount": 1, + "fixtureCount": 2, + "pass": true, + "failures": [] + }, + { + "slug": "data-chart", + "name": "DataChart", + "themeId": "linen", + "themeName": "Linen", + "caseCount": 3, + "fixtureCount": 3, + "pass": true, + "failures": [] + }, + { + "slug": "data-grid-lite", + "name": "DataGridLite", + "themeId": "linen", + "themeName": "Linen", + "caseCount": 2, + "fixtureCount": 3, + "pass": true, + "failures": [] + }, + { + "slug": "date-picker", + "name": "DatePicker", + "themeId": "linen", + "themeName": "Linen", + "caseCount": 3, + "fixtureCount": 3, + "pass": true, + "failures": [] + }, + { + "slug": "dialog", + "name": "Dialog", + "themeId": "linen", + "themeName": "Linen", + "caseCount": 3, + "fixtureCount": 3, + "pass": true, + "failures": [] + }, + { + "slug": "drawer", + "name": "Drawer", + "themeId": "linen", + "themeName": "Linen", + "caseCount": 6, + "fixtureCount": 6, + "pass": true, + "failures": [] + }, + { + "slug": "empty-state", + "name": "EmptyState", + "themeId": "linen", + "themeName": "Linen", + "caseCount": 15, + "fixtureCount": 3, + "pass": true, + "failures": [] + }, + { + "slug": "file-upload", + "name": "FileUpload", + "themeId": "linen", + "themeName": "Linen", + "caseCount": 3, + "fixtureCount": 6, + "pass": true, + "failures": [] + }, + { + "slug": "inline-edit", + "name": "InlineEdit", + "themeId": "linen", + "themeName": "Linen", + "caseCount": 3, + "fixtureCount": 1, + "pass": true, + "failures": [] + }, + { + "slug": "menu", + "name": "Menu", + "themeId": "linen", + "themeName": "Linen", + "caseCount": 3, + "fixtureCount": 6, + "pass": true, + "failures": [] + }, + { + "slug": "pagination", + "name": "Pagination", + "themeId": "linen", + "themeName": "Linen", + "caseCount": 2, + "fixtureCount": 1, + "pass": true, + "failures": [] + }, + { + "slug": "popover", + "name": "Popover", + "themeId": "linen", + "themeName": "Linen", + "caseCount": 3, + "fixtureCount": 3, + "pass": true, + "failures": [] + }, + { + "slug": "progress", + "name": "Progress", + "themeId": "linen", + "themeName": "Linen", + "caseCount": 10, + "fixtureCount": 3, + "pass": true, + "failures": [] + }, + { + "slug": "radio-group", + "name": "RadioGroup", + "themeId": "linen", + "themeName": "Linen", + "caseCount": 6, + "fixtureCount": 12, + "pass": true, + "failures": [] + }, + { + "slug": "range-date-picker", + "name": "RangeDatePicker", + "themeId": "linen", + "themeName": "Linen", + "caseCount": 3, + "fixtureCount": 3, + "pass": true, + "failures": [] + }, + { + "slug": "segmented-control", + "name": "SegmentedControl", + "themeId": "linen", + "themeName": "Linen", + "caseCount": 2, + "fixtureCount": 4, + "pass": true, + "failures": [] + }, + { + "slug": "select", + "name": "Select", + "themeId": "linen", + "themeName": "Linen", + "caseCount": 3, + "fixtureCount": 9, + "pass": true, + "failures": [] + }, + { + "slug": "side-nav", + "name": "SideNav", + "themeId": "linen", + "themeName": "Linen", + "caseCount": 1, + "fixtureCount": 2, + "pass": true, + "failures": [] + }, + { + "slug": "skeleton", + "name": "Skeleton", + "themeId": "linen", + "themeName": "Linen", + "caseCount": 9, + "fixtureCount": 3, + "pass": true, + "failures": [] + }, + { + "slug": "stepper", + "name": "Stepper", + "themeId": "linen", + "themeName": "Linen", + "caseCount": 4, + "fixtureCount": 3, + "pass": true, + "failures": [] + }, + { + "slug": "switch", + "name": "Switch", + "themeId": "linen", + "themeName": "Linen", + "caseCount": 3, + "fixtureCount": 6, + "pass": true, + "failures": [] + }, + { + "slug": "table", + "name": "Table", + "themeId": "linen", + "themeName": "Linen", + "caseCount": 2, + "fixtureCount": 3, + "pass": true, + "failures": [] + }, + { + "slug": "tabs", + "name": "Tabs", + "themeId": "linen", + "themeName": "Linen", + "caseCount": 6, + "fixtureCount": 12, + "pass": true, + "failures": [] + }, + { + "slug": "text-field", + "name": "TextField", + "themeId": "linen", + "themeName": "Linen", + "caseCount": 4, + "fixtureCount": 6, + "pass": true, + "failures": [] + }, + { + "slug": "textarea", + "name": "Textarea", + "themeId": "linen", + "themeName": "Linen", + "caseCount": 3, + "fixtureCount": 6, + "pass": true, + "failures": [] + }, + { + "slug": "toast", + "name": "Toast", + "themeId": "linen", + "themeName": "Linen", + "caseCount": 4, + "fixtureCount": 2, + "pass": true, + "failures": [] + }, + { + "slug": "tooltip", + "name": "Tooltip", + "themeId": "linen", + "themeName": "Linen", + "caseCount": 1, + "fixtureCount": 1, + "pass": true, + "failures": [] + }, + { + "slug": "tree-view", + "name": "TreeView", + "themeId": "linen", + "themeName": "Linen", + "caseCount": 1, + "fixtureCount": 2, + "pass": true, + "failures": [] + } + ] + } +} diff --git a/qualification/reviewed-math-policy.json b/qualification/reviewed-math-policy.json index 3f428dd..a83574e 100644 --- a/qualification/reviewed-math-policy.json +++ b/qualification/reviewed-math-policy.json @@ -5,9 +5,10 @@ "rendered": "not-collected" }, "assumptions": [ - "This review applies only to the original Browser test project content and recorded widths.", - "Reviewed findings remain failed conservative single-line/max-token estimates; they are not rendered passes.", - "No other failure category or unsupported proof is waived." + "Mathematical results use the declared single-line, wrapping, anywhere-wrapping, or managed-scroll behavior; the full single-line estimate remains evidence.", + "Wrapping components use minimum block sizes unless a real height or line cap is declared. Reserved inline chrome is included in the width budget.", + "These estimates do not measure font shaping, rendered geometry, keyboard behavior, or accessibility conformance.", + "The October 4 single-line policy is preserved in reviewed-math-policy-20261004.json as historical evidence. Current findings are not waived." ], "project": { "inputSha256": "f26c9f3b852c7e0a2a764146671a27869296f75f1bfcd13bd2a9049a29aa1fd6", @@ -20,20 +21,36 @@ 1280 ], "fixtureCount": 180, - "reviewedFailures": [ + "reviewedFailures": [], + "fixtureInventory": [ { "componentId": "accordion", - "id": "size=md__open", - "name": "accordion-md-open", - "caseKey": "size=md", + "id": "size=sm__rest", + "name": "accordion-sm", + "caseKey": "size=sm", "axes": { - "size": "md" + "size": "sm" }, "states": [ - "open" + "rest" ], "props": {}, - "sampleText": "Deployment timeline and stakeholder notes", + "sampleText": "Overview and rollout checklist", + "declared": [ + "contrast", + "target", + "layout" + ], + "proofCounts": { + "contrast": 2, + "distinctness": 0, + "target": 1, + "layout": 1, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + }, "layout": [ { "target": "panel", @@ -46,143 +63,211 @@ "heights": [ 160 ], - "estimatedInlinePx": 399.36, + "estimatedInlinePx": 284, + "minimumInlinePx": 40.4, + "textBehavior": "wrap-anywhere", "requiredBlockPx": 128, "widthChecks": [ { "width": 320, - "fitsWidth": false, + "fitsWidth": true, "fitsHeight": true, - "pass": false + "availableTextPx": 288, + "estimatedLineCount": 1, + "estimatedBlockPx": 128, + "fitsLines": true, + "pass": true }, { "width": 420, "fitsWidth": true, "fitsHeight": true, + "availableTextPx": 388, + "estimatedLineCount": 1, + "estimatedBlockPx": 128, + "fitsLines": true, "pass": true }, { "width": 768, "fitsWidth": true, "fitsHeight": true, + "availableTextPx": 736, + "estimatedLineCount": 1, + "estimatedBlockPx": 128, + "fitsLines": true, "pass": true }, { "width": 1280, "fitsWidth": true, "fitsHeight": true, + "availableTextPx": 1248, + "estimatedLineCount": 1, + "estimatedBlockPx": 128, + "fitsLines": true, "pass": true } ], - "pass": false + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "Text uses word boundaries and grapheme-safe breaks for estimated wrapping.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true } ] }, { - "componentId": "badge", - "id": "emphasis=soft|size=md|tone=success__rest", - "name": "success-soft", - "caseKey": "emphasis=soft|size=md|tone=success", + "componentId": "accordion", + "id": "size=md__open", + "name": "accordion-md-open", + "caseKey": "size=md", "axes": { - "tone": "success", - "emphasis": "soft", "size": "md" }, "states": [ - "rest" + "open" ], "props": {}, - "sampleText": "Healthy", + "sampleText": "Deployment timeline and stakeholder notes", + "declared": [ + "contrast", + "target", + "layout" + ], + "proofCounts": { + "contrast": 2, + "distinctness": 0, + "target": 1, + "layout": 1, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + }, "layout": [ { - "target": "root", + "target": "panel", "widths": [ - 120, - 200, 320, + 420, 768, 1280 ], "heights": [ - 30 + 160 ], - "estimatedInlinePx": 134.57, - "requiredBlockPx": 30, + "estimatedInlinePx": 376.4, + "minimumInlinePx": 40.4, + "textBehavior": "wrap-anywhere", + "requiredBlockPx": 128, "widthChecks": [ { - "width": 120, - "fitsWidth": false, - "fitsHeight": true, - "pass": false - }, - { - "width": 200, + "width": 320, "fitsWidth": true, "fitsHeight": true, + "availableTextPx": 288, + "estimatedLineCount": 2, + "estimatedBlockPx": 128, + "fitsLines": true, "pass": true }, { - "width": 320, + "width": 420, "fitsWidth": true, "fitsHeight": true, + "availableTextPx": 388, + "estimatedLineCount": 1, + "estimatedBlockPx": 128, + "fitsLines": true, "pass": true }, { "width": 768, "fitsWidth": true, "fitsHeight": true, + "availableTextPx": 736, + "estimatedLineCount": 1, + "estimatedBlockPx": 128, + "fitsLines": true, "pass": true }, { "width": 1280, "fitsWidth": true, "fitsHeight": true, + "availableTextPx": 1248, + "estimatedLineCount": 1, + "estimatedBlockPx": 128, + "fitsLines": true, "pass": true } ], - "pass": false + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "Text uses word boundaries and grapheme-safe breaks for estimated wrapping.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true } ] }, { - "componentId": "badge", - "id": "emphasis=solid|size=md|tone=success__rest", - "name": "success-solid", - "caseKey": "emphasis=solid|size=md|tone=success", + "componentId": "alert", + "id": "tone=neutral__rest", + "name": "neutral", + "caseKey": "tone=neutral", "axes": { - "tone": "success", - "emphasis": "solid", - "size": "md" + "tone": "neutral" }, "states": [ "rest" ], "props": {}, - "sampleText": "Healthy", + "declared": [ + "contrast", + "target", + "layout", + "helperText" + ], + "proofCounts": { + "contrast": 2, + "distinctness": 0, + "target": 1, + "layout": 1, + "helperText": 1, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + }, "layout": [ { "target": "root", "widths": [ - 120, - 200, + 280, + 360, 320, 768, 1280 ], "heights": [ - 30 + 120, + 160 ], - "estimatedInlinePx": 134.57, - "requiredBlockPx": 30, + "estimatedInlinePx": 0, + "minimumInlinePx": 0, + "textBehavior": "single-line", + "requiredBlockPx": 120, "widthChecks": [ { - "width": 120, - "fitsWidth": false, + "width": 280, + "fitsWidth": true, "fitsHeight": true, - "pass": false + "pass": true }, { - "width": 200, + "width": 360, "fitsWidth": true, "fitsHeight": true, "pass": true @@ -206,49 +291,69 @@ "pass": true } ], - "pass": false + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true } ] }, { - "componentId": "badge", - "id": "emphasis=outline|size=md|tone=success__rest", - "name": "success-outline", - "caseKey": "emphasis=outline|size=md|tone=success", + "componentId": "alert", + "id": "tone=brand__rest", + "name": "brand", + "caseKey": "tone=brand", "axes": { - "tone": "success", - "emphasis": "outline", - "size": "md" + "tone": "brand" }, "states": [ "rest" ], "props": {}, - "sampleText": "Healthy", + "declared": [ + "contrast", + "target", + "layout", + "helperText" + ], + "proofCounts": { + "contrast": 2, + "distinctness": 0, + "target": 1, + "layout": 1, + "helperText": 1, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + }, "layout": [ { "target": "root", "widths": [ - 120, - 200, + 280, + 360, 320, 768, 1280 ], "heights": [ - 30 + 120, + 160 ], - "estimatedInlinePx": 134.57, - "requiredBlockPx": 30, + "estimatedInlinePx": 0, + "minimumInlinePx": 0, + "textBehavior": "single-line", + "requiredBlockPx": 120, "widthChecks": [ { - "width": 120, - "fitsWidth": false, + "width": 280, + "fitsWidth": true, "fitsHeight": true, - "pass": false + "pass": true }, { - "width": 200, + "width": 360, "fitsWidth": true, "fitsHeight": true, "pass": true @@ -272,43 +377,72 @@ "pass": true } ], - "pass": false + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true } ] }, { - "componentId": "breadcrumbs", - "id": "size=md__rest", - "name": "breadcrumbs-md", - "caseKey": "size=md", + "componentId": "alert", + "id": "tone=success__rest", + "name": "success", + "caseKey": "tone=success", "axes": { - "size": "md" + "tone": "success" }, "states": [ "rest" ], "props": {}, - "sampleText": "Workspace / Release / Production", + "declared": [ + "contrast", + "target", + "layout", + "helperText" + ], + "proofCounts": { + "contrast": 2, + "distinctness": 0, + "target": 1, + "layout": 1, + "helperText": 1, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + }, "layout": [ { "target": "root", "widths": [ - 220, + 280, + 360, 320, 768, 1280 ], "heights": [ - 48 + 120, + 160 ], - "estimatedInlinePx": 266.88, - "requiredBlockPx": 44, + "estimatedInlinePx": 0, + "minimumInlinePx": 0, + "textBehavior": "single-line", + "requiredBlockPx": 120, "widthChecks": [ { - "width": 220, - "fitsWidth": false, + "width": 280, + "fitsWidth": true, "fitsHeight": true, - "pass": false + "pass": true + }, + { + "width": 360, + "fitsWidth": true, + "fitsHeight": true, + "pass": true }, { "width": 320, @@ -329,51 +463,69 @@ "pass": true } ], - "pass": false + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true } ] }, { - "componentId": "button", - "id": "content=label|size=lg|variant=solid__rest", - "name": "solid-sizes (content=label|size=lg|variant=solid)", - "caseKey": "content=label|size=lg|variant=solid", + "componentId": "alert", + "id": "tone=warning__rest", + "name": "warning", + "caseKey": "tone=warning", "axes": { - "variant": "solid", - "size": "lg", - "content": "label" + "tone": "warning" }, "states": [ "rest" ], "props": {}, - "sampleText": "Continue", + "declared": [ + "contrast", + "target", + "layout", + "helperText" + ], + "proofCounts": { + "contrast": 2, + "distinctness": 0, + "target": 1, + "layout": 1, + "helperText": 1, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + }, "layout": [ { "target": "root", "widths": [ - 180, - 240, + 280, + 360, 320, 768, 1280 ], "heights": [ - 44, - 48, - 52 + 120, + 160 ], - "estimatedInlinePx": 230.36, - "requiredBlockPx": 52, + "estimatedInlinePx": 0, + "minimumInlinePx": 0, + "textBehavior": "single-line", + "requiredBlockPx": 120, "widthChecks": [ { - "width": 180, - "fitsWidth": false, + "width": 280, + "fitsWidth": true, "fitsHeight": true, - "pass": false + "pass": true }, { - "width": 240, + "width": 360, "fitsWidth": true, "fitsHeight": true, "pass": true @@ -397,54 +549,69 @@ "pass": true } ], - "pass": false + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true } ] }, { - "componentId": "button", - "id": "content=label|size=md|variant=link__rest__as=a|href=#", - "name": "link-anchor", - "caseKey": "content=label|size=md|variant=link", + "componentId": "alert", + "id": "tone=danger__rest", + "name": "danger", + "caseKey": "tone=danger", "axes": { - "variant": "link", - "size": "md", - "content": "label" + "tone": "danger" }, "states": [ "rest" ], - "props": { - "as": "a", - "href": "#" + "props": {}, + "declared": [ + "contrast", + "target", + "layout", + "helperText" + ], + "proofCounts": { + "contrast": 2, + "distinctness": 0, + "target": 1, + "layout": 1, + "helperText": 1, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 }, - "sampleText": "Open details", "layout": [ { "target": "root", "widths": [ - 180, - 240, + 280, + 360, 320, 768, 1280 ], "heights": [ - 44, - 48, - 52 + 120, + 160 ], - "estimatedInlinePx": 232.56, - "requiredBlockPx": 48, + "estimatedInlinePx": 0, + "minimumInlinePx": 0, + "textBehavior": "single-line", + "requiredBlockPx": 120, "widthChecks": [ { - "width": 180, - "fitsWidth": false, + "width": 280, + "fitsWidth": true, "fitsHeight": true, - "pass": false + "pass": true }, { - "width": 240, + "width": 360, "fitsWidth": true, "fitsHeight": true, "pass": true @@ -468,1267 +635,1478 @@ "pass": true } ], - "pass": false + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true } ] }, { - "componentId": "checkbox", - "id": "size=sm__rest", - "name": "default (size=sm)", - "caseKey": "size=sm", + "componentId": "avatar", + "id": "shape=circle|size=sm__rest", + "name": "small-circle", + "caseKey": "shape=circle|size=sm", "axes": { - "size": "sm" + "size": "sm", + "shape": "circle" }, "states": [ "rest" ], "props": {}, - "sampleText": "Send release notes", + "declared": [ + "contrast", + "target" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 0, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + }, + "layout": [] + }, + { + "componentId": "avatar", + "id": "shape=circle|size=md__rest", + "name": "medium-circle", + "caseKey": "shape=circle|size=md", + "axes": { + "size": "md", + "shape": "circle" + }, + "states": [ + "rest" + ], + "props": {}, + "declared": [ + "contrast", + "target" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 0, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + }, + "layout": [] + }, + { + "componentId": "avatar", + "id": "shape=rounded|size=lg__rest", + "name": "large-rounded", + "caseKey": "shape=rounded|size=lg", + "axes": { + "size": "lg", + "shape": "rounded" + }, + "states": [ + "rest" + ], + "props": {}, + "declared": [ + "contrast", + "target" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 1, + "layout": 0, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + }, + "layout": [] + }, + { + "componentId": "badge", + "id": "emphasis=soft|size=md|tone=neutral__rest", + "name": "neutral-soft", + "caseKey": "emphasis=soft|size=md|tone=neutral", + "axes": { + "tone": "neutral", + "emphasis": "soft", + "size": "md" + }, + "states": [ + "rest" + ], + "props": {}, + "sampleText": "Draft", + "declared": [ + "contrast", + "layout" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 0, + "layout": 1, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + }, "layout": [ { "target": "root", "widths": [ - 240, + 120, + 200, 320, 768, 1280 ], - "heights": [ - 44 - ], - "estimatedInlinePx": 268.83, - "requiredBlockPx": 44, + "heights": [], + "estimatedInlinePx": 104.7, + "minimumInlinePx": 44.96, + "textBehavior": "wrap-anywhere", + "requiredBlockPx": 30, "widthChecks": [ { - "width": 240, - "fitsWidth": false, + "width": 120, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 89.98, + "estimatedLineCount": 1, + "estimatedBlockPx": 30, + "fitsLines": true, + "pass": true + }, + { + "width": 200, + "fitsWidth": true, "fitsHeight": true, - "pass": false + "availableTextPx": 169.98, + "estimatedLineCount": 1, + "estimatedBlockPx": 30, + "fitsLines": true, + "pass": true }, { "width": 320, "fitsWidth": true, "fitsHeight": true, + "availableTextPx": 289.98, + "estimatedLineCount": 1, + "estimatedBlockPx": 30, + "fitsLines": true, "pass": true }, { "width": 768, "fitsWidth": true, "fitsHeight": true, + "availableTextPx": 737.98, + "estimatedLineCount": 1, + "estimatedBlockPx": 30, + "fitsLines": true, "pass": true }, { "width": 1280, "fitsWidth": true, "fitsHeight": true, + "availableTextPx": 1249.98, + "estimatedLineCount": 1, + "estimatedBlockPx": 30, + "fitsLines": true, "pass": true } ], - "pass": false + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "Text uses word boundaries and grapheme-safe breaks for estimated wrapping.", + "Block size is a minimum; no maximum height is declared.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true } ] }, { - "componentId": "checkbox", - "id": "size=md__rest", - "name": "default (size=md)", - "caseKey": "size=md", + "componentId": "badge", + "id": "emphasis=solid|size=md|tone=neutral__rest", + "name": "neutral-solid", + "caseKey": "emphasis=solid|size=md|tone=neutral", "axes": { + "tone": "neutral", + "emphasis": "solid", "size": "md" }, "states": [ "rest" ], "props": {}, - "sampleText": "Send release notes", + "sampleText": "Draft", + "declared": [ + "contrast", + "layout" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 0, + "layout": 1, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + }, "layout": [ { "target": "root", "widths": [ - 240, + 120, + 200, 320, 768, 1280 ], - "heights": [ - 44 - ], - "estimatedInlinePx": 268.83, - "requiredBlockPx": 44, + "heights": [], + "estimatedInlinePx": 104.7, + "minimumInlinePx": 44.96, + "textBehavior": "wrap-anywhere", + "requiredBlockPx": 30, "widthChecks": [ { - "width": 240, - "fitsWidth": false, + "width": 120, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 89.98, + "estimatedLineCount": 1, + "estimatedBlockPx": 30, + "fitsLines": true, + "pass": true + }, + { + "width": 200, + "fitsWidth": true, "fitsHeight": true, - "pass": false + "availableTextPx": 169.98, + "estimatedLineCount": 1, + "estimatedBlockPx": 30, + "fitsLines": true, + "pass": true }, { "width": 320, "fitsWidth": true, "fitsHeight": true, + "availableTextPx": 289.98, + "estimatedLineCount": 1, + "estimatedBlockPx": 30, + "fitsLines": true, "pass": true }, { "width": 768, "fitsWidth": true, "fitsHeight": true, + "availableTextPx": 737.98, + "estimatedLineCount": 1, + "estimatedBlockPx": 30, + "fitsLines": true, "pass": true }, { "width": 1280, "fitsWidth": true, "fitsHeight": true, + "availableTextPx": 1249.98, + "estimatedLineCount": 1, + "estimatedBlockPx": 30, + "fitsLines": true, "pass": true } ], - "pass": false + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "Text uses word boundaries and grapheme-safe breaks for estimated wrapping.", + "Block size is a minimum; no maximum height is declared.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true } ] }, { - "componentId": "checkbox", - "id": "size=lg__rest", - "name": "default (size=lg)", - "caseKey": "size=lg", + "componentId": "badge", + "id": "emphasis=outline|size=md|tone=neutral__rest", + "name": "neutral-outline", + "caseKey": "emphasis=outline|size=md|tone=neutral", "axes": { - "size": "lg" + "tone": "neutral", + "emphasis": "outline", + "size": "md" }, "states": [ "rest" ], "props": {}, - "sampleText": "Send release notes", + "sampleText": "Draft", + "declared": [ + "contrast", + "layout" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 0, + "layout": 1, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + }, "layout": [ { "target": "root", "widths": [ - 240, + 120, + 200, 320, 768, 1280 ], - "heights": [ - 44 - ], - "estimatedInlinePx": 268.83, - "requiredBlockPx": 44, + "heights": [], + "estimatedInlinePx": 104.7, + "minimumInlinePx": 44.96, + "textBehavior": "wrap-anywhere", + "requiredBlockPx": 30, "widthChecks": [ { - "width": 240, - "fitsWidth": false, + "width": 120, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 89.98, + "estimatedLineCount": 1, + "estimatedBlockPx": 30, + "fitsLines": true, + "pass": true + }, + { + "width": 200, + "fitsWidth": true, "fitsHeight": true, - "pass": false + "availableTextPx": 169.98, + "estimatedLineCount": 1, + "estimatedBlockPx": 30, + "fitsLines": true, + "pass": true }, { "width": 320, "fitsWidth": true, "fitsHeight": true, + "availableTextPx": 289.98, + "estimatedLineCount": 1, + "estimatedBlockPx": 30, + "fitsLines": true, "pass": true }, { "width": 768, "fitsWidth": true, "fitsHeight": true, + "availableTextPx": 737.98, + "estimatedLineCount": 1, + "estimatedBlockPx": 30, + "fitsLines": true, "pass": true }, { "width": 1280, "fitsWidth": true, "fitsHeight": true, + "availableTextPx": 1249.98, + "estimatedLineCount": 1, + "estimatedBlockPx": 30, + "fitsLines": true, "pass": true } ], - "pass": false + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "Text uses word boundaries and grapheme-safe breaks for estimated wrapping.", + "Block size is a minimum; no maximum height is declared.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true } ] }, { - "componentId": "checkbox", - "id": "size=sm__checked", - "name": "checked (size=sm)", - "caseKey": "size=sm", + "componentId": "badge", + "id": "emphasis=soft|size=md|tone=brand__rest", + "name": "brand-soft", + "caseKey": "emphasis=soft|size=md|tone=brand", "axes": { - "size": "sm" + "tone": "brand", + "emphasis": "soft", + "size": "md" }, "states": [ - "checked" + "rest" ], "props": {}, - "sampleText": "Send release notes", + "sampleText": "Launch", + "declared": [ + "contrast", + "layout" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 0, + "layout": 1, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + }, "layout": [ { "target": "root", "widths": [ - 240, + 120, + 200, 320, 768, 1280 ], - "heights": [ - 44 - ], - "estimatedInlinePx": 268.83, - "requiredBlockPx": 44, + "heights": [], + "estimatedInlinePx": 119.63, + "minimumInlinePx": 44.96, + "textBehavior": "wrap-anywhere", + "requiredBlockPx": 30, "widthChecks": [ { - "width": 240, - "fitsWidth": false, + "width": 120, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 89.98, + "estimatedLineCount": 1, + "estimatedBlockPx": 30, + "fitsLines": true, + "pass": true + }, + { + "width": 200, + "fitsWidth": true, "fitsHeight": true, - "pass": false + "availableTextPx": 169.98, + "estimatedLineCount": 1, + "estimatedBlockPx": 30, + "fitsLines": true, + "pass": true }, { "width": 320, "fitsWidth": true, "fitsHeight": true, + "availableTextPx": 289.98, + "estimatedLineCount": 1, + "estimatedBlockPx": 30, + "fitsLines": true, "pass": true }, { "width": 768, "fitsWidth": true, "fitsHeight": true, + "availableTextPx": 737.98, + "estimatedLineCount": 1, + "estimatedBlockPx": 30, + "fitsLines": true, "pass": true }, { "width": 1280, "fitsWidth": true, "fitsHeight": true, + "availableTextPx": 1249.98, + "estimatedLineCount": 1, + "estimatedBlockPx": 30, + "fitsLines": true, "pass": true } ], - "pass": false + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "Text uses word boundaries and grapheme-safe breaks for estimated wrapping.", + "Block size is a minimum; no maximum height is declared.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true } ] }, { - "componentId": "checkbox", - "id": "size=md__checked", - "name": "checked (size=md)", - "caseKey": "size=md", + "componentId": "badge", + "id": "emphasis=solid|size=md|tone=brand__rest", + "name": "brand-solid", + "caseKey": "emphasis=solid|size=md|tone=brand", "axes": { + "tone": "brand", + "emphasis": "solid", "size": "md" }, "states": [ - "checked" + "rest" ], "props": {}, - "sampleText": "Send release notes", + "sampleText": "Launch", + "declared": [ + "contrast", + "layout" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 0, + "layout": 1, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + }, "layout": [ { "target": "root", "widths": [ - 240, + 120, + 200, 320, 768, 1280 ], - "heights": [ - 44 - ], - "estimatedInlinePx": 268.83, - "requiredBlockPx": 44, + "heights": [], + "estimatedInlinePx": 119.63, + "minimumInlinePx": 44.96, + "textBehavior": "wrap-anywhere", + "requiredBlockPx": 30, "widthChecks": [ { - "width": 240, - "fitsWidth": false, + "width": 120, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 89.98, + "estimatedLineCount": 1, + "estimatedBlockPx": 30, + "fitsLines": true, + "pass": true + }, + { + "width": 200, + "fitsWidth": true, "fitsHeight": true, - "pass": false + "availableTextPx": 169.98, + "estimatedLineCount": 1, + "estimatedBlockPx": 30, + "fitsLines": true, + "pass": true }, { "width": 320, "fitsWidth": true, "fitsHeight": true, + "availableTextPx": 289.98, + "estimatedLineCount": 1, + "estimatedBlockPx": 30, + "fitsLines": true, "pass": true }, { "width": 768, "fitsWidth": true, "fitsHeight": true, + "availableTextPx": 737.98, + "estimatedLineCount": 1, + "estimatedBlockPx": 30, + "fitsLines": true, "pass": true }, { "width": 1280, "fitsWidth": true, "fitsHeight": true, + "availableTextPx": 1249.98, + "estimatedLineCount": 1, + "estimatedBlockPx": 30, + "fitsLines": true, "pass": true } ], - "pass": false + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "Text uses word boundaries and grapheme-safe breaks for estimated wrapping.", + "Block size is a minimum; no maximum height is declared.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true } ] }, { - "componentId": "checkbox", - "id": "size=lg__checked", - "name": "checked (size=lg)", - "caseKey": "size=lg", + "componentId": "badge", + "id": "emphasis=outline|size=md|tone=brand__rest", + "name": "brand-outline", + "caseKey": "emphasis=outline|size=md|tone=brand", "axes": { - "size": "lg" + "tone": "brand", + "emphasis": "outline", + "size": "md" }, "states": [ - "checked" + "rest" ], "props": {}, - "sampleText": "Send release notes", + "sampleText": "Launch", + "declared": [ + "contrast", + "layout" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 0, + "layout": 1, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + }, "layout": [ { "target": "root", "widths": [ - 240, + 120, + 200, 320, 768, 1280 ], - "heights": [ - 44 - ], - "estimatedInlinePx": 268.83, - "requiredBlockPx": 44, + "heights": [], + "estimatedInlinePx": 119.63, + "minimumInlinePx": 44.96, + "textBehavior": "wrap-anywhere", + "requiredBlockPx": 30, "widthChecks": [ { - "width": 240, - "fitsWidth": false, + "width": 120, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 89.98, + "estimatedLineCount": 1, + "estimatedBlockPx": 30, + "fitsLines": true, + "pass": true + }, + { + "width": 200, + "fitsWidth": true, "fitsHeight": true, - "pass": false + "availableTextPx": 169.98, + "estimatedLineCount": 1, + "estimatedBlockPx": 30, + "fitsLines": true, + "pass": true }, { "width": 320, "fitsWidth": true, "fitsHeight": true, + "availableTextPx": 289.98, + "estimatedLineCount": 1, + "estimatedBlockPx": 30, + "fitsLines": true, "pass": true }, { "width": 768, "fitsWidth": true, "fitsHeight": true, + "availableTextPx": 737.98, + "estimatedLineCount": 1, + "estimatedBlockPx": 30, + "fitsLines": true, "pass": true }, { "width": 1280, "fitsWidth": true, "fitsHeight": true, + "availableTextPx": 1249.98, + "estimatedLineCount": 1, + "estimatedBlockPx": 30, + "fitsLines": true, "pass": true } ], - "pass": false + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "Text uses word boundaries and grapheme-safe breaks for estimated wrapping.", + "Block size is a minimum; no maximum height is declared.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true } ] }, { - "componentId": "checkbox", - "id": "size=sm__indeterminate", - "name": "indeterminate (size=sm)", - "caseKey": "size=sm", + "componentId": "badge", + "id": "emphasis=soft|size=md|tone=success__rest", + "name": "success-soft", + "caseKey": "emphasis=soft|size=md|tone=success", "axes": { - "size": "sm" + "tone": "success", + "emphasis": "soft", + "size": "md" }, "states": [ - "indeterminate" + "rest" ], "props": {}, - "sampleText": "Send release notes", + "sampleText": "Healthy", + "declared": [ + "contrast", + "layout" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 0, + "layout": 1, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + }, "layout": [ { "target": "root", "widths": [ - 240, + 120, + 200, 320, 768, 1280 ], - "heights": [ - 44 - ], - "estimatedInlinePx": 268.83, - "requiredBlockPx": 44, + "heights": [], + "estimatedInlinePx": 134.57, + "minimumInlinePx": 44.96, + "textBehavior": "wrap-anywhere", + "requiredBlockPx": 30, "widthChecks": [ { - "width": 240, - "fitsWidth": false, + "width": 120, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 89.98, + "estimatedLineCount": 2, + "estimatedBlockPx": 53.34, + "fitsLines": true, + "pass": true + }, + { + "width": 200, + "fitsWidth": true, "fitsHeight": true, - "pass": false + "availableTextPx": 169.98, + "estimatedLineCount": 1, + "estimatedBlockPx": 30, + "fitsLines": true, + "pass": true }, { "width": 320, "fitsWidth": true, "fitsHeight": true, + "availableTextPx": 289.98, + "estimatedLineCount": 1, + "estimatedBlockPx": 30, + "fitsLines": true, "pass": true }, { "width": 768, "fitsWidth": true, "fitsHeight": true, + "availableTextPx": 737.98, + "estimatedLineCount": 1, + "estimatedBlockPx": 30, + "fitsLines": true, "pass": true }, { "width": 1280, "fitsWidth": true, "fitsHeight": true, + "availableTextPx": 1249.98, + "estimatedLineCount": 1, + "estimatedBlockPx": 30, + "fitsLines": true, "pass": true } ], - "pass": false + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "Text uses word boundaries and grapheme-safe breaks for estimated wrapping.", + "Block size is a minimum; no maximum height is declared.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true } ] }, { - "componentId": "checkbox", - "id": "size=md__indeterminate", - "name": "indeterminate (size=md)", - "caseKey": "size=md", + "componentId": "badge", + "id": "emphasis=solid|size=md|tone=success__rest", + "name": "success-solid", + "caseKey": "emphasis=solid|size=md|tone=success", "axes": { + "tone": "success", + "emphasis": "solid", "size": "md" }, "states": [ - "indeterminate" + "rest" ], "props": {}, - "sampleText": "Send release notes", + "sampleText": "Healthy", + "declared": [ + "contrast", + "layout" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 0, + "layout": 1, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + }, "layout": [ { "target": "root", "widths": [ - 240, + 120, + 200, 320, 768, 1280 ], - "heights": [ - 44 - ], - "estimatedInlinePx": 268.83, - "requiredBlockPx": 44, + "heights": [], + "estimatedInlinePx": 134.57, + "minimumInlinePx": 44.96, + "textBehavior": "wrap-anywhere", + "requiredBlockPx": 30, "widthChecks": [ { - "width": 240, - "fitsWidth": false, + "width": 120, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 89.98, + "estimatedLineCount": 2, + "estimatedBlockPx": 53.34, + "fitsLines": true, + "pass": true + }, + { + "width": 200, + "fitsWidth": true, "fitsHeight": true, - "pass": false + "availableTextPx": 169.98, + "estimatedLineCount": 1, + "estimatedBlockPx": 30, + "fitsLines": true, + "pass": true }, { "width": 320, "fitsWidth": true, "fitsHeight": true, + "availableTextPx": 289.98, + "estimatedLineCount": 1, + "estimatedBlockPx": 30, + "fitsLines": true, "pass": true }, { "width": 768, "fitsWidth": true, "fitsHeight": true, + "availableTextPx": 737.98, + "estimatedLineCount": 1, + "estimatedBlockPx": 30, + "fitsLines": true, "pass": true }, { "width": 1280, "fitsWidth": true, "fitsHeight": true, + "availableTextPx": 1249.98, + "estimatedLineCount": 1, + "estimatedBlockPx": 30, + "fitsLines": true, "pass": true } ], - "pass": false + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "Text uses word boundaries and grapheme-safe breaks for estimated wrapping.", + "Block size is a minimum; no maximum height is declared.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true } ] }, { - "componentId": "checkbox", - "id": "size=lg__indeterminate", - "name": "indeterminate (size=lg)", - "caseKey": "size=lg", + "componentId": "badge", + "id": "emphasis=outline|size=md|tone=success__rest", + "name": "success-outline", + "caseKey": "emphasis=outline|size=md|tone=success", "axes": { - "size": "lg" + "tone": "success", + "emphasis": "outline", + "size": "md" }, "states": [ - "indeterminate" + "rest" ], "props": {}, - "sampleText": "Send release notes", - "layout": [ - { - "target": "root", - "widths": [ - 240, - 320, - 768, + "sampleText": "Healthy", + "declared": [ + "contrast", + "layout" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 0, + "layout": 1, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + }, + "layout": [ + { + "target": "root", + "widths": [ + 120, + 200, + 320, + 768, 1280 ], - "heights": [ - 44 - ], - "estimatedInlinePx": 268.83, - "requiredBlockPx": 44, + "heights": [], + "estimatedInlinePx": 134.57, + "minimumInlinePx": 44.96, + "textBehavior": "wrap-anywhere", + "requiredBlockPx": 30, "widthChecks": [ { - "width": 240, - "fitsWidth": false, + "width": 120, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 89.98, + "estimatedLineCount": 2, + "estimatedBlockPx": 53.34, + "fitsLines": true, + "pass": true + }, + { + "width": 200, + "fitsWidth": true, "fitsHeight": true, - "pass": false + "availableTextPx": 169.98, + "estimatedLineCount": 1, + "estimatedBlockPx": 30, + "fitsLines": true, + "pass": true }, { "width": 320, "fitsWidth": true, "fitsHeight": true, + "availableTextPx": 289.98, + "estimatedLineCount": 1, + "estimatedBlockPx": 30, + "fitsLines": true, "pass": true }, { "width": 768, "fitsWidth": true, "fitsHeight": true, + "availableTextPx": 737.98, + "estimatedLineCount": 1, + "estimatedBlockPx": 30, + "fitsLines": true, "pass": true }, { "width": 1280, "fitsWidth": true, "fitsHeight": true, + "availableTextPx": 1249.98, + "estimatedLineCount": 1, + "estimatedBlockPx": 30, + "fitsLines": true, "pass": true } ], - "pass": false + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "Text uses word boundaries and grapheme-safe breaks for estimated wrapping.", + "Block size is a minimum; no maximum height is declared.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true } ] }, { - "componentId": "text-field", - "id": "size=lg__rest", - "name": "sizes (size=lg)", - "caseKey": "size=lg", + "componentId": "badge", + "id": "emphasis=soft|size=md|tone=warning__rest", + "name": "warning-soft", + "caseKey": "emphasis=soft|size=md|tone=warning", "axes": { - "size": "lg" + "tone": "warning", + "emphasis": "soft", + "size": "md" }, "states": [ "rest" ], "props": {}, - "sampleText": "Project name", + "sampleText": "Review", + "declared": [ + "contrast", + "layout" + ], + "proofCounts": { + "contrast": 1, + "distinctness": 0, + "target": 0, + "layout": 1, + "helperText": 0, + "optionRow": 0, + "anchoredSurface": 0, + "motion": 0 + }, "layout": [ { - "target": "field", + "target": "root", "widths": [ - 240, + 120, + 200, 320, - 420, 768, 1280 ], - "heights": [ - 44, - 48, - 52 - ], - "estimatedInlinePx": 310, - "requiredBlockPx": 52, + "heights": [], + "estimatedInlinePx": 119.63, + "minimumInlinePx": 44.96, + "textBehavior": "wrap-anywhere", + "requiredBlockPx": 30, "widthChecks": [ { - "width": 240, - "fitsWidth": false, + "width": 120, + "fitsWidth": true, "fitsHeight": true, - "pass": false + "availableTextPx": 89.98, + "estimatedLineCount": 1, + "estimatedBlockPx": 30, + "fitsLines": true, + "pass": true }, { - "width": 320, + "width": 200, "fitsWidth": true, "fitsHeight": true, + "availableTextPx": 169.98, + "estimatedLineCount": 1, + "estimatedBlockPx": 30, + "fitsLines": true, "pass": true }, { - "width": 420, + "width": 320, "fitsWidth": true, "fitsHeight": true, + "availableTextPx": 289.98, + "estimatedLineCount": 1, + "estimatedBlockPx": 30, + "fitsLines": true, "pass": true }, { "width": 768, "fitsWidth": true, "fitsHeight": true, + "availableTextPx": 737.98, + "estimatedLineCount": 1, + "estimatedBlockPx": 30, + "fitsLines": true, "pass": true }, { "width": 1280, "fitsWidth": true, "fitsHeight": true, + "availableTextPx": 1249.98, + "estimatedLineCount": 1, + "estimatedBlockPx": 30, + "fitsLines": true, "pass": true } ], - "pass": false + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "Text uses word boundaries and grapheme-safe breaks for estimated wrapping.", + "Block size is a minimum; no maximum height is declared.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true } ] - } - ], - "fixtureInventory": [ + }, { - "componentId": "accordion", - "id": "size=sm__rest", - "name": "accordion-sm", - "caseKey": "size=sm", + "componentId": "badge", + "id": "emphasis=solid|size=md|tone=warning__rest", + "name": "warning-solid", + "caseKey": "emphasis=solid|size=md|tone=warning", "axes": { - "size": "sm" + "tone": "warning", + "emphasis": "solid", + "size": "md" }, "states": [ "rest" ], "props": {}, - "sampleText": "Overview and rollout checklist", + "sampleText": "Review", "declared": [ "contrast", - "target", "layout" ], "proofCounts": { - "contrast": 2, + "contrast": 1, "distinctness": 0, - "target": 1, + "target": 0, "layout": 1, "helperText": 0, "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [ + { + "target": "root", + "widths": [ + 120, + 200, + 320, + 768, + 1280 + ], + "heights": [], + "estimatedInlinePx": 119.63, + "minimumInlinePx": 44.96, + "textBehavior": "wrap-anywhere", + "requiredBlockPx": 30, + "widthChecks": [ + { + "width": 120, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 89.98, + "estimatedLineCount": 1, + "estimatedBlockPx": 30, + "fitsLines": true, + "pass": true + }, + { + "width": 200, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 169.98, + "estimatedLineCount": 1, + "estimatedBlockPx": 30, + "fitsLines": true, + "pass": true + }, + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 289.98, + "estimatedLineCount": 1, + "estimatedBlockPx": 30, + "fitsLines": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 737.98, + "estimatedLineCount": 1, + "estimatedBlockPx": 30, + "fitsLines": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 1249.98, + "estimatedLineCount": 1, + "estimatedBlockPx": 30, + "fitsLines": true, + "pass": true + } + ], + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "Text uses word boundaries and grapheme-safe breaks for estimated wrapping.", + "Block size is a minimum; no maximum height is declared.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true + } + ] }, { - "componentId": "accordion", - "id": "size=md__open", - "name": "accordion-md-open", - "caseKey": "size=md", + "componentId": "badge", + "id": "emphasis=outline|size=md|tone=warning__rest", + "name": "warning-outline", + "caseKey": "emphasis=outline|size=md|tone=warning", "axes": { + "tone": "warning", + "emphasis": "outline", "size": "md" }, "states": [ - "open" + "rest" ], "props": {}, - "sampleText": "Deployment timeline and stakeholder notes", + "sampleText": "Review", "declared": [ "contrast", - "target", "layout" ], "proofCounts": { - "contrast": 2, + "contrast": 1, "distinctness": 0, - "target": 1, + "target": 0, "layout": 1, "helperText": 0, "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [ + { + "target": "root", + "widths": [ + 120, + 200, + 320, + 768, + 1280 + ], + "heights": [], + "estimatedInlinePx": 119.63, + "minimumInlinePx": 44.96, + "textBehavior": "wrap-anywhere", + "requiredBlockPx": 30, + "widthChecks": [ + { + "width": 120, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 89.98, + "estimatedLineCount": 1, + "estimatedBlockPx": 30, + "fitsLines": true, + "pass": true + }, + { + "width": 200, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 169.98, + "estimatedLineCount": 1, + "estimatedBlockPx": 30, + "fitsLines": true, + "pass": true + }, + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 289.98, + "estimatedLineCount": 1, + "estimatedBlockPx": 30, + "fitsLines": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 737.98, + "estimatedLineCount": 1, + "estimatedBlockPx": 30, + "fitsLines": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 1249.98, + "estimatedLineCount": 1, + "estimatedBlockPx": 30, + "fitsLines": true, + "pass": true + } + ], + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "Text uses word boundaries and grapheme-safe breaks for estimated wrapping.", + "Block size is a minimum; no maximum height is declared.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true + } + ] }, { - "componentId": "alert", - "id": "tone=neutral__rest", - "name": "neutral", - "caseKey": "tone=neutral", + "componentId": "badge", + "id": "emphasis=soft|size=md|tone=danger__rest", + "name": "danger-soft", + "caseKey": "emphasis=soft|size=md|tone=danger", "axes": { - "tone": "neutral" + "tone": "danger", + "emphasis": "soft", + "size": "md" }, "states": [ "rest" ], "props": {}, + "sampleText": "Failed", "declared": [ "contrast", - "target", - "layout", - "helperText" + "layout" ], "proofCounts": { - "contrast": 2, + "contrast": 1, "distinctness": 0, - "target": 1, + "target": 0, "layout": 1, - "helperText": 1, + "helperText": 0, "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } - }, - { - "componentId": "alert", - "id": "tone=brand__rest", - "name": "brand", - "caseKey": "tone=brand", - "axes": { - "tone": "brand" - }, - "states": [ - "rest" - ], - "props": {}, - "declared": [ - "contrast", - "target", - "layout", - "helperText" - ], - "proofCounts": { - "contrast": 2, - "distinctness": 0, - "target": 1, - "layout": 1, - "helperText": 1, - "optionRow": 0, - "anchoredSurface": 0, - "motion": 0 - } - }, - { - "componentId": "alert", - "id": "tone=success__rest", - "name": "success", - "caseKey": "tone=success", - "axes": { - "tone": "success" - }, - "states": [ - "rest" - ], - "props": {}, - "declared": [ - "contrast", - "target", - "layout", - "helperText" - ], - "proofCounts": { - "contrast": 2, - "distinctness": 0, - "target": 1, - "layout": 1, - "helperText": 1, - "optionRow": 0, - "anchoredSurface": 0, - "motion": 0 - } - }, - { - "componentId": "alert", - "id": "tone=warning__rest", - "name": "warning", - "caseKey": "tone=warning", - "axes": { - "tone": "warning" - }, - "states": [ - "rest" - ], - "props": {}, - "declared": [ - "contrast", - "target", - "layout", - "helperText" - ], - "proofCounts": { - "contrast": 2, - "distinctness": 0, - "target": 1, - "layout": 1, - "helperText": 1, - "optionRow": 0, - "anchoredSurface": 0, - "motion": 0 - } - }, - { - "componentId": "alert", - "id": "tone=danger__rest", - "name": "danger", - "caseKey": "tone=danger", - "axes": { - "tone": "danger" - }, - "states": [ - "rest" - ], - "props": {}, - "declared": [ - "contrast", - "target", - "layout", - "helperText" - ], - "proofCounts": { - "contrast": 2, - "distinctness": 0, - "target": 1, - "layout": 1, - "helperText": 1, - "optionRow": 0, - "anchoredSurface": 0, - "motion": 0 - } - }, - { - "componentId": "avatar", - "id": "shape=circle|size=sm__rest", - "name": "small-circle", - "caseKey": "shape=circle|size=sm", - "axes": { - "size": "sm", - "shape": "circle" - }, - "states": [ - "rest" - ], - "props": {}, - "declared": [ - "contrast", - "target" - ], - "proofCounts": { - "contrast": 1, - "distinctness": 0, - "target": 1, - "layout": 0, - "helperText": 0, - "optionRow": 0, - "anchoredSurface": 0, - "motion": 0 - } - }, - { - "componentId": "avatar", - "id": "shape=circle|size=md__rest", - "name": "medium-circle", - "caseKey": "shape=circle|size=md", - "axes": { - "size": "md", - "shape": "circle" - }, - "states": [ - "rest" - ], - "props": {}, - "declared": [ - "contrast", - "target" - ], - "proofCounts": { - "contrast": 1, - "distinctness": 0, - "target": 1, - "layout": 0, - "helperText": 0, - "optionRow": 0, - "anchoredSurface": 0, - "motion": 0 - } - }, - { - "componentId": "avatar", - "id": "shape=rounded|size=lg__rest", - "name": "large-rounded", - "caseKey": "shape=rounded|size=lg", - "axes": { - "size": "lg", - "shape": "rounded" - }, - "states": [ - "rest" - ], - "props": {}, - "declared": [ - "contrast", - "target" - ], - "proofCounts": { - "contrast": 1, - "distinctness": 0, - "target": 1, - "layout": 0, - "helperText": 0, - "optionRow": 0, - "anchoredSurface": 0, - "motion": 0 - } - }, - { - "componentId": "badge", - "id": "emphasis=soft|size=md|tone=neutral__rest", - "name": "neutral-soft", - "caseKey": "emphasis=soft|size=md|tone=neutral", - "axes": { - "tone": "neutral", - "emphasis": "soft", - "size": "md" - }, - "states": [ - "rest" - ], - "props": {}, - "sampleText": "Draft", - "declared": [ - "contrast", - "layout" - ], - "proofCounts": { - "contrast": 1, - "distinctness": 0, - "target": 0, - "layout": 1, - "helperText": 0, - "optionRow": 0, - "anchoredSurface": 0, - "motion": 0 - } - }, - { - "componentId": "badge", - "id": "emphasis=solid|size=md|tone=neutral__rest", - "name": "neutral-solid", - "caseKey": "emphasis=solid|size=md|tone=neutral", - "axes": { - "tone": "neutral", - "emphasis": "solid", - "size": "md" - }, - "states": [ - "rest" - ], - "props": {}, - "sampleText": "Draft", - "declared": [ - "contrast", - "layout" - ], - "proofCounts": { - "contrast": 1, - "distinctness": 0, - "target": 0, - "layout": 1, - "helperText": 0, - "optionRow": 0, - "anchoredSurface": 0, - "motion": 0 - } - }, - { - "componentId": "badge", - "id": "emphasis=outline|size=md|tone=neutral__rest", - "name": "neutral-outline", - "caseKey": "emphasis=outline|size=md|tone=neutral", - "axes": { - "tone": "neutral", - "emphasis": "outline", - "size": "md" - }, - "states": [ - "rest" - ], - "props": {}, - "sampleText": "Draft", - "declared": [ - "contrast", - "layout" - ], - "proofCounts": { - "contrast": 1, - "distinctness": 0, - "target": 0, - "layout": 1, - "helperText": 0, - "optionRow": 0, - "anchoredSurface": 0, - "motion": 0 - } - }, - { - "componentId": "badge", - "id": "emphasis=soft|size=md|tone=brand__rest", - "name": "brand-soft", - "caseKey": "emphasis=soft|size=md|tone=brand", - "axes": { - "tone": "brand", - "emphasis": "soft", - "size": "md" - }, - "states": [ - "rest" - ], - "props": {}, - "sampleText": "Launch", - "declared": [ - "contrast", - "layout" - ], - "proofCounts": { - "contrast": 1, - "distinctness": 0, - "target": 0, - "layout": 1, - "helperText": 0, - "optionRow": 0, - "anchoredSurface": 0, - "motion": 0 - } - }, - { - "componentId": "badge", - "id": "emphasis=solid|size=md|tone=brand__rest", - "name": "brand-solid", - "caseKey": "emphasis=solid|size=md|tone=brand", - "axes": { - "tone": "brand", - "emphasis": "solid", - "size": "md" - }, - "states": [ - "rest" - ], - "props": {}, - "sampleText": "Launch", - "declared": [ - "contrast", - "layout" - ], - "proofCounts": { - "contrast": 1, - "distinctness": 0, - "target": 0, - "layout": 1, - "helperText": 0, - "optionRow": 0, - "anchoredSurface": 0, - "motion": 0 - } - }, - { - "componentId": "badge", - "id": "emphasis=outline|size=md|tone=brand__rest", - "name": "brand-outline", - "caseKey": "emphasis=outline|size=md|tone=brand", - "axes": { - "tone": "brand", - "emphasis": "outline", - "size": "md" - }, - "states": [ - "rest" - ], - "props": {}, - "sampleText": "Launch", - "declared": [ - "contrast", - "layout" - ], - "proofCounts": { - "contrast": 1, - "distinctness": 0, - "target": 0, - "layout": 1, - "helperText": 0, - "optionRow": 0, - "anchoredSurface": 0, - "motion": 0 - } - }, - { - "componentId": "badge", - "id": "emphasis=soft|size=md|tone=success__rest", - "name": "success-soft", - "caseKey": "emphasis=soft|size=md|tone=success", - "axes": { - "tone": "success", - "emphasis": "soft", - "size": "md" - }, - "states": [ - "rest" - ], - "props": {}, - "sampleText": "Healthy", - "declared": [ - "contrast", - "layout" - ], - "proofCounts": { - "contrast": 1, - "distinctness": 0, - "target": 0, - "layout": 1, - "helperText": 0, - "optionRow": 0, - "anchoredSurface": 0, - "motion": 0 - } - }, - { - "componentId": "badge", - "id": "emphasis=solid|size=md|tone=success__rest", - "name": "success-solid", - "caseKey": "emphasis=solid|size=md|tone=success", - "axes": { - "tone": "success", - "emphasis": "solid", - "size": "md" - }, - "states": [ - "rest" - ], - "props": {}, - "sampleText": "Healthy", - "declared": [ - "contrast", - "layout" - ], - "proofCounts": { - "contrast": 1, - "distinctness": 0, - "target": 0, - "layout": 1, - "helperText": 0, - "optionRow": 0, - "anchoredSurface": 0, - "motion": 0 - } - }, - { - "componentId": "badge", - "id": "emphasis=outline|size=md|tone=success__rest", - "name": "success-outline", - "caseKey": "emphasis=outline|size=md|tone=success", - "axes": { - "tone": "success", - "emphasis": "outline", - "size": "md" - }, - "states": [ - "rest" - ], - "props": {}, - "sampleText": "Healthy", - "declared": [ - "contrast", - "layout" - ], - "proofCounts": { - "contrast": 1, - "distinctness": 0, - "target": 0, - "layout": 1, - "helperText": 0, - "optionRow": 0, - "anchoredSurface": 0, - "motion": 0 - } - }, - { - "componentId": "badge", - "id": "emphasis=soft|size=md|tone=warning__rest", - "name": "warning-soft", - "caseKey": "emphasis=soft|size=md|tone=warning", - "axes": { - "tone": "warning", - "emphasis": "soft", - "size": "md" - }, - "states": [ - "rest" - ], - "props": {}, - "sampleText": "Review", - "declared": [ - "contrast", - "layout" - ], - "proofCounts": { - "contrast": 1, - "distinctness": 0, - "target": 0, - "layout": 1, - "helperText": 0, - "optionRow": 0, - "anchoredSurface": 0, - "motion": 0 - } - }, - { - "componentId": "badge", - "id": "emphasis=solid|size=md|tone=warning__rest", - "name": "warning-solid", - "caseKey": "emphasis=solid|size=md|tone=warning", - "axes": { - "tone": "warning", - "emphasis": "solid", - "size": "md" - }, - "states": [ - "rest" - ], - "props": {}, - "sampleText": "Review", - "declared": [ - "contrast", - "layout" - ], - "proofCounts": { - "contrast": 1, - "distinctness": 0, - "target": 0, - "layout": 1, - "helperText": 0, - "optionRow": 0, - "anchoredSurface": 0, - "motion": 0 - } - }, - { - "componentId": "badge", - "id": "emphasis=outline|size=md|tone=warning__rest", - "name": "warning-outline", - "caseKey": "emphasis=outline|size=md|tone=warning", - "axes": { - "tone": "warning", - "emphasis": "outline", - "size": "md" - }, - "states": [ - "rest" - ], - "props": {}, - "sampleText": "Review", - "declared": [ - "contrast", - "layout" - ], - "proofCounts": { - "contrast": 1, - "distinctness": 0, - "target": 0, - "layout": 1, - "helperText": 0, - "optionRow": 0, - "anchoredSurface": 0, - "motion": 0 - } - }, - { - "componentId": "badge", - "id": "emphasis=soft|size=md|tone=danger__rest", - "name": "danger-soft", - "caseKey": "emphasis=soft|size=md|tone=danger", - "axes": { - "tone": "danger", - "emphasis": "soft", - "size": "md" }, - "states": [ - "rest" - ], - "props": {}, - "sampleText": "Failed", - "declared": [ - "contrast", - "layout" - ], - "proofCounts": { - "contrast": 1, - "distinctness": 0, - "target": 0, - "layout": 1, - "helperText": 0, - "optionRow": 0, - "anchoredSurface": 0, - "motion": 0 - } + "layout": [ + { + "target": "root", + "widths": [ + 120, + 200, + 320, + 768, + 1280 + ], + "heights": [], + "estimatedInlinePx": 119.63, + "minimumInlinePx": 44.96, + "textBehavior": "wrap-anywhere", + "requiredBlockPx": 30, + "widthChecks": [ + { + "width": 120, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 89.98, + "estimatedLineCount": 1, + "estimatedBlockPx": 30, + "fitsLines": true, + "pass": true + }, + { + "width": 200, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 169.98, + "estimatedLineCount": 1, + "estimatedBlockPx": 30, + "fitsLines": true, + "pass": true + }, + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 289.98, + "estimatedLineCount": 1, + "estimatedBlockPx": 30, + "fitsLines": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 737.98, + "estimatedLineCount": 1, + "estimatedBlockPx": 30, + "fitsLines": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 1249.98, + "estimatedLineCount": 1, + "estimatedBlockPx": 30, + "fitsLines": true, + "pass": true + } + ], + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "Text uses word boundaries and grapheme-safe breaks for estimated wrapping.", + "Block size is a minimum; no maximum height is declared.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true + } + ] }, { "componentId": "badge", @@ -1758,7 +2136,83 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [ + { + "target": "root", + "widths": [ + 120, + 200, + 320, + 768, + 1280 + ], + "heights": [], + "estimatedInlinePx": 119.63, + "minimumInlinePx": 44.96, + "textBehavior": "wrap-anywhere", + "requiredBlockPx": 30, + "widthChecks": [ + { + "width": 120, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 89.98, + "estimatedLineCount": 1, + "estimatedBlockPx": 30, + "fitsLines": true, + "pass": true + }, + { + "width": 200, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 169.98, + "estimatedLineCount": 1, + "estimatedBlockPx": 30, + "fitsLines": true, + "pass": true + }, + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 289.98, + "estimatedLineCount": 1, + "estimatedBlockPx": 30, + "fitsLines": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 737.98, + "estimatedLineCount": 1, + "estimatedBlockPx": 30, + "fitsLines": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 1249.98, + "estimatedLineCount": 1, + "estimatedBlockPx": 30, + "fitsLines": true, + "pass": true + } + ], + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "Text uses word boundaries and grapheme-safe breaks for estimated wrapping.", + "Block size is a minimum; no maximum height is declared.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true + } + ] }, { "componentId": "badge", @@ -1788,7 +2242,83 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [ + { + "target": "root", + "widths": [ + 120, + 200, + 320, + 768, + 1280 + ], + "heights": [], + "estimatedInlinePx": 119.63, + "minimumInlinePx": 44.96, + "textBehavior": "wrap-anywhere", + "requiredBlockPx": 30, + "widthChecks": [ + { + "width": 120, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 89.98, + "estimatedLineCount": 1, + "estimatedBlockPx": 30, + "fitsLines": true, + "pass": true + }, + { + "width": 200, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 169.98, + "estimatedLineCount": 1, + "estimatedBlockPx": 30, + "fitsLines": true, + "pass": true + }, + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 289.98, + "estimatedLineCount": 1, + "estimatedBlockPx": 30, + "fitsLines": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 737.98, + "estimatedLineCount": 1, + "estimatedBlockPx": 30, + "fitsLines": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 1249.98, + "estimatedLineCount": 1, + "estimatedBlockPx": 30, + "fitsLines": true, + "pass": true + } + ], + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "Text uses word boundaries and grapheme-safe breaks for estimated wrapping.", + "Block size is a minimum; no maximum height is declared.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true + } + ] }, { "componentId": "breadcrumbs", @@ -1817,7 +2347,73 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [ + { + "target": "root", + "widths": [ + 220, + 320, + 768, + 1280 + ], + "heights": [], + "estimatedInlinePx": 298.88, + "minimumInlinePx": 126.4, + "textBehavior": "wrap", + "requiredBlockPx": 44, + "widthChecks": [ + { + "width": 220, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 172, + "estimatedLineCount": 2, + "estimatedBlockPx": 44, + "fitsLines": true, + "pass": true + }, + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 272, + "estimatedLineCount": 1, + "estimatedBlockPx": 44, + "fitsLines": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 720, + "estimatedLineCount": 1, + "estimatedBlockPx": 44, + "fitsLines": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 1232, + "estimatedLineCount": 1, + "estimatedBlockPx": 44, + "fitsLines": true, + "pass": true + } + ], + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "Text uses whitespace word boundaries for estimated wrapping.", + "Wrapping uses the default line height of 1.4.", + "Block size is a minimum; no maximum height is declared.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true + } + ] }, { "componentId": "button", @@ -1848,7 +2444,83 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [ + { + "target": "root", + "widths": [ + 180, + 240, + 320, + 768, + 1280 + ], + "heights": [], + "estimatedInlinePx": 172.82, + "minimumInlinePx": 68.28, + "textBehavior": "wrap-anywhere", + "requiredBlockPx": 48, + "widthChecks": [ + { + "width": 180, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 126.66, + "estimatedLineCount": 1, + "estimatedBlockPx": 48, + "fitsLines": true, + "pass": true + }, + { + "width": 240, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 186.66, + "estimatedLineCount": 1, + "estimatedBlockPx": 48, + "fitsLines": true, + "pass": true + }, + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 266.66, + "estimatedLineCount": 1, + "estimatedBlockPx": 48, + "fitsLines": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 714.66, + "estimatedLineCount": 1, + "estimatedBlockPx": 48, + "fitsLines": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 1226.66, + "estimatedLineCount": 1, + "estimatedBlockPx": 48, + "fitsLines": true, + "pass": true + } + ], + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "Text uses word boundaries and grapheme-safe breaks for estimated wrapping.", + "Block size is a minimum; no maximum height is declared.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true + } + ] }, { "componentId": "button", @@ -1879,7 +2551,83 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [ + { + "target": "root", + "widths": [ + 180, + 240, + 320, + 768, + 1280 + ], + "heights": [], + "estimatedInlinePx": 172.82, + "minimumInlinePx": 68.28, + "textBehavior": "wrap-anywhere", + "requiredBlockPx": 48, + "widthChecks": [ + { + "width": 180, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 126.66, + "estimatedLineCount": 1, + "estimatedBlockPx": 48, + "fitsLines": true, + "pass": true + }, + { + "width": 240, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 186.66, + "estimatedLineCount": 1, + "estimatedBlockPx": 48, + "fitsLines": true, + "pass": true + }, + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 266.66, + "estimatedLineCount": 1, + "estimatedBlockPx": 48, + "fitsLines": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 714.66, + "estimatedLineCount": 1, + "estimatedBlockPx": 48, + "fitsLines": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 1226.66, + "estimatedLineCount": 1, + "estimatedBlockPx": 48, + "fitsLines": true, + "pass": true + } + ], + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "Text uses word boundaries and grapheme-safe breaks for estimated wrapping.", + "Block size is a minimum; no maximum height is declared.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true + } + ] }, { "componentId": "button", @@ -1910,7 +2658,83 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [ + { + "target": "root", + "widths": [ + 180, + 240, + 320, + 768, + 1280 + ], + "heights": [], + "estimatedInlinePx": 172.82, + "minimumInlinePx": 68.28, + "textBehavior": "wrap-anywhere", + "requiredBlockPx": 48, + "widthChecks": [ + { + "width": 180, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 126.66, + "estimatedLineCount": 1, + "estimatedBlockPx": 48, + "fitsLines": true, + "pass": true + }, + { + "width": 240, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 186.66, + "estimatedLineCount": 1, + "estimatedBlockPx": 48, + "fitsLines": true, + "pass": true + }, + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 266.66, + "estimatedLineCount": 1, + "estimatedBlockPx": 48, + "fitsLines": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 714.66, + "estimatedLineCount": 1, + "estimatedBlockPx": 48, + "fitsLines": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 1226.66, + "estimatedLineCount": 1, + "estimatedBlockPx": 48, + "fitsLines": true, + "pass": true + } + ], + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "Text uses word boundaries and grapheme-safe breaks for estimated wrapping.", + "Block size is a minimum; no maximum height is declared.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true + } + ] }, { "componentId": "button", @@ -1941,7 +2765,83 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [ + { + "target": "root", + "widths": [ + 180, + 240, + 320, + 768, + 1280 + ], + "heights": [], + "estimatedInlinePx": 172.82, + "minimumInlinePx": 68.28, + "textBehavior": "wrap-anywhere", + "requiredBlockPx": 48, + "widthChecks": [ + { + "width": 180, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 126.66, + "estimatedLineCount": 1, + "estimatedBlockPx": 48, + "fitsLines": true, + "pass": true + }, + { + "width": 240, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 186.66, + "estimatedLineCount": 1, + "estimatedBlockPx": 48, + "fitsLines": true, + "pass": true + }, + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 266.66, + "estimatedLineCount": 1, + "estimatedBlockPx": 48, + "fitsLines": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 714.66, + "estimatedLineCount": 1, + "estimatedBlockPx": 48, + "fitsLines": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 1226.66, + "estimatedLineCount": 1, + "estimatedBlockPx": 48, + "fitsLines": true, + "pass": true + } + ], + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "Text uses word boundaries and grapheme-safe breaks for estimated wrapping.", + "Block size is a minimum; no maximum height is declared.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true + } + ] }, { "componentId": "button", @@ -1972,7 +2872,83 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [ + { + "target": "root", + "widths": [ + 180, + 240, + 320, + 768, + 1280 + ], + "heights": [], + "estimatedInlinePx": 172.82, + "minimumInlinePx": 68.28, + "textBehavior": "wrap-anywhere", + "requiredBlockPx": 48, + "widthChecks": [ + { + "width": 180, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 126.66, + "estimatedLineCount": 1, + "estimatedBlockPx": 48, + "fitsLines": true, + "pass": true + }, + { + "width": 240, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 186.66, + "estimatedLineCount": 1, + "estimatedBlockPx": 48, + "fitsLines": true, + "pass": true + }, + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 266.66, + "estimatedLineCount": 1, + "estimatedBlockPx": 48, + "fitsLines": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 714.66, + "estimatedLineCount": 1, + "estimatedBlockPx": 48, + "fitsLines": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 1226.66, + "estimatedLineCount": 1, + "estimatedBlockPx": 48, + "fitsLines": true, + "pass": true + } + ], + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "Text uses word boundaries and grapheme-safe breaks for estimated wrapping.", + "Block size is a minimum; no maximum height is declared.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true + } + ] }, { "componentId": "button", @@ -2003,7 +2979,83 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [ + { + "target": "root", + "widths": [ + 180, + 240, + 320, + 768, + 1280 + ], + "heights": [], + "estimatedInlinePx": 172.82, + "minimumInlinePx": 68.28, + "textBehavior": "wrap-anywhere", + "requiredBlockPx": 48, + "widthChecks": [ + { + "width": 180, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 126.66, + "estimatedLineCount": 1, + "estimatedBlockPx": 48, + "fitsLines": true, + "pass": true + }, + { + "width": 240, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 186.66, + "estimatedLineCount": 1, + "estimatedBlockPx": 48, + "fitsLines": true, + "pass": true + }, + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 266.66, + "estimatedLineCount": 1, + "estimatedBlockPx": 48, + "fitsLines": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 714.66, + "estimatedLineCount": 1, + "estimatedBlockPx": 48, + "fitsLines": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 1226.66, + "estimatedLineCount": 1, + "estimatedBlockPx": 48, + "fitsLines": true, + "pass": true + } + ], + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "Text uses word boundaries and grapheme-safe breaks for estimated wrapping.", + "Block size is a minimum; no maximum height is declared.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true + } + ] }, { "componentId": "button", @@ -2034,7 +3086,83 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [ + { + "target": "root", + "widths": [ + 180, + 240, + 320, + 768, + 1280 + ], + "heights": [], + "estimatedInlinePx": 149.5, + "minimumInlinePx": 44.96, + "textBehavior": "wrap-anywhere", + "requiredBlockPx": 44, + "widthChecks": [ + { + "width": 180, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 149.98, + "estimatedLineCount": 1, + "estimatedBlockPx": 44, + "fitsLines": true, + "pass": true + }, + { + "width": 240, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 209.98, + "estimatedLineCount": 1, + "estimatedBlockPx": 44, + "fitsLines": true, + "pass": true + }, + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 289.98, + "estimatedLineCount": 1, + "estimatedBlockPx": 44, + "fitsLines": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 737.98, + "estimatedLineCount": 1, + "estimatedBlockPx": 44, + "fitsLines": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 1249.98, + "estimatedLineCount": 1, + "estimatedBlockPx": 44, + "fitsLines": true, + "pass": true + } + ], + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "Text uses word boundaries and grapheme-safe breaks for estimated wrapping.", + "Block size is a minimum; no maximum height is declared.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true + } + ] }, { "componentId": "button", @@ -2065,7 +3193,83 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [ + { + "target": "root", + "widths": [ + 180, + 240, + 320, + 768, + 1280 + ], + "heights": [], + "estimatedInlinePx": 172.82, + "minimumInlinePx": 68.28, + "textBehavior": "wrap-anywhere", + "requiredBlockPx": 44, + "widthChecks": [ + { + "width": 180, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 126.66, + "estimatedLineCount": 1, + "estimatedBlockPx": 44, + "fitsLines": true, + "pass": true + }, + { + "width": 240, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 186.66, + "estimatedLineCount": 1, + "estimatedBlockPx": 44, + "fitsLines": true, + "pass": true + }, + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 266.66, + "estimatedLineCount": 1, + "estimatedBlockPx": 44, + "fitsLines": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 714.66, + "estimatedLineCount": 1, + "estimatedBlockPx": 44, + "fitsLines": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 1226.66, + "estimatedLineCount": 1, + "estimatedBlockPx": 44, + "fitsLines": true, + "pass": true + } + ], + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "Text uses word boundaries and grapheme-safe breaks for estimated wrapping.", + "Block size is a minimum; no maximum height is declared.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true + } + ] }, { "componentId": "button", @@ -2096,7 +3300,83 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [ + { + "target": "root", + "widths": [ + 180, + 240, + 320, + 768, + 1280 + ], + "heights": [], + "estimatedInlinePx": 230.36, + "minimumInlinePx": 91.01, + "textBehavior": "wrap-anywhere", + "requiredBlockPx": 52, + "widthChecks": [ + { + "width": 180, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 108.9, + "estimatedLineCount": 2, + "estimatedBlockPx": 78.21, + "fitsLines": true, + "pass": true + }, + { + "width": 240, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 168.9, + "estimatedLineCount": 1, + "estimatedBlockPx": 52, + "fitsLines": true, + "pass": true + }, + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 248.9, + "estimatedLineCount": 1, + "estimatedBlockPx": 52, + "fitsLines": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 696.9, + "estimatedLineCount": 1, + "estimatedBlockPx": 52, + "fitsLines": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 1208.9, + "estimatedLineCount": 1, + "estimatedBlockPx": 52, + "fitsLines": true, + "pass": true + } + ], + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "Text uses word boundaries and grapheme-safe breaks for estimated wrapping.", + "Block size is a minimum; no maximum height is declared.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true + } + ] }, { "componentId": "button", @@ -2126,7 +3406,83 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [ + { + "target": "root", + "widths": [ + 180, + 240, + 320, + 768, + 1280 + ], + "heights": [], + "estimatedInlinePx": 18, + "minimumInlinePx": 18, + "textBehavior": "wrap-anywhere", + "requiredBlockPx": 48, + "widthChecks": [ + { + "width": 180, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 162, + "estimatedLineCount": 1, + "estimatedBlockPx": 48, + "fitsLines": true, + "pass": true + }, + { + "width": 240, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 222, + "estimatedLineCount": 1, + "estimatedBlockPx": 48, + "fitsLines": true, + "pass": true + }, + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 302, + "estimatedLineCount": 1, + "estimatedBlockPx": 48, + "fitsLines": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 750, + "estimatedLineCount": 1, + "estimatedBlockPx": 48, + "fitsLines": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 1262, + "estimatedLineCount": 1, + "estimatedBlockPx": 48, + "fitsLines": true, + "pass": true + } + ], + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "Text uses word boundaries and grapheme-safe breaks for estimated wrapping.", + "Block size is a minimum; no maximum height is declared.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true + } + ] }, { "componentId": "button", @@ -2156,7 +3512,83 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [ + { + "target": "root", + "widths": [ + 180, + 240, + 320, + 768, + 1280 + ], + "heights": [], + "estimatedInlinePx": 20, + "minimumInlinePx": 20, + "textBehavior": "wrap-anywhere", + "requiredBlockPx": 52, + "widthChecks": [ + { + "width": 180, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 160, + "estimatedLineCount": 1, + "estimatedBlockPx": 52, + "fitsLines": true, + "pass": true + }, + { + "width": 240, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 220, + "estimatedLineCount": 1, + "estimatedBlockPx": 52, + "fitsLines": true, + "pass": true + }, + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 300, + "estimatedLineCount": 1, + "estimatedBlockPx": 52, + "fitsLines": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 748, + "estimatedLineCount": 1, + "estimatedBlockPx": 52, + "fitsLines": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 1260, + "estimatedLineCount": 1, + "estimatedBlockPx": 52, + "fitsLines": true, + "pass": true + } + ], + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "Text uses word boundaries and grapheme-safe breaks for estimated wrapping.", + "Block size is a minimum; no maximum height is declared.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true + } + ] }, { "componentId": "button", @@ -2187,7 +3619,83 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [ + { + "target": "root", + "widths": [ + 180, + 240, + 320, + 768, + 1280 + ], + "heights": [], + "estimatedInlinePx": 142.95, + "minimumInlinePx": 68.28, + "textBehavior": "wrap-anywhere", + "requiredBlockPx": 48, + "widthChecks": [ + { + "width": 180, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 126.66, + "estimatedLineCount": 1, + "estimatedBlockPx": 48, + "fitsLines": true, + "pass": true + }, + { + "width": 240, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 186.66, + "estimatedLineCount": 1, + "estimatedBlockPx": 48, + "fitsLines": true, + "pass": true + }, + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 266.66, + "estimatedLineCount": 1, + "estimatedBlockPx": 48, + "fitsLines": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 714.66, + "estimatedLineCount": 1, + "estimatedBlockPx": 48, + "fitsLines": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 1226.66, + "estimatedLineCount": 1, + "estimatedBlockPx": 48, + "fitsLines": true, + "pass": true + } + ], + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "Text uses word boundaries and grapheme-safe breaks for estimated wrapping.", + "Block size is a minimum; no maximum height is declared.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true + } + ] }, { "componentId": "button", @@ -2221,7 +3729,83 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [ + { + "target": "root", + "widths": [ + 180, + 240, + 320, + 768, + 1280 + ], + "heights": [], + "estimatedInlinePx": 232.56, + "minimumInlinePx": 68.28, + "textBehavior": "wrap-anywhere", + "requiredBlockPx": 48, + "widthChecks": [ + { + "width": 180, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 126.66, + "estimatedLineCount": 2, + "estimatedBlockPx": 58.67, + "fitsLines": true, + "pass": true + }, + { + "width": 240, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 186.66, + "estimatedLineCount": 1, + "estimatedBlockPx": 48, + "fitsLines": true, + "pass": true + }, + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 266.66, + "estimatedLineCount": 1, + "estimatedBlockPx": 48, + "fitsLines": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 714.66, + "estimatedLineCount": 1, + "estimatedBlockPx": 48, + "fitsLines": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 1226.66, + "estimatedLineCount": 1, + "estimatedBlockPx": 48, + "fitsLines": true, + "pass": true + } + ], + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "Text uses word boundaries and grapheme-safe breaks for estimated wrapping.", + "Block size is a minimum; no maximum height is declared.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true + } + ] }, { "componentId": "card", @@ -2249,7 +3833,64 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [ + { + "target": "root", + "widths": [ + 280, + 360, + 320, + 768, + 1280 + ], + "heights": [ + 220, + 320 + ], + "estimatedInlinePx": 0, + "minimumInlinePx": 0, + "textBehavior": "single-line", + "requiredBlockPx": 220, + "widthChecks": [ + { + "width": 280, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 360, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + } + ], + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true + } + ] }, { "componentId": "card", @@ -2277,7 +3918,64 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [ + { + "target": "root", + "widths": [ + 280, + 360, + 320, + 768, + 1280 + ], + "heights": [ + 220, + 320 + ], + "estimatedInlinePx": 0, + "minimumInlinePx": 0, + "textBehavior": "single-line", + "requiredBlockPx": 220, + "widthChecks": [ + { + "width": 280, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 360, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + } + ], + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true + } + ] }, { "componentId": "card", @@ -2305,7 +4003,64 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [ + { + "target": "root", + "widths": [ + 280, + 360, + 320, + 768, + 1280 + ], + "heights": [ + 220, + 320 + ], + "estimatedInlinePx": 0, + "minimumInlinePx": 0, + "textBehavior": "single-line", + "requiredBlockPx": 220, + "widthChecks": [ + { + "width": 280, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 360, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + } + ], + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true + } + ] }, { "componentId": "checkbox", @@ -2335,7 +4090,72 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [ + { + "target": "root", + "widths": [ + 240, + 320, + 768, + 1280 + ], + "heights": [], + "estimatedInlinePx": 308.84, + "minimumInlinePx": 144.56, + "textBehavior": "wrap", + "requiredBlockPx": 44, + "widthChecks": [ + { + "width": 240, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 199.99, + "estimatedLineCount": 2, + "estimatedBlockPx": 72.01, + "fitsLines": true, + "pass": true + }, + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 279.99, + "estimatedLineCount": 1, + "estimatedBlockPx": 44, + "fitsLines": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 727.99, + "estimatedLineCount": 1, + "estimatedBlockPx": 44, + "fitsLines": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 1239.99, + "estimatedLineCount": 1, + "estimatedBlockPx": 44, + "fitsLines": true, + "pass": true + } + ], + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "Text uses whitespace word boundaries for estimated wrapping.", + "Block size is a minimum; no maximum height is declared.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true + } + ] }, { "componentId": "checkbox", @@ -2365,7 +4185,72 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [ + { + "target": "root", + "widths": [ + 240, + 320, + 768, + 1280 + ], + "heights": [], + "estimatedInlinePx": 308.84, + "minimumInlinePx": 144.56, + "textBehavior": "wrap", + "requiredBlockPx": 44, + "widthChecks": [ + { + "width": 240, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 199.99, + "estimatedLineCount": 2, + "estimatedBlockPx": 72.01, + "fitsLines": true, + "pass": true + }, + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 279.99, + "estimatedLineCount": 1, + "estimatedBlockPx": 44, + "fitsLines": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 727.99, + "estimatedLineCount": 1, + "estimatedBlockPx": 44, + "fitsLines": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 1239.99, + "estimatedLineCount": 1, + "estimatedBlockPx": 44, + "fitsLines": true, + "pass": true + } + ], + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "Text uses whitespace word boundaries for estimated wrapping.", + "Block size is a minimum; no maximum height is declared.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true + } + ] }, { "componentId": "checkbox", @@ -2395,7 +4280,72 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [ + { + "target": "root", + "widths": [ + 240, + 320, + 768, + 1280 + ], + "heights": [], + "estimatedInlinePx": 308.84, + "minimumInlinePx": 144.56, + "textBehavior": "wrap", + "requiredBlockPx": 44, + "widthChecks": [ + { + "width": 240, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 199.99, + "estimatedLineCount": 2, + "estimatedBlockPx": 72.01, + "fitsLines": true, + "pass": true + }, + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 279.99, + "estimatedLineCount": 1, + "estimatedBlockPx": 44, + "fitsLines": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 727.99, + "estimatedLineCount": 1, + "estimatedBlockPx": 44, + "fitsLines": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 1239.99, + "estimatedLineCount": 1, + "estimatedBlockPx": 44, + "fitsLines": true, + "pass": true + } + ], + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "Text uses whitespace word boundaries for estimated wrapping.", + "Block size is a minimum; no maximum height is declared.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true + } + ] }, { "componentId": "checkbox", @@ -2425,7 +4375,72 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [ + { + "target": "root", + "widths": [ + 240, + 320, + 768, + 1280 + ], + "heights": [], + "estimatedInlinePx": 308.84, + "minimumInlinePx": 144.56, + "textBehavior": "wrap", + "requiredBlockPx": 44, + "widthChecks": [ + { + "width": 240, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 199.99, + "estimatedLineCount": 2, + "estimatedBlockPx": 72.01, + "fitsLines": true, + "pass": true + }, + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 279.99, + "estimatedLineCount": 1, + "estimatedBlockPx": 44, + "fitsLines": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 727.99, + "estimatedLineCount": 1, + "estimatedBlockPx": 44, + "fitsLines": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 1239.99, + "estimatedLineCount": 1, + "estimatedBlockPx": 44, + "fitsLines": true, + "pass": true + } + ], + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "Text uses whitespace word boundaries for estimated wrapping.", + "Block size is a minimum; no maximum height is declared.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true + } + ] }, { "componentId": "checkbox", @@ -2455,7 +4470,72 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [ + { + "target": "root", + "widths": [ + 240, + 320, + 768, + 1280 + ], + "heights": [], + "estimatedInlinePx": 308.84, + "minimumInlinePx": 144.56, + "textBehavior": "wrap", + "requiredBlockPx": 44, + "widthChecks": [ + { + "width": 240, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 199.99, + "estimatedLineCount": 2, + "estimatedBlockPx": 72.01, + "fitsLines": true, + "pass": true + }, + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 279.99, + "estimatedLineCount": 1, + "estimatedBlockPx": 44, + "fitsLines": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 727.99, + "estimatedLineCount": 1, + "estimatedBlockPx": 44, + "fitsLines": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 1239.99, + "estimatedLineCount": 1, + "estimatedBlockPx": 44, + "fitsLines": true, + "pass": true + } + ], + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "Text uses whitespace word boundaries for estimated wrapping.", + "Block size is a minimum; no maximum height is declared.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true + } + ] }, { "componentId": "checkbox", @@ -2485,7 +4565,72 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [ + { + "target": "root", + "widths": [ + 240, + 320, + 768, + 1280 + ], + "heights": [], + "estimatedInlinePx": 308.84, + "minimumInlinePx": 144.56, + "textBehavior": "wrap", + "requiredBlockPx": 44, + "widthChecks": [ + { + "width": 240, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 199.99, + "estimatedLineCount": 2, + "estimatedBlockPx": 72.01, + "fitsLines": true, + "pass": true + }, + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 279.99, + "estimatedLineCount": 1, + "estimatedBlockPx": 44, + "fitsLines": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 727.99, + "estimatedLineCount": 1, + "estimatedBlockPx": 44, + "fitsLines": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 1239.99, + "estimatedLineCount": 1, + "estimatedBlockPx": 44, + "fitsLines": true, + "pass": true + } + ], + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "Text uses whitespace word boundaries for estimated wrapping.", + "Block size is a minimum; no maximum height is declared.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true + } + ] }, { "componentId": "checkbox", @@ -2515,7 +4660,72 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [ + { + "target": "root", + "widths": [ + 240, + 320, + 768, + 1280 + ], + "heights": [], + "estimatedInlinePx": 308.84, + "minimumInlinePx": 144.56, + "textBehavior": "wrap", + "requiredBlockPx": 44, + "widthChecks": [ + { + "width": 240, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 199.99, + "estimatedLineCount": 2, + "estimatedBlockPx": 72.01, + "fitsLines": true, + "pass": true + }, + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 279.99, + "estimatedLineCount": 1, + "estimatedBlockPx": 44, + "fitsLines": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 727.99, + "estimatedLineCount": 1, + "estimatedBlockPx": 44, + "fitsLines": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 1239.99, + "estimatedLineCount": 1, + "estimatedBlockPx": 44, + "fitsLines": true, + "pass": true + } + ], + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "Text uses whitespace word boundaries for estimated wrapping.", + "Block size is a minimum; no maximum height is declared.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true + } + ] }, { "componentId": "checkbox", @@ -2545,7 +4755,72 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [ + { + "target": "root", + "widths": [ + 240, + 320, + 768, + 1280 + ], + "heights": [], + "estimatedInlinePx": 308.84, + "minimumInlinePx": 144.56, + "textBehavior": "wrap", + "requiredBlockPx": 44, + "widthChecks": [ + { + "width": 240, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 199.99, + "estimatedLineCount": 2, + "estimatedBlockPx": 72.01, + "fitsLines": true, + "pass": true + }, + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 279.99, + "estimatedLineCount": 1, + "estimatedBlockPx": 44, + "fitsLines": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 727.99, + "estimatedLineCount": 1, + "estimatedBlockPx": 44, + "fitsLines": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 1239.99, + "estimatedLineCount": 1, + "estimatedBlockPx": 44, + "fitsLines": true, + "pass": true + } + ], + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "Text uses whitespace word boundaries for estimated wrapping.", + "Block size is a minimum; no maximum height is declared.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true + } + ] }, { "componentId": "checkbox", @@ -2575,7 +4850,72 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [ + { + "target": "root", + "widths": [ + 240, + 320, + 768, + 1280 + ], + "heights": [], + "estimatedInlinePx": 308.84, + "minimumInlinePx": 144.56, + "textBehavior": "wrap", + "requiredBlockPx": 44, + "widthChecks": [ + { + "width": 240, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 199.99, + "estimatedLineCount": 2, + "estimatedBlockPx": 72.01, + "fitsLines": true, + "pass": true + }, + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 279.99, + "estimatedLineCount": 1, + "estimatedBlockPx": 44, + "fitsLines": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 727.99, + "estimatedLineCount": 1, + "estimatedBlockPx": 44, + "fitsLines": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 1239.99, + "estimatedLineCount": 1, + "estimatedBlockPx": 44, + "fitsLines": true, + "pass": true + } + ], + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "Text uses whitespace word boundaries for estimated wrapping.", + "Block size is a minimum; no maximum height is declared.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true + } + ] }, { "componentId": "chip", @@ -2604,7 +4944,64 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [ + { + "target": "button", + "widths": [ + 120, + 200, + 320, + 768, + 1280 + ], + "heights": [ + 36, + 40 + ], + "estimatedInlinePx": 53.34, + "minimumInlinePx": 53.34, + "textBehavior": "single-line", + "requiredBlockPx": 40, + "widthChecks": [ + { + "width": 120, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 200, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + } + ], + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true + } + ] }, { "componentId": "chip", @@ -2633,7 +5030,64 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [ + { + "target": "button", + "widths": [ + 120, + 200, + 320, + 768, + 1280 + ], + "heights": [ + 36, + 40 + ], + "estimatedInlinePx": 53.34, + "minimumInlinePx": 53.34, + "textBehavior": "single-line", + "requiredBlockPx": 40, + "widthChecks": [ + { + "width": 120, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 200, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + } + ], + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true + } + ] }, { "componentId": "chip", @@ -2662,7 +5116,64 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [ + { + "target": "button", + "widths": [ + 120, + 200, + 320, + 768, + 1280 + ], + "heights": [ + 36, + 40 + ], + "estimatedInlinePx": 53.34, + "minimumInlinePx": 53.34, + "textBehavior": "single-line", + "requiredBlockPx": 36, + "widthChecks": [ + { + "width": 120, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 200, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + } + ], + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true + } + ] }, { "componentId": "combobox", @@ -2691,7 +5202,8 @@ "optionRow": 1, "anchoredSurface": 3, "motion": 0 - } + }, + "layout": [] }, { "componentId": "combobox", @@ -2720,7 +5232,8 @@ "optionRow": 1, "anchoredSurface": 3, "motion": 0 - } + }, + "layout": [] }, { "componentId": "combobox", @@ -2749,7 +5262,8 @@ "optionRow": 1, "anchoredSurface": 3, "motion": 0 - } + }, + "layout": [] }, { "componentId": "combobox", @@ -2778,7 +5292,8 @@ "optionRow": 1, "anchoredSurface": 3, "motion": 0 - } + }, + "layout": [] }, { "componentId": "combobox", @@ -2807,7 +5322,8 @@ "optionRow": 1, "anchoredSurface": 3, "motion": 0 - } + }, + "layout": [] }, { "componentId": "combobox", @@ -2836,7 +5352,8 @@ "optionRow": 1, "anchoredSurface": 3, "motion": 0 - } + }, + "layout": [] }, { "componentId": "combobox", @@ -2865,7 +5382,8 @@ "optionRow": 1, "anchoredSurface": 3, "motion": 0 - } + }, + "layout": [] }, { "componentId": "combobox", @@ -2894,7 +5412,8 @@ "optionRow": 1, "anchoredSurface": 3, "motion": 0 - } + }, + "layout": [] }, { "componentId": "combobox", @@ -2923,7 +5442,8 @@ "optionRow": 1, "anchoredSurface": 3, "motion": 0 - } + }, + "layout": [] }, { "componentId": "command-palette", @@ -2950,7 +5470,8 @@ "optionRow": 1, "anchoredSurface": 3, "motion": 0 - } + }, + "layout": [] }, { "componentId": "command-palette", @@ -2977,7 +5498,8 @@ "optionRow": 1, "anchoredSurface": 3, "motion": 0 - } + }, + "layout": [] }, { "componentId": "data-chart", @@ -3004,7 +5526,57 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [ + { + "target": "surface", + "widths": [ + 320, + 640, + 768, + 1280 + ], + "heights": [ + 240, + 320 + ], + "estimatedInlinePx": 0, + "minimumInlinePx": 0, + "textBehavior": "single-line", + "requiredBlockPx": 280, + "widthChecks": [ + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 640, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + } + ], + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true + } + ] }, { "componentId": "data-chart", @@ -3031,7 +5603,57 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [ + { + "target": "surface", + "widths": [ + 320, + 640, + 768, + 1280 + ], + "heights": [ + 240, + 320 + ], + "estimatedInlinePx": 0, + "minimumInlinePx": 0, + "textBehavior": "single-line", + "requiredBlockPx": 280, + "widthChecks": [ + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 640, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + } + ], + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true + } + ] }, { "componentId": "data-chart", @@ -3058,7 +5680,57 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [ + { + "target": "surface", + "widths": [ + 320, + 640, + 768, + 1280 + ], + "heights": [ + 240, + 320 + ], + "estimatedInlinePx": 0, + "minimumInlinePx": 0, + "textBehavior": "single-line", + "requiredBlockPx": 280, + "widthChecks": [ + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 640, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + } + ], + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true + } + ] }, { "componentId": "data-grid-lite", @@ -3086,7 +5758,64 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [ + { + "target": "shell", + "widths": [ + 320, + 480, + 760, + 768, + 1280 + ], + "heights": [ + 240, + 360 + ], + "estimatedInlinePx": 0, + "minimumInlinePx": 0, + "textBehavior": "single-line", + "requiredBlockPx": 240, + "widthChecks": [ + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 480, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 760, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + } + ], + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true + } + ] }, { "componentId": "data-grid-lite", @@ -3114,7 +5843,64 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [ + { + "target": "shell", + "widths": [ + 320, + 480, + 760, + 768, + 1280 + ], + "heights": [ + 240, + 360 + ], + "estimatedInlinePx": 0, + "minimumInlinePx": 0, + "textBehavior": "single-line", + "requiredBlockPx": 240, + "widthChecks": [ + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 480, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 760, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + } + ], + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true + } + ] }, { "componentId": "data-grid-lite", @@ -3142,7 +5928,64 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [ + { + "target": "shell", + "widths": [ + 320, + 480, + 760, + 768, + 1280 + ], + "heights": [ + 240, + 360 + ], + "estimatedInlinePx": 0, + "minimumInlinePx": 0, + "textBehavior": "single-line", + "requiredBlockPx": 240, + "widthChecks": [ + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 480, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 760, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + } + ], + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true + } + ] }, { "componentId": "date-picker", @@ -3171,7 +6014,64 @@ "optionRow": 0, "anchoredSurface": 3, "motion": 0 - } + }, + "layout": [ + { + "target": "surface", + "widths": [ + 280, + 320, + 360, + 768, + 1280 + ], + "heights": [ + 324, + 360 + ], + "estimatedInlinePx": 0, + "minimumInlinePx": 0, + "textBehavior": "single-line", + "requiredBlockPx": 324, + "widthChecks": [ + { + "width": 280, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 360, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + } + ], + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true + } + ] }, { "componentId": "date-picker", @@ -3200,7 +6100,64 @@ "optionRow": 0, "anchoredSurface": 3, "motion": 0 - } + }, + "layout": [ + { + "target": "surface", + "widths": [ + 280, + 320, + 360, + 768, + 1280 + ], + "heights": [ + 324, + 360 + ], + "estimatedInlinePx": 0, + "minimumInlinePx": 0, + "textBehavior": "single-line", + "requiredBlockPx": 324, + "widthChecks": [ + { + "width": 280, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 360, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + } + ], + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true + } + ] }, { "componentId": "date-picker", @@ -3229,7 +6186,64 @@ "optionRow": 0, "anchoredSurface": 3, "motion": 0 - } + }, + "layout": [ + { + "target": "surface", + "widths": [ + 280, + 320, + 360, + 768, + 1280 + ], + "heights": [ + 324, + 360 + ], + "estimatedInlinePx": 0, + "minimumInlinePx": 0, + "textBehavior": "single-line", + "requiredBlockPx": 324, + "widthChecks": [ + { + "width": 280, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 360, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + } + ], + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true + } + ] }, { "componentId": "dialog", @@ -3256,7 +6270,72 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [ + { + "target": "surface", + "widths": [ + 360, + 420, + 560, + 320, + 768, + 1280 + ], + "heights": [ + 220, + 320, + 420 + ], + "estimatedInlinePx": 0, + "minimumInlinePx": 0, + "textBehavior": "single-line", + "requiredBlockPx": 220, + "widthChecks": [ + { + "width": 360, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 420, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 560, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + } + ], + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true + } + ] }, { "componentId": "dialog", @@ -3283,7 +6362,72 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [ + { + "target": "surface", + "widths": [ + 360, + 420, + 560, + 320, + 768, + 1280 + ], + "heights": [ + 220, + 320, + 420 + ], + "estimatedInlinePx": 0, + "minimumInlinePx": 0, + "textBehavior": "single-line", + "requiredBlockPx": 220, + "widthChecks": [ + { + "width": 360, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 420, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 560, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + } + ], + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true + } + ] }, { "componentId": "dialog", @@ -3310,7 +6454,72 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [ + { + "target": "surface", + "widths": [ + 360, + 420, + 560, + 320, + 768, + 1280 + ], + "heights": [ + 220, + 320, + 420 + ], + "estimatedInlinePx": 0, + "minimumInlinePx": 0, + "textBehavior": "single-line", + "requiredBlockPx": 220, + "widthChecks": [ + { + "width": 360, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 420, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 560, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + } + ], + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true + } + ] }, { "componentId": "drawer", @@ -3338,7 +6547,72 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [ + { + "target": "surface", + "widths": [ + 360, + 420, + 520, + 320, + 768, + 1280 + ], + "heights": [ + 420, + 540, + 640 + ], + "estimatedInlinePx": 0, + "minimumInlinePx": 0, + "textBehavior": "single-line", + "requiredBlockPx": 420, + "widthChecks": [ + { + "width": 360, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 420, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 520, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + } + ], + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true + } + ] }, { "componentId": "drawer", @@ -3366,7 +6640,72 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [ + { + "target": "surface", + "widths": [ + 360, + 420, + 520, + 320, + 768, + 1280 + ], + "heights": [ + 420, + 540, + 640 + ], + "estimatedInlinePx": 0, + "minimumInlinePx": 0, + "textBehavior": "single-line", + "requiredBlockPx": 420, + "widthChecks": [ + { + "width": 360, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 420, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 520, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + } + ], + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true + } + ] }, { "componentId": "drawer", @@ -3394,7 +6733,72 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [ + { + "target": "surface", + "widths": [ + 360, + 420, + 520, + 320, + 768, + 1280 + ], + "heights": [ + 420, + 540, + 640 + ], + "estimatedInlinePx": 0, + "minimumInlinePx": 0, + "textBehavior": "single-line", + "requiredBlockPx": 420, + "widthChecks": [ + { + "width": 360, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 420, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 520, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + } + ], + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true + } + ] }, { "componentId": "drawer", @@ -3422,7 +6826,72 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [ + { + "target": "surface", + "widths": [ + 360, + 420, + 520, + 320, + 768, + 1280 + ], + "heights": [ + 420, + 540, + 640 + ], + "estimatedInlinePx": 0, + "minimumInlinePx": 0, + "textBehavior": "single-line", + "requiredBlockPx": 420, + "widthChecks": [ + { + "width": 360, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 420, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 520, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + } + ], + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true + } + ] }, { "componentId": "drawer", @@ -3450,7 +6919,72 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [ + { + "target": "surface", + "widths": [ + 360, + 420, + 520, + 320, + 768, + 1280 + ], + "heights": [ + 420, + 540, + 640 + ], + "estimatedInlinePx": 0, + "minimumInlinePx": 0, + "textBehavior": "single-line", + "requiredBlockPx": 420, + "widthChecks": [ + { + "width": 360, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 420, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 520, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + } + ], + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true + } + ] }, { "componentId": "drawer", @@ -3478,7 +7012,72 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [ + { + "target": "surface", + "widths": [ + 360, + 420, + 520, + 320, + 768, + 1280 + ], + "heights": [ + 420, + 540, + 640 + ], + "estimatedInlinePx": 0, + "minimumInlinePx": 0, + "textBehavior": "single-line", + "requiredBlockPx": 420, + "widthChecks": [ + { + "width": 360, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 420, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 520, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + } + ], + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true + } + ] }, { "componentId": "empty-state", @@ -3506,7 +7105,72 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [ + { + "target": "root", + "widths": [ + 280, + 360, + 480, + 320, + 768, + 1280 + ], + "heights": [ + 220, + 280, + 340 + ], + "estimatedInlinePx": 0, + "minimumInlinePx": 0, + "textBehavior": "single-line", + "requiredBlockPx": 220, + "widthChecks": [ + { + "width": 280, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 360, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 480, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + } + ], + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true + } + ] }, { "componentId": "empty-state", @@ -3534,7 +7198,72 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [ + { + "target": "root", + "widths": [ + 280, + 360, + 480, + 320, + 768, + 1280 + ], + "heights": [ + 220, + 280, + 340 + ], + "estimatedInlinePx": 0, + "minimumInlinePx": 0, + "textBehavior": "single-line", + "requiredBlockPx": 220, + "widthChecks": [ + { + "width": 280, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 360, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 480, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + } + ], + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true + } + ] }, { "componentId": "empty-state", @@ -3562,7 +7291,72 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [ + { + "target": "root", + "widths": [ + 280, + 360, + 480, + 320, + 768, + 1280 + ], + "heights": [ + 220, + 280, + 340 + ], + "estimatedInlinePx": 0, + "minimumInlinePx": 0, + "textBehavior": "single-line", + "requiredBlockPx": 220, + "widthChecks": [ + { + "width": 280, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 360, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 480, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + } + ], + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true + } + ] }, { "componentId": "file-upload", @@ -3590,7 +7384,71 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [ + { + "target": "field", + "widths": [ + 280, + 360, + 460, + 320, + 768, + 1280 + ], + "heights": [ + 120, + 160 + ], + "estimatedInlinePx": 0, + "minimumInlinePx": 0, + "textBehavior": "single-line", + "requiredBlockPx": 120, + "widthChecks": [ + { + "width": 280, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 360, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 460, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + } + ], + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true + } + ] }, { "componentId": "file-upload", @@ -3618,7 +7476,71 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [ + { + "target": "field", + "widths": [ + 280, + 360, + 460, + 320, + 768, + 1280 + ], + "heights": [ + 120, + 160 + ], + "estimatedInlinePx": 0, + "minimumInlinePx": 0, + "textBehavior": "single-line", + "requiredBlockPx": 120, + "widthChecks": [ + { + "width": 280, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 360, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 460, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + } + ], + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true + } + ] }, { "componentId": "file-upload", @@ -3646,7 +7568,71 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [ + { + "target": "field", + "widths": [ + 280, + 360, + 460, + 320, + 768, + 1280 + ], + "heights": [ + 120, + 160 + ], + "estimatedInlinePx": 0, + "minimumInlinePx": 0, + "textBehavior": "single-line", + "requiredBlockPx": 120, + "widthChecks": [ + { + "width": 280, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 360, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 460, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + } + ], + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true + } + ] }, { "componentId": "file-upload", @@ -3674,7 +7660,71 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [ + { + "target": "field", + "widths": [ + 280, + 360, + 460, + 320, + 768, + 1280 + ], + "heights": [ + 120, + 160 + ], + "estimatedInlinePx": 0, + "minimumInlinePx": 0, + "textBehavior": "single-line", + "requiredBlockPx": 120, + "widthChecks": [ + { + "width": 280, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 360, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 460, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + } + ], + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true + } + ] }, { "componentId": "file-upload", @@ -3702,7 +7752,71 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [ + { + "target": "field", + "widths": [ + 280, + 360, + 460, + 320, + 768, + 1280 + ], + "heights": [ + 120, + 160 + ], + "estimatedInlinePx": 0, + "minimumInlinePx": 0, + "textBehavior": "single-line", + "requiredBlockPx": 120, + "widthChecks": [ + { + "width": 280, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 360, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 460, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + } + ], + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true + } + ] }, { "componentId": "file-upload", @@ -3730,7 +7844,71 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [ + { + "target": "field", + "widths": [ + 280, + 360, + 460, + 320, + 768, + 1280 + ], + "heights": [ + 120, + 160 + ], + "estimatedInlinePx": 0, + "minimumInlinePx": 0, + "textBehavior": "single-line", + "requiredBlockPx": 120, + "widthChecks": [ + { + "width": 280, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 360, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 460, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + } + ], + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true + } + ] }, { "componentId": "inline-edit", @@ -3757,7 +7935,8 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [] }, { "componentId": "menu", @@ -3785,7 +7964,8 @@ "optionRow": 1, "anchoredSurface": 3, "motion": 0 - } + }, + "layout": [] }, { "componentId": "menu", @@ -3813,7 +7993,8 @@ "optionRow": 1, "anchoredSurface": 3, "motion": 0 - } + }, + "layout": [] }, { "componentId": "menu", @@ -3841,7 +8022,8 @@ "optionRow": 1, "anchoredSurface": 3, "motion": 0 - } + }, + "layout": [] }, { "componentId": "menu", @@ -3869,7 +8051,8 @@ "optionRow": 1, "anchoredSurface": 3, "motion": 0 - } + }, + "layout": [] }, { "componentId": "menu", @@ -3897,7 +8080,8 @@ "optionRow": 1, "anchoredSurface": 3, "motion": 0 - } + }, + "layout": [] }, { "componentId": "menu", @@ -3925,7 +8109,8 @@ "optionRow": 1, "anchoredSurface": 3, "motion": 0 - } + }, + "layout": [] }, { "componentId": "pagination", @@ -3954,7 +8139,63 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [ + { + "target": "root", + "widths": [ + 280, + 420, + 320, + 768, + 1280 + ], + "heights": [ + 48 + ], + "estimatedInlinePx": 70.56, + "minimumInlinePx": 70.56, + "textBehavior": "single-line", + "requiredBlockPx": 44, + "widthChecks": [ + { + "width": 280, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 420, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + } + ], + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true + } + ] }, { "componentId": "popover", @@ -3981,7 +8222,8 @@ "optionRow": 0, "anchoredSurface": 3, "motion": 0 - } + }, + "layout": [] }, { "componentId": "popover", @@ -4008,7 +8250,8 @@ "optionRow": 0, "anchoredSurface": 3, "motion": 0 - } + }, + "layout": [] }, { "componentId": "popover", @@ -4035,7 +8278,8 @@ "optionRow": 0, "anchoredSurface": 3, "motion": 0 - } + }, + "layout": [] }, { "componentId": "progress", @@ -4063,7 +8307,70 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [ + { + "target": "track", + "widths": [ + 180, + 280, + 360, + 320, + 768, + 1280 + ], + "heights": [ + 12 + ], + "estimatedInlinePx": 0, + "minimumInlinePx": 0, + "textBehavior": "single-line", + "requiredBlockPx": 12, + "widthChecks": [ + { + "width": 180, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 280, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 360, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + } + ], + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true + } + ] }, { "componentId": "progress", @@ -4091,7 +8398,70 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [ + { + "target": "track", + "widths": [ + 180, + 280, + 360, + 320, + 768, + 1280 + ], + "heights": [ + 12 + ], + "estimatedInlinePx": 0, + "minimumInlinePx": 0, + "textBehavior": "single-line", + "requiredBlockPx": 10, + "widthChecks": [ + { + "width": 180, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 280, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 360, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + } + ], + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true + } + ] }, { "componentId": "progress", @@ -4119,7 +8489,70 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [ + { + "target": "track", + "widths": [ + 180, + 280, + 360, + 320, + 768, + 1280 + ], + "heights": [ + 12 + ], + "estimatedInlinePx": 0, + "minimumInlinePx": 0, + "textBehavior": "single-line", + "requiredBlockPx": 12, + "widthChecks": [ + { + "width": 180, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 280, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 360, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + } + ], + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true + } + ] }, { "componentId": "radio-group", @@ -4148,7 +8581,8 @@ "optionRow": 1, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [] }, { "componentId": "radio-group", @@ -4177,7 +8611,8 @@ "optionRow": 1, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [] }, { "componentId": "radio-group", @@ -4206,7 +8641,8 @@ "optionRow": 1, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [] }, { "componentId": "radio-group", @@ -4235,7 +8671,8 @@ "optionRow": 1, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [] }, { "componentId": "radio-group", @@ -4264,7 +8701,8 @@ "optionRow": 1, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [] }, { "componentId": "radio-group", @@ -4293,7 +8731,8 @@ "optionRow": 1, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [] }, { "componentId": "radio-group", @@ -4322,7 +8761,8 @@ "optionRow": 1, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [] }, { "componentId": "radio-group", @@ -4351,7 +8791,8 @@ "optionRow": 1, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [] }, { "componentId": "radio-group", @@ -4380,7 +8821,8 @@ "optionRow": 1, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [] }, { "componentId": "radio-group", @@ -4409,7 +8851,8 @@ "optionRow": 1, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [] }, { "componentId": "radio-group", @@ -4438,7 +8881,8 @@ "optionRow": 1, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [] }, { "componentId": "radio-group", @@ -4467,7 +8911,8 @@ "optionRow": 1, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [] }, { "componentId": "range-date-picker", @@ -4496,7 +8941,64 @@ "optionRow": 0, "anchoredSurface": 3, "motion": 0 - } + }, + "layout": [ + { + "target": "surface", + "widths": [ + 560, + 720, + 320, + 768, + 1280 + ], + "heights": [ + 360, + 420 + ], + "estimatedInlinePx": 0, + "minimumInlinePx": 0, + "textBehavior": "single-line", + "requiredBlockPx": 360, + "widthChecks": [ + { + "width": 560, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 720, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + } + ], + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true + } + ] }, { "componentId": "range-date-picker", @@ -4525,7 +9027,64 @@ "optionRow": 0, "anchoredSurface": 3, "motion": 0 - } + }, + "layout": [ + { + "target": "surface", + "widths": [ + 560, + 720, + 320, + 768, + 1280 + ], + "heights": [ + 360, + 420 + ], + "estimatedInlinePx": 0, + "minimumInlinePx": 0, + "textBehavior": "single-line", + "requiredBlockPx": 360, + "widthChecks": [ + { + "width": 560, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 720, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + } + ], + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true + } + ] }, { "componentId": "range-date-picker", @@ -4554,7 +9113,64 @@ "optionRow": 0, "anchoredSurface": 3, "motion": 0 - } + }, + "layout": [ + { + "target": "surface", + "widths": [ + 560, + 720, + 320, + 768, + 1280 + ], + "heights": [ + 360, + 420 + ], + "estimatedInlinePx": 0, + "minimumInlinePx": 0, + "textBehavior": "single-line", + "requiredBlockPx": 360, + "widthChecks": [ + { + "width": 560, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 720, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + } + ], + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true + } + ] }, { "componentId": "segmented-control", @@ -4581,7 +9197,8 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [] }, { "componentId": "segmented-control", @@ -4608,7 +9225,8 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [] }, { "componentId": "segmented-control", @@ -4635,7 +9253,8 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [] }, { "componentId": "segmented-control", @@ -4662,7 +9281,8 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [] }, { "componentId": "select", @@ -4691,7 +9311,8 @@ "optionRow": 1, "anchoredSurface": 3, "motion": 0 - } + }, + "layout": [] }, { "componentId": "select", @@ -4720,7 +9341,8 @@ "optionRow": 1, "anchoredSurface": 3, "motion": 0 - } + }, + "layout": [] }, { "componentId": "select", @@ -4749,7 +9371,8 @@ "optionRow": 1, "anchoredSurface": 3, "motion": 0 - } + }, + "layout": [] }, { "componentId": "select", @@ -4778,7 +9401,8 @@ "optionRow": 1, "anchoredSurface": 3, "motion": 0 - } + }, + "layout": [] }, { "componentId": "select", @@ -4807,7 +9431,8 @@ "optionRow": 1, "anchoredSurface": 3, "motion": 0 - } + }, + "layout": [] }, { "componentId": "select", @@ -4836,7 +9461,8 @@ "optionRow": 1, "anchoredSurface": 3, "motion": 0 - } + }, + "layout": [] }, { "componentId": "select", @@ -4865,7 +9491,8 @@ "optionRow": 1, "anchoredSurface": 3, "motion": 0 - } + }, + "layout": [] }, { "componentId": "select", @@ -4894,7 +9521,8 @@ "optionRow": 1, "anchoredSurface": 3, "motion": 0 - } + }, + "layout": [] }, { "componentId": "select", @@ -4923,7 +9551,8 @@ "optionRow": 1, "anchoredSurface": 3, "motion": 0 - } + }, + "layout": [] }, { "componentId": "side-nav", @@ -4949,7 +9578,57 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [ + { + "target": "root", + "widths": [ + 240, + 320, + 768, + 1280 + ], + "heights": [ + 320, + 480 + ], + "estimatedInlinePx": 0, + "minimumInlinePx": 0, + "textBehavior": "single-line", + "requiredBlockPx": 44, + "widthChecks": [ + { + "width": 240, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + } + ], + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true + } + ] }, { "componentId": "side-nav", @@ -4975,7 +9654,57 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [ + { + "target": "root", + "widths": [ + 240, + 320, + 768, + 1280 + ], + "heights": [ + 320, + 480 + ], + "estimatedInlinePx": 0, + "minimumInlinePx": 0, + "textBehavior": "single-line", + "requiredBlockPx": 44, + "widthChecks": [ + { + "width": 240, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + } + ], + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true + } + ] }, { "componentId": "skeleton", @@ -5002,7 +9731,72 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [ + { + "target": "root", + "widths": [ + 120, + 240, + 360, + 320, + 768, + 1280 + ], + "heights": [ + 16, + 128, + 56 + ], + "estimatedInlinePx": 0, + "minimumInlinePx": 0, + "textBehavior": "single-line", + "requiredBlockPx": 14, + "widthChecks": [ + { + "width": 120, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 240, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 360, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + } + ], + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true + } + ] }, { "componentId": "skeleton", @@ -5029,7 +9823,72 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [ + { + "target": "root", + "widths": [ + 120, + 240, + 360, + 320, + 768, + 1280 + ], + "heights": [ + 16, + 128, + 56 + ], + "estimatedInlinePx": 0, + "minimumInlinePx": 0, + "textBehavior": "single-line", + "requiredBlockPx": 128, + "widthChecks": [ + { + "width": 120, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 240, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 360, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + } + ], + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true + } + ] }, { "componentId": "skeleton", @@ -5056,7 +9915,72 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [ + { + "target": "root", + "widths": [ + 120, + 240, + 360, + 320, + 768, + 1280 + ], + "heights": [ + 16, + 128, + 56 + ], + "estimatedInlinePx": 0, + "minimumInlinePx": 0, + "textBehavior": "single-line", + "requiredBlockPx": 56, + "widthChecks": [ + { + "width": 120, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 240, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 360, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + } + ], + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true + } + ] }, { "componentId": "stepper", @@ -5084,7 +10008,8 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [] }, { "componentId": "stepper", @@ -5112,7 +10037,8 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [] }, { "componentId": "stepper", @@ -5140,7 +10066,8 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [] }, { "componentId": "switch", @@ -5168,7 +10095,8 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 1 - } + }, + "layout": [] }, { "componentId": "switch", @@ -5196,7 +10124,8 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 1 - } + }, + "layout": [] }, { "componentId": "switch", @@ -5224,7 +10153,8 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 1 - } + }, + "layout": [] }, { "componentId": "switch", @@ -5252,7 +10182,8 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 1 - } + }, + "layout": [] }, { "componentId": "switch", @@ -5280,7 +10211,8 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 1 - } + }, + "layout": [] }, { "componentId": "switch", @@ -5308,7 +10240,8 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 1 - } + }, + "layout": [] }, { "componentId": "table", @@ -5336,7 +10269,64 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [ + { + "target": "shell", + "widths": [ + 320, + 480, + 760, + 768, + 1280 + ], + "heights": [ + 240, + 360 + ], + "estimatedInlinePx": 0, + "minimumInlinePx": 0, + "textBehavior": "single-line", + "requiredBlockPx": 240, + "widthChecks": [ + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 480, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 760, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + } + ], + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true + } + ] }, { "componentId": "table", @@ -5364,7 +10354,64 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [ + { + "target": "shell", + "widths": [ + 320, + 480, + 760, + 768, + 1280 + ], + "heights": [ + 240, + 360 + ], + "estimatedInlinePx": 0, + "minimumInlinePx": 0, + "textBehavior": "single-line", + "requiredBlockPx": 240, + "widthChecks": [ + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 480, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 760, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + } + ], + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true + } + ] }, { "componentId": "table", @@ -5392,7 +10439,64 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [ + { + "target": "shell", + "widths": [ + 320, + 480, + 760, + 768, + 1280 + ], + "heights": [ + 240, + 360 + ], + "estimatedInlinePx": 0, + "minimumInlinePx": 0, + "textBehavior": "single-line", + "requiredBlockPx": 240, + "widthChecks": [ + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 480, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 760, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + } + ], + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true + } + ] }, { "componentId": "tabs", @@ -5420,7 +10524,8 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [] }, { "componentId": "tabs", @@ -5448,7 +10553,8 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [] }, { "componentId": "tabs", @@ -5476,7 +10582,8 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [] }, { "componentId": "tabs", @@ -5504,7 +10611,8 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [] }, { "componentId": "tabs", @@ -5532,7 +10640,8 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [] }, { "componentId": "tabs", @@ -5560,7 +10669,8 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [] }, { "componentId": "tabs", @@ -5588,7 +10698,8 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [] }, { "componentId": "tabs", @@ -5616,7 +10727,8 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [] }, { "componentId": "tabs", @@ -5644,7 +10756,8 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [] }, { "componentId": "tabs", @@ -5672,7 +10785,8 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [] }, { "componentId": "tabs", @@ -5700,7 +10814,8 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [] }, { "componentId": "tabs", @@ -5728,7 +10843,8 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [] }, { "componentId": "text-field", @@ -5758,7 +10874,86 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [ + { + "target": "field", + "widths": [ + 240, + 320, + 420, + 768, + 1280 + ], + "heights": [ + 44, + 48, + 52 + ], + "estimatedInlinePx": 209.24, + "minimumInlinePx": 44.96, + "textBehavior": "scroll", + "requiredBlockPx": 44, + "widthChecks": [ + { + "width": 240, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 209.98, + "estimatedLineCount": 1, + "estimatedBlockPx": 44, + "fitsLines": true, + "pass": true + }, + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 289.98, + "estimatedLineCount": 1, + "estimatedBlockPx": 44, + "fitsLines": true, + "pass": true + }, + { + "width": 420, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 389.98, + "estimatedLineCount": 1, + "estimatedBlockPx": 44, + "fitsLines": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 737.98, + "estimatedLineCount": 1, + "estimatedBlockPx": 44, + "fitsLines": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 1249.98, + "estimatedLineCount": 1, + "estimatedBlockPx": 44, + "fitsLines": true, + "pass": true + } + ], + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "The native text viewport scrolls horizontally; full text visibility is not verified.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true + } + ] }, { "componentId": "text-field", @@ -5788,7 +10983,86 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [ + { + "target": "field", + "widths": [ + 240, + 320, + 420, + 768, + 1280 + ], + "heights": [ + 44, + 48, + 52 + ], + "estimatedInlinePx": 187.74, + "minimumInlinePx": 64.54, + "textBehavior": "scroll", + "requiredBlockPx": 44, + "widthChecks": [ + { + "width": 240, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 186.66, + "estimatedLineCount": 1, + "estimatedBlockPx": 44, + "fitsLines": true, + "pass": true + }, + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 266.66, + "estimatedLineCount": 1, + "estimatedBlockPx": 44, + "fitsLines": true, + "pass": true + }, + { + "width": 420, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 366.66, + "estimatedLineCount": 1, + "estimatedBlockPx": 44, + "fitsLines": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 714.66, + "estimatedLineCount": 1, + "estimatedBlockPx": 44, + "fitsLines": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 1226.66, + "estimatedLineCount": 1, + "estimatedBlockPx": 44, + "fitsLines": true, + "pass": true + } + ], + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "The native text viewport scrolls horizontally; full text visibility is not verified.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true + } + ] }, { "componentId": "text-field", @@ -5818,7 +11092,86 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [ + { + "target": "field", + "widths": [ + 240, + 320, + 420, + 768, + 1280 + ], + "heights": [ + 44, + 48, + 52 + ], + "estimatedInlinePx": 232.56, + "minimumInlinePx": 68.28, + "textBehavior": "scroll", + "requiredBlockPx": 48, + "widthChecks": [ + { + "width": 240, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 186.66, + "estimatedLineCount": 1, + "estimatedBlockPx": 48, + "fitsLines": true, + "pass": true + }, + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 266.66, + "estimatedLineCount": 1, + "estimatedBlockPx": 48, + "fitsLines": true, + "pass": true + }, + { + "width": 420, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 366.66, + "estimatedLineCount": 1, + "estimatedBlockPx": 48, + "fitsLines": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 714.66, + "estimatedLineCount": 1, + "estimatedBlockPx": 48, + "fitsLines": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 1226.66, + "estimatedLineCount": 1, + "estimatedBlockPx": 48, + "fitsLines": true, + "pass": true + } + ], + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "The native text viewport scrolls horizontally; full text visibility is not verified.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true + } + ] }, { "componentId": "text-field", @@ -5848,7 +11201,86 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [ + { + "target": "field", + "widths": [ + 240, + 320, + 420, + 768, + 1280 + ], + "heights": [ + 44, + 48, + 52 + ], + "estimatedInlinePx": 310, + "minimumInlinePx": 91.01, + "textBehavior": "scroll", + "requiredBlockPx": 52, + "widthChecks": [ + { + "width": 240, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 168.9, + "estimatedLineCount": 1, + "estimatedBlockPx": 52, + "fitsLines": true, + "pass": true + }, + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 248.9, + "estimatedLineCount": 1, + "estimatedBlockPx": 52, + "fitsLines": true, + "pass": true + }, + { + "width": 420, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 348.9, + "estimatedLineCount": 1, + "estimatedBlockPx": 52, + "fitsLines": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 696.9, + "estimatedLineCount": 1, + "estimatedBlockPx": 52, + "fitsLines": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 1208.9, + "estimatedLineCount": 1, + "estimatedBlockPx": 52, + "fitsLines": true, + "pass": true + } + ], + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "The native text viewport scrolls horizontally; full text visibility is not verified.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true + } + ] }, { "componentId": "text-field", @@ -5878,7 +11310,86 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [ + { + "target": "field", + "widths": [ + 240, + 320, + 420, + 768, + 1280 + ], + "heights": [ + 44, + 48, + 52 + ], + "estimatedInlinePx": 232.56, + "minimumInlinePx": 68.28, + "textBehavior": "scroll", + "requiredBlockPx": 48, + "widthChecks": [ + { + "width": 240, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 186.66, + "estimatedLineCount": 1, + "estimatedBlockPx": 48, + "fitsLines": true, + "pass": true + }, + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 266.66, + "estimatedLineCount": 1, + "estimatedBlockPx": 48, + "fitsLines": true, + "pass": true + }, + { + "width": 420, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 366.66, + "estimatedLineCount": 1, + "estimatedBlockPx": 48, + "fitsLines": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 714.66, + "estimatedLineCount": 1, + "estimatedBlockPx": 48, + "fitsLines": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 1226.66, + "estimatedLineCount": 1, + "estimatedBlockPx": 48, + "fitsLines": true, + "pass": true + } + ], + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "The native text viewport scrolls horizontally; full text visibility is not verified.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true + } + ] }, { "componentId": "text-field", @@ -5908,7 +11419,86 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [ + { + "target": "field", + "widths": [ + 240, + 320, + 420, + 768, + 1280 + ], + "heights": [ + 44, + 48, + 52 + ], + "estimatedInlinePx": 187.74, + "minimumInlinePx": 64.54, + "textBehavior": "scroll", + "requiredBlockPx": 44, + "widthChecks": [ + { + "width": 240, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 186.66, + "estimatedLineCount": 1, + "estimatedBlockPx": 44, + "fitsLines": true, + "pass": true + }, + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 266.66, + "estimatedLineCount": 1, + "estimatedBlockPx": 44, + "fitsLines": true, + "pass": true + }, + { + "width": 420, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 366.66, + "estimatedLineCount": 1, + "estimatedBlockPx": 44, + "fitsLines": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 714.66, + "estimatedLineCount": 1, + "estimatedBlockPx": 44, + "fitsLines": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "availableTextPx": 1226.66, + "estimatedLineCount": 1, + "estimatedBlockPx": 44, + "fitsLines": true, + "pass": true + } + ], + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "The native text viewport scrolls horizontally; full text visibility is not verified.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true + } + ] }, { "componentId": "textarea", @@ -5938,7 +11528,72 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [ + { + "target": "field", + "widths": [ + 260, + 340, + 440, + 320, + 768, + 1280 + ], + "heights": [ + 120, + 136, + 160 + ], + "estimatedInlinePx": 512.54, + "minimumInlinePx": 512.54, + "textBehavior": "single-line", + "requiredBlockPx": 120, + "widthChecks": [ + { + "width": 260, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 340, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 440, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + } + ], + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true + } + ] }, { "componentId": "textarea", @@ -5968,7 +11623,72 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [ + { + "target": "field", + "widths": [ + 260, + 340, + 440, + 320, + 768, + 1280 + ], + "heights": [ + 120, + 136, + 160 + ], + "estimatedInlinePx": 665.68, + "minimumInlinePx": 665.68, + "textBehavior": "single-line", + "requiredBlockPx": 136, + "widthChecks": [ + { + "width": 260, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 340, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 440, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + } + ], + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true + } + ] }, { "componentId": "textarea", @@ -5998,7 +11718,72 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [ + { + "target": "field", + "widths": [ + 260, + 340, + 440, + 320, + 768, + 1280 + ], + "heights": [ + 120, + 136, + 160 + ], + "estimatedInlinePx": 887.33, + "minimumInlinePx": 887.33, + "textBehavior": "single-line", + "requiredBlockPx": 160, + "widthChecks": [ + { + "width": 260, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 340, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 440, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + } + ], + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true + } + ] }, { "componentId": "textarea", @@ -6028,7 +11813,72 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [ + { + "target": "field", + "widths": [ + 260, + 340, + 440, + 320, + 768, + 1280 + ], + "heights": [ + 120, + 136, + 160 + ], + "estimatedInlinePx": 512.54, + "minimumInlinePx": 512.54, + "textBehavior": "single-line", + "requiredBlockPx": 120, + "widthChecks": [ + { + "width": 260, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 340, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 440, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + } + ], + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true + } + ] }, { "componentId": "textarea", @@ -6058,7 +11908,72 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [ + { + "target": "field", + "widths": [ + 260, + 340, + 440, + 320, + 768, + 1280 + ], + "heights": [ + 120, + 136, + 160 + ], + "estimatedInlinePx": 665.68, + "minimumInlinePx": 665.68, + "textBehavior": "single-line", + "requiredBlockPx": 136, + "widthChecks": [ + { + "width": 260, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 340, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 440, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + } + ], + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true + } + ] }, { "componentId": "textarea", @@ -6088,7 +12003,72 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [ + { + "target": "field", + "widths": [ + 260, + 340, + 440, + 320, + 768, + 1280 + ], + "heights": [ + 120, + 136, + 160 + ], + "estimatedInlinePx": 887.33, + "minimumInlinePx": 887.33, + "textBehavior": "single-line", + "requiredBlockPx": 160, + "widthChecks": [ + { + "width": 260, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 340, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 440, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + } + ], + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true + } + ] }, { "componentId": "toast", @@ -6117,7 +12097,8 @@ "optionRow": 0, "anchoredSurface": 3, "motion": 1 - } + }, + "layout": [] }, { "componentId": "toast", @@ -6146,7 +12127,8 @@ "optionRow": 0, "anchoredSurface": 3, "motion": 1 - } + }, + "layout": [] }, { "componentId": "tooltip", @@ -6173,7 +12155,8 @@ "optionRow": 0, "anchoredSurface": 3, "motion": 1 - } + }, + "layout": [] }, { "componentId": "tree-view", @@ -6199,7 +12182,57 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [ + { + "target": "root", + "widths": [ + 240, + 320, + 768, + 1280 + ], + "heights": [ + 240, + 420 + ], + "estimatedInlinePx": 0, + "minimumInlinePx": 0, + "textBehavior": "single-line", + "requiredBlockPx": 44, + "widthChecks": [ + { + "width": 240, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + } + ], + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true + } + ] }, { "componentId": "tree-view", @@ -6225,7 +12258,57 @@ "optionRow": 0, "anchoredSurface": 0, "motion": 0 - } + }, + "layout": [ + { + "target": "root", + "widths": [ + 240, + 320, + 768, + 1280 + ], + "heights": [ + 240, + 420 + ], + "estimatedInlinePx": 0, + "minimumInlinePx": 0, + "textBehavior": "single-line", + "requiredBlockPx": 44, + "widthChecks": [ + { + "width": 240, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 320, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 768, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + }, + { + "width": 1280, + "fitsWidth": true, + "fitsHeight": true, + "pass": true + } + ], + "assumptions": [ + "The full single-line estimate uses a 0.56em glyph advance and maximum length token values.", + "Font shaping, vertical padding, and rendered container geometry are not measured." + ], + "pass": true + } + ] } ], "runs": [ @@ -6236,16 +12319,8 @@ "themeName": "Browser test", "caseCount": 2, "fixtureCount": 2, - "pass": false, - "failures": [ - { - "fixtureName": "Browser test / Accordion / accordion-md-open", - "caseKey": "size=md", - "reasons": [ - "layout overflow at 320px" - ] - } - ] + "pass": true, + "failures": [] }, { "slug": "alert", @@ -6274,30 +12349,8 @@ "themeName": "Browser test", "caseCount": 30, "fixtureCount": 15, - "pass": false, - "failures": [ - { - "fixtureName": "Browser test / Badge / success-soft", - "caseKey": "emphasis=soft|size=md|tone=success", - "reasons": [ - "layout overflow at 120px" - ] - }, - { - "fixtureName": "Browser test / Badge / success-solid", - "caseKey": "emphasis=solid|size=md|tone=success", - "reasons": [ - "layout overflow at 120px" - ] - }, - { - "fixtureName": "Browser test / Badge / success-outline", - "caseKey": "emphasis=outline|size=md|tone=success", - "reasons": [ - "layout overflow at 120px" - ] - } - ] + "pass": true, + "failures": [] }, { "slug": "breadcrumbs", @@ -6306,16 +12359,8 @@ "themeName": "Browser test", "caseCount": 2, "fixtureCount": 1, - "pass": false, - "failures": [ - { - "fixtureName": "Browser test / Breadcrumbs / breadcrumbs-md", - "caseKey": "size=md", - "reasons": [ - "layout overflow at 220px" - ] - } - ] + "pass": true, + "failures": [] }, { "slug": "button", @@ -6324,23 +12369,8 @@ "themeName": "Browser test", "caseCount": 120, "fixtureCount": 13, - "pass": false, - "failures": [ - { - "fixtureName": "Browser test / Button / solid-sizes (content=label|size=lg|variant=solid)", - "caseKey": "content=label|size=lg|variant=solid", - "reasons": [ - "layout overflow at 180px" - ] - }, - { - "fixtureName": "Browser test / Button / link-anchor", - "caseKey": "content=label|size=md|variant=link", - "reasons": [ - "layout overflow at 180px" - ] - } - ] + "pass": true, + "failures": [] }, { "slug": "card", @@ -6359,72 +12389,8 @@ "themeName": "Browser test", "caseCount": 3, "fixtureCount": 9, - "pass": false, - "failures": [ - { - "fixtureName": "Browser test / Checkbox / default (size=sm)", - "caseKey": "size=sm", - "reasons": [ - "layout overflow at 240px" - ] - }, - { - "fixtureName": "Browser test / Checkbox / default (size=md)", - "caseKey": "size=md", - "reasons": [ - "layout overflow at 240px" - ] - }, - { - "fixtureName": "Browser test / Checkbox / default (size=lg)", - "caseKey": "size=lg", - "reasons": [ - "layout overflow at 240px" - ] - }, - { - "fixtureName": "Browser test / Checkbox / checked (size=sm)", - "caseKey": "size=sm", - "reasons": [ - "layout overflow at 240px" - ] - }, - { - "fixtureName": "Browser test / Checkbox / checked (size=md)", - "caseKey": "size=md", - "reasons": [ - "layout overflow at 240px" - ] - }, - { - "fixtureName": "Browser test / Checkbox / checked (size=lg)", - "caseKey": "size=lg", - "reasons": [ - "layout overflow at 240px" - ] - }, - { - "fixtureName": "Browser test / Checkbox / indeterminate (size=sm)", - "caseKey": "size=sm", - "reasons": [ - "layout overflow at 240px" - ] - }, - { - "fixtureName": "Browser test / Checkbox / indeterminate (size=md)", - "caseKey": "size=md", - "reasons": [ - "layout overflow at 240px" - ] - }, - { - "fixtureName": "Browser test / Checkbox / indeterminate (size=lg)", - "caseKey": "size=lg", - "reasons": [ - "layout overflow at 240px" - ] - } - ] + "pass": true, + "failures": [] }, { "slug": "chip", @@ -6683,16 +12649,8 @@ "themeName": "Browser test", "caseCount": 4, "fixtureCount": 6, - "pass": false, - "failures": [ - { - "fixtureName": "Browser test / TextField / sizes (size=lg)", - "caseKey": "size=lg", - "reasons": [ - "layout overflow at 240px" - ] - } - ] + "pass": true, + "failures": [] }, { "slug": "textarea", @@ -6739,10 +12697,10 @@ "componentCount": 38, "themeCount": 1, "runCount": 38, - "passedRuns": 32, + "passedRuns": 38, "fixtureCount": 180, - "failedFixtureCount": 17, - "pass": false + "failedFixtureCount": 0, + "pass": true } }, "matrix": { @@ -6800,10 +12758,10 @@ "componentCount": 38, "themeCount": 4, "runCount": 152, - "passedRuns": 132, + "passedRuns": 152, "fixtureCount": 720, - "failedFixtureCount": 45, - "pass": false + "failedFixtureCount": 0, + "pass": true }, "runs": [ { @@ -6813,16 +12771,8 @@ "themeName": "Cobalt", "caseCount": 2, "fixtureCount": 2, - "pass": false, - "failures": [ - { - "fixtureName": "Cobalt / Accordion / accordion-md-open", - "caseKey": "size=md", - "reasons": [ - "layout overflow at 320px" - ] - } - ] + "pass": true, + "failures": [] }, { "slug": "alert", @@ -6851,30 +12801,8 @@ "themeName": "Cobalt", "caseCount": 30, "fixtureCount": 15, - "pass": false, - "failures": [ - { - "fixtureName": "Cobalt / Badge / success-soft", - "caseKey": "emphasis=soft|size=md|tone=success", - "reasons": [ - "layout overflow at 120px" - ] - }, - { - "fixtureName": "Cobalt / Badge / success-solid", - "caseKey": "emphasis=solid|size=md|tone=success", - "reasons": [ - "layout overflow at 120px" - ] - }, - { - "fixtureName": "Cobalt / Badge / success-outline", - "caseKey": "emphasis=outline|size=md|tone=success", - "reasons": [ - "layout overflow at 120px" - ] - } - ] + "pass": true, + "failures": [] }, { "slug": "breadcrumbs", @@ -6883,16 +12811,8 @@ "themeName": "Cobalt", "caseCount": 2, "fixtureCount": 1, - "pass": false, - "failures": [ - { - "fixtureName": "Cobalt / Breadcrumbs / breadcrumbs-md", - "caseKey": "size=md", - "reasons": [ - "layout overflow at 220px" - ] - } - ] + "pass": true, + "failures": [] }, { "slug": "button", @@ -6901,23 +12821,8 @@ "themeName": "Cobalt", "caseCount": 120, "fixtureCount": 13, - "pass": false, - "failures": [ - { - "fixtureName": "Cobalt / Button / solid-sizes (content=label|size=lg|variant=solid)", - "caseKey": "content=label|size=lg|variant=solid", - "reasons": [ - "layout overflow at 180px" - ] - }, - { - "fixtureName": "Cobalt / Button / link-anchor", - "caseKey": "content=label|size=md|variant=link", - "reasons": [ - "layout overflow at 180px" - ] - } - ] + "pass": true, + "failures": [] }, { "slug": "card", @@ -6936,72 +12841,8 @@ "themeName": "Cobalt", "caseCount": 3, "fixtureCount": 9, - "pass": false, - "failures": [ - { - "fixtureName": "Cobalt / Checkbox / default (size=sm)", - "caseKey": "size=sm", - "reasons": [ - "layout overflow at 240px" - ] - }, - { - "fixtureName": "Cobalt / Checkbox / default (size=md)", - "caseKey": "size=md", - "reasons": [ - "layout overflow at 240px" - ] - }, - { - "fixtureName": "Cobalt / Checkbox / default (size=lg)", - "caseKey": "size=lg", - "reasons": [ - "layout overflow at 240px" - ] - }, - { - "fixtureName": "Cobalt / Checkbox / checked (size=sm)", - "caseKey": "size=sm", - "reasons": [ - "layout overflow at 240px" - ] - }, - { - "fixtureName": "Cobalt / Checkbox / checked (size=md)", - "caseKey": "size=md", - "reasons": [ - "layout overflow at 240px" - ] - }, - { - "fixtureName": "Cobalt / Checkbox / checked (size=lg)", - "caseKey": "size=lg", - "reasons": [ - "layout overflow at 240px" - ] - }, - { - "fixtureName": "Cobalt / Checkbox / indeterminate (size=sm)", - "caseKey": "size=sm", - "reasons": [ - "layout overflow at 240px" - ] - }, - { - "fixtureName": "Cobalt / Checkbox / indeterminate (size=md)", - "caseKey": "size=md", - "reasons": [ - "layout overflow at 240px" - ] - }, - { - "fixtureName": "Cobalt / Checkbox / indeterminate (size=lg)", - "caseKey": "size=lg", - "reasons": [ - "layout overflow at 240px" - ] - } - ] + "pass": true, + "failures": [] }, { "slug": "chip", @@ -7260,16 +13101,8 @@ "themeName": "Cobalt", "caseCount": 4, "fixtureCount": 6, - "pass": false, - "failures": [ - { - "fixtureName": "Cobalt / TextField / sizes (size=lg)", - "caseKey": "size=lg", - "reasons": [ - "layout overflow at 240px" - ] - } - ] + "pass": true, + "failures": [] }, { "slug": "textarea", @@ -7318,16 +13151,8 @@ "themeName": "Sage", "caseCount": 2, "fixtureCount": 2, - "pass": false, - "failures": [ - { - "fixtureName": "Sage / Accordion / accordion-md-open", - "caseKey": "size=md", - "reasons": [ - "layout overflow at 320px" - ] - } - ] + "pass": true, + "failures": [] }, { "slug": "alert", @@ -7366,16 +13191,8 @@ "themeName": "Sage", "caseCount": 2, "fixtureCount": 1, - "pass": false, - "failures": [ - { - "fixtureName": "Sage / Breadcrumbs / breadcrumbs-md", - "caseKey": "size=md", - "reasons": [ - "layout overflow at 220px" - ] - } - ] + "pass": true, + "failures": [] }, { "slug": "button", @@ -7384,23 +13201,8 @@ "themeName": "Sage", "caseCount": 120, "fixtureCount": 13, - "pass": false, - "failures": [ - { - "fixtureName": "Sage / Button / solid-sizes (content=label|size=lg|variant=solid)", - "caseKey": "content=label|size=lg|variant=solid", - "reasons": [ - "layout overflow at 180px" - ] - }, - { - "fixtureName": "Sage / Button / link-anchor", - "caseKey": "content=label|size=md|variant=link", - "reasons": [ - "layout overflow at 180px" - ] - } - ] + "pass": true, + "failures": [] }, { "slug": "card", @@ -7679,16 +13481,8 @@ "themeName": "Sage", "caseCount": 4, "fixtureCount": 6, - "pass": false, - "failures": [ - { - "fixtureName": "Sage / TextField / sizes (size=lg)", - "caseKey": "size=lg", - "reasons": [ - "layout overflow at 240px" - ] - } - ] + "pass": true, + "failures": [] }, { "slug": "textarea", @@ -7737,16 +13531,8 @@ "themeName": "Ember", "caseCount": 2, "fixtureCount": 2, - "pass": false, - "failures": [ - { - "fixtureName": "Ember / Accordion / accordion-md-open", - "caseKey": "size=md", - "reasons": [ - "layout overflow at 320px" - ] - } - ] + "pass": true, + "failures": [] }, { "slug": "alert", @@ -7775,30 +13561,8 @@ "themeName": "Ember", "caseCount": 30, "fixtureCount": 15, - "pass": false, - "failures": [ - { - "fixtureName": "Ember / Badge / success-soft", - "caseKey": "emphasis=soft|size=md|tone=success", - "reasons": [ - "layout overflow at 120px" - ] - }, - { - "fixtureName": "Ember / Badge / success-solid", - "caseKey": "emphasis=solid|size=md|tone=success", - "reasons": [ - "layout overflow at 120px" - ] - }, - { - "fixtureName": "Ember / Badge / success-outline", - "caseKey": "emphasis=outline|size=md|tone=success", - "reasons": [ - "layout overflow at 120px" - ] - } - ] + "pass": true, + "failures": [] }, { "slug": "breadcrumbs", @@ -7807,16 +13571,8 @@ "themeName": "Ember", "caseCount": 2, "fixtureCount": 1, - "pass": false, - "failures": [ - { - "fixtureName": "Ember / Breadcrumbs / breadcrumbs-md", - "caseKey": "size=md", - "reasons": [ - "layout overflow at 220px" - ] - } - ] + "pass": true, + "failures": [] }, { "slug": "button", @@ -7825,23 +13581,8 @@ "themeName": "Ember", "caseCount": 120, "fixtureCount": 13, - "pass": false, - "failures": [ - { - "fixtureName": "Ember / Button / solid-sizes (content=label|size=lg|variant=solid)", - "caseKey": "content=label|size=lg|variant=solid", - "reasons": [ - "layout overflow at 180px" - ] - }, - { - "fixtureName": "Ember / Button / link-anchor", - "caseKey": "content=label|size=md|variant=link", - "reasons": [ - "layout overflow at 180px" - ] - } - ] + "pass": true, + "failures": [] }, { "slug": "card", @@ -7860,72 +13601,8 @@ "themeName": "Ember", "caseCount": 3, "fixtureCount": 9, - "pass": false, - "failures": [ - { - "fixtureName": "Ember / Checkbox / default (size=sm)", - "caseKey": "size=sm", - "reasons": [ - "layout overflow at 240px" - ] - }, - { - "fixtureName": "Ember / Checkbox / default (size=md)", - "caseKey": "size=md", - "reasons": [ - "layout overflow at 240px" - ] - }, - { - "fixtureName": "Ember / Checkbox / default (size=lg)", - "caseKey": "size=lg", - "reasons": [ - "layout overflow at 240px" - ] - }, - { - "fixtureName": "Ember / Checkbox / checked (size=sm)", - "caseKey": "size=sm", - "reasons": [ - "layout overflow at 240px" - ] - }, - { - "fixtureName": "Ember / Checkbox / checked (size=md)", - "caseKey": "size=md", - "reasons": [ - "layout overflow at 240px" - ] - }, - { - "fixtureName": "Ember / Checkbox / checked (size=lg)", - "caseKey": "size=lg", - "reasons": [ - "layout overflow at 240px" - ] - }, - { - "fixtureName": "Ember / Checkbox / indeterminate (size=sm)", - "caseKey": "size=sm", - "reasons": [ - "layout overflow at 240px" - ] - }, - { - "fixtureName": "Ember / Checkbox / indeterminate (size=md)", - "caseKey": "size=md", - "reasons": [ - "layout overflow at 240px" - ] - }, - { - "fixtureName": "Ember / Checkbox / indeterminate (size=lg)", - "caseKey": "size=lg", - "reasons": [ - "layout overflow at 240px" - ] - } - ] + "pass": true, + "failures": [] }, { "slug": "chip", @@ -8184,16 +13861,8 @@ "themeName": "Ember", "caseCount": 4, "fixtureCount": 6, - "pass": false, - "failures": [ - { - "fixtureName": "Ember / TextField / sizes (size=lg)", - "caseKey": "size=lg", - "reasons": [ - "layout overflow at 240px" - ] - } - ] + "pass": true, + "failures": [] }, { "slug": "textarea", @@ -8242,16 +13911,8 @@ "themeName": "Linen", "caseCount": 2, "fixtureCount": 2, - "pass": false, - "failures": [ - { - "fixtureName": "Linen / Accordion / accordion-md-open", - "caseKey": "size=md", - "reasons": [ - "layout overflow at 320px" - ] - } - ] + "pass": true, + "failures": [] }, { "slug": "alert", @@ -8280,30 +13941,8 @@ "themeName": "Linen", "caseCount": 30, "fixtureCount": 15, - "pass": false, - "failures": [ - { - "fixtureName": "Linen / Badge / success-soft", - "caseKey": "emphasis=soft|size=md|tone=success", - "reasons": [ - "layout overflow at 120px" - ] - }, - { - "fixtureName": "Linen / Badge / success-solid", - "caseKey": "emphasis=solid|size=md|tone=success", - "reasons": [ - "layout overflow at 120px" - ] - }, - { - "fixtureName": "Linen / Badge / success-outline", - "caseKey": "emphasis=outline|size=md|tone=success", - "reasons": [ - "layout overflow at 120px" - ] - } - ] + "pass": true, + "failures": [] }, { "slug": "breadcrumbs", @@ -8312,16 +13951,8 @@ "themeName": "Linen", "caseCount": 2, "fixtureCount": 1, - "pass": false, - "failures": [ - { - "fixtureName": "Linen / Breadcrumbs / breadcrumbs-md", - "caseKey": "size=md", - "reasons": [ - "layout overflow at 220px" - ] - } - ] + "pass": true, + "failures": [] }, { "slug": "button", @@ -8330,16 +13961,8 @@ "themeName": "Linen", "caseCount": 120, "fixtureCount": 13, - "pass": false, - "failures": [ - { - "fixtureName": "Linen / Button / link-anchor", - "caseKey": "content=label|size=md|variant=link", - "reasons": [ - "layout overflow at 180px" - ] - } - ] + "pass": true, + "failures": [] }, { "slug": "card", diff --git a/scripts/assess-release-math.mjs b/scripts/assess-release-math.mjs index 0216bcf..fb45df8 100644 --- a/scripts/assess-release-math.mjs +++ b/scripts/assess-release-math.mjs @@ -28,9 +28,6 @@ function same(actual, expected, message) { } function identity(fixture) { return Object.fromEntries(identityFields.filter((key) => fixture[key] !== undefined).map((key) => [key, fixture[key]])); } function key(fixture) { return `${fixture.componentId}:${fixture.id}`; } -function layoutSignature(proof) { - return Object.fromEntries(['target', 'widths', 'heights', 'estimatedInlinePx', 'requiredBlockPx', 'widthChecks', 'pass'].map((key) => [key, proof[key]])); -} /** Checks the exact reviewed project findings without changing mathematical verdicts. */ export function assertReviewedProjectMath(value, policy) { @@ -42,10 +39,8 @@ export function assertReviewedProjectMath(value, policy) { const inventory = array(expected.fixtureInventory, 'reviewed inventory'); const reviewed = array(expected.reviewedFailures, 'reviewed failures'); const expectedById = new Map(inventory.map((fixture) => [key(fixture), fixture])); - const failuresById = new Map(reviewed.map((fixture) => [key(fixture), fixture])); - if (expectedById.size !== 180 || failuresById.size !== 17 || fixtures.length !== 180) throw new Error('Preserve the reviewed 180-fixture inventory and 17 findings.'); + if (expectedById.size !== 180 || reviewed.length !== 0 || fixtures.length !== 180) throw new Error('Preserve the reviewed 180-fixture inventory without new findings.'); const observed = new Set(); - const observedFailures = new Set(); for (const value of fixtures) { const fixture = object(value, 'fixture'); const id = key(fixture); @@ -60,19 +55,13 @@ export function assertReviewedProjectMath(value, policy) { for (const category of categories) { const proofs = array(fixture[category], `${id} ${category}`); if (proofs.length !== baseline.proofCounts[category]) throw new Error(`Proof count changed for ${id} ${category}.`); - if (category !== 'layout' && proofs.some((proof) => proof.pass !== true)) throw new Error(`Unreviewed ${category} failure for ${id}.`); + if (proofs.some((proof) => proof.pass !== true)) throw new Error(`Unreviewed ${category} failure for ${id}.`); } - const reviewedFailure = failuresById.get(id); - const failedLayout = fixture.layout.filter((proof) => proof.pass !== true); - if (reviewedFailure) { - same(failedLayout.map(layoutSignature), reviewedFailure.layout.map(layoutSignature), `Reviewed layout checks changed for ${id}.`); - if (fixture.pass !== false) throw new Error(`Reviewed finding ${id} must remain a mathematical failure.`); - observedFailures.add(id); - } else if (failedLayout.length || fixture.pass !== true) throw new Error(`Unreviewed mathematical failure ${id}.`); + same(fixture.layout, baseline.layout, `Reviewed layout checks changed for ${id}.`); + if (fixture.pass !== true) throw new Error(`Unreviewed mathematical failure ${id}.`); } - same([...observedFailures].sort(), [...failuresById.keys()].sort(), 'Reviewed failure identities changed.'); const summary = object(report.summary, 'project summary'); - same(summary, expected.summary, 'Project totals must retain 180 fixtures, 17 findings, and a failed mathematical verdict.'); + same(summary, expected.summary, 'Project totals must retain 180 fixtures and the reviewed mathematical verdict.'); const expectedRuns = new Map(expected.runs.map((run) => [run.slug, run])); const runs = array(report.runs, 'project runs'); const runIds = new Set(); @@ -82,7 +71,7 @@ export function assertReviewedProjectMath(value, policy) { runIds.add(run.slug); same(run, expectedRuns.get(run.slug), `Reviewed project verdicts changed for ${run.slug}.`); } - return { fixtureCount: fixtures.length, reviewedFailureCount: observedFailures.size, mathematicalPass: false }; + return { fixtureCount: fixtures.length, reviewedFailureCount: summary.failedFixtureCount, mathematicalPass: summary.pass }; } /** Preserves the exact reviewed component-theme failures, including their check reasons. */ @@ -101,15 +90,21 @@ export function assertReviewedMatrix(value, policy) { seen.add(id); same(run, byId.get(id), `Reviewed matrix verdicts changed for ${id}.`); } - return { fixtureCount: 720, reviewedFailureCount: 45, mathematicalPass: false }; + return { fixtureCount: report.summary.fixtureCount, reviewedFailureCount: report.summary.failedFixtureCount, mathematicalPass: report.summary.pass }; } function strictArtifact(executable, args, cwd) { const env = { ...process.env }; delete env.NODE_PATH; const result = spawnSync(process.execPath, [executable, ...args, '--json', '--strict'], { cwd, env, encoding: 'utf8', timeout: 90_000, maxBuffer: 8 * 1024 * 1024 }); - if (result.status !== 1 || !/strict verification failed/.test(result.stderr)) throw new Error(`Strict mathematical verification must fail after emitting its artifact: ${result.error?.message ?? result.stderr}`); - return object(JSON.parse(result.stdout), 'strict artifact'); + if (result.error) throw new Error(`Strict mathematical verification did not finish: ${result.error.message}`); + const report = object(JSON.parse(result.stdout), 'strict artifact'); + const summary = object(report.summary, 'strict artifact summary'); + if (typeof summary.pass !== 'boolean') throw new Error('Strict artifact requires a mathematical pass verdict.'); + const expectedStatus = summary.pass ? 0 : 1; + const strictFailure = /strict verification failed/.test(result.stderr); + if (result.status !== expectedStatus || strictFailure === summary.pass) throw new Error('Strict exit status disagrees with the mathematical verdict.'); + return report; } export function assessReleaseMath(repositoryRoot = root) { diff --git a/scripts/assess-release-math.test.mjs b/scripts/assess-release-math.test.mjs new file mode 100644 index 0000000..b6159c0 --- /dev/null +++ b/scripts/assess-release-math.test.mjs @@ -0,0 +1,97 @@ +import assert from 'node:assert/strict'; +import { spawnSync } from 'node:child_process'; +import { mkdtempSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs'; +import { tmpdir } from 'node:os'; +import { join, resolve } from 'node:path'; +import { before, test } from 'node:test'; +import { assessReleaseMath, assertReviewedMatrix, assertReviewedProjectMath } from './assess-release-math.mjs'; + +const root = resolve(import.meta.dirname, '..'); +const policy = JSON.parse(readFileSync(join(root, 'qualification/reviewed-math-policy.json'), 'utf8')); +let project; +let matrix; + +before(() => { + for (const [kind, args] of [ + ['project', ['project', 'verify', '--input=qualification/original-project.json']], + ['matrix', ['components', 'matrix']] + ]) { + const result = spawnSync(process.execPath, ['dist/bin/dk.js', ...args, '--json', '--strict'], { + cwd: root, encoding: 'utf8', timeout: 90_000, maxBuffer: 8 * 1024 * 1024 + }); + assert.equal(result.error, undefined); + assert.equal(result.status, 0, result.stderr); + assert.equal(result.stderr, ''); + if (kind === 'project') project = JSON.parse(result.stdout); + else matrix = JSON.parse(result.stdout); + } +}); + +test('accepts the exact current project inventory without historical layout failures', () => { + assert.deepEqual(project.summary, { + componentCount: 38, themeCount: 1, runCount: 38, passedRuns: 38, + fixtureCount: 180, failedFixtureCount: 0, pass: true + }); + assert.deepEqual(assertReviewedProjectMath(project, policy), { + fixtureCount: 180, reviewedFailureCount: 0, mathematicalPass: true + }); +}); + +test('accepts all current matrix runs with their actual strict verdict', () => { + assert.deepEqual(matrix.summary, { + componentCount: 38, themeCount: 4, runCount: 152, passedRuns: 152, + fixtureCount: 720, failedFixtureCount: 0, pass: true + }); + assert.deepEqual(assertReviewedMatrix(matrix, policy), { + fixtureCount: 720, reviewedFailureCount: 0, mathematicalPass: true + }); +}); + +test('assesses standalone built artifacts with the passing strict exit status', () => { + const result = assessReleaseMath(root); + assert.deepEqual(result.project, { fixtureCount: 180, reviewedFailureCount: 0, mathematicalPass: true }); + assert.deepEqual(result.matrix, { fixtureCount: 720, reviewedFailureCount: 0, mathematicalPass: true }); + assert.equal(result.rendered, 'not-collected'); +}); + +test('rejects new failures, width verdicts, coverage gaps and changed case inputs', () => { + const failed = structuredClone(project); + failed.fixtures[0].layout[0].widthChecks[0].fitsWidth = false; + failed.fixtures[0].layout[0].widthChecks[0].pass = false; + failed.fixtures[0].layout[0].pass = false; + failed.fixtures[0].pass = false; + assert.throws(() => assertReviewedProjectMath(failed, policy), /Unreviewed|layout checks changed/); + + const widthOnly = structuredClone(project); + widthOnly.fixtures[0].layout[0].widthChecks[0].width += 1; + assert.throws(() => assertReviewedProjectMath(widthOnly, policy), /layout checks changed/); + + const incomplete = structuredClone(project); + incomplete.fixtures[0].coverage.complete = false; + assert.throws(() => assertReviewedProjectMath(incomplete, policy), /incomplete proof/); + + const changed = structuredClone(project); + changed.fixtures[0].sampleText += ' new content'; + assert.throws(() => assertReviewedProjectMath(changed, policy), /case inputs changed/); + + const matrixFailure = structuredClone(matrix); + matrixFailure.runs[0].pass = false; + matrixFailure.runs[0].failures.push('new actual failure'); + assert.throws(() => assertReviewedMatrix(matrixFailure, policy), /matrix verdicts changed/); +}); + +test('rejects inconsistent strict status even when a passing artifact is emitted', () => { + const fixtureRoot = mkdtempSync(join(tmpdir(), 'dkcli-math-status-')); + try { + mkdirSync(join(fixtureRoot, 'qualification')); + mkdirSync(join(fixtureRoot, 'dist/bin'), { recursive: true }); + writeFileSync(join(fixtureRoot, 'qualification/reviewed-math-policy.json'), JSON.stringify(policy)); + writeFileSync(join(fixtureRoot, 'qualification/original-project.json'), readFileSync(join(root, 'qualification/original-project.json'))); + writeFileSync(join(fixtureRoot, 'dist/package.json'), JSON.stringify({ type: 'module' })); + writeFileSync(join(fixtureRoot, 'dist/report.json'), JSON.stringify(project)); + writeFileSync(join(fixtureRoot, 'dist/bin/dk.js'), "import { readFileSync } from 'node:fs';\nprocess.stdout.write(readFileSync(new URL('../report.json', import.meta.url)));\nprocess.stderr.write('strict verification failed');\nprocess.exitCode = 1;\n"); + assert.throws(() => assessReleaseMath(fixtureRoot), /Strict exit status disagrees with the mathematical verdict/); + } finally { + rmSync(fixtureRoot, { recursive: true, force: true }); + } +}); diff --git a/src/lib/dk/cli.test.ts b/src/lib/dk/cli.test.ts index cc38c04..d50b69e 100644 --- a/src/lib/dk/cli.test.ts +++ b/src/lib/dk/cli.test.ts @@ -88,6 +88,28 @@ function readArtifact(text: string): Record { } describe('cli', () => { + it.each(['layout', 'compose', 'saliency'])('rejects malformed %s documents before emitting null metrics', async (command) => { + const captured = makeIo({ '/virtual/bad.json': JSON.stringify({ frame: { width: 960, height: 640, columns: 'invalid' }, elements: [{ id: 'title', kind: 'text', x: 0, y: 0, width: 220, height: 48 }] }) }); + expect(await runCli([command, '--input=bad.json', '--json'], captured.io)).toBe(1); + expect(captured.stdout).toBe(''); + expect(captured.stderr.length).toBeGreaterThan(0); + }); + it.each([ + ['scale', '--steps=1.5'], + ['scale', '--unit=cm'], + ['scale', '--base=-16'], + ['ease', '--mass=0'], + ['ease', '--preset=typo'], + ['jerk', '--duration=-1'], + ['linebreak', '--chars=0'], + ['text', '--font=0'] + ])('rejects invalid mathematical input without emitting an artifact: %j', async (args) => { + const captured = makeIo(); + expect(await runCli(args, captured.io)).toBe(1); + expect(captured.stdout).toBe(''); + expect(captured.stderr.length).toBeGreaterThan(0); + }); + it('lists every advertised tool command in help', () => { expect(CLI_COMMANDS).toEqual([ 'cms', @@ -238,8 +260,8 @@ describe('cli', () => { }; const cases: Array<{ args: string[]; expected: string }> = [ - { args: ['components', 'verify', '--name', 'button', '--theme', 'cobalt'], expected: 'Fail: Button, Cobalt' }, - { args: ['components', 'matrix', '--name', 'button', '--theme', 'cobalt'], expected: 'Fail 13' }, + { args: ['components', 'verify', '--name', 'button', '--theme', 'cobalt'], expected: 'Pass: Button, Cobalt' }, + { args: ['components', 'matrix', '--name', 'button', '--theme', 'cobalt'], expected: 'Pass 13' }, { args: ['perfect', '--seed', '#295dff', '--ratio', 'perfect-fourth', '--motion', 'snappy'], expected: '--perfect-base-color' }, { args: ['palette', '#3b82f6', '--mode', 'both'], expected: '/* dk palette #3b82f6 --engine=' }, { args: ['distinct', '#295dff', '--harmony', 'split-complementary'], expected: 'Minimum color difference:' }, @@ -401,21 +423,36 @@ describe('cli', () => { expect(capture.stderr).toContain('strict'); }); - it.each(['verify', 'matrix'])('strict component %s reports the narrow layout failure and preserves JSON', async (command) => { + it.each(['verify', 'matrix'])('strict component %s succeeds for all 13 wrapping button fixtures', async (command) => { const capture = makeIo(); const code = await runCli(['components', command, '--name', 'button', '--theme', 'cobalt', '--json', '--strict'], capture.io); const payload = readArtifact(capture.stdout); expect(payload.mode).toBe(command); - expect(code).toBe(1); - expect(jsonRecord(payload.summary).pass).toBe(false); - expect(payload.runs).toEqual(expect.arrayContaining([ - expect.objectContaining({ failures: expect.arrayContaining([ - expect.objectContaining({ reasons: expect.arrayContaining([expect.stringContaining('180')]) }) - ]) }) - ])); - expect(capture.stderr).toContain('strict'); + expect(code).toBe(0); + expect(jsonRecord(payload.summary)).toMatchObject({ pass: true, fixtureCount: 13, failedFixtureCount: 0 }); + expect(payload.runs).toEqual([expect.objectContaining({ slug: 'button', pass: true, fixtureCount: 13, failures: [] })]); + expect(capture.stderr).toBe(''); + }); + + it.each(['verify', 'matrix'])('strict component %s preserves the artifact for a genuinely failed contrast fixture', async (command) => { + const tokens = await import('@dkcli/tokens'); + const original = tokens.createTheme; + const spy = vi.spyOn(tokens, 'createTheme').mockImplementation((options) => { + const theme = original(options); + return { ...theme, families: { ...theme.families, color: { ...theme.families.color, primary: '#ffffff', 'on-primary': '#ffffff' } } }; + }); + try { + const captured = makeIo(); + expect(await runCli(['components', command, '--name=button', '--theme=cobalt', '--json', '--strict'], captured.io)).toBe(1); + const artifact = readArtifact(captured.stdout); + expect(jsonRecord(artifact.summary)).toMatchObject({ pass: false, fixtureCount: 13 }); + expect(artifact.runs).toEqual([expect.objectContaining({ pass: false, failures: expect.arrayContaining([ + expect.objectContaining({ reasons: expect.arrayContaining([expect.stringContaining('contrast 0.0Lc')]) }) + ]) })]); + expect(captured.stderr).toContain('strict'); + } finally { spy.mockRestore(); } }); it('strict component verification succeeds for passing mathematical fixtures', async () => { diff --git a/src/lib/dk/cli.ts b/src/lib/dk/cli.ts index a871699..d3d5de8 100644 --- a/src/lib/dk/cli.ts +++ b/src/lib/dk/cli.ts @@ -342,7 +342,9 @@ function getNumberFlag(flags: FlagMap, name: string, fallback: number, helpComma } function getIntegerFlag(flags: FlagMap, name: string, fallback: number, helpCommand: string): number { - return Math.round(getNumberFlag(flags, name, fallback, helpCommand)); + const value = getNumberFlag(flags, name, fallback, helpCommand); + if (!Number.isInteger(value)) fail(`Option --${name} requires an integer.`, helpCommand); + return value; } function resolveFormat( @@ -884,6 +886,9 @@ function renderEase(flags: FlagMap): string { const helpCommand = 'ease'; const format = resolveFormat(flags, 'css', ['css', 'json', 'text'], helpCommand); const preset = getStringFlag(flags, 'preset'); + if (preset !== undefined && !Object.hasOwn(SPRING_PRESETS, preset)) { + fail(`Unknown spring preset "${preset}". Use ${Object.keys(SPRING_PRESETS).join(', ')}.`, helpCommand); + } const params = preset && Object.hasOwn(SPRING_PRESETS, preset) ? SPRING_PRESETS[preset] diff --git a/src/lib/dk/project-cli.test.ts b/src/lib/dk/project-cli.test.ts index e36591c..230e033 100644 --- a/src/lib/dk/project-cli.test.ts +++ b/src/lib/dk/project-cli.test.ts @@ -15,11 +15,19 @@ const project = { viewports: [320, 768, 1280], history: [], reviews: [] }; +const failedProject = { + ...project, + theme: { ...project.theme, overrides: { color: { primary: '#ffffff', 'on-primary': '#ffffff' } } } +}; + function canonical(value: unknown): string { return JSON.stringify(value, (_key, current: unknown) => isRecord(current) ? Object.fromEntries(Object.keys(current).sort().map((key) => [key, current[key]])) : current); } -const identity = createHash('sha256').update(canonical({ schemaVersion: project.schemaVersion, theme: project.theme, viewports: project.viewports })).digest('hex'); +function identityFor(input: typeof project): string { + return createHash('sha256').update(canonical({ schemaVersion: input.schemaVersion, theme: input.theme, viewports: input.viewports })).digest('hex'); +} +const identity = identityFor(project); type Capture = { io: CliIO & { writeFile: (filePath: string, value: string) => Promise }; writes: Record; readonly stdout: string; readonly stderr: string }; function capture(files: Record = {}): Capture { @@ -55,7 +63,8 @@ describe('portable project CLI', () => { const summary = object(result.summary); expect(summary.componentCount).toBe(38); expect(summary.fixtureCount).toBe(180); - expect(summary.failedFixtureCount).toBeGreaterThan(0); + expect(summary.failedFixtureCount).toBe(0); + expect(summary.pass).toBe(true); const fixtures = result.fixtures; expect(Array.isArray(fixtures)).toBe(true); if (!Array.isArray(fixtures)) throw new Error('Expected mathematical fixtures.'); @@ -69,15 +78,32 @@ describe('portable project CLI', () => { }); }); expect(widthChecks.map((check) => check.width)).toEqual(expect.arrayContaining([320, 768, 1280])); - expect(widthChecks.some((check) => typeof check.width === 'number' && check.width < 320 && check.pass === false)).toBe(true); + expect(widthChecks.some((check) => typeof check.width === 'number' && check.width < 320)).toBe(true); + expect(widthChecks.every((check) => check.pass === true)).toBe(true); }); - it('emits the complete math artifact before a strict failure', async () => { + it('emits the complete math artifact with a strict success for the reviewed theme', async () => { const output = capture({ '/virtual/project.json': JSON.stringify(project) }); + expect(await runCli(['project', 'verify', '--input=project.json', '--format=json', '--strict'], output.io)).toBe(0); + expect(object(artifact(output.stdout).summary)).toMatchObject({ componentCount: 38, fixtureCount: 180, failedFixtureCount: 0, pass: true }); + expect(output.stderr).toBe(''); + }); + + it('emits the complete math artifact before a real contrast failure exits strictly', async () => { + const output = capture({ '/virtual/project.json': JSON.stringify(failedProject) }); expect(await runCli(['project', 'verify', '--input=project.json', '--format=json', '--strict'], output.io)).toBe(1); - expect(object(artifact(output.stdout).summary).componentCount).toBe(38); + const result = artifact(output.stdout); + expect(object(result.summary).componentCount).toBe(38); + expect(object(result.summary).failedFixtureCount).toBeGreaterThan(0); + if (!Array.isArray(result.fixtures)) throw new Error('Expected mathematical fixtures.'); + const contrast = result.fixtures.flatMap((fixture: unknown) => { + const checks = object(fixture).contrast; + if (!Array.isArray(checks)) throw new Error('Expected contrast proofs.'); + return checks.map((check: unknown) => object(check)); + }); + expect(contrast.some((check) => check.pass === false && check.lc === 0 && check.foreground === check.background)).toBe(true); expect(output.stderr).toContain('strict verification failed'); - expect(output.stderr).toContain('layout overflow'); + expect(output.stderr).toContain('contrast'); }); it('applies a matching override patch while retaining the previous revision', async () => { @@ -117,44 +143,50 @@ function string(value: unknown): string { if (typeof value !== 'string') throw new Error('Expected a string.'); return value; } -let receiptPromise: Promise | undefined; -async function makeReceipt(): Promise { - receiptPromise ??= (async () => { - const output = capture({ '/virtual/project.json': JSON.stringify(project) }); - await runCli(['project', 'verify', '--input=project.json', '--json'], output.io); - const report = artifact(output.stdout); - if (!Array.isArray(report.fixtures) || !Array.isArray(report.runs)) throw new Error('Expected complete math results.'); - const fixtures = report.fixtures.map((fixture: unknown) => object(fixture)); - const components = report.runs.map((run: unknown) => { - const item = object(run); - const slug = string(item.slug); - const cases = fixtures.filter((fixture) => fixture.componentId === slug); +const receiptPromises = new Map>(); +async function makeReceipt(sourceProject = project): Promise { + const sourceIdentity = identityFor(sourceProject); + let receiptPromise = receiptPromises.get(sourceIdentity); + if (!receiptPromise) { + receiptPromise = (async () => { + const output = capture({ '/virtual/project.json': JSON.stringify(sourceProject) }); + await runCli(['project', 'verify', '--input=project.json', '--json'], output.io); + const report = artifact(output.stdout); + if (!Array.isArray(report.fixtures) || !Array.isArray(report.runs)) throw new Error('Expected complete math results.'); + const fixtures = report.fixtures.map((fixture: unknown) => object(fixture)); + const components = report.runs.map((run: unknown) => { + const item = object(run); + const slug = string(item.slug); + const cases = fixtures.filter((fixture) => fixture.componentId === slug); + return { + component: slug, fixtureCount: number(item.fixtureCount), + failedCaseIds: cases.filter((fixture) => fixture.pass === false).map((fixture) => string(fixture.id)), + unsupportedCaseIds: cases.filter((fixture) => object(fixture.coverage).complete === false).map((fixture) => string(fixture.id)) + }; + }); + const failedCount = components.reduce((sum, component) => sum + component.failedCaseIds.length, 0); return { - component: slug, fixtureCount: number(item.fixtureCount), - failedCaseIds: cases.filter((fixture) => fixture.pass === false).map((fixture) => string(fixture.id)), - unsupportedCaseIds: cases.filter((fixture) => object(fixture.coverage).complete === false).map((fixture) => string(fixture.id)) + schemaVersion: 1, createdAt: '2026-10-04T12:00:00.000Z', projectIdentity: sourceIdentity, + artifactFingerprint: 'a'.repeat(64), status: failedCount > 0 ? 'failed' : 'incomplete', + browser: { provider: 'local-chromium', version: '131.0.0', userAgent: 'Qualification test browser' }, + viewports: [...sourceProject.viewports], fonts: { requested: { ...sourceProject.theme.fonts }, ready: true, loaded: [] }, + mathematics: { + fixtureCount: components.reduce((sum, component) => sum + component.fixtureCount, 0), + failedCount, + unsupportedCount: components.reduce((sum, component) => sum + component.unsupportedCaseIds.length, 0), components + }, + measurements: sourceProject.viewports.map((width) => ({ + component: 'button', caseId: 'rest', axes: { size: 'md' }, state: { rest: true }, + viewport: { width, height: 900 }, + checks: [{ kind: 'overflow', status: 'pass', message: 'The measured button fits its container.' }], + geometry: [{ selector: 'button', bounds: { x: 0, y: 0, width: 80, height: 44 }, clientWidth: 80, scrollWidth: 80, textOverflow: false, containerOverflow: false, fontFamily: 'system-ui', fontSize: '16px' }], + screenshot: { mediaType: 'image/jpeg', data: 'aGVsbG8=', width, height: 900 } + })), + coverage: { componentCount: 1, caseCount: 1, widthCount: sourceProject.viewports.length, untested: ['Other components have no measured scenes in this test receipt.'], unsupported: [] } }; - }); - return { - schemaVersion: 1, createdAt: '2026-10-04T12:00:00.000Z', projectIdentity: identity, - artifactFingerprint: 'a'.repeat(64), status: 'failed', - browser: { provider: 'local-chromium', version: '131.0.0', userAgent: 'Qualification test browser' }, - viewports: [...project.viewports], fonts: { requested: { ...project.theme.fonts }, ready: true, loaded: [] }, - mathematics: { - fixtureCount: components.reduce((sum, component) => sum + component.fixtureCount, 0), - failedCount: components.reduce((sum, component) => sum + component.failedCaseIds.length, 0), - unsupportedCount: components.reduce((sum, component) => sum + component.unsupportedCaseIds.length, 0), components - }, - measurements: project.viewports.map((width) => ({ - component: 'button', caseId: 'rest', axes: { size: 'md' }, state: { rest: true }, - viewport: { width, height: 900 }, - checks: [{ kind: 'overflow', status: 'pass', message: 'The measured button fits its container.' }], - geometry: [{ selector: 'button', bounds: { x: 0, y: 0, width: 80, height: 44 }, clientWidth: 80, scrollWidth: 80, textOverflow: false, containerOverflow: false, fontFamily: 'system-ui', fontSize: '16px' }], - screenshot: { mediaType: 'image/jpeg', data: 'aGVsbG8=', width, height: 900 } - })), - coverage: { componentCount: 1, caseCount: 1, widthCount: project.viewports.length, untested: ['Other components have no measured scenes in this test receipt.'], unsupported: [] } - }; - })(); + })(); + receiptPromises.set(sourceIdentity, receiptPromise); + } return structuredClone(await receiptPromise); } function respond(output: Capture, receipt: unknown): ReturnType> { @@ -205,12 +237,13 @@ describe('project qualification transport', () => { }); it('posts exact project content with optional bearer authentication and emits a failed receipt before strict exit', async () => { - const receipt = await makeReceipt(); - const output = capture({ '/virtual/project.json': JSON.stringify(project) }); + const receipt = await makeReceipt(failedProject); + expect(receipt.mathematics.failedCount).toBeGreaterThan(0); + const output = capture({ '/virtual/project.json': JSON.stringify(failedProject) }); const request = respond(output, receipt); expect(await runCli(['project', 'qualify', '--input=project.json', '--runtime-url=https://runtime.example/workbench', '--runtime-token=private-test-token', `--artifact-fingerprint=${receipt.artifactFingerprint}`, '--json', '--strict'], output.io)).toBe(1); expect(request).toHaveBeenCalledWith('https://runtime.example/api/dk/projects/qualify', expect.objectContaining({ - method: 'POST', headers: { 'content-type': 'application/json', authorization: 'Bearer private-test-token' }, body: JSON.stringify({ project }) + method: 'POST', headers: { 'content-type': 'application/json', authorization: 'Bearer private-test-token' }, body: JSON.stringify({ project: failedProject }) })); expect(artifact(output.stdout)).toEqual(receipt); expect(output.stderr).toContain('strict verification failed'); @@ -219,8 +252,8 @@ describe('project qualification transport', () => { }); it('keeps the default artifact exit compatible and identifies provenance as reported by the runtime', async () => { - const output = capture({ '/virtual/project.json': JSON.stringify(project) }); - respond(output, await makeReceipt()); + const output = capture({ '/virtual/project.json': JSON.stringify(failedProject) }); + respond(output, await makeReceipt(failedProject)); expect(await runCli(['project', 'qualify', '--input=project.json', '--runtime-url=http://localhost:4173'], output.io)).toBe(0); expect(output.stdout).toContain('(reported by runtime)'); expect(output.stdout).toContain('unsigned runtime claims'); @@ -228,7 +261,7 @@ describe('project qualification transport', () => { }); it.each(['identity', 'fingerprint', 'false pass', 'math coverage'])('rejects mismatched or malformed %s evidence before emitting an artifact', async (kind) => { - const receipt = await makeReceipt(); + const receipt = await makeReceipt(failedProject); if (kind === 'identity') receipt.projectIdentity = '0'.repeat(64); if (kind === 'false pass') receipt.status = 'passed'; if (kind === 'math coverage') { @@ -238,7 +271,7 @@ describe('project qualification transport', () => { receipt.mathematics.failedCount -= removed.failedCaseIds.length; receipt.mathematics.unsupportedCount -= removed.unsupportedCaseIds.length; } - const output = capture({ '/virtual/project.json': JSON.stringify(project) }); + const output = capture({ '/virtual/project.json': JSON.stringify(failedProject) }); respond(output, receipt); const args = ['project', 'qualify', '--input=project.json', '--runtime-url=http://localhost:4173', '--json']; if (kind === 'fingerprint') args.push(`--artifact-fingerprint=${'b'.repeat(64)}`); diff --git a/src/lib/dk/release-math-assessment.test.js b/src/lib/dk/release-math-assessment.test.js index c0b6329..5e35af0 100644 --- a/src/lib/dk/release-math-assessment.test.js +++ b/src/lib/dk/release-math-assessment.test.js @@ -7,17 +7,16 @@ import { assertReviewedMatrix, assertReviewedProjectMath } from '../../../script const policy = JSON.parse(readFileSync(new URL('../../../qualification/reviewed-math-policy.json', import.meta.url), 'utf8')); // A producer claim, not a mathematical implementation: mutations below must be -// rejected even when its totals still claim the exact reviewed failure count. +// rejected even when its totals still claim the exact reviewed passing count. function claimedProjectReport() { const fixtures = policy.project.fixtureInventory.map((item) => { - const { declared, proofCounts, ...identity } = item; - const reviewed = policy.project.reviewedFailures.find((failure) => failure.componentId === item.componentId && failure.id === item.id); + const { declared, proofCounts, layout, ...identity } = item; return { ...identity, - ...Object.fromEntries(Object.entries(proofCounts).map(([kind, count]) => [kind, kind === 'layout' && reviewed ? structuredClone(reviewed.layout) : Array.from({ length: count }, () => ({ pass: true }))])), + ...Object.fromEntries(Object.entries(proofCounts).map(([kind, count]) => [kind, kind === 'layout' ? structuredClone(layout) : Array.from({ length: count }, () => ({ pass: true }))])), evidence: 'mathematical', resolved: true, coverage: { declared: [...declared], evaluated: [...declared], unsupported: [], complete: true }, - pass: !reviewed + pass: true }; }); return { @@ -25,56 +24,72 @@ function claimedProjectReport() { }; } -describe('reviewed mathematical release findings', () => { - it('accepts the sealed review while preserving failed mathematical verdicts', () => { +function accordionFixture(report) { + const fixture = report.fixtures.find((fixture) => fixture.componentId === 'accordion' && fixture.id === 'size=sm__rest'); + if (!fixture || fixture.pass !== true) throw new Error('Expected the reviewed passing Accordion small fixture.'); + return fixture; +} + +function cobaltAccordionRun(report) { + const run = report.runs.find((run) => run.themeId === 'cobalt' && run.slug === 'accordion'); + if (!run || run.pass !== true) throw new Error('Expected the reviewed passing Cobalt Accordion run.'); + return run; +} + +describe('reviewed mathematical release evidence', () => { + it('accepts the sealed review while preserving current mathematical verdicts', () => { const report = claimedProjectReport(); - expect(assertReviewedProjectMath(report, policy)).toEqual({ fixtureCount: 180, reviewedFailureCount: 17, mathematicalPass: false }); - expect(assertReviewedMatrix(policy.matrix, policy)).toEqual({ fixtureCount: 720, reviewedFailureCount: 45, mathematicalPass: false }); - expect(report.fixtures.filter((fixture) => !fixture.pass)).toHaveLength(17); + expect(assertReviewedProjectMath(report, policy)).toEqual({ fixtureCount: 180, reviewedFailureCount: 0, mathematicalPass: true }); + expect(assertReviewedMatrix(policy.matrix, policy)).toEqual({ fixtureCount: 720, reviewedFailureCount: 0, mathematicalPass: true }); + expect(report.fixtures.filter((fixture) => !fixture.pass)).toHaveLength(0); }); it('rejects a changed case identity at the same claimed count', () => { const report = claimedProjectReport(); - report.fixtures.find((fixture) => !fixture.pass).id += '-unexpected'; + accordionFixture(report).id += '-unexpected'; expect(() => assertReviewedProjectMath(report, policy)).toThrow('Unexpected or duplicate fixture'); }); it('rejects changed sample inputs and failed widths for an otherwise reviewed case', () => { const changedText = claimedProjectReport(); - changedText.fixtures.find((fixture) => !fixture.pass).sampleText = 'Different authored content'; + accordionFixture(changedText).sampleText = 'Different authored content'; expect(() => assertReviewedProjectMath(changedText, policy)).toThrow('case inputs changed'); const changedWidth = claimedProjectReport(); - changedWidth.fixtures.find((fixture) => !fixture.pass).layout[0].widthChecks[0].width = 240; + accordionFixture(changedWidth).layout[0].widthChecks[0].width = 240; expect(() => assertReviewedProjectMath(changedWidth, policy)).toThrow('layout checks changed'); }); it('rejects a new failure category, unsupported proof, and removed contrast evidence even at the same count', () => { const contrast = claimedProjectReport(); - contrast.fixtures.find((fixture) => !fixture.pass).contrast[0].pass = false; + accordionFixture(contrast).contrast[0].pass = false; expect(() => assertReviewedProjectMath(contrast, policy)).toThrow('Unreviewed contrast failure'); const unsupported = claimedProjectReport(); - unsupported.fixtures[0].coverage.unsupported.push('screenshot'); + accordionFixture(unsupported).coverage.unsupported.push('screenshot'); expect(() => assertReviewedProjectMath(unsupported, policy)).toThrow('Unsupported proof'); const missingCheck = claimedProjectReport(); - missingCheck.fixtures[0].contrast.pop(); + accordionFixture(missingCheck).contrast.pop(); expect(() => assertReviewedProjectMath(missingCheck, policy)).toThrow('Proof count changed'); }); - it('rejects falsely passing reviewed findings and different project content', () => { + it('rejects a claimed pass with a failed proof and different project content', () => { const falsePass = claimedProjectReport(); - falsePass.fixtures.find((fixture) => !fixture.pass).pass = true; - expect(() => assertReviewedProjectMath(falsePass, policy)).toThrow('must remain a mathematical failure'); + const fixture = accordionFixture(falsePass); + fixture.layout[0].widthChecks[0].fitsWidth = false; + fixture.layout[0].widthChecks[0].pass = false; + fixture.layout[0].pass = false; + expect(fixture.pass).toBe(true); + expect(() => assertReviewedProjectMath(falsePass, policy)).toThrow('Unreviewed layout failure'); const otherProject = claimedProjectReport(); otherProject.projectIdentity = '0'.repeat(64); expect(() => assertReviewedProjectMath(otherProject, policy)).toThrow('projectIdentity changed'); }); - it('rejects swapped matrix findings and new check reasons at the same 720/45 totals', () => { + it('rejects changed matrix inventory and new check reasons at the same claimed totals', () => { const swapped = structuredClone(policy.matrix); - swapped.runs.find((run) => !run.pass).failures[0].caseKey = 'size=unexpected'; - expect(() => assertReviewedMatrix(swapped, policy)).toThrow('matrix verdicts changed'); + cobaltAccordionRun(swapped).slug = 'unexpected-component'; + expect(() => assertReviewedMatrix(swapped, policy)).toThrow('Unexpected or duplicate matrix run'); const newCheck = structuredClone(policy.matrix); - newCheck.runs.find((run) => !run.pass).failures[0].reasons.push('contrast failed'); + cobaltAccordionRun(newCheck).failures.push({ caseKey: 'size=sm', reasons: ['contrast failed'] }); expect(() => assertReviewedMatrix(newCheck, policy)).toThrow('matrix verdicts changed'); }); });