Skip to content

feat(web): generate OG images, redesign share flow, and overhaul stack builder DX#1097

Merged
AmanVarshney01 merged 1 commit into
mainfrom
feat/web-og-images-builder-dx
Jul 2, 2026
Merged

feat(web): generate OG images, redesign share flow, and overhaul stack builder DX#1097
AmanVarshney01 merged 1 commit into
mainfrom
feat/web-og-images-builder-dx

Conversation

@AmanVarshney01

@AmanVarshney01 AmanVarshney01 commented Jul 2, 2026

Copy link
Copy Markdown
Owner

OG images

The site-wide og:image pointed at a 3166×1909 raw homepage screenshot on R2, declared as 1200×630, and shared stacks all rendered the same generic preview.

  • /og/stack — dynamic per-stack OG card: project name, $ <pm> create better-t-stack@latest <name> prompt line, and color-coded chips for every selected tech (capped at 15 + "+N more"). Params parse through the same sanitizing nuqs loader as /stack.
  • /og/site/[page] — designed 1200×630 cards for home, /new, /showcase, /analytics (allowlisted, statically generated).
  • Shared OgShell terminal frame (Catppuccin palette); the docs OG route is refactored onto it so all three generators stay visually in sync.
  • /stack metadata now emits the per-stack image, uses the tech summary as the OG description, and keeps query params in og:url (previously dropped).

Share flow

ShareDialog rebuilt: live social-card preview (the actual /og/stack render), one-click copy rows for the share link and the CLI command, native share sheet when available, stack-aware X post text, and an on-demand QR toggle. Project names with spaces are now normalized identically across the dialog, the /stack page, and the builder hook.

Stack builder DX

  • CLI command collapses to one line with a Flags expand toggle (it previously rendered ~20 lines for a rich stack).
  • Selected-stack badges flattened into a chip cloud — clicking a badge jumps to its category section; only the X removes.
  • New sticky category rail with scroll-spy active highlight and jump-to-section, on desktop and mobile.
  • Actions consolidated into one panel: inline YOLO toggle (destructive styling when armed), Load only shown when a save exists, single-item settings menu removed.
  • Responsive: sidebar narrows to 19rem until lg, mobile rail, aria-describedby on the project-name error, focus/active rings no longer clipped by the rail.

CLI parity + tests

Audited every web compatibility rule against apps/cli validation. One gap found and fixed: the CLI rejects tauri with Convex Better Auth on Next.js/TanStack Start; the web now disables it with the same reason and auto-adjusts.

New property test: 3,000 seeded random stacks run through sanitize + the adjust cascade to a fixpoint, asserting convergence and that no selected option remains in a disabled state — i.e. every copyable command is CLI-valid.

Test plan

  • bun test in apps/web (21 tests, incl. new invariant + regression tests)
  • bun run check clean; tsc --noEmit clean apart from pre-existing bun:test resolution
  • Manually verified in-browser: OG endpoints render correct 1200×630 PNGs, share dialog (desktop + mobile), rail jumps/scroll-spy, badge navigation, YOLO toggle, 390px/700px/1280px layouts

Summary by CodeRabbit

  • New Features

    • Added category navigation in the stack builder to jump between sections.
    • Added more share options, including preset loading and a new mode toggle in the builder.
    • Added richer preview images for home, analytics, showcase, stack, and docs pages.
  • Bug Fixes

    • Improved stack compatibility handling so unsupported options are removed automatically.
    • Updated stack previews and badges to better reflect selected technologies.
    • Improved project name and command display behavior for easier reading.

…k builder dx

OG images:
- add per-stack dynamic og image route (/og/stack) rendering the project
  name and color-coded tech chips from the share url params
- add designed og cards for home, /new, /showcase, /analytics
  (/og/site/[page]) replacing the misdeclared 3166x1909 raw screenshot
- extract shared OgShell terminal frame and refactor the docs og route
  onto it; /stack metadata now emits the per-stack image, stack summary
  as description, and keeps query params in og:url

Share flow:
- rebuild ShareDialog: live social-card preview, copy link + copy cli
  command rows, native share, better X post text, on-demand QR
- normalize project names (spaces to dashes) consistently across the
  dialog, /stack page, and builder hook

Stack builder:
- collapse cli command to one line with a flags toggle
- flatten selected-stack badges into a chip cloud; badges now jump to
  their category section (X removes)
- add sticky category rail with scroll-spy active state and jump-to-section
- consolidate actions into one panel; inline YOLO toggle, drop the
  single-item settings menu and "no saved stack" placeholder
- responsive fixes: narrower sidebar until lg, mobile rail, a11y wiring
  for the project name error

Compatibility:
- mirror the cli rule blocking tauri with convex better-auth on next.js
  or tanstack start (disable reason + auto-adjust)
- add property test: 3000 random stacks converge and contain no
  disabled selections after adjustment
@vercel

vercel Bot commented Jul 2, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
create-better-t-stack-web Ready Ready Preview, Comment Jul 2, 2026 6:18pm

Request Review

@coderabbitai

coderabbitai Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

