轻提示Toast
在屏幕中央或底部短暂提示一条结果信息(成功 / 失败 / 警告 / 加载中),不打断当前操作
反馈 规格 12 · 轻提示 Toast 6 端实现 触摸优先
<!-- ① 令牌:PC 令牌 + 移动端 --kole-m-* 合成单文件,引一次 -->
<link rel="stylesheet" href="kole-ui/mobile/tokens.css">
<!-- ② 本组件样式(全量则用 kole-ui/mobile/components/index.css) -->
<link rel="stylesheet" href="kole-ui/mobile/components/mobile-toast.css">
<!-- ③ 结构照抄下方任一演示块(类名与 6 端实现一致) -->
演示
每个演示都是真实渲染:预览帧加载 frameworks-mobile/Toast.html?demo=<id>(只显示该演示块),代码是该演示块在演示页里的原文,可复制。全部演示同屏可看 演示页 ↗。
01 组件类型
最常用的形态:居中显示一条结果,不打断当前操作。
查看代码(演示页原文 · 12 行)
<section class="demo-block" data-demo="center">
<p class="demo-label">居中提示(position=center,点按钮显示;再点一次收起)</p>
<div class="demo-frame" data-assert="toast-center">
<div class="demo-frame__body">
<button class="demo-trigger" type="button" id="toast-center-trigger"
data-behavior="click-toggles-class:#toast-center-box|is-open">显示居中提示</button>
</div>
<div class="kole-m-toast kole-m-toast--center" id="toast-center-box" role="status" aria-live="polite">
<span class="kole-m-toast__text">已保存到草稿箱</span>
</div>
</div>
</section>成功 / 警告等语气只改变图标与语义,文字始终是唯一的信息载体。
查看代码(演示页原文 · 13 行)
<section class="demo-block" data-demo="tone">
<p class="demo-label">五种语气(tone=success / warning / danger:同一浮层,语义由文字承担)</p>
<div class="demo-frame" data-assert="toast-tone">
<div class="kole-m-toast kole-m-toast--center is-open" role="status" aria-live="polite">
<span class="kole-m-toast__icon" aria-hidden="true">✓</span>
<span class="kole-m-toast__text">提交成功</span>
</div>
<div class="kole-m-toast kole-m-toast--bottom is-open" role="status" aria-live="polite">
<span class="kole-m-toast__icon" aria-hidden="true">!</span>
<span class="kole-m-toast__text">网络不稳定,已切换离线模式</span>
</div>
</div>
</section>出现在顶部时不遮挡底部操作区,适合「已复制」这类轻反馈。
查看代码(演示页原文 · 8 行)
<section class="demo-block" data-demo="top">
<p class="demo-label">顶部提示(position=top:不遮挡底部操作区)</p>
<div class="demo-frame" data-assert="toast-top">
<div class="kole-m-toast kole-m-toast--top is-open" role="status" aria-live="polite">
<span class="kole-m-toast__text">已复制订单号</span>
</div>
</div>
</section>02 组件状态
tone=loading 时图标持续旋转并标记 aria-busy;减少动态偏好下停止旋转。
查看代码(演示页原文 · 9 行)
<section class="demo-block" data-demo="loading">
<p class="demo-label">加载中(tone=loading:图标旋转,aria-busy=true;reduced-motion 下停止)</p>
<div class="demo-frame" data-assert="toast-loading">
<div class="kole-m-toast kole-m-toast--center kole-m-toast--loading is-open" role="status" aria-live="polite" aria-busy="true">
<span class="kole-m-toast__icon" aria-hidden="true">◌</span>
<span class="kole-m-toast__text">正在提交…</span>
</div>
</div>
</section>mask=true 时遮罩吸收手势,下方内容在提示期间不可点。
查看代码(演示页原文 · 9 行)
<section class="demo-block" data-demo="mask">
<p class="demo-label">带遮罩(mask=true:吸收手势,下方不可点;默认展开以便对照)</p>
<div class="demo-frame" data-assert="toast-mask">
<div class="kole-m-toast__mask is-open" aria-hidden="true"></div>
<div class="kole-m-toast kole-m-toast--center is-open" role="status" aria-live="polite">
<span class="kole-m-toast__text">请稍候,正在校验信息</span>
</div>
</div>
</section>API
props / events / slots 为 6 端实现的公共接口(说明文字取自规格对应小节)。字段名与各端源码逐名核对:node tools/verify-mobile-docs.mjs
Props
| 名称 | 类型 | 默认值 | 说明 | 必传 |
|---|---|---|---|---|
tone | 'info' | 'success' | 'warning' | 'danger' | 'loading' | 'info' | 变体 tone:语义配色与图标形态(规格 §12.3) | N |
position | 'center' | 'bottom' | 'top' | 'center' | 变体 position:提示出现的位置(规格 §12.3) | N |
open | boolean | false | 状态 open:展开可见(规格 §12.4) | N |
mask | boolean | false | 是否显示遮罩并阻断下方交互(规格 §12.3) | N |
text | string | '' | 提示文字(规格 §12.2 text) | N |
「必传」按严格定义:实现里**没有默认值**时才为 Y(本门禁逐条核对 props 与各端源码的默认值,防止契约与实现脱节)。
事件
| 名称 | 参数 | 说明 |
|---|
插槽
| 名称 | 说明 |
|---|---|
default | 提示内容(与 text 二选一;text 优先) |
CSS 变量
组件级变量(在组件样式表里定义)。业务侧可在自己的作用域内覆盖,不必改组件源码。
| 名称 | 默认值 | 说明 |
|---|---|---|
--kole-m-toast-radius | var(--kole-radius-large) | 组件内部默认值,可在业务侧覆盖 |
--kole-m-toast-max-width | 70% | 组件内部默认值,可在业务侧覆盖 |
何时使用
- 在屏幕中央或底部短暂提示一条结果信息(成功 / 失败 / 警告 / 加载中),不打断当前操作
- 提示本身不可点(不抢焦点、不阻断),mask=true 时遮罩吸收手势
- 出现 / 消失动画 240ms;自动关闭时长由宿主控制(本组件只负责显示态)
- 容器 role="status" + aria-live="polite"(结果朗读一次,不反复打断)
- 图标为装饰(aria-hidden="true"),语义全部由文字承担
交互与触控
- 提示本身不可点(不抢焦点、不阻断),mask=true 时遮罩吸收手势
- 出现 / 消失动画 240ms;自动关闭时长由宿主控制(本组件只负责显示态)
无障碍
- 容器 role="status" + aria-live="polite"(结果朗读一次,不反复打断)
- 图标为装饰(aria-hidden="true"),语义全部由文字承担
相似组件
从「该用哪一个」的角度区分;PC 端的对应实现见 PC 文档站。
| 组件 | 何时用它而不是本组件 |
|---|---|
| 弹出层Popup | 需要承载内容与操作时用弹出层,轻提示只承载一句结果 |
| 通知栏NoticeBar | 常驻的通告用通知栏,一次性反馈用轻提示 |
| 对话框Dialog | 需要用户做决定(确认 / 取消)时用对话框,提示不需要决定 |
规格未定 / 禁止发明
| 类别 | 条目 |
|---|---|
| 禁止发明 | 自动关闭的默认时长 |
| 禁止发明 | 多条提示的排队 / 合并策略 |
| 规格未定 | 单行文字的最大宽度与换行规则 |
| 规格未定 | 是否需要点击穿透设置 |
结构(anatomy)
| 字段 | 说明 |
|---|---|
toast | 浮层本体,居中于视口 |
icon | 状态图标(可选) |
text | 提示文字 |
mask | 可选透明遮罩(mask=true 时阻断下方点击) |
变体维度与类名映射
类名映射由构建脚本从契约 variantClasses 生成,并被 verify:mobile-docs 逐条对照组件 CSS 校验(类/变量必须真实存在)。
| 维度 | 取值 | 对应类名 / 变量 |
|---|---|---|
tone | info / success / warning / danger / loading | info (由数据驱动,无专属类) success (由数据驱动,无专属类) warning (由数据驱动,无专属类) danger (由数据驱动,无专属类) loading .kole-m-toast--loading |
position | center / bottom / top | center .kole-m-toast--center bottom .kole-m-toast--bottom top .kole-m-toast--top |
mask | false / true | false (由数据驱动,无专属类) true .kole-m-toast__mask |
代表变体
| 变体 | 标签 |
|---|---|
tone=info · position=center · mask=false | 居中提示 |
tone=success · position=bottom · mask=false | 底部成功提示 |
tone=loading · position=center · mask=true | 加载中(带遮罩) |
用到的令牌
构建时从本组件样式表扫描得出。蓝色为移动端自有令牌,绿色为继承的 PC 令牌(改一处两端生效)。
6 端源码
同一组件的六份实现(生产环境的类名与结构一致,差异只在技术栈写法与单位)。点开查看,右侧可复制。
frameworks-mobile/Toast.css · 纯样式(CSS) · 91 行
/* Kole UI Mobile · Toast 样式 — 对齐移动端规格 §12
轻提示:深底 + 反色文字(亮色 15.78:1 / 暗色 15.00:1,脚本实测),居中于视口;
不抢焦点、不阻断交互(mask=true 时遮罩吸收手势);动效 240ms。 */
.kole-m-toast {
--kole-m-toast-radius: var(--kole-radius-large);
--kole-m-toast-max-width: 70%;
position: fixed;
z-index: 2100;
box-sizing: border-box;
display: flex;
flex-direction: column;
align-items: center;
gap: var(--kole-space-8);
max-width: var(--kole-m-toast-max-width);
padding: var(--kole-space-16) var(--kole-space-20);
border-radius: var(--kole-m-toast-radius);
background: var(--kole-color-tooltip-bg);
color: var(--kole-color-text-inverse);
font-family: var(--kole-font-family);
font-size: var(--kole-m-font-size-label);
line-height: 1.4;
text-align: center;
opacity: 0;
pointer-events: none;
transition: opacity var(--kole-m-duration-slide) var(--kole-m-ease-slide);
}
.kole-m-toast.is-open {
opacity: 1;
}
/* 变体 position:三个位置(默认居中) */
.kole-m-toast--center {
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
.kole-m-toast--bottom {
left: 50%;
bottom: calc(var(--kole-m-touch-target) + var(--kole-m-safe-bottom));
transform: translateX(-50%);
}
.kole-m-toast--top {
left: 50%;
top: calc(var(--kole-m-navbar-height) + var(--kole-m-safe-top));
transform: translateX(-50%);
}
.kole-m-toast__icon {
flex: 0 0 auto;
font-size: var(--kole-m-font-size-title);
line-height: 1;
}
.kole-m-toast__text {
word-break: break-word;
}
/* 状态 loading:图标持续旋转(reduced-motion 下停止) */
.kole-m-toast--loading .kole-m-toast__icon {
animation: kole-m-toast-spin 1s linear infinite;
}
@keyframes kole-m-toast-spin {
to { transform: rotate(360deg); }
}
@media (prefers-reduced-motion: reduce) {
.kole-m-toast { transition: none; }
.kole-m-toast--loading .kole-m-toast__icon { animation: none; }
}
/* 变体 mask=true:透明遮罩吸收手势,下方不可点 */
.kole-m-toast__mask {
position: fixed;
inset: 0;
z-index: 2099;
background: var(--kole-color-mask-strong);
opacity: 0;
pointer-events: none;
transition: opacity var(--kole-m-duration-slide) var(--kole-m-ease-slide);
}
.kole-m-toast__mask.is-open {
opacity: 1;
pointer-events: auto;
}
frameworks-mobile/Toast.html · H5 原生(无框架) · 119 行
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
<title>Kole UI Mobile · Toast(H5)</title>
<link rel="stylesheet" href="../.design_library/kole-ui-mobile/colors_and_type.css">
<link rel="stylesheet" href="Toast.css">
<style>
body { margin: 0; background: var(--kole-color-page-bg); font-family: var(--kole-font-family); color: var(--kole-color-text-body); }
.demo { max-width: 375px; margin: 0 auto; padding: var(--kole-m-gutter) 0; }
.demo-label { margin: 0; padding: var(--kole-space-12) var(--kole-m-gutter) var(--kole-space-8);
font-size: var(--kole-m-font-size-label); color: var(--kole-color-text-secondary); }
/* 展示框:transform 建立包含块,提示与遮罩限定在框内(生产环境落在视口上) */
.demo-frame { position: relative; max-width: 375px; margin: 0 auto; height: 200px;
overflow: hidden; transform: translateZ(0); background: var(--kole-color-page-bg);
border-block: 1px solid var(--kole-color-border); }
.demo-frame__body { padding: var(--kole-m-gutter); font-size: var(--kole-m-font-size-label);
color: var(--kole-color-text-secondary); line-height: 1.7; }
.demo-trigger { min-height: var(--kole-m-touch-target); padding: 0 var(--kole-m-gutter);
border: 1px solid var(--kole-color-border); border-radius: var(--kole-radius-base);
background: var(--kole-color-card-bg); color: var(--kole-color-text-body);
font-family: inherit; font-size: var(--kole-m-font-size-label); cursor: pointer; touch-action: manipulation; }
.demo-trigger:focus-visible { outline: 2px solid var(--kole-color-focus-ring); outline-offset: 2px; }
.demo-block[hidden] { display: none; }
</style>
</head>
<body>
<div class="demo">
<section class="demo-block" data-demo="center">
<p class="demo-label">居中提示(position=center,点按钮显示;再点一次收起)</p>
<div class="demo-frame" data-assert="toast-center">
<div class="demo-frame__body">
<button class="demo-trigger" type="button" id="toast-center-trigger"
data-behavior="click-toggles-class:#toast-center-box|is-open">显示居中提示</button>
</div>
<div class="kole-m-toast kole-m-toast--center" id="toast-center-box" role="status" aria-live="polite">
<span class="kole-m-toast__text">已保存到草稿箱</span>
</div>
</div>
</section>
<section class="demo-block" data-demo="tone">
<p class="demo-label">五种语气(tone=success / warning / danger:同一浮层,语义由文字承担)</p>
<div class="demo-frame" data-assert="toast-tone">
<div class="kole-m-toast kole-m-toast--center is-open" role="status" aria-live="polite">
<span class="kole-m-toast__icon" aria-hidden="true">✓</span>
<span class="kole-m-toast__text">提交成功</span>
</div>
<div class="kole-m-toast kole-m-toast--bottom is-open" role="status" aria-live="polite">
<span class="kole-m-toast__icon" aria-hidden="true">!</span>
<span class="kole-m-toast__text">网络不稳定,已切换离线模式</span>
</div>
</div>
</section>
<section class="demo-block" data-demo="top">
<p class="demo-label">顶部提示(position=top:不遮挡底部操作区)</p>
<div class="demo-frame" data-assert="toast-top">
<div class="kole-m-toast kole-m-toast--top is-open" role="status" aria-live="polite">
<span class="kole-m-toast__text">已复制订单号</span>
</div>
</div>
</section>
<section class="demo-block" data-demo="loading">
<p class="demo-label">加载中(tone=loading:图标旋转,aria-busy=true;reduced-motion 下停止)</p>
<div class="demo-frame" data-assert="toast-loading">
<div class="kole-m-toast kole-m-toast--center kole-m-toast--loading is-open" role="status" aria-live="polite" aria-busy="true">
<span class="kole-m-toast__icon" aria-hidden="true">◌</span>
<span class="kole-m-toast__text">正在提交…</span>
</div>
</div>
</section>
<section class="demo-block" data-demo="mask">
<p class="demo-label">带遮罩(mask=true:吸收手势,下方不可点;默认展开以便对照)</p>
<div class="demo-frame" data-assert="toast-mask">
<div class="kole-m-toast__mask is-open" aria-hidden="true"></div>
<div class="kole-m-toast kole-m-toast--center is-open" role="status" aria-live="polite">
<span class="kole-m-toast__text">请稍候,正在校验信息</span>
</div>
</div>
</section>
</div>
<script>
/* 演示页交互:点触发器切换提示显示态(真实业务里由请求结果驱动)。 */
(function () {
var trig = document.getElementById('toast-center-trigger');
var box = document.getElementById('toast-center-box');
if (!trig || !box) return;
trig.addEventListener('click', function () {
box.classList.toggle('is-open');
});
})();
</script>
<script>
/* ?demo=<id> → 只显示该演示块(文档站按块预览用;无参数时全部显示,测试与回归走无参数路径) */
(function () {
var id = new URLSearchParams(location.search).get('demo');
if (!id) return;
var blocks = Array.prototype.slice.call(document.querySelectorAll('.demo-block'));
var hit = false;
blocks.forEach(function (b) {
var on = b.getAttribute('data-demo') === id;
if (on) hit = true;
b.hidden = !on;
});
if (!hit) { blocks.forEach(function (b) { b.hidden = false; }); return; }
document.body.classList.add('demo-single');
blocks.forEach(function (b) {
var label = b.querySelector('.demo-label');
if (label && !b.hidden) label.hidden = true;
});
})();
</script>
</body>
</html>
frameworks-mobile/Toast.jsx · React · 35 行
import React from 'react';
import './Toast.css';
/* 轻提示(移动端)— 规格 §12;容器 role=status + aria-live=polite(朗读一次不打断);
提示本身不可点、不抢焦点;mask=true 时遮罩吸收手势。 */
export default function Toast({
tone = 'info',
position = 'center',
open = false,
mask = false,
text = '',
children = null,
}) {
const content = text || children;
const cls =
'kole-m-toast' +
` kole-m-toast--${position}` +
(tone === 'loading' ? ' kole-m-toast--loading' : '') +
(open ? ' is-open' : '');
return (
<>
<div className={'kole-m-toast__mask' + (open && mask ? ' is-open' : '')} aria-hidden="true" />
<div className={cls} role="status" aria-live="polite" aria-busy={tone === 'loading' ? 'true' : undefined}>
{tone === 'loading' ? (
<span className="kole-m-toast__icon" aria-hidden="true">
◌
</span>
) : null}
<span className="kole-m-toast__text">{content}</span>
</div>
</>
);
}
frameworks-mobile/Toast.vue2.vue · Vue 2 · 49 行
<template>
<div>
<div class="kole-m-toast__mask" :class="{ 'is-open': open && mask }" aria-hidden="true"></div>
<div
class="kole-m-toast"
:class="toastClass"
role="status"
aria-live="polite"
:aria-busy="tone === 'loading' ? 'true' : null"
>
<span v-if="tone === 'loading'" class="kole-m-toast__icon" aria-hidden="true">◌</span>
<span class="kole-m-toast__text">{{ content }}</span>
</div>
</div>
</template>
<script>
export default {
name: 'KoleMToast',
props: {
tone: { type: String, default: 'info' },
position: { type: String, default: 'center' },
open: { type: Boolean, default: false },
mask: { type: Boolean, default: false },
text: { type: String, default: '' }
},
computed: {
content: function () {
if (this.text) return this.text;
var nodes = (this.$slots && this.$slots.default) || [];
return nodes
.map(function (n) {
return n.children && typeof n.children === 'string' ? n.children : '';
})
.join('');
},
toastClass: function () {
return [
'kole-m-toast--' + this.position,
this.tone === 'loading' ? 'kole-m-toast--loading' : '',
this.open ? 'is-open' : ''
].filter(Boolean);
}
}
};
</script>
<style src="./Toast.css"></style>
frameworks-mobile/Toast.vue3.vue · Vue 3 · 43 行
<template>
<div>
<div class="kole-m-toast__mask" :class="{ 'is-open': open && mask }" aria-hidden="true"></div>
<div
class="kole-m-toast"
:class="toastClass"
role="status"
aria-live="polite"
:aria-busy="tone === 'loading' ? 'true' : null"
>
<span v-if="tone === 'loading'" class="kole-m-toast__icon" aria-hidden="true">◌</span>
<span class="kole-m-toast__text">{{ content }}</span>
</div>
</div>
</template>
<script setup>
import { computed, useSlots } from 'vue';
const props = defineProps({
tone: { type: String, default: 'info' },
position: { type: String, default: 'center' },
open: { type: Boolean, default: false },
mask: { type: Boolean, default: false },
text: { type: String, default: '' }
});
const slots = useSlots();
const content = computed(() => {
if (props.text) return props.text;
const nodes = slots.default ? slots.default() : [];
return nodes.map((n) => (typeof n.children === 'string' ? n.children : '')).join('');
});
const toastClass = computed(() => [
`kole-m-toast--${props.position}`,
props.tone === 'loading' ? 'kole-m-toast--loading' : '',
props.open ? 'is-open' : ''
].filter(Boolean));
</script>
<style src="./Toast.css"></style>
frameworks-mobile/Toast.uniapp.vue · uni-app(跨端:小程序 / App / H5) · 105 行
<template>
<view>
<view class="kole-m-toast__mask" :class="{ 'is-open': open && mask }" aria-hidden="true"></view>
<view
class="kole-m-toast"
:class="toastClass"
:role="'status'"
:aria-busy="tone === 'loading' ? 'true' : 'false'"
>
<text v-if="tone === 'loading'" class="kole-m-toast__icon">◌</text>
<text class="kole-m-toast__text">{{ content }}</text>
</view>
</view>
</template>
<script setup>
/* uni-app 端 · 轻提示(移动端)— 规格 §12
跨端差异:用 view / text;尺寸用 rpx(2rpx ≈ 1px);朗读靠 role="status"(H5 产物生效);
提示不承载交互,因此没有 touch 事件。 */
import { computed, useSlots } from 'vue';
const props = defineProps({
tone: { type: String, default: 'info' },
position: { type: String, default: 'center' },
open: { type: Boolean, default: false },
mask: { type: Boolean, default: false },
text: { type: String, default: '' }
});
const slots = useSlots();
const content = computed(() => {
if (props.text) return props.text;
const nodes = slots.default ? slots.default() : [];
return nodes.map((n) => (typeof n.children === 'string' ? n.children : '')).join('');
});
const toastClass = computed(() => [
`kole-m-toast--${props.position}`,
props.tone === 'loading' ? 'kole-m-toast--loading' : '',
props.open ? 'is-open' : ''
].filter(Boolean));
</script>
<style>
.kole-m-toast {
--kole-m-toast-max-width: 70%;
--kole-m-touch-target: 88rpx;
--kole-m-navbar-height: 88rpx;
--kole-m-font-size-label: 28rpx;
--kole-m-font-size-title: 34rpx;
position: fixed;
z-index: 2100;
box-sizing: border-box;
display: flex;
flex-direction: column;
align-items: center;
max-width: var(--kole-m-toast-max-width);
padding: 32rpx 40rpx;
border-radius: 16rpx;
background-color: var(--kole-color-tooltip-bg);
color: var(--kole-color-text-inverse);
font-size: var(--kole-m-font-size-label);
text-align: center;
opacity: 0;
}
.kole-m-toast.is-open { opacity: 1; }
.kole-m-toast--center {
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
.kole-m-toast--bottom {
left: 50%;
bottom: calc(var(--kole-m-touch-target) + var(--kole-m-safe-bottom));
transform: translateX(-50%);
}
.kole-m-toast--top {
left: 50%;
top: calc(var(--kole-m-navbar-height) + var(--kole-m-safe-top));
transform: translateX(-50%);
}
.kole-m-toast__icon {
margin-bottom: 16rpx;
font-size: var(--kole-m-font-size-title);
}
.kole-m-toast__text { word-break: break-word; }
.kole-m-toast__mask {
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 2099;
background-color: var(--kole-color-mask-strong);
opacity: 0;
}
</style>
测试与回归
断言在真实的 375×640 设备帧里跑(引擎与 PC 侧共用 tests/_runtime.js,触控行为动词来自移动端 tests/mobile/_behaviors.js)。
断言 16 条 · 全部通过 报告 2026-09-22 23:01:05
node site/dev-server.js &
REG_BASE=http://127.0.0.1:3311 node tools/run-mobile-regression.mjs # 全量 5 个组件
npm run verify:mobile-docs # 本页内容完整性 + API 与源码一致性
设计契约
components/mobile-toast.json(点击展开原始 JSON)
{
"schemaVersion": 1,
"sourceKind": "authored-spec",
"provenance": "authored-in-repo",
"specFile": "spec/移动端规格.md",
"confidence": "high",
"specSection": "12 · 轻提示 Toast",
"slug": "mobile-toast",
"name": "轻提示 Toast",
"semanticTypeCandidates": [
"toast",
"message",
"snackbar"
],
"variantDimensions": [
{
"name": "tone",
"values": [
"info",
"success",
"warning",
"danger",
"loading"
]
},
{
"name": "position",
"values": [
"center",
"bottom",
"top"
]
},
{
"name": "mask",
"values": [
"false",
"true"
]
}
],
"representativeVariants": [
{
"tone": "info",
"position": "center",
"mask": "false",
"label": "居中提示"
},
{
"tone": "success",
"position": "bottom",
"mask": "false",
"label": "底部成功提示"
},
{
"tone": "loading",
"position": "center",
"mask": "true",
"label": "加载中(带遮罩)"
}
],
"anatomy": {
"toast": "浮层本体,居中于视口",
"icon": "状态图标(可选)",
"text": "提示文字",
"mask": "可选透明遮罩(mask=true 时阻断下方点击)"
},
"structurePatterns": {
"tone": "info / success / warning / danger / loading",
"position": "center / bottom / top",
"mask": "false / true(阻断交互)"
},
"usageHints": [
"在屏幕中央或底部短暂提示一条结果信息(成功 / 失败 / 警告 / 加载中),不打断当前操作",
"提示本身不可点(不抢焦点、不阻断),mask=true 时遮罩吸收手势",
"出现 / 消失动画 240ms;自动关闭时长由宿主控制(本组件只负责显示态)",
"容器 role=\"status\" + aria-live=\"polite\"(结果朗读一次,不反复打断)",
"图标为装饰(aria-hidden=\"true\"),语义全部由文字承担"
],
"doNotInvent": [
"自动关闭的默认时长",
"多条提示的排队 / 合并策略"
],
"unknowns": [
"单行文字的最大宽度与换行规则",
"是否需要点击穿透设置"
],
"interaction": [
"提示本身不可点(不抢焦点、不阻断),mask=true 时遮罩吸收手势",
"出现 / 消失动画 240ms;自动关闭时长由宿主控制(本组件只负责显示态)"
],
"accessibility": [
"容器 role=\"status\" + aria-live=\"polite\"(结果朗读一次,不反复打断)",
"图标为装饰(aria-hidden=\"true\"),语义全部由文字承担"
],
"api": {
"source": "implementation",
"note": "props / events / slots 为 6 端实现的公共接口(说明文字取自规格对应小节)。字段名与各端源码逐名核对:node tools/verify-mobile-docs.mjs",
"requiredNote": "「必传」按严格定义:实现里**没有默认值**时才为 Y(本门禁逐条核对 props 与各端源码的默认值,防止契约与实现脱节)。",
"props": [
{
"name": "tone",
"type": "'info' | 'success' | 'warning' | 'danger' | 'loading'",
"default": "'info'",
"desc": "变体 tone:语义配色与图标形态(规格 §12.3)",
"required": false
},
{
"name": "position",
"type": "'center' | 'bottom' | 'top'",
"default": "'center'",
"desc": "变体 position:提示出现的位置(规格 §12.3)",
"required": false
},
{
"name": "open",
"type": "boolean",
"default": "false",
"desc": "状态 open:展开可见(规格 §12.4)",
"required": false
},
{
"name": "mask",
"type": "boolean",
"default": "false",
"desc": "是否显示遮罩并阻断下方交互(规格 §12.3)",
"required": false
},
{
"name": "text",
"type": "string",
"default": "''",
"desc": "提示文字(规格 §12.2 text)",
"required": false
}
],
"events": [],
"slots": [
{
"name": "default",
"desc": "提示内容(与 text 二选一;text 优先)"
}
]
},
"variantClasses": {
"tone": {
"info": [],
"success": [],
"warning": [],
"danger": [],
"loading": [
".kole-m-toast--loading"
]
},
"position": {
"center": [
".kole-m-toast--center"
],
"bottom": [
".kole-m-toast--bottom"
],
"top": [
".kole-m-toast--top"
]
},
"mask": {
"false": [],
"true": [
".kole-m-toast__mask"
]
}
},
"demos": [
{
"id": "center",
"group": "01 组件类型",
"title": "居中提示",
"desc": "最常用的形态:居中显示一条结果,不打断当前操作。",
"variant": "position=center"
},
{
"id": "tone",
"group": "01 组件类型",
"title": "语义语气",
"desc": "成功 / 警告等语气只改变图标与语义,文字始终是唯一的信息载体。",
"variant": "tone=success|warning"
},
{
"id": "top",
"group": "01 组件类型",
"title": "顶部提示",
"desc": "出现在顶部时不遮挡底部操作区,适合「已复制」这类轻反馈。",
"variant": "position=top"
},
{
"id": "loading",
"group": "02 组件状态",
"title": "加载中",
"desc": "tone=loading 时图标持续旋转并标记 aria-busy;减少动态偏好下停止旋转。",
"variant": "状态 loading"
},
{
"id": "mask",
"group": "02 组件状态",
"title": "带遮罩",
"desc": "mask=true 时遮罩吸收手势,下方内容在提示期间不可点。",
"variant": "mask=true"
}
],
"related": [
{
"slug": "mobile-popup",
"why": "需要承载内容与操作时用弹出层,轻提示只承载一句结果"
},
{
"slug": "mobile-noticebar",
"why": "常驻的通告用通知栏,一次性反馈用轻提示"
},
{
"slug": "mobile-dialog",
"why": "需要用户做决定(确认 / 取消)时用对话框,提示不需要决定"
}
]
}