π One-Screen Board Dashboard¶
Design Philosophy¶
Board directors have 2-3 minutes to:
- Understand current state
- Identify what needs attention
- Make decisions
- Delegate actions
The one-screen dashboard delivers this without scrolling, clicking, or searching.
Information Hierarchy¶
Level 1: Overall Health (5 seconds)¶
Single metric or visualization showing overall compliance/security posture
Level 2: Key Metrics (15 seconds)¶
3-5 critical metrics that matter to the board
Level 3: Decisions Required (30 seconds)¶
Actions needing board input, clearly prioritized
Level 4: Context & Trends (remaining time)¶
Supporting information for decision-making
Dashboard Layout¶
<div class="gc-board-dashboard">
<!-- Fixed Header -->
<header class="gc-board-header">
<div class="gc-board-header__brand">
<h1>GetCimple Board View</h1>
</div>
<div class="gc-board-header__period">
<select aria-label="Reporting period">
<option>Q4 2024</option>
<option selected>Q1 2025</option>
</select>
</div>
<div class="gc-board-header__actions">
<button class="gc-board-header__export">Export Report</button>
<button class="gc-board-header__settings">Settings</button>
</div>
</header>
<!-- Main Dashboard Grid -->
<main class="gc-board-main">
<!-- Hero Metric -->
<section class="gc-board-hero" aria-label="Overall compliance status">
<div class="gc-board-hero__content">
<div class="gc-board-hero__metric">
<span class="gc-board-hero__value">85%</span>
<span class="gc-board-hero__label">Overall Compliance</span>
</div>
<div class="gc-board-hero__trend">
<svg class="gc-board-hero__arrow gc-board-hero__arrow--up">
<use href="#icon-trend-up"></use>
</svg>
<span>+5% from last quarter</span>
</div>
<div class="gc-board-hero__benchmark">Industry Average: 78%</div>
</div>
<div class="gc-board-hero__visual">
<!-- Circular progress or gauge -->
<svg class="gc-board-hero__gauge" viewBox="0 0 200 200">
<!-- Gauge visualization -->
</svg>
</div>
</section>
<!-- Critical Metrics Row -->
<section class="gc-board-metrics" aria-label="Key metrics">
<article class="gc-board-metric">
<h3 class="gc-board-metric__title">Frameworks</h3>
<div class="gc-board-metric__value">
<span class="gc-board-metric__current">3</span>
<span class="gc-board-metric__total">/ 5</span>
</div>
<div class="gc-board-metric__status gc-board-metric__status--warning">
At Target
</div>
</article>
<article class="gc-board-metric">
<h3 class="gc-board-metric__title">Risk Score</h3>
<div class="gc-board-metric__value">
<span class="gc-board-metric__score">Medium</span>
</div>
<div class="gc-board-metric__status gc-board-metric__status--stable">
Stable
</div>
</article>
<article class="gc-board-metric">
<h3 class="gc-board-metric__title">Actions</h3>
<div class="gc-board-metric__value">
<span class="gc-board-metric__urgent">2</span>
</div>
<div class="gc-board-metric__status gc-board-metric__status--attention">
Required
</div>
</article>
<article class="gc-board-metric">
<h3 class="gc-board-metric__title">Investment</h3>
<div class="gc-board-metric__value">
<span class="gc-board-metric__amount">$125k</span>
</div>
<div class="gc-board-metric__status gc-board-metric__status--info">
YTD Spend
</div>
</article>
</section>
<!-- Decisions Section -->
<section class="gc-board-decisions" aria-label="Decisions required">
<h2 class="gc-board-decisions__title">Board Decisions Required</h2>
<div class="gc-board-decision gc-board-decision--urgent">
<div class="gc-board-decision__content">
<h3 class="gc-board-decision__title">
Approve Essential Eight Target
</h3>
<p class="gc-board-decision__description">
Set target maturity level for next 12 months
</p>
<div class="gc-board-decision__meta">
<span class="gc-board-decision__time">3 min</span>
<span class="gc-board-decision__impact">High Impact</span>
</div>
</div>
<div class="gc-board-decision__actions">
<button
class="gc-board-decision__action gc-board-decision__action--primary"
>
Review & Decide
</button>
</div>
</div>
<div class="gc-board-decision">
<div class="gc-board-decision__content">
<h3 class="gc-board-decision__title">Cyber Insurance Renewal</h3>
<p class="gc-board-decision__description">
Review and approve renewal terms
</p>
<div class="gc-board-decision__meta">
<span class="gc-board-decision__time">5 min</span>
<span class="gc-board-decision__deadline">Due: March 15</span>
</div>
</div>
<div class="gc-board-decision__actions">
<button class="gc-board-decision__action">Review</button>
<button
class="gc-board-decision__action gc-board-decision__action--defer"
>
Defer
</button>
</div>
</div>
</section>
<!-- Framework Status Grid -->
<section class="gc-board-frameworks" aria-label="Framework compliance">
<h2 class="gc-board-frameworks__title">Compliance Frameworks</h2>
<div class="gc-board-frameworks__grid">
<article class="gc-board-framework">
<header class="gc-board-framework__header">
<h3 class="gc-board-framework__name">Essential Eight</h3>
<span
class="gc-board-framework__badge gc-board-framework__badge--warning"
>
ML1
</span>
</header>
<div class="gc-board-framework__progress">
<div class="gc-board-framework__bar">
<div class="gc-board-framework__fill" style="width: 40%"></div>
</div>
<div class="gc-board-framework__labels">
<span>Current: 40%</span>
<span>Target: ML2 (70%)</span>
</div>
</div>
</article>
<!-- More framework cards -->
</div>
</section>
<!-- Trend Chart -->
<section class="gc-board-trends" aria-label="Compliance trends">
<h2 class="gc-board-trends__title">12-Month Trend</h2>
<div class="gc-board-trends__chart">
<!-- Simplified line chart -->
<canvas id="trend-chart" aria-label="Compliance trend chart"></canvas>
</div>
<div class="gc-board-trends__legend">
<span class="gc-board-trends__legend-item">
<span
class="gc-board-trends__legend-color"
style="background: var(--gc-blue-500)"
></span>
Actual
</span>
<span class="gc-board-trends__legend-item">
<span
class="gc-board-trends__legend-color"
style="background: var(--gc-gray-400)"
></span>
Target
</span>
</div>
</section>
</main>
</div>
Responsive Grid Layout¶
.gc-board-dashboard {
height: 100vh;
display: flex;
flex-direction: column;
background: var(--gc-bg-secondary);
}
.gc-board-header {
height: 64px;
background: var(--gc-bg-primary);
border-bottom: 1px solid var(--gc-border-default);
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 24px;
flex-shrink: 0;
}
.gc-board-main {
flex: 1;
padding: 24px;
overflow-y: auto;
display: grid;
grid-template-columns: repeat(12, 1fr);
grid-template-rows: auto auto auto 1fr;
gap: 24px;
max-width: 1440px;
margin: 0 auto;
width: 100%;
}
/* Hero Section - Full Width */
.gc-board-hero {
grid-column: 1 / -1;
background: var(--gc-bg-primary);
border-radius: 16px;
padding: 32px;
display: flex;
justify-content: space-between;
align-items: center;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
}
.gc-board-hero__metric {
display: flex;
flex-direction: column;
gap: 8px;
}
.gc-board-hero__value {
font-size: 48px;
font-weight: 700;
color: var(--gc-text-primary);
line-height: 1;
}
.gc-board-hero__label {
font-size: 18px;
color: var(--gc-text-secondary);
}
/* Metrics Row */
.gc-board-metrics {
grid-column: 1 / -1;
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 16px;
}
.gc-board-metric {
background: var(--gc-bg-primary);
border-radius: 12px;
padding: 20px;
display: flex;
flex-direction: column;
gap: 12px;
}
.gc-board-metric__title {
font-size: 14px;
font-weight: 500;
color: var(--gc-text-secondary);
text-transform: uppercase;
letter-spacing: 0.05em;
}
.gc-board-metric__value {
font-size: 32px;
font-weight: 600;
color: var(--gc-text-primary);
}
.gc-board-metric__status {
font-size: 14px;
font-weight: 500;
padding: 4px 8px;
border-radius: 6px;
align-self: flex-start;
}
.gc-board-metric__status--warning {
background: var(--gc-amber-100);
color: var(--gc-amber-700);
}
.gc-board-metric__status--attention {
background: var(--gc-red-100);
color: var(--gc-red-700);
}
/* Decisions Section */
.gc-board-decisions {
grid-column: 1 / 7;
background: var(--gc-bg-primary);
border-radius: 12px;
padding: 24px;
}
.gc-board-decision {
display: flex;
justify-content: space-between;
align-items: center;
padding: 16px;
margin-bottom: 12px;
border: 2px solid var(--gc-border-default);
border-radius: 8px;
transition: all 0.2s ease;
}
.gc-board-decision--urgent {
border-color: var(--gc-red-300);
background: var(--gc-red-50);
}
.gc-board-decision:hover {
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}
/* Frameworks Grid */
.gc-board-frameworks {
grid-column: 7 / -1;
background: var(--gc-bg-primary);
border-radius: 12px;
padding: 24px;
}
.gc-board-frameworks__grid {
display: grid;
gap: 16px;
}
.gc-board-framework {
padding: 16px;
border: 1px solid var(--gc-border-light);
border-radius: 8px;
}
/* Trends Section */
.gc-board-trends {
grid-column: 1 / -1;
background: var(--gc-bg-primary);
border-radius: 12px;
padding: 24px;
min-height: 300px;
}
Interactive Features¶
Quick Actions¶
class BoardDashboard {
constructor() {
this.initQuickActions()
this.initAutoRefresh()
this.initKeyboardShortcuts()
}
initQuickActions() {
// One-click decision actions
document.querySelectorAll('.gc-board-decision__action').forEach((btn) => {
btn.addEventListener('click', (e) => {
const decision = e.target.closest('.gc-board-decision')
const type = e.target.dataset.action || 'review'
if (type === 'defer') {
this.deferDecision(decision)
} else {
this.openDecisionModal(decision)
}
})
})
}
openDecisionModal(decision) {
// Quick decision interface
const modal = new DecisionModal({
title: decision.querySelector('.gc-board-decision__title').textContent,
timeEstimate: decision.querySelector('.gc-board-decision__time')
.textContent,
onComplete: (result) => {
this.submitDecision(decision, result)
this.updateDashboard()
},
})
modal.open()
}
deferDecision(decision) {
// Quick defer with reason
const reason = prompt('Reason for deferral (optional):')
fetch('/api/decisions/defer', {
method: 'POST',
body: JSON.stringify({
decisionId: decision.dataset.id,
reason,
deferUntil: this.getNextQuarter(),
}),
}).then(() => {
decision.classList.add('gc-board-decision--deferred')
this.showNotification('Decision deferred to next quarter')
})
}
initAutoRefresh() {
// Refresh data every 5 minutes
setInterval(
() => {
this.refreshMetrics()
},
5 * 60 * 1000
)
// Visual countdown to next refresh
this.showRefreshTimer()
}
initKeyboardShortcuts() {
document.addEventListener('keydown', (e) => {
// Quick navigation
if (e.key === '1' && e.ctrlKey) {
this.focusSection('decisions')
} else if (e.key === '2' && e.ctrlKey) {
this.focusSection('frameworks')
} else if (e.key === 'r' && e.ctrlKey) {
e.preventDefault()
this.refreshMetrics()
} else if (e.key === 'e' && e.ctrlKey) {
e.preventDefault()
this.exportReport()
}
})
}
}
Simplified Charts¶
class SimplifiedChart {
constructor(canvas, data) {
this.ctx = canvas.getContext('2d')
this.data = data
this.render()
}
render() {
// Simple line chart for trends
const width = this.ctx.canvas.width
const height = this.ctx.canvas.height
const padding = 40
// Clear canvas
this.ctx.clearRect(0, 0, width, height)
// Draw axes
this.ctx.strokeStyle = '#E5E7EB'
this.ctx.lineWidth = 1
this.ctx.beginPath()
this.ctx.moveTo(padding, padding)
this.ctx.lineTo(padding, height - padding)
this.ctx.lineTo(width - padding, height - padding)
this.ctx.stroke()
// Draw data line
this.ctx.strokeStyle = '#3B82F6'
this.ctx.lineWidth = 2
this.ctx.beginPath()
this.data.forEach((point, i) => {
const x = padding + (i / (this.data.length - 1)) * (width - 2 * padding)
const y = height - padding - (point.value / 100) * (height - 2 * padding)
if (i === 0) {
this.ctx.moveTo(x, y)
} else {
this.ctx.lineTo(x, y)
}
// Draw point
this.ctx.fillStyle = '#3B82F6'
this.ctx.beginPath()
this.ctx.arc(x, y, 4, 0, Math.PI * 2)
this.ctx.fill()
})
this.ctx.stroke()
// Draw target line
this.ctx.strokeStyle = '#9CA3AF'
this.ctx.lineWidth = 1
this.ctx.setLineDash([5, 5])
this.ctx.beginPath()
const targetY = height - padding - (85 / 100) * (height - 2 * padding)
this.ctx.moveTo(padding, targetY)
this.ctx.lineTo(width - padding, targetY)
this.ctx.stroke()
this.ctx.setLineDash([])
}
}
Mobile Optimization¶
@media (max-width: 1024px) {
.gc-board-main {
grid-template-columns: 1fr;
padding: 16px;
}
.gc-board-hero,
.gc-board-metrics,
.gc-board-decisions,
.gc-board-frameworks,
.gc-board-trends {
grid-column: 1 / -1;
}
.gc-board-hero {
flex-direction: column;
text-align: center;
}
.gc-board-metrics {
grid-template-columns: repeat(2, 1fr);
}
}
@media (max-width: 640px) {
.gc-board-hero__value {
font-size: 36px;
}
.gc-board-metrics {
grid-template-columns: 1fr;
}
.gc-board-decision {
flex-direction: column;
align-items: flex-start;
}
.gc-board-decision__actions {
width: 100%;
display: flex;
gap: 8px;
margin-top: 12px;
}
.gc-board-decision__action {
flex: 1;
}
}
Print Optimization¶
@media print {
.gc-board-dashboard {
height: auto;
}
.gc-board-header__actions,
.gc-board-decision__action--defer {
display: none;
}
.gc-board-main {
display: block;
padding: 0;
}
.gc-board-hero,
.gc-board-metrics,
.gc-board-decisions,
.gc-board-frameworks,
.gc-board-trends {
page-break-inside: avoid;
margin-bottom: 20px;
}
.gc-board-trends__chart {
height: 200px;
}
}
Performance Optimization¶
class DashboardPerformance {
constructor() {
this.cache = new Map()
this.pendingRequests = new Map()
}
async fetchMetric(key, url) {
// Check cache first
if (this.cache.has(key)) {
const cached = this.cache.get(key)
if (Date.now() - cached.timestamp < 60000) {
// 1 minute cache
return cached.data
}
}
// Deduplicate requests
if (this.pendingRequests.has(key)) {
return this.pendingRequests.get(key)
}
// Fetch with caching
const promise = fetch(url)
.then((r) => r.json())
.then((data) => {
this.cache.set(key, {
data,
timestamp: Date.now(),
})
this.pendingRequests.delete(key)
return data
})
this.pendingRequests.set(key, promise)
return promise
}
async loadDashboard() {
// Parallel fetch all metrics
const [compliance, frameworks, decisions, trends] = await Promise.all([
this.fetchMetric('compliance', '/api/metrics/compliance'),
this.fetchMetric('frameworks', '/api/metrics/frameworks'),
this.fetchMetric('decisions', '/api/decisions/pending'),
this.fetchMetric('trends', '/api/metrics/trends'),
])
return {
compliance,
frameworks,
decisions,
trends,
}
}
// Progressive enhancement
enhanceWithDetails() {
if ('requestIdleCallback' in window) {
requestIdleCallback(() => {
this.loadDetailedMetrics()
})
} else {
setTimeout(() => this.loadDetailedMetrics(), 1000)
}
}
}
React Implementation¶
export const BoardDashboard: React.FC = () => {
const [metrics, setMetrics] = useState(null)
const [loading, setLoading] = useState(true)
const [refreshing, setRefreshing] = useState(false)
// Load initial data
useEffect(() => {
loadDashboardData().then((data) => {
setMetrics(data)
setLoading(false)
})
}, [])
// Auto-refresh
useEffect(() => {
const interval = setInterval(
() => {
setRefreshing(true)
loadDashboardData().then((data) => {
setMetrics(data)
setRefreshing(false)
})
},
5 * 60 * 1000
)
return () => clearInterval(interval)
}, [])
if (loading) {
return <DashboardSkeleton />
}
return (
<div className="gc-board-dashboard">
<BoardHeader onExport={exportReport} />
<main className="gc-board-main">
<HeroMetric
value={metrics.overallCompliance}
trend={metrics.trend}
benchmark={metrics.industryAverage}
/>
<MetricsRow metrics={metrics.keyMetrics} />
<DecisionsPanel
decisions={metrics.pendingDecisions}
onDecide={handleDecision}
onDefer={handleDefer}
/>
<FrameworksGrid frameworks={metrics.frameworks} />
<TrendChart data={metrics.trendData} />
</main>
{refreshing && <RefreshIndicator />}
</div>
)
}
Testing Checklist¶
- Dashboard loads in < 2 seconds
- All information visible without scrolling on 1080p
- Decisions can be made in < 3 clicks
- Mobile view maintains all critical information
- Print version is board-ready
- Keyboard shortcuts work correctly
- Auto-refresh doesn't disrupt user actions
- Color coding is consistent and accessible
- Export generates PDF in < 5 seconds
- Works offline with cached data
Conclusion¶
The one-screen board dashboard delivers maximum insight with minimum cognitive load. By focusing on decisions rather than data, GetCimple enables directors to fulfill their governance obligations efficiently and confidently.