Skip to content

Sheet01 / 09 · Opening

TypeBricolage Grotesque 800 · clamped 2.5 to 7.4rem

Groundoklch(31% 0.08 40) · not black, never white

Contrast11.03:1 body · AAA

Craig Hawkes · Lead Developer & Design Systems Architect · Hamilton, Scotland

Available for lead, principal, head of engineering or first technical leader roles, full-time or contract, remote or hybrid

I build the system, the product that sits on top of it, and the gates that hold both.

Sheet02 / 09 · The work

Groundoklch(93% 0.025 85) · bone, not white

OrderBy what it proves, not by date

Span2016 to now · 6 posts, 2 current

StackNext.js · React Aria · OKLCH · Playwright · C#

Fifteen years, in the order that matters

The work, by what it proves rather than by when it happened.

A design system, then payment infrastructure, then the department I ran, then the side research. They're not the same size and aren't billed as though they are.

inclusive.io

Co-founder & Design Systems Engineer
Feb 2024 to now

An accessibility-gated design system and a navigable ATS product, built on my own across two full rebuilds. The best thing I've made, and the only reason I can say so is that something other than me checks it.

  • 117Components
  • 26ATS Workflows
  • 174Routes Audited
  • 0Axe Violations

117 components · 26 ATS workflows · 174 routes audited · 1,564 tests · 360 contrast pairings per run · 0 axe violations, both themes · three-tier W3C DTCG tokens in OKLCH holding 280px to 5K · React Aria

BPO Collections

Full Stack Developer, contract
Sept 2025 to Apr 2026

Hired to make an FCA-regulated collections site accessible, then asked to take on the payment migration instead. Least fashionable work I've done and easily the most useful: when it breaks it isn't an ugly page, it's somebody already in arrears who can't get the payment through.

  • 80%More mobile payments
  • 70%Of those on Apple Pay
  • 2,046Lines of C# written
  • 0Failed cutovers

Four screens to one · 80% more mobile payments, 70% Apple Pay · Fiserv end to end · legacy ASP.NET onto a typed Next.js stack · 0 failed cutovers

Head of Web

Paul McFadden Wealth
Dec 2022 to Jan 2024

Owning a revenue number is different from contributing to one. It means the funnel breaking is your problem at nine on a Sunday, and every argument about page speed, form length and one more tracking script lands on your desk. Most technical decisions turn out to be staffing decisions in a costume.

  • 3Hired
  • 7Co-managed
  • 60%Open rates up
  • 27%Cold replies up

Personally accountable for £10m+ of web revenue · hired three, co-managed seven · open rates up 60%, cold-outreach replies up 27%

Quoin, and six years in print

Graphic Designer, NOVAVAPES
Oct 2016 to Sept 2022

Print's had baseline grids since metal type; the web has never had one. Nobody asked me for it and I built it anyway, which tells you most of what you need to know. The six years before it are why the build half is any good. One shot, it goes on a press, and a wrong bleed arrives as a room full of boxes.

  • 228Of 239, seated
  • 0Sizes a fit has to change
  • 76msTo fit, with no browser
  • 4th of 9Coherence vs design systems

Quoin v1.26.0 · fits the design before the CSS exists · 228 of 239 seated on this page, 0 sizes changed, 76ms with no browser, 26 kB, no dependencies, MIT · NOVAVAPES, graphic designer to brand lead · DesignBuddy UK, 50+ clients since 2017

How I'd run your team

Seven principles, each linked on /work to where it has already been done

  • A standard is a gate, not a statement
  • You get the number without asking twice
  • Own the number, not a share of it
  • Hold both halves
  • Finish what other people left
  • Release like it's going to press
  • Bring people on, and write it down

Every other number here is checked by something. This bit isn't. It's the only part of the site that's an opinion rather than a measurement, so it gets to have an edge. Wrote it angry. Took the swearing out on the read-back and then put all of it straight back in.

Sheet03 / 09 · The problem

Claim typeArgument · nothing here is testable

Measure62ch · two columns above 52rem

Groundoklch(26% 0.065 40) · the same umber, one tier down

The thing I keep being hired to fix

We went from 200kbps to 2000mbps, everything got fancier, usability got cluttered and overcomplicated.

Three decades on from documents anybody could open, it takes two megabytes of JavaScript to show you a paragraph that was already text. The scroll gets hijacked so a logo can rotate on the way past. Zoom gets switched off because it breaks the layout. There's a functioning industry selling a widget that makes an inaccessible site compliant without making it one bit easier to read.

Machines got faster every single year of that, and the whole gain went on decoration. Almost none of it is malicious, because checking who got locked out is boring and the cool shit is what gets shared. But the outcome stops caring what you meant. Somebody can't read the page, the reason is a contrast ratio that looked nicer, and your information is now behind a door with you holding the key.

Theme parks are made for people, but not disabled people. That is slowly changing. The web is going the other way, and it has better tools and more bandwidth than it has ever had.

Nobody sets out to lock people out. It just never gets checked, and unchecked always drifts one way.

Sheet04 / 09 · The whole job

Accentoklch(72% 0.17 55) · 7.02:1 against the ink

SourceBritish Dyslexia Association style guidance

NoteFull-bleed accent · used once, on purpose

The whole job

The web needs to be for everyone.

That's it. That's the job.

The British Dyslexia Association asks for dark text on a light background rather than black on pure white, because white dazzles. WCAG pushes the contrast up, the dyslexia guidance pushes it back down, the maximum turns out not to be the target, and somebody has to sit and work out which reader they are serving. That is a person's job, not a linter's. The ground on this page is not white.

Sheet05 / 09 · The method

Gatenpm run verify · fails the build, not a report

Checked byEvery deploy, before it ships, not by me

Last runNo receipt in this build

Found9 real defects in my own AAA claim

How I'd change what you ship

A statement is not a standard.

A standard you can only assert is a standard you haven't met yet, and you won't know which until something checks.

Nearly every company shipping an inaccessible site has an accessibility statement somewhere on it. Almost none have a test that could fail. A statement costs an afternoon. A gate costs you the argument, in public, the day it goes red, which is exactly why it works and exactly why nobody wants to write it.

Writing the gate is the work. I put one in front of my own AAA claim and it took the claim straight off me: nine real defects nothing else had caught, including an unnamed dialog sitting behind every sheet in the system. That is the version of this you actually want on your team.

Shipped as gates, not intentions: 360 contrast pairings on every run · 1,564 tests across 174 routes · 0 axe violations in both themes · AA everywhere, AAA on thirteen criteria, three exceptions published rather than quietly dropped · a colour-literal ratchet that only moves down · a check on the delivered CSS rather than the source, because the build was quietly flattening OKLCH to hex and nothing said a word.

This is the bit that makes people go quiet in interviews. Not the number, the fact that it can fail.

Sheet06 / 09 · The commercial case

Measured+80% mobile payments · 70% Apple Pay

SourceClient analytics · their numbers, not mine

ConstraintFCA-regulated · PCI-DSS · live throughout

Why this pays for itself

The accessibility fix and the money fix keep turning out to be the same fix.

A person in arrears, on a phone, being asked to type a sixteen digit card number into a debt collection form has several good reasons to give up. Removing those reasons is an accessibility job and a conversion job at the same time. Mobile payments went up 80%, and 70% of them came through Apple Pay.

That flow had four screens because those two jobs belonged to two different people. Almost every expensive problem I get called into lives in that gap. Design says it's an engineering problem, engineering says it's a design one, and it sits there for a year with nobody's name against it. I can hold both halves, which is the only reason I can see it at all.

See how I did it: the payments case study

SagePay to Fiserv, finished solo after roughly half a dozen developers had left it short of testing: tokenisation, digital wallets, HMAC-SHA256, 3D Secure, store ID routing, 2,046 lines of C# I hadn't written since 2016, against a PCI-DSS estate of 97+ stored procedures that never stopped taking live money. Zero failed cutovers.

Nobody's posting a Fiserv gateway on Dribbble with a nice gradient behind it. It still moved more money than anything I've made.

Sheet07 / 09 · How I got here

OrderDeveloper first, design in the middle

SkillsListed only if shipped, no bars

Sourcesrc/data/about.ts · src/data/skills.ts

How I got here

You can't see a foundation. You only ever see what happens when there wasn't one.

I started building for the web at fourteen and had the degree by nineteen. Then I couldn't get a job doing it. A few years of freelance and whatever else paid, by the end of which I'd had enough of the industry entirely, so I retrained as a graphic designer and did that properly for six years instead. I came back to code in 2020 and spent the guts of a year rebuilding everything I'd let go rusty. So this isn't a designer who learned to build, it's the other way round, and the six years in the middle are the whole point. I make shit look good, then I build it.

