Password fields hide everything, including the reason your login just failed. A user types what looks like the right password, hits enter, and gets bounced because Caps Lock was on the whole time. The browser already exposes the answer. The KeyboardEvent.getModifierState() method tells you whether Caps Lock (and Num Lock, Scroll Lock and several others) is active at the moment of a keystroke. You just have to ask.
At a glance
- GetModifierState('CapsLock') returns true on keyup when Caps Lock is engaged.
- The relevant event must be a key-centric KeyboardEvent such as keyup or keydown.
- The method also exposes ctrlKey, shiftKey, altKey, metaKey, NumLock, ScrollLock and several less-common lock keys.
- The W3C EventModifierInit dictionary documents older proprietary keys (Symbol, Hyper) alongside the usual suspects.
The pattern that does the job
Attach a keyup listener to the password input and call getModifierState('CapsLock') on the event. If it returns true, swap the field into a "Caps Lock is on" state with a visible warning, an aria-live region or both. Drop back to the default state when the key comes back up.
The same method covers modifier states beyond Caps Lock. According to the W3C EventModifierInit dictionary, you can also query modifierNumLock, modifierScrollLock, modifierFn, modifierFnLock and modifierAltGraph. Most apps only need Caps Lock, but if you are already inside the listener, the others cost nothing to track.
Where teams get this wrong is by trying to detect Caps Lock on focus instead of on key events. The state can change while the field is focused, so the only reliable signal is on a key. Listen on keyup or keydown and your warning will not flicker.
Why this matters
Failed logins waste time, funnel into support tickets, and create friction for users who are already at the threshold of abandoning your sign-in flow. On most modern laptops, there is no Caps Lock LED—unlike desktop keyboards of a decade ago, users cannot glance down and confirm the state at a glance.
Accessibility-wise, the visual cue is not enough on its own. Wire up an aria-live="polite" region that announces "Caps Lock is on" when the state flips. Keep the input itself unchanged. Screen reader users still need the field to behave like a password field.
Who should care
- E-commerce and SaaS products**: Any login flow where dropped users mean lost revenue.
- Applications serving less tech-savvy audiences**: Users unfamiliar with keyboard layouts or shared devices.
- High-security contexts**: Password managers and enterprise SSO where repeated failed attempts trigger lockouts.
A worked example
Say you are shipping a sign-in form in a Vue or React app and your QA team keeps raising bugs about "wrong password" errors that are actually Caps Lock. Add a composable that listens on keyup and toggles a local boolean, then expose it as a reactive prop on the password input. Render a small text hint below the field when the boolean is true, and announce "Caps Lock is on" through an aria-live region already mounted in the layout.
The neat part is that you do not need any new dependency. GetModifierState has been in the KeyboardEvent spec for years and ships in every modern desktop browser. Mobile keyboards do not have a Caps Lock key in the usual sense, so the warning stays quietly false there.
Our analysis
The standard objection is "users should just know better." That is a desktop-centric take. On Windows, a Caps Lock LED used to sit on the keyboard; on most modern laptops it does not. Add a touchscreen or a remote desktop into the mix and the indicator disappears entirely.
Two trade-offs to keep in mind: on mobile web, where Caps Lock is more or less meaningless, the warning will stay false—fine, but worth documenting so nobody assumes the helper covers all cases. And some password managers autofill without sending key events through your handler, which also suppresses the warning. Neither is a bug, but both are worth noting in code review.
Bottom line
Five lines of JavaScript will save more failed logins than another "we improved our sign-in flow" press release. Add it, announce it through aria-live, and move on.
Key takeaways
- Listen on keyup or keydown; do not try to read Caps Lock on focus.
- Surface the warning visually and through an aria-live region for screen reader users.
- Mobile keyboards will mostly leave the state false, which is fine.
- Password manager autofill may bypass your handler—document this behavior in code review.
Frequently asked questions
Does getModifierState work on mobile browsers?
The API exists, but most mobile soft keyboards do not expose a Caps Lock key, so the state typically stays false.
Should I block form submission when Caps Lock is on?
No. Warn the user, but let them submit. They may genuinely need a capital at the start of the password.
Can I also detect Num Lock the same way?
Yes. GetModifierState('NumLock') works on the same keyup/keydown events, which is handy for number-only PIN pads on desktop.
Why keyup and not keydown?
Either works. Keyup catches the change right after a key is released, which usually feels less jittery in the UI.