Close Menu
    DevStackTipsDevStackTips
    • Home
    • News & Updates
      1. Tech & Work
      2. View All

      Sunshine And March Vibes (2025 Wallpapers Edition)

      June 1, 2025

      The Case For Minimal WordPress Setups: A Contrarian View On Theme Frameworks

      June 1, 2025

      How To Fix Largest Contentful Paint Issues With Subpart Analysis

      June 1, 2025

      How To Prevent WordPress SQL Injection Attacks

      June 1, 2025

      7 MagSafe accessories that I recommend every iPhone user should have

      June 1, 2025

      I replaced my Kindle with an iPad Mini as my ebook reader – 8 reasons why I don’t regret it

      June 1, 2025

      Windows 11 version 25H2: Everything you need to know about Microsoft’s next OS release

      May 31, 2025

      Elden Ring Nightreign already has a duos Seamless Co-op mod from the creator of the beloved original, and it’ll be “expanded on in the future”

      May 31, 2025
    • Development
      1. Algorithms & Data Structures
      2. Artificial Intelligence
      3. Back-End Development
      4. Databases
      5. Front-End Development
      6. Libraries & Frameworks
      7. Machine Learning
      8. Security
      9. Software Engineering
      10. Tools & IDEs
      11. Web Design
      12. Web Development
      13. Web Security
      14. Programming Languages
        • PHP
        • JavaScript
      Featured

      Student Record Android App using SQLite

      June 1, 2025
      Recent

      Student Record Android App using SQLite

      June 1, 2025

      When Array uses less memory than Uint8Array (in V8)

      June 1, 2025

      Laravel 12 Starter Kits: Definite Guide Which to Choose

      June 1, 2025
    • Operating Systems
      1. Windows
      2. Linux
      3. macOS
      Featured

      Photobooth is photobooth software for the Raspberry Pi and PC

      June 1, 2025
      Recent

      Photobooth is photobooth software for the Raspberry Pi and PC

      June 1, 2025

      Le notizie minori del mondo GNU/Linux e dintorni della settimana nr 22/2025

      June 1, 2025

      Rilasciata PorteuX 2.1: Novità e Approfondimenti sulla Distribuzione GNU/Linux Portatile Basata su Slackware

      June 1, 2025
    • Learning Resources
      • Books
      • Cheatsheets
      • Tutorials & Guides
    Home»News & Updates»Baseline Status in a WordPress Block

    Baseline Status in a WordPress Block

    February 5, 2025

    You know about Baseline, right? And you may have heard that the Chrome team made a web component for it.

    Here it is!

    Of course, we could simply drop the HTML component into the page. But I never know where we’re going to use something like this. The Almanac, obs. But I’m sure there are times where embedded it in other pages and posts makes sense.

    That’s exactly what WordPress blocks are good for. We can take an already reusable component and make it repeatable when working in the WordPress editor. So that’s what I did! That component you see up there is the <baseline-status> web component formatted as a WordPress block. Let’s drop another one in just for kicks.

    Pretty neat! I saw that Pawel Grzybek made an equivalent for Hugo. There’s an Astro equivalent, too. Because I’m fairly green with WordPress block development I thought I’d write a bit up on how it’s put together. There are still rough edges that I’d like to smooth out later, but this is a good enough point to share the basic idea.

    Scaffolding the project

    I used the @wordpress/create-block package to bootstrap and initialize the project. All that means is I cd‘d into the /wp-content/plugins directory from the command line and ran the install command to plop it all in there.

    npm install @wordpress/create-block
    Mac Finder window with the WordPress plugins directory open and showing the baseline-status plugin folder.
    The command prompts you through the setup process to name the project and all that.

    The baseline-status.php file is where the plugin is registered. And yes, it’s looks completely the same as it’s been for years, just not in a style.css file like it is for themes. The difference is that the create-block package does some lifting to register the widget so I don’t have to:

    <?php
    /**
     * Plugin Name:       Baseline Status
     * Plugin URI:        https://css-tricks.com
     * Description:       Displays current Baseline availability for web platform features.
     * Requires at least: 6.6
     * Requires PHP:      7.2
     * Version:           0.1.0
     * Author:            geoffgraham
     * License:           GPL-2.0-or-later
     * License URI:       https://www.gnu.org/licenses/gpl-2.0.html
     * Text Domain:       baseline-status
     *
     * @package CssTricks
     */
    
    if ( ! defined( 'ABSPATH' ) ) {
      exit; // Exit if accessed directly.
    }
    
    function csstricks_baseline_status_block_init() {
      register_block_type( __DIR__ . '/build' );
    }
    add_action( 'init', 'csstricks_baseline_status_block_init' );
    
    ?>

    The real meat is in src directory.

    Mac Finder window with the WordPress project's src folder open with seven files, two are highlighted in orange: edit.js and render.php.

    The create-block package also did some filling of the blanks in the block-json file based on the onboarding process:

    {
      "$schema": "https://schemas.wp.org/trunk/block.json",
      "apiVersion": 2,
      "name": "css-tricks/baseline-status",
      "version": "0.1.0",
      "title": "Baseline Status",
      "category": "widgets",
      "icon": "chart-pie",
      "description": "Displays current Baseline availability for web platform features.",
      "example": {},
      "supports": {
        "html": false
      },
      "textdomain": "baseline-status",
      "editorScript": "file:./index.js",
      "editorStyle": "file:./index.css",
      "style": "file:./style-index.css",
      "render": "file:./render.php",
      "viewScript": "file:./view.js"
    }

    Going off some tutorials published right here on CSS-Tricks, I knew that WordPress blocks render twice — once on the front end and once on the back end — and there’s a file for each one in the src folder:

    • render.php: Handles the front-end view
    • edit.js: Handles the back-end view

    The front-end and back-end markup

    Cool. I started with the <baseline-status> web component’s markup:

    <script src="https://cdn.jsdelivr.net/npm/baseline-status@1.0.8/baseline-status.min.js" type="module"></script>
    <baseline-status featureId="anchor-positioning"></baseline-status>

    I’d hate to inject that <script> every time the block pops up, so I decided to enqueue the file conditionally based on the block being displayed on the page. This is happening in the main baseline-status.php file which I treated sorta the same way as a theme’s functions.php file. It’s just where helper functions go.

    // ... same code as before
    
    // Enqueue the minified script
    function csstricks_enqueue_block_assets() {
      wp_enqueue_script(
        'baseline-status-widget-script',
        'https://cdn.jsdelivr.net/npm/baseline-status@1.0.4/baseline-status.min.js',
        array(),
        '1.0.4',
        true
      );
    }
    add_action( 'enqueue_block_assets', 'csstricks_enqueue_block_assets' );
    
    // Adds the 'type="module"' attribute to the script
    function csstricks_add_type_attribute($tag, $handle, $src) {
      if ( 'baseline-status-widget-script' === $handle ) {
        $tag = '<script type="module" src="' . esc_url( $src ) . '"></script>';
      }
      return $tag;
    }
    add_filter( 'script_loader_tag', 'csstricks_add_type_attribute', 10, 3 );
    
    // Enqueues the scripts and styles for the back end
    function csstricks_enqueue_block_editor_assets() {
      // Enqueues the scripts
      wp_enqueue_script(
        'baseline-status-widget-block',
        plugins_url( 'block.js', __FILE__ ),
        array( 'wp-blocks', 'wp-element', 'wp-editor' ),
        false,
      );
    
      // Enqueues the styles
      wp_enqueue_style(
        'baseline-status-widget-block-editor',
        plugins_url( 'style.css', __FILE__ ),
        array( 'wp-edit-blocks' ),
        false,
      );
    }
    add_action( 'enqueue_block_editor_assets', 'csstricks_enqueue_block_editor_assets' );

    The final result bakes the script directly into the plugin so that it adheres to the WordPress Plugin Directory guidelines. If that wasn’t the case, I’d probably keep the hosted script intact because I’m completely uninterested in maintaining it. Oh, and that csstricks_add_type_attribute() function is to help import the file as an ES module. There’s a wp_enqueue_script_module() action available to hook into that should handle that, but I couldn’t get it to do the trick.

    With that in hand, I can put the component’s markup into a template. The render.php file is where all the front-end goodness resides, so that’s where I dropped the markup:

    <baseline-status
      <?php echo get_block_wrapper_attributes(); ?> 
      featureId="[FEATURE]">
    </baseline-status>

    That get_block_wrapper_attibutes() thing is recommended by the WordPress docs as a way to output all of a block’s information for debugging things, such as which features it ought to support.

    [FEATURE]is a placeholder that will eventually tell the component which web platform to render information about. We may as well work on that now. I can register attributes for the component in block.json:

    "attributes": { "showBaselineStatus": {
      "featureID": {
      "type": "string"
      }
    },

    Now we can update the markup in render.php to echo the featureID when it’s been established.

    <baseline-status
      <?php echo get_block_wrapper_attributes(); ?> 
      featureId="<?php echo esc_html( $featureID ); ?>">
    </baseline-status>

    There will be more edits to that markup a little later. But first, I need to put the markup in the edit.js file so that the component renders in the WordPress editor when adding it to the page.

    <baseline-status { ...useBlockProps() } featureId={ featureID }></baseline-status>

    useBlockProps is the JavaScript equivalent of get_block_wrapper_attibutes() and can be good for debugging on the back end.

    At this point, the block is fully rendered on the page when dropped in! The problems are:

    • It’s not passing in the feature I want to display.
    • It’s not editable.

    I’ll work on the latter first. That way, I can simply plug the right variable in there once everything’s been hooked up.

    Block settings

    One of the nicer aspects of WordPress DX is that we have direct access to the same controls that WordPress uses for its own blocks. We import them and extend them where needed.

    Hostinger

    I started by importing the stuff in edit.js:

    import { InspectorControls, useBlockProps } from '@wordpress/block-editor';
    import { PanelBody, TextControl } from '@wordpress/components';
    import './editor.scss';

    This gives me a few handy things:

    • InspectorControls are good for debugging.
    • useBlockProps are what can be debugged.
    • PanelBody is the main wrapper for the block settings.
    • TextControl is the field I want to pass into the markup where [FEATURE] currently is.
    • editor.scss provides styles for the controls.

    Before I get to the controls, there’s an Edit function needed to use as a wrapper for all the work:

    export default function Edit( { attributes, setAttributes } ) {
      // Controls
    }

    First is InspectorTools and the PanelBody:

    export default function Edit( { attributes, setAttributes } ) {
      // React components need a parent element
      <>
        <InspectorControls>
          <PanelBody title={ __( 'Settings', 'baseline-status' ) }>
          // Controls
          </PanelBody>
        </InspectorControls>
      </>
    }

    Then it’s time for the actual text input control. I really had to lean on this introductory tutorial on block development for the following code, notably this section.

    export default function Edit( { attributes, setAttributes } ) {
      <>
        <InspectorControls>
          <PanelBody title={ __( 'Settings', 'baseline-status' ) }>
            // Controls
            <TextControl
              label={ __(
                'Feature', // Input label
                'baseline-status'
              ) }
              value={ featureID || '' }
              onChange={ ( value ) =>
                setAttributes( { featureID: value } )
              }
            />
         </PanelBody>
        </InspectorControls>
      </>
    }

    Tie it all together

    At this point, I have:

    • The front-end view
    • The back-end view
    • Block settings with a text input
    • All the logic for handling state

    Oh yeah! Can’t forget to define the featureID variable because that’s what populates in the component’s markup. Back in edit.js:

    const { featureID } = attributes;

    In short: The feature’s ID is what constitutes the block’s attributes. Now I need to register that attribute so the block recognizes it. Back in block.json in a new section:

    "attributes": {
      "featureID": {
        "type": "string"
      }
    },

    Pretty straightforward, I think. Just a single text field that’s a string. It’s at this time that I can finally wire it up to the front-end markup in render.php:

    <baseline-status
      <?php echo get_block_wrapper_attributes(); ?>
      featureId="<?php echo esc_html( $featureID ); ?>">
    </baseline-status>

    Styling the component

    I struggled with this more than I care to admit. I’ve dabbled with styling the Shadow DOM but only academically, so to speak. This is the first time I’ve attempted to style a web component with Shadow DOM parts on something being used in production.

    If you’re new to Shadow DOM, the basic idea is that it prevents styles and scripts from “leaking” in or out of the component. This is a big selling point of web components because it’s so darn easy to drop them into any project and have them “just” work.

    But how do you style a third-party web component? It depends on how the developer sets things up because there are ways to allow styles to “pierce” through the Shadow DOM. Ollie Williams wrote “Styling in the Shadow DOM With CSS Shadow Parts” for us a while back and it was super helpful in pointing me in the right direction. Chris has one, too.

    A few other more articles I used:

    • “Options for styling web components” (Nolan Lawson, super well done!)
    • “Styling web components” (Chris Ferdinandi)
    • “Styling” (webcomponents.guide)

    First off, I knew I could select the <baseline-status> element directly without any classes, IDs, or other attributes:

    baseline-status {
      /* Styles! */
    }

    I peeked at the script’s source code to see what I was working with. I had a few light styles I could use right away on the type selector:

    baseline-status {
      background: #000;
      border: solid 5px #f8a100;
      border-radius: 8px;
      color: #fff;
      display: block;
      margin-block-end: 1.5em;
      padding: .5em;
    }

    I noticed a CSS color variable in the source code that I could use in place of hard-coded values, so I redefined them and set them where needed:

    baseline-status {
      --color-text: #fff;
      --color-outline: var(--orange);
    
      border: solid 5px var(--color-outline);
      border-radius: 8px;
      color: var(--color-text);
      display: block;
      margin-block-end: var(--gap);
      padding: calc(var(--gap) / 4);
    }

    Now for a tricky part. The component’s markup looks close to this in the DOM when fully rendered:

    <baseline-status class="wp-block-css-tricks-baseline-status" featureid="anchor-positioning"></baseline-status>
    <h1>Anchor positioning</h1>
    <details>
      <summary aria-label="Baseline: Limited availability. Supported in Chrome: yes. Supported in Edge: yes. Supported in Firefox: no. Supported in Safari: no.">
        <baseline-icon aria-hidden="true" support="limited"></baseline-icon>
        <div class="baseline-status-title" aria-hidden="true">
          <div>Limited availability</div>
            <div class="baseline-status-browsers">
            <!-- Browser icons -->
            </div>
        </div>
      </summary><p>This feature is not Baseline because it does not work in some of the most widely-used browsers.</p><p><a href="https://github.com/web-platform-dx/web-features/blob/main/features/anchor-positioning.yml">Learn more</a></p></details>
    <baseline-status class="wp-block-css-tricks-baseline-status" featureid="anchor-positioning"></baseline-status>

    I wanted to play with the idea of hiding the <h1> element in some contexts but thought twice about it because not displaying the title only really works for Almanac content when you’re on the page for the same feature as what’s rendered in the component. Any other context and the heading is a “need” for providing context as far as what feature we’re looking at. Maybe that can be a future enhancement where the heading can be toggled on and off.

    Voilà

    Get the plugin!

    This is freely available in the WordPress Plugin Directory as of today! This is my very first plugin I’ve submitted to WordPress on my own behalf, so this is really exciting for me!

    Get the plugin

    Future improvements

    This is far from fully baked but definitely gets the job done for now. In the future it’d be nice if this thing could do a few more things:

    • Live update: The widget does not update on the back end until the page refreshes. I’d love to see the final rendering before hitting Publish on something. I got it where typing into the text input is instantly reflected on the back end. It’s just that the component doesn’t re-render to show the update.
    • Variations: As in “large” and “small”.
    • Heading: Toggle to hide or show, depending on where the block is used.

    Baseline Status in a WordPress Block originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

    Source: Read More 

    Facebook Twitter Reddit Email Copy Link
    Previous ArticleDigital AGI Mind-Clones: Hire the World’s Greatest Thinkers
    Next Article Distribution Release: Univention Corporate Server 5.2-0

    Related Posts

    News & Updates

    7 MagSafe accessories that I recommend every iPhone user should have

    June 1, 2025
    News & Updates

    I replaced my Kindle with an iPad Mini as my ebook reader – 8 reasons why I don’t regret it

    June 1, 2025
    Leave A Reply Cancel Reply

    Continue Reading

    DAI#46 – Skeleton key, exam cheats, and famous AI voices

    Artificial Intelligence

    3 ways Google’s AI Mode is going to change how you shop online

    News & Updates

    Error’d: Just Beastly

    Development

    The Minecraft Movie reviews are rolling in and it’s not looking good — “You will want to block your memory.”

    News & Updates

    Highlights

    CVE-2025-37878 – Linux Kernel Perf Core Context Assignment Vulnerability

    May 9, 2025

    CVE ID : CVE-2025-37878

    Published : May 9, 2025, 7:16 a.m. | 4 hours, 51 minutes ago

    Description : In the Linux kernel, the following vulnerability has been resolved:

    perf/core: Fix WARN_ON(!ctx) in __free_event() for partial init

    Move the get_ctx(child_ctx) call and the child_event->ctx assignment to
    occur immediately after the child event is allocated. Ensure that
    child_event->ctx is non-NULL before any subsequent error path within
    inherit_event calls free_event(), satisfying the assumptions of the
    cleanup code.

    Details:

    There’s no clear Fixes tag, because this bug is a side-effect of
    multiple interacting commits over time (up to 15 years old), not
    a single regression.

    The code initially incremented refcount then assigned context
    immediately after the child_event was created. Later, an early
    validity check for child_event was added before the
    refcount/assignment. Even later, a WARN_ON_ONCE() cleanup check was
    added, assuming event->ctx is valid if the pmu_ctx is valid.
    The problem is that the WARN_ON_ONCE() could trigger after the initial
    check passed but before child_event->ctx was assigned, violating its
    precondition. The solution is to assign child_event->ctx right after
    its initial validation. This ensures the context exists for any
    subsequent checks or cleanup routines, resolving the WARN_ON_ONCE().

    To resolve it, defer the refcount update and child_event->ctx assignment
    directly after child_event->pmu_ctx is set but before checking if the
    parent event is orphaned. The cleanup routine depends on
    event->pmu_ctx being non-NULL before it verifies event->ctx is
    non-NULL. This also maintains the author’s original intent of passing
    in child_ctx to find_get_pmu_context before its refcount/assignment.

    [ mingo: Expanded the changelog from another email by Gabriel Shahrouzi. ]

    Severity: 0.0 | NA

    Visit the link for more details, such as CVSS details, affected products, timeline, and more…

    New ‘Sneaky 2FA’ Phishing Kit Targets Microsoft 365 Accounts with 2FA Code Bypass

    January 17, 2025

    Top 5 Best Facility Management Software in the Market

    January 6, 2025

    Critical Remote Code Execution Vulnerability Addressed in GiveWP Plugin

    August 20, 2024
    © DevStackTips 2025. All rights reserved.
    • Contact
    • Privacy Policy

    Type above and press Enter to search. Press Esc to cancel.