Skip to content

[refactor] Consolidate hand-rolled eyebrow labels into <Eyebrow>#2815

Merged
joshestein merged 7 commits into
masterfrom
josh/2795-consolidate-eyebrow
Jul 24, 2026
Merged

[refactor] Consolidate hand-rolled eyebrow labels into <Eyebrow>#2815
joshestein merged 7 commits into
masterfrom
josh/2795-consolidate-eyebrow

Conversation

@joshestein

@joshestein joshestein commented Jul 21, 2026

Copy link
Copy Markdown
Collaborator

Closes #2795.

What changed

The "uppercase + small + letter-spaced" label recipe was hand-rolled at ~26 call sites with drifting tracking (0.05–0.18em), weight (medium/semibold), and size (12–16px). This PR:

  • Converts all of them to <Eyebrow> from @bluedot/ui, snapped to navy/40 and /50 → /60; text-white opacity-70text-white/70; rapid-grants text-bluedot-dark → default blue.
  • Eyebrow gains id + style pass-through: id for the aria-labelledby target in MyBlueDotSidebar; style for runtime colours (HeroSection category labels, Congratulations course accent, puzzles prize accent) that Tailwind classes can't express (arbitrary-value classes must be build-time literals).
  • Heading-role eyebrows (/puzzles/technical-ai-safety + /submitted section h2s, /courses "Projects" h2, CourseSchedule sidebar H2s) keep their h2 semantics — each is its section's only heading, and Eyebrow renders a <p> — but their inline classes are snapped to the same voice, so they're visually identical to the component.
  • Storybook: added Muted and DynamicAccent stories.

Deliberately not converted

  • Badges/chips (calendar-tile month labels, lander tag chips, program-page pills), Nav "NEW" tags, AnnouncementBanner — non-label uppercase, out of scope per the issue.
  • RoundGroup's inline lead-in label (mid-sentence span; a <p> would break the flow).

Screenshots (after)

Puzzles page — section headings + prize labels now one voice:

Rapid grants — step labels and stats on the sanctioned palette:

Earlier round (pre designer-voice commit, layout unchanged): homepage, lander, alumni:


Testing

  • Website suite: 1004/1004 (11 snapshots updated across the two commits; diffs are exactly the class swaps + one span→p)
  • Website typecheck + lint clean; libraries/ui typecheck failures are pre-existing (Footer/IconButton stories, Login.test — none touch Eyebrow)
  • Viewport check at 390px and 1440px on the five affected public pages: no horizontal scroll, no layout breakage
  • Auth-gated surfaces (course unit page, my-courses, congratulations, facilitator-feedback) covered by snapshots only — worth a staging click-through

🤖 Generated with Claude Code

@coderabbitai

coderabbitai Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@joshestein, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 51 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: f27e8db5-ffb8-4c6c-8592-1046d5499afe

📥 Commits

Reviewing files that changed from the base of the PR and between 896ba52 and 5a6457f.

