Novus Technologies Novus Technologies design kit v0.9.0
Menu

Foundations

Alignment & content fit

One short value, one line. Identifiers, names, chips, dates, amounts, button labels, and column headers never break across lines, at any width. Layouts make room, scroll, or truncate; they never squeeze a value into two or three rows. The rule is part of constitution Principle IV and an automated layout audit enforces it on every change.

Tables scroll, content stays whole

ReferenceMerchantProductAmount (USD)StatusSubmittedActions
TXN-241099Hillcrest Grocers Makatinovapay151.17 Status · Settled27 Aug 2026, 18:31
TXN-241073Riverside Fuel Station 12novastore401.87 Status · Pending27 Aug 2026, 17:57
TXN-240981Harbourfront Pharmacynovabank96.40 Status · Failed27 Aug 2026, 15:12
Right: single-line cells in a scroll container
<div class="card card--flush">
  <div class="tablewrap">
    <table class="table">
      <thead><tr><th>Reference</th><th>Merchant</th><th>Product</th><th class="num">Amount (USD)</th><th>Status</th><th>Submitted</th><th class="cell--actions">Actions</th></tr></thead>
      <tbody>
        <tr><td><code>TXN-241099</code></td><td>Hillcrest Grocers Makati</td><td>novapay</td><td class="num">151.17</td><td><span class="badge badge--success"><span class="badge__dot"></span> Status · Settled</span></td><td>27 Aug 2026, 18:31</td><td class="cell--actions"><button class="btn btn--ghost btn--sm">Detail</button></td></tr>
        <tr><td><code>TXN-241073</code></td><td>Riverside Fuel Station 12</td><td>novastore</td><td class="num">401.87</td><td><span class="badge badge--warning"><span class="badge__dot"></span> Status · Pending</span></td><td>27 Aug 2026, 17:57</td><td class="cell--actions"><button class="btn btn--ghost btn--sm">Detail</button></td></tr>
        <tr><td><code>TXN-240981</code></td><td>Harbourfront Pharmacy</td><td>novabank</td><td class="num">96.40</td><td><span class="badge badge--danger"><span class="badge__dot"></span> Status · Failed</span></td><td>27 Aug 2026, 15:12</td><td class="cell--actions"><button class="btn btn--ghost btn--sm">Detail</button></td></tr>
      </tbody>
    </table>
  </div>
</div>
ReferenceMerchantProductAmount (USD)StatusSubmitted
TXN-241099Hillcrest Grocers Makatinovapay151.17 Status · Settled27 Aug 2026, 18:31
TXN-241073Riverside Fuel Station 12novastore401.87 Status · Pending27 Aug 2026, 17:57
TXN-240981Harbourfront Pharmacynovabank96.40 Status · Failed27 Aug 2026, 15:12
Wrong: the same table squeezed to fit
<div style="max-width:26rem">
  <table class="table">
    <thead><tr><th>Reference</th><th>Merchant</th><th>Product</th><th class="num">Amount (USD)</th><th>Status</th><th>Submitted</th></tr></thead>
    <tbody>
      <tr><td><code>TXN-241099</code></td><td>Hillcrest Grocers Makati</td><td>novapay</td><td class="num">151.17</td><td><span class="badge badge--success"><span class="badge__dot"></span> Status · Settled</span></td><td>27 Aug 2026, 18:31</td></tr>
      <tr><td><code>TXN-241073</code></td><td>Riverside Fuel Station 12</td><td>novastore</td><td class="num">401.87</td><td><span class="badge badge--warning"><span class="badge__dot"></span> Status · Pending</span></td><td>27 Aug 2026, 17:57</td></tr>
      <tr><td><code>TXN-240981</code></td><td>Harbourfront Pharmacy</td><td>novabank</td><td class="num">96.40</td><td><span class="badge badge--danger"><span class="badge__dot"></span> Status · Failed</span></td><td>27 Aug 2026, 15:12</td></tr>
    </tbody>
  </table>
