I'm working on a website which has icons on file upload links, like so:
This works for people who can see the icons, but what about those who can't? Accessibility means giving all users an equivalent experience, regardless of how they're accessing the site.
If the icons were inline images, we could add alt text with the filetype. However, in this case, they're background images.
So, we want to add visually hidden text to the end of file links with the filetype, e.g. "(PDF)". This will let screen reader users know that the link will open a file. We visually hide it because it is redundant for those who can see the icon.
To accomplish this, we need to add an template_preprocess_file_link override, then a custom file-link.html.twig template file.
The preprocess function, placed in your theme's .theme file (replace THEMENAME with your theme's name):
Preprocess function, Drupal 8-9
This was correct when I wrote it, but the file_create_url
method was apparently deprecated in 9.3.0 and removed in Drupal 10. Read on for the updated version.
// Add variables for file-link.html.twig template so we can re-write it to include a visually-hidden filetype indication in the link for accessibility
function THEMENAME_preprocess_file_link(&$variables) {
$variables['file_url'] = file_create_url($variables['file']->getFileUri());
$variables['filename'] = $variables['file']->getFilename();
if (empty($variables['description'])) {
$variables['link_text'] = $variables['filename'];
}
else {
$variables['link_text'] = $variables['description'];
}
}
Preprocess function, Drupal 9.3.0 +
// Add variables for file-link.html.twig template so we can re-write it to include a visually-hidden filetype indication in the link for accessibility
function THEMENAME_preprocess_file_link(&$variables) {
$file_url_generator = \Drupal::service('file_url_generator');
$variables['file_url'] = $file_url_generator
->generate($variables['file']
->getFileUri());
$variables['filename'] = $variables['file']->getFilename();
if (empty($variables['description'])) {
$variables['link_text'] = $variables['filename'];
}
else {
$variables['link_text'] = $variables['description'];
}
}
We're getting the file URL so we can print it separately in the template, and creating a link_text
variable that is set to the name of the file, or whatever is in the file description field, if it's filled.
Template, all Drupal versions
In your custom theme, create field-link.html.twig with this content:
{% set filename = filename|split('.') %}
{% set file_ext = filename[1]|join('')|upper %}
<span{{ attributes }}>
<a href="{{ file_url }}">{{ link_text }}
{% if (description is not empty) %}
<span class="visually-hidden">({{ file_ext }})</span>
{% endif %}
</a>
</span>
We only show the extension when the file description field has content. If the description is empty, the file name is shown, which already contains the extension. The filename is not ideal and it's better for users to enter something in the description field, but if they don't, it's better than nothing. Unfortunately in Drupal 8, you can't make a file field description required; for that and other reasons, it's better to use the core Media module, and do similar processing to display the media name with the file extension.
We take the filename variable, split it at the dot character, then create a file_ext
variable from the second item (which should be the file extension), use the join
filter to put it back together, then uppercase it. Uppercasing isn't really necessary in this case, as the extension isn't shown; it would be if you want to visually show it, which I would do if there was no icon.
We then print the file_ext variable after the link text (but inside the link, which is important as users may read the link out of context), wrapped in a span with the visually-hidden
class. This renders it invisible, but still readable by screen readers. The CSS for this class is:
visually-hidden {
position: absolute !important;
height: 1px;
width: 1px;
overflow: hidden;
clip-path: inset(1px 1px 1px 1px);
}
The resulting output for an example PDF:
<span class="file file--mime-application-pdf file--application-pdf">
<a href="/sites/default/files/2019-05/job_shadow_safety_miosha.pdf">Job Shadow Safety MIOSHA
<span class="visually-hidden">(PDF)</span>
</a>
</span>
If you want the file extension to be visible for all users, just remove the visually-hidden span.
Comments
Hi--love your site.
Just tried this on a site and I got an error that "Error: Call to undefined function file_create_url() in procurement_preprocess_file_link()" -- have you ever seen that?
Hi Tim, I haven't - this was written 4 years ago, so it's possible it's no longer correct, or it's an issue with your specific implementation. Can't tell without being able to look at it in more detail, sorry.
As I'm working on upgrading my site to Drupal 10, I get a notice from the Upgrade Status module:
"Call to deprecated function file_create_url(). Deprecated in drupal:9.3.0 and is removed from drupal:10.0.0. Use the appropriate method on \Drupal\Core\File\FileUrlGeneratorInterface instead."
So it appears this method is no longer valid. I'll update the blog post if/when I get around to fixing it.
Edit: post updated for Drupal 9.3.0+.
Saved my life. Tkanks :)