Structured

January 25, 2025

Updating my library for a better future

Visit the library

A screenshot showing HTML code with inline microdata.

Now, I’ve been around the web a while and I’ve played the SEO game with many different search engines. Consequently, along the way, I became a big fan of structured data. It just clicked in my head as an easy way to provide additional context to our content. A way to enable a richer experience without mucking up the presentation.

Of course, with the big boy on the block, that meant using the JSON-LD as that’s what they “recommend.” And to be fair, it is easy to add to a page and then to update and maintain — especially on large websites.

Of course, on my own little website, there’s not a whole bunch of data to…well…structure, so the JSON-LD is more of a general description of ownership. Still, I’ve been adding it to all my pages for a long time.

<script type="application/ld+json"> 
{
	"@context": "http://schema.org"
	"@type": "WebSite",
	"name": "Strength of One // Library",
	"url": "http://strengthofone.com/library/",
	"image": "http://strengthofone.com/soo-icon.png",
	"description": "Find out more about my virtual library.",
}
</script>

All fine and good. I was recently plugging along making updates to the library (to add and update links) when I came across an article on microdata for books. Andy Dalton‘s article for the HTMLHell advent calendar switched on the light bulb and I realized I had the perfect page for playing around with structured data in a more expansive way by incorporating microdata directly in the HTML.

When I first started learning about structured data (long ago), I remembered thinking microdata actually made more sense than JSON-LD because it was more closely aligned with the actual content. The main reason I never started incorporating it into my pages (beyond the Google influence) was that my content didn’t seem appropriate. I wasn’t writing up recipes, movie reviews, promoting events or anything that really fit the value pairs approach. With my library though, adding the microdata directly inline made perfect sense. Each library card is already presenting information that could be connected to a Schema.org value.

I started slowly with a few entries and validating each as I completed it, quickly getting the hang of it and gaining confidence. I think the only edit to the HTML structure I had to make was to wrap the author in a <span> element so that it connected with the itemscope in the parent <p> tag. From there, it became a production chore to copy/paste/find/replace for each book as I watched some football and relaxed. Here’s a sample book entry with the microdata in bold. (And yes, the fact that the code is not indented drives my designer brain crazy, but in this narrow blog column, it’s easier to read when the lines don’t wrap so much. I’ll probably write another post about the woes and wails of code formatting for designers.)

<article class="book-card fiction paperback" itemscope itemtype="https://schema.org/Book">
<div class="book">
<h2 class="title"><cite itemprop="name">The Lord of The Rings</cite></h2>
<h3 class="subtitle">Special Edition</h3>
<p class="author" itemprop="author" itemscope itemtype="https://schema.org/Person"><span itemprop="name">J.R.R. Tolkien</span></p>
</div>
<div class="book-details">
<ul>
<li class="category">F</li>
<li class="category" itemprop="bookFormat" itemscope itemtype="https://schema.org/Hardcover">HC</li>
<li class="link">
<a itemprop="url" href="https://openlibrary.org/books/OL38062258M/Lord_of_the_Rings" title="View at the Open Library">
<svg viewBox="0 0 24 24" aria-hidden="true"><title>View at the Open Library</title><path fill="currentColor" d="M3.9,12C3.9,10.29 5.29,8.9 7,8.9H11V7H7A5,5 0 0,0 2,12A5,5 0 0,0 7,17H11V15.1H7C5.29,15.1 3.9,13.71 3.9,12M8,13H16V11H8V13M17,7H13V8.9H17C18.71,8.9 20.1,10.29 20.1,12C20.1,13.71 18.71,15.1 17,15.1H13V17H17A5,5 0 0,0 22,12A5,5 0 0,0 17,7Z"></path></svg>
<span class="visually-hidden">View at the Open Library</span>
</a>
</li>
</ul>
</div>
</article>

I also took the opportunity to clarify the difference between “authors” and “editors” for several books. I removed the distinction from the content and put it into the microdata instead. This does seem wrong in a certain sense — providing the value for machines and obscuring it from humans, but it does make the design cleaner and more consistent. It’s probably something I’ll have to tackle in a future update though. The same is true with some of the other UI details such as the category labels in each book card. Looking at them with fresh eyes, I should probably include some sort of legend or maybe a tooltip. Along the same lines, I can now probably refactor the CSS a bit to style the content using the microdata attribute selectors instead of my custom classes (eventually).

Using structured data in this way also tracks with the “less Javascript is more” idea I’ve been working towards. Adding a <script> tag to the HTML for JSON-LD still feels like regular ol’ Javascript and as such, overkill (even if it isn’t).

Working through all the books, I then circled back for the comics which had a similar pattern but with a few differences unique to them. Namely involving how to indicate each issue number in the series. One or two more production sessions and I was able to update all those entries as well. Again, like with the author’s name, the only change to the HTML was adding an extra <span> for the issue number. Here’s an excerpt from one of the comic book cards.

<article class="book-card fiction comic" itemscope itemtype="https://schema.org/ComicSeries">
<div class="book">
<h2 class="title"><cite itemprop="name">The Uncanny X-Men</cite></h2>
<h3 class="subtitle" itemprop="hasPart" itemscope itemtype="https://schema.org/ComicIssue"><span itemprop="issueNumber">#170</span></h3>
<p class="author" itemprop="author" itemscope itemtype="https://schema.org/Person"><span itemprop="name">Chris Claremont</span></p>
</div>

<!-- more stuff -->

</article>

One of the weird things I noticed when reading up about using microdata versus JSON-LD was the criticism based solely on the the microdata making the HTML “messy.” And by weird, I mean ridiculous. Given the popularity of utility based CSS frameworks (ahem, Tailwind) filling HTML elements with long strings of classes, it’s a bit silly to criticize microdata for “messy” HTML. And by silly, I mean stupid.

Opinions aside, I’m really excited to get this all in place and to keep using it as I add more books. Even though search engines seem to be dying of self-inflicted AI wounds, I’m still a firm believer that structured data will always be beneficial. Somehow, adding this microdata makes the me feel like the the page is more robust and future-proof. Heck, if it helps one person find a book, it’s all worth it.

Visit the library

Political Discourse

January 18, 2025

A tale in three acts

Act One

A round black sticker with the word, "Trump" in red slab serif type with a dog pooping on top of it.

Act Two

Two round stickers. The top one is black with the word, "Trump" in red slab serif type with a dog crapping on top of it. The bottom is an illustration of President Donald Trump as a clown with a frown with the word, "Nope" in white sans serif type.

Act Three

A round sticker with an illustration of President Donald Trump as a clown with a frown with the word, "Nope" in white sans serif type.

A series of stickers from a hike I took over the holidays — all posted on the back of trail signs presented in the order I found them. I do love when multiple people all comment on each other’s graffiti. Always have. There’s a subtext beyond any media, outside of any other conversations. The street speaks for itself. It speaks without expectation. Often times, they speak without ever seeing the response or the reaction. Often times, they do not realize they are on the same side, speaking the same language, expressing the same fears. Three different signs, three different stickers, from two different people.

Westchester, New York.

Local Archeology: 2024 Recap

January 11, 2025

A review of the progress and discoveries.

Read last year’s inaugural post or view the updated photo gallery.

A torn piece of paper with a musical score printed on it.

Coming out of last year’s post on the project and my efforts, I was feeling a bit defeated. There’s just so much to clean up that the task seems insurmountable and the effort insignificant. But I still love going out in the woods and I’m not going to ignore a problem I could help to solve. Overall progress was a bit lighter in 2024 for two main reasons.

  1. Recovery: After 2023’s massive effort, we needed to let the forest heal from the scars of removing all that large scale commercial debris. Overall, less disturbance to the forest meant less trash removed. This was particularly true over the summer.
  2. Terraforming: It seemed like the maintenance and upkeep of our own little forest wildflower yard garden took a lot more time than in 2023. I imagine this is somewhat cyclical and 2024 was just one of those years where the long term tasks all needed attention. It also seemed like a really hot summer which led to exhaustion earlier in the day and less hikes into the forest.

The Trash

With the reduced emphasis on tackling the commercial dumping, I was really just picking up small loose items to fill my pair of trusty five gallon buckets.

MonthBags (33 gallon)Buckets (5 gallon)
January4
February32
March111
April8
May6
June16
July1
August2
September3
October1
November
December413
Total1353

Using the same conservative estimates from 2023, 50 pounds per bag comes out to 650 pounds for the bags. For the buckets, 5 gallons at 8.33 pounds per gallon comes out to 41.65 pounds. The more conservative estimate per bucket is probably around 20 pounds. This leads to a total of 1,060 pounds of trash put in buckets. All combined for a total estimate of 1,710 pounds of trash removed in 2024. Nearly one ton (0.855) of trash removed from the forest. Not bad at all.

Given that the effort was not on the large scale commercial dumping grounds, but rather on the small bits of trash and debris, there weren’t many “wow” items pulled out. This also led to fewer photos in the gallery, because…once you’ve seen a photo of a beer can, you don’t need to see them all. One fun item was the cast iron water main cap that I’m now using as a cigar ashtray.

  • Car seat (2): Not a child safety seat, but actual seats from cars with metal frame, fabric, foam, etc.
  • Bottles
  • Cans
  • Plastic
  • Styrofoam
  • Broken glass
  • Paper
  • Scrap metal
  • Construction debris
  • Golf balls
  • Toys
  • Balloons
  • Tools

The Design

No significant changes to the design of the gallery this year. I’m still happy with the look and feel, so it was just a matter of updating the typography for the year subheads.

The Development

The code for the gallery did get an overhaul this year, most notably from my recent foray into using srcset on the img tags. Taking what I learned from building my new photo page, I started creating the different necessary image sizes, but decided not to go as crazy. The grid items in this design are much more consistent across larger viewports, so I didn’t need to create as many image options for the browser. I settled with three sizes: 220px, 460px and 1440px for the modal.

<img srcset="img/02-10-2024-220w.webp 220w, 
    img/02-10-2024-460w.webp 460w" 
    sizes="(max-width: 479px) 460px, 220px" 
    loading="lazy" src="img/02-10-2024-220w.webp" 
    data-img="img/02-10-2024.webp" 
    alt="A silver dented car wheel hubcap." class="thumb" loading="lazy">

I also found another responsive image testing tool, but it seems…too opaque? There’s really no indication what it’s testing against or what’s going on. I remain skeptical.

This change also meant a rewrite of the Javascript for the modal popup. Previously, the modal grabbed the image source path and used it. With multiple image sources — and wanting to have a larger image size just for the modal — I switched to pulled the correct image path from a new data attribute. Data attributes are great and I really find them to be helpful in connecting the dots between design and function.

This works great, but does introduce a bit of a delay as the image loads — particularly on subsequent images. The first time you click on an image, it loads fine, but close it and click on a second image and you’ll see the first image appear briefly before the second image loads. I’m not sure if it’s due to the lazy loading I added to all the images or just a caching issue or something more granular with the JS where data attributes take longer to load than a direct image path. In any case, it’s something I can live with (for now). Perhaps I can modify it with a CSS transition to make the image swap seamless instead of so jarring.


As an aside:

This is a common scenario for web designer/developer/engineers in the corporate world. “I can’t change the functional code, so I’ll try to improve the interface with a bit of slight of hand to make the experience less irritating.”


There were also some other small performance upgrades as well.

  • Switched to serving the main typeface locally rather than via a third party.
  • Changed all the image formats from JPG to WebP. (I kind of wish we could start using the JPEG XL format, but there’s not enough browser support.)

All of these updates also meant going back and updating all of last year’s content. All new images sizes and code edits for all of the 2024 images. Not difficult, just work. Good to do while watching sports or binging some other trash TV. Just a little visual/auditory ambient distraction as you tackle each little production task.

Looking Ahead

The new year has started with a burst of renewed energy and it looks like I’ll be able to clear out a ton (literally) more trash this year including more of the large scale commercial debris items. Although, it’s harder to find ground items in the winter, the lack of vegetation allows for more visibility into areas that are completely overgrown in summer. Plus, there’s no (or very little) home terraforming chores to handle. I’ve already scoped out two new areas in need of help and added them to my ongoing list. I’m mostly waiting for the ground to thaw out before starting. Here’s to making your little piece of the world a little cleaner, a little brighter and a little healthier for the future.

Please don’t litter.

View the updated photo gallery

Instant

December 26, 2024

Another step in recapturing content: Bringing photos back in-house.

With the ongoing enshittification of the platforms we once trusted, I took another step towards untangling myself from corporate social media. I finally committed to stop posting on Instagram and return to posting my photos on my own site.

View the photo page.

Background

After years of immersing these platforms into out digital lives, it’s unsurprisingly tedious to extract yourself from their tangled grasp. I’ve been dutifully moving certain projects off of Instagram and over to their own pages. The one big theme that is still hanging around is…the more general “photo” theme.

Design

Nothing too fancy in terms of the page design: fit into the site’s theme, basic full width grid for the photos and easy to update. Luckily, I’ve been enamored with the square aspect ratio for a while, so the layout was simple. Letting the photos stand on their own was also the point, so there’s no titles or copy to account for in the design.

Development

Since the design is so simple, I did want to add a bit of complication to the development side. I’ve been playing around with a few image gallery options, but didn’t really want to add the overhead of Javascript. Instead, I decided to use the page as an opportunity to once again tackle the image srcset property. Now, I’ve used srcset in production before with the picture element, but it’s always been confusing. In this case, I wasn’t interested in art directing the photos across viewport sizes or layouts, but merely trying to provide alternative sizes for better performance. This made adding the srcset property to the img element the best option.

As I mentioned, the spec for the property never seems to stick in my head. The production part is easy enough: create different sizes of each photo based on what size is needed in the layout for different viewports. For the code itself: add a list of source paths to the images along with media queries in the sizes attribute to give the browser a hint as to which image to use. Plus, provide a default fallback image path. It looks fairly straight forward, but there’s a bunch of interacting parts that always confuse me. Here’s my initial attempt with two image sizes for 480px wide and 720px wide. (Don’t forget the alt text either!)

<img srcset="blue-420w.webp 420w, blue-720w.webp 720w" 
    sizes="(max-width: 768px) calc(100vw - 4rem), calc(100vw - 6rem - 224px)"
    loading="lazy" src="blue-420w.webp" 
    alt="A dark blue sky in the top left with the silhouette of bare trees coming in from the bottom right.">

But…once you try to test it out, things start getting more complicated. Looking at the browser dev tools as you resize the window appears to load the correct image, but it also seems to show the incorrect size when you hover over the img element. The dev tools network tab does a better job, but still doesn’t adjust on resize or provide any insight into how the browser is processing the srcset setup.

It’s worth mentioning that the other reason I started with two sizes is…laziness. The performance value of srcset (and the picture element) run up against the additional production work of creating all the different image sizes. After 14 years on Instagram, the sheer volume of photos to process is daunting. Even discounting some of the other themes and culling many photos altogether, it was a bit to much to think about at the start. Starting small, cherry picking just a few images and just tackling two sizes kept the project moving.

Right. Basic layout and images formatted and set up in the HTML. Now on to that testing conundrum. It quickly became clear that the combo of my two images sizes and the media queries weren’t really helping anything. Turns out the layout didn’t really help simplify things either. The three column layout necessitated smaller images at larger viewport widths and the one column layout (below 768px wide) required large images initially — and then smaller images as the viewport shrank. So…two layouts needing three (or more) media queries to load two (or more) image sizes. This is where my head starts to hurt and I start to think, “there should be a tool for this”.

A bit of research and luckily, I was able to find this linter for responsive images to help my debug and build better media queries. Just run the bookmark (or Chrome extension) on your page and it will run through the images and conditions to make recommendations. In looking at my page, it recommended two different sets of potential image sizes and a new set of media queries. As I suspected, it meant creating more than just two image sizes, so I relented and braced myself for more production work. I went with the more lengthy size list recommended by the linter which meant abandoning my previous two sizes. Here’s the new code:

<img srcset="blue-256w.webp 256w, 
    blue-609w.webp 609w, 
    blue-888w.webp 888w, 
    blue-1240w.webp 1240w, 
    blue-1510w.webp 1510w, 
    blue-1740w.webp 1740w, 
    blue-1940w.webp 1940w" 
    sizes="(min-width: 780px) calc(33.33vw - 32px), 
	(min-width: 380px) calc(100vw - 64px), 
	calc(60vw + 80px)" 
    loading="lazy" src="img/photos/blue-609w.webp" 
    alt="A dark blue sky in the top left with the silhouette of bare trees coming in from the bottom right.">

