// full-stack web developer

Ιοαnnis Καpεrdαs

I design and build high-performance digital products, web applications, and brand websites using Next.js, React, and TypeScript, from strategy and wireframes to production deployment.

tools & software

design · development · delivery

  • React
  • TypeScript
  • Next.js
  • Node.js
  • Tailwind CSS
  • PostgreSQL
  • Supabase
  • GraphQL
  • Figma
  • Framer
  • Adobe Photoshop
  • Adobe InDesign
  • Git
  • GitHub
  • Vercel

selected work: My latest projects

A few recent projects: the problem, what I shipped, and what changed.

CafeScoop screenshot

Recurring office ordering · 2026

CafeScoop

result

A direct channel that helps neighborhood cafés turn nearby offices into repeat customers, while each office gets a fast link-to-order flow and the café gets one clear ticket instead of message chaos.

1 link → 1 café ticket

project details
problem
Neighborhood cafés lose repeat office business to fragmented phone and WhatsApp orders: the same details are collected every day, cutoffs are unclear, and staff must manually combine each person's choices into one order.
built
A bilingual recurring-order platform in Next.js 15, TypeScript and Supabase. One organizer creates the group and schedule, teammates join from a link without required signup, the live order locks automatically, and the café receives one aggregated ticket. Kitchen workflow, email notifications, revenue reporting and Sentry complete the operating loop.
  • Next.js 15
  • TypeScript
  • Supabase
  • Realtime
  • EL / EN
Greece Digital Monument screenshot

Non-profit digital archive · 2026

Greece Digital Monument

result

A single, museum-grade home for 12 documented lost hotels and growing, with every claim traceable to reputable sources and every image public-domain or CC with full attribution.

12 verified hotels

project details
problem
Greece's 20th-century grand hotels (Belle Époque seafront palaces, interwar landmarks, the modernist state Xenia network) are being demolished or left to decay. Their histories are fragmented across unreliable, increasingly AI-muddied sources, with no map-based way for the public to explore them.
built
An independent, source-verified, bilingual (EL/EN) archive in React 19 + TypeScript + Vite. Explorable by interactive Leaflet map, architectural style, region and a chronological lifespan timeline, plus an AI “historic archivist” (Groq / Llama 3.3) grounded in the cited record, not invented detail.
  • React 19
  • TypeScript
  • Leaflet
  • Groq AI
  • EL / EN
Infinite Impressions × Cookies screenshot

AI compliance console · 2026

Infinite Impressions × Cookies

result

A stark, editorial, WCAG-AA console with real Recharts data-viz, plus a JS bundle trimmed from ~1057 kB to ~216 kB along the way.

1057 → 216 kB

project details
problem
Cookie-consent banners sit in a no-win zone: privacy law (GDPR, CPRA, Consent Mode v2) demands fair, transparent consent while teams chase opt-ins, so the web is full of dark patterns, trackers firing before consent, and inaccessible banners. Most teams can't even tell if theirs is compliant.
built
An AI console that turns that uncertainty into action across five modules: a Trust Auditor that scores any URL and emits copy-pasteable code fixes, an AI banner redesigner with live preview and React/HTML export, a script-load timeline inspector, regional consent analytics, and a privacy consultant tuned on EDPB/CNIL/CCPA guidance. React 19 + Vite + Tailwind v4.
  • React 19
  • Vite
  • Tailwind v4
  • Recharts
  • AI
MailSentinel screenshot

Explainable spam ML · 2026

MailSentinel

result

A login-free, privacy-by-design demo you try in one click, with an open model card and modern-mail domain adaptation so today's email scores correctly. Optional read-only Gmail/Outlook OAuth.

97.7% accuracy

project details
problem
Spam and phishing tools are black boxes: most “detectors” are hand-written if/else rules with no honest metrics, demos make you log into Gmail before you see anything, and the public spam corpora are 20 years old.
built
A real classifier (TF-IDF + logistic regression, 97.7% accuracy, 0.997 ROC-AUC) built as a glass box: each word's weight drives inline red/green highlighting and a ranked “why” panel. Inference runs client-side (pasted email never leaves the device) with a Python training pipeline and a parity test matching the TypeScript inference to machine epsilon. Next.js 16 + TypeScript.
  • Next.js 16
  • TypeScript
  • scikit-learn
  • Explainable ML

code: Recent Code Initiatives

Patterns I reach for on real work. Each one solves a problem you'll recognise. Pick one to read it.

Modal.tsx

Modal dialogs that trap focus and restore it, without pulling in a UI library.

View on GitHub
// Problem: accessible modal dialogs that trap focus and restore it on close,
// without pulling in a whole UI library.
import { useEffect, useRef } from "react";

export function Modal({ open, onClose, title, children }: {
  open: boolean;
  onClose: () => void;
  title: string;
  children: React.ReactNode;
}) {
  const ref = useRef<HTMLDivElement>(null);

  useEffect(() => {
    if (!open) return;
    const previouslyFocused = document.activeElement as HTMLElement | null;
    const focusables = ref.current!.querySelectorAll<HTMLElement>(
      'a[href], button:not([disabled]), input, [tabindex]:not([tabindex="-1"])',
    );
    focusables[0]?.focus();

    function onKeyDown(e: KeyboardEvent) {
      if (e.key === "Escape") return onClose();
      if (e.key !== "Tab" || focusables.length === 0) return;
      const first = focusables[0];
      const last = focusables[focusables.length - 1];
      // Wrap focus so Tab never escapes the dialog.
      if (e.shiftKey && document.activeElement === first) {
        e.preventDefault();
        last.focus();
      } else if (!e.shiftKey && document.activeElement === last) {
        e.preventDefault();
        first.focus();
      }
    }

    document.addEventListener("keydown", onKeyDown);
    return () => {
      document.removeEventListener("keydown", onKeyDown);
      previouslyFocused?.focus(); // restore focus to the trigger element
    };
  }, [open, onClose]);

  if (!open) return null;
  return (
    <div className="overlay" onClick={onClose}>
      <div
        ref={ref}
        role="dialog"
        aria-modal="true"
        aria-label={title}
        onClick={(e) => e.stopPropagation()}
      >
        {children}
      </div>
    </div>
  );
}

about: Full-stack execution

I'm Ioannis Kaperdas, a full-stack web developer with hands-on experience delivering websites and web applications across a range of projects. I hold an MSc in Business Information Technology Systems from the University of Strathclyde and an MSc in IT and Applications from the University of West Attica.

I care about the things that make software actually good: fast load times, accessible markup, typed and maintainable code, and clear communication along the way, not jargon.

Tools are means to an end. I pick the right ones for the job and keep the codebase something the next person can live with.

what I do

  • Web apps & dashboards
  • /Marketing & landing sites
  • /Performance & SEO
  • /Design to code
based in
Athens, Greece · Remote worldwide
stack
TypeScript · React · Next.js
working with
Startups, agencies & founders

// toolkit

  • TypeScript
  • React
  • Next.js
  • Node.js
  • Tailwind CSS
  • PostgreSQL
  • REST & GraphQL
  • Figma → Code
  • Web Vitals
  • Vercel / CI

contact: Let's work together

Have something in mind, or just want to chat? Tell me about it and I'll reply within a day.

Securing your message...