<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
	<title>Steven Woodson</title>
	<subtitle>Steven Woodson - Independent Web Development Consultant writing about my experiences architecting, developing, and managing web applications.</subtitle>
	
	<link href="https://stevenwoodson.com/feed/feed.xml" rel="self"/>
	<link href="https://stevenwoodson.com"/>
	<updated>2026-04-12T19:30:24Z</updated>
	<id>https://stevenwoodson.com/</id>
	<author>
		<name>Steve Woodson</name>
		<email>me@stevenwoodson.com</email>
	</author>
  
	<entry>
		<title>Adding Bluesky Activity to an Eleventy Blog</title>
		<link href="https://stevenwoodson.com/blog/adding-bluesky-activity-to-an-eleventy-blog"/>
		<published>2026-04-03T15:58:37.000Z</published>
		<updated>2026-04-03T16:07:27.000Z</updated>
		<id>https://stevenwoodson.com/blog/adding-bluesky-activity-to-an-eleventy-blog</id>
    <summary>How I added Bluesky likes, reshares, and responses to my blog posts in Eleventy using the Bluesky API.</summary>
		<content type="html">&lt;img src=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2026/04/Adding-Bluesky-Activity-to-an-Eleventy-Blog.png&quot; alt=&quot;&quot;&gt;
&lt;p&gt;I&amp;#8217;ve been noticing so many folks adding Bluesky likes and comments to their personal blogs, after tacking on webmentions a while back I thought it&amp;#8217;d be a great addition to mine too. So, I finally made some time to get it added.&lt;/p&gt;



&lt;p&gt;I&amp;#8217;ll share some implementation notes first, then the 11ty-centric code that pulls the feed and displays it. My solution was deeply inspired by the notes and code graciously shared by a handful of folks, I&amp;#8217;ve linked to their content at the bottom of this post.&lt;/p&gt;



&lt;p&gt;Let&amp;#8217;s dive in!&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Implementation Notes&lt;/h2&gt;



&lt;p&gt;I&amp;#8217;ll admit this was simpler than I had anticipated, &lt;a href=&quot;https://docs.bsky.app/docs/category/http-reference&quot;&gt;the public API&lt;/a&gt; has everything you&amp;#8217;ll need to grab likes, reshares, and responses. That said, I wanted to make note of a few specifics that you may want to change depending on how you want this set up.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Eleventy-specific&lt;/h3&gt;



&lt;p&gt;This is specific to an 11ty (now &lt;a href=&quot;https://www.11ty.dev/blog/build-awesome/&quot;&gt;Build Awesome&lt;/a&gt;) build, but the JavaScript is largely portable. All you&amp;#8217;d need to change is how you cache the responses, or include &lt;a href=&quot;https://github.com/11ty/eleventy-fetch&quot;&gt;eleventy-fetch&lt;/a&gt; into your project.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Static HTML Rendering&lt;/h3&gt;



&lt;p&gt;I&amp;#8217;m going against the grain a bit and am only grabbing Bluesky data when the site is built. &lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;Pros &amp;#8211; No need for client-side JavaScript, more performant&lt;/li&gt;



&lt;li&gt;Cons &amp;#8211; Not real-time, so my site will lag behind a bit&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;I felt the pros outweighed the cons for my own case. I&amp;#8217;m humble enough to admit I don&amp;#8217;t get the most social media activity, so I&amp;#8217;m okay if a like on one of my posts takes a couple hours to show up on my blog.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Bluesky Post URL&lt;/h3&gt;



&lt;p&gt;This (any?) Bluesky reactions implementation necessitates that you have a &lt;strong&gt;Bluesky post URL&lt;/strong&gt; somehow attributed to your blog posts. For static files, that&amp;#8217;d be as simple as adding &lt;code&gt;bluesky&lt;/code&gt; to your &lt;a href=&quot;https://www.11ty.dev/docs/data-frontmatter/&quot;&gt;frontmatter&lt;/a&gt; and updating the Javascript to grab that instead of &lt;code&gt;post.custom_fields.bluesky&lt;/code&gt;. &lt;/p&gt;



&lt;p&gt;If you&amp;#8217;re pulling blog data from elsewhere then it&amp;#8217;ll need to come with that payload somehow. In my case, all my blog data is coming from a headless WordPress implementation so I&amp;#8217;m saving the Bluesky post URL as a WP native custom field.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Code&lt;/h2&gt;



&lt;p&gt;Finally, some code! &lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;bluesky.js&lt;/h3&gt;



&lt;p&gt;Here&amp;#8217;s my full &lt;code&gt;bluesky.js&lt;/code&gt; data file, it&amp;#8217;s grabbing and caching all Bluesky likes, reshares, and responses for any post with a bluesky post url attributed.&lt;/p&gt;



&lt;pre class=&quot;wp-block-code language-js&quot;&gt;&lt;code&gt;import { AssetCache } from &quot;@11ty/eleventy-fetch&quot;;
import getBlogposts from &quot;./blogposts.js&quot;;

const BSKY_API = &quot;https://public.api.bsky.app/xrpc&quot;;

/**
 * Parse a bsky.app URL into handle and post rkey
 * e.g. https://bsky.app/profile/stevenwoodson.com/post/3jzu2ove5g22i
 */
function parseBskyUrl(url) {
  const match = url.match(/bsky&#92;.app&#92;/profile&#92;/(&amp;#91;^/]+)&#92;/post&#92;/(&amp;#91;a-zA-Z0-9]+)/);
  if (!match) return null;
  return { handle: match&amp;#91;1], rkey: match&amp;#91;2] };
}

/**
 * Resolve a Bluesky handle to a DID
 */
async function resolveHandle(handle) {
  try {
    const params = new URLSearchParams({ handle });
    const response = await fetch(
      `${BSKY_API}/com.atproto.identity.resolveHandle?${params}`,
    );
    const data = await response.json();
    if (!data?.did) {
      throw new Error(`Could not resolve handle: ${handle}`);
    }
    return data.did;
  } catch (err) {
    throw new Error(`Failed to resolve handle ${handle}: ${err.message}`);
  }
}

/**
 * Fetch the post thread (replies)
 */
async function getPostThread(uri) {
  try {
    const params = new URLSearchParams({ uri, depth: 10 });
    const response = await fetch(
      `${BSKY_API}/app.bsky.feed.getPostThread?${params}`,
    );
    const data = await response.json();
    if (!data?.thread) {
      throw new Error(`No thread data returned for URI: ${uri}`);
    }
    return data.thread;
  } catch (err) {
    throw new Error(`Failed to fetch thread ${uri}: ${err.message}`);
  }
}

/**
 * Fetch likes for a post
 */
async function getLikes(uri) {
  try {
    const likes = &amp;#91;];
    let cursor;

    do {
      const params = { uri, limit: 100 };
      if (cursor) params.cursor = cursor;
      const response = await fetch(
        `${BSKY_API}/app.bsky.feed.getLikes?${new URLSearchParams(params)}`,
      );
      const data = await response.json();
      if (data?.likes) {
        likes.push(...data.likes);
      }
      cursor = data?.cursor;
    } while (cursor);

    return likes;
  } catch (err) {
    throw new Error(`Failed to fetch likes for ${uri}: ${err.message}`);
  }
}

/**
 * Fetch reposters for a post
 */
async function getReposters(uri) {
  try {
    const reposters = &amp;#91;];
    let cursor;

    do {
      const params = { uri, limit: 100 };
      if (cursor) params.cursor = cursor;
      const response = await fetch(
        `${BSKY_API}/app.bsky.feed.getRepostedBy?${new URLSearchParams(params)}`,
      );
      const data = await response.json();
      if (data?.repostedBy) {
        reposters.push(...data.repostedBy);
      }
      cursor = data?.cursor;
    } while (cursor);

    return reposters;
  } catch (err) {
    throw new Error(`Failed to fetch reposters for ${uri}: ${err.message}`);
  }
}

/**
 * Flatten replies from a thread into a simple list
 */
function extractReplies(thread) {
  const replies = &amp;#91;];
  if (!thread?.replies) return replies;

  for (const reply of thread.replies) {
    if (
      reply.post &amp;amp;&amp;amp;
      reply.post.author &amp;amp;&amp;amp;
      reply.post.author.did &amp;amp;&amp;amp;
      reply.post.uri
    ) {
      replies.push({
        author: {
          name:
            reply.post.author.displayName ||
            reply.post.author.handle ||
            &quot;Unknown&quot;,
          handle: reply.post.author.handle || &quot;unknown&quot;,
          avatar: reply.post.author.avatar || null,
          did: reply.post.author.did,
        },
        text: reply.post.record?.text || &quot;&quot;,
        createdAt: reply.post.record?.createdAt || null,
        likeCount: reply.post.likeCount || 0,
        replyCount: reply.post.replyCount || 0,
        url: `https://bsky.app/profile/${reply.post.author.handle || reply.post.author.did || &quot;&quot;}/post/${reply.post.uri.split(&quot;/&quot;).pop()}`,
        replies: extractReplies(reply),
      });
    }
  }

  // Sort by likes descending
  replies.sort((a, b) =&gt; b.likeCount - a.likeCount);
  return replies;
}

/**
 * Fetch all Bluesky reactions for a single post URL
 */
async function fetchBlueskyData(bskyUrl) {
  const parsed = parseBskyUrl(bskyUrl);
  if (!parsed) {
    throw new Error(`Invalid Bluesky URL: ${bskyUrl}`);
  }

  const did = await resolveHandle(parsed.handle);
  if (!did) {
    throw new Error(`Could not resolve DID for handle: ${parsed.handle}`);
  }

  const atUri = `at://${did}/app.bsky.feed.post/${parsed.rkey}`;

  const &amp;#91;thread, likes, reposters] = await Promise.all(&amp;#91;
    getPostThread(atUri),
    getLikes(atUri),
    getReposters(atUri),
  ]);

  if (!thread || !thread.post) {
    throw new Error(`Invalid thread data for ${atUri}`);
  }

  const postUrl = `https://bsky.app/profile/${did}/post/${parsed.rkey}`;

  return {
    postUrl,
    likes: likes.map((like) =&gt; ({
      name: like.actor?.displayName || like.actor?.handle || &quot;Unknown&quot;,
      handle: like.actor?.handle || &quot;unknown&quot;,
      avatar: like.actor?.avatar || null,
      url: `https://bsky.app/profile/${like.actor?.handle || like.actor?.did || &quot;&quot;}`,
    })),
    reposters: reposters.map((reposter) =&gt; ({
      name: reposter.displayName || reposter.handle || &quot;Unknown&quot;,
      handle: reposter.handle || &quot;unknown&quot;,
      avatar: reposter.avatar || null,
      url: `https://bsky.app/profile/${reposter.handle || reposter.did || &quot;&quot;}`,
    })),
    replies: extractReplies(thread),
    likeCount: likes.length,
    repostCount: reposters.length,
    replyCount: thread.replies ? thread.replies.length : 0,
  };
}

export default async () =&gt; {
  const cache = new AssetCache(&quot;bluesky&quot;);

  if (cache.isCacheValid(&quot;2h&quot;)) {
    console.log(&quot;Using cached bluesky&quot;);
    return cache.getCachedValue();
  }

  console.log(&quot;Fetching Bluesky comments...&quot;);
  const blogposts = await getBlogposts();
  const postsWithBluesky = blogposts.filter(
    (post) =&gt; post.custom_fields?.bluesky,
  );

  if (postsWithBluesky.length === 0) {
    console.log(&quot;No blog posts with Bluesky URLs found&quot;);
    const empty = {};
    await cache.save(empty, &quot;json&quot;);
    return empty;
  }

  console.log(`Found ${postsWithBluesky.length} blog posts with Bluesky URLs`);

  const results = {};

  for (const post of postsWithBluesky) {
    try {
      const data = await fetchBlueskyData(post.custom_fields.bluesky);
      if (data) {
        results&amp;#91;post.slug] = data;
      }
    } catch (err) {
      console.warn(
        `Failed to fetch Bluesky data for &quot;${post.slug}&quot;:`,
        err.message,
      );
    }
  }

  await cache.save(results, &quot;json&quot;);
  return results;
};&lt;/code&gt;&lt;/pre&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Filters&lt;/h3&gt;



&lt;p&gt;I then set up these two filters for use in my template.&lt;/p&gt;



&lt;pre class=&quot;wp-block-code&quot;&gt;&lt;code&gt;  blueskyBySlug: (bluesky, slug) =&gt; {
    return bluesky &amp;amp;&amp;amp; bluesky&amp;#91;slug] ? bluesky&amp;#91;slug] : null;
  },
  blueskyIsOwn: (handle) =&gt; {
    return handle === &quot;stevenwoodson.com&quot;;
  },&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;The first one &lt;code&gt;blueskyBySlug&lt;/code&gt; grabs the Bluesky data for that particular post, based on its slug. The second one &lt;code&gt;blueskyIsOwn&lt;/code&gt; helps me add a little extra styling for responses that are from me.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Template Partial&lt;/h3&gt;



&lt;p&gt;I then pull from the data gathered using the filter when rendering each post, here&amp;#8217;s the full partial file that I&amp;#8217;m using just below the post content.&lt;/p&gt;



&lt;pre class=&quot;wp-block-code language-njk&quot;&gt;&lt;code&gt;{% set blueskyData = bluesky | blueskyBySlug(blogpost.slug) %}

&amp;lt;div class=&quot;l-bluesky flow&quot;&gt;
  {% if blueskyData %}
    {% if (blueskyData.likeCount &gt; 0) 
      or(blueskyData.repostCount &gt; 0)or(blueskyData.replyCount &gt; 0) %}
      &amp;lt;header data-header-type=&quot;inline&quot;&gt;
        &amp;lt;h2 class=&quot;headline3&quot; id=&quot;bluesky-reactions&quot;&gt;
          &amp;lt;span aria-hidden=&quot;true&quot; class=&quot;fe fe-Bluesky&quot;&gt;&amp;lt;/span&gt; Bluesky Reactions&amp;lt;/h2&gt;
        &amp;lt;a href=&quot;{{ blueskyData.postUrl }}&quot; rel=&quot;noopener noreferrer&quot;&gt;View on Bluesky&amp;lt;/a&gt;
      &amp;lt;/header&gt;
    {% endif %}

    {% if blueskyData.likeCount &gt; 0 %}
      &amp;lt;h3 class=&quot;headline5&quot;&gt;
        &amp;lt;span aria-hidden=&quot;true&quot; class=&quot;fe fe-thumbs-up&quot;&gt;&amp;lt;/span&gt;
        {{ blueskyData.likeCount }}
        {% if blueskyData.likeCount == 1 %}Like{% else %}Likes{% endif %}
      &amp;lt;/h3&gt;
      &amp;lt;div class=&quot;avatars&quot;&gt;
        {% for like in blueskyData.likes %}
          &amp;lt;a class=&quot;bluesky-avatar&quot; href=&quot;{{ like.url }}&quot; rel=&quot;noopener noreferrer&quot;&gt;
            {% if like.avatar %}
              &amp;lt;img src=&quot;{{ like.avatar }}&quot; alt=&quot;{{ like.name }}&quot; class=&quot;avatar&quot; loading=&quot;lazy&quot;&gt;
            {% else %}
              &amp;lt;img src=&quot;{{ &#39;/assets/images/default-avatar.gif&#39; | url }}&quot; alt=&quot;{{ like.name }}&quot; class=&quot;avatar&quot; loading=&quot;lazy&quot;&gt;
            {% endif %}
          &amp;lt;/a&gt;
        {% endfor %}
      &amp;lt;/div&gt;
    {% endif %}

    {% if blueskyData.repostCount &gt; 0 %}
      &amp;lt;h3 class=&quot;headline5&quot;&gt;
        &amp;lt;span aria-hidden=&quot;true&quot; class=&quot;fe fe-repeat&quot;&gt;&amp;lt;/span&gt;
        {{ blueskyData.repostCount }}
        {% if blueskyData.repostCount == 1 %}Repost{% else %}Reposts{% endif %}
      &amp;lt;/h3&gt;
      &amp;lt;div class=&quot;avatars&quot;&gt;
        {% for reposter in blueskyData.reposters %}
          &amp;lt;a class=&quot;bluesky-avatar&quot; href=&quot;{{ reposter.url }}&quot; rel=&quot;noopener noreferrer&quot;&gt;
            {% if reposter.avatar %}
              &amp;lt;img src=&quot;{{ reposter.avatar }}&quot; alt=&quot;{{ reposter.name }}&quot; class=&quot;avatar&quot; loading=&quot;lazy&quot;&gt;
            {% else %}
              &amp;lt;img src=&quot;{{ &#39;/assets/images/default-avatar.gif&#39; | url }}&quot; alt=&quot;{{ reposter.name }}&quot; class=&quot;avatar&quot; loading=&quot;lazy&quot;&gt;
            {% endif %}
          &amp;lt;/a&gt;
        {% endfor %}
      &amp;lt;/div&gt;
    {% endif %}

    {% if blueskyData.replyCount &gt; 0 %}
      &amp;lt;h3 class=&quot;headline5&quot;&gt;
        &amp;lt;span aria-hidden=&quot;true&quot; class=&quot;fe fe-message-square&quot;&gt;&amp;lt;/span&gt;
        {{ blueskyData.replyCount }}
        {% if blueskyData.replyCount == 1 %}Reply{% else %}Replies{% endif %}
      &amp;lt;/h3&gt;
      {% macro blueskyReply(reply) %}
        {% if reply.text %}
          {% set blueskyHandle = reply.author.handle | default(&#39;&#39;) | lower | replace(&#39;@&#39;, &#39;&#39;) %}
          &amp;lt;li class=&quot;reply bluesky-reply{% if blueskyHandle|blueskyIsOwn %} reply--own{% endif %}&quot;&gt;
            &amp;lt;div class=&quot;reply__meta&quot;&gt;
              {% if reply.author.avatar %}
                &amp;lt;img src=&quot;{{ reply.author.avatar }}&quot; alt=&quot;{{ reply.author.name }}&quot; class=&quot;avatar&quot; loading=&quot;lazy&quot;&gt;
              {% else %}
                &amp;lt;img src=&quot;{{ &#39;/assets/images/default-avatar.gif&#39; | url }}&quot; alt=&quot;&quot; class=&quot;avatar&quot; loading=&quot;lazy&quot;&gt;
              {% endif %}
              &amp;lt;p&gt;
                &amp;lt;strong&gt;
                  &amp;lt;a href=&quot;{{ reply.url }}&quot; rel=&quot;noopener noreferrer&quot;&gt;{{ reply.author.name }}&amp;lt;/a&gt;
                  {% if reply.createdAt %}
                    on &amp;lt;time datetime=&quot;{{ reply.createdAt }}&quot;&gt;{{ reply.createdAt | readableDateFromISO }}&amp;lt;/time&gt;
                  {% endif %}
                &amp;lt;/strong&gt;
              &amp;lt;/p&gt;
            &amp;lt;/div&gt;
            &amp;lt;div class=&quot;reply__content&quot;&gt;
              {{ reply.text }}
            &amp;lt;/div&gt;
            &amp;lt;small class=&quot;bluesky-reply-meta&quot;&gt;
              {{ reply.likeCount }}
              {% if reply.likeCount == 1 %}like{% else %}likes{% endif %} &amp;amp;bull;
              {{ reply.replyCount }}
              {% if reply.replyCount == 1 %}reply{% else %}replies{% endif %}
            &amp;lt;/small&gt;
            {% if reply.replies and reply.replies.length &gt; 0 %}
              &amp;lt;ul class=&quot;replies bluesky-nested-replies&quot;&gt;
                {% for childReply in reply.replies %}
                  {{ blueskyReply(childReply) }}
                {% endfor %}
            &amp;lt;/ul&gt;
          {% endif %}
        &amp;lt;/li&gt;
      {% endif %}
    {% endmacro %}
    &amp;lt;ul class=&quot;replies&quot;&gt;
      {% for reply in blueskyData.replies %}
        {{ blueskyReply(reply) }}
      {% endfor %}
    &amp;lt;/ul&gt;
  {% endif %}
{% endif %}
&amp;lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Shout outs&lt;/h2&gt;



&lt;p&gt;Shout out to these folks who added Bluesky content to their blogs as well, and also chose to write about their process: &lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://whitep4nth3r.com/blog/show-bluesky-likes-on-blog-posts/&quot;&gt;How I show Bluesky likes on my blog posts&lt;/a&gt; by Salma Alam-Naylor&lt;/li&gt;



&lt;li&gt;&lt;a href=&quot;https://loige.co/how-i-added-bluesky-likes-to-my-astro-blog/&quot;&gt;How I added Bluesky likes to my Astro blog&lt;/a&gt; by Luciano Mammino&lt;/li&gt;



&lt;li&gt;&lt;a href=&quot;https://projects.verou.me/bluesky-likes/&quot;&gt;Bluesky Likes Web Components&lt;/a&gt; by Lea Verou&lt;/li&gt;



&lt;li&gt;&lt;a href=&quot;https://www.jjude.com/tech-notes/bsky-comments-11ty/&quot;&gt;Adding Bluesky Comments to 11ty Blog&lt;/a&gt; by Joseph Jude&lt;/li&gt;



&lt;li&gt;&lt;a href=&quot;https://brittanyellich.com/bluesky-comments-likes/&quot;&gt;I finally added Bluesky comments and likes to my blog (and you can too!)&lt;/a&gt; by Brittany Ellich&lt;/li&gt;



&lt;li&gt;&lt;a href=&quot;https://blog.jade0x.com/post/adding-bluesky-comments-to-your-astro-blog/&quot;&gt;Adding Bluesky Comments to Your Astro Blog&lt;/a&gt; by Jade Garafola&lt;/li&gt;
&lt;/ul&gt;

    </content>
	</entry>
  
	<entry>
		<title>Why Every Client Relationship Matters More Now</title>
		<link href="https://stevenwoodson.com/blog/why-every-client-relationship-matters-more-now"/>
		<published>2024-12-01T13:47:06.000Z</published>
		<updated>2026-04-02T21:13:42.000Z</updated>
		<id>https://stevenwoodson.com/blog/why-every-client-relationship-matters-more-now</id>
    <summary>A personal reflection on the deep connections that emerge when you pour your whole self into client relationships.</summary>
		<content type="html">&lt;img src=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2024/12/Why-Every-Client-Relationship-Matters-More-Now.png&quot; alt=&quot;&quot;&gt;
&lt;p&gt;Saying goodbye to a client is always tough, but as a freelance consultant, it feels profoundly different from my agency days. Back then, wrapping up a project was just another routine in the consulting lifecycle—finishing one assignment, then smoothly transitioning to the next. I&amp;#8217;d write out some documentation, exchange brief pleasantries and occasionally connect on LinkedIn or share emails, and move on without much emotional investment.&lt;/p&gt;



&lt;p&gt;Recently, I said farewell to one of my first long-term clients as a freelancer, and the experience cut deeper than I expected. We parted on great terms, even leaving the door open for potential future collaboration, but something about the goodbye felt more personal, more significant.&lt;/p&gt;



&lt;p&gt;As I&amp;#8217;ve reflected on this feeling, I&amp;#8217;ve realized the core difference: when you&amp;#8217;re a freelancer, you&amp;#8217;re not just delivering a service—you&amp;#8217;re building relationships under your own name and reputation. Every project is a direct reflection of you, not an agency&amp;#8217;s broader portfolio. This shifts everything. I found myself investing more deeply, caring more intensely about the work and the people behind the project.&lt;/p&gt;



&lt;p&gt;These clients have become more than just professional contacts. In many ways, they&amp;#8217;ve replaced the co-workers I no longer see daily. When the project ended, it didn&amp;#8217;t feel like a simple contract conclusion—it felt like saying goodbye to colleagues, to a temporary work family.&lt;/p&gt;



&lt;p&gt;This emotional complexity is new territory for me. I&amp;#8217;m learning that giving every client my absolute best means opening myself up to deeper connections—and potentially deeper goodbyes. But I&amp;#8217;m not going to change my approach. These genuine relationships are a strength, not a weakness. They&amp;#8217;re what set independent consultants apart.&lt;/p&gt;



&lt;p&gt;As I move forward, I&amp;#8217;ll continue to pour my full self into each project, knowing that professionalism and personal connection aren&amp;#8217;t mutually exclusive. They&amp;#8217;re what make freelance consulting not just a job, but a meaningful professional journey.&lt;/p&gt;



&lt;p&gt;In the end, this was a great reminder of why I pursued this path in the first place.&lt;/p&gt;

    </content>
	</entry>
  
	<entry>
		<title>Fluid Font Size by Character Count</title>
		<link href="https://stevenwoodson.com/blog/fluid-font-size-by-character-count"/>
		<published>2024-03-17T20:13:10.000Z</published>
		<updated>2024-03-17T20:13:12.000Z</updated>
		<id>https://stevenwoodson.com/blog/fluid-font-size-by-character-count</id>
    <summary>Larger font sizes for shorter headlines, vanilla JavaScript for the character count calculations and CSS clamp for responsive sizing</summary>
		<content type="html">&lt;img src=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2024/03/Variable-Fluid-Font-Size-with-Clamp-1.webp&quot; alt=&quot;&quot;&gt;
&lt;p&gt;I recently had a use case where CMS provided headlines needed to reasonably fit into a well defined space within a hero section. The hope was to fill that space a bit better by using larger font sizes for short headlines, and smaller sizes for longer ones, all based on character length.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;The Result&lt;/h2&gt;



&lt;p&gt;Putting the end result up here at the top because I know you&amp;#8217;re pressed for time and just want &lt;strong&gt;Something That Works™️&lt;/strong&gt;&lt;/p&gt;



&lt;p class=&quot;codepen&quot; data-height=&quot;500&quot; data-default-tab=&quot;result&quot; data-slug-hash=&quot;XWGQBEL&quot; data-user=&quot;stevenwoodson&quot; style=&quot;height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;&quot;&gt;
  &lt;span&gt;See the Pen &lt;a href=&quot;https://codepen.io/stevenwoodson/pen/XWGQBEL&quot;&gt;
  Length-dependent Font Size&lt;/a&gt; by Steve Woodson (&lt;a href=&quot;https://codepen.io/stevenwoodson&quot;&gt;@stevenwoodson&lt;/a&gt;)
  on &lt;a href=&quot;https://codepen.io/&quot;&gt;CodePen&lt;/a&gt;.&lt;/span&gt;
&lt;/p&gt;
&lt;script async=&quot;&quot; src=&quot;https://cpwebassets.codepen.io/assets/embed/ei.js&quot;&gt;&lt;/script&gt;



&lt;p&gt;The part you want to copy is the JavaScript, the CSS is mostly just for illustrative purposes as is most of the HTML. Here&amp;#8217;s how to use it:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;Add the JavaScript to your page&lt;/li&gt;



&lt;li&gt;Change any of the six variables (&lt;code&gt;minFontSize&lt;/code&gt;, &lt;code&gt;maxFontSize&lt;/code&gt;, &lt;code&gt;minLineLength&lt;/code&gt;, &lt;code&gt;maxLineLength&lt;/code&gt;, &lt;code&gt;minViewportWidth&lt;/code&gt;, &lt;code&gt;maxViewportWidth&lt;/code&gt;) to fit your needs.&lt;/li&gt;



&lt;li&gt;Add &lt;code&gt;data-dynamic-font-size&lt;/code&gt; to any headline you want to have this fluid size&lt;/li&gt;
&lt;/ul&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;How it Works&lt;/h2&gt;



&lt;p&gt;The &lt;strong&gt;TL;DR&lt;/strong&gt; is &amp;#8220;with CSS Clamp and a bit of JavaScript&amp;#8221;, read on for something a bit more descriptive.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;The Variables&lt;/h3&gt;



&lt;p&gt;We need a &lt;strong&gt;general range of characters&lt;/strong&gt; that should affect the font sizing. This could vary based on the amount of space you have available. These are defined in &lt;code&gt;minLineLength&lt;/code&gt; and &lt;code&gt;maxLineLength&lt;/code&gt;. &lt;/p&gt;



&lt;p&gt;We also need to know &lt;strong&gt;how big (and small) the text should be allowed to get&lt;/strong&gt;. These are defined (in pixels) in &lt;code&gt;minFontSize&lt;/code&gt; and &lt;code&gt;maxFontSize&lt;/code&gt;. &lt;/p&gt;



&lt;p&gt;Finally, we need to know &lt;strong&gt;the range of browser widths that should affect the calculations&lt;/strong&gt;. These are also in pixel values and are defined in &lt;code&gt;minViewportWidth&lt;/code&gt; and &lt;code&gt;maxViewportWidth&lt;/code&gt;.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Relative Max Size&lt;/h3&gt;



&lt;p&gt;The first thing we need is a character count of the text in the container we&amp;#8217;re referencing. This is achieved by the line &lt;code&gt;dynamicFontEl.textContent.replace(/(&amp;lt;([^&gt;]+)&gt;)/gi, &quot;&quot;).trim().length&lt;/code&gt; towards the bottom. That&amp;#8217;s stripping out any HTML tags and then trimming before getting a string count.&lt;/p&gt;



&lt;p&gt;With the variables and the total number of characters defined, the first calculation is to determine the ideal max font size for that character length, this is gathered in &lt;code&gt;relativeMaxFontSize&lt;/code&gt;. The fewer characters there are, the more space there is available to make the font size bigger. &lt;/p&gt;



&lt;pre class=&quot;wp-block-code language-js&quot;&gt;&lt;code&gt;const relativeMaxFontSize =
  textLength &amp;lt; minLineLength
    ? maxFontSize
    : textLength &gt; maxLineLength
    ? minFontSize
    : maxFontSize -
      ((maxFontSize - minFontSize) * (textLength - minLineLength)) /
        (maxLineLength - minLineLength);&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;Some examples to illustrate:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;If the text is less than or equal to &lt;code&gt;minLineLength&lt;/code&gt;, the font size will be &lt;code&gt;maxFontSize&lt;/code&gt;&lt;/li&gt;



&lt;li&gt;If the text is in between the min and max line length, we calculate where in that range it is and use the same range percentage for the font size. So a line length that is right in the middle of the min and max would lead to a font size right in the middle too.&lt;/li&gt;



&lt;li&gt;If the text is greater than or equal to &lt;code&gt;maxLineLength&lt;/code&gt;, the font size will be &lt;code&gt;minFontSize&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Introducing Clamp&lt;/h3&gt;



&lt;p&gt;We could stick with just the calculation above, set that resulting &lt;code&gt;relativeMaxFontSize&lt;/code&gt; to be the font size of the container and call it a day. But, I wanted to make use of fluid typography so the text would size appropriately on smaller viewports too. This is where good old CSS clamp comes in.&lt;/p&gt;



&lt;p&gt;First we calculate a subsection of the viewport width range set in &lt;code&gt;minViewportWidth&lt;/code&gt; and &lt;code&gt;maxViewportWidth&lt;/code&gt; relative to the max size we calculated before. We&amp;#8217;re doing this because we don&amp;#8217;t need the smallest character length headlines to size down until much smaller than the rest. Less characters means it can stick around at the larger size for longer without unwanted line breaks.&lt;/p&gt;



&lt;pre class=&quot;wp-block-code language-js&quot;&gt;&lt;code&gt;// Relative max viewport width, gets smaller the bigger the font size so it doesn&#39;t resize too soon
const relativeMaxViewportWidth =
  maxViewportWidth * (minFontSize / relativeMaxFontSize);

// Relative min viewport width, gets bigger the smaller the font size so it doesn&#39;t resize too late
const relativeMinViewportWidth =
  minViewportWidth * (maxFontSize / relativeMaxFontSize);

// Viewport width calculations
const viewportWidth =
  (100 * (maxFontSize - minFontSize)) /
  (relativeMaxViewportWidth - relativeMinViewportWidth);&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;Then, we get the &lt;code&gt;viewportWidth&lt;/code&gt; and &lt;code&gt;relativeFontSize&lt;/code&gt; to use in the clamp(). These are using the relative viewport sizes to adjust the font size at the right viewport size for that character length.&lt;/p&gt;



&lt;pre class=&quot;wp-block-code language-js&quot;&gt;&lt;code&gt;// Viewport width calculations
const viewportWidth =
  (100 * (maxFontSize - minFontSize)) /
  (relativeMaxViewportWidth - relativeMinViewportWidth);

// Relative font size calculation
const relativeFontSize =
  (relativeMinViewportWidth * maxFontSize -
    relativeMaxViewportWidth * minFontSize) /
  (relativeMinViewportWidth - relativeMaxViewportWidth);&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;Finally, we&amp;#8217;re ready to pull together the final clamp values!&lt;/p&gt;



&lt;p&gt;The clamp min is always going to be &lt;code&gt;minFontSize&lt;/code&gt; represented in &lt;code&gt;rem&lt;/code&gt; values, the clamp max value uses &lt;code&gt;relativeMaxFontSize&lt;/code&gt;, because we want to make sure it&amp;#8217;s not allowed to ever get any bigger than that. The current font size uses all that calculation magic above and is set to &lt;code&gt;${viewportWidth}vw + ${relativeFontSize / 16}rem&lt;/code&gt;.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;The End Result&lt;/h2&gt;



&lt;p&gt;What we&amp;#8217;re left with is a calculated font size that is bigger the shorter the text gets, and that font size fluidly resizes based on the viewport width at the ideal time for the size and space available.&lt;/p&gt;



&lt;p&gt;The best way to get a better sense for how this works is to &lt;a href=&quot;https://codepen.io/stevenwoodson/full/XWGQBEL&quot; rel=&quot;nofollow&quot;&gt;open up that CodePen&lt;/a&gt; shared above in a new tab and resize the browser. You&amp;#8217;ll notice that the last (longest and smallest) headline doesn&amp;#8217;t resize at all, and each subsequent headline going up will start to resize one after the other.&lt;/p&gt;



&lt;p&gt;All filling the available space as well as they possibly could. Pretty cool, right?&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;References&lt;/h2&gt;



&lt;p&gt;I&amp;#8217;m always big on giving credit to the posts and projects that inspired me, here&amp;#8217;s some for this work in no particular order:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://www.smashingmagazine.com/2022/01/modern-fluid-typography-css-clamp/&quot; rel=&quot;nofollow&quot;&gt;Modern Fluid Typography Using CSS Clamp&lt;/a&gt;&lt;/li&gt;



&lt;li&gt;&lt;a href=&quot;https://utopia.fyi/type/calculator&quot; rel=&quot;nofollow&quot;&gt;Fluid Type Calculator by Utopia.fyi&lt;/a&gt;&lt;/li&gt;



&lt;li&gt;&lt;a href=&quot;https://css-tricks.com/viewport-sized-typography/&quot; rel=&quot;nofollow&quot;&gt;Viewport Sized Typography&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

    </content>
	</entry>
  
	<entry>
		<title>Practical Developer Tips for Digital Accessibility Advocacy</title>
		<link href="https://stevenwoodson.com/blog/practical-developer-tips-for-digital-accessibility-advocacy"/>
		<published>2023-12-11T12:17:07.000Z</published>
		<updated>2023-12-11T12:39:48.000Z</updated>
		<id>https://stevenwoodson.com/blog/practical-developer-tips-for-digital-accessibility-advocacy</id>
    <summary>Digital accessibility advocacy tips from a web developer&amp;#8217;s perspective, all focusing on building accessibility support into our daily work.</summary>
		<content type="html">&lt;img src=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/12/Practical-Developer-Tips-for-Digital-Accessibility-Advocacy.webp&quot; alt=&quot;&quot;&gt;
&lt;p&gt;Hat tip to all the other digital accessibility focused folks out there, fighting the good fight to advocate, educate, and drive accessibility forward.&lt;/p&gt;



&lt;p&gt;This post is my little contribution to the rallying cry of doing everything we can to push digital accessibility and equal access forward. This is coming from my perspective as a web developer so these tips are going to skew pretty heavily in that direction, but I&amp;#8217;m hopeful they give you ideas for your own unique situation.&lt;/p&gt;



&lt;p&gt;Sometimes, all we need is to give ourselves permission to just do it anyway.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;It&amp;#8217;s hard out there&lt;/h2&gt;



&lt;p&gt;The difficulty of accessibility focused work is well known, and others far smarter than me have written some thoughtful articles about it.&lt;/p&gt;



&lt;blockquote class=&quot;wp-block-quote is-layout-flow wp-block-quote-is-layout-flow&quot;&gt;
&lt;p&gt;Stress and adversity are standard for people who have chosen accessibility careers.&lt;/p&gt;
&lt;cite&gt;&lt;a href=&quot;https://accessability.substack.com/p/the-accessibility-profession-can&quot; rel=&quot;nofollow&quot;&gt;Sheri Byrne-Haber, CPACC&lt;/a&gt;&lt;/cite&gt;&lt;/blockquote&gt;



&lt;blockquote class=&quot;wp-block-quote is-layout-flow wp-block-quote-is-layout-flow&quot;&gt;
&lt;p&gt;And because we care so much, we end up sinking a lot of our energies into this work. Mental as well as physical energy.&lt;/p&gt;
&lt;cite&gt;&lt;a href=&quot;https://nicsteenhout.substack.com/p/burnout-in-the-digital-accessibility&quot; rel=&quot;nofollow&quot;&gt;Nic Steenhout&lt;/a&gt;&lt;/cite&gt;&lt;/blockquote&gt;



&lt;p&gt;From combating misconceptions and willful ignorance, to painfully slow progress and &lt;a href=&quot;https://adrianroselli.com/2022/05/the-performative-a11yship-of-gaad.html&quot; rel=&quot;nofollow&quot;&gt;performative allyship&lt;/a&gt; with little action &amp;#8211; there&amp;#8217;s a lot to be stressed about.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;But we have to keep trying&lt;/h2&gt;



&lt;p&gt;One strong common thread in an industry full of &amp;#8220;it depends&amp;#8221; grey areas is our dedication to equal access, it&amp;#8217;s a primary motivator keeping us going.&lt;/p&gt;



&lt;blockquote class=&quot;wp-block-quote is-layout-flow wp-block-quote-is-layout-flow&quot;&gt;
&lt;p&gt;Perhaps the most difficult thing is internalizing all of this. Digital accessibility work is not easy, but it is vital.&lt;/p&gt;
&lt;cite&gt;&lt;a href=&quot;https://ericwbailey.website/published/truths-about-digital-accessibility/&quot; rel=&quot;nofollow&quot;&gt;Eric W. Bailey&lt;/a&gt;&lt;/cite&gt;&lt;/blockquote&gt;



&lt;blockquote class=&quot;wp-block-quote is-layout-flow wp-block-quote-is-layout-flow&quot;&gt;
&lt;p&gt;In the end, we must guarantee an accessible web that everyone can use and adjust to their needs. This is the basic promise of the web, a promise that is broken every day.&lt;/p&gt;
&lt;cite&gt;&lt;a href=&quot;https://yatil.net/blog/accessibility-action&quot; rel=&quot;nofollow&quot;&gt;Eric Eggert&lt;/a&gt;&lt;/cite&gt;&lt;/blockquote&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Practical Tips&lt;/h2&gt;



&lt;p&gt;As a web dev consultant, I&amp;#8217;ve had the opportunity to be part of quite a few different kinds of teams and have worked on just as many different tech stacks. With that breadth of experience, I&amp;#8217;ve also had the opportunity to try several ideas to push accessibility forward. &lt;/p&gt;



&lt;p&gt;Here&amp;#8217;s the top three that have worked for me, both in terms of delivering a more accessible end product and in sparking more interest and support from those around me.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Be the squeaky wheel&lt;/h3&gt;



&lt;p&gt;Sometimes you just know that accessibility is not a shared priority. Likely you&amp;#8217;ve already had &amp;#8220;the talk&amp;#8221; with the team/stakeholders/client and, while they sounded receptive and interested, there hasn&amp;#8217;t been much follow through.&lt;/p&gt;



&lt;p&gt;In these situations, the best advice I have is to be the relentless &amp;#8220;&lt;a href=&quot;https://en.wikipedia.org/wiki/The_squeaky_wheel_gets_the_grease&quot; rel=&quot;nofollow&quot;&gt;squeaky wheel&lt;/a&gt;&amp;#8220;. For example:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;Ask about user stories, acceptance criteria, and testing instructions that can help ensure an equal experience is considered throughout their lifecycle.&lt;/li&gt;



&lt;li&gt;Ask about user testing, especially for complex and/or novel interactions.&lt;/li&gt;



&lt;li&gt;Ask accessibility related questions when talking through a new design.&lt;/li&gt;



&lt;li&gt;Bring up issues while in development, and in code reviews.&lt;/li&gt;



&lt;li&gt;Get a commitment to addressing tech debt on a regular cadence, and push for accessibility to be part of that.&lt;/li&gt;



&lt;li&gt;Make sure testing is happening on assistive devices too.&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;Some specific questions I recall asking recently:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&amp;#8220;What should focus states of interactive controls look like?&amp;#8221;&lt;/li&gt;



&lt;li&gt;&amp;#8220;How should this custom widget work for keyboard only navigation?&amp;#8221;&lt;/li&gt;



&lt;li&gt;&amp;#8220;Can we get annotations for the preferred tab order?&amp;#8221;&lt;/li&gt;



&lt;li&gt;&amp;#8220;How can we guarantee a minimum contrast for this text on a background image?&amp;#8221;&lt;/li&gt;



&lt;li&gt;&amp;#8220;The video player doesn&amp;#8217;t have play/pause controls, can we add?&amp;#8221;&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;&lt;strong&gt;The main goal here&lt;/strong&gt; is to center accessibility in as many discussions as possible. Get others thinking about it and asking questions too. Show the practical benefits, explain how minor course corrections early on can save a lot of time later.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Celebrate attempts as well as wins&lt;/h3&gt;



&lt;p&gt;There are few topics in our industry with more grey area and &amp;#8220;it depends&amp;#8221; answers, so it&amp;#8217;s natural for folks still new to accessibility to be hesitant or misinformed.&lt;/p&gt;



&lt;p&gt;In fact, I&amp;#8217;ve put in several years of reading, learning, and applying accessibility best practices and eventually built up enough experience and confidence to even go get &lt;a href=&quot;https://stevenwoodson.com/blog/cpacc-exam-reflection/&quot;&gt;CPACC certified&lt;/a&gt;. And yet,&lt;em&gt; I still struggle with imposter syndrome&lt;/em&gt;.&lt;/p&gt;



&lt;blockquote class=&quot;wp-block-quote is-layout-flow wp-block-quote-is-layout-flow&quot;&gt;
&lt;p&gt;There are so many &amp;#8220;moving parts&amp;#8221; to accessibility that it&amp;#8217;s hard to define specific technical requirements.&lt;/p&gt;
&lt;cite&gt;&lt;a href=&quot;https://nicsteenhout.substack.com/p/accessibility-specific-tech-requirements&quot; rel=&quot;nofollow&quot;&gt;Nic Steenhout&lt;/a&gt;&lt;/cite&gt;&lt;/blockquote&gt;



&lt;p&gt;The key is to identify an enthusiasm to learn and improve in those around you, and encourage that in any way you can. For example:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;Give kudos when you notice all color combinations in the design system are accessible, then show how to annotate appropriate active, focus, and hover states for interactive controls.&lt;/li&gt;



&lt;li&gt;Celebrate their effort put into adding ARIA roles to a new interactive widget, even if they&amp;#8217;re all wrong. Then coach them through how to fix it, and &lt;a href=&quot;https://www.w3.org/TR/using-aria/#rule1&quot; rel=&quot;nofollow&quot;&gt;explaining the first rule of ARIA&lt;/a&gt;.&lt;/li&gt;



&lt;li&gt;Give appreciation to the QA folks that try a screen reader for the first time, and explain the difference between tabbing through operable controls and using a screen reader to navigate all content.&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;&lt;strong&gt;The main goal here&lt;/strong&gt; is to encourage more growth and learning, and to build up enough psychological safety for folks to feel comfortable asking questions and trying. Be their mentor and provide the guardrails, help them work through it on their own and in real situations.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;When all else fails, do it anyway&lt;/h3&gt;



&lt;p&gt;This may be the most controversial, but I can&amp;#8217;t deny it&amp;#8217;s effectiveness when applied at the right time. When you come across situations where you know the chosen direction is going to be inaccessible, and you have the means (and &lt;a href=&quot;https://en.wikipedia.org/wiki/Spoon_theory&quot; rel=&quot;nofollow&quot;&gt;the spoons&lt;/a&gt;) to make it accessible anyway, just do it anyway.&lt;/p&gt;



&lt;p&gt;Examples from a developer&amp;#8217;s perspective:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;In the video player missing pause controls scenario noted above, go ahead and add them in during development. Force someone to say &amp;#8220;yes I acknowledge this is bad for accessibility and still want you to remove it&amp;#8221; and see how often that happens. In our remote first world, this usually means you also have it in writing.&lt;/li&gt;



&lt;li&gt;If you see a non-decorative image without alt text &amp;#8211; and no path to get it &amp;#8220;officially&amp;#8221; provided &amp;#8211; do your best and add it yourself.&lt;/li&gt;



&lt;li&gt;Links that should be buttons, buttons that should be links, interactive controls using span and div tags. If you&amp;#8217;re touching that code anyway, fix them.&lt;/li&gt;



&lt;li&gt;No design direction on focus and hover states and no connection to the design team? Yeah I hate that too, but add them anyway and try to follow the designs as best as you can. If someone hates it, you&amp;#8217;ll hear about it and will get a chance to make it even better.&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;&lt;strong&gt;The main goal here&lt;/strong&gt; is to do our best to release products that are just a bit more accessible then they otherwise would have been. It may not move the needle very much for accessibility as a whole, but will help the people using your digital product have a better time.&lt;/p&gt;



&lt;p&gt;There&amp;#8217;s a reason this one is last, because it&amp;#8217;s generally not a great idea to be subversive, and depending on your situation could even land you in a bit of trouble. So please do take some caution.&lt;/p&gt;



&lt;p&gt;From my experience, there have been countless situations where I&amp;#8217;ve made the decision to do it anyway and no one noticed, because they&amp;#8217;re not looking for it. There are also many situations where my work was quite noticeable (like the pause controls on a video) and someone asked about it. More than half the time they&amp;#8217;ll say something like &amp;#8220;huh, okay then&amp;#8221; and move on, sometimes those people slowly turn into accessibility advocates too.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Every bit helps&lt;/h2&gt;



&lt;p&gt;I leave it up to you to determine which may be appropriate for your situation. Remember that the overall intent is to drive progress despite an initial lack of support, while you continue to try building that support.&lt;/p&gt;



&lt;p&gt;Sometimes, these tips help spread the word and builds momentum. Sometimes they don&amp;#8217;t. Either way, every bit helps when it comes to accessibility.&lt;/p&gt;

    </content>
	</entry>
  
	<entry>
		<title>Nuxt 2 to Astro 3 Replatforming &#8211; from Setup to Production</title>
		<link href="https://stevenwoodson.com/blog/replatforming-from-nuxtjs-2-to-astro"/>
		<published>2023-12-03T18:33:17.000Z</published>
		<updated>2023-12-06T01:47:07.000Z</updated>
		<id>https://stevenwoodson.com/blog/replatforming-from-nuxtjs-2-to-astro</id>
    <summary>Notes from a replatform effort from Nuxt to Astro, including setup, struggles, migration, and finding solutions for search and sitemap.</summary>
		<content type="html">&lt;img src=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/12/Replatforming-from-Nuxt-2-to-Astro-3.webp&quot; alt=&quot;&quot;&gt;
&lt;p&gt;I keep hearing great things about &lt;a href=&quot;https://astro.build/&quot; rel=&quot;nofollow&quot;&gt;Astro&lt;/a&gt;, and just so happened to have &lt;a href=&quot;https://a11y-solutions.stevenwoodson.com/&quot;&gt;a personal project&lt;/a&gt; that I needed to move off of Nuxt 2 as &lt;a href=&quot;https://v2.nuxt.com/lts/&quot; rel=&quot;nofollow&quot;&gt;its end of life date is rapidly approaching&lt;/a&gt;.&lt;/p&gt;



&lt;p&gt;Here&amp;#8217;s my journey from never using Astro to pushing the refactored codebase live. Luckily, there&amp;#8217;s &lt;a href=&quot;https://docs.astro.build/en/guides/migrate-to-astro/from-nuxtjs/&quot; rel=&quot;nofollow&quot;&gt;a handy Nuxt to Astro migration guide&lt;/a&gt; that I&amp;#8217;ll be using to get started.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Initial Setup&lt;/h2&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Install&lt;/h3&gt;



&lt;p&gt;Thanks Astro installer for the reminder that I&amp;#8217;m on an embarrassingly old version of Node. Got the new shiny &lt;a href=&quot;https://nodejs.org/en/download&quot; rel=&quot;nofollow&quot;&gt;Node LTS version 20.9.0&lt;/a&gt; installed and ready to roll.&lt;/p&gt;



&lt;figure class=&quot;wp-block-image size-full&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;486&quot; height=&quot;156&quot; src=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/10/Screen-Shot-2023-10-31-at-3.16.31-PM.png&quot; alt=&quot;Console output screenshot with an ASCII art smiling face and the following text:

Liftoff confirmed. Explore your project! 
Run npm run dev to start the dev server. CTRL+C to stop. 
Add frameworks like react or tailwind using astro add.

Stuck? Join us at https://astro.build/chat

Houston: Good luck out there, astronaut! 🚀&quot; class=&quot;wp-image-513&quot; srcset=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/10/Screen-Shot-2023-10-31-at-3.16.31-PM.png 486w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/10/Screen-Shot-2023-10-31-at-3.16.31-PM-300x96.png 300w&quot; sizes=&quot;auto, (max-width: 486px) 100vw, 486px&quot; /&gt;&lt;figcaption class=&quot;wp-element-caption&quot;&gt;Little attention to detail things like this really goes a long way!&lt;/figcaption&gt;&lt;/figure&gt;



&lt;p&gt;First impressions matter, and the Astro installer was really great. The step by step questions to get the project set up using &lt;code&gt;npm create astro@latest&lt;/code&gt; was a great touch. I didn&amp;#8217;t bother with adding TypeScript support for this, sorry TS fam but this project ain&amp;#8217;t it.&lt;/p&gt;



&lt;p&gt;Opened the demo &lt;code&gt;index.astro&lt;/code&gt; file and realized I didn&amp;#8217;t have any code formatting. Quick search and &amp;#8211; of course &amp;#8211; &lt;a href=&quot;https://docs.astro.build/en/editor-setup/&quot; rel=&quot;nofollow&quot;&gt;there&amp;#8217;s a whole page for IDE setup&lt;/a&gt;. I installed the Astro VS Code Extension and I was up and running. Also ended up adding the &lt;a href=&quot;https://github.com/withastro/prettier-plugin-astro&quot; rel=&quot;nofollow&quot;&gt;Astro-specific Prettier plugin&lt;/a&gt; too.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Initial Struggles&lt;/h2&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Web Components&lt;/h3&gt;



&lt;p&gt;As this was the first bigger topic I tackled (and struggled with), I ended up blogging about this particular topic separately over at &lt;a href=&quot;https://stevenwoodson.com/blog/web-components-in-astro/&quot;&gt;Web Components in Astro&lt;/a&gt;.&lt;/p&gt;



&lt;p&gt;Check that out for a deeper dive of my initial struggles wrapping my mind around astro components and how they can play nice with native web components.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Avoiding TypeScript&lt;/h3&gt;



&lt;p&gt;Little did I know that the &amp;#8220;I didn&amp;#8217;t bother with adding TypeScript support for this&amp;#8221; comment above would come back to haunt me. Turns out, there&amp;#8217;s no way to truly opt out of TypeScript and if you&amp;#8217;re using Astro VS Code Extension you&amp;#8217;re also locked into TS linting that surfaces TS errors too.&lt;/p&gt;



&lt;p&gt;I did a whole lot of increasingly desperate searches, trolled through the &lt;a href=&quot;https://github.com/withastro/language-tools&quot; rel=&quot;nofollow&quot;&gt;Astro Language Tools Github&lt;/a&gt; issues, and even joined the official Discord to finally come to this conclusion. I was particularly bummed to find out that &lt;a href=&quot;https://github.com/withastro/language-tools/tree/legacy-1.0/packages/vscode#configuration&quot; rel=&quot;nofollow&quot;&gt;the previous V1 version for the Astro VS Code plugin had an option for this&lt;/a&gt;, and it had to be removed (I think) due to the language server swap to Volar.&lt;/p&gt;



&lt;p&gt;For sake of completeness, here&amp;#8217;s some of what I tried unsuccessfully to disable TypeScript linting in VS Code for this Astro project:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;Set &lt;code&gt;&quot;typescript.validate.enable&quot;: false,&lt;/code&gt; in my VS Code workspace settings &amp;amp; restarted&lt;/li&gt;



&lt;li&gt;In the&amp;nbsp;&lt;code&gt;tsconfig.json&lt;/code&gt;&amp;nbsp;file
&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;Set &lt;code&gt;&quot;exclude&quot;: [&quot;src&quot;]&lt;/code&gt;&lt;/li&gt;



&lt;li&gt;Set &lt;code&gt;{ &quot;compilerOptions&quot;: { &quot;skipLibCheck&quot;: true } }&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;



&lt;li&gt;In a newly created&amp;nbsp;&lt;code&gt;tslint.json&lt;/code&gt;&amp;nbsp;file
&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;Set &lt;code&gt;&quot;linterOptions&quot;: { &quot;exclude&quot;: [ &quot;src&quot; ] }&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt; I&amp;#8217;m unsure why the standard advice of adding rules to workspace settings, tsconfig, and tslint didn&amp;#8217;t work, but I have a feeling it&amp;#8217;s due to the linting coming from another extension. &lt;/p&gt;



&lt;h4 class=&quot;wp-block-heading&quot;&gt;The Only Thing That Worked Disabling TS Errors&lt;/h4&gt;



&lt;p&gt;Because this is a relatively small project and I was thoroughly defeated with all the above research and attempts, I finally gave up. Here&amp;#8217;s what I&amp;#8217;m using to be able to move forward without errors:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;Adding &lt;code&gt;// @ts-nocheck&lt;/code&gt; at the top of files that I wanted to ensure the whole file ignored TS errors.&lt;/li&gt;



&lt;li&gt;In smaller use cases, I&amp;#8217;m using &lt;code&gt;// @ts-ignore&lt;/code&gt; for individual statements to ignore.&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;I&amp;#8217;m not sure why file-based ignoring works where project and IDE workspace settings didn&amp;#8217;t, so if you have any explanation I&amp;#8217;d love to hear about it. Will update this post if I do get some clarification.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Layouts &amp;amp; Styles&lt;/h2&gt;



&lt;p&gt;I figured, while I&amp;#8217;m tearing everything apart I might as well overhaul the styles a bit to take advantage of fluid type and space, design tokens, and to simplify the overall setup.&lt;/p&gt;



&lt;p&gt;Without considering that refactoring, this was the most straightforward part of the process. I copy/pasted all the style folders into a new &lt;code&gt;src/assets/css&lt;/code&gt; folder and imported the app.css file in my primary layout and it just worked. I opted not to use &lt;a href=&quot;https://docs.astro.build/en/core-concepts/project-structure/#srcstyles&quot; rel=&quot;nofollow&quot;&gt;the &lt;code&gt;src/styles&lt;/code&gt; convention&lt;/a&gt; because it wasn&amp;#8217;t required and I typically use an assets directory in other projects so that felt more comfortable.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Migrating Content&lt;/h2&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Static Content&lt;/h3&gt;



&lt;p&gt;Static content goes from &lt;code&gt;/static&lt;/code&gt; to the &lt;code&gt;/public&lt;/code&gt; folder, simple enough! This basically consisted of the code examples (which are saved as independent minimal HTML, CSS, and JavaScript files) and images including the favicon.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Page Content&lt;/h3&gt;



&lt;p&gt;For actual site pages, I didn&amp;#8217;t have a whole lot more than plain HTML in my page components so moving them consisted of:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;copying the &lt;code&gt;.vue&lt;/code&gt; files over and renaming them to &lt;code&gt;.astro&lt;/code&gt;&lt;/li&gt;



&lt;li&gt;removing the &lt;code&gt;&amp;lt;template&amp;gt;&lt;/code&gt; container and replacing with &lt;code&gt;&amp;lt;Layout&amp;gt;&lt;/code&gt;&lt;/li&gt;



&lt;li&gt;Moving the page title from the Vue component to the Layout container.&lt;/li&gt;



&lt;li&gt;Changing links from &lt;code&gt;&amp;lt;nuxt-link&amp;gt;&lt;/code&gt; to plain &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt; links.&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;For example, here&amp;#8217;s some pseudocode that encompasses all my pages structured in Nuxt &lt;code&gt;.vue&lt;/code&gt; components, using the Accessibility Statement as an example.&lt;/p&gt;



&lt;pre class=&quot;wp-block-code language-vue&quot;&gt;&lt;code&gt;&amp;lt;template&amp;gt;
  &amp;lt;article&amp;gt;
    &amp;lt;h1&amp;gt;Accessibility Statement&amp;lt;/h1&amp;gt;
    &amp;lt;p&amp;gt;Page Content.&amp;lt;/p&amp;gt;
  &amp;lt;/article&amp;gt;
&amp;lt;/template&amp;gt;

&amp;lt;script&amp;gt;
export default {
  data() {
    return { title: &#39;Accessibility Statement&#39; };
  },
  head() {
    return { title: this.title };
  },
};
&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;This page moved to Astro then looked like this.&lt;/p&gt;



&lt;pre class=&quot;wp-block-code language-astro&quot;&gt;&lt;code&gt;---
import Layout from &#39;../layouts/Layout.astro&#39;;
---

&amp;lt;Layout title=&quot;Accessibility Statement&quot;&amp;gt;
  &amp;lt;article class=&quot;page-container&quot;&amp;gt;
    &amp;lt;h1&amp;gt;Accessibility Statement&amp;lt;/h1&amp;gt;
    &amp;lt;p&amp;gt;Page Content.&amp;lt;/p&amp;gt;
  &amp;lt;/article&amp;gt;
&amp;lt;/Layout&amp;gt;&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;For any Nuxt links in the content, I did a find and replace and that worked out just fine. Nuxt links like &lt;code&gt;&amp;lt;nuxt-link to=&quot;/solutions/focus/&quot;&amp;gt;Focus&amp;lt;/nuxt-link&amp;gt;&lt;/code&gt; changed to &lt;code&gt;&amp;lt;a href=&quot;/solutions/focus/&quot;&amp;gt;Focus&amp;lt;/a&amp;gt;&lt;/code&gt; instead.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Dynamic Routes&lt;/h3&gt;



&lt;p&gt;I would say this ended up being the vast majority of my work in migrating over, dynamic routes is how static content is compiled into pages based on the content inside &lt;code&gt;/content&lt;/code&gt; and the route templates in &lt;code&gt;/pages&lt;/code&gt;.&lt;/p&gt;



&lt;p&gt;I referred back to &lt;a href=&quot;https://docs.astro.build/en/guides/migrate-to-astro/from-nuxtjs/#reference-convert-nuxtjs-syntax-to-astro&quot; rel=&quot;nofollow&quot;&gt;the Convert NuxtJS Syntax to Astro reference&lt;/a&gt; quite a bit for these routes.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Lack of Content Subfolder Support&lt;/h3&gt;



&lt;p&gt;The biggest difference between Nuxt and Astro is that it appears Astro doesn&amp;#8217;t allow for nested content collections (&lt;a href=&quot;https://docs.astro.build/en/guides/content-collections/#organizing-with-subdirectories&quot;&gt;source&lt;/a&gt;), so I needed to pull subfolders up to the root content folder all together under &amp;#8220;solutions&amp;#8221;. Not my favorite, but I&amp;#8217;m going with it so I don&amp;#8217;t have to figure out a customized workaround to do it.&lt;/p&gt;



&lt;p&gt;Another interesting tidbit is that content slugs have to all be unique, even if they&amp;#8217;re in different content subfolders.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Migrating Components&lt;/h2&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Vue Components&lt;/h3&gt;



&lt;p&gt;At first, I had grand ambitions of not pulling Vue into this project and going vanilla JS for everything. But for the sake of time to get everything ported over and online as soon as possible, I opted to keep some Vue components.&lt;/p&gt;



&lt;p&gt;To add Vue support on an existing Astro installation, you can use &lt;a href=&quot;https://docs.astro.build/en/guides/integrations-guide/vue/#astro-add-command&quot; rel=&quot;nofollow&quot;&gt;the Astro Add command&lt;/a&gt;. So in my case I did &lt;code&gt;npx astro add vue&lt;/code&gt; and it loaded everything up including the necessary configuration updates.&lt;/p&gt;



&lt;p&gt;Luckily, I didn&amp;#8217;t have to change much in my Vue components. Even though I was also upgrading from Vue 2 to Vue 3 as part of this process. Woohoo!&lt;/p&gt;



&lt;p&gt;For me, it mostly consisted of &lt;a href=&quot;https://v3-migration.vuejs.org/breaking-changes/v-model.html&quot; rel=&quot;nofollow&quot;&gt;swapping from &lt;code&gt;v-bind.sync&lt;/code&gt; to &lt;code&gt;v-model&lt;/code&gt;&lt;/a&gt;. If you&amp;#8217;re jumping from Vue 2 to Vue 3 as part of this too, I recommend reading through &lt;a href=&quot;https://v3-migration.vuejs.org/&quot; rel=&quot;nofollow&quot;&gt;the migration guide&lt;/a&gt; for additional breaking changes.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Vue + Web Components&lt;/h3&gt;



&lt;p&gt;I had a bit of trouble getting Vue to play nice with my native web components, and realized I&amp;#8217;ve not had to address this before. Vue will try to find a registered Vue component for any custom tags it encounters, throwing a &amp;#8220;failed to resolve component&amp;#8221; error for any that don&amp;#8217;t match.&lt;/p&gt;



&lt;p&gt;After some research I found out that there&amp;#8217;s &lt;a href=&quot;https://vuejs.org/guide/extras/web-components.html&quot; rel=&quot;nofollow&quot;&gt;a really elegant solution to this with a configuration update&lt;/a&gt;, setting &lt;code&gt;isCustomElement&lt;/code&gt; to be anything with a dash (or in my case ending with a &lt;code&gt;-l&lt;/code&gt;) in the &lt;code&gt;compilerOptions&lt;/code&gt; is all it takes.&lt;/p&gt;



&lt;p&gt;To Astro-ify the above solution, these same settings &lt;a href=&quot;https://docs.astro.build/en/guides/integrations-guide/vue/#options&quot; rel=&quot;nofollow&quot;&gt;can be added to the Vue portion of the Astro config&lt;/a&gt; at &lt;code&gt;astro.config.mjs&lt;/code&gt;. Mine ended up looking like the following:&lt;/p&gt;



&lt;pre class=&quot;wp-block-code language-js&quot;&gt;&lt;code&gt;import { defineConfig } from &#39;astro/config&#39;;
import vue from &#39;@astrojs/vue&#39;;

// https://astro.build/config
export default defineConfig({
  integrations: &amp;#91;
    vue({
      template: {
        compilerOptions: {
          // treat all tags with a -l as custom elements
          isCustomElement: (tag) =&amp;gt; tag.includes(&#39;-l&#39;),
        },
      },
    }),
  ],
});&lt;/code&gt;&lt;/pre&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Finishing Touches&lt;/h2&gt;



&lt;p&gt;Great, I&amp;#8217;ve got all my pages loading again! Now for a few remaining items that I had before and wanted to maintain in this new codebase.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Site Search&lt;/h3&gt;



&lt;p&gt;&lt;a href=&quot;https://v2.nuxt.com/docs/directory-structure/content#full-text-search&quot;&gt;Nuxt 2 had a full text search option built in&lt;/a&gt;, that was what I used for the site search functionality before. I&amp;#8217;m unsure if there is something comparable for Nuxt 3 without relying on an third party provider like Algolia.&lt;/p&gt;



&lt;p&gt;I really didn&amp;#8217;t want to have to give up the full site search functionality, and I didn&amp;#8217;t see anything built in to help pull this off. I did a bit of searching and found a couple options, ended up sticking with &lt;a href=&quot;https://pagefind.app/&quot; rel=&quot;nofollow&quot;&gt;Pagefind&lt;/a&gt; through CloudCannon. I really liked&lt;a href=&quot;https://blog.otterlord.dev/posts/astro-search/&quot; rel=&quot;nofollow&quot;&gt; this walkthrough by Reuben Tier for getting it set up specifically in Astro&lt;/a&gt;.&lt;/p&gt;



&lt;p&gt;That worked really great, and it&amp;#8217;s part of the build process so it can&amp;#8217;t get much more set-it-and-forget-it than that.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Sitemap Generator&lt;/h3&gt;



&lt;p&gt;Now that the site itself is looking pretty good, I set my sights to cleaning it up for a production launch. The first thing I look at when doing that is making sure I have an accurate sitemap.&lt;/p&gt;



&lt;p&gt;Astro sites don&amp;#8217;t come with a sitemap by default, but there is &lt;a href=&quot;https://docs.astro.build/en/guides/integrations-guide/sitemap/&quot; rel=&quot;nofollow&quot;&gt;a really easy integration for adding it here&lt;/a&gt;. Essentially, you just need to run through that installer and then add the root site URL to the config. When it was done, mine ended up looking like this&lt;/p&gt;



&lt;pre class=&quot;wp-block-code language-js&quot;&gt;&lt;code&gt;export default defineConfig({
  site: &#39;https://a11y-solutions.stevenwoodson.com&#39;,
  integrations: &amp;#91;vue({
    template: {
      compilerOptions: {
        // treat all tags with a -l as custom elements
        isCustomElement: tag =&amp;gt; tag.includes(&#39;-l&#39;)
      }
    }
  }), sitemap()]
});&lt;/code&gt;&lt;/pre&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;View Transitions&lt;/h3&gt;



&lt;p&gt;There&amp;#8217;s a neat built-in feature of Nuxt that lets you do cross-fade page transitions. I thought, since Astro would be static generated, I would lose that little nice to have. Turns out I was wrong! &lt;a href=&quot;https://docs.astro.build/en/guides/view-transitions/&quot;&gt;Astro View Transitions&lt;/a&gt; to the rescue, I imported and added the &lt;code&gt;&amp;lt;ViewTransitions /&amp;gt;&lt;/code&gt; to my primary layout and it worked.&lt;/p&gt;



&lt;p&gt;Only thing I noticed, was that the main navigation would briefly lose its style every time I clicked on a new page. It looks like that may be because it&amp;#8217;s a Vue component loaded in an astro island? Utilizing the &lt;code&gt;transition:persist&lt;/code&gt; property to persist its state worked in this case, thankfully. I had experimented with moving the CSS of that component to the global styles which also helped, but I much prefer the portability of keeping styles with the component so I&amp;#8217;m sticking with the persist property instead.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Final Thoughts&lt;/h2&gt;



&lt;p&gt;This was a great exercise for me to learn about and explore Astro, and besides the few snags I hit early on it was as smooth of a process as I could have hoped. Here&amp;#8217;s some general notes now that I&amp;#8217;m done.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Ideal Projects for Astro&lt;/h3&gt;



&lt;p&gt;I now feel like I have a better understanding of what kind of projects will benefit most from this framework. I think the sweet spot would be sites that are mostly static, but have some mild to moderate interactivity that could benefit from a more robust library.&lt;/p&gt;



&lt;p&gt;For example, as noted above in the Vue Components section, there were cases where I tried to do the JavaScript heavy lifting within an Astro component with the ultimate goal being to remove Vue altogether.&lt;/p&gt;



&lt;p&gt;But, all that vanilla JS added up quickly. Shifting back to Vue for only the component pieces that made sense shrunk the code in those components to less than half.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Performance&lt;/h3&gt;



&lt;p&gt;This is a relatively small site, and I was able to get most of it statically generated both in Nuxt and Astro, so I didn&amp;#8217;t notice a whole lot of performance change between the two versions.&lt;/p&gt;



&lt;p&gt;Looks like Lighthouse agreed with that sentiment, both received a 99 of 100. That 1 is likely due to some cumulative layout shift that I really should dig into.&lt;/p&gt;



&lt;p&gt;I do, however, feel like I had a much better control and understanding of components that could potentially affect performance and more control over how their handled because of the concept of &lt;a href=&quot;https://docs.astro.build/en/concepts/islands/&quot; rel=&quot;nofollow&quot;&gt;Astro Islands&lt;/a&gt;.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Time to Complete Replatform&lt;/h3&gt;



&lt;p&gt;While this blog post is rather long, I do feel like I&amp;#8217;ve glossed over the more time intensive parts which may give a the wrong impression regarding how long this actually took me. So I&amp;#8217;m going to try to describe the replatforming efforts that took the most time, and then estimate how long it all took me to complete.&lt;/p&gt;



&lt;p&gt;Replatforming &lt;a href=&quot;https://a11y-solutions.stevenwoodson.com/&quot;&gt;Accessibility Solutions&lt;/a&gt; included the following:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;Learning Astro&lt;/li&gt;



&lt;li&gt;Nuxt 2 to Astro 3 conversion&lt;/li&gt;



&lt;li&gt;Deep dive into &lt;a href=&quot;https://stevenwoodson.com/blog/web-components-in-astro/&quot;&gt;Web Components within Astro&lt;/a&gt;&lt;/li&gt;



&lt;li&gt;Vue 2 to Vue 3 conversion&lt;/li&gt;



&lt;li&gt;SCSS to vanilla CSS&lt;/li&gt;



&lt;li&gt;Styles overhauled to use custom properties and fluid typography &amp;amp; spacing&lt;/li&gt;



&lt;li&gt;Native search to Pagefind&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;With all the above, I&amp;#8217;d estimate this took me around 80 hours to complete. Inclusive of reading documentation, coding, troubleshooting, testing, and releasing.&lt;/p&gt;

    </content>
	</entry>
  
	<entry>
		<title>Why Budgets are so Important for Software Development  Projects</title>
		<link href="https://stevenwoodson.com/blog/why-budgets-are-so-important-for-software-development-projects"/>
		<published>2023-11-25T13:20:33.000Z</published>
		<updated>2023-11-25T13:20:36.000Z</updated>
		<id>https://stevenwoodson.com/blog/why-budgets-are-so-important-for-software-development-projects</id>
    <summary>Using a real estate analogy, I share why budget is a critical question that should be considered carefully when starting a new software dev project.</summary>
		<content type="html">&lt;img src=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/11/Why-Budgets-are-so-Important-for-Software-Development.png&quot; alt=&quot;&quot;&gt;
&lt;p&gt;As a freelance web dev consultant, I&amp;#8217;ve been having a whole lot of introductory conversations. They generally fit in one of a few categories:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;potential clients looking for a developer for an upcoming project&lt;/li&gt;



&lt;li&gt;fellow consultants looking for a dev to join them on a project&lt;/li&gt;



&lt;li&gt;someone for either of the previous two groups that don&amp;#8217;t yet have something specific in mind, but want to find potential consultants for work they&amp;#8217;re trying to get started soon.&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;One big question I always ask all of them is &amp;#8220;What&amp;#8217;s your budget?&amp;#8221;. As you might expect, this is also one of the hardest questions to get answered. &lt;/p&gt;



&lt;p&gt;In this post I&amp;#8217;m going to share why I believe it&amp;#8217;s a critical question that should be considered carefully. Starting with a real estate analogy.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;A Real Estate Analogy? Really?&lt;/h2&gt;



&lt;p&gt;I have a way with analogies to help convey details about software development. Usually they&amp;#8217;re half-baked comparisons shared with folks as a shortcut to get them to understand some eccentricity, difficulty, or complexity in the topic we&amp;#8217;re discussing.&lt;/p&gt;



&lt;p&gt;The critical requirement of such analogies is that they&amp;#8217;re based on a shared understanding, which we rely on to bridge that gap. &lt;/p&gt;



&lt;p&gt;In one such conversation, I got the idea to compare a software development budget to a real estate budget and it worked surprisingly well. So well, in fact, that I&amp;#8217;ve started using it all the time and led to writing this post.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;You Have to Start Somewhere&lt;/h2&gt;



&lt;p&gt;This is the crux of why this analogy works, budgets may not be the most fun thing to talk about but they certainly are the most practical way to set expectations and find a good starting point.&lt;/p&gt;



&lt;p&gt;It&amp;#8217;s just as true with a software project as it is with buying a house. Knowing the budget is one vital step to finding a solution that is within reach and provides maximum value.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Setting Expectations, Saving Time&lt;/h2&gt;



&lt;p&gt;What&amp;#8217;s one of the first questions you&amp;#8217;ll get from a realtor? Yep, budget and probably location.&lt;/p&gt;



&lt;p&gt;Why? Because they&amp;#8217;re not going to take you to an open house for a $2MM home when you&amp;#8217;re looking to spend a fraction of that. They&amp;#8217;re not going to suggest a home that&amp;#8217;s 200 miles (320km) away from your ideal location.&lt;/p&gt;



&lt;p&gt;Would you love that $2MM house? Probably.&lt;/p&gt;



&lt;p&gt;Do you want to be responsible for the maintenance and monthly bills? Probably not.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Taking the Analogy Further&lt;/h2&gt;



&lt;p&gt;Here are some other examples I gave when sharing this analogy, depending on the situation.&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;strong&gt;Additional Constraints&lt;/strong&gt; &amp;#8211; I had briefly mentioned in the analogy that desired location is another early question that sets some helpful constraints. &lt;br /&gt;The same would be true for required technologies or an existing codebase that needs to be retained.&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;Prebuilt vs Custom&lt;/strong&gt; &amp;#8211; You may have the budget and desire for a custom built home, giving you full control over every detail. It&amp;#8217;ll take longer, but you have more opportunity to get exactly what you want.&lt;br /&gt;I would say this is even more true with software development, the potential return on investment is higher when you have something built specifically for your unique needs.&lt;/li&gt;
&lt;/ul&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Wrapping Up&lt;/h2&gt;



&lt;p&gt;I hope this analogy helps highlight the importance of discussing budget &amp;#8211; and any other constraints &amp;#8211; from the start. It&amp;#8217;s important to understand and work within these constraints as early as possible&lt;/p&gt;



&lt;p&gt;So when you&amp;#8217;re ready to start that new web application project and are asked about budget, rather than concluding their motivations are &amp;#8220;how much can we make on this deal&amp;#8221;, consider instead that they&amp;#8217;re trying to determine &amp;#8220;how can we achieve the most value that&amp;#8217;s within budget&amp;#8221;.&lt;/p&gt;

    </content>
	</entry>
  
	<entry>
		<title>Web Components in Astro</title>
		<link href="https://stevenwoodson.com/blog/web-components-in-astro"/>
		<published>2023-11-06T19:11:20.000Z</published>
		<updated>2023-12-03T20:12:34.000Z</updated>
		<id>https://stevenwoodson.com/blog/web-components-in-astro</id>
    <summary>How to use native web components inside an Astro project, with a few isolated examples and explanation of some issues I had to navigate.</summary>
		<content type="html">&lt;img src=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/11/Web-Components-in-Astro.webp&quot; alt=&quot;&quot;&gt;
&lt;p&gt;I&amp;#8217;ve recently been jumping into learning more about &lt;a href=&quot;https://astro.build/&quot; rel=&quot;nofollow&quot;&gt;Astro&lt;/a&gt;, using a personal project that needs to be replatformed as an excuse to give it a try. The first thing I wanted to dive into and learn more about was how components are handled, and especially how well it plays with &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Web_Components&quot; rel=&quot;nofollow&quot;&gt;browser native Web Components&lt;/a&gt;.&lt;/p&gt;



&lt;p&gt;I burned &lt;em&gt;a lot&lt;/em&gt; of time here just trying to understand how a web component could be copied into an Astro project. So instead of waiting to blog about the whole process, I wanted to share my learnings about components separately first.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;First Attempt &amp;#8211; AstroHeart&lt;/h2&gt;



&lt;p&gt;My first attempt was to get the &lt;code&gt;AstroHeart&lt;/code&gt; example in the &lt;a href=&quot;https://docs.astro.build/en/guides/client-side-scripts/#web-components-with-custom-elements&quot; rel=&quot;nofollow&quot;&gt;Web components with custom elements&lt;/a&gt; section of the Scripts and Event Handling documentation to work.&lt;/p&gt;



&lt;p&gt;I got it working when the whole code block was placed in a &lt;a href=&quot;https://docs.astro.build/en/core-concepts/layouts/&quot; rel=&quot;nofollow&quot;&gt;layout&lt;/a&gt; or a &lt;a href=&quot;https://docs.astro.build/en/core-concepts/astro-pages/&quot; rel=&quot;nofollow&quot;&gt;page&lt;/a&gt;, but not when it was a referenced component from the &lt;code&gt;/components/&lt;/code&gt; directory. I think we can all agree that adding web components to a page or layout would be categorized as a &amp;#8220;Bad Idea&amp;#8221;.&lt;/p&gt;



&lt;p&gt;After much fiddling I came to the conclusion that an Astro component that then instantiates a native web component is the best I could manage. It feels weird, but it works.&lt;/p&gt;



&lt;p&gt;&lt;a href=&quot;https://stackblitz.com/edit/github-mdsq7h-1r8rqx?file=src%2Fpages%2Findex.astro&quot;&gt;Here&amp;#8217;s the full minimal example of AstroHeart working as a web component wrapped with an Astro component on Stackblitz&lt;/a&gt;.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Second Web Component Attempt &amp;#8211; Sidebar&lt;/h2&gt;



&lt;p&gt;Now that I know it&amp;#8217;s possible, I wanted to ramp things up with an actually usable — and slightly more complicated — example. The first web component I needed to port over for my project happened to be &lt;a href=&quot;https://every-layout.dev/layouts/sidebar/#the-component&quot; rel=&quot;nofollow&quot;&gt;the excellent Sidebar from the Every Layout project&lt;/a&gt;.&lt;/p&gt;



&lt;p&gt;A couple new issues emerged in this process of adding it, here&amp;#8217;s how I got passed them both.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Passing Props&lt;/h3&gt;



&lt;p&gt;Welp, the Every Layout Sidebar has five separate attributes that can be added to the &lt;code&gt;&amp;lt;sidebar-l&amp;gt;&lt;/code&gt; custom element and I quickly realized I need to manage that prop handshake between Astro and the native web component.&lt;/p&gt;



&lt;p&gt;First, I went with the most obvious solution of gathering all props and manually passing them in, something like the following:&lt;/p&gt;



&lt;pre class=&quot;wp-block-code&quot;&gt;&lt;code&gt;---
const { side, sideWidth, contentMin, space, noStretch } = Astro.props;
---

&amp;lt;sidebar-l
  side={side}
  sideWidth={sideWidth}
  contentMin={contentMin}
  space={space}
  noStretch={noStretch}
&amp;gt;
  &amp;lt;slot /&amp;gt;
&amp;lt;/sidebar-l&amp;gt;&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;But that so verbose and error prone, there&amp;#8217;s gotta be a better way right? Turns out, there is! Replace that code above with this and you&amp;#8217;re all set.&lt;/p&gt;



&lt;pre class=&quot;wp-block-code&quot;&gt;&lt;code&gt;&amp;lt;sidebar-l {...Astro.props}&amp;gt;
  &amp;lt;slot /&amp;gt;
&amp;lt;/sidebar-l&amp;gt;&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;Still a little weird but much better, right? Now if names change or more props are added I don&amp;#8217;t have to keep going back to update this.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Scoped vs Global Styles&lt;/h3&gt;



&lt;p&gt;When styles are added to the Astro component using &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; tags, they&amp;#8217;re automatically scoped. In some cases, as is the case for this Sidebar component with several modification options, this can be less desirable.&lt;/p&gt;



&lt;p&gt;I found this out first hand when I was attempting to adjust the spacing between the sidebar and the main content area using &lt;code&gt;&amp;lt;Sidebar space=&quot;var(--space-m)&quot;&amp;gt;&lt;/code&gt;. I came to realize that the default styles were of a higher specificity than the modified styles applied via that &lt;code&gt;space&lt;/code&gt; attribute.&lt;/p&gt;



&lt;p&gt;In cases like this, drop a &lt;code&gt;is:global&lt;/code&gt; into the opening style tag like this, &lt;code&gt;&amp;lt;style is:global&amp;gt;&lt;/code&gt; and it&amp;#8217;ll be treated as a global style where the cascade for modifications will work.&lt;/p&gt;



&lt;p&gt;I suppose you could also move the component styles to where the rest of your global styles are located, but you lose that encapsulation of everything being in one place.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Live example&lt;/h3&gt;



&lt;p&gt;I&amp;#8217;ve worked up &lt;a href=&quot;https://stackblitz.com/edit/github-mdsq7h-sa7py9?file=src%2Fcomponents%2FSidebar.astro&quot; rel=&quot;nofollow&quot;&gt;another Stackblitz for this Sidebar component example&lt;/a&gt; too.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Third Attempt &amp;#8211; Auto-import Multiple Components&lt;/h2&gt;



&lt;p&gt;I still felt a bit odd with wrapping web components inside Astro components, even though I was able to overcome any blockers that emerged. I think it&amp;#8217;s because having to manually Astro-ify every component I wanted to use feels like it goes against the portability of native web components.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;So, I tried again. &lt;/h3&gt;



&lt;p&gt;With a bit more encouragement from &lt;a href=&quot;https://daniel-saunders.com/&quot;&gt;Daniel Saunders&lt;/a&gt;, who reminded me that all it really takes is an import of the web component somewhere in the page, I set my sights on trying that.&lt;/p&gt;



&lt;p&gt;As a quick proof of concept, I grabbed another of the free Every Layout components &amp;#8211; &lt;a href=&quot;https://every-layout.dev/layouts/stack/&quot;&gt;the Stack&lt;/a&gt; &amp;#8211; and built up a minimally viable page with the following. The references to &lt;code&gt;web-components&lt;/code&gt; is because I wanted another separate directory under &lt;code&gt;src&lt;/code&gt; to keep Astro components separate from web components.&lt;/p&gt;



&lt;pre class=&quot;wp-block-code&quot;&gt;&lt;code&gt;&amp;lt;stack-l space=&quot;3rem&quot;&amp;gt;
  &amp;lt;h2&amp;gt;H2 headline&amp;lt;/h2&amp;gt;
  &amp;lt;stack-l space=&quot;1.5rem&quot;&amp;gt;
    &amp;lt;p&amp;gt;
      Lorem ipsum dolor sit amet, consectetur adipisicing elit. Quasi aperiam,
      cupiditate qui totam incidunt ipsum dolores.
    &amp;lt;/p&amp;gt;
    &amp;lt;p&amp;gt;
      Lorem ipsum dolor sit amet, consectetur adipisicing elit. Quasi aperiam,
      cupiditate qui totam incidunt ipsum dolores.
    &amp;lt;/p&amp;gt;
  &amp;lt;/stack-l&amp;gt;
  &amp;lt;h2&amp;gt;H2 headline&amp;lt;/h2&amp;gt;
  
&amp;lt;/stack-l&amp;gt;
&amp;lt;script&amp;gt;
  import &#39;../web-components/Stack/Stack.js&#39;;
  import &#39;../web-components/Stack/Stack.css&#39;;
&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;Sure enough, that worked just fine!&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Automating imports&lt;/h3&gt;



&lt;p&gt;But, adding a script tag with imports of the (potentially double digit) JS and CSS web component files I&amp;#8217;d need to use on every page doesn&amp;#8217;t sound like fun to me. Instead, I set my sights on auto-importing based on a glob pattern.&lt;/p&gt;



&lt;p&gt;Enter &lt;a href=&quot;https://docs.astro.build/en/reference/api-reference/#astroglob&quot;&gt;Astro.glob()&lt;/a&gt; as that sounds like just what I need. I realized that what I was doing wasn&amp;#8217;t going to work though, because I was attempting the import in the front matter which meant this was trying to import on the server side where &lt;code&gt;HTMLElement&lt;/code&gt; isn&amp;#8217;t defined.&lt;/p&gt;



&lt;figure class=&quot;wp-block-image size-large&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1024&quot; height=&quot;606&quot; src=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/11/Screen-Shot-2023-11-21-at-7.25.22-AM-1024x606.png&quot; alt=&quot;Error message from Astro with the following details.

An error occurred. HTMLElement is not defined in Stack/Stack.js on line 1&quot; class=&quot;wp-image-522&quot; srcset=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/11/Screen-Shot-2023-11-21-at-7.25.22-AM-1024x606.png 1024w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/11/Screen-Shot-2023-11-21-at-7.25.22-AM-300x177.png 300w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/11/Screen-Shot-2023-11-21-at-7.25.22-AM-768x454.png 768w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/11/Screen-Shot-2023-11-21-at-7.25.22-AM.png 1070w&quot; sizes=&quot;auto, (max-width: 1024px) 100vw, 1024px&quot; /&gt;&lt;/figure&gt;



&lt;p&gt;Remembering that it needs to be in a script tag within the content of the page rather than in the front matter got me away from that error, but it still didn&amp;#8217;t work.&lt;/p&gt;



&lt;p&gt;I read up on what &lt;code&gt;Astro.glob()&lt;/code&gt; is doing, it&amp;#8217;s a wrapper for &lt;a href=&quot;https://vitejs.dev/guide/features.html#glob-import&quot; rel=&quot;nofollow&quot;&gt;Vite&amp;#8217;s Glob Import&lt;/a&gt; (&lt;code&gt;import.meta.glob&lt;/code&gt;). That&amp;#8217;s set up by default to lazy load which meant that it wasn&amp;#8217;t going to end up on the page because I wasn&amp;#8217;t using it on the server side. From the docs:&lt;/p&gt;



&lt;blockquote class=&quot;wp-block-quote is-layout-flow wp-block-quote-is-layout-flow&quot;&gt;
&lt;p&gt;Matched files are by default lazy-loaded via dynamic import and will be split into separate chunks during build. If you&amp;#8217;d rather import all the modules directly (e.g. relying on side-effects in these modules to be applied first), you can pass&amp;nbsp;&lt;code&gt;{ eager: true }&lt;/code&gt;&amp;nbsp;as the second argument&lt;/p&gt;
&lt;/blockquote&gt;



&lt;p&gt;Once I did just that, I got it working!&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Auto-importing at the Layout Level&lt;/h3&gt;



&lt;p&gt;One more change now that I had something functional, instead of doing this import on the page I moved it to the bottom of my global layout file. &lt;/p&gt;



&lt;p&gt;Now, I have access to all web components on all pages that use this layout. Here&amp;#8217;s the final code for importing everything, placed in my layout:&lt;/p&gt;



&lt;pre class=&quot;wp-block-code&quot;&gt;&lt;code&gt;&amp;lt;script&amp;gt;
  const webComponentsCSS = await import.meta.glob(
    &#39;../web-components/**/*.css&#39;,
    { eager: true }
  );
  const webComponentsJS = await import.meta.glob(
    &#39;../web-components/**/*.js&#39;,
    { eager: true }
  );
&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;I added a second web component to my setup to make sure it was importing both, and sure enough it did! Rather than pasting all that code here, scroll down to the Live example link below to see it all isolated in another Stackblitz.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Performance Implications&lt;/h3&gt;



&lt;p&gt;Because I&amp;#8217;m still learning about all this, and the project I have in mind to use it is very small, I&amp;#8217;ve not delved too deep into the implications of importing all web components in this way. I did run a production build to see how everything came together, it appears that all components are compiled into one &lt;code&gt;hoisted.js&lt;/code&gt; file.&lt;/p&gt;



&lt;p&gt;There&amp;#8217;s likely some consideration needed for very large applications, because I&amp;#8217;d bet the compiled source wouldn&amp;#8217;t be ideal for projects of any real complexity. The first thing I&amp;#8217;d try is bundling web components into sub directories and using this glob import pattern in smaller chunks.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Live example&lt;/h3&gt;



&lt;p&gt;Once again, &lt;a href=&quot;https://stackblitz.com/edit/github-mdsq7h-9whase?file=src%2Fpages%2Findex.astro&quot;&gt;I made this a minimally reproducible Stackblitz of two native web components&lt;/a&gt; being used on a page after being auto imported in the parent layout.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Conclusion&lt;/h2&gt;



&lt;p&gt;That&amp;#8217;s where I&amp;#8217;m at as of November 2023, I&amp;#8217;m pretty happy with the auto-importing method noted in that third attempt above. But I am still very interested in hearing about other ways to do this, and especially about how it affects page performance. If you&amp;#8217;ve got other methods to make this work, I&amp;#8217;d love to see some examples!&lt;/p&gt;



&lt;p&gt;I&amp;#8217;ve already updated this post with the third example after some more fiddling, if I figure out or hear of any further updates I&amp;#8217;ll be sure to keep this post updated.&lt;/p&gt;



&lt;p&gt;I&amp;#8217;ve also completed the replatforming of &lt;a href=&quot;https://a11y-solutions.stevenwoodson.com/&quot;&gt;Accessibility Solutions&lt;/a&gt; from Nuxt to Astro, which was the impetus behind this deep dive into web components in Astro. If you&amp;#8217;re interested in reading more about that experience, check out &lt;a href=&quot;https://stevenwoodson.com/blog/replatforming-from-nuxtjs-2-to-astro/&quot;&gt;&lt;a href=&quot;https://stevenwoodson.com/blog/replatforming-from-nuxtjs-2-to-astro/&quot;&gt;Nuxt 2 to Astro 3 Replatforming – from Setup to Production&lt;/a&gt;&lt;/a&gt;.&lt;/p&gt;

    </content>
	</entry>
  
	<entry>
		<title>Fighting Back Against Toxic Productivity</title>
		<link href="https://stevenwoodson.com/blog/fighting-back-against-toxic-productivity"/>
		<published>2023-10-31T16:23:08.000Z</published>
		<updated>2023-11-25T13:34:32.000Z</updated>
		<id>https://stevenwoodson.com/blog/fighting-back-against-toxic-productivity</id>
    <summary>I have a sometimes unhealthy obsession with my todo list, here I share some intentional self awareness strategies I use to periodically recalibrate</summary>
		<content type="html">&lt;img src=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/10/Fighting-Back-Against-Toxic-Productivity.webp&quot; alt=&quot;&quot;&gt;
&lt;p&gt;I received some great feedback from a recent social media post (on &lt;a href=&quot;https://www.linkedin.com/feed/update/urn:li:activity:7123368776414240768/&quot;&gt;LinkedIn&lt;/a&gt; and &lt;a href=&quot;https://mastodon.online/@stevenwoodson/111302627916927130&quot;&gt;Mastodon&lt;/a&gt;) about my sometimes unhealthy obsession with my todo list, I&amp;#8217;m glad it resonated with so many people!&lt;/p&gt;



&lt;p&gt;It&amp;#8217;s been a topic on my mind all year, though the nature of my fixation has shifted over the past 6 months &amp;#8211; especially as I transitioned from a full time job into freelance &amp;#8211; the underlying basics remain the same.&lt;/p&gt;



&lt;p&gt;After diving into the subject a little deeper I came to find out about the term &lt;strong&gt;Toxic Productivity&lt;/strong&gt; which sums it up pretty well. There are other related terms like &amp;#8220;&lt;a href=&quot;https://thedecisionlab.com/biases/action-bias&quot; rel=&quot;nofollow&quot;&gt;action bias&lt;/a&gt;&amp;#8221; and &amp;#8220;&lt;a href=&quot;https://www.npr.org/sections/codeswitch/2020/04/03/826015780/when-the-hustle-isnt-enough&quot; rel=&quot;nofollow&quot;&gt;hustle culture&lt;/a&gt;&amp;#8220;, but the former makes it sound more positive and the latter seems more about external pressure.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;What is Toxic Productivity?&lt;/h2&gt;



&lt;p&gt;I want to focus more on strategies to combat toxic productivity in this post, but felt a quick definition would be useful to start with.&lt;/p&gt;



&lt;blockquote class=&quot;wp-block-quote is-layout-flow wp-block-quote-is-layout-flow&quot;&gt;
&lt;p&gt;Toxic productivity is the desire to be productive at all times and at all costs, pushing yourself to unhealthy extremes in order to accomplish more.&lt;/p&gt;
&lt;/blockquote&gt;



&lt;figure class=&quot;wp-block-image size-large&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1024&quot; height=&quot;576&quot; src=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/10/PXL_20230814_154210633-1024x576.webp&quot; alt=&quot;A neon blue sign hung up on a wall with a large blue and white hibiscus flower and leaf patterned wallpaper. The neon sign reads &amp;quot;HUSTLE&amp;quot; in all capital letters.&quot; class=&quot;wp-image-505&quot; srcset=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/10/PXL_20230814_154210633-1024x576.webp 1024w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/10/PXL_20230814_154210633-300x169.webp 300w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/10/PXL_20230814_154210633-768x432.webp 768w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/10/PXL_20230814_154210633-1536x864.webp 1536w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/10/PXL_20230814_154210633.webp 1600w&quot; sizes=&quot;auto, (max-width: 1024px) 100vw, 1024px&quot; /&gt;&lt;/figure&gt;



&lt;p&gt;I&amp;#8217;ve pulled together some articles that helped me learn more if you&amp;#8217;re interested in diving deeper too:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://www.psychologytoday.com/us/blog/leading-success/202201/when-doing-is-your-undoing-toxic-productivity&quot; rel=&quot;nofollow&quot;&gt;When Doing is Your Undoing: Toxic Productivity&lt;/a&gt;&lt;/li&gt;



&lt;li&gt;&lt;a href=&quot;https://universitytimes.ie/2022/02/the-psychology-of-toxic-productivity-and-how-its-impacting-students/&quot; rel=&quot;nofollow&quot;&gt;The Psychology of Toxic Productivity – and How It’s Impacting Students&lt;/a&gt;&lt;/li&gt;



&lt;li&gt;&lt;a href=&quot;https://www.npr.org/sections/codeswitch/2020/04/03/826015780/when-the-hustle-isnt-enough&quot; rel=&quot;nofollow&quot;&gt;When The &amp;#8216;Hustle&amp;#8217; Isn&amp;#8217;t Enough&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Strategies to Fight Back&lt;/h2&gt;



&lt;p&gt;Following are the most impactful things I&amp;#8217;ve tried, so much so that I&amp;#8217;ve utilized them all multiple times.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Recalibrate the importance of &amp;#8220;the List&amp;#8221;&lt;/h3&gt;



&lt;p&gt;This was the focus of the social post I mentioned and led to this blog post. Read the full post on &lt;a href=&quot;https://www.linkedin.com/feed/update/urn:li:activity:7123368776414240768/&quot;&gt;LinkedIn&lt;/a&gt; or &lt;a href=&quot;https://mastodon.online/@stevenwoodson/111302627916927130&quot;&gt;Mastodon&lt;/a&gt; for more.&lt;/p&gt;



&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;&lt;/p&gt;



&lt;p&gt;I like being organized with a list of tasks, and marking something off my list is a really effective motivator for me. But I tend to lose sight of the big picture if left to my own devices for too long.&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;Just because it wasn’t “on the list” doesn’t mean it’s not still something important to do.&lt;/li&gt;



&lt;li&gt;Just because it is “on the list” doesn’t mean you’re going to be ready and able to do it when you planned.&lt;/li&gt;
&lt;/ul&gt;



&lt;blockquote class=&quot;wp-block-quote is-layout-flow wp-block-quote-is-layout-flow&quot;&gt;
&lt;p&gt;I regularly remind myself that “the list” doesn&amp;#8217;t define me or my worth.&lt;/p&gt;
&lt;/blockquote&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Define your Core Values&lt;/h3&gt;



&lt;p&gt;By core values, I&amp;#8217;m referring to the rarely changing long term principles and beliefs that guide our choices. How we want to be perceived and remembered by others. &lt;/p&gt;



&lt;p&gt;I went through the exercise of defining them in &lt;a href=&quot;https://stevenwoodson.com/mission-statement/&quot;&gt;my Mission Statement&lt;/a&gt; that I completed &lt;a href=&quot;https://stevenwoodson.com/blog/soft-skills-productivity-and-goal-setting/#personal-mission-statement&quot;&gt;nearly 5 years ago&lt;/a&gt;, I still refer back to it periodically for a boost of motivation and to realign my goals.&lt;/p&gt;



&lt;blockquote class=&quot;wp-block-quote is-layout-flow wp-block-quote-is-layout-flow&quot;&gt;
&lt;p&gt;Knowing your core values helps clarify what&amp;#8217;s important now, what is essential, and what can wait.&lt;/p&gt;
&lt;/blockquote&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Set Realistic Goals&lt;/h3&gt;



&lt;p&gt;It&amp;#8217;s a great exercise to think big and set long term goals that stretch you personally or professionally, but those should continue to be broken down into short term goals, and then into tasks that are each individually achievable.&lt;/p&gt;



&lt;p&gt;These tasks should have realistic deadlines too, estimating is a difficult and imprecise practice. Give it your best guess, see how it goes, and recalibrate. For example, I _constantly_ underestimate how long content marketing and outreach efforts take so I&amp;#8217;ve continued to adjust my expectations.&lt;/p&gt;



&lt;blockquote class=&quot;wp-block-quote is-layout-flow wp-block-quote-is-layout-flow&quot;&gt;
&lt;p&gt;To quote Amy Hupe from her presentation &lt;a href=&quot;https://2023.stateofthebrowser.com/speaker/amy-hupe/&quot; rel=&quot;nofollow&quot;&gt;It all means nothing in the end&lt;/a&gt;, &amp;#8220;fuck moonshots&amp;#8221;.&lt;/p&gt;
&lt;/blockquote&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Set Boundaries&lt;/h3&gt;



&lt;p&gt;I wrote a bit about this in &lt;a href=&quot;https://stevenwoodson.com/blog/how-i-hacked-my-brain-to-let-myself-relax-in-the-evenings/&quot;&gt;&lt;/a&gt;&lt;a href=&quot;https://stevenwoodson.com/blog/how-i-hacked-my-brain-to-let-myself-relax-in-the-evenings/&quot;&gt;How I Hacked My Brain to Let Myself Relax in the Evenings&lt;/a&gt;, and happy to say I&amp;#8217;m still holding firm on some of the key points there:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;I get to bed and wake up at the same time every day, even weekends. That regular cadence helps with sleep quality more than I thought possible.&lt;/li&gt;



&lt;li&gt;I still have general time boxed sections of the day that I hold myself to no matter what. No more work after dinner unless there&amp;#8217;s a very good reason for it.&lt;/li&gt;
&lt;/ul&gt;



&lt;blockquote class=&quot;wp-block-quote is-layout-flow wp-block-quote-is-layout-flow&quot;&gt;
&lt;p&gt;Setting boundaries to get the sleep I need, reframing time away from work so I don&amp;#8217;t feel guilty about it, and having a predictable schedule all lead to more productivity in less time.&lt;/p&gt;
&lt;/blockquote&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Embrace Low Energy Days&lt;/h3&gt;



&lt;p&gt;As I get older, I&amp;#8217;ve noticed that some days I really can&amp;#8217;t force myself to power through like I used to. Maybe I should say &amp;#8220;not willing to&amp;#8221; rather than &amp;#8220;can&amp;#8217;t&amp;#8221; there, older Steve recognizes when something isn&amp;#8217;t the fire drill that younger Steve may have thought.&lt;/p&gt;



&lt;p&gt;Rather than fighting myself, over-caffeinating, and generally wasting time trying, I&amp;#8217;ve instead opted to take another path when this happens.&lt;/p&gt;



&lt;blockquote class=&quot;wp-block-quote is-layout-flow wp-block-quote-is-layout-flow&quot;&gt;
&lt;p&gt;I ask myself &amp;#8220;What can I do today to make tomorrow better?&amp;#8221; &lt;/p&gt;
&lt;/blockquote&gt;



&lt;p&gt;Focusing on tomorrow&amp;#8217;s productivity helps me to reframe and not think so critically about my lack of it today. Some days it means getting up and doing a bit of meal prep, going for a walk, taking a nap, cleaning up my todo list and low-level prepping. &lt;/p&gt;



&lt;p&gt;All of these things can make tomorrows productivity that little bit easier.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;The Common Thread&lt;/h2&gt;



&lt;p&gt;It may come as no surprise that the common thread through all the strategies mentioned above is self awareness. Forcing myself to think about my actions, acknowledge habits I should stop (or start), and embrace my limitations all require some level of self awareness.&lt;/p&gt;



&lt;p&gt;If you&amp;#8217;re feeling the pressures of toxic productivity, give some of these strategies a try. It&amp;#8217;s likely that not all of them will work for you, but maybe the act of trying will give you some ideas for your own situation.&lt;/p&gt;



&lt;p&gt;Any time and attention put towards figuring out core values, clarifying what feels out of alignment, and steps taken to improve is time well spent.&lt;/p&gt;

    </content>
	</entry>
  
	<entry>
		<title>State of the Browser 2023 Conference Recap</title>
		<link href="https://stevenwoodson.com/blog/state-of-the-browser-2023-conference-recap"/>
		<published>2023-10-18T02:34:43.000Z</published>
		<updated>2023-10-30T19:51:55.000Z</updated>
		<id>https://stevenwoodson.com/blog/state-of-the-browser-2023-conference-recap</id>
    <summary>A brief bulleted recap of State of the Browser 2023 from my perspective, including the decision to sponsor, and a bit about traveling to London to attend in person.</summary>
		<content type="html">&lt;img src=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/10/State-of-the-Browser-2023-Recap.png&quot; alt=&quot;&quot;&gt;
&lt;h2 class=&quot;wp-block-heading&quot;&gt;Preamble&lt;/h2&gt;



&lt;p&gt;I&amp;#8217;ve been working on this recap in bits and pieces since &lt;a href=&quot;https://2023.stateofthebrowser.com/&quot; rel=&quot;nofollow&quot;&gt;the State of the Browser conference&lt;/a&gt; in September. Now that it&amp;#8217;s already mid-October I&amp;#8217;m happy to finally be able to share my lightly edited notes from each of the speaker sessions.&lt;/p&gt;



&lt;p&gt;I also added a &amp;#8220;More Background&amp;#8221; section at the end that gets into more detail about how a guy from the Midwest ended up sponsoring and attending a conference in London.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;SotB Sessions&lt;/h2&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Michael R.&amp;nbsp;Lorek &amp;#8211; &amp;#8220;Web Standards: Does Anyone Actually Care?&amp;#8221;&lt;/h3&gt;



&lt;blockquote class=&quot;wp-block-quote is-layout-flow wp-block-quote-is-layout-flow&quot;&gt;
&lt;p&gt;Validate your code, test for accessibility, pen test it &amp;#8211; it all contributes towards the resilience of the code you release. &lt;strong&gt;Educate yourself and your peers on web standards&lt;/strong&gt;, it&amp;#8217;s important!&lt;/p&gt;
&lt;/blockquote&gt;



&lt;figure class=&quot;wp-block-image size-large&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1024&quot; height=&quot;640&quot; src=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/10/Michael-R.-Lorek-1-1024x640.png&quot; alt=&quot;Slide reads &amp;quot;Web Standards: Does Anyone Actually Care?&amp;quot; with Michael R. Lorek standing at the podium to the right.&quot; class=&quot;wp-image-492&quot; srcset=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/10/Michael-R.-Lorek-1-1024x640.png 1024w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/10/Michael-R.-Lorek-1-300x188.png 300w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/10/Michael-R.-Lorek-1-768x480.png 768w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/10/Michael-R.-Lorek-1-1536x960.png 1536w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/10/Michael-R.-Lorek-1-2048x1280.png 2048w&quot; sizes=&quot;auto, (max-width: 1024px) 100vw, 1024px&quot; /&gt;&lt;/figure&gt;



&lt;p&gt;&lt;a href=&quot;https://2023.stateofthebrowser.com/speaker/michael-lorek/&quot;&gt;Recording and Transcript of &amp;#8220;Web Standards: Does Anyone Actually Care?&amp;#8221;&lt;/a&gt;&lt;/p&gt;



&lt;h4 class=&quot;wp-block-heading&quot;&gt;My Session Notes&lt;/h4&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;Highlights of the evolution of the web, from introduction in 1991 to 1.13 billion (200 million active) websites on the internet today&lt;/li&gt;



&lt;li&gt;Recap of UX layers of a Web Page from 2006&lt;/li&gt;



&lt;li&gt;Definition of Web Standards from the W3C, standards are important. But now in 2023, do folks still care about web standards?
&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;Zero of the global top 100 used sites in 2022 contained valid HTML, two in 2021&lt;/li&gt;



&lt;li&gt;Three had valid CSS in 2021, 13 of 94 were valid CSS in 2022&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;



&lt;li&gt;The web is broken, why? What&amp;#8217;s the cause
&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;WYSIWYG tools, building sites without any code&lt;/li&gt;



&lt;li&gt;WordPress, themes are rarely validated&lt;/li&gt;



&lt;li&gt;Third Party Snippets, often risks of questionable code quality&lt;/li&gt;



&lt;li&gt;Design systems are mostly on JS frameworks&lt;/li&gt;



&lt;li&gt;Enterprise tech like No code, SaaS&lt;/li&gt;



&lt;li&gt;AI? Not ready but will it ever fix all that flawed code?&lt;/li&gt;



&lt;li&gt;Inadequate teaching of semantics and standards&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;



&lt;li&gt;Why do we need to validate, what are the benefits?
&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;SEO &amp;#8211; better crawlability, semantics are going to help&lt;/li&gt;



&lt;li&gt;Accessibility &amp;#8211; Improper formatting can lead to barriers, lack of semantics or misuse causes confusion&lt;/li&gt;



&lt;li&gt;Security &amp;#8211; Missing doctypes, unescaped characters, improper attribute quoting. All can lead to inconsistent rendering, opens up vulnerabilities, and manipulating attributes for XSS attacks&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;



&lt;li&gt;Details matter






&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;HTML charset needs to be prioritized in the first 1024 characters&lt;/li&gt;
&lt;/ul&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;Be aware of inconsistencies and semantic details, acronyms vs abbreviations for example&lt;/li&gt;



&lt;li&gt;UTC, Cities and Time Zones &amp;#8211; adding &amp;lt;time&amp;gt; to the code to detail the time zone&lt;/li&gt;



&lt;li&gt;Multiple languages mixed on one page&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;



&lt;li&gt;Code quality is imperative
&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;Ensure code is clean and readable, understand its backend and frontend performance,  accessibility, sustainability&lt;/li&gt;



&lt;li&gt;It all adds up!&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Chris Ferdinandi &amp;#8211; &amp;#8220;Speedy Dev Insights&amp;#8221;&lt;/h3&gt;



&lt;p&gt;&lt;a href=&quot;https://2023.stateofthebrowser.com/speaker/chris-ferdinandi/&quot; rel=&quot;nofollow&quot;&gt;Recordings and Transcript of &amp;#8220;Speedy Dev Insights&amp;#8221;&lt;/a&gt;&lt;/p&gt;



&lt;h4 class=&quot;wp-block-heading&quot;&gt;My Session Notes&lt;/h4&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;My First Web Component
&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;Creating a custom element requires at least one dash (single word elements are reserved)&lt;/li&gt;



&lt;li&gt;&lt;code&gt;customElements.define()&lt;/code&gt;&lt;/li&gt;



&lt;li&gt;&lt;code&gt;constructor&lt;/code&gt; function runs when a new instance is created&lt;/li&gt;



&lt;li&gt;additional functions can be created to manage callbacks and other actions like the &lt;code&gt;connectedCallback&lt;/code&gt; example&lt;/li&gt;



&lt;li&gt;Quick intro of a counter button, placed in the page 3 times to illustrate the same component being used multiple times and maintaining its own state&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;



&lt;li&gt;Two-way data binding with 15 lines of JavaScript
&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;Demo is two identical forms that we want to match values across them&lt;/li&gt;



&lt;li&gt;JS native API called &lt;code&gt;Proxy&lt;/code&gt; watches for changes and lets you take action in response&lt;/li&gt;



&lt;li&gt;&lt;code&gt;input&lt;/code&gt; event listener can trigger when changes are made to inputs, that in turn can trigger the change that &lt;code&gt;Proxy&lt;/code&gt; is watches for which updates the other input&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;



&lt;li&gt;13 web dev tricks
&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;strong&gt;Event Delegation&lt;/strong&gt; &amp;#8211; To catch events on multiple elements, attach an event listener to a parent and filter to only the ones you care about&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;Early Return pattern&lt;/strong&gt; &amp;#8211; nested &lt;code&gt;if&lt;/code&gt; statements are hard to read, can return &lt;code&gt;return;&lt;/code&gt; when the opposite is true instead&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;Multiple Selectors&lt;/strong&gt; &amp;#8211; You can pass a comma separated string to &lt;code&gt;querySelector&lt;/code&gt; and &lt;code&gt;querySelectorAll&lt;/code&gt;&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;Data Attribute selectors&lt;/strong&gt; &amp;#8211; cleaner code, but also can make event delegation easier by using the handler name to run the equivalent function&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;Object =&amp;gt; Query String Conversion &lt;/strong&gt;&amp;#8211; Use the &lt;code&gt;URLSearchParams&lt;/code&gt; method and run the &lt;code&gt;toString()&lt;/code&gt; method on it&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;Dedupe an Array&lt;/strong&gt; &amp;#8211; Utilize a &lt;code&gt;Set&lt;/code&gt; which can&amp;#8217;t have duplicate values, &lt;code&gt;Array.from(new Set(wizards));&lt;/code&gt;&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;Random ID generator &lt;/strong&gt;&amp;#8211; &lt;code&gt;crypto.randomUUID();&lt;/code&gt; &lt;/li&gt;



&lt;li&gt;&lt;strong&gt;True Type checking&lt;/strong&gt; &amp;#8211; &lt;code&gt;typeof&lt;/code&gt; is unreliable because it returns an object for several different types. Instead call the &lt;code&gt;Object.prototype.ToString&lt;/code&gt; method on the item instead. Returns object followed by the actual object type like &lt;code&gt;[object Array]&lt;/code&gt;&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;Numeric separators&lt;/strong&gt; &amp;#8211; In modern JS you can use underscores to break big numbers into smaller parts, commas will cause issues&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;Looping over objects&lt;/strong&gt; &amp;#8211; Rather than for/in, instead use a simple for/of loop and &lt;code&gt;Object.entries&lt;/code&gt;&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;Object Property shorthands&lt;/strong&gt; &amp;#8211; Variable names as the property and name will auto-assign&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;Simpler Boolean returns &lt;/strong&gt;&amp;#8211; Returning the conditional check if it&amp;#8217;s returning a boolean itself&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;GIF is pronounced &amp;#8216;jif&amp;#8217;&lt;/strong&gt; &amp;#8211; (personal note, this is also &lt;a href=&quot;https://youtu.be/CBtKxsuGvko?t=56&quot; rel=&quot;nofollow&quot;&gt;backed up by the creator of the format&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Amy Hupe &amp;#8211; &amp;#8220;It all means nothing in the end&amp;#8221;&lt;/h3&gt;



&lt;blockquote class=&quot;wp-block-quote is-layout-flow wp-block-quote-is-layout-flow&quot;&gt;
&lt;p&gt;We need to know where we&amp;#8217;re heading to, and we need to derive meaning from the journey and the destination. And the good news is that we get to decide that. &lt;strong&gt;It all means nothing in the end, but only if you let it.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;



&lt;figure class=&quot;wp-block-image size-large&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1024&quot; height=&quot;771&quot; src=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/10/Amy-Hupe-2-1024x771.jpg&quot; alt=&quot;Slide with the text &amp;quot;How do I know if I&#39;m doing well if I&#39;m not collecting corporate gold stars...?&amp;quot; with four rows of 12 gold star emojis each, directly below filling the rest of the slide. Amy standing at the podium to the right.&quot; class=&quot;wp-image-482&quot; srcset=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/10/Amy-Hupe-2-1024x771.jpg 1024w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/10/Amy-Hupe-2-300x226.jpg 300w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/10/Amy-Hupe-2-768x578.jpg 768w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/10/Amy-Hupe-2-1536x1157.jpg 1536w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/10/Amy-Hupe-2-2048x1542.jpg 2048w&quot; sizes=&quot;auto, (max-width: 1024px) 100vw, 1024px&quot; /&gt;&lt;/figure&gt;



&lt;p&gt;&lt;a href=&quot;https://2023.stateofthebrowser.com/speaker/amy-hupe/&quot; rel=&quot;nofollow&quot;&gt;Recording and Transcript of &amp;#8220;It all means nothing in the end&amp;#8221;&lt;/a&gt;&lt;/p&gt;



&lt;h4 class=&quot;wp-block-heading&quot;&gt;My Session Notes&lt;/h4&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;Had historically derived meaning from her work, very career driven&lt;/li&gt;



&lt;li&gt;Sudden loss of motivation early this year, leading to an existential crisis.&lt;/li&gt;



&lt;li&gt;Time away to travel and recharge, came back still feeling uninspired&lt;/li&gt;



&lt;li&gt;Turned to journaling and deep introspection, realized a loss of purpose in her work&lt;/li&gt;



&lt;li&gt;Emphasized the importance of connecting one&amp;#8217;s work to a core perspective
&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;the intersection of skills, experiences, and values. &lt;/li&gt;



&lt;li&gt;It&amp;#8217;s unique to you.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;



&lt;li&gt;Setting meaningful goals is crucial and that goals should:
&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;align with my core perspective&lt;/li&gt;



&lt;li&gt;be something I care about&lt;/li&gt;



&lt;li&gt;be achievable (fuck moonshots!)&lt;/li&gt;



&lt;li&gt;be measurable (know if I&amp;#8217;m making progress)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;



&lt;li&gt;Achieving goals and tracking progress are important for avoiding burnout. Signs of burnout:
&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;Emotional exhaustion &amp;#8211; the fatigue that comes from caring too much, for too long&lt;/li&gt;



&lt;li&gt;Depersonalization &amp;#8211; depletion of empathy, caring, and compassion&lt;/li&gt;



&lt;li&gt;Decreased sense of accomplishment &amp;#8211; sense of futility, feeling that nothing you do makes a difference&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;



&lt;li&gt;Disconnect from external validation, we&amp;#8217;ll always need some but it should not be the sole source of self-worth. Examples:
&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;pay raises&lt;/li&gt;



&lt;li&gt;promotions&lt;/li&gt;



&lt;li&gt;performance reviews&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;



&lt;li&gt;Cultivating meaning involves focusing on fulfilling my purpose, making progress against my goals, and enjoying the journey.&lt;/li&gt;



&lt;li&gt;While the ultimate outcome might be uncertain, the journey and how one gets there can bring meaning to one&amp;#8217;s work and life.&lt;/li&gt;
&lt;/ul&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Ian Lloyd &amp;#8211; &amp;#8220;Accessibility Is Easy… Except for When It Isn’t&amp;#8221;&lt;/h3&gt;



&lt;blockquote class=&quot;wp-block-quote is-layout-flow wp-block-quote-is-layout-flow&quot;&gt;
&lt;p&gt;Leverage native HTML for built in accessibility affordances, use ARIA and tried &amp;amp; tested patterns for common use cases, get creative with complex UIs but be sure to test thoroughly.&lt;/p&gt;
&lt;/blockquote&gt;



&lt;figure class=&quot;wp-block-image size-large&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1024&quot; height=&quot;771&quot; src=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/10/Ian-Lloyd-1024x771.jpg&quot; alt=&quot;Presentation slide titled &amp;quot;Fartboard - an accessible farting soundboard&amp;quot; with four accessible buttons &amp;quot;Fart 1&amp;quot; thru 4 with descriptions like &amp;quot;abrupt, punctual&amp;quot;, &amp;quot;raspy&amp;quot;, and &amp;quot;long, raspy, multi-tonal&amp;quot;. Ian Lloyd standing at the podium to the right&quot; class=&quot;wp-image-483&quot; srcset=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/10/Ian-Lloyd-1024x771.jpg 1024w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/10/Ian-Lloyd-300x226.jpg 300w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/10/Ian-Lloyd-768x578.jpg 768w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/10/Ian-Lloyd-1536x1157.jpg 1536w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/10/Ian-Lloyd-2048x1542.jpg 2048w&quot; sizes=&quot;auto, (max-width: 1024px) 100vw, 1024px&quot; /&gt;&lt;/figure&gt;



&lt;p&gt;&lt;a href=&quot;https://2023.stateofthebrowser.com/speaker/ian-lloyd/&quot; rel=&quot;nofollow&quot;&gt;Recording and Transcript of &amp;#8220;Accessibility Is Easy… Except for When It Isn’t&amp;#8221;&lt;/a&gt;&lt;/p&gt;



&lt;h4 class=&quot;wp-block-heading&quot;&gt;My Session Notes&lt;/h4&gt;



&lt;p&gt;Acronyms referred in the talk&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;strong&gt;WCAG&lt;/strong&gt; &amp;#8211; Web Content Accessibility Guidelines&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;SC &lt;/strong&gt;&amp;#8211; Success Criterion&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;ARIA&lt;/strong&gt; &amp;#8211; Accessible Rich Internet Applications&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;a11y&lt;/strong&gt; &amp;#8211; Accessibility&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;AT&lt;/strong&gt; &amp;#8211; Assistive Technology (e.g. screen readers)&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;Accessibility low hanging fruit&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt;, if you need to roll your own you&amp;#8217;ll need to make sure they&amp;#8217;re keyboard operable, with a button role, styled, and state maintenance&lt;/li&gt;



&lt;li&gt;&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;Hall of Shame&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;the linkput &amp;#8211; an input element of type button wrapped with a link&lt;/li&gt;



&lt;li&gt;the lutton &amp;#8211; a link with a role of button&lt;/li&gt;



&lt;li&gt;disclosure/checkbox/button/link/heading combo &amp;#8211; div&lt;/li&gt;



&lt;li&gt;First rule of ARIA &amp;#8211; don&amp;#8217;t use ARIA if a native HTML element or attribute exists with the semantics and behavior you need.&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;Tricky examples&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;Table row selection, shows the detail of selected rows in the right panel&lt;/li&gt;



&lt;li&gt;Multi-select menu &amp;#8211; choosing between &lt;code&gt;&amp;lt;select&amp;gt;&lt;/code&gt;, checkboxes, checkboxes in a disclosure&lt;/li&gt;



&lt;li&gt;Theater seat chooser &amp;#8211; be sure to convey detail, if seat button is dimmed explain why, make navigation easier with keyboard (arrow keys), group sections of the seats together for quicker traversing&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;Recap&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;Native HTML is preferred&lt;/li&gt;



&lt;li&gt;Use ARIA and tried &amp;amp; tested patterns where possible&lt;/li&gt;



&lt;li&gt;For complex UIs, start with as much native HTML and tested patterns first and then get creative. Test with as much Assistive Technology and as many users as you can&lt;/li&gt;
&lt;/ul&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Kilian Valkhov &amp;#8211; &amp;#8220;Stop using JS for that: Moving features to CSS and HTML&amp;#8221;&lt;/h3&gt;



&lt;blockquote class=&quot;wp-block-quote is-layout-flow wp-block-quote-is-layout-flow&quot;&gt;
&lt;p&gt;Just because you know something needs a specific browser hack, that may not be true anymore. You can make better websites if you check those assumptions every now and then.&lt;/p&gt;
&lt;/blockquote&gt;



&lt;figure class=&quot;wp-block-image size-large&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1024&quot; height=&quot;640&quot; src=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/10/Screen-Shot-2023-10-17-at-9.55.37-PM-1024x640.png&quot; alt=&quot;Slide reads &amp;quot;Choose the least powerful language suitable for a given purpose&amp;quot;. Kilian Valkhov is standing at the podium to the right.&quot; class=&quot;wp-image-497&quot; srcset=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/10/Screen-Shot-2023-10-17-at-9.55.37-PM-1024x640.png 1024w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/10/Screen-Shot-2023-10-17-at-9.55.37-PM-300x188.png 300w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/10/Screen-Shot-2023-10-17-at-9.55.37-PM-768x480.png 768w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/10/Screen-Shot-2023-10-17-at-9.55.37-PM-1536x960.png 1536w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/10/Screen-Shot-2023-10-17-at-9.55.37-PM-2048x1280.png 2048w&quot; sizes=&quot;auto, (max-width: 1024px) 100vw, 1024px&quot; /&gt;&lt;/figure&gt;



&lt;p&gt;&lt;a href=&quot;https://2023.stateofthebrowser.com/speaker/kilian-valkhof/&quot; rel=&quot;nofollow&quot;&gt;Recording and Transcript of &amp;#8220;Stop using JS for that: Moving features to CSS and HTML&amp;#8221;&lt;/a&gt;&lt;/p&gt;



&lt;h4 class=&quot;wp-block-heading&quot;&gt;My Session Notes&lt;/h4&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;The rule of least power suggests choosing the least powerful language for a given purpose in web development. Helps to maintain accessibility and performance.
&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;HTML is preferred over CSS&lt;/li&gt;



&lt;li&gt;CSS is preferred over JavaScript&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;



&lt;li&gt;Old techniques will still work, but just because it used to require JavaScript doesn&amp;#8217;t mean that&amp;#8217;s still the case.
&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;Several web features and interactions that traditionally required JavaScript can now be achieved with HTML and CSS.&lt;/li&gt;



&lt;li&gt;It&amp;#8217;s important to question assumptions and try new web development techniques.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;



&lt;li&gt;Examples
&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;custom toggles &amp;#8211; can be achieved with CSS using pseudo elements &lt;code&gt;:before&lt;/code&gt; and &lt;code&gt;:checked&lt;/code&gt; &lt;/li&gt;



&lt;li&gt;&lt;strong&gt;datalists&lt;/strong&gt; &amp;#8211; HTML &lt;code&gt;&amp;lt;datalist&amp;gt;&lt;/code&gt; auto suggest using the &lt;code&gt;&amp;lt;option&amp;gt;&lt;/code&gt; items inside&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;color pickers&lt;/strong&gt; &amp;#8211; &lt;code&gt;&amp;lt;input type=&quot;color&quot;&amp;gt;&lt;/code&gt; and it can pick colors from anywhere on the screen (even outside the browser)&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;smooth scrolling&lt;/strong&gt; &amp;#8211; &lt;code&gt;scroll-behavior: smooth;&lt;/code&gt; CSS property on &lt;code&gt;html&lt;/code&gt;&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;scroll margins&lt;/strong&gt; &amp;#8211; CSS property that only applies while scrolling &lt;code&gt;scroll-margin-top&lt;/code&gt;&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;scroll target&lt;/strong&gt; &amp;#8211; &lt;code&gt;:target&lt;/code&gt; property will let you style the scroll target&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;fixed (sticky) elements&lt;/strong&gt; &amp;#8211; CSS &lt;code&gt;position: sticky&lt;/code&gt; will lock elements to the top as you scroll&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;scroll snapping&lt;/strong&gt; &amp;#8211; useful for carousels or sliders, &lt;code&gt;scroll-snap-type&lt;/code&gt; on the parent and &lt;code&gt;scroll-snap-align&lt;/code&gt; on the child items&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;accordions&lt;/strong&gt; &amp;#8211; &lt;code&gt;&amp;lt;details&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;summary&amp;gt;&lt;/code&gt; can do essentially the same thing, can also use &lt;code&gt;:marker&lt;/code&gt; to have a different marker for when the details element is open or closed.&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;modals&lt;/strong&gt; &amp;#8211; &lt;code&gt;&amp;lt;dialog&amp;gt;&lt;/code&gt; gets us most of the way there, still needs a bit of JavaScript&lt;/li&gt;



&lt;li&gt;container queries! Not in the presentation, wanted to make note of them&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;



&lt;li&gt;&lt;/li&gt;
&lt;/ul&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Diego Gonzalez &amp;#8211; &amp;#8220;THE BLUE PWANET&amp;#8221;&lt;/h3&gt;



&lt;blockquote class=&quot;wp-block-quote is-layout-flow wp-block-quote-is-layout-flow&quot;&gt;
&lt;p&gt;The evolution of Progressive Web Apps (PWAs), their core technologies, deep integration with the operating system, Project Fugu for enhancing web capabilities, and the potential future of the Web Install API&lt;/p&gt;
&lt;/blockquote&gt;



&lt;figure class=&quot;wp-block-image size-large&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1024&quot; height=&quot;771&quot; src=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/10/Diego-Gonzalez-1-1024x771.jpg&quot; alt=&quot;Slide with &amp;quot;Every PWA is a web app but not every web app is a PWA&amp;quot;. Diego standing at the podium to the right of the screen&quot; class=&quot;wp-image-485&quot; srcset=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/10/Diego-Gonzalez-1-1024x771.jpg 1024w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/10/Diego-Gonzalez-1-300x226.jpg 300w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/10/Diego-Gonzalez-1-768x578.jpg 768w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/10/Diego-Gonzalez-1-1536x1157.jpg 1536w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/10/Diego-Gonzalez-1-2048x1542.jpg 2048w&quot; sizes=&quot;auto, (max-width: 1024px) 100vw, 1024px&quot; /&gt;&lt;/figure&gt;



&lt;p&gt;&lt;a href=&quot;https://2023.stateofthebrowser.com/speaker/diego-gonzalez/&quot; rel=&quot;nofollow&quot;&gt;Recording and Transcript of &amp;#8220;THE BLUE PWANET&amp;#8221;&lt;/a&gt;&lt;/p&gt;



&lt;h4 class=&quot;wp-block-heading&quot;&gt;My Session Notes&lt;/h4&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;Progressive Web Apps (PWAs) &amp;#8211; Yes they&amp;#8217;re still a thing, yes they still exist, and are stronger than ever.&lt;/li&gt;



&lt;li&gt;Evolution of web applications
&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;starting with Ajax and the ability to change web page elements without reloading the entire page.&lt;/li&gt;



&lt;li&gt;Link to homescreen, just a link with no other functionality&lt;/li&gt;



&lt;li&gt;PWAs&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;



&lt;li&gt;PWAs have three fundamentals
&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;HTTPS&lt;/li&gt;



&lt;li&gt;Manifest file&lt;/li&gt;



&lt;li&gt;Service Worker&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;



&lt;li&gt;Project Fugu, a joint effort between various companies to bring powerful APIs to the web platform
&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;Importance of deep integration into the operating system, more advanced applications and no longer a &amp;#8220;lite&amp;#8221; version&lt;/li&gt;



&lt;li&gt;new features for web apps such as access to clipboard, window controls, file handling, shortcuts, and web share targets.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;



&lt;li&gt;Every PWA is a web app, but not every web app is a PWA&lt;/li&gt;



&lt;li&gt;Coming up short on getting developers on board with building PWAs, using Service Workers and Manifest files, and understanding the benefits
&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;Calls into question if install-ability criteria should change, Chrome already dropped the service worker requirement&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;



&lt;li&gt;Web Install API, a new idea
&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;allow websites to install other websites or web applications without proprietary workarounds&lt;/li&gt;



&lt;li&gt;enable scenarios of online web stores&lt;/li&gt;



&lt;li&gt;install same ad cross-domain apps&lt;/li&gt;



&lt;li&gt;Challenges &amp;#8211; Preserve privacy of users, avoid install prompt spam&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;



&lt;li&gt;Expansion of PWAs
&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;widget &amp;#8211; companion apps using adaptive card templates&lt;/li&gt;



&lt;li&gt;sidebars &amp;#8211; introduces new display override values, can be detected in CSS &amp;amp; JS too&lt;/li&gt;



&lt;li&gt;these initiatives are Edge-specific, seeking interest from other browsers&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;



&lt;li&gt;mentions the evolution of apps and the appearance of new app types packaged in web bundles&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Harry Roberts &amp;#8211; &amp;#8220;Cache Rules Everything&amp;#8221;&lt;/h3&gt;



&lt;blockquote class=&quot;wp-block-quote is-layout-flow wp-block-quote-is-layout-flow&quot;&gt;
&lt;p&gt;We only have two jobs; we only need two tags &amp;#8211; &lt;code&gt;cache-control&lt;/code&gt; and &lt;code&gt;etag&lt;/code&gt;. Consider how fresh a file needs to be and set these headers accordingly, and don&amp;#8217;t revalidate hashed files.&lt;/p&gt;
&lt;/blockquote&gt;



&lt;figure class=&quot;wp-block-image size-large&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1024&quot; height=&quot;771&quot; src=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/10/Harry-Roberts-1024x771.jpg&quot; alt=&quot;Slide in big bold white text on red background &amp;quot;cache rules everything&amp;quot;. Harry Roberts standing at the podium to the right.&quot; class=&quot;wp-image-486&quot; srcset=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/10/Harry-Roberts-1024x771.jpg 1024w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/10/Harry-Roberts-300x226.jpg 300w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/10/Harry-Roberts-768x578.jpg 768w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/10/Harry-Roberts-1536x1157.jpg 1536w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/10/Harry-Roberts-2048x1542.jpg 2048w&quot; sizes=&quot;auto, (max-width: 1024px) 100vw, 1024px&quot; /&gt;&lt;/figure&gt;



&lt;p&gt;&lt;a href=&quot;https://2023.stateofthebrowser.com/speaker/harry-roberts/&quot; rel=&quot;nofollow&quot;&gt;Recording and Transcript of &amp;#8220;Cache Rules Everything&amp;#8221;&lt;/a&gt;&lt;/p&gt;



&lt;h4 class=&quot;wp-block-heading&quot;&gt;My Session Notes&lt;/h4&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;The best request is the one that&amp;#8217;s never made&lt;/li&gt;



&lt;li&gt;Key Concepts
&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;strong&gt;Cache&lt;/strong&gt; &amp;#8211; how long can I reuse this file without checking for updates?&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;Revalidation&lt;/strong&gt; &amp;#8211; How do I check that a file has changed after my cache time limit is up?&lt;/li&gt;



&lt;li&gt;Cache stops the client from speaking to the server so revaldation shouldn&amp;#8217;t happen while cached&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;Fresh&lt;/strong&gt; &amp;#8211; file is in the cache within it&amp;#8217;s expiration date&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;Stale&lt;/strong&gt; &amp;#8211; file is in the cache but has passed it&amp;#8217;s expiration date&lt;/li&gt;



&lt;li&gt;Fresh does not mean up to date&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;Request&lt;/strong&gt; &amp;#8211; Client requests a response from the server&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;Response&lt;/strong&gt; &amp;#8211; Server responds to the request, with a status code like 200 (success) or 304 (renewal of cache)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;



&lt;li&gt;Headers we don&amp;#8217;t need
&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;strong&gt;pragma&lt;/strong&gt; &amp;#8211; &lt;code&gt;pragma: no-cache&lt;/code&gt;, not meant to be a response header, just delete immediately&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;expires&lt;/strong&gt; &amp;#8211; &lt;code&gt;expires: wed, 13 sep 2023 08:49:31 gmt&lt;/code&gt;, not terrible but expires at an absolute time and fails if a user has changed their system time. Cache-control is better&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;last-modified&lt;/strong&gt; &amp;#8211; &lt;code&gt;last-modified: wed, 13 sep 2023 08:49:31 gmt&lt;/code&gt;, a revalidation header that&amp;#8217;s not harmful but etag is better&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;



&lt;li&gt;Headers we do want
&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;strong&gt;cache-control&lt;/strong&gt; &amp;#8211; &lt;code&gt;cache-control: private, max-age=300, must-revalidate&lt;/code&gt;, a caching header, rules and conditions for caching a response
&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;code&gt;max-age&lt;/code&gt; &amp;#8211; how long, in seconds&lt;/li&gt;



&lt;li&gt;&lt;code&gt;no-store&lt;/code&gt; &amp;#8211; don&amp;#8217;t store this file&lt;/li&gt;



&lt;li&gt;&lt;code&gt;no-cache&lt;/code&gt; &amp;#8211; forbids the browser from going straight to the cache, will check with the server first before releasing a cached file to the browser&lt;/li&gt;



&lt;li&gt;&lt;code&gt;public&lt;/code&gt; &amp;#8211; max-age assumes public, exposes cacheability on a CDN&lt;/li&gt;



&lt;li&gt;&lt;code&gt;private&lt;/code&gt; &amp;#8211; intended for personal use, don&amp;#8217;t reuse or store in CDN&lt;/li&gt;



&lt;li&gt;&lt;code&gt;must-revalidate&lt;/code&gt; &amp;#8211; caches are permitted to serve stale content (e.g. when offline) unless explicitly told otherwise with this directive&lt;/li&gt;



&lt;li&gt;&lt;code&gt;stale-while-revalidate&lt;/code&gt; &amp;#8211; revalidation is synchronous, this makes it async while revalidation is pending&lt;/li&gt;



&lt;li&gt;&lt;code&gt;s-maxage&lt;/code&gt; &amp;#8211; shared max age for CDNs, separate from browsers. Can flush CDN cache on build, so you can afford to make these higher&lt;/li&gt;



&lt;li&gt;&lt;code&gt;immutable&lt;/code&gt; &amp;#8211; contract with the browser that &amp;#8220;this file will never changed&amp;#8221;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;etag&lt;/strong&gt; &amp;#8211; weak etags only hashes the file name, strong etags hash the contents of the file and are preferred. This is a revalidation header, slightly more reliable than &lt;code&gt;last-modified&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;



&lt;li&gt;Can we cache this file?
&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;yes &amp;#8211; &lt;code&gt;cache-control: max-age=3600&lt;/code&gt;&lt;/li&gt;



&lt;li&gt;no &amp;#8211; &lt;code&gt;cache-control: no-store&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;



&lt;li&gt;Does this need to always be up-to-date?
&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;yes &amp;#8211; &lt;code&gt;cache-control: no-cache&lt;/code&gt;&lt;/li&gt;



&lt;li&gt;no &amp;#8211; &lt;code&gt;cache-control: max-age=3600&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;



&lt;li&gt;Can this response be shared?
&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;yes &amp;#8211; &lt;code&gt;cache-control: public, &lt;code&gt;max-age=3600&lt;/code&gt;&lt;/code&gt;&lt;/li&gt;



&lt;li&gt;no &amp;#8211; &lt;code&gt;cache-control: private&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;



&lt;li&gt;Can we reuse this response even if it&amp;#8217;s stale?
&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;yes &amp;#8211; &lt;code&gt;cache-control: &lt;code&gt;max-age=3600&lt;/code&gt;&lt;/code&gt;&lt;/li&gt;



&lt;li&gt;no &amp;#8211; &lt;code&gt;cache-control: &lt;code&gt;&lt;code&gt;max-age=3600&lt;/code&gt;&lt;/code&gt;, must-revalidate&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;



&lt;li&gt;Can we tolerate a slightly out of date response while performing revalidation?
&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;yes &amp;#8211; &lt;code&gt;cache-control: &lt;code&gt;max-age=3600&lt;/code&gt;, stale-while-revalidate=600&lt;/code&gt;&lt;/li&gt;



&lt;li&gt;no &amp;#8211; &lt;code&gt;cache-control: &lt;code&gt;&lt;code&gt;max-age=360&lt;/code&gt;&lt;/code&gt;0&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;



&lt;li&gt;Do we need to configure CDNs and browsers differently?
&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;yes &amp;#8211; &lt;code&gt;cache-control: max-age=3600, s-maxage=86400&lt;/code&gt;&lt;/li&gt;



&lt;li&gt;no &amp;#8211; &lt;code&gt;cache-control: max-age=3600&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;



&lt;li&gt;Is the file hashed?
&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;yes &amp;#8211; &lt;code&gt;cache-control: &lt;code&gt;max-age=2147483648&lt;/code&gt;, immutable&lt;/code&gt;&lt;/li&gt;



&lt;li&gt;no &amp;#8211; &lt;code&gt;cache-control: &lt;code&gt;&lt;code&gt;max-age=360&lt;/code&gt;&lt;/code&gt;0&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;



&lt;li&gt;Prefer etag, so we don&amp;#8217;t fall into the trap of last modified forcing revalidating &amp;#8211; especially with static site generators&lt;/li&gt;



&lt;li&gt;Don&amp;#8217;t revalidate hashed files, because those are always going to change with a new build.&lt;/li&gt;



&lt;li&gt;We only have two jobs; we only need two tags &amp;#8211; &lt;code&gt;cache-control&lt;/code&gt; and &lt;code&gt;etag&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Ana Rodrigues &amp;#8211; &amp;#8220;Exploring the Potential of the Web Speech API in Karaoke&amp;#8221;&lt;/h3&gt;



&lt;blockquote class=&quot;wp-block-quote is-layout-flow wp-block-quote-is-layout-flow&quot;&gt;
&lt;p&gt;Working example of a karaoke app using vanilla HTML, CSS, and JavaScript using the Web Speech API. You can learn by building &amp;#8220;useless&amp;#8221; things. Side projects don&amp;#8217;t need to be monetized in order to be valid.&lt;/p&gt;
&lt;/blockquote&gt;



&lt;figure class=&quot;wp-block-image size-large&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1024&quot; height=&quot;771&quot; src=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/10/Ana-Rodrigues-2-1024x771.jpg&quot; alt=&quot;Realtime demo of the in browser karaoke, telling you which words you got correct. Twinkle twinkle little star lyrics on screen, Ana standing at the podium to the right&quot; class=&quot;wp-image-491&quot; srcset=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/10/Ana-Rodrigues-2-1024x771.jpg 1024w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/10/Ana-Rodrigues-2-300x226.jpg 300w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/10/Ana-Rodrigues-2-768x578.jpg 768w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/10/Ana-Rodrigues-2-1536x1157.jpg 1536w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/10/Ana-Rodrigues-2-2048x1542.jpg 2048w&quot; sizes=&quot;auto, (max-width: 1024px) 100vw, 1024px&quot; /&gt;&lt;/figure&gt;



&lt;p&gt;&lt;a href=&quot;https://2023.stateofthebrowser.com/speaker/ana-rodrigues/&quot; rel=&quot;nofollow&quot;&gt;Recording and Transcript of &amp;#8220;Exploring the Potential of the Web Speech API in Karaoke&amp;#8221;&lt;/a&gt;&lt;/p&gt;



&lt;h4 class=&quot;wp-block-heading&quot;&gt;My Session Notes&lt;/h4&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;Exploring the potential of the Web Speech API to gamify karaoke, inspired by her love for the Rasmus and the desire to sing their songs (other than &amp;#8220;in the shadows&amp;#8221;)&lt;/li&gt;



&lt;li&gt;Using HTML, CSS, and vanilla JavaScript &amp;#8211; avoiding libraries &amp;#8211; to demonstrate browser native capabilities&lt;/li&gt;



&lt;li&gt;Web Speech API &amp;#8211; split into two, speech recognition and speech synthesis&lt;/li&gt;



&lt;li&gt;Web Speech API isn&amp;#8217;t perfect and has certain quirks
&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;not supporting continuous dictation, so there&amp;#8217;s JS that forces it to start itself back over again when it stops&lt;/li&gt;



&lt;li&gt;needing HTTPS to send data&lt;/li&gt;



&lt;li&gt;doesn&amp;#8217;t work offline&lt;/li&gt;



&lt;li&gt;not supported in Firefox&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;



&lt;li&gt;Demo of the karaoke interface, where the speech recognition begins with the song and transcribes her singing, trying to match the lyrics with the input and highlighting correctly matched portions in green and unmatched in red&lt;/li&gt;



&lt;li&gt;Some matching challenges when singing vs speaking, with demos of both ways&lt;/li&gt;



&lt;li&gt;Concludes by encouraging side projects and creative exploration as valid learning experiences in web development.&lt;/li&gt;



&lt;li&gt;You can learn by building &amp;#8220;useless&amp;#8221; things. Side projects don&amp;#8217;t need to be monetized in order to be valid. They don&amp;#8217;t need to become NPM packages or open source projects.&lt;/li&gt;
&lt;/ul&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Heydon Pickering &amp;#8211; &amp;#8220;What are Accessibility Overlays?&amp;#8221;&lt;/h3&gt;



&lt;p&gt;The conference was ended with a world premiere of &amp;#8220;What are Accessibility Overlays?&amp;#8221; by Heydon, a masterpiece that blends useful information and humor in a way only Heydon can. &lt;a href=&quot;https://briefs.video/videos/what-are-accessibility-overlays/&quot; rel=&quot;nofollow&quot;&gt;Check it out here&lt;/a&gt;.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Other Recaps&lt;/h2&gt;



&lt;p&gt;Here are a few links from others regarding SotB2023, I&amp;#8217;ll try to keep this updated as I find more. Send me a link if you know of one I&amp;#8217;m missing!&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://serieseight.com/journal/sotb23&quot; rel=&quot;nofollow&quot;&gt;What we learned at State of the Browser 2023&lt;/a&gt; by Alistair Shepherd&lt;/li&gt;



&lt;li&gt;&lt;a href=&quot;https://gomakethings.com/it-all-means-nothing-in-the-end/&quot; rel=&quot;nofollow&quot;&gt;It all means nothing in the end&lt;/a&gt; by Chris Ferdinandi&lt;/li&gt;
&lt;/ul&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;More Background&lt;/h2&gt;



&lt;p&gt;I&amp;#8217;ve shared bits of this on social and in conversations, figured it&amp;#8217;d be worthwhile to also share here to complete the picture of how a random guy from the Midwest in the States would find himself sponsoring and attending an event in London.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;How it Started&lt;/h3&gt;



&lt;p&gt;For me, it all started back in April as initial details about State of the Browser 2023 were being released. &lt;/p&gt;



&lt;p&gt;Even though the conference had been going since 2011, I hadn&amp;#8217;t personally been aware of SotB until late in 2022 as recordings from that year&amp;#8217;s conference were making the rounds. I greatly enjoyed those recordings and really liked the focus on web standards, accessibility, performance, and sustainability.&lt;/p&gt;



&lt;p&gt;When I heard about the call for sponsors in April for the 2023 conference, I had a feeling there&amp;#8217;d be a significant overlap between folks interested in attending and those with potential interest in &lt;a href=&quot;https://beinclusive.app/&quot;&gt;Be Inclusive&lt;/a&gt;. So I decided to go ahead and sponsor not realizing at the time that sponsorship came with an in person ticket to attend as well.&lt;/p&gt;



&lt;figure class=&quot;wp-block-image size-large&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1024&quot; height=&quot;771&quot; src=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/10/SotB2023-sponsor-1024x771.jpg&quot; alt=&quot;At the start of the conference, a slide that shows the logos of all sponsors is displayed as Dave Letorey details and thanks each of them. Be Inclusive is top center under the &amp;quot;Small Sponsors&amp;quot; category.&quot; class=&quot;wp-image-489&quot; srcset=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/10/SotB2023-sponsor-1024x771.jpg 1024w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/10/SotB2023-sponsor-300x226.jpg 300w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/10/SotB2023-sponsor-768x578.jpg 768w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/10/SotB2023-sponsor-1536x1157.jpg 1536w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/10/SotB2023-sponsor-2048x1542.jpg 2048w&quot; sizes=&quot;auto, (max-width: 1024px) 100vw, 1024px&quot; /&gt;&lt;/figure&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;The Snowball&lt;/h3&gt;



&lt;p&gt;My wife and I had never been outside of North America before, and hadbeen talking about visiting England first. We even looked up some flights and rates but we found it to be too expensive (this was around mid 2022 at the height of the &amp;#8220;revenge travel&amp;#8221; boom) so we opted to wait it out.&lt;/p&gt;



&lt;p&gt;This snowball of a sponsored in person ticket renewed our interest, and in May of 2023 we started researching again. To our surprise, flights and rates had improved enough to make this a viable option. So we got to planning, where should we stay? How long?&lt;/p&gt;



&lt;p&gt;That snowball grew until we had booked a vacation spanning nearly two weeks and four destinations. The SotB Conference would be the first full day and once that was done, it&amp;#8217;s vacation time!&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Final Prep&lt;/h3&gt;



&lt;p&gt;Now that I had quite a bit riding on this one decision, and I was about to take an 8 hour flight to be there in person, my next thought was &amp;#8220;oh no, how do I force myself to get social and talk to people?&amp;#8221;&lt;/p&gt;



&lt;p&gt;I ended up deciding to rely on an ice breaker in the form of individually customized business cards. They were something visual, and afforded me a way to walk up to someone and say &amp;#8220;Hey I&amp;#8217;m Steve, want a one of a kind business card?&amp;#8221;&lt;/p&gt;



&lt;figure class=&quot;wp-block-image size-large&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1024&quot; height=&quot;771&quot; src=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/09/PXL_20230909_123844846-1024x771.jpg&quot; alt=&quot;&quot; class=&quot;wp-image-470&quot; srcset=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/09/PXL_20230909_123844846-1024x771.jpg 1024w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/09/PXL_20230909_123844846-300x226.jpg 300w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/09/PXL_20230909_123844846-768x578.jpg 768w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/09/PXL_20230909_123844846-1536x1157.jpg 1536w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/09/PXL_20230909_123844846-2048x1542.jpg 2048w&quot; sizes=&quot;auto, (max-width: 1024px) 100vw, 1024px&quot; /&gt;&lt;/figure&gt;



&lt;p&gt;Happy to say I was able to give away half of the 50 unique cards I had printed! If you&amp;#8217;d like to read more about the process of making them, check out my &lt;a href=&quot;https://stevenwoodson.com/blog/one-of-a-kind-business-cards/&quot;&gt;One of a Kind Business Cards&lt;/a&gt; blog.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Aftermath&lt;/h3&gt;



&lt;p&gt;It was a lot of time and effort to prepare, but this conference and the vacation that followed it were truly highlights of the year for me. I had a blast learning from these wonderful speakers, the conversations during breaks and at the after party, and the ideas that re-watching and cleaning up my notes have sparked.&lt;/p&gt;



&lt;p&gt;Every time I come back to these speaker sessions I find myself digging into details, finding tangents, reading more, and experimenting. Huge kudos to every single one of the speakers, I&amp;#8217;m sure I&amp;#8217;m not the only one they&amp;#8217;ve inspired to give more thought and experimentation towards the topics they covered.&lt;/p&gt;



&lt;p&gt;Onward and upward! 🚀&lt;/p&gt;

    </content>
	</entry>
  
	<entry>
		<title>One of a Kind Business Cards</title>
		<link href="https://stevenwoodson.com/blog/one-of-a-kind-business-cards"/>
		<published>2023-09-10T12:46:36.000Z</published>
		<updated>2023-09-10T12:46:38.000Z</updated>
		<id>https://stevenwoodson.com/blog/one-of-a-kind-business-cards</id>
    <summary>I wanted memorable and personal business cards to share. Here&amp;#8217;s my process towards a unique experience that encourages further engagement.</summary>
		<content type="html">&lt;img src=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/09/One-of-a-Kind-Business-Cards.webp&quot; alt=&quot;&quot;&gt;
&lt;p&gt;It&amp;#8217;s been a while since I made some business cards, but I really wanted something to leave with folks I meet at conferences and meetups that would be both memorable and personal.&lt;/p&gt;



&lt;p&gt;A tall order for some business cards, but I think I did alright. Here&amp;#8217;s how it came together.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;The Idea&lt;/h2&gt;



&lt;p&gt;I wanted every card to have a personal touch, and a unique touchpoint for every person that received one. I know cards are a bit dated so my primary focus was to get them to _want to_ find me online.&lt;/p&gt;



&lt;p&gt;So I thought, &lt;em&gt;what about a unique QR code for each card&lt;/em&gt;? That&amp;#8217;d be great start for a unique experience online, but different QR codes are not visually distinctive.&lt;/p&gt;



&lt;p&gt;Okay, &lt;em&gt;what about in addition to those QR codes there&amp;#8217;s a unique plaid swatch on each card too&lt;/em&gt;? Now we&amp;#8217;re talking! Plaid is kind of my thing, and making a custom plaid color palette for each person would be really memorable.&lt;/p&gt;



&lt;p&gt;To pull that off, I knew I could take advantage of what &lt;a href=&quot;https://www.moo.com/us/about/printfinity&quot;&gt;Moo refers to as Printfinity&lt;/a&gt;, basically they let you upload a unique back to every single card you order.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Digital Implementation&lt;/h2&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;SVGs&lt;/h3&gt;



&lt;p&gt;I already have a &lt;a href=&quot;https://stevenwoodson.com/plaid-generator/&quot;&gt;plaid swatch generator&lt;/a&gt; (read more about &lt;a href=&quot;https://stevenwoodson.com/blog/plaid-swatch-generator/&quot;&gt;how I made the plaid swatch generator here&lt;/a&gt;) so I jumped right into creating 50 unique color palettes. This was likely the most time consuming part, but the end result was so worth it!&lt;/p&gt;



&lt;figure class=&quot;wp-block-image size-large&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1024&quot; height=&quot;525&quot; src=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/09/Screen-Shot-2023-08-17-at-9.37.08-AM-1024x525.png&quot; alt=&quot;Small plaid color swatches in a quilt of 10 wide by 5 tall, illustrating the variety of colors that had been printed.&quot; class=&quot;wp-image-465&quot; srcset=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/09/Screen-Shot-2023-08-17-at-9.37.08-AM-1024x525.png 1024w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/09/Screen-Shot-2023-08-17-at-9.37.08-AM-300x154.png 300w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/09/Screen-Shot-2023-08-17-at-9.37.08-AM-768x394.png 768w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/09/Screen-Shot-2023-08-17-at-9.37.08-AM.png 1532w&quot; sizes=&quot;auto, (max-width: 1024px) 100vw, 1024px&quot; /&gt;&lt;figcaption class=&quot;wp-element-caption&quot;&gt;Swatches of all 50 unique plaid color palettes&lt;/figcaption&gt;&lt;/figure&gt;



&lt;p&gt;I used the &lt;a href=&quot;https://svgexport.io/&quot; rel=&quot;nofollow&quot;&gt;SVG Export&lt;/a&gt; Chrome extension to grab a copy of all the unique SVGs for later use.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;QR Codes&lt;/h3&gt;



&lt;p&gt;I just googled &amp;#8220;QR Code Generator&amp;#8221; and found this one called &lt;a href=&quot;https://qrexplore.com/generate/&quot; rel=&quot;nofollow&quot;&gt;QRExplore&lt;/a&gt; that was able to generate them all in one go. I was dreading having to make these individually manually so this was a welcome shortcut!&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Personalized Contact Pages&lt;/h3&gt;



&lt;p&gt;I then got to work building out the pages that the QR codes point towards. Each QR code has a unique code at the end that lets me know which particular card &amp;#8211; and thus which plaid swatch &amp;#8211; the person received. &lt;/p&gt;



&lt;p&gt;So the page itself features that same plaid fairly prominently, just after a big vCard download button (because that&amp;#8217;s the #1 reason they&amp;#8217;re here in the first place). I&amp;#8217;m hoping the vCard is a helpful touch, so folks could add me to their contacts as easily as possible.&lt;/p&gt;



&lt;figure class=&quot;wp-block-image size-large&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1024&quot; height=&quot;626&quot; src=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/09/Screen-Shot-2023-09-10-at-6.50.56-AM-1-1024x626.png&quot; alt=&quot;Top section of the contact page specifically created for folks that get my business cards. Headline reads &amp;quot;Looks like you chose card 1 of 50, great choice!&amp;quot;&quot; class=&quot;wp-image-467&quot; srcset=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/09/Screen-Shot-2023-09-10-at-6.50.56-AM-1-1024x626.png 1024w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/09/Screen-Shot-2023-09-10-at-6.50.56-AM-1-300x183.png 300w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/09/Screen-Shot-2023-09-10-at-6.50.56-AM-1-768x470.png 768w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/09/Screen-Shot-2023-09-10-at-6.50.56-AM-1.png 1115w&quot; sizes=&quot;auto, (max-width: 1024px) 100vw, 1024px&quot; /&gt;&lt;figcaption class=&quot;wp-element-caption&quot;&gt;Example of the plaid from card #1 at the top of the contact page in the QR code&lt;/figcaption&gt;&lt;/figure&gt;



&lt;p&gt;Since I went through all the trouble of making these unique plaid swatches, I saved them all as exportable SVGs so the recipient can download and use them too. I&amp;#8217;m surrounded by exceptionally creative people all the time, hoping something fun comes out of this!&lt;/p&gt;



&lt;p&gt;Because I plan on keeping card #1, I&amp;#8217;ll share the URL to it here so you can see the final result for yourself, &lt;a href=&quot;https://stevenwoodson.com/irl/2wahemEK/&quot;&gt;Card 1 of 50 Contact Page&lt;/a&gt;.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Print Implementation&lt;/h2&gt;



&lt;p&gt;While I&amp;#8217;m not a designer by trade, I do occasionally enjoy exploring creative layouts and personal projects like this are the perfect time for that. I used Affinity Designer to pull my assets in and get to work.&lt;/p&gt;



&lt;p&gt;After a whole lot of fiddling (which I didn&amp;#8217;t screenshot unfortunately) I settled on a fairly unconventional portrait orientation for both the front and back of the cards.&lt;/p&gt;



&lt;figure class=&quot;wp-block-image size-large&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1024&quot; height=&quot;668&quot; src=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/09/Screen-Shot-2023-09-10-at-6.58.23-AM-1024x668.png&quot; alt=&quot;Proof images of the front of the business cards and the back of card numbered 1 of 50.&quot; class=&quot;wp-image-468&quot; srcset=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/09/Screen-Shot-2023-09-10-at-6.58.23-AM-1024x668.png 1024w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/09/Screen-Shot-2023-09-10-at-6.58.23-AM-300x196.png 300w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/09/Screen-Shot-2023-09-10-at-6.58.23-AM-768x501.png 768w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/09/Screen-Shot-2023-09-10-at-6.58.23-AM.png 1055w&quot; sizes=&quot;auto, (max-width: 1024px) 100vw, 1024px&quot; /&gt;&lt;figcaption class=&quot;wp-element-caption&quot;&gt;The front of every card, and the back of card #1 once the designs were finalized&lt;/figcaption&gt;&lt;/figure&gt;



&lt;p&gt;I made the sections that don&amp;#8217;t change into symbols first, just in case I spotted a typo or wanted to change something, at least I wouldn&amp;#8217;t need to do it 50 times later on.&lt;/p&gt;



&lt;p&gt;Then, I copied the back artboard 9 times horizontally, selected all 10 and copied them again vertically 5 times to end up with 50. The next time sink was then replacing the plaid backgrounds on every card back, and adding the unique QR codes to each of them.&lt;/p&gt;



&lt;figure class=&quot;wp-block-image size-large&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1024&quot; height=&quot;767&quot; src=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/09/Screen-Shot-2023-08-16-at-5.51.50-AM-1024x767.png&quot; alt=&quot;Screenshot of all 50 Affinity Designer Artboards for the card backs next to the one shared card front artboard.&quot; class=&quot;wp-image-469&quot; srcset=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/09/Screen-Shot-2023-08-16-at-5.51.50-AM-1024x767.png 1024w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/09/Screen-Shot-2023-08-16-at-5.51.50-AM-300x225.png 300w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/09/Screen-Shot-2023-08-16-at-5.51.50-AM-768x575.png 768w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/09/Screen-Shot-2023-08-16-at-5.51.50-AM-1536x1151.png 1536w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/09/Screen-Shot-2023-08-16-at-5.51.50-AM.png 1671w&quot; sizes=&quot;auto, (max-width: 1024px) 100vw, 1024px&quot; /&gt;&lt;figcaption class=&quot;wp-element-caption&quot;&gt;The glorious end result of all 50 card backs&lt;/figcaption&gt;&lt;/figure&gt;



&lt;p&gt;Because I&amp;#8217;m a web developer with a deeply engrained sense of &amp;#8220;measure twice and cut once&amp;#8221; I also then manually tested each and every QR code before sending this off to print.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;The Printed Cards&lt;/h3&gt;



&lt;p&gt;I went with Moo mainly because of the Printfinity, without which this whole idea wouldn&amp;#8217;t have been possible. But, I also knew they had a 100% recyclable print option and that was one of my only requirements for printing. I&amp;#8217;m trying to be mindful of my environmental impact and wanted to make sure these cards matched that intent.&lt;/p&gt;



&lt;p&gt;It wasn&amp;#8217;t until around this point that I realized I took some chances on several of the color combinations I used and wasn&amp;#8217;t quite sure if they&amp;#8217;d translate on card stock. I live and breathe RGB, I&amp;#8217;ve seen that mindset lead to some muddy results when translating to CMYK, and yet I still didn&amp;#8217;t think about it til the end. Oh well, I thought, let&amp;#8217;s just go for it and see.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;The Grand Reveal&lt;/h2&gt;



&lt;p&gt;The day my cards arrived felt like my birthday, I could not wait to see how they turned out in print. &lt;/p&gt;



&lt;p&gt;After testing a handful of the QR codes (again) and satisfied I actually pulled this off. I let it sink in that it&amp;#8217;s a real thing now.&lt;/p&gt;



&lt;figure class=&quot;wp-block-image size-large&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1024&quot; height=&quot;771&quot; src=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/09/PXL_20230909_123844846-1024x771.jpg&quot; alt=&quot;Printed business cards fanned out on a granite counter top, on top of the fanned cards is a Moo ShowCase business card holder&quot; class=&quot;wp-image-470&quot; srcset=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/09/PXL_20230909_123844846-1024x771.jpg 1024w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/09/PXL_20230909_123844846-300x226.jpg 300w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/09/PXL_20230909_123844846-768x578.jpg 768w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/09/PXL_20230909_123844846-1536x1157.jpg 1536w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/09/PXL_20230909_123844846-2048x1542.jpg 2048w&quot; sizes=&quot;auto, (max-width: 1024px) 100vw, 1024px&quot; /&gt;&lt;figcaption class=&quot;wp-element-caption&quot;&gt;The glorious result of all that effort, so pleased with how it turned out!&lt;/figcaption&gt;&lt;/figure&gt;



&lt;p&gt;I&amp;#8217;m happy with how they all turned out, some are more muted and others a whole lot more vibrant in print but that just adds to the variety I think.&lt;/p&gt;



&lt;p&gt;With my order, I also purchased a neat &lt;a href=&quot;https://www.moo.com/us/accessories/business-card-holders/showcase-us&quot; rel=&quot;nofollow&quot;&gt;ShowCase&lt;/a&gt; to hold a bunch of them (pictured above). It&amp;#8217;s the perfect way to let folks pick the swatch they like the most.&lt;/p&gt;



&lt;p&gt;So, now if we meet in person be sure to ask for your very own one of a kind business card!&lt;/p&gt;

    </content>
	</entry>
  
	<entry>
		<title>Going Independent &#8211; Two Months In</title>
		<link href="https://stevenwoodson.com/blog/going-independent-two-months-in"/>
		<published>2023-08-30T13:47:53.000Z</published>
		<updated>2023-08-30T13:47:55.000Z</updated>
		<id>https://stevenwoodson.com/blog/going-independent-two-months-in</id>
    <summary>The last freelance journey post, covering the past few weeks of progress including open source contributions, site updates, and a new project!</summary>
		<content type="html">&lt;img src=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/08/Going-Independent-Two-Months-In.png&quot; alt=&quot;&quot;&gt;
&lt;p&gt;I mentioned &lt;a href=&quot;https://stevenwoodson.com/blog/going-independent-week-four-five-check-in/&quot;&gt;in the last post&lt;/a&gt; that I was considering only taking this through week 8. I&amp;#8217;ve confirmed that desire, opting instead to focus on content that&amp;#8217;s more relevant to potential clients, other freelancers, and web devs.&lt;/p&gt;



&lt;p&gt;Hope you enjoyed these glimpses into my day to day of building this up. Here&amp;#8217;s &lt;a href=&quot;https://stevenwoodson.com/blog/going-independent-week-one-check-in/&quot;&gt;week 1&lt;/a&gt;, &lt;a href=&quot;https://stevenwoodson.com/blog/going-independent-week-two-check-in/&quot;&gt;week 2&lt;/a&gt;, and &lt;a href=&quot;https://stevenwoodson.com/blog/going-independent-week-three-check-in/&quot;&gt;week 3&lt;/a&gt; as well if you&amp;#8217;re interested in some earlier posts about this evolution.&lt;/p&gt;



&lt;p&gt;Let&amp;#8217;s dive in one last time!&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;What&amp;#8217;s Happened the Past Few Weeks&lt;/h2&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;strong&gt;Cutting back on social network posting to LinkedIn and Mastodon&lt;/strong&gt;. I had been posting some content on LinkedIn, Twitter (X?), BlueSky, Mastodon, and Facebook for a bit. After looking through the stats and feedback, I decided to narrow focus to these two to save time. I may incorporate others again later depending on the topic, but want to see how it goes with two.&lt;/li&gt;



&lt;li&gt;I realize follower &amp;amp; view stats can often be a vanity metric, but I&amp;#8217;m happy to note that &lt;strong&gt;I&amp;#8217;ve seen slow but steady increases&lt;/strong&gt; in website traffic, LinkedIn followers &amp;amp; views, and Mastodon followers and interactions. Any one of them can spark a connection so I don&amp;#8217;t take it for granted!&lt;/li&gt;



&lt;li&gt;I released that &lt;a href=&quot;https://stevenwoodson.com/blog/eleventy-style-guide-generator-with-webc-component-support/&quot;&gt;&lt;strong&gt;11ty style guide generator&lt;/strong&gt;&lt;/a&gt; and &lt;a href=&quot;https://stevenwoodson.com/blog/eleventy-style-guide-generator-step-by-step-guide-adding-to-an-existing-site/&quot;&gt;shared how to add it to an existing site&lt;/a&gt; after doing so for this site. Still a work in progress, but feel free to &lt;a href=&quot;https://stevenwoodson.com/design-system/&quot;&gt;check it out&lt;/a&gt;!&lt;/li&gt;



&lt;li&gt;Just before integrating that style guide generator, I &lt;strong&gt;overhauled the styling of this site&lt;/strong&gt; to use more modern CSS best practices like custom properties, fluid grid and typography powered by clamp, and generally making better use of the space with a touch more color.&lt;/li&gt;



&lt;li&gt;I just received some &lt;strong&gt;business cards &lt;/strong&gt;that I took more time than I care to admit designing, they have a unique IRL -&gt; online connection that I&amp;#8217;m pretty proud of. I&amp;#8217;ll be blogging about that process too so stay tuned for more on that (likely next week).&lt;/li&gt;



&lt;li&gt;I&amp;#8217;m getting started on a &lt;strong&gt;web dev project for a former colleague&lt;/strong&gt; that reconnected, more to come on that as soon as I can!&lt;/li&gt;



&lt;li&gt;I stopped counting the number of touchpoints I&amp;#8217;ve had, from meetings, email conversations, and social connections I know I&amp;#8217;m easily &lt;strong&gt;in the triple digits of new contacts&lt;/strong&gt; since the last update. Surprisingly, it does get a little easier each time. The harder part is tracking progress and making sure I don&amp;#8217;t miss reconnecting. I have a pretty nice homegrown CRM (as I hinted at last time) that is working for me for now. Trying to be frugal and so many of the CRM offerings out there are pretty expensive for my needs at the moment.&lt;/li&gt;
&lt;/ul&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;What&amp;#8217;s Coming Up&lt;/h2&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;Focusing on this &lt;strong&gt;active project&lt;/strong&gt; I had mentioned above, going to knock it out of the park! I&amp;#8217;m working with a really great guy who is also a freelance consultant, we&amp;#8217;re just getting started but it&amp;#8217;s already been nice working with someone who &amp;#8220;gets it&amp;#8221;.&lt;/li&gt;



&lt;li&gt;Getting really excited about &lt;a href=&quot;https://2023.stateofthebrowser.com/&quot; rel=&quot;nofollow&quot;&gt;the &lt;strong&gt;State of the Browser conference&lt;/strong&gt;&lt;/a&gt; coming up in a few weeks! My &lt;a href=&quot;https://beinclusive.app/&quot;&gt;accessibility auditing SaaS app Be Inclusive&lt;/a&gt; is a sponsor of that wonderful event, and with the sponsorship I get an in-person ticket to attend. So I&amp;#8217;m packing up and traveling internationally to meet some new people and share some of my fancy new business cards!&lt;/li&gt;



&lt;li&gt;I have a few additional &lt;strong&gt;project leads&lt;/strong&gt; that are in various stages of the pipeline, all so different and exciting. Looking forward to seeing where those go.&lt;/li&gt;



&lt;li&gt;Upcoming &lt;strong&gt;updates to Be Inclusive&lt;/strong&gt; including the addition of WCAG 2.2 criteria.&lt;/li&gt;



&lt;li&gt;I have a handful of additional side projects and blog post ideas I&amp;#8217;m going to continue to chip away at, now would be a good time &lt;a href=&quot;https://stevenwoodson.com/feed/feed.xml&quot;&gt;to follow the RSS feed&lt;/a&gt; so you don&amp;#8217;t miss any of it!&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;As I had mentioned in the intro, this&amp;#8217;ll be the last &amp;#8220;Going Freelance&amp;#8221; update post here but I do intend to still share bits of my progress on social media. So be sure to connect with me on &lt;a href=&quot;https://mastodon.online/@stevenwoodson&quot;&gt;Mastodon&lt;/a&gt; and &lt;a href=&quot;https://www.linkedin.com/in/stevenwoodson/&quot;&gt;LinkedIn&lt;/a&gt;!&lt;/p&gt;



&lt;p&gt;Thanks again for reading, onward and upward! 🚀&lt;/p&gt;

    </content>
	</entry>
  
	<entry>
		<title>Eleventy Style Guide Generator &#8211; Step by Step guide adding to an existing site</title>
		<link href="https://stevenwoodson.com/blog/eleventy-style-guide-generator-step-by-step-guide-adding-to-an-existing-site"/>
		<published>2023-08-25T13:43:42.000Z</published>
		<updated>2026-03-31T21:05:20.000Z</updated>
		<id>https://stevenwoodson.com/blog/eleventy-style-guide-generator-step-by-step-guide-adding-to-an-existing-site</id>
    <summary>Step by Step guide to adding the Eleventy Style Guide Generator to an existing site. Details on how to avoid needing WebC or Design Tokens included.</summary>
		<content type="html">&lt;img src=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/08/Eleventy-Style-Guide-Generator-Step-by-Step-guide-to-add-to-an-existing-site-1.png&quot; alt=&quot;&quot;&gt;
&lt;p&gt;I shared the &lt;a href=&quot;https://stevenwoodson.com/blog/eleventy-style-guide-generator-with-webc-component-support/&quot;&gt;&lt;/a&gt;&lt;a href=&quot;https://stevenwoodson.com/blog/eleventy-style-guide-generator-with-webc-component-support/&quot;&gt;Eleventy Style Guide Generator&lt;/a&gt; (&lt;a href=&quot;https://github.com/stevenwoodson/11ty-design-system&quot;&gt;source is on GitHub&lt;/a&gt;, &lt;a href=&quot;https://stevenwoodson.github.io/11ty-design-system/&quot;&gt;here’s the demo site&lt;/a&gt; it generates, and &lt;a href=&quot;https://www.youtube.com/watch?v=3mhA2bH6q8s&quot; rel=&quot;nofollow&quot;&gt;an intro video from the September Eleventy meetup&lt;/a&gt;) a few weeks ago. Because it&amp;#8217;s a full Eleventy repo and not a plugin (can&amp;#8217;t be until &lt;a href=&quot;https://github.com/11ty/eleventy/issues/1612&quot; rel=&quot;nofollow&quot;&gt;Virtual Templates&lt;/a&gt; are supported), I received a few questions about its requirements including:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;Are design tokens required?&lt;/li&gt;



&lt;li&gt;Is the use of WebC required?&lt;/li&gt;



&lt;li&gt;Can I use a CSS preprocessor like SCSS?&lt;/li&gt;



&lt;li&gt;How do I add this to an existing Eleventy site?&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;This post attempts to answer these questions, I&amp;#8217;ll also be updating the repo with more of these details as well.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Are design tokens required?&lt;/h2&gt;



&lt;p&gt;&lt;strong&gt;No, you don&amp;#8217;t need design tokens to use this generator. &lt;/strong&gt;&lt;/p&gt;



&lt;p&gt;If you decide not to use design tokens you can also remove some of the additional dependencies and files including:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;the Tailwind dependency &amp;amp; configuration file at `tailwind.config.js. All it&amp;#8217;s doing is converting the JSON sourced design tokens into usable CSS custom properties.&lt;/li&gt;



&lt;li&gt;the two CSS utilities at &lt;code&gt;src/_utilities/css-utils&lt;/code&gt;. Similarly, they&amp;#8217;re only being used by the Tailwind configuration to generate the custom properties.&lt;/li&gt;



&lt;li&gt;the tokens static design system page at &lt;code&gt;src/design-system/Atoms/tokens.njk&lt;/code&gt;. It&amp;#8217;s only there to itemize the individual design tokens.&lt;/li&gt;
&lt;/ul&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Is the use of WebC required?&lt;/h2&gt;



&lt;p&gt;&lt;strong&gt;Not technically, but it&amp;#8217;s highly encouraged. &lt;/strong&gt;&lt;/p&gt;



&lt;p&gt;WebC brings first-class components to Eleventy, this being a way to itemize components in a style guide it stands to reason that the two are well suited as a pair.&lt;/p&gt;



&lt;p&gt;If you choose not to use WebC, or need to ease into supporting it in your existing project, here&amp;#8217;s a few considerations.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Naming Collisions&lt;/h3&gt;



&lt;p&gt;The generator is assuming that each component under &lt;code&gt;src/_includes/components&lt;/code&gt; has a &lt;code&gt;.webc&lt;/code&gt; component and an &lt;code&gt;.njk&lt;/code&gt; example defined. If your components are also going to be in Nunjucks you&amp;#8217;ll need another qualifier added to example files so you can programmatically tell them apart.&lt;/p&gt;



&lt;p&gt;Something like &lt;code&gt;.stories.njk&lt;/code&gt; (like Storybook) would work fine. Then you&amp;#8217;d update &lt;code&gt;src/design-system/components-pages.njk&lt;/code&gt; to include that extra qualifier, for example like this&lt;/p&gt;



&lt;pre class=&quot;wp-block-code language-njk&quot;&gt;&lt;code&gt;{%- macro c(path, name, params) -%}
  {% include &quot;components/&quot; + path + &quot;/&quot; + name + &quot;.stories.njk&quot; ignore missing %}
{%- endmacro -%}&lt;/code&gt;&lt;/pre&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Layouts&lt;/h3&gt;



&lt;p&gt;There are also two WebC layouts that are being used to generate the style guide files at &lt;code&gt;src/_includes/layouts/ds-static.webc&lt;/code&gt; and &lt;code&gt;src/_includes/layouts/ds-wrapper.webc&lt;/code&gt;.&lt;/p&gt;



&lt;p&gt;You could enable WebC just for new pages and templates like these, but it should be fairly straightforward to convert into another template language if you don&amp;#8217;t want to enable WebC at all.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Existing components&lt;/h3&gt;



&lt;p&gt;There are a handful of components defined in the repo and they&amp;#8217;re all WebC based components, so you won&amp;#8217;t (easily) be able to make use of them.&lt;/p&gt;



&lt;p&gt;You could convert them to another template language but that effort may not be worthwhile depending on your needs.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Can I use a CSS preprocessor like SCSS?&lt;/h2&gt;



&lt;p&gt;&lt;strong&gt;Sure, you can still use a preprocessor like LESS, SCSS, or Stylus&lt;/strong&gt; if that fits better in your workflow.&lt;/p&gt;



&lt;p&gt;I had decided on using only CSS to reduce the number of dependencies required, that&amp;#8217;s all.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;How do I add this to an existing Eleventy site?&lt;/h2&gt;



&lt;p&gt;I jotting down what I&amp;#8217;ve needed to do to add this to my own site, and listed it all here.&lt;/p&gt;



&lt;p&gt;This including everything that I had noted above as optional, so I&amp;#8217;ve split it up into sections so you can skip the bits you don&amp;#8217;t want.&lt;/p&gt;



&lt;p&gt;File path references directly relate to &lt;a href=&quot;https://github.com/stevenwoodson/11ty-design-system/tree/main/src&quot;&gt;the paths in the 11ty-design-system repo&lt;/a&gt; starting with &lt;code&gt;src&lt;/code&gt;&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Essentials Setup&lt;/h3&gt;



&lt;p&gt;First, you need to determine where you want the style guide to live, in the repo I&amp;#8217;ve set this as &lt;code&gt;/design-system&lt;/code&gt; but you can place it anywhere that makes sense for your project.&lt;/p&gt;



&lt;h4 class=&quot;wp-block-heading&quot;&gt;Copy the following pages to this new folder&lt;/h4&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;code&gt;src/design-system/components-pages.njk&lt;/code&gt; &amp;#8211; generates the style guide page for each component, with tabs for Demo, HTML, and Context. Loads the demo as an iframe of&amp;#8230;&lt;/li&gt;



&lt;li&gt;&lt;code&gt;src/design-system/components-full-pages.njk&lt;/code&gt; &amp;#8211; this is the isolated component in a blank page. Iframed into the component page above.
&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;This is relying on a &amp;#8220;base&amp;#8221; layout too, if yours is in a different location you&amp;#8217;ll need to update &lt;code&gt;layout: layouts/base.webc&lt;/code&gt; as well.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;



&lt;li&gt;&lt;code&gt;src/design-system/index.md&lt;/code&gt; &amp;#8211; The style guide homepage, feel free to replace the content here (and even change the template language).&lt;/li&gt;



&lt;li&gt;&lt;code&gt;src/design-system/Atoms&lt;/code&gt; &amp;#8211; This folder contains files that itemize the design system tokens and most major native HTML elements&lt;/li&gt;
&lt;/ul&gt;



&lt;h4 class=&quot;wp-block-heading&quot;&gt;Copy the following to your layouts directory&lt;/h4&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;code&gt;src/_includes/layouts/ds-&lt;code&gt;wrapper&lt;/code&gt;.webc&lt;/code&gt; &amp;#8211; The layout of the design system pages, including the sidebar menu.
&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;This is relying on a &amp;#8220;base&amp;#8221; layout too, if yours is in a different location you&amp;#8217;ll need to update &lt;code&gt;layout: layouts/base.webc&lt;/code&gt; as well.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;



&lt;li&gt;&lt;code&gt;src/_includes/layouts/ds-static.webc&lt;/code&gt; &amp;#8211; The layout for any static content pages you add in addition to the auto-generated ones.&lt;/li&gt;
&lt;/ul&gt;



&lt;h4 class=&quot;wp-block-heading&quot;&gt;Copy the following into your &lt;code&gt;.eleventy.js&lt;/code&gt; config&lt;/h4&gt;



&lt;pre class=&quot;wp-block-code language-js&quot;&gt;&lt;code&gt;/* Shortcodes */
config.addPairedShortcode(&#39;brace&#39;, function (content, type = &#39;curly&#39;) {
  const &amp;#91;opening, closing] = {
    curly: &amp;#91;&#39;{{&#39;, &#39;}}&#39;],
    silent: &amp;#91;&#39;{%-&#39;, &#39;-%}&#39;]
  }&amp;#91;type];
  return `${opening}${content}${closing}`;
});

config.addPairedShortcode(&#39;prettify&#39;, (content) =&amp;gt; {
  return prettify(content);
});

/* Filters */
config.addFilter(&#39;console&#39;, function (value) {
  return JSON.stringify(value, null, 2);
});

/* Custom Collections */
// Filter source file names using a glob
config.addCollection(&#39;dsAtoms&#39;, function (collectionApi) {
  return collectionApi.getFilteredByGlob(&#39;**/design-system/Atoms/**/*&#39;);
});&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;Descriptions in the same order as the code above:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;Brace &amp;amp; Prettify shortcodes, for help displaying content in the Code and Context tab panels&lt;/li&gt;



&lt;li&gt;&lt;code&gt;console&lt;/code&gt; filter for stringifying the JSON configuration in the Context tab&lt;/li&gt;



&lt;li&gt;&lt;code&gt;dsAtoms&lt;/code&gt; custom collection so we can also add the static pages under the &lt;code&gt;Atoms&lt;/code&gt; directory to the style guide menu&lt;/li&gt;
&lt;/ul&gt;



&lt;h4 class=&quot;wp-block-heading&quot;&gt;Copy the foundational CSS &amp;amp; JS&lt;/h4&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;code&gt;src/assets/css/design-system.css&lt;/code&gt; &amp;#8211; helps with setting up design system specific styles like color swatches. Everything is prefixed with &lt;code&gt;ds-&lt;/code&gt; to avoid CSS collisions.&lt;/li&gt;



&lt;li&gt;&lt;code&gt;src/assets/js/vendor/seven-minute-tabs.js&lt;/code&gt; &amp;#8211; Powers the Demo, HTML, and Context tab structure for the component style guide pages&lt;/li&gt;
&lt;/ul&gt;



&lt;h4 class=&quot;wp-block-heading&quot;&gt;Copy the following data files&lt;/h4&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;code&gt;src/_data/components.js&lt;/code&gt; &amp;#8211; generates all the components as a collection, be sure to update the folder paths here too if they&amp;#8217;re different in your setup.&lt;/li&gt;



&lt;li&gt;&lt;code&gt;src/_data/global.js&lt;/code&gt; &amp;#8211; defines the &lt;code&gt;random&lt;/code&gt; function used in some templates to cache bust the CSS.
&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;If you have a cache busting filter in place you can skip this one and update references to &lt;code&gt;global.random()&lt;/code&gt; with that instead.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;With these steps I had a functional style guide being generated. You can see my progress at &lt;a href=&quot;https://stevenwoodson.com/design-system/&quot;&gt;https://stevenwoodson.com/design-system/&lt;/a&gt;.&lt;/p&gt;



&lt;figure class=&quot;wp-block-image size-large&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1024&quot; height=&quot;732&quot; src=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/08/Screen-Shot-2023-08-25-at-8.37.55-AM-1024x732.png&quot; alt=&quot;Screenshot of the design system open to the design tokens page. The top of the page lists the color tokens starting with background, and then listing text and grey shades.&quot; class=&quot;wp-image-453&quot; srcset=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/08/Screen-Shot-2023-08-25-at-8.37.55-AM-1024x732.png 1024w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/08/Screen-Shot-2023-08-25-at-8.37.55-AM-300x214.png 300w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/08/Screen-Shot-2023-08-25-at-8.37.55-AM-768x549.png 768w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/08/Screen-Shot-2023-08-25-at-8.37.55-AM.png 1269w&quot; sizes=&quot;auto, (max-width: 1024px) 100vw, 1024px&quot; /&gt;&lt;figcaption class=&quot;wp-element-caption&quot;&gt;Screenshot of the Design Tokens design system page after following the steps in this section to get it added to my site.&lt;/figcaption&gt;&lt;/figure&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Adding Design Tokens&lt;/h3&gt;



&lt;p&gt;Here&amp;#8217;s what you need to copy to get design tokens set up:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;code&gt;tailwind.config.js&lt;/code&gt; &amp;#8211; This is what powers the generation of all the CSS custom properties&lt;/li&gt;



&lt;li&gt;&lt;code&gt;postcss.config.js&lt;/code&gt; &amp;#8211; PostCSS triggers the tailwind configuration, it also minifies the CSS&lt;/li&gt;



&lt;li&gt;Build step for the above in your package.json, &lt;code&gt;&quot;css&quot;: &quot;npx postcss src/assets/css/*.css --base src --dir dist&quot;,&lt;/code&gt; for example&lt;/li&gt;



&lt;li&gt;&lt;code&gt;src/_data/designTokens&lt;/code&gt; &amp;#8211; This is where all the design token JSON files are stored, they&amp;#8217;re in the data directory to make it easier to parse that data with Eleventy&amp;#8217;s automated data cascade &lt;/li&gt;



&lt;li&gt;&lt;code&gt;src/_utilities/css-utils&lt;/code&gt; &amp;#8211; Utility classes that the tailwind config needs in order to generate the custom properties and the clamp values that many of them need&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;The act of utilizing the design tokens is likely another post entirely, but the TL;DR of it is that you&amp;#8217;re going to need to comb through your CSS and replace color, spacing, and other tokenized values with those coming from this generator.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Adding WebC Support &amp;amp; Components&lt;/h3&gt;



&lt;h4 class=&quot;wp-block-heading&quot;&gt;Adding WebC support&lt;/h4&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;You&amp;#8217;re going to &lt;strong&gt;need Eleventy 2.0.0 or newer&lt;/strong&gt; to be able to utilize WebC. I was on an older 1.x version and was surprised at how easy the upgrade process was. Hoping you have similar luck if you need to upgrade as well.&lt;/li&gt;



&lt;li&gt;Follow the &lt;a href=&quot;https://www.11ty.dev/docs/languages/webc/#installation&quot; rel=&quot;nofollow&quot;&gt;install steps from the official WebC docs&lt;/a&gt;, currently it&amp;#8217;s not bundled with core so you&amp;#8217;ll need to perform an NPM install and add the plugin to your config.&lt;/li&gt;
&lt;/ul&gt;



&lt;h4 class=&quot;wp-block-heading&quot;&gt;WebC Component Structure&lt;/h4&gt;



&lt;p&gt;All components are to be stored under &lt;code&gt;src/_includes/components&lt;/code&gt;. One component per folder is the ideal and you can nest folders down to three levels currently.&lt;/p&gt;



&lt;p&gt;Here&amp;#8217;s the structure of a typical component folder, using &lt;a href=&quot;https://github.com/stevenwoodson/11ty-design-system/tree/main/src/_includes/components/_Compositions/sidebar&quot;&gt;the Sidebar component&lt;/a&gt; as an example:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;code&gt;sidebar-l.webc&lt;/code&gt; WebC component file &amp;#8211; This is where the component itself is defined, in this case the WebC component is a wrapper for a pre-defined true web component so it only loads the CSS and JS. &lt;/li&gt;



&lt;li&gt;&lt;code&gt;sidebar.config.js&lt;/code&gt; Configuration file &amp;#8211; Defines the component name and any additional context needed to demonstrate what the component can do.&lt;/li&gt;



&lt;li&gt;&lt;code&gt;Sidebar.css&lt;/code&gt; Web Component &amp;#8211; it&amp;#8217;s not required to store component styles here rather than with the rest of the site styles but can be a nice way to keep everything together in one place.&lt;/li&gt;



&lt;li&gt;&lt;code&gt;Sidebar.js&lt;/code&gt; Web Component JavaScript &amp;#8211; This is where the Web Component is initialized using standard &lt;code&gt;HTMLElement&lt;/code&gt; and &lt;code&gt;customElements&lt;/code&gt; syntax.&lt;/li&gt;



&lt;li&gt;&lt;code&gt;sidebar.njk&lt;/code&gt; style guide example &amp;#8211; The Nunjucks template file is where we demonstrate the component with example content.&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;Check out &lt;a href=&quot;https://github.com/stevenwoodson/11ty-design-system/tree/main/src/_includes/components/card&quot;&gt;the Card component&lt;/a&gt; for an example of a simpler &lt;a href=&quot;https://www.11ty.dev/docs/languages/webc/#html-only-components&quot; rel=&quot;nofollow&quot;&gt;HTML-only component&lt;/a&gt;. This one contains a .webc component, a config, and a Nunjucks template for rendering the examples.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Send me your feedback!&lt;/h2&gt;



&lt;p&gt;Did you add this Eleventy Style Guide Generator to your site? I&amp;#8217;d love to hear about it! If you&amp;#8217;re willing to share your process adding it as well, that could inform future updates and documentation to make it easier for the next person. So please do &lt;a href=&quot;https://stevenwoodson.com/project-inquiry/&quot;&gt;reach out&lt;/a&gt;!&lt;/p&gt;

    </content>
	</entry>
  
	<entry>
		<title>Pulling WordPress Post Categories &#038; Tags Into Eleventy</title>
		<link href="https://stevenwoodson.com/blog/pulling-wordpress-post-categories-tags-into-eleventy"/>
		<published>2023-08-11T19:21:02.000Z</published>
		<updated>2023-08-14T20:04:35.000Z</updated>
		<id>https://stevenwoodson.com/blog/pulling-wordpress-post-categories-tags-into-eleventy</id>
    <summary>Step by step guide to gathering post categories and tags from the WordPress REST API for better filtering and discoverability in an Eleventy blog.</summary>
		<content type="html">&lt;img src=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/08/Pulling-WordPress-Post-Categories-Tags-Into-Eleventy.png&quot; alt=&quot;&quot;&gt;
&lt;h2 class=&quot;wp-block-heading&quot;&gt;TL;DR&lt;/h2&gt;



&lt;p&gt;This is another part in a series of posts about WordPress content being pulled into Eleventy, including:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://stevenwoodson.com/blog/composable-architecture-powered-by-wordpress/&quot;&gt;Composable Architecture Powered by WordPress&lt;/a&gt; (where it all started)&lt;/li&gt;



&lt;li&gt;&lt;a href=&quot;https://stevenwoodson.com/blog/pulling-wordpress-content-into-eleventy/&quot;&gt;Pulling WordPress Content into Eleventy&lt;/a&gt;&lt;/li&gt;



&lt;li&gt;&lt;a href=&quot;https://stevenwoodson.com/blog/adding-a-table-of-contents-to-dynamic-content-in-11ty/&quot;&gt;Adding a Table of Contents to dynamic content in 11ty&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;This post is specifically expanding on the progress from the &lt;a href=&quot;https://stevenwoodson.com/blog/pulling-wordpress-content-into-eleventy/&quot;&gt;Pulling WordPress Content into Eleventy&lt;/a&gt; post to add categories and tags to my blog posts. I&amp;#8217;m also going to add per category-filtered pages, and a per tags-filtered pages for good measure.&lt;/p&gt;



&lt;p&gt;Here&amp;#8217;s what it&amp;#8217;s going to look like when it&amp;#8217;s all done, or you can &lt;a href=&quot;https://stevenwoodson.com/blog/&quot;&gt;click around my blog&lt;/a&gt; to see it for yourself.&lt;/p&gt;



&lt;figure class=&quot;wp-block-image size-large&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1024&quot; height=&quot;600&quot; src=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/08/localhost_8080_blog_-1024x600.png&quot; alt=&quot;&quot; class=&quot;wp-image-422&quot; srcset=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/08/localhost_8080_blog_-1024x600.png 1024w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/08/localhost_8080_blog_-300x176.png 300w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/08/localhost_8080_blog_-768x450.png 768w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/08/localhost_8080_blog_.png 1170w&quot; sizes=&quot;auto, (max-width: 1024px) 100vw, 1024px&quot; /&gt;&lt;figcaption class=&quot;wp-element-caption&quot;&gt;Category Link List &lt;/figcaption&gt;&lt;/figure&gt;



&lt;p&gt;&lt;/p&gt;&lt;figure class=&quot;wp-block-image size-large&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1024&quot; height=&quot;595&quot; class=&quot;wp-image-423&quot; src=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/08/localhost_8080_blog_category_webdev_-1024x595.png&quot; alt=&quot;&quot; srcset=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/08/localhost_8080_blog_category_webdev_-1024x595.png 1024w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/08/localhost_8080_blog_category_webdev_-300x174.png 300w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/08/localhost_8080_blog_category_webdev_-768x446.png 768w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/08/localhost_8080_blog_category_webdev_.png 1170w&quot; sizes=&quot;auto, (max-width: 1024px) 100vw, 1024px&quot; /&gt;&lt;figcaption class=&quot;wp-element-caption&quot;&gt;Posts filtered by Category&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;



&lt;figure class=&quot;wp-block-image size-large&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1024&quot; height=&quot;887&quot; src=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/08/localhost_8080_blog_eleventy-style-guide-generator-with-webc-component-support_-1024x887.png&quot; alt=&quot;&quot; class=&quot;wp-image-424&quot; srcset=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/08/localhost_8080_blog_eleventy-style-guide-generator-with-webc-component-support_-1024x887.png 1024w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/08/localhost_8080_blog_eleventy-style-guide-generator-with-webc-component-support_-300x260.png 300w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/08/localhost_8080_blog_eleventy-style-guide-generator-with-webc-component-support_-768x666.png 768w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/08/localhost_8080_blog_eleventy-style-guide-generator-with-webc-component-support_.png 1170w&quot; sizes=&quot;auto, (max-width: 1024px) 100vw, 1024px&quot; /&gt;&lt;figcaption class=&quot;wp-element-caption&quot;&gt;Categories and Tags listed at the top of a post&lt;/figcaption&gt;&lt;/figure&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Why?&lt;/h2&gt;



&lt;p&gt;As I&amp;#8217;ve been focusing more on blogging lately, I&amp;#8217;ve amassed enough posts that I started realizing that it&amp;#8217;s getting progressively harder to find a blog post the older it gets.&lt;/p&gt;



&lt;p&gt;Really the only way to do so currently is to go to the main blog page and scroll or run a page search. Not ideal. I could add a site search but I&amp;#8217;ve been dragging my heels on that too, so I&amp;#8217;m opting for something a little easier for now.&lt;/p&gt;



&lt;p&gt;Time to utilize the built in functionality of Categories and Tags from WordPress!&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Adding Categories and Tags to Posts&lt;/h2&gt;



&lt;p&gt;First things first, we need to make sure to add some categories and tags to blog posts. I hadn&amp;#8217;t up until now because I wasn&amp;#8217;t using them, so I spent some time coming up with a list of categories and tags I&amp;#8217;d like to use and then using &amp;#8220;Quick Edit&amp;#8221; to apply them to posts quickly. Check out &lt;a href=&quot;https://wordpress.com/learn/courses/intro-to-blogging/categories-and-tags/&quot; rel=&quot;nofollow&quot;&gt;this Categories and tags article&lt;/a&gt; for more details.&lt;/p&gt;



&lt;p&gt;Once we have that data set up in WordPress, we need to make sure we&amp;#8217;re gathering it in Eleventy when performing a build. We need to have a list of &lt;strong&gt;categories&lt;/strong&gt; and &lt;strong&gt;tags&lt;/strong&gt; attributed to the post in order to link to them in the blog post template.&lt;/p&gt;



&lt;p&gt;These are collectively what WordPress refers to as &lt;code&gt;terms&lt;/code&gt;. The details (slug, title, etc.) of terms are not surfaced in the default REST API response, instead we&amp;#8217;re only going to see references to their IDs in the main data object like this:&lt;/p&gt;



&lt;pre class=&quot;wp-block-code language-json&quot;&gt;&lt;code&gt;&quot;categories&quot;: &amp;#91;7],
&quot;tags&quot;: &amp;#91;34],&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;We&amp;#8217;ll also see RESTful links inside &lt;code&gt;_links&lt;/code&gt; (which would return these details separately) like the following:&lt;/p&gt;



&lt;pre class=&quot;wp-block-code language-json&quot;&gt;&lt;code&gt;&quot;wp:term&quot;: &amp;#91;
  {
    &quot;taxonomy&quot;: &quot;category&quot;,
    &quot;embeddable&quot;: true,
    &quot;href&quot;: &quot;https://mysite.com/wp-json/wp/v2/categories?post=1&quot;
  },
  {
    &quot;taxonomy&quot;: &quot;post_tag&quot;,
    &quot;embeddable&quot;: true,
    &quot;href&quot;: &quot;https://mysite.com/wp-json/wp/v2/tags?post=1&quot;
  }
],&lt;/code&gt;&lt;/pre&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Getting terms in the REST API response&lt;/h3&gt;



&lt;p&gt;Instead of performing multiple queries to get the category and tag data, we can add them to the embed section of the same query we&amp;#8217;re already using.&lt;/p&gt;



&lt;p&gt;As noted in the previous post about pulling content from WordPress, I&amp;#8217;m splitting out the posts method &lt;a href=&quot;https://stevenwoodson.com/blog/pulling-wordpress-content-into-eleventy/#getallposts&quot;&gt;getAllPosts&lt;/a&gt; from the post details method &lt;a href=&quot;https://stevenwoodson.com/blog/pulling-wordpress-content-into-eleventy/#requestposts&quot;&gt;requestPosts&lt;/a&gt;.&lt;/p&gt;



&lt;p&gt;To add the terms details, we add &lt;code&gt;&amp;amp;_embed=wp:term&lt;/code&gt; to the API request in &lt;code&gt;requestPosts&lt;/code&gt;. So in our previous code &lt;code&gt;_embed: &quot;wp:featuredmedia&quot;,&lt;/code&gt; turns into &lt;code&gt;_embed: &quot;wp:featuredmedia,wp:term&quot;,&lt;/code&gt;.&lt;/p&gt;



&lt;p&gt;Next, we need to make sense of that data and add it to the blogpost data object we&amp;#8217;re using to generate the blog pages.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Organizing the term data&lt;/h3&gt;



&lt;p&gt;WordPress doesn&amp;#8217;t discern between a &amp;#8220;category&amp;#8221; and a &amp;#8220;tag&amp;#8221; in the embedded JSON, all terms are stored together with an associated &lt;strong&gt;taxonomy&lt;/strong&gt;. Categories are in the &lt;code&gt;category&lt;/code&gt; taxonomy, and tags are in the &lt;code&gt;post_tag&lt;/code&gt; taxonomy.&lt;/p&gt;



&lt;p&gt;For the same example as above where there&amp;#8217;s one category whose ID is 7 and one tag with an ID of 34, here&amp;#8217;s a slightly trimmed version of what that raw data ends up looking like:&lt;/p&gt;



&lt;pre class=&quot;wp-block-code language-json&quot;&gt;&lt;code&gt;&quot;wp:term&quot;: &amp;#91;
  &amp;#91;
    {
      &quot;id&quot;: 7,
      &quot;link&quot;: &quot;https://mysite.com/category/webdev/&quot;,
      &quot;name&quot;: &quot;Web Dev&quot;,
      &quot;slug&quot;: &quot;webdev&quot;,
      &quot;taxonomy&quot;: &quot;category&quot;,
      
    }
  ],
  &amp;#91;
    {
      &quot;id&quot;: 34,
      &quot;link&quot;: &quot;https://mysite.com/tag/eleventy/&quot;,
      &quot;name&quot;: &quot;eleventy&quot;,
      &quot;slug&quot;: &quot;eleventy&quot;,
      &quot;taxonomy&quot;: &quot;post_tag&quot;,
    }
  ]
]&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;So, we&amp;#8217;re going to need to separate categories and tags ourselves to be able to use them in those separate contexts. Here&amp;#8217;s how I&amp;#8217;m doing it.&lt;/p&gt;



&lt;p&gt;Before&lt;/p&gt;



&lt;pre class=&quot;wp-block-code language-js&quot;&gt;&lt;code&gt;metaDescription: metaDescription,
slug: post.slug,
title: post.title.rendered,&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;After&lt;/p&gt;



&lt;pre class=&quot;wp-block-code language-js&quot;&gt;&lt;code&gt;metaDescription: metaDescription,
slug: post.slug,
title: post.title.rendered,
terms: post._embedded&amp;#91;&quot;wp:term&quot;] ? post._embedded&amp;#91;&quot;wp:term&quot;] : null,
categories: post.categories,
tags: post.tags,
categoriesDetail:
  post._embedded&amp;#91;&quot;wp:term&quot;]?.length &amp;gt; 0
    ? post._embedded&amp;#91;&quot;wp:term&quot;].filter(
        (term) =&amp;gt; term&amp;#91;0]?.taxonomy == &quot;category&quot;
      )&amp;#91;0]
    : null,
tagsDetail:
  post._embedded&amp;#91;&quot;wp:term&quot;]?.length &amp;gt; 0
    ? post._embedded&amp;#91;&quot;wp:term&quot;].filter(
        (term) =&amp;gt; term&amp;#91;0]?.taxonomy == &quot;post_tag&quot;
      )&amp;#91;0]
    : null,&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;It&amp;#8217;s a little gnarly looking, but we don&amp;#8217;t want our code to break if there aren&amp;#8217;t any categories or tags defined so the checks are all squished in there too. If tags are found, I&amp;#8217;m then filtering out categories and tags separately in the returned post data.&lt;/p&gt;



&lt;p&gt;You&amp;#8217;ll notice I&amp;#8217;m also passing along the array of categories and tags IDs in &lt;code&gt;categories&lt;/code&gt; and &lt;code&gt;tags&lt;/code&gt; too, this is for more easily filtering blog posts by these terms in the individual category and tag pages.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Blog Post Updates&lt;/h2&gt;



&lt;p&gt;Now that I have post categories and tags, time to add them to the blog post pages!&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Categories&lt;/h3&gt;



&lt;p&gt;I have &amp;#8220;Published&amp;#8221;, &amp;#8220;Last Updated&amp;#8221;, and reading time already listed at the top of the page just below the headline and above the blog contents. I want to add the category(ies) here too, so here&amp;#8217;s the relevant Nunjucks template code for that addition:&lt;/p&gt;



&lt;pre class=&quot;wp-block-code language-njk&quot;&gt;&lt;code&gt;{% if blogpost.categoriesDetail %}
  &amp;lt;p&amp;gt;
    &amp;lt;strong&amp;gt;Posted in &amp;lt;/strong&amp;gt;
    {% for category in blogpost.categoriesDetail %}
      &amp;lt;a href=&quot;{{ &#39;/blog/category/&#39; + category.slug | url }}&quot;&amp;gt;{{ category.name }}&amp;lt;/a&amp;gt;
      {% if not loop.last %}, {% endif %}
    {% endfor %}
  &amp;lt;/p&amp;gt;
{% endif %}&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;I&amp;#8217;m checking for the presence of categories first, some of my posts aren&amp;#8217;t categorized yet so I don&amp;#8217;t want this to show at all for those. I then loop through the categories defined (because there can be multiple) and render a comma separated list.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Tags&lt;/h3&gt;



&lt;p&gt;The Nunjucks template code for tags is similar to the categories above, I ended up adding it to the top of the page as well but am considering moving to the bottom.&lt;/p&gt;



&lt;pre class=&quot;wp-block-code language-njk&quot;&gt;&lt;code&gt;{% if blogpost.tagsDetail %}
  &amp;lt;p&amp;gt;
    &amp;lt;span aria-hidden=&quot;true&quot; class=&quot;fe fe-tag&quot;&amp;gt;&amp;lt;/span&amp;gt;
    &amp;lt;strong&amp;gt;
      {% if blogpost.tagsDetail.length &amp;gt; 1 %}Tags{% else %}Tag{% endif %}
    &amp;lt;/strong&amp;gt;:
  {% for tag in blogpost.tagsDetail %}
      &amp;lt;a href=&quot;{{ &#39;/blog/tag/&#39; + tag.slug | url }}&quot;&amp;gt;{{ tag.name }}&amp;lt;/a&amp;gt;
      {% if not loop.last %}, {% endif %}
    {% endfor %}
  &amp;lt;/p&amp;gt;
{% endif %}&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;The biggest difference with this template snippet is that I&amp;#8217;m pluralizing &amp;#8220;Tags&amp;#8221; instead of &amp;#8220;Tag&amp;#8221; if there are more than one.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;New Pages&lt;/h2&gt;



&lt;p&gt;These additions are all looking great, but if you&amp;#8217;re following along you may notice that the links I&amp;#8217;ve added to the blog post template are all going to 404 pages. Of course, that&amp;#8217;s because they don&amp;#8217;t exist yet so let&amp;#8217;s get on that.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Gathering Categories and Tags Data&lt;/h3&gt;



&lt;p&gt;I&amp;#8217;ve not figured out a way to compile all the categories and tags into their own collections by using the data we&amp;#8217;ve already gathered in blog posts. Instead, I had to run separate REST API calls for them. If you know of a better way to do this please do let me know!&lt;/p&gt;



&lt;p&gt;In setting up these two new REST API calls, I noticed quite a bit of duplication between them, so I opted to do some cleanup and isolate the API calls and data manipulation needed for them all. I called it &lt;code&gt;/utils/wp-json.js&lt;/code&gt;. Here&amp;#8217;s the code for that:&lt;/p&gt;



&lt;pre class=&quot;wp-block-code language-js&quot;&gt;&lt;code&gt;const { AssetCache } = require(&quot;@11ty/eleventy-fetch&quot;);
const axios = require(&quot;axios&quot;);
const jsdom = require(&quot;jsdom&quot;);

// Config
const ITEMS_PER_REQUEST = 10;

/**
 * WordPress API call by page
 *
 * @param {Int} page - Page number to fetch, defaults to 1
 * @return {Object} - Total, Pages, and full API data
 */
async function requestPage(apiBase, page = 1) {
  try {
    // https://developer.wordpress.org/rest-api/using-the-rest-api/pagination/
    const url = apiBase;
    const params = {
      params: {
        page: page,
        per_page: ITEMS_PER_REQUEST,
        _embed: &quot;wp:featuredmedia,wp:term&quot;,
        order: &quot;desc&quot;,
      },
    };
    const response = await axios.get(url, params);

    return {
      total: parseInt(response.headers&amp;#91;&quot;x-wp-total&quot;], 10),
      pages: parseInt(response.headers&amp;#91;&quot;x-wp-totalpages&quot;], 10),
      data: response.data,
    };
  } catch (err) {
    console.error(&quot;API not responding, no data returned&quot;, err);
    return {
      total: 0,
      pages: 0,
      data: &amp;#91;],
    };
  }
}

/**
 * Get all data from a WordPress API endpoint
 * Use cached values if available, pull from API if not.
 *
 * @return {Array} - array of data objects
 */
async function getAllContent(API_BASE, ASSET_CACHENAME) {
  const cache = new AssetCache(ASSET_CACHENAME);
  let requests = &amp;#91;];
  let apiData = &amp;#91;];

  if (cache.isCacheValid(&quot;2h&quot;)) {
    console.log(&quot;Using cached &quot; + ASSET_CACHENAME);
    return cache.getCachedValue();
  }

  // make first request and marge results with array
  const request = await requestPage(API_BASE);
  console.log(
    &quot;Using API &quot; +
      ASSET_CACHENAME +
      &quot;, retrieving &quot; +
      request.pages +
      &quot; pages, &quot; +
      request.total +
      &quot; total records.&quot;
  );
  apiData.push(...request.data);

  if (request.pages &amp;gt; 1) {
    // create additional requests
    for (let page = 2; page &amp;lt;= request.pages; page++) {
      const request = requestPage(API_BASE, page);
      requests.push(request);
    }

    // resolve all additional requests in parallel
    const allResponses = await Promise.all(requests);
    allResponses.map((response) =&amp;gt; {
      apiData.push(...response.data);
    });
  }

  // return data
  await cache.save(apiData, &quot;json&quot;);
  return apiData;
}

/**
 * Clean up and convert the API response for our needs
 */
async function processContent(content) {
  return Promise.all(
    content.map(async (post) =&amp;gt; {
      // remove HTML-Tags from the excerpt for meta description
      let metaDescription = post.excerpt.rendered.replace(/(&amp;lt;(&amp;#91;^&amp;gt;]+)&amp;gt;)/gi, &quot;&quot;);
      metaDescription = metaDescription.replace(&quot;&#92;n&quot;, &quot;&quot;);

      // Code highlighting with Eleventy Syntax Highlighting
      // https://www.11ty.dev/docs/plugins/syntaxhighlight/
      const formattedContent = highlightCode(prepared.content);

      // Return only the data that is needed for the actual output
      return await {
        content: post.content.rendered,
        formattedContent: formattedContent,
        custom_fields: post.custom_fields ? post.custom_fields : null,
        date: post.date,
        dateRFC3339: new Date(post.date).toISOString(),
        modifiedDate: post.modified,
        modifiedDateRFC3339: new Date(post.modified).toISOString(),
        excerpt: post.excerpt.rendered,
        formattedDate: new Date(post.date).toLocaleDateString(&quot;en-US&quot;, {
          year: &quot;numeric&quot;,
          month: &quot;long&quot;,
          day: &quot;numeric&quot;,
        }),
        formattedModifiedDate: new Date(post.modified).toLocaleDateString(
          &quot;en-US&quot;,
          {
            year: &quot;numeric&quot;,
            month: &quot;long&quot;,
            day: &quot;numeric&quot;,
          }
        ),
        heroImageFull:
          post._embedded&amp;#91;&quot;wp:featuredmedia&quot;] &amp;amp;&amp;amp;
          post._embedded&amp;#91;&quot;wp:featuredmedia&quot;].length &amp;gt; 0
            ? post._embedded&amp;#91;&quot;wp:featuredmedia&quot;]&amp;#91;0].media_details.sizes.full
                .source_url
            : null,
        heroImageThumb:
          post._embedded&amp;#91;&quot;wp:featuredmedia&quot;] &amp;amp;&amp;amp;
          post._embedded&amp;#91;&quot;wp:featuredmedia&quot;].length &amp;gt; 0
            ? post._embedded&amp;#91;&quot;wp:featuredmedia&quot;]&amp;#91;0].media_details.sizes
                .medium_large
              ? post._embedded&amp;#91;&quot;wp:featuredmedia&quot;]&amp;#91;0].media_details.sizes
                  .medium_large.source_url
              : post._embedded&amp;#91;&quot;wp:featuredmedia&quot;]&amp;#91;0].media_details.sizes.full
                  .source_url
            : null,
        metaDescription: metaDescription,
        slug: post.slug,
        title: post.title.rendered,
        terms: post._embedded&amp;#91;&quot;wp:term&quot;] ? post._embedded&amp;#91;&quot;wp:term&quot;] : null,
        categories: post.categories,
        tags: post.tags,
        categoriesDetail:
          post._embedded&amp;#91;&quot;wp:term&quot;]?.length &amp;gt; 0
            ? post._embedded&amp;#91;&quot;wp:term&quot;].filter(
                (term) =&amp;gt; term&amp;#91;0]?.taxonomy == &quot;category&quot;
              )&amp;#91;0]
            : null,
        tagsDetail:
          post._embedded&amp;#91;&quot;wp:term&quot;]?.length &amp;gt; 0
            ? post._embedded&amp;#91;&quot;wp:term&quot;].filter(
                (term) =&amp;gt; term&amp;#91;0]?.taxonomy == &quot;post_tag&quot;
              )&amp;#91;0]
            : null,
      };
    })
  );
}

function sortNameAlpha(content) {
  return content.sort((a, b) =&amp;gt; {
    if (a.name &amp;lt; b.name) return -1;
    else if (a.name &amp;gt; b.name) return 1;
    else return 0;
  });
}

module.exports = {
  requestPage: requestPage,
  getAllContent: getAllContent,
  processContent: processContent,
  sortNameAlpha: sortNameAlpha,
};&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;With this new set of utilities, the actual data JS files are super small. Here are the &lt;code&gt;blogposts.js&lt;/code&gt;, &lt;code&gt;blogcategories.js&lt;/code&gt;. and &lt;code&gt;blogtags.js&lt;/code&gt; files.&lt;/p&gt;



&lt;p&gt;&lt;strong&gt;blogposts.js&lt;/strong&gt;&lt;/p&gt;



&lt;pre class=&quot;wp-block-code language-js&quot;&gt;&lt;code&gt;const { getAllContent, processContent } = require(&quot;../utils/wp-json&quot;);

const API_BASE =
  &quot;https://mysite.com/wp-json/wp/v2/posts&quot;;
const ASSET_CACHENAME = &quot;blogposts&quot;;

// export for 11ty
module.exports = async () =&amp;gt; {
  const blogposts = await getAllContent(API_BASE, ASSET_CACHENAME);
  const processedPosts = await processContent(blogposts);
  return processedPosts;
};
&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;&lt;strong&gt;blogcategories.js&lt;/strong&gt;&lt;/p&gt;



&lt;pre class=&quot;wp-block-code language-js&quot;&gt;&lt;code&gt;const { getAllContent, sortNameAlpha } = require(&quot;../utils/wp-json&quot;);

const API_BASE =
  &quot;https://mysite.com/wp-json/wp/v2/categories&quot;;
const ASSET_CACHENAME = &quot;blogcategories&quot;;

// export for 11ty
module.exports = async () =&amp;gt; {
  const blogcategories = await getAllContent(API_BASE, ASSET_CACHENAME);
  return sortNameAlpha(blogcategories);
};&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;&lt;strong&gt;&lt;code&gt;blogtags&lt;/code&gt;.js&lt;/strong&gt;&lt;/p&gt;



&lt;pre class=&quot;wp-block-code language-js&quot;&gt;&lt;code&gt;const { getAllContent, sortNameAlpha } = require(&quot;../utils/wp-json&quot;);

const API_BASE =
  &quot;https://mysite.com/wp-json/wp/v2/tags&quot;;
const ASSET_CACHENAME = &quot;blogtags&quot;;

// export for 11ty
module.exports = async () =&amp;gt; {
  const blogtags = await getAllContent(API_BASE, ASSET_CACHENAME);
  return sortNameAlpha(blogtags);
};&lt;/code&gt;&lt;/pre&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Creating a Blog Post Term Filter&lt;/h3&gt;



&lt;p&gt;Great, we have data now! The next step is to set up a filter so we can show category and tag pages with just the posts that contain them. Because both are ID based, I opted to create one filter that&amp;#8217;d work for either. Here&amp;#8217;s the code&lt;/p&gt;



&lt;pre class=&quot;wp-block-code language-js&quot;&gt;&lt;code&gt;  // Get the elements of a collection that contains the provided ID for the provided taxonomy
  eleventyConfig.addFilter(&quot;blogTermFilter&quot;, (items, taxonomy, termID) =&amp;gt; {
    return items.filter((post) =&amp;gt; {
      return post&amp;#91;taxonomy].includes(termID);
    });
  });&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;Now I can pass the taxonomy (&lt;code&gt;categories&lt;/code&gt; or &lt;code&gt;tags&lt;/code&gt;) and its ID to get a filtered list of posts with that category or tag.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Creating the Category and Tag Pages&lt;/h3&gt;



&lt;p&gt;We&amp;#8217;re getting really close now!&lt;/p&gt;



&lt;p&gt;The final step is to create the tags and categories filtered pages. For me, they both have basically the same structure so I&amp;#8217;m just going to share the categories one here.&lt;/p&gt;



&lt;pre class=&quot;wp-block-code language-njk&quot;&gt;&lt;code&gt;---
layout: layouts/base.njk
pagination:
  data: blogcategories
  size: 1
  alias: blogcategory
permalink: blog/category/{{ blogcategory.slug }}/
---
{% set blogslist = blogposts | blogTermFilter(&quot;categories&quot;, blogcategory.id) %}

&amp;lt;section class=&quot;l-container h-feed hfeed&quot;&amp;gt;
  &amp;lt;header class=&quot;feature-list__header&quot;&amp;gt;
    &amp;lt;h1 class=&quot;p-name&quot;&amp;gt;Blog Posts categorized under &quot;{{ blogcategory.name }}&quot;&amp;lt;/h1&amp;gt;
    &amp;lt;a href=&quot;{{ metadata.feed.path }}&quot;&amp;gt;RSS Feed&amp;lt;/a&amp;gt;
  &amp;lt;/header&amp;gt;
  &amp;lt;p&amp;gt;
    &amp;lt;a href=&quot;{{ &#39;/blog/&#39; | url }}&quot; class=&quot;btn btn--secondary btn--small&quot;&amp;gt;back to all blog posts&amp;lt;/a&amp;gt;
  &amp;lt;/p&amp;gt;

  &amp;lt;div class=&quot;grid-3-wide feature-list&quot;&amp;gt;
    {% for post in blogslist %}
      &amp;lt;div class=&quot;card z-depth-1 h-entry hentry&quot;&amp;gt;
        &amp;lt;div class=&quot;img-16-9-aspect&quot;&amp;gt;
          {%- if  post.heroImageThumb %}
            &amp;lt;img src=&quot;{{  post.heroImageThumb }}&quot; alt=&quot;&quot; loading=&quot;lazy&quot;&amp;gt;
          {% else %}
            &amp;lt;img src=&quot;/assets/images/posts/post-hero-placeholder.png&quot; alt=&quot;&quot; loading=&quot;lazy&quot;&amp;gt;
          {% endif %}
        &amp;lt;/div&amp;gt;

        &amp;lt;div class=&quot;card__content&quot;&amp;gt;
          &amp;lt;{{itemheader}} class=&quot;headline4 p-name entry-title&quot;&amp;gt;
            &amp;lt;a href=&quot;/blog/{{post.slug}}&quot; class=&quot;u-url&quot; rel=&quot;bookmark&quot;&amp;gt;
              {% if post.title %}{{ post.title | safe }}
              {% endif %}
            &amp;lt;/a&amp;gt;
          &amp;lt;/{{itemheader}}&amp;gt;
          &amp;lt;div class=&quot;l-post__meta&quot;&amp;gt;
            &amp;lt;p&amp;gt;
              &amp;lt;strong&amp;gt;
                &amp;lt;span aria-hidden=&quot;true&quot; class=&quot;fe fe-calendar&quot;&amp;gt;&amp;lt;/span&amp;gt;
                &amp;lt;time class=&quot;postlist-date&quot; datetime=&quot;{{ post.date }}&quot;&amp;gt;{{ post.formattedDate }}&amp;lt;/time&amp;gt;
              &amp;lt;/strong&amp;gt;
            &amp;lt;/p&amp;gt;
          &amp;lt;/div&amp;gt;
          &amp;lt;div class=&quot;p-summary entry-summary&quot;&amp;gt;
            {%- if post.excerpt %}{{ post.excerpt | safe }}
            {% endif %}
          &amp;lt;/div&amp;gt;
        &amp;lt;/div&amp;gt;
      &amp;lt;/div&amp;gt;
    {% endfor %}
  &amp;lt;/div&amp;gt;
&amp;lt;/section&amp;gt;&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;You can copy this file and replace the references to categories over to tags instead for the Tags version. For example &lt;code&gt;{% set blogslist = blogposts | blogTermFilter(&quot;tags&quot;,blogtag.id) %}&lt;/code&gt;&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Potential Further Enhancements&lt;/h2&gt;



&lt;p&gt;This ended up being a bit more to figure out than I anticipated going into it, and I&amp;#8217;m pretty happy with where it is now.&lt;/p&gt;



&lt;p&gt;I do, however, have some ideas for future further enhancements:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;In addition to Previous and Next posts at the bottom of a blog post page, it&amp;#8217;d be really great to have &lt;strong&gt;a &amp;#8220;Related posts&amp;#8221; section&lt;/strong&gt;. That&amp;#8217;s a fairly common feature of blogs and would help with discoverability.&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;Advanced filtering&lt;/strong&gt; from within the main Blog page would be nice, rather than just separate pages per category and tag. This would open up further options like filtering by category and tag together.&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;I may make time for these soon, let me know if you&amp;#8217;d be interested in reading more about that!&lt;/p&gt;

    </content>
	</entry>
  
	<entry>
		<title>Eleventy Style Guide Generator with WebC Component Support</title>
		<link href="https://stevenwoodson.com/blog/eleventy-style-guide-generator-with-webc-component-support"/>
		<published>2023-08-03T17:27:17.000Z</published>
		<updated>2026-04-03T15:39:20.000Z</updated>
		<id>https://stevenwoodson.com/blog/eleventy-style-guide-generator-with-webc-component-support</id>
    <summary>The simplicity and speed of Eleventy with the organization of a self documenting design system. Style guide generator that incorporates design tokens, fluid typography, and WebC components.</summary>
		<content type="html">&lt;img src=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/08/Eleventy-Style-Guide-Generator-with-WebC-Components.png&quot; alt=&quot;&quot;&gt;
&lt;h2 class=&quot;wp-block-heading&quot;&gt;TL;DR&lt;/h2&gt;



&lt;p&gt;I created an Eleventy style guide generator, features include:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;Automatically itemized JSON-formatted &lt;strong&gt;design tokens&lt;/strong&gt; like colors, fonts, and fluid scale and sizing.&lt;/li&gt;



&lt;li&gt;Automatically itemized &lt;strong&gt;components and their variations&lt;/strong&gt; based on a simple configuration file format&lt;/li&gt;



&lt;li&gt;Eleventy &lt;strong&gt;&lt;a href=&quot;https://www.11ty.dev/docs/languages/webc/&quot; rel=&quot;nofollow&quot;&gt;WebC Component&lt;/a&gt; support&lt;/strong&gt; and examples&lt;/li&gt;



&lt;li&gt;Supports &lt;strong&gt;standalone documentation pages&lt;/strong&gt;, perfect for displaying foundational HTML elements and explaining design system details in a more curated way.&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;&lt;a href=&quot;https://github.com/stevenwoodson/11ty-design-system&quot;&gt;Source is on GitHub&lt;/a&gt;, &lt;a href=&quot;https://stevenwoodson.github.io/11ty-design-system/&quot;&gt;here&amp;#8217;s the demo site&lt;/a&gt; it generates, and &lt;a href=&quot;https://www.youtube.com/watch?v=3mhA2bH6q8s&quot; rel=&quot;nofollow&quot;&gt;an intro video from the September Eleventy meetup&lt;/a&gt;.&lt;/p&gt;



&lt;p&gt;For more detail about how to add it to your own Eleventy site, check out the &lt;a href=&quot;https://stevenwoodson.com/blog/eleventy-style-guide-generator-step-by-step-guide-adding-to-an-existing-site/&quot;&gt;Step by Step guide adding to an existing site&lt;/a&gt; follow up post.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Impetus&lt;/h2&gt;



&lt;p&gt;I recently had the opportunity to create a new fairly simple Eleventy site and found myself wanting to also document some of the visual language and components therein.&lt;/p&gt;



&lt;p&gt;I didn&amp;#8217;t want to go overboard and install something like Storybook for this because &amp;#8211; while I love Storybook &amp;#8211; it can be &lt;em&gt;a lot&lt;/em&gt; and this site definitely didn&amp;#8217;t need all that.&lt;/p&gt;



&lt;p&gt;So, in true developer fashion, rather than manually create a few static style guide pages and move on, I decided to make a design system generator!&lt;/p&gt;



&lt;p&gt;I love the &lt;strong&gt;simplicity and speed of 11ty&lt;/strong&gt;, and I love the organization of a good &lt;strong&gt;self documenting design system&lt;/strong&gt;, so I combined the excellent work of several really smart people to pull these two together in a way that acts as a pretty nice starting point for a fluid, responsive, and blazing fast 11ty powered website.&lt;/p&gt;



&lt;p&gt;Read on if you&amp;#8217;re interested in the pieces that make up the whole, including the inspiration behind them.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Design Tokens&lt;/h2&gt;



&lt;p&gt;What better place to start than the at the smallest building blocks of a design system, right?&lt;/p&gt;



&lt;aside class=&quot;wp-block-create-block-aside&quot;&gt;
&lt;p&gt;Need a quick intro to design tokens and how they relate to design systems? &lt;a href=&quot;https://www.smashingmagazine.com/2019/11/smashing-podcast-episode-3/&quot; rel=&quot;nofollow&quot;&gt;Check out this &amp;#8220;What Are Design Tokens?&amp;#8221; Smashing Magazine podcast episode with Jina Anne&lt;/a&gt;.&lt;/p&gt;
&lt;/aside&gt;



&lt;p&gt;Design tokens are vital to the overall look and feel and are (ideally) used everywhere to promote consistency with spacing, fonts, colors, etc.&lt;/p&gt;



&lt;p&gt;All tokens are stored in the &lt;code&gt;/src/_data/designTokens/&lt;/code&gt; directory, I did this purposefully because having the JSON in the Eleventy Data folder gives me these values to manipulate and use for free. Making the visual itemizing noted below really simple.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Token Definition&lt;/h3&gt;



&lt;p&gt;Here&amp;#8217;s the structure I used for the token files themselves, they all came with a title, description, and an array of items. I also optionally added a meta object to store additional info, for example the sizes and spacing ones link to the Utopia calculator where it was generated.&lt;/p&gt;



&lt;pre class=&quot;wp-block-code language-json&quot;&gt;&lt;code&gt;{
  &quot;title&quot;: &quot;Name for these Tokens&quot;,
  &quot;description&quot;: &quot;Longform description of what these tokens define.&quot;,
  &quot;meta&quot;: {},
  &quot;items&quot;: &amp;#91;]
}&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;I used the excellent tools James and Trys created called &lt;a href=&quot;https://utopia.fyi/&quot; rel=&quot;nofollow&quot;&gt;Utopia&lt;/a&gt; for the sizes and spacing tokens. Here&amp;#8217;s the breakdown:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;The &lt;a href=&quot;https://utopia.fyi/type/calculator&quot; rel=&quot;nofollow&quot;&gt;Type Calculator&lt;/a&gt; defines the &lt;a href=&quot;https://github.com/stevenwoodson/11ty-design-system/blob/main/src/_data/designTokens/sizes.json&quot; rel=&quot;nofollow&quot;&gt;sizes.json&lt;/a&gt; tokens&lt;/li&gt;



&lt;li&gt;The &lt;a href=&quot;https://utopia.fyi/space/calculator&quot; rel=&quot;nofollow&quot;&gt;Space Calculator&lt;/a&gt; defines the &lt;a href=&quot;https://github.com/stevenwoodson/11ty-design-system/blob/main/src/_data/designTokens/spacing.json&quot; rel=&quot;nofollow&quot;&gt;spacing.json&lt;/a&gt; tokens&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;I also added a &lt;a href=&quot;https://github.com/stevenwoodson/11ty-design-system/blob/main/src/_data/designTokens/colors.json&quot; rel=&quot;nofollow&quot;&gt;colors.json&lt;/a&gt; and &lt;a href=&quot;https://github.com/stevenwoodson/11ty-design-system/blob/main/src/_data/designTokens/fonts.json&quot; rel=&quot;nofollow&quot;&gt;fonts.json&lt;/a&gt; token JSON file to itemize those for consistency as well. &lt;/p&gt;



&lt;p&gt;I now have a solid foundation for fluid typography and spacing, and consistent fonts and colors, so the applications I build will look wonderful regardless of the available space and will be consistent from page to page with minimal effort.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Converting to CSS Variables&lt;/h3&gt;



&lt;p&gt;Now that I have my tokens beautifully organized, I also needed a way to convert them into usable CSS variables.&lt;/p&gt;



&lt;p&gt;There are some great well established tools out there that do this, such as &lt;a href=&quot;https://github.com/salesforce-ux/theo&quot; rel=&quot;nofollow&quot;&gt;Theo&lt;/a&gt; and &lt;a href=&quot;https://amzn.github.io/style-dictionary/#/&quot; rel=&quot;nofollow&quot;&gt;Style Dictionary&lt;/a&gt; but for this I turned to the sage advice of Andy Bell via &lt;a href=&quot;https://buildexcellentwebsit.es/&quot; rel=&quot;nofollow&quot;&gt;buildexcellentwebsit.es&lt;/a&gt; and used Tailwind to run through the token JSON files and generate the CSS variables I can use in my styling.&lt;/p&gt;



&lt;p&gt;I went this route because it seemed to fit how I like to organize things, already had concepts inherent to Utopia (including &lt;code&gt;clamp&lt;/code&gt;), and was already working in an Eleventy site.&lt;/p&gt;



&lt;p&gt;These are defined in the &lt;a href=&quot;https://github.com/stevenwoodson/11ty-design-system/tree/main/src/_utilities/css-utils&quot; rel=&quot;nofollow&quot;&gt;/_utilities/css-utils&lt;/a&gt; folder and used in the &lt;a href=&quot;https://github.com/stevenwoodson/11ty-design-system/blob/main/tailwind.config.js&quot; rel=&quot;nofollow&quot;&gt;tailwind.config.js&lt;/a&gt; configuration.&lt;/p&gt;



&lt;p&gt;Here&amp;#8217;s a truncated example of what it generates:&lt;/p&gt;



&lt;pre class=&quot;wp-block-code language-css&quot;&gt;&lt;code&gt;:root {
    --color-bg: #fff;
    --color-bg-alt: #fffaeb;
    ...
    --space-2xs: clamp(0.5rem,0.46rem + 0.19vw,0.625rem);
    --space-xs: clamp(0.75rem,0.69rem + 0.29vw,0.9375rem);
    ...
    --space-xs-s: clamp(0.75rem,0.59rem + 0.78vw,1.25rem);
    --space-s-m: clamp(1rem,0.53rem + 2.33vw,2.5rem);
    ...
    --size-step-n2: clamp(0.6875rem,0.65rem + 0.19vw,0.8125rem);
    ...
    --font-base: Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif;
    --font-serif: Georgia,sans-serif;
    --font-code: Lucida Console,Monaco,monospace;
}&lt;/code&gt;&lt;/pre&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Visually Itemizing the Tokens&lt;/h3&gt;



&lt;p&gt;Being so vitally important to a successful product, I wanted a seamless and automated way to itemize these tokens for easy visual reference.&lt;/p&gt;



&lt;p&gt;I&amp;#8217;m still shocked at how easy this part ended up being once I decided to store the design tokens in the Eleventy data directory.&lt;/p&gt;



&lt;p&gt;Because that data was automatically surfaced to page templates, all I needed to do here was to create an 11ty Nunjucks-based page that loops through the design token values and applies the tokens to some placeholder elements. You can see how this comes together at &lt;a href=&quot;https://github.com/stevenwoodson/11ty-design-system/blob/main/src/design-system/Atoms/tokens.njk&quot; rel=&quot;nofollow&quot;&gt;/src/design-system/Atoms/tokens.njk&lt;/a&gt; and a screenshot of it in action below.&lt;/p&gt;



&lt;figure class=&quot;wp-block-image size-large&quot;&gt;&lt;a href=&quot;https://stevenwoodson.github.io/11ty-design-system/design-system/Atoms/tokens/&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1024&quot; height=&quot;456&quot; src=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/08/Screen-Shot-2023-08-03-at-11.47.09-AM-1024x456.png&quot; alt=&quot;Three screenshots of the same page horizontally overlapping to illustrate some of the page contents more easily. You can go to this page using the link on this image.&quot; class=&quot;wp-image-388&quot; srcset=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/08/Screen-Shot-2023-08-03-at-11.47.09-AM-1024x456.png 1024w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/08/Screen-Shot-2023-08-03-at-11.47.09-AM-300x134.png 300w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/08/Screen-Shot-2023-08-03-at-11.47.09-AM-768x342.png 768w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/08/Screen-Shot-2023-08-03-at-11.47.09-AM-1536x684.png 1536w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/08/Screen-Shot-2023-08-03-at-11.47.09-AM-2048x912.png 2048w&quot; sizes=&quot;auto, (max-width: 1024px) 100vw, 1024px&quot; /&gt;&lt;/a&gt;&lt;figcaption class=&quot;wp-element-caption&quot;&gt;Some overlapping screenshots of the tokens style guide page to illustrate how it itemizes the tokens visually&lt;/figcaption&gt;&lt;/figure&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Style Guide Generator&lt;/h2&gt;



&lt;p&gt;With design tokens defined, accessible via CSS, and visually itemized in a page. I thought &amp;#8220;wouldn&amp;#8217;t it be great to have individual components automatically itemized in a similar way?&amp;#8221;.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Foundation&lt;/h3&gt;



&lt;p&gt;After some searching for existing solutions I found &lt;a href=&quot;https://github.com/trys/eleventy-design-system/&quot; rel=&quot;nofollow&quot;&gt;this Eleventy Design System gem&lt;/a&gt; by Trys Mudford. It&amp;#8217;s truly a wonderful foundation for exactly what I was hoping to achieve with this project.&lt;/p&gt;



&lt;p&gt;While integrating that into what I was building, I noticed a few things that I wanted to expand upon to make it more versatile. Here&amp;#8217;s the highlights of what I added:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;Moved to a subfolder so it can be in the same codebase as the rest of the site&lt;/li&gt;



&lt;li&gt;Ability to define components in subfolders of the &lt;code&gt;_includes/components&lt;/code&gt; directory&lt;/li&gt;



&lt;li&gt;WebC components support and examples&lt;/li&gt;
&lt;/ul&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;WebC Support&lt;/h3&gt;



&lt;p&gt;It just so happened that I was reading more about &lt;a href=&quot;https://www.11ty.dev/docs/languages/webc/&quot; rel=&quot;nofollow&quot;&gt;WebC&lt;/a&gt; while iterating on this idea. WebC is a new 11ty file extension that brings first-class components to Eleventy. An obvious addition to a style guide generator that itemizes components!&lt;/p&gt;



&lt;p&gt;I will caution that WebC is still fairly new, there&amp;#8217;s some things that work great with other file extensions that just don&amp;#8217;t yet for WebC. I&amp;#8217;ve itemized some of these &lt;a href=&quot;https://github.com/stevenwoodson/11ty-design-system/tree/main#todo&quot; rel=&quot;nofollow&quot;&gt;as TODO items in the readme&lt;/a&gt;.&lt;/p&gt;



&lt;p&gt;To see some WebC component examples, check out the &lt;a href=&quot;https://github.com/stevenwoodson/11ty-design-system/tree/main/src/_includes/components/_Compositions/sidebar&quot; rel=&quot;nofollow&quot;&gt;Sidebar&lt;/a&gt; and &lt;a href=&quot;https://github.com/stevenwoodson/11ty-design-system/tree/main/src/_includes/components/_Compositions/stack&quot; rel=&quot;nofollow&quot;&gt;Stack&lt;/a&gt; composition components courtesy of &lt;a href=&quot;https://every-layout.dev/&quot; rel=&quot;nofollow&quot;&gt;Every Layout&lt;/a&gt;, and the &lt;a href=&quot;https://github.com/stevenwoodson/11ty-design-system/tree/main/src/_includes/components/card&quot; rel=&quot;nofollow&quot;&gt;Card&lt;/a&gt; component utilizing some style inspiration from &lt;a href=&quot;https://smolcss.dev/&quot; rel=&quot;nofollow&quot;&gt;SmolCSS&lt;/a&gt;.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Other Conventions&lt;/h2&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Standalone Documentation Pages&lt;/h3&gt;



&lt;p&gt;Rather than having to create a &amp;#8220;component&amp;#8221; for foundational HTML elements and long form documentation within the style guide, I&amp;#8217;ve built it so that you can define standard pages in the style guide as you would any other page.&lt;/p&gt;



&lt;p&gt;It will automatically merge these static pages with the generated ones for components too!&lt;/p&gt;



&lt;p&gt;For some examples of static pages, check out the &lt;a href=&quot;https://github.com/stevenwoodson/11ty-design-system/tree/main/src/design-system/Atoms&quot; rel=&quot;nofollow&quot;&gt;src/design-system/Atoms&lt;/a&gt; folder. That&amp;#8217;s where I&amp;#8217;ve added block, form, inline, and preformatted HTML tags to quickly be able to preview how changes to the design tokens and styles affect these critical native tags.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Styling Methodology&lt;/h3&gt;



&lt;p&gt;I&amp;#8217;ve quickly become a big fan of &lt;a href=&quot;https://cube.fyi/&quot; rel=&quot;nofollow&quot;&gt;CubeCSS&lt;/a&gt; and have tried to adhere to those guidelines as best I can here. If you haven&amp;#8217;t read about it yet I highly recommend you check that site out and give it a try.&lt;/p&gt;



&lt;p&gt;Admittedly, in some cases it gets a little weird but I have my reasons! For example, I have web component styles sitting in the same folder as the web component itself (&lt;a href=&quot;https://github.com/stevenwoodson/11ty-design-system/tree/main/src/_includes/components/_Compositions/sidebar&quot; rel=&quot;nofollow&quot;&gt;Sidebar&lt;/a&gt; for example) and I have others (&lt;a href=&quot;https://github.com/stevenwoodson/11ty-design-system/tree/main/src/_includes/components/card&quot; rel=&quot;nofollow&quot;&gt;Card&lt;/a&gt; for example) that have styles in the assets folder (&lt;a href=&quot;https://github.com/stevenwoodson/11ty-design-system/blob/main/src/assets/css/blocks/card.css&quot; rel=&quot;nofollow&quot;&gt;card.css&lt;/a&gt; in this case) instead.&lt;/p&gt;



&lt;p&gt;I did this because I wanted to adhere to the &lt;a href=&quot;https://www.11ty.dev/docs/languages/webc/#html-only-components&quot; rel=&quot;nofollow&quot;&gt;HTML-only Components&lt;/a&gt; rule where it renders native HTML and ignores the host component tag. Adding and referencing the styles in the component definition would have turned the Card example into a true web component instead.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Live Site Example&lt;/h2&gt;



&lt;p&gt;I&amp;#8217;ve actually been sitting on this for a little while, before releasing something for general use I wanted to make sure to put it through a real site build process first. Happy to share the first such site has just been pushed live for the best (and most patient) client a guy could ask for &amp;#8211; my dad! &lt;/p&gt;



&lt;p&gt;His site at &lt;a href=&quot;https://dannywoodson.org/&quot;&gt;dannywoodson.org&lt;/a&gt; and &lt;a href=&quot;https://dannywoodson.org/design-system/&quot;&gt;the style guide for it is here&lt;/a&gt;.&lt;/p&gt;



&lt;p&gt;Pretty neat to be able to compare the demo site with this live site and see the similarities, but also the differences!&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Plans for the Future&lt;/h2&gt;



&lt;p&gt;I think there&amp;#8217;s a lot of potential here, a couple of the bigger things I&amp;#8217;d love to do with this are:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;Continue to add foundational web components that can be reused across any kind of tech stack. Im considering adding some more baseline WebC components to this project as well, especially those found in&amp;nbsp;&lt;a href=&quot;https://github.com/11ty/tugboat/tree/main/_components&quot;&gt;Tugboat&lt;/a&gt;.&lt;/li&gt;



&lt;li&gt;I&amp;#8217;d ideally like this to be an Eleventy plugin that can be installed on an Eleventy site, rather than a full Eleventy site in itself. As far as I can tell there&amp;#8217;s not a way to do so until&amp;nbsp;&lt;a href=&quot;https://github.com/11ty/eleventy/issues/1612&quot;&gt;Virtual Templates&lt;/a&gt;&amp;nbsp;are made available.&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;Have any ideas? Are you going to give it a try?&lt;/p&gt;



&lt;p&gt;I&amp;#8217;d love to hear from you, reach out on the socials or start up &lt;a href=&quot;https://github.com/stevenwoodson/11ty-design-system/issues/new&quot; rel=&quot;nofollow&quot;&gt;an issue on GitHub&lt;/a&gt;.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Resources&lt;/h2&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Design Systems, Fluid Typography, Styling Methodologies oh my!&lt;/h3&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;Fluid type, space, and grid calculations via &lt;a href=&quot;https://utopia.fyi/&quot; rel=&quot;nofollow&quot;&gt;Utopia&lt;/a&gt;&lt;/li&gt;



&lt;li&gt;CSS methodology based on &lt;a href=&quot;https://cube.fyi/&quot; rel=&quot;nofollow&quot;&gt;Cube CSS&lt;/a&gt;&lt;/li&gt;



&lt;li&gt;&lt;a href=&quot;https://github.com/trys/eleventy-design-system/&quot; rel=&quot;nofollow&quot;&gt;Eleventy Design System&lt;/a&gt; by Trys Mudford &amp;amp; the companion explanatory &lt;a href=&quot;https://www.trysmudford.com/blog/eleventy-design-system/&quot; rel=&quot;nofollow&quot;&gt;blog post&lt;/a&gt;.&lt;/li&gt;



&lt;li&gt;Some of the base components and styles are based on &lt;a href=&quot;https://smolcss.dev/&quot; rel=&quot;nofollow&quot;&gt;SmolCSS&lt;/a&gt; and &lt;a href=&quot;https://every-layout.dev/&quot; rel=&quot;nofollow&quot;&gt;Every Layout&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Getting Started with WebC&lt;/h3&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;Eleventy &lt;a href=&quot;https://www.11ty.dev/docs/languages/webc/&quot; rel=&quot;nofollow&quot;&gt;WebC Components&lt;/a&gt; main documentation&lt;/li&gt;



&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=X-Bpjrkz-V8&quot; rel=&quot;nofollow&quot;&gt;Crash Course in Eleventy’s new WebC Plugin&lt;/a&gt; video&lt;/li&gt;



&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=p0wDUK0Z5Nw&quot; rel=&quot;nofollow&quot;&gt;Interactive Progressively-enhanced Web Components with WebC&lt;/a&gt; video&lt;/li&gt;



&lt;li&gt;&lt;a href=&quot;https://11ty.rocks/posts/introduction-webc/&quot; rel=&quot;nofollow&quot;&gt;Introduction to WebC&lt;/a&gt; &amp;amp; &lt;a href=&quot;https://11ty.rocks/posts/understanding-webc-features-and-concepts/&quot;&gt;Understanding WebC Features and Concepts&lt;/a&gt; from 11ty Rocks&lt;/li&gt;



&lt;li&gt;&lt;a href=&quot;https://11ty.webc.fun/&quot;&gt;11ty &amp;amp; WebC&lt;/a&gt; by W. Evan Sheehan&lt;/li&gt;
&lt;/ul&gt;

    </content>
	</entry>
  
	<entry>
		<title>Going Independent – Week Four &#038; Five Check in</title>
		<link href="https://stevenwoodson.com/blog/going-independent-week-four-five-check-in"/>
		<published>2023-07-31T13:43:02.000Z</published>
		<updated>2023-08-10T17:23:04.000Z</updated>
		<id>https://stevenwoodson.com/blog/going-independent-week-four-five-check-in</id>
    <summary>Weeks four and five freelance journey check in, touches on a contract opportunity I chose not to pursue, how I&amp;#8217;m getting more organized, and other progress &amp;#038; plans.</summary>
		<content type="html">&lt;img src=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/07/Going-Independent-Week-Four-Check-in.png&quot; alt=&quot;&quot;&gt;
&lt;p&gt;Wow, five weeks already? I missed one so this is a roundup of the last two weeks. Here&amp;#8217;s &lt;a href=&quot;https://stevenwoodson.com/blog/going-independent-week-one-check-in/&quot;&gt;week 1&lt;/a&gt;, &lt;a href=&quot;https://stevenwoodson.com/blog/going-independent-week-two-check-in/&quot;&gt;week 2&lt;/a&gt;, and &lt;a href=&quot;https://stevenwoodson.com/blog/going-independent-week-three-check-in/&quot;&gt;week 3&lt;/a&gt; if you&amp;#8217;re interested. Let&amp;#8217;s dive in!&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;What Happened the Last Two Weeks&lt;/h2&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;I agonized over a potential opportunity to take on &lt;strong&gt;a long term contract at a fairly large financial services platform&lt;/strong&gt;, even pulled out the old pro/con list trick to help me decide. After days of mulling it over I decided not to apply. I knew that the benefits of regular income and some project stability were going to be overshadowed by my lack of time making my own consulting practice a reality and I&amp;#8217;m still deeply committed to making this work.&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;Four virtual meetings&lt;/strong&gt;, two were follow-ups from prior chats with other freelancers/entrepreneurs, one was with a branding and copywriter that helped me with some wonderful ideas, and the last was a potential lead with a really nice small web dev agency.&lt;/li&gt;



&lt;li&gt;Several (sorry, I didn&amp;#8217;t keep detailed track of how many) &lt;strong&gt;follow up emails with connections&lt;/strong&gt;, potential leads, and ongoing opportunities.&lt;/li&gt;



&lt;li&gt;Took a few weekdays off right in the middle of these two weeks for &lt;strong&gt;a family road trip&lt;/strong&gt; (part of the reason this is a two week roundup). I&amp;#8217;ll admit it was difficult at first as I was concerned about losing any momentum I&amp;#8217;ve built up, but it really did me some good to unplug for a while and just be.&lt;/li&gt;



&lt;li&gt;I teased last time about some progress on the &lt;strong&gt;11ty style guide generator&lt;/strong&gt;, I was able to get a first iteration of it complete enough to mark off my list. Before releasing, however, I wanted to use it on a real project of my own to work out any initial bugs so&amp;#8230;&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;First semi-complete iteration for my dad&amp;#8217;s website&lt;/strong&gt; was pushed online using the style guide generator, sent it over for initial feedback. I did end up finding a few things that needed to be adjusted in that generator so I&amp;#8217;m glad I did this first. Sorry that took so long dad!&lt;/li&gt;



&lt;li&gt;I spent &lt;strong&gt;half a day working with a trusted friend&lt;/strong&gt;, we shared some things that we&amp;#8217;ve been working on and looking for ways to help each other. One big takeaway for me is to put more effort towards analyzing data from my outreach efforts, basically a sales retro.&lt;/li&gt;
&lt;/ul&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;What&amp;#8217;s Coming Up This Week&lt;/h2&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;I&amp;#8217;m going to set up &lt;strong&gt;a homemade CRM to keep myself more organized&lt;/strong&gt;. I&amp;#8217;m not ready to purchase one yet so I&amp;#8217;m going to use Google Sheets for now. The sales retro conversation last week made me realize that, while I have some pretty good notes and a little system for reminding myself to follow up with folks, I have missed some opportunities to follow up with people and don&amp;#8217;t want to continue that trend.&lt;/li&gt;



&lt;li&gt;Finishing touches on the &lt;strong&gt;11ty style guide generator&lt;/strong&gt; and share it in another post, if you&amp;#8217;re interested stay tuned!&lt;/li&gt;



&lt;li&gt;Finalize &lt;strong&gt;the website for my dad&lt;/strong&gt; and hopefully launching it this week.&lt;/li&gt;



&lt;li&gt;Collect all the actionable bits from my personal musings, ideas, and meeting notes. Then &lt;strong&gt;parse through it all and prioritize&lt;/strong&gt;. I haven&amp;#8217;t done this in a few weeks and am already noticing I&amp;#8217;ve become less focused and more confused about what&amp;#8217;s important now. When that happens, I tend to fixate on one or two things that catch my attention instead.&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;I have a feeling there&amp;#8217;s going to be a lot more added to this list, especially after I&amp;#8217;ve completed that last line item, so stay tuned for the update next week to see how far I get with all this.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Should I keep going?&lt;/h2&gt;



&lt;p&gt;I&amp;#8217;m starting to think the weekly check ins have run its course and am considering stopping after week 8, two full months feels like as good a place as any.&lt;/p&gt;



&lt;p&gt;If you have strong feelings about me keeping this going, any feedback, or good vibes, &lt;a href=&quot;mailto:me@stevenwoodson.com?subject=Keep%20the%20check%20ins%20going!&quot;&gt;reach out&lt;/a&gt; and let me know.&lt;/p&gt;



&lt;p&gt;Thanks again for reading, onward and upward! 🚀&lt;/p&gt;

    </content>
	</entry>
  
	<entry>
		<title>Going Independent &#8211; Week Three Check in</title>
		<link href="https://stevenwoodson.com/blog/going-independent-week-three-check-in"/>
		<published>2023-07-17T15:00:39.000Z</published>
		<updated>2026-04-01T12:39:51.000Z</updated>
		<id>https://stevenwoodson.com/blog/going-independent-week-three-check-in</id>
    <summary>Week three check in on my freelance web developer journey. Some more networking, my first big proposal rejection, and some coding progress for an 11ty style guide generator.</summary>
		<content type="html">&lt;img src=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/07/Going-Independent-Week-Three-Check-in.png&quot; alt=&quot;&quot;&gt;
&lt;p&gt;Keeping the weekly check in train rolling! Here&amp;#8217;s &lt;a href=&quot;https://stevenwoodson.com/blog/going-independent-week-one-check-in/&quot;&gt;week 1&lt;/a&gt; and &lt;a href=&quot;https://stevenwoodson.com/blog/going-independent-week-two-check-in/&quot;&gt;week 2&lt;/a&gt; in case you&amp;#8217;re looking to catch up.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;What Happened Last Week&lt;/h2&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;I got a response back about that big &lt;strong&gt;project proposal&lt;/strong&gt; I mentioned last week, they&amp;#8217;re still interested but we decided on a few foundational changes that necessitated updates to that proposal. Happy to report these updates didn&amp;#8217;t end up taking a whole day like it did for the first iteration.
&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;Later in the week I heard back about that proposal, they had &lt;strong&gt;decided not to move forward&lt;/strong&gt;. I decided to share this on social and realized how tough that actually is, to admit when things aren&amp;#8217;t going as planned.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;



&lt;li&gt;I sent &lt;strong&gt;15 follow up emails and two follow up calls&lt;/strong&gt; for leads and other networking and collaborator outreach. Couple meetings scheduled from these (noted below).&lt;/li&gt;



&lt;li&gt;I&amp;#8217;ve been picking back up on some cold-ish &lt;strong&gt;outreach for &lt;a href=&quot;https://beinclusive.app/&quot;&gt;Be Inclusive&lt;/a&gt;&lt;/strong&gt; too, still trying to build up a user base for that accessibility auditing tracking app. Feedback from users has been overwhelmingly positive, but getting users in there to try it out has been a struggle.&lt;/li&gt;



&lt;li&gt;Dev time! I had been away from code for a while as I&amp;#8217;ve been busy with outreach, content authoring, and other operations setup. I found that I needed that zone-in time with some code to boost my happy.
&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;I got into a great flow on Wednesday &amp;amp; Thursday and made a whole lot of progress towards the foundation of an &lt;strong&gt;11ty style guide generator&lt;/strong&gt;. More to come on this as soon as it&amp;#8217;s ready!&lt;/li&gt;



&lt;li&gt;I purposefully started with that foundational project so I can use it for the first time on a website for my dad (who has been really patient with me in getting it rolling!).&lt;/li&gt;



&lt;li&gt;I&amp;#8217;ve been using both of these goals as an opportunity to brush up on some trends that I&amp;#8217;ve not been able to put enough time into yet, like fluid typography and web components. The plan is to level up on those new-to-me things and also to have a better starting point for base styling new projects.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;What&amp;#8217;s Coming Up This Week&lt;/h2&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;So far, I have &lt;strong&gt;three meetings booked this week&lt;/strong&gt;. Three more in the works but not scheduled.
&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;Two are follow up calls with potential collaborators, to catch up and chat some more. Both were a result of my earlier outreach efforts, and I&amp;#8217;m so glad to have made these connections!&lt;/li&gt;



&lt;li&gt;The third is with a branding and copywriter for some help with my personal branding. Really excited for this one!&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;



&lt;li&gt;I have a really nice &lt;strong&gt;project lead from a prior co-worker&lt;/strong&gt; in early discussions, would be really fun working with them so I&amp;#8217;m keeping my fingers crossed!&lt;/li&gt;



&lt;li&gt;Going to keep iterating on that &lt;strong&gt;11ty style guide generator &lt;/strong&gt;project and hopefully release it for others to try out soon.&lt;/li&gt;



&lt;li&gt;I have a list of about a dozen more potential collaborator and networking contacts to reach out to, going to split them up and do a few a day.&lt;/li&gt;



&lt;li&gt;I&amp;#8217;m taking a couple days off at the end of this week, so I&amp;#8217;m trying to keep this list limited so it&amp;#8217;s more achievable. Looking forward to some time to unplug and come back refreshed next week!&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;Thanks again for reading, onward and upward! 🚀&lt;/p&gt;

    </content>
	</entry>
  
	<entry>
		<title>I Want To Be Known For… &#8211; Freelance Introspection</title>
		<link href="https://stevenwoodson.com/blog/i-want-to-be-known-for-freelance-introspection"/>
		<published>2023-07-14T19:24:17.000Z</published>
		<updated>2026-04-01T12:39:20.000Z</updated>
		<id>https://stevenwoodson.com/blog/i-want-to-be-known-for-freelance-introspection</id>
    <summary>This is a follow up post from the &amp;#8220;Ideal Client and Project&amp;#8221;, continuing my introspective journey as I iterate on my various online presences that I&amp;#8217;d roughly call my &amp;#8220;personal brand&amp;#8221;.</summary>
		<content type="html">&lt;img src=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/07/I-Want-To-Be-Known-For.-Freelance-Introspection.png&quot; alt=&quot;&quot;&gt;
&lt;p&gt;This is a follow up post from &lt;a href=&quot;https://stevenwoodson.com/blog/the-ideal-client-and-project-freelance-introspection/&quot;&gt;The Ideal Client and Project – Freelance Introspection&lt;/a&gt;, continuing my introspective journey as I iterate on my various online presences that I&amp;#8217;d roughly call my &amp;#8220;personal brand&amp;#8221;.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Taking a Step Back&lt;/h2&gt;



&lt;p&gt;In writing that previous post I realized that before I can think deeply about an &amp;#8220;ideal&amp;#8221; client or project I needed to work on who I want to be as a freelance web developer some more first.&lt;/p&gt;



&lt;p&gt;Some new questions emerged:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;How can I quantify an ideal project without first determining &lt;strong&gt;what I&amp;#8217;d professionally like to focus on&lt;/strong&gt;?&lt;/li&gt;



&lt;li&gt;How do I quantify an ideal client? Then, how do I &lt;strong&gt;make myself discoverable&lt;/strong&gt; to them?&lt;/li&gt;



&lt;li&gt;Conversely, how can I &lt;strong&gt;position myself as an ideal candidate&lt;/strong&gt; for them?&lt;/li&gt;
&lt;/ul&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Compiling Feedback&lt;/h3&gt;



&lt;p&gt;I&amp;#8217;ve had the pleasure of speaking with several people from all over the world these past few weeks. Because most of these conversations centered around consulting, they naturally touched on all these topics.&lt;/p&gt;



&lt;p&gt;In order to make sense of all those meeting notes, I&amp;#8217;ve compiled people&amp;#8217;s suggestions under three general topics, each relating to one of the questions above.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Professional Focus&lt;/h2&gt;



&lt;p class=&quot;has-medium-font-size&quot;&gt;I think this is similar to the common phrase &lt;strong&gt;Go to Market Strategy&lt;/strong&gt;, having a solid understanding of what client size, industry, project types, and focus areas I&amp;#8217;m best suited for (and most interested in) pursuing. Only then will I effectively be able to craft a message that reaches and resonates with them.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Set a Focus Area&lt;/h3&gt;



&lt;p&gt;Being a generalist is often a great quality to have, but just about every topic noted below gets harder without a particular focus point to start with.&lt;/p&gt;



&lt;blockquote class=&quot;wp-block-quote is-layout-flow wp-block-quote-is-layout-flow&quot;&gt;
&lt;p&gt;&amp;#8220;What are you known for?&amp;#8221; Ideally, you&amp;#8217;re marketing yourself for the work you enjoy most and are known for.&lt;/p&gt;
&lt;cite&gt;&lt;a href=&quot;https://www.themuse.com/coaches/leto-papadopoulos&quot;&gt;Leto Papadopoulos&lt;/a&gt;, &lt;a href=&quot;https://www.themuse.com/coaches/leto-papadopoulos&quot;&gt;Career Coach with the Muse&lt;/a&gt;&lt;/cite&gt;&lt;/blockquote&gt;



&lt;blockquote class=&quot;wp-block-quote is-layout-flow wp-block-quote-is-layout-flow&quot;&gt;
&lt;p&gt;Be focused and niche down, be specific about &amp;#8220;here&amp;#8217;s what I do&amp;#8221;&lt;/p&gt;
&lt;cite&gt;&lt;a href=&quot;https://www.gianoglio.com/&quot;&gt;Jim Gianoglio&lt;/a&gt;, &lt;a href=&quot;https://www.cauzle.com/&quot;&gt;Cauzle Analytics&lt;/a&gt;&lt;/cite&gt;&lt;/blockquote&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Make it Memorable&lt;/h3&gt;



&lt;p&gt;This is all about the messaging of that focus area so it&amp;#8217;s memorable to potential clients. Even if they&amp;#8217;re not looking now, they have a greater chance of remembering me later when they are.&lt;/p&gt;



&lt;blockquote class=&quot;wp-block-quote is-layout-flow wp-block-quote-is-layout-flow&quot;&gt;
&lt;p&gt;Let them picture you doing the work &amp;#8211; share an example of how you’ve done this.&amp;nbsp;&lt;/p&gt;
&lt;cite&gt;Leto Papadopoulos&lt;/cite&gt;&lt;/blockquote&gt;



&lt;blockquote class=&quot;wp-block-quote is-layout-flow wp-block-quote-is-layout-flow&quot;&gt;
&lt;p&gt;&amp;#8220;Why are you doing this?&amp;#8221; Need to be able to explain quickly and succinctly.&lt;/p&gt;



&lt;p&gt;Get better at explaining what you enjoy most and how that benefits them and their company&lt;/p&gt;
&lt;cite&gt;&lt;a href=&quot;https://bento.me/hereinthehive&quot;&gt;Dan Donald, Here In The Hive&lt;/a&gt;&lt;/cite&gt;&lt;/blockquote&gt;



&lt;blockquote class=&quot;wp-block-quote is-layout-flow wp-block-quote-is-layout-flow&quot;&gt;
&lt;p&gt;What&amp;#8217;s the one sentence tagline? &amp;#8220;I want you to think of &lt;em&gt;this&lt;/em&gt; when you think of &lt;em&gt;me&lt;/em&gt;&amp;#8220;&lt;/p&gt;
&lt;cite&gt;Jim Gianoglio&lt;/cite&gt;&lt;/blockquote&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Discoverability&lt;/h2&gt;



&lt;p class=&quot;has-medium-font-size&quot;&gt;I could have the most perfect website and LinkedIn profile the world has ever seen, but it wouldn&amp;#8217;t mean much if no one is looking at it.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Blog Posts&lt;/h3&gt;



&lt;blockquote class=&quot;wp-block-quote is-layout-flow wp-block-quote-is-layout-flow&quot;&gt;
&lt;p&gt;It&amp;#8217;s no secret that regular blogging has a huge impact on SEO, and searching is a great way to get more serendipitous connections from people that need exactly what I can provide.&lt;/p&gt;



&lt;p&gt;Consider using [blog post] titles that are things non-tech people would search for, could lead to projects.&amp;nbsp;&lt;/p&gt;
&lt;cite&gt;Leto Papadopoulos&lt;/cite&gt;&lt;/blockquote&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Networking&lt;/h3&gt;



&lt;p&gt;There&amp;#8217;s no substitute for good ol&amp;#8217; fashioned networking. Lately I&amp;#8217;ve been finding some success especially with LinkedIn, less so with other platforms. Here&amp;#8217;s some advice from Leto about other networking avenues to explore.&lt;/p&gt;



&lt;blockquote class=&quot;wp-block-quote is-layout-flow wp-block-quote-is-layout-flow&quot;&gt;
&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;strong&gt;Think locally&lt;/strong&gt;: who can you meet with? Get in front of people!&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;Givers gain&lt;/strong&gt;: come out of every meeting with something. (idea, lead, introduction), but also see how you can help others. Be a resource.&lt;/li&gt;



&lt;li&gt;Set &lt;strong&gt;regular meetings&lt;/strong&gt; with people you connect with&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;Reach out to old colleagues&lt;/strong&gt; from previous roles. Let them know you would like to help with project work &amp;#8211; share a couple areas you can help with.&lt;/li&gt;
&lt;/ul&gt;
&lt;cite&gt;Leto Papadopoulos&lt;/cite&gt;&lt;/blockquote&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Entrepreneurship Through Acquisition (ETA)&lt;/h3&gt;



&lt;p&gt;Essentially, purchasing an established company and using that existing network to hit the ground running more quickly. While I&amp;#8217;m on the fence about whether I&amp;#8217;d be interested in this personally, it does seem a great way to quickly ramp up in the discoverability department.&lt;/p&gt;



&lt;p&gt;Thanks to Jim Gianoglio for introducing me to this topic. Jim also shared a couple resources to start with if there&amp;#8217;s interest in learning more:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://bookshop.org/a/90267/9781544501130&quot; rel=&quot;sponsored nofollow&quot;&gt;Buy Then Build: How Acquisition Entrepreneurs Outsmart the Startup Game&lt;/a&gt; book by Walker Deibel&lt;/li&gt;



&lt;li&gt;&lt;a href=&quot;https://www.gsb.stanford.edu/faculty-research/case-studies/models-entrepreneurship-through-acquisition&quot; rel=&quot;nofollow&quot;&gt;Models of Entrepreneurship Through Acquisition&lt;/a&gt; Stanford Research Study by R. Ellis, Lacey Wismer&lt;/li&gt;
&lt;/ul&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Positioning as an Ideal Candidate&lt;/h2&gt;



&lt;p class=&quot;has-medium-font-size&quot;&gt;I think this is often referred to as &lt;strong&gt;Value Proposition&lt;/strong&gt;. As a freelance web developer, it&amp;#8217;s important that my expertise, personality, and availability are front and center when potential clients come across my website or social profiles.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Speak in Outcomes&lt;/h3&gt;



&lt;p&gt;It&amp;#8217;s great to list technologies I&amp;#8217;m proficient in and places I&amp;#8217;ve worked, but those don&amp;#8217;t speak to what I can offer in terms of outcomes. It&amp;#8217;s an understanding of the potential outcomes that people really need in order to see me as an ideal candidate.&lt;/p&gt;



&lt;blockquote class=&quot;wp-block-quote is-layout-flow wp-block-quote-is-layout-flow&quot;&gt;
&lt;p&gt;&amp;#8220;What is the outcome for them?&amp;#8221; &amp;#8220;I do [x] so you get [y]&amp;#8221; &amp;#8211; What would a business leader need to hear to help them understand the benefit of what you&amp;#8217;re offering?&lt;/p&gt;
&lt;cite&gt;Dan Donald&lt;/cite&gt;&lt;/blockquote&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;References&lt;/h3&gt;



&lt;p&gt;While it&amp;#8217;s difficult sometimes to speak freely about past projects, there&amp;#8217;s no substitute for a strong case study. As Razvan points out below, even if you need to anonymize some of it.&lt;/p&gt;



&lt;blockquote class=&quot;wp-block-quote is-layout-flow wp-block-quote-is-layout-flow&quot;&gt;
&lt;p&gt;may be even with work you did for previous employers though as those deliverables are not your IPR anymore [you] won&amp;#8217;t be able to name, but you can still reference the client problem, solution, and benefits.&lt;/p&gt;
&lt;cite&gt;Razvan Popescu&lt;/cite&gt;&lt;/blockquote&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Testimonials&lt;/h3&gt;



&lt;p&gt;Quotes from peers and past clients can go a long way towards demonstrating expertise. They can highlight personal qualities better than case studies, and gives a little window into what it&amp;#8217;s like to work with me.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Work in Progress&lt;/h2&gt;



&lt;p&gt;Just like I mentioned in the last post, I&amp;#8217;m still figuring this all out and don&amp;#8217;t have great answers yet. But I think I&amp;#8217;m getting closer! &lt;/p&gt;



&lt;p&gt;Writing about my progress has helped me make sense of all my thoughts, compile the great feedback from others, and get myself on the right track.&lt;/p&gt;



&lt;p&gt;In the past weeks I&amp;#8217;ve&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;Added a &lt;a href=&quot;https://stevenwoodson.com/testimonials/&quot;&gt;testimonials section&lt;/a&gt;, as well as a couple randomized on some key pages.&lt;/li&gt;



&lt;li&gt;Tweaked some copy on &lt;a href=&quot;https://stevenwoodson.com/services/&quot;&gt;Services&lt;/a&gt;, added a dedicated &lt;a href=&quot;https://stevenwoodson.com/resume/&quot;&gt;Resume&lt;/a&gt; page, and shared my &lt;a href=&quot;https://stevenwoodson.com/mission-statement/&quot;&gt;personal &amp;amp; professional Mission Statement&lt;/a&gt;.&lt;/li&gt;



&lt;li&gt;Compiled a list of blog post ideas, some more (like this) about my freelance process and several others that deep dive into technical web development topics like Eleventy and Web Components&lt;a href=&quot;https://stevenwoodson.com/blog/the-ideal-client-and-project-freelance-introspection/#how-about-you&quot;&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;A sincere and enthusiastic thank you for reading all the way to the end. &lt;strong&gt;While I have your attention&lt;/strong&gt;, I’m still looking for freelance web development opportunities so if you have something coming up I’d love to hear more about&amp;nbsp;&lt;a href=&quot;https://stevenwoodson.com/project-inquiry/&quot;&gt;how I can help bring it to life&lt;/a&gt;!&lt;a href=&quot;https://stevenwoodson.com/blog/the-ideal-client-and-project-freelance-introspection/#how-about-you&quot;&gt;&lt;/a&gt;&lt;/p&gt;

    </content>
	</entry>
  
	<entry>
		<title>Going Independent &#8211; Week Two Check in</title>
		<link href="https://stevenwoodson.com/blog/going-independent-week-two-check-in"/>
		<published>2023-07-10T20:35:46.000Z</published>
		<updated>2026-04-01T12:38:26.000Z</updated>
		<id>https://stevenwoodson.com/blog/going-independent-week-two-check-in</id>
    <summary>Week two check in on my journey towards going independent. Setting up more operations, networking for freelance web development projects, and took a couple days off.</summary>
		<content type="html">&lt;img src=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/07/Going-Independent-Week-Two-Check-in.png&quot; alt=&quot;&quot;&gt;
&lt;p&gt;I&amp;#8217;m following &lt;a href=&quot;https://stevenwoodson.com/blog/going-independent-week-one-check-in/&quot;&gt;the same format I did in last weeks check in&lt;/a&gt;, it&amp;#8217;s the simplest way to jot things down quickly and seemed to be well received as &amp;#8220;easily parsable&amp;#8221;.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;What Happened Last Week&lt;/h2&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;I spent &lt;strong&gt;a whole day this week on one proposal draft&lt;/strong&gt;, I know it&amp;#8217;s going to get easier as I build up my process but I still was surprised at how much effort was involved!&lt;/li&gt;



&lt;li&gt;Had &lt;strong&gt;a few scheduled calls&lt;/strong&gt; with folks. One was with someone interested in hearing more about what services I can provide to potentially collaborate together, one with another person who decided to start their own company after being laid off as well. Some really nice chats!&lt;/li&gt;



&lt;li&gt;Based on some recommendations, I&amp;#8217;ve finally joined the modern social world and &lt;strong&gt;have been giving some Discord servers a try&lt;/strong&gt;. So far I&amp;#8217;ve joined a few freelance and frontend web development related servers, I need to manage the notifications from them but so far it&amp;#8217;s been nice!&lt;/li&gt;



&lt;li&gt;I also finally &lt;strong&gt;got an invite to &lt;a href=&quot;https://bsky.app/profile/stevenwoodson.bsky.social&quot; rel=&quot;nofollow&quot;&gt;BlueSky&lt;/a&gt;&lt;/strong&gt;, and am going to give that a try for a while. &lt;/li&gt;



&lt;li&gt;&lt;strong&gt;Blogged about &lt;a href=&quot;https://stevenwoodson.com/blog/the-ideal-client-and-project-freelance-introspection/&quot;&gt;The Ideal Client and Project&lt;/a&gt;&lt;/strong&gt; after bringing it up on some social media platforms, I received some great feedback on that and wanted to share that as well.&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;Started a new weekly plan&lt;/strong&gt; and have used it once so far, it feels better now that it focuses on some of my new (and renewed) professional goals around &lt;strong&gt;my freelance business and Be Inclusive&lt;/strong&gt;.&lt;/li&gt;



&lt;li&gt;I took Tuesday off to enjoy time with family for the 4th of July holiday. Also my dad arrived in town Friday so I took that day and the whole weekend away from the keyboard and enjoying that time too. Time away is so important to recharge and come back rested and ready to go.&lt;/li&gt;
&lt;/ul&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;What&amp;#8217;s Coming Up This Week&lt;/h2&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;I heard back about that&amp;nbsp;&lt;strong&gt;proposal for a lead&lt;/strong&gt;&amp;nbsp;that came in the week before last, need to get that updated and back out to them early this week.&lt;/li&gt;



&lt;li&gt;I&amp;#8217;m a terrible son and didn&amp;#8217;t get any work in on&amp;nbsp;&lt;strong&gt;rebuilding that site for my dad&lt;/strong&gt;, prioritizing it this week!&lt;/li&gt;



&lt;li&gt;I’ve done some work on&amp;nbsp;&lt;strong&gt;strengthening and refining my message&lt;/strong&gt;, that&amp;#8217;s what led to the ideal client and project blog post. I do, however, feel like I&amp;#8217;m just getting started and need to give this some more time and attention before making content changes.&lt;/li&gt;



&lt;li&gt;Besides this one, I&amp;#8217;ve got &lt;strong&gt;one other freelance introspection blog post&lt;/strong&gt; in a draft and scheduled later this week. I&amp;#8217;ve really been leaning into sharing more of my process. It&amp;#8217;s been serving multiple goals, I think best when I write stuff down so I get more clarity, and I also need to keep up some content authoring so I can periodically mention that I&amp;#8217;m still open to freelance projects.&lt;/li&gt;



&lt;li&gt;Continue &lt;strong&gt;iterating on that weekly plan&lt;/strong&gt; I restarted, and I plan on getting back into daily gratitude journaling and more in-depth review now that I have a weekly plan back in place.&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;Thanks for reading, onward and upward!&lt;/p&gt;



&lt;p&gt;If you&amp;#8217;re enjoying this format, why not have a read of &lt;a href=&quot;https://stevenwoodson.com/blog/going-independent-week-three-check-in/&quot;&gt;the Week Three Check In&lt;/a&gt;?&lt;/p&gt;

    </content>
	</entry>
  
	<entry>
		<title>The Ideal Client and Project &#8211; Freelance Introspection</title>
		<link href="https://stevenwoodson.com/blog/the-ideal-client-and-project-freelance-introspection"/>
		<published>2023-07-06T14:19:25.000Z</published>
		<updated>2023-08-10T17:23:43.000Z</updated>
		<id>https://stevenwoodson.com/blog/the-ideal-client-and-project-freelance-introspection</id>
    <summary>There&amp;#8217;s a surprising amount of introspection required when you decide to go freelance. One is to figure out &amp;#8220;the ideal&amp;#8221; client and project. Here&amp;#8217;s my process so far.</summary>
		<content type="html">&lt;img src=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/07/The-Ideal-Client-and-Project-Freelance-Introspection.png&quot; alt=&quot;&quot;&gt;
&lt;p&gt;I&amp;#8217;ve realized there&amp;#8217;s a surprising amount of introspection required when you decide to go freelance. One is to figure out &amp;#8220;the ideal&amp;#8221; client and project.&lt;/p&gt;



&lt;p&gt;When working at a company we don&amp;#8217;t get the luxury of considering these ideals, you&amp;#8217;re assigned a project and that&amp;#8217;s that.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Getting Started&lt;/h2&gt;



&lt;p&gt;Now that I&amp;#8217;m actively looking for opportunities, it&amp;#8217;s important to have those ideals in mind so I can try to target those companies and projects that would be the best fit for me.&lt;/p&gt;



&lt;p&gt;I&amp;#8217;m gonna be honest, I still don&amp;#8217;t have a great answer to this yet. Since I committed to sharing more of my process of going freelance, I&amp;#8217;m posting my process now and hope to have an update soon with a clearer answer.&lt;/p&gt;



&lt;p&gt;Here&amp;#8217;s some questions I&amp;#8217;m mulling over so far, I feel like the answers of these will help me get there:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;What are some recent projects that made me feel the most proud when it was completed?&lt;/li&gt;



&lt;li&gt;What kind of project teams did I tend to gel with the best?&lt;/li&gt;



&lt;li&gt;What technologies were in use that made me the most fulfilled as a developer?&lt;/li&gt;



&lt;li&gt;What kinds of clients inspired me to give my all every day?&lt;/li&gt;
&lt;/ul&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Taking it Social&lt;/h2&gt;



&lt;p&gt;An earlier draft of this went out on &lt;a href=&quot;https://www.linkedin.com/feed/update/urn:li:activity:7082439318253813761/&quot; rel=&quot;nofollow&quot;&gt;LinkedIn&lt;/a&gt;, &lt;a href=&quot;https://twitter.com/stevenwoodson/status/1676674141242511388&quot; rel=&quot;nofollow&quot;&gt;Twitter&lt;/a&gt;, and &lt;a href=&quot;https://mastodon.online/@stevenwoodson/110663116338677052&quot; rel=&quot;nofollow&quot;&gt;Mastodon&lt;/a&gt;. I got a couple of very interesting replies that I&amp;#8217;m going to add to my own process, sharing them below so you can give them a go as well.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Alistair Shepherd&lt;/h3&gt;



&lt;aside class=&quot;wp-block-create-block-aside&quot;&gt;
&lt;p&gt;Unrelated, but I gotta &lt;a href=&quot;https://alistairshepherd.uk/&quot;&gt;highlight the creativity of Alistair&amp;#8217;s website&lt;/a&gt;, especially the header. If you&amp;#8217;re interested in the process that led to that amazing result check out &lt;a href=&quot;https://2022.stateofthebrowser.com/speaker/alistair-shepherd/&quot;&gt;his talk from the 2022 State of the Browser conference&lt;/a&gt;.&lt;/p&gt;
&lt;/aside&gt;



&lt;p&gt;Many thanks to Alistair for sharing this gem via Mastodon.&lt;/p&gt;



&lt;iframe loading=&quot;lazy&quot; src=&quot;https://mastodon.scot/@accudio/110663266961572444/embed&quot; class=&quot;mastodon-embed&quot; style=&quot;max-width: 100%; border: 0&quot; width=&quot;400&quot; height=&quot;450&quot; allowfullscreen=&quot;allowfullscreen&quot;&gt;&lt;/iframe&gt;



&lt;p&gt;It really makes sense to focus on the people and industries that resonate and provide more satisfaction. Something I&amp;#8217;ll be keeping in mind in the work ahead.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Jon Gibbins&lt;/h3&gt;



&lt;aside class=&quot;wp-block-create-block-aside&quot;&gt;
&lt;p&gt;This advice from Jon really highlights his commitment to making the digital world better for all people and for our planet with &lt;a href=&quot;https://digitalasitshouldbe.com/&quot;&gt;As It Should Be&lt;/a&gt;.&lt;/p&gt;
&lt;/aside&gt;



&lt;blockquote class=&quot;wp-block-quote is-layout-flow wp-block-quote-is-layout-flow&quot;&gt;
&lt;p&gt;I’ve certainly found the focus that comes from such introspection to be beneficial to also focussing my marketing efforts and value proposition.&lt;/p&gt;



&lt;p&gt;One exercise I did a few years back was to put a line down the middle of a piece of paper, then write the things / values that matter most to me personally on one side, and the things I think matter most in my work or even the main kinds of projects / clients I’ve worked on / with. Then draw lines from one side to another to connect items that are linked to find common ground. These are areas of focus. Anything else should show where your work is not meeting your personal values.&lt;/p&gt;



&lt;p&gt;You can do something similar with Ikigai exercises.&lt;/p&gt;
&lt;cite&gt;Jon Gibbins &lt;a href=&quot;https://www.linkedin.com/feed/update/urn:li:activity:7082439318253813761?commentUrn=urn%3Ali%3Acomment%3A%28activity%3A7082439318253813761%2C7082446474705293313%29&quot; rel=&quot;nofollow&quot;&gt;via LinkedIn comment&lt;/a&gt;&lt;/cite&gt;&lt;/blockquote&gt;



&lt;p&gt;I really like this mentality of starting with my own values and letting that guide the process of finding common ground for clients and projects.&lt;/p&gt;



&lt;p&gt;I had recently shared my &lt;a href=&quot;https://stevenwoodson.com/mission-statement/&quot;&gt;personal and professional mission statement&lt;/a&gt; that I&amp;#8217;ve been iterating on for over 5 years, I&amp;#8217;m going to try this suggestion with that as my starting point.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;How About You?&lt;/h2&gt;



&lt;p&gt;Have you done an exercise like this before? If you have some ideas to help me bring this more into focus I&amp;#8217;d love to hear it!&lt;/p&gt;



&lt;p&gt;By the way, I&amp;#8217;m still looking for freelance web dev opportunities so if you have something coming up I&amp;#8217;d love to hear more about &lt;a href=&quot;https://stevenwoodson.com/project-inquiry/&quot;&gt;how I can help bring it to life&lt;/a&gt;!&lt;/p&gt;

    </content>
	</entry>
  
	<entry>
		<title>Going Independent &#8211; Week One Check in</title>
		<link href="https://stevenwoodson.com/blog/going-independent-week-one-check-in"/>
		<published>2023-07-02T15:59:55.000Z</published>
		<updated>2023-08-10T17:23:43.000Z</updated>
		<id>https://stevenwoodson.com/blog/going-independent-week-one-check-in</id>
    <summary>A week since I shared I&amp;#8217;m going freelance, which was a week after being laid off. Keeping that weekly cadence for a bit as I continue to figure things out.</summary>
		<content type="html">&lt;img src=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/07/Going-Independent-Week-One-Check-in.png&quot; alt=&quot;&quot;&gt;
&lt;p&gt;It&amp;#8217;s been a week since I shared &lt;a href=&quot;https://stevenwoodson.com/blog/steve-woodson-independent-web-development-consultant/&quot;&gt;I&amp;#8217;m focusing on full time freelance web dev&lt;/a&gt;, which was about a week after &lt;a href=&quot;https://stevenwoodson.com/blog/i-was-laid-off-now-what/&quot;&gt;being let go from my previous full time position&lt;/a&gt;.&lt;/p&gt;



&lt;p&gt;Thanks to so many of you that expressed appreciation for the openness, and those that interest in hearing more about my process to going full time freelance, I thought I may try to keep that weekly cadence for a bit as I continue to figure things out.&lt;/p&gt;



&lt;p&gt;So, here&amp;#8217;s a quick bulleted check in after one week!&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;What Happened Last Week&lt;/h2&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Operations setup&lt;/h3&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;I&amp;#8217;ve got an LLC, business insurance, a PO Box, and business checking account ready to go.&lt;/li&gt;



&lt;li&gt;Opted for &lt;strong&gt;an &lt;a href=&quot;https://www.plutio.com/&quot; rel=&quot;nofollow&quot;&gt;all-in-one project management SaaS called Plutio&lt;/a&gt;&lt;/strong&gt; that will allow me to handle scheduling meetings (like Calendly) as well as manage proposals, contracts, projects, and invoices. I tried several others first and this was the one that clicked best with how I like to stay organized, and offered all the features I need right now.&lt;/li&gt;



&lt;li&gt;I set up &lt;a href=&quot;https://workspace.google.com/&quot; rel=&quot;nofollow&quot;&gt;&lt;strong&gt;a Google Workspace&lt;/strong&gt;&lt;/a&gt; too, I wanted to have some separation between my personal email and files and those for my business. I also had trouble with emails arriving slowly when set up using POP3 fetching as part of my personal account.&lt;/li&gt;



&lt;li&gt;I have a fairly detailed &lt;strong&gt;spreadsheet that helped me figure out what my rates should be&lt;/strong&gt; in order to cover expenses and replace my salary. I&amp;#8217;ve figured out how that translates within a few popular billing models (like flat rate, retainer, etc.) too.&lt;/li&gt;



&lt;li&gt;I&amp;#8217;ve been working on some &lt;strong&gt;templates for proposals, contracts, and generally getting myself organized&lt;/strong&gt; to keep track of leads and conversations. I&amp;#8217;m thinking this may be a good topic for a future post. Let me know if you&amp;#8217;re interested in hearing more!&lt;/li&gt;



&lt;li&gt;I&amp;#8217;ve &lt;strong&gt;scrapped all my old weekly planning&lt;/strong&gt;, and whole sections of my todo app. It&amp;#8217;s amazing how much of ones life is dictated by their job!&lt;/li&gt;
&lt;/ul&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Networking &amp;amp; Outreach&lt;/h3&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;I&amp;#8217;ve only &lt;strong&gt;posted once on LinkedIn, Twitter, and Mastodon&lt;/strong&gt; this past week, I&amp;#8217;m honestly not sure how often I want to post moving forward, but I know I&amp;#8217;m never going to be a once-a-day content generating machine that I&amp;#8217;ve seen on these platforms. It takes a lot of effort and I just can&amp;#8217;t get past the feeling like I&amp;#8217;m repeating myself too much.&lt;/li&gt;



&lt;li&gt;I&amp;#8217;ve had a few great &lt;strong&gt;chats with other folks looking for their next adventure too&lt;/strong&gt;. It&amp;#8217;s been nice to work through some feelings, share a bit about ourselves, and help each other get excited for the future. I&amp;#8217;m still open to chat if you&amp;#8217;re in this situation as well, reach out so we can set up some time!&lt;/li&gt;



&lt;li&gt;Either via email or social messaging I&amp;#8217;ve reached out to &lt;strong&gt;about 10 individuals or agencies that I wanted to network with&lt;/strong&gt; and chat about freelancing in general. I&amp;#8217;ve had a really nice conversation with two (thank you both!) and hopefully more coming soon. If you&amp;#8217;re interested in talking shop about freelance, consulting, and otherwise working solo please do reach out as well!&lt;/li&gt;



&lt;li&gt;I&amp;#8217;ve started some more &lt;strong&gt;targeted outreach for my digital accessibility auditing SaaS app &lt;a href=&quot;https://beinclusive.app/&quot;&gt;Be Inclusive&lt;/a&gt; too&lt;/strong&gt;, another 10 or so went out last week and two have responded back so far!&lt;/li&gt;



&lt;li&gt;Also, happy to report I&amp;#8217;ve had &lt;strong&gt;a few project leads&lt;/strong&gt; come up, waiting on a meeting for one and need to work on a proposal for another. The third ended up being too expensive so we agreed to not move forward &amp;#8211; a great reminder to insist on a budget upfront so you know what constraints you&amp;#8217;re working under.&lt;/li&gt;
&lt;/ul&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;What&amp;#8217;s Coming Up This Week&lt;/h2&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;I&amp;#8217;m going to put some time into &lt;strong&gt;a proposal for a lead&lt;/strong&gt; that came in last week.&lt;/li&gt;



&lt;li&gt;I&amp;#8217;ve received several great pieces of advice from a few of the outreach conversations with other freelancers. I will be working on &lt;strong&gt;strengthening and refining my message&lt;/strong&gt;, in particular around the services I provide. I&amp;#8217;ve been so used to moulding myself to the project as I never had control over what I was assigned, now that I do I need to put more thought into the ideal projects for my skillset.&lt;/li&gt;



&lt;li&gt;I&amp;#8217;m shooting for &lt;strong&gt;at least one blog post a week&lt;/strong&gt;, I don&amp;#8217;t want to go overboard right away and feel like one a week should be manageable for a while. This one may count for this week, though I have some drafts I could put some time into as well.&lt;/li&gt;



&lt;li&gt;I&amp;#8217;ve got to get going on &lt;strong&gt;rebuilding a site for my dad&lt;/strong&gt;, looking forward to trying out a couple new things and learning in that process as well.&lt;/li&gt;



&lt;li&gt;I mentioned above that I scrapped my weekly planning, now that I&amp;#8217;ve hit the reset button I&amp;#8217;m going to &lt;strong&gt;build up a weekly plan that focuses more on my freelance business and Be Inclusive&lt;/strong&gt; in addition to the personal and life stuff I had previously. Excited to get that started and iterate over time based on what works for me.  &lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;I&amp;#8217;m rather new to &amp;#8220;working in the open&amp;#8221; so I&amp;#8217;m just doing what feels right. What do you think, do you like this format? Got any tips for an old dev trying new things?&lt;/p&gt;



&lt;p&gt;If you&amp;#8217;re enjoying this format, why not have a read of &lt;a href=&quot;https://stevenwoodson.com/blog/going-independent-week-two-check-in/&quot;&gt;the Week Two Check In&lt;/a&gt;?&lt;/p&gt;

    </content>
	</entry>
  
	<entry>
		<title>Steve Woodson &#8211; Independent Web Development Consultant</title>
		<link href="https://stevenwoodson.com/blog/steve-woodson-independent-web-development-consultant"/>
		<published>2023-06-26T11:14:57.000Z</published>
		<updated>2023-08-10T17:23:44.000Z</updated>
		<id>https://stevenwoodson.com/blog/steve-woodson-independent-web-development-consultant</id>
    <summary>I&amp;#8217;m manifesting a decade old dream and moving towards independent web development consulting. Here&amp;#8217;s some reflection of the past week, and how I came to choose this exciting next adventure.</summary>
		<content type="html">&lt;img src=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/06/Steve-Woodson-Independent-Web-Development-Consultant.png&quot; alt=&quot;&quot;&gt;
&lt;h2 class=&quot;wp-block-heading&quot;&gt;TL;DR&lt;/h2&gt;



&lt;p&gt;I&amp;#8217;m terrible at burying the lede (as it&amp;#8217;s the title of this post) so I&amp;#8217;m starting with the TL;DR. I&amp;#8217;m venturing out on my own offering freelance web development consulting!&lt;/p&gt;



&lt;p&gt;Check out:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://stevenwoodson.com/services/&quot;&gt;Services&lt;/a&gt; for my core values, how I&amp;#8217;m different, and what web development services I can offer.&lt;/li&gt;



&lt;li&gt;&lt;a href=&quot;https://stevenwoodson.com/testimonials/&quot;&gt;Testimonials&lt;/a&gt; for some kind words folks have recently shared about me, my work, and my leadership.&lt;/li&gt;



&lt;li&gt;&lt;a href=&quot;https://stevenwoodson.com/project-inquiry/&quot;&gt;Project Inquiry&lt;/a&gt; to get in contact about an upcoming project I can help you bring to life!&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;The rest of this post shares some reflections on the past week, and goes into more detail about how I got to the point where I&amp;#8217;m ready to go independent, and what else I have planned for the near future.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Reflecting on the Last Week&lt;/h2&gt;



&lt;p&gt;One week ago today I &lt;a href=&quot;https://stevenwoodson.com/blog/i-was-laid-off-now-what/&quot;&gt;shared that I was laid off&lt;/a&gt;. I didn&amp;#8217;t realize at the time how much this would resonate with people and am deeply humbled by the response.&lt;/p&gt;



&lt;p&gt;Hundreds of reactions, dozens of comments, and just as many direct messages and emails. I&amp;#8217;ve even got a few meetings scheduled with folks just to chat about our experiences and talk about what gets us excited about the future.&lt;/p&gt;



&lt;p&gt;The offer made last week about being open to chat still stands. If you were recently affected by workforce reductions, are struggling to find the next adventure, or just want to chat about the difficulties we&amp;#8217;ve all been facing these past few years.&lt;/p&gt;



&lt;p&gt;Feel free to DM me on a social platform or &lt;a href=&quot;mailto:me@stevenwoodson.com&quot;&gt;send me an email&lt;/a&gt;. We can even book a half hour chat if you prefer to talk face to face. Let&amp;#8217;s make some lemonade from these lemons!&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;The Dream&lt;/h2&gt;



&lt;p&gt;A few years into my web development career, somewhere around 2005, I started to dream about venturing out on my own. I would even go so far as to take on the odd freelance job when the opportunity would arise.&lt;/p&gt;



&lt;p&gt;But I was a long way from taking a chance on full time freelance web development consulting.&lt;/p&gt;



&lt;p&gt;I knew there was so much I could accomplish but also that the work itself is only one (albeit very important) part. I recognized that there&amp;#8217;s marketing, sales, and business management that goes into it as well. Frankly, I was terrified of all three.&lt;/p&gt;



&lt;p&gt;The most visceral reaction, however, was the scarcity mindset. The fear that I&amp;#8217;d get everything set up, put myself out there, and nothing happened. I wouldn&amp;#8217;t make any digital marketing connections, wouldn&amp;#8217;t find clients that saw my potential as a web application developer, wouldn&amp;#8217;t land any web dev projects.&lt;/p&gt;



&lt;p&gt;It was enough to deter me from even giving it a try. So, for over 15 years this dream sat there in the back of my mind.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Why Now?&lt;/h2&gt;



&lt;p&gt;It&amp;#8217;s amazing how much time one has to sit and think when not engrossed in day to day work. I spent a lot of that time this past week thinking about&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;what I&amp;#8217;ve yet to accomplish&lt;/li&gt;



&lt;li&gt;what I want my days to look like&lt;/li&gt;



&lt;li&gt;and what is going to get me excited to get up out of bed every morning&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;I knew right away, there&amp;#8217;s still that one big goal that checks all three of these boxes. Maybe losing my job is the universe telling me to just go for it already?&lt;/p&gt;



&lt;p&gt;I had shared that thought privately with several colleagues, friends, and family and every single one had something positive to say. Here are some of them that really resonated with me:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&amp;#8220;What you water will grow&amp;#8221;&lt;/li&gt;



&lt;li&gt;&amp;#8220;Sometimes many of us need that kick in the ass.&amp;#8221;&lt;/li&gt;



&lt;li&gt;&amp;#8220;This is definitely a sign from the universe for the push you needed [&amp;#8230;] everything happens for a reason!&amp;#8221;&lt;/li&gt;



&lt;li&gt;&amp;#8220;That would be so great! One door closes, another opens&amp;#8221;&lt;/li&gt;



&lt;li&gt;&amp;#8220;That&amp;#8217;s great to hear on independent consulting. I&amp;#8217;m big on universal signals, too!&amp;#8221;&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;I&amp;#8217;ve grown a lot in the last handful of years, not only as a developer but as a more confident leader, empathetic collaborator, and accessibility focused technologist. I&amp;#8217;ve worked hard building a personal brand and now I feel ready to share all that experience directly with client teams as an independent consultant.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Making it a Reality&lt;/h2&gt;



&lt;p&gt;I&amp;#8217;ve spent a considerable amount of time working through details and getting everything set up. In addition to the site updates noted at the beginning of this post I&amp;#8217;ve also secured&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;a registered LLC, in partnership with my wife&lt;/li&gt;



&lt;li&gt;business insurance, including cyber coverage&lt;/li&gt;



&lt;li&gt;operations details including communications workfows, project management, and contract templates&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;I&amp;#8217;m sure there&amp;#8217;s countless more things that I need to do, but I&amp;#8217;ve got the fundamental aspects ready and am officially &lt;strong&gt;open for business&lt;/strong&gt;!&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;What&amp;#8217;s Next?&lt;/h2&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Thought Leadership&lt;/h3&gt;



&lt;p&gt;Expect to see more &amp;#8220;thought leadership&amp;#8221; from me, even though I am not altogether fond of that term it does serve as a useful shortcut to what I mean. I have a dozen or so blog posts in draft ranging from technology leadership, productivity, and specific solutions to some of the day to day struggles of web developers.&lt;/p&gt;



&lt;p&gt;I&amp;#8217;m still learning when it comes to marketing, but one thing that always resonated with me is the simple fact that sharing what I&amp;#8217;ve learned and helping others builds awareness and connections. That awareness and those connections can often lead to surprising and unexpected things.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Be Inclusive&lt;/h3&gt;



&lt;p&gt;I&amp;#8217;m also working on more roadmap items for my &lt;a href=&quot;https://beinclusive.app/&quot;&gt;SaaS accessibility auditing app Be Inclusive&lt;/a&gt; too. I&amp;#8217;ve often lamented not having as much time as I would have liked to keep breathing more life into that app, make it faster, more compelling, and easier to use for the people that work so hard every day to push accessibility forward.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Got ideas?&lt;/h3&gt;



&lt;p&gt;What else would you like to see more of from me? If you&amp;#8217;ve got some thoughts and ideas I&amp;#8217;m certainly interested in hearing more about it.&lt;/p&gt;



&lt;p&gt;I have some time now, and I&amp;#8217;m so excited to be able to start spending more of it contributing to the web development, application development, and digital accessibility communities that have helped me learn and grow so much these past 20 years.&lt;/p&gt;



&lt;p&gt;&lt;/p&gt;



&lt;p&gt;If you have a project coming up that could use an experienced web developer to help bring it to life, &lt;a href=&quot;https://stevenwoodson.com/project-inquiry/&quot;&gt;get in touch&lt;/a&gt;! I&amp;#8217;d love to hear more about what you have planned, and how I can help. &lt;/p&gt;



&lt;p&gt;Onward and upward! 🎉&lt;/p&gt;

    </content>
	</entry>
  
	<entry>
		<title>I Was Laid Off, Now What?</title>
		<link href="https://stevenwoodson.com/blog/i-was-laid-off-now-what"/>
		<published>2023-06-19T14:33:37.000Z</published>
		<updated>2023-08-10T16:22:26.000Z</updated>
		<id>https://stevenwoodson.com/blog/i-was-laid-off-now-what</id>
    <summary>I was laid off, this post is where I share some of the research I had done, my reflections from the first few days, and an open invite to chat with others affected.</summary>
		<content type="html">&lt;img src=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/06/I-Was-Laid-Off-Now-What.png&quot; alt=&quot;&quot;&gt;
&lt;p&gt;If you&amp;#8217;ve been following the news lately, it&amp;#8217;s been a rather tough time in technology this year. Not a week goes by without news of several major companies letting go of significant percentages of their workforce, so much so that many organizations are &lt;a href=&quot;https://techcrunch.com/2023/06/05/tech-industry-layoffs-2023/&quot; rel=&quot;nofollow&quot;&gt;keeping track of 2023 tech layoffs&lt;/a&gt;.&lt;/p&gt;



&lt;p&gt;Well, as of last week I&amp;#8217;m now one of the tens of thousands affected by these ongoing tech workforce reductions.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Facing The Feelings&lt;/h2&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Grief, Stress, Guilt&lt;/h3&gt;



&lt;p&gt;A few months ago, because of how pervasive layoffs have been industry-wide lately &amp;#8211; including multiple rounds at my previous employer &amp;#8211; I had done some reading on how to best support those affected. &lt;a href=&quot;https://www.verywellmind.com/job-loss-grief-symptoms-coping-5220039&quot; rel=&quot;nofollow&quot;&gt;This article about Job Loss Grief&lt;/a&gt; and &lt;a href=&quot;https://www.helpguide.org/articles/stress/job-loss-and-unemployment-stress.htm&quot; rel=&quot;nofollow&quot;&gt;this one about Unemployment Stress&lt;/a&gt; struck a chord with me and I appreciate their advice even more as I read them again now. I highly recommend them, for folks recently laid off as well as for those interested in helping them. &lt;/p&gt;



&lt;p&gt;Some of the most common advice in these and many other articles was to write about your feelings, share your story, and talk about it. That was the impetus for this post.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;My Immediate Reactions&lt;/h3&gt;



&lt;p&gt;My first thoughts mid-call as I realized what was happening were &amp;#8220;what about my coachees? what about my project team?&amp;#8221; These people are going to be affected by this as well, now needing to carry on with less help and likely more anxiety about their own futures.&lt;/p&gt;



&lt;p&gt;I&amp;#8217;ve come to realize how much of a daily routine and sense of self gets tied up in a job after nearly 8 years. The more difficult realization is how quickly it can all disappear. Get the call and minutes later you&amp;#8217;re locked out. I understand the precaution of it, and I know it&amp;#8217;s never easy for those on the other side too, but there&amp;#8217;s an abrupt coldness to it that still stings.&lt;/p&gt;



&lt;p&gt;As strange as it may sound, I also felt a bit of guilt. Thinking of the tasks I postponed til later. That client project ticket I was actively working on. I should have committed that code, I should have written out some notes about my thought process, I should have been more diligent about taking notes about future plans.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Reflections&lt;/h3&gt;



&lt;p&gt;I had jotted down some reflections as they came to mind, here&amp;#8217;s some of them.&lt;/p&gt;



&lt;p&gt;Several times I&amp;#8217;ve found myself in the Slack app on my phone, my autopilot taking me there to check out the latest messages only to be reminded that it&amp;#8217;s gone.&lt;/p&gt;



&lt;p&gt;I was blown away by the dozen or so folks who reached out to me immediately in these first days, that kind of support is not something I expected nor take for granted. I appreciate each and every one of them.&lt;/p&gt;



&lt;p&gt;I have grown so much as a developer, a mentor, and a person in these past 8 years. I had the privilege of being a career coach to 10 wonderful people, worked on dozens of client projects, and had a small part in bringing accessibility to the forefront company-wide.&lt;/p&gt;



&lt;p&gt;I&amp;#8217;m going to miss so so much.&lt;/p&gt;



&lt;p&gt;The little things. The everyday chatter with colleagues, shared pet pictures and music recommendations, opinions about industry news and latest goings on in web development.&lt;/p&gt;



&lt;p&gt;The big things. The years-long collective accessibility documentation I had helped to curate in the company wiki. The client project I had spent the past year and a half guiding towards less technical debt, better accessibility, improved performance, and more efficient process. The team I was fortunate enough to work with, help guide, and learn from every day.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Self Care&lt;/h2&gt;



&lt;p&gt;In the same articles I had mentioned in the section above, another key point they all share is that you need to be intentional about taking care of yourself. Try to eat well and get good exercise and rest, seek good company, and practice mindfulness. It&amp;#8217;s critical to do these things to balance against the grief, stress, and guilt.&lt;/p&gt;



&lt;p&gt;Here&amp;#8217;s some of my self care items these first few days.&lt;/p&gt;



&lt;p&gt;I watched some guilty pleasure shows I don&amp;#8217;t often make the time for, especially in those moments where any deep work or thought doesn&amp;#8217;t come so easily. I gave myself permission to slow down and just enjoy something a little mindless.&lt;/p&gt;



&lt;p&gt;Went on an hour and a half walk with my wife, including through a forest preserve. Time outdoors and in nature is never wasted.&lt;/p&gt;



&lt;p&gt;A couple close friends took time out and set up an outing for all of us. It was a great time, I didn&amp;#8217;t realize until later how much that lifted me up. I absolutely needed it and am so grateful.&lt;/p&gt;



&lt;p&gt;Being the organization and productivity nerd that I am, I got some strange sense of enjoyment cleaning up my note taking apps, updating a bunch of my contacts with photos, and ironically enough in capturing the thoughts that led to this blog post.&lt;/p&gt;



&lt;p&gt;I finally reorganized the living room entertainment center, wire management and all.&lt;/p&gt;



&lt;p&gt;Today, I&amp;#8217;m about to get to some yard work while listening to an audiobook.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Wrapping up&lt;/h2&gt;



&lt;p&gt;It was cathartic to jot down these thoughts and then compile them to share openly. I do feel like it&amp;#8217;s helping me process things and start to look optimistically at the future.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Open Invitation to Chat&lt;/h3&gt;



&lt;p&gt;I&amp;#8217;m sharing all this here in the hopes that it helps others going through this too to know you&amp;#8217;re not alone, and whatever you&amp;#8217;re feeling is a normal part of the process. Maybe it helps you make sense of your own experiences too.&lt;/p&gt;



&lt;p&gt;Open invitation to you as well to chat via email, social media DMs, whatever you like. Let&amp;#8217;s talk about it, share some reflections and process things together, then we can move on to talk about what has us excited for the future.&lt;/p&gt;



&lt;p&gt;Speaking of, I&amp;#8217;m some combination of excited and nervous about what I&amp;#8217;m planning next. Working on some details and will be sharing more about that very soon. Stay tuned!&lt;/p&gt;

    </content>
	</entry>
  
	<entry>
		<title>Intentional Attention &#8211; My Organization Strategy for Interesting Reads</title>
		<link href="https://stevenwoodson.com/blog/intentional-attention-my-organization-strategy-for-interesting-reads"/>
		<published>2023-05-21T15:00:15.000Z</published>
		<updated>2023-08-10T16:24:12.000Z</updated>
		<id>https://stevenwoodson.com/blog/intentional-attention-my-organization-strategy-for-interesting-reads</id>
    <summary>With an ever growing list of interesting content and limited time to read it all, this is how I&amp;#8217;ve been more intentional about how I organize and parse.</summary>
		<content type="html">&lt;img src=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/05/Intentional-Attention.png&quot; alt=&quot;&quot;&gt;
&lt;p&gt;I&amp;#8217;m trying to be more intentional about reading through the backlog of cool projects, articles, and newsletters I collect throughout the day. Be it from colleagues, social media, my blog feed reader, or from emailed newsletters and link roundups, there&amp;#8217;s a lot of great stuff out there!&lt;/p&gt;



&lt;p&gt;I&amp;#8217;ve come to terms with the fact that there&amp;#8217;s always going to be more to see and do than there is time for them, and there&amp;#8217;s no better example of that than this constant stream of great content.&lt;/p&gt;



&lt;p&gt;This is why I regularly refine my organization and time management strategies, I want to be more intentional about what I put attention towards. In this article I&amp;#8217;ll share how I moved from ad hoc and rushed parsing to a more sustainable and intentional way of giving this great content the time they deserve. &lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;&amp;#8220;Not Right Now&amp;#8221; Ad Hoc Attention&lt;/h2&gt;



&lt;p&gt;Before, when I found an interesting article I&amp;#8217;d tell myself &amp;#8220;not right now&amp;#8221;. So it sits in a tab or quickly added to a reader app. A great newsletter arrives but &amp;#8220;I can&amp;#8217;t today&amp;#8221; so it sits unread in my email until I have more time.&lt;/p&gt;



&lt;p&gt;But, more time doesn&amp;#8217;t materialize. So I&amp;#8217;d parse through as best I can. Skim some articles over lunch or in between meetings. Click through some newsletters as I mindlessly check emails. I knew I was missing out but tried to rationalize that I got the gist of it.&lt;/p&gt;



&lt;p&gt;I&amp;#8217;m sure you&amp;#8217;ve been there too.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Intentional Attention&lt;/h2&gt;



&lt;p&gt;I came to realize I was getting overwhelmed with it all and the ad hoc parsing wasn&amp;#8217;t really working. I always felt rushed and knew I was missing out on some great ideas, projects, and sales.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Getting organized&lt;/h3&gt;



&lt;p&gt;Here&amp;#8217;s what I&amp;#8217;ve been doing about it:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;strong&gt;Mark emails as &amp;#8220;&lt;a href=&quot;https://stevenwoodson.com/blog/email-newsletters-my-emotional-response-spectrum-and-organization-strategy/&quot;&gt;BAC&amp;#8217;N&lt;/a&gt;&amp;#8220;&lt;/strong&gt;&lt;br /&gt;It&amp;#8217;s not spam but also not urgent, so I remove from the inbox but leave it unread&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;Articles and links go to a &lt;a href=&quot;https://get.todoist.io/ajusu1yue4vv&quot;&gt;Todoist&lt;/a&gt; to do list&lt;br /&gt;&lt;/strong&gt;If it&amp;#8217;s more urgent I may set a due date, then I close the tab&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;Set time aside to get to as much as I can&lt;/strong&gt;&lt;br /&gt;Timeboxed and go in date order so I don&amp;#8217;t miss time-sensitive items like sales or world events&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;I move them so they don&amp;#8217;t take brain space, they all go to the same place so I stay organized, and I can mentally let them go because I know I&amp;#8217;ll get back to them later.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Time to read!&lt;/h3&gt;



&lt;p&gt;I chose a weekend day where I&amp;#8217;m more relaxed and able to concentrate, and I timebox myself so I don&amp;#8217;t end up going down too deep a spiral to the detriment of other things I&amp;#8217;d like to get to in the day. This is definitely the hardest part and something I still struggle with. &lt;/p&gt;



&lt;p&gt;When I do get to reading through this backlog of content, I do a few additional things to help me parse quicker, remember important things, and plan ahead:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;strong&gt;Something I want to save long term?&lt;br /&gt;&lt;/strong&gt;Great ideas, methodologies, and projects I may want to refer back to, for example. I add it as a clip to Evernote, it captures the content in the page so I can more easily search for it later.&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;Something I need to act on?&lt;/strong&gt;&lt;br /&gt;A great sale coming up, something I want to participate in, or share with others. I either do it right then and there, or I make a todo to address it at a future time.&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;Something I realize I&amp;#8217;m not interested in?&lt;br /&gt;&lt;/strong&gt;Especially for longer lists or articles I start by skimming through, if I realize it&amp;#8217;s not something I want to spend time on I give myself permission to let it go and move on.&lt;/li&gt;
&lt;/ul&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;It&amp;#8217;s Working&lt;/h2&gt;



&lt;p&gt;No more unreads in my inbox.&lt;/p&gt;



&lt;p&gt;No more tabs slowing down my browser.&lt;/p&gt;



&lt;p&gt;I feel a bit more connected and a bit less overwhelmed.&lt;/p&gt;



&lt;p&gt;It&amp;#8217;s been a handful of weeks and I&amp;#8217;m fairly certain if I didn&amp;#8217;t have this organization in place I wouldn&amp;#8217;t have made the time to discover some really interesting projects, thought provoking articles, and a couple good sales. I&amp;#8217;m so glad I did.&lt;/p&gt;



&lt;p&gt;As I finished drafting this, I realized it&amp;#8217;s an unintentional follow up to &lt;a href=&quot;https://stevenwoodson.com/blog/email-newsletters-my-emotional-response-spectrum-and-organization-strategy/&quot;&gt;Email Newsletters – My Emotional Response Spectrum and Organization Strategy&lt;/a&gt;. If this article resonates with you, you may enjoy that one as well, it&amp;#8217;s a deeper dive on how I tackle the onslaught of daily emails.&lt;/p&gt;



&lt;p&gt;What do you do about the constant stream of articles, links, and emails? I&amp;#8217;d like to hear about it, always on the lookout to keep refining my process!&lt;/p&gt;

    </content>
	</entry>
  
	<entry>
		<title>Plaid Swatch Generator</title>
		<link href="https://stevenwoodson.com/blog/plaid-swatch-generator"/>
		<published>2023-04-23T19:50:40.000Z</published>
		<updated>2023-08-10T16:24:07.000Z</updated>
		<id>https://stevenwoodson.com/blog/plaid-swatch-generator</id>
    <summary>SVG based plaid swatch generator, choose your five colors and generate a unique plaid pattern that you can download and share.</summary>
		<content type="html">&lt;img src=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/04/Plaid-Swatch-Generator.png&quot; alt=&quot;&quot;&gt;
&lt;p&gt;Sometimes you&amp;#8217;ve gotta make time to build something fun, today I whipped up a &lt;a href=&quot;https://stevenwoodson.com/plaid-generator/&quot;&gt;Plaid Swatch Generator&lt;/a&gt;!&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Background&lt;/h2&gt;



&lt;p&gt;I built up my personal branding centered around the use of various plaid color combinations. I wanted all my projects to have unique color palettes but a distinctive style that was immediately apparent. For example, below are four of the logos I&amp;#8217;ve created.&lt;/p&gt;



&lt;figure class=&quot;wp-block-image size-large&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1024&quot; height=&quot;512&quot; src=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/04/personal-branding-1024x512.png&quot; alt=&quot;Four logos that Steve had created, in clockwise order from top left is the logo for StevenWoodson.com with lots of shades of greens, then Be Inclusive with pinks and purples, then Plaid &amp;amp; Peppers with browns and reds, and finally Accessibility Solutions with reds and blues.&quot; class=&quot;wp-image-312&quot; srcset=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/04/personal-branding-1024x512.png 1024w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/04/personal-branding-300x150.png 300w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/04/personal-branding-768x384.png 768w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/04/personal-branding.png 1200w&quot; sizes=&quot;auto, (max-width: 1024px) 100vw, 1024px&quot; /&gt;&lt;figcaption class=&quot;wp-element-caption&quot;&gt;Some examples of my personal/professional branding&lt;/figcaption&gt;&lt;/figure&gt;



&lt;p&gt;Early on, I was able to figure out how to get this to render as an SVG so I had more options for manipulating and so the plaid was always as performant and crisp as possible. I built &lt;a href=&quot;https://codepen.io/stevenwoodson/pen/VgJYNJ&quot; rel=&quot;nofollow&quot;&gt;a quick CodePen of it&lt;/a&gt; and moved on.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Impetus&lt;/h2&gt;



&lt;p&gt;I had a laundry list of things to do today, but I didn&amp;#8217;t have much motivation so I was looking around CodePen (as you do) and came across the plaid Pen I had noted above. It sparked a thought &amp;#8220;hey this could pretty easily become a cool plaid generator, I&amp;#8217;d just need to figure out how to change the colors dynamically.&amp;#8221;&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Build&lt;/h2&gt;



&lt;p&gt;The dynamic color updates ended up being the main chunk of work here. Once I got color input elements added to the page and wired up with a bit of JavaScript, changing the fill of the related SVG rectangles was pretty straightforward.&lt;/p&gt;



&lt;p&gt;I figured out how to get the colors to reflect in the example as you pan through the color wheel too, making choosing colors pretty intuitive as long as your browser supports more modern color picker options.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Easy Sharing&lt;/h2&gt;



&lt;figure class=&quot;wp-block-image size-large&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1024&quot; height=&quot;1024&quot; src=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/04/swatch-examples-1024x1024.png&quot; alt=&quot;Nine plaid swatch examples with widely ranging colors. Easily generated with this plaid swatch generator!&quot; class=&quot;wp-image-313&quot; srcset=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/04/swatch-examples-1024x1024.png 1024w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/04/swatch-examples-300x300.png 300w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/04/swatch-examples-150x150.png 150w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/04/swatch-examples-768x768.png 768w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/04/swatch-examples.png 1500w&quot; sizes=&quot;auto, (max-width: 1024px) 100vw, 1024px&quot; /&gt;&lt;figcaption class=&quot;wp-element-caption&quot;&gt;Check out some of the ones I generated as I was testing!&lt;/figcaption&gt;&lt;/figure&gt;



&lt;p&gt;Since the main chunk of work wasn&amp;#8217;t too bad, I set my sights on making this as easy as possible to grab and share. So I&amp;#8217;ve added:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;a download button, so you can get an SVG of your creation straight away&lt;/li&gt;



&lt;li&gt;a URL auto-update to match your choices, making it easy to bookmark or share too&lt;/li&gt;
&lt;/ul&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Feedback?&lt;/h2&gt;



&lt;p&gt;I have some other ideas for this, but wanted to make sure I kept it to an afternoon of hacking away. I do want to revisit it and refine with more, so give the &lt;a href=&quot;https://stevenwoodson.com/plaid-generator/&quot;&gt;Plaid Swatch Generator&lt;/a&gt; a try and let me know what you&amp;#8217;d like to see added!&lt;/p&gt;

    </content>
	</entry>
  
	<entry>
		<title>How I Hacked My Brain to Let Myself Relax in the Evenings</title>
		<link href="https://stevenwoodson.com/blog/how-i-hacked-my-brain-to-let-myself-relax-in-the-evenings"/>
		<published>2023-03-28T12:27:44.000Z</published>
		<updated>2023-08-10T17:24:52.000Z</updated>
		<id>https://stevenwoodson.com/blog/how-i-hacked-my-brain-to-let-myself-relax-in-the-evenings</id>
    <summary>A successful productivity experiment that has reduced my anxiety and lets me give myself permission to relax in the evenings.</summary>
		<content type="html">&lt;img src=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/03/How-I-Hacked-My-Brain-to-Let-Myself-Relax-in-the-Evenings.png&quot; alt=&quot;&quot;&gt;
&lt;p&gt;Do you ever find yourself constantly on the go, always trying to be productive? I&amp;#8217;ve struggled with this for most of my adult life, spending evenings checking off tasks on my to-do list, a blur of writing code and responding to emails. But after a while, I realized that this wasn&amp;#8217;t sustainable. I was regularly anxious, tired, and felt like I couldn&amp;#8217;t just relax and recharge. I also didn&amp;#8217;t like the feeling that family and friends were getting the leftover time not otherwise spent in this productivity cycle.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;The Impetus&lt;/h2&gt;



&lt;p&gt;I&amp;#8217;ve been reading a whole lot of self help and productivity books the past few years. Largely as a shot of motivation to keep up the few &amp;#8220;tricks&amp;#8221; that have started to help me control that anxiety while still pursuing goals, but also out of a desire to find new or different tricks to try.&lt;/p&gt;



&lt;p&gt;One concept that came up often in these books is around being intentional with your time, be it making time for what you consider important, removing what you don&amp;#8217;t consider to be essential, or building resilience to distractions.&lt;/p&gt;



&lt;p&gt;They all come down to taking charge of your time. &lt;/p&gt;



&lt;blockquote class=&quot;wp-block-quote is-layout-flow wp-block-quote-is-layout-flow&quot;&gt;
&lt;p&gt;“You only waste time if you’re not intentional about how you spend it.”&lt;/p&gt;
&lt;cite&gt;Jake Knapp &amp;amp; John Zeratsky, &lt;a href=&quot;https://www.indiebound.org/book/9780525572428?aff=stevenwoodson&quot; rel=&quot;sponsored nofollow&quot;&gt;Make Time: How to Focus on What Matters Every Day&lt;/a&gt;&lt;/cite&gt;&lt;/blockquote&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;How I took charge of my time&lt;/h2&gt;



&lt;p&gt;First, I had to come to terms with the fact that I have a strong need to feel productive. I&amp;#8217;ve come to realize I can only really relax when I feel a sense of accomplishment, and know that I have a plan for continuing that into the future. So, instead of fighting against this part of myself, I decided to work with it.&lt;/p&gt;



&lt;blockquote class=&quot;wp-block-quote is-layout-flow wp-block-quote-is-layout-flow&quot;&gt;
&lt;p&gt;“Remember that if you don’t prioritize your life someone else will.”&lt;/p&gt;
&lt;cite&gt;Greg Mckeown, &lt;a href=&quot;https://www.indiebound.org/book/9780804137386?aff=stevenwoodson&quot; rel=&quot;sponsored nofollow&quot;&gt;Essentialism: The Disciplined Pursuit of Less&lt;/a&gt;&lt;/cite&gt;&lt;/blockquote&gt;



&lt;p&gt;I started by time-boxing my productive time in the early morning before work. I found that I was most focused and least distracted in the morning, so I dedicate a couple of hours each morning to tackling my most important goal for that day.&lt;/p&gt;



&lt;blockquote class=&quot;wp-block-quote is-layout-flow wp-block-quote-is-layout-flow&quot;&gt;
&lt;p&gt;“something magic happens when you start the day with one high-priority goal.”&lt;/p&gt;
&lt;cite&gt;Jake Knapp &amp;amp; John Zeratsky, &lt;a href=&quot;https://www.indiebound.org/book/9780525572428?aff=stevenwoodson&quot; rel=&quot;sponsored nofollow&quot;&gt;Make Time: How to Focus on What Matters Every Day&lt;/a&gt;&lt;/cite&gt;&lt;/blockquote&gt;



&lt;p&gt;Next, I started setting a daily &amp;#8220;theme&amp;#8221; for myself. This meant that each day, I would focus on one particular goal. For example, Monday might be dedicated to building a feature on &lt;a href=&quot;https://beinclusive.app/&quot;&gt;Be Inclusive&lt;/a&gt;, Tuesday focuses on a blog post (like I am today), Wednesday towards reading a backlog of blog posts I&amp;#8217;ve let pile up, etc. This helped me to avoid spending too much time on one particular area and ensured that I was making progress on all fronts.&lt;/p&gt;



&lt;p&gt;I come up with these daily goals during my weekly planning every Sunday, so I&amp;#8217;ve got the whole week mapped out in advance. This gave me peace of mind in the evening knowing I had time set aside and a plan for it the next day.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Permission to Relax&lt;/h2&gt;



&lt;p&gt;The end result? I can now relax in the evenings without feeling guilty or anxious about all the things I need to do. And, because I made it a habit to go to bed at the same time every night and wake up at the same time every morning, I&amp;#8217;ve noticed that I&amp;#8217;m sleeping better and have more energy throughout the day.&lt;/p&gt;



&lt;blockquote class=&quot;wp-block-quote is-layout-flow wp-block-quote-is-layout-flow&quot;&gt;
&lt;p&gt;&amp;#8220;The people we love most should not be content getting whatever time is left over. Everyone benefits when we hold time on our schedule to live up to our values and do our share.&amp;#8221;&lt;/p&gt;
&lt;cite&gt;Nir Eyal, &lt;a href=&quot;https://www.indiebound.org/book/9781948836531?aff=stevenwoodson&quot; rel=&quot;sponsored nofollow&quot;&gt;Indistractable&lt;/a&gt;&lt;/cite&gt;&lt;/blockquote&gt;



&lt;p&gt;If you&amp;#8217;re someone who always needs to be productive, I encourage you to give this approach a try. It might take some time to get used to, but trust me, it&amp;#8217;s worth it.&lt;/p&gt;

    </content>
	</entry>
  
	<entry>
		<title>Email Newsletters &#8211; My Emotional Response Spectrum and Organization Strategy</title>
		<link href="https://stevenwoodson.com/blog/email-newsletters-my-emotional-response-spectrum-and-organization-strategy"/>
		<published>2023-03-13T11:43:08.000Z</published>
		<updated>2023-08-10T16:25:37.000Z</updated>
		<id>https://stevenwoodson.com/blog/email-newsletters-my-emotional-response-spectrum-and-organization-strategy</id>
    <summary>How I personally categorize newsletters, how that categorization has influenced how they&amp;#8217;re organized, and how much attention they get as a result.</summary>
		<content type="html">&lt;img src=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/03/Email-Newsletters-My-Emotional-Response-Spectrum-and-Organization-Strategy.png&quot; alt=&quot;&quot;&gt;
&lt;p&gt;How&amp;#8217;s that for a title? Basically, this is all about how I personally categorize newsletters, how that categorization has influenced how they&amp;#8217;re organized, and how much attention they get as a result.&lt;/p&gt;



&lt;p&gt;I don&amp;#8217;t think it&amp;#8217;s a controversial statement to say that email newsletters fall on a spectrum of emotional response between &amp;#8220;awww yeah!&amp;#8221; to &amp;#8220;please, no.&amp;#8221; I&amp;#8217;m sure I&amp;#8217;m not the first to frame it this way, though I&amp;#8217;ve yet to see anyone else act on that emotional response in how they organize and read through these incoming messages.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Emotional Response Spectrum&lt;/h2&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Awww yeah!&lt;/h3&gt;



&lt;p&gt;These are the newsletters that help me stay in touch with the world, my industries of interest, and people I admire. The emotional response for me is one of excited anticipation, I know I&amp;#8217;m going to spend a good deal of time on these and will generally enjoy them.&lt;/p&gt;



&lt;p&gt;The vast majority of these tend to be of a link roundup variety. People go through quite a bit of trouble sifting through the endless stream of information and share the gold, and I really appreciate that. Some others are deeper dives into current events or a particular topic that I find interesting.&lt;/p&gt;



&lt;p&gt;&lt;strong&gt;Some of my current favorites:&lt;/strong&gt;&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://buttondown.email/the-a11y-project&quot;&gt;The A11y Project&lt;/a&gt;&lt;/li&gt;



&lt;li&gt;&lt;a href=&quot;https://ericwbailey.website/newsletter&quot; rel=&quot;nofollow&quot;&gt;SC 2.4.4&lt;/a&gt; by Eric Bailey&lt;/li&gt;



&lt;li&gt;&lt;a href=&quot;https://www.smashingmagazine.com/the-smashing-newsletter/&quot; rel=&quot;nofollow&quot;&gt;Smashing Magazine&lt;/a&gt;&lt;/li&gt;



&lt;li&gt;&lt;a href=&quot;https://www.theskimm.com/newsletters&quot; rel=&quot;nofollow&quot;&gt;The Skimm&lt;/a&gt;&lt;/li&gt;



&lt;li&gt;&lt;a href=&quot;https://join1440.com/&quot; rel=&quot;nofollow&quot;&gt;1440 Daily Digest&lt;/a&gt;&lt;/li&gt;



&lt;li&gt;&lt;a href=&quot;https://www.readsomethinggreat.com/&quot; rel=&quot;nofollow&quot;&gt;Read Something Great&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Alright, thanks.&lt;/h3&gt;



&lt;p&gt;These are the newsletters that are only occasionally relevant to me, have a certain quality that I appreciate, or otherwise are &amp;#8220;okay&amp;#8221; enough to open and scan in batches.&lt;/p&gt;



&lt;p&gt;Nearly all of these are the daily-ish emails from brands, I regret to admit that most haven&amp;#8217;t been unsubscribed because of the occasional emailed coupon. Some select few (like those noted below) I mostly keep around because I enjoy their messaging or find the information useful enough to scan when I can.&lt;/p&gt;



&lt;p&gt;&lt;strong&gt;Some Examples:&lt;/strong&gt;&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://us.whogivesacrap.org/&quot; rel=&quot;nofollow&quot;&gt;Who Gives a Crap&lt;/a&gt; &amp;#8211; Great brand that puts a lot of effort into making their mailers enjoyable&lt;/li&gt;



&lt;li&gt;USPS Informed Delivery&lt;/li&gt;
&lt;/ul&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Please, no.&lt;/h3&gt;



&lt;p&gt;Basically any mass-email sent to me without my asking for it. &lt;/p&gt;



&lt;p&gt;&lt;strong&gt;Some examples:&lt;/strong&gt;&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;Any newsletters I get auto-added to when I create an account, bonus 👎 points for those that don&amp;#8217;t even make it a checkbox option in the sign up form.&lt;/li&gt;



&lt;li&gt;Unsolicited messages for products or services past the first three. I give many a pass on a few cold emails when done tactfully, can&amp;#8217;t fault someone trying to grow their business.&lt;/li&gt;



&lt;li&gt;Extra rage bonus points to any I can&amp;#8217;t easily unsubscribe from. One big example was the Ozy Media company, which I continued to get spammed daily until they finally shut down.&lt;/li&gt;
&lt;/ul&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Email Organization Strategy&lt;/h2&gt;



&lt;p&gt;I get an unbelievable amount of emails every day, most I don&amp;#8217;t need to spend mental cycles on anywhere remotely near daily. I am &amp;#8211; for better or worse &amp;#8211; an inbox zero kind of guy, so I&amp;#8217;ve honed this strategy over the years to balance the need for a tidy inbox with the need to not be sifting through emails for hours every day.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;My Newsletter Rules&lt;/h3&gt;



&lt;p&gt;I have a few strongly held rules to help maintain a reasonable number of emails coming through:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;If I&amp;#8217;ve not opened any messages from a particular sender&lt;strong&gt; in the last week or last 5 emails&lt;/strong&gt;, whichever comes first, then I unsubscribe. This one is occasionally hard because I _want_ to like it, but actions speak louder than intentions. Time to go.&lt;/li&gt;



&lt;li&gt;If you send &lt;strong&gt;more than 2 emails a day&lt;/strong&gt;, I&amp;#8217;m looking for ways to adjust my preferences. If I can&amp;#8217;t adjust email frequency I&amp;#8217;m out. I can barely count on one hand the reasons I&amp;#8217;d be okay with this frequency of messaging.&lt;/li&gt;



&lt;li&gt;If you send the same email multiple times, I seriously consider unsubscribing. I&amp;#8217;m really sorry, I know email marketing is hard, but it&amp;#8217;s not like Twitter where we&amp;#8217;re trying to stay fresh in the ongoing stream of posts. Multiples of the same email still need to be handled separately and my patience for that is long gone.&lt;/li&gt;
&lt;/ul&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Automated Categorization&lt;/h3&gt;



&lt;p&gt;Now that I&amp;#8217;ve level set on the messages I&amp;#8217;m willing to keep, I need to keep that list of messages organized. I use Gmail, though I&amp;#8217;m sure this would work with any email organization system worth using. I set up rules for individual sender email addresses to go into one of a few newsletter specific labels:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;strong&gt;BAC&amp;#8217;N&lt;/strong&gt; &amp;#8211; Anything that doesn&amp;#8217;t fit in the below two categories. I call it bac&amp;#8217;n because, like spam, bacon is best enjoyed only occasionally.&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;Daily News&lt;/strong&gt; &amp;#8211; All the world events, financial, and other general daily news I try to consume to stay informed&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;Industry&lt;/strong&gt; &amp;#8211; All the web development, accessibility, design system, technology, etc. news that keeps me up to date on what&amp;#8217;s trending around me professionally.&lt;/li&gt;
&lt;/ul&gt;



&lt;figure class=&quot;wp-block-image size-full&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;253&quot; height=&quot;150&quot; src=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/03/Screen-Shot-2023-03-11-at-9.24.28-AM.png&quot; alt=&quot;My mailing list labels in Gmail, first is &amp;quot;BAC&#39;N&amp;quot;, followed by &amp;quot;Daily News&amp;quot; and &amp;quot;Industry&amp;quot;.&quot; class=&quot;wp-image-292&quot; /&gt;&lt;figcaption class=&quot;wp-element-caption&quot;&gt;My newsletter inboxes&lt;/figcaption&gt;&lt;/figure&gt;



&lt;p&gt;These emails are set to skip the inbox and go directly to the selected folder. This way I don&amp;#8217;t get notifications of new messages, and I&amp;#8217;m able to ignore them until I&amp;#8217;m ready. Which brings me to&amp;#8230;&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Managing my Attention&lt;/h3&gt;



&lt;p&gt;Great, now I&amp;#8217;ve got stacks of organized emails by category. Now, what do I do with them?&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;BAC&amp;#8217;N emails are scanned as quickly as I can manage, I tend to do this about once a week. I take note of sales and any links to articles I may want to read in more detail.&lt;/li&gt;



&lt;li&gt;Daily News I try (and often fail) to keep up with every morning. If I miss a few I read the latest and then scan the older ones for any info I may have missed. Given that they tend to be daily news, it&amp;#8217;s often updates on ongoing stories so this has worked out pretty well.&lt;/li&gt;



&lt;li&gt;Industry emails tend to take me the longest to get through because there are a lot of collections of links to long form articles. I try to chip away at these as I have some time but honestly this is the hardest one to manage because I wanna read _it all_ but it takes a lot of time.&lt;/li&gt;
&lt;/ul&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Archives&lt;/h3&gt;



&lt;p&gt;This one may seem a bit weird, but hear me out.&lt;/p&gt;



&lt;p&gt;I keep an embarrassingly long history of newsletter emails, like months and sometimes years worth. This has served me well enough in the past that I&amp;#8217;ve stuck with it, being able to refer back to previous emails from the same sender helps me in a few ways.&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;strong&gt;&amp;#8220;Big Sale&amp;#8221; emails&lt;/strong&gt; &amp;#8211; We all get them, the &amp;#8220;blowout&amp;#8221; sale messages for 20% off and the like. Well, I can see that you just had a similar sale a few months ago for 30% off, so I&amp;#8217;ll wait. Thanks.&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;Frequency changes&lt;/strong&gt; &amp;#8211; Sometimes companies change their strategies and start sending more/less emails. If it gets to be annoying and I&amp;#8217;m unsure why because I&amp;#8217;ve been subscribed forever, this sometimes helps explain. A company that used to email weekly starts sending a couple messages a day, for example.&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;Newsletter Rules&lt;/strong&gt; &amp;#8211; All three of my newsletter rules requires that I have some reference to several recent messages, can only do that by saving them!&lt;/li&gt;
&lt;/ul&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Overthinking it?&lt;/h2&gt;



&lt;p&gt;Sometimes I do think &amp;#8220;this is a lot of consideration for email newsletters&amp;#8221;. But I&amp;#8217;ve come to the conclusion that the occasional time I put into curating saves me a whole lot more in the long run. Besides, if you don&amp;#8217;t control where your attention goes someone else will.&lt;/p&gt;

    </content>
	</entry>
  
	<entry>
		<title>CloudFront Function for basic auth, redirect, and serving from S3</title>
		<link href="https://stevenwoodson.com/blog/cloudfront-function-for-basic-auth-redirect-and-serving-from-s3"/>
		<published>2023-03-09T14:08:28.000Z</published>
		<updated>2023-08-10T17:26:15.000Z</updated>
		<id>https://stevenwoodson.com/blog/cloudfront-function-for-basic-auth-redirect-and-serving-from-s3</id>
    <summary>How to switch from Lambda@Edge to a single CloudFront function for serving static content from an AWS S3 bucket, with redirects and basic auth included!</summary>
		<content type="html">&lt;img src=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/03/CloudFront-Function-for-basic-auth-redirect-and-serving-from-S3.png&quot; alt=&quot;&quot;&gt;
&lt;p&gt;I recently had the opportunity to rethink how some CDN customizations were happening within an Amazon AWS managed project. Previously, we had used a couple Lambda@Edge functions to perform some light modifications during the &lt;strong&gt;Viewer request&lt;/strong&gt; and &lt;strong&gt;Origin request&lt;/strong&gt; cache behaviors. We had largely inherited the setup so hitting the reset button with some needed changes seemed a good idea.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Initial CloudFront Function Research&lt;/h2&gt;



&lt;p&gt;Enter &lt;a href=&quot;https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/cloudfront-functions.html&quot; rel=&quot;nofollow&quot;&gt;CloudFront Functions&lt;/a&gt;, I had actually not heard of them previously but apparently they&amp;#8217;ve been around &lt;a href=&quot;https://aws.amazon.com/blogs/aws/introducing-cloudfront-functions-run-your-code-at-the-edge-with-low-latency-at-any-scale/&quot; rel=&quot;nofollow&quot;&gt;since 2021&lt;/a&gt;. After getting up to speed reading up on the use case it seemed an ideal evolution from Lambda@Edge. The parts I was most excited about was reading the following:&lt;/p&gt;



&lt;blockquote class=&quot;wp-block-quote is-layout-flow wp-block-quote-is-layout-flow&quot;&gt;
&lt;p&gt;offers submillisecond startup times, scales immediately to handle millions of requests per second, and is highly secure&lt;/p&gt;
&lt;/blockquote&gt;



&lt;p&gt;Our use case needed the following:&lt;/p&gt;



&lt;aside class=&quot;wp-block-create-block-aside&quot;&gt;
&lt;p&gt;A huge thank you to Joshua Lyman for writing up &lt;a href=&quot;https://www.joshualyman.com/2022/01/add-http-basic-authentication-to-cloudfront-distributions/&quot;&gt;Add HTTP Basic Authentication to CloudFront Distributions&lt;/a&gt;, it ended up being a great head start to basic authentication I needed (item #1 on the list).&lt;/p&gt;
&lt;/aside&gt;



&lt;ol class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;strong&gt;Basic Authentication for all lower environments&lt;/strong&gt;, because we don&amp;#8217;t want public access to content that&amp;#8217;s still a work in progress&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;More control over redirects&lt;/strong&gt;, because we&amp;#8217;re using a JavaScript framework we weren&amp;#8217;t able to control the response status as well as we would have liked (301 instead of 302 for example)&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;Add index.html to request URLs that don’t include a file name&lt;/strong&gt;, also because we&amp;#8217;re using a statically generated site based on a JavaScript framework.&lt;/li&gt;
&lt;/ol&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Gotchas&lt;/h3&gt;



&lt;p&gt;I was initially surprised &amp;#8211; coming from the Lambda functions mentality &amp;#8211; that I wasn&amp;#8217;t able to use a couple of the JavaScript niceties like the Nullish coalescing operator (??), Optional chaining operator (?.), and Conditional (ternary) operator. Found out that that&amp;#8217;s because &lt;a href=&quot;https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/functions-javascript-runtime-features.html#writing-functions-javascript-features-restricted-features&quot; rel=&quot;nofollow&quot;&gt;CloudFront functions only support ES 5.1&lt;/a&gt;.&lt;/p&gt;



&lt;p&gt;I was also pleasantly surprised to see a high focus on optimization and utilization, you even get a handy &amp;#8220;&lt;a href=&quot;https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/test-function.html?icmpid=docs_cf_help_panel#compute-utilization&quot; rel=&quot;nofollow&quot;&gt;Compute Utilization&lt;/a&gt;&amp;#8221; score every time you test so you can keep tabs on ensuring it&amp;#8217;s comfortably within the max allowed time.&lt;/p&gt;



&lt;p&gt;While much of these weren&amp;#8217;t an issue for me, I also found out that:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;Dynamic code evaluation (&lt;code&gt;eval&lt;/code&gt; for example) and Timers are not supported.&lt;/li&gt;



&lt;li&gt;The maximum memory assigned to CloudFront Functions is 2MB&lt;/li&gt;



&lt;li&gt;CloudFront Functions only respond to Viewer triggers whereas Lambda@Edge can work with both Viewer and Origin triggers. In my case I was able to merge the code we were using in both situations without any issue, but your mileage may vary.&lt;/li&gt;



&lt;li&gt;CloudFront Functions only support JavaScript (ES 5.1 as noted above), Lambda@Edge supports Node.js and Python too.&lt;/li&gt;



&lt;li&gt;CloudFront Functions do not have access to the network or filesystem.&lt;/li&gt;



&lt;li&gt;CloudFront Functions can only manipulate HTTP headers.&lt;/li&gt;
&lt;/ul&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Benefits&lt;/h3&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;Overall cheaper, and there&amp;#8217;s a free tier available too.&lt;/li&gt;



&lt;li&gt;Building &amp;amp; testing are so much faster because you can do so directly within CloudFront, no more saving, creating a version, attributing that to the CloudFront cache behavior and deploying to test!&lt;/li&gt;



&lt;li&gt;Speed and scalability are amazing, you can support 10,000,000 requests per _second_ or more compared to up to 10,000 requests per second per Region for Lambda@Edge.&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;If you&amp;#8217;re interested in more notes to help you choose which way to go, this &lt;a href=&quot;https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/edge-functions.html#edge-functions-choosing&quot; rel=&quot;nofollow&quot;&gt;Choosing between CloudFront Functions and Lambda@Edge&lt;/a&gt; section was really helpful for me.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;The Code&lt;/h2&gt;



&lt;p&gt;Let&amp;#8217;s get to work!&lt;/p&gt;



&lt;p&gt;Final source&lt;/p&gt;



&lt;pre class=&quot;wp-block-code language-js&quot;&gt;&lt;code&gt;var authRedirect = {
  statusCode: 401,
  statusDescription: &quot;Unauthorized&quot;,
  headers: {
    &quot;www-authenticate&quot;: {
      value: &#39;Basic realm=&quot;Enter credentials to access this secure site&quot;&#39;,
    },
  },
};

/**
 * Basic Authentication
 *
 * On all lower environment (dev &amp;amp; qa) we need basic authentication so general
 * public cannot access these sites. Bypass on prod and if authentication is
 * already secured.
 *
 * @param authorization - Incoming request authorization value
 * @param host - Incoming request host value
 * @returns boolean - Whether this request is authenticated
 */
function authCheck(authorization, host) {
  var productionHosts = &amp;#91;&quot;mywebsite.com&quot;, &quot;www.mywebsite.com&quot;, &quot;blog.mywebsite.com&quot;];

  // The Base64-encoded Auth string `secretuser:secretpass` that should be present.
  var expected = &quot;Basic c2VjcmV0dXNlcjpzZWNyZXRwYXNz&quot;;

  // If this is a production website, we do not want to force any authentication
  if (productionHosts.indexOf(host) &amp;gt; -1) {
    return true;
  }

  // If an Authorization header is supplied and it&#39;s an exact match
  if (authorization &amp;amp;&amp;amp; authorization.value === expected) {
    return true;
  }

  //Not production, auth header is either missing or failed to match
  return false;
}

/**
 * Redirect Check
 *
 * @param host - Incoming request host value
 * @param requestURI - Incoming request URI
 * @returns object|false - Returns an object containing the redirect details or false if no redirect necessary
 */
function redirectCheck(host, requestURI) {
  redirectPaths = {
    // &#39;&amp;#91;FROM]&#39;: {&#39;Location&#39;: &#39;&amp;#91;TO]&#39;, &#39;status&#39;: &amp;#91;301|302]}},
    &quot;/old-page-1&quot;: { Location: &quot;/new-page-1/&quot;, status: 301 },
    &quot;/old-page-2&quot;: { Location: &quot;/new-page-2/&quot;, status: 301 },
    &quot;/old-page-3&quot;: { Location: &quot;/new-page-3/&quot;, status: 302 },
  };

  if (Object.keys(redirectPaths).indexOf(requestURI) &amp;gt;= 0) {
    var redirectTo = redirectPaths&amp;#91;requestURI];
    return redirectTo;
  }

  return false;
}

/**
 * Redirect Response
 *
 * @param redirectTo - object containing the redirect location and status details
 * @returns object - response object with redirect details
 */
function redirectResponse(redirectTo) {
  return {
    statusCode: redirectTo.status,
    statusDescription: &quot;Moved&quot;,
    headers: {
      location: { value: redirectTo.Location },
    },
  };
}

/**
 * URL Rewrite
 *
 * Appends the /index.html to requests that don’t include a file name or extension
 * in the URL. Useful for single page applications or statically generated websites
 * that are hosted in an Amazon S3 bucket.
 *
 * @see https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/example-function-add-index.html
 * @param uri - Incoming request URI
 * @returns
 */
function uriRewrite(uri) {
  // Check whether the URI is missing a file name.
  if (uri.endsWith(&quot;/&quot;)) {
    return (uri += &quot;index.html&quot;);
  }
  // Check whether the URI is missing a file extension.
  else if (!uri.includes(&quot;.&quot;)) {
    return (uri += &quot;/index.html&quot;);
  } else {
    return uri;
  }
}

/**
 * Primary Handler
 *
 * Checks for authentication, then redirect, then URL rewriting
 *
 * @param event
 * @returns
 */
function handler(event) {
  var authorization = event.request.headers.authorization;
  var host =
    event.request.headers.host &amp;amp;&amp;amp; event.request.headers.host.value
      ? event.request.headers.host.value.toLowerCase()
      : &quot;&quot;;
  var requestURI = event.request.uri.replace(/&#92;/$/, &quot;&quot;);

  if (authCheck(authorization, host) === false) {
    return authRedirect;
  }

  var redirect = redirectCheck(host, requestURI);
  if (redirect !== false) {
    return redirectResponse(redirect);
  }

  event.request.uri = uriRewrite(requestURI);

  return event.request;
}
&lt;/code&gt;&lt;/pre&gt;

    </content>
	</entry>
  
	<entry>
		<title>Conserving Sentry Transactions by Ignoring Laravel Routes</title>
		<link href="https://stevenwoodson.com/blog/conserving-sentry-transactions-by-ignoring-laravel-routes"/>
		<published>2023-02-27T13:35:27.000Z</published>
		<updated>2023-08-10T17:28:06.000Z</updated>
		<id>https://stevenwoodson.com/blog/conserving-sentry-transactions-by-ignoring-laravel-routes</id>
    <summary>Sentry limits transactions per month, this shows how to conserve them by ignoring specific Laravel PHP routes you don&amp;#8217;t need to trace later.</summary>
		<content type="html">&lt;img src=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/02/Conserving-Sentry-Transactions-by-Ignoring-Laravel-Routes.png&quot; alt=&quot;&quot;&gt;
&lt;p&gt;Yet another case of trying to find an answer to a very particular problem, coming up empty, solving it myself, and posting about it for future generations.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Update 2023-04-01&lt;/h2&gt;



&lt;p&gt;Thanks to &lt;a href=&quot;https://twitter.com/cleptric&quot; rel=&quot;nofollow&quot;&gt;Michi Hoffmann&lt;/a&gt;, this is now even easier to manage with a built in &lt;code&gt;ignore_transactions&lt;/code&gt; configuration option now available as of &lt;a href=&quot;https://github.com/getsentry/sentry-php/releases/tag/3.17.0&quot; rel=&quot;nofollow&quot;&gt;release 3.17.0 of sentry-php&lt;/a&gt;!&lt;/p&gt;



&lt;p&gt;Now, instead of using the &lt;code&gt;before_send_transaction&lt;/code&gt; config I swapped to using &lt;code&gt;ignore_transactions&lt;/code&gt; and my code changed to the following:&lt;/p&gt;



&lt;pre class=&quot;wp-block-code language-php&quot;&gt;&lt;code&gt;&#39;ignore_transactions&#39; =&amp;gt; &amp;#91;&#39;/_debugbar&#39;, &#39;/monitoring&#39;, &#39;/pleaseignoreme&#39;],&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;Below is the original post for posterity, &lt;strong&gt;but with the addition above that solution is no longer necessary.&lt;/strong&gt;&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;The Problem&lt;/h2&gt;



&lt;p&gt;I have a couple Laravel PHP routes that are triggered quite often, both are very specific to other site monitoring (is the site down?) and session management (am I still logged in?). These being utility routes that don&amp;#8217;t serve a user-facing purpose, I don&amp;#8217;t need any additional logging details about them from Sentry.&lt;/p&gt;



&lt;p&gt;Can I prevent the transaction from being sent to Sentry before it even leaves my server? I tried searching terms like &amp;#8220;sentry laravel ignore route&amp;#8221; and &amp;#8220;sentry control transactions from PHP&amp;#8221; to no avail so I got to work.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;The Solution&lt;/h2&gt;



&lt;p&gt;In my searches, I did find &lt;a href=&quot;https://docs.sentry.io/platforms/php/guides/laravel/configuration/filtering/#using-platformidentifier-namebefore-send-transaction-&quot; rel=&quot;nofollow&quot;&gt;this helpful documentation for before_send_transaction&lt;/a&gt; specifically for Laravel. It&amp;#8217;s an optional addition to the &lt;code&gt;/config/sentry.php&lt;/code&gt; configuration. In it, you can define any logic you want to return &lt;code&gt;null&lt;/code&gt; in preferred situations. This null return will lead to the event being discarded. Bingo!&lt;/p&gt;



&lt;p&gt;Here&amp;#8217;s the code I worked up.&lt;/p&gt;



&lt;pre class=&quot;wp-block-code language-php&quot;&gt;&lt;code&gt;&#39;before_send_transaction&#39; =&amp;gt; function (
    &#92;Sentry&#92;Event $transaction
): ?&#92;Sentry&#92;Event {
    $ignore = &amp;#91;&#39;_debugbar&#39;, &#39;monitoring&#39;, &#39;pleaseignoreme&#39;];
    $request = $transaction-&amp;gt;getRequest();
    $check = array_filter($ignore, function ($url) use ($request) {
        if (stripos($request&amp;#91;&#39;url&#39;], $url) !== false) {
            return true;
        }
    });

    if (count($check) &amp;gt; 0) {
        return null;
    }

    return $transaction;
},&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;Here&amp;#8217;s what it&amp;#8217;s doing:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;code&gt;$ignore&lt;/code&gt; is the array of URL key terms I want to ignore, this is all you need to edit. Add any URL segments that you&amp;#8217;d prefer not to be logged by Sentry and you good to go&lt;/li&gt;



&lt;li&gt;&lt;code&gt;$request&lt;/code&gt; is the request details coming from the packaged &lt;code&gt;$transaction&lt;/code&gt; generated by Sentry. In it is the full URL that we use to test against&lt;/li&gt;



&lt;li&gt;&lt;code&gt;$check&lt;/code&gt; was my method of iterating through the ignore list and checking for a match. This filters the &lt;code&gt;$ignore&lt;/code&gt; array down to just matches.&lt;/li&gt;



&lt;li&gt;We then see if the count of &lt;code&gt;$check&lt;/code&gt; is greater than zero, if so then one of the routes matched and we want to return &lt;code&gt;null&lt;/code&gt; to ignore this transaction. Otherwise, return as normal.&lt;/li&gt;
&lt;/ul&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Closure Alternative&lt;/h3&gt;



&lt;p&gt;Shout out to &lt;a href=&quot;https://twitter.com/stayallive&quot; rel=&quot;nofollow&quot;&gt;Alex Bouma&lt;/a&gt; for also noting in &lt;a href=&quot;https://gist.github.com/stayallive/7ab6becea0a0a3ab0443eaec22deafc4&quot; rel=&quot;nofollow&quot;&gt;this Github Gist&lt;/a&gt; how to avoid using a closure (as I had in the solution above) because it makes &lt;code&gt;php artisan config:cache&lt;/code&gt; no longer work. Something I hadn&amp;#8217;t noticed until after I had the above solution in place.&lt;/p&gt;



&lt;p&gt;If you choose to continue using &lt;code&gt;before_send_transaction&lt;/code&gt; and need config caching, give a class a try! &lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;That&amp;#8217;s it!&lt;/h2&gt;



&lt;p&gt;Pretty simple, but it took a bit of digging through documentation to find &lt;code&gt;before_send_transaction&lt;/code&gt; and through the Sentry package to see what &lt;code&gt;$transaction&lt;/code&gt; contained so I&amp;#8217;m hoping to spare the next dev that bit of trouble.&lt;/p&gt;



&lt;p&gt;Onward and upward! 🚀&lt;/p&gt;

    </content>
	</entry>
  
	<entry>
		<title>Joining the IndieWeb with Webmentions and Microformats</title>
		<link href="https://stevenwoodson.com/blog/joining-the-indieweb-with-webmentions-and-microformats"/>
		<published>2023-02-19T17:18:05.000Z</published>
		<updated>2023-08-10T16:28:14.000Z</updated>
		<id>https://stevenwoodson.com/blog/joining-the-indieweb-with-webmentions-and-microformats</id>
    <summary>A collection of resources that helped me get Webmentions set up on the blog posts for this site.</summary>
		<content type="html">&lt;img src=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/02/Joining-the-IndieWeb-with-Webmentions-and-Microformats.png&quot; alt=&quot;&quot;&gt;
&lt;p&gt;I&amp;#8217;ve finally spent some time setting up Webmentions and adding some Microformats formatting to this site!&lt;/p&gt;



&lt;p&gt;These technologies have been around for quite a while but it&amp;#8217;s taken me a handful of attempts (and a whole lot of help) to understand them enough to actually move forward. I&amp;#8217;ve tried occasionally to wrap my mind around them but I kept getting confused about &lt;em&gt;what to do&lt;/em&gt; and would give up for a while. I feel a little better admitting these troubles when I see folks I consider among the best in the industry &amp;#8211; like &lt;a href=&quot;https://chriscoyier.net/2023/01/05/i-feel-contractually-obliged-to-link-to-a-post-like-bring-back-personal-blogging/&quot; rel=&quot;nofollow&quot;&gt;Chris Coyier&lt;/a&gt; and &lt;a href=&quot;https://www.miriamsuzanne.com/2022/06/04/indiweb/&quot; rel=&quot;nofollow&quot;&gt;Miriam Suzanne&lt;/a&gt; &amp;#8211; having similar tough times with it.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Resources&lt;/h2&gt;



&lt;p&gt;Rather than rehashing all the details of getting my setup working, I think it&amp;#8217;s better to list all the resources that helped me understand and/or integrate Webmentions here. Perhaps it&amp;#8217;ll help you on your path to understanding and integrating them into your sites too.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;IndieWeb &amp;amp; Personal Blogging&lt;/h3&gt;



&lt;p&gt;Thoughts from others on this topic that really helped me understand and get excited about putting some time into implementation.&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=X3SrZuH00GQ&quot; rel=&quot;nofollow&quot;&gt;Own your content on Social Media using the IndieWeb&lt;/a&gt;  by Zach Leatherman&lt;/li&gt;



&lt;li&gt;&lt;a href=&quot;https://matthiasott.com/articles/into-the-personal-website-verse&quot; rel=&quot;nofollow&quot;&gt;Into the&amp;nbsp;Personal-Website-Verse&lt;/a&gt; by Matthias Ott&lt;/li&gt;



&lt;li&gt;&lt;a href=&quot;https://www.miriamsuzanne.com/2022/06/04/indiweb/&quot; rel=&quot;nofollow&quot;&gt;Am I on the IndieWeb Yet?&lt;/a&gt; by Miriam Suzanne&lt;/li&gt;



&lt;li&gt;&lt;a href=&quot;https://www.theverge.com/23513418/bring-back-personal-blogging&quot; rel=&quot;nofollow&quot;&gt;Bring back personal blogging&lt;/a&gt; by Monique Judge&lt;/li&gt;
&lt;/ul&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Tutorials &amp;amp; How-tos&lt;/h3&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://keithjgrant.com/posts/2019/02/adding-webmention-support-to-a-static-site/&quot; rel=&quot;nofollow&quot;&gt;Adding Webmention Support to a Static Site&lt;/a&gt; by Keith J. Grant&lt;/li&gt;



&lt;li&gt;&lt;a href=&quot;https://mxb.dev/blog/using-webmentions-on-static-sites/&quot; rel=&quot;nofollow&quot;&gt;Using Webmentions in Eleventy&lt;/a&gt; by Max Böck&lt;/li&gt;



&lt;li&gt;&lt;a href=&quot;https://sia.codes/posts/webmentions-eleventy-in-depth/&quot; rel=&quot;nofollow&quot;&gt;An In-Depth Tutorial of Webmentions + Eleventy&lt;/a&gt; by Sia Karamalegos&lt;/li&gt;



&lt;li&gt;&lt;a href=&quot;https://www.swyx.io/clientside-webmentions&quot; rel=&quot;nofollow&quot;&gt;Clientside Webmentions&lt;/a&gt; by swyx&lt;/li&gt;
&lt;/ul&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Documentation &amp;amp; Services&lt;/h3&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://indieweb.org/&quot; rel=&quot;nofollow&quot;&gt;IndieWeb&lt;/a&gt; &amp;#8211; specifically &lt;a href=&quot;https://indieweb.org/Webmention&quot; rel=&quot;nofollow&quot;&gt;IndieWeb Webmention&lt;/a&gt; for an intro on what they are and how to implement&lt;/li&gt;



&lt;li&gt;&lt;a href=&quot;https://microformats.org/wiki/microformats2&quot; rel=&quot;nofollow&quot;&gt;Microformats&lt;/a&gt; &amp;#8211; a simple way to markup structured information in HTML&lt;/li&gt;



&lt;li&gt;&lt;a href=&quot;https://webmention.io/&quot; rel=&quot;nofollow&quot;&gt;Webmention.io&lt;/a&gt; &amp;#8211; a hosted service created to easily receive webmentions on any web page&lt;/li&gt;



&lt;li&gt;&lt;a href=&quot;https://brid.gy/&quot; rel=&quot;nofollow&quot;&gt;Brid.gy&lt;/a&gt; &amp;#8211; connects your web site to social media&lt;/li&gt;
&lt;/ul&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Great Implementation Examples&lt;/h3&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://mxb.dev/&quot;&gt;Max Böck&lt;/a&gt;&lt;/li&gt;



&lt;li&gt;&lt;a href=&quot;https://yatil.net/&quot; rel=&quot;nofollow&quot;&gt;Eric Eggert&lt;/a&gt;&lt;/li&gt;



&lt;li&gt;&lt;a href=&quot;https://sia.codes/&quot; rel=&quot;nofollow&quot;&gt;Sia Karamalegos&lt;/a&gt;&lt;/li&gt;



&lt;li&gt;&lt;a href=&quot;https://adactio.com/&quot; rel=&quot;nofollow&quot;&gt;Jeremy Keith&lt;/a&gt;&lt;/li&gt;



&lt;li&gt;&lt;a href=&quot;https://www.zachleat.com/&quot; rel=&quot;nofollow&quot;&gt;Zach Leatherman&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Work in Progress&lt;/h2&gt;



&lt;p&gt;There&amp;#8217;s still more that I want to do with this, I&amp;#8217;d love to find a good/automated way to capture syndication links for example. But I&amp;#8217;m happy enough with what I&amp;#8217;ve built so far to launch it and try to iterate over time. Check it out &lt;a href=&quot;https://stevenwoodson.com/blog/&quot;&gt;at the bottom of blog posts on my site&lt;/a&gt;!&lt;/p&gt;



&lt;p&gt;What opinion, tutorial, or other reference would you add to this list? I&amp;#8217;d love to hear about it so I can continue to learn more about this fascinating topic. I&amp;#8217;ll try to keep this list updated over time as I discover more great resources too. Thanks for reading!&lt;/p&gt;

    </content>
	</entry>
  
	<entry>
		<title>Adding a Table of Contents to dynamic content in 11ty</title>
		<link href="https://stevenwoodson.com/blog/adding-a-table-of-contents-to-dynamic-content-in-11ty"/>
		<published>2023-02-12T17:47:03.000Z</published>
		<updated>2023-08-14T13:42:45.000Z</updated>
		<id>https://stevenwoodson.com/blog/adding-a-table-of-contents-to-dynamic-content-in-11ty</id>
    <summary>Code that introduces automated anchor links to headlines in Eleventy dynamic-loaded content. Improves accessibility and makes content sharing easier!</summary>
		<content type="html">&lt;img src=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/08/Adding-a-Table-of-Contents-to-dynamic-content-in-11ty.png&quot; alt=&quot;&quot;&gt;
&lt;p&gt;This is a follow up post to &lt;a href=&quot;https://stevenwoodson.com/blog/pulling-wordpress-content-into-eleventy/&quot;&gt;Pulling WordPress Content into Eleventy&lt;/a&gt;, I&amp;#8217;ve also written about &lt;a href=&quot;https://stevenwoodson.com/blog/pulling-wordpress-post-categories-tags-into-eleventy/&quot;&gt;how to make use of WordPress post categories and tags&lt;/a&gt; too. If you haven&amp;#8217;t already I&amp;#8217;d suggest giving those a read too!&lt;/p&gt;



&lt;p&gt;As part of that transition to using dynamic data, I had lost the automated anchor links that were applied to all h2-h6 headlines. I&amp;#8217;m a huge fan of deep links like this because:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;it makes it easier to share specific sections of an article&lt;/li&gt;



&lt;li&gt;you can then create a table of contents section, helpful especially for long form content&lt;/li&gt;



&lt;li&gt;it provides another option for quick scanning and navigation&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;Well, I finally made some time to re-introduce them! Here&amp;#8217;s how I did it.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Preparing the Headlines with Anchor Tags&lt;/h2&gt;



&lt;p&gt;In my &lt;code&gt;blogposts.js&lt;/code&gt; data file I added the following function (utilizing &lt;a href=&quot;https://github.com/jsdom/jsdom&quot; rel=&quot;nofollow&quot;&gt;jsdom&lt;/a&gt;)&amp;nbsp;that&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;Finds all H2 thru H6 headlines &lt;/li&gt;



&lt;li&gt;Uses the headline text to create a slug that has stripped out all code, spaces, and special characters&lt;/li&gt;



&lt;li&gt;Appends an anchor link to each headline&lt;/li&gt;



&lt;li&gt;Compiles a JavaScript array of all headlines and the generated slugs for the next part&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;Here&amp;#8217;s the code&lt;/p&gt;



&lt;pre class=&quot;wp-block-code language-js&quot;&gt;&lt;code&gt;function prepareHeadlines(content) {
  const dom = new JSDOM(content);
  let headerElements = dom.window.document.querySelectorAll(&quot;h2,h3,h4,h5,h6&quot;);
  let headers = &amp;#91;];

  if (headerElements.length) {
    headerElements.forEach((header) =&amp;gt; {
      const slug = header.innerHTML
        .replace(/(&amp;lt;(&amp;#91;^&amp;gt;]+)&amp;gt;)/gi, &quot;&quot;)
        .replace(/&amp;amp;nbsp;|&amp;amp;amp;/gi, &quot; &quot;)
        .replace(/&amp;#91;^a-zA-Z0-9 ]/gi, &quot;&quot;)
        .replace(/ /gi, &quot;-&quot;)
        .replace(/-+/gi, &quot;-&quot;)
        .toLowerCase();

      const title = header.innerHTML.replace(/(&amp;lt;(&amp;#91;^&amp;gt;]+)&amp;gt;)/gi, &quot;&quot;);

      header.innerHTML =
        header.innerHTML +
        &#39;&amp;lt;a href=&quot;#&#39; +
        slug +
        &#39;&quot; class=&quot;anchor&quot;&amp;gt;&amp;lt;span aria-hidden=&quot;true&quot;&amp;gt;#&amp;lt;/span&amp;gt;&amp;lt;span&amp;gt;anchor&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&#39;;
      header.id = slug;

      headers.push({
        slug: slug,
        title: title,
        tagName: header.tagName,
      });
    });

    content = dom.window.document.body.innerHTML;
  }
  return { content: content, headers: headers };
}&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;There&amp;#8217;s very likely a cleaner way to do all this, but I got it to &amp;#8220;good enough&amp;#8221; and called it a day. If you&amp;#8217;ve got ideas for improvements please use the links at the bottom of this page to let me know!&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Constructing the Page Outline&lt;/h2&gt;



&lt;p&gt;With the &lt;code&gt;headers&lt;/code&gt; compiled in the function above, I then wanted to be able to show the headlines in a page outline where sub-headlines were nested under the parent. For example all H3 headlines below an H2. &lt;/p&gt;



&lt;p&gt;Here&amp;#8217;s a recursive function to handle that part.&lt;/p&gt;



&lt;pre class=&quot;wp-block-code language-js&quot;&gt;&lt;code&gt;function tableOfContentsNesting(headers, currentLevel = 2) {
  const nestedSection = {};

  if (headers.length &amp;gt; 0) {
    for (let index = 0; index &amp;lt; headers.length; index++) {
      const header = headers&amp;#91;index];
      const headerLevel = parseInt(header.tagName.substring(1, 2));

      if (headerLevel &amp;lt; currentLevel) {
        break;
      }

      if (headerLevel == currentLevel) {
        header.children = tableOfContentsNesting(
          headers.slice(index + 1),
          headerLevel + 1
        );
        nestedSection&amp;#91;header.slug] = header;
      }
    }
  }

  return nestedSection;
}&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;This will create a multidimensional object where each top-level item will optionally have a &lt;code&gt;children&lt;/code&gt; property that contains its child headings, and can go all the way down to H6.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Pulling it all together&lt;/h2&gt;



&lt;p&gt;Those two functions defined above do the heavy lifting, but they still need to be applied to the &lt;code&gt;processContent&lt;/code&gt; function so the data that the site uses will have this new content available. &lt;/p&gt;



&lt;p&gt;Here&amp;#8217;s the relevant changes:&lt;/p&gt;



&lt;pre class=&quot;wp-block-code language-js&quot;&gt;&lt;code&gt;// Applying ID anchors to headlines and returning a flat list of headers for an outline
const prepared = prepareHeadlines(post.content.rendered);

// Code highlighting with Eleventy Syntax Highlighting
// https://www.11ty.dev/docs/plugins/syntaxhighlight/
const formattedContent = highlightCode(prepared.content);

// Create a multidimensional outline using the flat outline provided by prepareHeadlines
const tableOfContents = tableOfContentsNesting(prepared.headers);


// Return only the data that is needed for the actual output
return await {
  content: post.content.rendered,
  formattedContent: formattedContent,
  tableOfContents: tableOfContents,
  custom_fields: post.custom_fields ? post.custom_fields : null,&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;I opted to save the &lt;code&gt;formattedContent&lt;/code&gt; separate from the &lt;code&gt;content&lt;/code&gt; so I could have the unformatted content to use in my XML Feed that doesn&amp;#8217;t really need all that extra HTML. I then also added &lt;code&gt;tableOfContents&lt;/code&gt; so I can use it in my template. Speaking of, that brings us to the next section.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Creating a Table of Contents Macro&lt;/h2&gt;



&lt;p&gt;Because the &lt;code&gt;tableOfContents&lt;/code&gt; is a multidimensional object that can be (theoretically) 5 levels deep, I wanted to make sure the table of contents I&amp;#8217;m adding to the page would be able to handle all that too. &lt;/p&gt;



&lt;p&gt;So, I turned to the handy dandy &lt;a href=&quot;https://mozilla.github.io/nunjucks/templating.html#keyword-arguments&quot; rel=&quot;nofollow&quot;&gt;Nunjucks Macros&lt;/a&gt; to do the job. I chose macros because I can pass just the data I want it to be concerned with and not have to mess around with global data in standard templates. I&amp;#8217;ll admit I tried a template first and ended up in some infinite loop situations, lesson learned!&lt;/p&gt;



&lt;p&gt;Here&amp;#8217;s the Table of Contents macro I created, saved at &lt;code&gt;site/_includes/macros/tableofcontents.njk&lt;/code&gt;.&lt;/p&gt;



&lt;pre class=&quot;wp-block-code language-html&quot;&gt;&lt;code&gt;{% macro tableOfContents(items) %}
&amp;lt;ul&amp;gt;
  {% for key, item in items %}
    &amp;lt;li&amp;gt;&amp;lt;a href=&quot;#{{ item.slug }}&quot;&amp;gt;{{ item.title | safe }}&amp;lt;/a&amp;gt;
      {%- if item.children | length %}
        {{ tableOfContents(item.children) }}
      {% endif %}
    &amp;lt;/li&amp;gt;
  {% endfor %}
&amp;lt;/ul&amp;gt;
{% endmacro %}&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;Pretty simple right? That&amp;#8217;s because it&amp;#8217;s set up to run recursively, so it&amp;#8217;s calling itself to create the nested lists that we&amp;#8217;re going to render on the page.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Adding to the Blogpost Template&lt;/h2&gt;



&lt;p&gt;Alright it&amp;#8217;s the moment of truth, let&amp;#8217;s get all that into the page template!&lt;/p&gt;



&lt;p&gt;I chose to put this list inside a &lt;code&gt;&amp;lt;details&amp;gt;&lt;/code&gt; element so it can be collapsed by default, though you can also update to include &lt;code&gt;open&lt;/code&gt; as a property of &lt;code&gt;&amp;lt;details&amp;gt;&lt;/code&gt; to have it collapsible but open by default. Up to you!&lt;/p&gt;



&lt;pre class=&quot;wp-block-code language-html&quot;&gt;&lt;code&gt;&amp;lt;nav aria-label=&quot;Article&quot;&amp;gt;
  &amp;lt;details&amp;gt;
    &amp;lt;summary&amp;gt;In This Article&amp;lt;/summary&amp;gt;
    {{ tableOfContents(blogpost.tableOfContents) }}
  &amp;lt;/details&amp;gt;
&amp;lt;/nav&amp;gt;&lt;/code&gt;&lt;/pre&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Wrapping Up&lt;/h2&gt;



&lt;p&gt;That&amp;#8217;s all there was to it. It&amp;#8217;s not a lot of code but I&amp;#8217;ll admit the recursive aspects took me a bit of head scratching to figure out initially. Hoping it saves you a bit of that struggle.&lt;/p&gt;



&lt;p&gt;If you end up using this, or improving upon it, please reach out and let me know!&lt;/p&gt;

    </content>
	</entry>
  
	<entry>
		<title>Solving Animation Layout Flickering Caused by CSS Transitions</title>
		<link href="https://stevenwoodson.com/blog/solving-animation-layout-flickering-caused-by-css-transitions"/>
		<published>2023-02-04T17:12:58.000Z</published>
		<updated>2023-08-10T17:27:04.000Z</updated>
		<id>https://stevenwoodson.com/blog/solving-animation-layout-flickering-caused-by-css-transitions</id>
    <summary>Solving layout flickering (or animation flashing) happening on browser resize caused by CSS transitions, with live before and after examples!</summary>
		<content type="html">&lt;img src=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/02/Solving-Animation-Layout-Flickering-Caused-by-CSS-Transitions.png&quot; alt=&quot;&quot;&gt;
&lt;p&gt;I recently had the opportunity to redo the header section of an inherited codebase. Everything was going great, my version was nearly 30% less code, more accessible with larger clickable targets and focus handling, and life was good.&lt;/p&gt;



&lt;p&gt;But then it happened&amp;#8230;&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;The Layout Flickering Problem&lt;/h2&gt;



&lt;p&gt;As I&amp;#8217;m testing in a few browsers I noticed the mobile version of the header menu was flickering closed when resizing from the desktop to the mobile breakpoint. A quick debug later and I realized it was because of the &lt;code&gt;transition&lt;/code&gt; CSS property I have set on the menu so it&amp;#8217;ll slide out and fade in when opened.&lt;/p&gt;



&lt;p&gt;Specifically, that transition is triggered when going from desktop to mobile because I&amp;#8217;m visually hiding it once it gets to a point where it needs to be opened with a button. When those styles are applied, the transition animation triggers.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Before Example&lt;/h3&gt;



&lt;p&gt;Here&amp;#8217;s a live example of the CSS animation flashing I&amp;#8217;m talking about:&lt;/p&gt;



&lt;p class=&quot;codepen&quot; data-height=&quot;500&quot; data-default-tab=&quot;result&quot; data-slug-hash=&quot;abjGjrr&quot; data-user=&quot;stevenwoodson&quot; style=&quot;height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;&quot;&gt;
  &lt;span&gt;See the Pen &lt;a href=&quot;https://codepen.io/stevenwoodson/pen/abjGjrr&quot;&gt;
  Flashing Mobile Only Slide Out Menu Example&lt;/a&gt; by Steve Woodson (&lt;a href=&quot;https://codepen.io/stevenwoodson&quot;&gt;@stevenwoodson&lt;/a&gt;)
  on &lt;a href=&quot;https://codepen.io/&quot;&gt;CodePen&lt;/a&gt;.&lt;/span&gt;
&lt;/p&gt;
&lt;script async=&quot;&quot; src=&quot;https://cpwebassets.codepen.io/assets/embed/ei.js&quot;&gt;&lt;/script&gt;



&lt;p&gt;I didn&amp;#8217;t want to remove the CSS transition, but I also didn&amp;#8217;t want this weird animation flashing on resize, so what do I do?&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;The Prevent Animation Transition Solution&lt;/h2&gt;



&lt;p&gt;I wasn&amp;#8217;t very happy with any of the solutions I found out there, they were some variation of:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;really heavy-handed, like removing all transitions while the browser is resized&lt;/li&gt;



&lt;li&gt;relied on fragile solutions like &lt;code&gt;setTimeout&lt;/code&gt;&lt;/li&gt;



&lt;li&gt;&amp;#8220;just don&amp;#8217;t use animation 🤷‍♀️&amp;#8221;&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;So I got to work. The obvious problem was the &lt;code&gt;transition&lt;/code&gt; applying when adding the mobile-only styles so there&amp;#8217;s gotta be a way to prevent just that one use case, right? You bet there is, and it&amp;#8217;s only two small changes!&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Isolate &lt;code&gt;transition&lt;/code&gt; Into a Unique Class&lt;/h3&gt;



&lt;p&gt;First, we need to isolate the transition separately from the styles that are applied when the menu is opened. In my case it was a new class called &lt;code&gt;nav-opening&lt;/code&gt;:&lt;/p&gt;



&lt;pre class=&quot;wp-block-code language-css&quot;&gt;&lt;code&gt;.nav-opening {
  transition: all 0.5s ease;
}&lt;/code&gt;&lt;/pre&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Add &amp;amp; Remove the Transition&lt;/h3&gt;



&lt;p&gt;Second, we need to add the transition-only  &lt;code&gt;nav-opening&lt;/code&gt; class when adding the open class &lt;code&gt;nav-opened&lt;/code&gt;. This happens on click of the mobile menu button:&lt;/p&gt;



&lt;pre class=&quot;wp-block-code language-js&quot;&gt;&lt;code&gt;this.el.classList.add(&#39;nav-opening&#39;);
this.el.classList.add(&#39;nav-opened&#39;);&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;And then we need to remove &lt;code&gt;nav-opening&lt;/code&gt; when the animation is done closing, for this I turned to &lt;code&gt;transitionend&lt;/code&gt;. That way I don&amp;#8217;t have to guess with something like &lt;code&gt;setTimeout&lt;/code&gt;, I trigger the removal of that class when I know the animation is done. Like this:&lt;/p&gt;



&lt;pre class=&quot;wp-block-code language-js&quot;&gt;&lt;code&gt;this.el.addEventListener(&#39;transitionend&#39;, this.setFocus.bind(this));

SlideOutMenu.prototype.setFocus = function (e) {
  if (e.target == this.el &amp;amp;&amp;amp; e.propertyName == &#39;opacity&#39;) {
    const compStyles = window.getComputedStyle(this.el);
    if (compStyles.opacity == 1) {
      this.firstTabStop.focus();
    } else {
      this.openButton.focus();
      this.el.classList.remove(&#39;nav-opening&#39;);
    }
  }
};&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;The &lt;code&gt;setFocus&lt;/code&gt; function here is checking that the animation &lt;code&gt;transitionend&lt;/code&gt; happened on the element we care about, and only triggering for the opacity transition. The latter is just in case you have multiple transitions happening (opacity, left, visibility, etc.), you don&amp;#8217;t want this running for all of them every time.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;After Example&lt;/h3&gt;



&lt;p&gt;Here&amp;#8217;s the same example with the above modifications applied, no more layout flickering on resize!&lt;/p&gt;



&lt;p class=&quot;codepen&quot; data-height=&quot;500&quot; data-default-tab=&quot;result&quot; data-slug-hash=&quot;KKBJYKE&quot; data-user=&quot;stevenwoodson&quot; style=&quot;height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;&quot;&gt;
  &lt;span&gt;See the Pen &lt;a href=&quot;https://codepen.io/stevenwoodson/pen/KKBJYKE&quot;&gt;
  Flashing Mobile Only Slide Out Menu Example&lt;/a&gt; by Steve Woodson (&lt;a href=&quot;https://codepen.io/stevenwoodson&quot;&gt;@stevenwoodson&lt;/a&gt;)
  on &lt;a href=&quot;https://codepen.io/&quot;&gt;CodePen&lt;/a&gt;.&lt;/span&gt;
&lt;/p&gt;
&lt;script async=&quot;&quot; src=&quot;https://cpwebassets.codepen.io/assets/embed/ei.js&quot;&gt;&lt;/script&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Wrapping Up&lt;/h2&gt;



&lt;p&gt;The code examples in CodePen were intentionally kept lean on styles and uses vanilla JavaScript in the hopes that you can more easily make use of it in your project. Be sure to copy from the After example, otherwise I wrote this whole blog post for nothing!&lt;/p&gt;



&lt;p&gt;I wanted to also note that, while a bit too much for my use case, I do really like this &lt;a href=&quot;https://css-tricks.com/stop-animations-during-window-resizing/&quot; rel=&quot;nofollow&quot;&gt;Stop Animations During Window Resizing&lt;/a&gt; example that Chris worked up. If you have a lot going on transitions-wise this may be a cleaner solution for you than having to manage each piece individually.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Further Reading&lt;/h3&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://inclusive-components.design/menus-menu-buttons/&quot; rel=&quot;nofollow&quot;&gt;Menus &amp;amp; Menu Buttons&lt;/a&gt; &amp;#8211; Inclusive Components by Heydon Pickering&lt;/li&gt;



&lt;li&gt;&lt;a href=&quot;https://moderncss.dev/css-only-accessible-dropdown-navigation-menu/&quot; rel=&quot;nofollow&quot;&gt;CSS-Only Accessible Dropdown Navigation Menu&lt;/a&gt; &amp;#8211; Stephanie Eckles&lt;/li&gt;



&lt;li&gt;&lt;a href=&quot;https://www.joshwcomeau.com/animation/css-transitions/&quot; rel=&quot;nofollow&quot;&gt;An Interactive Guide to CSS Transitions&lt;/a&gt; &amp;#8211; Josh W Comeau&lt;/li&gt;
&lt;/ul&gt;

    </content>
	</entry>
  
	<entry>
		<title>A Step-by-Step Guide to Sorting Eleventy Global Data Files by Date</title>
		<link href="https://stevenwoodson.com/blog/a-step-by-step-guide-to-sorting-eleventy-global-data-files-by-date"/>
		<published>2023-02-02T13:26:55.000Z</published>
		<updated>2023-08-14T13:35:03.000Z</updated>
		<id>https://stevenwoodson.com/blog/a-step-by-step-guide-to-sorting-eleventy-global-data-files-by-date</id>
    <summary>Step-by-step guide to date-based global data sorting in Eleventy, including automated by file last modified and manually via a data property</summary>
		<content type="html">&lt;img src=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/08/A-Step-by-Step-Guide-to-Sorting-Eleventy-Global-Data-Files-by-Date.png&quot; alt=&quot;&quot;&gt;
&lt;p&gt;In Eleventy, there&amp;#8217;s a lot of helpful built in methods to manage &lt;em&gt;content&lt;/em&gt; sorting by date. But if you wanted to manage your static (or API-driven?) &lt;em&gt;data&lt;/em&gt; files by date then you&amp;#8217;ve got a bit of manual work to do. In this post I&amp;#8217;m going to share two date sorting options, automated by file last modified and manually via another data property. Let&amp;#8217;s see some code!&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;The Setup&lt;/h2&gt;



&lt;p&gt;Both of the following sorting options assume that you have a data folder set up in your Eleventy codebase full of files of individual records. A data collection object of objects will then be made available to you at build time.&lt;/p&gt;



&lt;p&gt;For example, with this folder structure:&lt;/p&gt;



&lt;pre class=&quot;wp-block-code&quot;&gt;&lt;code&gt;_data
└─ widgets
   │  Algorithm Ace.json
   │  Binary Buddy.json
   │  Debugging Dynamo.json
   │  Function Friend.json
   │  Loop Leader.json
   │  Statement Strategist.json
   │  Syntax Savior.json&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;You&amp;#8217;ll get something like the following collection when referencing &lt;code&gt;widgets&lt;/code&gt; in your templates, where &lt;code&gt;intro&lt;/code&gt; is some example data inside the individual JSON files:&lt;/p&gt;



&lt;pre class=&quot;wp-block-code language-json&quot;&gt;&lt;code&gt;{
  &#39;Algorithm Ace&#39;: {
    intro: &#39;The expert in all things algorithm, helping you optimize your code like a pro.&#39;
  },
  &#39;Binary Buddy&#39;: {
    intro: &#39;The ultimate sidekick for your digital adventures, always there to convert and calculate.&#39;
  },
  &#39;Debugging Dynamo&#39;: {
    intro: &#39;Helping you solve your coding problems with lightning speed, making debugging a breeze.&#39;
  },
  &#39;Function Friend&#39;: {
    intro: &#39;A trusty organized companion whose motto is &quot;A place for everything and everything in its place&quot;&#39;
  },
  &#39;Loop Leader&#39;: {
    intro: &#39;The master at managing loops, ensuring your code runs smoothly and efficiently every time.&#39;
  },
  &#39;Statement Strategist&#39;: {
    intro: &#39;Fearlessly guiding you through even the toughest if/else statements&#39;
  },
  &#39;Syntax Savior&#39;: {
    intro: &#39;Never fear whether you need a color or a semicolon again!&#39;
  }
}&lt;/code&gt;&lt;/pre&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Automated Date Sorting by Last Modified&lt;/h2&gt;



&lt;p&gt;I wanted to document this one for posterity because, alas, it ended up not being the solution we wanted for the problem we were solving. But it works great if you have a need to sort by modified date, less control but more automated. Here&amp;#8217;s how it works&lt;/p&gt;



&lt;p&gt;Create a filter in your 11ty config file at &lt;code&gt;.eleventy.js&lt;/code&gt; like the following.&lt;/p&gt;



&lt;pre class=&quot;wp-block-code language-js&quot;&gt;&lt;code&gt;  eleventyConfig.addFilter(&quot;sortDataByDate&quot;, (obj) =&amp;gt; {
    const fs = require(&quot;fs&quot;);
    const directory_name = &quot;data/members&quot;;
    const filenames = fs.readdirSync(directory_name);
    const sorted = {};

    filenames
      .map((fileName) =&amp;gt; ({
        name: fileName,
        time: fs.statSync(`${directory_name}/${fileName}`).mtime.getTime(),
      }))
      .sort((a, b) =&amp;gt; b.time - a.time) // Latest first, swap a &amp;amp; b for opposite
      .map((file) =&amp;gt; file.name.split(&quot;.&quot;)&amp;#91;0])
      .forEach((name) =&amp;gt; (sorted&amp;#91;name] = obj&amp;#91;name]));
    return sorted;
  });&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;Note that the above code sorts by latest first, if you need to swap that then reverse &lt;code&gt;a&lt;/code&gt; and &lt;code&gt;b&lt;/code&gt; in line 12.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Manual Date Sorting by Data Property&lt;/h2&gt;



&lt;p&gt;This is going to take a bit more work, but you&amp;#8217;ll have the most control over how the sorting happens. So if you don&amp;#8217;t want the order of sorting to change as you change your files, this is the one for you.&lt;/p&gt;



&lt;p&gt;First, you&amp;#8217;ll need to add dates to the data in each file. The format is up to you, as long as it&amp;#8217;s parsable by JavaScript. I&amp;#8217;d recommend either a plain date like &lt;code&gt;2023-12-25&lt;/code&gt; if you don&amp;#8217;t need to get too granular, or date and time like `&lt;code&gt;2023-12-25T12:30&lt;/code&gt; if you also need time. You can get into seconds too, if you need.&lt;/p&gt;



&lt;p&gt;Here&amp;#8217;s an updated example of good ol&amp;#8217; Binary Buddy&lt;/p&gt;



&lt;pre class=&quot;wp-block-code&quot;&gt;&lt;code&gt;{
  date: &#39;&lt;span style=&quot;background-color: rgba(0, 0, 0, 0.2); font-family: inherit; font-size: inherit; color: initial;&quot;&gt;2023-12-25T12:30&lt;/span&gt;&#39;,
  intro: &#39;The ultimate sidekick for your digital adventures, always there to convert and calculate.&#39;
}&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;And here&amp;#8217;s a new filter to add to the Eleventy config:&lt;/p&gt;



&lt;pre class=&quot;wp-block-code language-js&quot;&gt;&lt;code&gt;  /**
   * Sort by data files `date` field
   */
  eleventyConfig.addFilter(&quot;sortDataByDate&quot;, (obj) =&amp;gt; {
    const sorted = {};
    Object.keys(obj)
      .sort((a, b) =&amp;gt; {
        return obj&amp;#91;a].date &amp;gt; obj&amp;#91;b].date ? 1 : -1;
      })
      .forEach((name) =&amp;gt; (sorted&amp;#91;name] = obj&amp;#91;name]));
    return sorted;
  });&lt;/code&gt;&lt;/pre&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Using the filtered data in your template&lt;/h2&gt;



&lt;p&gt;Regardless of whether you chose manual or automated date sorting, the resulting usage in a template is the same. Here&amp;#8217;s an example of the for loop you&amp;#8217;ll need, with the sorting filter applied to it:&lt;/p&gt;



&lt;pre class=&quot;wp-block-code&quot;&gt;&lt;code&gt;&amp;lt;ul class=&quot;widgets&quot;&amp;gt;
{% for key, widget in widgets | sortDataByDate %}
  &amp;lt;li&amp;gt;{{ key }} - {{ widget.intro }}&amp;lt;/li&amp;gt;
{% endfor %}
&amp;lt;/ul&amp;gt;&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;That&amp;#8217;s it! Now that list will be sorted by the last modified date of the files within your &lt;code&gt;_data/widgets&lt;/code&gt; folder.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Some Background&lt;/h2&gt;



&lt;p&gt;Adding this at the end here for folks that are interested in how this came about, as I realize it&amp;#8217;s a rather interesting use case. I heard about &lt;a href=&quot;https://a11y-webring.club/&quot;&gt;a11y-webring.club&lt;/a&gt; that &lt;a href=&quot;https://ericwbailey.website/&quot;&gt;Eric Bailey&lt;/a&gt; started and &amp;#8211; of course &amp;#8211; jumped at the chance to get this site added to it.&lt;/p&gt;



&lt;p&gt;To get added, you have to start a pull request adding your details to a single members.json file in the 11ty-based site code. As you can imagine, once more than one person edited that file to add their details a merge conflict ensued. So I started &lt;a href=&quot;https://github.com/ericwbailey/a11y-webring.club/issues/60&quot;&gt;this discussion&lt;/a&gt; to bring up the idea of file-based additions instead of everyone editing one file, which led to &lt;a href=&quot;https://github.com/ericwbailey/a11y-webring.club/pull/67&quot;&gt;this PR&lt;/a&gt;.&lt;/p&gt;



&lt;p&gt;In working through a solution to ensure folks were added chronologically, I came up with both methods outlined in this post. We ended up going with the latter.&lt;/p&gt;



&lt;p&gt;Have you tackled date-based data sorting another way? We&amp;#8217;d love to hear more perspectives and insights on this!&lt;/p&gt;

    </content>
	</entry>
  
	<entry>
		<title>Getting Started in Technical Leadership</title>
		<link href="https://stevenwoodson.com/blog/getting-started-in-technical-leadership"/>
		<published>2023-01-18T01:08:59.000Z</published>
		<updated>2023-08-10T17:24:18.000Z</updated>
		<id>https://stevenwoodson.com/blog/getting-started-in-technical-leadership</id>
    <summary>I get asked &amp;#8220;how do I get started in technical leadership&amp;#8221; enough that I decided to share a bit of what has helped me grow over the years.</summary>
		<content type="html">&lt;img src=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/01/getting-started-in-technical-leadership.png&quot; alt=&quot;&quot;&gt;
&lt;p&gt;I&amp;#8217;ve been given the immense privilege of leading projects and teams of various sizes over the years, and an equally immense amount of grace as I inevitably messed &lt;em&gt;something&lt;/em&gt; up in each of them. It&amp;#8217;s part of the natural progression of shifting from an individual contributor to a leader.&lt;/p&gt;



&lt;p&gt;I&amp;#8217;m very much still learning myself, but I get asked &amp;#8220;how do I get started&amp;#8221; enough that I decided to draft this post to share a bit of what has helped me grow over the years. In addition to lived experiences, there are a whole lot of resources I&amp;#8217;ve devoured that helped me frame my thoughts, get clarity, and armed me with templates that helped me make sense of this weird, hectic, beautiful, nebulous, and deeply rewarding responsibility.&lt;/p&gt;



&lt;p&gt;I&amp;#8217;ve split this up into the major concepts that I rely on day to day, at the bottom of each I&amp;#8217;ve linked to several resources that have helped me in some way. They explain these topics with the depth they deserve, all have left a lasting impression.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;The Many Types of Leadership&lt;/h2&gt;



&lt;p&gt;There&amp;#8217;s countless types of leadership out there, and it is all going to depend on your industry and the size and structure of your organization. But I find it easier to think in terms of what is most often in focus, and for that I break it down into leadership of people, of projects, and of operations.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;People Leadership&lt;/h3&gt;



&lt;p&gt;Coming from a technical background, our analytical brains are celebrated and our skills honed specifically to break down tasks in ways that computers can interpret. We don&amp;#8217;t tend to be as naturally adept at doing the same for people, it takes a different kind of mindset and a whole lot more effort in collaboration and understanding.&lt;/p&gt;



&lt;p&gt;However, there truly is no greater joy than those opportunities to help someone achieve something that&amp;#8217;s important to them. &lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Project Leadership&lt;/h3&gt;



&lt;p&gt;Not necessarily project management, think more like technical team leads, architects, and staff engineers. These are technical leaders that are stepping out &amp;#8211; at least in part &amp;#8211; of the day to day code to do other things that support the team.&lt;/p&gt;



&lt;p&gt;These are the people that translate project goals into technical milestones, make high level architectural decisions, helps with defining and grooming epics, creating tickets, identifying risk, and otherwise supports the rest of the engineering team.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Operational Leadership&lt;/h3&gt;



&lt;p&gt;These are the Engineering Managers, Developer Relations, and Director level roles that are less in the day to day leadership of individuals and projects but are still technical in nature.&lt;/p&gt;



&lt;p&gt;This one is admittedly a bit broad in scope, I apologize to folks in any of these roles who may feel I&amp;#8217;m over-generalizing.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;How do I choose?&lt;/h3&gt;



&lt;p&gt;While many (most?) roles are likely going to span two or even all three of these types of leadership, it&amp;#8217;s likely that one of them is going to be of higher interest to you. My suggestion would be to lean into that one initially.&lt;/p&gt;



&lt;p&gt;Leadership duties can be emotionally draining, especially as you&amp;#8217;re just starting out. Focusing on the parts that are of greater interest to you will hopefully give you some extra energy to offset some of the drain as you work on building your resiliency.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Standout Skills&lt;/h2&gt;



&lt;p&gt;In my attempt to maintain some semblance of brevity with this post, I&amp;#8217;m focusing on the top three skills that I&amp;#8217;ve turned to most in my technical leadership journey. For each I&amp;#8217;ll give a quick what and why, some of my must have notes, and then recommended books that fundamentally changed how I approach that skill.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Organization&lt;/h3&gt;



&lt;p&gt;The more leadership you take on, the less things are going to come to you organized and ready to go. More often than not you&amp;#8217;re going to be the one that becomes that organizing filter for others. Embracing nebulous requests, context switching, and acting as a mentor to others all require some level of personal and professional organization.&lt;/p&gt;



&lt;h4 class=&quot;wp-block-heading&quot;&gt;Organization Must Haves&lt;/h4&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;Create a todo list that never leaves your side, offload as much as you can to it so you can focus on thinking instead of remembering&lt;/li&gt;



&lt;li&gt;Documentation is a superpower, take some time to set up a place you can quickly turn to for realtime notes and use it &amp;#8211; especially during meetings&lt;/li&gt;



&lt;li&gt;Strive for context and clarity in everything&lt;/li&gt;



&lt;li&gt;Follow up when you say you&amp;#8217;re going to&lt;/li&gt;
&lt;/ul&gt;



&lt;h4 class=&quot;wp-block-heading&quot;&gt;Recommended Reading&lt;/h4&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://www.indiebound.org/book/9780735211292?aff=stevenwoodson&quot; rel=&quot;sponsored nofollow&quot;&gt;Atomic Habits&lt;/a&gt;&lt;/strong&gt; by &lt;em&gt;James Clear&lt;/em&gt;&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://www.indiebound.org/book/9780679762881?aff=stevenwoodson&quot; rel=&quot;sponsored nofollow&quot;&gt;High Output Management&lt;/a&gt; &lt;/strong&gt;by &lt;em&gt;Andrew S Grove&lt;/em&gt;&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://www.indiebound.org/book/9781500615994?aff=stevenwoodson&quot; rel=&quot;sponsored nofollow&quot;&gt;How to Make Sense of Any Mess&lt;/a&gt;&lt;/strong&gt; by &lt;em&gt;Abby Covert&lt;/em&gt;&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://www.indiebound.org/book/9780525536222?aff=stevenwoodson&quot; rel=&quot;sponsored nofollow&quot;&gt;Measure What Matters&lt;/a&gt;&lt;/strong&gt; by &lt;em&gt;John Doerr&lt;/em&gt;&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://www.indiebound.org/book/9781501121746?aff=stevenwoodson&quot; rel=&quot;sponsored nofollow&quot;&gt;Sprint: How to Solve Big Problems and Test New Ideas in Just Five Days&lt;/a&gt;&lt;/strong&gt; by Jake Knapp&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://www.indiebound.org/book/9780312430009?aff=stevenwoodson&quot; rel=&quot;sponsored nofollow&quot;&gt;The Checklist Manifesto&lt;/a&gt;&lt;/strong&gt; by &lt;em&gt;Atul Gawande&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Communication&lt;/h3&gt;



&lt;p&gt;Being a leader means a whole lot of communication, both in terms of clearly articulating what you&amp;#8217;re trying to say as well as active listening when others are speaking. It takes a great deal of empathy and emotional intelligence to communicate effectively.&lt;/p&gt;



&lt;h4 class=&quot;wp-block-heading&quot;&gt;Communication Must Haves&lt;/h4&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;Assume best intentions from everyone, start with empathy&lt;/li&gt;



&lt;li&gt;Psychological safety is paramount to an effective and happy team&lt;/li&gt;



&lt;li&gt;Listen with the intent to understand, not to respond&lt;/li&gt;



&lt;li&gt;Learn how to recognize burnout and boredom in others, and how to recognize what drains and what recharges you too&lt;/li&gt;
&lt;/ul&gt;



&lt;h4 class=&quot;wp-block-heading&quot;&gt;Recommended Reading&lt;/h4&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://stevenwoodson.com/blog/soft-skills-improving-your-emotional-intelligence/&quot;&gt;&lt;strong&gt;Soft Skills: Improving Your Emotional Intelligence&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://www.indiebound.org/book/9781953450241?aff=stevenwoodson&quot; rel=&quot;sponsored nofollow&quot;&gt;Active listening&lt;/a&gt;&lt;/strong&gt; by Drs. Carl R. Rogers and Richard E. Farson&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://www.indiebound.org/book/9781984818324?aff=stevenwoodson&quot; rel=&quot;sponsored nofollow&quot;&gt;Burnout&lt;/a&gt;&lt;/strong&gt; by &lt;em&gt;Emily Nagoski and Amelia Nagoski&lt;/em&gt;&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://www.indiebound.org/book/9781250274175?aff=stevenwoodson&quot; rel=&quot;sponsored nofollow&quot;&gt;Losing our Minds&lt;/a&gt;&lt;/strong&gt; by &lt;em&gt;Lucy Foulkes, PhD&lt;/em&gt;&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://www.indiebound.org/book/9781892005281?aff=stevenwoodson&quot; rel=&quot;sponsored nofollow&quot;&gt;Nonviolent Communication&lt;/a&gt;&lt;/strong&gt; by &lt;em&gt;Marshall B. Rosenberg, PhD&lt;/em&gt;&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://www.indiebound.org/book/9781933820484?aff=stevenwoodson&quot; rel=&quot;sponsored nofollow&quot;&gt;Practical Empathy&lt;/a&gt;&lt;/strong&gt;&amp;nbsp;by Indi Young&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://www.indiebound.org/book/9780307352156?aff=stevenwoodson&quot; rel=&quot;sponsored nofollow&quot;&gt;Quiet: The Power of Introverts in a World That Can’t Stop Talking&lt;/a&gt;&lt;/strong&gt;&amp;nbsp;by Susan Cain&lt;/li&gt;
&lt;/ul&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Interpersonal Relationships&lt;/h3&gt;



&lt;p&gt;Building and maintaining relationships will take significantly more time, but it&amp;#8217;s worthwhile and necessary groundwork. Those relationships are crucial to knowing what and when to delegate, how to manage challenging situations, and when to shift from mentor to coach and from coach to sponsor.&lt;/p&gt;



&lt;h4 class=&quot;wp-block-heading&quot;&gt;Interpersonal Relationship Must Haves&lt;/h4&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;We are much more than the sum of our parts&lt;/li&gt;



&lt;li&gt;Take all the blame, share all the praise, lead by example&lt;/li&gt;



&lt;li&gt;Get comfortable with delegating and measuring productivity as a team rather than as an individual&lt;/li&gt;



&lt;li&gt;Have a support network to turn to when you need advice, need to brainstorm something particularly tricky, and to get honest feedback&lt;/li&gt;
&lt;/ul&gt;



&lt;h4 class=&quot;wp-block-heading&quot;&gt;Recommended Reading&lt;/h4&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://www.indiebound.org/book/9780399592522?aff=stevenwoodson&quot; rel=&quot;sponsored nofollow&quot;&gt;Dare to Lead&lt;/a&gt;&lt;/strong&gt; by &lt;em&gt;Brene Brown&lt;/em&gt;&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://www.indiebound.org/book/9798986769318?aff=stevenwoodson&quot; rel=&quot;sponsored nofollow&quot;&gt;Engineering Management for the Rest Of Us&lt;/a&gt;&lt;/strong&gt; by &lt;em&gt;Sarah Drasner&lt;/em&gt;&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://www.indiebound.org/book/9781250235374?aff=stevenwoodson&quot; rel=&quot;sponsored nofollow&quot;&gt;Radical Candor&lt;/a&gt;&lt;/strong&gt; by &lt;em&gt;Kim Scott&lt;/em&gt;&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://www.indiebound.org/book/9781937557881?aff=stevenwoodson&quot; rel=&quot;sponsored nofollow&quot;&gt;Resilient Management&lt;/a&gt;&lt;/strong&gt; by &lt;em&gt;Lara Hogan&lt;/em&gt;&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://www.indiebound.org/book/9781491973899?aff=stevenwoodson&quot; rel=&quot;sponsored nofollow&quot;&gt;The Manager&amp;#8217;s Path&lt;/a&gt;&lt;/strong&gt; by &lt;em&gt;Camille Fournier&lt;/em&gt;&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://www.indiebound.org/book/9781098118730?aff=stevenwoodson&quot; rel=&quot;sponsored nofollow&quot;&gt;The Staff Engineer&amp;#8217;s Path&lt;/a&gt;&lt;/strong&gt; by &lt;em&gt;Tanya Reilly&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Additional Resources&lt;/h2&gt;



&lt;p&gt;Here&amp;#8217;s a few more resources that really helped me that I couldn&amp;#8217;t neatly fit into one of the skill categories above.&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://alistapart.com/article/the-foundation-of-technical-leadership/&quot;&gt;The Foundation of Technical Leadership&lt;/a&gt; by &lt;em&gt;Brandon Gregory&lt;/em&gt;&lt;/li&gt;



&lt;li&gt;&lt;a href=&quot;https://intenseminimalism.com/2015/the-three-speeds-of-collaboration-tool-selection-and-culture-fit/&quot;&gt;The Three Speeds of Collaboration&lt;/a&gt; by &lt;em&gt;Erin &amp;#8216;Folletto&amp;#8217; Casali&lt;/em&gt;&lt;/li&gt;



&lt;li&gt;&lt;a href=&quot;https://review.firstround.com/give-away-your-legos-and-other-commandments-for-scaling-startups&quot;&gt;‘Give Away Your Legos’ and Other Commandments for Scaling Startups&lt;/a&gt; by &lt;em&gt;Molly Graham&lt;/em&gt;&lt;/li&gt;



&lt;li&gt;&lt;a href=&quot;https://www.udemy.com/course/practical-leadership/&quot;&gt;Leadership: Practical Leadership Skills&lt;/a&gt; course by &lt;em&gt;Chris Croft&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;What Would You Add?&lt;/h2&gt;



&lt;p&gt;As I had mentioned at the beginning of this post, I&amp;#8217;m still learning and am very interested to hear your thoughts on the types of leadership, standout skills, and especially any additional resources you&amp;#8217;d like to share. Use the links below to get a conversation started about it!&lt;/p&gt;

    </content>
	</entry>
  
	<entry>
		<title>Thought Experiment &#8211; Greatest Superpower and Biggest Weakness</title>
		<link href="https://stevenwoodson.com/blog/thought-experiment-greatest-superpower-and-biggest-weakness"/>
		<published>2023-01-02T12:38:19.000Z</published>
		<updated>2023-08-10T16:30:45.000Z</updated>
		<id>https://stevenwoodson.com/blog/thought-experiment-greatest-superpower-and-biggest-weakness</id>
    <summary>Reflections on my strengths and weaknesses both professionally and personally, with an action plan to leverage the strengths and improve on the weaknesses.</summary>
		<content type="html">&lt;img src=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/01/greatest-superpower-biggest-weakness.png&quot; alt=&quot;&quot;&gt;
&lt;p&gt;It could be the nostalgic reflection that seems to come on at the end of the calendar year but I&amp;#8217;m giving this some thought to see if it affects my goals going into 2023. I asked myself two questions:&lt;/p&gt;



&lt;ol class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;What&amp;#8217;s your Greatest Superpower and how are you going to leverage it?&lt;/li&gt;



&lt;li&gt;What&amp;#8217;s your Biggest Weakness and how are you going to work on it?&lt;a href=&quot;https://twitter.com/stevenwoodson/status/1604838871895875584&quot;&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;



&lt;p&gt;As I started to consider these questions, I realized that each of them could be answered differently based on the role areas of my life. This is something I&amp;#8217;ve put a lot of thought into after reading books like &lt;em&gt;The 7 Habits of Highly Effective People&lt;/em&gt; and &lt;em&gt;Getting Things Done&lt;/em&gt;. Habit 2 in The 7 Habits has a whole section on identifying role areas of your life and the goals you want to accomplish in each area, I highly recommend giving it a try!&lt;/p&gt;



&lt;p&gt;So I&amp;#8217;ve decided to answer these questions from two different role perspectives that each loosely combine a few roles I try to be mindful of each week, &lt;strong&gt;personal&lt;/strong&gt; and &lt;strong&gt;professional&lt;/strong&gt;.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Greatest Superpower&lt;/h2&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Professional&lt;/h3&gt;



&lt;p&gt;My greatest superpower professionally has been &lt;strong&gt;patient persistence&lt;/strong&gt;, I don&amp;#8217;t easily give up on something I believe in and will keep chipping away at it until it&amp;#8217;s done. For the most part, this has revolved around making progress towards something tangible. For example coding a web application, or completing the coursework and training to become CPACC certified.&lt;/p&gt;



&lt;p&gt;This year, I plan on leveraging this superpower more for progress towards the important but less urgent intangibles to which I often don&amp;#8217;t give enough attention. Thinking areas like thought leadership and outreach.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Personal&lt;/h3&gt;



&lt;p&gt;My greatest superpower personally has been &lt;strong&gt;organization&lt;/strong&gt;, everything I want to accomplish is in a list on Todoist, long form notes are all compiled together in Evernote, and I generally am able to find what I need when I need it.&lt;/p&gt;



&lt;p&gt;This year, I plan on leveraging this superpower to do more self reflection. I want to put more effort into figuring out what&amp;#8217;s working for me and what isn&amp;#8217;t. Finding a sustainable balance in my personal productivity is often rather elusive, I&amp;#8217;m hopeful that this intentional reflection will help clarify what I&amp;#8217;ve been missing.  &lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Biggest Weakness&lt;/h2&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Professional&lt;/h3&gt;



&lt;p&gt;My biggest weakness professionally has been &lt;strong&gt;analysis paralysis&lt;/strong&gt;, I tend to get pretty far into the weeds on things that &amp;#8211; in the moment &amp;#8211; seem to matter a great deal but end up not being so important in the grand scheme of things.&lt;/p&gt;



&lt;p&gt;This year, I&amp;#8217;m going to work on it by approaching challenges with a more structured plan. I&amp;#8217;ve used many tools over the years that have helped me calm the paralysis but I&amp;#8217;ve not been consistent and fall back into bad habits. Tools like pomodoro timers, time blocking, and OKRs all help clarify &amp;#8220;what is important now?&amp;#8221; but they all take a level of advance effort and planning that I need to work on.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Personal&lt;/h3&gt;



&lt;p&gt;My biggest weakness personally has been &lt;strong&gt;procrastination&lt;/strong&gt;, I&amp;#8217;m sure this is a familiar one to many but the aspect of it that I&amp;#8217;m still trying to determine is if I&amp;#8217;m really procrastinating too much or if I&amp;#8217;ve set expectations on myself too high.&lt;/p&gt;



&lt;p&gt;This year, I&amp;#8217;m going to work on it by making sure I&amp;#8217;m setting the right goals for the right time. I&amp;#8217;ll continue to try following the mantra of &amp;#8220;Less but Better&amp;#8221;. When I&amp;#8217;m planning out my week, I&amp;#8217;m going to make sure to prioritize some down time to recharge and do things that I enjoy. Rest is vitally important to maintain the energy to tackle all my goals.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Final Thoughts&lt;/h2&gt;



&lt;p&gt;This was a really nice thought experiment, I&amp;#8217;m feeling pretty optimistic about these plans and about the tools I&amp;#8217;ve compiled to leverage my strengths and improve on my weaknesses. Here&amp;#8217;s my action plan:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;Prioritize progress in thought leadership and outreach.&lt;/li&gt;



&lt;li&gt;More self reflection by way of weekly planning and daily journaling.&lt;/li&gt;



&lt;li&gt;Structure the week ahead with more clarity and purpose, utilize pomodoro and time blocking when appropriate.&lt;/li&gt;



&lt;li&gt;&amp;#8220;Less but Better&amp;#8221;. Prioritize some down time to recharge and do things that I enjoy.&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;If this sparked a similar thought experiment for you I&amp;#8217;d really like to hear about it!&lt;/p&gt;

    </content>
	</entry>
  
	<entry>
		<title>Pulling WordPress Content into Eleventy</title>
		<link href="https://stevenwoodson.com/blog/pulling-wordpress-content-into-eleventy"/>
		<published>2022-12-17T17:46:09.000Z</published>
		<updated>2023-08-30T13:48:52.000Z</updated>
		<id>https://stevenwoodson.com/blog/pulling-wordpress-content-into-eleventy</id>
    <summary>Composable Architecture Powered by WordPress part 2 &amp;#8211; How to pull content from a WordPress RESTful API into an Eleventy generated static site.</summary>
		<content type="html">&lt;img src=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/12/pulling-wordpress-content-into-eleventy.png&quot; alt=&quot;&quot;&gt;
&lt;p&gt;This is a follow up on my previous post &lt;a href=&quot;https://stevenwoodson.com/blog/composable-architecture-powered-by-wordpress/&quot;&gt;Composable Architecture Powered by WordPress&lt;/a&gt;, focused specifically on how to pull content from WordPress via the built-in RESTful API into an &lt;a href=&quot;https://www.11ty.dev/&quot;&gt;Eleventy&lt;/a&gt;-based static site. If you still need to get WordPress set up and ready to act as a composable content source I&amp;#8217;d recommend reading that one first.&lt;/p&gt;



&lt;p&gt;Interested in deeper dives on using WordPress data in Eleventy? I have a follow up post about &lt;a href=&quot;https://stevenwoodson.com/blog/adding-a-table-of-contents-to-dynamic-content-in-11ty/&quot;&gt;how to create a table of contents based on WordPress Rest API data&lt;/a&gt; as well as one on &lt;a href=&quot;https://stevenwoodson.com/blog/pulling-wordpress-post-categories-tags-into-eleventy/&quot;&gt;how to utilize WordPress categories and tags within Eleventy&lt;/a&gt; too!&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Getting Started&lt;/h2&gt;



&lt;p&gt;There are some initial hurdles to pulling dynamic content into a static site generator like Eleventy, but the performance and portability of the end result is so worth it. I do believe &amp;#8211; now that I&amp;#8217;m a few posts in with this new setup &amp;#8211; that this is going to be an easier authoring experience (WordPress GUI vs Markdown files) without sacrificing the user experience (more performant and portable static pages vs dynamic) I had curated previously.&lt;/p&gt;



&lt;p&gt;Here&amp;#8217;s an overview of the most critical parts transitioning from &lt;strong&gt;Markdown-based Eleventy collections&lt;/strong&gt; to &lt;strong&gt;dynamic data pulled from WordPress&lt;/strong&gt;, I go into further detail in dedicated sections below:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;strong&gt;Pulling data using Fetch&lt;/strong&gt; &amp;#8211; This is the real bulk of the work, getting data dynamically from an outside source.&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;Computed Data rather than Collections&lt;/strong&gt; &amp;#8211; There&amp;#8217;s some setup and changes needed to go from one content paradigm to the other. Including rebuilding the blog pages, updating the sitemap and meta, and some style modifications.&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;Triggering a new build on publish&lt;/strong&gt; &amp;#8211; Now that content is separated from the Eleventy codebase, we need a new way to trigger a rebuild when new content is published.&lt;/li&gt;
&lt;/ul&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Pulling data using Fetch&lt;/h2&gt;



&lt;p&gt;To get all the benefits of a static site build, you need to collect and process as much content at build time as possible so that it can be served as static content. Sounds pretty straightforward, and it is when all your content is local files in your repo, but when you start loading dynamic data it gets a bit tricky. &lt;/p&gt;



&lt;p&gt;By far the biggest paradigm shift is in moving from a &lt;a href=&quot;https://www.11ty.dev/docs/collections/&quot;&gt;collection&lt;/a&gt; of posts to &lt;a href=&quot;https://www.11ty.dev/docs/data-computed/&quot;&gt;computed data&lt;/a&gt; from a dynamic data source using a &lt;a href=&quot;https://www.11ty.dev/docs/data-js/&quot;&gt;JavaScript data file&lt;/a&gt;. That necessitates pulling and processing &lt;em&gt;all of&lt;/em&gt; the content at build time.&lt;/p&gt;



&lt;p&gt;When pulling content from WordPress you need to be mindful of the 100 items per page limit imposed by the API. There are ways around this by resetting that limit but in my opinion that&amp;#8217;s not necessary because we can accommodate in how we fetch the data instead. This makes our method of gathering all data more resilient to change, and we can reuse these concepts to pull content from other data sources that may not let you change the limits. That was the architectural decision behind &lt;code&gt;getAllPosts&lt;/code&gt; and &lt;code&gt;requestPosts&lt;/code&gt; in the code below, I&amp;#8217;m using the former to coordinate pulling any number of pages of content in parallel using the latter method.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Finally, some code!&lt;/h3&gt;



&lt;p&gt;I&amp;#8217;m going to start with the full source code of the JavaScript data file I&amp;#8217;m using first, and then will explain each section/method within it afterwards and in the same order. This file is in my &lt;code&gt;&#92;site&#92;_data&lt;/code&gt; directory saved as &lt;code&gt;blogposts.js&lt;/code&gt;&lt;/p&gt;



&lt;pre class=&quot;wp-block-code language-js&quot;&gt;&lt;code&gt;const { AssetCache } = require(&quot;@11ty/eleventy-fetch&quot;);
const axios = require(&quot;axios&quot;);
const jsdom = require(&quot;jsdom&quot;);
const loadLanguages = require(&quot;prismjs/components/&quot;);
const Prism = require(&quot;prismjs&quot;);

const { JSDOM } = jsdom;
loadLanguages(&amp;#91;&quot;php&quot;]);

// Config
const ITEMS_PER_REQUEST = 10;
const API_BASE = &quot;https://mysite.com/wp-json/wp/v2/posts&quot;;

/**
 * Blog post API call by page
 *
 * @param {Int} page - Page number to fetch, defaults to 1
 * @return {Object} - Total, Pages, and full API data
 */
async function requestPosts(page = 1) {
  try {
    // https://developer.wordpress.org/rest-api/using-the-rest-api/pagination/
    const url = API_BASE;
    const params = {
      params: {
        page: page,
        per_page: ITEMS_PER_REQUEST,
        _embed: &quot;wp:featuredmedia&quot;,
        order: &quot;desc&quot;,
      },
    };
    const response = await axios.get(url, params);

    return {
      total: parseInt(response.headers&amp;#91;&quot;x-wp-total&quot;], 10),
      pages: parseInt(response.headers&amp;#91;&quot;x-wp-totalpages&quot;], 10),
      data: response.data,
    };
  } catch (err) {
    console.error(&quot;API not responding, no data returned&quot;, err);
    return {
      total: 0,
      pages: 0,
      data: &amp;#91;],
    };
  }
}

/**
 * Get all blog posts from the API
 * Use cached values if available, pull from API if not.
 *
 * @return {Array} - array of blog posts
 */
async function getAllPosts() {
  const cache = new AssetCache(&quot;blogposts&quot;);
  let requests = &amp;#91;];
  let apiData = &amp;#91;];

  if (cache.isCacheValid(&quot;2h&quot;)) {
    console.log(&quot;Using cached blogposts&quot;);
    return cache.getCachedValue();
  }

  // make first request and marge results with array
  const request = await requestPosts();
  console.log(
    &quot;Using API blogposts, retrieving &quot; +
      request.pages +
      &quot; pages, &quot; +
      request.total +
      &quot; total posts.&quot;
  );
  apiData.push(...request.data);

  if (request.pages &gt; 1) {
    // create additional requests
    for (let page = 2; page &amp;lt;= request.pages; page++) {
      const request = requestPosts(page);
      requests.push(request);
    }

    // resolve all additional requests in parallel
    const allResponses = await Promise.all(requests);
    allResponses.map((response) =&gt; {
      apiData.push(...response.data);
    });
  }

  // return data
  await cache.save(apiData, &quot;json&quot;);
  return apiData;
}

/**
 * Clean up and convert the API response for our needs
 */
async function processPosts(blogposts) {
  return Promise.all(
    blogposts.map(async (post) =&gt; {
      // remove HTML-Tags from the excerpt for meta description
      let metaDescription = post.excerpt.rendered.replace(/(&amp;lt;(&amp;#91;^&gt;]+)&gt;)/gi, &quot;&quot;);
      metaDescription = metaDescription.replace(&quot;&#92;n&quot;, &quot;&quot;);

      // Code highlighting with Eleventy Syntax Highlighting
      // https://www.11ty.dev/docs/plugins/syntaxhighlight/
      let content = highlightCode(post.content.rendered);

      // Return only the data that is needed for the actual output
      return {
        content: content,
        date: post.date,
        modifiedDate: post.modified,
        excerpt: post.excerpt.rendered,
        formattedDate: new Date(post.date).toLocaleDateString(&quot;en-US&quot;, {
          year: &quot;numeric&quot;,
          month: &quot;long&quot;,
          day: &quot;numeric&quot;,
        }),
        heroImageFull:
          post._embedded &amp;amp;&amp;amp; post._embedded&amp;#91;&quot;wp:featuredmedia&quot;].length &gt; 0
            ? post._embedded&amp;#91;&quot;wp:featuredmedia&quot;]&amp;#91;0].media_details.sizes.full
                .source_url
            : null,
        heroImageThumb:
          post._embedded &amp;amp;&amp;amp; post._embedded&amp;#91;&quot;wp:featuredmedia&quot;].length &gt; 0
            ? post._embedded&amp;#91;&quot;wp:featuredmedia&quot;]&amp;#91;0].media_details.sizes
                .medium_large.source_url
            : null,

        metaDescription: metaDescription,
        slug: post.slug,
        title: post.title.rendered,
      };
    })
  );
}

/**
 * Use Prism.js to highlight embedded code
 */
function highlightCode(content) {
  // since Prism.js works on the DOM,
  // we need an instance of JSDOM in the build
  const dom = new JSDOM(content);

  let preElements = dom.window.document.querySelectorAll(&quot;pre&quot;);

  // WordPress delivers a `code`-tag that is wrapped in a `pre`
  // the used language is specified by a CSS class
  if (preElements.length) {
    preElements.forEach((pre) =&gt; {
      let code = pre.querySelector(&quot;code&quot;);

      if (code) {
        // get specified language from css-classname
        let codeLanguage = &quot;html&quot;;
        const preClass = pre.className;

        var matches = preClass.match(/language-(.*)/);
        if (matches != null) {
          codeLanguage = matches&amp;#91;1];
        }

        // save the language for later use in CSS
        pre.dataset.language = codeLanguage;

        // set grammar that prism should use for highlighting
        let prismGrammar = Prism.languages.html;

        if (
          codeLanguage === &quot;javascript&quot; ||
          codeLanguage === &quot;js&quot; ||
          codeLanguage === &quot;json&quot;
        ) {
          prismGrammar = Prism.languages.javascript;
        }

        if (codeLanguage === &quot;css&quot;) {
          prismGrammar = Prism.languages.css;
        }

        if (codeLanguage === &quot;php&quot;) {
          prismGrammar = Prism.languages.php;
        }
        // highlight code
        code.innerHTML = Prism.highlight(
          code.textContent,
          prismGrammar,
          codeLanguage
        );

        code.classList.add(`language-${codeLanguage}`);
      }
    });

    content = dom.window.document.body.innerHTML;
  }

  return content;
}

// export for 11ty
module.exports = async () =&gt; {
  const blogposts = await getAllPosts();
  const processedPosts = await processPosts(blogposts);
  return processedPosts;
};
&lt;/code&gt;&lt;/pre&gt;



&lt;aside class=&quot;wp-block-create-block-aside&quot;&gt;
&lt;p&gt;Prism is a lightweight, extensible syntax highlighter, built with modern web standards in mind. Check out &lt;a href=&quot;https://prismjs.com/index.html&quot;&gt;prismjs.com&lt;/a&gt; for more details and examples.&lt;/p&gt;
&lt;/aside&gt;



&lt;p&gt;I&amp;#8217;m using:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://axios-http.com/&quot;&gt;Axios&lt;/a&gt; &amp;#8211; a promise based HTTP client for the browser and node.js &amp;#8211; to get the API data&lt;/li&gt;



&lt;li&gt;&lt;a href=&quot;https://www.11ty.dev/docs/plugins/fetch/&quot;&gt;AssetCache from Fetch&lt;/a&gt; to cache that response so I don&amp;#8217;t have to hit my API quite as often especially when I&amp;#8217;m working locally&lt;/li&gt;



&lt;li&gt;&lt;a href=&quot;https://prismjs.com/index.html&quot;&gt;Prism&lt;/a&gt; &amp;#8211; a lightweight syntax highlighter built with modern web standards &amp;#8211;  to preprocess code block formatting&lt;/li&gt;



&lt;li&gt;&lt;a href=&quot;https://github.com/jsdom/jsdom&quot;&gt;jsdom&lt;/a&gt; since Prism works on the DOM&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;I&amp;#8217;m also setting some configuration to load the php Prism language, and setting some defaults for items I want per page and the API base URL.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;&lt;code&gt;requestPosts&lt;/code&gt;&lt;/h3&gt;



&lt;p&gt;I&amp;#8217;ve split out &lt;code&gt;requestPosts&lt;/code&gt; as its own method with a &lt;code&gt;page&lt;/code&gt; parameter so I can gather individual pages of content at a time. This method will return the resulting data along with the total number of items (&lt;code&gt;total&lt;/code&gt;) and the total number of pages (&lt;code&gt;pages&lt;/code&gt;) for this post type.&lt;/p&gt;



&lt;p&gt;This is where you can customize your request to perform any filtering, including extra embedded data, setting an order, etc. See &lt;a href=&quot;https://developer.wordpress.org/rest-api/using-the-rest-api/&quot;&gt;Using the REST API&lt;/a&gt; for more details on these options. I&amp;#8217;m setting the page based on the provided page parameter, setting &lt;code&gt;per_page&lt;/code&gt; to the config value, and am requesting the &lt;code&gt;wp:featuredmedia&lt;/code&gt; data so I can grab the featured image URLs I need.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;&lt;code&gt;getAllPosts&lt;/code&gt;&lt;/h3&gt;



&lt;p&gt;I also needed a method that could facilitate gathering multiple pages of post content and combining them together, hence &lt;code&gt;getAllPosts&lt;/code&gt;. This checks the cache first before kicking off a new API request, if it&amp;#8217;s cached that cached response is returned instead. Otherwise it runs one (or more) &lt;code&gt;requestPosts&lt;/code&gt; calls in parallel to get all pages of content. The result is then cached and returned.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;&lt;code&gt;processPosts&lt;/code&gt;&lt;/h3&gt;



&lt;p&gt;Next, I do some light processing of the post content to simplify it for my needs here. This includes things like reducing &lt;code&gt;post.excerpt.rendered&lt;/code&gt; to &lt;code&gt;post.excerpt&lt;/code&gt; and drilling down into the &lt;code&gt;wp:featuredmedia&lt;/code&gt; object to get the full and thumbnail versions of the featured image for each post. I&amp;#8217;m also filtering the post content through &lt;code&gt;highlightCode&lt;/code&gt;, more on that next.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;&lt;code&gt;highlightCode&lt;/code&gt;&lt;/h3&gt;



&lt;p&gt;I tend to have a good deal of code blocks in my posts and I want them to be at least somewhat readable with proper formatting and highlighting, that&amp;#8217;s what this method does. &lt;code&gt;highlightCode&lt;/code&gt; will:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;run through the incoming content and find all &lt;code&gt;&amp;lt;pre&amp;gt;&lt;/code&gt; tags that denotes a code block&lt;/li&gt;



&lt;li&gt;for each code block identified it&amp;#8217;ll look for a classname that starts with &lt;code&gt;language-&lt;/code&gt; and will set the &lt;code&gt;codeLanguage&lt;/code&gt; and &lt;code&gt;prismGrammar&lt;/code&gt; accordingly and defaults to HTML.&lt;/li&gt;



&lt;li&gt;triggers a &lt;code&gt;Prism.highlight&lt;/code&gt; based on what was determined above.&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;This is where you&amp;#8217;d need to add other languages you plan to support, there&amp;#8217;s also a &lt;a href=&quot;https://prismjs.com/plugins/autoloader/&quot;&gt;Prism autoloader&lt;/a&gt; that I opted against using but it may be helpful for you if you regularly post a lot of language types.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Recap&lt;/h3&gt;



&lt;aside class=&quot;wp-block-create-block-aside&quot;&gt;
&lt;p&gt;Shout out to Martin Schneider&amp;#8217;s &lt;a href=&quot;https://martinschneider.me/articles/building-a-website-with-11ty-and-wordpress/&quot;&gt;Building a Blog with 11ty and WordPress&lt;/a&gt; and Jérôme Coupé&amp;#8217;s &lt;a href=&quot;https://www.webstoemp.com/blog/performant-data-fetching-promises-eleventy/&quot;&gt;Performant data fetching with promises and Eleventy&lt;/a&gt;, these excellent articles gave me a fairly substantial boost on the format of this data file and in performing the API calls in parallel.&lt;/p&gt;
&lt;/aside&gt;



&lt;p&gt;I need to gather all post content, in order to avoid per page limits I&amp;#8217;m determining how many pages of content I need to pull and am running those API calls &amp;#8211; one per page &amp;#8211; in parallel. The results are then combined, processed, and then parsed for code block formatting, before being returned.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Computed Data rather than Collections&lt;/h2&gt;



&lt;p&gt;Now that we have the computed data ready to go, we now move onto rebuilding the blog section which includes the main paginated blog index as well as the layout of individual posts. Let&amp;#8217;s dive right in!&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Rebuilding the blog&lt;/h3&gt;



&lt;p&gt;Rebuilding the blog section of my Eleventy site to use computed data rather than a collection was surprisingly simple, here&amp;#8217;s the major steps:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;Remove the static folder of blog posts and accompanying images&lt;/li&gt;



&lt;li&gt;Update/replace the main blog index page to use computed data&lt;/li&gt;



&lt;li&gt;Update/replace the blog post template to use computed data&lt;/li&gt;



&lt;li&gt;Other tie ins you may have, for example showing latest posts on the homepage&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;The biggest change for me was in shifting from a blogpost layout at &lt;code&gt;/site/_includes/layouts/blogpost.nkj&lt;/code&gt; to a top level page at &lt;code&gt;/site/blogpost.njk&lt;/code&gt;. this new top level page needed some additional &lt;a href=&quot;https://www.11ty.dev/docs/data-frontmatter/&quot;&gt;front matter data&lt;/a&gt; to define where the data is coming from and what the permalink should be. Here&amp;#8217;s what I ended up with:&lt;/p&gt;



&lt;pre class=&quot;wp-block-code&quot;&gt;&lt;code&gt;---
layout: layouts/base.njk
templateClass: tmpl-post
pagination:
  data: blogposts
  size: 1
  alias: blogpost
permalink: blog/{{ blogpost.slug }}/
---&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;This is pulling all blog posts from the &lt;code&gt;/site/_data/blogposts.js&lt;/code&gt; computed data and paginating it based on the blog post slug, effectively creating a unique page per post. Now I can go to &lt;a href=&quot;https://stevenwoodson.com/blog/composable-architecture-powered-by-wordpress/&quot;&gt;https://stevenwoodson.com/blog/composable-architecture-powered-by-wordpress/&lt;/a&gt; and see that post.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Sitemap and Metadata adjustments&lt;/h3&gt;



&lt;p&gt;It&amp;#8217;s starting to look like a blog again! But there&amp;#8217;s a few other items you&amp;#8217;re likely going to need to address that may not be immediately apparent, following are what I had to address.&lt;/p&gt;



&lt;p&gt;&lt;strong&gt;Meta tag tweaks in the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; section&lt;/strong&gt;&lt;/p&gt;



&lt;p&gt;Now that we&amp;#8217;re dealing with separate data for blog posts, we can&amp;#8217;t rely on basic data like &lt;code&gt;title&lt;/code&gt;, for example I had to swap from&lt;/p&gt;



&lt;pre class=&quot;wp-block-code language-html&quot;&gt;&lt;code&gt;&amp;lt;title&amp;gt;{{ title or metadata.title }}&amp;lt;/title&amp;gt;
&amp;lt;meta name=&quot;description&quot; content=&quot;{{ description or metadata.description }}&quot;&amp;gt;&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;to&lt;/p&gt;



&lt;pre class=&quot;wp-block-code language-html&quot;&gt;&lt;code&gt;&amp;lt;title&amp;gt;{{ title or blogpost.title | safe or metadata.title }}&amp;lt;/title&amp;gt;
&amp;lt;meta name=&quot;description&quot; content=&quot;{{ description or blogpost.metaDescription or metadata.description }}&quot;&amp;gt;&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;for the title and description. Note that I had to add that &lt;code&gt; | safe&lt;/code&gt; after the &lt;code&gt;blogpost.title&lt;/code&gt; because of the potential for HTML special characters. WordPress automatically changes quotes (&amp;#8220;&amp;#8230;&amp;#8221;) into quotation marks (“…”) for example.&lt;/p&gt;



&lt;p&gt;&lt;strong&gt;Changes to how the sitemap is generated&lt;/strong&gt;&lt;/p&gt;



&lt;p&gt;You can&amp;#8217;t rely on &lt;code&gt;collections.all&lt;/code&gt; to grab all pages of content anymore, for me I had to add the following to my sitemap in order to ensure the blog posts were still added:&lt;/p&gt;



&lt;pre class=&quot;wp-block-code&quot;&gt;&lt;code&gt;{%- for blog in blogposts %}
  {% set absoluteUrl %}{{ metadata.url }}/blog/{{ blog.slug  }}{% endset %}
  &amp;lt;url&amp;gt;
    &amp;lt;loc&amp;gt;{{ absoluteUrl }}&amp;lt;/loc&amp;gt;
    &amp;lt;lastmod&amp;gt;{{ blog.modifiedDate | htmlDateString }}&amp;lt;/lastmod&amp;gt;
  &amp;lt;/url&amp;gt;
{%- endfor %}&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;&lt;strong&gt;Updates to XML/JSON blog post feeds&lt;/strong&gt;&lt;/p&gt;



&lt;p&gt;Similarly, I had to rebuild the XML- and JSON-based feeds to use the new data. Following is my code update for the XML feed:&lt;/p&gt;



&lt;pre class=&quot;wp-block-code&quot;&gt;&lt;code&gt;{%- for blog in blogposts %}
{% set absoluteUrl %}{{ metadata.url }}/blog/{{ blog.slug  }}{% endset %}
&amp;lt;entry&amp;gt;
	&amp;lt;title&amp;gt;{{ blog.title | safe }}&amp;lt;/title&amp;gt;
	&amp;lt;link href=&quot;{{ absoluteUrl }}&quot;/&amp;gt;
	&amp;lt;published&amp;gt;{{ blog.date }}&amp;lt;/published&amp;gt;
	&amp;lt;updated&amp;gt;{{ blog.modifiedDate }}&amp;lt;/updated&amp;gt;
	&amp;lt;id&amp;gt;{{ absoluteUrl }}&amp;lt;/id&amp;gt;
    {% if blog.heroImageFull %}&amp;lt;image&amp;gt;{{ blog.heroImageFull }}&amp;lt;/image&amp;gt;&amp;gt;{% endif %}
    {% if blog.excerpt %}&amp;lt;summary&amp;gt;{{ blog.excerpt | striptags(true) }}&amp;lt;/summary&amp;gt;{% endif %}
	&amp;lt;content type=&quot;html&quot;&amp;gt;{{ blog.content | htmlToAbsoluteUrls(absoluteUrl) }}&amp;lt;/content&amp;gt;
&amp;lt;/entry&amp;gt;
{%- endfor %}&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;Here it is for the JSON version:&lt;/p&gt;



&lt;pre class=&quot;wp-block-code&quot;&gt;&lt;code&gt;&quot;items&quot;: &amp;#91;
	{%- for blog in blogposts %}
	{% set absoluteUrl %}{{ metadata.url }}/blog/{{ blog.slug  }}{% endset %}
	{
		&quot;id&quot;: &quot;{{ absoluteUrl }}&quot;,
		&quot;url&quot;: &quot;{{ absoluteUrl }}&quot;,
		&quot;title&quot;: &quot;{{ blog.title | safe }}&quot;,
		{% if blog.excerpt %}&quot;summary&quot;: &quot;{{ blog.excerpt | striptags(true) }}&quot;,{% endif %}
		{% if blog.heroImageFull %}&quot;image&quot;: &quot;{{ blog.heroImageFull }}&quot;,{% endif %}
		&quot;content_html&quot;: {% if blog.content %}{{ blog.content | dump | safe }}{% else %}&quot;&quot;{% endif %},
		&quot;date_published&quot;: &quot;{{ blog.date }}&quot;,
		&quot;date_modified&quot;: &quot;{{ blog.modifiedDate }}&quot;
	}
	{%- if not loop.last -%}
	,
	{%- endif -%}
	{%- endfor %}
]&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;&lt;strong&gt;Rewrite rules added to &lt;code&gt;.htaccess&lt;/code&gt; if your blog URLs are changing&lt;/strong&gt;&lt;/p&gt;



&lt;p&gt;This last one is dependent on the changes you&amp;#8217;re making, in my case I moved from &amp;#8220;/posts&amp;#8221; to &amp;#8220;/blog&amp;#8221; as part of this switch so I made sure to add rewrite rules to 301 redirect users to the right location. Here&amp;#8217;s  my &lt;code&gt;/site/static/.htaccess&lt;/code&gt; file you can start from, the first &lt;code&gt;RewriteRule&lt;/code&gt; redirects the main blog page and the second redirects individual blog posts that used to be in subdirectories based on year and month:&lt;/p&gt;



&lt;pre class=&quot;wp-block-code&quot;&gt;&lt;code&gt;Options +FollowSymLinks
RewriteEngine On
RewriteRule ^posts/$ /blog/ &amp;#91;R=301,NC,L]
RewriteRule ^posts/(.*)/(.*)/$ /blog/$2 &amp;#91;R=301,NC,L]&lt;/code&gt;&lt;/pre&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Structure &amp;amp; Style modifications&lt;/h3&gt;



&lt;p&gt;If you made use of any &lt;strong&gt;shortcodes&lt;/strong&gt; specific to your blog posts, it&amp;#8217;s probably a good idea to get rid of those to keep your code clean. For example I had ones for custom blockquotes and asides that are now no longer necessary.&lt;/p&gt;



&lt;p&gt;I also found that WordPress wraps content in slightly different ways than I had with my CSS styles, so I needed to adjust my stylesheets to follow suit. This part is going to be rather dependent on how much of the baked-in styles you&amp;#8217;re going to want to utilize from WordPress. It might be easiest to import everything wholesale but I opted to keep it cleaner by only defining what I knew for sure I&amp;#8217;d need. Here&amp;#8217;s my &lt;code&gt;/site/assets/scss/layout/_wordpress.scss&lt;/code&gt; file in case you wanted to use that as a starting point for your own. I like having it separate in its own file as a reminder that this is CSS specifically for WordPress-provided content.&lt;/p&gt;



&lt;pre class=&quot;wp-block-code language-css&quot;&gt;&lt;code&gt;/* ==========================================================================
   CSS to support content generated by WordPress
   ========================================================================== */
.cp_embed_wrapper {
  margin: $global-spacing calc($global-spacing * -1);

  @media (min-width: $bp_small_desktop_min) {
    margin: $global-spacing calc($global-spacing-double * -1);
  }
}

/* WordPress Blockquote
   ========================================================================== */
.wp-block-quote {
  @extend .quote;

  cite {
    @extend .font-secondary;
    @extend .quote__attribution;
    display: block;
    font-style: normal;
    font-weight: $fw-bold;
    text-align: center;
  }

  p {
    @extend .quote__content;
  }
}

/* WordPress Column layouts
   ========================================================================== */
.wp-block-columns {
  display: flex;
  margin-bottom: 1.75em;
  box-sizing: border-box;
  flex-wrap: wrap !important;
  align-items: initial !important;
  /**
  * All Columns Alignment
  */
}
@media (min-width: 782px) {
  .wp-block-columns {
    flex-wrap: nowrap !important;
  }
}
.wp-block-columns.are-vertically-aligned-top {
  align-items: flex-start;
}
.wp-block-columns.are-vertically-aligned-center {
  align-items: center;
}
.wp-block-columns.are-vertically-aligned-bottom {
  align-items: flex-end;
}
@media (max-width: 781px) {
  .wp-block-columns:not(.is-not-stacked-on-mobile) &amp;gt; .wp-block-column {
    flex-basis: 100% !important;
  }
}
@media (min-width: 782px) {
  .wp-block-columns:not(.is-not-stacked-on-mobile) &amp;gt; .wp-block-column {
    flex-basis: 0;
    flex-grow: 1;
  }
  .wp-block-columns:not(.is-not-stacked-on-mobile)
    &amp;gt; .wp-block-column&amp;#91;style*=&quot;flex-basis&quot;] {
    flex-grow: 0;
  }
}
.wp-block-columns.is-not-stacked-on-mobile {
  flex-wrap: nowrap !important;
}
.wp-block-columns.is-not-stacked-on-mobile &amp;gt; .wp-block-column {
  flex-basis: 0;
  flex-grow: 1;
}
.wp-block-columns.is-not-stacked-on-mobile
  &amp;gt; .wp-block-column&amp;#91;style*=&quot;flex-basis&quot;] {
  flex-grow: 0;
}

:where(.wp-block-columns.has-background) {
  padding: 1.25em 2.375em;
}

.wp-block-column {
  flex-grow: 1;
  min-width: 0;
  word-break: break-word;
  overflow-wrap: break-word;
  /**
  * Individual Column Alignment
  */
}
.wp-block-column.is-vertically-aligned-top {
  align-self: flex-start;
}
.wp-block-column.is-vertically-aligned-center {
  align-self: center;
}
.wp-block-column.is-vertically-aligned-bottom {
  align-self: flex-end;
}
.wp-block-column.is-vertically-aligned-top,
.wp-block-column.is-vertically-aligned-center,
.wp-block-column.is-vertically-aligned-bottom {
  width: 100%;
}&lt;/code&gt;&lt;/pre&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Triggering a new build on publish&lt;/h2&gt;



&lt;p&gt;Last but certainly not least, I needed a way to trigger a fresh build as I published new content. I wanted to make this as easy as possible on myself so I put some time into figuring out how to automate these builds rather than having to do it manually every time. There are two parts to this, a build pipeline that gets triggered and a way to automatically trigger it.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;CI/CD Build Pipelines&lt;/h3&gt;



&lt;p&gt;This is only going to work if you have some sort of build pipeline that you can tap into, if you&amp;#8217;re manually triggering new static site builds within your Eleventy setup then you&amp;#8217;ll have to continue to do that after publishing new content to your WordPress blog. &lt;/p&gt;



&lt;p&gt;Otherwise, there are options for &lt;a href=&quot;https://docs.github.com/en/actions/learn-github-actions/understanding-github-actions&quot;&gt;Github Actions&lt;/a&gt;, &lt;a href=&quot;https://learn.microsoft.com/en-us/azure/devops/pipelines/build/triggers?view=azure-devops&quot;&gt;Azure Pipelines&lt;/a&gt;, and &lt;a href=&quot;https://bitbucket.org/product/features/pipelines&quot;&gt;Bitbucket Pipelines&lt;/a&gt; but your unique setup is going to dictate how this works for you. Be sure, as part of this pipeline, that you&amp;#8217;re removing the cache so it&amp;#8217;s pulling fresh blog content each time. Here&amp;#8217;s the NPM part of my pipeline:&lt;/p&gt;



&lt;pre class=&quot;wp-block-code&quot;&gt;&lt;code&gt;rm -rf .cache/
npm ci
npm run build&lt;/code&gt;&lt;/pre&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Triggering a build on publish&lt;/h3&gt;



&lt;p&gt;After a bit of research I opted for the &lt;a href=&quot;https://wordpress.org/plugins/wp-webhooks/&quot;&gt;WP Webhooks WordPress plugin&lt;/a&gt; for this, it does a whole lot more than I need right now and is fairly straightforward. Once you&amp;#8217;ve got it installed go to the settings and click on the &amp;#8220;Send Data&amp;#8221; menu item. From there you&amp;#8217;ll get a list of available webhook triggers. The two I ended up sticking with were &amp;#8220;Post updated&amp;#8221; and &amp;#8220;Post deleted&amp;#8221;, I didn&amp;#8217;t keep &amp;#8220;Post created&amp;#8221; because &amp;#8211; as far as I could tell &amp;#8211; &amp;#8220;Post updated&amp;#8221; triggered for that too so it wasn&amp;#8217;t necessary.&lt;/p&gt;



&lt;p&gt;For both of those triggers, I clicked on the &amp;#8220;Add Webhook URL&amp;#8221; button and added my unique CI/CD pipeline URL that triggers a new build.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Wrapping up&lt;/h2&gt;



&lt;p&gt;This post ended up being a lot longer than I anticipated and I hope it doesn&amp;#8217;t make this process seem especially daunting, I went into a lot more detail than usual to try to help you avoid some of the mistakes that I made the first time around. I got so excited to launch this that I initially forgot about proper redirects, updating the sitemap, and all the XML/RSS feeds &amp;#8211; essentially cutting the site off from feed readers, backlinks, and search engines. Don&amp;#8217;t be like me!&lt;/p&gt;



&lt;p&gt;If you give this a shot and run into any trouble (or just want to say thanks!) please do feel free to get in touch or start a conversation at one of the links below. Thanks!&lt;/p&gt;

    </content>
	</entry>
  
	<entry>
		<title>SSH &#8220;Server Key Changed&#8221; Error and How to Fix It</title>
		<link href="https://stevenwoodson.com/blog/ssh-server-key-changed-error-and-how-to-fix-it"/>
		<published>2022-12-14T13:14:54.000Z</published>
		<updated>2023-08-10T16:32:08.000Z</updated>
		<id>https://stevenwoodson.com/blog/ssh-server-key-changed-error-and-how-to-fix-it</id>
    <summary>Brief description of the common &amp;#8220;server key has changed&amp;#8221; error, and details on how to fix it on a Mac local server environment.</summary>
		<content type="html">&lt;img src=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/12/Navicat-Known-Hosts-Error.png&quot; alt=&quot;&quot;&gt;
&lt;p&gt;I&amp;#8217;ve come across the following SSH server key error a handful of times while working within my local environments and every time I&amp;#8217;ve had to look up what it is and how to get around it. So naturally I&amp;#8217;ve decided to put the answer here so I have it easier the next time this happens.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;The Error&lt;/h2&gt;



&lt;p&gt;Here&amp;#8217;s the error I keep getting when trying to open up a local MySQL server using Navicat:&lt;/p&gt;



&lt;blockquote class=&quot;wp-block-quote is-layout-flow wp-block-quote-is-layout-flow&quot;&gt;
&lt;p&gt;The server key has changed. Either you are under attack or the administrator changed the key.&lt;/p&gt;
&lt;/blockquote&gt;



&lt;p&gt;Similar issues can happen in other programs that utilize SSH like PuTTY or FileZilla (via SFTP) but I&amp;#8217;ve only experienced it in Navicat on a Mac recently so that&amp;#8217;s what I&amp;#8217;m detailing here. If you have more details I could add for other environments or programs, definitely get in touch using the links at the bottom of this post!&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Why It&amp;#8217;s Happening&lt;/h2&gt;



&lt;p&gt;When establishing an SSH connection, the server sends its public key to the client to authenticate that it is who it claims to be. When you accept the connection, this public key gets stored in your local &lt;code&gt;~/.ssh/known_hosts&lt;/code&gt;&amp;nbsp;file (there&amp;#8217;s also a system-wide file at&amp;nbsp;&lt;code&gt;/etc/ssh/known_hosts&lt;/code&gt;). The next time you attempt to connect to that same server, you&amp;#8217;ll get a warning if the public key has changed from the one that was stored. This can happen for several valid reasons like server maintenance or IP/hostname changes, you get the warning because it could also be a potential security breach.&lt;/p&gt;



&lt;p&gt;For my local server situation, I realized I have more than one server that refers to MySQL at localhost (127.0.0.1). Each server has it&amp;#8217;s own unique public key so each time I connect to one and then attempt to connect to the other I get this error because the keys don&amp;#8217;t match.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;How to Fix It&lt;/h2&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;A reminder to remain vigilant about security, especially if you&amp;#8217;re experiencing this with a remote SSH connection. In my case since it&amp;#8217;s a local development environment so I know I&amp;#8217;m in the clear.&lt;/li&gt;



&lt;li&gt;Open up &lt;code&gt;~/.ssh/known_hosts&lt;/code&gt;, each line in that file is an individual known host record. Find the one associated with the server you&amp;#8217;re having issues with. If you&amp;#8217;re having trouble finding the proper line to remove, the error you get will likely reference the public key that you can then search for in this file.&lt;/li&gt;



&lt;li&gt;Remove the public key record that is causing the issue. For me, it&amp;#8217;s the one that starts with 127.0.0.1.&lt;/li&gt;



&lt;li&gt;A more robust solution to this is to change my local servers to point to unique IP addresses so this doesn&amp;#8217;t keep happening, but that&amp;#8217;s a problem for future Steve to deal with.&lt;/li&gt;
&lt;/ul&gt;

    </content>
	</entry>
  
	<entry>
		<title>Making Intervention Image Ignore SVGs</title>
		<link href="https://stevenwoodson.com/blog/making-intervention-image-ignore-svgs"/>
		<published>2022-11-30T02:38:54.000Z</published>
		<updated>2023-08-10T16:33:18.000Z</updated>
		<id>https://stevenwoodson.com/blog/making-intervention-image-ignore-svgs</id>
    <summary>Quick writeup of how I handled a pervasive Intervention Image error &amp;#8220;Intervention&#92;Image&#92;Exception&#92;NotReadableException&amp;#8221; in a Laravel app.</summary>
		<content type="html">&lt;img src=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/11/making-intervention-image-ignore-svgs.png&quot; alt=&quot;&quot;&gt;
&lt;p&gt;I recently started seeing a recurring exception pop up on one of my sites related to resizing and caching of images, after a bit of digging I realized what the underlying issue ended up being and figured I should document how I went about fixing it.&lt;/p&gt;



&lt;p&gt;This particular site is built with Laravel using &lt;a href=&quot;https://image.intervention.io/v2&quot;&gt;Intervention Image&lt;/a&gt;, &lt;a href=&quot;https://image.intervention.io/v2/usage/cache&quot;&gt;Intervention Image Cache&lt;/a&gt;, and utilizing Intervention&amp;#8217;s &lt;a href=&quot;https://image.intervention.io/v2/usage/url-manipulation&quot;&gt;URL based image manipulation&lt;/a&gt;. Your mileage may vary if you&amp;#8217;re using some other PHP framework but you should still be able to copy the solution _somewhere_ if not in the exact file I&amp;#8217;m referencing. If you do I&amp;#8217;d love to hear about it!&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;The Problem&lt;/h2&gt;



&lt;p&gt;The problem ended up being that some of the images I was trying to resize using Intervention were SVGs, and the default image manipulation Intervention uses is the &lt;a href=&quot;https://libgd.github.io/&quot;&gt;GD library&lt;/a&gt; which only supports JPG, PNG, GIF, BMP or WebP files. When it encounters an SVG (or any other unsupported format) it fails.&lt;/p&gt;



&lt;p&gt;Here&amp;#8217;s the relevant part of the stack trace (emphasis added to the first line):&lt;/p&gt;



&lt;pre class=&quot;wp-block-code&quot;&gt;&lt;code&gt;&lt;strong&gt;Intervention&#92;Image&#92;Exception&#92;NotReadableException: Unsupported image type image/svg+xml. GD driver is only able to decode JPG, PNG, GIF, BMP or WebP files.
&lt;/strong&gt;#48 /vendor/intervention/image/src/Intervention/Image/Gd/Decoder.php(75): Intervention&#92;Image&#92;Gd&#92;Decoder::initFromPath
#47 /vendor/intervention/image/src/Intervention/Image/AbstractDecoder.php(344): Intervention&#92;Image&#92;AbstractDecoder::init
#46 /vendor/intervention/image/src/Intervention/Image/AbstractDriver.php(66): Intervention&#92;Image&#92;AbstractDriver::init
#45 /vendor/intervention/image/src/Intervention/Image/ImageManager.php(54): Intervention&#92;Image&#92;ImageManager::make
#44 &amp;#91;internal](0): call_user_func_array
#43 /vendor/intervention/imagecache/src/Intervention/Image/ImageCache.php(257): Intervention&#92;Image&#92;ImageCache::processCall
#42 /vendor/intervention/imagecache/src/Intervention/Image/ImageCache.php(273): Intervention&#92;Image&#92;ImageCache::process
#41 /vendor/intervention/imagecache/src/Intervention/Image/ImageCache.php(315): Intervention&#92;Image&#92;ImageCache::get
#40 /vendor/intervention/image/src/Intervention/Image/ImageManager.php(92): Intervention&#92;Image&#92;ImageManager::cache
#39 /vendor/intervention/imagecache/src/Intervention/Image/ImageCacheController.php(58): Intervention&#92;Image&#92;ImageCacheController::getImage
#38 /vendor/intervention/imagecache/src/Intervention/Image/ImageCacheController.php(31): Intervention&#92;Image&#92;ImageCacheController::getResponse&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;I can&amp;#8217;t check for this &lt;code&gt;Intervention&#92;Image&#92;Exception&#92;NotReadableException&lt;/code&gt; exception using a try/catch because I&amp;#8217;m using URL-based image manipulation. For example, attempting to load &lt;code&gt;mysite.com/imagecache/small/logo.svg&lt;/code&gt; was what was throwing this &lt;code&gt;NotReadableException&lt;/code&gt; error. There&amp;#8217;s no code under my control triggering these image manipulations, it&amp;#8217;s all vendor code through Intervention that handles these URLs.&lt;/p&gt;



&lt;p&gt;The biggest reason I implemented resizing and caching was for performance reasons. So in my case, its okay for SVG images to not be programmatically resized because they&amp;#8217;re already vector and (generally) are already way smaller in filesize.&lt;/p&gt;



&lt;p&gt;&lt;strong&gt;So, how do I get Intervention Image to ignore SVGs?&lt;/strong&gt;&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;The Solution&lt;/h2&gt;



&lt;p&gt;I&amp;#8217;m sure many of you are gonna scroll to this section pretty quickly (I know I would&amp;#8217;ve too if it was documented while I was searching) so I&amp;#8217;m going to cut to the chase.&lt;/p&gt;



&lt;p&gt;In Laravel there&amp;#8217;s a &lt;a href=&quot;https://laravel.com/docs/9.x/errors&quot;&gt;global exception handler&lt;/a&gt; defined at &lt;code&gt;app/Exceptions/Handler.php&lt;/code&gt;, it&amp;#8217;s where all uncaught exceptions flow through. You may have had to add some code to this handler for app monitoring service integrations, I&amp;#8217;m glad I did because otherwise I wouldn&amp;#8217;t have even known about this error in the first place!&lt;/p&gt;



&lt;p&gt;Here&amp;#8217;s my addition to the &lt;code&gt;render&lt;/code&gt; method in that &lt;code&gt;app/Exceptions/Handler.php&lt;/code&gt; file:&lt;/p&gt;



&lt;pre class=&quot;wp-block-code language-php&quot;&gt;&lt;code&gt;    /**
     * Render an exception into an HTTP response.
     *
     * @param  &#92;Illuminate&#92;Http&#92;Request  $request
     * @param  &#92;Throwable   $exception
     * @return &#92;Illuminate&#92;Http&#92;Response
     */
    public function render($request, Throwable $exception)
    {
        /**
         * If the error class is `Intervention&#92;Image&#92;Exception&#92;NotReadableException`, 
         * redirect the image URL to the original instead to avoid 500 errors on
         * the frontend. In particular, it has trouble with SVG images as the GD 
         * Library doesn&#39;t support anything other than JPG, PNG, GIF, BMP or WebP files.
         * 
         * This RegEx handles all Intervention Image filters defined (&#39;/small/&#39;,
         * &#39;/medium/&#39;, &#39;/large/&#39;, for example) by isolating the URL path after
         * the configured Intervention URL Manipulation route (`imagecache` in 
         * this case) and replaces it with `/original/` which is a built in 
         * Intervention route that sends am HTTP response with the original image file.
         * 
         * @see https://image.intervention.io/v2/usage/url-manipulation
         */
        if ( get_class($exception) == &quot;Intervention&#92;Image&#92;Exception&#92;NotReadableException&quot; ) {
            header(&#39;Location: &#39;. preg_replace(&#39;/(imagecache&#92;/&amp;#91;^&#92;/]*&#92;/)+/i&#39;, &#39;imagecache/original/&#39;, $request-&amp;gt;getUri()) );
            exit();
        }

        return parent::render($request, $exception);
    }&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;The whole description of what this is doing is in the comment block, be sure to change &lt;code&gt;imagecache&lt;/code&gt; in there to whatever you defined as your &lt;strong&gt;route&lt;/strong&gt; in &lt;code&gt;config/imagecache.php&lt;/code&gt;!&lt;/p&gt;

    </content>
	</entry>
  
	<entry>
		<title>Composable Architecture Powered by WordPress</title>
		<link href="https://stevenwoodson.com/blog/composable-architecture-powered-by-wordpress"/>
		<published>2022-11-01T13:32:49.000Z</published>
		<updated>2023-08-14T13:41:41.000Z</updated>
		<id>https://stevenwoodson.com/blog/composable-architecture-powered-by-wordpress</id>
    <summary>One developers journey toward content authoring nirvana. Thought process, decisions, and source code for a WordPress composable architecture.</summary>
		<content type="html">&lt;img src=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/11/wordpress-as-a-content-hub.png&quot; alt=&quot;&quot;&gt;
&lt;p&gt;It&amp;#8217;s inevitable that &amp;#8211; as a developer &amp;#8211; I continue to change my mind and feel the urge to tinker again and again. Like a pendulum swinging back and forth I&amp;#8217;ve noticed that my tendencies for choosing technologies also oscillates between complexity and simplicity. Always on the hunt for that happy medium where the developer, authoring, and user experiences are all as good as they can be. Inevitably, however, choices made to improve one of these three can become a hindrance on the others. Here&amp;#8217;s my journey towards the next evolution of content generation for all my sites, leveraging composable architecture powered by WordPress.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;A Focus on Simplicity&lt;/h2&gt;



&lt;p&gt;A couple years ago I switched this personal site of mine from an over-engineered monstrosity of custom code to a minimalist &lt;a href=&quot;https://www.11ty.dev/&quot;&gt;Eleventy&lt;/a&gt; build. It was a liberating experience being able to scrap so much and still have a functional site that was more performant and simple to maintain. After a couple years tinkering with it I came to this conclusion about the three experiences noted above:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;strong&gt;Developer Experience&lt;/strong&gt; &amp;#8211; greatly improved due to less code, no databases, and a more straightforward organization&lt;/li&gt;



&lt;li&gt;&lt;strong style=&quot;color: initial; font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe UI&amp;quot;, Roboto, Oxygen-Sans, Ubuntu, Cantarell, &amp;quot;Helvetica Neue&amp;quot;, sans-serif;&quot;&gt;User Experience&lt;/strong&gt;&lt;span style=&quot;color: initial; font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe UI&amp;quot;, Roboto, Oxygen-Sans, Ubuntu, Cantarell, &amp;quot;Helvetica Neue&amp;quot;, sans-serif;&quot;&gt; &amp;#8211; greatly improved due to a redeveloped frontend for better accessibility, and 11ty being a static site generator it&amp;#8217;s way more performant&lt;/span&gt;&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;Authoring Experience&lt;/strong&gt; &amp;#8211; more cumbersome with markdown files powering page content (including blog posts), no longer having an administration section to manage content from anywhere&lt;/li&gt;
&lt;/ul&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Simplicity at What Cost?&lt;/h2&gt;



&lt;p&gt;As I alluded to in the Authoring Experience note above, the only problem I came across was that I had to be at my computer with an IDE open in order to draft new blog posts. &amp;#8220;No problem&amp;#8221; I thought, &amp;#8220;I&amp;#8217;m always at my computer anyway&amp;#8221; and off I went.&lt;/p&gt;



&lt;p&gt;Well, since that switch I&amp;#8217;ve posted all of three new blog posts &amp;#8211; just about &lt;em&gt;one per year&lt;/em&gt;. I finally admitted to myself that it was due to the barrier of having to start a new markdown file, by the time I sat at my computer the post idea was either gone completely or I lost some of the more compelling aspects of it.&lt;/p&gt;



&lt;p&gt;I started dreaming of a happy middle ground, one where I could still have the performance and simplicity of a static site but with the ease of an admin interface for adding content.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Growing Ambitions&lt;/h2&gt;



&lt;p&gt;Once I had it in my head that I wanted to make some changes for this site, of course I couldn&amp;#8217;t help also considering the challenges I was having with content over at my &lt;a href=&quot;https://beinclusive.app/admin/users&quot;&gt;accessibility auditing app Be Inclusive&lt;/a&gt;. I had switched to using &lt;a href=&quot;https://twill.io/&quot;&gt;Twill&lt;/a&gt; over there just over a year ago for similar reasons as I had for wanting a change on this site now. I need an easier authoring experience if I have any hope at keeping up with content creation, but from the start it felt like a bolted on solution that didn&amp;#8217;t quite fit. &lt;/p&gt;



&lt;p&gt;That got me wondering about content authoring for multiple sites, is there a way I could make something work long-term that&amp;#8217;d be versatile enough for my shifting priorities across projects?&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Enter Composable Architecture&lt;/h2&gt;



&lt;p&gt;That led to me turning to the concept of composable architecture, while my needs are relatively narrow focused (mostly blog and article-based content) it&amp;#8217;s still the best term I can think of. It can be referred to as microservices architecture, modular architecture, and headless architecture. Us technologists aren&amp;#8217;t that great at naming things&amp;#8230;&lt;/p&gt;



&lt;p&gt;Basically, I&amp;#8217;m looking to offload the authoring of content to another service entirely and pulling that data as needed into any applications I want. This type of content architecture is very popular nowadays and for good reason, it&amp;#8217;s fairly compelling to be able to split the code apart from the content and to have the latter available for multiple kinds of applications. Larger companies have used it effectively to share content across websites, native applications, social, and marketing (often paired with terms like omnichannel or digital transformation).&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Choosing WordPress&lt;/h2&gt;



&lt;p&gt;After some fiddling with SaaS composable architecture content providers and trying a few options out, I settled on ol&amp;#8217; reliable WordPress for my content authoring needs. Here&amp;#8217;s a few reasons why:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;As a web developer, I&amp;#8217;m fortunate enough to be able to do a bit of work upfront in order to self host and not incur fees for an out-of-the-box content provider. Many were great, I just couldn&amp;#8217;t justify the cost.&lt;/li&gt;



&lt;li&gt;WordPress comes pre-packaged with the ability to set up a multisite installation, so I can have content for this personal site, the Be Inclusive app, and any other sites I need to add into the future.&lt;/li&gt;



&lt;li&gt;WordPress comes with a RESTful API already baked in too, so I can run API calls to gather the content I need. If my needs get to be more custom, I can also create my own RESTful API endpoints for full control.&lt;/li&gt;



&lt;li&gt;With custom post types, I can also compartmentalize different types of content with a simple plugin. Leaving me with a versatile and extensible framework to grow into.&lt;/li&gt;
&lt;/ul&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Getting Started&lt;/h2&gt;



&lt;p&gt;Setting this up was actually much simpler than I was anticipating, here&amp;#8217;s a quick rundown:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;I wanted to position the content somewhere reasonable for all my content needs, for me that ended up being as a separately hosted subdomain.&lt;/li&gt;



&lt;li&gt;I installed WordPress at the subdomain, and then configured multisite using the instructions in this &lt;a href=&quot;https://wordpress.org/support/article/create-a-network/&quot;&gt;Create a Network&lt;/a&gt; WP support page.&lt;/li&gt;



&lt;li&gt;I started a new &amp;#8220;site&amp;#8221; for both this personal site, and for the Be Inclusive app, from the &lt;a href=&quot;https://wordpress.org/support/article/network-admin-sites-screen/#add-site&quot;&gt;Network Admin Sites Screen&lt;/a&gt;.&lt;/li&gt;



&lt;li&gt;I disabled all themes, since this is going to be used solely as a content hub where sites will be pulling content from the API. As far as I know, you can&amp;#8217;t have _no theme_ so I created a simplified one used mainly for redirecting traffic. See details and source code below under the &amp;#8220;Redirect Theme&amp;#8221; headline.&lt;/li&gt;



&lt;li&gt;I added a few plugins that defined custom post types I need for my site content (besides standard blog posts). See details and source code below under the &amp;#8220;Custom Post Types&amp;#8221; headline.&lt;/li&gt;
&lt;/ul&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Redirect Theme&lt;/h3&gt;



&lt;p&gt;I didn&amp;#8217;t want folks using this subdomain to see the content I was creating, so the theme I created was called &amp;#8220;Redirect&amp;#8221; and has three files &amp;#8211; &lt;code&gt;functions.php&lt;/code&gt;, &lt;code&gt;index.php&lt;/code&gt;, and &lt;code&gt;style.css&lt;/code&gt;. All three files get added to &lt;code&gt;wp-content/themes/redirect&lt;/code&gt;. You then set that as the active theme and you&amp;#8217;re all set!&lt;/p&gt;



&lt;h4 class=&quot;wp-block-heading&quot;&gt;functions.php&lt;/h4&gt;



&lt;p&gt;I needed to be able to support post thumbnails, that&amp;#8217;s really all this one is used for currently.&lt;/p&gt;



&lt;pre class=&quot;wp-block-code language-php&quot;&gt;&lt;code&gt;&amp;lt;?php

if ( ! function_exists( &#39;redirect_theme_support&#39; ) ) :

	/**
	 * Sets up theme defaults and registers support for various WordPress features.
	 *
	 * @since Twenty Twenty-Two 1.0
	 *
	 * @return void
	 */
	function redirect_theme_support() {
		// Add support for block styles.
		add_theme_support( &#39;post-thumbnails&#39; );
	}

endif;

add_action( &#39;after_setup_theme&#39;, &#39;redirect_theme_support&#39; );
&lt;/code&gt;&lt;/pre&gt;



&lt;h4 class=&quot;wp-block-heading&quot;&gt;index.php&lt;/h4&gt;



&lt;p&gt;This is where we perform the redirect sitewide, the only downside is that you can&amp;#8217;t use the &amp;#8220;Preview&amp;#8221; link when authoring.&lt;/p&gt;



&lt;pre class=&quot;wp-block-code language-php&quot;&gt;&lt;code&gt;&amp;lt;?php

// We want to run this site as a headless implementation, redirect to the site instead
header(&quot;Location: https://yourdomain.com/&quot;);&lt;/code&gt;&lt;/pre&gt;



&lt;h4 class=&quot;wp-block-heading&quot;&gt;style.css&lt;/h4&gt;



&lt;p&gt;This is only here to give the meta information we need to see in the WordPress admin&lt;/p&gt;



&lt;pre class=&quot;wp-block-code language-css&quot;&gt;&lt;code&gt;/*
   Theme Name: Redirect
   Description: Redirects the front end to another domain
*/&lt;/code&gt;&lt;/pre&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Custom Post Types&lt;/h3&gt;



&lt;p&gt;I needed a couple custom post types defined for FAQ and Help Article content separate from the blog posts. Here&amp;#8217;s an example of the plugin for FAQs, it&amp;#8217;s a single file that gets added to &lt;code&gt;wp-content/plugins&lt;/code&gt;&lt;/p&gt;



&lt;pre class=&quot;wp-block-code language-php&quot;&gt;&lt;code&gt;&amp;lt;?php

/**
 * FAQs custom post type plugin
 *
 * @wordpress-plugin
 * Plugin Name:       Custom Post Type - FAQ
 * Description:       Defines a custom FAQ post type for use in the admin editor
 * Version:           1.0.0
 * Author:            Steve Woodson
 * Author URI:        https://stevenwoodson.com/
 */

// If this file is called directly, abort.
if ( ! defined( &#39;WPINC&#39; ) ) {
	die;
}


/**
 * Register the FAQ Custom Post Type.
 *
 * @see https://developer.wordpress.org/reference/functions/register_post_type
 */
function faq_post_type() {

	$labels = array(
		&#39;name&#39;           =&amp;gt; _x( &#39;FAQs&#39;, &#39;Post Type General Name&#39; ),
		&#39;singular_name&#39;  =&amp;gt; _x( &#39;FAQ&#39;, &#39;Post Type Singular Name&#39; ),
		&#39;menu_name&#39;      =&amp;gt; __( &#39;FAQs&#39; ),
		&#39;name_admin_bar&#39; =&amp;gt; __( &#39;FAQs&#39; ),
		&#39;archives&#39;       =&amp;gt; __( &#39;FAQ Archives&#39; ),
		&#39;attributes&#39;     =&amp;gt; __( &#39;FAQ Attributes&#39; ),
		&#39;all_items&#39;      =&amp;gt; __( &#39;All FAQs&#39; ),
		&#39;add_new&#39;        =&amp;gt; __( &#39;Add New FAQ&#39; ),
	);
	$args   = array(
		&#39;label&#39;               =&amp;gt; __( &#39;FAQs&#39; ),
		&#39;description&#39;         =&amp;gt; __( &#39;A frequently asked question&#39; ),
		&#39;labels&#39;              =&amp;gt; $labels,
		&#39;supports&#39;            =&amp;gt; array( &#39;title&#39;, &#39;editor&#39; ),
		&#39;hierarchical&#39;        =&amp;gt; false,
		&#39;public&#39;              =&amp;gt; true,
		&#39;publicly_queryable&#39;  =&amp;gt; true,
		&#39;show_ui&#39;             =&amp;gt; true,
		&#39;show_in_menu&#39;        =&amp;gt; true,
		&#39;menu_position&#39;       =&amp;gt; 5,
		&#39;exclude_from_search&#39; =&amp;gt; false,
		&#39;searchable&#39;          =&amp;gt; true,
		&#39;publicly_queryable&#39;  =&amp;gt; true,
		&#39;rewrite&#39;             =&amp;gt; false,
		&#39;capability_type&#39;     =&amp;gt; &#39;post&#39;,
		&#39;show_in_rest&#39;        =&amp;gt; true,
		&#39;rest_base&#39;           =&amp;gt; &#39;faqs&#39;,
		&#39;has_archive&#39;         =&amp;gt; true,
		&#39;taxonomies&#39;          =&amp;gt; &amp;#91;&#39;category&#39;, &#39;post_tag&#39;]
	);
	register_post_type( &#39;faq&#39;, $args );
}
add_action( &#39;init&#39;, &#39;faq_post_type&#39; );
&lt;/code&gt;&lt;/pre&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Next Steps&lt;/h2&gt;



&lt;p&gt;So I&amp;#8217;ve settled on a composable architecture setup, chose WordPress to facilitate it, and got it all set up as a content hub for all my growing content authoring needs. The next steps are:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;Migrating any existing content into it. For me that was a mixture of copy/paste of content, uploading images to WordPress and replacing the pasted version with the uploaded version, and then making sure I reset the publish date to be the same as the original.&lt;/li&gt;



&lt;li&gt;Pulling WordPress Posts into Laravel for &lt;a href=&quot;https://beinclusive.app/&quot;&gt;Be Inclusive&lt;/a&gt;&lt;/li&gt;



&lt;li&gt;Pulling WordPress Posts into Eleventy for this site&lt;/li&gt;



&lt;li&gt;I&amp;#8217;m also considering setting this up for &lt;a href=&quot;https://a11y-solutions.stevenwoodson.com/&quot;&gt;Accessibility Solutions&lt;/a&gt; too, which is built using Nuxt&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;This post is getting longer than I planned! I&amp;#8217;m going to split out how I approached getting content from WordPress into these sites as separate posts. Each site being built with different languages and frameworks I&amp;#8217;m sure they&amp;#8217;re all going to have their own surprises.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Following Up&lt;/h2&gt;



&lt;p&gt;Here are the deeper dives of using WordPress sourced data in other projects, more to come!&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://stevenwoodson.com/blog/pulling-wordpress-content-into-eleventy/&quot;&gt;Pulling WordPress Content into Eleventy&lt;/a&gt;&lt;/li&gt;



&lt;li&gt;&lt;a href=&quot;https://stevenwoodson.com/blog/adding-a-table-of-contents-to-dynamic-content-in-11ty/&quot;&gt;Adding a Table of Contents to dynamic content in 11ty&lt;/a&gt;&lt;/li&gt;



&lt;li&gt;&lt;a href=&quot;https://stevenwoodson.com/blog/pulling-wordpress-post-categories-tags-into-eleventy/&quot;&gt;Pulling WordPress Post Categories &amp;amp; Tags Into Eleventy&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

    </content>
	</entry>
  
	<entry>
		<title>IAAP CPACC Exam Preparation and Reflection</title>
		<link href="https://stevenwoodson.com/blog/cpacc-exam-reflection"/>
		<published>2022-03-20T11:57:00.000Z</published>
		<updated>2023-08-10T16:36:27.000Z</updated>
		<id>https://stevenwoodson.com/blog/cpacc-exam-reflection</id>
    <summary>Some notes on how I prepared for the CPACC Certification exam and reflecting on the exam itself.</summary>
		<content type="html">&lt;img src=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/10/hero-2.png&quot; alt=&quot;&quot;&gt;
&lt;p&gt;Happy to say I recently checked off a pretty large goal off my list by achieving my CPACC certification! I sat for the exam in early February 2022 and received notification of my passing it early in March.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot; id=&quot;preparation&quot;&gt;Preparation&lt;/h2&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;I participated in a CPACC study cohort at work, four of us worked through the&amp;nbsp;&lt;a href=&quot;https://dequeuniversity.com/curriculum/courses/iaap-cpacc&quot;&gt;IAAP CPACC Certification Preparation Course&lt;/a&gt;&amp;nbsp;course together. I took fairly detailed bulleted notes that I referred back to often. The course is well organized and I got a lot of great detail out of it.&lt;/li&gt;



&lt;li&gt;Prior to the cohort I had purchased a year-long license to that course twice and had trouble making the time to get through it on my own. Having others to talk to and hold me accountable was just the kick of motivation I needed, having study buddies is highly recommended!&lt;/li&gt;



&lt;li&gt;I read through the whole&amp;nbsp;&lt;a href=&quot;https://www.accessibilityassociation.org/resource/IAAP_CPACC_BOK_March2020&quot; target=&quot;_blank&quot; rel=&quot;noreferrer noopener nofollow&quot;&gt;Body of Knowledge (BoK)&lt;/a&gt;, &lt;a href=&quot;https://www.accessibilityassociation.org/s/cpacc-certification-content-outline&quot; rel=&quot;nofollow&quot;&gt;outline&lt;/a&gt;, and &lt;a href=&quot;https://www.accessibilityassociation.org/s/cpacc-sample-exam-questions&quot; rel=&quot;nofollow&quot;&gt;sample questions&lt;/a&gt; provided by Accessibility Association as well, outlined in the &lt;a href=&quot;https://www.accessibilityassociation.org/s/certified-professional&quot; rel=&quot;nofollow&quot;&gt;Prepare for the CPACC Exam section of this page&lt;/a&gt;.&lt;/li&gt;



&lt;li&gt;In the weeks leading up to my exam, I created approximately 400 flash cards to study from. I ended up using&amp;nbsp;&lt;a href=&quot;https://apps.ankiweb.net/&quot;&gt;Anki&lt;/a&gt;&amp;nbsp;because I wanted an easy way to sync flash cards between devices so I could also study from my phone on the go. That worked out great because every opportunity I had to swipe through a few cards did add up &amp;#8211; waiting for a haircut, in line at the grocery store, etc. I think the act of making the flash cards were as helpful as studying them, but if you&amp;#8217;re interested in perusing mine &lt;a href=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2023/01/CPACC-Exam-Prep-Anki-Packages.zip&quot;&gt;here you go&lt;/a&gt;.&lt;/li&gt;



&lt;li&gt;I had read a few horror stories about poor remote proctored exam experiences so I prepared a bedroom with minimal decoration far away from any noise or distraction. I had my personal computer set up with the Kryterion Sentinel program well in advance too as that is required for the exam. I didn’t bother even trying with my work laptop because of the warnings about avoiding firewalls and admin access requirement.&lt;/li&gt;



&lt;li&gt;The exam is 100 multiple choice questions and you get 2 hours to complete, you’re able to have a notepad and pen and nothing else nearby when taking it remotely. That means no phones, devices, smart watches, etc.&lt;/li&gt;
&lt;/ul&gt;



&lt;h2 class=&quot;wp-block-heading&quot; id=&quot;exam-reflection&quot;&gt;Exam Reflection&lt;/h2&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;I was stopped after my second question to take off and show my glasses and confirm I&amp;#8217;m not using a notepad (the only other allowed thing in a remote proctored exam).&lt;/li&gt;



&lt;li&gt;I took 1 hour 10 minutes, had 15 questions flagged for review after the first pass and probably agonized over these for longer than I should have. I was initially nervous about timing but now I would say the 2 hours they give was more than sufficient. I do appreciate that they offer more time to folks who feel they may need it as well.&lt;/li&gt;



&lt;li&gt;It goes way deeper into applying guidelines to real-world situations and often provides at least two reasonable options and forces you to choose the BEST. Makes it very difficult to feel confident in my responses but I do think it does a good job pusing you to apply what you&amp;#8217;ve learned rather than relying on memorization.&lt;/li&gt;



&lt;li&gt;There were questions about the UN CRPD that I had no idea how to answer, and I felt fairly confident about that and the Marrakesh Treaty in particular. In hindsight I think maybe some extra due diligence reading through the linked materials available through the Deque course as well as the BoK would have helped, but it’s already quite a lot of information.&lt;/li&gt;



&lt;li&gt;There was a very specific question about the Japanese Industrial Standard (JIS) X 8341 that I was unprepared for, definitely need to read even further into the international and domain specific laws if you want to get all of these right.&lt;/li&gt;



&lt;li&gt;There was some wording that used &amp;#8220;social&amp;#8221; and &amp;#8220;systemic&amp;#8221; that led me to different answers than I would have put otherwise. Be mindful of the subtle context they&amp;#8217;re adding because it could change your answer.&lt;/li&gt;



&lt;li&gt;I studied a lot about the different types of color blindness, didn&amp;#8217;t personally get any questions that went deep enough to know all the particular types and names but I&amp;#8217;m still glad to have learned that extra information.&lt;/li&gt;



&lt;li&gt;Before sitting for the exam I was under the impression that statistics were very important, only got a question about most pervasive type of disability and general percentage of people with disabilities stats.&lt;/li&gt;
&lt;/ul&gt;



&lt;h2 class=&quot;wp-block-heading&quot; id=&quot;wrapping-up&quot;&gt;Wrapping up&lt;/h2&gt;



&lt;p&gt;In all, I would say this was one of the most difficult exams I&amp;#8217;ve had to prepare for mostly due to the sheer volume of information involved. But passing it and having all that important foundational knowledge made the hard work worthwhile in my opinion.&lt;/p&gt;

    </content>
	</entry>
  
	<entry>
		<title>Implementing Dark Mode</title>
		<link href="https://stevenwoodson.com/blog/implementing-dark-mode"/>
		<published>2021-03-14T11:52:00.000Z</published>
		<updated>2023-08-10T17:27:05.000Z</updated>
		<id>https://stevenwoodson.com/blog/implementing-dark-mode</id>
    <summary>Details of how I implemented dark mode on Be Inclusive, including some issues I encountered along the way.</summary>
		<content type="html">&lt;img src=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/10/hero-1.png&quot; alt=&quot;&quot;&gt;
&lt;p&gt;I love getting feedback from folks using the&amp;nbsp;&lt;a href=&quot;https://beinclusive.app/&quot;&gt;Be Inclusive app&lt;/a&gt;, my favorite feedback by far is actionable suggestions to make the app better. One such wonderful suggestion — and the topic of this post — that I&amp;#8217;ve recently taken the time to implement is a&amp;nbsp;&lt;strong&gt;Dark Mode&lt;/strong&gt;&amp;nbsp;theme. It was wrought with tons of twists and turns, and ended up taking a lot more thought and effort than I had anticipated at the start, so I&amp;#8217;m documenting the process including initial setup, major necessary changes, and how I worked in support for both the browser native&amp;nbsp;&lt;code&gt;prefers-color-scheme&lt;/code&gt;&amp;nbsp;as well as a toggle to choose between the default and dark mode on the user profile page. At the end of this post, I also have a list of resources that were extremely helpful in getting things set up. So let&amp;#8217;s dive in!&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot; id=&quot;why-implement-dark-mode%3F&quot;&gt;Why Implement Dark Mode?&lt;/h2&gt;



&lt;p&gt;First things first, gotta ask the obvious question of why we should take the time to work in dark mode. Other than it being all the rage for at least a few years now, darker themes are also helpful for reducing eye strain and for people with low vision or photosensitive conditions. This alone was reason enough for me to set some time aside for this.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot; id=&quot;getting-started&quot;&gt;Getting Started&lt;/h2&gt;



&lt;p&gt;I had mentioned in the intro that I didn&amp;#8217;t anticipate this taking very long, that&amp;#8217;s because I had already spent a considerable amount of time setting up a&amp;nbsp;&lt;a href=&quot;https://beinclusive.app/styleguide/?p=atoms-brand-colors&quot;&gt;color scheme fully defined in SCSS variables&lt;/a&gt;. Initially, I naively assumed that swapping these colors would get me most of the way there. Boy was I wrong, let&amp;#8217;s take a look at all the issues I came across.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot; id=&quot;scss-variables&quot;&gt;SCSS Variables&lt;/h3&gt;



&lt;p&gt;The first issue I realized I had was that SCSS variables wouldn&amp;#8217;t let me swap colors without having basically&amp;nbsp;&lt;em&gt;all&lt;/em&gt;&amp;nbsp;styles duplicated for dark and light modes, yikes. After a bit of research to allay my concerns about browser support, I added another layer to my stylesheet organization that set up&amp;nbsp;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties&quot;&gt;CSS Custom Variables&lt;/a&gt;&amp;nbsp;with a fallback to the light theme values for browsers that don&amp;#8217;t support variables.&lt;/p&gt;



&lt;p&gt;This ended up being just one additional SCSS file, but it also necessitated my changing all references to individual SCSS color variables into references to the new Custom Variable instead. As you can imagine, this affected essentially every single stylesheet but was still relatively straightforward. For example&amp;nbsp;&lt;code&gt;$c-primary&lt;/code&gt;&amp;nbsp;changed to&amp;nbsp;&lt;code&gt;var(--c-primary)&lt;/code&gt;.&lt;/p&gt;



&lt;p&gt;Once the leg work was done to convert to custom variables, I then adjusted the colors to be more dark mode friendly, it took a&amp;nbsp;&lt;em&gt;lot&lt;/em&gt;&amp;nbsp;of adjustments but the image below shows the final comparison between the same named colors between light and dark mode.&lt;/p&gt;



&lt;figure class=&quot;wp-block-image size-large&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1024&quot; height=&quot;576&quot; src=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/10/swatches-1024x576.png&quot; alt=&quot;Light and Dark mode examples of sitewide color swatches placed side by side&quot; class=&quot;wp-image-85&quot; srcset=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/10/swatches-1024x576.png 1024w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/10/swatches-300x169.png 300w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/10/swatches-768x432.png 768w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/10/swatches-1536x864.png 1536w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/10/swatches.png 1920w&quot; sizes=&quot;auto, (max-width: 1024px) 100vw, 1024px&quot; /&gt;&lt;figcaption class=&quot;wp-element-caption&quot;&gt;Itemized list of all major colors used across the site, left is light mode and the right is the finalized dark mode.&lt;/figcaption&gt;&lt;/figure&gt;



&lt;h3 class=&quot;wp-block-heading&quot; id=&quot;visual-depth&quot;&gt;Visual Depth&lt;/h3&gt;



&lt;p&gt;I rely heavily on drop shadows to give the perception of depth across the whole site, turns out drop shadows for depth on dark mode looks either like absolute garbage or is not visible at all in the first place. Live and learn. After much consideration and reading (check the resources below for some great detailed case studies on depth in dark backgrounds), I replaced the drop shadows with increasingly lighter background colors for higher elevations.&lt;/p&gt;



&lt;figure class=&quot;wp-block-image size-large&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1024&quot; height=&quot;576&quot; src=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/10/depth-1024x576.png&quot; alt=&quot;Light and Dark mode examples of sitewide depth placed side by side&quot; class=&quot;wp-image-83&quot; srcset=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/10/depth-1024x576.png 1024w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/10/depth-300x169.png 300w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/10/depth-768x432.png 768w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/10/depth-1536x864.png 1536w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/10/depth.png 1920w&quot; sizes=&quot;auto, (max-width: 1024px) 100vw, 1024px&quot; /&gt;&lt;figcaption class=&quot;wp-element-caption&quot;&gt;Various levels of depth supported sitewide, left is the default with dropshadow and right is the dark mode relying more heavily on background colors instead.&lt;/figcaption&gt;&lt;/figure&gt;



&lt;p&gt;This reliance on background color changes also led to a great deal more testing in two key areas:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;Ensuring a minimum contrast between the background and the content is maintained since the background color is no longer static.&lt;/li&gt;



&lt;li&gt;Check across multiple screens including mobile devices with brightness turned down to ensure that the differences in depth are perceptible enough.&lt;/li&gt;
&lt;/ul&gt;



&lt;h3 class=&quot;wp-block-heading&quot; id=&quot;tweaks%2C-tweaks%2C-tweaks&quot;&gt;Tweaks, Tweaks, Tweaks&lt;/h3&gt;



&lt;p&gt;Even with the intense adjustments made to the underlying colors, there were still a dozen or so situations where the light mode styling was just not suitable for a dark mode. Generally anywhere that relied on gradients and different colors to visually discern between elements fell flat. Either it became too busy, unreadable, or just plain visually unappealing.&lt;/p&gt;



&lt;p&gt;Below is the evolution of alert styles, for example. The light mode version of errors, info, success, and warning alerts is on the left. Center is the same styled alerts with dark mode colors, obviously not ideal as the content won&amp;#8217;t be readable on the right half and the gradients took on a weird alternating color that looked odd. After several tweaks, the best result I found was to remove the gradients altogether and instead increase the border thickness to still call attention to these important messages.&lt;/p&gt;



&lt;figure class=&quot;wp-block-image size-large&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1024&quot; height=&quot;576&quot; src=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/10/alert-tweaks-1024x576.png&quot; alt=&quot;Three versions of alert styling for errors, info, success, and warnings. Left shows what they look like on a light background heavily relying on gradients and thin borders. Center shows the same alerts in dark mode with no tweaks, difficult to read and too busy. Right is the finalized version with no gradients and thicker borders.&quot; class=&quot;wp-image-82&quot; srcset=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/10/alert-tweaks-1024x576.png 1024w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/10/alert-tweaks-300x169.png 300w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/10/alert-tweaks-768x432.png 768w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/10/alert-tweaks-1536x864.png 1536w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/10/alert-tweaks.png 1920w&quot; sizes=&quot;auto, (max-width: 1024px) 100vw, 1024px&quot; /&gt;&lt;figcaption class=&quot;wp-element-caption&quot;&gt;Three versions of alert styles, left is the light mode, center is dark mode before any tweaks (not great), right is after several iterations of tweaking to make them look okay on a darker bavckground.&lt;/figcaption&gt;&lt;/figure&gt;



&lt;h2 class=&quot;wp-block-heading&quot; id=&quot;supporting-prefers-color-scheme-and-user-preference&quot;&gt;Supporting&amp;nbsp;&lt;code&gt;prefers-color-scheme&lt;/code&gt;&amp;nbsp;and User Preference&lt;/h2&gt;



&lt;p&gt;There were two important use cases I wanted to ensure I covered:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;Make sure to adhere to&amp;nbsp;&lt;code&gt;prefers-color-scheme&lt;/code&gt;&amp;nbsp;first, and especially if JavaScript is disabled.&lt;/li&gt;



&lt;li&gt;Give users the option to choose separately from their current&amp;nbsp;&lt;code&gt;prefers-color-scheme&lt;/code&gt;&amp;nbsp;setting.&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;As it turns out, supporting both these situations called for a bit of duplicative styling. Since they were both very important to me, I opted to figure out a way to keep the source code&amp;nbsp;DRY&amp;nbsp;even though the compiled styles have some duplication.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot; id=&quot;the-problem-supporting-both&quot;&gt;The Problem Supporting Both&lt;/h3&gt;



&lt;p&gt;To change colors and apply specific styles supporting dark mode with&amp;nbsp;&lt;code&gt;prefers-color-scheme&lt;/code&gt;, you can easily use an&amp;nbsp;&lt;code&gt;@media&lt;/code&gt;&amp;nbsp;query like this:&lt;/p&gt;



&lt;pre class=&quot;wp-block-code language-css&quot;&gt;&lt;code&gt;--c-primary: #{$c-primary};

@media (prefers-color-scheme: dark) {
  --c-primary: #{$c-darkmode-primary};
}&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;This works great for that, but doesn&amp;#8217;t support user-selected themes. There are many ways to keep track of users theme selections, I opted for storing in&amp;nbsp;&lt;code&gt;localStorage&lt;/code&gt;&amp;nbsp;close to how it&amp;#8217;s implemented&amp;nbsp;&lt;a href=&quot;https://css-tricks.com/a-complete-guide-to-dark-mode-on-the-web/#storing-a-users-preference&quot;&gt;here on this excellent CSS Tricks article&lt;/a&gt;. I have a small piece of JavaScript that then grabs that value from localStorage and adds a data attribute to the&amp;nbsp;&lt;code&gt;&amp;lt;html&amp;gt;&lt;/code&gt;&amp;nbsp;element. With that setup, we can change styles based on user selection with the same basic code from above, like this:&lt;/p&gt;



&lt;pre class=&quot;wp-block-code language-css&quot;&gt;&lt;code&gt;--c-primary: #{$c-primary};

@media (prefers-color-scheme: dark) {
  --c-primary: #{$c-darkmode-primary};
}&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;Problem is, there&amp;#8217;s no great way to do&amp;nbsp;&lt;em&gt;both&lt;/em&gt;&amp;nbsp;of those together because one is a selector on a parent and the other is a media query. My solve was to place all dark mode specific styles in a dedicated&amp;nbsp;&lt;code&gt;_dark-mode.scss&lt;/code&gt;&amp;nbsp;partial and then import that into both methods. Here&amp;#8217;s a simplified example:&lt;/p&gt;



&lt;pre class=&quot;wp-block-code language-css&quot;&gt;&lt;code&gt;@media (prefers-color-scheme: dark) {
  html.no-js {
    @import &quot;dark-mode&quot;;
  }
}

&amp;#91;data-color-scheme=&quot;dark&quot;] {
  @import &quot;dark-mode&quot;;
}&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;You might have noticed that the&amp;nbsp;&lt;code&gt;prefers-color-scheme&lt;/code&gt;&amp;nbsp;snippet also has another selector inside checking specifically for&amp;nbsp;&lt;code&gt;html.no-js&lt;/code&gt;, this is because we don&amp;#8217;t want these settings to override the users preference in the case where their operating system is set to dark mode but their preference for this site is for light mode. Since the user preference is applied with JavaScript, we fall back to&amp;nbsp;&lt;code&gt;prefers-color-scheme&lt;/code&gt;&amp;nbsp;when JavaScript is disabled instead. Part of the small JavaScript snippet noted above also removes the&amp;nbsp;&lt;code&gt;no-js&lt;/code&gt;&amp;nbsp;class from the HTML element as it adds the&amp;nbsp;&lt;code&gt;data-color-scheme&lt;/code&gt;&amp;nbsp;attribute.&lt;/p&gt;



&lt;p&gt;A bit of a bummer having the contents of&amp;nbsp;&lt;code&gt;_dark-mode.scss&lt;/code&gt;&amp;nbsp;in my styles twice but it&amp;#8217;s currently just a couple hundred lines of CSS coming in at less than 10kb before being minified, so the overall impact is small enough to be worthwhile for the additional support.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot; id=&quot;avoiding-flash-of-incorrect-theme-(foit)&quot;&gt;Avoiding Flash of Incorrect Theme (FOIT)&lt;/h2&gt;



&lt;p&gt;Also referred to as Flash of Default Theme (FODT), this is conceptually similar to the classic&amp;nbsp;&lt;a href=&quot;https://en.wikipedia.org/wiki/Flash_of_unstyled_content&quot;&gt;Flash of Unstyled Content (FOUC)&lt;/a&gt;&amp;nbsp;but specifically referring to the potential for the default (light) theme to be displayed briefly while the user preference has yet to be defined.&lt;/p&gt;



&lt;p&gt;With the above JavaScript solution in place I got everything I was looking for, the site theme starts with the&amp;nbsp;&lt;code&gt;prefers-color-scheme&lt;/code&gt;&amp;nbsp;value and then persists the users selection when changed. Great! But in testing on some slower machines and mobile devices I started noticing that the lighter default theme was getting applied briefly before the dark mode kicked in. Especially not ideal for the very users I implemented this feature for, people with photosensitivity seeing a flash of the lighter theme first wouldn&amp;#8217;t likely appreciate that.&lt;/p&gt;



&lt;p&gt;So how&amp;#8217;d I fix it? I broke one of the cardinal rules of JavaScript and placed this theme-based script in the&amp;nbsp;&lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt;&amp;nbsp;before the stylesheets. In this case I felt it was the right thing to do, it&amp;#8217;s a very small amount of JavaScript and it completely removed the FOIT in subsequent tests.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot; id=&quot;putting-it-all-together&quot;&gt;Putting it All Together&lt;/h2&gt;



&lt;p&gt;Because I&amp;#8217;ve got a lot going on in my stack for Be Inclusive that complicates the underlying implementation I&amp;#8217;ve noted thusfar, I opted to create a bare-bones example for you in a CodePen. I tried to cram everything into one pen so there are some minor deviations, but I hope this gets the point across!&lt;/p&gt;



&lt;p class=&quot;codepen&quot; data-height=&quot;500&quot; data-default-tab=&quot;html,result&quot; data-slug-hash=&quot;jOVJYOj&quot; data-user=&quot;stevenwoodson&quot; style=&quot;height: 500px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;&quot;&gt;
  &lt;span&gt;See the Pen &lt;a href=&quot;https://codepen.io/stevenwoodson/pen/jOVJYOj&quot;&gt;
  Dark Mode Toggle&lt;/a&gt; by Steve Woodson (&lt;a href=&quot;https://codepen.io/stevenwoodson&quot;&gt;@stevenwoodson&lt;/a&gt;)
  on &lt;a href=&quot;https://codepen.io/&quot;&gt;CodePen&lt;/a&gt;.&lt;/span&gt;
&lt;/p&gt;
&lt;script async=&quot;&quot; src=&quot;https://cpwebassets.codepen.io/assets/embed/ei.js&quot;&gt;&lt;/script&gt;



&lt;h2 class=&quot;wp-block-heading&quot; id=&quot;references&quot;&gt;References&lt;/h2&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties&quot;&gt;MDN &amp;#8211; Using CSS custom properties (variables)&lt;/a&gt;&lt;/li&gt;



&lt;li&gt;&lt;a href=&quot;https://css-tricks.com/a-complete-guide-to-dark-mode-on-the-web/#ua-styles&quot;&gt;CSS Tricks &amp;#8211; A Complete Guide to Dark Mode on the Web&lt;/a&gt;&lt;/li&gt;



&lt;li&gt;&lt;a href=&quot;https://uxplanet.org/8-tips-for-dark-theme-design-8dfc2f8f7ab6&quot;&gt;Nick Babich &amp;#8211; 8 Tips for Dark Theme Design&lt;/a&gt;&lt;/li&gt;



&lt;li&gt;&lt;a href=&quot;https://www.material.io/design/color/dark-theme.html#usage&quot;&gt;Material Design &amp;#8211; Dark theme&lt;/a&gt;&lt;/li&gt;



&lt;li&gt;&lt;a href=&quot;https://blog.jim-nielsen.com/2019/applying-multiple-background-colors-with-css/&quot;&gt;Applying Multiple Background Colors with CSS&lt;/a&gt;&lt;/li&gt;



&lt;li&gt;&lt;a href=&quot;https://www.a11yproject.com/posts/2020-01-23-operating-system-and-browser-accessibility-display-modes/&quot;&gt;The A11y Project &amp;#8211; Operating System and Browser Accessibility Display Modes&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

    </content>
	</entry>
  
	<entry>
		<title>Alternatives for object-fit in IE11</title>
		<link href="https://stevenwoodson.com/blog/ie11-object-fit-alternative"/>
		<published>2020-08-30T11:50:00.000Z</published>
		<updated>2023-08-10T17:27:06.000Z</updated>
		<id>https://stevenwoodson.com/blog/ie11-object-fit-alternative</id>
    <summary>A background image compromise to support IE11 while also giving modern browsers the best experience using CSS object-fit. Presented with detailed problem, solution, examples, and alternatives.</summary>
		<content type="html">&lt;img src=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/10/hero.png&quot; alt=&quot;&quot;&gt;
&lt;h2 class=&quot;wp-block-heading&quot; id=&quot;the-problem&quot;&gt;The problem&lt;/h2&gt;



&lt;p&gt;I recently came across an interesting issue where several prerequisites led to my turning to the&amp;nbsp;&lt;code&gt;object-fit&lt;/code&gt;&amp;nbsp;CSS property to achieve the desired layout. Here were the prerequisites:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;Support multiple image sizes based on browser size (srcset)&lt;/li&gt;



&lt;li&gt;Image paths are coming from another location after pageload so CSS-based background images were not feasible&lt;/li&gt;



&lt;li&gt;The image is used in the background under a gradient and some text.&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;Using&amp;nbsp;&lt;code&gt;object-fit&lt;/code&gt;&amp;nbsp;alone would have worked great if it weren&amp;#8217;t for the&amp;nbsp;&lt;a href=&quot;https://caniuse.com/#search=object-fit&quot;&gt;absolutely zero support in IE11&lt;/a&gt;, which happened to be a requirement for this particular project as well. The final solution ended up not being as straightforward as I had hoped so I&amp;#8217;m documenting here hopeing that it helps someone else that came to the same crossroads.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot; id=&quot;first-attempt&quot;&gt;First Attempt&lt;/h2&gt;



&lt;p&gt;To try to sidestep the use of&amp;nbsp;&lt;code&gt;object-fit&lt;/code&gt;&amp;nbsp;for the background image, I opted instead to use absolute positioning and force a width of 100% like the example below. This mostly worked, but there were some cases on smaller breakpoints (depending on the image aspect ratio) that led to visible gaps in the container. I made the background cyan in this example so this gap is more noticeable.&lt;/p&gt;



&lt;p class=&quot;codepen&quot; data-height=&quot;500&quot; data-default-tab=&quot;html,result&quot; data-slug-hash=&quot;WNwOpbZ&quot; data-user=&quot;stevenwoodson&quot; style=&quot;height: 500px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;&quot;&gt;
  &lt;span&gt;See the Pen &lt;a href=&quot;https://codepen.io/stevenwoodson/pen/WNwOpbZ&quot;&gt;
  IE11 object-fit Alternative &amp;#8211; before&lt;/a&gt; by Steve Woodson (&lt;a href=&quot;https://codepen.io/stevenwoodson&quot;&gt;@stevenwoodson&lt;/a&gt;)
  on &lt;a href=&quot;https://codepen.io/&quot;&gt;CodePen&lt;/a&gt;.&lt;/span&gt;
&lt;/p&gt;
&lt;script async=&quot;&quot; src=&quot;https://cpwebassets.codepen.io/assets/embed/ei.js&quot;&gt;&lt;/script&gt;



&lt;h2 class=&quot;wp-block-heading&quot; id=&quot;final-solution&quot;&gt;Final Solution&lt;/h2&gt;



&lt;p&gt;In order to prevent gaps on mobile while also ensuring that IE11 desktop users had a reasonable experience, I finally opted to use a combination of the absolutely positioned method above&amp;nbsp;&lt;em&gt;and&lt;/em&gt;&amp;nbsp;&lt;code&gt;object-fit&lt;/code&gt;. I set the latter to kick in on mobile so I don&amp;#8217;t have to try addressing these gaps individually like the revised example below.&lt;/p&gt;



&lt;p class=&quot;codepen&quot; data-height=&quot;500&quot; data-default-tab=&quot;html,result&quot; data-slug-hash=&quot;dyMRNxb&quot; data-user=&quot;stevenwoodson&quot; style=&quot;height: 500px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;&quot;&gt;
  &lt;span&gt;See the Pen &lt;a href=&quot;https://codepen.io/stevenwoodson/pen/dyMRNxb&quot;&gt;
  IE11 object-fit Alternative &amp;#8211; after&lt;/a&gt; by Steve Woodson (&lt;a href=&quot;https://codepen.io/stevenwoodson&quot;&gt;@stevenwoodson&lt;/a&gt;)
  on &lt;a href=&quot;https://codepen.io/&quot;&gt;CodePen&lt;/a&gt;.&lt;/span&gt;
&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot; id=&quot;rationale&quot;&gt;Rationale&lt;/h2&gt;



&lt;p&gt;I believe the combination of absolutely positioning on desktop/tablet and&amp;nbsp;&lt;code&gt;object-fit&lt;/code&gt;&amp;nbsp;on mobile was a worthwhile tradeoff between supporting an older browser while also ensuring modern browsers get the best experience possible. Going on the assumption that IE11 usage is rather low and that this number drops even lower at a mobile breakpoint size, I felt this was a reasonable solution.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot; id=&quot;alternatives&quot;&gt;Alternatives&lt;/h2&gt;



&lt;p&gt;Depending on your particular situation, here are a few other alternatives that could be pursued:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;strong&gt;Use a background image instead of an&amp;nbsp;&lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt;&lt;/strong&gt;. If you don&amp;#8217;t need to support multiple image sizes using srcset or if the paths to the images are static and could be supplied using CSS breakpoints, you could use a background image with&amp;nbsp;&lt;code&gt;background-size: cover&lt;/code&gt;.&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;Use Modernizr&lt;/strong&gt;. If something like this is just one instance where you need to strike the balance between supporting IE11 while also building to modern standards, you could implement&amp;nbsp;&lt;a href=&quot;https://modernizr.com/&quot;&gt;Modernizr&lt;/a&gt;&amp;nbsp;to give you more control. Being able to check for support and have solutions for both situations is very powerful.&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;Defined aspect ratio&lt;/strong&gt;. Finally, if the image you&amp;#8217;re trying to implement needs to fit into a container that has a set aspect ratio and doesn&amp;#8217;t need to fit to content placed inside, you could have the images sized to the same aspect ratio as the container. Then the absolute positioning would work across all breakpoints and&amp;nbsp;&lt;code&gt;object-fit&lt;/code&gt;&amp;nbsp;wouldn&amp;#8217;t be necessary.&lt;/li&gt;
&lt;/ul&gt;



&lt;h2 class=&quot;wp-block-heading&quot; id=&quot;references&quot;&gt;References&lt;/h2&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/object-fit&quot;&gt;https://developer.mozilla.org/en-US/docs/Web/CSS/object-fit&lt;/a&gt;&lt;/li&gt;



&lt;li&gt;&lt;a href=&quot;https://caniuse.com/#search=object-fit&quot;&gt;https://caniuse.com/#search=object-fit&lt;/a&gt;&lt;/li&gt;



&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/background-size&quot;&gt;https://developer.mozilla.org/en-US/docs/Web/CSS/background-size&lt;/a&gt;&lt;/li&gt;



&lt;li&gt;&lt;a href=&quot;https://modernizr.com/&quot;&gt;https://modernizr.com/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

    </content>
	</entry>
  
	<entry>
		<title>Soft Skills: Productivity and Goal Setting</title>
		<link href="https://stevenwoodson.com/blog/soft-skills-productivity-and-goal-setting"/>
		<published>2019-01-23T11:48:00.000Z</published>
		<updated>2023-10-30T20:15:54.000Z</updated>
		<id>https://stevenwoodson.com/blog/soft-skills-productivity-and-goal-setting</id>
    <summary>It&amp;#8217;s easier to set well-defined, achievable goals when you base them on long-term, general goals. A personal mission statement and thinking in terms of roles can help.</summary>
		<content type="html">&lt;img src=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/10/soft-skills-productivity-and-goal-setting.png&quot; alt=&quot;&quot;&gt;
&lt;p&gt;I wrote an earlier post called &amp;#8220;&lt;a href=&quot;https://stevenwoodson.com/blog/soft-skills-improving-your-emotional-intelligence/&quot;&gt;Improving Your Emotional Intelligence&lt;/a&gt;,&amp;#8221; which introduced the topic of soft skills and practical advice on how to incorporate those skills into your life in measurable ways. In this second installment of soft-skills topics, we&amp;#8217;ll explore some practical, high-level productivity techniques including the methodologies from &amp;#8220;&lt;a href=&quot;https://gettingthingsdone.com/&quot; rel=&quot;nofollow&quot;&gt;Getting Things Done&lt;/a&gt;&lt;strong&gt;,&lt;/strong&gt;&amp;#8221; &amp;#8220;&lt;a href=&quot;https://www.stephencovey.com/7habits/7habits.php&quot; rel=&quot;nofollow&quot;&gt;The Seven Habits of Highly Effective People,&lt;/a&gt;&amp;#8221; and &amp;#8220;&lt;a href=&quot;https://www.whatmatters.com/&quot; rel=&quot;nofollow&quot;&gt;Measure What Matters&lt;/a&gt;.&amp;#8221;&lt;/p&gt;



&lt;p&gt;With an initial investment in organization and goal setting, you can then focus on small periodic routines that help you improve daily productivity. This approach has worked wonders for me, I hope it can for you too!&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot; id=&quot;getting-started&quot;&gt;Getting Started&lt;/h2&gt;



&lt;h3 class=&quot;wp-block-heading&quot; id=&quot;setup-and-capturing&quot;&gt;Setup and Capturing&lt;/h3&gt;



&lt;p&gt;The single action that I could look back and say &amp;#8220;that was where it all started,&amp;#8221; was when I took to heart the concept of capturing everything that has my attention and to get it out of my head.&lt;/p&gt;



&lt;blockquote class=&quot;wp-block-quote is-layout-flow wp-block-quote-is-layout-flow&quot;&gt;
&lt;p&gt;Use your mind to think about things, rather than think of them. You want to be adding value as you think about projects and people, not simply reminding yourself they exist.&lt;/p&gt;
&lt;cite&gt;David Allen, Getting Things Done&lt;/cite&gt;&lt;/blockquote&gt;



&lt;p&gt;With a bit of time setting up&amp;nbsp;Evernote&amp;nbsp;for long-term notes and&amp;nbsp;&lt;a href=&quot;https://todoist.com/?lang=en&quot; rel=&quot;nofollow&quot;&gt;Todoist&lt;/a&gt;&amp;nbsp;for managing tasks, I now have a quick and easy way to offload things that catch my attention. I&amp;#8217;m able to mentally let these thoughts and tasks go safely in the knowledge that they&amp;#8217;re in a place where I won&amp;#8217;t forget about them, freeing me to focus on the task at hand. There are many note and task tools out there, including good old pen and paper, the ones you choose should fit your organization style and be as easy to use as possible. Any friction in this process will make it difficult to keep it up.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot; id=&quot;weekly-review&quot;&gt;Weekly Review&lt;/h3&gt;



&lt;p&gt;In order to maintain the momentum and trust in the system I created, I perform a weekly review following the Getting Things Done (GTD) mantra of &amp;#8220;&lt;strong&gt;get&lt;/strong&gt;&amp;nbsp;&lt;strong&gt;clear, get current, and get creative&lt;/strong&gt;.&amp;#8221;&amp;nbsp;&lt;a href=&quot;https://gettingthingsdone.com/2015/07/podcast-07-guided-gtd-weekly-review/&quot; rel=&quot;nofollow&quot;&gt;This podcast&lt;/a&gt;&amp;nbsp;is a great overview of the GTD Weekly Review, setting aside some time every week to clear all inboxes, thoroughly review all outstanding tasks, and take a bit of time getting creative with new ideas. This weekly review strengthens my resolve to keep it up and reminds me that I can truly mentally let go of tasks once I&amp;#8217;ve captured them.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot; id=&quot;setting-goals&quot;&gt;Setting Goals&lt;/h2&gt;



&lt;h3 class=&quot;wp-block-heading&quot; id=&quot;personal-mission-statement&quot;&gt;Personal Mission Statement&lt;/h3&gt;



&lt;p&gt;I find it&amp;#8217;s a lot easier to find the motivation to be productive when I have a plan, beginning with the end in mind as Stephen Covey would say, and the best way to have a plan is to develop a personal mission statement. This is your long-term personal philosophy, what you want to be and to do and on the values or principles upon which being and doing are based. With this personal mission, you have the essence of your own proactivity.&lt;/p&gt;



&lt;blockquote class=&quot;wp-block-quote is-layout-flow wp-block-quote-is-layout-flow&quot;&gt;
&lt;p&gt;A personal mission statement focuses on what you want to be and to do and on the values or principles upon which being and doing are based.&lt;/p&gt;
&lt;cite&gt;Stephen Covey, The 7 Habits&lt;/cite&gt;&lt;/blockquote&gt;



&lt;p&gt;It&amp;#8217;s well worth the effort to really think this through, there&amp;#8217;s no right or wrong way to write your mission statement. A blank page can be pretty daunting, I found the &lt;a href=&quot;https://msb.franklincovey.com/&quot; rel=&quot;nofollow&quot;&gt;Mission Statement Builder&lt;/a&gt; asked some &lt;a href=&quot;https://msb.franklincovey.com/inspired/mission_statement_examples&quot; rel=&quot;nofollow&quot;&gt;thought-provoking questions and the examples&lt;/a&gt; were very helpful in seeing how others were organized.&lt;/p&gt;



&lt;p&gt;After seeing how beneficial it was to share my mission statement with others privately as I was coaching them through this process, I decided to put it online publicly to help even more folks out. You can read &lt;a href=&quot;https://stevenwoodson.com/mission-statement/&quot;&gt;my personal and professional mission statement here&lt;/a&gt;, I hope it helps you structure yours and I&amp;#8217;d love to hear about your process when you do!&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot; id=&quot;long-term-goals&quot;&gt;Long-Term Goals&lt;/h3&gt;



&lt;p&gt;With a method to stay organized and a deeper understanding of your guiding principles, you have a solid foundation for setting long-term goals. Make these big long-term goals; goals that really stretch you. And make sure they&amp;#8217;re your goals, not the goals that you think you should have. They should be goals that get you excited about the future, goals that you can remind yourself of when you&amp;#8217;re feeling your motivation starts&amp;nbsp;to wane.&lt;/p&gt;



&lt;p&gt;From these goals, identify a small handful of specific goals that are achievable within the next five years. Be sure to be as specific and laser-focused as possible, using present tense and deadline dates. Remember, &amp;#8220;Vague goals produce vague results.&amp;#8221;&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot; id=&quot;short-term-goals&quot;&gt;Short-Term Goals&lt;/h3&gt;



&lt;p&gt;Now that you&amp;#8217;ve identified your personal mission and specific long-term goals, it&amp;#8217;s time to start planning for the week ahead with these goals in mind. To put&amp;nbsp;&lt;strong&gt;first things first&lt;/strong&gt;, start by identifying all your roles for the upcoming week. You have roles as an individual, a spouse, a parent, manager, developer, etc. These roles aren&amp;#8217;t set in stone, just consider the week ahead.&lt;/p&gt;



&lt;p&gt;With your roles defined and your long-term goals in mind, think of one or two important results you feel you should accomplish in each role during the next seven days. These are your&amp;nbsp;&lt;strong&gt;goals&lt;/strong&gt;&amp;nbsp;for the week. For example one of my goals for this week in my Bounteous Resource role was to finish this blog post today. This relates to one of my specific goals to contribute as a resource to Bounteous with at least four blog posts and/or presentations this year. Which is a more specific goal based on one of my personal missions to &amp;#8220;Be helpful and productive, a resource in more than just coding.&amp;#8221;&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot; id=&quot;let&#39;s-get-to-work!&quot;&gt;Let&amp;#8217;s Get to Work!&lt;/h2&gt;



&lt;p&gt;Alright! You&amp;#8217;ve got a plan for the week and a cleared mind ready to get to it &amp;#8211; now what? More often than not, the bigger items on our to-do lists require a good deal of focus. For knowledge workers like us, that&amp;#8217;s especially true. Getting and maintaining focus can be extremely difficult, I&amp;#8217;d say without a doubt that&amp;#8217;s the hardest thing I struggle with daily. Here are some tips to help get yourself into that state of focus.&lt;/p&gt;



&lt;p&gt;&lt;strong&gt;Listen to your body&lt;/strong&gt;. When do you feel like you&amp;#8217;re able to focus more easily? Strive to fit the focus tasks into these times and save the rest for more shallow concerns.&lt;/p&gt;



&lt;p&gt;&lt;strong&gt;Understand that focus is painful at first&lt;/strong&gt;. &amp;#8220;To get into a focused mode, you have to push through the initial pain of contorting your mind to a single task.&amp;#8221; &amp;#8211; Soft Skills book&lt;/p&gt;



&lt;p&gt;&lt;strong&gt;Control focus-busting impulses&lt;/strong&gt;. Identify bad habits by reflecting on parts of your day you feel guilty about. Identify the cue that triggers it, the activity, and the reward. Next, try to figure out if there&amp;#8217;s some other routine you can substitute for the one you&amp;#8217;re currently doing for that habit. If possible, try to find something that you can do that will carry a similar reward or even the same type of reward. For example, I had recently identified that I would fire up Twitter when I&amp;#8217;d start a code build on a larger project that I knew would take a few minutes to run, but many times I&amp;#8217;d spend much longer than those few minutes catching up on new content. So my cue was starting a code build, the activity was opening Twitter, and the reward was the temporary reprieve of watching the build progress. Lately, I&amp;#8217;ve replaced the activity with checking email. I usually have a very small number of messages built up so it doesn&amp;#8217;t take more than a few minutes and I&amp;#8217;m able to get something reasonably productive done rather than peeking at social media.&lt;/p&gt;



&lt;p&gt;&lt;strong&gt;When you lose momentum, try a change of context&lt;/strong&gt;. Like pausing on writing code to go take care of some code reviews. Get up and stretch your legs, go for a walk, and just get away from the screen for a bit. Set a very small goal and a time limit to complete, sometimes that setting and completing a to-do quickly can build your momentum back up. If nothing seems to be working, honestly assess your current situation and state of mind. Are you preoccupied with something else going on? Did you not get enough rest? Are you feeling overworked? This honest assessment can help you identify an underlying issue that needs to be dealt with. This tip led me to realize that I was trying to push myself too much in the evenings, I wasn&amp;#8217;t allowing myself to rest enough to be fresh the next day. I honestly still struggle with this.&lt;/p&gt;



&lt;p&gt;&lt;strong&gt;Avoid Burnout&lt;/strong&gt;. Most commonly you&amp;#8217;re hitting a wall, starting out with high interest that quickly wanes. Motivation starts low but rises as you make progress, but the slow pace of results wears at motivation until both interest and motivation are gone. Pushing past the wall is difficult but extremely rewarding. Set a schedule and hold yourself accountable.&lt;/p&gt;



&lt;p&gt;Overworking is real, it&amp;#8217;s horrible, and it should be avoided. Luckily at Bounteous, I&amp;#8217;ve seen far less of it than any other company, places where people would brag about the number of hours they put in last week. Know your warning signs and have the personal integrity to back off a bit and let yourself rest.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot; id=&quot;recap&quot;&gt;Recap&lt;/h2&gt;



&lt;p&gt;For the TL;DR crowd, we&amp;#8217;ve outlined three core principles that can open you up to more purposeful productivity:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;strong&gt;Getting Started&lt;/strong&gt;:&amp;nbsp;Offload tasks and long-term notes to a trusted system and free your mind to think about things, rather than think of them.&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;Setting Goals&lt;/strong&gt;:&amp;nbsp;Create a personal mission statement that will inform your long-term goals; use these to come up with short-term goals that are then split into actionable items.&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;Get to work&lt;/strong&gt;:With your upcoming week&amp;#8217;s goals clearly defined, there&amp;#8217;s nothing left to do but get started! Be mindful of momentum, and focus and listen to your body for the ideal times to get the most out of your day.&lt;/li&gt;
&lt;/ul&gt;



&lt;h2 class=&quot;wp-block-heading&quot; id=&quot;resources&quot;&gt;Resources&lt;/h2&gt;



&lt;h3 class=&quot;wp-block-heading&quot; id=&quot;books&quot;&gt;Books&lt;/h3&gt;



&lt;p&gt;If you&amp;#8217;re looking to add to your reading list, I recommend the following books:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://www.stephencovey.com/7habits/7habits.php&quot; rel=&quot;nofollow&quot;&gt;The 7 Habits of Highly Effective People&lt;/a&gt;&amp;nbsp;by Stephen R. Covey&lt;/li&gt;



&lt;li&gt;&lt;a href=&quot;https://gettingthingsdone.com/&quot; rel=&quot;nofollow&quot;&gt;Getting Things Done&lt;/a&gt;&amp;nbsp;by David Allen&lt;/li&gt;
&lt;/ul&gt;



&lt;h3 class=&quot;wp-block-heading&quot; id=&quot;articles&quot;&gt;Articles&lt;/h3&gt;



&lt;p&gt;For the web-friendly, bookmarkable article, here are a few options:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://www.bounteous.com/insights/2017/06/26/effective-approaches-working-deeply-and-getting-most-out-each-day/&quot; rel=&quot;nofollow&quot;&gt;Effective Approaches for Working Deeply and Getting the Most Out of Each Day&lt;/a&gt;&amp;nbsp;by Owen Berry&lt;/li&gt;



&lt;li&gt;&lt;a href=&quot;https://www.bounteous.com/insights/2016/05/09/leveling-nerd-geek-and-fast-tracking-your-career/&quot; rel=&quot;nofollow&quot;&gt;Leveling up from Nerd to Geek and Fast-Tracking Your Caree&lt;/a&gt;r by Brett Birschbach&lt;/li&gt;



&lt;li&gt;&lt;a href=&quot;https://www.artofmanliness.com/2011/02/08/create-a-life-plan/&quot; rel=&quot;nofollow&quot;&gt;Craft the Life You Want&lt;/a&gt;&amp;nbsp;by Brett McKay&lt;/li&gt;



&lt;li&gt;&lt;a href=&quot;https://alistapart.com/article/stayingmotivated&quot; rel=&quot;nofollow&quot;&gt;Staying Motivated&lt;/a&gt;&amp;nbsp;by Kevin Cornell&lt;/li&gt;
&lt;/ul&gt;

    </content>
	</entry>
  
	<entry>
		<title>Empathy in Web Accessibility</title>
		<link href="https://stevenwoodson.com/blog/empathy-in-web-accessibility"/>
		<published>2018-05-17T11:47:00.000Z</published>
		<updated>2023-08-14T20:36:38.000Z</updated>
		<id>https://stevenwoodson.com/blog/empathy-in-web-accessibility</id>
    <summary>On Global Accessibility Awareness Day, we explore how building empathy is fundamental to improving our approach to web accessibility.</summary>
		<content type="html">&lt;img src=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/10/accessibility-awareness.jpg&quot; alt=&quot;&quot;&gt;
&lt;p&gt;Today we celebrate &lt;a href=&quot;https://www.globalaccessibilityawarenessday.org/&quot; rel=&quot;nofollow&quot;&gt;Global Accessibility Awareness Day&lt;/a&gt; with an exploration into how building empathy is fundamental to improving our approach to web accessibility. We&amp;#8217;ve touched on cognitive empathy as part of an &lt;a href=&quot;https://stevenwoodson.com/blog/soft-skills-improving-your-emotional-intelligence/&quot;&gt;introduction to emotional intelligence&lt;/a&gt;, now we&amp;#8217;re going to look at it from the perspective of accessibility. We&amp;#8217;ll describe what kinds of empathy are needed, why it&amp;#8217;s so important, and how to fit it into our everyday routines.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot; id=&quot;what-kind-of-empathy-is-needed%3F&quot;&gt;What Kind of Empathy is Needed?&lt;/h2&gt;



&lt;p&gt;First and foremost, we need strong empathy towards all users of the interfaces and applications we build regardless of handicap. We wouldn&amp;#8217;t knowingly release a new feature that couldn&amp;#8217;t be accessed by screen-reading software. The first step in ensuring that doesn&amp;#8217;t happen is empathy towards users that rely on such software to navigate the web.&lt;/p&gt;



&lt;p&gt;Following closely is empathy towards our clients, who also have the best intentions for the projects they entrust us to build. As the subject matter experts, we need to ensure we educate our clients on what it means to be accessible and to work as partners with them towards that goal.&lt;/p&gt;



&lt;p&gt;Last but not least, we need to be empathetic towards each other. To all our fellow designers, developers, QA analysts, and the myriad others we work with every day because we&amp;#8217;re all at different points in our journeys with web accessibility and can benefit from each other&amp;#8217;s unique perspectives.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot; id=&quot;why-is-empathy-important-to-accessibility%3F&quot;&gt;Why is Empathy Important to Accessibility?&lt;/h2&gt;



&lt;p&gt;Despite all the legislation and technical documentation urging for a more accessible Internet, the bottom line is it&amp;#8217;s just the right thing to do. Understanding and awareness are two of the biggest roadblocks to getting product engineers to consider issues of accessibility. Awareness to include small course corrections at the early stages of a project could have a significant impact, and the kinds of empathy mentioned above can help build that awareness.&lt;/p&gt;



&lt;p&gt;The Internet has the unique ability to connect people to a world they may otherwise have never known. Where people can experience a type of unrestricted liberty. Take Jessie Lorenz for example. She is executive director of the Independent Living Resource Center San Francisco. Having been blind since birth, she says, &amp;#8220;A lot of people are afraid of the blind. Facebook lets me control the narrative and break down some of the stigmas and show people who I am.&amp;#8221; We, as the builders of the web, have the power to connect these people!&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot; id=&quot;how-do-i-incorporate-empathy%3F&quot;&gt;How Do I Incorporate Empathy?&lt;/h2&gt;



&lt;p&gt;It&amp;#8217;s one thing to read about empathy, but it&amp;#8217;s important to move from education into action. The following steps outline ways to make accessibility part of our routine.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot; id=&quot;experience-it-for-yourself&quot;&gt;Experience It For Yourself&lt;/h3&gt;



&lt;p&gt;To build cognitive empathy for accessibility we need to understand how disabled users navigate the web. Two great places to start building that understanding is by reading about those with disabilities and attempting to experience the web as they do first-hand. The GAAD website has&amp;nbsp;&lt;a href=&quot;http://www.globalaccessibilityawarenessday.org/participate.php&quot;&gt;some great ideas&lt;/a&gt;&amp;nbsp;you can try today and check out the Resources section below for some great books and online resources.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot; id=&quot;become-a-resource&quot;&gt;Become A Resource&lt;/h3&gt;



&lt;p&gt;As you strengthen your own empathy and understanding, you can take steps to become a resource to others in helping them on their journey. You don&amp;#8217;t have to be an accessibility expert to be an advocate. Start by viewing your work with this new perspective and share this knowledge with your organization wherever you can. Consider the following tips:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;Bring up accessibility concerns in meetings&lt;/li&gt;



&lt;li&gt;Add an accessibility section to your company&amp;#8217;s knowledge base&lt;/li&gt;



&lt;li&gt;Host a lunch-and-learn or informational session (and record for future viewing!)&lt;/li&gt;



&lt;li&gt;Build an &amp;#8216;Accessibility Review&amp;#8217; stage into standard project templates&lt;/li&gt;



&lt;li&gt;Start an #accessibility channel in your company&amp;#8217;s messaging app (Slack, Hipchat, Yammer, etc.)&lt;/li&gt;
&lt;/ul&gt;



&lt;h3 class=&quot;wp-block-heading&quot; id=&quot;make-it-personal&quot;&gt;Make it Personal&lt;/h3&gt;



&lt;p&gt;In all situations mentioned thus far in this section, I find that it&amp;#8217;s best to speak in terms of real-world situations because compliance doesn&amp;#8217;t inspire action, people do. For example “this text color doesn&amp;#8217;t meet WCAG 2.0 AA success criterion 1.4.3&amp;#8243; doesn&amp;#8217;t have quite the same impact as &amp;#8220;People with red-green color blindness will have trouble reading this text.&amp;#8221; Both describe the same issue, but humanizing the issue makes all the difference.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot; id=&quot;in-closing&quot;&gt;In Closing&lt;/h2&gt;



&lt;p&gt;Accessibility is a large and multifaceted topic; it&amp;#8217;s easy to become intimidated and overwhelmed. Remember the old adage that &amp;#8220;Rome wasn&amp;#8217;t built in a day,&amp;#8221; small steps towards inclusion in our designs and code can still affect a large amount of people. And those small steps will become more and more clear as we develop our empathy for those that benefit from it.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot; id=&quot;resources&quot;&gt;Resources&lt;/h2&gt;



&lt;h3 class=&quot;wp-block-heading&quot; id=&quot;books&quot;&gt;Books&lt;/h3&gt;



&lt;p&gt;If you&amp;#8217;re looking to add to your reading list, I recommend the following books:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://rosenfeldmedia.com/books/a-web-for-everyone/&quot; rel=&quot;nofollow&quot;&gt;A Web for Everyone: Designing Accessible User Experiences&lt;/a&gt;&amp;nbsp;by Sarah Horton &amp;amp; Whitney Quesenbery&lt;/li&gt;



&lt;li&gt;&lt;a href=&quot;https://abookapart.com/products/design-for-real-life&quot; rel=&quot;nofollow&quot;&gt;Design for Real Life&lt;/a&gt;&amp;nbsp;by Sara Wachter-Boettcher and Eric Meyer&lt;/li&gt;



&lt;li&gt;&lt;a href=&quot;https://rosenfeldmedia.com/books/practical-empathy/&quot; rel=&quot;nofollow&quot;&gt;Practical Empathy&lt;/a&gt;&amp;nbsp;by Indi Young&lt;/li&gt;
&lt;/ul&gt;



&lt;h3 class=&quot;wp-block-heading&quot; id=&quot;articles&quot;&gt;Articles&lt;/h3&gt;



&lt;p&gt;For the web-friendly, bookmarkable article, here are a few options:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://dojo.ministryoftesting.com/lessons/30-days-of-accessibility-testing&quot; rel=&quot;nofollow&quot;&gt;30 Days of Accessibility Testing&lt;/a&gt;&lt;/li&gt;



&lt;li&gt;&lt;a href=&quot;https://a11yproject.com/&quot; rel=&quot;nofollow&quot;&gt;The A11Y Project&lt;/a&gt;&lt;/li&gt;



&lt;li&gt;&lt;a href=&quot;https://maya-benari.com/empathy-maker/&quot; rel=&quot;nofollow&quot;&gt;Empathy Maker&lt;/a&gt;&lt;/li&gt;



&lt;li&gt;&lt;a href=&quot;https://empathyprompts.net/&quot; rel=&quot;nofollow&quot;&gt;Empathy Prompts&lt;/a&gt;&lt;/li&gt;



&lt;li&gt;&lt;a href=&quot;https://www.microsoft.com/en-us/design/inclusive&quot; rel=&quot;nofollow&quot;&gt;Inclusive Design at Microsoft&lt;/a&gt;&lt;/li&gt;



&lt;li&gt;&lt;a href=&quot;https://www.w3.org/WAI/intro/people-use-web/stories&quot; rel=&quot;nofollow&quot;&gt;Stories of Web Users&lt;/a&gt;&amp;nbsp;at&amp;nbsp;&lt;a href=&quot;https://w3.org/&quot; rel=&quot;nofollow&quot;&gt;W3.org&lt;/a&gt;&lt;/li&gt;



&lt;li&gt;&lt;a href=&quot;https://www.wired.com/2015/02/meet-team-makes-possible-blind-use-facebook/&quot; rel=&quot;nofollow&quot;&gt;Meet the Team That Makes it Possible for the Blind to Use Facebook&lt;/a&gt;&amp;nbsp;by Cade Metz&lt;/li&gt;
&lt;/ul&gt;

    </content>
	</entry>
  
	<entry>
		<title>What the Font are Vertical Rhythm and Modular Scale?</title>
		<link href="https://stevenwoodson.com/blog/what-the-font-are-vertical-rhythm-and-modular-scale"/>
		<published>2018-03-26T11:33:00.000Z</published>
		<updated>2023-08-10T17:27:07.000Z</updated>
		<id>https://stevenwoodson.com/blog/what-the-font-are-vertical-rhythm-and-modular-scale</id>
    <summary>Why you should care about vertical rhythm and modular scale, defines them, and a demonstration on how to integrate them into your next project.</summary>
		<content type="html">&lt;img src=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/10/hero-1.jpg&quot; alt=&quot;&quot;&gt;
&lt;p&gt;Anyone that&amp;#8217;s been in earshot of me has likely heard my ramblings about style guides, and a big part of that lately has been establishing a vertical rhythm and using a modular scale. But what the font am I talking about? In this article I&amp;#8217;m going to walk you through why you should care about vertical rhythm and modular scale, define them, and finish up with a demonstration on how to integrate them into your next project.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot; id=&quot;why-should-i-care%3F&quot;&gt;Why Should I Care?&lt;/h2&gt;



&lt;p&gt;I wanted to start with this first because I feel it&amp;#8217;s beneficial to understand the problem we&amp;#8217;re trying to solve and the importance of both vertical rhythm and modular scale as parts of an overall solution. The whole reason this article is on both vertical rhythm and modular scale is because they both contribute to the visual harmony of a page.&amp;nbsp;&lt;strong&gt;Vertical rhythm&lt;/strong&gt;&amp;nbsp;engages and guides the reader down the page, good vertical rhythm makes a layout more balanced and beautiful, and its content more readable.&amp;nbsp;&lt;strong&gt;Modular scale&lt;/strong&gt;&amp;nbsp;provides resonant numbers using culturally relevant, historically pleasing ratios and encourages thoughtful repetition. Repetition breeds familiarity and reinforces a pattern that helps the user make sense of the content and layout. The Before example below is using the browser default font sizes and line heights, while the After example shows how this same content changed when applying the concepts I&amp;#8217;m introducing below.&lt;/p&gt;



&lt;figure class=&quot;wp-block-image size-large&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1024&quot; height=&quot;600&quot; src=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/10/before-after-1024x600.png&quot; alt=&quot;&quot; class=&quot;wp-image-61&quot; srcset=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/10/before-after-1024x600.png 1024w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/10/before-after-300x176.png 300w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/10/before-after-768x450.png 768w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/10/before-after-1536x901.png 1536w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/10/before-after-2048x1201.png 2048w&quot; sizes=&quot;auto, (max-width: 1024px) 100vw, 1024px&quot; /&gt;&lt;figcaption class=&quot;wp-element-caption&quot;&gt;Before and After applying the concepts of vertical rhythm and modular scale&lt;/figcaption&gt;&lt;/figure&gt;



&lt;h2 class=&quot;wp-block-heading&quot; id=&quot;vertical-rhythm&quot;&gt;Vertical Rhythm&lt;/h2&gt;



&lt;p&gt;Vertical rhythm is the spacing and arrangement of text as the user descends the page. Vertical [ ↕ ] Rhythm [ a repeated pattern ]. It&amp;#8217;s contributed by three factors; font size, line height, and vertical margins or padding. The basic unit of vertical space is line height &amp;#8211; typically the line height of the body copy, as that will be the dominant content on any given page. To illustrate, I&amp;#8217;m going to take examples from a recent site I&amp;#8217;ve been working on. At the beginning of the project, we were provided designs that use Open Sans 15px font with a line height of 25px. So in this example below the vertical rhythm base unit would be 25px.&lt;/p&gt;



&lt;figure class=&quot;wp-block-image size-large&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1024&quot; height=&quot;356&quot; src=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/10/VR-line-height-1024x356.png&quot; alt=&quot;&quot; class=&quot;wp-image-69&quot; srcset=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/10/VR-line-height-1024x356.png 1024w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/10/VR-line-height-300x104.png 300w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/10/VR-line-height-768x267.png 768w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/10/VR-line-height-1536x534.png 1536w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/10/VR-line-height-2048x712.png 2048w&quot; sizes=&quot;auto, (max-width: 1024px) 100vw, 1024px&quot; /&gt;&lt;figcaption class=&quot;wp-element-caption&quot;&gt;Vertical Rhythm Line Height example&lt;/figcaption&gt;&lt;/figure&gt;



&lt;p&gt;With our base unit figured out, we need to now ensure that it&amp;#8217;s maintained throughout the body copy. A common place to lose the rhythm is in the margin gaps set between block level elements. The default browser treatment of paragraphs is to insert a top and bottom margin of 1em and call it a day. This is not ideal if the rhythm is to be maintained, in our example below that&amp;#8217;d mean the margins would be 15px instead of the 25px we need. It&amp;#8217;s also worth noting that different fonts have different line heights built into them further causing visually inconsistent spacing. So margins and line heights need to be reset.&lt;/p&gt;



&lt;figure class=&quot;wp-block-image size-full&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;487&quot; height=&quot;435&quot; src=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/10/VR-example-no-margins-zoomed.png&quot; alt=&quot;&quot; class=&quot;wp-image-68&quot; srcset=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/10/VR-example-no-margins-zoomed.png 487w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/10/VR-example-no-margins-zoomed-300x268.png 300w&quot; sizes=&quot;auto, (max-width: 487px) 100vw, 487px&quot; /&gt;&lt;figcaption class=&quot;wp-element-caption&quot;&gt;An example showing the gaps from margins between block level elements&lt;/figcaption&gt;&lt;/figure&gt;



&lt;p&gt;This is common practice nowadays but I wanted to note that it&amp;#8217;s always best to start things out on a level playing field, we need to make sure all vertical measurements including line height, padding, and margins are reset in a cross-browser compatible way. With this level playing field we&amp;#8217;ll be able to then set these values as needed while maintaining the rhythm. I&amp;#8217;ve used the Normalize.css, good ol&amp;#8217; Reset CSS from Eric Meyer, and even the following block with success. Whichever you choose will depend on your projects needs.&lt;/p&gt;



&lt;pre class=&quot;wp-block-code language-css&quot;&gt;&lt;code&gt;body,
pre,
div,
dl,
dt,
dd,
ul,
ol,
li,
h1,
h2,
h3,
h4,
h5,
h6,
form,
fieldset,
p,
blockquote,
th,
td {
  margin: 0;
  padding: 0;
}&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;With any site there are bound to be quite a few variations in text sizes for elements such as headlines and block quotes, all these differing text sizes should also take up multiples of the base unit. Continuing our example, that&amp;#8217;d mean that every diversion from the paragraph text size should still take multiples of 25px. This is where the real fun begins, adjusting the line height and margin of these elements accordingly. We&amp;#8217;ll dive into more details on this in the last section on how to integrate these concepts into your projects.&lt;/p&gt;



&lt;figure class=&quot;wp-block-image size-large&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1024&quot; height=&quot;714&quot; src=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/10/VR-multiple-variations-1024x714.png&quot; alt=&quot;&quot; class=&quot;wp-image-70&quot; srcset=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/10/VR-multiple-variations-1024x714.png 1024w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/10/VR-multiple-variations-300x209.png 300w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/10/VR-multiple-variations-768x535.png 768w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/10/VR-multiple-variations.png 1166w&quot; sizes=&quot;auto, (max-width: 1024px) 100vw, 1024px&quot; /&gt;&lt;figcaption class=&quot;wp-element-caption&quot;&gt;Illustration of multiple variations in text size&lt;/figcaption&gt;&lt;/figure&gt;



&lt;h2 class=&quot;wp-block-heading&quot; id=&quot;modular-scale&quot;&gt;Modular Scale&lt;/h2&gt;



&lt;p&gt;Modular scale is a sequence of numbers that relate to one another in a meaningful way.&lt;/p&gt;



&lt;blockquote class=&quot;wp-block-quote is-layout-flow wp-block-quote-is-layout-flow&quot;&gt;
&lt;p&gt;A modular scale, like a musical scale, is a prearranged set of harmonious proportions.&lt;/p&gt;
&lt;cite&gt;Robert Bringhurst&lt;/cite&gt;&lt;/blockquote&gt;



&lt;p&gt;You start with a&amp;nbsp;&lt;strong&gt;ratio&lt;/strong&gt;&amp;nbsp;and a&amp;nbsp;&lt;strong&gt;number&lt;/strong&gt;. Like Vertical Rhythm, the best way to ensure the effectiveness of your starting number is to use the body copy. Since this is setting the scale, we want to use its font size rather than its line height. Continuing with the same example from before, we&amp;#8217;d use 15px as our base number. The ratio is more difficult to find, most common are the&amp;nbsp;&lt;strong&gt;Golden Ratio&lt;/strong&gt;&amp;nbsp;(1.618) and&amp;nbsp;&lt;strong&gt;Perfect Fourth&lt;/strong&gt;&amp;nbsp;(1.333). Both are culturally relevant; the former being a favorite in classical Greek and Renaissance math, architecture and art, and also found in nature; the latter was popular in early 20th-century music. One good rule of thumb is to consider how much typographical and layout variety you need. If you require steps for all six headers you&amp;#8217;ll likely want to choose a ratio that allows for more subtlety between steps.&lt;/p&gt;



&lt;p&gt;On the web, using a modular scale means choosing numbers from the scale for type sizes, line heights, line length (the width of a line of copy), margins, column width, and more.&lt;/p&gt;



&lt;p&gt;With a ratio and number chosen, you can then multiply and divide them to get many resonant numbers. Below is a list of resonant numbers we&amp;#8217;d get from using the 15px base and the &amp;#8220;golden ratio&amp;#8221; of 1:1.618 for the multiplication/division, I&amp;#8217;m using the super helpful&amp;nbsp;&lt;a href=&quot;https://www.modularscale.com/&quot;&gt;modularscale.com&lt;/a&gt;&amp;nbsp;site to do these calculations for me and show me visually what the resulting sequence looks like.&lt;/p&gt;



&lt;figure class=&quot;wp-block-image size-large&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1024&quot; height=&quot;787&quot; src=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/10/ms-golden-ratio-1024x787.png&quot; alt=&quot;&quot; class=&quot;wp-image-64&quot; srcset=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/10/ms-golden-ratio-1024x787.png 1024w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/10/ms-golden-ratio-300x230.png 300w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/10/ms-golden-ratio-768x590.png 768w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/10/ms-golden-ratio.png 1312w&quot; sizes=&quot;auto, (max-width: 1024px) 100vw, 1024px&quot; /&gt;&lt;figcaption class=&quot;wp-element-caption&quot;&gt;An example of modular scale applied with the Golden Ratio from modularscale.com&lt;/figcaption&gt;&lt;/figure&gt;



&lt;p&gt;What this did was take the starting number of&amp;nbsp;&lt;strong&gt;15&lt;/strong&gt;&amp;nbsp;and&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;Multiply it by the golden ratio of 1.618 to get&amp;nbsp;&lt;strong&gt;24.27&lt;/strong&gt;&lt;/li&gt;



&lt;li&gt;Then it multiplied that by 1.618 to get&amp;nbsp;&lt;strong&gt;39.269&lt;/strong&gt;, and so on&lt;/li&gt;



&lt;li&gt;It also divided 15 by 1.618 to get&amp;nbsp;&lt;strong&gt;9.271&lt;/strong&gt;&lt;/li&gt;



&lt;li&gt;Then divided that by 1.618 to get&amp;nbsp;&lt;strong&gt;5.73&lt;/strong&gt;&amp;nbsp;and so on&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;For comparison, below is the same 15px base and the &amp;#8220;Perfect Fourth&amp;#8221; ratio of 1 to 1.333 mentioned previously. As you can see, this ratio has mor subtlety between steps in the new set of calculations. Because the ratio is smaller, the steps between scale are closer together and more subtle.&lt;/p&gt;



&lt;figure class=&quot;wp-block-image size-large&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1024&quot; height=&quot;787&quot; src=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/10/ms-perfect-fourth-1024x787.png&quot; alt=&quot;&quot; class=&quot;wp-image-65&quot; srcset=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/10/ms-perfect-fourth-1024x787.png 1024w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/10/ms-perfect-fourth-300x230.png 300w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/10/ms-perfect-fourth-768x590.png 768w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/10/ms-perfect-fourth.png 1312w&quot; sizes=&quot;auto, (max-width: 1024px) 100vw, 1024px&quot; /&gt;&lt;figcaption class=&quot;wp-element-caption&quot;&gt;An example of modular scale applied with the Perfect Fourth Ratio&lt;/figcaption&gt;&lt;/figure&gt;



&lt;p&gt;You also have the option of defining a second base number and ratio to generate what&amp;#8217;s called a double-stranded modular scale. This combines two sets of resonant numbers to give you more measurement options that fill in the gaps of the first scale. For the second base number, I&amp;#8217;ve seen people use smaller caption text and larger numbers like the width of a column in the 12-column grid they&amp;#8217;re implementing. So long as it&amp;#8217;s an &amp;#8220;important&amp;#8221; number to the layout, there is no wrong answer. Here&amp;#8217;s what adding a scond base of 95px (the size of one column in a 1140px wide layout) would look like.&lt;/p&gt;



&lt;figure class=&quot;wp-block-image size-large&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1024&quot; height=&quot;787&quot; src=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/10/Screen-Shot-2017-07-29-at-9.02.09-PM-1024x787.png&quot; alt=&quot;&quot; class=&quot;wp-image-66&quot; srcset=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/10/Screen-Shot-2017-07-29-at-9.02.09-PM-1024x787.png 1024w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/10/Screen-Shot-2017-07-29-at-9.02.09-PM-300x230.png 300w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/10/Screen-Shot-2017-07-29-at-9.02.09-PM-768x590.png 768w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/10/Screen-Shot-2017-07-29-at-9.02.09-PM.png 1312w&quot; sizes=&quot;auto, (max-width: 1024px) 100vw, 1024px&quot; /&gt;&lt;figcaption class=&quot;wp-element-caption&quot;&gt;An example of modular scale applied with the Golden Ratio and two base numbers&lt;/figcaption&gt;&lt;/figure&gt;



&lt;p&gt;Great! Now we have a lot more options to work with!&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot; id=&quot;history-lesson&quot;&gt;History Lesson&lt;/h2&gt;



&lt;aside class=&quot;wp-block-create-block-aside&quot;&gt;
&lt;figure class=&quot;wp-block-image size-large&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;890&quot; height=&quot;1024&quot; src=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/10/sort-890x1024.png&quot; alt=&quot;&quot; class=&quot;wp-image-67&quot; srcset=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/10/sort-890x1024.png 890w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/10/sort-261x300.png 261w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/10/sort-768x884.png 768w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/10/sort-1334x1536.png 1334w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/10/sort-1779x2048.png 1779w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/10/sort.png 2000w&quot; sizes=&quot;auto, (max-width: 890px) 100vw, 890px&quot; /&gt;&lt;figcaption class=&quot;wp-element-caption&quot;&gt;A metal sort illustration&lt;/figcaption&gt;&lt;/figure&gt;



&lt;figure class=&quot;wp-block-image size-large&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1024&quot; height=&quot;675&quot; src=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/10/caslon-1024x675.jpg&quot; alt=&quot;&quot; class=&quot;wp-image-62&quot; srcset=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/10/caslon-1024x675.jpg 1024w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/10/caslon-300x198.jpg 300w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/10/caslon-768x506.jpg 768w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/10/caslon-1536x1012.jpg 1536w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/10/caslon-2048x1350.jpg 2048w&quot; sizes=&quot;auto, (max-width: 1024px) 100vw, 1024px&quot; /&gt;&lt;figcaption class=&quot;wp-element-caption&quot;&gt;A type case, where metal sorts were stored in small compartments of drawers&lt;/figcaption&gt;&lt;/figure&gt;
&lt;/aside&gt;



&lt;p&gt;The origin of establishing a typographic scale came as much from practical need as aesthetic judgement. Back when printing was done via letterpress, movable type was composed by hand for each page using cast metal sorts (upper right). It was impractical to manufacture and store whole character sets of each typeface in all possible sizes. Over time, typesetters settled on a usable range of sizes that worked harmoniously together.&lt;/p&gt;



&lt;p&gt;&lt;strong&gt;Bonus&lt;/strong&gt;&amp;nbsp;&amp;#8211; letters were organized and stored in small compartments of drawers (lower right) in a type case. Capital letters were traditionally stored in a separate drawer, or case placed above the case holding the other letters. This is the origin of the terms &amp;#8220;upper case&amp;#8221; and &amp;#8220;lower case.&amp;#8221;&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot; id=&quot;similarities-and-differences&quot;&gt;Similarities and Differences&lt;/h2&gt;



&lt;p&gt;Now let&amp;#8217;s quickly go over some similarities and differences between Vertical Rhythm and Modular Scale&lt;/p&gt;



&lt;p&gt;As mentioned at the beginning, this article includes both vertical rhythm and modular scale because both are interrelated and contribute to the visual harmony of a page. Both vertical rhythm and modular scale most often use the primary body copy for their starting base numbers.&lt;/p&gt;



&lt;p&gt;Where they differ most is in their application &amp;#8211; vertical rhythm is only concerned with typography where modular scale provides a set of resonant numbers to use for typography, columns, line lengths, etc. Another key difference is how strict they&amp;#8217;re intended to be followed. With vertical rhythm the more consistently you follow the pattern the better the rhythm will be. On the other hand, modular scale is more of a tool that can be improvised as necessary. The quote below is in reference to the math involved with Modular Scale.&lt;/p&gt;



&lt;blockquote class=&quot;wp-block-quote is-layout-flow wp-block-quote-is-layout-flow&quot;&gt;
&lt;p&gt;Math is no substitute for an experienced designer&amp;#8217;s eye, but it can provide both hints and constraints for decision making.&lt;/p&gt;
&lt;cite&gt;Tim Brown&lt;/cite&gt;&lt;/blockquote&gt;



&lt;h2 class=&quot;wp-block-heading&quot; id=&quot;integration&quot;&gt;Integration&lt;/h2&gt;



&lt;p&gt;Hopefully, by now I&amp;#8217;ve convinced you that these methodologies are beneficial and worth your time to implement because I&amp;#8217;ve got two bits of bad news.&lt;/p&gt;



&lt;p&gt;First, a pervasive assumption is, &amp;#8220;I can plug a couple of numbers into a generator and it all just works!&amp;#8221; While there are online tools that help with a lot of the math and guesswork,there&amp;#8217;s still going to be some trial and error. Every project is different, so&lt;br /&gt;a perfectly viable solution for one isn&amp;#8217;t necessarily going to fit for another.&lt;/p&gt;



&lt;p&gt;Second, all materials I&amp;#8217;ve found online related to these topics have come from the design or design-dev mindset where the person determining the modular scale and vertical rhythm is the one ultimately in charge of making those typographic decisions. In our case more often than not we&amp;#8217;re handed designs that need to be converted from Sketch/PSD to web.&lt;/p&gt;



&lt;p&gt;But there&amp;#8217;s hope! On my most recent projects, I&amp;#8217;ve successfully taken what was provided by a design team and was able to work them into a rhythm and scale that fit the guidelines mentioned thus far while also maintaining the integrity of the design. The trick is in getting the calculations as close as possible and setting up an example page that clearly illustrates the benefits before reaching out to the design team for the OK to move forward with the changes. In my experience, they&amp;#8217;ve been even bigger type nerds than me and appreciated the extra effort involved.&lt;/p&gt;

    </content>
	</entry>
  
	<entry>
		<title>Soft Skills: Improving Your Emotional Intelligence</title>
		<link href="https://stevenwoodson.com/blog/soft-skills-improving-your-emotional-intelligence"/>
		<published>2017-12-04T11:21:00.000Z</published>
		<updated>2023-08-10T16:38:28.000Z</updated>
		<id>https://stevenwoodson.com/blog/soft-skills-improving-your-emotional-intelligence</id>
    <summary>The term &amp;#8220;Soft Skills&amp;#8221; covers a broad spectrum of topics in interpersonal, communication, leadership, and personal skills. These are arguably the most important skills for your career, but by definition, they are also difficult to measure.</summary>
		<content type="html">&lt;img src=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2017/12/emotional-intelligence.jpg&quot; alt=&quot;&quot;&gt;
&lt;p&gt;Who doesn&amp;#8217;t want to make a difference in other people’s lives while also maintaining productivity? Proven soft skill techniques can make this seemingly impossible feat a reality! The term &amp;#8220;Soft Skills&amp;#8221; covers a broad spectrum of topics in interpersonal, communication, leadership, and personal skills. These are arguably the most important skills for your career, but by definition, they are also difficult to measure. This is the first in a series of articles meant to introduce you to some soft skill topics, providing some background information and practical advice on how to incorporate it into your life in measurable ways. In this article we&amp;#8217;ll focus on introducing Emotional Intelligence (EI) before diving into the what, why, and how of perceiving, using, and managing emotion to improve your EI.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot; id=&quot;an-introduction&quot;&gt;An Introduction&lt;/h2&gt;



&lt;p&gt;EI is a relatively new field of study (scientific articles on EI first began to appear in the early &amp;#8217;90s) that focuses on reasoning about emotions and the use of emotions to enhance thought. A model of EI pioneered by Dr. Daniel Goleman often referred to as the &amp;#8220;Mixed Model&amp;#8221; breaks EI down into the following five traits:&lt;/p&gt;



&lt;ol class=&quot;global-margin-vertical&quot;&gt;
  &lt;li&gt;
    &lt;img src=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2017/12/self_awareness.png%22&quot; alt=&quot;&quot; class=&quot;pull-right&quot; /&gt;
    &lt;h3&gt;Self Awareness&lt;/h3&gt;
    &lt;p&gt;Knowing what you&amp;#8217;re feeling and using your feelings as a guide.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;img decoding=&quot;async&quot; src=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2017/12/self_management.png&quot; alt=&quot;&quot; class=&quot;pull-right&quot; /&gt;
    &lt;h3&gt;Self Management&lt;/h3&gt;
    &lt;p&gt;Handling distressing emotions in effective ways, marshaling positive emotions, and aligning our actions with our passions.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;img decoding=&quot;async&quot; src=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2017/12/empathy.png&quot; alt=&quot;&quot; class=&quot;pull-right&quot; /&gt;
    &lt;h3&gt;Empathy&lt;/h3&gt;
    &lt;p&gt;Discerning how someone else is feeling without them telling you in words.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;img decoding=&quot;async&quot; src=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2017/12/social_skill.png&quot; alt=&quot;&quot; class=&quot;pull-right&quot; /&gt;
    &lt;h3&gt;Social Skill&lt;/h3&gt;
    &lt;p&gt;Handling relationships and conflict, and being the type of person people enjoy being around.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;img decoding=&quot;async&quot; src=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2017/12/motivation.png&quot; alt=&quot;&quot; class=&quot;pull-right&quot; /&gt;
    &lt;h3&gt;Motivation&lt;/h3&gt;
    &lt;p&gt;The drive to work towards your goals despite setbacks.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;



&lt;p&gt;Goleman developed this model while covering neuroscience and psychology for the&amp;nbsp;&lt;em&gt;New York Times&lt;/em&gt;, based on the discovery that the emotional centers of the brain (a more primitive part that rose earlier in evolution), are involved in every thought and every decision.&lt;/p&gt;



&lt;blockquote class=&quot;wp-block-quote is-layout-flow wp-block-quote-is-layout-flow&quot;&gt;
&lt;p&gt;I saw there was a critical mass of new understanding of how our brain is designed and what that means for understanding our emotional lives and the relationship to cognition, to thought.&lt;/p&gt;
&lt;/blockquote&gt;



&lt;p&gt;Continuing his research, Goleman was able to interview and analyze over 500 corporations in search of what they&amp;#8217;re looking for in different skill sets. He found that emotional intelligence was twice as important as IQ plus technical skill combined in distinguishing star performers from the average. In other words, learning how to perceive, use, and manage emotions helps develop a greater understanding of yourself and those around you. Using this as a foundation, we can focus on the what, why, and how of perceiving, using, and managing emotion.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot; id=&quot;perceiving-emotions&quot;&gt;Perceiving Emotions&lt;/h2&gt;



&lt;blockquote class=&quot;wp-block-quote is-layout-flow wp-block-quote-is-layout-flow&quot;&gt;
&lt;p&gt;For real influence we need to go from our here to their there.&lt;/p&gt;
&lt;cite&gt;How to Really Understand Someone Else’s Point of View&lt;/cite&gt;&lt;/blockquote&gt;



&lt;p&gt;A big part of emotional perception is empathy, which itself is split into two major components. Emotional empathy is when someone’s emotion causes you similar feelings and memories. Cognitive empathy is the capacity to understand someone’s emotion and acknowledging that their reasoning and emotions as valid even if they differ from your own. We’re going to focus on the latter, on adopting an empathetic mindset that can be applied with the people you work with and the projects on which you work.&lt;/p&gt;



&lt;blockquote class=&quot;wp-block-quote is-layout-flow wp-block-quote-is-layout-flow&quot;&gt;
&lt;p&gt;At first, most people seem to think that empathy is about showing warmth and kindness, or at least tolerance, toward another person. People think empathy is &amp;#8220;to walk in someone else’s shoes,&amp;#8221; to put themselves in that person’s place and embrace or excuse his behavior. This is not what empathy is about. Not exactly. Empathy is a noun—a thing. Empathy is an understanding you develop about another person. Empathizing is the use of that under-standing—an action.&lt;/p&gt;
&lt;cite&gt;Practical Empathy&lt;/cite&gt;&lt;/blockquote&gt;



&lt;h3 class=&quot;wp-block-heading&quot; id=&quot;why%3F&quot;&gt;Why?&lt;/h3&gt;



&lt;p&gt;Stereotypically, developers are portrayed as apathetic to the needs of the end user, but it has been my experience that this perception is at least partially due to circumstance. &amp;#8220;You don’t know what you don’t know,&amp;#8221; as the saying goes. If we’re not aware of the difficulties that users have with the applications we build, it’s not apathy, but rather lack of information that we fall prey to. In truth, web development lends itself very well to the empathetic. Since our jobs are centered around building things for others, empathy for these users can only make us better.&lt;/p&gt;



&lt;p&gt;Remember that we&amp;#8217;re talking about cognitive empathy rather than emotional: We&amp;#8217;re trying to understand our users and use that understanding to improve what we&amp;#8217;re building for them. Imagine developing a feature that you ensured remained accessible to those with disabilities, or a particularly timely and helpful user experience for a focus group. These could be the reason an end user sticks with your solutions over the competition&amp;#8217;s. The understanding we gain from an empathetic mindset leads to a desire to help and makes the act of helping that much more rewarding. So developing an empathetic mindset isn’t just the right thing to do, it enhances your strategic thinking, your creative process, and transforms your ability to work together with others.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot; id=&quot;how%3F&quot;&gt;How?&lt;/h3&gt;



&lt;p&gt;Empathy is built by dropping into a certain mindset when the opportunity arises to gather knowledge. If someone you want to understand has the time, you take it. You drop into a neutral frame of mind, try to discover the deeper reasons behind what they are saying, and shut down your own thinking and emotions.&lt;/p&gt;



&lt;p&gt;Empathy is not always about revelation, it consists of stuff you already know, but possibly didn’t pay a lot of attention to or maybe misremembered. The best part about listening is that you don’t have to be &amp;#8220;a good facilitator&amp;#8221; or a &amp;#8220;skilled interviewer&amp;#8221; to develop empathy. It’s more about just being yourself, in curiosity mode. Kids can do it. What it takes is the ability to let go of your own thoughts and really absorb what you hear.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot; id=&quot;using-emotions&quot;&gt;Using Emotions&lt;/h2&gt;



&lt;blockquote class=&quot;wp-block-quote is-layout-flow wp-block-quote-is-layout-flow&quot;&gt;
&lt;p&gt;Happiness is a conscious choice, not an automatic response.&lt;/p&gt;
&lt;cite&gt;Mildred Barthel&lt;/cite&gt;&lt;/blockquote&gt;



&lt;p&gt;First and foremost, you can’t go into this process with the intent of understanding someone else only to induce a change in their beliefs or behavior. The application of empathy that has impact on your work is to use it in support of someone. Empathy in support is being willing to acknowledge another person’s intent and work with it, morphing your own intent because of the empathy you developed for the other person.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot; id=&quot;why%3F-2&quot;&gt;Why?&lt;/h3&gt;



&lt;p&gt;Your improved understanding changes the way you see things and the way you speak to people. It raises your awareness and subconsciously shifts your own thinking and expanding your horizon.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot; id=&quot;how%3F-2&quot;&gt;How?&lt;/h3&gt;



&lt;p&gt;Here’s a list of ideas to keep in mind, because everyone loves a good short list!&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;Assume best intentions from those around you. If your understanding is negative, ask for clarification.&lt;/li&gt;



&lt;li&gt;Show appreciation for even the small things people do for you.&lt;/li&gt;



&lt;li&gt;Compliment people on their efforts: It’s easy to lose sight of the hard work involved.&lt;/li&gt;



&lt;li&gt;Give of your time freely and be a resource.&lt;/li&gt;



&lt;li&gt;When you feel the urge to respond with &amp;#8220;well, actually,&amp;#8221; first try to perceive the situation from their perspective. Force yourself to think &amp;#8220;well, maybe&amp;#8230;&amp;#8221; instead. You may be surprised with how you finish that sentence.&lt;/li&gt;



&lt;li&gt;Inject humor into your day-to-day interactions, within reason. Humor is associated with close interpersonal relationships and effective in reducing stress, which in turn enhances empathy.&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;Providing code review feedback is a good opportunity to effectively use emotion. An empathetic mindset along with the goodwill you’ve built using the suggestions thus far will help you adjust what you say and how you collaborate. Here&amp;#8217;s another list of ideas to consider when providing feedback:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;Take your time and ensure you understand the context of the code that’s being reviewed. You can’t provide meaningful feedback to code without proper context.&lt;/li&gt;



&lt;li&gt;Provide positive feedback, too. No one benefits from only actionable and potentially negative review feedback.&lt;/li&gt;



&lt;li&gt;Ask questions and be careful about suggesting specific solutions. Often your suggestions are taken at face value and implemented without further discussion, missing a potential learning opportunity and a better solution for both parties.&lt;/li&gt;



&lt;li&gt;Be pragmatic. It’s okay to change your mindset based on the circumstances. For example, reviewing a time-sensitive small hotfix versus a brand new feature with far-reaching implications.&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;For more tips from the reviewee’s perspective, I highly recommend reading Brett Birschbach’s blog post &lt;a href=&quot;https://www.bounteous.com/insights/2017/05/11/mentality-drives-technical-competency/&quot; rel=&quot;nofollow&quot;&gt;Mentality Drives Technical Competency&lt;/a&gt;.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot; id=&quot;managing-emotions&quot;&gt;Managing Emotions&lt;/h3&gt;



&lt;p&gt;I’d like to end this article with some thoughts on managing emotions, both from a personal and an interpersonal perspective. I think Stephanie Noble had a great analogy for personal emotions when she described them as &amp;#8220;Honored Guests&amp;#8221; in your home. Identify the emotion, but don’t identify with the emotion. Explore this emotion-guest with loving curiosity. Allow it to come and go. Neither cling to it nor hold it hostage.&lt;/p&gt;



&lt;blockquote class=&quot;wp-block-quote is-layout-flow wp-block-quote-is-layout-flow&quot;&gt;
&lt;p&gt;Always honor your guest, but if its company makes you uncomfortable, you can develop techniques to shorten the visits. Your guest has certain needs as well as certain information for you. The quicker you get to the needs/information exchange the less time you have to spend with an emotion. The more time you spend ‘suffering’ its company, the longer it will be before it leaves. So, cut to the chase.&lt;/p&gt;
&lt;cite&gt;Emotions as Honored Guests&lt;/cite&gt;&lt;/blockquote&gt;



&lt;p&gt;Reactions during a knowledge-gathering, neutral-listening session are normal. Don’t feel embarrassed that you actually felt the emotion for a while first. You can’t prevent emotions from occurring, but you can recognize and then ignore them. When you have a reaction during a listening session, you don’t want to betray the fact to the speaker, because you do not want to cause them to have a reaction of their own, or let your reaction influence what they are telling you.&lt;/p&gt;



&lt;blockquote class=&quot;wp-block-quote is-layout-flow wp-block-quote-is-layout-flow&quot;&gt;
&lt;p&gt;Recognizing your own reactions and assumptions—before they distract you—leaves your mind open to considering foreign possibilities. You will be able to recognize when you condemn an idea or a reaction, be able to stop yourself, and then be able to spend some time investigating it instead.&lt;/p&gt;
&lt;cite&gt;Practical Empathy&lt;/cite&gt;&lt;/blockquote&gt;



&lt;h2 class=&quot;wp-block-heading&quot; id=&quot;resources&quot;&gt;Resources&lt;/h2&gt;



&lt;h3 class=&quot;wp-block-heading&quot; id=&quot;books&quot;&gt;Books&lt;/h3&gt;



&lt;p&gt;If you&amp;#8217;re looking to add to your reading list, I recommend the following books:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://abookapart.com/products/design-for-real-life&quot; rel=&quot;nofollow&quot;&gt;Design for Real Life&lt;/a&gt; by Sara Wachter-Boettcher and Eric Meyer&lt;/li&gt;



&lt;li&gt;&lt;a href=&quot;https://www.talentsmart.com/products/emotional-intelligence-2.0/&quot; rel=&quot;nofollow&quot;&gt;Emotional Intelligence 2.0&lt;/a&gt; by Travis Bradberry &amp;amp; Jean Greaves&lt;/li&gt;



&lt;li&gt;&lt;a href=&quot;https://read.macmillan.com/lp/losing-our-minds/&quot; rel=&quot;nofollow&quot;&gt;Losing Our Minds&lt;/a&gt; by Lucy Foulkes, PhD&lt;/li&gt;



&lt;li&gt;&lt;a href=&quot;https://rosenfeldmedia.com/books/practical-empathy/&quot; rel=&quot;nofollow&quot;&gt;Practical Empathy&lt;/a&gt; by Indi Young&lt;/li&gt;



&lt;li&gt;&lt;a href=&quot;https://susancain.net/book/quiet/&quot; rel=&quot;nofollow&quot;&gt;Quiet: The Power of Introverts in a World That Can&amp;#8217;t Stop Talking&lt;/a&gt; by Susan Cain&lt;/li&gt;
&lt;/ul&gt;



&lt;h3 class=&quot;wp-block-heading&quot; id=&quot;articles&quot;&gt;Articles&lt;/h3&gt;



&lt;p&gt;For the web-friendly, bookmarkable article, here are a few options:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://www.bounteous.com/insights/2017/05/11/mentality-drives-technical-competency&quot; rel=&quot;nofollow&quot;&gt;Mentality Drives Technical Competency&lt;/a&gt; by Brett Birschbach&lt;/li&gt;



&lt;li&gt;&lt;a href=&quot;https://www.researchgate.net/publication/5907081_Human_Abilities_Emotional_Intelligence&quot; rel=&quot;nofollow&quot;&gt;Human Abilities: Emotional Intelligence&lt;/a&gt;&lt;/li&gt;



&lt;li&gt;&lt;a href=&quot;https://hbr.org/2013/04/how-to-really-understand-someo&quot; rel=&quot;nofollow&quot;&gt;How to Really Understand Someone Else’s Point of View&lt;/a&gt; by Mark Goulston and John Ullmen&lt;/li&gt;



&lt;li&gt;&lt;a href=&quot;https://mobile.nytimes.com/2013/03/31/magazine/is-giving-the-secret-to-getting-ahead.html&quot; rel=&quot;nofollow&quot;&gt;Is Giving the Secret to Getting Ahead?&lt;/a&gt; by Art Streiber&lt;/li&gt;



&lt;li&gt;&lt;a href=&quot;https://medium.com/@mrjoelkemp/giving-better-code-reviews-16109e0fdd36&quot; rel=&quot;nofollow&quot;&gt;Giving Better Code Reviews&lt;/a&gt; by Joel Kemp&lt;/li&gt;



&lt;li&gt;&lt;a href=&quot;https://medium.com/@ericajoy/you-are-valued-988198481c17&quot; rel=&quot;nofollow&quot;&gt;You are Valued&lt;/a&gt; by Erica Joy&lt;/li&gt;



&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=Y7m9eNoB3NU&quot; rel=&quot;nofollow&quot;&gt;Daniel Goleman Explains Emotional Intelligence&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

    </content>
	</entry>
  
	<entry>
		<title>The Four Levels of Mobile Browser Testing</title>
		<link href="https://stevenwoodson.com/blog/four-levels-mobile-browser-testing"/>
		<published>2017-03-30T11:19:00.000Z</published>
		<updated>2023-08-10T16:51:03.000Z</updated>
		<id>https://stevenwoodson.com/blog/four-levels-mobile-browser-testing</id>
    <summary>Tips to help you embrace the budget and time constraints of your project while testing in mobile browsers.</summary>
		<content type="html">
&lt;p&gt;If there&amp;#8217;s one item that doesn&amp;#8217;t change from project to project, it&amp;#8217;s timelines and budgets. So how do we manage the growing complexity of development and testing within the constraints of time and budget? In this article, I&amp;#8217;m going to share some tips that will help you embrace the constraints of your projects while testing in mobile browsers.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot; id=&quot;four-questions&quot;&gt;Four Questions&lt;/h2&gt;



&lt;p&gt;We have sophisticated build tools, automated testing, design systems, and an unending onslaught of acronym-named projects at our disposal, but there&amp;#8217;s no replacing manual testing. Planning out your testing strategy is crucial. But first, you need a general understanding of the project. Ask yourself:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;strong&gt;What&amp;#8217;s the lifecycle?&lt;/strong&gt;&lt;br /&gt;Is this going to be a one-off promotional project that will disappear in a couple months? An evergreen application critical to business success?&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;What&amp;#8217;s the scale and complexity?&lt;/strong&gt;&lt;br /&gt;A small promotional site or a large multi-site system? It&amp;#8217;s also a good idea to factor in the inherent complexity of a CMS if one will be used.&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;Who is your user?&lt;/strong&gt;&lt;br /&gt;Do you have any analytics or insight into your audience and browsing behavior? Ideally, what are your priority devices?&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;What&amp;#8217;s the timeline and budget?&lt;/strong&gt;&lt;br /&gt;This sets the clearest constraints that we need to work within.&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;Depending on the answers to the questions above, I concede that compromises may be necessary, but you will still need to do some mobile browser testing. There&amp;#8217;s no timeline short enough, no site simple enough, and no budget small enough to completely cut testing out of the process altogether. So let&amp;#8217;s talk about the four levels of mobile browser testing and how each can fit into your workflow.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot; id=&quot;four-levels&quot;&gt;Four Levels&lt;/h2&gt;



&lt;h3 class=&quot;wp-block-heading&quot; id=&quot;level-1%3A-resizing-a-standard-desktop-browser&quot;&gt;Level 1: Resizing a standard desktop browser&lt;/h3&gt;



&lt;p&gt;&lt;strong&gt;Trust me. In theory, it should work.&lt;/strong&gt;&amp;nbsp;Resizing a desktop browser down to mobile sizes is useful as a real-time sanity check during initial development. When you&amp;#8217;re still working out your breakpoints and how page components will react to different screen sizes, it&amp;#8217;s nice to see how it stacks across them all.&lt;/p&gt;



&lt;p&gt;&lt;strong&gt;Pros&lt;/strong&gt;&amp;nbsp;&amp;#8211; Quickest and easiest, readily available all the time.&lt;/p&gt;



&lt;p&gt;&lt;strong&gt;Cons&lt;/strong&gt;&amp;nbsp;&amp;#8211; Not indicative of the mobile user experience, browser bugs, or touch events.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot; id=&quot;level-2%3A-desktop-browser-mobile-emulators&quot;&gt;Level 2: Desktop browser mobile emulators&lt;/h3&gt;



&lt;p&gt;&lt;strong&gt;Trust others; they say it should work&lt;/strong&gt;. Chrome&amp;#8217;s Dev Tools Device Toolbar and Firefox&amp;#8217;s Responsive Design Mode are great options here, useful for deeper dives when special consideration is needed for mobile features like touch events. They also have advanced options to throttle connection speeds and simulate mobile device sensors like Geolocation and Device orientation.&lt;/p&gt;



&lt;p&gt;&lt;strong&gt;Pros&lt;/strong&gt;&amp;nbsp;&amp;#8211; Great for initial checks on basic mobile functionality, constraining to more realistic device screen sizes.&lt;/p&gt;



&lt;p&gt;&lt;strong&gt;Cons&lt;/strong&gt;&amp;nbsp;&amp;#8211; While better than simple browser resizing, this method still fails to reveal browser/device bugs and overall performance issues.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot; id=&quot;level-3%3A-ios%2Fandroid-simulator-with-stock-browsers&quot;&gt;Level 3: iOS/Android simulator with stock browsers&lt;/h3&gt;



&lt;p&gt;&lt;strong&gt;Trust me (really this time).&lt;/strong&gt;&amp;nbsp;With actual device simulators, you&amp;#8217;re able to test in a full mobile device environment in the native stock browser. Useful for identifying real mobile browser bugs and for getting a better sense for the page layout within the chrome of the device. Unfortunately, the list of browsers available is limited because you can&amp;#8217;t install apps as you would normally on a physical device. On Android, you have access to the Android Browser, the Chromium Content Shell and Firefox. On iOS, you really only have access to mobile Safari. Special shout out to cloud-based emulator providers like BrowserStack and SauceLabs that offer further options that are not available or practical through OOTB simulators.&lt;/p&gt;



&lt;p&gt;&lt;strong&gt;Pros&lt;/strong&gt;&amp;nbsp;&amp;#8211; Using the actual mobile browser and surrounding chrome, better for finding layout bugs.&lt;/p&gt;



&lt;p&gt;&lt;strong&gt;Cons&lt;/strong&gt;&amp;nbsp;&amp;#8211; Limited browsers available for use in emulators, often takes a lot of system resources so not realistic to leave open all day, still not indicative of performance and touch optimization.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot; id=&quot;level-4%3A-actual-devices&quot;&gt;Level 4: Actual devices&lt;/h3&gt;



&lt;p&gt;&lt;strong&gt;The real deal.&lt;/strong&gt;&amp;nbsp;There is no comparison to testing on a physical device in your hands, it&amp;#8217;s the only way to test mobile-specific details like touch target sizes and placement of controls. The time needed to test on all the mobile devices you have access to will increase as you continue to add devices. Luckily there are tools like GhostLab and Browser-Sync that can help speed up simultaneous testing on all devices.&lt;/p&gt;



&lt;p&gt;&lt;strong&gt;Pros&lt;/strong&gt;&amp;nbsp;&amp;#8211; Real performance and user experience of your project on a mobile device&lt;/p&gt;



&lt;p&gt;&lt;strong&gt;Cons&lt;/strong&gt;&amp;nbsp;&amp;#8211; A queue of devices that cover a reasonable subset of the mobile landscape can be expensive and time to set-up and test is higher.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot; id=&quot;wrapping-up&quot;&gt;Wrapping Up&lt;/h2&gt;



&lt;p&gt;Mobile browser testing is a delicate balance of the ideal test-everything-everywhere and the practicality of limited time and budget. Frame it in the scope of your project and use the strengths of the various levels defined above to your advantage.&lt;/p&gt;

    </content>
	</entry>
  
	<entry>
		<title>Managing Multi-Tenant Component Styles</title>
		<link href="https://stevenwoodson.com/blog/managing-multi-tenant-component-styles"/>
		<published>2016-06-22T11:07:00.000Z</published>
		<updated>2023-08-10T17:27:08.000Z</updated>
		<id>https://stevenwoodson.com/blog/managing-multi-tenant-component-styles</id>
    <summary>Tracing the path we took to accomplish a unique, cohesive design system for multiple sites within a single Content Management System (CMS) platform installation.</summary>
		<content type="html">&lt;img src=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/10/hero.jpg&quot; alt=&quot;&quot;&gt;
&lt;p&gt;In this article, we&amp;#8217;re going to trace the path we took to accomplish a unique, cohesive design system for multiple sites within a single Content Management System (CMS) platform installation. While some of the details in the&amp;nbsp;&lt;em&gt;Workflow&lt;/em&gt;&amp;nbsp;section are specific to the CMS platform we&amp;#8217;re using (Adobe Experience Manager or AEM), the concepts are platform agnostic. Along with defining multitenancy and its structure, we&amp;#8217;ll explore the importance of a dynamic style guide, the necessity of a theming tool for better collaboration, and the efficiencies we achieved by introducing both into our workflow.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot; id=&quot;multitenancy&quot;&gt;Multitenancy&lt;/h2&gt;



&lt;p&gt;Multitenancy is a software architecture in which a single instance of a software application serves multiple tenants. As an analogy, that single instance is like an apartment building: Each &amp;#8216;tenant&amp;#8217; in that building shares common infrastructure like electricity and gas, but are otherwise their own unique entity. From a CMS perspective, the software application, AEM in our case, powers multiple tenant websites that share components and image assets while each has its own structure, content, and assets.&lt;/p&gt;



&lt;p&gt;The more that needs to be shared between sites, the more ideal it becomes to utilize a multi-tenant system. This shared structure allows us to rapidly create new feature-rich tenant sites, reinforces a common nomenclature across all teams, and reduces development effort.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot; id=&quot;benefits-and-pitfalls&quot;&gt;Benefits and Pitfalls&lt;/h3&gt;



&lt;p&gt;There are several practical benefits to a multi-tenant system (including cost savings from a shared infrastructure), but let&amp;#8217;s primarily focus on the design and development benefits. Designing for the responsive web is extremely difficult because there are no guarantees to the size and capability of the browser being used. This difficulty is amplified in a CMS platform because the content itself can no longer be guaranteed. Breaking content down into logical components shared between all tenants creates a common nomenclature shared between design and development. It&amp;#8217;s easier to think and design in terms of the components that make up a page than trying to consider all possibilities at once. From a development perspective, building a component once and having that functionality available to all tenants is a powerful abstraction.&lt;/p&gt;



&lt;p&gt;There are, however, inherent complexities involved when trying to design and build for a multi-tenant CMS. Developing a component that&amp;#8217;s configurable enough and with a layout malleable enough to be useful across all tenants, present and future, is a tall order. The value of shared components is reduced if they need to be completely restyled for each tenant, so if efficiencies are to be gained in the design process, layout constraints must be set and standardized. The next two sections detail tools we&amp;#8217;ve used to mitigate these complexities.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot; id=&quot;dynamic-style-guide&quot;&gt;Dynamic Style Guide&lt;/h2&gt;



&lt;p&gt;A style guide is basically a table of contents for all the elements and components that make up a website, consolidates all the available options, and documents the visual language.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot; id=&quot;why-do-we-need-one%3F&quot;&gt;Why do we need one?&lt;/h3&gt;



&lt;p&gt;Having a style guide:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;Ensures brand and code consistency;&lt;/li&gt;



&lt;li&gt;Creates a reference that informs future additions and enhancements;&lt;/li&gt;



&lt;li&gt;Helps with code reviews and QA;&lt;/li&gt;



&lt;li&gt;Helps identify where styling attention is needed&amp;nbsp;&lt;em&gt;before&lt;/em&gt;&amp;nbsp;it&amp;#8217;s needed;&lt;/li&gt;



&lt;li&gt;Reinforces a system of reusable modular components independent of pages.&lt;/li&gt;
&lt;/ul&gt;



&lt;h3 class=&quot;wp-block-heading&quot; id=&quot;what-makes-it-dynamic%3F&quot;&gt;What makes it dynamic?&lt;/h3&gt;



&lt;p&gt;A style guide can be made dynamic by including its code within the website codebase so they share the same stylesheets. Within a CMS environment we can build the style guide using the elements and components that it defines, making the style guide itself a dynamic representation of them. Any style or script changes are immediately reflected in both the site and the style guide, which further enhances its value to help with QA and maintain brand and code consistency.&lt;/p&gt;



&lt;p&gt;A style guide is a great substitute for little to no available content when starting a new tenant site—it already includes all the same building blocks that are used to add content.&lt;/p&gt;



&lt;p&gt;&lt;em&gt;(So many great articles, tools, and references are available regarding the concepts of style guides and pattern libraries. I&amp;#8217;d recommend checking out&lt;/em&gt; &lt;em&gt;&lt;a href=&quot;http://styleguides.io/&quot; rel=&quot;nofollow&quot;&gt;Website Style Guide Resources&lt;/a&gt; for more information.)&lt;/em&gt;&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot; id=&quot;theming-tool&quot;&gt;Theming Tool&lt;/h2&gt;



&lt;p&gt;With each tenant site that is added to a multi-tenant CMS, it becomes increasingly important to enforce as much consistency between them as possible. Unwilling to build cookie-cutter sites that all look the same, we developed the concept of standardized variables for colors and fonts and created a tool that could act as the bridge between design and development when building each new tenant site. The theming tool is a single HTML page that itemizes these variables and illustrates basic page elements in common situations (default, on a dark background, in a sidebar) and looks like a simplified, single page style guide.&lt;/p&gt;



&lt;figure class=&quot;wp-block-image size-full&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;900&quot; height=&quot;506&quot; src=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/10/multitenant_colorfonts-2_0.jpg&quot; alt=&quot;&quot; class=&quot;wp-image-39&quot; srcset=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/10/multitenant_colorfonts-2_0.jpg 900w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/10/multitenant_colorfonts-2_0-300x169.jpg 300w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/10/multitenant_colorfonts-2_0-768x432.jpg 768w&quot; sizes=&quot;auto, (max-width: 900px) 100vw, 900px&quot; /&gt;&lt;/figure&gt;



&lt;p&gt;&lt;a href=&quot;https://stevenwoodson.com/assets/images/posts/2016/multi-tenant/multitenant_colorfonts-2_0.jpg&quot;&gt;&lt;/a&gt;&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot; id=&quot;why-do-we-need-one%3F-2&quot;&gt;Why do we need one?&lt;/h3&gt;



&lt;p&gt;This theming tool may appear to be an unnecessary overlap with the style guide, but there are two important reasons why it has to be separate. First, taking the tool out of the CMS means that it has no external dependencies and can be shared and updated with as little barrier as possible. This gives the design team free rein to incorporate new styles in this tool before development kicks off. Second, including only the site-wide standardized variables and basic page elements encourages a laser focus on nailing these fundamental details first. This is especially important because these fundamentals are the foundation that everything else, including all our custom components, is built on.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot; id=&quot;our-workflow&quot;&gt;Our Workflow&lt;/h3&gt;



&lt;p&gt;We knew from the beginning that our AEM instance would be multi-tenant, so we started with a base content project that contains all shared components and their default styles and scripts. Each tenant site&amp;#8217;s content project inherits and extends the styles and scripts of the base content project to support the branding and unique needs of the site. Because each site also needs to support i18n, the root of each tenant site contains language-based homepages with all other pages as their children.&lt;/p&gt;



&lt;figure class=&quot;wp-block-image size-full&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;900&quot; height=&quot;506&quot; src=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/10/multitenant_filestructure-3_0.jpg&quot; alt=&quot;&quot; class=&quot;wp-image-40&quot; srcset=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/10/multitenant_filestructure-3_0.jpg 900w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/10/multitenant_filestructure-3_0-300x169.jpg 300w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/10/multitenant_filestructure-3_0-768x432.jpg 768w&quot; sizes=&quot;auto, (max-width: 900px) 100vw, 900px&quot; /&gt;&lt;/figure&gt;



&lt;p&gt;&lt;a href=&quot;https://stevenwoodson.com/assets/images/posts/2016/multi-tenant/multitenant_filestructure-3_0.jpg&quot;&gt;&lt;/a&gt;&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot; id=&quot;style-guide&quot;&gt;Style Guide&lt;/h2&gt;



&lt;p&gt;This tenant site root is also an ideal location for the style guide because it&amp;#8217;s unique to the site but does not need to be publicly accessible or specific to a language. This way, each site has its own implementation of the style guide to illustrate its unique styles and components.&lt;/p&gt;



&lt;figure class=&quot;wp-block-image size-full&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;900&quot; height=&quot;506&quot; src=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/10/multitenant_styleguide-2_0.jpg&quot; alt=&quot;&quot; class=&quot;wp-image-41&quot; srcset=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/10/multitenant_styleguide-2_0.jpg 900w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/10/multitenant_styleguide-2_0-300x169.jpg 300w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/10/multitenant_styleguide-2_0-768x432.jpg 768w&quot; sizes=&quot;auto, (max-width: 900px) 100vw, 900px&quot; /&gt;&lt;/figure&gt;



&lt;p&gt;&lt;a href=&quot;https://stevenwoodson.com/assets/images/posts/2016/multi-tenant/multitenant_styleguide-2_0.jpg&quot;&gt;&lt;/a&gt;&lt;/p&gt;



&lt;p&gt;Each site&amp;#8217;s style guide is split into three sections that loosely match its LESS folder structure: Base, Layout, and Component. These sections are then further split into subpages:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;The&amp;nbsp;&lt;em&gt;Base&lt;/em&gt;&amp;nbsp;section itemizes all the color and font variables, as well as displays all the styles and variations of basic page elements, links, buttons, forms, images, and icons.&lt;/li&gt;



&lt;li&gt;The&amp;nbsp;&lt;em&gt;Layout&lt;/em&gt;&amp;nbsp;pages illustrate the grid system, header, footer, sidebar, and mobile navigation styles.&lt;/li&gt;



&lt;li&gt;The&amp;nbsp;&lt;em&gt;Component&lt;/em&gt;&amp;nbsp;section contains a page for each custom component that is developed and shared. These pages contain an instance of the component for each possible configuration variation. For example, we include an instance of a component with and without an optional background color or alternate content positioning. The ability to see all the possible ways a component can be rendered is extremely helpful in QA after code changes are made. It&amp;#8217;s very easy to neglect styling an optional sub-headline that accounts for no images or missing calls to action.&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;Because the style guide content is not authored and updated for public viewing, it&amp;#8217;s stored and deployed from the codebase. We do this both to maintain version control of the style guide content over time and to automatically keep developers&amp;#8217; local copies of the style guide in sync. With AEM, we use FileVault to sync any changes made in authoring back to the codebase so we are still able to use the authoring UI.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot; id=&quot;theming-tool-2&quot;&gt;Theming Tool&lt;/h2&gt;



&lt;p&gt;The underlying goal of our multi-tenant AEM instance was to build a library of shared components and styles that each tenant site could benefit from while also maintaining a unique visual language and site structure. By the time we completed the second site, we had amassed dozens of shared components and the sites were so visually unique that it was difficult to tell they came from the same instance. Mission accomplished… sort of.&lt;/p&gt;



&lt;p&gt;We received PSDs from the design team that outwardly appeared to mirror existing components and required little development effort to create, but in actuality, the minor functional and visual changes took considerably longer than expected. The time dedicated to these changes negated the time saved utilizing the shared components. We didn&amp;#8217;t want to sacrifice the unique designs but needed a way to enforce some consistency.&lt;/p&gt;



&lt;p&gt;To simplify common style changes, we started with the concept of standardized variables representing common colors and fonts, then introduced a default font size to which all set font sizes were made relative. The theming tool is a combination of these variables and basic page element styles. We were also able to use the same LESS stylesheets that each tenant site shares and left them largely unchanged so any modifications in the theming tool can easily be added back into the site.&lt;/p&gt;



&lt;figure class=&quot;wp-block-image size-full&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;900&quot; height=&quot;506&quot; src=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/10/multitenant_theming-2_0.jpg&quot; alt=&quot;&quot; class=&quot;wp-image-42&quot; srcset=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/10/multitenant_theming-2_0.jpg 900w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/10/multitenant_theming-2_0-300x169.jpg 300w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/10/multitenant_theming-2_0-768x432.jpg 768w&quot; sizes=&quot;auto, (max-width: 900px) 100vw, 900px&quot; /&gt;&lt;figcaption class=&quot;wp-element-caption&quot;&gt;Illustrating the changes to default typography in the theming tool after changing the font and color variables.&lt;/figcaption&gt;&lt;/figure&gt;



&lt;p&gt;To combat the design limitations imposed by this standardization, we gave our components multiple configuration options that can change their appearance dramatically.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot; id=&quot;process&quot;&gt;Process&lt;/h2&gt;



&lt;p&gt;To further expedite the process, we created a generic template tenant site and a script to generate a new maven project that copies it. This template site is complete with its own style guide and the standardized variables mentioned above.&lt;/p&gt;



&lt;p&gt;When we start a new tenant site, we first share the theming tool with the design team. While they&amp;#8217;re working on that, we use the script mentioned above to create the new site. Once the theming tool is ready, we copy the handful of stylesheets from it into this new site. With these two steps, we have a new site and its foundational styling complete and ready to preview in the style guide.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot; id=&quot;results&quot;&gt;Results&lt;/h2&gt;



&lt;p&gt;While this is still very much an active work in progress, we&amp;#8217;ve already noticed significant improvements in workflow and a reduction in development time. Because the style guide is synced with the codebase, as it was introduced into our workflow and populated with content, we started to have a common point of reference to use for code reviews and QA. Being able to test new components or code modifications here meant we didn&amp;#8217;t have to spend additional time building a new page for testing. This was especially true in the early stages of a site when content authoring was just beginning.&lt;/p&gt;



&lt;p&gt;The most striking improvement, however, can be seen when comparing the first tenant site developed with the theming tool and the one before it. Jira issues were reduced from 95 to eight and development time was reduced from three developers logging 710 hours collectively down to one developer logging 70 hours. That&amp;#8217;s what we call efficient!&lt;/p&gt;



&lt;p&gt;&lt;em&gt;Thanks to Catrina Ahlbach, Brett Birschbach, Seth Dobbs, Robbie Forer, Paul Foster, Heather Gantz, Brian Langeland, Dan McClain, Ericka Seastrand, John Telford, Jamie Vonk, Brendan Walsh, and Leah Weyandt!&lt;/em&gt;&lt;/p&gt;

    </content>
	</entry>
  
	<entry>
		<title>Off-canvas Menu + Fixed Navbar = Buggy iOS Safari</title>
		<link href="https://stevenwoodson.com/blog/off-canvas-menu-fixed-navbar-buggy-ios-safari"/>
		<published>2013-10-04T11:04:00.000Z</published>
		<updated>2023-08-10T17:27:09.000Z</updated>
		<id>https://stevenwoodson.com/blog/off-canvas-menu-fixed-navbar-buggy-ios-safari</id>
    <summary>While working on a responsive refresh of the Upshot website I came across some buggy behavior when mixing an off-canvas menu with a fixed position element on the page. I realize that fixed elements have had a history of bugginess in iOS and detailed articles have already been written. However, I hadn&amp;#8217;t seen what was occurring for me [&amp;hellip;]</summary>
		<content type="html">
&lt;p&gt;While working on a responsive refresh of the Upshot website I came across some buggy behavior when mixing an off-canvas menu with a fixed position element on the page. I realize that fixed elements have had a history of bugginess in iOS and &lt;a href=&quot;https://remysharp.com/2012/05/24/issues-with-position-fixed-scrolling-on-ios/&quot; rel=&quot;nofollow&quot;&gt;detailed&lt;/a&gt; &lt;a href=&quot;https://www.quirksmode.org/blog/archives/2010/12/the_fifth_posit.html&quot; rel=&quot;nofollow&quot;&gt;articles&lt;/a&gt; have already been written. However, I hadn&amp;#8217;t seen what was occurring for me discussed anywhere yet. In this case the fixed position element contained the &amp;#8220;hamburger&amp;#8221; menu link and the logo so we wanted that to stay fixed to the top of the page, but interacting with the off-canvas menu was problematic.&lt;/p&gt;



&lt;div class=&quot;wp-block-columns is-layout-flex wp-container-core-columns-is-layout-9d6595d7 wp-block-columns-is-layout-flex&quot;&gt;
&lt;div class=&quot;wp-block-column is-layout-flow wp-block-column-is-layout-flow&quot;&gt;
&lt;figure class=&quot;wp-block-image size-full&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;594&quot; height=&quot;988&quot; src=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/10/shifted-fixed-nav.png&quot; alt=&quot;Screenshot example with the fixed navigation shifted to the right&quot; class=&quot;wp-image-34&quot; srcset=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/10/shifted-fixed-nav.png 594w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/10/shifted-fixed-nav-180x300.png 180w&quot; sizes=&quot;auto, (max-width: 594px) 100vw, 594px&quot; /&gt;&lt;/figure&gt;
&lt;/div&gt;



&lt;div class=&quot;wp-block-column is-layout-flow wp-block-column-is-layout-flow&quot;&gt;
&lt;figure class=&quot;wp-block-image size-full&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;594&quot; height=&quot;988&quot; src=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/10/correct-nav.png&quot; alt=&quot;Screenshot example with the fixed navigation corrected and properly aligned&quot; class=&quot;wp-image-35&quot; srcset=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/10/correct-nav.png 594w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/10/correct-nav-180x300.png 180w&quot; sizes=&quot;auto, (max-width: 594px) 100vw, 594px&quot; /&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;



&lt;p&gt;&lt;a href=&quot;https://stevenwoodson.com/assets/images/posts/2015/correct-nav.png&quot;&gt;&lt;/a&gt;&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot; id=&quot;the-issue&quot;&gt;The Issue&lt;/h2&gt;



&lt;p&gt;In the screenshots to the right you can see what the page looks like on load (left image) and what it looked like occasionally after sliding the off-canvas menu out and back (right image). Sometimes it was fine and others were even worse than this screenshot, very random.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot; id=&quot;the-fix&quot;&gt;The Fix&lt;/h2&gt;



&lt;p&gt;To fix (so far it appears to be fixed in iOS7 Safari from my tests) I added a&amp;nbsp;&lt;code&gt;left: inherit;&lt;/code&gt;&amp;nbsp;to the fixed menu. Easy fix but figuring it out took longer than I care to admit.&lt;/p&gt;

    </content>
	</entry>
  
	<entry>
		<title>Avoiding Career Stagnation</title>
		<link href="https://stevenwoodson.com/blog/avoiding-career-stagnation"/>
		<published>2013-07-13T11:03:00.000Z</published>
		<updated>2023-08-10T16:50:07.000Z</updated>
		<id>https://stevenwoodson.com/blog/avoiding-career-stagnation</id>
    <summary>As a web developer the potential for career stagnation &amp;#8211; feeling stuck, idle, and lacking progression &amp;#8211; is higher than average. With technology constantly being improved and reinvented it&amp;#8217;s amazingly easy to find yourself falling behind. I&amp;#8217;ve felt this way several times over the years, to the point where I&amp;#8217;ve started to also notice what [&amp;hellip;]</summary>
		<content type="html">
&lt;p&gt;As a web developer the potential for career stagnation &amp;#8211; feeling stuck, idle, and lacking progression &amp;#8211; is higher than average. With technology constantly being improved and reinvented it&amp;#8217;s amazingly easy to find yourself falling behind. I&amp;#8217;ve felt this way several times over the years, to the point where I&amp;#8217;ve started to also notice what led me out of the rut and back on track. I want to share some of these in the hope that it&amp;#8217;ll help someone else out there, but also as a reminder to myself that conscious effort in these four areas goes a long way in avoiding stagnation.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot; id=&quot;surround-yourself-with-people-you-admire&quot;&gt;Surround yourself with people you admire&lt;/h2&gt;



&lt;p&gt;The web design and development community is second to none, there are countless creatives out there that are genuine and extremely giving people. From lending a hand in IRC or forums to contributing to frameworks and open source code, in terms of resources and community we want for nothing.&lt;/p&gt;



&lt;p&gt;Getting out to conferences, meeting people in your field, and if you&amp;#8217;re lucky enough to work with people you admire is helpful on many levels but the key especially when considering this topic is that they keep that fire burning in you to take yourself to the next level.&lt;/p&gt;



&lt;p&gt;I&amp;#8217;ll admit I&amp;#8217;m not the most proactive when it comes to going out to meetups, but I&amp;#8217;m fortunate enough to work and chat with extraordinarily talented people that I turn to regularly for advice and conversation. Keeping up with the blogs and tweets of people I admire also helps. Find people that inspire you.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot; id=&quot;work-on-projects-you-really-care-about&quot;&gt;Work on projects you really care about&lt;/h2&gt;



&lt;p&gt;I know, this sounds obvious. But when you inevitably get sucked into the daily grind you can quickly lose sight of the forest for the trees. In my experience this has been most common in marketing and advertising projects. There have been times I&amp;#8217;ve worked on one promotion after another meant to only live for a few months, all seemingly sharing the same concepts. It&amp;#8217;s hard to stay motivated when you&amp;#8217;re not challenging yourself and have no personal connection to the project.&lt;/p&gt;



&lt;p&gt;So if you find the work you&amp;#8217;re doing is just &amp;#8216;paying the bills&amp;#8217; and doesn&amp;#8217;t spark your creative interests then try injecting some yourself. Think about what could be changed/added that would make you more interested, chances are it&amp;#8217;ll have the same effect in others &amp;#8211; including the target audience.&lt;/p&gt;



&lt;p&gt;You could also look into freelance or pro bono side work with organizations that you truly care about. This is great for getting outside the daily norm and is a perfect segue into the next topic&amp;#8230;&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot; id=&quot;learn-something-new%2C-change-it-up&quot;&gt;Learn something new, change it up&lt;/h2&gt;



&lt;p&gt;Everyone remembers the excitement and curiosity they felt when they were just starting out, you can get that back by trying something outside your core competency. Designers can try learning more about programming and vice versa, get into project management, take on some UX, there&amp;#8217;s always more out there to explore. I tried my hand at Ruby on Rails for a bit, and grew to not only appreciate it as a programming language but also saw myself incorporating more RoR organizational concepts into my PHP programming.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot; id=&quot;take-a-break&quot;&gt;Take a break&lt;/h2&gt;



&lt;p&gt;All work and no play make Steve&amp;#8230; something something. It&amp;#8217;s scary how easy it is to burn yourself out, you can tell you&amp;#8217;re burning out when you start to get frustrated easily and have a hard time concentrating. Take a break once in a while, go for a walk and have a good laugh, keep some hobbies, and for cryin&amp;#8217; out loud get enough sleep! It really does do wonders.&lt;/p&gt;

    </content>
	</entry>
  
	<entry>
		<title>Filtering and Sorting a Table with PHP</title>
		<link href="https://stevenwoodson.com/blog/filtering-and-sorting"/>
		<published>2012-11-16T10:50:00.000Z</published>
		<updated>2023-08-10T17:28:51.000Z</updated>
		<id>https://stevenwoodson.com/blog/filtering-and-sorting</id>
    <summary>I had a friend ask me earlier this week if I knew of any resources that could point him in the right direction on how to create a PHP page of tabular data that had the ability to be both filtered and sorted. We both weren&amp;#8217;t able to find anything suitably simplified so the underlying [&amp;hellip;]</summary>
		<content type="html">
&lt;p&gt;I had a friend ask me earlier this week if I knew of any resources that could point him in the right direction on how to create a PHP page of tabular data that had the ability to be both filtered and sorted. We both weren&amp;#8217;t able to find anything suitably simplified so the underlying concepts are easily understandable. This article attempts to fill that gap.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot; id=&quot;getting-started&quot;&gt;Getting Started&lt;/h2&gt;



&lt;p&gt;I want to start out with a bit of a description of what the following code is meant to do and how it&amp;#8217;s accomplished. This is — by design — an extremely bare-bones example, meant to be understood rather than duplicated verbatim. I went with a no JavaScript solution here but the act of filtering and sorting would likely feel quicker and more natural if JavaScript was used. If you want to follow along at home I&amp;#8217;d suggest&amp;nbsp;&lt;a href=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/10/filtersort.zip&quot;&gt;downloading this zip&amp;nbsp;of all the (three) files&lt;/a&gt; necessary to get started. In that zip you&amp;#8217;ll find a SQL file of the single table that holds all the data we&amp;#8217;ll be using, an include with a single function where most of the magic happens, and a small index file to see the resulting magic unfold.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot; id=&quot;the-data&quot;&gt;The Data&lt;/h2&gt;



&lt;p&gt;No tricks here, just some simple data to illustrate our purposes. The example is of a fictional three day seminar that has scheduled sessions at set times throughout the three days. Each session is meant for a specific skill level and covers one of several categories. It&amp;#8217;s a straight SQL dump so you can use PHPMyAdmin/Navicat/etc. to create and upload the data so you can play along as you read. Here&amp;#8217;s a couple sample rows of that data:&lt;/p&gt;



&lt;figure class=&quot;wp-block-table&quot;&gt;&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th scope=&quot;col&quot;&gt;SESSION&lt;/th&gt;&lt;th scope=&quot;col&quot;&gt;DATE&lt;/th&gt;&lt;th scope=&quot;col&quot;&gt;START TIME&lt;/th&gt;&lt;th scope=&quot;col&quot;&gt;END TIME&lt;/th&gt;&lt;th scope=&quot;col&quot;&gt;LEVEL&lt;/th&gt;&lt;th scope=&quot;col&quot;&gt;CATEGORY&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Cras faucibus velit in ligula feugiat eu consectetur arcu aliquam.&lt;/td&gt;&lt;td&gt;2013-06-11&lt;/td&gt;&lt;td&gt;12:00:00&lt;/td&gt;&lt;td&gt;13:30:00&lt;/td&gt;&lt;td&gt;Beginner&lt;/td&gt;&lt;td&gt;Healthcare&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Donec et odio est, sed vulputate ligula.&lt;/td&gt;&lt;td&gt;2013-06-12&lt;/td&gt;&lt;td&gt;14:00:00&lt;/td&gt;&lt;td&gt;15:30:00&lt;/td&gt;&lt;td&gt;Beginner&lt;/td&gt;&lt;td&gt;Management&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;figcaption class=&quot;wp-element-caption&quot;&gt;2013 Sessions&lt;/figcaption&gt;&lt;/figure&gt;



&lt;h2 class=&quot;wp-block-heading&quot; id=&quot;the-magic-function&quot;&gt;The Magic Function&lt;/h2&gt;



&lt;p&gt;This is where we get to the real meat &amp;#8216;n&amp;#8217; potatoes, I&amp;#8217;m introducing it early because it&amp;#8217;s necessary to know how this works before discussing the next couple sections. The function below is located in the zip in the filtersort.inc.php file.&lt;/p&gt;



&lt;pre class=&quot;wp-block-code language-php&quot;&gt;&lt;code&gt;&amp;lt;?php

/**
 * Function to gather and return the appropriate results based on the filters received
 * @param type $filter - Array of filters to apply to the query
 * @param type $sort - Array of sorts to apply to the query
 * @return array $return - An array of filtered results
 */
function filtersort_query ( $filter = array(), $sort = array() ) {

	// Starter Variables
	$filter_sql_array = array();
	$return = array();
	$sort_sql_array = array();

	// Default Filter and Sort query additions, if the parameters are empty these will be the fallbacks that are used.
	$filter_sql = &#39;&#39;;
	$sort_sql = &#39;category ASC&#39;;

	// Organize the filter additions to the query
	if ( isset($filter) &amp;amp;&amp;amp; count( $filter ) &amp;gt; 0 ) {
		foreach ( $filter AS $filter_key =&amp;gt; $filter_item ) {
			$filter_sql_array&amp;#91;] = &#39;`&#39;. $filter_key .&#39;` = &quot;&#39;. $filter_item .&#39;&quot;&#39;;
		}
	}
	// Organize the sort additions to the query
	if ( isset($sort) &amp;amp;&amp;amp; count( $sort ) &amp;gt; 0 ) {
		foreach ( $sort AS $sort_key =&amp;gt; $sort_item ) {
			$sort_sql_array&amp;#91;] = &#39;`&#39;. $sort_key .&#39;` &#39;. $sort_item;
		}
	}

	// Build the query, add the filters and sorts to their proper places
	$query = mysql_query(&#39;
		SELECT
			*
		FROM
			sessions
			&#39;. ( count( $filter_sql_array ) &amp;gt; 0 ? &#39;WHERE &#39;. implode( &#39; AND &#39;, $filter_sql_array ) : $filter_sql ) .
		 	( count( $sort_sql_array ) &amp;gt; 0 ? &#39;ORDER BY &#39;. implode( &#39;, &#39;, $sort_sql_array ) : $sort_sql )
	);

	while ( $row = mysql_fetch_array($query, MYSQL_ASSOC) ) {
	    $return&amp;#91;] = $row;
	}

	return $return;
}

?&amp;gt;&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;This&amp;nbsp;&lt;code&gt;filtersort_query&lt;/code&gt;&amp;nbsp;function accepts two associative arrays as parameters for filtering and sorting respectively. The&amp;nbsp;&lt;code&gt;$filter&lt;/code&gt;&amp;nbsp;array&amp;nbsp;&lt;em&gt;keys&lt;/em&gt;&amp;nbsp;are the table columns to filter and the&amp;nbsp;&lt;em&gt;values&lt;/em&gt;&amp;nbsp;are the values we want to keep while filtering all others out. The&amp;nbsp;&lt;code&gt;$sort&lt;/code&gt;&amp;nbsp;array&amp;nbsp;&lt;em&gt;keys&lt;/em&gt;&amp;nbsp;are the table columns to sort and the&amp;nbsp;&lt;em&gt;values&lt;/em&gt;&amp;nbsp;can be &amp;#8216;ASC&amp;#8217; or &amp;#8216;DESC&amp;#8217; to sort either in ascending or descending order. The function takes each of these arrays and converts each key/value pair into what will be their parts of the MySQL query.&lt;/p&gt;



&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: It&amp;#8217;s recommended that if your table data is expected to be long that you also add some sort of pagination to the query/page to maintain a decent page load.&lt;/p&gt;



&lt;p&gt;The query itself starts as a select of the whole table, so on initial page load all results are displayed. The filters and sorts are added if they&amp;#8217;re present in the function parameters explained above. I&amp;#8217;ve also added a default filter and sort in case you want to have some defaults to fall back on if the arrays are empty. These are the two variables called $filter_sql and $sort_sql.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot; id=&quot;filtering&quot;&gt;Filtering&lt;/h2&gt;



&lt;p&gt;The filtering is done by a form located at the top of the index page, in my example they&amp;#8217;re all select boxes but this could be extended to incorporate radio buttons and text inputs to suit your needs.&lt;/p&gt;



&lt;figure class=&quot;wp-block-image size-full&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;575&quot; height=&quot;35&quot; src=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/10/filter_form.575.webp&quot; alt=&quot;Example of the filters I&#39;m using for this example, all select boxes with a &amp;quot;Filter&amp;quot; submit button&quot; class=&quot;wp-image-29&quot; srcset=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/10/filter_form.575.webp 575w, https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/10/filter_form.575-300x18.webp 300w&quot; sizes=&quot;auto, (max-width: 575px) 100vw, 575px&quot; /&gt;&lt;/figure&gt;



&lt;p&gt;When the form is submitted, the index.php example will iterate through the returned form fields and set up an associative array for filtering. The array holds the same key/value pairs as is provided by the form and any with an empty value are weeded out and not sent.&lt;/p&gt;



&lt;pre class=&quot;wp-block-code language-php&quot;&gt;&lt;code&gt;// Starter Variables
$filter = array();
$sort = array();

// If we have GET variables, try to filter/sort
if ( isset($_GET) &amp;amp;&amp;amp; count($_GET) &amp;gt; 0 ) {

	if ( isset($_GET&amp;#91;&#39;sort&#39;]) &amp;amp;&amp;amp; strlen($_GET&amp;#91;&#39;sort&#39;]) &amp;gt; 0 ) {
		$sort&amp;#91; $_GET&amp;#91;&#39;sort&#39;] ] = &#39;ASC&#39;;
	}

	foreach ( $_GET AS $getkey =&amp;gt; $getvar ) {
		if ( $getvar != &quot;&quot; ) {
			$filter&amp;#91;$getkey] = stripslashes($getvar);
		}
	}
	unset($filter&amp;#91;&#39;sort&#39;], $filter&amp;#91;&#39;submit&#39;]);
}&lt;/code&gt;&lt;/pre&gt;



&lt;h2 class=&quot;wp-block-heading&quot; id=&quot;sorting&quot;&gt;Sorting&lt;/h2&gt;



&lt;p&gt;I believe that sorting is meant for after filters are set, as a way to more easily find the filtered result for which you&amp;#8217;re searching. Going with this assumption, sorting takes the previously set key/values of the filters (explained above) and tacks on one more variable &amp;#8216;sort&amp;#8217;. This is all added to the href of a link tag on each of the headlines, so clicking on a headline sorts by that column. In this example I&amp;#8217;m only sorting in ascending order so that&amp;#8217;s the only thing added, if you wanted to add some sort of cycling between sorting in ascending and descending orders this would be where you&amp;#8217;d put it. You can see the code that adds the sort to the Magic Query in lines 19-21 of the snippet directly above this section. This is where the order (set as &amp;#8216;ASC&amp;#8217;) is hard coded for all columns.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot; id=&quot;putting-it-all-together&quot;&gt;Putting it All Together&lt;/h2&gt;



&lt;p&gt;The table starts as a full list of all records in the table, selecting any value in the filter dropdowns filters to records that have only that value in that column, after submitting a filter you can click on a headline to sort the filtered data by that column in ascending order.&lt;/p&gt;



&lt;p&gt;As mentioned above, adding some JavaScript to help with the filtering and sorting without page reload isn&amp;#8217;t a bad idea. Pagination is another common addition especially for tables that can span thousands or more records. Also, building up the sort method might be helpful as well. For example adding the ability to sort by multiple columns and/or allowing for sorting in descending order. Finally, this could be updated to also include a keyword search, giving the end user even more flexibility on narrowing the results to find what they need. Sky&amp;#8217;s the limit with an application like this, so I&amp;#8217;d recommend spending some time determining your requirements early on and plan accordingly.&lt;/p&gt;

    </content>
	</entry>
  
	<entry>
		<title>PHP Functions &#8211; Part 2</title>
		<link href="https://stevenwoodson.com/blog/php-functions-part-2"/>
		<published>2012-10-11T10:49:00.000Z</published>
		<updated>2023-08-10T17:28:51.000Z</updated>
		<id>https://stevenwoodson.com/blog/php-functions-part-2</id>
    <summary>Pad, Reduce, Chunk I lumped these three functions together because I doubt I could count on one hand the number of times I&amp;#8217;ve used any of them. Though once you understand what they do there are several applications for them that would make development cleaner if not also just plain easier. Here they are in [&amp;hellip;]</summary>
		<content type="html">
&lt;h2 class=&quot;wp-block-heading&quot; id=&quot;pad%2C-reduce%2C-chunk&quot;&gt;Pad, Reduce, Chunk&lt;/h2&gt;



&lt;p&gt;I lumped these three functions together because I doubt I could count on one hand the number of times I&amp;#8217;ve used any of them. Though once you understand what they do there are several applications for them that would make development cleaner if not also just plain easier. Here they are in the order I&amp;#8217;m using them in the forthcoming example:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://www.php.net/manual/en/function.array-pad.php&quot; rel=&quot;nofollow&quot;&gt;&lt;code&gt;array_pad&lt;/code&gt;&lt;/a&gt; – Pads an array to the specified size with the specified value&lt;/li&gt;



&lt;li&gt;&lt;a href=&quot;https://www.php.net/manual/en/function.array-reduce.php&quot; rel=&quot;nofollow&quot;&gt;&lt;code&gt;array_reduce&lt;/code&gt;&lt;/a&gt; – Applies a function iteratively to the elements of the array so as to reduce the array to a single value.&lt;/li&gt;



&lt;li&gt;&lt;a href=&quot;https://www.php.net/manual/en/function.array-chunk.php&quot; rel=&quot;nofollow&quot;&gt;&lt;code&gt;array_chunk&lt;/code&gt;&lt;/a&gt; – Splits an array into chunks of the specified size&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;For a semi-useful example, we&amp;#8217;re going to run through these three functions in a script that gathers the top 20 scores for display on a page. Find the full example at the bottom of this post, I&amp;#8217;m going to explain all the parts individually first.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot; id=&quot;array_pad&quot;&gt;array_pad&lt;/h3&gt;



&lt;pre class=&quot;wp-block-code language-php&quot;&gt;&lt;code&gt;// Pad the resulting array up to 20
$top_20 = array_pad( $db_result, 20, FALSE );&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;In our example we need to be able to display 20 results, it&amp;#8217;s not much of a top 20 without 20 results right? So we&amp;#8217;re going to use&amp;nbsp;&lt;code&gt;array_pad&lt;/code&gt;, this will fill the array with 20 items no matter the number of results. The first parameter is the array we need to pad (the $db_result in the example), second is the pad size (20 in the example), finally the third parameter is the value used to pad. Though not used here, we could also pad to the beginning of the array by making the second parameter a negative number, also the third parameter is not restricted to a boolean and can be a number, string, array, etc.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot; id=&quot;array_reduce&quot;&gt;array_reduce&lt;/h3&gt;



&lt;pre class=&quot;wp-block-code language-php&quot;&gt;&lt;code&gt;function r_scoresum ( $sum, $player ) {
	if ( $player !== FALSE ) {
		$sum += $player&amp;#91;&#39;score&#39;];
	}
	return $sum;
}
// Gather total score of all players in the top 20
$top_20_total = array_reduce( $top_20, &quot;r_scoresum&quot; );&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;The two snippets above determine the sum of the returned scores using&amp;nbsp;&lt;code&gt;array_reduce&lt;/code&gt;. Array_reduce requires two parameters, the array to reduce and the name of a callback function to use to perform said reduction. There&amp;#8217;s an optional third parameter called initial that is used at the beginning of the process or as a final result in case the array is empty. The function here makes sure the array item passed to the r_scoresum function is not set to FALSE (one of the padded items that are possibly added at the end of the array) before adding the score to the running total.&amp;nbsp;&lt;code&gt;$top_20_total&lt;/code&gt;&amp;nbsp;will now contain the integer 1080898.&lt;/p&gt;



&lt;pre class=&quot;wp-block-code language-php&quot;&gt;&lt;code&gt;function r_output ( $output, $player ) { if ( $player === FALSE ) { $output .=
&quot;&#92;t&quot;.&#39;
&amp;lt;li&amp;gt;&amp;#91;empty]&amp;lt;/li&amp;gt;
&#39;.&quot;&#92;n&quot;; } else { $output .= &quot;&#92;t&quot;.&#39;
&amp;lt;li&amp;gt;
  &amp;lt;a href=&quot;#&quot;
    &amp;gt;&#39;. $player&amp;#91;&#39;name&#39;] .&#39; - &#39;. number_format( $player&amp;#91;&#39;score&#39;] ) .&#39;&amp;lt;/a
  &amp;gt;
&amp;lt;/li&amp;gt;
&#39;.&quot;&#92;n&quot;; } return $output; } // Set up a simple page of output for the top
players $top_20_output = array_reduce( $page, &quot;r_output&quot; );&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;This is another use of the&amp;nbsp;&lt;code&gt;array_reduce&lt;/code&gt;&amp;nbsp;function, I set up a function r_output that takes the array items and produces some HTML I then use to display the data in a more readable format.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot; id=&quot;array_chunk&quot;&gt;array_chunk&lt;/h3&gt;



&lt;pre class=&quot;wp-block-code language-php&quot;&gt;&lt;code&gt;// Paginate the results
$top_20_paginated = array_chunk($top_20, 10);&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;&lt;code&gt;array_chunk&lt;/code&gt;&amp;nbsp;splits an array into a multidimensional array whose parent items contain arrays of the specified size. This is an example of a possible use of array_chunk, we&amp;#8217;re using it as a simple method for paginating results. I wanted ten items per page so I set&amp;nbsp;&lt;code&gt;array_chunk&lt;/code&gt;&amp;nbsp;with the array to chunk and the second parameter was set to 10. So now&amp;nbsp;&lt;code&gt;$top_20_paginated&lt;/code&gt;&amp;nbsp;will contain an two-dimensional array with two values, each of these two values will themselves be an array of ten results each.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot; id=&quot;full-example&quot;&gt;Full Example&lt;/h3&gt;



&lt;p&gt;Here&amp;#8217;s the code in it&amp;#8217;s entirety:&lt;/p&gt;



&lt;pre class=&quot;wp-block-code language-php&quot;&gt;&lt;code&gt;&amp;lt;?php
/**
 * Reduce the $player array to the sum of all returned scores
 * @param int $sum - Stored sum of all player scores
 * @param array $player - Array of individual player data
 * @return int - The total sum of all provided player scores
 */
function r_scoresum ( $sum, $player ) {
	if ( $player !== FALSE ) {
		$sum += $player&amp;#91;&#39;score&#39;];
	}
	return $sum;
}
/**
 * Return a simple output of the players and their scores linking elsewhere
 * @param string $output
 * @param array $player - Array of individual player data
 * @return string - The ordered list items of the player scores
 */
function r_output ( $output, $player ) {
	if ( $player === FALSE ) {
		$output .= &quot;&#92;t&quot;.&#39;&amp;lt;li&amp;gt;&amp;#91;empty]&amp;lt;/li&amp;gt;&#39;.&quot;&#92;n&quot;;
	} else {
		$output .= &quot;&#92;t&quot;.&#39;&amp;lt;li&amp;gt;&amp;lt;a href=&quot;#&quot;&amp;gt;&#39;. $player&amp;#91;&#39;name&#39;] .&#39; - &#39;. number_format( $player&amp;#91;&#39;score&#39;] ) .&#39;&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&#39;.&quot;&#92;n&quot;;
	}
	return $output;
}

// Faux Database results
$db_result = array(
	array( &#39;name&#39; =&amp;gt; &#39;Nelson Stryker&#39;, &#39;score&#39; =&amp;gt; 66581 ),
	array( &#39;name&#39; =&amp;gt; &#39;Fernando Cuneo&#39;, &#39;score&#39; =&amp;gt; 65897 ),
	array( &#39;name&#39; =&amp;gt; &#39;Darryl Gudger&#39;, &#39;score&#39; =&amp;gt; 64152 ),
	array( &#39;name&#39; =&amp;gt; &#39;Lance Secrist&#39;, &#39;score&#39; =&amp;gt; 63652 ),
	array( &#39;name&#39; =&amp;gt; &#39;Christian Rostad&#39;, &#39;score&#39; =&amp;gt; 62800 ),
	array( &#39;name&#39; =&amp;gt; &#39;Nelson Wantz&#39;, &#39;score&#39; =&amp;gt; 61896 ),
	array( &#39;name&#39; =&amp;gt; &#39;Dona Hirata&#39;, &#39;score&#39; =&amp;gt; 61688 ),
	array( &#39;name&#39; =&amp;gt; &#39;Marcia Kyser&#39;, &#39;score&#39; =&amp;gt; 60161 ),
	array( &#39;name&#39; =&amp;gt; &#39;Julio Nordberg&#39;, &#39;score&#39; =&amp;gt; 59093 ),
	array( &#39;name&#39; =&amp;gt; &#39;Maricela Gapinski&#39;, &#39;score&#39; =&amp;gt; 58780 ),
	array( &#39;name&#39; =&amp;gt; &#39;Adrienne Carreon&#39;, &#39;score&#39; =&amp;gt; 58702 ),
	array( &#39;name&#39; =&amp;gt; &#39;Nelson Rearick&#39;, &#39;score&#39; =&amp;gt; 58326 ),
	array( &#39;name&#39; =&amp;gt; &#39;Ericka Ursery&#39;, &#39;score&#39; =&amp;gt; 58101 ),
	array( &#39;name&#39; =&amp;gt; &#39;Earlene Carstens&#39;, &#39;score&#39; =&amp;gt; 57684 ),
	array( &#39;name&#39; =&amp;gt; &#39;Sofia Regal&#39;, &#39;score&#39; =&amp;gt; 57047 ),
	array( &#39;name&#39; =&amp;gt; &#39;Katy Unknow&#39;, &#39;score&#39; =&amp;gt; 56453 ),
	array( &#39;name&#39; =&amp;gt; &#39;Lance Weibel&#39;, &#39;score&#39; =&amp;gt; 55362 ),
	array( &#39;name&#39; =&amp;gt; &#39;Clare Deppen&#39;, &#39;score&#39; =&amp;gt; 54523 )
);
// Count of original database results
$db_result_count = count( $db_result );

// Pad the resulting array up to 20
$top_20 = array_pad( $db_result, 20, FALSE );
// Gather total score of all players in the top 20
$top_20_total = array_reduce( $top_20, &quot;r_scoresum&quot; );
// Paginate the results
$top_20_paginated = array_chunk($top_20, 10);

//After pagination is determined, set up an ordered list to display
foreach ( $top_20_paginated as $pagenum =&amp;gt; $page ){
	// Set up a simple page of output for the top players
	$top_20_output = array_reduce( $page, &quot;r_output&quot; );
	echo &#39;&amp;lt;h2&amp;gt;Page &#39;. ( $pagenum + 1 ) .&#39;&amp;lt;/h2&amp;gt;&#39;.&quot;&#92;n&quot;.
		&#39;&amp;lt;ol start=&quot;&#39;. ( $pagenum * 10  + 1 ) .&#39;&quot;&amp;gt;&#39;.&quot;&#92;n&quot;. $top_20_output .&#39;&amp;lt;/ol&amp;gt;&#39;.&quot;&#92;n&quot;;
}

// General stats
echo &#39;
&amp;lt;dl&amp;gt;
	&amp;lt;dt&amp;gt;Sum of the Top 20 scores&amp;lt;/dt&amp;gt;&amp;lt;dd&amp;gt;&#39;. number_format( $top_20_total ) .&#39; points&amp;lt;/dd&amp;gt;
	&amp;lt;dt&amp;gt;Average of the Top 20 scores&amp;lt;/dt&amp;gt;&amp;lt;dd&amp;gt;&#39;. number_format( round( $top_20_total / $db_result_count ) ) .&#39; points&amp;lt;/dd&amp;gt;
&amp;lt;/dl&amp;gt;&#39;;
?&amp;gt;&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;And the output will look like this:&lt;/p&gt;



&lt;pre class=&quot;wp-block-code language-html&quot;&gt;&lt;code&gt;&amp;lt;h2&amp;gt;Page 1&amp;lt;/h2&amp;gt;
&amp;lt;ol start=&quot;1&quot;&amp;gt;
  &amp;lt;li&amp;gt;&amp;lt;a href=&quot;#&quot;&amp;gt;Nelson Stryker - 66,581&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
  &amp;lt;li&amp;gt;&amp;lt;a href=&quot;#&quot;&amp;gt;Fernando Cuneo - 65,897&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
  &amp;lt;li&amp;gt;&amp;lt;a href=&quot;#&quot;&amp;gt;Darryl Gudger - 64,152&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
  &amp;lt;li&amp;gt;&amp;lt;a href=&quot;#&quot;&amp;gt;Lance Secrist - 63,652&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
  &amp;lt;li&amp;gt;&amp;lt;a href=&quot;#&quot;&amp;gt;Christian Rostad - 62,800&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
  &amp;lt;li&amp;gt;&amp;lt;a href=&quot;#&quot;&amp;gt;Nelson Wantz - 61,896&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
  &amp;lt;li&amp;gt;&amp;lt;a href=&quot;#&quot;&amp;gt;Dona Hirata - 61,688&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
  &amp;lt;li&amp;gt;&amp;lt;a href=&quot;#&quot;&amp;gt;Marcia Kyser - 60,161&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
  &amp;lt;li&amp;gt;&amp;lt;a href=&quot;#&quot;&amp;gt;Julio Nordberg - 59,093&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
  &amp;lt;li&amp;gt;&amp;lt;a href=&quot;#&quot;&amp;gt;Maricela Gapinski - 58,780&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;
&amp;lt;h2&amp;gt;Page 2&amp;lt;/h2&amp;gt;
&amp;lt;ol start=&quot;11&quot;&amp;gt;
  &amp;lt;li&amp;gt;&amp;lt;a href=&quot;#&quot;&amp;gt;Adrienne Carreon - 58,702&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
  &amp;lt;li&amp;gt;&amp;lt;a href=&quot;#&quot;&amp;gt;Nelson Rearick - 58,326&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
  &amp;lt;li&amp;gt;&amp;lt;a href=&quot;#&quot;&amp;gt;Ericka Ursery - 58,101&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
  &amp;lt;li&amp;gt;&amp;lt;a href=&quot;#&quot;&amp;gt;Earlene Carstens - 57,684&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
  &amp;lt;li&amp;gt;&amp;lt;a href=&quot;#&quot;&amp;gt;Sofia Regal - 57,047&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
  &amp;lt;li&amp;gt;&amp;lt;a href=&quot;#&quot;&amp;gt;Katy Unknow - 56,453&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
  &amp;lt;li&amp;gt;&amp;lt;a href=&quot;#&quot;&amp;gt;Lance Weibel - 55,362&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
  &amp;lt;li&amp;gt;&amp;lt;a href=&quot;#&quot;&amp;gt;Clare Deppen - 54,523&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
  &amp;lt;li&amp;gt;&amp;#91;empty]&amp;lt;/li&amp;gt;
  &amp;lt;li&amp;gt;&amp;#91;empty]&amp;lt;/li&amp;gt;
&amp;lt;/ol&amp;gt;

&amp;lt;dl&amp;gt;
  &amp;lt;dt&amp;gt;Sum of the Top 20 scores&amp;lt;/dt&amp;gt;
  &amp;lt;dd&amp;gt;1,080,898 points&amp;lt;/dd&amp;gt;
  &amp;lt;dt&amp;gt;Average of the Top 20 scores&amp;lt;/dt&amp;gt;
  &amp;lt;dd&amp;gt;60,050 points&amp;lt;/dd&amp;gt;
&amp;lt;/dl&amp;gt;&lt;/code&gt;&lt;/pre&gt;

    </content>
	</entry>
  
	<entry>
		<title>Recharge &#038; Release</title>
		<link href="https://stevenwoodson.com/blog/recharge-release"/>
		<published>2012-10-07T10:46:00.000Z</published>
		<updated>2023-08-10T16:44:12.000Z</updated>
		<id>https://stevenwoodson.com/blog/recharge-release</id>
    <summary>I find that I go through productivity phases, generally the two I am most cognizant of are what I can loosely refer to as recharge and release. When recharging I feel I read a lot more than usual. I catch up on a backlog of articles I flagged, dig into books I had been meaning [&amp;hellip;]</summary>
		<content type="html">
&lt;p&gt;I find that I go through productivity phases, generally the two I am most cognizant of are what I can loosely refer to as recharge and release.&lt;/p&gt;



&lt;p&gt;When recharging I feel I read a lot more than usual. I catch up on a backlog of articles I flagged, dig into books I had been meaning to read, and generally find outside inspiration from various sources. I sleep and even eat more. The result is a focused sense of creativity and determination in my own work. I get the urge to update my website, portfolio, and blog. I dust off personal projects that had slipped away from me and give them another chance. I sleep and eat less and typically can focus on tasks longer. Leading inevitably back to the beginning.&lt;/p&gt;



&lt;p&gt;I suppose that makes sense, it&amp;#8217;s pretty easy to burn oneself out by working too much and a person can only read and rest so much before the urge to create flares up again.&lt;/p&gt;

    </content>
	</entry>
  
	<entry>
		<title>No Shame In Looking It Up</title>
		<link href="https://stevenwoodson.com/blog/no-shame-in-looking-it-up"/>
		<published>2012-09-27T10:44:00.000Z</published>
		<updated>2023-08-10T17:24:55.000Z</updated>
		<id>https://stevenwoodson.com/blog/no-shame-in-looking-it-up</id>
    <summary>Web developers are tasked with remembering several different programming languages in order to effectively get the job done. HTML, CSS, &amp;amp; Javascript are the big three of course, but there are others such as Coffeescript, SCSS, XML that help workflow and facilitate tasks. Backend devs also commonly need to know scripting languages (e.g. PHP, Ruby [&amp;hellip;]</summary>
		<content type="html">
&lt;p&gt;Web developers are tasked with remembering several different programming languages in order to effectively get the job done. HTML, CSS, &amp;amp; Javascript are the big three of course, but there are others such as Coffeescript, SCSS, XML that help workflow and facilitate tasks. Backend devs also commonly need to know scripting languages (e.g. PHP, Ruby on Rails, .NET) and how to interact with a database (e.g. SQLite, MSSQL, MySQL). Beyond the base programming languages there&amp;#8217;s no shortage of frameworks and plugins available to increase organization and assist in common tasks, jQuery and CodeIgniter being two notable examples. Though, with great power comes great responsibility &amp;#8211; as the saying goes.&lt;/p&gt;



&lt;p&gt;Each framework and plugin has their own documentation and structure that needs to be reviewed and retained. On top of this foundation of programming language and frameworks knowledge, there are other principles and methodologies to also keep in mind:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;architectural patterns e.g. REST&lt;/li&gt;



&lt;li&gt;programming principles&amp;nbsp;e.g. DRY&lt;/li&gt;



&lt;li&gt;workflow methodologies&amp;nbsp;e.g. Agile and Lean&lt;/li&gt;



&lt;li&gt;semantic &amp;amp; accessibility best practices&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;In this veritable acronym stew of programming &amp;amp; process, I wholeheartedly believe that there&amp;#8217;s &lt;strong&gt;no shame in looking it up&lt;/strong&gt;. There&amp;#8217;s simply too much out there to interact with and utilize to be able to retain it all. Need to find a string in a string but don&amp;#8217;t recall which PHP string function to use, go ahead and &lt;a href=&quot;https://php.net/manual/en/ref.strings.php&quot; rel=&quot;nofollow&quot;&gt;check it out&lt;/a&gt;! I think it&amp;#8217;s far more important for us to grasp what these languages, frameworks, and plugins are capable of before we attempt to memorize all the available methods and their syntax. You could always look up how to write a jQuery selector for all &lt;code&gt;&amp;lt;input&gt;&lt;/code&gt; elements with a name attribute starting with &amp;#8220;quarterly&amp;#8221; (it&amp;#8217;s &lt;a href=&quot;http://api.jquery.com/attribute-starts-with-selector/&quot; rel=&quot;nofollow&quot;&gt;[name ^=&amp;#8221;quarterly&amp;#8221;]&lt;/a&gt; by the way), having the knowledge that such a selector is available is an excellent start.&lt;/p&gt;

    </content>
	</entry>
  
	<entry>
		<title>PHP Functions &#8211; Part 1</title>
		<link href="https://stevenwoodson.com/blog/php-functions-part-1"/>
		<published>2012-09-06T10:41:00.000Z</published>
		<updated>2023-08-10T17:28:52.000Z</updated>
		<id>https://stevenwoodson.com/blog/php-functions-part-1</id>
    <summary>I&amp;#8217;ve been working with PHP for several years, dare I say a decade at this point, and I&amp;#8217;m still surprised at functions I&amp;#8217;ve never or rarely used. In an attempt to share in my delight of discovering these gems and hopefully enlighten others to some very helpful functions, I&amp;#8217;m going to try to start a [&amp;hellip;]</summary>
		<content type="html">
&lt;p&gt;I&amp;#8217;ve been working with PHP for several years, dare I say a decade at this point, and I&amp;#8217;m still surprised at functions I&amp;#8217;ve never or rarely used. In an attempt to share in my delight of discovering these gems and hopefully enlighten others to some very helpful functions, I&amp;#8217;m going to try to start a series of quick articles explaining various PHP functions. This first part will go over four very inter-related array functions.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot; id=&quot;pop%2C-push%2C-shift%2C-unshift&quot;&gt;Pop, Push, Shift, Unshift&lt;/h2&gt;



&lt;p&gt;Gotta love the naming conventions sometimes, these make me think of a &lt;a href=&quot;https://en.wikipedia.org/wiki/Technologic&quot; rel=&quot;nofollow&quot;&gt;Daft Punk song&lt;/a&gt;. Anyways, these are four array functions that I like to keep lumped together in my mind as they do very similar tasks. Here they are in the order I&amp;#8217;m using them in the forthcoming example:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;a href=&quot;http://www.php.net/manual/en/function.array-shift.php&quot; rel=&quot;nofollow&quot;&gt;&lt;code&gt;array_shift&lt;/code&gt;&lt;/a&gt; – Takes the first variable off the array and returns it&lt;/li&gt;



&lt;li&gt;&lt;a href=&quot;https://www.php.net/manual/en/function.array-pop.php&quot; rel=&quot;nofollow&quot;&gt;&lt;code&gt;array_pop&lt;/code&gt;&lt;/a&gt; – Takes the last variable off the array and returns it&lt;/li&gt;



&lt;li&gt;&lt;a href=&quot;https://www.php.net/manual/en/function.array-push.php&quot; rel=&quot;nofollow&quot;&gt;&lt;code&gt;array_push&lt;/code&gt;&lt;/a&gt; – Adds a variable onto the end of the array and returns the array count&lt;/li&gt;



&lt;li&gt;&lt;a href=&quot;https://www.php.net/manual/en/function.array-unshift.php&quot; rel=&quot;nofollow&quot;&gt;&lt;code&gt;array_unshift&lt;/code&gt;&lt;/a&gt; – Adds a variable onto the beginning of the array and returns the array count&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;One thing worth noting that I find helps me remember these functions without having to look them up constantly. The first pair (&lt;code&gt;array_pop&lt;/code&gt;&amp;nbsp;and&amp;nbsp;&lt;code&gt;array_shift&lt;/code&gt;) and the second pair (&lt;code&gt;array_push&lt;/code&gt;&amp;nbsp;and&amp;nbsp;&lt;code&gt;array_unshift&lt;/code&gt;) do the same things, the only distinctions are whether it&amp;#8217;s targeting the beginning or the end of the array and the returned value. Function&amp;nbsp;&lt;code&gt;array_pop&lt;/code&gt;&amp;nbsp;and&amp;nbsp;&lt;code&gt;array_shift&lt;/code&gt;&amp;nbsp;return the variable that was removed from the array,&amp;nbsp;&lt;code&gt;array_push&lt;/code&gt;&amp;nbsp;and&amp;nbsp;&lt;code&gt;array_unshift&lt;/code&gt;&amp;nbsp;return the array count after the addition was made.&lt;/p&gt;



&lt;p&gt;Here&amp;#8217;s an example that uses all four functions mentioned here to modify an array.&lt;/p&gt;



&lt;pre class=&quot;wp-block-code language-php&quot;&gt;&lt;code&gt;$input = array(0, 1, 2, 3, 4, 5);

echo &#39;
  &amp;lt;strong&amp;gt;Starting Array&amp;lt;/strong&amp;gt;
  &amp;lt;code&amp;gt;$input = array (&#39; . implode(&#39;, &#39;, $input ) . &#39;)&amp;lt;/code&amp;gt;
&#39;;

$result = array_shift($input);

echo &#39;
  &amp;lt;strong&amp;gt;Using array_shift&amp;lt;/strong&amp;gt; - &amp;lt;em&amp;gt;Takes the first variable off the array and returns it&amp;lt;/em&amp;gt;
  &amp;lt;code&amp;gt;$result = array_shift($input);&amp;lt;/code&amp;gt;
  &amp;lt;strong&amp;gt;Result:&amp;lt;/strong&amp;gt;
  &amp;lt;code&amp;gt;$input = array (&#39; . implode(&#39;, &#39;, $input ) . &#39;)&amp;lt;/code&amp;gt;
  &amp;lt;code&amp;gt;$result = &#39; . $result . &#39;&amp;lt;/code&amp;gt;
&#39;;

$result = array_pop($input);

echo &#39;
  &amp;lt;strong&amp;gt;Using array_pop&amp;lt;/strong&amp;gt; - &amp;lt;em&amp;gt;Takes the last variable off the array and returns it&amp;lt;/em&amp;gt;
  &amp;lt;code&amp;gt;$result = array_pop($input);&amp;lt;/code&amp;gt;
  &amp;lt;strong&amp;gt;Result:&amp;lt;/strong&amp;gt;
  &amp;lt;code&amp;gt;$input = array (&#39; . implode(&#39;, &#39;, $input ) . &#39;)&amp;lt;/code&amp;gt;
  &amp;lt;code&amp;gt;$result = &#39; . $result . &#39;&amp;lt;/code&amp;gt;
&#39;;

$result = array_push($input, 5);

echo &#39;
  &amp;lt;strong&amp;gt;Using array_push&amp;lt;/strong&amp;gt; - &amp;lt;em&amp;gt;Adds a variable onto the end of the array and returns the count of the array&amp;lt;/em&amp;gt;
  &amp;lt;code&amp;gt;$result = array_push($input, 5);&amp;lt;/code&amp;gt;
  &amp;lt;strong&amp;gt;Result:&amp;lt;/strong&amp;gt;
  &amp;lt;code&amp;gt;$input = array (&#39; . implode(&#39;, &#39;, $input ) . &#39;)&amp;lt;/code&amp;gt;
  &amp;lt;code&amp;gt;$result = &#39; . $result . &#39;&amp;lt;/code&amp;gt;&#39;;

$result = array_unshift($input, 0);

echo &#39;
  &amp;lt;strong&amp;gt;Using array_unshift&amp;lt;/strong&amp;gt; - &amp;lt;em&amp;gt;Adds a variable onto the beginning of the array and returns the count of the array&amp;lt;/em&amp;gt;
  &amp;lt;code&amp;gt;$result = array_unshift($input, 0);&amp;lt;/code&amp;gt;
  &amp;lt;strong&amp;gt;Result:&amp;lt;/strong&amp;gt;
  &amp;lt;code&amp;gt;$input = array (&#39; . implode(&#39;, &#39;, $input ) . &#39;)&amp;lt;/code&amp;gt;
  &amp;lt;code&amp;gt;$result = &#39; . $result . &#39;&amp;lt;/code&amp;gt;
&#39;;
?&amp;gt;&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;This code returns:&lt;/p&gt;



&lt;pre class=&quot;wp-block-code&quot;&gt;&lt;code&gt;Starting Array
$input = array (0, 1, 2, 3, 4, 5)

Using array_shift – Takes the first variable off the array and returns it
$result = array_shift($input);
Result:
$input = array (1, 2, 3, 4, 5)
$result = 0

Using array_pop – Takes the last variable off the array and returns it
$result = array_pop($input);
Result:
$input = array (1, 2, 3, 4)
$result = 5

Using array_push – Adds a variable onto the end of the array and returns the count of the array
$result = array_push($input, 5);
Result:
$input = array (1, 2, 3, 4, 5)
$result = 5

Using array_unshift – Adds a variable onto the beginning of the array and returns the count of the array
$result = array_unshift($input, 0);
Result:
$input = array (0, 1, 2, 3, 4, 5)
$result = 6&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;A bit cyclical I admit, but it gets the point across. We removed the first then last values from the array and then turned around and added them back in. So we end up with the same array values that we started with.&lt;/p&gt;



&lt;p&gt;Stay tuned, in Part 2 I&amp;#8217;ll be talking about a few more underutilized array functions.&lt;/p&gt;

    </content>
	</entry>
  
	<entry>
		<title>Apache 2GB file limit</title>
		<link href="https://stevenwoodson.com/blog/apache-2gb-file-limit"/>
		<published>2011-02-16T13:40:00.000Z</published>
		<updated>2023-08-10T17:25:33.000Z</updated>
		<id>https://stevenwoodson.com/blog/apache-2gb-file-limit</id>
    <summary>Just came across a new issue I hadn&amp;#8217;t dealt with before, apparently once a file (in my case a very large&amp;nbsp;error_log&amp;nbsp;file) reaches 2GB or 2,147,483,647 bytes it can&amp;#8217;t work with the file anymore. In this case the system couldn&amp;#8217;t write to the error log and threw an Internal Server Error. I didn&amp;#8217;t realize what the [&amp;hellip;]</summary>
		<content type="html">
&lt;p&gt;Just came across a new issue I hadn&amp;#8217;t dealt with before, apparently once a file (in my case a very large&amp;nbsp;&lt;code&gt;error_log&lt;/code&gt;&amp;nbsp;file) reaches 2GB or 2,147,483,647 bytes it can&amp;#8217;t work with the file anymore. In this case the system couldn&amp;#8217;t write to the error log and threw an Internal Server Error. I didn&amp;#8217;t realize what the issue was until I went to download the error log to diagnose and found out it was exactly 2GB. Lo and behold as soon as I renamed it and started a new log the site worked fine again.&lt;/p&gt;



&lt;p&gt;After some research I found it&amp;#8217;s due to Apache 1.3.x or 2.0.x 32bit register limits. Apache 2.2.x changed it to a 64bit register so that limit is no longer an issue for people running newer versions.&lt;/p&gt;



&lt;p&gt;Anyone else have experiences dealing with this limit?&lt;/p&gt;

    </content>
	</entry>
  
	<entry>
		<title>URI vs URL? Think Scotch vs Whisky</title>
		<link href="https://stevenwoodson.com/blog/uri-vs-url-think-scotch-vs-whisky"/>
		<published>2010-12-02T13:37:00.000Z</published>
		<updated>2023-08-10T16:45:00.000Z</updated>
		<id>https://stevenwoodson.com/blog/uri-vs-url-think-scotch-vs-whisky</id>
    <summary>For the longest time, I&amp;#8217;ve been unconsciously transposing&amp;nbsp;URI&amp;nbsp;and&amp;nbsp;URL&amp;nbsp;(with the occasional sprinkling of &amp;#8220;web address&amp;#8221;) in conversations and correspondences without ever really considering that they are abbreviations for two different things. That is until I decided to finally read up on these confounding abbreviations and got the proper info on the real difference between&amp;nbsp;URL&amp;nbsp;and&amp;nbsp;URI. So you [&amp;hellip;]</summary>
		<content type="html">
&lt;p&gt;For the longest time, I&amp;#8217;ve been unconsciously transposing&amp;nbsp;URI&amp;nbsp;and&amp;nbsp;URL&amp;nbsp;(with the occasional sprinkling of &amp;#8220;web address&amp;#8221;) in conversations and correspondences without ever really considering that they are abbreviations for two different things. That is until I decided to finally read up on these confounding abbreviations and got the proper info on the real difference between&amp;nbsp;URL&amp;nbsp;and&amp;nbsp;URI. So you wanna know what the difference is?&lt;/p&gt;



&lt;p&gt;After all that build up I&amp;#8217;m sad to say there really isn&amp;#8217;t much of a difference.&amp;nbsp;URI&amp;nbsp;vs&amp;nbsp;URL&amp;nbsp;is actually very similar to Whisky vs Scotch &amp;#8211; how&amp;#8217;s that for a memory jogging association? Any good bartender that knows their jigger from their jeroboam will tell you that Scotch is really just a whisky that was produced in Scotland. A&amp;nbsp;URL&amp;nbsp;is actually a&amp;nbsp;URI&amp;nbsp;that identifies the location of the resource. For another explanation, A&amp;nbsp;URL&amp;nbsp;is a&amp;nbsp;URI&amp;nbsp;but a&amp;nbsp;URI&amp;nbsp;is not necessarily a&amp;nbsp;URL&amp;nbsp;just like scotch is always a whiskey but a whiskey is not necessarily scotch.&lt;/p&gt;



&lt;p&gt;Another little tidbit I discovered while researching this is that there&amp;#8217;s another abbreviation called&amp;nbsp;URN&amp;nbsp;that identifies a resource by name in a particular namespace and is also a&amp;nbsp;URI. So for example 1400082471 is the&amp;nbsp;ISBN&amp;nbsp;URN&amp;nbsp;of the book Dreaming in Code.&lt;/p&gt;



&lt;figure class=&quot;wp-block-image size-full&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;300&quot; height=&quot;155&quot; src=&quot;https://content.walnutcreekcreative.com/stevewoodson/wp-content/uploads/sites/3/2022/10/uri-vs-url.png&quot; alt=&quot;A Venn-like diagram showing URL on the left and URN on the right with a slight overlap, above is URI with an encompassing line to them both signifying that a URL and a URN are both URIs&quot; class=&quot;wp-image-15&quot; /&gt;&lt;/figure&gt;



&lt;p&gt;So to recap, a&amp;nbsp;URL&amp;nbsp;is a&amp;nbsp;URI&amp;nbsp;that identifies a location (hence the last word of the abbreviation being location) and&amp;nbsp;URN&amp;nbsp;is a&amp;nbsp;URI&amp;nbsp;that identifies a name (hence the last word of the abbreviation being name). For all you right brain visual learners out there, check out the diagram to the left as a reminder.&lt;/p&gt;



&lt;p&gt;Not really the most groundbreaking of topics but I thought it was cool to know the difference (what little difference there is at least) especially considering it&amp;#8217;s a part of what I do on a daily basis. Hopefully someone out there feels the same.&lt;/p&gt;

    </content>
	</entry>
  
	<entry>
		<title>Website Performance – gZipping &#038; Extended Expirations</title>
		<link href="https://stevenwoodson.com/blog/website-performance-gzipping-and-extended-expirations"/>
		<published>2008-07-06T13:29:00.000Z</published>
		<updated>2023-08-10T17:28:39.000Z</updated>
		<id>https://stevenwoodson.com/blog/website-performance-gzipping-and-extended-expirations</id>
    <summary>With a mixture of a few techniques added to the .htaccess or httpd.conf files, you can reduce the pageload and the bandwith usage of your website fairly easily. In the examples below i&amp;#8217;m referring to expressions for addition to .htaccess, the http.conf file may be structured differently to do the same things. I&amp;#8217;m setting this [&amp;hellip;]</summary>
		<content type="html">
&lt;p&gt;With a mixture of a few techniques added to the .htaccess or httpd.conf files, you can reduce the pageload and the bandwith usage of your website fairly easily. In the examples below i&amp;#8217;m referring to expressions for addition to .htaccess, the http.conf file may be structured differently to do the same things. I&amp;#8217;m setting this up in a standard LAMP setup with Apache 2 and PHP5. It will also wirk with versions of PHP4 but not all the following items work in Apache 1.3.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot; id=&quot;apache-module-mod_expires&quot;&gt;Apache Module mod_expires&lt;/h2&gt;



&lt;p&gt;The first thing to do is extend the amount of time text, images, css, flash and javascript are stored in the users cache. To turn this on we need&amp;nbsp;&lt;em&gt;mod_expires&lt;/em&gt;&amp;nbsp;to be active, we then use the expression&amp;nbsp;&lt;code&gt;ExpiresActive On&lt;/code&gt;. We then set a default expiry for everything which is generally set to 6 hours (300 seconds) from the time of access. This is set using the expression&amp;nbsp;&lt;code&gt;ExpiresDefault A300&lt;/code&gt;. Alternatively we can set it to expire a set amount of time since the file was last modified using M instead of A as in&amp;nbsp;&lt;code&gt;ExpiresDefault M300&lt;/code&gt;. Then, if need be, we can take a more granular approach to expiration times by setting expiration by filetype using the expression&amp;nbsp;&lt;code&gt;ExpiresByType _[mimetype][a|m][seconds]_&lt;/code&gt;. So to set GIF images to expire one week from time of access we&amp;#8217;d use&amp;nbsp;&lt;code&gt;ExpiresByType image/gif A604800&lt;/code&gt;.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot; id=&quot;fileetag-directive&quot;&gt;FileETag Directive&lt;/h2&gt;



&lt;p&gt;The FileETag directive configures the file attributes that are used to create the ETag (entity tag) response header field when the document is based on a file. Because we&amp;#8217;re manually setting expirations we don&amp;#8217;t require these headers so it&amp;#8217;s easiest to just turn it off using the expression&amp;nbsp;&lt;code&gt;FileETag none&lt;/code&gt;.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot; id=&quot;compressing-php-pages&quot;&gt;Compressing PHP Pages&lt;/h2&gt;



&lt;p&gt;The following requires the PHP installation to have the&amp;nbsp;&lt;em&gt;zlib extension&lt;/em&gt;&amp;nbsp;enabled, which it should be by default. We use the expression&amp;nbsp;&lt;code&gt;php_value output_handler ob_gzhandler&lt;/code&gt;&amp;nbsp;to turn on compression of the final php pages sent to the users browser. It&amp;#8217;s noted on the&amp;nbsp;&lt;a href=&quot;https://php.net/&quot; rel=&quot;nofollow&quot;&gt;PHP.net&lt;/a&gt;&amp;nbsp;website that turning this on via the php.ini&amp;nbsp;&lt;code&gt;zlib.output_compression&lt;/code&gt;&amp;nbsp;is preferred if available to edit.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot; id=&quot;apache-module-mod_deflate&quot;&gt;Apache Module mod_deflate&lt;/h2&gt;



&lt;p&gt;As of Apache 2.0, there&amp;#8217;s an available module that compresses the server output before sending to the user, called&amp;nbsp;&lt;em&gt;mod_deflate&lt;/em&gt;. This module must be turned on in order to work. I set this up to compress by filetype so it looks like&amp;nbsp;&lt;code&gt;AddOutputFilterByType DEFLATE text/html text/plain text/xml text/css text/javascript text/x-javascript application/javascript application/x-javascript&lt;/code&gt;. There are ways to set this up even on a per browser basis but since the browser negotiates with the server before any files are transferred, it&amp;#8217;s not necessary. If the browser is not compatible with compressed content, the server will provide the content uncompressed.&lt;/p&gt;



&lt;p&gt;It&amp;#8217;s also worth noting that Apache version 1.3 had a method for serving compressed content called&amp;nbsp;&lt;em&gt;mod_gzip&lt;/em&gt;.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot; id=&quot;final-notes&quot;&gt;Final Notes&lt;/h2&gt;



&lt;p&gt;Using these methods I&amp;#8217;ve decreased pageload and bandwith on my site, after adding it my ySlow score for the homepage went from a D(64) to a B(83) and there was a noticeable increase in pageload speed as I was navigating the site. Many people stress that including this code into the httpd.conf file is better because it loads faster and isn&amp;#8217;t reloaded every page load as the .htaccess file would be but my being on a shared environment I haven&amp;#8217;t tried it myself..&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot; id=&quot;final-code&quot;&gt;Final Code&lt;/h2&gt;



&lt;pre class=&quot;wp-block-code&quot;&gt;&lt;code&gt;ExpiresActive On
ExpiresDefault A300
ExpiresByType text/javascript A2592000
ExpiresByType text/x-javascript A2592000
ExpiresByType application/javascript A2592000
ExpiresByType application/x-javascript A2592000
ExpiresByType text/css A604800
ExpiresByType image/gif A604800
ExpiresByType image/png A604800
ExpiresByType image/jpeg A604800
ExpiresByType text/plain A604800
ExpiresByType application/x-shockwave-flash A2592000
ExpiresByType application/pdf A604800
ExpiresByType text/html A300
FileETag none  php_value output_handler ob_gzhandler  AddOutputFilterByType DEFLATE text/html text/plain text/xml text/css text/javascript text/x-javascript application/javascript application/x-javascript&lt;/code&gt;&lt;/pre&gt;

    </content>
	</entry>
  
	<entry>
		<title>MySQL Optimization Top Ten List</title>
		<link href="https://stevenwoodson.com/blog/mysql-optimization-top-ten-list"/>
		<published>2007-11-06T13:27:00.000Z</published>
		<updated>2023-08-11T16:19:19.000Z</updated>
		<id>https://stevenwoodson.com/blog/mysql-optimization-top-ten-list</id>
    <summary>I recently had the opportunity to delve deeper into MySQL and how to optimize and generally better my database structure and queries. There’s a lot of information on this topic so I thought i’d share the top ten things that matter to me when creating and querying databases.</summary>
		<content type="html">
&lt;p&gt;I recently had the opportunity to delve deeper into MySQL and how to optimize and generally better my database structure and queries. There’s a lot of information on this topic so I thought i’d share the top ten things that matter to me when creating and querying databases.&lt;/p&gt;



&lt;ol class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;Make the fields of your tables as small as possible, if you know a varchar field is never going to need a value more than 10 characters, set it to varchar(10).&lt;/li&gt;



&lt;li&gt;Always use auto Increment IDs in your tables, even if you don’t expect you’ll need them now they always seem to come in handy.&lt;/li&gt;



&lt;li&gt;Index all the fields you will be querying directly, especially those that you know an exact value for.&lt;/li&gt;



&lt;li&gt;Normalize the data structure as much as possible, duplicate data is just more for the server to wade through when running a query.&lt;/li&gt;



&lt;li&gt;If you’re querying something against a number e.g.( WHERE x = 12 ) there’s no need to use quotes around the number value, this actually slows down the query because it needs to convert from a string to a number.&lt;/li&gt;



&lt;li&gt;Limit the use of LIKE and the % wildcard e.g.(WHERE userName LIKE %Woods%) as this slows the query considerably. If you know that the value is always going to be at the beginning of a field, be sure to only use the wildcard at the end to speed up the query e.g.(WHERE userName LIKE Woods%).&lt;/li&gt;



&lt;li&gt;Multiple field indexes are good at speeding up queries but be aware that the first field in the index must be the first item from that index searched in the query or the whole thing won’t be used the way you intended.&lt;/li&gt;



&lt;li&gt;Be aware that there is a cost to indexing, both in time and space. Indexes speed up select queries but slow down deletes and inserts and basically any other queries that involve writing to the DB. The more indexes a table has the slower these write queries will be because the indexes have to be changed with the data. The indexes also take up disk space which may cause a database to reach its disk limit more quickly. The practical implication of both these factors is that if you don’t need a particular index to help queries perform better, don’t create it.&lt;/li&gt;



&lt;li&gt;Use the EXPLAIN keyword with your query to get important information on the query including the possible keys and estimated rows that will need to be searched to find a result. Especially handy with multiple table queries with joins.&lt;/li&gt;



&lt;li&gt;Be careful with joins, if used incorrectly they can slow down a query considerably. Values from the preceding table (as listed in the output of EXPLAIN) are used to find rows in the current table. So if you have three tables each with 1000 rows to search you’re actually returning 1,000,000,000 rows of data. Obviously this is too many and indexes should be employed to reduce this number to something more manageable.&lt;/li&gt;
&lt;/ol&gt;

    </content>
	</entry>
</feed>
