CasosNoticiasSobre míPrecios
Pregúntale a GaganComenzar

Explorar

  • Inicio
  • Sobre mí
  • Casos
  • Noticias
  • Precios

Conectar

  • Preguntar
  • Reservar llamada
  • Enviar correo

Recursos

  • Documentación
  • Kit de medios
  • Mapa del sitio
  • Feed RSS

Legal

  • Privacidad
  • Términos de uso

© 2026 Gagan Malik. Todos los derechos reservados.

Privacidad|Términos de uso|Mapa del sitio

Technical

  • Tech Stack
  • SEO & Discovery
  • Design System
  • UI Components
  1. Inicio
  2. Documentation

Documentation

Technical reference — tech stack, SEO & discovery, design system (spacing, accessibility, shadcn Maia), and UI components.

Tech Stack

Last updated: 2026-02-27

PWA

  • Serwist — Service worker via @serwist/turbopack (Turbopack-compatible)
  • Offline support — Precache static assets, runtime cache pages on visit, fallback to /~offline for documents when offline
  • Manifest — public/manifest.json with name, icons (maskable), theme_color; installable on desktop and mobile

Framework

  • Next.js 16 — App Router, server components, static/dynamic rendering
  • React 19 — Latest React with concurrent features

UI

  • shadcn/ui — Radix Maia theme (style: "radix-maia" in components.json)
  • Tailwind CSS v4 — Utility-first styling via @tailwindcss/postcss
  • Radix UI — Accessible primitives (Select, Tooltip, Slot, etc.)
  • Lucide React — Icon library
  • class-variance-authority (cva) — Variant styling for components
  • tailwind-merge — Merge Tailwind classes without conflicts

Data

  • Neon — Serverless Postgres (@neondatabase/serverless)
  • Drizzle ORM — Type-safe queries and migrations
  • pgvector — Vector embeddings for RAG (Ask page)

AI

  • Vercel AI SDK — ai and @ai-sdk/react for streaming chat
  • OpenAI — Embeddings and chat completions (via API key or Vercel AI Gateway)

Feature Flags

  • lib/feature-flags.ts — getFeatureFlag(name) reads NEXT_PUBLIC_FEATURE_<NAME>=true
  • middleware.ts — Optional headers for RSC (e.g. x-feature-hero-variant)
  • See FEATURE_FLAGS.md for usage

Internationalization

  • next-intl — Locale-based routing, messages, getTranslations/useTranslations, Link/usePathname/useRouter from @/i18n/navigation
  • Locales: en (default), localePrefix: 'always' → /en/about, etc.
  • Messages: messages/en.json with namespaces (Common, HomePage, StoriesPage, etc.)
  • RAG/Ask: English-only per plan; no i18n extraction for Ask page strings

Key Libraries

CategoryPackagePurpose
Markdown@mdx-js/mdx, react-markdown, remark-gfm, rehype-highlight, rehype-slugMDX and Markdown rendering
Searchfuse.jsClient-side fuzzy search
Command palettecmdkCommand menu (⌘K)
Carouselembla-carousel-reactImage/content carousels
Forms & validationzodSchema validation
Analytics@vercel/analytics, @vercel/speed-insightsVercel analytics and Core Web Vitals
PaymentsstripeCheckout and webhooks
Themingnext-themesDark/light mode
ToastssonnerToast notifications
Motionframer-motionDeclarative animations; lib/motion.ts re-exports + useReducedMotionTransition()
PWA@serwist/turbopack, serwistService worker, offline support, precaching

SEO & Discovery

Last updated: 2026-02-24

Summary of how the site is made discoverable to search engines and AI answer engines. For the full plan (metrics, events, AEO), see ANALYTICS_SEO_PLAN.md.

Sitemap

  • File: app/sitemap.ts
  • URL: /sitemap.xml (linked from robots.txt)
  • Contents: Base pages (home, stories, about, newsroom, pricing, ask, docs, legal), story projects, and newsroom posts — all locales (en, hi, ar, es)
  • Source: getLocalizedUrl() from lib/site.ts; NEXT_PUBLIC_SITE_URL for base URL

