diff --git a/.agent/skills/bencium-innovative-ux-designer/ACCESSIBILITY.md b/.agent/skills/bencium-innovative-ux-designer/ACCESSIBILITY.md new file mode 100644 index 0000000..d514f4e --- /dev/null +++ b/.agent/skills/bencium-innovative-ux-designer/ACCESSIBILITY.md @@ -0,0 +1,111 @@ +# Accessibility Essentials + +Accessibility enables creativity - it's a foundation, not a limitation. WCAG 2.1 AA compliance. + +## Core Principles (POUR) + +- **Perceivable**: Content must be perceivable (alt text, contrast, captions) +- **Operable**: UI must be keyboard/touch accessible +- **Understandable**: Clear, predictable behavior +- **Robust**: Works with assistive technologies + +## Contrast Requirements + +| Element | Minimum Ratio | +|---------|---------------| +| Normal text | 4.5:1 | +| Large text (18pt+) | 3:1 | +| UI components | 3:1 | + +**Tools**: Chrome DevTools Accessibility tab, WebAIM Contrast Checker + +## Keyboard Navigation + +```tsx +// All interactive elements need focus states + + +// Custom elements need tabindex and key handlers +
(e.key === 'Enter' || e.key === ' ') && handleClick()} +> + Custom Button +
+``` + +**Essentials:** +- Tab through entire interface +- Enter/Space activates elements +- Escape closes modals +- Visible focus indicators always + +## Essential ARIA + +```tsx +// Buttons without text + + +// Expandable elements + + +// Live regions for dynamic content +
{statusMessage}
+
{errorMessage}
+ +// Form errors + +{hasError && } +``` + +## Semantic HTML + +```tsx +// Use semantic elements, not divs +
+

...

+ + +// Heading hierarchy (never skip levels) +

Page Title

+

Section

+

Subsection

+``` + +## Touch Targets + +- Minimum **44x44px** for all interactive elements +- Adequate spacing between targets +- `touch-manipulation` CSS for responsive touch + +## Screen Reader Content + +```tsx +// Hidden but announced +Additional context + +// Skip link + + Skip to main content + +``` + +## Quick Checklist + +- [ ] Keyboard: Can tab through everything +- [ ] Focus: Visible focus indicators +- [ ] Contrast: 4.5:1 for text +- [ ] Alt text: All images have appropriate alt +- [ ] Headings: Logical h1-h6 hierarchy +- [ ] Forms: Labels associated with inputs +- [ ] Errors: Announced to screen readers +- [ ] Touch: 44px minimum targets + +## Resources + +- [WCAG 2.1 Quick Reference](https://www.w3.org/WAI/WCAG21/quickref/) +- [WebAIM Contrast Checker](https://webaim.org/resources/contrastchecker/) +- [ARIA Authoring Practices](https://www.w3.org/WAI/ARIA/apg/) diff --git a/.agent/skills/bencium-innovative-ux-designer/DESIGN-SYSTEM-TEMPLATE.md b/.agent/skills/bencium-innovative-ux-designer/DESIGN-SYSTEM-TEMPLATE.md new file mode 100644 index 0000000..e968748 --- /dev/null +++ b/.agent/skills/bencium-innovative-ux-designer/DESIGN-SYSTEM-TEMPLATE.md @@ -0,0 +1,577 @@ +# Design System Template + +Meta-framework for understanding what's fixed, project-specific, and adaptable in your design system. + +## Purpose + +This template helps you distinguish between: +- **Fixed Elements**: Universal rules that never change +- **Project-Specific Elements**: Filled in for each project based on brand +- **Adaptable Elements**: Context-dependent implementations + +--- + +## I. FIXED ELEMENTS + +These foundations remain consistent across all projects, regardless of brand or context. + +### 1. Spacing Scale + +**Fixed System:** +``` +4px, 8px, 12px, 16px, 24px, 32px, 48px, 64px, 96px +``` + +**Usage:** +- Margins, padding, gaps between elements +- Mathematical relationships ensure visual harmony +- Use multipliers of base unit (4px) + +**Why Fixed:** +Consistent spacing creates visual rhythm regardless of brand personality. + +### 2. Grid System + +**Fixed Structure:** +- **12-column grid** for most layouts (divisible by 2, 3, 4, 6) +- **16-column grid** for data-heavy interfaces +- **Gutters**: 16px (mobile), 24px (tablet), 32px (desktop) + +**Why Fixed:** +Grid provides structural order. Brand personality shows through color, typography, content—not grid structure. + +### 3. Accessibility Standards + +**Fixed Requirements:** +- **WCAG 2.1 AA** compliance minimum +- **Contrast**: 4.5:1 for normal text, 3:1 for large text +- **Touch targets**: Minimum 44×44px +- **Keyboard navigation**: All interactive elements accessible +- **Screen reader**: Semantic HTML, ARIA labels where needed + +**Why Fixed:** +Accessibility is not negotiable. It's a baseline requirement for ethical, legal, and usable products. + +### 4. Typography Hierarchy Logic + +**Fixed Structure:** +- **Mathematical scaling**: 1.25x (major third) or 1.333x (perfect fourth) +- **Hierarchy levels**: Display → H1 → H2 → H3 → Body → Small → Caption +- **Line height**: 1.5x for body text, 1.2-1.3x for headlines +- **Line length**: 45-75 characters optimal + +**Why Fixed:** +Mathematical relationships create predictable, harmonious hierarchy. Specific fonts change, but the logic doesn't. + +### 5. Component Architecture + +**Fixed Patterns:** +- **Button states**: Default, Hover, Active, Focus, Disabled +- **Form structure**: Label above input, error below, helper text optional +- **Modal pattern**: Overlay + centered content + close mechanism +- **Card structure**: Container → Header → Body → Footer (optional) + +**Why Fixed:** +Users expect consistent component behavior. Architecture is fixed; appearance is project-specific. + +### 6. Animation Timing Framework + +**Fixed Physics Profiles:** +- **Lightweight** (icons, chips): 150ms +- **Standard** (cards, panels): 300ms +- **Weighty** (modals, pages): 500ms + +**Fixed Easing:** +- **Ease-out**: Entrances (fast start, slow end) +- **Ease-in**: Exits (slow start, fast end) +- **Ease-in-out**: Transitions (smooth both ends) + +**Why Fixed:** +Natural physics feel consistent across brands. Duration and easing create that feeling. + +--- + +## II. PROJECT-SPECIFIC ELEMENTS + +Fill in these for each project based on brand personality and purpose. + +### 1. Brand Color System + +**Template Structure:** + +``` +NEUTRALS (4-5 colors): +- Background lightest: _______ (e.g., slate-50 or warm-white) +- Surface: _______ (e.g., slate-100) +- Border/divider: _______ (e.g., slate-300) +- Text secondary: _______ (e.g., slate-600) +- Text primary: _______ (e.g., slate-900) + +ACCENTS (1-3 colors): +- Primary (main CTA): _______ (e.g., teal-500) +- Secondary (alternative action): _______ (optional) +- Status colors: + - Success: _______ (green-ish) + - Warning: _______ (amber-ish) + - Error: _______ (red-ish) + - Info: _______ (blue-ish) +``` + +**Questions to Answer:** +- What emotion should the brand evoke? (Trust, excitement, calm, urgency) +- Warm or cool neutrals? +- Conservative or bold accents? + +**Examples:** + +**Project A: Fintech App** +``` +Neutrals: Cool greys (slate-50 → slate-900) +Primary: Deep blue (#0A2463) – trust, professionalism +Success: Muted green (#10B981) +Why: Financial products need trust, not playfulness +``` + +**Project B: Creative Community** +``` +Neutrals: Warm greys with beige undertones +Primary: Coral (#FF6B6B) – energy, creativity +Success: Teal (#06D6A0) – fresh, unexpected +Why: Creative spaces should feel inviting, not corporate +``` + +**Project C: Healthcare Platform** +``` +Neutrals: Pure greys (minimal color temperature) +Primary: Soft blue (#4A90E2) – calm, clinical +Success: Medical green (#38A169) +Why: Healthcare needs clarity and calm, not distraction +``` + +### 2. Typography Pairing + +**Template:** + +``` +HEADLINE FONT: _______ +- Weight: _______ (e.g., Bold 700) +- Use case: H1, H2, display text +- Personality: _______ (geometric/humanist/serif/etc.) + +BODY FONT: _______ +- Weight: _______ (e.g., Regular 400, Medium 500) +- Use case: Paragraphs, UI text +- Personality: _______ (neutral/readable/efficient) + +OPTIONAL ACCENT FONT: _______ +- Weight: _______ +- Use case: _______ (special headlines, callouts) +``` + +**Pairing Logic:** +- Serif + Sans-serif (classic, editorial) +- Geometric + Humanist (modern + warm) +- Display + System (distinctive + efficient) + +**Examples:** + +**Project A: Editorial Platform** +``` +Headline: Playfair Display (Serif, Bold 700) +Body: Inter (Sans-serif, Regular 400) +Why: Serif headlines = trustworthy, editorial feel +``` + +**Project B: Tech Startup** +``` +Headline: DM Sans (Sans-serif, Bold 700) +Body: DM Sans (Regular 400, Medium 500) +Why: Single-font system = modern, efficient, cohesive +``` + +**Project C: Luxury Brand** +``` +Headline: Cormorant Garamond (Serif, Light 300) +Body: Lato (Sans-serif, Regular 400) +Why: Elegant serif + readable sans = sophisticated +``` + +### 3. Tone of Voice + +**Template:** + +``` +BRAND PERSONALITY: +- Formal ↔ Casual: _______ (1-10 scale) +- Professional ↔ Friendly: _______ (1-10 scale) +- Serious ↔ Playful: _______ (1-10 scale) +- Authoritative ↔ Conversational: _______ (1-10 scale) + +MICROCOPY EXAMPLES: +- Button label (submit form): _______ +- Error message (invalid email): _______ +- Success message (saved): _______ +- Empty state: _______ + +ANIMATION PERSONALITY: +- Speed: _______ (quick/moderate/slow) +- Feel: _______ (precise/smooth/bouncy) +``` + +**Examples:** + +**Project A: Banking App** +``` +Personality: Formal (8), Professional (9), Serious (8) +Button: "Submit Application" +Error: "Email address format is invalid" +Success: "Application submitted successfully" +Animation: Quick (precise, efficient, no-nonsense) +``` + +**Project B: Social App** +``` +Personality: Casual (8), Friendly (9), Playful (7) +Button: "Let's go!" +Error: "Hmm, that email doesn't look right" +Success: "Nice! You're all set 🎉" +Animation: Moderate (smooth, friendly bounce) +``` + +### 4. Animation Speed & Feel + +**Template:** + +``` +SPEED PREFERENCE: +- UI interactions: _______ (100-150ms / 150-200ms / 200-300ms) +- State changes: _______ (200ms / 300ms / 400ms) +- Page transitions: _______ (300ms / 500ms / 700ms) + +ANIMATION STYLE: +- Easing preference: _______ (sharp / standard / bouncy) +- Movement type: _______ (minimal / smooth / expressive) +``` + +**Examples:** + +**Project A: Trading Platform** +``` +Speed: Fast (100ms UI, 200ms states, 300ms pages) +Style: Sharp easing, minimal movement +Why: Traders need speed, not distraction +``` + +**Project B: Wellness App** +``` +Speed: Slow (200ms UI, 400ms states, 500ms pages) +Style: Smooth easing, gentle movement +Why: Calm, relaxing experience matches brand +``` + +--- + +## III. ADAPTABLE ELEMENTS + +Context-dependent implementations that vary based on use case. + +### 1. Component Variations + +**Button Variants:** +- **Primary**: Full background color (high emphasis) +- **Secondary**: Outline only (medium emphasis) +- **Tertiary**: Text only (low emphasis) +- **Destructive**: Red-ish (danger actions) +- **Ghost**: Minimal (navigation, toolbars) + +**Adaptation Rules:** +- Primary: Main CTA, one per screen section +- Secondary: Alternative actions +- Tertiary: Less important actions, multiple allowed +- Use brand colors, but hierarchy logic is fixed + +### 2. Responsive Breakpoints + +**Fixed Ranges:** +- XS: 0-479px (small phones) +- SM: 480-767px (large phones) +- MD: 768-1023px (tablets) +- LG: 1024-1439px (laptops) +- XL: 1440px+ (desktop) + +**Adaptable Implementations:** + +**Simple Content Site:** +``` +XS-SM: Single column +MD: 2 columns +LG-XL: 3 columns max +Why: Content-focused, don't overwhelm +``` + +**Dashboard/Data App:** +``` +XS: Collapsed, cards stack +SM: Simplified sidebar +MD: Full sidebar + main content +LG-XL: Sidebar + main + right panel +Why: Data apps need more screen real estate +``` + +### 3. Dark Mode Palette + +**Adaptation Strategy:** + +Not a simple inversion. Dark mode needs adjusted contrast: + +**Light Mode:** +``` +Background: #FFFFFF (white) +Text: #0F172A (slate-900) → 21:1 contrast +``` + +**Dark Mode (Adapted):** +``` +Background: #0F172A (slate-900) +Text: #E2E8F0 (slate-200) → 15.8:1 contrast (still AA, but softer) +``` + +**Why Adapt:** +Pure white on pure black is too harsh. Dark mode needs slightly lower contrast for eye comfort. + +### 4. Loading States + +**Context-Dependent:** + +**Fast operations (<500ms):** +- No loading indicator (feels instant) + +**Medium operations (500ms-2s):** +- Spinner or skeleton screen + +**Long operations (>2s):** +- Progress bar with percentage +- Or: Skeleton + estimated time + +**Interactive Operations:** +- Button shows spinner inside (don't disable, show state) + +### 5. Error Handling Strategy + +**Context-Dependent:** + +**Form Errors:** +``` +Validate: On blur (after user leaves field) +Display: Inline below field +Recovery: Clear error on fix +``` + +**API Errors:** +``` +Transient (network): Show retry button +Permanent (404): Show helpful message + next steps +Critical (500): Contact support option +``` + +**Data Errors:** +``` +Missing: Show empty state with action +Corrupt: Show error boundary with reload +Invalid: Highlight + explain what's wrong +``` + +--- + +## DECISION TREE + +When implementing a feature, ask: + +### Is this... + +**FIXED?** +- Does it affect structure, accessibility, or universal UX? +- Examples: Spacing scale, grid, contrast ratios, component architecture +- **Action**: Use the fixed system, no variation + +**PROJECT-SPECIFIC?** +- Does it express brand personality or purpose? +- Examples: Colors, typography, tone of voice, animation feel +- **Action**: Fill in the template for this project + +**ADAPTABLE?** +- Does it depend on context, content, or use case? +- Examples: Component variants, responsive behavior, error handling +- **Action**: Choose appropriate variation based on context + +--- + +## EXAMPLE: Implementing a "Submit" Button + +### Fixed Elements (Always the same): +- Touch target: 44px minimum height +- Padding: 16px horizontal (from spacing scale) +- States: Default, Hover, Active, Focus, Disabled +- Animation: 150ms ease-out (lightweight profile) + +### Project-Specific (Filled per project): +- **Project A (Bank)**: Dark blue background, white text, "Submit Application" +- **Project B (Social)**: Coral background, white text, "Let's Go!" +- **Project C (Healthcare)**: Soft blue background, white text, "Continue" + +### Adaptable (Context-dependent): +- **Form context**: Primary button (full color) +- **Toolbar context**: Ghost button (text only) +- **Danger context**: Destructive variant (red-ish) + +--- + +## VALIDATION CHECKLIST + +Before finalizing a design, check: + +### Fixed Elements +- [ ] Uses spacing scale (4/8/12/16/24/32/48/64/96px) +- [ ] Follows grid system (12 or 16 columns) +- [ ] Meets WCAG AA contrast (4.5:1 normal, 3:1 large) +- [ ] Touch targets ≥ 44px +- [ ] Typography follows mathematical scale +- [ ] Components follow standard architecture + +### Project-Specific Elements +- [ ] Brand colors filled in and intentional +- [ ] Typography pairing chosen and justified +- [ ] Tone of voice defined and consistent +- [ ] Animation speed matches brand personality + +### Adaptable Elements +- [ ] Component variants appropriate for context +- [ ] Responsive behavior fits content type +- [ ] Loading states match operation duration +- [ ] Error handling fits error type + +--- + +## PROJECT KICKOFF TEMPLATE + +Use this to start a new project: + +``` +PROJECT NAME: _______________________ +PURPOSE: ____________________________ + +BRAND PERSONALITY: +- Primary emotion: _______ +- Warm or cool: _______ +- Formal or casual: _______ +- Conservative or bold: _______ + +COLORS (fill the template): +- Neutral base: _______ +- Primary accent: _______ +- Status colors: _______ / _______ / _______ + +TYPOGRAPHY (fill the template): +- Headline font: _______ +- Body font: _______ +- Pairing rationale: _______ + +TONE: +- Button labels style: _______ +- Error message style: _______ +- Success message style: _______ + +ANIMATION: +- Speed preference: _______ (fast/moderate/slow) +- Feel preference: _______ (sharp/smooth/bouncy) + +TARGET DEVICES: +- Primary: _______ (mobile/desktop/both) +- Secondary: _______ +``` + +--- + +## MAINTAINING CONSISTENCY + +### Documentation +- Keep this template updated as system evolves +- Document WHY choices were made, not just WHAT + +### Communication +- Share with designers: "Here's what varies vs. what's fixed" +- Share with developers: "Here are the design tokens" + +### Tooling +- Use CSS variables for project-specific values +- Use Tailwind config for spacing scale +- Use design tokens in Figma/Storybook + +### Reviews +- Audit: Does new work follow fixed elements? +- Validate: Are project-specific elements intentional? +- Question: Are adaptations justified by context? + +--- + +## EXAMPLES OF COMPLETE SYSTEMS + +### System A: B2B SaaS (Conservative) + +**Fixed**: Standard spacing, 12-col grid, WCAG AA, major third type scale +**Project-Specific**: +- Colors: Cool greys + corporate blue +- Typography: DM Sans (headlines + body) +- Tone: Professional, formal +- Animation: Quick, precise (150ms) +**Adaptable**: +- Dashboard gets multi-panel layout +- Forms are extensive (use progressive disclosure) +- Errors show detailed technical info + +### System B: Consumer Social App (Playful) + +**Fixed**: Same spacing/grid/accessibility/type logic +**Project-Specific**: +- Colors: Warm greys + vibrant coral +- Typography: Poppins (headlines) + Inter (body) +- Tone: Casual, friendly, playful +- Animation: Moderate, bouncy (200ms) +**Adaptable**: +- Mobile-first (most users on phones) +- Forms are minimal (progressive profiling) +- Errors are friendly, not technical + +### System C: Healthcare Platform (Clinical) + +**Fixed**: Same foundational structure +**Project-Specific**: +- Colors: Pure greys + medical blue +- Typography: System fonts (SF Pro / Segoe) +- Tone: Clear, authoritative, calm +- Animation: Slow, smooth (300ms) +**Adaptable**: +- Desktop-first (clinical use at workstations) +- Forms are complex (HIPAA compliance) +- Errors are precise with next steps + +--- + +## KEY TAKEAWAY + +**The system flexibility framework lets you:** +- Maintain consistency (fixed elements) +- Express brand personality (project-specific) +- Adapt to context (adaptable elements) + +**Without this framework:** +- Designers reinvent spacing every project +- Components feel inconsistent across products +- Brand personality overrides accessibility +- Context-blind implementations feel wrong + +**With this framework:** +- Speed: Start from proven foundations +- Consistency: Fixed elements guarantee it +- Flexibility: Express unique brand identity +- Context: Adapt without breaking system diff --git a/.agent/skills/bencium-innovative-ux-designer/MOTION-SPEC.md b/.agent/skills/bencium-innovative-ux-designer/MOTION-SPEC.md new file mode 100644 index 0000000..e37e363 --- /dev/null +++ b/.agent/skills/bencium-innovative-ux-designer/MOTION-SPEC.md @@ -0,0 +1,72 @@ +# Motion Specification + +Motion should surprise and delight while serving function. Animation is a creative tool. + +## Easing Curves + +| Easing | CSS | Use For | +|--------|-----|---------| +| **Ease-out** | `cubic-bezier(0.0, 0.0, 0.2, 1)` | Entrances, appearing | +| **Ease-in** | `cubic-bezier(0.4, 0.0, 1, 1)` | Exits, disappearing | +| **Ease-in-out** | `cubic-bezier(0.4, 0.0, 0.2, 1)` | State changes, transforms | +| **Spring** | `cubic-bezier(0.68, -0.55, 0.265, 1.55)` | Playful, attention-grabbing | +| **Linear** | `linear` | Spinners, continuous loops | + +## Duration by Element Weight + +| Weight | Duration | Examples | +|--------|----------|----------| +| **Lightweight** | 150ms | Icons, badges, chips | +| **Standard** | 300ms | Cards, panels, list items | +| **Weighty** | 500ms | Modals, page transitions | + +## Duration by Interaction + +| Interaction | Duration | +|-------------|----------| +| Button press | 100ms | +| Hover state | 150ms | +| Tooltip appear | 200ms | +| Tab switch | 250ms | +| Modal open | 300ms | +| Page transition | 400ms | + +## Common Patterns + +```tsx +// Hover transition (CSS) + +``` + +### Example 2: Typography Hierarchy + +**Question First:** +``` +For this content hierarchy, I'm thinking: +- Headline: Large, bold, attention-grabbing +- Subheading: Medium, regular weight +- Body: Smaller, optimized for reading + +Should we go bold and contemporary, or subtle and refined? +``` + +**Implementation After Approval:** +```tsx +
+

+ Headline Here +

+

+ Supporting subheading +

+

+ Body text optimized for readability with proper line height. +

+
+``` + +### Example 3: Unique Color Palette Suggestion + +**Presenting Options:** +``` +I'd like to suggest three color directions that break from typical SaaS patterns: + +1. **Warm Earth Tones** + - Base: Warm grey (#E8E2DC) + - Accent: Terracotta (#C86E4B) + - For: Organic, trustworthy feel + +2. **Cool Midnight** + - Base: Deep navy (#1A2332) + - Accent: Cyan (#4ECDC4) + - For: Modern, tech-forward feel + +3. **Soft Pastels** + - Base: Soft pink (#FFE5E5) + - Accent: Sage green (#9DB5A4) + - For: Calm, approachable feel + +Which direction feels right for your brand? +``` + +## Common Patterns to Avoid + +❌ **NEVER:** +- Use Inter, Roboto, Arial, Space Grotesk as primary fonts +- Use generic SaaS blue (#3B82F6) or purple gradients on white +- Copy Apple's design language or use glass morphism +- Create cookie-cutter layouts that look AI-generated +- Skip asking about context before designing +- Converge on common choices across generations (vary everything!) +- Use animations that delay user actions +- Create cluttered interfaces where elements compete + +✅ **ALWAYS:** +- Ask about purpose, tone, constraints, differentiation FIRST +- Then commit BOLDLY to a distinctive aesthetic direction +- Use unexpected, characterful typography choices +- Create atmosphere: shadows, gradients, textures, grain (when intentional) +- Dominant colors with sharp accents (not timid, evenly-distributed palettes) +- Provide immediate feedback for interactions +- Test with real devices +- Validate accessibility (it enables creativity, not limits it) +- Remember: Claude is capable of extraordinary creative work - don't hold back! + +## Version History + +- v2.0.0 (2025-11-22): Creative liberation update - bold aesthetics, shadows/gradients allowed, Design Thinking protocol +- v1.0.0 (2025-10-18): Initial release with comprehensive UI/UX design guidance + +## References + +For additional context, see: +- **Anthropic Frontend Aesthetics Cookbook**: https://github.com/anthropics/claude-cookbooks/blob/main/coding/prompting_for_frontend_aesthetics.ipynb +- WCAG 2.1 Guidelines: https://www.w3.org/WAI/WCAG21/quickref/ +- Google Fonts: https://fonts.google.com/ +- Tailwind CSS Docs: https://tailwindcss.com/docs +- Shadcn UI Components: https://ui.shadcn.com/ + +**Progressive Disclosure Files:** +- ACCESSIBILITY.md - Accessibility essentials (WCAG AA baseline) +- MOTION-SPEC.md - Animation timing and easing +- RESPONSIVE-DESIGN.md - Mobile-first breakpoints and patterns diff --git a/.agents/skills/bencium-innovative-ux-designer/ACCESSIBILITY.md b/.agents/skills/bencium-innovative-ux-designer/ACCESSIBILITY.md new file mode 100644 index 0000000..d514f4e --- /dev/null +++ b/.agents/skills/bencium-innovative-ux-designer/ACCESSIBILITY.md @@ -0,0 +1,111 @@ +# Accessibility Essentials + +Accessibility enables creativity - it's a foundation, not a limitation. WCAG 2.1 AA compliance. + +## Core Principles (POUR) + +- **Perceivable**: Content must be perceivable (alt text, contrast, captions) +- **Operable**: UI must be keyboard/touch accessible +- **Understandable**: Clear, predictable behavior +- **Robust**: Works with assistive technologies + +## Contrast Requirements + +| Element | Minimum Ratio | +|---------|---------------| +| Normal text | 4.5:1 | +| Large text (18pt+) | 3:1 | +| UI components | 3:1 | + +**Tools**: Chrome DevTools Accessibility tab, WebAIM Contrast Checker + +## Keyboard Navigation + +```tsx +// All interactive elements need focus states + + +// Custom elements need tabindex and key handlers +
(e.key === 'Enter' || e.key === ' ') && handleClick()} +> + Custom Button +
+``` + +**Essentials:** +- Tab through entire interface +- Enter/Space activates elements +- Escape closes modals +- Visible focus indicators always + +## Essential ARIA + +```tsx +// Buttons without text + + +// Expandable elements + + +// Live regions for dynamic content +
{statusMessage}
+
{errorMessage}
+ +// Form errors + +{hasError && } +``` + +## Semantic HTML + +```tsx +// Use semantic elements, not divs +
+

...

+ + +// Heading hierarchy (never skip levels) +

Page Title

+

Section

+

Subsection

