Skip to content

Instantly share code, notes, and snippets.

@maxbeatty
Created September 17, 2014 19:00
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save maxbeatty/f5ad269ff8a920e3ad24 to your computer and use it in GitHub Desktop.
Save maxbeatty/f5ad269ff8a920e3ad24 to your computer and use it in GitHub Desktop.
Adventures in Spriting
$lowRes: sprite-map("chat/sprites/*.png")
$highRes: sprite-map("chat/sprites-retina/*.png")
.chat-icon
margin-right: 10px
cursor: pointer
+sprite-background(message-icon, $lowRes, $highRes)
&:hover
+sprite-background(message-icon_hover, $lowRes, $highRes)
&.unread
+sprite-background(unread-message-icon, $lowRes, $highRes)
&:hover
+sprite-background(unread-message-icon_hover, $lowRes, $highRes)
&:active,
&.active
+sprite-background(unread-message-icon_active, $lowRes, $highRes)
tr
&.chat-open
outline: 1px $pale-blue solid
.chat-icon
+sprite-background(unread-message-icon, $lowRes, $highRes)
// Sprites with retina support
// needs to be passed in the name of the class you are trying to grab
// and the sprite location for low and retina res
=sprite-background($name, $sprites, $sprites-retina)
background-repeat: no-repeat
display: block
height: image-height(sprite-file($sprites, $name))
width: image-width(sprite-file($sprites, $name))
$width: image-width(sprite-file($sprites, $name))
// LOW RES
// Media Query: wrapping both sprite maps in a query ensures only one is downloaded
@media only screen and ( -webkit-max-device-pixel-ratio: 1), only screen and ( max__moz-device-pixel-ratio: 1), only screen and ( -o-max-device-pixel-ratio: 1/1), only screen and ( max-device-pixel-ratio: 1), only screen and ( max-resolution: 96dpi), only screen and ( max-resolution: 1.0dppx)
background-image: sprite-url($sprites)
background-position: sprite-position($sprites, $name)
// RETINA RES
// Media Query: wrapping both sprite maps in a query ensures only one is downloaded
@media only screen and ( -webkit-min-device-pixel-ratio: 1.1), only screen and ( min__moz-device-pixel-ratio: 1.1), only screen and ( -o-min-device-pixel-ratio: 11/10), only screen and ( min-device-pixel-ratio: 1.1), only screen and ( min-resolution: 97dpi), only screen and ( min-resolution: 1.1dppx)
// Workaround for https://gist.github.com/2140082
@if sprite-position($sprites, $name) != sprite-position($sprites-retina, $name)
$ypos: round(nth(sprite-position($sprites-retina, $name), 2) / 2)
background-position: 0 $ypos
+background-size($width auto)
background-image: sprite-url($sprites-retina)
.chat-icon {
margin-right: 10px;
cursor: pointer;
background-repeat: no-repeat;
display: block;
height: 20px;
width: 21px
}
@media only screen and (-webkit-max-device-pixel-ratio: 1), only screen and (max__moz-device-pixel-ratio: 1), only screen and (-o-max-device-pixel-ratio: 1 / 1), only screen and (max-device-pixel-ratio: 1), only screen and (max-resolution: 96dpi), only screen and (max-resolution: 1dppx) {
.chat-icon {
background-image: url('/images/chat/sprites-s41fc7c6c72.png');
background-position: 0 0
}
}
@media only screen and (-webkit-min-device-pixel-ratio: 1.1), only screen and (min__moz-device-pixel-ratio: 1.1), only screen and (-o-min-device-pixel-ratio: 11 / 10), only screen and (min-device-pixel-ratio: 1.1), only screen and (min-resolution: 97dpi), only screen and (min-resolution: 1.1dppx) {
.chat-icon {
-moz-background-size: 21px auto;
-o-background-size: 21px auto;
-webkit-background-size: 21px auto;
background-size: 21px auto;
background-image: url('/images/chat/sprites-retina-s0be810e70a.png')
}
}
.chat-icon:hover {
background-repeat: no-repeat;
display: block;
height: 20px;
width: 21px
}
@media only screen and (-webkit-max-device-pixel-ratio: 1), only screen and (max__moz-device-pixel-ratio: 1), only screen and (-o-max-device-pixel-ratio: 1 / 1), only screen and (max-device-pixel-ratio: 1), only screen and (max-resolution: 96dpi), only screen and (max-resolution: 1dppx) {
.chat-icon:hover {
background-image: url('/images/chat/sprites-s41fc7c6c72.png');
background-position: 0 -20px
}
}
@media only screen and (-webkit-min-device-pixel-ratio: 1.1), only screen and (min__moz-device-pixel-ratio: 1.1), only screen and (-o-min-device-pixel-ratio: 11 / 10), only screen and (min-device-pixel-ratio: 1.1), only screen and (min-resolution: 97dpi), only screen and (min-resolution: 1.1dppx) {
.chat-icon:hover {
background-position: 0 -20px;
-moz-background-size: 21px auto;
-o-background-size: 21px auto;
-webkit-background-size: 21px auto;
background-size: 21px auto;
background-image: url('/images/chat/sprites-retina-s0be810e70a.png')
}
}
.chat-icon.unread {
background-repeat: no-repeat;
display: block;
height: 20px;
width: 21px
}
@media only screen and (-webkit-max-device-pixel-ratio: 1), only screen and (max__moz-device-pixel-ratio: 1), only screen and (-o-max-device-pixel-ratio: 1 / 1), only screen and (max-device-pixel-ratio: 1), only screen and (max-resolution: 96dpi), only screen and (max-resolution: 1dppx) {
.chat-icon.unread {
background-image: url('/images/chat/sprites-s41fc7c6c72.png');
background-position: 0 -40px
}
}
@media only screen and (-webkit-min-device-pixel-ratio: 1.1), only screen and (min__moz-device-pixel-ratio: 1.1), only screen and (-o-min-device-pixel-ratio: 11 / 10), only screen and (min-device-pixel-ratio: 1.1), only screen and (min-resolution: 97dpi), only screen and (min-resolution: 1.1dppx) {
.chat-icon.unread {
background-position: 0 -40px;
-moz-background-size: 21px auto;
-o-background-size: 21px auto;
-webkit-background-size: 21px auto;
background-size: 21px auto;
background-image: url('/images/chat/sprites-retina-s0be810e70a.png')
}
}
.chat-icon.unread:hover {
background-repeat: no-repeat;
display: block;
height: 20px;
width: 21px
}
@media only screen and (-webkit-max-device-pixel-ratio: 1), only screen and (max__moz-device-pixel-ratio: 1), only screen and (-o-max-device-pixel-ratio: 1 / 1), only screen and (max-device-pixel-ratio: 1), only screen and (max-resolution: 96dpi), only screen and (max-resolution: 1dppx) {
.chat-icon.unread:hover {
background-image: url('/images/chat/sprites-s41fc7c6c72.png');
background-position: 0 -80px
}
}
@media only screen and (-webkit-min-device-pixel-ratio: 1.1), only screen and (min__moz-device-pixel-ratio: 1.1), only screen and (-o-min-device-pixel-ratio: 11 / 10), only screen and (min-device-pixel-ratio: 1.1), only screen and (min-resolution: 97dpi), only screen and (min-resolution: 1.1dppx) {
.chat-icon.unread:hover {
background-position: 0 -80px;
-moz-background-size: 21px auto;
-o-background-size: 21px auto;
-webkit-background-size: 21px auto;
background-size: 21px auto;
background-image: url('/images/chat/sprites-retina-s0be810e70a.png')
}
}
.chat-icon.unread:active,.chat-icon.unread.active {
background-repeat: no-repeat;
display: block;
height: 20px;
width: 21px
}
@media only screen and (-webkit-max-device-pixel-ratio: 1), only screen and (max__moz-device-pixel-ratio: 1), only screen and (-o-max-device-pixel-ratio: 1 / 1), only screen and (max-device-pixel-ratio: 1), only screen and (max-resolution: 96dpi), only screen and (max-resolution: 1dppx) {
.chat-icon.unread:active,.chat-icon.unread.active {
background-image: url('/images/chat/sprites-s41fc7c6c72.png');
background-position: 0 -60px
}
}
@media only screen and (-webkit-min-device-pixel-ratio: 1.1), only screen and (min__moz-device-pixel-ratio: 1.1), only screen and (-o-min-device-pixel-ratio: 11 / 10), only screen and (min-device-pixel-ratio: 1.1), only screen and (min-resolution: 97dpi), only screen and (min-resolution: 1.1dppx) {
.chat-icon.unread:active,.chat-icon.unread.active {
background-position: 0 -60px;
-moz-background-size: 21px auto;
-o-background-size: 21px auto;
-webkit-background-size: 21px auto;
background-size: 21px auto;
background-image: url('/images/chat/sprites-retina-s0be810e70a.png')
}
}
tr.chat-open {
outline: 1px #7ab9f9 solid
}
tr.chat-open .chat-icon {
background-repeat: no-repeat;
display: block;
height: 20px;
width: 21px
}
@media only screen and (-webkit-max-device-pixel-ratio: 1), only screen and (max__moz-device-pixel-ratio: 1), only screen and (-o-max-device-pixel-ratio: 1 / 1), only screen and (max-device-pixel-ratio: 1), only screen and (max-resolution: 96dpi), only screen and (max-resolution: 1dppx) {
tr.chat-open .chat-icon {
background-image: url('/images/chat/sprites-s41fc7c6c72.png');
background-position: 0 -40px
}
}
@media only screen and (-webkit-min-device-pixel-ratio: 1.1), only screen and (min__moz-device-pixel-ratio: 1.1), only screen and (-o-min-device-pixel-ratio: 11 / 10), only screen and (min-device-pixel-ratio: 1.1), only screen and (min-resolution: 97dpi), only screen and (min-resolution: 1.1dppx) {
tr.chat-open .chat-icon {
background-position: 0 -40px;
-moz-background-size: 21px auto;
-o-background-size: 21px auto;
-webkit-background-size: 21px auto;
background-size: 21px auto;
background-image: url('/images/chat/sprites-retina-s0be810e70a.png')
}
}
$icon: 20px
%productNavSprite
display: block
background-repeat: no-repeat
background-size: sprite-width($sprite-product-nav) sprite-height($sprite-product-nav)
@include lodpi
background-image: sprite-url($sprite-product-nav)
@include hidpi
background-image: sprite-url($sprite-product-nav_2x)
@mixin soup($product)
$p: bell- + $product
background-position: sprite-position($sprite-product-nav, $p)
&:hover
background-position: sprite-position($sprite-product-nav, $p + _hover)
.mProductNav-notifications-trigger
width: $icon
height: $icon
cursor: pointer
@extend %productNavSprite
background-position: sprite-position($sprite-product-nav, bell)
&:hover
background-position: sprite-position($sprite-product-nav, bell_hover)
// TODO: verify this psuedo-code soup
.mProductNav--advertisers &.is-unread
@include soup(ifa)
.mProductNav--publishers &.is-unread
@include soup(ifp)
@mixin _dpi($ratio: 1)
@media (-webkit-min-device-pixel-ratio: $ratio), (min--moz-device-pixel-ratio: $ratio), (-o-min-device-pixel-ratio: $ratio), (min-device-pixel-ratio: $ratio), (min-resolution: $ratio * 96dpi), (min-resolution: $ratio * 1dppx)
@content
// "retina" aka "2x"
@mixin hidpi
// Default pixel ratio: 1.3 to support Nexus 7
// Depending on your target, you may want to set a
// more suitable minimum pixel ratio here:
// http://bjango.com/articles/min-device-pixel-ratio/
$hidpi-min-pixel-ratio: 1.3 !default
@include _dpi($hidpi-min-pixel-ratio)
@content
// everyday resolution useful to avoid downloading both normal and retina assets
@mixin lodpi
@include _dpi()
@content
.mProductNav-brand,.mProductNav-notifications-trigger {
display: block;
background-repeat: no-repeat;
background-size: 69px 204px
}
@media (-webkit-min-device-pixel-ratio: 1), (min--moz-device-pixel-ratio: 1), (-o-min-device-pixel-ratio: 1), (min-device-pixel-ratio: 1), (min-resolution: 96dpi), (min-resolution: 1dppx) {
.mProductNav-brand,.mProductNav-notifications-trigger {
background-image: url('images/sprite/product-nav-sedc09af347.png')
}
}
@media (-webkit-min-device-pixel-ratio: 1.3), (min--moz-device-pixel-ratio: 1.3), (-o-min-device-pixel-ratio: 1.3), (min-device-pixel-ratio: 1.3), (min-resolution: 124.8dpi), (min-resolution: 1.3dppx) {
.mProductNav-brand,.mProductNav-notifications-trigger {
background-image: url('images/sprite/product-nav_2x-sdfb2de34c3.png')
}
}
.mProductNav-notifications-trigger {
width: 20px;
height: 20px;
cursor: pointer;
background-position: 0 -84px
}
.mProductNav-notifications-trigger:hover {
background-position: 0 -104px
}
.mProductNav--advertisers .mProductNav-notifications-trigger.is-unread {
background-position: 0 0
}
.mProductNav--advertisers .mProductNav-notifications-trigger.is-unread:hover {
background-position: 0 -21px
}
.mProductNav--publishers .mProductNav-notifications-trigger.is-unread {
background-position: 0 -42px
}
.mProductNav--publishers .mProductNav-notifications-trigger.is-unread:hover {
background-position: 0 -63px
}
@maxbeatty
Copy link
Author

It's been awhile since I've worked with sprites in Compass and retina supports is a new twist. The two implementations aren't apples to apples, but I think fair enough. Jeff had to support an active state. I had to support two brand variations. Ultimately, from a quick eyeballing, it looks like more or less the same amount of Sass produced 1/3 the CSS.

What's good from both approaches?

What isn't great from both approaches?

Is it fine they both do things differently?

Is there a pattern we want to take forward and reuse?

Learn. Learn. Learn.

@jeffharnois
Copy link

One of the benefits of my approach vs Max's approach is my approach is entirely reusable without a bunch of extra boilerplate code. Simply use the mixin with a sprite map. The extra CSS bloat is something that I would like to fix, and is partially due to how I am reusing the mixin.

Max's approach obviously saves on css output and is going to be more performant. I think if Max's can be made a bit more reusable as a single line mixin, it'd be the best of both worlds.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment