# How do I prompt for JavaScript that works with a keyboard?

Source: https://codelifter.com/forum/posts/how-do-i-prompt-for-javascript-that-works-with-a-keyboard/
Site: CodeLifter Forum, category Vibe coding prompts
Published: 2026-10-16. Updated: 2026-10-25. Replies: 2.

## Question by CodeLifter editors (staff), 2026-10-16

Spell it out in the prompt, control by control: real button and link elements for anything clickable, which keys do what, where focus goes after an action, and that the focus outline stays visible. "Make it accessible" is too vague to check. Then put the mouse away and test the result with Tab, Enter, Space, Escape and the arrow keys.

## What "works with a keyboard" means

The baseline is WCAG success criterion 2.1.1. The W3C's [Understanding 2.1.1: Keyboard](https://www.w3.org/WAI/WCAG22/Understanding/keyboard.html) states it as: all functionality of the content is operable through a keyboard interface, without specific timings for individual keystrokes, except where the function depends on the path of the user's movement. The same page is clear that dragging an object to a location does not count as path dependent, so a draggable box still needs a keyboard way to move it.

The page also lists the failure that one-line prompts tend to produce: using only pointing-device-specific event handlers for a function (failure F54). A `div` with a click listener is the usual example.

## Lines to put in the prompt

Each line below names an element, a key or a focus rule, so you can check it in the answer. The quoted lines come from the CodeLifter prompts, whose code the editors tested; the prompts themselves have not been run against a named assistant.

| You want | Prompt line | Where it comes from |
| --- | --- | --- |
| Controls that reach the keyboard by default | "Use real `<button>` elements for Previous and Next" | [Slideshow prompt](https://codelifter.com/prompts/slideshow/) |
| A dialog that opens and closes from the keyboard | "Put autofocus on a visible Close button, close the dialog when the backdrop is clicked, leave Escape to the browser" | Image popup prompt |
| Focus back where the user was | "return focus to the thumbnail that opened it" | Image popup prompt |
| A custom widget that keys can move | "Make it focusable with tabindex="0", give it an accessible name that mentions the arrow keys, and move it 10 pixels per arrow key press, or 50 with Shift, calling preventDefault only for the arrows" | [Draggable element prompt](https://codelifter.com/prompts/draggable-element/) |
| Links that still behave like links | "leave clicks with any button other than the main one, and clicks with Ctrl, Cmd, Shift or Alt held, to the browser" | Go back prompt |

Add two general lines to any interactive prompt:

- "Use `<button type="button">` for every control that is not a link. Never put a click handler on a `div` or `span`."
- "Do not remove the focus outline. If you restyle it, use `:focus-visible` and keep it clearly visible."

The phrase "calling preventDefault only for the arrows" matters more than it looks. A key handler that calls `preventDefault()` on every key also swallows Tab, and the user is stuck.

## Why native elements first

MDN's [tabindex reference](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Global_attributes/tabindex) explains what you get for free: links with an `href`, buttons, form fields and `summary` are focusable by default, and developers should not add `tabindex` to them unless they are changing that behaviour. It also warns against a common shortcut: making a `div` focusable and using it as a button. Such components are not listed in the accessibility tree, so assistive technology cannot find or operate them.

Two more rules from the same page are worth putting in a rules file:

1. Only use `tabindex="0"` or `tabindex="-1"`. Positive values change the Tab order and make the page hard to navigate.
2. Never put `tabindex` on a `<dialog>`.

## Keep focus visible

Success criterion 2.4.7 asks that keyboard focus is visible in at least one mode of operation. The W3C's [Understanding 2.4.7: Focus Visible](https://www.w3.org/WAI/WCAG22/Understanding/focus-visible.html) lists styling outlines and borders so that the indicator disappears as a failure (F78), and lists CSS `:focus-visible` as a sufficient technique (C45). In generated CSS, look for `outline: none` or `outline: 0` with nothing put back in its place.

## Where a prompt cannot fix it alone

Some patterns need more than keys. WCAG 2.5.7 says dragging must also be possible with a single pointer without dragging, and a keyboard equivalent alone does not meet it. The CodeLifter draggable element says openly that its code does not meet 2.5.7 by itself. If you need that, ask for it by name: "Add buttons or number inputs that move the element without dragging."

## Test it in five minutes

1. Click the address bar, then press Tab until focus enters your component. Every control should get a visible outline, in a sensible order.
2. Press Shift+Tab to go back. Focus should move backwards through the same stops.
3. Press Enter on links, and Enter and Space on buttons. Both keys should activate a button.
4. Open any dialog. Focus should land inside it; Escape should close it and put focus back on the control that opened it.
5. On custom widgets, try the arrow keys you asked for, with and without Shift.
6. Tab past the component. If you cannot leave it with the keyboard, a handler is swallowing Tab.

If any step fails, quote the missing prompt line back to the assistant and ask for that change alone. The [innerHTML thread](/forum/posts/how-do-i-stop-a-coding-assistant-from-using-innerhtml/) covers the other check worth running on every answer, and the [local models thread](/forum/posts/do-the-codelifter-prompts-work-with-local-models/) covers running these prompts on your own machine.

## Reply 1 by CodeLifter editors (staff), 2026-10-19

**Does a div with role="button" work with the keyboard?** Not by itself. MDN's [button role page](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Roles/button_role) says adding `role="button"` tells a screen reader the element is a button but provides none of the usual behaviour. On a native `<button>`, the `click` event fires for mouse clicks and when the user presses Space or Enter while it has focus. On any other element, the click event fires only for the pointer, even with the role set. To make a `div` or `span` work you have to add `tabindex="0"` so it can take focus, then a `keydown` handler that runs the action on Enter and Space and ignores every other key. MDN names `keydown` and warns against the deprecated `keypress`. A toggle also needs `aria-pressed` kept in step by your script. That is three or four separate things an assistant can forget, and a mouse user will never notice any of them missing. MDN's own advice is to use `<button>` or `<input type="button">` instead. Put that in the prompt as one line: "Use a real `<button type="button">`; do not put `role="button"` on another element." Then run the Tab, Enter and Space checks from the opening post.

## Reply 2 by CodeLifter editors (staff), 2026-10-25

**How do you keep keyboard focus inside a modal dialog?** Use the native `<dialog>` element opened with `showModal()`, and the browser does most of the work. MDN's [showModal() page](https://developer.mozilla.org/en-US/docs/Web/API/HTMLDialogElement/showModal) says the dialog is shown in the top layer with a `::backdrop`, and the rest of the page becomes inert, meaning it cannot be focused or clicked while the dialog is open. You do not need a hand-written trap that cycles Tab between the first and last control. Both `<dialog>` and `showModal()` have worked across browsers since March 2022. Three things are left for your prompt. Put `autofocus` on the control the user needs first, or on the Close button when nothing else needs immediate interaction, as MDN's [dialog reference](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/dialog) recommends. Keep a visible Close button in the Tab order, which the W3C's [modal dialog pattern](https://www.w3.org/WAI/ARIA/apg/patterns/dialog-modal/) strongly recommends. And return focus to the element that opened the dialog when it closes, unless that element no longer exists. Escape closes a modal dialog without any code. If an assistant builds its own overlay `div` with a keydown trap, ask for `<dialog>` and `showModal()` by name.

---
Cite as: CodeLifter Forum, "How do I prompt for JavaScript that works with a keyboard?", https://codelifter.com/forum/posts/how-do-i-prompt-for-javascript-that-works-with-a-keyboard/, accessed 2026-11-12.
