Blogger Card Plugin with Web Components

Blogger Card Plugin with Web Components

Codeable.io

Building a Modern WordPress Blogger Cards Plugin with Web Components

Updated 2025

In this updated tutorial, I’ll show you how to create a production-ready Blogger Cards plugin for WordPress that displays user information in beautiful, interactive cards. This 2025 edition includes modern best practices, Gutenberg block support, accessibility features, and responsive design.

What We’ll Build

A WordPress plugin that displays authors and subscribers using interactive cards built with Web Components. The plugin features:

  • Native Gutenberg block integration
  • Shortcode support for backward compatibility
  • Full accessibility with ARIA labels and keyboard navigation
  • Responsive design with a mobile-first approach
  • Dark mode support
  • Modern JavaScript (ES6+) and CSS features
  • Security hardening with proper escaping and sanitization

You can extend this code to fit your needs, show single-user cards in blog posts, or integrate it directly into your theme.

Why Web Components?

Web Components provide encapsulation through Shadow DOM, making our user cards reusable and isolated from theme styles. This architectural pattern separates client and server-side code, making it easier to maintain and scale larger projects.

For more about Web Components, check out MDN’s Web Components documentation.

Download the complete plugin code from GitHub.

Project Structure

blogger-cards/
├── assets/
│   ├── card.css          # Grid and global styles
│   └── card.js           # Web Component definition
├── templates/
│   └── card.tpl.php      # Template for rendering cards
├── block.json            # Gutenberg block configuration
├── blogger-cards.php     # Main plugin file
└── index.php             # Security file

The Main Plugin File

Let’s start with blogger-cards.php. This is where we register our assets, shortcode, and Gutenberg block.

Modern Asset Loading

function blogger_cards_enqueue_assets() {
    $plugin_version = '1.0.0';

    wp_enqueue_style(
        'blogger-cards',
        plugins_url( 'assets/card.css', __FILE__ ),
        array(),
        $plugin_version,
        'all'
    );

    wp_enqueue_script(
        'blogger-cards',
        plugins_url( 'assets/card.js', __FILE__ ),
        array(),
        $plugin_version,
        array(
            'strategy'  => 'defer',
            'in_footer' => true,
        )
    );
}
add_action( 'wp_enqueue_scripts', 'blogger_cards_enqueue_assets' );

What’s New in 2025:

  • Using defer strategy for better performance
  • Proper versioning for cache busting
  • Named functions instead of anonymous closures for better debugging

Shortcode with Attributes

function blogger_cards_shortcode( $atts ) {
    $atts = shortcode_atts(
        array(
            'roles' => 'author,subscriber',
            'limit' => -1,
        ),
        $atts,
        'display_users'
    );

    ob_start();
    include plugin_dir_path( __FILE__ ) . 'templates/card.tpl.php';
    return ob_get_clean();
}
add_shortcode( 'display_users', 'blogger_cards_shortcode' );

This allows flexible usage:

  • [display_users] – Default: all authors and subscribers
  • [display_users roles="author" limit="6"] – First 6 authors only
  • [display_users roles="editor,contributor"] – Multiple specific roles

Gutenberg Block Registration

function blogger_cards_register_block() {
    if ( ! function_exists( 'register_block_type' ) ) {
        return;
    }

    register_block_type(
        __DIR__ . '/block.json',
        array(
            'render_callback' => 'blogger_cards_shortcode',
        )
    );
}
add_action( 'init', 'blogger_cards_register_block' );

We reuse the same render callback for both shortcode and block, maintaining consistency.

The Template File

The template file (templates/card.tpl.php) handles security and data processing:

Security First

defined( 'ABSPATH' ) || exit;

// Parse and sanitize inputs
$roles = isset( $atts['roles'] ) ? explode( ',', $atts['roles'] ) : array( 'author', 'subscriber' );
$roles = array_map( 'trim', $roles );
$limit = isset( $atts['limit'] ) ? intval( $atts['limit'] ) : -1;

Proper Data Escaping

<user-card
    name="<?php echo esc_attr( $display_name ); ?>"
    avatar="<?php echo esc_url( $avatar_url ); ?>"
>
    <div slot="email"><?php echo esc_html( $user->user_email ); ?></div>
    <div slot="url">
        <?php if ( ! empty( $user->user_url ) ) : ?>
            <a href="<?php echo esc_url( $user->user_url ); ?>"
               target="_blank"
               rel="noopener noreferrer">
                <?php echo esc_html( $user->user_url ); ?>
            </a>
        <?php else : ?>
            <?php esc_html_e( 'Not provided', 'blogger-cards' ); ?>
        <?php endif; ?>
    </div>
