Ranting and raving about anything I feel like complaining about.

Modern Web Development - Part 3

This is the third of ten parts of this blog post. The topics will be:

What’s Wrong with CSS?

If you’re going to do web development, you’ll need to learn how cascading style sheets (CSS) work. It’s a fine system for defining the look and feel of your designs but as a developer I find them more painful than necessary. Let’s discuss some of those pains.

So let’s assume I was writing a simple contact page form. So I’ll have a number of rules for the different parts of the form (I would probably not have rules specifically for the contact form, but have a common set of rules, but for this example it’s good enough):

contact_blank

It’s plain enough, so let’s make it pretty with some CSS:

/* home-contact.css */
h2
{
  font-style: italic;
  color: #222;
}

.big-form input[type=text]
{
  width: 300px;
  height: 25px;
  padding: 5px;
  border-radius: 5px;
  border: 1px black solid;
  background-color: #EEE;
  color: #222;
  margin: 2px 0;
  font-size: 18px;
}

.big-form label
{
  color: #222;
  font-size: 14px;
}

This works, but there are some common problems with this including repeating of values, repeating of selector syntax, no good way to handle shims or polyfills as well as no way to calculate values. In this way, CSS is purely content and presents maintenance issues.

A Better CSS?

To the rescue are dynamic stylesheet languages. Among these are:

  • Less: Client-side or server-side supported language for solving stylesheet problems.
  • Sass: Command-line or server-side supported language for solving stylesheet problems.

Both of these languages are similar in scope as they support features like:

  • Nested Styles
  • Variables
  • Mixins

They are both great languages the key importance here is that you should stop writing plain old CSS and use a dynamic stylesheet language. 

While Sass is a bit more powerful, I decided on Less as I found it easier to integrate with ASP.NET MVC. There are a number of ways to do this, but for me the least amount of friction came with a simple package I could install to my web project called dotless. This project can be installed via Nuget:

dotless

Dotless installs a http handler so that if you refer to .less files in your stylesheet declarations, it returns the generated CSS. Some might prefer to compile to .css at compile time, but since the .js can be changed at runtime too, I though this was the best mix of the two solutions. So let’s see how LESS can fix this for us.

Getting Less Working in ASP.NET MVC

One of the basic concepts around these dynamic stylesheet languages is that CSS is completely valid. So if I change my .css file to .less (and change the link to point to the .less file instead):

@* Contact.cshmtl *@
@model ModernWebDev.Models.SomeFormModel
@{
  ViewBag.Title = "Contact Us!";
}
@section Stylesheets
{
  <link rel="stylesheet" href="@Url.Content("~/Content/Home.Contact.less")" />
}
<h2>
  Contact</h2>
...

At this point the page still works because the new .less stylesheet is returned as the CSS it contains. Let’s refactor the CSS to use some of our new found power.

Refactoring the CSS with Less

The first change I want to make is to centralize the text color so that if I change it, I don’t have to search and replace. Less has the concept of variables. These variables start with the ‘@’ symbol like so:

@variable_name: value;

This means it can add a variable to hold our color at the top of our less file and use it everywhere:

/* home-contact.less */
@text-color: #222;

h2
{
  font-style: italic;
  color: @text-color;
}

.big-form input[type=text]
{
  width: 300px;
  height: 25px;
  padding: 5px;
  border-radius: 5px;
  border: 1px black solid;
  background-color: #EEE;
  color: @text-color;
  margin: 2px 0;
  font-size: 18px;
}

.big-form label
{
  color: @text-color;
  font-size: 14px;
}

While the variables tend to be constants, you can perform operations on them too. For example, I could use another variable to store the base size of my font then use operations to change the value as necessary:

/* home-contact.less */
@text-color: #222;
@base-font-size: 14px;

h2
{
  font-style: italic;
  color: @text-color;
}

.big-form input[type=text]
{
  width: 300px;
  height: 25px;
  padding: 5px;
  border-radius: 5px;
  border: 1px black solid;
  background-color: #EEE;
  color: @text-color;
  margin: 2px 0;
  font-size: @base-font-size + 4;
}

.big-form label
{
  color: @text-color;
  font-size: @base-font-size;
}

Line 21 is the magical line here. Notice that we’re saying that the size of the textbox text is going to be 4 larger than the base font, whatever that ends up being. Less can infer many types of operations and data types. For example, all of these work fine:

@aColor: Blue;
@aDarkerColor: @aColor + 80%;

@font-size: 14px;
@large-font-size: @font-size + 4;
@huge-font-size: @font-size + 8px;

Next thing is nested rules. Instead of repeating the name of the class (.big-form), less can nest it so that the nested styles will include the class name like so:

/* home-contact.less */
@text-color: #222;
@base-font-size: 14px;

h2
{
  font-style: italic;
  color: @text-color;
}

.big-form 
{
  input[type=text]
  {
    width: 300px;
    height: 25px;
    padding: 5px;
    border-radius: 5px;
    border: 1px black solid;
    background-color: #EEE;
    color: @text-color;
    margin: 2px 0;
    font-size: @base-font-size + 4;
  }

  label
  {
    color: @text-color;
    font-size: @base-font-size;
  }
}

While this is a whole lot more readable, it generates the right CSS to do the job that is implied. The CSS will include the duplication but this will be much easier to read and maintain.

We can also support mixins. The idea of mixins is to allow function-like syntax to add one or more properties. Mixins are defined with a ‘.’ to start then a name and parameter list. A simple mixin could be:

.solid-border()
{
  border: 1px black solid;
}

This allows us to use it in a rule and everything inside it is dumped in the rule:

.big-form 
{
  input[type=text]
  {
    .solid-border();
    //...
  }
  // ...
}

