Skip to content

feat(docs): add 3D hero visual — cinematic particle network#212

Merged
bbopen merged 5 commits intomainfrom
feat/hero-visual
Mar 20, 2026
Merged

feat(docs): add 3D hero visual — cinematic particle network#212
bbopen merged 5 commits intomainfrom
feat/hero-visual

Conversation

@bbopen
Copy link
Copy Markdown
Owner

@bbopen bbopen commented Mar 20, 2026

Summary

Add an interactive 3D hero section to the VitePress docs site featuring a Hermes 4-inspired cinematic particle network:

  • 15K noise-driven particles in amber/sapphire/green tywrap palette
  • Neural edge tubes with pulsing opacity for data-flow aesthetic
  • Bloom + vignette post-processing for cinematic feel
  • Smooth mouse parallax and scroll-reactive camera
  • Asymmetric left-aligned layout with shimmer gradient headline
  • Force-dark theme with transparent backgrounds for canvas integration

New dev dependencies

  • three — Three.js core
  • postprocessing — Bloom, Vignette effects
  • @types/three — type definitions

Files

File Purpose
docs/.vitepress/theme/composables/useThreeScene.ts Three.js scene composable — particles, edges, bloom, parallax
docs/.vitepress/theme/components/Hero3D.vue Hero component — canvas, overlay, headline, CTAs
docs/.vitepress/theme/custom.css Hide default VitePress hero, transparent backgrounds
docs/.vitepress/theme/index.ts Custom theme wiring via layout-top slot
docs/.vitepress/config.ts Added appearance: 'force-dark'
docs/index.md Removed dead hero frontmatter
docs/plans/ Design doc, implementation plan, codecert verification

Review fixes applied

  • Removed unrelated docs/download_hermes.py and docs/check_errors.cjs
  • Fixed mouse parallax (was broken — mouse never updated from targetMouse)
  • Fixed resource disposal (tube geometries, materials, node meshes)
  • Moved nodes into coreGroup for consistent parallax
  • Moved three/postprocessing to devDependencies
  • Made home page path matching robust with cleanUrls

Test plan

  • npm run docs:build passes
  • Visual review on desktop and mobile
  • Verify no regressions on inner doc pages
  • Check performance on lower-end hardware

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 20, 2026

Caution

Review failed

Pull request was closed or merged during review

📝 Walkthrough

Walkthrough

Replaces the static VitePress homepage hero with a Three.js-backed Hero component and composable, integrates them into a custom VitePress theme (forced dark appearance + CSS), adds Three.js/postprocessing devDependencies and types, removes two docs/plans files and adds docs/plans/ to .gitignore.

Changes

Cohort / File(s) Summary
3D Hero Implementation
docs/.vitepress/theme/components/Hero3D.vue, docs/.vitepress/theme/composables/useThreeScene.ts
Adds a Vue SFC hero and a composable that create and manage a Three.js scene with EffectComposer (bloom, vignette), procedural particle shaders, instanced nodes, tube meshes, RAF animation loop, mouse/scroll/resize handling, reduced-motion opt-out, and proper disposal.
Theme Integration & Styling
docs/.vitepress/theme/index.ts, docs/.vitepress/config.ts, docs/.vitepress/theme/custom.css
Introduces a custom theme that mounts the Hero on home routes, sets appearance: 'force-dark', hides the default VitePress hero, and provides CSS to position and style the canvas, overlay, and left-aligned content.
Homepage Content
docs/index.md, docs/public/llms-full.txt
Removes the static hero front-matter block from homepage source files.
Planning Documents & Ignore
docs/plans/2026-01-22-tywrapped-ecosystem-vision.md, docs/plans/2026-03-10-tywrap-promotion.md, .gitignore
Deletes two planning/RFC documents and adds docs/plans/ to .gitignore so local plans are ignored.
Dependencies
package.json
Adds three, postprocessing, and @types/three to devDependencies for the new 3D hero and TypeScript support.

Sequence Diagram

sequenceDiagram
    participant Vue as Hero3D Component
    participant Composable as useThreeScene Composable
    participant ThreeJS as Three.js + Composer
    participant Browser as Browser Events

    Vue->>Composable: initialize(canvas, width, height)
    Composable->>ThreeJS: create Scene, Camera, Renderer, EffectComposer
    Composable->>ThreeJS: add particles, instanced nodes, tube meshes
    Composable-->>Vue: return { start, resize, onScroll, dispose }

    Vue->>Composable: start()
    loop RAF loop
        Browser->>Composable: scroll / mouse / resize
        Composable->>ThreeJS: update uniforms (uTime, uScroll, mouse), animate transforms
        Composable->>ThreeJS: composer.render()
    end
    Vue->>Composable: dispose() (on unmount)
    Composable->>ThreeJS: cancel RAF, dispose geometries/materials/composer/renderer
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested labels

priority:p2

Poem

🐇 I hopped through shaders in the night,
Particles wink and tubes alight,
Scroll soft and watch the core revolve,
A tiny rabbit's bloom evolve,
Hop, shimmer, docs take flight!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

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.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically summarizes the main change: adding a 3D hero visual with a cinematic particle network to the documentation site.
Description check ✅ Passed The description provides a comprehensive overview of the changes, including the visual features, new dependencies, files modified, fixes applied, and test plan—all directly related to the changeset.

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

✨ 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 feat/hero-visual

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.

@github-actions github-actions Bot added the area:docs Area: documentation label Mar 20, 2026
@codecov-commenter
Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

Add an interactive 3D hero section to the VitePress docs site,
featuring a Hermes 4-inspired cinematic particle network with:

- 15K noise-driven particles in amber/sapphire/green tywrap palette
- Neural edge tubes with pulsing opacity for data-flow aesthetic
- Bloom + vignette post-processing for high-end visual feel
- Mouse parallax and scroll-reactive camera movement
- Asymmetric left-aligned layout with shimmer gradient headline
- Force-dark theme, transparent backgrounds for canvas integration

Ported from bbopen/tywrap-hero-visual (React Three Fiber) and
reimagined as a raw Three.js + Vue 3 composable. Includes design
docs and codecert patch equivalence verification.

New deps: three, postprocessing, @types/three
@bbopen bbopen force-pushed the feat/hero-visual branch from 0915972 to 959e739 Compare March 20, 2026 07:09
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0915972096

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

import type { Theme } from 'vitepress'
import { h } from 'vue'
import { useRoute } from 'vitepress'
import Hero3D from './components/Hero3D.vue'
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Lazy-load the hero component outside the common docs bundle

Hero3D is imported eagerly from the theme entry, and that component immediately pulls useThreeScene, three, and postprocessing at module scope. Because the default theme entry is loaded on every route, all inner docs pages now download the full 3D stack even though isHome prevents it from rendering there. This is a site-wide performance regression for non-home pages; loading the hero asynchronously only when the current route is / would keep Three.js off the common path.

Useful? React with 👍 / 👎.

@bbopen
Copy link
Copy Markdown
Owner Author

bbopen commented Mar 20, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 20, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot added documentation Improvements or additions to documentation enhancement New feature or request labels Mar 20, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/.vitepress/theme/components/Hero3D.vue`:
- Around line 58-86: Add simple accessibility attributes: mark the decorative
canvas (ref="canvasRef" / class="hero-canvas") as aria-hidden="true" so screen
readers ignore it, and add a descriptive aria-label on the interactive section
element (class="hero-section") to convey the hero's purpose to assistive tech;
update the <template> in Hero3D.vue accordingly (no behavior changes to
canvasRef or hero-content).

In `@docs/.vitepress/theme/composables/useThreeScene.ts`:
- Around line 204-264: The particle count is fixed at 15000 which can cause poor
performance on mobile; modify the initialization around the PARTICLE_COUNT
constant in useThreeScene (where positions, colors, sizes, geometry and
particlesMesh are created) to compute PARTICLE_COUNT based on device capability
or a passed quality setting (e.g., detect mobile via window.innerWidth and
userAgent or expose a config flag), then allocate positions/colors/sizes arrays
and create BufferAttributes using that computed count so the rest of the
particle creation loop and the geometry/material/particlesMesh setup work with
the reduced count.

In `@docs/.vitepress/theme/custom.css`:
- Around line 24-30: Add a concise comment above the media query that documents
the VitePress version (or range) these internal selectors were verified against
and why (they target internal classes that may change), referencing the
selectors .VPHome .VPFeatures .container and .VPHome .VPHomeContent .container
so future maintainers know this is version-dependent and where to update if
VitePress is bumped.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 47c7940e-e922-49a4-b797-20a433330f76

📥 Commits

Reviewing files that changed from the base of the PR and between 494cbf7 and 959e739.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (11)
  • .gitignore
  • docs/.vitepress/config.ts
  • docs/.vitepress/theme/components/Hero3D.vue
  • docs/.vitepress/theme/composables/useThreeScene.ts
  • docs/.vitepress/theme/custom.css
  • docs/.vitepress/theme/index.ts
  • docs/index.md
  • docs/plans/2026-01-22-tywrapped-ecosystem-vision.md
  • docs/plans/2026-03-10-tywrap-promotion.md
  • docs/public/llms-full.txt
  • package.json
💤 Files with no reviewable changes (4)
  • docs/public/llms-full.txt
  • docs/index.md
  • docs/plans/2026-01-22-tywrapped-ecosystem-vision.md
  • docs/plans/2026-03-10-tywrap-promotion.md
📜 Review details
🔇 Additional comments (10)
.gitignore (1)

12-14: Looks good: local planning artifacts are cleanly excluded.

The new docs/plans/ rule is scoped and clear, and matches the “local only” intent without affecting other docs content.

docs/.vitepress/config.ts (1)

7-7: LGTM — Force-dark mode aligns with the 3D hero design.

The appearance: 'force-dark' setting ensures the dark theme required by the cinematic hero visual is consistently applied across the docs site.

docs/.vitepress/theme/custom.css (1)

1-22: LGTM — Clean overrides for the custom hero integration.

The CSS correctly hides the default VitePress hero and establishes transparent backgrounds for seamless 3D canvas integration.

docs/.vitepress/theme/index.ts (1)

10-16: LGTM — Clean conditional Hero3D injection.

The home route detection correctly handles both / and /tywrap (with/without trailing slash) via the regex replacement. The conditional slot injection pattern is idiomatic for VitePress theme customization.

docs/.vitepress/theme/components/Hero3D.vue (1)

1-56: LGTM — Well-structured component with proper lifecycle management.

The component correctly:

  • Initializes Three.js scene on mount with canvas dimensions
  • Throttles scroll events via requestAnimationFrame
  • Cleans up event listeners and disposes the scene on unmount
  • Uses site.base for link paths to support deployment under a subpath
docs/.vitepress/theme/composables/useThreeScene.ts (4)

146-179: LGTM — Solid Three.js initialization with good performance defaults.

The setup correctly:

  • Caps devicePixelRatio at 2 for performance on high-DPI displays
  • Uses powerPreference: 'high-performance'
  • Configures ACES filmic tone mapping for cinematic look
  • Registers mousemove listener for parallax tracking

181-196: LGTM — Post-processing pipeline well-configured.

Using HalfFloatType for the frame buffer provides HDR headroom for the bloom effect. The bloom intensity of 4.0 is high but intentional for the plasma aesthetic.


369-389: LGTM — Comprehensive resource disposal.

The dispose function properly cleans up:

  • Animation frame via cancelAnimationFrame
  • Event listener removal
  • All geometries and materials (including traversing linesGroup for tubes)
  • Composer and renderer

This prevents memory leaks when the component unmounts.


297-326: Neural edge tubes are correctly created and will be disposed via traverse.

The 40 tube geometries with CatmullRom curves create the visual "axon" effect. Storing materials in lineMaterials enables the pulsing opacity animation, and the linesGroup.traverse() in dispose() properly cleans up all mesh resources.

package.json (1)

129-147: LGTM — Three.js dependencies correctly placed in devDependencies.

Placing three, postprocessing, and @types/three in devDependencies is correct since they're only used for the docs site build, not the published library. The versions (three ^0.183.2 and @types/three ^0.183.1) are compatible.

Comment thread docs/.vitepress/theme/components/Hero3D.vue
Comment thread docs/.vitepress/theme/composables/useThreeScene.ts Outdated
Comment thread docs/.vitepress/theme/custom.css
- Add aria-hidden to decorative canvas, aria-label to hero section
- Reduce particle count to 5000 on mobile (< 768px width)
- Document VitePress version dependency for internal CSS selectors
@bbopen
Copy link
Copy Markdown
Owner Author

bbopen commented Mar 20, 2026

@coderabbitai resolve

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/.vitepress/theme/components/Hero3D.vue`:
- Around line 16-24: The onScroll handler currently queues a RAF that may run
after threeScene is nulled and cause a post-unmount error; modify onScroll to
save the requestAnimationFrame ID (e.g., rafId) when scheduling, and in the
component's onBeforeUnmount cleanup cancel that RAF via
cancelAnimationFrame(rafId) and reset scrollTicking/rafId and/or avoid calling
threeScene!.onScroll when threeScene is null. Update references in the onScroll
function and the onBeforeUnmount block to use the stored rafId and ensure no RAF
callback runs after threeScene is disposed.

In `@docs/.vitepress/theme/composables/useThreeScene.ts`:
- Around line 160-165: The global mousemove listener is added immediately
(window.addEventListener('mousemove', onMouseMove)) before scene init completes;
move attaching the onMouseMove handler into the start() routine and add an
idempotence guard (e.g., a boolean like isStarted or a flag on the scene) so the
listener is only registered after renderer/composer/geometry setup succeeds and
only once; also ensure corresponding removal in stop() (or cleanup) to avoid
leaks and do not attach the listener at module scope where targetMouse is
defined.

In `@docs/.vitepress/theme/custom.css`:
- Around line 23-32: The left inset is hard-coded as padding-left: 4rem in the
.VPHome .VPFeatures .container and .VPHome .VPHomeContent .container rules,
causing misalignment with the hero which computes its inset from
--vp-layout-max-width in Hero3D.vue; change the CSS to derive the inset from the
same variable (e.g. replace padding-left: 4rem with padding-left: calc((100% -
var(--vp-layout-max-width)) / 2) or use the same --hero-inset custom property
emitted by Hero3D.vue) so both hero and content use the same offset and stay
aligned across viewport sizes.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: d2ff80ee-23a0-419e-b890-93f6712b24e1

📥 Commits

Reviewing files that changed from the base of the PR and between 959e739 and 89d8832.

📒 Files selected for processing (3)
  • docs/.vitepress/theme/components/Hero3D.vue
  • docs/.vitepress/theme/composables/useThreeScene.ts
  • docs/.vitepress/theme/custom.css
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: os (windows-latest)

Comment thread docs/.vitepress/theme/components/Hero3D.vue
Comment thread docs/.vitepress/theme/composables/useThreeScene.ts
Comment thread docs/.vitepress/theme/custom.css
coderabbitai[bot]
coderabbitai Bot previously approved these changes Mar 20, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 20, 2026

✅ Actions performed

Comments resolved and changes approved.

- Cancel queued scroll RAF on unmount to prevent post-dispose errors
- Use optional chaining (threeScene?.onScroll) instead of non-null assertion
- Defer mousemove listener registration to start() with idempotence guard
- Align CSS padding-left with hero's --vp-layout-max-width calc
@coderabbitai coderabbitai Bot added the area:tooling Area: tooling and CLI label Mar 20, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/.vitepress/theme/components/Hero3D.vue`:
- Around line 28-43: Check prefers-reduced-motion at mount using
window.matchMedia('(prefers-reduced-motion: reduce)').matches and, if true, skip
initializing the decorative canvas (do not call useThreeScene or
threeScene.start), do not add the resize/scroll listeners (onResize, onScroll),
and instead add a CSS state class (e.g., 'prefers-reduced-motion') to the
component root so styles can disable shimmer/fade animations and keep copy/CTAs
visible; apply the same guard to the other initialization blocks that reference
threeScene, animation starts, or CSS transitions so all motion-related behavior
(mouse/scroll parallax, canvas rendering, shimmer text, fade-ins) is no-op when
reduced-motion is enabled.
- Around line 28-43: The scene isn't seeded with the current scroll position so
it stays at the top until the first scroll; after creating/starting threeScene
in the onMounted block (where canvasRef, useThreeScene, threeScene are used)
call the onScroll handler once (or otherwise apply window.scrollY to the scene)
to initialize the scene state, then continue registering
window.addEventListener('scroll', onScroll, { passive: true }); this ensures the
scene reflects the current scrollY immediately.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: c77c71bd-da10-4eb0-94d6-93a8ea67ee6b

📥 Commits

Reviewing files that changed from the base of the PR and between 89d8832 and 33d424e.

📒 Files selected for processing (3)
  • docs/.vitepress/theme/components/Hero3D.vue
  • docs/.vitepress/theme/composables/useThreeScene.ts
  • docs/.vitepress/theme/custom.css
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: os (windows-latest)
🔇 Additional comments (2)
docs/.vitepress/theme/custom.css (1)

23-33: Desktop inset alignment looks good.

Using the same --vp-layout-max-width-based offset here as in docs/.vitepress/theme/components/Hero3D.vue Line 132 keeps the home sections aligned with the hero copy across desktop widths.

docs/.vitepress/theme/composables/useThreeScene.ts (1)

366-394: Startup/teardown sequencing looks solid.

Deferring mousemove registration to start() and canceling the RAF plus disposing geometries, materials, composer, and renderer in dispose() closes the lifecycle leak path cleanly.

Comment thread docs/.vitepress/theme/components/Hero3D.vue
bbopen added 2 commits March 20, 2026 13:50
- Skip Three.js canvas init when prefers-reduced-motion: reduce
- Disable shimmer and fade-up CSS animations for reduced motion
- Seed scene with current scrollY on mount for restored scroll positions
@bbopen
Copy link
Copy Markdown
Owner Author

bbopen commented Mar 20, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 20, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/.vitepress/theme/components/Hero3D.vue`:
- Around line 216-231: Add explicit high-contrast keyboard focus styles for the
.btn-primary and .btn-secondary rules: implement :focus and preferably
:focus-visible selectors to show a visible outline/box-shadow (keeping the
9999px border-radius and existing transition), use an accessible color or
increased contrast (and outline-offset for clarity) and ensure the focus styles
do not rely on browser defaults so keyboard users can clearly see focus on the
dark hero background when using the Hero3D.vue buttons.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: ed892be0-2836-426f-8bdb-3a88cdda4705

