Overview
The Footer component (taskbar) provides the Windows XP taskbar experience with a Start menu, open window buttons, system tray icons, and a clock.
Component Structure
function Footer({
onMouseDownApp, // Handler for taskbar window buttons
apps, // Array of open applications
focusedAppId, // Currently focused app ID
onMouseDown, // Generic mousedown handler
onClickMenuItem, // Start menu item handler
}) {
const [time, setTime] = useState(getTime);
const [menuOn, setMenuOn] = useState(false);
return (
<Container onMouseDown={_onMouseDown}>
{/* Start Menu */}
<div ref={menu} className="footer__start__menu">
{menuOn && <FooterMenu onClick={_onClickMenuItem} />}
</div>
{/* Left: Start button + window buttons */}
<div className="footer__items left">
<img src={startButton} className="footer__start" onMouseDown={toggleMenu} />
{apps.map(app => !app.header.noFooterWindow && (
<FooterWindow key={app.id} {...app} />
))}
</div>
{/* Right: System tray + clock */}
<div className="footer__items right">
<img src={sound} onClick={toggleVolume} />
<img src={usb} />
<img src={risk} />
<Balloon />
<div className="footer__time">{time}</div>
</div>
</Container>
);
}
Source: src/WinXP/Footer/index.jsx:32-157
Taskbar Layout
Container Styling
const Container = styled.footer`
height: 30px;
background: linear-gradient(
to bottom,
#1f2f86 0,
#3165c4 3%,
#3682e5 6%,
#4490e6 10%,
// ... Windows XP blue gradient
);
position: absolute;
bottom: 0;
right: 0;
left: 0;
display: flex;
`;
Source: src/WinXP/Footer/index.jsx:174-199
Left Section (Start + Windows)
.footer__items.left {
height: 100%;
flex: 1;
overflow: hidden; /* Scroll if too many windows */
}
Right Section (System Tray)
.footer__items.right {
background: linear-gradient(
to bottom,
#0c59b9 1%,
#139ee9 6%,
// ... lighter blue gradient
);
border-left: 1px solid #1042af;
box-shadow: inset 1px 0 1px #18bbff;
padding: 0 10px;
margin-left: 10px;
flex-shrink: 0; /* Fixed width */
}
Source: src/WinXP/Footer/index.jsx:200-227
The iconic Windows XP Start button:
<img
src={startButton}
alt="start"
className="footer__start"
onMouseDown={toggleMenu}
/>
Toggle Logic
function toggleMenu(e) {
e.stopPropagation();
onMouseDown(); // Focus desktop
setMenuOn(on => !on);
}
Source: src/WinXP/Footer/index.jsx:48-52
Styling
.footer__start {
height: 100%;
margin-right: 10px;
position: relative;
&:hover {
filter: brightness(105%);
}
&:active {
pointer-events: none;
filter: brightness(85%);
}
}
Source: src/WinXP/Footer/index.jsx:232-243
The Start menu is a complex component with two columns and nested submenus.
Component Structure
function FooterMenu({ className, onClick }) {
const [hovering, setHovering] = useState('');
return (
<div className={className}>
<header>
<img className="header__img" src={user} alt="avatar" />
<span className="header__text">Skillz</span>
<EggCounter />
</header>
<section className="menu" onMouseOver={onMouseOver}>
<hr className="orange-hr" />
<div className="menu__left">
{/* Pinned programs */}
</div>
<div className="menu__right">
{/* System items */}
</div>
</section>
<footer>
<div onClick={() => onClick('Log Off')}>Log Off</div>
<div onClick={() => onClick('Turn Off Computer')}>Turn Off Computer</div>
</footer>
</div>
);
}
Source: src/WinXP/Footer/FooterMenu.jsx:94-254
<Item onClick={onClick} text="Internet" icon={ie}>
<div className="menu__item__subtext">Internet Explorer</div>
</Item>
<Items
onClick={onClick}
items={[
{ icon: mine, text: 'Minesweeper' },
{ icon: notepad, text: 'Notepad' },
{ icon: winamp, text: 'Winamp' },
{ icon: paint, text: 'Paint' },
{ icon: mediaPlayer, text: 'Media Player' },
{ icon: messenger, text: 'PictoChat' },
]}
/>
Source: src/WinXP/Footer/FooterMenu.jsx:111-128
Egg Counter
The start menu header displays an egg counter:
function EggCounter() {
const [count, setCount] = useState(() => {
try {
const savedData = localStorage.getItem('eggData');
const parsed = savedData ? JSON.parse(savedData) : [];
return Array.isArray(parsed) ? parsed.length : 0;
} catch (e) {
return 0;
}
});
return (
<EggCounterContainer>
<EggCounterIcon src={eggIcon} alt="Egg" />
<span>x {count}</span>
</EggCounterContainer>
);
}
Source: src/WinXP/Footer/FooterMenu.jsx:57-92
The egg counter reads from localStorage and updates in real-time using storage events.
Hovering over items with arrows shows submenus:
<Item
style={
hovering === 'All Programs'
? { backgroundColor: '#2f71cd', color: '#FFF' }
: {}
}
text="All Programs"
icon={empty}
>
{hovering === 'All Programs' && (
<SubMenu data={AllPrograms} onClick={onClick} />
)}
</Item>
Source: src/WinXP/Footer/FooterMenu.jsx:132-158
Menu Click Handler
function onClickMenuItem(itemName) {
switch (itemName) {
case 'Internet':
dispatch({ type: ADD_APP, payload: appSettings['Internet Explorer'] });
break;
case 'Minesweeper':
dispatch({ type: ADD_APP, payload: appSettings.Minesweeper });
break;
case 'Log Off':
dispatch({ type: POWER_OFF, payload: POWER_STATE.LOG_OFF });
break;
case 'Turn Off Computer':
dispatch({ type: POWER_OFF, payload: POWER_STATE.TURN_OFF });
break;
default:
dispatch({
type: ADD_APP,
payload: {
...appSettings.Error,
injectProps: {
message: `C:\\\nApplication '${itemName}' not found`,
},
},
});
}
}
Source: src/WinXP/index.jsx:138-190
Each open application gets a taskbar button.
Component
function FooterWindow({ id, icon, title, onMouseDown, isFocus }) {
function _onMouseDown() {
onMouseDown(id);
}
return (
<div
onMouseDown={_onMouseDown}
className={`footer__window ${isFocus ? 'focus' : 'cover'}`}
>
<img className="footer__icon" src={icon} alt={title} />
<div className="footer__text">{title}</div>
</div>
);
}
Source: src/WinXP/Footer/index.jsx:159-172
Clicking a taskbar button has different effects:
function onMouseDownFooterApp(id) {
const app = state.apps.find(a => a.id === id);
if (app) {
if (app.id === focusedAppId && !app.minimized) {
// Already focused -> minimize
dispatch({ type: MINIMIZE_APP, payload: id });
} else {
// Not focused or minimized -> focus
dispatch({ type: FOCUS_APP, payload: id });
}
}
}
Source: src/WinXP/index.jsx:112-121
.footer__window {
flex: 1;
max-width: 150px;
color: #fff;
border-radius: 2px;
margin-top: 2px;
padding: 0 8px;
height: 22px;
font-size: 11px;
background-color: #3c81f3;
box-shadow: inset -1px 0px rgba(0, 0, 0, 0.3),
inset 1px 1px 1px rgba(255, 255, 255, 0.2);
}
/* Focused window */
.footer__window.focus {
background-color: #1e52b7;
box-shadow: inset 0 0 1px 1px rgba(0, 0, 0, 0.2),
inset 1px 0 1px rgba(0, 0, 0, 0.7);
}
/* Hover effect */
.footer__window.cover:hover {
background-color: #53a3ff;
}
Source: src/WinXP/Footer/index.jsx:250-309
System Tray
Icons
The system tray displays status icons:
<div className="footer__items right">
<img
ref={soundIconRef}
className="footer__icon"
src={sound}
alt="Volume"
onClick={() => setShowVolume(v => !v)}
style={{ cursor: 'pointer' }}
/>
<img className="footer__icon" src={usb} alt="" />
<img className="footer__icon" src={risk} alt="" />
<Balloon /> {/* Notification balloon */}
<div className="footer__time">{time}</div>
</div>
Source: src/WinXP/Footer/index.jsx:128-143
Icon Styling
.footer__icon {
height: 15px;
width: 15px;
}
Source: src/WinXP/Footer/index.jsx:266-269
Clock
The clock updates every second:
const getTime = () => {
const date = new Date();
let hour = date.getHours();
let hourPostFix = 'AM';
let min = date.getMinutes();
if (hour >= 12) {
hour -= 12;
hourPostFix = 'PM';
}
if (hour === 0) {
hour = 12;
}
if (min < 10) {
min = '0' + min;
}
return `${hour}:${min} ${hourPostFix}`;
};
const [time, setTime] = useState(getTime);
useEffect(() => {
const timer = setInterval(() => {
const newTime = getTime();
newTime !== time && setTime(newTime);
}, 1000);
return () => clearInterval(timer);
}, [time]);
Source: src/WinXP/Footer/index.jsx:14-67
Clock Styling
.footer__time {
margin: 0 5px;
color: #fff;
font-size: 11px;
font-weight: lighter;
text-shadow: none;
}
Source: src/WinXP/Footer/index.jsx:310-316
Volume Control
Clicking the volume icon shows a volume slider:
const [showVolume, setShowVolume] = useState(false);
const { volume, setVolume, isMuted, setIsMuted } = useVolume();
<img
ref={soundIconRef}
src={sound}
onClick={() => setShowVolume(v => !v)}
/>
<div ref={sliderRef}>
{showVolume && (
<VolumeSlider
volume={volume}
onVolumeChange={setVolume}
isMuted={isMuted}
onMuteChange={setIsMuted}
/>
)}
</div>
Source: src/WinXP/Footer/index.jsx:43-154
Click Outside Handler
useEffect(() => {
function handleClickOutside(event) {
if (
showVolume &&
sliderRef.current &&
!sliderRef.current.contains(event.target) &&
soundIconRef.current &&
!soundIconRef.current.contains(event.target)
) {
setShowVolume(false);
}
}
document.addEventListener('mousedown', handleClickOutside);
return () => {
document.removeEventListener('mousedown', handleClickOutside);
};
}, [showVolume]);
Source: src/WinXP/Footer/index.jsx:82-99
The volume control integrates with the VolumeContext to persist settings across the application.
Notification Balloon
The system tray includes a notification balloon component:
<div style={{ position: 'relative', width: 0, height: 0 }}>
<Balloon />
</div>
This provides tooltip-style notifications for system events.
The Start menu appears above the Start button:
.footer__start__menu {
position: absolute;
left: 0;
box-shadow: 2px 4px 2px rgba(0, 0, 0, 0.5);
bottom: 100%; /* Position above taskbar */
}
Source: src/WinXP/Footer/index.jsx:244-249
Outside Click Detection
The Start menu closes when clicking outside:
useEffect(() => {
if (!menuOn) return;
const target = menu.current;
if (!target) return;
function handleMouseDown(e) {
if (e.target.closest('.footer__start')) return; // Ignore Start button
if (!target.contains(e.target)) {
setMenuOn(false);
}
}
window.addEventListener('mousedown', handleMouseDown);
return () => window.removeEventListener('mousedown', handleMouseDown);
}, [menuOn]);
Source: src/WinXP/Footer/index.jsx:68-80
Next Steps
Desktop Overview
Return to desktop architecture
Windows
Learn about window management
Icons
Explore desktop icons