A FRONTEND JUDGMENT SKILL FOR CODING AGENTS

Same experience.
Fewer moving parts.

Build, simplify, and audit web UI using what the browser and your codebase already provide. Keep the behavior, accessibility, compatibility, and feel. Cut the machinery that does not earn its place.

Understand the skill

WHAT IS FEWER PARTS?

A simpler path to the same result.

Fewer Parts is an installable skill for coding agents. It reads the interface you have, the experience you need, and the constraints of your project.Then looks for the adoptable progressive enhancements.

It is not a UI library, a framework, or a fixed catalogue of CSS tricks. It adds judgment: when to use the platform, when to adapt an existing pattern, and when the code in front of it should stay exactly as it is.

01

Build

Start from the interaction and choose proportionate machinery.

02

Simplify

Preserve the experience while removing duplicated work and state.

03

Audit

Find where complexity earns its place—and where it no longer does.

Modern CSS and platform guidance surface possibilities. Baseline and Can I Use provide evidence. Fewer Parts makes the decision in the context of your codebase.

THE PROMISE, MADE VISIBLE

Keep the experience. Lose the machinery.

These are project decisions, not blanket rules. Choose a case to see what stays, what disappears, and why the result is still honest.

Choose a project decision
REDUCE THE MACHINERY

A textarea that follows its content

Measuring a field in JavaScript to resize it is work the browser can do.

Open the worked example
What stays
A field that grows, scrolls, and remains manually resizable
What goes
Two listeners, height measurement, and inline style mutation
Decision
Progressively enhance with CSS
CURRENT PROJECT · JS
const resize = (field) => {
  field.style.height = "auto";
  field.style.height = `${field.scrollHeight}px`;
};

field.addEventListener("input", () => resize(field));
window.addEventListener("resize", () => resize(field));
WITH FEWER PARTS · CSS
.comment-field {
  field-sizing: content;
  min-block-size: 3lh;
  max-block-size: 12lh;
}

Newly available, so the base stays a conventional resizable textarea with a sensible minimum height. Nothing breaks where the property is ignored.

REDUCE THE MACHINERY

A card that reads its own space

A viewport breakpoint answers the wrong question for a component that moves between a page and a sidebar.

Open the worked example
What stays
The same compact and expanded card layouts
What goes
Viewport coupling and parent-specific overrides
Decision
Let the component respond to its container
CURRENT PROJECT · CSS
/* Wrong in the sidebar: the viewport is wide, the card is not. */
@media (min-width: 640px) {
  .resource-card {
    grid-template-columns: 96px 1fr;
  }
}
WITH FEWER PARTS · CSS
.card-slot {
  container-type: inline-size;
}

@container (min-width: 320px) {
  .resource-card {
    grid-template-columns: 96px 1fr;
  }
}

The single-column card remains the base layout, so the component is still right in a slot that never reaches the threshold.

REDUCE THE MACHINERY

The parent reflects real form state

A class mirroring a checked radio is a second copy of state the form already holds.

Open the worked example
What stays
Native selection, labels, and keyboard behavior
What goes
Mirrored classes and a change listener
Decision
Style the state the form already owns
CURRENT PROJECT · JS
group.addEventListener("change", () => {
  for (const option of group.querySelectorAll(".option")) {
    const input = option.querySelector("input");
    option.classList.toggle("is-selected", input.checked);
  }
});
WITH FEWER PARTS · CSS
.option:has(input:checked) {
  border-color: var(--accent);
  background: var(--accent-surface);
}

The listener goes. Arrow-key selection, labels, and the checked state stay exactly where they were, in the native radio group.

REDUCE THE MACHINERY

Modality that comes from the platform

A custom overlay has to rebuild the top layer, focus containment, Escape, and focus return, then unwind all of it.

Open the worked example
What stays
A modal flow with application-specific save logic
What goes
Focus trapping, Escape handling, scroll lock, and z-index work
Decision
Keep app logic; return modality to the platform
CURRENT PROJECT · JS
overlay.hidden = false;
document.body.style.overflow = "hidden";
document.addEventListener("keydown", closeOnEscape);
trapFocus(overlay);
// ...then undo all four on close, and put focus back on the trigger
WITH FEWER PARTS · JS
dialog.showModal();

dialog.addEventListener("close", () => {
  save(dialog.returnValue);
});