⛔ Files ignored due to path filters (1)
  • apps/website/src/components/certificate/__snapshots__/CertificateCTA.test.tsx.snap is excluded by !**/*.snap
📒 Files selected for processing (1)
  • apps/website/src/components/certificate/CertificateCTA.tsx
📝 Walkthrough

Walkthrough

The shared Eyebrow component now accepts optional id and style props, with additional Storybook examples. Website components and pages replace several manually styled paragraph or span labels with Eyebrow, preserving content and selected colour, spacing, and alignment classes. Course and puzzle page typography classes are also updated, including winner labels and section headings.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The PR standardises eyebrow labels with Eyebrow, adds the needed pass-throughs and stories, and keeps non-label uppercase text out of scope.
Out of Scope Changes check ✅ Passed The changed files all support Eyebrow consolidation, visual parity, or related docs/stories, with no unrelated scope creep evident.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Title check ✅ Passed The title is concise and accurately reflects the main change of consolidating eyebrow labels into the shared component.
Description check ✅ Passed It covers motivation, issue linkage, exclusions, screenshots, and testing; only the checklist and screenshot table format are omitted.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch josh/2795-consolidate-eyebrow

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.

@joshestein joshestein changed the title [refactor] Consolidate hand-rolled eyebrow labels into <Eyebrow> (#2795) [refactor] Consolidate hand-rolled eyebrow labels into <Eyebrow> Jul 21, 2026
@joshestein
joshestein changed the base branch from master to josh/2753-sweep-raw-components July 21, 2026 13:06
@joshestein

Copy link
Copy Markdown
Collaborator Author

@greptile-apps review

@greptile-apps

greptile-apps Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR consolidates ~26 hand-rolled "uppercase + small + letter-spaced" label patterns across the website into the canonical <Eyebrow> component from @bluedot/ui, with designer sign-off on the voice (12 px / font-medium / tracking-wide 0.04 em). The component receives two new prop pass-throughs — id (for aria-labelledby in MyBlueDotSidebar) and style (for runtime colour values that cannot be expressed as Tailwind literals).

  • Eyebrow component: gains id and style prop pass-through; two new Storybook stories (Muted, DynamicAccent) document the sanctioned colour variants.
  • 26 call sites across certificate, course, lander, grants, alumni, puzzle, and my-courses surfaces are converted; drifting tracking (0.05–0.18 em), weight (semiboldmedium), and size (12–16 px) are collapsed to the canonical spec.
  • Heading-role eyebrows (CourseSchedule, courses/index.tsx, puzzle section h2s) keep their h2 semantics but are snapped to the same visual voice inline.

Confidence Score: 5/5

Safe to merge — pure visual refactoring with designer sign-off, no behavioural or data-flow changes.

All changes are CSS class consolidations onto an already-stable shared component. The id and style prop additions are minimal and correctly wired at every call site. Snapshot suite updated cleanly across all affected surfaces, and the aria-labelledby reference in MyBlueDotSidebar is correctly preserved via the new id prop.

No files require special attention. The auth-gated surfaces (course unit page, congratulations, facilitator-feedback) are covered by snapshot tests only; a staging click-through is noted in the PR as a recommended follow-up.

Important Files Changed

Filename Overview
libraries/ui/src/Eyebrow.tsx Adds id and style prop pass-throughs; both are straightforward and used correctly at call sites.
libraries/ui/src/Eyebrow.stories.tsx Adds Muted and DynamicAccent stories covering the two new usage patterns; well-commented.
apps/website/src/components/my-bluedot/MyBlueDotSidebar.tsx Uses new id prop to preserve aria-labelledby reference on the nav landmark; semantics are correct.
apps/website/src/components/lander/components/HeroSection.tsx Four duplicated category-label <p> blocks replaced by <Eyebrow style={...}> using the new style pass-through; inline style correctly overrides the default text-bluedot-normal class.
apps/website/src/components/courses/Congratulations.tsx Three eyebrow patterns converted; navy/40navy/60 and tracking-[0.06em] → canonical tracking, all sanctioned per PR description.
apps/website/src/components/certificate/CertificateCard.tsx Four eyebrow instances converted; responsive sizing (text-size-xs md:text-size-sm) collapsed to canonical text-size-xxs — intentional per designer sign-off.
apps/website/src/components/rapid-grants/HowItWorksSection.tsx eyebrowClass prop made optional; text-bluedot-dark removed from linked/community card call sites (defaults to canonical blue), navy/40navy/60 on inactive cards.
apps/website/src/pages/puzzles/technical-ai-safety.tsx EYEBROW_CLASS constant snapped to canonical voice; prize-label <p> blocks with tracking-[0.18em] replaced by <Eyebrow style={...}> for the dynamic first-prize accent colour.
apps/website/src/components/courses/CourseSchedule.tsx Heading-role h2 eyebrow gets font-medium added to match canonical voice; kept as h2 for correct document outline.
apps/website/src/pages/facilitator-feedback/[meetPersonId].tsx Eyebrow already imported; three section-label <p> elements with tracking-wider navy/50 replaced by <Eyebrow className="text-bluedot-navy/60">.
apps/website/src/components/courses/UnitLayout.tsx Course title eyebrow converted from P with manual classes to <Eyebrow className="mb-2">; class snapshot confirms correct output.
apps/website/src/components/homepage/NewsletterBanner.tsx <span><Eyebrow className="text-white/70">; element type change from inline to block is safe inside the flex-col container.
apps/website/src/components/homepage/EventsSection.tsx Location label converted to default <Eyebrow>; no colour override needed as it was already text-bluedot-normal.
apps/website/src/components/homepage/MergedLadder.tsx P with manual tracking replaced by <Eyebrow className="text-bluedot-navy/60">; change is consistent with other muted eyebrow usages.
apps/website/src/components/alumni/RecentAlumniList.tsx "More stories" label converted; font-semibold tracking-[0.14em] collapsed to canonical voice.
apps/website/src/pages/puzzles/technical-ai-safety/submitted.tsx EYEBROW_CLASS constant updated to match canonical voice; no component usage change in this file.
apps/website/src/components/lander/components/PersonasSection.tsx "What this looks like" labels converted from navy/40 semibold tracking-[0.08em] to <Eyebrow className="text-bluedot-navy/60">; colour and weight normalised.
apps/website/src/components/grants/sections/GrantStatsStrip.tsx Stat label extracted from inline class string to <Eyebrow className="text-bluedot-navy/60">; labelClass variable removed.
apps/website/src/components/my-courses/CourseListRow.tsx Facilitator-mode course title eyebrow converted; font-semiboldfont-medium and tracking normalised.
apps/website/src/components/certificate/CertificateCTA.tsx Cohort date label converted; text-size-xs tracking-[0.24px] leading-relaxed collapsed to canonical text-size-xxs tracking-wide per designer voice.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[Hand-rolled eyebrow pattern
text-size-xs/xxs · font-semibold · tracking-0.05-0.18em] -->|replaced by| B[Eyebrow component
text-size-xxs · font-medium · tracking-wide 0.04em]

    B --> C{Colour source}
    C -->|static Tailwind class| D[className prop
e.g. text-bluedot-navy/60
text-white/70 · text-charcoal-mid]
    C -->|runtime value| E[style prop
e.g. courseColors.accent
TAS.full]
    C -->|default| F[text-bluedot-normal
canonical brand blue]

    G[Heading-role eyebrows
CourseSchedule · courses/index · puzzles h2s] -->|keep h2 semantics| H[inline classes snapped
font-semibold → font-medium
tracking aligned]

    B --> I[id prop
aria-labelledby target
MyBlueDotSidebar nav]
Loading

Reviews (3): Last reviewed commit: "[chore] Remove PR screenshots after embe..." | Re-trigger Greptile

Base automatically changed from josh/2753-sweep-raw-components to master July 23, 2026 11:10
joshestein and others added 5 commits July 24, 2026 10:38
Converts 26 uppercase label call sites across 16 files to the Eyebrow
component, snapping size/weight/tracking to its defaults (text-size-xxs
/ font-medium / tracking-wide). Colours stay as per-site className or
style overrides. Eyebrow gains id + style pass-through for the
aria-labelledby target in MyBlueDotSidebar and the runtime course/accent
colours in HeroSection, Congratulations, and the puzzles page.

CertificateCard keeps its explicit sizes (the certificate is a
fixed-proportion artifact). Heading-role eyebrows (puzzles h2s,
/courses Projects, CourseSchedule sidebar), badges/pills, and
AnnouncementBanner are deliberately not converted.

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

Designer sign-off: 12px only, font-medium, tracking-wide, colours limited
to brand blue / navy-60 / charcoal (certificates) / white-70 / dynamic
accent. Collapses the navy/40-50 scatter to navy/60, normalises the
newsletter label to text-white/70, drops CertificateCard's size and
leading exceptions, and snaps the heading-role eyebrows (puzzles pages,
/courses Projects, CourseSchedule sidebar) to the same voice inline
while keeping their h2 semantics.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@joshestein
joshestein force-pushed the josh/2795-consolidate-eyebrow branch from fd5e470 to 0d7e580 Compare July 24, 2026 08:46
@Will-Howard
Will-Howard temporarily deployed to josh/2795-consolidate-eyebrow - bluedot-preview PR #2815 July 24, 2026 08:46 — with Render Destroyed
@joshestein
joshestein marked this pull request as ready for review July 24, 2026 08:46
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@joshestein

Copy link
Copy Markdown
Collaborator Author

@greptile-apps review

@joshestein

Copy link
Copy Markdown
Collaborator Author

@claude review

@claude

claude Bot commented Jul 24, 2026

Copy link
Copy Markdown

Claude encountered an error —— View job


I'll analyze this and get back to you.

@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.

🧹 Nitpick comments (1)
apps/website/src/components/certificate/CertificateCTA.tsx (1)

42-44: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Pass the dynamic accent through style instead of an arbitrary colour utility.

text-[var(--cta-accent)] is an arbitrary colour class. Use style={{ color: accentColor }} here now that Eyebrow explicitly forwards style; the CSS variable can remain for the CTA button and background.

As per coding guidelines, website TSX should use named BlueDot colour utilities rather than arbitrary colour classes.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/website/src/components/certificate/CertificateCTA.tsx` around lines 42 -
44, Update the Eyebrow rendering in CertificateCTA to remove the arbitrary
text-[var(--cta-accent)] utility and pass the dynamic accent through style={{
color: accentColor }} instead. Keep the CSS variable available for the CTA
button and background, and preserve the existing Eyebrow styling otherwise.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@apps/website/src/components/certificate/CertificateCTA.tsx`:
- Around line 42-44: Update the Eyebrow rendering in CertificateCTA to remove
the arbitrary text-[var(--cta-accent)] utility and pass the dynamic accent
through style={{ color: accentColor }} instead. Keep the CSS variable available
for the CTA button and background, and preserve the existing Eyebrow styling
otherwise.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 9a79bbd8-3578-46ca-9e43-2511da12fd4b

📥 Commits

Reviewing files that changed from the base of the PR and between a166207 and 896ba52.

⛔ Files ignored due to path filters (8)
  • apps/website/src/__tests__/pages/__snapshots__/account.test.tsx.snap is excluded by !**/*.snap
  • apps/website/src/__tests__/pages/__snapshots__/join-us.test.tsx.snap is excluded by !**/*.snap
  • apps/website/src/components/certificate/__snapshots__/CertificateCTA.test.tsx.snap is excluded by !**/*.snap
  • apps/website/src/components/certificate/__snapshots__/CertificateCard.test.tsx.snap is excluded by !**/*.snap
  • apps/website/src/components/courses/__snapshots__/UnitLayout.test.tsx.snap is excluded by !**/*.snap
  • apps/website/src/components/lander/__snapshots__/AgiStrategyLander.test.tsx.snap is excluded by !**/*.snap
  • apps/website/src/components/lander/components/__snapshots__/HeroSection.test.tsx.snap is excluded by !**/*.snap
  • apps/website/src/components/lander/components/__snapshots__/PersonasSection.test.tsx.snap is excluded by !**/*.snap
📒 Files selected for processing (21)
  • apps/website/src/components/alumni/RecentAlumniList.tsx
  • apps/website/src/components/certificate/CertificateCTA.tsx
  • apps/website/src/components/certificate/CertificateCard.tsx
  • apps/website/src/components/courses/Congratulations.tsx
  • apps/website/src/components/courses/CourseSchedule.tsx
  • apps/website/src/components/courses/UnitLayout.tsx
  • apps/website/src/components/grants/sections/GrantStatsStrip.tsx
  • apps/website/src/components/homepage/EventsSection.tsx
  • apps/website/src/components/homepage/MergedLadder.tsx
  • apps/website/src/components/homepage/NewsletterBanner.tsx
  • apps/website/src/components/lander/components/HeroSection.tsx
  • apps/website/src/components/lander/components/PersonasSection.tsx
  • apps/website/src/components/my-bluedot/MyBlueDotSidebar.tsx
  • apps/website/src/components/my-courses/CourseListRow.tsx
  • apps/website/src/components/rapid-grants/HowItWorksSection.tsx
  • apps/website/src/pages/courses/index.tsx
  • apps/website/src/pages/facilitator-feedback/[meetPersonId].tsx
  • apps/website/src/pages/puzzles/technical-ai-safety.tsx
  • apps/website/src/pages/puzzles/technical-ai-safety/submitted.tsx
  • libraries/ui/src/Eyebrow.stories.tsx
  • libraries/ui/src/Eyebrow.tsx

Matches the other dynamic-accent Eyebrow call sites; the --cta-accent
CSS variable remains for the CTA button background.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@Will-Howard
Will-Howard temporarily deployed to josh/2795-consolidate-eyebrow - bluedot-storybook-preview PR #2815 July 24, 2026 08:55 — with Render Destroyed
@Will-Howard
Will-Howard temporarily deployed to josh/2795-consolidate-eyebrow - bluedot-preview PR #2815 July 24, 2026 08:55 — with Render Destroyed
@joshestein
joshestein merged commit a7c91e4 into master Jul 24, 2026
7 checks passed
@joshestein
joshestein deleted the josh/2795-consolidate-eyebrow branch July 24, 2026 09:00
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.

Consolidate ad-hoc eyebrow/label recipes into <Eyebrow>

2 participants