
Replacing placeholder images with CSS-Fu! ๐ฆธโโ๏ธ
- toucaan
- 2 minutes
- December 31, 2018
Ok, itโs the new year eve, so letโs do something lame. Yes, because why not! ๐
Also, happy new year 2019 everyone! ๐
Update (Jan, 25th โ19): We are no longer using the
uploader
code and Carrierwave gem as explained in the original article below. We migrated to using direct uploads with Cloudinary instead, even though the placeholder images are still being handled using CSS3. Exactly the way it has been explained below.
Itโs fairly common in the land of consumer apps to serve a placeholder image (default_image) for new users who have signed up but not uploaded an image for themselves. Like this one below:

To achieve this, there is often an uploader method like so:
def default_url
unless version_name.blank?
โ/assets/users/default_user/โ + [version_name.downcase, โdefault-user.pngโ].compact.join(โ_โ)
else
โ/assets/users/default_user/โ + [โdefault-user.pngโ].compact.join(โ_โ)
end
end
We were are using the classy Carrierwave gem by Jonas Nicklas for file uploads on our Rails app.
So the first step is to remove the code for default_images
and figure out some CSS properties with which we can paint that sort of image client-side. I removed the entire block of code for default_url
on the rails_uploader so that no http
request is fired to serve a default image when the user doesnโt have an avatar for themselves.
We paint a placeholder image using CSS (SCSS) like so:
@charset "UTF-8";
.profile-icon {
font-size: 15px;
border: 0.1em solid #ccc;
width: 10em;
height: 10em;
border-radius: 50%;
overflow: hidden;
position: relative;
background: #eee;
&:before {
content: โโ;
display: block;
position: absolute;
border: 0.1em solid #ccc;
background: white;
width: 7em;
height: 7em;
border-radius: 50%;
bottom: -3em;
left: 50%;
margin-left: -3.5em;
}
&:after {
content: โโ;
display: block;
position: absolute;
width: 4em;
height: 4em;
border: 0.1em solid #ccc;
border-radius: 50%;
background: white;
top: 1.8em;
left: 50%;
margin-left: -2em;
}
}
.profile-image-container {
height: 200px;
width: 200px;
}
.profile-image {
border: 5px solid #fff;
border-radius: 50%;
box-shadow: 0 0 26px rgba(0, 0, 0, 0.35) inset, 0 0 15px rgba(0, 0, 0, 0.35);
height: 150px;
left: -5px;
top: -5px;
width: 150px;
input {
background: transparent left top no-repeat;
height: 140px;
width: 137px;
cursor: pointer;
position: absolute;
left: 0;
z-index: 1;
opacity: 0;
}
label {
background: transparent left top no-repeat;
height: 140px;
width: 137px;
cursor: pointer;
position: absolute;
left: 0;
z-index: 1;
opacity: 0;
opacity: 0.4;
}
}
A single class with :before & :after pseudos does it!
Then in the view layer (we use haml
on Bubblin!):
- unless @user.image.blank?
.center.profile-icon.profile-image # โ Render a placeholder image using CSS
= form_for @user, remote: :true, html: { multipart: true, method: :patch, id: โaddImageโ } do |f|
= f.label :image, โNew image?โ, class: โflexโ;
= f.hidden_field(:image_cache)
= f.file_field :image, onchange: โuploadImage(this)โ, data: {max_file_size: 1.megabytes }
And boom! Gone is the overhead of serving another sorry image (jpg/png) file. Could potentially save quite a bit of bandwidth and a bunch of http requests on your app.
Sounds cool, huh? Or lame may be. Anyway, happy new year and happy coding!
Written by: Sonica Arora, CTO of Bubblin Superbooks. Follow me on Twitter perhaps?
Edited by: Marvin Danig, CEO of Bubblin Superbooks.
P.S.: Itโs likely that some of you viewed this site on your desktop. If you did that we recommend revisiting us on your iPad!