busy-office-ui
Menu

Permissions & read-only

Every ERP screen renders differently for a clerk, an approver and an auditor. The framework ships no permission system — that is your server's job — but it has a clear stance on how a screen should degrade, and getting it wrong is how apps leak both information and support tickets.

The three degradations, and when to use each

TreatmentUse whenNever use when
Omit — the control is not in the DOM The user can never perform this action in this context (not an approver, wrong role, wrong record state). A control you can never use teaches nothing and invites a ticket. The action is temporarily unavailable — omitting makes the UI look broken or inconsistent between visits.
Disable — present, disabled The action is possible for this user but blocked right now by state (nothing selected, form invalid, save in flight). Always pair with the reason. The block is permanent for this user — that is what omit is for. A permanently disabled button is a permanent question.
Read-only — the value, not the input The user may see the data but not change it. Render key-value facts, not disabled inputs. Never as <input disabled> for display: disabled controls are skipped by keyboard, often fail contrast, and cannot be copied — while claiming to be a form field.
<!-- A viewer sees FACTS, not disabled inputs. Same data, honest control. -->
<dl class="bo-kv">
  <div><dt>Vendor</dt><dd>Umbrella Logistics</dd></div>
  <div><dt>Amount</dt><dd class="bo-u-tabular">$12,400.00</dd></div>
</dl>

<!-- An editor sees the form -->
<div class="bo-form-field">
  <label class="bo-form-field__label" for="vendor">Vendor</label>
  <input class="bo-input" id="vendor" value="Umbrella Logistics" />
</div>

The rule of thumb: the shape of the screen tells the truth about what you can do. A viewer's record detail is a document; an editor's is a form.

What the framework gives you

  • Key-value facts — the read-only counterpart of a form section.
  • readonly on .bo-input — for the narrow case where the field must stay a field (it submits, its text is selectable) but cannot be edited.
  • --readonly / --disabled on rich text — with the two-channel rule: the modifier is the visible half, aria-readonly/aria-disabled the programmatic half.
  • Empty and error states — including the "you don't have access" screen, which is a state, not an error.

Rules that keep permission bugs out

  1. The server decides; the UI reflects. Never rely on a hidden control as a security boundary — every action needs its own server-side check. A missing button is a courtesy, not a lock.
  2. Don't render what you then hide. Sending a disabled control that contains data the user may not see leaks it in the HTML. Omit it server-side.
  3. Say why, once. A disabled control needs its reason nearby (a hint, a tooltip, or a line above the action bar) — not on hover only, which touch and keyboard users never get.
  4. Degrade the screen, not each control. A viewer's screen should look deliberate — facts and a timeline — rather than an editor's screen with everything greyed out.
  5. Field-level permissions are a data contract. If the server may withhold a field, the pattern's data contract should say so; a silently missing field reads as a bug.
↑↓ move Enter open Esc close