Skip to main content

Overview

The MenuBar component renders a macOS-inspired system bar at the top of the screen. It displays the portfolio branding on the left and a live clock with date on the right, creating an authentic desktop OS experience.

Features

  • Fixed Top Bar: Always visible at the top of the viewport
  • Live Clock: Updates every second with current time
  • Date Display: Shows weekday, month, and day
  • Glassmorphism: Translucent background with backdrop blur
  • Responsive: Hides some elements on small screens
  • No Props Required: Self-contained component

Props

The MenuBar component accepts no props. It’s a fully self-contained component.
export default function MenuBar() {
  // ...
}

Usage Examples

import MenuBar from "./components/MenuBar";

function App() {
  return (
    <div className="relative h-screen">
      <MenuBar />
      {/* Rest of application */}
    </div>
  );
}

Structure

The MenuBar is a fixed bar with two sections:
<div className="
  fixed top-0 left-0 right-0 
  h-7                          /* 28px height */
  bg-black/50                  /* Semi-transparent black */
  backdrop-blur-xl             /* Blur background content */
  border-b border-white/10     /* Subtle bottom border */
  flex items-center justify-between 
  px-4 
  z-[9999]                     /* Above all content */
  select-none                  /* Prevent text selection */
">
  {/* Left section: Branding */}
  <div className="flex items-center gap-4 text-white text-xs">
    {/* Content */}
  </div>

  {/* Right section: Time & Date */}
  <div className="flex items-center gap-3 text-white text-xs">
    {/* Content */}
  </div>
</div>

Left Section: Branding

The left side displays the Apple logo and portfolio branding:
<div className="flex items-center gap-4 text-white text-xs">
  {/* Apple logo */}
  <span className="text-lg leading-none"></span>
  
  {/* Name */}
  <span className="
    font-semibold 
    hover:bg-white/10 
    px-2 py-0.5 
    rounded 
    cursor-default 
    transition-colors
  ">
    Javier Navas
  </span>
  
  {/* Portfolio label (hidden on mobile) */}
  <span className="
    text-white/50 
    hover:bg-white/10 
    px-2 py-0.5 
    rounded 
    cursor-default 
    transition-colors 
    hidden sm:inline
  ">
    Portfolio
  </span>
</div>

Hover Effects

Each text item has a subtle hover effect:
hover:bg-white/10        /* Slight background on hover */
px-2 py-0.5              /* Padding for clickable feel */
rounded                  /* Rounded corners */
cursor-default           /* Standard cursor (not pointer) */
transition-colors        /* Smooth color transition */
This mimics macOS menu items without actual menu functionality.

Right Section: Time & Date

The right side shows live time and date:
<div className="flex items-center gap-3 text-white text-xs">
  {/* Date (hidden on mobile) */}
  <span className="text-white/60 hidden sm:inline">{dateStr}</span>
  
  {/* Time */}
  <span className="font-medium">{timeStr}</span>
</div>

Time Update Logic

The component uses setInterval to update every second:
import { useState, useEffect } from "react";

export default function MenuBar() {
  const [time, setTime] = useState(new Date());

  useEffect(() => {
    const interval = setInterval(() => setTime(new Date()), 1000);
    return () => clearInterval(interval);
  }, []);

  // Format time and date
  const timeStr = time.toLocaleTimeString("es-ES", { 
    hour: "2-digit", 
    minute: "2-digit" 
  });
  
  const dateStr = time.toLocaleDateString("es-ES", { 
    weekday: "short", 
    month: "short", 
    day: "numeric" 
  });

  // Render...
}

Formatting

const timeStr = time.toLocaleTimeString("es-ES", { 
  hour: "2-digit", 
  minute: "2-digit" 
});
// Output: "14:30"

Cleanup

The interval is properly cleaned up on unmount:
useEffect(() => {
  const interval = setInterval(() => setTime(new Date()), 1000);
  return () => clearInterval(interval);  // Cleanup
}, []);

Styling

The MenuBar uses glassmorphism for a modern macOS look:

Background Effect

bg-black/50          /* 50% black opacity */
backdrop-blur-xl     /* Strong blur on content behind */
border-b border-white/10  /* Subtle bottom border */
This creates a translucent bar that shows blurred content beneath it.

Typography

