• Stars
    star
    1,092
  • Rank 42,434 (Top 0.9 %)
  • Language
    JavaScript
  • License
    MIT License
  • Created about 7 years ago
  • Updated over 1 year ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

It is a trap! A lock for a Focus. 🔓

REACT FOCUS LOCK

it-is-a-trap
  • browser friendly focus lock
  • matching all your use cases
  • trusted by best UI frameworks
  • the thing Admiral Ackbar was talking about

CircleCI status npm bundle size downloads


It is a trap! We got your focus and will not let him out!

  • Modal dialogs. You can not leave it with "Tab", ie do a "tab-out".
  • Focused tasks. It will aways brings you back, as you can "lock" user inside a component.
  • Any any other case, when you have to lock user intention and focus, if that's what a11y is asking for.

Trusted

Trusted by Atlassian AtlasKit, ReachUI, SmoothUI, Storybook and we will do our best to earn your trust too!

Features

  • no keyboard control, everything is done watching a focus behavior, not emulating it. Thus works always and everywhere.
  • React Portals support. Even if some data is in outer space - it is still in lock.
  • Scattered locks, or focus lock groups - you can setup different isolated locks, and tab from one to another.
  • Controllable isolation level.
  • variable size bundle. Uses sidecar to trim UI part to 1.5kb.

💡 focus locks is only the first part, there are also scroll lock and text-to-speech lock you have to use to really "lock" the user. Try react-focus-on to archive everything above, assembled in the right order.

How to use

Just wrap something with focus lock, and focus will be moved inside on mount.

 import FocusLock from 'react-focus-lock';

 const JailForAFocus = ({onClose}) => (
    <FocusLock>
      You can not leave this form
      <button onClick={onClose} />
    </FocusLock>
 );

Demo - https://codesandbox.io/s/5wmrwlvxv4.

WHY?

From MDN Article about accessible dialogs:

  • The dialog must be properly labeled
  • Keyboard focus must be managed correctly

This one is about managing the focus.

I've got a good article about focus management, dialogs and WAI-ARIA.

API

FocusLock would work perfectly even with no props set.

FocusLock has few props to tune behavior, all props are optional:

  • disabled, to disable(enable) behavior without altering the tree.
  • className, to set the className of the internal wrapper.
  • returnFocus, to return focus into initial position on unmount(not disable).

By default returnFocus is disabled, so FocusLock will not restore original focus on deactivation.

