Forever Giving
Compliance-grade charity onboarding, designed and built in code.

Client
Forever Giving
My Role
Stack
Claude
Cursor
Storybook
Supabase
React, TypeScript, Radix UI,
CSS Modules, Turborepo
Date
Q4 2025 - Q2 2026
Background
Forever Giving is a startup. Charities onboard onto the platform and then create NFT collections that get sold on the FG website, and to give those collections extra appeal the charities partner with famous artists to create the work.
Once a charity is on the platform it isn't running a one-off sale. It gets its own landing page within the FG website that tells its story, shows the collections it has released, and credits the artists it has collaborated with. It's a returning product surface the charity owns and keeps adding to over time.
I designed and built two flows end to end: the onboarding and verification a charity goes through to join, and the flow a charity uses to create an NFT collection once it has been verified. I held the product owner and designer roles at the same time, working in code rather than handing a design file to someone else to build.
8
UK charity legal entity types verified
2
Government API registers integrated
50+
Components documented in Storybook

See it in action
A charity starts by telling the platform what kind of organisation it is, choosing its legal entity type from a fixed list. That single choice determines everything that follows: which identifiers it is asked for, which register its details get checked against, and what the rest of the flow looks like.
Picking the entity type
A charity starts by telling the platform what kind of organisation it is, choosing its legal entity type from a fixed list. That single choice determines everything that follows: which identifiers it is asked for, which register its details get checked against, and what the rest of the flow looks like.
The live KYB check
When a charity enters its number, the platform calls the relevant government register, the Charity Commission or Companies House, and brings back the organisation's real registered details to confirm. The reviewer and the charity are looking at live register data, not something typed in by hand. The flow then confirms the people attached to the organisation, its directors or trustees, and its registered address.
Creating a collection
Once a charity is verified, the collection-creation flow opens up in its own space on the platform. The charity names the collection, attaches the collaborating artist, adds the artwork and its editions, sets pricing, and builds the landing page the collection will be sold from.
Designing the flow
The product had two audiences who were new to almost all of it: charity staff setting themselves up, and first-time buyers purchasing art. So the through-line across every screen was making an unfamiliar, regulated, web3 product feel learnable, through the language it used and the pace it asked people to work at.
Speaking web2 to a web3 product
The platform runs on NFTs, but its users are charity staff and first-time buyers, not crypto natives. So across every screen the rule was the same: keep the web3 machinery out of sight and meet people in familiar language. Each industry term got one of three treatments, rename, explain, or hide.
Rename. An NFT is never an NFT. It's a Forever Giving Certificate, an FGC, named for what it does rather than what it technically is.
Explain. Terms that had to stay were translated in plain language, inline, the moment they appear, an FGC is "like a deed for a house and a will rolled into one", with fuller help articles a click away for anyone who wants more.
Hide. Minting never appears at all. It happens invisibly when a buyer purchases an FGC, so the charity never learns the concept exists. The best jargon is the kind a user never meets.
Compliance is the exception. The regulated terms can't be reworded without changing what's legally being asked, so the softening moves to the wrapper: the verification step is "Confirm charity details", not "Complete your KYB", explained as fraud protection. The data stays exact, the framing stays human.

Onboarding you can put down and come back to
Verifying a charity and getting it ready to sell takes more than one sitting, and the person doing it is fitting it around actually running a charity. So onboarding isn't a single linear wizard. It's a home dashboard that breaks the work into sections and reveals them progressively.
The live task, confirming charity details, sits at the top, while the next steps, linking a business bank account, setting up the first collection, inviting colleagues, stay visibly locked until the step they depend on is complete. Progress can be saved and resumed at any point within a section, so the charity is never forced to finish in one hit and never loses its place. KYB is the gate: clearing it is what unlocks the rest of the platform.

Designing the dead-end out
The hardest state to design well is the one where the automated check can't settle, because the organisation may be entirely legitimate and simply has no machine-readable register to answer for it. From the charity's side this is deliberately not a rejection. The state reads as a case a person is now reviewing, with a clear label, a calm tone, and a next step, so a charity that can't be auto-verified is told it's in hand rather than turned away. The same outcome that routes a case to the FG Admin queue reads, on the charity's screen, as the work simply moving to a human.
Validated with users, not assumed
None of the above was shipped on instinct. The journeys were prototype-tested with seven charity staff, drawn from a mix of organisations from small to large, and the clearest finding decided where the design effort went. The people using this are effectively two users at once. On compliance they're experts: they've registered charities and been through this kind of verification many times, so the entity-type choice and the KYB steps were close to second nature and needed no hand-holding. The unfamiliarity was all on the other side, in the web3 mechanics and in selling art. So the soft naming, the inline explanations and the help were concentrated where people actually got stuck, and the compliance steps were kept fast and precise rather than padded with guidance this audience didn't need. It's also why the compliance wording stays exact while the web2 language does the work everywhere else on the platform.
The operating model
On a regulated product the most valuable design artefact isn't a screen. It's the document that defines how the platform is allowed to behave.

