#app
View Compiled
*
  box-sizing border-box

body
  min-height 100vh
  background linear-gradient(45deg, hsl(0, 0%, 15%), hsl(0, 0%, 70%))

#app
  display flex
  flex-direction column
  align-items center
  justify-content center
  min-height 100vh
  font-size 2rem
  font-family sans-serif

  & > div
    background repeating-linear-gradient(30deg, var(--strapColor) 0, var(--strapColor) 5px, transparent 5px, transparent 10px) 5px 0 / 90% 100% no-repeat,
      repeating-linear-gradient(-30deg, transparent 0, transparent 5px, var(--strapColor) 5px, var(--strapColor) 10px) 5px 0 / 90% 100% no-repeat, var(--strapBg)
    &:nth-of-type(1)
      --strapColor rgba(245, 229, 27, .25)
      --strapBg rgba(254, 241, 96, 1)
View Compiled
const {
  React,
  React: { useEffect, useState },
  ReactDOM: { render },
  moment,
  styled,
} = window
const ROOT = document.querySelector('#app')

const clip =
  'polygon(0 5%, 5% 0, 95% 0, 100% 5%, 100% 95%, 95% 100%, 5% 100%, 0 95%)'

const Hand = styled.div`
  width: ${p => (p.type === 'seconds' ? 2 : 5)}px;
  height: ${p => (p.type ? 40 : 20)}px;
  background: white;
  position: absolute;
  top: 50%;
  left: 50%;
  transform-origin: bottom;
  transform: translate(-50%, -100%) rotate(${p => p.value}deg);
`

const Strap = styled.div`
  clip-path: ${clip};
  height: 300px;
  overflow: hidden;
  position: relative;
  width: 100px;
`

const Bezel = styled.div`
  background: silver;
  height: 60%;
  left: 50%;
  position: absolute;
  top: 50%;
  transform: translate(-50%, -50%);
  clip-path: ${clip};
  width: 102%;
`
const Screen = styled.div`
  height: 100%;
  position: absolute;
  top: 50%;
  left: 50%;
  overflow: hidden;
  transform: translate(-50%, -50%);
  width: 90%;
  background: #000;
  color: #fff;
`

const Face = styled.div`
  display: flex;
  flex-direction: column;
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
`

const Value = styled.div`
  font-size: ${p => (p.small ? 2 : 3)}rem;
  line-height: ${p => (p.small ? 2 : 3)}rem;
  text-transform: uppercase;
  text-align: center;
  opacity: ${p => (p.opaque ? 0.5 : 1)};
`

const DefaultFace = date => (
  <Face>
    <Value>{date.format('HH')}</Value>
    <Value>{date.format('mm')}</Value>
  </Face>
)

const Watch = ({face}) => {
  const [date, setDate] = useState(moment())
  useEffect(() => {
    const TICK = setInterval(() => setDate(moment()), 1000)
    return () => {
      clearInterval(TICK)
    }
  }, [])
  return (
    <Strap>
      <Bezel>
        <Screen>
          {face(date)}
        </Screen>
      </Bezel>
    </Strap>
  )
}

Watch.defaultProps = {
  face: DefaultFace
}

const AnalogFace = date => {
  const seconds = (360 / 60) * date.seconds()
  const minutes = (360 / 60) * date.minutes()
  const hours = (360 / 12) * date.format('h')
  return (
    <Face>
      <Hand type='seconds' value={seconds}/>
      <Hand type='minutes' value={minutes}/>
      <Hand value={hours}/>      
    </Face>
  )
}

render(<Watch face={AnalogFace} />, ROOT)
View Compiled

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://cdnjs.cloudflare.com/ajax/libs/react/16.13.1/umd/react.production.min.js
  2. https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js
  3. https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.26.0/moment.min.js
  4. https://unpkg.com/react-is@16.13.1/umd/react-is.production.min.js
  5. https://cdnjs.cloudflare.com/ajax/libs/styled-components/5.1.0/styled-components.min.js