<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Ryan VerWey</title>
    <description>The latest articles on Forem by Ryan VerWey (@rverwey).</description>
    <link>https://forem.com/rverwey</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1213395%2Fb0a67ef6-3d3c-41a1-b327-06e902153dc4.png</url>
      <title>Forem: Ryan VerWey</title>
      <link>https://forem.com/rverwey</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/rverwey"/>
    <language>en</language>
    <item>
      <title>What AI Overviews Mean for Your Small Business Website in 2026</title>
      <dc:creator>Ryan VerWey</dc:creator>
      <pubDate>Mon, 13 Apr 2026 12:31:00 +0000</pubDate>
      <link>https://forem.com/rverwey/what-ai-overviews-mean-for-your-small-business-website-in-2026-49d7</link>
      <guid>https://forem.com/rverwey/what-ai-overviews-mean-for-your-small-business-website-in-2026-49d7</guid>
      <description>&lt;p&gt;Google's search results page looks different than it did two years ago. In many cases, the first thing a visitor sees when they search is not a list of websites to click through. It is a generated summary: a paragraph or two crafted by Google's AI that pulls from multiple sources, answers the question directly, and often satisfies the reader without a single click.&lt;/p&gt;

&lt;p&gt;This feature, called AI Overviews (formerly Search Generative Experience), launched broadly across the United States in 2024 and has since expanded to over 100 countries. It now appears on a significant share of informational and research queries. The businesses that appear as sources inside those summaries get a citation and a link. The businesses that do not appear lose traffic they do not even know they are losing.&lt;/p&gt;

&lt;p&gt;For small business owners, this shift raises practical questions. Which searches trigger AI Overviews? Is your site's traffic declining because of this? And what can you actually do about it? This post walks through each of those questions with direct answers.&lt;/p&gt;

&lt;h2&gt;
  
  
  What AI Overviews Are and Where They Show Up
&lt;/h2&gt;

&lt;p&gt;AI Overviews are generated summaries that appear at the top of certain Google search results pages, above the traditional organic link list. They are produced by a large language model trained on web content and constructed in real time based on the user's query. The AI retrieves relevant content from across the web, synthesizes it into a coherent response, and cites the sources it drew from.&lt;/p&gt;

&lt;p&gt;Not every query triggers an AI Overview. Google's systems favor them for informational queries: "how does X work," "what is the difference between A and B," "what should I do when Y happens." They appear less consistently on navigational queries (someone searching for a specific brand or website) and on highly transactional queries (someone ready to buy a specific product). Local queries, like "best plumber near me" or "divorce attorney in Tampa," tend to show the local pack and traditional results more than AI summaries, though that is beginning to change.&lt;/p&gt;



&lt;h2&gt;
  
  
  How This Affects Small Business Websites
&lt;/h2&gt;

&lt;p&gt;The impact of AI Overviews is uneven, and that matters.&lt;/p&gt;

&lt;p&gt;If your business lives almost entirely on transactional or local queries, your Google Search traffic has probably held up better than sites that depend heavily on informational or educational content. A roofing company ranking for "roof repair Jacksonville FL" is less exposed to AI Overview displacement than a marketing blog or a financial planning site answering broad research questions.&lt;/p&gt;

&lt;p&gt;But the line is blurrier than it seems. Here is why:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Many small businesses published educational blog content specifically to attract customers researching before they buy. That content is precisely what AI Overviews are summarizing and displacing clicks from.&lt;/li&gt;
&lt;li&gt;AI Overviews are expanding into more query types over time, including some local and service-based queries.&lt;/li&gt;
&lt;li&gt;Even when AI Overviews appear on transactional queries, they push the traditional organic results further down the page, reducing click-through rates even for high-ranking pages.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The businesses most affected combine two characteristics: they rely on blog or resource content for a meaningful share of organic traffic, and they have not yet optimized that content to be cited rather than bypassed by AI systems.&lt;/p&gt;

&lt;blockquote&gt;The real risk for small businesses is not that AI Overviews exist. It is that competitors who understand how to get cited in them will start owning the answer to every question your customers ask before they ever reach your website.&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The Opportunity Hiding Inside the Shift
&lt;/h2&gt;

&lt;p&gt;Before treating AI Overviews as purely negative, it is worth understanding the other side of the equation. When your content is cited inside an AI Overview, your business name and a link appear at the top of the search results page, above every traditional organic result, for what could be a high-intent query.&lt;/p&gt;

&lt;p&gt;Early data from SEO researchers tracking AI Overview citation behavior suggests that appearing as a primary cited source generates meaningful referral traffic and, more importantly, brand recognition from users who may not click but do absorb the source name. Being the business cited in the answer to "what should I look for when hiring a web design agency" or "how do I know if my Facebook ads are working" builds authority in a way that is difficult to quantify but impossible to ignore.&lt;/p&gt;

&lt;p&gt;The goal is not to resist the shift. The goal is to position your content as the source the AI reaches for.&lt;/p&gt;

&lt;h2&gt;
  
  
  Structured Data: Give AI Systems a Map of Your Business
&lt;/h2&gt;

&lt;p&gt;Structured data is Schema.org markup added to your website that describes your content in a machine-readable format. It tells search crawlers and AI retrieval systems not just what your page says, but what your business is, who operates it, what services you offer, where you serve customers, and what questions your page answers.&lt;/p&gt;

&lt;p&gt;For small businesses, the most important schema types to implement are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;LocalBusiness&lt;/strong&gt; (or a more specific subtype like Plumber, LegalService, or HealthAndBeautyBusiness): includes your business name, address, phone number, hours of operation, and service areas&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;FAQPage&lt;/strong&gt;: marks up question-and-answer content in a format that AI systems retrieve with high accuracy for informational queries&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Article&lt;/strong&gt; or &lt;strong&gt;BlogPosting&lt;/strong&gt;: describes authored content with publication date, author credentials, and topic signals&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Service&lt;/strong&gt;: describes specific services you offer, including pricing ranges when applicable&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Person&lt;/strong&gt; or &lt;strong&gt;Organization&lt;/strong&gt;: establishes your brand identity and links to authoritative profiles like Google Business Profile and LinkedIn&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of these require a developer to implement if your site runs on a modern platform. Many can be added through plugins or CMS fields. What matters is accuracy and completeness. Partial or inaccurate structured data can confuse retrieval systems, so verify your markup using Google's Rich Results Test after every implementation.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ Content: The Format AI Overviews Favor Most
&lt;/h2&gt;

&lt;p&gt;Of all the content formats that AI systems retrieve reliably, FAQ sections are the most consistently favored. The reason is structural: a question followed immediately by a direct, complete answer is exactly the format a generative AI is trying to produce. When your page provides that structure explicitly, it reduces the work the AI has to do to extract and use your content.&lt;/p&gt;

&lt;p&gt;For small businesses, this means building FAQ sections into service pages, not just into dedicated FAQ pages. Each major service you offer should include a section that addresses the questions your customers actually ask: what it costs, how long it takes, what is included, what sets you apart, and what happens after they contact you. Blog posts should include a FAQ section at the end or integrated throughout, covering the secondary questions that readers bring to the topic.&lt;/p&gt;



&lt;p&gt;Use precise, specific language in your FAQ answers. "It typically costs between $1,500 and $4,000 depending on the number of service pages and complexity of the design" is far more citable than "pricing varies based on your needs." AI systems and the users they are answering prefer specific, verifiable information over vague, uncommitted language.&lt;/p&gt;

&lt;p&gt;Apply &lt;code&gt;FAQPage&lt;/code&gt; schema markup to these sections so search systems can identify and parse them without ambiguity.&lt;/p&gt;

&lt;h2&gt;
  
  
  E-E-A-T Signals: Demonstrating That You Are the Real Expert
&lt;/h2&gt;

&lt;p&gt;Google's quality framework for evaluating content is called E-E-A-T: Experience, Expertise, Authoritativeness, and Trustworthiness. These were designed to help human quality raters assess content, but they also describe the signals that AI retrieval systems use when selecting which sources to cite. Content that signals genuine expertise and real experience is surfaced more reliably than generic content that covers the same topic without demonstrating who is behind it.&lt;/p&gt;

&lt;p&gt;For a small business website, building E-E-A-T signals means several concrete things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Author attribution on every blog post, with a bio that details the author's credentials, years of experience, and specific areas of expertise&lt;/li&gt;
&lt;li&gt;A robust About page that tells the real story of your business, including the founder's background, the team's qualifications, and any relevant certifications, licenses, or professional memberships&lt;/li&gt;
&lt;li&gt;Case studies or portfolio pages that document real results with specific, verifiable outcomes&lt;/li&gt;
&lt;li&gt;Third-party validation in the form of reviews, press mentions, directory listings, and industry association memberships&lt;/li&gt;
&lt;li&gt;Named experts speaking in your content, not just a generic brand voice&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The businesses that get cited in AI Overviews consistently are the ones that make it easy for AI systems to verify who is behind the content and why that person or organization should be trusted on this topic. Anonymity is the opposite of citable.&lt;/p&gt;

&lt;p&gt;For a deeper look at how these same signals apply to AI-powered search tools like ChatGPT, Perplexity, and Google Gemini, the &lt;a href="https://dev.to/blog/generative-engine-optimization-guide"&gt;generative engine optimization guide&lt;/a&gt; covers the full framework in detail.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to Do This Month
&lt;/h2&gt;

&lt;p&gt;If you want to start acting on this, prioritize in this order:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Audit your service pages for FAQ sections. Add at least five questions and direct answers to each major service page, and apply &lt;code&gt;FAQPage&lt;/code&gt; schema markup to each set.&lt;/li&gt;
&lt;li&gt;Check your structured data. Run your homepage and top service pages through Google's Rich Results Test. Fix any errors or missing fields in your &lt;code&gt;LocalBusiness&lt;/code&gt; and &lt;code&gt;Article&lt;/code&gt; schema.&lt;/li&gt;
&lt;li&gt;Update your About page and author bios. Make sure every piece of authored content on your site names a real person with real credentials. Thin or absent author information is one of the most common E-E-A-T gaps we find on small business sites.&lt;/li&gt;
&lt;li&gt;Rewrite your top blog posts for question-answering structure. Pick your three highest-traffic posts and rewrite the opening so the first paragraph directly answers the core question the post addresses. Then add a FAQ section at the bottom.&lt;/li&gt;
&lt;li&gt;Build citations. Reach out to local publications, industry directories, and partner businesses to earn links and mentions. Each external citation of your business name and website is a signal that AI systems register when deciding whether you are an authoritative source.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;None of this requires a new website or a new strategy. It requires making your existing content cleaner, more specific, and more clearly attributable to a real business run by real experts.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>programming</category>
      <category>startup</category>
    </item>
    <item>
      <title>The Axios Supply Chain Attack: What Happened, How to Check, and What to Do Next</title>
      <dc:creator>Ryan VerWey</dc:creator>
      <pubDate>Mon, 06 Apr 2026 14:09:00 +0000</pubDate>
      <link>https://forem.com/rverwey/the-axios-supply-chain-attack-what-happened-how-to-check-and-what-to-do-next-18n</link>
      <guid>https://forem.com/rverwey/the-axios-supply-chain-attack-what-happened-how-to-check-and-what-to-do-next-18n</guid>
      <description>&lt;p&gt;&lt;em&gt;Two malicious versions of Axios were published to npm on March 31, 2026, hiding a dependency that deployed a Remote Access Trojan to developer machines and CI/CD servers. Here is what happened, how the attacker covered their tracks, and exactly what to do if your environment was exposed.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;On March 31, 2026, two versions of Axios were pulled from npm after security researchers confirmed they contained a hidden dependency that deployed a Remote Access Trojan to any machine that ran npm install during a window of under three hours. Axios, a JavaScript HTTP client with over 100 million weekly downloads, had been poisoned. The malicious code inside Axios itself was zero lines. The weapon was a dependency that nobody invited, delivered through a mechanism every JavaScript developer trusts: the npm install lifecycle.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a Supply Chain Attack Is
