id: mod-a11y-click-noninteractive
language: tsx
severity: error
message: non-interactive element with onClick lacks keyboard affordances
note: '{"severity":"high","category":"convention","resolution":"Use a semantic interactive element, or add role, onKeyDown, and tabIndex together when clickability is intentional. Elements carrying spread props are not flagged."}'
files:
  - '**/packages/ui-*/src/**/*.tsx'
  - '**/packages/*-react/src/**/*.tsx'
  - '**/.slopgate/fixtures/src/**/*.tsx'
ignores:
  - '**/*.test.*'
rule:
  any:
    - kind: jsx_opening_element
      all:
        - has: { field: name, regex: '^(div|span)$' }
        - has: { kind: jsx_attribute, has: { kind: property_identifier, regex: '^onClick$' } }
      not:
        any:
          - all:
              - has: { kind: jsx_attribute, has: { kind: property_identifier, regex: '^role$' } }
              - has: { kind: jsx_attribute, has: { kind: property_identifier, regex: '^onKeyDown$' } }
              - has: { kind: jsx_attribute, has: { kind: property_identifier, regex: '^tabIndex$' } }
          - has: { kind: jsx_expression }
    - kind: jsx_self_closing_element
      all:
        - has: { field: name, regex: '^(div|span)$' }
        - has: { kind: jsx_attribute, has: { kind: property_identifier, regex: '^onClick$' } }
      not:
        any:
          - all:
              - has: { kind: jsx_attribute, has: { kind: property_identifier, regex: '^role$' } }
              - has: { kind: jsx_attribute, has: { kind: property_identifier, regex: '^onKeyDown$' } }
              - has: { kind: jsx_attribute, has: { kind: property_identifier, regex: '^tabIndex$' } }
          - has: { kind: jsx_expression }
