Overview
A clinical research EDC-style Forms module with a calm, clinical, high-trust aesthetic. This specification serves as both the Forms module design and a reference pattern for the broader Metricis portal UI refactoring.
1. Design Tokens
Color Palette
/* Primary palette */
--color-primary: #2563eb; /* Blue 600 - primary actions */
--color-primary-hover: #1d4ed8; /* Blue 700 - hover states */
--color-primary-light: #dbeafe; /* Blue 100 - subtle backgrounds */
--color-primary-ring: rgba(37, 99, 235, 0.3); /* Focus ring */
/* Neutral palette */
--color-bg-page: #f8fafc; /* Slate 50 - page background */
--color-bg-surface: #ffffff; /* White - cards, panels */
--color-bg-muted: #f1f5f9; /* Slate 100 - subtle backgrounds */
--color-bg-hover: #f8fafc; /* Slate 50 - hover states */
/* Border colors */
--color-border: #e2e8f0; /* Slate 200 - default borders */
--color-border-strong: #cbd5e1; /* Slate 300 - emphasis borders */
/* Text colors */
--color-text-primary: #0f172a; /* Slate 900 - headings */
--color-text-secondary: #475569; /* Slate 600 - body text */
--color-text-muted: #94a3b8; /* Slate 400 - metadata */
--color-text-inverse: #ffffff; /* White - on dark backgrounds */
/* Status colors */
--color-success: #22c55e; /* Green 500 */
--color-success-light: #dcfce7; /* Green 100 */
--color-warning: #f59e0b; /* Amber 500 */
--color-warning-light: #fef3c7; /* Amber 100 */
--color-danger: #ef4444; /* Red 500 */
--color-danger-light: #fee2e2; /* Red 100 */
/* Sidebar (dark) */
--color-sidebar-bg: #1e293b; /* Slate 800 */
--color-sidebar-text: #94a3b8; /* Slate 400 */
--color-sidebar-text-active: #ffffff;
--color-sidebar-hover: #334155; /* Slate 700 */
--color-sidebar-accent: #8b5cf6; /* Violet 500 */
/* Chart colors */
--color-chart-1: #6366f1; /* Indigo 500 */
--color-chart-2: #10b981; /* Emerald 500 */
--color-chart-3: #f59e0b; /* Amber 500 */
--color-chart-4: #ef4444; /* Red 500 */
Typography Scale
/* Font family */
--font-sans: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
/* Font sizes */
--font-size-xs: 0.75rem; /* 12px - metadata, badges */
--font-size-sm: 0.875rem; /* 14px - body small, labels */
--font-size-base: 1rem; /* 16px - body default */
--font-size-lg: 1.125rem; /* 18px - section headers */
--font-size-xl: 1.25rem; /* 20px - card titles */
--font-size-2xl: 1.5rem; /* 24px - page subtitles */
--font-size-3xl: 1.875rem; /* 30px - page titles */
/* Font weights */
--font-weight-normal: 400;
--font-weight-medium: 500;
--font-weight-semibold: 600;
--font-weight-bold: 700;
/* Line heights */
--line-height-tight: 1.25;
--line-height-normal: 1.5;
--line-height-relaxed: 1.625;
Spacing Scale (8px grid)
--space-0: 0;
--space-1: 0.25rem; /* 4px */
--space-2: 0.5rem; /* 8px */
--space-3: 0.75rem; /* 12px */
--space-4: 1rem; /* 16px */
--space-5: 1.25rem; /* 20px */
--space-6: 1.5rem; /* 24px */
--space-8: 2rem; /* 32px */
--space-10: 2.5rem; /* 40px */
--space-12: 3rem; /* 48px */
--space-16: 4rem; /* 64px */
Border Radius
--radius-sm: 4px; /* Small elements, badges */
--radius-md: 6px; /* Buttons, inputs */
--radius-lg: 8px; /* Cards, panels */
--radius-xl: 12px; /* Large containers */
--radius-full: 9999px; /* Pills, avatars */
Shadows
--shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
--shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -2px rgba(0, 0, 0, 0.1);
--shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -4px rgba(0, 0, 0, 0.1);
Transitions
--transition-fast: 150ms ease;
--transition-normal: 200ms ease;
--transition-slow: 300ms ease;
Z-Index Scale
--z-base: 0;
--z-dropdown: 50;
--z-sticky: 100;
--z-overlay: 200;
--z-modal: 300;
--z-toast: 400;
2. Layout Specifications
Responsive Breakpoints
| Breakpoint |
Width |
Behavior |
sm |
640px |
Mobile - single column |
md |
768px |
Tablet - sidebar collapses to icon rail |
lg |
1024px |
Desktop - 2-3 column layouts |
xl |
1280px |
Large desktop - full 3-column |
2xl |
1536px |
Extra large - max content width |
3-Column Layout
┌─────────────────────────────────────────────────────────────────┐
│ Sidebar (260px) │ List Pane (320px) │ Detail Pane (flex-1) │
│ │ │ │
│ ┌─────────────┐ │ ┌──────────────────┐ │ ┌────────────────────┐ │
│ │ Logo │ │ │ Breadcrumb │ │ │ Header + Actions │ │
│ ├─────────────┤ │ ├──────────────────┤ │ ├────────────────────┤ │
│ │ Nav Section │ │ │ Search + Filter │ │ │ │ │
│ │ - Item │ │ ├──────────────────┤ │ │ Content Area │ │
│ │ - Item │ │ │ │ │ │ (max-w-3xl) │ │
│ │ - Item │ │ │ List Items │ │ │ │ │
│ ├─────────────┤ │ │ - Selected ▌ │ │ │ Form sections, │ │
│ │ Study │ │ │ - Item │ │ │ fields, etc. │ │
│ │ Selector │ │ │ - Item │ │ │ │ │
│ ├─────────────┤ │ │ │ │ │ │ │
│ │ Section 2 │ │ │ │ │ │ │ │
│ │ - Item │ │ │ │ │ │ │ │
│ ├─────────────┤ │ │ │ │ │ │ │
│ │ User Menu │ │ │ │ │ │ │ │
│ └─────────────┘ │ └──────────────────┘ │ └────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
Column Specifications
| Column |
Width |
Behavior |
| Sidebar |
260px fixed |
Collapses to 64px icon rail < lg |
| List Pane |
320px fixed |
Collapsible on lg+, full screen < md |
| Detail Pane |
flex-1 |
Min 480px, content max-w-3xl centered |
3. Component Specifications
StatusPill
States: draft | published | locked | archived
Size: sm (h-5) | md (h-6)
Style: Rounded-full, subtle background, medium text
| Status |
Background |
Text |
Border |
| draft |
bg-slate-100 |
text-slate-600 |
- |
| published |
bg-green-50 |
text-green-700 |
- |
| locked |
bg-amber-50 |
text-amber-700 |
- |
| archived |
bg-slate-50 |
text-slate-500 |
border-slate-200 |
ListItem
Height: 56px minimum
Padding: px-4 py-3
Selected: left-2 border-l-2 border-primary bg-primary-light/30
Hover: bg-slate-50
Structure:
┌─────────────────────────────────────────┐
│ ▌ [Icon] Title [···] │
│ Subtitle / metadata │
└─────────────────────────────────────────┘
Background: white
Border: 1px solid border
Border-radius: radius-lg
Padding: p-6
Gap between sections: gap-6
ConditionalBlock
Border: 1px dashed border-strong
Background: bg-slate-50/50
Padding: p-4
Condition chip: bg-primary-light text-primary text-xs rounded-md px-2 py-1
| State |
Border |
Ring |
Background |
| Default |
border |
- |
white |
| Hover |
border-strong |
- |
white |
| Focus |
border-primary |
ring-2 ring-primary/30 |
white |
| Error |
border-danger |
ring-2 ring-danger/30 |
white |
| Disabled |
border |
- |
bg-muted |
4. Route Structure
/forms # Forms module root
/forms/:studyId # Study forms list
/forms/:studyId/events # Events list (visits/timepoints)
/forms/:studyId/events/:eventId # Event detail with forms list
/forms/:studyId/events/:eventId/forms/:formId # Form detail/editor
/forms/:studyId/library # Form templates library
/forms/:studyId/settings # Forms module settings
URL Examples
/forms/study-123/events/baseline/forms/demographics
/forms/study-123/events/week-4/forms/adverse-events
/forms/study-123/library
5. State Model
interface FormsModuleState {
// Navigation
selectedStudyId: string | null;
selectedEventId: string | null;
selectedFormId: string | null;
// UI State
sidebarCollapsed: boolean;
listPaneCollapsed: boolean;
// Data
events: Event[];
forms: Form[];
selectedForm: FormDetail | null;
// Filters
searchQuery: string;
statusFilter: FormStatus[];
// Editor State
isDirty: boolean;
validationErrors: ValidationError[];
}
interface Event {
id: string;
code: string;
name: string;
targetDay: number;
formCount: number;
}
interface Form {
id: string;
code: string;
name: string;
subtitle: string;
status: 'draft' | 'published' | 'locked' | 'archived';
questionCount: number;
lastModified: string;
languages: string[];
}
interface FormDetail extends Form {
sections: FormSection[];
metadata: FormMetadata;
}
interface FormSection {
id: string;
title: string;
description?: string;
fields: FormField[];
condition?: ConditionalLogic;
}
interface FormField {
id: string;
type: 'text' | 'number' | 'date' | 'select' | 'radio' | 'checkbox' | 'textarea';
label: string;
helpText?: string;
required: boolean;
validation?: FieldValidation;
condition?: ConditionalLogic;
options?: FieldOption[]; // For select/radio/checkbox
}
6. Accessibility Requirements
Keyboard Navigation
| Key |
Action |
| Tab |
Move between focusable elements |
| Shift+Tab |
Move backwards |
| Enter/Space |
Activate buttons, select items |
| Arrow Up/Down |
Navigate list items, menu items |
| Escape |
Close modals, dropdowns, cancel |
| Cmd/Ctrl+S |
Save form (when editing) |
ARIA Requirements
- Sidebar:
nav with aria-label="Main navigation"
- List pane:
role="listbox" with aria-activedescendant
- Selected items:
aria-selected="true"
- Status pills: Include
aria-label for screen readers
- Form fields: Proper
label association, aria-describedby for help text
- Error states:
aria-invalid="true", aria-errormessage
Focus Management
- Visible focus ring on all interactive elements
- Focus trap in modals and dropdowns
- Return focus to trigger element on close
- Skip link for main content
7. Responsive Behavior
Mobile (< 768px)
- Sidebar: Hidden, accessible via hamburger menu (drawer)
- List pane: Full screen view
- Detail pane: Full screen view (navigated to via route)
- Navigation: Stack views, use back button
Tablet (768px - 1024px)
- Sidebar: Collapsed to 64px icon rail
- List pane: 280px width, collapsible
- Detail pane: Remaining width
- Touch: Larger hit targets (min 44px)
Desktop (> 1024px)
- Full 3-column layout
- Sidebar: 260px
- List pane: 320px (collapsible)
- Detail pane: flex-1
8. Animation & Transitions
Micro-interactions
| Element |
Property |
Duration |
Easing |
| Buttons |
background, transform |
150ms |
ease |
| List selection |
background, border |
150ms |
ease |
| Dropdown open |
opacity, transform |
200ms |
ease-out |
| Sidebar collapse |
width |
200ms |
ease |
| Toast enter |
transform, opacity |
300ms |
ease-out |
| Toast exit |
opacity |
200ms |
ease-in |
Loading States
- Skeleton loaders for content areas
- Subtle pulse animation
- Maintain layout stability (no layout shift)
9. Component Inventory
Required Components (shadcn/ui + custom)
Navigation
- [ ] SidebarNav
- [ ] SidebarSection
- [ ] SidebarLink
- [ ] SidebarUserMenu
- [ ] Breadcrumbs
- [ ] IconRail (collapsed sidebar)
List Pane
- [ ] SearchInput
- [ ] FilterButton + Popover
- [ ] ListItem
- [ ] ListItemSkeleton
- [ ] EmptyState
Detail Pane
- [ ] DetailHeader
- [ ] StatusPill
- [ ] LanguageSelector
- [ ] OptionsMenu
- [ ] FormSection
- [ ] ConditionalBlock
- [ ] FieldRow
- [ ] TextInput
- [ ] NumberInput
- [ ] DateInput
- [ ] Select
- [ ] RadioGroup
- [ ] CheckboxGroup
- [ ] Textarea
- [ ] InlineHelp
Feedback
- [ ] Toast
- [ ] LoadingSkeleton
- [ ] ErrorState
- [ ] ConfirmDialog
10. Implementation Priority
Phase 1: Foundation
- Design tokens in Tailwind config
- Base layout components (AppShell, Sidebar)
- Core UI primitives (Button, Input, Select)
Phase 2: Navigation
- SidebarNav with collapsed state
- Breadcrumbs
- List pane with search/filter
- Events list view
- Forms list within event
- Form detail view
- Form sections and fields
Phase 4: Interactivity
- Form editing capabilities
- Conditional logic UI
- Validation and error states
- Save/publish workflow
Phase 5: Polish
- Loading states and skeletons
- Animations and transitions
- Keyboard navigation refinement
- Accessibility audit