Archive for 'Code'

HTML5, GeoLocation and Google

UPDATED ZIP file of code now available here

It seems now it’s all the rage to start leveraging some of the new HTML5 spec in your designs, especially now that Firefox 3.5, Chrome & Safari on iPhone 3.0 all support many of the new HTML5 tags.

I started looking into geolocation for html5 recently and most of the blog posts I found relate to grabbing the geolocation from the browser (which by the way is the Lat & Long) and popping it on the map using Google or Yahoo maps.

However the reality is that while that’s a nice novel factor, you often don’t want a Lat & Long, what you want is an area, where is this user & what features are around them. I personally wanted to avoid Google Maps like the plague as their terms & conditions are nasty when it comes to corporate applications.

So, I set about trying to find out how I could get the area where a user is, without using Google Maps API. And it turns out it’s not really all that hard. Due to privacy reasons I can’t show you the actual application where this is used, but I can give you some code and a sample file and you can knock yourself out having fun with it. Here is a demo of what we are doing

The idea:
1) Does the users browser support geolocation?
2) Get the geoocation using either navigator.geolocation.getCurrentPosition or navigator.geolocation.watchPosition
3) Hand the Lat & Long off to a reverse geocode service
4) Get a result back allowing me to say “You are in: (area)”

The code
Is really pretty straight forward for the geolocation data from the browser. First up, check to see if the browser supports geolocation and if so then ask the browser for the geolocation.

  if (navigator.geolocation) {
            // YES
            // snapshot of position:
            navigator.geolocation.getCurrentPosition(renderPosition, renderError);
            // updating position:
            // navigator.geolocation.watchPosition(renderPosition, renderError);
        } else {
            // NO
            // PUT ERROR MESSAGE HERE - OR SIMPLY DO NOTHING
        }
    }

You’ll see above that I have both getCurrentPosition and watchPosition, just uncomment the one you want. Note that watchPosition will continually poll the browser for updated positions, if you are feeding that to Google Maps, apparently it’s against their API rules to send continually updating data (you need to check that for yourself).

At this point we now have a Lat & Long (note: you also get other data around accuracy, altitude etc – more on that in the attached zip file) … but I don’t want that, I want an area. So I use a WebService from GeoNames to do the reverse lookup. This is a nice service that can return XML or JSON for you, and includes a bunch of details about that area.

Now this is where I got caught out. I use JQuery to handle my javascript to get the updated location and write it to my page. Of course javascript is limited in what it can do from one domain to another. A lovely friend of mine kindly pointed out that I’d need some form of proxy to grab the data. He threw together a very simple PHP one for me. So at this point you need jQuery and PHP to get the data.

The PHP file looks like – note you could use readfile(), however many servers have that disabled so I wrote up a simple function using CURL to get the file:

< ?php if ($_GET['type'] == 'JSON') {
    CURL_file_get_contents('http://ws.geonames.org/findNearbyPlaceNameJSON?lat='.$_GET['lat'].'&lng='.$_GET['lon']);
} 

if ($_GET['type'] == 'XML') {
    CURL_file_get_contents('http://ws.geonames.org/findNearbyPlaceName?lat='.$_GET['lat'].'&lng='.$_GET['lon']);
}

function CURL_file_get_contents( $url ) {
           $open_file = curl_init();
             // Tell CURL the url to open.
           curl_setopt($open_file, CURLOPT_URL, $url);
            // Tell CURL to return the file in a string.
           curl_setopt($open_file, CURLOPT_RETURNTRANSFER, 1);
            // Execute the CURL command and retrun the page in a string.
          $return_var = curl_exec($open_file);
          curl_close($open_file);
           echo $return_var;
} ?>

Right, so now we just need some javascript to tie it all together (oh it’s assumed you have jQuery installed).

//jQuery to render the output
        function renderPosition(position) {

            if (!window.count) window.count = 0;
            var urlJSON ='';

            count ++;
            // var urlJSON = 'http://ws.geonames.org/findNearbyPlaceNameJSON?lat=-36.9104718&lng=174.9133483';
            var urlJSON = 'geonames.php?type=JSON&lat='+position.coords.latitude+'&lon='+position.coords.longitude;
            var urlXML  = 'geonames.php?type=XML&lat=' +position.coords.latitude+'&lon='+position.coords.longitude;

            $('#d').html( 'Latitude: ' + position.coords.latitude + '
'+ 'Longitude: ' + position.coords.longitude + '
' + 'Accuracy: ' + position.coords.accuracy + '
'+ 'Update number: ' + count + '
'+ 'Altitude: ' + position.coords.altitude + '
'+ 'Altitude accuracy: ' + position.coords.altitudeAccuracy + '
'+ 'Heading: ' + position.coords.heading + '
'+ 'Speed:' + position.coords.speed + '
');
            // now get the XML reverse geo data
            $.getJSON(urlJSON, function(json) {

                /* Parse JSON objects */
                $.each(json.geonames,function(i,item) {
                    // get the name - which is the suburb - and update the page
                    // alert('name: ' + item.name);
                    $('#jsonResults').html(You live in: ' + item.name );
                });

            });
        }

        function renderError() {
            $('#georesults').html('Err houston, we have a problem');
        }

This is kind of overkill, but it shows you all the data we get back from the geolocation service within HTML5.

Now you have results, and as you can see from $(‘#jsonResults’).html(‘You live in: ‘ + item.name); I simply output the name element from the JSON back to the html.

From here you can do anything with this, write it into a form, mimic iPhone apps etc. I’ve not tried this, but someone did tell me that this also works fine on Android.

Download the zip file and have a look or take a look at the demo (note you need Firefox 3.5, mobile Safari etc)

Links

To video or not to video?

I only need to cast my mind back a day or two in my web history to remember the web the way it was. Way back in 1994 I tried to start a music sharing website for christian music – I even had some chats with big radio stations and such like, but the web then just wasn’t ready.

Fast forward a few years (dang is it 15 years already?) and the web is a fundamentally different place and rich media has taken on a whole new meaning.

Tonight on Twitter @CoryOBrien sent out the following tweet

Safari 4 has a video intro similar to the vid that plays the first time you start your Mac. Slick! http://www.apple.com/safari/welcome/

I took a look (I’ll pause here while those of you with Safari 4 go take a look at the link) and thought, you know what … yeah that’s kinda neat. But then it dawned on me, something wasn’t quite right. So I took a look a little deeper and discovered it wasn’t actually a video, but rather a collection of media and some very subtle use of HTML5 and CSS3.

But more on that in a second.

Cory then pointed me to an article he’d written about the Honda Let it Shine commercial on Vimeo – which if you haven’t seen you should take a look on Vimeo, go on, we’ll wait for you ….. Cory talks in his post about how this advert breaks past the traditional video box to take ownership of the screen to really draw the user in.

We started to wonder how they both did what they did, which might be better and which if either we might use.

So how do they do it?

I was simply going to explore the Safari 4 ‘video’ when Cory suggested a comparison of the two techniques. Now I’d love to spend a few hours and dig around work up some examples etc – but someone else can do that, I’m sure someone really smart will write some tutorial about it too. Me however, I was up at 4:30am this morning getting ready for a code release, so my brain blurry good it not is ….. STILL, I did want to explore a couple of things.

Fundamentally these two systems use the same idea – that is Javascript to place content into an existing or create a new HTML element. What they do with those elements is … well where the magic lies.

Vimeo & Honda

Lets look at this one first as it’s shall we say, the more traditional of the two. The way they do this is they’ve allowed the Vimeo player to have an extra attribute set ‘allowScriptAccess’ is set to always. This means their video controller can now interact with javascript on the page.

So when you click the PLAY button, it doesn’t play, instead it triggers a bit of Javascript that writes into the page a new SWF object that overlays the entire page. This object is full screen, set to scale and has some trickery around it to make sure it lines up with the Vimeo page.

Combine the line up magic and some nice masking and transparency in the now full screen (not full page, if you scroll down far enough the effect falls off), and theillusion to be complete – making it look like a set of different movies working together when it’s simply one overlay. For example, the “avatar” icon suddenly is a live and is an animated version of the insight logo – the background is alive with animation. No not really it’s one big animation, it’s just got some masking and animated transparency allowing you to see (or not) the HTML underneath.

Safari 4

This one is a little more clever, and only works in Safari 4. Actually technically speaking it probably could work in Firefox 3.5 and Opera – although you’d have to hack the JS a little. There is nothing (that I can see) that is particularly Safari 4 specific.

Okay in a nut shell this is what they do: They have an HTML page, with some empty DIV tags, they have an AUDIO tag and a VIDEO tag – these are both new to HTML5. The audio is an MP4 that plays in the browser and the video is an MOV that again just plays in the browser – no plugins needed and controllable by CSS. The Audio is hidden by the CSS – yet the audio still plays. The video (a small compass with the needle moving) has no controllers either.

There is some Javascript that sets up a CANPLAY function – used to test if this is Safari 4 a WELCOME MESSAGE function and a couple of other things – nothing overly complex really – other than some language stuff. Without looking too deep the WELCOME MESSAGE bit swaps out the welcome image based on your language settings.

The CSS is where it gets a bit funky. They make use of some really nice CSS features and this really shows the POWER of where the web is going. The ENTIRE animation is done with CSS, including the timings. Okay that’s a lie, the JS does a little of the timing work to ensure it’s all loaded before starting (I think) .. but CSS pretty much does it all.

Sure they have chosen to use CSS such as -webkit-animation-delay, however according to the working draft for CSS3 the code animation-delay css tag should work. (When I’m awake – I’ll work up some examples)

Which would I use

Right now I’d have to say – reluctantly – that if I was building something like this I’d have to go down the JS & Flash path. Why? Well because we have a lovely browser called IE – the bane of the interwebs. We have older browsers etc etc. However having said that, I’m currently building a site and am using CSS3 in it a fair bit.

Which is better?

The Safari 4 model is better! It’s faster to load, faster to build, it requires NO plugins, it’s … elegant. It’s great from an SEO point of view, your content is still your content in an open HTML format, your JS is light and your CSS simply does what CSS does – tells the browser put this here, now, do this with it.

Digging Deeper

Want to take a look for yourself try these:

With this collection of media – just jpg’s png’s and mp4’s Apple web designers have built what looks to be a high end video with HTML5 and CSS3

Summary

The reality is most of this is nothing new, and yet at the same time it’s brand new. Finally with HTML5 and CSS3 we have browsers that remove the barrier to content, we don’t need to rely on 3rd party stuff. Flash can do what it was designed to do – be a PART of the web, not be the web. Although having just said that – is this the beginning of the end for Flash & Silverlight? I mean why would you even bother to invest time and effort into developing these rich media “videos” when a few isolated elements whipped up in Photoshop with some CSS can do such rich content? Ajax, HTML5 & CSS3 could well spell the END of an area for Flash & Silverlight

My last comment – I love that CSS & the browser can do all the animation, timing and scaling not just of graphics but also of Video. If you haven’t seen it yet, grab Firefox 3.5 Preview and check this link out demoing some of the video ability in Firefox 3.5. This will show you some of the true power of a REAL modern web browser.

Links

» Safari 4 welcome ‘video’ – only works in Safari 4
» Honda Insight advert on Vimeo – click play to see effect
» Cory O’Brien looks at the future of adverts
» Download the latest Safari 4
» Download the latest Firefox 3.5 beta

Mr K’s guide to CakePHP

I’m playing around a bit with CakePHP again today … and for some reason a few things REALLY did my head in. Like REALLY did my head in.

Stupid really because about a year ago I built a full web solution in CakePHP with users, ACL, AUTH and all that stuff – and deployed it. I guess that’s what you get however when you don’t develop full time. You loose things.

So, I started again – grabbing a copy of CakePHP and hitting the ground. Here’s a few things I found useful along the way:

  1. forget all about ACL, ACO and ARO’s – you don’t need them for user login/logout
  2. run through cakePHP’s own auth tutorial (all you need is a User table in your database)
  3. the AUTH component will do all your heavy lifting (eg: password encryption etc) provided you have a USERNAME and PASSWORD – if however you instead use EMAIL and PASSWORD, it wont. And it won’t give you any decent errors. It’ll just do your head in!! However you can quickly and easily fix this – read how to quickly change the AUTH variables here – just tell AUTH that it’s username is actually your email address field.
  4. HTML helpers are fantastic, quickly rename a default form label using:
    echo $form->input(‘database.feild_name’, array(‘label’=>’Use_this_name’));

I’ve also seen 101 different ways to output the data for the currently logged in user. From completely re-writing your Controllers and Models to … well all sorts of complicated ways. But I’m telling you there is a FAR simpler way to do it.

AUTH – it’s your friend, it really is!

Check if a user is logged in from your layout file:
- if( $session->check(‘Auth.User.id’) )

Write our a user name from your layout file:
- $session->read(‘Auth.User.username’)

Or if you are in your Controller simply use:
- $this->Auth->user(‘id’)
-
$this->Auth->user(‘username’) // insert what ever database field you want

I knew there had to be simple ways to do this stuff!! Do you have any really simple, handy CakePHP tips you want to share?

A change in development

It seems, or would seem that there is a shift beginning in the development community. How many times have you read a story about some great online success where the actual idea has come from someone with almost no coding experience or ability? These people are often forced to find someone else to create the solution for them, costing a reasonable chunk of money up front.

Frameworks have increasingly lowered that entry barrier to full application (web suite) development. You can now grab a framework that does a huge chunk of the work for you. We’ve all seen the videos for building a blog in 10 minutes but even these require some development. If you want to build something reasonably full on you need to know code, or at the very minimum learn some HTML and CSS.

Now a new change is coming, introduced today at “Future of Web Apps” in Miami, 280Atlas is a solution that allows you rapidly develop web based applications with little to no code required. It’s a beautifully designed system that runs on top of the Cappuccino web framework, and gives you point and click control over many aspects of your application development.

Sure, if you want to get technical and start doing more complex things, then yes, you are still going to need code, but for those who want to throw together a proof of concept, build and interface that consumes existing data, create mash-ups, etc, 280Atlas is going to take the entry barrier and smash it to bits.

Take a look at the video below that shows 208Atlas in use. The video walks through the basic premise and then in the last handful of minutes, builds a fully functioning RSS reader – requiring 0 code, 0 html and 0 css skills.

I am going to guess that 280Atlas will allow you (if not at launch then very soon after) to render your project as an Adobe Air (or Mozilla Prisim or …) type desktop application, heck possibly even as a Web Snippet for pseudo iPhone and Android applications.

The next step in the future of web interface, web application development is just around the corner, and I think it’s called 280Atlas – note, it’s still hidden, not in beta yet!

Update to Studiowhiz.com

Well, it looks like Wordpress 2.5 is out. I’ll be looking to update around here, however I have to make sure things keep working ey! This is why there haven’t been too many posts just now. Trying to sort out a few things before we look to update.

Microformats – Simple data formats for the masses

Microformats – Simple data formats for the masses

A Short Primer

You have probably already heard about Microformats. You’ve probably also wondered what they are. So let me tell you: microformats are set a of predefined attributes that you add to already existing markup. These enable both humans and machines to easily access the data they hold. Simply, they are small semantic tweaks to your web pages’ HTML/XHTML that make available previously inaccessible information. This information can include:

  • Contact information
  • Event information
  • Resume information
  • Reviews
  • Bookmark information
  • Syndication information

The beauty of microformats is that they don’t affect how your web pages are rendered by a browser. They are built upon already existing standards that you know and love: the class, rel and rev attributes.

An Example

This is a review taken from a popular online store.

A review from a popular online store

This is how the code looks:

<div>
<p>
 <img src="/img/5-stars.png" alt="5 star rating"/>

 <strong>this book will change your life</strong>, January 3, 2007</p>
 <p>Reviewer: <a href="some-link">kristen</a> (San Francisco, CA) -
 <a href="some-link">See all my reviews</a>

</p>
<p>If you build web sites (or intend to have one built for you), and you
 haven't read this book, then stop what you are doing and buy it. When
 it arrives, stop what you are doing, and read it. It is awesome, and
 funny (believe it or not) and you will be happy that you did.</p>
</div>

Now consider the following code:

<div class="hreview">
 <p>

  <img src="/img/5-stars.png" alt="5 star rating" title="5" class="rating" />
  <strong class="summary">this book will change your life</strong>,
  <abbr class="dtreviewed" title="20070103T1146">January 3, 2007</abbr></p>

  <p>Reviewer: <a href="some-link" class="reviewer vcard"><span class="fn">kristen</span></a> (San Francisco, CA) -
  <a href="some-link">See all my reviews</a>

 </p>
 <span class="type" style="dispay:none;">product</span>
 <div class="item" style="display: none;">

  <a class="fn url" href="http://www.amazon.com/Designing-Web-Standards-Jeffrey-Zeldman/dp/0321385551/sr=8-1/qid=1171921047/ref=pd_bbs_sr_1/102-6812629-1991344?ie=UTF8&s=books">Designing with Webstandards</a>
 </div>
 <p class="description">If you build web sites (or intend to have one built for you),
 and you haven't read this book, then stop what you are doing and buy it. When it
 arrives, stop what you are doing, and read it. It is awesome, and funny (believe
 it or not) and you will be happy that you did.</p>

</div>

Not that much different – just a few extra class attributes (in bold) and a new HTML element here and there. That is what makes Microformats so cool – you don’t need to learn any new markup language – it’s all the same stuff you work with everyday!

How is this useful?

Having all this information available in a format that can easily be gathered is the key to Microformats. Not only can third party web applications easily access this data, but desktop applications can store it, too.

Imagine a world where you visit a web site and your browser tells you there is contact/event information available on that page and gives you the option to save the date to your address book/calendar application of choice.

Imagine a world with search engines that return resumes, reviews or events from millions of web sites. All this can be achieved by using Microformats.

Currently there are few smart applications and bits of software that can deal with Microformats:

  • Finda
    All finda’s company listings are marked up using hCard so you can simply save their vCard to your address book
  • .Mac Webmail
    The .Mac Webmail service now supports hcard
  • Microformats Extensions for Dreamweaver
    Dreamweaver Microformats Extensions (download) support authoring hCard, hCalendar, XFN, rel-tag, rel-license
  • Firefox Extensions
    Operator – provides an architecture for Microformat parsing which is likely to be incorporated into the core of future versions of Firefox.

    Tails – The Tails Firefox Extension allows you to view microformats embedded on a web page, and perform customizable actions on the microformats via Tails Scripts.

  • hKit Microformats Toolkit for PHP5
    A php toolkit to extract common Microformats from a page
  • Technorati Contacts Feed Service
    Technorati Contacts Feed Service is a deployment of X2V to convert hCards to vCard (.vcf) format.
  • Technorati Events Feed Service
    Technorati Events Feed Service is a deployment of X2V to convert hCalendar events to iCalendar (.ics) format.
  • Technorati Microformats Search
    Technorati Microformats Search. Search for contacts (hCard), events (hCalendar), or reviews (hReview) published on blogs and other web sites.
  • Upcoming.org
    Upcoming.org – hCalendar support in events listings and individual events.
  • X2V

    Brian Suda has created several XSLT files to extract microformats from HTML. From that the X2V webservice/favelet emerged. The XSLT and favelet extracts hCard and to produces .vcf (vCard) files and hCalendar to produce .ics (iCal) files.

  • Yahoo Local
    Yahoo local supports hCard, hCalendar, and hReview.
  • Yahoo UK Movies

    Yahoo! UK Movies supports hReview.

Conclusion

We – as web professionals – need to start using Microformats in everything we build. The more we use Microformats in our web development the quicker it’ll be embraced by software houses and application developers. We need to bring Microformats to the mainstream and it’s only you and I that can help that push!

Don’t be scared of Microformats. Embrace them, use them, love them. It’s easy!

Studiowhiz Hosting: support us a we grow

Hey there all you web nutters … I’m thinking of a little side project. And I need your input. If studiowhiz was to offer hosting to you would you consider using it? I’m not just talking any old hosting, I’m talking about hosting that will allow you the ability to run PHP beside ASP, Perl beside Ruby on Rails, MySQL beside MSSQL, pop, imap and all that usual stuff.

We could give you the flexibility you need to build the web apps you always dreamed of. Our hosting would scale as you do, with a small monthly fee & pay as you go add ons, hosting with us could be the advantage you need. You’d have access to 24hr, 7day support, your site would be hosted on some of the most robust systems on the net today.

Would you be interested? I’m considering this as one way of helping grow Studiowhiz. As this site continues to grow and I have more ideas we need community support to do that. By offering a service we all need & use we can get fantastic hosting and any profit will be funneled directly into the growth of Studiowhiz from a blog to a resource.

I would need 10 people/websites to consider driving this forward (that would see you paying around US$13 a month). Oh and yes this would potentially be a ‘cloud computing’ solution (don’t ya love buzz words). Register your interest by either a comment or drop me an email webmaster [at] studiowhiz (you figure the rest out).

Edit:
I now have 2 or 3 other parties interested so need maybe 7 more to register interest

PHP tricks

phpAs you know from my earlier post I’m rusty when it comes to coding. We’ll I’m working on a small project in my weekends (which I’ll unveil in the coming weeks) and while it’s more a proof of concept than anything, I’m using it as an excuse to brush up on my PHP skills – not that I program any more in my day job (yeah!)

So I thought I’d share a couple of things I’ve found – that are probably old news to you.

Remove duplicates from an array: a really, really simple thing to do.
Lets say I have an array $myArray = (‘Red’, ‘Green’, ‘Red’, ‘Blue’, ‘Red’, ‘Yellow’) and I didn’t want Red in there 3 times. PHP has a nice little function called array_unique().

$myArray = ('Red', 'Green', 'Red', 'Blue', 'Red', 'Yellow');

$clean_array =  array_unique($myArray);

$clean_array now looks like (‘Red’, ‘Green’, ‘Blue’, ‘Yellow’)

Insert multiple rows with one insert:
I always thought you couldn’t add multiple rows to a table with an insert. I thought each insert had to be a single row. Well this is not so as I found out today. Lets say you are adding 10 items to a ‘tag’ table (id, tag) you can do it like this:

INSERT INTO 'tag' (tag) VALUES
    ('apple'), ('windows'),
    ('css'), ('seo'), ('html'),
    ('design'), ('xhtml'), ('lcd'),
    ('usb'), ('crt');

Of course you can dynamically build the values and pop in many rows at once. I saw a post on some blog (I’ve misplaced the link since getting this to work) that mentioned adding many megabytes of data this way – I can’t say I’d recommend that as a particularly efficient way – but for adding maybe 50 tags it’s jolly fast.

On a side note, anyone know of anyway to get mysql to return the id’s for the rows it just added. If you are adding one row at a time of course you can use ‘mysql_insert_id()’ to return the id (only if you are using auto_increment) of the previous insert. Sadly this doesn’t seem to work with multirow inserts.

Rusty coding

I used to develop full time, that is designing, programming and full development of websites. Of course that was a number of years ago, and my job now, while requiring a growing understanding of the internet, doesn’t see me coding or developing.

I’m starting to set up a small website in the evenings, just a small site I hope people will enjoy. It should be the sort of site I can set up and only touch once in a while. [...]

A better WordPress

I enjoy using WordPress, after all it is pretty much the leading blogging software. It’s been through a number of iterations and has become a pretty solid hunk of code.

However I have to say, WordPress upgrading sucks. I’ve just completed an upgrade here and seriously, all that downloading, unzipping, making sure I backup my files and then uploading the new ones. Bah humbug. Why can’t WordPress have an auto-upgrade feature?

How hard is it to have the system know what files have changed, and simply update those directly? Sure allow the admin of the site to verify file updates (in-case they have hacked the odd file).

Maybe I should just switch to a hosted version of WordPress ey?

Mix ‘n’ Match

Mix ‘n’ Match

phpGetting a website or online business online is not the easiest thing, especially if you are not a coder. Trying to find out what CSS, PHP, ASP and other terms mean is hard. When you start looking at off the shelf products it makes it even more confusing.

I’ve just been asked by a friend if he can mix and match a PHP application with a website built in ASP. Strangely enough he’s the 3rd person to ask me this in the last 7 days.

Well the short answer is no, you can not install a PHP application on an ASP server. (By the way, ASP & PHP are coding languages used to build web applications – look for more explanation in my upcoming Jargon Explained series). Trying to install PHP on an ASP server is like trying to put Diesel in your Petrol car.

[...]

DIY CSS Framework

CSS (cascading style sheets) is the defining layer of any website. For those not sure, CSS is the bit of a web page, that tells your browser where to put things when rendering (drawing) the page.

CSS is fantastic, however with little to no Web Standards between the many browsers out there you can’t be gauranteed that your layout will always work. For example a default paragraph may have more margins in one browser over another.

Darren Wood is someone I would class as a CSS Guru (although I’m not sure he’ll agree with that). He spoke at the recent BarCamp mini-conference in Auckland specifically about this. He has some tricks to help level the playing feild & he’s kind enough to share his files for you to learn and work from.

I’m adding this to my ‘Must Recommend’ list for any web designer, hobbiest or professional. Starting your web project with Darrens framework, means you will have less headaches.

» Darren Wood » DIY CSS Frameworks

Tags: , , , ,