All services
Pillar 03 · Web

Frontend Development Services

What you get

Capabilities included in every engagement.

  • Next.js App Router (RSC, Server Actions)
  • Astro with islands architecture
  • Plain React + Vite for SPAs
  • TypeScript strict
  • Tailwind CSS + design tokens
  • shadcn/ui on Radix primitives
  • TanStack Query
  • React Hook Form + Zod
  • Playwright + Vitest
  • WCAG 2.2 AA accessibility
The stack we default to

What we use. Why we pick it.

We useWhy we pick it
Astro - content-firstMarketing sites, docs, blogs, landing pages. Zero JS by default. Islands hydrate only what needs interactivity. LCP in the tens of milliseconds.
Next.js App Router - product appsServer Components by default, client islands where they are needed. Streaming, Suspense, Server Actions, route groups. The honest modern React setup for anything that is an app, not a page.
Plain React + Vite - pure SPAsAdmin panels, internal tools, rich editors. When the thing is genuinely an app and server rendering buys nothing, we skip Next.js overhead and ship a Vite SPA.
TypeScript (strict)Every prop, every API response, every form field typed. 'undefined is not an object' production crashes stop happening.
Tailwind + shadcn/uiRadix primitives styled in our repo, not in a node_module. Full control over markup, no library upgrade nightmares, ARIA handled correctly upstream.
TanStack Query (client) / RSC (server)Server components for data that lives on the server. TanStack Query for anything client-side. Pick per component, not per project.
React Hook Form + ZodForms validated by the same Zod schemas the API uses. Client-server drift on form fields stops being a bug class.
Playwright + VitestUnit tests for business logic, Playwright for critical flows. Visual regression where design fidelity matters.
axe-core + keyboard auditAutomated a11y checks on every PR. Keyboard pass before every ship.
Reference architecture

How it fits together.

Astro for content-first pages, Next for app-shell interactivity. Same framework family, different job picked for the job.

Browser Any device Zero JS by default Content-first Astro Marketing · docs · blog Islands · MDX App shell Next.js App Router RSC · actions · streaming Auth · dashboards Shared Tailwind · shadcn/ui One token system Same components, two runtimes HTML JS import import

Decisions that shape a frontend project

Four decisions in week 1 set the next two years:

  • Framework - Next.js, Astro, plain React + Vite. Pick per product, not per trend.
  • Rendering - Server Components for read-heavy pages, client islands for interaction, streaming for anything over 100ms.
  • Styling - Tailwind + design tokens (our default), or a CSS-in-JS runtime (rarely). The ergonomics win of CSS-in-JS rarely earns its performance tax on new builds.
  • Deploy target - Vercel, Netlify, Cloudflare Pages, self-hosted. We meet your infrastructure.

Get these right and every feature is cheaper. Get them wrong and you are fighting the bundle size for two years.

What you actually want from a modern frontend

Speed on real networks, not localhost. Accessibility without an accessibility consultant. Design tokens that survive a rebrand. A component system that lives in the repo, not in a library you cannot change. Tests that run in CI and block merges. Deploys that roll back in one click.

Every one of those is boring. All of them matter more than the framework brand.

Which framework, when

Astro - content-first pages. Marketing, docs, blogs, landing pages, anything the user mostly reads. Zero JS shipped by default. Islands hydrate only what actually interacts. LCP usually under 500ms on cable, under 1.2s on 4G throttled. This site you are reading is built this way.

Next.js App Router - app shells. Dashboards, auth-gated products, server-rendered pages with real data. The React Server Components + streaming + Server Actions combination is the most honest modern React setup. We pick it for anything that is an app, not a page.

Plain React + Vite - admin panels, internal tools, heavy client apps. When the thing is genuinely an SPA (rich editor, data-grid tool) and server rendering buys nothing, we skip Next.js overhead and ship a Vite SPA.

We pick and explain. Tell us the product and we tell you which framework we would pick and why.

Core Web Vitals as a design discipline

Fast pages are not an optimization at the end. They come from decisions made early:

  • LCP (largest contentful paint) - preload the hero image and hero font. No layout shift for above-the-fold content.
  • INP (interaction to next paint) - keep hydration islands small. Defer non-critical JS to idle.
  • CLS (cumulative layout shift) - always set image width and height. Reserve space for anything that loads late.

We instrument CWV with Vercel Analytics or a self-hosted alternative. The dashboard tells us when a deploy regressed something. Regressions are caught and fixed, not discovered from a user complaint.

Accessibility, not theatre

Every ship passes a keyboard-only pass and a screen-reader pass (VoiceOver + NVDA). We use Radix under shadcn because the ARIA is done correctly upstream. axe-core runs on every PR. Contrast ratios are part of the design token system, not a last-minute fix.

Accessibility is not a compliance checkbox. An app that works with a keyboard works for a user holding a baby, a user with tendonitis, a user on a train, and a user behind a screen reader.

Tailwind + shadcn/ui - why this is the right default

Most component libraries (MUI, Chakra, Ant Design) solve the “I need a button” problem and create two new problems:

  1. The styles live in node_modules. Changing a border-radius means fighting the library or shipping CSS overrides.
  2. The bundle bloats. Even with tree-shaking, a component library is 80 to 200KB of CSS + JS you did not write.

