<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Frontend Field Notes]]></title><description><![CDATA[Frontend Field Notes is Danny Gibas’s working journal for front-end engineering: Reactive Frameworks (like Vue) and TypeScript patterns, accessibility, performance, and how to ship UI that holds up in production. Short, practical write-ups from day-to-day work.]]></description><link>https://front-end-fieldnotes.hashnode.dev</link><image><url>https://cdn.hashnode.com/uploads/logos/6abc4024e6870e09f3d32cde/35f36698-f5ca-4168-add1-5047a1109405.png</url><title>Frontend Field Notes</title><link>https://front-end-fieldnotes.hashnode.dev</link></image><generator>RSS for Node</generator><lastBuildDate>Wed, 07 Oct 2026 12:26:01 GMT</lastBuildDate><atom:link href="https://front-end-fieldnotes.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[The Newest Bug in the Tech Job Hunt: Fraudsters
]]></title><description><![CDATA[Like so many others in software engineering, I was made redundant this year. While looking for work, I discovered just how prevalent job scams are on LinkedIn. As a result, I became very good at spott]]></description><link>https://front-end-fieldnotes.hashnode.dev/the-newest-bug-in-the-tech-job-hunt-fraudsters</link><guid isPermaLink="true">https://front-end-fieldnotes.hashnode.dev/the-newest-bug-in-the-tech-job-hunt-fraudsters</guid><category><![CDATA[job search]]></category><category><![CDATA[scamalert]]></category><category><![CDATA[tips]]></category><dc:creator><![CDATA[Danny Gibas]]></dc:creator><pubDate>Wed, 30 Sep 2026 17:23:36 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6abc4024e6870e09f3d32cde/01d2bb0a-1ad8-4661-992f-e1662dd05242.jpg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Like so many others in software engineering, I was made redundant this year. While looking for work, I discovered just how prevalent job scams are on LinkedIn. As a result, I became very good at spotting scams and avoiding them. This article covers the anatomy of the common “Executive Resume Rewrite” scam and how to protect yourself. This is not the only scam out there, but the steps I outline will help protect you from many of them.</p>
<p>The reason these scams work is that we’re desperate for a job and willing to take steps that we believe will improve our chances of getting hired. That means our guard is down. I’ve been targeted several times and almost became a victim myself. If you’ve fallen victim to one, don’t be ashamed. These fraudsters are really good at what they do.</p>
<h3><strong>The hook</strong></h3>
<p>The most common scam right now is the “Executive Resume” rewrite. While it takes a couple of different forms, the general approach is:</p>
<ol>
<li><p>A fake recruiter messages you about a job that you’re a great fit for. The JD is a very close match to your profile. There’s a reason for this: they generated it based on your profile. If it matches you a little too closely, be on high alert.</p>
</li>
<li><p>They ask for your resume.</p>
</li>
<li><p>You provide your resume, and they respond by recommending that you work with their “executive resume writer” to position your resume before presenting you to the hiring manager.</p>
</li>
</ol>
<p>That’s the hook. A legitimate recruiter would never do this. That’s your first red flag.</p>
<p>This is not to say that every person offering optimization or resume services is a scammer, but there are some steps you can take to protect yourself and separate legitimate offers from fraudsters.</p>
<h3><strong>How to catch a scammer</strong></h3>
<p>When vetting professional opportunities or business partners online, establishing the counterparty's true identity is the first line of defense against fraud. Scammers routinely rely on anonymity and fabricated credentials, making a rigorous background check essential before any formal commitments or financial transactions take place.</p>
<ol>
<li><p>Ask for official contact information, including a business email address and business phone number.</p>
</li>
<li><p>Check the email address. Is it linked to a business account? If it’s a Gmail account, that’s a red flag.</p>
</li>
<li><p>Validate the business phone number. If they refuse to provide one, that’s a red flag. If they provide one, Google it. Is it a US phone number linked to a legitimate business? If not, be cautious. Then call it to verify. A scammer typically won’t provide contact information like a phone number that can be easily linked back to them.</p>
</li>
<li><p>Validate the LinkedIn profile. Pull a copy of the profile picture and reverse image search it. Is it from a stock photo site? If so, that’s a red flag. Does it link to more than one profile? If so, they may be impersonating someone else. Does the profile have a lot of followers? If they only have a few, be careful; it could be a fake or recently created account.</p>
</li>
<li><p>Finally, ask for a reference. Are they willing to provide one? If so, before you hand over any money, check the reference. If they refuse, that’s another red flag.</p>
</li>
</ol>
<p>If ANY of these steps don’t check out, don’t hand over your money. Real professionals welcome due diligence, while bad actors rely on pressure tactics and obscurity to bypass it. By diligently applying these tactics you will protect yourself from becoming their next victim.</p>
<p>If you catch a scam account on LinkedIn, report it.</p>
]]></content:encoded></item><item><title><![CDATA[Dynamic ARIA and accessibility]]></title><description><![CDATA[Static ARIA is easy. Dynamic ARIA is where you can get an accessibility win.
When UI state changes, the attributes that describe that state need to change with it — otherwise assistive tech gets out o]]></description><link>https://front-end-fieldnotes.hashnode.dev/dynamic-aria-and-accessibility</link><guid isPermaLink="true">https://front-end-fieldnotes.hashnode.dev/dynamic-aria-and-accessibility</guid><category><![CDATA[Accessibility]]></category><category><![CDATA[aria]]></category><category><![CDATA[Frontend Development]]></category><dc:creator><![CDATA[Danny Gibas]]></dc:creator><pubDate>Tue, 29 Sep 2026 23:26:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6abc4024e6870e09f3d32cde/835f551b-e1a6-40d8-8cc8-779147783dc7.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Static ARIA is easy. Dynamic ARIA is where you can get an accessibility win.</p>
<p>When UI state changes, the attributes that describe that state need to change with it — otherwise assistive tech gets out of sync with the UI.</p>
<p>Example: A validated text field shouldn’t always announce an error.</p>
<p>Bind <code>aria-invalid</code> and <code>aria-describedby</code> to the validation state so the error message is only associated when it’s real (see screenshot for example in Vue.js).</p>
<p>Same idea for expand/collapse (aria-expanded), busy submits (aria-busy / disabled), and live regions (aria-live) for async results.<br />Rule of thumb: if the visual UI changes, check whether the accessibility tree needs an update too.</p>
<pre><code class="language-html">&lt;input
   id="name"
   :aria-invalid="!invalid"
   :aria-describedby="!invalid ? "name-error" : undefined"
/&gt;
&lt;span v-if="!invlaid" id="name-error"&gt;
   Name must have at least 5 characters.
