EmpireUI
Get Pro
← Blog7 min read#neumorphism#alarm-clock-ui#notification-design

Neumorphism Alarm Clock UI: Time and Notification Design

Build a neumorphism alarm clock UI with soft shadows, embossed digits, and accessible notification states — real code, real values, no fluff.

Soft neumorphic alarm clock UI on a light grey background with embossed time digits and toggle buttons

Why Alarm Clocks Are the Perfect Neumorphism Demo

Honestly, neumorphism was practically invented for clock interfaces. The whole idea — soft extrusions, inner shadows, surfaces that look physically touchable — maps perfectly onto knobs, digits, and toggle buttons that a real clock would have.

When you look at how the style works, it's all about perceived depth on a single flat surface. An alarm clock needs to communicate state: is the alarm on or off? Is the time ticking or paused? Those states map cleanly onto the two core shadow modes in neumorphism: outset (raised) and inset (pressed).

If you've already read the what is neumorphism overview on this blog, you know the theory. This article is about actually building a working clock component — with real shadow values, real code, and honest notes about where the style hits its limits.

The Shadow Math Behind Neumorphic Clock Digits

The signature neumorphic look needs exactly two shadows — one light, one dark — offset in opposite directions from a base background color. For a light-mode clock on #e0e5ec, the standard values are box-shadow: 6px 6px 12px #b8bec7, -6px -6px 12px #ffffff. That 6px offset with a 12px blur gives a convincing soft extrusion.

For the digit display specifically, you'll want to wrap each digit in its own card so individual characters feel embossed. A common mistake is wrapping the entire time string in one element — it ends up looking like a flat pill rather than individual tactile buttons.

Inset digits (for pressed/active states) flip to box-shadow: inset 4px 4px 8px #b8bec7, inset -4px -4px 8px #ffffff. The inset keyword is everything here. Don't mix inset and outset shadows on the same element or the depth illusion collapses.

Building the Alarm Clock Card Component in React

Here's a minimal React component for the clock face. It uses Tailwind v4.0.2 for layout and spacing, with inline styles for the shadow values since Tailwind's shadow utilities can't express arbitrary rgba pairs without a custom config.

import { useState, useEffect } from 'react';

const neuShadow = '6px 6px 12px #b8bec7, -6px -6px 12px #ffffff';
const neuInset = 'inset 4px 4px 8px #b8bec7, inset -4px -4px 8px #ffffff';

export function AlarmClock() {
  const [time, setTime] = useState(new Date());
  const [alarmOn, setAlarmOn] = useState(false);

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

  const pad = (n: number) => String(n).padStart(2, '0');
  const hh = pad(time.getHours());
  const mm = pad(time.getMinutes());
  const ss = pad(time.getSeconds());

  return (
    <div
      className="flex flex-col items-center gap-6 p-10 rounded-3xl"
      style={{ background: '#e0e5ec', boxShadow: neuShadow }}
    >
      <div className="flex items-center gap-3">
        {[hh, ':', mm, ':', ss].map((seg, i) => (
          <span
            key={i}
            className="text-4xl font-mono font-bold text-slate-600 px-3 py-2 rounded-xl"
            style={{
              background: '#e0e5ec',
              boxShadow: seg === ':' ? 'none' : neuInset,
              letterSpacing: '0.05em',
            }}
          >
            {seg}
          </span>
        ))}
      </div>

      <button
        onClick={() => setAlarmOn(v => !v)}
        className="px-6 py-3 rounded-full text-sm font-semibold text-slate-500 transition-all duration-150"
        style={{
          background: '#e0e5ec',
          boxShadow: alarmOn ? neuInset : neuShadow,
          color: alarmOn ? '#6366f1' : '#94a3b8',
        }}
        aria-pressed={alarmOn}
      >
        {alarmOn ? 'Alarm ON' : 'Alarm OFF'}
      </button>
    </div>
  );
}

The aria-pressed attribute on the toggle button is non-negotiable. Neumorphism's visual cues are subtle — the shadow difference between active and inactive states is easy to miss for users with low vision. Screen readers need that semantic signal regardless of what the shadows look like.

Notification States and the Alarm Ring Animation

An alarm clock without a firing state is just a clock. The notification ring animation is where neumorphism gets interesting — you can pulse the outer card shadow to simulate a physical vibration without breaking the visual language.

The trick is animating between the standard outset shadow and a slightly stronger version using @keyframes. Using rgba(183,190,199,1) for the dark shadow and rgba(255,255,255,1) for the light, you can deepen those values during the pulse to make the card feel like it's physically buzzing on the desk.

@keyframes neu-pulse {
  0%, 100% {
    box-shadow: 6px 6px 12px #b8bec7, -6px -6px 12px #ffffff;
  }
  50% {
    box-shadow: 10px 10px 20px #a3aab4, -10px -10px 20px #ffffff;
  }
}

.alarm-ringing {
  animation: neu-pulse 0.6s ease-in-out infinite;
}

Apply that class conditionally in React when the alarm is firing. Keep the animation duration at 0.6s or slower — faster than that and the shadow recalculation becomes janky on lower-end Android WebViews. And always respect prefers-reduced-motion. Wrap the animation in a media query so users who've opted out don't get a strobing card.

Neumorphism vs Glassmorphism for Time-Based UIs

People ask this a lot. The honest answer: neumorphism works better for functional clock UIs, and glassmorphism works better for ambient/decorative ones. The glassmorphism vs neumorphism breakdown goes deeper on the theory, but for clocks specifically the reasoning is practical.

Glassmorphism needs a vivid background to look good — you need something colorful bleeding through that frosted layer. A clock in a dashboard with a plain white or grey background will make the glass effect look washed out. Neumorphism is self-contained. The depth comes from the element itself, not from what's behind it.