&lt;/h2&gt;

&lt;p&gt;A supply chain attack targets the tools and dependencies developers use to build software rather than the deployed application itself. Instead of trying to exploit a production server directly, the attacker inserts malicious code into a package that developers install as part of their normal workflow. Every npm install runs with implicit trust that the resulting dependency tree is safe. Supply chain attacks exploit that trust by corrupting a link in the chain well before the code reaches any deployment environment. Because the attack executes at install time on the developer's own machine or CI/CD runner, it can access secrets, credentials, and file system contents that production servers would never expose.&lt;/p&gt;

&lt;p&gt;Axios became a target because of its reach. A package downloaded 100 million times per week appears in frontend frameworks, backend services, and enterprise codebases across every industry. A successful compromise of even a two-hour window touches hundreds of thousands of installs. The axios attack was not the first npm supply chain incident, but the scale of the target, the operational sophistication of the execution, and the completeness of the self-cleanup make it one of the most significant ever documented.&lt;/p&gt;

&lt;h2&gt;
  
  
  How the Attack Was Staged: A Timeline
&lt;/h2&gt;

&lt;p&gt;The operation shows clear evidence of deliberate preparation. Forensic analysis by StepSecurity traced the groundwork back more than 18 hours before any malicious Axios version appeared on npm. The attacker executed each step in sequence, with each one making the next harder to detect.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;March 30, 2026, early morning: An attacker-controlled account published a package called &lt;a href="mailto:plain-crypto-js@4.2.0"&gt;plain-crypto-js@4.2.0&lt;/a&gt; to npm. This version was a clean, bit-for-bit copy of the legitimate crypto-js library. No malicious code was present. Its sole purpose was to establish publishing history for the account so it would not trigger "brand-new account" warnings in security scanners during later steps.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Later that same day, late evening: The same account published &lt;a href="mailto:plain-crypto-js@4.2.1"&gt;plain-crypto-js@4.2.1&lt;/a&gt;. This version introduced two new files: an obfuscated JavaScript dropper named setup.js, and a pre-staged clean replacement file used for anti-forensic cover after execution. It also added a postinstall hook to the package manifest pointing to setup.js. Socket's automated scanner flagged this version as malicious within six minutes of publication.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;March 31, 2026, 12:21 AM UTC: The primary Axios maintainer account, which the attacker had compromised, published &lt;a href="mailto:axios@1.14.1"&gt;axios@1.14.1&lt;/a&gt; to npm. The only difference from the previous clean version (1.14.0) was a single addition to package.json: plain-crypto-js listed as a runtime dependency. No corresponding commit or release tag exists in the Axios GitHub repository for this version. Every legitimate Axios release uses npm's OIDC Trusted Publisher mechanism, tying each publish cryptographically to a verified GitHub Actions workflow. The malicious version had no such verification.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;39 minutes later: &lt;a href="mailto:axios@0.30.4"&gt;axios@0.30.4&lt;/a&gt; was published using the same method. Both the 1.x and 0.x release branches were now compromised. Any project using a caret range on either branch would pull the poisoned version on its next npm install.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Approximately 3:15 AM UTC: npm unpublished both malicious Axios versions and placed a security hold on plain-crypto-js. Between the first malicious publish and the removal, less than three hours elapsed.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  What the Dropper Did Once Installed
&lt;/h2&gt;

&lt;p&gt;When a developer ran npm install and resolved either malicious Axios version, npm automatically installed &lt;a href="mailto:plain-crypto-js@4.2.1"&gt;plain-crypto-js@4.2.1&lt;/a&gt; as a transitive dependency, then executed the postinstall hook, launching setup.js. The dropper used a two-layer obfuscation scheme to hide its strings from static analysis tools: module names, the command-and-control server address, shell commands, and file paths were all encoded behind a combination of reversed Base64 and an XOR cipher keyed to a string embedded in the script.&lt;/p&gt;

&lt;p&gt;Once the strings were decoded at runtime, the dropper detected the operating system and routed to a platform-specific payload delivery path. On macOS, it wrote an AppleScript to a temporary file, executed it silently, and instructed it to download a compiled binary to /Library/Caches/com.apple.act.mond, a path deliberately constructed to resemble a legitimate Apple system daemon. On Windows, it located PowerShell, copied it to a path inside Program Data under the name wt.exe (mimicking Windows Terminal), then used a hidden VBScript wrapper to fetch and run a PowerShell payload without opening any visible window. On Linux and other Unix-like systems, it fetched a Python script, saved it to the /tmp directory, and launched it as a background process orphaned from the npm process tree.&lt;/p&gt;

&lt;p&gt;Supply chain attacks insert malicious code at the dependency level, where it runs before reaching any production environment.&lt;/p&gt;

&lt;p&gt;All three platform payloads contacted the same command-and-control server at sfrclak.com on port 8000. The server returned a second-stage Remote Access Trojan tailored to each platform. The macOS RAT was captured and reverse-engineered by researchers before the server went offline. It was a fully functional C++ binary that fingerprinted the victim system, collected running processes and directory listings, executed arbitrary shell commands, deployed further payloads, and beaconed back to the attacker every 60 seconds. Each platform variant sent a distinct identifier in its POST body so the server could route the correct second-stage payload.&lt;/p&gt;

&lt;p&gt;“There are zero lines of malicious code inside Axios itself. Both poisoned releases inject a fake dependency whose sole purpose is to run a postinstall script that deploys a cross-platform remote access trojan.”&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;StepSecurity, March 2026&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After launching the platform-specific payload, setup.js performed three cleanup steps in sequence: it deleted itself, deleted the malicious package.json (which contained the postinstall hook), and replaced it with the pre-staged clean version that reported version 4.2.0 rather than 4.2.1. Running npm audit after the fact would raise no flags. Inspecting the package directory in node_modules would show what appeared to be a clean, ordinary package. The only forensic indicator that survived the cleanup was the presence of the plain-crypto-js directory itself, because that package has never appeared as a dependency in any legitimate Axios release.&lt;/p&gt;

&lt;p&gt;The Windows payload left one additional persistent artifact: the copied PowerShell binary at %PROGRAMDATA%\wt.exe. This file would survive dependency reinstalls and system reboots. Even after removing plain-crypto-js and installing a clean version of Axios, wt.exe would remain on the machine as long as the operating system was not reformatted.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Determine Whether You Were Affected
&lt;/h2&gt;

&lt;p&gt;The malicious versions were live between approximately 12:21 AM and 3:15 AM UTC on March 31, 2026. Any environment where npm install ran during that window and could have resolved &lt;a href="mailto:axios@1.14.1"&gt;axios@1.14.1&lt;/a&gt; or &lt;a href="mailto:axios@0.30.4"&gt;axios@0.30.4&lt;/a&gt; through a caret or wildcard range warrants a full investigation. Work through the following checks in order.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Search your dependency lockfiles. Open package-lock.json, yarn.lock, or pnpm-lock.yaml in every project that uses Axios and search for references to axios 1.14.1, axios 0.30.4, and the string plain-crypto-js. Any match in a lockfile means the compromised version was resolved during an install. If your repositories live on GitHub, the code search interface lets you scan your entire organization's lockfiles at once.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Check the installed node_modules directory. In any project where you suspect the package was installed, look inside node_modules for a folder named plain-crypto-js. Its presence confirms the dropper ran, regardless of the version number reported inside the folder. Because the dropper replaces the manifest with a downgraded version number as part of its cleanup, the folder's existence is a more reliable indicator than any version string.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Review CI/CD pipeline logs. Identify every npm install or npm ci run that executed between 12:21 AM and 3:15 AM UTC on March 31. Search those logs for plain-crypto-js appearing in the dependency resolution output. If you have outbound network monitoring on your CI runners, look for connections to the domain sfrclak.com or the IP address 142.11.206.73 on port 8000.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Check developer machines for RAT artifacts. The dropper writes a payload file at a predictable location on each operating system. On macOS, look for a file named com.apple.act.mond inside the Library/Caches directory. On Windows, look for a file named wt.exe inside the Program Data directory. On Linux, look for a file named ld.py inside the tmp directory. Finding any of these files confirms that the dropper reached its second stage and established a RAT on the machine.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Search corporate network and DNS logs. If your organization runs DNS filtering, a proxy, or a firewall with logging, search for any queries or connections to the domain sfrclak.com. A hit from a developer machine or CI runner in the early hours of March 31 confirms that the RAT dropper contacted the attacker's server successfully.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  What to Do Based on What You Found
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Compromised Version Appeared Only in Source Files
&lt;/h3&gt;

&lt;p&gt;If you found the version in a lockfile or branch that was never actually installed in a live environment, the exposure is limited to source. Update your version specification to a known-safe release: &lt;a href="mailto:axios@1.14.0"&gt;axios@1.14.0&lt;/a&gt; for 1.x projects, or &lt;a href="mailto:axios@0.30.3"&gt;axios@0.30.3&lt;/a&gt; for 0.x projects. Add an overrides or resolutions block to your package.json to prevent future installs from resolving to the compromised versions through a transitive dependency. Delete the node_modules/plain-crypto-js directory if it exists, run a clean install, and regenerate your lockfile. Review any open pull requests that reference the compromised versions and close them without merging, even if CI passed. The CI runner may have executed the dropper on those branches.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Package Was Installed on an Ephemeral CI Runner
&lt;/h3&gt;

&lt;p&gt;GitHub-hosted runners and similar ephemeral environments are destroyed after each job executes, so the runner itself is not a persistent threat. The concern is the credentials that were accessible to the workflow during the compromised run. Rotate every secret that was injected into that environment: npm tokens, cloud provider access keys and session tokens for AWS, GCP, and Azure, SSH keys, API keys stored as environment variables, and any values sourced from the repository's secrets store. After rotating, review the access logs for each service those credentials could reach and look for activity in the window following the compromised install.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Dropper Ran on a Developer Machine or Self-Hosted Runner
&lt;/h3&gt;

&lt;p&gt;Treat any machine where you confirmed the dropper ran as fully compromised. A loaded Remote Access Trojan can execute arbitrary commands, deploy additional payloads, exfiltrate files, and establish persistence mechanisms beyond the initial install. Local inspection alone cannot determine what the attacker accessed, exfiltrated, or modified. The response path has four steps: isolate the machine from the network immediately; document every credential that was accessible on that machine before wiping it, including API keys, SSH keys, cloud access tokens, npm tokens, database connection strings, and any .env files across local projects; reformat the operating system and rebuild from a known-good baseline; rotate every documented credential from a separate, clean machine before reconnecting anything to internal services. Review the access logs for those services covering the period during and after the compromise window.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hardening Your Workflow to Limit Exposure Going Forward
&lt;/h2&gt;

