Skip to content
Agentmart
Submit a tool

native-feel

Opinionated rules for making an Expo + TypeScript app feel genuinely native on both platforms — a strict spacing/design-token system…

Skill by hzblj

IndexedMIT★ 1
Write a review

Install

Works with Claude Code, Claude apps, Codex, Cursor, Gemini CLI, GitHub Copilot, VS Code, OpenCode, Goose, Windsurf. Read the repository before you give any tool access to your data.

npx agentmarket install skill/hzblj-native-feel

Asks which of your tools to add it to (Claude Code, Codex, Cursor, VS Code, Windsurf, Gemini CLI, Claude Desktop) and shows the change first. See https://agentmart.dev/cli.

Get the weekly diff

The weekly diff: one email a week with new agent tools, notable updates and grade changes, plus security alerts for tools you follow. Double opt-in, one-click unsubscribe.

We use your email only for this newsletter. Privacy

About

Opinionated rules for making an Expo + TypeScript app feel genuinely native on both platforms — a strict spacing/design-token system, Platform.OS/Platform.select branching, expo-haptics (impact/selection/notification) used with restraint, per-platform Pressable feedback, safe areas and edge-to-edge, keyboard handling, scroll feel, Dynamic Type and accessibility, dark mode, and reduced motion. Triggers on: Platform.select, expo-haptics, impactAsync, selectionAsync, notificationAsync, android_ripple, safe area insets, edge-to-edge, KeyboardAvoidingView, useColorScheme, dark mode, hitSlop, touch target, VoiceOver, TalkBack, reduced motion, spacing tokens.

Use cases: design

No reviews yet. Installed it? Reviews come from verified installers only. How reviews work

Permissions

What this skill can do and what it asks for, from its SKILL.md. Checked Oct 25, 2026.

Tools
No tools found
Secrets
None asked for
Install scripts
None found

Network hosts it talks to 1

  • codeload.github.com · found in install command

Files it touches

  • ~/.claude/skills/native-feel

What's inside

Skill name
native-feel
Description
Opinionated rules for making an Expo + TypeScript app feel genuinely native on both platforms — a strict spacing/design-token system, Platform.OS/Platform.select branching, expo-haptics (impact/selection/notification) used with restraint, per-platform Pressable feedback, safe areas and edge-to-edge, keyboard handling, scroll feel, Dynamic Type and accessibility, dark mode, and reduced motion. Triggers on: Platform.select, expo-haptics, impactAsync, selectionAsync, notificationAsync, android_ripple, safe area insets, edge-to-edge, KeyboardAvoidingView, useColorScheme, dark mode, hitSlop, touch target, VoiceOver, TalkBack, reduced motion, spacing tokens.
License
MIT
Instructions
372 lines in SKILL.md · view on GitHub

Files (2)

  • SKILL.md
  • openai.yaml · agents/

SKILL.md (first lines)

# UX & Pixel Precision

A native-feeling app is a compound of small correct decisions: consistent spacing, platform-appropriate interactions, and respect for the system settings the user has already chosen (text size, contrast, motion, theme). This skill is the opinionated house style for that work in an Expo + TypeScript app.

The single most important rule: **respect the platform and the user's system settings.** Do not force one platform's design language onto the other, and never override a choice the user made in iOS/Android settings (text size, reduced motion, dark mode).

## Spacing & Design Tokens

- Follow the spacing system strictly.
- No arbitrary spacing values.
- No random padding or margins.
- Align with design tokens.
- Visual alignment must be intentional.
- Interaction feedback must feel deliberate.
- Motion must support UX, not distract from it.

Use a fixed spacing scale (an 8pt grid with a 4pt half-step is the reliable default) and reference it by name — never type raw numbers into styles.

```tsx
// Good — tokens from a single source of truth
export const spacing = {
  xs: 4,
  sm: 8,
  md: 12,
  lg: 16,
  xl: 24,
  xxl: 32,
} as const;

<View style={{ paddingHorizontal: spacing.lg, gap: spacing.sm }} />
```

```tsx
// Bad — arbitrary magic numbers scattered across components
<View style={{ paddingHorizontal: 17, marginTop: 13, gap: 7 }} />
```

