Designing Accessible Experiences from the API Up (with GraphQL)
What is GraphQL knew Accessibility? Shifting Accessibility Left
Hello Hello,
Welcome to my space on the internet where I talk about my experiences in the tech space whether it be projects I’m actively working on or events I attend. If you are a tech lover, searching for an answer to your question within the mobile space, or just looking for a fellow woman in tech feel free to subscribe and stick around. Happy to have you here in my corner. 😆
A few weeks ago I had the opportunity to speak at GraphQLConf 2025 in Amsterdam, The Netherlands where I spoke about accessibility at the API level.
The title of my presentation was “What if GraphQL Knew Accessibility?”. What if we moved key accessibility hints into the GraphQL Schema so clients receive semantics alongside data? Instead of bolting on accessibility late in the UI, we’d shift it left to the API contract. The core idea is embedding accessibility information into your GraphQL schema to make a stronger push towards accessible applications. This was done by using directives to provide additional information that could be used for accessibility purposes to help screen readers inform users of what is on their screen.
Why Shift Left?
Before getting into any GraphQL Schema or API calls, we have to hone in on why accessibility matters and why we should shift this topic left:
Accessibility affects millions of users, so treating it as extra polish is too late
Clients depend on consistent semantics, for example screen readers to give additional information to users
We want to increase inclusivity throughout the app to cater to more people. While this is important from a business standpoint because you will be able to target a larger user base, it is morally important for users to know that you value their business.
The Status Quo (and its cracks)
Now let’s zoom in on what developers are actually working with today.
query {
title
releaseYear
rating
}
On the surface, this looks fine but the cracks start to show when we think about these pain points:
There is no single source of truth for accessibility messaging
There can be brittle, duplicated client side accessibility logic
Accessibility enhancements added late instead of built in
No easy way to provide per instance, field level nuance
The Shift: Treat Accessibility as Product Data
Now, here is the shift in thinking: what if our schema itself carried accessibility metadata?
query {
title
releaseYear
rating
a11y // example: server provided summary for focused item
}
How It Works (at a glance)
With that extra field, clients can apply consistent announcements or labels where they are needed without reinventing or constructing strings on every platform. However, to make this happen, we need to wire up our schema in a smarter way:

We teach the schema about accessibility using custom directives and a light wiring plugin. We can query for the accessibility information (a11y) and construct the accessibility messaging on the API side and send it to the client. The client in this case would be Android apps, iOS apps, and Web pages.
These custom directives are the secret sauce. Directives annotate types or fields with small bits of a11y metadata and the plugin assembles a concise summary of (based only on the fields actually queried) and exposes it as a11y to the client. There is also flexibility in this process. Briefly mentioned above, you can add additional information per attribute that you query for with one of the added directives. We will dive deeper into this in Part 2.
Directives:
directive @a11yLabel(field: String!) on OBJECT | FIELD_DEFINITION
directive @a11yTemplate(summary: String) on OBJECTTypes:
type Movie @a11yTemplate(summary: "{title} , {rating} , {releaseYear}") {
id: ID!
title: String! @a11yLabel(field: "title")
releaseYear: Int,
rating: Float,
a11y: A11y!
}
type A11y {
templates: A11yTemplates
// more fields shown in Part 2
}
type A11yTemplates { summary: String }Query:
query {
movies {
title
releaseYear
a11y {
templates { summary }
}
}
}
Clients (Android, iOS, Web) can then map this metadata to platform semantics. These would apply to contentDescriptions, accessibilityLabels, and aria-* for each platform respectively. Shifting accessibility left in the development process puts it more in focus and treats it as a priority to ensure users are provided a great and consistent user experience. This ensures consistency across platforms. This is especially important for users that have visual impairments.
What this buys you immediately
Single source of truth for accessibility messaging
Less client duplication and fewer brittle branches
Dynamic, per-instance summaries with minimal effort
Optional field-level nuance when needed
This is especially valuable for users of screen readers because the message is consistent across Android, iOS, and Web when they make the same query.
Questions to Answer Next
However, there are a few questions that are left to be answered:
Ownership: Who owns the accessibility directives? Backend engineers? Designers? Product? In reality, it will most likely be a joint effort.
Completeness: What should happen when metadata is missing? How do we enforce completeness?
Localization?
Scale: How does this play with federation?
This was a summary of the overall idea and project and will be further developed and will hopefully be useful to engineers.
What’s next (Part 2)
If you want to peek under the hood or try the pattern, the repo is here: (GraphQL-a11y). Feedback and issues welcome! Part 2 will dive deeper into the code and land next week.
Please feel free to subscribe for more content and share this newsletter with anyone that may use GraphQL and cares about making accessible applications! We can make a bigger, lasting effort to create inclusive technology. See you next time!
P.S. GraphQLConf Amsterdam was a blast! Huge thanks to everyone who came to the talk and jammed on this idea afterward.


