Dominando la personalización del admin WordPress: Guía del desarrollador 2026

Dominando la personalización del admin WordPress: Guía del desarrollador 2026

Última verificación: 14 de octubre de 2026
21 min de lectura
Guía
Desarrollador full-stack
500+ proyectos WP

#Por qué personalizar el admin de WordPress

Entregar un panel WordPress genérico a tus clientes es como entregar las llaves de un avión a alguien que solo necesita conducir un coche. El admin de WordPress está diseñado para desarrolladores, no para usuarios finales. Los clientes no necesitan ver “Herramientas”, “Ajustes” ni “Plugins”, ya que esas opciones solo generan confusión, errores accidentales y tickets de soporte innecesarios.

Descubre más sobre desarrollo profesional WordPress en WPPoland.

La personalización del admin no es solo una cuestión estética: reduce los costes de soporte y deja claro que el panel se construyó para las personas que realmente inician sesión. Esta guía está escrita para desarrolladores que entregan sitios a editores, gestores de tienda y roles de cliente. Recorre la carga de CSS y JS del admin, la limpieza de menús, las comprobaciones de capacidades, los roles propios, las páginas de ajustes, el esquema de color, la pantalla de acceso, el aislamiento en un plugin must-use y cómo evitar romper las pantallas del núcleo. Las referencias oficiales para cargar recursos y construir menús son admin_enqueue_scripts y admin_menu.

#Por qué la personalización del admin reduce el soporte

Tras la entrega de una tienda WooCommerce hecha con un constructor de páginas, el cliente suele ver decenas de entradas de menú y usar un puñado. El resto genera preguntas: “¿puedo hacer clic en Temas?”, “¿qué es ACF?”, “¿por qué veo WooCommerce > Estado?”.

Quitar Herramientas, Ajustes y los editores de archivos a todos los que no tienen manage_options no va de colores más bonitos. Las rutas peligrosas desaparecen de la vista de quien no tiene acceso completo.

La segunda ganancia es la incorporación. En lugar de una hora de “no hagas clic aquí”, muestras una página de Soporte con documentación, contacto y una lista breve de publicación. Eso es white-label como producto, no solo un cambio de logo.

#Personalización del admin en un plugin must-use en lugar de functions.php

No pongas la personalización del admin en el functions.php del tema hijo. Cuando el cliente cambia de tema, las reglas de menú, la carga de recursos y las comprobaciones de capacidades desaparecen con él. El código de agencia que debe sobrevivir a un cambio de tema va en un plugin must-use dentro de wp-content/mu-plugins/.

Una estructura típica:

wp-content/mu-plugins/
  wppoland-admin-ux.php          # archivo de arranque (carga la carpeta)
  wppoland-admin-ux/
    menu.php
    enqueue.php
    capabilities.php
    dashboard.php

El archivo de arranque en la raíz de mu-plugins/ se carga automáticamente. Solo incluye los archivos de la subcarpeta. La ventaja es que el código se ejecuta siempre, sean cuales sean los plugins normales activos, y versionas la carpeta entera sin que nadie pueda desactivarla desde la pantalla de Plugins.

<?php
/**
 * Plugin Name: WPPoland Admin UX
 * Description: Limpieza de menús, carga de recursos y páginas de soporte para roles de cliente.
 * Author: wppoland.com
 */

declare(strict_types=1);

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

$dir = __DIR__ . '/wppoland-admin-ux';
require_once $dir . '/menu.php';
require_once $dir . '/enqueue.php';
require_once $dir . '/capabilities.php';
require_once $dir . '/dashboard.php';

Prueba siempre con una cuenta propia que tenga el rol del cliente (Editor, Gestor de la tienda o un rol personalizado). Si te quedas con la sesión de Super Admin en staging, no verás los fallos que acabas de “arreglar” para el cliente.

Si el proyecto ya tiene un plugin de agencia con autocarga de Composer, el archivo de arranque must-use puede limitarse a cargar ese paquete. La idea es la misma: la experiencia del admin no puede morir cuando en un rediseño se sustituye un tema hijo de Elementor.