## Platform-Native Feel

Respect iOS and Android platform conventions. The app must feel native on both. Use platform-specific patterns where they differ, and never assume behavior is identical — test on both.

| Concern | iOS | Android |
| --- | --- | --- |
| **Navigation** | Swipe-back gesture, bottom tabs | Material top tabs, hardware/predictive back |
| **Typography** | SF Pro (system) | Roboto (system) — use system fonts by default |
| **Haptics** | Rich Taptic Engine feedback | Subtler vibration motor |
| **Alerts & Sheets** | Action sheets and alerts | Material dialogs and bottom sheets |
| **Icons** | SF Symbols style (outline) | Material Icons style (filled) |
| **Scrolling** | Rubber-band bounce | Edge glow / overscroll |
| **Pressable** | Scale-down on press (~0.96) | Ripple effect (`android_ripple`) |

- Use `Platform.OS` or `Platform.select()` for platform-specific logic.
- Do not force one platform's design language onto the other.
- Test on both platforms — never assume behavior is identical.

## Platform branching in code

Branch with `Platform.OS` for control flow and `Platform.select()` for value maps. `Platform.select` accepts a `default`/`native` key and returns `undefined` for unmatched platforms, so it slots cleanly into styles.

```tsx
import { Platform } from 'react-native';

// Good — value map with a default
const shadow = Platform.select({
  ios: { shadowColor: '#000', shadowOpacity: 0.1, shadowRadius: 8, shadowOffset: { width: 0, height: 2 } },
  android: { elevation: 3 },
  default: {},
});

// Good — control flow for behavior that has no value form
if (Platform.OS === 'android') {
  // request a runtime permission, use hardware back, etc.
}

// Platform.Version is a number on Android (API level) and a string on iOS
const isAndroid13Plus = Platform.OS === 'android' && Number(Platform.Version) >= 33;
```

```tsx
// Bad — assuming iOS everywhere, shipping iOS shadows that do nothing on Android
<View style={{ shadowColor: '#000', shadowOpacity: 0.1, shadowRadius: 8 }} />
```

Prefer `.ios.tsx` / `.android.tsx` file extensions when an entire component diverges, rather than a component riddled with `Platform.OS` branches.

## Haptics (`expo-haptics`)

Haptics are punctuation, not narration. A well-placed tap confirms an action; constant buzzing is noise that users disable. Fire haptics on **meaningful, discrete events** and stay silent during scrolling, typing, and passive UI updates.

```tsx
import * as Haptics from 'expo-haptics';
```

### The three families

| API | Values | Fire it when |
| --- | --- | --- |
| `Haptics.impactAsync(style)` | `ImpactFeedbackStyle.Light` / `.Medium` / `.Heavy` (also `.Soft` / `.Rigid`) | A discrete physical action lands — button confirm, drag snap, drawer open, item drop |
| `Haptics.selectionAsync()` | (none) | A value changes in a picker, segmented control, toggle, slider tick, or stepper |
| `Haptics.notificationAsync(type)` | `NotificationFeedbackType.Success` / `.Warning` / `.Error` | An async outcome resolves — save succeeded, validation failed, payment declined |

```tsx
// Good — light impact on a primary action, matched to weight
const onConfirm = async () => {
  await Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
  submit();
}

// Good — selection feedback as a segmented control changes value
const onSegmentChange = (next: string) => {
  Haptics.selectionAsync();
  setValue(next);
}

// Good — notification feedback tied to an async result
try {
  await save();
  await Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success);
} catch {

From hzblj/skills, MIT.

More from hzblj/skills

Signals

Stars
1
Forks
0
Open issues
0
Last commit (this folder)
Jul 15, 2026 (3 months ago)
Last release
Aug 19, 2026 (2 months ago)

From GitHub (fetched Oct 24, 2026).

Details

License
MIT
First seen
Oct 24, 2026
Last checked
Oct 24, 2026

More from hzblj

Cite this page

native-feel skill. Agent Mart. https://agentmart.dev/skills/hzblj-native-feel (last updated 2026-10-24)