Robots

  • File: app/robots.ts
  • URL: /robots.txt
  • Rules: User-Agent: * — allow /, disallow /api/
  • Sitemap: Points to {baseUrl}/sitemap.xml

AI Crawlers

We allow the following AI crawlers to access public content (AEO / answer engine optimization):

CrawlerPurpose
GPTBotOpenAI (ChatGPT)
PerplexityBotPerplexity
Google-ExtendedGoogle AI Overviews
anthropic-aiClaude
CCBotCommon Crawl

Each has explicit allow: "/" and disallow: ["/api/"] in app/robots.ts. This supports appearing as a cited source for queries like "who is Gagan Malik" or "how to contact Gagan Malik."

Canonicals

Canonical URLs are set via metadata.alternates.canonical on key pages (layout, stories, newsroom, pricing, story detail) to avoid duplicate content. Uses getLocalizedUrl() for locale-prefixed URLs.

Related

  • ANALYTICS_SEO_PLAN.md — Full SEO/AEO plan, metrics, implementation phases
  • ANALYTICS_IMPLEMENTATION.md — Events, env vars, GA4

Design System — shadcn UI Consistency

Source of truth: components.json with style: "radix-maia"
Last updated: 2026-02-27

Native Maia theme (enforced)

Use native shadcn Maia theme components only. Do not add custom style overrides that deviate from the theme.

  • All UI components (Select, Button, Dialog, etc.) must come from the shadcn registry.
  • No className overrides for radius, background, or border on shadcn primitives. If something looks wrong, fix the component file or theme, not the usage.
  • Add or update components with:
    Code
    npx shadcn@latest add <component> --overwrite
    
  • Do not change style or baseColor in components.json unless intentionally switching themes.

Design tokens

  • Radius: Defined in app/globals.css via @theme inline and :root.
  • Muted (light mode): --muted: oklch(0.92 0 0) — tuned for perceptible ghost-button hover contrast against --background (WCAG 2.1 non-text contrast).
  • --radius: 1rem (large). Derived: --radius-sm through --radius-4xl.
  • Use the same radius token for related elements (e.g. trigger and dropdown).

Spacing scale

Use a consistent spacing scale for padding, margins, and gaps:

PixelsTailwindUsage
4p-1, gap-1Tight gaps, icon padding
8p-2, gap-2Small gaps between related elements
16p-4, gap-4Default padding, card gaps
24p-6, gap-6Section spacing
32p-8, gap-8Large section breaks

Avoid arbitrary values unless aligning to a specific design spec.

Accessibility

  • WCAG AA — Text contrast and non-text contrast (ghost buttons, borders).
  • Focus states — All interactive elements have visible focus rings (focus-visible:ring-2, focus-visible:ring-ring).
  • Semantic markup — Use aria-label, aria-describedby, role where appropriate. Prefer native elements (<button>, <a>) over divs.
  • Reduced motion — Respect prefers-reduced-motion for animations. Transitions should clarify state, not distract.

Motion (Framer Motion)

  • Library: Framer Motion is available for declarative enter/exit, layout, and gesture-based animations.
  • Import path: Use @/lib/motion (re-exports motion, AnimatePresence, and types) so future reduced-motion or config changes stay centralized.
  • Reduced motion: When adding animated components, use the useReducedMotionTransition() hook from @/lib/motion for the transition prop so animations are disabled when the user prefers reduced motion. See lib/hooks.ts for usePrefersReducedMotion().
  • Guideline: Use motion to clarify state and hierarchy; avoid decorative or distracting animation.

Touch targets

  • Minimum 44×44pt for interactive elements (buttons, links, form controls).
  • Use min-h-11 (44px) or size-11 for touch-friendly buttons on mobile.
  • Ensure adequate spacing between tap targets to prevent mis-taps.

Dropdowns and popovers

  • Corner radius: Dropdowns/popovers that attach to triggers must use the same corner radius as the trigger.
    • Example: Select trigger uses rounded-4xl (pill) → SelectContent uses rounded-4xl.
  • Positioning: For attached dropdowns that should overlap the trigger (shadcn Maia style), use:
    Code
    sideOffset={-4}
    
    instead of a positive gap (e.g. sideOffset={8}).

UI alignment checklist

When aligning a component with a reference (screenshot or live example), verify before declaring done:

  • Compared to reference screenshot or live example
  • Trigger: radius, size, padding match
  • Content: radius, position, overlap match
  • Items: first/last/middle use context-aware radius (first:rounded-t-*, last:rounded-b-*)
  • No conflicting transforms or offsets (check full styling chain)
  • Docs updated only after verification

Component-specific notes

ComponentTrigger radiusContent radiusItem radiussideOffset
Selectrounded-4xlrounded-4xlfirst:rounded-t-4xl last:rounded-b-4xl first:last:rounded-4xl-4
Switch————
Table——first:rounded-l-lg last:rounded-r-lg (8px, on first/last cell per row)—

Switch: Use for boolean toggles (e.g. Ask voice settings: Voice mode, Wake word). Wrap in min-h-[44px] min-w-[44px] flex items-center justify-center when a larger touch target is needed. From shadcn registry; has built-in focus-visible ring.

Table row cells: First and last cells (th or td) in each row use rounded-l-lg / rounded-r-lg (8px) in default and hover states. Requires border-separate border-spacing-0 on the table for rounded corners to render correctly.

UI Components

Source: components/ui — shadcn registry (radix-maia theme)
Last updated: 2026-03-19

shadcn components

All components are from the shadcn registry unless noted. Add or update with:

Code
npx shadcn@latest add <component> --overwrite
ComponentFileDescription
Badgebadge.tsxStatus labels, tags
Breadcrumbbreadcrumb.tsxNavigation breadcrumbs
Buttonbutton.tsxPrimary actions, variants (default, outline, ghost, etc.)
Cardcard.tsxCard container with header, content, footer
Carouselcarousel.tsxImage/content carousel (embla-carousel)
Commandcommand.tsxCommand palette (⌘K)
Dialogdialog.tsxModal dialogs
Dropdown Menudropdown-menu.tsxContext menus, dropdown actions
Hover Cardhover-card.tsxHover-triggered popover
Inputinput.tsxText input field
Input Groupinput-group.tsxInput with addons (prefix/suffix)
Paginationpagination.tsxPage navigation
Popoverpopover.tsxFloating popover content
Selectselect.tsxDropdown select
Separatorseparator.tsxVisual divider
Sheetsheet.tsxSlide-out panel
Sidebarsidebar.tsxCollapsible sidebar layout
Skeletonskeleton.tsxLoading placeholder
Sonnersonner.tsxToast notifications
Switchswitch.tsxToggle (Voice mode, Wake word in Ask voice settings sheet)
Tabletable.tsxData tables
Textareatextarea.tsxMulti-line text input
Tooltiptooltip.tsxHover tooltips

Custom components (in components/ui)

ComponentFileDescription
Tracked Linktracked-link.tsxLink wrapper with analytics (trackEvent)

Page skeletons (components/skeletons)

Route-level loading UIs use shared skeleton components. They mirror mobile vs desktop layouts: tighter horizontal padding (px-4 → sm:px-6 / sm:px-8), primary CTA pairs stacked vertically with full-width controls on small viewports (flex-col → sm:flex-row), newsroom bento heroes with image on top and text block below on mobile (matching WritingsBentoCard), and section headers with title + “View archive” stacked on narrow screens. Inline skeletons in app/**/loading.tsx (e.g. pricing, archive, legal index) follow the same padding pattern.

En esta página

  • PWA
  • Framework
  • UI
  • Data
  • AI
  • Feature Flags
  • Internationalization
  • Key Libraries
  • Sitemap
  • Robots
  • AI Crawlers
  • Canonicals
  • Related
  • Native Maia theme (enforced)
  • Design tokens
  • Spacing scale
  • Accessibility
  • Motion (Framer Motion)
  • Touch targets
  • Dropdowns and popovers
  • UI alignment checklist
  • Component-specific notes
  • shadcn components
  • Custom components (in `components/ui`)
  • Page skeletons (`components/skeletons`)