&lt;p&gt;The axios attack exploited a detection window measured in hours. Several controls at the package manager level narrow or close that window for future attacks. Most major JavaScript package managers now support a minimum release age policy, a setting that prevents newly published packages from being automatically resolved until they have been live for a configured number of days. This policy would have blocked &lt;a href="mailto:plain-crypto-js@4.2.1"&gt;plain-crypto-js@4.2.1&lt;/a&gt;, which was published only hours before the compromised axios versions appeared. The documentation for npm, pnpm, yarn, and bun each covers this setting directly. Running installs with the ignore-scripts flag in CI/CD pipelines prevents postinstall hooks from executing at all, removing the specific mechanism the dropper relied on. Combining a minimum release age policy with script suppression in CI provides two independent controls against this class of attack.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>cybersecurity</category>
      <category>devops</category>
      <category>npm</category>
    </item>
    <item>
      <title>Generative Engine Optimization: What It Is and How to Rank in the Age of AI Search</title>
      <dc:creator>Ryan VerWey</dc:creator>
      <pubDate>Sat, 14 Mar 2026 00:00:00 +0000</pubDate>
      <link>https://forem.com/rverwey/generative-engine-optimization-what-it-is-and-how-to-rank-in-the-age-of-ai-search-2lmd</link>
      <guid>https://forem.com/rverwey/generative-engine-optimization-what-it-is-and-how-to-rank-in-the-age-of-ai-search-2lmd</guid>
      <description>&lt;p&gt;For the past two decades, getting found online meant ranking in Google's list of blue links. That model is not disappearing, but it is no longer the only interface people use to find information. A growing share of search queries are now handled by AI-powered tools that generate a direct answer rather than a ranked list of pages to click through. ChatGPT, Perplexity, Google's AI Overviews, Microsoft Copilot, and similar systems are fielding hundreds of millions of queries per day and constructing synthesized responses drawn from content across the web. Generative Engine Optimization, or GEO, is the practice of making your content the kind that AI systems cite, quote, and reference when building those answers.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Generative Engine Optimization Is
&lt;/h2&gt;

&lt;p&gt;The term GEO was formally introduced in a 2023 research paper from Princeton, Georgia Tech, and IIT Delhi, which studied how specific content characteristics influence citation rates in AI-generated responses. The core finding was that certain qualities - clear structure, cited statistics, authoritative sourcing, and direct question-answering formats - significantly increased the likelihood that a generative AI system would surface that content in a response. GEO is the discipline of applying those characteristics intentionally across your web presence so that AI systems include your brand and content when constructing answers in your industry.&lt;/p&gt;

&lt;h2&gt;
  
  
  How AI Search Engines Retrieve and Use Content
&lt;/h2&gt;

&lt;p&gt;Traditional search engines index pages and rank them by relevance and authority for a given query. Generative engines work differently. They use a process called retrieval-augmented generation (RAG), where the AI first retrieves a set of relevant documents, then synthesizes a coherent answer by drawing on the most credible and clearly-written content in that retrieval pool. The sources that end up cited or referenced in the final answer are not necessarily the ones with the highest domain authority. They are the ones whose content was the clearest, most directly relevant, and most authoritative at the time of retrieval. This is a meaningful shift: a well-structured page from a smaller brand can out-cite a larger competitor's poorly organized content.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Factors That Drive GEO Visibility
&lt;/h2&gt;

&lt;p&gt;Research into how generative engines select and surface content has identified a consistent set of signals that improve citation rates. These factors do not replace traditional SEO considerations - they build on top of them.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Authoritative sourcing: content that cites credible statistics, research, and primary sources is significantly more likely to be quoted by AI systems than content making unsupported claims&lt;/li&gt;
&lt;li&gt;Direct question-answering structure: pages that open with a clear, concise answer to a specific question perform better in retrieval than pages that bury the answer several paragraphs in&lt;/li&gt;
&lt;li&gt;E-E-A-T signals: Experience, Expertise, Authoritativeness, and Trustworthiness markers - author bios, credentials, original case studies, named experts - are evaluated by both Google and generative AI systems&lt;/li&gt;
&lt;li&gt;Structured data and schema markup: Schema.org markup helps AI systems understand what your content represents and the entities it describes, improving accurate retrieval&lt;/li&gt;
&lt;li&gt;Brand mentions and backlinks: citations from credible third-party sources signal to AI systems that your brand is a recognized authority in your space&lt;/li&gt;
&lt;li&gt;Content freshness: AI systems retrieval-augmented with recent content deprioritize outdated statistics and stale information&lt;/li&gt;
&lt;li&gt;Content depth and specificity: comprehensive answers to narrow, specific questions consistently outperform shallow coverage of broad topics&lt;/li&gt;
&lt;li&gt;Clean, readable prose: AI systems parse and synthesize content more effectively when it is written in clear language with a logical heading structure&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;The content that AI systems cite most is not necessarily the content that ranks highest on Google. It is the content that most clearly and credibly answers the exact question the user is asking.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  5 Ways to Improve Your GEO Visibility Right Now
&lt;/h2&gt;

&lt;p&gt;You do not need to rebuild your entire content strategy to improve your visibility in AI-generated responses. These five actions work across virtually any business type and can be applied to existing content as well as new content going forward.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Structure content to directly answer questions: identify the specific questions your customers ask most often and build dedicated sections that answer each one concisely and completely. An H2 heading that mirrors a user query followed immediately by a direct answer is the structure generative engines retrieve most reliably. FAQ sections, in particular, carry significant weight in AI retrieval systems.&lt;/li&gt;
&lt;li&gt;Add statistics and cite your sources: unsupported claims are treated as lower-quality by both Google and AI systems. Back every significant claim with a specific statistic, study, or data point and link to the original source. Multiple studies have confirmed that content with cited data is surfaced by AI systems at measurably higher rates than content making the same points without evidence.&lt;/li&gt;
&lt;li&gt;Build brand mentions and authoritative backlinks: being referenced by established publications, industry directories, local news outlets, and credible websites signals to AI systems that your brand is a recognized authority. A consistent local PR strategy, guest contributions to relevant industry publications, and placement in authoritative directories all build the citation profile that generative engines weigh heavily.&lt;/li&gt;
&lt;li&gt;Complete and optimize your structured data: implement Schema.org markup appropriate to your business type - LocalBusiness, Article, FAQPage, Service, Person, or Organization. This gives AI systems a machine-readable understanding of who you are, what you offer, and where you operate. Pages with complete structured data are retrieved and represented more accurately in AI-generated responses.&lt;/li&gt;
&lt;li&gt;Publish original research, data, or documented expertise: AI systems give significant weight to content presenting original insights, proprietary data, or clearly documented professional experience. A case study showing specific results you achieved for a client, a survey with original findings, or a detailed walkthrough based on real work you have completed is far more citable than a generically written overview that dozens of other sites have already published.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  GEO and Traditional SEO Are Not in Conflict
&lt;/h2&gt;

&lt;p&gt;A common question when businesses first encounter GEO is whether optimizing for AI means doing something different from or contrary to their existing SEO strategy. In practice, the two approaches are highly compatible. The factors that improve visibility in AI-generated responses - authoritative content, clear structure, credible sourcing, schema markup, E-E-A-T signals, and backlink quality - are the same factors that improve traditional search rankings. A business already investing in strong technical SEO and high-quality content is building the foundation for GEO at the same time. The additions GEO specifically requires are mostly about content format: being more explicit in answering questions, being more rigorous about citing evidence, and being more deliberate about building citation-worthy expertise and brand authority.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Business Case for Acting Now
&lt;/h2&gt;

&lt;p&gt;AI search is not a niche use case. Google's AI Overviews appear on the majority of informational queries in the United States. Perplexity crossed 100 million monthly active users in 2025. ChatGPT Search is actively indexing the live web. The businesses building GEO-optimized content libraries today are positioning themselves to be the sources AI systems default to when answering questions in their industry. The window to establish that authority before the competitive landscape fully catches on is open - but it is narrowing quickly.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Echo Effect builds content strategies designed for visibility in both traditional search and AI-generated responses. From structured data implementation to content audits to full editorial calendars built around GEO principles, we help businesses become the authoritative source their industries should be citing. Schedule a discovery call to talk through what a GEO strategy looks like for your business.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>seo</category>
      <category>ai</category>
      <category>contentmarketing</category>
    </item>
    <item>
      <title>Why Next.js and Vercel Are the Best SEO Investment Your Business Can Make in 2026</title>
      <dc:creator>Ryan VerWey</dc:creator>
      <pubDate>Thu, 05 Mar 2026 00:00:00 +0000</pubDate>
      <link>https://forem.com/rverwey/why-nextjs-and-vercel-are-the-best-seo-investment-your-business-can-make-in-2026-2c3a</link>
      <guid>https://forem.com/rverwey/why-nextjs-and-vercel-are-the-best-seo-investment-your-business-can-make-in-2026-2c3a</guid>
      <description>&lt;p&gt;Search engine optimization in 2026 is not just about keywords and backlinks. Google's algorithm has been weighting technical performance aggressively since the Core Web Vitals rollout, and the gap between a fast website and a slow one is now reflected directly in rankings. The businesses winning in organic search are increasingly the ones whose websites load instantly, are structurally clean for crawlers, and deliver a seamless experience on every device. Next.js combined with Vercel's global edge network is the most complete answer to all of those requirements that exists in the modern web stack.&lt;/p&gt;

&lt;h2&gt;
  
  
  Core Web Vitals Are a Hard Ranking Factor - And Most Business Websites Fail Them
&lt;/h2&gt;

&lt;p&gt;Google's Core Web Vitals measure three things: how fast your largest piece of content loads (Largest Contentful Paint, or LCP), how quickly your page responds to the first user interaction (Interaction to Next Paint, or INP), and how much your layout shifts around as the page loads (Cumulative Layout Shift, or CLS). Sites that score in the Good range on all three receive a measurable ranking boost in competitive search results. The majority of business websites built on drag-and-drop builders or older WordPress themes fail at least one of these metrics, often multiple. Next.js is architected from the ground up to make good Core Web Vitals scores achievable without heroic developer effort.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Next.js Renders Pages Gives You a Structural SEO Advantage
&lt;/h2&gt;

&lt;p&gt;Traditional single-page applications built in plain React send an almost empty HTML document to the browser and rely on JavaScript to render the content. Googlebot can execute JavaScript, but it introduces indexing delays and creates situations where your content is crawled later or less reliably than a statically or server-rendered page. Next.js solves this by rendering HTML on the server before it ever reaches the browser or a crawler. Googlebot receives a fully populated HTML document the moment it crawls your URL - every heading, paragraph, and link is immediately visible and indexable with no JavaScript execution required. This is a structural advantage that is simply not available to businesses on most website builders.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next.js App Router Features That Google Directly Rewards
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Built-in Metadata API: define page titles, descriptions, Open Graph tags, and structured data with type-safe TypeScript objects - no plugin required&lt;/li&gt;
&lt;li&gt;Automatic sitemap generation: export a sitemap.ts file and Next.js builds and serves a dynamic XML sitemap that updates as you add content&lt;/li&gt;
&lt;li&gt;Image optimization: the next/image component automatically serves WebP or AVIF formats, resizes images to the viewport, and uses lazy loading by default - directly improving LCP scores&lt;/li&gt;
&lt;li&gt;Font optimization: next/font eliminates layout shift from web fonts loading by inlining font CSS and preloading font files automatically&lt;/li&gt;
&lt;li&gt;Route-level code splitting: each page only loads the JavaScript it actually needs, keeping bundle sizes minimal and Time to Interactive fast&lt;/li&gt;
&lt;li&gt;React Server Components: pages built with RSC ship zero client-side JavaScript for static content, dramatically reducing bundle size and improving all performance metrics&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Vercel's Edge Network: The Speed Advantage Your Competitors Don't Have
&lt;/h2&gt;

