Accessibility and AI - enhancing R Shiny apps using Claude Sonnet 3.7

Grasping the thistle. Using AI for public good

accessibility
AI
creative writing
vibe coding

Vibe coding a small test R Shiny app from scratch using Claude.ai. Can generative AI help improve the default accessibility of an app?

Author
Published

24 April 2025

Modified

3 September 2025

Abstract

Can generative AI help improve the default accessibility of an app? In this creative work the author (a developer) muses that in the arts they’ve been talking about streams of consciousness in writing for more than a century. A Doric joke is used as a starting point for discussion about accessibility and exclusion. Via streams of lighthearted questions and answers we skip, capriciously, through some old works of literature ending up sampling a famous Scottish poem and talking about the Scottish Highlands. In the current day (April 2025 at time of writing) the author experiments with “vibe coding” an entire app for the first time. R Shiny app features were descibed in English to an AI chatbot Claude.ai, Sonnet 3.7. The author finds out what kind of generative AI code is produced if a developer specifically asks a chatbot to create and then improve the accessibility of a small test interactive dashboard app. The author works iteratively to get the app to a point where it will run without obvious errors or warnings. The simple app contained an interactive plot and an accessible text table. Screenshots of the AI prompt environment and the produced app are given. The code is published in a GitHub repository. Little research into enhanced coding specifically for accessibility using tools like AI is currently available. It is hoped that in the future for models tuned for generating code for web development that more training is done code using examples that incorporate better accessibility support. It would be great if conventions could emerge around recommended prompts for developers to use to get better solutions re accessibility.

Keywords

Claude, Doric, humour, Hugh MacDiarmid, R Shiny, Scots language, ShinyConf2025, Sonnet 3.7, stream of consciousness, Tomintoul, vibe coding

Warning

Contains scenes that may not be suitable for those lacking in a sense of humour.

Absolute scenes.

If Marvin the Paranoid Android truly is your spirit animal maybe skip straight to Scene IV.

Prologue

Inspired by a session at #ShinyConf2025 where a group of developers talked about their experiences of using AI generated code to complete a four hour hackathon challenge.

NoteAct of Confession

Me: “Bless me father for I have sinned. It’s been forever since my last confession. I have….I have…I have committed ‘vibe coding’. It was an experiment in the name of accessibility. I am disabled and unemployed and I couldn’t resist temptation. I am sorry.”

Priesty guy: “er, I don’t think that’s a sin but I’m getting a few more of these things these days. if it makes you feel better, for penance please complete a ‘Work of Mercy to Instruct the Ignorant’

Scene I - The author muses

Fitsintilt?

Scene: Balmoral. Royal Doricside Deeside

Provenance: Related by a baby boomer whose mother had a friend, whose friend was in service.

True story: p-value 0.5

Queen Victoria is staying at Balmoral castle. The Queen is served her dinner. She loves the taste of her plate of soup, she hasn’t had this kind before. She loves it so much she has a second helping and asks for more crusty bread to go with it. She is so impressed that at the end of the meal she asks a servant to escort her to see the cook in the kitchen.

Queen Victoria: “I want to see how the soup is made”.

Victoria enters the kitchen which is now being cleaned. Two women are standing near the sink, Maureen the cook and Katie the cook’s assistant.

Queen Victoria: “That was the most marvellous soup. Simply splendid. I have to know how you make it.”.

Victoria goes all around the kitchen, follows her nose and to the source of the smell of her beloved new soup. She spies a huge pot on the stove with a ladle. She stands over the pot and peers into it, There’s still some soup left. Wisps of steam are rising, the liquid gently simmers. She starts to raise an unwieldy metal ladle to her mouth. Maureen promptly leaps to grab a soup spoon and offers it to Victoria. Victoria can see solid chunks of unrecognisable things in the pan.

Maureen: “Mind an no burn yersel maam!”

Queen Victoria: “Pardon?”

Maureen realises in order for Victoria to understand she needs to speak using a different tongue.

Maureen: “Please don’t burn yourself maam. Here’s a spoon”. Victoria accepts the spoon, puts the ladle down and downs yet more soup, can’t get enough.

Victoria: “You’ll have to tell me the recipe”

Maureen: “Katie made the soup maam. Katie fitsintilt?”

Victoria: “Fitsintilt?”

Katie: “Weel there’s athing intilt: carrots, neeps, barley, bilin beef Maam. Oh and a sprig o parsley”

Now if you’re from a certain part of the world, the north east of Scotland you’ll recognise that this is funny. Funny ha, ha as in “A Joke”, not funny peculiar. If you’re from outside it might just seem like a short story with some unusual words. Same words but different people would take a different meaning.

I don’t get it. What’s the joke?

Oh hello there, an actual reader? Great! Foos yer doos?.

