con un clic
create-component
Scaffold a UI component with unistyles variants pattern
Instalar con Codex o Claude Copia este prompt, pégalo en Codex, Claude u otro asistente, y deja que revise la página de la skill y la instale por ti.
Menú
Scaffold a UI component with unistyles variants pattern
Instalar con Codex o Claude Copia este prompt, pégalo en Codex, Claude u otro asistente, y deja que revise la página de la skill y la instale por ti.
Basado en la clasificación ocupacional SOC
Transform the base template into a specific application
Add or update i18n keys in both English and Arabic locale files
Scaffold a complete feature module with types, service, hooks, schema, and constants
Generate a validated form with Zod schema, react-hook-form, and i18n
Add a new screen or tab to the app with routing and i18n
Review changed code against project conventions and rules
| name | create-component |
| description | Scaffold a UI component with unistyles variants pattern |
| auto-invocable | true |
| triggers | ["create a component","add a UI component","new component","scaffold component"] |
Scaffold a UI component following the project's exact unistyles variant pattern.
Ask the user for:
src/common/components/) or feature-specific (src/features/<feature>/components/)<ComponentName>/
├── <ComponentName>.tsx
├── <ComponentName>.types.ts
├── <ComponentName>.styles.ts
└── index.ts
// <ComponentName>.types.ts
import type { ViewProps } from 'react-native';
import type { <ComponentName>StyleVariants } from './<ComponentName>.styles';
export interface <ComponentName>Props extends ViewProps, <ComponentName>StyleVariants {
// Required props
// Optional props with defaults documented
}
Type rules:
ViewProps, PressableProps, etc.).styles.ts fileimport type for all type imports@/common/components/shared.types (e.g., ComponentSize)Follow the exact pattern from Button.styles.ts:
// <ComponentName>.styles.ts
import { StyleSheet, type UnistylesVariants } from 'react-native-unistyles';
export const styles = StyleSheet.create((theme) => ({
container: {
// Base styles using theme tokens
backgroundColor: theme.colors.background.surface,
borderRadius: theme.metrics.borderRadius.md,
padding: theme.metrics.spacing.p16,
// Variants block for prop-driven style changes
variants: {
variant: {
primary: { /* ... */ },
secondary: { /* ... */ },
},
size: {
sm: { padding: theme.metrics.spacing.p8 },
md: { padding: theme.metrics.spacing.p12 },
lg: { padding: theme.metrics.spacing.p16 },
},
disabled: {
true: { opacity: 0.5 },
},
},
},
// Additional style targets with their own variants if needed
}));
export type <ComponentName>StyleVariants = UnistylesVariants<typeof styles>;
Style rules:
StyleSheet from react-native-unistylesStyleSheet.create((theme) => ({...}))theme.colors.*theme.metrics.spacing.* or theme.metrics.spacingV.*variants block for any prop-driven style changestrue/false keys) for toggleable states<ComponentName>StyleVariants// <ComponentName>.tsx
import { View } from 'react-native';
import { Text } from '@/common/components/Text';
import { styles } from './<ComponentName>.styles';
import type { <ComponentName>Props } from './<ComponentName>.types';
export function <ComponentName>({
// Destructure props with defaults
variant = 'primary',
size = 'md',
disabled = false,
style,
...rest
}: <ComponentName>Props) {
// Call useVariants ONCE before accessing any styles
styles.useVariants({ variant, size, disabled });
return (
<View
style={[styles.container, style]}
accessibilityRole="..." // appropriate role
{...rest}
>
{/* Component content */}
</View>
);
}
If interactive (handles press):
import { Pressable } from 'react-native';
import Animated from 'react-native-reanimated';
import { useAnimatedPress } from '@/hooks/useAnimatedPress';
const AnimatedPressable = Animated.createAnimatedComponent(Pressable);
export function <ComponentName>({ onPress, disabled, ...props }: <ComponentName>Props) {
const { animatedStyle, onPressIn, onPressOut } = useAnimatedPress();
styles.useVariants({ disabled });
return (
<AnimatedPressable
style={[styles.container, animatedStyle, props.style]}
onPress={onPress}
onPressIn={onPressIn}
onPressOut={onPressOut}
disabled={disabled}
accessibilityRole="button"
accessibilityLabel={/* i18n label */}
accessibilityState={{ disabled }}
{...props}
>
{/* Content */}
</AnimatedPressable>
);
}
Component rules:
styles.useVariants(...) once per render, before accessing stylesaccessibilityRole and accessibilityLabelaccessibilityState for disabled, busy, selected statesText from @/common/components/Text, NOT from react-nativeuseTranslation() hook// index.ts
export { <ComponentName> } from './<ComponentName>';
export type { <ComponentName>Props } from './<ComponentName>.types';
If the component is in src/common/components/, add it to src/common/components/index.ts in the appropriate category section:
// Add under the matching category comment
export { <ComponentName> } from './<ComponentName>';
export type { <ComponentName>Props } from './<ComponentName>';
Run npm run validate to ensure no TypeScript or lint errors.
StyleSheet from react-native-unistyles, NOT react-nativeText from @/common/components/Text, NOT react-nativestyles.useVariants() called once before any style access@/ path aliases