{"id":18376834,"url":"https://github.com/bbc/lrud-spatial","last_synced_at":"2026-03-07T20:32:09.261Z","repository":{"id":177287421,"uuid":"251294930","full_name":"bbc/lrud-spatial","owner":"bbc","description":"Left, Right, Up, Down. A spatial navigation library for devices with input via directional controls.","archived":false,"fork":false,"pushed_at":"2026-02-02T13:13:37.000Z","size":621,"stargazers_count":53,"open_issues_count":1,"forks_count":7,"subscribers_count":26,"default_branch":"master","last_synced_at":"2026-02-02T23:48:06.983Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/bbc.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":"CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2020-03-30T12:16:15.000Z","updated_at":"2026-02-02T13:13:41.000Z","dependencies_parsed_at":null,"dependency_job_id":"d5273bf4-4806-4fb7-9b55-3114fea179e6","html_url":"https://github.com/bbc/lrud-spatial","commit_stats":null,"previous_names":["bbc/lrud-spatial"],"tags_count":15,"template":false,"template_full_name":null,"purl":"pkg:github/bbc/lrud-spatial","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bbc%2Flrud-spatial","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bbc%2Flrud-spatial/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bbc%2Flrud-spatial/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bbc%2Flrud-spatial/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bbc","download_url":"https://codeload.github.com/bbc/lrud-spatial/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bbc%2Flrud-spatial/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30229744,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-07T19:01:10.287Z","status":"ssl_error","status_checked_at":"2026-03-07T18:59:58.103Z","response_time":53,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-06T00:24:59.801Z","updated_at":"2026-03-07T20:32:09.222Z","avatar_url":"https://github.com/bbc.png","language":"JavaScript","readme":"\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://github.com/bbc/lrud-spatial/blob/master/.github/lrud.svg?raw=true\" alt=\"LRUD spatial\"/\u003e\n\u003c/p\u003e\n\nMove focus around a HTML document using Left, Right, Up, Down keys.\n\n## API\n\n\u003cpre\u003e\ngetNextFocus(\u003ci\u003ecurrentFocus\u003c/i\u003e, \u003ci\u003ekeyOrKeyCode\u003c/i\u003e, \u003ci\u003e[scope]\u003c/i\u003e)\n\u003c/pre\u003e\n\n### Parameters\n\n- `currentFocus` should be an\n  [`HTMLElement`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement)\n  that you want LRUD spatial to consider as the element you are navigating _from_.\n  In simple applications, this can just be a reference to `document.activeElement`.\n- `keyOrKeyCode` should be a\n  [`key`](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key) string or\n  a [`keyCode`](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/keyCode)\n  decimal representing the directional key pressed.\n- `scope` is an optional `HTMLElement` that you only want to look for focusable candidates inside of. Defaults to the whole page if not provided.\n\n### Returns\n\nAn `HTMLElement` that LRUD spatial thinks you should\nnavigate _to_.\n\n## Focusables\n\nLRUD spatial defines focusable elements as those which match any of the\nfollowing CSS selectors:\n\n- `[tabindex]` (for tabindex \u003e= 0)\n- `a`\n- `button`\n- `input`\n\n### Ignoring Focusables\n\nAny potential candidate with the `lrud-ignore` class, or inside any parent with the `lrud-ignore` class, will not be considered focusable and will be skipped over. By default LRUD will not ignore candidates that have `opacity: 0` or have a parent with `opacity: 0`, so this class can be used for that.\n\nFocusables with a `tabindex=\"-1\"` attribute will be skipped over, however any focusable inside any parent with `tabindex=\"-1\"` will still be considered focusable.\n\nPotential candidates with `disabled` or `aria-disabled=\"true\"` attributes will not be considered focusable.\n\n### Focusable Overlap\n\nBy default, LRUD will measure to all candidates that are in the direction to move. It will also include candidates that overlap the current focus by up to 30%, allowing for e.g. a 'right' movement to include something that is above the current focus, but has half of it's size expanding to the right.\n\nThis threshold can be adjusted on a per-element basis with the `data-lrud-overlap-threshold` attribute, as a float from 0.0 to 1.0. An overlap of 0.0 will make a candidate only be considered if it is located _entirely_ in the direction of movement.\n\nPlease also note that LRUD does not consider the Z Axis, which can cause surprising results with elements that are overlapped in this way, including in the case of full screen overlays on existing UIs. The above attribute can help alleviate this issue.\n\n## Containers\n\nFocusables can be wrapped in containers. Containers can keep track of the last\nactive focusable item within them using a `data-focus` attribute.\n\n\u003e Focusables that should be tracked must have a unique ID attribute.\n\nWhen a container has no previous focus state, it's first focusable element is\nused instead.\n\nAt this time, containers are defined as matching the CSS selectors:\n`nav`, `section` or `.lrud-container`.\n\n### Block exits\n\nIn some instances, it is desirable to prevent lrud-spatial from selecting another\n\"best candidate\" for example at the bottom of a list. To do this, a container element\nshould add an additional `data-block-exit` attribute to prevent further selection in\nthat direction. This should be a space separated list.\n\nE.g.\n\n```html\n\u003cdiv class=\"lrud-container\" data-block-exit=\"up down\"\u003e...\u003c/div\u003e\n```\n\n### Focusable Containers\n\nBy default, LRUD only measures the distances to focusables and does not consider where their container boundaries are. Adding the attribute `data-lrud-consider-container-distance` to a container will include it in the distance calculations, as well as its children.\n\nIf the container is the closest out of all the possible focusables assessed, LRUD will return one of its children - even if they are not necessarily the spatially closest focusable.\n\nThe above container focus logic will still be used, and moving into a focusable container will move to its last focused child if there was one, at any level of container depth inside it.\n\n\n## How does it work?\n\nTo determine the next element that should be focused;\n\n1. Uses the key code to get the direction of movement\n2. From the currently focused DOM element, get the coordinates of the edge\n   corresponding to the direction you are moving\n3. For all other focusable elements, get the coordinates of the opposite edge\n   (the edge you are entering)\n4. Find the line between the exit and entry coordinates that has the shortest\n   length\n\n## Developing\n\nRequirements: Node.js 18\n\nTo get started, run `npm ci`.\n\n### Building\n\n`npm run build` will emit a transpiled and minified version of the library.\nThis is run during CI to prepare the artifact published to NPM. It can also be\nuseful for integrating against another local project with `npm link`.\n\n### Testing\n\nThe `test/layouts` directory contains HTML designed to mirror various\nreal-world use cases, and allow for behavioural vertification of library\nfeatures such as containers and grids.\n\nSignificant new features should come with corresponding layouts that test them.\n\nUse `npm test` to run the tests.\n\n#### Debugging\n\nThe tests use [puppeteer](https://github.com/puppeteer/puppeteer) to spin up a\nheadless browser. The browser loads the layouts mentioned above and runs\nscenarios from [lrud.test.js](./test/lrud.test.js) against the unminified\ncode from `lib/`.\n\nTo investigate why a test is failing, or just to hack on some changes... run\n`npm run server`. Then go to [http://localhost:3005](http://localhost:3005) and\nselect a layout.\n\n## Contact\n\n[TVOpenSource@bbc.co.uk](mailto:TVOpenSource@bbc.co.uk) - we aim to respond to emails within a week.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbbc%2Flrud-spatial","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbbc%2Flrud-spatial","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbbc%2Flrud-spatial/lists"}