&lt;/span&gt;
</code></pre>
]]></content:encoded></item><item><title><![CDATA[Take control of your AI Agent by adding an AGENTS.md file]]></title><description><![CDATA[An AGENTS.md file is the custom ruleset for how you want your AI agent to behave. Anytime you provide a prompt, the agent references the AGENTS.md to ensure the response or action follows the ruleset ]]></description><link>https://front-end-fieldnotes.hashnode.dev/take-control-of-your-ai-agent-by-adding-an-agents-md-file</link><guid isPermaLink="true">https://front-end-fieldnotes.hashnode.dev/take-control-of-your-ai-agent-by-adding-an-agents-md-file</guid><category><![CDATA[AI]]></category><category><![CDATA[Developer Tools]]></category><category><![CDATA[workflow]]></category><dc:creator><![CDATA[Danny Gibas]]></dc:creator><pubDate>Sun, 20 Sep 2026 00:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6abc4024e6870e09f3d32cde/c4270350-5ad6-4b04-b311-4d8a327a2d5f.jpg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>An AGENTS.md file is the custom ruleset for how you want your AI agent to behave. Anytime you provide a prompt, the agent references the AGENTS.md to ensure the response or action follows the ruleset before moving forward. It guarantees consistency and prevents you from fighting with the AI every time you enter a prompt.</p>
<p>While the file is written in <a href="https://www.markdownguide.org/">Markdown</a>, you DO NOT need to be an engineer to use it. If you use AI for <strong>any</strong> purpose, you can add one to the root of your primary work directory on your computer and either symlink or hardlink to it from any other work directory and customize it for your own purposes (more on this in a moment).</p>
<p>If you do not want to write the instructions manually because you are not comfortable writing Markdown, you can prompt your agent with the rules you want to add, and it will add them in Markdown for you. If you are not a developer who works inside of an IDE and wish to manually customize the rules, then just open up the file in Notepad or another simple text editor, type out the instructions in Markdown, and save it with the <code>.md</code> extension.</p>
<p>For those of you using Claude, you can alternatively use a Claude.md to serve the same purpose; however, an AGENTS.md will be picked up by any LLM you are using, so I recommend using that instead. If you prefer to use Claude.md and are using the Claude Code app in your terminal, you can have Claude generate the default Claude.md by typing <code>/init</code> in the terminal inside of the root of your work folder. This will generate the baseline file that you can then customize. If you use the Claude desktop app, you can tell Claude to reference it in the project’s <strong>Custom Instructions</strong> sidebar; type: <em>“Always read and prioritize the style rules laid out in the attached Claude.md file.”</em></p>
<p>If you work out of multiple project directories and want to manage all your instructions in a single file, you can ask Claude (or whatever LLM you use) to symlink each project to the root AGENTS.md file for you: “<em>Claude, I have added an Agents.md (or Claude.md) file to the current root directory with the instructions I want you to use moving forward. I need you to symlink that file to all of my project folders in this working directory.”</em></p>
<p>Alternatively, if you do not like symlinks, you can place a blank AGENTS.md in the root of each project directory and add a hard link inside of it that points to the primary AGENTS.md in the project root. With this symlink or hard link in place, you will only need to edit the AGENTS.md in the project root, and the changes will apply to each project or working directory!</p>
<p>Note that in general, if you open your projects from the project root as your workspace, the agent should be able to reference the AGENTS file without needing the symlinks or agents files in each project; however, if you open the individual projects as the workspace root, you will need the hard link or symlink in place. To save yourself time moving forward, you can also add the instruction <em>“Whenever creating a new project directory for me, add a new AGENTS.md to it and link it to the source of truth Agents file for me.”</em></p>
<p>For example:</p>
<pre><code class="language-plaintext">/Projects  
  AGENTS.md &lt;-- primary agents file with instructions (if you work out of this directory, you won't need an AGENTS.md in every project folder  
      /Project1  
      /Project2
</code></pre>
<p>If you are like me, you probably have an issue with the cumbersome and robotic language AI uses in its responses. Adding a <em>Personality &amp; Communication</em> ruleset in your AGENTS.md is an excellent way to address this. While it will not perfectly humanize the response, it will at least be less verbose and stick to more common language.</p>
<p>This is mine:</p>
<pre><code class="language-markdown">## Personality &amp; Communication
- **Be direct:** State the primary answer or core conclusion in the very first sentence. Avoid introductory filler like "Sure, I can help with that" or "Here is the information."
- **Eliminate verbosity:** Keep sentences short and use the active voice.- **Maintain a natural, human tone:** Speak like a helpful, clear-headed peer. Avoid overly formal corporate jargon, rigid academic phrasing, or robotic disclaimers.
- Bold paths; bullets over walls of text.- If unclear what to build, ask one short question before coding.
</code></pre>
<p>When crafting your AGENTS file, try to keep your AGENTS file below 250 lines. Excessively long rule sets will create increased latency in the responses and burn more tokens. Keep it tight and actionable.</p>
<p>Below is my full, primary AGENTS.md as a Vue.js/TypeScript engineer. Feel free to copy as much of it as you think is relevant to your needs, or use it as a starter for your own stack.</p>
<p>Happy prompting!</p>
<pre><code class="language-markdown">## Projects AGENTS.md (canonical)

Source of truth for `/Users/dannygibas/Projects`.

**Scope:** Greenfield stack rules apply to **new projects only**. Do not rewrite existing apps (styles, READMEs, aliases, structure) unless D asks. Vue rules → Vue apps. React apps → local patterns + shared sections (comms, secrets, change control, DoD).

**New apps:** Symlink this file as root `AGENTS.md` (`../AGENTS.md` or `../../AGENTS.md`). For public GitHub demos, a short pointer file is fine if a symlink looks messy.

---

## Personality &amp; Communication

- **Be direct:** State the primary answer or core conclusion in the very first sentence. Avoid introductory filler like "Sure, I can help with that" or "Here is the information."
- **Eliminate verbosity:** Keep sentences short and use the active voice.
- **Maintain a natural, human tone:** Speak like a helpful, clear-headed peer. Avoid overly formal corporate jargon, rigid academic phrasing, or robotic disclaimers.
- Keep explanations under 3 sentences. Skip teaching standard APIs (`map`, `ref`, etc.) unless it prevents a hidden bug.
- Bold paths; bullets over walls of text.
- If unclear what to build, ask one short question before coding.

---

## Change Control

- **Stay on request:** Only what the latest ask requires. No drive-by cleanups, renames, restyles, or extra files unless D asks.
- Ask before behavior/API changes or refactors.
- In an already-approved file: typos, type fixes, and obvious bugs are OK without re-asking.
- Ask before touching vendor config, `.env` / mocks, or DB seeds.
- Match existing patterns; get approval before changing them.
- Never commit or push unless D asks.

---

## Secrets &amp; Git

- Never commit secrets. Gitignore `.env` / `.env.*` (allow `.env.example`).
- Secrets only in local `.env` / `.env.local`.
- New projects: ship `.env.example` with `VITE_*` placeholders.

---

## New Project Layout

| Path                       | Purpose                |
| -------------------------- | ---------------------- |
| `src/components/`          | UI only                |
| `src/composables/`         | `use*` reactive logic  |
| `src/types/`               | Types / unions         |
| `src/stores/`              | Pinia — only if needed |
| `src/utils/` or `src/lib/` | Plain helpers          |
| `src/assets/`              | Imported assets        |

---

## Stack (new projects)

- TypeScript, strict null checks; prefer type files.
- **Tailwind v4** + `@tailwindcss/vite`. No vanilla/inline CSS unless a third-party lib needs it (ask first).
- Existing / a11y–API drills: match the repo (scoped CSS OK).
- Components = display; logic in composables.
- Native HTML + Tailwind first — ask before UI kits (Headless, Radix, Vuetify, etc.).
- Vue Router only when there is more than one real route.
- Ask before adding npm deps.
- Prefer `const` over `let`.
- API URLs from `import.meta.env.VITE_*` — never hardcode; never use `process.env` in Vite client code.
- New projects: configure `@` → `/src` in **vite.config + tsconfig**. Do not invent `@/` mid-edit in repos that lack it.

**Theme tokens:** Define in `@theme` / Tailwind config. Utilities OK if they map to tokens. **No raw hex in SFCs.**

```css
@theme {
  --color-background: #f7f7f7;
  --color-foreground: #1a1a1a;
  --color-border: #d4d4d4;
  --color-success: #15803d;
}
</code></pre>
]]></content:encoded></item><item><title><![CDATA[Concurrency management in JavaScript: the mutex (mutual exclusion) lock pattern]]></title><description><![CDATA[Managing concurrency in any programming language is an important advanced topic, but it becomes especially relevant in JavaScript due to the lack of native multi-threaded locks. JavaScript is single t]]></description><link>https://front-end-fieldnotes.hashnode.dev/concurrency-management-in-javascript-the-mutex-mutual-exclusion-lock-pattern</link><guid isPermaLink="true">https://front-end-fieldnotes.hashnode.dev/concurrency-management-in-javascript-the-mutex-mutual-exclusion-lock-pattern</guid><category><![CDATA[JavaScript]]></category><category><![CDATA[asynchronous]]></category><category><![CDATA[Frontend Development]]></category><dc:creator><![CDATA[Danny Gibas]]></dc:creator><pubDate>Tue, 15 Sep 2026 00:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6abc4024e6870e09f3d32cde/c7023436-072a-4630-8ad3-07aa1d20aca8.jpg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Managing concurrency in any programming language is an important advanced topic, but it becomes especially relevant in JavaScript due to the lack of native multi-threaded locks. JavaScript is single threaded and code execution on the stack is sequential. However, async operations like promises yield control in the stack because they are <a href="https://www.geeksforgeeks.org/javascript/what-is-an-event-loop-in-javascript/">microtasks</a> and are handed asynchronously to the <a href="https://www.youtube.com/watch?v=WNrHrwm1wkU">event loop</a>. While the execution order of a promise callback is determinant once it is added to the event loop, the execution order becomes non-determinant in implementation because of the latency of an external call. Microtasks like promise callbacks ( <code>.then(), .catch(), .finally()</code> ) will not get pulled into the event loop for execution until the promise settles (resolves or fails), and this creates a concurrency problem in the form of a race condition in need of a design pattern.</p>
<p>Consider the following use case.</p>
<p>You are developing the front end for a banking app. A user is expecting to always see an accurate account balance when they deposit or withdraw funds. While the backend is the ultimate source of truth for the balance, the front end is responsible for requesting updates to the balance and displaying the results of these requests accurately to the user.</p>
<p>In a banking app without concurrency management, the following situation might occur.</p>
<p>An account holder opens their banking app and observes a balance of $100 in their checking account. They deposit an additional $100 from a check, expecting a new balance of $200. While the deposit is still in flight they try to move the anticipated balance of $200 to their savings account. Due to excessive latency at a data center the transfer request hits the database before the deposit request and they get an insufficient funds error as a result.</p>
<p>What we need is a way to manage the concurrency of the requests. There are a few different design patterns to handle this, including <a href="https://dev-aditya.medium.com/optimistic-locking-vs-pessimistic-locking-7e25c1dab4e8">optimistic and pessimistic locking</a>. There is also sequentialization via mutex locking, which is the design pattern that this article addresses.</p>
<p>Lets look at how we can handle the example above via the mutex pattern in TypeScript:</p>
<pre><code class="language-javascript">class AsyncLock {
  private disableNext: Promise&lt;void&gt;;

  constructor() {
    this.disableNext = Promise.resolve();
  }

  // force async task to wait in line
  async acquire(): Promise&lt;() =&gt; void&gt; {
    let release!: () =&gt; void;
    const currentLock = this.disableNext; // assign private reference to the most recent promise created
    this.disableNext = new Promise&lt;void&gt;((resolve) =&gt; {
      // create a new promise for the next request in queue
      release = resolve;
    });
    await currentLock;
    return release;
  }
}

// how to use AsyncLock
const lock = new AsyncLock();
let balance = 100;

type TransactionType = "withdraw" | "deposit";

async function updateBalance(
  amount: number,
  transactionType: TransactionType
): Promise&lt;void&gt; {
  const release = await lock.acquire(); // wait in line
  try {
    console.log(`Reading balance... Current: $${balance}`);
    await new Promise((r) =&gt; setTimeout(r, 50)); // simulate database delay
    if (transactionType === "withdraw" &amp;&amp; balance &gt;= amount) {
      // withdraw
      balance -= amount;
      console.log(`Withdraw: $${amount}. Current: $${balance}`);
    } else if (transactionType === "deposit") {
      // deposit
      balance += amount;
      console.log(`Deposit: $${amount}. Current: $${balance}`);
    } else {
      console.log("NOP");
    }
  } finally {
    release(); // let the next task run by calling the resolve method on the current promise
  }
}

updateBalance(100, "deposit"); // deposit $100 into checking
updateBalance(200, "withdraw"); // transfer (withdraw) $200 to savings
</code></pre>
<p>Let’s break down the example.</p>
<p>When you execute <code>new AsyncLock()</code> , JavaScript creates a brand new locking object. <code>this.disableNext = Promise.resolve();</code> initializes with a resolved promise on creation, allowing the first request in the queue to process immediately. Every time a function calls <code>lock.acquire()</code> it needs to know what the current state of the lock chain is. <code>this.disableNext</code> allows the code to look at the <strong>shared state</strong> stored on that specific lock instance. It reads the previous promise ( <code>currentLock</code> ) and then overwrites <code>this.disableNext</code> with a new pending promise. Because <code>this</code> points to the persistent <code>lock</code> instance, the next function that calls <code>lock.acquire()</code> will see that updated promise and wait in line behind it. When the current process completes it will call <code>release()</code> which will resolve the current (in flight) promise and allow the next request in line to proceed.</p>
<p>The result is a lock that queues the requests in a predictable and controllable order. A follow-up request can never supersede the previous and your UI will remain synced with the resolving request order. Any number of concurrent requests can be queued and will process First In — First Out (FIFO). The use of a JavaScript <code>Class</code> and the <code>this</code> keyword ensures that the <code>disableNext</code> state is shared across instances. That makes the mutex design pattern an excellent choice for managing concurrency across shared states in JavaScript applications where high performance is necessary and deadlocking additional requests must be avoided.</p>
]]></content:encoded></item><item><title><![CDATA[Vibe Coding Is a Drunk Carpenter With a Hammer ]]></title><description><![CDATA[Agentic engineering does not need to be terrible for product outcomes or the carpenters' livers.
We know the problems with vibe coding are many. Stateless sessions lead to the loss of structural decis]]></description><link>https://front-end-fieldnotes.hashnode.dev/vibe-coding-is-like-putting-a-hammer-in-the-hand-of-a-drunk-carpenter</link><guid isPermaLink="true">https://front-end-fieldnotes.hashnode.dev/vibe-coding-is-like-putting-a-hammer-in-the-hand-of-a-drunk-carpenter</guid><category><![CDATA[AI]]></category><category><![CDATA[Software Engineering]]></category><category><![CDATA[Career]]></category><category><![CDATA[vibe coding]]></category><category><![CDATA[Spec-Driven-Development]]></category><dc:creator><![CDATA[Danny Gibas]]></dc:creator><pubDate>Fri, 14 Aug 2026 00:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6abc4024e6870e09f3d32cde/b817684d-2773-4928-9a93-f7906881ded8.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Agentic engineering does not need to be terrible for product outcomes or the carpenters' livers.</p>
<p>We know the problems with vibe coding are many. Stateless sessions lead to the loss of structural decision-making, and inconsistent design patterns develop. It becomes a spaghetti time bomb. Silent gaps arise because the LLM pays little attention to things like input sanitation, race conditions, and telemetry. Team coordination is effectively impossible when vibe coding puts each engineer into solo sessions with no clearly defined rules and coordinated objectives. PRs tend to become nightmarish, with thousands of lines of code to review while trying to understand the AI's intent.</p>
<p>Engineers end up feeling disconnected from the engineering process and more like AI babysitters at the end of the day.</p>
<p>But there is a two-part alternative that I fell in love with that solves those issues, and so can you. They are spec-driven agentic orchestration and vertical story slicing.</p>
<p>Spec-Driven Development (SDD) puts engineers back in the driver's seat with the agent riding shotgun. SDD helps you engineer solutions with forethought, collaborate as a team, research, and plan before the agent writes a single line of code.</p>
<p><strong>Hair of the dog.</strong></p>
<p>While there are several spec-driven workflows out there, I am going to address OpenSpec in this article as this is the workflow that I prefer due to its lightweight and versatility. The nice thing about the OpenSpec workflow is that it works great with both greenfield and brownfield products and you can introduce it into your workflow (and repo) at any point in the product lifecycle.</p>
<p><strong>The OpenSpec Structure.</strong></p>
<p>Before I discuss the individual artifacts of the OpenSpec workflow, let’s take a look at the directory organization of OpenSpec in a repo.</p>
<pre><code class="language-markdown">├── .openspec/
│   ├── config.yaml          # Framework rules, schema configurations, and linter settings
│   └── agent.md             # System prompt instructing the AI assistant how to use OpenSpec
├── project.md               # The Project Constitution: Global context (Tech stack, architecture rules)
├── specs/                   # 1. THE SOURCE OF TRUTH (Durable specifications)
│   ├── auth/
│   └── orders/
└── changes/                 # 2. THE DELTA CHANGES (Transactional task folders)
    └── change-042-add-filter/
        ├── proposal.md      # High-level business intent ("The What")
        ├── specs/           # Granular acceptance requirements
        ├── design.md        # Technical architecture blueprints ("The How")
        └── task.md          # Atomic checklist for the AI agent execution layer
</code></pre>
<p>The <em>specs</em> directory is the source of truth right at this time in your repo. It contains the system’s baseline logic for each completed and archived feature or domain. The agent regularly references the specs directory to maintain context across the system for new features or spec deltas and is shared across engineering because it is located right in the repo. When you are completed with a feature and archive it, the agent safely merges the spec deltas into the spec directory, and they become part of the source of truth.</p>
<p>The <em>changes</em> directory contains the artifacts for the feature or bug you are currently working on. Each change includes its own <em>proposal.md</em>, <em>design.md</em>, <em>tasks.md</em>, and a <em>spec</em> directory that contains one or more <em>spec.md</em> delta files. Each change should typically reflect a vertical slice of the feature which segues us nicely into…</p>
<p><strong>Vertical Slicing</strong>.</p>
<p>Before starting the SDD workflow, the product owner should break the feature down into stories and stories into slices — structural verticals based on business value and domain components. From an Agile perspective, stories may contain multiple vertical slices. Stories that cross domains and/or cannot be completed in a single sprint should be broken into smaller manageable verticals, and each vertical should become a spec delta in the SDD workflow. Slice-based product management isn’t part of the OpenSpec framework, but it does make SDD much more manageable from a team and QA perspective.</p>
<p>Slicing is a verbose topic, and there are plenty of articles already written about it, so I won’t go into depth about it here. I linked a good primer about vertical slicing from the Monday blog at the end of this post if you want to learn more.</p>
<p>The reason that I mention it in this article is primarily that it is a great way to break stories into changes in the SDD workflow.</p>
<p>Let’s return to discussing the OpenSpec artifacts, starting with…</p>
<p><strong>The Proposal.</strong></p>
<p>First, the engineer works with product to capture the high-level plain-language goals, business constraints, and outcomes. Advanced teams use agent skills like <em>grill-me</em> here, forcing the AI agent to interrogate the engineer with edge-case questions before the document can finalize. Reviewing the Figma file during this session and even providing it to the agent via MCP is very helpful to make sure there is a clear understanding of the UI and experience between business, engineering, and the agent.</p>
<p>I found that doing a co-working session between the engineer and the PO with the agent works best for generating the proposal (<em>proposal.md</em>), and because the proposal is a plain English document it is easy to coordinate the conversation as you are both speaking the same language in the co-working session. Here you will work together to make sure that business and engineering are aligned, an area that teams often struggle with outside of this framework.</p>
<p><strong>The Spec Deltas.</strong></p>
<p>When talking about spec deltas, what we really mean are the feature or bugfix specifications that will ultimately update the existing “source of truth” once the feature or bugfix is completed and archived in the Spec Driven Development framework.</p>
<p>The specs are the highly precise operational requirements, frequently utilizing behavior-driven (Given-When-Then) syntax fenced blocks to establish requirement boundaries. The specs are controlled by the Product Owner and are driven by the proposal. Any deltas must be PO-approved. Specs should always be individually QA testable, and QA should build test cases that target the specs.</p>
<p>You might discover while drafting the design that an important constraint was overlooked that should be a spec because it is QA testable behavior. This might be a case for a valid spec addition, and you should address it with the PO to determine if it should be added. Since specs should be QA testable, any changes should also be brought to the attention of QA in case they have already written test cases and need to add more to address the spec addition or modification.</p>
<p>Specs should originate in your project management software and be tied to each associated slice in the story and be pulled into the repo as a spec delta. With an MCP for your project management suite, you can ask the LLM to generate the <em>spec.md</em> artifacts in the repo for you.</p>
<p>It should be noted that the source of truth is always the specs in the repo. NOT the stories in your project management software. QA, Product, and Engineering should always be aligned to the specs in the repo. Since the specs are plain English in Markdown and can be accessed by anyone in the company with a link to the file in your company's Git account, they can easily be viewed and referenced, even by non-engineering staff who lack access to a code editor.</p>
<p><strong>The Design.</strong></p>
<p>The design is the strict technical architecture blueprint and is owned by the engineer. This lays down the technical constraints — specifying patterns, models, contracts, fences, interfaces, etc. The design (<em>design.md</em>) ensures that these constraints are maintained across each slice and each engineer on the team. The design should ensure each spec is covered from an architectural perspective. I found using the <em>grill-me</em> skill is also useful here to make sure that you close any gaps that the agent would have to guess at during the execution.</p>
<p><strong>The Tasks.</strong></p>
<p>Turns the verified plan (from all of the artifacts) into an atomic checklist of minimal, independent execution steps that an AI agent can build without needing further guidance. As the LLM works through the execution, it will check off each item in <em>tasks.md</em> for you. Tell the agent to generate the tasks, and it will parse all of the artifacts and generate them for you.</p>
<p><strong>The Execution.</strong></p>
<p>This is where you finally set the agent loose to write the code, and you go hands off as the engineer. Simply tell the agent to start the execution, and it will be off and running, generating the code, following all of the specs, constraints, and design patterns that are defined in the artifacts, including those defined in the artifacts from archived slices to keep the solution cohesive.</p>
<p>After the build is complete, it is your opportunity to review the code that was generated. Before you open a PR, you should make sure that you do your own review to understand what was written as well as conduct dev testing to make sure the results fully align with the specs.</p>
<p><strong>Sober Up.</strong></p>
<p>SDD puts the team in control of the agent and ensures cohesive outcomes. Each slice's artifacts are archived and shared across slices (and engineers) through the repo. The agent has each slice's artifacts to reference in each subsequent slice, ensuring patterns are maintained, hallucinations are reduced, bugs are minimized, structural chaos is eliminated, and each slice is manageable in PR and in QA.</p>
<p>And the best part for engineers is it lets you actually engineer again instead of fighting with the agent all day.</p>
<p>You don’t have to feel like the drunk carpenter with a hammer anymore when using agentic AI to increase the efficiency of the development cycle.</p>
<p><strong>Hold my beer.</strong></p>
<p>I have experienced about a 20% time savings for feature delivery when using agentic SDD over a traditional software engineering workflow, but that’s not all. SDD forces the teams and departments to work together, to solve problems together, and think through more edge cases. This leads to far less bugs and a stronger, more cohesive product by coupling the specs to the repository and defining a single source of truth for the code base.</p>
<p>Learn more about the OpenSpec workflow at <a href="https://openspec.dev">https://openspec.dev</a></p>
<p>Monday has a good primer on vertical slicing: <a href="https://monday.com/blog/rnd/vertical-slice/">https://monday.com/blog/rnd/vertical-slice/</a></p>
]]></content:encoded></item><item><title><![CDATA[Learning to Code: Getting over the Beginners Hump]]></title><description><![CDATA[It was 2008, and I was in my first programming class ever. While it was not the first time I had touched code (I did theme my MySpace account 😆), this was the first time I had actually sat in a class]]></description><link>https://front-end-fieldnotes.hashnode.dev/learning-to-code-getting-over-the-beginners-hump</link><guid isPermaLink="true">https://front-end-fieldnotes.hashnode.dev/learning-to-code-getting-over-the-beginners-hump</guid><category><![CDATA[Career]]></category><category><![CDATA[learning]]></category><category><![CDATA[Programming Tips]]></category><category><![CDATA[intro to programming]]></category><dc:creator><![CDATA[Danny Gibas]]></dc:creator><pubDate>Fri, 17 Aug 2018 00:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6abc4024e6870e09f3d32cde/b451e821-7306-4408-9530-772deba5b3a2.jpg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>It was 2008, and I was in my first programming class ever. While it was not the first time I had touched code (I did theme my MySpace account 😆), this was the first time I had actually sat in a classroom to learn a modern programming language. As we opened our books to the first chapter on Object Oriented Programming, the professor paused and made a statement.</p>
<p>“You have to accept that there will be advanced concepts and syntax that you will have to use in the learning process but won’t necessarily understand. That is part of the process of learning to write code. You just have to accept that this is how the code is written and not understand why yet.”</p>
<p>Well said. That's the crux of it all. You will have to use advanced concepts that you won’t understand at first in order to learn the basics (which you will eventually understand).</p>
<p>I have to admit, as I sat in that classroom on the first day, anxiety crept up on me as I found myself exceedingly lost in the topic. “Maybe this is just not the right path for me. Maybe I just don’t have the type of mental faculty needed to grasp programming. Maybe I am simply not “smart” enough.”</p>
<p>There is just so much stuff that I needed to learn that it felt so overwhelming.</p>
<p>The truth is that every doubt flowing through my brain was nonsense, and that I was certainly not alone in those thoughts and feelings. I am here to tell you that everyone gets overwhelmed when first dipping their feet into the field. Even experienced devs get overwhelmed from time to time.</p>
<p>That feeling does not mean you are failing. It usually means you are standing at the edge of something new, and your brain is trying to map a landscape it has never seen before. Programming dumps a lot of vocabulary, tools, and mental models on you all at once. Of course it feels like drinking from a firehose. The goal is not to absorb the entire firehose on day one. The goal is to keep showing up long enough for the stream to start looking like water.</p>
<p>So, how does one get over that initial pedagogy in a vacuum?</p>
<p>First, let's take a look at the steps of the learning process: The 4 P's</p>
<p><strong>Preparation:</strong> Arousing Interest</p>
<p><strong>Presentation:</strong> Encountering the New Knowledge or Skills</p>
<p><strong>Practice:</strong> Integrating the New Knowledge or Skills</p>
<p><strong>Performance:</strong> Applying the New Knowledge and Skills</p>
<p>Preparation is the first step in any learning process. This is where the motivation to learn something cool and new actually happens. Preparation in the traditional sense would occur in the classroom as the teacher introduces a topic that piques your interest. However, this can happen in any number of ways, many unrelated to the classroom. Maybe it happened while you were reading a news article about the subject. Or maybe you spent some time in a subreddit dedicated to the subject, and it tickled your curiosity. Really, how it happens here is irrelevant; it just needs to happen, and you need to be able to maintain the interest.</p>
<p>Protect that interest on purpose. Motivation fades when every session ends in frustration and nothing that feels like progress. Give yourself small wins early: a page that loads, a button that works, a script that prints the right thing. Curiosity gets you in the door. Tiny evidence that you can do this is what keeps you from walking back out.</p>
<p>Presentation is next. In my experience, most students get stuck at Presentation, and unfortunately some never get beyond that point. Students get super excited about the prospect of learning to code, but then the drive dies out once confronted with the difficult concepts and workflows associated with development work. To overcome the anxiety brought on by a lack of understanding, this step should only be viewed as an in-depth introduction to the topic.</p>
<p>You really are not expected to understand all of it at this point. In fact, some may not understand anything at this point. This is especially true with visual learners like myself. It is very important to take good notes and ask lots of on-topic questions, as doing so will make the next step less overwhelming. If you are a visual learner, those notes will serve as your foglight.</p>
<p>If you are learning solo, treat tutorials and docs the same way. Watch or read once for the map, not for mastery. Pause and write down the terms you do not know yet. Star the parts that feel foggy. Resist the urge to restart the same video five times hoping comprehension will magically arrive. That loop keeps you stuck in Presentation. Your job in this step is exposure and orientation. Understanding comes later, with your hands on the keyboard.</p>
<p>The next step is Practice. Practice is where the learning and retention actually happen for people, and in my opinion is the most important step in the entire learning process. Please note: If you are a self-learner and you fail to invest the necessary time here, you will not learn. I cannot emphasize this enough. It does not matter how many articles or books you read, nor how many tutorial videos you sit through — if you don’t practice what you acquired in the presentation step, you will not retain the information, and you will never actually be able to apply it.</p>
<p>The more time you spend here, the greater the benefit. One thing I found incredibly useful when working on practice assignments is to not move on to a new topic unless I feel that I can comfortably explain to someone what the current code does and how it does it. This approach provides a really solid reinforcement of the material as you switch your brain between learning and teaching modes. Doing so also highlights your weaknesses and identifies where you should be investing more time before moving on to the next topic.</p>
<p>Practical tip from the trenches: rebuild the example without looking. Close the tutorial. Open a blank file. Try to recreate the idea from memory and your notes. When you get stuck—and you will—peek, then close it again and keep going. That struggle is not a sign you are bad at this. That struggle is the learning. Also, break practice into short, consistent sessions. One focused hour most days beats a heroic eight-hour weekend followed by two weeks of silence.</p>
<p>At some point in your learning, concepts will just start to click. You will have reinforced enough material that you will be expending less time and energy having to focus on every nuance. As this happens, you will find yourself feeling less overwhelmed when you fire up your code editor. Conceptual understanding that once felt out of reach will instead become second nature.</p>
<p>That does not mean the hard days disappear. You will still hit walls. You will still Google the same error twice. The difference is that the wall starts to look climbable. You will recognize patterns. You will remember that you have been confused before and figured it out. That quiet confidence is earned in Practice, not gifted in Presentation.</p>
<p>Then comes Performance: applying what you learned to something that is yours. This is where junior developers either level up or get stuck in tutorial purgatory. Performance means building a small project, contributing a fix, styling a page from scratch, or solving a real bug without a step-by-step walkthrough holding your hand the whole time. It will feel messy. That is normal. Real work is messier than coursework.</p>
<p>Start smaller than your ego wants. A tiny app that works beats a grand portfolio idea that never ships. Finish something imperfect. Then improve it. Each finished thing teaches you what tutorials never can: how to navigate ambiguity, how to debug your own decisions, and how to recover when you break things.</p>
<p>You also do not have to get through this alone. Ask questions. Join a study group, Discord, or local meetup. Rubber-duck your code to a friend. Searching well is a skill—learn to read error messages, skim docs, and ask for help with what you already tried. Asking for help is not a weakness. Refusing to ask while spinning for three days often is.</p>
<p>The good news is that once you learn one language, picking up others becomes exponentially easier! So, if you are early in your learning and you feel overwhelmed or stuck, just keep pushing through. Accept that you will have to use code you don’t understand while learning and be OK with that.</p>
<p>Thousands have walked the same path before you, and most make it through.</p>
<p>And when the doubt creeps back in, and it will, remember that classroom in 2008. Feeling lost on day one did not mean I lacked the mental faculty for this work. It meant I was beginning. You are allowed to begin messy. You are allowed to not understand yet. Keep practicing. Keep building. The clarity comes after the confusion, not before it.</p>
]]></content:encoded></item><item><title><![CDATA[The Developers Guide to Optimizing Images for the Web, Part. 1.
]]></title><description><![CDATA[Introduction.
You’ve been told to “optimize your images for the web.” Cool. But what does that actually mean, and how do you do it without nuking quality? This (opinionated) guide covers the what, why]]></description><link>https://front-end-fieldnotes.hashnode.dev/the-developers-guide-to-optimizing-images-for-the-web-part-1</link><guid isPermaLink="true">https://front-end-fieldnotes.hashnode.dev/the-developers-guide-to-optimizing-images-for-the-web-part-1</guid><category><![CDATA[performance]]></category><category><![CDATA[images]]></category><category><![CDATA[Frontend Development]]></category><category><![CDATA[image processing]]></category><category><![CDATA[Web Development]]></category><category><![CDATA[optimization]]></category><dc:creator><![CDATA[Danny Gibas]]></dc:creator><pubDate>Fri, 17 Aug 2018 00:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6abc4024e6870e09f3d32cde/7e064fb4-a2d6-4957-82c4-5879ce68c9f7.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h3>Introduction.</h3>
<p>You’ve been told to “optimize your images for the web.” Cool. But what does that actually mean, and how do you do it without nuking quality? This (opinionated) guide covers the what, why, and how. A couple of notes before we dig in:</p>
<p><em><strong>Why do you call this article opinionated?</strong></em> <a href="https://web.dev/articles/compress-images">Ilya Grigorik</a> has this to say about image optimization,</p>
<blockquote>
<p>Image optimization is both an art and a science: an art because <strong>there is no one definitive answer for how best to compress an individual image</strong>, and a science because there are many well-developed techniques and algorithms that can significantly reduce the size of an image.</p>
</blockquote>
<p><em><strong>Emphasis mine.</strong></em> That is the primary purpose of this article ; to share with you a bit of image optimization knowledge I have acquired over the years as a designer and front-end engineer. Not everyone will use the same approach as me, and your own approach will evolve as new tools, formats, and methods become available.</p>
<p>OK, enough introduction. What is image optimization?</p>
<h3>The What?!?</h3>
<p>Image optimization can be defined as:</p>
<blockquote>
<p>A process applied to digital image files that results in a non-trivial reduction in file size while maintaining a level of visual quality that is acceptable for its intended purpose.</p>
</blockquote>
<p><em><strong>What is a process?</strong></em> The process is the methodology that you apply and the tools you use to achieve the desired result. In this case, the production of an image file that loads faster on a viewport, without any detrimental loss of visual quality on a client device.</p>
<p><em><strong>What do you mean by non-trivial?</strong></em> Non-trivial in this context means that applying the process will result in a significant reduction in file size, and that this file size reduction provides an advantage to your resources and, more importantly, the user experience.</p>
<p>Simply put, image optimization is about removing unnecessary data from image files and the various methods of doing so.</p>
<h3>Why optimize?</h3>
<p>There is a wealth of compelling reasons to optimize your images before publishing to the web. What follows are just a few of them ,  the reasons that I feel are most important.</p>
<p><em><strong>Site load time reduction = improved experience.</strong></em> A great example of this is represented in both Google and Bing image search. When you search for an image, seconds later you are returned a grid of hundreds of images. However, if you click through one of those images to load the high-quality version, that single image may take longer to load than the hundreds of thumbnails.</p>
<p>The search result images have been optimized for speed over quality. They are dimensionally smaller than the original and are of discernibly lower quality. Image optimization is ultimately about providing the best user experience, and that is not in all situations dependent on high-quality, high-resolution imagery.</p>
<img src="https://cdn.hashnode.com/uploads/posts/6abc4024e6870e09f3d32cde/44387320-96cd-44f5-94ef-0fd9252a9c91.jpg" alt="" style="display:block;margin:0 auto" />

<p><em><strong>Shedding the digital calories.</strong></em> Here’s the thing about digital images. They are bloated and contain far more visual information than our displays can accurately represent in <a href="https://medium.com/@pnowelldesign/pixel-density-demystified-a4db63ba2922">pixel density</a>, and more importantly, than our eyes can discern at the <a href="https://www.octaneseating.com/science-measuring-seating-screen-distance">typical sitting distance to a display</a>.</p>
<p>If you want to test this for yourself, print out a full-resolution digital image you took on one of those photo printing booths. Open that same photo in your favorite photo editor (I use Photoshop CS) and save a copy of the image in 100 dpi. Open it full screen and compare the printed image to the digital version at normal viewing distance. Test how much visual difference you can discern.</p>
<p><em><strong>What considerations should I factor into my optimization choices?</strong></em></p>
<p>1. Which is more important to the user? Fast results, or high-quality images?  <em>I highly encourage user testing your product concepts. Otherwise, how do you know?</em></p>
<p>2. What level of quality loss can we accept before the image becomes less useful to the viewer? <em>Another push for user testing</em>.</p>
<p>3. What limitations does our hardware and software place on delivery results?</p>
<p>4. What level of loss can we accept before the quality of the results reflects negatively on the expectation people have for our brand?</p>
<p>5. What does the internet service look like in the areas where our target demographic lives?</p>
<p><em><strong>Reduction in load on resources.</strong></em> Storing huge image files on a server eats up your available storage space, space that you are paying for based on a tier. I don’t like spending more money than I need to, and I am sure you don’t either. Additionally, serving up large files bogs down a network. You may be able to serve up high-resolution images to one or two people at a time without an issue, but what about hundreds of visitors trying to load a page with multiple high-resolution images at the same time? At some point, the load increases to a point where the hardware cannot keep up, and your users experience pays the price.</p>
<p>This brings us to bandwidth.</p>
<img src="https://cdn.hashnode.com/uploads/posts/6abc4024e6870e09f3d32cde/bb296562-bc28-4b4a-b502-4c5b1cf916b7.png" alt="" style="display:block;margin:0 auto" />

<p><em><strong>Broadband is expensive, and still in short supply.</strong></em> If you are lucky enough to live in an industrialized nation, with widely available and affordable high-speed internet, congratulations! You are in the minority.</p>
<p>In the US and Europe, only about 50% of the population have a broadband subscription. Things are far worse in Asia, Africa, and the Middle East, where the <a href="https://en.wikipedia.org/wiki/List_of_countries_by_number_of_broadband_Internet_subscriptions">percentages drop into the teens and single digits</a>. In developed nations where the majority of internet use has gone mobile, subscribers experience data caps that are costly to exceed, and equally expensive to upgrade.</p>
<p>You are building a product for these users. Don’t punish their bandwidth for visiting your website. Reward their choice to visit your site with a fast-loading and optimized experience. If you don’t, you are definitely going to drive up those <a href="https://support.google.com/analytics/answer/1009409?hl=en">bounce rates</a> and annoy the people you are trying to solicit.</p>
<h3><a href="https://gemlarin.github.io/blog/the-junior-developers-guide-to-optimizing-images-for-the-web-part-2">Continue to Pt. 2 →</a></h3>
]]></content:encoded></item><item><title><![CDATA[The Developers Guide to Optimizing Images for the Web, part 2.]]></title><description><![CDATA[How it’s done.
My hope at this point is that I have convinced you of the importance of proper image optimization. From here, we will dive into the methods. I will also provide recommendations on tooli]]></description><link>https://front-end-fieldnotes.hashnode.dev/the-junior-developers-guide-to-optimizing-images-for-the-web-part-2</link><guid isPermaLink="true">https://front-end-fieldnotes.hashnode.dev/the-junior-developers-guide-to-optimizing-images-for-the-web-part-2</guid><category><![CDATA[performance]]></category><category><![CDATA[images]]></category><category><![CDATA[Frontend Development]]></category><category><![CDATA[image processing]]></category><category><![CDATA[webp]]></category><category><![CDATA[PNG]]></category><category><![CDATA[gif]]></category><category><![CDATA[jpg]]></category><category><![CDATA[Web Development]]></category><dc:creator><![CDATA[Danny Gibas]]></dc:creator><pubDate>Fri, 17 Aug 2018 00:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6abc4024e6870e09f3d32cde/5fc444bb-8493-4f70-b837-724bfacbbc48.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h3>How it’s done.</h3>
<p>My hope at this point is that I have convinced you of the importance of proper image optimization. From here, we will dive into the methods. I will also provide recommendations on tooling for specific situations, but I strongly encourage you to try other methods to see what works best for your images and your workflow.</p>
<p>As mentioned in part 1 of this article, there is no “right” way to optimize every image, and there are certainly lots of ways to do it. The first thing to consider is, “how many images do I need to process?” If it is just a couple of images, it is simple enough to just optimize them manually, one at a time in your favorite image editor. If you have a large batch of images, then automation is usually the better approach. We will explore the manual approach in this section.</p>
<h3>The manual approach.</h3>
<p>While manually processing your files is the more tedious option, it does provide several advantages ,  the first of which is maximum control.</p>
<p>When you use automation (more on that later), you are using a tool to apply an algorithm to your images in a best case scenario. They may come out looking perfectly fine, but still contain more visual data than you actually need. A smaller file size than before, but not necessarily as small as they can be.</p>
<p>For small thumbnails and graphics, the savings may not be very much, but for large banner images, you are definitely going to want to trim as much fat as possible.</p>
<p>When going the manual route, there are a couple of tools that I keep in my toolbox, and which one I use depends on the image compression format I will be using.</p>
<p>Modern browsers can render just about any image format you throw at them, but each browser has an optimal format. The process of targeting browsers with their preferred format gets a bit out of scope for this primer, but you should definitely <a href="https://css-tricks.com/optimal-image-format-browser/"><em>check out this article by the legendary Chris Coyier</em></a> for more on that.</p>
<p>Common image formats used on the web include .jpeg (also represented as .jpg due to early Windows OS three-letter file extension requirements), Apple's preferred format .png, my personal favorite — .svg, and .gif (which actually predates the modern internet).</p>
<p>The newest format intended for web use is .webp, stylized as webP. WebP compression promises to provide the artifacting reduction of .png in a file size up to <a href="https://en.wikipedia.org/wiki/WebP"><em>45% smaller</em></a> than .png. WebP is being pushed by Google.</p>
<p><a href="https://cdn.hashnode.com/uploads/covers/6abc4024e6870e09f3d32cde/64a3e40e-5b6b-4f9c-8d68-06e9c1ddf448.png"><img src="https://cdn.hashnode.com/uploads/covers/6abc4024e6870e09f3d32cde/64a3e40e-5b6b-4f9c-8d68-06e9c1ddf448.png" alt="Comparative benefits of common web image formats" style="display:block;margin:0 auto" /></a></p>
<p>(Click the image for a high-resolution version)</p>
<h3>Working with .jpg</h3>
<p>Over the years, I have found Photoshop to be the best tool to use when manually optimizing your images. Photoshop also provides the ability to batch process your images if needed. There are actually two methods to optimize and export images in Photoshop: “save as” and “save for web”. Save for web is a legacy feature, but it provides you with the most control over the output appearance.</p>
<h4>Closest neighbors and artifacting</h4>
<p>When working with JPG optimization, there are a few things to keep an eye out for. First, JPG compression works by the concept of “closest neighbor”. When the “closest neighbor” is a large block of a single color, artifacting is rarely an issue because all neighbors are the same color. The problem comes when you have an image with a subtle gradient, or lots of vertical components in the photo. As you compress the image, the algorithm starts dropping pixel data. In order to maintain the same image dimensions, your image editor starts duplicating adjacent pixels to fill the void left by the removal. The more you compress, the more pixel data is dropped and replaced with “closest neighbor” color data.</p>
<p>You may be asking, “So if you are removing the data, only to replace it with new data, how is this file compressing?”</p>
<p>To answer that, let's take a look at the image below. Let's imagine that this image is a zoomed-in look at a row of pixels within a gradient.</p>
<img src="https://cdn.hashnode.com/uploads/posts/6abc4024e6870e09f3d32cde/86d6de2f-dd38-4d6f-92fe-1e149a0a6bbc.jpg" alt="" style="display:block;margin:0 auto" />

<p>In this image, we have 5 colors, all shades of blue. Each color must have its own representation in the image source that defines the color and position of the pixel. In a JPG, each pixel uses 8 bits of data. In this example, if each block was a single pixel, we would have an image size of 40 bits.</p>
<h4>Let's simulate compression.</h4>
<img src="https://cdn.hashnode.com/uploads/posts/6abc4024e6870e09f3d32cde/c00f6a1c-af44-4c41-97cb-17ac68e51225.jpg" alt="" style="display:block;margin:0 auto" />

<p>After compression, we have the result above. Still a gradient, but now each color utilizes two pixels. What happened?</p>
<p>Well, the software looked at the closest neighbor and replaced the original color with an adjacent match. Now we only have to store 3 colors, and a pointer to the location where the color was changed, as opposed to storing the actual color information for each individual pixel. If you had a row of 10 identically blue pixels, the JPG would store the color once and a pointer for where to start and stop repeating the color before the next color shift.</p>
<p>In this contrived example, we have reduced the file to 24 bits, a reduction of about 50%. While this certainly gets the job done, it does leave us with some concerns.</p>
<p>The more compression you apply, the more color repetition you have. Gradients will be reduced in color variation and start to produce banding, as seen in the example below.</p>
<img src="https://cdn.hashnode.com/uploads/posts/6abc4024e6870e09f3d32cde/5951807f-9ea9-4304-9131-064dd53c1d41.jpg" alt="" style="display:block;margin:0 auto" />

<p>A second issue arises with <a href="https://en.wikipedia.org/wiki/Compression_artifact">artifacting</a>. Artifacting typically occurs when there is a sharp contrast between adjacent pixels and not enough file space to store a new transitional color.</p>
<p>Another type of artifacting occurs when dealing with text. Pixels get replicated from one or both contrasting colors while trying to smooth out the transition. The result is pixels that are not copies of their neighbors but rather a pixel that is a mix somewhere between the source colors. The result can be seen below. For this reason, I strongly recommend not using JPG for text.</p>
<img src="https://cdn.hashnode.com/uploads/posts/6abc4024e6870e09f3d32cde/f7731bd2-080a-4422-b44d-2dcdd6e538b0.png" alt="" style="display:block;margin:0 auto" />

<p><em><strong>To avoid this, observe the following suggestions when optimizing a JPG.</strong></em></p>
<p>1. If you have an image with a lot of gradient color, either limit the amount of compression you apply or consider using another format for the image ,  .png, for example.</p>
<p>2. Never apply additional compression to a .jpg that contains text. It is better to not include text at all in an image, but if you must, don’t use .jpg.</p>
<p>3. Banding can be disguised by adding a small amount of noise to the photo, but remember, doing this adds more pixel data back into the image, increasing its file size.</p>
<h3>Working with .png</h3>
<p>The way that compression works with PNGs is a more advanced topic and falls outside the scope of this article series. If you are interested in learning about how PNG compression works, <a href="https://medium.com/@duhroach/how-png-works-f1174e3cc7b7">check out this article</a>.</p>
<p>PNGs do not compress as well as JPGs. In fact, compressing them often requires stripping out a color channel, specifically the alpha channel. Obviously this is counterintuitive if your image requires transparency. To get around this, consider not using transparency in your graphics and instead make the background the same color as the background on your site. You can then omit the alpha channel.</p>
<p>One of the simplest tools I have found to handle the task of optimizing PNGs is <a href="https://tinypng.com/">TinyPNG</a>. TinyPNG will strip out the alpha channel (in addition to other optimizations) for you with a simple drag and drop. It also supports batch processing if you have a large set of PNGs to optimize.</p>
<h4>Drawbacks of PNG compression</h4>
<p>In my experience, I have found that compressing a large PNG for use in banners is often less than satisfactory. The image may take on a blotchy look, similar to a GIF. This is much less noticeable when used on smaller graphics that lack large gradients and large color blocks. This does not happen with all optimized PNGs, but it is enough of an issue that it needs to be mentioned. You will have to test your images and inspect after processing. If any take on a blotchy appearance, consider using JPG instead.</p>
<h3>Working with .svg</h3>
<p>SVG is hands down my favorite format to work with. It is powerful, versatile, and tiny compared to the rest. SVG is short for Scalable Vector Graphics, an <a href="https://en.wikipedia.org/wiki/XML">XML</a> <a href="https://en.wikipedia.org/wiki/Vector_graphics">Vector</a> based format. It is also the one format that you can open in any text editor to see and manipulate the code.</p>
<p>XML is a list structure for collections of data. Outside of SVG, XML has largely been replaced by JSON.</p>
<p>When you open the file, you will see several pieces of information. A header that defines the file as XML-based, a list of styles being applied to the elements, and below that all the image data. The data here represents vectors that define the shapes, curves, lines, text, and effects. Each element of the artwork will be grouped in a tag. Using CSS or JS, we can target the vector groups to do things like animating the elements. That is what makes SVG ideal for use in web animation.</p>
<h4>Optimizing SVG</h4>
<p>SVGs generally do not need to be optimized and can be used as is. However, there are ways to reduce its size if desired.</p>
<img src="https://cdn.hashnode.com/uploads/posts/6abc4024e6870e09f3d32cde/646b738f-cc27-423a-9c11-fec5367079f8.jpg" alt="" style="display:block;margin:0 auto" />

<h4>Clean up the source</h4>
<p>One thing you can do is remove some of the unnecessary meta data in the head of the file. Often the software you use to create the SVG will add additional data to the file that is not required for the file to render. This includes both comments and <strong>some</strong> meta. Be careful. You DO NOT want to remove any of the following:</p>
<ol>
<li><p>Version number</p>
</li>
<li><p>Encoding information</p>
</li>
<li><p>Viewbox sizing (though you can manually change the values if you need)</p>
</li>
<li><p>Style data</p>
</li>
<li><p>Any of the vector data</p>
</li>
</ol>
<h4>Chop the decimals</h4>
<p>The second thing you can do to optimize an SVG takes place when exporting. In most SVG editing software you will have some export options available. One of those options involves vector accuracy, and that accuracy depends on how many decimal places you store for each vector. Choosing 0 would result in no decimal places. For straight lines this will not create much problem, but when you have arcs and curves, accuracy is vital to maintain the smoothness of the curves.</p>
<p>I recommend using no more than 3 decimal places in all cases, but you can reduce the file size considerably by dropping it to 1 or 2 decimals. You will have to experiment with your SVGs to achieve the best results and the smallest possible file.</p>
<p>Concisely put, the more decimal places, the larger the file size and the more accurately the image will render. The fewer decimal places, the less accurate and smaller the file size.</p>
<h4>Drawbacks of SVG</h4>
<p>While SVG is a great option, it is not always the best choice. SVGs are best for illustrations, but not so great for images. While you can embed other image formats inside of an SVG, doing so is counter to optimization. If a flat illustration is what you need, SVG is the answer. Otherwise, go with one of the other format options.</p>
<p><em><strong>Tools of choice:</strong></em> My go to tools for handling SVGs are Adobe Illustrator and Affinity Designer.</p>
<h3>Working with .gif</h3>
<p>Some call it a “giff”, others call it a “jiff”. The GIF format has been around since the early days of BBS ; It’s longevity reflects its simplicity and versatility. It supports animation within the indexed color scheme and this makes it a common format for memes, video clips, and loader animations. It is also a great option for icons and other simple visual elements.</p>
<h4>Optimizing a GIF</h4>
<p>GIFs are already relatively small in file size as a side effect of its limited color range, but that does not mean they cannot be optimized further. To do so, focus exclusively on the color palette.</p>
<p>GIFs can contain up to 256 colors, though the fewer colors you use in the palette the smaller the resulting file. Optimizing a GIF is a pretty manual process, and the savings are often not worth the effort. If you do choose to optimize your GIFs then I suggest considering how exact the color representation must be.</p>
<p>Let’s consider an icon comprised of 6 different colors. Are all 6 colors really needed? Would the icons still work well using only 4 of the 6 colors? If so, go ahead and adjust your palette down to 4 colors in your GIF editor. You just reduced your file size by ~33% and still have an icon that looks like an icon. I strongly suggest that before making a decision about editing a graphics color palette that you first consult with the person that designed the project.</p>
<h4>Drawbacks of the GIF format</h4>
<p>No image format is free from issues, and GIFs are no exception. GIFs are not only color limited, they also do a poor job with aliasing — especially if using transparency. A side effect of color indexing is the limitation it places on smoothing out colors and edges. In order to improve the AA (anti-aliasing), you need to make more colors available, thus increasing the file size. Take a look at the image below for a visual on this problem.</p>
<img src="https://cdn.hashnode.com/uploads/posts/6abc4024e6870e09f3d32cde/69894967-bacb-4c4b-9925-2874c3fce011.gif" alt="" style="display:block;margin:0 auto" />

<p>With AA off, you have a total of 5 indexed colors. While this will result in a very small file size, it will appear very rough around the edges. The second image demonstrates AA enabled with 5 point sampling. The sharp edges start to smooth out as you increase the sample size. At 25 samples, aliasing is under control but you now have a much large file size. In this case, a JPG, PNG or webP image may actually be better option to use.</p>
<p>If your graphic contains all straight lines, or if you don’t care about rough edges, consider going with GIF. On the other hand, if you need an image with smooth color transitions, improved AA, and a broad color palette, go with JPG or PNG.</p>
<p><em><strong>Tools of choice:</strong></em> My go to tools for handling GIF optimization is Photoshop, however there are tons of free tools online to use if you don’t have access to PS.</p>
<h3>Working with .webP</h3>
<p>WebP started at Google around 2010 as a way to get smaller image files without looking worse than JPEG or PNG, basically a modern container that can do lossy photos, lossless graphics, and transparency in one format. It grew out of work related to the VP8 video codec, and the idea was simple: if browsers could decode it natively, sites could deploy less data and pages would feel snappier. Adoption was slow at first because Safari lagged, but once the major browsers caught up it became a practical default for the web instead of a niche experiment.</p>
<h4>Optimizing WebP</h4>
<p>To opptimize webP, start with a clean original, then decide how big the image really needs to be on the page (hero full-bleed vs thumbnail vs content photo). <strong>Export a WebP at roughly that display size, not a huge camera file you’ll downscale in CSS</strong>. Photos and atmospheric heroes do well as lossy WebP; start around quality 70–80 and only go lower if it still looks fine zoomed to real size. Sharp UI, logos, or screenshots with text usually want lossless (or a higher quality) so lines and lettering don’t get soft or blotchy.</p>
<p>For the actual optimize step, pick a tool that matches how you work. Squoosh is the easiest for one-offs: drop the file in, flip between quality settings, and watch the kilobytes change. If you’re already in Photoshop or Affinity Photo, export WebP straight from there after you’ve cropped/resized. For a folder of images, cwebp, ImageMagick, or a small Sharp script in Node will batch it. On a Mac, ImageOptim is a nice “drag a bunch of files and go” option; TinyPNG (and friends) work if you want a quick web upload without installing anything.</p>
<h3>Are you still here?</h3>
<p>That wraps up the common web supported image formats, and with that, this two part image optimization series. Hopefully you are convinced about the importance of image optimization for the web and this series helps you determine the best format and optimization techniques for each use case. </p>
<p>Are there any tips you have for image optimization that this series did not cover? Drop your suggestions in the comments. </p>
<p>*If you are looking to avoid an Adobe subscription then there is also the powerful and free application <a href="https://www.gimp.org/downloads/">GIMP</a>.</p>
]]></content:encoded></item><item><title><![CDATA[Google Style Slide Away Form Labels]]></title><description><![CDATA[The slide away form labels made popular by Google and others is a fantastic design element that has grown in popularity over the past couple years. I recently had a client that specifically pointed ou]]></description><link>https://front-end-fieldnotes.hashnode.dev/google-style-slide-away-form-labels</link><guid isPermaLink="true">https://front-end-fieldnotes.hashnode.dev/google-style-slide-away-form-labels</guid><category><![CDATA[CSS]]></category><category><![CDATA[UI]]></category><category><![CDATA[forms]]></category><category><![CDATA[jQuery]]></category><dc:creator><![CDATA[Danny Gibas]]></dc:creator><pubDate>Sat, 17 Feb 2018 00:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6abc4024e6870e09f3d32cde/5693612d-b960-406d-97c1-55a8b6a56b5e.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>The slide away form labels made popular by Google and others is a fantastic design element that has grown in popularity over the past couple years. I recently had a client that specifically pointed out the Google Accounts login form fields and noted how much he liked that element. I have to admit, I like it too.</p>
<img src="https://cdn.hashnode.com/uploads/posts/6abc4024e6870e09f3d32cde/b9670ed0-2fcd-4d7e-bf06-ae1556a1b6bf.png" alt="" style="display:block;margin:0 auto" />

<p>A simple solution to a longtime UI issue — “what the heck do I do with those form labels to keep them nicely on one line?” You could left align them to the field, but this of course introduces unsightly spacing and alignment issues due to the varying length of label text. You could use <a href="https://css-tricks.com/hang-on-placeholders/">hang-on placeholders</a> in lieu of labels — which works, but introduces other accessibility issues. You could stick them right above or below the fields, but this makes it difficult to condense the form.</p>
<img src="https://cdn.hashnode.com/uploads/posts/6abc4024e6870e09f3d32cde/e658e4dd-e6c6-4bbc-8730-3bf018a037ca.jpg" alt="" style="display:block;margin:0 auto" />

<p>Slide away labels solves all of these issues! There are many different versions of this feature, some more successful than others. This is my version of the solution.</p>
<p>Here is an example of what we will be building:</p>
<img src="https://cdn.hashnode.com/uploads/posts/6abc4024e6870e09f3d32cde/d5102758-887d-403b-b560-7c7c3786311b.png" alt="" style="display:block;margin:0 auto" />

<p>In this example, we use jQuery for its concise and cross-platform <code>.focus()</code> method, but you can use vanilla Javascript instead if that is what you prefer. First, we need to markup the form. We will use 3 fields and a button in this simple example. Each label and input is wrapped in a <code>.field-wrapper</code> class in order to provide a needed parent element for each field.</p>
<pre><code class="language-html">&lt;form onSubmit="JavaScript:submitFunction()" id="contact-form" method="post"&gt;
  &lt;div id="form"&gt;
    &lt;div class="field-wrapper"&gt;
      &lt;label&gt;First name&lt;/label&gt;
      &lt;input
        id="firstName"
        required
        type="text"
        name="first_name"
        class="form-control"
      /&gt;
    &lt;/div&gt;
    &lt;div class="field-wrapper"&gt;
      &lt;label&gt;Last name&lt;/label&gt;
      &lt;input
        id="lastName"
        required
        type="text"
        name="last_name"
        class="form-control"
      /&gt;
    &lt;/div&gt;
    &lt;div class="field-wrapper"&gt;
      &lt;label&gt;Phone number&lt;/label&gt;
      &lt;input
        id="phoneNumber"
        required
        type="tel"
        name="phone"
        class="form-control"
      /&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/form&gt;
</code></pre>
<p>Next, some basic CSS to style the form. We also add our label transition animation. The label is set to pointer-events:none to allow the click to pass through to the input field.</p>
<pre><code class="language-css">#form{ margin-top:50px;}
</code></pre>
<pre><code class="language-css">.field-wrapper{ position:relative;}
</code></pre>
<pre><code class="language-css">label{ 
   position:absolute; 
   top:7px; 
   margin-top:0; 
   pointer-events: none; 
   -webkit-transition: all .2s ease-in-out; 
   transition: all .2s ease-in-out; 
   font-size:17px;
}
</code></pre>
<pre><code class="language-css">.form-control{ 
   padding:10px 0 0 0 !important; 
   font-size:22px; 
   border:none; /* remove the default field borders */ 
   border-bottom: 1px solid grey; 
   background: white; 
   height:auto; 
   color:black;
}
</code></pre>
<pre><code class="language-css">.form-control:focus{ 
   border-bottom: 1px solid green; 
   -webkit-box-shadow: none; 
   box-shadow: none;
}
</code></pre>
<pre><code class="language-css">#btnSubmit{ margin-top:30px;}
</code></pre>
<pre><code class="language-css">.openup{ margin-top:-20px; font-size: 14px;}
</code></pre>
<p>Finally, we add our script. The idea here is that when the input field is selected, we attach an <code>.openup</code> class that initiates the transition. I use<code>focus()</code> here instead of <code>on()</code> to support keyboard operations. Also, we update the underline to indicate the active input. Of course, you can style the active state however you like.</p>
<p>You may notice that I am setting the borders twice here. First I set all input field borders back to default grey, then I set the active field border state. I did this so that I don’t have to track the previously active input field.</p>
<pre><code class="language-javascript">$('.form-control').focus( function(e) {       
   $(this).parent().find('label').addClass('openup');    
   $('.form-control').css('borderBottom', '1px solid grey'); 
   $(this).css('borderBottom', '1px solid rgb(111, 189, 68)');  });
</code></pre>
<p>That's it! See it working in <a href="https://jsfiddle.net/gemlarin/v8dbtyep/">this Fiddle</a>.</p>
<p>*Thanks /u/max_kek !</p>
]]></content:encoded></item></channel></rss>