@xihan-ui/styles 1.0.0-alpha.0

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.
Files changed (114) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/LICENSE +21 -0
  3. package/README.md +27 -0
  4. package/css/accordion.css +108 -0
  5. package/css/affix.css +27 -0
  6. package/css/alert.css +108 -0
  7. package/css/anchor.css +137 -0
  8. package/css/avatar-group.css +82 -0
  9. package/css/avatar.css +55 -0
  10. package/css/back-top.css +61 -0
  11. package/css/badge.css +50 -0
  12. package/css/breadcrumb.css +127 -0
  13. package/css/button-group.css +131 -0
  14. package/css/button.css +98 -0
  15. package/css/calendar.css +219 -0
  16. package/css/card.css +116 -0
  17. package/css/carousel.css +193 -0
  18. package/css/cascader.css +397 -0
  19. package/css/checkbox-group.css +185 -0
  20. package/css/checkbox.css +91 -0
  21. package/css/clipboard.css +133 -0
  22. package/css/code-block.css +81 -0
  23. package/css/collapsible.css +63 -0
  24. package/css/color-picker.css +424 -0
  25. package/css/combobox.css +433 -0
  26. package/css/composer.css +127 -0
  27. package/css/context-menu.css +263 -0
  28. package/css/countdown.css +21 -0
  29. package/css/date-field.css +200 -0
  30. package/css/date-picker.css +231 -0
  31. package/css/descriptions.css +200 -0
  32. package/css/dialog.css +127 -0
  33. package/css/drawer.css +372 -0
  34. package/css/dynamic-input.css +157 -0
  35. package/css/editable.css +249 -0
  36. package/css/ellipsis.css +41 -0
  37. package/css/empty-state.css +78 -0
  38. package/css/field.css +65 -0
  39. package/css/file-upload.css +298 -0
  40. package/css/flex.css +95 -0
  41. package/css/float-button.css +121 -0
  42. package/css/form.css +185 -0
  43. package/css/gradient-text.css +62 -0
  44. package/css/grid.css +428 -0
  45. package/css/highlight.css +21 -0
  46. package/css/hover-card.css +146 -0
  47. package/css/icon-wrapper.css +64 -0
  48. package/css/icon.css +54 -0
  49. package/css/image.css +45 -0
  50. package/css/infinite-scroll.css +22 -0
  51. package/css/layers.css +2 -0
  52. package/css/layout.css +169 -0
  53. package/css/list.css +117 -0
  54. package/css/listbox.css +174 -0
  55. package/css/loading-bar.css +66 -0
  56. package/css/log.css +66 -0
  57. package/css/marquee.css +182 -0
  58. package/css/mention.css +222 -0
  59. package/css/menu.css +144 -0
  60. package/css/menubar.css +271 -0
  61. package/css/motion.css +58 -0
  62. package/css/navigation-menu.css +270 -0
  63. package/css/number-animation.css +32 -0
  64. package/css/number-field.css +219 -0
  65. package/css/page-header.css +85 -0
  66. package/css/pagination.css +148 -0
  67. package/css/pin-input.css +157 -0
  68. package/css/popconfirm.css +150 -0
  69. package/css/popover.css +150 -0
  70. package/css/popselect.css +207 -0
  71. package/css/progress.css +37 -0
  72. package/css/qr-code.css +50 -0
  73. package/css/radio-group.css +101 -0
  74. package/css/rating.css +141 -0
  75. package/css/reset.css +22 -0
  76. package/css/result.css +104 -0
  77. package/css/scroll-area.css +176 -0
  78. package/css/select.css +264 -0
  79. package/css/separator.css +17 -0
  80. package/css/skeleton.css +78 -0
  81. package/css/slider.css +197 -0
  82. package/css/spinner.css +74 -0
  83. package/css/splitter.css +127 -0
  84. package/css/statistic.css +67 -0
  85. package/css/steps.css +252 -0
  86. package/css/switch.css +61 -0
  87. package/css/table.css +404 -0
  88. package/css/tabs.css +157 -0
  89. package/css/tags-input.css +360 -0
  90. package/css/text-field.css +195 -0
  91. package/css/thread.css +107 -0
  92. package/css/time-field.css +200 -0
  93. package/css/time-picker.css +347 -0
  94. package/css/time.css +20 -0
  95. package/css/timeline.css +284 -0
  96. package/css/toast.css +195 -0
  97. package/css/toaster.css +69 -0
  98. package/css/toggle-group.css +160 -0
  99. package/css/toggle.css +103 -0
  100. package/css/tone.css +85 -0
  101. package/css/toolbar.css +114 -0
  102. package/css/tooltip.css +109 -0
  103. package/css/tour.css +305 -0
  104. package/css/transfer.css +318 -0
  105. package/css/tree-select.css +441 -0
  106. package/css/tree.css +204 -0
  107. package/css/typography.css +151 -0
  108. package/css/undefined.css +38 -0
  109. package/css/virtualizer.css +104 -0
  110. package/css/visually-hidden.css +13 -0
  111. package/css/watermark.css +48 -0
  112. package/index.css +113 -0
  113. package/index.unlayered.css +16164 -0
  114. package/package.json +163 -0
