vunor

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Vunor

Vunor

Vunor is two things in one package:
  1. A UnoCSS design-system preset (
    presetVunor
    )
    that derives a complete theme — perceptual color palette, golden-ratio typography/spacing, layered backgrounds, font-aware margin correction — from a few mathematical knobs.
  2. A Vue 3 component library of 30+ accessible components built on Reka UI, styled entirely through UnoCSS classes (zero CSS files, no scoped styles).
The two halves are independent: the preset works without the components, and the components are skinnable through the same shortcut system you'd use for your own code.
Vunor是一个集成了两大功能的包:
  1. UnoCSS设计系统预设(
    presetVunor
    :仅通过几个参数,即可生成完整主题——包含感知调色板、黄金比例排版/间距、分层背景、字体适配的边距修正。
  2. Vue 3组件库:基于Reka UI构建的30+个无障碍组件,完全通过UnoCSS类实现样式(零CSS文件,无作用域样式)。
这两部分相互独立:预设可脱离组件单独使用,组件也可通过与自定义代码相同的快捷方式系统进行样式定制。

Mental model

心智模型

Three layers stack on top of each other. Read top-down to understand any class:
SEMANTIC CLASSES        scope-primary, layer-0, surface-100, c8-filled, i8-filled, card
   ↓ expand to (UnoCSS shortcuts, deep-merged from defineShortcuts() objects)
LOW-LEVEL UTILITIES     bg-current, current-bg-scope-color-500, h-fingertip, p-$m
   ↓ resolve to (UnoCSS rules from presetVunor)
CSS CUSTOM PROPERTIES   --scope-color-500, --current-bg, --v-fingertip, --card-spacing
Everything that paints, sizes, or themes flows through CSS variables. Set
scope-primary
once high in the tree → every descendant
bg-current
,
c8-filled
,
surface-100
etc. picks up the primary palette. Change
scope-error
on a subtree → that subtree turns red, no other classes change.
三层结构相互叠加。自上而下阅读即可理解各类的作用:
SEMANTIC CLASSES        scope-primary, layer-0, surface-100, c8-filled, i8-filled, card
   ↓ expand to (UnoCSS shortcuts, deep-merged from defineShortcuts() objects)
LOW-LEVEL UTILITIES     bg-current, current-bg-scope-color-500, h-fingertip, p-$m
   ↓ resolve to (UnoCSS rules from presetVunor)
CSS CUSTOM PROPERTIES   --scope-color-500, --current-bg, --v-fingertip, --card-spacing
所有涉及着色、尺寸或主题的内容均通过CSS变量传递。 在树形结构的高层设置
scope-primary
→ 所有后代的
bg-current
c8-filled
surface-100
等类都会继承主调色板。在子树上修改
scope-error
→ 该子树变为红色,无需修改其他类。

Required foundation: scope

必备基础:scope

scope-{name}
declares which palette is active for a subtree. Without an active scope, the color-aware classes have no values to render. Vunor preflights install
scope-neutral
on
:root
automatically, so things work out of the box and incidental UI (default borders, layer backgrounds, idle text/icons) reads as a calm neutral.
The recommended pattern is to keep
scope-neutral
as the page default and only opt into a stronger scope on accent elements:
  • Brand-colored interactive elements (primary buttons, focused inputs, active tabs, brand banners) →
    scope-primary
    (or
    scope-secondary
    for an alternate accent).
  • State-bearing elements (error inputs, destructive buttons, validation messages) →
    scope-error
    . For warnings →
    scope-warn
    . For success →
    scope-good
    .
That way the page chrome stays neutral and the eye is drawn to the elements that genuinely need attention.
html
<!-- Page-level scope is neutral (preflight default — no explicit class needed) -->
<body class="layer-0">
  <header></header>

  <!-- Brand accent: only the button opts into primary -->
  <button class="scope-primary c8-filled">Save</button>

  <!-- State change: same input, different scope = different visual weight -->
  <VuInput v-model="email" :error="emailError" />
  <!-- VuInput auto-applies scope-error internally when :error is set -->

  <!-- Destructive action: opt into error scope explicitly -->
  <button class="scope-error c8-flat">Delete account</button>
</body>
Valid names:
primary
,
secondary
,
good
,
warn
,
error
,
grey
,
neutral
.
scope-{name}
用于声明子树的活动调色板。没有活动scope时,颜色感知类将无法渲染出对应值。 Vunor的预飞行样式会自动在
:root
上添加
scope-neutral
,因此默认情况下即可正常工作,且常规UI(默认边框、分层背景、静态文本/图标)会呈现为柔和的中性色调。
推荐模式:将
scope-neutral
作为页面默认值,仅在强调元素上启用更醒目的scope:
  • 品牌色交互元素(主按钮、聚焦输入框、激活标签页、品牌横幅)→
    scope-primary
    (或
    scope-secondary
    作为备选强调色)。
  • 状态标识元素(错误输入框、破坏性按钮、验证消息)→
    scope-error
    ;警告元素→
    scope-warn
    ;成功元素→
    scope-good
这样页面框架保持中性,用户注意力会自然被吸引到真正需要关注的元素上。
html
<!-- 页面级scope为中性(预飞行默认值——无需显式添加类) -->
<body class="layer-0">
  <header></header>

  <!-- 品牌强调:仅按钮启用primary scope -->
  <button class="scope-primary c8-filled">保存</button>

  <!-- 状态切换:同一输入框,不同scope对应不同视觉权重 -->
  <VuInput v-model="email" :error="emailError" />
  <!-- 当:error为true时,VuInput会自动在内部应用scope-error -->

  <!-- 破坏性操作:显式启用error scope -->
  <button class="scope-error c8-flat">删除账户</button>
</body>
有效名称:
primary
secondary
good
warn
error
grey
neutral

Quick start

快速开始

ts
// uno.config.ts
import { defineConfig } from 'unocss'
import { presetVunor, vunorShortcuts } from 'vunor/theme'

export default defineConfig({
  presets: [presetVunor({ palette: { colors: { primary: '#6B4EFF' } } })],
  shortcuts: [vunorShortcuts()],
})
ts
// vite.config.ts — add VunorVueResolver for auto-import of <VuFoo>
import Components from 'unplugin-vue-components/vite'
import { VunorVueResolver } from 'vunor/vite'
plugins: [vue(), UnoCSS(), Components({ resolvers: [VunorVueResolver] })]

// OR, for Nuxt 3
modules: ['vunor/nuxt']
html
<!-- App.vue -->
<html class="scope-primary">
  <body class="layer-0">
    <VuCard level="h3">
      <VuCardHeader>Hello</VuCardHeader>
      <VuButton class="c8-filled">Save</VuButton>
    </VuCard>
  </body>
</html>
See references/setup.md for the full setup.
ts
// uno.config.ts
import { defineConfig } from 'unocss'
import { presetVunor, vunorShortcuts } from 'vunor/theme'

export default defineConfig({
  presets: [presetVunor({ palette: { colors: { primary: '#6B4EFF' } } })],
  shortcuts: [vunorShortcuts()],
})
ts
// vite.config.ts — 添加VunorVueResolver以自动导入<VuFoo>组件
import Components from 'unplugin-vue-components/vite'
import { VunorVueResolver } from 'vunor/vite'
plugins: [vue(), UnoCSS(), Components({ resolvers: [VunorVueResolver] })]

// 或者,针对Nuxt 3
modules: ['vunor/nuxt']
html
<!-- App.vue -->
<html class="scope-primary">
  <body class="layer-0">
    <VuCard level="h3">
      <VuCardHeader>你好</VuCardHeader>
      <VuButton class="c8-filled">保存</VuButton>
    </VuCard>
  </body>
</html>
完整设置请查看references/setup.md

How to use this skill

如何使用此技能

Load only the reference file you need. Each file is self-contained.
FileLoad when...
references/setup.mdInstalling Vunor, configuring Vite or Nuxt, listing package exports
references/colors.mdUsing
scope-*
,
layer-*
,
surface-*
,
current-*
,
bg-current
; understanding light/dark behavior
references/theme.mdTuning palette (vividness, saturation, flatness, layersDepth, lightest/darkest), defining custom surfaces, configuring fingertip, baseRadius, typography, animations
references/typography.mdChoosing typography utilities, using golden-ratio spacing tokens, applying
text-mt-*
/
text-mb-*
margin correction, sizing touch targets with
fingertip-*
references/cards.mdBuilding cards:
<VuCard>
,
card
shortcut,
card-{level}
rule,
--card-spacing
, density, rounded corners, header levels
references/shortcuts.mdCustomizing or overriding component styles, understanding c8 (clickable) and i8 (input) systems, using
defineShortcuts
/
mergeVunorShortcuts
/
vunorShortcuts
references/rules.mdLooking up a specific UnoCSS rule pattern provided by Vunor (
scope-*
,
current-*
,
card-*
,
fingertip-*
,
i8-*
,
icon-*
,
text-m*-*
)
references/components.mdUsing non-form components: VuCard, VuButton, VuDialog, VuTabs, VuMenu, VuPopover, VuAppLayout, VuAppToasts, VuIcon, VuLoadingIndicator, VuPagination, VuProgressBar
references/forms.mdUsing form components: VuInput, VuSelect, VuCombobox, VuCheckbox, VuRadioGroup, VuSlider, VuDatePicker
仅加载所需的参考文件。每个文件都是独立的。
文件加载时机
references/setup.md安装Vunor、配置Vite或Nuxt、列出包导出内容时
references/colors.md使用
scope-*
layer-*
surface-*
current-*
bg-current
;理解明暗模式行为时
references/theme.md调优调色板(鲜艳度、饱和度、扁平化程度、分层深度、最亮/最暗值)、定义自定义surface、配置fingertip、baseRadius、排版、动画时
references/typography.md选择排版工具类、使用黄金比例间距令牌、应用
text-mt-*
/
text-mb-*
边距修正、通过
fingertip-*
设置触摸目标大小时
references/cards.md构建卡片:
<VuCard>
card
快捷方式、
card-{level}
规则、
--card-spacing
、密度、圆角、标题级别时
references/shortcuts.md自定义或覆盖组件样式、理解c8(可点击)和i8(输入)系统、使用
defineShortcuts
/
mergeVunorShortcuts
/
vunorShortcuts
references/rules.md查找Vunor提供的特定UnoCSS规则模式(
scope-*
current-*
card-*
fingertip-*
i8-*
icon-*
text-m*-*
)时
references/components.md使用非表单组件:VuCard、VuButton、VuDialog、VuTabs、VuMenu、VuPopover、VuAppLayout、VuAppToasts、VuIcon、VuLoadingIndicator、VuPagination、VuProgressBar时
references/forms.md使用表单组件:VuInput、VuSelect、VuCombobox、VuCheckbox、VuRadioGroup、VuSlider、VuDatePicker时

Quick reference

快速参考

ts
// Preset & shortcuts
import { presetVunor, vunorShortcuts, defineShortcuts,
         mergeVunorShortcuts, toUnoShortcut } from 'vunor/theme'
import type { TVunorPaletteOptions, TVunorPaletteColor,
              TVunorMainPaletteAdvanced, TVunorLayerPaletteAdvanced,
              TVunorSurfaceConfig, TVunorTheme,
              TVunorShortcut } from 'vunor/theme'

// PI composables (provide/inject)
import { useInputPi, useInputProps, useInputBaseProps,
         useCardPI } from 'vunor'
import { useProvideInject } from 'vunor/utils'

// Vue / Nuxt integration
import { VunorVueResolver } from 'vunor/vite'   // unplugin-vue-components resolver
// nuxt.config: modules: ['vunor/nuxt']

// Components (auto-imported when resolver/module is set up)
// <VuButton>, <VuCard>, <VuCardHeader>, <VuCardInner>, <VuDialog>, <VuTabs>,
// <VuMenu>, <VuMenuItem>, <VuPopover>, <VuAppLayout>, <VuAppToasts>,
// <VuIcon>, <VuLoadingIndicator>, <VuInnerLoading>, <VuLabel>, <VuPagination>,
// <VuProgressBar>, <VuOverflowContainer>, <VuCalendar>,
// <VuInput>, <VuInputBase>, <VuSelect>, <VuCombobox>, <VuCheckbox>,
// <VuRadioGroup>, <VuSlider>, <VuDatePicker>, <VuDevTools>
ts
// 预设与快捷方式
import { presetVunor, vunorShortcuts, defineShortcuts,
         mergeVunorShortcuts, toUnoShortcut } from 'vunor/theme'
import type { TVunorPaletteOptions, TVunorPaletteColor,
              TVunorMainPaletteAdvanced, TVunorLayerPaletteAdvanced,
              TVunorSurfaceConfig, TVunorTheme,
              TVunorShortcut } from 'vunor/theme'

// PI组合式函数(provide/inject)
import { useInputPi, useInputProps, useInputBaseProps,
         useCardPI } from 'vunor'
import { useProvideInject } from 'vunor/utils'

// Vue / Nuxt 集成
import { VunorVueResolver } from 'vunor/vite'   // unplugin-vue-components 解析器
// nuxt.config: modules: ['vunor/nuxt']

// 组件(配置解析器/模块后自动导入)
// <VuButton>, <VuCard>, <VuCardHeader>, <VuCardInner>, <VuDialog>, <VuTabs>,
// <VuMenu>, <VuMenuItem>, <VuPopover>, <VuAppLayout>, <VuAppToasts>,
// <VuIcon>, <VuLoadingIndicator>, <VuInnerLoading>, <VuLabel>, <VuPagination>,
// <VuProgressBar>, <VuOverflowContainer>, <VuCalendar>,
// <VuInput>, <VuInputBase>, <VuSelect>, <VuCombobox>, <VuCheckbox>,
// <VuRadioGroup>, <VuSlider>, <VuDatePicker>, <VuDevTools>

Cheatsheet of semantic classes

语义化类速查表

html
<!-- palette scope -->
<div class="scope-primary | scope-error | scope-good | scope-warn |
            scope-secondary | scope-grey | scope-neutral"></div>

<!-- depth backgrounds (auto light/dark) -->
<div class="layer-0"></div>   <!-- 0 outermost, 4 innermost -->

<!-- colored blocks (semantic background+text+border bundle) -->
<div class="surface-0"></div>             <!-- = layer-0 -->
<div class="surface-50 | surface-100 | … | surface-900"></div>

<!-- direct CSS-var painting -->
<div class="current-bg-scope-color-500 current-text-white">
  <span class="bg-current text-current"></span>
</div>
<div class="bg-scope-color-500/50 text-scope-light-1"></div>

<!-- clickable styles -->
<button class="c8-filled | c8-flat | c8-outlined | c8-light | c8-chrome"></button>
<!-- c8-chrome stays neutral inside any scope (use for Cancel / Select all / None
     buttons sitting next to a scoped primary CTA) -->

<!-- input styles -->
<div class="i8 i8-filled | i8-flat | i8-round"></div>

<!-- card -->
<VuCard level="h3" rounded dense></VuCard>

<!-- spacing & typography -->
<p class="text-h1 text-mb-$m">Title</p>          <!-- font-aware margin -->
<div class="p-$m gap-$s h-fingertip rounded-base"></div>
html
<!-- 调色板scope -->
<div class="scope-primary | scope-error | scope-good | scope-warn |
            scope-secondary | scope-grey | scope-neutral"></div>

<!-- 分层背景(自动适配明暗模式) -->
<div class="layer-0"></div>   <!-- 0为最外层,4为最内层 -->

<!-- 彩色块(语义化背景+文本+边框组合) -->
<div class="surface-0"></div>             <!-- 等同于layer-0 -->
<div class="surface-50 | surface-100 | … | surface-900"></div>

<!-- 直接使用CSS变量着色 -->
<div class="current-bg-scope-color-500 current-text-white">
  <span class="bg-current text-current"></span>
</div>
<div class="bg-scope-color-500/50 text-scope-light-1"></div>

<!-- 可点击样式 -->
<button class="c8-filled | c8-flat | c8-outlined | c8-light | c8-chrome"></button>
<!-- c8-chrome在任何scope内都保持中性(适用于与带scope的主操作按钮相邻的取消/全选/全不选按钮) -->

<!-- 输入样式 -->
<div class="i8 i8-filled | i8-flat | i8-round"></div>

<!-- 卡片 -->
<VuCard level="h3" rounded dense></VuCard>

<!-- 间距与排版 -->
<p class="text-h1 text-mb-$m">标题</p>          <!-- 字体适配边距 -->
<div class="p-$m gap-$s h-fingertip rounded-base"></div>

Ground rules

基本原则

  • Manage color through
    scope-*
    , not hard-coded palette names.
    The page chrome stays neutral by default (
    scope-neutral
    is preflight-installed on
    :root
    ). Apply
    scope-primary
    (or
    scope-secondary
    ) only on accent elements — primary buttons, focused inputs, brand banners. To reflect state, switch to
    scope-error
    for negatives/destructive actions,
    scope-warn
    for warnings,
    scope-good
    for success.
  • Inside components, prefer scope-relative classes over fixed colors. Reach for
    bg-current
    ,
    text-current
    ,
    border-current
    ,
    text-current-hl
    ,
    bg-current/10
    , or
    bg-scope-color-500
    not
    bg-primary-500
    . That way the same component re-tints automatically when its parent scope changes (
    <button class="c8-filled">
    works in any scope;
    <button class="bg-primary-500">
    does not). Use specific palette colors (
    bg-primary-500
    ,
    text-error-700
    ) only when you genuinely need a scope-independent color.
  • Don't write
    <style>
    blocks or scoped styles.
    Compose UnoCSS utilities and Vunor shortcuts. To customize component appearance, override its shortcut via
    vunorShortcuts(myOverrides)
    — see references/shortcuts.md.
  • Layers and surfaces handle dark mode for you — they read
    --scope-light-*
    in light mode and
    --scope-dark-*
    under
    .dark
    /
    prefers-color-scheme: dark
    . Don't add
    dark:
    prefixes to layer/surface utilities.
  • Spacing tokens use a
    $
    prefix
    :
    p-$m
    ,
    gap-$s
    ,
    m-$l
    . Plain
    p-m
    is a different (built-in UnoCSS) utility.
  • Touch targets default to
    --v-fingertip
    (3em).
    Buttons/inputs use
    h-fingertip
    ; override per-subtree with
    fingertip-xs|s|m|l|xl
    .
  • scope-{name}
    only sets variables.
    It paints nothing on its own — combine with
    bg-current
    ,
    layer-*
    ,
    surface-*
    ,
    c8-*
    ,
    i8-*
    , etc.
  • 通过
    scope-*
    管理颜色,而非硬编码调色板名称。
    页面框架默认保持中性(
    scope-neutral
    已通过预飞行样式添加到
    :root
    )。仅在强调元素上应用
    scope-primary
    (或
    scope-secondary
    )——如主按钮、聚焦输入框、品牌横幅。要体现状态,针对负面/破坏性操作切换为
    scope-error
    ,警告切换为
    scope-warn
    ,成功切换为
    scope-good
  • 在组件内部,优先使用与scope关联的类,而非固定颜色。 选择
    bg-current
    text-current
    border-current
    text-current-hl
    bg-current/10
    bg-scope-color-500
    ——而非
    bg-primary-500
    。这样当父级scope变化时,同一组件会自动重新着色(
    <button class="c8-filled">
    可在任意scope中生效;
    <button class="bg-primary-500">
    则不行)。仅在确实需要与scope无关的颜色时,才使用特定调色板颜色(如
    bg-primary-500
    text-error-700
    )。
  • 不要编写
    <style>
    块或作用域样式。
    组合UnoCSS工具类和Vunor快捷方式。如需自定义组件外观,可通过
    vunorShortcuts(myOverrides)
    覆盖其快捷方式——详见references/shortcuts.md
  • Layers和surfaces会自动处理暗色模式——它们在亮色模式下读取
    --scope-light-*
    ,在
    .dark
    /
    prefers-color-scheme: dark
    下读取
    --scope-dark-*
    。无需为layer/surface工具类添加
    dark:
    前缀。
  • 间距令牌使用
    $
    前缀
    p-$m
    gap-$s
    m-$l
    。普通的
    p-m
    是另一个(UnoCSS内置的)工具类。
  • 触摸目标默认大小为
    --v-fingertip
    (3em)。
    按钮/输入框使用
    h-fingertip
    ;可通过
    fingertip-xs|s|m|l|xl
    在子树级别覆盖。
  • scope-{name}
    仅设置变量。
    自身不会产生任何样式——需与
    bg-current
    layer-*
    surface-*
    c8-*
    i8-*
    等类组合使用。