• Skip to main content
  • Skip to primary sidebar
Martin Taylor

Martin Taylor

PHP, CSS, MySQL and WordPress tips and techniques

CSS

Thoughts on Tailwind CSS

Posted on March 15, 2024

While following YouTube tutorials on frameworks such as Vue.js, I’d often find that the presenter was using Tailwind CSS. Of course, the tutorial focus wasn’t on styling, but it seemed to me that Tailwind was worth a deeper dive. Now I’ve had the chance to do that.

This isn’t a full review of Tailwind, let alone a tutorial – just some thoughts and opinions. Bear in mind that I’m primarily a PHP developer, although I’ve recently acquired a keen interest in using Alpine JS to spice up the web front end. So the majority of my pages are server-side rendered, with some styling changes applied dynamically by JS.

It took me a while to understand how to use Tailwind in a conventional web project. Although there is a CDN version, it’s intended only for experimentation, and they even label it “Play CDN”. The only other alternative appeared to be to use npm to install and control Tailwind, which (wrongly) led me to the conclusion that it was designed for use with a compiled framework such as Vue.js.

Eventually, though, I found and followed the instructions to install and use the Tailwind CLI without Node.js. I did this in the Linux container on my Chromebook, where I use VS Code, and to be fair, it was pretty straightforward. As a note, the VS Code extension for Tailwind is well worth installing, too.

Tailwind is a huge library of low-level CSS classes – almost all the classes contain just one declaration. So the Tailwind class ‘block’ is simply ‘display:block’, and Tailwind class ‘p-1’ is just ‘padding: 0.25rem’. Incidentally, this is why the CDN version weighs in at 111KB – all those classes add up.

Because the classes are so low-level, you have to combine them on each element. Here’s a typical example.

<h4 class="font-bold mt-12 pb-2 border-b border-gray-200">

I’ll be honest, I don’t like this much. It isn’t far removed from using inline styles, and poses a real risk to reuse. If I have five or six h4 elements on a page, with this approach, the chances are that I’ll get one of them slightly wrong; and if they all need changing to a different shade of grey, that’s more work than I want.

To counter this, Tailwind has an @apply directive, that can be used to map a conventional CSS selector to a list of Tailwind classes.

h4 {
  @apply font-bold mt-12 pb-2 border-b border-gray-200;
}

OK, this answers the reuse question, but it’s a bit of a hybrid that dilutes the Tailwind approach, and in fact the Tailwind docs argue that you should avoid it wherever possible. For me, I would just use standard CSS in this situation anyway.

So I’ve already seen enough to tell me that I won’t be using Tailwind, but what features did I like?

As you add classes to your HTML, Tailwind generates a CSS file that exactly matches. There are no redundant classes, no awkwardly named classes, and there’s no risk of conflicting rules. I recognise all three of these situations.

I also quite liked the Tailwind approach to responsive styling, using a simple modifier on a class to have it apply only at a given breakpoint. So using an example from the docs:

<!-- Width of 16 by default, 32 on medium screens, and 48 on large screens -->
<img class="w-16 md:w-32 lg:w-48" src="...">

I’ve ended up wondering if Tailwind is best for people who prefer not to get their hands dirty with CSS, and for whom the busy HTML class lists are not an issue.

And finally, I do have one use case where I might just get a benefit from Tailwind. If I’m mocking up a page and don’t want to go to the effort of defining my own CSS rules, Tailwind does have an advantage over inline styles. But that’s as far as I would go with it. Otherwise, it’s a sledgehammer / walnut situation as far as I’m concerned.

Filed under: CSS

CSS-only menus in WordPress

Posted on May 26, 2020

I recently set myself the challenge of building a CSS-only menu (including sub-menu dropdowns) for a WordPress site. Why? Partly as an academic exercise, but partly in an attempt to reduce what appeared to be a too-busy combination of CSS, JS and PHP in the site and theme as it stood.

All this of course in contravention of the well-established KISS principle, but hey … we’re in lockdown and it’s good to have something to occupy the little grey cells.

One thing I soon discovered is that if you need a dropdown menu, you’ll be relying on the so-called “CSS Checkbox Hack”. The term “hack” isn’t quite fair, because the HTML and CSS used are perfectly legitimate and have valid use cases of their own. It’s just that there’s a no-doubt unintended way to use this particular feature of CSS.

There are scores of explanations of the checkbox hack on the web, so I’ll be brief. The idea is that you set up your HTML like this:

<label for="menu-toggle" class="menu-toggle-label">Menu</label>
<input type="checkbox" id="menu-toggle" class="menu-toggle">
<ul class="menu">
    <li>Blah</li>
</ul>

The label will be visible. The checkbox has to be hidden because it’s only there to trigger the action we need (and that’s the “hack”). The <ul> will initially be invisible.

Now, in HTML, if you click the label for a checkbox, it will toggle the checkbox’s “checked” state. And we can use that as a selector in CSS to drive a change in another element – for example to change the visibility of that <ul>. Here’s the relevant part of the CSS:

.menu, .menu-toggle {
    display: none;
}
.menu-toggle:checked + ul {
    display: block;
}

That second rule says that when menu-toggle is checked, find an adjacent <ul> and make it visible. Simple enough. So using the checkbox’s label effectively as a button causes the <ul> to appear and disappear.

The next challenge was to find a way to arrange WP’s menu HTML with the necessary labels and checkboxes. I think the only way to do this is to generate the HTML yourself, and the usual suggestion is to create an extension to the Walker_Nav_Menu class, where you can set up your own start/end HTML for the items and levels in the menu.