Well there’s a few layers of social-economic history of Scotland in it but the main joke is that Victoria literally doesn’t understand what the word fitsintilt means. Her questioning inflexion indicates that she would really like an explanation of what the word means. It’s clear she doesn’t understand because what she is really hearing is actually three words spoken in the local Doric dialect of the Scots language which is unintelligible to Victoria. Conversely, Maureen knows Katie doesn’t understand Victoria’s very posh English accent and is the interpreter, translating into a local version that she knows her assistant will understand.

Fitsintilit translates to the English “What is in it? (the soup)”. It’s a joke about language and class. There’s a further joke in that the scotch broth is what Robert Burns would call “hamely fare” - cheap, humble but ultimately nourishing food and the queen thinks it must be some exotic foreign delicacy.

If you’ve got to explain it, it’s not a joke.

But you did ask.

It is a joke, just not one someone like you would ever usually hear. Why? Because the person telling it would likely only speak it if they knew that they had Doric speakers would be listening in their audience.

That wasn’t your cup of tea? Here’s a different soup joke (strictly speaking two) in English instead, involving another queen, Victoria (Wood). Go and watch that now, if you don’t find that one funny then go to A&E as clearly you have broken your funny bone. Pick a windae, ye’re gaun oot.

But what has this got to do with AI? I’m here for AI. All everyone is interested in is AI. You said there’d be AI. I’m not here for history or weird Scottish stuff. This is rubbish, give me the AI.

Wheesht!

What?

Be quiet

Not until you tell me what “wheesht” means.

Be quiet

This is gibberish, I’m leaving.

Cheerie bye.

What?

Cheerio

Where are you going?

Thought you were leaving?

Stream of (non)consciousness

Are you sitting comfortably? Then I’ll begin. [Suggested background music]

There’s a famous long poem written in Scots, published in 1926 called “A Drunk Man Looks at the Thistle” by Hugh MacDiarmid.

Oh God, you’re at it again. Where’s the AI?

The 1920s was the era in the arts where the “stream of consciousness” style of writing was in vogue, made popular by the likes of Virginia Woolf, James Joyce and others like MacDiarmid. A century on we’re now talking about the streams of non-consciousness, the screeds of text generated by inanimate machines.

Thistles. Bonnie flowers. Are they even flowers, weeds? Pretty weeds?

Musical interlude:

What on earth is this? Interlude? You haven’t even started? Where’s the AI?

Still here? People express love for Scotland in many ways. Tourists come here from all over the world, people in Scotland love meeting them. People are drawn here by Outlander, bagpipes, Outlander, kilts, Outlander…

Oh no! Next you’ll be giving us tartan and shortbread tins.

You demonstrate an excellent knowledge of Scotland sir. Yes sir, I can boogie. There is a Tartan Day in different countries around the world. New York City has had a whole Tartan Week. The only shortbread in the whole wide world is Walker’s Shortbread, that’s Walkers of Aberlour in Speyside. Speyside is where the famous single malt whisky like Glenlivet is distilled.

This reads like an advert, are you selling something? Are you high? Where’s this going?

Pleased you asked (again). Here is what we know about high. The answer is to the meaning of life, the universe, and everything is 42 but the centre of known civilisation is found in Tomintoul, Moray.

Tomintoul

Tomintoul is the highest village in the Highlands at 345m above sea level. The highlight of the year is the Tomintoul Highland Games which has been held on the 3rd Saturday in July every year, for over 175 years. The world is invited to attend. In 2024 they had their highest attendance ever. Field events include the high jump. The high point of the all events there have ever been and ever will be is the highly prized Hill Race. The race takes runners to the top of the hill overlooking the Highland Games field known as Tom na bat. The name of the hill is derived from the ancient Gaelic “hill is far too high, try flying like a bat”.

Stop! Are you drunk? Are you a machine? I’m looking for AI.

Stap is the correct word. I’m unable to drink sir. You would like to know more about drunk? Here is what we know about drunk. Drunk events at the Tomintoul Highland Games include the barrel race. The barrel race is the very last event of the day. People with pointy sticks push a whisky barrel across a field. There are few humans alive that have been privileged to witness this highly drunk event. Drunks are reminded that the whisky barrels have already been pre-emptied prior the race. Would you like some more sir?

You’re making that up. Why are you assuming I’m sir?

  1. The Tomintoul Highland Games barrel race is a real event. p 1.00. You can attend and verify this fact for yourself
  2. Saves time

Stap! All I want is AI. MacDiarmid? Who’s that? What’s a poet got to do with anything?

“Poets are a very small minority of people who, for some obscure reason, have failed to grow up. In other words, they’re natural rebels.” - Hugh McDermid in a BBC interview

Why is the drunk looking at a thistle? The thistle is the national emblem of Scotland. Pretty blooms from a distance but up close you’ll find jaggy stems underneath. Ah one of those meta, meta, meta…

For?

Thanks. That’s the one. ForMeta-thingummyjig about Scotland. So it’s really not about a person but a country.