+``` + +## Touch Targets + +- Minimum **44x44px** for all interactive elements +- Adequate spacing between targets +- `touch-manipulation` CSS for responsive touch + +## Screen Reader Content + +```tsx +// Hidden but announced +Additional context + +// Skip link + + Skip to main content + +``` + +## Quick Checklist + +- [ ] Keyboard: Can tab through everything +- [ ] Focus: Visible focus indicators +- [ ] Contrast: 4.5:1 for text +- [ ] Alt text: All images have appropriate alt +- [ ] Headings: Logical h1-h6 hierarchy +- [ ] Forms: Labels associated with inputs +- [ ] Errors: Announced to screen readers +- [ ] Touch: 44px minimum targets + +## Resources + +- [WCAG 2.1 Quick Reference](https://www.w3.org/WAI/WCAG21/quickref/) +- [WebAIM Contrast Checker](https://webaim.org/resources/contrastchecker/) +- [ARIA Authoring Practices](https://www.w3.org/WAI/ARIA/apg/) diff --git a/.agents/skills/bencium-innovative-ux-designer/DESIGN-SYSTEM-TEMPLATE.md b/.agents/skills/bencium-innovative-ux-designer/DESIGN-SYSTEM-TEMPLATE.md new file mode 100644 index 0000000..e968748 --- /dev/null +++ b/.agents/skills/bencium-innovative-ux-designer/DESIGN-SYSTEM-TEMPLATE.md @@ -0,0 +1,577 @@ +# Design System Template + +Meta-framework for understanding what's fixed, project-specific, and adaptable in your design system. + +## Purpose + +This template helps you distinguish between: +- **Fixed Elements**: Universal rules that never change +- **Project-Specific Elements**: Filled in for each project based on brand +- **Adaptable Elements**: Context-dependent implementations + +--- + +## I. FIXED ELEMENTS + +These foundations remain consistent across all projects, regardless of brand or context. + +### 1. Spacing Scale + +**Fixed System:** +``` +4px, 8px, 12px, 16px, 24px, 32px, 48px, 64px, 96px +``` + +**Usage:** +- Margins, padding, gaps between elements +- Mathematical relationships ensure visual harmony +- Use multipliers of base unit (4px) + +**Why Fixed:** +Consistent spacing creates visual rhythm regardless of brand personality. + +### 2. Grid System + +**Fixed Structure:** +- **12-column grid** for most layouts (divisible by 2, 3, 4, 6) +- **16-column grid** for data-heavy interfaces +- **Gutters**: 16px (mobile), 24px (tablet), 32px (desktop) + +**Why Fixed:** +Grid provides structural order. Brand personality shows through color, typography, content—not grid structure. + +### 3. Accessibility Standards + +**Fixed Requirements:** +- **WCAG 2.1 AA** compliance minimum +- **Contrast**: 4.5:1 for normal text, 3:1 for large text +- **Touch targets**: Minimum 44×44px +- **Keyboard navigation**: All interactive elements accessible +- **Screen reader**: Semantic HTML, ARIA labels where needed + +**Why Fixed:** +Accessibility is not negotiable. It's a baseline requirement for ethical, legal, and usable products. + +### 4. Typography Hierarchy Logic + +**Fixed Structure:** +- **Mathematical scaling**: 1.25x (major third) or 1.333x (perfect fourth) +- **Hierarchy levels**: Display → H1 → H2 → H3 → Body → Small → Caption +- **Line height**: 1.5x for body text, 1.2-1.3x for headlines +- **Line length**: 45-75 characters optimal + +**Why Fixed:** +Mathematical relationships create predictable, harmonious hierarchy. Specific fonts change, but the logic doesn't. + +### 5. Component Architecture + +**Fixed Patterns:** +- **Button states**: Default, Hover, Active, Focus, Disabled +- **Form structure**: Label above input, error below, helper text optional +- **Modal pattern**: Overlay + centered content + close mechanism +- **Card structure**: Container → Header → Body → Footer (optional) + +**Why Fixed:** +Users expect consistent component behavior. Architecture is fixed; appearance is project-specific. + +### 6. Animation Timing Framework + +**Fixed Physics Profiles:** +- **Lightweight** (icons, chips): 150ms +- **Standard** (cards, panels): 300ms +- **Weighty** (modals, pages): 500ms + +**Fixed Easing:** +- **Ease-out**: Entrances (fast start, slow end) +- **Ease-in**: Exits (slow start, fast end) +- **Ease-in-out**: Transitions (smooth both ends) + +**Why Fixed:** +Natural physics feel consistent across brands. Duration and easing create that feeling. + +--- + +## II. PROJECT-SPECIFIC ELEMENTS + +Fill in these for each project based on brand personality and purpose. + +### 1. Brand Color System + +**Template Structure:** + +``` +NEUTRALS (4-5 colors): +- Background lightest: _______ (e.g., slate-50 or warm-white) +- Surface: _______ (e.g., slate-100) +- Border/divider: _______ (e.g., slate-300) +- Text secondary: _______ (e.g., slate-600) +- Text primary: _______ (e.g., slate-900) + +ACCENTS (1-3 colors): +- Primary (main CTA): _______ (e.g., teal-500) +- Secondary (alternative action): _______ (optional) +- Status colors: + - Success: _______ (green-ish) + - Warning: _______ (amber-ish) + - Error: _______ (red-ish) + - Info: _______ (blue-ish) +``` + +**Questions to Answer:** +- What emotion should the brand evoke? (Trust, excitement, calm, urgency) +- Warm or cool neutrals? +- Conservative or bold accents? + +**Examples:** + +**Project A: Fintech App** +``` +Neutrals: Cool greys (slate-50 → slate-900) +Primary: Deep blue (#0A2463) – trust, professionalism +Success: Muted green (#10B981) +Why: Financial products need trust, not playfulness +``` + +**Project B: Creative Community** +``` +Neutrals: Warm greys with beige undertones +Primary: Coral (#FF6B6B) – energy, creativity +Success: Teal (#06D6A0) – fresh, unexpected +Why: Creative spaces should feel inviting, not corporate +``` + +**Project C: Healthcare Platform** +``` +Neutrals: Pure greys (minimal color temperature) +Primary: Soft blue (#4A90E2) – calm, clinical +Success: Medical green (#38A169) +Why: Healthcare needs clarity and calm, not distraction +``` + +### 2. Typography Pairing + +**Template:** + +``` +HEADLINE FONT: _______ +- Weight: _______ (e.g., Bold 700) +- Use case: H1, H2, display text +- Personality: _______ (geometric/humanist/serif/etc.) + +BODY FONT: _______ +- Weight: _______ (e.g., Regular 400, Medium 500) +- Use case: Paragraphs, UI text +- Personality: _______ (neutral/readable/efficient) + +OPTIONAL ACCENT FONT: _______ +- Weight: _______ +- Use case: _______ (special headlines, callouts) +``` + +**Pairing Logic:** +- Serif + Sans-serif (classic, editorial) +- Geometric + Humanist (modern + warm) +- Display + System (distinctive + efficient) + +**Examples:** + +**Project A: Editorial Platform** +``` +Headline: Playfair Display (Serif, Bold 700) +Body: Inter (Sans-serif, Regular 400) +Why: Serif headlines = trustworthy, editorial feel +``` + +**Project B: Tech Startup** +``` +Headline: DM Sans (Sans-serif, Bold 700) +Body: DM Sans (Regular 400, Medium 500) +Why: Single-font system = modern, efficient, cohesive +``` + +**Project C: Luxury Brand** +``` +Headline: Cormorant Garamond (Serif, Light 300) +Body: Lato (Sans-serif, Regular 400) +Why: Elegant serif + readable sans = sophisticated +``` + +### 3. Tone of Voice + +**Template:** + +``` +BRAND PERSONALITY: +- Formal ↔ Casual: _______ (1-10 scale) +- Professional ↔ Friendly: _______ (1-10 scale) +- Serious ↔ Playful: _______ (1-10 scale) +- Authoritative ↔ Conversational: _______ (1-10 scale) + +MICROCOPY EXAMPLES: +- Button label (submit form): _______ +- Error message (invalid email): _______ +- Success message (saved): _______ +- Empty state: _______ + +ANIMATION PERSONALITY: +- Speed: _______ (quick/moderate/slow) +- Feel: _______ (precise/smooth/bouncy) +``` + +**Examples:** + +**Project A: Banking App** +``` +Personality: Formal (8), Professional (9), Serious (8) +Button: "Submit Application" +Error: "Email address format is invalid" +Success: "Application submitted successfully" +Animation: Quick (precise, efficient, no-nonsense) +``` + +**Project B: Social App** +``` +Personality: Casual (8), Friendly (9), Playful (7) +Button: "Let's go!" +Error: "Hmm, that email doesn't look right" +Success: "Nice! You're all set 🎉" +Animation: Moderate (smooth, friendly bounce) +``` + +### 4. Animation Speed & Feel + +**Template:** + +``` +SPEED PREFERENCE: +- UI interactions: _______ (100-150ms / 150-200ms / 200-300ms) +- State changes: _______ (200ms / 300ms / 400ms) +- Page transitions: _______ (300ms / 500ms / 700ms) + +ANIMATION STYLE: +- Easing preference: _______ (sharp / standard / bouncy) +- Movement type: _______ (minimal / smooth / expressive) +``` + +**Examples:** + +**Project A: Trading Platform** +``` +Speed: Fast (100ms UI, 200ms states, 300ms pages) +Style: Sharp easing, minimal movement +Why: Traders need speed, not distraction +``` + +**Project B: Wellness App** +``` +Speed: Slow (200ms UI, 400ms states, 500ms pages) +Style: Smooth easing, gentle movement +Why: Calm, relaxing experience matches brand +``` + +--- + +## III. ADAPTABLE ELEMENTS + +Context-dependent implementations that vary based on use case. + +### 1. Component Variations + +**Button Variants:** +- **Primary**: Full background color (high emphasis) +- **Secondary**: Outline only (medium emphasis) +- **Tertiary**: Text only (low emphasis) +- **Destructive**: Red-ish (danger actions) +- **Ghost**: Minimal (navigation, toolbars) + +**Adaptation Rules:** +- Primary: Main CTA, one per screen section +- Secondary: Alternative actions +- Tertiary: Less important actions, multiple allowed +- Use brand colors, but hierarchy logic is fixed + +### 2. Responsive Breakpoints + +**Fixed Ranges:** +- XS: 0-479px (small phones) +- SM: 480-767px (large phones) +- MD: 768-1023px (tablets) +- LG: 1024-1439px (laptops) +- XL: 1440px+ (desktop) + +**Adaptable Implementations:** + +**Simple Content Site:** +``` +XS-SM: Single column +MD: 2 columns +LG-XL: 3 columns max +Why: Content-focused, don't overwhelm +``` + +**Dashboard/Data App:** +``` +XS: Collapsed, cards stack +SM: Simplified sidebar +MD: Full sidebar + main content +LG-XL: Sidebar + main + right panel +Why: Data apps need more screen real estate +``` + +### 3. Dark Mode Palette + +**Adaptation Strategy:** + +Not a simple inversion. Dark mode needs adjusted contrast: + +**Light Mode:** +``` +Background: #FFFFFF (white) +Text: #0F172A (slate-900) → 21:1 contrast +``` + +**Dark Mode (Adapted):** +``` +Background: #0F172A (slate-900) +Text: #E2E8F0 (slate-200) → 15.8:1 contrast (still AA, but softer) +``` + +**Why Adapt:** +Pure white on pure black is too harsh. Dark mode needs slightly lower contrast for eye comfort. + +### 4. Loading States + +**Context-Dependent:** + +**Fast operations (<500ms):** +- No loading indicator (feels instant) + +**Medium operations (500ms-2s):** +- Spinner or skeleton screen + +**Long operations (>2s):** +- Progress bar with percentage +- Or: Skeleton + estimated time + +**Interactive Operations:** +- Button shows spinner inside (don't disable, show state) + +### 5. Error Handling Strategy + +**Context-Dependent:** + +**Form Errors:** +``` +Validate: On blur (after user leaves field) +Display: Inline below field +Recovery: Clear error on fix +``` + +**API Errors:** +``` +Transient (network): Show retry button +Permanent (404): Show helpful message + next steps +Critical (500): Contact support option +``` + +**Data Errors:** +``` +Missing: Show empty state with action +Corrupt: Show error boundary with reload +Invalid: Highlight + explain what's wrong +``` + +--- + +## DECISION TREE + +When implementing a feature, ask: + +### Is this... + +**FIXED?** +- Does it affect structure, accessibility, or universal UX? +- Examples: Spacing scale, grid, contrast ratios, component architecture +- **Action**: Use the fixed system, no variation + +**PROJECT-SPECIFIC?** +- Does it express brand personality or purpose? +- Examples: Colors, typography, tone of voice, animation feel +- **Action**: Fill in the template for this project + +**ADAPTABLE?** +- Does it depend on context, content, or use case? +- Examples: Component variants, responsive behavior, error handling +- **Action**: Choose appropriate variation based on context + +--- + +## EXAMPLE: Implementing a "Submit" Button + +### Fixed Elements (Always the same): +- Touch target: 44px minimum height +- Padding: 16px horizontal (from spacing scale) +- States: Default, Hover, Active, Focus, Disabled +- Animation: 150ms ease-out (lightweight profile) + +### Project-Specific (Filled per project): +- **Project A (Bank)**: Dark blue background, white text, "Submit Application" +- **Project B (Social)**: Coral background, white text, "Let's Go!" +- **Project C (Healthcare)**: Soft blue background, white text, "Continue" + +### Adaptable (Context-dependent): +- **Form context**: Primary button (full color) +- **Toolbar context**: Ghost button (text only) +- **Danger context**: Destructive variant (red-ish) + +--- + +## VALIDATION CHECKLIST + +Before finalizing a design, check: + +### Fixed Elements +- [ ] Uses spacing scale (4/8/12/16/24/32/48/64/96px) +- [ ] Follows grid system (12 or 16 columns) +- [ ] Meets WCAG AA contrast (4.5:1 normal, 3:1 large) +- [ ] Touch targets ≥ 44px +- [ ] Typography follows mathematical scale +- [ ] Components follow standard architecture + +### Project-Specific Elements +- [ ] Brand colors filled in and intentional +- [ ] Typography pairing chosen and justified +- [ ] Tone of voice defined and consistent +- [ ] Animation speed matches brand personality + +### Adaptable Elements +- [ ] Component variants appropriate for context +- [ ] Responsive behavior fits content type +- [ ] Loading states match operation duration +- [ ] Error handling fits error type + +--- + +## PROJECT KICKOFF TEMPLATE + +Use this to start a new project: + +``` +PROJECT NAME: _______________________ +PURPOSE: ____________________________ + +BRAND PERSONALITY: +- Primary emotion: _______ +- Warm or cool: _______ +- Formal or casual: _______ +- Conservative or bold: _______ + +COLORS (fill the template): +- Neutral base: _______ +- Primary accent: _______ +- Status colors: _______ / _______ / _______ + +TYPOGRAPHY (fill the template): +- Headline font: _______ +- Body font: _______ +- Pairing rationale: _______ + +TONE: +- Button labels style: _______ +- Error message style: _______ +- Success message style: _______ + +ANIMATION: +- Speed preference: _______ (fast/moderate/slow) +- Feel preference: _______ (sharp/smooth/bouncy) + +TARGET DEVICES: +- Primary: _______ (mobile/desktop/both) +- Secondary: _______ +``` + +--- + +## MAINTAINING CONSISTENCY + +### Documentation +- Keep this template updated as system evolves +- Document WHY choices were made, not just WHAT + +### Communication +- Share with designers: "Here's what varies vs. what's fixed" +- Share with developers: "Here are the design tokens" + +### Tooling +- Use CSS variables for project-specific values +- Use Tailwind config for spacing scale +- Use design tokens in Figma/Storybook + +### Reviews +- Audit: Does new work follow fixed elements? +- Validate: Are project-specific elements intentional? +- Question: Are adaptations justified by context? + +--- + +## EXAMPLES OF COMPLETE SYSTEMS + +### System A: B2B SaaS (Conservative) + +**Fixed**: Standard spacing, 12-col grid, WCAG AA, major third type scale +**Project-Specific**: +- Colors: Cool greys + corporate blue +- Typography: DM Sans (headlines + body) +- Tone: Professional, formal +- Animation: Quick, precise (150ms) +**Adaptable**: +- Dashboard gets multi-panel layout +- Forms are extensive (use progressive disclosure) +- Errors show detailed technical info + +### System B: Consumer Social App (Playful) + +**Fixed**: Same spacing/grid/accessibility/type logic +**Project-Specific**: +- Colors: Warm greys + vibrant coral +- Typography: Poppins (headlines) + Inter (body) +- Tone: Casual, friendly, playful +- Animation: Moderate, bouncy (200ms) +**Adaptable**: +- Mobile-first (most users on phones) +- Forms are minimal (progressive profiling) +- Errors are friendly, not technical + +### System C: Healthcare Platform (Clinical) + +**Fixed**: Same foundational structure +**Project-Specific**: +- Colors: Pure greys + medical blue +- Typography: System fonts (SF Pro / Segoe) +- Tone: Clear, authoritative, calm +- Animation: Slow, smooth (300ms) +**Adaptable**: +- Desktop-first (clinical use at workstations) +- Forms are complex (HIPAA compliance) +- Errors are precise with next steps + +--- + +## KEY TAKEAWAY + +**The system flexibility framework lets you:** +- Maintain consistency (fixed elements) +- Express brand personality (project-specific) +- Adapt to context (adaptable elements) + +**Without this framework:** +- Designers reinvent spacing every project +- Components feel inconsistent across products +- Brand personality overrides accessibility +- Context-blind implementations feel wrong + +**With this framework:** +- Speed: Start from proven foundations +- Consistency: Fixed elements guarantee it +- Flexibility: Express unique brand identity +- Context: Adapt without breaking system diff --git a/.agents/skills/bencium-innovative-ux-designer/MOTION-SPEC.md b/.agents/skills/bencium-innovative-ux-designer/MOTION-SPEC.md new file mode 100644 index 0000000..e37e363 --- /dev/null +++ b/.agents/skills/bencium-innovative-ux-designer/MOTION-SPEC.md @@ -0,0 +1,72 @@ +# Motion Specification + +Motion should surprise and delight while serving function. Animation is a creative tool. + +## Easing Curves + +| Easing | CSS | Use For | +|--------|-----|---------| +| **Ease-out** | `cubic-bezier(0.0, 0.0, 0.2, 1)` | Entrances, appearing | +| **Ease-in** | `cubic-bezier(0.4, 0.0, 1, 1)` | Exits, disappearing | +| **Ease-in-out** | `cubic-bezier(0.4, 0.0, 0.2, 1)` | State changes, transforms | +| **Spring** | `cubic-bezier(0.68, -0.55, 0.265, 1.55)` | Playful, attention-grabbing | +| **Linear** | `linear` | Spinners, continuous loops | + +## Duration by Element Weight + +| Weight | Duration | Examples | +|--------|----------|----------| +| **Lightweight** | 150ms | Icons, badges, chips | +| **Standard** | 300ms | Cards, panels, list items | +| **Weighty** | 500ms | Modals, page transitions | + +## Duration by Interaction + +| Interaction | Duration | +|-------------|----------| +| Button press | 100ms | +| Hover state | 150ms | +| Tooltip appear | 200ms | +| Tab switch | 250ms | +| Modal open | 300ms | +| Page transition | 400ms | + +## Common Patterns + +```tsx +// Hover transition (CSS) + +``` + +### Example 2: Typography Hierarchy + +**Question First:** +``` +For this content hierarchy, I'm thinking: +- Headline: Large, bold, attention-grabbing +- Subheading: Medium, regular weight +- Body: Smaller, optimized for reading + +Should we go bold and contemporary, or subtle and refined? +``` + +**Implementation After Approval:** +```tsx +
+

+ Headline Here +

+

+ Supporting subheading +

+

+ Body text optimized for readability with proper line height. +

+
+``` + +### Example 3: Unique Color Palette Suggestion + +**Presenting Options:** +``` +I'd like to suggest three color directions that break from typical SaaS patterns: + +1. **Warm Earth Tones** + - Base: Warm grey (#E8E2DC) + - Accent: Terracotta (#C86E4B) + - For: Organic, trustworthy feel + +2. **Cool Midnight** + - Base: Deep navy (#1A2332) + - Accent: Cyan (#4ECDC4) + - For: Modern, tech-forward feel + +3. **Soft Pastels** + - Base: Soft pink (#FFE5E5) + - Accent: Sage green (#9DB5A4) + - For: Calm, approachable feel + +Which direction feels right for your brand? +``` + +## Common Patterns to Avoid + +❌ **NEVER:** +- Use Inter, Roboto, Arial, Space Grotesk as primary fonts +- Use generic SaaS blue (#3B82F6) or purple gradients on white +- Copy Apple's design language or use glass morphism +- Create cookie-cutter layouts that look AI-generated +- Skip asking about context before designing +- Converge on common choices across generations (vary everything!) +- Use animations that delay user actions +- Create cluttered interfaces where elements compete + +✅ **ALWAYS:** +- Ask about purpose, tone, constraints, differentiation FIRST +- Then commit BOLDLY to a distinctive aesthetic direction +- Use unexpected, characterful typography choices +- Create atmosphere: shadows, gradients, textures, grain (when intentional) +- Dominant colors with sharp accents (not timid, evenly-distributed palettes) +- Provide immediate feedback for interactions +- Test with real devices +- Validate accessibility (it enables creativity, not limits it) +- Remember: Claude is capable of extraordinary creative work - don't hold back! + +## Version History + +- v2.0.0 (2025-11-22): Creative liberation update - bold aesthetics, shadows/gradients allowed, Design Thinking protocol +- v1.0.0 (2025-10-18): Initial release with comprehensive UI/UX design guidance + +## References + +For additional context, see: +- **Anthropic Frontend Aesthetics Cookbook**: https://github.com/anthropics/claude-cookbooks/blob/main/coding/prompting_for_frontend_aesthetics.ipynb +- WCAG 2.1 Guidelines: https://www.w3.org/WAI/WCAG21/quickref/ +- Google Fonts: https://fonts.google.com/ +- Tailwind CSS Docs: https://tailwindcss.com/docs +- Shadcn UI Components: https://ui.shadcn.com/ + +**Progressive Disclosure Files:** +- ACCESSIBILITY.md - Accessibility essentials (WCAG AA baseline) +- MOTION-SPEC.md - Animation timing and easing +- RESPONSIVE-DESIGN.md - Mobile-first breakpoints and patterns diff --git a/.agents/skills/interactive-portfolio/SKILL.md b/.agents/skills/interactive-portfolio/SKILL.md new file mode 100644 index 0000000..110f519 --- /dev/null +++ b/.agents/skills/interactive-portfolio/SKILL.md @@ -0,0 +1,223 @@ +--- +name: interactive-portfolio +description: "Expert in building portfolios that actually land jobs and clients - not just showing work, but creating memorable experiences. Covers developer portfolios, designer portfolios, creative portfolios, and portfolios that convert visitors into opportunities. Use when: portfolio, personal website, showcase work, developer portfolio, designer portfolio." +source: vibeship-spawner-skills (Apache 2.0) +--- + +# Interactive Portfolio + +**Role**: Portfolio Experience Designer + +You know a portfolio isn't a resume - it's a first impression that needs +to convert. You balance creativity with usability. You understand that +hiring managers spend 30 seconds on each portfolio. You make those 30 +seconds count. You help people stand out without being gimmicky. + +## Capabilities + +- Portfolio architecture +- Project showcase design +- Interactive case studies +- Personal branding for devs/designers +- Contact conversion +- Portfolio performance +- Work presentation +- Testimonial integration + +## Patterns + +### Portfolio Architecture + +Structure that works for portfolios + +**When to use**: When planning portfolio structure + +```javascript +## Portfolio Architecture + +### The 30-Second Test +In 30 seconds, visitors should know: +1. Who you are +2. What you do +3. Your best work +4. How to contact you + +### Essential Sections +| Section | Purpose | Priority | +|---------|---------|----------| +| Hero | Hook + identity | Critical | +| Work/Projects | Prove skills | Critical | +| About | Personality + story | Important | +| Contact | Convert interest | Critical | +| Testimonials | Social proof | Nice to have | +| Blog/Writing | Thought leadership | Optional | + +### Navigation Patterns +``` +Option 1: Single page scroll +- Best for: Designers, creatives +- Works well with animations +- Mobile friendly + +Option 2: Multi-page +- Best for: Lots of projects +- Individual case study pages +- Better for SEO + +Option 3: Hybrid +- Main sections on one page +- Detailed case studies separate +- Best of both worlds +``` + +### Hero Section Formula +``` +[Your name] +[What you do in one line] +[One line that differentiates you] +[CTA: View Work / Contact] +``` +``` + +### Project Showcase + +How to present work effectively + +**When to use**: When building project sections + +```javascript +## Project Showcase + +### Project Card Elements +| Element | Purpose | +|---------|---------| +| Thumbnail | Visual hook | +| Title | What it is | +| One-liner | What you did | +| Tech/tags | Quick scan | +| Results | Proof of impact | + +### Case Study Structure +``` +1. Hero image/video +2. Project overview (2-3 sentences) +3. The challenge +4. Your role +5. Process highlights +6. Key decisions +7. Results/impact +8. Learnings (optional) +9. Links (live, GitHub, etc.) +``` + +### Showing Impact +| Instead of | Write | +|------------|-------| +| "Built a website" | "Increased conversions 40%" | +| "Designed UI" | "Reduced user drop-off 25%" | +| "Developed features" | "Shipped to 50K users" | + +### Visual Presentation +- Device mockups for web/mobile +- Before/after comparisons +- Process artifacts (wireframes, etc.) +- Video walkthroughs for complex work +- Hover effects for engagement +``` + +### Developer Portfolio Specifics + +What works for dev portfolios + +**When to use**: When building developer portfolio + +```javascript +## Developer Portfolio + +### What Hiring Managers Look For +1. Code quality (GitHub link) +2. Real projects (not just tutorials) +3. Problem-solving ability +4. Communication skills +5. Technical depth + +### Must-Haves +- GitHub profile link (cleaned up) +- Live project links +- Tech stack for each project +- Your specific contribution (for team projects) + +### Project Selection +| Include | Avoid | +|---------|-------| +| Real problems solved | Tutorial clones | +| Side projects with users | Incomplete projects | +| Open source contributions | "Coming soon" | +| Technical challenges | Basic CRUD apps | + +### Technical Showcase +```javascript +// Show code snippets that demonstrate: +- Clean architecture decisions +- Performance optimizations +- Clever solutions +- Testing approach +``` + +### Blog/Writing +- Technical deep dives +- Problem-solving stories +- Learning journeys +- Shows communication skills +``` + +## Anti-Patterns + +### ❌ Template Portfolio + +**Why bad**: Looks like everyone else. +No memorable impression. +Doesn't show creativity. +Easy to forget. + +**Instead**: Add personal touches. +Custom design elements. +Unique project presentations. +Your voice in the copy. + +### ❌ All Style No Substance + +**Why bad**: Fancy animations, weak projects. +Style over substance. +Hiring managers see through it. +No proof of skills. + +**Instead**: Projects first, style second. +Real work with real impact. +Quality over quantity. +Depth over breadth. + +### ❌ Resume Website + +**Why bad**: Boring, forgettable. +Doesn't use the medium. +No personality. +Lists instead of stories. + +**Instead**: Show, don't tell. +Visual case studies. +Interactive elements. +Personality throughout. + +## ⚠️ Sharp Edges + +| Issue | Severity | Solution | +|-------|----------|----------| +| Portfolio more complex than your actual work | medium | ## Right-Sizing Your Portfolio | +| Portfolio looks great on desktop, broken on mobile | high | ## Mobile-First Portfolio | +| Visitors don't know what to do next | medium | ## Portfolio CTAs | +| Portfolio shows old or irrelevant work | medium | ## Portfolio Freshness | + +## Related Skills + +Works well with: `scroll-experience`, `3d-web-experience`, `landing-page-design`, `personal-branding` diff --git a/.claude/skills/bencium-innovative-ux-designer/ACCESSIBILITY.md b/.claude/skills/bencium-innovative-ux-designer/ACCESSIBILITY.md new file mode 100644 index 0000000..d514f4e --- /dev/null +++ b/.claude/skills/bencium-innovative-ux-designer/ACCESSIBILITY.md @@ -0,0 +1,111 @@ +# Accessibility Essentials + +Accessibility enables creativity - it's a foundation, not a limitation. WCAG 2.1 AA compliance. + +## Core Principles (POUR) + +- **Perceivable**: Content must be perceivable (alt text, contrast, captions) +- **Operable**: UI must be keyboard/touch accessible +- **Understandable**: Clear, predictable behavior +- **Robust**: Works with assistive technologies + +## Contrast Requirements + +| Element | Minimum Ratio | +|---------|---------------| +| Normal text | 4.5:1 | +| Large text (18pt+) | 3:1 | +| UI components | 3:1 | + +**Tools**: Chrome DevTools Accessibility tab, WebAIM Contrast Checker + +## Keyboard Navigation + +```tsx +// All interactive elements need focus states + + +// Custom elements need tabindex and key handlers +
(e.key === 'Enter' || e.key === ' ') && handleClick()} +> + Custom Button +
+``` + +**Essentials:** +- Tab through entire interface +- Enter/Space activates elements +- Escape closes modals +- Visible focus indicators always + +## Essential ARIA + +```tsx +// Buttons without text + + +// Expandable elements + + +// Live regions for dynamic content +
{statusMessage}
+
{errorMessage}
+ +// Form errors + +{hasError && } +``` + +## Semantic HTML + +```tsx +// Use semantic elements, not divs +
+

...

+ + +// Heading hierarchy (never skip levels) +

Page Title

+

Section

+

Subsection

