Skip to content

feat: single-scroll redesign — kill AI-template tells, tri-panel OSS, consulting-first narrative#154

Merged
jbdevprimary merged 1 commit into
mainfrom
feat/de-ai-redesign
Jun 9, 2026
Merged

feat: single-scroll redesign — kill AI-template tells, tri-panel OSS, consulting-first narrative#154
jbdevprimary merged 1 commit into
mainfrom
feat/de-ai-redesign

Conversation

@jbdevprimary

Copy link
Copy Markdown
Contributor

Why

Direct feedback: the site "looks like an AI-generated website," plus directives to tighten all copy, put real thought into layout/positioning, focus on the three feature-complete OSS flagships, and represent Jan 2026–present as independent consulting rather than an open-source "position."

Diagnosis and design spec are in-repo: .ui-design/reviews/ai-look-diagnosis_20260609.md (what reads as AI and why) and docs/design/redesign-2026-06.md (the build-ready spec this implements).

What

IA — six identical tab shells → one opinionated scroll (Work · Open Source · Skills · Contact) with a sticky scroll-spy nav. About/Earlier Career/Education fold into hero, Work, and footer. The 6-second recruiter story (who, proof, action) is fully above the fold.

Visual de-slop — mesh shader, both dot grids, gradient orbs → one static amber glow (@paper-design/shaders-react removed from the bundle). Animated gradient name, pulse pill, glassmorphism stat cards, hash-rotated accent bars, ~80-badge chip walls, and staggered entrances all gone. Stats are a borderless typographic band; Skills is a spec-sheet; project rules are colored by category.

Hero — compact identity block: mono name, modest serif title, one-line Flipside proof. No giant name, no display-type headline.

Content — current period is jonbogaty.com — Independent Platform & DevOps Consultant (now ON the resume, closing the gap; OSS is a supporting mention). Projects focused to the three flagships in a tri-panel with real package tables: paranoid-passwd (copy resynced from the live repo — Rust-native password manager, Slint GUI, Argon2id/AES-256-GCM vault), radioactive-ralph, Extended Data Library. Agentic + Strata dropped.

Verification

  • 24 unit + 17 e2e green, astro check + tsc 0 errors, biome clean
  • DOCX re-rendered via LibreOffice QC: still 2 pages, consulting entry present, three-flagship Open Source section
  • Site screenshot-verified on the production build at 1440px + 390px, every section read against the spec mockups

🤖 Generated with Claude Code

… consulting-first narrative

Implements docs/design/redesign-2026-06.md (from the ai-look diagnosis +
recruiter positioning review).

IA: six identical tab shells → one opinionated scroll (Work, Open
Source, Skills, Contact) with a sticky scroll-spy anchor nav. About,
Earlier Career, and Education fold into hero, Work, and footer.

Visual de-slop: mesh shader + both dot grids + gradient orbs replaced
by one static amber glow (drops @paper-design/shaders-react); animated
gradient name, pulse pill, glassmorphism stat cards, hash-rotated
accent bars, badge chip-walls, and 5-stage staggered entrances all
removed. Stats are a borderless typographic band; skills are spec-sheet
rows; project accent rules are colored by category.

Hero: compact identity block (mono name, modest serif title, one-line
Flipside proof) — no display-type headline, no giant name.

Content: Jan 2026–present is now jonbogaty.com / Independent Platform &
DevOps Consultant (on the resume — closes the gap; OSS is a supporting
mention, not a position). Projects focused to the three feature-complete
flagships in a tri-panel: paranoid-passwd (copy resynced from the live
repo — Rust-native password manager, Slint GUI, encrypted vault),
radioactive-ralph, Extended Data Library. Agentic and Strata dropped.

Decision: positioning thesis drives layout — proof above the fold, receipts at depth 1, packages at depth 2
Why: recruiter 6-second scan is the primary use case; the six-tab IA hid the proof behind clicks
Resolves: 'looks like an AI-generated website' feedback; copy tightening + layout/positioning directive

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

coderabbitai Bot commented Jun 9, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@jbdevprimary, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 22 minutes and 7 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

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.

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 include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c6a8353c-2cca-4c9a-9dac-59afc58953d9

📥 Commits

Reviewing files that changed from the base of the PR and between c0b9051 and da052f7.

