ux/ui expert — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited ux/ui expert (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
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.
Frontend + UX/UI Skill Matrix — applicable to Next.js App Router, Astro, and React projects.
'use client' only at interaction boundaries (forms, polling, real-time, toggles)min-h-svh on main shells (handles mobile browser dynamic toolbars)client:load → immediate, client:idle → non-critical, client:visible → below fold.astro — no 'use client' equivalent needed.dark class on <html> + CSS variable overrides in .dark {}var(--token-name)box-sizing: border-box globally — predictable sizingprefers-reduced-motion: always disable/minimize animations when setThe most commonly missed UX gap. Sidebar must work as a drawer on mobile:
'use client'
export function Sidebar() {
const [open, setOpen] = useState(false)
const pathname = usePathname()
useEffect(() => { setOpen(false) }, [pathname])
useEffect(() => {
document.body.style.overflow = open ? 'hidden' : ''
return () => { document.body.style.overflow = '' }
}, [open])
return (
<>
<header className="sticky top-0 z-40 flex items-center justify-between px-4 h-14 lg:hidden">
<Logo />
<button onClick={() => setOpen(true)} aria-label="Open menu"><Menu size={20} /></button>
</header>
{open && <div className="fixed inset-0 z-40 bg-black/60 lg:hidden" onClick={() => setOpen(false)} />}
<aside className={`fixed inset-y-0 left-0 z-50 w-[280px] transition-transform duration-300
lg:sticky lg:top-0 lg:h-screen lg:translate-x-0 ${open ? 'translate-x-0' : '-translate-x-full'}`}>
{/* nav content */}
</aside>
</>
)
}/* Page shell — always fill responsive padding, never leave empty breakpoints */
.page-shell {
max-width: 1280px;
margin-inline: auto;
padding: var(--space-6) var(--space-4);
}
@media (min-width: 640px) { .page-shell { padding: var(--space-8) var(--space-6); } }
@media (min-width: 1280px) { .page-shell { padding: var(--space-10) var(--space-8); } }
/* Grids always need explicit gap */
.metric-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: var(--space-4); }
.panel-grid { display: grid; gap: var(--space-4); }
@media (min-width: 1024px) { .panel-grid { grid-template-columns: 1fr 1fr; } }Every table must have: cell padding, horizontal scroll wrapper, hover state, empty state.
.data-table th { padding: var(--space-3) var(--space-4) var(--space-2); }
.data-table td { padding: var(--space-3) var(--space-4); border-top: 1px solid var(--border); }Wrap all tables: <div class="overflow-x-auto rounded-[inherit]"><table>...</table></div>
useEffect(() => {
let cancelled = false
const poll = async () => {
try {
const data = await fetchSomething()
if (!cancelled) setData(data)
} catch {
// always include catch — silent errors hide real problems
}
}
poll()
const id = setInterval(poll, 4000)
return () => { cancelled = true; clearInterval(id) }
}, [])aria-labelfocus-visible ring on all interactive controlsprefers-reduced-motion respectedfocus-visible ring on all controls_Last reviewed: 2026-05-14 — automated polish pass per issue #100._
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.