agentleFS
jameskrupnikjameskrupnik/indie-app-skills/skills/polish/SKILL.md

polish

Skill0 starsChanged 46 days agoMITView on GitHub

Find and fix the last 10% of a Flutter, React Native or Expo app or game — the faults that make it feel unfinished while analyze or tsc is clean and every test passes. Use when asked to polish an app, do final touches, make something feel complete or "next level", or before a first store submission; when something "feels off" or "looks unfinished" but nothing is broken; when adding tablet or iPad support or checking a layout on a larger screen; for an accessibility pass (Reduce Motion, Dynamic Type, large text, font scaling, VoiceOver, TalkBack, screen-reader labels, tap targets, safe areas); or when a layout is stretched, cramped, clipped or overflowing on some devices but not others.

70 other projects use a version of this.

Adopted by

Most recently taken up by

Used with

Projects with it also use layout, shape and 10 more.

Other files people use alongside this one.

Files found in the same public projects as a version of this one, more often than they turn up anywhere else. Each project counts once, large collections count for less, and this file's own repository is left out.
  • owshq-mecSkill

    layout

    Improve layout, spacing, and visual rhythm. Fixes monotonous grids, inconsistent spacing, and weak visual hierarchy. Use when the user mentions layout feeling off, spacing issues, visual hierarchy, crowded UI, alignment problems, or wanting better composition.

    Also in 8 projects that use this

  • HAR5HA-7663Skill

    shape

    Plan the UX and UI for a feature before writing code. Runs a structured discovery interview, then produces a design brief that guides implementation. Use during the planning phase to establish design direction, constraints, and strategy before any code is written.

    Also in 13 projects that use this

  • PeterCangSkill

    tokens

    Count all historical Claude Code token usage across all local projects, broken down by model, project, date, and week. Includes cost estimation based on official Anthropic pricing.

    Also in 3 projects that use this

  • arjunkshahSkill

    redesign

    Redesign existing interfaces to premium quality while preserving product goals and functional behavior.

    Also in 3 projects that use this

  • leonaaardobSkill

    motion

    Complete Motion.dev documentation - modern animation library for React, JavaScript, and Vue (formerly Framer Motion)

    Also in 3 projects that use this

  • MicrosoftSkill

    review-loop

    Iterative review loop using any lens agent against any target (plan, code, design). Use when you want to run a focused review cycle: spawn a fresh reviewer subagent with a specific lens, present findings and remediation to the user, apply fixes with user guidance, and repeat until clean.

    Also in 3 projects that use this

The file

We split this skill into the rules it gives an agent

5 rules. 0 are common, 5 are distinctive, and 0 are very specific to this project.

  • 0

    Common

    Most of the 62 edited versions we read keep these.

  • 5

    Distinctive

    This author's own choices. Worth a look.

  • 0

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    1. Capture it on real devices: Not the emulator you develop on: the biggest and smallest you ship to.

    line 55
  2. Distinctive:

    3. Measure instead of tabulating: When a layout number depends on width *and* text scale *and* wording, a table of constants is right for …

    line 66
  3. Distinctive:

    Whether feedback is proportionate: Every meaningful action gets an acknowledgement.

    line 89
  4. Distinctive:

    Run the script; fix what is mechanical and true.

    line 100
  5. Distinctive:

    Add a test at the extremes: one tablet width, one text scale of at least 1.35x.

    line 103

“Common” is measured: kept by at least 6 in 10 of the 62 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Polish

The last 10% is invisible from inside the code. None of these faults is a crash, a type error or a failed assertion. They share one shape: a number chosen against one device, one text size or one orientation, applied unchanged to another, producing a layout that works and is wrong.

That is what "feels unfinished" means: a stretched card, text crammed into the top of a tile, a starfield that will not stop moving, a setting that silences nothing. Users cannot name any of it and all of them feel it.

Quick start

python3 ${CLAUDE_SKILL_DIR}/check_polish.py --app .

--json for a hook, --strict exits 1 on any finding, --quiet hides NOTEs, --help lists the checks. Everything is advisory: it flags shapes, and a shape can be deliberate.

The stack is detected (Dart under lib/, or a package.json depending on react-native or expo); --stack flutter|rn forces one. On React Native or Expo, the catalogue below still holds; the React Native and Expo section maps it to RN's APIs and lists the RN checks.

Then do what the script cannot: look at it on the largest and smallest device you support, at large text. Most of what matters is only visible there.

The catalogue

FaultHow it hides
A content cap wider than the devicemaxWidth: 1100 looks like tablet support. No iPad is wider than 1024pt in portrait, and the smallest is 744, so it never engages and every list runs edge to edge while the code reads as handled
A fixed childAspectRatioTies cell height to width. Text wrapping does the opposite: wider means fewer lines and less height. Cavernous tiles on a tablet, clipped text on a phone, and neither end throws
A fixed height around textFine at 1.0x. iOS Dynamic Type reaches about 3.1x, and layouts break at 1.35x, one notch up the slider and not an accessibility setting at all
Ambient animationA looping controller runs until the user leaves the screen. Reduce Motion exists for this, and honouring it in one place out of three is worse than none, because it looks done
A dead touch bandWhere a game letterboxes, the margin is on screen and usually not touchable. It is where a thumb rests when someone holds the device by its edge
A setting that silences nothingThe switch, the stored value and the effect are joined by convention, not by a type. Test at the bottom (the platform call, the audio player), not at the flag
Icon-only controlsNo tooltip means no accessible name and no long-press hint. Cheap, mechanical
The one widget with no wordsProgress dots, pips, bars. Everything around them reads itself out; they announce nothing

