An Interest In:
Web News this Week
- March 27, 2024
- March 26, 2024
- March 25, 2024
- March 24, 2024
- March 23, 2024
- March 22, 2024
- March 21, 2024
Build a Twitter Clone From Scratch: The Design
This article represents the first in a new group effort by the Nettuts+ staff, which covers the process of designing and building a web app from scratch – in multiple languages! We’ll use a fictional Twitter-clone, called Ribbit, as the basis for this series.
In this tutorial, we need to focus on the UI. We’ll leverage the popular LESS Preprocessor to make our CSS as manageable as possible.
Introduction
Be sure to download the assets for this tutorial, if working along.
This tutorial is divided into five major parts, which explain how to style various pages of Ribbit’s layout. I will reference HTML elements using CSS selectors to make it easier to understand. But before diving into the layout, let’s briefly discuss nesting.
Nesting
In CSS, referencing a nested element can result in lengthy selectors. For example:
someId {/* ... */}someId div.someClass {/* ... */}someId div.someClass p.someOtherClass {/* ... */}someId div.someClass p.someOtherClass target {/* ... */}
And it can grow even bigger! With LESS, you can nest one element in another, making it easier to read:
someId {/* ... */ div.someClass { /* ... */ p.someOtherClass { /* ... */ target { /* ... */ } } }}
Variables and Mixins
Create a new file and name it, style.less
. When using any style preprocessor, it’s a good idea to store important colors and sizes within variables; you can easily adjust their values without searching the file, looking for property values that you need to change. We will use a handful of variables for the text color, border color, and content width:
@text-color: #3F3E3D;@border-color: #D2D2D2;@content-width: 860px;
Now, let’s create two mixins. The first will create the illusion of anti-aliased text, and the second will allow for cross-browser gradients. The former is rather simple:
.antialiased (@color) { color: @color; text-shadow: @color 0 0 1px;}
The trick is to create a shadow underneath the text with the same color and a one-pixel spread, making the browser display a nice shade around the text.
Now for the gradient; this is more complicated than the anti-aliased text because every browser implements gradients differently. Once we’ve compensated for the various vendor prefixes, here is the code:
.gradient4f (@p1, @c1, @p2, @c2, @p3, @c3, @p4, @c4) { background: @c1; background: -moz-linear-gradient(top, @c1 @p1, @c2 @p2, @c3 @p3, @c4 @p4); background: -webkit-gradient(linear, left top, left bottom, color-stop(@p1, @c1), color-stop(@p2, @c2), color-stop(@p3, @c3), color-stop(@p4, @c4)); background: -webkit-linear-gradient(top, @c1 @p1, @c2 @p2, @c3 @p3, @c4 @p4); background: -o-linear-gradient(top, @c1 @p1, @c2 @p2, @c3 @p3, @c4 @p4); background: -ms-linear-gradient(top, @c1 @p1, @c2 @p2, @c3 @p3, @c4 @p4); background: linear-gradient(to bottom, @c1 @p1, @c2 @p2, @c3 @p3, @c4 @p4);}
Every browser has a prefix: -moz-
for Firefox, -webkit-
for Chrome, etc. The last line uses the W3C recommended version for gradients. If a browser supports it, it will override the previous properties because it’s the last background
property declaration in the rule. The linear-gradient
function accepts eight parameters: four pairs of percent-color values. It creates the gradient with four color steps.
Global Styles
Let’s next style some global elements, such as for buttons and links. We want all elements to use the Helvetica
or Arial
fonts with the text color defined earlier:
* { font-family: sans-serif; color: @text-color;}
Body
The body is pretty easy; we need a white background with an image-based pattern. There are no margins and padding:
body { background: white url(gfx/bg.png); margin: 0; padding: 0;}
Inputs
We’ll also provide a default style for all <input/>
elements in the page:
input { width: 236px; height: 38px; border: 1px solid @border-color; padding: 0 10px; outline: none; font-size: 17px; &:focus { background: #FFFDF2; }}
We set the default size and padding, and we use the @border-color
variable to remove the annoying blue outline when the element is focused. You should notice another bit of LESS sugar: we can add CSS pseudo-classes (and normal classes too) using the &
character (parent reference), as shown here:
&:focus { background: #FFFDF2;}
This causes the input to have a light yellow background, when focused.
Submits
Submit buttons will use both the previously defined mixin and the border-radius
to create nice effect:
input[type="submit"] { height: 36px; border: 1px solid #7BC574; border-radius: 2px; color: white; font-size: 12px; font-weight: bold; padding: 0 20px; cursor: pointer; .gradient4f(0%, #8CD585, 23%, #82CD7A, 86%, #55AD4C, 100%, #4FA945);}
Links
The links should have a different color than normal text. We’ll also underline them on hover:
a { text-decoration: none; .antialiased(#58B84E); &:hover { text-decoration: underline; }}
Basic Template
We will begin with the portion of the layout that remains the same in every page. Here is the HTML code, which I will explain below:
<!DOCTYPE HTML><html><head> <link rel="stylesheet/less" href="style.less"> <script src="less.js"></script></head><body> <header> <div class="wrapper"> <img src="gfx/logo.png"> <span>Twitter Clone</span></p></div></header><div id="content"><div class="wrapper"></div></div><footer><div class="wrapper">Ribbit - A Twitter Clone Tutorial<img src="gfx/logo-nettuts.png"></div></footer></body></html>
We start with a normal doctype
definition and document head
. You can use the less.js
library and include the style.less
in the development stage (as I did in this code). Later, you can compile the LESS file into CSS, if you don’t wish to use less.js
. As you’ve probably noticed by now, the layout is divided into three parts: header
, #content
, and footer
. You should save this HTML to see if you are styling everything correctly.
Header
Let’s tackle the header
. It contains Ribbit’s logo and the two words: ‘Twitter Clone’. It’s wrapped in a wrapper, the width of which is controlled by the @content-width
variable. There are several wrappers in the layout, and all are @content-width
wide with auto
margin:
.wrapper { width: @content-width; margin: auto;}
The header itself is 85px
tall and page wide:
header {background: url(gfx/bg-header.png);height: 85px;width: 100%;}
After the width, add div.wrapper
‘s style with vertical padding:
div.wrapper { padding: 11px 0;}
So the header should look like:
header { background: url(gfx/bg-header.png); height: 85px; width: 100%; div.wrapper { padding: 11px 0; }
Images in the wrapper need to be 10px
lower, in order to be nicely centered:
img { position: relative; top: 10px; margin: 0 15px 0 0;}
Also, the font in <span/>
elements must be larger than the default size:
span { font-size: 18px; margin: 0 42px 0 0;}
Here’s how our design should look at this point.
Content
There’s not much we can do with #content
at this time. We’ll add some margin to the bottom and a minimum height; the layout will look funky if it’s not tall enough:
#content {margin-bottom: 15px;min-height: 560px;}
Inside, the wrapper needs to have some vertical margin with an automatic horizontal margin:
div.wrapper { margin: 38px auto;}
Footer
Like the header, the footer is the same for all pages. We’ll use a background image and a smaller font size. We’ll also need to clear: both
, because we’ll use floats in the content. Without clear
ing, the footer will not adjust in accordance with the content:
footer {background: url(gfx/bg-footer.png);height: 251px;font-size: 14px;clear: both;}
Let’s now add some padding to the wrapper, and images within it should float to the right:
div.wrapper { padding: 15px; img { float: right; }}
Here’s our footer:
The Home Page
This page displays for users not logged in to Ribbit. Therefore, it will need to present the login form in the header and a register form, with a big frog image in the content. Let’s start with a basic template.
Login Boxes
Add this login form to the div.wrapper
of the header
, after the <span/>
element:
<form> <input type="text"> <input type="password"></form>
These inputs are already styled, but we do need to add the margins and make the form display
as inline
. Append this after span
in div.wrapper
of header
:
form { display: inline; input { margin: 0 0 0 14px; }}
Register Form
Here is the HTML for the registration form:
<img src="gfx/frog.jpg"><div class="panel right"> <h1>New to Ribbit?</h1> <form> <input name="email" type="text"> <input name="password" type="text"> <input name="password2" type="password"> <input type="submit" value="Create Account"> </form></div>
Add this HTML within div.wrapper
of #content
. We want the image to have rounded corners and to be floated to the left (add this after margin in div.wrapper
of #content
):
img { border-radius: 6px; float: left;}
Now, we can style the registration form. It will also be a panel that we’ll use later; that’s why we will style the .panel
:
div.panel { border: 1px solid @border-color; background: white; margin: 0; margin-bottom: 29px; border-radius: 6px; font-size: 14px;}
For now, though, we will only style the right
panel. It’s narrower and sticks to the right side of the panel. Naturally, insert the following into div.panel
:
&.right { width: 303px; height: 313px; float: right;}
Also, we need to take care of the header and content of the panel. We use <h1/>
elements for the header and <p/>
elements for content. Notice that you can use the *
wildcard inside of another element:
* { margin: 6px 0;}form { padding: 0 23px;}h1 { border-bottom: 1px solid @border-color; margin: 5px 0; font-weight: normal; font-size: 18px; padding: 13px 23px; height: 23px;}p { padding: 0 24px; margin: 18px 0;}
Here is how div.panel
‘s style should look:
div.panel { border: 1px solid @border-color; background: white; margin: 0; margin-bottom: 29px; border-radius: 6px; font-size: 14px; &.right { width: 303px; height: 313px; float: right; } * {margin: 6px 0;}h1 {border-bottom: 1px solid @border-color;margin: 5px 0;font-weight: normal;font-size: 18px;padding: 13px 23px;height: 23px;}p {padding: 0 24px;margin: 18px 0;}}
And here is a screenshot of how this page should look, thus far (click to see full size):