Skip to content
Closed
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changeset/uikit-keyboard-notification.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@astryxdesign/core': patch
---

[chore] Internal: the keyboard notifications' duration has its own export in the UIKit timing table, so code that reads only it bundles no other timing value. Not exported from the package entry point.

@vjeux
46 changes: 46 additions & 0 deletions packages/core/src/motion/uikit/keyboardNotification.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// Copyright (c) Meta Platforms, Inc. and affiliates.

/**
* @file keyboardNotification.test.ts
* @input timing.ts, bundled on its own with esbuild
* @output Proof that the keyboard notifications' duration is its own export:
* the timing table composes it, and a bundle that reads only it carries no
* other row of the table
* @position Guards architecture:component-style-authoring INV6 for the
* keyboard layout guide's one read of the timing table
*/

import {execFileSync} from 'node:child_process';
import {describe, expect, it} from 'vitest';
import {keyboardNotification, timing} from './timing';

describe('keyboardNotification', () => {
it('is the timing table\u2019s keyboard duration', () => {
expect(timing.keyboardNotificationMs).toBe(keyboardNotification.durationMs);
});

it('a bundle that reads only the keyboard\u2019s duration drops the rest of the table', () => {
// esbuild runs in its own Node process: jsdom's TextEncoder fails its
// startup check.
const code = execFileSync(
process.execPath,
[
'--input-type=module',
'-e',
[
"import {build} from 'esbuild';",
'const result = await build({',
" stdin: {contents: \"import {keyboardNotification} from './timing'; export const ms = keyboardNotification.durationMs;\", resolveDir: process.cwd(), loader: 'ts'},",
" bundle: true, write: false, format: 'esm', minify: true, logLevel: 'silent',",
'});',
'process.stdout.write(result.outputFiles[0].text);',
].join('\n'),
],
{cwd: __dirname, encoding: 'utf8'},
);
expect(code).toContain('383.3');
// No other row: not the navigation bar's duration, not the press clocks.
expect(code).not.toContain('navigationBarHideShowMs');
expect(code).not.toContain('buttonReleaseMs');
Comment on lines +44 to +45

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At this head this build is var e={durationMs:383.3},{durationMs:s}=e,o={durationMs:500,curve:[.42,0,.58,1]},…: it carries the Reduce Motion fade row through the binding at timing.ts:144-145, which these two assertions don't name, so the test passes while the bundle still carries another row. Asserting, for example, expect(code).not.toContain('durationMs:500') makes it fail today.

});
});
32 changes: 23 additions & 9 deletions packages/core/src/motion/uikit/timing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,28 @@ export const press = {
buttonReleaseMs: 250,
} as const;

/**
* The keyboard notifications' duration, its own export so the keyboard layout
* guide reads it without the rest of the table
* (`architecture:component-style-authoring` INV6). `timing` composes it under
* its table path, `keyboardNotificationMs`, which stays out of the roles.
*/
export const keyboardNotification = {
/**
* The duration the keyboard notifications report
* (UIKeyboardAnimationDurationUserInfoKey, with curve 7), show and hide
* alike, in ms. The frame follows `springs.keyboard`, cut at this time.
* @source measured: UIKit lab (injected host app), iOS 26.2 simulator,
* iPhone 17 Pro, UIKeyboardWillShow / WillHide / WillChangeFrame read,
* Oct 23 2026
*/
durationMs: 383.3,
} as const;

// Composed from a binding, so a bundle that reads only the keyboard's
// duration drops the rest of the table.
const {durationMs: keyboardNotificationMs} = keyboardNotification;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

esbuild keeps this top-level destructuring in every bundle that includes timing.ts (it can't rule out a getter), so the keyboard row now rides into the animator's reducedMotionCrossFade import (97 → 112 B gzip, minified) and every press read. Declaring the duration once as a sourced module constant that both keyboardNotification.durationMs and timing.keyboardNotificationMs name lets esbuild drop it from bundles that don't read it; uikit.test.ts would then check a leaf that names a sourced constant where the constant is declared, as it already does for bindings.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 9865edf: each value is now one sourced constant (KEYBOARD_NOTIFICATION_MS, REDUCED_MOTION_CROSS_FADE_MS, REDUCED_MOTION_CROSS_FADE_CURVE) that its group and timing both name, with no top-level destructuring left, so neither group rides into the other's bundle. The keyboard bundle test now asserts no durationMs:500 and the animator's bundle test no 383.3; both failed on 216d05c and pass here. The table test takes a constant documented with @source as a row of its own.


/**
* The Reduce Motion fade, its own export so the animator reads it without
* the rest of the table (`architecture:component-style-authoring` INV6).
Expand Down Expand Up @@ -161,15 +183,7 @@ export const timing = {
* iPhone 17 Pro, read, Oct 23 2026
*/
navigationBarHideShowMs: 200,
/**
* The duration the keyboard notifications report
* (UIKeyboardAnimationDurationUserInfoKey, with curve 7), show and hide
* alike, in ms. The frame follows `springs.keyboard`, cut at this time.
* @source measured: UIKit lab (injected host app), iOS 26.2 simulator,
* iPhone 17 Pro, UIKeyboardWillShow / WillHide / WillChangeFrame read,
* Oct 23 2026
*/
keyboardNotificationMs: 383.3,
keyboardNotificationMs,
/**
* Core Animation's implicit animation duration
* (`CATransaction.animationDuration()`), in ms.
Expand Down
Loading