Skip to content

Feat/design refresh#9

Merged
keithce merged 4 commits into
mainfrom
feat/design-refresh
Apr 19, 2026
Merged

Feat/design refresh#9
keithce merged 4 commits into
mainfrom
feat/design-refresh

Conversation

@keithce

@keithce keithce commented Apr 18, 2026

Copy link
Copy Markdown
Collaborator

Summary by CodeRabbit

  • New Features

    • New decorative and interactive visuals (backdrop, spectrum, waveform) and an updated page header/navigation.
  • Style

    • Design refresh: new color system, typography, selection/glow styles, utilities, and revised animations with reduced-motion support.
    • Updated profile, footer, and social links visuals and interactions.
  • Refactor

    • Main layout reworked to a responsive two-column grid; project listing modularized into reusable cards and accordion offsets.
  • Documentation

    • Added repository guidance and development instructions.

Reshape the link-tree into a "personal platform" inspired by render.com:
dark plum-tinted canvas, asymmetric identity rail + deployment stream,
editorial Instrument Serif display type paired with Geist UI and mono
metadata.

Introduce audio-engineering accents that reinforce Keith's dual identity
(dev × audio engineer) without fake status indicators: interactive
spectrum bars that respond to cursor position like a real EQ, a 432 Hz
tuning tag, and a waveform footer divider. Palette locked to the brand
plum scale; sharp-cornered cards match the render.com aesthetic.

Hover interactions add subtle sophistication throughout: letter-stagger
on the display name, avatar halo intensify, left accent bar on project
cards, row wash on channel links, and a rotating nav diamond. Dotted
backdrop with radial glow and light SVG grain replaces the flickering
grid.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings April 18, 2026 04:57
@vercel

vercel Bot commented Apr 18, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
rproj-dev-home Ready Ready Preview, Comment, Open in v0 Apr 19, 2026 3:32am

@coderabbitai

coderabbitai Bot commented Apr 18, 2026

Copy link
Copy Markdown
Contributor

Warning

Rate limit exceeded

@keithce has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 3 minutes and 52 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 3 minutes and 52 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 0b4b2426-87af-49d4-a19f-a18cdb430516

📥 Commits

Reviewing files that changed from the base of the PR and between 89defb3 and c4dcaff.

📒 Files selected for processing (9)
  • AGENTS.md
  • app/globals.css
  • app/page.tsx
  • components/dev-greeting.tsx
  • components/profile-header.tsx
  • components/project-card.tsx
  • components/social-links-section.tsx
  • components/ui/spectrum.tsx
  • components/ui/waveform.tsx
📝 Walkthrough

Walkthrough

The PR adds documentation, overhauls global styles (new ink/glow tokens, animation tweaks, utilities), updates layout and metadata, replaces the FlickeringGrid with Backdrop and DevGreeting, introduces new visual components (Backdrop, Spectrum, Waveform, ProjectCard, Waveform), refactors project/social/profile/footer UIs, and removes the canvas-based FlickeringGrid.

Changes

Cohort / File(s) Summary
Documentation & Metadata
AGENTS.md, app/layout.tsx, app/manifest.ts
Add Codex guidance file; import Instrument_Serif font and expose CSS var; update metadata descriptions and remove newsletter wording; root HTML class now includes dark and font variable.
Global Styling System
app/globals.css
Major token overhaul: remove chart/sidebar tokens, add --color-ink-* and --color-glow*; remap semantic tokens in .dark; add --font-display; modify fadeInUp timing, add grain-shift, reduced-motion rule, selection styles, and new visual utility classes (.backdrop-dots, .backdrop-glow, .grain, .hairline, .underline-grow).
Page Layout
app/page.tsx
Switch from centered single-column to responsive two-column grid with sticky profile aside; add top monospaced nav, replace FlickeringGrid with DevGreeting + Backdrop, remove NewsletterSignup, add overflow-x-hidden.
New UI Components
components/dev-greeting.tsx, components/ui/backdrop.tsx, components/ui/spectrum.tsx, components/ui/waveform.tsx
Add DevGreeting (client console greeting), Backdrop (decorative multi-layer viewport overlay), Spectrum (interactive EQ-like bars responding to pointer), and Waveform (SVG gradient path).
Project UI & Refactor
components/project-card.tsx, components/projects-accordion.tsx, components/projects-section.tsx
Introduce ProjectCard component; refactor ProjectsSection to use it; update ProjectsAccordion to accept startIndex?: number, delegate rendering, change trigger labels and animation variants.
Profile / Social / Footer UI
components/profile-header.tsx, components/social-links-section.tsx, components/footer.tsx
ProfileHeader: new hover glow, Spectrum, animated surname spans, avatar fallback update. SocialLinksSection: switch from button grid to indexed list with handles and adjusted animations. Footer: replace text layout with Waveform and compact metadata row.
Component Removal
components/ui/flickering-grid.tsx
Removed entire FlickeringGrid component (canvas-based flicker animation, observers, DPR scaling, and export).

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Poem

