Popover 气泡卡片
点击 / 悬停 / 聚焦触发,可承载标题、正文与自定义内容的浮层面板。支持 12 向放置、双轴偏移、开合动画、portal、modal 化与嵌套浮层。
基础用法
点击触发
放置方向
placement 支持 12 向:四基向 top / bottom / left / right 各配 -start / -end 交叉轴对齐(bottom-start 面板左缘对齐触发元素左缘,是最常见形态)。空间不足沿主轴翻转时对齐后缀保留(bottom-start → top-start),对齐后仍做视口夹取。
四个方向
12 向放置(-start / -end)
触发方式
trigger 控制触发方式:click(默认)/ hover / focus / contextmenu / manual,空格分隔可多选(如 "click hover")。hover 触发时 hover-delay / hover-hide-delay 控制开合防抖延时(默认 150 / 100ms,无延时 hover 会闪开闪关);悬停区域为触发元素 + 浮层面板(跨间隙移动不闪关)。manual 模式不绑定任何宿主事件,显隐完全由宿主 open 控制。
悬停触发
自定义开合延时
通用显隐延迟(open-delay / close-delay)
触屏降级(coarse pointer 自动生效)
触屏设备(pointer: coarse,如手机 / 平板)没有稳定的 hover 语义:点按会同时合成 hover 与 click,若沿用桌面行为会出现「刚打开即关闭」。当 trigger 含 hover 时,触屏下 hover 通道自动停用、降级为点按切换——点按触发元素打开、再点按关闭、点按浮层外部关闭(外点关闭替代 hover-out);fine pointer(桌面鼠标)行为完全不变。与受控 open、long-press-delay 触屏长按兼容:长按打开后抬手不会误关(800ms 收尾守卫),下一次点按才切换。
触屏点按切换(触屏设备自动生效)
禁用
disabled 禁用整个 popover:点击 / 悬停 / 聚焦 / 右键 / 按键触发均不响应,宿主降饱和(opacity .6)并同步 aria-disabled。禁用触发元素(如原生 disabled button)不会派发鼠标事件,可在外层包一层 span 再挂 popover(兼容方案)。
整体禁用
宽度定制
width 控制面板宽度:数字(px)、"trigger"(与触发元素同宽)或任意 CSS 值(如 50%)。width="trigger" 适合「面板与触发控件等宽」的下拉选择形态。
宽度定制(width)
布尔属性 same-width 是「面板与触发器严格等宽」的快捷通道(等效 width="trigger",但会同时内联归零 min-width——触发器比面板默认 min-width(200px)窄时也能严格等宽)。显式设置 width 属性时以 width 为准;移除 same-width 后宽度交还 CSS 兜底。
偏移与碰撞细调
offset 双轴偏移:"主轴距离" 或 "主轴距离, 交叉轴偏移"(默认 8, 0)。碰撞细调:collision-padding 视口夹取边距(默认 4px);fallback-placements 自定义回退序列(请求放不下时按序列逐一尝试);hide-when-detached 锚点完全脱离视口时隐藏面板。
双轴偏移(offset)
碰撞细调(collision-padding / fallback-placements / hide-when-detached)
初始焦点与键盘
focus-on-open 打开时焦点移入面板内首个可聚焦元素;initial-focus 指定选择器精确聚焦(优先级更高,解析不到回落 focus-on-open)。trigger-keys 指定按键在触发元素聚焦时切换开合(空格分隔多键)。
指定初始焦点(initial-focus)
打开后焦点直接进入下面的输入框:
按键打开(trigger-keys)
Portal 挂载点
append-to 把面板移到宿主容器之外(body 或选择器),避免被宿主容器的 overflow: hidden / clip 裁剪;定位基于视口坐标,移出后不受影响。面板移出 shadow 后点击面板内部仍不触发外部点击关闭。
Portal 挂载(append-to)
箭头与视口自动调整
默认显示指向触发元素中心的箭头(锚点中心在面板指向边上的投影;视口边缘避让导致面板偏移时,箭头仍跟随锚点中心);arrow="false" 隐藏箭头。默认空间不足时自动沿主轴翻转并避让视口边缘;auto-adjust-overflow="false" 关闭自动调整,面板保持声明 placement(可能溢出视口)。
箭头显隐与指向
箭头与角融合(arrow-merge)
关闭自动调整
自定义内容
自定义内容(slot=content)
slot="content" 可以放置任意自定义内容。 关闭按钮与声明式关层
closable 在面板右上角显示关闭按钮(part="close"),点击关闭并还原焦点。内容里任意元素加 data-popover="close" 即声明式关层——点击它关闭 popover(适合「确定 / 知道了」类操作按钮)。
关闭按钮与声明式关层
颜色变体
color 语义色变体:primary / success / warning / danger——面板 tint 底 + 语义色描边(含箭头),全部由 token 派生(dark 主题自动适配)。
颜色变体(color)
开合动画
面板打开 / 关闭播放 fade + scale 动画,transform-origin 随放置方向感知(从「对着触发元素的那条边」向外展开,-start/-end 贴到对齐边)。prefers-reduced-motion 下自动停用动画。
开合动画(方向感知)
内容实时与自动关闭
默认(无 fresh)关闭时内容冻结、打开时写入最新值;fresh 开启后关闭状态也持续同步内容(受控内容场景防闪烁)。auto-close 打开后超时自动关闭(引导提示 / onboarding 场景)。
fresh:关闭时内容持续更新
auto-close:超时自动关闭
嵌套浮层
浮层内容里可以再打开子浮层(popover / tooltip):子浮层从父浮层内容触发,层级与定位自动正确;父层关闭时子层一并关闭;Esc 逐层关闭并逐层还原焦点。
嵌套浮层(卡片内再弹)
父面板内再触发子浮层:
虚拟触发
virtual 模式没有真实锚点(同 tooltip):由宿主通过 virtual-x / virtual-y(视口坐标)或 virtual-anchor(锚点元素选择器)指定位置,适合图表、画布上的坐标提示;设置 open 控制显隐。虚拟模式下点击触发元素与外部点击都不改变状态,生命周期完全由宿主控制。
虚拟触发(画布坐标跟随)
移动鼠标查看坐标提示
虚拟触发(锚点坐标定位)
虚拟触发(锚点元素跟随)
Modal 化
modal 把 popover 变成模态浮层:全屏遮罩 + 焦点陷阱(Tab / Shift+Tab 在面板内循环,焦点逃逸拉回,仅最上层 modal 接管)+ 滚动锁(拦截 wheel / 滚动方向键,滚动条保持可见)+ aria-modal。点击遮罩关闭并还原焦点。
Modal 化(遮罩 + 焦点锁 + 滚动锁)
焦点被锁在面板内:
无障碍关联与键盘
触发元素自动同步 ARIA 关联:aria-haspopup="dialog" + aria-expanded(随开合)+ aria-controls(面板 id),屏幕阅读器可感知浮层状态。trigger-keys 默认 Enter Space——聚焦触发元素按 Enter / 空格即可开合(原生 button 上与合成 click 幂等,不会闪开闪关),可用属性覆盖为其他按键。
键盘开合(默认 Enter / Space)
trap-focus 焦点陷阱
trap-focus 把焦点陷阱从 modal 中独立出来:不显示遮罩、不锁滚动,但 Tab / Shift+Tab 在面板内循环不逃逸——适合表单浮层(背景仍可交互,焦点不出走)。modal 与 trap-focus 叠加幂等(陷阱只挂一份)。
trap-focus(表单浮层焦点不逃逸)
关闭行为开关
close-on-outside / close-on-escape(默认均为 true 保持现行为)分别控制「点击外部关闭」与「Esc 关闭」;oas-before-close 是可取消关闭事件(preventDefault() 阻止关闭),所有关闭入口(触发切换 / 外点 / Esc / 关闭按钮 / 声明式关层 / 遮罩 / auto-close / 面板内选择)都会先过它——detail.source 标注来源。
关闭开关 + 可取消关闭(oas-before-close)
尺寸与样式变量
size 尺寸档位:small / medium(默认)/ large——内边距、最小宽度与字号随之缩放(走 token)。面板样式可通过组件级 CSS 变量定制:--oas-popover-bg / --oas-popover-border / --oas-popover-shadow / --oas-popover-radius / --oas-popover-padding / --oas-popover-min-width(默认值回落全局 token,dark 主题自动跟随)。
尺寸档位(size)
面板 CSS 变量定制(--oas-popover-*)
结构化插槽与说明
slot="header" 接管整个头部(标题区让位);slot="footer" 渲染底部操作区(与正文以分隔线区隔);slot="description" 渲染补充说明并自动关联 aria-describedby(屏幕阅读器可朗读面板描述)。
header / footer / description 插槽
关闭后更改立即生效,无需保存。
高度约束与面板内滚动
available-height 把面板最大高度约束为主轴方向视口剩余空间;scrollable 开启面板内滚动(头部 / 底部固定、正文区滚动),长内容不再撑出视口。长列表场景两者常组合使用。
scrollable + available-height(长列表滚动)
面板高度不超出视口剩余空间,正文区内部滚动:
焦点归还与空态
final-focus 指定关闭后焦点归还的目标元素(选择器;也可用 finalFocusEl property 传入元素,优先级更高);缺省归还触发元素。hide-empty 在面板无任何内容(无 title / content / 插槽内容)时保持隐藏——防止空白面板闪现。
final-focus(关闭后焦点到指定元素)
hide-empty(无内容不弹空白面板)
滚动行为(sticky / close-on-scroll)
sticky 三档:partial(默认,滚动跟随重定位)/ always(锚点滚出视口后面板贴视口边缘保持可见)/ off(不跟随滚动)。close-on-scroll 则改为「滚动即关闭」——下拉选择类浮层在滚动时直接收起更符合直觉。
sticky=always(锚点滚出后面板贴边保持)
↓ 滚动这里 ↓
close-on-scroll(滚动即关闭)
右键光标定位与触屏长按
trigger="contextmenu" 右键在光标处打开面板(不再锚定触发元素中心);触屏设备上按住长按同样在触点处打开(移动端没有右键),长按时长可用 long-press-delay 调整(ms,默认 500)。打开后滚动页面,面板回到触发元素侧跟随(光标点没有滚动语义)。
contextmenu 能力(主路径已默认内含):右键光标定位、触屏长按与 placement/size 断点简写属于独立能力包(子路径
@oas-ui/ui/feedback/popover/contextmenu,import 即注册);主路径@oas-ui/ui/feedback/popover已默认内含,无需显式引用(下方 demo 走全量入口同样已内含)。若改用纯核入口@oas-ui/ui/feedback/popover/core引入,则不含这些增强——此时trigger="contextmenu"仍支持基础右键触发打开面板(锚定触发元素,core 行为),但不定位光标、无长按与断点响应(控制台 dev 告警提示显式 import 能力包或换回主路径)。
contextmenu 光标定位(右键 / 触屏长按)
面板出现在右键光标处(触屏长按同样触发,长按时长由 long-press-delay 调整):
面板内选择后自动关闭
dismiss-on-select 开启后面板内任意点击(含插槽内容)视为完成选择并自动关闭——选项面板免写 data-popover="close"。关闭同样会经过 oas-before-close(可拦截)。
dismiss-on-select(选择即关)
内容销毁与懒挂载
destroy-on-hide 关闭时卸载面板内容呈现(插槽节点脱离分配、属性文本清空),重新打开时再挂载——重内容(图表 / 大列表)浮层在关闭期间零渲染开销,等价懒挂载。宿主 light DOM 节点不会删除。
destroy-on-hide(关闭销毁、重开重建)
关闭后此内容被卸载(重开时重新挂载):
断点响应与触发细调
placement / size 支持断点简写(协议同 space/grid):"bottom md:right" = 基础值 + 空格分隔的 断点:值(sm=640 / md=768 / lg=1024 / xl=1280,移动优先 min-width)——窄屏在下方、≥768px 在右侧。断点响应属于 contextmenu 能力包(主路径默认内含,见上文「contextmenu 能力」;纯核 core 入口回落基础值)。trigger="mousedown" 按下即开(无需抬起,比 click 快一拍)。
placement 断点简写(拖动窗口宽度看切换)
trigger=mousedown(按下即开)
render-panel 纯面板渲染
render-panel 面向宿主自组场景:popover 不绑定任何触发(一律按 manual 处理、不写触发元素 ARIA),配合 virtual-x / virtual-y 坐标或 append-to 由宿主完全接管摆放与显隐——比如自定义触发器、画布内嵌面板。
render-panel(宿主自组触发器)
受控显示
open 属性受控:外部按钮设置 / 移除 open 控制显隐(点击外部 / Esc 仍会关闭)。
受控显示(open 属性)
API
oas-popover
属性
| 属性 | 说明 | 类型 | 默认值 |
|---|---|---|---|
append-to | portal 挂载点:打开时面板移入目标容器(body 或 CSS 选择器),关闭移回宿主 shadow;适合面板被宿主容器裁剪(overflow)的场景 | string | — |
arrow | 是否显示箭头(默认 true;arrow="false" 隐藏,箭头元素与 ::part(arrow) 保留) | string | true |
arrow-merge | 箭头与面板角融合:直角三角与面板角共边融合——直角贴角点、直角边与面板两边共线(描边与面板描边共带续接),尖端正交指向锚点,对应角圆角归零;仅 -start/-end 位置生效,center placement 不触发 | boolean | — |
auto-adjust-overflow | 视口边缘自动翻转与避让(默认 true;"false" 关闭,保持声明 placement,可能溢出视口) | string | true |
auto-close | 打开后超时自动关闭(毫秒),如 auto-close="3000";未设置不自动关闭 | string | — |
available-height | 内容最大可用高度约束(视口剩余空间) | boolean | — |
closable | 面板右上角显示关闭按钮(part="close"),点击关闭并还原焦点到触发元素 | boolean | — |
close-delay | 通用关闭延迟(毫秒,默认 0;非 hover 触发路径生效,hover 路径优先 hover-hide-delay) | string | — |
close-on-escape | Esc 关闭(默认 true) | — | — |
close-on-outside | 点击外部关闭(默认 true) | string | true |
close-on-scroll | 滚动时关闭 | boolean | — |
collision-padding | 视口边缘夹取边距(px,默认 4),面板贴边避让时保留的间距 | string | — |
color | 颜色变体:primary / success / warning / danger(面板 tint 底 + 语义色描边,走 token 派生变量含 dark 变体);未设置或非法值保持默认中性面板 | string | — |
content | 正文文本 | string | — |
destroy-on-hide | 关闭后销毁面板内容呈现(宿主 light DOM 节点保留:改写 slot 名脱离分配并 hidden,属性文本清空);重新打开瞬间恢复挂载 | boolean | — |
disabled | 整体禁用:click / hover / focus / contextmenu / trigger-keys 触发均不响应,宿主降饱和并同步 aria-disabled | boolean | — |
dismiss-on-select | 面板内容点击(命名 slot 内容 / 面板内部含关闭按钮)视为完成选择即关闭(可被 before-close 拦截);触发元素点击不触发(防点开即关) | boolean | — |
fallback-placements | 自定义回退序列(逗号或空格分隔,如 "left, right"):请求 placement 放不下时按序列逐一尝试 fit,首个 fit 者胜出,全不 fit 取序列末位并夹取;未设置走默认主轴翻转 | string | — |
final-focus | 关闭后焦点归还目标(选择器) | string | — |
focus-on-open | 打开时焦点移入面板内首个可聚焦元素 | boolean | — |
fresh | 关闭时也持续更新内容(默认关闭态冻结内容,打开时写入最新值;fresh 开启后关闭态同步写入) | boolean | — |
hide-empty | 无内容时隐藏面板 | boolean | — |
hide-when-detached | 锚点完全脱离视口时隐藏面板(打开语义保留,避免孤悬屏外) | boolean | — |
hover-delay | hover 触发时打开防抖延时(毫秒,默认 150;未设置回落 open-delay) | string | — |
hover-hide-delay | hover 触发时关闭防抖延时(毫秒,默认 100;未设置回落 close-delay) | string | — |
initial-focus | 打开时聚焦指定选择器元素(宿主 light DOM 优先,含 slot 内容;解析不到回落 focus-on-open),优先级高于 focus-on-open | string | — |
long-press-delay | 触屏长按触发延迟毫秒数(默认 500;仅 contextmenu 触发生效,属 contextmenu 能力包,能力语义见上文) | — | — |
modal | modal 化:全屏遮罩 + 焦点陷阱(Tab 面板内循环)+ 滚动锁 + aria-modal;点击遮罩关闭 | boolean | — |
offset | 双轴偏移:"主轴距离" 或 "主轴距离, 交叉轴偏移"(px,默认 8, 0),如 offset="12, 20" | — | — |
open | 受控显示(布尔属性,存在即显示) | boolean | — |
open-delay | 通用打开延迟(毫秒,默认 0;非 hover 触发路径生效,hover 路径优先 hover-delay) | string | — |
placement | 浮层位置(12 向:四基向 top/bottom/left/right 各配 -start/-end 交叉轴对齐) | string | top |
render-panel | 纯面板渲染模式:无触发语义(一律按 manual 处理),定位回落 virtual 坐标/锚点,宿主可用 append-to 自行摆放 | boolean | — |
same-width | 面板宽度 = 触发器宽度(严格等宽;显式 width 优先) | boolean | — |
scrollable | 面板内容超限滚动 | boolean | — |
size | 尺寸档 | string | medium |
sticky | 视口边缘贴住:partial(默认)/ always / off(关闭贴边跟随) | string | partial |
title | 标题文本(渲染进可见标题区;读取后即从宿主移除,不残留原生悬浮提示;清空传空串);富内容用 slot="title" | string | — |
trap-focus | 焦点陷阱独立开关(与 modal 遮罩解耦,表单浮层 Tab 不逃逸) | boolean | — |
trigger | 触发方式:click(默认)/ hover / focus / contextmenu / manual,空格分隔可多选(如 "click hover")。含 hover 时触屏(coarse pointer)自动降级为点按切换:点按开、再点按/外点关,桌面 hover 行为不变 | string | click |
trigger-keys | 指定按键在触发元素聚焦时切换开合(空格分隔,如 "Enter Space";缺省即绑定 Enter Space) | string | Enter Space |
virtual | 虚拟触发模式(同 tooltip,不依赖锚点元素) | boolean | — |
virtual-anchor | 虚拟锚点元素选择器(virtual-x/virtual-y 未设置时生效) | — | — |
virtual-x | 虚拟锚点 x(视口坐标,px) | — | — |
virtual-y | 虚拟锚点 y(视口坐标,px) | — | — |
width | 面板宽度:数字(px)/ "trigger"(与触发元素同宽)/ 任意 CSS 值(如 50%、240px);未设置保持默认 | string | — |
事件
| 事件 | 说明 |
|---|---|
oas-before-close | 关闭请求前派发(各关闭入口汇聚:trigger/outside/escape/close-btn/declarative/backdrop/auto/dismiss/scroll),cancelable,detail: { source },preventDefault 阻止关闭 |
oas-open-change | open 状态变化,detail: { open } |
插槽
| 名称 | 说明 |
|---|---|
| 默认 | 面板主体内容 |
content | 面板内容(与默认插槽等价,具名便于显式指定) |
description | 描述富内容插槽(aria-describedby 关联) |
footer | 底部结构化插槽 |
header | 头部结构化插槽 |
title | 标题富内容插槽,有内容时覆盖 title 属性文案 |
CSS 变量
| CSS 变量 | 默认值 |
|---|---|
--oas-glass-blur | none |
--oas-glass-ring | transparent |
--oas-origin-x | center |
--oas-origin-y | center |
--oas-popover-arrow-height | 12px |
--oas-popover-arrow-radius | 0 |
--oas-popover-arrow-width | 12px |
--oas-popover-bg | var(--oas-color-bg) |
--oas-popover-border | var(--oas-color-border) |
--oas-popover-font-size | var(--oas-font-size-md) |
--oas-popover-min-width | 200px |
--oas-popover-padding | var(--oas-space-4) |
--oas-popover-radius | var(--oas-radius-md) |
--oas-popover-shadow | var(--oas-shadow-md) |
点击触发元素切换显隐,点击外部或按 Esc 关闭;role="dialog"。嵌套浮层:父关闭时级联关闭子层,Esc 逐层关闭并还原焦点到触发元素。