grab and grabbing are two great CSS cursors you can use when you are moving things around.

Windows: Mac:

Since those are not standard, it is really tricky to get them working cross browser. This article is going to show you all the available workarounds to get the best version working everywhere.

Browsers

Firefox

The grab icons were first introduced in Firefox 1.5 (November 29, 2005).

.grab { cursor: -moz-grab; }
.grabbing { cursor: -moz-grabbing; }

Chrome & Safari on Mac & Linux

The cursor were then introduced on Webkit in March 2008 but only for Mac. The cursors are also working on Linux.

.grab { cursor: -webkit-grab; }
.grabbing { cursor: -webkit-grabbing; }

Chrome Windows

However, the icons don't work on Windows. This is even more vicious as the CSS rule is being parsed and accepted, but it doesn't change the cursor. Therefore you cannot do something like this:

.grab {
  cursor: move;
  cursor: -webkit-grab; /* NOT WORKING */
}

You have to do two distinct rules, one for Windows and one for Mac & Linux.

There is a way to get the cursor working using a custom cursor. Fortunately for us, Google already did the asset in Gmail and Google Maps.

.grab { cursor: url(https://mail.google.com/mail/images/2/openhand.cur) 8 8, move; }
.grabbing { cursor: url(https://mail.google.com/mail/images/2/closedhand.cur) 8 8, move; }

Internet Explorer 7, 8, 9

Now, as usual, Internet Explorer doesn't support everything. You cannot specify the relative position of the cursor. This is not the end of the world, we can just ignore it and the cursor will be slightly misaligned. Since the cursor isn't a pointer, this is not an issue in practice.

.grab { cursor: url(https://mail.google.com/mail/images/2/openhand.cur), move; }
.grabbing { cursor: url(https://mail.google.com/mail/images/2/closedhand.cur), move; }

I did not test IE6 nor IE10. If you know something about those please leave a comment 🙂

Opera

Opera doesn't have any support for custom cursors. So we can use the move cursor that is less than ideal but gives the idea of movement.

.grab, .grabbing { cursor: move; }

Conclusion

I made this jsFiddle to test all the different ways to show cursors. You can play with it to handle browsers I did not list here.

There is a viable solution for all the major browsers except Opera. Sadly, I couldn't find a way to do feature detection in order to see which version to use in which case. Also, because of the Webkit issue on Windows, you cannot make a simple sequence of rules and let the browser ignores the ones he doesn't know. You have to get a browser sniffing library and include the appropriate rule for the browser.

Some related articles that helped me come to the solution. Note that no one is successfully managing to get it working on all the browsers.

Layout Algorithms: Facebook | Google Plus | Lightbox | Lightbox Android | 500px

Google Plus has a really nice image gallery. They somehow managed to display all the photos without cropping, without reordering and without any holes. We are going to see how they did it in this blog post.

How does it work?

Here we have three images with various sizes and aspect ratio and we want to display them in the page. The layout algorithm is the consequence of one clever trick: all the images of the same row are have the same height.

So the only unknown is H, the height of the row given the three images we want to show. Using some basic math, we can solve the problem!

So now we know how to calculate H, the height of all the images. Since we want to keep aspect ratio, we can also calculate their width. As you can see, it is trivial to generalize the operation to n images.

How many images?

Now the tricky part is to decide how many images we want to put in the row. I came up with a solution that gives similar results to Google Plus but I'm not 100% sure that's how they do it.

One fact you can observe is the fact that few images leads to a huge row while many images lead to a small row.

So the idea is to try adding one image at a time and have a threshold for the maximum height we want. Once the row is smaller than this threshold, we render it!

Conclusion

Check this other article to find where best to place the breaks.
Check out the Demo! Here's a little Pro/Con to know if this technique will fit your needs.

Pros:

  • No cropping
  • No reordering
  • No holes
  • Arbitrary Width

Cons:

  • Portrait images are much smaller than landscape ones
  • All the rows do not have the same height
  • The view feels a bit chaotic with no clear structure
  • Requires dynamic resize of images

Some other implementations I found on the internet:

Layout Algorithms: Facebook | Google Plus | Lightbox | Lightbox Android | 500px

Lightbox.com has a really interesting image layout algorithm. We're going to see how it works and its best use case.

How does it work?

Column based

The algorithm is column based. You pick a number of columns at the beginning. Then every time you want to layout an image, you just place it to the smallest column.

Some facts about this layout: All the images here have the same width. The order is not particularly respected. The end of the stream is not properly aligned.

Bigger Images

The interesting part of Lightbox layout is the ability to make some images bigger. When you are about to layout an image, you look at the height of the neighbor columns. If the column has the same size, then you can to extend the image to take the width of both columns.

Beating the Odds

Having two adjacent columns with the exact same size is rarely going to happen in practice. In order to solve this situation we are going to cheat a little. We draw an invisible grid and every time an image doesn't perfectly align with the grid, we crop it to the nearest line.

The bigger the grid is, the more opportunity you will have to make bigger images but at the same time, the more you will crop your images.

When to make images bigger?

This might be counter intuitive but you don't want to make images bigger every time the opportunity present itself. Every time you make an image bigger, it is going to preserve two adjacent columns of the same height. If you keep adding bigger images on top of those two column, you essentially created a column that has twice the width.

Using a column based layout implies that landscape images are much smaller than the portrait ones. In order to restore balance, Lightbox uses the following heuristic. If the image is landscape, then it has 60% chance to be made bigger, only 10% when it is a portrait.

Conclusion

Check out the Demo!

This layout is very good for random collection of images in an infinite stream. Here's a little Pro/Con to know if this technique will fit your needs.

Pros:

  • Can make some images bigger
  • No holes
  • Need to store only two dimensions per image
  • Arbitrary number of columns

Cons:

  • Landscape images are much smaller than portrait ones
  • Images that can be bigger is very arbitrary
  • Small cropping
  • Order is not respected
  • End of stream is not well aligned

Some other implementations I found on the internet:

In this article, I'm going to guide you through a concrete problem I had to solve. Eventually, we'll see how to use percentage values in the background-position CSS property and how it solves a lot of tough issues.

Usual way

Positioning the image

The usual way to position images inside a container is to specify where the top left of the image is going to be compared to the top left of the container.

Both are really easy to implement in CSS. On the left you can see the code using a <img> tag inside a container, the other one is a container with a background-image attribute.

.container {
  position: relative;
}
 
.container img {
  position: absolute;
  top: 12px;
  left: 20px;
}
.container {
  background-position: 12px 20px;
}

Moving inside container

Now let's say you want to be able to drag the image inside the container and make sure it doesn't go outside of the bounds. You are going to start doing some basic maths to compute the maximum values for top and left.

The left position is going to go from 0 to container_width - image_width. Apply the same formula to calculate the maximum top position.

Image is bigger than container

So far so good. Now let's say our image is bigger than the container. We're going to extend our definition of inside the container. All the container must be filled with the image.

Again, using simple math we can compute the bounds of the left offset. Again, it is 0 and container_width - image_width. Except that container_width - image_width is negative this time.

You are now going to handle positive and negative values. The values now also get really counter intuitive. When you see 12px 20px you have a rough idea of how the image will be positioned. -12px -20px is really harder to conceptualize.

Invariants

So great, now you have written all your reposition interface and have stored the perfect position the user entered. Now, for some reason, instead of a rectangular container, you want a square one. You are in a not so comfortable position.

The values we computed can no longer be used because they no longer have the same meaning. The within bound constraint no longer holds true and the position is completely off. It is the same if you attempt to scale the image and container.

Background Image Percentage

Definition

Instead of using the previous definition of the image position, we can use another one. When the left border of the image is on the left border of the container, left is equal to 0%. When the right border of the image is on the right border of the container, left is equal to 100%.

Here's what the two values 0% and 100% looks like in the two examples:

To find the intermediate steps, we just do a linear interpolation between the two values.

left = (container_width - image_width) * percentage

Bound check

The first obvious advantage is that we no longer have to do math to compute if an image is within the bounds of the container. It is if and only if the value is between 0 and 100.

Invariants

Another way to think about this positioning is to draw two axis, one for the image on for the container. If we set the value to be 60%, then the position is going to be where the 60% mark on the two axis is the same point.

As you can see, this new definition works well with different ratios and scaling.

Horizontal and Vertical

If you pay close attention, you notice that if the image is the same size of the container, the value is ignored. The two axis will be perfectly aligned so all the points will match. Setting 30% or 80% doesn't matter.

If you don't believe the picture, just look at the math.

left = (container_width - image_width) * percentage
left = 0 * percentage
left = 0

The takeaway here is that you need the user to set two values. One when the image is going to scroll vertically. One when the image is going to scroll horizontally.

Conclusion

At first, I didn't understand how percentage values for the background position were working. I was really confused because this is not the way intuitively you would have used percentage.

However, once I tried to implement a robust reposition tool, I realized that this definition of percentage was convenient. It solves in an elegant way many of the issues we had with the usual positioning method.

I hope that you learned something here 🙂

There's one big issue when displaying images inline like smileys is to position them at the right height.

Using vertical-align pre-sets

Usually what you do is try to use vertical-align and test all the possibilities. Then you are frustrated because you can't find the one that is pixel perfect the height you want!

middle , bottom , sub , text-bottom , baseline , text-top , super , top

Using position: relative

What I used to do was to find the nearest vertical-align and then use position: relative; top: -1px; to slightly adjust the vertical position. This works very well, it doesn't change the line-height and sets the image at the exact position you want!

Using vertical-align: number

Until I re-read the documentation for vertical-align ... It appears that we can specify a number instead of the pre-sets! You can just do vertical-align: -2px; and it works. It is relative to the baseline pre-set.

-4px , -3px , -2px , -1px , baseline , 1px , 2px , 3px , 4px