FA-11431 / Accessibility interaction semantics / Open access
A nested header is exposed as a page-wide banner landmark · case 01
Section-local headers and footers appear in the page landmark inventory, or unnamed sections create noisy regions.
ROOT CAUSE
HTML tag names are mapped to landmark roles without the required naming and ancestor context.
VERIFIED REPAIR
Require names for form/section landmarks and inspect the complete ancestor chain for header/footer sectioning context.
Unsuccessful approach: Checking only the nearest ancestor still mistakes a header wrapped inside an article for a page-wide banner.
Case contract
This deliberately bounded native landmark adapter accepts tag, accessible name, and nearest-first ancestor tag names. main/nav/aside map to main/navigation/complementary. Named form/section map to form/region; empty or ASCII-whitespace-only names yield None. header/footer map to banner/contentinfo only outside every article, aside, main, nav or section ancestor. Other tags yield None. This adapter does not implement author role overrides or full HTML semantics.
Why this case matters
A controlled offline accessibility-data model. It isolates the stated contract; it does not simulate browser accessibility APIs or claim full ARIA conformance.
1 / The failure
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(tag, name, ancestors):
return {'main':'main','nav':'navigation','aside':'complementary','form':'form','section':'region','header':'banner','footer':'contentinfo'}.get(tag)
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
check('deep local header', solve('header', str(N), ['div']*N+['article','body']), None)
check('deep local footer', solve('footer', str(N), ['div']*N+['main','body']), None)
check('page header', solve('header', str(N), ['div']*N+['body']), 'banner')
check('page footer', solve('footer', '', ['body']), 'contentinfo')
check('unnamed section', solve('section', ' '*N, ['body']), None)
check('named section', solve('section', 'Results '+str(N), ['body']), 'region')
check('unnamed form', solve('form', '', []), None)
check('named form', solve('form', str(N), []), 'form')
check('unconditional navigation', solve('nav', '', ['article']), 'navigation')
check('nonlandmark', solve('div', str(N), []), None)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| deep local header | banner | None | Failed |
| deep local footer | contentinfo | None | Failed |
| page header | banner | banner | Passed |
| page footer | contentinfo | contentinfo | Passed |
| unnamed section | region | None | Failed |
| named section | region | region | Passed |
| unnamed form | form | None | Failed |
| named form | form | form | Passed |
| unconditional navigation | navigation | navigation | Passed |
| nonlandmark | None | None | Passed |
SHA-256 / 6912ca38d7428809618ee2705037b35dcbd781dce49cf426f2979731770cbddf
2 / The unsuccessful fix
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(tag, name, ancestors):
if tag in ('form','section') and not name.strip(''.join(map(chr, [32,9,13,10,12,11]))): return None
if tag in ('header','footer') and ancestors and ancestors[0] in {'article','aside','main','nav','section'}: return None
return {'main':'main','nav':'navigation','aside':'complementary','form':'form','section':'region','header':'banner','footer':'contentinfo'}.get(tag)
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
check('deep local header', solve('header', str(N), ['div']*N+['article','body']), None)
check('deep local footer', solve('footer', str(N), ['div']*N+['main','body']), None)
check('page header', solve('header', str(N), ['div']*N+['body']), 'banner')
check('page footer', solve('footer', '', ['body']), 'contentinfo')
check('unnamed section', solve('section', ' '*N, ['body']), None)
check('named section', solve('section', 'Results '+str(N), ['body']), 'region')
check('unnamed form', solve('form', '', []), None)
check('named form', solve('form', str(N), []), 'form')
check('unconditional navigation', solve('nav', '', ['article']), 'navigation')
check('nonlandmark', solve('div', str(N), []), None)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| deep local header | banner | None | Failed |
| deep local footer | contentinfo | None | Failed |
| page header | banner | banner | Passed |
| page footer | contentinfo | contentinfo | Passed |
| unnamed section | None | None | Passed |
| named section | region | region | Passed |
| unnamed form | None | None | Passed |
| named form | form | form | Passed |
| unconditional navigation | navigation | navigation | Passed |
| nonlandmark | None | None | Passed |
SHA-256 / 19bbb31ca97f3b4b1b108b716aad15deecd1eaae0e467e8f094e73fe6da4a6ce
3 / The verified repair
Exit 0"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(tag, name, ancestors):
if tag in ('form','section') and not name.strip(''.join(map(chr, [32,9,13,10,12,11]))): return None
if tag in ('header','footer') and any(parent in {'article','aside','main','nav','section'} for parent in ancestors): return None
return {'main':'main','nav':'navigation','aside':'complementary','form':'form','section':'region','header':'banner','footer':'contentinfo'}.get(tag)
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
check('deep local header', solve('header', str(N), ['div']*N+['article','body']), None)
check('deep local footer', solve('footer', str(N), ['div']*N+['main','body']), None)
check('page header', solve('header', str(N), ['div']*N+['body']), 'banner')
check('page footer', solve('footer', '', ['body']), 'contentinfo')
check('unnamed section', solve('section', ' '*N, ['body']), None)
check('named section', solve('section', 'Results '+str(N), ['body']), 'region')
check('unnamed form', solve('form', '', []), None)
check('named form', solve('form', str(N), []), 'form')
check('unconditional navigation', solve('nav', '', ['article']), 'navigation')
check('nonlandmark', solve('div', str(N), []), None)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| deep local header | None | None | Passed |
| deep local footer | None | None | Passed |
| page header | banner | banner | Passed |
| page footer | contentinfo | contentinfo | Passed |
| unnamed section | None | None | Passed |
| named section | region | region | Passed |
| unnamed form | None | None | Passed |
| named form | form | form | Passed |
| unconditional navigation | navigation | navigation | Passed |
| nonlandmark | None | None | Passed |
SHA-256 / 13c3a15a938b9c49a09ccebbfc51d247b71a90654b898a60c3ecaf6a0c02b5ff
Verification & scope
This reproducer isolates one failure mechanism. Results cover the supplied fixtures. Variants within a family share a test contract and should remain grouped when constructing evaluation splits. Related mechanisms with a shared evaluation_group must also remain together; these controlled models are not independent production incidents.
Observations recorded using Python 3.12.14 at 2026-09-29T14:38:47.907254+00:00.
Case digest / 5cd85c5a288926421c92c64cd857edb843d5cae0da20a2f3422be1da26d9056a