</div>

The squeezed table breaks the reference at its hyphen and stacks merchant names and dates onto several lines, so every row has a different height and nothing can be scanned. Wrap the table in .tablewrap instead.

Wrap only free text

Descriptions, notes, and messages are the only content that wraps. In tables they opt in with .cell--wrap, which keeps a readable minimum width; everywhere else paragraphs wrap as normal text.

Use the width you have

The mirror of the single-line rule: a paragraph should not stack into three or four lines while a quarter of its row sits empty. Portal text runs to the width of its container. A reading measure is a deliberate choice, marked with .measure, and belongs to long-form prose, not to a console screen.

Dashboards

Every workspace you can open, with the cases waiting in each one, the breaks still unmatched, and the approvals sitting with you today.

Right: the description runs the width of the row
<div class="pagehead" style="margin:0">
  <div class="pagehead__row"><h1>Dashboards</h1></div>
  <p class="muted pagehead__desc">Every workspace you can open, with the cases waiting in each one, the breaks still unmatched, and the approvals sitting with you today.</p>
</div>

Dashboards

Every workspace you can open, with the cases waiting in each one, the breaks still unmatched, and the approvals sitting with you today.

Wrong: the same sentence capped at a narrow column
<div class="pagehead" style="margin:0">
  <div class="pagehead__row"><h1>Dashboards</h1></div>
  <p class="muted pagehead__desc" style="max-width:18rem">Every workspace you can open, with the cases waiting in each one, the breaks still unmatched, and the approvals sitting with you today.</p>
</div>

The layout audit flags any portal text block that wraps to three or more lines while a quarter or more of its row stays unused and nothing sits beside it. Text that shares its row with a chart or a card is not flagged: the space is in use.

Truncate fixed slots

operations.administrator@novustech.com.sg
One line with an ellipsis; the full value stays available
<div class="card" style="max-width:15rem;display:flex;align-items:center;gap:var(--space-2);padding:var(--space-3)">
  <svg class="icon icon--sm" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="8" r="3.75"/><path d="M4.75 20a7.25 7.25 0 0 1 14.5 0"/></svg><span class="truncate" title="operations.administrator@novustech.com.sg">operations.administrator@novustech.com.sg</span>
</div>

When a value must fit a slot it cannot grow, such as a user name in the header, .truncate keeps it on one line with an ellipsis. The full value must stay available: in title, in the opened menu, or nearby.

Numbers and actions align to the end

  • Amounts, counts, and percentages are right-aligned with tabular numerals (class="num" on the header and the cells), so digits line up.
  • Row actions sit in the last column, aligned to the end (.cell--actions); screen actions sit at the end of the title row in the page header.
  • Text, identifiers, and dates are left-aligned. Never centre data.

One control height per row

Search, filter trigger, select, and button share the 44px height
<div class="filterrow filterrow--nowrap" style="margin:0">
  <input class="input" type="search" placeholder="Search" aria-label="Search">
  <button type="button" class="filtermenu__trigger"><svg class="icon icon--sm" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 5h16l-6.2 7.4V18l-3.6 2v-7.6Z"/></svg><span>Filters</span><svg class="icon icon--sm" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m6 9 6 6 6-6"/></svg></button>
  <span class="selectwrap"><select class="select" aria-label="Rows per page"><option>10 / page</option><option>20 / page</option></select></span>
  <button class="btn btn--secondary">Export</button>
</div>

Checklist

  • Every data table sits in .tablewrap; only free-text columns carry .cell--wrap.
  • Chips, badges, buttons, breadcrumb items, navigation labels, filter triggers, and footer text are single-line by construction in the console layer.
  • A value that must fit a fixed slot uses .truncate and keeps its full text available.
  • Toolbars keep one control height and one row; on narrow screens they move as whole groups or scroll, never breaking a label.
  • Check at 1440px and 375px. The layout audit (scripts/layout-audit.mjs) fails any page where short content wraps or the page scrolls sideways.