Define the rules before the screens
On a compliance product the screens are downstream of the rules, so the rules come first. Before any flow gets designed, the platform's allowed behaviour is worked out and written down as a single source of truth. On Forever Giving that document is the KYB Decision Matrix, and the UI, the backend and the admin workflow all answer to it.
Build design-in-code so the system survives handover
Code is the source of truth, not a design file that informs a separate build somewhere downstream. The design system lives in the repo as real, documented React from the first hour. That matters most at the moment a project changes hands, which on Forever Giving was always the plan. The team inherits a working codebase and a documented system, not a specification to rebuild.
Design for the exceptions, not just the happy path
A regulated flow is mostly defined by what happens when the automated answer isn't available. The work that separates a real compliance product from a demo is the graceful fallback: the legitimate organisation that can't be verified automatically still has a route through, handed to a human rather than turned away.
The problem
The hard part of this work is that "a charity" is not one thing in UK law. There are eight different legal entity types a charity can be registered as, and they do not verify the same way.
Some are registered only with the Charity Commission. Some are also registered with Companies House as a company. Some sit with other regulators, and some have no machine-readable register at all and can only be checked by hand.
That difference runs deeper than which API to call. Each entity type needs different identifiers captured at the start, gets checked against a different source, stores a different shape of evidence, and needs a different route through the system when the standard check isn't available.
A charitable company has both a charity number and a company number and has to be checked against both registers. A trust might have a charity number or might not, and if it doesn't there is no automated check to run. An exempt charity may not appear on either register in a usable form.
So the design problem wasn't a single onboarding flow. It was a set of related flows that share a surface but diverge underneath according to what kind of charity is going through them. Each entity type needs different data captured, a different governance signal shown to the reviewer, and a different shape of platform space after onboarding.
How I got to this approach
The interesting decisions on Forever Giving were made before most of the screens existed. Five of them shaped everything that followed.

Storybook from day one
For me the components being real, documented React in the repo is the entire point. Code is the source of truth, not a Figma library that informs a separate build somewhere downstream. So Storybook isn't documentation I add after the fact, it's where the system lives from the first hour.
The sequencing follows from a simple premise: across most products the large majority of components are the ones you always need, so you know roughly what the library has to contain before you start. I sketch a few key screens first, let those become the templates that tell me which components are actually load-bearing, then build them properly in React and document them in Storybook as I go.
That mattered more than usual here because I always knew Forever Giving would hand over to a wider build team once it was running. They inherit working, inspectable components in the codebase, not a design file they have to translate into a build.
The KYB matrix as a governance document
Defining what verification actually involved was mine to own, and fairly early, before I'd touched either API, I could see the spread of cases that had to be accounted for across the eight entity types. That told me a screen-level spec wouldn't hold it.
What it needed was a single source of truth that the entity dropdown in the UI, the backend logic that decides which register to call, and the FG Admin review process all had to obey, with an explicit rule that any change to the dropdown gets reflected in the matrix first. Those three layers drift if nothing forces them to agree, and in a compliance flow drift is how you end up verifying the wrong thing.
I treated the matrix as a governance document rather than my own private map, and it became the requirement and the source of truth for the whole team, the thing we worked from in stakeholder and technical meetings.
Per-charity CMS
A charity on Forever Giving isn't running a one-off transaction, it's maintaining a returning presence across multiple collections and multiple artist partnerships over time. That framing is what drove the CMS design. Each charity needed its own managed space to maintain that presence, not a shared admin screen where every charity's content sits in one pile.
I designed it so that the organisation record created during onboarding is the anchor everything else belongs to, so once a charity is verified it has a real, scoped place to work in, and that space is built around the same verified entity the rest of the platform already trusts. The verification and the workspace aren't two separate concerns bolted together, the verified entity is the thing the workspace is built around.
Dual API with snapshot storage
The two registers exist in the flow because UK charities aren't all registered in the same place, so the entity type a charity selects is what dictates which register gets called, one of them, the other, or both.
When a check runs I store the full response from the register as a snapshot rather than just reading it and moving on. The main reason is compliance. Verification is a point-in-time judgement, and what matters for audit is what the register said at the moment we verified the charity, not what it happens to say if you query it again months later. The snapshot is the evidence that the check was made and what it returned. There's a practical benefit too, in that a stored snapshot keeps the FG Admin review fast and avoids re-hitting the registers every time someone opens a record.
Graceful fallback
Not every verification can be settled automatically. Some entity types have no machine-readable register to call in the first place, and separately, an API that should answer can fail or come back with something unexpected. The wrong response to either is to block the charity, because in both cases the organisation may be entirely legitimate and the only thing missing is an automated answer.
So instead of failing the onboarding, the check resolves to a state that hands the case to the compliance team to pick up, with everything the check gathered kept and flagged upstream so the reviewer inherits the full picture and can investigate by hand. The charity isn't dead-ended, the work simply moves to a person, and nothing reaches the platform without having actually been verified, just by a human rather than a register call.
The KYB Decision Matrix
One source of truth that defines how eight UK charity legal entity types are verified, and that the UI, the backend, and the admin workflow all have to obey.

