Skip to content

自定义规则

UnoCSS 的自定义规则功能允许我们创建无法用预设满足的特殊样式规则,扩展框架的能力边界。

🎯 规则系统概述

自定义规则通过正则表达式匹配类名,然后返回对应的 CSS 样式对象,提供了极大的灵活性。

基本语法

typescript
// uno.config.ts
export default defineConfig({
  rules: [
    // 静态规则
    ['custom-class', { color: 'red' }],
    
    // 动态规则
    [/^m-(.+)$/, ([, num]) => ({ margin: `${num}px` })],
    
    // 复杂规则
    [/^grid-cols-(.+)$/, ([, cols]) => {
      return {
        'grid-template-columns': `repeat(${cols}, minmax(0, 1fr))`
      }
    }]
  ]
})

📐 布局相关规则

尺寸控制规则

typescript
rules: [
  // 侧边栏宽度
  ['sidebar-width', { 'width': 'var(--sidebar-width)' }],
  
  // 头部高度
  ['header-height', { 'height': 'var(--header-height)' }],
  
  // 标签视图高度
  ['tags-view-height', { 'height': 'var(--tags-view-height)' }],
  
  // 动态宽度设置
  [/^w-custom-(.+)$/, ([, width]) => ({ width: `${width}px` })],
  
  // 动态高度设置
  [/^h-custom-(.+)$/, ([, height]) => ({ height: `${height}px` })]
]

使用示例

html
<!-- 系统布局尺寸 -->
<aside class="sidebar-width bg-gray-800 text-white">侧边栏</aside>
<header class="header-height bg-white shadow-sm">头部导航</header>
<div class="tags-view-height bg-gray-50 border-b">标签栏</div>

<!-- 自定义尺寸 -->
<div class="w-custom-350 h-custom-200 bg-blue-100">
  自定义尺寸容器 (350px × 200px)
</div>

滚动控制规则

typescript
rules: [
  // 基础滚动
  ['scrollbar', { 'overflow': 'auto' }],
  ['scrollbar-none', { 'overflow': 'hidden' }],
  
  // 方向滚动
  ['scrollbar-x', { 'overflow-x': 'auto', 'overflow-y': 'hidden' }],
  ['scrollbar-y', { 'overflow-y': 'auto', 'overflow-x': 'hidden' }],
  
  // 平滑滚动
  ['scroll-smooth', { 'scroll-behavior': 'smooth' }],
  
  // 滚动条样式(webkit)
  ['scrollbar-thin', {
    '&::-webkit-scrollbar': {
      width: '4px',
      height: '4px'
    },
    '&::-webkit-scrollbar-track': {
      background: 'transparent'
    },
    '&::-webkit-scrollbar-thumb': {
      background: 'rgba(0, 0, 0, 0.2)',
      'border-radius': '2px'
    }
  }],
  
  // 隐藏滚动条但保持滚动功能
  ['scrollbar-hide', {
    '-ms-overflow-style': 'none',
    'scrollbar-width': 'none',
    '&::-webkit-scrollbar': {
      display: 'none'
    }
  }]
]

使用示例

html
<!-- 滚动容器 -->
<div class="h-80 scrollbar-y scrollbar-thin bg-white border rounded-lg">
  <div class="p-4">
    <p>这里是很长的内容...</p>
    <p>可以垂直滚动...</p>
    <!-- 更多内容 -->
  </div>
</div>

<!-- 水平滚动表格 -->
<div class="scrollbar-x scrollbar-hide">
  <table class="min-w-full whitespace-nowrap">
    <thead>
      <tr class="bg-gray-50">
        <th class="px-6 py-3">列1</th>
        <th class="px-6 py-3">列2</th>
        <th class="px-6 py-3">列3</th>
        <!-- 更多列 -->
      </tr>
    </thead>
    <tbody>
      <!-- 表格数据 -->
    </tbody>
  </table>
</div>

<!-- 平滑滚动容器 -->
<div class="h-96 overflow-y-auto scroll-smooth">
  <div id="section1" class="h-64 bg-red-100 flex items-center justify-center">
    <h2 class="text-2xl font-bold">第一部分</h2>
  </div>
  <div id="section2" class="h-64 bg-green-100 flex items-center justify-center">
    <h2 class="text-2xl font-bold">第二部分</h2>
  </div>
</div>

📝 文本处理规则

文本省略规则