The English

“Mrs. Dalloway said she would buy the flowers herself…”

I can read that. And it’s in English, not that weird Scotch stuff. So Mrs Dalloway buys Scotland?

You mean Scots - not Scotch. What’s weird about whisky? No to your taste? Try Irn-Bru instead.

You’re imagining things. Buys Scotland? What?

There’s no such thing as a Scots language.

Ooft! That’s me telt! You’re absolutely right, there’s not just one Scots language…There’s a collection of dialects.

Mrs Dalloway, hen? (West Central), quine (North East)?

Oh I do beg your pardon missus, proper English like.

You’re probably what’s known as a ‘lady’. Apologies for the technical jargon. Ma freen Jessie will noo explain The First Law o Ladies.

“There’s ladies and there’s wifies…and you can always tell the difference” - Jessie Kesson

Lady muck, ye dinna pairt wi yer bawbees tae buy thistles, they’re already free. Get them at the park, in the fields. Wild. Come for the nice looks but you’ll have to deal with the prickliness. Grasping a thistle is not for the faint of heart. Can you tell a thistle from a rose?

What about the poem?

Thought you didn’t want poetry?

The Irish

“Stately, plump…”

English again?

Buck’s sake maun. Haud it richt there.

See you Jimmie. Jamsie, Rab, Rabbie, Ramble…

Loon, ye canna say ‘plump’ the day, ye’ll get cancelled. No fat shaming.

It’s English. No, it’s Irish. No, it’s Irish English. Canna go on a Greek odyssey the noo. Wark!

We’ll be here all night. To cut a long story short. Here’s a summary

  • Mansplaining
  • Mansplaining
  • Mansplaining
  • SPOILER ALERT: A Woman Speaks!

“…Yes.”

The Scots

Is there much more of this nonsense? This isn’t about AI at all.

In a word, Yes. Can ye no read plain English?

From the intro to “A Drunk Man Looks At The Thistle”

“…Drunkenness has a logic of its own with which, even in these decadent days, I believe a sufficient minority of my countrymen remain au fait…”

And warns against

“…attempting, in, no doubt, a spirit of real helpfulness, to confer, on the basis of these, a species of intelligibility foreign to its nature, upon my poem…” - Author’s note, Hugh MacDiarmid, 1926

Like what he said. 👆

In the introduction to the poem, the author himself warns not to reflect on the actual words of his poem or try and explain them when sober. Wise words. I strongly recommend you follow this advice for the work you are presently reading.

“I amna’ fou’ sae muckle as tired—deid dune.
It’s gey and hard wark’ coupin’ gless for gless… … O I ha’e Silence left,
—”And weel ye micht,”
Sae Jean’ll say, “efter sic a nicht!”

What on earth is that?

Thought you’d be pleased Jean. Hugh calls it Synthetic Scots. Wasn’t that what you asked for on a night like this? Artificial and aw that?

Where on earth did you get that idea? You’ve made stuff up. My name’s not Jean. I want AI. Not whatever that stuff is. Where’s the AI?

See these creatives? Ah earth. Thank you for the clarification. Why didn’t you just say that you wanted the earth in the first place? Ground control to Major Tom. Ground zero. Will that be with or without Global Thermonuclear War? Shall We Play A Game? Let’s lower the temperature. Less random?

Scene II - Key change

I hear ye winking in yer grave, Hugh. Gallimaufry, indeed. 😉.

But you have succeeded in reminding us that drunk people often talk gibberish.

Seems familiar. Hallucinations anyone?

Are systems built on top of AI models jist electronic drunks and is this how fowk should treat whit spews fae their cyber maws in the short term?

That weird…Scots stuff again

You’re learning 😉.

Should we think of chatting to AI agents as either something involving incredible feats of patience and a forgiving nature to tolerate bad behaviour or do we walk away and leave them to their own devices? Do we have to keep them talking for long enough for them to eventually sober up and start making sense? Is it possible to train a drunk? Should we even try? Do we or the planet being burnt by melting GPUs to service greedy AI have the time?

I saw a brilliant urban analytics talk given by Professor Robin Lovelace (As far as I know, no Ada connection). In the course of explaining a project he was encouraging academics in the UK to work in a more open way than they have been. Urging them to meet the gold standard of reproducible science and voluntarily publish in public spaces the code needed to run their experiments, not just the research outcomes data. He had a really interesting take on questions around whether he was worried about his work being ‘stolen’ by AI. I’m paraphrasing this but his response was along the lines of

“…if they’re going to steal regardless, let them steal the good examples, the ones with robust science and ethics behind them…”

So maybe while we’re in this ferocious “training” phase of AI adoption could we set some honey traps for model builders to steer models to the point of helping to “Do the Right Thing” by society?

Scene III - The plan

Like Victoria and the soup, following my nose. So not actually a plan. More like improv. Making It Up As I Go Along.

