Design direction changed from Clinical Utilitarian to Clinical Luxury, updated all plans etc
This commit is contained in:
@@ -76,15 +76,15 @@ The sidebar replicates the dark navigation panel found in EMIS Web and similar c
|
||||
|
||||
### Styling
|
||||
|
||||
- Each item: 44px height, 16px left padding, icon (18px, `lucide-react`) + label in Inter 500, 14px
|
||||
- Each item: 44px height, 16px left padding, icon (18px, `lucide-react`) + label in [UI font] 500, 14px
|
||||
- Default state: white text at 70% opacity, transparent background
|
||||
- Hover state: white text at 100% opacity, background `rgba(255,255,255,0.08)`
|
||||
- Active state: white text at 100%, NHS blue left border (3px), background `rgba(255,255,255,0.12)`, label in Inter 600
|
||||
- Active state: white text at 100%, NHS blue left border (3px), background `rgba(255,255,255,0.12)`, label in [UI font] 600
|
||||
- A thin horizontal separator line (`1px solid rgba(255,255,255,0.1)`) appears between "Summary" and "Consultations" (separating the overview from the detail views)
|
||||
|
||||
### Sidebar Footer
|
||||
|
||||
At the bottom of the sidebar, in small text (Inter 400, 11px, `#64748B`):
|
||||
At the bottom of the sidebar, in small text ([UI font] 400, 11px, `#64748B`):
|
||||
```
|
||||
Session: A.CHARLWOOD
|
||||
Logged in: [current time]
|
||||
@@ -98,7 +98,7 @@ At the top, above the navigation items, a small logo or system name:
|
||||
CareerRecord PMR
|
||||
v1.0.0
|
||||
```
|
||||
In Inter 500, 13px, white at 50% opacity. Styled like the "EMIS Web" branding that appears in the top-left of the real system.
|
||||
In [UI font] 500, 13px, white at 50% opacity. Styled like clinical system branding that appears in the top-left of the navigation.
|
||||
|
||||
---
|
||||
|
||||
@@ -132,7 +132,7 @@ A breadcrumb appears at the top of the main content area:
|
||||
Patient Record > Consultations > Interim Head, Population Health
|
||||
```
|
||||
|
||||
The breadcrumb updates as the user navigates deeper (e.g., expanding a consultation). Clicking "Patient Record" returns to Summary. Clicking "Consultations" collapses any expanded entries and shows the full journal list. The breadcrumb is styled in Inter 400, 13px, gray-400, with chevron separators.
|
||||
The breadcrumb updates as the user navigates deeper (e.g., expanding a consultation). Clicking "Patient Record" returns to Summary. Clicking "Consultations" collapses any expanded entries and shows the full journal list. The breadcrumb is styled in [UI font] 400, 13px, gray-400, with chevron separators.
|
||||
|
||||
### Secondary Navigation: Within-View Interactions
|
||||
|
||||
@@ -146,18 +146,18 @@ The breadcrumb updates as the user navigates deeper (e.g., expanding a consultat
|
||||
|
||||
---
|
||||
|
||||
## Design Guidance (from /frontend-design)
|
||||
## Design Guidance
|
||||
|
||||
### Aesthetic Direction
|
||||
|
||||
**Clinical Institutional Precision** — The NHS Patient Administration System (PAS) header bar, faithfully reproduced as personal branding. This is not a "medical theme" website. It is a clinical system UI that happens to contain career data instead of patient data. The fidelity to real NHS IT systems (EMIS Web, SystmOne, Lorenzo) is the entire point.
|
||||
**Clinical Luxury** — The patient banner and sidebar draw their *structure* from NHS clinical systems (PAS headers, EMIS Web navigation), but the *execution* is premium — refined typography, layered shadows, considered spacing. The clinical metaphor lives in the layout and conventions (surname-first, pipe separators, status dots); the luxury lives in the finish.
|
||||
|
||||
- **Tone**: Utilitarian, institutional, information-dense. No decoration. No gradients. No shadows. The beauty is in the data density, the pipe separators, the monospaced identifiers, the surname-first convention, the green status dot.
|
||||
- **Tone**: Precise, information-dense, and refined. Generous whitespace, layered shadows, and premium typography elevate what would otherwise be institutional UI. The clinical conventions (data density, pipe separators, monospaced identifiers, surname-first, green status dot) provide authentic texture.
|
||||
- **Typography Discipline**:
|
||||
- Inter at 600 weight for the patient name — the anchor element
|
||||
- [UI font] at 600 weight for the patient name — the anchor element
|
||||
- Geist Mono for structured identifiers (NHS Number, DOB) — monospaced data feels like it came from a database
|
||||
- Inter at normal weight for demographic text
|
||||
- The pipe character `|` as a data separator is a deliberate NHS PAS convention
|
||||
- [UI font] at normal weight for demographic text
|
||||
- The pipe character `|` as a data separator is a deliberate clinical convention
|
||||
|
||||
### Design System Tokens
|
||||
|
||||
@@ -170,7 +170,7 @@ The breadcrumb updates as the user navigates deeper (e.g., expanding a consultat
|
||||
| Border | `#E5E7EB` | 1px solid borders |
|
||||
| Border Radius | `4px` | All UI elements |
|
||||
| Green Status | `#22C55E` | Active status dot |
|
||||
| Font Text | `Inter` | All text content |
|
||||
| Font Text | [UI font] | All text content (Elvaro or Blumir — see CLAUDE.md) |
|
||||
| Font Data | `Geist Mono` | Monospaced identifiers |
|
||||
|
||||
### Key Design Decisions
|
||||
@@ -197,7 +197,7 @@ The breadcrumb updates as the user navigates deeper (e.g., expanding a consultat
|
||||
5. **Navigation Item States**:
|
||||
- Default: white text at 70% opacity, transparent background
|
||||
- Hover: white text at 100%, background `rgba(255,255,255,0.08)`
|
||||
- Active: white text at 100%, 3px NHS blue left border, background `rgba(255,255,255,0.12)`, Inter 600 weight
|
||||
- Active: white text at 100%, 3px NHS blue left border, background `rgba(255,255,255,0.12)`, [UI font] 600 weight
|
||||
|
||||
6. **Interface Materialization Animations** (PMRInterface):
|
||||
- Patient banner slides down (200ms ease-out)
|
||||
@@ -281,7 +281,7 @@ const navItems: NavItem[] = [
|
||||
<button
|
||||
className={`
|
||||
w-full h-[44px] px-4 flex items-center gap-3
|
||||
font-inter text-[14px] font-medium
|
||||
font-ui text-[14px] font-medium
|
||||
transition-all duration-150
|
||||
${isActive
|
||||
? 'text-white bg-white/[0.12] border-l-[3px] border-pmr-nhsblue font-semibold'
|
||||
@@ -485,7 +485,7 @@ const viewLabels: Record<ViewId, string> = {
|
||||
referrals: 'Contact'
|
||||
}
|
||||
|
||||
// Styling: Inter 400, 13px, gray-400
|
||||
// Styling: [UI font] 400, 13px, gray-400
|
||||
// Chevron separators using Lucide ChevronRight
|
||||
// Clickable links navigate back
|
||||
```
|
||||
|
||||
@@ -1,359 +0,0 @@
|
||||
# Reference: Boot Sequence + ECG Animation
|
||||
|
||||
> Covers the full pre-login flow: terminal boot → cursor transition → ECG heartbeat → name reveal → flatline. The flatline→login transition is covered in `ref-transition-login.md`.
|
||||
|
||||
---
|
||||
|
||||
## Current Architecture
|
||||
|
||||
Two components manage the pre-login flow:
|
||||
- `src/components/BootSequence.tsx` → terminal text animation, ends with blinking cursor
|
||||
- `src/components/ECGAnimation.tsx` → canvas-based heartbeat + name tracing + flatline + bg transition
|
||||
- `App.tsx` phases: `boot → ecg → login → pmr`
|
||||
|
||||
## What Needs to Change
|
||||
|
||||
### 1. Boot Sequence — Clean Up for Easy Config
|
||||
|
||||
**Problem:** Boot text lines are hardcoded as HTML strings with inline Tailwind classes. Adding/removing/reordering lines requires editing raw HTML. The `dangerouslySetInnerHTML` approach is fragile.
|
||||
|
||||
**Fix:** Refactor to a clean config-driven structure:
|
||||
```typescript
|
||||
// Example config structure — easy to customize
|
||||
const BOOT_CONFIG = {
|
||||
header: { text: 'CLINICAL TERMINAL v3.2.1', style: 'bright' },
|
||||
lines: [
|
||||
{ type: 'status', text: 'Initialising pharmacist profile...' },
|
||||
{ type: 'separator' },
|
||||
{ type: 'field', label: 'SYSTEM', value: 'NHS Norfolk & Waveney ICB' },
|
||||
{ type: 'field', label: 'USER', value: 'Andy Charlwood' },
|
||||
{ type: 'field', label: 'ROLE', value: 'Deputy Head of Population Health & Data Analysis' },
|
||||
{ type: 'field', label: 'LOCATION', value: 'Norwich, UK' },
|
||||
{ type: 'separator' },
|
||||
{ type: 'status', text: 'Loading modules...' },
|
||||
{ type: 'module', name: 'pharmacist_core.sys' },
|
||||
{ type: 'module', name: 'population_health.mod' },
|
||||
{ type: 'module', name: 'data_analytics.eng' },
|
||||
{ type: 'separator' },
|
||||
{ type: 'ready', text: 'READY — Rendering CV..' },
|
||||
],
|
||||
timing: { lineDelay: 220, holdAfterComplete: 400, fadeOutDuration: 800 },
|
||||
}
|
||||
```
|
||||
- Each line type maps to a React component (not raw HTML)
|
||||
- Colors remain: bright green `#00ff41`, dim green `#3a6b45`, cyan labels `#00e5ff`
|
||||
- Staggered reveal timing stays the same (220ms per line)
|
||||
- Font: Fira Code (this is the terminal phase, NOT the PMR — Fira Code is correct here)
|
||||
|
||||
### 2. Cursor → Dot Transition
|
||||
|
||||
**Problem:** The boot sequence ends with a blinking green block cursor (`.animate-blink`). The ECG animation starts with a glowing dot that appears at the far left of the screen. There's a visual disconnect — the cursor and dot don't connect.
|
||||
|
||||
**Fix:** The blinking cursor at the end of boot should smoothly transition INTO the ECG's glowing trace dot:
|
||||
- At end of boot, capture the cursor's screen position (x, y)
|
||||
- Pass this position to ECGAnimation via props
|
||||
- ECGAnimation starts with its glowing dot AT the cursor position
|
||||
- The cursor stops blinking and morphs: block cursor → circular glow (scale down width, increase glow, ~300ms)
|
||||
- The dot then begins moving rightward, drawing the flatline/heartbeat trace behind it
|
||||
- This means the ECG trace starts at the cursor position, NOT the far left edge
|
||||
|
||||
### 3. ECG Start Position
|
||||
|
||||
**Problem:** Currently the ECG trace starts at x=0 (far left of viewport). The cursor ends somewhere in the middle-left of the screen. This means the dot "teleports" from cursor position to the left edge.
|
||||
|
||||
**Fix:** The ECG animation should:
|
||||
- Accept a `startPosition: { x: number, y: number }` prop from the boot sequence
|
||||
- Begin the trace from that position
|
||||
- The first section of trace is a flat line from the cursor position rightward
|
||||
- Heartbeats begin after the first flat gap (same timing as now, just offset)
|
||||
- The viewport scroll logic already handles the "head" position — just shift the world-space origin
|
||||
|
||||
### 4. Text Reveal — Mask Technique
|
||||
|
||||
**Problem:** The current ECGAnimation.tsx reveals letters by stroking them with progressive alpha (`letterProgress > 0.3` → fade in). This looks like letters fading in, not like the ECG line IS the letter shape.
|
||||
|
||||
**Reference:** `ECGCombined.tsx` (Remotion version at project root) uses a superior technique:
|
||||
- The actual text characters are pre-rendered on the canvas (stroke-only, no fill)
|
||||
- A wipe mask follows the ECG trace head, revealing the text underneath
|
||||
- The ECG trace line IS the path that forms each letter shape (via the `getYAtX` function which returns letter Y values when in text region)
|
||||
- Connector lines between letters sit at the baseline
|
||||
- The neon glow filter applies to both the trace and revealed text
|
||||
|
||||
**What to adopt from ECGCombined.tsx:**
|
||||
- The mask-based text reveal approach (the trace unveils pre-rendered text)
|
||||
- The connector lines between letters at baseline
|
||||
- The neon glow SVG filters (or canvas equivalents)
|
||||
- The text mask brush that follows the trace head
|
||||
|
||||
**What to KEEP from current ECGAnimation.tsx:**
|
||||
- The character spacing (current `LETTER_W`, `LETTER_G`, `SPACE_W` values — preferred over ECGCombined.tsx spacing)
|
||||
- The heartbeat waveform shape (`generateHeartbeatPoints`)
|
||||
- The beat timing and amplitude escalation (0.3 → 0.55 → 0.85 → 1.0)
|
||||
- The canvas rendering approach (not SVG — canvas is correct for this performance-sensitive animation)
|
||||
- The viewport scrolling/camera logic
|
||||
- The flatline draw phase
|
||||
- The scanline and vignette effects
|
||||
- The background color transition to `#1E293B`
|
||||
|
||||
### 5. Text Rendering
|
||||
|
||||
- The name is still "ANDREW CHARLWOOD"
|
||||
- Letters are stroke-only (no fill) in neon green `#00ff41`
|
||||
- Each letter shape is defined by the `ECG_LETTERS` point arrays (keep these)
|
||||
- The trace line passes through each letter's shape points, making the ECG waveform form recognizable letter shapes
|
||||
- Between letters, the trace returns to baseline with short connector segments
|
||||
- Neon glow effect on both trace and revealed text
|
||||
|
||||
## Transition to Login
|
||||
|
||||
After the text is fully revealed and the flatline extends to the right edge, the flow continues as described in `ref-transition-login.md`:
|
||||
1. Name holds with glow (300ms)
|
||||
2. Glow fades, flatline extends right
|
||||
3. Canvas fades to black (200ms)
|
||||
4. Background transitions to dark blue-gray `#1E293B` (200ms)
|
||||
5. Login card materializes
|
||||
|
||||
## prefers-reduced-motion
|
||||
|
||||
With reduced motion enabled:
|
||||
- Boot text appears instantly (no stagger)
|
||||
- Cursor appears immediately
|
||||
- ECG animation is skipped entirely — transition straight from boot to login
|
||||
- Or: show the final frame (name fully revealed, flatline) as a static image for 1 second, then proceed to login
|
||||
|
||||
## Testing Checklist
|
||||
|
||||
- [ ] Boot text renders correctly with all lines
|
||||
- [ ] Blinking cursor visible at end of boot
|
||||
- [ ] Cursor smoothly transitions to ECG dot (no teleport)
|
||||
- [ ] ECG trace starts from cursor position
|
||||
- [ ] Heartbeats render with increasing amplitude
|
||||
- [ ] Name letters revealed via mask technique (not alpha fade)
|
||||
- [ ] Connector lines between letters
|
||||
- [ ] Neon glow on trace and text
|
||||
- [ ] Flatline extends to right edge after name
|
||||
- [ ] Background transitions to `#1E293B`
|
||||
- [ ] Scanlines and vignette present
|
||||
- [ ] Reduced motion: instant/static
|
||||
- [ ] Mobile: scales correctly
|
||||
|
||||
---
|
||||
|
||||
## Design Guidance (from /frontend-design)
|
||||
|
||||
> Pre-baked design direction. Do NOT invoke `/frontend-design` at runtime — this section contains the output.
|
||||
|
||||
### Aesthetic Direction: Authentic Clinical Terminal → Medical Monitor Realism
|
||||
|
||||
This isn't "medical-themed" design — this IS medical equipment interfaces. Two distinct phases:
|
||||
1. **Boot Terminal**: Authentic 1990s clinical system boot sequence (think legacy pharmacy dispensing systems, hospital terminal logins). CRT monitor aesthetic with phosphor green, scanlines, slight text glow.
|
||||
2. **ECG Monitor**: Hospital bedside cardiac monitor realism. The heartbeat isn't decorative — it's a functioning waveform that becomes letterforms through technical precision.
|
||||
|
||||
**Visual Signature**: The cursor-to-dot morphing transition. Most animations have discrete phases; this creates continuous material transformation — the blinking terminal cursor literally becomes the ECG trace point. It's the moment where "loading system" becomes "reading vital signs."
|
||||
|
||||
### Boot Sequence — Type-Safe Config
|
||||
|
||||
```typescript
|
||||
type BootLineType = 'header' | 'status' | 'separator' | 'field' | 'module' | 'ready'
|
||||
|
||||
interface BootLine {
|
||||
type: BootLineType
|
||||
text: string
|
||||
label?: string
|
||||
value?: string
|
||||
style?: 'bright' | 'dim' | 'cyan'
|
||||
}
|
||||
|
||||
interface BootConfig {
|
||||
header: string
|
||||
lines: BootLine[]
|
||||
timing: {
|
||||
lineDelay: number
|
||||
cursorBlinkInterval: number
|
||||
holdAfterComplete: number
|
||||
fadeOutDuration: number
|
||||
}
|
||||
colors: {
|
||||
bright: string
|
||||
dim: string
|
||||
cyan: string
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Component architecture:
|
||||
- `<BootLine>` — renders individual line types with appropriate styling
|
||||
- `<BootCursor>` — separate component for cursor with ref exposure for position capture
|
||||
- Config-driven rendering replaces hardcoded HTML
|
||||
|
||||
Example config:
|
||||
```typescript
|
||||
const BOOT_CONFIG: BootConfig = {
|
||||
header: 'CLINICAL TERMINAL v3.2.1',
|
||||
lines: [
|
||||
{ type: 'status', text: 'Initialising pharmacist profile...', style: 'dim' },
|
||||
{ type: 'separator', text: '---', style: 'dim' },
|
||||
{ type: 'field', label: 'SYSTEM', value: 'NHS Norfolk & Waveney ICB', style: 'cyan' },
|
||||
{ type: 'field', label: 'USER', value: 'Andy Charlwood', style: 'bright' },
|
||||
// ... etc
|
||||
],
|
||||
timing: { lineDelay: 220, cursorBlinkInterval: 530, holdAfterComplete: 400, fadeOutDuration: 800 },
|
||||
colors: { bright: '#00ff41', dim: '#3a6b45', cyan: '#00e5ff' }
|
||||
}
|
||||
```
|
||||
|
||||
Example BootLine component:
|
||||
```typescript
|
||||
function BootLine({ line }: { line: BootLine }) {
|
||||
const colors = BOOT_CONFIG.colors
|
||||
const color = line.style ? colors[line.style] : colors.dim
|
||||
|
||||
if (line.type === 'field') {
|
||||
return (
|
||||
<div className="font-mono text-sm leading-relaxed">
|
||||
<span style={{ color: colors.cyan }}>{line.label?.padEnd(9)}</span>
|
||||
<span style={{ color }}>{line.value}</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
if (line.type === 'module') {
|
||||
return (
|
||||
<div className="font-mono text-sm leading-relaxed">
|
||||
<span className="font-bold" style={{ color: colors.bright }}>[OK]</span>
|
||||
{' '}
|
||||
<span style={{ color: colors.dim }}>{line.text}</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// ... other types
|
||||
}
|
||||
```
|
||||
|
||||
### Cursor → Dot Transition — Implementation
|
||||
|
||||
```typescript
|
||||
const [cursorPosition, setCursorPosition] = useState<{x: number, y: number} | null>(null)
|
||||
const cursorRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
useEffect(() => {
|
||||
if (cursorRef.current) {
|
||||
const rect = cursorRef.current.getBoundingClientRect()
|
||||
setCursorPosition({
|
||||
x: rect.left + rect.width / 2,
|
||||
y: rect.top + rect.height / 2
|
||||
})
|
||||
}
|
||||
}, [/* trigger when boot completes */])
|
||||
|
||||
// Pass to ECG:
|
||||
<ECGAnimation startPosition={cursorPosition} onComplete={...} />
|
||||
```
|
||||
|
||||
Morph animation: width 8px→0px, height 16px→6px, border-radius 0→50% (300ms ease-out). Simultaneously fade in ECG dot at same position. After morph complete, begin trace movement.
|
||||
|
||||
### Canvas Mask Reveal — Implementation
|
||||
|
||||
```javascript
|
||||
// Pre-render text to offscreen canvas
|
||||
const textCanvas = document.createElement('canvas')
|
||||
const textCtx = textCanvas.getContext('2d')
|
||||
textCtx.strokeStyle = lineColor
|
||||
textCtx.lineWidth = 1.5
|
||||
textCtx.font = `bold ${fontSize}px Arial`
|
||||
textLayout.forEach(item => {
|
||||
textCtx.strokeText(item.char, item.centerX, baselineY)
|
||||
})
|
||||
|
||||
// During animation loop:
|
||||
ctx.save()
|
||||
|
||||
// Create clipping path following trace head
|
||||
ctx.beginPath()
|
||||
ctx.rect(0, 0, headSX + 20, vh) // reveal up to head position + lead
|
||||
ctx.clip()
|
||||
|
||||
// Draw pre-rendered text through clip
|
||||
ctx.drawImage(textCanvas, -viewOff, 0)
|
||||
|
||||
ctx.restore()
|
||||
|
||||
// Feathered leading edge:
|
||||
const gradient = ctx.createLinearGradient(headSX - 30, 0, headSX, 0)
|
||||
gradient.addColorStop(0, 'rgba(0,255,65,0)')
|
||||
gradient.addColorStop(1, 'rgba(0,255,65,1)')
|
||||
```
|
||||
|
||||
### Connector Lines Between Letters
|
||||
|
||||
```javascript
|
||||
const connectors: {startX: number, endX: number}[] = []
|
||||
for (let i = 0; i < textLayout.length - 1; i++) {
|
||||
const curr = textLayout[i]
|
||||
const next = textLayout[i + 1]
|
||||
const endInset = CONNECTOR_INSETS[curr.char] || 0
|
||||
const startInset = CONNECTOR_INSETS[next.char] || 0
|
||||
connectors.push({
|
||||
startX: curr.endX - endInset,
|
||||
endX: next.startX + startInset
|
||||
})
|
||||
}
|
||||
|
||||
// During draw:
|
||||
connectors.forEach(conn => {
|
||||
if (headWX > conn.startX) {
|
||||
const connectorEndX = Math.min(conn.endX, headWX)
|
||||
ctx.beginPath()
|
||||
ctx.moveTo(conn.startX - viewOff, baselineY)
|
||||
ctx.lineTo(connectorEndX - viewOff, baselineY)
|
||||
ctx.stroke()
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### Visual Enhancement Details
|
||||
|
||||
**CRT Scanlines** (boot phase):
|
||||
```css
|
||||
.boot-scanlines {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: repeating-linear-gradient(
|
||||
0deg,
|
||||
rgba(0, 0, 0, 0.15) 0px,
|
||||
transparent 1px,
|
||||
transparent 2px,
|
||||
rgba(0, 0, 0, 0.15) 3px
|
||||
);
|
||||
pointer-events: none;
|
||||
animation: scanline-drift 8s linear infinite;
|
||||
}
|
||||
```
|
||||
|
||||
**Phosphor Glow** (terminal text):
|
||||
```css
|
||||
.terminal-text {
|
||||
text-shadow:
|
||||
0 0 4px currentColor,
|
||||
0 0 8px currentColor,
|
||||
0 0 12px rgba(0, 255, 65, 0.3);
|
||||
}
|
||||
```
|
||||
|
||||
**ECG Neon Glow** (canvas — multi-layer):
|
||||
- Primary trace: 2px solid line
|
||||
- Glow layer 1: 6px @ 25% opacity + shadowBlur 14px
|
||||
- Glow layer 2: Inner 3px core for sharpness
|
||||
- Text: Same multi-layer glow approach
|
||||
|
||||
**Background Transition** (smooth RGB interpolation):
|
||||
```javascript
|
||||
const bgProgress = (elapsed - flatlineStartTime) / BG_TRANSITION
|
||||
ctx.fillStyle = `rgb(
|
||||
${Math.round(0 + (30 * bgProgress))},
|
||||
${Math.round(0 + (41 * bgProgress))},
|
||||
${Math.round(0 + (59 * bgProgress))}
|
||||
)` // black → #1E293B
|
||||
ctx.fillRect(0, 0, vw, vh)
|
||||
```
|
||||
@@ -24,9 +24,9 @@ Entries are stacked vertically, most recent at top. Each entry has a collapsed s
|
||||
```
|
||||
|
||||
- Date in Geist Mono, 13px, gray-500 (left-aligned)
|
||||
- Organization in Inter 400, 13px, NHS blue
|
||||
- Role title in Inter 600, 15px, gray-900
|
||||
- Key coded entry: a single-line summary of the most notable achievement, prefixed with "Key:" in Inter 500, gray-500
|
||||
- Organization in [UI font] 400, 13px, NHS blue
|
||||
- Role title in [UI font] 600, 15px, gray-900
|
||||
- Key coded entry: a single-line summary of the most notable achievement, prefixed with "Key:" in [UI font] 500, gray-500
|
||||
- Expand chevron button (right-aligned)
|
||||
- Status dot: green for current roles, gray for historical
|
||||
|
||||
@@ -78,7 +78,7 @@ This is a direct mapping from the clinical consultation format (SOAP notes) to c
|
||||
| **Examination** | Analysis / Findings | What Andy discovered, built, or analyzed — the technical and analytical work |
|
||||
| **Plan** | Outcomes / Delivery | What was achieved, what impact was measured, what's ongoing |
|
||||
|
||||
Section headers ("HISTORY", "EXAMINATION", "PLAN") are styled in Inter 600, 12px, uppercase, letter-spacing 0.05em, gray-400 — exactly like the section dividers in a clinical consultation record.
|
||||
Section headers ("HISTORY", "EXAMINATION", "PLAN") are styled in [UI font] 600, 12px, uppercase, letter-spacing 0.05em, gray-400 — styled like clinical consultation record section dividers.
|
||||
|
||||
## Coded Entries
|
||||
|
||||
@@ -110,15 +110,13 @@ This visual grouping helps the user quickly scan which organization each entry b
|
||||
|
||||
---
|
||||
|
||||
## Design Guidance (from /frontend-design)
|
||||
## Design Guidance
|
||||
|
||||
### Aesthetic Direction
|
||||
|
||||
**Utilitarian clinical information system.** Zero decorative flourish. Border-heavy, table-heavy, functional. Light-mode only. The design language of EMIS Web and SystmOne—systems that every NHS pharmacist interacts with daily.
|
||||
**Clinical Luxury.** The structure and conventions of a clinical consultation journal — reverse-chronological entries, H/E/P format, coded entries, traffic-light status indicators — executed with premium refinement. Refined shadows, generous spacing, considered typography. Light-mode only.
|
||||
|
||||
This is a **faithful digital clinical information system**—structured, database-driven, tabular, with the distinctive UI patterns of actual NHS clinical software: patient banner, tabbed clinical views, consultation history as a reverse-chronological journal, coded entries with SNOMED-style references, traffic-light status indicators, and action buttons styled as clinical system controls.
|
||||
|
||||
**What makes this unforgettable:** The History / Examination / Plan structure maps perfectly to Context / Analysis / Outcome. A clinician seeing this will immediately recognize the consultation journal format. A recruiter gets highly structured, scannable career data. The accordion behavior, coded entries, and status indicators all draw from real clinical software patterns.
|
||||
**What makes this unforgettable:** The History / Examination / Plan structure maps perfectly to Context / Analysis / Outcome. A clinician seeing this will immediately recognize the consultation journal format. A recruiter gets highly structured, scannable career data. The accordion behavior, coded entries, and status indicators draw from clinical software patterns — but the execution feels polished and premium, not institutional.
|
||||
|
||||
### Key Design Decisions
|
||||
|
||||
@@ -127,7 +125,7 @@ This is a **faithful digital clinical information system**—structured, databas
|
||||
- **History** → Context / Background (why the role existed, reporting lines)
|
||||
- **Examination** → Analysis / Findings (what was discovered, built, analyzed)
|
||||
- **Plan** → Outcomes / Delivery (what was achieved, impact measured)
|
||||
- Section headers styled as clinical system dividers: Inter 600, 12px, uppercase, letter-spacing 0.05em, gray-400
|
||||
- Section headers styled as clinical system dividers: [UI font] 600, 12px, uppercase, letter-spacing 0.05em, gray-400
|
||||
|
||||
2. **Height-Only Expand Animation (200ms)**
|
||||
- No opacity fade on content—content simply grows/shrinks
|
||||
@@ -140,7 +138,7 @@ This is a **faithful digital clinical information system**—structured, databas
|
||||
- Tesco PLC: Teal (`#00897B`)
|
||||
|
||||
4. **Typography System**
|
||||
- Inter for text content
|
||||
- [UI font] for text content (Elvaro or Blumir — see CLAUDE.md)
|
||||
- Geist Mono for dates, codes, timestamps
|
||||
- Border radius: 4px throughout
|
||||
- Borders: 1px solid #E5E7EB
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
# Reference: Visual Design System
|
||||
|
||||
> The SINGLE SOURCE OF TRUTH for colors, typography, spacing, surfaces, and motion throughout the Clinical Record PMR. Aligned with the **Clinical Luxury** direction defined in CLAUDE.md.
|
||||
> The SINGLE SOURCE OF TRUTH for colors, typography, spacing, surfaces, and motion throughout the Clinical Record PMR. Follows the **Clinical Luxury** direction in CLAUDE.md.
|
||||
|
||||
---
|
||||
|
||||
## Design Philosophy
|
||||
|
||||
This is a **premium portfolio** that uses the structure and metaphor of a GP clinical system — not a faithful NHS software clone. Real clinical systems (EMIS Web, SystmOne) are dense, border-heavy, and purely functional. We keep their *structure* (patient banner, sidebar navigation, record sections, tables, status indicators) but elevate the *execution* with refined typography, atmospheric depth, and considered whitespace.
|
||||
A **premium portfolio** that uses the structure and metaphor of a GP clinical system — not a faithful NHS software clone. Real clinical systems (EMIS Web, SystmOne) are dense, border-heavy, and purely functional. We keep their *structure* (patient banner, sidebar navigation, record sections, tables, status indicators) but elevate the *execution* with refined typography, atmospheric depth, and considered whitespace.
|
||||
|
||||
The goal is contrast: clinical precision married to luxury refinement. The "wow" comes from recognizing the clinical metaphor while being surprised by how good it looks.
|
||||
|
||||
@@ -17,9 +17,9 @@ The goal is contrast: clinical precision married to luxury refinement. The "wow"
|
||||
**Light-mode only.** The metaphor demands it — clinical systems operate under bright consulting room lights. No dark mode.
|
||||
|
||||
**Backgrounds:**
|
||||
- Main content area: `#F5F7FA` — cool light gray base. Add atmospheric depth: a very faint noise/grain texture overlay, or a subtle warm tint, so it feels like quality paper rather than a flat spreadsheet. The content surface should have *presence*.
|
||||
- Main content area: `#F5F7FA` — cool light gray base. Add atmospheric depth — a faint noise/grain texture overlay or a subtle warm tint — so the surface feels like quality paper, not a flat spreadsheet.
|
||||
- Card/panel surfaces: `#FFFFFF` — clean white. Cards float above the content surface via layered shadows (see Surfaces section).
|
||||
- Sidebar: `#1E293B` — dark blue-gray. The gravitas anchor. This dark chrome is what makes it feel like "serious software."
|
||||
- Sidebar: `#1E293B` — dark blue-gray. The gravitas anchor — dark chrome that reads as serious software.
|
||||
- Patient banner: `#334155` — lighter blue-gray with white text. Subtle drop shadow below to separate from content.
|
||||
- Login screen background: `#1E293B` — same as sidebar. Carries through to PMR entrance seamlessly.
|
||||
|
||||
@@ -30,7 +30,7 @@ The goal is contrast: clinical precision married to luxury refinement. The "wow"
|
||||
- On dark surfaces: `#FFFFFF` (white primary), `#94A3B8` (slate-400 secondary)
|
||||
|
||||
**Accent and status colors:**
|
||||
- **NHS Blue `#005EB8`** — THE accent color. Buttons, active nav states, links, interactive elements. This is the actual NHS brand blue — it will be instantly recognized and is the strongest signal of the clinical metaphor. Use it confidently but not everywhere.
|
||||
- **NHS Blue `#005EB8`** — THE accent color. Buttons, active nav states, links, interactive elements. The actual NHS brand blue — instantly recognizable, the strongest signal of the clinical metaphor. Use it confidently but not everywhere.
|
||||
- Green `#22C55E` — active/resolved/current states. Status dots, current role indicators.
|
||||
- Amber `#F59E0B` — alerts, in-progress items. The clinical alert banner background.
|
||||
- Red `#EF4444` — urgent/critical. Used very sparingly — only genuinely important items.
|
||||
@@ -47,12 +47,7 @@ The goal is contrast: clinical precision married to luxury refinement. The "wow"
|
||||
|
||||
## Typography
|
||||
|
||||
Typography is the primary vehicle for the premium feel. The font choice must feel *designed* — intentional and distinctive — while still reading cleanly at small clinical-system sizes (11-14px).
|
||||
|
||||
**Font selection:**
|
||||
- **UI / Body font**: Choose a distinctive geometric or humanist sans-serif with character. **Do not use** Inter, Roboto, Arial, or system-ui defaults — these read as generic/AI-generated. Candidates: **Satoshi**, **General Sans**, **Outfit**, **DM Sans**, or similar. The chosen font should have personality at 13px. Whichever is selected, configure it as the primary `font-family` across all UI elements.
|
||||
- **Monospace / Data font**: **Geist Mono** — for timestamps, coded entries, registration numbers, NHS numbers, tabular data values. This monospace texture is what sells the "clinical software" feel. Falls back to Fira Code.
|
||||
- **Terminal phase**: **Fira Code** — locked, do not change.
|
||||
See Claude.md for info on font choice. Typography carries the premium feel. The font choice must feel *designed* — intentional and distinctive — while reading cleanly at small clinical-system sizes (11-14px).
|
||||
|
||||
**Type scale (tight, clinical):**
|
||||
- Patient banner name: [UI font] 600, 20px
|
||||
@@ -69,7 +64,7 @@ Typography is the primary vehicle for the premium feel. The font choice must fee
|
||||
- Timestamps: Geist Mono 400, 11-12px
|
||||
- Alert banner text: [UI font] 500, 14px
|
||||
|
||||
**Hierarchy through weight, not size.** Use 400/500/600/700 weight variations within a narrow size range. Bold section headers, medium labels, regular body. This keeps the clinical density while creating clear scannable hierarchy.
|
||||
**Hierarchy through weight, not size.** Use 400/500/600/700 weight variations within a narrow size range. Bold section headers, medium labels, regular body. This keeps the clinical density while creating clear, scannable hierarchy.
|
||||
|
||||
---
|
||||
|
||||
@@ -85,27 +80,27 @@ More generous than real clinical software. The clinical metaphor provides struct
|
||||
- **Border radius:** 4px default for cards, inputs, buttons (clinical precision). 12px exception for the login card only.
|
||||
- **Table row height:** 40px
|
||||
- **Section spacing:** 24px between content blocks
|
||||
- **Base unit:** 4px grid — but use it with more generosity than a real clinical system would
|
||||
- **Base unit:** 4px grid — applied more generously than in real clinical systems
|
||||
|
||||
---
|
||||
|
||||
## Surfaces & Depth
|
||||
|
||||
This is where we diverge most from real clinical software. Real systems are flat and border-heavy. This project uses **shadows and layering** to create premium depth — while keeping borders where they're authentically clinical (tables, input fields).
|
||||
Our biggest departure from real clinical software. Real systems are flat and border-heavy; we use **shadows and layering** for depth — while keeping borders where they're authentically clinical (tables, input fields).
|
||||
|
||||
**Cards:**
|
||||
- Border: `1px solid #E5E7EB` (keep the clinical border — it's authentic)
|
||||
- Shadow: Multi-layered — `0 1px 2px rgba(0,0,0,0.04), 0 4px 12px rgba(0,0,0,0.03)`. Gentle float, not Material Design dramatic.
|
||||
- Border-radius: `4px`
|
||||
- Hover: Cards may lift very slightly — 1-2px translateY + shadow deepens to `0 2px 4px rgba(0,0,0,0.06), 0 8px 16px rgba(0,0,0,0.04)`. Restrained, not bouncy.
|
||||
- Card headers: Light gray `#F9FAFB` background with `1px solid #E5E7EB` bottom border. Uppercase title in [UI font] 600, 12-13px. This is the most "clinical" element — keep it precise.
|
||||
- Card headers: Light gray `#F9FAFB` background with `1px solid #E5E7EB` bottom border. Uppercase title in [UI font] 600, 12-13px. The most "clinical" element — keep it precise.
|
||||
|
||||
**Tables:**
|
||||
- Full `<table>` markup with styled headers — this is where clinical authenticity lives.
|
||||
- Full `<table>` markup with styled headers — where clinical authenticity lives.
|
||||
- Table headers: `#F9FAFB` background, `1px solid #E5E7EB` borders.
|
||||
- Alternating rows: `#FFFFFF` / `#F9FAFB` — subtle but scannable.
|
||||
- Row hover: `#EFF6FF` background (blue tint).
|
||||
- Cell borders: `1px solid #E5E7EB` — keep full borders on tables. This is authentic.
|
||||
- Cell borders: `1px solid #E5E7EB` — keep full borders on tables. Authentic.
|
||||
|
||||
**Sidebar:**
|
||||
- Background: `#1E293B`
|
||||
@@ -131,21 +126,21 @@ Motion should feel **considered and premium** — never flashy, never gratuitous
|
||||
- Patient banner slides down: 200ms, ease-out
|
||||
- Sidebar slides from left: 250ms, ease-out, 50ms delay
|
||||
- Content fades in: 300ms, 100ms delay after sidebar
|
||||
- This staggered materialization is the single most impactful animation moment.
|
||||
- The staggered materialization — the most impactful animation.
|
||||
|
||||
**Navigation switches:** Instant content swap. No crossfade, no slide. This preserves the "software application" feel — clinical systems switch tabs instantly.
|
||||
|
||||
**Expandable content:** Height-only animation, 200ms, `ease-out`. Content grows/shrinks — no opacity fade.
|
||||
|
||||
**Clinical alert entrance:** Spring animation (Framer Motion `type: "spring"`, moderate damping). This is the one element that *demands attention* — the spring overshoot is earned here.
|
||||
**Clinical alert entrance:** Spring animation (Framer Motion `type: "spring"`, moderate damping). The one element that *demands attention* — the spring overshoot is earned here.
|
||||
|
||||
**Alert acknowledge:** Warning icon cross-fades to green checkmark (200ms) → hold 200ms → alert height collapses (200ms ease-out).
|
||||
|
||||
**Hover states:** Subtle and immediate. Background-color transitions at 100ms. Card lifts are 1-2px max with shadow deepening. Think: OS-level responsiveness, not playful bouncing.
|
||||
|
||||
**Login typing:** Character-by-character reveal: 30ms/char for username, 20ms/dot for password. Cursor blink at 530ms.
|
||||
**Login typing:** Character-by-character reveal at a natural pace: 80ms/char for username, 60ms/dot for password. Cursor blink at 530ms. After typing completes, "Log In" button becomes interactive — user clicks to proceed (not auto-triggered).
|
||||
|
||||
**Patient banner condensation:** Smooth height transition (200ms) from 80px → 48px as user scrolls past 100px. Buttery smooth, no jank.
|
||||
**Patient banner condensation:** Height transition (200ms) from 80px → 48px as user scrolls past 100px. Buttery smooth, no jank.
|
||||
|
||||
**`prefers-reduced-motion`:** All animations skip to final state instantly. Typing completes immediately. Alert appears without slide. Expand/collapse is instant. No exceptions.
|
||||
|
||||
@@ -153,7 +148,7 @@ Motion should feel **considered and premium** — never flashy, never gratuitous
|
||||
|
||||
## What Makes This Design Distinctive
|
||||
|
||||
The memorability comes from **contrasts**:
|
||||
The design stands on **contrasts**:
|
||||
- Dark, serious sidebar next to warm, airy content
|
||||
- Small, precise monospace data in generous whitespace fields
|
||||
- NHS blue punching through an otherwise muted, restrained palette
|
||||
|
||||
@@ -49,13 +49,14 @@
|
||||
- On a consultation entry: "Expand", "Copy to clipboard", "View coded entries"
|
||||
- On a medication row: "View prescribing history", "Copy to clipboard"
|
||||
- On a problem entry: "View linked consultations", "Copy to clipboard"
|
||||
- Context menus styled: white background, `1px solid #E5E7EB` border, 4px radius, `box-shadow: 0 4px 12px rgba(0,0,0,0.1)`. Items in Inter 400, 14px, 36px row height.
|
||||
- Context menus styled: white background, `1px solid #E5E7EB` border, 4px radius, `box-shadow: 0 4px 12px rgba(0,0,0,0.1)`. Items in [UI font] 400, 14px, 36px row height.
|
||||
|
||||
### Login Screen Typing
|
||||
- The username types character-by-character (30ms per character).
|
||||
- The password dots appear faster (20ms per dot).
|
||||
- The username types character-by-character at a natural reading pace (80ms per character).
|
||||
- The password dots appear at a deliberate pace (60ms per dot).
|
||||
- A blinking cursor appears in the active field (530ms blink interval).
|
||||
- The "Log In" button shows a brief active/pressed state before the interface materializes.
|
||||
- After typing completes, the "Log In" button becomes clearly interactive (full opacity, hover state). The user clicks it to proceed — this is NOT auto-triggered.
|
||||
- On click, the button shows a brief pressed state before the interface materializes.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -109,13 +109,13 @@ The preview panel uses the same tree-indented structure as the Investigations ex
|
||||
|
||||
---
|
||||
|
||||
## Design Guidance (from /frontend-design)
|
||||
## Design Guidance
|
||||
|
||||
### Aesthetic Direction
|
||||
|
||||
**Tone:** Clinical utilitarian — faithful reproduction of NHS clinical software (EMIS Web / SystmOne). Zero decorative flourish. Borders as the dominant structuring element. Dense, scannable, table-first. Light-mode only. The visual language is institutional and familiar to any NHS clinician.
|
||||
**Tone:** Clinical Luxury — the *structure* of clinical investigation results and attached documents (tables, status badges, expandable rows) executed with premium refinement. Borders provide authentic clinical structuring; layered shadows, generous spacing, and refined typography provide the luxury finish. Light-mode only.
|
||||
|
||||
**Differentiation:** The expanded-row tree-indented monospace structure using box-drawing characters is the signature element. It transforms a flat data table into something that reads like a lab report or radiology result — structured, indented, with labelled fields in `Geist Mono`. The pipe-and-branch characters (`├─`, `│`, `└─`) create a distinctly clinical-system aesthetic that no standard portfolio site would ever use.
|
||||
**Differentiation:** The expanded-row tree-indented monospace structure using box-drawing characters is the signature element. It transforms a flat data table into something that reads like a lab report or radiology result — structured, indented, with labelled fields in `Geist Mono`. The pipe-and-branch characters (`├─`, `│`, `└─`) create a distinctly clinical aesthetic that no standard portfolio site would ever use.
|
||||
|
||||
### Key Design Decisions
|
||||
|
||||
@@ -136,11 +136,12 @@ Both views share an identical expand/collapse mechanic:
|
||||
|
||||
#### Typography & Spacing
|
||||
|
||||
- **Primary font:** Inter (text, labels, table headers)
|
||||
- **Primary font:** [UI font] (text, labels, table headers — Elvaro or Blumir, see CLAUDE.md)
|
||||
- **Monospace font:** Geist Mono (tree-indented expanded content)
|
||||
- **Border radius:** 4px throughout
|
||||
- **Border color:** `#E5E7EB` (Tailwind gray-200)
|
||||
- **NHS Blue:** `#005EB8` (action buttons, links)
|
||||
- **Card shadow:** Multi-layered per design system
|
||||
|
||||
### Implementation Patterns
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@ Skills are grouped into three "medication types," mimicking how clinical systems
|
||||
|
||||
## Table Styling
|
||||
|
||||
- Table headers: Inter 600, 13px, uppercase, gray-400, `#F9FAFB` background
|
||||
- Table headers: [UI font] 600, 13px, uppercase, gray-400, `#F9FAFB` background
|
||||
- Table rows: alternating `#FFFFFF` / `#F9FAFB` backgrounds
|
||||
- Row height: 40px
|
||||
- All borders: `1px solid #E5E7EB`
|
||||
@@ -94,44 +94,44 @@ Table columns are sortable by clicking the header. Clicking "Dose" sorts by prof
|
||||
|
||||
---
|
||||
|
||||
## Design Guidance (from /frontend-design)
|
||||
## Design Guidance
|
||||
|
||||
### Aesthetic Direction
|
||||
|
||||
**Clinical-Utilitarian / NHS PMR Fidelity**
|
||||
**Clinical Luxury**
|
||||
|
||||
This implementation follows the Clinical Record (Design 7) medications-as-skills metaphor with absolute fidelity to the specification. The aesthetic is clinical-utilitarian: light-mode only, border-heavy, table-driven, zero decorative flourish. Every design decision mirrors real NHS PMR systems (EMIS Web, SystmOne). The component is not themed loosely -- it is a faithful reproduction of how medications lists appear in actual GP clinical software, repurposed to present skills as active prescriptions.
|
||||
The medications-as-skills metaphor uses the *structure* of an active medications list — tabs, table layout, status indicators, prescribing history — but executed with premium refinement. Layered shadows on cards, generous spacing, refined typography. Light-mode only.
|
||||
|
||||
**Purpose:** Present 18 professional skills as an active medications list that clinicians will instantly recognize and recruiters will find navigable and information-dense.
|
||||
**Purpose:** Present 18 professional skills as an active medications list. Clinicians recognize the format; recruiters get navigable, information-dense content.
|
||||
|
||||
**Tone:** Institutional, functional, border-heavy. No shadows, no rounded corners beyond 4px, no gradients. Clinical systems are designed for rapid information retrieval under time pressure -- that same quality makes this an efficient skills display.
|
||||
**Tone:** Precise, structured, refined. Tables and borders provide clinical authenticity; shadows, typography, and spacing provide the luxury finish. Clinical systems are designed for rapid information retrieval under time pressure — that same quality makes this an efficient skills display.
|
||||
|
||||
**Constraints followed:**
|
||||
- Light-mode ONLY (clinical systems are light-mode by design)
|
||||
**Constraints:**
|
||||
- Light-mode ONLY
|
||||
- NHS blue `#005EB8` as the sole accent color
|
||||
- Border radius capped at 4px
|
||||
- Inter for all text, Geist Mono for prescribing history data
|
||||
- All borders `1px solid #E5E7EB`
|
||||
- No decorative elements whatsoever
|
||||
- Border radius 4px for clinical elements
|
||||
- [UI font] for all text (Elvaro or Blumir — see CLAUDE.md), Geist Mono for prescribing history data
|
||||
- Borders `1px solid #E5E7EB` on tables
|
||||
- Card surfaces with multi-layered shadows per design system
|
||||
|
||||
**Differentiation:** The medications-as-skills mapping provides richer data than any traditional "skills list." Dose maps to proficiency, Frequency maps to usage patterns, Start maps to when the skill was acquired, and prescribing history shows the skill's evolution over time. This is not decoration -- it is genuinely useful information architecture.
|
||||
**Differentiation:** The medications-as-skills mapping provides richer data than any traditional "skills list." Dose maps to proficiency, Frequency maps to usage patterns, Start maps to when the skill was acquired, and prescribing history shows the skill's evolution over time. Genuinely useful information architecture.
|
||||
|
||||
### Key Design Decisions
|
||||
|
||||
#### 1. Three Category Tabs
|
||||
- **"Active Medications"** (8 technical skills), **"Clinical Medications"** (6 healthcare domain skills), **"PRN (As Required)"** (4 strategic/leadership skills)
|
||||
- Active tab: white background + NHS blue (`#005EB8`) 2px bottom border
|
||||
- Inactive tabs: `#F9FAFB` background, gray text, hover brightens
|
||||
- Inactive tabs: `#F9FAFB` background, gray text, hover brightens to white
|
||||
- Count badges show the number of items per category
|
||||
- Full ARIA `role="tablist"`, `role="tab"`, `aria-selected`, `aria-controls` semantics
|
||||
|
||||
#### 2. Semantic HTML Table
|
||||
- Proper `<table>`, `<thead>`, `<th scope="col">`, `<tbody>`, `<tr>`, `<td>` markup
|
||||
- Five columns: Drug Name, Dose, Frequency, Start, Status
|
||||
- Headers: Inter 600, 13px, uppercase, 0.03em tracking, `#F9FAFB` background
|
||||
- Headers: [UI font] 600, 13px, uppercase, 0.03em tracking, `#F9FAFB` background
|
||||
- Row height: 40px
|
||||
- Alternating `#FFFFFF` / `#F9FAFB` row backgrounds via CSS `:nth-child(even)`
|
||||
- Hover state: `#EFF6FF` (subtle blue tint) -- **no transform, no lift, no shadow**
|
||||
- Hover state: `#EFF6FF` (subtle blue tint)
|
||||
- Status dots: 6px green circles inline with "Active" text
|
||||
- All borders: `1px solid #E5E7EB`
|
||||
|
||||
|
||||
@@ -61,13 +61,13 @@ Traffic lights are 8px circles with the status colors (green, amber, red, gray).
|
||||
|
||||
---
|
||||
|
||||
## Design Guidance (from /frontend-design)
|
||||
## Design Guidance
|
||||
|
||||
### Aesthetic Direction
|
||||
|
||||
**Clinical Utilitarian** — This is not a place for flashy gradients or playful animation. The metaphor demands the disciplined, functional aesthetic of an actual NHS clinical system. Think EMIS/SystmOne: white backgrounds, precise table layouts, 1px borders, quiet colour discipline. The visual power comes from the *content structure*, not decoration. The traffic light dots and expandable narratives do all the heavy lifting. Every pixel serves a clinical purpose.
|
||||
**Clinical Luxury** — The Problems view uses the clinical structure of a problem list (traffic lights, coded entries, expandable narratives) but executes with premium refinement. White card surfaces with layered shadows, generous padding, refined typography. The visual power comes from the *content structure* — traffic light dots and expandable narratives do the heavy lifting — while the luxury finish makes it feel polished and intentional.
|
||||
|
||||
The distinctiveness comes from the *concept itself* — framing career achievements as a Problem List is the creative act. The execution must be restrained to sell the metaphor.
|
||||
The distinctiveness comes from the *concept itself* — framing career achievements as a Problem List is the creative act. The premium execution makes it memorable.
|
||||
|
||||
### Key Design Decisions
|
||||
|
||||
@@ -78,18 +78,18 @@ The distinctiveness comes from the *concept itself* — framing career achieveme
|
||||
- Implementation uses flexbox with gap-2 for dot-label pairing
|
||||
|
||||
2. **Typography System**
|
||||
- **Inter** for all body text, headers, and UI labels — clean, clinical, readable
|
||||
- **[UI font]** for all body text, headers, and UI labels (Elvaro or Blumir — see CLAUDE.md)
|
||||
- **Geist Mono** for codes and dates — SNOMED-style codes like `[EFF001]`, `[MGT001]` must be monospace
|
||||
- Font sizes: 13px for table headers (uppercase, tracking-wider), 14px for body text
|
||||
- Header styling: `font-inter font-semibold text-xs uppercase tracking-wider text-gray-400`
|
||||
- Header styling: `font-ui font-semibold text-xs uppercase tracking-wider text-gray-400`
|
||||
|
||||
3. **Color Palette (Locked)**
|
||||
- Light-mode ONLY (clinical systems are light-mode by design)
|
||||
- Light-mode ONLY
|
||||
- NHS Blue: `#005EB8` (Tailwind `text-pmr-nhsblue`) — used for links and accents
|
||||
- Borders: `1px solid #E5E7EB` (gray-200) — consistent table borders
|
||||
- Row hover: `#EFF6FF` (blue-50) — subtle clinical highlight
|
||||
- Background: White cards on `#F5F7FA` (pmr-content) background
|
||||
- Border radius: 4px max — clinical systems use sharp corners
|
||||
- Row hover: `#EFF6FF` (blue-50) — subtle highlight
|
||||
- Background: White cards on `#F5F7FA` (pmr-content) background with layered shadows per design system
|
||||
- Border radius: 4px for clinical elements
|
||||
|
||||
4. **Table Structure**
|
||||
- Semantic HTML: `<table>`, `<thead>`, `<th scope="col">`, `<tbody>`, `<tr>`, `<td>`
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
---
|
||||
|
||||
## Design Guidance (from /frontend-design)
|
||||
## Design Guidance
|
||||
|
||||
### Aesthetic Direction
|
||||
|
||||
**Tone: Clinical Utilitarian** — This is an NHS-styled patient medical record system. The aesthetic is intentionally institutional: clean, functional, bureaucratic-in-a-charming-way. The humor comes from the deadpan application of clinical form conventions to a personal contact form. No flourishes, no gradients, no decorative frills. The beauty is in the precision of the grid, the crispness of the type hierarchy, and the tongue-in-cheek seriousness of it all.
|
||||
**Tone: Clinical Luxury** — A contact form styled as a clinical referral, with the *structure* of an NHS referral form (priority levels, reference numbers, clinical fields) but executed with premium refinement. The humor comes from the deadpan application of clinical form conventions to a personal contact form. The beauty is in the precision of the grid, the crispness of the type hierarchy, refined inputs, and the tongue-in-cheek seriousness of it all.
|
||||
|
||||
**What makes it memorable**: The collision between NHS clinical form seriousness and the fact that you are "referring" to a person's contact page. The pre-filled "patient" header, the priority radio buttons with their wry tooltips, the reference number generation — all of this is a joke delivered with a completely straight face.
|
||||
**What makes it memorable**: The collision between clinical form structure and the fact that you are "referring" to a person's contact page. The pre-filled "patient" header, the priority radio buttons with their wry tooltips, the reference number generation — all of this is a joke delivered with a completely straight face, in a beautifully finished package.
|
||||
|
||||
### Key Design Decisions
|
||||
|
||||
@@ -37,7 +37,7 @@ Each priority option features a colored dot indicator and supports hover tooltip
|
||||
| Card border | `1px solid #E5E7EB` | `border-pmr-border` |
|
||||
| Input border | `1px solid #D1D5DB` | `border-pmr-border-dark` |
|
||||
| Border radius | `4px` | `rounded` |
|
||||
| Label font | Inter 500, 13px, gray-600 | `font-inter font-medium text-sm text-gray-600` |
|
||||
| Label font | [UI font] 500, 13px, gray-600 | `font-ui font-medium text-sm text-gray-600` |
|
||||
| Mono font | Geist Mono | `font-mono` (reference numbers) |
|
||||
| Input padding | `8px 12px` | `py-2 px-3` |
|
||||
| Focus state | NHS blue border + glow | `focus:border-pmr-nhsblue focus:ring-2 focus:ring-pmr-nhsblue/15` |
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
The landing view after login. This mimics the "Patient Summary" screen — the first screen a clinician sees when opening a patient record, showing the most important information at a glance.
|
||||
|
||||
**Layout:** A grid of summary cards arranged in a 2-column layout on desktop, single column on mobile. Each card has a header bar with the card title in Inter 600, 14px, uppercase, on a `#F9FAFB` background with `1px solid #E5E7EB` bottom border.
|
||||
**Layout:** A grid of summary cards arranged in a 2-column layout on desktop, single column on mobile. Each card has a header bar with the card title in [UI font] 600, 14px, uppercase, on a `#F9FAFB` background with `1px solid #E5E7EB` bottom border.
|
||||
|
||||
### Card 1: Patient Demographics (spans full width)
|
||||
|
||||
@@ -21,7 +21,7 @@ The landing view after login. This mimics the "Patient Summary" screen — the f
|
||||
+---------------------------------------------------------------------+
|
||||
```
|
||||
|
||||
A two-column key-value table. Labels in Inter 500, 13px, gray-500. Values in Inter 400, 14px, gray-900. Labels right-aligned, values left-aligned — mimicking clinical system demographics layout.
|
||||
A two-column key-value table. Labels in [UI font] 500, 13px, gray-500. Values in [UI font] 400, 14px, gray-900. Labels right-aligned, values left-aligned — clinical form layout.
|
||||
|
||||
### Card 2: Active Problems (left column)
|
||||
|
||||
@@ -34,7 +34,7 @@ A two-column key-value table. Labels in Inter 500, 13px, gray-500. Values in Int
|
||||
+---------------------------------------------------------------------+
|
||||
```
|
||||
|
||||
A list with green dots for active/current items, amber dots for in-progress items. Each entry has a title in Inter 500, 14px, and a date range or status in Geist Mono, 12px, right-aligned. Click an entry to navigate to the corresponding Consultation.
|
||||
A list with green dots for active/current items, amber dots for in-progress items. Each entry has a title in [UI font] 500, 14px, and a date range or status in Geist Mono, 12px, right-aligned. Click an entry to navigate to the corresponding Consultation.
|
||||
|
||||
### Card 3: Current Medications — Quick View (right column)
|
||||
|
||||
@@ -89,7 +89,7 @@ When the user first loads the Summary view (immediately after the login transiti
|
||||
- Background: amber (`#FEF3C7` — amber-100, light amber)
|
||||
- Left border: 4px solid `#F59E0B` (amber-500)
|
||||
- Warning icon: `AlertTriangle` from Lucide, amber-600
|
||||
- Text: Inter 500, 14px, `#92400E` (amber-800)
|
||||
- Text: [UI font] 500, 14px, `#92400E` (amber-800)
|
||||
- "Acknowledge" button: small outlined button, amber border and text
|
||||
|
||||
### Behavior
|
||||
@@ -117,19 +117,19 @@ This second alert reinforces the key technical achievement in clinical language.
|
||||
|
||||
---
|
||||
|
||||
## Design Guidance (from /frontend-design)
|
||||
## Design Guidance
|
||||
|
||||
### Aesthetic Direction
|
||||
|
||||
**Clinical Precision Meets Professional Polish**
|
||||
**Clinical Luxury**
|
||||
|
||||
The NHS clinical record aesthetic draws from real-world electronic patient record systems (EPR), balancing institutional gravitas with polished execution. Key visual principles:
|
||||
The Summary view and Clinical Alert use clinical structure (card-based summary, status dots, coded entries, alert banners) with premium execution. Key visual principles:
|
||||
|
||||
- **Light-mode ONLY** — Consistent with clinical systems that prioritize readability over dark aesthetics
|
||||
- **NHS blue (#005EB8)** — Institutional anchor color for headers and accents
|
||||
- **Card-based architecture** — All information lives in contained, bordered cards (1px solid #E5E7EB, 4px border-radius)
|
||||
- **Monospace for data** — Geist Mono for all coded entries, dates, and numerical values (creates clinical system authenticity)
|
||||
- **High information density** — Compact layouts that maximize data visibility (16px card padding, tight line-heights)
|
||||
- **Light-mode ONLY**
|
||||
- **NHS blue (#005EB8)** — The accent color for headers and accents
|
||||
- **Card-based architecture** — All information lives in contained, bordered cards with layered shadows (per design system)
|
||||
- **Monospace for data** — Geist Mono for all coded entries, dates, and numerical values (clinical texture)
|
||||
- **Generous but structured** — More whitespace than a real clinical system. Cards have 16-24px padding. Content breathes.
|
||||
- **Status dots** — Green/amber/red traffic light indicators for at-a-glance status assessment
|
||||
|
||||
### Key Design Decisions
|
||||
@@ -157,9 +157,9 @@ This sequence transforms dismissal from a jarring disappearance into a satisfyin
|
||||
|
||||
**3. Typography Hierarchy**
|
||||
|
||||
- **Card headers**: Inter 600, 14px, uppercase, letter-spacing-wide — creates clear section delineation
|
||||
- **Labels**: Inter 500, 13px, gray-500, right-aligned — mimics clinical form layout
|
||||
- **Values**: Inter 400, 14px, gray-900, left-aligned — primary data focus
|
||||
- **Card headers**: [UI font] 600, 14px, uppercase, letter-spacing-wide — creates clear section delineation
|
||||
- **Labels**: [UI font] 500, 13px, gray-500, right-aligned — clinical form layout
|
||||
- **Values**: [UI font] 400, 14px, gray-900, left-aligned — primary data focus
|
||||
- **Coded values**: Geist Mono, 12px — all dates, IDs, percentages, status codes
|
||||
|
||||
### Implementation Patterns
|
||||
|
||||
@@ -18,16 +18,16 @@ The flatline has a subtle audio-visual implication without actual sound — the
|
||||
|
||||
The entire canvas fades to black over 200ms (the name and flatline dissolve into darkness). Then, from black, the background transitions to a dark blue-gray (`#1E293B`) over 200ms. This is the color of a clinical system login screen — the dark institutional background that every NHS worker recognizes from their Monday morning.
|
||||
|
||||
## Phase 3: Login Sequence (1200ms)
|
||||
## Phase 3: Login Sequence (user-paced)
|
||||
|
||||
A login panel materializes center-screen: a white card (320px wide, 12px border-radius, subtle shadow) on the dark blue-gray background. The card contains:
|
||||
A login panel materializes center-screen: a white card (320px wide, 12px border-radius, refined shadow) on the dark blue-gray background. The card contains:
|
||||
|
||||
- A small NHS-blue shield icon or generic clinical system logo at the top
|
||||
- **Username field**: Empty text input with label "Username". After 200ms, a cursor appears and types `A.CHARLWOOD` character by character (30ms per character, ~350ms total). The typing uses Geist Mono / monospace font.
|
||||
- **Password field**: After a 150ms pause, dots fill the password field in rapid succession (8 dots, 20ms each, ~160ms total).
|
||||
- **"Log In" button**: NHS blue (`#005EB8`), full width. After another 150ms pause, the button receives a subtle pressed state (darkens slightly, 100ms) as if clicked.
|
||||
- **Username field**: Empty text input with label "Username". After 400ms, a cursor appears and types `A.CHARLWOOD` character by character at a natural reading pace (80ms per character, ~880ms total). The typing uses Geist Mono / monospace font.
|
||||
- **Password field**: After a 300ms pause, dots fill the password field at a deliberate pace (8 dots, 60ms each, ~480ms total).
|
||||
- **"Log In" button**: NHS blue (`#005EB8`), full width. After typing completes, the button becomes clearly available as a **user-interactive element**. The user clicks it to proceed. The button should have a visible hover state and feel like a natural call-to-action — this is the moment where the user "logs in" to the record.
|
||||
|
||||
The login card holds for 200ms in its "submitted" state, then...
|
||||
**Important**: The login button is NOT auto-clicked. The user must click it. This creates a deliberate, satisfying interaction — the user is choosing to enter the record. On click, the button shows a brief pressed state (darkens slightly, 100ms), then...
|
||||
|
||||
## Phase 4: Interface Materialization (500ms)
|
||||
|
||||
@@ -42,42 +42,41 @@ The login card scales up slightly (103%) and fades out (200ms). As it fades, the
|
||||
|
||||
The full PMR interface is visible: patient banner at top, dark sidebar on left, Summary view in the main content area, and the clinical alert banner demanding attention. The user is now "logged in" to Andy's career record.
|
||||
|
||||
**Total transition duration:** ~2.7 seconds
|
||||
**Total transition duration:** ~2s for typing to complete, then user-paced (waits for button click), then ~500ms for interface materialization.
|
||||
|
||||
## Why This Works
|
||||
|
||||
The login sequence is the most immersive transition of all designs. Every NHS worker, every pharmacist, every GP has typed their credentials into a clinical system at 8am on a Monday. This transition puts them right there. It's specific, it's authentic, and it immediately establishes the metaphor: you are opening a patient record. The "patient" happens to be a career.
|
||||
The login sequence is the most immersive transition. Every NHS worker, every pharmacist, every GP recognizes the shape of a clinical login screen. This transition evokes that recognition — but executed with premium refinement rather than institutional austerity. The natural typing pace lets the user absorb what's happening. And the interactive login button is the pivotal moment: the user *chooses* to enter the record. That moment of agency makes the experience feel personal, not passive.
|
||||
|
||||
## Login Animation Implementation Notes
|
||||
|
||||
- Component mounts with dark blue-gray background
|
||||
- Login card fades in (Framer Motion, 200ms)
|
||||
- Username typing: `setInterval` adds one character per 30ms to a state string
|
||||
- Password dots: `setInterval` adds one dot per 20ms
|
||||
- Button press: state change triggers visual pressed state, then 200ms delay
|
||||
- `onComplete` callback fires, parent component swaps to PMRInterface
|
||||
- Typing respects `prefers-reduced-motion` — with reduced motion, full username appears instantly and login completes in ~500ms total
|
||||
- Username typing: `setInterval` adds one character per 80ms to a state string (~880ms total)
|
||||
- Password dots: `setInterval` adds one dot per 60ms (~480ms total)
|
||||
- After typing completes: button becomes interactive (opacity goes to 1, cursor: pointer)
|
||||
- **User clicks the "Log In" button** — this is NOT auto-triggered
|
||||
- On click: button shows pressed state (100ms), then `onComplete` callback fires
|
||||
- Typing respects `prefers-reduced-motion` — with reduced motion, full username and password appear instantly, button is immediately interactive
|
||||
- **Font: Geist Mono** for username/password fields (NOT Fira Code)
|
||||
|
||||
---
|
||||
|
||||
## Design Guidance (from /frontend-design)
|
||||
## Design Guidance
|
||||
|
||||
> Pre-baked design direction. Do NOT invoke `/frontend-design` at runtime — this section contains the output.
|
||||
### Aesthetic Direction: Clinical Luxury
|
||||
|
||||
### Aesthetic Direction: Institutional Utilitarian
|
||||
|
||||
This is not "exciting" design — it is the visual equivalent of fluorescent lights, laminate desks, and the smell of hand sanitiser at 07:58 on a Monday morning. The card must look like every single hospital login prompt a doctor has ever seen: clean white, unadorned, functional. No personality. The branding is the only concession to identity. The magic is not visual flair — it is the uncanny recognition of "oh, this is exactly what that looks like" combined with the satisfying typewriter rhythm of credentials appearing.
|
||||
The login card evokes the structure of a clinical system login — shield icon, two fields, a button — but executed with premium refinement. Clean white card with refined shadow, considered spacing, and the satisfying rhythm of credentials appearing at a natural pace. The recognition factor ("oh, this looks like a clinical login") is the creative hook; the premium finish is what makes it memorable.
|
||||
|
||||
### Key Design Decisions
|
||||
|
||||
1. **Active field focus ring**: NHS-blue border (`1px solid #005EB8`) on the currently active field, inactive fields shift to `#FAFAFA` background. Mirrors real NHS login forms (Lorenzo, SystmOne, EMIS Web). Transition 150ms.
|
||||
2. **Reduced shadow to spec**: Use exactly `0 1px 2px rgba(0,0,0,0.03)`. Card sits on dark background through border definition, not shadow depth — more faithful to real NHS software.
|
||||
3. **Border**: Use `1px solid #E5E7EB` per design system (not `rgba(255,255,255,0.1)`).
|
||||
4. **Timer cleanup**: Track every `setInterval` and `setTimeout` via refs, clear all on unmount.
|
||||
5. **Consolidated active field state**: Single `activeField` state (`'username' | 'password' | null`) instead of separate booleans.
|
||||
6. **Accessibility**: `role="status"` + `aria-label` on outer container. Cursor pipes `aria-hidden="true"`. Card entrance `scale: 0.98` (not 0).
|
||||
7. **The Monday-morning feeling**: No gradients, no decorative elements, no loading spinners, no "Welcome back!" messaging. Just white rectangle on gray, shield icon, two fields, button. Typing speed deliberately mechanical.
|
||||
1. **Active field focus ring**: NHS-blue border (`1px solid #005EB8`) on the currently active field, inactive fields shift to `#FAFAFA` background. Clinical login convention. Transition 150ms.
|
||||
2. **Refined card shadow**: Multi-layered shadow `0 1px 2px rgba(0,0,0,0.04), 0 4px 12px rgba(0,0,0,0.03)` — the card should feel like it floats above the dark background. Combined with `1px solid #E5E7EB` border.
|
||||
3. **Timer cleanup**: Track every `setInterval` and `setTimeout` via refs, clear all on unmount.
|
||||
4. **Consolidated active field state**: Single `activeField` state (`'username' | 'password' | 'done' | null`) instead of separate booleans. `'done'` state indicates typing is complete and button is ready.
|
||||
5. **Accessibility**: `role="status"` + `aria-label` on outer container. Cursor pipes `aria-hidden="true"`. Card entrance `scale: 0.98` (not 0).
|
||||
6. **User-initiated login**: After typing completes, the "Log In" button is clearly interactive. Hover state (slight darken), cursor: pointer, and the button should feel like an invitation to click. This is the one moment of user agency in the boot sequence — make it satisfying.
|
||||
7. **Natural typing pace**: 80ms/char for username, 60ms/dot for password. Deliberate and readable, not frantically fast.
|
||||
|
||||
### Implementation Pattern
|
||||
|
||||
@@ -94,9 +93,10 @@ interface LoginScreenProps {
|
||||
const [username, setUsername] = useState('')
|
||||
const [passwordDots, setPasswordDots] = useState(0)
|
||||
const [showCursor, setShowCursor] = useState(true)
|
||||
const [activeField, setActiveField] = useState<'username' | 'password' | null>('username')
|
||||
const [activeField, setActiveField] = useState<'username' | 'password' | 'done' | null>('username')
|
||||
const [buttonPressed, setButtonPressed] = useState(false)
|
||||
const [isExiting, setIsExiting] = useState(false)
|
||||
const [typingComplete, setTypingComplete] = useState(false)
|
||||
|
||||
const fullUsername = 'A.CHARLWOOD'
|
||||
const passwordLength = 8
|
||||
@@ -111,7 +111,7 @@ Card structure:
|
||||
padding: '32px',
|
||||
borderRadius: '12px',
|
||||
border: '1px solid #E5E7EB',
|
||||
boxShadow: '0 1px 2px rgba(0, 0, 0, 0.03)',
|
||||
boxShadow: '0 1px 2px rgba(0,0,0,0.04), 0 4px 12px rgba(0,0,0,0.03)',
|
||||
}}
|
||||
initial={{ opacity: 0, scale: 0.98 }}
|
||||
animate={isExiting ? { scale: 1.03, opacity: 0 } : { scale: 1, opacity: 1 }}
|
||||
@@ -131,12 +131,12 @@ Branding header:
|
||||
<Shield size={26} style={{ color: '#005EB8' }} strokeWidth={2.5} />
|
||||
</div>
|
||||
<span style={{
|
||||
fontFamily: "'Inter', system-ui, sans-serif",
|
||||
fontFamily: "'[UI font]', system-ui, sans-serif",
|
||||
fontSize: '13px', fontWeight: 600,
|
||||
color: '#64748B', letterSpacing: '0.01em',
|
||||
}}>CareerRecord PMR</span>
|
||||
<span style={{
|
||||
fontFamily: "'Inter', system-ui, sans-serif",
|
||||
fontFamily: "'[UI font]', system-ui, sans-serif",
|
||||
fontSize: '11px', fontWeight: 400,
|
||||
color: '#94A3B8', marginTop: '2px',
|
||||
}}>Clinical Information System</span>
|
||||
@@ -165,18 +165,25 @@ Input field pattern (username example):
|
||||
</div>
|
||||
```
|
||||
|
||||
Login button:
|
||||
Login button (interactive — user clicks to proceed):
|
||||
```tsx
|
||||
<button style={{
|
||||
width: '100%',
|
||||
padding: '10px 16px',
|
||||
fontFamily: "'Inter', system-ui, sans-serif",
|
||||
fontSize: '14px', fontWeight: 600,
|
||||
color: '#FFFFFF',
|
||||
backgroundColor: buttonPressed ? '#004494' : '#005EB8',
|
||||
border: 'none',
|
||||
borderRadius: '4px',
|
||||
}}>Log In</button>
|
||||
<button
|
||||
onClick={typingComplete ? handleLogin : undefined}
|
||||
disabled={!typingComplete}
|
||||
style={{
|
||||
width: '100%',
|
||||
padding: '10px 16px',
|
||||
fontFamily: "'[UI font]', system-ui, sans-serif",
|
||||
fontSize: '14px', fontWeight: 600,
|
||||
color: '#FFFFFF',
|
||||
backgroundColor: buttonPressed ? '#004494' : '#005EB8',
|
||||
border: 'none',
|
||||
borderRadius: '4px',
|
||||
cursor: typingComplete ? 'pointer' : 'default',
|
||||
opacity: typingComplete ? 1 : 0.6,
|
||||
transition: 'background-color 150ms, opacity 300ms',
|
||||
}}
|
||||
>Log In</button>
|
||||
```
|
||||
|
||||
Typing sequence (reduced motion branch):
|
||||
@@ -184,18 +191,21 @@ Typing sequence (reduced motion branch):
|
||||
if (prefersReducedMotion) {
|
||||
setUsername(fullUsername)
|
||||
setPasswordDots(passwordLength)
|
||||
setActiveField(null)
|
||||
setTimeout(() => { setButtonPressed(true); setTimeout(triggerComplete, 100) }, 300)
|
||||
setActiveField('done')
|
||||
setTypingComplete(true)
|
||||
// Button is immediately available for user to click
|
||||
return
|
||||
}
|
||||
// Normal: username at 30ms/char, 150ms pause, password at 20ms/dot, 150ms pause, button press
|
||||
// Normal: username at 80ms/char, 300ms pause, password at 60ms/dot
|
||||
// After typing completes: setTypingComplete(true), button becomes interactive
|
||||
// User clicks "Log In" to proceed — no auto-click
|
||||
```
|
||||
|
||||
Footer:
|
||||
```tsx
|
||||
<div style={{ marginTop: '22px', paddingTop: '18px', borderTop: '1px solid #E5E7EB' }}>
|
||||
<p style={{
|
||||
fontFamily: "'Inter', system-ui, sans-serif",
|
||||
fontFamily: "'[UI font]', system-ui, sans-serif",
|
||||
fontSize: '11px', color: '#94A3B8', textAlign: 'center',
|
||||
}}>Secure clinical system login</p>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user