AgentStack
SKILL verified MIT Self-run

Gluestack Ui V5:variants

skill-gluestack-agent-skills-variants · by gluestack

Guide for creating custom variants for gluestack-ui v5 components - covers tva usage, extending components, variant patterns, and customization strategies.

No reviews yet
0 installs
2 views
0.0% view→install

Install

$ agentstack add skill-gluestack-agent-skills-variants

✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

Are you the author of Gluestack Ui V5:variants? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

Gluestack UI v5 — Creating Component Variants

This sub-skill focuses on creating custom variants for existing gluestack-ui v5 components, allowing you to extend the design system with project-specific styling patterns while maintaining consistency and type safety.

When to Create a Variant

Create a new variant when:

  1. Repeating the same style combination - Multiple places use the same className pattern
  2. Project-specific design patterns - Brand-specific button styles, card types, etc.
  3. Conditional styling - Component appearance changes based on props
  4. Extending existing components - Adding new visual styles to Gluestack components
  5. Theme-specific variations - Different appearances for specific contexts

Don't create variants for:

  • One-off custom styles (use className instead)
  • Simple modifications (use existing props + className)
  • Styles that should be in the global design system

Variant Creation Workflow

Step 1: Analyze the Component

Before creating a variant, understand:

  1. What's the base component? - Button, Card, Badge, etc.
  2. What visual states are needed? - Colors, sizes, borders, shadows
  3. Are there sub-components? - ButtonText, CardHeader, etc.
  4. What props should control variants? - variant, size, state props
  5. Should variants affect children? - Parent variants for sub-components

Step 2: Plan Variant Structure

Define your variant system:

// Example: Planning a Badge component with variants
{
  variant: ['default', 'success', 'warning', 'error', 'info']
  size: ['sm', 'md', 'lg']
  shape: ['rounded', 'pill', 'square']
}

Step 3: Implement with tva

Use tva (Tailwind Variant Authority) to create type-safe, composable variants.

Creating Simple Variants

Template: Adding Variants to a Custom Component

import React from 'react';
import { tva } from '@gluestack-ui/utils/nativewind-utils';
import { Box } from '@/components/ui/box';
import { Text } from '@/components/ui/text';

interface BadgeProps {
  readonly variant?: 'default' | 'success' | 'warning' | 'error' | 'info';
  readonly size?: 'sm' | 'md' | 'lg';
  readonly shape?: 'rounded' | 'pill' | 'square';
  readonly className?: string;
  readonly children: React.ReactNode;
}

// Define variant styles
const badgeStyles = tva({
  base: 'inline-flex items-center justify-center font-medium',
  variants: {
    variant: {
      default: 'bg-muted text-muted-foreground',
      success: 'bg-primary/10 text-primary border border-primary/20',
      warning: 'bg-accent/10 text-accent-foreground border border-accent/20',
      error: 'bg-destructive/10 text-destructive border border-destructive/20',
      info: 'bg-secondary/10 text-secondary-foreground border border-secondary/20',
    },
    size: {
      sm: 'px-2 py-0.5 text-xs',
      md: 'px-3 py-1 text-sm',
      lg: 'px-4 py-1.5 text-base',
    },
    shape: {
      rounded: 'rounded-md',
      pill: 'rounded-full',
      square: 'rounded-none',
    },
  },
  defaultVariants: {
    variant: 'default',
    size: 'md',
    shape: 'rounded',
  },
});

export const Badge = ({
  variant,
  size,
  shape,
  className,
  children
}: BadgeProps) => {
  return (
    
      {children}
    
  );
};

// Usage:
// Active
// Error

Key Points:

  • ✅ Uses tva for variant management
  • ✅ Base styles apply to all variants
  • ✅ Multiple variant dimensions (variant, size, shape)
  • ✅ Default variants specified
  • ✅ className override support with class parameter
  • ✅ TypeScript types for variant options

Extending Existing Gluestack Components

Template: Adding Custom Variants to Button

import React from 'react';
import { tva } from '@gluestack-ui/utils/nativewind-utils';
import { Button as GluestackButton, ButtonText } from '@/components/ui/button';

// Define additional variant styles
const customButtonStyles = tva({
  base: '',
  variants: {
    variant: {
      // Extend existing variants with new ones
      gradient: 'bg-gradient-to-r from-primary to-accent',
      glass: 'bg-background/20 backdrop-blur-lg border border-border/50',
      neon: 'bg-transparent border-2 border-primary shadow-[0_0_15px_rgba(59,130,246,0.5)]',
    },
    size: {
      // Add custom sizes
      xs: 'px-2 py-1',
      xl: 'px-8 py-4',
    },
  },
});

interface CustomButtonProps {
  readonly variant?: 'default' | 'destructive' | 'outline' | 'secondary' | 'ghost' | 'link' | 'gradient' | 'glass' | 'neon';
  readonly size?: 'default' | 'sm' | 'lg' | 'icon' | 'xs' | 'xl';
  readonly className?: string;
  readonly onPress?: () => void;
  readonly isDisabled?: boolean;
  readonly children: React.ReactNode;
}

export const CustomButton = ({
  variant = 'default',
  size = 'default',
  className,
  onPress,
  isDisabled,
  children,
}: CustomButtonProps) => {
  // Use Gluestack Button for built-in variants
  if (['default', 'destructive', 'outline', 'secondary', 'ghost', 'link'].includes(variant)) {
    return (
      
        {children}
      
    );
  }

  // Use custom variants
  return (
    
      {children}
    
  );
};

// Usage:
// 
//   Gradient Button
// 
//
// 
//   Neon Button
// 

Key Points:

  • ✅ Extends existing component
  • ✅ Preserves original variants
  • ✅ Adds new custom variants
  • ✅ Maintains compound component pattern
  • ✅ Type-safe variant options

Parent-Child Variant Relationships

When creating components with sub-components, use parentVariants to style children based on parent state.

Template: Card with Variant-Aware Children

import React from 'react';
import { tva } from '@gluestack-ui/utils/nativewind-utils';
import { Box } from '@/components/ui/box';
import { Heading } from '@/components/ui/heading';
import { Text } from '@/components/ui/text';

interface CardProps {
  readonly variant?: 'default' | 'elevated' | 'outlined' | 'ghost';
  readonly colorScheme?: 'neutral' | 'primary' | 'success' | 'error';
  readonly className?: string;
  readonly children: React.ReactNode;
}

interface CardHeaderProps {
  readonly className?: string;
  readonly children: React.ReactNode;
}

interface CardBodyProps {
  readonly className?: string;
  readonly children: React.ReactNode;
}

// Parent card styles
const cardStyles = tva({
  base: 'rounded-lg overflow-hidden',
  variants: {
    variant: {
      default: 'border border-border bg-card',
      elevated: 'shadow-hard-2 bg-card',
      outlined: 'border-2 border-border bg-transparent',
      ghost: 'bg-transparent',
    },
    colorScheme: {
      neutral: '',
      primary: 'border-primary/20',
      success: 'border-primary/20',
      error: 'border-destructive/20',
    },
  },
  compoundVariants: [
    {
      variant: 'default',
      colorScheme: 'primary',
      class: 'bg-primary/5',
    },
    {
      variant: 'default',
      colorScheme: 'success',
      class: 'bg-primary/5',
    },
    {
      variant: 'default',
      colorScheme: 'error',
      class: 'bg-destructive/5',
    },
  ],
  defaultVariants: {
    variant: 'default',
    colorScheme: 'neutral',
  },
});

// Child styles that respond to parent variants
const cardHeaderStyles = tva({
  base: 'p-4 border-b',
  parentVariants: {
    variant: {
      default: 'border-border',
      elevated: 'border-border/50',
      outlined: 'border-border',
      ghost: 'border-transparent',
    },
    colorScheme: {
      neutral: '',
      primary: 'border-primary/20 bg-primary/5',
      success: 'border-primary/20 bg-primary/5',
      error: 'border-destructive/20 bg-destructive/5',
    },
  },
});

const cardBodyStyles = tva({
  base: 'p-4',
  parentVariants: {
    colorScheme: {
      neutral: '',
      primary: '',
      success: '',
      error: '',
    },
  },
});

// Context to share variant state with children
const CardContext = React.createContext>({
  variant: 'default',
  colorScheme: 'neutral',
});

export const Card = ({
  variant = 'default',
  colorScheme = 'neutral',
  className,
  children
}: CardProps) => {
  return (
    
      
        {children}
      
    
  );
};