But that seemed like a tad too much complexity, so I went for a custom approach based on WP’s wp_get_nav_menu_items function. This brings back a sorted list of the menu items, each with a value defining its parent item, so you can work out what the submenu hierarchy is. By the way, I’d not realised until using this that the menu items are saved as posts in WP’s database.

The first bit of code here is where we grab the menu items and create an array of objects that reflect the hierarchy.

$menu_items = wp_get_nav_menu_items('primary-menu');
// Construct menu hierarchy
$menu = [];
foreach ($menu_items as $index=>$menu_item) {
	$parent = $menu_item->menu_item_parent;
	$menu_obj = new stdClass();
	$menu_obj->url = $menu_item->url;
	$menu_obj->title = $menu_item->title;
	if ($parent) { // Add submenu to parent object
		$menu[$lvl_index]->submenus[] = $menu_obj;		
	} else { // Make new parent object
		$menu[$index] = $menu_obj;
		$menu[$index]->submenus = [];
		$lvl_index = $index;
	}
}

We end up with an array of objects for the top-level menu items, each with an array of its submenu objects. Armed with this, we can generate the HTML. In my case, it looked like this. It’s a pretty straightforward iteration through the array and sub-menu arrays, shoving those labels and checkboxes in where necessary.

echo '<nav class="nav-primary">';
echo '<label for="menu-toggle" class="menu-toggle-label top-menu-toggle-label">Menu</label>';
echo '<input type="checkbox" id="menu-toggle" class="menu-toggle">';
echo '<ul class="menu menu-primary">';
foreach ($menu as $index=>$menu_item) {
	$has_submenu = count($menu_item->submenus);
	$classes = 'menu-item '.'menu-item'.$index;
	if ($has_submenu) $classes .= ' menu-item-has-children';
	echo '<li class="',$classes,'">';
	echo '<a class="menu-link" href="',$menu_item->url,'">';
	echo '<span>',$menu_item->title,'</span>';
	echo '</a>';
	if ($has_submenu) {
		echo '<label for="menu-toggle-',$index,'" class="menu-toggle-label">+</label>';
		echo '<input type="checkbox" id="menu-toggle-',$index,'" class="menu-toggle">';
		echo '<ul class="sub-menu">';
		foreach ($menu_item->submenus as $submenu_item) {
			echo '<li class="menu-item submenu-item">';
			echo '<a class="menu-link submenu-link" href="',$submenu_item->url,'">';
			echo '<span>',$submenu_item->title,'</span>';
			echo '</a>';
			echo '</li>';
		}
		echo '</ul>';
	}
	echo '</li>';
}
echo '</ul>';
echo '</nav>';

And hey, it works. But I decided against using it.

A few reasons for that.

First – it uses an unintended consequence of a CSS feature, and it puts checkboxes into a page that no user will ever see. That’s not what checkboxes are for.

Second – I still hold to the principle that CSS is for styling and JS is for functionality. This approach blurs that line.

Third – a JS approach allows me to do more with the HTML, like adding classes to dropdowns when they are visible … I can’t see a way to do this with CSS alone.

So all-in-all, a good exercise with some useful lessons.

Filed under: CSS, PHP, WordPress

Using CSS Grid for layout

Posted on May 21, 2020

I have used a CSS Grid layout to display a Calendar, as described here. I was impressed by how easy it was to implement responsive behaviour with a grid, just by changing the ‘grid-template-columns’ value.

The other day, I decided to try CSS Grid as a way to lay out a WordPress page, and in this case one that uses a Genesis child theme. The layout of particular interest was in the body of the page, containing the content and sidebar.

The sidebar can be placed to the left or right of the content, according to theme customisation options in the admin UI. The stylesheets I’ve seen generally use “float: left” and/or “float: right” to achieve the layout.

Thing is, I’m not hugely fond of “float”, apart from quite limited contexts such as images in text blocks. I’ve often had problems with other page elements being affected by the float, which leads me to look for alternatives.

So the technique I’ve used is, first, to associate a grid-area with each of the two elements. Note that in Genesis at least, the elements are of the types <main> and <aside>.

main {
	grid-area: main;
}
aside {
	grid-area: aside;
}

Once these grid area names have been defined, they can be used to define the layout of the grid columns as follows:

.content-sidebar-wrap {
    display: grid;
}
.content-sidebar .content-sidebar-wrap {
    grid-template-columns: 66% 33%;
    grid-template-areas: "main aside";
}
.sidebar-content .content-sidebar-wrap {
    grid-template-columns: 33% 66%;
    grid-template-areas: "aside main";
}

Note that Genesis handily assigns a class to the <body> element, to define the layout that the admin has selected. It’s either ‘content-sidebar’ or ‘sidebar-content’ in this case, but this approach would be easy enough to adapt for other scenarios.

This page itself uses the technique – take a look at the HTML/CSS sources for more detail.

For small / mobile devices, it’s straightforward enough – just apply “display: block” or something like that to the wrapping container.

Now, I must be honest and say that while this works really well, I have a niggling reservation. Many many years ago, we used HTML tables to apply structure and layout to web pages. It’s certainly not deemed best practice these days … use tables where you have tabular data, and nowhere else. Could the same be said of CSS Grids?

Filed under: CSS, Genesis, WordPress

Primary Sidebar

Recent Posts

  • Populating Alpine data in a PHP page (part 2)
  • Thoughts on Tailwind CSS
  • Understanding JavaScript assignments
  • Populating Alpine data in a PHP page (part 1)
  • Alpine JS

Copyright Martin Taylor © 2025