Fix RTL support across AI plugin admin UI#573
Conversation
routes/ai-home/style.scss is bundled into JS via wp-build, so it does not pass through the RTLCSS auto-flip and no -rtl version is generated. Physical left/right properties therefore break RTL layouts. Replace padding-left with padding-inline-start, and document the constraint in a header comment so future styles use logical properties from the start. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
In RTL languages, "previous" points right and "next" points left, so mirror the chevron icons with isRTL() to keep the navigation direction consistent with reading order. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The build pipeline runs rtlcss over compiled CSS, so physical properties in SCSS are flipped automatically. These cases bypass that pipeline and need explicit handling: - Directional icons/glyphs: flip chevron icons in the shared ImageHistoryNav and the taxonomy parent separator with isRTL(). - JS/JSX inline styles: rtlcss never sees runtime-set styles, so use logical properties (float: inline-end, marginInlineStart). - PHP inline style attributes: same — switch the spinner spacing to margin-inline-start. - Move the "Back to List" arrow into the translatable string so RTL locales can swap ← for →. Also collapse the alt-text meta box echo chain into a single printf for readability. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## develop #573 +/- ##
=============================================
- Coverage 72.40% 72.24% -0.17%
Complexity 1162 1162
=============================================
Files 68 68
Lines 5596 5609 +13
=============================================
Hits 4052 4052
- Misses 1544 1557 +13
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
| <div class="ability-explorer-detail"> | ||
| <div class="ability-detail-header"> | ||
| <a href="<?php echo esc_url( $back_url ); ?>" class="button">← <?php esc_html_e( 'Back to List', 'ai' ); ?></a> | ||
| <a href="<?php echo esc_url( $back_url ); ?>" class="button"><?php echo wp_kses_post( __( '← Back to List', 'ai' ) ); ?></a> |
The WordPress core .spinner style applies float: left, which pushed the spinner away from the Generate button and broke its inline alignment. Override with float: none so the spinner stays next to the button. Also drop the redundant aria-hidden attribute. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The button markup in add_button_to_media_modal() was built via long string concatenation, making it hard to read and inconsistent with the printf-based meta box markup. Switch to sprintf with positional placeholders for parity with render_attachment_meta_box(). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
| $button_text = empty( get_post_meta( $post->ID, '_wp_attachment_image_alt', true ) ) ? __( 'Generate', 'ai' ) : __( 'Regenerate', 'ai' ); | ||
|
|
||
| echo '<div class="ai-alt-text-media-actions" style="margin-top: 16px;">'; | ||
| echo '<button id="ai-alt-text-generate-button" class="button button-secondary" type="button" data-attachment-id="' . absint( $post->ID ) . '">' . esc_html( $button_text ) . '</button><span class="spinner" aria-hidden="true" style="margin-left: 8px;"></span><p class="description" aria-live="polite" style="margin-top: 10px; line-height: 1.3;"></p>'; | ||
| echo '</div>'; | ||
| $button_text =empty( get_post_meta( $post->ID, '_wp_attachment_image_alt', true ) ) ? __( 'Generate', 'ai' ) : __( 'Regenerate', 'ai' ); | ||
|
|
||
| printf( | ||
| '<div class="ai-alt-text-media-actions" style="margin-top: 16px;">' . | ||
| '<button id="ai-alt-text-generate-button" class="button button-secondary" type="button" data-attachment-id="%1$d">%2$s</button>' . | ||
| '<span class="spinner" style="margin-inline-start: 8px; float: none;"></span>' . | ||
| '<p class="description" aria-live="polite" style="margin-top: 10px; line-height: 1.3;"></p>' . | ||
| '</div>', | ||
| absint( $post->ID ), | ||
| esc_html( $button_text ) | ||
| ); |
There was a problem hiding this comment.
This section includes three changes:
- Change
margin-lefttomargin-inline-start - Refactor using printf for readability
- Add
float: noneto the spinner
- Add
float: noneto the spinner
This is a minor bug fix that is not directly related to this PR. Without it, the spinner would not be placed directly next to the button.
develop branch:
This branch:
| 'html' => '<div class="ai-alt-text-media-actions"><button id="ai-alt-text-generate-button" class="button button-secondary" type="button" data-attachment-id="' . absint( $post->ID ) . '">' . esc_html( $button_text ) . '</button><span class="spinner" aria-hidden="true" style="margin-left: 8px;"></span><p class="description" aria-live="polite" style="margin-top: 6px; font-size: 12px;"></p></div>', | ||
| 'html' => sprintf( | ||
| '<div class="">' . | ||
| '<button id="ai-alt-text-generate-button" class="button button-secondary" type="button" data-attachment-id="%1$d">%2$s</button>' . | ||
| '<span class="spinner" aria-hidden="true" style="margin-inline-start: 8px; float: none;"></span>' . | ||
| '<p class="description" aria-live="polite" style="margin-top: 6px; font-size: 12px;"></p>' . | ||
| '</div>', | ||
| absint( $post->ID ), | ||
| esc_html( $button_text ) | ||
| ), |
| padding-left: 40px; | ||
| padding-inline-start: 40px; |
There was a problem hiding this comment.
It would be best to just use WP Stylelint for this stuff so that folks don't need to think about it. Adding WP Stylelint in #594
| { suggestion.parent + ' › ' } | ||
| { suggestion.parent + | ||
| ( isRTL() ? ' ‹ ' : ' › ' ) } |
There was a problem hiding this comment.
I cannot test this due to an error in my environment, but I believe this change is correct.
{"code":"rest_ability_invalid_method","message":"Abilities that perform updates require POST method.","data":{"status":405}}
| container = document.createElement( 'span' ); | ||
| container.className = 'ai-excerpt-inline-wrapper'; | ||
| container.style.cssText = 'float: right;'; | ||
| container.style.cssText = 'float: inline-end;'; |
| <Button | ||
| className="ai-image-history-nav__arrow" | ||
| icon={ chevronLeft } | ||
| icon={ isRTL() ? chevronRight : chevronLeft } |
Add a space between the assignment operator and empty() for readability and consistency with PHP coding standards. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message. To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. |
The sprintf refactor dropped the wrapper class, leaving an empty class="" on the media modal div. The media modal JS locates the spinner and status elements through the .ai-alt-text-media-actions container and bails out when it is missing, so the click handler was never attached and the button label never switched to "Regenerate". Restoring the class fixes the failing E2E tests. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
dkotter
left a comment
There was a problem hiding this comment.
Left one minor comment but otherwise looks good to me
| printf( | ||
| '<div class="ai-alt-text-media-actions" style="margin-top: 16px;">' . | ||
| '<button id="ai-alt-text-generate-button" class="button button-secondary" type="button" data-attachment-id="%1$d">%2$s</button>' . | ||
| '<span class="spinner" style="margin-inline-start: 8px; float: none;"></span>' . |
There was a problem hiding this comment.
We had aria-hidden="true" on the original code but not seeing that here. I do see that still on the code changes below (line 348) so wondering if that's needed here still?
There was a problem hiding this comment.
Note I added this back in 5bd9d63 to unblock this PR and get it in the 1.0 release.
If that was meant to be removed, let me know and we can open a follow up PR
There was a problem hiding this comment.
@dkotter Sorry for the late reply. It was my simple mistake that I deleted aria-hidden="true". Thank you for restoring it.





Closes #483
What?
Fixes several spots in the admin UI that did not render correctly in RTL locales.
How?
Use of AI Tools
AI assistance: Yes
Tool(s): Claude Code
Model(s): Claude Opus 4.7
Used for: Auditing the codebase for RTL-sensitive code and drafting the fixes; all changes were reviewed and verified by me.
Testing Instructions
Changelog Entry