# Moodle Accessibility

> Use when ensuring Moodle plugin UI meets WCAG 2.1 AA — semantic HTML in Mustache, ARIA via core helpers, keyboard navigation, color contrast in SCSS, focus management in modals, screen reader testing, and Pa11y/axe automation.

- **Type:** Skill
- **Install:** `agentstack add skill-saadrahman01-claude-moodle-dev-moodle-accessibility`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [SaadRahman01](https://agentstack.voostack.com/s/saadrahman01)
- **Installs:** 0
- **Category:** [Search](https://agentstack.voostack.com/c/search)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [SaadRahman01](https://github.com/SaadRahman01)
- **Source:** https://github.com/SaadRahman01/claude-moodle-dev/tree/main/skills/moodle-accessibility

## Install

```sh
agentstack add skill-saadrahman01-claude-moodle-dev-moodle-accessibility
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# Moodle Accessibility (WCAG 2.1 AA)

## Overview

Moodle targets WCAG 2.1 AA. UI in plugins must follow same standard for inclusion. Boost theme + Bootstrap 5 + Moodle's component library do most of the heavy lifting — your job is to use them correctly.

## When to Use

- Building any UI (Mustache template, modal, custom form element)
- Reviewing PR for accessibility regression
- Submission to moodle.org plugin directory (a11y is reviewed)
- Responding to a user-reported a11y bug

## Top rules

1. **Use semantic HTML**: `` for actions, `` for navigation, ``–`` in order
2. **Every interactive element keyboard-reachable** with visible `:focus`
3. **Every image** has `alt`; decorative images: `alt=""`
4. **Color contrast** ≥ 4.5:1 (text), 3:1 (UI components)
5. **Forms**: `` linked to every input
6. **Don't use color alone** to convey meaning
7. **Modals trap focus**, restore on close

## Semantic Mustache

```mustache
{{! BAD }}
Save

{{! GOOD }}
{{#str}}save, core{{/str}}
```

Headings:

```mustache
{{#str}}announcements, local_example{{/str}}
...
```

Skip levels = WCAG fail. `` then `` is wrong.

## Forms — formslib

`MoodleQuickForm` outputs `` automatically. Don't bypass:

```php
$mform->addElement('text', 'name', get_string('name', 'local_example'));
$mform->setType('name', PARAM_TEXT);
$mform->addRule('name', null, 'required', null, 'client');

// Help button — adds aria-described relationship
$mform->addHelpButton('name', 'name', 'local_example');
```

For required fields, `addRule('required')` adds `aria-required="true"`.

## Buttons / links

| Element | Use for |
|---------|---------|
| `` | Form submit |
| `` | JS action |
| `` | Navigation to a URL |

Never `` — use ``. Never `` unless you also handle keyboard (Enter + Space) and focus.

## Icons

```mustache
{{#pix}}t/edit, core, {{#str}}edit, core{{/str}}{{/pix}}
```

Pix renderer outputs `` with the title as alt. For decorative icons accompanying visible text:

```mustache

  {{#pix}}t/edit, core{{/pix}}
  {{#str}}edit, core{{/str}}

```

Bootstrap 5: `.visually-hidden` (was `.sr-only` in BS4).

## Color contrast

Boost defines `$primary`, `$secondary`, etc. Don't override to low-contrast values.

```scss
// theme/yourtheme/scss/post.scss
$primary: #0f6fc5;     // contrast vs white = 5.13:1 ✓
$danger:  #d9534f;     // contrast vs white = 3.34:1 ✗ — fails AA for text
```

Test: https://webaim.org/resources/contrastchecker/

Don't rely on color only:

```mustache
{{! BAD — color only }}
{{name}}

{{! GOOD — icon + color }}

  {{#pix}}t/error, core, {{#str}}invalid, local_example{{/str}}{{/pix}}
  {{name}}

```

## Tables

```mustache

  {{#str}}attendance, local_example{{/str}}
  
    
      {{#str}}user, core{{/str}}
      {{#str}}status, core{{/str}}
    
  
  
    {{#rows}}
    
      {{name}}
      {{status}}
    
    {{/rows}}
  

```

`html_table` from `lib/outputcomponents.php` renders accessibly:

```php
$table = new \html_table();
$table->head = [get_string('name'), get_string('status')];
$table->headspan = [1, 1];
$table->caption = get_string('attendance', 'local_example');
echo \html_writer::table($table);
```

## ARIA — minimal use

Native HTML > ARIA. Only add ARIA when no native equivalent.

```mustache
{{! tab pattern — needs ARIA }}

  One
  Two

...
```

Bootstrap 5 tab JS handles arrow keys. Don't reimplement.

## Modals

Use `core/modal` — handles focus trap, ESC key, focus restore on close.

```javascript
import Modal from 'core/modal';

const modal = await Modal.create({
    title: await getString('confirm', 'core'),
    body: await Templates.render('local_example/confirm', {}),
    show: true,
    removeOnClose: true,
});
// focus auto-traps inside; on close, focus returns to trigger element
```

Custom focus management:

```javascript
import {trapFocus} from 'core/local/aria/focusmanager';
const release = trapFocus(modalEl);
// ... when closing:
release();
triggerEl.focus();
```

## Live regions (toasts, async updates)

```javascript
import {add as addToast} from 'core/toast';
addToast('Saved', {type: 'success'});
```

`core/toast` uses `aria-live="polite"`. For urgent alerts: `aria-live="assertive"` (sparingly — interrupts screen readers).

## Keyboard

Every interactive control:
- Focusable in source order (don't `tabindex="0"` everything)
- Activated with Enter / Space
- Visible focus ring (don't `outline: 0` without replacement)
- Custom widgets follow [WAI-ARIA Authoring Practices](https://www.w3.org/WAI/ARIA/apg/patterns/)

## Screen reader testing

| OS | SR | Browser |
|----|----|---------|
| Windows | NVDA (free) | Firefox |
| macOS | VoiceOver | Safari |
| Linux | Orca | Firefox |
| iOS | VoiceOver | Safari |
| Android | TalkBack | Chrome |

Quick checks:
- Tab through entire UI — every interactive element reachable
- Use SR-only — does the experience make sense?
- Resize text 200% — layout still works?
- Disable CSS — content order still meaningful?

## Automation

```bash
# axe-core via Puppeteer
npm install -g @axe-core/cli
axe http://localhost:8000/local/example/

# Pa11y
npm install -g pa11y
pa11y --standard WCAG2AA http://localhost:8000/local/example/
```

CI integration: run on PRs against staging.

## Common Mistakes

| Mistake | Fix |
|---------|-----|
| `` | `` |
| Missing `alt` on `` | Add — `alt=""` if decorative |
| `placeholder` as label | Add real `` |
| `outline: 0` no replacement | Restore visible focus indicator |
| Heading level skip | Sequential ``/``/`` |
| Link text "click here" / "more" | Descriptive (`Edit user "Alice"`) |
| `role="button"` without keyboard | Use `` instead |
| Color-only error indication | Add icon + text |
| Color contrast ` with no role | Either add role or use text instead |
| `aria-hidden="true"` on focusable element | Either unhide or remove from tab order |

## Plugin directory review

moodle.org reviewers run a11y checks. Common rejection reasons:
- New custom modal without focus trap
- Hard-coded colors failing AA contrast
- Custom widgets without ARIA / keyboard
- Missing `` on form fields

## References

- Accessibility: https://moodledev.io/general/development/policies/accessibility
- WCAG 2.1: https://www.w3.org/TR/WCAG21/
- WAI-ARIA APG: https://www.w3.org/WAI/ARIA/apg/
- Boost a11y: https://docs.moodle.org/dev/Boost_-_Accessibility
- pix renderer: https://moodledev.io/docs/apis/subsystems/output#pix-icons

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [SaadRahman01](https://github.com/SaadRahman01)
- **Source:** [SaadRahman01/claude-moodle-dev](https://github.com/SaadRahman01/claude-moodle-dev)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-saadrahman01-claude-moodle-dev-moodle-accessibility
- Seller: https://agentstack.voostack.com/s/saadrahman01
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
