Visual Design Guidelines
This document is the authoritative reference for the kbpy web interface's visual design system. All templates should follow these rules. See also docs/guidelines/ux.md for terminology, personas, and information architecture.
Typography
Font Stack
| Role | Font | Weights | CSS Class | CDN |
|---|---|---|---|---|
| Headings | Montserrat | 500, 600, 700 | font-heading |
Google Fonts |
| Body | Inter | 400, 500, 600, 700 | font-sans (default) |
Google Fonts |
| Monospace | JetBrains Mono | 400, 500, 600 | font-mono |
Google Fonts |
Type Scale
| Element | Classes |
|---|---|
| Page title (h1) | font-heading text-2xl font-bold text-gray-900 |
| Section heading (h2) | text-lg font-semibold text-gray-900 |
| Subsection heading (h3) | font-medium text-gray-900 or font-semibold text-gray-900 |
| Body text | text-sm text-gray-600 |
| Description below header | text-sm text-gray-600 mt-2 max-w-2xl |
| Stats number (index/detail) | text-2xl font-heading font-bold |
| Stats number (dashboard only) | text-3xl font-heading font-bold |
| Metadata/labels | text-xs text-gray-500 |
| Sidebar section headers | text-[10px] font-semibold text-gray-400 uppercase tracking-wider |
When to Use Monospace
Use font-mono only for:
- Code identifiers inside definition blocks (
from_attribute,match_field) - Source file paths and line numbers
- Code snippets (
<pre><code>) - Entity IDs (like
SVC_001) when they are technical identifiers
Do not use font-mono for:
- Entity set names displayed to users (use
| readablefilter instead) - Relationship names, partition names, expectation names
Quoting External Names
When externally-provided names (entity sets, partitions, relationships, expectations, attributes, attestation sources) appear inline in prose or sentences, wrap them in typographic single quotes (\u2018\u2026\u2019):
- Do: Entity in \u2018all employees\u2019 where \u2018salary\u2019 is greater than 100000
- Don't: Entity in all employees where salary is greater than 100000
Use the | readable_quoted Jinja2 filter for prose contexts. Use the plain | readable filter for headings, standalone link labels, badges, and table cells where quotes are not needed.
Names generated by _readable_name() in theory.py are automatically quoted for use in description sentences.
Education Text
Education text paragraphs (the text-sm text-gray-600 descriptions below page headers) explain domain concepts to users. When highlighting kbpy-specific terms in education text:
- Do use
<strong>to emphasise domain-specific terms (e.g.<strong>expector</strong>,<strong>is_true</strong>,<strong>aligned</strong>) - Do not use semantic colors (
text-known-true,text-void, etc.) for terms in education text — colors are reserved for data values and status indicators, not prose - Do use
<em>for rhetorical questions or emphasis that isn't a term definition
Color System
Accent Color
| Token | Hex | Usage |
|---|---|---|
accent |
#06B6D4 |
Links, active sidebar items, interactive highlights |
accent-hover |
#0891B2 |
Hover state for accent-colored elements |
Semantic Data Colors
These represent the four possible states of a data value or evaluation result.
| Token | Hex | Tailwind | Meaning |
|---|---|---|---|
known-true |
#10B981 |
emerald-500 | True value, met expectation, aligned |
known-false |
#6B7280 |
gray-500 | False value (neutral, not an error) |
unknown |
#F59E0B |
amber-500 | Missing data, uncertain status |
void |
#EF4444 |
red-500 | Not applicable, not met, misaligned |
These are defined in both the Tailwind config (for JIT-compiled classes) and as explicit CSS rules (for classes that Tailwind CDN cannot generate dynamically). Both definitions are required.
Type Badge Colors
Each domain concept type has a consistent badge color:
| Concept | Color | Example |
|---|---|---|
| Entity Set | emerald | bg-emerald-100 text-emerald-800 |
| Partition | yellow | bg-yellow-100 text-yellow-800 |
| Expectation | pink | bg-pink-100 text-pink-800 |
| Relationship | teal | bg-teal-100 text-teal-800 |
| Entity Type | purple | bg-purple-100 text-purple-800 |
| Entity | blue | bg-blue-100 text-blue-800 |
| Attestation Source | cyan | bg-cyan-100 text-cyan-800 |
| Expector | indigo | bg-indigo-100 text-indigo-800 |
| Expectee | orange | bg-orange-100 text-orange-800 |
| Question | amber | bg-amber-100 text-amber-800 |
These are defined in partials/badge.html via the type_badge macro. Always use the macro rather than hardcoding badge colors in templates.
Status Badge Colors
| Status | Background | Text |
|---|---|---|
| Met | bg-green-100 |
text-green-800 |
| Not met | bg-red-100 |
text-red-800 |
| Uncertain | bg-amber-100 |
text-amber-800 |
Gray Scale Usage
Limit gray usage to these tones for consistent hierarchy:
| Gray | Role |
|---|---|
gray-50 |
Subtle backgrounds (table headers, sidebar) |
gray-100 |
Hover backgrounds, code backgrounds |
gray-200 |
Borders, dividers |
gray-400 |
Sidebar section headers, separators, secondary counts |
gray-500 |
Metadata text, labels |
gray-600 |
Body text, descriptions |
gray-700 |
Secondary headings, emphasized body |
gray-900 |
Primary headings, strong text |
Layout
Content Width
| Context | Class | When |
|---|---|---|
| Standard module pages | max-w-4xl |
Most pages |
| Pages with tables or wide content | max-w-5xl |
Entity sets index, campaign dashboard, actions index |
| Graph or source code viewer | max-w-6xl |
Domain model, source file viewer |
| Standalone pages (no sidebar) | max-w-4xl mx-auto |
Domain overview |
| Dashboard landing | max-w-3xl mx-auto |
Home page |
Sidebar Pages
Content in sidebar pages (extending module_base.html) must not use mx-auto. The content left-aligns against the sidebar edge. Centering creates misalignment with the sidebar navigation.
Spacing
| Element | Spacing |
|---|---|
| Page header to content | mb-6 |
| Between major sections | mb-8 |
| Description text max width | max-w-2xl |
| Section heading to content | mb-4 |
| Card internal padding | p-4 (standard), p-5 (dashboard stats), p-6 (wide cards) |
Components
Cards
Standard card: bg-white border border-gray-200 rounded-lg p-4
Interactive card (clickable): Add hover:border-accent hover:shadow-sm transition-all cursor-pointer
Status-bordered card: Add border-l-4 border-l-{color}-500 for left accent stripe, or border-2 border-{color}-500 for full status border.
Stats Cards
Index pages (Pattern B): bg-white border border-gray-200 rounded-lg p-4 text-center
- Number:
text-2xl font-heading font-bold text-{color} - Label:
text-sm text-gray-500
Detail pages with semantic meaning (Pattern C): Add border-l-4 border-l-{color}-500
Dashboard navigational stats (Pattern A): bg-gray-50 rounded-lg p-5 hover:bg-gray-100 transition-colors
- Number:
text-3xl font-heading font-bold text-gray-900
Tables
Container: bg-white border border-gray-200 rounded-lg overflow-hidden
Header: bg-gray-50
Header cell: px-4 py-3 text-left text-xs font-semibold text-gray-500 uppercase tracking-wider
Body: divide-y divide-gray-200
Row: hover:bg-gray-50 (add cursor-pointer if clickable)
Cell: px-4 py-3 text-sm
Always include tracking-wider on table header cells.
Badges
Use the macros from partials/badge.html:
type_badge(type)— for domain concept type labelsstatus_badge(status)— for met/not_met/uncertainexhaustive_badge(is_exhaustive)— for exhaustive/partialvalue_badge(status, display, reason)— for data values
Buttons
Standard button: inline-flex items-center px-4 py-2 text-sm text-gray-700 bg-white border border-gray-200 rounded-lg hover:bg-gray-50 transition-colors
Compact button (toolbars): px-3 py-2 instead of px-4 py-2
Small button (source view): px-2.5 py-1 text-xs text-gray-600 bg-gray-100 rounded hover:bg-gray-200 hover:text-gray-800
Primary CTA (rare): px-4 py-2 text-sm text-white bg-accent rounded-lg hover:bg-accent-hover
All buttons must include transition-colors.
Back Links
<div class="mt-8">
<a href="..." class="inline-flex items-center px-4 py-2 text-sm text-gray-700 bg-white border border-gray-200 rounded-lg hover:bg-gray-50 transition-colors">
← Back to ...
</a>
</div>
Empty States
Use the empty_state macro from partials/empty_state.html:
bg-gray-50 border border-gray-200 rounded-lg p-8 text-center
Page Headers
Standard pattern for all module pages:
<div class="mb-6">
<div class="flex items-center space-x-3 mb-2">
{{ type_badge("concept_type") }}
<h1 class="font-heading text-2xl font-bold text-gray-900">Title</h1>
</div>
<p class="text-sm text-gray-500">Metadata</p>
<p class="text-sm text-gray-600 mt-2 max-w-2xl">Description paragraph.</p>
</div>
Interactivity
Hover States
All interactive elements must have visible hover feedback:
- Links:
hover:underlineand/orhover:text-accent - Cards:
hover:border-accent hover:shadow-sm transition-all - Buttons:
hover:bg-gray-50 transition-colorsorhover:bg-gray-100 transition-colors - Table rows:
hover:bg-gray-50
Transitions
All elements that change appearance on hover must include a transition:
- Color changes:
transition-colors - Multiple property changes (border + shadow):
transition-all
Focus States
All interactive elements must be keyboard-accessible with visible focus indicators:
a:focus-visible, button:focus-visible, [onclick]:focus-visible, select:focus-visible {
outline: 2px solid #06B6D4;
outline-offset: 2px;
}
Elements with onclick handlers must have tabindex="0" to be keyboard-reachable, and must respond to Enter/Space keypress.
Accessibility
Emoji
Emoji used as decorative icons must be wrapped in <span aria-hidden="true"> to prevent screen readers from announcing them:
<span aria-hidden="true" class="mr-2">📋</span> Expectations
Status Communication
Status should be communicated through multiple channels (not color alone):
- Color + text label (badges show "met", "not met", "uncertain")
- Color + icon (value badges show ✓, ✗, ❓, ⚠️ alongside color)
- Reason chains provide textual explanation
Semantic HTML
- Use
<nav>for navigation regions - Use
<section>for distinct content sections - Use
<table>with proper<thead>,<th scope="col">for tabular data - Use heading hierarchy correctly (h1 → h2 → h3, never skip levels)
Border Radius
| Element | Radius |
|---|---|
| Cards, containers, tables | rounded-lg (8px) |
| Badges | rounded (4px) |
| Dropdowns | rounded-md (6px) |
| Progress bars | rounded-full |
| Buttons | rounded-lg (8px) |
Shadows
Shadows are used sparingly for interactive feedback only:
hover:shadow-smon interactive cardshover:shadow-mdon domain overview campaign cards- No shadows on static elements
WRIT Docs