So now there are seven image sizes to pair with the three media queries. A bunch more production work indeed. This immediately took me back to using the picture element in production, i.e., creating tons of different sizes for each image. I ran the linter again and it was happy, but I’m still not satisfied with the testing scenario. I think there’s an opportunity for browsers to really clarify which condition is being met and why it’s choosing a particular image size. Maybe I just need to start using a browser just for development.

The linter did seem to get confused on a couple of occasions. The first issue occurred as I added more and more images. Using the bookmarklet in Firefox caused the linter to hang and eventually crash. I suspect it’s just trying to handle too many options and runs out of memory. Switching over to Chrome and using the extension worked fine and didn’t crash. The other issue was a bit more esoteric. For a few of the images, the linter gets confused and thinks I’m using the srcset image list for art direction (using images with different content) when I’m not.

Images in srcset attribute must not be different

It seems the image name-609w.webp doesn’t show the same contents as name-1940w.webp does, the determined difference is 5%.

This is an issue I wasn’t able to resolve. Recreating the image sizes didn’t help. I can only suspect that the linter is choking on the pixels values between the small and large sizes. Perhaps it has something to do with the compression.

From there, it was just a ton of art direction to curate the next row of images, size them all, compression, format swap, update the HTML and repeat, repeat, repeat. Perhaps at some point, I set up a Photoshop action or Automator workflow to batch process the images, but that’s another project. I think the hardest part has actually been finding the original files. I end up cross referencing my phone with my export from Instagram and various external hard drives only to often discover a specific photo is only in one or two of those locations. Sigh.

Testing & Next Steps

Looking at the PageSpeed performance and it seems like Google is happy. Here’s the desktop score. The mobile score is almost identical with only the performance score being a tiny bit lower at 94.

Google PageSpeed score showing 100 points for Performance, Accessibility, Best Practices and SEO.

PageSpeed is still mad at me (naturally) as the volume of photos keeps adding up to a large payload, but whatever, it’s my page, the images are set to lazy load and hopefully visitors are taking their time to actually look at the photos. Perhaps, I’ll limit the amount of images on initial page load and add a “show more” button to reveal more. Might be a good UX update to play around with in a future update.

I’m also not sure if PageSpeed is accurately evaluating the responsive images sizes. It’s still saying I need to size the images properly. Admittedly, I will need to go back and work on the image compression again to help reduce the overall page weight. I suspect I may need to add another image size to the srcset list and adjust the media queries to handle it. I’m just not convinced its working at it’s optimal best yet. I’ll also do more extensive performance testing with other tools to get comparison metrics.

Looking ahead, there’s still many more photos to process, so like the virtual library, I’ll add this page to my monthly backlog to update as I find time. I’m sure I’ll also need to adjust the layout for photos with different aspect ratios as I’m hoping to dive back into actual film photography soon. For now, this page is just one small step ahead to reclaim my content so keep checking back to see what I post.

View the photo page.

Smirk

December 8, 2024

Hand painted graffiti of a face with eyes closed and a sly smile

A bit of hand painted fun I found on the bottom of a light post. Eyes closed, big nose and a sly smile reminiscent of the Amazon logo. Maybe that smile is a nod to the Amazon logo. Better yet, perhaps it reflects the feeling of escaping from Amazon.

West Side, Manhattan.

Nowhere

November 16, 2024

A stencil spray paint graffiti on the sidewalk in orange reading, "New York or nowhere".

A fresh stencil from the sidewalks of Manhattan. I do love stencils although the cynical side of me thinks this might be part of some advertising campaign. Let’s hope it’s just the exuberance of a local artist instead. A kind of mantra for perseverance.

Midtown, Manhattan.

Ferengi

November 2, 2024

The end of a large metal dumpster painted with a light blue background. Graffiti of lightening bolts and skulls are across the top. A large painting of an alien head in shades of orange is at the bottom. It has buck teeth, scales and the letter "D" embossed on the forehead.

I saw this dumpster and that face immediately reminded me of the Ferengi race from Star Trek. I think it’s the pronounced forehead and ears. And of course, the color. I imagine it was even more striking when new and fresh. Now though, it’s been tagged, rusted and beaten about construction sites for a while. Bonus: it has a few skulls across the top for the obligatory Halloween vibe.

West Side, Manhattan.