reusable-ui-components
Guidelines for creating reusable, portable UI components with native-first design, compound patterns, and accessibility
Install
mkdir -p .claude/skills/reusable-ui-components && curl -L -o skill.zip "https://mcp.directory/api/skills/download/3898" && unzip -o skill.zip -d .claude/skills/reusable-ui-components && rm skill.zipInstalls to .claude/skills/reusable-ui-components
About this skill
Creating Reusable UI Components for Expo Router
This guide covers building production-quality, portable UI components inspired by shadcn/ui, Base UI, Radix, and Konsta UI. Components follow iOS San Francisco design guidelines with liquid glass aesthetics and prioritize native primitives with graceful fallbacks.
Philosophy
Core Principles
- Portable & Copy-Paste Ready - Components should be self-contained and easy to copy between projects
- Native-First - Always check for Expo Router primitives before building custom solutions
- iOS Design Language - Use San Francisco style guide as the baseline for all platforms
- Compound Components - Break complex components into composable sub-components
- CSS Variables for Customization - Use design tokens for theming, not hardcoded values
- Accessibility Built-In - Keyboard handling, safe areas, and screen reader support by default
Inspiration Sources
| Library | Learn From |
|---|---|
| shadcn/ui | Component structure, copy-paste architecture |
| Radix UI | Compound component patterns, accessibility primitives |
| Base UI | Headless component APIs, composition patterns |
| Konsta UI | iOS liquid glass aesthetics, platform-adaptive styling |
Component File Structure
src/components/ui/
├── button.tsx # Default (shared) implementation
├── button.ios.tsx # iOS-specific overrides (optional)
├── button.web.tsx # Web-specific overrides (optional)
└── button.android.tsx # Android-specific overrides (optional)
Metro Resolution Priority:
.ios.tsx/.android.tsx/.web.tsx(platform-specific).native.tsx(iOS + Android).tsx(fallback for all platforms)
Design Tokens & CSS Variables
Global Theme Variables
Define customizable design tokens in src/global.css:
@import "tailwindcss/theme.css" layer(theme);
@import "tailwindcss/preflight.css" layer(base);
@import "tailwindcss/utilities.css";
/* Import Apple system colors */
@import "./css/sf.css";
@layer theme {
@theme {
/* Typography Scale */
--font-sans: system-ui;
--font-mono: ui-monospace;
--font-rounded: ui-rounded;
/* Component Tokens */
--component-radius: 12px;
--component-radius-lg: 16px;
--component-radius-full: 9999px;
/* Spacing Scale */
--spacing-xs: 4px;
--spacing-sm: 8px;
--spacing-md: 12px;
--spacing-lg: 16px;
--spacing-xl: 24px;
/* Animation */
--transition-fast: 150ms;
--transition-normal: 200ms;
--transition-slow: 300ms;
}
}
/* Platform-specific overrides */
@media ios {
:root {
--font-sans: system-ui;
--font-rounded: ui-rounded;
--component-radius: 10px;
}
}
@media android {
:root {
--font-sans: normal;
--font-rounded: normal;
--component-radius: 8px;
}
}
Apple System Colors
Create platform-adaptive colors in src/css/sf.css:
@layer base {
html {
color-scheme: light dark;
}
}
:root {
/* Primary Colors */
--sf-blue: light-dark(rgb(0 122 255), rgb(10 132 255));
--sf-green: light-dark(rgb(52 199 89), rgb(48 209 89));
--sf-red: light-dark(rgb(255 59 48), rgb(255 69 58));
--sf-orange: light-dark(rgb(255 149 0), rgb(255 159 10));
--sf-yellow: light-dark(rgb(255 204 0), rgb(255 214 10));
--sf-purple: light-dark(rgb(175 82 222), rgb(191 90 242));
--sf-pink: light-dark(rgb(255 45 85), rgb(255 55 95));
/* Gray Scale */
--sf-gray: light-dark(rgb(142 142 147), rgb(142 142 147));
--sf-gray-2: light-dark(rgb(174 174 178), rgb(99 99 102));
--sf-gray-3: light-dark(rgb(199 199 204), rgb(72 72 74));
--sf-gray-4: light-dark(rgb(209 209 214), rgb(58 58 60));
--sf-gray-5: light-dark(rgb(229 229 234), rgb(44 44 46));
--sf-gray-6: light-dark(rgb(242 242 247), rgb(28 28 30));
/* Text Colors */
--sf-text: light-dark(rgb(0 0 0), rgb(255 255 255));
--sf-text-2: light-dark(rgb(60 60 67 / 0.6), rgb(235 235 245 / 0.6));
--sf-text-3: light-dark(rgb(60 60 67 / 0.3), rgb(235 235 245 / 0.3));
--sf-text-placeholder: light-dark(rgb(60 60 67 / 0.3), rgb(235 235 245 / 0.3));
/* Background Colors */
--sf-bg: light-dark(rgb(255 255 255), rgb(0 0 0));
--sf-bg-2: light-dark(rgb(242 242 247), rgb(28 28 30));
--sf-grouped-bg: light-dark(rgb(242 242 247), rgb(0 0 0));
--sf-grouped-bg-2: light-dark(rgb(255 255 255), rgb(28 28 30));
/* Border & Fill */
--sf-border: light-dark(rgb(60 60 67 / 0.12), rgb(84 84 88 / 0.65));
--sf-fill: light-dark(rgb(120 120 128 / 0.2), rgb(120 120 128 / 0.32));
/* Link Color */
--sf-link: var(--sf-blue);
}
/* iOS: Use native platform colors */
@media ios {
:root {
--sf-blue: platformColor(systemBlue);
--sf-green: platformColor(systemGreen);
--sf-red: platformColor(systemRed);
--sf-orange: platformColor(systemOrange);
--sf-yellow: platformColor(systemYellow);
--sf-purple: platformColor(systemPurple);
--sf-pink: platformColor(systemPink);
--sf-gray: platformColor(systemGray);
--sf-gray-2: platformColor(systemGray2);
--sf-gray-3: platformColor(systemGray3);
--sf-gray-4: platformColor(systemGray4);
--sf-gray-5: platformColor(systemGray5);
--sf-gray-6: platformColor(systemGray6);
--sf-text: platformColor(label);
--sf-text-2: platformColor(secondaryLabel);
--sf-text-3: platformColor(tertiaryLabel);
--sf-text-placeholder: platformColor(placeholderText);
--sf-bg: platformColor(systemBackground);
--sf-bg-2: platformColor(secondarySystemBackground);
--sf-grouped-bg: platformColor(systemGroupedBackground);
--sf-grouped-bg-2: platformColor(secondarySystemGroupedBackground);
--sf-border: platformColor(separator);
--sf-fill: platformColor(tertiarySystemFill);
--sf-link: platformColor(link);
}
}
/* Register as Tailwind theme colors */
@layer theme {
@theme {
--color-sf-blue: var(--sf-blue);
--color-sf-green: var(--sf-green);
--color-sf-red: var(--sf-red);
--color-sf-orange: var(--sf-orange);
--color-sf-yellow: var(--sf-yellow);
--color-sf-purple: var(--sf-purple);
--color-sf-pink: var(--sf-pink);
--color-sf-gray: var(--sf-gray);
--color-sf-gray-2: var(--sf-gray-2);
--color-sf-gray-3: var(--sf-gray-3);
--color-sf-gray-4: var(--sf-gray-4);
--color-sf-gray-5: var(--sf-gray-5);
--color-sf-gray-6: var(--sf-gray-6);
--color-sf-text: var(--sf-text);
--color-sf-text-2: var(--sf-text-2);
--color-sf-text-3: var(--sf-text-3);
--color-sf-text-placeholder: var(--sf-text-placeholder);
--color-sf-bg: var(--sf-bg);
--color-sf-bg-2: var(--sf-bg-2);
--color-sf-grouped-bg: var(--sf-grouped-bg);
--color-sf-grouped-bg-2: var(--sf-grouped-bg-2);
--color-sf-border: var(--sf-border);
--color-sf-fill: var(--sf-fill);
--color-sf-link: var(--sf-link);
}
}
Accessing CSS Variables in JavaScript
import { useCSSVariable } from "@/tw";
function MyComponent() {
const primaryColor = useCSSVariable("--sf-blue");
const borderColor = useCSSVariable("--sf-border");
return (
<View style={{ borderColor }}>
<Text style={{ color: primaryColor }}>Hello</Text>
</View>
);
}
Compound Component Pattern
Use compound components for complex, multi-element UI. This provides flexibility while maintaining cohesive behavior.
Template Structure
"use client";
import React, { createContext, use } from "react";
import { View, Text, Pressable } from "@/tw";
import { cn } from "@/lib/utils";
import type { ViewProps, TextProps } from "react-native";
// 1. Define Context for shared state
interface ComponentContextValue {
variant: "default" | "outline" | "ghost";
size: "sm" | "md" | "lg";
disabled?: boolean;
}
const ComponentContext = createContext<ComponentContextValue | null>(null);
function useComponentContext() {
const context = use(ComponentContext);
if (!context) {
throw new Error("Component parts must be used within Component.Root");
}
return context;
}
// 2. Root component provides context
interface RootProps extends ViewProps {
variant?: ComponentContextValue["variant"];
size?: ComponentContextValue["size"];
disabled?: boolean;
}
function Root({
variant = "default",
size = "md",
disabled,
children,
className,
...props
}: RootProps) {
return (
<ComponentContext value={{ variant, size, disabled }}>
<View
{...props}
className={cn(
"flex-row items-center",
disabled && "opacity-50",
className
)}
>
{children}
</View>
</ComponentContext>
);
}
// 3. Sub-components consume context
function Label({ className, ...props }: TextProps) {
const { size } = useComponentContext();
return (
<Text
{...props}
className={cn(
"text-sf-text",
size === "sm" && "text-sm",
size === "md" && "text-base",
size === "lg" && "text-lg",
className
)}
/>
);
}
function Icon({ className, ...props }: ViewProps) {
const { size } = useComponentContext();
const sizeClass = {
sm: "w-4 h-4",
md: "w-5 h-5",
lg: "w-6 h-6",
}[size];
return (
<View {...props} className={cn(sizeClass, className)} />
);
}
// 4. Export as compound component
export const Component = {
Root,
Label,
Icon,
};
// 5. Convenience export for simple usage
export function SimpleComponent(props: RootProps & { label: string }) {
const { label, ...rootProps } = props;
return (
<Component.Root {...rootProps}>
<Component.Label>{label}</Component.Label>
</Component.Root>
);
}
Native-First Component Development
Check for Expo Router Primitives First
Before building custom components, check if Expo Router or Expo provides a native primitive:
| Component Need | Check First |
|---|---|
| Navigation Stack | expo-router Stack |
| Tab Navigation | expo-router Tabs |
| Mod |
Content truncated.
More by EvanBacon
View all skills by EvanBacon →You might also like
flutter-development
aj-geddes
Build beautiful cross-platform mobile apps with Flutter and Dart. Covers widgets, state management with Provider/BLoC, navigation, API integration, and material design.
drawio-diagrams-enhanced
jgtolentino
Create professional draw.io (diagrams.net) diagrams in XML format (.drawio files) with integrated PMP/PMBOK methodologies, extensive visual asset libraries, and industry-standard professional templates. Use this skill when users ask to create flowcharts, swimlane diagrams, cross-functional flowcharts, org charts, network diagrams, UML diagrams, BPMN, project management diagrams (WBS, Gantt, PERT, RACI), risk matrices, stakeholder maps, or any other visual diagram in draw.io format. This skill includes access to custom shape libraries for icons, clipart, and professional symbols.
ui-ux-pro-max
nextlevelbuilder
"UI/UX design intelligence. 50 styles, 21 palettes, 50 font pairings, 20 charts, 8 stacks (React, Next.js, Vue, Svelte, SwiftUI, React Native, Flutter, Tailwind). Actions: plan, build, create, design, implement, review, fix, improve, optimize, enhance, refactor, check UI/UX code. Projects: website, landing page, dashboard, admin panel, e-commerce, SaaS, portfolio, blog, mobile app, .html, .tsx, .vue, .svelte. Elements: button, modal, navbar, sidebar, card, table, form, chart. Styles: glassmorphism, claymorphism, minimalism, brutalism, neumorphism, bento grid, dark mode, responsive, skeuomorphism, flat design. Topics: color palette, accessibility, animation, layout, typography, font pairing, spacing, hover, shadow, gradient."
godot
bfollington
This skill should be used when working on Godot Engine projects. It provides specialized knowledge of Godot's file formats (.gd, .tscn, .tres), architecture patterns (component-based, signal-driven, resource-based), common pitfalls, validation tools, code templates, and CLI workflows. The `godot` command is available for running the game, validating scripts, importing resources, and exporting builds. Use this skill for tasks involving Godot game development, debugging scene/resource files, implementing game systems, or creating new Godot components.
nano-banana-pro
garg-aayush
Generate and edit images using Google's Nano Banana Pro (Gemini 3 Pro Image) API. Use when the user asks to generate, create, edit, modify, change, alter, or update images. Also use when user references an existing image file and asks to modify it in any way (e.g., "modify this image", "change the background", "replace X with Y"). Supports both text-to-image generation and image-to-image editing with configurable resolution (1K default, 2K, or 4K for high resolution). DO NOT read the image file first - use this skill directly with the --input-image parameter.
fastapi-templates
wshobson
Create production-ready FastAPI projects with async patterns, dependency injection, and comprehensive error handling. Use when building new FastAPI applications or setting up backend API projects.
Related MCP Servers
Browse all serversFlyonUI is a React UI library for accessing component code, block metadata, and building workflows with conversational c
Create modern React UI components instantly with Magic AI Agent. Integrates with top IDEs for fast, stunning design and
Access shadcn/ui v4 components, blocks, and demos for rapid React UI library development. Seamless integration and sourc
Edit PDF and DOC files online with Office Word. Access advanced text formatting, table editing, and image scaling in you
Empower your Unity projects with Unity-MCP: AI-driven control, seamless integration, and advanced workflows within the U
Voice MCP powers two-way voice apps with Google Cloud Speech to Text, Speech Recognition, and Text to Speech API for acc
Stay ahead of the MCP ecosystem
Get weekly updates on new skills and servers.