对话框

data-modal 契约 + behaviors/modal:焦点陷阱、滚动锁、Esc/遮罩关闭、焦点还原;Sheet 四向贴边滑入。

<div class="demo-row">  <button class="btn" data-modal-open="demo-modal-sm">小号对话框</button>  <button class="btn" data-modal-open="demo-modal-md">默认对话框</button>  <button class="btn" data-modal-open="demo-modal-lg">大号对话框</button>  <button class="btn" data-modal-open="demo-sheet-right">右侧 Sheet</button></div> <div class="modal-backdrop" data-modal="demo-modal-sm" hidden>  <div class="modal modal--sm">    <div class="modal-header">      <div>        <h3 class="modal-title">小号对话框</h3>        <p class="modal-description">max-width 360px,Esc / 点击遮罩可关闭</p>      </div>      <button class="modal-close" data-modal-close aria-label="关闭">×</button>    </div>    <div class="modal-body">      <p>焦点已锁定在对话框内:Tab 会在关闭钮与底部按钮之间循环,关闭后焦点还原到触发按钮。</p>    </div>    <div class="modal-footer">      <button class="btn btn-sm" data-modal-close>取消</button>      <button class="btn btn-sm btn-primary" data-modal-close>确定</button>    </div>  </div></div> <div class="modal-backdrop" data-modal="demo-modal-md" hidden>  <div class="modal">    <div class="modal-header">      <div>        <h3 class="modal-title">默认对话框</h3>        <p class="modal-description">max-width 480px</p>      </div>      <button class="modal-close" data-modal-close aria-label="关闭">×</button>    </div>    <div class="modal-body">      <p>面板进场为 dialogScaleIn 200ms:scale(.96) translateY(8px) → scale(1) translateY(0)。</p>      <p>同时只允许打开一个浮层对话框——打开另一个会自动关掉当前这个。</p>    </div>    <div class="modal-footer">      <button class="btn btn-sm" data-modal-close>取消</button>      <button class="btn btn-sm btn-primary" data-modal-close>保存</button>    </div>  </div></div> <div class="modal-backdrop" data-modal="demo-modal-lg" hidden>  <div class="modal modal--lg">    <div class="modal-header">      <div>        <h3 class="modal-title">大号对话框</h3>        <p class="modal-description">max-width 640px,内容超高时 modal-body 内部滚动</p>      </div>      <button class="modal-close" data-modal-close aria-label="关闭">×</button>    </div>    <div class="modal-body">      <p>背景已滚动锁定(body overflow 关闭时恢复旧值)。</p>      <p>尺寸档:modal--sm 360 / 默认 480 / modal--lg 640 / modal--xl 800 / modal--full 全屏。</p>    </div>    <div class="modal-footer">      <button class="btn btn-sm" data-modal-close>关闭</button>    </div>  </div></div> <div class="modal-backdrop" data-modal="demo-sheet-right" hidden>  <div class="modal modal--sheet-right">    <div class="modal-header">      <div>        <h3 class="modal-title">右侧 Sheet</h3>        <p class="modal-description">宽 360px,translateX(40px) 滑入</p>      </div>      <button class="modal-close" data-modal-close aria-label="关闭">×</button>    </div>    <div class="modal-body">      <p>Sheet 变体:modal--sheet-left / right / top / bottom,圆角只保留非贴边侧。</p>      <p>宽度档:默认 360px,modal--sheet-sm 280px,modal--sheet-lg 480px(上下方向为 300px / 50vh)。</p>    </div>    <div class="modal-footer">      <button class="btn btn-sm" data-modal-close>关闭</button>    </div>  </div></div>

安装

bun add @icen.ai/ui

一行引入该组件(CSS 与 behavior 自动带上,无需关心内部 CSS 文件名):

import { initModal } from '@icen.ai/ui/kit/modal';initModal();

也可以用脚手架直接打印引入行:

bunx --bun @icen.ai/ui add modal

Astro 项目注意:kit 入口里的 CSS import 在页面 <script> 里会被 Astro 的 client bundle 摇掉——请改在布局 frontmatter 里引 CSS(import '@icen.ai/ui/components/modal.css';),JS 行为仍可走 kit。Vite SPA / webpack 项目无此问题。

用法

import { initModal, open, close } from '@icen.ai/ui/behaviors/modal';initModal(); // 事件委托一次绑定,后续 open(id) / close(id) 可编程控制