Mantén los archivos pequeños. Uno para el menú, otro para los recursos, otro para los bloqueos. Quien tenga que averiguar más adelante por qué desapareció el menú de ACF no debería bucear en un functions.php de 800 líneas.

#Qué diferencia un plugin must-use de un plugin normal

El manual de administración avanzada enumera las diferencias, y varias cambian cómo hay que escribir el código del admin:

  • Los plugins must-use están activos en todos los sitios de la instalación y no se pueden desactivar desde wp-admin.
  • PHP los carga en orden alfabético, antes que los plugins normales.
  • Los archivos en subcarpetas no se incluyen automáticamente. Por eso la estructura anterior necesita un archivo de arranque en la raíz.
  • No aparecen en la lista por defecto de la pantalla de Plugins, sino en una lista Must-Use aparte que solo ven los usuarios con acceso a Plugins.
  • No aparecen en los avisos de actualización, así que las versiones nuevas las sigues tú.
  • Los hooks de activación no se ejecutan para los plugins de la carpeta must-use.

El último punto es el que más sorprende. El código que un plugin normal pondría en register_activation_hook, como crear roles, necesita en un plugin must-use su propia comprobación de versión. Las secciones siguientes muestran el patrón.

#Cómo comprobar capacidades antes de cambiar el menú del admin

remove_menu_page y add_menu_page son API de visibilidad. No sustituyen al control de acceso. Quien conozca la URL puede seguir abriendo tools.php u options-general.php si su capacidad lo permite.

Reglas que aguantan en producción:

  1. Comprueba current_user_can( 'manage_options' ) (o una capacidad más concreta) antes de ocultar menús a los roles de cliente.
  2. No ocultes nunca los menús a quien tiene que depurar el sitio. Un administrador sin menú visible cuesta más tiempo del que ahorra.
  3. Cuando de verdad haya que bloquear, usa admin_init o map_meta_cap y responde con wp_die() o una redirección con explicación, no solo con display: none en CSS.

En redacciones donde colaboradores externos y editores de plantilla comparten el sitio, compensa un rol propio con edit_posts y upload_files, sin install_plugins ni edit_themes. Así la limpieza del menú refleja ese rol en lugar de convertirse en una larga lista de excepciones por ID de usuario. get_role( 'editor' )->remove_cap( '...' ) pertenece a una migración puntual o a un comando de CLI, no a un hook que se ejecuta en cada página del admin.

/**
 * Bloquea el acceso directo por URL a Herramientas para no administradores.
 */
function wppoland_block_tools_screen(): void {
	if ( ! is_admin() || wp_doing_ajax() ) {
		return;
	}

	if ( current_user_can( 'manage_options' ) ) {
		return;
	}

	$screen = function_exists( 'get_current_screen' ) ? get_current_screen() : null;
	if ( ! $screen || 'tools' !== $screen->id ) {
		return;
	}

	wp_die(
		esc_html__( 'No tienes acceso a esta pantalla.', 'wppoland-admin-ux' ),
		esc_html__( 'Acceso denegado', 'wppoland-admin-ux' ),
		[ 'response' => 403 ]
	);
}
add_action( 'current_screen', 'wppoland_block_tools_screen' );

Los nombres de capacidades son el contrato. Los ID de usuario cambian, pero el gestor de la tienda conserva manage_woocommerce. Construye las reglas alrededor de capacidades, no del usuario con ID 12. Nunca confíes únicamente en la ocultación visual para el control de acceso.

#Rol de cliente propio con add_role y map_meta_cap

Los roles se guardan en la base de datos, no en el código. La referencia de add_role indica que la función se detiene y devuelve null si el rol ya existe. Una lista de capacidades modificada no tiene efecto hasta que el rol se elimina y se vuelve a crear. Sin una comprobación de versión eso ocurriría en cada petición, y como los plugins must-use no tienen hook de activación, un valor de versión guardado es la protección más sencilla.

