Prepared as part of the comprehensive bug-fix & feature sprint.
This document covers visual, interaction, and architectural suggestions for the next UI iteration.
The current frontend is a Vanilla JS single-page application rendered entirely through DOM manipulation in three monolithic files (app.js, company.js, candidate.js). While functional, this architecture creates scaling pain: global variable collisions, no component isolation, brittle event wiring via inline onclick strings, and template literals that embed raw HTML without consistent XSS guards.
Recommended direction: Migrate incrementally to a component-based architecture (React / Vue / Svelte) while preserving the existing REST API surface. This can be done module-by-module — start with the candidate portal, then company, then admin.
| Area | Issue | Impact |
|---|---|---|
| Script coupling | company.js (2 400 lines), candidate.js (1 900 lines) — single files |
Any edit risks breaking unrelated features |
| State management | Globals: authToken, currentUser, currentRole, blindMode, selectedApplications |
Race conditions between tabs; no reactivity |
| Inline styles | 60 %+ of UI uses inline style="..." in template literals |
Dark-mode must target [style*=] selectors — fragile |
| XSS surface | Mixed use of escapeHtml, esc, and raw interpolation |
Semi-patched but still risky in new code |
| Accessibility | No ARIA roles on dynamic content; no focus management on modals | WCAG 2.1 AA non-compliant |
| Mobile | Layouts rely on fixed widths; no responsive breakpoints in key views | Unusable below 768 px |
Replace scattered color literals with CSS custom properties:
:root {
--color-primary: #4F46E5;
--color-primary-hover: #4338CA;
--color-success: #10b981;
--color-danger: #ef4444;
--color-warning: #f59e0b;
--color-surface: #ffffff;
--color-surface-alt:#f8fafc;
--color-text: #1e293b;
--color-text-muted: #64748b;
--radius-sm: 6px;
--radius-md: 10px;
--radius-lg: 16px;
--shadow-sm: 0 1px 3px rgba(0,0,0,0.08);
--shadow-md: 0 4px 12px rgba(0,0,0,0.1);
--font-display: 'Clash Display', sans-serif;
--font-body: 'Inter', sans-serif;
}
[data-theme="dark"] {
--color-surface: #1e1e2e;
--color-surface-alt:#2a2a3e;
--color-text: #e2e8f0;
--color-text-muted: #94a3b8;
}Every component should reference var(--color-*) — zero hardcoded colors anywhere.
Build a shared set of reusable components:
| Component | Purpose |
|---|---|
<StatusBadge status="hired"/> |
Consistent color/icon mapping |
<Modal title="" onClose/> |
Focus-trap, ESC key, backdrop click |
<DataTable columns rows pagination/> |
Sortable, filterable, mobile-responsive |
<StatsCard icon label value trend/> |
KPI cards across all dashboards |
<Timeline events/> |
Status history, audit trail |
<QuizEngine questions onSubmit/> |
Reusable assessment taking flow |
<InterviewCard session/> |
Type badge, recording, proctoring |
┌──────────────────────────────────────────────────┐
│ Top Bar (logo, search, notifications, avatar) │
├──────────┬───────────────────────────────────────┤
│ Sidebar │ Main Content Area │
│ (nav) │ ┌─────────────────────────────────┐ │
│ │ │ Page Header + Breadcrumbs │ │
│ • Dash │ ├─────────────────────────────────┤ │
│ • Jobs │ │ Content │ │
│ • Apps │ │ │ │
│ • ... │ │ │ │
│ │ └─────────────────────────────────┘ │
├──────────┴───────────────────────────────────────┤
│ Footer (minimal — version, links) │
└──────────────────────────────────────────────────┘
- Sidebar collapses to icons on screens < 1024 px.
- Full hamburger menu on mobile (< 768 px).
Replace the current showNotification() toast with a notification center:
- Bell icon in top bar with unread count badge.
- Dropdown panel showing last 20 notifications.
- Backend: add a
notificationsMongoDB collection withuser_id,type,message,read,created_at. - WebSocket (socket.io) or SSE push for live updates when:
- Application status changes
- Interview scheduled
- New message from recruiter
- Assessment graded
Wire up transactional emails for all lifecycle events:
| Trigger | |
|---|---|
| Application submitted | Confirmation to candidate + alert to recruiter |
| Status → Shortlisted | Congratulations + next steps |
| Status → Interviewed | Interview invitation with calendar .ics attachment |
| Interview completed | Thank-you + recruiter review prompt |
| Status → Hired | Offer letter + onboarding link |
| Status → Rejected | Graceful decline with optional feedback |
| Assessment graded | Score report to candidate |
- Application tracker view: Kanban-style pipeline showing where each application sits.
- Interview prep: Show a "Prepare" button 15 min before scheduled interview with system check (webcam, mic, bandwidth).
- Skill gap analysis: After assessment, show a radar chart comparing candidate skills vs. job requirements.
- Pipeline board: Drag-and-drop Kanban for each job's applicants (Pending → Shortlisted → Interviewed → Hired/Rejected).
- Bulk actions toolbar: Floating action bar when checkboxes are selected.
- Interview replay: In-app video player for recorded interviews with timestamped malpractice markers.
- Comparative view: Side-by-side candidate comparison with scores, skills, and interview notes.
- Extract inline styles to CSS classes (already partially done with
enterprise-ui.css) - Fix all XSS vulnerabilities (completed in this sprint)
- Add comprehensive dark mode support (completed)
- Add
data-app-idattributes to checkboxes instead of parsingonchange.toString() - Add ARIA labels to all interactive elements
- Split each JS file into modules (ES6
import/exportor simple IIFE modules) - Create a shared
components/directory with template functions - Centralize API calls into an
ApiServiceclass with interceptors for auth, error-handling - Add a lightweight router (
window.hashchangeorhistory.pushState)
- Choose React (team familiarity) or Vue (lower barrier)
- Scaffold with Vite for fast dev server
- Migrate one portal at a time, starting with candidate (smallest surface)
- Keep the Flask backend unchanged — it's a clean REST API
- Implement design system tokens globally
- Add skeleton loaders for all async content
- Add keyboard navigation and screen reader support
- Implement dark/light mode via system preference with manual override
- Performance audit: lazy-load non-critical JS, optimize images, add service worker
| Metric | Current | Target |
|---|---|---|
| First Contentful Paint | ~2.5s (CDN scripts) | < 1.5s |
| Time to Interactive | ~4s | < 2.5s |
| Bundle Size | ~300 KB (unminified) | < 150 KB (minified + gzipped) |
- Defer Chart.js loading until analytics tab is actually opened.
- Lazy-load Google Fonts — use
font-display: swap. - Add
loading="lazy"to all images. - Minify and bundle JS/CSS for production (
esbuildorrollup).
The current system works and the bug-fix sprint has made it significantly more robust. The highest-ROI next step is Phase 2 — splitting the monolithic JS files into reusable modules without changing the visual design. This alone will reduce regression risk, make dark mode maintenance trivial, and set the stage for an eventual framework migration.