fix(upgrade): stack paywall cards on narrow shell to stop cramping - #343
Conversation
…amping The web shell hard-caps content at --app-max-w (640px), but the plan grid flipped to three columns at sm: (640px) — so the Free/Yearly/Monthly cards, each carrying its own CTA, were crushed into ~190px: CTAs wrapped to two lines, prices jammed against /mês, and the monthly card's feature list wrapped brutally. Three pricing cards physically cannot breathe inside a 640px column. Gate the 3-column mode (grid, card order, hero lift, loading skeleton) behind lg: so inside the shell the cards stack full-width — CTAs on one line, prices and feature lists with room — and the side-by-side comparison returns once a wider desktop canvas exists. Mobile is unaffected: it uses a separate stacked radio-card layout that is already full-width and roomy. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
There was a problem hiding this comment.
Code Review: PR #343
Scope: PR #343 — fix(upgrade): stack paywall cards on narrow shell to stop cramping
Recommendation: APPROVE
Summary
Three web-only components (plan-card.tsx, plan-selection.tsx, pricing-section.tsx) are updated to swap sm:grid-cols-3 / sm:order-* / sm:-translate-y-3 to their lg: equivalents, preventing the three plan cards from cramping at the 640px shell maximum width. The change is a pure Tailwind breakpoint promotion with no logic, state, or type impact. All three files are consistent; the mobile platform is correctly unaffected.
Findings
Critical
None
High
None
Medium
None
Low / Info
None
Subagents
| Agent | Verdict |
|---|---|
| parity-checker | PAIRED — mobile already uses a full-width stacked View layout; breakpoint-responsive grid is a web-platform adapter. No mirror change required. |
| i18n-syncer | IN SYNC — no user-facing strings or i18n keys were added, removed, or altered. |
| contract-aligner | N/A — no packages/shared types or endpoint constants changed. |
| security-reviewer | N/A — no orbit-api code changed. |
Validation
| Check | Result | Details |
|---|---|---|
| Lint | N/A | Permission restrictions blocked automated re-run; PR author confirmed clean. |
| Type check | N/A | Permission restrictions blocked automated re-run; PR author confirmed clean. |
| Tests | N/A | Permission restrictions blocked automated re-run; PR author confirmed 9/9 pass. Tests assert on text/role/testid, not class names — structurally immune to the breakpoint rename. |
| Build (api) | N/A | orbit-api not touched. |
Deferred — N/A dimensions
- Dead/stale code (#2): N/A — diff removes no exports; all changed symbols still referenced.
- SOLID/clean-arch (#3): N/A — no structural or architectural change; functions untouched.
- Comment policy (#4): N/A — no comments added or changed in the diff.
- No-workaround (#5): N/A — this is a layout root-cause fix at the correct layer (Tailwind breakpoints), not a workaround.
- Type safety (#6): N/A — no TypeScript types changed.
- No console.log (#7): N/A — no logging added.
- Contract drift + backward-compat (#11): N/A — no shared types or DTO fields changed.
- Security (#12): N/A — no auth, data exposure, or injection surface changed.
- Backend hard rules (#13): N/A — orbit-api not touched.
What's good
- Root-cause fix at the correct layer: breakpoint promotion is exactly the right lever — no workarounds, no special-case CSS, no JS.
- All three grid/order/translate-y sites updated atomically:
plan-selection.tsx,pricing-section.tsx(loading skeleton grid), andplan-card.tsx(card order + hero elevation) all flip together so the layout cannot partially regress. anchorcard order cleanup: removing the redundantsm:order-3(which is already the default stacking position) is a clean simplification.- Tests are breakpoint-agnostic: assertions target text, roles, and
data-testid— structurally immune to CSS class changes and correctly describe behavior, not implementation. - Mobile parity reasoning is sound: mobile uses a React Native
Viewstack with no equivalent responsive grid to update.
Recommendation
Clean, minimal, correct fix. All three mutation sites are consistent, no logic is touched, parity and i18n are confirmed clear. Approve and merge.
|



Problem
On the upgrade paywall, the three plan cards (Free / Yearly / Monthly) were crushed side-by-side and looked too tight — CTAs wrapped to two lines (
Continuar grátis,Assinar Pro), prices jammed against/mês, and the monthly card's feature list wrapped badly.Root cause
The web shell hard-caps content at
--app-max-w: 640px(globals.css/app/(app)/layout.tsx), butPlanSelectionflipped to 3 columns atsm:(640px) — exactly the cap. Three cards, each with their own CTA, end up at ~190px wide. Three pricing cards physically cannot breathe inside a 640px column.Fix
Gate the entire 3-column mode behind
lg:(grid, cardorder, hero-translate-y, and the loading skeleton — all flipped together so nothing reorders without the grid). Inside the 640px shell the cards now stack full-width: CTAs sit on one line, prices and the monthly feature list have room. The side-by-side 3-column comparison returns automatically once a wider desktop canvas exists (the parked desktop redesign).Parity
Mobile is unaffected — it uses a separate stacked radio-card paywall (
apps/mobile/.../plan-selection.tsx) that is already full-width and roomy. No mobile change needed; no new APK required.Tests
apps/web/__tests__/components/upgrade/*— 9/9 pass (assert text/role/testid, not grid classes).🤖 Generated with Claude Code