Skip to content

Getting Started Frontend

Mathew Storm edited this page Jul 29, 2025 · 1 revision

🎨 Frontend Developer Onboarding - React & UI

Welcome frontend developers! Let's build interfaces that work everywhere - from modern browsers to 2G connections.

🎯 What You'll Build

As a frontend developer for EduLite, you'll create:

  • 📱 Responsive interfaces that work on any device
  • Lightning-fast pages optimized for slow networks
  • 🌍 Multi-language support for global accessibility
  • 🌙 Dark mode for comfortable studying
  • ♾️ Lightweight features for unreliable connections

📋 Prerequisites

Required Knowledge

  • HTML/CSS basics (elements, styling, flexbox)
  • JavaScript fundamentals (functions, arrays, objects)
  • Git basics (clone, commit, push)

Nice to Have

  • React experience (we'll teach you!)
  • Tailwind CSS knowledge
  • Component-based thinking
  • Accessibility awareness

🚀 Your First Hour

Quick Setup Check

Already did the Quick Start? Skip to Understanding the Architecture!

Need setup? Follow these steps:

# 1. Clone your fork
git clone https://github.com/YOUR_USERNAME/EduLite.git
cd EduLite/Frontend/EduLiteFrontend

# 2. Install dependencies
npm install

# 3. Start development server
npm run dev

Visit http://localhost:5173/ - You should see EduLite!

🎨 Understanding the Architecture

Project Structure

Frontend/EduLiteFrontend/
├── src/
│   ├── components/           # Reusable UI components
│   │   ├── Navbar.jsx       # Navigation bar
│   │   ├── DarkModeToggle.jsx # Theme switcher
│   │   ├── LanguageSwitcher.jsx
│   │   └── Sidebar.jsx     # Mobile menu
│   ├── pages/               # Full page components
│   │   ├── Home.jsx        # Landing page
│   │   ├── About.jsx       # About EduLite
│   │   └── Chats.jsx       # Messaging
│   ├── i18n/                # Translations
│   │   ├── locales/
│   │   │   ├── en.json     # English
│   │   │   └── ar.json     # Arabic
│   │   └── index.js        # i18n setup
│   ├── assets/              # Images, logos
│   ├── App.jsx              # Main app component
│   └── main.jsx            # Entry point
├── public/                  # Static files
├── index.html              # HTML template
└── vite.config.js          # Build configuration

Key Technologies

1. React + Vite

  • React: Component-based UI library
  • Vite: Super fast build tool (way faster than webpack!)

2. Tailwind CSS

  • Utility-first CSS framework
  • No more naming classes!
  • Responsive by default

3. React Router

  • Client-side routing
  • No page reloads = faster experience

4. i18next

  • Internationalization (i18n)
  • Easy translation management

📝 Your First Frontend Task

Task: Add a Study Timer Component

Let's create a simple timer to help students track study sessions!

Step 1: Create the Component

File: src/components/StudyTimer.jsx

import { useState, useEffect } from 'react';
import { useTranslation } from 'react-i18next';

function StudyTimer() {
  const { t } = useTranslation();
  const [seconds, setSeconds] = useState(0);
  const [isActive, setIsActive] = useState(false);

  useEffect(() => {
    let interval = null;
    
    if (isActive) {
      interval = setInterval(() => {
        setSeconds(seconds => seconds + 1);
      }, 1000);
    } else if (!isActive && seconds !== 0) {
      clearInterval(interval);
    }
    
    return () => clearInterval(interval);
  }, [isActive, seconds]);

  const toggle = () => {
    setIsActive(!isActive);
  };

  const reset = () => {
    setSeconds(0);
    setIsActive(false);
  };

  const formatTime = (totalSeconds) => {
    const hours = Math.floor(totalSeconds / 3600);
    const minutes = Math.floor((totalSeconds % 3600) / 60);
    const secs = totalSeconds % 60;
    
    return `${hours.toString().padStart(2, '0')}:
            ${minutes.toString().padStart(2, '0')}:
            ${secs.toString().padStart(2, '0')}`;
  };

  return (
    <div className="bg-white dark:bg-gray-800 rounded-lg shadow-md p-6">
      <h3 className="text-lg font-semibold mb-4 text-gray-800 dark:text-white">
        {t('studyTimer.title', 'Study Timer')}
      </h3>
      
      <div className="text-4xl font-mono text-center mb-6 text-blue-600 dark:text-blue-400">
        {formatTime(seconds)}
      </div>
      
      <div className="flex gap-2 justify-center">
        <button
          onClick={toggle}
          className="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600 
                     transition-colors duration-200"
        >
          {isActive ? t('studyTimer.pause', 'Pause') : t('studyTimer.start', 'Start')}
        </button>
        
        <button
          onClick={reset}
          className="px-4 py-2 bg-gray-500 text-white rounded hover:bg-gray-600 
                     transition-colors duration-200"
        >
          {t('studyTimer.reset', 'Reset')}
        </button>
      </div>
    </div>
  );
}

export default StudyTimer;

Step 2: Add Translations

File: src/i18n/locales/en.json

Add to the JSON:

{
  // ... existing translations ...
  "studyTimer": {
    "title": "Study Timer",
    "start": "Start",
    "pause": "Pause",
    "reset": "Reset"
  }
}

File: src/i18n/locales/ar.json

{
  // ... existing translations ...
  "studyTimer": {
    "title": "مؤقت الدراسة",
    "start": "ابدأ",
    "pause": "إيقاف مؤقت",
    "reset": "إعادة تعيين"
  }
}

Step 3: Use the Component

File: src/pages/Home.jsx

Import and add the timer:

import StudyTimer from '../components/StudyTimer';

// In your JSX, add:
<StudyTimer />

Step 4: Test Your Component

  1. Save all files
  2. Check the browser (auto-refreshes with Vite!)
  3. Test the timer functionality
  4. Switch languages to test translations
  5. Toggle dark mode to check styling

🎉 Congratulations! You've created your first EduLite component!

📖 Deep Dive: Frontend Standards

1. Component Philosophy

Build for reusability and accessibility:

// ❌ BAD: Hardcoded, not reusable
function Button() {
  return (
    <button className="bg-blue-500 text-white px-4 py-2">
      Click Me
    </button>
  );
}

// ✅ GOOD: Flexible, accessible
function Button({ 
  children, 
  variant = 'primary', 
  size = 'medium',
  onClick,
  disabled = false,
  ...props 
}) {
  const variants = {
    primary: 'bg-blue-500 hover:bg-blue-600 text-white',
    secondary: 'bg-gray-500 hover:bg-gray-600 text-white',
    danger: 'bg-red-500 hover:bg-red-600 text-white'
  };
  
  const sizes = {
    small: 'px-3 py-1 text-sm',
    medium: 'px-4 py-2',
    large: 'px-6 py-3 text-lg'
  };
  
  return (
    <button
      className={`
        ${variants[variant]} 
        ${sizes[size]} 
        rounded transition-colors duration-200
        disabled:opacity-50 disabled:cursor-not-allowed
        focus:outline-none focus:ring-2 focus:ring-offset-2
      `}
      onClick={onClick}
      disabled={disabled}
      {...props}
    >
      {children}
    </button>
  );
}

2. Performance First

Optimize for Slow Networks

// ❌ BAD: Large image, no optimization
<img src="/huge-image.png" alt="Hero" />

// ✅ GOOD: Optimized loading
import { useState, useEffect } from 'react';

function OptimizedImage({ src, alt, placeholder }) {
  const [imageSrc, setImageSrc] = useState(placeholder);
  const [loading, setLoading] = useState(true);
  
  useEffect(() => {
    const img = new Image();
    img.src = src;
    img.onload = () => {
      setImageSrc(src);
      setLoading(false);
    };
  }, [src]);
  
  return (
    <div className="relative">
      <img 
        src={imageSrc} 
        alt={alt}
        className={`transition-opacity duration-300 
          ${loading ? 'opacity-50' : 'opacity-100'}`}
      />
      {loading && (
        <div className="absolute inset-0 flex items-center justify-center">
          <div className="animate-spin h-8 w-8 border-b-2 border-blue-500" />
        </div>
      )}
    </div>
  );
}

Code Splitting

// Lazy load heavy components
import { lazy, Suspense } from 'react';

const HeavyComponent = lazy(() => import('./HeavyComponent'));

function App() {
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <HeavyComponent />
    </Suspense>
  );
}

