ServiceNow Has a VPAT. You Still Failed Your Audit.
ServiceNow publishes a VPAT. It is a real document. It says real things about their platform's accessibility conformance. And when your organization's 508 compliance officer reads it, they reasonably conclude that your ServiceNow portal is accessible. It is a logical conclusion based on official documentation from the platform vendor. It is also, in our experience, wrong about 80% of the time.
Not because ServiceNow is lying. They are not. The VPAT covers the platform as delivered out of the box, with default configurations, using out-of-the-box widgets in their default state. That describes approximately zero production ServiceNow portals. Every portal we have ever audited has custom widgets, custom themes, custom catalog items, and custom CSS that overrides whatever accessibility ServiceNow built into the baseline. The VPAT covers the house as it left the factory. You renovated the house. The renovations are what the auditor is looking at, and the renovations do not have a VPAT.
We have audited and remediated ServiceNow portals for federal agencies, state government, and enterprise clients. Our 508 audit pass rate is 100%. Not because the audits are easy. Because we know exactly where ServiceNow portals break and we fix those things before the auditor shows up. This post is the list. These are the six failures we see on virtually every ServiceNow portal that comes through our door, and how to fix each one.
1. Custom Widgets That Forgot Keyboards Exist
This is the big one. It is on every portal. Every single one.
ServiceNow's Service Portal uses AngularJS (not modern Angular, the 2016 version that Google has been trying to retire for years, but ServiceNow is still running it because enterprise platform migrations move at the speed of committee approval). When developers build custom widgets, they reach for <div> and <span> elements with ng-click handlers because that is what every ServiceNow tutorial shows. The widget works. You click it, it does the thing. Ship it.
Except a keyboard user cannot activate a <div> with ng-click. It is not focusable. It has no role. It does not respond to Enter or Space. For a sighted mouse user, it is a button. For everyone else, it does not exist. It is not broken in the sense that it throws an error. It is broken in the sense that an entire category of users cannot use it at all, and nobody noticed because nobody tested with a keyboard. Nobody ever tests with a keyboard. We say this with the weary certainty of people who have typed "Tab" into more ServiceNow portals than we would like to count.
The fix: Use <button> elements. This is not a ServiceNow-specific recommendation. This is HTML. The same HTML that has existed since 1995. A <button> is focusable, activates on Enter and Space, and announces itself to screen readers without any additional work. If your widget has something a user is supposed to click, it should be a <button>. If it is currently a <div> with ng-click, replacing it takes about 30 seconds and fixes three WCAG criteria at once: 2.1.1 Keyboard, 4.1.2 Name Role Value, and 2.4.7 Focus Visible. That is a lot of compliance for 30 seconds of work. We wish all fixes had this ratio. They do not.
2. The Catalog Item Accessibility Disaster
ServiceNow catalog items are forms. They collect information from users. This is one of the most fundamental interactions on the web, and ServiceNow's catalog item builder makes it remarkably easy to create forms that are completely inaccessible. Not through malice. Through defaults.
Here is what happens. An admin creates a catalog item. They add variables: a text field, a dropdown, a checkbox group. ServiceNow renders these with labels, and in many cases the label association is technically correct in the HTML. So far so good. Then the admin adds help text using the "instructions" field. ServiceNow renders this as a separate element with no programmatic association to the input. A screen reader user fills out the form, hears the label, does not hear the instructions, and submits incorrect information because the context that sighted users can see was never communicated to them. The form "works." The form is not accessible.
Then the admin adds a "depends on" relationship between two variables. Variable B only appears when Variable A is set to "Yes." When Variable A changes, Variable B appears in the DOM. Focus does not move to it. A screen reader user does not know it appeared. They submit the form without filling out Variable B because they did not know Variable B existed. ServiceNow does not handle this focus management for you. It assumes you will handle it yourself. You will not handle it yourself because you probably do not know it is a problem, which is why you are reading this post.
The fix: For instruction text, use aria-describedby to programmatically associate help text with its input. For conditional fields, add a small script in the catalog item's client script that moves focus to the newly visible field when it appears. This is five lines of JavaScript. It is not complicated. It is just not something ServiceNow does for you, and it is not something most ServiceNow admins know they need to do. The gap between "ServiceNow can be accessible" and "ServiceNow is accessible by default" is where most audit failures live. It is a wide gap. We have measured it.
3. The Portal Theme That Broke Focus Indicators
ServiceNow portals ship with visible focus indicators. They are not beautiful. They are the browser's default outline: a blue ring on Chrome, a dotted border on Firefox, whatever Safari feels like doing that day. They work. They tell keyboard users where they are on the page, which is the entire point.
Then someone on the team decides the portal needs a custom theme. The custom theme includes this CSS, or some version of it:
*:focus {
outline: none;
}We have lost count of how many times we have found this exact line in a ServiceNow portal theme. It is in the CSS on approximately every portal where a designer was involved and nobody on the team understood what focus indicators are for. We do not blame the designer. We blame the process that allowed a global CSS rule to remove a critical accessibility feature without anyone asking "wait, what does this do?"
What it does is make your portal fail WCAG 2.4.7 Focus Visible. What it also does is make your portal unusable for anyone navigating with a keyboard. They are pressing Tab. They are moving through the page. They cannot see where they are. They are navigating blind on a sighted interface. It is the accessibility equivalent of removing all the road signs on a highway and hoping drivers figure it out.
The fix: Remove outline: none from your theme CSS. If the default browser focus indicators do not match your design system, replace them with custom focus indicators that are at least as visible. A 2px solid outline in your brand color works. A box-shadow works. Anything works, as long as it is visible, has sufficient contrast against the background, and appears on every focusable element. The bar is not high. It is just non-negotiable.
4. Screen Reader Announcements That Never Happen
ServiceNow portals are single-page applications. When you click a link in the portal, the page does not actually reload. The URL changes, the content swaps, and visually everything looks like a new page loaded. But a screen reader does not see visual changes. A screen reader needs to be told that the page changed. ServiceNow does not consistently tell it.
The result: a screen reader user clicks a link. The content changes. The screen reader says nothing. The user has no idea anything happened. They might click the link again. They might think it is broken. They might navigate away from your portal entirely, which is what usually happens, and you will never know because your analytics track page views, not the people who gave up.
This same problem applies to any dynamic content update in the portal. Filtering a list. Submitting a form. Loading search results. If the update happens without a full page reload (and in ServiceNow's Service Portal, updates almost never trigger a full page reload), the screen reader user is left in the dark. Literally. They are using the page without seeing it, and the page is not talking to them.
The fix: Use aria-live regions. An aria-live="polite" region will cause the screen reader to announce its content when it changes, without interrupting whatever the user was doing. For page navigations, add a visually hidden live region that announces the new page title when the route changes. For form submissions, announce "Form submitted successfully" or the relevant status message. For filtered lists, announce the result count. Each of these is a small addition. A <div> with aria-live="polite", visually hidden, updated with JavaScript when the relevant action occurs. ServiceNow will not add these for you. Your custom widgets need to include them. If they do not, every dynamic interaction on your portal is silent for screen reader users, which means every dynamic interaction on your portal fails WCAG 4.1.3 Status Messages.
5. Color Contrast in the "We Have a Brand" Sense
ServiceNow portal themes support custom colors. Every organization uses this to apply their brand colors. This is expected and fine. What is not fine is when the brand colors fail WCAG contrast requirements, which happens more often than any branding team wants to hear.
Here is the pattern. The organization's brand blue is #4A90D9. It looks professional. It looks trustworthy. It looks like a bank. It has a 3.2:1 contrast ratio against white backgrounds. WCAG 2.2 AA requires 4.5:1 for normal text. The brand blue fails. Not by a little. By enough that an automated scan catches it, which means an auditor will definitely catch it, which means your VPAT will have a "Does Not Support" notation on 1.4.3 Contrast Minimum, which means your portal is non-compliant.
The brand team picked the color. The ServiceNow admin applied the color. The developer built widgets using the color. Nobody checked the contrast ratio because contrast ratios are not part of most brand guidelines, and when they are, they are in a section nobody reads. We have seen brand guidelines that are 47 pages long and contain zero mentions of WCAG. Forty-seven pages about typography, photography style, and the correct clear space around the logo. Zero words about whether the text is actually readable. This is a pattern we encounter so frequently that we have stopped being surprised by it. We have not stopped being disappointed.
The fix: Check every text and interactive element color combination against WCAG contrast requirements. Use WebAIM's contrast checker or the browser's built-in accessibility inspector. Normal text needs 4.5:1. Large text (18px bold or 24px regular) needs 3:1. Interactive components and graphics need 3:1. If your brand blue fails, darken it until it passes. The difference between #4A90D9 and #2E6DB4 is imperceptible to most sighted users and is the difference between passing and failing an audit. Your brand team will survive.
6. Widget Loading States That Leave Users Stranded
ServiceNow widgets load asynchronously. The portal renders, and then individual widgets fetch their data and populate. During the loading phase, widgets often show a spinner, or a skeleton, or nothing at all. What they almost never do is communicate the loading state to assistive technology.
A sighted user sees the spinner and waits. A screen reader user encounters an empty widget, or a widget that says nothing, and has no idea whether content is loading, has failed to load, or simply does not exist. They might move past it. They might wait. They do not know what to do because the widget did not tell them anything.
When the widget finishes loading and content appears, the screen reader is not notified. The content is just there now, silently, like it was always there. If the user already moved past the widget, they will not know that the content they skipped is now available. If they stayed and waited, they do not know the wait is over.
The fix: Add loading state announcements. When a widget begins loading, update an aria-live region with "Loading [widget name]." When it finishes, announce "[Widget name] loaded" or move focus to the newly loaded content if it is the primary content on the page. Use aria-busy="true" on the widget container during loading to indicate that the content is not yet ready. These are small additions that completely change the experience for screen reader users. The difference between a portal that silently loads content and one that communicates its state is the difference between an interface and a guessing game.
The Pattern Underneath All Six
Every failure on this list has the same root cause: ServiceNow provides accessibility infrastructure in its base platform, and then every customization you make on top of it has the potential to break that infrastructure. ServiceNow gives you the raw materials. It does not prevent you from assembling them wrong. And the portal customization workflow (themes, widgets, catalog items, client scripts) makes it easy to build something that looks correct, works for mouse users, and is completely inaccessible to everyone else.
The fix is never "stop customizing ServiceNow." That is not realistic. The fix is to build every customization with accessibility as a requirement, not an afterthought. Test every widget with a keyboard before calling it done. Run a screen reader through every catalog item. Check contrast on every color in your theme. Add live region announcements to every dynamic update. These are not massive engineering efforts. They are habits that take a few extra minutes per widget and save you from explaining to your compliance officer why the portal that "meets 508" according to ServiceNow's VPAT just failed an actual audit.
If your portal has already been built and you are not sure where it stands, an accessibility audit will tell you exactly what needs to be fixed and how. If the audit has already happened and the results were not what you hoped for, we fix that too. Either way, the list above is where we start. Because it is where the problems always are.
