Skip to content

Commit 8cac991

Browse files
committed
feat(rules): add button-width-classes rule (v2.2.3)
Detect wp:button blocks where "width":N is set in JSON but the outer <div> is missing has-custom-width / wp-block-button__width-{N} classes, or the inner <a> still carries width:{N}% inline style. WordPress core/button save() expresses percentage width via CSS classes on the outer div — not inline style — so any mismatch causes a block validation failure in the editor. Caught by this rule before deployment. Also bump bin/pt-cli application version from 2.0.0 to 2.2.3.
1 parent eca3244 commit 8cac991

3 files changed

Lines changed: 46 additions & 1 deletion

File tree

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file.
44

55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
66

7+
## [2.2.3] - 2026-05-13
8+
9+
### Added
10+
11+
- **`button-width-classes` rule** (`BaseRules`) — detects `wp:button` blocks that have `"width":N` in their JSON but whose HTML outer `<div>` is missing the `has-custom-width` and/or `wp-block-button__width-{N}` classes, or where the inner `<a>` still carries a `width:{N}%` inline style. WordPress's `core/button` save function expresses percentage width via CSS classes on the outer div — not inline style — so any mismatch causes a block validation failure in the editor.
12+
713
## [2.2.2] - 2026-05-12
814

915
### Added

bin/pt-cli

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ use Imagewize\PtCli\Commands\Scaffold\StyleCreateCommand;
2121
use Imagewize\PtCli\Commands\TemplateCheckCommand;
2222
use Symfony\Component\Console\Application;
2323

24-
$application = new Application('pt-cli', '2.0.0');
24+
$application = new Application('pt-cli', '2.2.3');
2525
$application->add(new CheckCommand());
2626
$application->add(new TemplateCheckCommand());
2727
$application->add(new PatternCreateCommand());

src/Compliance/Rules/BaseRules.php

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ public function check(string $filePath): array
6161
$violations = array_merge($violations, $this->checkExternalUrls($content));
6262
$violations = array_merge($violations, $this->checkButtonRootFontSize($content));
6363
$violations = array_merge($violations, $this->checkButtonClassNameOrder($content));
64+
$violations = array_merge($violations, $this->checkButtonWidthClasses($content));
6465
$violations = array_merge($violations, $this->checkCoverBlockMinHeight($content));
6566
$violations = array_merge($violations, $this->checkEmptyBorderSideObjects($content));
6667
$violations = array_merge($violations, $this->checkBorderColorRawSlug($content));
@@ -246,6 +247,44 @@ private function checkButtonClassNameOrder(string $content): array
246247
return [];
247248
}
248249

250+
private function checkButtonWidthClasses(string $content): array
251+
{
252+
// When wp:button has "width":N, the save() function emits:
253+
// <div class="wp-block-button has-custom-width wp-block-button__width-{N} ...">
254+
// and does NOT put width:{N}% in the <a> inline style.
255+
// Patterns that instead use is-width-full + inline width:100% will fail validation.
256+
$lines = explode("\n", $content);
257+
$total = count($lines);
258+
for ($i = 0; $i < $total; $i++) {
259+
$line = $lines[$i];
260+
if (!str_contains($line, '<!-- wp:button') || !preg_match('/"width"\s*:\s*(\d+)/', $line, $m)) {
261+
continue;
262+
}
263+
$pct = (int) $m[1];
264+
// Scan current line and the next two for the HTML div.
265+
for ($j = $i; $j <= $i + 2 && $j < $total; $j++) {
266+
$html = $lines[$j];
267+
if (!str_contains($html, 'wp-block-button')) {
268+
continue;
269+
}
270+
if (!str_contains($html, 'has-custom-width') || !str_contains($html, "wp-block-button__width-{$pct}")) {
271+
return [$this->violation(
272+
'button-width-classes',
273+
"wp:button has \"width\":{$pct} but the outer <div> is missing has-custom-width and/or wp-block-button__width-{$pct} — add both classes and remove width:{$pct}% from the <a> inline style to match core/button save() output"
274+
)];
275+
}
276+
if (str_contains($html, "width:{$pct}%")) {
277+
return [$this->violation(
278+
'button-width-classes',
279+
"wp:button has \"width\":{$pct} but the <a> still has width:{$pct}% inline style — remove it (save() expresses width via has-custom-width + wp-block-button__width-{$pct} classes on the outer div, not inline style)"
280+
)];
281+
}
282+
break;
283+
}
284+
}
285+
return [];
286+
}
287+
249288
private function checkCoverBlockMinHeight(string $content): array
250289
{
251290
foreach (explode("\n", $content) as $line) {

0 commit comments

Comments
 (0)