This results in the border line being added to the input. A great reason to use mixins is to handle cross-browser css like border-radius:

.rounded-corners-all(@size) {
  border-radius: @size;
  -webkit-border-radius: @size;
  -moz-border-radius: @size;
}

Notice that the @size variable is in the parameter list then used in the mixin. It can be used to leave size information in the rule like so:

.big-form 
{
  input[type=text]
  {
    .rounded-corners-all(5px);
    .solid-border();
    //...
  }
}

This will result in all three values being put in the rule with a size of 5px. But what’s interesting is that if the value passed in is multi-values, it can still accept it as a single value for example:

.big-form 
{
  input[type=text]
  {
    .rounded-corners-all(5px 10px 5px 15px);
    //...
  }
}

This works fine because the language knows that without any commas it should be treated as one value.

We’re getting close to a good solution, but we have all this information in one less file and it hurts its reuse. So we should refactor into several files moving the colors and mixins into their own files. Less has an interesting concept of import. If we move the colors and mixins to their own files, we can then use @import to pull in these other files:

/* home-contact.less */
@import "colors";   // will find .less if it exists
@import "mixins";   // otherwise will drop to css
@import "site.css"; // importing css is fine too

By specifying the import without the extension, it will try and find a .less file or fallback to a .css file. This way as you refactor into .less you don’t have to go fix up names as you need less features (pun intended).

This import will bring in complete .css files as well. The import here doesn’t just refer to the imported files but actually merges them into the file for you. So the resulting .less file contains all of the imported files. This means that you can build your CSS modularly without having to worry about the number of stylesheets that the browser can handle.

Debugging Less

Finally, sometimes you just need to see what is happening after processing. The easiest way to see this is to just browse to the .less file:

results

You can see this all working the working progress of the source code:

What do you think?

 
 

Comments

Gravatar Have a look at stylus too.
Gravatar I think it all looks great if you don't plan on using any design tools. But I question whether HTML/CSS design tools out there will play nicely with these technologies (Less and Sass). At least for a while, anyhow. What are your thoughts on that?
Gravatar Aaron,

You have a point, but for me the design tools were useful in wireframing the overall design, not individual pages and since you can mix CSS and LESS it worked well. The other way to handle it is to do LESS at design-time. There are a couple of tools that will build .css as you edit the .less files but I didn't like the workflow for me...might be good for you.
Gravatar Blake,

Thanks...I think the important idea is to use one; not specifically which one to use.
Gravatar Great tutorial again. Noob question: how is dotless different from referencing less.js in you application?
Gravatar Mithun,

With dotless, the transformation happens on the server. Makes it easier to navigate to the .less file to see how the transformation happens. I also think it's lighter on the client-side. Either way is fine though.
Gravatar Rik,

The less file syntax is the SyntaxHighlighter javascript plugin for my blog. I don't have it in VS at all. Mindscape was crashy for me. I am using Sublime Text 2 to edit...good highlighting for LESS/SASS but no intellisense (which is fine for me).
Gravatar Just an FYI...the link to the source code is not working.
Gravatar I'm still astounded that there is no design (page) view IDE for the razor engine. How do you deal with this?
Gravatar Tamarack,

Fixed. Thanks for letting me know. Forgot to upload it.
Gravatar Jeff,

I understand the desire for a design view but it's not practical IMO. The problem is that with Razor, templates and for-each loops present more of a template pattern. We see this in the ASP.NET WebForms designer as being wrong or error'ed most of the time so I dont' even bother to use it in WebForms. So, no...it doesn't bother me ;)

Gravatar What i'm thinking is, we might have to specify Hardware Configuration for every web site in future,as the emerging client scripts makes 80% of work is processing at client side only.
Gravatar Do I, as a developer, have control of the content of a .less file in the source of the .net page? For instance, do I have access to the variables and functions defined in the .less page via the "mypage.aspx.cs" file so I can manipulate the .less variables with say, the user login preferences, before the server spits it out to the browser? This would be a much more rich ability. Integrating the code behind files with the .less files to allow me to dynamically change the content of the .less variables based on logic in my code behind.
Gravatar Perry,

There are ways to accomplish that but it not necessary. You can do much of what you want in Razor and leave LESS/SASS to handle the layout only.
Gravatar Hi,

Just wanted to mention the mindscape webworkbench. It has a free edition and makes working with less, sass, coffeescript a lot easier from within vs.
http://visualstudiogallery.msdn.microsoft.com/2b96d16a-c986-4501-8f97-8008f9db141a
Gravatar Ike,

I am aware of the extension, but for me I found it crash-y so I stopped using it.
Gravatar Great series Shawn! I am enjoying it very much and learning lots!

I agree, Mindscape Webworkbench is simply PAINFUL - crashy and slow. I feel that "Web Essentials from Mads Kristensen" ( VS extension) does a MUCH better job with CSS/LESS overall.

Also, I recommend you look at "Chirpy" on Codeplex for automatic handling of LESS -> CSS translation, combining and minification of CSS, JS etc ... well worth a look.

Thanks for sharing your experience!





Gravatar I really like the Chirpy VS2010 extension for Less. It processes the less and generates a separate minified css version
Gravatar Chirpy is cool but I can't take the .chirpy.less way they do it. I don't want my tools bleeding into my source.
Gravatar Thanks to you, I now have an idea of what Less is .. Thank you!
Gravatar I also found mindscape webworkbench very buggy and it really slowed down with large files. Chirpy doesn't really bring anything to the party except 'save-time' compilation of less.
Gravatar Enjoying this series a lot! Thanks!
Gravatar great tutorial. will start using Less now. noticed it when using bootstrap from twitter.

you are the best...

Add a Comment

*
*
*