Notification Centre

Central notification interface.

Notifications , 2 unread

html

<section class="ntc" aria-label="Notifications" style="--accent:#BFD9F2;" data-filter="all">
  <div class="ntc__header">
    <h3 class="ntc__heading">Notifications <span class="ntc__count" aria-hidden="true">2</span><span class="ntc__sr">, 2 unread</span></h3>
    <button type="button" class="ntc__mark">Mark all as read</button>
  </div>
  <div class="ntc__tabs" role="group" aria-label="Filter notifications">
    <button type="button" class="ntc__tab ntc__tab--all">All</button>
    <button type="button" class="ntc__tab ntc__tab--unread">Unread</button>
    <button type="button" class="ntc__tab ntc__tab--mentions">Mentions</button>
  </div>
  <ul class="ntc__list">
    <li class="ntc__item ntc__item--unread">
      <p class="ntc__line"><strong>Priya Nair</strong> mentioned you in the roadmap</p>
      <time class="ntc__time" datetime="2026-08-18T14:32:00">2:32 PM</time>
    </li>
    <li class="ntc__item">
      <p class="ntc__line">Weekly digest is ready</p>
      <time class="ntc__time" datetime="2026-08-18T09:00:00">9:00 AM</time>
    </li>
    <li class="ntc__item">
      <p class="ntc__line">Build #482 succeeded</p>
      <time class="ntc__time" datetime="2026-08-17T18:12:00">Yesterday</time>
    </li>
  </ul>
