Unclassified
UI Design Guide — IntegrateWise Platform
Design system, components, and patterns for building workbenches on top of IntegrateWise.
Design Language
Forest · Paper · Combo
| Token | Value | Usage |
|---|---|---|
| Canvas | #1A1A1A | Sidebar, nav foundation (dark earthy) |
| Paper | #F7F6F2 | Main content (warm white) |
| Accent | #5B814F | Forest green — highlights, active states |
| Gold | #B8943F | Insight, premium, wayfinding |
Typography
| Font | Usage |
|---|---|
| Bebas Neue | Display / hero headlines ONLY |
| DM Serif Display | h1–h3 (serif headings) |
| Instrument Sans | h4–h6, body, UI (default sans) |
| IBM Plex Mono | Code, technical labels, data |
Design Tokens
:root {
/* Surfaces */
--iw-surface-canvas: #f4f0e8;
--iw-surface-default: #ebe5d8;
--iw-surface-subtle: #e0d9c8;
--iw-surface-raised: #f9f6ef;
--iw-surface-inverse: #0c0c0c;
/* Text */
--iw-text-strong: #0c0c0c;
--iw-text-default: #0c0c0c;
--iw-text-muted: #5a5550;
--iw-text-faint: #8a8580;
--iw-text-inverse: #f4f0e8;
--iw-text-accent: #2d5a3d;
--iw-text-highlight: #b8943f;
/* Borders */
--iw-border-subtle: #d8d0c0;
--iw-border-default: #c4baa8;
--iw-border-strong: #a89a88;
--iw-border-accent: #b8943f;
/* Status */
--iw-status-success: #3d7a50;
--iw-status-warning: #b8943f;
--iw-status-danger: #8b2020;
--iw-status-info: #3d5247;
}
Layout Structure
App Shell
┌─────────────────────────────────────────────────────────────┐
│ Sidebar (240px) │ Main Content │
├───────────────────┼─────────────────────────────────────────┤
│ Logo │ Header (breadcrumb, actions) │
│ Navigation │ ┌─────────────────────────────────┐ │
│ - Dashboard │ │ │ │
│ - Entities │ │ Page Content │ │
│ - Integrations │ │ │ │
│ - Capabilities │ │ │ │
│ - Settings │ └─────────────────────────────────┘ │
│ │ Footer (optional) │
└───────────────────┴─────────────────────────────────────────┘
Sidebar
<Sidebar>
<Logo />
<Navigation>
<NavItem icon="dashboard" label="Dashboard" href="/app/work/dashboard" />
<NavItem icon="entities" label="Entities" href="/app/work/entities" />
<NavItem icon="integrations" label="Integrations" href="/app/work/integrations" />
<NavItem icon="capabilities" label="Capabilities" href="/app/work/capabilities" />
<NavItem icon="settings" label="Settings" href="/app/work/settings" />
</Navigation>
<UserProfile />
</Sidebar>
Core Components
1. Entity Card
Displays a single entity (account, contact, deal, etc.).
<EntityCard
entity={{
id: "acc_001",
name: "Acme Corp",
entity_type: "account",
status: "active",
metadata: { industry: "Technology", ARR: 120000 },
}}
onClick={() => navigate(`/app/work/entities/acc_001`)}
/>
Props:
interface EntityCardProps {
entity: SpineEntity;
onClick?: () => void;
selected?: boolean;
showActions?: boolean;
}
2. Entity List
Displays a list of entities with filtering and sorting.
<EntityList
entities={workbench.entities.deal}
filters={[
{ key: "status", label: "Status", options: ["open", "won", "lost"] },
{ key: "source_tool", label: "Source", options: ["salesforce", "hubspot"] },
]}
sortBy="updated_at"
sortOrder="desc"
onEntityClick={(entity) => navigate(`/app/work/entities/${entity.id}`)}
/>
3. Connector Card
Displays a connector with status and connect button.
<ConnectorCard
connector={{
id: "salesforce",
name: "Salesforce",
category: "crm",
status: "available",
capabilities: { auth: true, webhook: true },
}}
onConnect={() => connectConnector("salesforce")}
/>
4. Capability Card
Displays a capability with execute button.
<CapabilityCard
capability={{
name: "salesforce:read-accounts",
description: "Read accounts from Salesforce",
min_tier: "free",
channel: "sync",
}}
onExecute={(params) => executeCapability("salesforce:read-accounts", params)}
/>
5. Signal Card
Displays a signal (insight, alert, notification).
<SignalCard
signal={{
id: "sig_001",
severity: "warning",
title: "Deal at risk",
description: "Acme Corp deal hasn't been updated in 14 days",
timestamp: "2026-07-21T10:30:00Z",
}}
onDismiss={() => dismissSignal("sig_001")}
/>
6. Readiness Meter
Displays department readiness score.
<ReadinessMeter
readiness={{
overall_score: 0.85,
overall_state: "live",
buckets: [
{ capability: "accounts", state: "live", score: 0.9 },
{ capability: "deals", state: "live", score: 0.85 },
],
}}
/>
7. Timeline
Displays activity timeline.
<Timeline
events={[
{ id: "evt_001", type: "created", title: "Deal created", timestamp: "2026-07-21T10:00:00Z" },
{ id: "evt_002", type: "updated", title: "Stage changed", timestamp: "2026-07-21T11:00:00Z" },
]}
/>
Page Templates
Dashboard Page
function DashboardPage() {
const { workbench, loading } = useWorkbench(token, tenantId, "SALES");
if (loading) return <Spinner />;
return (
<Page>
<Header>
<Title>Sales Dashboard</Title>
<Actions>
<Button onClick={refresh}>Refresh</Button>
</Actions>
</Header>
<Metrics>
<MetricCard label="Accounts" value={workbench.entities.account.length} />
<MetricCard label="Deals" value={workbench.entities.deal.length} />
<MetricCard label="People" value={workbench.entities.person.length} />
<MetricCard label="Signals" value={workbench.signals.length} />
</Metrics>
<ReadinessMeter readiness={workbench.readiness} />
<Grid>
<Section title="Recent Deals">
<EntityList entities={workbench.entities.deal.slice(0, 5)} />
</Section>
<Section title="Signals">
<SignalList signals={workbench.signals.slice(0, 5)} />
</Section>
</Grid>
</Page>
);
}
Entities Page
function EntitiesPage() {
const { workbench, loading } = useWorkbench(token, tenantId, "SALES");
const [entityType, setEntityType] = useState("deal");
return (
<Page>
<Header>
<Title>Entities</Title>
</Header>
<EntityList
entities={workbench.entities[entityType] || []}
filters={[...]}
sortBy="updated_at"
onEntityClick={(entity) => navigate(`/app/work/entities/${entity.id}`)}
/>
</Page>
);
}
Integrations Page
function IntegrationsPage() {
const { connectors, loading, connect } = useConnectors(token, tenantId);
const { integrations } = useIntegrations(token, tenantId);
return (
<Page>
<Header>
<Title>Integrations</Title>
</Header>
<Section title="Connected">
{integrations.map((integration) => (
<ConnectorCard
key={integration.provider}
connector={integration}
status="connected"
onDisconnect={() => disconnect(integration.provider)}
/>
))}
</Section>
<Section title="Available">
{connectors
.filter((c) => c.status === "available")
.map((connector) => (
<ConnectorCard
key={connector.id}
connector={connector}
onConnect={() => connect(connector.id)}
/>
))}
</Section>
</Page>
);
}
Capabilities Page
function CapabilitiesPage() {
const { capabilities, loading, execute } = useCapabilities(token, tenantId);
return (
<Page>
<Header>
<Title>Capabilities</Title>
</Header>
<Grid>
{capabilities.map((capability) => (
<CapabilityCard
key={capability.name}
capability={capability}
onExecute={(params) => execute(capability.name, params)}
/>
))}
</Grid>
</Page>
);
}
Entity Detail Page
function EntityDetailPage({ entityId }) {
const { workbench } = useWorkbench(token, tenantId, "SALES");
const entity = findEntity(workbench, entityId);
return (
<Page>
<Header>
<BackButton onClick={() => navigate(-1)} />
<Title>{entity.name}</Title>
<Actions>
<Button onClick={() => editEntity(entity)}>Edit</Button>
<Button onClick={() => deleteEntity(entity)} variant="danger">
Delete
</Button>
</Actions>
</Header>
<Grid>
<Section title="Details">
<EntityDetails entity={entity} />
</Section>
<Section title="Relationships">
<RelationshipList
relationships={workbench.relationships.filter(
(r) => r.source === entity.id || r.target === entity.id
)}
/>
</Section>
<Section title="Timeline">
<Timeline events={workbench.timeline.filter((e) => e.entity_id === entity.id)} />
</Section>
<Section title="Signals">
<SignalList signals={workbench.signals.filter((s) => s.entity_id === entity.id)} />
</Section>
</Grid>
</Page>
);
}
Responsive Design
Breakpoints
/* Mobile */
@media (max-width: 640px) {
.sidebar {
display: none;
}
.main-content {
padding: 16px;
}
}
/* Tablet */
@media (min-width: 641px) and (max-width: 1024px) {
.sidebar {
width: 64px;
}
.sidebar-label {
display: none;
}
}
/* Desktop */
@media (min-width: 1025px) {
.sidebar {
width: 240px;
}
.main-content {
padding: 24px;
}
}
Dark Mode
@media (prefers-color-scheme: dark) {
:root {
--iw-surface-canvas: #1a1a1a;
--iw-surface-default: #2a2a2a;
--iw-surface-subtle: #3a3a3a;
--iw-surface-raised: #4a4a4a;
--iw-surface-inverse: #f4f0e8;
--iw-text-strong: #f4f0e8;
--iw-text-default: #e0e0e0;
--iw-text-muted: #a0a0a0;
--iw-text-faint: #707070;
--iw-text-inverse: #1a1a1a;
--iw-border-subtle: #3a3a3a;
--iw-border-default: #4a4a4a;
--iw-border-strong: #5a5a5a;
}
}
Accessibility
Color Contrast
| Token | Light Mode | Dark Mode | Ratio |
|---|---|---|---|
| Text on Paper | #0c0c0c on #f4f0e8 | #f4f0e8 on #1a1a1a | 15.4:1 |
| Text on Canvas | #f4f0e8 on #1a1a1a | #0c0c0c on #f4f0e8 | 15.4:1 |
| Accent on Paper | #2d5a3d on #f4f0e8 | #3d7a50 on #1a1a1a | 7.2:1 |
Keyboard Navigation
- All interactive elements must be focusable
- Focus order follows visual layout
- Skip links for main content
ARIA Labels
<button aria-label="Connect Salesforce" onClick={connect}>
Connect
</button>
<nav aria-label="Main navigation">
<a href="/dashboard" aria-current="page">Dashboard</a>
</nav>
Component Library
Available Components
| Component | Location | Description |
|---|---|---|
Button | @/components/common/button | Primary, secondary, danger variants |
Card | @/components/common/card | Container with header, body, footer |
Input | @/components/common/input | Text, number, search inputs |
Select | @/components/common/select | Dropdown selection |
Modal | @/components/common/modal | Dialog overlay |
Toast | @/components/common/toast | Notification messages |
Spinner | @/components/common/spinner | Loading indicator |
Tabs | @/components/common/tabs | Tab navigation |
Badge | @/components/common/badge | Status indicators |
Avatar | @/components/common/avatar | User/entity avatars |
Import Pattern
Resources
- Design Tokens:
apps/web/src/styles/design-tokens.css - Components:
apps/web/src/components/ - API Reference:
docs/api/PLATFORM_API.md - Service Topology:
docs/api/SERVICE_TOPOLOGY.md