.content
  #okinawa.widget.clock(data-timezone="+9")
    .shadow
    .hand.seconds
    .hand.minutes
    .hand.hours
    .hand-cap
    label Okinawa
  #seattle.widget.clock(data-timezone="-8")
    .shadow
    .hand.seconds
    .hand.minutes
    .hand.hours
    .hand-cap
    label Seattle
  #amalfi.widget.clock(data-timezone="+1")
    .shadow
    .hand.seconds
    .hand.minutes
    .hand.hours
    .hand-cap
    label Amalfi
View Compiled
$color-primary: #673AB7
$color-secondary: #FFF
$color-tertiary: #5A2EA5

$size-base: 12px
$size-small: $size-base / 2
$size-large: $size-base * 2

$clock-size: $size-base * 32
$clock-size-small: $size-base * 20
$clock-weight: $size-small
$clock-weight-small: $size-small / 2
$clock-seconds-length: 95%
$clock-minutes-length: 85%
$clock-hours-length: 75%

html
  height: 100%
  display: flex
  justify-content: center
  align-items: center
  background-color: $color-primary
  overflow: hidden

.content
  display: flex
  align-items: center
  
  .widget
    padding: $size-large
    margin: $size-large
    
    &.clock
      position: relative
      width: $clock-size
      height: $clock-size
      border-radius: 100%
      box-sizing: border-box
      background-color: $color-tertiary
      
      // specific to smaller clocks
      &#okinawa, &#amalfi
        width: $clock-size-small
        height: $clock-size-small
        
        &:before
          border-width: $clock-weight-small

        .hand
          height: $clock-weight-small
          top: ($clock-size-small - $clock-weight-small) / 2
          transform-origin: right ($clock-weight-small / 2)

          &.seconds
            display: none
      
      // clock rim
      &:before
        content: ''
        width: 100%
        height: 100%
        position: absolute
        top: 0
        left: 0
        border: $clock-weight solid $color-secondary
        border-radius: 100%
        box-sizing: border-box
      
      // clock shadow
      .shadow
        content: ''
        width: 100%
        height: 500px
        position: absolute
        top: 50%
        left: 0
        background: linear-gradient(to bottom, rgba(0, 0, 0, 0.65) 0%, rgba(0, 0, 0, 0) 100%)
        transform: rotate(-45deg)
        transform-origin: 50% 0%
        opacity: 0.3
        z-index: -1
      
      .hand
        height: $clock-weight
        position: absolute
        top: ($clock-size - $clock-weight) / 2
        background-color: $color-secondary
        border-radius: 100% 0% 0% 100%
        transform-origin: right ($clock-weight / 2)
        transition: all 0.05s cubic-bezier(0, 0, 0.52, 2.51) 0s
        
        &.seconds
          width: 50 - (100 - $clock-seconds-length)
          left: 100 - $clock-seconds-length
          opacity: 0.25
        
        &.minutes
          width: 50 - (100 - $clock-minutes-length)
          left: 100 - $clock-minutes-length
          opacity: 0.5
        
        &.hours
          width: 50 - (100 - $clock-hours-length)
          left: 100 - $clock-hours-length
          opacity: 0.75
      
      .hand-cap
        width: 5%
        height: 5%
        position: absolute
        top: 47.5%
        left: 47.5%
        background-color: $color-secondary
        border-radius: 100%

    label
      display: block
      width: 100%
      position: absolute
      top: -$size-base * 2
      left: 0
      font-family: sans-serif
      font-weight: 200
      font-size: 12px
      text-transform: uppercase
      text-align: center
      letter-spacing: 4px
      color: $color-secondary
      opacity: 0.5
View Compiled
class Clock {
  constructor(id) {
    this.timezone = parseInt(document.getElementById(id).dataset.timezone);
    
    if (this.isDST(new Date())) {
      this.timezone += 1;
    }
 
    this.handSeconds = document.querySelector(`#${id} .hand.seconds`);
    this.handMinutes = document.querySelector(`#${id} .hand.minutes`);
    this.handHours = document.querySelector(`#${id} .hand.hours`);
    
    this.getTime();
    this.cycle();
  }
  
  isDST(now) {
    const jan = new Date(now.getFullYear(), 0, 1);
    const jul = new Date(now.getFullYear(), 6, 1);
    const dst = Math.max(jan.getTimezoneOffset(), jul.getTimezoneOffset());
    
    return now.getTimezoneOffset() < dst;
  }
  
  draw(hours, minutes, seconds) {
    const drawSeconds = ((seconds / 60) * 360) + 90;
    const drawMinutes = ((minutes / 60) * 360) + 90;
    const drawHours = ((hours / 12) * 360) + 90;
    
    this.handSeconds.style.transform = `rotate(${drawSeconds}deg)`;
    this.handMinutes.style.transform = `rotate(${drawMinutes}deg)`;
    this.handHours.style.transform = `rotate(${drawHours}deg)`;
    
    // fix for animation bump on when clock hands hit zero
    if (drawSeconds === 444 || drawSeconds === 90) {
      this.handSeconds.style.transition = "all 0s ease 0s";
    } else {
      this.handSeconds.style.transition = "all 0.05s cubic-bezier(0, 0, 0.52, 2.51) 0s";
    }
  }
  
  getTime() {
    const now = new Date();

    const hours = now.getUTCHours() + this.timezone;
    const minutes = now.getUTCMinutes();
    const seconds = now.getUTCSeconds();
    
    this.draw(hours, minutes, seconds);
  }
  
  cycle() {
    setInterval(this.getTime.bind(this), 1000);
  }
}

new Clock('okinawa');
new Clock('seattle');
new Clock('amalfi');

// this is just a rough draft for some effects
// const shadowOkinawa = document.querySelector("#okinawa .shadow");
// const shadowSeattle = document.querySelector("#seattle .shadow");
// const shadowAmalfi = document.querySelector("#amalfi .shadow");

// const handleMouseMove = (event) => {
//   const percent = parseInt((100 * event.pageX) / window.innerWidth);
//   const drawShadow = (0.22 * percent) - 50;
    
//   shadowOkinawa.style.transform = `rotate(${drawShadow}deg)`;
//   shadowSeattle.style.transform = `rotate(${drawShadow}deg)`;
//   shadowAmalfi.style.transform = `rotate(${drawShadow}deg)`;
// };

// document.onmousemove = handleMouseMove;
View Compiled

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.