3. Accessibility Standards

Always Include ARIA Labels

// ❌ BAD: No accessibility
<div onClick={handleClick}>×</div>

// ✅ GOOD: Accessible
<button
  onClick={handleClick}
  aria-label="Close dialog"
  className="p-2 hover:bg-gray-100 rounded"
>
  <span aria-hidden="true">×</span>
</button>

Keyboard Navigation

function AccessibleMenu() {
  const [isOpen, setIsOpen] = useState(false);
  
  const handleKeyDown = (e) => {
    if (e.key === 'Escape') {
      setIsOpen(false);
    }
  };
  
  return (
    <div onKeyDown={handleKeyDown}>
      <button
        onClick={() => setIsOpen(!isOpen)}
        aria-expanded={isOpen}
        aria-haspopup="true"
      >
        Menu
      </button>
      
      {isOpen && (
        <ul role="menu">
          <li role="menuitem" tabIndex="0">Option 1</li>
          <li role="menuitem" tabIndex="0">Option 2</li>
        </ul>
      )}
    </div>
  );
}

4. Responsive Design

Mobile-First Approach

// Start with mobile, add desktop styles
function ResponsiveCard({ title, content }) {
  return (
    <div className="
      p-4 bg-white rounded-lg shadow
      /* Mobile: Full width */
      w-full
      /* Tablet: Half width */
      md:w-1/2
      /* Desktop: Third width */
      lg:w-1/3
      /* Large desktop: Quarter width */
      xl:w-1/4
    ">
      <h3 className="
        text-lg font-semibold mb-2
        /* Larger text on desktop */
        lg:text-xl
      ">
        {title}
      </h3>
      <p className="text-gray-600">{content}</p>
    </div>
  );
}

