Showing posts with label css3. Show all posts
Showing posts with label css3. Show all posts

Sunday, October 2, 2016

Creating A Fancy CSS Button

I was in a meeting at work the other day where we were discussing our interview process for front-end developers.  We were taking a step back and trying to decide what exactly we were going to look for in our interview process and if we were finding those skills in the developers we were interviewing.  During the process the thought of interviewing for CSS was brought up and we started looking into some possible interview questions and stumbled across this website.
The first one is a simple question, but was really interesting.  People in the room seemed to think it was way too hard and I wasn't 100% sure how I would do it without internet access.  Here is the question:  Using CSS properties alone, recreate this button:

So I decided to take this as a personal challenge and ended up coming pretty close to it by just eyeballing the colors, spacings, etc.  It also took me way less time than I thought it would have and I learned a few things in the process.  Here is the end result:

Breaking it down, I started with the easies things first to get me going.  So I tackled the actual text of the button as my first thing.  To get the text to appear correctly, I knew the font-family needed to be a Sans-Serif, the font weight to be a bit on the bold size, to update the text color to a dark grey instead of black, and finally to just make sure whatever text in the button is uppercase.  Here is the css to do those things:

.button {

  text-transform:uppercase;

  color:#444444;

  font-family:sans-serif;

  font-weight:bold;

  font-size: 14px;

}



The next thing I decided to tackle was the stars.  The solution to this is to use a font-family, svg, or something else with before and after selectors.  If you haven't used them before check out the article about using after and before selectors.  I decided to use font-awesome in my solution because it had a star icon and I could just reference the CDN in my jsfiddle.   If you haven't used font icons before I wrote about it previously here.  I messed with the colors a bit and ended up with some pretty simple before and after selectors.

.button::before{

   font-family: FontAwesome;

   color:#999999;

   content: "\f005";

}




.button::after{

   font-family: FontAwesome;

   color:#999999;

   content: "\f005";

}


So now that the content is correct, the next thing was to take a stab at the background and the the first border.  So I added some padding to get the spacing of the button correct, and then went to work on the background.  I knew you could have gradients in css, but I hadn't used them in a while so I didn't quite remember the syntax.  I found some examples online like this and then created a gradient to the bottom with my 2 shades of gray.  Finally I added a simple boarder and some border radius and we are starting to look pretty good now.

