Skip to content

fix: pricing and advisory body text is legible again (1.08:1 to 7.57:1) - #61

Merged
sweetmantech merged 1 commit into
mainfrom
fix/1909-pricing-contrast
Jul 29, 2026
Merged

fix: pricing and advisory body text is legible again (1.08:1 to 7.57:1)#61
sweetmantech merged 1 commit into
mainfrom
fix/1909-pricing-contrast

Conversation

@sweetmantech

@sweetmantech sweetmantech commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Fixes recoupable/chat#1909. Most of the body text on /pricing was invisible: it rendered at 1.08:1 contrast in dark and 1.07:1 in light, against a WCAG AA floor of 4.5:1. /pricing is the second most visited page on the site and it is the page meant to sell the product.

Root cause

--muted is a surface token, not a text token, and four pages used it as a text color.

token light dark intended use
--muted #f7f7f7 #151515 backgrounds / surfaces
--muted-foreground #6b6b6b #a0a0a0 secondary text

app/pricing/page.tsx picked the text color with a highlighted-card ternary whose non-highlighted branch reached for the surface token, so only the middle Pro card (which hardcodes text-white/70 and friends) was readable. Headings and prices use --foreground, which is why the page looked superficially fine: "Plus", "$19", "Partner" were legible and nothing else was.

Change

All 24 occurrences of text-[var(--muted)] swapped to text-[var(--muted-foreground)], the token already used correctly in 65 other places:

file occurrences
app/pricing/page.tsx 10
app/advisory/page.tsx 10
app/advisory/book/BookingForm.tsx 3
app/advisory/book/page.tsx 1

/advisory and /advisory/book had the identical defect, so they are fixed in the same pass. No change to --muted itself, which stays a surface token and is still used as one in 3 places.

Measured result