typescript
rules: [
  // 单行省略
  ['text-ellipsis', {
    'white-space': 'nowrap',
    'overflow': 'hidden',
    'text-overflow': 'ellipsis'
  }],
  
  // 多行省略
  [/^line-clamp-(\d+)$/, ([, lines]) => ({
    'overflow': 'hidden',
    'display': '-webkit-box',
    '-webkit-line-clamp': lines,
    '-webkit-box-orient': 'vertical'
  })],
  
  // 字符限制
  [/^char-limit-(\d+)$/, ([, chars]) => ({
    'max-width': `${chars}ch`,
    'overflow': 'hidden',
    'text-overflow': 'ellipsis',
    'white-space': 'nowrap'
  })]
]

使用示例

html
<!-- 单行文本省略 -->
<div class="w-48">
  <p class="text-ellipsis text-gray-700">
    这是一段很长的文本内容,会被截断并显示省略号
  </p>
</div>

<!-- 多行文本省略 -->
<div class="w-64">
  <p class="line-clamp-3 text-gray-600">
    这是一段很长的文本内容,会被限制在三行内显示。
    超出部分会被截断并显示省略号。
    这样可以保持布局的整齐性。
    多余的文本将被隐藏。
  </p>
</div>

<!-- 字符数限制 -->
<div>
  <p class="char-limit-20 text-sm">
    这段文本会被限制在20个字符宽度内
  </p>
</div>

<!-- 卡片中的文本处理 -->
<div class="max-w-sm bg-white rounded-lg shadow-md overflow-hidden">
  <img src="image.jpg" alt="图片" class="w-full h-48 object-cover">
  <div class="p-4">
    <h3 class="text-ellipsis font-semibold text-lg mb-2">
      这是一个很长的卡片标题可能会被截断
    </h3>
    <p class="line-clamp-2 text-gray-600 text-sm">
      这是卡片的描述内容,可能会很长,
      所以我们使用两行省略来保持布局的整齐。
      多余的内容会被隐藏起来。
    </p>
  </div>
</div>

文本装饰规则

typescript
rules: [
  // 文本阴影
  ['text-shadow', {
    'text-shadow': '1px 1px 2px rgba(0, 0, 0, 0.1)'
  }],
  ['text-shadow-lg', {
    'text-shadow': '2px 2px 4px rgba(0, 0, 0, 0.2)'
  }],
  
  // 文本描边
  [/^text-stroke-(.+)$/, ([, width]) => ({
    '-webkit-text-stroke': `${width}px currentColor`,
    'text-stroke': `${width}px currentColor`
  })],
  
  // 文本渐变
  ['text-gradient', {
    'background': 'linear-gradient(45deg, #667eea 0%, #764ba2 100%)',
    '-webkit-background-clip': 'text',
    'background-clip': 'text',
    '-webkit-text-fill-color': 'transparent',
    'color': 'transparent'
  }],
  
  // 打字机效果
  ['text-typing', {
    'border-right': '2px solid',
    'white-space': 'nowrap',
    'overflow': 'hidden',
    'animation': 'typing 3.5s steps(40, end), blink-caret 0.75s step-end infinite'
  }]
]

使用示例

html
<!-- 文本特效 -->
<div class="space-y-4">
  <h1 class="text-3xl font-bold text-shadow-lg">带阴影的标题</h1>
  
  <h2 class="text-2xl font-bold text-stroke-1 text-white bg-black p-4">
    描边文字效果
  </h2>
  
  <h3 class="text-4xl font-bold text-gradient">
    渐变色标题
  </h3>
  
  <div class="text-typing font-mono text-xl">
    打字机效果文本...
  </div>
</div>

🎨 视觉效果规则

定位和变换规则

typescript
rules: [
  // 相对定位并填满容器
  ['relative-full', {
    'position': 'relative',
    'width': '100%',
    'height': '100%'
  }],
  
  // 绝对定位居中
  ['absolute-center', {
    'position': 'absolute',
    'top': '50%',
    'left': '50%',
    'transform': 'translate(-50%, -50%)'
  }],
  
  // 固定宽高比
  [/^aspect-(\d+)-(\d+)$/, ([, w, h]) => ({
    'aspect-ratio': `${w}/${h}`
  })],
  
  // 3D 变换
  ['transform-3d', {
    'transform-style': 'preserve-3d'
  }],
  
  // 硬件加速
  ['gpu-accelerated', {
    'transform': 'translateZ(0)',
    'backface-visibility': 'hidden',
    'perspective': '1000px'
  }]
]

使用示例

html
<!-- 定位布局 -->
<div class="relative-full bg-gray-100">
  <div class="absolute-center bg-white p-8 rounded-lg shadow-lg">
    <h2 class="text-xl font-semibold mb-4">居中弹窗</h2>
    <p>这是一个居中显示的内容块</p>
  </div>
