Skip to content
All work

Case study 04 · Web app

Customer sign-in portal

The front door to an industrial software suite. I built self-service account recovery, then re-skinned every screen around one token contract.

Role
Recovery flows · rebrand author
When
2022–2026
Where
Detechtion.AI
Platforms
Web · Next.js · TypeScript
self-service recovery flows, built and tested from scratch
3
tests across the three forms, covering render, validation and submit
9
routes re-skinned around one token contract
12
net lines of duplicated CSS removed
~270

Overview

The unified sign-in portal in front of the company's monitoring and analytics web apps. People sign in, complete verification steps, recover a forgotten username or password, then land on a launcher showing the apps they can use. One codebase produces more than one visual theme, chosen at build time.

I worked on it in two periods: building the account-recovery flows in 2022, and in 2026 re-skinning the whole portal around a single semantic token contract and fixing the rough edges that came with it.

My part. In 2022: all three recovery flows (forgot password, reset password, forgot username), their data hooks, pages and tests. In 2026: the portal-wide re-skin across all 12 routes, a shared auth-page shell and brand header, both theme token sheets and the theme-flash fix.

Try it

Theme Forge

A fictional software suite's sign-in flow that re-skins itself from one token contract. Reproduce the wrong-theme flash, the autofill mismatch and the missing web font, then flip each one to the fix.

  • Brand switcher driven by tokens
  • Live password-rule checklist
  • One-time-code input with paste
  • Semantic button audit
Booting demo…

Engineering stories

Problem, approach, outcome.

  1. 01

    Self-service account recovery, built and tested from scratch

    • React + TypeScript forms
    • Error & success UX
    • React Testing Library

    Problem

    People using the software suite needed to recover a forgotten username or reset a password without calling support, with clear feedback at every step.

    Approach

    • Built three flows end to end, each with a form component, a data hook and a routed page.
    • Inline validation with a hooks-based form library: required fields, password rules and confirmation matching, plus inline success and error alerts. A successful reset returns people to sign-in with a confirmation banner.
    • Tried a separate schema-validation layer first, then simplified to the form library's built-in rules two days later and removed that layer.
    • Covered each form with React Testing Library tests, including router mocking and async submit handling.

    Outcome

    All three flows landed incrementally over about six weeks. Teammates later generalized the forgot-password form into a shared recovery form, and the screens were re-skinned in 2026.

    • 3 flows
    • 3 suites · 9 tests
  2. 02

    One codebase, more than one theme and no flash

    • Design tokens
    • Multi-theme builds
    • Next.js build config

    Problem

    After the company rebrand, all 12 screens needed a new look, and the per-page stylesheets had drifted into near-copies of each other. People also saw a brief flash of the light default theme before the dark theme loaded.

    Approach

    • Designed one shared set of semantic custom properties (surfaces, borders, muted greys, glow, on-primary). Each theme fills the same names with its own values, so shared components never know which theme they're in.
    • Introduced a common auth-page shell and brand-header component, and moved duplicated layout rules into them.
    • Traced the flash to the theme stylesheet being chosen and loaded at runtime. Moved the choice to build time with an environment-driven bundler alias, configured for both webpack and Turbopack, so the theme CSS is a static import that ships with first paint.

    Outcome

    All 12 routes were re-skinned, the duplicated CSS shrank by about 270 lines and both themes come from one contract. The wrong-theme flash is gone.

    • 12 routes
    • ~270 net CSS lines removed
    • No wrong-theme flash
  3. 03

    Red means danger, so it shouldn't also mean “continue”

    • Semantic colour
    • Design-system governance
    • Minimal-diff changes

    Problem

    Every primary action was filled in alarm red. The new design system keeps red for irreversible or high-consequence actions, and in an industrial product suite red usually means an alarm.

    Approach

    • Paired with an AI coding assistant to audit the app against the design system, then reviewed and prioritized the six areas of drift it surfaced.
    • Retokenized the primary button to a neutral fill with its own hover and active shades, and added a destructive variant that keeps the red.
    • An audit of every button showed the forms already used the semantic variant, so only the final sign-in button needed a code change. The spec allows that one to keep the accent.

    Outcome

    Every primary action picked up the new style from a single token change, and only one component needed an edit.

    The drift audit was AI-assisted. I reviewed the findings, set priorities and made the calls.

  4. 04

    The input that looked different when autofilled

    • Cross-browser CSS
    • Web typography
    • Root-cause analysis

    Problem

    On the dark sign-in screen, the username field looked different depending on whether you typed, picked a browser autofill suggestion or clicked away: a different background and a slightly different font size.

    Approach

    • The app had no autofill styling, so the browser painted its own light treatment over the dark input. The fix repaints autofilled fields in the theme's surface colour and pins text colour and size.
    • Chasing the size shift found a second, hidden cause: the brand typeface was named in the font stack but never actually loaded, so everyone without it installed got a fallback with different metrics. Proper font-face declarations fixed it.

    Outcome

    The field looks the same typed, autofilled or unfocused, and the typeface renders the same on every machine. The browser's own suggestion dropdown is native UI outside the page's control, and that limit is documented.

    I raised the symptom and directed the investigation; an AI coding assistant ran the searches and found both causes. I reviewed and committed the fixes.

More highlights

  • Made the recovery screens theme-aware from the start, so they follow whichever visual theme a build uses.
  • Moved the default button from alarm red to a neutral fill, keeping red for destructive actions only. One token change, one component edit.
  • With an AI coding assistant, fixed browser-autofill styling that repainted dark inputs light, and a brand web font that was declared but never loaded.

Stack

Front end
Next.js (static export)TypeScriptReactReact-Bootstrapreact-hook-form
Styling
CSS ModulesCSS custom-property tokensBuild-time theme aliases
Quality
JestReact Testing LibraryESLint

What I took away

  • Themes should differ only in values. The moment a component knows which brand it's in, you've lost.
  • Do anything that has to be right on first paint at build time.
  • Colour has meaning. Audit what it means, not just what hex it is.
Esc

↑↓ to move ↵ to select