export const CardHeader = ({ className, children }: CardHeaderProps) => {
  const { variant, colorScheme } = React.useContext(CardContext);
  return (
    
      {children}
    
  );
};

export const CardBody = ({ className, children }: CardBodyProps) => {
  const { variant, colorScheme } = React.useContext(CardContext);
  return (
    
      {children}
    
  );
};

// Usage:
// 
//   
//     Success Card
//   
//   
//     This card responds to parent variants
//   
// 

Key Points:

  • ✅ Parent context shares variant state
  • ✅ Children use parentVariants to style based on parent
  • ✅ Compound variants for complex combinations
  • ✅ Type-safe context usage
  • ✅ Flexible composition

Compound Variants

Use compound variants when combinations of variant options need special styling.

Template: Button with Compound Variants

import React from 'react';
import { tva } from '@gluestack-ui/utils/nativewind-utils';
import { Button, ButtonText, ButtonIcon } from '@/components/ui/button';
import { Loader2Icon } from '@/components/ui/icon';

interface ActionButtonProps {
  readonly variant?: 'solid' | 'outline' | 'ghost';
  readonly colorScheme?: 'primary' | 'secondary' | 'destructive';
  readonly size?: 'sm' | 'md' | 'lg';
  readonly isLoading?: boolean;
  readonly isDisabled?: boolean;
  readonly className?: string;
  readonly onPress?: () => void;
  readonly children: React.ReactNode;
}

const actionButtonStyles = tva({
  base: 'rounded-md font-medium transition-colors',
  variants: {
    variant: {
      solid: '',
      outline: 'border-2 bg-transparent',
      ghost: 'bg-transparent',
    },
    colorScheme: {
      primary: '',
      secondary: '',
      destructive: '',
    },
    size: {
      sm: 'px-3 py-1.5 text-sm',
      md: 'px-4 py-2 text-base',
      lg: 'px-6 py-3 text-lg',
    },
  },
  compoundVariants: [
    // Solid + Primary
    {
      variant: 'solid',
      colorScheme: 'primary',
      class: 'bg-primary text-primary-foreground data-[hover=true]:bg-primary/90',
    },
    // Solid + Destructive
    {
      variant: 'solid',
      colorScheme: 'destructive',
      class: 'bg-destructive text-white data-[hover=true]:bg-destructive/90',
    },
    // Outline + Primary
    {
      variant: 'outline',
      colorScheme: 'primary',
      class: 'border-primary text-primary data-[hover=true]:bg-primary/10',
    },
    // Outline + Destructive
    {
      variant: 'outline',
      colorScheme: 'destructive',
      class: 'border-destructive text-destructive data-[hover=true]:bg-destructive/10',
    },
    // Ghost + Primary
    {
      variant: 'ghost',
      colorScheme: 'primary',
      class: 'text-primary data-[hover=true]:bg-primary/10',
    },
    // Ghost + Destructive
    {
      variant: 'ghost',
      colorScheme: 'destructive',
      class: 'text-destructive data-[hover=true]:bg-destructive/10',
    },
  ],
  defaultVariants: {
    variant: 'solid',
    colorScheme: 'primary',
    size: 'md',
  },
});

export const ActionButton = ({
  variant,
  colorScheme,
  size,
  isLoading = false,
  isDisabled = false,
  className,
  onPress,
  children,
}: ActionButtonProps) => {
  return (
    
      {isLoading && }
      {children}
    
  );
};

// Usage:
// 
//   Primary Action
// 
//
// 
//   Delete
// 

Key Points:

  • ✅ Compound variants handle specific combinations
  • ✅ Base variants provide defaults
  • ✅ Hover states with data attributes
  • ✅ Loading state integration
  • ✅ Flexible variant combinations

Common Variant Patterns

Pattern 1: Status Badges

const statusBadgeStyles = tva({
  base: 'inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-semibold',
  variants: {
    status: {
      active: 'bg-primary/10 text-primary',
      inactive: 'bg-muted text-muted-foreground',
      pending: 'bg-accent/10 text-accent-foreground',
      completed: 'bg-primary/10 text-primary',
      failed: 'bg-destructive/10 text-destructive',
    },
  },
  defaultVariants: {
    status: 'inactive',
  },
});

// Usage:
// 
//   Active
// 

Pattern 2: Alert Variants

const alertStyles = tva({
  base: 'rounded-lg border p-4',
  variants: {
    severity: {
      info: 'bg-secondary/10 border-secondary/20 text-secondary-foreground',
      success: 'bg-primary/10 border-primary/20 text-primary',
      warning: 'bg-accent/10 border-accent/20 text-accent-foreground',
      error: 'bg-destructive/10 border-destructive/20 text-destructive',
    },
  },
  defaultVariants: {
    severity: 'info',
  },
});

// Usage:
// 
//   Error message
// 

Pattern 3: Interactive Card States

const interactiveCardStyles = tva({
  base: 'rounded-lg border border-border p-4 transition-all cursor-pointer',
  variants: {
    state: {
      default: 'bg-card data-[hover=true]:bg-muted/50',
      selected: 'bg-primary/10 border-primary',
      disabled: 'bg-muted opacity-60 cursor-not-allowed',
    },
  },
  defaultVariants: {
    state: 'default',
  },
});

// Usage:
// 
//   
//     Selected Card
//   
// 

Pattern 4: Size Variants with Consistent Ratios

const avatarStyles = tva({
  base: 'rounded-full overflow-hidden',
  variants: {
    size: {
      xs: 'w-6 h-6',
      sm: 'w-8 h-8',
      md: 'w-12 h-12',
      lg: 'w-16 h-16',
      xl: 'w-20 h-20',
      '2xl': 'w-24 h-24',
    },
  },
  defaultVariants: {
    size: 'md',
  },
});

// Usage:
// 

Best Practices for Variants

✅ Do's

  1. Use semantic variant names

```tsx // ✅ GOOD: Semantic names variant: 'primary' | 'secondary' | 'destructive'

// ❌ BAD: Generic names variant: 'blue' | 'red' | 'green' ```

  1. Provide default variants

``tsx // ✅ GOOD: Always specify defaults defaultVariants: { variant: 'default', size: 'md', } ``

  1. Use compound variants for combinations

``tsx // ✅ GOOD: Handle specific combinations compoundVariants: [ { variant: 'outline', colorScheme: 'primary', class: 'border-primary text-primary', }, ] ``

  1. Keep variant dimensions focused

``tsx // ✅ GOOD: Clear separation variants: { variant: { ... }, // Visual style size: { ... }, // Size state: { ... }, // Interactive state } ``

  1. Use ONLY semantic tokens in variant styles - NO EXCEPTIONS

```tsx // ✅ CORRECT: Semantic tokens with alpha values success: 'bg-primary/10 text-primary border-primary/20' error: 'bg-destructive/10 text-destructive border-destructive/20' muted: 'bg-muted text-muted-foreground border-border'

// ❌ PROHIBITED: Numbered color tokens success: 'bg-green-100 text-green-800 border-green-200' error: 'bg-red-100 text-red-800 border-red-200'

// ❌ PROHIBITED: Generic tokens muted: 'bg-neutral-100 text-neutral-600 border-neutral-300' muted: 'bg-gray-100 text-gray-600 border-gray-300'

// ❌ PROHIBITED: Typography tokens text: 'text-typography-900' ```

❌ Don'ts

  1. Don't create too many variant dimensions

```tsx // ❌ BAD: Too many dimensions variants: { variant: { ... }, size: { ... }, color: { ... }, border: { ... }, shadow: { ... }, rounded: { ... }, }

// ✅ GOOD: Focused dimensions variants: { variant: { ... }, size: { ... }, } ```

  1. Don't mix concerns in variant names

```tsx // ❌ BAD: Mixing visual and semantic variant: 'primary' | 'large-primary' | 'small-secondary'

// ✅ GOOD: Separate dimensions variant: 'primary' | 'secondary' size: 'sm' | 'md' | 'lg' ```

  1. Don't duplicate existing component props

```tsx // ❌ BAD: Duplicating Button's variant prop const CustomButton = ({ variant, ... }: { variant: 'new1' | 'new2' })

// ✅ GOOD: Extend existing variants const CustomButton = ({ variant, ... }: { variant: 'default' | 'outline' | 'new1'

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet — be the first.

Versions

  • v0.1.0 Imported from the upstream source.