</div>

<!-- 宽高比容器 -->
<div class="aspect-16-9 bg-gray-200 rounded-lg overflow-hidden">
  <img src="video-thumbnail.jpg" alt="视频缩略图" class="w-full h-full object-cover">
  <div class="absolute-center">
    <button class="bg-white bg-opacity-80 rounded-full p-4 hover:bg-opacity-100 transition-all">
      <svg class="w-8 h-8 text-blue-600" fill="currentColor" viewBox="0 0 20 20">
        <path d="M6.3 2.841A1.5 1.5 0 004 4.11v11.78a1.5 1.5 0 002.3 1.269l9.344-5.89a1.5 1.5 0 000-2.538L6.3 2.84z"></path>
      </svg>
    </button>
  </div>
</div>

<!-- 3D 卡片效果 -->
<div class="transform-3d gpu-accelerated perspective-1000">
  <div class="bg-white rounded-xl shadow-lg p-6 hover:rotateY-12 transition-transform duration-300">
    <h3 class="text-lg font-semibold mb-2">3D 卡片</h3>
    <p class="text-gray-600">鼠标悬停查看3D效果</p>
  </div>
</div>

滤镜效果规则

typescript
rules: [
  // 模糊效果
  [/^blur-(\d+)$/, ([, amount]) => ({
    'filter': `blur(${amount}px)`
  })],
  
  // 亮度调节
  [/^brightness-(\d+)$/, ([, value]) => ({
    'filter': `brightness(${value}%)`
  })],
  
  // 对比度调节
  [/^contrast-(\d+)$/, ([, value]) => ({
    'filter': `contrast(${value}%)`
  })],
  
  // 饱和度调节
  [/^saturate-(\d+)$/, ([, value]) => ({
    'filter': `saturate(${value}%)`
  })],
  
  // 色相旋转
  [/^hue-rotate-(\d+)$/, ([, deg]) => ({
    'filter': `hue-rotate(${deg}deg)`
  })],
  
  // 投影效果
  [/^drop-shadow-(.+)$/, ([, shadow]) => ({
    'filter': `drop-shadow(${shadow.replace(/-/g, ' ')})`
  })],
  
  // 组合滤镜
  ['filter-vintage', {
    'filter': 'sepia(50%) contrast(120%) brightness(110%) saturate(90%)'
  }],
  
  ['filter-grayscale', {
    'filter': 'grayscale(100%)'
  }]
]

使用示例

html
<!-- 滤镜效果展示 -->
<div class="grid grid-cols-2 md:grid-cols-4 gap-4">
  <!-- 原图 -->
  <div class="text-center">
    <img src="sample.jpg" alt="原图" class="w-full h-32 object-cover rounded-lg">
    <p class="text-sm mt-2">原图</p>
  </div>
  
  <!-- 模糊效果 -->
  <div class="text-center">
    <img src="sample.jpg" alt="模糊" class="w-full h-32 object-cover rounded-lg blur-4">
    <p class="text-sm mt-2">模糊</p>
  </div>
  
  <!-- 复古效果 -->
  <div class="text-center">
    <img src="sample.jpg" alt="复古" class="w-full h-32 object-cover rounded-lg filter-vintage">
    <p class="text-sm mt-2">复古</p>
  </div>
  
  <!-- 灰度效果 -->
  <div class="text-center">
    <img src="sample.jpg" alt="灰度" class="w-full h-32 object-cover rounded-lg filter-grayscale">
    <p class="text-sm mt-2">灰度</p>
  </div>
</div>

<!-- 悬停滤镜效果 -->
<div class="group relative overflow-hidden rounded-lg">
  <img src="sample.jpg" alt="悬停效果" class="w-full h-48 object-cover transition-all duration-300 group-hover:brightness-110 group-hover:saturate-120">
  <div class="absolute inset-0 bg-black bg-opacity-0 group-hover:bg-opacity-20 transition-all duration-300 flex items-center justify-center">
    <span class="text-white opacity-0 group-hover:opacity-100 transition-opacity duration-300 font-semibold">
      查看详情
    </span>
  </div>
</div>

🎭 动画和过渡规则

自定义动画规则

typescript
rules: [
  // 呼吸动画
  ['animate-breathing', {
    'animation': 'breathing 2s ease-in-out infinite alternate'
  }],
  
  // 脉动动画
  ['animate-pulse-slow', {
    'animation': 'pulse 3s cubic-bezier(0.4, 0, 0.6, 1) infinite'
  }],
  
  // 摇摆动画
  ['animate-wiggle', {
    'animation': 'wiggle 1s ease-in-out infinite'
  }],
  
  // 弹跳进入
  ['animate-bounce-in', {
    'animation': 'bounceIn 0.75s ease-out'
  }],
  
  // 淡入放大
  ['animate-zoom-in', {
    'animation': 'zoomIn 0.5s ease-out'
  }],
  
  // 滑入效果
  [/^animate-slide-in-(.+)$/, ([, direction]) => {
    const directions = {
      'left': 'slideInLeft',
      'right': 'slideInRight', 
      'up': 'slideInUp',
      'down': 'slideInDown'
    }
    return {
      'animation': `${directions[direction] || 'slideInLeft'} 0.5s ease-out`
    }
  }]
]

使用示例

html
<!-- 动画效果展示 -->
<div class="grid grid-cols-2 md:grid-cols-3 gap-6 p-8">
  <!-- 呼吸效果 -->
  <div class="text-center">
    <div class="w-16 h-16 bg-blue-500 rounded-full mx-auto animate-breathing"></div>
    <p class="mt-2 text-sm">呼吸动画</p>
  </div>
  
  <!-- 慢脉动 -->
  <div class="text-center">
    <div class="w-16 h-16 bg-green-500 rounded-full mx-auto animate-pulse-slow"></div>
    <p class="mt-2 text-sm">慢脉动</p>
  </div>
  
  <!-- 摇摆效果 -->
  <div class="text-center">
    <div class="w-16 h-16 bg-red-500 rounded-full mx-auto animate-wiggle"></div>
    <p class="mt-2 text-sm">摇摆动画</p>
  </div>
</div>

<!-- 页面进入动画 -->
<section class="animate-slide-in-up">
  <h2 class="text-2xl font-bold mb-4 animate-zoom-in">欢迎来到我们的网站</h2>
  <p class="text-gray-600 animate-slide-in-left" style="animation-delay: 0.2s;">
    这里是网站的介绍内容
  </p>
  <button class="mt-4 px-6 py-2 bg-blue-500 text-white rounded animate-bounce-in" style="animation-delay: 0.4s;">
    立即开始
  </button>
</section>

过渡效果规则

typescript
rules: [
  // 自定义过渡时长
  [/^duration-(.+)$/, ([, time]) => ({
    'transition-duration': `${time}ms`
  })],
  
  // 自定义缓动函数
  ['ease-bounce', {
    'transition-timing-function': 'cubic-bezier(0.68, -0.55, 0.265, 1.55)'
  }],
  
  ['ease-in-back', {
    'transition-timing-function': 'cubic-bezier(0.6, -0.28, 0.735, 0.045)'
  }],
  
  ['ease-out-back', {
    'transition-timing-function': 'cubic-bezier(0.175, 0.885, 0.32, 1.275)'
  }],
  
  // 多属性过渡
  ['transition-all-smooth', {
    'transition': 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)'
  }],
  
  ['transition-colors-fast', {
    'transition': 'background-color 0.15s ease, color 0.15s ease'
  }]
]

使用示例

html
<!-- 过渡效果按钮 -->
<div class="space-x-4">
  <button class="px-6 py-2 bg-blue-500 text-white rounded transition-all-smooth hover:bg-blue-600 hover:scale-105">
    平滑过渡
  </button>
  
  <button class="px-6 py-2 bg-green-500 text-white rounded duration-200 ease-bounce hover:bg-green-600 hover:scale-110">
    弹跳效果
  </button>
  
  <button class="px-6 py-2 bg-purple-500 text-white rounded duration-300 ease-out-back hover:bg-purple-600 hover:scale-105">
    回弹效果
  </button>
</div>

<!-- 卡片悬停效果 -->
<div class="max-w-sm bg-white rounded-lg shadow-md transition-all-smooth hover:shadow-xl hover:scale-102 cursor-pointer">
  <img src="card-image.jpg" alt="卡片图片" class="w-full h-48 object-cover rounded-t-lg">
  <div class="p-6">
    <h3 class="text-lg font-semibold mb-2 transition-colors-fast group-hover:text-blue-600">
      卡片标题
    </h3>
    <p class="text-gray-600">卡片描述内容...</p>
  </div>
</div>

🔧 实用工具规则

调试辅助规则

typescript
rules: [
  // 边框调试
  ['debug-border', {
    'border': '1px solid red !important'
  }],
  
  ['debug-outline', {
    'outline': '2px solid lime !important',
    'outline-offset': '2px'
  }],
  
  // 网格调试
  ['debug-grid', {
    'background-image': 'linear-gradient(rgba(255,0,0,0.1) 1px, transparent 1px), linear-gradient(90deg, rgba(255,0,0,0.1) 1px, transparent 1px)',
    'background-size': '20px 20px'
  }],
  
  // 内容占位
  ['placeholder-content', {
    '&::before': {
      'content': '"Lorem ipsum dolor sit amet..."',
      'color': '#666',
      'font-style': 'italic'
    }
  }],
  
  // 开发环境显示
  ['dev-only', {
    '@media (prefers-reduced-motion: no-preference)': {
      'display': 'block'
    }
  }]
]

性能优化规则

typescript
rules: [
  // 内容可见性优化
  ['content-visibility-auto', {
    'content-visibility': 'auto'
  }],
  
  // 图层提升
  ['will-change-transform', {
    'will-change': 'transform'
  }],
  
  ['will-change-opacity', {
    'will-change': 'opacity'
  }],
  
  // 字体渲染优化
  ['font-smooth', {
    '-webkit-font-smoothing': 'antialiased',
    '-moz-osx-font-smoothing': 'grayscale'
  }],
  
  // 触摸优化
  ['touch-manipulation', {
    'touch-action': 'manipulation'
  }]
]

使用示例

html
<!-- 调试模式 -->
<div class="debug-grid p-4">
  <div class="debug-border p-4 mb-4">
    <h2 class="debug-outline">调试标题</h2>
    <p class="placeholder-content"></p>
  </div>
</div>

<!-- 性能优化 -->
<div class="content-visibility-auto">
  <div class="will-change-transform hover:scale-105 transition-transform">
    <img src="large-image.jpg" alt="大图片" class="w-full font-smooth">
  </div>
</div>

<!-- 移动端优化 -->
<button class="touch-manipulation bg-blue-500 text-white px-6 py-3 rounded-lg active:scale-95 transition-transform">
  触摸按钮
</button>

📱 响应式自定义规则

容器查询规则

typescript
rules: [
  // 容器查询支持
  [/^container-(.+)$/, ([, size]) => {
    const sizes = {
      'xs': '320px',
      'sm': '640px', 
      'md': '768px',
      'lg': '1024px',
      'xl': '1280px'
    }
    return {
      'container-type': 'inline-size',
      '@container': `(min-width: ${sizes[size]}) { /* 样式 */ }`
    }
  }],
  
  // 视口单位
  ['vh-screen', { 'height': '100vh' }],
  ['vw-screen', { 'width': '100vw' }],
  ['dvh-screen', { 'height': '100dvh' }], // 动态视口高度
  ['svh-screen', { 'height': '100svh' }], // 小视口高度
  ['lvh-screen', { 'height': '100lvh' }], // 大视口高度
]

🎨 主题相关规则

主题切换规则

typescript
rules: [
  // CSS变量绑定
  [/^var-(.+)$/, ([, varName]) => ({
    [varName.replace(/-/g, '_')]: `var(--${varName})`
  })],
  
  // 主题感知样式
  ['theme-bg', {
    'background-color': 'var(--bg-color)',
    'color': 'var(--text-color)'
  }],
  
  ['theme-border', {
    'border-color': 'var(--border-color)'
  }],
  
  // 系统主题检测
  ['prefer-dark', {
    '@media (prefers-color-scheme: dark)': {
      /* 暗色样式 */
    }
  }],
  
  ['prefer-light', {
    '@media (prefers-color-scheme: light)': {
      /* 亮色样式 */
    }
  }]
]

使用示例

html
<!-- 主题适配组件 -->
<div class="theme-bg theme-border border rounded-lg p-6">
  <h2 class="var-heading-color text-xl font-semibold">主题标题</h2>
  <p class="var-text-secondary">这是主题适配的文本内容</p>
</div>

<!-- 系统主题响应 -->
<div class="prefer-dark:bg-gray-900 prefer-light:bg-white p-4 rounded-lg">
  <p class="prefer-dark:text-white prefer-light:text-gray-900">
    根据系统主题自动调整的内容
  </p>
</div>

📊 使用建议

1. 规则命名规范

  • 使用描述性名称:text-ellipsis 而不是 te
  • 保持一致的命名模式:animate-*, filter-*
  • 避免与现有工具类冲突

2. 性能考虑

  • 优先使用静态规则
  • 复杂动态规则考虑性能影响
  • 合理使用正则表达式

3. 维护性原则

  • 添加注释说明规则用途
  • 定期review和优化规则
  • 记录规则的使用场景

通过自定义规则,我们可以扩展 UnoCSS 的能力,满足项目的特殊需求,同时保持代码的简洁和可维护性。