ProComponents, templates & AI tooling
2.3k

样式

使用 Tailwind 或 StyleSheet API 为 HeroUI Native 组件编写样式

HeroUI Native 提供灵活的样式方案:Tailwind CSS 工具类、StyleSheet API,以及用于动态样式的 render props。

样式原则

HeroUI Native 以 className 作为主要样式入口,所有组件均可通过 className 使用 Tailwind 类。

StyleSheet 优先级: 同时传入 style(StyleSheet)与 className 时,style 优先级更高,可在需要时覆盖 Tailwind。

动画样式: 部分样式属性由 react-native-reanimated 驱动,与 StyleSheet 类似,其优先级也高于 className。要确认哪些属性受动画占用、无法仅靠 className 设置:

  • 在 IDE 中悬停 className — TypeScript 定义会提示可用属性
  • 查阅组件文档 — 组件页顶部通常有样式源码链接,其中会标注动画相关限制

自定义动画样式: 若某属性被动画占用,可在支持 animation 属性的组件上通过 animation 进行调整。

基础样式

使用 className: 所有 HeroUI Native 组件都支持 className

import { Button } from 'heroui-native';

<Button className="bg-accent px-6 py-3 rounded-lg">
  <Button.Label>Custom Button</Button.Label>
</Button>;

使用 style: 也可通过 style 传入内联样式:

import { Button } from 'heroui-native';

<Button style={{ backgroundColor: '#8B5CF6', paddingHorizontal: 24 }}>
  <Button.Label>Styled Button</Button.Label>
</Button>;

Render Props

使用渲染函数读取组件状态并动态定制内容:

import { RadioGroup, Label, cn } from 'heroui-native';

<RadioGroup value={value} onValueChange={setValue}>
  <RadioGroup.Item value="option1">
    {({ isSelected, isInvalid, isDisabled }) => (
      <>
        <Label
          className={cn(
            'text-foreground',
            isSelected && 'text-accent font-semibold'
          )}
        >
          Option 1
        </Label>
        <Radio
          className={cn(
            'border-2 rounded-full',
            isSelected && 'border-accent bg-accent'
          )}
        >
          {isSelected && <CustomIcon />}
        </Radio>
      </>
    )}
  </RadioGroup.Item>
</RadioGroup>;

封装可复用组件

结合 tailwind-variants(Tailwind 优先的变体 API)封装自定义组件:

import { Button } from 'heroui-native';
import type { ButtonRootProps } from 'heroui-native';
import { tv, type VariantProps } from 'tailwind-variants';

const customButtonVariants = tv({
  base: 'font-semibold rounded-lg',
  variants: {
    intent: {
      primary: 'bg-blue-500',
      secondary: 'bg-gray-200',
      danger: 'bg-red-500',
    },
  },
  defaultVariants: {
    intent: 'primary',
  },
});

const customLabelVariants = tv({
  base: '',
  variants: {
    intent: {
      primary: 'text-white',
      secondary: 'text-gray-800',
      danger: 'text-white',
    },
  },
  defaultVariants: {
    intent: 'primary',
  },
});

type CustomButtonVariants = VariantProps<typeof customButtonVariants>;

interface CustomButtonProps
  extends Omit<ButtonRootProps, 'className' | 'variant'>,
    CustomButtonVariants {
  className?: string;
  labelClassName?: string;
}

export function CustomButton({
  intent,
  className,
  labelClassName,
  children,
  ...props
}: CustomButtonProps) {
  return (
    <Button
      className={customButtonVariants({ intent, className })}
      {...props}
    >
      <Button.Label
        className={customLabelVariants({ intent, className: labelClassName })}
      >
        {children}
      </Button.Label>
    </Button>
  );
}

使用组件自带的 classNames

每个 HeroUI Native 组件都会导出与内部一致的 classNames 工具对象,便于让自定义组件在视觉上与库内组件保持一致。

例如,让自定义 Link 看起来像 Button

import { buttonClassNames, cn } from 'heroui-native';
import { Pressable, Text } from 'react-native';

interface LinkProps {
  href: string;
  variant?: 'primary' | 'secondary' | 'outline' | 'ghost';
  size?: 'sm' | 'md' | 'lg';
  children: React.ReactNode;
  className?: string;
}

export function Link({
  href,
  variant = 'primary',
  size = 'md',
  children,
  className,
}: LinkProps) {
  return (
    <Pressable
      className={cn(
        buttonClassNames.root({ variant, size }),
        className
      )}
      onPress={() => {
        // Handle navigation
      }}
    >
      <Text className={buttonClassNames.label({ variant, size })}>
        {children}
      </Text>
    </Pressable>
  );
}