function wppoland_register_client_role(): void {
	if ( '2' === get_option( 'wppoland_roles_version' ) ) {
		return;
	}

	remove_role( 'client_manager' );
	add_role(
		'client_manager',
		'Editor del cliente',
		[
			'read'                 => true,
			'edit_posts'           => true,
			'edit_published_posts' => true,
			'publish_posts'        => true,
			'delete_posts'         => true,
			'edit_pages'           => true,
			'edit_published_pages' => true,
			'edit_others_pages'    => true,
			'publish_pages'        => true,
			'upload_files'         => true,
			'moderate_comments'    => true,
		]
	);

	update_option( 'wppoland_roles_version', '2' );
}
add_action( 'init', 'wppoland_register_client_role' );

Las capacidades que no están en la lista, el rol no las tiene. Es más seguro que poner manage_options => false de forma explícita, porque una clave olvidada no abre nada. Cuando cambie la lista, sube el valor de versión.

Algunas páginas no debe editarlas el cliente aunque el rol tenga edit_others_pages, normalmente la portada. El filtro map_meta_cap traduce metacapacidades como edit_post a capacidades primitivas, y ahí puedes añadir do_not_allow para una página concreta:

function wppoland_protect_front_page( array $caps, string $cap, int $user_id, array $args ): array {
	if ( ! in_array( $cap, [ 'edit_post', 'delete_post' ], true ) || empty( $args[0] ) ) {
		return $caps;
	}

	$front_page = (int) get_option( 'page_on_front' );

	if ( $front_page && (int) $args[0] === $front_page && ! user_can( $user_id, 'manage_options' ) ) {
		$caps[] = 'do_not_allow';
	}

	return $caps;
}
add_filter( 'map_meta_cap', 'wppoland_protect_front_page', 10, 4 );

La ventaja frente a ocultar el enlace Editar es que la regla se aplica en todas partes: en la lista de páginas, en el editor de bloques, en la REST API y en la edición rápida.

#Cómo ocultar menús del admin de WordPress

El primer paso visible para crear una experiencia white-label es eliminar los elementos del menú que tus clientes no necesitan. WordPress proporciona la función remove_menu_page() para esto, con una prioridad tardía en admin_menu para que los plugins ya hayan registrado sus menús.

/**
 * Limpia el menú del admin para no administradores.
 */
function wppoland_clean_admin_menu(): void {
	if ( current_user_can( 'manage_options' ) ) {
		return;
	}

	remove_menu_page( 'tools.php' );
	remove_menu_page( 'options-general.php' );
	remove_menu_page( 'edit-comments.php' );
	remove_menu_page( 'edit.php?post_type=acf-field-group' );

	remove_submenu_page( 'themes.php', 'theme-editor.php' );
	remove_submenu_page( 'plugins.php', 'plugin-editor.php' );
}
add_action( 'admin_menu', 'wppoland_clean_admin_menu', 999 );

La prioridad 999 es deliberada. Muchos plugins registran sus menús en admin_menu con la prioridad por defecto, 10. Si la limpieza se ejecuta demasiado pronto, el slug todavía no existe y remove_menu_page no hace nada. Registra temporalmente $GLOBALS['menu'] en staging si no sabes qué slug usa un plugin. ACF, Yoast y WooCommerce tienen rutas poco evidentes.

remove_menu_page() solo oculta los enlaces del menú. Para seguridad real, combina siempre la ocultación con un bloqueo por capacidad donde la ruta sea peligrosa (editores de archivos, grupos de campos ACF, instalación de plugins).

Para eliminar submenús concretos sin quitar el menú padre completo, usa remove_submenu_page(). Esto te permite mantener “Entradas” visible pero ocultar “Categorías” o “Etiquetas” para ciertos roles. En tiendas WooCommerce es habitual que el gestor conserve Productos, Pedidos y Cupones, pero pierda Estado y Extensiones.

No quites la Biblioteca de medios a editores que de verdad suben imágenes. Es la limpieza excesiva clásica: el panel queda ordenado y al día siguiente llega el ticket de que falta “Subir”. Limpia primero las herramientas técnicas y las rutas de contenido al final.

#Crear una página de administración personalizada en WordPress