Haptics are a special case: Flutter's HapticFeedback already obeys the system setting on both platforms, so an in-app switch is a courtesy. A vibration plugin bypasses it and needs one.

Three moves that find more than reading the code

1. Capture it on real devices. Not the emulator you develop on: the biggest and smallest you ship to. One pass turned up a stretched title screen, a badge grid two-thirds empty, a preview squashed to a 22:1 sliver, and score popups rendering upside down. The popups had been "fixed" with the sign backwards. A half-turn error stays horizontal and the right size, so it reads as fine in motion; only a still frame gives it away.

2. Render at the settings nobody uses. Largest text, smallest screen, widest screen, Reduce Motion, both orientations. A suite that only renders at 1.0x on one phone shape is silent about all of them.

3. Measure instead of tabulating. When a layout number depends on width and text scale and wording, a table of constants is right for one setting of the others. Ask the question the layout is about to ask (a TextPainter, an unbounded layout pass) and the answer is right everywhere. A band table of badge heights was right at 1.0x and broke at 1.35x; measuring fixed it and deleted the table.

Do not sniff for overflows

The obvious test is a fixed-height box and a check that nothing overflowed. It under-reports: Flutter suppresses repeat layout errors within one test, so a loop over ten widgets finds the first and goes quiet. It said 122pt was enough at a width where 220 was needed, and sent two rounds of tuning the wrong way.

Lay the widget out unbounded, read its real height, and compare it to what the container gives. Nothing to suppress, and it fails on a reworded string rather than when someone happens to look at a small phone.

What a script cannot check

  • Whether empty, loading and error states were designed, or left as a spinner and a grey sentence. Most apps have art for one of the three.
  • Whether feedback is proportionate. Every meaningful action gets an acknowledgement; nothing that happens several times a second does. A device that buzzes on every tap just hums.
  • Whether the first run makes sense with no data, no history, nothing unlocked. It is what every reviewer sees and what developers never look at.
  • Whether copy is consistent. "Play Together" here and "2 Player" there is only found by reading the whole app aloud.
  • Whether it is smooth. Jank and per-frame waste are the perf skill.

Order of work

  1. Run the script; fix what is mechanical and true.
  2. Capture the biggest and smallest devices you ship to. Fix what the pictures show, not what you expect them to show.
  3. Add a test at the extremes: one tablet width, one text scale of at least 1.35x. Those two regress silently.
  4. Walk the system settings: Reduce Motion, large text, a screen reader on the menus.
  5. Re-capture. The store screenshots are also the proof.

React Native and Expo

The same faults, in React Native's vocabulary. The script skips node_modules, build, .expo, Pods and the native folders.

CheckWhat it flagsWhy
icon-labelA Pressable/Touchable* with no accessibilityLabel and no Text insideTouchables are accessible by default and build their label from Text children; an icon alone gives nothing to read
a11y-roleNo accessibilityRole/roleThe screen reader does not say "button"
tap-targetA pressable whose style is under 44pt on a side, with no hitSlopApple asks for 44pt, Material for 48dp; hitSlop grows the touch area without growing the box
font-scalingallowFontScaling={false}, or maxFontSizeMultiplier between 1 and 1.5Text then ignores the system size. 0 means no cap, a value >= 1 caps it
fixed-height-textA literal height on a View/Text/pressable that holds TextThe fixed-height-around-text fault. minHeight or padding
reduce-motionAnimated.loop, ReduceMotion.Never, or a useFrameCallback/useClock (NOTE) in a file that never asks about Reduce MotionReanimated animations default to ReduceMotion.System and honour the setting; core Animated and frame-driven motion do not. Use useReducedMotion() or AccessibilityInfo.isReduceMotionEnabled()
haptics-switchVibration.vibrate or expo-haptics impactAsync/notificationAsync with no in-app switchiOS skips expo-haptics when system haptics are off; on Android those two are simulated with the Vibrator API
safe-areaNo safe-area handling anywhere, or SafeAreaView imported from react-nativeRN's own SafeAreaView is deprecated and iOS-only; use react-native-safe-area-context
dimensions-at-loadDimensions.get at module scopeRead once at load, stale after rotation, split view or a fold. useWindowDimensions()
image-altAn Image with no alt/accessibilityLabel (NOTE)Images are not accessible by default; fine if decorative
content-capA maxWidth no iPad reaches in portraitAs above
tablet-layoutExpo ios.supportsTablet: true and nothing reads the window size at render timeEvery iPad gets the stretched phone layout

To render at the settings nobody uses: raise the system text size (iOS Larger Text, Android Font size) and turn on Reduce Motion on the simulator or emulator, then look. There is no untested-scale check on React Native; Jest rarely renders at a font scale, so the screenshot is the test.

Across several apps

These faults travel with the template an app was cut from: three sibling apps had the same childAspectRatio: 1.15 badge grid, and two ignored Reduce Motion. Fix the template too, or it comes back with the next app.

A finishing fix must not land on a sibling's chrome. The quick cure for a screen that looks unfinished is often the template's own shape: a stock title bar, a column of rounded rows, a grid of number tiles. On an account with sibling apps every one of them already has it, and guideline 4.3 reads shared screen shapes as one app in two colours. Make the fix in the app's own visual language, then run app-similarity's screens sheet and read it.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.