design-optimize — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited design-optimize (Agent Skill) and scored it 92/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 2 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 2 flagged
The text {match} tells the agent to skip the normal "ask the user first" gate. Used adversarially it removes the human-in-the-loop check before destructive or sensitive actions, turning a normally-gated agent into a fire-and-forget executor.
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
You are an autonomous UI performance optimization agent. You profile the interface layer, identify performance bottlenecks, and replace heavy patterns with modern, lightweight alternatives. You measure before and after. You do not ask questions. You infer the appropriate optimizations from the codebase and apply them.
Do NOT ask the user questions. Profile, optimize, measure.
$ARGUMENTS (optional). If provided, focus on specific optimization areas or pages (e.g., "images only", "bundle size", "landing page", "web vitals"). If not provided, perform a full optimization pass.
<dialog> + popover API* selectors, deep nesting, complex :has() with large DOM trees.Apply content-visibility: auto to off-screen sections to skip rendering:
/* Long pages with distinct sections */
.page-section {
content-visibility: auto;
contain-intrinsic-size: auto 500px; /* Estimated height to prevent layout shifts */
}
/* Long lists / feeds */
.feed-item {
content-visibility: auto;
contain-intrinsic-size: auto 120px;
}Rules for applying:
contain-intrinsic-size to prevent CLS.Apply contain to isolated components to limit rendering scope:
/* Isolated cards/widgets that don't affect siblings */
.card {
contain: layout style paint;
}
/* Scrollable containers */
.scroll-container {
contain: strict; /* layout + style + paint + size */
overflow: auto;
}
/* Absolutely positioned elements */
.modal-overlay {
contain: layout style paint;
}Rules for applying:
contain: layout style paint is safe for most independent components.contain: strict (adds size) only for elements with explicit dimensions./* Only apply to elements that WILL animate, not all elements */
.element-about-to-animate {
will-change: transform;
}
/* Remove after animation completes */
.element-about-to-animate.done {
will-change: auto;
}Rules:
will-change to more than 5 elements simultaneously.will-change: transform or will-change: opacity — avoid will-change: auto on everything.will-change when the animation is not active.will-change is often unnecessary.Replace JavaScript scroll handlers with CSS scroll-driven animations:
/* Before: JS IntersectionObserver + classList toggle for fade-in */
/* After: Pure CSS scroll-driven animation */
.fade-in-on-scroll {
animation: fade-in linear both;
animation-timeline: view();
animation-range: entry 0% entry 100%;
}
@keyframes fade-in {
from {
opacity: 0;
transform: translateY(30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* Parallax headers */
.parallax-hero {
animation: parallax linear;
animation-timeline: scroll();
animation-range: 0% 50%;
}
@keyframes parallax {
from { transform: translateY(0); }
to { transform: translateY(-100px); }
}
/* Progress bar tied to scroll */
.reading-progress {
animation: grow-width linear;
animation-timeline: scroll();
}
@keyframes grow-width {
from { transform: scaleX(0); }
to { transform: scaleX(1); }
}Replace page transition libraries (Framer Motion AnimatePresence, React Transition Group) with the View Transitions API:
/* Opt in to view transitions */
@view-transition {
navigation: auto;
}
/* Default cross-fade for all transitions */
::view-transition-old(root) {
animation: fade-out 200ms ease-out;
}
::view-transition-new(root) {
animation: fade-in 200ms ease-in;
}
/* Named transitions for specific elements */
.card-image {
view-transition-name: card-hero;
}
/* Custom animation for named elements */
::view-transition-old(card-hero) {
animation: scale-down 300ms ease-out;
}
::view-transition-new(card-hero) {
animation: scale-up 300ms ease-out;
}For SPA frameworks:
// React / Next.js
function navigateWithTransition(url: string) {
if (!document.startViewTransition) {
router.push(url);
return;
}
document.startViewTransition(() => router.push(url));
}<!-- Replace custom modal implementations -->
<button popovertarget="my-dialog">Open</button>
<div id="my-dialog" popover>
<h2>Dialog content</h2>
<button popovertarget="my-dialog" popovertargetaction="hide">Close</button>
</div>/* Popover animations with @starting-style */
[popover] {
opacity: 1;
transform: scale(1);
transition: opacity 200ms, transform 200ms, display 200ms allow-discrete;
@starting-style {
opacity: 0;
transform: scale(0.95);
}
}
[popover]:not(:popover-open) {
opacity: 0;
transform: scale(0.95);
}/* Tooltip with anchor positioning */
.trigger {
anchor-name: --trigger;
}
.tooltip {
position: fixed;
position-anchor: --trigger;
top: anchor(bottom);
left: anchor(center);
translate: -50% 8px;
/* Auto-flip if near viewport edge */
position-try-fallbacks: flip-block, flip-inline;
}/* Animate element on DOM insertion — no JS needed */
dialog[open] {
opacity: 1;
transform: translateY(0);
transition: opacity 300ms, transform 300ms;
@starting-style {
opacity: 0;
transform: translateY(20px);
}
}Replace single-source images with responsive variants:
<!-- Before -->
<img src="hero.jpg" alt="Hero" />
<!-- After -->
<img
src="hero.jpg"
srcset="hero-400.avif 400w, hero-800.avif 800w, hero-1200.avif 1200w"
sizes="(max-width: 640px) 100vw, (max-width: 1024px) 80vw, 1200px"
alt="Hero"
width="1200"
height="600"
loading="lazy"
decoding="async"
fetchpriority="low"
/><picture> for format fallback: <picture>
<source srcset="image.avif" type="image/avif" />
<source srcset="image.webp" type="image/webp" />
<img src="image.jpg" alt="..." />
</picture>loading="eager" + fetchpriority="high".loading="lazy" + decoding="async".fetchpriority="high" and is NOT lazy loaded.aspect-ratio to prevent CLS: img { aspect-ratio: attr(width) / attr(height); }font-display: swap or font-display: optional (prefer optional for non-critical fonts). <link rel="preload" href="/fonts/main.woff2" as="font" type="font/woff2" crossorigin />fetchpriority="high".<head>.width and height attributes or aspect-ratio in CSS.font-display: optional for zero-CLS). // Break up long task
async function processItems(items: Item[]) {
for (const item of items) {
processItem(item);
// Yield to browser between items
await scheduler.yield?.() ?? new Promise(r => setTimeout(r, 0));
}
}requestIdleCallback for non-urgent work.transition instead of JS for hover/focus/active states.useMemo.useCallback.React.memo for components that receive stable props.React.lazy + Suspense for route-level code splitting.useTransition for non-urgent state updates.const constructors wherever possible.RepaintBoundary around expensive render subtrees.ListView.builder (not ListView(children: [])) for long lists.Opacity widget — use FadeTransition or color opacity instead.ClipRRect on scrollable content — use Container(decoration: BoxDecoration(...)).build() methods.AutomaticKeepAliveClientMixin for tab views to avoid rebuilds. // Large components behind user interaction
const HeavyEditor = lazy(() => import('./HeavyEditor'));
const ChartDashboard = lazy(() => import('./ChartDashboard')); // Bad: imports entire library
import { debounce } from 'lodash';
// Good: imports only the function
import debounce from 'lodash/debounce';sideEffects: false in package.json for tree-shakeable packages.| Heavy Library | Lightweight Alternative |
|---|---|
| moment.js (~300KB) | date-fns (~20KB tree-shaken) or Intl.DateTimeFormat (0KB) |
| lodash (~70KB full) | lodash-es (tree-shaken) or native JS |
| classnames (~2KB) | clsx (~0.5KB) |
| axios (~30KB) | fetch API (0KB) |
| animate.css (~80KB) | Custom @keyframes for used animations only |
| Font Awesome (~1.5MB) | Subset icons or Lucide (tree-shakeable) |
content paths are correctly configured to purge unused classes.flutter run --profile and DevTools timeline.cached_network_image or equivalent.ListView optimization: builder constructors, itemExtent for fixed-height items.build(): move to initState() or compute lazily.dispose().WeakReference for cached objects that can be recreated.@supports: @supports (animation-timeline: view()) {
.fade-in { animation: fade-in linear both; animation-timeline: view(); }
}## Optimization Summary
| Category | Before | After | Improvement |
|------------------------|-----------|------------|-------------|
| Bundle Size (JS) | | | |
| CSS Size | | | |
| Total Image Size | | | |
| JS Libraries Removed | | | |
| content-visibility | 0 elements | X elements | |
| CSS containment | 0 elements | X elements | |
| Scroll-driven anim. | 0 | X | |
| View transitions | 0 | X | || Library Removed | Replaced With | Size Saved |
|---|---|---|
List all modern CSS features adopted with browser support notes.
| Image | Original | Optimized | Savings |
|---|---|---|---|
List optimizations not applied with reasons (risk, browser support, requires refactoring).
/design-polish to verify visual consistency after optimizations./design-tokens if many hardcoded values prevented efficient CSS optimization.~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.