🐰 Hopping in to tweak the hue and glow,

Spectrum hums and waveform bends low,
Ink lines ripple, cards now brightly show,
Backdrop gleams while old grids let go. ✨

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'Feat/design refresh' is vague and generic, using the non-descriptive term 'design refresh' that doesn't convey specific information about the changeset despite substantial modifications across multiple components and styles. Consider using a more descriptive title that highlights a key change, such as 'Refactor layout and styling with new design system components' or 'Redesign homepage with two-column layout and updated visual components'.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/design-refresh

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 and usage tips.

Copilot AI 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.

Pull request overview

Design refresh for the link-tree landing page, shifting to a darker “ink/glow” visual system and new interactive/ornamental UI elements while refactoring project and social link presentation.

Changes:

  • Replaced the flickering canvas background with a CSS-based backdrop (glow/dots/grain) and introduced waveform/spectrum accents.
  • Refactored projects UI into a reusable ProjectCard and updated the accordion/section layout and motion timing.
  • Updated page composition (two-column layout, sticky sidebar) and refreshed metadata/manifest copy.

Reviewed changes

Copilot reviewed 16 out of 16 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
components/ui/waveform.tsx Adds a reusable SVG waveform accent used in the footer.
components/ui/spectrum.tsx Adds an interactive spectrum/EQ bar flourish for the profile header.
components/ui/flickering-grid.tsx Removes the previous canvas-based flickering grid background.
components/ui/backdrop.tsx Adds fixed CSS-based backdrop layers (glow/dots/grain + top hairline).
components/social-links-section.tsx Redesigns social links into a divided list with hover treatments and handles.
components/projects-section.tsx Updates section header/layout and swaps inline card markup for ProjectCard.
components/projects-accordion.tsx Updates accordion toggle UI/motion and uses ProjectCard with index offset support.
components/project-card.tsx Introduces a shared project card component used by section + accordion.
components/profile-header.tsx Redesigns header typography and adds the Spectrum accent row.
components/footer.tsx Updates footer styling and adds Waveform accent.
components/dev-greeting.tsx Adds a client-side console greeting/easter egg.
app/page.tsx Rebuilds the page layout (sticky aside, new nav, removes newsletter section).
app/manifest.ts Updates PWA description copy (removes newsletter mention).
app/layout.tsx Adds Instrument Serif font and forces dark mode on the root <html> class.
app/globals.css Adds ink/glow token system, new backdrop utilities, refined motion, selection styling.
AGENTS.md Adds agent guidance / repo overview documentation.

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

Comment thread components/social-links-section.tsx Outdated
Comment on lines +44 to +48
<li key={link.name} className="relative overflow-hidden">
<span
aria-hidden="true"
className="pointer-events-none absolute inset-0 bg-glow/[0.04] translate-x-[-101%] transition-transform duration-500 ease-[cubic-bezier(0.16,1,0.3,1)] group-hover/channel:translate-x-0"
/>

Copilot AI Apr 18, 2026

Copy link

Choose a reason for hiding this comment

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

The hover overlay ( with group-hover/channel:translate-x-0) will never animate because the group/channel class is on the , which is a sibling (not an ancestor) of that . Move group/channel to the

  • (or wrap both elements in a parent with group/channel) so the overlay can respond to the group hover state.

  • Copilot uses AI. Check for mistakes.
    Comment thread components/social-links-section.tsx Outdated
    target={isMail ? undefined : "_blank"}
    rel={isMail ? undefined : "noopener noreferrer"}
    className="group/channel relative flex items-center gap-4 py-3 text-sm text-ink-300 transition-colors hover:text-ink-100"
    style={{ animationDelay: `${index * 60}ms` }}

    Copilot AI Apr 18, 2026

    Copy link

    Choose a reason for hiding this comment

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

    style={{ animationDelay: ... }} is set on the , but there’s no animation applied to that element (no animation-* / animate-* class), so the delay has no effect. Either add the intended animation to this element or remove the unused inline style.

    Suggested change
    style={{ animationDelay: `${index * 60}ms` }}

    Copilot uses AI. Check for mistakes.
    Comment thread components/project-card.tsx Outdated
    return (
    <article
    className="group relative border border-ink-800 bg-ink-950/60 backdrop-blur-sm overflow-hidden transition-colors duration-500 hover:border-ink-700 hover:bg-ink-900/70"
    style={{ animationDelay: `${index * 80}ms` }}

    Copilot AI Apr 18, 2026

    Copy link

    Choose a reason for hiding this comment

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

    animationDelay is set on the

    , but the component doesn’t apply any animation to the element, so this inline style currently has no effect. Consider adding the intended animation class (if staggered entrance is desired) or removing the unused style to reduce dead code.

    Suggested change
    style={{ animationDelay: `${index * 80}ms` }}

    Copilot uses AI. Check for mistakes.
    Comment on lines +29 to +30
    console.log(title, titleStyle);
    console.log(body, bodyStyle);

    Copilot AI Apr 18, 2026

    Copy link

    Choose a reason for hiding this comment

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

    This logs to the browser console in production builds. If this is meant as a dev-only easter egg, consider gating it behind process.env.NODE_ENV !== "production" (or a feature flag) to avoid polluting user consoles and potential automated log capture.

    Copilot uses AI. Check for mistakes.
    Comment on lines +18 to +26
    d="M0 12 L20 12 L24 4 L28 20 L32 8 L36 16 L40 10 L44 14 L48 6 L52 18 L56 11 L60 13 L80 12 L84 10 L88 14 L92 7 L96 17 L100 12 L120 12 L124 5 L128 19 L132 9 L136 15 L140 12 L160 12 L164 11 L168 13 L172 8 L176 16 L180 10 L184 14 L188 12 L208 12 L212 4 L216 20 L220 9 L224 15 L228 12 L248 12 L252 10 L256 14 L260 6 L264 18 L268 12 L288 12 L292 11 L296 13 L300 8 L304 16 L308 12 L328 12 L332 5 L336 19 L340 10 L344 14 L348 12 L368 12 L372 9 L376 15 L380 11 L384 13 L400 12"
    stroke="url(#wave-gradient)"
    strokeWidth="1"
    strokeLinecap="round"
    strokeLinejoin="round"
    />
    <defs>
    <linearGradient id="wave-gradient" x1="0" y1="0" x2="1" y2="0">
    <stop offset="0%" stopColor="currentColor" stopOpacity="0" />

    Copilot AI Apr 18, 2026

    Copy link

    Choose a reason for hiding this comment

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

    linearGradient uses a hard-coded id (wave-gradient). If more than one is ever rendered on the same page, the duplicated ids can cause incorrect gradient references. Consider generating a unique id per component instance (e.g., via useId) and using it in both the id and stroke URL reference.

    Copilot uses AI. Check for mistakes.
    Comment thread AGENTS.md Outdated
    - **Styling**: Tailwind CSS v4 with custom brand color palette
    - **UI Components**: shadcn/ui (new-york style)
    - **Icons**: Phosphor Icons (@phosphor-icons/react)
    - **Fonts**: Geist Sans & Geist Mono

    Copilot AI Apr 18, 2026

    Copy link

    Choose a reason for hiding this comment

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

    AGENTS.md lists only Geist Sans/Mono under Fonts, but the app now also adds Instrument Serif in app/layout.tsx. Update this section so agent guidance reflects the current typography stack.

    Suggested change
    - **Fonts**: Geist Sans & Geist Mono
    - **Fonts**: Geist Sans, Geist Mono, & Instrument Serif

    Copilot uses AI. Check for mistakes.
    Comment thread app/page.tsx
    Comment on lines +1 to +6
    import { DevGreeting } from "@/components/dev-greeting";
    import { Footer } from "@/components/footer";
    import { NewsletterSignup } from "@/components/newsletter-signup";
    import { ProfileHeader } from "@/components/profile-header";
    import { ProjectsSection } from "@/components/projects-section";
    import { SocialLinksSection } from "@/components/social-links-section";
    import { FlickeringGrid } from "@/components/ui/flickering-grid";
    import { Backdrop } from "@/components/ui/backdrop";

    Copilot AI Apr 18, 2026

    Copy link

    Choose a reason for hiding this comment

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

    NewsletterSignup was removed from the page and metadata no longer references the newsletter, but the newsletter UI component and /api/newsletter route still exist in the repo and appear unused. Consider either removing that unused feature (and env var expectations) or reintroducing it in the new layout so the codebase doesn’t accumulate dead code.

    Copilot uses AI. Check for mistakes.

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

    Actionable comments posted: 11

    🤖 Prompt for all review comments with AI agents
    Verify each finding against the current code and only fix it if needed.
    
    Inline comments:
    In `@AGENTS.md`:
    - Around line 19-55: The markdown has lint violations: add a blank line after
    each top-level heading ("Tech Stack", "Project Structure", "Brand Color System",
    "Path Aliases") to satisfy MD022, ensure the fenced code block that lists the
    project tree has an empty blank line before and after it to satisfy MD031, and
    declare the fenced block language as text (change ``` to ```text) to satisfy
    MD040; update the headings and the block around the component tree in AGENTS.md
    accordingly so the linter passes.
    
    In `@app/globals.css`:
    - Around line 203-211: .grain is absolutely positioned but assumes a positioned
    ancestor; update the stylesheet to either document this contract and/or add a
    helper class so callers have a known context. Specifically, add a short comment
    explaining that .grain requires a positioned container, and introduce a
    companion selector (e.g., .grain-host) that sets positioning and clipping
    (position: relative; overflow: hidden; or contain: strict) so .grain will be
    contained — reference .grain and the new .grain-host in the comment and
    docstring so callers know to wrap grain with .grain-host.
    - Around line 132-226: Insert a blank line before the declaration
    font-feature-settings (the rule containing font-feature-settings) to satisfy
    declaration-empty-line-before, and change any occurrences of currentColor to
    lowercase currentcolor in the .underline-grow background-image declaration;
    leave the existing keyframes name fadeInUp and its usage in .animate-fade-in-up
    unchanged (or add a stylelint disable comment if you want to silence
    keyframes-name-pattern).
    
    In `@app/page.tsx`:
    - Around line 15-21: The <nav> element in app/page.tsx currently only contains
    branding and a version label and should not expose a navigation landmark;
    replace the <nav className="..."> wrapper (the element that contains the brand
    span with class "group/nav" and the version span) with a <header> (or a
    non-landmark container such as <div role="presentation">) to avoid misleading
    screen-reader navigation lists, keeping the inner spans and classes unchanged.
    
    In `@components/profile-header.tsx`:
    - Around line 31-48: The screen reader may read the per-letter spans of the
    surname individually; add an aria-label to the h1 (the element with
    className="group/name ...") such as aria-label="Keith Elliott" and mark the
    decorative split-letter span (the span wrapping {ELLIOTT.split(...).map(...)})
    as aria-hidden="true" so the visual animation is preserved but screen readers
    announce the full name once; update the h1 and the inner span accordingly
    (references: ELLIOTT constant, the <h1> element and the decorative inline-flex
    <span>).
    
    In `@components/project-card.tsx`:
    - Around line 15-19: The stagger delay on the article is a no-op because the
    entrance animation class (animate-fade-in-up) is on the parent grid; move the
    animate-fade-in-up class from the parent grids (projects-section.tsx and
    projects-accordion.tsx) onto each card's root element (the <article> in
    project-card.tsx) so the per-index style={{ animationDelay: `${index * 80}ms` }}
    takes effect, and remove the animate-fade-in-up from the parent containers to
    avoid double-animation.
    
    In `@components/social-links-section.tsx`:
    - Line 54: The per-row stagger using style={{ animationDelay: `${index * 60}ms`
    }} in components/social-links-section.tsx is dead because no animation utility
    is applied; either remove that inline animationDelay or add an entrance
    animation class (e.g. animate-fade-in-up) to the row anchor so the delay takes
    effect. Locate the anchor/map row where index is used (the element with the
    inline style) and either delete the animationDelay prop or add the entrance
    animation utility to its className (keeping the same index-based delay) so the
    stagger becomes meaningful.
    - Around line 44-70: The hover wipe fails because the named group
    "group/channel" is applied to the <a> (descendant) while the sliding background
    <span> is a sibling; move the "group/channel" class from the anchor element (the
    <a> that currently has className="group/channel ...") to the parent list item
    <li key={link.name} ...> so both the background span and the anchor share the
    same group scope, then remove "group/channel" from the anchor's className and
    keep the group-hover/channel:* utilities on the background span and anchor
    children.
    
    In `@components/ui/spectrum.tsx`:
    - Around line 30-40: Touch taps can leave bars stuck because pointerleave may
    not fire; update the component so taps reset the cursor: add onPointerUp and
    onPointerCancel handlers that call handleLeave (or call setCursor(null)) and/or
    restrict onPointerDown to only handle mouse pointers by checking
    event.pointerType before invoking handleMove. Modify the element that currently
    uses onPointerDown={handleMove} to either gate by event.pointerType or wire up
    onPointerUp/onPointerCancel to the existing handleLeave to ensure cursor is
    cleared after touch.
    - Line 38: Replace the template-literal class concatenation with the cn() class
    merger: import cn from 'lib/utils' (or the existing named export) at the top of
    components/ui/spectrum.tsx and wrap the base classes and incoming className in
    cn(), e.g. change the className assignment that currently uses
    `className={\`group relative flex items-end gap-[3px] h-4 ${className ?? ""}\`}`
    to use `className={cn("group relative flex items-end gap-[3px] h-4",
    className)}` so Tailwind utilities are merged/deduped correctly; ensure you
    reference the cn symbol and the className prop in your change.
    
    In `@components/ui/waveform.tsx`:
    - Around line 8-31: The SVG gradient id "wave-gradient" and its reference
    url(`#wave-gradient`) are hardcoded and will collide when multiple Waveform
    components render; update the Waveform component to generate a stable unique id
    via React.useId() (or similar) and use that id for both the <linearGradient
    id=...> and the path's stroke="url(#...)" so each instance has a distinct
    gradient reference (replace literal "wave-gradient" in both places with the
    generated id).
    
    🪄 Autofix (Beta)

    Fix all unresolved CodeRabbit comments on this PR:

    • Push a commit to this branch (recommended)
    • Create a new PR with the fixes

    ℹ️ Review info
    ⚙️ Run configuration

    Configuration used: Organization UI

    Review profile: ASSERTIVE

    Plan: Pro

    Run ID: 41335351-b4cd-4a8e-970b-6437c142fab1

    📥 Commits

    Reviewing files that changed from the base of the PR and between 26e53a7 and c22141b.

    📒 Files selected for processing (16)
    • AGENTS.md
    • app/globals.css
    • app/layout.tsx
    • app/manifest.ts
    • app/page.tsx
    • components/dev-greeting.tsx
    • components/footer.tsx
    • components/profile-header.tsx
    • components/project-card.tsx
    • components/projects-accordion.tsx
    • components/projects-section.tsx
    • components/social-links-section.tsx
    • components/ui/backdrop.tsx
    • components/ui/flickering-grid.tsx
    • components/ui/spectrum.tsx
    • components/ui/waveform.tsx
    💤 Files with no reviewable changes (1)
    • components/ui/flickering-grid.tsx

    Comment thread AGENTS.md
    Comment thread app/globals.css Outdated
    Comment thread app/globals.css
    Comment on lines +203 to +211
    .grain {
    position: absolute;
    inset: -5%;
    pointer-events: none;
    opacity: 0.035;
    mix-blend-mode: overlay;
    background-image: url("data:image/svg+xml;utf8,<svg viewBox='0 0 200 200' xmlns='http://www.w3.org/2000/svg'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 1, 0 0 0 0 1, 0 0 0 0 1, 0 0 0 0.6 0'/></filter><rect width='100%' height='100%' filter='url(%23n)'/></svg>");
    animation: grain-shift 8s steps(4) infinite;
    }

    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 | 🔵 Trivial

    .grain assumes a positioned ancestor — document or enforce.

    .grain uses position: absolute; inset: -5%; without any self-contained positioning context. Any caller that applies it to a child whose nearest ancestor isn't positioned will see the grain escape to the viewport. Since this is a shared utility, either (a) add a brief comment stating the contract, or (b) add contain: strict / pair with a documented .grain-host { position: relative; overflow: hidden; } helper.

    🤖 Prompt for AI Agents
    Verify each finding against the current code and only fix it if needed.
    
    In `@app/globals.css` around lines 203 - 211, .grain is absolutely positioned but
    assumes a positioned ancestor; update the stylesheet to either document this
    contract and/or add a helper class so callers have a known context.
    Specifically, add a short comment explaining that .grain requires a positioned
    container, and introduce a companion selector (e.g., .grain-host) that sets
    positioning and clipping (position: relative; overflow: hidden; or contain:
    strict) so .grain will be contained — reference .grain and the new .grain-host
    in the comment and docstring so callers know to wrap grain with .grain-host.
    

    Comment thread app/page.tsx Outdated
    Comment thread components/profile-header.tsx Outdated
    Comment thread components/social-links-section.tsx Outdated
    Comment thread components/social-links-section.tsx Outdated
    Comment thread components/ui/spectrum.tsx
    onPointerMove={handleMove}
    onPointerLeave={handleLeave}
    onPointerDown={handleMove}
    className={`group relative flex items-end gap-[3px] h-4 ${className ?? ""}`}

    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 | 🔵 Trivial

    Use cn() for class merging.

    Template-literal concatenation works but will not de-duplicate conflicting utilities when callers pass overriding classes. As per coding guidelines, components/**/*.{tsx,ts} should use the cn() utility from lib/utils.ts for Tailwind class merging.

    ♻️ Proposed refactor
    -import { type PointerEvent, useCallback, useRef, useState } from "react";
    +import { type PointerEvent, useCallback, useRef, useState } from "react";
    +import { cn } from "@/lib/utils";
     ...
    -      className={`group relative flex items-end gap-[3px] h-4 ${className ?? ""}`}
    +      className={cn("group relative flex items-end gap-[3px] h-4", className)}
    🤖 Prompt for AI Agents
    Verify each finding against the current code and only fix it if needed.
    
    In `@components/ui/spectrum.tsx` at line 38, Replace the template-literal class
    concatenation with the cn() class merger: import cn from 'lib/utils' (or the
    existing named export) at the top of components/ui/spectrum.tsx and wrap the
    base classes and incoming className in cn(), e.g. change the className
    assignment that currently uses `className={\`group relative flex items-end
    gap-[3px] h-4 ${className ?? ""}\`}` to use `className={cn("group relative flex
    items-end gap-[3px] h-4", className)}` so Tailwind utilities are merged/deduped
    correctly; ensure you reference the cn symbol and the className prop in your
    change.
    

    Comment thread components/ui/waveform.tsx
    Remove the "v4.0 · 2026" stamp from the nav and the "432 Hz" label next
    to the spectrum (the bars stand on their own). Point the X social link
    at x.com instead of the legacy twitter.com domain.
    
    Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

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

    ♻️ Duplicate comments (3)
    app/page.tsx (1)

    15-20: ⚠️ Potential issue | 🟡 Minor

    Use a non-navigation wrapper for the brand row.

    This <nav> exposes a navigation landmark but contains no links or navigation controls. Use <header> or a plain container instead.

    Proposed fix
    -        <nav className="mb-10 md:mb-16 flex items-center font-mono text-[10px] tracking-[0.25em] uppercase text-ink-500">
    +        <header className="mb-10 md:mb-16 flex items-center font-mono text-[10px] tracking-[0.25em] uppercase text-ink-500">
               <span className="group/nav flex items-center gap-2">
                 <span className="inline-block h-2.5 w-2.5 rotate-45 bg-glow transition-transform duration-700 ease-[cubic-bezier(0.16,1,0.3,1)] group-hover/nav:rotate-[135deg]" />
                 <span>Resonant / Home</span>
               </span>
    -        </nav>
    +        </header>
    🤖 Prompt for AI Agents
    Verify each finding against the current code and only fix it if needed.
    
    In `@app/page.tsx` around lines 15 - 20, The brand row is wrapped in a <nav> but
    contains no links, so replace that navigation landmark with a non-navigation
    container: change the <nav className="mb-10 md:mb-16 flex items-center font-mono
    text-[10px] tracking-[0.25em] uppercase text-ink-500"> element to a semantic
    <header> or a plain <div> (keeping the same className and child spans, including
    the "group/nav" span and its inline-block indicator) so the markup no longer
    exposes an empty navigation landmark while preserving styling and structure.
    
    components/social-links-section.tsx (1)

    44-54: ⚠️ Potential issue | 🟠 Major

    Fix the row group scope and make the staggered delay active.

    The hover wipe still cannot trigger because group/channel is on the <a>, while the wipe <span> is its sibling. The inline animationDelay is also still a no-op without an animation class.

    Proposed fix
    -            <li key={link.name} className="relative overflow-hidden">
    +            <li key={link.name} className="group/channel relative overflow-hidden">
                   <span
                     aria-hidden="true"
                     className="pointer-events-none absolute inset-0 bg-glow/[0.04] translate-x-[-101%] transition-transform duration-500 ease-[cubic-bezier(0.16,1,0.3,1)] group-hover/channel:translate-x-0"
                   />
                   <a
                     href={link.url}
                     target={isMail ? undefined : "_blank"}
                     rel={isMail ? undefined : "noopener noreferrer"}
    -                className="group/channel relative flex items-center gap-4 py-3 text-sm text-ink-300 transition-colors hover:text-ink-100"
    +                className="relative flex animate-fade-in-up items-center gap-4 py-3 text-sm text-ink-300 transition-colors hover:text-ink-100"
                     style={{ animationDelay: `${index * 60}ms` }}
                   >
    In Tailwind CSS v4, do named group-hover variants such as group-hover/channel apply to sibling elements, or only descendants of the element with group/channel?
    
    🤖 Prompt for AI Agents
    Verify each finding against the current code and only fix it if needed.
    
    In `@components/social-links-section.tsx` around lines 44 - 54, The hover wipe
    fails because the named group class ("group/channel") is placed on the <a> while
    the wipe <span> is its sibling, and the inline animationDelay is no-op without
    an animation class; move the group/channel class to the parent element that
    contains both the <span> and <a> (the <li> or another wrapper) so the span can
    respond to group-hover/channel, and add a real animation class to the element
    being delayed (e.g., the wipe <span> or the <a>) so the style animationDelay on
    the element (style={{ animationDelay: `${index * 60}ms` }}) takes effect; update
    references for group/channel, the wipe <span>, and the inline animationDelay
    accordingly.
    
    components/profile-header.tsx (1)

    31-47: 🧹 Nitpick | 🔵 Trivial

    Add an accessible name for the split-letter heading.

    The animated per-letter surname can be announced awkwardly by some assistive tech. Give the <h1> a single accessible name and hide the decorative split span.

    Proposed refactor
    -          <h1 className="group/name font-display text-5xl md:text-6xl leading-[0.95] tracking-tight text-ink-100">
    +          <h1
    +            aria-label="Keith Elliott"
    +            className="group/name font-display text-5xl md:text-6xl leading-[0.95] tracking-tight text-ink-100"
    +          >
                 Keith
                 <br />
    -            <span className="italic text-glow inline-flex">
    +            <span aria-hidden="true" className="italic text-glow inline-flex">
    🤖 Prompt for AI Agents
    Verify each finding against the current code and only fix it if needed.
    
    In `@components/profile-header.tsx` around lines 31 - 47, The H1 currently renders
    the surname as individual animated letters which may be announced awkwardly;
    give the <h1 className="group/name"> a single accessible name (e.g., set an
    aria-label with the full name like "Keith Elliott" or include a visually-hidden
    full-name node inside the H1) and mark the animated per-letter span (the <span
    className="italic text-glow inline-flex"> that maps ELLIOTT) as decorative by
    adding aria-hidden="true" and role="presentation" (and remove it from keyboard
    flow if necessary), ensuring screen readers only announce the single accessible
    name.
    
    🤖 Prompt for all review comments with AI agents
    Verify each finding against the current code and only fix it if needed.
    
    Duplicate comments:
    In `@app/page.tsx`:
    - Around line 15-20: The brand row is wrapped in a <nav> but contains no links,
    so replace that navigation landmark with a non-navigation container: change the
    <nav className="mb-10 md:mb-16 flex items-center font-mono text-[10px]
    tracking-[0.25em] uppercase text-ink-500"> element to a semantic <header> or a
    plain <div> (keeping the same className and child spans, including the
    "group/nav" span and its inline-block indicator) so the markup no longer exposes
    an empty navigation landmark while preserving styling and structure.
    
    In `@components/profile-header.tsx`:
    - Around line 31-47: The H1 currently renders the surname as individual animated
    letters which may be announced awkwardly; give the <h1 className="group/name"> a
    single accessible name (e.g., set an aria-label with the full name like "Keith
    Elliott" or include a visually-hidden full-name node inside the H1) and mark the
    animated per-letter span (the <span className="italic text-glow inline-flex">
    that maps ELLIOTT) as decorative by adding aria-hidden="true" and
    role="presentation" (and remove it from keyboard flow if necessary), ensuring
    screen readers only announce the single accessible name.
    
    In `@components/social-links-section.tsx`:
    - Around line 44-54: The hover wipe fails because the named group class
    ("group/channel") is placed on the <a> while the wipe <span> is its sibling, and
    the inline animationDelay is no-op without an animation class; move the
    group/channel class to the parent element that contains both the <span> and <a>
    (the <li> or another wrapper) so the span can respond to group-hover/channel,
    and add a real animation class to the element being delayed (e.g., the wipe
    <span> or the <a>) so the style animationDelay on the element (style={{
    animationDelay: `${index * 60}ms` }}) takes effect; update references for
    group/channel, the wipe <span>, and the inline animationDelay accordingly.
    

    ℹ️ Review info
    ⚙️ Run configuration

    Configuration used: Organization UI

    Review profile: ASSERTIVE

    Plan: Pro

    Run ID: c43fa086-875b-49c9-b88d-e99302af1cc7

    📥 Commits

    Reviewing files that changed from the base of the PR and between c22141b and 89defb3.

    📒 Files selected for processing (3)
    • app/page.tsx
    • components/profile-header.tsx
    • components/social-links-section.tsx

    - social-links: move group/channel to <li> so hover wipe actually triggers
    - spectrum: reset bars on pointerup/cancel so touch taps don't stick
    - waveform: use useId() for gradient id to avoid collisions
    - project-card, social-links: drop no-op animationDelay inline styles
    - page: <nav> → <header> (brand row has no links)
    - profile-header: aria-label on h1, aria-hidden on split-letter span
    - dev-greeting: gate console output to non-production builds
    - agents.md: list Instrument Serif, fix markdownlint nits
    - globals.css: stylelint fixes (blank line, lowercase currentcolor)
    
    Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
    # Conflicts:
    #	app/layout.tsx
    #	app/page.tsx
    #	components/footer.tsx
    #	components/profile-header.tsx
    #	components/projects-accordion.tsx
    #	components/projects-section.tsx
    #	components/social-links-section.tsx
    #	components/ui/flickering-grid.tsx
    @keithce keithce merged commit a24a2b7 into main Apr 19, 2026
    3 checks passed
    @keithce keithce deleted the feat/design-refresh branch April 19, 2026 03:37
    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