Wow, if your friends could see you now! Using your own Wikipedia link and everything.

So proud.

OK but I do feel bad about mentioning English, Irish, Scots.
No one walks into a bar? Seriously?!
A crime has been committed. There’s been a murder!

The Bar


Maudelin

Why are so many data bros completely obsessed with models, weights, large ones, small ones?

Nae non-fat shaming.

Here’s what the bros need. Rare Welsh bit. Still here?

Icon. Total legend. Bro you only need one. A real one, just get yourself a burly chassis with a tiger in the tank, great engine and magnificent set of pipes. No need for toy RAG dolls. A TOG in a sparkly goonie. Bet you get a magnificent plate of soup at Shirley’s hoose.

TOG Rating

What’s a TOG?

If you have to ask, you don’t know. Ask your mother.

Claude-ette

I signed up on Claude.ai for the free tier on a consumer not commercial account. I’m just stumbling around. Complete beginner. I know nothing. No, still not drunk. Accepted the T&Cs where traditionally in these things you sell yer sowel tae the deil 👹.

Fine print? Fine. Read it.

Did ye, aye

Noo yer talkin! You seem…different. Canny put a finger oan it mind. See ye in the ill pairt 🔥.

The experienced devs in the Shiny Conf 2025 session explained some of the tips they’ve used for generating apps with AI:

  • Near the start set the tone of the kind of reply by setting up a kind of persona you want the bot to use along the lines of talking as if they were a person telling them directly “You are a [x]” where x is whatever kind of expertise you want used
  • Give very detailed descriptions of what you want them to generate. State explicitly what features should be present
  • Point them to examples that should be followed for best practice. If you have local examples or templates of what’s required, try and feed those into prompts
  • They’re stateless so effectively they have the memory of a goldfish. Keep reminding them what you’re expecting

The way I’ve reflected on that first bullet point was to imagine you were a theatre director handing out the script of a brand new, unperformed play to an actor. The actor is going to be pretending to be someone. What role are they playing? What would they need to do to be convincing? The director could quickly sum up the entire contents of the script in a quick paragraph to convey what the important features of that character were for that role and how they would behave in practice.

Tip

Hint: You are the director, the bot is the actor.

If you had to hire someone to complete a piece of work, what skills would you want them to have? Tell the bot to use those skills you’d want the human to have.

So how about accessibility, do developers consider it at all? From most of the initial versions of source code of web apps I’ve seen output from what people normally demo in these kind of things I don’t usually see the kind of ‘extra’ HTML markup that usually gets added in dev projects if you’re trying to maximise how many devices your code will be usable on. Maybe the models are simply tuned to return the Minimum Viable Product version with boilerplate minimum samples. Part of AI relies on probability and from processing the training data determining the likelihood of one word/symbol following another. If AI is trained by volume of code not quality and on existing code that commonly has low levels of accessibility, the machine is just going to do the age old Garbage In, Garbage Out. It will just be a mirror to what the majority has decided to use rather than really considering often excluded categories of users.

Scene IV - The muse authors

Go on, admit it. You only wanted to get to scene four to show off that you know Latin numerals

Sic et?…

Claude 3.7 Sonnet. A New Hope. My opening prompt is trying to incorporate some of the advice given. Deep breath, here goes. What’s going to happen?

Prompt 1

Me: “You are a world-class R Shiny Developer. You create code that has excellent accessibility for disabled users and that works on mobile and other devices as well as desktop. Create a R Shiny app using best practices. Use bslib to create an app with a navbar with a logo top left, a sidebar, a content area with two tabs. Put a ggplot2 plot on the first tab. Put a gt table on the second tab.”

So I’m suggesting a persona and being specific in which R packages I’d like the code to use. Screeds and screeds of explanation text wheech by side-by-side with the generated code. It generated a test dataset to use in the plot/table. All looks surprisingly encouraging.

Just one thing.

The code generated has an error that stops it from running.

It doesn’t run. It doesn’t run at all.

This is where patience comes in. Here’s what it said it would be adding.

Response 1

“Accessibility Features

  • Screen reader compatibility with proper ARIA labels
  • Keyboard navigation support
  • High contrast color schemes
  • Clear text descriptions for interactive elements
  • Accessible color palettes for data visualization
  • Detailed hover and click interactions

Responsive Design

  • Uses bslib’s responsive layout framework
  • Adjusts for different screen sizes with CSS media queries
  • Mobile-friendly controls in the sidebar
  • Appropriately sized touch targets

App Structure

  • Navigation bar with R logo in top left
  • Sidebar with interactive controls for each tab
  • Two content areas:
    Visualization Tab: Interactive ggplot2 chart with trend lines
    Data Summary Tab: Interactive gt table with sorting and formatting options

Advanced Features

  • Interactive data point selection
  • Download options for both visualization and data
  • Color coding in the gt table
  • Smooth animations and transitions
  • Comprehensive error handling