@@ -0,0 +1,148 @@
1
+ @layer xihan.components {
2
+ /* 根节点是 nav 地标,只负责把上一页 / 页码 / 下一页排成一行。
3
+ 页数为 0 时连接层会打 data-empty,这里不拿它做隐藏:作者常把「第 x-y 条」这类
4
+ 区间回显也写在 nav 里,整块藏掉会连那行字一起吞了,是否隐藏交给作者决定 */
5
+ [data-scope='pagination'][data-part='root'] {
6
+ /* 尺寸档只换这三个私有槽,上一页/下一页、页码与省略号都从 root 继承它们,
7
+ 换档不必给每个部件各写一条选择器 */
8
+ --xh-_pagination-item-size: var(--xh-control-h-md);
9
+ --xh-_pagination-item-px: var(--xh-space-2);
10
+ --xh-_pagination-font-size: var(--xh-text-label-size);
11
+
12
+ /* 当前页的选中态随语气走。没写 data-tone 时这几个 --xh-_tone-* 都没被声明,
13
+ 退回原来的品牌底与其配色。聚焦环不在其列,它恒是 --xh-ring-focus */
14
+ --xh-_pagination-selected-bg: var(--xh-_tone, var(--xh-bg-brand));
15
+ --xh-_pagination-selected-bg-hover: var(--xh-_tone-hover, var(--xh-bg-brand-hover));
16
+ --xh-_pagination-selected-bg-active: var(--xh-_tone-active, var(--xh-bg-brand-active));
17
+ --xh-_pagination-selected-fg: var(--xh-_tone-on, var(--xh-fg-on-brand));
18
+
19
+ display: flex;
20
+ align-items: center;
21
+ /* 页码多到一行放不下就换行,宁可折行也不要把按钮压扁或撑出横向滚动 */
22
+ flex-wrap: wrap;
23
+ gap: var(--xh-pagination-gap, var(--xh-space-1));
24
+ }
25
+
26
+ /* —— 尺寸 —— */
27
+ /* 缺省档写在上面的基础规则里。格子高度走控件高度尺;内边距不在控件内边距那把尺上
28
+ (页码格子靠最小宽度撑等宽,内边距比同高的控件窄一档),三档一律取空间尺 */
29
+ [data-scope='pagination'][data-part='root'][data-size='sm'] {
30
+ --xh-_pagination-item-size: var(--xh-control-h-sm);
31
+ --xh-_pagination-item-px: var(--xh-space-1_5);
32
+ --xh-_pagination-font-size: var(--xh-font-size-sm);
33
+ }
34
+
35
+ [data-scope='pagination'][data-part='root'][data-size='lg'] {
36
+ --xh-_pagination-item-size: var(--xh-control-h-lg);
37
+ --xh-_pagination-item-px: var(--xh-space-3);
38
+ --xh-_pagination-font-size: var(--xh-font-size-lg);
39
+ }
40
+
41
+ [data-scope='pagination'][data-part='root'][hidden] {
42
+ display: none;
43
+ }
44
+
45
+ /* 三类格子共用同一副骨架,行高与间距一致,切页时基线不会跳 */
46
+ [data-scope='pagination'][data-part='prev-trigger'],
47
+ [data-scope='pagination'][data-part='next-trigger'],
48
+ [data-scope='pagination'][data-part='item'],
49
+ [data-scope='pagination'][data-part='ellipsis'] {
50
+ display: inline-flex;
51
+ flex: none;
52
+ align-items: center;
53
+ justify-content: center;
54
+ /* 等宽的关键:1 与 10 的内容宽度不同,靠最小宽度把它们撑到同一格,
55
+ 行宽才不会随当前页左右抖动。三位数超过最小宽度时才自然变宽 */
56
+ min-inline-size: var(--xh-pagination-item-min-size, var(--xh-_pagination-item-size));
57
+ block-size: var(--xh-pagination-item-h, var(--xh-_pagination-item-size));
58
+ padding-inline: var(--xh-pagination-item-px, var(--xh-_pagination-item-px));
59
+ /* 省略号也占一条透明描边:只给按钮描边的话两者内容盒不等高,一行里就对不齐 */
60
+ border: var(--xh-stroke-thin) solid transparent;
61
+ border-radius: var(--xh-pagination-item-radius, var(--xh-shape-control));
62
+ font-size: var(--xh-pagination-font-size, var(--xh-_pagination-font-size));
63
+ line-height: var(--xh-leading-none);
64
+ /* 等宽数字:1 与 7 的字形宽度在比例字体里并不相同,不定住会让同位数页码抖一格 */
65
+ font-variant-numeric: tabular-nums;
66
+ white-space: nowrap;
67
+ user-select: none;
68
+ }
69
+
70
+ [data-scope='pagination'][data-part='prev-trigger'][hidden],
71
+ [data-scope='pagination'][data-part='next-trigger'][hidden],
72
+ [data-scope='pagination'][data-part='item'][hidden],
73
+ [data-scope='pagination'][data-part='ellipsis'][hidden] {
74
+ display: none;
75
+ }
76
+
77
+ /* 三个按钮共用的可点观感:默认透明底,只有交互与当前页才上色 */
78
+ [data-scope='pagination'][data-part='prev-trigger'],
79
+ [data-scope='pagination'][data-part='next-trigger'],
80
+ [data-scope='pagination'][data-part='item'] {
81
+ background: var(--xh-pagination-item-bg, transparent);
82
+ color: var(--xh-pagination-item-fg, var(--xh-fg-default));
83
+ font-weight: var(--xh-pagination-item-font-weight, var(--xh-text-label-weight));
84
+ cursor: pointer;
85
+ transition:
86
+ background var(--xh-motion-duration-micro) var(--xh-motion-ease-enter),
87
+ color var(--xh-motion-duration-micro) var(--xh-motion-ease-enter),
88
+ border-color var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
89
+ }
90
+
91
+ /* 悬停这条选择器比当前页那条多一节(:hover),同层里权重更高、会反过来压住它,
92
+ 所以悬停一路显式把当前页排除:指针停在当前页上不该把品牌底色洗成灰底。
93
+ 页码条目从不禁用(连接层只给上一页/下一页发原生 disabled),故这边不必再排除 :disabled */
94
+ [data-scope='pagination'][data-part='prev-trigger']:hover:not(:disabled),
95
+ [data-scope='pagination'][data-part='next-trigger']:hover:not(:disabled),
96
+ [data-scope='pagination'][data-part='item']:not([data-selected]):hover {
97
+ background: var(--xh-pagination-item-bg-hover, var(--xh-bg-subtle-hover));
98
+ }
99
+
100
+ [data-scope='pagination'][data-part='prev-trigger']:active:not(:disabled),
101
+ [data-scope='pagination'][data-part='next-trigger']:active:not(:disabled),
102
+ [data-scope='pagination'][data-part='item']:not([data-selected]):active {
103
+ background: var(--xh-pagination-item-bg-active, var(--xh-bg-subtle-active));
104
+ }
105
+
106
+ [data-scope='pagination'][data-part='prev-trigger']:focus-visible,
107
+ [data-scope='pagination'][data-part='next-trigger']:focus-visible,
108
+ [data-scope='pagination'][data-part='item']:focus-visible {
109
+ outline: var(--xh-ring-width) solid var(--xh-ring-focus);
110
+ outline-offset: var(--xh-ring-offset);
111
+ }
112
+
113
+ /* 首末页的上一页 / 下一页是原生 disabled(不可聚焦、进不了 Tab 序列),
114
+ 所以这里必须用 :disabled;连接层同时给的 data-disabled 只是留给作者的钩子。
115
+ 分页条目从不禁用,那边也就没有对应规则 */
116
+ [data-scope='pagination'][data-part='prev-trigger']:disabled,
117
+ [data-scope='pagination'][data-part='next-trigger']:disabled {
118
+ color: var(--xh-fg-disabled);
119
+ cursor: not-allowed;
120
+ }
121
+
122
+ /* 当前页:data-selected 与 aria-current='page' 由连接层成对给出,
123
+ 样式挂在 data 钩子上,无障碍语义留给 aria,两边各司其职。
124
+ 只换底色不加粗——字重一变字形就变宽,页码格子会跟着长一点,正是这里要避免的抖动 */
125
+ [data-scope='pagination'][data-part='item'][data-selected] {
126
+ border-color: var(--xh-pagination-item-border-selected, var(--xh-_pagination-selected-bg));
127
+ background: var(--xh-pagination-item-bg-selected, var(--xh-_pagination-selected-bg));
128
+ color: var(--xh-pagination-item-fg-selected, var(--xh-_pagination-selected-fg));
129
+ }
130
+
131
+ [data-scope='pagination'][data-part='item'][data-selected]:hover {
132
+ border-color: var(--xh-pagination-item-border-selected-hover, var(--xh-_pagination-selected-bg-hover));
133
+ background: var(--xh-pagination-item-bg-selected-hover, var(--xh-_pagination-selected-bg-hover));
134
+ }
135
+
136
+ [data-scope='pagination'][data-part='item'][data-selected]:active {
137
+ border-color: var(--xh-pagination-item-border-selected-active, var(--xh-_pagination-selected-bg-active));
138
+ background: var(--xh-pagination-item-bg-selected-active, var(--xh-_pagination-selected-bg-active));
139
+ }
140
+
141
+ /* 省略号是纯视觉占位,读屏那边已被 aria-hidden 摘掉:
142
+ 吞掉指针事件,免得它出现悬停反馈、看起来像一颗点得动的按钮 */
143
+ [data-scope='pagination'][data-part='ellipsis'] {
144
+ color: var(--xh-pagination-ellipsis-fg, var(--xh-fg-subtle));
145
+ cursor: default;
146
+ pointer-events: none;
147
+ }
148
+ }
@@ -0,0 +1,157 @@
1
+ @layer xihan.components {
2
+ /* 与文本框、数字框同一套堆叠:标签一行、格子一行 */
3
+ [data-scope='pin-input'][data-part='root'] {
4
+ /* 尺寸档只换这四个私有槽,标题与格子都从 root 继承它们,换档不必给每个部件各写一条选择器 */
5
+ --xh-_pin-input-box-size: var(--xh-control-h-lg);
6
+ --xh-_pin-input-box-gap: var(--xh-space-2);
7
+ --xh-_pin-input-box-font-size: var(--xh-font-size-xl);
8
+ --xh-_pin-input-label-font-size: var(--xh-text-label-size);
9
+ /* 形态档只换这三个私有槽,缺省这一份就是描边式(描边 + 画布底) */
10
+ --xh-_pin-input-box-bg: var(--xh-bg-canvas);
11
+ --xh-_pin-input-box-border: var(--xh-border-default);
12
+ --xh-_pin-input-box-border-hover: var(--xh-border-strong);
13
+ /* 聚焦环与填满态的强调色随语气走。没写 data-tone 时 --xh-_tone 没被声明,退回原来的取值 */
14
+ --xh-_pin-input-ring: var(--xh-ring-focus);
15
+ --xh-_pin-input-accent: var(--xh-_tone, var(--xh-bg-brand));
16
+
17
+ display: inline-flex;
18
+ flex-direction: column;
19
+ gap: var(--xh-pin-input-gap, var(--xh-space-1));
20
+ }
21
+
22
+ /* 声明了 display 就盖掉了 UA 的 [hidden]{display:none},收起态得自己还回去 */
23
+ [data-scope='pin-input'][data-part='root'][hidden] {
24
+ display: none;
25
+ }
26
+
27
+ /* —— 形态 —— */
28
+ /* 形态只决定颜色怎么用;用哪族颜色由 data-tone 决定。
29
+ 不做 solid:实心填充盖住了"这里能打字"这件事,输入框用不上它 */
30
+ [data-scope='pin-input'][data-part='root'][data-variant='outline'] {
31
+ --xh-_pin-input-box-bg: var(--xh-bg-canvas);
32
+ --xh-_pin-input-box-border: var(--xh-border-default);
33
+ --xh-_pin-input-box-border-hover: var(--xh-border-strong);
34
+ }
35
+
36
+ /* 填充式:底色说明这里是一格输入位,描边让位给底色 */
37
+ [data-scope='pin-input'][data-part='root'][data-variant='subtle'] {
38
+ --xh-_pin-input-box-bg: var(--xh-_tone-subtle, var(--xh-bg-subtle));
39
+ --xh-_pin-input-box-border: transparent;
40
+ --xh-_pin-input-box-border-hover: var(--xh-_tone-border, var(--xh-border-default));
41
+ }
42
+
43
+ /* 无边框、透明底:静息时格子完全隐入背景,可见反馈全靠悬停描边与聚焦环 */
44
+ [data-scope='pin-input'][data-part='root'][data-variant='ghost'] {
45
+ --xh-_pin-input-box-bg: transparent;
46
+ --xh-_pin-input-box-border: transparent;
47
+ --xh-_pin-input-box-border-hover: var(--xh-_tone-border, var(--xh-border-default));
48
+ }
49
+
50
+ /* —— 尺寸 —— */
51
+ [data-scope='pin-input'][data-part='root'][data-size='sm'] {
52
+ --xh-_pin-input-box-size: var(--xh-control-h-md);
53
+ --xh-_pin-input-box-gap: var(--xh-control-gap-sm);
54
+ --xh-_pin-input-box-font-size: var(--xh-font-size-lg);
55
+ --xh-_pin-input-label-font-size: var(--xh-font-size-sm);
56
+ }
57
+
58
+ /* 格子是正方形,缺省档已经占到控件高度表的顶档,lg 档由顶档加一格间距合成,不另立数值 */
59
+ [data-scope='pin-input'][data-part='root'][data-size='lg'] {
60
+ --xh-_pin-input-box-size: calc(var(--xh-control-h-lg) + var(--xh-space-2));
61
+ --xh-_pin-input-box-gap: var(--xh-control-gap-lg);
62
+ --xh-_pin-input-box-font-size: var(--xh-font-size-2xl);
63
+ --xh-_pin-input-label-font-size: var(--xh-font-size-lg);
64
+ }
65
+
66
+ [data-scope='pin-input'][data-part='label'] {
67
+ color: var(--xh-pin-input-label-fg, var(--xh-fg-default));
68
+ font-size: var(--xh-pin-input-label-font-size, var(--xh-_pin-input-label-font-size));
69
+ font-weight: var(--xh-pin-input-label-font-weight, var(--xh-text-label-weight));
70
+ line-height: var(--xh-leading-none);
71
+ }
72
+
73
+ [data-scope='pin-input'][data-part='label'][data-disabled] {
74
+ color: var(--xh-pin-input-label-fg-disabled, var(--xh-fg-subtle));
75
+ }
76
+
77
+ /* 每格是等宽方框:宽高同取一个尺寸,内边距归零。
78
+ 留了内边距,单字符居中后可用宽度不足,中文与全角字符会被挤出框外 */
79
+ [data-scope='pin-input'][data-part='input'] {
80
+ flex: none;
81
+ inline-size: var(--xh-pin-input-box-size, var(--xh-_pin-input-box-size));
82
+ block-size: var(--xh-pin-input-box-size, var(--xh-_pin-input-box-size));
83
+ padding: 0;
84
+ border: var(--xh-stroke-thin) solid var(--xh-pin-input-box-border, var(--xh-_pin-input-box-border));
85
+ border-radius: var(--xh-pin-input-box-radius, var(--xh-shape-control));
86
+ background: var(--xh-pin-input-box-bg, var(--xh-_pin-input-box-bg));
87
+ color: var(--xh-pin-input-box-fg, var(--xh-fg-default));
88
+ /* 表单控件不继承页面字体,只给字号会让格子用 UA 默认字族 */
89
+ font-family: inherit;
90
+ font-size: var(--xh-pin-input-box-font-size, var(--xh-_pin-input-box-font-size));
91
+ /* 等宽数字:各位字宽不一时逐格填入会让每格里的字符左右跳 */
92
+ font-variant-numeric: tabular-nums;
93
+ line-height: var(--xh-leading-none);
94
+ text-align: center;
95
+ transition: border-color var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
96
+ }
97
+
98
+ /* 格间距只能长在格子自己身上:包住这一行的容器是作者写的,不在角色表里,
99
+ 指望它给 gap 就等于把间距交给了皮肤管不着的地方。
100
+ 用相邻兄弟而不是给每格加外边距,是为了不在首尾多出两段空白 */
101
+ [data-scope='pin-input'][data-part='input'] + [data-scope='pin-input'][data-part='input'] {
102
+ margin-inline-start: var(--xh-pin-input-box-gap, var(--xh-_pin-input-box-gap));
103
+ }
104
+
105
+ [data-scope='pin-input'][data-part='input']::placeholder {
106
+ color: var(--xh-pin-input-placeholder-fg, var(--xh-fg-subtle));
107
+ }
108
+
109
+ /* hover 只在中性态换描边:invalid 正拿边框颜色说事,被 hover 盖掉那条信息就没了 */
110
+ [data-scope='pin-input'][data-part='input']:hover:not(:disabled, [data-invalid]) {
111
+ border-color: var(--xh-pin-input-box-border-hover, var(--xh-_pin-input-box-border-hover));
112
+ }
113
+
114
+ /* 一行格子长得一模一样,光标那道竖线太细,看不出正在填哪一格:
115
+ 当前格必须有明显的环。data-focus 由机器给(它知道谁是当前格),
116
+ :focus-visible 兜住机器状态还没回流的那一帧,两条同时写不冲突 */
117
+ [data-scope='pin-input'][data-part='input'][data-focus],
118
+ [data-scope='pin-input'][data-part='input']:focus-visible {
119
+ border-color: var(--xh-pin-input-box-border-focus, var(--xh-_pin-input-ring));
120
+ outline: var(--xh-ring-width) solid var(--xh-_pin-input-ring);
121
+ outline-offset: var(--xh-ring-offset);
122
+ }
123
+
124
+ [data-scope='pin-input'][data-part='input'][data-invalid] {
125
+ border-color: var(--xh-pin-input-box-border-invalid, var(--xh-color-danger-500));
126
+ }
127
+
128
+ /* 每格都是原生 disabled 的单体控件(禁用的格子不该能聚焦),所以用 :disabled */
129
+ [data-scope='pin-input'][data-part='input']:disabled {
130
+ background: var(--xh-pin-input-box-bg-disabled, var(--xh-bg-subtle));
131
+ color: var(--xh-fg-disabled);
132
+ cursor: not-allowed;
133
+ }
134
+
135
+ /* 填满只代表"格子都有字",不代表"验证码对",所以用品牌色而不是成功色收个尾。
136
+ 已经标了 invalid 的不在此列——那是明确说了不对 */
137
+ [data-scope='pin-input'][data-part='root'][data-complete]
138
+ [data-scope='pin-input'][data-part='input']:not([data-invalid], :disabled) {
139
+ border-color: var(--xh-pin-input-box-border-complete, var(--xh-_pin-input-accent));
140
+ }
141
+
142
+ /* 连接层把它写成 type=hidden,UA 自己就不渲染;这里的视觉隐藏是给元素升级前那一瞬兜底
143
+ (作者手写的是一个普通 input,升级完成前它是可见的文本框)。
144
+ 刻意不写 display:写了反而会盖掉 UA 对 type=hidden 的收起,把它显出来 */
145
+ [data-scope='pin-input'][data-part='hidden-input'] {
146
+ position: absolute;
147
+ inline-size: 1px;
148
+ block-size: 1px;
149
+ padding: 0;
150
+ margin: -1px;
151
+ border: 0;
152
+ overflow: hidden;
153
+ clip-path: inset(50%);
154
+ white-space: nowrap;
155
+ pointer-events: none;
156
+ }
157
+ }
@@ -0,0 +1,150 @@
1
+ @layer xihan.components {
2
+ /* 根只框住触发器:浮层树靠 positioner 的固定定位飞出去,不占根的盒子 */
3
+ [data-scope='popconfirm'][data-part='root'] {
4
+ display: inline-block;
5
+ }
6
+
7
+ /* 坐标由定位引擎写进 style(inset-inline-start / inset-block-start),这里只定位模式与层级 */
8
+ [data-scope='popconfirm'][data-part='positioner'] {
9
+ position: fixed;
10
+ z-index: var(--xh-layer-popover);
11
+ /* 空壳不吃指针,交互只发生在 content 上 */
12
+ pointer-events: none;
13
+ }
14
+
15
+ /* 锚点被滚出可视区:整个浮层随之收起 */
16
+ [data-scope='popconfirm'][data-part='positioner'][data-hidden] {
17
+ visibility: hidden;
18
+ }
19
+
20
+ /* 面板排成三列:左列吃掉剩余宽度,右两列各摆一颗按钮。
21
+ 标题、说明与作者塞进来的内容各占一整行,两颗按钮因此并排落在最后一行的右侧。 */
22
+ [data-scope='popconfirm'][data-part='content'] {
23
+ /* 尺寸档只换这三个私有槽 */
24
+ --xh-_popconfirm-py: var(--xh-space-3);
25
+ --xh-_popconfirm-px: var(--xh-control-px-md);
26
+ --xh-_popconfirm-max-w: var(--xh-overlay-max-w);
27
+
28
+ display: grid;
29
+ grid-template-columns: 1fr auto auto;
30
+ align-items: center;
31
+ gap: var(--xh-popconfirm-gap, var(--xh-space-2));
32
+ inline-size: max-content;
33
+ max-inline-size: var(--xh-popconfirm-max-w, var(--xh-_popconfirm-max-w));
34
+ padding-block: var(--xh-popconfirm-py, var(--xh-_popconfirm-py));
35
+ padding-inline: var(--xh-popconfirm-px, var(--xh-_popconfirm-px));
36
+ border: var(--xh-stroke-thin) solid var(--xh-popconfirm-border, var(--xh-border-default));
37
+ border-radius: var(--xh-popconfirm-radius, var(--xh-shape-surface));
38
+ background: var(--xh-popconfirm-bg, var(--xh-bg-surface));
39
+ color: var(--xh-popconfirm-fg, var(--xh-fg-default));
40
+ box-shadow: var(--xh-popconfirm-shadow, var(--xh-elevation-2));
41
+ pointer-events: auto;
42
+ }
43
+
44
+ [data-scope='popconfirm'][data-part='content'] > * {
45
+ grid-column: 1 / -1;
46
+ }
47
+
48
+ /* display 会盖掉 hidden 属性的 UA 规则,收起态显式让位 */
49
+ [data-scope='popconfirm'][data-part='content'][hidden] {
50
+ display: none;
51
+ }
52
+
53
+ [data-scope='popconfirm'][data-part='content']:focus-visible {
54
+ outline: none;
55
+ }
56
+
57
+ [data-scope='popconfirm'][data-part='content'][data-state='open'] {
58
+ animation: xh-fade-in var(--xh-motion-duration-enter) var(--xh-motion-ease-enter);
59
+ }
60
+
61
+ /* —— 尺寸 —— */
62
+ /* 缺省档写在 content 的基础规则里;行内内缩取 control-px 那把尺,纵向按 space 档同步 */
63
+ [data-scope='popconfirm'][data-part='content'][data-size='sm'] {
64
+ --xh-_popconfirm-py: var(--xh-space-2);
65
+ --xh-_popconfirm-px: var(--xh-control-px-sm);
66
+ --xh-_popconfirm-max-w: var(--xh-overlay-max-w-sm);
67
+ }
68
+
69
+ [data-scope='popconfirm'][data-part='content'][data-size='lg'] {
70
+ --xh-_popconfirm-py: var(--xh-space-4);
71
+ --xh-_popconfirm-px: var(--xh-control-px-lg);
72
+ --xh-_popconfirm-max-w: var(--xh-overlay-max-w-lg);
73
+ }
74
+
75
+ [data-scope='popconfirm'][data-part='title'] {
76
+ margin: 0;
77
+ font-size: var(--xh-popconfirm-title-font-size, var(--xh-text-label-size));
78
+ font-weight: var(--xh-popconfirm-title-font-weight, var(--xh-font-weight-semibold));
79
+ color: var(--xh-popconfirm-title-fg, var(--xh-fg-default));
80
+ }
81
+
82
+ [data-scope='popconfirm'][data-part='description'] {
83
+ margin: 0;
84
+ font-size: var(--xh-text-body-size);
85
+ line-height: var(--xh-leading-normal);
86
+ color: var(--xh-popconfirm-description-fg, var(--xh-fg-muted));
87
+ }
88
+
89
+ /* —— 两颗按钮 —— */
90
+ [data-scope='popconfirm'][data-part='confirm-trigger'],
91
+ [data-scope='popconfirm'][data-part='cancel-trigger'] {
92
+ display: inline-flex;
93
+ align-items: center;
94
+ justify-content: center;
95
+ block-size: var(--xh-control-h-sm);
96
+ padding-inline: var(--xh-control-px-sm);
97
+ border: var(--xh-stroke-thin) solid transparent;
98
+ border-radius: var(--xh-shape-control);
99
+ font-size: var(--xh-font-size-sm);
100
+ font-weight: var(--xh-text-label-weight);
101
+ line-height: var(--xh-leading-none);
102
+ white-space: nowrap;
103
+ cursor: pointer;
104
+ transition: background var(--xh-motion-duration-micro) var(--xh-motion-ease-enter);
105
+ }
106
+
107
+ /* 并排落在最后一行的右侧两格;取消在左、确认在右 */
108
+ [data-scope='popconfirm'][data-part='content'] > [data-part='cancel-trigger'] {
109
+ grid-column: 2;
110
+ }
111
+
112
+ [data-scope='popconfirm'][data-part='content'] > [data-part='confirm-trigger'] {
113
+ grid-column: 3;
114
+ }
115
+
116
+ /* 确认是实心的主操作。底色走共享语气槽:作者在 content 上写 data-tone="danger"
117
+ 就整体换成危险色,不写则用品牌色 */
118
+ [data-scope='popconfirm'][data-part='confirm-trigger'] {
119
+ background: var(--xh-popconfirm-confirm-bg, var(--xh-_tone, var(--xh-bg-brand)));
120
+ color: var(--xh-popconfirm-confirm-fg, var(--xh-_tone-on, var(--xh-fg-on-brand)));
121
+ }
122
+
123
+ [data-scope='popconfirm'][data-part='confirm-trigger']:hover {
124
+ background: var(--xh-_tone-hover, var(--xh-bg-brand-hover));
125
+ }
126
+
127
+ [data-scope='popconfirm'][data-part='confirm-trigger']:active {
128
+ background: var(--xh-_tone-active, var(--xh-bg-brand-active));
129
+ }
130
+
131
+ /* 取消是次要出口,用淡底中性色,不参与语气 */
132
+ [data-scope='popconfirm'][data-part='cancel-trigger'] {
133
+ background: var(--xh-popconfirm-cancel-bg, var(--xh-bg-subtle));
134
+ color: var(--xh-popconfirm-cancel-fg, var(--xh-fg-default));
135
+ }
136
+
137
+ [data-scope='popconfirm'][data-part='cancel-trigger']:hover {
138
+ background: var(--xh-bg-subtle-hover);
139
+ }
140
+
141
+ [data-scope='popconfirm'][data-part='cancel-trigger']:active {
142
+ background: var(--xh-bg-subtle-active);
143
+ }
144
+
145
+ [data-scope='popconfirm'][data-part='confirm-trigger']:focus-visible,
146
+ [data-scope='popconfirm'][data-part='cancel-trigger']:focus-visible {
147
+ outline: var(--xh-ring-width) solid var(--xh-ring-focus);
148
+ outline-offset: var(--xh-ring-offset);
149
+ }
150
+ }
@@ -0,0 +1,150 @@
1
+ @layer xihan.components {
2
+ /* 坐标由定位引擎写进 style(inset-inline-start / inset-block-start),这里只定位模式与层级 */
3
+ [data-scope='popover'][data-part='positioner'] {
4
+ position: fixed;
5
+ z-index: var(--xh-layer-popover);
6
+ /* 空壳不吃指针,交互只发生在 content 上 */
7
+ pointer-events: none;
8
+ }
9
+
10
+ /* 锚点被滚出可视区:整个浮层随之收起 */
11
+ [data-scope='popover'][data-part='positioner'][data-hidden] {
12
+ visibility: hidden;
13
+ }
14
+
15
+ [data-scope='popover'][data-part='content'] {
16
+ /* 尺寸档只换这两个私有槽。popover 没有 root 部件,size 落在浮层树最外层的 content 上。
17
+ 语气不接:面板是内容容器,语气该由里面的内容表达 */
18
+ --xh-_popover-py: var(--xh-space-3);
19
+ --xh-_popover-px: var(--xh-control-px-md);
20
+ --xh-_popover-max-w: var(--xh-overlay-max-w);
21
+
22
+ position: relative;
23
+ display: flex;
24
+ flex-direction: column;
25
+ gap: var(--xh-popover-gap, var(--xh-space-2));
26
+ inline-size: max-content;
27
+ max-inline-size: var(--xh-popover-max-w, var(--xh-_popover-max-w));
28
+ padding-block: var(--xh-popover-py, var(--xh-_popover-py));
29
+ padding-inline: var(--xh-popover-px, var(--xh-_popover-px));
30
+ border: var(--xh-stroke-thin) solid var(--xh-popover-border, var(--xh-border-default));
31
+ border-radius: var(--xh-popover-radius, var(--xh-shape-surface));
32
+ background: var(--xh-popover-bg, var(--xh-bg-surface));
33
+ color: var(--xh-popover-fg, var(--xh-fg-default));
34
+ box-shadow: var(--xh-popover-shadow, var(--xh-elevation-2));
35
+ pointer-events: auto;
36
+ }
37
+
38
+ /* display 会盖掉 hidden 属性的 UA 规则,收起态显式让位 */
39
+ [data-scope='popover'][data-part='content'][hidden] {
40
+ display: none;
41
+ }
42
+
43
+ [data-scope='popover'][data-part='content']:focus-visible {
44
+ outline: none;
45
+ }
46
+
47
+ [data-scope='popover'][data-part='content'][data-state='open'] {
48
+ animation: xh-fade-in var(--xh-motion-duration-enter) var(--xh-motion-ease-enter);
49
+ }
50
+
51
+ /* —— 尺寸 —— */
52
+ /* 缺省档写在 content 的基础规则里;行内内缩本就取 control-px 那把尺,上下各挪一档,
53
+ 纵向内缩按 space 档同步 */
54
+ [data-scope='popover'][data-part='content'][data-size='sm'] {
55
+ --xh-_popover-py: var(--xh-space-2);
56
+ --xh-_popover-px: var(--xh-control-px-sm);
57
+ --xh-_popover-max-w: var(--xh-overlay-max-w-sm);
58
+ }
59
+
60
+ [data-scope='popover'][data-part='content'][data-size='lg'] {
61
+ --xh-_popover-py: var(--xh-space-4);
62
+ --xh-_popover-px: var(--xh-control-px-lg);
63
+ --xh-_popover-max-w: var(--xh-overlay-max-w-lg);
64
+ }
65
+
66
+ [data-scope='popover'][data-part='title'] {
67
+ margin: 0;
68
+ font-size: var(--xh-popover-title-font-size, var(--xh-text-label-size));
69
+ font-weight: var(--xh-popover-title-font-weight, var(--xh-font-weight-semibold));
70
+ color: var(--xh-popover-title-fg, var(--xh-fg-default));
71
+ }
72
+
73
+ [data-scope='popover'][data-part='description'] {
74
+ margin: 0;
75
+ font-size: var(--xh-text-body-size);
76
+ color: var(--xh-popover-description-fg, var(--xh-fg-muted));
77
+ }
78
+
79
+ /* 关闭按钮是绝对定位的,不占流内宽度:标题得自己让出右上角那块,
80
+ 否则长标题会直接钻到按钮底下。只在真有关闭按钮时让位。 */
81
+ [data-scope='popover'][data-part='content']:has([data-scope='popover'][data-part='close-trigger'])
82
+ [data-scope='popover'][data-part='title'] {
83
+ padding-inline-end: calc(var(--xh-control-h-sm) + var(--xh-space-2));
84
+ }
85
+
86
+ [data-scope='popover'][data-part='close-trigger'] {
87
+ position: absolute;
88
+ inset-block-start: var(--xh-space-2);
89
+ inset-inline-end: var(--xh-space-2);
90
+ display: inline-flex;
91
+ align-items: center;
92
+ justify-content: center;
93
+ block-size: var(--xh-control-h-sm);
94
+ inline-size: var(--xh-control-h-sm);
95
+ border: 0;
96
+ border-radius: var(--xh-shape-control);
97
+ background: transparent;
98
+ color: var(--xh-fg-muted);
99
+ cursor: pointer;
100
+ }
101
+
102
+ [data-scope='popover'][data-part='close-trigger']:hover {
103
+ background: var(--xh-bg-subtle);
104
+ color: var(--xh-fg-default);
105
+ }
106
+
107
+ [data-scope='popover'][data-part='close-trigger']:focus-visible {
108
+ outline: var(--xh-ring-width) solid var(--xh-ring-focus);
109
+ outline-offset: var(--xh-ring-offset);
110
+ }
111
+
112
+ /* 箭头是旋转 45° 的方块,只保留朝向锚点那两条边的描边;
113
+ 引擎不产出箭头偏移,因此沿交叉轴固定居中 */
114
+ [data-scope='popover'][data-part='arrow'] {
115
+ position: absolute;
116
+ inline-size: var(--xh-popover-arrow-size, var(--xh-overlay-arrow-size));
117
+ block-size: var(--xh-popover-arrow-size, var(--xh-overlay-arrow-size));
118
+ background: var(--xh-popover-bg, var(--xh-bg-surface));
119
+ border: var(--xh-stroke-thin) solid var(--xh-popover-border, var(--xh-border-default));
120
+ rotate: 45deg;
121
+ }
122
+
123
+ [data-scope='popover'][data-part='arrow'][data-placement^='bottom'] {
124
+ inset-block-start: calc(var(--xh-popover-arrow-size, var(--xh-overlay-arrow-size)) / -2);
125
+ inset-inline-start: calc(50% - var(--xh-popover-arrow-size, var(--xh-overlay-arrow-size)) / 2);
126
+ border-block-end: 0;
127
+ border-inline-end: 0;
128
+ }
129
+
130
+ [data-scope='popover'][data-part='arrow'][data-placement^='top'] {
131
+ inset-block-end: calc(var(--xh-popover-arrow-size, var(--xh-overlay-arrow-size)) / -2);
132
+ inset-inline-start: calc(50% - var(--xh-popover-arrow-size, var(--xh-overlay-arrow-size)) / 2);
133
+ border-block-start: 0;
134
+ border-inline-start: 0;
135
+ }
136
+
137
+ [data-scope='popover'][data-part='arrow'][data-placement^='right'] {
138
+ inset-inline-start: calc(var(--xh-popover-arrow-size, var(--xh-overlay-arrow-size)) / -2);
139
+ inset-block-start: calc(50% - var(--xh-popover-arrow-size, var(--xh-overlay-arrow-size)) / 2);
140
+ border-block-start: 0;
141
+ border-inline-end: 0;
142
+ }
143
+
144
+ [data-scope='popover'][data-part='arrow'][data-placement^='left'] {
145
+ inset-inline-end: calc(var(--xh-popover-arrow-size, var(--xh-overlay-arrow-size)) / -2);
146
+ inset-block-start: calc(50% - var(--xh-popover-arrow-size, var(--xh-overlay-arrow-size)) / 2);
147
+ border-block-end: 0;
148
+ border-inline-start: 0;
149
+ }
150
+ }