text-xs              /* Small text (12px) */
text-white           /* White text */
text-white/50        /* 50% opacity for secondary items */
text-white/60        /* 60% opacity for date */
font-semibold        /* Bold name */
font-medium          /* Medium weight time */

Spacing

h-7                  /* 28px height */
px-4                 /* 16px horizontal padding */
gap-4                /* 16px gap between items (left section) */
gap-3                /* 12px gap between items (right section) */

Z-Index

The MenuBar uses z-[9999] to ensure it stays above all content:
z-[9999]             /* Highest z-index */
This matches the Dock’s z-index, keeping both system UI elements on top.

Responsive Design

On small screens, some elements are hidden:
{/* Portfolio label - only visible on sm+ screens */}
<span className="... hidden sm:inline">
  Portfolio
</span>

{/* Date - only visible on sm+ screens */}
<span className="... hidden sm:inline">{dateStr}</span>
This prevents text overflow on mobile devices while keeping the time always visible.

Breakpoints

Tailwind’s sm: breakpoint is 640px:
  • < 640px: Shows logo, name, and time only
  • ≥ 640px: Shows logo, name, “Portfolio” label, date, and time

Performance Considerations

Interval Optimization

The component updates every second, which is efficient for a clock:
setInterval(() => setTime(new Date()), 1000);  // 1 second interval
For even better performance on low-end devices, consider:
// Update only when minute changes
setInterval(() => {
  const now = new Date();
  if (now.getSeconds() === 0) {
    setTime(now);
  }
}, 1000);
However, this adds complexity and the current approach is perfectly fine.

Render Optimization

The component is lightweight and doesn’t need memoization. Each re-render updates only the time strings.

Customization

Change Height

<div className="h-7">  {/* Default: 28px */}
<div className="h-8">  {/* Taller: 32px */}
<div className="h-6">  {/* Shorter: 24px */}
Remember to update the Desktop’s top offset:
<div className="absolute top-7 ...">  {/* Match MenuBar height */}

Change Colors

{/* Default: Black translucent */}
className="bg-black/50 backdrop-blur-xl"

{/* Dark gray translucent */}
className="bg-gray-900/50 backdrop-blur-xl"

{/* White translucent (light mode) */}
className="bg-white/70 backdrop-blur-xl text-black"

Add Menu Items

<div className="flex items-center gap-4 text-white text-xs">
  <span className="text-lg leading-none"></span>
  <span className="font-semibold ...">Javier Navas</span>
  <span className="...">Portfolio</span>
  
  {/* Add custom menu items */}
  <span className="hover:bg-white/10 px-2 py-0.5 rounded cursor-pointer">
    About
  </span>
  <span className="hover:bg-white/10 px-2 py-0.5 rounded cursor-pointer">
    Projects
  </span>
</div>

Change Locale

// Spanish (default)
time.toLocaleTimeString("es-ES", { ... });
time.toLocaleDateString("es-ES", { ... });

// English
time.toLocaleTimeString("en-US", { ... });
time.toLocaleDateString("en-US", { ... });

// Use user's locale
time.toLocaleTimeString(navigator.language, { ... });
time.toLocaleDateString(navigator.language, { ... });

Integration with Desktop

The MenuBar is rendered at the top level of the Desktop component:
// Desktop.tsx
export default function Desktop() {
  return (
    <div className="w-screen h-screen overflow-hidden relative">
      {/* MenuBar at top */}
      <MenuBar />

      {/* Desktop area - top: 28px to avoid overlap */}
      <div className="absolute top-7 left-0 right-0 bottom-20 overflow-hidden">
        {/* Folder icons, windows, etc. */}
      </div>

      {/* Dock at bottom */}
      <Dock items={dockItems} />
    </div>
  );
}
The Desktop reserves:
  • Top 28px (top-7): MenuBar space
  • Bottom 80px (bottom-20): Dock space
  • Middle area: Workspace for windows and icons

Accessibility

The MenuBar is marked as non-interactive:
select-none          /* Prevent text selection */
cursor-default       /* Default cursor, not pointer */

Recommendations

For improved accessibility, consider:
<div 
  className="..."
  role="banner"                    // ARIA role
  aria-label="System menu bar"    // Screen reader label
>
  • Desktop - Parent container that includes MenuBar
  • Dock - Bottom counterpart to the MenuBar
  • Window - Windows positioned below the MenuBar
  • Terminal - Applications displayed in the workspace

Build docs developers (and LLMs) love