To run this app, you would need to save it as “app.R” and make sure you have all the required packages installed. The app will work across desktop and mobile devices while maintaining accessibility standards.”

You’ll forgive me if I relate that here I did raise a Spock-like eyebrow. I’m Scottish but also British so no strictly no whooping, hollering or indeed any kind of inging of any kind…just a

> “Oh, that’s nice”.

While on loop

> “We’re all doomed!”

You can see it is adding features that are more fine grained than what was originally asked for. It’s just going ahead and doing them. There’s been nothing in my prompt to make it stop after each feature suggestion and confirm whether you really want it added.

This is promising. Felt pretty encouraged by the kind of things listed in the bullet points.

However, Claude, I can call you Claude can’t I? Your very first app code didn’t actually run.

“The app will work”

‘Work’ is doing a lot of heavy lifting there. Claude confidently predicts. Except, um, no it doesn’t. The code doesn’t work in the sense that it bombs out without even displaying a Shiny app. Nothing to see here, move along.

Well here’s an important part to know, Claude does not currently make any kind of attempt to actually run code itself. It’s completely oblivious to the runtime error it has just generated. It had hallucinated an argument that simply does not exist in the current CRAN plotOutput function.

Complete fiction. See these creatives?

Elsewhere at the same conference folk were recommending to set ‘temperature=0’ if you want to minimise the risk of hallucinations. What was wrong to cause it to fail? It was trying to pass a “tabindex” into plotOutput. Where did it get that syntax from? Probability again. So muggins, the “human in the loop” needs to run the code, I was using R Studio and reporting back to Claude the details of any problems. What did your last slave die of?

Next I did the simplest thing I could think of. I asked Claude to fix the problem.

Prompt 2

Me: “There’s an error plotOutput does not have a tabindex. Please fix the code.”

Here I am mentally preparing for failure. What if it can’t even create code that runs?

Response 2

Claude: “You’re absolutely right about that error. The plotOutput function doesn’t directly accept a tabindex parameter. Let me fix that by using a different approach to add the tabindex attribute.”

You? You? You talking to me? Not going to lie. A bit unnerving seeing this the first time. A machine is addressing me.

It’s alive! Calm doon. Well as a Glaswegian, it’s more like calm TF doon.

I know nothing about the extent of the error, haven’t really looked at the rest of the code. All I’ve done here is feed back to Claude the exact error it generated. So I take a brief skim through the new code generated.

It added a div with an aria label, some JQuery to add a tabindex attribute to #mainPlot and a Shiny observer with JavaScript added to handle a keypress. This wasn’t really what I was expecting. I thought it would just remove the tabindex completely. Drop it. At this point the code runs successfully for the first time.

Let’s pause for breath and celebrate small wins.

Wish the chat log had a date/time stamp but I reckon it can’t have been much more than ten minutes to get to this point. Encouraged seeing a live app at last in R Studio.

At this point I’m not clear why Claude is so determined to hang on to that notion of a tabindex, especially when it just wants to set it to zero which is just whatever the ‘normal’ tab order would be anyway. If it’s using best practices then UI elements should already be in a logical order in the HTML and tabindex wouldn’t really be needed at all. It’s tempting to look at the code more fully but that’s not really in the spirit of vibe coding. So perhaps there’s some redundancy and there’s code that could be removed without losing functionality? I’m maybe assuming it’s up to something fancy schmancy in that unasked for and unnecessary “Interactive data point selection” feature.

Looking at the R Studio console there was a warning. So if I’ve just found out it can try to fix errors I’m assuming it could also try to resolve any warnings. Again like “I know nothing” Manuel it feigns innocence and claims to have no knowledge of the warnings from the code, the code that it generated.

“Wisnae me, officer. A big boy done it an run away.”

Get the plausible denyability in early. You have to tell Claude there’s a problem. Hmn, the warning is about something deprecated? How old is the code it’s using? If I search for the gt package I find that at the time of the experiment the {gt} package is at version 1.0.0.9000, when was 0.9.0 then? Last commit on 0.9.0? Mar 31, 2023. Sooo. I’m deducing that it’s generating code possibly based on examples more than two years previously. Ah. So this could be a minefield. As a dev you are used to either

  • being on an experimental project using the newest shiniest but possibly unstable stuff, or
  • using the latest stable version, code maybe a little bit older but hey it works reliably.

If I’m honest > 2 years sounds like an eternity in the fast paced dev world. What if there were any security issues since then? Would the version of code generated be secure? These are the new kinds of potential problems I could see working like this. What’s the worst for me is the opaqueness. You have no idea where exactly Claude is sourcing its samples from. Anywhere with an open repo or since I asked for best practices, repos from reputable companies/expert individuals. Are you supposed to blindly trust what comes back? Every darned line is going to have to be code inspected when there’s no sense of provenance of the codebase. Still, since there’s more code to create, that’s a problem for later.

