Permissions
The permissions matrix is the screen behind “who can do what”: roles across the top, each with its actions, and the resources down the side as a tree. Each cell says allow, deny or nothing; a cell that says nothing takes its answer from the roles that role inherits and from the resource above, and shows that answer faintly, so the effect of every grant is in front of the reader.
Every cell can say why it is what it is — “Denied: Staff denies Read on Payroll” — in its accessible name and in the line under the grid. Click to cycle a cell, select a range and set it in one go, undo and redo, export to CSV. Changes are held until Save, and your page sees them first: it can refuse any change, or review the whole set before it is written.
Who can do what in a finance system
Four roles: Managers inherit from Staff, and the Finance lead from Managers and the external Auditors. Move round the cells with the arrow keys and read the line under the grid; Space cycles a cell through inherit, allow and deny. Payroll only has Read and Export.
Get the code
<script src="/assets/bmx-components.min.js"></script>
<bmx-permission-matrix id="pm-finance" label="Finance permissions" style="inline-size: 100%; --bmx-permission-matrix-height: 30rem"></bmx-permission-matrix>
<script>
const permissionMatrix = document.getElementById('pm-finance');
permissionMatrix.resources = [ /* your resources */ ];
</script>
Changes reviewed before they are saved
The same kind of matrix for a hospital's records. This page refuses, on its own authority, any grant to Students on patient records. Save does not write anything straight away: the page shows what would change, and you approve the set or throw it away.
Get the code
<script src="/assets/bmx-components.min.js"></script>
<bmx-permission-matrix id="pm-records" label="Patient record permissions" style="inline-size: 100%; --bmx-permission-matrix-height: 24rem"></bmx-permission-matrix>
<script>
const permissionMatrix = document.getElementById('pm-records');
permissionMatrix.resources = [ /* your resources */ ];
</script>
When roles disagree
The engineer on call inherits from Engineers, who may deploy to production, and from Contractors, who may not. Which wins is a policy: deny overrides, as most systems choose, or allow overrides. And a cell nothing decides falls to the default. Change either and the matrix works every cell out again.
Get the code
<script src="/assets/bmx-components.min.js"></script>
<bmx-select id="pm-conflict" label="When inherited roles disagree" value="deny-overrides" style="inline-size: min(100%, 16rem)">
<bmx-option value="deny-overrides">Deny overrides</bmx-option>
<bmx-option value="allow-overrides">Allow overrides</bmx-option>
</bmx-select>
<bmx-select id="pm-default" label="When nothing decides" value="deny" style="inline-size: min(100%, 14rem)">
<bmx-option value="deny">Deny</bmx-option>
<bmx-option value="allow">Allow</bmx-option>
</bmx-select>
<bmx-permission-matrix id="pm-oncall" label="Deployment permissions" readonly style="inline-size: 100%; --bmx-permission-matrix-height: 18rem"></bmx-permission-matrix>
<script>
const permissionMatrix = document.getElementById('pm-oncall');
permissionMatrix.resources = [ /* your resources */ ];
</script>
A whole organisation
Twenty roles and nearly three hundred resources in a tree of departments, systems and areas — over sixteen thousand cells. Choose which roles to show as columns; every cell is still worked out through inheritance, and only the rows in view are drawn.
Get the code
<script src="/assets/bmx-components.min.js"></script>
<bmx-select id="pm-pick" label="Roles shown" multiple max-tags="6" style="inline-size: min(100%, 30rem)"></bmx-select>
<bmx-permission-matrix id="pm-org" label="Organisation permissions" style="inline-size: 100%; --bmx-permission-matrix-height: 28rem"></bmx-permission-matrix>
<script>
const select = document.getElementById('pm-pick');
select.options = [ /* your options */ ];
const permissionMatrix = document.getElementById('pm-org');
permissionMatrix.resources = [ /* your resources */ ];
</script>