5. Dark Mode Support

Every Component Must Support Dark Mode

function Card({ children }) {
  return (
    <div className="
      /* Light mode */
      bg-white text-gray-900
      /* Dark mode */
      dark:bg-gray-800 dark:text-white
      /* Shared styles */
      rounded-lg shadow-md p-6
      transition-colors duration-200
    ">
      {children}
    </div>
  );
}

6. Internationalization (i18n)

Always Use Translation Keys

// ❌ BAD: Hardcoded text
<h1>Welcome to EduLite</h1>

// ✅ GOOD: Translatable
import { useTranslation } from 'react-i18next';

function Welcome() {
  const { t } = useTranslation();
  
  return (
    <h1>{t('welcome.title', 'Welcome to EduLite')}</h1>
  );
}

Handle RTL Languages

function Layout({ children }) {
  const { i18n } = useTranslation();
  const isRTL = i18n.dir() === 'rtl';
  
  return (
    <div className={`${isRTL ? 'rtl' : 'ltr'}`} dir={i18n.dir()}>
      {children}
    </div>
  );
}

🔧 Common Frontend Tasks

1. Creating a New Page

// 1. Create page component
// src/pages/StudyGroups.jsx
import { useTranslation } from 'react-i18next';

function StudyGroups() {
  const { t } = useTranslation();
  
  return (
    <div className="container mx-auto px-4 py-8">
      <h1 className="text-3xl font-bold mb-6">
        {t('studyGroups.title')}
      </h1>
      {/* Page content */}
    </div>
  );
}

export default StudyGroups;

// 2. Add route in App.jsx
import StudyGroups from './pages/StudyGroups';

<Route path="/study-groups" element={<StudyGroups />} />

// 3. Add navigation link
<Link to="/study-groups">{t('nav.studyGroups')}</Link>

2. Creating a Reusable Component

// src/components/ui/Alert.jsx
import { XMarkIcon } from '@heroicons/react/24/outline';

function Alert({ 
  type = 'info', 
  title, 
  message, 
  onClose,
  closable = true 
}) {
  const types = {
    info: 'bg-blue-50 text-blue-800 border-blue-200',
    success: 'bg-green-50 text-green-800 border-green-200',
    warning: 'bg-yellow-50 text-yellow-800 border-yellow-200',
    error: 'bg-red-50 text-red-800 border-red-200'
  };
  
  return (
    <div className={`${types[type]} border rounded-lg p-4 relative`}>
      <div className="flex">
        <div className="flex-1">
          {title && (
            <h3 className="font-semibold mb-1">{title}</h3>
          )}
          <p>{message}</p>
        </div>
        
        {closable && onClose && (
          <button
            onClick={onClose}
            className="ml-4 p-1 hover:bg-black/10 rounded"
            aria-label="Close alert"
          >
            <XMarkIcon className="h-5 w-5" />
          </button>
        )}
      </div>
    </div>
  );
}