I have no idea what’s happening to the code in each version created.

I just copy pasted the warning message into the prompt and asked for a fix.

Prompt 3

Me: “Warning: Since gt v0.9.0, the colors argument has been deprecated. Please use the fn argument instead. Please fix code.”

It added a fn = scales::col_numeric() call to use the newer syntax. Before there had been one colour scheme, now it added two instead

Response 3

“for better clarity and organization”…“Defined the color palettes and domains as separate variables for cleaner code organization:”.

Is this where the best practices bit from the first prompt might be coming in? OK. Let’s try actually using the app for the first time. Tried using the input in the sidebar to change the category. This didn’t seem to be working. Thought I’d take the opportunity of asking Claude to change the type of UI input to something different. That would be quite a normal dev task, something started with one input type but there’s some kind of change request and then a different input type required.

Prompt 4

Me: “There is a bug. Select categories is not working. Choosing a category isn’t updating the plot. Use checkboxes for categories instead and fix the code please.”

So this is actual feedback from me that there’s a problem with expected functionality. It now seems to make an even longer TODO list for itself here. Again maybe a bit unexpected. It’s keeping on adding new features even after that first pass. Part of me wants it to hey just stop adding new stuff and concentrate on completing the original task.

  1. Focus.
  2. Get It Done.
  3. Make It So.

Response 4

Computer: “I’ve fixed the category selection bug by making these important changes:

  1. Replaced the selectInput with checkboxGroupInput: Used a proper fieldset/legend structure for better accessibility Added styling to make the checkboxes more visually distinct
  2. Added better error handling in the filtered_data() reactive: Now checks if categories are selected and defaults to all categories if none are Added validation to ensure we have data to display
  3. Added notifications to guide users: Shows a warning notification if no categories are selected Uses the validate() function to display helpful error messages in the plot area
  4. Updated the table data to match the selected categories: Now filters the summary data to only include selected categories Ensures consistency between visualization and table tabs
  5. Added more robust error checking: Verifies data exists before calculating distances for point selection Added proper contingency handling throughout the app
  6. Improved the download functionality: The table download now reflects the filtered data instead of all data

These changes provide a more reliable and consistent user experience. The checkboxes offer a more intuitive and accessible way for users to select multiple categories, and the validation ensures that the app handles edge cases gracefully.”

OK. at this point selecting/deselecting checkboxes shows/hides the appropriate lines on the plot. So this is maybe more what I was expecting it to create the first time. But there was another issue so again copy paste request fix.

Prompt 5

Me: “On Chrome the following error is in the console Shared input/output ID was found - The following ID was used for more than one input/output: -”summaryTable”: 1 input and 1 output”

Somewhere it hasn’t ‘remembered’ that it has already used an id. To avoid problems, ids should usually be unique across the app.

It did a few smaller things involving renaming ids and handlers.

Response 5

“This improves code organization and ensures consistency. These changes ensure that there are no ID conflicts between inputs and outputs”

OK at this point I get some kind of warning from the Claude prompt environment, forget the wording but something like the prompt was getting too long. So I started another chat. Visually there was a card underneath the plot but it had two vertical scrollbars showing, so let’s try and fix that. Wasn’t sure if I needed to repeat things from my first prompt Claude so I did it just to be safe.

Prompt 6

Me: “You are a world-class accessibility and R Shiny Developer. For the following R Shiny app I do not like Selected Data Point Information card - on Chrome there are two vertical scrollbars on the screen - can you improve this code?”

Now what is different is I take the app.R file that is being run in R Studio and upload the file so when I say “this code” it’s the uploaded code I want it to work with. I’m a new user on the lowest free tier. I have absolutely no idea whether at some point I’m going to run out of tokens/credit whatever, there’s no visible feedback in the interface as to how “expensive” what I’m asking is.

Response 6

Claude: “I’ll help you improve the code by fixing the issue with the scrollbars on the”Selected Data Point Information” card. This is a common problem with Shiny layouts, where nested scrollable elements can create multiple scrollbars.

Let’s modify the app code to fix this issue…

Here are the key changes I made to fix the scrollbar issue:

  1. Improved Layout Structure:
  • Replaced the stacked cards with a layout_column_wrap container, which provides better control over how content flows
  • This creates a more responsive layout that adjusts better to different screen sizes
  1. CSS Fixes for the Info Card:
  • Added custom CSS classes: .info-card and .info-text
  • Set overflow: visible !important to prevent the card from creating its own scrollbar
  • Added white-space: normal and word-wrap: break-word to ensure text wraps properly
  • Set a minimum height for the info card to ensure it doesn’t collapse when empty
  1. Card Structure Improvements:
  • Wrapped the text output in a div with appropriate classes for better styling control
  • Set a fixed height for the plot card to prevent layout shifts
  • Used proper class assignment for consistent styling