Una de las formas más útiles de mejorar la experiencia del cliente es crear páginas admin personalizadas con exactamente la información que necesita. Evita frameworks de opciones pesados si solo necesitas una página de soporte: la API del núcleo es rápida y predecible.

function wppoland_register_support_page(): void {
	add_menu_page(
		'Soporte al cliente',
		'Soporte',
		'edit_posts',
		'wppoland-support',
		'wppoland_render_support',
		'dashicons-sos',
		90
	);
}
add_action( 'admin_menu', 'wppoland_register_support_page' );

function wppoland_render_support(): void {
	if ( ! current_user_can( 'edit_posts' ) ) {
		wp_die( esc_html__( 'Acceso denegado', 'wppoland-admin-ux' ) );
	}
	?>
	<div class="wrap">
		<h1><?php echo esc_html__( '¿Necesitas ayuda?', 'wppoland-admin-ux' ); ?></h1>
		<div class="card">
			<h2><?php echo esc_html__( 'Contacta con tu desarrollador', 'wppoland-admin-ux' ); ?></h2>
			<p>
				<?php echo esc_html__( 'Correo:', 'wppoland-admin-ux' ); ?>
				<a href="mailto:[email protected]">dev@wppoland.com</a>
			</p>
		</div>
	</div>
	<?php
}

Mantén el callback sencillo: enlaces a documentación, una lista de publicación y el estado de las copias de seguridad si ya tienes ese dato. No construyas un mini CMS dentro de la página de Soporte.

Usa add_submenu_page bajo un menú de tipo de contenido existente cuando la ayuda se refiere a un solo tipo (por ejemplo “Eventos > Ayuda”). Reserva los menús de primer nivel para lo que el cliente abre cada semana. Demasiados iconos de primer nivel recrean el caos que acabas de limpiar.

#Página de ajustes para el cliente con la Settings API

Las páginas admin personalizadas pueden incluir formularios para que los clientes gestionen configuraciones concretas de su sitio, como el teléfono de soporte o los enlaces a redes sociales, sin acceso a Ajustes. Una página construida con la Settings API les muestra solo esos campos. Según el manual, WordPress se encarga entonces de guardar los datos enviados, añade los nonces y envía el formulario a través de wp-admin/options.php, que comprueba las capacidades de forma estricta.

function wppoland_register_client_settings(): void {
	register_setting(
		'wppoland_client',
		'wppoland_support_phone',
		[
			'type'              => 'string',
			'sanitize_callback' => 'sanitize_text_field',
			'default'           => '',
		]
	);

	add_settings_section( 'wppoland_contact', 'Contacto', '__return_false', 'wppoland-client-settings' );

	add_settings_field(
		'wppoland_support_phone',
		'Teléfono de soporte',
		'wppoland_render_phone_field',
		'wppoland-client-settings',
		'wppoland_contact'
	);
}
add_action( 'admin_init', 'wppoland_register_client_settings' );

function wppoland_render_phone_field(): void {
	printf(
		'<input type="text" name="wppoland_support_phone" value="%s" class="regular-text">',
		esc_attr( (string) get_option( 'wppoland_support_phone', '' ) )
	);
}

function wppoland_add_client_settings_page(): void {
	add_menu_page(
		'Ajustes del sitio',
		'Sitio',
		'edit_pages',
		'wppoland-client-settings',
		'wppoland_render_client_settings',
		'dashicons-admin-generic',
		91
	);
}
add_action( 'admin_menu', 'wppoland_add_client_settings_page' );

function wppoland_render_client_settings(): void {
	?>
	<div class="wrap">
		<h1><?php echo esc_html( get_admin_page_title() ); ?></h1>
		<?php settings_errors(); ?>
		<form action="options.php" method="post">
			<?php
			settings_fields( 'wppoland_client' );
			do_settings_sections( 'wppoland-client-settings' );
			submit_button();
			?>
		</form>
	</div>
	<?php
}

add_filter( 'option_page_capability_wppoland_client', fn() => 'edit_pages' );