</section>
<style>
.ntc { font: 13px/1.4 system-ui, sans-serif; color: #241430; background: #FAF7F2; max-width: 320px; width: 320px; padding: 16px; border-radius: 12px; box-sizing: border-box; }
.ntc__header { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-bottom: 10px; }
.ntc__heading { margin: 0; font-size: 15px; font-weight: 700; display: flex; align-items: center; gap: 6px; }
.ntc__count { background: var(--accent); border-radius: 999px; padding: 1px 7px; font-size: 11px; }
.ntc__sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
.ntc__mark { background: transparent; border: none; color: #241430a0; font: inherit; font-size: 11px; text-decoration: underline; cursor: pointer; flex-shrink: 0; }
.ntc__tabs { display: flex; gap: 6px; margin-bottom: 10px; }
.ntc__tab { background: #fff; border: 1px solid #24143020; border-radius: 999px; padding: 4px 10px; font: inherit; font-size: 11px; font-weight: 600; cursor: pointer; }
.ntc[data-filter="all"] .ntc__tab--all,
.ntc[data-filter="unread"] .ntc__tab--unread,
.ntc[data-filter="mentions"] .ntc__tab--mentions { background: var(--accent); border-color: transparent; }
.ntc__list { list-style: none; margin: 0; padding: 0; }
.ntc__item { padding: 9px 10px; border-radius: 8px; margin-bottom: 6px; background: #fff; }
.ntc__item--unread { box-shadow: inset 3px 0 0 var(--accent); }
.ntc__line { margin: 0; }
.ntc__time { display: block; margin-top: 3px; font-size: 11px; color: #24143080; }
</style>

tailwind

<section aria-label="Notifications" class="ntc-tw font-sans text-[13px] text-[#241430] bg-[#FAF7F2] max-w-[320px] w-[320px] p-4 rounded-xl box-border" style="--accent:#BFD9F2" data-filter="all">
  <div class="flex items-center justify-between gap-2 mb-2.5">
    <h3 class="m-0 text-[15px] font-bold flex items-center gap-1.5">Notifications <span aria-hidden="true" class="rounded-full py-0.5 px-1.5 text-[11px]" style="background:#BFD9F2">2</span><span class="sr-only">, 2 unread</span></h3>
    <button type="button" class="bg-transparent border-0 text-[#241430a0] text-[11px] underline cursor-pointer flex-shrink-0">Mark all as read</button>
  </div>
  <div role="group" aria-label="Filter notifications" class="flex gap-1.5 mb-2.5">
    <button type="button" class="ntc-tw__tab ntc-tw__tab--all bg-white border border-[#24143020] rounded-full py-1 px-2.5 text-[11px] font-semibold cursor-pointer">All</button>
    <button type="button" class="ntc-tw__tab ntc-tw__tab--unread bg-white border border-[#24143020] rounded-full py-1 px-2.5 text-[11px] font-semibold cursor-pointer">Unread</button>
    <button type="button" class="ntc-tw__tab ntc-tw__tab--mentions bg-white border border-[#24143020] rounded-full py-1 px-2.5 text-[11px] font-semibold cursor-pointer">Mentions</button>
  </div>
  <ul class="list-none m-0 p-0">
    <li class="py-2.5 px-2.5 rounded-lg mb-1.5 bg-white" style="box-shadow: inset 3px 0 0 #BFD9F2">
      <p class="m-0"><strong>Priya Nair</strong> mentioned you in the roadmap</p>
      <time class="block mt-1 text-[11px] text-[#24143080]" datetime="2026-08-18T14:32:00">2:32 PM</time>
    </li>
    <li class="py-2.5 px-2.5 rounded-lg mb-1.5 bg-white">
      <p class="m-0">Weekly digest is ready</p>
      <time class="block mt-1 text-[11px] text-[#24143080]" datetime="2026-08-18T09:00:00">9:00 AM</time>
    </li>
    <li class="py-2.5 px-2.5 rounded-lg bg-white">
      <p class="m-0">Build #482 succeeded</p>
      <time class="block mt-1 text-[11px] text-[#24143080]" datetime="2026-08-17T18:12:00">Yesterday</time>
    </li>
  </ul>
</section>
<style>
.sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
.ntc-tw[data-filter="all"] .ntc-tw__tab--all,
.ntc-tw[data-filter="unread"] .ntc-tw__tab--unread,
.ntc-tw[data-filter="mentions"] .ntc-tw__tab--mentions { background: #BFD9F2; border-color: transparent; }
</style>

react

export function NotificationCentre({
  heading = "Notifications",
  unreadCount = 2,
  accent = "#BFD9F2",
  filter = "all",
  showMarkAllRead = true,
}) {
  const tab = (value, label) => (
    <button type="button" key={value} style={{ background: filter === value ? accent : "#fff", borderColor: filter === value ? "transparent" : "#24143020", border: "1px solid", borderRadius: 999, padding: "4px 10px", font: "inherit", fontSize: 11, fontWeight: 600, cursor: "pointer" }}>{label}</button>
  );
  const items = [
    { unread: true, line: <><strong>Priya Nair</strong> mentioned you in the roadmap</>, time: "2026-08-18T14:32:00", label: "2:32 PM" },
    { unread: false, line: "Weekly digest is ready", time: "2026-08-18T09:00:00", label: "9:00 AM" },
    { unread: false, line: "Build #482 succeeded", time: "2026-08-17T18:12:00", label: "Yesterday" },
  ];
  return (
    <section aria-label={heading} style={{ fontFamily: "system-ui, sans-serif", fontSize: 13, color: "#241430", background: "#FAF7F2", maxWidth: 320, width: 320, padding: 16, borderRadius: 12, boxSizing: "border-box" }}>
      <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", gap: 8, marginBottom: 10 }}>
        <h3 style={{ margin: 0, fontSize: 15, fontWeight: 700, display: "flex", alignItems: "center", gap: 6 }}>
          {heading} <span aria-hidden="true" style={{ background: accent, borderRadius: 999, padding: "1px 7px", fontSize: 11 }}>{unreadCount}</span>
          <span style={{ position: "absolute", width: 1, height: 1, overflow: "hidden" }}>, {unreadCount} unread</span>
        </h3>
        {showMarkAllRead && <button type="button" style={{ background: "transparent", border: "none", color: "#241430a0", font: "inherit", fontSize: 11, textDecoration: "underline", cursor: "pointer", flexShrink: 0 }}>Mark all as read</button>}
      </div>
      <div role="group" aria-label="Filter notifications" style={{ display: "flex", gap: 6, marginBottom: 10 }}>
        {tab("all", "All")}
        {tab("unread", "Unread")}
        {tab("mentions", "Mentions")}
      </div>
      <ul style={{ listStyle: "none", margin: 0, padding: 0 }}>
        {items.map((item, i) => (
          <li key={i} style={{ padding: "9px 10px", borderRadius: 8, marginBottom: i === items.length - 1 ? 0 : 6, background: "#fff", boxShadow: item.unread ? `inset 3px 0 0 ${accent}` : "none" }}>
            <p style={{ margin: 0 }}>{item.line}</p>
            <time style={{ display: "block", marginTop: 3, fontSize: 11, color: "#24143080" }} dateTime={item.time}>{item.label}</time>
          </li>
        ))}
      </ul>
    </section>
  );
}