可用的 classNames 导出:

每个组件都会导出对应的 classNames 对象,例如:

  • buttonClassNames — 包含 rootlabel 函数
  • cardClassNames — 包含 rootheaderbodyfooterlabeldescription 函数
  • chipClassNames — 包含 rootlabel 函数
  • 其他组件同理……

典型用法:

import { buttonClassNames } from 'heroui-native';

// Use with variant and size options
const rootClasses = buttonClassNames.root({
  variant: 'primary',
  size: 'md',
  className: 'custom-class', // Optional: merge with your own classes
});

const labelClasses = buttonClassNames.label({
  variant: 'primary',
  size: 'md',
});

classNames 函数的变体参数与对应组件一致,便于在自定义组件与 HeroUI 组件之间保持视觉一致。

响应式设计

HeroUI Native 通过 Uniwind 支持 Tailwind 的响应式断点前缀,如 sm:md:lg:xl:,按屏幕宽度条件应用样式。

移动优先: 先写无前缀的小屏样式,再用断点为大屏增强。

响应式排版与间距

import { Button } from 'heroui-native';
import { View, Text } from 'react-native';

<View className="px-4 sm:px-6 lg:px-8 py-6 sm:py-8">
  <Text className="text-2xl sm:text-3xl lg:text-4xl font-bold mb-4 sm:mb-6">
    Responsive Heading
  </Text>
  <Button className="px-3 sm:px-4 lg:px-6">
    <Button.Label className="text-sm sm:text-base lg:text-lg">
      Responsive Button
    </Button.Label>
  </Button>
</View>;

响应式布局

import { View, Text } from 'react-native';

<View className="flex-row flex-wrap">
  {/* Mobile: 1 column, Tablet: 2 columns, Desktop: 3 columns */}
  <View className="w-full sm:w-1/2 lg:w-1/3 p-2">
    <View className="bg-accent p-4 rounded-lg">
      <Text className="text-accent-foreground">Item 1</Text>
    </View>
  </View>
</View>;

默认断点:

  • sm:640px
  • md:768px
  • lg:1024px
  • xl:1280px
  • 2xl:1536px

自定义断点与更多说明见 Uniwind 断点文档

工具函数

HeroUI Native 提供一些样式相关的工具。

cn

cn 用于合并 Tailwind 类并处理冲突,适合条件类或与 props 传入的类合并:

import { cn } from 'heroui-native';
import { View } from 'react-native';

function MyComponent({ className, isActive }) {
  return (
    <View
      className={cn(
        'bg-background p-4 rounded-lg',
        'border border-separator',
        isActive && 'bg-accent',
        className
      )}
    />
  );
}

cn 基于 tailwind-variants,具备:

  • 自动合并 Tailwind 类(twMerge: true
  • 自定义透明度分组等能力
  • 合理的冲突解决(靠后的类覆盖靠前的类)

冲突示例:

// 'bg-accent' overrides 'bg-background'
cn('bg-background p-4', 'bg-accent');
// Result: 'p-4 bg-accent'

useThemeColor

从 CSS 变量读取主题色,支持单色或一次读取多种颜色(后者更高效)。

单色:

import { useThemeColor } from 'heroui-native';

function MyComponent() {
  const accentColor = useThemeColor('accent');
  const dangerColor = useThemeColor('danger');

  return (
    <View style={{ borderColor: accentColor }}>
      <Text style={{ color: dangerColor }}>Error message</Text>
    </View>
  );
}

多色(推荐在需要多个 token 时使用):

import { useThemeColor } from 'heroui-native';

function MyComponent() {
  const [accentColor, backgroundColor, dangerColor] = useThemeColor([
    'accent',
    'background',
    'danger',
  ]);

  return (
    <View style={{ borderColor: accentColor, backgroundColor }}>
      <Text style={{ color: dangerColor }}>Error message</Text>
    </View>
  );
}

类型签名:

// Single color
useThemeColor(themeColor: ThemeColor): string

// Multiple colors (with type inference for tuples)
useThemeColor<T extends readonly [ThemeColor, ...ThemeColor[]]>(
  themeColor: T
): CreateStringTuple<T['length']>

// Multiple colors (array)
useThemeColor(themeColor: ThemeColor[]): string[]

可用主题色包括:backgroundforegroundsurfaceaccentdefaultsuccesswarningdanger 及其 hover、soft、foreground 等变体,以及 mutedborderseparatorfieldoverlay 等语义色。

下一步

本页目录