Skip to content

polish(team): semantic h1, portfolio context, tablist keyboard nav#19

Open
f1shyfang wants to merge 1 commit into
mainfrom
polish/team
Open

polish(team): semantic h1, portfolio context, tablist keyboard nav#19
f1shyfang wants to merge 1 commit into
mainfrom
polish/team

Conversation

@f1shyfang
Copy link
Copy Markdown
Owner

Summary

Polish pass on /team plus the shared MemberCard and FilterButton it uses. Critique score moved from 18/40 → 33/40 (largest individual-page jump in the series).

Page (team/page.tsx, TeamClient.tsx)

  • Added the missing <h1> ("The students running CEUS this year") with eyebrow + subtitle. The page used to open straight into filter chips with no title or context.
  • Each active portfolio now has its own <h2> + a short student-voice blurb (CATEGORY_BLURBS dictionary) + member-count line. The page used to look like a directory dump.
  • Empty state rebuilt as bordered card + brand voice ("This portfolio is recruiting.") + Brand Navy contact CTA. Separate top-level state for "committee still being finalised."
  • Filter row wrapped in <section aria-label="Filter by portfolio"> + <div role="tablist"> with full WAI-ARIA arrow-key + Home/End navigation (automatic activation; roving tabindex on FilterButton children).
  • GSAP entrance now wrapped in prefers-reduced-motion check via window.matchMedia, using gsap.context() for proper cleanup.
  • Container background switched from bg-gray-100 to bg-white to match other polished pages.

MemberCard (shared)

  • focus-visible: everywhere (was focus:).
  • motion-safe:hover:scale-125 — magnitude preserved per the "warm-hearted chapter" design spec, just guarded for reduced-motion users.
  • Explicit transition-[transform,box-shadow,colors] (was transition-all).
  • Richer alt text: "{name}, {role}" for screen readers.
  • Hairline border, Ink Strong heading color.
  • Non-clickable variant (no linkedInUrl): now drops interactive hover affordances (scale, shadow lift, focus ring, name color shift) so a static card doesn't look interactive.

FilterButton (shared)

  • Active state to Brand Navy (was bg-blue-500).
  • role="tab" + aria-selected={isActive} + tabIndex={isActive ? 0 : -1} for tablist children.
  • Hairline border, focus-visible ring.
  • Fixed file-header copy-paste artifact (was labelled EventFilterButton).

Test plan

  • npm run dev/team — verify hero eyebrow + h1 + subtitle appear above the filter row
  • Click through portfolios; verify each section shows its blurb + member count
  • Keyboard: Tab into the filter row, then use ArrowLeft / ArrowRight / Home / End to navigate (focus AND select)
  • Set prefers-reduced-motion: reduce; verify the GSAP card entrance is static
  • On a portfolio with no linkedInUrl on a member (or all members), verify the card doesn't scale or color-shift on hover
  • On an empty portfolio (or empty global dataset), verify the appropriate dignified state appears

Verification

  • tsc --noEmit: clean
  • eslint: clean

Merge note

Independently mergeable. No file overlap with other polish PRs.

🤖 Generated with Claude Code

… states

Page opened straight into filter chips with no title or context.
Added an eyebrow + semantic h1 ("The students running CEUS this year")
+ subtitle, then per-portfolio h2 with a short student-voice blurb
and member-count line. Portfolio blurbs sit in a small dictionary so
they're easy to revise without touching layout.

GSAP entrance now wrapped in window.matchMedia('(prefers-reduced-motion: reduce)')
check and uses gsap.context() for proper cleanup.

