| Date | Score | Grade | Errors | Change |
|---|---|---|---|---|
| ${entry.date.take(10)} | ${"%.1f".format(entry.score)} | ${entry.grade} | ${entry.errors} | $changeHtml |
| Now | ${"%.1f".format(score.score)} | ${score.grade} | ${score.totalErrors} | $currentChangeHtml |
| Criterion | Name | Level | Status | Issues |
|---|---|---|---|---|
| ${c.criterion} | ") + appendLine("${c.name} | ${c.level.label} | ") + appendLine("$statusBadge | ") + appendLine("$issueStr | ") + appendLine("
| Rule | Severity | Impact | Count | WCAG |
|---|---|---|---|---|
| ${rule.id} | ${rule.severity.label} | ${rule.impact.label} | $count | ${rule.wcagCriteria.joinToString(", ")} |
@@ -64,7 +128,7 @@ Since some of the code demonstrates the effect of inaccessible coding practices,
## License
android-compose-accessibility-techniques is licensed under under the Apache License, Version 2.0. See [LICENSE](LICENSE) file for more information.
-Copyright 2023-2024 CVS Health and/or one of its affiliates
+Copyright 2023-2025 CVS Health and/or one of its affiliates
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
diff --git a/app/build.gradle b/app/build.gradle
index 46552c5..636b8a1 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -75,6 +75,7 @@ dependencies {
implementation "androidx.constraintlayout:constraintlayout-compose:1.1.1"
implementation "androidx.navigation:navigation-compose:2.9.7"
+ lintChecks project(':lint-checks')
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.3.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.7.0'
@@ -83,4 +84,41 @@ dependencies {
androidTestImplementation "androidx.navigation:navigation-testing:2.9.7"
debugImplementation 'androidx.compose.ui:ui-tooling'
debugImplementation 'androidx.compose.ui:ui-test-manifest'
+}
+
+// a11y-check: runs on every build, produces clickable warnings + HTML report
+tasks.register('a11yCheck', JavaExec) {
+ description = 'Run accessibility check on Compose source files'
+ group = 'verification'
+ def jarTask = project(':A11yAgent').tasks.named('shadowJar')
+ dependsOn jarTask
+ classpath = files(jarTask.map { it.archiveFile })
+ args = ["${projectDir}/src/main/java", '--format', 'gradle']
+ ignoreExitValue = true // don't fail the build, just show warnings
+}
+
+tasks.register('a11yCheckHtml', JavaExec) {
+ description = 'Generate accessibility HTML report'
+ group = 'verification'
+ def jarTask = project(':A11yAgent').tasks.named('shadowJar')
+ dependsOn jarTask
+ classpath = files(jarTask.map { it.archiveFile })
+ args = ["${projectDir}/src/main/java", '--format', 'html']
+
+ def reportFile = file("${buildDir}/reports/a11y-check.html")
+ doFirst {
+ reportFile.parentFile.mkdirs()
+ standardOutput = new FileOutputStream(reportFile)
+ }
+ doLast {
+ println ""
+ println "====================================================================="
+ println " Accessibility report: file://${reportFile.absolutePath}"
+ println "====================================================================="
+ }
+ ignoreExitValue = true
+}
+
+tasks.named('a11yCheck').configure {
+ finalizedBy 'a11yCheckHtml'
}
\ No newline at end of file
diff --git a/build.gradle b/build.gradle
index 7b92a13..065c470 100644
--- a/build.gradle
+++ b/build.gradle
@@ -4,5 +4,7 @@ plugins {
id 'com.android.library' version '9.1.0' apply false
id 'org.jetbrains.kotlin.android' version '2.3.20' apply false
id 'org.jetbrains.kotlin.plugin.compose' version '2.3.20' apply false
+ id 'org.jetbrains.kotlin.jvm' version '2.3.20' apply false
+ id 'org.jetbrains.kotlin.plugin.serialization' version '2.3.20' apply false
id 'org.jetbrains.dokka' version '2.2.0'
}
\ No newline at end of file
diff --git a/gradle-plugin/build.gradle.kts b/gradle-plugin/build.gradle.kts
new file mode 100644
index 0000000..ac6dec7
--- /dev/null
+++ b/gradle-plugin/build.gradle.kts
@@ -0,0 +1,22 @@
+plugins {
+ `kotlin-dsl`
+ `java-gradle-plugin`
+}
+
+group = "com.cvshealth.a11y"
+version = "0.1.0"
+
+repositories {
+ mavenCentral()
+}
+
+gradlePlugin {
+ plugins {
+ create("a11yPlugin") {
+ id = "com.cvshealth.a11y"
+ implementationClass = "com.cvshealth.a11y.gradle.A11yGradlePlugin"
+ displayName = "Compose Accessibility Checker"
+ description = "WCAG 2.2 static analysis for Jetpack Compose"
+ }
+ }
+}
diff --git a/gradle-plugin/settings.gradle.kts b/gradle-plugin/settings.gradle.kts
new file mode 100644
index 0000000..f016cb4
--- /dev/null
+++ b/gradle-plugin/settings.gradle.kts
@@ -0,0 +1,8 @@
+rootProject.name = "a11y-gradle-plugin"
+
+pluginManagement {
+ repositories {
+ mavenCentral()
+ gradlePluginPortal()
+ }
+}
diff --git a/gradle-plugin/src/main/kotlin/com/cvshealth/a11y/gradle/A11yCheckTask.kt b/gradle-plugin/src/main/kotlin/com/cvshealth/a11y/gradle/A11yCheckTask.kt
new file mode 100644
index 0000000..44f20b7
--- /dev/null
+++ b/gradle-plugin/src/main/kotlin/com/cvshealth/a11y/gradle/A11yCheckTask.kt
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2026 CVS Health and/or one of its affiliates
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.cvshealth.a11y.gradle
+
+import org.gradle.api.Action
+import org.gradle.api.DefaultTask
+import org.gradle.api.GradleException
+import org.gradle.api.file.RegularFileProperty
+import org.gradle.api.provider.ListProperty
+import org.gradle.api.provider.Property
+import org.gradle.api.tasks.Input
+import org.gradle.api.tasks.InputFile
+import org.gradle.api.tasks.Optional
+import org.gradle.api.tasks.TaskAction
+import org.gradle.process.ExecOperations
+import org.gradle.process.JavaExecSpec
+import javax.inject.Inject
+
+abstract class A11yCheckTask @Inject constructor(
+ private val execOperations: ExecOperations
+) : DefaultTask() {
+
+ @get:InputFile
+ abstract val jarFile: RegularFileProperty
+
+ @get:Input
+ abstract val paths: ListProperty