Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

# 🐛 Bug Report: Select Dropdown Positioning Issues in Closed Shadow DOM #8337

Closed as not planned
Labels
@tuandang-diag

Description

🐛 Bug Report: Select Dropdown Positioning Issues in Closed Shadow DOM

📋 Issue Summary

Ant Design Vue Select dropdowns calculate incorrect positions when rendered inside a closed Shadow DOM, resulting in negative coordinates and dropdowns appearing offscreen.

🌍 Environment

  • Ant Design Vue: v4.2.6
  • Vue: 3.x
  • Browser: Chrome/Firefox/Safari (all affected)
  • Shadow DOM Mode: Closed
  • Use Case: Micro-frontend architecture with CSS isolation

🎯 Expected Behavior

Select dropdowns should position correctly relative to their trigger elements, appearing below or above the select input as appropriate.

🚫 Actual Behavior

Dropdowns render with negative coordinates (e.g., left: -387px; top: -820px), appearing offscreen and unusable.

🔬 Root Cause Analysis

The issue occurs because:

  1. Coordinate System Mismatch: Ant Design's positioning logic uses getBoundingClientRect() which returns coordinates relative to the viewport
  2. Shadow DOM Boundary: When components are inside a closed Shadow DOM, the coordinate system becomes isolated
  3. Container Context: The getPopupContainer function cannot properly bridge the coordinate gap between shadow DOM and main document

📝 Reproduction Steps

1. Create Shadow DOM Setup

<!DOCTYPE html>
<html>
 <head>
 <title>Shadow DOM Test</title>
 </head>
 <body>
 <div id="shadow-host"></div>
 <script type="module">
 // Create closed shadow DOM
 const shadowHost = document.getElementById('shadow-host')
 const shadowRoot = shadowHost.attachShadow({ mode: 'closed' })
 // Mount Vue app inside shadow DOM
 shadowRoot.innerHTML = '<div id="app"></div>'
 // Your Vue app mounts here...
 </script>
 </body>
</html>

2. Vue Component with Select

<template>
 <ConfigProvider :get-popup-container="getPopupContainer">
 <Select v-model:value="selectedValue" placeholder="Select an option" :options="options" />
 </ConfigProvider>
</template>
<script setup>
import { Select, ConfigProvider } from 'ant-design-vue'
import { ref } from 'vue'

const selectedValue = ref(undefined)
const options = [
 { label: 'Option 1', value: '1' },
 { label: 'Option 2', value: '2' },
 { label: 'Option 3', value: '3' },
]

// This doesn't work properly in closed Shadow DOM
const getPopupContainer = (triggerNode) => {
 return document.body // Points to main document, not shadow DOM
}
</script>

3. Observe the Issue

  1. Click the Select dropdown
  2. Open browser DevTools
  3. Inspect the dropdown element
  4. Notice negative left and top values in computed styles

🔧 Current Workarounds Attempted

We've tried several approaches, all with limitations:

1. Custom Popup Container Inside Shadow DOM

function createShadowDOMContainer() {
 return (triggerNode) => {
 const shadowRoot = getShadowRoot()
 return shadowRoot // Still has coordinate issues
 }
}

2. Coordinate Transformation Patch

// Patch getBoundingClientRect to transform coordinates
const originalGetBoundingClientRect = Element.prototype.getBoundingClientRect
Element.prototype.getBoundingClientRect = function () {
 const rect = originalGetBoundingClientRect.call(this)
 // Transform coordinates based on shadow host offset
 // Complex and fragile approach
}

3. Viewport-Fixed Container

function createViewportContainer() {
 return (triggerNode) => {
 const container = document.createElement('div')
 container.style.cssText = `
 position: fixed;
 top: 0;
 left: 0;
 width: 100vw;
 height: 100vh;
 pointer-events: none;
 z-index: 1050;
 `
 shadowRoot.appendChild(container)
 return container
 }
}

💡 Proposed Solutions

Option 1: Shadow DOM Detection and Coordinate Adjustment

Add built-in detection for Shadow DOM context and automatically adjust positioning calculations:

// In Ant Design's positioning logic
function getAdjustedPosition(triggerElement, popupElement) {
 const shadowRoot = triggerElement.getRootNode()
 if (shadowRoot instanceof ShadowRoot) {
 const shadowHost = shadowRoot.host
 const hostRect = shadowHost.getBoundingClientRect()
 const triggerRect = triggerElement.getBoundingClientRect()
 // Adjust coordinates relative to shadow host
 return {
 left: triggerRect.left - hostRect.left,
 top: triggerRect.top - hostRect.top,
 }
 }
 // Normal positioning for non-shadow DOM
 return normalPositioning(triggerElement, popupElement)
}

Option 2: Enhanced getPopupContainer Support

Provide better utilities for Shadow DOM popup containers:

// New utility from Ant Design Vue
import { createShadowDOMPopupContainer } from 'ant-design-vue/es/utils'
const getPopupContainer = createShadowDOMPopupContainer({
 mode: 'closed',
 coordinateTransform: true,
})

Option 3: Configuration Option

Add a configuration option to handle Shadow DOM positioning:

<ConfigProvider :get-popup-container="getPopupContainer" :shadow-dom-support="true">
 <!-- Components -->
</ConfigProvider>

🎯 Impact

This issue affects:

  • Micro-frontend architectures using Shadow DOM for CSS isolation
  • Web Components integration with Vue apps
  • Third-party widget development requiring style isolation
  • Enterprise applications with strict CSS encapsulation requirements

📚 Additional Context

  • Similar issues exist in other UI libraries (React Ant Design, Material UI)
  • Shadow DOM usage is increasing for micro-frontend architectures
  • CSS isolation is crucial for enterprise applications
  • This affects all popup-based components (Select, DatePicker, Dropdown, etc.)

🙏 Community Support Needed

This is a significant architectural challenge that would benefit from:

  • Core team guidance on preferred solution approach
  • Community input on Shadow DOM best practices
  • Potential collaboration on implementation

Would the maintainers be open to discussing this issue and potential solutions? We're happy to contribute to the implementation once a direction is established.

🔗 Related Issues


Environment Details:

  • OS: macOS/Windows/Linux (all affected)
  • Node.js: 18.x+
  • Build Tool: Vite 6.x
  • Architecture: Micro-frontend with Shadow DOM isolation

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions

        AltStyle によって変換されたページ (->オリジナル) /