Theme Before After AA (4.5:1) AAA (7:1)
dark (#0a0a0a card) #151515 at 1.08:1 #a0a0a0 at 7.57:1 pass pass
light (#ffffff card) #f7f7f7 at 1.07:1 #6b6b6b at 5.33:1 pass n/a

Regression guard

lib/__tests__/mutedTokenUsage.test.ts walks app/, components/ and lib/ and fails if the surface token appears inside a text-* utility, naming the offending files. It runs under the existing pnpm test, so no CI config was needed.

Proven to work in both directions rather than just asserted: with the token fix stashed the guard fails and lists all four files; with it applied the guard passes. It also skips __tests__ so it does not match the literal in its own source (it flagged itself on the first run).

Verification

  • Full suite 28/28 pass (1 new test).
  • pnpm build compiles, all pages prerendered.
  • Contrast ratios computed from the token values with the WCAG relative-luminance formula, not eyeballed.
  • Preview screenshots of both themes to follow in a comment, per the house rule for frontend PRs.

Branch was cut from main after #60 merged, so app/pricing/page.tsx includes the card-on-file CTA.

Note for the other open pricing PRs: #57 and #58 both touch app/pricing/page.tsx and will need a rebase on top of this.

🤖 Generated with Claude Code


Summary by cubic

Restore readable body text on /pricing and /advisory by using the correct text token. Contrast improves from ~1.08:1 to 7.57:1 (dark) and 5.33:1 (light), meeting WCAG AA/AAA.

  • Bug Fixes
    • Replaced 24 uses of text-[var(--muted)] with text-[var(--muted-foreground)] across pricing and advisory pages (including booking); no token value changes.
    • Added lib/__tests__/mutedTokenUsage.test.ts to fail if --muted is used as a text color; runs via pnpm test.

Written for commit 5316ed8. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • Style

    • Improved text contrast and readability across advisory booking, advisory, and pricing pages by standardizing muted text colors.
  • Tests

    • Added automated coverage to prevent outdated muted text styling from being reintroduced.

--muted is a surface color (#f7f7f7 light, #151515 dark), but four pages used
it as a text color, rendering text the same shade as the card behind it. On
/pricing that measured 1.08:1 in dark and 1.07:1 in light against a WCAG AA
floor of 4.5:1, so every feature bullet and description on the Plus and Partner
cards was invisible in both themes.

Swap all 24 occurrences to --muted-foreground, the token that already covers
secondary text in 65 other places: 7.57:1 dark (AA and AAA) and 5.33:1 light.
Add a test that fails if the surface token is used as a text color again.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@cursor

cursor Bot commented Jul 29, 2026

Copy link
Copy Markdown

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

@vercel

vercel Bot commented Jul 29, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
marketing Ready Ready Preview Jul 29, 2026 11:47pm

Request Review

@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 444a8081-bf8d-48ae-8b21-28f5376e4d9b

📥 Commits

Reviewing files that changed from the base of the PR and between daec044 and 5316ed8.

📒 Files selected for processing (5)
  • app/advisory/book/BookingForm.tsx
  • app/advisory/book/page.tsx
  • app/advisory/page.tsx
  • app/pricing/page.tsx
  • lib/__tests__/mutedTokenUsage.test.ts

📝 Walkthrough

Walkthrough

Updated advisory and pricing page text styles from var(--muted) to var(--muted-foreground). Added a Vitest scan covering app, components, and lib to prevent reintroduction of the deprecated text token.

Changes

Muted foreground token migration

Layer / File(s) Summary
Advisory text token updates
app/advisory/page.tsx, app/advisory/book/*
Advisory content, booking states, package descriptions, and disclaimers now use var(--muted-foreground).
Pricing text token updates
app/pricing/page.tsx
Plan cards, hero content, FAQ elements, and the CTA now use var(--muted-foreground) for non-highlighted text.
Muted token regression test
lib/__tests__/mutedTokenUsage.test.ts
Scans TypeScript source files and fails when text-[var(--muted)] is present.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related issues

  • Issue 1909: The changes implement its proposed pricing and advisory token replacement and add the requested regression check.
🚥 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 fix for pricing and advisory text contrast with the relevant impact.
Linked Issues check ✅ Passed The PR updates the affected pricing and advisory text colors to the correct token and adds a regression test against reusing the muted text utility.
Out of Scope Changes check ✅ Passed The changes are focused on the contrast fix and regression coverage, with no unrelated functional or structural additions.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/1909-pricing-contrast

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.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

No issues found across 5 files

Re-trigger cubic

@sweetmantech

Copy link
Copy Markdown
Contributor Author

Preview verification

Preview: https://marketing-a3xrvorqt-recoup.vercel.app, confirmed built from head 5316ed87.
Tested 2026-07-29, Chrome 1440x1000.

Every text element was measured against its own resolved background, in both themes, on both affected pages. The pass criterion is the one the bug violated: no element below the WCAG AA 4.5:1 floor.

Page Theme Elements measured Below AA Worst element Median
/pricing dark 58 0 5.35:1 ("For managers & small teams") 7.57:1
/pricing light 58 0 5.33:1 5.33:1
/advisory dark 73 0 7.31:1
/advisory light 73 0 5.33:1 ("AI Advisory")

262 element measurements, zero failures. Before this PR the same /pricing measurement returned 1.08:1 in dark and 1.07:1 in light for every body element on the Plus and Partner cards.

A measurement caveat worth recording

My first pass reported the Pro card as failing at 1.05:1 across 12 elements. That was wrong, and it is the same artifact that appeared in the original audit: the Pro card uses text-white/90-style classes, which Tailwind v4 emits as oklab(...), and a naive regex that grabs the first three numbers out of an oklab() string reads them as RGB and produces a garbage ratio. The "Most popular" badge was similarly misread because its own white pill background was not resolved.

Re-measured by painting each color onto a 1x1 canvas and reading the pixel back, which resolves oklab(), color-mix() and named colors to true sRGB, then compositing foreground alpha over the resolved background. That is where the numbers above come from. Anyone re-running this check should use that method rather than parsing the computed color string.

Screenshots

1. /pricing dark — all three cards legible

Pricing dark theme after the fix

2. /pricing light — same, in the other theme

Pricing light theme after the fix

Compare against the pre-fix state, where only the middle Pro card was readable: https://raw.githubusercontent.com/recoupable/marketing/assets/pr56/shots/1-pricing-pro-card.png

Regression guard

lib/__tests__/mutedTokenUsage.test.ts passes on this branch and was verified to fail in the other direction: with the token fix stashed it reports all four offending files by path. It also initially matched its own source and now skips __tests__.

Suite 28/28, pnpm build clean, all pages prerendered.

Fixes recoupable/chat#1909. Part of recoupable/chat#1902.

@sweetmantech
sweetmantech merged commit fec197d into main Jul 29, 2026
3 checks passed
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.

P0: pricing page body text is invisible (1.08:1 contrast) in both themes

1 participant