diff design_system/test/catalog_test.js @ 258:60a876c4587a

[ui] Add semantic primitive ownership Build a layered Zenbu token and sizing system, make authored controls use native-underneath primitives, migrate mrjunejune without imposing visual surfaces, and document/enforce HTML ownership in the catalog and wiki. Co-authored-by: Copilot <[email protected]>
author MrJuneJune <me@mrjunejune.com>
date Wed, 05 Aug 2026 05:25:40 -0700
parents
children 667156fcd3e3
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/design_system/test/catalog_test.js	Wed Aug 05 05:25:40 2026 -0700
@@ -0,0 +1,2100 @@
+const assert = require('node:assert/strict');
+const http = require('node:http');
+const net = require('node:net');
+const path = require('node:path');
+const { spawn } = require('node:child_process');
+
+const RUNFILES = process.env.JS_BINARY__RUNFILES;
+const WORKSPACE = process.env.JS_BINARY__WORKSPACE;
+const runfilesWorkspace = path.join(RUNFILES, WORKSPACE);
+const playwrightPath = path.join(
+  runfilesWorkspace,
+  'hg-web/e2e/node_modules/playwright-core',
+);
+const { chromium } = require(playwrightPath);
+
+function findFreePort() {
+  return new Promise((resolve, reject) => {
+    const server = net.createServer();
+    server.once('error', reject);
+    server.listen(0, '127.0.0.1', () => {
+      const address = server.address();
+      server.close(error => {
+        if (error) reject(error);
+        else resolve(String(address.port));
+      });
+    });
+  });
+}
+
+async function stopProcess(child) {
+  if (!child || child.exitCode !== null) return;
+  child.kill('SIGTERM');
+  await new Promise(resolve => {
+    const timer = setTimeout(() => {
+      if (child.exitCode === null) child.kill('SIGKILL');
+    }, 3000);
+    child.once('exit', () => {
+      clearTimeout(timer);
+      resolve();
+    });
+  });
+}
+
+async function waitForServer(server, baseUrl, logs) {
+  const deadline = Date.now() + 15000;
+  while (Date.now() < deadline) {
+    if (server.exitCode !== null) {
+      throw new Error(`Server exited with ${server.exitCode}\n${logs.join('')}`);
+    }
+    try {
+      const response = await fetch(baseUrl);
+      if (response.ok) return;
+    } catch {
+      // Keep waiting.
+    }
+    await new Promise(resolve => setTimeout(resolve, 100));
+  }
+  throw new Error(`Server startup timed out\n${logs.join('')}`);
+}
+
+function requestRawPath(port, requestPath) {
+  return new Promise((resolve, reject) => {
+    const request = http.request({
+      host: '127.0.0.1',
+      port,
+      method: 'GET',
+      path: requestPath,
+      agent: false,
+    }, response => {
+      response.resume();
+      response.on('end', () => resolve(response.statusCode));
+    });
+    request.on('error', reject);
+    request.end();
+  });
+}
+
+(async () => {
+  assert.ok(RUNFILES);
+  assert.ok(WORKSPACE);
+  const serverBinary = path.join(
+    runfilesWorkspace,
+    'design_system/design_system_server',
+  );
+  const chromiumPath = path.resolve(process.env.CHROMIUM_PATH);
+  const port = await findFreePort();
+  const baseUrl = `http://127.0.0.1:${port}`;
+  const logs = [];
+  let server;
+  let browser;
+
+  try {
+    const invalidServer = spawn(serverBinary, [], {
+      cwd: runfilesWorkspace,
+      env: {
+        ...process.env,
+        DESIGN_SYSTEM_PORT: 'not-a-port',
+      },
+      stdio: ['ignore', 'pipe', 'pipe'],
+    });
+    const invalidExit = await new Promise(resolve => {
+      invalidServer.once('exit', (code, signal) => resolve({ code, signal }));
+    });
+    assert.equal(invalidExit.code, 1);
+    assert.equal(invalidExit.signal, null);
+
+    server = spawn(serverBinary, [], {
+      cwd: runfilesWorkspace,
+      env: {
+        ...process.env,
+        DESIGN_SYSTEM_PORT: port,
+      },
+      stdio: ['ignore', 'pipe', 'pipe'],
+    });
+    server.stdout.on('data', chunk => logs.push(chunk.toString()));
+    server.stderr.on('data', chunk => logs.push(chunk.toString()));
+    await waitForServer(server, baseUrl, logs);
+    assert.equal(await requestRawPath(port, '/../../MODULE.bazel'), 400);
+
+    for (const route of [
+      '/',
+      '/tokens',
+      '/icons',
+      '/components',
+      '/components/button',
+      '/components/card',
+      '/components/alert',
+      '/components/field',
+      '/components/notifications',
+      '/components/stack',
+    ]) {
+      const response = await fetch(`${baseUrl}${route}`);
+      assert.equal(response.status, 200, route);
+      assert.match(
+        response.headers.get('content-type') || '',
+        /^text\/html/,
+      );
+    }
+    for (const asset of [
+      '/styles/tokens.css',
+      '/styles/reference.css',
+      '/styles/semantic.css',
+      '/styles/density.css',
+      '/styles/themes.css',
+      '/styles/components.css',
+      '/styles/elements.css',
+      '/components/index.js',
+      '/components/icon.js',
+      '/catalog.js',
+      '/catalog.js',
+    ]) {
+      const response = await fetch(`${baseUrl}${asset}`);
+      assert.equal(response.status, 200, asset);
+      assert.ok((await response.text()).length > 100, asset);
+    }
+
+    browser = await chromium.launch({
+      executablePath: chromiumPath,
+      headless: true,
+      args: ['--no-sandbox'],
+    });
+    const context = await browser.newContext({ colorScheme: 'light' });
+    const page = await context.newPage();
+    const errors = [];
+    const devtools = await context.newCDPSession(page);
+    await devtools.send('Runtime.enable');
+    devtools.on('Runtime.exceptionThrown', event => {
+      const exception = event.exceptionDetails;
+      errors.push(
+        `${exception.text}: ${exception.exception?.description || ''} ` +
+        `${exception.url || ''}:${exception.lineNumber + 1}`,
+      );
+    });
+    page.on('pageerror', error => errors.push(error.stack || error.message));
+    page.on('console', message => {
+      if (message.type() === 'error') {
+        const location = message.location();
+        errors.push(
+          `${message.text()} ${location.url || ''}:${location.lineNumber + 1}`,
+        );
+      }
+    });
+    page.on('response', response => {
+      if (response.status() >= 400) {
+        errors.push(`${response.status()} ${response.url()}`);
+      }
+    });
+
+    await page.goto(`${baseUrl}/components/button`, {
+      waitUntil: 'networkidle',
+    });
+    try {
+      await page.waitForFunction(
+        () => customElements.get('zen-button') &&
+          customElements.get('zen-story'),
+        undefined,
+        { timeout: 5000 },
+      );
+    } catch (error) {
+      throw new Error(`${error.message}\n${errors.join('\n')}`);
+    }
+    const catalogState = await page.evaluate(async () => {
+      const links = [
+        ...document.querySelectorAll(
+          '.catalog-nav[aria-label="Components"] a',
+        ),
+      ];
+      const elements = [...new Set(
+        links.map(link => link.dataset.zenElement).filter(Boolean),
+      )];
+      const statuses = await Promise.all(
+        links.map(async link => (await fetch(link.href)).status),
+      );
+      return {
+        cards: document.querySelectorAll('.component-grid > a').length,
+        elements,
+        links: links.length,
+        missing: elements.filter(name => !customElements.get(name)),
+        routes: links.map(link => new URL(link.href).pathname),
+        statuses,
+      };
+    });
+    assert.equal(catalogState.cards, 70);
+    assert.equal(catalogState.links, 70);
+    assert.equal(catalogState.elements.length, 69);
+    assert.deepEqual(catalogState.missing, []);
+    assert.deepEqual(
+      [...new Set(catalogState.statuses)],
+      [200],
+    );
+    const primitiveOwnership = await page.evaluate(() => {
+      const buttonOwners = [
+        'zen-alert',
+        'zen-attachment',
+        'zen-button',
+        'zen-button-group',
+        'zen-calendar',
+        'zen-carousel',
+        'zen-combobox',
+        'zen-command',
+        'zen-context-menu',
+        'zen-data-table',
+        'zen-date-picker',
+        'zen-dropdown-menu',
+        'zen-input-group',
+        'zen-menubar',
+        'zen-message-scroller',
+        'zen-navigation-menu',
+        'zen-notifications',
+        'zen-sidebar',
+        'zen-tabs',
+        'zen-toggle',
+        'zen-toggle-group',
+      ].join(',');
+      const controlOwners = [
+        'zen-calendar',
+        'zen-checkbox',
+        'zen-combobox',
+        'zen-command',
+        'zen-date-picker',
+        'zen-field',
+        'zen-form',
+        'zen-input',
+        'zen-input-group',
+        'zen-input-otp',
+        'zen-native-select',
+        'zen-radio-group',
+        'zen-select',
+        'zen-slider',
+        'zen-switch',
+        'zen-textarea',
+      ].join(',');
+      return {
+        buttons: [...document.querySelectorAll('button')].filter(button =>
+          !button.closest(buttonOwners) &&
+          !button.matches('zen-story > header > button')
+        ).map(button => button.outerHTML),
+        controls: [
+          ...document.querySelectorAll('input, select, textarea'),
+        ].filter(control => !control.closest(controlOwners))
+          .map(control => control.outerHTML),
+      };
+    });
+    assert.deepEqual(primitiveOwnership, {
+      buttons: [],
+      controls: [],
+    });
+    const brokenCatalogPages = await page.evaluate(routes => {
+      const failures = [];
+      const buttonOwners = [
+        'zen-alert',
+        'zen-attachment',
+        'zen-button',
+        'zen-button-group',
+        'zen-calendar',
+        'zen-carousel',
+        'zen-combobox',
+        'zen-command',
+        'zen-context-menu',
+        'zen-data-table',
+        'zen-date-picker',
+        'zen-dropdown-menu',
+        'zen-input-group',
+        'zen-menubar',
+        'zen-message-scroller',
+        'zen-navigation-menu',
+        'zen-notifications',
+        'zen-sidebar',
+        'zen-tabs',
+        'zen-toggle',
+        'zen-toggle-group',
+      ].join(',');
+      for (const route of ['/', '/tokens', '/icons', '/components', ...routes]) {
+        history.replaceState({}, '', route);
+        window.dispatchEvent(new PopStateEvent('popstate'));
+        const visible = [
+          ...document.querySelectorAll('[data-catalog-page]:not([hidden])'),
+        ];
+        const expected = route === '/' || route === '/components'
+          ? 'overview'
+          : route.slice(route.lastIndexOf('/') + 1);
+        if (visible.length !== 1 ||
+            visible[0].dataset.catalogPage !== expected ||
+            !visible[0].querySelector('h1')?.textContent.trim()) {
+          failures.push(route);
+          continue;
+        }
+        if (route.startsWith('/components/')) {
+          const story = visible[0].querySelector('zen-story');
+          const canvas = story?.querySelector('.story-canvas');
+          const source = story?.querySelector('pre code');
+          if (!story || !canvas?.children.length ||
+              !source?.textContent.trim()) {
+            failures.push(`${route}:empty-story`);
+          }
+          const unownedButtons = [...canvas?.querySelectorAll('button') || []]
+            .filter(button => !button.closest(buttonOwners));
+          if (unownedButtons.length) {
+            failures.push(`${route}:unowned-button`);
+          }
+        }
+      }
+      history.replaceState({}, '', '/components/button');
+      window.dispatchEvent(new PopStateEvent('popstate'));
+      return failures;
+    }, catalogState.routes);
+    assert.deepEqual(brokenCatalogPages, []);
+    await page.keyboard.press('/');
+    assert.equal(
+      await page.locator('#componentSearch').evaluate(
+        input => document.activeElement === input,
+      ),
+      true,
+    );
+    await page.locator('#componentSearch').fill('date');
+    assert.equal(
+      await page.locator(
+        '.catalog-nav[aria-label="Components"] a:not([hidden])',
+      ).count(),
+      2,
+    );
+    assert.equal(
+      await page.locator('.component-grid > a:not([hidden])').count(),
+      2,
+    );
+    assert.match(
+      await page.locator('#componentSearchStatus').textContent(),
+      /^2 components found$/,
+    );
+    await page.locator('#componentSearch').press('Escape');
+    assert.equal(await page.locator('#componentSearch').inputValue(), '');
+    assert.equal(
+      await page.locator(
+        '.catalog-nav[aria-label="Components"] a:not([hidden])',
+      ).count(),
+      70,
+    );
+    await page.locator('#componentSearch').fill('stack');
+    assert.equal(
+      await page.locator(
+        '.catalog-nav[aria-label="Components"] a[href="/components/stack"]',
+      ).getAttribute('hidden'),
+      null,
+    );
+    assert.equal(
+      await page.locator(
+        '.component-grid > a[href="/components/stack"]',
+      ).getAttribute('hidden'),
+      null,
+    );
+    await page.locator('#componentSearch').press('Escape');
+    assert.equal(
+      await page.locator('[data-catalog-page="button"]').isVisible(),
+      true,
+    );
+    assert.equal(
+      await page.locator('.catalog-sidebar a[href="/components/button"]').getAttribute('aria-current'),
+      'page',
+    );
+    assert.match(
+      await page.locator('zen-story pre').first().textContent(),
+      /<zen-button>/,
+    );
+    const plainPresentation = await page.evaluate(() => {
+      const style = document.createElement('style');
+      style.textContent = `
+        .application-button {
+          padding: 7px;
+          border: 3px solid currentColor;
+          box-shadow: none;
+          background: transparent;
+        }
+        .application-input {
+          appearance: auto;
+          padding: 9px;
+          border: 4px solid currentColor;
+          background: transparent;
+        }
+      `;
+      document.head.append(style);
+      const buttonHost = document.createElement('zen-button');
+      buttonHost.setAttribute('appearance', 'plain');
+      buttonHost.setAttribute('loading', '');
+      const button = document.createElement('button');
+      button.className = 'application-button';
+      button.textContent = 'Plain';
+      buttonHost.append(button);
+
+      const card = document.createElement('zen-card');
+      card.setAttribute('appearance', 'plain');
+      const article = document.createElement('article');
+      card.append(article);
+
+      const field = document.createElement('zen-field');
+      field.setAttribute('appearance', 'plain');
+      const label = document.createElement('label');
+      label.textContent = 'Plain field';
+      const input = document.createElement('input');
+      field.append(label, input);
+      const inputHost = document.createElement('zen-input');
+      inputHost.setAttribute('appearance', 'plain');
+      inputHost.setAttribute('size', 'lg');
+      const applicationInput = document.createElement('input');
+      applicationInput.className = 'application-input';
+      inputHost.append(applicationInput);
+      document.body.append(buttonHost, card, field, inputHost);
+
+      const buttonStyle = getComputedStyle(button);
+      const cardStyle = getComputedStyle(article);
+      return {
+        buttonBackground: buttonStyle.backgroundColor,
+        buttonBorder: buttonStyle.borderTopWidth,
+        buttonPadding: buttonStyle.paddingTop,
+        buttonShadow: buttonStyle.boxShadow,
+        buttonOpacity: buttonStyle.opacity,
+        buttonSpinner: getComputedStyle(button, '::before').content,
+        cardBackground: cardStyle.backgroundColor,
+        cardBorder: cardStyle.borderTopWidth,
+        cardShadow: cardStyle.boxShadow,
+        fieldDisplay: getComputedStyle(field).display,
+        inputAppearance: getComputedStyle(input).appearance,
+        plainInputAppearance: getComputedStyle(applicationInput).appearance,
+        plainInputBorder: getComputedStyle(applicationInput).borderTopWidth,
+        plainInputPadding: getComputedStyle(applicationInput).paddingTop,
+        labelFor: label.htmlFor,
+        inputId: input.id,
+      };
+    });
+    assert.deepEqual(plainPresentation, {
+      buttonBackground: 'rgba(0, 0, 0, 0)',
+      buttonBorder: '3px',
+      buttonPadding: '7px',
+      buttonShadow: 'none',
+      buttonOpacity: '1',
+      buttonSpinner: 'none',
+      cardBackground: 'rgba(0, 0, 0, 0)',
+      cardBorder: '0px',
+      cardShadow: 'none',
+      fieldDisplay: 'contents',
+      inputAppearance: 'auto',
+      plainInputAppearance: 'auto',
+      plainInputBorder: '4px',
+      plainInputPadding: '9px',
+      labelFor: plainPresentation.inputId,
+      inputId: plainPresentation.inputId,
+    });
+    const buttonScale = await page.locator(
+      '[data-catalog-page="button"] zen-button[size] > button',
+    ).evaluateAll(buttons => buttons.map(button => {
+      const host = button.parentElement;
+      const style = getComputedStyle(button);
+      const hostStyle = getComputedStyle(host);
+      return {
+        fontSize: Number.parseFloat(style.fontSize),
+        height: button.getBoundingClientRect().height,
+        paddingInline: Number.parseFloat(style.paddingInlineStart),
+        size: host.getAttribute('size'),
+        variableHeight: Number.parseFloat(
+          hostStyle.getPropertyValue('--zenbu-control-height'),
+        ) * Number.parseFloat(getComputedStyle(document.documentElement).fontSize),
+      };
+    }));
+    assert.deepEqual(
+      buttonScale.map(item => item.size),
+      ['xs', 'sm', 'md', 'lg', 'xl'],
+    );
+    for (let index = 1; index < buttonScale.length; index++) {
+      assert.ok(buttonScale[index].height > buttonScale[index - 1].height);
+      assert.ok(
+        buttonScale[index].paddingInline >
+          buttonScale[index - 1].paddingInline,
+      );
+      assert.ok(
+        buttonScale[index].fontSize > buttonScale[index - 1].fontSize,
+      );
+    }
+    for (const item of buttonScale) {
+      assert.ok(
+        Math.abs(item.height - item.variableHeight) < 1,
+        JSON.stringify(buttonScale),
+      );
+    }
+    const compactScale = await page.evaluate(() => {
+      document.documentElement.dataset.zenDensity = 'compact';
+      return [...document.querySelectorAll(
+        '[data-catalog-page="button"] zen-button[size] > button',
+      )]
+        .map(button => button.getBoundingClientRect().height);
+    });
+    assert.equal(compactScale.length, buttonScale.length);
+    for (let index = 0; index < compactScale.length; index++) {
+      assert.ok(compactScale[index] < buttonScale[index].height);
+    }
+    await page.evaluate(() => {
+      delete document.documentElement.dataset.zenDensity;
+    });
+
+    await page.emulateMedia({ reducedMotion: 'reduce' });
+    assert.equal(
+      await page.evaluate(() =>
+        getComputedStyle(document.documentElement)
+          .getPropertyValue('--zenbu-sys-motion-duration-state')
+          .trim()
+      ),
+      '1ms',
+    );
+    await page.emulateMedia({ reducedMotion: 'no-preference' });
+
+    assert.equal(
+      await page.locator('zen-button[loading]:not([appearance="plain"])').getAttribute('inert'),
+      null,
+    );
+    assert.equal(
+      await page.locator('zen-button[loading]:not([appearance="plain"]) button').getAttribute('aria-disabled'),
+      'true',
+    );
+    assert.equal(
+      await page.locator('zen-button[loading]:not([appearance="plain"]) button').getAttribute('aria-busy'),
+      'true',
+    );
+    await page.evaluate(() => {
+      window.__loadingClicks = 0;
+      document.querySelector('zen-button[loading]:not([appearance="plain"]) button')
+        .addEventListener('click', () => window.__loadingClicks++);
+    });
+    await page.locator('zen-button[loading]:not([appearance="plain"]) button').evaluate(
+      button => button.click(),
+    );
+    assert.equal(await page.evaluate(() => window.__loadingClicks), 0);
+    await page.locator('zen-button[loading]:not([appearance="plain"]) button').focus();
+    assert.equal(
+      await page.locator('zen-button[loading]:not([appearance="plain"]) button').evaluate(
+        button => document.activeElement === button,
+      ),
+      true,
+    );
+    await page.keyboard.press('Tab');
+    assert.equal(
+      await page.locator('zen-button[loading]:not([appearance="plain"]) button').evaluate(
+        button => document.activeElement === button,
+      ),
+      false,
+    );
+    const nativeDisabledPreserved = await page.evaluate(async () => {
+      const wrapper = document.createElement('zen-button');
+      const button = document.createElement('button');
+      button.disabled = true;
+      button.textContent = 'Native disabled';
+      wrapper.append(button);
+      document.body.append(wrapper);
+      await customElements.whenDefined('zen-button');
+      wrapper.setAttribute('disabled', '');
+      wrapper.removeAttribute('disabled');
+      return button.disabled;
+    });
+    assert.equal(nativeDisabledPreserved, true);
+    const replacementButtonState = await page.evaluate(async () => {
+      const wrapper = document.createElement('zen-button');
+      wrapper.setAttribute('disabled', '');
+      const first = document.createElement('button');
+      first.textContent = 'First';
+      wrapper.append(first);
+      document.body.append(wrapper);
+      await new Promise(resolve => setTimeout(resolve));
+      const second = document.createElement('button');
+      second.textContent = 'Second';
+      wrapper.replaceChildren(second);
+      await new Promise(resolve => setTimeout(resolve));
+      return {
+        firstDisabled: first.disabled,
+        secondAriaDisabled: second.getAttribute('aria-disabled'),
+        wrapperInert: wrapper.hasAttribute('inert'),
+      };
+    });
+    assert.equal(replacementButtonState.firstDisabled, false);
+    assert.equal(replacementButtonState.secondAriaDisabled, 'true');
+    assert.equal(replacementButtonState.wrapperInert, false);
+    const modifiedClickAllowed = await page.evaluate(() => {
+      const link = document.querySelector(
+        '.catalog-sidebar a[href="/components/card"]',
+      );
+      return link.dispatchEvent(new MouseEvent('click', {
+        bubbles: true,
+        cancelable: true,
+        button: 0,
+        ctrlKey: true,
+      }));
+    });
+    assert.equal(modifiedClickAllowed, true);
+
+    await page.goto(`${baseUrl}/icons`, { waitUntil: 'networkidle' });
+    const iconCount = await page.locator('#iconGrid figure').count();
+    assert.ok(iconCount >= 30);
+    assert.equal(
+      await page.locator('#iconGrid figure zen-icon > svg').count(),
+      iconCount,
+    );
+    const iconFallback = await page.evaluate(() => {
+      const icon = document.createElement('zen-icon');
+      icon.setAttribute('name', 'not-a-real-icon');
+      icon.setAttribute('label', 'Missing icon fallback');
+      document.body.append(icon);
+      return {
+        fallback: icon.dataset.zenIcon,
+        hidden: icon.getAttribute('aria-hidden'),
+        label: icon.getAttribute('aria-label'),
+        role: icon.getAttribute('role'),
+      };
+    });
+    assert.deepEqual(iconFallback, {
+      fallback: 'alert',
+      hidden: null,
+      label: 'Missing icon fallback',
+      role: 'img',
+    });
+
+    await page.goto(`${baseUrl}/components/link`, {
+      waitUntil: 'networkidle',
+    });
+    const linkPrimitive = page.locator('zen-link[effect="paw"]').first();
+    assert.equal(
+      await linkPrimitive.locator(
+        ':scope > a > zen-icon[name="paw"][data-zen-link-decoration]',
+      ).count(),
+      1,
+    );
+    const pawMotion = await linkPrimitive.locator('zen-icon').evaluate(icon => {
+      const style = getComputedStyle(icon);
+      const keyframes = icon.getAnimations().some(animation =>
+        animation.animationName === 'zen-paw-step'
+      );
+      return {
+        animationDuration: style.animationDuration,
+        color: style.color,
+        keyframes,
+      };
+    });
+    assert.notEqual(pawMotion.animationDuration, '0s');
+    assert.notEqual(pawMotion.color, 'rgba(0, 0, 0, 0)');
+    assert.equal(pawMotion.keyframes, true);
+    await page.emulateMedia({ reducedMotion: 'reduce' });
+    assert.equal(
+      await linkPrimitive.locator('zen-icon').evaluate(
+        icon => getComputedStyle(icon).animationName,
+      ),
+      'none',
+    );
+    await page.emulateMedia({ reducedMotion: 'no-preference' });
+
+    await page.goto(`${baseUrl}/components/text`, {
+      waitUntil: 'networkidle',
+    });
+    const textScale = await page.locator('zen-text[size] > p').evaluateAll(
+      texts => texts.map(text => Number.parseFloat(
+        getComputedStyle(text).fontSize,
+      )),
+    );
+    assert.equal(textScale.length, 5);
+    for (let index = 1; index < textScale.length; index++) {
+      assert.ok(textScale[index] > textScale[index - 1]);
+    }
+
+    await page.goto(`${baseUrl}/components/heading`, {
+      waitUntil: 'networkidle',
+    });
+    const headingScale = await page.locator(
+      'zen-heading[size] > h3',
+    ).evaluateAll(headings => headings.map(heading => ({
+      fontSize: Number.parseFloat(getComputedStyle(heading).fontSize),
+      weight: Number(getComputedStyle(heading).fontWeight),
+    })));
+    assert.equal(headingScale.length, 5);
+    assert.ok(headingScale.every(heading => heading.weight >= 700));
+    for (let index = 1; index < headingScale.length; index++) {
+      assert.ok(
+        headingScale[index].fontSize > headingScale[index - 1].fontSize,
+      );
+    }
+
+    await page.goto(`${baseUrl}/components/box`, {
+      waitUntil: 'networkidle',
+    });
+    const boxScale = await page.locator('zen-box[padding]').evaluateAll(
+      boxes => boxes.map(box => Number.parseFloat(
+        getComputedStyle(box).paddingTop,
+      )),
+    );
+    assert.equal(boxScale.length, 5);
+    for (let index = 1; index < boxScale.length; index++) {
+      assert.ok(boxScale[index] > boxScale[index - 1]);
+    }
+
+    await page.goto(`${baseUrl}/components/skeleton`, {
+      waitUntil: 'networkidle',
+    });
+    assert.equal(
+      await page.locator('zen-skeleton[width="60%"]').evaluate(
+        skeleton => skeleton.style.getPropertyValue(
+          '--zenbu-skeleton-width',
+        ),
+      ),
+      '60%',
+    );
+
+    await page.goto(`${baseUrl}/tokens`, { waitUntil: 'networkidle' });
+    const colorTokenState = await page.evaluate(() => {
+      const root = document.documentElement;
+      const value = name => getComputedStyle(root)
+        .getPropertyValue(`--zen-color-${name}`)
+        .trim();
+      const systemValue = name => getComputedStyle(root)
+        .getPropertyValue(`--zenbu-sys-${name}`)
+        .trim();
+      const families = [
+        'neutral',
+        'red',
+        'orange',
+        'amber',
+        'green',
+        'teal',
+        'blue',
+        'violet',
+        'rose',
+        'brown',
+      ];
+      const steps = [
+        '50',
+        '100',
+        '200',
+        '300',
+        '400',
+        '500',
+        '600',
+        '700',
+        '800',
+        '900',
+        '950',
+      ];
+      const rampsComplete = families.every(family =>
+        steps.every(step => value(`${family}-${step}`))
+      );
+      const data = Array.from(
+        { length: 10 },
+        (_, index) => value(`data-${index + 1}`),
+      );
+      const materials = [
+        'paper',
+        'washi',
+        'linen',
+        'sumi',
+        'brick',
+        'persimmon',
+        'ochre',
+        'moss',
+        'patina',
+        'indigo',
+        'plum',
+        'clay',
+        'wood',
+      ].map(value);
+      const lightMuted = systemValue('color-surface-subtle');
+      root.dataset.zenTheme = 'dark';
+      const darkMuted = systemValue('color-surface-subtle');
+      const darkData = Array.from(
+        { length: 10 },
+        (_, index) => value(`data-${index + 1}`),
+      );
+      const themes = ['paper', 'ink', 'playful'].map(theme => {
+        root.dataset.zenTheme = theme;
+        const channels = value => value.match(/\d+(?:\.\d+)?/g)
+          .slice(0, 3)
+          .map(Number);
+        const luminance = value => {
+          const converted = channels(value).map(channel => {
+            const normalized = channel / 255;
+            return normalized <= 0.04045
+              ? normalized / 12.92
+              : ((normalized + 0.055) / 1.055) ** 2.4;
+          });
+          return converted[0] * 0.2126 +
+            converted[1] * 0.7152 +
+            converted[2] * 0.0722;
+        };
+        const contrasts = [
+          'info',
+          'success',
+          'warning',
+          'danger',
+        ].map(tone => {
+          const probe = document.createElement('span');
+          probe.style.background =
+            `var(--zenbu-sys-color-${tone}-background)`;
+          probe.style.color =
+            `var(--zenbu-sys-color-${tone}-foreground)`;
+          document.body.append(probe);
+          const probeStyle = getComputedStyle(probe);
+          const foreground = luminance(probeStyle.color);
+          const background = luminance(probeStyle.backgroundColor);
+          const contrast = (Math.max(foreground, background) + 0.05) /
+            (Math.min(foreground, background) + 0.05);
+          probe.remove();
+          return contrast;
+        });
+        return {
+          canvas: systemValue('color-surface-page'),
+          contrast: Math.min(...contrasts),
+          font: getComputedStyle(root)
+            .getPropertyValue('--zenbu-sys-font-family-ui')
+            .trim(),
+          name: theme,
+          radius: getComputedStyle(root)
+            .getPropertyValue('--zenbu-sys-radius-container')
+            .trim(),
+        };
+      });
+      delete root.dataset.zenTheme;
+      return {
+        darkData,
+        darkMuted,
+        data,
+        lightMuted,
+        materials,
+        rampsComplete,
+        themes,
+      };
+    });
+    assert.equal(colorTokenState.rampsComplete, true);
+    assert.equal(new Set(colorTokenState.materials).size, 13);
+    assert.equal(new Set(colorTokenState.data).size, 10);
+    assert.equal(new Set(colorTokenState.darkData).size, 10);
+    assert.notDeepEqual(colorTokenState.data, colorTokenState.darkData);
+    assert.equal(
+      new Set(colorTokenState.themes.map(theme => theme.canvas)).size,
+      3,
+    );
+    assert.notEqual(
+      colorTokenState.themes.find(theme => theme.name === 'paper').radius,
+      colorTokenState.themes.find(theme => theme.name === 'playful').radius,
+    );
+    assert.ok(
+      colorTokenState.themes.every(theme => theme.contrast >= 4.5),
+      JSON.stringify(colorTokenState.themes),
+    );
+    assert.notEqual(
+      colorTokenState.lightMuted,
+      colorTokenState.darkMuted,
+    );
+    assert.equal(await page.locator('.palette-grid > div').count(), 13);
+    assert.equal(await page.locator('.data-palette > span').count(), 10);
+    await page.evaluate(() => {
+      document.documentElement.dataset.zenTheme = 'playful';
+    });
+    await page.emulateMedia({ forcedColors: 'active' });
+    assert.equal(
+      await page.evaluate(() =>
+        getComputedStyle(document.documentElement)
+          .getPropertyValue('--zenbu-sys-color-surface-page')
+          .trim()
+          .toLowerCase()
+      ),
+      'canvas',
+    );
+    await page.emulateMedia({ forcedColors: 'none' });
+    await page.evaluate(() => {
+      delete document.documentElement.dataset.zenTheme;
+    });
+
+    await page.goto(`${baseUrl}/components/field`, {
+      waitUntil: 'networkidle',
+    });
+    const fieldWiring = await page.locator('zen-field').first().evaluate(field => {
+      const label = field.querySelector('label');
+      const input = field.querySelector('input');
+      const help = field.querySelector('small');
+      input.checkValidity();
+      return {
+        describedBy: input.getAttribute('aria-describedby'),
+        helpId: help.id,
+        inputId: input.id,
+        invalid: field.hasAttribute('data-invalid'),
+        labelFor: label.htmlFor,
+      };
+    });
+    assert.ok(fieldWiring.inputId);
+    assert.equal(fieldWiring.labelFor, fieldWiring.inputId);
+    assert.equal(fieldWiring.describedBy, fieldWiring.helpId);
+    assert.equal(fieldWiring.invalid, true);
+    await page.locator('zen-field input').first().focus();
+    assert.equal(
+      await page.locator('zen-field input').first().evaluate(
+        input => getComputedStyle(input).outlineStyle,
+      ),
+      'none',
+    );
+    const replacementField = await page.locator('zen-field').first().evaluate(
+      async field => {
+        const label = field.querySelector('label');
+        const oldInput = field.querySelector('input');
+        const blocker = document.createElement('div');
+        blocker.id = 'zen-field-3';
+        document.body.append(blocker);
+        const nextInput = document.createElement('input');
+        nextInput.required = true;
+        oldInput.replaceWith(nextInput);
+        await new Promise(resolve => setTimeout(resolve));
+        oldInput.dispatchEvent(new Event('invalid'));
+        const help = field.querySelector('small');
+        nextInput.id = 'replacement-email';
+        help.id = 'replacement-help';
+        await new Promise(resolve => setTimeout(resolve));
+        return {
+          describedBy: nextInput.getAttribute('aria-describedby'),
+          helpId: help.id,
+          labelFor: label.htmlFor,
+          nextId: nextInput.id,
+          blockerId: blocker.id,
+        };
+      },
+    );
+    assert.ok(replacementField.nextId);
+    assert.notEqual(replacementField.nextId, replacementField.blockerId);
+    assert.equal(replacementField.labelFor, replacementField.nextId);
+    assert.equal(replacementField.describedBy, replacementField.helpId);
+
+    await page.goto(`${baseUrl}/components/alert`, {
+      waitUntil: 'networkidle',
+    });
+    await page.evaluate(() => {
+      window.__dismissed = 0;
+      document.addEventListener('zen-dismiss', () => {
+        window.__dismissed++;
+      });
+    });
+    const dismissible = page.locator('zen-alert[dismissible]');
+    assert.equal(await dismissible.getAttribute('role'), 'alert');
+    await dismissible.locator('[data-zen-dismiss]').click();
+    assert.equal(await dismissible.count(), 0);
+    assert.equal(await page.evaluate(() => window.__dismissed), 1);
+    const authoredDismissState = await page.evaluate(async () => {
+      const alert = document.createElement('zen-alert');
+      alert.setAttribute('dismissible', '');
+      const message = document.createElement('p');
+      message.textContent = 'Authored dismiss control';
+      const dismiss = document.createElement('button');
+      dismiss.dataset.zenDismiss = '';
+      alert.append(message, dismiss);
+      document.body.append(alert);
+      await new Promise(resolve => setTimeout(resolve));
+      let events = 0;
+      alert.addEventListener('zen-dismiss', () => events++);
+      alert.removeAttribute('dismissible');
+      dismiss.click();
+      return {
+        connected: alert.isConnected,
+        events,
+      };
+    });
+    assert.equal(authoredDismissState.connected, true);
+    assert.equal(authoredDismissState.events, 0);
+
+    await page.goto(`${baseUrl}/components/accordion`, {
+      waitUntil: 'networkidle',
+    });
+    const accordion = page.locator('zen-accordion').first();
+    const disclosureIcon = accordion.locator(
+      'summary > zen-icon[data-zen-disclosure-icon]',
+    ).nth(1);
+    const collapsedDisclosure = await disclosureIcon.evaluate(icon => {
+      const style = getComputedStyle(icon);
+      return {
+        duration: style.transitionDuration,
+        transform: style.transform,
+      };
+    });
+    assert.notEqual(collapsedDisclosure.duration, '0s');
+    await accordion.locator('summary').nth(1).click();
+    await page.waitForTimeout(250);
+    assert.notEqual(
+      await disclosureIcon.evaluate(icon => getComputedStyle(icon).transform),
+      collapsedDisclosure.transform,
+    );
+    assert.equal(
+      await accordion.locator('details').first().getAttribute('open'),
+      null,
+    );
+    assert.equal(
+      await accordion.locator('details').nth(1).getAttribute('open'),
+      '',
+    );
+    await accordion.locator('summary').nth(1).press('ArrowUp');
+    assert.equal(
+      await accordion.locator('summary').first().evaluate(
+        summary => document.activeElement === summary,
+      ),
+      true,
+    );
+
+    await page.goto(`${baseUrl}/components/aspect-ratio`, {
+      waitUntil: 'networkidle',
+    });
+    const aspectColors = await page.locator('.demo-media').evaluate(media => {
+      const style = getComputedStyle(media);
+      const probe = document.createElement('span');
+      probe.style.background = 'var(--zenbu-sys-color-surface-subtle)';
+      document.body.append(probe);
+      const token = getComputedStyle(probe).backgroundColor;
+      probe.remove();
+      return {
+        background: style.backgroundColor,
+        image: style.backgroundImage,
+        token,
+      };
+    });
+    assert.equal(aspectColors.image, 'none');
+    assert.equal(aspectColors.background, aspectColors.token);
+
+    await page.goto(`${baseUrl}/components/tabs`, {
+      waitUntil: 'networkidle',
+    });
+    await page.evaluate(() => {
+      window.__tabValue = null;
+      document.querySelector('zen-tabs').addEventListener(
+        'zen-change',
+        event => {
+          window.__tabValue = event.detail.value;
+        },
+      );
+    });
+    await page.locator('zen-tabs [role="tab"]').nth(1).click();
+    assert.equal(
+      await page.locator('zen-tabs [role="tab"]').nth(1)
+        .getAttribute('aria-selected'),
+      'true',
+    );
+    assert.equal(
+      await page.locator('zen-tabs [role="tabpanel"]').nth(1)
+        .getAttribute('hidden'),
+      null,
+    );
+    assert.equal(await page.evaluate(() => window.__tabValue), 'history');
+
+    await page.goto(`${baseUrl}/components/dialog`, {
+      waitUntil: 'networkidle',
+    });
+    await page.locator('zen-dialog [data-zen-trigger]').click();
+    assert.equal(await page.locator('zen-dialog dialog').getAttribute('open'), '');
+    await page.locator('zen-dialog [data-zen-close]').click();
+    assert.equal(await page.locator('zen-dialog dialog').getAttribute('open'), null);
+    const dynamicOverlays = await page.evaluate(async () => {
+      const ownAction = button => {
+        const owner = document.createElement('zen-button');
+        owner.setAttribute('size', 'md');
+        owner.append(button);
+        return owner;
+      };
+      const dialogHost = document.createElement('zen-dialog');
+      const popoverHost = document.createElement('zen-popover');
+      const tooltipHost = document.createElement('zen-tooltip');
+      const menuHost = document.createElement('zen-dropdown-menu');
+      document.body.append(dialogHost, popoverHost, tooltipHost, menuHost);
+
+      const dialogTrigger = document.createElement('button');
+      dialogTrigger.type = 'button';
+      dialogTrigger.dataset.zenTrigger = '';
+      const dialog = document.createElement('dialog');
+      dialogHost.append(ownAction(dialogTrigger), dialog);
+
+      const popoverTrigger = document.createElement('button');
+      popoverTrigger.type = 'button';
+      popoverTrigger.dataset.zenTrigger = '';
+      const popover = document.createElement('div');
+      popover.dataset.zenContent = '';
+      popoverHost.append(ownAction(popoverTrigger), popover);
+
+      const tooltipTrigger = document.createElement('button');
+      tooltipTrigger.type = 'button';
+      tooltipTrigger.dataset.zenTrigger = '';
+      const tooltip = document.createElement('span');
+      tooltip.dataset.zenContent = '';
+      tooltipHost.append(ownAction(tooltipTrigger), tooltip);
+
+      const menuTrigger = document.createElement('button');
+      menuTrigger.type = 'button';
+      menuTrigger.dataset.zenTrigger = '';
+      const menu = document.createElement('div');
+      menu.setAttribute('role', 'menu');
+      const item = document.createElement('button');
+      item.type = 'button';
+      item.setAttribute('role', 'menuitem');
+      menu.append(item);
+      menuHost.append(menuTrigger, menu);
+
+      await new Promise(resolve => setTimeout(resolve));
+      dialogTrigger.click();
+      const dialogOpen = dialog.open;
+      dialog.close();
+      popoverTrigger.click();
+      menuTrigger.click();
+      tooltipTrigger.focus();
+      await new Promise(resolve => setTimeout(resolve, 375));
+      return {
+        dialogControlled: Boolean(dialogTrigger.getAttribute('aria-controls')),
+        dialogOpen,
+        menuOpen: !menu.hidden,
+        popoverExpanded: popoverTrigger.getAttribute('aria-expanded'),
+        tooltipOpen: !tooltip.hidden,
+      };
+    });
+    assert.deepEqual(dynamicOverlays, {
+      dialogControlled: true,
+      dialogOpen: true,
+      menuOpen: true,
+      popoverExpanded: 'true',
+      tooltipOpen: true,
+    });
+
+    await page.goto(`${baseUrl}/components/dropdown-menu`, {
+      waitUntil: 'networkidle',
+    });
+    const dropdownTrigger = page.locator(
+      'zen-dropdown-menu [data-zen-trigger]',
+    );
+    await dropdownTrigger.click();
+    assert.equal(
+      await page.locator('zen-dropdown-menu [role="menu"]')
+        .getAttribute('hidden'),
+      null,
+    );
+    await page.keyboard.press('Escape');
+    assert.equal(
+      await page.locator('zen-dropdown-menu [role="menu"]')
+        .getAttribute('hidden'),
+      '',
+    );
+    assert.equal(
+      await dropdownTrigger.evaluate(
+        trigger => document.activeElement === trigger,
+      ),
+      true,
+    );
+    await dropdownTrigger.click();
+    await page.locator('zen-dropdown-menu [role="menuitem"]').first().click();
+    assert.equal(
+      await dropdownTrigger.evaluate(
+        trigger => document.activeElement === trigger,
+      ),
+      true,
+    );
+
+    await page.goto(`${baseUrl}/components/combobox`, {
+      waitUntil: 'networkidle',
+    });
+    const comboboxInput = page.locator('zen-combobox input');
+    await comboboxInput.fill('seo');
+    assert.equal(
+      await page.locator('zen-combobox [role="option"]:not([hidden])').count(),
+      1,
+    );
+    await comboboxInput.press('ArrowDown');
+    await comboboxInput.press('Enter');
+    assert.equal(await comboboxInput.inputValue(), 'seobeo');
+    assert.equal(
+      await page.locator('zen-combobox [role="listbox"]')
+        .getAttribute('hidden'),
+      '',
+    );
+
+    await page.goto(`${baseUrl}/components/date-picker`, {
+      waitUntil: 'networkidle',
+    });
+    const dateInput = page.locator('zen-date-picker input[type="date"]');
+    assert.equal(await dateInput.getAttribute('aria-hidden'), 'true');
+    assert.equal(
+      await dateInput.evaluate(input => getComputedStyle(input).position),
+      'absolute',
+    );
+    await page.evaluate(() => {
+      window.__dateValue = null;
+      document.querySelector('zen-date-picker').addEventListener(
+        'zen-change',
+        event => {
+          window.__dateValue = event.detail.value;
+        },
+      );
+    });
+    const dateTrigger = page.locator(
+      'zen-date-picker [data-zen-date-trigger]',
+    );
+    await dateTrigger.click();
+    assert.equal(
+      await page.locator('zen-date-picker [data-zen-calendar-panel]')
+        .getAttribute('hidden'),
+      null,
+    );
+    assert.equal(
+      await page.locator(
+        'zen-date-picker [data-zen-calendar-day]',
+      ).count(),
+      42,
+    );
+    const chosenDate = await page.locator(
+      'zen-date-picker [data-zen-calendar-day]:not([data-outside]):not(:disabled)',
+    ).nth(14).getAttribute('data-zen-calendar-day');
+    await page.locator(
+      `zen-date-picker [data-zen-calendar-day="${chosenDate}"]`,
+    ).click();
+    assert.equal(await dateInput.inputValue(), chosenDate);
+    assert.equal(await page.evaluate(() => window.__dateValue), chosenDate);
+    assert.equal(await dateTrigger.getAttribute('aria-expanded'), 'false');
+    const triggerName = await dateTrigger.evaluate(trigger =>
+      (trigger.getAttribute('aria-labelledby') || '')
+        .split(/\s+/)
+        .map(id => document.getElementById(id)?.textContent || '')
+        .join(' ')
+    );
+    assert.match(triggerName, /Deploy on/);
+    assert.equal(
+      await page.locator('zen-date-picker > label').getAttribute('for'),
+      await dateTrigger.getAttribute('id'),
+    );
+    const minimumDate = `${chosenDate.slice(0, 8)}10`;
+    await dateInput.evaluate((input, minimum) => {
+      input.min = minimum;
+    }, minimumDate);
+    await page.waitForTimeout(0);
+    await dateTrigger.click();
+    const focusedBoundary = page.locator(
+      `zen-date-picker [data-zen-calendar-day="${chosenDate}"]`,
+    );
+    await focusedBoundary.press('PageUp');
+    assert.equal(
+      await page.evaluate(() =>
+        document.activeElement?.dataset.zenCalendarDay
+      ),
+      minimumDate,
+    );
+    assert.equal(
+      await page.locator(
+        'zen-date-picker [data-zen-calendar-day][tabindex="0"]:not(:disabled)',
+      ).count(),
+      1,
+    );
+    await page.keyboard.press('Escape');
+    await dateInput.evaluate(input => {
+      input.disabled = true;
+    });
+    await page.waitForTimeout(0);
+    assert.equal(await dateTrigger.isDisabled(), true);
+    await dateInput.evaluate(input => {
+      input.disabled = false;
+      input.readOnly = true;
+    });
+    await page.waitForTimeout(0);
+    assert.equal(await dateTrigger.isDisabled(), true);
+    await dateInput.evaluate(input => {
+      input.readOnly = false;
+      input.required = true;
+      input.value = '';
+      input.dispatchEvent(new Event('input', { bubbles: true }));
+      input.reportValidity();
+    });
+    await page.waitForTimeout(0);
+    assert.equal(await dateTrigger.getAttribute('aria-invalid'), 'true');
+    assert.equal(
+      await dateTrigger.evaluate(
+        trigger => document.activeElement === trigger,
+      ),
+      true,
+    );
+    assert.equal(
+      await page.locator('zen-date-picker').getAttribute('data-invalid'),
+      '',
+    );
+    await dateInput.evaluate((input, value) => {
+      input.value = value;
+      input.dispatchEvent(new Event('input', { bubbles: true }));
+    }, chosenDate);
+    assert.equal(await dateTrigger.getAttribute('aria-invalid'), 'false');
+    const earlyYear = await page.evaluate(async () => {
+      const calendar = document.createElement('zen-calendar');
+      const input = document.createElement('input');
+      input.type = 'date';
+      input.value = '0001-01-01';
+      input.setAttribute('aria-label', 'Early date');
+      calendar.append(input);
+      document.body.append(calendar);
+      await new Promise(resolve => setTimeout(resolve));
+      return {
+        selected: calendar.querySelector(
+          '[data-zen-calendar-day][aria-selected="true"]',
+        )?.dataset.zenCalendarDay,
+        value: input.value,
+      };
+    });
+    assert.deepEqual(earlyYear, {
+      selected: '0001-01-01',
+      value: '0001-01-01',
+    });
+    const resetDateState = await page.evaluate(async () => {
+      const label = document.createElement('span');
+      label.id = 'reset-date-label';
+      label.textContent = 'Reset date';
+      const form = document.createElement('form');
+      const picker = document.createElement('zen-date-picker');
+      const input = document.createElement('input');
+      input.type = 'date';
+      input.defaultValue = '2026-08-04';
+      input.setAttribute('aria-labelledby', label.id);
+      picker.append(input);
+      form.append(picker);
+      document.body.append(label, form);
+      await new Promise(resolve => setTimeout(resolve));
+      input.value = '2026-08-12';
+      input.dispatchEvent(new Event('input', { bubbles: true }));
+      form.reset();
+      await new Promise(resolve => setTimeout(resolve));
+      const trigger = picker.querySelector('[data-zen-date-trigger]');
+      return {
+        labelledBy: trigger.getAttribute('aria-labelledby'),
+        selected: picker.querySelector(
+          '[data-zen-calendar-day][aria-selected="true"]',
+        )?.dataset.zenCalendarDay,
+        value: input.value,
+      };
+    });
+    assert.match(resetDateState.labelledBy, /^reset-date-label /);
+    assert.deepEqual(
+      {
+        selected: resetDateState.selected,
+        value: resetDateState.value,
+      },
+      {
+        selected: '2026-08-04',
+        value: '2026-08-04',
+      },
+    );
+
+    await page.goto(`${baseUrl}/components/input-otp`, {
+      waitUntil: 'networkidle',
+    });
+    await page.evaluate(() => {
+      window.__otpValue = null;
+      document.querySelector('zen-input-otp').addEventListener(
+        'zen-complete',
+        event => {
+          window.__otpValue = event.detail.value;
+        },
+      );
+    });
+    await page.locator('zen-input-otp input').evaluate(input => {
+      input.value = '12a3456';
+      input.dispatchEvent(new Event('input', { bubbles: true }));
+    });
+    assert.equal(await page.locator('zen-input-otp input').inputValue(), '123456');
+    assert.equal(
+      await page.locator('zen-input-otp [data-zen-otp-slots] > span').count(),
+      6,
+    );
+    const otp = page.locator('zen-input-otp');
+    await otp.evaluate(host => host.setAttribute('size', 'xs'));
+    const compactOtpSize = await otp.locator(
+      '[data-zen-otp-slots] > span',
+    ).first().evaluate(slot => slot.getBoundingClientRect().width);
+    await otp.evaluate(host => host.setAttribute('size', 'xl'));
+    const largeOtpSize = await otp.locator(
+      '[data-zen-otp-slots] > span',
+    ).first().evaluate(slot => slot.getBoundingClientRect().width);
+    assert.ok(largeOtpSize > compactOtpSize);
+    assert.equal(await page.evaluate(() => window.__otpValue), '123456');
+
+    await page.goto(`${baseUrl}/components/data-table`, {
+      waitUntil: 'networkidle',
+    });
+    await page.locator('zen-data-table [data-zen-sort="commits"]').click();
+    assert.equal(
+      await page.locator('zen-data-table th').nth(1).getAttribute('aria-sort'),
+      'ascending',
+    );
+    assert.equal(
+      await page.locator('zen-data-table tbody tr').first().locator(
+        '[data-key="commits"]',
+      ).textContent(),
+      '12',
+    );
+
+    await page.goto(`${baseUrl}/components/carousel`, {
+      waitUntil: 'networkidle',
+    });
+    await page.evaluate(() => {
+      window.__carouselIndex = null;
+      document.querySelector('zen-carousel').addEventListener(
+        'zen-change',
+        event => {
+          window.__carouselIndex = event.detail.index;
+        },
+      );
+    });
+    await page.locator('zen-carousel [data-zen-next]').click();
+    assert.equal(await page.evaluate(() => window.__carouselIndex), 1);
+    assert.equal(
+      await page.locator('zen-carousel [data-zen-prev]').isDisabled(),
+      false,
+    );
+
+    await page.goto(`${baseUrl}/components/resizable`, {
+      waitUntil: 'networkidle',
+    });
+    const resizeHandle = page.locator('zen-resizable [data-zen-handle]');
+    const initialPercent = Number(
+      await resizeHandle.getAttribute('aria-valuenow'),
+    );
+    await resizeHandle.focus();
+    await resizeHandle.press('ArrowRight');
+    assert.ok(
+      Number(await resizeHandle.getAttribute('aria-valuenow')) > initialPercent,
+    );
+
+    await page.goto(`${baseUrl}/components/switch`, {
+      waitUntil: 'networkidle',
+    });
+    const switchInput = page.locator('zen-switch input');
+    assert.equal(await switchInput.getAttribute('role'), 'switch');
+    assert.equal(await switchInput.getAttribute('aria-checked'), 'true');
+    await switchInput.uncheck();
+    assert.equal(await switchInput.getAttribute('aria-checked'), 'false');
+
+    await page.goto(`${baseUrl}/components/checkbox`, {
+      waitUntil: 'networkidle',
+    });
+    assert.equal(
+      await page.locator('zen-checkbox input').evaluate(
+        input => getComputedStyle(input).appearance,
+      ),
+      'none',
+    );
+    await page.goto(`${baseUrl}/components/native-select`, {
+      waitUntil: 'networkidle',
+    });
+    assert.equal(
+      await page.locator('zen-native-select select').evaluate(
+        select => getComputedStyle(select).appearance,
+      ),
+      'none',
+    );
+    assert.equal(
+      await page.locator(
+        'zen-native-select > zen-icon[data-zen-select-icon]',
+      ).count(),
+      1,
+    );
+    await page.goto(`${baseUrl}/components/slider`, {
+      waitUntil: 'networkidle',
+    });
+    assert.equal(
+      await page.locator('zen-slider input').evaluate(
+        input => getComputedStyle(input).appearance,
+      ),
+      'none',
+    );
+
+    await page.goto(`${baseUrl}/components/toggle-group`, {
+      waitUntil: 'networkidle',
+    });
+    await page.locator('zen-toggle-group button[value="right"]').click();
+    assert.equal(
+      await page.locator('zen-toggle-group button[value="right"]')
+        .getAttribute('aria-pressed'),
+      'true',
+    );
+    assert.equal(
+      await page.locator('zen-toggle-group').getAttribute('value'),
+      'right',
+    );
+
+    await page.goto(`${baseUrl}/components/sidebar`, {
+      waitUntil: 'networkidle',
+    });
+    const sidebarTrigger = page.locator(
+      'zen-sidebar [data-zen-sidebar-trigger]',
+    );
+    const sidebarPanel = page.locator(
+      'zen-sidebar [data-zen-sidebar-panel]',
+    );
+    assert.equal(await sidebarTrigger.getAttribute('aria-expanded'), 'true');
+    assert.equal(await sidebarPanel.getAttribute('hidden'), null);
+    await sidebarTrigger.click();
+    assert.equal(await sidebarTrigger.getAttribute('aria-expanded'), 'false');
+    assert.equal(await sidebarPanel.getAttribute('hidden'), '');
+
+    await page.goto(`${baseUrl}/components/notifications`, {
+      waitUntil: 'networkidle',
+    });
+    const notificationScope = page.locator('zen-notifications').first();
+    const notificationPosition = await notificationScope.locator(
+      '[data-zen-notification-stack]',
+    ).evaluate(stack => {
+      const style = getComputedStyle(stack);
+      return {
+        bottom: style.bottom,
+        position: style.position,
+        right: style.right,
+      };
+    });
+    assert.equal(notificationPosition.position, 'fixed');
+    assert.notEqual(notificationPosition.bottom, 'auto');
+    assert.notEqual(notificationPosition.right, 'auto');
+
+    const dedupe = await notificationScope.evaluate(scope => {
+      const source = scope.querySelector('[data-notification-demo]');
+      const send = detail => source.dispatchEvent(
+        new CustomEvent('zen-notify', {
+          bubbles: true,
+          composed: false,
+          detail,
+        }),
+      );
+      send({
+        version: 1,
+        id: 'dedupe',
+        tone: 'info',
+        message: 'First announcement',
+        announcement: 'polite',
+        persistent: true,
+      });
+      send({
+        version: 1,
+        id: 'dedupe',
+        tone: 'success',
+        message: 'Updated without duplication',
+        announcement: 'polite',
+        persistent: true,
+      });
+      send({ version: 99, id: 'invalid' });
+      return {
+        size: scope.size,
+        visible: scope.visibleCount,
+      };
+    });
+    assert.deepEqual(dedupe, { size: 1, visible: 1 });
+    assert.equal(
+      await notificationScope.locator('article').count(),
+      1,
+    );
+    assert.match(
+      await notificationScope.locator('article').textContent(),
+      /Updated without duplication/,
+    );
+    await page.waitForFunction(() =>
+      document.querySelector(
+        'zen-notifications [data-zen-live="polite"]',
+      )?.textContent === 'First announcement'
+    );
+
+    const bounded = await notificationScope.evaluate(scope => {
+      let accepted = 0;
+      for (let index = 0; index < 25; index++) {
+        if (scope.notify({
+          version: 1,
+          id: `bounded-${index}`,
+          tone: 'info',
+          message: `Bounded ${index}`,
+          announcement: 'none',
+          durationMs: 120000,
+        })) accepted++;
+      }
+      return {
+        accepted,
+        size: scope.size,
+        visible: scope.visibleCount,
+      };
+    });
+    assert.deepEqual(bounded, { accepted: 25, size: 23, visible: 3 });
+    assert.equal(
+      await notificationScope.locator('article').count(),
+      3,
+    );
+    await page.waitForFunction(() =>
+      [...document.querySelectorAll(
+        'zen-notifications article',
+      )].some(article => article.dataset.depth === '2')
+    );
+    const collapsedStack = await notificationScope.locator(
+      '[data-zen-notification-stack]',
+    ).evaluate(stack => {
+      const articles = [...stack.querySelectorAll('article')];
+      return {
+        depths: articles.map(article => article.dataset.depth),
+        height: stack.getBoundingClientRect().height,
+        positions: articles.map(article =>
+          getComputedStyle(article).position
+        ),
+        transforms: articles.map(article =>
+          getComputedStyle(article).transform
+        ),
+      };
+    });
+    assert.deepEqual(collapsedStack.depths, ['2', '1', '0']);
+    assert.deepEqual(
+      collapsedStack.positions,
+      ['absolute', 'absolute', 'absolute'],
+    );
+    await notificationScope.locator('article').last().hover();
+    await page.waitForTimeout(300);
+    const expandedStack = await notificationScope.locator(
+      '[data-zen-notification-stack]',
+    ).evaluate(stack => ({
+      height: stack.getBoundingClientRect().height,
+      rects: [...stack.querySelectorAll('article')].map(article => {
+        const bounds = article.getBoundingClientRect();
+        return {
+          bottom: bounds.bottom,
+          left: bounds.left,
+          right: bounds.right,
+          top: bounds.top,
+        };
+      }),
+      tops: [...stack.querySelectorAll('article')].map(
+        article => Math.round(article.getBoundingClientRect().top),
+      ),
+      transforms: [...stack.querySelectorAll('article')].map(
+        article => getComputedStyle(article).transform,
+      ),
+    }));
+    assert.ok(expandedStack.height > collapsedStack.height);
+    assert.equal(new Set(expandedStack.tops).size, 3);
+    const expandedGaps = [
+      expandedStack.rects[1].top - expandedStack.rects[0].bottom,
+      expandedStack.rects[2].top - expandedStack.rects[1].bottom,
+    ];
+    for (const gap of expandedGaps) {
+      assert.ok(gap >= 10 && gap <= 14, JSON.stringify(expandedGaps));
+    }
+    assert.notDeepEqual(
+      expandedStack.transforms,
+      collapsedStack.transforms,
+    );
+    const gapX =
+      (expandedStack.rects[0].left + expandedStack.rects[0].right) / 2;
+    const gapY =
+      (expandedStack.rects[0].bottom + expandedStack.rects[1].top) / 2;
+    await page.mouse.move(gapX, gapY);
+    await page.waitForTimeout(100);
+    const gapState = await notificationScope.locator(
+      '[data-zen-notification-stack]',
+    ).evaluate(stack => ({
+      height: stack.getBoundingClientRect().height,
+      hovered: stack.matches(':hover'),
+    }));
+    assert.equal(gapState.hovered, true);
+    assert.ok(gapState.height >= expandedStack.height - 1);
+    await page.mouse.move(0, 0);
+
+    const actionScope = await page.evaluate(() => {
+      const scope = document.createElement('zen-notifications');
+      scope.id = 'action-scope';
+      const source = document.createElement('button');
+      scope.append(source);
+      document.body.append(scope);
+      window.__notificationAction = null;
+      scope.addEventListener('zen-notification-action', event => {
+        window.__notificationAction = event.detail;
+      });
+      source.dispatchEvent(new CustomEvent('zen-notify', {
+        bubbles: true,
+        composed: false,
+        detail: {
+          version: 1,
+          id: 'action',
+          tone: 'error',
+          message: 'Action required',
+          announcement: 'assertive',
+          persistent: true,
+          action: {
+            token: 'opaque-secret-token',
+            label: 'Retry',
+          },
+        },
+      }));
+      return scope.id;
+    });
+    assert.equal(
+      await page.locator(`#${actionScope}`).evaluate(
+        scope => scope.records,
+      ),
+      undefined,
+    );
+    const actionArticle = page.locator(
+      `#${actionScope} [data-zen-notification-id="action"]`,
+    );
+    assert.doesNotMatch(
+      await actionArticle.evaluate(article => article.outerHTML),
+      /opaque-secret-token/,
+    );
+    await actionArticle.locator('.zen-notification-action').focus();
+    await page.locator(`#${actionScope}`).evaluate(scope => {
+      const source = scope.querySelector('button');
+      source.dispatchEvent(new CustomEvent('zen-notify', {
+        bubbles: true,
+        composed: false,
+        detail: {
+          version: 1,
+          id: 'action-neighbor',
+          tone: 'info',
+          message: 'Neighbor',
+          announcement: 'none',
+          persistent: true,
+        },
+      }));
+    });
+    assert.equal(
+      await actionArticle.locator('.zen-notification-action').evaluate(
+        action => document.activeElement === action,
+      ),
+      true,
+    );
+    await actionArticle.locator('.zen-notification-action').click();
+    assert.deepEqual(
+      await page.evaluate(() => window.__notificationAction),
+      {
+        version: 1,
+        id: 'action',
+        token: 'opaque-secret-token',
+      },
+    );
+    await page.locator(`#${actionScope}`).evaluate(scope => {
+      const source = scope.querySelector('button');
+      source.dispatchEvent(new CustomEvent(
+        'zen-dismiss-notification',
+        {
+          bubbles: true,
+          composed: false,
+          detail: { version: 1, id: 'action' },
+        },
+      ));
+    });
+    await actionArticle.waitFor({ state: 'detached' });
+
+    await page.evaluate(() => {
+      const scope = document.createElement('zen-notifications');
+      scope.id = 'announcement-scope';
+      const source = document.createElement('button');
+      scope.append(source);
+      document.body.append(scope);
+      window.__announcements = [];
+      const region = scope.querySelector('[data-zen-live="polite"]');
+      new MutationObserver(() => {
+        if (region.textContent) {
+          window.__announcements.push(region.textContent);
+        }
+      }).observe(region, { childList: true });
+      for (let index = 0; index < 3; index++) {
+        source.dispatchEvent(new CustomEvent('zen-notify', {
+          bubbles: true,
+          composed: false,
+          detail: {
+            version: 1,
+            id: `announcement-${index}`,
+            tone: 'info',
+            message: `Announcement ${index}`,
+            announcement: 'polite',
+            persistent: true,
+          },
+        }));
+      }
+    });
+    await page.waitForFunction(() =>
+      window.__announcements?.length === 3
+    );
+    assert.deepEqual(
+      await page.evaluate(() => window.__announcements),
+      ['Announcement 0', 'Announcement 1', 'Announcement 2'],
+    );
+
+    const composedRejected = await page.evaluate(() => {
+      const scope = document.createElement('zen-notifications');
+      const source = document.createElement('button');
+      scope.append(source);
+      document.body.append(scope);
+      source.dispatchEvent(new CustomEvent('zen-notify', {
+        bubbles: true,
+        composed: true,
+        detail: {
+          version: 1,
+          id: 'composed',
+          tone: 'info',
+          message: 'Must be rejected',
+          announcement: 'none',
+          persistent: true,
+        },
+      }));
+      return scope.size;
+    });
+    assert.equal(composedRejected, 0);
+
+    await page.evaluate(() => {
+      const scope = document.createElement('zen-notifications');
+      scope.id = 'focus-timer-scope';
+      const source = document.createElement('button');
+      scope.append(source);
+      document.body.append(scope);
+      source.dispatchEvent(new CustomEvent('zen-notify', {
+        bubbles: true,
+        composed: false,
+        detail: {
+          version: 1,
+          id: 'focus-timer',
+          tone: 'warning',
+          message: 'Focus timer',
+          announcement: 'none',
+          durationMs: 300,
+          action: { token: 'focus-token', label: 'Keep focused' },
+        },
+      }));
+    });
+    const focusTimer = page.locator(
+      '#focus-timer-scope [data-zen-notification-id="focus-timer"]',
+    );
+    await focusTimer.locator('.zen-notification-action').focus();
+    await page.locator('#focus-timer-scope').evaluate(scope => {
+      const source = scope.querySelector(':scope > button');
+      source.dispatchEvent(new CustomEvent('zen-notify', {
+        bubbles: true,
+        composed: false,
+        detail: {
+          version: 1,
+          id: 'focus-neighbor',
+          tone: 'info',
+          message: 'Focus neighbor',
+          announcement: 'none',
+          persistent: true,
+        },
+      }));
+    });
+    await page.waitForTimeout(500);
+    assert.equal(await focusTimer.count(), 1);
+    assert.equal(
+      await focusTimer.locator('.zen-notification-action').evaluate(
+        action => document.activeElement === action,
+      ),
+      true,
+    );
+    await page.mouse.move(0, 0);
+    await page.locator('#catalogMain').focus();
+    await page.waitForFunction(() =>
+      !document.querySelector(
+        '#focus-timer-scope [data-zen-notification-id="focus-timer"]',
+      )
+    );
+
+    await page.evaluate(() => {
+      const scope = document.createElement('zen-notifications');
+      scope.id = 'timer-scope';
+      const source = document.createElement('button');
+      scope.append(source);
+      document.body.append(scope);
+      source.dispatchEvent(new CustomEvent('zen-notify', {
+        bubbles: true,
+        composed: false,
+        detail: {
+          version: 1,
+          id: 'timer',
+          tone: 'info',
+          message: 'Paused timer',
+          announcement: 'none',
+          durationMs: 300,
+        },
+      }));
+    });
+    const timerArticle = page.locator('#timer-scope article');
+    await timerArticle.hover();
+    await page.waitForTimeout(500);
+    assert.equal(await timerArticle.count(), 1);
+    await page.mouse.move(0, 0);
+    await page.waitForFunction(() =>
+      !document.querySelector('#timer-scope article')
+    );
+
+    await page.evaluate(() => {
+      const scope = document.createElement('zen-notifications');
+      scope.id = 'hidden-timer-scope';
+      const source = document.createElement('button');
+      scope.append(source);
+      document.body.append(scope);
+      source.dispatchEvent(new CustomEvent('zen-notify', {
+        bubbles: true,
+        composed: false,
+        detail: {
+          version: 1,
+          id: 'hidden-timer',
+          tone: 'info',
+          message: 'Hidden timer',
+          announcement: 'none',
+          durationMs: 300,
+        },
+      }));
+      Object.defineProperty(document, 'hidden', {
+        configurable: true,
+        value: true,
+      });
+      document.dispatchEvent(new Event('visibilitychange'));
+    });
+    await page.waitForTimeout(500);
+    assert.equal(
+      await page.locator('#hidden-timer-scope article').count(),
+      1,
+    );
+    await page.evaluate(() => {
+      Object.defineProperty(document, 'hidden', {
+        configurable: true,
+        value: false,
+      });
+      document.dispatchEvent(new Event('visibilitychange'));
+    });
+    await page.waitForFunction(() =>
+      !document.querySelector('#hidden-timer-scope article')
+    );
+
+    const isolated = await page.evaluate(() => {
+      const makeScope = id => {
+        const scope = document.createElement('zen-notifications');
+        scope.id = id;
+        const source = document.createElement('button');
+        scope.append(source);
+        document.body.append(scope);
+        return { scope, source };
+      };
+      const first = makeScope('scope-one');
+      const second = makeScope('scope-two');
+      first.source.dispatchEvent(new CustomEvent('zen-notify', {
+        bubbles: true,
+        composed: false,
+        detail: {
+          version: 1,
+          id: 'isolated',
+          tone: 'success',
+          message: 'Only first scope',
+          announcement: 'none',
+          persistent: true,
+        },
+      }));
+      return [first.scope.size, second.scope.size];
+    });
+    assert.deepEqual(isolated, [1, 0]);
+
+    const lightCanvas = await page.evaluate(() =>
+      getComputedStyle(document.documentElement)
+        .getPropertyValue('--zenbu-sys-color-surface-page')
+        .trim()
+    );
+    await page.locator('#themeToggle').click();
+    const darkCanvas = await page.evaluate(() =>
+      getComputedStyle(document.documentElement)
+        .getPropertyValue('--zenbu-sys-color-surface-page')
+        .trim()
+    );
+    assert.notEqual(lightCanvas, darkCanvas);
+    assert.equal(
+      await page.evaluate(() => localStorage.getItem('zen-theme')),
+      'dark',
+    );
+
+    assert.deepEqual(errors, []);
+    await context.close();
+
+    const darkContext = await browser.newContext({ colorScheme: 'dark' });
+    const darkPage = await darkContext.newPage();
+    await darkPage.goto(baseUrl, { waitUntil: 'networkidle' });
+    assert.equal(
+      await darkPage.locator('#themeToggle').getAttribute('aria-pressed'),
+      'true',
+    );
+    const systemDarkCanvas = await darkPage.evaluate(() =>
+      getComputedStyle(document.documentElement)
+        .getPropertyValue('--zenbu-sys-color-surface-page')
+        .trim()
+    );
+    await darkPage.locator('#themeToggle').click();
+    const explicitLightCanvas = await darkPage.evaluate(() =>
+      getComputedStyle(document.documentElement)
+        .getPropertyValue('--zenbu-sys-color-surface-page')
+        .trim()
+    );
+    assert.notEqual(systemDarkCanvas, explicitLightCanvas);
+    await darkContext.close();
+
+    const mobileContext = await browser.newContext({
+      colorScheme: 'light',
+      viewport: { width: 390, height: 844 },
+    });
+    const mobilePage = await mobileContext.newPage();
+    const mobileErrors = [];
+    mobilePage.on('pageerror', error => {
+      mobileErrors.push(error.stack || error.message);
+    });
+    mobilePage.on('console', message => {
+      if (message.type() === 'error') mobileErrors.push(message.text());
+    });
+    await mobilePage.goto(baseUrl, { waitUntil: 'networkidle' });
+    await mobilePage.waitForFunction(() =>
+      customElements.get('zen-date-picker') &&
+      document.querySelectorAll(
+        '.catalog-nav[aria-label="Components"] a',
+      ).length === 70
+    );
+    const mobileFailures = await mobilePage.evaluate(() => {
+      const routes = [
+        '/',
+        '/tokens',
+        '/icons',
+        ...[...document.querySelectorAll(
+          '.catalog-nav[aria-label="Components"] a',
+        )].map(link => new URL(link.href).pathname),
+      ];
+      const failures = [];
+      for (const route of routes) {
+        history.replaceState({}, '', route);
+        window.dispatchEvent(new PopStateEvent('popstate'));
+        const page = document.querySelector(
+          '[data-catalog-page]:not([hidden])',
+        );
+        if (!page ||
+            document.documentElement.scrollWidth > innerWidth + 1 ||
+            page.getBoundingClientRect().right > innerWidth + 1) {
+          failures.push(route);
+        }
+      }
+      return failures;
+    });
+    assert.deepEqual(mobileFailures, []);
+    await mobilePage.goto(`${baseUrl}/components/date-picker`, {
+      waitUntil: 'networkidle',
+    });
+    await mobilePage.locator(
+      'zen-date-picker [data-zen-date-trigger]',
+    ).click();
+    const mobileCalendarBounds = await mobilePage.locator(
+      'zen-date-picker [data-zen-calendar-panel]',
+    ).evaluate(panel => {
+      const bounds = panel.getBoundingClientRect();
+      return {
+        bottom: bounds.bottom,
+        left: bounds.left,
+        right: bounds.right,
+        top: bounds.top,
+      };
+    });
+    assert.ok(mobileCalendarBounds.left >= 0);
+    assert.ok(mobileCalendarBounds.right <= 390);
+    assert.ok(mobileCalendarBounds.top >= 0);
+    assert.ok(mobileCalendarBounds.bottom <= 844);
+    assert.deepEqual(mobileErrors, []);
+    await mobileContext.close();
+  } finally {
+    if (browser) await browser.close();
+    await stopProcess(server);
+  }
+})().catch(error => {
+  console.error(error.stack || error);
+  process.exitCode = 1;
+});