shadcn/ui gives you Radix primitives (which handle ARIA correctly) styled with Tailwind in your repo. Want to change the button? Edit the file. No library upgrade path breaks your design.

Case studies

  • Al Rajhi-custodied gold trading across iOS, Android, and web - Flutter Web admin dashboard alongside mobile apps, live Socket.IO price feed rendering identically across devices, multi-timeframe historical charts. Shows the design challenge of financial UI at 60fps with real-time data.
  • Trading calculators with live prices wired in - custom chart rendering via Flutter CustomPainter, multi-timeframe views with tick-by-tick updates. The same design challenges apply on the web: off-the-shelf chart libraries run out of road for trading-terminal UI, and hand-built rendering wins for fidelity.

How we work on a frontend engagement

Week 1 - architecture. Page list, data boundaries, server-vs-client split, auth strategy, deploy target, analytics plan. Written down and reviewed before a single JSX line.

Weeks 2 to 3 - the spine. Shell, auth, design-token system, first real page rendered. Preview URL live by Friday of week 2. Product reviews happen on real URLs, not localhost.

Weeks 4 to 10 - pages, one per week. Lighthouse runs on every PR. Regressions block merge. Playwright covers critical flows end to end.

Weeks 11 to 12 - polish and launch. Accessibility pass, analytics, error boundaries, Core Web Vitals dashboards, SEO validation.

Design systems and component libraries

  • Design tokens in CSS variables - one source of truth for colour, spacing, type scale, radii, shadows.
  • shadcn/ui on Radix primitives - copy-in components, not a dependency. ARIA correct upstream.
  • Storybook for component libraries that need isolated review across teams.
  • Figma design tokens synced to CSS variables via a token pipeline when the design team changes often.

Deploys, CI, and rollbacks

  • GitHub Actions - type-check, unit tests, Playwright against preview on every PR.
  • Preview deploys per branch. Product reviews happen on real URLs.
  • Production behind a protected branch. Manual gate before promote.
  • One-click rollback. vercel rollback or equivalent. We test the rollback in the first week, before we need it.

What we will not do

  • jQuery-era SPAs. If the build is a static page, it ships as a static page.
  • Webflow as the final answer. Fine for a 4-week marketing MVP. Not fine for a product that needs to grow.
  • Bootstrap in 2026. Tailwind + design tokens does it better in less markup.
  • CSS-in-JS runtimes on new builds. The performance tax is real. The ergonomics win is marginal against Tailwind + CSS variables.

Why teams pick our frontend delivery

We pick the framework to match the product: Astro for content, Next.js for apps, plain React + Vite for SPAs. Opinionated, but not married to any of them. If your product is three mostly-static pages, we will talk you out of Next.js.

Core Web Vitals go on the dashboard from week 1, not after a user complaint. Accessibility is engineering: Radix for ARIA, axe-core on every PR, keyboard pass before every ship. Design tokens live in CSS variables so a brand colour change moves one line, not twelve files. Senior engineers on the product. Source and design files yours at launch.

FAQ

Questions people ask before we start.

Depends on what you are building. Astro for pages the user mostly reads (marketing, docs, blog). Next.js for apps with auth, data fetching, and user state. Plain React + Vite for pure SPAs (admin, dashboard). We pick and explain the call in the discovery doc.

Yes, where they fit. Server Components cut the client bundle dramatically for read-heavy pages. Client components for anything that needs browser state or interaction. The split is per-component, not per-project, and we make it explicit in the architecture doc.

Every build ships with a real sitemap, JSON-LD structured data, canonical tags, and meta and Open Graph tags. On Astro builds we often hit 100/100/100/100 Lighthouse scores. On Next.js 95+. We treat SEO as engineering, not content-team marketing.

Yes. Radix primitives under shadcn handle most ARIA correctly. We keyboard-test every flow before ship. axe-core runs on every PR. Contrast and focus rings are part of the design token system, not a post-launch audit.

CSS variables in a token file. Tailwind config reads from them. shadcn components consume them. Light and dark modes from the same tokens. Brand colour changes once and the whole app follows.

Tailwind + CSS variables on new builds. CSS-in-JS runtimes carry a performance tax that only pays off for dynamic theming Tailwind cannot do. On the builds we ship, it rarely earns its place.

Yes. Vercel (default), Netlify, Cloudflare Pages, AWS Amplify, or a self-hosted container on the customer's infrastructure. Preview deploys per branch. Production behind a protected branch. Rollback is one click.

Unit tests (Vitest) for business logic. Playwright covering critical user flows against a preview URL on every PR. Visual regression where the design needs it. We do not chase 100% coverage - we chase the tests that catch regressions.

Start a frontend development services engagement

Tell us what you’re building.

Send us what you are building - product, users, and what fast means for them. We reply with a framework pick, a timeline, and a scoped proposal for a discovery sprint. NDA signed on request. Response within 1 business day.

The team on the call

Named engineers, not a pool.

You speak to the person who’ll review the architecture. No account-manager layer. No offshore switcheroo.

Founder & Lead Engineer

Sameer Donga

Shipping Flutter, FastAPI, and AI systems since 2019. Reviews the architecture on every engagement.