π΄ Accessibility-First Card Components¶
Design Philosophy¶
GetCimple's card components prioritize clarity and accessibility for board directors who may:
- Be 50+ years old with presbyopia
- Use reading glasses
- Review on tablets during meetings
- Have limited time (2-3 minutes)
- Prefer clear visual hierarchy
Card Component Specifications¶
Base Card Structure¶
<article class="gc-card" role="region" aria-labelledby="card-title-{id}">
<header class="gc-card__header">
<h2 id="card-title-{id}" class="gc-card__title">Card Title</h2>
<span class="gc-card__badge" aria-label="Status">Status</span>
</header>
<div class="gc-card__body">
<div class="gc-card__metric">
<span class="gc-card__metric-value">85%</span>
<span class="gc-card__metric-label">Complete</span>
</div>
<p class="gc-card__description">Brief description text</p>
</div>
<footer class="gc-card__footer">
<button class="gc-card__action" aria-label="View details">
View Details
</button>
</footer>
</article>
Visual Design Specifications¶
Typography¶
.gc-card {
/* Base font size - Never below 16px */
font-size: 16px;
line-height: 1.6;
font-family: 'Inter', system-ui, sans-serif;
}
.gc-card__title {
font-size: 20px; /* 1.25rem */
font-weight: 600;
color: var(--gc-text-primary);
}
.gc-card__metric-value {
font-size: 32px; /* 2rem */
font-weight: 700;
font-variant-numeric: tabular-nums;
}
.gc-card__metric-label {
font-size: 14px; /* 0.875rem - Exception for labels */
text-transform: uppercase;
letter-spacing: 0.05em;
color: var(--gc-text-secondary);
}
.gc-card__description {
font-size: 16px;
color: var(--gc-text-secondary);
}
Layout & Spacing¶
.gc-card {
/* 4:3 aspect ratio for optimal scanning */
aspect-ratio: 4 / 3;
min-height: 240px;
max-width: 400px;
/* Generous padding for touch targets */
padding: 24px;
/* Clear visual boundaries */
border: 2px solid var(--gc-border-default);
border-radius: 12px;
background: var(--gc-bg-card);
/* Subtle elevation */
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
/* Smooth interactions */
transition: all 0.2s ease;
}
.gc-card:hover,
.gc-card:focus-within {
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);
border-color: var(--gc-border-hover);
}
.gc-card__header {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: 16px;
}
.gc-card__body {
flex: 1;
display: flex;
flex-direction: column;
gap: 12px;
}
.gc-card__footer {
margin-top: auto;
padding-top: 16px;
border-top: 1px solid var(--gc-border-light);
}
Color System¶
Semantic Colors¶
:root {
/* Status Colors - Traffic Light System */
--gc-status-good: #10b981; /* Green */
--gc-status-warning: #f59e0b; /* Amber */
--gc-status-critical: #ef4444; /* Red */
/* Informational */
--gc-info: #3b82f6; /* Blue */
--gc-inactive: #6b7280; /* Gray */
/* Text Colors */
--gc-text-primary: #111827;
--gc-text-secondary: #4b5563;
--gc-text-disabled: #9ca3af;
/* Backgrounds */
--gc-bg-card: #ffffff;
--gc-bg-hover: #f9fafb;
/* Borders */
--gc-border-default: #e5e7eb;
--gc-border-hover: #3b82f6;
--gc-border-light: #f3f4f6;
}
/* High Contrast Mode */
@media (prefers-contrast: high) {
:root {
--gc-text-primary: #000000;
--gc-text-secondary: #374151;
--gc-border-default: #6b7280;
--gc-status-good: #059669;
--gc-status-warning: #d97706;
--gc-status-critical: #dc2626;
}
}
/* Dark Mode */
@media (prefers-color-scheme: dark) {
:root {
--gc-text-primary: #f9fafb;
--gc-text-secondary: #d1d5db;
--gc-bg-card: #1f2937;
--gc-bg-hover: #374151;
--gc-border-default: #4b5563;
--gc-border-hover: #60a5fa;
}
}
Accessibility Features¶
Keyboard Navigation¶
.gc-card:focus-visible,
.gc-card__action:focus-visible {
outline: 3px solid var(--gc-info);
outline-offset: 2px;
}
.gc-card__action {
/* Large touch target (minimum 44x44px) */
min-height: 44px;
padding: 12px 24px;
/* Clear interaction states */
cursor: pointer;
background: var(--gc-info);
color: white;
border: none;
border-radius: 8px;
font-size: 16px;
font-weight: 500;
transition: background 0.2s ease;
}
.gc-card__action:hover {
background: #2563eb;
}
.gc-card__action:active {
transform: scale(0.98);
}
Screen Reader Support¶
<!-- Status Badge with Context -->
<span
class="gc-card__badge gc-card__badge--success"
role="status"
aria-label="Status: Compliant"
>
<span aria-hidden="true">β</span>
Compliant
</span>
<!-- Progress Indicator -->
<div
class="gc-card__progress"
role="progressbar"
aria-valuenow="85"
aria-valuemin="0"
aria-valuemax="100"
aria-label="85% complete"
>
<div class="gc-card__progress-bar" style="width: 85%"></div>
</div>
<!-- Time Estimate -->
<time class="gc-card__time" datetime="PT3M">
<span class="sr-only">Estimated time:</span>
3 minutes
</time>
Card Variants¶
1. Status Card¶
Used for compliance status, maturity levels, and framework progress.
<article class="gc-card gc-card--status">
<header class="gc-card__header">
<h2>Essential Eight</h2>
<span class="gc-card__badge gc-card__badge--warning">ML1</span>
</header>
<div class="gc-card__body">
<div class="gc-card__metric">
<span class="gc-card__metric-value">40%</span>
<span class="gc-card__metric-label">Current Maturity</span>
</div>
<div class="gc-card__target">Target: ML2 (70%)</div>
<div class="gc-card__progress" role="progressbar">
<div class="gc-card__progress-bar" style="width: 40%"></div>
</div>
</div>
<footer class="gc-card__footer">
<button class="gc-card__action">Review Details</button>
</footer>
</article>
2. Action Card¶
For board decisions and approvals.
<article class="gc-card gc-card--action">
<header class="gc-card__header">
<h2>Board Decision Required</h2>
<time class="gc-card__time">3 min</time>
</header>
<div class="gc-card__body">
<p class="gc-card__highlight">
Set target maturity level for Essential Eight
</p>
<ul class="gc-card__options">
<li>ML1: Basic Protection ($50k)</li>
<li>ML2: Industry Standard ($150k)</li>
<li>ML3: Advanced ($400k)</li>
</ul>
</div>
<footer class="gc-card__footer">
<button class="gc-card__action gc-card__action--primary">
Make Decision
</button>
</footer>
</article>
3. Metric Card¶
For key performance indicators.
<article class="gc-card gc-card--metric">
<div class="gc-card__body">
<div class="gc-card__metric gc-card__metric--large">
<span class="gc-card__metric-value">92%</span>
<span class="gc-card__metric-label">Compliance Score</span>
</div>
<div class="gc-card__trend gc-card__trend--up">
<span aria-label="Increased by">β 5%</span>
from last quarter
</div>
</div>
</article>
4. Summary Card¶
For aggregated information.
<article class="gc-card gc-card--summary">
<header class="gc-card__header">
<h2>Quarterly Summary</h2>
</header>
<div class="gc-card__body">
<dl class="gc-card__list">
<div class="gc-card__list-item">
<dt>Frameworks Tracked</dt>
<dd>5</dd>
</div>
<div class="gc-card__list-item">
<dt>Overall Compliance</dt>
<dd>85%</dd>
</div>
<div class="gc-card__list-item">
<dt>Actions Required</dt>
<dd>2</dd>
</div>
</dl>
</div>
<footer class="gc-card__footer">
<button class="gc-card__action">View Report</button>
</footer>
</article>
Responsive Behavior¶
Desktop (1200px+)¶
- Cards in 3-4 column grid
- Full aspect ratio maintained
- Hover states enabled
Tablet (768px - 1199px)¶
- Cards in 2 column grid
- Slightly reduced padding (20px)
- Touch-optimized interactions
Mobile (< 768px)¶
- Single column stack
- Full width cards
- Increased touch targets (48px minimum)
- Simplified metrics display
@media (max-width: 767px) {
.gc-card {
aspect-ratio: auto;
min-height: auto;
max-width: 100%;
padding: 20px;
}
.gc-card__metric-value {
font-size: 28px;
}
.gc-card__action {
width: 100%;
min-height: 48px;
}
}
Animation & Transitions¶
/* Smooth state changes */
.gc-card {
transition:
box-shadow 0.2s ease,
border-color 0.2s ease,
transform 0.2s ease;
}
/* Progress bar animation */
@keyframes progress-fill {
from {
width: 0;
}
to {
width: var(--progress);
}
}
.gc-card__progress-bar {
animation: progress-fill 1s ease-out;
transition: width 0.5s ease;
}
/* Badge pulse for attention */
@keyframes pulse {
0%,
100% {
opacity: 1;
}
50% {
opacity: 0.7;
}
}
.gc-card__badge--critical {
animation: pulse 2s infinite;
}
Implementation Examples¶
React Component¶
interface CardProps {
title: string
metric?: {
value: string | number
label: string
}
status?: 'success' | 'warning' | 'critical'
action?: {
label: string
onClick: () => void
}
timeEstimate?: number // in minutes
}
export const Card: React.FC<CardProps> = ({
title,
metric,
status,
action,
timeEstimate,
}) => {
return (
<article
className="gc-card"
role="region"
aria-labelledby={`card-${title}`}
>
<header className="gc-card__header">
<h2 id={`card-${title}`}>{title}</h2>
{status && <StatusBadge status={status} />}
{timeEstimate && <TimeEstimate minutes={timeEstimate} />}
</header>
{metric && (
<div className="gc-card__body">
<div className="gc-card__metric">
<span className="gc-card__metric-value">{metric.value}</span>
<span className="gc-card__metric-label">{metric.label}</span>
</div>
</div>
)}
{action && (
<footer className="gc-card__footer">
<button
className="gc-card__action"
onClick={action.onClick}
aria-label={action.label}
>
{action.label}
</button>
</footer>
)}
</article>
)
}
Testing Checklist¶
Accessibility Testing¶
- Keyboard navigation works for all interactive elements
- Screen reader announces card content correctly
- Color contrast meets WCAG 2.1 AA (4.5:1 for normal text, 3:1 for large text)
- Focus indicators are visible and clear
- Touch targets are minimum 44x44px
Visual Testing¶
- Cards maintain 4:3 aspect ratio on desktop
- Text remains readable at 200% zoom
- High contrast mode displays correctly
- Dark mode maintains readability
- Responsive breakpoints work smoothly
Performance Testing¶
- Cards render within 16ms (60fps)
- Animations use CSS transforms for GPU acceleration
- No layout shift when content loads
- Lazy loading for off-screen cards
Conclusion¶
These accessibility-first card components ensure GetCimple's interface is usable by all board directors, regardless of age, ability, or device. The design prioritizes clarity, readability, and quick comprehension - essential for time-pressed executives making critical governance decisions.