export default Alert;

3. Handling API Calls

// src/hooks/useApi.js
import { useState, useEffect } from 'react';

function useApi(url, options = {}) {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);
  
  useEffect(() => {
    const fetchData = async () => {
      try {
        setLoading(true);
        const response = await fetch(url, {
          ...options,
          headers: {
            'Content-Type': 'application/json',
            ...options.headers
          }
        });
        
        if (!response.ok) {
          throw new Error(`HTTP error! status: ${response.status}`);
        }
        
        const json = await response.json();
        setData(json);
      } catch (err) {
        setError(err.message);
      } finally {
        setLoading(false);
      }
    };
    
    fetchData();
  }, [url]);
  
  return { data, loading, error };
}

// Usage
function UserList() {
  const { data, loading, error } = useApi('/api/users/');
  
  if (loading) return <div>Loading...</div>;
  if (error) return <div>Error: {error}</div>;
  
  return (
    <ul>
      {data.results.map(user => (
        <li key={user.id}>{user.username}</li>
      ))}
    </ul>
  );
}

📏 Frontend Best Practices

1. State Management

// Use Context for global state
import { createContext, useContext, useState } from 'react';

const UserContext = createContext();

export function UserProvider({ children }) {
  const [user, setUser] = useState(null);
  
  return (
    <UserContext.Provider value={{ user, setUser }}>
      {children}
    </UserContext.Provider>
  );
}

export function useUser() {
  const context = useContext(UserContext);
  if (!context) {
    throw new Error('useUser must be used within UserProvider');
  }
  return context;
}

2. Error Boundaries

class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false };
  }
  
  static getDerivedStateFromError(error) {
    return { hasError: true };
  }
  
  componentDidCatch(error, errorInfo) {
    console.error('Error caught:', error, errorInfo);
  }
  
  render() {
    if (this.state.hasError) {
      return (
        <div className="flex flex-col items-center justify-center h-screen">
          <h1 className="text-2xl font-bold mb-4">
            Oops! Something went wrong.
          </h1>
          <button
            onClick={() => window.location.reload()}
            className="px-4 py-2 bg-blue-500 text-white rounded"
          >
            Reload Page
          </button>
        </div>
      );
    }
    
    return this.props.children;
  }
}

3. Performance Optimization

// Memoize expensive computations
import { useMemo } from 'react';

function ExpensiveList({ items, filter }) {
  const filteredItems = useMemo(() => {
    return items.filter(item => 
      item.name.toLowerCase().includes(filter.toLowerCase())
    );
  }, [items, filter]);
  
  return (
    <ul>
      {filteredItems.map(item => (
        <li key={item.id}>{item.name}</li>
      ))}
    </ul>
  );
}

// Prevent unnecessary re-renders
import { memo } from 'react';

const ExpensiveComponent = memo(function ExpensiveComponent({ data }) {
  // Component only re-renders if data changes
  return <div>{/* Complex rendering */}</div>;
});

📚 Resources

EduLite Specific

React Learning

Tailwind CSS

Accessibility

🆘 Getting Help

Where to Ask

  1. Component questions: Comment in your PR
  2. Design questions: Discord #design channel
  3. Quick help: Discord #frontend channel
  4. Bugs: Create a GitHub issue

How to Ask

**What I'm building:**
A study group card component

**Problem:**
The card looks different in dark mode

**What I tried:**
- Added dark: prefix to Tailwind classes
- Checked parent components for overrides

**Code:**
``jsx
<div className="bg-white dark:bg-gray-800">
  {/* Content */}
</div>
``

**Screenshot:**
[Attach screenshot]

🌟 You're Ready!

You now have everything to start building amazing frontend features:

  • ✅ Environment set up
  • ✅ Architecture understood
  • ✅ First component built
  • ✅ Standards learned
  • ✅ Resources bookmarked

Your Impact

  • 1 component = Better user experience for thousands
  • 1 optimization = Faster loading on slow networks
  • 1 accessibility fix = More inclusive education
  • 1 translation = New language community served

Welcome to the frontend team! Let's build interfaces that work for everyone, everywhere. 🎨

💚 Good design is invisible. Great design is accessible. The best design empowers students to focus on learning, not on figuring out the interface.

Clone this wiki locally