+``` + +## Touch Targets + +- Minimum **44x44px** for all interactive elements +- Adequate spacing between targets +- `touch-manipulation` CSS for responsive touch + +## Screen Reader Content + +```tsx +// Hidden but announced +Additional context + +// Skip link + + Skip to main content + +``` + +## Quick Checklist + +- [ ] Keyboard: Can tab through everything +- [ ] Focus: Visible focus indicators +- [ ] Contrast: 4.5:1 for text +- [ ] Alt text: All images have appropriate alt +- [ ] Headings: Logical h1-h6 hierarchy +- [ ] Forms: Labels associated with inputs +- [ ] Errors: Announced to screen readers +- [ ] Touch: 44px minimum targets + +## Resources + +- [WCAG 2.1 Quick Reference](https://www.w3.org/WAI/WCAG21/quickref/) +- [WebAIM Contrast Checker](https://webaim.org/resources/contrastchecker/) +- [ARIA Authoring Practices](https://www.w3.org/WAI/ARIA/apg/) diff --git a/.claude/skills/bencium-innovative-ux-designer/DESIGN-SYSTEM-TEMPLATE.md b/.claude/skills/bencium-innovative-ux-designer/DESIGN-SYSTEM-TEMPLATE.md new file mode 100644 index 0000000..e968748 --- /dev/null +++ b/.claude/skills/bencium-innovative-ux-designer/DESIGN-SYSTEM-TEMPLATE.md @@ -0,0 +1,577 @@ +# Design System Template + +Meta-framework for understanding what's fixed, project-specific, and adaptable in your design system. + +## Purpose + +This template helps you distinguish between: +- **Fixed Elements**: Universal rules that never change +- **Project-Specific Elements**: Filled in for each project based on brand +- **Adaptable Elements**: Context-dependent implementations + +--- + +## I. FIXED ELEMENTS + +These foundations remain consistent across all projects, regardless of brand or context. + +### 1. Spacing Scale + +**Fixed System:** +``` +4px, 8px, 12px, 16px, 24px, 32px, 48px, 64px, 96px +``` + +**Usage:** +- Margins, padding, gaps between elements +- Mathematical relationships ensure visual harmony +- Use multipliers of base unit (4px) + +**Why Fixed:** +Consistent spacing creates visual rhythm regardless of brand personality. + +### 2. Grid System + +**Fixed Structure:** +- **12-column grid** for most layouts (divisible by 2, 3, 4, 6) +- **16-column grid** for data-heavy interfaces +- **Gutters**: 16px (mobile), 24px (tablet), 32px (desktop) + +**Why Fixed:** +Grid provides structural order. Brand personality shows through color, typography, content—not grid structure. + +### 3. Accessibility Standards + +**Fixed Requirements:** +- **WCAG 2.1 AA** compliance minimum +- **Contrast**: 4.5:1 for normal text, 3:1 for large text +- **Touch targets**: Minimum 44×44px +- **Keyboard navigation**: All interactive elements accessible +- **Screen reader**: Semantic HTML, ARIA labels where needed + +**Why Fixed:** +Accessibility is not negotiable. It's a baseline requirement for ethical, legal, and usable products. + +### 4. Typography Hierarchy Logic + +**Fixed Structure:** +- **Mathematical scaling**: 1.25x (major third) or 1.333x (perfect fourth) +- **Hierarchy levels**: Display → H1 → H2 → H3 → Body → Small → Caption +- **Line height**: 1.5x for body text, 1.2-1.3x for headlines +- **Line length**: 45-75 characters optimal + +**Why Fixed:** +Mathematical relationships create predictable, harmonious hierarchy. Specific fonts change, but the logic doesn't. + +### 5. Component Architecture + +**Fixed Patterns:** +- **Button states**: Default, Hover, Active, Focus, Disabled +- **Form structure**: Label above input, error below, helper text optional +- **Modal pattern**: Overlay + centered content + close mechanism +- **Card structure**: Container → Header → Body → Footer (optional) + +**Why Fixed:** +Users expect consistent component behavior. Architecture is fixed; appearance is project-specific. + +### 6. Animation Timing Framework + +**Fixed Physics Profiles:** +- **Lightweight** (icons, chips): 150ms +- **Standard** (cards, panels): 300ms +- **Weighty** (modals, pages): 500ms + +**Fixed Easing:** +- **Ease-out**: Entrances (fast start, slow end) +- **Ease-in**: Exits (slow start, fast end) +- **Ease-in-out**: Transitions (smooth both ends) + +**Why Fixed:** +Natural physics feel consistent across brands. Duration and easing create that feeling. + +--- + +## II. PROJECT-SPECIFIC ELEMENTS + +Fill in these for each project based on brand personality and purpose. + +### 1. Brand Color System + +**Template Structure:** + +``` +NEUTRALS (4-5 colors): +- Background lightest: _______ (e.g., slate-50 or warm-white) +- Surface: _______ (e.g., slate-100) +- Border/divider: _______ (e.g., slate-300) +- Text secondary: _______ (e.g., slate-600) +- Text primary: _______ (e.g., slate-900) + +ACCENTS (1-3 colors): +- Primary (main CTA): _______ (e.g., teal-500) +- Secondary (alternative action): _______ (optional) +- Status colors: + - Success: _______ (green-ish) + - Warning: _______ (amber-ish) + - Error: _______ (red-ish) + - Info: _______ (blue-ish) +``` + +**Questions to Answer:** +- What emotion should the brand evoke? (Trust, excitement, calm, urgency) +- Warm or cool neutrals? +- Conservative or bold accents? + +**Examples:** + +**Project A: Fintech App** +``` +Neutrals: Cool greys (slate-50 → slate-900) +Primary: Deep blue (#0A2463) – trust, professionalism +Success: Muted green (#10B981) +Why: Financial products need trust, not playfulness +``` + +**Project B: Creative Community** +``` +Neutrals: Warm greys with beige undertones +Primary: Coral (#FF6B6B) – energy, creativity +Success: Teal (#06D6A0) – fresh, unexpected +Why: Creative spaces should feel inviting, not corporate +``` + +**Project C: Healthcare Platform** +``` +Neutrals: Pure greys (minimal color temperature) +Primary: Soft blue (#4A90E2) – calm, clinical +Success: Medical green (#38A169) +Why: Healthcare needs clarity and calm, not distraction +``` + +### 2. Typography Pairing + +**Template:** + +``` +HEADLINE FONT: _______ +- Weight: _______ (e.g., Bold 700) +- Use case: H1, H2, display text +- Personality: _______ (geometric/humanist/serif/etc.) + +BODY FONT: _______ +- Weight: _______ (e.g., Regular 400, Medium 500) +- Use case: Paragraphs, UI text +- Personality: _______ (neutral/readable/efficient) + +OPTIONAL ACCENT FONT: _______ +- Weight: _______ +- Use case: _______ (special headlines, callouts) +``` + +**Pairing Logic:** +- Serif + Sans-serif (classic, editorial) +- Geometric + Humanist (modern + warm) +- Display + System (distinctive + efficient) + +**Examples:** + +**Project A: Editorial Platform** +``` +Headline: Playfair Display (Serif, Bold 700) +Body: Inter (Sans-serif, Regular 400) +Why: Serif headlines = trustworthy, editorial feel +``` + +**Project B: Tech Startup** +``` +Headline: DM Sans (Sans-serif, Bold 700) +Body: DM Sans (Regular 400, Medium 500) +Why: Single-font system = modern, efficient, cohesive +``` + +**Project C: Luxury Brand** +``` +Headline: Cormorant Garamond (Serif, Light 300) +Body: Lato (Sans-serif, Regular 400) +Why: Elegant serif + readable sans = sophisticated +``` + +### 3. Tone of Voice + +**Template:** + +``` +BRAND PERSONALITY: +- Formal ↔ Casual: _______ (1-10 scale) +- Professional ↔ Friendly: _______ (1-10 scale) +- Serious ↔ Playful: _______ (1-10 scale) +- Authoritative ↔ Conversational: _______ (1-10 scale) + +MICROCOPY EXAMPLES: +- Button label (submit form): _______ +- Error message (invalid email): _______ +- Success message (saved): _______ +- Empty state: _______ + +ANIMATION PERSONALITY: +- Speed: _______ (quick/moderate/slow) +- Feel: _______ (precise/smooth/bouncy) +``` + +**Examples:** + +**Project A: Banking App** +``` +Personality: Formal (8), Professional (9), Serious (8) +Button: "Submit Application" +Error: "Email address format is invalid" +Success: "Application submitted successfully" +Animation: Quick (precise, efficient, no-nonsense) +``` + +**Project B: Social App** +``` +Personality: Casual (8), Friendly (9), Playful (7) +Button: "Let's go!" +Error: "Hmm, that email doesn't look right" +Success: "Nice! You're all set 🎉" +Animation: Moderate (smooth, friendly bounce) +``` + +### 4. Animation Speed & Feel + +**Template:** + +``` +SPEED PREFERENCE: +- UI interactions: _______ (100-150ms / 150-200ms / 200-300ms) +- State changes: _______ (200ms / 300ms / 400ms) +- Page transitions: _______ (300ms / 500ms / 700ms) + +ANIMATION STYLE: +- Easing preference: _______ (sharp / standard / bouncy) +- Movement type: _______ (minimal / smooth / expressive) +``` + +**Examples:** + +**Project A: Trading Platform** +``` +Speed: Fast (100ms UI, 200ms states, 300ms pages) +Style: Sharp easing, minimal movement +Why: Traders need speed, not distraction +``` + +**Project B: Wellness App** +``` +Speed: Slow (200ms UI, 400ms states, 500ms pages) +Style: Smooth easing, gentle movement +Why: Calm, relaxing experience matches brand +``` + +--- + +## III. ADAPTABLE ELEMENTS + +Context-dependent implementations that vary based on use case. + +### 1. Component Variations + +**Button Variants:** +- **Primary**: Full background color (high emphasis) +- **Secondary**: Outline only (medium emphasis) +- **Tertiary**: Text only (low emphasis) +- **Destructive**: Red-ish (danger actions) +- **Ghost**: Minimal (navigation, toolbars) + +**Adaptation Rules:** +- Primary: Main CTA, one per screen section +- Secondary: Alternative actions +- Tertiary: Less important actions, multiple allowed +- Use brand colors, but hierarchy logic is fixed + +### 2. Responsive Breakpoints + +**Fixed Ranges:** +- XS: 0-479px (small phones) +- SM: 480-767px (large phones) +- MD: 768-1023px (tablets) +- LG: 1024-1439px (laptops) +- XL: 1440px+ (desktop) + +**Adaptable Implementations:** + +**Simple Content Site:** +``` +XS-SM: Single column +MD: 2 columns +LG-XL: 3 columns max +Why: Content-focused, don't overwhelm +``` + +**Dashboard/Data App:** +``` +XS: Collapsed, cards stack +SM: Simplified sidebar +MD: Full sidebar + main content +LG-XL: Sidebar + main + right panel +Why: Data apps need more screen real estate +``` + +### 3. Dark Mode Palette + +**Adaptation Strategy:** + +Not a simple inversion. Dark mode needs adjusted contrast: + +**Light Mode:** +``` +Background: #FFFFFF (white) +Text: #0F172A (slate-900) → 21:1 contrast +``` + +**Dark Mode (Adapted):** +``` +Background: #0F172A (slate-900) +Text: #E2E8F0 (slate-200) → 15.8:1 contrast (still AA, but softer) +``` + +**Why Adapt:** +Pure white on pure black is too harsh. Dark mode needs slightly lower contrast for eye comfort. + +### 4. Loading States + +**Context-Dependent:** + +**Fast operations (<500ms):** +- No loading indicator (feels instant) + +**Medium operations (500ms-2s):** +- Spinner or skeleton screen + +**Long operations (>2s):** +- Progress bar with percentage +- Or: Skeleton + estimated time + +**Interactive Operations:** +- Button shows spinner inside (don't disable, show state) + +### 5. Error Handling Strategy + +**Context-Dependent:** + +**Form Errors:** +``` +Validate: On blur (after user leaves field) +Display: Inline below field +Recovery: Clear error on fix +``` + +**API Errors:** +``` +Transient (network): Show retry button +Permanent (404): Show helpful message + next steps +Critical (500): Contact support option +``` + +**Data Errors:** +``` +Missing: Show empty state with action +Corrupt: Show error boundary with reload +Invalid: Highlight + explain what's wrong +``` + +--- + +## DECISION TREE + +When implementing a feature, ask: + +### Is this... + +**FIXED?** +- Does it affect structure, accessibility, or universal UX? +- Examples: Spacing scale, grid, contrast ratios, component architecture +- **Action**: Use the fixed system, no variation + +**PROJECT-SPECIFIC?** +- Does it express brand personality or purpose? +- Examples: Colors, typography, tone of voice, animation feel +- **Action**: Fill in the template for this project + +**ADAPTABLE?** +- Does it depend on context, content, or use case? +- Examples: Component variants, responsive behavior, error handling +- **Action**: Choose appropriate variation based on context + +--- + +## EXAMPLE: Implementing a "Submit" Button + +### Fixed Elements (Always the same): +- Touch target: 44px minimum height +- Padding: 16px horizontal (from spacing scale) +- States: Default, Hover, Active, Focus, Disabled +- Animation: 150ms ease-out (lightweight profile) + +### Project-Specific (Filled per project): +- **Project A (Bank)**: Dark blue background, white text, "Submit Application" +- **Project B (Social)**: Coral background, white text, "Let's Go!" +- **Project C (Healthcare)**: Soft blue background, white text, "Continue" + +### Adaptable (Context-dependent): +- **Form context**: Primary button (full color) +- **Toolbar context**: Ghost button (text only) +- **Danger context**: Destructive variant (red-ish) + +--- + +## VALIDATION CHECKLIST + +Before finalizing a design, check: + +### Fixed Elements +- [ ] Uses spacing scale (4/8/12/16/24/32/48/64/96px) +- [ ] Follows grid system (12 or 16 columns) +- [ ] Meets WCAG AA contrast (4.5:1 normal, 3:1 large) +- [ ] Touch targets ≥ 44px +- [ ] Typography follows mathematical scale +- [ ] Components follow standard architecture + +### Project-Specific Elements +- [ ] Brand colors filled in and intentional +- [ ] Typography pairing chosen and justified +- [ ] Tone of voice defined and consistent +- [ ] Animation speed matches brand personality + +### Adaptable Elements +- [ ] Component variants appropriate for context +- [ ] Responsive behavior fits content type +- [ ] Loading states match operation duration +- [ ] Error handling fits error type + +--- + +## PROJECT KICKOFF TEMPLATE + +Use this to start a new project: + +``` +PROJECT NAME: _______________________ +PURPOSE: ____________________________ + +BRAND PERSONALITY: +- Primary emotion: _______ +- Warm or cool: _______ +- Formal or casual: _______ +- Conservative or bold: _______ + +COLORS (fill the template): +- Neutral base: _______ +- Primary accent: _______ +- Status colors: _______ / _______ / _______ + +TYPOGRAPHY (fill the template): +- Headline font: _______ +- Body font: _______ +- Pairing rationale: _______ + +TONE: +- Button labels style: _______ +- Error message style: _______ +- Success message style: _______ + +ANIMATION: +- Speed preference: _______ (fast/moderate/slow) +- Feel preference: _______ (sharp/smooth/bouncy) + +TARGET DEVICES: +- Primary: _______ (mobile/desktop/both) +- Secondary: _______ +``` + +--- + +## MAINTAINING CONSISTENCY + +### Documentation +- Keep this template updated as system evolves +- Document WHY choices were made, not just WHAT + +### Communication +- Share with designers: "Here's what varies vs. what's fixed" +- Share with developers: "Here are the design tokens" + +### Tooling +- Use CSS variables for project-specific values +- Use Tailwind config for spacing scale +- Use design tokens in Figma/Storybook + +### Reviews +- Audit: Does new work follow fixed elements? +- Validate: Are project-specific elements intentional? +- Question: Are adaptations justified by context? + +--- + +## EXAMPLES OF COMPLETE SYSTEMS + +### System A: B2B SaaS (Conservative) + +**Fixed**: Standard spacing, 12-col grid, WCAG AA, major third type scale +**Project-Specific**: +- Colors: Cool greys + corporate blue +- Typography: DM Sans (headlines + body) +- Tone: Professional, formal +- Animation: Quick, precise (150ms) +**Adaptable**: +- Dashboard gets multi-panel layout +- Forms are extensive (use progressive disclosure) +- Errors show detailed technical info + +### System B: Consumer Social App (Playful) + +**Fixed**: Same spacing/grid/accessibility/type logic +**Project-Specific**: +- Colors: Warm greys + vibrant coral +- Typography: Poppins (headlines) + Inter (body) +- Tone: Casual, friendly, playful +- Animation: Moderate, bouncy (200ms) +**Adaptable**: +- Mobile-first (most users on phones) +- Forms are minimal (progressive profiling) +- Errors are friendly, not technical + +### System C: Healthcare Platform (Clinical) + +**Fixed**: Same foundational structure +**Project-Specific**: +- Colors: Pure greys + medical blue +- Typography: System fonts (SF Pro / Segoe) +- Tone: Clear, authoritative, calm +- Animation: Slow, smooth (300ms) +**Adaptable**: +- Desktop-first (clinical use at workstations) +- Forms are complex (HIPAA compliance) +- Errors are precise with next steps + +--- + +## KEY TAKEAWAY + +**The system flexibility framework lets you:** +- Maintain consistency (fixed elements) +- Express brand personality (project-specific) +- Adapt to context (adaptable elements) + +**Without this framework:** +- Designers reinvent spacing every project +- Components feel inconsistent across products +- Brand personality overrides accessibility +- Context-blind implementations feel wrong + +**With this framework:** +- Speed: Start from proven foundations +- Consistency: Fixed elements guarantee it +- Flexibility: Express unique brand identity +- Context: Adapt without breaking system diff --git a/.claude/skills/bencium-innovative-ux-designer/MOTION-SPEC.md b/.claude/skills/bencium-innovative-ux-designer/MOTION-SPEC.md new file mode 100644 index 0000000..e37e363 --- /dev/null +++ b/.claude/skills/bencium-innovative-ux-designer/MOTION-SPEC.md @@ -0,0 +1,72 @@ +# Motion Specification + +Motion should surprise and delight while serving function. Animation is a creative tool. + +## Easing Curves + +| Easing | CSS | Use For | +|--------|-----|---------| +| **Ease-out** | `cubic-bezier(0.0, 0.0, 0.2, 1)` | Entrances, appearing | +| **Ease-in** | `cubic-bezier(0.4, 0.0, 1, 1)` | Exits, disappearing | +| **Ease-in-out** | `cubic-bezier(0.4, 0.0, 0.2, 1)` | State changes, transforms | +| **Spring** | `cubic-bezier(0.68, -0.55, 0.265, 1.55)` | Playful, attention-grabbing | +| **Linear** | `linear` | Spinners, continuous loops | + +## Duration by Element Weight + +| Weight | Duration | Examples | +|--------|----------|----------| +| **Lightweight** | 150ms | Icons, badges, chips | +| **Standard** | 300ms | Cards, panels, list items | +| **Weighty** | 500ms | Modals, page transitions | + +## Duration by Interaction + +| Interaction | Duration | +|-------------|----------| +| Button press | 100ms | +| Hover state | 150ms | +| Tooltip appear | 200ms | +| Tab switch | 250ms | +| Modal open | 300ms | +| Page transition | 400ms | + +## Common Patterns + +```tsx +// Hover transition (CSS) + +``` + +### Example 2: Typography Hierarchy + +**Question First:** +``` +For this content hierarchy, I'm thinking: +- Headline: Large, bold, attention-grabbing +- Subheading: Medium, regular weight +- Body: Smaller, optimized for reading + +Should we go bold and contemporary, or subtle and refined? +``` + +**Implementation After Approval:** +```tsx +
+

+ Headline Here +

+

+ Supporting subheading +

+

+ Body text optimized for readability with proper line height. +

