Updating Alma Letter Accessibility

Common Accessibility Issues to Check in Your Alma Letters

CARLI is actively reviewing I-Share Alma letters for accessibility issues and working to bring them into compliance with WCAG 2.1 Level AA. This page documents common issues we have identified across Alma letter XSL templates, along with the specific changes CARLI has made to the shared letter components distributed through the Network Zone. We will update this page as our review continues.

Last Updated: Sept. 9, 2026

Structure and Markup

Missing language declaration

Every letter should have lang="en" on the <html> tag. Without it, screen readers may guess the wrong language and mispronounce content.

Instead of:

<html>

Use:

<html lang="en">

 

Non-descriptive image alt text

Most Alma letters use the header.xsl template to display the library logo, but some letters have the code for the logo written into the letter's XSL. 
Search your letter's XSL for <img and check the alt= value. If it says alt="logo" that needs to be fixed. It should either be your institution's name, or alt="" if the letter heading already identifies the sender.

Instead of:

<img src="cid:logo.jpg" alt="logo"/>

Use:

<img src="cid:logo.jpg" alt=""/>

or

<img src="cid:logo.jpg" alt="University Library"/>

 

Invalid HTML tags: <h>

<h> is not a real HTML element and carries no meaning for assistive technology. Replace with <p> for body text, or a proper heading tag (<h2>, <h3>) if it is actually a heading.

 

Presentational <b> instead of semantic <strong>

For text that needs to convey emphasis or importance, use <strong> rather than <b>. Most email clients render them identically but <strong> carries meaning for assistive technology.

 

Tables and Styling

Data tables missing column scope

Any table displaying item data (titles, due dates, barcodes, etc.) should have scope="col" on each <th> element so screen readers can associate headers with the correct data cells.

Instead of:

<th>Title</th>

Use:

<th scope="col">Title</th>

 

Deprecated bgcolor attribute

The bgcolor attribute is old HTML and not reliably supported. Replace with an inline style.

Instead of:

<tr bgcolor="#f5f5f5">

Use:

<tr style="background-color:#f5f5f5;">

 

Hardcoded small font size

If your letter has font-size:80% in the body style, remove it or set it to 100%. Shrinking text below the user's default is an accessibility problem, especially for low vision users.

 

<br/> used for spacing inside tables

Line breaks placed inside a <table> but outside a <td> are invalid HTML and can behave unpredictably across email clients. Use padding or margin on surrounding elements instead.

 


What CARLI Changed in the Shared Letter Templates

The following changes were made to the shared Alma letter component templates in the I-Share Network Zone and propagated to all member libraries. If you have made local customizations to any of these templates, your version was not overwritten and you may need to apply these changes manually.

style.xsl

The global CSS stylesheet inherited by all letters. Changes here propagate across every letter automatically.

Fixed h4 line-height 

The original value of 0.2em collapsed heading text on itself. 1.4em is the accessible standard.

Before:

h4 {line-height: 0.2em}

After:

h4 {line-height: 1.4em}

Bumped font size from 80% to 100%

Forcing body text to 80% of the user's default is an accessibility problem, especially for low vision users who have deliberately set their browser text size larger.

Before:

font-family: Tahoma; color:#333; margin:0; padding:0em; font-size:80%

After:

font-family: Tahoma, Arial, sans-serif; color:#333333; margin:0; padding:0; font-size:100%

Removed text-shadow calls

White shadow on a light gray header and dark shadow on a dark footer both render invisibly and serve no purpose. Removed from both headerTableStyleCss and footerTableStyleCss.

Before (header):

background-color:#e9e9e9; width:100%; height:30px; text-shadow:1px 1px 1px #fff;

After (header):

background-color:#e9e9e9; width:100%; height:30px;

Before (footer):

background-color:#444; width:100%; text-shadow:1px 1px 1px #333; color:#fff; margin-top:1em; font-weight:700; line-height:2em; font-size:150%;

After (footer):

background-color:#333333; width:100%; color:#ffffff; margin-top:1em; font-weight:700; line-height:2em; font-size:150%;

Improved footer contrast