The KYB Decision Matrix is the strategic spine of the whole project. It lays out, for each of the eight UK charity legal entity types, exactly what gets asked for, which register it gets checked against, what gets stored, and what happens if the automated check fails.
The eight entity types each resolve to one of three verification paths, with a manual route for anything the automated checks can't settle:
Charitable Incorporated Organisation and a registered society charity are checked against the Charity Commission.
A statutory charity is checked against Companies House.
A charitable company limited by guarantee is checked against both.
Trusts and unincorporated associations are checked against the Charity Commission if they hold a charity number, and routed to manual review if they don't.
Royal Charter and exempt charities can carry either identifier or neither, so they may go to one register, both, or to manual review.
Enforced in code, not just written down
What makes it a governance document rather than a reference is the rule attached to it: any change to the entity dropdown has to be reflected in the matrix first, and the UI, backend, and admin workflows must not diverge from it.
That rule is enforced in the code. The eight-entity dropdown lives as a single source-of-truth list, and the function that maps each entity to its verification path and identifier rules carries a comment requiring it to stay in sync across the app onboarding and the FG Admin control. The routing logic that decides which register to call is a separate module reading from the same rules. The dropdown a charity picks from, the backend that calls the registers, and the admin review that handles exceptions all answer to the same definition.
This is the part of the work that was platform architecture rather than screen design.
Under the hood

The per-charity workspace is built as org-scoped multi-tenancy rather than a separate system stood up per charity. There is one backend, and the organisation record is the anchor that everything belongs to. A single resolver determines the active organisation for a signed-in user from their membership, and every record the charity creates, its collections, artworks, collaborating artists, directors, and landing pages, carries that organisation's id. Row-Level Security enforces the boundary, so a charity only ever sees and manages its own content.
Clearing KYB is what opens this space up rather than creating a new system, so the workspace a charity lands in is built around the exact organisation record the platform has already verified. The charity's landing page is content it manages from here, stored against the collection as structured data for both the pre-launch and main states of the page, with validated upload routes for the charity's logo and impact imagery.
Dual-API integration and the snapshot strategy
Verification calls two government registers, the Charity Commission and Companies House, both server-side, with the verification path deciding which one runs. The matrix governs that routing as a single map from entity type to the registers it calls:
Each response is written to a snapshot table, one per register, storing the raw payload as JSON alongside a small set of extracted columns: the charity name, its status, its registered address and its trustees.
The extracted columns give the FG Admin reviewer a clean, readable summary to decide from, while the complete response stays underneath for audit. The snapshots sit behind a short cache so opening a record doesn't re-hit the register every time. The schema includes the snapshot tables, an upsert function, and Row-Level Security and service-role policies, built as proper migrations.
Graceful fallback, the manual review path
KYB outcomes are modelled as a small explicit set of states, each mapping to a label, a tone, and a call to action in the UI, so the charity always sees where it stands:
The verification engine resolves an entity with no usable identifier, or a type that has no automated register, to a manual path, which is the same destination as the action-required state. That path routes the case, with everything the check has gathered, to the FG Admin surface for a human to decide.
Design system in production
The design system lives in code as a shared package and is documented in Storybook, with more than fifty components each carrying its own story and the design tokens codified alongside them. It covers the standard primitives a product needs, the domain-specific components this product needs like artwork and collection cards, and the flow components the onboarding depends on like the step indicator and file dropzone.
Because it is real React in the repo rather than a separate design file, the components in these case study screens are the same components running in production, and the team inheriting the project inherits a documented system rather than a specification to rebuild.
Turbo monorepo architecture
The project is a Turbo monorepo with three applications sharing one design system package. The main product surface is a Next.js App Router application holding the onboarding, the verified charity's workspace, the collection management, and the FG Admin tools. Alongside it sit a second React application and the marketing website, and all three consume the same shared component library.
What I'd take forward
Forever Giving confirmed the operating model rather than changing it. The matrix-first approach kept three layers of a regulated platform coherent under real time pressure, and building design-in-code meant the handover is a real codebase rather than a set of designs for someone else to interpret. Both of those are things I'd start with again on the next regulated platform, not things I'd arrive at by the end.
Honest state of the work:
Forever Giving is real production work, but it's at the stage where some things are deliberately pragmatic and worth being honest about.
The active-organisation rule is currently the user's first membership. That is correct for the single-charity-per-user case the platform has today, but it would need a proper organisation switcher before a user could belong to more than one charity.
Several API routes carry defensive handling for schema drift, catching unknown-column errors and degrading gracefully, which is a sign the schema moved fast during the build and would benefit from a consolidation pass.
The matrix is enforced through cross-referenced modules and a sync comment rather than a single shared constant that all three layers physically import. Collapsing it to one imported source would make the governance rule structural rather than conventional.
Some collection-creation steps were built idempotently to survive retries and partial saves, which is the right instinct under time pressure but leaves a few places where the create-or-update logic could be simpler.
None of these block the work. They are the honest state of a product built quickly under real constraints, by one person holding both the product and design roles, on the way to a planned handover.