Project 2 – CSS only interactive widget
While powerful for generating dynamic spaces, large script files can become cumbersome for a browser and animations can be taxing on the CPU without using acceleration plugins like those created by GreenSock. However, then you are just bringing in more code for the browser to render. Additionally, many people doubt or are ignorant, myself included, of the full arsenal of CSS selectors. Therefore, in the spirit of the KISS principle, and in order to practice CSS selection. I am practicing utilizing the full capabilities of CSS selectors and CSS3 transforms to create a playful and interactive widget that is created without a single line of script code. Here is the demo, beware though because the demo will only work fully in -webkit- browsers (i.e. chrome or safari) For a full explanation simply continue reading.
In a project like this, the most important part is the structure of the HTML elements, because by definition CSS selectors can only move down the HTML tags. Specifically, the most important selector in this example is the tilde, or this ‘~’. See W3School’s reference for all CSS selectors. The tilde is unique because it selects all matching elements at the same level but following the first reference. Hence I can make a set of radio buttons(one selection at a time), which can reflect changes later in the DOM without being descendents. Alright, here is the html code:
<!DOCTYPE html> <html> <head> <link href="//netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.css" rel="stylesheet"> <link rel="stylesheet" type="text/css" href="styles.css"> </head> <body> <div class="clockWrapper"> <input type="radio" id="party" name="clockpoint" checked="checked"><label for="party"><i class="fa fa-glass"></i></label> <input type="radio" id="sleep" name="clockpoint"><label for="sleep"><i class="fa fa-moon-o"></i></label> <input type="radio" id="learning" name="clockpoint"><label for="learning"><i class="fa fa-superscript"></i></label> <input type="radio" id="work" name="clockpoint"><label for="work"><i class="fa fa-laptop"></i></label> <input type="radio" id="eating" name="clockpoint"><label for="eating"><i class="fa fa-cutlery"></i></label> <input type="radio" id="play" name="clockpoint"><label for="play"><i class="fa fa-gamepad"></i></label> <input type="radio" id="creative" name="clockpoint"><label for="creative"><i class="fa fa-rocket"></i></label> <input type="radio" id="social" name="clockpoint"><label for="social"><i class="fa fa-users"></i></label> <div class="clock"> <img class="clockFace" src="clockFace.svg"/> <div class="clockPointer fa-stack"><i class="fa fa-caret-up fa-stack-1x"></i><i class="fa fa-caret-up fa-stack-2x"></i></div> <svg class="minuteHand"> <line x1="75" y1="0" x2="75" y2="90" stroke="rgba(100, 100, 100, 1)" stroke-width="3"/> </svg> <svg class="hourHand"> <line x1="55" y1="30" x2="75" y2="75" stroke="rgba(100, 100, 100, 1)" stroke-width="3"/> </svg> <div class="title"> </div> </div> </div> </body> </html>
Notice the id on each input[type=”radio”] matches the for on the following label so that clicking the label will trigger the input, which will be hidden. I also used font-awesome for all the icons, hence the i tags with fa- classes: I recommend checking it out if you are not familiar with it. The clockFace.svg is just something I wiped up in Adobe Illustrator, the colors were chosen using Kuler.
In particular, the div.clock houses all of my components which need to be manipulated. This element is preceded by all of the radio buttons so that I can affect it using them through CSS selectors.
Here are all of the static styles, mostly just standard positioning and styling:
div.clockWrapper { width: 600px; height: 600px; margin: 100px auto; border: solid 4px white; border-radius: 300px; background-color: rgba(95, 199, 168, 1); position: relative; box-shadow: 2px 2px 15px black; } input[type="radio"] { display: none; } input[type="radio"]+label>i { color: rgba(255, 255, 255, .3); } input[type="radio"]:checked+label>i { color: rgba(255, 255, 255, 1); } label { font-size: 70px; position: absolute; margin-left: -23px; text-align: center; z-index: 3; } label[for="party"] { top: 20px; left: 290px; } label[for="sleep"] { top: 89px; left: 475px; } label[for="learning"] { top: 250px; left: 540px; } label[for="work"] { top: 427px; left: 475px; } label[for="eating"] { top: 509px; left: 295px; } label[for="play"] { top: 427px; left: 125px; } label[for="creative"] { left: 40px; top: 250px; } label[for="social"] { top: 89px; left: 125px; } div.clock { position: absolute; width: 100%; height: 100%; top: 0; left: 0; } div.clock img.clockFace { width: 350px; height: 350px; top: 125px; left: 125px; position: absolute; } div.clockPointer { height: 100%; width: 100%; z-index: 1; position: absolute; transition: 1s; -webkit-transition: 1s; } div.clockPointer i { font-size: 40px; width: 20px; left: 50%; position: absolute; top: 98px; margin-left: -10px; } div.clockPointer i.fa-stack-2x { color: rgba(206,133,127,1); } div.clockPointer i.fa-stack-1x { color: white; font-size: 69px; margin-left: -18px; top: 101px; }
The only tricky part for this resides in the transitions placed on the clock hands:
svg.hourHand, svg.minuteHand { position: absolute; width: 150px; height: 150px; left: 50%; top: 50%; margin-left: -75px; margin-top: -75px; z-index: 2; -webkit-transition:all 2s cubic-bezier(.52, .27, .15, .91); -moz-transition:all 2s cubic-bezier(.52, .27, .15, .91); -o-transition:all 2s cubic-bezier(.52, .27, .15, .91); transition:all 2s cubic-bezier(.52, .27, .15, .91); }
I used a custom cubic-bezier function created at cubic-bezier.com. It is that same way that svg curves are created, through the Bézier curve functions. This was actually my first time using Bézier curves and I was surprised at the simplicity of the tools available.
Next was the implementation of the tilde selector in order to create different scenes for each button selected. They are in the format of:
input#inputID:checked~div.clock div.objectToBeManipulated
Due to the structure of the html, every selector could be written like this.
Here are the full selectors for the pointer:
input#sleep:checked~div.clock div.clockPointer { transform: rotate(45deg); -webkit-transform: rotate(45deg); -moz-transform: rotate(45deg); -o-transform: rotate(45deg); } input#learning:checked~div.clock div.clockPointer { transform: rotate(90deg); -webkit-transform: rotate(90deg); -moz-transform: rotate(90deg); -o-transform: rotate(90deg); } input#work:checked~div.clock div.clockPointer { transform: rotate(135deg); -webkit-transform: rotate(135deg); -moz-transform: rotate(135deg); -o-transform: rotate(135deg); } input#eating:checked~div.clock div.clockPointer { transform: rotate(180deg); -webkit-transform: rotate(180deg); -moz-transform: rotate(180deg); -o-transform: rotate(180deg); } input#play:checked~div.clock div.clockPointer { transform: rotate(-135deg); -webkit-transform: rotate(-135deg); -moz-transform: rotate(-135deg); -o-transform: rotate(-135deg); } input#creative:checked~div.clock div.clockPointer { transform: rotate(-90deg); -webkit-transform: rotate(-90deg); -moz-transform: rotate(-90deg); -o-transform: rotate(-90deg); } input#social:checked~div.clock div.clockPointer { transform: rotate(-45deg); -webkit-transform: rotate(-45deg); -moz-transform: rotate(-45deg); -o-transform: rotate(-45deg); }
The hour hand:
/*start hour hand transformations*/ input#sleep:checked~div.clock svg.hourHand { transform: rotate(90deg); -webkit-transform: rotate(90deg); -moz-transform: rotate(90deg); -o-transform: rotate(90deg); } input#learning:checked~div.clock svg.hourHand { transform: rotate(180deg); -webkit-transform: rotate(180deg); -moz-transform: rotate(180deg); -o-transform: rotate(180deg); } input#work:checked~div.clock svg.hourHand { transform: rotate(270deg); -webkit-transform: rotate(270deg); -moz-transform: rotate(270deg); -o-transform: rotate(270deg); } input#eating:checked~div.clock svg.hourHand { transform: rotate(360deg); -webkit-transform: rotate(360deg); -moz-transform: rotate(360deg); -o-transform: rotate(360deg); } input#play:checked~div.clock svg.hourHand { transform: rotate(450deg); -webkit-transform: rotate(450deg); -moz-transform: rotate(450deg); -o-transform: rotate(450deg); } input#creative:checked~div.clock svg.hourHand { transform: rotate(540deg); -webkit-transform: rotate(540deg); -moz-transform: rotate(540deg); -o-transform: rotate(540deg); } input#social:checked~div.clock svg.hourHand { transform: rotate(630deg); -webkit-transform: rotate(630deg); -moz-transform: rotate(630deg); -o-transform: rotate(630deg); }
The minute hand:
/*start minute hand transformations*/ input#sleep:checked~div.clock svg.minuteHand { transform: rotate(180deg); -webkit-transform: rotate(180deg); -moz-transform: rotate(180deg); -o-transform: rotate(180deg); } input#learning:checked~div.clock svg.minuteHand { transform: rotate(470deg); -webkit-transform: rotate(470deg); -moz-transform: rotate(470deg); -o-transform: rotate(470deg); } input#work:checked~div.clock svg.minuteHand { transform: rotate(700deg); -webkit-transform: rotate(700deg); -moz-transform: rotate(700deg); -o-transform: rotate(700deg); } input#eating:checked~div.clock svg.minuteHand { transform: rotate(1000deg); -webkit-transform: rotate(1000deg); -moz-transform: rotate(1000deg); -o-transform: rotate(1000deg); } input#play:checked~div.clock svg.minuteHand { transform: rotate(1250deg); -webkit-transform: rotate(1250deg); -moz-transform: rotate(1250deg); -o-transform: rotate(1250deg); } input#creative:checked~div.clock svg.minuteHand { transform: rotate(1440deg); -webkit-transform: rotate(1440deg); -moz-transform: rotate(1440deg); -o-transform: rotate(1440deg); } input#social:checked~div.clock svg.minuteHand { transform: rotate(1520deg); -webkit-transform: rotate(1520deg); -moz-transform: rotate(1520deg); -o-transform: rotate(1520deg); }/*start minute hand transformations*/ input#sleep:checked~div.clock svg.minuteHand { transform: rotate(180deg); -webkit-transform: rotate(180deg); -moz-transform: rotate(180deg); -o-transform: rotate(180deg); } input#learning:checked~div.clock svg.minuteHand { transform: rotate(470deg); -webkit-transform: rotate(470deg); -moz-transform: rotate(470deg); -o-transform: rotate(470deg); } input#work:checked~div.clock svg.minuteHand { transform: rotate(700deg); -webkit-transform: rotate(700deg); -moz-transform: rotate(700deg); -o-transform: rotate(700deg); } input#eating:checked~div.clock svg.minuteHand { transform: rotate(1000deg); -webkit-transform: rotate(1000deg); -moz-transform: rotate(1000deg); -o-transform: rotate(1000deg); } input#play:checked~div.clock svg.minuteHand { transform: rotate(1250deg); -webkit-transform: rotate(1250deg); -moz-transform: rotate(1250deg); -o-transform: rotate(1250deg); } input#creative:checked~div.clock svg.minuteHand { transform: rotate(1440deg); -webkit-transform: rotate(1440deg); -moz-transform: rotate(1440deg); -o-transform: rotate(1440deg); } input#social:checked~div.clock svg.minuteHand { transform: rotate(1520deg); -webkit-transform: rotate(1520deg); -moz-transform: rotate(1520deg); -o-transform: rotate(1520deg); }
And finally the title:
/*div.title*/ div.title { text-align: center; font-size: 40px; position: absolute; top: 340px; left: 300px; width: 0; height: 50px; z-index: 3; color: rgba(206,133,127,1); font-family: sans-serif; } div.title:after { } input#party:checked~div.clock div.title:after { content: "Party"; margin-left: -45px; } input#sleep:checked~div.clock div.title:after { content: 'Sleep'; margin-left: -50px; } input#learning:checked~div.clock div.title:after { content: 'Learning'; margin-left: -76px; } input#work:checked~div.clock div.title:after { content: 'Work'; margin-left: -44px; } input#eating:checked~div.clock div.title:after { content: 'Eating'; margin-left: -56px; } input#play:checked~div.clock div.title:after { content: 'Play'; margin-left: -33px; } input#creative:checked~div.clock div.title:after { content: 'Creative'; margin-left: -73px; } input#social:checked~div.clock div.title:after { content: 'Social'; margin-left: -55px; }
Notice the title required the use of the ‘:after’ pseudo class in order to have css generated content and it could have also worked with a ‘:before’ class. These are in the same category as ‘:hover’ and ‘:checked’ used on the input selectors. You can read more about pseudo classes here
Well that pretty much covers it, hope you liked the explaination!