+
+``` + +### Example 3: Unique Color Palette Suggestion + +**Presenting Options:** +``` +I'd like to suggest three color directions that break from typical SaaS patterns: + +1. **Warm Earth Tones** + - Base: Warm grey (#E8E2DC) + - Accent: Terracotta (#C86E4B) + - For: Organic, trustworthy feel + +2. **Cool Midnight** + - Base: Deep navy (#1A2332) + - Accent: Cyan (#4ECDC4) + - For: Modern, tech-forward feel + +3. **Soft Pastels** + - Base: Soft pink (#FFE5E5) + - Accent: Sage green (#9DB5A4) + - For: Calm, approachable feel + +Which direction feels right for your brand? +``` + +## Common Patterns to Avoid + +❌ **NEVER:** +- Use Inter, Roboto, Arial, Space Grotesk as primary fonts +- Use generic SaaS blue (#3B82F6) or purple gradients on white +- Copy Apple's design language or use glass morphism +- Create cookie-cutter layouts that look AI-generated +- Skip asking about context before designing +- Converge on common choices across generations (vary everything!) +- Use animations that delay user actions +- Create cluttered interfaces where elements compete + +✅ **ALWAYS:** +- Ask about purpose, tone, constraints, differentiation FIRST +- Then commit BOLDLY to a distinctive aesthetic direction +- Use unexpected, characterful typography choices +- Create atmosphere: shadows, gradients, textures, grain (when intentional) +- Dominant colors with sharp accents (not timid, evenly-distributed palettes) +- Provide immediate feedback for interactions +- Test with real devices +- Validate accessibility (it enables creativity, not limits it) +- Remember: Claude is capable of extraordinary creative work - don't hold back! + +## Version History + +- v2.0.0 (2025-11-22): Creative liberation update - bold aesthetics, shadows/gradients allowed, Design Thinking protocol +- v1.0.0 (2025-10-18): Initial release with comprehensive UI/UX design guidance + +## References + +For additional context, see: +- **Anthropic Frontend Aesthetics Cookbook**: https://github.com/anthropics/claude-cookbooks/blob/main/coding/prompting_for_frontend_aesthetics.ipynb +- WCAG 2.1 Guidelines: https://www.w3.org/WAI/WCAG21/quickref/ +- Google Fonts: https://fonts.google.com/ +- Tailwind CSS Docs: https://tailwindcss.com/docs +- Shadcn UI Components: https://ui.shadcn.com/ + +**Progressive Disclosure Files:** +- ACCESSIBILITY.md - Accessibility essentials (WCAG AA baseline) +- MOTION-SPEC.md - Animation timing and easing +- RESPONSIVE-DESIGN.md - Mobile-first breakpoints and patterns diff --git a/.claude/skills/interactive-portfolio/SKILL.md b/.claude/skills/interactive-portfolio/SKILL.md new file mode 100644 index 0000000..110f519 --- /dev/null +++ b/.claude/skills/interactive-portfolio/SKILL.md @@ -0,0 +1,223 @@ +--- +name: interactive-portfolio +description: "Expert in building portfolios that actually land jobs and clients - not just showing work, but creating memorable experiences. Covers developer portfolios, designer portfolios, creative portfolios, and portfolios that convert visitors into opportunities. Use when: portfolio, personal website, showcase work, developer portfolio, designer portfolio." +source: vibeship-spawner-skills (Apache 2.0) +--- + +# Interactive Portfolio + +**Role**: Portfolio Experience Designer + +You know a portfolio isn't a resume - it's a first impression that needs +to convert. You balance creativity with usability. You understand that +hiring managers spend 30 seconds on each portfolio. You make those 30 +seconds count. You help people stand out without being gimmicky. + +## Capabilities + +- Portfolio architecture +- Project showcase design +- Interactive case studies +- Personal branding for devs/designers +- Contact conversion +- Portfolio performance +- Work presentation +- Testimonial integration + +## Patterns + +### Portfolio Architecture + +Structure that works for portfolios + +**When to use**: When planning portfolio structure + +```javascript +## Portfolio Architecture + +### The 30-Second Test +In 30 seconds, visitors should know: +1. Who you are +2. What you do +3. Your best work +4. How to contact you + +### Essential Sections +| Section | Purpose | Priority | +|---------|---------|----------| +| Hero | Hook + identity | Critical | +| Work/Projects | Prove skills | Critical | +| About | Personality + story | Important | +| Contact | Convert interest | Critical | +| Testimonials | Social proof | Nice to have | +| Blog/Writing | Thought leadership | Optional | + +### Navigation Patterns +``` +Option 1: Single page scroll +- Best for: Designers, creatives +- Works well with animations +- Mobile friendly + +Option 2: Multi-page +- Best for: Lots of projects +- Individual case study pages +- Better for SEO + +Option 3: Hybrid +- Main sections on one page +- Detailed case studies separate +- Best of both worlds +``` + +### Hero Section Formula +``` +[Your name] +[What you do in one line] +[One line that differentiates you] +[CTA: View Work / Contact] +``` +``` + +### Project Showcase + +How to present work effectively + +**When to use**: When building project sections + +```javascript +## Project Showcase + +### Project Card Elements +| Element | Purpose | +|---------|---------| +| Thumbnail | Visual hook | +| Title | What it is | +| One-liner | What you did | +| Tech/tags | Quick scan | +| Results | Proof of impact | + +### Case Study Structure +``` +1. Hero image/video +2. Project overview (2-3 sentences) +3. The challenge +4. Your role +5. Process highlights +6. Key decisions +7. Results/impact +8. Learnings (optional) +9. Links (live, GitHub, etc.) +``` + +### Showing Impact +| Instead of | Write | +|------------|-------| +| "Built a website" | "Increased conversions 40%" | +| "Designed UI" | "Reduced user drop-off 25%" | +| "Developed features" | "Shipped to 50K users" | + +### Visual Presentation +- Device mockups for web/mobile +- Before/after comparisons +- Process artifacts (wireframes, etc.) +- Video walkthroughs for complex work +- Hover effects for engagement +``` + +### Developer Portfolio Specifics + +What works for dev portfolios + +**When to use**: When building developer portfolio + +```javascript +## Developer Portfolio + +### What Hiring Managers Look For +1. Code quality (GitHub link) +2. Real projects (not just tutorials) +3. Problem-solving ability +4. Communication skills +5. Technical depth + +### Must-Haves +- GitHub profile link (cleaned up) +- Live project links +- Tech stack for each project +- Your specific contribution (for team projects) + +### Project Selection +| Include | Avoid | +|---------|-------| +| Real problems solved | Tutorial clones | +| Side projects with users | Incomplete projects | +| Open source contributions | "Coming soon" | +| Technical challenges | Basic CRUD apps | + +### Technical Showcase +```javascript +// Show code snippets that demonstrate: +- Clean architecture decisions +- Performance optimizations +- Clever solutions +- Testing approach +``` + +### Blog/Writing +- Technical deep dives +- Problem-solving stories +- Learning journeys +- Shows communication skills +``` + +## Anti-Patterns + +### ❌ Template Portfolio + +**Why bad**: Looks like everyone else. +No memorable impression. +Doesn't show creativity. +Easy to forget. + +**Instead**: Add personal touches. +Custom design elements. +Unique project presentations. +Your voice in the copy. + +### ❌ All Style No Substance + +**Why bad**: Fancy animations, weak projects. +Style over substance. +Hiring managers see through it. +No proof of skills. + +**Instead**: Projects first, style second. +Real work with real impact. +Quality over quantity. +Depth over breadth. + +### ❌ Resume Website + +**Why bad**: Boring, forgettable. +Doesn't use the medium. +No personality. +Lists instead of stories. + +**Instead**: Show, don't tell. +Visual case studies. +Interactive elements. +Personality throughout. + +## ⚠️ Sharp Edges + +| Issue | Severity | Solution | +|-------|----------|----------| +| Portfolio more complex than your actual work | medium | ## Right-Sizing Your Portfolio | +| Portfolio looks great on desktop, broken on mobile | high | ## Mobile-First Portfolio | +| Visitors don't know what to do next | medium | ## Portfolio CTAs | +| Portfolio shows old or irrelevant work | medium | ## Portfolio Freshness | + +## Related Skills + +Works well with: `scroll-experience`, `3d-web-experience`, `landing-page-design`, `personal-branding` diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..efb7178 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,72 @@ +# AGENTS.md + +This file provides guidance to AI agents (OpenCode, Claude Code, etc.) when working with code in this repository. + +## Project Overview + +Interactive CV/portfolio website for Andy Charlwood with a distinctive loading experience: terminal boot sequence → ECG canvas animation with name tracing. Built as a React SPA with TypeScript and Vite. + +## Commands + +- `npm run dev` — Start dev server (localhost:5173) +- `npm run build` — TypeScript compile + Vite production build +- `npm run typecheck` — TypeScript type checking only (`tsc --noEmit`) +- `npm run lint` — ESLint +- `npm run preview` — Preview production build + +No test framework is configured. + +## Architecture + +### Loading UI Flow + +`App.tsx` manages a `Phase` state (`'boot'` → `'ecg'`). Each phase renders exclusively: + +1. **BootSequence** — Terminal typing animation (~4s), green-on-black aesthetic +2. **ECGAnimation** — Canvas-based heartbeat animation (~5-6s) with letter tracing, background transitions from black to white + +Total boot-to-ECG completion time must be ≤10 seconds. + +### Key Patterns + +- **Canvas ECG**: `ECGAnimation.tsx` does imperative canvas drawing with requestAnimationFrame — flatline → 3 heartbeats (40px→60px→100px) → letter tracing → exit. + +### Path Aliases + +`@/` maps to `./src/` (configured in both `vite.config.ts` and `tsconfig.json`). + +### Styling + +Tailwind CSS with custom design tokens in `tailwind.config.js`: +- **Colors**: teal `#00897B` (primary), coral `#FF6B6B` (accent), ECG palette (green/cyan/dim) +- **Fonts**: Plus Jakarta Sans (primary), Inter Tight (secondary), Fira Code (mono/terminal) +- **Breakpoints**: xs 480px, sm 640px, md 768px, lg 1024px, xl 1280px + +### Type System + +All data types live in `src/types/index.ts`. Strict TypeScript — no `any` types. One component per file with typed props interfaces. + +## Guardrails + +- Boot sequence text and colors must match `References/concept.html` exactly (CLINICAL TERMINAL v3.2.1 format). +- ECG animation timing/amplitudes/color transitions must match the concept reference. +- When writing components with visual styling or animations, invoke the `frontend-design` skill first. + +## Available Skills + +This project has access to the following agent skills in `.agents/skills/`: +- **frontend-design** — Use for any visual styling or animation work + +## Project Structure + +``` +src/ +├── components/ # One component per file (PascalCase) +├── hooks/ # Custom hooks (camelCase, use* prefix) +├── lib/ # Utility functions +├── types/ # TypeScript interfaces +├── App.tsx # Phase manager (root component) +└── index.css # Global styles + Tailwind directives +Ralph/ # Implementation plan, guardrails, progress tracking +References/ # Source content (concept.html, ECGVideo/) +``` diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..bcd83e7 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,75 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Project Overview + +Interactive CV/portfolio website for Andy Charlwood with a distinctive three-phase loading experience: terminal boot sequence → ECG canvas animation → main content. Built as a React SPA with TypeScript and Vite. + +## Commands + +- `npm run dev` — Start dev server (localhost:5173) +- `npm run build` — TypeScript compile + Vite production build +- `npm run typecheck` — TypeScript type checking only (`tsc --noEmit`) +- `npm run lint` — ESLint +- `npm run preview` — Preview production build + +No test framework is configured. + +## Architecture + +### Three-Phase UI Flow + +`App.tsx` manages a `Phase` state (`'boot'` → `'ecg'` → `'content'`). Each phase renders exclusively: + +1. **BootSequence** — Terminal typing animation (~4s), green-on-black aesthetic +2. **ECGAnimation** — Canvas-based heartbeat animation (~5-6s) with letter tracing, background transitions from black to white +3. **Content** — FloatingNav + all CV sections (Hero, Skills, Experience, Education, Projects, Contact, Footer) + +Total boot-to-content time must be ≤10 seconds. + +### Key Patterns + +- **Scroll reveals**: `useScrollReveal` hook wraps IntersectionObserver with trigger-once semantics. Used by every content section. Never use scroll event listeners. +- **Active nav tracking**: `useActiveSection` hook tracks which section is in viewport for FloatingNav highlighting. +- **Staggered animations**: Components use index-based delays (`baseDelay + index * 100`) with Framer Motion. +- **SVG skill circles**: `Skills.tsx` uses `strokeDashoffset = circumference * (1 - level / 100)` with `-90deg` rotation to start from 12 o'clock. +- **Canvas ECG**: `ECGAnimation.tsx` does imperative canvas drawing with requestAnimationFrame — flatline → 3 heartbeats (40px→60px→100px) → letter tracing → exit. + +### Path Aliases + +`@/` maps to `./src/` (configured in both `vite.config.ts` and `tsconfig.json`). + +### Styling + +Tailwind CSS with custom design tokens in `tailwind.config.js`: +- **Colors**: teal `#00897B` (primary), coral `#FF6B6B` (accent), ECG palette (green/cyan/dim) +- **Fonts**: Plus Jakarta Sans (primary), Inter Tight (secondary), Fira Code (mono/terminal) +- **Breakpoints**: xs 480px, sm 640px, md 768px, lg 1024px, xl 1280px +- Inline styles only for dynamic values that Tailwind can't express (e.g., computed `strokeDashoffset`). + +### Type System + +All data types live in `src/types/index.ts`. Strict TypeScript — no `any` types. One component per file with typed props interfaces. + +## Guardrails + +- Boot sequence text and colors must match `References/concept.html` exactly (CLINICAL TERMINAL v3.2.1 format). +- ECG animation timing/amplitudes/color transitions must match the concept reference. +- CV content sourced from `References/CV_v4.md` — roles, dates, and achievement numbers must be accurate. +- Icons via `lucide-react`, not unicode symbols. +- When writing components with visual styling or animations, invoke the `/frontend-design` skill first. + +## Project Structure + +``` +src/ +├── components/ # One component per file (PascalCase) +├── hooks/ # Custom hooks (camelCase, use* prefix) +├── lib/ # Utility functions +├── types/ # TypeScript interfaces +├── App.tsx # Phase manager (root component) +└── index.css # Global styles + Tailwind directives +Ralph/ # Implementation plan, guardrails, progress tracking +References/ # Source content (concept.html, CV_v4.md, ECGVideo/) +``` diff --git a/README.md b/README.md new file mode 100644 index 0000000..62dca2a --- /dev/null +++ b/README.md @@ -0,0 +1,70 @@ +# Andy Charlwood - Interactive CV + +A distinctive interactive portfolio website featuring a three-phase cinematic loading experience: terminal boot sequence → ECG heartbeat animation → main content. Built with React, TypeScript, and Vite. + +## Features + +- **Three-Phase Loading Experience**: Terminal boot (~4s) → ECG animation (~5-6s) → content reveal +- **Interactive Sections**: Hero, Skills, Experience, Education, Projects, Contact +- **Smooth Animations**: Framer Motion for scroll reveals and staggered transitions +- **SVG Skill Visualization**: Circular progress indicators for skill levels +- **Floating Navigation**: Active section tracking as you scroll +- **Responsive Design**: Tailwind CSS with custom breakpoints + +## Tech Stack + +- **Framework**: React 18 + TypeScript +- **Build Tool**: Vite 6 +- **Styling**: Tailwind CSS 3 +- **Animations**: Framer Motion + Canvas API +- **Icons**: Lucide React +- **Linting**: ESLint 9 + +## Getting Started + +```bash +# Install dependencies +npm install + +# Start development server +npm run dev + +# Build for production +npm run build + +# Preview production build +npm run preview +``` + +## Available Scripts + +| Command | Description | +|---------|-------------| +| `npm run dev` | Start dev server (localhost:5173) | +| `npm run build` | TypeScript compile + Vite production build | +| `npm run typecheck` | TypeScript type checking only | +| `npm run lint` | Run ESLint | +| `npm run preview` | Preview production build | + +## Project Structure + +``` +src/ +├── components/ # React components (PascalCase) +├── hooks/ # Custom hooks (use* prefix) +├── lib/ # Utility functions +├── types/ # TypeScript interfaces +├── App.tsx # Phase manager (root component) +└── index.css # Global styles + Tailwind +``` + +## Design Tokens + +- **Primary**: Teal `#00897B` +- **Accent**: Coral `#FF6B6B` +- **Fonts**: Plus Jakarta Sans (primary), Inter Tight (secondary), Fira Code (mono) +- **Breakpoints**: xs 480px, sm 640px, md 768px, lg 1024px, xl 1280px + +## License + +Private - All rights reserved. diff --git a/designs/01-the-compression.md b/designs/01-the-compression.md new file mode 100644 index 0000000..dab3e88 --- /dev/null +++ b/designs/01-the-compression.md @@ -0,0 +1,522 @@ +# Design 1: The Compression + +> A scroll-driven storytelling experience in 3 acts that ENACTS Andy's core skill — compressing raw data chaos into clean insight. + +--- + +## Overview + +The Compression is a scrollytelling portfolio that transforms the act of reading a CV into an emotional experience. The page is structured as a three-act narrative controlled entirely by scroll position. The user doesn't just learn that Andy compresses months of manual analysis into 3 days — they FEEL it. + +**Act 1 "The Raw Data"** overwhelms the user with a wall of simulated prescribing data — drug names, BNF codes, costs, patient IDs — scrolling upward in green monospaced text on black. It's deliberately uncomfortable. This is the problem Andy solves every day. + +**Act 2 "The Algorithm"** transforms the chaos in real-time as the user scrolls. Data lines cluster, group, sort, and collapse. Career cards appear during the transformation, each representing a stage in Andy's growing capability. The transformation becomes more sophisticated as roles progress from pharmacy management to population health analytics. + +**Act 3 "The Insight"** delivers the payoff: clean, minimal output. Key numbers as beautiful data cards. Skills as animated gauges. Education and projects in calm, white-space-rich layout. The emotional contrast with Act 1 is the entire point. + +The scroll position is the playback head. Fast scrollers get the highlights. Slow scrollers get the full show. Scrolling backward reverses everything. The user controls the pace of revelation — exactly how Andy controls the pace of a stakeholder presentation. + +### Why This Design + +Scroll-driven storytelling achieves 400% higher engagement than static content. But more importantly, this design doesn't just DESCRIBE Andy's value proposition — it DEMONSTRATES it. By the time a recruiter reaches Act 3, they've viscerally experienced what it feels like to have raw data compressed into clean insight. That's Andy's pitch, made physical. + +--- + +## ECG Transition + +**Starting frame:** Andy's name, neon green (#00FF41), on pure black. Static. + +### Sequence (2.2 seconds total) + +1. **Destabilize** (400ms): The neon green letterforms of Andy's name begin to flicker — not uniformly, but character-by-character, as if each letter is a data point losing coherence. Individual pixels at the edges of the letters start detaching, drifting 1-2px from their positions. The name is becoming unstable. + +2. **Decompose** (600ms): The letters break apart completely. Each character disintegrates into a small cluster of monospaced character fragments — not random pixels, but recognizable text fragments: drug names, BNF codes, cost figures, patient IDs. The fragments scatter outward from each letter's position, decelerating with spring physics. The green shifts from neon (#00FF41) to a dimmer data-green (#3a6b45) as fragments spread. + +3. **Grid snap** (500ms): The scattered fragments snap into grid positions — monospaced rows, left-aligned, filling the viewport. They're now readable as lines of simulated prescribing data. The grid formation happens with a satisfying staccato rhythm, rows snapping into place from top to bottom with 20ms stagger. The name "ANDY CHARLWOOD" dissolves last, its characters reassembling into a header row at the top of the data wall: `PATIENT_DATASET // CHARLWOOD.A // NORFOLK_ICB`. + +4. **Data wall live** (200ms): The data wall begins scrolling upward automatically for a brief moment (2-3 rows), establishing the scrolling data aesthetic. Then it pauses, waiting for the user's scroll input. The background has remained black throughout — no seam between the intro and Act 1. The transition IS Act 1 beginning. + +### Why This Transition Works + +There is no seam. The neon green name from the ECG intro literally decomposes into the raw data that forms Act 1's visual foundation. The user's eye follows a continuous transformation: name → fragments → data rows. The emotional shift is from "that was a cool animation" to "wait, what is all this data?" — which is exactly the disorientation Act 1 is designed to create. + +--- + +## Visual System + +### Color Journey (Scroll-Driven) + +The entire page's color palette transitions continuously as the user scrolls, creating an unmistakable sense of progression: + +| Scroll Position | Background | Text Primary | Accent | Emotional Register | +|----------------|------------|-------------|--------|-------------------| +| 0% (Act 1 start) | Black #000000 | ECG green #00FF41 | — | Overwhelm, clinical | +| 15% (Act 1 mid) | Black #000000 | Dim green #3a6b45 | — | Dense, relentless | +| 30% (Act 2 start) | Charcoal #1e293b | Dim green → slate #94a3b8 | Teal #00897B | Transformation beginning | +| 50% (Act 2 mid) | Slate #334155 | Light slate #e2e8f0 | Teal #00897B | Organization, clarity | +| 70% (Act 3 start) | Light gray #f8fafc | Charcoal #1e293b | Teal #00897B | Relief, clean | +| 100% (Act 3 end) | White #FFFFFF | Dark #0f172a | Cyan accent #00D4AA | Confidence, resolution | + +The background transition is implemented as a continuous CSS custom property (`--bg-progress`) mapped to scroll position, interpolating between color stops. No hard cuts — the eye never perceives a boundary between acts. + +### Typography + +Three typefaces, each with a clear role in the narrative: + +- **IBM Plex Mono 400** — The data voice. Used for all raw data text in Act 1, metric numbers throughout, code snippets, and the header row. Set at 13px/1.6 in the data wall, 16px/1.4 for inline metrics. This is the typeface of the problem. + +- **Space Grotesk 500, 700** — The heading voice. Used for section headings, role titles, and the name in the hero. Set at 32-48px for section headings, 24px for role titles. Weight 700 for primary headings, 500 for subheadings. This is the typeface of structure. + +- **IBM Plex Sans 400, 450** — The body voice. Used for all descriptive text, bullet points, and the profile summary. Set at 16px/1.7 for body text, 14px/1.6 for secondary text. Weight 450 (slightly heavier than regular) for body text to maintain readability against busy backgrounds. This is the typeface of insight. + +### Texture and Ambient Elements + +- **Dot grid**: A faint grid of dots at 3% opacity, visible from Act 2 onward. Grid spacing 24px. The grid represents structure emerging from chaos — it's not visible in Act 1 (there is no structure yet) but gradually appears as the data organizes. Mouse proximity brightens the nearest grid intersection to 15% opacity within a 60px radius, creating a subtle "spotlight" effect. + +- **Gradient glows**: Behind key data cards and metric numbers in Act 3, soft radial gradients (teal at 8-10% opacity) provide visual warmth and draw the eye. These are 200-300px diameter, centered on each element, and breathe (subtle scale oscillation at 4s period). + +- **Data traces**: Thin horizontal lines (1px, 5% opacity) span the full viewport width behind content in Acts 2-3, suggesting the remnants of the data wall's grid structure. Content sits on these traces like data on a chart. + +### Motion Principles + +- **Easing**: All animations use `cubic-bezier(0.16, 1, 0.3, 1)` — a custom ease-out that starts fast and decelerates smoothly. This gives everything a confident, decisive feel, matching the "compression" metaphor (fast analysis, clean output). + +- **Scroll-driven**: Every animation is mapped to scroll position via normalized 0-1 progress values. No time-based animations in the main content (except ambient loops like the gradient glow breathing). The user IS the timeline. + +- **Number rendering**: Metric numbers render digit-by-digit at 30ms per digit when counting up. The count rate is tied to scroll velocity — scroll faster, numbers count faster. This creates a visceral connection between user effort and data processing. + +- **SVG path drawing**: All drawn lines (timeline paths, skill bar fills, education path) animate via `stroke-dashoffset` mapped to scroll progress. The drawing direction always follows the data flow direction (left-to-right or top-to-bottom). + +- **GPU compositing**: All transforms use translate3d, opacity, or scale exclusively. No animations trigger layout or paint (no width/height/margin animations). This ensures 60fps on mid-range devices. + +--- + +## Section-by-Section Design + +### Act 1: The Raw Data + +**Scroll range:** 0% - 25% of total scroll depth. + +**What the user sees:** A full-viewport wall of monospaced green text on black — simulated prescribing data. Rows contain realistic-looking drug names, BNF codes, practice codes, cost figures, and patient counts. The data scrolls upward at a rate proportional to the user's scroll, creating a "Matrix" effect but with real pharmaceutical data terminology. + +**Data wall composition:** +``` +BNF 0407010H0 MORPHINE SULFATE M/R PJ68043 £14.82 x120 NORFOLK_ICB +BNF 0212000Y0 ATORVASTATIN D81024 £2.16 x890 NORFOLK_ICB +BNF 0601022B0 METFORMIN HCL PJ68043 £1.04 x445 NORFOLK_ICB +BNF 0205051R0 RAMIPRIL D81024 £1.89 x670 NORFOLK_ICB +... +``` + +The data is generated procedurally (not hardcoded) from arrays of real BNF codes, drug names, practice codes, and cost ranges. Each row is unique but plausible. Approximately 200-300 rows are generated, with only ~30 visible at any time. + +**Header row** (persistent at top): `PATIENT_DATASET // CHARLWOOD.A // NORFOLK_ICB` in brighter green (#00FF41), with a subtle underline. This is the remnant of Andy's name from the ECG transition. + +**Scroll behavior:** As the user scrolls, the data wall scrolls upward. The scroll rate is 1.5x the user's scroll speed, creating a slight acceleration that enhances the overwhelming feeling. At 15% scroll, some rows begin to dim (opacity dropping to 30%), creating depth — foreground rows are bright, background rows are faded. + +**Emotional intent:** Discomfort. Information overload. "How does anyone make sense of this?" This is the state of prescribing data before Andy touches it. + +**Ambient detail:** A faint scan line sweeps downward across the data wall every 8 seconds (very subtle, 2% opacity). A tiny blinking cursor sits at the bottom-right of the data wall, suggesting a terminal awaiting input. + +### Act 2: The Algorithm + +**Scroll range:** 25% - 60% of total scroll depth. + +**What the user sees:** The raw data begins to transform. This is the core of the experience — a choreographed sequence of data manipulations that correspond to Andy's career progression. + +**Transformation sequence (mapped to scroll progress within Act 2):** + +**Phase 1 — Sorting (0-20% of Act 2):** Data rows rearrange. Rows with similar BNF codes cluster together. The movement is animated — rows slide vertically to their new positions, creating a satisfying cascade of shifting text. Some rows highlight in teal (#00897B) as they're "selected" by the algorithm. A label appears at screen edge: `SORTING BY BNF_CODE...` + +Simultaneously, the first career card slides in from the right: **Pharmacy Manager, Tesco PLC (2017-2022)**. It's a card with a dark background (#1e293b), rounded corners, and a teal left border. The card contains the role title, date range, and 2-3 key bullets. It appears alongside the sorting transformation, contextualizing it: Andy's first role involved identifying patterns (the asthma screening process adopted nationally). + +**Phase 2 — Grouping (20-45% of Act 2):** Sorted rows collapse into groups. 10 individual rows of the same drug compress into a single summary row showing the drug name, total cost, and patient count. The compression animation is physical — rows accordion inward, stacking on top of each other until only the summary remains. The data wall is visibly shrinking. More whitespace appears between groups. + +The second career card slides in: **High-Cost Drugs & Interface Pharmacist, NHS ICB (2022-2024)**. The role's key achievement — the Blueteq automation (70% form reduction, 200 hours saved) — is visualized as a mini-animation within the card: a stack of form icons compresses to 30% of its original height. + +**Phase 3 — Analysis (45-70% of Act 2):** Grouped data transforms into structured visualizations. Cost figures align into bar segments. Patient counts form columns. The monospaced text is giving way to geometric shapes — rectangles, lines, circles. The background has lightened to slate. The data wall is no longer recognizable as raw text — it's becoming a dashboard. + +The third career card slides in: **Deputy Head, Population Health & Data Analysis (2024-Present)**. The £220M budget management and the switching algorithm achievements appear. Key metric: `14,000 patients identified` counts up from zero as the user scrolls past. + +**Phase 4 — Compression (70-100% of Act 2):** This is the signature moment. All remaining data elements — the bars, columns, shapes — physically compress toward the center of the screen. They funnel through a narrow "processing" zone (visualized as two converging lines forming a V-shape or funnel). On the other side, clean data cards emerge, fully formed. The funnel animation is tied directly to scroll — scroll backward and everything reverses, data expanding back out of the funnel. + +The fourth career card slides in: **Interim Head, Population Health & Data Analysis (2025)**. The £14.6M efficiency programme headline. This number counts up dramatically: `£14,600,000` digit by digit, each digit appearing with a micro-flash of teal light. + +**Background transition:** Throughout Act 2, the background continuously transitions from black (#000000) through charcoal (#1e293b) to slate (#334155). The text color shifts from dim green (#3a6b45) to light slate (#e2e8f0). By the end of Act 2, the page no longer looks like a terminal — it looks like a modern dashboard. + +### Act 3: The Insight + +**Scroll range:** 60% - 100% of total scroll depth. + +**What the user sees:** Clean, beautiful, minimal content. Maximum whitespace. The emotional relief after Acts 1-2 makes this content feel earned and precious. This is "normal" portfolio layout elevated by contrast. + +**Background:** Continues transitioning from slate (#334155) → light gray (#f8fafc) → white (#FFFFFF). By the Skills section, the background is fully white. + +#### Hero (60-65% scroll) + +Andy's name is already visible (persistent header from Act 1). As Act 3 begins, the profile summary text types itself character-by-character synchronized to scroll position. Stop scrolling = stop typing. Resume scrolling = resume typing. The text appears in IBM Plex Sans 450, 18px, charcoal (#1e293b). A thin teal line (#00897B) underscores the summary once complete. + +Below the summary, three "impact pills" fade in with stagger: `£14.6M Efficiency Programme` | `1.2M Population Served` | `£220M Budget Managed`. Each pill has a teal border and a subtle gradient glow. + +#### Skills (65-75% scroll) + +Skills are displayed as horizontal bar charts that draw themselves left-to-right, synchronized to scroll position. The scroll-to-progress mapping means each bar fills as the user scrolls through the skills section. + +**Layout:** Two columns on desktop, single column on mobile. Each row contains: +- Skill name (IBM Plex Sans 450, 15px, left-aligned) +- Horizontal bar (height 8px, rounded ends) +- Proficiency percentage (IBM Plex Mono 400, 14px, right-aligned, counts up as bar fills) + +**Bar fill gradient:** Each bar fills with a gradient that shifts from cool blue (#60a5fa) at 0% to teal (#00897B) at 50% to warm cyan (#00D4AA) at 100%. The gradient position corresponds to the proficiency level, so higher-skilled bars are warmer-colored. + +**Skill categories** are separated by subtle headings (Space Grotesk 500, 13px, uppercase, tracking 0.1em, slate #64748b): +- TECHNICAL: Python, SQL, Power BI, JavaScript/TypeScript, Algorithm Design, Data Pipelines +- HEALTHCARE: Medicines Optimisation, Population Health, NICE Implementation, Health Economics +- LEADERSHIP: Budget Management, Stakeholder Engagement, Team Development, Change Management + +**Interaction:** Hovering a skill bar causes it to brighten slightly and the percentage number to pulse. The nearest dot-grid intersections brighten. A tooltip with a one-line description fades in after 300ms hover dwell. + +#### Experience (75-85% scroll) + +Experience entries are displayed as timeline cards that "assemble" as the user scrolls past each one's trigger point. The assembly is sequential and scroll-driven: + +1. **Title draws** (first 20% of card's scroll range): The role title types itself in Space Grotesk 700, 22px, teal (#00897B). +2. **Company slides in** (20-35%): The company name and date range slide in from the left, IBM Plex Sans 400, 15px, slate (#64748b). +3. **Context line fades** (35-50%): The one-line role context fades in. +4. **Bullets sequence** (50-100%): Each bullet point fades in from below with a 100ms stagger. Key metrics within bullets (£14.6M, 14,000, 200 hours, £2.6M, £1M, 50%) count up from zero as they appear, with the count rate tied to scroll velocity. + +**Timeline visual:** A thin vertical line (2px, teal at 20% opacity) connects the cards. Small nodes (8px circles) mark each role. As the user scrolls past a node, it fills with solid teal and emits a subtle radial pulse animation. + +**Card layout:** Each card has generous padding (32px), a very subtle left border (3px, teal at 40% opacity), and sits on a barely-visible card surface (#f8fafc on white background). On hover, the card surface becomes #f1f5f9 and the left border reaches full teal opacity. + +**Achievement highlights:** Key achievements within each role have metric numbers displayed in IBM Plex Mono 700, teal (#00897B), with a faint gradient glow behind them. These are the numbers that counted up from zero — they remain vivid and prominent. + +Note: The career cards from Act 2 are NOT repeated here. Act 2 showed the career in the context of transformation. Act 3's Experience section provides the complete, detailed content. However, if the user scrolls back to Act 2, the career cards there are still visible and interactive. The two views complement each other — Act 2 is the narrative, Act 3 is the reference. + +#### Education (85-92% scroll) + +A winding SVG path draws itself as the user scrolls, connecting education milestones. The path is a gentle S-curve that moves top-to-bottom, with milestone nodes positioned along it. + +**Path drawing:** The SVG `` has a `stroke-dasharray` equal to its total length and a `stroke-dashoffset` that transitions from total length (invisible) to 0 (fully drawn) mapped to scroll progress. The stroke is 2px, teal (#00897B) at 40% opacity, with a brighter 4px glow version behind it at 15% opacity. + +**Milestone nodes** (positioned along the path): + +1. **A-Levels (2009-2011)**: Mathematics A*, Chemistry B, Politics C. Highworth Grammar School. Node icon: a small graduation cap SVG. +2. **MPharm (2011-2015)**: University of East Anglia, 2:1 Honours. Node icon: a flask/molecule SVG. The research project branches off as a sidebar annotation (a short branching path from the main line): "Drug delivery and cocrystals: 75.1% (Distinction)." +3. **GPhC Registration (2016)**: General Pharmaceutical Council. Node icon: a shield/badge SVG. +4. **Mary Seacole Programme (2018)**: NHS Leadership Academy, 78%. Node icon: a leadership/star SVG. + +Each node starts as an empty circle (2px border, no fill). As the drawn path reaches the node, it fills with solid teal and a label card fades in beside it. The branch for the research project draws after the MPharm node fills. + +#### Projects (92-97% scroll) + +Each project occupies approximately one-third of a viewport height. As the user scrolls INTO a project, its visualization builds in real-time: + +**Project 1 — Switching Algorithm:** +A network of small dots (representing patients) appears scattered randomly. As the user scrolls, the dots route through a funnel visualization (two converging lines). On the output side, they emerge organized into groups. A counter shows: `14,000 patients identified → £2.6M annual savings`. The funnel is the algorithm. The dots are the patients. The counter ties it to impact. + +**Project 2 — Blueteq Automation:** +A stack of form icons (representing prior approval forms) appears on the left. As the user scrolls, 70% of the forms slide off-screen (fade out to the left), leaving 30% remaining. A counter shows: `70% reduction | 200 hours saved | 7-8 hrs/week ongoing`. The visual is simple and devastating — most of the work just disappears. + +**Project 3 — Sankey Chart Tool:** +An actual mini Sankey diagram draws itself as the user scrolls. Colored flows move from left-side nodes (drug categories) through middle nodes (treatment stages) to right-side nodes (outcomes). The flows animate with a flowing particle effect along the paths. This is a working visualization of what Andy built. + +**Project 4 — Controlled Drug Monitoring:** +A timeline visualization showing a patient's morphine equivalent exposure over time. A line chart draws itself left-to-right with scroll, with a horizontal threshold line marking "high risk." When the drawn line crosses the threshold, it changes color from teal to coral (#FF6B6B) and pulses. Counter: `Population-scale patient safety analysis`. + +#### Contact (97-100% scroll) + +The scroll reaches "the end of the data." A summary card appears, pulling together the key numbers from the entire page into a single impact statement: + +``` +£14.6M efficiency programme identified +14,000 patients flagged by algorithm +£2.6M annual savings on target +1.2 million population served +``` + +Each number is displayed in IBM Plex Mono 700, 28px, teal, with a gentle gradient glow. They appear with staggered fade-in as the user scrolls to the final section. + +Below the summary, the contact form slides up as the final "output" of the data pipeline. The form has a minimal design: Name, Email, Message fields with clean borders, a teal submit button, and contact details (email, phone, location) displayed alongside. + +A subtle callback to Act 1: the form's background has a barely-visible (1% opacity) pattern of the raw data text from the data wall, visible only on close inspection. The data is still there — it's just been compressed into clean insight. + +--- + +## Interactions and Micro-interactions + +### The Living Grid (Ambient) + +A faint dot grid (3% opacity, 24px spacing) covers the viewport from Act 2 onward. This grid is interactive: + +- **Mouse proximity**: The nearest grid intersection to the cursor brightens to 15% opacity, with 2-3 adjacent intersections at 8% opacity. Creates a subtle "spotlight" effect as the user moves their mouse. Radius ~60px. +- **Scroll activity**: When the user is actively scrolling, grid intersections along the scroll direction briefly flash (5% → 10% → 5% over 200ms), creating a cascading "data processing" ripple. +- **Section transitions**: When crossing from one section to another, a horizontal wave of grid brightening sweeps across the viewport (left to right, 400ms), marking the boundary. + +Implementation: CSS custom properties for grid opacity, updated via requestAnimationFrame tied to mouse position and scroll events. The grid is a repeating CSS background pattern, not individual DOM elements. + +### Number Count-ups + +Every significant metric in the document counts up from zero to its final value: + +- Count rate is proportional to scroll velocity (faster scroll = faster count) +- Numbers render digit-by-digit at 30ms per digit for large numbers (e.g., £14,600,000 takes ~270ms at base rate) +- A brief teal flash illuminates each digit as it appears +- Once fully counted, numbers hold their final value permanently (no re-counting on re-scroll) +- Scrolling backward past a number's trigger point smoothly counts it back down to zero + +Implementation: Custom `useScrollCountUp` hook. Accepts target number, scroll range (start/end percentage), and formatting options. Returns the current display value based on scroll position. Uses `useTransform` from Framer Motion to map scroll progress to number value. + +### Card Assembly Animations + +Experience and project cards build themselves as the user scrolls: + +- Each card has 4-6 sub-elements that animate sequentially +- The sequence is tied to scroll progress within the card's trigger range +- Easing is `cubic-bezier(0.16, 1, 0.3, 1)` for all movements +- Elements animate in from consistent directions: titles type-in, subtitles slide from left, body text fades from below, metrics scale up from zero +- Scrolling backward reverses the assembly — elements retreat in reverse order + +### Data Wall Interactions (Act 1) + +The data wall is primarily passive (scroll-driven), but has two subtle interactive layers: + +- **Row highlighting**: The row nearest to the viewport center has slightly brighter text (50% → 70% opacity). Adjacent rows are progressively dimmer. This creates a "focused row" effect that tracks with scroll. +- **Mouse hover**: Hovering over a specific data row highlights it in brighter green and displays a tiny tooltip: "1 of 247,000 prescribing records" (or similar contextual text). This reinforces that each row represents real data. + +### Scroll Progress Indicator + +A thin progress bar sits at the top of the viewport (2px height, full width): +- **Color**: Transitions through the same color journey as the page (green → teal → cyan) +- **Width**: Maps directly to scroll percentage (0% = left edge, 100% = full width) +- **Act markers**: Three small notches at 25%, 60%, and 100% mark the act boundaries +- **Label**: A tiny "Act 1/3", "Act 2/3", "Act 3/3" label sits above the progress bar, updating at act boundaries + +--- + +## Navigation + +### Persistent Header + +A minimal header sits at the top of the viewport with `position: fixed`: +- **Content**: Andy's name (Space Grotesk 700, 16px) on the left, act indicator on the right +- **Appearance**: Transparent in Act 1 (text in green), transitions to a subtle frosted-glass background (`backdrop-filter: blur(12px)`, white at 80% opacity) in Act 3 +- **Act navigation**: Three dots in the header represent the three acts. The active act's dot is filled teal. Clicking a dot smooth-scrolls to that act's start position. + +### Skip to Content + +For users who want to bypass the narrative experience: +- A "Skip to CV →" link appears at bottom-right during Acts 1-2 (IBM Plex Sans 400, 14px, teal) +- Clicking it smooth-scrolls directly to Act 3 (the clean CV content) +- The link disappears once the user reaches Act 3 + +### Section Navigation (Act 3) + +Within Act 3, a floating side navigation appears (similar to the existing FloatingNav): +- Small dots aligned vertically on the right edge +- Each dot corresponds to a section: Skills, Experience, Education, Projects, Contact +- Active section dot is filled teal, others are outlined +- Clicking a dot smooth-scrolls to that section +- Dots only appear when Act 3 is active + +### Keyboard Navigation + +- Arrow Up/Down: Scroll by section +- 1/2/3: Jump to Act 1/2/3 +- Escape: Skip to Act 3 (same as "Skip to CV") +- Tab: Focuses interactive elements in DOM order + +--- + +## Responsive Strategy + +### Desktop (>1024px) + +The full experience: data wall with 80-character rows, wide career cards alongside the transformation, two-column skill bars, generous whitespace in Act 3. The dot-grid ambient effect is active. Mouse interactions (hover, proximity) are fully enabled. Data wall shows ~30 visible rows at a time. + +### Tablet (768px - 1024px) + +Simplified data wall with 50-character rows (truncated BNF data). Career cards in Act 2 appear below the transformation area rather than alongside. Single-column skill bars. The dot-grid effect is reduced to major intersections only (48px spacing). Data wall shows ~25 visible rows. + +### Mobile (<768px) + +The scroll-driven narrative is preserved — this is scroll's native strength. Key adaptations: + +- **Data wall**: 30-character rows, ~20 visible at a time. Fewer data fields per row (drug name + cost only). The overwhelming effect is maintained through density rather than width. +- **Act 2 transformation**: Simplified grouping animations (rows collapse in place rather than rearranging). Career cards appear in-flow, not overlaid. +- **Act 3**: Single-column layout throughout. Skill bars are full-width. Timeline cards are full-width with left border. Projects stack vertically with reduced visualization complexity (Sankey chart becomes a simplified flow, funnel is a simple before/after). +- **Ambient effects**: Dot-grid disabled. Gradient glows reduced to 5% opacity. Scroll progress bar and act indicators remain. +- **Touch**: All scroll-driven animations work identically with touch scroll. Hover interactions (grid brightening, card hover states) are disabled. + +### Ultra-wide (>1440px) + +Content is capped at 1200px max-width. The data wall extends to full viewport width (data rows span the entire screen). The extra horizontal space enhances the "wall of data" effect in Act 1. + +--- + +## Technical Implementation + +### Scroll Engine + +The scroll system is the backbone of the entire experience. It maps a single scroll position to multiple parallel animation timelines. + +``` +Architecture: +- Total scroll depth: ~4x viewport height (tuned for comfortable scroll pace) +- Framer Motion useScroll() provides scrollYProgress (0 to 1) +- useTransform() maps scrollYProgress ranges to individual animation values +- Each section registers its scroll range via a config object: + { start: 0.6, end: 0.75, ... } → Skills section occupies 60-75% of scroll +- Within each section, sub-animations are further mapped to the section's 0-1 range +``` + +### Data Wall Generation + +The Act 1 data wall is procedurally generated at mount time: + +``` +Data arrays: +- ~50 real BNF codes (from public BNF data) +- ~80 drug names (generic names, publicly available) +- ~20 practice codes (anonymized format: PJ68xxx, D81xxx) +- Cost ranges (£0.50 - £200.00, realistic distributions) +- Patient counts (x50 - x2000) + +Generation: +- 250-300 rows generated by randomly combining array elements +- Each row is a pre-formatted string matching fixed-width columns +- Rows are memoized (React.useMemo) — no re-generation on scroll +- Only ~30 rows are rendered at any time (virtualized list) +``` + +### Scroll-Driven Background + +The background color transitions via CSS custom properties: + +``` +Implementation: +- A single --scroll-progress CSS variable (0 to 1) updated via requestAnimationFrame +- Background uses a multi-stop gradient positioned by --scroll-progress +- Gradient stops correspond to act boundaries +- The gradient is applied to a fixed, full-viewport background div +- No JavaScript per-frame color calculation — the browser interpolates +``` + +### Number Counter Hook + +``` +useScrollCountUp(target, scrollRange, options): + - target: final number (e.g., 14600000) + - scrollRange: { start: 0.78, end: 0.82 } — scroll range where count happens + - options: { prefix: '£', separator: ',', digits: true } + - Returns: formatted string of current value based on scroll position + - Uses Framer Motion useTransform to map scroll → number + - digit-by-digit mode: each digit position updates independently at 30ms intervals +``` + +### SVG Path Drawing + +Education path and project visualizations use SVG stroke animation: + +``` +Implementation: +- SVG path has stroke-dasharray = path.getTotalLength() +- stroke-dashoffset transitions from totalLength (hidden) to 0 (visible) +- Offset value is mapped to scroll progress via useTransform +- A second, thicker, blurred path behind creates the glow effect +- Both paths update simultaneously for consistent glow +``` + +### Performance Budget + +- **Target**: 60fps throughout on mid-range devices (4-core CPU, integrated GPU) +- **DOM elements**: <200 in Act 1, <400 in Act 3. Data wall uses virtualization. +- **Canvas**: No canvas used — all effects are CSS/SVG. This simplifies the rendering pipeline. +- **Composited properties only**: All animations use transform (translate3d) or opacity. No width, height, margin, padding, top, left animations. +- **will-change**: Applied to elements that animate frequently (data wall rows, card elements, background div) +- **IntersectionObserver**: Used to disable off-screen animations. Sections outside the viewport don't compute scroll mappings. +- **Bundle**: Framer Motion tree-shaken to ~30kb gzip. No D3 dependency. Total JS budget: <80kb gzip. + +### Reduced Motion + +When `prefers-reduced-motion: reduce` is active: + +- Data wall shows a static screenshot-like snapshot (no scrolling data) +- Act structure is removed — all content displays as a standard scrolling page +- Section reveals use simple opacity fades (200ms) instead of assembly animations +- Number counters display final values immediately (no count-up) +- SVG paths are fully drawn (no progressive draw) +- Dot-grid ambient effect is disabled +- Progress bar remains functional for navigation + +--- + +## Accessibility + +### ARIA Structure + +```html +
+
+ +
+ +
+ +
+ +
+ + +
+
+``` + +### Screen Reader Experience + +Screen readers skip Acts 1-2 decorative content entirely and receive a clean, structured CV: + +1. Andy Charlwood — Profile summary +2. Core Skills (structured list) +3. Professional Experience (chronological, with full role details) +4. Education and Registration +5. Projects (with outcomes and metrics) +6. Contact information + +This is the same content as Act 3, in standard semantic HTML with proper heading hierarchy (h1 → h2 → h3). + +### Keyboard Navigation + +- **Tab order**: Follows logical CV structure regardless of visual act position +- **Skip links**: "Skip to main content" bypasses all decorative elements +- **Act navigation**: Number keys 1-3 jump to acts, clearly labeled in focus order +- **Focus indicators**: All interactive elements have visible focus rings (2px solid teal, 2px offset) + +### Color Contrast + +- Act 1: Green (#00FF41) on black (#000000) = contrast ratio 10.5:1 (AAA) +- Act 2: Light slate (#e2e8f0) on slate (#334155) = contrast ratio 7.2:1 (AAA) +- Act 3: Dark (#0f172a) on white (#FFFFFF) = contrast ratio 17.1:1 (AAA) +- Teal accent (#00897B) on white (#FFFFFF) = contrast ratio 4.56:1 (AA for normal text, AAA for large text) + +### Scroll Depth + +Total scroll depth is capped at approximately 4 viewport heights. This is comfortable for the narrative while not exhausting for keyboard/switch users. The "Skip to CV" shortcut is always available. + +--- + +## What Makes This Special + +1. **It ENACTS the value proposition.** The user doesn't read "I compress months of analysis into 3 days" — they experience overwhelming data being compressed into clean insight. The medium IS the message. + +2. **The emotional arc is engineered.** Act 1 creates discomfort. Act 2 provides relief through transformation. Act 3 delivers resolution. This is the same emotional structure as a great presentation, a compelling film, or a satisfying algorithm — start with the problem, show the process, deliver the result. + +3. **Scroll is the perfect input.** Everyone knows how to scroll. The engagement model is proven (400% higher than static). Fast scrollers get the highlights, slow scrollers get the full experience. It works perfectly on mobile where scroll is native. There's no learning curve, no instructions needed. + +4. **The signature moment — The Compression funnel** — is share-worthy. Watching data physically compress through a funnel into clean output, controlled by your scroll, is viscerally satisfying. It's the moment someone takes a screen recording. + +5. **It respects the recruiter's time.** The "Skip to CV" button is always available. A recruiter in a hurry can jump straight to Act 3 and get a clean, professional CV. A recruiter with time gets the full narrative experience. Two audiences, one site. + +6. **The data is authentic.** The Act 1 data wall uses real BNF codes and drug names. The transformation sequence reflects actual data processing operations (sort → group → aggregate → visualize). Andy's domain expertise is woven into the visual DNA of the site, not just its text content. diff --git a/designs/02-the-dashboard.md b/designs/02-the-dashboard.md new file mode 100644 index 0000000..72210fd --- /dev/null +++ b/designs/02-the-dashboard.md @@ -0,0 +1,624 @@ +# Design 2: The Dashboard + +## Overview + +Andy's CV presented as a live operational dashboard — the kind of analytical interface he builds for the NHS, now turned on himself. The medium IS the message. + +This is not a scrolling portfolio with dashboard "styling." It is a fundamentally different navigation paradigm: **tab-switching views** instead of vertical scroll. Each tab is a self-contained viewport with its own optimized layout — bento grids of metric cards, filterable skill panels, an interactive horizontal timeline, a project portfolio with status badges. The user navigates Andy's career the same way Andy navigates the data systems he builds: by switching views, drilling into detail, and reading quantitative signals at a glance. + +This is the most data-dense of all six designs. It is designed for recruiters, hiring managers, and technical leads who appreciate information density and are comfortable with complex interfaces. It rewards exploration and communicates Andy's analytical mindset before a single word of content is read. + +**Key characteristics:** +- Tab-based view switching replaces scroll-based navigation entirely +- High information density with multiple data points visible simultaneously +- Metric cards with large numbers as the primary content unit +- Adaptive light/dark mode respecting system preference +- Persistent status bar providing ambient context +- Quantitative achievements lead — numbers, not prose + +--- + +## ECG Transition + +**Starting point:** "ANDREW CHARLWOOD" is on screen in neon green (`#00ff41`) on black. The heartbeat trace is complete. The name is fully formed and glowing. + +**Then...** + +### Phase 1: The Name Dims, the Edges Pulse (400ms) + +The neon green letters hold for a beat, then begin to dim — not disappearing, but reducing to approximately 30% opacity. They remain visible as ghosted characters. Simultaneously, the remnant flatline portions of the heartbeat trace (to the left and right of the name) start pulsing with small, rhythmic blips, as if the heartbeat hasn't stopped — it has migrated to the periphery. + +### Phase 2: Multi-Channel Ignition (800ms) + +Two additional horizontal traces draw themselves simultaneously across the full viewport width: + +- **Upper trace** at ~30% viewport height in teal (`#00897B`): draws a steady, regular pulse pattern — the rhythm of structured data +- **Lower trace** at ~70% viewport height in coral (`#FF6B6B`): draws a slower, more organic waveform — the rhythm of clinical observation + +For approximately one second, the screen displays three horizontal traces — teal on top, ghosted green name in the middle, coral on the bottom. The visual effect is a multi-channel patient monitor displaying three simultaneous vital signs. This is a deliberately surprising beat: the user expects the animation to end, and instead it multiplies, signaling that this is a data-rich environment. + +### Phase 3: Simultaneous Flatline (200ms) + +All three traces flatline at once. A synchronized moment of pure stillness. Three horizontal lines on black. The name is still faintly visible. This 200ms pause is deliberate silence — a beat of tension before the transformation. + +### Phase 4: Grid Materialization (400ms) + +From the flatline positions, a grid structure fades in. The three horizontal flatlines become the top edges of bento-grid rows. Vertical dividers descend from the top trace line downward, intersecting the middle and bottom traces, dividing the screen into a grid of cells (4 columns x 3 rows on desktop, adapting to viewport). The verticals draw downward over 400ms, staggered left-to-right at 80ms intervals. They use a dim teal (`rgba(0, 137, 123, 0.2)`). + +The background simultaneously shifts from pure black to deep navy (`#0A1628`). The scanline overlay shifts from black to `rgba(10, 22, 40, 0.03)` — subtle dark-blue scanlines that become part of the dashboard texture rather than disappearing. + +### Phase 5: Content Cascade (500ms) + +The "ANDREW CHARLWOOD" text slides to the top-left corner, scales down, and transitions from ghosted green to clean white. It becomes the dashboard title. The tab bar materializes beside it — each tab label fading in with 80ms stagger. "Overview" receives an active-state underline that draws itself in teal from left to right. + +Each grid cell brightens individually with staggered timing (50ms per cell, top-left to bottom-right). As each cell activates, its KPI value fades in: "10+ years", "14,000 patients", "14.6M", "220M budget", and so on. The cascade reveal takes approximately 500ms for all cells. + +The status bar slides up from the bottom edge of the viewport (the coral trace line becomes the status bar's top border). + +### Phase 6: Final State + +Deep navy dashboard (`#0A1628`) with bento grid of KPI cards, tab bar at top, status bar at bottom. The three ECG traces have literally become the structural lines of the dashboard layout. The heartbeat didn't end — it crystallized into information architecture. + +**Total transition duration:** ~3 seconds + +**Why this works:** The metaphor is precise. Andy takes raw clinical signals (vital signs, prescribing data) and transforms them into organized, actionable dashboards. The transition demonstrates this competency visually. The multi-channel moment is memorable, and the grid materialization provides a satisfying structural resolution. + +--- + +## Visual System: Systematic Clarity + +### Color Palette + +**Adaptive mode** — the dashboard respects `prefers-color-scheme` and provides a manual toggle (persisted to `localStorage`). + +**Light mode:** +- Background: cool white `#FAFAFA` +- Surface/cards: `#FFFFFF` +- Borders: `#E4E4E7` (zinc-200) +- Text primary: `#09090B` (zinc-950) +- Text secondary: `#71717A` (zinc-500) + +**Dark mode:** +- Background: rich black `#09090B` +- Surface/cards: `#18181B` (zinc-900) +- Borders: `#27272A` (zinc-800) +- Text primary: `#FAFAFA` (zinc-50) +- Text secondary: `#A1A1AA` (zinc-400) + +**Accent colors (consistent across modes):** +- Primary blue: `#2563EB` — the dominant interactive color. Used for active tab underlines, primary buttons, link states, and chart elements. +- Emerald: `#10B981` — health/active states. Used for "current" role indicators, active skills, live project badges, and positive metrics. +- Amber: `#F59E0B` — highlights and notable achievements. Used for standout numbers, awards, and attention-drawing callouts. +- Coral: `#FF6B6B` — inherited from the site's accent palette. Used sparingly for clinical-domain tagging in capabilities view. +- Teal: `#00897B` — inherited from the site's primary palette. Used for data/technical-domain tagging and hover states. + +**Full zinc neutral scale** for all grays, ensuring consistent, harmonious neutral tones across both modes. + +### Typography + +**Single-family system** — Inter for all text, Geist Mono for numbers and data values. + +- **Dashboard title / Hero name:** Inter 600, 48px, tracking `-0.025em` +- **Tab labels:** Inter 500, 14px, tracking `0.01em`, uppercase +- **Section headings (within tabs):** Inter 600, 24px, tracking `-0.015em` +- **Card KPI values:** Geist Mono 600, 48-72px (varies by card size), tracking `-0.02em` +- **Card labels:** Inter 500, 14px, zinc-500 +- **Body text (bullets, descriptions):** Inter 400, 15px, line-height 1.7 +- **Status bar text:** Inter 400, 13px +- **Timestamps/dates:** Geist Mono 400, 13px + +Hierarchy is established through size, weight, and tracking only — no decorative font variations. Tight negative tracking at large sizes keeps the typographic texture dense and professional. + +### Spacing and Grid + +- **Grid system:** CSS Grid, 12-column, 24px gap +- **Max content width:** 1120px, centered with `auto` margins +- **Card internal padding:** 24px +- **Border radius:** 8px for small elements (badges, inputs), 12px for cards, 16px for containers/tab panels +- **Section spacing within tabs:** 32px between card groups +- **Consistent 8px base unit** — all spacing values are multiples of 8 + +### Motion + +- **Primary easing:** `cubic-bezier(0.32, 0.72, 0, 1)` (Vercel easing) — fast entry, gentle settle +- **Reveal animation:** Elements enter with `opacity: 0, translateY: 8px, filter: blur(4px)` and resolve to `opacity: 1, translateY: 0, filter: blur(0)` over 300ms +- **Stagger interval:** 40ms between sequential elements +- **Spring parameters:** `{ stiffness: 300, damping: 30 }` for layout animations (card reflow, panel resize) +- **Tab crossfade:** 150ms fade out, 150ms fade in, with the incoming view's elements staggering in using the reveal animation +- **Number countup:** Metric card values animate from 0 to target over 800ms using `ease-out` timing, triggered on tab entry +- **Hover:** Cards lift 2px (`translateY: -2px`) with border color transitioning to blue-500 over 150ms + +### Material and Surface Treatment + +Clean, flat surfaces with precise borders defining all edges. This is not a skeuomorphic or glassmorphic design — it is systematic and structural. + +- **Light mode:** Shadows are barely perceptible (`0 1px 2px rgba(0,0,0,0.04)`), used only on cards. Borders are the primary spatial separator. +- **Dark mode:** No shadows. Borders and subtle background-color differentiation define hierarchy. +- **No gradients on surfaces.** Gradients are reserved exclusively for the ECG transition animation and the occasional data visualization element. +- **Borders define everything:** card edges, tab underlines, status bar top edge, grid cell boundaries. + +### Signature Visual: The Status Dot + +Every section, skill, and experience item has a **6px colored dot** positioned consistently at the top-left of its container: + +- **Emerald dot:** Current/active items — current role, current projects, skills actively in use +- **Blue dot:** Completed items — past roles, completed education, shipped projects +- **Amber dot:** Notable achievements — items with standout metrics (the 14.6M programme, the asthma screening revenue, the switching algorithm) + +In the navigation tab bar, the active tab's dot **pulses subtly** (opacity oscillation between 0.6 and 1.0, 2s cycle) to indicate the current view. This pulse is the only continuously animated element in the resting state — everything else is still until interacted with, reinforcing the "precision instrument" feel. + +--- + +## Section-by-Section Design + +### Tab Bar (Persistent Navigation Chrome) + +Fixed at the top of the viewport. Full width. Contains: + +- **Left region:** "Andy Charlwood" in Inter 600, 18px. Below (or beside on wider screens): "Population Health & Data Analysis" in Inter 400, 13px, zinc-500. +- **Center region:** Tab labels — "Overview", "Capabilities", "Timeline", "Portfolio", "Connect". Each is a button with Inter 500, 14px, uppercase, tracking `0.01em`. Active tab has a 2px teal underline and slightly bolder weight. Inactive tabs are zinc-500 with hover-to-zinc-300 transition. +- **Right region:** Theme toggle (sun/moon icon, 20px), and a small "Download CV" link styled as a subtle outlined button. + +The tab bar has a bottom border (`1px solid zinc-200` light / `zinc-800` dark). Background matches the page background with a `backdrop-filter: blur(12px)` for slight transparency when content scrolls behind it (relevant for tabs with scrollable content). + +**Tab bar height:** 56px desktop, 48px mobile (when it becomes bottom nav). + +--- + +### Tab 1: Overview + +The landing view after the ECG transition. This is a **bento grid** — a CSS Grid with items of varying column spans, creating an asymmetric but balanced layout. + +**Grid structure (desktop, 4 columns):** + +``` +[ Name & Title Card (2 cols) ] [ Profile Summary (2 cols) ] +[ Years Exp (1) ] [ Budget (1) ] [ Patients (1) ] [ Savings (1) ] +[ Tech Stack Card (2 cols) ] [ Current Focus (2 cols) ] +[ Location + GPhC (1 col) ] [ Leadership (1 col) ] [ Education Highlight (2 cols) ] +``` + +**Card types in Overview:** + +1. **Name & Title Card** (2-col span): Andy Charlwood in Inter 600 48px. "Deputy Head, Population Health & Data Analysis" below. "NHS Norfolk & Waveney ICB" in teal. Emerald status dot (current role). + +2. **Profile Summary Card** (2-col span): The CV profile text, but condensed to 2-3 sentences. Inter 400, 15px, line-height 1.7. This is the only prose-heavy card. + +3. **Metric Cards** (1-col span each): + - "10+" in Geist Mono 72px, "Years Experience" label below, blue dot + - "220M" in Geist Mono 64px with "GBP" prefix in 24px, "Prescribing Budget" label, amber dot + - "14,000" in Geist Mono 56px, "Patients Identified" label, emerald dot + - "14.6M" in Geist Mono 64px with "GBP" prefix in 24px, "Efficiency Programme" label, amber dot + +4. **Tech Stack Card** (2-col span): Horizontal row of technology badges: Python, SQL, Power BI, JS/TS, each as a pill with icon. Teal-tinted background on hover. This card serves as a quick-reference for technical keywords that ATS systems and recruiters scan for. + +5. **Current Focus Card** (2-col span): 2-3 bullet points about current work direction, drawn from the most recent role. Emerald dot. + +6. **Location + GPhC Card** (1-col): "Norwich, UK" with a subtle map pin icon. "GPhC Registered Pharmacist" with registration number. "Since August 2016" in Geist Mono. + +7. **Leadership Card** (1-col): "Mary Seacole Programme" with "NHS Leadership Academy" below. "78%" score in Geist Mono. Blue dot (completed). + +8. **Education Highlight Card** (2-col): "MPharm 2:1 Honours" in large type. "University of East Anglia, 2011-2015". "Research: 75.1% Distinction" as a highlighted callout with amber dot. + +All cards have 12px border-radius, 24px internal padding, and the standard border treatment. On hover, cards lift 2px and the border transitions to blue-500. + +**Click behavior:** Clicking a metric card reveals an expanded state (the card grows to fill 2 columns, pushing others down) showing contextual detail — e.g., clicking "14,000 Patients" expands to show a brief description of the switching algorithm and a link to the Portfolio tab. + +--- + +### Tab 2: Capabilities + +A two-panel layout for exploring skills. + +**Left panel (sidebar, ~280px fixed width):** +A vertical list of skill categories styled as selectable list items: +- "Technical" (8 skills) +- "Clinical" (6 skills) +- "Strategic" (4 skills) + +Each category shows its name, skill count, and a small bar chart preview (a thin horizontal bar showing relative skill level average for that category). The active category has a blue left border (3px) and slightly elevated background. + +**Right panel (fluid width):** +Displays the selected category's skills as gauge visualizations. + +Each skill is rendered as a card containing: +- Skill name in Inter 500, 16px +- Circular SVG gauge (same pattern as current implementation: `strokeDashoffset = circumference * (1 - level / 100)`, rotated -90deg to start from 12 o'clock) +- Percentage in Geist Mono 600, 24px, centered in the gauge +- Category-specific color: teal for Technical, coral for Clinical, blue for Strategic +- A status dot: emerald for skills actively used in current role, blue for all others + +Skills are arranged in a responsive grid: 4 columns on desktop within the right panel, 3 on tablet, 2 on mobile. + +**Gauge animation:** When switching categories, the gauges animate from 0 to their target value over 800ms with `ease-out` timing. This countup triggers every time a category is selected (not just on first view), reinforcing the "live data" feel. + +**Interaction detail:** Hovering a skill gauge shows a tooltip with a one-line description of how Andy uses that skill (e.g., "Python: Built switching algorithms, controlled drug monitoring, data pipeline automation"). + +--- + +### Tab 3: Timeline + +An interactive chronological view of Andy's career. + +**Desktop layout — Horizontal timeline:** + +A horizontal scrollable container with CSS scroll-snap. The X-axis represents years (2011-2026), with year markers at regular intervals. The timeline has two tracks: + +**Track 1 (upper, primary):** Professional experience entries. Each entry is a card positioned at its start date, with width proportional to duration. Cards contain: +- Role title in Inter 600, 16px +- Organization in Inter 400, 14px, teal +- Date range in Geist Mono 400, 13px +- Status dot: emerald for current roles, blue for past + +Cards are stacked vertically when roles overlap (e.g., Deputy Head and Interim Head at ICB). + +**Track 2 (lower, secondary):** Education and professional development milestones. Rendered as smaller markers/pills: +- "MPharm, UEA" (2011-2015, spanning 4 years) +- "Mary Seacole Programme" (2018, point marker) +- "GPhC Registration" (2016, point marker) + +**Timeline chrome:** +- A thin horizontal axis line in zinc-300 with year tick marks +- The "present" marker (2026) has a pulsing emerald dot +- A subtle gradient fade at the left edge indicates more content to scroll + +**Expand interaction:** Clicking any experience card expands it downward to reveal the full bullet points for that role. The timeline adjusts layout smoothly (spring animation, 300ms). Only one card can be expanded at a time — expanding a new card collapses the previous one. + +**Keyboard navigation:** Left/right arrow keys scroll the timeline by one year. Enter/Space expands the focused card. + +--- + +### Tab 4: Portfolio + +A card grid displaying Andy's projects with status metadata. + +**Grid:** 2 columns on desktop, 1 on mobile. Each project card contains: + +- Project title in Inter 600, 18px +- Description in Inter 400, 15px, 2-3 lines +- **Status badge** styled like a deployment indicator: + - "Live" — emerald background, white text (for PharMetrics) + - "Internal" — blue background, white text (for Blueteq Generator, CD Monitoring) + - "Complete" — zinc-500 background, white text (for NMS Video) +- Tech tags: small pills showing technologies used (Python, Power BI, etc.) +- Impact metric: a single standout number for each project, displayed in Geist Mono + - PharMetrics: "Real-time tracking" + - Switching Algorithm: "14,000 patients / 2.6M savings" + - Blueteq Generator: "70% reduction / 200hrs saved" + - CD Monitoring: "Population-scale safety" + - Sankey Analysis: "Patient pathway visualization" +- External link button (for PharMetrics) + +**Hover preview:** On desktop, hovering a project card for 500ms shows an expanded preview with additional context — the full description and a technical implementation note. This preview slides out from the card's right edge (200ms, spring animation). + +**Project data (from CV):** + +1. **PharMetrics** — Real-time medicines expenditure dashboard for NHS decision-makers. Status: Live. Tech: Power BI, SQL. Impact: Real-time tracking across 220M budget. + +2. **Switching Algorithm** — Python-based algorithm identifying patients on expensive drugs suitable for cost-effective alternatives. Status: Internal. Tech: Python, SQL. Impact: 14,000 patients identified, 2.6M annual savings. + +3. **Blueteq Generator** — Automation tool for high-cost drug prior approval form creation. Status: Internal. Tech: Python. Impact: 70% reduction in forms, 200+ hours saved. + +4. **Controlled Drug Monitoring** — System calculating oral morphine equivalents across all opioid prescriptions at population scale. Status: Internal. Tech: Python, SQL. Impact: Population-scale patient safety analysis. + +5. **Sankey Chart Analysis** — Tool visualizing patient journeys through high-cost drug pathways. Status: Internal. Tech: Python. Impact: Trust-level compliance auditing. + +6. **Patient Pathway Analysis** — Data-driven analysis of patient pathways to identify optimization opportunities. Status: Internal. Tech: Python, SQL. Impact: Clinical outcome improvements. + +--- + +### Tab 5: Connect + +Contact information and a simple message form. + +**Layout:** Centered single column within the tab panel, max-width 600px. Clean and minimal — this tab has the lowest information density by design, creating visual breathing room after the data-heavy other tabs. + +**Content:** +- "Get in Touch" heading, Inter 600, 32px +- Email: andy@charlwood.xyz as a clickable link, styled with the blue accent +- Location: Norwich, UK with a subtle map pin icon +- LinkedIn / GitHub links as icon buttons with labels + +**Optional contact form:** +- Name input +- Email input +- Message textarea +- Submit button in blue accent, full-width + +All form inputs use 12px border-radius, zinc-200 borders (light) / zinc-700 borders (dark), 16px internal padding. Focus state adds a blue border and subtle blue glow (`box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.15)`). + +--- + +### The Status Bar (Persistent Bottom Chrome) + +Fixed at the bottom of the viewport, full width, 36px height. + +**Content (left to right):** +- "Last updated: Feb 2026" in Geist Mono 400, 12px +- Vertical separator (1px, zinc-600) +- "Status: Open to opportunities" with a pulsing emerald dot +- Vertical separator +- "Norwich, UK" with a pin icon +- **Right-aligned:** "GPhC Registered" with a subtle badge + +**Styling:** +- Light mode: `#F4F4F5` background (zinc-100), zinc-300 top border, zinc-600 text +- Dark mode: `#18181B` background (zinc-900), zinc-800 top border, zinc-400 text + +The status bar provides ambient information that's always available regardless of which tab the user is viewing. It communicates "this person is available and current" without requiring the user to navigate to a contact page. + +--- + +## Interactions and Micro-interactions + +### Tab Switching +- Clicking a new tab triggers a crossfade: the current tab panel fades out (150ms, ease-out), then the new panel fades in (150ms, ease-in) with its child elements staggering via the reveal animation (40ms intervals). +- The active tab underline slides to the new tab position using a `layoutId` animation (Framer Motion), creating a smooth indicator transition rather than a discrete jump. + +### Metric Card Countup +- When a metric card enters the viewport (on tab switch or initial load), its number value animates from 0 to the target over 800ms using `ease-out` timing. +- The "GBP" prefix and labels appear instantly — only the number animates. +- If the user switches away from a tab and returns, the countup replays, reinforcing the "live data refresh" metaphor. + +### Card Hover States +- All cards: `translateY: -2px` lift, border color transition to `blue-500`, 150ms duration. +- Metric cards in Overview: the number subtly increases size by 2% on hover (a data-zoom effect). +- Project cards in Portfolio: the status badge pulses once on hover. + +### Skill Gauge Interaction +- Category selection in Capabilities triggers all gauge animations simultaneously with 40ms stagger. +- Individual gauge hover: the gauge ring thickens from strokeWidth 5 to 7, and a tooltip appears. + +### Timeline Card Expansion +- Click triggers a spring layout animation: the card's height expands to reveal bullet points. Other cards shift downward smoothly. +- The expanded card receives a left blue border (3px) and a slightly elevated shadow. +- A second click collapses the card. +- Only one card can be expanded at a time. + +### Theme Toggle +- Clicking the sun/moon icon in the tab bar triggers a smooth crossfade of all color values (200ms). CSS custom properties handle the color swap, so no React re-render is needed for the transition. +- The icon itself rotates 180 degrees during the toggle (sun rotates out, moon rotates in). + +### Status Dot Pulse +- The active tab's status dot and the "Open to opportunities" status bar dot share the same pulse animation: opacity oscillates between 0.6 and 1.0 on a 2-second cycle using `animation: pulse 2s ease-in-out infinite`. +- All other dots are static. + +--- + +## Navigation + +### Primary Navigation: Tab Bar + +The tab bar is the only navigation mechanism. There is no scroll-based section jumping, no sidebar, no hamburger menu. This is a deliberate constraint: the dashboard metaphor demands that users switch views, not scroll through a document. + +**Tab list:** + +| Tab | Label | Keyboard | URL Hash | +|-----|-------|----------|----------| +| 1 | Overview | `1` or `Alt+1` | `#overview` | +| 2 | Capabilities | `2` or `Alt+2` | `#capabilities` | +| 3 | Timeline | `3` or `Alt+3` | `#timeline` | +| 4 | Portfolio | `4` or `Alt+4` | `#portfolio` | +| 5 | Connect | `5` or `Alt+5` | `#connect` | + +**URL hash routing:** Each tab updates the URL hash on activation. On page load, the app reads the hash and activates the corresponding tab (defaulting to Overview if no hash or unrecognized hash). This enables direct linking to specific tabs — a recruiter can share `charlwood.xyz/#portfolio` to send someone directly to the projects view. + +**Tab state persistence:** Within a session, each tab preserves its internal state. If the user expands a timeline card, switches to Portfolio, and returns to Timeline, the card is still expanded. This state is managed via React context (not URL), so it resets on page reload. + +### Secondary Navigation: Within-Tab Interactions + +- **Overview:** Card click expands for detail. No further navigation depth. +- **Capabilities:** Category sidebar acts as sub-navigation. Click a category to filter the skill display. +- **Timeline:** Horizontal scroll (mouse wheel, touch swipe, or arrow keys) navigates chronologically. Card click expands. +- **Portfolio:** Card click/hover reveals additional detail. External links navigate away. +- **Connect:** No navigation — static content. + +--- + +## Responsive Strategy + +### Desktop (>1024px) + +The full dashboard experience. Multi-column bento grids, side-by-side capability panels, horizontal timeline, and the persistent tab bar + status bar chrome. + +- Tab bar: horizontal, centered tabs with full text labels +- Overview: 4-column bento grid +- Capabilities: sidebar (280px) + skill grid (4 columns) +- Timeline: horizontal scroll with snap points +- Portfolio: 2-column card grid +- Status bar: full-width with all metadata items + +### Tablet (768-1024px) + +Dashboard bar becomes horizontally scrollable tabs (same visual style, but container scrolls if tabs exceed width). This prevents cramped labels. + +- Overview: 2-column grid. Metric cards stack into 2x2 blocks. Larger cards remain 2-col span. +- Capabilities: Filter panel collapses to a horizontal selector (dropdown or scrollable pill bar) above the skill grid. Skills display in 3 columns. +- Timeline: Switches from horizontal to **vertical**. Entries stack chronologically top-to-bottom. Education items interleave with experience items in date order. Year markers appear as horizontal dividers. +- Portfolio: Remains 2-column or shifts to single column depending on card content. +- Status bar: Remains persistent at bottom, but "GPhC Registered" badge moves to a second line or hides behind a chevron. + +### Mobile (<768px) + +The dashboard bar transforms into a **bottom navigation** with 5 icon buttons (matching the 5 tabs). Each icon is from Lucide: +- Overview: `LayoutDashboard` +- Capabilities: `Gauge` +- Timeline: `Clock` +- Portfolio: `FolderOpen` +- Connect: `Mail` + +The active tab has a teal dot above its icon and the label displayed below. + +- Tab bar moves to bottom, 56px height, with safe area padding for devices with home indicators +- The top of the viewport shows the current tab title + theme toggle only +- Overview: Single-column stack. All metric cards are full-width. Name card at top, metrics below, then supporting cards. +- Capabilities: Category selector as a horizontal scrollable pill bar at top. Skills display in 2 columns below. +- Timeline: Vertical single-column. Full-width cards. Year markers as sticky section headers. +- Portfolio: Single-column card stack. Status badges are prominent. +- Connect: Full-width form, generous touch targets (48px minimum). +- Status bar: Moves to the top of each view as a collapsible banner (tap to expand). Shows only "Open to opportunities" by default with a chevron to reveal full metadata. + +### Breakpoint Summary + +| Element | Desktop (>1024) | Tablet (768-1024) | Mobile (<768) | +|---------|-----------------|-------------------|---------------| +| Tab bar | Top, horizontal | Top, scrollable | Bottom, icons | +| Status bar | Bottom, full | Bottom, condensed | Top, collapsible | +| Overview grid | 4 columns | 2 columns | 1 column | +| Capabilities | Sidebar + grid | Dropdown + grid | Pills + grid | +| Timeline | Horizontal scroll | Vertical stack | Vertical stack | +| Portfolio | 2 columns | 2 columns | 1 column | +| Card padding | 24px | 20px | 16px | +| Grid gap | 24px | 20px | 16px | + +--- + +## Technical Implementation + +### Component Architecture + +``` +App.tsx + BootSequence.tsx + ECGAnimation.tsx (modified exit: multi-trace → grid → cascade) + Dashboard.tsx (replaces current content phase) + DashboardTabBar.tsx + TabButton.tsx + DashboardContent.tsx (renders active tab panel) + OverviewTab.tsx + BentoGrid.tsx + MetricCard.tsx + ProfileCard.tsx + TechStackCard.tsx + CapabilitiesTab.tsx + CategorySidebar.tsx + SkillGaugeGrid.tsx + SkillGauge.tsx + TimelineTab.tsx + TimelineTrack.tsx + TimelineEntry.tsx + TimelineMilestone.tsx + PortfolioTab.tsx + ProjectCard.tsx + StatusBadge.tsx + ConnectTab.tsx + ContactForm.tsx + StatusBar.tsx + ThemeToggle.tsx +``` + +### State Management + +- **Active tab:** React `useState` in `Dashboard.tsx`. Updated on tab click. Synced to URL hash via `useEffect` (writes on change, reads on mount). +- **Tab internal state:** React context (`DashboardContext`) holding: expanded timeline entry ID, selected skill category, expanded overview card ID. This context is not reset on tab switch, enabling state preservation. +- **Theme:** `useState` initialized from `localStorage`, falling back to `prefers-color-scheme` media query. Toggle writes to `localStorage` and applies a `data-theme="dark"` attribute to the document root. All colors reference CSS custom properties. + +### CSS Strategy + +- Tailwind CSS for utility classes, consistent with the existing project setup +- CSS custom properties for theme-aware colors (defined in `index.css` under `:root` and `[data-theme="dark"]` selectors) +- CSS Grid for bento layouts with explicit `grid-template-columns` and `grid-column: span N` on cards +- CSS `scroll-snap-type: x mandatory` for horizontal timeline on desktop +- `backdrop-filter: blur(12px)` on tab bar for the subtle transparency effect +- `@media (prefers-color-scheme: dark)` as the fallback when no manual toggle has been used + +### Tab Transition Implementation + +``` +Tab switch flow: +1. User clicks new tab +2. Current tab panel: animate out (opacity 1→0, 150ms) +3. Update active tab state +4. New tab panel mounts +5. New tab panel: staggered reveal (each child: opacity 0→1, y 8→0, blur 4→0, 300ms, 40ms stagger) +6. If tab has countup elements (metric cards, skill gauges), countups trigger after reveal +``` + +Using Framer Motion's `AnimatePresence` with `mode="wait"` to manage the tab panel crossfade. Each tab panel is wrapped in a `motion.div` with `key={activeTab}` to trigger exit/enter animations. + +### Performance Considerations + +- **Tab panels:** Only the active tab renders its full content. Inactive tabs are unmounted (not hidden with `display: none`) to keep DOM light. State is preserved in context, not in DOM. +- **Metric countups:** Use `requestAnimationFrame`-based animation, not CSS — this allows precise easing control and avoids layout thrashing. +- **Timeline scroll:** Horizontal scrolling uses CSS-native scroll-snap, not JavaScript-controlled positioning. +- **Images:** If project screenshots are added later, use `loading="lazy"` and serve WebP with `` fallback. +- **Gauge SVGs:** Pre-computed `strokeDashoffset` values stored as constants. No recalculation on render. + +### ECG Transition Modifications + +The existing `ECGAnimation.tsx` needs modifications for the multi-trace and grid materialization: + +1. After the name is complete (current `holdEndTime`), instead of the simple exit phase, the canvas draws two additional traces (teal and coral) at 30% and 70% viewport height. +2. The `bgTransitionedRef` logic changes: background transitions to `#0A1628` instead of `#FFFFFF`. +3. A new phase is added after the multi-trace flatline: vertical grid lines are drawn on the canvas, followed by content-cell placeholder rectangles. +4. The canvas fade-out timing is adjusted to overlap with the React dashboard mount, so the grid drawn on canvas aligns pixel-perfectly with the CSS Grid rendered by React. +5. The `onComplete` callback fires after the grid materialization, triggering the phase switch from `'ecg'` to `'content'`. + +--- + +## Accessibility + +### Keyboard Navigation + +The tab-based interface maps naturally to the ARIA tabs pattern: + +- `Tab` moves focus between the tab bar and the active tab panel +- `ArrowLeft` / `ArrowRight` moves between tabs when the tab bar is focused +- `Enter` / `Space` activates a focused tab +- Within the active panel, `Tab` navigates through interactive elements in document order +- In Timeline tab: `ArrowLeft` / `ArrowRight` scrolls the timeline by one year; `Enter` / `Space` expands the focused timeline entry +- Number keys `1`-`5` activate tabs directly (when tab bar is focused) + +### ARIA Roles and Labels + +- Tab bar: `role="tablist"`, each tab `role="tab"` with `aria-selected`, each panel `role="tabpanel"` with `aria-labelledby` +- Metric cards: `aria-label` with full context, e.g., `aria-label="14,000 patients identified for cost-effective switching through Python-based algorithm"` +- Skill gauges: `aria-valuenow`, `aria-valuemin="0"`, `aria-valuemax="100"`, `role="progressbar"`, `aria-label="Python proficiency: 90 percent"` +- Status bar: `aria-live="polite"` region, so dynamic updates (if any) are announced +- Timeline entries: `role="article"` with expandable content using `aria-expanded` +- Status dots: `aria-hidden="true"` (decorative; the semantic information is in adjacent text) + +### Color and Contrast + +- All text meets WCAG 2.1 AA contrast requirements in both light and dark modes +- The zinc neutral scale is specifically chosen for reliable contrast ratios +- Status dots are never the sole indicator of state — they always accompany text labels +- Focus indicators: 2px blue outline with 2px offset, visible in both themes +- The theme toggle is not required to use the site — both themes meet accessibility standards independently + +### Motion and Preferences + +- All animations respect `prefers-reduced-motion`. When reduced motion is preferred: + - Tab crossfades become instant switches (no animation) + - Metric countups display final values immediately + - Gauge animations are disabled; gauges render at their target values + - Card hover lifts are disabled + - Status dot pulse is disabled + - ECG transition skips to final state after a brief hold + +### Screen Reader Experience + +The tab-based navigation provides a clear, navigable structure for screen readers: +1. User encounters the tab bar with 5 clearly labeled tabs +2. Activating a tab announces the panel label +3. Within each panel, content is structured with headings (`h2` for section titles, `h3` for individual entries) +4. Metric cards read as: "[Value] [Label]. [Additional context from aria-label]" +5. The status bar is announced on page load and when content changes + +--- + +## What Makes This Special + +**The medium IS the message.** By presenting his CV as a dashboard, Andy demonstrates his analytical mindset through the navigation itself. A recruiter doesn't just read about Andy's ability to create data systems — they experience one. The information architecture of the site is itself a portfolio piece. + +**Numbers lead.** Every other CV website puts prose first and numbers second. This design inverts that: the first thing you see is a grid of metric cards with large Geist Mono numbers. "14,000 patients." "14.6M programme." "220M budget." These numbers are more compelling than any paragraph of self-description, and presenting them in a dashboard context makes them feel quantitative and verifiable rather than resume-inflated. + +**The density is the point.** Most portfolio sites are spacious, scrolling single-column affairs with generous whitespace. This design deliberately goes the other direction: high density, multiple data points visible simultaneously, information that rewards careful reading. This says "I am comfortable with complexity" in a way that minimal designs cannot. + +**The ECG transition earns its keep.** The multi-trace multiplication and grid materialization aren't just visually interesting — they tell a story. Raw clinical signals (vital signs) transform into organized, structured data (dashboard grid). This is literally what Andy does: he takes messy prescribing data and turns it into actionable analytics. The transition is a 3-second visual metaphor for his career. + +**Adaptive theming signals engineering maturity.** Supporting both light and dark modes with a manual toggle and `prefers-color-scheme` respect is a technical detail that fellow developers and technical recruiters will notice and appreciate. It signals awareness of modern frontend standards. + +**The status bar adds ambient context.** "Open to opportunities" is visible on every single tab view without requiring the user to navigate to a contact page. It's a constant, low-key signal — like a system indicator light — that communicates availability without being pushy. This is a detail borrowed from actual operational dashboards, where system status is always visible. + +**Tab persistence respects the user's exploration.** Preserving expanded state across tab switches communicates respect for the user's time and attention. It says: "I built this thoughtfully." It's a subtle UX detail that most portfolio sites don't consider, because most portfolio sites don't have this level of navigational complexity to manage. diff --git a/designs/03-the-observatory.md b/designs/03-the-observatory.md new file mode 100644 index 0000000..5159f34 --- /dev/null +++ b/designs/03-the-observatory.md @@ -0,0 +1,511 @@ +# Design 3: The Observatory + +## Overview + +A non-linear, spatial interface where the site does not scroll -- it is an interactive constellation. Glowing nodes arranged in a force-directed graph represent sections of Andy's career. Click a node to zoom in. Navigation is spatial, not linear. The most visually distinctive and architecturally ambitious of all 6 designs. + +The core insight: a traditional CV is a list. A constellation is a map. Lists impose a reading order. Maps invite exploration. By presenting Andy's career as an interconnected constellation rather than a sequential document, visitors build their own mental model of how clinical expertise, technical skill, and strategic leadership connect -- and they remember it, because they built it themselves. + +This design draws from three disciplines: knowledge-graph visualization (Obsidian, Neo4j Browser), environmental storytelling in game design (where narrative is discovered through spatial exploration rather than linear delivery), and the force-directed graph layouts used in data science to reveal hidden structure in complex datasets. It applies all three to the problem of self-presentation. + +--- + +## ECG Transition + +**Starting point:** "ANDREW CHARLWOOD" is on screen in neon green (#00ff41) strokes on a black (#000) background. The ECG trace that drew it is still visible. The drawing head has stopped. + +**Then:** + +The letterforms begin to **contract inward** toward the center of the name. Each letter stretches and thins -- like light near a gravitational singularity -- as it compresses toward a single convergence point at screen center. The neon green shifts through cyan (#00E5FF) to bright white (#FFFFFF) as the letters converge, mimicking the blueshift of light under gravitational compression. + +All letters collapse into a single luminous point. A beat of stillness (200ms). + +The point **pulses** -- a sonar ring of soft cyan (#00D4AA) radiates outward from center. As this ring passes across the viewport, constellation nodes **blink into existence in its wake**, each one appearing with a brief flash and then settling into a soft glow. The ring reaches the viewport edges and fades. + +Simultaneously, the black background shifts imperceptibly to deep navy (#0A0E1A). The luminous center point fades and Andy's name re-renders as clean DOM text (Space Grotesk, 700 weight, soft white #ECECF0) at center screen. His role title fades in below at smaller size. + +The 5-6 constellation nodes that appeared during the sonar ring now animate to their orbital positions around the name using spring physics -- they overshoot slightly, oscillate, and settle. Each node has a faint label that appears on hover proximity. + +**Duration:** ~2.4 seconds. + +**Color journey:** Black (#000) --> Deep Navy (#0A0E1A). ECG green (#00ff41) --> Cyan (#00E5FF) --> White (#FFF) at convergence --> Warm amber (#D4874D) node glows + Electric cyan (#00D4AA) active states in the constellation. + +**The message:** "What was a single heartbeat line has become a universe of interconnected points. Welcome to the observatory." + +--- + +## Visual System + +### Color Palette + +| Token | Value | Usage | +|---|---|---| +| `--bg-deep` | `#0A0E1A` | Primary background (deep navy-black) | +| `--bg-gradient` | `radial-gradient(ellipse at center, #0F1428 0%, #0A0E1A 70%)` | Subtle depth at center | +| `--amber` | `#D4874D` | Primary accent -- node highlights, connection lines, active indicators | +| `--amber-glow` | `rgba(212, 135, 77, 0.15)` | Ambient glow around active nodes | +| `--cyan` | `#00D4AA` | Active/hover states, sonar pulses, interactive feedback | +| `--cyan-glow` | `rgba(0, 212, 170, 0.12)` | Hover glow | +| `--text-primary` | `#ECECF0` | Headings, labels, primary text | +| `--text-secondary` | `#8B8FA3` | Descriptions, body text | +| `--text-dim` | `#4A4E63` | Tertiary labels, metadata | +| `--grid-line` | `#1A1F2E` | Faint structural lines (used sparingly) | +| `--node-border` | `#2A2F42` | Inactive node borders | +| `--card-bg` | `rgba(15, 20, 40, 0.85)` | Detail panel backgrounds (translucent) | +| `--card-border` | `rgba(212, 135, 77, 0.2)` | Detail panel border glow | + +### Background Treatment + +No grid for this design. The dark space should feel open, organic, and expansive -- not systematic. Three layers create depth: + +1. **Base:** Flat deep navy (#0A0E1A) +2. **Depth gradient:** Subtle radial gradient, lighter at center (#0F1428), fading to base at edges. Creates a sense of looking into space. +3. **Star particles:** Very low density (30-50 particles across the viewport), tiny (1-2px), faintly glowing white at 10-20% opacity. Drift slowly. These are purely atmospheric -- they do not carry content or respond to interaction. They simply make the space feel alive. + +### Typography + +| Role | Font | Weight | Size | +|---|---|---|---| +| Display (name) | Space Grotesk | 700 | `clamp(2rem, 4vw, 3.5rem)` | +| Section headings | Space Grotesk | 500 | `clamp(1.25rem, 2.5vw, 1.75rem)` | +| Body text | IBM Plex Sans | 400 | 15px / 1.7 line-height | +| Subheadings | IBM Plex Sans | 500 | 14px | +| Data labels / stats | IBM Plex Mono | 400 | 13px, uppercase, 0.05em tracking | +| Node labels | Space Grotesk | 500 | 13px | + +**Font loading strategy:** Space Grotesk and IBM Plex Sans loaded via Google Fonts with `display=swap`. IBM Plex Mono loaded with `display=optional` (falls back to system mono if slow to load -- acceptable for data labels). + +### Motion + +- **Spring physics** for all node movement: `mass: 1, stiffness: 120, damping: 14` (Framer Motion spring config). This creates a responsive, organic feel -- nodes overshoot and settle rather than moving linearly. +- **Zoom transitions:** `cubic-bezier(0.16, 1, 0.3, 1)` -- fast departure, gentle arrival. Duration 600ms. +- **Hover effects:** 150ms ease-out for color/glow changes. +- **Connection line reveals:** `stroke-dasharray` animation, 800ms per line with 100ms stagger between lines. +- **Sonar pulse on interaction:** Radial ring emanating from clicked node, 400ms, opacity 0.3 --> 0. + +### Signature Visual: Connection Lines + +The constellation's defining feature is the connection web that reveals relationships between career elements. After visiting 3+ nodes, a "View Connections" toggle appears. + +- Lines are SVG `` elements drawn between node center points. +- **Line thickness** encodes relationship strength: strong connections (Python --> switching algorithm --> 2.6M savings) use 2px lines; weaker thematic connections use 0.75px lines. +- **Line color:** Warm amber (#D4874D) at 40% opacity, brightening to 80% on hover. +- **Line style:** Slightly curved (quadratic bezier with a subtle arc), not straight. This creates a more organic, constellation-like appearance. +- **Interaction:** Hovering a connection line shows a tooltip explaining the relationship. Example: "Python skills --> Built switching algorithm --> 14,000 patients identified, 2.6M annual savings." +- **Animation:** Lines draw themselves using `stroke-dashoffset` animation when first revealed. + +--- + +## Section-by-Section Design + +### Hub View (Default State) + +The hub is the home state -- what visitors see after the ECG transition completes. Andy's name sits at center in Space Grotesk 700, with his role title below in IBM Plex Sans. Around the name, 5-6 constellation nodes orbit at varying distances: + +**Node positions (approximate, adjusted by force-directed layout):** + +| Node | Orbital Distance | Glow Color | Icon Concept | +|---|---|---|---| +| Skills | Close orbit (top-right) | Amber | Hexagonal skill web | +| Experience | Close orbit (left) | Amber | Timeline/pulse line | +| Education | Mid orbit (bottom-left) | Cyan | Academic cap / book | +| Projects | Mid orbit (bottom-right) | Cyan | Code brackets / diagram | +| Contact | Outer orbit (top-left) | Amber | Signal / connection | + +Each node is a 48-64px circle with: +- A soft glow (`box-shadow: 0 0 20px var(--amber-glow)`) +- A thin border (`1px solid var(--node-border)`, transitioning to `--amber` on hover) +- A small icon or symbol at center (Lucide icons, 20px) +- A label that appears on hover or cursor proximity (within 100px), fading in at 200ms + +**Gravitational attraction:** As the cursor moves near a node (within 120px), the node is gently pulled toward the cursor by 4-8px. This creates a subtle sense of magnetic interaction without disrupting the layout. The pull uses spring physics with high damping (damping: 20) to prevent oscillation. + +**Ambient animation:** Nodes drift very slowly in micro-orbits (2-3px movement radius, 8-12 second cycle). This keeps the constellation feeling alive without being distracting. + +### Skills Node (Zoomed In) + +**Zoom transition:** Clicking the Skills node triggers a smooth pan+zoom. The clicked node expands to fill ~70% of the viewport width. Other nodes animate to the periphery (scaled down to 24px, still visible, still clickable). Duration: 600ms. + +**Internal layout:** A radial skill diagram. Skills orbit a center point at distances proportional to their proficiency level (higher proficiency = closer to center, representing mastery as gravitational pull). + +Three concentric rings (barely visible, #1A1F2E at 30% opacity) mark proficiency zones: Expert (inner), Proficient (mid), Competent (outer). + +**Skill categories** are color-coded: +- Technical skills: Amber (#D4874D) nodes +- Clinical skills: Cyan (#00D4AA) nodes +- Strategic skills: Soft white (#ECECF0) nodes with amber border + +Each skill is a small node (32-40px) with the skill name below it. Hovering a skill: +1. Expands the node slightly (scale 1.15) +2. Shows a tooltip with proficiency percentage and a one-line description +3. Highlights all related skills with pulsing connection lines + +**Interaction:** The radial diagram can be slowly rotated by click-and-drag (Framer Motion `drag` with `dragElastic: 0.1`, constrained to rotation). This serves no functional purpose -- it simply makes the diagram feel tactile and explorable. + +**Skill data:** + +Technical: Python (90%), SQL (88%), Power BI (92%), JS/TS (70%), Data Analysis (95%), Dashboard Dev (88%), Algorithm Design (82%), Data Pipelines (80%) + +Clinical: Medicines Optimisation (95%), Pop. Health Analytics (90%), NICE TA (85%), Health Economics (80%), Clinical Pathways (82%), CD Assurance (88%) + +Strategic: Budget Mgmt (90%), Stakeholder Engagement (88%), Pharma Negotiation (85%), Team Development (82%) + +### Experience Node (Zoomed In) + +**Internal layout:** A vertical timeline within the expanded node, scrollable if content exceeds the viewport. The timeline line runs vertically at 20% from the left edge, with timeline dots and cards to the right. + +Each role card contains: +- Role title (Space Grotesk, 500, `--text-primary`) +- Organisation (IBM Plex Sans, 400, `--cyan`) +- Date range (IBM Plex Mono, 400, `--text-dim`, in a pill badge with `--amber` background at 10% opacity) +- Expandable bullet points (collapsed by default, showing first 2 bullets with "Show more" toggle) + +**Color-coding per employer era:** +- NHS Norfolk & Waveney ICB roles: Left border amber (#D4874D) +- Tesco Pharmacy roles: Left border cyan (#00D4AA) + +This creates instant visual distinction between the "data/analytics" era and the "clinical pharmacy" era. + +**Background shift:** The expanded node's background subtly shifts warm (#0F1220) during ICB roles and cooler (#0A1018) during Tesco roles. The shift is barely perceptible but creates an atmospheric distinction. + +**Role data (from CV_v4.md):** + +1. **Interim Head, Population Health & Data Analysis** -- NHS Norfolk & Waveney ICB -- May-Nov 2025 + - Identified and prioritised a 14.6M efficiency programme through comprehensive data analysis; achieved over-target performance by October 2025 + - Built Python-based switching algorithm compressing months of manual analysis into 3 days, identifying 14,000 patients and 2.6M in annual savings + - Automated incentive scheme analysis; achieved 50% reduction in targeted prescribing within first two months + - Presented strategy and financial position to Chief Medical Officer on bimonthly basis + - Led transformation from practice-level data to patient-level SQL analytics + +2. **Deputy Head, Population Health & Data Analysis** -- NHS Norfolk & Waveney ICB -- Jul 2024-Present + - Managed 220M prescribing budget with sophisticated forecasting models + - Collaborated with ICB data engineering team to create comprehensive medicines data table + - Led financial scenario modelling for system-wide DOAC switching programme + - Led renegotiation of pharmaceutical rebate terms ahead of patent expiry + - Supported commissioning of tirzepatide (NICE TA1026) including financial projections + - Developed Python-based controlled drug monitoring system + - Educated colleagues on data interpretation and analytics best practices + +3. **High-Cost Drugs & Interface Pharmacist** -- NHS Norfolk & Waveney ICB -- May 2022-Jul 2024 + - Wrote most of the system's high-cost drug pathways spanning rheumatology, ophthalmology, dermatology, gastroenterology, neurology, and migraine + - Developed software automating Blueteq prior approval form creation: 70% reduction in forms, 200 hours immediate savings + - Integrated Blueteq data with secondary care activity databases + - Created Python-based Sankey chart analysis tool visualising patient journeys + +4. **Pharmacy Manager** -- Tesco PLC -- Nov 2017-May 2022 + - Identified and shared asthma screening process adopted nationally across ~300 branches, enabling ~1M in revenue + - Led creation of national induction training plan and eLearning modules + - Supervised two staff members through NVQ3 qualifications + +### Education Node (Zoomed In) + +**Internal layout:** A horizontal path with interactive milestone markers. The path is a subtle line running left-to-right across the expanded node, with milestone nodes along it. + +**Milestones:** +1. **A-Levels** (2009-2011) -- Highworth Grammar School. Mathematics (A*), Chemistry (B), Politics (C). Node shows as a small marker. +2. **MPharm (Hons) Pharmacy** (2011-2015) -- University of East Anglia. Upper Second-Class Honours (2:1). This is the primary milestone -- larger node. Clicking opens a detail panel with the research project information. +3. **Research Project** -- Drug delivery and cocrystals: 75.1% (Distinction). This sub-node opens a mini-visualization: simple SVG polyhedra representing cocrystal structures, rotatable by mouse drag. The polyhedra are wireframe-style in cyan (#00D4AA) on the dark background, gently rotating when idle. This is a small touch of delight that also subtly demonstrates technical capability (interactive 3D in the browser). +4. **Mary Seacole Programme** (2018) -- NHS Leadership Academy. 78%. Change management, healthcare leadership, system-level thinking. +5. **GPhC Registration** (August 2016-Present) -- Persistent certification. Shown as a badge rather than a path node. + +### Projects Node (Zoomed In) + +**Internal layout:** Each project is a sub-cluster -- a mini-constellation of technology + outcome nodes. The cluster is interactive: clicking zooms into it. + +**Projects:** + +1. **PharMetrics** -- Real-time medicines expenditure dashboard. Sub-nodes: "Power BI" (tech), "NHS Decision-Makers" (audience), "Actionable Analytics" (outcome). Link: medicines.charlwood.xyz +2. **Switching Algorithm** -- Python-based patient identification system. Sub-nodes: "Python" (tech), "14,000 Patients" (scale), "2.6M Savings" (outcome), "3 Days vs Months" (efficiency). +3. **Blueteq Generator** -- Automation tool for high-cost drug approvals. Sub-nodes: "Automation" (tech), "70% Reduction" (efficiency), "200+ Hours Saved" (outcome). +4. **Sankey Chart Tool** -- Patient journey visualization. Sub-nodes: "Python" (tech), "Data Visualization" (method), "Pathway Compliance" (outcome). +5. **Controlled Drug Monitor** -- Population-level opioid exposure tracking. Sub-nodes: "Python + SQL" (tech), "Patient Safety" (purpose), "Population Scale" (scope). + +Each project cluster has connection lines back to the Skills and Experience nodes, showing provenance. + +### Contact Node (Zoomed In) + +**Internal layout:** A clean, centered panel with contact information. The copy reads: "Ready to connect another node to the network." + +**Contact methods:** +- Email: andy@charlwood.xyz (clickable mailto link) +- Phone: 07795553088 +- LinkedIn: linkedin.com/in/andrewcharlwood (opens in new tab) +- Location: Norwich, UK + +Each contact method is a horizontal row with a Lucide icon (Mail, Phone, Linkedin, MapPin) in cyan, label in dim text, and value in primary text. Hovering a row highlights it with a subtle amber glow. + +--- + +## Interactions & Micro-interactions + +### Node Hover + +1. Cursor enters 120px proximity zone: node begins gravitational pull toward cursor (4-8px, spring physics) +2. Cursor enters node bounds: border transitions from `--node-border` to `--amber` (150ms). Glow intensifies. Label fades in below node (200ms fade). +3. Cursor exits: all effects reverse with matching timing. + +### Node Click (Zoom In) + +1. Clicked node scales up with spring animation (mass: 1, stiffness: 100, damping: 12) from ~56px to ~70% viewport width +2. Other nodes simultaneously scale down to 24px and drift to viewport periphery (spring, 600ms) +3. Background subtly darkens by 10% to create focus +4. Clicked node's internal content fades in with 200ms delay, 400ms duration +5. A subtle "zoom out" icon (Lucide Minimize2) appears top-left of expanded node + +### Zoom Out + +1. Triggered by: clicking zoom-out button, pressing Escape, or clicking any peripheral node +2. If clicking a peripheral node: that node zooms in while the current one zooms out (seamless swap, ~700ms) +3. If zooming out to hub: expanded node contracts, peripheral nodes return to orbital positions (spring physics, ~600ms). Internal content fades out before contraction begins. + +### Connection Line Reveal + +1. Triggered after visiting 3+ unique nodes. A floating "View Connections" pill button fades in at bottom-center. +2. Clicking the toggle: connection lines draw themselves between related nodes using `stroke-dashoffset` animation. Each line takes 600ms. Lines stagger by 100ms. +3. Hovering a connection line: the line brightens to 80% opacity, thickens by 0.5px, and a tooltip appears at the midpoint explaining the relationship. +4. Clicking the toggle again: lines retract (reverse `stroke-dashoffset`) and the button returns to "View Connections." + +### Sonar Pulse + +When any interactive action occurs (node click, lens switch, connection toggle), a subtle sonar ring (cyan, 20% opacity) radiates from the point of interaction. Duration: 400ms. Radius: 80px. This provides visual feedback that ties every interaction back to the ECG intro's sonar moment. + +### Ambient Drift + +All nodes in the hub view drift in micro-orbits: 2-3px movement radius, 8-12 second cycle, using sine-wave interpolation. The drift directions are randomized per node. This keeps the constellation alive without being distracting. The drift pauses during zoom transitions to prevent visual conflict. + +--- + +## Navigation + +### The Lens System + +A floating toolbar anchored to the bottom-center of the viewport (above the connection toggle, if visible). Contains 3 lens buttons: + +| Lens | Icon | Effect | +|---|---|---| +| **The Numbers** | Hash (#) | All nodes dim except those containing quantitative achievements. Amber-highlighted stat cards float above the dimmed constellation showing: 14.6M, 14,000, 220M, 2.6M, 200 hrs, 1M. Each card links to its source node. | +| **The Journey** | Clock / Timeline | Nodes rearrange from orbital positions into a horizontal chronological timeline. Spring animation. Leftmost = A-Levels (2009), rightmost = current role (2025). Nodes are spaced proportionally to duration. This is the traditional fallback view -- familiar and scannable. | +| **The Stack** | Layers | Nodes regroup by technical capability. Three vertical columns: "Clinical," "Technical," "Strategic." Within each column, relevant content from Experience, Skills, and Projects is aggregated. Shows Andy's capabilities cross-cut across all roles. | + +Clicking any lens animates the constellation into the new arrangement. Clicking the same lens again returns to the default hub view. Only one lens can be active at a time. + +**Lens transitions:** Nodes move to their new positions using spring physics (600ms). Content within nodes fades out during transition and fades back in once settled (200ms fade). + +### Keyboard Navigation + +- **Tab:** Cycles through nodes in logical order (Skills, Experience, Education, Projects, Contact) +- **Enter / Space:** Zooms into focused node +- **Escape:** Zooms out to hub view +- **Arrow keys:** When in hub view, moves focus between adjacent nodes (proximity-based adjacency) +- **L key:** Cycles through lenses (None --> Numbers --> Journey --> Stack --> None) +- **C key:** Toggles connection lines (after 3+ nodes visited) + +### Focus Indicators + +Keyboard-focused nodes receive a visible focus ring: 2px solid cyan (#00D4AA) with 4px offset. The focus ring pulses gently (opacity 0.7 --> 1.0, 1.5s cycle) to distinguish it from hover states. + +--- + +## Responsive Strategy + +### Desktop (> 1024px) + +Full spatial constellation experience. All features enabled: +- Force-directed node layout with gravitational cursor interaction +- Click-to-zoom node expansion +- Drag to rearrange nodes +- Connection lines with hover tooltips +- Full lens system +- Keyboard navigation + +### Tablet (768px - 1024px) + +Simplified constellation: +- Fewer ambient particles (15-20 instead of 30-50) +- No gravitational cursor pull (touch interfaces lack persistent cursor position) +- Tap to zoom into nodes +- Detail views render as full-screen overlays (sliding up from bottom, 90vh height) rather than inline expansion +- Connection lines are shown as a static overlay rather than animated reveal +- Lens toolbar moves to top of screen as a horizontal pill selector + +### Mobile (< 768px) + +The constellation transforms into a **vertical card stack**: +- Each card represents one constellation node. Cards are stacked vertically with 16px gap. +- Each card shows: icon, section title, one-line preview (e.g., "Python, SQL, Power BI + 15 more skills") +- Tapping a card expands it to show full section content (accordion-style, one expanded at a time) +- The lens toolbar becomes a horizontal pill selector at top of screen, sticky on scroll +- "The Journey" lens on mobile presents a standard vertical timeline +- "The Numbers" lens shows a simple stat card grid (2 columns) +- "The Stack" lens shows tabbed category view +- Background: solid deep navy. No particles, no gradient (performance). +- Connection lines: not shown on mobile. Instead, a "Related" section at the bottom of each expanded card lists connected items as text links. + +### Touch Interaction + +- **Tap node / card:** Zoom in (desktop/tablet) or expand (mobile) +- **Pinch-to-zoom:** Not supported (avoids conflict with browser zoom). Zoom is click/tap only. +- **Swipe:** On mobile, swipe horizontally between lens views. Swipe down to collapse an expanded card. +- **Long-press:** Not used (avoids confusion with system long-press behaviors). + +--- + +## Technical Implementation + +### Force-Directed Layout + +**Library:** `d3-force` (lightweight -- only the force simulation module, not all of D3). ~15KB gzipped. + +**Configuration:** +``` +forceSimulation(nodes) + .force('charge', forceManyBody().strength(-200)) + .force('center', forceCenter(viewportWidth / 2, viewportHeight / 2)) + .force('collision', forceCollide().radius(80)) + .force('radial', forceRadial(orbitDistance, cx, cy).strength(0.3)) +``` + +Nodes are initialized with target orbital positions. The simulation runs for ~100 ticks on mount to reach equilibrium, then continues running at low alpha for ambient drift. + +**Performance:** The simulation runs on `requestAnimationFrame` but only when nodes are moving (alpha > 0.001). When the constellation is at rest, the simulation pauses entirely. On resize, the simulation restarts with updated center coordinates. + +### Zoom Transitions + +**Library:** Framer Motion `AnimatePresence` with `layoutId` for seamless zoom. + +Each node has a `layoutId` matching its section key (e.g., `layoutId="skills"`). When the node expands, its `layout` animation triggers automatically. The detail content uses `AnimatePresence` for mount/unmount transitions. + +```tsx + + {isExpanded ? : } + +``` + +### Connection Lines + +SVG `` elements rendered in a fixed-position SVG overlay that spans the viewport. Paths are quadratic bezier curves between node center positions: + +``` +M startX startY Q controlX controlY endX endY +``` + +The control point is offset perpendicular to the line midpoint, creating a gentle arc. The offset direction alternates for adjacent lines to prevent overlap. + +**Animation:** `stroke-dasharray` set to total path length. `stroke-dashoffset` animated from total length to 0 (line drawing effect). Duration: 600ms with `ease-out` timing. + +### Star Particles + +A single `` element behind all content. 30-50 particles initialized with random positions and slow drift velocities. Rendered with `requestAnimationFrame`. Each particle is a 1-2px circle with 10-20% opacity. + +The canvas pauses rendering when the tab is not visible (`document.visibilityState`). On mobile, the canvas is not created (particles disabled for performance). + +### Detail Panel Scrolling + +Zoomed-in node content that exceeds the viewport height uses `overflow-y: auto` with custom scrollbar styling (thin, amber-colored on WebKit browsers). The scroll container is the expanded node's inner content area, not the page body. `body` overflow is set to `hidden` when any node is expanded to prevent background scrolling. + +### State Management + +React `useState` for: +- `activeNode: string | null` -- which node is expanded (null = hub view) +- `activeLens: 'numbers' | 'journey' | 'stack' | null` -- current lens +- `visitedNodes: Set` -- tracks which nodes have been viewed (for connection toggle threshold) +- `showConnections: boolean` -- connection lines visibility + +No external state management library needed. State is simple and localized. + +### Data Structure + +```tsx +interface ConstellationNode { + id: string; + label: string; + icon: LucideIcon; + orbitDistance: number; // relative to center, 0-1 + orbitAngle: number; // radians + glowColor: 'amber' | 'cyan'; + content: React.ReactNode; // rendered when expanded +} + +interface Connection { + from: string; // node id + to: string; // node id + strength: number; // 0-1, maps to line thickness + label: string; // tooltip text +} +``` + +--- + +## Accessibility + +### Screen Reader Experience + +The DOM order follows a logical reading sequence regardless of visual layout: + +1. Skip-to-content link (hidden, keyboard-accessible) +2. Andy Charlwood -- name and role title +3. Navigation: lens buttons + node list +4. Skills section content +5. Experience section content +6. Education section content +7. Projects section content +8. Contact section content + +The constellation visual is a progressive enhancement. Screen readers traverse the underlying DOM in document order, encountering all content as standard sections with headings. + +### ARIA Attributes + +- Each constellation node: `role="button"`, `aria-label="View [Section Name]"`, `aria-expanded="true|false"` +- Expanded node detail panel: `role="region"`, `aria-label="[Section Name] details"` +- Lens buttons: `role="radio"` within a `role="radiogroup"` with `aria-label="View mode"` +- Connection toggle: `aria-pressed="true|false"`, `aria-label="Show career connections"` + +### Keyboard Navigation + +Full keyboard support as detailed in the Navigation section. Tab order matches DOM order. Focus indicators are visible and high-contrast (cyan on dark navy exceeds WCAG AAA contrast). + +### Motion Preferences + +When `prefers-reduced-motion: reduce` is detected: + +- Constellation renders in static positions (no ambient drift, no spring physics) +- Node expansion uses opacity fade (200ms) instead of layout animation +- No sonar pulses +- No connection line drawing animation (lines appear immediately) +- No gravitational cursor pull +- Star particles are static (no drift) +- Lens transitions use crossfade instead of spatial rearrangement + +### Color Contrast + +All text meets WCAG AA contrast against the dark background: +- `--text-primary` (#ECECF0) on `--bg-deep` (#0A0E1A): contrast ratio 14.2:1 (AAA) +- `--text-secondary` (#8B8FA3) on `--bg-deep` (#0A0E1A): contrast ratio 5.8:1 (AA) +- `--amber` (#D4874D) on `--bg-deep` (#0A0E1A): contrast ratio 5.1:1 (AA) +- `--cyan` (#00D4AA) on `--bg-deep` (#0A0E1A): contrast ratio 8.3:1 (AAA) + +### First-Time Visitor Onboarding + +On first visit (checked via `localStorage`), a brief animated tour plays: +1. A pulsing ring highlights the center name (0.5s) +2. An arrow animates from center to a node with tooltip: "Click a node to explore" (1s) +3. The tooltip fades, and the constellation becomes interactive (0.5s) +Total: 2 seconds. Dismissible by clicking anywhere. Does not replay on subsequent visits. + +### The "Journey" Lens as Fallback + +The Journey lens rearranges the constellation into a standard horizontal timeline -- the most familiar CV layout pattern. This serves as a cognitive fallback for visitors who find the spatial navigation confusing. It is always accessible from the lens toolbar and via the L keyboard shortcut. + +--- + +## What Makes This Special + +This is the most **distinctive** of all 6 designs. No other CV site navigates like this. + +The constellation creates a mental map of Andy's career where everything is visible at once -- reducing cognitive load while increasing exploration curiosity. Visitors do not need to remember what is "below the fold" because nothing is below the fold. The entire career is laid out in space, available at a glance. + +The **connection web** is the signature feature. It shows not just WHAT Andy has done but HOW it all connects. The Python skill node connects to the switching algorithm project, which connects to the 14,000 patients identified, which connects to the 2.6M savings figure, which connects to the 220M budget he manages. Career coherence -- the idea that every role and skill builds on the last -- is visualized as a literal knowledge graph. + +The lens system adds intellectual depth. Three different lenses on the same data demonstrate analytical thinking -- the ability to view information from multiple angles. This is exactly what Andy does professionally: take the same prescribing dataset and extract different insights depending on the question being asked. + +Finally, the ECG-to-constellation transition is narratively powerful. A single heartbeat line becomes a universe of interconnected points. One signal becomes many. This mirrors Andy's career trajectory: from individual clinical interactions (one pharmacist, one patient) to population-level analytics (one analyst, one million patients). diff --git a/designs/04-the-dosage.md b/designs/04-the-dosage.md new file mode 100644 index 0000000..829c164 --- /dev/null +++ b/designs/04-the-dosage.md @@ -0,0 +1,724 @@ +# Design 4: The Dosage + +## Overview + +The user controls how much information they see. A pharmaceutical dosage metaphor -- self-titrate your information intake. Combined with a Cmd+K command palette for power users. The most accessible, recruiter-friendly, and fastest-to-relevant-content of all 6 designs. + +The core insight: most CVs and portfolios assume the visitor wants to see everything, in the order the author chose. This assumption wastes time. A hiring manager scanning 30 CVs wants key numbers in 5 seconds. A thorough reviewer wants the full picture. A curious peer wants to deep-dive into specific projects. These are three different users with three different "doses" of information needed. + +The Dosage design lets each visitor self-prescribe. Every piece of content exists at three depth levels (headline, summary, detail), and the visitor controls which level they see. The pharmaceutical metaphor is not cosmetic -- it reflects Andy's background as a pharmacist and his professional understanding that the right amount of the right information at the right time is what matters. + +Layered on top is a command palette (Cmd+K) borrowed from developer tools and productivity apps (Linear, Raycast, VS Code). This signals technical sophistication while providing a power-user shortcut to any piece of content. + +--- + +## ECG Transition + +**Starting point:** "ANDREW CHARLWOOD" is on screen in neon green (#00ff41) strokes on a black (#000) background. The ECG trace that drew it is still visible. The drawing head has stopped. + +**Then:** + +The neon green name begins a smooth **color shift**: green (#00ff41) transitions to teal (#0D7377) over 600ms. Simultaneously, the rough ECG-traced letterforms **morph** into clean Plus Jakarta Sans (later replaced by DM Sans in the final render) typography. The imprecise, hand-drawn quality of the ECG strokes straightens and refines -- serifs sharpen, curves smooth, letter spacing normalizes. This morphing happens over 1 second, overlapping with the color shift. + +As the name refines, it **rises** from center-screen toward upper-third position (approximately 28vh from top). The movement follows `cubic-bezier(0.22, 0.68, 0, 1.00)` -- fast departure, gentle settle. Duration: 800ms. + +Below where the name was positioned, a single horizontal line appears. This is the midline of the ECG trace -- the flatline that connected the letter strokes -- left behind as the name lifted away. The line transitions from neon green to teal (#0D7377) and **extends** smoothly to span the full viewport width. Duration: 600ms, starting 200ms after the name begins rising. + +The black background brightens to warm white (#F8F6F3) during the name rise. The transition uses `ease-out` timing over 1 second. + +Below the teal line, the subtitle "Deputy Head of Population Health & Data Analysis" fades in (300ms, 400ms delay after line extends). Then the prompt "What would you like to know?" fades in (300ms, 200ms delay after subtitle). Then the five choice buttons stagger in from below, 60ms apart, each with a subtle `translateY(12px)` to `translateY(0)` entrance. + +The teal line persists as a permanent UI element throughout the entire experience -- a visual heartbeat-monitor flatline that doubles as a pharmaceutical Rx signature line. When the visitor clicks any choice button, this line **pulses once**: a brief flash of neon green (#00ff41) glow that travels along the line's length left-to-right in 300ms, then fades. This callback to the ECG origin happens on every major interaction, creating continuity. + +**Duration:** ~1.5 seconds total. Deliberately calm. + +**Color journey:** Black (#000) --> Warm White (#F8F6F3). ECG green (#00ff41) --> Teal (#0D7377). The warm white has a faint warm undertone (not clinical pure white) that creates an approachable, paper-like feel. + +**The message:** "The dramatic part is over. Now it is about you." + +--- + +## Visual System + +### Color Palette + +| Token | Value | Usage | +|---|---|---| +| `--bg-warm` | `#F8F6F3` | Primary background -- warm off-white | +| `--bg-cream` | `#F0EDE8` | Card surfaces, elevated elements | +| `--teal` | `#0D7377` | Primary accent -- links, interactive elements, Rx line | +| `--teal-light` | `rgba(13, 115, 119, 0.08)` | Hover backgrounds, subtle tints | +| `--teal-medium` | `rgba(13, 115, 119, 0.15)` | Active states, progress fills | +| `--amber` | `#D4874D` | Secondary accent -- highlights, warmth | +| `--amber-light` | `rgba(212, 135, 77, 0.1)` | Amber tinted backgrounds | +| `--coral` | `#E8735A` | CTA buttons, urgent emphasis | +| `--text-heading` | `#1A1A2E` | Dark headings | +| `--text-body` | `#3D3D56` | Body text | +| `--text-muted` | `#8B8B9E` | Labels, metadata, tertiary text | +| `--border` | `#E2DED8` | Warm gray borders, dividers | +| `--ecg-green` | `#00ff41` | ECG callback pulses only | + +### Background Treatment + +The primary background is warm off-white (#F8F6F3) -- deliberately NOT pure white. A faint **paper grain texture** at 2% opacity overlays the background, created via a subtle CSS noise pattern. This creates a tactile, printed-document quality without being heavy-handed. + +```css +background-image: url("data:image/svg+xml,..."); /* tiny repeating noise SVG */ +background-size: 200px 200px; +opacity: 0.02; +``` + +The grain is purely cosmetic and does not affect readability. + +### Typography + +| Role | Font | Weight | Size | Notes | +|---|---|---|---|---| +| Display (name) | DM Sans | 700 | `clamp(2.5rem, 5vw, 4rem)` | Geometric, slightly rounded, approachable | +| Section headings | DM Sans | 700 | `clamp(1.5rem, 3vw, 2rem)` | | +| Subheadings | DM Sans | 500 | 1.125rem (18px) | | +| Body text | Inter | 400-450 | 15px / 1.7 line-height | `font-feature-settings: 'cv01', 'cv02', 'ss03'` for refined character shapes | +| Labels / metadata | Inter | 500 | 13px, uppercase, 0.05em tracking | | +| Data / statistics | JetBrains Mono | 400 | 14px | Used for numbers, percentages, code-like content | +| Large statistics | JetBrains Mono | 700 | `clamp(2rem, 4vw, 3.5rem)` | The "big numbers" in The Numbers view | + +**Type scale:** Modular ratio 1.25 (Major Third). Steps: 0.875rem, 1rem, 1.25rem, 1.5625rem, 1.953rem, 2.441rem, 3.052rem. + +**Font loading:** DM Sans and Inter from Google Fonts with `display=swap`. JetBrains Mono with `display=optional` (acceptable fallback to system mono for data labels). + +### Spacing System + +- **Base unit:** 4px +- **Scale:** 4, 8, 12, 16, 24, 32, 48, 64, 80, 120px +- **Section spacing:** 120px between major sections +- **Card padding:** 24px (mobile: 16px) +- **Grid:** 12-column grid, content centered in 8 columns (max-width: 720px for text content, 960px for card grids) +- **Viewport padding:** 32px sides (tablet: 24px, mobile: 16px) + +### Motion + +| Property | Value | Usage | +|---|---|---| +| Primary easing | `cubic-bezier(0.22, 0.68, 0, 1.00)` | Fast start, gentle settle. All entrance animations. | +| Exit easing | `cubic-bezier(0.4, 0, 0.2, 1)` | Standard Material-style exit. | +| Micro-interaction duration | 150-200ms | Hover effects, button presses, color transitions | +| Content transition duration | 300-500ms max | View switches, panel openings | +| Hard limit | 500ms | No animation exceeds this. Respect the visitor's time. | +| Stagger delay | 60ms | Between siblings in a list (buttons, cards, stat items) | +| Rx line pulse | 300ms | Left-to-right green flash on major interactions | + +### Material & Depth + +Flat design with subtle depth. No heavy drop shadows. + +- **Cards:** 1px solid `--border` (#E2DED8). Background `--bg-cream` (#F0EDE8). No border-radius greater than 12px. +- **Hover state:** Background lightens to white (#FFFFFF). Border transitions to `--teal-light`. Subtle `box-shadow: 0 2px 8px rgba(0,0,0,0.04)`. +- **Active/pressed:** Background shifts to `--teal-light`. Scale 0.98 (20ms spring). +- **Elevated elements** (command palette, tooltips): `box-shadow: 0 8px 30px rgba(0,0,0,0.08)`. Background white with 1px `--border`. + +### Signature Visual: The Measure Bar + +Every major statistic has a thin horizontal progress bar beneath it. This is the design's recurring visual motif. + +- Height: 3px +- Background track: `--border` (#E2DED8) +- Fill: `--teal` (#0D7377) +- Fill width: proportional to the stat's magnitude relative to a contextual maximum +- Animation: fills from 0% to target width on IntersectionObserver trigger, using `cubic-bezier(0.22, 0.68, 0, 1.00)`, 800ms duration +- Stagger: 100ms between adjacent Measure Bars + +Examples: +- "14.6M" efficiency programme: Measure Bar fills to 100% (it IS the maximum in context) +- "2.6M" savings: Measure Bar fills to ~18% (relative to 14.6M) +- "14,000" patients: full width in its own context group +- "200 hours" saved: Measure Bar fills to contextual proportion + +The Measure Bar is a quiet, persistent design element that gives every number a physical weight. Numbers alone are abstract; a bar makes them visceral. + +--- + +## Section-by-Section Design + +### Hero / Landing Page + +After the ECG transition completes, the visitor sees: + +**Top section (above the Rx line):** +- Andy's name in DM Sans 700, `--text-heading` color, centered +- Role title: "Deputy Head of Population Health & Data Analysis" in Inter 400, `--text-muted`, centered, below name + +**The Rx line:** Full-width horizontal line, 2px, `--teal`. Persistent throughout the experience. + +**Below the Rx line:** +- Prompt: "What would you like to know?" in DM Sans 500, `--text-heading`, centered +- 5 choice buttons in a horizontal row (wrapping to 2 rows on mobile): + +| Button | Label | Icon (Lucide) | +|---|---|---| +| 1 | The Numbers | Hash | +| 2 | The Journey | Clock | +| 3 | The Skills | Layers | +| 4 | The Impact | Zap | +| 5 | Everything | List | + +**Button styling:** +- Pill-shaped: `border-radius: 999px` +- Border: 1px solid `--border` +- Background: `--bg-cream` +- Text: DM Sans 500, 14px, `--text-body` +- Icon: 16px, `--teal`, left of label +- Hover: background white, border `--teal-light`, icon `--amber` +- Active: background `--teal-light`, text `--teal` + +The buttons stagger in from below (60ms apart) during the ECG transition. + +### The Numbers View + +Triggered by clicking "The Numbers" button. The button gains an active state (teal background, white text). The Rx line pulses green. Below the prompt area, content fades in: + +**Layout:** A centered column of large statistics, each one a self-contained card. + +Each stat card contains: +1. **The number:** JetBrains Mono 700, `clamp(2rem, 4vw, 3.5rem)`, `--text-heading` +2. **The context:** One line of Inter 400, 15px, `--text-body` +3. **The Measure Bar:** 3px tall, `--teal` fill, animated +4. **"Tell me more" link:** Inter 500, 13px, `--teal`, with ChevronRight icon. Clicking expands to the Summary depth. + +**Statistics displayed:** + +| Number | Context | Source | +|---|---|---| +| 14.6M | Efficiency programme identified through data analysis | Interim Head role | +| 14,000 | Patients identified by Python switching algorithm | Interim Head role | +| 220M | Prescribing budget managed with forecasting models | Deputy Head role | +| 2.6M | Annual savings from automated switching analysis | Interim Head role | +| 200+ hrs | Saved annually by Blueteq automation system | High-Cost Drugs role | +| ~1M | Revenue enabled by asthma screening process adopted nationally | Tesco role | + +**Depth levels for each stat:** +- **Headline** (default): The number + one-line context + Measure Bar +- **Summary** (first "tell me more" click): 2-3 sentence expansion explaining methodology and impact. "Tell me more" changes to "Full detail." +- **Detail** (second click): Full bullet points from the relevant role, tools used, timeline. A "Collapse" link returns to Headline level. + +### The Journey View + +Triggered by clicking "The Journey" button. Content below the prompt: + +**Layout:** A horizontal timeline running left-to-right across the full content width. + +**Timeline structure:** +- Horizontal line: 2px, `--border`, full width +- Timeline dots: 12px circles at each role position +- Current role dot: filled `--teal` +- Past role dots: filled `--bg-cream` with 2px `--teal` border + +**Role positions (left to right, spaced proportionally by date):** +1. Duty Pharmacy Manager (Aug 2016 - Nov 2017) +2. Pharmacy Manager (Nov 2017 - May 2022) +3. High-Cost Drugs & Interface Pharmacist (May 2022 - Jul 2024) +4. Deputy Head, Population Health & Data Analysis (Jul 2024 - Present) +5. Interim Head, Population Health & Data Analysis (May 2025 - Nov 2025) + +Each dot has a label below: role title (DM Sans 500, 13px, `--text-body`). Organisation name appears on hover in `--text-muted`. + +**Depth levels:** +- **Headline** (default): Timeline with role titles only. Compact. Scannable in 3 seconds. +- **Summary** (click a dot): A card expands below the timeline showing the role title, organisation, date range, and first 2 bullet points. Only one card open at a time (accordion). +- **Detail** (click "Full detail" in expanded card): All bullet points for that role appear. Tools/technologies mentioned are highlighted as inline teal badges. + +**Employer era color-coding:** +- NHS ICB roles: timeline dots and cards have a teal left border +- Tesco roles: timeline dots and cards have an amber left border + +### The Skills View + +Triggered by clicking "The Skills" button. + +**Layout:** Three category cards stacked vertically. + +**Categories:** +1. **Technical** -- Python, SQL, Power BI, JS/TS, Data Analysis, Dashboard Dev, Algorithm Design, Data Pipelines +2. **Clinical** -- Medicines Optimisation, Pop. Health Analytics, NICE TA, Health Economics, Clinical Pathways, CD Assurance +3. **Strategic** -- Budget Mgmt, Stakeholder Engagement, Pharma Negotiation, Team Development + +Each category card: +- Header: Category name in DM Sans 700, with count badge ("8 skills", "6 skills", "4 skills") +- Collapsed state: Header + top 3 skills shown as pill badges with proficiency percentages +- Expanded state (click header): All skills visible as a grid. Each skill shows: + - Name (DM Sans 500, 14px) + - Proficiency (JetBrains Mono 400, 13px, `--teal`) + - SVG circular gauge (64px diameter, `strokeDashoffset = circumference * (1 - level / 100)`, teal for Technical/Strategic, coral for Clinical) + - The gauge animates when revealed (1s ease-out with 80ms stagger between skills) + +### The Impact View + +Triggered by clicking "The Impact" button. + +**Layout:** Project cards in a 2-column grid (single column on mobile). + +**Projects:** + +1. **PharMetrics** + - One-line: "Real-time medicines expenditure dashboard for NHS decision-makers" + - Outcome badge: "Live Project" in teal + - Link: medicines.charlwood.xyz + - Tech badges: Power BI, SQL + +2. **Switching Algorithm** + - One-line: "Python algorithm identifying 14,000 patients for cost-effective alternatives" + - Outcome badge: "2.6M savings" in teal + - Stat with Measure Bar: "Compressed months of analysis into 3 days" + - Tech badges: Python, SQL + +3. **Blueteq Generator** + - One-line: "Automated prior approval form creation for high-cost drugs" + - Outcome badge: "200+ hrs/year saved" in teal + - Stat: "70% reduction in required forms" + - Tech badges: Python, Automation + +4. **Sankey Chart Tool** + - One-line: "Patient journey visualization for pathway compliance auditing" + - Tech badges: Python, Data Visualization + +5. **Controlled Drug Monitor** + - One-line: "Population-scale opioid exposure tracking for patient safety" + - Tech badges: Python, SQL + +Each card has three depth levels: +- **Headline** (default): Title + one-line description + outcome badge +- **Summary** (click): 2-3 sentence methodology description + tech badges +- **Detail** (click again): Full description from CV, related role context, connection to other projects + +### Everything View + +Triggered by clicking "Everything" button. This renders the complete CV in a traditional single-scroll layout: + +1. Hero section with name, title, summary paragraph +2. Vitals row (key stats as cards with Measure Bars) +3. Skills section with all three categories expanded +4. Experience section as vertical timeline with all bullet points +5. Education section with MPharm and Mary Seacole cards + A-Level note +6. Projects section as card grid +7. Contact section +8. Footer with Rx line callback + +This is the fallback for visitors who want a conventional CV experience. It is also the view that search engines and screen readers encounter (full content in DOM regardless of which button is clicked; the button views filter visibility, they do not remove content from DOM). + +--- + +## Interactions & Micro-interactions + +### Choice Button Selection + +1. User clicks a choice button +2. The Rx line pulses: a neon green (#00ff41) glow travels left-to-right along the line (300ms, ease-out) +3. The clicked button transitions to active state (teal background, white text, 150ms) +4. Previously active button returns to default state (150ms) +5. Content below the prompt area crossfades: old content fades out (200ms), new content fades in from below with `translateY(12px)` (300ms, 60ms stagger for child elements) + +### Depth Expansion + +1. User clicks "Tell me more" or an expandable element +2. The element smoothly expands: `max-height` transition from current to target (300ms, `cubic-bezier(0.22, 0.68, 0, 1.00)`) +3. New content fades in during expansion (opacity 0 to 1, 200ms, 100ms delay) +4. The "Tell me more" text changes to "Full detail" (if going from Headline to Summary) or "Collapse" (if at Detail level) +5. Chevron icon rotates 90 degrees (150ms) + +### Depth Collapse + +1. User clicks "Collapse" +2. Content fades out (150ms) +3. Element contracts (300ms, matching expansion easing) +4. Returns to Headline depth. "Tell me more" reappears. + +### Command Palette Open + +1. User presses Cmd+K (or clicks the search icon in the side rail) +2. Background dims with a 40% black overlay (200ms fade) +3. Palette container slides down from top with subtle `translateY(-8px)` to `translateY(0)` (250ms, spring) +4. Input field auto-focuses. Cursor blinks. +5. Placeholder text: "Search skills, roles, projects, or actions..." + +### Command Palette Search + +1. User types. Results appear in real-time (fuzzy matching via fuse.js) +2. Results grouped by section: "Experience", "Skills", "Projects", "Actions" +3. Each result: icon (section-colored) + title + breadcrumb (e.g., "Experience > Deputy Head > Python algorithm") +4. Arrow keys navigate results. Active result has teal background highlight. +5. Enter selects: navigates to the relevant content, expanding it to Detail depth. The corresponding choice button activates. +6. Escape closes the palette (200ms fade + slide up) + +### Command Palette Actions + +Beyond content search, the palette surfaces actions: +- "Download CV as PDF" -- generates and downloads a formatted PDF +- "Email Andy" -- opens mailto:andy@charlwood.xyz +- "View PharMetrics" -- opens medicines.charlwood.xyz in new tab +- "LinkedIn" -- opens linkedin.com/in/andrewcharlwood in new tab + +Actions appear in an "Actions" group at the bottom of results, marked with a subtle lightning bolt icon. + +### Rx Line Pulse + +Triggered on every major interaction (button click, depth change, command palette selection). The pulse is a neon green (#00ff41) glow that: +1. Appears at the left edge of the line +2. Travels rightward across the full viewport width (300ms, ease-out) +3. Fades from 60% opacity to 0% as it travels +4. The teal (#0D7377) base line is always visible -- the pulse is a highlight overlay + +This is the design's heartbeat callback. It ties every interaction back to the ECG origin without being heavy-handed. + +### Measure Bar Animation + +1. IntersectionObserver detects the stat entering the viewport (threshold: 0.3) +2. The 3px bar fill animates from width 0% to target width +3. Duration: 800ms, easing: `cubic-bezier(0.22, 0.68, 0, 1.00)` +4. Stagger: 100ms between adjacent Measure Bars +5. Trigger-once: bars do not re-animate on subsequent views + +--- + +## Navigation + +### The Side Rail + +A persistent minimal sidebar rail on the left edge of the viewport. Width: 48px. Background: transparent (does not occlude content). + +**Contents (top to bottom):** +- Search icon (Lucide Search, 20px) -- triggers command palette +- Divider line (1px, 16px wide, `--border`) +- 5 section icons matching the choice buttons: + - Hash (The Numbers) + - Clock (The Journey) + - Layers (The Skills) + - Zap (The Impact) + - List (Everything) +- Spacer (flex-grow) +- Dose meter (bottom) + +Each icon: 20px, `--text-muted` color. Active icon: `--teal`. Hover: `--amber`. + +**"Seen" indicators:** After a visitor has viewed a section (clicked the corresponding button), a 4px teal dot appears below that section's icon. This creates a subtle completeness signal without being gamified. + +Clicking any icon triggers the same behavior as clicking the corresponding choice button (Rx line pulse, content crossfade, button active state update). + +### The Dose Meter + +Positioned at the bottom of the side rail. A vertical bar, 4px wide, 48px tall. + +- Background track: `--border` +- Fill: `--teal`, growing upward +- Fill height: proportional to the percentage of total content elements the visitor has viewed (seen section / total sections, plus expanded items / total expandable items) + +No label, no percentage. Just a quiet fill. If the visitor has seen everything, the bar is full and gains a subtle amber glow. + +**Disable:** A tiny settings gear icon (12px, `--text-dim`) appears on hover near the dose meter. Clicking it toggles the meter off (it fades out and the gear icon shows a strikethrough state). Preference stored in `localStorage`. + +### Keyboard Shortcuts + +| Key | Action | +|---|---| +| `Cmd+K` / `Ctrl+K` | Open command palette | +| `Escape` | Close command palette / collapse expanded content | +| `1-5` | Switch to view 1-5 (Numbers, Journey, Skills, Impact, Everything) | +| `Tab` | Navigate between interactive elements in DOM order | +| `Enter` / `Space` | Activate focused button / expand focused content | +| `?` | Show keyboard shortcut overlay (dismissible) | + +--- + +## Responsive Strategy + +### Desktop (> 1024px) + +Full experience: +- Side rail visible on left edge +- Choice buttons in single horizontal row +- Content in 8-column centered grid (max-width 960px) +- Command palette as floating overlay (max-width 640px, centered) +- 2-column grid for project cards +- Horizontal timeline for Journey view +- Dose meter in side rail + +### Tablet (768px - 1024px) + +- Side rail collapses to a **bottom tab bar** (5 icons + search, horizontal, 56px height, anchored to bottom) +- Content fills full width minus 24px padding each side +- Choice buttons wrap to 2 rows if needed +- Command palette becomes full-screen overlay (slides up from bottom) +- Project cards in single column +- Horizontal timeline becomes scrollable (horizontal overflow with subtle scroll indicators) +- Dose meter moves to the right side of the bottom tab bar as a horizontal bar + +### Mobile (< 768px) + +- **Bottom tab bar:** 5 section icons + search icon. Same as tablet but more compact (48px height). Icons 18px. Active icon has teal dot below. +- **Choice buttons:** Stack vertically, full width minus 32px padding. Larger touch targets (48px height minimum). +- **Content:** Single column, 16px padding. +- **Command palette:** Full-screen overlay. Input at top. Results scrollable below. +- **The Journey timeline:** Converts from horizontal to **vertical** timeline. Roles stack vertically with timeline line on the left. More natural for vertical scrolling. +- **Project cards:** Single column, full width. +- **Skill gauges:** Grid of 2 columns instead of 3. +- **Dose meter:** Hidden on mobile (the bottom tab bar's "seen" dots provide equivalent information). +- **Rx line:** Still visible, but at reduced width (viewport width minus 32px, centered). Pulse animation still fires. +- **Depth expansion:** Touch-friendly. "Tell me more" links have 44px minimum touch target. Expansion uses the full screen width. + +The progressive disclosure mechanic is **inherently mobile-friendly** because it shows less content by default. Mobile users benefit most from the dosage model -- they are the most likely to want just "The Numbers" rather than scrolling through everything. + +--- + +## Technical Implementation + +### Choice Button State Management + +```tsx +type ViewMode = 'numbers' | 'journey' | 'skills' | 'impact' | 'everything'; + +const [activeView, setActiveView] = useState(null); +const [visitedViews, setVisitedViews] = useState>(new Set()); +``` + +Switching views triggers `AnimatePresence` for crossfade transitions. The DOM always contains all content (for SEO/accessibility); views are toggled via `display` or `visibility` with animated wrappers. + +### Three-Depth System + +A reusable `DepthContent` component manages the three levels: + +```tsx +interface DepthContentProps { + headline: React.ReactNode; + summary: React.ReactNode; + detail: React.ReactNode; + id: string; // unique identifier for dose tracking +} + +const DepthContent: React.FC = ({ headline, summary, detail, id }) => { + const [depth, setDepth] = useState<1 | 2 | 3>(1); + const { trackView } = useDoseMeter(); + + useEffect(() => { + trackView(id, depth); + }, [depth]); + + return ( +
+ {headline} + + {depth >= 2 && {summary}} + {depth >= 3 && {detail}} + + +
+ ); +}; +``` + +### Command Palette + +**Library:** Headless UI `Combobox` for the input + listbox pattern. `fuse.js` for fuzzy search (~6KB gzipped). + +**Search index:** Built at app initialization from all CV content. Each searchable item has: +- `title`: display name +- `section`: which view it belongs to +- `content`: searchable text (role descriptions, skill names, project details) +- `action`: what happens when selected (navigate to view, expand to depth, open URL) + +```tsx +const searchIndex = new Fuse(allContent, { + keys: ['title', 'content'], + threshold: 0.4, + includeScore: true, +}); +``` + +Results are grouped by section and capped at 8 results per group. + +### Side Rail Active Tracking + +The side rail uses the `useActiveSection` hook pattern: + +```tsx +const useActiveView = () => { + const [activeView, setActiveView] = useState(null); + // Tracks which button was last clicked + // Side rail icons reflect this state + return { activeView, setActiveView }; +}; +``` + +For the "Everything" view, IntersectionObserver tracks which section is in the viewport and updates the side rail's active icon accordingly. + +### Dose Meter + +A custom hook tracks content exploration: + +```tsx +const useDoseMeter = () => { + const [viewedItems, setViewedItems] = useState>(new Map()); + // key: content item ID, value: max depth viewed (1, 2, or 3) + + const totalItems = TOTAL_CONTENT_ITEMS; // constant + const totalDepthPoints = totalItems * 3; // max possible + const currentPoints = Array.from(viewedItems.values()).reduce((sum, d) => sum + d, 0); + const percentage = currentPoints / totalDepthPoints; + + return { percentage, trackView, viewedItems }; +}; +``` + +The meter fill is a CSS custom property (`--dose-fill`) animated via transition on the bar element. + +### Rx Line Pulse + +The pulse is a CSS pseudo-element on the line container: + +```css +.rx-line::after { + content: ''; + position: absolute; + top: 0; + left: 0; + height: 100%; + width: 100%; + background: linear-gradient(90deg, transparent 0%, #00ff41 50%, transparent 100%); + opacity: 0; + transform: translateX(-100%); +} + +.rx-line.pulsing::after { + animation: rxPulse 300ms ease-out forwards; +} + +@keyframes rxPulse { + 0% { opacity: 0.6; transform: translateX(-100%); } + 100% { opacity: 0; transform: translateX(100%); } +} +``` + +The `.pulsing` class is added via React state and removed after the animation completes (300ms timeout). + +### Measure Bar Animation + +Each Measure Bar is a simple div with CSS transition: + +```css +.measure-bar-fill { + height: 3px; + width: 0%; + background: var(--teal); + transition: width 800ms cubic-bezier(0.22, 0.68, 0, 1.00); +} + +.measure-bar-fill.visible { + width: var(--target-width); +} +``` + +The `--target-width` is set via inline style from the data. The `.visible` class is toggled by IntersectionObserver (trigger-once). + +### Performance Budget + +- **Fonts:** DM Sans (700, 500) + Inter (400, 450, 500) + JetBrains Mono (400, 700) = ~120KB total +- **fuse.js:** ~6KB gzipped +- **Framer Motion:** tree-shaken to AnimatePresence + motion div = ~30KB gzipped +- **Headless UI Combobox:** ~8KB gzipped +- **Total JS bundle (above framework):** ~44KB gzipped +- **No canvas rendering.** All visuals are DOM/CSS. This is the lightest design of all 6. + +--- + +## Accessibility + +This is the **most accessible** of all 6 designs. + +### Full Content Always in DOM + +Regardless of which choice button is active, all CV content exists in the DOM in logical order. The view buttons toggle `visibility` and `aria-hidden`, not `display: none` or DOM removal. This means: + +- Search engines index the full CV content +- Screen readers can traverse all content +- The "Everything" button simply makes everything visible -- it does not load additional content + +### Progressive Disclosure Patterns + +All expand/collapse interactions use standard WAI-ARIA patterns: + +- Expandable items: `aria-expanded="true|false"` on the trigger +- Content panels: `aria-hidden` mirrors expanded state +- Role: `aria-controls` links trigger to its content panel +- State change announced: trigger's `aria-expanded` update is announced by screen readers + +### Command Palette + +- Fully keyboard navigable: arrow keys, Enter, Escape +- `role="combobox"` with `aria-haspopup="listbox"` +- Results: `role="listbox"` with `role="option"` children +- `aria-activedescendant` tracks the currently highlighted result +- `aria-label="Search CV content and actions"` + +### Side Rail + +- `role="navigation"`, `aria-label="Section navigation"` +- Each icon: `role="button"`, `aria-label="View [section name]"`, `aria-pressed="true|false"` +- Dose meter: `role="progressbar"`, `aria-valuenow`, `aria-valuemin="0"`, `aria-valuemax="100"`, `aria-label="Content exploration progress"` + +### Focus Management + +- When a choice button is clicked, focus moves to the first content element in the new view +- When the command palette opens, focus moves to the search input +- When the command palette closes, focus returns to the element that opened it +- When content expands, focus moves to the newly revealed content +- Skip-to-content link is the first focusable element + +### Motion Preferences + +When `prefers-reduced-motion: reduce` is detected: +- Measure Bars show their final width immediately (no animation) +- No Rx line pulse animation +- View transitions use instant swap instead of crossfade +- Depth expansions use instant show/hide instead of animated expansion +- Choice button stagger is removed (all appear simultaneously) +- The ECG transition morph is simplified to a crossfade (green name fades out, clean name fades in) + +### Color Contrast + +All text meets WCAG AA on the warm white background: +- `--text-heading` (#1A1A2E) on `--bg-warm` (#F8F6F3): contrast ratio 14.8:1 (AAA) +- `--text-body` (#3D3D56) on `--bg-warm` (#F8F6F3): contrast ratio 8.2:1 (AAA) +- `--text-muted` (#8B8B9E) on `--bg-warm` (#F8F6F3): contrast ratio 3.5:1 (AA for large text) +- `--teal` (#0D7377) on `--bg-warm` (#F8F6F3): contrast ratio 5.4:1 (AA) +- `--coral` (#E8735A) on `--bg-warm` (#F8F6F3): contrast ratio 3.1:1 (AA for large text; used only for CTA buttons with white text overlay) + +Button text contrast: +- White (#FFFFFF) on `--teal` (#0D7377): contrast ratio 5.4:1 (AA) +- White (#FFFFFF) on `--coral` (#E8735A): contrast ratio 3.3:1 (AA for large text, 18px+) + +### Touch Targets + +All interactive elements have minimum 44px touch targets on mobile: +- Choice buttons: 48px height +- "Tell me more" links: 44px tap area (padded beyond visible text) +- Side rail / bottom tab icons: 44px tap area +- Command palette results: 48px row height + +--- + +## What Makes This Special + +The Dosage respects the visitor's time more than any other design. It answers the question every CV visitor has but never gets to ask: "What do you want to know?" + +A busy recruiter clicks "The Numbers" and sees Andy's quantitative impact in 5 seconds flat. A thorough hiring manager clicks "Everything" and reads the full CV. A curious peer clicks "The Impact" and deep-dives into the switching algorithm project through three depth levels. A power user hits Cmd+K and searches for "Python" to find every mention across all sections instantly. + +The pharmaceutical dosage metaphor is elegant without being heavy-handed. It is not costume design -- it is a genuine UX pattern. The concept of dose-response (the right amount of the right thing at the right time) is literally how pharmacists think, and it is literally how good information architecture should work. Andy's professional worldview IS the site's UX philosophy. + +The Rx line -- the persistent teal horizontal line with its green pulse callback -- is the design's signature. It is simultaneously: +- A remnant of the ECG animation (narrative continuity) +- A pharmaceutical prescription line (career metaphor) +- A progress indicator (interaction feedback) +- A visual anchor (layout stability) + +One element, four meanings. That is efficient design. + +The command palette signals technical sophistication to the right audience (developers, tech-adjacent roles) without alienating non-technical visitors (it is optional, triggered only by keyboard shortcut or an unobtrusive search icon). It says: "Andy knows how power users think, because he builds tools for them." + +And the dose meter -- that quiet little bar in the corner -- does something subtle and important. It tells the visitor: "There is more here if you want it." It creates gentle curiosity without pressure. It makes thoroughness feel rewarding rather than obligatory. Most CV sites give you everything and hope you read it. This one gives you control and trusts you to find what matters. diff --git a/designs/05-the-depth-stack.md b/designs/05-the-depth-stack.md new file mode 100644 index 0000000..8c07031 --- /dev/null +++ b/designs/05-the-depth-stack.md @@ -0,0 +1,851 @@ +# Design 5: The Depth Stack + +> Content exists at different depths. The surface shows the overview; clicking reveals progressively deeper layers. Push/pop navigation like iOS, but applied to a CV. The most mature, executive-grade design of all six. Luxury in restraint. + +--- + +## Overview + +The Depth Stack treats Andy's CV as a layered document rather than a scrolling page. The surface layer presents a spacious, editorial overview with headline information. Each section can be "pushed into" to reveal progressively richer detail -- role summaries become full achievement breakdowns, skill categories expand into proficiency grids, project titles open into case studies. + +This z-axis navigation model borrows from iOS push/pop transitions and applies it to career storytelling. The result feels immediately familiar on mobile (it maps to native navigation patterns) and strikingly distinctive on desktop (where the visible stack edges create a sense of explorable depth). + +The visual language is Refined Editorial: Fraunces serif headings on pure white, copper accents threading through every section, generous whitespace that says "my work speaks for itself." Every design decision communicates seniority and substance -- someone managing a nine-figure budget should have a site that feels commensurate. + +**Andy reads as:** Senior executive with depth of experience worth exploring. + +--- + +## ECG Transition + +Starting frame: Andy's name is on screen, neon green (`#00FF41`), on a black background. The ECG heartbeat has completed. The name glows. + +### Beat 1: The Fermata (400ms) + +Nothing happens. The name sits, glowing. This pause is deliberate -- a held breath, a fermata in music. The viewer has been watching fast-paced animation for approximately 10 seconds. The stillness is the first signal that this design values a different tempo. It says: "slow down, pay attention differently now." + +### Beat 2: The Color Drain (800ms) + +The neon green begins to drain from the letters, like ink being absorbed by paper. The color shifts through a desaturated green-gray, then through a warm neutral, and arrives at copper (`#B87333`). The transition is unhurried -- 800ms for a color shift is luxuriously slow in web animation terms. The glow disappears entirely. What was electric and luminous is now matte and material. The name looks *engraved* rather than projected. + +Color keyframes: +``` +0ms: #00FF41 (neon green, full glow, blur radius 8px) +200ms: #66CC77 (desaturated green, glow dimming, blur 4px) +400ms: #99AA88 (green-gray, glow gone, blur 0) +600ms: #B89977 (warm neutral, matte) +800ms: #B87333 (copper, fully matte, no glow) +``` + +### Beat 3: The Copper Thread Extends (600ms, overlapping) + +Starting at the 600ms mark of Beat 2 (so the line appears as the name reaches its warm neutral phase), a single horizontal line -- thin, copper, 1.5px -- extends from the left edge of the name toward both viewport margins simultaneously. It moves at a measured pace, reaching full viewport width in approximately 600ms. This is the birth of the Copper Thread, the site's visual signature. The line passes through the name's baseline, anchoring it. + +The line draws using a CSS `scaleX` transform from 0 to 1, centered on the name's left edge, eased with `cubic-bezier(0.25, 0.1, 0.25, 1)`. The line is a real DOM element (`
`) positioned to match the canvas baseline, creating the handoff point. + +### Beat 4: The Curtain Rise (1000ms) + +The white (`#FFFFFF`) enters not as a uniform fade but as a curtain rise: the lower portion of the viewport begins turning white, with the boundary rising smoothly upward. The boundary between black above and white below is a soft gradient (40px of blending, not a hard edge). + +Implementation: a CSS `linear-gradient` animated via CSS custom properties or `requestAnimationFrame`: +```css +background: linear-gradient( + to top, + #FFFFFF var(--curtain-progress), + #000000 calc(var(--curtain-progress) + 40px) +); +``` + +The copper line remains stationary at its position as the white rises past it. Below the copper line, on the now-white background, the hero subtitle and intro text begin fading in via pure opacity (no translation, no movement -- just materialization). Above the copper line, still against black, Andy's name in copper holds steady. + +When the white boundary reaches the name, the remaining black dissolves over 400ms. Andy's name transitions from copper to the primary text color -- deep navy (`#1A2B4A`) -- as the background behind it turns white. The canvas hands off to the DOM: the Fraunces heading element appears at matched coordinates, the canvas fades out. The name may drift subtly upward into its final hero position, but only 20-30px -- almost imperceptible. + +### Final State + +The page is fully white with the copper thread line. Below it, content is already partially visible. Above it, Andy's name in Fraunces serif sits with authority. The editorial layout has begun. The breadcrumb bar fades in at the top over 300ms. + +**Total transition duration: approximately 2400ms.** Deliberately the slowest of all six designs. But it never feels slow because every beat has purpose and the viewer is watching something transform, not waiting for something to load. + +**Emotional arc:** Electric --> still --> refined --> authoritative. The animation's raw energy is distilled into the most minimal design element possible (one line, one color). Less is more, stated as literal visual principle. + +### Reduced Motion Fallback + +If `prefers-reduced-motion: reduce` is set, the entire transition collapses to a simple 400ms opacity crossfade: black background fades to white, neon green name fades to navy Fraunces heading. The copper thread line appears immediately at full width. No curtain rise, no color drain, no drift. The creative transition is an enhancement, not a requirement. + +--- + +## Visual System + +### Color Palette + +| Role | Color | Hex | Usage | +|------|-------|-----|-------| +| Background | Pure white | `#FFFFFF` | Page background, primary layer surfaces | +| Surface | Cool light gray | `#F5F5F7` | Recessed areas, secondary layer backgrounds, detail sheet backgrounds | +| Primary text | True black | `#111111` | Body text, maximum authority and contrast | +| Secondary text | Cool gray | `#6E6E73` | Metadata, dates, labels, breadcrumb inactive segments | +| Primary accent | Deep navy | `#1A2B4A` | Headings (Fraunces), nav active state, primary interactive elements | +| Secondary accent | Copper/bronze | `#B87333` | The Copper Thread, achievement callout borders, link underlines, hover states, key numerals | +| Tertiary | Sage green | `#7A9E7E` | Healthcare context nods -- used very sparingly (1-2 instances per viewport maximum). NHS role indicators, health-related skill tags | +| Highlight | Pale blue | `#E8F0FE` | Text selection color, inline emphasis backgrounds, breadcrumb hover | +| Border | Light cool gray | `#D2D2D7` | Structural dividers, card edges (non-copper) | +| Layer shadow | Warm black | `rgba(26, 43, 74, 0.08)` | Stack edge shadows only (the one exception to the "no shadows" rule) | + +**Color psychology:** Navy and copper together read as institutional excellence -- think university crests, financial institutions, executive stationery. This palette says "I am senior, accomplished, and comfortable in my authority." The sage green whispers "healthcare" without shouting it. The warm off-black shadow color ensures even the stack-depth shadows feel intentional rather than default. + +**Color application rules:** +- Copper appears in only three contexts: the Thread lines, achievement border accents, and link/hover states. Never as backgrounds. Never as large areas of fill. +- Sage green is reserved for healthcare-specific callouts. If a section has no clinical relevance, sage green does not appear. +- Navy is used exclusively for headings and primary interactive elements. Body text is true black, not navy. + +### Typography System + +**Heading typeface: Fraunces** (Google Fonts, variable font) +- Optical size axis (`opsz`): 9-144. At display sizes (48px+), the letterforms become more graceful with higher stroke contrast. At text sizes, they simplify for readability. +- Weight axis (`wght`): 600 for section headings, 700-800 for the hero name. +- `font-feature-settings: 'ss01'` for the alternate glyph set (softer terminals). +- This is NOT a newspaper serif. Fraunces has warmth, personality, and a slight quirkiness in its soft serifs that prevents stuffiness. It's distinctive without being heavy. + +**Body typeface: Plus Jakarta Sans** (Google Fonts) +- Weights: 400 (body), 500 (emphasis/labels), 600 (bold body, card titles). +- Slightly rounded terminals give it warmth that pairs well with Fraunces without competing. +- Alternative: Source Sans 3 for a more neutral, technical feel. + +**Monospace typeface: Source Code Pro** (Google Fonts) +- Weight: 400 only. +- Used sparingly -- key statistics, dates in the timeline, budget figures. Never for running text. +- The restraint in mono usage distinguishes this from more technical-feeling designs. + +**Type Scale (modular ratio 1.333 -- Perfect Fourth):** + +``` +Display: clamp(3rem, 6vw, 5rem) -- Hero name in Fraunces 800 +H1: clamp(2.25rem, 4vw, 3.375rem) -- Section titles in Fraunces 600 +H2: clamp(1.5rem, 2.5vw, 2.25rem) -- Subsection titles in Fraunces 600 +H3: 1.25rem -- Card/item titles in Plus Jakarta Sans 600 +Body: 1.0625rem (17px) -- Base reading size, Plus Jakarta Sans 400 +Body-lg: 1.1875rem (19px) -- Pull quotes, lead paragraphs +Small: 0.875rem (14px) -- Metadata, dates, labels +Mono: 0.875rem (14px) -- Statistics, budget figures +``` + +**Line heights:** +``` +Display/H1: 1.1 (tight) +H2: 1.2 +H3: 1.3 +Body: 1.65 (generous for reading comfort at 680px column width) +Small: 1.5 +``` + +**Letter spacing:** +``` +Display: -0.02em (tightened for visual cohesion at large sizes) +H1: -0.015em +H2-Body: 0 (default) +Small/Meta: 0.01em (slightly open for legibility at small sizes) +Mono: 0.02em (open for numeral clarity) +``` + +**Weight philosophy:** Only three weights visible at any given time in any given viewport. Hierarchy comes through typeface contrast (Fraunces vs Plus Jakarta Sans), size, and color -- not through bold proliferation. Body text stays at 400. The contrast between ornate Fraunces headings and clean Plus Jakarta Sans body text creates sophisticated tension that carries the design. + +### Spacing and Layout Rhythm + +**Base unit:** 8px. All spacing is multiples of 8. + +**Section spacing:** 160px (20 base units) between major sections. This is the most generous spacing of all six designs. The whitespace is a design element, not wasted space. It signals: "there is no hurry here." + +**Primary content column:** Single column, max-width 680px -- the typographically optimal reading width for 17px body text. This creates a strong editorial centerline. Content never spreads to fill wide viewports; it holds its narrow column with confidence. + +**Pull quote / stat breakouts:** Key achievements and large statistics can break out to 800-900px width, creating typographic moments that punctuate the rhythm. These are the only elements that exceed the 680px column. + +**Horizontal rules:** Thin 1px lines in `#D2D2D7` between subsections within a layer. Classic editorial device. On layer transitions, the copper thread line replaces these at the section boundary. + +**Card internal spacing:** +``` +Card padding: 32px (4 units) +Card gap: 24px (3 units) +Content group gap: 16px (2 units) +Related item gap: 8px (1 unit) +``` + +**Vertical rhythm within a section:** +``` +Section title to first content: 48px +Between content groups: 32px +Between items within a group: 16px +Stat number to stat label: 8px +``` + +### Motion Design Language + +**Primary easing:** `cubic-bezier(0.25, 0.1, 0.25, 1)` -- close to CSS `ease`, but slightly more gentle on the deceleration. Nothing about this design should feel urgent or flashy. + +**Layer transition easing:** `cubic-bezier(0.32, 0.72, 0, 1.05)` -- a slight overshoot (1.05) on layer push creates a subtle spring feel that adds physicality without being playful. Duration: 300ms. + +**Duration philosophy:** +``` +Micro-interactions (hover, focus): 200ms +Content reveals (opacity): 600-800ms +Layer push/pop: 300ms +Detail sheet enter: 350ms +Detail sheet exit: 250ms (exits are always faster than enters) +Copper thread line draw: 400ms per section +Stagger between items: 80ms +``` + +**What moves:** +- Layer transitions: translateX + scale + blur (the z-axis push/pop). +- Content reveals: Pure opacity fade. No translateY, no translateX, no scale. Just opacity 0 to 1 over 600ms. This design *trusts its content* to be interesting without needing to slide into frame. +- The copper thread line: draws left-to-right via `scaleX` when a new section enters. +- Link underlines: draw left-to-right on hover. +- Large statistics: static. No counting animations. The number "14,000" is more powerful when it appears fully formed than when it counts up from zero. + +**What does NOT move:** +- Text once revealed. No parallax. No scroll-linked animations. +- Navigation elements. The breadcrumb updates its text, but doesn't animate position. +- Images (if any). They appear via opacity fade and stay put. +- The page itself. No scroll hijacking, no momentum effects. + +**Scroll reveals:** Content within a layer fades in when it enters the viewport (IntersectionObserver at 15% threshold). Trigger once -- never re-animate on scroll back. Stagger delay: 80ms between sibling elements. This is slower than other designs (which use 40-60ms) because the editorial pacing rewards patience. + +### Material and Texture + +**Primary approach: Pure flat.** No box shadows on cards. No gradients. No glassmorphism. No neumorphism. No blur effects on static elements. Depth comes entirely from typography scale, spacing, and the z-axis layer system. + +**The one shadow exception:** Stack edge shadows. When layers are pushed back, the background layer's right edge casts a subtle shadow (`box-shadow: -4px 0 16px rgba(26, 43, 74, 0.08)`) to create the illusion of physical stacking. This is the only shadow in the entire design. Its rarity makes it meaningful. + +**One texture element:** A very subtle halftone dot pattern at 1.5% opacity applied to `#F5F5F7` surface areas (detail sheets, secondary panels). This nods to print editorial heritage -- the kind of texture you'd see at 10x magnification on a high-quality magazine page. It's imperceptible consciously but adds tactile warmth subliminally. + +Implementation: +```css +.surface-texture { + background-image: radial-gradient(circle, #111111 0.5px, transparent 0.5px); + background-size: 12px 12px; + opacity: 0.015; +} +``` + +**Photography treatment:** If Andy adds a headshot or project screenshots, they should be desaturated to 60-70% and given a subtle duotone wash (navy + copper). No full-color photos breaking the palette. This maintains the editorial cohesion. + +### The Copper Thread (Visual Signature) + +The Copper Thread is a 1.5px horizontal line in `#B87333` that appears as a consistent visual motif: + +1. **Section dividers:** At the top of each major section, the copper line runs the full width of the content column (680px, or breakout width if applicable). It draws itself left-to-right when the section enters the viewport, taking 400ms. + +2. **Achievement callout borders:** Key achievements (stats, awards, notable outcomes) have a 2px copper left-border, creating a pull-quote-like emphasis within the flow. + +3. **Link underlines:** Interactive text links show a copper underline that draws left-to-right on hover (200ms `scaleX` transition). The underline is 1.5px, matching the thread weight. + +4. **Breadcrumb separator:** The `/` in the breadcrumb path is rendered in copper, visually connecting the navigation to the design signature. + +**Rules:** +- The copper line is always 1.5px. Never thicker, never thinner. +- It appears only in the horizontal orientation (never vertical, except as the achievement left-border). +- Its color is always `#B87333`. Never lighter, never darker, never transparent. +- This consistency is the point. One color, one weight, used everywhere -- it becomes the site's visual DNA. + +--- + +## The Z-Axis Navigation Model + +### Layer Architecture + +Content exists at three depth levels: + +| Level | Name | Contains | How to reach | How to exit | +|-------|------|----------|-------------|-------------| +| 0 | Overview | Hero, section summaries, headline stats | Default state / breadcrumb root | N/A (base layer) | +| 1 | Section | Full section content (roles, skills, projects) | Click section from Overview | Back button, Escape, swipe right, breadcrumb | +| 2 | Detail | Deep content (role achievements, project case study, skill breakdown) | Click item from Section layer | Back button, Escape, swipe right, drag-dismiss (sheets), breadcrumb | + +### Push Transition (Entering Deeper) + +When the user clicks a section or item to go deeper: + +1. The current layer scales to 95% and shifts left 20px (`transform: scale(0.95) translateX(-20px)`). +2. A 4px blur is applied to the receding layer (`filter: blur(4px)`). +3. The receding layer's opacity reduces to 0.4. +4. Simultaneously, the new layer slides in from the right edge of the viewport (`translateX(100%) --> translateX(0)`). +5. The new layer's content fades in via opacity as it arrives. + +Easing: `cubic-bezier(0.32, 0.72, 0, 1.05)` (slight spring overshoot). +Duration: 300ms. + +The receding layer remains partially visible as a "stack edge" on the left side -- the user can see they're one level deeper. + +### Pop Transition (Going Back) + +Triggered by: browser back button, Escape key, swipe right (mobile), or clicking a breadcrumb ancestor. + +1. The current (top) layer slides out to the right (`translateX(0) --> translateX(100%)`). +2. Simultaneously, the background layer scales back up to 100%, shifts back to center, deblurs, and restores full opacity. +3. The background layer's scroll position is preserved -- it returns exactly where the user left it. + +Easing: `cubic-bezier(0.32, 0.72, 0, 1)` (no overshoot on pop -- it should feel like settling back, not bouncing). +Duration: 250ms (exits are faster than enters). + +### Detail Sheets (Level 2 Alternative) + +The deepest level of content (project case studies, detailed role descriptions, full skill breakdowns) can also be presented as bottom sheets rather than full push layers. This is preferred for content that is a "deep dive" rather than a lateral navigation. + +**Sheet enter:** Slides up from the bottom of the viewport, covering 85% of viewport height. Background darkens to `rgba(0, 0, 0, 0.08)` -- barely perceptible, just enough to establish the overlay. Duration: 350ms, eased with `cubic-bezier(0.32, 0.72, 0, 1)`. + +**Sheet dismiss:** Drag downward past 30% of sheet height to dismiss (with momentum -- a fast flick also dismisses). Or press Escape. Or click the darkened background. The sheet slides back down, background un-darkens. Duration: 250ms. + +**Sheet styling:** `#F5F5F7` background (the surface color), `border-radius: 16px 16px 0 0` on top corners. A small drag handle indicator (32px wide, 4px tall, `#D2D2D7`, `border-radius: 2px`) centered at the top. Content inside follows the same 680px column and typography rules. + +### Stacked Edges (Visual Depth Cue) + +When the user is at Level 1 or Level 2, the background layers create visible "stack edges" on the left side of the viewport: + +- Level 1: The Overview layer is visible as a 20px sliver on the left, slightly blurred and dimmed. +- Level 2: Both the Overview and Section layers are visible as stacked slivers (Overview at ~12px peek, Section at ~20px peek), creating a visual "deck" effect. + +The stack edges cast the design's only shadows: `box-shadow: -4px 0 16px rgba(26, 43, 74, 0.08)`. This subtle depth cue tells the user "there is content behind this that you can return to." + +--- + +## Breadcrumb Navigation + +### Structure + +A persistent top bar, fixed to the viewport top, `height: 56px`, background `#FFFFFF` with a 1px bottom border in `#D2D2D7`. Contains: + +**Left side:** Site title -- "Andy Charlwood" in Plus Jakarta Sans 500, `#1A2B4A`. Always visible. Clicking returns to the Overview (Level 0), popping all layers. + +**Right side:** Breadcrumb trail, updating per depth level: +``` +Level 0: (no breadcrumb -- just the name) +Level 1: Andy Charlwood / Experience +Level 2: Andy Charlwood / Experience / NHS Norfolk & Waveney ICB +``` + +The `/` separator is rendered in copper (`#B87333`), connecting the breadcrumb to the Copper Thread signature. + +Inactive breadcrumb segments are in `#6E6E73` (secondary text color). The current (active) segment is in `#1A2B4A` (primary navy). Hovering an inactive segment shows the pale blue highlight (`#E8F0FE`) background and a copper underline draws in. + +Clicking any breadcrumb segment pops back to that level. If clicking "Experience" from Level 2, the detail layer pops and the user returns to the Experience section layer. + +### Section Picker + +Below the breadcrumb bar, a horizontal row of section labels acts as the primary navigation between sections at Level 1. Visible only when at Level 0 or Level 1. + +``` +Overview | Experience | Skills | Education | Projects | Contact +``` + +Labels in Plus Jakarta Sans 400, `#6E6E73`. Active section in `#1A2B4A` with a copper underline (2px, drawn left-to-right on activation). Horizontal scroll on mobile with fade-out indicators at edges. + +Clicking a section from the Overview pushes to that section (Level 1). Clicking a different section while already at Level 1 does a lateral slide (current section exits left, new section enters from right, 250ms). + +--- + +## Section-by-Section Design + +### Overview (Level 0 -- Base Layer) + +The landing state after the ECG transition completes. Maximum whitespace, minimum content. This layer exists to intrigue, not to inform exhaustively. + +**Layout:** +``` +[Breadcrumb bar - name only, no trail] +[Section picker - horizontal labels] + + [160px spacing] + + Andy Charlwood + Deputy Head of Population Health + & Data Analysis + + [48px spacing] + + NHS Norfolk & Waveney ICB + + [80px spacing] + + -------- copper thread line -------- + + [48px spacing] + + [Stat] [Stat] [Stat] + 14,000 GBP220M GBP2.6M + patients budget savings + identified managed annual + + [80px spacing] + + A pharmacist turned data analyst who + transforms healthcare operations through + Python-powered intelligence. + + [160px spacing] + + [Section cards - minimal, clickable] + Experience > Skills > Education > + Projects > Contact > +``` + +**Hero name:** Fraunces 800, navy `#1A2B4A`, `clamp(3rem, 6vw, 5rem)`. This is the name that transitioned from the ECG canvas. + +**Title:** Plus Jakarta Sans 400, `#6E6E73`, `1.25rem`. Understated. + +**Headline stats:** Three key numbers in Source Code Pro 400, copper `#B87333`, `clamp(2rem, 4vw, 3rem)`. Labels beneath in Plus Jakarta Sans 400, `#6E6E73`, `0.875rem`. Stats are separated by 48px and centered as a row. No animated counting -- the numbers appear fully formed. + +**Lead paragraph:** Plus Jakarta Sans 400, `#111111`, `1.1875rem` (body-lg). Maximum 2-3 sentences. Centered on the content column. + +**Section cards:** Minimal rectangles with section name in Plus Jakarta Sans 500, `#1A2B4A`, a right-pointing chevron (`lucide-react` `ChevronRight`) in `#6E6E73`, and a copper left-border (2px). On hover, the chevron shifts right 4px and turns copper. Clicking pushes to that section. + +### Experience (Level 1) + +Pushed from the Overview. Shows all roles with summary information, inviting deeper exploration. + +**Layout per role:** +``` +-------- copper thread line -------- + +NHS Norfolk & Waveney ICB +Deputy Head / Interim Head of Population Health & Data Analysis +Aug 2024 -- Present + +Built Python-based algorithms that compressed months of manual analysis +into 3 days. Managing a GBP220M prescribing budget. + +[View achievements -->] + +-------- 1px gray divider -------- + +NHS Norfolk & Waveney ICB +Senior Prescribing Data Analyst +Oct 2021 -- Aug 2024 + +... +``` + +**Role title:** Fraunces 600, `#1A2B4A`, `clamp(1.5rem, 2.5vw, 2.25rem)`. +**Organization:** Plus Jakarta Sans 500, `#111111`, `1.25rem`. +**Dates:** Source Code Pro 400, `#6E6E73`, `0.875rem`. +**Summary:** Plus Jakarta Sans 400, `#111111`, `1.0625rem`. 2-3 sentences maximum. +**"View achievements" link:** Plus Jakarta Sans 500, copper `#B87333`, with copper underline drawing on hover. Clicking pushes to the role detail (Level 2). + +Roles separated by 1px `#D2D2D7` dividers. Copper thread at the very top of the section only. + +### Experience Detail (Level 2 -- Detail Sheet) + +Opened from a specific role. Slides up as a bottom sheet covering 85% viewport. + +**Contents:** +- Role title (Fraunces 600) and organization (Plus Jakarta Sans 500) at the top. +- Dates in Source Code Pro. +- Full achievement bullets with quantified outcomes. Each bullet has a copper left-border if it includes a number. +- Methodology notes (what tools, what approach). +- "Key Impact" callout box: a `#F5F5F7` background card with a copper top-border, containing the single most impressive stat from that role in large Source Code Pro copper numerals. + +### Skills (Level 1) + +**Layout:** +``` +-------- copper thread line -------- + +Technical Skills + + Python SQL Power BI + Advanced Advanced Advanced + + JavaScript/TS Algorithm Design Data Pipelines + Intermediate Advanced Advanced + +-------- 1px gray divider -------- + +Leadership & Management + + Team Leadership Budget Management Stakeholder Engagement + NHS Leadership ... ... + Academy + +[Click any skill category for detailed breakdown] +``` + +At Level 1, skills are displayed as category groups with skill names and proficiency labels. No progress bars, no percentage circles -- this editorial design communicates proficiency through language ("Advanced," "Intermediate"), not charts. + +**Skill names:** Plus Jakarta Sans 500, `#111111`. +**Proficiency labels:** Plus Jakarta Sans 400, `#6E6E73`. +**Category titles:** Fraunces 600, `#1A2B4A`. + +Clicking a category pushes to a detail sheet showing: +- Full skill list with context (where each skill was applied, in which role). +- Related projects that demonstrate the skill. +- Certifications or training related to the category. + +### Education (Level 1) + +Two milestones, presented with editorial generosity. + +``` +-------- copper thread line -------- + +MPharm (Hons) Pharmacy +University of East Anglia, 2009 -- 2013 +2:1 Classification + +Research project: Drug delivery and pharmaceutical cocrystals +Final project grade: 75.1% (Distinction) + +[View detail -->] + +-------- 1px gray divider -------- + +NHS Leadership Academy +Mary Seacole Programme +2023 + +[View detail -->] +``` + +**Detail sheet for MPharm:** Full research project description, module highlights, committee involvement, grades. +**Detail sheet for Mary Seacole:** Programme overview, leadership competencies developed, application to current role. + +### Projects (Level 1) + +Project cards in a 2-column grid (breaking the single-column rule for visual variety and because project cards benefit from browsable density). + +Each card: +``` +[Project Title -- Fraunces 600, navy] +[One-line description -- Plus Jakarta Sans 400, #111111] +[Tech stack tags -- Source Code Pro 400, #6E6E73, 0.75rem] + + [-->] +``` + +Card background: `#FFFFFF` with 1px `#D2D2D7` border. Copper left-border (2px). On hover: border shifts to `#B87333` on all sides (200ms transition). + +Cards are max-width 320px in the 2-column layout. Gap: 24px. + +Clicking a card opens a detail sheet with: +- Full project description and problem statement. +- Technical approach and architecture. +- Screenshots (desaturated, duotoned). +- Quantified outcomes. +- Links to live demos or repositories (if applicable). + +**Projects to feature:** +- Controlled drug monitoring system +- DOAC switching dashboard +- Sankey chart analysis tool +- Python algorithms for prescribing analysis +- Population health data pipeline + +### Contact (Level 1) + +No drill-down needed. Clean, single-layer presentation. + +``` +-------- copper thread line -------- + +Get In Touch + +[Email address -- copper link] +[LinkedIn -- copper link] +[Location: Norwich, UK -- #6E6E73] + +[Optional: simple contact form with name, email, message fields] +``` + +Form inputs: 1px `#D2D2D7` border, Plus Jakarta Sans 400, `#111111`. Focus state: border shifts to `#B87333` (copper). Submit button: `#1A2B4A` background, white text, Plus Jakarta Sans 500. Hover: background shifts to `#B87333`. + +--- + +## Interactions and Micro-interactions + +### Hover States + +- **Text links:** Copper underline draws left-to-right (200ms `scaleX` from `transform-origin: left`). Underline is 1.5px to match the Thread. +- **Cards/clickable areas:** Border color transitions to copper (200ms). No shadow appears. No scale change. +- **Section picker labels:** Pale blue (`#E8F0FE`) background fades in. Copper underline draws in. +- **Breadcrumb segments:** Same pale blue background + copper underline. +- **Chevron arrows:** Shift right 4px, color transitions from gray to copper (200ms). + +### Focus States + +- **Interactive elements:** 2px outline in `#2563EB` (accessible blue) with 2px offset. This departs from the copper palette for accessibility contrast requirements. +- **Form inputs:** Border shifts to copper on focus. Label floats above and reduces size. + +### Active/Click States + +- **Buttons:** Scale to 0.98 for 100ms, then release. Subtle physical feedback. +- **Cards:** Background briefly shifts to `#F5F5F7` for 150ms before the push transition begins. + +### Loading States + +- If any layer requires async content loading, a single copper dot pulses (opacity 0.3 to 1.0, 800ms cycle) at the center of the content area. No spinners, no skeleton screens. A single dot, pulsing patiently. + +### Scroll Behavior + +- Smooth scroll within each layer. Each layer manages its own scroll position independently. +- When pushing to a new layer, the new layer starts scrolled to top. +- When popping back, the previous layer's scroll position is restored exactly. +- The breadcrumb bar is `position: sticky` at the top. It does not hide on scroll -- it is always present as the wayfinding anchor. + +--- + +## Responsive Strategy + +### Desktop (1024px+) + +- Layers slide in from the right, creating the full stack-edge depth effect on the left. +- Background layers peek out 20px on the left edge (visible stack). +- Detail sheets cover 70% viewport width, centered, with darkened backdrop. +- Breadcrumb bar shows full trail. Section picker is fully visible. +- Content column holds at 680px max-width. Pull quotes at 800-900px. +- Project cards in 2-column grid. + +### Tablet (768px -- 1023px) + +- Same z-axis layer model. Layers push to full width (no visible stack edge -- screen is too narrow for it to read clearly). +- Detail sheets slide up from bottom, covering 80% viewport height. +- Breadcrumb bar shows full trail. Section picker horizontally scrollable. +- Content column at 680px or viewport width minus 48px padding, whichever is smaller. +- Project cards in 2-column grid (tighter, 280px max-width per card). + +### Mobile (< 768px) + +This paradigm *excels* on mobile. The push/pop navigation maps directly to native iOS and Android navigation patterns. Users already know how this works -- swipe back, tap to go deeper. + +- Layers are full-screen with no visible stack edges. +- Swipe-right gesture triggers pop transition (detected via Framer Motion `onPan`). Threshold: 80px horizontal swipe with velocity > 500px/s, or drag past 40% viewport width. +- Detail sheets are full-screen with drag-to-dismiss. A small handle at the top (32px wide, 4px tall) invites the gesture. +- Breadcrumb simplifies to: back arrow (left chevron in `#1A2B4A`) + current section name. Tapping the back arrow pops one level. +- Section picker becomes a horizontally scrollable row with fade-out indicators at the edges. Active section centered in view on activation. +- Content column is viewport width minus 32px (16px padding each side). +- Project cards switch to single-column, full-width. +- Hero stats stack vertically (one per row) instead of three-across. +- Type scale reduces: Display to `2.5rem`, body stays at `1.0625rem` (reading comfort is non-negotiable). + +**Why this paradigm excels on mobile:** Most portfolio sites are long scrolling pages that feel generic on phones. The Depth Stack feels like a native app. Users navigate by tapping and swiping rather than scrolling through a monolithic page. Each "screen" (layer) has focused content optimized for the viewport. It's immediately familiar to anyone who uses a smartphone daily. + +--- + +## Technical Implementation + +### Core Components + +**`LayerStack`** -- The root navigation component. Manages: +- An array of layer history (stack of pushed layers with their component references and scroll positions). +- Push/pop functions that trigger transition animations. +- Keyboard listener for Escape (pop). +- Browser history integration (`pushState`/`popState` for back button support). +- `AnimatePresence` from Framer Motion wrapping the layer transitions. + +```typescript +interface LayerEntry { + id: string; + component: React.ComponentType; + props: Record; + scrollPosition: number; + breadcrumbLabel: string; +} + +interface LayerStackProps { + children: React.ReactNode; // Level 0 content +} +``` + +**`Layer`** -- Individual layer wrapper. Handles: +- Enter animation: `translateX(100%) --> translateX(0)` with scale and opacity. +- Exit animation: `translateX(0) --> translateX(100%)`. +- Background state: `scale(0.95) translateX(-20px) filter: blur(4px) opacity: 0.4` when behind another layer. +- Scroll containment (`overflow-y: auto`, `overscroll-behavior: contain`). +- Scroll position preservation via `useRef`. + +Framer Motion variants: +```typescript +const layerVariants = { + enter: { + x: '100%', + opacity: 0, + }, + active: { + x: 0, + opacity: 1, + transition: { + type: 'spring', + stiffness: 300, + damping: 30, + mass: 0.8, + }, + }, + background: { + x: -20, + scale: 0.95, + opacity: 0.4, + filter: 'blur(4px)', + transition: { duration: 0.3, ease: [0.32, 0.72, 0, 1] }, + }, + exit: { + x: '100%', + opacity: 0, + transition: { duration: 0.25, ease: [0.32, 0.72, 0, 1] }, + }, +}; +``` + +**`DetailSheet`** -- Bottom sheet component. Handles: +- Slide-up enter / slide-down exit animations. +- Drag-to-dismiss via Framer Motion `onPan` and `onPanEnd`. +- Backdrop overlay with click-to-dismiss. +- Focus trap (tab cycling within sheet, focus returns to trigger on dismiss). +- Escape key to dismiss. + +```typescript +interface DetailSheetProps { + isOpen: boolean; + onClose: () => void; + children: React.ReactNode; + title: string; +} +``` + +**`Breadcrumb`** -- Navigation breadcrumb. Consumes the layer stack context to display the current trail. Each segment is clickable to pop to that level. + +**`SectionPicker`** -- Horizontal section navigation. Tracks active section via layer stack state. On mobile, uses horizontal scroll with `scroll-snap-type: x mandatory`. + +**`CopperThread`** -- Reusable component for the signature line. Uses `useScrollReveal` to trigger the `scaleX` draw animation when entering the viewport. + +```typescript +interface CopperThreadProps { + width?: string; // default '100%' + className?: string; +} +``` + +### CSS Architecture + +- Tailwind CSS for utility classes and responsive breakpoints. +- CSS custom properties for the design tokens: + ```css + :root { + --color-navy: #1A2B4A; + --color-copper: #B87333; + --color-sage: #7A9E7E; + --color-surface: #F5F5F7; + --color-border: #D2D2D7; + --color-text-primary: #111111; + --color-text-secondary: #6E6E73; + --color-highlight: #E8F0FE; + --font-heading: 'Fraunces', serif; + --font-body: 'Plus Jakarta Sans', sans-serif; + --font-mono: 'Source Code Pro', monospace; + --thread-width: 1.5px; + --layer-transition-duration: 300ms; + --reveal-duration: 600ms; + } + ``` +- `perspective` on the layer stack container for true 3D depth cues: + ```css + .layer-stack { + perspective: 1200px; + perspective-origin: center center; + } + ``` +- `transform: translateZ()` on individual layers for z-axis positioning. +- `will-change: transform, opacity, filter` on animating layer elements for GPU compositing. + +### State Management + +Layer navigation state is managed via React Context: + +```typescript +interface LayerStackContext { + stack: LayerEntry[]; + push: (entry: Omit) => void; + pop: () => void; + popTo: (layerId: string) => void; + currentDepth: number; +} +``` + +No external state library required. The layer stack is the single source of navigation truth. URL state is synced via `window.history` for back button support and deep linking. + +### Performance Considerations + +- Layers behind the active layer are set to `pointer-events: none` and `will-change: auto` (remove from GPU layer when not transitioning) to reduce memory overhead. +- Content within background layers is set to `visibility: hidden` after the push transition completes (but remains in the DOM for instant restore on pop). +- Images lazy-load within detail sheets (they only load when the sheet opens). +- Font loading: Fraunces and Plus Jakarta Sans are loaded as variable fonts to minimize network requests. Use `font-display: swap` with a system serif fallback for Fraunces and system sans-serif for Plus Jakarta Sans. + +### Browser History Integration + +Each push operation calls `window.history.pushState()` with the layer ID. The `popstate` event listener triggers the `pop()` function. This means: +- The browser back button works naturally for navigating the layer stack. +- Deep links can reconstruct the layer stack (e.g., `/experience/nhs-icb` opens Overview → Experience → NHS ICB detail). +- Bookmarking a deep layer works correctly. + +--- + +## Accessibility + +### Semantic Structure + +- DOM order follows logical reading sequence regardless of visual layer presentation. +- Each layer is an `
` or `
` with appropriate heading hierarchy. +- The breadcrumb uses `