feat(eidos): shape Fase 3 — eventful corner morph (the differentiator)

Shape becomes a two-moment channel: resting form (Fase 1) + the morph (event).
Folded cross-modally into the existing `press-squeeze` contact firma — one press
now drives scale (motion) + shadow-flatten (depth) + corner-firm (shape): the
`continuous` corner squares up at the press peak (--shape-smoothing 2 -> 3 -> 2).

- `@property --shape-smoothing { syntax: <number>; inherits: true }` so the number
  interpolates smoothly (mirrors the motion @property pattern).
- Set only at the keyframe peak -> interpolates from/to the element underlying
  smoothing (respects a theme override). No-op for non-continuous corners (they do
  not read --shape-smoothing). Degrades with reduced-motion via the firma system.

Material 3 Expressive is the only other system with shape morph (bespoke, closed);
this rides the unified firma — one event, many modalities.

Verified: check 0 errors; eidos config+motion+generated 78/78. Regenerated base.css.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
active-uix
dev 4 months ago
parent e04c098787
commit 7255a52a0a

@ -552,6 +552,15 @@ describe('ActiveEidos config', () => {
);
});
it('emits the Fase 3 eventful corner morph (@property + press-squeeze --shape-smoothing)', () => {
const css = createThemeBaseEidos().renderStaticCss();
// registered as a typed number so the corner morph interpolates instead of jumping
expect(css).toContain('@property --shape-smoothing {');
expect(css).toContain("syntax: '<number>';");
// the cross-modal contact firma (press-squeeze) firms the corner at the press peak
expect(css).toContain('--shape-smoothing: 3;');
});
it('emits the Phase 3 typography scales + optical tracking', () => {
const css = createThemeBaseEidos().renderStaticCss()
expect(css).toContain('--tracking-tight: -0.02em;')

@ -4363,6 +4363,12 @@
z-index: var(--depth-recessed-z);
}
@property --shape-smoothing {
syntax: '<number>';
inherits: true;
initial-value: 2;
}
[data-shape='rounded'] {
corner-shape: round;
}
@ -4758,6 +4764,7 @@
transform: scale(0.96);
filter: brightness(0.96);
box-shadow: 0 0 0 0 transparent;
--shape-smoothing: 3;
}
100% {
transform: scale(1);

@ -149,10 +149,18 @@ export const BUILTIN_KEYFRAMES: Readonly<Record<string, KeyframeStops>> = {
'present-rise': {
from: { opacity: '0', transform: 'translateY(8px)', 'box-shadow': '0 0 0 0 transparent' }
},
// Contact: the shadow flattens to the surface at the press peak, then returns (recede).
// Contact: cross-modal press firma — scale (motion) + shadow flattens to the surface (depth) +
// the corner firms (shape: `--shape-smoothing` rises → squircle squares up). Only set at the
// peak so it interpolates from/to the element's underlying smoothing (respects a theme
// override); a no-op for non-`continuous` corners, which don't read `--shape-smoothing`.
'press-squeeze': {
'0%': { transform: 'scale(1)' },
'40%': { transform: 'scale(0.96)', filter: 'brightness(0.96)', 'box-shadow': '0 0 0 0 transparent' },
'40%': {
transform: 'scale(0.96)',
filter: 'brightness(0.96)',
'box-shadow': '0 0 0 0 transparent',
'--shape-smoothing': '3'
},
'100%': { transform: 'scale(1)' }
},
'commit-settle': {

@ -839,6 +839,12 @@ function appendShapeDeclarations(declarations: string[], shape: ShapePrimitiveSe
*/
function renderShapeBlocks(shape: ShapePrimitiveSet): string[] {
const blocks: string[] = []
// Register --shape-smoothing as a typed number so the eventful corner morph (the press-squeeze
// firma, SHAPE_ENGINE_RFC Fase 3) interpolates smoothly instead of jumping. inherits:true keeps
// the :root default + any theme override cascading to every element.
blocks.push(
`@property --shape-smoothing {\n\tsyntax: '<number>';\n\tinherits: true;\n\tinitial-value: ${shape.smoothing};\n}`
)
for (const [family, value] of Object.entries(shape.families)) {
blocks.push(renderBlock(`[data-shape='${family}']`, [`corner-shape: ${value};`]))
}

Loading…
Cancel
Save

Powered by TurnKey Linux.