next_tag( 'svg' ) ) { $svg_processor->set_attribute( 'aria-hidden', 'true' ); $svg_processor->set_attribute( 'focusable', 'false' ); } $svg = $svg_processor->get_updated_html(); // Wrapper inline styles. Width/height are baked inline only when the author // set an explicit iconSize; left unset, sizing falls to the kit-tunable // `--wp--custom--airo-wp--icon--default-size` CSS var (see style.scss), // mirroring the pre-dynamic lazy save's hasExplicitSize behaviour so a Style // Kit / theme.json icon size is not silently overridden by an inline style. $wrapper_style = 'display:inline-flex;align-items:center;justify-content:center;border-radius:inherit;'; if ( $has_explicit_size ) { $wrapper_style = sprintf( 'width:%1$dpx;height:%1$dpx;', $icon_size ) . $wrapper_style; } if ( 0 !== $rotation ) { $wrapper_style .= sprintf( 'transform:rotate(%ddeg);', $rotation ); } // ARIA: decorative → hidden from AT; otherwise labelled with the // author's label. With no label, a linked icon still needs a name, so // it falls back to a humanized icon name (e.g. "circle-check" → // "Circle Check"). An unlabelled, unlinked icon is hidden instead: // announcing its slug tells a screen-reader user nothing true. $has_link = ! empty( $attributes['linkUrl'] ); if ( $is_decorative || ( '' === trim( $aria_label ) && ! $has_link ) ) { $aria = ' role="presentation" aria-hidden="true"'; } else { $label = '' !== trim( $aria_label ) ? $aria_label : ucwords( str_replace( '-', ' ', $icon ) ); $aria = sprintf( ' role="img" aria-label="%s"', esc_attr( $label ) ); } $icon_html = sprintf( '
%s
', esc_attr( $wrapper_style ), $aria, $svg ); // Optional link wrap (mirrors save.js: plain , noopener on _blank). $url = ! empty( $attributes['linkUrl'] ) ? esc_url( $attributes['linkUrl'] ) : ''; if ( '' !== $url ) { $target = isset( $attributes['linkTarget'] ) ? (string) $attributes['linkTarget'] : '_self'; $rel = isset( $attributes['linkRel'] ) ? (string) $attributes['linkRel'] : ''; if ( '_blank' === $target && '' === $rel ) { $rel = 'noopener noreferrer'; } $rel_attr = '' !== $rel ? sprintf( ' rel="%s"', esc_attr( $rel ) ) : ''; $icon_html = sprintf( '%s', $url, esc_attr( $target ), $rel_attr, $icon_html ); } // A stored `align` of left/center/right only ever exists on un-migrated // legacy content (the deprecation strips it on first editor re-save, // and a fresh icon never writes one) — so ITS presence, not // `justification`'s value, is what identifies "not yet migrated" // here. `justification` always reads back as a value (its own // block.json default fills in when the key is missing), so it cannot // be used to detect that. `align: wide|full` is unrelated (still a // live, current feature) and is left alone. $legacy_align = isset( $attributes['align'] ) ? (string) $attributes['align'] : ''; $justification = in_array( $legacy_align, array( 'left', 'center', 'right' ), true ) ? $legacy_align : ( isset( $attributes['justification'] ) ? (string) $attributes['justification'] : 'center' ); $justify_class = in_array( $justification, array( 'left', 'center', 'right' ), true ) ? ' airo-wp-justify--' . $justification : ''; $wrapper_attributes = get_block_wrapper_attributes( array( 'class' => 'airo-wp-icon airo-wp-justify' . $justify_class ) ); $html = sprintf( '
%s
', $wrapper_attributes, $icon_html ); // Strip the stale alignleft/aligncenter/alignright class WordPress's // align support unavoidably adds for un-migrated legacy content (see // docblock above) — the `airo-wp-justify--{value}` class already // positions the wrapper correctly, and the stale class's `margin: // auto` fights it. alignwide/alignfull are left untouched: those // remain a live, current feature and must keep escaping the column. $align_processor = new WP_HTML_Tag_Processor( $html ); if ( $align_processor->next_tag() ) { $align_processor->remove_class( 'alignleft' ); $align_processor->remove_class( 'aligncenter' ); $align_processor->remove_class( 'alignright' ); $html = $align_processor->get_updated_html(); } // The wrapper is now a full-column-width positioning box, so a background, // border or padding on it would paint across the column instead of hugging // the icon. Move them to the icon box. return airowp_route_visual_supports( $html, $attributes, 'airo-wp-icon__wrapper', array( 'color', 'border', 'spacing.padding' ) ); } } // Output directly (WordPress captures echo'd output from render callbacks). echo airowp_render_icon( $attributes, $content, $block ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped