TL;DR

Have you seen those viral SBTI Tests with highly relatable labels like "ATM-er" or "CTRL" flooding social media? As a developer or indie hacker, you might wonder: How long would it take to build something like this? What used to take a week of full-stack development can now be compressed into half a day using OpenSpec, Spec Coding, and Vibe Coding. This article shows how we used AI coding agents like Trae or Cursor to move from an OpenSpec proposal to task execution, scoring logic, radar charts, and shareable result posters.

Table of Contents

Key Takeaways

  • Paradigm Shift: Transitioning from "writing code" to managing OpenSpec proposals, specs, tasks, and agent execution.
  • Architecture: Utilizing Next.js independent routing (/quiz/[slug]) paired with a robust i18n system.
  • Animation Optimization: Leveraging requestAnimationFrame to eliminate visual flickering during quiz transitions.
  • Bug Squashing: Bypassing html2canvas rendering bugs with rgba colors and CSS shadows.

🔧 Experience the Result: You can check out the final product we built with AI by visiting the SBTI Online Test.

Vibe Coding vs Spec Coding

Before diving in, let's clarify the two main paradigms of AI-assisted development:

  1. Vibe Coding: Going with the flow. You tell the AI "what kind of page you want" in natural language, and tweak as you go. Perfect for rapid MVPs and visual inspiration.
  2. Spec Coding: An engineering mindset. You first use OpenSpec to produce artifacts such as proposal.md, specs/spec.md, and tasks.md. Once reviewed, the agent executes the plan in batches. Perfect for complex state management and long-term projects.

For the SBTI test site, we used a hybrid approach: "Early-stage Spec Coding + Late-stage Vibe Coding for details."

OpenSpec in Practice: Turn an Idea into an Executable Change

Real Spec Coding is not just asking AI to write a document first. It means putting the idea into a traceable change lifecycle. For the SBTI quiz site, the first step is not asking AI to write the page directly. First, install and initialize OpenSpec in the project:

graph LR A["openspec init"] --> B["/opsx:propose"] B --> C["Review specs & tasks"] C --> D["/opsx:apply"] D --> E["Test & iterate"] E --> F["/opsx:archive"] style B fill:#e1f5fe,stroke:#01579b style D fill:#fff3e0,stroke:#e65100 style F fill:#e8f5e9,stroke:#2e7d32
bash
# Install OpenSpec globally
npm install -g @fission-ai/openspec@latest

# Navigate to your project and initialize
cd qubittool
openspec init

This creates an openspec/ directory so your AI IDE can understand the SDD workflow. Then use the propose command in Trae, Cursor, or Claude Code:

text
/opsx:propose build-sbti-quiz

The goal is not to generate React components immediately. The goal is to make the AI produce reviewable engineering artifacts:

File Purpose What it captures for SBTI
proposal.md Why this change exists and what is out of scope This is a lightweight personality quiz, not a medical diagnosis
specs/spec.md Observable user behavior Intro, quiz flow, scoring, result page, gallery, poster sharing
tasks.md Executable checklist Data model, routes, scoring, radar chart, i18n, validation

Before running apply, review these files manually. spec.md and tasks.md are especially important because they decide whether the agent edits with focus or starts changing unrelated files.

Step 1: Requirements Breakdown & Spec Generation (1 Hour)

An SBTI test seems simple, but involves complex state flows: Home (Intro) → Quiz Flow → Calculation → Result Poster → Personality Gallery.

SBTI personality test result card design showing JOKE-R type When generating the Spec, we first broke down the core visual elements (like the character card design of JOKE-R)

We started by opening the chat in an AI IDE like Trae or Cursor and putting the high-level requirement into the OpenSpec propose stage instead of letting the agent write code directly:

Example Prompt: "I want to build a 15-dimensional personality test site similar to SBTI. Stack: Next.js (Pages Router) + CSS Modules. Core requirements:

  1. Data: Read 30 questions and 15 personality mapping dimensions from a single quiz-definitions.ts.
  2. Routing: Independent /quiz/sbti structure, supporting SSR.
  3. UI: High-fidelity clone, responsive for mobile, must include frosted glass effects.
  4. Feature: Generate a 15-dimensional radar chart poster upon completion, supporting long-press to save. Please use /opsx:propose build-sbti-quiz to create an OpenSpec change. Generate proposal.md, specs/spec.md, and tasks.md. Do not write code yet."

