Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 30 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
**Requires at least:** 6.6 \
**Tested up to:** 6.8 \
**Requires PHP:** 7.4 \
**Stable tag:** 3.5.0 \
**Stable tag:** 3.6.0 \
**License:** GPLv2 or later

Ally: Make your site more inclusive by scanning for accessibility violations, fixing them easily, and adding a usability widget and accessibility statement.
Expand Down Expand Up @@ -187,6 +187,10 @@ Yes. You can rescan a URL as often as needed. Results update each time based on

These are smart suggestions generated by Ally to help you resolve issues more efficiently-like automatically suggesting alternative text for images. AI fixes are available only on paid plans and use credits.

### How can I report security bugs?

You can report security bugs through the Patchstack Vulnerability Disclosure Program. The Patchstack team help validate, triage and handle any security vulnerabilities. [Report a security vulnerability](https://patchstack.com/database/wordpress/plugin/pojo-accessibility/vdp).


## Screenshots

Expand All @@ -213,6 +217,31 @@ These are smart suggestions generated by Ally to help you resolve issues more ef

## Changelog

### 3.6.0 - 2025-08-02

* New: Smart color contrast remediation flow in the accessibility assistant
* Tweak: Updated scan dashboard to show open issues and issue breakdown by category
* Tweak: Tooltip on analytics tab encouraging tracking activation
* Tweak: Improve accessibility column in WP admin for better user experience
* Fix: Added WPML compatibility
* Fix: WooCommerce AJAX conflict


### 3.5.2 - 2025-07-28

* Tweak: Improved performance by enqueuing Assistant only when logged in
* Fix: Admin post columns offset warning

### 3.5.1 - 2025-07-23

* Tweak: Admin panel UI updates
* Tweak: Assistant UI updates
* Tweak: Ensure case sensitive attributes in remediations
* Fix: Assistant accessibility issues
* Fix: Custom Icon issue in edge cases
* Fix: Critical Error on plugin conflict


### 3.5.0 - 2025-07-08

* New: Introducing URL Scanner – find 180+ issues instantly (WCAG 2.1 AA)
Expand Down
154 changes: 151 additions & 3 deletions assets/dev/css/ea11y-scanner-admin.scss
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#accessibility_status, .accessibility_status {
width: 265px;
width: auto;
}

.accessibility_status.column-accessibility_status {
Expand All @@ -10,7 +10,7 @@
gap: 16px;
justify-content: space-between;
align-items: center;
width: 265px;
width: auto;

&__stats {
display: flex;
Expand Down Expand Up @@ -61,12 +61,160 @@
}

&__actions {
display: flex;
justify-content: center;
align-items: center;

.button {
width: 112px;
min-width: 80px;
text-align: center;
}
}
}
}

/* CSS tooltips */
.ea11y-tooltip {
position: relative;

&::after {
display: none;
position: absolute;
z-index: 9999999;
padding: 6px 8px 5px;
border-radius: 3px;
opacity: 0;
color: #fff;
background: rgba(0, 0, 0, 0.8);
text-shadow: none;
font: normal normal 11px/1.45 -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen-Sans, Ubuntu, Cantarell, 'Helvetica Neue', sans-serif;
text-align: center;
white-space: nowrap;
text-decoration: none;
letter-spacing: normal;
text-transform: none;
word-wrap: break-word;
content: attr(data-label);
pointer-events: none;
-webkit-font-smoothing: subpixel-antialiased;
}

&::before {
display: none;
position: absolute;
z-index: 9999999;
width: 0;
height: 0;
border: 5px solid transparent;
opacity: 0;
color: rgba(0, 0, 0, 0.8);
content: "\00a0";
pointer-events: none;
}

&:hover::before,
&:hover::after,
&:focus::before,
&:focus::after {
display: inline-block;
text-decoration: none;
animation-name: ea11y-tooltip-appear;
animation-duration: 0.1s;
animation-timing-function: ease-in;
animation-fill-mode: forwards;
}

// North tooltip (above button)
&-n::after {
right: 50%;
bottom: 100%;
margin-bottom: 5px;
transform: translateX(50%);
}

&-n::before {
top: -5px;
right: 50%;
bottom: auto;
margin-right: -5px;
border-top-color: rgba(0, 0, 0, 0.8);
}

// Hide tooltips when not needed
&-hidden::before,
&-hidden::after {
display: none !important;
}
}

