base-ui icon indicating copy to clipboard operation
base-ui copied to clipboard

[combobox] Hidden input styles in combobox interferes with dnd operations

Open JayashTripathy opened this issue 2 months ago • 3 comments

Hidden input with position: fixed interferes with drag-and-drop operations

Description

The Combobox component creates a hidden input element with position: fixed; top: 0px; left: 0px; for form integration and accessibility. When the Combobox is used inside draggable elements (e.g., with @atlaskit/pragmatic-drag-and-drop or similar libraries), this fixed positioning causes the hidden input to be positioned at the viewport corner, which interferes with drag-and-drop operations and affects the drag preview.

Environment

  • Base UI Version: @base-ui-components/react (latest)
  • React Version: 18.x
  • Browser: Chrome/Firefox/Safari (all affected)

Reproduction

  1. Create a Combobox component inside a draggable container
  2. Implement drag-and-drop using any DnD library (e.g., @atlaskit/pragmatic-drag-and-drop, react-dnd, etc.)
  3. Attempt to drag the container
  4. Observe that the hidden input element at the top-left corner of the viewport interferes with the drag operation

Code Example

import { Combobox } from '@base-ui-components/react/combobox';
import { draggable } from '@atlaskit/pragmatic-drag-and-drop/element/adapter';

function DraggableCombobox() {
  const ref = useRef(null);
  
  useEffect(() => {
    const element = ref.current;
    if (!element) return;
    
    return draggable({
      element,
      getInitialData: () => ({ id: 'item-1' }),
    });
  }, []);
  
  return (
    <div ref={ref}>
      <Combobox.Root value="option1">
        <Combobox.Trigger>Select option</Combobox.Trigger>
        <Combobox.Portal>
          <Combobox.Positioner>
            <Combobox.Popup>
              {/* ... */}
            </Combobox.Popup>
          </Combobox.Positioner>
        </Combobox.Portal>
      </Combobox.Root>
    </div>
  );
}

Expected Behavior

The hidden input should be positioned relative to its parent container (using position: absolute) so it doesn't interfere with drag-and-drop operations or appear outside the component's bounds.

Actual Behavior

The hidden input is created with inline styles:

<input 
  id="base-ui-:r2f:" 
  tabindex="-1" 
  aria-hidden="true" 
  value="CLOSED" 
  style="clip: rect(0px, 0px, 0px, 0px); overflow: hidden; white-space: nowrap; position: fixed; top: 0px; left: 0px; border: 0px; padding: 0px; width: 1px; height: 1px; margin: -1px;"
>

The position: fixed; top: 0px; left: 0px; positions the input at the viewport corner, which:

  1. Interferes with drag-and-drop calculations
  2. Can affect the drag preview generation
  3. May capture unintended pointer events in some scenarios

Suggested Solution

Change the hidden input's positioning from fixed to absolute to keep it within its parent's bounds:

- style="position: fixed; top: 0px; left: 0px; ..."
+ style="position: absolute; top: 0px; left: 0px; ..."

This maintains the same visual hiding effect while keeping the element scoped to its container, preventing interference with drag-and-drop operations.

Workaround

Currently using the inputRef prop to manually override the positioning:

const hiddenInputRef = useRef<HTMLInputElement>(null);

useEffect(() => {
  const node = hiddenInputRef.current;
  if (node) {
    node.style.position = 'absolute';
    node.style.pointerEvents = 'none';
  }
}, []);

<Combobox.Root inputRef={hiddenInputRef}>
  {/* ... */}
</Combobox.Root>

Additional Context

This issue affects any use case where Combobox is used within draggable UI elements, which is common in:

  • Kanban boards
  • Sortable lists
  • Draggable cards/panels
  • Dynamic layouts with DnD

The hidden input serves an important purpose for accessibility and form integration, so it should remain in the DOM—just with positioning that doesn't interfere with parent element interactions.

JayashTripathy avatar Oct 21 '25 06:10 JayashTripathy

contain: layout CSS on that container div will scope the fixed hidden input element to it, which is the same as position: absolute

atomiks avatar Oct 21 '25 06:10 atomiks

@atomiks Yes, this makes sense. Should I close this issue?

JayashTripathy avatar Oct 21 '25 06:10 JayashTripathy

Almost a year ago, the style was changed from position: absolute to position: fixed: https://github.com/mui/base-ui/pull/1077. @atomiks @michaldudak Do you think it makes sense to change a utility behavior to fix an issue in the docs? 🤔 For context, Material UI uses position: absolute: https://github.com/mui/material-ui/blob/master/packages/mui-utils/src/visuallyHidden/visuallyHidden.ts

LukasTy avatar Oct 27 '25 15:10 LukasTy