</user-card>

Security improvements:

  • esc_attr() for HTML attributes
  • esc_url() for URLs
  • esc_html() for text content
  • wp_kses_post() for formatted bio content
  • Proper rel="noopener noreferrer" for external links

The Web Component

Our card.js file defines the custom <user-card> element with modern JavaScript:

Modern Class Structure

class BloggerCard extends HTMLElement {
    constructor() {
        super();
        this.showInfo = false;
        this.attachShadow({ mode: 'open' });
        this.shadowRoot.appendChild(template.content.cloneNode(true));
    }

    connectedCallback() {
        this.render();
        this.attachEventListeners();
    }

    disconnectedCallback() {
        this.detachEventListeners();
    }

    static get observedAttributes() {
        return ['name', 'avatar'];
    }

    attributeChangedCallback(name, oldValue, newValue) {
        if (oldValue !== newValue) {
            this.render();
        }
    }
}

Modern JavaScript features:

  • Proper lifecycle methods
  • Attribute observation for reactive updates
  • Clean event listener management
  • textContent instead of innerHTML for security

Accessibility Features

toggleUserInfo() {
    this.showInfo = !this.showInfo;
    const userInfo = this.shadowRoot.querySelector('.user-card__info');
    const toggleButton = this.shadowRoot.querySelector('.user-card__info-toggle');

    if (this.showInfo) {
        userInfo.setAttribute('aria-hidden', 'false');
        toggleButton.setAttribute('aria-expanded', 'true');
        toggleButton.textContent = 'Hide Info';
    } else {
        userInfo.setAttribute('aria-hidden', 'true');
        toggleButton.setAttribute('aria-expanded', 'false');
        toggleButton.textContent = 'Show Info';
    }
}

Accessibility improvements:

  • ARIA labels and states
  • Semantic HTML with proper roles
  • Keyboard navigation support
  • Screen reader friendly
  • Alt text for images
  • Focus management

Modern CSS

The card.css file uses modern CSS features:

CSS Custom Properties

:root {
  --blogger-card-primary: #0073aa;
  --blogger-card-primary-dark: #005a87;
  --blogger-card-white: #ffffff;
  --blogger-card-bg: #f2f1f1;
  --blogger-card-text: #333333;
  --blogger-card-spacing: 1.25rem;
}

Users can override these in their theme for easy customization.

Responsive Grid

.grid {
  display: grid;
  gap: var(--blogger-card-spacing);
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 280px), 1fr));
  align-items: start;
}

@media (min-width: 768px) {
  .grid {
    grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
  }
}

Modern Features in card.js Styles

.user-card img {
  width: 100%;
  aspect-ratio: 1 / 1;
  object-fit: cover;
}

.user-card h3 {
  font-size: clamp(1.25rem, 4vw, 1.5rem);
}

@media (prefers-reduced-motion: reduce) {
  .user-card,
  .user-card button {
    transition: none;
  }
}

@media (prefers-color-scheme: dark) {
  .user-card {
    background: var(--blogger-card-bg, #2c2c2c);
    color: var(--blogger-card-text, #e0e0e0);
  }
}

Modern CSS features:

  • aspect-ratio for consistent image dimensions
  • clamp() for fluid typography
  • Container queries support
  • prefers-reduced-motion for accessibility
  • prefers-color-scheme for automatic dark mode
  • Smooth animations with proper fallbacks

Using the Plugin

As a Gutenberg Block

  1. Edit any page or post
  2. Click ‘+’ to add a new block
  3. Search for “Blogger Cards”
  4. Configure options in the sidebar

As a Shortcode

Add to any page or post:

[display_users]

[display_users roles="editor,author,contributor"]

Extending the Plugin

The modular architecture makes it easy to extend:

  1. Add new user fields – Edit the template file and add slots
  2. Custom styling – Override CSS custom properties in your theme
  3. Additional attributes – Add to block.json and handle in the shortcode
  4. Different layouts – Modify the grid CSS or create multiple templates

Conclusion

This modernized Blogger Cards plugin demonstrates how to build production-ready WordPress plugins in 2025. The combination of Web Components, modern JavaScript, accessible design, and Gutenberg integration creates a robust, maintainable solution.

The same architectural approach works excellently for larger projects with multiple features and components. The separation of concerns between client and server-side code makes the plugin easy to extend and maintain.

Download the complete code from GitHub.

Resources

Happy coding! 🚀

Leave a Reply

Your email address will not be published. Required fields are marked *