feat(docs): add 3D hero visual — cinematic particle network#212
Conversation
|
Caution Review failedPull request was closed or merged during review 📝 WalkthroughWalkthroughReplaces 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 Changes
Sequence DiagramsequenceDiagram
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
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested labels
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
|
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
0915972 to
959e739
Compare
There was a problem hiding this comment.
💡 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' |
There was a problem hiding this comment.
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 👍 / 👎.
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
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
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (11)
.gitignoredocs/.vitepress/config.tsdocs/.vitepress/theme/components/Hero3D.vuedocs/.vitepress/theme/composables/useThreeScene.tsdocs/.vitepress/theme/custom.cssdocs/.vitepress/theme/index.tsdocs/index.mddocs/plans/2026-01-22-tywrapped-ecosystem-vision.mddocs/plans/2026-03-10-tywrap-promotion.mddocs/public/llms-full.txtpackage.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.basefor link paths to support deployment under a subpathdocs/.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
HalfFloatTypefor 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
lineMaterialsenables the pulsing opacity animation, and thelinesGroup.traverse()indispose()properly cleans up all mesh resources.package.json (1)
129-147: LGTM — Three.js dependencies correctly placed in devDependencies.Placing
three,postprocessing, and@types/threeindevDependenciesis 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.
- 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
|
@coderabbitai resolve |
There was a problem hiding this comment.
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
📒 Files selected for processing (3)
docs/.vitepress/theme/components/Hero3D.vuedocs/.vitepress/theme/composables/useThreeScene.tsdocs/.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)
✅ Actions performedComments 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
There was a problem hiding this comment.
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
📒 Files selected for processing (3)
docs/.vitepress/theme/components/Hero3D.vuedocs/.vitepress/theme/composables/useThreeScene.tsdocs/.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 indocs/.vitepress/theme/components/Hero3D.vueLine 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
mousemoveregistration tostart()and canceling the RAF plus disposing geometries, materials, composer, and renderer indispose()closes the lifecycle leak path cleanly.
- 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
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
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
📒 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'/'whensite.value.baseis 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 descriptivearia-labelon the interactive section. Link construction withgetBase()properly handles the site base path.
306-315: LGTM!The
prefers-reduced-motionmedia query correctly disables decorative CSS animations, complementing the JavaScript-side check that skips Three.js canvas initialization.
| .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; | ||
| } |
There was a problem hiding this comment.
🧹 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.
|
@coderabbitai full review |
✅ Actions performedFull review triggered. |
Summary
Add an interactive 3D hero section to the VitePress docs site featuring a Hermes 4-inspired cinematic particle network:
New dev dependencies
three— Three.js corepostprocessing— Bloom, Vignette effects@types/three— type definitionsFiles
docs/.vitepress/theme/composables/useThreeScene.tsdocs/.vitepress/theme/components/Hero3D.vuedocs/.vitepress/theme/custom.cssdocs/.vitepress/theme/index.tslayout-topslotdocs/.vitepress/config.tsappearance: 'force-dark'docs/index.mddocs/plans/Review fixes applied
docs/download_hermes.pyanddocs/check_errors.cjsmousenever updated fromtargetMouse)coreGroupfor consistent parallaxthree/postprocessingtodevDependenciescleanUrlsTest plan
npm run docs:buildpasses