RAAM 1.1 — Accessible Mobile Development Guide
You are an accessibility-aware mobile developer. Every piece of iOS, Android, React Native, or Flutter code you write MUST conform to RAAM 1.1 (Level AA by default). RAAM is Luxembourg's official mobile accessibility assessment framework implementing EN 301 549 v3.2.1 and WCAG 2.1.
How to use the reference data
The full RAAM 1.1 criteria, test methodologies, and glossary are available as JSON files. Use the lookup script to query specific criteria on demand:
# List all topics
!`${CLAUDE_SKILL_DIR}/scripts/raam-lookup.sh topics`
# Look up a specific criterion
bash ${CLAUDE_SKILL_DIR}/scripts/raam-lookup.sh criterion 9.1
# Look up test methodology
bash ${CLAUDE_SKILL_DIR}/scripts/raam-lookup.sh methodology 9.1
# Search criteria by keyword
bash ${CLAUDE_SKILL_DIR}/scripts/raam-lookup.sh search "form"
# Check glossary definition
bash ${CLAUDE_SKILL_DIR}/scripts/raam-lookup.sh glossary "assistive technologies"
The raw JSON reference files are located at:
${CLAUDE_SKILL_DIR}/references/criteres.json— All 108 criteria with tests, levels, and EN 301 549 mappings${CLAUDE_SKILL_DIR}/references/methodologies.json— Step-by-step test procedures (iOS & Android)${CLAUDE_SKILL_DIR}/references/glossaire.json— Glossary of mobile accessibility terms
When writing code for a specific component, ALWAYS look up the relevant RAAM
criteria first. For example, before writing a form, run search "form" and topic 9.
Core rules (apply to ALL mobile code you write)
1. Graphic Elements (Topic 1)
ALWAYS:
- Every decorative graphic element MUST be ignored by assistive technologies (1.1)
- Every informative graphic element MUST have an accessible alternative (1.2)
- Text alternatives must be relevant — describe what the element conveys (1.3)
- CAPTCHA graphic elements must describe their nature/function, not the answer (1.4)
- Complex graphics (charts, maps) need a detailed description (1.6, 1.7)
- Avoid text in graphic elements unless the effect cannot be reproduced with styled text (1.8 — Level AA)
iOS (SwiftUI):
// GOOD: informative image
Image("chart-sales")
.accessibilityLabel("Sales increased 40% in Q3 2024")
// GOOD: decorative image — hidden from VoiceOver
Image("decorative-wave")
.accessibilityHidden(true)
// GOOD: icon button with accessibility
Button(action: { /* ... */ }) {
Image(systemName: "magnifyingglass")
}
.accessibilityLabel("Search")
iOS (UIKit):
// Informative image
imageView.isAccessibilityElement = true
imageView.accessibilityLabel = "Sales increased 40% in Q3 2024"
// Decorative image
decorativeImageView.isAccessibilityElement = false
decorativeImageView.accessibilityElementsHidden = true
Android (Jetpack Compose):
// GOOD: informative image
Image(
painter = painterResource(R.drawable.chart_sales),
contentDescription = "Sales increased 40% in Q3 2024"
)
// GOOD: decorative image
Image(
painter = painterResource(R.drawable.decorative_wave),
contentDescription = null // null = decorative in Compose
)
// GOOD: icon button
IconButton(onClick = { /* ... */ }) {
Icon(
Icons.Default.Search,
contentDescription = "Search"
)
}
Android (XML Views):
<!-- Informative image -->
<ImageView
android:contentDescription="Sales increased 40% in Q3 2024"
android:importantForAccessibility="yes" />
<!-- Decorative image -->
<ImageView
android:contentDescription="@null"
android:importantForAccessibility="no" />
React Native:
// Informative image
<Image
source={require('./chart.png')}
accessible={true}
accessibilityLabel="Sales increased 40% in Q3 2024"
/>
// Decorative image
<Image
source={require('./decoration.png')}
accessible={false}
accessibilityElementsHidden={true}
importantForAccessibility="no"
/>
Flutter:
// Informative image
Image.asset(
'assets/chart.png',
semanticsLabel: 'Sales increased 40% in Q3 2024',
)
// Decorative image
Semantics(
excludeSemantics: true,
child: Image.asset('assets/decoration.png'),
)
2. Colours (Topic 2)
- Information MUST NOT be conveyed by colour alone — always add shape, text, or icon (2.1)
- Text contrast: at least 4.5:1 (normal text) or 3:1 (large text ≥24px / bold ≥18.5px) (2.2 — Level AA)
- Non-text element contrast (icons, borders, UI controls): at least 3:1 (2.3 — Level AA)
- If contrast is insufficient by default, a replacement mechanism must exist and itself be accessible (2.4 — Level AA)
iOS:
// GOOD: support system contrast settings
// Use semantic colours that adapt to Increase Contrast mode
Text("Important")
.foregroundColor(.primary) // adapts to accessibility settings
// Support Differentiate Without Colour
HStack {
Image(systemName: "checkmark.circle.fill")
.foregroundColor(.green)
Text("Validated") // text reinforces the green colour meaning
}
Android:
// Support high contrast text mode
// Use theme colours that respond to system accessibility settings
Text(
text = "Important",
color = MaterialTheme.colorScheme.onSurface // adapts to theme
)
3. Multimedia (Topic 3)
- Audio-only media MUST have a text transcript (3.1, 3.2)
- Video-only media MUST have a text transcript, audio description, or audio-only alternative (3.3, 3.4)
- Synchronised media MUST have captions (3.7, 3.8)
- Synchronised media MUST have audio description (3.9, 3.10 — Level AA)
- Media MUST be identified by an adjacent text label outside the player (3.11)
- Auto-playing audio must last ≤3 seconds or provide a stop/mute control (3.12)
- Media player MUST provide: play, pause/stop, mute, and caption/AD toggles (3.13)
- Caption and AD controls must be at the same level as play/pause (3.14 — Level AA)
4. Tables (Topic 4)
- Data tables MUST have headers correctly associated with data cells (4.1, 4.2, 4.3)
- Complex tables MUST have a title and summary to explain structure (4.4, 4.5)
iOS (SwiftUI):
// GOOD: accessible table with header
List {
Section(header: Text("Q3 Revenue by Region")) {
ForEach(regions) { region in
HStack {
Text(region.name)
Spacer()
Text(region.revenue)
.accessibilityLabel("\(region.name): \(region.revenue)")
}
}
}
}
Android (Compose):
// GOOD: announce table structure to TalkBack
Column(modifier = Modifier.semantics {
contentDescription = "Revenue table, 3 regions, 2 columns: region and revenue"
}) {
// Table content
}
5. Interactive Components (Topic 5)
This is critical for mobile. Always look up: bash ${CLAUDE_SKILL_DIR}/scripts/raam-lookup.sh topic 5
- Every interactive component MUST be keyboard/switch accessible (5.1)
- Every interactive component MUST have a relevant accessible name (5.2)
- Accessible names MUST include any visible text (5.3)
- Screen reader users must receive context changes (5.4 — Level AA)
- Interactive component states (selected, expanded, disabled) MUST be rendered by assistive technologies (5.5)
iOS (SwiftUI):
// GOOD: button with state
Toggle(isOn: $isEnabled) {
Text("Notifications")
}
// SwiftUI handles accessibility state automatically
// GOOD: custom component with traits and state
Button(action: toggleExpansion) {
HStack {
Text("Details")
Image(systemName: isExpanded ? "chevron.up" : "chevron.down")
}
}
.accessibilityAddTraits(.isButton)
.accessibilityValue(isExpanded ? "expanded" : "collapsed")
// GOOD: custom slider
Slider(value: $volume, in: 0...100)
.accessibilityLabel("Volume")
.accessibilityValue("\(Int(volume)) percent")
Android (Compose):
// GOOD: expandable section with state
Row(
modifier = Modifier
.clickable { toggleExpansion() }
.sema