A cutting-edge, interactive authentication interface featuring animated characters that respond to user interactions with expressive eye tracking, dynamic mouth animations, and gesture-based feedback. Built with modern React, CSS Modules, and a focus on exceptional UX/DX.
Live Demo β’ Features β’ Architecture β’ Getting Started
UXWebsite is a sophisticated React-based authentication system that combines functional form validation with playful, interactive character animations. The project demonstrates advanced React patterns, state management, CSS animations, and responsive design principlesβperfect for understanding modern frontend architecture.
- π 4 Animated Characters with state-driven expressions and gestures
- π Mouse-tracking eye system with real-time pupil positioning
- π¨ Dark/Light Theme Toggle with CSS Variables and persistence
- π± Fully Responsive Design (mobile-first, tested down to 320px)
- β Form Validation with real-time character feedback
- π SPA Navigation using React Router v7
- β‘ Performance Optimized with Vite bundling and CSS Modules
Each character responds dynamically to user interactions:
| Reaction | Visual State | Trigger |
|---|---|---|
| idle | Eyes tracking mouse, neutral mouth | Default state, blur events |
| typing | Eyes tracking, typing mouth (circle) | Text field focus/input |
| closed | Eyes closed (8px bar), closed mouth | Password field input |
| side | Eyes shifted left (-8px), scared mouth (diagonal) | Password field mouse hover |
| surprise | Large white eyes (36px), open mouth (circle) | Form validation failure |
| submit | Happy eyes (white with small pupils), smiling mouth | Form submission success |
- Rectangle (Blue) - 140Γ300px, positioned back-left
- Box (Red) - 90Γ180px, positioned back-center
- Semicircle (Orange) - 170Γ100px, positioned front-left
- Cylinder (Yellow) - 100Γ150px, positioned front-right
- SignIn (
/) - Email + Password login with 4-character ensemble - SignUp (
/signup) - Full registration with name, email, and password confirmation - ForgotPassword (
/forgot) - Email-based password recovery with success state
Desktop (1024px+) β Full layout with side-by-side split
Tablet (768px) β Adjusted spacing, scaled characters (-20%)
Mobile (480px) β Stacked layout, scaled characters (-40%)
Tiny (320px) β Minimal sizing, optimized touch targets
src/
βββ components/
β βββ characters/
β β βββ Character.jsx # Main character container, reaction state handler
β β βββ Eye.jsx # Eye component with mouse tracking logic
β β βββ character.module.css # Character shapes, mouth animations, responsiveness
β βββ ui/
β βββ Button.jsx # Reusable button component
β βββ Input.jsx # Input with password toggle functionality
β βββ ThemeToggle.jsx # Dark/light mode switcher
β βββ ui.module.css # UI component styling with responsive scaling
βββ context/
β βββ ThemeContext.jsx # Theme provider (dark/light) with localStorage
βββ pages/
β βββ SignIn.jsx # Login page with validation and 4 characters
β βββ SignUp.jsx # Registration page with form fields
β βββ ForgotPassword.jsx # Password reset flow with confirmation state
βββ styles/
β βββ auth.module.css # Authentication pages layout, form styling, media queries
β βββ global.css # Theme variables, eye animations, global styles
βββ App.jsx # Router configuration, main entry
βββ main.jsx # React DOM mount point
| Layer | Technology | Purpose |
|---|---|---|
| Frontend Framework | React 19.2.0 | Component-based UI |
| Routing | React Router 7.11.0 | Client-side navigation |
| Build Tool | Vite 7.2.4 | Fast ES module bundling |
| Styling | CSS Modules + CSS Variables | Scoped styles, theme management |
| State Management | React Hooks (useState) | Local component state |
| Linting | ESLint 9.39.1 | Code quality |
/* Light Mode (Default) */
--bg-page: #f3f4f6 /* Background */
--bg-panel: #ffffff /* Form panels */
--text-main: #1f2937 /* Primary text */
--text-muted: #6b7280 /* Secondary text */
--input-border: #e5e7eb /* Input borders */
/* Dark Mode */
--bg-page: #0f1419 /* Background */
--bg-panel: #1a2332 /* Form panels */
--text-main: #e5e7eb /* Primary text */
--text-muted: #9ca3af /* Secondary text */
--input-border: #374151 /* Input borders */- Shapes: Simple geometric forms (rectangle, box, semicircle, cylinder)
- Eyes: 30px circles with 10px pupils, smooth tracking animation
- Mouth: Context-sensitive animations (20 states across all animations)
- Shadows: Subtle drop-shadow for depth perception
/* Eye Tracking */
.pupil {
transform: translate(${Math.cos(angle) * 5}px, ${Math.sin(angle) * 5}px);
/* Uses real-time angle calculation from mouse position */
}
/* Mouth Transitions */
transition: all 0.2s ease-in-out;
/* Tilt Animation (Validation Failure) */
@keyframes tilt-shake {
0%, 100% { transform: rotate(-5deg); }
25% { transform: rotate(-8deg); }
50% { transform: rotate(-5deg); }
75% { transform: rotate(-2deg); }
}- Node.js β₯ 18.x
- npm β₯ 9.x or yarn β₯ 3.x
# Clone the repository
git clone <repository-url>
cd UXwebsite
# Install dependencies
npm install
# Start development server
npm run devDevelopment server runs at http://localhost:5173
# Create optimized production bundle
npm run build
# Preview production build locally
npm run preview# Run ESLint
npm run lint
# Fix linting issues automatically
npm run lint -- --fix<Character
reaction="idle|typing|closed|side|surprise|submit" // Current reaction state
color="#4f46e5" // Hex color
variant="rectangle|box|semicircle|cylinder" // Shape variant
tilted={false} // Enable tilt animation
/>Reaction States:
idle: Default, eyes track mousetyping: Eyes track, typing mouthclosed: Eyes closed, closed mouth (password input)side: Eyes left, scared mouth (password hover)surprise: Large eyes, open mouth (validation error)submit: Happy eyes, smiling mouth (form success)
<Input
type="text|email|password" // Input type
placeholder="Enter..." // Placeholder text
isPassword={false} // Enable password toggle
showPassword={false} // Show/hide password text
onTogglePassword={() => {}} // Password toggle handler
value="" // Controlled input value
onChange={(e) => {}} // Change handler
onFocus={() => {}} // Focus handler
onBlur={() => {}} // Blur handler
/>Features:
- Conditional eye/lock icon for password fields
- Smooth icon hover animations
- Touch-friendly click area (42px right padding)
- iOS zoom prevention (16px base font-size)
<Button
type="submit|button|reset"
onClick={() => {}}
>
Click me
</Button>Styling:
- 12px padding, 8px border-radius
- Indigo primary (#4f46e5)
- Hover scale(1.05) with smooth transition
- Active scale(0.95)
<ThemeToggle />Behavior:
- Floating position (top: 25px, right: 25px)
- Toggles
.darkclass on root element - Persists preference (can be extended to localStorage)
// SignIn.jsx
const [reaction, setReaction] = useState("idle");
const [showPassword, setShowPassword] = useState(false);
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [tilted, setTilted] = useState(false);
const [errorMessage, setErrorMessage] = useState("");const handleSubmit = (e) => {
e.preventDefault();
if (!email.trim() || !password.trim()) {
// Validation failed
setReaction("surprise");
setTilted(true);
setErrorMessage("Please fill in all fields");
// Auto-reset after 3 seconds
setTimeout(() => {
setReaction("idle");
setTilted(false);
setErrorMessage("");
}, 3000);
return;
}
// Validation passed
setReaction("submit");
};// On typing in password field
onChange={() => setReaction("closed")} // Eyes close
// On mouse enter (SignIn only)
onMouseEnter={() => setReaction("side")} // Eyes shift left
// On toggle click
onTogglePassword={() => {
setShowPassword(!showPassword);
setReaction("closed"); // Eyes close briefly
}}// On mouse enter
onMouseEnter={() => setReaction("submit")} // Happy face
// On mouse leave
onMouseLeave={() => setReaction("idle")} // Back to normalDesktop (1024px+)
- Split-screen: 50% artSide | 50% formSide
- Full character sizes
- Eye size: 30px
Tablet (768px)
.container { flex-direction: column; }
.scene > div { transform: translateY(40px); }
.character (all variants) { scale: 0.75; }
.eye { width: 24px; height: 24px; }Mobile (480px)
.scene > div { transform: translateY(60px); }
.character (all variants) { scale: 0.5; }
.eye { width: 20px; height: 20px; }
.input { font-size: 16px; } /* iOS zoom prevention */Tiny Phones (320px)
.character (all variants) { scale: 0.4; }
.input { font-size: 14px; }- Button padding: 12px (minimum 44Γ44px touch target)
- Input height: 44px
- Eye toggle: 42px right padding for easier clicking
- Dark mode for reduced eye strain on mobile
- Client-side validation only (frontend UX feedback)
- Password fields use HTML
type="password"when not toggled - No actual backend integration (demo/prototype)
- Server-side validation - Never trust client-side validation alone
- HTTPS only - Encrypt data in transit
- Password hashing - Use bcrypt or similar on backend
- Rate limiting - Prevent brute force attacks
- CSRF protection - Add token-based CSRF prevention
- Input sanitization - Sanitize all inputs before processing
- CORS headers - Restrict API access appropriately
Authentication Flow:
- Submit empty form β Surprise animation triggers for 3s
- Fill email only β Error on submit
- Fill password only β Error on submit
- Fill both β Success animation
- Toggle password β Eyes briefly close
Character Animations:
- Hover password field (SignIn) β Eyes shift left
- Type in password field β Eyes close
- Hover submit button β Happy face
- Leave form area β Idle state
Responsive Design:
- Desktop view β 4 characters visible (SignIn)
- Tablet (768px) β Characters scaled, positioned lower
- Mobile (480px) β Stacked layout, further scaled
- Tiny (320px) β Minimal sizing, readable text
Theme Toggle:
- Click theme icon β Colors invert smoothly
- Light mode β Light backgrounds, dark text
- Dark mode β Dark backgrounds, light text
- Preference persists β (can extend with localStorage)
Navigation:
- SignIn β SignUp β Works
- SignUp β SignIn β Works
- SignIn β ForgotPassword β Works
- Direct URL access β Page loads correctly
- No Backend Integration - Form submissions don't persist data
- No Email Validation - Basic required-field checks only
- No Password Strength Meter - Could enhance with Zxcvbn
- Theme Persistence - Currently not saved to localStorage
- No Accessibility Focus Management - Could improve keyboard navigation
- No Error Boundary - Could add React Error Boundary
- No Loading States - No async operation feedback
React Patterns:
- Functional components with hooks
- Controlled form inputs
- Event handler delegation
- Conditional rendering
- Component composition
- State lifting
Advanced Techniques:
- Mouse position tracking (real-time angle calculation)
- CSS-in-JS with Modules
- Theme switching with CSS Variables
- Responsive design with media queries
- Animation timing with setTimeout
- Dynamic className management
Best Practices:
- Single Responsibility Principle (small components)
- DRY (reusable Input/Button components)
- Semantic HTML structure
- Accessible button/form elements
- Mobile-first design approach
- Performance optimization (Vite)
Contributions are welcome! Please follow these guidelines:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit changes (
git commit -m 'Add amazing feature') - Push to branch (
git push origin feature/amazing-feature) - Open a Pull Request
- Use ESLint configuration provided
- Follow React best practices (hooks over class components)
- Use CSS Modules for scoped styling
- Keep components small and focused
- Add descriptive comments for complex logic
This project is licensed under the MIT License - see the LICENSE file for details.
- π Bug Reports: Open an issue with detailed reproduction steps
- π‘ Feature Requests: Suggest enhancements with use cases
- π§ Questions: Check existing issues before asking
- Inspired by modern UX design principles
- Built with React, Vite, and modern web standards
- Character animation concepts from playful UI design
Made with β€οΈ by Yuri Vieira
β If you find this project helpful, please give it a star!
Last Updated: December 2025 Version: 1.0.0