After propose runs, the AI will generate artifacts under openspec/changes/build-sbti-quiz/. Review proposal.md, specs/spec.md, and tasks.md first, and make sure they capture the data structures, component hierarchy, and acceptance scenarios correctly. Only when the spec is rigorous enough, including where state lives, how scores are normalized, and whether poster sharing depends on Canvas, will the later execution avoid infinite loops of hallucinated bugs.

After reviewing the artifacts, run:

text
/opsx:apply build-sbti-quiz

The AI will now read tasks.md, implement the code step by step, and check off the tasks. Compared with a giant prompt like "build me an SBTI test site," this workflow greatly reduces the chance of unrelated edits.

Step 2: Core Architecture & Component Generation (2 Hours)

After approving the Spec, we authorized the AI Agent to execute the tasks. This is where the AI's explosive productivity shines:

1. Dynamic Routing System

The AI automatically scaffolded /pages/quiz/[slug]/index.tsx and /pages/quiz/[slug]/result/[code].tsx. This decoupled architecture means the system can host not just SBTI, but MBTI, Enneagram, or any other quiz in the future with zero routing overhead.

2. State Machine Flow

The AI generated useState-based view switching logic, enabling seamless, SPA-like transitions between IntroViewQuizViewResultView without page reloads, while maintaining the answer array context.

3. Multilingual i18n

By providing just a base Chinese JSON file, the AI Agent auto-translated the English JSON and injected the useTranslation('sbti-test') hooks across all React components perfectly.

The value of OpenSpec here is that "must be bilingual" becomes an acceptance condition, not a reminder after the fact:

markdown
#### Scenario: English and Chinese pages have equivalent content
- **WHEN** a user visits `/zh/quiz/sbti` and `/quiz/sbti`
- **THEN** both pages show the same number of questions, result types, and dimension explanations
- **AND** all copy must come from `public/locales/{lang}/sbti-test.json`

Step 3: Polishing High-Fidelity UX (1.5 Hours)

The skeleton was up, but the devil is in the details. Here we switched back to Vibe Coding mode to tackle specific UI/UX bugs interactively:

Pain Point 1: Flickering Quiz Cards

When a user clicked an option, the transition to the next question had a brief, jarring flicker.

  • Vibe Prompt: "The current setTimeout animation feels flickering. Refactor it using pure CSS transitions."
  • AI Solution: The AI introduced a Double requestAnimationFrame (rAF) pattern. It sets the card opacity to 0, waits one frame, updates the question index, and restores opacity: 1 on the next frame. The result? Buttery smooth fade-ins.

Pain Point 2: Red Borders on Generated Posters

The result page uses html2canvas to capture the DOM into an image. However, the generated poster had weird red borders and broken layouts.

SBTI test result poster generated with html2canvas showing radar chart This is the perfectly rendered poster after fixing the canvas bugs
- **Vibe Prompt**: "`html2canvas` is rendering `box-shadow` incorrectly, and rgba backgrounds turn black." - **AI Solution**: The AI quickly identified this as a known limitation of `html2canvas`. It bulk-converted `rgba()` to equivalent Hex colors in the CSS, removed complex shadows from the screenshot target, and wrapped it in a fixed-width container. Problem solved.

Pain Point 3: WeChat Browser Download Blocking

  • Vibe Prompt: "Clicking 'Save Image' does nothing inside the WeChat in-app browser."
  • AI Solution: The AI added a navigator.userAgent sniffer. If it detects MicroMessenger, it hides the download button and displays a toast notification: "Please long-press the image to save."

OpenSpec Artifact Examples for the SBTI Site

To make this post reusable, here is a simplified version of what the OpenSpec artifacts should contain. Do not let the agent work from a single sentence like "build an SBTI quiz." Write the behavior as WHEN/THEN scenarios.

Example specs/spec.md

markdown
### Requirement: User completes the SBTI quiz and receives a result

#### Scenario: First visit to the quiz page
- **WHEN** a user visits `/quiz/sbti`
- **THEN** the system shows the quiz intro, start button, and result examples
- **AND** the mobile first screen should show the core CTA without horizontal scrolling

#### Scenario: User completes all questions
- **WHEN** the user answers every question
- **THEN** the system accumulates scores across 15 dimensions
- **AND** normalizes each score to 0-100
- **AND** maps the weighted combination to one SBTI result code

#### Scenario: User saves the result poster
- **WHEN** the user clicks Save Poster
- **THEN** the system generates an image containing the personality code, radar chart, and share copy
- **AND** in WeChat browser, the UI instructs the user to long-press and save the image

Example tasks.md

markdown
- [ ] Define the `sbti` quiz data model: questions, options, dimension weights, and result mapping
- [ ] Implement the reusable `/quiz/[slug]` entry page
- [ ] Implement quiz state flow: intro -> quiz -> result
- [ ] Implement 15-dimension score normalization and result-code matching
- [ ] Implement the radar chart and result explanation page
- [ ] Implement `html2canvas` poster generation and WeChat save guidance
- [ ] Add `zh` / `en` i18n copy
- [ ] Add mobile acceptance checks: 375px width, no horizontal scroll, CTA visible

If the AI discovers that html2canvas does not support some CSS effects reliably, do not let it improvise in chat. Update the OpenSpec decision first:

markdown
#### Decision: Use simplified CSS inside the screenshot target
- Do not use complex `box-shadow` inside the poster DOM
- Use opaque Hex colors for screenshot backgrounds
- Use a fixed-width screenshot container to avoid DPR layout drift on mobile

Then continue /opsx:apply. This keeps OpenSpec as the single source of truth, and future maintainers can understand why the poster area does not use the most complex frosted-glass effects.

Best Practices for Indie Hackers

  1. CSS Modules > Tailwind for Micro-adjustments: While Tailwind is fast, when you need the AI to make pixel-perfect UI tweaks (like removing negative margins that cause mobile whitespace), isolated CSS Modules drastically reduce style contamination.
  2. Single Source of Truth: Never let the AI hardcode quiz questions inside UI components. Force it to write a quiz-definitions.ts first, and have all components read from it.
  3. OpenSpec first, Vibe Coding later: Architecture, data model, and scoring logic should be captured in OpenSpec. Colors, animation, spacing, and micro-interactions can be polished with Vibe Coding.
  4. Commit Often: AI will break things. Whenever a major module like quiz logic works, commit it immediately. If the AI gets stuck in a bug-fixing loop, roll back and rewrite the spec.
  5. Archive after shipping: Once the feature is live, run /opsx:archive build-sbti-quiz so proposal, spec, and tasks become project memory instead of stale active context.

FAQ

Q1: Can Vibe Coding completely replace writing code?

Not yet. Vibe Coding is great for "velocity," but for complex animation timing (like rAF) or platform-specific edge cases (like WeChat browsers), you still need debugging intuition to guide the AI toward the right fix.

Q2: What kind of projects is Spec Coding best for?

It's ideal for projects with explicit data flows and multiple interconnected files (e.g., Web Apps with state management and user systems). If you're building a simple single-page calculator, pure Vibe Coding is usually enough.

Q3: Why use independent routing for quiz results?

For SEO and social virality. If you only have /quiz/sbti, anyone clicking a shared link just sees the homepage. By using /quiz/sbti/result/ATM-er coupled with Open Graph tags, you enable true virality: users click a link and immediately see their friend's specific personality result.

Q4: What was the most valuable OpenSpec step in this project?

The propose step. It turned "build an SBTI quiz" into reviewable proposal, spec, and tasks artifacts, surfacing risks around quiz data modeling, scoring logic, i18n, poster generation, and mobile behavior before the agent started writing code.

Summary

From requirement breakdown to final deployment, OpenSpec, Spec Coding, and Vibe Coding compressed what used to be days of full-stack, multilingual web development into a single afternoon.

In this era, AI agents amplify implementation, while product definition, spec review, and acceptance design become more important. OpenSpec does not make the AI freer; it puts the AI on rails so it can execute quickly without drifting.

👉 See the result of Vibe Coding: Take the SBTI Test