That said, dark-mode neumorphism is genuinely hard. The style was designed around #e0e5ec-range backgrounds. On dark surfaces you need to flip the shadow strategy entirely — your dark shadow becomes nearly invisible and you have to increase the light shadow's spread. If you're building a theme toggle that switches between light and dark, make sure you're maintaining two completely separate shadow sets, not just inverting colors.

Tailwind Configuration for Neumorphic Clock Utilities

Tailwind's default shadow scale doesn't get you neumorphism out of the box — you need to extend it with custom values. In Tailwind v4.0.2, the theme.extend.boxShadow config key is where you land.

// tailwind.config.js
module.exports = {
  theme: {
    extend: {
      boxShadow: {
        'neu-out': '6px 6px 12px #b8bec7, -6px -6px 12px #ffffff',
        'neu-in':  'inset 4px 4px 8px #b8bec7, inset -4px -4px 8px #ffffff',
        'neu-out-sm': '3px 3px 6px #b8bec7, -3px -3px 6px #ffffff',
        'neu-in-sm':  'inset 2px 2px 4px #b8bec7, inset -2px -2px 4px #ffffff',
      },
      colors: {
        'neu-base': '#e0e5ec',
      },
    },
  },
};

With this setup you can write shadow-neu-out and shadow-neu-in in JSX directly. The -sm variants are useful for inner digit cards where the 6px offset on a small element would overwhelm the content. The base color utility saves you from repeating that hex everywhere — define it once and reference bg-neu-base across the whole component tree.

Accessibility and Contrast Problems You'll Actually Hit

Let's be direct: neumorphism has a contrast problem. The style's signature light-on-light look makes it genuinely hard to meet WCAG 2.1 AA contrast ratios for text on the pressed (inset) state. A #94a3b8 label on #e0e5ec clocks in around 2.8:1 — well below the 4.5:1 requirement for normal text.

The fix isn't to abandon the style. It's to use the shadow depth for decoration only, and ensure the active state changes the text color to something with real contrast. #374151 on #e0e5ec gives you 8.2:1. Use that for your active alarm state. The visual 'pressed' feeling comes from the inset shadow, not from the text color, so you can change the label color freely without breaking the aesthetic.

What about color blind users? The alarm ON / alarm OFF distinction can't rely solely on color shifts. That's why the aria-pressed attribute in the code above matters, and why the button label text should change state too. Shadow direction changes alone are not an accessible state indicator.

Extending the Clock to a Full Notification Panel

An alarm clock component is a great starting point, but most real apps need this embedded in a wider notification context — a dashboard widget, a timer sidebar, a scheduled task UI. The same shadow system scales well. You can nest neumorphic cards by making the inner cards use neu-in-sm shadows while the outer wrapper uses the standard neu-out — this creates a visual hierarchy without needing borders or different background colors.

For a multi-alarm list, each alarm row should be its own outset card. Don't use a single inset container with flat rows inside. The tactile metaphor breaks down when the list items don't feel individually pressable. That's actually a broader rule with this style: what is neumorphism explains how the metaphor derives from physical objects, and lists of flat items inside a pressed container make no physical sense.

Also worth noting: if you're building this alongside other UI components that use particles background react or animated backdrops, be careful. Neumorphism needs a solid, consistent background color to function. Animated backgrounds will break the shadow illusion entirely — the shadows are tuned to exactly one background color, and any variation destroys the effect.

FAQ

What background color should I use for neumorphism in a clock component?

The standard starting point is #e0e5ec. Your two shadows derive from this — the dark shadow is typically #b8bec7 (about 8% darker) and the light shadow is #ffffff. If you change the base, you need to recalculate both shadow colors proportionally or the depth effect won't read correctly.

How do I handle dark mode with a neumorphic alarm clock?

You need a completely separate shadow set for dark mode — don't just invert. A common dark base is #2d2d2d with shadows like #1a1a1a (dark) and rgba(255,255,255,0.05) (light). The light shadow needs to be very subtle in dark mode or it looks like a glowing border rather than a soft extrusion.

Can I animate the alarm ring without hurting performance?

Yes, but keep the animation on the box-shadow property only — don't animate transform or opacity at the same time. Also set animation-will-change: box-shadow on the ringing element. Keep the keyframe duration at 0.6s or above, and wrap the entire @keyframes block in @media (prefers-reduced-motion: no-preference) to respect accessibility preferences.

Why does my neumorphic digit look flat instead of embossed?

Two common causes: the element's background color doesn't match the parent background exactly (even a 1-unit difference breaks the illusion), or you've applied a border that creates a visual edge and overrides the shadow depth. Make sure the digit card has the same background as its container and no border or outline applied.

Is neumorphism accessible enough for production alarm clock UIs?

It requires extra work. You need to supplement shadow-based state indicators with text changes, aria attributes (aria-pressed, aria-live for the alarm state), and sufficient color contrast on labels. The style alone fails WCAG AA on most shadow-state indicators, so always pair visual depth with semantic HTML.

How do I make the seconds tick feel smooth rather than jumping?

Use CSS transitions on the opacity or a slight scale transform when the digit changes, not on box-shadow — shadow transitions during number changes create flicker. A 100ms opacity transition (0.85 → 1.0) on digit update gives a soft refresh feel that fits the neumorphic tactile metaphor without performance cost.

Free components in 40 styles
React & Tailwind, copy-paste ready.
Browse →

Read next

Neumorphism Cards: 8 Soft-UI Variants with Accessibility FixesSoft UI Buttons: 12 Neumorphic Variants with Hover StatesComponent State Design: Default, Hover, Active, Disabled, ErrorTailwind Footer Design: 4-Column Link Layout with Newsletter