La última línea es la que suele faltar. El archivo del núcleo wp-admin/options.php indica que los grupos de opciones de todos los ajustes registrados exigen manage_options por defecto, y que el filtro option_page_capability_{$option_page} cambia ese requisito para un grupo. Sin el filtro, el editor ve la página, rellena el campo y recibe un error al guardar. El filtro abre solo el grupo wppoland_client, no el resto de Ajustes.

#Cargar CSS y JS del admin con admin_enqueue_scripts y no con admin_head

admin_head con un bloque <style> en línea funciona en demos, pero escala mal: sin versiones, con una invalidación de caché incómoda y mezclando presentación con PHP. Usa admin_enqueue_scripts, carga tus propios archivos y limítalos a las pantallas que los necesitan.

function wppoland_admin_assets( string $hook_suffix ): void {
	// Solo nuestras páginas y el escritorio, no todas las pantallas del núcleo.
	$allowed = [
		'index.php',
		'toplevel_page_wppoland-support',
	];

	if ( ! in_array( $hook_suffix, $allowed, true ) ) {
		return;
	}

	$base = plugin_dir_url( __FILE__ );
	$ver  = '2026.09.21';

	wp_enqueue_style(
		'wppoland-admin-ux',
		$base . 'assets/admin.css',
		[],
		$ver
	);

	wp_enqueue_script(
		'wppoland-admin-ux',
		$base . 'assets/admin.js',
		[ 'jquery' ],
		$ver,
		true
	);

	wp_localize_script(
		'wppoland-admin-ux',
		'wppolandAdminUx',
		[
			'ajaxUrl' => admin_url( 'admin-ajax.php' ),
			'nonce'   => wp_create_nonce( 'wppoland_admin_ux' ),
		]
	);
}
add_action( 'admin_enqueue_scripts', 'wppoland_admin_assets' );

$hook_suffix es la clave para no romper las pantallas del núcleo. Una hoja de estilos global que oculta .notice en todas las páginas del admin elimina también los avisos del núcleo sobre actualizaciones de seguridad críticas. Limita los selectores a tus propios contenedores (.wppoland-support-wrap) o a tus propios sufijos de hook.

Para los colores de marca en la barra de administración, sobrescribe pocos selectores documentados y prueba tanto el editor clásico como el de bloques. Gutenberg tiene su propia interfaz: un #wpadminbar { background } amplio está bien, mientras que !important sobre .components-button es la receta para un editor roto.

/* assets/admin.css: alcance estrecho */
#wpadminbar {
	background: #1f2937;
}

#toplevel_page_wppoland-support .wp-menu-image::before {
	color: #059669;
}

.wppoland-support-wrap .card {
	max-width: 40rem;
}

Si admin.js habla con la REST API, usa wp_create_nonce( 'wp_rest' ) o tu propio nonce localizado como en el ejemplo. “Solo usuarios con sesión” no basta sin comprobar el nonce en el servidor. Evita además llamar a wp_print_scripts directamente en el callback de add_menu_page, porque rompe el orden de dependencias.

#Cambiar los colores del admin de WordPress con un esquema de color propio

Los colores del menú lateral y de la barra de herramientas vienen del esquema de color que cada usuario elige en su perfil. En lugar de sobrescribirlos con !important, puedes registrar tu propio esquema con wp_admin_css_color. La función recibe una clave única, un nombre, la URL del archivo CSS con el esquema, una lista de colores que sirven de vista previa en el perfil y, de forma opcional, los colores de los iconos SVG (base, focus y current).

function wppoland_register_admin_scheme(): void {
	wp_admin_css_color(
		'wppoland',
		'WPPoland',
		plugin_dir_url( __FILE__ ) . 'assets/admin-colors.css',
		[ '#1f2937', '#059669', '#d1fae5', '#ffffff' ],
		[
			'base'    => '#d1d5db',
			'focus'   => '#ffffff',
			'current' => '#ffffff',
		]
	);
}
add_action( 'admin_init', 'wppoland_register_admin_scheme' );