This PR adds shared Open Graph rendering primitives and new OG image routes, updates page metadata to reference new OG image URLs, introduces stack-utils helpers for project-name formatting and selected-tech derivation, rewrites the share dialog, reworks stack builder UI (category navigation, badge flattening, action buttons, CLI command expansion), and adds a Tauri/Convex/Better-Auth compatibility rule with tests.

Changes

OG Image Revamp and Stack Builder UI

Layer / File(s) Summary
Shared OG rendering primitives
apps/web/src/lib/og.tsx
Adds OG_SIZE, ogColors, and the OgShell component providing a consistent header/footer frame for OG image rendering.
OG image routes
apps/web/src/app/og/docs/[...slug]/route.tsx, apps/web/src/app/og/site/[page]/route.tsx, apps/web/src/app/og/stack/route.tsx
Docs OG route refactored to use OgShell/OG_SIZE; new /og/site/[page] route renders static-page OG images from a PAGES map with generateStaticParams; new /og/stack route renders tech-chip previews and install commands.
Stack formatting and selection helpers
apps/web/src/lib/stack-utils.ts
Adds formatProjectName, SelectedTech, getSelectedTechs, and a shared serializeStackToSearchString helper used by generateStackOgImageUrl and existing sharing URL functions.
Page metadata updates
apps/web/src/app/(home)/analytics/page.tsx, apps/web/src/app/(home)/new/page.tsx, apps/web/src/app/(home)/showcase/page.tsx, apps/web/src/app/layout.tsx, apps/web/src/app/(home)/stack/page.tsx
OpenGraph/Twitter image URLs point to new better-t-stack.dev/og/site/* assets; the stack page's generateMetadata now derives description, sharing URL, and OG image dynamically via stack-utils.
Stack display and share dialog
apps/web/src/app/(home)/stack/_components/stack-display.tsx, apps/web/src/components/ui/share-dialog.tsx
StackDisplay derives tech badges via getSelectedTechs; ShareDialog is rewritten to show an OG preview image, copy actions for link/command, Twitter/native share, and a toggleable QR panel.
Category navigation rail
apps/web/src/app/(home)/new/_components/stack-builder/category-nav.tsx
New CategoryNav component tracks active category via scroll position and auto-scrolls its chip rail; scrollToCategorySection jumps to a category section.
Selected badges flattened with jump-to-category
apps/web/src/app/(home)/new/_components/stack-builder/selected-stack-badges.tsx
Replaces grouped category rendering with a flat chip list and adds an onJump callback for navigating to a category.
Action buttons extended
apps/web/src/app/(home)/new/_components/action-buttons.tsx, apps/web/src/app/(home)/new/_components/share-button.tsx
ActionButtons gains preset, share, and YOLO-toggle props with shared muted styling; ShareButton switches to a primary color scheme.
Stack builder layout wiring
apps/web/src/app/(home)/new/_components/stack-builder/index.tsx, apps/web/src/app/(home)/new/_components/stack-builder/use-stack-builder.ts, apps/web/src/app/(home)/new/_components/stack-builder/tech-categories.tsx
Wires CategoryNav and jump navigation, centralizes ActionButtons, adds expandable/truncated CLI command display, updates error accessibility ids, extends useStackBuilder with categoryProgress, and adjusts a placeholder height.

Tauri/Convex/Better-Auth Compatibility Rule

Layer / File(s) Summary
Tauri incompatibility predicate and wiring
apps/web/src/app/(home)/new/_components/utils.ts
Adds isTauriBlockedByConvexBetterAuth and wires it into analyzeStackCompatibility (removes tauri addon when blocked) and getDisabledReason (returns a specific message).
Compatibility invariant tests
apps/web/test/stack-compatibility-invariant.test.ts
Adds randomized and deterministic tests validating that stack adjustment converges and no disabled selections remain, including a targeted check that tauri is removed for Convex/Better-Auth/Next.js stacks.

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main changes: OG image generation, the share flow redesign, and the stack builder UX overhaul.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint install failed. For unrecoverable errors, disable the tool in CodeRabbit configuration.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/web/src/app/(home)/stack/page.tsx (1)

17-46: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Normalize projectName before building /stack metadata

loadStackParams() preserves raw query values, so hand-edited or legacy /stack?name=... URLs can render unformatted titles and OG alt text here. Format params.projectName the same way as ShareDialog and the builder to keep previews consistent.

🧹 Nitpick comments (5)
apps/web/src/lib/og.tsx (1)

65-93: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Optional: dedupe the traffic-light dots.

The three colored dot divs only differ by color; could be mapped from a small array for less repetition. Purely cosmetic.

♻️ Optional dedup
-          <div style={{ display: "flex", gap: "8px" }}>
-            <div style={{ width: "12px", height: "12px", borderRadius: "50%", background: ogColors.red, display: "flex" }} />
-            <div style={{ width: "12px", height: "12px", borderRadius: "50%", background: ogColors.yellow, display: "flex" }} />
-            <div style={{ width: "12px", height: "12px", borderRadius: "50%", background: ogColors.green, display: "flex" }} />
-          </div>
+          <div style={{ display: "flex", gap: "8px" }}>
+            {[ogColors.red, ogColors.yellow, ogColors.green].map((c) => (
+              <div key={c} style={{ width: "12px", height: "12px", borderRadius: "50%", background: c, display: "flex" }} />
+            ))}
+          </div>
apps/web/src/app/og/site/[page]/route.tsx (1)

57-78: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Command-line block duplicates the one in the stack OG route.

This $ <command> block (dollar sign span + monospace command text) is nearly identical to the one in apps/web/src/app/og/stack/route.tsx (lines 58-71), minus the blinking-cursor div. Could be extracted into a small shared OgCommandLine primitive in apps/web/src/lib/og.tsx alongside OgShell to keep styling consistent as more OG pages are added.

apps/web/src/app/(home)/new/_components/stack-builder/category-nav.tsx (2)

45-73: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Prefer tracking category alongside element instead of re-parsing from id.

sections is built by mapping progress to elements and losing the category association; the loop then recovers it via section.id.slice(idPrefix.length + 1) as typeof current (line 64), requiring an unchecked cast. Zipping { category, el } pairs up front would avoid the string-slicing/cast round-trip.

♻️ Proposed refactor
-    const sections = progress
-      .map(({ category }) => document.getElementById(`${idPrefix}-${category}`))
-      .filter((el): el is HTMLElement => !!el);
-    const viewport = sections[0] ? getScrollViewport(sections[0]) : null;
+    const sections = progress
+      .map(({ category }) => ({
+        category,
+        el: document.getElementById(`${idPrefix}-${category}`),
+      }))
+      .filter((s): s is { category: TechCategory; el: HTMLElement } => !!s.el);
+    const viewport = sections[0] ? getScrollViewport(sections[0].el) : null;
@@
-      let current = progress[0]?.category ?? null;
-      for (const section of sections) {
-        if (section.getBoundingClientRect().top - viewportTop <= 80) {
-          current = section.id.slice(idPrefix.length + 1) as typeof current;
-        }
-      }
+      let current = progress[0]?.category ?? null;
+      for (const section of sections) {
+        if (section.el.getBoundingClientRect().top - viewportTop <= 80) {
+          current = section.category;
+        }
+      }

94-119: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add aria-current to the active category chip.

The active chip is only conveyed visually (ring styling); screen-reader users have no indication which category is currently active while scrolling.

♻️ Proposed fix
           <button
             key={category}
             type="button"
             data-category={category}
+            aria-current={isActive ? "true" : undefined}
             onClick={() => scrollToCategorySection(idPrefix, category)}
apps/web/src/lib/stack-utils.ts (1)

56-79: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicated sentinel-filtering logic vs. generateStackSummary.

The "none"/"false"/git|install|auth === "true" skip rules here duplicate the identical filtering already implemented in generateStackSummary (same file, lines 81-105). If one is updated without the other, badge counts and summary text will silently diverge.

Consider extracting a shared isSelectableTechId(category, id) helper used by both functions.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f2325371-b6a0-4e20-a059-9c9609cef2a3

📥 Commits

Reviewing files that changed from the base of the PR and between 7abd369 and 78c30c0.

📒 Files selected for processing (22)
  • apps/web/src/app/(home)/analytics/page.tsx
  • apps/web/src/app/(home)/new/_components/action-buttons.tsx
  • apps/web/src/app/(home)/new/_components/share-button.tsx
  • apps/web/src/app/(home)/new/_components/stack-builder/category-nav.tsx
  • apps/web/src/app/(home)/new/_components/stack-builder/index.tsx
  • apps/web/src/app/(home)/new/_components/stack-builder/selected-stack-badges.tsx
  • apps/web/src/app/(home)/new/_components/stack-builder/tech-categories.tsx
  • apps/web/src/app/(home)/new/_components/stack-builder/use-stack-builder.ts
  • apps/web/src/app/(home)/new/_components/utils.ts
  • apps/web/src/app/(home)/new/_components/yolo-toggle.tsx
  • apps/web/src/app/(home)/new/page.tsx
  • apps/web/src/app/(home)/showcase/page.tsx
  • apps/web/src/app/(home)/stack/_components/stack-display.tsx
  • apps/web/src/app/(home)/stack/page.tsx
  • apps/web/src/app/layout.tsx
  • apps/web/src/app/og/docs/[...slug]/route.tsx
  • apps/web/src/app/og/site/[page]/route.tsx
  • apps/web/src/app/og/stack/route.tsx
  • apps/web/src/components/ui/share-dialog.tsx
  • apps/web/src/lib/og.tsx
  • apps/web/src/lib/stack-utils.ts
  • apps/web/test/stack-compatibility-invariant.test.ts
💤 Files with no reviewable changes (1)
  • apps/web/src/app/(home)/new/_components/yolo-toggle.tsx

@AmanVarshney01

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@AmanVarshney01 AmanVarshney01 merged commit 0ca6f35 into main Jul 2, 2026
3 checks passed
@AmanVarshney01 AmanVarshney01 deleted the feat/web-og-images-builder-dx branch July 2, 2026 18:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant