AIM / MSN -
Phone -
Or you can use the contact form in the footer

Pure CSS KittenAuth

10th of June, 2010

See the KittenAuth Demo.

Now when I first heard about kittens being the future of spam-prevention, I laughed. But really, this is a good idea. Not necessarily kittens, but something that would appeal to your users.

A quick note; this is not a tutorial - as such. I'm not going to explain things, I'm merely showing how to make a KittenAuth, if you're still starting up with programming or web design - you will get very confused.

KittenAuth

Now I'm not going to dedicate this to say why I think this KittenAuth is a good idea. I acknowledge it's highly impractical, blah blah blah. This blog post is just dedicated to showing how to make a KittenAuth, but just using CSS (and I'll be doing the serverside using PHP). Currently the only existing KittenAuths that I can find are either JavaScript based, or running on a horrible CSS with tiny, fiddly, checkboxes everywhere.

You may also find it interesting to read all about how this KittenAuth idea was thought up.

Making the Front End

Now first things first, we need to get some HTML down. Let's just use one repetitive image to start off with.

<form class="kittenAuth" action="confirm.php?n=0" method="post">
<img src="kittenAuth.png" alt="kittenAuthCSS" class="title" />
<div class="cell">

<input type="checkbox" class="checkbox" name="0" id="00" /><label for="00"><img src="kittenImage.png" alt="Kitten Test" /></label>
<input type="checkbox" class="checkbox" name="0" id="00" /><label for="00"><img src="kittenImage.png" alt="Kitten Test" /></label>
<input type="checkbox" class="checkbox" name="0" id="00" /><label for="00"><img src="kittenImage.png" alt="Kitten Test" /></label>

<input type="checkbox" class="checkbox" name="0" id="00" /><label for="00"><img src="kittenImage.png" alt="Kitten Test" /></label>
<input type="checkbox" class="checkbox" name="0" id="00" /><label for="00"><img src="kittenImage.png" alt="Kitten Test" /></label>
<input type="checkbox" class="checkbox" name="0" id="00" /><label for="00"><img src="kittenImage.png" alt="Kitten Test" /></label>

<input type="checkbox" class="checkbox" name="0" id="00" /><label for="00"><img src="kittenImage.png" alt="Kitten Test" /></label>
<input type="checkbox" class="checkbox" name="0" id="00" /><label for="00"><img src="kittenImage.png" alt="Kitten Test" /></label>
<input type="checkbox" class="checkbox" name="0" id="00" /><label for="00"><img src="kittenImage.png" alt="Kitten Test" /></label>

</div>
</form>

Make sure that the input and the label are on the same line; the CSS depends on that a bit later on.

Speaking of which, here's the CSS we're gonna use.

.kittenAuth{
width: 379px;
margin: auto;
}
.kittenAuth .title{
display: block;
margin: 15px auto 5px;
}
.kittenAuth .cell{
float: left;
margin: 0 1px -2px;
}
.kittenAuth .checkbox{
position: absolute;
width: 120px;
height: 30px;
margin: 95px 0 0 0;
opacity: 0;
z-index: 1;
}
.kittenAuth label img{
margin: 1px;
border: 1px solid #ccc;
opacity: 0.5;
width: 120px;
height: 120px;
}
.kittenAuth label img:hover{
opacity: 0.9;
border-color: #666;
}
.kittenAuth .checkbox:checked+label img{
opacity: 1;
margin: 0;
border: 2px solid #333;
}

I'll quickly go through the idea of this code with you.

Each checkbox has a label associated with it, each label wrapping around an image. Because of the label's association with the checkbox, when you click the image, the checkbox is clicked too.

IE doesn't support this label feature with images, for whatever reason, so the code uses the opacity style, so that IE won't make the checkbox invisible. I've moved about the checkbox so it has some padding for click-area and made it as sightly as possible, but unfortunately IE-users lose out here.

KittenAuth in IE

Then (this is the last selector) we use CSS3's adjacent sibling combinator to do something very sneaky. We make the image and the checkbox turn to an opacity of 100%, only when the tickbox is checked.

And the rest is basic CSS that I won't bother going over.

Now all we need is some server-side to get the images, randomize their order, and check that the correct images were selected.

Making the Back End

Now I'm gonna be doing this with PHP, but the general algorithms should cross over to ASP or whatever.

First of all, you need to have a lot of images. I only used 18 different images (6 kittens, 12 others) - but in reality you should be looking at a lot more. You can find a load of images on flickr's Creative Commons section (means that you can use for free as long as you give credit).

The images this script calls upon are Kitten1.png, Kitten2.png etc. and Other1.png, Other2.png etc.

Now here's the PHP I'm going to use that creates all of the KittenAuths.

<?php

session_start();
session_destroy();
session_start();
$_SESSION['n'] = 0;
function kittenAuth(){
$_SESSION['answers'][$_SESSION['n']] = "";
$imageArray = Array();
for($i = 0; $i < 9; $i++){
if($i < 3){
$ran = 0;
$taken = true;
while($ran == 0 || $taken == true){
$ran = rand(1, 6);
$taken = false;
for($j = 0; $j < count($imageArray); $j++){
$number = explode("Kitten", $imageArray[$j]);
if((int)$number[1] == $ran){
$taken = true;
}
}
}
$imageArray[$i] = "Kitten".$ran;
}
else{
$ran = 0;
$taken = true;
while($ran == 0 || $taken == true){
$ran = rand(1, 12);
$taken = false;
for($j = 3; $j < count($imageArray); $j++){
$number = explode("Other", $imageArray[$j]);
if((int)$number[1] == $ran){
$taken = true;
}
}
}
$imageArray[$i] = "Other".$ran;
}
}

echo '<form class="kittenAuth" action="confirm.php?n='.$_SESSION['n'].'" method="post">';
echo '<img src="kittenAuth.png" alt="kittenAuthCSS" class="title" />';

$takenValues = Array();
for($i = 0; $i < 9; $i++){
$ran = -1;
$taken = true;
while($ran == -1 || $taken == true){
$ran = rand(0, 8);
$taken = false;
for($j = 0; $j < count($takenValues); $j++){
if($takenValues[$j] == $ran){
$taken = true;
}
}
}
if($imageArray[$ran][0] == 'K'){
$_SESSION['answers'][$_SESSION['n']] .= $i;
}
$_SESSION['pictures'][$_SESSION['n']][$i] = $imageArray[$ran];
$takenValues[$i] = $ran;

echo '<div class="cell">';

echo '<input type="checkbox" class="checkbox" name="'.$i.'" id="'.$_SESSION['n'].$i.'" /><label for="'.$_SESSION['n'].$i.'"><img src="kittenImage.php?n='.$_SESSION['n'].'&i='.$i.'" alt="Kitten Test" /></label>';

echo '</div>';
}
echo '<input type="submit" class="submit" value="See? Human." />';
echo '</form>';

$_SESSION['n']++;
}

?>

I'll explain what the script's doing systematically.

First of all any previous session is destroyed, before a new one is started.

When the function is called, it initializes the nth answer (for if there are more than one KittenAuths on any page) as a session variable. This will store which pictures are the correct ones.

The for loop runs through and firstly ($i < 3) creates 3 exclusive and random cat images, and then 6 random and exclusive other images.

Then the second for loop runs, using the same exclusive/random algorithm to put the 9 images stored in imageArray out in a random order. At the same time, it also sets the nth answer for the KittenAuth. Line #59 checks to see if the image's first letter is a 'K', meaning it's a picture of a Kitten. If picture number 1, 5 and 7 were cats, the session data would be "157".

Importantly, within the second for loop, you'll notice that the script is creating a 2D array within $_SESSION['pictures'], for the nth KittenAuth and picture number i.

This is used to create the image. HTML calls upon this image URL: "kittenImage.php?n=0&i=0" (whereas n and i refer to the nth KittenAuth and image number i.

This kittenImage.php has the following source code.

<?php

session_start();
header('Content-type: image/png');
$image = imagecreatefrompng("Images/".$_SESSION['pictures'][$_GET['n']][$_GET['i']].".png");
imagepng($image);

?>

$_SESSION['pictures'][$_GET['n']][$_GET['i']] will return a value such as Kitten5 or Other7.

Now we just need to check whether what they select is right or not when they submit, and we're finished!

The form created earlier links to a confirm.php?n=n, and the source code is as follows.

<?php

session_start();
$answer = "";
for($i = 0; $i < 9; $i++){
if(isset($_POST[$i])){
$answer .= $i;
}
}
$human = false;
if(isset($_SESSION['answers'][$_GET['n']])){
if($answer == $_SESSION['answers'][$_GET['n']]){
$human = true;
include("passed.php");
}
}
if($human == false){
include("failed.php");
}
session_destroy();

?>

Quite simply, this just checks the answer stored in the session, to the answer created when the user submitted the form.

And that's it! We have KittenAuth.

If you feel any of this was explained poorly or my coding is inefficient or deprecated in any way - please leave a comment or send me an email. Of course, if you fancy leaving some praise - that's up to you too.


Comments

There are no comments yet, go on - make one.


Display Name*: Email (no spam)*: Website: Comment*: