CSS is executed client-side and so it cannot check for the existence of an image, font or other asset file being referenced. However, since Sass is written in Ruby, it allows for server-side calls by extending Sass via custom functions. Here is a custom function to check for the existence of a file:
module Sass::Script::Functions
# Does the supplied image exist?
def file_exists(image_file)
path = image_file.value
Sass::Script::Bool.new(File.exists?(path))
end
end
If this code is placed in a file named functions.rb, the Sass watch command would be:
sass --watch style.scss:style.css --require functions.rb
So, why would you ever need to check for the existence of a file at Sass compile time? One place I found it useful (I'm sure there are other uses) was when eliminating duplication of internationalized CTA (call-to-action) images. Canadian (or British) English is similar to U.S. English in many ways, but there are some words that are a different between the two (favorite vs favourite for example). The following Sass mixin selects a CTA image from a folder based on the lang attribute set on the page. In the case of Canadian English, it will first check to see if the image exists in the en-ca folder. If not, it will fall back to using the image from the en-us folder. This avoids duplication of the English images that are the same in both Canadian and U.S. English. The benefit of this is:
- Fewer total assets, so they are easier to maintain
- The total asset payload is smaller (especially important if used in a mobile app)
@mixin localeImage($image: null) {
[lang="en-us"] & {
background-image: url('assets/img/en-us/#{$image}');
}
[lang="en-ca"] & {
$file: 'assets/img/en-ca/#{$image}';
@if file_exists($file) {
background-image: url('#{$file}');
} else {
background-image: url('assets/img/en-us/#{$image}');
}
}
[lang="fr-ca"] & {
background-image: url('assets/img/fr-ca/#{$image}');
}
}