zz-shopify-components
Version:
Reusable Shopify components for theme projects
187 lines (147 loc) • 5.9 kB
Markdown
轻量、高性能、可扩展、可访问(a11y)的 Web Component 弹窗,基于原生 `<dialog>` 能力并内置降级方案。
### 特性
- 原生 `<dialog>`:内建焦点管理、`aria-modal`,退化时提供焦点陷阱与交互保障
- 高性能:零依赖、轻量动画、滚动锁、Backdrop 点击关闭、ESC 关闭
- 可扩展:Shadow DOM、`::part(...)`、CSS 变量、事件、编程式 API
- 易用:全局触发器 `[data-zz-modal-target]`、`[data-zz-modal-close]`
- 移动端 Drawer:加 `sheet-on-mobile` 时,小屏(≤768px)自下而上贴底弹出
---
### 快速开始(Shopify / Theme Section)
在 Section 中确保加载脚本(`sections/zz-modal.liquid` 已内置):
```liquid
<script defer id="zz-modal-js" src="{{ 'zz-modal.js' | asset_url }}"></script>
```
无 JS 安全:
```css
zz-modal:not(:defined) { display: none !important; }
```
Section 中通过 CSS 变量响应式配置:
```css
#your-modal-id {
--zz-modal-radius: {{ section.settings.radius_mobile }}px;
--zz-modal-padding: {{ section.settings.padding_mobile }}px;
--zz-modal-width: {{ section.settings.width_mobile }};
}
@media (min-width: 768px) {
#your-modal-id {
--zz-modal-radius: {{ section.settings.radius_pc }}px;
--zz-modal-padding: {{ section.settings.padding_pc }}px;
--zz-modal-width: {{ section.settings.width_pc }};
}
}
```
Section 设置项(当前实现):
- 基本:`modal_id`(必填,挂到 `<zz-modal id="...">` 上)
- 显隐:`show_header`、`show_footer`
- 行为:`no_backdrop_close`(禁用遮罩点击关闭)
- 文案:`header_text`、`footer_text`
- 尺寸(移动/PC):`width_mobile`、`width_pc`(支持 `px` 或 `vw`)
- 圆角(移动/PC):`radius_mobile`、`radius_pc`(单位 px)
- 内边距(移动/PC):`padding_mobile`、`padding_pc`(单位 px)
> 当 slot 为空时,组件会自动隐藏 Header/Footer(属性 `no-header-auto` / `no-footer-auto`)。
---
### 基本用法
触发按钮 + 弹窗结构:
```html
<button data-zz-modal-target="#demo-modal">Open Modal</button>
<zz-modal id="demo-modal">
<div slot="header">标题</div>
<div>这里是内容</div>
<div slot="footer">
<button data-zz-modal-close>取消</button>
<button data-zz-modal-close>确定</button>
</div>
<!-- 内置关闭按钮也可用:shadow 内提供 part="close-button" 按钮 -->
<!-- 任何带 data-zz-modal-close 的元素都会关闭当前弹窗 -->
<!-- 任意元素 data-zz-modal-target="#id" 可打开对应弹窗 -->
```
移动端 Drawer 用法(≤768px 时贴底弹出):
```html
<zz-modal id="m1" sheet-on-mobile>
<div slot="header">标题</div>
<div>内容</div>
<div slot="footer"><button data-zz-modal-close>关闭</button></div>
<!-- Drawer 下 body 会自动为底部预留安全区 padding -->
</zz-modal>
```
---
### 编程式 API
```js
const modal = document.querySelector('#demo-modal');
modal.show(); // 打开
modal.hide(); // 关闭
modal.toggle(); // 切换
// 等价别名:modal.showModal() / modal.close()
```
---
### 属性(Attributes)
- `open`:受控显示状态(加上即显示,移除即关闭)
- `no-esc-close`:禁用 ESC 关闭
- `no-backdrop-close`:禁用点击遮罩关闭
- `no-scroll-lock`:禁用页面滚动锁
- `inert-others`:打开时让页面其它区域 inert(更强的可访问隔离)
- `no-header` / `no-footer`:强制隐藏 Header/Footer(无视 slot 内容)
- `no-header-auto` / `no-footer-auto`:组件自动加的属性,用于在对应 slot 为空时隐藏区域
- `sheet-on-mobile`:仅在小屏(≤768px)启用底部 Drawer 模式
---
### 事件(Events)
- `zz-modal:open`
- `zz-modal:close`
监听示例:
```js
document.querySelector('#demo-modal')
.addEventListener('zz-modal:open', () => console.log('opened'));
```
---
### 样式定制
CSS 变量:
- `--zz-modal-width`(默认 `min(720px, 92vw)`)
- `--zz-modal-max-width`(默认 `90vw`)
- `--zz-modal-max-height`(默认 `85vh`)
- `--zz-modal-radius`(默认 `12px`)
- `--zz-modal-padding`(默认 `16px`)
- `--zz-modal-background`(默认 `#fff`)
- `--zz-modal-shadow`(默认 `0 20px 60px rgba(0,0,0,0.2)`)
- `--zz-modal-backdrop`(默认 `rgba(0,0,0,0.5)`)
- `--zz-modal-backdrop-filter`(默认 `blur(0px)`)
- `--zz-modal-z-index`(默认 `9999`)
- `--zz-sheet-width`(Drawer 模式宽度,默认 `100%`)
Shadow Parts:
- `dialog`、`backdrop`、`panel`、`header`、`body`、`footer`、`close-button`
示例:
```css
/* 外部覆盖内部部件样式 */
zz-modal::part(dialog) { max-width: 720px; }
zz-modal::part(panel) { border-radius: 16px; }
/* 使用变量 */
#demo-modal { --zz-modal-background: #111; color: #fff; }
```
---
### 可访问性(a11y)
- 原生 `<dialog>` 优先,具备 `aria-modal`,ESC 关闭、焦点管理
- 降级路径提供基本焦点陷阱与键盘导航保障
- `inert-others` 可在打开时禁用页面其他区域交互
---
### 无 JS 安全
网络不佳或脚本未加载时隐藏 Light DOM,避免内容外露:
```css
zz-modal:not(:defined) { display: none !important; }
```
---
### 多实例
```html
<button data-zz-modal-target="#m1">打开 1</button>
<button data-zz-modal-target="#m2">打开 2</button>
<zz-modal id="m1">...</zz-modal>
<zz-modal id="m2">...</zz-modal>
```
---
### 常见问题
- 遮罩点击无效?检查是否设置了 `no-backdrop-close`。
- 页面滚动未锁定?移除 `no-scroll-lock` 或确认 CSS 未覆盖 `overflow`。
- Header/Footer 不显示?若未显式设置 `show_header/show_footer`,请确保 slot 有内容;或移除 `no-header/no-footer`。
- 移动端仍居中?确认 `<zz-modal>` 标签加了 `sheet-on-mobile`,并且视口宽度 ≤ 768px。
---
### 变更位置
- 组件实现:`assets/zz-modal.js`
- Section:`sections/zz-modal.liquid`