Questify

2.1.0

Released

Features

  • Added the Design System v2.0 typography system (#304). apps/web/src/app/layout.tsx now self-hosts and preloads three families, replacing the previous Inter-only setup: Geist (body/UI) via the official geist package, plus Instrument Serif (display/headings) and JetBrains Mono (code/stat labels) via next/font/google. Geist ships through the geist package rather than next/font/google because Geist post-dates Next 14.2's bundled Google-font catalogue. globals.css aliases the loader variables (--font-geist-sans, --font-instrument-serif, --font-jetbrains-mono) into semantic --font-sans / --font-serif / --font-mono tokens and adds .t-display (serif + italic), .t-serif, and .t-mono utility helpers; tailwind.config.ts maps font-sans / font-serif / font-mono onto those tokens. All faces are preloaded with metric-matched fallbacks (display: 'swap') to avoid layout shift.

  • Replaced the shadcn default palette in packages/ui with the Questify brand design tokens (Design System v2.0, #303). globals.css now defines an indigo primary, gold secondary, and parchment background for both :root and .dark, with all harmonised neutrals (muted, accent, card, popover, border, input, ring). Added tonal status tokens for success / warning / destructive / info (solid + soft fill + foreground), plus motion (--duration-*, --ease-default), spacing (--space-*), radius (--radius-sm|md|lg|xl), and shadow (--shadow-sm|md|lg) custom properties. Extended tailwind.config.ts to expose the fixed questify.* brand palette (indigo, gold, parchment, ink) and status.* variants alongside the existing semantic tokens, and mapped the radius/shadow/duration scales to the new tokens. Existing components compile unchanged.

  • Applied the Design System v2.0 brand to the component layer and key app surfaces (#305). packages/ui Button keeps the indigo default / gold secondary token defaults and gains a brand-cta variant (dark questify.ink panel, gold text/border) for hero CTAs; Card is now cva-based with the parchment/dark-surface default plus a hero dark-panel variant; a new Badge component exposes tonal status chips (success, warning, destructive, info, default) built on the #303 soft-status tokens; Input focus now lifts both the border and ring to indigo. In apps/web, the quest status chip (quest-status-badge.tsx) now renders through Badge instead of ad-hoc bg-green-100/bg-red-100 colour strings, a matching tonal TaskStatusBadge (task-status-control.tsx) is available for task status, quest card titles (quest-card.tsx, public-quest-card.tsx) render in Instrument Serif via font-serif, and the dashboard opens with a new WelcomeHero panel (hero Card, .t-display greeting, .t-mono eyebrow, gold brand-cta).

Fixes

  • POST /api/billing/checkout refuses an account that already holds a Pro-granting subscription, with a 409 pointing at the Billing Portal. Hiding the buy buttons is not enforcement: a double click, a retry, or a direct call would open a second Checkout session, and Stripe will happily run two independent subscriptions for one customer (#486).

  • Stripe customer creation carries an idempotency key derived from the user id, so two concurrent first checkouts converge on one customer. Without it both saw no mapping, both created a customer, and the losing upsert left one holding a live Checkout session and its invoices that neither the mapping nor the Billing Portal could find again (#486).

  • Locked personas in the model picker render as plain cards rather than disabled buttons. The "Upgrade to Pro" link was interactive content nested inside a disabled <button> — invalid HTML, and unreachable by pointer and assistive technology alike, so the one control that could unlock the card was the one control that could not be used (#486).

  • The dev TUI re-checks for a live-mode context after an account switch. The guard ran before the switch was offered, so choosing a live account from the picker walked straight past it into fetching a live webhook signing secret into .env and forwarding production events at localhost (#486).

  • Fixed the onboarding wizard dying with "Application error: a client-side exception has occurred" when a step advanced. Skip, Back, and Continue all redirected to /onboarding — the route their own form lives on — and a Server Action that redirects returns nothing, so useFormState was left holding undefined and the next render crashed reading state.message. Same-route transitions now revalidate and return a state; only leaving for the dashboard still redirects, where the form unmounts anyway. Every state read in the wizard components is also guarded, since a redirecting action legitimately yields undefined (#460).

  • Fixed signup confirmation and password-reset email links embedding http://localhost:3000 instead of the production domain (#308). Extracted getSiteOrigin() into its own module and added VERCEL_URL as an intermediate fallback between NEXT_PUBLIC_SITE_URL and the localhost default. A console warning now fires when the localhost fallback is reached in production or Vercel environments.

Technical changes· 2 entries for database, tooling, CI, tests and docs

Documentation

  • Added project-specific AI coding guidance for Questify 2.0 and linked it from the README documentation section.

  • Refined AI_CODING_GUIDANCE.md with a current-version status header (root package.json as the canonical version), concrete Keep-a-Changelog emoji-section conventions, a NEXT_PUBLIC_FEATURE_* flag reference table, and a Release Checklist covering version bump, changelog promotion, and tagging.