Permission Matrix

Grid mapping roles to permissions.

Document permissions

ViewerEditorAdmin
View✓✓✓
Edit—✓✓
Delete——✓

✓ allowed   — not allowed

html

<div class="pmx pmx--editor">
  <p class="pmx__title">Document permissions</p>
  <table class="pmx__table" style="--a:#A8E6C1;">
    <tr><th></th><th class="pmx__col pmx__col--viewer">Viewer</th><th class="pmx__col pmx__col--editor">Editor</th><th class="pmx__col pmx__col--admin">Admin</th></tr>
    <tr><td>View</td><td class="pmx__col pmx__col--viewer">&#10003;</td><td class="pmx__col pmx__col--editor">&#10003;</td><td class="pmx__col pmx__col--admin">&#10003;</td></tr>
    <tr><td>Edit</td><td class="pmx__col pmx__col--viewer">&mdash;</td><td class="pmx__col pmx__col--editor">&#10003;</td><td class="pmx__col pmx__col--admin">&#10003;</td></tr>
    <tr><td>Delete</td><td class="pmx__col pmx__col--viewer">&mdash;</td><td class="pmx__col pmx__col--editor">&mdash;</td><td class="pmx__col pmx__col--admin">&#10003;</td></tr>
  </table>
  <p class="pmx__legend">&#10003; allowed &nbsp; &mdash; not allowed</p>
</div>
<style>
.pmx { font: 12px/1.5 system-ui, sans-serif; color: #241430; background: #FAF7F2; padding: 16px; border-radius: 12px; width: 300px; box-sizing: border-box; }
.pmx__title { margin: 0 0 10px; font-weight: 700; }
.pmx__table { width: 100%; border-collapse: collapse; text-align: center; }
.pmx__table th, .pmx__table td { padding: 6px 4px; border-bottom: 1px solid #24143014; font-size: 11px; }
.pmx__table td:first-child, .pmx__table th:first-child { text-align: left; }
.pmx--viewer .pmx__col--viewer, .pmx--editor .pmx__col--editor, .pmx--admin .pmx__col--admin { background: var(--a); border-radius: 4px; }
.pmx__legend { margin: 10px 0 0; font-size: 10px; color: #24143099; }
</style>

tailwind

<div class="pmxtw pmxtw--editor font-sans text-[#241430] bg-[#FAF7F2] p-4 rounded-xl w-[300px] box-border">
  <p class="m-0 mb-2.5 font-bold">Document permissions</p>
  <table class="w-full border-collapse text-center" style="--a:#A8E6C1;">
    <tr><th></th><th class="pmxtw__col pmxtw__col--viewer py-1.5 px-1 border-b border-[#24143014] text-[11px]">Viewer</th><th class="pmxtw__col pmxtw__col--editor py-1.5 px-1 border-b border-[#24143014] text-[11px]">Editor</th><th class="pmxtw__col pmxtw__col--admin py-1.5 px-1 border-b border-[#24143014] text-[11px]">Admin</th></tr>
    <tr><td class="text-left py-1.5 px-1 border-b border-[#24143014] text-[11px]">View</td><td class="pmxtw__col pmxtw__col--viewer py-1.5 px-1 border-b border-[#24143014] text-[11px]">&#10003;</td><td class="pmxtw__col pmxtw__col--editor py-1.5 px-1 border-b border-[#24143014] text-[11px]">&#10003;</td><td class="pmxtw__col pmxtw__col--admin py-1.5 px-1 border-b border-[#24143014] text-[11px]">&#10003;</td></tr>
    <tr><td class="text-left py-1.5 px-1 border-b border-[#24143014] text-[11px]">Edit</td><td class="pmxtw__col pmxtw__col--viewer py-1.5 px-1 border-b border-[#24143014] text-[11px]">&mdash;</td><td class="pmxtw__col pmxtw__col--editor py-1.5 px-1 border-b border-[#24143014] text-[11px]">&#10003;</td><td class="pmxtw__col pmxtw__col--admin py-1.5 px-1 border-b border-[#24143014] text-[11px]">&#10003;</td></tr>
    <tr><td class="text-left py-1.5 px-1 text-[11px]">Delete</td><td class="pmxtw__col pmxtw__col--viewer py-1.5 px-1 text-[11px]">&mdash;</td><td class="pmxtw__col pmxtw__col--editor py-1.5 px-1 text-[11px]">&mdash;</td><td class="pmxtw__col pmxtw__col--admin py-1.5 px-1 text-[11px]">&#10003;</td></tr>
  </table>
  <p class="mt-2.5 text-[10px] text-[#24143099]">&#10003; allowed &nbsp; &mdash; not allowed</p>
</div>
<style>
.pmxtw--viewer .pmxtw__col--viewer, .pmxtw--editor .pmxtw__col--editor, .pmxtw--admin .pmxtw__col--admin { background: var(--a); border-radius: 4px; }
</style>

react

export function PermissionMatrix({
  highlightRole = "editor",
  accent = "#A8E6C1",
  showLegend = true,
  matrixName = "Document permissions",
}) {
  const roles = ["viewer", "editor", "admin"];
  const rows = [
    { label: "View", grants: { viewer: true, editor: true, admin: true } },
    { label: "Edit", grants: { viewer: false, editor: true, admin: true } },
    { label: "Delete", grants: { viewer: false, editor: false, admin: true } },
  ];
  const cell = (role) => ({ padding: "6px 4px", borderBottom: "1px solid #24143014", fontSize: 11, background: role === highlightRole ? accent : "transparent", borderRadius: role === highlightRole ? 4 : 0 });
  return (
    <div style={{ font: "12px/1.5 system-ui, sans-serif", color: "#241430", background: "#FAF7F2", padding: 16, borderRadius: 12, width: 300, boxSizing: "border-box" }}>
      <p style={{ margin: "0 0 10px", fontWeight: 700 }}>{matrixName}</p>
      <table style={{ width: "100%", borderCollapse: "collapse", textAlign: "center" }}>
        <tbody>
          <tr><th></th>{roles.map((r) => <th key={r} style={cell(r)}>{r[0].toUpperCase() + r.slice(1)}</th>)}</tr>
          {rows.map((row) => (
            <tr key={row.label}>
              <td style={{ textAlign: "left", padding: "6px 4px", borderBottom: "1px solid #24143014", fontSize: 11 }}>{row.label}</td>
              {roles.map((r) => <td key={r} style={cell(r)}>{row.grants[r] ? "\u2713" : "\u2014"}</td>)}
            </tr>
          ))}
        </tbody>
      </table>
      {showLegend && <p style={{ margin: "10px 0 0", fontSize: 10, color: "#24143099" }}>&#10003; allowed &nbsp; &mdash; not allowed</p>}
    </div>
  );
}