' . \Zadex\Documents\Sanitizer::clean_svg((string) $icon['svg']) . ''; } elseif (!empty($icon['value'])) { $icon_html = ''; } $after = ($settings['icon_position'] ?? 'before') === 'after'; // Size preset as a zero-specificity class (style.css `:where(.zx-button--lg)`), so the Padding / Typography // controls' generated `.zx-` rules always win (T217). Legacy padding values map to their preset. $size_aliases = ['8px 16px' => 'sm', '12px 24px' => 'md', '16px 32px' => 'lg', '20px 40px' => 'xl']; $size = (string) ($settings['size'] ?? 'md'); $size = $size_aliases[$size] ?? $size; if (!in_array($size, ['sm', 'md', 'lg', 'xl'], true)) { $size = 'md'; } $attrs = (string) preg_replace('/class="([^"]*)"/', 'class="$1 zx-button--' . esc_attr($size) . '"', $attrs, 1); if (!empty($settings['button_id'])) { // The Button ID wins over a CSS ID set in the Advanced tab (never print two id attributes; mirrors Preview.vue). $attrs = trim((string) preg_replace('/(^|\s)id="[^"]*"/', '', $attrs)) . ' id="' . esc_attr((string) $settings['button_id']) . '"'; } $rel = []; if (!empty($zx_link['nofollow'])) { $rel[] = 'nofollow'; } if (($zx_link['target'] ?? '') === '_blank') { $rel[] = 'noopener'; } $inner = ($after ? '' : $icon_html) . '' . esc_html($text) . '' . ($after ? $icon_html : ''); if ($href !== '') { $zx_html = '' . $inner . "\n"; } else { // has-* classes: themes skip their `button:not(.has-text-color)` colour rules (docs/WIDGET-GUIDE.md). $zx_html = '\n"; } echo wp_kses($zx_html, \Zadex\Frontend\Kses::allowed_html());