array(
'primary' => '#3b82f6',
'secondary' => '#64748b',
'accent' => '#f59e0b',
'success' => '#22c55e',
'danger' => '#ef4444',
'warning' => '#eab308',
'info' => '#06b6d4',
'light' => '#f3f4f6',
'dark' => '#111827',
'muted' => '#6b7280',
),
'fonts' => array(
'heading' => 'Inter',
'body' => 'Inter',
),
);
const COLOR_LABELS = array(
'primary' => 'Primary',
'secondary' => 'Secondary',
'accent' => 'Accent',
'success' => 'Success',
'danger' => 'Danger',
'warning' => 'Warning',
'info' => 'Info',
'light' => 'Light',
'dark' => 'Dark',
'muted' => 'Muted',
);
const NONCE_ACTION = 'wpaddons_save_tailwind_theme';
/**
* @var self|null
*/
private static $instance = null;
public function __construct() {
self::$instance = $this;
}
/**
* Shared instance used by the Advanced Addons admin menu.
*/
public static function instance(): self {
if ( null === self::$instance ) {
self::$instance = new self();
}
return self::$instance;
}
public function render_settings_page(): void {
if ( ! current_user_can( 'manage_options' ) ) {
return;
}
// Handle form submission directly — bypass Settings API nesting issues.
$saved = false;
if (
isset( $_POST['wpaddons_save_theme'] ) &&
check_admin_referer( self::NONCE_ACTION, 'wpaddons_theme_nonce' )
) {
$this->save_theme( $_POST );
$saved = true;
}
$theme = self::get_theme();
?>
bg-primary text-light rounded-lg p-4
$label ) {
if ( isset( $post_data['wpaddons_theme_colors'][ $key ] ) ) {
$color = sanitize_hex_color( wp_unslash( $post_data['wpaddons_theme_colors'][ $key ] ) );
if ( $color ) {
$theme['colors'][ $key ] = $color;
}
}
}
}
if ( isset( $post_data['wpaddons_theme_fonts'] ) && is_array( $post_data['wpaddons_theme_fonts'] ) ) {
foreach ( array( 'heading', 'body' ) as $key ) {
if ( isset( $post_data['wpaddons_theme_fonts'][ $key ] ) && is_string( $post_data['wpaddons_theme_fonts'][ $key ] ) ) {
$font = sanitize_text_field( wp_unslash( trim( $post_data['wpaddons_theme_fonts'][ $key ] ) ) );
if ( $font ) {
$theme['fonts'][ $key ] = $font;
}
}
}
}
update_option( self::OPTION_NAME, wp_json_encode( $theme ) );
}
public static function get_theme(): array {
$theme = get_option( self::OPTION_NAME, '' );
$decoded = json_decode( $theme, true );
if ( ! is_array( $decoded ) ) {
return self::DEFAULTS;
}
return array_replace_recursive( self::DEFAULTS, $decoded );
}
/**
* Convert the theme array into a Tailwind v4 @theme CSS block.
*/
public static function get_theme_css(): string {
$theme = self::get_theme();
$lines = array( '@theme {' );
foreach ( $theme['colors'] as $name => $hex ) {
$lines[] = sprintf( ' --color-%s: %s;', $name, $hex );
}
if ( ! empty( $theme['fonts']['heading'] ) && 'Inter' !== $theme['fonts']['heading'] ) {
$escaped = esc_attr( $theme['fonts']['heading'] );
$lines[] = sprintf( ' --font-heading: "%s", sans-serif;', $escaped );
}
if ( ! empty( $theme['fonts']['body'] ) && 'Inter' !== $theme['fonts']['body'] ) {
$escaped = esc_attr( $theme['fonts']['body'] );
$lines[] = sprintf( ' --font-body: "%s", sans-serif;', $escaped );
}
$lines[] = '}';
return implode( "\n", $lines );
}
public static function get_colors_for_js(): array {
$theme = self::get_theme();
return $theme['colors'] ?? self::DEFAULTS['colors'];
}
public static function get_fonts_for_js(): array {
$theme = self::get_theme();
return $theme['fonts'] ?? self::DEFAULTS['fonts'];
}
}