Skip to content
Open
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
20 changes: 20 additions & 0 deletions client/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ Codify is a comprehensive web application that offers courses and roadmaps to va
- User authentication and personalized profiles
- Comprehensive admin panel for content and user management
- Optimized responsive design for seamless mobile and desktop experiences
- New unified Hero section with stable layout (no button jumping) and video background
- Desktop navigation now shows primary links (Home, About, Editor, Courses, Roadmaps, Notes, Questions, Bookmarks, Contributors, Contact) without needing the hamburger menu
- Accessible focus states (focus-visible rings) added to all navigation links & CTAs
- Playwright end-to-end tests for responsive navbar behavior

## Technologies Used
- **Frontend:** React, React Router, CSS 💻
Expand Down Expand Up @@ -52,6 +56,22 @@ https://github.com/user-attachments/assets/9ba51e5e-8e16-48f1-96d3-fe1e497541a0
## Usage
Once the application is running, navigate to `http://localhost:5173` in your web browser to access the app. You can create an account, log in, and start learning about bitwise operations.

### Running E2E Tests (Playwright)
1. From the `client` directory install dependencies (including Playwright): this will auto-install browsers on first run.
2. Run tests:
```
pnpm test:e2e (or) npm run test:e2e
```
Optional:
```
npm run test:e2e:ui # Interactive UI mode
npm run test:e2e:headed # Run in headed browsers
```

The navbar test validates:
- Desktop (xl) shows all primary links and hides the hamburger.
- Mobile view shows hamburger and reveals links after opening the menu.

## Contributing
Contributions are welcome! Please follow these steps:
1. Fork the repository.
Expand Down
64 changes: 64 additions & 0 deletions client/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 6 additions & 2 deletions client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@
"dev": "vite",
"build": "vite build",
"lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0",
"preview": "vite preview"
"preview": "vite preview",
"test:e2e": "playwright test",
"test:e2e:ui": "playwright test --ui",
"test:e2e:headed": "playwright test --headed"
},
"dependencies": {
"@codemirror/lang-cpp": "^6.0.3",
Expand Down Expand Up @@ -46,6 +49,7 @@
"eslint-plugin-react-refresh": "^0.4.7",
"postcss": "^8.4.41",
"tailwindcss": "^3.4.17",
"vite": "^6.3.5"
"vite": "^6.3.5",
"@playwright/test": "^1.48.2"
}
}
25 changes: 25 additions & 0 deletions client/playwright.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// @ts-check
import { defineConfig, devices } from '@playwright/test';

export default defineConfig({
testDir: './tests',
timeout: 30 * 1000,
expect: { timeout: 5000 },
fullyParallel: true,
retries: 0,
reporter: [['list']],
use: {
baseURL: process.env.PLAYWRIGHT_BASE_URL || 'http://localhost:5173',
trace: 'retain-on-failure'
},
projects: [
{
name: 'Desktop Chrome',
use: { ...devices['Desktop Chrome'] }
},
{
name: 'Mobile Chrome',
use: { ...devices['Pixel 7'] }
}
]
});
116 changes: 116 additions & 0 deletions client/src/components/Hero.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import React from 'react';
import { motion } from 'framer-motion';
import { useTheme } from '../context/ThemeContext';

/**
* Hero section extracted from Home page for clarity & reusability.
* Adjusted to avoid layout shift / button jumping by:
* - Providing explicit min-height using CSS variable offset for navbar.
* - Reserving space for async content (no conditional height changes).
* - Using flex + gap with consistent sizing.
*/
function Hero() {
const { theme } = useTheme();
const isDark = theme === 'dark';

return (
<section className="relative hero-section flex items-center justify-center min-h-screen-minus-nav">
{/* Video Background */}
<video
autoPlay
loop
muted
playsInline
className={`absolute inset-0 w-full h-full object-cover ${isDark ? 'opacity-20' : 'opacity-40'}`}
>
<source src="/Videos/vid1.mp4" type="video/mp4" />
</video>

{/* Overlay */}
<div className="absolute inset-0 bg-gradient-to-br from-primary/20 via-transparent to-secondary/20" />

{/* Ambient Shapes */}
<div className="absolute inset-0 overflow-hidden pointer-events-none select-none">
<div className="absolute top-20 left-20 w-72 h-72 bg-primary/10 rounded-full blur-3xl animate-pulse" />
<div className="absolute bottom-20 right-20 w-96 h-96 bg-secondary/10 rounded-full blur-3xl animate-pulse animation-delay-1000" />
<div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-80 h-80 bg-accent/5 rounded-full blur-3xl animate-pulse animation-delay-500" />
</div>

{/* Core Content */}
<div className="relative z-20 text-center max-w-6xl mx-auto px-6 py-8 pt-32 sm:pt-40">
<motion.div
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5, delay: 0.15 }}
className={`inline-flex items-center gap-2 px-4 py-2 rounded-full backdrop-blur-sm border mb-8 ${isDark ? 'bg-dark-bg-secondary/80 border-dark-border/50' : 'bg-light-bg-secondary/80 border-light-border/50'}`}
>
<span className="w-2 h-2 bg-primary rounded-full animate-pulse" />
<span className="text-sm font-medium">🚀 Join 1000+ learners worldwide</span>
</motion.div>

<motion.h1
initial={{ opacity: 0, y: 24 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.3 }}
className={`text-4xl sm:text-5xl lg:text-6xl xl:text-7xl font-righteous tracking-wider mb-6 leading-tight ${isDark ? 'text-dark-text-primary' : 'text-light-text-primary'}`}
>
Master Coding with
<span className="block text-transparent bg-clip-text bg-gradient-to-r from-primary via-secondary to-accent mt-2 py-1">Interactive Learning</span>
</motion.h1>

<motion.p
initial={{ opacity: 0, y: 24 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.45 }}
className={`text-lg sm:text-xl lg:text-2xl mb-10 max-w-3xl mx-auto ${isDark ? 'text-dark-text-secondary' : 'text-light-text-secondary'}`}
>
Discover the perfect learning path with hands-on projects, expert guidance, and a community of passionate developers.
</motion.p>

<motion.div
initial={{ opacity: 0, y: 24 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.6 }}
className="flex flex-col sm:flex-row gap-4 justify-center items-center"
>
<motion.a
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.97 }}
href="/courses"
className="group bg-gradient-to-r from-primary to-secondary text-white py-3 sm:py-4 px-7 sm:px-9 text-base sm:text-lg rounded-xl font-semibold transition-all duration-300 hover:shadow-2xl inline-flex items-center gap-3 shadow-primary/30"
>
<span>Start Learning Free</span>
<svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5 transform group-hover:translate-x-1 transition-transform" viewBox="0 0 20 20" fill="currentColor"><path fillRule="evenodd" d="M10.293 5.293a1 1 0 011.414 0l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414-1.414L12.586 11H5a1 1 0 110-2h7.586l-2.293-2.293a1 1 0 010-1.414z" clipRule="evenodd" /></svg>
</motion.a>
<motion.a
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.97 }}
href="#demo"
className="group bg-transparent border-2 border-primary text-primary py-3 sm:py-4 px-7 sm:px-9 text-base sm:text-lg rounded-xl font-semibold transition-all duration-300 hover:bg-primary hover:text-white inline-flex items-center gap-3"
>
<span>Watch Demo</span>
<svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5 transform group-hover:scale-110 transition-transform" viewBox="0 0 20 20" fill="currentColor"><path fillRule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM9.555 7.168A1 1 0 008 8v4a1 1 0 001.555.832l3-2a1 1 0 000-1.664l-3-2z" clipRule="evenodd" /></svg>
</motion.a>
</motion.div>

<div className="mt-12">
<p className={`text-xs sm:text-sm mb-4 ${isDark ? 'text-dark-text-secondary' : 'text-light-text-secondary'}`}>Trusted by developers from 100+ countries</p>
<div className="flex justify-center items-center gap-6 opacity-70">
<div className="w-14 sm:w-16 h-6 sm:h-8 bg-gradient-to-r from-primary/20 to-secondary/20 rounded" />
<div className="w-14 sm:w-16 h-6 sm:h-8 bg-gradient-to-r from-secondary/20 to-accent/20 rounded" />
<div className="w-14 sm:w-16 h-6 sm:h-8 bg-gradient-to-r from-accent/20 to-primary/20 rounded" />
</div>
</div>

{/* Scroll Indicator */}
<div className="absolute bottom-6 left-1/2 -translate-x-1/2 animate-bounce">
<div className="w-6 h-10 border-2 border-primary rounded-full flex justify-center">
<div className="w-1 h-3 bg-primary rounded-full mt-2 animate-pulse" />
</div>
</div>
</div>
</section>
);
}

export default Hero;
Loading