&lt;p&gt;Page speed is not just about code quality - it is about proximity. When a user in Miami requests your website and your server is in Virginia, the round-trip time adds latency to every single request. Vercel operates a global edge network with nodes on six continents. Your website's pages are cached and served from whichever node is physically closest to each visitor. A user in Los Angeles gets your site from a California edge node. A user in London gets it from a European node. The result is that your Time to First Byte (TTFB) - one of the earliest signals Google measures - stays fast for every visitor regardless of geography. This is infrastructure that used to require a dedicated CDN contract and significant engineering work. With Vercel it is the default.&lt;/p&gt;

&lt;h2&gt;
  
  
  Automatic Image Optimization That Directly Improves Your LCP Score
&lt;/h2&gt;

&lt;p&gt;Images are the single most common reason a page fails its Largest Contentful Paint score. An unoptimized JPEG uploaded straight from a camera is typically 3 to 8 megabytes and forces the browser to download and decode an enormous file before rendering. The next/image component solves this automatically: it converts images to modern WebP or AVIF formats (30 to 50% smaller than JPEG at equivalent quality), resizes the image to match the screen size requesting it, and adds lazy loading so images below the fold do not block the initial page render. The hero image on your homepage - almost always the LCP element - gets a priority hint that tells the browser to fetch it before anything else. These are optimizations that many web development agencies implement manually and inconsistently. In Next.js they are the default behavior.&lt;/p&gt;

&lt;h2&gt;
  
  
  Structured Data and the Metadata API: How Next.js Makes Schema Easy
&lt;/h2&gt;

&lt;p&gt;Structured data (Schema.org markup) tells Google exactly what your content represents - whether it is a local business, a blog article, a product, a FAQ, or a review. Rich results in Google Search - the star ratings, FAQ dropdowns, and knowledge panels you see on certain results - are only available to pages that implement structured data correctly. In older web stacks, adding Schema.org markup meant maintaining raw JSON-LD scripts scattered across template files. Next.js lets you export typed metadata objects from each page and generate structured data programmatically from your content. For a business with a blog, every article page can automatically generate Article schema. Every service page can generate Service schema. This level of structured data coverage, done consistently across every page, is a meaningful signal to Google about the quality and organization of your site.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The businesses that will dominate search in 2026 are not the ones with the most content - they are the ones with the fastest, most technically correct websites. Next.js and Vercel make that achievable for businesses of any size.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What These Performance Gains Actually Mean for Your Rankings
&lt;/h2&gt;

&lt;p&gt;The performance advantages of Next.js and Vercel are not theoretical. Businesses that migrate from WordPress or a website builder to a properly built Next.js site typically see LCP drop from the 3 to 5 second range into under one second, Core Web Vitals go from Needs Improvement or Poor to fully Good, and organic click-through rate improve because fast-loading pages rank higher and earn more impressions. Combined with clean semantic HTML that search engines can parse without JavaScript, a full sitemap that updates automatically, and properly structured metadata on every page, a Next.js site built correctly gives you a technical SEO foundation that most small business websites simply do not have. That foundation compounds over time - every piece of content you add lands on a platform that Google trusts and rewards.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Echo Effect builds all client websites on Next.js deployed to Vercel - because it gives our clients a technical SEO advantage from day one. If your current website is slow, hard to update, or failing its Core Web Vitals, schedule a discovery call and we will show you exactly what a rebuild would do for your search rankings and conversion rate.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>seo</category>
      <category>webdesign</category>
      <category>webdev</category>
    </item>
    <item>
      <title>7 Things Every Local Business Website Must Have in 2025</title>
      <dc:creator>Ryan VerWey</dc:creator>
      <pubDate>Tue, 20 Jan 2026 00:00:00 +0000</pubDate>
      <link>https://forem.com/rverwey/7-things-every-local-business-website-must-have-in-2025-2pa8</link>
      <guid>https://forem.com/rverwey/7-things-every-local-business-website-must-have-in-2025-2pa8</guid>
      <description>&lt;p&gt;Most local business websites are built backward. They lead with the company history, bury the contact information, and assume visitors will scroll to find a reason to care. In 2025, the average visitor decides within three seconds whether to stay or leave - and that decision happens almost entirely above the fold. These seven elements are what separate a website that generates business from one that just occupies a domain.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. A Clear Value Proposition Above the Fold
&lt;/h2&gt;

&lt;p&gt;Your homepage headline should answer one question immediately: what do you do and who do you do it for. "Full-Service HVAC in Tampa Bay - Same-Day Service Available" tells a visitor exactly what they need to know in under five seconds. Vague headlines like "Committed to Excellence" or your company tagline tell visitors nothing actionable. If a new visitor cannot determine your core service and location within three seconds of landing on your site, you are losing business to competitors whose sites communicate more clearly.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Responsive, Mobile-First Design
&lt;/h2&gt;

&lt;p&gt;Over 65% of local search traffic now comes from mobile devices. Google has used mobile-first indexing since 2019, which means Google primarily crawls and evaluates your site based on how it appears on a phone - not a desktop. A site that looks polished on a laptop but breaks on a phone is actively hurting your search rankings and sending your highest-intent traffic straight to competitors. Every element - navigation, buttons, forms, images, font sizes - must be designed for a 375px screen first.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Page Load Speed Under 3 Seconds
&lt;/h2&gt;

&lt;p&gt;Google uses Core Web Vitals as a direct ranking factor, and 53% of mobile visitors abandon a page that takes longer than three seconds to load. For local businesses competing in search results, a slow site is a double penalty: you rank lower and you lose the visitors who do find you. Most speed issues trace back to unoptimized images, unused JavaScript, and low-quality hosting. These are all solvable problems that have an outsized impact on your bottom line.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Trust Signals Throughout
&lt;/h2&gt;

&lt;p&gt;Trust signals are the elements that answer a visitor's unspoken question: "Why should I choose this business over everyone else?" This includes Google review snippets, industry certifications and licenses, before-and-after photos, named testimonials with photos, and any press mentions or awards. Professional photography of real work, real team members, and real job sites builds trust far more effectively than stock images. Every page where a visitor might be deciding whether to contact you should have at least one trust element visible.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;People do not buy from businesses they found online. They buy from businesses that felt trustworthy when they got there.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  5. One Primary CTA on Every Page
&lt;/h2&gt;

&lt;p&gt;The surest way to kill conversions is to give visitors too many options. When someone arrives on your service page, they should see one prominent action: "Get a Free Quote," "Schedule a Call," or "Book Now." Secondary links - to other services, to your blog, to your About page - are fine to include, but there should be one action that visually dominates the page. Confusion is the enemy of conversion, and a page with five competing calls to action generates fewer contacts than a page with one clear path.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Contact Information and Location Everywhere
&lt;/h2&gt;

&lt;p&gt;Your phone number should appear in the header of every page - clickable on mobile - and again in the footer. Your physical address (if you have one) or service area should be visible without scrolling on your Contact page. A Google Maps embed on your Contact page is a minor addition with significant trust and SEO value. Visitors who have to hunt for your contact information leave. Put it everywhere.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Local Schema Markup
&lt;/h2&gt;

&lt;p&gt;Schema markup is structured data embedded in your page code that tells Google precisely what your business is, where it operates, its hours, its reviews, and its services. Google uses this data to populate rich results in search - star ratings, service areas, and business details that appear directly in the search results page before a visitor even clicks your link. For local businesses this markup is a competitive advantage that most DIY websites and even many agencies overlook entirely.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Echo Effect builds conversion-optimized websites for local businesses from the ground up - mobile-first design, Core Web Vitals performance, local SEO structure, and all seven of these elements built in from day one. Contact us for a free website audit.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>webdesign</category>
      <category>seo</category>
      <category>conversion</category>
    </item>
    <item>
      <title>Building an Intelligent Portfolio Filtering System with Next.js and React Context</title>
      <dc:creator>Ryan VerWey</dc:creator>
      <pubDate>Sun, 23 Nov 2025 23:25:59 +0000</pubDate>
      <link>https://forem.com/rverwey/building-an-intelligent-portfolio-filtering-system-with-nextjs-and-react-context-di7</link>
      <guid>https://forem.com/rverwey/building-an-intelligent-portfolio-filtering-system-with-nextjs-and-react-context-di7</guid>
      <description>&lt;h2&gt;
  
  
  The Challenge: Helping Recruiters Find What Matters
&lt;/h2&gt;

&lt;p&gt;When building a portfolio website, one of the biggest challenges is information overload. You want to showcase everything you've built - your full-stack capabilities, data projects, UI/UX work, project management experience - but recruiters and hiring managers often have specific interests. &lt;/p&gt;

&lt;p&gt;A front-end recruiter doesn't need to sift through your database optimization projects, and a data science manager shouldn't have to scroll past your Angular components.&lt;/p&gt;

&lt;p&gt;The solution? An intelligent filtering system that asks visitors what they're interested in and quietly highlights the most relevant content throughout the entire portfolio.&lt;/p&gt;

&lt;h2&gt;
  
  
  Design Goals and UX Principles
&lt;/h2&gt;

&lt;p&gt;Before writing a single line of code, I established clear design goals based on fundamental UX principles:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Progressive Disclosure
&lt;/h3&gt;

&lt;p&gt;Don't overwhelm users with all filtering options at once. Start with high-level interests (Full Stack, Web Development, Data, etc.) and let the system handle the complexity behind the scenes.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Persistence Without Friction
&lt;/h3&gt;

&lt;p&gt;Filter selections should persist across page navigation and browser sessions, but users should be able to adjust them at any time without going through a complex flow.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Subtle Over Aggressive
&lt;/h3&gt;

&lt;p&gt;Visual highlighting should guide attention without screaming. Instead of hiding non-matching content (which feels restrictive), use subtle visual cues - lighter borders, reduced opacity - to naturally draw the eye to relevant items.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. F-Pattern Optimization
&lt;/h3&gt;

&lt;p&gt;Leverage the natural F-shaped reading pattern by placing filter controls at the top of each page and using horizontal layouts that align with how users scan content.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. First-Time User Experience
&lt;/h3&gt;

&lt;p&gt;Show a welcome modal only once per session, making it feel like a helpful concierge rather than an annoying popup.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. URL-Based Context Setting
&lt;/h3&gt;

&lt;p&gt;Enable direct linking to filtered views through URL parameters, allowing users to share specific portfolio contexts or bookmark their preferred view.&lt;/p&gt;

&lt;h2&gt;
  
  
  Technical Architecture
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Technology Stack
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Next.js 14 with App Router for server-side rendering and optimal performance&lt;/li&gt;
&lt;li&gt;React Context API for global state management&lt;/li&gt;
&lt;li&gt;TypeScript for type safety across the entire filtering system&lt;/li&gt;
&lt;li&gt;localStorage and sessionStorage for different persistence needs&lt;/li&gt;
&lt;li&gt;Tailwind CSS for responsive, utility-first styling&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Core Components
&lt;/h3&gt;

&lt;p&gt;The system is built around four key architectural pieces:&lt;/p&gt;

&lt;h4&gt;
  
  
  1. Central Filter Configuration (&lt;code&gt;lib/filters.ts&lt;/code&gt;)
&lt;/h4&gt;

&lt;p&gt;The heart of the system is a centralized configuration file that maps high-level interest categories to specific technology tags:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;filterPresets&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;InterestCategory&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;FilterPreset&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;full-stack&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;full-stack&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Full Stack Development&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;End-to-end application development with modern frameworks&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;React&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Angular&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Vue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Next.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Node.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Express&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.NET&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;C#&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;JavaScript&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;TypeScript&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Python&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SQL&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SQL Server&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;MongoDB&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;REST APIs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;CI/CD&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Docker&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Kubernetes&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="c1"&gt;// ... more presets&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This approach follows the Single Source of Truth principle - all filtering logic stems from this one configuration, making it easy to add new categories or adjust mappings.&lt;/p&gt;

&lt;p&gt;The file also includes helper functions for matching logic:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;matchesFilters&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;itemTags&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="nx"&gt;activeFilters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]):&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;activeFilters&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filter&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;itemTags&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getFilterStrength&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;itemTags&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="nx"&gt;activeFilters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]):&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;activeFilters&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;matches&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;itemTags&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tag&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;activeFilters&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;matches&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;activeFilters&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;getFilterStrength&lt;/code&gt; function is crucial - it returns a 0-1 score indicating how strongly an item matches active filters, enabling nuanced visual highlighting.&lt;/p&gt;

&lt;h4&gt;
  
  
  2. Global State Management (&lt;code&gt;contexts/FilterContext.tsx&lt;/code&gt;)
&lt;/h4&gt;

&lt;p&gt;React Context provides global state without prop drilling. The FilterContext manages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;activeFilters: Array of currently active technology tags&lt;/li&gt;
&lt;li&gt;selectedInterest: The high-level category chosen by the user&lt;/li&gt;
&lt;li&gt;hasSeenWelcome: Whether the user has dismissed the welcome modal&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The implementation uses lazy initialization to avoid hydration mismatches, a common gotcha in Next.js:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;activeFilters&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setActiveFilters&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;undefined&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;stored&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;portfolioFilters&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;stored&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;stored&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This pattern ensures localStorage is only accessed on the client, preventing server/client render discrepancies.&lt;/p&gt;

&lt;h3&gt;
  
  
  URL Parameter Integration
&lt;/h3&gt;

&lt;p&gt;A powerful feature of the filtering system is the ability to set filters directly through URL parameters. This enables shareable links and bookmarkable views:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;loadInitialInterest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;InterestCategory&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;undefined&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;

  &lt;span class="c1"&gt;// Check URL parameter first&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;urlParams&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URLSearchParams&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;search&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;urlInterest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;urlParams&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;interest&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;urlInterest&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;urlInterest&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;filterPresets&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;urlInterest&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;InterestCategory&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// Fall back to stored preference&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;stored&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;portfolioInterest&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;stored&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;stored&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;InterestCategory&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This creates several powerful use cases:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Direct Links&lt;/strong&gt;: Share &lt;code&gt;https://www.ryanverwey.dev/?interest=full-stack&lt;/code&gt; to showcase full-stack work&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Resume Integration&lt;/strong&gt;: Add filtered links to your resume for role-specific portfolios&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Job Applications&lt;/strong&gt;: Send &lt;code&gt;?interest=web-development&lt;/code&gt; to a front-end position&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Social Media&lt;/strong&gt;: Tweet different filtered views for different audiences&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The URL parameter takes precedence over stored preferences, ensuring shared links always display the intended context. After initial load, the selection is persisted to localStorage for subsequent navigation.&lt;/p&gt;

&lt;p&gt;The context also provides methods for manipulating filters:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;applyPreset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useCallback&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;interest&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;InterestCategory&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;preset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;filterPresets&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;interest&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="nf"&gt;setSelectedInterest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;interest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nf"&gt;setActiveFilters&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;interest&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;browse-all&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;preset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  3. &lt;strong&gt;Welcome Modal&lt;/strong&gt; (&lt;code&gt;components/WelcomeModal.tsx&lt;/code&gt;)
&lt;/h4&gt;

&lt;p&gt;First impressions matter. The welcome modal appears once per session, presenting six interest options in a clean, scannable grid:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;fixed&lt;/span&gt; &lt;span class="na"&gt;inset-0&lt;/span&gt; &lt;span class="na"&gt;z-50&lt;/span&gt; &lt;span class="na"&gt;flex&lt;/span&gt; &lt;span class="na"&gt;items-center&lt;/span&gt; &lt;span class="na"&gt;justify-center&lt;/span&gt; &lt;span class="na"&gt;p-4&lt;/span&gt; 
     &lt;span class="na"&gt;bg-black&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="na"&gt;50&lt;/span&gt; &lt;span class="na"&gt;backdrop-blur-sm&lt;/span&gt; &lt;span class="na"&gt;animate-in&lt;/span&gt; &lt;span class="na"&gt;fade-in&lt;/span&gt; &lt;span class="na"&gt;duration-200&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;relative&lt;/span&gt; &lt;span class="na"&gt;w-full&lt;/span&gt; &lt;span class="na"&gt;max-w-lg&lt;/span&gt; &lt;span class="na"&gt;bg-white&lt;/span&gt; &lt;span class="na"&gt;dark&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="na"&gt;bg-zinc-900&lt;/span&gt; 
       &lt;span class="na"&gt;rounded-xl&lt;/span&gt; &lt;span class="na"&gt;shadow-xl&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* Interest options in a 2-column grid */&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Key UX decisions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Backdrop blur creates depth and focuses attention&lt;/li&gt;
&lt;li&gt;Max-width constraint ensures readability on large screens&lt;/li&gt;
&lt;li&gt;Skip option respects user autonomy, no forced interaction&lt;/li&gt;
&lt;li&gt;sessionStorage (not localStorage) means the modal returns in new sessions, gently reminding return visitors they can customize their view&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  4. Filter Bar (&lt;code&gt;components/FilterBar.tsx&lt;/code&gt;)
&lt;/h4&gt;

&lt;p&gt;The FilterBar appears consistently at the top of filtered pages, providing both context and control:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="na"&gt;bg-white&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="na"&gt;80&lt;/span&gt; &lt;span class="na"&gt;dark&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="na"&gt;bg-zinc-950&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="na"&gt;80&lt;/span&gt; &lt;span class="na"&gt;backdrop-blur-sm&lt;/span&gt; 
     &lt;span class="na"&gt;border-b&lt;/span&gt; &lt;span class="na"&gt;border-zinc-200&lt;/span&gt; &lt;span class="na"&gt;dark&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="na"&gt;border-zinc-800&lt;/span&gt; &lt;span class="na"&gt;mb-8&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"container mx-auto max-w-6xl px-4 py-4"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* Interest selector buttons */&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* Active filters display */&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The semi-transparent background with backdrop blur creates a modern, elevated feel while maintaining visual hierarchy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementation Patterns
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Highlighting Strategy
&lt;/h3&gt;

&lt;p&gt;The highlighting system uses a three-tier approach based on match strength:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getHighlightClass&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[])&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;activeFilters&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;strength&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getFilterStrength&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;activeFilters&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;strength&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ring-1 ring-zinc-400 dark:ring-zinc-600&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;strength&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ring-1 ring-zinc-300 dark:ring-zinc-700&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;opacity-40&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This creates a visual hierarchy:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Strong matches (50%+ tags match) get a noticeable but subtle ring&lt;/li&gt;
&lt;li&gt;Partial matches get a lighter ring&lt;/li&gt;
&lt;li&gt;Non-matches are dimmed but still visible&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This follows the Recognition Over Recall principle - users can see all content but immediately recognize what's most relevant.&lt;/p&gt;

&lt;h3&gt;
  
  
  Type Safety Throughout
&lt;/h3&gt;

&lt;p&gt;TypeScript ensures compile-time safety across the entire system:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;InterestCategory&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; 
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;full-stack&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;web-development&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;back-end&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;data&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;project-management&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;browse-all&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;FilterPreset&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;InterestCategory&lt;/span&gt;
  &lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
  &lt;span class="nx"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Union types for categories prevent typos and enable autocomplete. Every filter interaction is type-checked, reducing runtime errors.&lt;/p&gt;

&lt;h3&gt;
  
  
  Performance Considerations
&lt;/h3&gt;

&lt;p&gt;Several optimizations ensure the filtering system doesn't impact performance:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Memoization with useCallback: Filter operations are memoized to prevent unnecessary re-renders&lt;/li&gt;
&lt;li&gt;Lazy initialization: State is initialized once, not on every render&lt;/li&gt;
&lt;li&gt;Selective filtering: Projects page doesn't apply filtering logic (only highlighting) to avoid jarring layout shifts&lt;/li&gt;
&lt;li&gt;Static generation: Pages remain statically generated, filtering happens entirely client-side&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Accessibility
&lt;/h3&gt;

&lt;p&gt;The system incorporates several accessibility features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Semantic HTML: Buttons use proper &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt; elements, not divs&lt;/li&gt;
&lt;li&gt;Focus management: Keyboard navigation works naturally through filter options&lt;/li&gt;
&lt;li&gt;Color contrast: All text meets WCAG AA standards&lt;/li&gt;
&lt;li&gt;Reduced motion: Animations respect &lt;code&gt;prefers-reduced-motion&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Screen reader labels: Filter buttons clearly announce their state&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Page-Specific Implementations
&lt;/h2&gt;

&lt;h3&gt;
  
  
  About Page
&lt;/h3&gt;

&lt;p&gt;The About page uses highlighting on both experience cards and skill buttons:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;renderSkillButton&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;skill&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isFiltered&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;activeFilters&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;skill&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt;
      &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;`block w-full px-3 py-2 text-sm rounded-lg border transition-all &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;
        &lt;span class="nx"&gt;isFiltered&lt;/span&gt;
          &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;border-zinc-400 bg-zinc-100 dark:bg-zinc-800 font-medium&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
          &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;border-zinc-200 dark:border-zinc-700 hover:border-zinc-300&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;skill&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This creates a cohesive experience where skills relevant to the selected interest are subtly emphasized.&lt;/p&gt;

&lt;h3&gt;
  
  
  Projects Page
&lt;/h3&gt;

&lt;p&gt;Projects showcase a unique challenge - they have both category filters (Web Apps, Websites, Tools) and global filters. The solution:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;filteredProjects&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;projects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;categoryMatch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;selectedCategory&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;All&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;category&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;selectedCategory&lt;/span&gt;
  &lt;span class="c1"&gt;// Don't filter by activeFilters - only highlight&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;categoryMatch&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Projects remain visible regardless of global filters (avoiding confusing disappearing content), but the highlighting guides attention to relevant tech stacks.&lt;/p&gt;

&lt;h3&gt;
  
  
  Experience Page
&lt;/h3&gt;

&lt;p&gt;The Experience page combines a collapsible skill sidebar with the global FilterBar:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;toggleSkill&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;skill&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;activeFilters&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;skill&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;removeFilter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;skill&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;addFilter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;skill&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This allows granular control - users can click individual skills to add them to filters, creating a dynamic, exploratory experience.&lt;/p&gt;

&lt;h3&gt;
  
  
  Blog Page
&lt;/h3&gt;

&lt;p&gt;The blog integrates with existing category/tag filters while syncing with global state:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;currentTag&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;activeFilters&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;currentTag&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;addFilter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;currentTag&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;currentTag&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;activeFilters&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;addFilter&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This creates continuity - selecting a tag in the blog updates global filters, so navigating to the Experience page automatically highlights related skills.&lt;/p&gt;

&lt;h2&gt;
  
  
  UI/UX Principles in Action
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Visual Hierarchy
&lt;/h3&gt;

&lt;p&gt;The entire system leverages Gestalt principles of perception:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Proximity: Related controls are grouped together&lt;/li&gt;
&lt;li&gt;Similarity: Matching tags share visual styling&lt;/li&gt;
&lt;li&gt;Continuity: The FilterBar maintains consistent positioning across pages&lt;/li&gt;
&lt;li&gt;Figure-Ground: Highlighted items naturally pop against dimmed content&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Cognitive Load Reduction
&lt;/h3&gt;

&lt;p&gt;Rather than forcing users to understand tag taxonomies, the system presents intent-based presets:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"I'm interested in Full Stack Development" → System applies 20+ relevant tags&lt;/li&gt;
&lt;li&gt;User never needs to know the underlying complexity&lt;/li&gt;
&lt;li&gt;Follows Don Norman's principle of mapping mental models to system models&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Feedback and Affordances
&lt;/h3&gt;

&lt;p&gt;Every interaction provides clear feedback:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Hover states signal clickability&lt;/li&gt;
&lt;li&gt;Active states show current selection&lt;/li&gt;
&lt;li&gt;Transition animations create continuity (but are subtle, 150ms or less)&lt;/li&gt;
&lt;li&gt;Read-only badges (active filters) provide context without inviting accidental interaction&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Mobile Responsiveness
&lt;/h3&gt;

&lt;p&gt;The system adapts to smaller screens:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.flex-col&lt;/span&gt; &lt;span class="nt"&gt;sm&lt;/span&gt;&lt;span class="nd"&gt;:flex-row&lt;/span&gt; &lt;span class="nt"&gt;sm&lt;/span&gt;&lt;span class="nd"&gt;:items-center&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On mobile, filter buttons stack vertically. On desktop, they flow horizontally. This maintains usability across all viewport sizes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lessons Learned
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What Worked Well
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Central configuration&lt;/strong&gt;: Having one source of truth made the system easy to extend and debug&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Subtle highlighting&lt;/strong&gt;: Users report the filtering feels "helpful, not pushy"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Browse All as default&lt;/strong&gt;: Starting unfiltered respects user agency and prevents confusion&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Type safety&lt;/strong&gt;: TypeScript caught numerous bugs during development&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  What I'd Do Differently
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Analytics integration&lt;/strong&gt;: Tracking which interests users select would inform content strategy&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fuzzy matching&lt;/strong&gt;: Currently, matching is exact - "React" vs "ReactJS" won't match. A fuzzy matcher would be more forgiving&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A/B testing&lt;/strong&gt;: Test different highlight intensities to optimize for conversion&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Filter combinations&lt;/strong&gt;: Allow multiple interest selections simultaneously for hybrid roles&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Performance Metrics
&lt;/h3&gt;

&lt;p&gt;After deployment:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;First Contentful Paint: &amp;lt; 1.2s&lt;/li&gt;
&lt;li&gt;Largest Contentful Paint: &amp;lt; 2.0s
&lt;/li&gt;
&lt;li&gt;Cumulative Layout Shift: 0.01 (excellent)&lt;/li&gt;
&lt;li&gt;Time to Interactive: &amp;lt; 2.5s&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The filtering system adds negligible overhead - most logic is simple array operations, and React's reconciliation handles updates efficiently.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Building an intelligent filtering system isn't just about functionality - it's about understanding user intent and creating an interface that feels anticipatory rather than reactive. By combining thoughtful UX principles, modern React patterns, and performant architecture, we created a system that helps visitors find exactly what they're looking for without feeling restrictive.&lt;/p&gt;

&lt;p&gt;The key insights:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Start with user research&lt;/strong&gt;: Understand why people visit your portfolio&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Map intents to implementations&lt;/strong&gt;: High-level categories → specific tags&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prioritize subtlety&lt;/strong&gt;: Guide, don't force&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Maintain consistency&lt;/strong&gt;: Same patterns across all pages&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enable shareability&lt;/strong&gt;: URL parameters make filtered views shareable&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test on real users&lt;/strong&gt;: Assumptions ≠ reality&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This system demonstrates that sophisticated features don't require complex UIs. By leveraging fundamental design principles and modern web technologies, we created an experience that feels effortless - the hallmark of great design.&lt;/p&gt;

&lt;h3&gt;
  
  
  Real-World Application
&lt;/h3&gt;

&lt;p&gt;The URL-based filtering has proven particularly valuable in job applications. Instead of sending recruiters to a generic portfolio, I can now send role-specific links:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Full-Stack Position&lt;/strong&gt;: &lt;code&gt;?interest=full-stack&lt;/code&gt; - Highlights end-to-end development work&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Front-End Role&lt;/strong&gt;: &lt;code&gt;?interest=web-development&lt;/code&gt; - Emphasizes UI components and React expertise&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Backend Position&lt;/strong&gt;: &lt;code&gt;?interest=back-end&lt;/code&gt; - Showcases API development and database work&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data Role&lt;/strong&gt;: &lt;code&gt;?interest=data&lt;/code&gt; - Features analytics, ETL, and visualization projects&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This targeted approach increases engagement by showing recruiters exactly what they're looking for, without making them hunt through unrelated content.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Technologies Used&lt;/strong&gt;: React, Next.js 14, TypeScript, Tailwind CSS, React Context API&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Methodologies&lt;/strong&gt;: User-Centered Design, Progressive Enhancement, Mobile-First Development, Accessibility-First&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;UI/UX Principles&lt;/strong&gt;: Progressive Disclosure, Recognition Over Recall, F-Pattern Layout, Gestalt Principles, Visual Hierarchy&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>typescript</category>
      <category>learning</category>
      <category>portfolio</category>
    </item>
    <item>
      <title>Why Utility Classes in CSS Make Modern Front-End Development Faster, Cleaner, and More Scalable</title>
      <dc:creator>Ryan VerWey</dc:creator>
      <pubDate>Sat, 15 Nov 2025 18:20:10 +0000</pubDate>
      <link>https://forem.com/rverwey/why-utility-classes-in-css-make-modern-front-end-development-faster-cleaner-and-more-scalable-1ddf</link>
      <guid>https://forem.com/rverwey/why-utility-classes-in-css-make-modern-front-end-development-faster-cleaner-and-more-scalable-1ddf</guid>
      <description>&lt;h2&gt;
  
  
  A practical guide to why “utility-first” CSS is winning; and how to use it effectively.
&lt;/h2&gt;




&lt;h2&gt;
  
  
  Introduction: The War Against Bloated Stylesheets
&lt;/h2&gt;

&lt;p&gt;Every front-end engineer eventually hits the same wall; a stylesheet so massive and disorganized that every update feels like defusing a bomb. Overrides pile up, class names drift, and the entire CSS layer becomes a fragile maze.&lt;/p&gt;

&lt;p&gt;Utility-first CSS emerged as a response to these long-standing pain points; it’s not a fad; it’s a pragmatic architectural pattern that shifts how modern teams write, scale, and maintain UI code. Recent industry reflections highlight that developers are increasingly turning to utility-first approaches because they offer a more predictable and maintainable styling model (Pickering, 2024).&lt;/p&gt;




&lt;h2&gt;
  
  
  The Problem: Traditional CSS Scales Poorly
&lt;/h2&gt;

&lt;p&gt;For decades, CSS has favored “semantic class names,” which promise clarity but often backfire in large systems.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Naming Complexity
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Endless debates over &lt;code&gt;.btn-primary&lt;/code&gt; vs. &lt;code&gt;.button--primary&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Inconsistent naming conventions across teams&lt;/li&gt;
&lt;li&gt;Cognitive load introduced by abstract class semantics&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. CSS That Only Grows
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Stylesheets rarely shrink.&lt;/li&gt;
&lt;li&gt;Apps accumulate years of cruft, unused selectors, legacy overrides, and specificity battles.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. Cascading Side Effects
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Changing the styling of one class often has unpredictable ripple effects across the application.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. Slow Iteration
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Developers bounce between HTML/JSX and CSS files; wasting time and mental energy on context switching.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Academic comparisons confirm that traditional CSS approaches often lead to inflated stylesheets and maintainability issues; especially in larger UI systems (Nandan et al., 2024).&lt;/p&gt;




&lt;h2&gt;
  
  
  The Solution: Utility-First CSS
&lt;/h2&gt;

&lt;p&gt;Utility-first CSS replaces named abstractions with &lt;strong&gt;small, single-purpose classes&lt;/strong&gt; that directly represent styling intent.&lt;/p&gt;

&lt;p&gt;Example:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;button class="px-4 py-2 bg-blue-600 text-white rounded-lg"&amp;gt;
  Save
&amp;lt;/button&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Instead of describing what an element “is,” utility classes tell the browser exactly what to do.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why This Model Works
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Predictable:&lt;/strong&gt; Every utility does one thing, consistently.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Local:&lt;/strong&gt; Styles live next to the markup they affect.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Explicit:&lt;/strong&gt; No hunting through CSS files to understand design decisions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fast:&lt;/strong&gt; Less cognitive overhead; fewer naming decisions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Industry practitioners argue that this model leads to dramatically faster development cycles and significantly fewer styling regressions (Wolff, 2019).&lt;/p&gt;




&lt;h2&gt;
  
  
  Core Benefits of Utility Classes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Faster Development Through Composability
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Utilities act as UI “lego blocks,” allowing rapid assembly of interfaces without external CSS files.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Smaller, More Performant CSS Bundles
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Utility-first frameworks like Tailwind purge unused classes; often producing production bundles under 10 KB.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. Style Consistency at Scale
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Shared spacing scale
&lt;/li&gt;
&lt;li&gt;Unified color tokens
&lt;/li&gt;
&lt;li&gt;Consistent typography
&lt;/li&gt;
&lt;li&gt;Predictable responsive behavior
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. Fewer Global CSS Conflicts
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Utility classes avoid deep cascading rules; refactoring becomes safer and more reversible.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  5. Perfect Fit for Component Architectures
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;React, Vue, Svelte, and modern design systems thrive on locality; styles stay close to logic and markup.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Real-World Use Cases
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Design Systems
&lt;/h3&gt;

&lt;p&gt;Utility-first CSS works as a low-level foundation for tokens and primitives; spacing, color, typography, grid utilities, and more.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Rapid Prototyping
&lt;/h3&gt;

&lt;p&gt;Teams can sketch entire pages in minutes using existing utility classes.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Large Engineering Teams
&lt;/h3&gt;

&lt;p&gt;Utility classes level the playing field; senior and junior devs can build consistent UI without deep CSS expertise.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. High-Churn UI Work
&lt;/h3&gt;

&lt;p&gt;Startups and product teams shipping daily benefit from the agility utility classes provide.&lt;/p&gt;

&lt;p&gt;Nandan et al. (2024) found that several utility-first frameworks significantly improved iteration speed and reduced styling-related defects when compared to traditional CSS methodologies.&lt;/p&gt;




&lt;h2&gt;
  
  
  Common Pitfalls and How to Avoid Them
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ❌ “Utility classes make HTML messy.”
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; Group utilities logically, use formatters, and extract components &lt;strong&gt;only&lt;/strong&gt; when patterns stabilize.&lt;/p&gt;

&lt;h3&gt;
  
  
  ❌ “It feels like inline styles.”
&lt;/h3&gt;

&lt;p&gt;Utilities differ from inline styles in key ways:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;They support responsive variants
&lt;/li&gt;
&lt;li&gt;They work with tokens
&lt;/li&gt;
&lt;li&gt;They respect global design constraints
&lt;/li&gt;
&lt;li&gt;They are reusable and compositional
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ❌ “Too many classes on a single element.”
&lt;/h3&gt;

&lt;p&gt;Good component architecture keeps utility usage manageable; extract components only when repetition naturally emerges.&lt;/p&gt;




&lt;h2&gt;
  
  
  Actionable Best Practices
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ✔ Build a stable design token foundation
&lt;/h3&gt;

&lt;p&gt;Spacing scales, color palettes, type ramps, and layout primitives ensure consistent output.&lt;/p&gt;

&lt;h3&gt;
  
  
  ✔ Keep your components granular
&lt;/h3&gt;

&lt;p&gt;Utility-first CSS thrives when components are focused and coherent.&lt;/p&gt;

&lt;h3&gt;
  
  
  ✔ Extract only meaningful abstractions
&lt;/h3&gt;

&lt;p&gt;Avoid over-engineering; let patterns emerge organically.&lt;/p&gt;

&lt;h3&gt;
  
  
  ✔ Document your utility usage
&lt;/h3&gt;

&lt;p&gt;Enhance discoverability and consistency across teams.&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusion: Utility-First CSS Is a Practical, Scalable Evolution
&lt;/h2&gt;

&lt;p&gt;Utility classes are not just a stylistic preference; they solve long-standing structural problems in CSS architecture. They improve predictability, speed up development, reduce regressions, and align perfectly with component-driven interfaces.&lt;/p&gt;

&lt;p&gt;Modern teams adopting utility-first CSS benefit from:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Smaller CSS bundles
&lt;/li&gt;
&lt;li&gt;Faster delivery cycles
&lt;/li&gt;
&lt;li&gt;Fewer styling bugs
&lt;/li&gt;
&lt;li&gt;Cleaner and more maintainable UI codebases
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As both practitioners and researchers have observed, utility-first CSS isn’t just viable; it’s &lt;em&gt;practically superior&lt;/em&gt; for many contemporary front-end workflows (Pickering, 2024; Nandan et al., 2024; Wolff, 2019).&lt;/p&gt;




&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Utility classes reduce naming overhead and improve velocity.&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;They shrink CSS bundle sizes through aggressive pruning.&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;They enforce design consistency and reduce regressions.&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;They integrate naturally with component-based frameworks.&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;They help large teams move faster with fewer styling conflicts.&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;p&gt;Nandan, S., Usha Sree, R., &amp;amp; Mohan, P. (2024). &lt;em&gt;Comparison of Utility-First CSS Frameworks.&lt;/em&gt; Journal of Innovation and Technology, 2024(32). &lt;a href="https://eprints.intimal.edu.my/2069/2/610" rel="noopener noreferrer"&gt;https://eprints.intimal.edu.my/2069/2/610&lt;/a&gt;  &lt;/p&gt;

&lt;p&gt;Pickering, H. (2024, February 19). &lt;em&gt;What is Utility-First CSS?&lt;/em&gt; HeydonWorks. &lt;a href="https://heydonworks.com/article/what-is-utility-first-css/" rel="noopener noreferrer"&gt;https://heydonworks.com/article/what-is-utility-first-css/&lt;/a&gt;  &lt;/p&gt;

&lt;p&gt;Wolff, S. (2019, May 26). &lt;em&gt;Utility-first CSS: Ridiculously fast front-end development for almost every design.&lt;/em&gt; Medium. &lt;a href="https://medium.com/@sascha.wolff/utility-first-css-ridiculously-fast-front-end-development-for-almost-every-design-503130d8fefc" rel="noopener noreferrer"&gt;https://medium.com/@sascha.wolff/utility-first-css-ridiculously-fast-front-end-development-for-almost-every-design-503130d8fefc&lt;/a&gt;  &lt;/p&gt;

</description>
      <category>webdev</category>
      <category>css</category>
      <category>tailwindcss</category>
      <category>frontend</category>
    </item>
    <item>
      <title>Process Mapping Tool</title>
      <dc:creator>Ryan VerWey</dc:creator>
      <pubDate>Sat, 15 Nov 2025 05:12:31 +0000</pubDate>
      <link>https://forem.com/rverwey/process-mapping-tool-5eh4</link>
      <guid>https://forem.com/rverwey/process-mapping-tool-5eh4</guid>
      <description>&lt;h1&gt;
  
  
  ProcessMapper: Visualize, Document, and Improve Team Workflows
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;Demo:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
👉 &lt;a href="https://ryanverwey.github.io/ProcessMapper/" rel="noopener noreferrer"&gt;https://ryanverwey.github.io/ProcessMapper/&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Excerpt
&lt;/h2&gt;

&lt;p&gt;ProcessMapper is a modern web app for building professional process maps and workflow diagrams. With an intuitive drag-and-drop editor, customizable node types, animated edges, background templates, and client-side image export, ProcessMapper turns tribal knowledge into shareable, actionable diagrams.&lt;/p&gt;

&lt;p&gt;Try the live demo above.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Process Mapping Matters
&lt;/h2&gt;

&lt;p&gt;Work really happens in processes — onboarding, incident response, release handoffs, customer journeys, and support escalations. When those processes live in scattered notes or institutional memory, teams lose time and make avoidable mistakes.&lt;/p&gt;

&lt;p&gt;Visual process maps improve:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ownership clarity
&lt;/li&gt;
&lt;li&gt;Handoff visibility
&lt;/li&gt;
&lt;li&gt;Onboarding speed
&lt;/li&gt;
&lt;li&gt;Bottleneck discovery
&lt;/li&gt;
&lt;li&gt;Automation opportunities
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ProcessMapper is designed to make creating those maps &lt;strong&gt;fast, intuitive, and enjoyable&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Product Overview
&lt;/h2&gt;

&lt;p&gt;ProcessMapper is a browser-based process mapping tool built for clarity, speed, and shareability. Its interface is clean and minimal — perfect for non-designers and technical users alike.&lt;/p&gt;

&lt;p&gt;Use it to create maps for presentations, documentation, training, or process improvement work.&lt;/p&gt;




&lt;h2&gt;
  
  
  Feature Breakdown
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Core Editing&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Drag &amp;amp; drop canvas:&lt;/strong&gt; Add and position nodes with mouse or touch.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multiple node shapes:&lt;/strong&gt; Rectangle, circle, diamond, hexagon.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Customizable appearance:&lt;/strong&gt; Per-node color, title, and content fields.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Connections &amp;amp; Edges&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Animated edges:&lt;/strong&gt; Smooth visual flow indicators.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Edge labels:&lt;/strong&gt; Add triggers, conditions, or SLAs.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Connection handles:&lt;/strong&gt; Intuitive directed linking between nodes.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Node Metadata &amp;amp; Editing&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Modal-based editing:&lt;/strong&gt; Titles, descriptions, owners, colors.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Owner + notes fields:&lt;/strong&gt; Reduce ambiguity by assigning responsibility.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Inline quick edit:&lt;/strong&gt; Fast adjustments without leaving the canvas.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Backgrounds &amp;amp; Templates&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Custom backgrounds:&lt;/strong&gt; Upload or link background images (brand templates, diagrams, etc.).
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Background-aligned templates:&lt;/strong&gt; Build consistent visuals quickly.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Export &amp;amp; Sharing&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Client-side PNG export:&lt;/strong&gt; Ideal for slide decks, wikis, SOPs, and reports.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Shareable live demo:&lt;/strong&gt; Let teammates interact with maps instantly.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;UX &amp;amp; Performance&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Responsive layout:&lt;/strong&gt; Works across screen sizes.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Smooth animations:&lt;/strong&gt; Clean transitions improve readability.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Quick User Walkthrough (5 Minutes)
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Open the live demo.
&lt;/li&gt;
&lt;li&gt;Use the toolbar to add a node shape.
&lt;/li&gt;
&lt;li&gt;Click a node → open the edit modal → add title, description, owner.
&lt;/li&gt;
&lt;li&gt;Hover a node, drag from its handle to another to create a connection.
&lt;/li&gt;
&lt;li&gt;Add a label to describe the transition.
&lt;/li&gt;
&lt;li&gt;Add a background image or template.
&lt;/li&gt;
&lt;li&gt;Export the completed map as PNG for documentation or presentations.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Who Benefits Most
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Product Managers &amp;amp; Engineers:&lt;/strong&gt; Visualize release flows and validation steps.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DevOps / SRE:&lt;/strong&gt; Turn runbooks and incident playbooks into stepwise diagrams.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Customer Success &amp;amp; Onboarding:&lt;/strong&gt; Map customer journeys and team handoffs.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Process Improvement Teams:&lt;/strong&gt; Compare “current vs. ideal” flows and find automation gaps.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Technical Highlights (High-Level)
&lt;/h2&gt;

&lt;p&gt;ProcessMapper uses a lightweight, modern front-end architecture with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Interactive node/edge canvas system
&lt;/li&gt;
&lt;li&gt;Modal-driven editing UI
&lt;/li&gt;
&lt;li&gt;Client-side export (no server round-trips)
&lt;/li&gt;
&lt;li&gt;Smooth animations and responsive rendering
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The result is a low-friction experience built for both quick drafts and polished diagrams.&lt;/p&gt;




&lt;h2&gt;
  
  
  Best Practices for Creating Useful Process Maps
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Start with outcomes:&lt;/strong&gt; Define the end goal first.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Assign owners:&lt;/strong&gt; Make responsibility explicit.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Include SLAs or timing:&lt;/strong&gt; Add timing data where delays matter.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use short labels:&lt;/strong&gt; Keep transitions clear and scannable.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Iterate with stakeholders:&lt;/strong&gt; Review maps with people involved in the process.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>webdev</category>
      <category>react</category>
      <category>tailwindcss</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Website Speed and SEO: Why Slow Sites Lose Business Every Day</title>
      <dc:creator>Ryan VerWey</dc:creator>
      <pubDate>Tue, 14 Oct 2025 00:00:00 +0000</pubDate>
      <link>https://forem.com/rverwey/website-speed-and-seo-why-slow-sites-lose-business-every-day-39l4</link>
      <guid>https://forem.com/rverwey/website-speed-and-seo-why-slow-sites-lose-business-every-day-39l4</guid>
      <description>&lt;h2&gt;
  
  
  Core Web Vitals: What They Measure and Why It Matters
&lt;/h2&gt;

&lt;p&gt;Google's Core Web Vitals are three specific performance metrics that measure the real-world experience of using your website. LCP (Largest Contentful Paint) measures how quickly the main content of a page loads - Google's target is under 2.5 seconds. INP (Interaction to Next Paint) measures how quickly the page responds to user input like clicks and taps - target is under 200 milliseconds. CLS (Cumulative Layout Shift) measures visual stability: whether page elements jump around as the page loads, which causes users to accidentally click the wrong thing. These three metrics have been incorporated into Google's ranking algorithm and are now a direct factor in where your site appears in search results.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Real Business Cost of a Slow Website
&lt;/h2&gt;

&lt;p&gt;Performance is not a technical vanity metric - it is directly tied to revenue. Amazon measured that a 100-millisecond delay in page load time cost them 1% in sales. Google found that 53% of mobile visitors abandon a page that takes longer than 3 seconds to load. For a local business generating $50,000 per year from its website, a site that runs at 6 seconds instead of 2 seconds could realistically be losing 20 to 30% of potential inbound leads before those visitors ever see your offer. A slow site is not just an inconvenience - it is a daily, invisible revenue leak.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A 1-second delay in page load time results in a 7% reduction in conversions. That is not a technical problem - it is a revenue problem that looks like a technical one.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The Fixes That Deliver the Most Impact
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Optimize and compress all images - serve WebP format instead of PNG or JPG, use lazy loading for below-the-fold images, and never upload images larger than their display size&lt;/li&gt;
&lt;li&gt;Eliminate unused CSS and JavaScript - most WordPress and website builder installs load dozens of scripts for features you are not using; auditing and removing these dramatically reduces load time&lt;/li&gt;
&lt;li&gt;Enable browser and server-side caching - returning visitors should load your site from their browser cache, not from the server, for every non-dynamic page&lt;/li&gt;
&lt;li&gt;Upgrade your hosting - shared hosting at $5/month is often the single biggest performance bottleneck; managed hosting or a quality VPS is not optional for high-converting sites&lt;/li&gt;
&lt;li&gt;Implement a Content Delivery Network (CDN) - serves your static assets from servers geographically close to your visitors instead of a single origin server&lt;/li&gt;
&lt;li&gt;Minimize third-party scripts - every analytics tool, chat widget, and social plugin adds load time; audit regularly and remove everything that is not delivering value&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Images: The Most Common Culprit
&lt;/h2&gt;

&lt;p&gt;In our experience auditing hundreds of small business websites, unoptimized images are a contributing factor in over 80% of performance issues. The typical pattern looks like this: a business owner takes a high-resolution photo on their phone, uploads the original 8MB file to their website, and the page is now loading an image that is 15x larger than it needs to be at its display size. The fix is straightforward: compress images before upload, convert to WebP format, set explicit width and height attributes to prevent layout shift, and use lazy loading for images that appear below the initial viewport.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hosting Quality Is Not Optional
&lt;/h2&gt;

&lt;p&gt;There is a significant and measurable performance gap between shared hosting at the lowest price tier and managed hosting or a quality VPS. Shared hosting puts your website on a server alongside hundreds or thousands of other sites, competing for the same resources. During peak traffic periods - which often coincide with your highest-value business hours - your site slows to a crawl while the resources are spread thin. For a business that relies on its website to generate leads, treating hosting as the smallest line item in the budget is a false economy.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Echo Effect builds websites that score green on Core Web Vitals out of the box - not as an afterthought. Every project includes performance optimization, image compression pipelines, CDN configuration, and hosting recommendations tailored to your expected traffic. Request a free audit of your current site's performance.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>webdev</category>
      <category>seo</category>
      <category>performance</category>
    </item>
    <item>
      <title>My First JavaScript Web App: Interactive Image Coordinate Finder</title>
      <dc:creator>Ryan VerWey</dc:creator>
      <pubDate>Wed, 06 Nov 2024 22:36:19 +0000</pubDate>
      <link>https://forem.com/rverwey/my-first-javascript-web-app-interactive-image-coordinate-finder-414d</link>
      <guid>https://forem.com/rverwey/my-first-javascript-web-app-interactive-image-coordinate-finder-414d</guid>
      <description>&lt;p&gt;After completing my first JavaScript course at Full Sail University, I was eager to dive into a project that would put my new skills to the test. My goal was to create something functional, intuitive, and practical. This led to the development of my first JavaScript web application: &lt;a href="https://github.com/RyanVerWey/Image-Coordinates" rel="noopener noreferrer"&gt;the Interactive Image Coordinate Finder&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The app allows users to upload a map image, click to record coordinates, and save these as layers with unique names and optional URLs. With this tool, anyone can map or annotate custom images, making it especially handy for visual projects that involve identifying specific regions or landmarks on a web page. In this post, I'll walk you through the app's features, setup, and usage.&lt;/p&gt;

&lt;p&gt;Before we begin, I think its an important caveat that this was also a work project. At my place of business, we work on closed networks meaning I'm not able to use APIs or other libraries so this project may be re creating the wheel or taking an old approach that more modern and advanced developers have improved upon. I can't wait to get there myself, for now lets get into it! &lt;/p&gt;

&lt;p&gt;Leading off we've got a simple web page that displays a map of the middle east. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu9zqrcu5ms1v0fszlj3h.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu9zqrcu5ms1v0fszlj3h.jpg" alt=" " width="800" height="819"&gt;&lt;/a&gt;&lt;br&gt;
I thought to myself, how can we make this useful..ish? Maybe some simple interactions.. But how? After a few hours of searching google, code pen, and a few other sites I realized a low level method is to use coordinates on the image. How do I find them? google sent me to paint which made me face palm in the middle of a quiet office. So I decided to create a tool that can help me grab them. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flwqywlf9zqs8zfromw4z.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flwqywlf9zqs8zfromw4z.jpg" alt=" " width="800" height="350"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I wanted to make a very simple UI that focused on utility. The app will allow users to browse for an image then display it on screen. The users is prompted for a Layer Name and optional link. They then click a bounding box around the area of the image they want to target, in the example I selected Egypt.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff2mutosgxydymn6svv31.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff2mutosgxydymn6svv31.jpg" alt=" " width="800" height="517"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once the users clicks save, the results will be below in the Layers Log where the user is able to copy just the coordinates, copy a line of code that works with the test page linked to the GitHub repo, Edit the layer, or delete the layer all together. &lt;/p&gt;

&lt;p&gt;The end result for the demo is a map of the middle east where the user is able to click on a country and visit its Wikipedia page! I've got a lot of other use cases that I'd like to try out with the tool. &lt;/p&gt;

&lt;p&gt;Thank you for reading!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>beginners</category>
      <category>learning</category>
    </item>
    <item>
      <title>Contrasting UX Design Methods</title>
      <dc:creator>Ryan VerWey</dc:creator>
      <pubDate>Sat, 25 Nov 2023 20:06:02 +0000</pubDate>
      <link>https://forem.com/rverwey/contrasting-ux-design-methods-38b</link>
      <guid>https://forem.com/rverwey/contrasting-ux-design-methods-38b</guid>
      <description>&lt;p&gt;Understanding the intricacies of the User Experience (UX) Design process is fundamental to creating products that not only meet but exceed user expectations. I want to delves into the nuanced steps of this process, emphasizing the importance of thorough testing and iteration in crafting exceptional user experiences. This model is taught by Google. I hope that readers will share their processes and experience in the comments so we can grow and learn together!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F61gfu28rjyld86be7cyw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F61gfu28rjyld86be7cyw.png" alt=" " width="800" height="519"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Brainstorm&lt;/strong&gt;: Embracing Empathy and Definition&lt;br&gt;
The journey of UX design begins with empathy, where understanding the user's needs, challenges, and aspirations is paramount. This stage involves extensive research to gather insights about the target audience. User interviews, surveys, and observation are key tools in this endeavor. The gathered data is then analyzed to create user personas, representing the typical users of the product. These personas guide the design process, ensuring that the product aligns with the real needs and wants of its users.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Define&lt;/strong&gt;. Here, the focus is on distilling the research into a clear problem statement. This stage is critical as it sets the direction for the design process. The problem statement should be user-centric, specific, and actionable, providing a clear framework for ideation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Design&lt;/strong&gt;: Unleashing Creativity and Ideation&lt;br&gt;
With a solid understanding of the user and a clearly defined problem statement, the design team moves into the ideation phase. This stage is all about creativity and exploring a wide array of solutions. Techniques like brainstorming, sketching, and mind mapping are employed to generate diverse ideas. The goal is to think broadly without constraints, considering even the most outlandish solutions, as these can sometimes lead to innovative breakthroughs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prototype&lt;/strong&gt;: Bringing Ideas to Life&lt;br&gt;
The transition from ideation to prototype is where concepts begin to take tangible form. Prototyping is about creating a preliminary version of the product, which can be a simple paper model or a more sophisticated digital prototype. The key is to develop something that can be tested and iterated upon. This stage is less about perfection and more about learning. It's a critical step in understanding how the theoretical ideas perform in practical scenarios.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Test&lt;/strong&gt;: Refining through Feedback&lt;br&gt;
Now, we arrive at the cornerstone of the UI UX Pro methodology: the testing phase. This stage is where the rubber meets the road. Testing involves putting the prototype in the hands of actual users and gathering feedback. The methodologies can vary from usability testing, A/B testing, to more in-depth interviews. The selection of test participants is crucial; they should represent the actual user base of the product.&lt;/p&gt;

&lt;p&gt;The feedback gathered is then meticulously analyzed. This phase is not just about identifying what users like or dislike, but also about understanding why they feel a certain way. The insights gained from testing are invaluable in refining the design. It's a process of iteration, where the design is continuously improved based on user feedback.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;: Iteration as a Pathway to Excellence&lt;br&gt;
In UX design, one principle stands out: the process is never truly complete. Even after a product is launched, the cycle of feedback and iteration continues. This ongoing process ensures that the product evolves with the changing needs and preferences of its users.&lt;/p&gt;

&lt;p&gt;The UX Design process is a comprehensive and user-centric approach. It emphasizes the importance of empathy, clear definition, creative ideation, tangible prototyping, and rigorous testing. By adhering to these principles, designers can create products that are not only functional but also delightful to use, ultimately leading to experiences that users love and cherish.&lt;/p&gt;

&lt;p&gt;Is the process you use at home or at work similar? &lt;/p&gt;

</description>
      <category>webdev</category>
      <category>ux</category>
      <category>beginners</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Hi! I'm new here..</title>
      <dc:creator>Ryan VerWey</dc:creator>
      <pubDate>Sun, 19 Nov 2023 04:12:57 +0000</pubDate>
      <link>https://forem.com/rverwey/hi-im-new-here-2069</link>
      <guid>https://forem.com/rverwey/hi-im-new-here-2069</guid>
      <description>&lt;p&gt;I’m Ryan VerWey, a mid-level developer with a burgeoning passion in the fields of web development and UI/UX design. With my roots firmly planted in SharePoint and Microsoft Power Platform, I've decided to venture into the dynamic world of web development and the creative realm of UI/UX design.&lt;/p&gt;

&lt;p&gt;My journey into this new domain is not just a career shift, but a quest to deepen my understanding and skills in a rapidly evolving digital landscape. Through this blog, I aim to share my experiences, challenges, and the valuable lessons learned along the way.&lt;/p&gt;

&lt;p&gt;I believe that growth comes from stepping out of our comfort zones. This transition marks a significant leap for me, from a specialized focus to a broader and more inclusive perspective in technology. My goal is to not only enhance my technical capabilities but also to develop a keen eye for design and user experience.&lt;/p&gt;

&lt;p&gt;Stay tuned as I navigate through this exciting phase of my career, where every day is a learning opportunity. I look forward to connecting with fellow enthusiasts, exchanging ideas, and growing together in this journey.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6m3equ1u6nb1youez8fw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6m3equ1u6nb1youez8fw.png" alt=" " width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>powerplatform</category>
      <category>newdev</category>
      <category>beginners</category>
    </item>
  </channel>
</rss>