Empty state replaced from a centered <p> with no CTA to the bordered-
card pattern used elsewhere: brand-voice copy ("This portfolio is
recruiting.") + Brand Navy contact CTA. Separate top-level state for
"committee still being finalised" when the global dataset is empty.

Container background switched from bg-gray-100 to bg-white to match
the polished home and events pages. Filter row gains
section[aria-label="Filter by portfolio"] + div[role="tablist"], with
full arrow-key + Home/End navigation (automatic activation; roving
tabindex on FilterButton children).

FilterButton (shared): active state to Brand Navy (was bg-blue-500),
role="tab" + aria-selected + tabIndex={isActive ? 0 : -1} for tablist
children, hairline border, focus-visible ring, fixed file-header
copy-paste artifact (was labelled EventFilterButton).

MemberCard (shared): focus-visible everywhere (was focus:),
motion-safe on hover:scale-125 (magnitude preserved per design spec),
explicit transition-[transform,box-shadow,colors], richer alt text
including role, hairline border, Ink Strong heading color. Non-clickable
variant (no LinkedIn URL) now drops interactive hover affordances
(scale, shadow lift, focus ring, name color shift) so it doesn't
mis-signal interactivity.

tsc --noEmit: clean. eslint: clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 29, 2026 11:28
@vercel
Copy link
Copy Markdown

vercel Bot commented May 29, 2026

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

Project Deployment Actions Updated (UTC)
ceus-website Ready Ready Preview, Comment May 29, 2026 11:29am

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Polish pass on the /team page and its shared UI components to improve semantic structure, accessibility (notably keyboard navigation), and overall page context/empty-state presentation.

Changes:

  • Added a semantic page intro (eyebrow + <h1> + subtitle) and richer per-portfolio context (blurb + member count + improved empty states).
  • Implemented tablist-style portfolio filters with roving tabindex and arrow/Home/End keyboard navigation.
  • Updated shared MemberCard and FilterButton styling/behavior (reduced-motion guards, focus-visible rings, brand color alignment, non-interactive card variant).

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 9 comments.

File Description
CEUS/src/app/team/TeamClient.tsx Adds hero/title context, tablist keyboard navigation, GSAP reduced-motion guard, and improved empty states for team portfolios.
CEUS/src/components/FilterButton.tsx Updates filter button styling and adds tab semantics (role/tabIndex/aria-selected).
CEUS/src/components/MemberCard.tsx Refines focus/hover behavior, reduced-motion guards, alt text, and non-clickable variant styling.
CEUS/src/app/team/page.tsx Updates /team metadata title/description for clearer SEO/context.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

'bg-white p-5 md:p-6 rounded-xl border border-gray-200 shadow-lg flex flex-col items-center text-center member-card-gsap';

const interactiveClasses =
'group cursor-pointer transition-[transform,box-shadow,colors] duration-300 ease-out ' +
className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-6 lg:gap-8"
>
{displayedMembers.map((member) => (
<div key={member.id} className="member-card-gsap">
Comment on lines +14 to +16
role="tab"
aria-selected={isActive}
tabIndex={isActive ? 0 : -1}
Comment on lines +171 to +174
<section
aria-labelledby="active-portfolio-heading"
className="container mx-auto px-4 sm:px-6 lg:px-8 pb-20 md:pb-28"
>
'bg-white p-5 md:p-6 rounded-xl border border-gray-200 shadow-lg flex flex-col items-center text-center member-card-gsap';

const interactiveClasses =
'group cursor-pointer transition-[transform,box-shadow,colors] duration-300 ease-out ' +
className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-6 lg:gap-8"
>
{displayedMembers.map((member) => (
<div key={member.id} className="member-card-gsap">
Comment on lines +14 to +16
role="tab"
aria-selected={isActive}
tabIndex={isActive ? 0 : -1}
Comment on lines +171 to +174
<section
aria-labelledby="active-portfolio-heading"
className="container mx-auto px-4 sm:px-6 lg:px-8 pb-20 md:pb-28"
>
Comment on lines +175 to +179
{!hasAnyMembers ? (
<div className="mx-auto max-w-2xl rounded-xl border border-gray-200 bg-gray-50 px-8 py-10 md:px-12 md:py-14 text-center shadow-sm">
<p className="text-lg font-semibold text-gray-900 mb-2">
The 2026 committee is still being finalised.
</p>
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.

2 participants