This is expected behavior for Modals, but it is better to implement it by your self. See unmounting and focus management for details

  • persistentFocus=false, requires any element to be focused. This also disables text selections inside, and outside focus lock.
  • autoFocus=true, enables or disables focusing into on Lock activation. If disabled Lock will blur an active focus.
  • noFocusGuards=false disabled focus guards - virtual inputs which secure tab index.
  • group=''' named focus group for focus scattering aka combined lock targets
  • shards=[] an array of ref pointing to the nodes, which focus lock should consider and a part of it. This is another way focus scattering.
  • whiteList=fn you could whitelist locations FocusLock should carry about. Everything outside it will ignore. For example - any modals.
  • as='div' if you need to change internal div element, to any other. Use ref forwarding to give FocusLock the node to work with.
  • lockProps={} to pass any extra props (except className) to the internal wrapper.
  • hasPositiveIndices=false to support a focus lock behavior when any elements tabIndex greater than 0.
  • crossFrame=true enables aggressive focus capturing within iframes

Focusing in OSX (Safari/Firefox) is strange!

By default tabbing in OSX sees only controls, but not links or anything else tabbable. This is system settings, and Safari/Firefox obey. Press Option+Tab in Safari to loop across all tabbables, or change the Safari settings. There is no way to fix Firefox, unless change system settings (Control+F7). See this issue for more information.

Set up

Requirements

  • version 1x is React 15/16 compatible
  • version 2+ requires React 16.8+ (hooks)

Import

react-focus-lock exposed 3 entry points: for the classical usage, and a sidecar one.

Default usage

  • 4kb, import FocusLock from 'react-focus-lock would give you component you are looking for.

Separated usage

Meanwhile - you dont need any focus related logic until it's needed. Thus - you may defer that logic till Lock activation and move all related code to a sidecar.

  • UI, 1.5kb, import FocusLockUI from 'react-focus-lock/UI - a DOM part of a lock.
  • Sidecar, 3.5kb, import Sidecar from 'react-focus-lock/sidecar - which is the real focus lock.
import FocusLockUI from "react-focus-lock/UI";
import {sidecar} from "use-sidecar";

// prefetch sidecar. data would be loaded, but js would not be executed
const FocusLockSidecar = sidecar(  
  () => import(/* webpackPrefetch: true */ "react-focus-lock/sidecar")
);

<FocusLockUI
    disabled={this.state.disabled}
    sideCar={FocusLockSidecar}
>
 {content}
</FocusLockUI> 

That would split FocusLock into two pieces, reducing app size and improving the first load. The cost of focus-lock is just 1.5kb!

Saved 3.5kb?! 🤷‍♂️ 3.5kb here and 3.5kb here, and your 20mb bundle is ready.

Autofocus

Use when you cannot use the native autoFocus prop - because you only want to autofocus once the Trap has been activated

  • prop data-autofocus on the element.
  • prop data-autofocus-inside on the element to focus on something inside.
  • AutoFocusInside component, as named export of this library.
 import FocusLock, { AutoFocusInside } from 'react-focus-lock';
 
 <FocusLock>
   <button>Click</button>
   <AutoFocusInside>
    <button>will be focused</button>
   </AutoFocusInside>
 </FocusLock>
 // is the same as
 
 <FocusLock>
   <button>Click</button>
    <button data-autofocus>will be focused</button>
 </FocusLock>

If there is more than one auto-focusable target - the first will be selected. If it is a part of radio group, and rest of radio group element are also autofocusable(just put them into AutoFocusInside) - checked one fill be selected.

AutoFocusInside will work only on Lock activation, and does nothing, then used outside of the lock. You can use MoveFocusInside to move focus inside with or without lock.

 import { MoveFocusInside } from 'react-focus-lock';
    
 <MoveFocusInside>
  <button>will be focused</button>
 </MoveFocusInside>

Portals

Use focus scattering to handle portals

  • using groups. Just create a few locks (only one could be active) with a same group name
const PortaledElement = () => (
   <FocusLock group="group42" disabled={true}>
     // "discoverable" portaled content
   </FocusLock>  
);

<FocusLock group="group42">
  // main content
</FocusLock>
  • using shards. Just pass all the pieces to the "shards" prop.
const PortaledElement = () => (
   <div ref={ref}>
     // "discoverable" portaled content
   </div>  
);

<FocusLock shards={[ref]}>
  // main content
</FocusLock>
  • without anything. FocusLock will not prevent focusing portaled element, but will not include them in to tab order
const PortaledElement = () => (
   <div>
     // NON-"discoverable" portaled content
   </div>  
);

<FocusLock shards={[ref]}>
  // main content
  <PortaledElement />
</FocusLock>

Using your own Components

You may use as prop to change what Focus-Lock will render around children.

<FocusLock as="section">
    <button>Click</button>
    <button data-autofocus>will be focused</button>
 </FocusLock>
 
 <FocusLock as={AnotherComponent} lockProps={{anyAnotherComponentProp: 4}}>
    <button>Click</button>
    <span>Hello there!</span>
 </FocusLock>

Guarding

As you may know - FocusLock is adding Focus Guards before and after lock to remove some side effects, like page scrolling. But shards will not have such guards, and it might be not so cool to use them - for example if no tabbable would be defined after shard - you will tab to the browser chrome.

You may wrap shard with InFocusGuard or just drop InFocusGuard here and there - that would solve the problem.

import {InFocusGuard} from 'react-focus-lock';

// wrap with
<InFocusGuard>
  <button />
</InFocusGuard>

// place before and after
<InFocusGuard />
<button />
<InFocusGuard />

InFocusGuards would be active(tabbable) only when tabble, it protecting, is focused.

Removing Tailing Guard

If only your modal is the last tabble element on the body - you might remove the Tailing Guard, to allow user tab into address bar.

<InFocusGuard/>
<button />  
// there is no "tailing" guard :)

Unmounting and focus management

  • In case FocusLock has returnFocus enabled, and it's going to be unmounted - focus will be returned after zero-timeout.
  • In case returnFocus is set to false, and you are going to control focus change on your own - keep in mind

React will first call Parent.componentWillUnmount, and next Child.componentWillUnmount

This means - Trap will be still active by the time you may want move(return) focus on componentWillUnmount. Please deffer this action with a zero-timeout.

Similarly, if you are using the disabled prop to control FocusLock, you will need a zero-timeout to correctly restore focus.

<FocusLock
  disabled={isFocusLockDisabled}
  onDeactivation={() => {
    // Without the zero-timeout, focus will likely remain on the button/control
    // you used to set isFocusLockDisabled = true
    window.setTimeout(() => myRef.current.focus(), 0);
  }
>

Return focus with no scroll

read more at the issue #83 or mdn article.

To return focus, but without jumpy page scroll returning a focus you might specify a focus option

<FocusLock
  returnFocus={{ preventScroll: false }} // working not in all browsers
>   

Not supported by Edge and Safari.

Not only for React

Uses focus-lock under the hood. It does also provide support for Vue.js and Vanilla DOM solutions

Warning!

Two different focus-lock-managers or even different version of a single one, active simultaneously will FIGHT!

Focus-lock will surrender, as long any other focus management library will not.

Focus fighting

You may wrap some render branch with FreeFocusInside, and react-focus-lock will ignore any focus inside marked node, thus landing a peace.

import { FreeFocusInside } from 'react-focus-lock';

<FreeFocusInside>
 <div id="portal-for-modals">
   in this div i am going to portal my modals, dont fight with them please
 </div>
</FreeFocusInside>

Even the better is to whiteList FocusLock areas - for example "you should handle only React Stuff in React Root"

<FocusLock whiteList={node => document.getElementById('root').contains(node)}>
 ...
</FocusLock>

PS: please use webpack or yarn resolution for force one version of react-focus-lock used

webpack.conf

 resolve: {    
    alias: {
      'react-focus-lock': path.resolve(path.join(__dirname, './node_modules/react-focus-lock'))
 ...

More

To create a "right" modal dialog you have to:

You may use react-focus-on to achieve everything above, assembled in the right order.

Licence

MIT

More Repositories

1

react-imported-component

✂️📦Bundler-independent solution for SSR-friendly code-splitting
TypeScript
651
star
2

react-remove-scroll

Removes and disables 📜in a "React" way
TypeScript
611
star
3

rewiremock

The right way to mock dependencies in Node.js or webpack environment.
JavaScript
442
star
4

memoize-state

The magic memoization for the State management. ✨🧠
JavaScript
324
star
5

react-focus-on

🎯 Solution for WAI ARIA compatible modal dialogs or full-screen tasks, you were looking for
TypeScript
271
star
6

use-callback-ref

🤙The same useRef, but it will callback
TypeScript
265
star
7

devolution

🦎 -> 🦖A de-evolution gun for your bundle!
JavaScript
189
star
8

react-prerendered-component

🤔Partial hydration and caching in a pre-suspense era
TypeScript
164
star
9

react-locky

"🔒-y" – Asgardian God of Event Scoping 📦, Scroll Locking 📜, Silence Casting 🙊
JavaScript
145
star
10

focus-lock

Gotcha! A11y util for scoping a focus.
TypeScript
139
star
11

vue-focus-lock

It is a trap! A lock for a Focus. A11y util for scoping a focus.
Vue
135
star
12

react-memoize

🧠 React memoization library we all deserve
JavaScript
129
star
13

react-shallow-context

☘️A speed optimization for a Context API
TypeScript
119
star
14

faste

Table based 📦 Finite State Machine 🤖
TypeScript
119
star
15

used-styles

📝All the critical styles you've used to render a page.
TypeScript
115
star
16

use-sidecar

Another way to code splitting
TypeScript
94
star
17

stylelint-semantic-groups

Opinionated rule ordering
TypeScript
92
star
18

beautiful-react-redux

Redux 🚀, Redux 🤘, Redux 🔥 - and the magic optimization
JavaScript
89
star
19

recondition

🤷‍♂️ Declarative render prop based Conditions
TypeScript
77
star
20

proxyequal

There is a bit more smart way to compare things, than a shallow equal.
JavaScript
72
star
21

kashe

A memoization library based on weakmaps. 🤯 Sometimes cache is kashe
TypeScript
63
star
22

react-remove-scroll-bar

Remove document scroll bar. Nothing more
TypeScript
59
star
23

react-scroll-locky

📜🔒 – React full-cream "anti-scroll" library, you were looking for
TypeScript
56
star
24

restate

A redux fractal state library 🕷
JavaScript
55
star
25

dom-focus-lock

It's a Trap! A11y util for scoping a focus.
JavaScript
52
star
26

react-event-injector

💉React way to addEventListener
TypeScript
45
star
27

React-stroller

🚶‍♂️Scroll as you Stroll - the most native custom scrollbars ever made
TypeScript
45
star
28

react-remock

Get any Component replaced. Anywhere. 🛠♻️
TypeScript
44
star
29

multiple-entry-points-example

a npm library with multiple entry points, all typed, all shipped in cjs/esm versions
TypeScript
43
star
30

react-reflexible

🐍 Responsible solution in a flexible form.
TypeScript
43
star
31

aria-hidden

🗣Cast aria-hidden to everything, except...
TypeScript
42
star
32

why-did-you-update-redux

Patch Redux to discover unnecessary re-renders
JavaScript
41
star
33

jsx-compress-loader

⚛️JSX optimisation loader to reduce size of React application
JavaScript
40
star
34

holistic-image

Wholesome image management
TypeScript
37
star
35

theKashey

This is me! 👨‍🔧And all the things I've done. 🧙🏻‍♂️
36
star
36

eslint-plugin-relations

Controls relationships between folders and packages 👩‍💻🤝👮🏻‍♂️
TypeScript
32
star
37

react-gearbox

⚙️📦 Gearbox - Renderless state provisioning and composition
TypeScript
30
star
38

styled-components-mixins

Use popular frameworks with Styled Components
JavaScript
30
star
39

react-on-time

Renderless composable ⏰timers and ⏱intervals
TypeScript
27
star
40

runtime-compress-loader

babel and typescript "runtime" helpers optimization loader
JavaScript
23
star
41

react-queue

⛓ Declarative task scheduler
TypeScript
23
star
42

plimited

👽Promise-based Resource Pool
TypeScript
15
star
43

flow-meter

http(s/2) chunked response meter
TypeScript
14
star
44

react-style-singleton

Simple stylesheet manager for good libraries
TypeScript
14
star
45

use-callback-state

The same `useState`, but it will callback - https://codesandbox.io/s/use-callback-state-fcxtb
TypeScript
14
star
46

require-control

Get the full control over the nodejs module system.
JavaScript
14
star
47

webpack-imported

📝stats-webpack-plugin and 💩webpack-flush-chunks had a baby!
TypeScript
13
star
48

react-rename

Create a renamed duplicate for any Component. For the Fun, and Debug ( sure 🤞)
TypeScript
11
star
49

ts-referent

🤖 project references made not by humans
TypeScript
9
star
50

partial-mock

A solution for ⬆️over and ⬇️under mocking
TypeScript
9
star
51

storybook-include

Folder based storybook configuration for monorepos
TypeScript
9
star
52

react-nyan-stroller

🐈🏳️‍🌈🏳️‍🌈🏳️‍🌈 Nyanyanyanyanyanyanya! (In form of custom scroll bar) https://codesandbox.io/s/j2l71rrxw
TypeScript
9
star
53

quadLoader

загрузчик по пирамиде
8
star
54

react-imported-library

✂Code-split any library using renderProps!
TypeScript
8
star
55

yarn-unlock-file

🧶🔓 keeping things up to date.
TypeScript
8
star
56

read-story-later

Defer storybook's story execution.
JavaScript
7
star
57

react-push-channel

🎈Context is to drill props down. React Push Channel to drill props up.
TypeScript
7
star
58

useReselect

hooks friendly reselect
TypeScript
6
star
59

address-complete

What are you looking for?
JavaScript
6
star
60

idea-exclude

Some your .ideas better be excluded
TypeScript
6
star
61

search-trie

Yet another O(n) trie. This time just and only for string search.
TypeScript
6
star
62

jest-typed-mock

Be safe!. And use the force of TS/Flow to make mocks better.
JavaScript
6
star
63

tileLoader

тайловый загрузчик для yandex2
5
star
64

react-loadable-library

Code-split any library using renderProps!
TypeScript
5
star
65

react-svg-atlas

Combine, isolate, skyrocket the SVG
JavaScript
4
star
66

the-country-names

How to explain the world? Split it into pieces and name the each one.
JavaScript
4
star
67

wipeWebpackCache

JavaScript
3
star
68

css-to-js-loader

Transforms your CSS to the `real` JS
JavaScript
3
star
69

package-self

Place yourself in the right place.
JavaScript
3
star
70

postcss-to-js

A way to create javascript code from css
JavaScript
3
star
71

react-side-channel

To the RenderProps and back.
TypeScript
3
star
72

react-dom-reflection

🔨React meets DOM-API
TypeScript
3
star
73

with-known-usage

👁ES5/ES6 compatible object key usage tracker
TypeScript
3
star
74

proxyquire-webpack-alias

Modification of proxyquire to work with webpack aliases. Proxies commonjs require/es6 import in order to allow overriding dependencies during testing.
JavaScript
3
star
75

restructor

Change your mind 🤯. Face naming standards 🤦‍♀️. Easily rename JavaScript modules 🧙🏻‍♂️.
JavaScript
3
star
76

scan-directory

Multithreaded nodejs directory scan
JavaScript
3
star
77

what-did-i-load

puppeteer based resource investigation
JavaScript
2
star
78

react-fiber-walker

Walks live React Fiber
TypeScript
2
star
79

function-double

Stand in for a function
JavaScript
2
star
80

vue-demi-focus-lock

Vue
2
star
81

derive-svg-components

Derive React components from SVG base
TypeScript
2
star
82

y2FastOverlay

Один из варианта получения скорости при работе с оверлеями напрямую
2
star
83

wipeNodeCache

JavaScript
2
star
84

compare-module-exports

JavaScript
2
star
85

flow-meter-ui

https://flow-meter-ui.vercel.app/
HTML
2
star
86

storybook-addon-poleaxe

a11y (poly) axe
TypeScript
2
star
87

4D

old school C++ stuff
C++
1
star
88

hoist-react-statics

A little helper for a better HOCs
JavaScript
1
star
89

rewiremock-simple-example

testing stuff
JavaScript
1
star
90

maps

различные околокартографические примеры
1
star
91

immutable-changes

Get will found the root change!
JavaScript
1
star
92

gulp-html-prefix

Simple gulp plugin to prefix classNames
JavaScript
1
star