Files
bible/rules/patterns/web-visual-baseline/contract.md

7.5 KiB

Contract: Web Visual Baseline

Version: 1.0

Scope

Defines the default visual baseline for future web applications in this ecosystem.

The canonical reference is the UI style from:

  • /Users/mchusavitin/Documents/git/chart/web/static/view.css
  • /Users/mchusavitin/Documents/git/chart/web/templates/view.html
  • /Users/mchusavitin/Documents/git/chart/web/templates/upload.html

When a project does not already have an established design system, use this baseline by default.

Core Direction

  • Prefer a clean, data-first interface over decorative marketing UI.
  • Default to server-rendered HTML with simple CSS.
  • Optimize for scanability, density, and operational clarity.
  • Use restrained visual hierarchy, not novelty effects.
  • Reuse the baseline directly when possible; copying the canonical CSS and adapting tokens is allowed.

Canonical Visual Language

  • Dark application header on top.
  • White page background and white content surfaces.
  • Light secondary surfaces for headers and table heads.
  • Thin gray borders with a subtle shadow.
  • Small radii (4px).
  • Dense but readable typography (14px/1.5 baseline).
  • Blue accent in the #2185d0 family for primary actions and active accents.
  • Tables and key-value layouts as the primary presentation pattern.
  • Status communicated with both text and color.

Typography

  • Use Lato, "Helvetica Neue", Arial, Helvetica, sans-serif unless a project has an approved alternative.
  • Page titles are compact and strong, not oversized hero typography.
  • Section titles should be clear and structural.
  • Avoid display fonts, novelty fonts, and oversized marketing headings in application UI.

Layout Primitives

  • page-header: dark global header with page title and compact actions.
  • page-main: centered content area with generous outer margin and bounded max width.
  • panel: white surface with border, light shadow, and simple heading strip.
  • section-card: heading followed by table/content block.
  • table-wrap: horizontal overflow container for dense data tables.

Preferred Components

  • Key-value tables for singleton object/detail views.
  • Dense data tables for repeated records.
  • Compact upload/open panels when local file input is needed.
  • Quiet header actions for secondary navigation.
  • Clear primary buttons for the main action on a screen.
  • Simple alert/error boxes with border + tinted background.

Status Rules

  • OK: green
  • Warning: amber
  • Critical: red
  • Unknown: gray
  • Empty: light gray

Status must not rely on color alone. Show text or another explicit indicator together with the color treatment.

Responsive Rules

  • Keep desktop density high.
  • Collapse grids to one column on small screens.
  • Preserve table readability with horizontal scrolling instead of destructive cardification by default.
  • Header actions may wrap or stack on mobile, but should remain compact.

Forbidden Drift

  • Do not default to glassmorphism, blurred shells, floating neon gradients, or soft-dribbble styling.
  • Do not replace dense tables with oversized card grids when the data is inherently tabular.
  • Do not introduce arbitrary color coding for non-status fields.
  • Do not use oversized border radii, heavy shadows, or large empty spacing as the default application style.
  • Do not import a SPA/dashboard aesthetic unless the product explicitly requires it.

Relationship To Other UI Contracts

  • Use this contract as the visual baseline.
  • Use table-management for shared table geometry and interaction seams.
  • Use controls-selection for button hierarchy, filters, and bulk selection semantics.
  • Pattern-specific contracts may override details only when they document the reason.

Copyable Starter CSS

Use this as the default starting point for new web apps:

:root {
  --bg: #ffffff;
  --surface: #ffffff;
  --surface-2: #f9fafb;
  --border: rgba(34, 36, 38, 0.15);
  --border-lite: rgba(34, 36, 38, 0.1);
  --ink: rgba(0, 0, 0, 0.87);
  --muted: rgba(0, 0, 0, 0.6);
  --accent: #2185d0;
  --accent-dark: #1678c2;
  --accent-bg: #dff0ff;
  --ok: #16ab39;
  --warn: #f2711c;
  --crit: #db2828;
  --header-bg: #1b1c1d;
  --radius: 4px;
  --shadow: 0 1px 2px 0 rgba(34, 36, 38, 0.15);
  --content-width: 1500px;
}

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font: 14px/1.5 Lato, "Helvetica Neue", Arial, Helvetica, sans-serif;
}

.page-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 14px 24px;
  background: var(--header-bg);
}

.page-header h1 {
  margin: 0;
  font-size: 18px;
  font-weight: 700;
  color: rgba(255, 255, 255, 0.9);
}

.page-main {
  width: min(var(--content-width), calc(100vw - 48px));
  margin: 28px auto 56px;
}

.panel {
  margin-bottom: 28px;
  overflow: hidden;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
}

.panel > h2 {
  margin: 0;
  padding: 13px 16px;
  background: var(--surface-2);
  border-bottom: 1px solid var(--border);
  font-size: 13px;
  font-weight: 700;
}

.table-wrap {
  overflow-x: auto;
}

.kv-table,
.data-table {
  width: 100%;
  border-collapse: collapse;
  background: var(--surface);
}

.kv-table th,
.kv-table td,
.data-table th,
.data-table td {
  padding: 11px 14px;
  text-align: left;
  vertical-align: top;
  border-top: 1px solid var(--border-lite);
}

.kv-table th,
.data-table th {
  background: var(--surface-2);
  border-top: 0;
  border-bottom: 1px solid var(--border-lite);
  font-weight: 700;
  white-space: nowrap;
}

.data-table tbody tr:hover {
  background: rgba(0, 0, 0, 0.04);
}

.button-primary {
  display: inline-block;
  padding: 8px 18px;
  border: none;
  border-radius: var(--radius);
  background: var(--accent);
  color: #fff;
  font: inherit;
  font-weight: 700;
  text-decoration: none;
  cursor: pointer;
}

.button-primary:hover {
  background: var(--accent-dark);
}

.header-action {
  display: inline-block;
  padding: 6px 14px;
  border-radius: var(--radius);
  background: rgba(255, 255, 255, 0.12);
  color: rgba(255, 255, 255, 0.85);
  text-decoration: none;
  font-size: 13px;
  font-weight: 700;
}

.header-action:hover {
  background: rgba(255, 255, 255, 0.2);
}

.status-ok { color: var(--ok); }
.status-warning { color: var(--warn); }
.status-critical { color: var(--crit); }
.status-unknown { color: rgba(0, 0, 0, 0.45); }

@media (max-width: 720px) {
  .page-header {
    flex-direction: column;
    padding: 12px 16px;
  }

  .page-main {
    width: calc(100vw - 24px);
    margin-top: 20px;
  }
}

Copyable Starter HTML

<header class="page-header">
  <h1>Application Title</h1>
  <a class="header-action" href="/back">Back</a>
</header>

<main class="page-main">
  <section class="panel">
    <h2>Overview</h2>
    <div class="table-wrap">
      <table class="kv-table">
        <tbody>
          <tr>
            <th>Host</th>
            <td>server-01</td>
          </tr>
          <tr>
            <th>Status</th>
            <td><span class="status-ok">OK</span></td>
          </tr>
        </tbody>
      </table>
    </div>
  </section>

  <section class="panel">
    <h2>Devices</h2>
    <div class="table-wrap">
      <table class="data-table">
        <thead>
          <tr>
            <th>Name</th>
            <th>Vendor</th>
            <th>Status</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td>NIC 1</td>
            <td>Intel</td>
            <td><span class="status-warning">Warning</span></td>
          </tr>
        </tbody>
      </table>
    </div>
  </section>
</main>