bama
BAMA: 모든 Mac 앱을 만들 수 있습니다
iOS·macOS 앱, 웹 제품 및 오픈소스
iOS & macOS apps, web products and open source: Slasher, Oncallz, Glowth, Tıbbi Bitkiler Atlası, Hane Bütçe, Worthia, Çengel Bulmaca, Slashbar, Argus Mesh, DayConsole, AgentFlow, DevOps TR.
스킬JavaScript · PythonGitHub @harun-yardimci온라인운영자 미확인
써 보셨어요? 한 번만 눌러 주세요. 로그인은 필요 없어요.
Agent skill for Claude Code & Codex: review and improve interfaces across task flow, microcopy, states, accessibility and visual craft.
저장소 README에서 가져옴
README 전체를 불러오는 중입니다. 아래는 저장된 발췌입니다.
Interface Craft is an agent skill that turns your coding agent into a careful product-design reviewer. When you ask it to "review this screen", "make this form better" or "fix the empty states", it doesn't just add shadows. It checks whether the user can finish the task, understand the copy, recover from errors, and use the interface with a keyboard or screen reader. Then it polishes the details, using your existing design system.
It combines five bodies of practice in one skill, with rules for which one wins when they conflict:README 전문 보기 ↗Interface Craft is an agent skill that turns your coding agent into a careful product-design reviewer. It doesn't stop at one look at a screenshot: it renders the page, measures it at three widths in both themes, tests it with real-length data, and reads every region at zoom. When you ask it to "review this screen", "make this form better" or "fix the empty states", it doesn't just add shadows. It checks whether the user can finish the task, understand the copy, recover from errors, and use the interface with a keyboard or screen reader. Then it polishes the details, using your existing design system.
It combines five bodies of practice in one skill, with rules for which one wins when they conflict:
The full list of principles and sources is in Foundations: what we follow.
These mockups show the kind of changes the skill proposes. Their HTML sources are in examples/.
Why: a placeholder disappears as soon as the user types. “Invalid input” gives the user nothing to act on. A button that stays tappable while a request is pending invites repeat submits. The skill fixes each of these and states the limits of its fix: disabling the button reduces duplicate taps, but only the backend can guarantee idempotency.
Why: routine “Are you sure?” dialogs teach people to click through. The skill asks what exactly will happen, names the button after the action, and uses Undo instead of a confirmation when the action can be reversed.
Why: a network error that looks like an empty account makes users think their data is gone. The skill separates initial, no results, failed, loading, partial and success states, and gives each one its own next step.
Why: dark patterns may convert once and cost trust afterwards. The skill never adds false urgency, hidden costs or guilt-based opt-outs, and it flags them when it finds them.
Why: these details only matter once the basics above work, which is why polish comes last in the skill's priority order. Surfaces follow one rule: tone separates regions, a line marks a boundary people must find (form fields need 3:1), and a shadow means "this floats". Motion never uses
ease-in, because its slow start makes the response to a click look late, and that includes exits. The values (radius math, easing tokens,tabular-nums,scale(0.97)on press, the 2× grouping gap, and so on) are starting defaults. Your design tokens and the rendered result take precedence.
Why: the most common theme bug is a fixed background (
Color.white,#F8FAFC,bg-white) paired with an adaptive text color that turns light in dark mode, leaving white text on a white surface. The skill checks that background and foreground come from the same theme source, and treats any fixed/adaptive mix as a bug unless its contrast is verified in both themes. It renders the screen and its overlays in both themes, and ships a static scanner,theme_audit.py, that flags these mixes in SwiftUI, Compose and Tailwind before users see them. See Theme audit for usage and example output.
Why: sample data with similar-length titles hides this bug until real content arrives. For carousels and grid rows the skill renders the shortest and longest real items side by side, then applies reserved text lines (
lineLimit(2, reservesSpace: true),line-clamp+min-height,minLines), stretch-to-tallest rows, fixed media ratios and a bottom-pinned meta row. Truncation must not hide information: the full title stays in the accessibility label. At large text sizes, readability wins over equal heights. Vertical lists with variable content keep their natural row heights; only media size and alignment stay consistent.
Why: these bugs rarely show up on the one simulator and sample data used during development. The skill checks the conditions real users hit: the smallest and largest widths, landscape and iPad, the keyboard open on every form, the largest text size, a slow network and a failed image. It names the condition behind each finding, because none of these settings show in a screenshot.
Why: a button that "does nothing", or a tap that opens the neighbouring card, usually means the hit area doesn't match what's drawn. In SwiftUI,
.clipped()cuts the drawing but not the hit area, so a fill-scaled image keeps catching taps with its invisible overflow. This is worst on iPad, where the frame is widest. The skill tap-tests every control next to media on the widest layout, andhit_area_audit.pyflags the pattern in code.
Why: a single look at a screenshot catches the loud problems and misses this kind. The right side is real output of
render_audit.mjsonexamples/audit-demo.html. Each numbered box is a measurement (pixels, contrast ratios, what sits under a click). The agent confirms every box on the screenshot, then reads every tile at zoom for what measurement can't see.
Why: layouts are built with "Ayşe Demir" and "₺240,00", then meet "Gülşah Çağlayan-Öztürkoğlu" and "₺1.234.567,89" in production. With
--stress, the audit makes every string about 40% longer with Turkish characters, adds long unbreakable words and big amounts, and doubles the text size. The report lists what appears only under stress.--states empty,loading,errordoes the same for every state you can reach by URL.
Why: users now read the template look as "nobody designed this". For new screens and redesigns, the skill first names the surface type (app, marketing or docs) and writes a short direction: who it is for, the one thing that matters, and what makes this product unlike its competitors. Then it checks the result for generic-look tells (the audit flags eyebrow labels, gradient text, nested cards, side stripes, border plus wide shadow and emoji icons), copy clichés in English and Turkish, and the logo test: could a competitor ship this page unchanged? An existing brand system always wins over these heuristics.
For a broad review, the agent gives a short summary first and then one table sorted by priority. Here is a trimmed example of a sign-up form review:
| # | Priority | Location | Before | After | User impact |
|---|---|---|---|---|---|
| 1 | Blocker | SignUp.tsx:42 | Submit stays enabled while pending; repeated taps send repeated requests | Disable during request, spinner + “Creating account…” | Fewer repeat submissions, visible status. Not verified: backend idempotency, flagged for follow-up |
| 2 | A11y | SignUp.tsx:18 | Email field has a placeholder only | Persistent <label> “Email”; placeholder is an example | Label stays visible while typing; screen readers announce it |
| 3 | Error | validation.ts:9 | “Invalid input” | “Use at least 8 characters. You have 6.” next to the field | The user knows exactly what to fix |
| 4 | Polish | Card.tsx:7 | rounded-xl on the card and on the inner button with p-2 | Card rounded-2xl (8 + 8), button rounded-lg | Nested corners look concentric |
Each visual finding names the run it came from (for example 375px, dark, empty state or only under --stress), so it can be found again.
After the table come three short parts:
Dimensions with nothing to report are left out; the table is never padded.
user intent > accessibility > platform conventions > existing design system > aesthetics.The visual reference includes native equivalents, so the skill is useful beyond CSS:
| Technique | Web | SwiftUI | Compose |
|---|---|---|---|
| Tabular numbers | font-variant-numeric: tabular-nums | .monospacedDigit() | fontFeatureSettings = "tnum" |
| Press feedback | active:scale-[0.96] | ButtonStyle + .scaleEffect | animateFloatAsState |
| Hit area | pseudo-element to 40–44px | .frame(minWidth: 44, minHeight: 44) | minimumInteractiveComponentSize() |
| Reduced motion | prefers-reduced-motion | accessibilityReduceMotion | animator duration scale |
Before using newer platform APIs, the skill checks your deployment target and dependency versions, and falls back to compatible alternatives when needed.
The microcopy reference covers locale formatting (₺1.234,50), plural rules and text expansion, plus Turkish-specific pitfalls: i/İ and ı/I casing, vowel-harmony suffixes on dynamic values, and consistent sen/siz address. The agent keeps the interface's existing locale and replies in your language.
The skill doesn't invent its own design theory. It builds on established, publicly documented guidance, listed below with sources. Each rule is a decision aid: the agent applies it only when it can point to an observed problem, and never as a checklist to fill.
Source: Jakob Nielsen, 10 Usability Heuristics for User Interface Design (Nielsen Norman Group). All ten are walked on every broad review.
| # | Heuristic | What the agent checks |
|---|---|---|
| 1 | Visibility of system status | Can the user tell pending, done and failed apart? Is there feedback right after an action? |
| 2 | Match between system and the real world | Do the terms and icons use the audience's words, not internal jargon? |
| 3 | User control and freedom | Can an accidental action be cancelled, undone or exited? |
| 4 | Consistency and standards | Do equivalent controls look and behave the same, and follow platform conventions? |
| 5 | Error prevention | Which costly mistake could a safe default, constraint or inline hint prevent? |
| 6 | Recognition rather than recall | Is the information needed for a decision visible at that moment? |
| 7 | Flexibility and efficiency of use | Do frequent tasks have shortcuts without hiding the standard path? |
| 8 | Aesthetic and minimalist design | Which elements compete with the main task without helping it? |
| 9 | Help users recognize, diagnose, and recover from errors | Does every error say what went wrong and what to do next, and keep the user's input? |
| 10 | Help and documentation | Is help available right where the confusion happens? |
Source: Jon Yablonski, Laws of UX. The skill covers the whole catalog. Each law is written as symptom → fix → what not to do, because these are hypotheses about behavior, not guarantees.
Core principles (used whenever they fit)
| Law | How it is applied | Guardrail |
|---|---|---|
| Fitts's law | Bigger targets, more spacing, primary action close to the task | Enlarged targets must not overlap |
| Hick's law | Clearer categories, ranked options, a sensible default | Don't delete useful options just to lower the count |
| Jakob's law | Follow platform and product conventions | Don't copy another product wholesale |
| Tesler's law | Move derivable effort into defaults and computed values | Inferred values stay visible and editable |
| Doherty threshold | Instant pressed state; honest pending and skeleton states | Never add artificial delay |
| Goal-gradient effect | A truthful step indicator in long flows | Never fake progress |
| Zeigarnik effect | Autosave drafts; a visible way to resume | No nagging or manufactured unfinished tasks |
| Von Restorff effect | One clearly primary action; the rest secondary | Don't highlight several things at once |
| Peak-end rule | Clear, useful completion moments (receipt, next step) | Celebration doesn't fix a broken flow |
| Aesthetic-usability effect |
Memory and cognitive load
| Law | How it is applied |
|---|---|
| Miller's law | Group information and keep context visible. The skill deliberately does not cap menus at “7±2”. |
| Chunking | Break long content and inputs (phone numbers, IBANs, steps) into meaningful groups |
| Working memory | Don't make users carry values from one screen to the next |
| Cognitive load | Remove redundant interpretation; organize around the task |
Grouping (Gestalt)
| Law | How it is applied |
|---|---|
| Law of Proximity | Spacing is the first tool for showing what belongs together |
| Law of Common Region | Containers only when spacing isn't enough; too many add clutter |
| Law of Similarity | Same function, same style; different function, visibly different |
| Law of Uniform Connectedness | Connect related items visually; make sure “Delete” doesn't look grouped with “Save” |
| Law of Prägnanz | Prefer simple, recognizable shapes and readable visual paths |
Applied only with a concrete fit
| Law | When it is used / limit |
|---|---|
| Choice overload | Hard comparisons → comparison tables and filters. A large set isn't automatically overwhelming. |
| Mental model | Surprising behavior → explain it in the user's terms and validate the assumption |
| Selective attention | Put task-relevant signals near the work; don't demand attention for secondary content |
| Serial position effect | Put key items first or last in long lists; never bury critical content |
| Flow | Fewer interruptions in sustained work, while keeping exits and important warnings |
| Paradox of the active user | Contextual, learn-by-doing help instead of up-front tutorials |
| Postel's law | Accept harmless format variations; never silently reinterpret ambiguous dates, amounts or identities |
| Occam's razor | Prefer the simpler adequate interaction without removing needed capability |
| Pareto principle | Prioritize tasks shown to be frequent or high-impact; never invent an 80/20 split |
| Parkinson's law | Remove unnecessary effort; never impose artificial time pressure |
| Cognitive bias |
The visual-craft layer is inspired by and adapted from make-interfaces-feel-better by Jakub Krehel (MIT). These are the techniques taken from it:
| Area | Techniques |
|---|---|
| Surfaces | Concentric border radius · optical alignment of icons · layered shadow rings (since 1.5: shadows mark elevation; tone and lines separate resting surfaces) · subtle image outlines · minimum hit area |
| Typography | text-wrap: balance / pretty · font smoothing · tabular numbers |
| Motion | Interruptible transitions · split and staggered enter animations · subtle exits · contextual icon animations · scale(0.96) on press · no animation on first render |
| Performance | No transition: all · will-change only when needed |
Since 1.5 the surface and motion rules are reconciled with later sources (see section 7): shadows now mean elevation rather than replacing borders, and exits use a strong ease-out instead of ease-in.
What Interface Craft changes: the values are starting defaults rather than rules, so design tokens and the rendered result win. It adds SwiftUI and Compose equivalents with OS-version checks, reduced-motion handling, a forced-colors caveat for shadow-only boundaries, and platform-specific target sizes (iOS 44pt, Android 48dp). Visual polish sits last in the priority order, after task, copy, states and accessibility.
Source: W3C, Web Content Accessibility Guidelines 2.2. Level AA is the baseline, plus the platform's own guidance (Apple HIG, Material 3).
| Check | Threshold |
|---|---|
| Text contrast | 4.5:1 (large text 3:1) |
| Non-text contrast (borders, icons, focus rings) | 3:1 |
| Target size | 24×24 CSS px minimum; 44×44 enhanced; iOS 44pt, Android 48dp |
| Focus | Always visible; not hidden by sticky UI; modals trap and return focus |
| Reflow / zoom | Works at 320px width and 200% text zoom |
| Motion | Honors reduced-motion preferences; nothing flashes more than 3 times per second |
The skill reports which accessibility checks it actually ran, and says clearly that an automated check is not a conformance certificate.
Sources: Apple HIG: Dark Mode · Material 3: color roles · Android: dark theme · MDN color-scheme and prefers-color-scheme.
surface/onSurface, systemBackground/label, bg-background/text-foreground). Fixed colors are only for deliberately theme-independent surfaces, and then the foreground is fixed too. Any fixed/adaptive mix must have its contrast verified in both themes.values-night, .dark CSS variables.scripts/theme_audit.py finds leads, and rendering both themes confirms them. On shared simulators the previous appearance is recorded and restored.These rules come from UX writing and ethical-design practice and are applied throughout:
user intent > accessibility > platform conventions > existing design system > aesthetics.Version 1.5 adds ideas from four open-source agent skills, paraphrased and reconciled with the rules above (see NOTICE):
| Source | License | What we took |
|---|---|---|
| emilkowalski/skills (Emil Kowalski) | MIT | Motion: decide by frequency of use, never ease-in, easing tokens, durations per component, interruptibility, scale(0.95) not scale(0), gesture thresholds, animation performance |
| jakubkrehel/skills (Jakub Krehel) | MIT | Stress harness with worst-case content, state workbench, variants on one axis, typography roles and size floors, the 2× grouping rule, escalation triggers, the cheapest-fix order |
| Taste Skill (Leon Lin) | MIT | Generic-look tells, copy tells, logo and category tests, rendering at several widths before critique |
| Impeccable (Paul Bakaus) | Apache-2.0 | Surface modes (operate / persuade / read), direction before code, the anti-pattern catalogue, OKLCH palette guidance, persona pass, the 8 interactive states |
Where they disagree, the skill picks one rule and says why. For example, Taste Skill bans resting shadows and hairlines, which dense product UIs can't follow. The skill instead gives tone, line and shadow each their own job.
/plugin marketplace add harun-yardimci/interface-craft
/plugin install interface-craft@interface-craft
git clone https://github.com/harun-yardimci/interface-craft.git
cp -R interface-craft/skills/interface-craft ~/.claude/skills/
To install it for a single project, copy it to .claude/skills/ in that repo instead.
git clone https://github.com/harun-yardimci/interface-craft.git
cp -R interface-craft/skills/interface-craft ~/.agents/skills/
For a single project, use .agents/skills/. The optional agents/openai.yaml sets the display name and default prompt in the Codex UI.
Download interface-craft.zip from the latest release and upload it under Settings → Capabilities → Skills.
The skill triggers automatically on broad interface requests. You can also call it directly:
/interface-craft review the checkout flow in app/checkout, review only
/interface-craft improve the onboarding screens, keep the layout
/interface-craft only fix the error and empty states on the invoices page
/interface-craft audit dark mode / light mode consistency in apps/ios
/interface-craft check the onboarding on iPhone SE with the keyboard open and at the largest text size
/interface-craft why do our screens feel inconsistent? run the token audit on src/
/interface-craft the favorite button on the Discover cards does nothing on iPad
/interface-craft review http://localhost:3000/checkout, things keep slipping past us
/interface-craft this landing page looks generic, make it ours
Bu ekranı incele, önce/sonra tablo ile raporla
For narrow tasks it steps aside when a more specific skill is installed. For example, it defers to a dedicated UX-writing skill for copy-only work or an accessibility-audit skill for a WCAG audit. When no such skill is installed, it handles the task with only the relevant reference.
Looking at a screenshot once finds the loud problems and misses a 3px misalignment, one card with different padding, or a border that disappears in dark mode. render_audit.mjs opens the page in your installed Chrome or Edge and measures it at 375, 768 and 1440px, in light and dark:
cd skills/interface-craft/scripts && npm install # once; no browser download
node render_audit.mjs http://localhost:3000/checkout --out ui-audit
node render_audit.mjs http://localhost:3000/invoices --stress --states empty,loading,error
node render_audit.mjs --tile-image screenshot.png # cut a screenshot into zoomed tiles
| Area | What it measures |
|---|---|
| Layout | Horizontal scroll, content cut by the viewport, missing mobile viewport, clipped or truncated text, overlapping text, content stuck under fixed bars, layout shift |
| Alignment and rhythm | Left edges 2–4px off the common edge, rows almost aligned, uneven gaps, unequal cards in a row, repeated items with different padding or icon size, headings with more space below than above, weak grouping |
| Color | WCAG contrast on the real composited background (alpha, opacity and oklch handled), placeholder contrast, form fields whose edges are too faint, light-gray body text |
| Targets and focus | Controls covered by an invisible layer, targets under 24px (with the spacing exception), missing accessible names, Tab traversal without a visible focus style or landing on hidden elements |
| Emphasis and tells | Competing filled primary buttons, accent hue sprawl, generic-look patterns, transition: all, ease-in |
| Media and type | Broken, stretched or blurry images, tiny text, line height by role, iOS input zoom, type-scale sprawl, a token inventory (sizes, colors, radii, spacing, left edges) |
--stress adds runs where every string is ~40% longer with Turkish characters and long unbreakable words, and runs with doubled text size. The report then lists what appears only under stress: layouts that are waiting for real data. --states audits each state through ?__state=….
Each run writes annotated.png (numbered boxes that match report.md) and zoomed tiles. The agent opens every tile with a fixed set of lenses (visual scan), then does a fresh-eyes persona pass. Hits are leads: the agent confirms each one on the screenshot and lists the hits it rejected, with reasons.
The skill includes a dependency-free static scanner for the most common theme bug: a fixed background next to an adaptive foreground. The agent runs it automatically during reviews, and you can also run it yourself or in CI:
python3 skills/interface-craft/scripts/theme_audit.py path/to/your/app
The scanner flags code like this, which looks fine in light mode and turns into white-on-white text in dark mode:
ScrollView { /* chips */ }
.background(Color(hex: "F8FAFC")) // fixed: stays light in dark mode
Text(title)
.foregroundStyle(isSelected ? .white : Theme.ink) // adaptive: turns light in dark mode
.background(
isSelected ? Theme.cobalt : Color.white, // fixed
in: RoundedRectangle(cornerRadius: 10)
)
<div class="bg-white text-foreground">…</div> <!-- fixed bg + theme text token -->
Example output:
[HIGH] asset-no-dark A.xcassets/Brand.colorset
Color set has no Dark appearance
[HIGH] mix FilterSheet.swift:4
Fixed background next to adaptive foreground (isSelected ? .white : Theme.ink): unless contrast is verified in both themes, text likely vanishes in one
> .background(Color(hex: "F8FAFC"))
[HIGH] mix FilterSheet.swift:9
Fixed background next to adaptive foreground (isSelected ? .white : Theme.ink): unless contrast is verified in both themes, text likely vanishes in one
> .background( isSelected ? Theme.cobalt : Color.white, in: RoundedRectangle(cornerRadius: 10) )
[HIGH] tw-mix web/Card.tsx:1
Fixed Tailwind background with a theme text token: unless contrast is verified in both themes, text likely vanishes in one
> <div class="bg-white text-foreground p-4">x</div>
[WARN] fixed-bg BrandButton.swift:3
Opaque fixed background; its foreground must be fixed too, or use an adaptive surface token
[WARN] web-no-color-scheme .
No `color-scheme` declaration found: native inputs and scrollbars may stay light in dark mode
The fix is to take the background and foreground from the same theme source:
.background(Theme.surface) // adaptive
.background(isSelected ? Theme.cobalt : Theme.chip, in: ...) // adaptive
| Rule | Severity | What it catches |
|---|---|---|
mix | high | SwiftUI: an opaque fixed background (Color.white, Color(hex:), .black) within a few lines of an adaptive foreground (.primary, .secondary, project tokens); multi-line modifiers included |
tw-mix | high | Tailwind: a fixed background (bg-white, bg-zinc-100…) with no dark:bg-*, on the same element as a theme text token (text-foreground, text-muted-foreground). Each class attribute is judged separately, so a nested element isn't hidden by its parent's dark: classes. |
asset-no-dark | high | Xcode color sets with no Dark appearance |
fixed-bg | warn | Opaque fixed backgrounds, which are fine for brand surfaces when the foreground is fixed too |
scheme-override | warn | .preferredColorScheme, .environment(\.colorScheme, .light), overrideUserInterfaceStyle, forced Compose themes |
tw-no-dark | warn | A fixed Tailwind color class without a dark: variant for the same utility on that element (e.g. border-gray-200 next to dark:bg-zinc-900 is still flagged) |
android-no-night · web-no-color-scheme | warn | Missing values-night/ resources; missing CSS color-scheme |
fixed-stroke · inline-color · fixed-color (CSS) | info | Literal border or inline colors that bypass theme tokens |
Scrims and shadows (Color.black.opacity(0.4), .shadow(color: .black…)) are intentionally ignored. Treat hits as leads, not verdicts: the agent confirms each one by rendering the screen, including its sheets, menus and every chip state, in both themes. Compose detection is file-level for now, so expect more false positives there than in SwiftUI or Tailwind.
A second scanner, token_audit.py, explains the vague feeling that screens "don't quite match". It collects every literal font size, spacing value and corner radius in SwiftUI, Compose, CSS and Tailwind, and reports how far the code has drifted from a real scale:
python3 skills/interface-craft/scripts/token_audit.py path/to/your/app # 4pt grid
python3 skills/interface-craft/scripts/token_audit.py path/to/your/app --grid 2 # half-step systems
Example output:
font 4 distinct literal values
11.5×1, 13×1, 15×2, 17×1
spacing 5 distinct literal values
6×1, 8×1, 13×2, 14×1, 16×2
radius 5 distinct literal values
7×1, 9×1, 10×1, 12×1, 14×1
[WARN] fixed-font 3 × `.system(size:)` does not scale with Dynamic Type. Use text styles (.body, .headline) or a scaled custom font.
Card.swift:4 > Text("Title").font(.system(size: 17, weight: .semibold))
[WARN] off-grid 3 spacing values not on the 4pt grid
Card.tsx:1 13 > <div class="p-[13px] text-[15px] rounded-[7px] gap-4">x</div>
Card.swift:9 14 > .padding(14)
[INFO] one-off values (used once; likely drift from the scale):
radius 7 Card.tsx:1
radius 9 styles.css:2
The agent maps each literal to the project's existing tokens and text styles rather than inventing new ones. A healthy scale is small, typically ~6–8 text styles, ~6–10 spacing steps and ~3–5 radii. CSS custom-property definitions such as --space-3: 12px are treated as tokens and skipped.
The third scanner, hit_area_audit.py, looks for places where the tappable area differs from what the user sees:
python3 skills/interface-craft/scripts/hit_area_audit.py path/to/your/app
[HIGH] clip-overflow Card.swift:8
Clipped fill/offset content without .contentShape: the overflow still takes taps and can cover nearby controls
> .clipped()
[WARN] small-target Card.swift:11
Tappable label is 24x24 pt: pad to 44x44 or add .contentShape on a larger frame
> .frame(width: 24, height: 24)
[WARN] small-target Card.swift:15
Tappable label is 120x28 pt: pad to 44x44 or add .contentShape on a larger frame
> Text("Short").frame(width: 120, height: 28)
[INFO] tap-catcher Card.swift:18
Near-invisible layer with a tap handler: make sure it is intentional and does not cover other controls
> Rectangle().fill(.white.opacity(0.001)).onTapGesture { }
| Rule | Severity | What it catches |
|---|---|---|
clip-overflow | high | SwiftUI: .scaledToFill() / contentMode: .fill / offset or scaled content, then .clipped() or .clipShape(), with no .contentShape(...). The invisible overflow still takes taps. |
small-target | warn | SwiftUI tappable labels under 44 pt in either dimension, unless a larger hit area is added afterwards (.padding(...) + .contentShape(...), or .frame(minWidth: 44, minHeight: 44)). .contentShape on the small frame alone doesn't count, because it doesn't enlarge the hit area. Compose clickables under 48 dp without minimumInteractiveComponentSize(). |
tap-catcher | info | Near-invisible layers with a tap handler (.opacity(0.001), Color.clear + .contentShape). SwiftUI doesn't hit-test views at exactly .opacity(0), so near-zero opacity is how a layer silently catches taps. |
overlay-hit | info | Web: position: absolute/fixed + inset: 0 (or Tailwind absolute inset-0) layers without pointer-events: none. |
Static scanning can't see runtime frames, so the agent confirms every hit by tapping each control next to media and just outside each card's edge, on the widest layout. For a debug view of the real frames, add .border(.red) in SwiftUI or use the DevTools element picker on the web.
skills/interface-craft/
├── SKILL.md # workflow, routing, hard rules, output format
├── NOTICE.md # credits for adapted open-source material
├── agents/openai.yaml # optional Codex UI metadata
└── references/ # loaded only when the task needs them
├── visual-scan.md # render audit usage + tile-by-tile scan + persona pass
├── stress-and-states.md # stress runs, break harness, state workbench, variants
├── generic-tells.md # surface types, direction before code, generic-look tells
├── interaction-and-feedback.md # Nielsen's 10 heuristics, flows, states, recovery
├── cognitive-principles.md # Laws of UX as decision aids
├── microcopy.md # patterns, before → after, localization
├── accessibility.md # WCAG 2.2 AA thresholds and checks
├── theming.md # light/dark consistency: pairing rules, patterns, audit
├── layout-resilience.md # safe areas, keyboard, sizes, large text, loading & media
└── visual-craft.md # surfaces, type, spacing, motion values + SwiftUI/Compose
skills/interface-craft/scripts/
├── render_audit.mjs # renders the page and measures it (Playwright + installed Chrome)
├── package.json # its only dependency: playwright-core
├── theme_audit.py # static scan for unverified fixed/adaptive color mixes
├── token_audit.py # static scan for font/spacing/radius drift
└── hit_area_audit.py # static scan for hit areas that differ from what's drawn
.claude-plugin/ # Claude Code plugin + marketplace manifests
examples/ # HTML sources of the README mockups
assets/ # rendered images
The skill uses progressive disclosure: SKILL.md stays small, and each reference file is loaded only when the task needs it.
Issues and PRs are welcome, especially:
Please keep additions evidence-based and concrete. A new rule should name the problem it solves and its limits.
Released under the MIT License © 2026 Harun Yardimci.
| Coherent spacing, type and color |
| Polish can hide problems, so function is tested separately |
| Name the specific bias and the observed issue; the category alone is not a design instruction |