The original footer background of #444 gave a contrast ratio of ~5.7:1 against white text — borderline for WCAG AA. Darkening to #333333 brings it to ~7:1, comfortably above the threshold.

Standardized hex colors to 6 digits

Shorthand hex values were expanded to their 6-digit equivalents throughout for consistency and to avoid ambiguity across email clients.

Examples:

#fff  →  #ffffff        #eee  →  #eeeeee        #333  →  #333333

Added font fallback stack

Added Arial, sans-serif as fallbacks after Tahoma. If Tahoma is unavailable, email clients now fall back gracefully rather than guessing.

Before:

font-family: Tahoma;

After:

font-family: Tahoma, Arial, sans-serif;

 

header.xsl

Provides the logo and letter title header used by most letters via <xsl:call-template name="head" />.

Fixed logo alt text

The institution logo in the main header is decorative — the letter name in the <h1> below it already identifies the context. Empty alt text tells screen readers to skip it. The I-Share logo in receiptPrinterHead has no accompanying heading, so it gets descriptive alt text instead.

Before (both templates):

<img src="cid:logo.jpg" alt="logo"/>

After (head template — decorative):

<img src="cid:logo.jpg" alt=""/>

After (receiptPrinterHead — descriptive):

<img src="https://www.carli.illinois.edu/.../iShare-stacked-color.jpg" width="200" height="57" alt="I-Share"/>

 

footer.xsl

 

Provides the address footer and contact/account links used by most letters.

Removed misapplied list styles

The listStyleCss template was being applied to a <tr> element in the lastFooter template. List styles on table rows have no effect and were removed.

Before:

<tr>
  <xsl:attribute name="style">
    <xsl:call-template name="listStyleCss" />
  </xsl:attribute>

After:

<tr>

 

mailReason.xsl

Provides the salutation template used by letters that call toWhomIsConcerned. No accessibility changes were needed to this template's structure or styling.

 

recordTitle.xsl

Displays the title, author, and volume/issue description for a physical item.

Removed whitespace spacers

The original template used three literal spaces before each value to create visual indentation. Screen readers may announce or stumble over leading whitespace. Replaced with margin-bottom: 0.5em on each <div> for proper spacing.

Before:

<div class="recordTitle">
  <span class="spacer_after_1em">   <xsl:value-of select="..."/></span>
</div>

After:

<div class="recordTitle" style="margin-bottom:0.5em;">
  <xsl:value-of select="..."/>
</div>

Removed undefined class and unnecessary wrapper spans

The spacer_after_1em class is not defined anywhere in style.xsl and was doing nothing. The extra wrapping <span> elements and empty class="" attributes were also removed.

 

senderReceiver.xsl

Displays the patron and institution address block used by most letters.

Removed misapplied list styles

The listStyleCss template was being applied to <table> elements in all three address blocks. List styles on tables have no effect. Removed from the user_for_printing, receiver/user, and organization_unit blocks.

Before:

<table cellspacing="0" cellpadding="5" border="0">
  <xsl:attribute name="style">
    <xsl:call-template name="listStyleCss" />
  </xsl:attribute>

After:

<table cellspacing="0" cellpadding="5" border="0">

Added conditional checks for empty address lines

The receiver and organization unit address blocks were rendering all address lines unconditionally, which could produce empty <tr> rows for patrons or libraries with incomplete address data. Added <xsl:if> checks to match the pattern already established in the user_for_printing block.

Before (receiver block — unconditional):

<tr><td><xsl:value-of select="user_address_list/user_address/line1"/></td></tr>
<tr><td><xsl:value-of select="user_address_list/user_address/line2"/></td></tr>

After (receiver block — conditional):

<xsl:variable name="rline1" select="user_address_list/user_address/line1"/>
<xsl:if test="$rline1 != ''"><tr><td><xsl:value-of select="$rline1"/></td></tr></xsl:if>
<xsl:variable name="rline2" select="user_address_list/user_address/line2"/>
<xsl:if test="$rline2 != ''"><tr><td><xsl:value-of select="$rline2"/></td></tr></xsl:if>