Global Texture - Noise Background
Global Texture - Noise Background
    uxproof on two phones: the empty chat, and a chat that lists the uploaded reports and generates a deck
    uxproof on two phones: the empty chat, and a chat that lists the uploaded reports and generates a deck
Produced by
PAISAK4U
Role
UX Engineering Lead — product concept, interaction design, information architecture, design system, accessibility, React implementation, AI workflow and presentation renderer
Scope
Interaction model and information architecture, the app and deck design systems, a React and TypeScript build, and a local AI workflow with grounding and fallbacks
Year
2026
Tools
Next.jsReact 19TypeScriptZustandFastAPIPydanticSanityOllamapptxgenjsFigmaClaude Code

Context

Research shouldn’t become a manual reporting exercise.

Quarterly UX research kept ending its life in slide decks. Each cycle meant pulling SUS scores, KPIs and findings out of files, rebuilding the same slide structures and checking that the numbers had survived the copy-paste.

UxProof is a self-initiated product, and that recurring workflow was where I started: what do researchers need to find, compare, decide and communicate every quarter? It gave the product its own model. Research periods, SUS scores, task results, findings and recommendations are first-class objects, because they’re the units researchers actually work with.

I designed and built it end to end: the interaction model, information architecture, design system and accessibility, the React implementation, the AI workflow and the presentation renderer. It runs on my own research practice, with real uploads and real decks, and its patterns are informed by my work on production AI systems.

uxproof open on a laptop: the chat home with the welcome headline, a row of quick actions and the composer

Product principle

The model interprets. The product owns the facts.

A tool that presents research to a client has to get the numbers right every time, so the first thing I designed was a boundary between what the model is allowed to do and what the product keeps for itself.

The model can pick out the findings that matter, interpret the evidence, write the narrative and turn prose into a report. It never owns the research numbers, the slide structure, the styling or the call on whether an output is valid. A typed validation layer sits between the two. Model output that fails the contract, or can’t be traced to stored evidence, is discarded, and nothing tries to repair it.

That decision shaped everything after it. The interaction states, the fallbacks, the accessibility behaviour, the rendering rules and the deck design all follow the same split.

Diagram of uxproof's division of authority: the model owns selection and narration on the left, code owns numbers, structure, styling and availability on the right, with a validation gate between them

Interaction design

The chat is a product interface, not a prompt box.

The workflow starts in the chat and follows how researchers already think about their material. Files become evidence, evidence becomes reports and research periods, reports become the context of the conversation, and the conversation leads to analysis and finally a presentation.

Files go in through the plus button in the composer. Structured data, such as the quarter, the year and the SUS score, is parsed into reports, and prose documents are converted by a local model. After that it’s plain language: “Analyse Q3 2025”, “Compare Q2 vs Q3”, “Generate the 2025 presentation”.

Plain language is ambiguous, so the interface resolves it. A bare “yes” after the assistant offers a presentation becomes a generation request. A follow-up question that names no period picks up the one being discussed. A workspace with no data asks for an upload before anything runs. They’re small decisions, and together they keep the chat on the task.

uxproof at phone width on three screens: the empty chat with its welcome headline and quick actions, a reply listing three uploaded files and the period they cover, and a Q3 2026 deck generating, with the slide strip still four across and a download button
An AI summary of an uploaded research PDF in the uxproof chat: metrics, key findings and recommendations, each claim followed by the page it was read from, and a closing line saying the research data was extracted and filed under Q3 2026
The uxproof chat after a request to generate the Q3 2026 presentation: the reply confirms the SUS score and task success, above a presentation card with the eight-slide strip and a button to generate and download the .pptx

AI behaviour

AI is an enhancement, never a dependency.

Structured uploads are parsed deterministically. Prose documents are converted by a local model under a literal-value check: every number has to appear verbatim in the source text or it’s dropped, and a report without a grounded SUS score isn’t created at all. Chat answers and document summaries follow the same rule, and a reply whose numbers can’t be traced to stored data is replaced by its template.

Every model step has a deterministic fallback. Conversational answers fall back to templates, summaries to excerpts, and extraction and planning to rule-based versions. With the model completely offline, the core workflow still works and decks still generate.

When the model is wrong or unavailable, the product deals with it, and the researcher still gets something they can use.

Flow diagram of the grounding gate: an uploaded document passes through model conversion, then two gates — each number must appear literally in the source or the field is dropped, and without a grounded SUS score no report is created at all
Diagram pairing each model step with its deterministic fallback: conversational answers fall back to templates, summaries to excerpts, prose conversion to reference storage, extraction and planning to rule-built equivalents

Design system

Constraints make the interface more coherent.

The application’s design system lives in Figma and in the code on the same tokens: 258 variables in five collections, colour in light and dark modes, and each variable carrying the CSS variable it maps to. Every Figma component is mapped to the React component and the source file that implement it.

The system also decides where interaction can happen. A control can live in one of six places: the top bar, a view header, a row or card, the composer, an anchored panel or a dialog. If something doesn’t fit, I redesign the interaction. That’s why the product has no toolbar, context menu, filter bar or settings page.

Type is two families, Schibsted Grotesk for the interface and IBM Plex Mono for data such as periods, file sizes and timings, across 33 named roles. Each view has one filled button, and destructive actions stay quiet until you confirm them. Patterns cover the recurring flows: uploading, loading, errors, empty states and deleting.

On a phone, the theme switch moves into the account menu, the history drawer goes full width and “Generate presentation” shortens to “Generate”. The slide strip stays four across, so a deck keeps its shape on any screen.

Because every rule is a limit, new screens stay consistent with the rest.

Design-system page on tokens: each colour traced from a primitive to its light and dark semantic token, the component property it binds to and the CSS it becomes, an inventory of the five variable collections and their modes, and the rule for writing each variable’s code syntax
Design-system page: the colour primitives, raw swatches for the neutrals, zinc, navy, mist, slate, lavender, night, iris, violet, purple, crimson, emerald and amber with their values, plus the brand mark violet and the two deck colours
Design-system page: the semantic text and icon colours, each token shown in light and dark beside the CSS it maps to and where it is used, with measured contrast ratios for the text pairs
Design-system page: the top bar, history drawer, composer and anchored panels, with a table listing six surfaces — top bar, view header, row or card, composer, anchored panel, centred dialog — and what lives in each
Design-system page: the two families, Schibsted Grotesk and IBM Plex Mono, and every named type role with its size, line height and tracking, a sample, and the classes it maps to in code
Design-system page on actions: the button matrix across variants, sizes and states, its anatomy, do and don’t rules, the tokens for each variant, how Figma properties map to React props, and the icon buttons
Design-system page: message rows with the assistant mark and an asymmetric bubble radius, a named step ladder showing pipeline progress, and the presentation card with its slide strip and download action
Design-system patterns page, upload and grounding: a file is attached, confirmed with the periods found, summarised with every figure citing its page, and turned into a deck only when the user asks, followed by the rules that make it correct
Design-system patterns: the destructive confirmation flow from trigger to confirm, pending and outcome, and loading and progress from fetching files to the named pipeline steps and a streaming summary, each with its rules
Design-system patterns: error recovery shown inline where each error happens, in a message, the deck card, a form field and an action toast, and notifications anchored under the avatar, each with its rules
Design-system patterns: the empty Files page, the empty chat history drawer and the welcome illustration, with rules for saying what is missing and offering one next step
Design-system page mapping each Figma component to the React component and the source file that implement it, from Button and Dialog to the thinking panel, presentation card and file row
Design-system page on responsive behaviour: how each component changes at the base, 640, 768 and 1024px breakpoints, the top bar at 390 and 320px, and four phone screens showing the chat, sign-in, files and the history drawer

Engineering architecture

Explicit boundaries keep the product predictable.

The architecture follows the product’s ownership model. The Next.js app owns the interface, uploads and deck rendering. A FastAPI service generates the slide plan, a headless CMS stores reports, sessions and plans, and model inference runs locally at a few explicit points. Research data is confidential, so all of it runs on one machine.

Deck content comes from a multi-agent workflow. Three agents run in sequence, for context, extraction and planning, and hand each other typed Pydantic contracts. The same content-block contract is mirrored in the CMS schema, the TypeScript types and the agents’ models, so malformed model output fails at a boundary and never reaches the interface or a client’s deck.

The agents are visible in the product too. While a deck is being generated, the chat names each step as it runs and ticks it off when it’s done, so the researcher can follow what the AI is doing.

Deterministic code owns the facts, validation, structure and rendering, and AI is used for interpretation where it helps.

Diagram of the uxproof architecture: a React browser client, the Next.js app with intent routing, grounding gate and deck renderer, connected to a Sanity content store, a FastAPI agent pipeline, and a local Ollama model — all inside a single-machine boundary
Diagram showing one content-block contract mirrored in three places: the Sanity content schema, the TypeScript types and GROQ projections in the app, and the Pydantic models in the agent pipeline
The uxproof chat on a tablet during deck generation: a presentation card with an elapsed counter, above a list of named pipeline stages where finished steps carry a check and the running step is marked with a violet dot

Accessibility

Accessibility is part of the interaction model.

The main chat flows work entirely from the keyboard. Every icon-only control, such as upload, send, history and account, has a real label. Focus rings are visible on every interactive element, and actions revealed on hover, like deleting a conversation, also appear on keyboard focus, so nothing in the product needs a pointer.

State changes are announced as well as shown. Toasts are polite live regions, errors are alerts, expandable summaries expose whether they’re open, and the active conversation is marked for assistive technology. Uploads render optimistically, generation cards move through clear idle, working, done and error states, and a restored session never quietly re-runs an old generation.

Contrast is measured against the shipped tokens. Every text pair is calculated from the values in the product and listed with its ratio, failures included. Two colours changed as a result. Muted text was darkened until it cleared 4.5:1 on every surface it appears on, and the dark-mode error pair, which measured 2.66:1 and made form errors nearly invisible, was inverted. The two hues that still fall short are used for icons only, by rule.

Design-system page: the dark theme palette, a measured contrast table listing each colour pair with its ratio and whether it passes, and three rules derived from the measurements

Presentation system

The output is deterministic too.

The generated deck has its own design system, kept separate from the app’s. It has thirteen slide types built on four masters, and a small fixed palette.

The model chooses and narrates the content, and the template controls the layout, type and colour. A slide only appears when the figures behind it exist, and at most one figure per slide is violet, so the problem is easy to spot.

A page from the deck build spec showing two slide masters drawn to scale: the content master with its eyebrow row, rule, title, standfirst and dashed data field, and the cover with a violet plate over a full-bleed greyscale photograph, each annotated with the inch positions it is built from
A page from the deck build spec: a palette table listing ground, ink, the three violets and the four ink tints, each with its swatch, hex, RGB values and the elements it is used for, and a closing note on text colours over violet fields

AI-assisted engineering

AI accelerates implementation. It does not own the design decisions.

I used an agentic AI workflow to speed up implementation: scaffolding components, exploring alternatives, proposing refactors and writing test plumbing. The decisions that need UX and engineering judgement stayed with me, including interaction behaviour, information architecture, edge states, accessibility, visual quality and consistency across the system.

Those decisions are written into the repository as invariants that every change is checked against. Numbers are never written by the model, every model step has a fallback, the deck template has one source of truth, and pages stay server components while interaction lives in client components. An audit agent reviews changes against those rules, and the tests produce versioned readiness reports.

When generated code kept introducing one-off spacing values and accent colours, I stopped correcting them one at a time. I wrote the rule into the project brief and the branding module, so every later change, generated or written by me, is held to the same constraint.

AI generates → UX engineering evaluates → the design system constrains → tests verify.

A spread of slides from a generated uxproof deck: the cover, a contents page, a section divider, the violet findings-summary slide tracing 78% of friction to four findings, and finding slides with bar charts and before-and-after figures

Outcome

A working example of AI product design where the interface and the system reinforce each other.

UxProof brings research practice, product design, interaction design, a design system, React engineering and AI behaviour together in one working flow, from an uploaded file to a finished deck.

It shows how an AI product can stay predictable when the model underneath it isn’t. Facts are grounded in the source, every model step has a deterministic alternative, interaction states are explicit, contrast is measured, and the deck comes from a system with fixed rules.

The uxproof Files page at tablet width: three uploaded PDFs, one expanded to its summary with metrics, findings and recommendations tagged with the page they came from, and a note that the data was filed under Q3 2026