Application Development

From JaxHax
Jump to: navigation, search

Application of CSS Level 3

This tutorial will take the basic HTML tags and styles you've been learning and put them together into a functional piece of code. You will be creating a simple, yet professional looking, header with the following goals:

  • Center a page heading and navigator
  • Center both horizontally and vertically
  • Treat this as a template, allowing variable amounts of text
  • Be responsive; change layouts based on screen view widths
  • Maintain valid standards for best possible cross platform ability
  • To begin, we'll use the same basic layout from our other lessons:

    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="utf-8">
      <title>Title</title>
      <meta name="description" content="">
      <link rel="stylesheet" href="style.css">
    </head>
    <body>
    </body>
    </html>
    

    Now, in addition to our own style.css, we're going to include normalize.css. Normalize is a maintained library of CSS whos goal is to ensure that all browsers will render your page the same before applying your own styles. Normalize, available at http://necolas.github.io/normalize.css/, is highly documented and commented, and we will only cover the parts to which we need to pay special attention in these lessons. For our purposes here, simply make it a standard practice to include normalize prior to any other styles and save yourself some bad surprises when crossing browsers later. I've provided a simplified version of normalize.css here:

    article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block;}
    audio,canvas,video{display:inline-block;}
    audio:not([controls]){display:none;height:0;}
    [hidden],template{display:none;}
    html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%;}
    body{margin:0;}
    a{background:transparent;}
    a:focus{outline:thin dotted;}
    a:active,a:hover{outline:0;}
    /* Address variable `h1` font-size and margin within `section` and `article`
     * contexts in Firefox 4+, Safari 5, and Chrome.*/
    h1{font-size:2em;margin:0.67em 0;}
    abbr[title]{border-bottom:1px dotted;}
    b,strong{font-weight:bold;}
    dfn{font-style:italic;}
    hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0;}
    mark{background:#ff0;color:#000;}
    code,kbd,pre,samp{font-family:monospace,serif;font-size:1em;}
    pre{white-space:pre-wrap;}
    q{quotes:"\201C""\201D""\2018""\2019";}
    small{font-size:80%;}
    sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline;}
    sup{top:-0.5em;}
    sub{bottom:-0.25em;}
    img{border:0;}
    svg:not(:root){overflow:hidden;}
    figure{margin:0;}
    fieldset{border:1px solid #c0c0c0;margin:02px;padding:0.35em 0.625em 0.75em;}
    legend{border:0;padding:0;}
    button,input,select,textarea{font-family:inherit;font-size:100%;margin:0;}
    button,input{line-height:normal;}
    button,select{text-transform:none;}
    button,htmlinput[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer;}
    button[disabled],htmlinput[disabled]{cursor:default;}
    input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0;}
    input[type="search"]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box;}
    input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none;}
    button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0;}
    textarea{overflow:auto;vertical-align:top;}
    table{border-collapse:collapse;border-spacing:0;}
    

    Then we'll simply call normalize before the rest of our styles like so:

    ...
      <meta name="description" content="">
      <link rel="stylesheet" href="normalize.css">
      <link rel="stylesheet" href="style.css">
    ...
    

    Now fill in some content for your HTML so we have something to work with:

    <body>
      <header>
        <h1>Here Be <em class="paper">Things</em></h1>
        <nav>
          <a>Things</a>
          <a>Stuff</a>
          <a>About</a>
          <a>Contact</a>
        </nav>
      </header>
    </body>
    

    Now we'll create our style.css; first we'll manage our header space, give it a background so we can see it, along with some reliable letter spacing so we don't get jumbled:

    header {
      letter-spacing: 1px;
      background: #f0f0f0;
    }
    

    Now the H1 tag has been around since the beginning of HTML, so browsers are all on the same page about what size H1 text is and how much space they get. All on the same page that is, except when that page is inside one of the new <section> or <article> tags. For whatever reason different browsers want to put their own spin on how h1 should look. Normalize levels the playing field here by specifying that all H1's have a top/bottom margin of .67em and no left right margin across the board. For the most part, this is great and makes everything uniform, but in the case of our header, we don't want any fancy pants margins getting in the way of our already complicated calculations:

    header h1,
    header nav {
      margin: 0;
    }
    

    Now that those margins are out of the way, we'll set our header size aand padding relative to the viewers page size. Effectively creating our own margins without having to know what the end users browser looks like:

    header {
        letter-spacing: 1px;
        height: 8em;
        padding: 2em 10%;
        background: #f0f0f0;
    }
    

    Next, we'll place our two elements (heading and navigator) in block containers to separate them out, but still leave them capable of being inline with each other:

    header h1,
    header nav {
      margin: 0;
      display: inline-block;
    }
    

    Note: this effects both the h1 and the nav containers inside the header container, since we specified both of these separated by a comma in our style statement.

    Working with Justify

    Justified text is the layout used by newspapers to put everything in those fancy columns. Justified type works by varying word spacing per line, and sometimes letter spacing as needed, to have equal line lengths per column. We'll use justify here to space our heading text out niceley:

    header {
      text-align: justify;
      letter-spacing: 1px;
    

    If you refresh your page now, you'll see that nothing has changed. The reason for this is that justify doesn't know how long the column is until there is a line break, or second line of text. It uses the line break to calculate the full length of the line. Without this, it doesn't space anything out and just looks like left align, the default we already had. The trick to getting justify to work is, as we said, to have more than one line of text. Trouble is, we don't have more text, and a chunk of whitespace at the end/under our text defeats the purpose of our styling entirely. The important thing to know is that justify works based on the length of text in our block, but what if there wasn't any text? Justify will simply apply to the block because everyone of the 0 length lines in the block will have the same 0 length of text. Also, because of the nature of the way styles cascade, any text style applied to a block in the same container and row will apply to the other blocks in the same container and row unless specifically overridden. We'll use the pseudo "after" to insert content behind our header element. The trick comes in when we don't actually inject any content. By doing this, we create a virtual container that won't affect our spacing or layout at all. We then specify that our nonexistent content is taking 100% of the width of our imaginary block. Now justify will kick in since it now has a block that is full, only it applies to all the blocks in the row:

    header::after {
      content: '';
      display: inline-block;
      width: 100%;
    }
    

    With that taken care of, we can clean up a bit. Give the rest of the page a bit of color, move our letter spacing to the body for some uniformity, and use our fancy text-transform to make sure anything types in our headers heading stands out:

    body {
      background: #2c3e50;
      letter-spacing: 1px;/*Apply spacing to entire page*/
    }
    
    header {
      text-align: justify;
      text-transform: uppercase;/*Make a little stronger*/
      ...
    

    Vertical Alignment

    So now we've got a proper header and we've go it lining up nicely horizontally. Next on our list, center it vertically:

    header h1,
    header nav {
      display: inline-block;
      margin: 0;
      vertical-align: middle;
    }
    

    The change here is subtle, but the navigator text is now middle aligned to the heading text. Nice, but not what we were going for. "But I thought you said h1 and nav separated by commas would apply the style to both!?" It does, only the h1, with it's no margin is already vertically centered, at top and bottom aligned for that matter. There is no 'white' space in our container for our heading to move. The effect is that the row becomes the height of the heading and our nav dutifully jumps to the middle of that height.

    Simple fix right, just make the h1 take up 100% of the space and then there will be enough room to move:

    header h1 {
      height: 100%;
    }
    

    Um, wrong... Now as far as the nav is concerned, the heading is the height of the header, and so middle aligns to the entire header. Unfortunately, the heading itself ignores our vertical-align and instead prefers to hang out at the top like we never even gave it more space. The reason being, our text thinks it's container is sized based on the text itself, not the style we've applied to it.

    If you guessed we'd use another pseudo to fix this, then you'd be right. This time we'll reference a fake container before our h1 element. We'll give it a height of 100%, but like last time, inject nothing for content. Without the text, when we apply the vertical-align style, it will actually apply to the entire container:

    header h1::before {
      content: '';
      display: inline-block;
      vertical-align: middle;
      height: 100%;
    }
    

    Allowing Variable Amounts of Text

    So now we've got things floating nice and centered, but what if we want to have a longer heading? Will it still fit nicely in our boxes?

    	<h1>Here Be <em class="paper">Things</em> so lets make this a much longer thing for illustrative purposes.</h1>
    

    Nope, looks like things start to overlap. That's a simple fix, we'll just restrict the width of our heading to 50% of the header:

    header h1 {
      height: 100%;
      width: 50%;/*So we apply a maximum of 50% to ensure we have room*/
    }
    

    Well, that got our heading off our navigator. Now we see our next issue, the text stays aligned, but this forces any text in our heading beyond the first line to jump down below the blocking we did to line everything up with our before pseudo. Now we'll just throw everything into another container so it all stays in the same row:

    	  <div><h1>Here Be <em class="paper">Things</em> so lets make this a much longer thing for illustrative purposes.</h1></div>
    

    And now we'll just update our style.css to accommodate the change adding the div:

    body {
      background: #2c3e50;
      letter-spacing: 1px;
    }
    
    header {
      text-align: justify;
      text-transform: uppercase;
      background: #f0f0f0;
    }
    
    header::after {
      content: '';
      display: inline-block;
      width: 100%;
    }
    
    header > div,
    header > div::before,
    header > div h1,
    header nav {
      display: inline-block;
      margin: 0;
      vertical-align: middle;
    }
    
    header > div {
      height: 100%;
      width: 50%;
    }
    
    header > div::before {
      content: '';
      /*inline-block no longer needed as no longer h1*/
      /*vertical-align: middle; inherited from above*/
      height: 100%;
    }
    

    Notice the '>' before the div tags. This means immediate child, or that this style should apply only to the first div following a header tag. This helps make sure that we don't create any styles that will come back to haunt any other div tags we may place in our page content later. Also, we applied our sizing to the div, instead of the h1, since that is the container that now effects everything. Now that we have this much text, justify looks a bit spacey and sloppy, so maybe well override that with:

    ...
      width: 50%;
      text-align: left;
    }
    

    Not that anyone should probably use that much text. This is a good example of why some developers will place a character or word limit on entries in their CMS, admin console, etc. I, personally, prefer to give the end user all the options and let them worry about how it looks than complain I'm crippling them or write me off as unusable. Now we'll just give a bit more height and a little less padding on the sides to accommodate our larger heading space:

    …
      text-transform: uppercase;
      height: 15em;
      padding: 2em 5%;
      background: #f0f0f0;
    …

    OK, so now our header's looking good…unless your screen is a bit small, then things get a bit squishy. Go ahead and check it out in a desktop browser, drag your browser windows to smaller widths. OK, so everything fits on a computer, but we need it responsive to smaller screens, like phones, also. So maybe the div wasn't the solution, let's remove that:

    …
    	  <h1>Here Be <em class="paper">Things</em> so lets make this a much longer thing for illustrative purposes.</h1>
    …

    And fix our css to remove the div's again:

    body {
      background: #2c3e50;
      letter-spacing: 1px;
    }
    
    header {
      text-align: justify;
      text-transform: uppercase;
      height: 15em;
      padding: 2em 5%;
      background: #f0f0f0;
    }
    
    header::after {
      content: '';
      display: inline-block;
      width: 100%;
    }
    
    header h1,
    header nav {
      display: inline-block;
      margin: 0;
      vertical-align: middle;
    }
    
    header h1 {
      height: 100%;
      width: 50%;
      text-align: left;
    }
    

    This time we'll just remove any reference to height at all from the header:

    …
    header {
      text-align: justify;
      text-transform: uppercase;
      padding: 2em 5%;
    …

    And kill the height reference in h1 as well:

    …
    header h1 {
      width: 50%;
    …

    Then we'll move our left align from h1:

    …
    header h1 {
      width: 50%;
    }
    …

    To h1, nav so things will line up nicely when the screen shrinks enough to make the overlap:

    …
      vertical-align: middle;
      text-align: left;
    }
    …

    So now things scale nicely, and the formatting degrades well when on a small screen. You can try this out by scaling the sides of the browser window in and out on a desktop browser. It also scales well if we drop our text size back down, go ahead and comment out the added text in the html and try it out:

    ...
    	  <h1>Here Be <em class="paper">Things</em><!-- so lets make this a much longer thing for illustrative purposes.--></h1>
    ...

    Now that's working better, but what if we needed the height defined? Such as with a static or minimum height graphic layout?

    At-Rules

    Ok, so we need a responsive layout AND we need to control our height. But, we need to control the height based on the size of a window we don't know. So now all we need to do is know the size of the window.

    First, lets roll this project back to where we can control the height again, lets put those divs back again:

    ...
    	  <div><h1>Here Be <em class="paper">Things</em> so lets make this a much longer thing for illustrative purposes.</h1></div>
    ...

    And fix our css to again accommodate the divs:

    ...
    body {
      background: #2c3e50;
      letter-spacing: 1px;
    }
    
    header {
      text-align: justify;
      text-transform: uppercase;
      padding: 2em 5%;
      background: #f0f0f0;
    }
    
    header::after {
      content: '';
      display: inline-block;
      width: 100%;
    }
    
    header > div,
    header > div::before,
    header > div h1,
    header nav {
      display: inline-block;
      margin: 0;
      vertical-align: middle;
      text-align: left;
    }
    
    header > div {
      width: 50%;
    }
    ...

    Then we'll add our heights back, starting with our header, which I'll make a little smaller this time:

    ...
      text-transform: uppercase;
      height: 12em;
      padding: 2em 5%;...
    
    ...header > div {
      height: 100%;
      width: 50%;
    ...

    And we'll put out before sudo back in place too:

    ...
    header > div::before {
      content: '';
      height: 100%;
    }...

    Now we'll style the anchors in our navigator to clearly sparate them:

    ...
    header nav a {
      padding: 0 0.6em;
      white-space: nowrap;
    }...

    And use another pseudo to kill the padding after the last entry:

    ...
    header nav a:last-child {
      padding-right: 0;
    }...

    Now to learn about our viewers browser. For this we'll use at-rules, which are instructions sent to the CSS parser. @rules are supported by firefox 3.5+, IE 9+, Opera 9+ and Webkit(Safari 4+ and Chrome 21+), with limited support in CSS2.1 in Gecko 1.7+(Firefox 1) and Webkit(Safari 1.3+ and Chrome 1+). You can also use the respond.js library to provide support for IE 6-8.

    The @rule we'll be using now is @media, which uses the format:

    @media (media_query) media_type media_query [expression]

    Where media specifies that the rules only apply to that certain media. All the supported browsers listed above support the media types "screen" and "print"; the gecko engine (Firefox) also has an extension to support "projection". "aural", "braille", "embossed", "handheld", "tty" and "tv" are pretty much only recognized by the browsers on those styles of media. The type "all" is also a media type, if you want to specify expressions to apply to any media. For the most part, rules that aren't recognized are typically ignored by the CSS parser, so specifying an @rule for @tv typically will not render unless on a television that supports @rules. Additionally you may explicitly specify qualifiers with "only", such as "@media only tv", which will also help keep older parsers from accidentally applying these rules.

    Other specifiers include "and", "," and "not". "and" specifies that both conditions must be met. "," means or, and specifies that either condition may be met. "not" specifies that the following condition can not be true in order for the rule to apply.

    The [expression] uses the format:

    [ <media_feature> [: <value>]? ]

    Most expressions can use the prefix's "min" or "max", for example "max-width", means a range of anything less than or equal to. Conversely min meaning a range of greater than or equal to. It's important to realize that equal to is a part of the equation. Many expressions can also use the prefix "device", such as "max-device-width", meaning that anything that is a certain screen size or less. Versus "max-width" which simply means anything that is a certain browser window size or less.

    media_features include:

  • "width" and "height" which are length checks
  • "aspect-ratio" which is horizontal/verticle
  • "color" and "monochrome" which is minimum bits per pixel; if a screen is not color/monochrome it will return a 0 on the respective wrong test
  • "color-index" is the number of colors the screen can display
  • "resolution" is the number of pixels in the screen, i.e.300dpi
  • "orientation"; the options here are simply landscape or portrait
  • "scan"; progressive or interlace
  • "grid" specifies grid display instead of a screen, such as a an LED readout. in the case of grid, "width: 15em" would specify a 15 dot wide LED panel; you may use min/max on width, but not grid
  • So now that we know all about the @media rule we'll create our @rule:

    @media screen and (max-width: 800px){
      header {
        height: auto;
      }
      
      header > div,
      header > div h1,
      header nav {
        height: auto;
        width: auto;
        display: block;
        text-align: center;
      }
    }
    

    Now our screen will scale nicely on larger screens, those over 800px, and will reformat on smaller screens and center our navigator under our heading block.