📥 Commits

Reviewing files that changed from the base of the PR and between 33d424e and eaa7d1a.

📒 Files selected for processing (1)
  • docs/.vitepress/theme/components/Hero3D.vue
📜 Review details
🔇 Additional comments (6)
docs/.vitepress/theme/components/Hero3D.vue (6)

1-14: LGTM!

Imports and setup are correct. Module-level variables appropriately track state across lifecycle hooks, and getBase() correctly falls back to '/' when site.value.base is undefined.


16-26: LGTM!

Scroll throttling correctly implemented with RAF ID tracking for cleanup. The optional chaining on line 23 provides safe handling for the unmount race condition.


28-50: LGTM!

Reduced motion support and scroll position seeding are correctly implemented. The early return at line 35 prevents unnecessary Three.js initialization for motion-sensitive users, and threeScene.onScroll(window.scrollY) on line 45 ensures correct initial state when browser restores scroll position.


52-69: LGTM!

Cleanup is thorough. RAF cancellation prevents post-unmount errors, and proper disposal order ensures no memory leaks from Three.js resources.


72-100: LGTM!

Template correctly implements accessibility with aria-hidden="true" on decorative elements and a descriptive aria-label on the interactive section. Link construction with getBase() properly handles the site base path.


306-315: LGTM!

The prefers-reduced-motion media query correctly disables decorative CSS animations, complementing the JavaScript-side check that skips Three.js canvas initialization.

Comment on lines +216 to +231
.btn-primary,
.btn-secondary {
display: inline-flex;
align-items: center;
justify-content: center;
width: 100%;
padding: 1rem 2.5rem;
font-weight: 700;
font-size: 1.125rem;
border-radius: 9999px;
text-align: center;
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
text-decoration: none;
cursor: pointer;
letter-spacing: 0.05em;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Consider adding explicit focus styles for keyboard navigation.

The buttons rely on browser default focus indicators, which may have insufficient contrast against the dark hero background in some browsers.

♿ Proposed focus styles
 .btn-primary,
 .btn-secondary {
   display: inline-flex;
   align-items: center;
   justify-content: center;
   width: 100%;
   padding: 1rem 2.5rem;
   font-weight: 700;
   font-size: 1.125rem;
   border-radius: 9999px;
   text-align: center;
   transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
   text-decoration: none;
   cursor: pointer;
   letter-spacing: 0.05em;
 }
+
+.btn-primary:focus-visible,
+.btn-secondary:focus-visible {
+  outline: 2px solid `#ffffff`;
+  outline-offset: 2px;
+}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/.vitepress/theme/components/Hero3D.vue` around lines 216 - 231, Add
explicit high-contrast keyboard focus styles for the .btn-primary and
.btn-secondary rules: implement :focus and preferably :focus-visible selectors
to show a visible outline/box-shadow (keeping the 9999px border-radius and
existing transition), use an accessible color or increased contrast (and
outline-offset for clarity) and ensure the focus styles do not rely on browser
defaults so keyboard users can clearly see focus on the dark hero background
when using the Hero3D.vue buttons.

@bbopen
Copy link
Copy Markdown
Owner Author

bbopen commented Mar 20, 2026

@coderabbitai full review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 20, 2026

✅ Actions performed

Full review triggered.

@coderabbitai coderabbitai Bot added the priority:p2 Priority P2 (medium) label Mar 20, 2026
@bbopen bbopen merged commit 5740a4e into main Mar 20, 2026
20 of 21 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:docs Area: documentation area:tooling Area: tooling and CLI documentation Improvements or additions to documentation enhancement New feature or request priority:p2 Priority P2 (medium)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants