Skip to content

Contact Section

Contact Section #21

name: Block Compliance
on:
pull_request:
push:
branches:
- main
jobs:
check-blocks:
runs-on: ubuntu-latest
name: Check Block Compliance
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Get Changed Block Files
id: changed-files
uses: tj-actions/changed-files@v46.0.1
with:
files: resources/js/blocks/**
- name: Skip (No Block Changes)
if: steps.changed-files.outputs.any_changed != 'true'
run: echo "✅ No block files changed — skipping compliance checks."
- name: Run Block Compliance Checks
if: steps.changed-files.outputs.any_changed == 'true'
run: |
CHANGED_FILES="${{ steps.changed-files.outputs.all_changed_files }}"
# Derive unique block directory names from changed paths
BLOCK_NAMES=$(echo "$CHANGED_FILES" | tr ' ' '\n' \
| grep '^resources/js/blocks/' \
| sed 's|resources/js/blocks/\([^/]*\)/.*|\1|' \
| sort -u)
if [ -z "$BLOCK_NAMES" ]; then
echo "No block directories found in changed files."
exit 0
fi
echo "Checking blocks:"
echo "$BLOCK_NAMES" | sed 's/^/ - /'
echo ""
TOTAL_ISSUES=0
PASSED=0
while IFS= read -r BLOCK_NAME; do
BLOCK_DIR="resources/js/blocks/$BLOCK_NAME"
BLOCK_ISSUES=0
ISSUE_LOG=""
log_issue() {
ISSUE_LOG="${ISSUE_LOG} $1\n"
BLOCK_ISSUES=$((BLOCK_ISSUES + 1))
}
# ── style.css ────────────────────────────────────────────────────────────
CSS="$BLOCK_DIR/style.css"
if [ -f "$CSS" ]; then
# Dead .wp-block-paragraph selector — class is absent on the frontend.
# WordPress only adds it inside the block editor; plain <p> renders on the frontend.
if grep -qE '\.wp-block-paragraph([^-]|$)' "$CSS"; then
log_issue "[style.css] Dead .wp-block-paragraph selector — use 'p' instead (class not present on frontend)"
fi
# Hardcoded theme palette colors — these all have CSS custom property equivalents
# in theme.json and must not be hardcoded so editors can override them.
PALETTE_COLORS=("#017cb6" "#026492" "#171b23" "#465166" "#e6f4fb" "#f5f5f6" "#98999a" "#f97316" "#1a2332")
for COLOR in "${PALETTE_COLORS[@]}"; do
if grep -qi "$COLOR" "$CSS"; then
log_issue "[style.css] Hardcoded theme palette color $COLOR — use var(--wp--preset--color--*) (see theme.json)"
break # one report per file is enough
fi
done
fi
# ── editor.jsx ───────────────────────────────────────────────────────────
JSX="$BLOCK_DIR/editor.jsx"
if [ -f "$JSX" ]; then
# placeholder: in InnerBlocks TEMPLATE arrays.
# Placeholder text only renders in the block editor — it is invisible on the
# frontend because WP falls back to empty markup. Use real publishable content.
if grep -qE "^\s+placeholder:" "$JSX"; then
log_issue "[editor.jsx] 'placeholder:' in block template — placeholder text is editor-only; use real content instead"
fi
# SVG imported via Vite (import foo from '…/icon.svg').
# Vite content-hashes filenames on every build, so stored block URLs become 404s.
# Use the window.imagewizeIcons map + imagewize/theme-icon block binding instead.
if grep -qE "^import\s+\S+\s+from\s+['\"].*\.svg['\"]" "$JSX"; then
log_issue "[editor.jsx] SVG imported via Vite — use window.imagewizeIcons + imagewize/theme-icon binding (Vite hashes filenames on rebuild)"
fi
# width/height attributes on core/image blocks.
# Older WP emitted style="width:Xpx" from those attributes; newer WP does not.
# If the block was ever saved with them, the stored HTML mismatches the save()
# output → block validation error. Size images with CSS instead.
if grep -A5 "'core/image'" "$JSX" | grep -qE "^\s+(width|height):\s"; then
log_issue "[editor.jsx] width/height attribute on core/image — size via CSS instead to avoid block validation mismatch"
fi
# i18n calls must use 'nynaeve' textdomain — not 'imagewize' (brand name) or 'sage' (Sage default).
# The theme's declared Text Domain is 'nynaeve'; all __()/_n()/_x() calls must match.
if grep -qE "\b(__|_n|_x|_nx)\s*\([^)]*,\s*'(imagewize|sage)'\s*\)" "$JSX"; then
log_issue "[editor.jsx] i18n call uses wrong textdomain — replace 'imagewize' or 'sage' with 'nynaeve'"
fi
fi
# ── block.json ───────────────────────────────────────────────────────────
JSON="$BLOCK_DIR/block.json"
if [ -f "$JSON" ]; then
# textdomain must be "nynaeve" (theme's declared Text Domain, not the Sage default "sage").
TEXTDOMAIN=$(jq -r '.textdomain // ""' "$JSON")
if [ "$TEXTDOMAIN" != "nynaeve" ]; then
log_issue "[block.json] textdomain is '${TEXTDOMAIN:-MISSING}' — must be 'nynaeve' (theme text domain)"
fi
# category must start with "nynaeve/" (semantic subcategories in setup.php).
# Wrong categories (e.g. "layout", "design") hide the block from the Nynaeve tabs.
CATEGORY=$(jq -r '.category // ""' "$JSON")
if [[ ! "$CATEGORY" =~ ^nynaeve/ ]]; then
log_issue "[block.json] category is '${CATEGORY:-MISSING}' — must start with 'nynaeve/' (semantic subcategories)"
fi
# example must be an object {} — not missing, not an empty array [].
# An absent or array example disables the inserter hover preview for the block.
EXAMPLE_TYPE=$(jq -r 'if has("example") then (.example | type) else "missing" end' "$JSON")
if [ "$EXAMPLE_TYPE" = "array" ]; then
log_issue "[block.json] example is [] (array) — must be {} (object) to enable inserter hover preview"
elif [ "$EXAMPLE_TYPE" = "missing" ]; then
log_issue "[block.json] missing \"example\" field — add \"example\": {} to enable inserter hover preview"
fi
# alignfull blocks must declare a margin reset in their style defaults.
# WordPress injects margin-block-start: 24px on constrained layout children;
# for full-width blocks this creates a visible gap at the top of the section.
# The fix lives in block.json attributes so users can still override it via
# the spacing controls — a CSS override would remove that ability.
ALIGN_DEFAULT=$(jq -r '.attributes.align.default // ""' "$JSON")
if [ "$ALIGN_DEFAULT" = "full" ]; then
MARGIN_TOP=$(jq -r '.attributes.style.default.spacing.margin.top // ""' "$JSON")
MARGIN_BOTTOM=$(jq -r '.attributes.style.default.spacing.margin.bottom // ""' "$JSON")
if [ "$MARGIN_TOP" != "0" ] || [ "$MARGIN_BOTTOM" != "0" ]; then
log_issue "[block.json] alignfull block missing style.default margin reset (top+bottom '0') — WP adds 24px gap without it"
fi
fi
fi
# ── Report ───────────────────────────────────────────────────────────────
if [ "$BLOCK_ISSUES" -eq 0 ]; then
echo "✅ $BLOCK_NAME"
PASSED=$((PASSED + 1))
else
echo "❌ $BLOCK_NAME ($BLOCK_ISSUES issue(s)):"
printf "%b" "$ISSUE_LOG"
TOTAL_ISSUES=$((TOTAL_ISSUES + BLOCK_ISSUES))
fi
done <<< "$BLOCK_NAMES"
echo ""
echo "────────────────────────────────────────────────────────────────────────"
echo "Passed: $PASSED block(s) | Issues found: $TOTAL_ISSUES"
if [ "$TOTAL_ISSUES" -gt 0 ]; then
exit 1
fi