Five people in my family and I'm the only one who isn't dyslexic. Mum, dad, both my sisters. That's why I check the contrast rather than claim it.

Frontend Core
React · Next.js · TypeScript · Tailwind CSS · HTML/CSS · JavaScript
Frameworks & Libraries
Angular · React Aria / React Stately · Radix UI · HeroUI · ShadCN · React Hook Form + Zod · Framer Motion · GSAP
Design Systems
Design Tokens (W3C DTCG) · OKLCH Colour · Atomic Design · Component APIs · Storybook · Figma plugin development
Version Control & CI/CD
Git · GitHub / GitHub Actions · CI/CD pipelines · Trunk-based workflow · Semantic commits (commitlint, husky) · Automated release gates
Deployment & Platform
Vercel · Preview and production environments · Cutover and rollback runbooks · Zero-downtime migration · Environment and secret management · IIS / Windows Server (legacy estates)
Shipping & Release
npm package publishing · Turborepo monorepos · Changesets versioning · publint / are-the-types-wrong · knip, syncpack
Backend & Infrastructure
Node.js · ASP.NET (VB.NET & C#) · SQL Server / T-SQL · REST APIs · Payment gateways (Fiserv) · PCI-DSS estates
Testing & Accessibility
WCAG 2.2 (AA gated, AAA where it holds) · axe-core / jest-axe · Playwright · Vitest / Jest · Testing Library · Lighthouse CI · Pa11y · Chromatic
Research & Documentation
Technical documentation · Design system specification · Competitive and standards research · Accessibility auditing · Handover and onboarding packs · Architecture decision records
Design & Creative
UI/UX Design · Adobe Creative Suite · Brand Identity · Prototyping · Art Direction · Typography
AI & Agent Systems
LLM providers (OpenAI, Anthropic, Google) · Prompt & context design · MCP servers · Cursor agent workflows · Tool calling & evaluation · llms.txt / AEO
AI & Agent Systems The speed is real and so is the rubbish, and the difference between them is entirely whether somebody read the thing before it went out. I run agent sessions the way I run builds. Tight brief, obvious done-state, nothing ships that I haven't read end to end. The skills and MCP servers and rules sitting in this repo are there because without them the work wanders off and comes back sounding like everybody else. Which is the one thing I'm not interested in being.

Best thing about years in print? You learn quickly that design isn't pixels. It's whether somebody actually lifts the thing off a shelf in a shop. No hover state is saving you there. Everything I build still starts with that question.

Sheet08 / 09 · Open non-conformances

Count43 · ratchet, down only

First count184 on 5 September, 130 of them in a stylesheet nobody was serving

Fixed since11, the last on 15 September

StatusOpen, published, not deleted

Also2 level A failures, live for months

The bit most people delete before you arrive

43 things on this site are still wrong.

43 colour literals bypass my own theme. I could have quietly fixed them before you got here. It's on the page because a portfolio that only shows the bits that worked is a showreel, and because if you're hiring at this level the useful question isn't whether somebody has open defects, it's whether they can tell you the number without being asked twice.

I also claimed AAA for eight months while the test that checks it had never once loaded. Eight typefaces gave me the same number and I read it as consistency; every measurement was against a font that had never loaded. A control that can't fail will agree with you three times in a row and sound authoritative doing it.

Ratchet: down only, one commit at a time, in public. A full audit of this build found fourteen more, nine fixed the same day, and two were level A failures that had been live for months because the suite only ever ran at one screen width. All of it is written up rather than deleted.

Every honest number on here started out as a worse one I'd talked myself out of publishing.

Sheet09 / 09 · Enquiries

ReplyInside the hour, UK working hours, by a person

Open toLead, principal, head of engineering or first technical leader · contract or permanent

Not on this sitePreloaders · hamburger menus · carousels

Enquiries

Tell me what's breaking.

If you already know which detail it is, say so in the first line and we'll skip the discovery call. I read every email that comes in. If it's a template I'll know, because you all use the same three openers.

No preloaders, no hamburger menus. Neither's on here and neither ever will be. That's not restraint, that's just not doing the stupid thing.