Task 1: Initialize React project with Vite, TypeScript, Tailwind

- Scaffolded project with React 18, Vite, TypeScript
- Configured Tailwind CSS with custom design tokens (teal, coral, ecg colors)
- Added Framer Motion and Lucide React dependencies
- Set up Google Fonts (Fira Code, Plus Jakarta Sans, Inter Tight)
- Created TypeScript interfaces for CV data types
- Added utility function for skill gauge calculations
- Quality checks passing: typecheck, build, lint all clean
This commit is contained in:
2026-02-10 15:39:29 +00:00
parent 06a312fbb7
commit f140c16881
21 changed files with 4603 additions and 2 deletions
+45
View File
@@ -0,0 +1,45 @@
import { useState } from 'react'
import type { Phase } from './types'
function App() {
const [phase, setPhase] = useState<Phase>('boot')
return (
<div className="min-h-screen bg-white">
{phase === 'boot' && (
<div className="fixed inset-0 bg-black flex flex-col justify-center p-10 font-mono text-sm">
<div className="text-ecg-green">CLINICAL TERMINAL v3.2.1</div>
<button
onClick={() => setPhase('content')}
className="mt-8 text-ecg-cyan hover:text-ecg-green transition-colors"
>
Press to skip boot sequence (placeholder)
</button>
</div>
)}
{phase === 'content' && (
<main className="max-w-[1000px] mx-auto px-8">
<section className="min-h-screen flex flex-col justify-center items-center text-center py-20">
<h1 className="font-primary font-bold text-5xl text-heading">Andy Charlwood</h1>
<p className="text-muted mt-2">Deputy Head of Population Health &amp; Data Analysis</p>
<span className="inline-block mt-2 px-4 py-1 border border-teal rounded-full text-xs text-teal font-medium">
Norwich, UK
</span>
<p className="mt-6 max-w-[560px] text-text">
GPhC Registered Pharmacist specialising in medicines optimisation, population health analytics, and NHS efficiency programmes.
</p>
</section>
<section className="py-20">
<h2 className="font-primary text-2xl font-bold text-heading text-center mb-8">
Components will be built in subsequent tasks
</h2>
</section>
</main>
)}
</div>
)
}
export default App
+67
View File
@@ -0,0 +1,67 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
:root {
--bg: #FFFFFF;
--text: #334155;
--heading: #0F172A;
--teal: #00897B;
--teal-light: rgba(0, 137, 123, 0.08);
--teal-medium: rgba(0, 137, 123, 0.15);
--coral: #FF6B6B;
--coral-light: rgba(255, 107, 107, 0.08);
--muted: #94A3B8;
--border: #E2E8F0;
--card-bg: #FFFFFF;
--shadow-sm: 0 1px 3px rgba(0,0,0,0.06);
--shadow-md: 0 4px 12px rgba(0,0,0,0.08);
--shadow-lg: 0 8px 24px rgba(0,0,0,0.1);
--radius: 16px;
--font-primary: 'Plus Jakarta Sans', system-ui, sans-serif;
--font-secondary: 'Inter Tight', system-ui, sans-serif;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: var(--font-primary);
font-size: 15px;
line-height: 1.7;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
@layer utilities {
.font-primary {
font-family: var(--font-primary);
}
.font-secondary {
font-family: var(--font-secondary);
}
.font-mono {
font-family: 'Fira Code', monospace;
}
}
@keyframes blink {
0%, 100% { opacity: 1; }
50% { opacity: 0; }
}
@keyframes seedPulse {
0%, 100% { text-shadow: 0 0 8px #00ff41, 0 0 16px #00ff41; }
50% { text-shadow: 0 0 14px #00ff41, 0 0 28px #00ff41, 0 0 40px rgba(0,255,65,0.3); }
}
.animate-blink {
animation: blink 1s step-end infinite;
}
.animate-seed-pulse {
animation: seedPulse 0.6s ease-in-out infinite;
}
+8
View File
@@ -0,0 +1,8 @@
export function calculateSkillOffset(level: number, radius: number): number {
const circumference = 2 * Math.PI * radius
return circumference * (1 - level / 100)
}
export function formatBootLine(text: string): string {
return text
}
+10
View File
@@ -0,0 +1,10 @@
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import './index.css'
import App from './App.tsx'
createRoot(document.getElementById('root')!).render(
<StrictMode>
<App />
</StrictMode>,
)
+41
View File
@@ -0,0 +1,41 @@
export interface Skill {
name: string
level: number
category: 'Technical' | 'Clinical' | 'Strategic'
color: 'teal' | 'coral'
}
export interface Experience {
role: string
org: string
date: string
bullets: string[]
isCurrent?: boolean
}
export interface Education {
degree: string
institution: string
period: string
detail: string
}
export interface Project {
title: string
description: string
link?: string
}
export interface ContactItem {
icon: 'phone' | 'mail' | 'linkedin' | 'mapPin'
value: string
label: string
href?: string
}
export type Phase = 'boot' | 'ecg' | 'content'
export interface BootLine {
html: string
delay: number
}
+1
View File
@@ -0,0 +1 @@
/// <reference types="vite/client" />