Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 11 additions & 82 deletions apps/web/src/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -269,17 +269,6 @@ pre {
animation: pulse-glow 2s ease-in-out infinite;
}

.animate-shimmer {
background: linear-gradient(
90deg,
rgba(255, 255, 255, 0) 0%,
rgba(255, 255, 255, 0.1) 50%,
rgba(255, 255, 255, 0) 100%
);
background-size: 200% 100%;
animation: shimmer 2s linear infinite;
}

.animate-gradient {
background-size: 200% 200%;
animation: gradient-shift 8s ease infinite;
Expand Down Expand Up @@ -557,76 +546,7 @@ pre {
}

/* ============================================
Enhanced Input Styling
============================================ */

.input-glow {
position: relative;
}

.input-glow::before {
content: "";
position: absolute;
inset: -2px;
background: linear-gradient(
135deg,
rgba(139, 92, 246, 0.3) 0%,
rgba(34, 211, 238, 0.3) 100%
);
border-radius: inherit;
opacity: 0;
transition: opacity var(--transition-base);
z-index: -1;
filter: blur(8px);
}

.input-glow:focus-within::before {
opacity: 1;
}

/* ============================================
Topic Chip Styles
============================================ */

.chip {
display: inline-flex;
align-items: center;
gap: 0.5rem;
padding: 0.625rem 1rem;
font-size: 0.875rem;
font-weight: 500;
border-radius: 0.75rem;
background: rgba(255, 255, 255, 0.03);
border: 1px solid rgba(255, 255, 255, 0.08);
color: rgba(255, 255, 255, 0.8);
transition: background var(--transition-base), border-color var(--transition-base), transform var(--transition-base);
cursor: pointer;
}

.chip:hover {
background: rgba(255, 255, 255, 0.06);
border-color: rgba(255, 255, 255, 0.15);
transform: translateY(-2px);
}

.chip:active {
transform: translateY(0);
}

/* ============================================
Text Utilities
============================================ */

.text-shadow-lg {
text-shadow: 0 4px 12px rgba(0, 0, 0, 0.5);
}

.text-shadow-glow {
text-shadow: 0 0 40px rgba(139, 92, 246, 0.5);
}

/* ============================================
Skeleton Loading
Skeleton Loading (Used by loading states)
============================================ */

.skeleton {
Expand All @@ -637,6 +557,15 @@ pre {
rgba(255, 255, 255, 0.03) 100%
);
background-size: 200% 100%;
animation: shimmer 1.5s ease-in-out infinite;
animation: skeleton-shimmer 1.5s ease-in-out infinite;
border-radius: 0.5rem;
}

@keyframes skeleton-shimmer {
0% {
background-position: -200% 0;
}
100% {
background-position: 200% 0;
}
}
28 changes: 18 additions & 10 deletions apps/web/src/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -484,9 +484,9 @@ export default function HomePage() {
<nav className="relative z-50 flex items-center justify-between px-6 lg:px-12 py-5 border-b border-white/[0.06]">
<div className="flex items-center gap-3">
<div className="w-11 h-11 rounded-xl bg-gradient-to-br from-primary-500 to-primary-600 flex items-center justify-center font-black text-lg shadow-lg shadow-primary-500/30 transition-transform hover:scale-105">
V
U
</div>
<span className="font-bold text-xl tracking-tight bg-gradient-to-r from-white to-white/80 bg-clip-text text-transparent">Video2Learn</span>
<span className="font-bold text-xl tracking-tight bg-gradient-to-r from-white to-white/80 bg-clip-text text-transparent">UVAI.io</span>
</div>

{/* Desktop Navigation */}
Expand All @@ -502,10 +502,10 @@ export default function HomePage() {
</Link>
<Link
href="/dashboard"
className="btn btn-primary px-5 py-2.5 group"
className="btn btn-primary px-5 py-2.5 group inline-flex items-center gap-2"
>
Get Started
<span className="ml-1 transition-transform group-hover:translate-x-1">→</span>
<span className="transition-transform group-hover:translate-x-1">→</span>
</Link>
</div>

Expand Down Expand Up @@ -613,14 +613,22 @@ export default function HomePage() {
<div className="mb-16">
<SuggestedPrompts
onSelectTopic={(query) => {
setVideoUrl(`https://www.youtube.com/results?search_query=${encodeURIComponent(query)}`);
const searchUrl = `https://www.youtube.com/results?search_query=${encodeURIComponent(query)}`;
if (typeof window !== 'undefined') {
window.open(searchUrl, '_blank', 'noopener,noreferrer');
}
}}
/>
</div>

{/* Video Preview Card */}
{/* Video Preview Card - Visual Example Only */}
<div className="max-w-lg mx-auto mb-16">
<div className="relative group">
{/* Example label */}
<div className="absolute -top-8 left-0 right-0 flex justify-center">
<span className="text-xs font-semibold tracking-widest text-white/30 uppercase">Example Preview</span>
</div>

{/* Glow effect */}
<div className="absolute -inset-4 bg-gradient-to-r from-primary-500/10 via-accent-500/10 to-primary-500/10 rounded-3xl blur-2xl opacity-0 group-hover:opacity-100 transition-opacity duration-700" />

Expand All @@ -632,7 +640,7 @@ export default function HomePage() {

{/* Play button overlay */}
<div className="absolute inset-0 flex items-center justify-center opacity-0 group-hover:opacity-100 transition-all duration-300">
<div className="w-16 h-16 rounded-full bg-white/10 backdrop-blur-sm border border-white/20 flex items-center justify-center hover:scale-110 transition-transform cursor-pointer">
<div className="w-16 h-16 rounded-full bg-white/10 backdrop-blur-sm border border-white/20 flex items-center justify-center hover:scale-110 transition-transform">
<span className="text-2xl ml-1">▶</span>
</div>
</div>
Expand Down Expand Up @@ -891,9 +899,9 @@ console.log(result.deployedUrl);
<div className="col-span-2 md:col-span-1">
<div className="flex items-center gap-3 mb-4">
<div className="w-10 h-10 rounded-xl bg-gradient-to-br from-primary-500 to-primary-600 flex items-center justify-center font-bold shadow-lg shadow-primary-500/25">
V
U
</div>
<span className="font-bold text-lg">Video2Learn</span>
<span className="font-bold text-lg">UVAI.io</span>
</div>
<p className="text-white/40 text-sm leading-relaxed mb-4">
Transform any video into interactive learning experiences with AI.
Expand Down Expand Up @@ -973,7 +981,7 @@ console.log(result.deployedUrl);
{/* Footer Bottom */}
<div className="pt-8 border-t border-white/[0.05] flex flex-col md:flex-row justify-between items-center gap-4">
<p className="text-white/30 text-sm">
© 2026 Video2Learn. Built with AI by <span className="text-primary-400">Aaron Wade</span>
© 2026 UVAI.io. Built with AI by <span className="text-primary-400">Aaron Wade</span>
</p>
<div className="flex items-center gap-2 text-white/30 text-sm">
<span className="relative flex h-2 w-2">
Expand Down
2 changes: 2 additions & 0 deletions apps/web/src/components/ui/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,13 +64,15 @@ const Button = forwardRef<HTMLButtonElement, ButtonProps>(
'bg-gradient-to-r from-red-500 via-red-600 to-red-500 bg-[length:200%_100%]',
'text-white shadow-lg shadow-red-500/30',
'hover:shadow-xl hover:shadow-red-500/40 hover:-translate-y-0.5',
'hover:bg-[position:100%_0]',
'active:translate-y-0',
'transition-all duration-300'
),
success: clsx(
'bg-gradient-to-r from-green-500 via-green-600 to-green-500 bg-[length:200%_100%]',
'text-white shadow-lg shadow-green-500/30',
'hover:shadow-xl hover:shadow-green-500/40 hover:-translate-y-0.5',
'hover:bg-[position:100%_0]',
'active:translate-y-0',
'transition-all duration-300'
),
Expand Down
20 changes: 14 additions & 6 deletions apps/web/src/components/ui/SuggestedPrompts.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,12 @@ export interface TopicChipProps extends HTMLAttributes<HTMLButtonElement> {
}

const TopicChip = forwardRef<HTMLButtonElement, TopicChipProps>(
({ topic, onSelect, className, ...props }, ref) => {
({ topic, onSelect, className, style, ...props }, ref) => {
return (
<button
ref={ref}
onClick={() => onSelect?.(topic.query)}
aria-label={`Search for ${topic.label} videos`}
className={clsx(
'group inline-flex items-center gap-2.5 px-4 py-2.5',
'rounded-xl',
Expand All @@ -76,8 +77,10 @@ const TopicChip = forwardRef<HTMLButtonElement, TopicChipProps>(
'hover:border-white/20',
'active:scale-100',
'focus:outline-none focus-visible:ring-2 focus-visible:ring-primary-500/50',
'motion-reduce:transition-none motion-reduce:transform-none',
className
)}
style={style}
{...props}
>
<span className={clsx(
Expand All @@ -89,7 +92,7 @@ const TopicChip = forwardRef<HTMLButtonElement, TopicChipProps>(
<span className={clsx(
'text-sm font-medium',
topic.textColor,
'group-hover:text-white transition-colors'
'group-hover:text-white transition-colors motion-reduce:transition-none'
)}>
{topic.label}
</span>
Expand All @@ -106,16 +109,21 @@ export interface SuggestedPromptsProps extends HTMLAttributes<HTMLDivElement> {
}

const SuggestedPrompts = forwardRef<HTMLDivElement, SuggestedPromptsProps>(
({ onSelectTopic, title = 'TRY A SUGGESTED TOPIC:', className, ...props }, ref) => {
({ onSelectTopic, title = 'TRY A SUGGESTED TOPIC:', className, style, ...props }, ref) => {
return (
<div
ref={ref}
className={clsx(
'w-full max-w-2xl mx-auto',
'animate-fade-in-up',
'animate-fade-in-up motion-reduce:animate-none motion-reduce:opacity-100',
className
)}
style={{ animationDelay: '200ms', opacity: 0 }}
style={{
...style,
animationDelay: '200ms',
opacity: 0,
animationFillMode: 'forwards'
}}
{...props}
Comment on lines 117 to 127
Copy link

Choose a reason for hiding this comment

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

Bug: An inline style opacity: 0 overrides the motion-reduce:opacity-100 class, making the component invisible for users with reduced motion enabled.
Severity: HIGH

Suggested Fix

Remove the opacity: 0 from the inline style object. The initial state should be managed by the animation keyframes (animate-fade-in-up) to ensure it does not conflict with the motion-reduce utility classes. This allows the motion-reduce:opacity-100 class to correctly apply when needed.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent.
Verify if this is a real issue. If it is, propose a fix; if not, explain why it's not
valid.

Location: apps/web/src/components/ui/SuggestedPrompts.tsx#L111-L127

Potential issue: The component sets an inline style `opacity: 0`, which has higher CSS
specificity than the `motion-reduce:opacity-100` utility class. For users with the
`prefers-reduced-motion` accessibility setting enabled, animations are correctly
disabled, but the element remains invisible because the inline style takes precedence.
This makes the `SuggestedPrompts` container and its child `TopicChip` components
unusable for this group of users, which is a significant accessibility issue.

Did we get this right? 👍 / 👎 to inform future reviews.

>
<p className="text-xs font-semibold tracking-widest text-white/40 mb-4 text-center">
Expand All @@ -132,7 +140,7 @@ const SuggestedPrompts = forwardRef<HTMLDivElement, SuggestedPromptsProps>(
animationFillMode: 'forwards',
opacity: 0
}}
className="animate-fade-in-up"
className="animate-fade-in-up motion-reduce:animate-none motion-reduce:opacity-100"
/>
))}
</div>
Expand Down