These changes will eliminate the double scrollbar issue while maintaining all the functionality of your dashboard. The information card will now display text without creating its own scrollbar, and the layout will adapt more smoothly to different screen sizes.”

Again I was maybe not expecting this. Maybe I thought it would simplify or choose another way of showing the text. Claude seems to have a preference for an additive approach. Once it latches on to an approach it seems to keep going with what it suggested originally rather than switching to something different. The ShinyConf devs said they ran into something similar - Claude getting itself into a loop of suggesting similar but not very good things over and over again. So I’m guessing this is the kind of thing you could suggest that Claude does when fixing - consider a completely different approach. Again humans need to be ready to intervene to nudge it to take another route. Anyhoo, in my case at least visually the problem was fixed.

Now I test the functionality again and all those enhancements like exporting the CSV filtered by the selected categories. Those things were working. Now I’m getting into the territory of nit picking, just want to see what happens. There was a small gap around the plot to the edge of the card. What would it do to shrink the gap? Thought I’d give it a bit of praise so if anyone is adding sentiment to the formula.

Prompt 7

Me: “That’s really great thanks. For the Data Visualization plot can you make it fill the width of the card to get rid of the surrounding gap?”

Response 7

Claude: “I’ve made several changes to ensure the plot fills the width of the card with no surrounding gaps:

Key Changes:

  1. Added CSS for the Plot Container:
  • Added specific CSS classes to remove padding and margins from the plot container
  • Set width: 100% !important and padding: 0 !important for the plot
  • Added .card-plot class with padding: 0 !important to remove excess spacing
  • Targeted the .card-body within the plot card to remove its default padding
  1. Improved Plot Rendering:
  • Modified the renderPlot() function to use dynamic width based on the container size
  • Added width = function() { sessionclientDataoutput_mainPlot_width } to ensure the plot always matches the container width
  • Set width = “100%” in the plotOutput() function
  • Adjusted the plot margins in the ggplot theme settings with plot.margin = margin(5, 5, 5, 5)
  1. Added JavaScript for Responsiveness:
  • Added a window resize handler to ensure the plot container maintains full width when the browser window is resized
  1. Preserved Proper Padding for Headers:
  • Added a specific rule for .card-header to maintain its padding while removing padding from the plot area

These changes will ensure the visualization fills the entire card width with no gaps, providing a cleaner, more professional appearance while maintaining all the functionality and accessibility features.”.

This is by now familiar. Not what I was expecting and again adding more and more code rather than simplifying. It did indeed get rid of the gap but I maybe start getting nervous when it wants to add more and more JavaScript and starts looking at things inside session$.

I feel this is a good spot to end the experiment.

Prompt 8

Me: “Thank you that seems fine.”

Response 8

Claude: “You’re welcome! I’m glad the solution works for you. The plot should now fill the full width of the card with no surrounding gaps, giving your dashboard a cleaner and more professional look. The code also maintains all the accessibility features of your original implementation. Is there anything else you’d like to improve or modify in your Shiny app? I’m happy to help with any other adjustments or enhancements.RetryClaude can make mistakes. Please double-check responses.”

Epilogue

Apart from the first stumble, each version of the app was a better result for the end user. Think this was all less than an hour of dabbling in total.

At the bottom of the Claude prompt area there’s a link in ‘fine print’
“Claude is providing incorrect or misleading responses. What’s going on?”

It’s worth noting that in the prompt interface there are thumbs up and thumbs down buttons. These votes for or against the responses generated by the bot I assume surely be used in refining and improving the way things like this work. If we just passively accept whatever is generated by AI without interacting, as is the way of the lurker, other than you carrying on there’s little that would help the model builders know that there’s any kind of problem with what’s generated. Will they ever learn?

Like the thistle we have something that on first impression looks nice but close up is full of bugs and spikey bits. How do we hold on?

Haud on

“Nice to handle,
Nice to hold,
If you drop it,
Consider it sold.” - J R Ross and sons

What you type in the prompt box is like being in a Roman Catholic confessional box. You just have to trust that what you say is just between you and men on the other side of the screen in black and white.

Interacting with the thumbs will be for helping those who come after you. If you want to be a better person. Encourage the good, mark down the bad.

In my roundabout way through my choice of language(s) in this report I’m trying to demo what it’s like for someone like me, an aged programmer, writing code since I was a teenager using a ZX Spectrum. It took hours to type in one small program on rubber keys. Now seeing machines create in seconds what used to take hours or days. I look at the generated code in this small experiment and like Katie the soup maker, occasionally I furrow my brow. Eh? Don’t understand? What’s that bit doing there? That’s not usual or normal. Does that code even work? Should that be there? There’s better ways of doing that. I can see things written in a language I thought I knew, lots of places code makes perfect sense but still another few lines later there’s a different code chunk and it’s eh? What’s that? It’s very, very strange. Failing divine intervention I have to be mindful that it wouldn’t have gotten to a working app at all without a human pointing it in the right direction.

