@starweave-lab/unocss-preset 0.0.0-stage → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 zchengfeng
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.en.md ADDED
@@ -0,0 +1,152 @@
1
+ # unocss-preset
2
+
3
+ English | [简体中文](./README.md)
4
+
5
+ A [UnoCSS](https://unocss.dev/) preset with border, text-size and
6
+ `content-visibility` rules, plus layout, element, motion and interaction
7
+ shortcuts.
8
+
9
+ Full documentation lives in [`docs/`](./docs):
10
+
11
+ ```bash
12
+ pnpm docs:dev # start the docs site
13
+ pnpm docs:build # build the docs site
14
+ ```
15
+
16
+ Examples render live on the docs site; previewing the Markdown on GitHub does
17
+ not load UnoCSS styles.
18
+
19
+ ## Features
20
+
21
+ - Border rules: `b-t-primary`, `b-b-dashed-2-red`, `b-x-dotted-3-primary`
22
+ - Text-size rules: `text-xs` through `text-5xl`, emitting both `font-size` and `line-height`
23
+ - `content-visibility`: `cv-auto`, `cv-hidden`, `cv-visible`
24
+ - `hocus:` variant matching both `:hover` and `:focus-visible`
25
+ - Shortcuts: size, element, flex/grid/position, motion and interaction effects
26
+ - Optional `prefix`
27
+
28
+ The docs include a [live demo](./docs/en/guide/demo.md) rendered with the preset.
29
+
30
+ ## Install
31
+
32
+ ```bash
33
+ pnpm add -D unocss @starweave-lab/unocss-preset
34
+ ```
35
+
36
+ Requires Node.js >= 20.19.0 and UnoCSS >= 66.
37
+
38
+ ## Usage
39
+
40
+ ```ts
41
+ // uno.config.ts
42
+ import { defineConfig } from 'unocss'
43
+ import { presetWind4 } from '@unocss/preset-wind4'
44
+ import presetUnocss from '@starweave-lab/unocss-preset'
45
+
46
+ export default defineConfig({
47
+ presets: [presetWind4(), presetUnocss()],
48
+ theme: {
49
+ colors: {
50
+ primary: '#3b82f6',
51
+ },
52
+ },
53
+ })
54
+ ```
55
+
56
+ Register the preset after `presetWind4` so its text sizes override the defaults.
57
+
58
+ ```html
59
+ <div class="p-6 bg-gray-50">
60
+ <div class="flex-between mb-4">
61
+ <h2 class="text-2md">Title</h2>
62
+ <span class="text-xs text-gray-500">Description</span>
63
+ </div>
64
+
65
+ <div class="b-t-2-primary p-3 cv-auto">Content</div>
66
+ </div>
67
+ ```
68
+
69
+ ## Options
70
+
71
+ ```ts
72
+ interface PresetUnocssOptions {
73
+ /** Prefix applied to every utility and shortcut, for example `u-`. */
74
+ prefix?: string
75
+ }
76
+ ```
77
+
78
+ ```ts
79
+ presetUnocss({ prefix: 'u-' })
80
+ ```
81
+
82
+ With a prefix, use `u-b-t-primary` and `hocus:u-cv-auto`.
83
+
84
+ ## Rules
85
+
86
+ ### Border
87
+
88
+ Syntax: `b-<direction>[-<style>][-<width>]-<color>`
89
+
90
+ | Utility | Output |
91
+ | ---------------------- | -------------------------------- |
92
+ | `b-t-primary` | `border-top: 1px solid primary` |
93
+ | `b-t-dashed-primary` | `border-top: 1px dashed primary` |
94
+ | `b-b-dashed-2-red` | `border-bottom: 2px dashed red` |
95
+ | `b-l-2-red` | `border-left: 2px solid red` |
96
+ | `b-x-dotted-3-primary` | left/right, 3px dotted primary |
97
+ | `b-y-double-1-gray` | top/bottom, 1px double gray |
98
+
99
+ ### Text size
100
+
101
+ `text-xs`, `text-sm`, `text-base`, `text-md`, `text-2md`, `text-lg`, `text-xl`,
102
+ `text-2xl`, `text-3xl`, `text-4xl`, `text-5xl`
103
+
104
+ Each utility sets both `font-size` and `line-height`; see the
105
+ [rules docs](./docs/en/guide/rules.md#text-size) for values.
106
+
107
+ ### content-visibility
108
+
109
+ `cv-auto`, `cv-hidden`, `cv-visible`
110
+
111
+ ### hocus variant
112
+
113
+ ```html
114
+ <button class="hocus:cv-auto">Button</button>
115
+ ```
116
+
117
+ ## Shortcuts
118
+
119
+ | Group | Shortcuts |
120
+ | ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
121
+ | Size | `full`, `min-full`, `max-full`, `full-flex` |
122
+ | Element | `button`, `reset-button`, `block-button`, `link`, `img-cover`, `img-contain`, `avatar`, `clickable`, `overlay`, `overlay-full` |
123
+ | Flex | `flex-center`, `flex-x-center`, `flex-y-center`, `flex-between`, `flex-between-center`, `flex-center-between` |
124
+ | Grid | `grid-center`, `grid-x-center`, `grid-y-center`, `grid-stretch`, `grid-between`, `grid-between-center`, `grid-center-between`, `grid-equal-cols` |
125
+ | Position | `absolute-center`, `absolute-x-center`, `absolute-y-center`, `fixed-center`, `fixed-x-center`, `fixed-y-center` |
126
+ | Motion | `motion-press`, `motion-float`, `motion-scale`, `motion-shrink`, `motion-rotate`, `motion-fade` |
127
+ | Interaction | `effect-selected`, `effect-focus`, `effect-interactive`, `effect-outline-ring`, `effect-disabled`, `effect-skeleton`, `effect-shadow`, `effect-border`, `effect-highlight` |
128
+
129
+ Some shortcuts require the `primary` theme color. See the
130
+ [shortcuts docs](./docs/en/guide/shortcuts.md) for expansions.
131
+
132
+ ## Development
133
+
134
+ ```bash
135
+ pnpm install # install dependencies (and Git hooks)
136
+ pnpm dev # watch build
137
+ pnpm test # run tests
138
+ pnpm typecheck # type check
139
+ pnpm lint # lint
140
+ pnpm format # format
141
+ pnpm check # typecheck + lint + format + test + build
142
+ pnpm commit # interactive commit (czg)
143
+ pnpm changelog # generate CHANGELOG.md
144
+ pnpm docs:dev # start the docs site
145
+ pnpm build # bundle with vite-plus (tsdown)
146
+ ```
147
+
148
+ See [CONTRIBUTING.en.md](./CONTRIBUTING.en.md) for the contribution workflow.
149
+
150
+ ## License
151
+
152
+ MIT
package/README.md CHANGED
@@ -1,3 +1,146 @@
1
- # Temporary Holding Version
1
+ # unocss-preset
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ [English](./README.en.md) | 简体中文
4
+
5
+ 一个基于 [UnoCSS](https://unocss.dev/) 的预设,提供边框、字号、`content-visibility` 规则,以及布局、元素、动效与交互反馈快捷方式。
6
+
7
+ 完整文档见 [`docs/`](./docs):
8
+
9
+ ```bash
10
+ pnpm docs:dev # 本地启动文档站
11
+ pnpm docs:build # 构建文档站
12
+ ```
13
+
14
+ 文档中的示例会在文档站内实时渲染;直接在 GitHub 预览 Markdown 不会加载 UnoCSS 样式。
15
+
16
+ ## 特性
17
+
18
+ - 边框规则:`b-t-primary`、`b-b-dashed-2-red`、`b-x-dotted-3-primary`
19
+ - 字号规则:`text-xs` 到 `text-5xl`,同时输出 `font-size` 与 `line-height`
20
+ - `content-visibility`:`cv-auto`、`cv-hidden`、`cv-visible`
21
+ - `hocus:` 变体:同时匹配 `:hover` 与 `:focus-visible`
22
+ - 快捷方式:尺寸、元素、flex/grid/定位、动效与交互反馈
23
+ - 支持 `prefix`
24
+
25
+ 文档包含一个由预设实时渲染的 [示例页面](./docs/guide/demo.md)。
26
+
27
+ ## 安装
28
+
29
+ ```bash
30
+ pnpm add -D unocss @starweave-lab/unocss-preset
31
+ ```
32
+
33
+ 要求 Node.js >= 20.19.0、UnoCSS >= 66。
34
+
35
+ ## 使用
36
+
37
+ ```ts
38
+ // uno.config.ts
39
+ import { defineConfig } from 'unocss'
40
+ import { presetWind4 } from '@unocss/preset-wind4'
41
+ import presetUnocss from '@starweave-lab/unocss-preset'
42
+
43
+ export default defineConfig({
44
+ presets: [presetWind4(), presetUnocss()],
45
+ theme: {
46
+ colors: {
47
+ primary: '#3b82f6',
48
+ },
49
+ },
50
+ })
51
+ ```
52
+
53
+ 预设放在 `presetWind4` 之后,字号规则才能覆盖默认的 `text-*` 尺寸。
54
+
55
+ ```html
56
+ <div class="p-6 bg-gray-50">
57
+ <div class="flex-between mb-4">
58
+ <h2 class="text-2md">标题</h2>
59
+ <span class="text-xs text-gray-500">说明</span>
60
+ </div>
61
+
62
+ <div class="b-t-2-primary p-3 cv-auto">内容</div>
63
+ </div>
64
+ ```
65
+
66
+ ## 配置项
67
+
68
+ ```ts
69
+ interface PresetUnocssOptions {
70
+ /** 为所有工具类和快捷方式添加前缀,例如 `u-` */
71
+ prefix?: string
72
+ }
73
+ ```
74
+
75
+ ```ts
76
+ presetUnocss({ prefix: 'u-' })
77
+ ```
78
+
79
+ 设置前缀后:`u-b-t-primary`、`hocus:u-cv-auto`。
80
+
81
+ ## 规则
82
+
83
+ ### 边框
84
+
85
+ 格式:`b-<方向>[-<样式>][-<宽度>]-<颜色>`
86
+
87
+ | 工具类 | 输出 |
88
+ | ---------------------- | -------------------------------- |
89
+ | `b-t-primary` | `border-top: 1px solid primary` |
90
+ | `b-t-dashed-primary` | `border-top: 1px dashed primary` |
91
+ | `b-b-dashed-2-red` | `border-bottom: 2px dashed red` |
92
+ | `b-l-2-red` | `border-left: 2px solid red` |
93
+ | `b-x-dotted-3-primary` | 左右边框,3px dotted primary |
94
+ | `b-y-double-1-gray` | 上下边框,1px double gray |
95
+
96
+ ### 字号
97
+
98
+ `text-xs`、`text-sm`、`text-base`、`text-md`、`text-2md`、`text-lg`、`text-xl`、`text-2xl`、`text-3xl`、`text-4xl`、`text-5xl`
99
+
100
+ 每个工具类同时设置 `font-size` 与 `line-height`,具体数值见[规则文档](./docs/guide/rules.md#字号)。
101
+
102
+ ### content-visibility
103
+
104
+ `cv-auto`、`cv-hidden`、`cv-visible`
105
+
106
+ ### hocus 变体
107
+
108
+ ```html
109
+ <button class="hocus:cv-auto">按钮</button>
110
+ ```
111
+
112
+ ## 快捷方式
113
+
114
+ | 分类 | 快捷方式 |
115
+ | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
116
+ | 尺寸 | `full`、`min-full`、`max-full`、`full-flex` |
117
+ | 元素 | `button`、`reset-button`、`block-button`、`link`、`img-cover`、`img-contain`、`avatar`、`clickable`、`overlay`、`overlay-full` |
118
+ | Flex | `flex-center`、`flex-x-center`、`flex-y-center`、`flex-between`、`flex-between-center`、`flex-center-between` |
119
+ | Grid | `grid-center`、`grid-x-center`、`grid-y-center`、`grid-stretch`、`grid-between`、`grid-between-center`、`grid-center-between`、`grid-equal-cols` |
120
+ | 定位 | `absolute-center`、`absolute-x-center`、`absolute-y-center`、`fixed-center`、`fixed-x-center`、`fixed-y-center` |
121
+ | 动效 | `motion-press`、`motion-float`、`motion-scale`、`motion-shrink`、`motion-rotate`、`motion-fade` |
122
+ | 交互反馈 | `effect-selected`、`effect-focus`、`effect-interactive`、`effect-outline-ring`、`effect-disabled`、`effect-skeleton`、`effect-shadow`、`effect-border`、`effect-highlight` |
123
+
124
+ 部分快捷方式依赖主题中的 `primary` 颜色。展开细节见[快捷方式文档](./docs/guide/shortcuts.md)。
125
+
126
+ ## 开发
127
+
128
+ ```bash
129
+ pnpm install # 安装依赖(同时安装 Git hooks)
130
+ pnpm dev # 监听构建
131
+ pnpm test # 运行测试
132
+ pnpm typecheck # 类型检查
133
+ pnpm lint # lint
134
+ pnpm format # 格式化
135
+ pnpm check # 类型检查 + lint + 格式 + 测试 + 构建
136
+ pnpm commit # czg 交互式提交
137
+ pnpm changelog # 生成 CHANGELOG.md
138
+ pnpm docs:dev # 启动文档站
139
+ pnpm build # 使用 vite-plus 内置 tsdown 打包
140
+ ```
141
+
142
+ 提交规范与流程见 [CONTRIBUTING.md](./CONTRIBUTING.md)。
143
+
144
+ ## License
145
+
146
+ MIT
@@ -0,0 +1,26 @@
1
+ //#region src/index.d.ts
2
+ /**
3
+ * Options accepted by {@link presetUnocss}.
4
+ */
5
+ export interface PresetUnocssOptions {
6
+ /**
7
+ * Prefix applied to every generated utility, for example `u-`.
8
+ * @default undefined
9
+ */
10
+ prefix?: string;
11
+ }
12
+ /**
13
+ *
14
+ * @example
15
+ * ```ts
16
+ * import presetUnocss from '@starweave-lab/unocss-preset'
17
+ *
18
+ * export default defineConfig({
19
+ * presets: [presetWind4(), presetUnocss()],
20
+ * })
21
+ * ```
22
+ */
23
+ export declare const presetUnocss: import("@unocss/core").PresetFactory<object, PresetUnocssOptions>;
24
+ //#endregion
25
+ export { presetUnocss as default };
26
+ //# sourceMappingURL=index.d.mts.map
package/dist/index.mjs ADDED
@@ -0,0 +1,413 @@
1
+ import { definePreset } from "@unocss/core";
2
+ //#region src/shortcuts/size.ts
3
+ /**
4
+ * Sizing shortcuts for filling a container.
5
+ *
6
+ * - `full` — `w-full h-full`
7
+ * - `min-full` — `min-w-full min-h-full`
8
+ * - `max-full` — `max-w-full max-h-full`
9
+ * - `full-flex` — full width, flexible height (`flex-1`, `min-h-0`)
10
+ */
11
+ const sizes$1 = {
12
+ full: "w-full h-full",
13
+ "min-full": "min-w-full min-h-full",
14
+ "max-full": "max-w-full max-h-full",
15
+ "full-flex": "w-full min-h-0 flex-1"
16
+ };
17
+ //#endregion
18
+ //#region src/shortcuts/element.ts
19
+ const easeOverlay = "ease-[cubic-bezier(0.4,0,0.2,1)]";
20
+ const elements = {
21
+ button: "flex items-center justify-center grow-0 shrink-0 bg-transparent border-none outline-none appearance-none touch-none cursor-pointer select-none focus-visible:outline-none",
22
+ "reset-button": "p-0 m-0 bg-transparent border-none outline-none appearance-none touch-none cursor-pointer select-none focus-visible:outline-none",
23
+ "block-button": "block w-full bg-transparent border-0 border-none outline-none appearance-none box-border cursor-pointer select-none",
24
+ link: "text-primary cursor-pointer hover:underline",
25
+ "img-cover": "block w-full h-full object-cover",
26
+ "img-contain": "block w-full h-full object-contain",
27
+ avatar: "inline-block rounded-full object-cover shrink-0 bg-gray-100",
28
+ clickable: "cursor-pointer select-none",
29
+ overlay: `absolute inset-0 w-full h-full z-50 bg-black/30 backdrop-blur-sm transition-opacity duration-200 ${easeOverlay}`,
30
+ "overlay-full": `fixed inset-0 w-full h-full z-50 bg-black/30 backdrop-blur-sm transition-opacity duration-200 ${easeOverlay}`
31
+ };
32
+ //#endregion
33
+ //#region src/shortcuts/layout.ts
34
+ /**
35
+ * Flex layout shortcuts.
36
+ *
37
+ * - `flex-center` / `flex-x-center` / `flex-y-center`
38
+ * - `flex-between` / `flex-between-center` / `flex-center-between`
39
+ */
40
+ const flexLayouts = {
41
+ "flex-center": "flex justify-center items-center",
42
+ "flex-x-center": "flex justify-center",
43
+ "flex-y-center": "flex items-center",
44
+ "flex-between": "flex justify-between",
45
+ "flex-between-center": "flex justify-between items-center",
46
+ "flex-center-between": "flex justify-center items-stretch"
47
+ };
48
+ /**
49
+ * Grid layout shortcuts, including `grid-equal-cols` which auto-fits
50
+ * equal-width columns.
51
+ */
52
+ const gridLayouts = {
53
+ "grid-center": "grid place-content-center",
54
+ "grid-x-center": "grid justify-center",
55
+ "grid-y-center": "grid items-center",
56
+ "grid-stretch": "grid items-stretch",
57
+ "grid-between": "grid justify-between",
58
+ "grid-between-center": "grid justify-between items-center",
59
+ "grid-center-between": "grid place-content-between",
60
+ "grid-equal-cols": "grid grid-cols-[repeat(auto-fit,minmax(0,1fr))]"
61
+ };
62
+ /**
63
+ * Absolute/fixed centering shortcuts, aligned on both axes or a single axis.
64
+ */
65
+ const positionLayouts = {
66
+ "absolute-center": "absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2",
67
+ "absolute-x-center": "absolute left-1/2 transform -translate-x-1/2",
68
+ "absolute-y-center": "absolute top-1/2 transform -translate-y-1/2",
69
+ "fixed-center": "fixed top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2",
70
+ "fixed-x-center": "fixed left-1/2 transform -translate-x-1/2",
71
+ "fixed-y-center": "fixed top-1/2 transform -translate-y-1/2"
72
+ };
73
+ const layouts = {
74
+ ...flexLayouts,
75
+ ...gridLayouts,
76
+ ...positionLayouts
77
+ };
78
+ //#endregion
79
+ //#region src/shortcuts/motion.ts
80
+ const easeMotion = "ease-[cubic-bezier(0.34,1.56,0.64,1)]";
81
+ const motions = {
82
+ "motion-press": `transition-transform duration-300 ${easeMotion} active:scale-[0.96] motion-reduce:transition-none`,
83
+ "motion-float": `transition-transform duration-200 ${easeMotion} hover:-translate-y-1`,
84
+ "motion-scale": `transition-transform duration-200 ${easeMotion} hover:scale-[1.02]`,
85
+ "motion-shrink": `transition-transform duration-200 ${easeMotion} hover:scale-[0.98]`,
86
+ "motion-rotate": `transition-transform duration-200 ${easeMotion} hover:rotate-3`,
87
+ "motion-fade": `transition-opacity duration-200 ${easeMotion} hover:opacity-70`
88
+ };
89
+ //#endregion
90
+ //#region src/shortcuts/effect.ts
91
+ const easeEffect = "ease-out";
92
+ const effects = {
93
+ "effect-selected": "bg-primary/10 text-primary",
94
+ "effect-focus": `transition duration-200 ${easeEffect} focus-within:border-primary focus-within:ring-2 focus-within:ring-primary/20`,
95
+ "effect-interactive": `cursor-pointer transition-colors duration-200 ${easeEffect} hover:bg-gray-100 active:bg-gray-200`,
96
+ "effect-outline-ring": `transition duration-200 ${easeEffect} outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2`,
97
+ "effect-disabled": "disabled:cursor-not-allowed disabled:opacity-50",
98
+ "effect-skeleton": "animate-pulse rounded-md bg-gray-200 dark:bg-gray-800",
99
+ "effect-shadow": `transition-shadow duration-200 ${easeEffect} hover:shadow-md`,
100
+ "effect-border": `transition-colors duration-200 ${easeEffect} hover:border-primary`,
101
+ "effect-highlight": `transition-colors duration-200 ${easeEffect} hover:bg-primary/10 hover:border-primary`
102
+ };
103
+ //#endregion
104
+ //#region src/shortcuts/index.ts
105
+ const shortcuts = {
106
+ ...sizes$1,
107
+ ...elements,
108
+ ...layouts,
109
+ ...motions,
110
+ ...effects
111
+ };
112
+ //#endregion
113
+ //#region src/rules/text.ts
114
+ const textSizes = {
115
+ xs: {
116
+ "font-size": "12px",
117
+ "line-height": "14px"
118
+ },
119
+ sm: {
120
+ "font-size": "14px",
121
+ "line-height": "18px"
122
+ },
123
+ base: {
124
+ "font-size": "16px",
125
+ "line-height": "24px"
126
+ },
127
+ md: {
128
+ "font-size": "18px",
129
+ "line-height": "28px"
130
+ },
131
+ "2md": {
132
+ "font-size": "20px",
133
+ "line-height": "32px"
134
+ },
135
+ lg: {
136
+ "font-size": "24px",
137
+ "line-height": "34px"
138
+ },
139
+ xl: {
140
+ "font-size": "28px",
141
+ "line-height": "40px"
142
+ },
143
+ "2xl": {
144
+ "font-size": "36px",
145
+ "line-height": "42px"
146
+ },
147
+ "3xl": {
148
+ "font-size": "48px",
149
+ "line-height": "56px"
150
+ },
151
+ "4xl": {
152
+ "font-size": "60px",
153
+ "line-height": "72px"
154
+ },
155
+ "5xl": {
156
+ "font-size": "72px",
157
+ "line-height": "80px"
158
+ }
159
+ };
160
+ const textSizePattern = Object.keys(textSizes).join("|");
161
+ const textSizeRules = [[new RegExp(`^text-(${textSizePattern})$`), ([, key]) => textSizes[key]]];
162
+ //#endregion
163
+ //#region src/rules/border.ts
164
+ const borderDirections = {
165
+ t: ["top"],
166
+ r: ["right"],
167
+ b: ["bottom"],
168
+ l: ["left"],
169
+ x: ["left", "right"],
170
+ y: ["top", "bottom"],
171
+ all: [
172
+ "top",
173
+ "right",
174
+ "bottom",
175
+ "left"
176
+ ]
177
+ };
178
+ /**
179
+ * Resolves a theme color path such as `primary`, `blue-500` or `light-blue-500`.
180
+ * Longer keys win so theme entries containing hyphens still work.
181
+ */
182
+ function lookupColor(colors, parts) {
183
+ if (!colors || typeof colors !== "object") return;
184
+ for (let size = parts.length; size > 0; size--) {
185
+ const key = parts.slice(0, size).join("-");
186
+ if (!(key in colors)) continue;
187
+ const value = colors[key];
188
+ if (size === parts.length) {
189
+ if (typeof value === "string") return value;
190
+ continue;
191
+ }
192
+ const nested = lookupColor(value, parts.slice(size));
193
+ if (nested) return nested;
194
+ }
195
+ }
196
+ function isColorFunction(value) {
197
+ return /^(?:rgba?|hsla?|hwb|lab|lch|oklab|oklch|color|var)\(/i.test(value);
198
+ }
199
+ const namedColors = new Set(`aliceblue antiquewhite aqua aquamarine azure beige bisque black blanchedalmond blue
200
+ blueviolet brown burlywood cadetblue chartreuse chocolate coral cornflowerblue cornsilk
201
+ crimson cyan darkblue darkcyan darkgoldenrod darkgray darkgreen darkgrey darkkhaki
202
+ darkmagenta darkolivegreen darkorange darkorchid darkred darksalmon darkseagreen
203
+ darkslateblue darkslategray darkslategrey darkturquoise darkviolet deeppink deepskyblue
204
+ dimgray dimgrey dodgerblue firebrick floralwhite forestgreen fuchsia gainsboro ghostwhite
205
+ gold goldenrod gray green greenyellow grey honeydew hotpink indianred indigo ivory khaki
206
+ lavender lavenderblush lawngreen lemonchiffon lightblue lightcoral lightcyan
207
+ lightgoldenrodyellow lightgray lightgreen lightgrey lightpink lightsalmon lightseagreen
208
+ lightskyblue lightslategray lightslategrey lightsteelblue lightyellow lime limegreen linen
209
+ magenta maroon mediumaquamarine mediumblue mediumorchid mediumpurple mediumseagreen
210
+ mediumslateblue mediumspringgreen mediumturquoise mediumvioletred midnightblue mintcream
211
+ mistyrose moccasin navajowhite navy oldlace olive olivedrab orange orangered orchid
212
+ palegoldenrod palegreen paleturquoise palevioletred papayawhip peachpuff peru pink plum
213
+ powderblue purple rebeccapurple red rosybrown royalblue saddlebrown salmon sandybrown
214
+ seagreen seashell sienna silver skyblue slateblue slategray slategrey snow springgreen
215
+ steelblue tan teal thistle tomato transparent turquoise violet wheat white whitesmoke
216
+ yellow yellowgreen currentcolor`.split(/\s+/));
217
+ /**
218
+ * Splits the opacity modifier from the color at a `/` outside any brackets or
219
+ * parentheses, so arbitrary values such as `[rgb(1_2_3_/_50%)]` stay intact.
220
+ */
221
+ function splitOpacity(input) {
222
+ let depth = 0;
223
+ let slash = -1;
224
+ for (let i = 0; i < input.length; i++) {
225
+ const char = input[i];
226
+ if (char === "[" || char === "(") depth++;
227
+ else if (char === "]" || char === ")") depth--;
228
+ else if (char === "/" && depth === 0) slash = i;
229
+ }
230
+ if (slash < 0) return [input, void 0];
231
+ return [input.slice(0, slash), input.slice(slash + 1)];
232
+ }
233
+ function toPercentage(value) {
234
+ const raw = value.startsWith("[") && value.endsWith("]") ? value.slice(1, -1) : value;
235
+ if (/^(?:\d+(?:\.\d+)?|\.\d+)%$/.test(raw)) return raw;
236
+ if (!raw) return;
237
+ const number = Number(raw);
238
+ if (!Number.isFinite(number) || number < 0) return;
239
+ return `${raw.includes(".") && number <= 1 ? number * 100 : number}%`;
240
+ }
241
+ /**
242
+ * Resolves theme colors, opacity modifiers and arbitrary values.
243
+ *
244
+ * - `primary` → theme color
245
+ * - `blue-500` → nested theme color
246
+ * - `primary/50` → `color-mix(...)`
247
+ * - `[#fff]` → `#fff`
248
+ * - `red` / `rgb(1,2,3)` → raw CSS colors
249
+ */
250
+ function resolveColor(input, colors) {
251
+ const [raw, opacity] = splitOpacity(input);
252
+ let resolved;
253
+ if (raw.startsWith("[") && raw.endsWith("]")) {
254
+ const inner = raw.slice(1, -1);
255
+ resolved = lookupColor(colors, inner.split("-")) ?? inner.replaceAll("_", " ");
256
+ } else resolved = lookupColor(colors, raw.split("-")) ?? (namedColors.has(raw.toLowerCase()) || isColorFunction(raw) ? raw : void 0);
257
+ if (!resolved) return;
258
+ if (opacity === void 0) return resolved;
259
+ const percent = toPercentage(opacity);
260
+ if (!percent) return;
261
+ return `color-mix(in srgb, ${resolved} ${percent}, transparent)`;
262
+ }
263
+ /**
264
+ * 边框规则,格式为: b-边框方向(不写默认生成所有方向)-边框样式(不写默认为solid)-边框长度(必须写)-边框颜色, 例子:
265
+ *
266
+ * @examples
267
+ *
268
+ * b-1-primary → 所有方向 1px solid primary
269
+ * b-all-1-primary → 所有方向 1px solid primary
270
+ * b-t-1-primary → border-top: 1px solid primary
271
+ * b-t-dashed-1-primary → border-top: 1px dashed primary
272
+ * b-b-dashed-2-red → border-bottom: 2px dashed red
273
+ * b-1-primary/50 → 所有方向 1px solid color-mix(primary 50%, transparent)
274
+ * b-t-1-[#fff] → border-top: 1px solid #fff
275
+ */
276
+ const borderRules = [[/^b-(?:(all|[trblxy])-)?(?:(solid|dashed|dotted|double)-)?(\d+)-(.+)$/, ([, direction, style, width, color], { theme }) => {
277
+ if (!width || !color) return;
278
+ const properties = borderDirections[direction ?? "all"];
279
+ const resolvedColor = resolveColor(color, theme.colors);
280
+ if (!resolvedColor) return;
281
+ const declarations = {};
282
+ for (const s of properties) {
283
+ declarations[`border-${s}-width`] = `${width}px`;
284
+ declarations[`border-${s}-style`] = style ?? "solid";
285
+ declarations[`border-${s}-color`] = resolvedColor;
286
+ }
287
+ return declarations;
288
+ }]];
289
+ const contentVisibilityRules = [
290
+ "auto",
291
+ "hidden",
292
+ "visible"
293
+ ].map((value) => [`cv-${value}`, { "content-visibility": value }]);
294
+ //#endregion
295
+ //#region src/rules/shadow.ts
296
+ const directions = [
297
+ "t",
298
+ "r",
299
+ "b",
300
+ "l",
301
+ "all"
302
+ ];
303
+ const sizes = {
304
+ sm: {
305
+ offset: 1,
306
+ blur: 2,
307
+ spread: 0,
308
+ color: "rgba(0, 0, 0, 0.05)"
309
+ },
310
+ "": {
311
+ offset: 1,
312
+ blur: 3,
313
+ spread: 0,
314
+ color: "rgba(0, 0, 0, 0.1)"
315
+ },
316
+ md: {
317
+ offset: 4,
318
+ blur: 6,
319
+ spread: -1,
320
+ color: "rgba(0, 0, 0, 0.1)"
321
+ },
322
+ lg: {
323
+ offset: 10,
324
+ blur: 15,
325
+ spread: -3,
326
+ color: "rgba(0, 0, 0, 0.1)"
327
+ },
328
+ xl: {
329
+ offset: 20,
330
+ blur: 25,
331
+ spread: -5,
332
+ color: "rgba(0, 0, 0, 0.1)"
333
+ },
334
+ "2xl": {
335
+ offset: 25,
336
+ blur: 50,
337
+ spread: -12,
338
+ color: "rgba(0, 0, 0, 0.25)"
339
+ }
340
+ };
341
+ function generateBoxShadows() {
342
+ return directions.reduce((acc, direction) => {
343
+ Object.keys(sizes).forEach((sizeKey) => {
344
+ const size = sizes[sizeKey];
345
+ const key = `${direction}${sizeKey ? "-" + sizeKey : sizeKey}`;
346
+ let boxShadow;
347
+ if (direction === "all") boxShadow = `0px 0px ${size.blur}px ${size.spread}px ${size.color}`;
348
+ else boxShadow = `${["l", "r"].includes(direction) ? direction === "r" ? size.offset : -size.offset : 0}px ${["t", "b"].includes(direction) ? direction === "b" ? size.offset : -size.offset : 0}px ${size.blur}px ${size.spread}px ${size.color}`;
349
+ acc[key] = { "box-shadow": boxShadow };
350
+ });
351
+ return acc;
352
+ }, {});
353
+ }
354
+ const shadows = generateBoxShadows();
355
+ /**
356
+ * 规则格式: shadow-[方向][-尺寸]
357
+ *
358
+ * - 方向: `t` / `r` / `b` / `l` / `all`,不写时等同于 `all`
359
+ * - 尺寸: `sm` / `md` / `lg` / `xl` / `2xl`,不写时为默认尺寸
360
+ */
361
+ const shadowRules = [[/^shadow-(?:([trbl]|all)(?:-(sm|md|lg|xl|2xl))?|(sm|md|lg|xl|2xl))$/, ([, direction, size, sizeOnly]) => {
362
+ const resolvedSize = size ?? sizeOnly;
363
+ const key = `${direction ?? "all"}${resolvedSize ? `-${resolvedSize}` : ""}`;
364
+ return shadows[key];
365
+ }]];
366
+ //#endregion
367
+ //#region src/rules/index.ts
368
+ const rules = [
369
+ ...borderRules,
370
+ ...textSizeRules,
371
+ ...contentVisibilityRules,
372
+ ...shadowRules
373
+ ];
374
+ //#endregion
375
+ //#region src/index.ts
376
+ /**
377
+ * `hocus:` variant matching both `:hover` and `:focus-visible`.
378
+ */
379
+ const hocusVariant = {
380
+ name: "hocus",
381
+ match: (matcher) => {
382
+ if (!matcher.startsWith("hocus:")) return;
383
+ return {
384
+ matcher: matcher.slice(6),
385
+ selector: (selector) => `${selector}:hover, ${selector}:focus-visible`
386
+ };
387
+ }
388
+ };
389
+ /**
390
+ *
391
+ * @example
392
+ * ```ts
393
+ * import presetUnocss from '@starweave-lab/unocss-preset'
394
+ *
395
+ * export default defineConfig({
396
+ * presets: [presetWind4(), presetUnocss()],
397
+ * })
398
+ * ```
399
+ */
400
+ const presetUnocss = definePreset((options = {}) => {
401
+ const { prefix } = options;
402
+ return {
403
+ name: "unocss-preset",
404
+ rules,
405
+ variants: [hocusVariant],
406
+ shortcuts,
407
+ prefix
408
+ };
409
+ });
410
+ //#endregion
411
+ export { presetUnocss as default, presetUnocss };
412
+
413
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":["sizes","sizes"],"sources":["../src/shortcuts/size.ts","../src/shortcuts/element.ts","../src/shortcuts/layout.ts","../src/shortcuts/motion.ts","../src/shortcuts/effect.ts","../src/shortcuts/index.ts","../src/rules/text.ts","../src/rules/border.ts","../src/rules/content-visibility.ts","../src/rules/shadow.ts","../src/rules/index.ts","../src/index.ts"],"sourcesContent":["/**\n * Sizing shortcuts for filling a container.\n *\n * - `full` — `w-full h-full`\n * - `min-full` — `min-w-full min-h-full`\n * - `max-full` — `max-w-full max-h-full`\n * - `full-flex` — full width, flexible height (`flex-1`, `min-h-0`)\n */\nexport const sizes = {\n full: 'w-full h-full',\n 'min-full': 'min-w-full min-h-full',\n 'max-full': 'max-w-full max-h-full',\n 'full-flex': 'w-full min-h-0 flex-1',\n}\n","// Overlays only fade, so a standard easing curve fits better than a spring.\nconst easeOverlay = 'ease-[cubic-bezier(0.4,0,0.2,1)]'\nexport const elements = {\n // button\n button:\n 'flex items-center justify-center grow-0 shrink-0 bg-transparent border-none outline-none appearance-none touch-none cursor-pointer select-none focus-visible:outline-none',\n 'reset-button':\n 'p-0 m-0 bg-transparent border-none outline-none appearance-none touch-none cursor-pointer select-none focus-visible:outline-none',\n 'block-button':\n 'block w-full bg-transparent border-0 border-none outline-none appearance-none box-border cursor-pointer select-none',\n\n link: 'text-primary cursor-pointer hover:underline',\n 'img-cover': 'block w-full h-full object-cover',\n 'img-contain': 'block w-full h-full object-contain',\n avatar: 'inline-block rounded-full object-cover shrink-0 bg-gray-100',\n clickable: 'cursor-pointer select-none',\n\n // 悬浮层\n overlay: `absolute inset-0 w-full h-full z-50 bg-black/30 backdrop-blur-sm transition-opacity duration-200 ${easeOverlay}`,\n 'overlay-full': `fixed inset-0 w-full h-full z-50 bg-black/30 backdrop-blur-sm transition-opacity duration-200 ${easeOverlay}`,\n}\n","/**\n * Flex layout shortcuts.\n *\n * - `flex-center` / `flex-x-center` / `flex-y-center`\n * - `flex-between` / `flex-between-center` / `flex-center-between`\n */\nconst flexLayouts = {\n 'flex-center': 'flex justify-center items-center',\n 'flex-x-center': 'flex justify-center',\n 'flex-y-center': 'flex items-center',\n 'flex-between': 'flex justify-between',\n 'flex-between-center': 'flex justify-between items-center',\n 'flex-center-between': 'flex justify-center items-stretch',\n}\n\n/**\n * Grid layout shortcuts, including `grid-equal-cols` which auto-fits\n * equal-width columns.\n */\nconst gridLayouts = {\n 'grid-center': 'grid place-content-center',\n 'grid-x-center': 'grid justify-center',\n 'grid-y-center': 'grid items-center',\n 'grid-stretch': 'grid items-stretch',\n 'grid-between': 'grid justify-between',\n 'grid-between-center': 'grid justify-between items-center',\n 'grid-center-between': 'grid place-content-between',\n 'grid-equal-cols': 'grid grid-cols-[repeat(auto-fit,minmax(0,1fr))]',\n}\n\n/**\n * Absolute/fixed centering shortcuts, aligned on both axes or a single axis.\n */\nconst positionLayouts = {\n 'absolute-center': 'absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2',\n 'absolute-x-center': 'absolute left-1/2 transform -translate-x-1/2',\n 'absolute-y-center': 'absolute top-1/2 transform -translate-y-1/2',\n 'fixed-center': 'fixed top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2',\n 'fixed-x-center': 'fixed left-1/2 transform -translate-x-1/2',\n 'fixed-y-center': 'fixed top-1/2 transform -translate-y-1/2',\n}\n\nexport const layouts = {\n ...flexLayouts,\n ...gridLayouts,\n ...positionLayouts,\n}\n","// Spring easing gives movement shortcuts their bounce.\nconst easeMotion = 'ease-[cubic-bezier(0.34,1.56,0.64,1)]'\n\nexport const motions = {\n // 按下弹簧效果\n 'motion-press': `transition-transform duration-300 ${easeMotion} active:scale-[0.96] motion-reduce:transition-none`,\n // 悬浮浮起\n 'motion-float': `transition-transform duration-200 ${easeMotion} hover:-translate-y-1`,\n // 悬浮放大\n 'motion-scale': `transition-transform duration-200 ${easeMotion} hover:scale-[1.02]`,\n // 悬浮缩小\n 'motion-shrink': `transition-transform duration-200 ${easeMotion} hover:scale-[0.98]`,\n // 悬浮旋转\n 'motion-rotate': `transition-transform duration-200 ${easeMotion} hover:rotate-3`,\n // 悬浮透明度变化\n 'motion-fade': `transition-opacity duration-200 ${easeMotion} hover:opacity-70`,\n}\n","// Effects are subtle feedback, so use a simple ease-out instead of a spring.\nconst easeEffect = 'ease-out'\n\nexport const effects = {\n // 选中状态\n 'effect-selected': 'bg-primary/10 text-primary',\n // 输入框聚焦反馈\n 'effect-focus': `transition duration-200 ${easeEffect} focus-within:border-primary focus-within:ring-2 focus-within:ring-primary/20`,\n // 通用交互反馈\n 'effect-interactive': `cursor-pointer transition-colors duration-200 ${easeEffect} hover:bg-gray-100 active:bg-gray-200`,\n // 聚焦轮廓\n 'effect-outline-ring': `transition duration-200 ${easeEffect} outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2`,\n // 禁用状态\n 'effect-disabled': 'disabled:cursor-not-allowed disabled:opacity-50',\n // 骨架屏基础样式\n 'effect-skeleton': 'animate-pulse rounded-md bg-gray-200 dark:bg-gray-800',\n // 悬浮阴影\n 'effect-shadow': `transition-shadow duration-200 ${easeEffect} hover:shadow-md`,\n // 悬浮边框高亮\n 'effect-border': `transition-colors duration-200 ${easeEffect} hover:border-primary`,\n // 悬浮高亮\n 'effect-highlight': `transition-colors duration-200 ${easeEffect} hover:bg-primary/10 hover:border-primary`,\n}\n","import { sizes } from './size'\nimport { elements } from './element'\nimport { layouts } from './layout'\nimport { motions } from './motion'\nimport { effects } from './effect'\n\nexport const shortcuts = {\n ...sizes,\n ...elements,\n ...layouts,\n ...motions,\n ...effects,\n}\n","import type { Rule } from '@unocss/core'\n\nconst textSizes = {\n xs: {\n 'font-size': '12px',\n 'line-height': '14px',\n },\n sm: {\n 'font-size': '14px',\n 'line-height': '18px',\n },\n base: {\n 'font-size': '16px',\n 'line-height': '24px',\n },\n md: {\n 'font-size': '18px',\n 'line-height': '28px',\n },\n '2md': {\n 'font-size': '20px',\n 'line-height': '32px',\n },\n lg: {\n 'font-size': '24px',\n 'line-height': '34px',\n },\n xl: {\n 'font-size': '28px',\n 'line-height': '40px',\n },\n '2xl': {\n 'font-size': '36px',\n 'line-height': '42px',\n },\n '3xl': {\n 'font-size': '48px',\n 'line-height': '56px',\n },\n '4xl': {\n 'font-size': '60px',\n 'line-height': '72px',\n },\n '5xl': {\n 'font-size': '72px',\n 'line-height': '80px',\n },\n} as const\nexport type TextSizeKeys = keyof typeof textSizes\nconst textSizePattern = Object.keys(textSizes).join('|')\n\nexport const textSizeRules: Rule[] = [\n [new RegExp(`^text-(${textSizePattern})$`), ([, key]) => textSizes[key as TextSizeKeys]],\n]\n","import type { Rule } from '@unocss/core'\n\ntype BorderDirection = 't' | 'r' | 'b' | 'l' | 'x' | 'y' | 'all'\n\nconst borderDirections: Record<BorderDirection, string[]> = {\n t: ['top'],\n r: ['right'],\n b: ['bottom'],\n l: ['left'],\n x: ['left', 'right'],\n y: ['top', 'bottom'],\n all: ['top', 'right', 'bottom', 'left'],\n}\n\n/**\n * Resolves a theme color path such as `primary`, `blue-500` or `light-blue-500`.\n * Longer keys win so theme entries containing hyphens still work.\n */\nfunction lookupColor(colors: unknown, parts: string[]): string | undefined {\n if (!colors || typeof colors !== 'object') return\n for (let size = parts.length; size > 0; size--) {\n const key = parts.slice(0, size).join('-')\n if (!(key in colors)) continue\n const value = (colors as Record<string, unknown>)[key]\n if (size === parts.length) {\n if (typeof value === 'string') return value\n continue\n }\n const nested = lookupColor(value, parts.slice(size))\n if (nested) return nested\n }\n}\n\nfunction isColorFunction(value: string): boolean {\n return /^(?:rgba?|hsla?|hwb|lab|lch|oklab|oklch|color|var)\\(/i.test(value)\n}\n\nconst namedColors = new Set(\n `aliceblue antiquewhite aqua aquamarine azure beige bisque black blanchedalmond blue\n blueviolet brown burlywood cadetblue chartreuse chocolate coral cornflowerblue cornsilk\n crimson cyan darkblue darkcyan darkgoldenrod darkgray darkgreen darkgrey darkkhaki\n darkmagenta darkolivegreen darkorange darkorchid darkred darksalmon darkseagreen\n darkslateblue darkslategray darkslategrey darkturquoise darkviolet deeppink deepskyblue\n dimgray dimgrey dodgerblue firebrick floralwhite forestgreen fuchsia gainsboro ghostwhite\n gold goldenrod gray green greenyellow grey honeydew hotpink indianred indigo ivory khaki\n lavender lavenderblush lawngreen lemonchiffon lightblue lightcoral lightcyan\n lightgoldenrodyellow lightgray lightgreen lightgrey lightpink lightsalmon lightseagreen\n lightskyblue lightslategray lightslategrey lightsteelblue lightyellow lime limegreen linen\n magenta maroon mediumaquamarine mediumblue mediumorchid mediumpurple mediumseagreen\n mediumslateblue mediumspringgreen mediumturquoise mediumvioletred midnightblue mintcream\n mistyrose moccasin navajowhite navy oldlace olive olivedrab orange orangered orchid\n palegoldenrod palegreen paleturquoise palevioletred papayawhip peachpuff peru pink plum\n powderblue purple rebeccapurple red rosybrown royalblue saddlebrown salmon sandybrown\n seagreen seashell sienna silver skyblue slateblue slategray slategrey snow springgreen\n steelblue tan teal thistle tomato transparent turquoise violet wheat white whitesmoke\n yellow yellowgreen currentcolor`.split(/\\s+/),\n)\n\n/**\n * Splits the opacity modifier from the color at a `/` outside any brackets or\n * parentheses, so arbitrary values such as `[rgb(1_2_3_/_50%)]` stay intact.\n */\nfunction splitOpacity(input: string): [string, string | undefined] {\n let depth = 0\n let slash = -1\n\n for (let i = 0; i < input.length; i++) {\n const char = input[i]\n if (char === '[' || char === '(') depth++\n else if (char === ']' || char === ')') depth--\n else if (char === '/' && depth === 0) slash = i\n }\n\n if (slash < 0) return [input, undefined]\n return [input.slice(0, slash), input.slice(slash + 1)]\n}\n\nfunction toPercentage(value: string): string | undefined {\n const raw = value.startsWith('[') && value.endsWith(']') ? value.slice(1, -1) : value\n if (/^(?:\\d+(?:\\.\\d+)?|\\.\\d+)%$/.test(raw)) return raw\n if (!raw) return\n\n const number = Number(raw)\n if (!Number.isFinite(number) || number < 0) return\n\n const percent = raw.includes('.') && number <= 1 ? number * 100 : number\n return `${percent}%`\n}\n\n/**\n * Resolves theme colors, opacity modifiers and arbitrary values.\n *\n * - `primary` → theme color\n * - `blue-500` → nested theme color\n * - `primary/50` → `color-mix(...)`\n * - `[#fff]` → `#fff`\n * - `red` / `rgb(1,2,3)` → raw CSS colors\n */\nfunction resolveColor(input: string, colors: unknown): string | undefined {\n const [raw, opacity] = splitOpacity(input)\n\n let resolved: string | undefined\n if (raw.startsWith('[') && raw.endsWith(']')) {\n const inner = raw.slice(1, -1)\n // Arbitrary values still resolve theme colors, e.g. `[primary]` → `#3b82f6`.\n resolved = lookupColor(colors, inner.split('-')) ?? inner.replaceAll('_', ' ')\n } else {\n resolved =\n lookupColor(colors, raw.split('-')) ??\n (namedColors.has(raw.toLowerCase()) || isColorFunction(raw) ? raw : undefined)\n }\n\n if (!resolved) return\n if (opacity === undefined) return resolved\n\n const percent = toPercentage(opacity)\n if (!percent) return\n\n return `color-mix(in srgb, ${resolved} ${percent}, transparent)`\n}\n\n/**\n * 边框规则,格式为: b-边框方向(不写默认生成所有方向)-边框样式(不写默认为solid)-边框长度(必须写)-边框颜色, 例子:\n *\n * @examples\n *\n * b-1-primary → 所有方向 1px solid primary\n * b-all-1-primary → 所有方向 1px solid primary\n * b-t-1-primary → border-top: 1px solid primary\n * b-t-dashed-1-primary → border-top: 1px dashed primary\n * b-b-dashed-2-red → border-bottom: 2px dashed red\n * b-1-primary/50 → 所有方向 1px solid color-mix(primary 50%, transparent)\n * b-t-1-[#fff] → border-top: 1px solid #fff\n */\nexport const borderRules: Rule[] = [\n [\n /^b-(?:(all|[trblxy])-)?(?:(solid|dashed|dotted|double)-)?(\\d+)-(.+)$/,\n ([, direction, style, width, color], { theme }) => {\n if (!width || !color) return\n const properties = borderDirections[(direction ?? 'all') as BorderDirection]\n const resolvedColor = resolveColor(color, (theme as { colors?: unknown }).colors)\n if (!resolvedColor) return\n\n const declarations: Record<string, string> = {}\n for (const s of properties) {\n declarations[`border-${s}-width`] = `${width}px`\n declarations[`border-${s}-style`] = style ?? 'solid'\n declarations[`border-${s}-color`] = resolvedColor\n }\n return declarations\n },\n ],\n]\n","import type { Rule } from '@unocss/core'\nconst values = ['auto', 'hidden', 'visible'] as const\n\nexport const contentVisibilityRules: Rule[] = values.map((value) => [\n `cv-${value}`,\n { 'content-visibility': value },\n])\n","import type { Rule } from '@unocss/core'\n\ntype Direction = 't' | 'r' | 'b' | 'l' | 'all'\ntype SizeKey = 'sm' | 'md' | '' | 'lg' | 'xl' | '2xl'\n\ninterface Size {\n offset: number\n blur: number\n spread: number\n color: string\n}\ntype BoxShadowStyles = Record<string, { 'box-shadow': string }>\n\nconst directions: Direction[] = ['t', 'r', 'b', 'l', 'all']\nconst sizes: Record<SizeKey, Size> = {\n sm: { offset: 1, blur: 2, spread: 0, color: 'rgba(0, 0, 0, 0.05)' },\n '': { offset: 1, blur: 3, spread: 0, color: 'rgba(0, 0, 0, 0.1)' },\n md: { offset: 4, blur: 6, spread: -1, color: 'rgba(0, 0, 0, 0.1)' },\n lg: { offset: 10, blur: 15, spread: -3, color: 'rgba(0, 0, 0, 0.1)' },\n xl: { offset: 20, blur: 25, spread: -5, color: 'rgba(0, 0, 0, 0.1)' },\n '2xl': { offset: 25, blur: 50, spread: -12, color: 'rgba(0, 0, 0, 0.25)' },\n}\n\nfunction generateBoxShadows() {\n return directions.reduce((acc: BoxShadowStyles, direction: Direction) => {\n Object.keys(sizes).forEach((sizeKey: string) => {\n const size = sizes[sizeKey as SizeKey]\n const key = `${direction}${sizeKey ? '-' + sizeKey : sizeKey}`\n let boxShadow: string\n\n if (direction === 'all') {\n boxShadow = `0px 0px ${size.blur}px ${size.spread}px ${size.color}`\n } else {\n const horizontalOffset = ['l', 'r'].includes(direction)\n ? direction === 'r'\n ? size.offset\n : -size.offset\n : 0\n const verticalOffset = ['t', 'b'].includes(direction)\n ? direction === 'b'\n ? size.offset\n : -size.offset\n : 0\n boxShadow = `${horizontalOffset}px ${verticalOffset}px ${size.blur}px ${size.spread}px ${size.color}`\n }\n acc[key] = { 'box-shadow': boxShadow }\n })\n return acc\n }, {} as BoxShadowStyles)\n}\n\nconst shadows = generateBoxShadows()\n\n/**\n * 规则格式: shadow-[方向][-尺寸]\n *\n * - 方向: `t` / `r` / `b` / `l` / `all`,不写时等同于 `all`\n * - 尺寸: `sm` / `md` / `lg` / `xl` / `2xl`,不写时为默认尺寸\n */\nexport const shadowRules: Rule[] = [\n [\n /^shadow-(?:([trbl]|all)(?:-(sm|md|lg|xl|2xl))?|(sm|md|lg|xl|2xl))$/,\n ([, direction, size, sizeOnly]) => {\n const resolvedSize = size ?? sizeOnly\n const key = `${direction ?? 'all'}${resolvedSize ? `-${resolvedSize}` : ''}`\n return shadows[key]\n },\n ],\n]\n","import { textSizeRules } from './text'\nimport { borderRules } from './border'\nimport { contentVisibilityRules } from './content-visibility'\nimport { shadowRules } from './shadow'\n\nexport const rules = [...borderRules, ...textSizeRules, ...contentVisibilityRules, ...shadowRules]\n","import type { Preset, Variant } from '@unocss/core'\nimport { definePreset } from '@unocss/core'\nimport { shortcuts } from './shortcuts'\nimport { rules } from './rules'\n\n/**\n * Options accepted by {@link presetUnocss}.\n */\nexport interface PresetUnocssOptions {\n /**\n * Prefix applied to every generated utility, for example `u-`.\n * @default undefined\n */\n prefix?: string\n}\n\n/**\n * `hocus:` variant matching both `:hover` and `:focus-visible`.\n */\nconst hocusVariant: Variant = {\n name: 'hocus',\n match: (matcher) => {\n if (!matcher.startsWith('hocus:')) return\n return {\n matcher: matcher.slice('hocus:'.length),\n selector: (selector) => `${selector}:hover, ${selector}:focus-visible`,\n }\n },\n}\n\n/**\n *\n * @example\n * ```ts\n * import presetUnocss from '@starweave-lab/unocss-preset'\n *\n * export default defineConfig({\n * presets: [presetWind4(), presetUnocss()],\n * })\n * ```\n */\nexport const presetUnocss = definePreset<PresetUnocssOptions>((options = {}) => {\n const { prefix } = options\n const preset: Preset = {\n name: 'unocss-preset',\n rules,\n variants: [hocusVariant],\n shortcuts,\n prefix: prefix,\n }\n return preset\n})\n\nexport default presetUnocss\n"],"mappings":";;;;;;;;;;AAQA,MAAaA,UAAQ;CACnB,MAAM;CACN,YAAY;CACZ,YAAY;CACZ,aAAa;AACf;;;ACZA,MAAM,cAAc;AACpB,MAAa,WAAW;CAEtB,QACE;CACF,gBACE;CACF,gBACE;CAEF,MAAM;CACN,aAAa;CACb,eAAe;CACf,QAAQ;CACR,WAAW;CAGX,SAAS,oGAAoG;CAC7G,gBAAgB,iGAAiG;AACnH;;;;;;;;;ACdA,MAAM,cAAc;CAClB,eAAe;CACf,iBAAiB;CACjB,iBAAiB;CACjB,gBAAgB;CAChB,uBAAuB;CACvB,uBAAuB;AACzB;;;;;AAMA,MAAM,cAAc;CAClB,eAAe;CACf,iBAAiB;CACjB,iBAAiB;CACjB,gBAAgB;CAChB,gBAAgB;CAChB,uBAAuB;CACvB,uBAAuB;CACvB,mBAAmB;AACrB;;;;AAKA,MAAM,kBAAkB;CACtB,mBAAmB;CACnB,qBAAqB;CACrB,qBAAqB;CACrB,gBAAgB;CAChB,kBAAkB;CAClB,kBAAkB;AACpB;AAEA,MAAa,UAAU;CACrB,GAAG;CACH,GAAG;CACH,GAAG;AACL;;;AC7CA,MAAM,aAAa;AAEnB,MAAa,UAAU;CAErB,gBAAgB,qCAAqC,WAAW;CAEhE,gBAAgB,qCAAqC,WAAW;CAEhE,gBAAgB,qCAAqC,WAAW;CAEhE,iBAAiB,qCAAqC,WAAW;CAEjE,iBAAiB,qCAAqC,WAAW;CAEjE,eAAe,mCAAmC,WAAW;AAC/D;;;ACfA,MAAM,aAAa;AAEnB,MAAa,UAAU;CAErB,mBAAmB;CAEnB,gBAAgB,2BAA2B,WAAW;CAEtD,sBAAsB,iDAAiD,WAAW;CAElF,uBAAuB,2BAA2B,WAAW;CAE7D,mBAAmB;CAEnB,mBAAmB;CAEnB,iBAAiB,kCAAkC,WAAW;CAE9D,iBAAiB,kCAAkC,WAAW;CAE9D,oBAAoB,kCAAkC,WAAW;AACnE;;;AChBA,MAAa,YAAY;CACvB,GAAGC;CACH,GAAG;CACH,GAAG;CACH,GAAG;CACH,GAAG;AACL;;;ACVA,MAAM,YAAY;CAChB,IAAI;EACF,aAAa;EACb,eAAe;CACjB;CACA,IAAI;EACF,aAAa;EACb,eAAe;CACjB;CACA,MAAM;EACJ,aAAa;EACb,eAAe;CACjB;CACA,IAAI;EACF,aAAa;EACb,eAAe;CACjB;CACA,OAAO;EACL,aAAa;EACb,eAAe;CACjB;CACA,IAAI;EACF,aAAa;EACb,eAAe;CACjB;CACA,IAAI;EACF,aAAa;EACb,eAAe;CACjB;CACA,OAAO;EACL,aAAa;EACb,eAAe;CACjB;CACA,OAAO;EACL,aAAa;EACb,eAAe;CACjB;CACA,OAAO;EACL,aAAa;EACb,eAAe;CACjB;CACA,OAAO;EACL,aAAa;EACb,eAAe;CACjB;AACF;AAEA,MAAM,kBAAkB,OAAO,KAAK,SAAS,CAAC,CAAC,KAAK,GAAG;AAEvD,MAAa,gBAAwB,CACnC,CAAC,IAAI,OAAO,UAAU,gBAAgB,GAAG,IAAI,GAAG,SAAS,UAAU,IAAoB,CACzF;;;ACjDA,MAAM,mBAAsD;CAC1D,GAAG,CAAC,KAAK;CACT,GAAG,CAAC,OAAO;CACX,GAAG,CAAC,QAAQ;CACZ,GAAG,CAAC,MAAM;CACV,GAAG,CAAC,QAAQ,OAAO;CACnB,GAAG,CAAC,OAAO,QAAQ;CACnB,KAAK;EAAC;EAAO;EAAS;EAAU;CAAM;AACxC;;;;;AAMA,SAAS,YAAY,QAAiB,OAAqC;CACzE,IAAI,CAAC,UAAU,OAAO,WAAW,UAAU;CAC3C,KAAK,IAAI,OAAO,MAAM,QAAQ,OAAO,GAAG,QAAQ;EAC9C,MAAM,MAAM,MAAM,MAAM,GAAG,IAAI,CAAC,CAAC,KAAK,GAAG;EACzC,IAAI,EAAE,OAAO,SAAS;EACtB,MAAM,QAAS,OAAmC;EAClD,IAAI,SAAS,MAAM,QAAQ;GACzB,IAAI,OAAO,UAAU,UAAU,OAAO;GACtC;EACF;EACA,MAAM,SAAS,YAAY,OAAO,MAAM,MAAM,IAAI,CAAC;EACnD,IAAI,QAAQ,OAAO;CACrB;AACF;AAEA,SAAS,gBAAgB,OAAwB;CAC/C,OAAO,wDAAwD,KAAK,KAAK;AAC3E;AAEA,MAAM,cAAc,IAAI,IACtB;;;;;;;;;;;;;;;;;mCAiBiC,MAAM,KAAK,CAC9C;;;;;AAMA,SAAS,aAAa,OAA6C;CACjE,IAAI,QAAQ;CACZ,IAAI,QAAQ;CAEZ,KAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;EACrC,MAAM,OAAO,MAAM;EACnB,IAAI,SAAS,OAAO,SAAS,KAAK;OAC7B,IAAI,SAAS,OAAO,SAAS,KAAK;OAClC,IAAI,SAAS,OAAO,UAAU,GAAG,QAAQ;CAChD;CAEA,IAAI,QAAQ,GAAG,OAAO,CAAC,OAAO,KAAA,CAAS;CACvC,OAAO,CAAC,MAAM,MAAM,GAAG,KAAK,GAAG,MAAM,MAAM,QAAQ,CAAC,CAAC;AACvD;AAEA,SAAS,aAAa,OAAmC;CACvD,MAAM,MAAM,MAAM,WAAW,GAAG,KAAK,MAAM,SAAS,GAAG,IAAI,MAAM,MAAM,GAAG,EAAE,IAAI;CAChF,IAAI,6BAA6B,KAAK,GAAG,GAAG,OAAO;CACnD,IAAI,CAAC,KAAK;CAEV,MAAM,SAAS,OAAO,GAAG;CACzB,IAAI,CAAC,OAAO,SAAS,MAAM,KAAK,SAAS,GAAG;CAG5C,OAAO,GADS,IAAI,SAAS,GAAG,KAAK,UAAU,IAAI,SAAS,MAAM,OAChD;AACpB;;;;;;;;;;AAWA,SAAS,aAAa,OAAe,QAAqC;CACxE,MAAM,CAAC,KAAK,WAAW,aAAa,KAAK;CAEzC,IAAI;CACJ,IAAI,IAAI,WAAW,GAAG,KAAK,IAAI,SAAS,GAAG,GAAG;EAC5C,MAAM,QAAQ,IAAI,MAAM,GAAG,EAAE;EAE7B,WAAW,YAAY,QAAQ,MAAM,MAAM,GAAG,CAAC,KAAK,MAAM,WAAW,KAAK,GAAG;CAC/E,OACE,WACE,YAAY,QAAQ,IAAI,MAAM,GAAG,CAAC,MACjC,YAAY,IAAI,IAAI,YAAY,CAAC,KAAK,gBAAgB,GAAG,IAAI,MAAM,KAAA;CAGxE,IAAI,CAAC,UAAU;CACf,IAAI,YAAY,KAAA,GAAW,OAAO;CAElC,MAAM,UAAU,aAAa,OAAO;CACpC,IAAI,CAAC,SAAS;CAEd,OAAO,sBAAsB,SAAS,GAAG,QAAQ;AACnD;;;;;;;;;;;;;;AAeA,MAAa,cAAsB,CACjC,CACE,yEACC,GAAG,WAAW,OAAO,OAAO,QAAQ,EAAE,YAAY;CACjD,IAAI,CAAC,SAAS,CAAC,OAAO;CACtB,MAAM,aAAa,iBAAkB,aAAa;CAClD,MAAM,gBAAgB,aAAa,OAAQ,MAA+B,MAAM;CAChF,IAAI,CAAC,eAAe;CAEpB,MAAM,eAAuC,CAAC;CAC9C,KAAK,MAAM,KAAK,YAAY;EAC1B,aAAa,UAAU,EAAE,WAAW,GAAG,MAAM;EAC7C,aAAa,UAAU,EAAE,WAAW,SAAS;EAC7C,aAAa,UAAU,EAAE,WAAW;CACtC;CACA,OAAO;AACT,CACF,CACF;ACrJA,MAAa,yBAAiC;CAF9B;CAAQ;CAAU;AAEY,CAAA,CAAO,KAAK,UAAU,CAClE,MAAM,SACN,EAAE,sBAAsB,MAAM,CAChC,CAAC;;;ACOD,MAAM,aAA0B;CAAC;CAAK;CAAK;CAAK;CAAK;AAAK;AAC1D,MAAM,QAA+B;CACnC,IAAI;EAAE,QAAQ;EAAG,MAAM;EAAG,QAAQ;EAAG,OAAO;CAAsB;CAClE,IAAI;EAAE,QAAQ;EAAG,MAAM;EAAG,QAAQ;EAAG,OAAO;CAAqB;CACjE,IAAI;EAAE,QAAQ;EAAG,MAAM;EAAG,QAAQ;EAAI,OAAO;CAAqB;CAClE,IAAI;EAAE,QAAQ;EAAI,MAAM;EAAI,QAAQ;EAAI,OAAO;CAAqB;CACpE,IAAI;EAAE,QAAQ;EAAI,MAAM;EAAI,QAAQ;EAAI,OAAO;CAAqB;CACpE,OAAO;EAAE,QAAQ;EAAI,MAAM;EAAI,QAAQ;EAAK,OAAO;CAAsB;AAC3E;AAEA,SAAS,qBAAqB;CAC5B,OAAO,WAAW,QAAQ,KAAsB,cAAyB;EACvE,OAAO,KAAK,KAAK,CAAC,CAAC,SAAS,YAAoB;GAC9C,MAAM,OAAO,MAAM;GACnB,MAAM,MAAM,GAAG,YAAY,UAAU,MAAM,UAAU;GACrD,IAAI;GAEJ,IAAI,cAAc,OAChB,YAAY,WAAW,KAAK,KAAK,KAAK,KAAK,OAAO,KAAK,KAAK;QAY5D,YAAY,GAVa,CAAC,KAAK,GAAG,CAAC,CAAC,SAAS,SAAS,IAClD,cAAc,MACZ,KAAK,SACL,CAAC,KAAK,SACR,EAM4B,KALT,CAAC,KAAK,GAAG,CAAC,CAAC,SAAS,SAAS,IAChD,cAAc,MACZ,KAAK,SACL,CAAC,KAAK,SACR,EACgD,KAAK,KAAK,KAAK,KAAK,KAAK,OAAO,KAAK,KAAK;GAEhG,IAAI,OAAO,EAAE,cAAc,UAAU;EACvC,CAAC;EACD,OAAO;CACT,GAAG,CAAC,CAAoB;AAC1B;AAEA,MAAM,UAAU,mBAAmB;;;;;;;AAQnC,MAAa,cAAsB,CACjC,CACE,uEACC,GAAG,WAAW,MAAM,cAAc;CACjC,MAAM,eAAe,QAAQ;CAC7B,MAAM,MAAM,GAAG,aAAa,QAAQ,eAAe,IAAI,iBAAiB;CACxE,OAAO,QAAQ;AACjB,CACF,CACF;;;AC/DA,MAAa,QAAQ;CAAC,GAAG;CAAa,GAAG;CAAe,GAAG;CAAwB,GAAG;AAAW;;;;;;ACcjG,MAAM,eAAwB;CAC5B,MAAM;CACN,QAAQ,YAAY;EAClB,IAAI,CAAC,QAAQ,WAAW,QAAQ,GAAG;EACnC,OAAO;GACL,SAAS,QAAQ,MAAM,CAAe;GACtC,WAAW,aAAa,GAAG,SAAS,UAAU,SAAS;EACzD;CACF;AACF;;;;;;;;;;;;AAaA,MAAa,eAAe,cAAmC,UAAU,CAAC,MAAM;CAC9E,MAAM,EAAE,WAAW;CAQnB,OAAO;EANL,MAAM;EACN;EACA,UAAU,CAAC,YAAY;EACvB;EACQ;CAEE;AACd,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,94 @@
1
1
  {
2
2
  "name": "@starweave-lab/unocss-preset",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.0.2",
4
+ "description": "A useful UnoCSS preset.",
5
+ "keywords": [
6
+ "atomic-css",
7
+ "preset",
8
+ "unocss",
9
+ "unocss-preset"
10
+ ],
11
+ "license": "MIT",
12
+ "author": "zchengfeng <2684837849@qq.com>",
13
+ "repository": {
14
+ "type": "git",
15
+ "url": "git+https://github.com/yibird/unocss-preset.git"
16
+ },
17
+ "funding": "https://github.com/sponsors/unocss-preset",
18
+ "files": [
19
+ "dist",
20
+ "README.md",
21
+ "LICENSE"
22
+ ],
23
+ "type": "module",
24
+ "sideEffects": false,
25
+ "main": "./dist/index.mjs",
26
+ "module": "./dist/index.mjs",
27
+ "types": "./dist/index.d.mts",
28
+ "exports": {
29
+ ".": {
30
+ "types": "./dist/index.d.mts",
31
+ "import": "./dist/index.mjs",
32
+ "default": "./dist/index.mjs"
33
+ },
34
+ "./package.json": "./package.json"
35
+ },
36
+ "publishConfig": {
37
+ "access": "public"
38
+ },
39
+ "dependencies": {
40
+ "@unocss/core": "^66.11.0"
41
+ },
42
+ "devDependencies": {
43
+ "@commitlint/cli": "^21.2.4",
44
+ "@commitlint/config-conventional": "^21.2.4",
45
+ "@types/node": "^26.6.5",
46
+ "@unocss/preset-wind4": "^66.11.0",
47
+ "conventional-changelog": "^8.1.3",
48
+ "conventional-changelog-conventionalcommits": "^10.4.1",
49
+ "cz-git": "^1.14.0",
50
+ "lint-staged": "^17.6.0",
51
+ "simple-git-hooks": "^2.14.0",
52
+ "typescript": "~7.0.2",
53
+ "vite-plus": "^1.3.0"
54
+ },
55
+ "simple-git-hooks": {
56
+ "pre-commit": "pnpm lint-staged",
57
+ "commit-msg": "pnpm exec commitlint --edit $1",
58
+ "preserveUnused": true
59
+ },
60
+ "lint-staged": {
61
+ "*.{js,mjs,cjs,ts,mts,cts}": [
62
+ "vp lint --fix --tsconfig=tsconfig.json",
63
+ "vp fmt --write"
64
+ ],
65
+ "*.{json,jsonc,md,yml,yaml,css,html}": [
66
+ "vp fmt --write"
67
+ ]
68
+ },
69
+ "config": {
70
+ "commitizen": {
71
+ "path": "node_modules/cz-git"
72
+ }
73
+ },
74
+ "engines": {
75
+ "node": ">=20.19.0"
76
+ },
77
+ "scripts": {
78
+ "dev": "vp pack --watch",
79
+ "build": "vp pack",
80
+ "test": "vp test",
81
+ "test:watch": "vp test watch",
82
+ "lint": "vp lint .",
83
+ "lint:fix": "vp lint . --fix",
84
+ "format": "vp fmt --write .",
85
+ "format:check": "vp fmt --check .",
86
+ "typecheck": "tsc --noEmit",
87
+ "check": "pnpm typecheck && vp check && vp test && vp pack",
88
+ "commit": "czg",
89
+ "changelog": "conventional-changelog -p conventionalcommits -i CHANGELOG.md -o CHANGELOG.md -r 0",
90
+ "docs:dev": "pnpm -C docs dev",
91
+ "docs:build": "pnpm -C docs build",
92
+ "docs:preview": "pnpm -C docs preview"
93
+ }
6
94
  }