(SP 2) [Frontend] Update Features section content and improve mobile UX#224
Conversation
- Features Section: Refined feature content and visuals. - Mobile UX: Improved responsive layout and scaling for feature cards and interactive elements. - Visual Enhancements: Added dynamic particle background effects to the Pricing section.
✅ Deploy Preview for develop-devlovers ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
📝 WalkthroughWalkthroughAdds global animation utilities/keyframes, three new UI primitives (GradientBadge, SectionHeading, ParticleCanvas), and refactors multiple About-page sections to a data-driven, responsive implementation with accessibility and styling updates. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant User as "User"
participant Pricing as "PricingSection"
participant Canvas as "ParticleCanvas"
participant RAF as "requestAnimationFrame"
rect rgba(135,206,235,0.5)
User->>Pricing: hover/focus pricing card (select shape)
end
Pricing->>Pricing: set activeShape state
Pricing->>Canvas: pass updated props.activeShape
Canvas->>Canvas: begin shape transition (easeInOutCubic)
Canvas->>RAF: requestAnimationFrame(loop)
RAF->>Canvas: render frame (update particle positions toward targets)
Canvas-->>Pricing: canvas visuals update
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested labels
Suggested reviewers
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
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 |
There was a problem hiding this comment.
Actionable comments posted: 5
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
frontend/components/about/SponsorsWall.tsx (1)
46-62: Addrel="noopener noreferrer"to external links opened in a new tab.
This prevents reverse‑tabnabbing and is a standard security hardening step.🔒 Proposed fix
- <Link - href="https://github.com/sponsors/DevLoversTeam" - target="_blank" + <Link + href="https://github.com/sponsors/DevLoversTeam" + target="_blank" + rel="noopener noreferrer" aria-label="Become a sponsor to claim your spot" className="relative flex items-center justify-center w-10 h-10 rounded-full border-2 border-dashed border-gray-300 dark:border-white/20 hover:border-[`#1e5eff`] dark:hover:border-[`#ff2d55`] bg-transparent hover:bg-[`#1e5eff`]/5 dark:hover:bg-[`#ff2d55`]/5 transition-all group/cta" > … - <Link href="https://github.com/sponsors/DevLoversTeam" target="_blank" className="hidden sm:block ml-3 mr-2 text-xs font-bold text-gray-500 hover:text-[`#1e5eff`] dark:hover:text-[`#ff2d55`] transition-colors"> + <Link href="https://github.com/sponsors/DevLoversTeam" target="_blank" rel="noopener noreferrer" className="hidden sm:block ml-3 mr-2 text-xs font-bold text-gray-500 hover:text-[`#1e5eff`] dark:hover:text-[`#ff2d55`] transition-colors">frontend/components/about/CommunitySection.tsx (1)
49-92: Addrel="noopener noreferrer"to external links opened in a new tab.
This prevents reverse‑tabnabbing and is a standard security hardening step.🔒 Proposed fix
- <Link - href="https://github.com/DevLoversTeam/devlovers.net/discussions" - target="_blank" + <Link + href="https://github.com/DevLoversTeam/devlovers.net/discussions" + target="_blank" + rel="noopener noreferrer" className="group inline-flex items-center gap-1.5 px-4 py-2 rounded-full … - <Link - href="https://github.com/DevLoversTeam/devlovers.net/discussions" - target="_blank" + <Link + href="https://github.com/DevLoversTeam/devlovers.net/discussions" + target="_blank" + rel="noopener noreferrer" className="group relative hidden md:inline-flex items-center justify-center gap-4 p-1.5 pl-6 pr-1.5 rounded-full
🤖 Fix all issues with AI agents
In `@frontend/components/about/FeaturesSection.tsx`:
- Around line 42-44: The string for blog.description is grammatically incorrect
("Stay updated. detailed articles..."); update the user-facing copy for the
"blog.description" key so it is a single, well-formed sentence (e.g., "Stay
updated with detailed articles on tech trends, coding tutorials, and industry
insights to keep you ahead of the curve.") by editing the value in
FeaturesSection.tsx where the "blog.description" key is defined.
In `@frontend/components/about/HeroSection.tsx`:
- Around line 32-34: Remove the aria-hidden attribute from the wrapper div that
renders the InteractiveGame component so its interactive buttons and keyboard
handlers (e.g., the X exit, Retry, play area, Space/ArrowUp, Escape handlers in
InteractiveGame) remain accessible to screen readers; instead, if you need
purely decorative semantics for the wrapper, convert it to a non-hiding
decorative element (or add an appropriate aria-label/role="img" on a separate
decorative element) and ensure the wrapper does not conceal focusable children
or their event handlers.
In `@frontend/components/ui/gradient-badge.tsx`:
- Around line 9-16: The GradientBadge component is concatenating className
directly which can emit a literal "undefined" token when className is not
provided; update the GradientBadge render to guard/normalize the prop (e.g.,
default className to an empty string or use conditional concatenation) so the
template string never includes "undefined" — adjust the GradientBadge function
signature or the className interpolation to use (className || '') or similar to
ensure safe classes.
In `@frontend/components/ui/particle-canvas.tsx`:
- Around line 147-345: Effect reinitializes 800 particles every time activeShape
changes, causing visible jumps; stop recreating particles by removing
activeShape from the useEffect dependency and instead read activeShape via a
ref. Change the effect to use an empty dependency array so initParticles,
resizeCanvas and draw run only once, add a ref (e.g., activeShapeRef) that you
update inside a separate useEffect when activeShape changes, and have
draw/readers use activeShapeRef.current (and prevShapeRef.current) instead of
the activeShape prop; keep particles.current, timeRef.current,
animationFrameId.current, initParticles and draw intact to preserve the running
loop and only update shape state via the ref to avoid teardown/reinit.
In `@frontend/components/ui/section-heading.tsx`:
- Around line 9-12: The JSX in SectionHeading is concatenating className
directly which stringifies undefined into "undefined"; update the SectionHeading
signature or render logic to provide a safe default (e.g., default className =
"" in the function parameters or normalize className before use) and use that
safe value in the template string so the literal "undefined" is never emitted;
reference SectionHeading and the className prop to locate and fix the code.
🧹 Nitpick comments (2)
frontend/app/globals.css (1)
334-372: Respectprefers-reduced-motionfor the new infinite animations.
These run continuously and should be disabled for users who request reduced motion.♿ Suggested accessibility tweak
+@media (prefers-reduced-motion: reduce) { + .animate-float, + .animate-spin-slow, + .animate-spin-slower, + .animate-dash-flow { + animation: none !important; + } +}frontend/components/about/PricingSection.tsx (1)
45-49: Mirror hover-driven visuals for keyboard focus.
Right now only pointer hover updatesactiveShape. Add focus/blur handlers so keyboard users get the same visual feedback.♿ Suggested improvement
<motion.div whileHover={{ y: -5 }} onMouseEnter={() => setActiveShape("brackets")} onMouseLeave={() => setActiveShape(null)} + onFocus={() => setActiveShape("brackets")} + onBlur={() => setActiveShape(null)} className="flex flex-col p-8 lg:p-10 rounded-3xl border border-gray-200 dark:border-neutral-800 bg-white/10 dark:bg-neutral-900/10 backdrop-blur-md shadow-sm relative z-10" > @@ <motion.div whileHover={{ y: -5 }} onMouseEnter={() => setActiveShape("heart")} onMouseLeave={() => setActiveShape(null)} + onFocus={() => setActiveShape("heart")} + onBlur={() => setActiveShape(null)} className="relative flex flex-col p-8 lg:p-10 rounded-3xl overflow-hidden backdrop-blur-md z-10 border border-[`#1e5eff`]/30 dark:border-[`#ff2d55`]/30 bg-gradient-to-b from-[`#1e5eff`]/5 to-white/10 dark:from-[`#ff2d55`]/10 dark:to-neutral-900/10" >Also applies to: 87-91
| useEffect(() => { | ||
| const canvas = canvasRef.current | ||
| if (!canvas) return | ||
|
|
||
| const ctx = canvas.getContext("2d") | ||
| if (!ctx) return | ||
|
|
||
| const resizeCanvas = () => { | ||
| canvas.width = canvas.parentElement?.offsetWidth || window.innerWidth | ||
| canvas.height = canvas.parentElement?.offsetHeight || window.innerHeight | ||
| initParticles(canvas.width, canvas.height) | ||
| } | ||
|
|
||
| const initParticles = (width: number, height: number) => { | ||
| const count = 800 | ||
| particles.current = [] | ||
|
|
||
| for (let i = 0; i < count; i++) { | ||
| const floatX = Math.random() * width | ||
| const floatY = Math.random() * height | ||
|
|
||
| const speed = 0.2 + Math.random() * 0.3 | ||
| const angle = Math.random() * Math.PI * 2 | ||
|
|
||
| particles.current.push({ | ||
| x: floatX, | ||
| y: floatY, | ||
| z: 0, | ||
| vx: 0, | ||
| vy: 0, | ||
| vz: 0, | ||
| floatX, | ||
| floatY, | ||
| floatVx: Math.cos(angle) * speed, | ||
| floatVy: Math.sin(angle) * speed, | ||
| }) | ||
| } | ||
| } | ||
|
|
||
| const draw = () => { | ||
| if (!canvas || !ctx) return | ||
| ctx.clearRect(0, 0, canvas.width, canvas.height) | ||
|
|
||
| timeRef.current += 0.016 | ||
|
|
||
| // Linear transition - no initial freeze | ||
| const inSpeed = 0.012 | ||
| const outSpeed = 0.006 // Slower out | ||
| if (targetTransitionRef.current > transitionRef.current) { | ||
| // Going IN | ||
| transitionRef.current = Math.min(targetTransitionRef.current, transitionRef.current + inSpeed) | ||
| } else if (targetTransitionRef.current < transitionRef.current) { | ||
| // Going OUT - slow and smooth | ||
| transitionRef.current = Math.max(targetTransitionRef.current, transitionRef.current - outSpeed) | ||
| } | ||
|
|
||
| // Apply smooth easing | ||
| const rawProgress = Math.max(0, Math.min(1, transitionRef.current)) | ||
| const progress = easeInOutCubic(rawProgress) | ||
|
|
||
| const { r, g, b, isDark } = getThemeColors() | ||
|
|
||
| const isMobileView = canvas.width < 768 | ||
|
|
||
| const currentShape = activeShape || prevShapeRef.current | ||
| const shapePoints = | ||
| currentShape === "brackets" | ||
| ? SHAPE_POINTS_BRACKETS | ||
| : currentShape === "heart" | ||
| ? SHAPE_POINTS_HEART | ||
| : null | ||
|
|
||
| // 3D rotation - always moving | ||
| const angle = timeRef.current * 0.4 | ||
| const cos = Math.cos(angle) | ||
| const sin = Math.sin(angle) | ||
|
|
||
| particles.current.forEach((p, i) => { | ||
| // Always update floating positions | ||
| p.floatX += p.floatVx | ||
| p.floatY += p.floatVy | ||
|
|
||
| if (p.floatX < 0 || p.floatX > canvas.width) { | ||
| p.floatVx *= -1 | ||
| p.floatX = Math.max(0, Math.min(canvas.width, p.floatX)) | ||
| } | ||
| if (p.floatY < 0 || p.floatY > canvas.height) { | ||
| p.floatVy *= -1 | ||
| p.floatY = Math.max(0, Math.min(canvas.height, p.floatY)) | ||
| } | ||
|
|
||
| // Calculate shape position | ||
| let shapeX = p.floatX | ||
| let shapeY = p.floatY | ||
|
|
||
| if (isMobileView) { | ||
| // Mobile: show both shapes - first half brackets, second half heart | ||
| const halfCount = Math.floor(particles.current.length / 2) | ||
| const isFirstHalf = i < halfCount | ||
| const localIndex = isFirstHalf ? i : i - halfCount | ||
| const mobileShapePoints = isFirstHalf ? SHAPE_POINTS_BRACKETS : SHAPE_POINTS_HEART | ||
| const shapePoint = mobileShapePoints[localIndex % mobileShapePoints.length] | ||
|
|
||
| const rotatedX = shapePoint.x * cos - shapePoint.z * sin | ||
| const rotatedZ = shapePoint.x * sin + shapePoint.z * cos | ||
| const rotatedY = shapePoint.y | ||
| const perspective = 3 | ||
| const scale = perspective / (perspective + rotatedZ) | ||
| const shapeScale = Math.min(canvas.width, canvas.height) * 0.7 | ||
|
|
||
| // Position: centered on each card | ||
| const centerX = canvas.width / 2 | ||
| const centerY = isFirstHalf ? canvas.height * 0.32 : canvas.height * 0.68 | ||
|
|
||
| shapeX = centerX + rotatedX * shapeScale * scale | ||
| shapeY = centerY + rotatedY * shapeScale * scale | ||
| } else if (shapePoints) { | ||
| // Desktop: original hover behavior | ||
| let centerX = canvas.width / 2 | ||
| const centerY = canvas.height / 2 | ||
| if (currentShape === "brackets") centerX = canvas.width * 0.25 | ||
| if (currentShape === "heart") centerX = canvas.width * 0.75 | ||
|
|
||
| const shapePoint = shapePoints[i % shapePoints.length] | ||
| const rotatedX = shapePoint.x * cos - shapePoint.z * sin | ||
| const rotatedZ = shapePoint.x * sin + shapePoint.z * cos | ||
| const rotatedY = shapePoint.y | ||
| const perspective = 3 | ||
| const scale = perspective / (perspective + rotatedZ) | ||
| const shapeScale = Math.min(canvas.width, canvas.height) * 0.35 | ||
| shapeX = centerX + rotatedX * shapeScale * scale | ||
| shapeY = centerY + rotatedY * shapeScale * scale | ||
| } | ||
|
|
||
| // Target position - on mobile always full shape, on desktop based on hover progress | ||
| const effectiveProgress = isMobileView ? 1 : progress | ||
| const targetX = p.floatX + (shapeX - p.floatX) * effectiveProgress | ||
| const targetY = p.floatY + (shapeY - p.floatY) * effectiveProgress | ||
|
|
||
| // Smooth spring physics - gentle force, high damping | ||
| const springForce = 0.04 // Gentle spring | ||
| const damping = 0.92 // High damping for smooth motion | ||
|
|
||
| // Add spring force toward target | ||
| p.vx += (targetX - p.x) * springForce | ||
| p.vy += (targetY - p.y) * springForce | ||
|
|
||
| // Apply damping | ||
| p.vx *= damping | ||
| p.vy *= damping | ||
|
|
||
| // Update position | ||
| p.x += p.vx | ||
| p.y += p.vy | ||
|
|
||
| // Alpha - on mobile always visible, on desktop based on progress | ||
| const alpha = isMobileView ? 0.3 : (0.15 + 0.45 * progress) | ||
|
|
||
| ctx.beginPath() | ||
| ctx.arc(p.x, p.y, 1.5, 0, Math.PI * 2) | ||
| ctx.fillStyle = `rgba(${r}, ${g}, ${b}, ${alpha})` | ||
| ctx.fill() | ||
| }) | ||
|
|
||
| // Connecting lines when in shape | ||
| if (progress > 0.5 && shapePoints) { | ||
| const lineAlpha = (progress - 0.5) * 0.2 | ||
| ctx.globalAlpha = lineAlpha | ||
| ctx.lineWidth = 0.5 | ||
| ctx.strokeStyle = isDark ? "#ff2d55" : "#1e5eff" | ||
| ctx.beginPath() | ||
|
|
||
| for (let i = 0; i < particles.current.length; i += 4) { | ||
| const p1 = particles.current[i] | ||
| for (let j = 1; j <= 4; j++) { | ||
| const p2 = particles.current[(i + j) % particles.current.length] | ||
| const distSq = (p1.x - p2.x) ** 2 + (p1.y - p2.y) ** 2 | ||
| if (distSq < 35 * 35) { | ||
| ctx.moveTo(p1.x, p1.y) | ||
| ctx.lineTo(p2.x, p2.y) | ||
| } | ||
| } | ||
| } | ||
| ctx.stroke() | ||
| ctx.globalAlpha = 1.0 | ||
| } | ||
|
|
||
| animationFrameId.current = requestAnimationFrame(draw) | ||
| } | ||
|
|
||
| resizeCanvas() | ||
| window.addEventListener("resize", resizeCanvas) | ||
| draw() | ||
|
|
||
| return () => { | ||
| window.removeEventListener("resize", resizeCanvas) | ||
| if (animationFrameId.current) cancelAnimationFrame(animationFrameId.current) | ||
| } | ||
| }, [activeShape]) |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# Check if the file exists and examine the actual useEffect dependency array
fd "particle-canvas.tsx" -t fRepository: DevLoversTeam/devlovers.net
Length of output: 113
🏁 Script executed:
# Get the actual file content around lines 147-345
if [ -f "frontend/components/ui/particle-canvas.tsx" ]; then
wc -l "frontend/components/ui/particle-canvas.tsx"
fiRepository: DevLoversTeam/devlovers.net
Length of output: 117
🏁 Script executed:
# Read the main useEffect to check the dependency array
if [ -f "frontend/components/ui/particle-canvas.tsx" ]; then
sed -n '147,350p' "frontend/components/ui/particle-canvas.tsx" | tail -10
fiRepository: DevLoversTeam/devlovers.net
Length of output: 328
🏁 Script executed:
# Read the full useEffect starting from line 147
sed -n '147,348p' "frontend/components/ui/particle-canvas.tsx"Repository: DevLoversTeam/devlovers.net
Length of output: 7237
🏁 Script executed:
# Check the beginning of the file to see the component structure and refs
sed -n '1,50p' "frontend/components/ui/particle-canvas.tsx"Repository: DevLoversTeam/devlovers.net
Length of output: 1217
🏁 Script executed:
# Verify there's no other initParticles call in the file
rg "initParticles" "frontend/components/ui/particle-canvas.tsx"Repository: DevLoversTeam/devlovers.net
Length of output: 182
Avoid reinitializing particles on every hover change.
The effect depends on activeShape, causing teardown and reinitialization of 800 particles on each hover state change. This creates visible animation jumps. Keep the animation loop stable by using an empty dependency array and reading the current shape via a ref instead.
🛠️ Suggested fix
export function ParticleCanvas({ activeShape, className }: ParticleCanvasProps) {
const canvasRef = useRef<HTMLCanvasElement>(null)
const particles = useRef<Point[]>([])
const animationFrameId = useRef<number | undefined>(undefined)
const timeRef = useRef(0)
// Simple transition: 0 = floating, 1 = shape
const transitionRef = useRef(0)
const targetTransitionRef = useRef(0)
const prevShapeRef = useRef<"brackets" | "heart" | null>(null)
const justLeftShapeRef = useRef(false)
+ const activeShapeRef = useRef<"brackets" | "heart" | null>(activeShape)
useEffect(() => {
+ activeShapeRef.current = activeShape
const wasInShape = targetTransitionRef.current === 1
targetTransitionRef.current = activeShape ? 1 : 0
if (activeShape) {
prevShapeRef.current = activeShape
justLeftShapeRef.current = false
} else if (wasInShape) {
justLeftShapeRef.current = true
// Gentle nudge instead of hard push
particles.current.forEach(p => {
p.vx *= 0.5
p.vy *= 0.5
})
}
}, [activeShape])
useEffect(() => {
const canvas = canvasRef.current
if (!canvas) return
@@
- const currentShape = activeShape || prevShapeRef.current
+ const currentShape = activeShapeRef.current || prevShapeRef.current
@@
- }, [activeShape])
+ }, [])🤖 Prompt for AI Agents
In `@frontend/components/ui/particle-canvas.tsx` around lines 147 - 345, Effect
reinitializes 800 particles every time activeShape changes, causing visible
jumps; stop recreating particles by removing activeShape from the useEffect
dependency and instead read activeShape via a ref. Change the effect to use an
empty dependency array so initParticles, resizeCanvas and draw run only once,
add a ref (e.g., activeShapeRef) that you update inside a separate useEffect
when activeShape changes, and have draw/readers use activeShapeRef.current (and
prevShapeRef.current) instead of the activeShape prop; keep particles.current,
timeRef.current, animationFrameId.current, initParticles and draw intact to
preserve the running loop and only update shape state via the ref to avoid
teardown/reinit.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@frontend/components/about/PricingSection.tsx`:
- Line 20: The section's aria-labelledby="pricing-heading" is broken because no
element with id "pricing-heading" is rendered; fix by ensuring a heading with
that id exists—either extend SectionHeading to accept and pass an id prop
through to its rendered <h2> (update SectionHeading's props to include id and
forward it) or, more simply, add a visually-hidden <h2
id="pricing-heading">Pricing</h2> inside PricingSection (use your app's existing
utility class for visually-hidden) so the aria reference resolves and
accessibility is restored.
🧹 Nitpick comments (4)
frontend/components/ui/particle-canvas.tsx (1)
349-349: Consider addingaria-hidden="true"for accessibility clarity.Since this canvas is purely decorative, explicitly marking it as hidden from assistive technologies would be beneficial.
♻️ Suggested improvement
- return <canvas ref={canvasRef} className={className} /> + return <canvas ref={canvasRef} className={className} aria-hidden="true" />frontend/components/about/PricingSection.tsx (1)
11-11: Unused import:cnutility is not used in this file.The
cnutility is imported but never referenced in the component.♻️ Remove unused import
-import { cn } from "@/lib/utils"frontend/components/about/FeaturesSection.tsx (2)
276-303: Consider enhancing tab accessibility with ARIA tab pattern.The current implementation uses
aria-currentwhich is acceptable, but for a tab-like interface, the ARIA tabs pattern (role="tablist",role="tab",aria-selected) would provide better screen reader semantics.♻️ Enhanced accessibility pattern
function TabButton({ page, isActive, onClick }: { page: Page; isActive: boolean; onClick: () => void }) { return ( <button onClick={onClick} - aria-label={t(`${page.id}.title`)} - aria-current={isActive ? 'true' : 'false'} + role="tab" + aria-selected={isActive} + aria-controls={`${page.id}-panel`} className={`relative p-2.5 md:px-5 md:py-2.5 rounded-full...`} >And update the container in
FeaturesSection:- <div className="inline-flex gap-1 md:gap-2 p-1.5 rounded-full..."> + <div role="tablist" aria-label="Feature categories" className="inline-flex gap-1 md:gap-2 p-1.5 rounded-full...">
307-315: Potential hydration mismatch withisMobilestate.The
isMobilestate initializes tofalseon the server but may betrueon the client if the viewport is narrow. This can cause a hydration mismatch warning in Next.js. Since it only affects visual scaling (not functionality), consider initializing based on a CSS approach or using a hook that handles SSR gracefully.♻️ SSR-safe alternative using CSS
Instead of JavaScript-based detection, consider using CSS custom properties or responsive Tailwind classes directly in
FeatureBubble:// In FeatureBubble, replace isMobile prop usage with responsive classes: const posX = feature.x // Use base value const posY = feature.y // Then use CSS transforms with media queries in className or inline styles style={{ '--scale-x': 'var(--mobile-scale-x, 1)', '--scale-y': 'var(--mobile-scale-y, 1)', }}Or suppress the warning if the visual flash is acceptable:
- const [isMobile, setIsMobile] = useState(false) + const [isMobile, setIsMobile] = useState(false) + const [mounted, setMounted] = useState(false) + + useEffect(() => { + setMounted(true) + }, [])
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@frontend/components/about/FeaturesSection.tsx`:
- Around line 276-395: The TabButton's aria-controls points to
`${page.id}-panel` but there is no corresponding panel element — update
TabButton to add an id (e.g. id={`${page.id}-tab`}) so each button has an
accessible tab id, and then update the panel container (the motion.div keyed by
activeTab inside FeaturesSection that renders the features) to include
role="tabpanel", id={`${activeTab}-panel`} and
aria-labelledby={`${activeTab}-tab`} so the tab/tabpanel ARIA relationship is
correctly wired; touch up attributes on TabButton (aria-controls remains
`${page.id}-panel`) and the panel container (role/id/aria-labelledby)
accordingly.
🧹 Nitpick comments (1)
frontend/components/ui/particle-canvas.tsx (1)
188-219: Consider using actual delta time for frame-rate independence.The hardcoded
0.016increment assumes 60fps. On high-refresh displays (120/144Hz), animations will appear faster. For decorative purposes this is acceptable, but usingperformance.now()delta would ensure consistent animation speed across devices.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@frontend/components/ui/particle-canvas.tsx`:
- Around line 189-199: The draw function can get a huge deltaTime after tab
backgrounding; clamp deltaTime right after it's computed in draw (where
deltaTime is derived from lastTimeRef and timestamp) to a sensible max (e.g.,
0.03–0.1s) before updating lastTimeRef.current and timeRef.current so particles
don't jump; update the code that computes deltaTime in draw to set deltaTime =
Math.min(deltaTime, MAX_DELTA) (define MAX_DELTA as a constant) and then proceed
with clearing the canvas and advancing timeRef.current.
🧹 Nitpick comments (2)
frontend/components/about/FeaturesSection.tsx (2)
136-185: Respect reduced-motion preferences for heavy animations.
This section uses multiple continuous animations; consider honoring reduced-motion settings to improve accessibility.♿ Suggested adjustment
-import { motion, AnimatePresence } from "framer-motion" +import { motion, AnimatePresence, useReducedMotion } from "framer-motion" @@ function FeatureBubble({ feature, index, href, isMobile }: { feature: Feature; index: number; href: string; isMobile: boolean }) { + const shouldReduceMotion = useReducedMotion() const Icon = feature.icon @@ - transition={{ - x: { delay: 0.1 + index * 0.08, duration: 0.6, type: "spring", bounce: 0.3 }, - y: { delay: 0.1 + index * 0.08, duration: 0.6, type: "spring", bounce: 0.3 }, - opacity: { delay: 0.1 + index * 0.08, duration: 0.3 }, - scale: { delay: 0.1 + index * 0.08, duration: 0.5, type: "spring", bounce: 0.4 }, - }} + transition={ + shouldReduceMotion + ? { duration: 0 } + : { + x: { delay: 0.1 + index * 0.08, duration: 0.6, type: "spring", bounce: 0.3 }, + y: { delay: 0.1 + index * 0.08, duration: 0.6, type: "spring", bounce: 0.3 }, + opacity: { delay: 0.1 + index * 0.08, duration: 0.3 }, + scale: { delay: 0.1 + index * 0.08, duration: 0.5, type: "spring", bounce: 0.4 }, + } + } @@ - <div - className="animate-float" + <div + className="animate-float motion-reduce:animate-none" style={{ animationDelay: `${floatDelay}s`, animationDuration: `${floatDuration}s` }} >
276-304: Add keyboard arrow navigation for the tablist.
Withrole="tablist", users expect left/right arrow support and rovingtabIndex. Right now only clicks/Tab work.⌨️ Suggested implementation
-function TabButton({ page, isActive, onClick }: { page: Page; isActive: boolean; onClick: () => void }) { +function TabButton({ + page, + isActive, + onClick, + onKeyDown, +}: { + page: Page + isActive: boolean + onClick: () => void + onKeyDown: (e: React.KeyboardEvent<HTMLButtonElement>) => void +}) { return ( <button + type="button" id={`${page.id}-tab`} onClick={onClick} + onKeyDown={onKeyDown} role="tab" aria-selected={isActive} aria-controls={`${page.id}-panel`} + tabIndex={isActive ? 0 : -1} @@ export function FeaturesSection() { const [activeTab, setActiveTab] = useState("qa") const [isMobile, setIsMobile] = useState(false) const activePage = pages.find(p => p.id === activeTab) || pages[0] + const onTabKeyDown = (event: React.KeyboardEvent<HTMLButtonElement>, index: number) => { + if (event.key !== "ArrowRight" && event.key !== "ArrowLeft") return + event.preventDefault() + const dir = event.key === "ArrowRight" ? 1 : -1 + const nextIndex = (index + dir + pages.length) % pages.length + const nextId = pages[nextIndex].id + setActiveTab(nextId) + document.getElementById(`${nextId}-tab`)?.focus() + } @@ - {pages.map((page) => ( + {pages.map((page, index) => ( <TabButton key={page.id} page={page} isActive={activeTab === page.id} onClick={() => setActiveTab(page.id)} + onKeyDown={(e) => onTabKeyDown(e, index)} /> ))}Also applies to: 307-382
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@frontend/components/about/FeaturesSection.tsx`:
- Around line 327-349: The component currently returns null until the client
mount (mounted guard), removing the section from SSR output; remove the mounted
state and early return so the FeaturesSection renders on the server, and instead
initialize and update isMobile safely for both SSR and client: initialize
isMobile using a window-safe check (e.g. typeof window !== "undefined" ?
window.innerWidth < 768 : false) and keep the existing useEffect to attach the
resize listener and call checkMobile on mount; leave onTabKeyDown, pages, and
setActiveTab logic as-is but ensure DOM focus calls are guarded by the same
window/document availability (or kept inside the setTimeout as present) so no
runtime errors occur during SSR.
🧹 Nitpick comments (1)
frontend/components/ui/particle-canvas.tsx (1)
157-161: Account for devicePixelRatio to avoid blurry canvas on HiDPI.Line 157–161 sets canvas size in CSS pixels only, so the drawing can look soft on retina displays. Scale the backing store by DPR and keep CSS size unchanged.
♻️ Proposed fix
const resizeCanvas = () => { - canvas.width = canvas.parentElement?.offsetWidth || window.innerWidth - canvas.height = canvas.parentElement?.offsetHeight || window.innerHeight - initParticles(canvas.width, canvas.height) + const width = canvas.parentElement?.offsetWidth || window.innerWidth + const height = canvas.parentElement?.offsetHeight || window.innerHeight + const dpr = window.devicePixelRatio || 1 + canvas.width = width * dpr + canvas.height = height * dpr + canvas.style.width = `${width}px` + canvas.style.height = `${height}px` + ctx.setTransform(dpr, 0, 0, dpr, 0, 0) + initParticles(width, height) }
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Fix all issues with AI agents
In `@frontend/components/ui/particle-canvas.tsx`:
- Around line 246-253: Boundary checks in the draw loop compare particle logical
positions (p.floatX / p.floatY) against canvas.width / canvas.height
(device-pixel-scaled), causing particles to drift off-screen on high-DPR
displays; update the resize handler (where logical dimensions are computed) to
store logicalWidth and logicalHeight in a ref (component-level), then use those
refs inside the draw function instead of canvas.width/canvas.height when
clamping and reflecting p.floatX/p.floatY and flipping p.floatVx/p.floatVy;
alternatively ensure you consistently scale positions by DPR before
comparing—apply this change to the draw logic that handles p.floatX, p.floatY,
p.floatVx, and p.floatVy so boundary checks use the same coordinate space as
initialization.
- Line 226: The mobile detection currently uses canvas.width (which is in
physical pixels) so isMobileView is wrong on HiDPI displays; change the check to
use the logical width (e.g., compute logicalWidth = canvas.width /
window.devicePixelRatio or getBoundingClientRect().width) and replace
occurrences of canvas.width in the isMobileView computation and the draw
function with this logicalWidth (or store it in a ref like logicalDimensionsRef)
so all layout decisions use logical pixels consistently; update the isMobileView
assignment and any conditional branches in the draw method/particle-canvas
component to use that logical value.
* (SP: 1) [Security] Enforce origin posture for shop APIs (admin/checkout same-origin; internal/webhooks non-browser) + docs * (SP: 1) [DB] Align stripe_events.order_id FK CASCADE across schema and migrations * feat(i18n): localize quiz anti-cheat, header and blog filters (#175) * feat(Blog): Adding last published post to the blog and category page, recommended posts, Changing styles to one unified format, Bug fixes * Update leaderboard-style * Update leaderboard-style * teat(Blog): fix of hover on author, fix of the line * (SP: 3) [Observability] Extend structured logging + correlation IDs across all shop routes; purge console.*; enforce explicit error codes * (SP: 1) [Admin][Security] Add safe product delete (PRODUCT_IN_USE) + mobile cards UI; tighten env/docs, locale normalization, cache-control, and logging semantics * fix: npm installing * (SP: 1) [Admin] Align products list in use checks with DB column names (order_items/inventory_moves) * fix:Update leaderboard: fixed background * fix: remove email from CurrentUser type to prevent PII exposure * refactor: align leaderboard UI with brand style (fixed bg, css vars, podium glow) * (SP: 1) [Frontend] About Us Page. Fixed game, topics, mobile layout - Fixed mobile tabs in FeaturesSection (icon-only on mobile) - Fixed game bugs: collision detection, animation, scoring system - Added multiple obstacle types with level progression - Improved game sizing for mobile while preserving desktop - Updated TopicsSection with local SVG icons and hover borders - Made DynamicGridBackground static grid opt-in via showStaticGrid prop - Limited SponsorsWall to display max 10 sponsors - Optimized CommunitySection button layout for mobile * refactor: update accent color hover effects to Footer - Update Footer links to use --accent-primary on hover - Update ThemeToggle icons to use --accent-primary on hover - Both components adapt colors to light/dark theme * refactor: improve accessibility - Add focus-visible styles for keyboard navigation accessibility * (SP: 1) [Shop] Fix Stripe checkout success redirect (remove duplicate locale /uk/uk) (#186) * fix:leaderboard update leaderboard-style * fix:leaderboard update leaderboard-style (#187) * fix:leaderboard update leaderboard-style on mobile * fix: resolve CodeRabbit issues and conflicts * fix:leaderboard update leaderboard-style on mobile (#188) * fix:leaderboard update leaderboard-style * fix:leaderboard update leaderboard-style on mobile * fix: resolve CodeRabbit issues and conflicts --------- Co-authored-by: Viktor Svertoka <victor.svertoka@gmail.com> * (SP: 1) [Frontend] Changing hero headline on shop main page (#190) * (SP: 1) [Shop] Fix checkout redirect 404 by removing duplicate locale in in-app routes and Stripe return_url * (SP: 1) [Shop] Fix locale cart page and orderid page * (SP: 1) [Frontend] Changin hero headline on shop main page * (SP: 1) [Frontend] Fix styles shop home page, buttons (#191) * fix: move row border to first cell to resolve CodeRabbit issue * fix: move row border to first cell to resolve CodeRabbit issue new * Feature/leaderboard style update (#192) * (SP: 7) [UI] Quiz UI polish: tabs styling, category accents, color scheme (#181, #193, #194) (#195) * Sanity (#196) * feat(Blog):fix for clickable link in post details, fix for author details * feat(Blog):refactoring after removing author modal * feat(Blog): fix unified date format * (SP: 3) [AI] Add AI word helper with Groq integration (#200) * (SP: 3) [AI] Add AI word helper with Groq integration - Implement Groq API with Llama 3.1 70B model - Add text selection detection on Q&A page - Create floating "Explain" button - Build draggable modal with 3-language support (uk/en/pl) - Add localStorage caching for instant repeated lookups - Implement guest CTA (login/signup) - Add rate limiting (10 requests/min) - Auth-gated feature (registered users only) Components: - SelectableText: Detects text selection - FloatingExplainButton: Appears on selection - AIWordHelper: Main modal with explanations * (SP: 1) i18n: fix Polish locale and set EN as default * Sanity (#202) * feat(Blog):fix for clickable link in post details, fix for author details * feat(Blog):refactoring after removing author modal * feat(Blog): fix unified date format * feat(Blog): Fix for click-outside-to-close search, recommended posts are limited to 3 * feat(Blog): selectedAuthorData fixed * feat(Blog): Added description for /blog/[slug] metadata, Added Schema.org JSON‑LD for Article (BlogPosting) and BreadcrumbList , Added <time datetime> tags where blog dates renders * Sanity (#203) * feat(Blog):fix for clickable link in post details, fix for author details * feat(Blog):refactoring after removing author modal * feat(Blog): fix unified date format * feat(Blog): Fix for click-outside-to-close search, recommended posts are limited to 3 * feat(Blog): selectedAuthorData fixed * feat(Blog): Added description for /blog/[slug] metadata, Added Schema.org JSON‑LD for Article (BlogPosting) and BreadcrumbList , Added <time datetime> tags where blog dates renders * feat(Blog): fix hover social links, fixed duplication not found search * (SP: 3) [Frontend] Refactor Header UI and navigation states (#201) * (SP: 3) [Frontend] Refactor Header UI and navigation states - Add icon to the language switcher - Add GitHub icon with stars indicator (frontend only) - Update logo styles - Improve touch interaction styles - Verify correct placement and alignment of all header components - Make mobile header modal full-screen - Disable background scroll when mobile menu is open - Highlight active navigation item - Update navigation styles: - Highlight Shop link when user is on Home pages - Highlight Home link when user is on Shop pages - Style changes only, no routing or logic changes * fix CodeRabbit * update HeaderButton styles * fix: add accessibility for HeaderButton * (SP: 3) [Testing] Vitest config + unit + integration tests for quiz module (#204) * feat(quiz-ui): quiz UI polish - tabs, category accents, color scheme (issues #181, #193, #194) - Refactor QaTabButton to shared CategoryTabButton component - Add category accent colors to QuizCard, buttons, progress indicators - Standardize colors with CSS variables, traffic light timer - Add DynamicGridBackground to quizzes list page - Border-only answer feedback, semi-transparent progress styles * docs: update .gitignore * fix(quiz): align disqualification threshold with warning banner Changed violationsCount > 3 to >= 3 in QuizResult points block to match the warning banner threshold at line 124. * feat(quiz-testing): add quiz unit tests - Configure Vitest for quiz module - Add test factories and setup utilities - Add quiz-crypto tests (13 tests) - Add quiz-session tests (12 tests) * test(quiz): add integration tests for verify-answer API and useAntiCheat hook (#199) - verify-answer.test.ts: 8 tests for API endpoint - Correct/wrong answer verification - Validation errors (missing fields, tampered data) - Security: rejects modified encrypted answers - quiz-anticheat.test.ts: 10 tests for useAntiCheat hook - Detects copy, paste, context-menu, tab-switch events - Respects isActive flag - Reset and cleanup functionality Total quiz tests: 52 (9 setup + 25 unit + 18 integration) * test(quiz): expand test coverage to 90%+ with hooks, API routes, and UI flow Add 28 new tests covering: - useQuizSession hook (6 tests) - useQuizGuards hook (8 tests) - guest-quiz storage (5 tests) - guest-result API route (5 tests) - quiz-slug API route (3 tests) - QuizContainer UI flow (1 test) Coverage: 35% -> 90.94% (quiz scope) Tests: 52 -> 80 * chore: remove coverage-quiz from git, add to .gitignore * chore: add coverage-quiz to .gitignore, fix quiz guards test * fix(leaderboard): improve table mobile * fix(leaderboard): correct malformed shadow class syntax for avatar glow * Feature/leaderboard style update (#206) * fix:leaderboard update leaderboard-style * fix:leaderboard update leaderboard-style on mobile * fix: resolve CodeRabbit issues and conflicts * fix: move row border to first cell to resolve CodeRabbit issue * fix: move row border to first cell to resolve CodeRabbit issue new * fix(leaderboard): improve table mobile * fix(leaderboard): correct malformed shadow class syntax for avatar glow * test(q&a): add comprehensive qa tests and coverage setup (#208) * test(q&a): add comprehensive qa tests and coverage setup * test(q&a): align mocks and reset in qa tests * (SP: 1) [Frontend] Remove Contacts References (#211) * test(q&a): add comprehensive qa tests and coverage setup * test(q&a): align mocks and reset in qa tests * chore(nav): remove contacts page references * Sanity (#209) * feat(Blog):fix for clickable link in post details, fix for author details * feat(Blog):refactoring after removing author modal * feat(Blog): fix unified date format * feat(Blog): Fix for click-outside-to-close search, recommended posts are limited to 3 * feat(Blog): selectedAuthorData fixed * feat(Blog): Added description for /blog/[slug] metadata, Added Schema.org JSON‑LD for Article (BlogPosting) and BreadcrumbList , Added <time datetime> tags where blog dates renders * feat(Blog): fix hover social links, fixed duplication not found search * feat(Blog): Added: breadcrumbs to the post details page and updated the BreadcrumbList, logo to the cocial links in User info, Fixed: main container alignment, category navigation in breadcrumbs * feat(Blog): Added: breadcrumbs to the post details page and updated the BreadcrumbList, logo to the cocial links in User info, Fixed: main container alignment, category navigation in breadcrumbs * feat(Blog): Added scroll on the main blog page on filtering by author, fied breadcrumbs category translaion, added category to the recommended cards, fixed search for localisations * feat(Blog): Changed image size on the post details page * chore(release): update changelog for v0.5.0 * chore(release): v0.5.0 * [Refactor] Code Quality Improvements: Accessibility, Mobile Support, … (#213) * fix(leaderboard): adjust podium heights for better visibility on desktop * Feature/leaderboard style update (#214) * fix(leaderboard): fix layout centering * feat(Blog) (#216) * feat(Blog) (#218) * feat(Blog):fix for clickable link in post details, fix for author details * feat(Blog):refactoring after removing author modal * feat(Blog): fix unified date format * feat(Blog): Fix for click-outside-to-close search, recommended posts are limited to 3 * feat(Blog): selectedAuthorData fixed * feat(Blog): Added description for /blog/[slug] metadata, Added Schema.org JSON‑LD for Article (BlogPosting) and BreadcrumbList , Added <time datetime> tags where blog dates renders * feat(Blog): fix hover social links, fixed duplication not found search * feat(Blog): Added: breadcrumbs to the post details page and updated the BreadcrumbList, logo to the cocial links in User info, Fixed: main container alignment, category navigation in breadcrumbs * feat(Blog): Added: breadcrumbs to the post details page and updated the BreadcrumbList, logo to the cocial links in User info, Fixed: main container alignment, category navigation in breadcrumbs * feat(Blog): Added scroll on the main blog page on filtering by author, fied breadcrumbs category translaion, added category to the recommended cards, fixed search for localisations * feat(Blog): Changed image size on the post details page * feat(Blog): added tests * feat(Blog): fix for big post on the post page, added tests * feat(Blog): resolving comments * feat(Blog): fixed hover for social links icins - dark theme * (SP: 1) feat(i18n): translate 404 error page (#217) - Add 404 translations (uk/en/pl) - Implement [Global/Local/Combined] strategy - Add helpful navigation links * (SP: 2) [Frontend] Refactor Home HeroSection and Footer stylestor/home (#221) * (SP:1) fix: 404 page layout (#219) - 404 translations (uk/en/pl) - Implement Global strategy * feat(Blog) (#222) * feat(Blog):fix for clickable link in post details, fix for author details * feat(Blog):refactoring after removing author modal * feat(Blog): fix unified date format * feat(Blog): Fix for click-outside-to-close search, recommended posts are limited to 3 * feat(Blog): selectedAuthorData fixed * feat(Blog): Added description for /blog/[slug] metadata, Added Schema.org JSON‑LD for Article (BlogPosting) and BreadcrumbList , Added <time datetime> tags where blog dates renders * feat(Blog): fix hover social links, fixed duplication not found search * feat(Blog): Added: breadcrumbs to the post details page and updated the BreadcrumbList, logo to the cocial links in User info, Fixed: main container alignment, category navigation in breadcrumbs * feat(Blog): Added: breadcrumbs to the post details page and updated the BreadcrumbList, logo to the cocial links in User info, Fixed: main container alignment, category navigation in breadcrumbs * feat(Blog): Added scroll on the main blog page on filtering by author, fied breadcrumbs category translaion, added category to the recommended cards, fixed search for localisations * feat(Blog): Changed image size on the post details page * feat(Blog): added tests * feat(Blog): fix for big post on the post page, added tests * feat(Blog): resolving comments * feat(Blog): fixed hover for social links icins - dark theme * feat(Blog): bringing the style on the blog page to a single site style * feat(blog): aligning syles * feat(blog): resolving comment from CodeRabbit * feat(blog):fix comment for deployment * Update AI model from 'llama-3.3-70b-versatile' to 'llama3-70b-8192' (#223) * (SP 2) [Frontend] Update Features section content and improve mobile UX (#224) * (SP 2) [Frontend] Update Features section content and improve mobile UX - Features Section: Refined feature content and visuals. - Mobile UX: Improved responsive layout and scaling for feature cards and interactive elements. - Visual Enhancements: Added dynamic particle background effects to the Pricing section. * fix(review): address accessibility, security, and performance feedback * fix(review): resolve accessibility and hydration issues * fix(perf): implement frame-rate independent animations * fix(review): address accessibility, security, and performance feedback * fix(review): enable SSR for features section and support HiDPI canvas * fix(review): correct HiDPI logic for particle canvas measurements * (SP:3) feat(i18n): add UA and PL translations for shop/admin pages Add comprehensive i18n support for shop and admin sections in 3 languages (en, uk, pl). Translation coverage: - Shop pages: main page, products, cart, checkout, orders - Admin pages: dashboard, products management, orders management - Navigation: header, mobile menu, category links - Product components: cards, filters, sort, badges (NEW/SALE) - Category names: Apparel, Lifestyle, Collectibles - All UI buttons, labels, and actions Key changes: - Added ~250+ translation keys to messages/en.json, messages/uk.json, messages/pl.json - Updated 20+ components to use useTranslations() and getTranslations() - Implemented color translation in cart and product detail pages - Translated hero message - Added badge translations * fix(i18n): correct translation keys and localization in shop pages - Replace confusing error.order with success.orderLabel in checkout success page heading - Localize boolean stockRestored display (yes/no instead of true/false) in order details - Fix active state detection for shop category links in mobile menu using search params - Add missing translation keys (orderLabel, yes, no) to all locales (en, uk, pl) * fix(netlify): resolve AI API crash and 404 locale/theme issues AI fixes: - Extract getClientIp to separate file (avoid db import crash) - Add missing zod dependency to package.json 404 page fixes: - Use NEXT_LOCALE cookie for locale detection on Netlify - Add theme detection script in root layout - Update styling with hero background and gradient text * Update frontend/app/not-found.tsx Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * (SP:3) feat(i18n): translate about page and auth form validation messages - Add about page translations (EN, UK, PL) - Add auth.fields.validation translations for form errors * fix(api): resolve Netlify 503 errors and harden AI explain endpoint - Use dynamic import for groq-sdk (Netlify compatibility) - Bypass rate limiting for unknown IPs (serverless safety) - Safe JSON parsing with request.text() + empty body check - Fix ReferenceError: remove undefined errorMessage variable - Remove sensitive debug info from client responses - Add i18n keys: pricing.heading, sponsors.ctaAriaLabel * feat(api): add GET health check endpoint for ai-explain * chore(release): v0.5.1 --------- Co-authored-by: liudmylasovetovs <milkaegik@gmail.com> Co-authored-by: Tetiana Zorii <131365289+TiZorii@users.noreply.github.com> Co-authored-by: Anna <komrakova.anna@gmail.com> Co-authored-by: AlinaRyabova <alinavr7@gmail.com> Co-authored-by: Yevhenii Datsenko <yevheniydatsenko@gmail.com> Co-authored-by: YNazymko12 <yulychka12@gmail.com> Co-authored-by: liudmylasovetovs <127711697+liudmylasovetovs@users.noreply.github.com> Co-authored-by: AlinaRyabova <115992255+AlinaRyabova@users.noreply.github.com> Co-authored-by: Lesia Soloviova <106915140+LesiaUKR@users.noreply.github.com> Co-authored-by: Yuliia Nazymko <122815071+YNazymko12@users.noreply.github.com> Co-authored-by: Yevhenii Datsenko <134847096+yevheniidatsenko@users.noreply.github.com> Co-authored-by: tetiana zorii <tanyusha.zoriy@gmail.com> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* (SP: 1) [Security] Enforce origin posture for shop APIs (admin/checkout same-origin; internal/webhooks non-browser) + docs * (SP: 1) [DB] Align stripe_events.order_id FK CASCADE across schema and migrations * feat(i18n): localize quiz anti-cheat, header and blog filters (#175) * feat(Blog): Adding last published post to the blog and category page, recommended posts, Changing styles to one unified format, Bug fixes * Update leaderboard-style * Update leaderboard-style * teat(Blog): fix of hover on author, fix of the line * (SP: 3) [Observability] Extend structured logging + correlation IDs across all shop routes; purge console.*; enforce explicit error codes * (SP: 1) [Admin][Security] Add safe product delete (PRODUCT_IN_USE) + mobile cards UI; tighten env/docs, locale normalization, cache-control, and logging semantics * fix: npm installing * (SP: 1) [Admin] Align products list in use checks with DB column names (order_items/inventory_moves) * fix:Update leaderboard: fixed background * fix: remove email from CurrentUser type to prevent PII exposure * refactor: align leaderboard UI with brand style (fixed bg, css vars, podium glow) * (SP: 1) [Frontend] About Us Page. Fixed game, topics, mobile layout - Fixed mobile tabs in FeaturesSection (icon-only on mobile) - Fixed game bugs: collision detection, animation, scoring system - Added multiple obstacle types with level progression - Improved game sizing for mobile while preserving desktop - Updated TopicsSection with local SVG icons and hover borders - Made DynamicGridBackground static grid opt-in via showStaticGrid prop - Limited SponsorsWall to display max 10 sponsors - Optimized CommunitySection button layout for mobile * refactor: update accent color hover effects to Footer - Update Footer links to use --accent-primary on hover - Update ThemeToggle icons to use --accent-primary on hover - Both components adapt colors to light/dark theme * refactor: improve accessibility - Add focus-visible styles for keyboard navigation accessibility * (SP: 1) [Shop] Fix Stripe checkout success redirect (remove duplicate locale /uk/uk) (#186) * fix:leaderboard update leaderboard-style * fix:leaderboard update leaderboard-style (#187) * fix:leaderboard update leaderboard-style on mobile * fix: resolve CodeRabbit issues and conflicts * fix:leaderboard update leaderboard-style on mobile (#188) * fix:leaderboard update leaderboard-style * fix:leaderboard update leaderboard-style on mobile * fix: resolve CodeRabbit issues and conflicts --------- * (SP: 1) [Frontend] Changing hero headline on shop main page (#190) * (SP: 1) [Shop] Fix checkout redirect 404 by removing duplicate locale in in-app routes and Stripe return_url * (SP: 1) [Shop] Fix locale cart page and orderid page * (SP: 1) [Frontend] Changin hero headline on shop main page * (SP: 1) [Frontend] Fix styles shop home page, buttons (#191) * fix: move row border to first cell to resolve CodeRabbit issue * fix: move row border to first cell to resolve CodeRabbit issue new * Feature/leaderboard style update (#192) * (SP: 7) [UI] Quiz UI polish: tabs styling, category accents, color scheme (#181, #193, #194) (#195) * Sanity (#196) * feat(Blog):fix for clickable link in post details, fix for author details * feat(Blog):refactoring after removing author modal * feat(Blog): fix unified date format * (SP: 3) [AI] Add AI word helper with Groq integration (#200) * (SP: 3) [AI] Add AI word helper with Groq integration - Implement Groq API with Llama 3.1 70B model - Add text selection detection on Q&A page - Create floating "Explain" button - Build draggable modal with 3-language support (uk/en/pl) - Add localStorage caching for instant repeated lookups - Implement guest CTA (login/signup) - Add rate limiting (10 requests/min) - Auth-gated feature (registered users only) Components: - SelectableText: Detects text selection - FloatingExplainButton: Appears on selection - AIWordHelper: Main modal with explanations * (SP: 1) i18n: fix Polish locale and set EN as default * Sanity (#202) * feat(Blog):fix for clickable link in post details, fix for author details * feat(Blog):refactoring after removing author modal * feat(Blog): fix unified date format * feat(Blog): Fix for click-outside-to-close search, recommended posts are limited to 3 * feat(Blog): selectedAuthorData fixed * feat(Blog): Added description for /blog/[slug] metadata, Added Schema.org JSON‑LD for Article (BlogPosting) and BreadcrumbList , Added <time datetime> tags where blog dates renders * Sanity (#203) * feat(Blog):fix for clickable link in post details, fix for author details * feat(Blog):refactoring after removing author modal * feat(Blog): fix unified date format * feat(Blog): Fix for click-outside-to-close search, recommended posts are limited to 3 * feat(Blog): selectedAuthorData fixed * feat(Blog): Added description for /blog/[slug] metadata, Added Schema.org JSON‑LD for Article (BlogPosting) and BreadcrumbList , Added <time datetime> tags where blog dates renders * feat(Blog): fix hover social links, fixed duplication not found search * (SP: 3) [Frontend] Refactor Header UI and navigation states (#201) * (SP: 3) [Frontend] Refactor Header UI and navigation states - Add icon to the language switcher - Add GitHub icon with stars indicator (frontend only) - Update logo styles - Improve touch interaction styles - Verify correct placement and alignment of all header components - Make mobile header modal full-screen - Disable background scroll when mobile menu is open - Highlight active navigation item - Update navigation styles: - Highlight Shop link when user is on Home pages - Highlight Home link when user is on Shop pages - Style changes only, no routing or logic changes * fix CodeRabbit * update HeaderButton styles * fix: add accessibility for HeaderButton * (SP: 3) [Testing] Vitest config + unit + integration tests for quiz module (#204) * feat(quiz-ui): quiz UI polish - tabs, category accents, color scheme (issues #181, #193, #194) - Refactor QaTabButton to shared CategoryTabButton component - Add category accent colors to QuizCard, buttons, progress indicators - Standardize colors with CSS variables, traffic light timer - Add DynamicGridBackground to quizzes list page - Border-only answer feedback, semi-transparent progress styles * docs: update .gitignore * fix(quiz): align disqualification threshold with warning banner Changed violationsCount > 3 to >= 3 in QuizResult points block to match the warning banner threshold at line 124. * feat(quiz-testing): add quiz unit tests - Configure Vitest for quiz module - Add test factories and setup utilities - Add quiz-crypto tests (13 tests) - Add quiz-session tests (12 tests) * test(quiz): add integration tests for verify-answer API and useAntiCheat hook (#199) - verify-answer.test.ts: 8 tests for API endpoint - Correct/wrong answer verification - Validation errors (missing fields, tampered data) - Security: rejects modified encrypted answers - quiz-anticheat.test.ts: 10 tests for useAntiCheat hook - Detects copy, paste, context-menu, tab-switch events - Respects isActive flag - Reset and cleanup functionality Total quiz tests: 52 (9 setup + 25 unit + 18 integration) * test(quiz): expand test coverage to 90%+ with hooks, API routes, and UI flow Add 28 new tests covering: - useQuizSession hook (6 tests) - useQuizGuards hook (8 tests) - guest-quiz storage (5 tests) - guest-result API route (5 tests) - quiz-slug API route (3 tests) - QuizContainer UI flow (1 test) Coverage: 35% -> 90.94% (quiz scope) Tests: 52 -> 80 * chore: remove coverage-quiz from git, add to .gitignore * chore: add coverage-quiz to .gitignore, fix quiz guards test * fix(leaderboard): improve table mobile * fix(leaderboard): correct malformed shadow class syntax for avatar glow * Feature/leaderboard style update (#206) * fix:leaderboard update leaderboard-style * fix:leaderboard update leaderboard-style on mobile * fix: resolve CodeRabbit issues and conflicts * fix: move row border to first cell to resolve CodeRabbit issue * fix: move row border to first cell to resolve CodeRabbit issue new * fix(leaderboard): improve table mobile * fix(leaderboard): correct malformed shadow class syntax for avatar glow * test(q&a): add comprehensive qa tests and coverage setup (#208) * test(q&a): add comprehensive qa tests and coverage setup * test(q&a): align mocks and reset in qa tests * (SP: 1) [Frontend] Remove Contacts References (#211) * test(q&a): add comprehensive qa tests and coverage setup * test(q&a): align mocks and reset in qa tests * chore(nav): remove contacts page references * Sanity (#209) * feat(Blog):fix for clickable link in post details, fix for author details * feat(Blog):refactoring after removing author modal * feat(Blog): fix unified date format * feat(Blog): Fix for click-outside-to-close search, recommended posts are limited to 3 * feat(Blog): selectedAuthorData fixed * feat(Blog): Added description for /blog/[slug] metadata, Added Schema.org JSON‑LD for Article (BlogPosting) and BreadcrumbList , Added <time datetime> tags where blog dates renders * feat(Blog): fix hover social links, fixed duplication not found search * feat(Blog): Added: breadcrumbs to the post details page and updated the BreadcrumbList, logo to the cocial links in User info, Fixed: main container alignment, category navigation in breadcrumbs * feat(Blog): Added: breadcrumbs to the post details page and updated the BreadcrumbList, logo to the cocial links in User info, Fixed: main container alignment, category navigation in breadcrumbs * feat(Blog): Added scroll on the main blog page on filtering by author, fied breadcrumbs category translaion, added category to the recommended cards, fixed search for localisations * feat(Blog): Changed image size on the post details page * chore(release): update changelog for v0.5.0 * chore(release): v0.5.0 * [Refactor] Code Quality Improvements: Accessibility, Mobile Support, … (#213) * fix(leaderboard): adjust podium heights for better visibility on desktop * Feature/leaderboard style update (#214) * fix(leaderboard): fix layout centering * feat(Blog) (#216) * feat(Blog) (#218) * feat(Blog):fix for clickable link in post details, fix for author details * feat(Blog):refactoring after removing author modal * feat(Blog): fix unified date format * feat(Blog): Fix for click-outside-to-close search, recommended posts are limited to 3 * feat(Blog): selectedAuthorData fixed * feat(Blog): Added description for /blog/[slug] metadata, Added Schema.org JSON‑LD for Article (BlogPosting) and BreadcrumbList , Added <time datetime> tags where blog dates renders * feat(Blog): fix hover social links, fixed duplication not found search * feat(Blog): Added: breadcrumbs to the post details page and updated the BreadcrumbList, logo to the cocial links in User info, Fixed: main container alignment, category navigation in breadcrumbs * feat(Blog): Added: breadcrumbs to the post details page and updated the BreadcrumbList, logo to the cocial links in User info, Fixed: main container alignment, category navigation in breadcrumbs * feat(Blog): Added scroll on the main blog page on filtering by author, fied breadcrumbs category translaion, added category to the recommended cards, fixed search for localisations * feat(Blog): Changed image size on the post details page * feat(Blog): added tests * feat(Blog): fix for big post on the post page, added tests * feat(Blog): resolving comments * feat(Blog): fixed hover for social links icins - dark theme * (SP: 1) feat(i18n): translate 404 error page (#217) - Add 404 translations (uk/en/pl) - Implement [Global/Local/Combined] strategy - Add helpful navigation links * (SP: 2) [Frontend] Refactor Home HeroSection and Footer stylestor/home (#221) * (SP:1) fix: 404 page layout (#219) - 404 translations (uk/en/pl) - Implement Global strategy * feat(Blog) (#222) * feat(Blog):fix for clickable link in post details, fix for author details * feat(Blog):refactoring after removing author modal * feat(Blog): fix unified date format * feat(Blog): Fix for click-outside-to-close search, recommended posts are limited to 3 * feat(Blog): selectedAuthorData fixed * feat(Blog): Added description for /blog/[slug] metadata, Added Schema.org JSON‑LD for Article (BlogPosting) and BreadcrumbList , Added <time datetime> tags where blog dates renders * feat(Blog): fix hover social links, fixed duplication not found search * feat(Blog): Added: breadcrumbs to the post details page and updated the BreadcrumbList, logo to the cocial links in User info, Fixed: main container alignment, category navigation in breadcrumbs * feat(Blog): Added: breadcrumbs to the post details page and updated the BreadcrumbList, logo to the cocial links in User info, Fixed: main container alignment, category navigation in breadcrumbs * feat(Blog): Added scroll on the main blog page on filtering by author, fied breadcrumbs category translaion, added category to the recommended cards, fixed search for localisations * feat(Blog): Changed image size on the post details page * feat(Blog): added tests * feat(Blog): fix for big post on the post page, added tests * feat(Blog): resolving comments * feat(Blog): fixed hover for social links icins - dark theme * feat(Blog): bringing the style on the blog page to a single site style * feat(blog): aligning syles * feat(blog): resolving comment from CodeRabbit * feat(blog):fix comment for deployment * Update AI model from 'llama-3.3-70b-versatile' to 'llama3-70b-8192' (#223) * (SP 2) [Frontend] Update Features section content and improve mobile UX (#224) * (SP 2) [Frontend] Update Features section content and improve mobile UX - Features Section: Refined feature content and visuals. - Mobile UX: Improved responsive layout and scaling for feature cards and interactive elements. - Visual Enhancements: Added dynamic particle background effects to the Pricing section. * fix(review): address accessibility, security, and performance feedback * fix(review): resolve accessibility and hydration issues * fix(perf): implement frame-rate independent animations * fix(review): address accessibility, security, and performance feedback * fix(review): enable SSR for features section and support HiDPI canvas * fix(review): correct HiDPI logic for particle canvas measurements * (SP:3) feat(i18n): add UA and PL translations for shop/admin pages Add comprehensive i18n support for shop and admin sections in 3 languages (en, uk, pl). Translation coverage: - Shop pages: main page, products, cart, checkout, orders - Admin pages: dashboard, products management, orders management - Navigation: header, mobile menu, category links - Product components: cards, filters, sort, badges (NEW/SALE) - Category names: Apparel, Lifestyle, Collectibles - All UI buttons, labels, and actions Key changes: - Added ~250+ translation keys to messages/en.json, messages/uk.json, messages/pl.json - Updated 20+ components to use useTranslations() and getTranslations() - Implemented color translation in cart and product detail pages - Translated hero message - Added badge translations * fix(i18n): correct translation keys and localization in shop pages - Replace confusing error.order with success.orderLabel in checkout success page heading - Localize boolean stockRestored display (yes/no instead of true/false) in order details - Fix active state detection for shop category links in mobile menu using search params - Add missing translation keys (orderLabel, yes, no) to all locales (en, uk, pl) * fix(netlify): resolve AI API crash and 404 locale/theme issues AI fixes: - Extract getClientIp to separate file (avoid db import crash) - Add missing zod dependency to package.json 404 page fixes: - Use NEXT_LOCALE cookie for locale detection on Netlify - Add theme detection script in root layout - Update styling with hero background and gradient text * Update frontend/app/not-found.tsx * (SP:3) feat(i18n): translate about page and auth form validation messages - Add about page translations (EN, UK, PL) - Add auth.fields.validation translations for form errors * fix(api): resolve Netlify 503 errors and harden AI explain endpoint - Use dynamic import for groq-sdk (Netlify compatibility) - Bypass rate limiting for unknown IPs (serverless safety) - Safe JSON parsing with request.text() + empty body check - Fix ReferenceError: remove undefined errorMessage variable - Remove sensitive debug info from client responses - Add i18n keys: pricing.heading, sponsors.ctaAriaLabel * feat(api): add GET health check endpoint for ai-explain * chore(release): v0.5.1 --------- Co-authored-by: liudmylasovetovs <milkaegik@gmail.com> Co-authored-by: Tetiana Zorii <131365289+TiZorii@users.noreply.github.com> Co-authored-by: Anna <komrakova.anna@gmail.com> Co-authored-by: AlinaRyabova <alinavr7@gmail.com> Co-authored-by: Yevhenii Datsenko <yevheniydatsenko@gmail.com> Co-authored-by: YNazymko12 <yulychka12@gmail.com> Co-authored-by: liudmylasovetovs <127711697+liudmylasovetovs@users.noreply.github.com> Co-authored-by: AlinaRyabova <115992255+AlinaRyabova@users.noreply.github.com> Co-authored-by: Lesia Soloviova <106915140+LesiaUKR@users.noreply.github.com> Co-authored-by: Yuliia Nazymko <122815071+YNazymko12@users.noreply.github.com> Co-authored-by: Yevhenii Datsenko <134847096+yevheniidatsenko@users.noreply.github.com> Co-authored-by: tetiana zorii <tanyusha.zoriy@gmail.com> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Description
This PR overhauls the About Page visuals and content to better reflect platform capabilities, specifically upgrading the Features and Pricing sections. It introduces dynamic background effects, refines feature messaging based on recent audits, and significantly improves mobile responsiveness.
Changes
🎨 Visual & UI Enhancements
GradientBadgecomponent for consistent section labeling.🧩 New Components
How Has This Been Tested?
localhost:3000.Checklist
Before submitting
Reviewers
Summary by CodeRabbit
New Features
Style
Accessibility
✏️ Tip: You can customize this high-level summary in your review settings.