# Role Without Required Attributes

**MUST** · **ID:** `interactions-rams-role-attributes` · **Category:** interactions
**Source:** [RAMS](https://www.rams.ai/)
**Interactive version:** https://ui-guides-agent-rules.netlify.app/principles/interactions-rams-role-attributes

> MUST: Use semantic HTML elements (<button>, <a>, <nav>) before ARIA roles. If using role="button" on a div, also add tabIndex="0" and keyboard handlers.

ARIA roles must include all required attributes

Rams ships this as a Moderate checklist row, not prose, and its only example is role="button" without tabIndex="0". The general rule (ours, from the ARIA spec rather than Rams): a role is a promise about behavior, and incomplete ARIA is often worse than none. role="checkbox" needs aria-checked; role="slider" needs aria-valuenow, aria-valuemin, and aria-valuemax.

## Bad — do not do this

`interactions-rams-role-attributes-bad`

```tsx
import { useState } from 'react';
import { HugeiconsIcon } from '@hugeicons/react';
import { CheckmarkSquare01Icon, SquareIcon } from '@hugeicons/core-free-icons';

export function RamsRoleAttributesBad() {
  const [checked, setChecked] = useState(false);

  return (
    <div className="w-full max-w-sm space-y-4">
      <div className="bg-card border border-border rounded-lg p-4">
        <h4 className="font-medium mb-3">Div Pretending to be Button</h4>
        <div className="space-y-4 p-3 bg-muted rounded-lg">
          {/* BAD: Using div with role="button" but NO keyboard handlers */}
          {/* Keyboard users cannot activate this! */}
          <div
            role="button"
            onClick={() => alert('Clicked!')}
            // BAD: Missing onKeyDown for Enter/Space!
            // BAD: Missing tabIndex for focus!
            className="px-4 py-2 bg-primary text-primary-foreground rounded-md cursor-pointer inline-block"
          >
            Click Me (div)
          </div>

          <div className="flex items-center gap-3">
            {/* BAD: role="checkbox" without aria-checked AND without keyboard handler */}
            <div
              role="checkbox"
              // BAD: Missing aria-checked={checked}!
              // BAD: Missing onKeyDown for Space!
              onClick={() => setChecked(!checked)}
              className="cursor-pointer"
            >
              {checked ? (
                <HugeiconsIcon icon={CheckmarkSquare01Icon} size={24} className="text-primary" />
              ) : (
                <HugeiconsIcon icon={SquareIcon} size={24} />
              )}
            </div>
            <span className="text-sm">Enable notifications</span>
          </div>

          {/* BAD: Clickable div with no semantic meaning */}
          <div
            onClick={() => alert('Link clicked')}
            className="text-primary underline cursor-pointer"
            // BAD: Should be <a> or <button>, not div
            // BAD: No role, no tabIndex, no keyboard handler
          >
            Click this link (it's a div)
          </div>
        </div>
        <div className="mt-3 bg-muted rounded p-2 font-mono text-xs">
          <code className="text-error">{'<div role="button" onClick={...}>'}</code>
          <p className="mt-1 text-error">No keyboard support!</p>
        </div>
      </div>
      <p className="text-xs text-error">
        Screen reader says "button" but keyboard can't activate it
      </p>
    </div>
  );
}
```

## Good — do this

`interactions-rams-role-attributes-good`

```tsx
import { useState } from 'react';
import { HugeiconsIcon } from '@hugeicons/react';
import { CheckmarkSquare01Icon, SquareIcon } from '@hugeicons/core-free-icons';

export function RamsRoleAttributesGood() {
  const [checked, setChecked] = useState(false);
  const [value, setValue] = useState(50);

  return (
    <div className="w-full max-w-sm space-y-4">
      <div className="bg-card border border-border rounded-lg p-4">
        <h4 className="font-medium mb-3">Complete ARIA Attributes</h4>
        <div className="space-y-4 p-3 bg-muted rounded-lg">
          <div className="flex items-center gap-3">
            <div
              role="checkbox"
              aria-checked={checked}
              aria-label="Enable notifications"
              tabIndex={0}
              onClick={() => setChecked(!checked)}
              onKeyDown={(e) => {
                if (e.key === ' ' || e.key === 'Enter') {
                  e.preventDefault();
                  setChecked(!checked);
                }
              }}
              className="cursor-pointer"
            >
              {checked ? (
                <HugeiconsIcon icon={CheckmarkSquare01Icon} size={24} className="text-primary" />
              ) : (
                <HugeiconsIcon icon={SquareIcon} size={24} />
              )}
            </div>
            <span className="text-sm">Enable notifications</span>
          </div>
          <div className="space-y-2">
            <label className="text-sm" id="volume-label">Volume: {value}%</label>
            <input
              type="range"
              role="slider"
              aria-valuenow={value}
              aria-valuemin={0}
              aria-valuemax={100}
              aria-labelledby="volume-label"
              value={value}
              onChange={(e) => setValue(Number(e.target.value))}
              className="w-full"
            />
          </div>
        </div>
        <div className="mt-3 bg-muted rounded p-2 font-mono text-xs">
          <code>role="checkbox" aria-checked=&#123;checked&#125;</code>
        </div>
      </div>
      <p className="text-xs text-success">
        Screen reader announces: "Enable notifications, checkbox, checked"
      </p>
    </div>
  );
}
```

## References

- [Rams review checklist](https://www.rams.ai/rams.md)
- [WCAG 4.1.2](https://www.w3.org/WAI/WCAG21/Understanding/name-role-value.html)
