Grouped List

Items grouped into categories.

Tasks

To do 2

  • Draft the proposal
  • Review budget

Done 1

  • Kickoff call

html

<section class="grouped-list grouped-list--bordered" style="--accent:#D9C8F0;--gap:14px;">
  <h3 class="grouped-list__heading">Tasks</h3>
  <div class="grouped-list__group">
    <h4 id="gl-todo" class="grouped-list__group-heading">To do <span class="grouped-list__count">2</span></h4>
    <ul aria-labelledby="gl-todo" class="grouped-list__items">
      <li>Draft the proposal</li>
      <li>Review budget</li>
    </ul>
  </div>
  <div class="grouped-list__group">
    <h4 id="gl-done" class="grouped-list__group-heading">Done <span class="grouped-list__count">1</span></h4>
    <ul aria-labelledby="gl-done" class="grouped-list__items">
      <li>Kickoff call</li>
    </ul>
  </div>
</section>
<style>
.grouped-list { font: 14px/1.4 system-ui, sans-serif; color: #241430; background: #FAF7F2; max-width: 320px; width: 320px; padding: 16px; border-radius: 12px; box-sizing: border-box; }
.grouped-list__heading { margin: 0 0 12px; font-size: 15px; font-weight: 700; }
.grouped-list__group + .grouped-list__group { margin-top: var(--gap); }
.grouped-list__group-heading { margin: 0 0 6px; font-size: 12px; font-weight: 700; color: var(--accent); text-transform: uppercase; letter-spacing: .03em; }
.grouped-list__count { background: var(--accent); color: #241430; border-radius: 10px; padding: 1px 7px; font-size: 11px; font-weight: 700; }
.grouped-list__items { list-style: none; margin: 0; padding: 0; }
.grouped-list__items li { padding: 7px 2px; }
.grouped-list--bordered .grouped-list__group { border: 1px solid #24143020; border-radius: 10px; padding: 10px 12px; }
.grouped-list--card .grouped-list__group { background: #fff; border-radius: 10px; padding: 10px 12px; box-shadow: 0 2px 8px rgba(36,20,48,.08); }
</style>

tailwind

<section class="grouped-list-tw grouped-list-tw--bordered font-sans text-sm text-[#241430] bg-[#FAF7F2] max-w-[320px] w-[320px] p-4 rounded-xl box-border" style="--accent:#D9C8F0;--gap:14px;">
  <h3 class="m-0 mb-3 text-[15px] font-bold">Tasks</h3>
  <div class="grouped-list-tw__group">
    <h4 id="glt-todo" class="m-0 mb-1.5 text-xs font-bold uppercase tracking-wide" style="color:#D9C8F0">To do <span class="rounded-full px-1.5 text-[11px] font-bold" style="background:#D9C8F0">2</span></h4>
    <ul aria-labelledby="glt-todo" class="list-none m-0 p-0">
      <li class="py-1.5 px-0.5">Draft the proposal</li>
      <li class="py-1.5 px-0.5">Review budget</li>
    </ul>
  </div>
  <div class="grouped-list-tw__group">
    <h4 id="glt-done" class="m-0 mb-1.5 text-xs font-bold uppercase tracking-wide" style="color:#D9C8F0">Done <span class="rounded-full px-1.5 text-[11px] font-bold" style="background:#D9C8F0">1</span></h4>
    <ul aria-labelledby="glt-done" class="list-none m-0 p-0">
      <li class="py-1.5 px-0.5">Kickoff call</li>
    </ul>
  </div>
</section>
<style>
.grouped-list-tw__group + .grouped-list-tw__group { margin-top: var(--gap); }
.grouped-list-tw--bordered .grouped-list-tw__group { border: 1px solid #24143020; border-radius: 10px; padding: 10px 12px; }
.grouped-list-tw--card .grouped-list-tw__group { background: #fff; border-radius: 10px; padding: 10px 12px; box-shadow: 0 2px 8px rgba(36,20,48,.08); }
</style>

react

export function GroupedList({
  heading = "Tasks",
  style = "bordered",
  accent = "#D9C8F0",
  showCounts = true,
  itemGap = 14,
}) {
  const groupStyle = {
    ...(style === "bordered" ? { border: "1px solid #24143020", borderRadius: 10, padding: "10px 12px" } : {}),
    ...(style === "card" ? { background: "#fff", borderRadius: 10, padding: "10px 12px", boxShadow: "0 2px 8px rgba(36,20,48,.08)" } : {}),
  };
  const groups = [
    { id: "todo", label: "To do", count: 2, items: ["Draft the proposal", "Review budget"] },
    { id: "done", label: "Done", count: 1, items: ["Kickoff call"] },
  ];
  return (
    <section style={{ fontFamily: "system-ui, sans-serif", fontSize: 14, color: "#241430", background: "#FAF7F2", maxWidth: 320, width: 320, padding: 16, borderRadius: 12, boxSizing: "border-box" }}>
      <h3 style={{ margin: "0 0 12px", fontSize: 15, fontWeight: 700 }}>{heading}</h3>
      {groups.map((g, i) => (
        <div key={g.id} style={{ ...groupStyle, marginTop: i === 0 ? 0 : itemGap }}>
          <h4 id={`rgl-${g.id}`} style={{ margin: "0 0 6px", fontSize: 12, fontWeight: 700, color: accent, textTransform: "uppercase", letterSpacing: ".03em" }}>
            {g.label}{showCounts && <span style={{ marginLeft: 6, background: accent, color: "#241430", borderRadius: 10, padding: "1px 7px", fontSize: 11, fontWeight: 700 }}>{g.count}</span>}
          </h4>
          <ul aria-labelledby={`rgl-${g.id}`} style={{ listStyle: "none", margin: 0, padding: 0 }}>
            {g.items.map((item) => <li key={item} style={{ padding: "7px 2px" }}>{item}</li>)}
          </ul>
        </div>
      ))}
    </section>
  );
}