.button{

  margin-left:10px;

  text-transform:uppercase;

  color:#444444;

  font-family:sans-serif;

  font-weight:bold;

  font-size: 14px;

  border-radius: 5px;

  border:.5px solid #aaaaaa;

  background: linear-gradient(180deg, #eeeeee, #aaaaaa);

  padding: 10px;

}


The last thing that was needed also the thing I was least certain about.  I was sure it could be done, cause otherwise why would the ask the question?  So I looked into box-shadow to see how that could be used and found something amazing.  It can be used N number of times!  So I finally had my solution.  I just needed a few different levels of box-shadow.  I needed one really dark layer around the border, followed by the larger light gray area.  Next I added a white gap followed by the final border.  Here is the final css for the .button class.

 
.button{

  margin-left:10px;

  text-transform:uppercase;

  color:#444444;

  font-family:sans-serif;

  font-weight:bold;

  font-size: 14px;

  border-radius: 5px;

  border:.5px solid #aaaaaa;

  background: linear-gradient(180deg, #eeeeee, #aaaaaa);

  box-shadow:  0 0 1px 1px #666666,

            0 0 0 6px #dddddd,

            0 0 0 8px #ffffff,

            0 0 0 10px #888888;

  padding: 10px;

}


Cheers!




Monday, December 15, 2014

Custom Angular Flyout

While working recently on a project I came across an issue where I had a finite amount of space and a bunch of actions that would be visible or hidden based on its current state.  I put some time into thinking how I could fit everything I needed and I thought that +90% of the time the user won't be performing any actions beyond just viewing the data so I started thinking about how to minify the amount of space my buttons took up.  I first thought about using icons, but unless they are so universal, it is typically better to just go with text.  So I thought about a button that has a popup menu.  I'm not sure if flyout is the technical term for it, but that sounded right to me so that is what I went with.  One of the benefits of this approach was if I needed to add more buttons down the road I know it won't cause any issues.  Likewise I will always know exactly how much real estate it will take up.
You could have infinite benders take up a finite space!

I knew that I wanted my angular directive to have a couple of things.  First I wanted the flyout to be driven by the developer so it can be reused over and over.  This can be easily solved with an ng-transclude directive.  Secondly, I wanted to make sure that it was smart enough to figure out where it was positioned in the window and dynamically display based on its position in the window.

The HTML for the flyout should be pretty straight forward.  An outer container, .flyoutContainer,  for the control, a container for the flyout, .flyout, and a div for the text/button to expand the flyout, .expanderContainer.  The .flyoutContainer has a couple events on it, blur and click, to figure out when to display the .flyout, and a ng-class directive to help with any styles you want to apply as well.  Here is the HTML result:
 
<div class='flyoutContainer' tabindex='0'  ng-click='click();' 
   ng-blur='lostFocus();' ng-class='{"expanded": options.isExpanded}'>
     <div class='expanderContainer' >
       {{options.display || 'Expand'}}
     </div>
     <div class='flyout'  ng-transclude ></div>
</div>

Next is the bare bones CSS to handle what we need.  We want the .flyout to be absolutely positioned, to start off hidden, and to go on top of other elements.  When the .expanded class is applied we will then want to change the visibility so the .flyout and its contents will be displayed.  The .flyoutContainer styles are pretty simple.  Change the positioning to relative so the absolutely positioned .flyout will be relative to it, and remove any outlining (unless you are into that kind of thing).  For the .expanderContainer I just added css to change the pointer to a cursor to make it explicit that it is clickable.  Here is the css:
    
.flyoutContainer{
    outline:none;
    position: relative;
}

.expanderContainer{
    cursor:pointer;
}

.flyout{
    position:absolute;
    top:auto;
    left:auto;
    z-index:1;
    visibility:hidden;
}

.expanded .flyout{
    visibility:visible;
}

Finally we come to the JS code to put it all together.  The easy part of the code is to just set the options.isExpanded appropriately based on the current state and event.  This will drive the CSS above.  The more difficult part of the code is making sure that the positioning of the .flyout is set appropriately based on where it is located in the window.  For this we pass in the $window parameter to the directive to get the window height and width.  We use the $window param instead of the window global element to make life easier for unit testing.  We can then compare those values with the getBoudingClientRect values of the .flyout to determine how much you need to update the top and left properties of it.  Here is the resulting JS:

function link(scope, element){
    scope.lostFocus = function lostFocus(){
        scope.options.isExpanded = false;
        clearFlyoutPosition();
    };

    scope.click = function click(){
        updateFlyoutPosition();
        scope.options.isExpanded = !scope.options.isExpanded;

        if (!scope.options.isExpanded){
            clearFlyoutPosition();
        }
    };
       
    function clearFlyoutPosition(){
        var $flyoutElement = angular.element
            element[0].querySelector('.flyout'));
        $flyoutElement.css('top', 'auto');
        $flyoutElement.css('left', 'auto');
    }

    function updateFlyoutPosition(){
        var flyoutElement = element[0].querySelector('.flyout'),
            $flyoutElement = angular.element(flyoutElement);

        if ($window.innerHeight < 
            flyoutElement.getBoundingClientRect().bottom){
            $flyoutElement.css('top', '-' +
                (flyoutElement.getBoundingClientRect().height ) + "px");
        }
        else{
            // set the top to the flyout container so it starts right at the bottom.
            $flyoutElement.css('top','auto');
        }

        if ($window.innerWidth < 
            flyoutElement.getBoundingClientRect().right){
            $flyoutElement.css('left', "-" +
                (flyoutElement.getBoundingClientRect().right -
              $window.innerWidth + 10) + "px");

        }
        else{
            $flyoutElement.css('left', 'auto');
        }

    }

}


Here is the end result with one flyout in the top left and one in the bottom right!




Wednesday, September 24, 2014

Restyling the checkbox.

When you start to work on webpages that need to function well in other devices, you start to learn that certain native HTML elements aren't really made for touch.  When using a mouse, you can be pretty precise with your mouse movements and your clicking.  However, when you attempt to touch something, smaller elements will cause the user to miss often.  There are some good articles on the interwebs on the recommended size of elements that will require touch, including some guidelines from Microsoft.  One of the elements that I have been getting frustrated with recently is the check box.  My fingers had a rough time clicking them on a tablet device.

This episode is almost 20 years old.

So I decided to come up with something that would be easier for a user with a touch interface to use, but still act as a check box.  Like most of my element redesign, I wanted to avoid using any JavaScript and rely on solely on CSS.  Taking a look around the web and other places I decided to settle on an on/off switch looking element.

The first thing we want to define is the button HTML.  First we define a container element for the class.  We are going to use this element as the container and anchor for everything inside it.  Next, we add a checkbox (because we still want to use the functionality of the checkbox for binding, form submitting, etc.  I then added a "background" element that would span the entire width and change based on the input being checked or unchecked (I'll get to this more later).  Lastly, we add a few elements to give it the "switch" feel.  That is a label for "On", "Off", and label that is going to act as the "slider" for the switch.  Our HTML is going to look like this:

<span class='toggleContainer'>
        
    <input class='toggleInput' type='checkbox' id='chk'></input>
    <span class='background'></span>
    <label class='slider' for='chk'></label>
    <label class='on' for='chk'>On</label>
    <label class='off' for='chk'>Off</label>

</span>

So you might think it is odd that I am using labels for these elements, especially for the "slider".  The reason that I do this, has to do with the "for" property of the label.  By clicking on the label, it will click the element for you.*

So now that we have our structure designed we need to style everything to turn it into a toggle button.  First we will style the container.  We are going to use this to determine the size of everything.  So we need to set the position it to be relative to give us an anchor for all the child elements, set overflow to hidden so nothing flows out of  the container, and finally just set a nice border.

.toggleContainer{
    border: 1px solid white;
    border-radius:6px;
    display:inline-block;
    width:50px;
    height:20px;
    position: relative; 
    overflow:hidden;
}


Next, lets style the check box.  We really just want to make it invisible, and not effect the rest of the HTML.
.toggleInput{
    position: absolute;
    opacity: 0;
}

Now we want to style the on, off, slider, and background.  I include these all together because they all need two styles each.  One for checked and one for unchecked.  So let's start with the first state which is unchecked.  For the background we are going to set it as the entire width and height of the container (including the border radius), and set the background-color for when the we are unchecked which I will set to red.  I am going to also set the transition of the background color which will give a smoother transition feel.

.toggleContainer .background {
    position: absolute;
    width: 100%;
    height: 100%;
    border-radius: 6px;
    background-color: red;
    opacity: 0.2;
    -moz-transition: background-color .3s;
    -webkit-transition: background-color .3s;
    transition: background-color .3s;
}

The slider will be defaulted to the left side of the toggle, with the on element hidden off the left, and the off displayed to the right of the slider.  Instead of just hiding the elements, I want to make it look smoother by moving objects to the left and right.  So I added a transition to the elements below.

.toggleContainer .toggleInput ~ .slider {
    position: absolute;
    width: 45%;
    height: 100%;
    content: "";
    background-color: white;
    border-radius: 6px;
    display: inline-block;
    z-index: 1;
    left: 0;
    -moz-transition: left 0.3s;
    -webkit-transition: left 0.3s;
    transition: left 0.3s;
}

.toggleContainer .toggleInput ~ .off, .toggleContainer .toggleInput ~ .on {
    width: 50%;
    position: relative;
    z-index: 10;
    display: inline-block;
    overflow: hidden;
    height: 100%;
    top: 50%;
    margin-top: -8px;
}

.toggleContainer .toggleInput ~ .on {
    left: -50%;
    float: left;
    -moz-transition: left 0.3s;
    -webkit-transition: left 0.3s;
    transition: left 0.3s;
    color: green;
}

.toggleContainer .toggleInput ~ .off {
    right: 0;
    -moz-transition: right 0.3s;
    -webkit-transition: right 0.3s;
    transition: right 0.3s;
    float: right;
    color: red;
}

Now that we have a default style, we need to add styles for when the element is checked.  To do this we can use the :checked css selector and apply the styles that we want to change.  Mostly we want to move everything to the right which gives the appearance of a switch being moved.  The only exception is the background which we will change from red to green.

.toggleContainer .toggleInput:checked ~ .background {
    background-color: green;
    -moz-transition: background-color 0.3s;
    -webkit-transition: background-color 0.3s;
    transition: background-color 0.3s;
}

.toggleContainer .toggleInput:checked ~ .slider {
    left: 55%;
    -moz-transition: left 0.3s;
    -webkit-transition: left 0.3s;
    transition: left 0.3s;
}

.toggleContainer .toggleInput:checked ~ .on {
    left: 5px;
    -moz-transition: left 0.3s;
    -webkit-transition: left 0.3s;
    transition: left 0.3s;
}
.toggleContainer .toggleInput:checked ~ .off {
    right: -50%;
    -moz-transition: right 0.3s;
    -webkit-transition: right 0.3s;
    transition: right 0.3s;
}



Here is the end result:
I think the best way to use this would be to create a widget, directive, or whatever your JS framework supports. With a little bit of extra JS you can add some additional classes such as a class to the entire module and style the entire element differently.

*If you are putting this on the web, you should really think about accessibility.  Having those extra labels for one input might confuse some screen readers, and by extension your users.  A couple of extra click handlers for the labels won't kill anyone. 

Tuesday, September 16, 2014

Using Iconic Fonts



One of the new things I've started incorporating in my development is the use of iconic fonts.

No No No!

I'm talking about fonts that have icons in their character set like font awesome, batch icons, ico moon, and many many more.

So why would you use a font with icons in them instead of just images?  Well there are a lot of really good reasons, that other blogs talk about, really well so I will summarize my thoughts and what I have learned.


  1. Really easy to use.
  2. You can style them like you would a normal font (color size etc.)
  3. You load them 1x.
  4. Pretty much all browsers support them.

But wait, what about accessibility and users that might need to use screen readers?  Won't using these trip them up?
Dr. Says No.
Well, not if they do it right.  If an icon font is working correctly it will use the Private Unicode character range.  So it won't trip up the reader.

So now that we have established the reasons of why to use them, we need to see how to use them.

First thing you need to do is load the font via CSS as follows:
@font-face{
    font-family:Batch;
    src:url('https://googledrive.com/host/0B4QQSpfhQZN5bFZ1ckV4QTRPb2s/batch-icons-webfont.eot');
    src:url('https://googledrive.com/host/0B4QQSpfhQZN5bFZ1ckV4QTRPb2s/batch-icons-webfont.eot?#iefix') format('embedded-opentype'),
        url('https://googledrive.com/host/0B4QQSpfhQZN5bFZ1ckV4QTRPb2s/batch-icons-webfont.woff') format('woff'),
        url('https://googledrive.com/host/0B4QQSpfhQZN5bFZ1ckV4QTRPb2s/batch-icons-webfont.ttf') format('truetype'),
        url('https://googledrive.com/host/0B4QQSpfhQZN5bFZ1ckV4QTRPb2s/batch-icons-webfont.svg#batchregular') format('svg');
    font-weight:normal;
    font-style:normal;
}



Next you use that character in your html.  
.batch {
    font-family:"Batch"; /*or whatever you've decalred your font name as*/
    font-size:16px;
    line-height:1;
    
}

Super easy!


That is nice, but I want to be awesome.  I want to have a button that does X (settings, cancel, ok, etc) and I don't want to have to have that code everywhere I have an ok button.  I just want to to have my class know about the font and character.

You can be that awesome.  We can use the ::before and ::after pseudo-elements to do that.
.before::before{
    font-family:"Batch";
    content: "\f000";
}


That is awesome! But you know, after thinking about it, I don't want my content in my css it doesn't feel right. I still think pseudo elements are sweet though. Good News! You can still use them and even use the 'data-' attributes as well. Just put the character code in a data attribute of your choice, and add the following class.
.data::before{
    font-family:"Batch";
    content: attr(data-icon);
}

Here is the final output of everything!
view the tif file.