Skip to content

UI Enhancements: ShinyText and PixelBlast#1

Open
ngmkhoi wants to merge 1 commit intovividkit:mainfrom
ngmkhoi:feature/ui-enhancements
Open

UI Enhancements: ShinyText and PixelBlast#1
ngmkhoi wants to merge 1 commit intovividkit:mainfrom
ngmkhoi:feature/ui-enhancements

Conversation

@ngmkhoi
Copy link

@ngmkhoi ngmkhoi commented Mar 4, 2026

Added animated shiny text effects and PixelBlast component to improve landing page aesthetics.

Copilot AI review requested due to automatic review settings March 4, 2026 07:22
@vercel
Copy link

vercel bot commented Mar 4, 2026

@ngmkhoi is attempting to deploy a commit to the Thieu Nguyen's projects Team on Vercel.

A member of the Team first needs to authorize it.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR enhances the landing page UI by introducing animated “shiny” headings and interactive spotlight/pixel background effects, enabled via React components inside the Astro site.

Changes:

  • Added new React components: ShinyText, SpotlightCard, and a WebGL-based PixelBlast.
  • Updated multiple landing-page sections to use the new animated text and spotlight card wrappers.
  • Updated Tailwind/global styling and added React/Tailwind-related dependencies + Astro React integration.

Reviewed changes

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

Show a summary per file
File Description
src/styles/global.css Adds Tailwind animate plugin usage and introduces theme/CSS variable definitions.
src/lib/utils.ts Adds cn() utility for class merging (clsx + tailwind-merge).
src/layouts/GuidesLayout.astro Formatting-only adjustments (quote/style changes) + canonical string formatting.
src/components/sections/Problem.astro Replaces cards/title styling with SpotlightCard + ShinyText.
src/components/sections/Pricing.astro Uses SpotlightCard and ShinyText in pricing section.
src/components/sections/Hero.astro Uses ShinyText for the hero headline.
src/components/sections/Features.astro Uses ShinyText for the section header.
src/components/sections/Commands.astro Uses SpotlightCard and ShinyText for commands section UI.
src/components/sections/ClaudeKit.astro Uses SpotlightCard and ShinyText for ClaudeKit section UI.
src/components/layout/AmbientBackground.astro Swaps static aurora blobs for PixelBlast background.
src/components/SpotlightCard.tsx New React spotlight-hover wrapper component.
src/components/ShinyText.tsx New React animated gradient text component.
src/components/PixelBlast.tsx New Three.js/postprocessing pixel shader background component.
astro.config.mjs Adds @astrojs/react integration.
package.json Adds React + animation/WebGL dependencies (motion, three, postprocessing, etc.).
package-lock.json Locks the newly added dependencies.
components.json Adds shadcn/ui config + aliases.

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

You can also share your feedback on Copilot code review. Take the survey.

Comment on lines 11 to 18
"dependencies": {
"@astrojs/react": "^4.4.2",
"@fontsource/dm-sans": "^5.2.8",
"@fontsource/fira-code": "^5.2.7",
"@fontsource/space-grotesk": "^5.2.10",
"@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3",
"@vercel/analytics": "^1.6.1",
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

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

@types/react and @types/react-dom are listed under dependencies. These are TypeScript-only type packages and typically belong in devDependencies so they aren’t installed/packed for production deploys. Move them to devDependencies (and keep runtime deps limited to code needed at runtime).

Copilot uses AI. Check for mistakes.
Comment on lines +6 to +7
const { class: className = "" } = Astro.props;
import PixelBlast from "@/components/PixelBlast";
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

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

import PixelBlast ... appears after executable code (const { class: ... } = Astro.props;). In Astro frontmatter (ESM), imports must be at the top level before other statements, otherwise this will fail to parse/build. Move the import above the const destructuring (and keep all imports grouped at the top).

Suggested change
const { class: className = "" } = Astro.props;
import PixelBlast from "@/components/PixelBlast";
import PixelBlast from "@/components/PixelBlast";
const { class: className = "" } = Astro.props;

Copilot uses AI. Check for mistakes.
Comment on lines +10 to +22
<div
class={`fixed inset-0 pointer-events-none overflow-hidden aurora-bg ${className}`}
>
<PixelBlast
client:only="react"
variant="square"
pixelSize={4}
color="#2070e7"
patternScale={2}
patternDensity={1}
pixelSizeJitter={0}
enableRipples
rippleSpeed={0.4}
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

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

The wrapper div uses pointer-events-none, but PixelBlast attaches pointerdown/pointermove listeners to its canvas to drive ripples. With pointer events disabled at the parent, the interaction handlers will never fire. Consider removing pointer-events-none or overriding it (e.g., pointer-events-auto) on the PixelBlast element/canvas so ripples work as intended.

Copilot uses AI. Check for mistakes.
Comment on lines 22 to +24
<h2 class="text-4xl md:text-5xl font-heading font-bold mb-6">
{t('pricing.section_title')}
<span class="block bg-gradient-to-r from-green-600 to-blue-600 dark:from-green-400 dark:to-blue-400 bg-clip-text text-transparent">
{t('pricing.section_subtitle')}
</span>
<div class="mb-2">
<ShinyText
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

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

<h2> contains block-level <div> wrappers around the title/subtitle. This is invalid HTML for headings and can negatively affect accessibility/semantics. Prefer <span> (styled as block) inside the heading, or place the wrapper <div>s outside the <h2>.

Copilot uses AI. Check for mistakes.
Comment on lines +33 to +38
<h1
class="font-heading text-5xl md:text-7xl font-bold mb-6 leading-tight fade-in-up"
style="animation-delay: 0.1s;"
>
<div class="mb-2">
<ShinyText
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

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

<h1> contains block-level <div> children. This is invalid heading markup and can break semantic structure for assistive tech. Replace the inner <div>s with <span>s (and apply block/margins via classes) or move the wrappers outside the <h1>.

Copilot uses AI. Check for mistakes.
Comment on lines +132 to +133
// plugins: [],
// };
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

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

There are stray commented-out lines (// plugins: [], etc.) at the bottom of this component file. These don’t appear to be relevant to ShinyText and should be removed to keep the component source clean.

Suggested change
// plugins: [],
// };

Copilot uses AI. Check for mistakes.
Comment on lines +652 to +664
return () => {
if (threeRef.current && mustReinit) return;
if (!threeRef.current) return;
const t = threeRef.current;
t.resizeObserver?.disconnect();
cancelAnimationFrame(t.raf!);
t.quad?.geometry.dispose();
t.material.dispose();
t.composer?.dispose();
t.renderer.dispose();
if (t.renderer.domElement.parentElement === container) container.removeChild(t.renderer.domElement);
threeRef.current = null;
};
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

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

The effect cleanup returns early when mustReinit is true (if (threeRef.current && mustReinit) return;). On initial mount mustReinit is true, so on unmount this cleanup will skip disposing the WebGL renderer/composer/canvas, leaking GPU resources and leaving RAF running. Cleanup should always dispose on unmount; if the goal is to avoid double-dispose during re-init, gate that differently (e.g., track a separate flag or always dispose in cleanup and remove the manual dispose at re-init).

Copilot uses AI. Check for mistakes.
Comment on lines 53 to +55
<h2 class="text-4xl md:text-5xl font-heading font-bold mb-6">
{t('problem.title')}
<span class="block bg-gradient-to-r from-red-600 to-orange-600 dark:from-red-400 dark:to-orange-400 bg-clip-text text-transparent">
{t('problem.subtitle')}
</span>
<div class="mb-2">
<ShinyText
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

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

<h2> contains block-level <div> children. This is invalid HTML (headings should only contain phrasing content), and can confuse screen readers / document outline. Use <span> wrappers (display:block via CSS) or move the block wrappers outside the heading.

Copilot uses AI. Check for mistakes.
setPosition({ x: e.clientX - rect.left, y: e.clientY - rect.top });
};

const handleFocus = () => {
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

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

On keyboard focus, handleFocus sets opacity but never updates position, so the spotlight will render at the default (0,0) until the user moves the mouse. Consider setting position on focus (e.g., center of the card or based on the focused element’s bounding box) so keyboard users get a sensible highlight location.

Suggested change
const handleFocus = () => {
const handleFocus = () => {
if (divRef.current) {
const rect = divRef.current.getBoundingClientRect();
setPosition({ x: rect.width / 2, y: rect.height / 2 });
}

Copilot uses AI. Check for mistakes.
Comment on lines +1 to +5
import { clsx, type ClassValue } from "clsx"
import { twMerge } from "tailwind-merge"

export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

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

src/lib/utils.ts uses double quotes and omits semicolons, which is inconsistent with the prevailing TS style in this repo (e.g. src/i18n/utils.ts and src/pages/robots.txt.ts use single quotes + semicolons). Align this new utility file with the existing formatting to avoid style drift/noisy diffs.

Suggested change
import { clsx, type ClassValue } from "clsx"
import { twMerge } from "tailwind-merge"
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
import { clsx, type ClassValue } from 'clsx';
import { twMerge } from 'tailwind-merge';
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));

Copilot uses AI. Check for mistakes.
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