Hallo, I would like to create a clock face with CSS. Right now I have a png for that. I would like to rebuild that with CSS. Does anybody have a hint how to start with that? (like with psuedo elements, position: absolute etc.) Here is the picture: Here is the CSS code that I have up to now. * { margin: 0; padding: 0; box-sizing: border-box; } body { display: flex; justify-content: center; align-items: center; min-height: 100vh; background: #091921; } .clock { width: 350px; height: 350px; display: flex; justify-content: center; align-items: center; background: url(clock.png); background-size: cover; border: 4px solid #091921; border-radius: 50%; /* box-shadow */ /* offset-x | offset-y | blur-radius | color */ box-shadow: 0 -15px 15px rgba(255, 255, 255, 0.05), inset 0 -15px 15px rgba(255, 255, 255, 0.05), 0 15px 15px rgba(0, 0, 0, 0.3), inset 0 15px 15px rgba(0, 0, 0, 0.3); /* CSS for the hands */ .clock:before { content: ""; position: absolute; width: 15px; height: 15px; background: #fff; border-radius: 50%; z-index: 10000; } .clock .hour, .clock .min, .clock .sec { position: absolute; } .clock .hour, .hr { width: 160px; height: 160px; /* border: 3px solid #ff105e; */ } .clock .min, .mn { width: 190px; height: 190px; /* border: 3px solid #eee2e6; */ } .clock .sec, .sc { width: 230px; height: 230px; /* border: 3px solid #d7f336; */ } .hr, .mn, .sc { display: flex; justify-content: center; position: absolute; /* align-items: center; */ /* border: 3px solid #ff105e; */ border-radius: 50%; } .hr:before { content: ""; position: absolute; width: 8px; height: 80px; background: #ff105e; z-index: 10; border-radius: 6px 6px 0 0; } .mn:before { content: ""; position: absolute; width: 4px; height: 90px; background: #fff; z-index: 11; border-radius: 6px 6px 0 0; } .sc:before { content: ""; position: absolute; width: 2px; height: 150px; background: #fff; z-index: 12; border-radius: 6px 6px 0 0; } } Code (CSS): HTML: <body> <div class="clock"> <div class="hour"> <div class="hr" id="hr"></div> </div> <div class="min"> <div class="mn" id="mn"></div> </div> <div class="sec"> <div class="sc" id="sc"></div> </div> </div> </body> HTML: Thanks a lot!
With javascript you can have a clock that shows the current time (on refresh): https://jsfiddle.net/1n8fxhvo/ You can remove some elements if you just want the face.
Thanks a lor for this! I already have a JS solution that I can use, but I can compare the solutions. Does anybody have an idea how to do this with CSS? (I would like to do as much as possible with CSS)
I stumbled over border: dashed in order to create these dashes in the circle but I think this is not very useful. Does anybody have experience with SVG? (stoke property in this case)?
Markup's a little heavy for my tastes, but this should get the job done. <div class="clockFace"> <div></div> <span></span> <span></span> <!-- .clockFace --></div> Code (markup): .clockFace { position:relative; width:10em; height:10em; } .clockFace:before, .clockFace:after, .clockFace div:before, .clockFace div:after { position:absolute; width:1.5em; line-height:1.5em; text-align:center; } .clockFace:before, .clockFace:after { left:50%; margin-left:-0.75em; } .clockFace:before { content:"12"; top:0; } .clockFace:after { content:"6"; bottom:0; } .clockFace div:before, .clockFace div:after { top:50%; margin-top:-0.75em; } .clockFace div:before { content:"9"; left:0; } .clockFace div:after { content:"3"; right:0; } .clockFace span:after, .clockFace span:before { content:""; box-sizing:border-box; position:absolute; left:50%; width:0.125em; height:100%; margin-left:-0.06125em; border:solid #000; border-width:1em 0; transform:rotate(30deg); } .clockFace span:after { transform:rotate(60deg); } .clockFace span + span:before { transform:rotate(-30deg); } .clockFace span + span:after { transform:rotate(-60deg); } Code (markup): That about what you want? Note, done in dynamic sizes since px is a giant middle finger to usability and accessibility.
Live demo here with working clock: https://cutcodedown.com/for_others/iago111/clock/ You might find the clock code a bit simpler than most. Note that the hours are grabbed as 120ths of a day so they "tick" the 6 degrees the other elements do.
Update, I fixed a bug in the math, and made the 12th ticks smaller to match the original image more closely... so be sure to ctrl-f5 to get the latest version. Oh, and if you want to control the size, just change the font-size on .clockFace, it's fully scalable off that one value. Side note, I made it behave like a proper old-fashioned clock, where EVERY hand "ticks" to 60ths, not just the second hand. It's funny how "proper" analog clocks are actually digital in function.
#deathshadow, thanks a lot for that solution, it works quite well! My ideas was to build up some kind of reusable blueprints/templates/components. E.g. I have a clock face with numbers and strokes, so I can build a simple clock or a watch, then I can built a chronograph, a compass (adding 360 strokes e.g.), finally I can build a artificial horizon tool for a plane e.g. Therefore these components have to be simple and flexible at the same time. I recede from JS since I really do not want to use it for simple graphic elements. On the other hand I do not want to use pictures since I cannot modify them.
Good idea. Made a medium article out of it. https://deathshadow.medium.com/html-css-analog-clock-with-minimal-javascript-9dcf9a16e079 A more fleshed out version, with more text explaining the how/what/why of it. I switched up how the ticks and hands are drawn slightly, and by mistake found that border-radius does some 'funky' but attractive things to the hands.
"Estupendo!", how the Spanish speaking community would call it. The fine art of frontend development.
It's actually more code than the canvas version you linked to, it's just the code is in different spots, the CSS being the lion's share of it. With a little optimization that canvas version could be dropped down to just 1.82k of scripting, almost 1k less than the CSS approach if you add the CSS and JS together. (function(d) { var canvas = d.getElementById("canvas"), ctx = canvas.getContext("2d"), radius = canvas.height / 2; ctx.translate(radius, radius); radius *= 0.90; drawClock(); function drawClock() { drawFace(); drawNumbers(); drawTime(); } function drawFace() { var grad = ctx.createRadialGradient(0,0,radius*0.95, 0,0,radius*1.05); grad.addColorStop(0, '#333'); grad.addColorStop(0.5, 'white'); grad.addColorStop(1, '#333'); ctx.beginPath(); ctx.arc(0, 0, radius, 0, 2 * Math.PI); ctx.fillStyle = 'white'; ctx.fill(); ctx.strokeStyle = grad; ctx.lineWidth = radius * 0.1; ctx.stroke(); ctx.beginPath(); ctx.arc(0, 0, radius * 0.1, 0, 2 * Math.PI); ctx.fillStyle = '#333'; ctx.fill(); } function drawNumbers() { ctx.font = radius * 0.15 + "px arial"; ctx.textBaseline = "middle"; ctx.textAlign ="center"; var pos = radius * 0.85, inc = Math.PI / 6, ang = inc * 7; for (var num = 1; num < 13; num++) { ctx.save(); ctx.rotate(ang); ctx.translate(0, pos); ctx.rotate(-ang); ctx.fillText(num.toString(), 0, 0); ctx.restore(); ang += inc; } } // drawNumbers function drawTime(){ var sxd2Rad = Math.PI / 30, now = new Date(); drawHand( (now.getHours() * 5 + Math.floor(now.getMinutes() / 12)) * sxd2Rad, 0.5, 0.07 ); drawHand( now.getMinutes() * sxd2Rad, 0.8, 0.07 ); drawHand( now.getSeconds() * sxd2Rad, 0.9, 0.02 ); } // drawTime function drawHand(pos, length, width) { ctx.save(); ctx.beginPath(); ctx.lineWidth = width * radius; ctx.lineCap = "round"; ctx.moveTo(0,0); ctx.rotate(pos); ctx.lineTo(0, -length * radius); ctx.stroke(); ctx.restore(); } // drawHand setInterval(drawClock, 1000); })(document); Code (markup): And that's with leveling the playing field by having it update on the second. Forked pen here: https://jsfiddle.net/Jason_Knight/1h0us4o7/26/ The biggest optimizations there being moving calculations out of a loop, removal of unnecessary arguments creating stack overhead, and understanding radians killing off a few "variables for nothing". More than one way to fry a fish. The big issue is that if you want it dynamic size you need to hook onresize to recalc the size, or if you want EM that's a bit tricky to implement... and really we should ALL be working in EM for most everything since it's the only "true" accessible screen media measurement.
O.k. guys, I implemented #deathshadow 's CSS tutorial: https://levelup.gitconnected.com/html-css-analog-clock-with-minimal-javascript-9dcf9a16e079 My original idea behind all that was to create a stopwatch. Things getting more complicated now, when you have clockfaces withing the clockface. I started with the CSS, without JS- functionality: Here is a picture: https://drive.google.com/file/d/1_51gfHjMLH8KjUsv_AxBkgl3tY5SpGO-/view?usp=sharing The 3 small clockfaces represent the minutes (at 12), seconds (at 9) and 10-nth seconds (at 6). I used #deathshadow 's technique of drawing the tick-marks by drawing lines within the circle where the border of these lines are the tick-marks. Rotating them draws all the tick marks. That works perfectly fine for the main clock, however I get problems positioning these lines in the small clocks. I made it by try-and-error, what is always bad! There are also 2 further problems. For the seconds I need 15 containers. (15 * 2 before/:after) * 2 (rotation)) That should be refactored somehow. And concerning the 10nth seconds, I do not have tick marks on opposite sites: The numerical series is: [36,72,108,144,180,216,252,288,324,360]. HTML: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Deathshadow watch</title> <link rel="stylesheet" type="text/css" href="style.css" /> <link rel="stylesheet" type="text/css" href="chrono.css" /> </head> <body> <div id="clock-wrapper"> <div id="clock"> <span></span> <span></span> <span></span> <!-- <i></i> <i></i> <i></i> --> </div> <div id="chrono-min"> <span></span> <span></span> <span></span> </div> <div id="chrono-tenth-sec"></div> <div id="chrono-sec"> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> </div> </div> HTML: style.css: / * CLOCK WRAPPER */ #clock-wrapper { font-size: 4em; position: relative; width: 10em; height: 10em; line-height: 1.5em; margin: 1em auto; padding: 0; } /* END OF CLOCK WRAPPER */ #clock { position: relative; width: 10em; height: 10em; border: 0.0625em solid #444; border-radius: 50%; box-shadow: inset 0.125em 0.25em 0.5em #0005, -0.25em -0.5em 0.5em #fffc, 0 0 0.0625em 0.5em #777, 0 0 0.0625em 0.5625em #000, 0.125em 0.25em 0.5em 0.5em #0004; } #clock *, #clock *:before, #clock *:after { box-sizing: border-box; #clock span { position: absolute; top: 0; left: 4.9375em; /* 10/2 - width/2 */ width: 0.125em; height: 100%; } #clock i, #clock span:before, #clock span:after { position: absolute; top: 0; /* (10-height) / 2 */ left: 0; width: 100%; height: 10em; } #clock span:nth-child(2) { transform: rotate(60deg); } #clock span:nth-child(3) { transform: rotate(120deg); } #clock span:before, #clock span:after { content: ""; border-style: solid; border-color: #000; border-width: 0.5em 0; /* background-color: #0f0; */ } #clock span:before { transform: rotate(0deg); } #clock span:after { transform: rotate(30deg); } Code (CSS): chrono.css /* stopwatch :before + :after */ #chrono-min, #chrono-tenth-sec, #chrono-sec { box-sizing: border-box; margin: 0; padding: 0; width: 3em; height: 3em; } #chrono-min { position: absolute; left: 3.5em; /* 10/2 - width/2 */ top: 1em; } #chrono-sec { position: absolute; top: 3.5em; left: 1em; } #chrono-tenth-sec { position: absolute; left: 3.5em; /* 10/2 - width/2 */ bottom: 1em; } #chrono-min:before { content: ""; display: inline-block; width: 3em; height: 3em; -moz-border-radius: 50%; -webkit-border-radius: 50%; border-radius: 50%; background-color: #fff; border: 0.05em solid gray; } #chrono-tenth-sec:before { content: ""; display: inline-block; width: 3em; height: 3em; -moz-border-radius: 50%; -webkit-border-radius: 50%; border-radius: 50%; background-color: #fff; border: 0.05em solid gray; } #chrono-sec:before { content: ""; display: inline-block; width: 3em; height: 3em; -moz-border-radius: 50%; -webkit-border-radius: 50%; border-radius: 50%; background-color: #fff; border: 0.05em solid gray; } /* stopwatch MIN tick-marks */ #chrono-min span { position: absolute; top: 0.05em; left: 1.55em; /* 3/2 - width/2 */ width: 0.05em; height: 100%; } #chrono-sec span { position: absolute; top: 0.05em; left: 1.55em; /* 3/2 - width/2 */ width: 0.02em; height: 100%; } #chrono-min i, #chrono-min span:before, #chrono-min span:after, #chrono-sec i, #chrono-sec span:before, #chrono-sec span:after { position: absolute; top: 0; /* (3-height) / 2 */ left: 0; width: 100%; height: 2.63em; } #chrono-min span:before, #chrono-min span:after, #chrono-sec span:before, #chrono-sec span:after { content: ""; border-style: solid; border-color: #000; border-width: 0.2em 0; /* background-color: #0f0; */ } /* ROTATE MIN */ #chrono-min span:before { transform: rotate(0deg); } #chrono-min span:after { transform: rotate(30deg); } #chrono-min span:nth-child(2) { transform: rotate(60deg); } #chrono-min span:nth-child(3) { transform: rotate(120deg); } /* ROTATE SEC */ #chrono-sec span:before { transform: rotate(0deg); } #chrono-sec span:before { transform: rotate(6deg); } #chrono-sec span:nth-child(2) { transform: rotate(12deg); } #chrono-sec span:nth-child(3) { transform: rotate(24deg); } #chrono-sec span:nth-child(4) { transform: rotate(36deg); } #chrono-sec span:nth-child(5) { transform: rotate(48deg); } #chrono-sec span:nth-child(6) { transform: rotate(60deg); } #chrono-sec span:nth-child(7) { transform: rotate(72deg); } #chrono-sec span:nth-child(8) { transform: rotate(84deg); } #chrono-sec span:nth-child(9) { transform: rotate(96deg); } #chrono-sec span:nth-child(10) { transform: rotate(108deg); } #chrono-sec span:nth-child(11) { transform: rotate(120deg); } #chrono-sec span:nth-child(12) { transform: rotate(132deg); } #chrono-sec span:nth-child(13) { transform: rotate(144deg); } #chrono-sec span:nth-child(14) { transform: rotate(156deg); } #chrono-sec span:nth-child(15) { transform: rotate(168deg); } Code (CSS):