Provide a general summary of the issue here
Typing a hex value into ColorField (inside a ColorPicker popover) and pressing Enter does not commit the typed value — the field's internal inputValue is only flushed to the color state on blur. If the user then closes the popover with Escape, the typed value is silently discarded and the previous color is restored, so users believe they picked a color they never actually committed.
🤔 Expected Behavior?
Pressing Enter in a ColorField commits the typed value, the same way NumberField does — useNumberField explicitly handles Enter and commits via flushSync(() => commit()) (packages/react-aria/src/numberfield/useNumberField.ts:261-267 on main).
😯 Current Behavior
useColorField wires commit to onBlur only (packages/react-aria/src/color/useColorField.ts:147 on main) and has no Enter handling. So:
- Type
#889096 into the hex field (previous color #3B82F6)
- Press Enter → the input keeps displaying
#889096, but the picker state (swatch, sliders, onChange) still holds #3B82F6
- Press Escape to close the popover → color silently reverts to
#3B82F6
Tabbing out of the field (blur) commits correctly.
This is the keyboard sibling of #9748 ("ColorPicker's color field doesn't commit its value if the popover is dismissed via outside click"). That one was fixed by #9768 (in react-aria@3.51.0) for the pointer-dismissal path, but the Enter/Escape paths are unaffected since React does not fire blur on unmount.
💁 Possible Solution
Mirror the useNumberField Enter handling in useColorField (commit on Enter, keep focus). Whether Escape should commit or cancel is a separate design question — cancel-on-Escape is conventional, and becomes predictable once Enter commits.
Workaround we ship meanwhile — commit via programmatic blur, refocus to preserve the focus ring (sidesteps the a11y objection from #2596):
<ColorField.Input
onKeyDown={(e) => {
if (e.key !== "Enter") return;
e.preventDefault();
const input = e.currentTarget;
input.blur();
input.focus();
}}
/>
🔦 Context
Operators picking a category color in a B2B console typed a custom hex, pressed Enter (the conventional "confirm" key for a text input), closed the popover, and saved — persisting the default blue instead of their chosen color, with no feedback that anything was dropped.
🖥️ Steps to Reproduce
Any ColorPicker + ColorField composition reproduces it, e.g. the docs example:
<ColorPicker defaultValue="#3B82F6">
{/* trigger ... */}
<Popover>
<ColorField colorSpace="hsb" aria-label="Hex">
<Input />
</ColorField>
</Popover>
</ColorPicker>
Type a different hex → press Enter → inspect the picker state: unchanged. Press Escape → typed value discarded.
Version
react-aria 3.51.0 / react-aria-components 1.20.0 (also reproduces reading main — useColorField.ts has no Enter handling)
What browsers are you seeing the problem on?
Chrome (Chromium 139) — behavior is library-level, not browser-specific.
What operating system are you using?
macOS 15
Provide a general summary of the issue here
Typing a hex value into
ColorField(inside aColorPickerpopover) and pressing Enter does not commit the typed value — the field's internalinputValueis only flushed to the color state on blur. If the user then closes the popover with Escape, the typed value is silently discarded and the previous color is restored, so users believe they picked a color they never actually committed.🤔 Expected Behavior?
Pressing Enter in a
ColorFieldcommits the typed value, the same wayNumberFielddoes —useNumberFieldexplicitly handles Enter and commits viaflushSync(() => commit())(packages/react-aria/src/numberfield/useNumberField.ts:261-267onmain).😯 Current Behavior
useColorFieldwirescommittoonBluronly (packages/react-aria/src/color/useColorField.ts:147onmain) and has no Enter handling. So:#889096into the hex field (previous color#3B82F6)#889096, but the picker state (swatch, sliders,onChange) still holds#3B82F6#3B82F6Tabbing out of the field (blur) commits correctly.
This is the keyboard sibling of #9748 ("ColorPicker's color field doesn't commit its value if the popover is dismissed via outside click"). That one was fixed by #9768 (in
react-aria@3.51.0) for the pointer-dismissal path, but the Enter/Escape paths are unaffected since React does not fire blur on unmount.💁 Possible Solution
Mirror the
useNumberFieldEnter handling inuseColorField(commit on Enter, keep focus). Whether Escape should commit or cancel is a separate design question — cancel-on-Escape is conventional, and becomes predictable once Enter commits.Workaround we ship meanwhile — commit via programmatic blur, refocus to preserve the focus ring (sidesteps the a11y objection from #2596):
🔦 Context
Operators picking a category color in a B2B console typed a custom hex, pressed Enter (the conventional "confirm" key for a text input), closed the popover, and saved — persisting the default blue instead of their chosen color, with no feedback that anything was dropped.
🖥️ Steps to Reproduce
Any
ColorPicker+ColorFieldcomposition reproduces it, e.g. the docs example:Type a different hex → press Enter → inspect the picker state: unchanged. Press Escape → typed value discarded.
Version
react-aria 3.51.0 / react-aria-components 1.20.0 (also reproduces reading
main—useColorField.tshas no Enter handling)What browsers are you seeing the problem on?
Chrome (Chromium 139) — behavior is library-level, not browser-specific.
What operating system are you using?
macOS 15