Integrations

WordPress

Enqueue the built files where a page needs them, as any stylesheet and script, and keep Elementor widgets started.

WordPress loads Pelatform UI as it loads any stylesheet and script: copy the files into your theme or plugin and enqueue them on the pages that use them. This setup was checked in WordPress 7.1 in Chromium, on pages of the Hello Elementor theme with Elementor and its Site Settings, on a dashboard page and on an admin page.

Enqueue the files yourself on a site where only your theme uses Pelatform UI and Elementor is not active. When a theme and plugins, or several plugins, use it on one site, require wordpress.php in each: a page runs one copy and one version, and the file registers the newest copy once, in the right order; on an Elementor site it also keeps the Site Settings out of the components.

Enqueue the files

Copy the Full files into your theme as a pelatform-ui folder and enqueue them. Scripts load deferred in the footer, which needs WordPress 6.3 or later.

PHP
add_action('wp_enqueue_scripts', function () {
    if (!is_page('dashboard')) {
        return;
    }
    $url = get_theme_file_uri('pelatform-ui/');
    wp_enqueue_style('plf-ui-preflight', $url . 'core/pelatform-ui-preflight.prefixed.min.css', array(), '0.1.0');
    wp_enqueue_style('plf-ui', $url . 'pelatform-ui.prefixed.min.css', array('plf-ui-preflight'), '0.1.0');
    wp_enqueue_script('plf-ui', $url . 'pelatform-ui.min.js', array(), '0.1.0', array('strategy' => 'defer', 'in_footer' => true));
}, 20);

The page then prints the styles in this order:

  1. The theme’s and other plugins’ styles, enqueued at the default priority
  2. Preflight
  3. Pelatform UI
  4. Icons and extensions, when a page enqueues them
  5. Your stylesheet, enqueued after them with plf-ui among its dependencies

Priority 20 prints the files after the theme’s own styles, so a theme rule as specific as a component’s, such as Hello Elementor’s button hover, does not win by coming later. Preflight comes first. It resets only what sits inside a root, such as a heading’s margins and a link’s color, so markup written with utilities, as the blocks are, looks as it does here, while the theme and other plugins keep their styles outside the roots. Your own stylesheet and scripts list plf-ui among their dependencies, so they load after it and your overrides win.

An extension adds its own files after them, in the same function, as its guide’s files to load list them; here DataTable:

PHP
wp_enqueue_style('plf-ui-datatable', $url . 'extensions/datatable.prefixed.min.css', array('plf-ui'), '0.1.0');
wp_enqueue_script('plf-ui-datatable', $url . 'extensions/datatable.min.js', array('plf-ui'), '0.1.0', array('strategy' => 'defer', 'in_footer' => true));

Icon classes take their icons from a stylesheet enqueued the same way, icons/icons.prefixed.min.css, and icons/icons-filled.prefixed.min.css for the filled icons:

PHP
wp_enqueue_style('plf-ui-icons', $url . 'icons/icons.prefixed.min.css', array('plf-ui'), '0.1.0');

In a plugin, plugins_url('pelatform-ui/', __FILE__) gives the folder’s address.

Choose one mode

WordPress pages share their CSS with the theme and plugins, so load Prefixed CSS, where every visual class starts with plf:. Use Clean CSS only on a page whose CSS you own.

Stylesheet Visual classes
pelatform-ui.prefixed.min.css Prefixed: plf:btn
pelatform-ui.min.css Clean: btn

Both modes use the same data-plf-* hooks and Browser script.

Root

Components sit inside an element with data-plf-root, such as the wrapper a template or shortcode prints:

PHP
add_shortcode('save_button', function () {
    return '<div data-plf-root><button class="plf:btn plf:btn-primary">Save</button></div>';
});

Theme colors

Each root declares its own tokens, so --plf-* variables set on :root do not reach its components, and a theme’s or Elementor’s colors stay their own. Map them onto the roots in an inline style on the plf-ui handle, after the files are enqueued; here Elementor’s primary color and text font:

PHP
wp_add_inline_style('plf-ui', '[data-plf-root] {
    --plf-color-primary: var(--e-global-color-primary, var(--plf-blue-600));
    --plf-font-family: var(--e-global-typography-text-font-family, system-ui), system-ui, sans-serif;
}');

A block theme’s colors are its --wp--preset--color--{slug} variables, used the same way. Check that --plf-color-on-primary, white by default, reads on your primary. A dark or auto root keeps its dark colors; Tokens shows a brand for both. If the theme sets html { font-size: 62.5% }, add --plf-rem: 1.6rem; to the rule (Installation).

Colors from your settings

A plugin whose customers choose their colors reads them from its settings and sets them on its root, here with a myplugin class on the root so that dark and auto roots take them too. Each color brings the text on it, white while white reads at 4.5:1 or more and near-black on a lighter color, and its text on the page; a status color also brings its soft fill (Tokens); a color left unset keeps the library’s:

