Case study · 2026

A multi-agency dashboard, built without an off-the-shelf design system.

Agency, user, country, and region administration for a Vienna-based international organization — with a custom GitHub-inspired design system, role-based access control, JWT auth, and session-timeout governance.

Where we started

The organization needed one administrative surface for agencies, the users inside each agency, and the country and region taxonomy those agencies are organized into. The work was spread across spreadsheets, ad-hoc forms, and reports that had to be assembled by hand each time someone asked “how many agencies in region X are active right now?”

Three concerns shaped the rebuild:

  • Accessibility was non-negotiable. The platform had to be operable by keyboard and assistive technology from day one.
  • The team did not want a UI-library lock-in. Every prior platform had inherited an opinionated component library that became expensive to break out of.
  • Session governance mattered. The admin surface had to auto-log-out reliably with a clear warning, not silently expire and lose work.

What had to be true

Five non-negotiables shaped the build before any code:

  1. A custom, GitHub-inspired design system with tokens, primitives, and documented accessibility expectations — not a vendor library skinned to look custom.
  2. Role-based access enforced at the API boundary, with admin and user surfaces clearly separated.
  3. JWT auth with explicit session timeout: two-hour inactivity logout, five-minute pre-expiry warning, and a one-click extend path.
  4. The frontend and backend must deploy independently on Azure App Service, using the organization’s existing identity and monitoring tenant.
  5. The schema must be reviewable as code — Prisma migrations, not freehand SQL applied through a console.

Stack and reasoning

  • React 19
  • Vite
  • TypeScript
  • React Router 7
  • TanStack Query
  • React Hook Form
  • Zod
  • Express
  • Prisma
  • PostgreSQL
  • JWT
  • Azure App Service

The choices, in order of impact:

  • React 19 on Vite. Fast HMR for a UI-heavy build, predictable bundle output, and zero framework lock-in on routing or rendering.
  • Custom design system, GitHub-style. Tokens, primitives, patterns, each component with documented keyboard and screen-reader behaviour. No external UI library means smaller bundles and complete control of accessibility.
  • Express + Prisma + Postgres. Plain, debuggable REST API with strong relational integrity. Prisma’s schema-first migrations made every change reviewable in PR.
  • TanStack Query. Server-state cache for the read-heavy admin workflow without re-inventing invalidation or background refresh.
  • React Hook Form + Zod. Type-safe forms with schema-derived validation, shared between client and server.
  • JWT with explicit inactivity timeout. Configurable via environment (two-hour idle by default), with client-side warning at five minutes remaining and a server-validated extend endpoint.

What was deliberately not picked:

  • A SaaS UI library. The accessibility floor required component-level control. A vendor library would have been 80 percent there and 100 percent unowned in the gaps.
  • Next.js. The product is an admin surface behind auth, not a content site. Vite + Express was the right size; no SSR tax for a logged-in dashboard.
  • A separate auth provider. JWT issued by the API was sufficient for the closed-user-group admin surface. No third-party identity vendor to onboard.

Architecture in plain language

A React 19 SPA built with Vite talks to an Express + Prisma REST API backed by Azure PostgreSQL Flexible Server. Auth happens at the API: login issues a JWT, the SPA carries it on every request, and the API enforces role-based access on each route.

Three patterns did most of the work:

  • Admin and user surfaces as separate route trees. Each tree has its own layout, navigation, and role gate at the route boundary. No conditional rendering inside shared pages.
  • Inactivity timeout on the client, validated on the server. The SPA tracks user activity and warns before logout; the server independently rejects expired tokens. Defence in depth.
  • Design tokens as CSS custom properties. Colours, spacing, typography, motion — all in tokens. Theming and accessibility audits operate on the token layer, not on hundreds of component files.

The decisions worth defending

A few choices took the most pushback and turned out to matter most.

Building a custom design system instead of using shadcn or MUI

The argument against custom was speed. The argument for custom was that strict accessibility, a long product lifetime, and a small bundle budget made every off-the-shelf option a permanent compromise. The custom system paid back inside the first quarter.

JWT with strict inactivity timeout

The default ergonomics of JWT are “long-lived token, no idle check.” That is the wrong default for an admin surface. The platform enforces a two-hour idle timeout with a five-minute warning and a single Stay-Logged-In button. Activity is tracked in the SPA and validated server-side on every API call.

Prisma schema-first, no console edits

Every schema change is a Prisma migration committed to the repository. Nobody edits production data definition through the database console. This made rollbacks predictable and audits trivial.

Documentation as a deliverable, not a footnote

The repository contains a handover document, a deployment guide, a session-timeout deployment guide, a custom-dropdown component reference, and a non-technical platform presentation brief. None of these were glamorous to write. All of them shorten the path for the next engineer on the project.

What shipped

  • Admin dashboard for agencies, users, countries, and regions, with role-based access control.
  • User dashboard for non-admin accounts, scoped to permitted entities.
  • Custom GitHub-inspired design system with documented accessibility expectations and zero external UI dependencies.
  • JWT authentication with two-hour inactivity timeout, five-minute warning, and one-click extend.
  • Prisma migration pipeline for reviewable, rollback-friendly schema changes.
  • Azure App Service deployment for frontend and backend, integrated with the organization’s existing identity and monitoring.
  • Operating documentation: handover, deployment, session-timeout, component reference, and a non-technical platform brief.

What I would do differently

  • Token rotation from day one. Issuing a fresh JWT on each significant action (not only on extend) would have simplified the eventual move to short-lived tokens.
  • Storybook for the design system earlier. The system is correct, but isolated component documentation would have shortened onboarding for the next contributor.
  • Automated accessibility regression in CI. Manual audits caught the issues, but an axe-core pass on every PR would have caught them at commit time.

Why this kind of work matters

Admin platforms tend to either inherit a vendor library that ages badly, or grow into a tangle of bespoke components nobody can audit. A small, opinionated, custom design system backed by a clean REST API and reviewable schema migrations is the cheapest investment a long-lived internal product can make — and the hardest to retrofit later.

Want a system like this?

I work with Vienna and EU teams on internal applications, admin platforms, and accessible design systems.

hello@albimeta.com · Response within 1 business day