Only application logic stays in JavaScript. The skill still checks the accessible name, how the dialog is dismissed, and where focus lands afterwards.

REDUCE THE MACHINERY

One set of tokens, both themes

A duplicated palette in a media query drifts the moment someone changes one colour.

In range beyond the five demos

What stays
The same light and dark themes
What goes
A second token declaration block
Decision
Keep both values beside the token they define
CURRENT PROJECT · CSS
:root {
  --surface: #fff;
  --text: #16202e;
}

@media (prefers-color-scheme: dark) {
  :root {
    --surface: #10182a;
    --text: #e8eefb;
  }
}
WITH FEWER PARTS · CSS
:root {
  color-scheme: light dark;
  --surface: light-dark(#fff, #10182a);
  --text: light-dark(#16202e, #e8eefb);
}

There is no demo for this one on this site. The skill gets there the way it gets anywhere else: it reads the live reference while it writes your code, and it checks the contrast of both results.

REDUCE THE MACHINERY

The superseded request actually stops

Ignoring a late response is not the same as cancelling the work behind it.

In range beyond the five demos

What stays
Latest-result wins and real error handling
What goes
A request that keeps running after it is obsolete
Decision
Use the platform cancellation primitive
CURRENT PROJECT · JS
let latest = 0;

const search = async (term) => {
  const id = ++latest;
  const response = await fetch(`/search?q=${encodeURIComponent(term)}`);
  // The superseded request still ran, and still resolved.
  if (id === latest) render(await response.json());
};
WITH FEWER PARTS · JS
let inFlight;

const search = async (term) => {
  inFlight?.abort();
  inFlight = new AbortController();
  try {
    const response = await fetch(`/search?q=${encodeURIComponent(term)}`, {
      signal: inFlight.signal,
    });
    render(await response.json());
  } catch (error) {
    if (error.name !== "AbortError") throw error;
  }
};

No demo for this one either. Note what did not disappear: the error path is still handled, because a cancelled request is not a failed one.

REDUCE THE MACHINERY

URL parsing that follows the platform

A regular expression becomes a second, incomplete definition of what a URL is.

Open the worked example
What stays
Validation feedback for absolute and relative URLs
What goes
A handwritten parser and its edge-case tests
Decision
Use the URL parser, with a constructor fallback
CURRENT PROJECT · JS
const urlPattern = /^(https?:\/\/)?[\w.-]+\.[a-z]{2,}(\/.*)?$/i;

const isValid = (value) => urlPattern.test(value);
WITH FEWER PARTS · JS
const isValid = (value, base = location.href) =>
  URL.canParse?.(value, base) ?? canConstructUrl(value, base);

The fallback uses the same URL constructor semantics, so older browsers get the same answer rather than a different home-grown grammar.

KEEP THE MACHINERY

A carousel whose complexity is doing real work

Native scroll snapping can make a lovely gallery. It is not automatically a replacement for product requirements such as virtualization, live announcements, RTL, and controlled state.

Restraint is part of the skill

What stays
The tested interaction and the library that currently delivers it
What goes
Nothing—replacement would only move complexity into project code
Decision
Keep the dependency; document why it earns its place
CURRENT PROJECT · JS
FEWER PARTS · KEEP · JS

Fewer Parts is not a campaign against JavaScript or dependencies. If the existing machinery is proportionate, tested, and easier to maintain than the proposed replacement, the smaller decision is not to rewrite it.

Compatibility is decided per project. The skill checks the exact syntax or API member against your declared targets and keeps a usable base when a feature is new. The final example is deliberate: fewer parts sometimes means avoiding a rewrite.

Five examples. One way of thinking. RUNNABLE PROOF

Try the outcomes, then inspect the decisions.

Support reviewed 14 September 2026. Baseline is a starting point; the skill checks your browser targets and the exact features in use. Newly available recipes preserve a usable base. About Baseline ↗

THE OTHER MODE

Or point it at the code
you already have.

Ask for an audit or a review and the skill reads the repository instead of writing to it. It establishes scope and your browser targets first, scores the dimensions it could actually assess, then ranks findings with file evidence, effort, and compatibility risk. Nothing changes unless you ask for the fixes separately.

Audit this project's UI with $fewer-parts. Read-only — no changes yet.

Sample report

14 components, 3 routes, the Tailwind v4 theme, and the shared stylesheet

FEWER PARTS SCORE2.8/ 5
COVERAGE CONFIDENCEMedium

Carefully labelled and consistent, with layout and overlay code still doing work the platform can own. Four changes account for most of the available reduction; the form-validation code should stay.

Scored dimensions
DimensionScoreEvidence
Interaction quality2/5The custom modal works by keyboard, but its close animation cannot be interrupted and focus briefly reaches the page behind it.
Project and styling-system fit3/5Tailwind v4 is installed and mostly used; three components keep a parallel stylesheet with duplicated spacing.
Runtime and dependency economy2/5Overlay, selected-state, and textarea measurement code duplicate behavior the current targets provide natively.
Accessibility and semantics4/5Labelling, focus order, announcements, and reduced-motion handling are consistent; preserve them during simplification.
Compatibility and fallbacks3/5Browserslist targets are respected, but two enhancements leave no usable base behind them.
Consistency and maintainability3/5Conventions are clear. Token use drifts in the more recently added components.

0–2 · gaps worth acting on3 · sound, mixed patterns4–5 · strong

  1. NowR-01

    Card layout keyed to the viewport

    src/components/ResourceCard.css:42

    Give the card slot a size container and move both breakpoints to @container, keeping the one-column layout as the base.

    Expected reduction: Remove two viewport-specific card overrides.

    • Impact 4/5
    • Effort S
    • Compat risk Low
    • Confidence High
  2. NowS-02

    Selected state synchronised in JavaScript

    src/components/PlanPicker.tsx:61

    Style the selected card with :has(input:checked) and drop the change listener. The radio group already holds the state.

    Expected reduction: Remove one listener and a mirrored selected-state class.

    • Impact 3/5
    • Effort S
    • Compat risk Low
    • Confidence High
  3. NextN-03

    Custom overlay reimplements modality

    src/ui/Modal.tsx:18

    Move to dialog.showModal() for the top layer, focus containment, and Escape. Keep the existing close animation and the confirm/cancel return values.

    Expected reduction: Remove the focus trap, Escape handler, scroll lock, and overlay z-index contract.

    • Impact 4/5
    • Effort M
    • Compat risk Low
    • Confidence Medium
  4. LaterM-04

    Spacing values duplicated outside the token scale

    src/styles/legacy.css

    Fold the remaining literal values into the Tailwind v4 theme so new components inherit them.

    Expected reduction: Remove the parallel spacing scale after consumers migrate.

    • Impact 2/5
    • Effort M
    • Compat risk Low
    • Confidence High

Strengths to preserve

Form labelling, validation timing, focus order, and prefers-reduced-motion handling are consistent. The form script owns real application behavior and should not be removed merely because CSS can style validity.

Verification gaps

Safari was not available in this environment, so the container-query and :has() recommendations were checked against documentation rather than run there. Two routes behind authentication were not inspected.

An illustrative excerpt: the project and the scores are invented, the structure is the one the skill follows. The number describes whether the intended experience uses proportionate, project-appropriate machinery — not code quality, accessibility conformance, or performance overall.

THE APPROACH

Less machinery, with judgment.

01

Start with the outcome.

Start with the intended feel and behavior, then choose the smallest implementation that honestly delivers them.

02

Keep the whole interaction.

Preserve keyboard access, application state, interruption, and the behavior people depend on.

03

Check what really works.

Read current sources without repeating discovery. Test the result, and keep existing machinery when it still earns its keep.

FOR YOUR CODING AGENT

Take the patterns
into your project.

Build, simplify, or audit. It uses platform guidance as evidence, adapts the decision to your codebase and styling system, and needs no MCP server or API key.

Download the skill View repository ↗

Install with one command

Run one of these from your project root. The installer finds your coding agents and lets you choose where to add the skill.

npm / npxnpx skills add GavinJaynes/skills --skill fewer-parts
pnpmpnpm dlx skills add GavinJaynes/skills --skill fewer-parts
Bunbunx skills add GavinJaynes/skills --skill fewer-parts

Prefer a manual install? Download the ZIP, extract it, and copy the complete fewer-parts folder to .agents/skills/fewer-parts/.

Then ask your agent:

Use $fewer-parts to deliver the same or better interaction with less project-specific machinery. Preserve the design, behavior, accessibility, browser targets, and existing styling conventions.

For agents with a different skill location, use their supported installation directory. Keep the references and assets together.