Tag: CSS

  • Beyond Basic Recipes: Optimizing WordPress for Food Blogs

    I got a call recently from a client, a passionate chef with a fantastic array of healthy, inspiring recipes. She wanted to launch her food blog, Jen’s Food Blog, which she’d been running on a basic WordPress setup for a while, into something more professional, a platform for product reviews and an engaging community. But here was the kicker: her current site was a total nightmare. Slow. Clunky. Images loading like dial-up. She was doing everything right with content, but the tech stack was just, well, let’s just say it wasn’t pretty. This is a classic case of needing to properly tackle optimizing WordPress for food blogs from the ground up, not just patching it. This builds on some of the excellent points raised in the WordPress.com blog’s examples of successful food blogs at wordpress.com/blog/2025/10/28/food-blog-examples/.

    Her initial approach, like many I see, was to just pile on plugins. A separate plugin for recipe cards, another for image optimization, one for SEO, then a few more for social media integration and analytics. Each plugin, while useful in isolation, added its own overhead. The database queries were through the roof, the page load times were abysmal, and the admin dashboard was lagging harder than a vintage modem. It created a situation where every update was a gamble, and the site felt like it was held together with duct tape and good intentions.

    Building a Robust Food Blog Foundation

    The real fix? It wasn’t about more plugins; it was about smart architecture. For a food blog, you’re dealing with rich content: high-resolution images, potentially embedded videos (like those used on Vegan Bunny Elle), and structured recipe data. You need WordPress to handle this efficiently. This means thinking about custom post types for recipes from the start, rather than shoehorning everything into standard posts with a basic recipe plugin. A custom post type gives you a dedicated structure for ingredients, instructions, cooking times, and nutritional info, making it far easier to manage, query, and even export later. Plus, it improves SEO, letting search engines understand your content better.

    <?php
    function register_recipe_post_type() {
        $labels = array(
            'name'                  => _x( 'Recipes', 'Post Type General Name', 'text_domain' ),
            'singular_name'         => _x( 'Recipe', 'Post Type Singular Name', 'text_domain' ),
            'menu_name'             => __( 'Recipes', 'text_domain' ),
            'name_admin_bar'        => __( 'Recipe', 'text_domain' ),
            'archives'              => __( 'Recipe Archives', 'text_domain' ),
            'parent_item_colon'     => __( 'Parent Recipe:', 'text_domain' ),
            'all_items'             => __( 'All Recipes', 'text_domain' ),
            'add_new_item'          => __( 'Add New Recipe', 'text_domain' ),
            'add_new'               => __( 'Add New', 'text_domain' ),
            'new_item'              => __( 'New Recipe', 'text_domain' ),
            'edit_item'             => __( 'Edit Recipe', 'text_domain' ),
            'update_item'           => __( 'Update Recipe', 'text_domain' ),
            'view_item'             => __( 'View Recipe', 'text_domain' ),
            'search_items'          => __( 'Search Recipes', 'text_domain' ),
            'not_found'             => __( 'Not found', 'text_domain' ),
            'not_found_in_trash'    => __( 'Not found in Trash', 'text_domain' ),
            'featured_image'        => __( 'Recipe Image', 'text_domain' ),
            'set_featured_image'    => __( 'Set recipe image', 'text_domain' ),
            'remove_featured_image' => __( 'Remove recipe image', 'text_domain' ),
            'use_featured_image'    => __( 'Use as recipe image', 'text_domain' ),
            'insert_into_item'      => __( 'Insert into recipe', 'text_domain' ),
            'uploaded_to_this_item' => __( 'Uploaded to this recipe', 'text_domain' ),
            'items_list'            => __( 'Recipes list', 'text_domain' ),
            'items_list_navigation' => __( 'Recipes list navigation', 'text_domain' ),
            'filter_items_list'     => __( 'Filter recipes list', 'text_domain' ),
        );
        $args = array(
            'label'                 => __( 'Recipe', 'text_domain' ),
            'description'           => __( 'Food recipes', 'text_domain' ),
            'labels'                => $labels,
            'supports'              => array( 'title', 'editor', 'thumbnail', 'excerpt', 'comments' ),
            'taxonomies'            => array( 'category', 'post_tag' ),
            'hierarchical'          => false,
            'public'                => true,
            'show_ui'               => true,
            'show_in_menu'          => true,
            'menu_position'         => 5,
            'menu_icon'             => 'dashicons-food',
            'show_in_admin_bar'     => true,
            'show_in_nav_menus'     => true,
            'can_export'            => true,
            'has_archive'           => true,
            'exclude_from_search'   => false,
            'publicly_queryable'    => true,
            'capability_type'       => 'post',
            'show_in_rest'          => true, // Essential for Gutenberg editor
        );
        register_post_type( 'recipe', $args );
    }
    add_action( 'init', 'register_recipe_post_type', 0 );
    ?>

    Once you’ve got your custom post type in place, you’re looking at a much cleaner way to manage your content. Then you can implement meta boxes for custom fields, or even explore Gutenberg blocks designed for recipes, but you’re doing it on a solid foundation. You’ll still need proper image optimization, maybe a CDN, and smart caching to serve up those gorgeous food photos fast, but you won’t be fighting your own backend to do it. Think about the user experience: a fast, well-organized site keeps people around, whether they’re looking for vegan basics from Vegan Bunny Elle or a vintage recipe from A Hundred Years Ago.

    So, What’s the Point?

    The lesson here is simple: a great food blog isn’t just about delicious recipes or stunning photography. It’s about a solid, performant WordPress setup that supports that content without fighting it. Don’t fall into the trap of just adding more plugins to solve every perceived need. Plan your content structure, especially for something as specific as recipes, and choose your tools wisely. This ensures your site loads quickly, is easy to manage, and provides an excellent experience for your readers.

    Look, this stuff gets complicated fast. If you’re tired of debugging someone else’s mess and just want your site to work, drop my team a line. We’ve probably seen it before.

  • Stop the WordPress Plugin Chaos: Namespaces and Coding Standards Done Right

    I got a call last month from a client whose flagship WordPress plugin, a custom solution for managing complex product variations, was starting to buckle. New features were taking forever to build, new developers couldn’t onboard without a week of “figuring stuff out,” and every update felt like a game of whack-a-mole. Total nightmare. The core issue? A codebase that had simply outgrown its initial, less-structured approach. When you’re dealing with serious WordPress plugin development standards, this kind of organic sprawl is a killer.

    It’s a familiar story, right? You start small, keep adding files, maybe a class or two, and before you know it, you’ve got a tangled mess. Dependencies are implicit, function names clash, and a simple change breaks something three files away that you didn’t even know was related. I’ve seen it a hundred times, and honestly, I’ve been there myself when I was starting out. You think, “I’ll just refactor later,” but later never comes, or it costs an arm and a leg.

    The obvious, but wrong, first step when things get messy is just to keep patching. Add another file, tack on another function globally. But that’s just duct-taping over a cracked foundation. The real fix for sustainable WordPress plugin development standards involves bringing in proper structure: PHP namespaces, Composer for autoloading, and consistent coding standards across the board. This isn’t just about making things look pretty; it’s about making your code scalable, maintainable, and, frankly, less of a headache for everyone involved.

    Implementing Proper WordPress Plugin Development Standards

    Let’s talk about how we actually tackle this. First up, namespaces. Instead of every function and class existing in a global soup, we compartmentalize. For that client plugin, we introduced a main namespace like MyAgency\ProductVariations\. This means no more guessing if init() is your init() or some other plugin’s. Coupled with PSR-4 autoloading via Composer, you ditch all those manual require_once statements. Composer loads your classes based on their namespace and file path. It’s clean, efficient, and auto-magical, almost. After setting up your composer.json, a quick composer install, and you’re golden. Here’s a basic `composer.json` example:

    {
      "name": "my-agency/product-variations",
      "description": "Custom product variation management.",
      "type": "wordpress-plugin",
      "license": "GPL-2.0-or-later",
      "autoload": {
          "psr-4": {
              "MyAgency\\ProductVariations\\": "src/"
          }
      }
    }

    See how the "MyAgency\\ProductVariations\\" namespace maps directly to the "src/" directory? That’s the PSR-4 magic right there. Your main plugin file then just needs to include Composer’s autoloader, and you’re off. For the full lowdown on setting this up, including specific class examples for paths, block registration, and enqueues, check out the detailed guide on developer.wordpress.org.

    Beyond structure, we need consistency. And that means coding standards. No two ways about it. JavaScript, CSS, PHP—they all need to play by the same rules. We’re talking ESLint and Prettier for JS, Stylelint for CSS/SCSS, and PHP_CodeSniffer with WordPress Coding Standards for PHP. Automate it. Make it part of your build process. If it’s not formatted correctly, the build fails. Period. It sounds strict, but it saves countless hours in code reviews and debugging ambiguous syntax issues. Trust me on this, it makes a huge difference, especially when you have multiple devs touching the same files.

    So, What’s the Point?

    • Namespaces keep your PHP code organized and prevent nasty conflicts.
    • Composer autoloading handles file inclusion so you don’t have to, making your codebase cleaner and faster.
    • Automated linting and formatting enforce consistent coding standards, saving your team from arguments and future bugs.

    This isn’t just theory; it’s how you build robust, scalable WordPress plugins that don’t become technical debt disasters a year down the line. It’s about building with confidence, knowing that your project can grow without collapsing under its own weight.

    Look, this stuff gets complicated fast. If you’re tired of debugging someone else’s mess and just want your site to work, drop my team a line. We’ve probably seen it before.

  • WordPress Block Development: September’s Core Updates Deliver

    Just last week, a client hit me up. Their marketing team was wrestling with an existing page, trying to add some simple accordions and custom-styled form fields without breaking anything. The current setup? A total nightmare of custom JS and inline styles from an older theme. I looked at it and thought, “Man, this used to be a headache to implement properly, especially if you needed it accessible.” But here’s the kicker: WordPress core, especially with the September 2025 updates, is making this kind of stuff genuinely straightforward now.

    If you’ve been knee-deep in custom block development, or just trying to keep up with how WordPress is evolving, these updates are solid. They’re not flashy marketing buzz; they’re practical tools that make our jobs easier and client sites more robust. We’re talking about significant strides in core functionality that streamline development, reduce custom code, and ultimately, make sites more maintainable.

    Streamlining WordPress Block Development

    First up, the official “What’s new for developers?” September 2025 report on the WordPress Developer Blog highlights the new **Accordion block**. This has been a long time coming. We’ve all built countless custom accordion solutions, often fighting with accessibility standards (WCAG, USWDS) or patching things together with conflicting JavaScript. Now, WordPress gives us native Accordion, Accordion Item, Header, and Panel blocks, all powered by the Interactivity API. This means less custom code, better semantics out of the box, and a more consistent user experience. Trust me, rolling your own accessible accordion from scratch is not fun, and having this in core is a huge win for cleaner block development.

    Next, let’s talk about styling. Remember the days of fighting with global CSS overrides for basic form elements? It was messy. With the continued effort to allow `theme.json` to style form elements, you can now target text inputs, textareas, and even select/dropdown elements directly. This is massive for maintaining a consistent design system. You can centralize your form styling right in your theme’s `theme.json`, making it predictable and easy to manage without adding a bunch of extra CSS that conflicts everywhere. For example, to style your text inputs:

    "elements": {
    	"textInput": {
    		"border": {
    			"radius": "0",
    			"style": "solid",
    			"width": "1px",
    			"color": "red"
    		},
    		"color": {
    			"text": "var(--wp--preset--color--theme-2)"
    		},
    		"typography": {
    			"fontFamily": "var(--wp--preset--font-family--inter)"
    		}
    	}
    }

    This kind of control, right from `theme.json`, removes a ton of boilerplate and ensures your styles are applied universally. It’s what we always wanted for true block themes, and it’s finally here.

    Beyond styling, the `@wordpress/create-block` package now allows block variants to define their own template files. If you’ve ever built complex blocks with many variations, you know the pain of conditional logic within a single template. This update simplifies managing those templates significantly. It’s a small change, but it makes a huge difference in the long-term maintainability of your custom blocks.

    And speaking of foundational work, the **Abilities API** is getting a Composer package and the PHP AI Client saw its first stable release. For those integrating advanced permissions or dabbling with AI in WordPress, this is the groundwork being laid. It means a more structured and official way to extend core functionality, rather than relying on custom solutions that might break with every major update.

    So, What’s the Point?

    The takeaway here is clear: WordPress core development is moving fast, and in the right direction. These aren’t just minor tweaks; they are foundational improvements that empower developers to build better, more maintainable, and more compliant sites with less effort. Leveraging these core features means fewer custom hacks, less technical debt, and more time focusing on what really matters for your clients.

    Look, this stuff gets complicated fast. If you’re tired of debugging someone else’s mess and just want your site to work, drop my team a line. We’ve probably seen it before.

  • Extending WordPress Navigation Blocks with Custom Content

    I recently had a client come to me, pulling their hair out over a seemingly simple request: they wanted to drop a custom “mini-cart” block directly into their main navigation menu. Sounds straightforward, right? Well, not so fast. WordPress’s Navigation block, by default, is pretty particular about what it lets you put in there. It’s a total pain when you’re trying to achieve something beyond the standard link, and frankly, it kills a lot of creative menu ideas. They’d tried to force it with some dodgy CSS, and it was, as you can imagine, a complete mess in the editor, sometimes breaking the whole layout on certain screen sizes. That’s when we needed to talk about Extending Navigation Blocks the right way.

    The core of the problem is that the Navigation block is built to be a navigation tool, not a free-form content area. This is generally good for stability, but when you need to integrate custom functionalities – like a dynamic cart summary, a login form, or even a simple image – you hit a brick wall. My first thought, before really digging in, was to just tell them to live with it, or maybe try to inject the block content via some elaborate JavaScript after the page loads. But that’s a fragile, client-unfriendly solution that makes future maintenance a nightmare. It’s an editor problem, and it needed an editor-level fix.

    Extending Navigation Blocks: The Proper Hook

    The real fix lies in understanding how Gutenberg registers and manages blocks. Specifically, we need to tap into the blocks.registerBlockType filter to extend the allowedBlocks property of the core/navigation block. This isn’t just a hack; it’s a built-in WordPress mechanism for extending block functionality. The key here is to enqueue your custom JavaScript file using the enqueue_block_editor_assets hook, ensuring your script loads where it needs to: the block editor. This approach lets you tell the editor, “Hey, this navigation block? It can also handle this other block type now.” It’s clean, it’s maintainable, and it actually works within the editor.

    add_action( 'enqueue_block_editor_assets', function() {
        wp_enqueue_script(
            'my-agency-extend-navigation-script',
            plugins_url( 'js/editor-script.js', __FILE__ ),
            array( 'wp-hooks', 'wp-blocks' ), // Dependencies for block filters.
            '1.0.0',
            true
        );
    } );

    Once that PHP snippet is in place, you need the JavaScript. Create an editor-script.js file inside a js folder within your plugin (or theme, if you know what you’re doing, but plugins are usually better for this kind of functionality). This script will then hook into the blocks.registerBlockType filter. You can see a great example of this over at developer.wordpress.org, which provided the foundation for this method.

    const addToNavigation = ( blockSettings, blockName ) => {
    	if ( blockName === 'core/navigation' ) {
    		return {
    			...blockSettings,
    			allowedBlocks: [
    				...( blockSettings.allowedBlocks ?? [] ),
    				'core/image', // Replace with your custom block or desired core block, e.g., 'my-plugin/mini-cart'
    			],
    		};
    	}
    	return blockSettings;
    };
    
    wp.hooks.addFilter(
    	'blocks.registerBlockType',
    	'my-agency-add-block-to-navigation',
    	addToNavigation
    );

    See that 'core/image' part? That’s where you’d drop the name of whatever block you need to add. For my client, it would have been their custom mini-cart block. This snippet tells the Navigation block, “Hey, you can also accept images now.” Or, in a real-world scenario, “You can accept my custom mega menu block,” or “my user profile widget.” It opens up the Navigation block to any block you define, without breaking anything. This is the solid, robust way to manage custom requirements within the block editor, especially when dealing with core blocks that have strict allowedBlocks definitions. Trust me on this, trying to hack around these limitations will bite you later.

    So, What’s the Point?

    The lesson here is simple: don’t fight the editor. When WordPress gives you hooks and filters, use them. Trying to force square pegs into round holes with CSS or sketchy JavaScript injections for editor functionality is a total nightmare for anyone who has to maintain the site later. This method for extending navigation blocks might seem like a few extra lines of code, but it provides a stable, forward-compatible solution that makes both your life and your client’s life much easier. It ensures custom elements can live gracefully within the navigation, exactly as intended.

    Look, this stuff gets complicated fast. If you’re tired of debugging someone else’s mess and just want your site to work, drop my team a line. We’ve probably seen it before.

  • Taming WordPress 6.9 Accordions: The Right Way to Style Them

    Just last week, I had a client reach out. They were revamping their FAQ page, and naturally, they wanted a sleek, branded accordion look. Easy enough, right? Except when we dropped in the new Accordion block in WordPress 6.9, it was… well, let’s just say it was bare bones. A functional accordion, absolutely, but miles away from the custom styling they envisioned. Total mess. This is a problem many of you are probably facing or will face soon. The new Accordion block is a fantastic addition, but out of the box, it’s a blank canvas. If you want it to truly blend with your theme and provide a polished user experience, you need to know how to approach styling WordPress Accordion blocks systematically. My first thought, and I’ll admit it, was to just start throwing custom CSS at it. A `border-radius` here, a `background-color` there, maybe an `!important` to override something stubborn. It worked, for a minute. But then the client wanted a different style for another section, and suddenly, I had a spaghetti of CSS that was a total nightmare to manage. This isn’t scalable, and it certainly isn’t the “right” way to leverage modern WordPress development. Trust me on this, you’ll regret it later. The real fix, the maintainable solution, lies in embracing `theme.json` first, and then using targeted custom CSS for the fine-tuning. WordPress 6.9’s Accordion block isn’t just one block; it’s a nested structure: `core/accordion`, `core/accordion-item`, `core/accordion-heading`, and `core/accordion-panel`. Each offers a hook for proper styling.

    Styling WordPress Accordion Blocks with theme.json

    You want to start by defining your base styles in your `theme.json` file. This is where you set the global or block-specific styles that become your foundation. Let’s look at a simplified example for the `core/accordion-item`, which acts as the wrapper for each question and answer pair.
    {
    	"styles": {
    		"blocks": {
    			"core/accordion-item": {
    				"border": {
    					"color": "#d5dae2",
    					"style": "solid",
    					"width": "1px",
    					"radius": "12px"
    				},
    				"color": {
    					"background": "#f6f7f9",
    					"text": "#343b46"
    				},
    				"shadow": "0 10px 15px -3px #80000080, 0 4px 6px -4px #80000080",
    				"spacing": {
    					"blockGap": "0"
    				}
    			}
    		}
    	}
    }
    See how clean that is? We’re defining borders, background, text color, and even a shadow right within the JSON. You should, of course, be referencing presets here in a real-world scenario, but this snippet illustrates the power. Next, we address the `core/accordion-heading` and `core/accordion-panel`. The heading is actually an `<h3>` element, so any existing heading styles will apply. You might need to reset or refine them.
    {
    	"styles": {
    		"blocks": {
    			"core/accordion-heading": {
    				"css": "&__toggle { padding: var(--wp--preset--spacing--40) var(--wp--style--block-gap); }",
    				"typography": {
    					"fontFamily": "inherit",
    					"fontSize": "inherit",
    					"fontStyle": "inherit",
    					"fontWeight": "500",
    					"lineHeight": "inherit",
    					"textTransform": "inherit"
    				}
    			},
    			"core/accordion-panel": {
    				"spacing": {
    					"padding": {
    						"top": "var(--wp--style--block-gap)",
    						"bottom": "var(--wp--style--block-gap)",
    						"left": "var(--wp--style--block-gap)",
    						"right": "var(--wp--style--block-gap)"
    					}
    				}
    			}
    		}
    	}
    }
    The `css` property within `theme.json` is a lifesaver for those tricky nested elements you can’t target directly, like the `__toggle` button within the heading. This keeps your styles localized. For a deeper dive into these techniques and more comprehensive examples, the original article on developer.wordpress.org is a fantastic resource.

    Fine-Tuning with Custom Block Stylesheets

    While `theme.json` gets you most of the way, some dynamic styling—like hover states or specific open/closed appearances—are best handled with a dedicated block stylesheet. We’re talking about creating `block-accordion.css` in your theme’s assets and enqueueing it properly with `wp_enqueue_block_style()`. This gives you precise control without bloating your `theme.json`.
    /* Add toggle button background on open/hover/focus. */
    .wp-block-accordion-item.is-open .wp-block-accordion-heading__toggle,
    .wp-block-accordion-heading__toggle:hover,
    .wp-block-accordion-heading__toggle:focus {
    	background: #eceff2; /* Use a preset here. */
    }
    This snippet, placed in your custom `block-accordion.css` file, applies a subtle background change when the accordion is open or hovered. It’s a clean way to handle these dynamic visual cues. You can also define block style variations, like a “Minimal” style, through separate JSON files and additional CSS, offering your users more design choices directly in the editor. This modular approach is key for extensibility and maintainability.

    So, What’s the Point?

    The WordPress Accordion block is a powerful tool, but like any new feature, knowing how to properly integrate and style it makes all the difference. Don’t fall into the trap of quick-fix CSS. Embrace `theme.json` for your foundational styles and leverage block stylesheets for the more nuanced, interactive elements. This approach ensures your accordion styling is robust, maintainable, and scalable, whether you’re building a simple FAQ or a complex interactive content section. It’s about working *with* WordPress, not against it. Look, this stuff gets complicated fast. If you’re tired of debugging someone else’s mess and just want your site to work, drop my team a line. We’ve probably seen it before.