diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2ea2c7e..cd4db97 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,7 +18,7 @@ jobs: - name: Setup Node.js uses: actions/setup-node@v4 with: - node-version: '20' + node-version: '24' cache: npm - name: Install dependencies diff --git a/README.md b/README.md index c9f33c0..6159def 100644 --- a/README.md +++ b/README.md @@ -1,320 +1,61 @@ -# ShadowStack +# Andrew Thompson Portfolio -> A modern developer portfolio built to showcase projects, technical -> skills, experience, and ongoing software development work. +Recruiter-focused portfolio for Andrew Thompson, a backend-focused full-stack developer building database-driven applications and REST APIs. -ShadowStack is my personal developer portfolio. It serves as a central -place to present the applications I build, the technologies I work with, -and my growth as a software developer. +V2 replaces the former ShadowStack public identity while preserving the proven React, Vite, Tailwind CSS, Vercel, Resend, testing, CI, and responsive-layout infrastructure. -The project is built with React and Vite, styled with Tailwind CSS, and -deployed with Vercel. It also includes automated testing, Storybook -component previews, GitHub Actions continuous integration, and a -serverless contact endpoint powered by Resend. +## Positioning -## Features +The homepage is designed to answer the core recruiter questions quickly: -- Responsive single-page developer portfolio -- Project showcase with GitHub and live-demo links -- Skills and technology presentation -- Experience and developer background sections -- Animated UI elements using Anime.js -- Reusable React component architecture -- Contact form backed by a serverless API endpoint -- Email delivery through Resend -- Honeypot bot protection and server-side input validation -- Component previews through Storybook -- Automated tests with Vitest and React Testing Library -- GitHub Actions CI for linting, testing, and production builds -- Vercel deployment configuration with SPA routing and security - headers +- Who is Andrew? A backend-focused full-stack developer. +- What is he seeking? A junior backend or full-stack role. +- What can he build? Database-backed APIs and complete client-server applications. +- What makes him different? More than 15 years of leadership, troubleshooting, communication, operations, coaching, and process improvement. +- Where is the evidence? ReliefRoot, FlowBoard, verified technical capabilities, and his professional history. +- How can he be contacted? Direct email, contact form, résumé, LinkedIn, and GitHub. -## Tech Stack +## Featured Work -### Frontend +- **ReliefRoot** - TypeScript, Node.js, Express, PostgreSQL, Prisma, Docker, and GitHub Actions. +- **FlowBoard** - React, TypeScript, Express, PostgreSQL, JWT authentication, and Tailwind CSS. -- React 19 -- Vite 6 -- Tailwind CSS -- React Icons -- Anime.js +Older bootcamp projects are intentionally separated into Earlier Work. The portfolio does not feature itself as a homepage project. -### Contact API +## Stack -- Vercel serverless function -- Resend email API -- Environment-based API credentials -- Server-side validation -- Honeypot spam protection +- React 19 and Vite 6 +- Tailwind CSS +- Vercel serverless contact endpoint +- Resend email delivery +- Vitest and React Testing Library +- ESLint and GitHub Actions -### Testing & Development +## Local Development -- Vitest -- React Testing Library -- Storybook -- ESLint -- PostCSS -- Autoprefixer +Prerequisites: Node.js 24 and npm. -### Deployment & CI - -- Vercel -- GitHub Actions -- Node.js 20 - -## Project Structure - -``` text -shadowStack/ -├── .github/ -│ └── workflows/ -│ └── ci.yml -├── .storybook/ -├── api/ -│ └── contact.js -├── docs/ -├── public/ -├── src/ -│ ├── assets/ -│ ├── components/ -│ │ └── __tests__/ -│ ├── constants/ -│ ├── data/ -│ ├── hooks/ -│ ├── layout/ -│ ├── App.jsx -│ ├── index.css -│ └── main.jsx -├── eslint.config.js -├── index.html -├── package.json -├── postcss.config.js -├── tailwind.config.js -├── vercel.json -└── vite.config.js -``` - -## Application Structure - -The portfolio is organized into reusable sections composed in `App.jsx`: - -``` text -Layout -├── Header -├── Hero -├── About -├── Projects -├── Skills -├── Experience -├── Contact -└── Footer -``` - -Project information is separated from presentation components so -portfolio entries can be rendered from project data rather than being -hard-coded directly into the project UI. - -## Contact Form - -ShadowStack includes a serverless contact endpoint at: - -``` text -POST /api/contact -``` - -The endpoint accepts: - -``` json -{ - "name": "Example Name", - "email": "example@example.com", - "message": "Hello!", - "website": "" -} -``` - -`website` is a honeypot field intended to remain empty for legitimate -submissions. - -The API: - -1. Parses the submitted request. -2. Silently accepts requests that trigger the honeypot. -3. Validates required fields. -4. Enforces maximum input lengths. -5. Sends the message through Resend. -6. Uses the visitor's email as the reply-to address. -7. Returns a success or error response to the client. - -The Resend API key is read from the server environment: - -``` text -RESEND_API_KEY -``` - -Secrets should never be committed to the repository. - -## Getting Started - -### Prerequisites - -- Node.js 20 -- npm - -### Clone the repository - -``` bash -git clone https://github.com/Ajthompson88/shadowStack.git -cd shadowStack -``` - -### Install dependencies - -``` bash -npm install -``` - -### Start the development server - -``` bash +```bash +npm ci npm run dev ``` -The Vite development server runs locally on port `5173`. - -## Available Scripts - - Command Purpose - --------------------------- -------------------------------------- - `npm run dev` Start the Vite development server - `npm run build` Create a production build - `npm run preview` Preview the production build locally - `npm run lint` Run ESLint - `npm run test` Run the Vitest test suite once - `npm run test:watch` Run Vitest in watch mode - `npm run storybook` Start Storybook on port 6006 - `npm run build-storybook` Build the static Storybook site - -## Testing - -Automated tests use Vitest with React Testing Library. - -Run the full test suite: - -``` bash -npm run test -``` - -For development with automatic reruns: +The development server runs at `http://127.0.0.1:5173`. -``` bash -npm run test:watch -``` - -Tests live alongside the application code, including component tests -under: +## Verification -``` text -src/components/__tests__/ -``` - -## Storybook - -Storybook provides isolated previews for reusable UI components and -visual states. - -Start Storybook: - -``` bash -npm run storybook -``` - -Build the static Storybook output: - -``` bash -npm run build-storybook -``` - -## Continuous Integration - -The GitHub Actions CI workflow runs on pushes to `main` or `master` and -on pull requests. - -The pipeline: - -``` text -Checkout - ↓ -Setup Node.js 20 - ↓ -npm ci - ↓ +```bash npm run lint - ↓ -npm run test - ↓ +npm test npm run build ``` -Before pushing significant changes, the same checks can be run locally: +CI runs the same checks on pull requests. Production remains protected on `main` while V2 is developed on `feat/portfolio-v2`. -``` bash -npm run lint -npm run test -npm run build -``` - -## Deployment - -ShadowStack is configured for deployment on Vercel. - -The included `vercel.json`: - -- Rewrites application routes to `index.html` for SPA behavior. -- Adds `X-Frame-Options: SAMEORIGIN`. -- Adds `X-Content-Type-Options: nosniff`. - -The contact endpoint under `api/contact.js` is deployed as a serverless -API route. - -The deployment environment must provide: - -``` text -RESEND_API_KEY -``` - -## Design - -ShadowStack uses a dark/noir visual system with glowing accents, -reusable section layouts, card-based content, and responsive -presentation. - -Project-specific visual conventions are documented in: - -``` text -docs/style-guide.md -``` - -The goal is to keep the portfolio visually distinctive without allowing -presentation code to overwhelm the underlying component structure. - -## Development Goals - -ShadowStack is both a portfolio and an actively maintained software -project. Its purpose is to demonstrate more than a static résumé by -showing practical frontend development, API integration, testing, CI, -deployment, component design, and continued iteration. - -Current development priorities include: - -- Keeping project information current as new applications are built -- Improving portfolio accessibility and responsive behavior -- Expanding automated test coverage -- Refining reusable components and visual consistency -- Improving the contact workflow and production email configuration +## Contact Form -## License +`POST /api/contact` validates name, email, and message fields, uses a honeypot for simple bot filtering, and sends mail through Resend. Deployment requires `RESEND_API_KEY`. -This project is licensed under the MIT License. +## Work Items -Copyright © 2025--2026 Andrew Thompson +V2 work is tracked in `TODO.md` with stable `PORT-###` identifiers and machine-readable states. diff --git a/TODO.md b/TODO.md new file mode 100644 index 0000000..ef6cb52 --- /dev/null +++ b/TODO.md @@ -0,0 +1,15 @@ +# Andrew Thompson Portfolio V2 Work Items + +## Planned + +- [PLANNED] PORT-002 Add verified project screenshots and architecture visuals for ReliefRoot and FlowBoard. +- [PLANNED] PORT-003 Add dedicated project detail routes with deeper technical case studies. +- [PLANNED] PORT-004 Review résumé positioning and confirm the final public download. +- [PLANNED] PORT-005 Validate the contact form in a Vercel preview deployment. +- [PLANNED] PORT-006 Run accessibility, responsive, performance, and metadata audits. +- [PLANNED] PORT-007 Gather recruiter and developer feedback before replacing production. + +## Completed + +- [COMPLETED] PORT-000 Preserve production and create `feat/portfolio-v2` from verified `main`. +- [COMPLETED] PORT-001 Establish recruiter-first V2 structure and Andrew Thompson branding. diff --git a/api/contact.js b/api/contact.js index 55752f0..152f5bb 100644 --- a/api/contact.js +++ b/api/contact.js @@ -32,7 +32,7 @@ export async function POST(request) { } const { data, error } = await resend.emails.send({ - from: "ShadowStack Portfolio ", + from: "Andrew Thompson Portfolio ", to: ["aj.thompson8888@gmail.com"], replyTo: email, subject: `Portfolio message from ${name}`, @@ -69,4 +69,4 @@ ${message} { status: 500 } ); } -} \ No newline at end of file +} diff --git a/docs/style-guide.md b/docs/style-guide.md index 0f0e615..5cf7c76 100644 --- a/docs/style-guide.md +++ b/docs/style-guide.md @@ -1,65 +1,36 @@ -# ShadowStack Style Guide +# Andrew Thompson Portfolio V2 Style Guide -This guide captures the visual language, color tokens, and animation standards that keep the noir aesthetic cohesive across the app. +## Design Direction -## Palette & Typography +The visual system should feel like calm engineering work: dark gray surfaces, crisp typography, restrained electric-blue accents, subtle borders, and minimal motion. Andrew Thompson's name and professional role are the dominant identity. -- **Primary font:** `Orbitron` (loaded in `index.html`, exposed as `font-mono` via Tailwind `fontFamily.mono`). -- **Base text/background:** `body` applies `bg-noir-900 text-white font-mono` (`src/index.css`). -- **Noir palette (`tailwind.config.js`)** - `noir-50` … `noir-950` cover grayscale steps; prefer these for text, separators, and gradients. -- **Accent palette (`blueglow`)** - - `blueglow` / `blueglow.DEFAULT`: #00B2FF - - `blueglow-light`: hover/soft highlight #66D8FF - - `blueglow-dark`: deeper accent #007ACC - Always reference these Tailwind tokens instead of hard-coded hex values. +Avoid hacker tropes, excessive glow, glitch effects, looping animation, and decoration that competes with project evidence. -## Layout Primitives +## Typography -- **`
` (`src/layout/Section.jsx`)** - Wraps page blocks with `bg-gradient-to-b` gradients, padding, and a constrained `maxWidth`. Override via props (`gradient`, `maxWidth`, `padding`, `className`) rather than duplicating the wrapper markup. -- **Spacing Rhythm** - Components should follow the existing sections’ `py-20 px-6` defaults for consistent vertical rhythm unless a section needs special treatment. +- **Display:** Space Grotesk for headings and key labels. +- **Body:** Inter for readable long-form text. +- **Technical details:** JetBrains Mono for compact identifiers and technology labels. -## Reusable Utility Classes (`src/index.css`) +## Color -- `.glossy-card` - Use for cards/project tiles. Provides rounded borders, subtle gradients, and hover sheen via pseudo-elements. Apply directly on the card’s root element. -- `.glossy-section` - Adds a gentle radial highlight overlay to a section/container. Useful for callouts that need extra depth. +- `noir-950` is the page background. +- `noir-900` and `noir-800` provide surface depth. +- `blueglow` is reserved for actions, eyebrows, focus states, and small evidence markers. +- Slate text colors establish hierarchy without reducing readability. -When a custom style is needed more than once, add a minimal utility under `@layer components` and reference the class; avoid scattering bespoke CSS files. +## Components -## Animations +- `surface-card` provides restrained card depth and border treatment. +- `button-primary` is used for the single highest-priority action in a group. +- `button-secondary` supports résumé and contact actions. +- `tech-pill` labels technology without turning skills into an icon wall. +- `Section` owns page width and vertical rhythm. +- `SectionTitle` owns the eyebrow, title, description, and alignment pattern. -- **anime.js usage** - Reserved for hero/logo intros (`Hero.jsx`). Large-scale timeline animations belong there; other components should stick to Tailwind transitions for micro-interactions. -- **Tailwind animation tokens (`tailwind.config.js`)** - - `animate-fadeIn`, `animate-slideFadeIn`, `animate-glow`, `animate-glitch` map to custom keyframes. - - Apply via `className="animate-fadeIn"` etc. rather than redefining keyframes locally. -- **Hover/Focus states** - Favor Tailwind utilities (`transition`, `duration-300`, `hover:text-white`, etc.) for consistent easing. +## Motion and Accessibility -## Component Patterns - -- **Typography** - Section headings: `text-3xl sm:text-4xl font-bold tracking-tight text-white` (see `SectionTitle.jsx`). New headings should align with these sizes and tracking. -- **Buttons** - Follow the Contact form button example: accent background (`bg-blueglow-dark`), `hover:bg-blueglow-light`, rounded corners, and drop shadows for depth. -- **Forms** - Inputs use `bg-gray-700`, light placeholders, and `focus:ring-blueglow`; reuse those classes for additional form fields to maintain consistency. - -## Accessibility & Motion - -- Keep contrast high by pairing light text with the darker noir shades. -- Avoid autoplaying or looping heavy animations outside the hero. If new motion is introduced, provide gentle easing and keep durations <1s unless there is a clear narrative reason. -- Maintain the `scroll-behavior: smooth` experience by avoiding components that hijack scrolling. - -## Adding New Visual Patterns - -1. Prototype with Tailwind utilities directly in the component. -2. If the pattern is reused, extract a class into `src/index.css` under `@layer components`. -3. If new colors/animations are required, extend `tailwind.config.js`; include comments explaining intent. -4. Document the addition in this guide so future contributors understand when/how to use it. - -Use this document as the single source of truth for visual decisions. When in doubt, reference `tailwind.config.js`, `src/index.css`, and existing components before introducing new tokens or styles. +- Motion is limited to short entrance transitions and micro-interactions. +- `prefers-reduced-motion` disables meaningful animation. +- Interactive elements require visible keyboard focus. +- Heading order, form labels, link names, contrast, and touch-target sizing must be verified before release. diff --git a/index.html b/index.html index 1ee18e8..1e00a1a 100644 --- a/index.html +++ b/index.html @@ -2,14 +2,16 @@ - + - + - Andrew Thompson | ShadowStack + + + Andrew Thompson | Backend-Focused Full-Stack Developer - +
diff --git a/package-lock.json b/package-lock.json index 657aeca..d44df30 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,14 +1,13 @@ { - "name": "shadowstackdev", + "name": "andrew-thompson-portfolio", "version": "0.2.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "shadowstackdev", + "name": "andrew-thompson-portfolio", "version": "0.2.0", "dependencies": { - "animejs": "^3.2.2", "react": "^19.3.0", "react-dom": "^19.3.0", "react-icons": "^5.7.0", @@ -3095,12 +3094,6 @@ "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/animejs": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/animejs/-/animejs-3.2.2.tgz", - "integrity": "sha512-Ao95qWLpDPXXM+WrmwcKbl6uNlC5tjnowlaRYtuVDHHoygjtIPfDUoK9NthrlZsQSKjZXlmji2TrBUAVbiH0LQ==", - "license": "MIT" - }, "node_modules/ansi-regex": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", diff --git a/package.json b/package.json index a852bfe..3e984a4 100644 --- a/package.json +++ b/package.json @@ -1,10 +1,10 @@ { - "name": "shadowstackdev", + "name": "andrew-thompson-portfolio", "private": true, "version": "0.2.0", "type": "module", "scripts": { - "dev": "vite -- host 127.0.0.1 -- port 5173", + "dev": "vite --host 127.0.0.1 --port 5173", "build": "vite build", "lint": "eslint . --ext .js,.jsx,.ts,.tsx", "preview": "vite preview", @@ -14,7 +14,6 @@ "build-storybook": "storybook build" }, "dependencies": { - "animejs": "^3.2.2", "react": "^19.3.0", "react-dom": "^19.3.0", "react-icons": "^5.7.0", diff --git a/public/Andrew_Thompson_Resume.pdf b/public/Andrew_Thompson_Resume.pdf new file mode 100644 index 0000000..28c6611 Binary files /dev/null and b/public/Andrew_Thompson_Resume.pdf differ diff --git a/public/favicon.svg b/public/favicon.svg new file mode 100644 index 0000000..187ca3b --- /dev/null +++ b/public/favicon.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/App.jsx b/src/App.jsx index 8222dad..fc4316a 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -7,16 +7,18 @@ import Contact from "./components/Contact"; import Footer from "./components/Footer"; import Header from "./components/Header"; import Experience from "./components/Experience"; +import WorkMethod from "./components/WorkMethod"; export default function App() { return (
- - + + +