Notification Bell
介绍
新消息到达时铃铛摆动、角标弹出计数;点击展开通知面板,可全部标为已读。
纯 HTML、CSS 与少量原生 JS,没有框架依赖。复制提示词交给 Claude Code、Cursor、Trae 或 Codex,模型会把 Notification Bell 加进你的项目并换成你的配色。
适用场景
通知、提示、加载与状态反馈,让用户知道操作有没有成功。
安装
订阅 Builder 后,用 8pt CLI 装进项目,或在已接入 8pt MCP 的编辑器里直接调用。
$ npm i -g @8pt-dev/cli && 8pt add notification-bell
mcp › 使用 8pt MCP 调用 get_component("notification-bell")
代码
Notification Bell 的源码,动效只用 transform 与 opacity,并尊重减少动态效果设置。
<div class="x-bell open">
<button type="button" class="x-bell__btn" aria-label="通知" aria-expanded="true">
<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M6 8a6 6 0 1 1 12 0c0 7 3 8 3 8H3s3-1 3-8"/><path d="M10.3 20a1.9 1.9 0 0 0 3.4 0"/></svg>
<span class="x-bell__badge">0</span>
</button>
<div class="x-bell__panel" role="dialog" aria-label="通知列表">
<header><b>通知</b><button type="button" class="x-bell__read">全部已读</button></header>
<ul></ul>
</div>
</div>.x-bell{position:relative;display:grid;justify-items:center;width:min(300px,100%);height:210px;color:var(--d-fg,#fff)}
.x-bell__btn{position:relative;display:grid;place-items:center;width:44px;height:44px;border-radius:12px;border:1px solid var(--d-line,rgba(255,255,255,.12));background:var(--d-card,#14141c);color:inherit;cursor:pointer}
.x-bell__btn svg{transform-origin:50% 10%}
.x-bell__btn.ring svg{animation:x-bell-ring .8s cubic-bezier(.36,.07,.19,.97)}
@keyframes x-bell-ring{0%,100%{transform:rotate(0)}15%{transform:rotate(18deg)}30%{transform:rotate(-15deg)}45%{transform:rotate(11deg)}60%{transform:rotate(-7deg)}75%{transform:rotate(3deg)}}
.x-bell__badge{position:absolute;right:-6px;top:-6px;min-width:20px;height:20px;padding:0 5px;border-radius:999px;border:2px solid var(--d-bg,#09090d);background:#ff5a5f;color:#fff;font:700 10.5px/16px var(--d-font,system-ui,sans-serif);text-align:center;font-variant-numeric:tabular-nums;transform:scale(0);transition:transform .35s cubic-bezier(.3,1.6,.5,1)}
.x-bell__badge.on{transform:scale(1)}
.x-bell__badge.pop{animation:x-bell-pop .35s cubic-bezier(.3,1.6,.5,1)}
@keyframes x-bell-pop{50%{transform:scale(1.3)}}
.x-bell__panel{position:absolute;top:54px;left:0;right:0;border-radius:14px;background:var(--d-card,#14141c);border:1px solid var(--d-line,rgba(255,255,255,.12));box-shadow:0 22px 44px -22px rgba(0,0,0,.7);opacity:0;transform:translateY(-6px) scale(.98);transform-origin:top center;pointer-events:none;transition:opacity .2s,transform .25s cubic-bezier(.2,.8,.2,1)}
.x-bell.open .x-bell__panel{opacity:1;transform:none;pointer-events:auto}
.x-bell__panel header{display:flex;justify-content:space-between;align-items:center;padding:10px 12px;border-bottom:1px solid var(--d-line,rgba(255,255,255,.1));font:13px/1 var(--d-font,system-ui,sans-serif)}
…const init = (root) => {
const wrap = root.querySelector('.x-bell'), btn = wrap.querySelector('.x-bell__btn'), badge = wrap.querySelector('.x-bell__badge'), ul = wrap.querySelector('ul');
const msgs = [['Ava 评论了你的组件', '2 分钟前'], ['构建 #482 已通过', '5 分钟前'], ['Leo 邀请你加入 Atlas 项目', '12 分钟前'], ['新主题「Dusk」已上架', '1 小时前']];
let n = 0, unread = 0, timer;
const draw = () => { badge.textContent = unread > 9 ? '9+' : unread; badge.classList.toggle('on', unread > 0); };
const push = () => {
const [t, s] = msgs[n++ % msgs.length], li = document.createElement('li');
li.innerHTML = `<i></i><div>${t}<small>${s}</small></div>`; ul.prepend(li); if (ul.children.length > 4) ul.lastElementChild.remove();
unread++; draw(); badge.classList.remove('pop'); void badge.offsetWidth; badge.classList.add('pop');
btn.classList.remove('ring'); void btn.offsetWidth; btn.classList.add('ring');
};
btn.addEventListener('click', () => { const o = wrap.classList.toggle('open'); btn.setAttribute('aria-expanded', String(o)); });
…订阅 Builder 查看完整源码。查看价格