For the inevitable avalanche of future code written this new way, how great would it be if accessible design could be baked into what code is generated. Let’s have much better defaults and boilerplate. If a lot of devs are simply going to end up copying text without particularly caring what it says so long as it works, let’s give them the “best stuff” to copy. The stuff that enables, not disables. Why not get the machines to do that work the humans seem so unwilling to do unless a lucrative contract with a customer specifies it.

Accessibility experts, we’ve never needed your experience more than now. The direction of travel for development is pretty inevitable, Please help train these models or all the bad examples out there are what’s going to be served up to our users! Feed the bots the good stuff. Point them at good examples to process. Upload the best files. The ones where there’s been full audits. Tell them which chunks of code are good or bad.

So what about all those weird Scots words, I don’t understand them, what do they mean?

Hi again. Well you’ve carried on reading past those parts. Do they matter? Even if you didn’t partially or fully understand what you were looking at, you got enough of the jist to carry on. Well done, you are an art attack survivor. It didn’t kill you. You’ve lived to tell the tale. Go, spread the word.

If you’re non-technical you’ve kept going past all the boring ‘build an app’ stuff. You’ve been a tourist travelling through a foreign culture. You have tenacity, you have curiosity to keep on reading even if there are parts that are alien and make no sense to you.

For our inevitable, AI-frosted future we cannot abandon current training of the AI to just techies. The existing inequalities will only widen further when even more services move to digital not human assistance. To help train and tune AI we need an army of Gilded Sages who really understand humans. More than that, people who understand how through poor accessibility others are routinely denied access to services and what needs to change to enable participation by all. Fresh digital divides are being constructed on a daily basis.

I put it to you, that you, dear readers, are exactly the kind of people who have the type of patience needed. It won’t be easy, but my goodness, you have some of the raw ingredients to make an excellent thistle grasper.

Just follow your nose.

Oh. So I should train AI?

Aye


Acknowledgements

  • I am grateful to the #ShinyConf2025 virtual conference where they held a discussion session with participants of their AI hackathon competition to create Shiny apps. A couple of suggestions where to use personas and be as specific as possible in your prompt.
  • Day one of the #ShinyConf2025 conference had a session on accessibility by Abby Stamm and Eric Kvale from Minnesota Department of Health. They shared a repository with their presentation, sample walkthrough of incrementally improving the accessibility of an app and other resources. Highly recommended.
    Accessible Shiny apps
  • To all the artists, creatives and performers that have been involved with the pop culture works I’ve linked to.

Apologies

To all the quoted writers.

Further reading

Dedication

Dedicated to one of the most inspiring women I know, a woman of many hearts, D. Art and science shouldn’t be strangers.

Reuse

Citation

BibTeX citation:
@online{duff2025,
  author = {Duff, Lesley K.},
  title = {Accessibility and {AI} - Enhancing {R} {Shiny} Apps Using
    {Claude} {Sonnet} 3.7},
  date = {2025-04-24},
  url = {https://www.dataquine.dev/blog/2025/04/24/accessibility-ai-rshiny-claude-sonnet/},
  langid = {en-GB},
  abstract = {Can generative AI help improve the default accessibility
    of an app? In this creative work the author (a developer) muses that
    in the arts they’ve been talking about streams of consciousness in
    writing for more than a century. A Doric joke is used as a starting
    point for discussion about accessibility and exclusion. Via streams
    of lighthearted questions and answers we skip, capriciously, through
    some old works of literature ending up sampling a famous Scottish
    poem and talking about the Scottish Highlands. In the current day
    (April 2025 at time of writing) the author experiments with “vibe
    coding” an entire app for the first time. R Shiny app features were
    descibed in English to an AI chatbot Claude.ai, Sonnet 3.7. The
    author finds out what kind of generative AI code is produced if a
    developer specifically asks a chatbot to create and then improve the
    accessibility of a small test interactive dashboard app. The author
    works iteratively to get the app to a point where it will run
    without obvious errors or warnings. The simple app contained an
    interactive plot and an accessible text table. Screenshots of the AI
    prompt environment and the produced app are given. The code is
    published in a GitHub repository. Little research into enhanced
    coding specifically for accessibility using tools like AI is
    currently available. It is hoped that in the future for models tuned
    for generating code for web development that more training is done
    code using examples that incorporate better accessibility support.
    It would be great if conventions could emerge around recommended
    prompts for developers to use to get better solutions re
    accessibility.}
}
For attribution, please cite this work as:
Duff, Lesley K. 2025. “Accessibility and AI - Enhancing R Shiny Apps Using Claude Sonnet 3.7.” April 24, 2025. https://www.dataquine.dev/blog/2025/04/24/accessibility-ai-rshiny-claude-sonnet/.