agentsclimarketplace

Ui

Skill Shankulkarni/react-native-ai-plugin/plugins/rn-ai/skills/ui

Use when building UI components or screens -- NativeWind v4 className patterns, CVA variants, dark mode, cn(), safe area, keyboard avoiding, expo-image, Pressable, compound components.From its SKILL.md

Install
npx -y skills add Shankulkarni/react-native-ai-plugin --skill ui

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

SKILL.md

6.1 KB, ~1.6k tokens by cl100k_base, as published. Nobody here has run it

UI -- NativeWind v4 + Components

NativeWind v4 brings Tailwind to React Native via the className prop.

CRITICAL Rendering Rules

// ❌ Never && with falsy -- renders "0" or crashes
{count && <Badge />}
{items.length && <List />}

// ✅ Ternary with null
{count > 0 ? <Badge count={count} /> : null}
{Boolean(items.length) && <List />}

// ❌ Strings outside <Text> crash New Architecture
<View>Hello</View>

// ✅
<View><Text>Hello</Text></View>

NativeWind Basics

// className works just like Tailwind
<View className="flex-1 bg-white px-4 py-6">
  <Text className="text-xl font-bold text-gray-900">Hello</Text>
  <Text className="text-sm text-gray-500 mt-1">Subtitle</Text>
</View>

// Conditional classes with cn()
import { cn } from '@/libs/cn'
<View className={cn('p-4 rounded-xl', isActive && 'bg-primary', isDisabled && 'opacity-50')} />

// Dark mode
<View className="bg-white dark:bg-gray-900">
  <Text className="text-gray-900 dark:text-white">Auto dark mode</Text>
</View>

Component Template

import { Pressable, Text, View } from 'react-native'
import { cva, type VariantProps } from 'class-variance-authority'
import { cn } from '@/libs/cn'

const buttonVariants = cva(
  'flex-row items-center justify-center rounded-xl gap-2 active:opacity-80',
  {
    variants: {
      variant: {
        primary: 'bg-primary',
        secondary: 'bg-gray-100 dark:bg-gray-800',
        ghost: 'bg-transparent',
        destructive: 'bg-red-500',
      },
      size: {
        sm: 'px-3 py-2 min-h-[32px]',
        md: 'px-4 py-3 min-h-[44px]',
        lg: 'px-6 py-4 min-h-[52px]',
      },
    },
    defaultVariants: { variant: 'primary', size: 'md' },
  },
)

const buttonTextVariants = cva('font-semibold', {
  variants: {
    variant: {
      primary: 'text-white',
      secondary: 'text-gray-900 dark:text-white',
      ghost: 'text-primary',
      destructive: 'text-white',
    },
    size: { sm: 'text-sm', md: 'text-base', lg: 'text-lg' },
  },
  defaultVariants: { variant: 'primary', size: 'md' },
})

type ButtonProps = React.ComponentProps<typeof Pressable> &
  VariantProps<typeof buttonVariants> & {
    label: string
    isLoading?: boolean
  }

function Button({ label, variant, size, isLoading, className, disabled, ...props }: ButtonProps) {
  return (
    <Pressable
      className={cn(buttonVariants({ variant, size }), className)}
      disabled={disabled || isLoading}
      accessibilityRole="button"
      accessibilityState={{ disabled: disabled || isLoading }}
      {...props}
    >
      {isLoading ? (
        <ActivityIndicator color={variant === 'primary' ? 'white' : '#6366f1'} size="small" />
      ) : (
        <Text className={cn(buttonTextVariants({ variant, size }))}>{label}</Text>
      )}
    </Pressable>
  )
}

export { Button }
export type { ButtonProps }

Safe Area

import { SafeAreaView } from 'react-native-safe-area-context'

// Full screen with safe area
<SafeAreaView className="flex-1 bg-white dark:bg-gray-900">
  <ScrollView>...</ScrollView>
</SafeAreaView>

// Just bottom safe area (tabs)
<View className="flex-1">
  <ScrollView contentContainerClassName="pb-safe">...</ScrollView>
</View>

// iOS ScrollView -- automatic safe area inset
<ScrollView contentInsetAdjustmentBehavior="automatic">...</ScrollView>

Keyboard Avoiding

import { KeyboardAvoidingView, Platform, ScrollView } from 'react-native'

<KeyboardAvoidingView
  className="flex-1"
  behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
>
  <ScrollView
    className="flex-1"
    keyboardShouldPersistTaps="handled"
    contentContainerClassName="p-4 gap-4"
  >
    {/* form inputs */}
  </ScrollView>
</KeyboardAvoidingView>

expo-image

import { Image } from 'expo-image'

<Image
  source={{ uri: avatarUrl }}
  placeholder={{ blurhash: 'LGFFaXYk^6#M@-5c,1J5@[or[Q6.' }}
  contentFit="cover"
  priority="high"
  className="w-12 h-12 rounded-full"
/>

Pressable (not TouchableOpacity)

// ✅ Pressable with className
<Pressable
  className="rounded-xl bg-primary px-4 py-3 active:opacity-70"
  onPress={handlePress}
  accessibilityRole="button"
>
  <Text className="text-white font-semibold">Press me</Text>
</Pressable>

// ✅ Pressable with style function for fine-grained pressed state
<Pressable style={({ pressed }) => [{ opacity: pressed ? 0.7 : 1 }]}>
  {({ pressed }) => <Text>{pressed ? 'Pressed!' : 'Press me'}</Text>}
</Pressable>

Compound Components

// Card compound component
function Card({ className, children, ...props }: ViewProps & { className?: string }) {
  return (
    <View className={cn('bg-white dark:bg-gray-900 rounded-2xl p-4 shadow-sm', className)} {...props}>
      {children}
    </View>
  )
}

function CardHeader({ className, children, ...props }: ViewProps & { className?: string }) {
  return <View className={cn('mb-3', className)} {...props}>{children}</View>
}

function CardTitle({ className, children, ...props }: TextProps & { className?: string }) {
  return <Text className={cn('text-lg font-bold text-gray-900 dark:text-white', className)} {...props}>{children}</Text>
}

Card.Header = CardHeader
Card.Title = CardTitle

// Usage
<Card>
  <Card.Header>
    <Card.Title>Hello</Card.Title>
  </Card.Header>
</Card>

Typography Scale

// Use consistent text classes -- define in tailwind.config.js theme
<Text className="text-3xl font-bold">Heading 1</Text>
<Text className="text-2xl font-bold">Heading 2</Text>
<Text className="text-xl font-semibold">Heading 3</Text>
<Text className="text-base text-gray-700 dark:text-gray-300">Body</Text>
<Text className="text-sm text-gray-500 dark:text-gray-400">Caption</Text>

Accessibility

  • accessibilityRole on every interactive element
  • accessibilityLabel on icon-only buttons
  • accessibilityState={{ disabled, selected }} when state changes
  • Minimum touch target: min-h-[44px] min-w-[44px]
  • importantForAccessibility="no" on decorative elements

What ships with it

Read from the repository

Just SKILL.md. No reference files, no scripts.

Keep looking

Skills are one crate of 326,512. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.