2.1.0
Released
Features
Added the Design System v2.0 typography system (#304).
apps/web/src/app/layout.tsxnow self-hosts and preloads three families, replacing the previous Inter-only setup: Geist (body/UI) via the officialgeistpackage, plus Instrument Serif (display/headings) and JetBrains Mono (code/stat labels) vianext/font/google. Geist ships through thegeistpackage rather thannext/font/googlebecause Geist post-dates Next 14.2's bundled Google-font catalogue.globals.cssaliases the loader variables (--font-geist-sans,--font-instrument-serif,--font-jetbrains-mono) into semantic--font-sans/--font-serif/--font-monotokens and adds.t-display(serif + italic),.t-serif, and.t-monoutility helpers;tailwind.config.tsmapsfont-sans/font-serif/font-monoonto those tokens. All faces are preloaded with metric-matched fallbacks (display: 'swap') to avoid layout shift.Replaced the shadcn default palette in
packages/uiwith the Questify brand design tokens (Design System v2.0, #303).globals.cssnow defines an indigo primary, gold secondary, and parchment background for both:rootand.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. Extendedtailwind.config.tsto expose the fixedquestify.*brand palette (indigo,gold,parchment,ink) andstatus.*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/uiButton keeps the indigodefault/ goldsecondarytoken defaults and gains abrand-ctavariant (darkquestify.inkpanel, gold text/border) for hero CTAs; Card is nowcva-based with the parchment/dark-surfacedefaultplus aherodark-panel variant; a newBadgecomponent 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. Inapps/web, the quest status chip (quest-status-badge.tsx) now renders throughBadgeinstead of ad-hocbg-green-100/bg-red-100colour strings, a matching tonalTaskStatusBadge(task-status-control.tsx) is available for task status, quest card titles (quest-card.tsx,public-quest-card.tsx) render in Instrument Serif viafont-serif, and the dashboard opens with a newWelcomeHeropanel (heroCard,.t-displaygreeting,.t-monoeyebrow, goldbrand-cta).
Fixes
POST /api/billing/checkoutrefuses 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
.envand 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, souseFormStatewas left holdingundefinedand the next render crashed readingstate.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 yieldsundefined(#460).Fixed signup confirmation and password-reset email links embedding
http://localhost:3000instead of the production domain (#308). ExtractedgetSiteOrigin()into its own module and addedVERCEL_URLas an intermediate fallback betweenNEXT_PUBLIC_SITE_URLand 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.mdwith a current-version status header (rootpackage.jsonas the canonical version), concrete Keep-a-Changelog emoji-section conventions, aNEXT_PUBLIC_FEATURE_*flag reference table, and a Release Checklist covering version bump, changelog promotion, and tagging.