Picking on my pretty ugly CSS code

I went to the CSSConf.asia in Singapore recently and enjoyed lots of interesting talks. Out of all interesting talks that are given out that day, the information that stands out the most to me was about writing maintainable CSS and creating improving the performance of the UI.

I work with a team of both front-end and back-end engineers. Therefore, writing maintainable code is very important to keep the productivity up and make everyone happy. Of course, it is important to focus on performance as well in order to keep your users happy; no one loves a slow and janky site.

The conference inspires me to start blogging again and work on some of unfinished projects. Honestly, I have been enjoying life too much lately.

Lets not start with some of my projects that I have been ignoring lately. There are feature requests, emails, and pull requests I still need to entertain. *sigh*

Old habits die hard

Prior to working with a team at Viki, I never really focus on making my CSS maintainable in my personal projects. I just care about making things work and ensuring that it looks pretty.

When I post CSS play things on CodePen, I always have the mentality that my code is not supposed to be production ready; it is just supposed to be pretty. I am also probably influenced by some of the pretty but not practical examples that I saw on CodePen. As a result, I have pretty demos but ugly CSS. Hence, my undying love towards my pretty ugly CSS code.

This is not a good thing because it started to become a habit of mine to write ugly CSS. I would continue writing ugly stuff on my own personal projects. This change when I started working with a group of people.

Working with a team has certainly change the way I write CSS code. At Viki, our team work with Sass with Compass all the time. When working with Sass, there are several guidelines that you need to keep in mind in order to prevent CSS code bloat, shitty performance, and nesting hell.

Measuring Improvement: Picking on myself

Have I improved? Why, yes! How do I know? Taking a look at one of my old pens, it is quite satisfying to understand why my CSS code was bad and knowing the right way to fix it.

I was looking at one of my old pens and I become highly critical of the code that I had written. Lets pick on myself by taking a look at the snippets of my HTML and CSS code from my Pure CSS Horizontal Accordion.

Note: The ugly and old version of the code is commented out.

/* 

The commented version out this Sass code is ugly and is replaced by a better one. Scroll down for the production ready version. Enjoy!

@import "compass/reset";
@import "compass/css3";

$body: #f77462;
$list: #6dc5dd;
$border: #5ab2ca;
$geneicons: #fff;

body {
	background: $body;
	font-family: Lato, sans-serif;
}

@font-face {
	font-family: 'Genericons';
	src: url('https://s3-us-west-2.amazonaws.com/s.cdpn.io/53819/genericons-regular-webfont.eot');
	src: url('https://s3-us-west-2.amazonaws.com/s.cdpn.io/53819/genericons-regular-webfont.woff') format('woff'),
		 url('https://s3-us-west-2.amazonaws.com/s.cdpn.io/53819/genericons-regular-webfont.eot') format('truetype');
	font-weight: normal;
	font-style: normal;
}

[class*="genericon"] {
	display: inline-block;
	width: 16px;
	height: 16px;
	-webkit-font-smoothing: antialiased;
	font-size: 16px;
	line-height: 1;
	font-family: 'Genericons';
	text-decoration: inherit;
	font-weight: normal;
	font-style: normal;
	vertical-align: top;
}

[class*="genericon"] {
	*overflow: auto;
	*zoom: 1;
	*display: inline;
}

.wrap {
	margin: 50px auto;
	width: 100%;
}

.tag > a:first-child:before,
.tag > a:nth-child(2):before {
	position: absolute;
	top: 90px;
	left: 25px;
	display: block;
	vertical-align: text-bottom;
	font: normal 1.5em Genericons;
	color: $geneicons;
	padding-right: 4px;
}

.tag > a:nth-child(2):before {
	font-size: 3em;
	left: 20px;
	top: 75px;
}

.tag > a:first-child, 
.tag > a:nth-child(2) {
	position: absolute;
	width: 85px;
	height: 200px;
	@include transition( all 0.4s 0.1s ease-out )
}

.tag > a:first-child {
	background-color: $list;
}

.tag > a:nth-child(2) {
	margin-left: 85px;
	background: #eee;
}

#github > div.tag > a:first-child:before,
#github > div.tag > a:nth-child(2):before {
	content: '\f200';
}

#twitter > div.tag > a:first-child:before,
#twitter > div.tag > a:nth-child(2):before {
	content: '\f202';
}

#facebook > div.tag > a:first-child:before,
#facebook > div.tag > a:nth-child(2):before {
	content: '\f204';
}

#linkedin > div.tag > a:first-child:before,
#linkedin > div.tag > a:nth-child(2):before {
	content: '\f208';
}

#insta > div.tag > a:first-child:before,
#insta > div.tag > a:nth-child(2):before {
	content: '\f215';
}

#youtube > div.tag > a:first-child:before,
#youtube > div.tag > a:nth-child(2):before {
	content: '\f213';
}
	
#tumblr > div.tag > a:first-child:before,
#tumblr > div.tag > a:nth-child(2):before {
	content: '\f214';
}

#dribbble > div.tag > a:first-child:before,
#dribbble > div.tag > a:nth-child(2):before {
	content: '\f201';
	padding-top: 3px;
	margin-top: -3px;
}

#github > div.tag > a:nth-child(2):before {
	color: #333;
}

#github > div.tag > a:nth-child(2) {
	background: #e6e6e6;
}

#twitter > div.tag > a:nth-child(2) {
	@include background-image(linear-gradient(#7adcf9, #4bc9f5));
}

#facebook > div.tag > a:nth-child(2) {
	@include background-image(linear-gradient(#548abf, #295b9e));
}

#linkedin > div.tag > a:nth-child(2) {
	@include background-image(linear-gradient(#00a9cd, #0083b4));
}

#youtube > div.tag > a:nth-child(2) {
	@include background-image(linear-gradient(#df192a, #c41222));
}

#insta > div.tag > a:nth-child(2) {
	@include background-image(linear-gradient(#7fc121, #298733));
}

#tumblr > div.tag > a:nth-child(2) {
	@include background-image(linear-gradient(#283e56, #325372));
}

#dribbble > div.tag > a:nth-child(2) {
	@include background-image(linear-gradient(#e03a70, #f189b8));
}

.accordion {
	background: $border;
	position: fixed;
	width: 100%;
	height: 200px;
	margin-top: -100px;
	left: 0;
	top: 50%;
	overflow: hidden;

	ul {
		margin-left: 30px;
		list-style-type: none;
	}

	li {
		overflow: hidden;
		position: relative;
		background-color: $list;
		border-right: $border 1px solid;
		width: 80px;
		height: 200px;
		float: left;
		display: block;
		@include transition( all 0.4s 0.1s ease-out );

		&:hover {
			width: 450px;

			a:first-child {
				margin-left: -100px;
			}

			a:nth-child(2) {
				margin-left: -5px;
			}
		}
	}

	.paragraph {
		position: relative;
		width: 360px;
		margin-left: 80px;
		padding: 50px 0 0 10px;
		height: 200px;
		background: #fff;

		h1 {
			font-size: 2.5em;
			margin-bottom: 10px;
		} 

		p {
			font-size: 0.88em;
			line-height: 1.5em;
			padding-right: 30px;
		}
	}
}

.clearfix {
	zoom: 1;
}

.clearfix:before, .clearfix:after {
	content: "";
	display: table;
}

.clearfix:after {
	clear: both;
} */

