css-orientation-lock
CSS must not lock the page to a single orientation
Checks that no @media (orientation: portrait|landscape) rule sets a transform: rotate(...) on the page, a known technique for defeating device orientation, and asks about any such rule that hides the page's main content.
Applies to. Any accessible (same-document, non-cross-origin) stylesheet, inline <style> blocks and same-origin <link> stylesheets already loaded into document.styleSheets, sheets adopted by the document, and those of each open shadow root, whose rules are matched against that root's elements.
Expectation. No @media (orientation: portrait) or @media (orientation: landscape) block sets a transform/-webkit-transform/rotate rotation of approximately 90 degrees (mod 180, i.e. ~90 or ~270), the well-known technique for visually forcing one orientation regardless of the device's actual orientation, which defeats WCAG 1.3.4's requirement that content not restrict its view to a single display orientation unless that orientation is essential. Such a rotation is judged on the visible elements its selector matches (ACT b33eff), visibility judged without the rotation itself: it fails where one of them holds the page's content (as defined below for hidden content), and any other element turned that way is asked about (cantTell): an arrow pointing to a panel beside it in one orientation and below it in the other locks nothing. A rule that turns no visible element, or only a pseudo-element, has nothing to judge. A second shape is asked about (cantTell): an orientation media block that hides the page's content with display: none or visibility: hidden, the usual form of WCAG F100 (content replaced by a "rotate your device" message in one orientation). The hidden element counts as the page's content when it is html or body, the main landmark, an ancestor of it, or, on a page without a main landmark, an element holding at least half of the body's text. Where the page has no <main> and no role="main", a custom element whose ElementInternals give it the main role (this.attachInternals().role) is the main landmark. Whether the orientation is essential, and whether the page stays usable, is left to a person.
What a finding reports.
mediaText(a rotation or hidden content): the media query that holds the rule, such as(orientation: portrait). A rotation of an element that doesn't hold the page's content is reported against that element (ORIENTATION_MEDIA_ROTATES_ELEMENT).selectorText(a rotation or hidden content): the selector of the style rule that rotates or hides. Empty when it could not be read.unreadableSheetCount(STYLESHEETS_NOT_READABLE): how many style sheets could not be read, usually because they come from another origin.
These data.details fields help to read and reproduce a finding. Apart from reasonCode and the few fields Output schema names as stable, they are not a stable contract: a field may be renamed or dropped in a minor release, so don’t build on them.
Examples
<html><head><title>Page title</title><style>body { background: #fff; }</style></head><body>Hi</body></html>
No orientation media query rotates the page.
<html><head><title>Page title</title><style>@media (orientation: landscape) {body { transform: rotate(90deg); }}</style></head><body>Hi</body></html>
A landscape media query forces a 90-degree rotation, locking the page to portrait regardless of device orientation.
How to check
When this rule can’t decide, it reports cantTell and leaves the question to a person. Each question it can ask is listed here under its reason code, with the steps to answer it. A finding carries the same steps as howToCheck, in the scan’s language. See Output schema.
ORIENTATION_MEDIA_HIDES_CONTENT
- Using the device toolbar in the developer tools or a real device, view the page in portrait and then in landscape.
- In the orientation the report names, check what is hidden and what is shown instead.
- Check whether the same content and functions are still available, such as through a different layout.
- Decide whether one orientation is essential (such as a piano app or a bank cheque photo).
Pass. The content stays available in both orientations, or a single orientation is essential.
Fail. Content is hidden and, for example, a "rotate your device" message is shown, so the page is unusable in that orientation, which fails WCAG 1.3.4 (F100).
ORIENTATION_MEDIA_ROTATES_ELEMENT
- Find the element the report names and note what it is (an icon, banner, panel, part of the content).
- Using the device toolbar in the developer tools or a real device, view the page in portrait and then in landscape.
- Check whether the element turns sideways in one orientation and whether that makes content hard to read or use.
Pass. The rotated element is small or decorative, and all content can be read and used in both orientations.
Fail. Content turns sideways in one orientation so it cannot be read or used normally, which fails WCAG 1.3.4 (ACT b33eff).
STYLESHEETS_NOT_READABLE
- Using the device toolbar in the developer tools or a real device, view the page in portrait.
- Switch to landscape and reload; then switch back.
- In each orientation, check that all content is shown upright, can be read and is not replaced by a "rotate your device" message.
Pass. The page shows its content upright and works in both orientations.
Fail. The page is locked to one orientation (content hidden, rotated sideways or replaced by a message), which fails WCAG 1.3.4.