Aria labels provide alternate text to adaptive technology tools such as screen readers. This helps website visitors navigate the site more accurately. One use case for aria-labels is on html anchor tags. Sitecore does not a way to manage this text by default. Follow along and I’ll show you how to modify the various link dialogs and give content authors the ability to manage this text. You can find the full solution on my github.
Aria-Label Example
The W3C gives in depth examples and use cases for aria labels. Consider this block of html.
<p>Seminole tax hike: Seminole city managers are proposing a 75% increase in property taxes for the coming fiscal year. <a href="taxhike.html ">[Read more...]</a> </p>
Visually the read more link would appear near the text. A person that can see the page would be able to recognize the read more link is associated with the text for the tax hike. It is even more apparent if the content is grouped visually in a box separate from other content on the page. But a person using a screen reader would only hear “read more”. The problem is even worse on a search results type page with multiple articles each with their own “read more” link. What am I reading more about? Adding an aria label makes the link clearer.
<p>Seminole tax hike: Seminole city managers are proposing a 75% increase in property taxes for the coming fiscal year. <a href="taxhike.html" aria-label="Read more about Seminole tax hike">[Read more...]</a> </p>
Now a person using a screen reader will hear “Read more about Seminole tax hike”. If each link on the search results type page has an aria label with a clear description, the user can navigate to the correct content.
Can’t We Already Use Aria-Labels in Sitecore?
The Sitecore SXA developer documentation gives an example of an aria label in a scriban template.
{{ sc_field i_item 'PromoLink' [['aria-label', (i_item.PromoLink.Title.raw)]] }}
This example suggests using the title field for the aria label text. This might not give the desired level of control. Generally using one field for two different purposes means it doesn’t work for either purpose. You could add a separate field to use for the aria label. But now you must update datasource templates, scriban templates, field renderers, and do regression testing on top of updating the content! It is very time consuming and error prone. You also have to remember to add the field to all future templates and renderings. It would be better if the aria-label was managed as part of the link.
Add Aria-Label to Rich Text Editor
The easiest place to start is with the rich text editor. The RTE is controlled by the file /sitecore/shell/Controls/Rich Text Editor/Editor Page.aspx. Add the following attribute to the <telerik:radeditor element.
ExternalDialogsPath="~/sitecore/shell/override/Controls/Rich Text Editor/Dialogs/"
To update the UI, Copy /Sitecore/shell/Controls/Rich Text Editor/Dialogs/LinkManager.ascx to /Sitecore/shell/override/Controls/Rich Text Editor/Dialogs/LinkManager.ascx. Now you can modify the overridden LinkManager leaving the original intact. This file is a mix of javascript and html. I used the LinkToolTip field as a template. Any place LinkToolTip exists, I copied and renamed LinkAriaLabel.
this._linkTooltip.value = currentLink.title; this._linkAriaLabel.value = currentLink.getAttribute("aria-label", ""); this._linkTooltip = $get("LinkTooltip"); this._linkAriaLabel = $get("LinkAriaLabel");
The field labels in the html for the link manager dialog are localized.
<script type="text/javascript">document.write(localization["LinkTooltip"]);</script>
The localization file is compiled inside the Telerik.Web.UI dll. It did not make sense for me to pull that out and create a localization label for one field. Since I don’t need to translate this UI, I just hard coded the label.
<tr> <td class="reLabelCell"> <label for="LinkAriaLabel" class="reDialogLabel"> <span>Aria Label</span> </label> </td> <td class="reControlCell"> <input type="text" id="LinkAriaLabel" /> </td> </tr>
You will have to refresh the page to see your changes. When you do, you can see the aria label appears on the hyperlink manager dialog.
The aria label is correctly output in the html source of the page.
Add Aria-Label to Sitecore Link Dialogs
There are three link dialogs in Sitecore. GeneralLink, InternalLink, ExternalLink. The general link is used in experience editor. The internal link and external link are used in content editor.
The process to update all three dialogs is the same, so I’ll show the process for the GeneralLink Dialog and leave the other two for you. You can find the full source for all three on my github.
The link dialogs are defined in Sitecore.Client.dll. The General Link is located at Sitecore.Shell.Applications.Dialogs.GeneralLink.GeneralLinkForm. My original plan was to extend the GeneralLinkForm and override methods. Unfortunately, the GeneralLinkForm does not have a way to inject code to modify the link attributes. I created a new GeneralLinkForm in my solution. I used the title as a template. Any place Title exists, I copied and renamed AriaLabel. Because the GeneralLinkForm inherits from Sitecore.Shell.Applications.Dialogs.LinkForm, I did have to make a few other modifications. The GeneralLinkForm calls base.OnLoad which calls a ParseLink method to read the attributes of the link. I did not want to pull in methods from 3 levels of inheritance. So I created a ParseLinkExtended function to read the aria label attribute. I call this in my OnLoad function after the base.OnLoad
protected override void OnLoad(EventArgs e) { Assert.ArgumentNotNull((object)e, nameof(e)); base.OnLoad(e); if (Context.ClientPage.IsEvent) return; this.CurrentMode = this.LinkType ?? string.Empty; this.ParseLinkExtended(this.GetLink()); this.InitControls(); this.SetModeSpecificControls(); RegisterScripts(); } private void ParseLinkExtended(string link) { Assert.ArgumentNotNull((object)link, nameof(link)); XmlDocument xmlDocument = XmlUtil.LoadXml(link); if (xmlDocument == null) return; XmlNode node = xmlDocument.SelectSingleNode("/link"); if (node == null) return; LinkAttributes["aria-label"] = XmlUtil.GetAttribute("aria-label", node); }
To update the UI, copy /Sitecore/shell/Applications/Dialogs/GeneralLink/GeneralLink.xml to /Sitecore/shell/override/Applications/Dialogs/GeneralLink/GeneralLink.xml. Now you can modify the overridden GeneralLink.xml leaving the original intact.
At the top of the file, change the codebeside type to point to the new extension class.
<CodeBeside Type="Foundation.SitecoreExtensions.Extensions.GeneralLinkForm, Foundation.SitecoreExtensions"/>
Near the bottom of the file, duplicate the lines for Query String and change the duplicate to Aria Label.
<Literal Text="Aria Label:" GridPanel.NoWrap="true" GridPanel.Align="left" /> <Edit Width="100%" Class="scQuirksBoxModel" GridPanel.Row.ID="AriaLabelRow" ID="AriaLabel" />
You might have to restart the web server for the changes to take effect. When they do, you will see the aria label field in the dialog. If you look at the raw view of a link, you will see the aria label attribute is saved correctly.
Exception for Internal Link Dialog
It is interesting to note one difference for the internal link dialog. At some point the dialog was changed from SPEAK UI to SHEER UI.
There is a config that overrides several of the dialogs to point them to a new place. I tried to update this dialog and decided it wasn’t worth it. So I deleted the override with a patch file and the internal link dialog reverts back to the updated SPEAK UI version, which also matches style-wise to the external link dialog.
<overrideDialogs> <override dialogUrl="/sitecore/shell/Applications/Dialogs/Internal%20link.aspx" with="/sitecore/client/applications/dialogs/InsertLinkViaTreeDialog" > <patch:delete /> </override> </overrideDialogs>
Update Link Renderer
The link renderer for SXA is part of Sitecore.XA.Foundation.Multisite.dll. The class Sitecore.XA.Foundation.Multisite.Pipelines.RenderField.GetLinkFieldValue is responsible for creating a new Sitecore.XA.Foundation.Multisite.LinkManagers.SxaLinkRenderer to actually create the anchor tag in the html with the appropriate attributes. The SxaLinkRenderer extends Sitecore.Xml.Xsl.LinkRenderer and uses the base Render method. The base render method does not have a way to inject any other code to modify the link attributes. I created a new SxaLinkRenderer in my solution. I combined Sitecore.XA.Foundation.Multisite.LinkManagers.SxaLinkRenderer with Sitecore.Xml.Xsl.LinkRenderer.Render and update the Render method to include the aria-label attribute.
if (linkField != null) { attributes["title"] = HttpUtility.HtmlAttributeEncode(StringUtil.GetString(attributes["title"], linkField.Title)); attributes["target"] = StringUtil.GetString(attributes["target"], linkField.Target); attributes["class"] = StringUtil.GetString(attributes["class"], linkField.Class); attributes["aria-label"] = StringUtil.GetString(attributes["aria-label"], linkField.GetAttribute("aria-label")); this.SetRelAttribute(attributes, linkField); }
I created a patch config to replace SXA’s GetLinkFieldValue with mine.
<pipelines> <renderField> <processor patch:instead="*[@type='Sitecore.XA.Foundation.Multisite.Pipelines.RenderField.GetLinkFieldValue, Sitecore.XA.Foundation.Multisite']" type="Foundation.Platform.SitecoreExtensions.Extensions.GetLinkFieldValue, Foundation.Platform.SitecoreExtensions" resolve="true" /> </renderField> </pipelines>
The updated link render will correctly output the aria-label for field renderers and both sc_field and i_item.LinkField in scriban templates. You don’t have to manually update the rendering variants which is a huge time savings!
Conclusion
It is relatively easy to include aria-labels in link dialogs in Sitecore. I wrote this article based on Sitecore 9.3. I did not compare the Sitecore dlls between versions, so I don’t know if you can do this once and apply to any version of Sitecore or if these changes need to be applied to each version. Either way, the concept of how to make these updates will apply to any recent version of Sitecore. With these changes in place, content authors can create more accessible websites!