raam-code — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited raam-code (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
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.
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 termsWhen 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.
ALWAYS:
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 = trueAndroid (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'),
)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
)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
}This is critical for mobile. Always look up: `bash ${CLAUDE_SKILL_DIR}/scripts/raam-lookup.sh topic 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() }
.semantics {
role = Role.Button
stateDescription = if (isExpanded) "expanded" else "collapsed"
}
) {
Text("Details")
Icon(
if (isExpanded) Icons.Default.ExpandLess else Icons.Default.ExpandMore,
contentDescription = null // described by parent semantics
)
}
// GOOD: disabled button announces state
Button(
onClick = { /* ... */ },
enabled = false // Compose announces "disabled" to TalkBack
) {
Text("Submit")
}React Native:
// GOOD: interactive component with state
<TouchableOpacity
accessible={true}
accessibilityRole="button"
accessibilityLabel="Details"
accessibilityState={{ expanded: isExpanded }}
onPress={toggleExpansion}
>
<Text>Details</Text>
</TouchableOpacity>iOS:
// SwiftUI: screen title
NavigationView {
ContentView()
.navigationTitle("Settings")
}
// UIKit: screen title
override func viewDidLoad() {
super.viewDidLoad()
title = "Settings"
}
// App language: set in Info.plist CFBundleDevelopmentRegion
// and Localizable.stringsAndroid:
// Compose: screen title for TalkBack
Scaffold(
topBar = {
TopAppBar(title = { Text("Settings") })
}
) { /* ... */ }
// Activity: label in AndroidManifest.xml
// <activity android:label="@string/settings_title" />
// Language: set in AndroidManifest.xml
// <application android:localeConfig="@xml/locales_config">iOS (SwiftUI):
// GOOD: heading semantics
Text("Account Settings")
.font(.title)
.accessibilityAddTraits(.isHeader)
Text("Privacy")
.font(.headline)
.accessibilityAddTraits(.isHeader)Android (Compose):
// GOOD: heading semantics
Text(
text = "Account Settings",
style = MaterialTheme.typography.headlineMedium,
modifier = Modifier.semantics { heading() }
)React Native:
<Text accessibilityRole="header">Account Settings</Text>iOS:
// GOOD: support Dynamic Type
Text("Welcome back")
.font(.body) // respects system text size
// Never use fixed point sizes for body text
// GOOD: allow both orientations in Info.plist
// UISupportedInterfaceOrientations: all orientationsAndroid:
// GOOD: use sp (scalable pixels) for text
Text(
text = "Welcome back",
fontSize = 16.sp // scales with system settings
)
// GOOD: support rotation in AndroidManifest.xml
// android:screenOrientation="unspecified"Critical topic for mobile apps. Always look up: `bash ${CLAUDE_SKILL_DIR}/scripts/raam-lookup.sh topic 9`
iOS (SwiftUI):
// GOOD: labelled text field
TextField("Email address", text: $email)
.textContentType(.emailAddress) // enables autocomplete (9.12)
.keyboardType(.emailAddress)
.accessibilityLabel("Email address")
.accessibilityHint("Required. Format: [email protected]")
// GOOD: field group
Section(header: Text("Personal information")) {
TextField("First name", text: $firstName)
.textContentType(.givenName)
TextField("Last name", text: $lastName)
.textContentType(.familyName)
}
// GOOD: error message
TextField("Email address", text: $email)
.accessibilityLabel("Email address")
if let error = emailError {
Text(error)
.foregroundColor(.red)
.accessibilityLabel("Error: \(error)")
// Post notification so VoiceOver announces immediately
}Android (Compose):
// GOOD: labelled text field with error
OutlinedTextField(
value = email,
onValueChange = { email = it },
label = { Text("Email address *") },
isError = emailError != null,
supportingText = emailError?.let { { Text(it) } },
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Email),
modifier = Modifier.semantics {
contentDescription = "Email address, required"
if (emailError != null) {
error("Error: $emailError")
}
}
)
// GOOD: autofill hint (9.12)
OutlinedTextField(
value = firstName,
onValueChange = { firstName = it },
label = { Text("First name") },
modifier = Modifier.autofill(
autofillTypes = listOf(AutofillType.PersonFirstName),
onFill = { firstName = it }
)
)React Native:
// GOOD: accessible form field
<View accessible={true} accessibilityRole="none">
<Text nativeID="emailLabel">Email address *</Text>
<TextInput
accessibilityLabelledBy="emailLabel"
accessibilityHint="Required. Format: [email protected]"
keyboardType="email-address"
textContentType="emailAddress" // iOS autocomplete
autoComplete="email" // Android autocomplete
value={email}
onChangeText={setEmail}
/>
{emailError && (
<Text accessibilityLiveRegion="polite" accessibilityRole="alert">
{emailError}
</Text>
)}
</View>iOS (SwiftUI):
// GOOD: logical focus order
VStack {
TextField("First name", text: $firstName)
TextField("Last name", text: $lastName)
Button("Submit") { submit() }
}
.accessibilityElement(children: .contain)
// SwiftUI follows visual order by default
// BAD: custom accessibility sort that breaks logical order
// .accessibilitySortPriority() — use only when neededAndroid:
// GOOD: traversal order follows layout
Column {
TextField(/* first name */)
TextField(/* last name */)
Button(onClick = { submit() }) { Text("Submit") }
}
// Compose follows composition order by default
// For XML views, use android:accessibilityTraversalBefore/After
// sparingly and only to fix non-obvious order issuesiOS:
// GOOD: single-pointer alternative to pinch-to-zoom
// Provide + / - buttons alongside pinch gesture
ZStack {
MapView()
VStack {
Spacer()
HStack {
Button(action: { zoomIn() }) {
Image(systemName: "plus.magnifyingglass")
}
.accessibilityLabel("Zoom in")
.frame(minWidth: 44, minHeight: 44) // minimum touch target
Button(action: { zoomOut() }) {
Image(systemName: "minus.magnifyingglass")
}
.accessibilityLabel("Zoom out")
.frame(minWidth: 44, minHeight: 44)
}
}
}Minimum touch target sizes:
// iOS: Apple recommends 44×44 pt minimum
.frame(minWidth: 44, minHeight: 44)
// RAAM 1.1 criterion 11.14: 24×24 CSS px minimum (AA)
// Using 44pt is safer and exceeds the requirement// Android: minimum 48dp (exceeds RAAM's 24px requirement)
Modifier.sizeIn(minWidth = 48.dp, minHeight = 48.dp)These topics cover documentation, editing tools, support services, and real-time communication. Query them individually when relevant:
bash ${CLAUDE_SKILL_DIR}/scripts/raam-lookup.sh topic 12 # Documentation & accessibility features
bash ${CLAUDE_SKILL_DIR}/scripts/raam-lookup.sh topic 13 # Editing tools
bash ${CLAUDE_SKILL_DIR}/scripts/raam-lookup.sh topic 14 # Support services
bash ${CLAUDE_SKILL_DIR}/scripts/raam-lookup.sh topic 15 # Real-time communicationKey rules for these topics:
| Feature | iOS (SwiftUI) | iOS (UIKit) | Android (Compose) | Android (XML) | React Native | Flutter |
|---|---|---|---|---|---|---|
| Label | .accessibilityLabel() | .accessibilityLabel | Modifier.semantics { contentDescription } | contentDescription | accessibilityLabel | Semantics(label:) |
| Hint | .accessibilityHint() | .accessibilityHint | Modifier.semantics { stateDescription } | — | accessibilityHint | Semantics(hint:) |
| Hidden | .accessibilityHidden(true) | .isAccessibilityElement = false | Modifier.semantics { invisibleToUser() } | importantForAccessibility="no" | accessible={false} | ExcludeSemantics() |
| Heading | .accessibilityAddTraits(.isHeader) | .accessibilityTraits = .header | Modifier.semantics { heading() } | accessibilityHeading | accessibilityRole="header" | Semantics(header: true) |
| Button | .accessibilityAddTraits(.isButton) | .accessibilityTraits = .button | Modifier.semantics { role = Role.Button } | role="button" | accessibilityRole="button" | Semantics(button: true) |
| Live region | .accessibilityAddTraits(.updatesFrequently) | .accessibilityTraits = .updatesFrequently | Modifier.semantics { liveRegion = LiveRegionMode.Polite } | accessibilityLiveRegion="polite" | accessibilityLiveRegion="polite" | Semantics(liveRegion:) |
bash ${CLAUDE_SKILL_DIR}/scripts/raam-lookup.sh criterion <topic.criterion>bash ${CLAUDE_SKILL_DIR}/scripts/raam-lookup.sh methodology <topic.criterion>bash ${CLAUDE_SKILL_DIR}/scripts/raam-lookup.sh glossary "<term>"~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.