<?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/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Syd Harris Notes</title>
    <link>https://sydharris.com/notes/</link>
    <description>A journal covering innovation, ideation, new thought, and product launches</description>
    
    <language>en-us</language>
    <lastBuildDate>Tue, 21 Apr 2026 00:44:23 GMT</lastBuildDate>
    <atom:link href="https://sydharris.com/notes/rss.xml" rel="self" type="application/rss+xml"/>
    <atom:updated>2026-04-21T00:44:23+00:00</atom:updated>
    
    <item>
      <author>Syd Harris</author>
      <title>Building Optimize CLI tool</title>
      <link>https://sydharris.com/notes/optimize-cli-tool/</link>
      <guid>https://sydharris.com/notes/optimize-cli-tool/</guid>
      <pubDate>Wed, 25 Feb 2026 00:00:00 +0000</pubDate>
      <atom:updated>Wed, 25 Feb 2026 00:00:00 +0000</atom:updated>
      <category> cli</category><category> developer tools</category><category> optimize</category>
      <description>A post-build validation tool that makes invisible website issues visible—built on a flash drive, shipped at 15kb</description>
      <content:encoded><![CDATA[<p>Back in September, I had this idea for a project (a launch for another time). There I was, building another microsite. Run it locally. Everything looks perfect. Push to production...</p>
<p>Broken Open Graph tags. Alt text missing on half the images. No Schema markup. URLs that reference the dev environment. And a critical page that has <code>noindex</code> from testing.</p>
<p>That's the thing about meta tags, schema markup, and accessibility attributes they're invisible until they're not. Until you push to production. Run the schema.org validator. Open Chrome DevTools and use Lighthouse. Someone shares your link and you don't see that pretty social image. A screen reader encounters your page. Or Google Search Console says you've got a bunch of 404 pages.</p>
<p>Who's got time to be missing key things when we're all trying to keep up with changes in AI? I just wanted to build a static site that technically had a fighting chance to stand out in generative and traditional search.</p>
<h2>Why Not Just Use Lighthouse?</h2>
<p>Lighthouse is great. But it's solving a different problem.</p>
<p>It boots up headless Chrome, executes JavaScript, measures Core Web Vitals, captures screenshots, analyzes runtime performance... For a static site where I just need to know &quot;are my meta tags correct?&quot; or &quot;does the schema JSON exist?&quot;, it's overkill during development.</p>
<p>Have you ever tried auditing a 50-page site? With Lighthouse you can only audit 1 page at a time. And if you want to run batch audits there's Unlighthouse, which is pretty cool. The downside is it has the same dependencies as Lighthouse. So all the above is true.</p>
<p>And if you're working in a resource-constrained environment (not by choice but by circumstance), every node_module and dependency counts. Running Lighthouse locally was not an option. Imagine building on a 20GB flash drive. How can you efficiently test a site before it makes it to production—and without causing your system to crash?</p>
<h2>Creative problem solving</h2>
<p>Then I thought, why not just test the build folder?</p>
<p>What started out as just a few simple Node.js script checks turned into something I could customize for any project. Space was no longer a problem. And what took minutes, I was able to do in seconds.</p>
<p>Feedback loop resolved. Code, test (errors), fix, test (pass), push.</p>
<blockquote>Constraints breed creativity</blockquote>
<h2>The What?</h2>
<p>A modular and extensible post-build and continuous CLI-first static site validator and diagnostic tool that you can plug into any environment. There are 8 core checks:</p>
<ul>
<li><strong>Meta</strong></li>
<li><strong>Open graph</strong></li>
<li><strong>Schema</strong></li>
<li><strong>Media</strong></li>
<li><strong>Links</strong></li>
<li><strong>Sources</strong></li>
<li><strong>Accessibility</strong></li>
<li><strong>Hierarchy</strong></li>
</ul>
<p>Each check is built as a module that can be configured for different project requirements via the configuration file. It scans 50 pages in seconds. That's fast enough to run on every build and get instant feedback while you're building.</p>
<p>It's 38.4KB unpacked for core engine. No dependencies are used other than <a href="https://github.com/changesets/changesets" target="blank" rel="noopener">Changesets</a> for versioning, <a href="https://pnpm.io/" target="blank" rel="noopener">pnpm</a> for modular repo management, and <a href="https://nodejs.org/en" target="blank" rel="noopener">Node.js</a>.</p>
<p>No bloated dependency trees. No hidden framework requirements. Just focused tooling that you can extend as needed. Run it as a dev dependency, include it in your agentic or CI/CD workflow, or adapt it and make your own tools with extensions or custom checks that plug into the core engine.</p>
<p>I decided to call it Optimize, and it's a project I'm working on at <a href="https://bynovl.com" target="blank" rel="noopener">NOVL</a>. If you're building static sites—whether with Eleventy, Hugo, Astro, Next.js static exports, or Jekyll—this is for you.</p>
<h3>Try it out</h3>
<p>Currently it's in beta and I'd love your feedback. The plan is to open source it, but for now, real feedback matters. It solved a problem I kept running into. Maybe it'll solve yours too.</p>
<p><strong>Install the package:</strong></p>
<pre><code class="language-bash"># Install on your project
npm install -D @bynovl/optimize 
</code></pre>
<p><strong>Set up your config (optimize.config.js):</strong></p>
<pre><code class="language-js">export default {
  outDir: 'dist', // required
  ignore: ['404.html', '403.html', '402.html'],
}
</code></pre>
<p><strong>Start testing:</strong></p>
<pre><code class="language-bash"># Runs a full audit
npx optimize
</code></pre>
<h3>Join me</h3>
<p>Come join me in optimizing the web.
Leaving no site behind. <a href="https://optimize.bynovl.com" target="blank" rel="noopener">Sign up for the beta</a>.</p>
]]></content:encoded>
    </item>
    
    <item>
      <author>Syd Harris</author>
      <title>Introducing the `context` Attribute: A New Standard for Semantic Web Markup</title>
      <link>https://sydharris.com/notes/context-attribute/</link>
      <guid>https://sydharris.com/notes/context-attribute/</guid>
      <pubDate>Sun, 12 Apr 2026 00:00:00 +0000</pubDate>
      <atom:updated>Sun, 12 Apr 2026 00:00:00 +0000</atom:updated>
      <category> HTML5</category><category> web standards</category><category> AI</category><category> semantic web</category>
      <description>Why a dedicated context attribute is the missing link for semantic, AI-ready, and accessible web markup. Learn how context bridges the gap between style and meaning, and why adoption matters.</description>
      <content:encoded><![CDATA[<!-- https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Global_attributes/contenteditable -->
<p>Modern web development, especially with frameworks like React, often produces HTML that is visually correct but semantically ambiguous. Class and ID names are frequently auto-generated or used solely for styling and JavaScript hooks, making it difficult for both humans and AI to understand the true intent of each element.</p>
<pre><code class="language-html">&lt;!-- Opaque, meaningless (typical of React or CSS-in-JS) --&gt;
&lt;div id=&quot;r1c2a3b4c5&quot; class=&quot;css-1a2b3c4&quot;&gt;&lt;/div&gt;
&lt;div class=&quot;sc-xyz123-0 kLJQwA&quot;&gt;&lt;/div&gt;

&lt;!-- Human-readable, meaningful --&gt;
&lt;div id=&quot;faqs-section&quot;&gt;&lt;/div&gt;
&lt;div class=&quot;author-title&quot;&gt;&lt;/div&gt;
</code></pre>
<p>While the latter examples provide context, relying on class or ID names for semantic meaning is not ideal and are often repurposed for styling or dynamic behavior not creating a clear separation of presentation and function. I've been thinking alot about a dedicated <code>context</code> attribute for HTML elements. This attribute encodes the semantic role or intent of an element, separate from its styling or dynamic data.</p>
<pre><code class="language-html">&lt;!-- class for style/presentation --&gt;
&lt;!-- context for semantic meaning --&gt;
&lt;div class=&quot;card-title&quot; context=&quot;author-title&quot;&gt;Jane Doe&lt;/div&gt;
</code></pre>
<h2>Why Not <code>aria-*</code>, <code>data-*</code>, <code>id</code>, <code>class</code>, or <code>itemscope</code>?</h2>
<p><strong>ARIA attributes</strong> (like <code>aria-label</code>, <code>aria-role</code>) are designed for accessibility, specifically to help screen readers and assistive technologies interpret web content. They are not intended for general semantic annotation or AI-driven workflows. ARIA's primary audience is accessibility tools, and its vocabulary is specific to accessibility needs.</p>
<pre><code class="language-html"> &lt;!-- ARIA for screen readers --&gt;
&lt;button aria-label=&quot;Close dialog&quot;&gt;✕&lt;/button&gt;
</code></pre>
<p><strong>Microdata</strong> and <strong>RDFa</strong> (e.g. <code>item-scope</code>) are standards for embedding structured data in HTML, usually for search engines and knowledge graphs. They require using pre-defined vocabularies (like schema.org), which can be too rigid or verbose for many real-world content needs. Microdata is best for marking up entities for external consumption, not for subjective, project-specific context.</p>
<pre><code class="language-html">&lt;div itemscope itemtype=&quot;https://schema.org/Person&quot;&gt;
  &lt;span itemprop=&quot;name&quot;&gt;Jane Doe&lt;/span&gt;
&lt;/div&gt;
</code></pre>
<p><strong>Data attributes</strong> (e.g. <code>data-*</code>) are meant for dynamic data and JavaScript hooks. While you can add semantic context using a data attribute (e.g., <code>data-context</code>), this is not their primary purpose. Data attributes are meant for storing custom data private to the page or application, often for scripting or state.</p>
<pre><code class="language-html">&lt;div class=&quot;user-card&quot; data-user-id=&quot;123&quot; data-state=&quot;expanded&quot;&gt;&lt;/div&gt;
</code></pre>
<p><strong>ID attributes</strong> are meant to be unique within a page and are primarily used for JavaScript targeting and as URL-friendly hash anchors (e.g., <code>#section-faqs</code> or <code>#what-is-context-attr</code>). While useful for navigation and scripting, they are not intended to convey semantic meaning about the content itself even some meaning can be extacted.</p>
<pre><code class="language-html">&lt;!-- Link: &lt;a href=&quot;#faq-1&quot;&gt;Jump to FAQ&lt;/a&gt; --&gt;
&lt;h2 id=&quot;faq-1&quot;&gt;What is context?&lt;/h2&gt;
</code></pre>
<p><strong>Classes</strong> are meant for presentation. While they can become descriptive using BEM (Block Element Modifier) or OOCSS (Object Oriented CSS), their focus should be on the element/component for styling not to communicate semantic intent. When BEM classes try to combine both presentational and semantic intent, they can become unwieldy and ambiguous.</p>
<pre><code class="language-html">&lt;!-- Style + semantic context, clearly separated --&gt;
&lt;div class=&quot;card-header__title&quot;&gt;&lt;/div&gt;
&lt;!-- Using BEM to combine style and semantic intent (author-title) --&gt;
&lt;div class=&quot;card-header__author-title&quot;&gt;&lt;/div&gt; 
</code></pre>
<h2>How <code>context</code> Differs</h2>
<ul>
<li><code>context</code> is for subjective, project- or content-specific meaning, not limited to accessibility or external knowledge graphs. Though it could work hand in hand.</li>
<li>It enables richer, more flexible annotation of intent, role, or meaning—beyond what ARIA, microdata and schema allow.</li>
<li>It is designed for both human and AI understanding, and can evolve with your content and workflows.</li>
<li>Every element can be mapped to a JSON object with both its presentational and semantic roles clearly defined.</li>
</ul>
<h2>Benefits</h2>
<ul>
<li><strong>Separation of Concerns:</strong> Style, function, and meaning are clearly separated.</li>
<li><strong>AI &amp; Automation:</strong> Enables robust extraction, translation, and content QA workflows.</li>
<li><strong>Accessibility:</strong> Tools can better infer the purpose of each element.</li>
<li><strong>Maintainability:</strong> Future developers can quickly understand the intent of markup.</li>
</ul>
<h2>Final Note</h2>
<p>While <code>context</code> is ideal for clarity and semantics, current tooling and frameworks (including React and some HTML validators) may not fully support custom attributes without the <code>data-</code> prefix. However, for most modern browsers and JavaScript, you can select and manipulate the <code>context</code> attribute directly via the DOM (e.g., <code>document.querySelector('[context=&quot;faq-question&quot;]')</code>).</p>
<p>By making this small change in how we develop, we enable immediate, robust HTML-to-JSON extraction, unlocking what I call an <strong>HTML-driven API</strong>. This means anyone who understands HTML and structured data can create an API or data contract directly from the markup—no need for separate backend schemas or complex integrations. The structure and content of the API payload are derived from the annotated HTML itself, using context attributes to map markup to JSON objects that preserve both presentational and semantic meaning. This approach lets the frontend drive the data model, ensures consistency between UI and data, and empowers rapid prototyping and integration.</p>
<h3>Example: Putting It All Together</h3>
<p>This flow shows how annotated HTML can be extracted to JSON, processed by a backend or AI, and then used to update the UI—enabling a seamless, dynamic, and intelligent web experience.</p>
<p><strong>Composing HTML</strong></p>
<pre><code class="language-html">&lt;section class=&quot;accordion&quot; context=&quot;faq-list&quot;&gt;
  &lt;div class=&quot;accordion-item&quot; context=&quot;faq-item&quot;&gt;
    &lt;h2 class=&quot;accordion-item__title&quot; context=&quot;faq-question&quot; aria-label=&quot;FAQ Question&quot; role=&quot;button&quot; aria-expanded=&quot;false&quot; tabindex=&quot;0&quot;&gt;What is context?&lt;/h2&gt;
    &lt;div class=&quot;accordion-item__desc&quot; context=&quot;faq-answer&quot; role=&quot;region&quot;&gt;A semantic marker for intent.&lt;/div&gt;
  &lt;/div&gt;
&lt;/section&gt;
</code></pre>
<p><strong>Extracting JSON with JavaScript</strong></p>
<pre><code class="language-js">const faqList = Array.from(document.querySelectorAll('[context=&quot;faq-item&quot;]')).map(item =&gt; ({
  &quot;faq-question&quot;: item.querySelector('[context=&quot;faq-question&quot;]')?.textContent.trim() || '',
  &quot;faq-answer&quot;: item.querySelector('[context=&quot;faq-answer&quot;]')?.textContent.trim() || ''
}));
const result = { &quot;faq-list&quot;: faqList };
</code></pre>
<p><strong>Generated JSON (HTML-Driven API) for Backend/AI Automation</strong></p>
<pre><code class="language-json">// Can stores as JS module or JSON
{
  &quot;faq-list&quot;: [
    {
      &quot;faq-question&quot;: &quot;What is context?&quot;,
      &quot;faq-answer&quot;: &quot;A semantic marker for intent.&quot;
    }
  ]
}
</code></pre>
<p><strong>Dynamic HTML Update from API/JSON</strong></p>
<pre><code class="language-js">// Select the all faqItems
const faqItems = document.querySelectorAll('[context=&quot;faq-item&quot;]');

// Option 1: Backend or local fetch (API)
fetch('/faqs.json').then(res =&gt; res.json()).then(data =&gt; { /* see below */});

// Option 2: Import as JS module (faqs.js exports the JSON)
import { data } from './faqs.js';

data[&quot;faq-list&quot;].forEach((faq, i) =&gt; {
  const item = faqItems[i];
  if (item) {
    const q = item.querySelector('[context=&quot;faq-question&quot;]');
    const a = item.querySelector('[context=&quot;faq-answer&quot;]');
    if (q) q.textContent = faq[&quot;faq-question&quot;];
    if (a) a.textContent = faq[&quot;faq-answer&quot;];
  }
});

</code></pre>
<p>If <code>context</code> were to become a new standard the real challenge is not technical, but social: developer adoption and education. Like Microdata, RDFa, Schema.org, and accessibility rules before it, the value of <code>context</code> will only be realized if teams understand, believe in, and apply it.</p>
]]></content:encoded>
    </item>
    
  </channel>
</rss>
