mobile-sweep — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited mobile-sweep (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.
Open a running web app in headless Chromium at three phone/tablet breakpoints (360×640, 390×844, 768×1024), walk every interactive element + every visible dialog, and flag the four high-impact mobile-layout failure modes:
document.documentElement.clientWidth.scrollWidth > clientWidth on a button / input / link (visible label is being cut).100vw - 16px or that hang off either side.Plus an optional static CSS scan that flags grid-template-columns patterns packing 3+ fixed-pixel widths summing to >200px without a @media (max-width: ...) override — the exact pattern that caused today's rewards-edit-row clipping bug.
The actual sweep is done by a bundled Playwright script (scripts/sweep.py). The skill body just orchestrates running it, interprets the findings, and proposes fixes the user can apply directly.
These elements are intentionally tiny or off-screen and should not be flagged. The scanner skips them automatically; pass --no-default-ignores to see them anyway.
.skip-to-content, .sr-only,.visually-hidden, .screen-reader-text, anything with aria-label starting with "skip", and structurally-detected SR-only elements (1×1 absolutely-positioned with overflow: hidden / clip-path).
is <p>, <li>, <td>, <dd>, <blockquote>, or any element with a class containing prose. WCAG 2.5.8 explicitly exempts these from the 24×24 target size rule. Exempted only from the small-touch-target check; off-screen and clipped-text still apply.
============================================================ === PRE-FLIGHT === ============================================================
Before running, verify:
Firebase Hosting URL, a localhost:NNNN dev server, or a file:// path to a static index.html. If the user only said "check mobile" with no context, look in the conversation: is there a recently deployed URL, or a firebase deploy output? Use that.
python3 --version). python3 -c "from playwright.sync_api import sync_playwright" 2>&1If that errors, run:
pip install playwright && playwright install chromiumuser must say so — Playwright will land on the sign-in modal otherwise. Options: (a) sign in once with a saved storage state, (b) point at a deep-linked screen, (c) use --open-selectors to dismiss the sign-in modal in-page.
Recovery:
worthless without it.
the public landing.
VALIDATION: python3 scripts/sweep.py --help exits 0. FALLBACK: if Playwright install fails (network, sandbox), stop and tell the user. Do not fake findings.
============================================================ === PHASE 1: SWEEP THE APP === ============================================================
Run the bundled scanner:
python3 ~/.claude/skills/mobile-sweep/scripts/sweep.py <URL>Useful flags:
| Flag | When to use |
|---|---|
--breakpoints 360,390,768 | Override the default breakpoints. Width-only; the script picks a matching height (small Android 360×640, iPhone 390×844, iPad 768×1024, otherwise width × width*1.78). |
--open-selectors "#open-rewards-btn,#open-settings-btn" | Click each selector before sweeping. Use this to reach modals/menus that aren't visible on first load. The skill calls these in sequence at each breakpoint. |
--wait-selectors ".board" | Wait for these to appear before sweeping (handy when content is fetched after first paint). |
--static-css <path> | Path to scan for rigid-grid CSS issues. Defaults to .; pass an empty string "" to disable. |
--out mobile-sweep-out | Output directory for the report + screenshots. Default mobile-sweep-out/. |
--ignore-selectors ".chip-marketing,.fab-decoration" | Add custom selectors to skip during checks. Useful for known decoration-only elements that aren't worth flagging. |
--no-default-ignores | Disable the built-in WCAG exemptions (skip-to-content links, sr-only/visually-hidden elements, inline prose anchors). Use when you want a strict baseline. |
The script writes:
<out>/mobile-sweep-report.md — the formatted report<out>/finding-<width>-<state>-<i>.png — a tight screenshot crop of each findingExit code: nonzero if any errors are found, so this can gate CI.
VALIDATION: the report file exists and contains at least the header. FALLBACK: if the script crashes (timeout loading the URL, browser launch failure), capture the stderr and surface it to the user before guessing what's wrong.
============================================================ === PHASE 2: INTERPRET AND FIX === ============================================================
Open the generated report. Each finding has:
scrollWidth 320 > clientWidth 80, clipped by 240px)For off-screen and clipped-text findings, the dominant pattern is a rigid CSS grid or flex layout. Don't paper over with overflow: hidden. Instead:
grid-template-columns: <pile of fixed px> to grid-template-areasthat stack rows under @media (max-width: 640px) so the wide field gets its own row.
min-width: 0 on the parent and width: 100% on thefield so the input fills the available row width.
For small-touch-target findings, add:
@media (hover: none) {
.my-btn {
min-width: 44px;
min-height: 44px;
}
}44px is the iOS HIG minimum. 48px is Material's guidance — both are safe.
For modal-too-wide findings, change the modal width to:
width: min(<desktop-width>, calc(100vw - 16px));For the rigid-grid static warnings, those are the _latent_ version of the runtime issues — the user might not have hit them yet because the modal isn't open, but they will. Apply the same grid-template-areas mobile override.
VALIDATION: a second run of the sweep on the fixed code shows the specific findings resolved. (Don't claim a fix worked without re-running.) FALLBACK: if a fix you applied moved a finding instead of removing it, that's the same bug in a different place — keep iterating until the report is clean.
============================================================ === PHASE 3 (OPTIONAL): WIRE INTO CI === ============================================================
If the user wants this to gate deploys, the script's nonzero exit makes that easy:
# .github/workflows/ci.yml
- name: Mobile sweep
run: python3 ~/.claude/skills/mobile-sweep/scripts/sweep.py "$DEPLOY_PREVIEW_URL"Only suggest this when the user has indicated CI is set up.
============================================================ === SELF-REVIEW === ============================================================
Score the output (1–5):
Did the report surface findings or confirm clean?
--open-selectors reach the modals the user actuallycares about? Were findings de-duplicated across breakpoints (the same element clipped at 360 and 390 should not double-count)?
decoration-only elements being flagged as touch targets?
If any score < 4:
--open-selectors to reach unseen modals, or --wait-selectorsif content was being captured before it loaded.
them by adding their parent button class to a future ignore list (for now, mention them as "likely false positives" in the report).
============================================================ === LEARNINGS CAPTURE === ============================================================
After completing, append one entry to ~/.claude/skills/mobile-sweep/LEARNINGS.md:
## <YYYY-MM-DD> — <project + scan scope>
- **What worked:** <concrete fix suggested by the report that landed
immediately, or a `--open-selectors` value that reached the right modal>
- **What was awkward:** <false positives, scroll-clipped inputs that were
intentional, modals that needed a multi-step click sequence to open>
- **Suggested patch:** <one concrete improvement — "default `--wait-selectors`
for SPAs to `body[data-loaded]`", "add `--ignore-selectors` for known
decorative icons", "auto-detect modal open buttons">
- **Verdict:** [Smooth / Minor friction / Major friction]engine. If Playwright is not installable, stop and say so.
to see what's broken.
transform: scale() or zoom as fixes. Both break touchhit-areas and accessibility — fix the layout instead.
(decorative pseudo-elements, etc.). The scanner already filters by visibility + isInteractive; if a class of false positives slips through, surface them in LEARNINGS so /evolve can add an ignore.
overflow: hidden on a parent as a fix for clipped text. Ithides the bug, doesn't fix it.
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.