PHP
/** White text while it reads at 4.5:1 or more on the color, near-black on a lighter one. */
function myplugin_on_color($color) {
    $hex = ltrim($color, '#');
    $hex = strlen($hex) === 3 ? preg_replace('/./', '$0$0', $hex) : $hex;
    $luminance = 0;
    foreach (array(0.2126, 0.7152, 0.0722) as $index => $weight) {
        $channel = hexdec(substr($hex, $index * 2, 2)) / 255;
        $luminance += $weight * ($channel <= 0.04045 ? $channel / 12.92 : (($channel + 0.055) / 1.055) ** 2.4);
    }
    return 1.05 / ($luminance + 0.05) >= 4.5 ? '#fff' : '#0a0a0a';
}

add_action('wp_enqueue_scripts', function () {
    plf_ui_enqueue();
    $css = '';
    foreach (array('primary' => 50, 'success' => 40, 'info' => 40, 'warning' => 40, 'danger' => 40) as $name => $share) {
        $color = sanitize_hex_color(get_option("myplugin_$name"));
        if (!$color) {
            continue;
        }
        $css .= "--plf-color-$name: $color; --plf-color-on-$name: " . myplugin_on_color($color) . '; '
            . "--plf-color-$name-text: color-mix(in srgb, var(--plf-color-$name) $share%, var(--plf-color-text)); ";
        if ($name !== 'primary') {
            $css .= "--plf-color-$name-bg: color-mix(in srgb, var(--plf-color-$name) 15%, var(--plf-color-surface)); ";
        }
    }
    if ($css) {
        wp_add_inline_style('plf-ui', ".myplugin[data-plf-root] { $css}");
    }
});

The hover, the borders, primary-subtle and the focus ring follow the colors. Primary’s 50% keeps more of a brand color and reads at 4.5:1 or more for Tailwind CSS’s 400 to 950 colors; a status color’s 40% reads at 6.5:1 or more on its soft fill for their 500 and 600 colors. A higher share keeps more of the color, so check its contrast. The root takes the mode the customer chose, light, dark or auto:

PHP
echo '<div class="myplugin" data-plf-root data-plf-theme="' . esc_attr(get_option('myplugin_mode', 'light')) . '">';

Elementor and dynamic content

The Full script starts the page once. Elementor replaces a widget’s markup while you edit it and adds popups later; one more line, after the files are enqueued, starts what it adds and destroys what it removes (Dynamic content):

PHP
wp_add_inline_script('plf-ui', "document.addEventListener('DOMContentLoaded', () => PLF.observe(document));", 'before');

Site Settings

Elementor’s Site Settings, under Theme Style, write rules for every button, field, label, link, paragraph, heading and image on the page, more specific than a component’s. wordpress.php keeps them outside the roots: each such rule matches only outside a root, with its specificity unchanged, so Elementor’s widgets and the rest of the page keep the Theme Style and the components keep their own look. Inside a root, site content (data-plf-site), such as a page’s content in a dashboard layout, takes the Theme Style as the rest of the page does. It regenerates Elementor’s CSS once for each Pelatform UI version. Without the file the Theme Style reaches the components, so on an Elementor site require it and enqueue the files with plf_ui_enqueue().

Register every file

On a site that loads single components instead of Full, or many extensions across its pages, wordpress.php in the built files registers each file as a handle with the files it needs, so you enqueue a name instead of writing paths. Copy the whole folder, keeping manifest.json beside it, and require the file once:

PHP
require __DIR__ . '/pelatform-ui/wordpress.php';

add_action('wp_enqueue_scripts', function () {
    if (is_page('dashboard')) {
        plf_ui_enqueue('preflight', 'datatable');
    }
});

Each name passed to plf_ui_enqueue() adds to the Full files:

Name Loads
None The Full files
preflight Preflight
icons, icons-filled The outline and the filled icon stylesheets
An extension, such as datatable or date-picker That extension
An export adapter, such as datatable-excel That adapter

The file prints Pelatform UI’s styles, Preflight first, and the styles that depend on them, after the page’s other styles, at any priority and in any enqueue order. When several plugins on a site ship the file, the newest copy’s files are registered, once.

Handle Loads
plf-ui The Full stylesheet and script
plf-ui-core Core, for single files
plf-ui-preflight Preflight, printed before the other Pelatform UI styles
plf-ui-icons, plf-ui-icons-filled The outline and the filled icon stylesheets
plf-ui-{name} One component or extension, such as plf-ui-modal or plf-ui-datatable, with the handles it needs

On a page with the Full files, WordPress prints none of the single files they hold, so nothing loads twice; without Full, enqueue only what a page uses, such as wp_enqueue_style('plf-ui-modal') and wp_enqueue_script('plf-ui-modal'). Single scripts do not start themselves; start them once, on the Core script:

PHP
wp_add_inline_script('plf-ui-core', "document.addEventListener('DOMContentLoaded', () => PLF.init());", 'before');

The stylesheets are Prefixed; on a page whose CSS you own, a filter switches them to Clean:

PHP
add_filter('plf_ui_mode', function () {
    return 'clean';
});

Admin pages

An admin page enqueues the files from admin_enqueue_scripts, on its own screen only. A layout that fills the screen, such as the application shell, takes the space beside the admin menu and below the admin bar: print it outside .wrap, in a root of its own, with h-full in place of its drawer-sticky: the admin bar stays above it, so the page beside the sidebar scrolls rather than the screen.

PHP
add_action('admin_menu', function () {
    add_menu_page('My app', 'My app', 'manage_options', 'my-app', function () {
        echo '<div class="my-app" data-plf-root><!-- The application shell, with h-full in place of drawer-sticky --></div>';
    });
});

add_action('admin_enqueue_scripts', function ($hook) {
    if ($hook !== 'toplevel_page_my-app') {
        return;
    }
    plf_ui_enqueue('preflight');
    wp_add_inline_style('plf-ui', '.wp-admin #wpcontent { padding-left: 0; } #wpbody-content { padding-bottom: 0; }
        #wpfooter { display: none; } .my-app { height: calc(100dvh - var(--wp-admin--admin-bar--height, 0px)); }');
});

The fields and choices keep their own look against the admin’s form styles.

Dashboard pages

A dashboard whose pages are written with Elementor or the block editor, such as a member area, puts each page’s content in the application shell. A plugin registers a page template for it, which each page then chooses beside Elementor’s templates:

PHP
add_filter('theme_page_templates', function ($templates) {
    return $templates + array('my-dashboard.php' => 'My dashboard');
});

add_filter('template_include', function ($template) {
    return is_page() && get_page_template_slug() === 'my-dashboard.php' ? __DIR__ . '/templates/my-dashboard.php' : $template;
}, 99);

The template prints the whole page, as Elementor Canvas does, and the page’s content in place of the shell’s <!-- Page content -->, where Elementor edits it:

PHP
<!doctype html>
<html <?php language_attributes(); ?>>
<head>
<meta charset="<?php bloginfo('charset'); ?>">
<meta name="viewport" content="width=device-width, initial-scale=1">
<?php wp_head(); ?>
</head>
<body <?php body_class(); ?>>
<?php wp_body_open(); ?>
<div class="my-dashboard" data-plf-root>
  <!-- The application shell, whose page content is: -->
  <div class="plf:rounded-xl plf:bg-base-100" data-plf-root data-plf-theme="light" data-plf-site>
    <?php while (have_posts()) { the_post(); the_content(); } ?>
  </div>
</div>
<?php wp_footer(); ?>
</body>
</html>

The content is site content in a root of its own: Preflight leaves it and Elementor’s Site Settings reach it, as elsewhere on the site, and data-plf-theme="light" keeps it light when the shell turns dark, since its colors are chosen for a light page. The shell’s sticky layout scrolls with the page, so Elementor’s scroll effects work. The admin bar sits fixed above it, so the template’s page sets --plf-sidebar-top to the bar’s height, and to 0 under 600px, where the bar scrolls away:

PHP
add_action('wp_enqueue_scripts', function () {
    if (!is_page() || get_page_template_slug() !== 'my-dashboard.php') {
        return;
    }
    plf_ui_enqueue('preflight');
    wp_add_inline_style('plf-ui', '.my-dashboard { --plf-sidebar-top: var(--wp-admin--admin-bar--height, 0px); }
        @media (max-width: 600px) { .my-dashboard { --plf-sidebar-top: 0px; } }');
});

Who may open these pages is the site’s own rule, such as a membership plugin’s.

Extend a product

A plugin that extends a product built on Pelatform UI uses the product’s files: the product registers them, as Register every file shows, and the plugin enqueues them by handle and lists plf-ui among its own scripts’ dependencies.

PHP
add_action('wp_enqueue_scripts', function () {
    if (!function_exists('plf_ui_enqueue')) {
        return;
    }
    plf_ui_enqueue('datatable');
    wp_enqueue_script('my-addon', plugins_url('addon.js', __FILE__), array('plf-ui'), '1.0.0', array('strategy' => 'defer', 'in_footer' => true));
});

The check keeps the site running while the product is inactive. The plugin never brings a copy of its own, since a page runs one copy and one version of Pelatform UI: another version is reported and does not start (Core). Its scripts use the global PLF of the product’s script, such as PLF.Toast.getOrCreateInstance(element), rather than importing Pelatform UI.

See the WordPress enqueue reference.


Built with Pelatform UIv0.1.0

Documentation

Getting started

Foundations

Components

Actions

Data display

Navigation

Feedback

Data input

Layout

Advanced

Integrations

Blocks

Application

Authentication

Icons