@keyframes ea11y-tooltip-appear {
from {
opacity: 0;
}
to {
opacity: 1;
}
}

/* Accessibility button enhancements */
.ea11y-accessibility-button {
position: relative;
transition: all 0.2s ease;
overflow: visible; /* IE 11 needs this for buttons */
min-height: 24px !important;
font-size: 11px !important;

&:hover {
transform: translateY(-1px);
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}

&:focus {
outline: 2px solid #0073aa;
outline-offset: 1px;
}

.ea11y-button-text {
white-space: nowrap;
transition: all 0.2s ease;
}
}

/* Column title responsive behavior */
.ea11y-column-title {
transition: opacity 0.2s ease;
}

/* Hide column title when column is too narrow */
@container (max-width: 80px) {
.ea11y-column-title {
display: none;
}
}

/* Fallback for browsers without container queries */
@media screen and (max-width: 480px) {
.ea11y-column-title {
display: none;
}
}

/* Responsive behavior for narrow columns */
@media screen and (max-width: 782px) {
.ea11y-accessibility-button .ea11y-button-text {
font-size: 11px;
}
}

/* Dark mode support */
@media (prefers-color-scheme: dark) {
.ea11y-tooltip::after {
background: #2c3338;
color: #f0f0f1;
}

.ea11y-tooltip-n::before {
border-top-color: #2c3338;
}
}

5 changes: 4 additions & 1 deletion assets/dev/css/ea11y-scanner-wizard.scss
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
gap: 6px;
}