El núcleo registra sus propios esquemas en el mismo hook: register_admin_color_schemes está enganchado a admin_init con prioridad 1 en wp-admin/includes/admin-filters.php. La elección se guarda como la opción de usuario admin_color, y el admin recibe la clase de body admin-color- más la clave, así que puedes dirigir reglas adicionales solo a tu esquema.

Con el filtro get_user_option_admin_color puedes forzar el esquema para todos, pero así quitas la elección a los usuarios. Hay quien elige un esquema porque su contraste le resulta más legible. Tu esquema como una opción más es un valor por defecto más seguro.

#Errores frecuentes al personalizar el admin de WordPress

Tres errores que se repiten en producción:

  1. Ocultar todos los avisos. .notice { display: none } elimina tanto el spam de plugins como los avisos críticos del núcleo. Filtra por la clase del plugin o usa remove_action sobre callbacks conocidos de admin_notices.
  2. Quitar Plugins o Actualizaciones a todos. Los roles de cliente no deben instalar plugins. El administrador sí. Si ocultas Actualizaciones a manage_options, pierdes de vista las versiones de seguridad.
  3. Reescribir pantallas de la Settings API. No sustituyas options-general.php por una página “simplificada” propia salvo que reproduzcas todas las opciones necesarias y su validación. Una copia incompleta rompe los enlaces permanentes, la URL del sitio y el idioma.

Cuando personalices el editor de bloques, usa enqueue_block_editor_assets por separado de admin_enqueue_scripts y carga solo lo que el editor necesita.

Una prueba de regresión práctica: abre la lista de entradas, Gutenberg con una página, la Biblioteca de medios, WooCommerce > Pedidos y tu página de Soporte, como Administrador y como Editor. Si alguna de esas pantallas pierde botones, avisos o navegación con teclado, revierte el CSS antes de entregar.

#Cómo personalizar la barra de administración de WordPress

El toolbar de WordPress ocupa espacio valioso en la parte superior de la pantalla y es visible en el frontend para los usuarios con sesión. Personalizarlo elimina distracciones y deja sitio para acciones rápidas como “Limpiar caché”, siempre con nonce y comprobación de capacidad, no con una URL GET abierta.

function wppoland_customize_toolbar( WP_Admin_Bar $wp_admin_bar ): void {
	$wp_admin_bar->remove_node( 'wp-logo' );
	$wp_admin_bar->remove_node( 'comments' );

	if ( ! current_user_can( 'manage_options' ) ) {
		return;
	}

	$wp_admin_bar->add_node(
		[
			'id'    => 'wppoland-clear-cache',
			'title' => 'Limpiar caché',
			'href'  => wp_nonce_url(
				admin_url( 'admin-post.php?action=wppoland_clear_cache' ),
				'wppoland_clear_cache'
			),
			'meta'  => [ 'title' => 'Vaciar la caché de objetos' ],
		]
	);
}
add_action( 'admin_bar_menu', 'wppoland_customize_toolbar', 999 );

Gestiona admin_post_wppoland_clear_cache con check_admin_referer y redirige de vuelta. Sin nonce, el botón rápido se convierte en un objetivo CSRF.

#Añadir widgets personalizados al escritorio de WordPress

El dashboard predeterminado de WordPress está lleno de widgets poco útiles para la mayoría de los clientes, como “Borrador rápido” o “Eventos y noticias de WordPress”. Sustitúyelos por el estado que el cliente pregunta de verdad: copias de seguridad, disponibilidad, último contenido publicado.

function wppoland_dashboard_widgets(): void {
	remove_meta_box( 'dashboard_primary', 'dashboard', 'side' );
	remove_meta_box( 'dashboard_quick_press', 'dashboard', 'side' );

	wp_add_dashboard_widget(
		'wppoland_status_widget',
		'Estado del sitio',
		'wppoland_render_status_widget'
	);
}
add_action( 'wp_dashboard_setup', 'wppoland_dashboard_widgets' );

function wppoland_render_status_widget(): void {
	$post_count = wp_count_posts()->publish;

	echo '<p><strong>Publicaciones</strong>: ' . esc_html( (string) $post_count ) . '</p>';
	echo '<p><strong>WordPress</strong>: ' . esc_html( get_bloginfo( 'version' ) ) . '</p>';
	echo '<p><strong>PHP</strong>: ' . esc_html( phpversion() ) . '</p>';
	echo '<p><a href="' . esc_url( admin_url( 'admin.php?page=wppoland-support' ) ) . '">Abrir soporte</a></p>';
}

No escribas “Actualizado” con una marca verde si el widget no lee la versión real. Un widget de estado que miente es peor que ningún widget. Los callbacks deben ser baratos: no llames a API externas de forma síncrona al pintar el escritorio, guarda el resultado en un transient y muestra cuándo se obtuvo.

#Qué hacen realmente los widgets por defecto del escritorio

Una razón habitual para quitar los widgets por defecto es la velocidad. El código fuente de wp-admin/includes/dashboard.php muestra que la ganancia es pequeña:

  • Eventos y noticias de WordPress (dashboard_primary) carga el feed mediante un widget con caché. El HTML generado se guarda en un transient por idioma y, cuando esa caché está vacía, el widget muestra un indicador de carga y trae el contenido por AJAX en lugar de retener la página.
  • Borrador rápido (dashboard_quick_press) reutiliza un único borrador automático por usuario, cuyo ID se guarda en la opción de usuario dashboard_quick_press_last_post_id. Los borradores automáticos de más de siete días los elimina wp_delete_auto_drafts, que se ejecuta con el evento programado wp_scheduled_auto_draft_delete.
  • Estado de salud del sitio (dashboard_site_health) lee un resultado guardado en el transient health-check-site-status-result en lugar de ejecutar todas las pruebas en cada visita.

El argumento para quitarlos es el foco, no la velocidad. Plantéate mantener la salud del sitio para los administradores, porque muestra avisos sobre la versión de PHP y módulos que faltan que, si no, pasan desapercibidos.

#Personalizar la página de acceso de WordPress con logo y enlace

La página de login es lo primero que ve el cliente cada día y completa la experiencia white-label. Tres hooks de wp-login.php cubren casi todo:

  • login_enqueue_scripts (desde la versión 3.1) es el hook para hojas de estilo y scripts en la página de acceso, el equivalente de admin_enqueue_scripts en wp-admin.
  • login_headerurl controla adónde enlaza el logo. Por defecto es wordpress.org.
  • login_headertext (desde la versión 5.2) controla el texto del enlace del logo. El filtro antiguo login_headertitle está obsoleto desde la 5.2, con la nota de que no se recomienda un atributo title en el logo por motivos de accesibilidad.
function wppoland_login_assets(): void {
	wp_enqueue_style(
		'wppoland-login',
		plugin_dir_url( __FILE__ ) . 'assets/login.css',
		[],
		'2026.10.14'
	);
}
add_action( 'login_enqueue_scripts', 'wppoland_login_assets' );

add_filter( 'login_headerurl', fn() => home_url( '/' ) );
add_filter( 'login_headertext', fn() => get_bloginfo( 'name' ) );
/* assets/login.css */
.login h1 a {
	background-image: url("../img/logo-cliente.svg");
	background-size: contain;
	width: 100%;
	height: 80px;
}

Los lectores de pantalla leen el texto del enlace, así que el nombre del sitio es mejor opción que una cadena vacía. Prueba la página con un plugin de doble factor o SSO activo, porque añaden campos propios que un CSS demasiado ajustado puede sacar de la pantalla.

#Qué revisar antes de entregar un admin de WordPress personalizado

Recorre esta lista con una cuenta de rol de cliente en staging:

  1. El menú muestra solo lo que el rol necesita; el administrador sigue viéndolo todo.
  2. Las URL directas a Herramientas, Ajustes o el editor de archivos están bloqueadas donde corresponde.
  3. El CSS y el JS del admin se cargan solo en los $hook_suffix permitidos.
  4. El editor de bloques y la Biblioteca de medios funcionan sin botones rotos ni avisos ocultos.
  5. Las acciones de la barra de herramientas exigen nonce y la capacidad adecuada.
  6. El código vive en un plugin must-use (o en un plugin de agencia), no en el tema.
  7. La página de ajustes guarda con el rol del cliente, no solo como administrador.
  8. El logo de acceso enlaza al sitio y tiene un texto de enlace legible.

