You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
svelte-kit-vice/scripts/check-eidos-recipes.ts

259 lines
9.2 KiB

eidos: focus ring uses box-shadow (project kills outlines globally), dialog overlay self-positions Two bugs surfaced by Playwright verification at /test/eidos: 1) Focus ring: archetypes.css used `outline: ...` for the focus-visible ring. The project's layout.css has a Tailwind layer/components rule `*:not(body):not(.focus-override) { outline: none !important }` that kills every outline. The eidos rule was never visible. Switch to a two-layer box-shadow ring (inner transparent gap + outer color). Box-shadow follows border-radius, so the ring also looks right on pills (Switch) and rounded corners (Toggle, Dialog buttons). Ring also stands out against accent-filled states because it sits OUTSIDE the element, not on top of it. 2) Dialog overlay: the recipe relied on [data-archetype='overlay'] from archetypes.css for `position: fixed; inset: 0`, but Dialog isn't migrated to MorfoRuntime yet — its provider uses legacy createAttrs which doesn't read morfo's `archetype` field. The overlay element had `position: static, height: 0`, so the backdrop was invisible. Inline `position: fixed; inset: 0` directly in the recipe. Component recipes shouldn't depend on archetype rules for foundational behavior; archetype is a transversal extra, not a foundation. Adds scripts/check-eidos-recipes.ts — Playwright script that opens /test/eidos, tabs into Toggle/Switch, opens the dialog, prints computed styles + saves screenshots to g:/tmp/eidos. Used to catch both bugs; reproducible for future visual checks. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
/**
* Verifies eidos recipes render correctly in /uix/components.
eidos: focus ring uses box-shadow (project kills outlines globally), dialog overlay self-positions Two bugs surfaced by Playwright verification at /test/eidos: 1) Focus ring: archetypes.css used `outline: ...` for the focus-visible ring. The project's layout.css has a Tailwind layer/components rule `*:not(body):not(.focus-override) { outline: none !important }` that kills every outline. The eidos rule was never visible. Switch to a two-layer box-shadow ring (inner transparent gap + outer color). Box-shadow follows border-radius, so the ring also looks right on pills (Switch) and rounded corners (Toggle, Dialog buttons). Ring also stands out against accent-filled states because it sits OUTSIDE the element, not on top of it. 2) Dialog overlay: the recipe relied on [data-archetype='overlay'] from archetypes.css for `position: fixed; inset: 0`, but Dialog isn't migrated to MorfoRuntime yet — its provider uses legacy createAttrs which doesn't read morfo's `archetype` field. The overlay element had `position: static, height: 0`, so the backdrop was invisible. Inline `position: fixed; inset: 0` directly in the recipe. Component recipes shouldn't depend on archetype rules for foundational behavior; archetype is a transversal extra, not a foundation. Adds scripts/check-eidos-recipes.ts — Playwright script that opens /test/eidos, tabs into Toggle/Switch, opens the dialog, prints computed styles + saves screenshots to g:/tmp/eidos. Used to catch both bugs; reproducible for future visual checks. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
*
* 1. Toggle (★) — has data-archetype + focus ring on Tab.
* 2. Switch — has data-archetype + focus ring on Tab.
* 3. Dialog — opens via click, content + overlay visible, persists.
*
* Saves screenshots to /tmp/eidos-*.png so I can inspect.
*/
import { chromium } from 'playwright';
import { mkdir } from 'node:fs/promises';
const OUT = 'g:/tmp/eidos';
async function probePort(): Promise<string | null> {
for (let port = 5173; port <= 5180; port++) {
eidos: focus ring uses box-shadow (project kills outlines globally), dialog overlay self-positions Two bugs surfaced by Playwright verification at /test/eidos: 1) Focus ring: archetypes.css used `outline: ...` for the focus-visible ring. The project's layout.css has a Tailwind layer/components rule `*:not(body):not(.focus-override) { outline: none !important }` that kills every outline. The eidos rule was never visible. Switch to a two-layer box-shadow ring (inner transparent gap + outer color). Box-shadow follows border-radius, so the ring also looks right on pills (Switch) and rounded corners (Toggle, Dialog buttons). Ring also stands out against accent-filled states because it sits OUTSIDE the element, not on top of it. 2) Dialog overlay: the recipe relied on [data-archetype='overlay'] from archetypes.css for `position: fixed; inset: 0`, but Dialog isn't migrated to MorfoRuntime yet — its provider uses legacy createAttrs which doesn't read morfo's `archetype` field. The overlay element had `position: static, height: 0`, so the backdrop was invisible. Inline `position: fixed; inset: 0` directly in the recipe. Component recipes shouldn't depend on archetype rules for foundational behavior; archetype is a transversal extra, not a foundation. Adds scripts/check-eidos-recipes.ts — Playwright script that opens /test/eidos, tabs into Toggle/Switch, opens the dialog, prints computed styles + saves screenshots to g:/tmp/eidos. Used to catch both bugs; reproducible for future visual checks. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
try {
const r = await fetch(`http://localhost:${port}/`, { signal: AbortSignal.timeout(500) });
if (r.ok || r.status === 404 || r.status === 500) return `http://localhost:${port}`;
eidos: focus ring uses box-shadow (project kills outlines globally), dialog overlay self-positions Two bugs surfaced by Playwright verification at /test/eidos: 1) Focus ring: archetypes.css used `outline: ...` for the focus-visible ring. The project's layout.css has a Tailwind layer/components rule `*:not(body):not(.focus-override) { outline: none !important }` that kills every outline. The eidos rule was never visible. Switch to a two-layer box-shadow ring (inner transparent gap + outer color). Box-shadow follows border-radius, so the ring also looks right on pills (Switch) and rounded corners (Toggle, Dialog buttons). Ring also stands out against accent-filled states because it sits OUTSIDE the element, not on top of it. 2) Dialog overlay: the recipe relied on [data-archetype='overlay'] from archetypes.css for `position: fixed; inset: 0`, but Dialog isn't migrated to MorfoRuntime yet — its provider uses legacy createAttrs which doesn't read morfo's `archetype` field. The overlay element had `position: static, height: 0`, so the backdrop was invisible. Inline `position: fixed; inset: 0` directly in the recipe. Component recipes shouldn't depend on archetype rules for foundational behavior; archetype is a transversal extra, not a foundation. Adds scripts/check-eidos-recipes.ts — Playwright script that opens /test/eidos, tabs into Toggle/Switch, opens the dialog, prints computed styles + saves screenshots to g:/tmp/eidos. Used to catch both bugs; reproducible for future visual checks. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
} catch {}
}
return null;
}
async function main() {
const base = await probePort();
if (!base) {
console.error('No dev server. Run npm run dev.');
process.exit(2);
}
await mkdir(OUT, { recursive: true });
const browser = await chromium.launch();
const ctx = await browser.newContext({ viewport: { width: 900, height: 1100 } });
const page = await ctx.newPage();
page.on('console', (msg) => {
if (msg.type() === 'error') console.log(`[console error] ${msg.text()}`);
});
// ── 1. Toggle ────────────────────────────────────────────────────────────
await page.goto(`${base}/uix/components/toggle`, { waitUntil: 'networkidle' });
eidos: drop legacy hand-rolled tokens.css, consume air-translated tokens Replace the V1.5 hand-rolled motion/easing/tint/opacity/focus-ring tokens with the air-translated equivalents already in eidos/themes/ base/_static.css and eidos/themes/base/light.css. Substitutions applied to archetypes.css, events.css, components/ dialog.css: --eidos-motion-fast → --eidos-duration-fast (120ms) --eidos-motion-base → --eidos-duration-slow (320ms) --eidos-easing-standard → --eidos-ease-default --eidos-easing-decelerate → --eidos-ease-out --eidos-easing-emphasized → --eidos-ease-spring --eidos-tint-neutral → --eidos-color-neutral-element --eidos-tint-affirm → --eidos-color-success-element --eidos-tint-fulfill → --eidos-color-success-element --eidos-tint-risk → --eidos-color-warning-element --eidos-tint-threat → --eidos-color-danger-element var(--eidos-opacity-hover) → 0.85 (single-site, archetype-internal) var(--eidos-opacity-disabled) → 0.5 (single-site, archetype-internal) Focus ring tokens (--eidos-focus-ring-{color,width,offset}) unchanged — same names already exist in air's _static.css/light.css. `affirm` and `fulfill` collapse to the same `success-element` because air's intent vocabulary doesn't distinguish them. The morfo's data-intent attr still preserves the distinction at the structural layer; the visual mapping is just the same color. Updated scripts/check-eidos-recipes.ts to navigate to the per-component demo URLs (/test/eidos/{toggle,switch,dialog}) since the index page no longer renders the recipes directly. Verification: smoke 68/68, morfo-check 66/66. Playwright entrance/exit curves intact (opacity interpolates over the configured durations). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
await page.waitForTimeout(300);
eidos: focus ring uses box-shadow (project kills outlines globally), dialog overlay self-positions Two bugs surfaced by Playwright verification at /test/eidos: 1) Focus ring: archetypes.css used `outline: ...` for the focus-visible ring. The project's layout.css has a Tailwind layer/components rule `*:not(body):not(.focus-override) { outline: none !important }` that kills every outline. The eidos rule was never visible. Switch to a two-layer box-shadow ring (inner transparent gap + outer color). Box-shadow follows border-radius, so the ring also looks right on pills (Switch) and rounded corners (Toggle, Dialog buttons). Ring also stands out against accent-filled states because it sits OUTSIDE the element, not on top of it. 2) Dialog overlay: the recipe relied on [data-archetype='overlay'] from archetypes.css for `position: fixed; inset: 0`, but Dialog isn't migrated to MorfoRuntime yet — its provider uses legacy createAttrs which doesn't read morfo's `archetype` field. The overlay element had `position: static, height: 0`, so the backdrop was invisible. Inline `position: fixed; inset: 0` directly in the recipe. Component recipes shouldn't depend on archetype rules for foundational behavior; archetype is a transversal extra, not a foundation. Adds scripts/check-eidos-recipes.ts — Playwright script that opens /test/eidos, tabs into Toggle/Switch, opens the dialog, prints computed styles + saves screenshots to g:/tmp/eidos. Used to catch both bugs; reproducible for future visual checks. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
const toggle = page.locator('[data-toggle]').first();
const toggleAttrs = await toggle.evaluate((el) => ({
dataToggle: el.getAttribute('data-toggle'),
dataArchetype: el.getAttribute('data-archetype'),
tag: el.tagName.toLowerCase()
}));
console.log('Toggle attrs:', toggleAttrs);
// Click body first so we have a focus origin, then tab into the toggle.
await page.locator('body').click({ position: { x: 5, y: 5 } });
await page.waitForTimeout(50);
// Tab until we hit the toggle button (skip the Soma layout bar if present).
for (let i = 0; i < 10; i++) {
await page.keyboard.press('Tab');
const isToggle = await page.evaluate(
() => document.activeElement?.hasAttribute('data-toggle') ?? false
);
eidos: focus ring uses box-shadow (project kills outlines globally), dialog overlay self-positions Two bugs surfaced by Playwright verification at /test/eidos: 1) Focus ring: archetypes.css used `outline: ...` for the focus-visible ring. The project's layout.css has a Tailwind layer/components rule `*:not(body):not(.focus-override) { outline: none !important }` that kills every outline. The eidos rule was never visible. Switch to a two-layer box-shadow ring (inner transparent gap + outer color). Box-shadow follows border-radius, so the ring also looks right on pills (Switch) and rounded corners (Toggle, Dialog buttons). Ring also stands out against accent-filled states because it sits OUTSIDE the element, not on top of it. 2) Dialog overlay: the recipe relied on [data-archetype='overlay'] from archetypes.css for `position: fixed; inset: 0`, but Dialog isn't migrated to MorfoRuntime yet — its provider uses legacy createAttrs which doesn't read morfo's `archetype` field. The overlay element had `position: static, height: 0`, so the backdrop was invisible. Inline `position: fixed; inset: 0` directly in the recipe. Component recipes shouldn't depend on archetype rules for foundational behavior; archetype is a transversal extra, not a foundation. Adds scripts/check-eidos-recipes.ts — Playwright script that opens /test/eidos, tabs into Toggle/Switch, opens the dialog, prints computed styles + saves screenshots to g:/tmp/eidos. Used to catch both bugs; reproducible for future visual checks. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
if (isToggle) break;
}
await page.waitForTimeout(100);
const toggleFocusOutline = await toggle.evaluate((el) => {
const cs = getComputedStyle(el);
return {
matchesFocusVisible: el.matches(':focus-visible'),
outlineStyle: cs.outlineStyle,
outlineWidth: cs.outlineWidth,
boxShadow: cs.boxShadow
};
});
console.log('Toggle ring (Tab focused):', toggleFocusOutline);
await page.screenshot({ path: `${OUT}/01-toggle-focused.png`, fullPage: false });
// ── 2. Switch ────────────────────────────────────────────────────────────
await page.goto(`${base}/uix/components/switch`, { waitUntil: 'networkidle' });
eidos: drop legacy hand-rolled tokens.css, consume air-translated tokens Replace the V1.5 hand-rolled motion/easing/tint/opacity/focus-ring tokens with the air-translated equivalents already in eidos/themes/ base/_static.css and eidos/themes/base/light.css. Substitutions applied to archetypes.css, events.css, components/ dialog.css: --eidos-motion-fast → --eidos-duration-fast (120ms) --eidos-motion-base → --eidos-duration-slow (320ms) --eidos-easing-standard → --eidos-ease-default --eidos-easing-decelerate → --eidos-ease-out --eidos-easing-emphasized → --eidos-ease-spring --eidos-tint-neutral → --eidos-color-neutral-element --eidos-tint-affirm → --eidos-color-success-element --eidos-tint-fulfill → --eidos-color-success-element --eidos-tint-risk → --eidos-color-warning-element --eidos-tint-threat → --eidos-color-danger-element var(--eidos-opacity-hover) → 0.85 (single-site, archetype-internal) var(--eidos-opacity-disabled) → 0.5 (single-site, archetype-internal) Focus ring tokens (--eidos-focus-ring-{color,width,offset}) unchanged — same names already exist in air's _static.css/light.css. `affirm` and `fulfill` collapse to the same `success-element` because air's intent vocabulary doesn't distinguish them. The morfo's data-intent attr still preserves the distinction at the structural layer; the visual mapping is just the same color. Updated scripts/check-eidos-recipes.ts to navigate to the per-component demo URLs (/test/eidos/{toggle,switch,dialog}) since the index page no longer renders the recipes directly. Verification: smoke 68/68, morfo-check 66/66. Playwright entrance/exit curves intact (opacity interpolates over the configured durations). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
await page.waitForTimeout(300);
eidos: focus ring uses box-shadow (project kills outlines globally), dialog overlay self-positions Two bugs surfaced by Playwright verification at /test/eidos: 1) Focus ring: archetypes.css used `outline: ...` for the focus-visible ring. The project's layout.css has a Tailwind layer/components rule `*:not(body):not(.focus-override) { outline: none !important }` that kills every outline. The eidos rule was never visible. Switch to a two-layer box-shadow ring (inner transparent gap + outer color). Box-shadow follows border-radius, so the ring also looks right on pills (Switch) and rounded corners (Toggle, Dialog buttons). Ring also stands out against accent-filled states because it sits OUTSIDE the element, not on top of it. 2) Dialog overlay: the recipe relied on [data-archetype='overlay'] from archetypes.css for `position: fixed; inset: 0`, but Dialog isn't migrated to MorfoRuntime yet — its provider uses legacy createAttrs which doesn't read morfo's `archetype` field. The overlay element had `position: static, height: 0`, so the backdrop was invisible. Inline `position: fixed; inset: 0` directly in the recipe. Component recipes shouldn't depend on archetype rules for foundational behavior; archetype is a transversal extra, not a foundation. Adds scripts/check-eidos-recipes.ts — Playwright script that opens /test/eidos, tabs into Toggle/Switch, opens the dialog, prints computed styles + saves screenshots to g:/tmp/eidos. Used to catch both bugs; reproducible for future visual checks. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
const sw = page.locator('[data-switch]').first();
const swAttrs = await sw.evaluate((el) => ({
dataSwitch: el.getAttribute('data-switch'),
dataArchetype: el.getAttribute('data-archetype'),
dataState: el.getAttribute('data-state'),
tag: el.tagName.toLowerCase()
}));
console.log('Switch attrs:', swAttrs);
for (let i = 0; i < 5; i++) {
await page.keyboard.press('Tab');
const isSwitch = await page.evaluate(
() => document.activeElement?.hasAttribute('data-switch') ?? false
);
eidos: focus ring uses box-shadow (project kills outlines globally), dialog overlay self-positions Two bugs surfaced by Playwright verification at /test/eidos: 1) Focus ring: archetypes.css used `outline: ...` for the focus-visible ring. The project's layout.css has a Tailwind layer/components rule `*:not(body):not(.focus-override) { outline: none !important }` that kills every outline. The eidos rule was never visible. Switch to a two-layer box-shadow ring (inner transparent gap + outer color). Box-shadow follows border-radius, so the ring also looks right on pills (Switch) and rounded corners (Toggle, Dialog buttons). Ring also stands out against accent-filled states because it sits OUTSIDE the element, not on top of it. 2) Dialog overlay: the recipe relied on [data-archetype='overlay'] from archetypes.css for `position: fixed; inset: 0`, but Dialog isn't migrated to MorfoRuntime yet — its provider uses legacy createAttrs which doesn't read morfo's `archetype` field. The overlay element had `position: static, height: 0`, so the backdrop was invisible. Inline `position: fixed; inset: 0` directly in the recipe. Component recipes shouldn't depend on archetype rules for foundational behavior; archetype is a transversal extra, not a foundation. Adds scripts/check-eidos-recipes.ts — Playwright script that opens /test/eidos, tabs into Toggle/Switch, opens the dialog, prints computed styles + saves screenshots to g:/tmp/eidos. Used to catch both bugs; reproducible for future visual checks. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
if (isSwitch) break;
}
await page.waitForTimeout(100);
const swFocusOutline = await sw.evaluate((el) => {
const cs = getComputedStyle(el);
return {
matchesFocusVisible: el.matches(':focus-visible'),
boxShadow: cs.boxShadow,
width: cs.width,
height: cs.height,
background: cs.backgroundColor
};
});
console.log('Switch ring (Tab focused):', swFocusOutline);
await page.screenshot({ path: `${OUT}/02-switch-focused.png`, fullPage: false });
// ── 3. Dialog ────────────────────────────────────────────────────────────
await page.goto(`${base}/uix/components/dialog`, { waitUntil: 'networkidle' });
eidos: drop legacy hand-rolled tokens.css, consume air-translated tokens Replace the V1.5 hand-rolled motion/easing/tint/opacity/focus-ring tokens with the air-translated equivalents already in eidos/themes/ base/_static.css and eidos/themes/base/light.css. Substitutions applied to archetypes.css, events.css, components/ dialog.css: --eidos-motion-fast → --eidos-duration-fast (120ms) --eidos-motion-base → --eidos-duration-slow (320ms) --eidos-easing-standard → --eidos-ease-default --eidos-easing-decelerate → --eidos-ease-out --eidos-easing-emphasized → --eidos-ease-spring --eidos-tint-neutral → --eidos-color-neutral-element --eidos-tint-affirm → --eidos-color-success-element --eidos-tint-fulfill → --eidos-color-success-element --eidos-tint-risk → --eidos-color-warning-element --eidos-tint-threat → --eidos-color-danger-element var(--eidos-opacity-hover) → 0.85 (single-site, archetype-internal) var(--eidos-opacity-disabled) → 0.5 (single-site, archetype-internal) Focus ring tokens (--eidos-focus-ring-{color,width,offset}) unchanged — same names already exist in air's _static.css/light.css. `affirm` and `fulfill` collapse to the same `success-element` because air's intent vocabulary doesn't distinguish them. The morfo's data-intent attr still preserves the distinction at the structural layer; the visual mapping is just the same color. Updated scripts/check-eidos-recipes.ts to navigate to the per-component demo URLs (/test/eidos/{toggle,switch,dialog}) since the index page no longer renders the recipes directly. Verification: smoke 68/68, morfo-check 66/66. Playwright entrance/exit curves intact (opacity interpolates over the configured durations). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
await page.waitForTimeout(300);
eidos: focus ring uses box-shadow (project kills outlines globally), dialog overlay self-positions Two bugs surfaced by Playwright verification at /test/eidos: 1) Focus ring: archetypes.css used `outline: ...` for the focus-visible ring. The project's layout.css has a Tailwind layer/components rule `*:not(body):not(.focus-override) { outline: none !important }` that kills every outline. The eidos rule was never visible. Switch to a two-layer box-shadow ring (inner transparent gap + outer color). Box-shadow follows border-radius, so the ring also looks right on pills (Switch) and rounded corners (Toggle, Dialog buttons). Ring also stands out against accent-filled states because it sits OUTSIDE the element, not on top of it. 2) Dialog overlay: the recipe relied on [data-archetype='overlay'] from archetypes.css for `position: fixed; inset: 0`, but Dialog isn't migrated to MorfoRuntime yet — its provider uses legacy createAttrs which doesn't read morfo's `archetype` field. The overlay element had `position: static, height: 0`, so the backdrop was invisible. Inline `position: fixed; inset: 0` directly in the recipe. Component recipes shouldn't depend on archetype rules for foundational behavior; archetype is a transversal extra, not a foundation. Adds scripts/check-eidos-recipes.ts — Playwright script that opens /test/eidos, tabs into Toggle/Switch, opens the dialog, prints computed styles + saves screenshots to g:/tmp/eidos. Used to catch both bugs; reproducible for future visual checks. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
const dialogTrigger = page.locator('[data-dialog-trigger]').first();
const triggerExists = (await dialogTrigger.count()) > 0;
console.log('Dialog trigger exists:', triggerExists);
if (triggerExists) {
// Inject a sampler that records opacity at every rAF after the click.
await page.evaluate(`(() => {
window.__samples = [];
const trigger = document.querySelector('[data-dialog-trigger]');
const t0 = performance.now();
trigger.click();
const tick = () => {
const t = performance.now() - t0;
const el = document.querySelector('[data-dialog-content]');
if (el) {
const cs = getComputedStyle(el);
window.__samples.push({
t: Math.round(t),
opacity: parseFloat(cs.opacity),
hasStartingStyle: el.hasAttribute('data-starting-style')
});
}
if (t < 500) requestAnimationFrame(tick);
};
requestAnimationFrame(tick);
})()`);
await page.waitForTimeout(550);
const entranceSamples = (await page.evaluate('window.__samples')) as Array<{
t: number;
opacity: number;
hasStartingStyle: boolean;
}>;
const decimated = entranceSamples.filter(
(_, i) => i % 3 === 0 || i === entranceSamples.length - 1
);
console.log('Entrance samples (t-ms, opacity, hasStartingStyle):');
for (const s of decimated) {
console.log(` t=${s.t}ms opacity=${s.opacity.toFixed(3)} starting=${s.hasStartingStyle}`);
}
const dialogContent = page.locator('[data-dialog-content]');
await page.screenshot({ path: `${OUT}/03a-dialog-entrance-mid.png`, fullPage: false });
eidos: focus ring uses box-shadow (project kills outlines globally), dialog overlay self-positions Two bugs surfaced by Playwright verification at /test/eidos: 1) Focus ring: archetypes.css used `outline: ...` for the focus-visible ring. The project's layout.css has a Tailwind layer/components rule `*:not(body):not(.focus-override) { outline: none !important }` that kills every outline. The eidos rule was never visible. Switch to a two-layer box-shadow ring (inner transparent gap + outer color). Box-shadow follows border-radius, so the ring also looks right on pills (Switch) and rounded corners (Toggle, Dialog buttons). Ring also stands out against accent-filled states because it sits OUTSIDE the element, not on top of it. 2) Dialog overlay: the recipe relied on [data-archetype='overlay'] from archetypes.css for `position: fixed; inset: 0`, but Dialog isn't migrated to MorfoRuntime yet — its provider uses legacy createAttrs which doesn't read morfo's `archetype` field. The overlay element had `position: static, height: 0`, so the backdrop was invisible. Inline `position: fixed; inset: 0` directly in the recipe. Component recipes shouldn't depend on archetype rules for foundational behavior; archetype is a transversal extra, not a foundation. Adds scripts/check-eidos-recipes.ts — Playwright script that opens /test/eidos, tabs into Toggle/Switch, opens the dialog, prints computed styles + saves screenshots to g:/tmp/eidos. Used to catch both bugs; reproducible for future visual checks. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
const dialogOverlay = page.locator('[data-dialog-overlay]');
const overlayCount = await dialogOverlay.count();
const contentCount = await dialogContent.count();
console.log(`Dialog opened — overlay count: ${overlayCount}, content count: ${contentCount}`);
if (contentCount > 0) {
const contentBox = await dialogContent.first().boundingBox();
const overlayBox = await dialogOverlay.first().boundingBox();
const overlayStyles = await dialogOverlay.first().evaluate((el) => {
const cs = getComputedStyle(el);
return {
position: cs.position,
inset: cs.inset,
top: cs.top,
left: cs.left,
right: cs.right,
bottom: cs.bottom,
width: cs.width,
height: cs.height,
background: cs.backgroundColor,
zIndex: cs.zIndex,
display: cs.display,
visibility: cs.visibility,
opacity: cs.opacity,
dataArchetype: el.getAttribute('data-archetype'),
parentTag: el.parentElement?.tagName.toLowerCase()
};
});
const contentStyles = await dialogContent.first().evaluate((el) => {
const cs = getComputedStyle(el);
return {
position: cs.position,
top: cs.top,
left: cs.left,
transform: cs.transform,
background: cs.backgroundColor,
zIndex: cs.zIndex,
display: cs.display,
visibility: cs.visibility,
opacity: cs.opacity
};
});
console.log('Dialog content box:', contentBox);
console.log('Dialog overlay box:', overlayBox);
console.log('Dialog overlay styles:', overlayStyles);
console.log('Dialog content styles:', contentStyles);
}
await page.screenshot({ path: `${OUT}/03-dialog-open.png`, fullPage: false });
await page.evaluate(`(() => {
window.__exitSamples = [];
const closeBtn = document.querySelector('[data-dialog-close]');
const t0 = performance.now();
closeBtn.click();
const tick = () => {
const t = performance.now() - t0;
const el = document.querySelector('[data-dialog-content]');
if (el) {
const cs = getComputedStyle(el);
window.__exitSamples.push({
t: Math.round(t),
opacity: parseFloat(cs.opacity),
hasEndingStyle: el.hasAttribute('data-ending-style'),
soma/dialog: soft-migrate close-* events through MorfoRuntime Inject a MorfoRuntime side-channel in DialogProvider that fires the prewrite → semantic.emit → handler cadence for the close-* events declared in the dialog morfo. The legacy provider machinery (assertProps + resolveMorfoProps + Provider class hierarchy) stays — runtime is alongside, not replacing. Wiring: - DialogProvider creates the runtime in its constructor with `events.{close-save, close-cancel, close-dismiss, close-dismiss-outside, close-after-fail}` all delegating to handleClose(). The runtime gets dom + semantic from soma + state sources for `open` + prop sources for `disabled`/`modal`. - DialogContentProvider registers the content part with the runtime when its constructor runs, so trigger() can resolve `event.target = partRef('content')` to a real DOM node. - DialogTriggerProvider.onclick keeps direct handleOpen() — the 'open' event in the morfo targets content, but content isn't in the DOM until AFTER open flips. Chicken-and-egg. - DialogCloseProvider.onclick → trigger('close-cancel'). - DialogContentProvider's dismissal callbacks: onEscapeKeydown → trigger('close-dismiss') onInteractOutside → trigger('close-dismiss-outside') What this unlocks: Each close path now writes `data-last-action` (saved/cancelled/ dismissed/dismissed-outside/failed) BEFORE flipping data-state, visible during the exit animation. The dialog recipe's tint rules in eidos finally fire in vivo. Verification (playwright check-eidos-recipes): @8ms post-click lastAction=cancelled state=open (prewrite) @241ms lastAction=cancelled state=open (signal hold) @378ms lastAction=cancelled state=closed (exit) @493ms opacity=0.017 (exiting) The 240ms wait between click and exit is sema's family='emerge' hold. The user can override per-event via `morfo.events[].hold` if 240ms feels too long for a dialog cancel. Smoke 68/68, morfo-check 66/66. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
lastAction: el.getAttribute('data-last-action'),
borderTopColor: cs.borderTopColor,
mounted: true
});
} else {
soma/dialog: soft-migrate close-* events through MorfoRuntime Inject a MorfoRuntime side-channel in DialogProvider that fires the prewrite → semantic.emit → handler cadence for the close-* events declared in the dialog morfo. The legacy provider machinery (assertProps + resolveMorfoProps + Provider class hierarchy) stays — runtime is alongside, not replacing. Wiring: - DialogProvider creates the runtime in its constructor with `events.{close-save, close-cancel, close-dismiss, close-dismiss-outside, close-after-fail}` all delegating to handleClose(). The runtime gets dom + semantic from soma + state sources for `open` + prop sources for `disabled`/`modal`. - DialogContentProvider registers the content part with the runtime when its constructor runs, so trigger() can resolve `event.target = partRef('content')` to a real DOM node. - DialogTriggerProvider.onclick keeps direct handleOpen() — the 'open' event in the morfo targets content, but content isn't in the DOM until AFTER open flips. Chicken-and-egg. - DialogCloseProvider.onclick → trigger('close-cancel'). - DialogContentProvider's dismissal callbacks: onEscapeKeydown → trigger('close-dismiss') onInteractOutside → trigger('close-dismiss-outside') What this unlocks: Each close path now writes `data-last-action` (saved/cancelled/ dismissed/dismissed-outside/failed) BEFORE flipping data-state, visible during the exit animation. The dialog recipe's tint rules in eidos finally fire in vivo. Verification (playwright check-eidos-recipes): @8ms post-click lastAction=cancelled state=open (prewrite) @241ms lastAction=cancelled state=open (signal hold) @378ms lastAction=cancelled state=closed (exit) @493ms opacity=0.017 (exiting) The 240ms wait between click and exit is sema's family='emerge' hold. The user can override per-event via `morfo.events[].hold` if 240ms feels too long for a dialog cancel. Smoke 68/68, morfo-check 66/66. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
window.__exitSamples.push({
t: Math.round(t), opacity: 0, hasEndingStyle: false, lastAction: null,
borderTopColor: '', mounted: false
});
}
if (t < 500) requestAnimationFrame(tick);
};
requestAnimationFrame(tick);
})()`);
await page.waitForTimeout(550);
const exitSamples = (await page.evaluate('window.__exitSamples')) as Array<{
t: number;
opacity: number;
hasEndingStyle: boolean;
soma/dialog: soft-migrate close-* events through MorfoRuntime Inject a MorfoRuntime side-channel in DialogProvider that fires the prewrite → semantic.emit → handler cadence for the close-* events declared in the dialog morfo. The legacy provider machinery (assertProps + resolveMorfoProps + Provider class hierarchy) stays — runtime is alongside, not replacing. Wiring: - DialogProvider creates the runtime in its constructor with `events.{close-save, close-cancel, close-dismiss, close-dismiss-outside, close-after-fail}` all delegating to handleClose(). The runtime gets dom + semantic from soma + state sources for `open` + prop sources for `disabled`/`modal`. - DialogContentProvider registers the content part with the runtime when its constructor runs, so trigger() can resolve `event.target = partRef('content')` to a real DOM node. - DialogTriggerProvider.onclick keeps direct handleOpen() — the 'open' event in the morfo targets content, but content isn't in the DOM until AFTER open flips. Chicken-and-egg. - DialogCloseProvider.onclick → trigger('close-cancel'). - DialogContentProvider's dismissal callbacks: onEscapeKeydown → trigger('close-dismiss') onInteractOutside → trigger('close-dismiss-outside') What this unlocks: Each close path now writes `data-last-action` (saved/cancelled/ dismissed/dismissed-outside/failed) BEFORE flipping data-state, visible during the exit animation. The dialog recipe's tint rules in eidos finally fire in vivo. Verification (playwright check-eidos-recipes): @8ms post-click lastAction=cancelled state=open (prewrite) @241ms lastAction=cancelled state=open (signal hold) @378ms lastAction=cancelled state=closed (exit) @493ms opacity=0.017 (exiting) The 240ms wait between click and exit is sema's family='emerge' hold. The user can override per-event via `morfo.events[].hold` if 240ms feels too long for a dialog cancel. Smoke 68/68, morfo-check 66/66. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
lastAction: string | null;
borderTopColor: string;
mounted: boolean;
}>;
const exitDecimated = exitSamples.filter((_, i) => i % 3 === 0 || i === exitSamples.length - 1);
soma/dialog: soft-migrate close-* events through MorfoRuntime Inject a MorfoRuntime side-channel in DialogProvider that fires the prewrite → semantic.emit → handler cadence for the close-* events declared in the dialog morfo. The legacy provider machinery (assertProps + resolveMorfoProps + Provider class hierarchy) stays — runtime is alongside, not replacing. Wiring: - DialogProvider creates the runtime in its constructor with `events.{close-save, close-cancel, close-dismiss, close-dismiss-outside, close-after-fail}` all delegating to handleClose(). The runtime gets dom + semantic from soma + state sources for `open` + prop sources for `disabled`/`modal`. - DialogContentProvider registers the content part with the runtime when its constructor runs, so trigger() can resolve `event.target = partRef('content')` to a real DOM node. - DialogTriggerProvider.onclick keeps direct handleOpen() — the 'open' event in the morfo targets content, but content isn't in the DOM until AFTER open flips. Chicken-and-egg. - DialogCloseProvider.onclick → trigger('close-cancel'). - DialogContentProvider's dismissal callbacks: onEscapeKeydown → trigger('close-dismiss') onInteractOutside → trigger('close-dismiss-outside') What this unlocks: Each close path now writes `data-last-action` (saved/cancelled/ dismissed/dismissed-outside/failed) BEFORE flipping data-state, visible during the exit animation. The dialog recipe's tint rules in eidos finally fire in vivo. Verification (playwright check-eidos-recipes): @8ms post-click lastAction=cancelled state=open (prewrite) @241ms lastAction=cancelled state=open (signal hold) @378ms lastAction=cancelled state=closed (exit) @493ms opacity=0.017 (exiting) The 240ms wait between click and exit is sema's family='emerge' hold. The user can override per-event via `morfo.events[].hold` if 240ms feels too long for a dialog cancel. Smoke 68/68, morfo-check 66/66. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
console.log('Exit samples (t-ms, opacity, hasEndingStyle, lastAction, borderTop, mounted):');
for (const s of exitDecimated) {
console.log(
soma/dialog: soft-migrate close-* events through MorfoRuntime Inject a MorfoRuntime side-channel in DialogProvider that fires the prewrite → semantic.emit → handler cadence for the close-* events declared in the dialog morfo. The legacy provider machinery (assertProps + resolveMorfoProps + Provider class hierarchy) stays — runtime is alongside, not replacing. Wiring: - DialogProvider creates the runtime in its constructor with `events.{close-save, close-cancel, close-dismiss, close-dismiss-outside, close-after-fail}` all delegating to handleClose(). The runtime gets dom + semantic from soma + state sources for `open` + prop sources for `disabled`/`modal`. - DialogContentProvider registers the content part with the runtime when its constructor runs, so trigger() can resolve `event.target = partRef('content')` to a real DOM node. - DialogTriggerProvider.onclick keeps direct handleOpen() — the 'open' event in the morfo targets content, but content isn't in the DOM until AFTER open flips. Chicken-and-egg. - DialogCloseProvider.onclick → trigger('close-cancel'). - DialogContentProvider's dismissal callbacks: onEscapeKeydown → trigger('close-dismiss') onInteractOutside → trigger('close-dismiss-outside') What this unlocks: Each close path now writes `data-last-action` (saved/cancelled/ dismissed/dismissed-outside/failed) BEFORE flipping data-state, visible during the exit animation. The dialog recipe's tint rules in eidos finally fire in vivo. Verification (playwright check-eidos-recipes): @8ms post-click lastAction=cancelled state=open (prewrite) @241ms lastAction=cancelled state=open (signal hold) @378ms lastAction=cancelled state=closed (exit) @493ms opacity=0.017 (exiting) The 240ms wait between click and exit is sema's family='emerge' hold. The user can override per-event via `morfo.events[].hold` if 240ms feels too long for a dialog cancel. Smoke 68/68, morfo-check 66/66. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
` t=${s.t}ms opacity=${s.opacity.toFixed(3)} ending=${s.hasEndingStyle} lastAction=${s.lastAction} border=${s.borderTopColor} mounted=${s.mounted}`
);
eidos: focus ring uses box-shadow (project kills outlines globally), dialog overlay self-positions Two bugs surfaced by Playwright verification at /test/eidos: 1) Focus ring: archetypes.css used `outline: ...` for the focus-visible ring. The project's layout.css has a Tailwind layer/components rule `*:not(body):not(.focus-override) { outline: none !important }` that kills every outline. The eidos rule was never visible. Switch to a two-layer box-shadow ring (inner transparent gap + outer color). Box-shadow follows border-radius, so the ring also looks right on pills (Switch) and rounded corners (Toggle, Dialog buttons). Ring also stands out against accent-filled states because it sits OUTSIDE the element, not on top of it. 2) Dialog overlay: the recipe relied on [data-archetype='overlay'] from archetypes.css for `position: fixed; inset: 0`, but Dialog isn't migrated to MorfoRuntime yet — its provider uses legacy createAttrs which doesn't read morfo's `archetype` field. The overlay element had `position: static, height: 0`, so the backdrop was invisible. Inline `position: fixed; inset: 0` directly in the recipe. Component recipes shouldn't depend on archetype rules for foundational behavior; archetype is a transversal extra, not a foundation. Adds scripts/check-eidos-recipes.ts — Playwright script that opens /test/eidos, tabs into Toggle/Switch, opens the dialog, prints computed styles + saves screenshots to g:/tmp/eidos. Used to catch both bugs; reproducible for future visual checks. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
}
await page.screenshot({ path: `${OUT}/04-dialog-closed.png`, fullPage: false });
eidos: focus ring uses box-shadow (project kills outlines globally), dialog overlay self-positions Two bugs surfaced by Playwright verification at /test/eidos: 1) Focus ring: archetypes.css used `outline: ...` for the focus-visible ring. The project's layout.css has a Tailwind layer/components rule `*:not(body):not(.focus-override) { outline: none !important }` that kills every outline. The eidos rule was never visible. Switch to a two-layer box-shadow ring (inner transparent gap + outer color). Box-shadow follows border-radius, so the ring also looks right on pills (Switch) and rounded corners (Toggle, Dialog buttons). Ring also stands out against accent-filled states because it sits OUTSIDE the element, not on top of it. 2) Dialog overlay: the recipe relied on [data-archetype='overlay'] from archetypes.css for `position: fixed; inset: 0`, but Dialog isn't migrated to MorfoRuntime yet — its provider uses legacy createAttrs which doesn't read morfo's `archetype` field. The overlay element had `position: static, height: 0`, so the backdrop was invisible. Inline `position: fixed; inset: 0` directly in the recipe. Component recipes shouldn't depend on archetype rules for foundational behavior; archetype is a transversal extra, not a foundation. Adds scripts/check-eidos-recipes.ts — Playwright script that opens /test/eidos, tabs into Toggle/Switch, opens the dialog, prints computed styles + saves screenshots to g:/tmp/eidos. Used to catch both bugs; reproducible for future visual checks. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
}
await browser.close();
console.log(`\nScreenshots saved to ${OUT}/`);
}
main().catch((e) => {
console.error(e);
process.exit(1);
});

Powered by TurnKey Linux.