/* Updated version - 24th Nov 2014 */

@import "compass/reset";
@import "compass/css3";

/* Variables */

$backgroundColor: #f77462;
$list: #6dc5dd;
$border: #5ab2ca;
$geneicons: #fff;

body {
  background: $backgroundColor;
  font-family: Lato, sans-serif;
}

@font-face {
  font-family: 'Genericons';
  src: url('https://s3-us-west-2.amazonaws.com/s.cdpn.io/53819/genericons-regular-webfont.eot');
  src: url('https://s3-us-west-2.amazonaws.com/s.cdpn.io/53819/genericons-regular-webfont.woff') format('woff'),
     url('https://s3-us-west-2.amazonaws.com/s.cdpn.io/53819/genericons-regular-webfont.eot') format('truetype');
  font-weight: normal;
  font-style: normal;
}

[class*="genericon"] {
  display: inline-block;
  width: 16px;
  height: 16px;
  -webkit-font-smoothing: antialiased;
  font-size: 16px;
  line-height: 1;
  font-family: 'Genericons';
  text-decoration: inherit;
  font-weight: normal;
  font-style: normal;
  vertical-align: top;
}

/* IE7 */
[class*="genericon"] {
  *overflow: auto;
  *zoom: 1;
  *display: inline;
}

.container {
  margin: 100px auto;
}

.accordion {
  background: $border;
  width: 100%;
  min-width: 950px;
  display: block;
  list-style-type: none;
  overflow: hidden;
  height: 200px;
  font-size: 0;
}

.tabs {
  display: inline-block;
  background-color: $list;
  border-right: $border 1px solid;
  width: 80px;
  height: 200px;
  overflow: hidden;
  position: relative;
  margin: 0;
  font-size: 16px;
  @include transition( all 0.4s 0.1s ease-in-out );

  &:hover {
    width: 450px;

    .social-links {
      a {
        &:before {
          margin-left: -100px;
        }

        &:after {
          margin-left: -5px;
        }
      }
    }
  }

  .paragraph {
    position: relative;
    width: 360px;
    margin-left: 80px;
    padding: 50px 0 0 10px;
    height: 200px;
    background: #fff;

    h1 {
      font-size: 2.5em;
      margin-bottom: 10px;
    } 

    p {
      font-size: 0.88em;
      line-height: 1.5em;
      padding-right: 30px;
    }
  }
}

.social-links {
  display: block;

  a {
    display: block;
    text-indent: -9999px;
    font-size: 0;
    line-height: 0;

    &:before, &:after {
      @include transition( all 0.4s 0.1s ease-in-out );
      width: 80px;
      height: 200px;
      position: absolute;
      text-indent: 0;
      padding-top: 90px;
      padding-left: 25px;
      display: block;
      font: normal 30px Genericons;
      color: $geneicons;
    }

    &:after {
      font-size: 48px;
      padding-left: 20px;
      padding-top: 80px;
      margin-left: 85px;
    }
  }
}

.twitter-icon {
  a {
    &:before, &:after {
      content: '\f202';
    }

    &:after {
      @include background-image(linear-gradient(#7adcf9, #4bc9f5));
    }
  }
}

.facebook-icon {
  a {
    &:before, &:after {
      content: '\f204';
    }

    &:after {
      @include background-image(linear-gradient(#548abf, #295b9e));
    }
  }
}

.linkedin-icon {
  a {
    &:before, &:after {
      content: '\f208';
    }

    &:after {
      @include background-image(linear-gradient(#00a9cd, #0083b4));
    }
  }
}

.insta-icon {
  a {
    &:before, &:after {
      content: '\f215';
    }

    &:after {
      @include background-image(linear-gradient(#7fc121, #298733));
    }
  }
}

.youtube-icon {
  a {
    &:before, &:after {
      content: '\f213';
    }

    &:after {
      @include background-image(linear-gradient(#df192a, #c41222));
    }
  }
}

.tumblr-icon {
  a {
    &:before, &:after {
      content: '\f214';
    }

    &:after {
      @include background-image(linear-gradient(#283e56, #325372));
    }
  }
}

.dribbble-icon {
  a {
    &:before, &:after {
      content: '\f201';
    }

    &:after {
      @include background-image(linear-gradient(#e03a70, #f189b8));
    }
  }
}

See the Pen Pure CSS Horizontal Accordion by Ren Aysha (@rrenula) on CodePen.

Uh, no.. just no. From the code, you would see crazy things like:

  • HTML: Repetitive a href tags without value. Markup can be made much simpler.
  • SASS: Coding it like I am still coding CSS. What’s up with the flood of ‘>’ symbol?
  • HTML: Usage of IDs; not that IDs are bad but it can be unmaintainable if used unwisely. And when it comes to this code, everything is unwise.
  • SASS: The use of floats; C’mon. We all know floats are bloody annoying. Inline-block is more likely the way to do it.
  • SASS: Too much nth-child usage when it can definitely be made simpler if the markup is simpler. Pseudo classes of BOTH before and after can be used instead of a combination of nth-child and :before pseudo class.
  • SASS: Usage of selectors like div.tags, which can make it hard to overwrite a particular property.

How to fix this ugly.. thing?

Lets make the markup simpler shall we? Instead of having the original ugly beast, we can definitely make it simpler like so:

<!-- Instead of having duplicated link tags -->
<li id="twitter">
  <div class="tag">
    <a href="http://twitter.com/renettarenula"></a>
    <a href="http://twitter.com/renettarenula"></a>
  </div>
  <div class="paragraph">
    <h1>Twitter</h1>
    <p>My thoughts in 140 characters or less. Sometimes, I do not know how to correctly use Twitter.</p>
  </div>
</li>

<!-- Make it simpler like this -->
<li class="tabs">
  <div class="social-links facebook-icon">
    <a href="http://facebook.com">Facebook</a>
  </div>
  <div class="paragraph">
    <h1>Facebook</h1>
    <p>Where I get to stalk my friends and let them stalk me. A place to get people to stroke your ego.</p>
  </div>
</li>

Repetitive link tags are gone and we also have a proper text link. Instead of using IDs, we are using classes in order to differentiate between the social icons.

With that out of the way, we can focus on Sass. We are including a text link, which I didn’t do previously because I want an easy way out. But we don’t want this text link to be visible; we want the social icons to show instead of a text link. Therefore, we are going use a combination of the text-indent, line-height, and font-size to hide it.

.social-links {
  display: block;

  a {
    display: block;
    text-indent: -9999px;
    font-size: 0;
    line-height: 0;
  }
}

Time to style each of the tabs! Instead of relying on floats to ensure that the elements are all inline side by side, we can switch that with inline-block property to minimize the headaches.

Also, I prefer to minimize unnecessary nesting as much as possible since it leads to overly specific selectors and also bad performance. I use nesting in order to style a particular component where the elements are connected as a group in order make up one specific component (think navigation or a carousel).

My personal preference is to get straight to the point by styling a specific selector that I want. The general advice is to not go more than 4 levels deep.

/* Instead of using floats and nesting */
.accordion {
  background: $border;
  position: fixed;
  width: 100%;
  height: 200px;
  margin-top: -100px;
  left: 0;
  top: 50%;
  overflow: hidden;

  li {
    overflow: hidden;
    position: relative;
    background-color: $list;
    border-right: $border 1px solid;
    width: 80px;
    height: 200px;
    float: left;
    display: block;
    @include transition( all 0.4s 0.1s ease-out );
  }
}

/* We can use inline-block */
.accordion {
  background: $border;
  width: 100%;
  min-width: 950px;
  display: block;
  list-style-type: none;
  overflow: hidden;
  height: 200px;
  font-size: 0; // remove spacing between items
}

.tabs {
  display: inline-block;
  background-color: $list;
  border-right: $border 1px solid;
  width: 80px;
  height: 200px;
  overflow: hidden;
  position: relative;
  margin: 0;
  font-size: 16px; // restore font-size back to normal
  @include transition( all 0.4s 0.1s ease-in-out );
}

But as you know, there is a caveat to using inline-blocks: it adds unwanted spacing between the elements. My favorite solution to this is to use the font-size hack where you set the font-size to 0 for the parent in order to remove spacing and just specify a font-size on the inline-block element in order to restore the text.

Now, the social icons. My god, the original code looks like the mother of madness! Why the hell did I put two bloody link tags with :before pseudo-elements instead of using BOTH :before and :after pseudo elements?! I am sure I was high when I wrote this.

/* Instead of this mother of madness */
.tag > a:first-child:before,
.tag > a:nth-child(2):before {
  position: absolute;
  top: 90px;
  left: 25px;
  display: block;
  vertical-align: text-bottom;
  font: normal 1.5em Genericons;
  color: $geneicons;
  padding-right: 4px;
}

.tag > a:nth-child(2):before {
  font-size: 3em;
  left: 20px;
  top: 75px;
}

/* A short and sweeter version */
.social-links {
  display: block;

  a {
    display: block;
    text-indent: -9999px;
    font-size: 0;
    line-height: 0;

    &:before, &:after {
      @include transition( all 0.4s 0.1s ease-in-out );
      width: 80px;
      height: 200px;
      position: absolute;
      text-indent: 0;
      padding-top: 90px;
      padding-left: 25px;
      display: block;
      font: normal 30px Genericons;
      color: $geneicons;
    }

    &:after {
      font-size: 48px;
      padding-left: 20px;
      padding-top: 80px;
      margin-left: 85px;
    }
  }
}

Finally, setting up individual icons and background color for the tabs when hovered:

/* Instead of this code */
#twitter > div.tag > a:first-child:before,
#twitter > div.tag > a:nth-child(2):before {
  content: '\f202';
}

#twitter > div.tag > a:nth-child(2) {
  @include background-image(linear-gradient(#7adcf9, #4bc9f5));
}

/* A cleaner way (with better performance) */
.twitter-icon {
  a {
    &:before, &:after {
      content: '\f202';
    }

    &:after {
      @include background-image(linear-gradient(#7adcf9, #4bc9f5));
    }
  }
}

Note: The pseudo class is supposed to appear as &:before. For some reason, my syntax highlighter is adding an unwanted semi-colon.

Now, I am happy to say that the horizontal accordion code is production ready. I won’t feel guilty if someone copy pasted the entire thing and put it on their project.

Well, this has been fun. Feel free to try this out when you’re bored. Browse an old code and see what you can find – you may surprise yourself. I know I did. I am still in denial; I am trying to convince myself that I was either very, very high or very, very sleepy when I wrote this.

Beautiful Horizontal Accordion with Pure CSS

This is long overdue! I created this last month before I started working and I love the result. I saw this accordion originally at Ch3mical.com, a portfolio by Paul Kelley. He had achieve this effect using images and jQuery. I decided to replicate the effect using pure CSS and font icons.

For a better experience, I suggest you to go to CodePen and view this effect on full page. It is acting a bit funky on the embedded version because of the smaller window size. And use Google Chrome! I didn’t know that it didn’t work well on Firefox. I need to fix the Firefox bug later (and also the small window size bug). But don’t count on it though; I am planning to move on to other stuff or create new pens. Since I have a short attention span, it doesn’t really allow me to work on same stuff for too long.

See the Pen Pure CSS Horizontal Accordion by Ren Aysha (@rrenula) on CodePen.

Achieving this effect is pretty simple, the main targeting element to animate would be the width of the list container, the left margin of the icon container, and the background gradient of the icon container.

The initial positioning of the elements can be a bit tricky. I won’t even attempt to explain it through words since it is futile. Have fun looking through the code and experiment with it! I hope you enjoy this one as much as I do! I know the people at CodePen enjoyed it since it was under the popular page for quite sometime!

Ren Aysha ❤ Viki

Starting 4th February, I will be working as a front-end engineer at Viki, an awesome company based in Singapore. It is a video streaming website with a crowdsource subtitling functionality. It makes the viewing of K-drama and J-drama even more awesome for all of you rabid fan girls (and boys).

I am quite excited about this new opportunity as I get to strike one new year’s resolution off my list:

Get involve with a popular company. Get in one of their development teams. Learn as much as I can from these people and teach them what I know also. Sharing knowledge would be a good way to go.

I am really excited to join the web team at Viki. From my interviews, I could tell that I will be working with talented, intelligent but also humble people.

I interviewed with several companies in the States and Middle East but I decided to stay put in Singapore. From my experience, the Middle East do not really have a good market when it comes to software engineering. It is quite limited and it is not as vast compared to the market in Singapore. Furthermore, receiving stupid or pretentious interview questions from Middle East companies really turns me off – employers are not the only one who are judging candidates. Candidates like me are viciously judging them as well.

For companies in the States, it didn’t really work out because of the H1-Bs visa quota. I applied quite late and they had told me they are out of visas and that applying for it could take for about a year! I decided I can’t be idle for too long. The next best option for me would be working in Singapore. Somewhere close to home!

I am looking forward to do awesome things at Viki. The mere fact that my work will have some presence really excites me. I will update you guys more on this! Taaa!~

Happy new year to all my readers and stalkers!

2013 has been extremely slow for me. I won’t say it is my best year. Aside from being slow, it is also the year I encountered many idiots and experience many inefficiencies: getting a job contract late (how hard can crafting an offer letter be?!), getting shit salary offers (despite being given a senior position), not being taken seriously (because of ageist assholes), being ignored, given stupid interview questions that are either an insult to my intelligence or are irrelevant to my skills, given degrading and demeaning insults in the guise of a joke, etc.

Despite being a slow year, 2013 is the year I graduated. Perhaps, turning into a fresh grad throws me into another scary reality. A reality that is plagued with inefficient twits, ageist pricks, and low pay. 2013 wants me to learn how to adapt and survive. It wants me to learn how to make things happen and not to wait around. That is definitely what I learn from this year: making stuff happen for myself.

So how did I make stuff happen? I worked on projects, I applied to other exciting startups, and I started blogging again. My real reason of working on projects is to learn new things and doing things that I am not comfortable with. But when said small projects got recognition and was added to some popular sites and receive good ratings, I was really excited! I mean, here is this ageist asshole saying that I am too young and might not have any skills but here are all these other people highlighting my projects and saying they like it and that it has been useful to them.

But I thought working on projects is not enough to make me learn. I decided to apply to several startups in order to get interviews and test my skills. I got my first ‘serious’ onsite technical interview several weeks ago and I manage to hold my own. They told me to code and modify an existing web app and despite a bit of stumbling at first, I manage to voice out my thought process and solve it anyway. I came out of the interview feeling good about myself. However, I didn’t want my interactions with these people to stop after the interview. So I asked them nicely if I can keep them as my contact. Who would say no if you ask nicely right?

I have decided that 2014 will be the year that I’ll expand my network and make more friends. Smart, intelligent friends that are like-minded and have the same interest. Working on projects alone will increase my intelligence but it will not really help me see other people. Getting interviews will be one way to nabbed all these people and turn them into valuable networks. And I have to say, expanding your network could be extremely exciting. I also used my mom’s network in order to get to know other people. For example, one of my mom’s colleagues – his son is working at Square Enix. As a rabid fan of Final Fantasy and who is interested in game development, I need to shoot him an email and say hello.

Is this my first time getting an interview, if you are wondering? No, it is not my first time but I won’t consider the others as good interviews. Here’s a list of dumb questions that I got during an interview:

  1. How to link to a jQuery file and CSS file in the HTML file? – I consider this an insult to my intelligence. And yes, this is suppose to be an onsite technical interview question.
  2. How to write jQuery selectors? – instead of making this sound like questions on an exam paper, wouldn’t it be more interesting for you to tell me to write a jQuery plugin onsite instead? It will be a better approach for knowing whether I can write a jQuery selector AND checking out my coding skills.
  3. I want you to work for me but it will be unpaid – [not exactly an interview question] but the next time you are thinking of asking someone to work for free, ask yourself this: why the hell would this person want to work on something for free for you? Do you think I’m running a charity organization here? Really now?
  4. How would you design an elevator? – How is this relevant to my skills as a front-end engineer? But truly, I don’t really have much issues when it comes to whether this question is related to my skills or not. What I have issues with is the company that asks this question. See, this is a company that claims to be working on technological design concept and turning it into reality. This is a company that fades in/out quotes on design and by Steve Jobs on their page. When you’re working on technological design concepts, I expect you to be creative in coming up with ways to access potential candidates’ skills. When you come up with an interview question that is not even remotely creative and is pretty common, it shows that you are just an empty vessel that is making a lot of noise. Why? If you can’t even be creative when it comes to designing an instrument to access candidates’ skills, I don’t really expect you to have much luck in the technological design department. It also shows that you are not trying hard enough and do not really take the interview seriously. Heck, you’re not even taking the candidate seriously! This question reflects your laziness; you’ve plucked it out from the Internet didn’t you? Sorry, this question has been asked before it was cool.

The onsite interview that I went to several weeks ago was a good one. It took around 4 hours and it required me to correct and make some modification to an existing app that they wrote. Very creative in my opinion. Another example of a creative interview is this one by Pirate3d.

So what are my resolution for 2014? Do I expect it to be a good year for me? Absolutely. My resolutions:

  1. Get involve with a popular company. Get in one of their development teams. Learn as much as I can from these people and teach them what I know also. Sharing knowledge would be a good way to go.
  2. Network and make more friends. Friends give you powers… sometimes..
  3. Master vanilla JavaScript and get myself into NodeJS
  4. Build a popular feature requested by the users of Tako Movable Comments – moving bulk comments from a post to another.
  5. Contribute and create more open source projects – help give back to the community!
  6. Read more books and stop obsessing over GRRM!
  7. Play more games. All work and no play makes me a dull girl.
  8. Get mom an Apple TV with my salary
  9. Get myself a PS4 with my salary
  10. Exercise more – being a couch potato is really starting to get to me

I wonder how many of these can I achieve. I wish you guys a great year for 2014! A year that you guys can be proud of! Man, this was a good rant. Haha!

WordPress: A different approach in managing stylesheets for different pages

Managing custom scripts and stylesheets for different pages can be a pain in the butt since you have to keep editing the functions.php file in order to enqueue and dequeue several stylesheets for a particular page according to your needs. This have several drawbacks:

  1. Editing functions.php file – This is not really a big deal if you have a definite number of pages with custom templates that uses custom stylesheets. You can just work with functions.php when you are developing the theme. But what if the number of pages that uses custom template & CSS grows, like mine? I have to edit functions.php every time I have a new project page and that isn’t cool. I should be able to do this without editing functions.php.
  2. Changing themes – This is another problem. If I change my theme, I need to remember the previous modification that I did for my previous functions.php. This isn’t efficient and will cause a lot of mistakes. I know. I just change my theme and was surprise when several of my pages broke. Then, I remembered the functions.php file. D’oh!

Today, I decided to share a different way on how to manage different stylesheets and scripts for a particular page. It is still in beta stage; I am working on making it better. I just want to share the general idea here.
(more…)

WordPress Theme Development: Getting Started with Bones and Sass

Bones is a starter theme for WordPress. If you are developing a WordPress theme, Bones will be a good starting point for you since you do not have to start from scratch. Sure, there are a lot of themes that you can modify and expand on. So why Bones, specifically?

  1. It is mobile first and responsive – creating a responsive theme becomes easier as you do not need to create the CSS from scratch. Responsive designs are a necessity because we want to cater to those who love browsing on their mobile devices (like me).
  2. Very detailed – the CSS and the SCSS files are fully documented, making it easier for you to customize it however you want.
  3. Comes with Sass and Less – Sass and Less are CSS preprocessors that allows you to use variables, mixins, and functions when developing your CSS files. This allows you to define properties just once and you can reuse these properties whenever you want to; it speeds up your development workflow as you do need need to repeat yourself (DRY).

What about learning curve? This should not be your concern about working with Bones as installing and working with Sass is quite easy. Besides, developers should not let learning curves get in their way of learning new tools and resources.
(more…)

CSS3 Thumbnail Hover Effect

I remembered an old blog post by @Sohtanaka where he created a thumbnail hover effect with CSS and jQuery. I decided to replicate the same effect using CSS3, making it to Pure CSS without any JavaScript involve.

See the Pen Pure CSS Thumbnail Hover Effect by Ren Aysha (@rrenula) on CodePen

It uses good old CSS3 transitions and it is really simple:

  1. Set CSS3 transitions on both the li element that holds the image and the image itself
  2. Style the li element and the image by setting default width, height, and borders. The image should be absolutely positioned in its container (li element)
  3. Ensure that the li element has a z-index property of 0
  4. Use the :hover pseudo-class on both elements in order to add in hover effects
    • The :hover pseudo-class for the li element should have a z-index of 5 or higher. This is important in order to ensure that the hovered image will overlapped other images when hovered.
    • :hover pseudo-class of the image should have a larger width and height. In order to vertically align the image when hover, set the properties margin-top, margin-left, top, and left accordingly; play around with the values. Without tinkering with values of these properties, the effect will not look as good as it will look like the image is being enlarge from the top left rather than at the center point.

I am looking for other inspirations when it comes to hover image effect. Mainly, flash sites have unique animations on hover; would love to check out some of the effects and replicate it with CSS3. If you have any flash sites suggestions, please let me know through comments!

Code Editor with Pure CSS

I found a really nice code editor design on Dribbble created by Roy Barber. I decided to try and re-create the design with pure CSS. I thought it will be a good exercise for me to start getting creative with CSS3 useful properties like pseudo-elements, pseudo-classes, nth-child selectors, transitions, etc.

See the Pen Pure CSS Code Editor by Ren Aysha (@rrenula) on CodePen

I like it better on a full-page rather than the embedded one! So check out the pure CSS code editor in all its glory on CodePen!

The HTML is not too complicated as the whole magic lies in the CSS. In order to create the code editor’s top bar with the colored Mac buttons, you just need to use pseudo-elements, the :before & :after. The main styling of the code editor (background-color) is under the .code-editor class while the top bar can be styled using the .code-editor:before.

The red, yellow, and green Mac button is also created using pseudo-elements and it uses the nth-child pseudo-class in order to style it individually; this is better than creating a new class for each of the button. The buttons are grouped under a span class called .control. The class span.control:before is needed in order to positioned the group together at the top bar. span.control:nth-child(n):before is needed in order to set the BG color and also set a distance between each other.

Keep in mind that when you use pseudo-elements, the content attribute is required. Therefore, either put an empty string or put some text in it.

As for the nice curve around the edges, you know the answer: border-radius makes this as easy as pie!

I can make this editable; however, I need to play with JavaScript Selection and Range in order to ensure that the syntax get highlighted as you type. This will take some time as it is a pain in the butt.

I am aware of these CSS3 properties before but I have never use it in order to create shapes or draw something with it. I will certainly try it often now given how easy it is. You just need a little bit of imagination to get going!

Testing Scrolling Events with Qunit.js

When I was writing the unit test for Anchorific.js, I was having a hard time writing test for the ScrollSpy implementation as there was not many documentation on testing for scrolling event. Therefore, I decided to come up with my own solution, which is not perfect, but it does a great job in doing what I wanted it to do. So, lets assume that we have a simple ScrollSpy plugin that will update the active state of the navigation based on your scroll position. Just have a look at the demo page of Anchorific.js, as Scrollspy.js was extracted from it.

The plugin code is quite simple, as you can see below.

/*
  Simple ScrollSpy implementation by Ren Aysha
  It updates the active links of the navigation based on your scroll position. 
  When a header tag is at the top of the viewport, it will highlight the navigation link that 
  has the href value of the header's ID.
  
  Usage: $( '.content' ).scrollspy();
  .content should contain several header tags - h1, h2, h3... Should have a navigation list with a 
  href value that corresponds to each header tag's ID.
  
  Example:
  
  <div class="content">
    <h1 id="hello-world">Hello World<h1>
    <p>Some lorem ipsum shit</p>
    <h1 id="hello-people">Hello People<h2>
    <p>Some lorem ipsum shit</p>
  </div>
  
  <div class="navigation">
    <ul>
      <li><a href="#hello-world">Hello World</a></li>
      <li><a href="#hello-people">Hello People</a></li>
    <ul>
  </div>
*/

if ( typeof Object.create !== 'function' ) {
	Object.create = function( obj ) {
		function F() {}
		F.prototype = obj;
		return new F();
	};
}

(function( $, window, document, undefined ) {
	"use strict";

	var ScrollSpy = {

		init: function( options, elem ) {
			var self = this;

			self.elem = elem;
			self.$elem = $( elem );

			self.headers = self.$elem.find( 'h1, h2, h3, h4, h5, h6' );
			
			self.spy();
		},

		spy: function() {
			var self = this, previous, current, list, top, prev;

			$( window ).scroll( function( e ) {
				// get all the header on top of the viewport
				current = self.headers.map( function( e ) {
					if ( ( $( this ).offset().top - $( window ).scrollTop() ) < 10 ) {
						return this;
					}
				});
				// get only the latest header on the viewport
				current = $( current ).eq( current.length - 1 );

				if ( current && current.length ) {
					// get all li tag that contains href of # ( all the parents )
					list = $( 'li:has("a[href="#' + current.attr( 'id' ) + '"]")' );

					if ( prev !== undefined ) {
						prev.removeClass( 'active' );
					}

					list.addClass( 'active' );
					prev = list;
				}
			});
		}
	};

	$.fn.scrollspy = function( options ) {
		return this.each(function() {
			if ( ! $.data( this, 'scrollspy' ) ) {
				var spy = Object.create( ScrollSpy );
				spy.init( options, this );
				$.data( this, 'scrollspy', spy );
			}
		});
	};

})( jQuery, window, document );

Testing scrolling event by using iFrame

My idea is to use iFrame when we are testing out scrolling events. So, it is important for us to create a HTML file where we can construct some header tags and use the plugin on it. It will look like this:

<!-- index.html -->
<body>
<div class="content">
    <h1 id="hello-world">Hello World<h1>
    <p>Some lorem ipsum shit. Obviously, this needs to be longer as we need to ensure that it can scroll down...</p>
    <h1 id="hello-people">Hello People<h2>
      <p>Some lorem ipsum shit. Obviously, this needs to be longer as we need to ensure that it can scroll down...</p>
  </div>
  
  <div class="navigation">
    <ul>
      <li><a href="#hello-world">Hello World</a></li>
      <li><a href="#hello-people">Hello People</a></li>
    <ul>
  </div>

<script src="src/scrollspy.js"></script>
    <!-- Needed for unit test -->
    <script type="text/javascript">
       var c = $( '.content' ).scrollspy(), people = $( 'h1#hello-people');
       // This is our test case, lets try jumping to hello-people, the test should be able to tell us that one link is highlighted
       $( 'html, body' ).animate({
           scrollTop: people.offset().top
       }, 3);
    </script>
</body>

Notice that I am adding another block of code; I didn't just stop at calling the scrollspy() method. I wrote a test fixture which will scroll down to the 'hello people' section - h1#hello-people will be just right at the edge of the view port when this code runs. This will cause the code below

 <li><a href="#hello-people">Hello People</a></li>

to be flagged with an active class.

Now, we need to do is call the index.html with an iFrame tag under the #qunit-fixture element:

<div id="qunit-fixture">
    <!-- To test scrollspy; iframe is needed in order to simulate scrolling -->
     <div id="scrollspy">
     <iframe src="../index.html" width="1200" height="1000" id="scroll">
     </iframe>
</div>

Finally, we will write the test. The test will check how many links are highlighted. Highlighted links should contain the class 'active'. Since we had written a test fixture that jumps straight to h1#hello-people, the navigation should only have one link that is active:

asyncTest( 'Scrollspy highlights links correctly when scroll', function() {
	setTimeout(function() {
   // iFrame has an ID of #scroll
		var s = $( '#scroll' ).contents().find( '.navigation' ).find( 'li.active' );
		equal( s.length, 1, "No of links highlighted is correct when scroll to h1#hello-people" );
		start();
	}, 100 );
});

We are using asyncTest() instead of the usual test() method because the link will not get highlighted immediately; it takes a bit of delay for the link to be flag with .active, though it is not for long.

The Downside

You need a local server (MAMP, XAMPP, etc) in order to run the test. Why? Well, try and run the test without a local server. Console.log will tell you:

  • Blocked a frame with origin "null" from accessing a frame with origin "null". Protocols, domains, and ports must match.
  • Uncaught SecurityError: Blocked a frame with origin "null" from accessing a frame with origin "null". Protocols, domains, and ports must match.

Why exactly? Because of Same-Origin Policy. For security reasons, it can only run when it originates from the same site. Specifically, it should have the same protocols, domain name, and port number. Therefore, you need to run the test in a local web server. For mamp, usually it is localhost:8888/scrollspy/test.html. You can also create an application with Node.js and put your test files in the application folder, start the server, and navigate to your test document.

A solution without local web server?

Luckily, there is one. I am using Grunt.js, a JavaScript task runner and you need to install Node.js in order to install Grunt. Grunt has a lot of plugins and one of them is Qunit.js. You need to configure it in your Gruntfile.js and run the test through the terminal by typing grunt or grunt qunit.

So how exactly do we use Grunt.js? That will be another post on its own!

You have another idea of doing this? I would love to hear it out.

Anchorific.js is now on Github!

I created Anchorific.js in order to learn three things:  GruntQunit, and how to develop a jQuery plugin. So, I developed a jQuery plugin in order to this. I am a big believer in learning by doing. Learning passively through a book just never works for me and I just need to go out there and do it. This is the real reason why Anchorific.js was born; it is not because I was too lazy to create anchored headings and anchor navigations on a single-page document.

In a nutshell, Anchorific.js creates anchored headings and generates nested anchor navigations automatically based on the header tags. My intention is for it to be used in a single-page project documentation, like the one that I created for the demo page of the plugin. There are other plugins that are able to create anchored headings automatically. But there is no harm done for using an over-used idea in order learn what I wanted to learn. Plus, I have never really stumbled upon a plugin that generates anchor-based nested navigations automatically based on the header tags. So, I think I managed to add my own twist for this one.

I am planning to write about Grunt.js and unit testing with Qunit. I encountered several problems when I was unit testing the Scrollspy functionality of the plugin. I had no idea how to test for scrolling events. I searched around for guides but most of them focused on testing for click events. I had to come up with another way on how to do it and I would love to write about it as people may be facing the same problem.

Anyway, check out the project page and Anchorific is also on Github! I will be writing about Grunt.js and Qunit.js soon.