Documenta en el README del proyecto qué menús se ocultan para cada rol y qué plugin must-use es dueño de las reglas. El siguiente desarrollador no debería tener que reconstruir los callbacks de admin_menu para entender por qué falta Ajustes para el gestor de la tienda.

#Cómo hacer marca blanca del admin de WordPress en 90 días

Este calendario ordena el trabajo de marca y seguridad del área de administración:

  • Días 1-30 (control de acceso): registra roles de cliente con capacidades mínimas detrás de una comprobación de versión. Añade los hooks de limpieza del menú en un plugin must-use para ocultar opciones sin uso a quien no es administrador. Usa map_meta_cap para que los roles restringidos no puedan editar ni borrar páginas clave (por ejemplo, la portada o la página de contacto).
  • Días 31-60 (escritorio y ajustes): crea widgets del escritorio que muestren el estado real y enlaces de soporte. Usa la Settings API para una página de ajustes propia con validación y abre su grupo de opciones al rol del cliente con el filtro de capacidad. Añade accesos en la barra de herramientas protegidos con nonce.
  • Días 61-90 (marca y acceso): carga los estilos del admin con admin_enqueue_scripts, registra un esquema de color y aplica la marca a la pantalla de acceso. Termina con la lista de entrega de arriba, probada con cada rol de cliente.

#Qué es lo más importante al personalizar el admin de WordPress

Personalizar el área de administración es experiencia de usuario y control de acceso, no cosmética. Carga recursos con admin_enqueue_scripts, construye y limpia menús en admin_menu, comprueba capacidades antes de ocultar o bloquear, aísla el código en un plugin must-use y mantén el CSS y el JS lejos de las pantallas del núcleo que no son tuyas.

Consulta también nuestros servicios de mantenimiento WordPress y auditoría de seguridad para mantener tu sitio optimizado y seguro.

Siguiente paso

Transforma el artículo en una implementación real

Este bloque refuerza el enlazado interno y lleva al lector al siguiente paso más útil dentro de la arquitectura del sitio.

¿Quieres implementar esto en tu sitio?

Si quieres transformar el artículo en mejoras concretas, rediseño o un plan de implementación, puedo cerrar el alcance y ejecutar.

Cluster relacionado

Explora otros servicios WordPress y base de conocimiento

Refuerza tu negocio con soporte técnico profesional en áreas clave del ecosistema WordPress.

Preguntas frecuentes

Respuestas prácticas para aplicar el tema en la ejecución real.

¿Estas técnicas funcionan con cualquier tema WordPress?#
Sí, porque el código vive en un plugin must-use y no en el tema. Solo las reglas CSS que apuntan a clases concretas de un tema necesitan adaptarse.
¿Estos métodos funcionan con constructores de páginas como Elementor?#
Sí. Los hooks del núcleo funcionan junto a los constructores de páginas. Revisa los slugs de menú que añade el constructor antes de ocultarlos, porque no siempre son evidentes.
¿remove_menu_page bloquea el acceso a una página?#
No. Solo quita el enlace del menú. Quien conozca la URL sigue entrando si su rol tiene la capacidad. El bloqueo real exige comprobar la capacidad en current_screen o admin_init, o un rol que no la tenga.
¿Cómo dejo que un editor guarde una página de la Settings API?#
wp-admin/options.php exige manage_options por defecto. El filtro option_page_capability_ seguido del nombre del grupo de ajustes permite fijar otra capacidad, como edit_pages, solo para ese grupo.
¿Quitar los widgets por defecto acelera el escritorio?#
Muy poco. El widget de noticias guarda su salida en un transient y la carga por AJAX, y el widget de salud del sitio lee un resultado almacenado. La ganancia es foco para el usuario, no velocidad.

¿Necesitas un FAQ adaptado a tu sector y mercado? Preparamos una versión alineada con tus objetivos de negocio.

Hablemos

Artículos Relacionados