⛔ Files ignored due to path filters (2)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
  • public/Jon_Bogaty_Resume.docx is excluded by !**/*.docx
📒 Files selected for processing (21)
  • .agent-state/decisions.ndjson
  • docs/design/redesign-2026-06.md
  • package.json
  • src/App.tsx
  • src/components/HeroSection.tsx
  • src/components/SectionTabs.tsx
  • src/components/SiteFooter.tsx
  • src/components/SiteNav.tsx
  • src/components/sections/AboutSection.tsx
  • src/components/sections/EarlierCareer.tsx
  • src/components/sections/EducationList.tsx
  • src/components/sections/JobList.tsx
  • src/components/sections/OpenSource.tsx
  • src/components/sections/ProjectGrid.tsx
  • src/components/sections/SkillGrid.tsx
  • src/components/sections/SkillSheet.tsx
  • src/content/resume.ts
  • src/index.css
  • tests/e2e/navigation.spec.ts
  • tests/e2e/resume.spec.ts
  • tests/unit/resume-data.test.ts
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/de-ai-redesign

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.

@amazon-q-developer amazon-q-developer 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.

Summary

This redesign successfully transforms the site from a tabbed interface to a single-scroll layout, removing AI-looking visual elements while maintaining clean functionality. The changes align well with the stated goals in the PR description.

Key Changes:

  • ✅ Converted from 6-tab interface to opinionated single-scroll layout
  • ✅ Removed shader backgrounds and decorative visual elements
  • ✅ Simplified hero section with compact identity block
  • ✅ Focused projects on three flagships with tri-panel layout
  • ✅ Updated current work period to consulting position
  • ✅ All 41 tests passing (24 unit + 17 e2e)

Critical Issue Identified:

  • 1 runtime error risk in OpenSource.tsx where missing fallback for undefined project categories could break rendering

The implementation is solid overall, but please address the identified issue before merging to prevent potential runtime failures.


You can now have the agent implement changes and create commits directly on your pull request's source branch. Simply comment with /q followed by your request in natural language to ask the agent to make changes.

Comment thread src/components/sections/OpenSource.tsx
@jbdevprimary jbdevprimary merged commit 88b41c9 into main Jun 9, 2026
14 checks passed
@jbdevprimary jbdevprimary deleted the feat/de-ai-redesign branch June 9, 2026 23:53

@gemini-code-assist gemini-code-assist 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.

Code Review

This pull request redesigns the portfolio site, transitioning from a tabbed layout to a single-scroll vertical narrative. It removes the dynamic mesh shader in favor of a static amber radial glow, updates the hero section with direct proof-focused copy, and restructures the Work, Open Source, and Skills sections into clean, typographic layouts. Additionally, the navigation is replaced with a scroll-spy header, and tests are updated to reflect these changes. Feedback on the changes highlights several potential runtime crashes and type errors due to missing defensive guards or optional chaining, particularly when accessing education, job, or summary arrays. There is also an issue where the scroll-spy active state is not cleared when scrolling back to the top of the page.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

{ label: 'Telegram', href: 'https://t.me/jbpersonaldev', icon: MessageCircle },
{ label: 'Email', href: 'mailto:jon@jonbogaty.com', icon: Mail },
]
const edu = resume.education[0]

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.

high

Accessing resume.education[0] directly at the module level can cause a runtime crash during module loading if the education array is empty or undefined. It is safer to use optional chaining here.

Suggested change
const edu = resume.education[0]
const edu = resume.education?.[0]

Comment on lines +55 to +58
<span>
{edu.studyType.replace(/ \(.*\)/, '')}, {edu.area} — {edu.institution} ({edu.endDate}
), with honors
</span>

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.

high

If edu is undefined (e.g., if the education array is empty), accessing edu.studyType will throw a TypeError at render time. Wrap this section in a conditional guard to ensure safe rendering.

          {edu && (
            <span>
              {edu.studyType.replace(/ \(.*\)/, '')}, {edu.area} — {edu.institution} ({edu.endDate}
              ), with honors
            </span>
          )}

Comment on lines +20 to +28
const observer = new IntersectionObserver(
(entries) => {
const visible = entries
.filter((e) => e.isIntersecting)
.sort((a, b) => b.intersectionRatio - a.intersectionRatio)
if (visible[0]) setActive(visible[0].target.id)
},
{ rootMargin: '-20% 0px -60% 0px' }
)

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.

medium

When the user scrolls back to the top of the page (e.g., the hero section), no sections will be intersecting the observer's root margin. Since visible[0] becomes undefined, the active state is never cleared, leaving the first nav link highlighted. Clear the active state when no sections are visible to improve the user experience.

    const observer = new IntersectionObserver(
      (entries) => {
        const visible = entries
          .filter((e) => e.isIntersecting)
          .sort((a, b) => b.intersectionRatio - a.intersectionRatio)
        if (visible[0]) {
          setActive(visible[0].target.id)
        } else {
          setActive(null)
        }
      },
      { rootMargin: '-20% 0px -60% 0px' }
    )

Comment on lines 16 to +18
const [selected, setSelected] = useState(0)
const active = jobs[selected]
const cloud = (active.tech ?? []).filter((t) => CLOUD_TECH.has(t))

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.

medium

If the jobs array is empty, active will be undefined, causing a runtime crash when trying to access active.tech or other properties during render. Add a defensive guard to return null early if no active job is found.

Suggested change
const [selected, setSelected] = useState(0)
const active = jobs[selected]
const cloud = (active.tech ?? []).filter((t) => CLOUD_TECH.has(t))
const [selected, setSelected] = useState(0)
const active = jobs[selected]
if (!active) return null
const cloud = (active.tech ?? []).filter((t) => CLOUD_TECH.has(t))

Comment thread src/App.tsx
className="border-t border-border/60 mx-auto max-w-5xl px-4 sm:px-6 py-14 scroll-mt-14"
>
<h2 className="font-heading text-3xl text-foreground mb-8">Open Source</h2>
<OpenSource items={resume.projects} lead={resume.about.summary[1]} />

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.

medium

If resume.about.summary has fewer than 2 elements, resume.about.summary[1] will be undefined. Since OpenSource expects lead to be a non-nullable string, this can cause a type mismatch or runtime issues. Use a nullish coalescing operator to provide a safe fallback.

Suggested change
<OpenSource items={resume.projects} lead={resume.about.summary[1]} />
<OpenSource items={resume.projects} lead={resume.about.summary[1] ?? ''} />

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.

1 participant