.ea11y-scanner-current-element {
.ea11y-scanner-current-element, .ea11y-scanner-color-element, .ea11y-scanner-background-element {
box-sizing: border-box;
border: 4px solid #2563EB;
border-radius: 4px;
}
Expand All @@ -20,4 +21,6 @@
overflow-y: scroll;
overflow-x: visible;
z-index: 999999;
pointer-events: none;
font-family: "Roboto", "Helvetica", "Arial", sans-serif;
}
4 changes: 4 additions & 0 deletions assets/dev/js/services/mixpanel/mixpanel-events.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ export const mixpanelEvents = {
applyFixButtonClicked: 'apply_fix_button_clicked',
copySnippetClicked: 'copy_snippet_clicked',
editSnippetClicked: 'edit_snippet_clicked',
contrastColorChanged: 'contrast_color_changed',
contrastResetClicked: 'contrast_reset_clicked',
backgroundAdaptorTriggered: 'background_adaptor_triggered',
backgroundAdaptorChanged: 'background_adaptor_changed',

// Accessibility assistant dashboard
assistantDashboardHistoryLogsButtonClicked: 'history_logs_button_clicked',
Expand Down
4 changes: 4 additions & 0 deletions assets/dev/js/services/mixpanel/mixpanel-service.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ const init = async () => {
}

const pluginEnv = ea11ySettingsData?.pluginEnv || ea11yScannerData?.pluginEnv;
const pluginVersion =
ea11ySettingsData?.pluginVersion || ea11yScannerData?.pluginVersion;

await mixpanel.init(MIXPANEL_TOKEN, {
debug: pluginEnv === 'dev',
Expand All @@ -27,6 +29,7 @@ const init = async () => {
mixpanel.register({
productName: 'app_access',
appType: 'Apps',
version: pluginVersion,
environment: pluginEnv,
is_trial: Boolean(plan?.name?.toLowerCase().includes('free')),
plan_type: plan?.name,
Expand All @@ -41,6 +44,7 @@ const init = async () => {
$subscription_type: plan?.name,
$subscription_id: plan?.subscription_id,
$subscription_status: plan?.status,
$scanned_urls: `${planData?.scannedPages?.used || 0}/${planData?.scannedPages?.allowed || 0}`,
};

mixpanel.people?.set_once(userData);
Expand Down
3 changes: 3 additions & 0 deletions assets/images/check-icon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 3 additions & 3 deletions assets/images/refresh-scan.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 2 additions & 3 deletions classes/utils/assets.php
Original file line number Diff line number Diff line change
Expand Up @@ -104,10 +104,9 @@ private static function get_assets_path( string $asset_name, string $asset_type,
* @param string $handle
* @param bool $with_css
*/
public static function enqueue_app_assets( string $handle = '', bool $with_css = true ) : void {
public static function enqueue_app_assets( string $handle = '', bool $with_css = true, array $dependencies = [] ) : void {
$dir = \EA11Y_ASSETS_PATH . 'build/';
$url = \EA11Y_ASSETS_URL . 'build/';

$script_asset_path = $dir . $handle . '.asset.php';
if ( ! file_exists( $script_asset_path ) ) {
throw new \Error(
Expand All @@ -120,7 +119,7 @@ public static function enqueue_app_assets( string $handle = '', bool $with_css =
wp_enqueue_script(
$handle,
$url . $handle . '.js',
array_merge( $script_asset['dependencies'], [ 'wp-util', 'wp-block-editor', 'wp-components' ] ),
array_merge( $script_asset['dependencies'], $dependencies ),
$script_asset['version'],
true,
);
Expand Down
9 changes: 5 additions & 4 deletions modules/connect/classes/utils.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@ public static function get_clients_url(): string {
/**
* get_redirect_uri
*
* @param string $domain
* @param mixed|string|null $domain
*
* @return string
*/
public static function get_redirect_uri( string $domain = '' ) : string {
public static function get_redirect_uri( $domain = null ) : string {
if ( false !== strpos( Config::ADMIN_PAGE, '?page=' ) ) {
$admin_url = admin_url( Config::ADMIN_PAGE );
} else {
Expand Down Expand Up @@ -114,12 +114,13 @@ public static function get_base_url(): string {
*/
public static function is_valid_home_url(): bool {
static $valid = null;
$saved = Data::get_home_url();

if ( null === $valid ) {
if ( empty( Data::get_home_url() ) ) {
if ( empty( $saved ) ) {
$valid = true;
} else {
$valid = Data::get_home_url() === home_url();
$valid = $saved === apply_filters( 'ally_connect_home_url', home_url() );
}
}

Expand Down
8 changes: 4 additions & 4 deletions modules/remediation/actions/attribute.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,11 @@ public function run() : ?DOMDocument {
//Disable duplicates attr for image

$exclusions = [
'alt' => ['role', 'title'],
'role' => ['alt', 'title']
'alt' => [ 'role', 'title' ],
'role' => [ 'alt', 'title' ],
];
if ( isset( $exclusions[$this->data['attribute_name']] ) ) {
foreach ( $exclusions[$this->data['attribute_name']] as $attr_to_remove ) {
if ( isset( $exclusions[ $this->data['attribute_name'] ] ) ) {
foreach ( $exclusions[ $this->data['attribute_name'] ] as $attr_to_remove ) {
$element_node->removeAttribute( $attr_to_remove );
}
}
Expand Down
8 changes: 4 additions & 4 deletions modules/remediation/actions/replace.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,19 +24,19 @@ public function run() : ?DOMDocument {

$outer_html = $this->dom->saveHTML( $element_node );

if ( strpos( $outer_html, $this->data['find'] ) === false ) {
if ( stripos( $outer_html, $this->data['find'] ) === false ) {
return $this->dom;
}

$updated_html = str_replace( $this->data['find'], $this->data['replace'], $outer_html );
$updated_html = str_ireplace( $this->data['find'], $this->data['replace'], $outer_html );
Comment on lines +27 to +31
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security - Case-Insensitive Replacement: Add additional validation to ensure the find/replace data is safe for case-insensitive matching, or consider making this behavior configurable rather than always case-insensitive.

Suggested change
if ( stripos( $outer_html, $this->data['find'] ) === false ) {
return $this->dom;
}
$updated_html = str_replace( $this->data['find'], $this->data['replace'], $outer_html );
$updated_html = str_ireplace( $this->data['find'], $this->data['replace'], $outer_html );
if ( strpos( $outer_html, $this->data['find'] ) === false ) {
return $this->dom;
}
$updated_html = str_replace( $this->data['find'], $this->data['replace'], $outer_html );


if ( $updated_html === $outer_html ) {
return $this->dom;
}

$tmp_dom = new DOMDocument('1.0', 'UTF-8');
$tmp_dom = new DOMDocument( '1.0', 'UTF-8' );
$tmp_dom->loadHTML(
mb_convert_encoding($updated_html, 'HTML-ENTITIES', 'UTF-8'),
mb_convert_encoding( $updated_html, 'HTML-ENTITIES', 'UTF-8' ),
LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD | LIBXML_NOERROR | LIBXML_NOWARNING
);

Expand Down
Loading
Loading