@yunlefun/vue 0.1.1 → 0.1.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.
Files changed (65) hide show
  1. package/README.md +26 -37
  2. package/components/YlfAccordion.vue +165 -0
  3. package/components/YlfAvatar.vue +70 -0
  4. package/components/YlfBadge.vue +142 -0
  5. package/components/YlfBottomMenu.vue +23 -6
  6. package/components/YlfBottomMenuItem.vue +41 -21
  7. package/components/YlfButton.vue +290 -7
  8. package/components/YlfCard.vue +74 -3
  9. package/components/YlfCheckbox.vue +84 -0
  10. package/components/YlfDialog.vue +174 -0
  11. package/components/YlfDropdownMenu.vue +98 -0
  12. package/components/YlfLogo.vue +83 -7
  13. package/components/YlfPopover.vue +64 -0
  14. package/components/YlfProgress.vue +65 -0
  15. package/components/YlfRadioGroup.vue +120 -0
  16. package/components/YlfSelect.vue +195 -0
  17. package/components/YlfSeparator.vue +68 -0
  18. package/components/YlfSlider.vue +89 -0
  19. package/components/YlfSwitch.vue +111 -0
  20. package/components/YlfTabs.vue +117 -0
  21. package/components/YlfTooltip.vue +67 -0
  22. package/components/accordion/demo.vue +11 -0
  23. package/components/accordion/index.md +19 -0
  24. package/components/avatar/demo.vue +9 -0
  25. package/components/avatar/index.md +15 -0
  26. package/components/badge/demo.vue +84 -0
  27. package/components/badge/index.md +41 -0
  28. package/components/bottom-menu/demo.vue +19 -10
  29. package/components/bottom-menu/index.md +25 -0
  30. package/components/button/demo.vue +101 -3
  31. package/components/button/index.md +41 -0
  32. package/components/card/demo.vue +76 -3
  33. package/components/card/index.md +19 -0
  34. package/components/checkbox/demo.vue +28 -0
  35. package/components/checkbox/index.md +14 -0
  36. package/components/common/demo.vue +7 -1
  37. package/components/common/index.md +10 -0
  38. package/components/dialog/demo.vue +33 -0
  39. package/components/dialog/index.md +30 -0
  40. package/components/dropdown-menu/demo.vue +27 -0
  41. package/components/dropdown-menu/index.md +27 -0
  42. package/components/popover/demo.vue +18 -0
  43. package/components/popover/index.md +21 -0
  44. package/components/progress/demo.vue +24 -0
  45. package/components/progress/index.md +17 -0
  46. package/components/radio/demo.vue +20 -0
  47. package/components/radio/index.md +23 -0
  48. package/components/select/demo.vue +20 -0
  49. package/components/select/index.md +41 -0
  50. package/components/separator/demo.vue +52 -0
  51. package/components/separator/index.md +19 -0
  52. package/components/slider/demo.vue +23 -0
  53. package/components/slider/index.md +16 -0
  54. package/components/switch/demo.vue +33 -0
  55. package/components/switch/index.md +18 -0
  56. package/components/tabs/demo.vue +30 -0
  57. package/components/tabs/index.md +18 -0
  58. package/components/theme.ts +5 -0
  59. package/components/tooltip/demo.vue +19 -0
  60. package/components/tooltip/index.md +21 -0
  61. package/dist/index.cjs +2 -1
  62. package/dist/index.d.cts +7 -1
  63. package/dist/index.d.mts +7 -1
  64. package/dist/index.d.ts +7 -1
  65. package/package.json +18 -3
@@ -1,15 +1,298 @@
1
+ <script lang="ts" setup>
2
+ import type { YlfAccentTone, YlfColorAppearance } from './theme'
3
+ import { computed } from 'vue'
4
+
5
+ type ButtonVariant
6
+ = | 'primary' // 晴空蓝实色(主操作)
7
+ | 'accent' // 高饱和纯色强调
8
+ | 'aurora' // @deprecated 使用 accent;旧调用映射到纯色
9
+ | 'secondary' // 描边
10
+ | 'soft' // 柔色填充
11
+ | 'ghost' // 幽灵
12
+ | 'success'
13
+ | 'warning'
14
+ | 'danger'
15
+
16
+ type ButtonSize = 'sm' | 'md' | 'lg'
17
+
18
+ const props = withDefaults(defineProps<{
19
+ variant?: ButtonVariant
20
+ /** 仅 accent 变体使用,默认晴空蓝 */
21
+ tone?: YlfAccentTone
22
+ /** accent 与状态变体的强调程度 */
23
+ appearance?: YlfColorAppearance
24
+ size?: ButtonSize
25
+ /** 胶囊圆角,默认开启(云是软的) */
26
+ round?: boolean
27
+ /** 占满整行宽度 */
28
+ block?: boolean
29
+ disabled?: boolean
30
+ loading?: boolean
31
+ /** 渲染标签,如 'a' */
32
+ tag?: string
33
+ }>(), {
34
+ variant: 'primary',
35
+ tone: 'blue',
36
+ appearance: 'solid',
37
+ size: 'md',
38
+ round: true,
39
+ block: false,
40
+ disabled: false,
41
+ loading: false,
42
+ tag: 'button',
43
+ })
44
+
45
+ const emit = defineEmits<{ click: [e: MouseEvent] }>()
46
+
47
+ const classes = computed(() => [
48
+ `ylf-button--${props.variant}`,
49
+ `ylf-button--${props.size}`,
50
+ `is-${props.appearance}`,
51
+ {
52
+ 'is-round': props.round,
53
+ 'is-block': props.block,
54
+ 'is-loading': props.loading,
55
+ 'is-disabled': props.disabled,
56
+ },
57
+ ])
58
+
59
+ function onClick(e: MouseEvent) {
60
+ if (props.disabled || props.loading)
61
+ return
62
+ emit('click', e)
63
+ }
64
+ </script>
65
+
1
66
  <template>
2
- <button class="ylf-button">
3
- <slot />
4
- </button>
67
+ <component
68
+ :is="tag"
69
+ class="ylf-button"
70
+ :class="classes"
71
+ :data-ylf-tone="tone"
72
+ :disabled="tag === 'button' ? (disabled || loading) : undefined"
73
+ :aria-busy="loading || undefined"
74
+ @click="onClick"
75
+ >
76
+ <span v-if="loading" class="ylf-button__spinner" aria-hidden="true" />
77
+ <slot name="icon" />
78
+ <span class="ylf-button__label"><slot /></span>
79
+ </component>
5
80
  </template>
6
81
 
7
82
  <style lang="scss">
8
83
  .ylf-button {
9
- background-color: #fff;
10
- border: 1px solid #d9d9d9;
11
- padding: 4px 12px;
84
+ --_radius: var(--ylf-radius, 14px);
85
+
86
+ display: inline-flex;
87
+ align-items: center;
88
+ justify-content: center;
89
+ gap: 8px;
90
+ border: 1px solid transparent;
91
+ border-radius: var(--_radius);
92
+ font-family: var(--ylf-font-body, inherit);
93
+ font-weight: 600;
94
+ line-height: 1;
95
+ white-space: nowrap;
96
+ cursor: pointer;
97
+ user-select: none;
98
+ text-decoration: none;
99
+ transition:
100
+ transform var(--ylf-duration-fast, 160ms) var(--ylf-ease-bounce, cubic-bezier(0.34, 1.56, 0.64, 1)),
101
+ box-shadow var(--ylf-duration-normal, 240ms) var(--ylf-ease-standard, ease),
102
+ background var(--ylf-duration-normal, 240ms) var(--ylf-ease-standard, ease),
103
+ color var(--ylf-duration-fast, 160ms) var(--ylf-ease-standard, ease),
104
+ border-color var(--ylf-duration-fast, 160ms) var(--ylf-ease-standard, ease);
105
+
106
+ &.is-round {
107
+ --_radius: var(--ylf-radius-pill, 999px);
108
+ }
109
+
110
+ &.is-block {
111
+ display: flex;
112
+ width: 100%;
113
+ }
114
+
115
+ &:active {
116
+ transform: scale(0.96);
117
+ }
118
+
119
+ &:focus-visible {
120
+ outline: none;
121
+ box-shadow:
122
+ 0 0 0 3px var(--ylf-c-brand-soft, #eff6ff),
123
+ 0 0 0 5px var(--ylf-c-brand, #2563eb);
124
+ }
125
+
126
+ // --- sizes ---
127
+ &--sm {
128
+ padding: 8px 18px;
129
+ font-size: 13px;
130
+ }
131
+
132
+ &--md {
133
+ padding: 11px 24px;
134
+ font-size: 15px;
135
+ }
136
+
137
+ &--lg {
138
+ padding: 14px 30px;
139
+ font-size: 17px;
140
+ }
141
+
142
+ // --- primary:晴空蓝实色(主操作,单色不花) ---
143
+ &--primary {
144
+ color: var(--ylf-c-text-on-accent, #fff);
145
+ background: var(--ylf-c-brand, #2563eb);
146
+ box-shadow: var(--ylf-shadow-sm);
147
+
148
+ &:hover {
149
+ background: var(--ylf-c-brand-hover, #1d4ed8);
150
+ transform: translateY(-2px);
151
+ box-shadow: var(--ylf-shadow-sm);
152
+ }
153
+ }
154
+
155
+ // 强调色来自共享上下文;实色上的文字与填充色成组维护。
156
+ &--accent,
157
+ &--aurora {
158
+ --_color: var(--ylf-accent, var(--ylf-c-brand, #2563eb));
159
+ --_soft: var(--ylf-accent-soft, var(--ylf-c-brand-soft, #eff6ff));
160
+ --_text: var(--ylf-accent-text, var(--ylf-c-brand, #2563eb));
161
+
162
+ color: var(--ylf-accent-on, var(--ylf-c-text-on-accent, #fff));
163
+ background: var(--ylf-accent, var(--ylf-c-brand, #2563eb));
164
+ box-shadow: var(--ylf-shadow-sm);
165
+
166
+ &:hover {
167
+ background: var(--ylf-accent-hover, var(--ylf-c-brand-hover, #1d4ed8));
168
+ }
169
+ }
170
+
171
+ // --- secondary:描边 ---
172
+ &--secondary {
173
+ color: var(--ylf-c-text, #0f172a);
174
+ background: var(--ylf-c-surface, #fff);
175
+ border-color: var(--ylf-c-border-strong, #cbd5e1);
176
+
177
+ &:hover {
178
+ color: var(--ylf-c-brand, #2563eb);
179
+ border-color: var(--ylf-c-brand, #2563eb);
180
+ transform: translateY(-2px);
181
+ }
182
+ }
183
+
184
+ // --- soft:柔色填充 ---
185
+ &--soft {
186
+ color: var(--ylf-c-brand, #2563eb);
187
+ background: var(--ylf-c-brand-soft, #eff6ff);
188
+
189
+ &:hover {
190
+ transform: translateY(-2px);
191
+ box-shadow: var(--ylf-shadow-sm, 0 2px 8px -2px rgba(15, 23, 42, 0.12));
192
+ }
193
+ }
194
+
195
+ // --- ghost:幽灵 ---
196
+ &--ghost {
197
+ color: var(--ylf-c-text-2, #475569);
198
+ background: transparent;
199
+
200
+ &:hover {
201
+ color: var(--ylf-c-brand, #2563eb);
202
+ background: var(--ylf-c-brand-soft, #eff6ff);
203
+ }
204
+ }
205
+
206
+ // --- 语义色 ---
207
+ @each $status, $fill, $on, $hover in (success, #22c55e, #124324, #4ade80), (warning, #facc15, #64380e, #eab308),
208
+ (danger, #ff6b4a, #551b0a, #ff856b)
209
+ {
210
+ &--#{$status} {
211
+ --_color: var(--ylf-status-#{$status}, #{$fill});
212
+ --_soft: var(--ylf-status-#{$status}-soft, var(--ylf-c-bg-soft, #f1f5f9));
213
+ --_text: var(--ylf-status-#{$status}-text, #{$on});
214
+
215
+ color: var(--ylf-status-#{$status}-on, $on);
216
+ background: var(--ylf-status-#{$status}, $fill);
217
+
218
+ &:hover {
219
+ background: var(--ylf-status-#{$status}-hover, $hover);
220
+ }
221
+ }
222
+ }
223
+
224
+ &--accent,
225
+ &--aurora,
226
+ &--success,
227
+ &--warning,
228
+ &--danger {
229
+ &.is-soft,
230
+ &.is-outline {
231
+ color: var(--_text);
232
+ box-shadow: none;
233
+
234
+ &:hover {
235
+ background: var(--_soft);
236
+ border-color: var(--_text);
237
+ }
238
+
239
+ &:focus-visible {
240
+ outline: 2px solid var(--ylf-c-brand, #2563eb);
241
+ outline-offset: 3px;
242
+ }
243
+ }
244
+
245
+ &.is-soft {
246
+ background: var(--_soft);
247
+ }
248
+
249
+ &.is-outline {
250
+ background: transparent;
251
+ border-color: var(--_text);
252
+ }
253
+ }
254
+
255
+ // --- 状态 ---
256
+ &.is-disabled,
257
+ &:disabled {
258
+ opacity: 0.45;
259
+ cursor: not-allowed;
260
+ box-shadow: none;
261
+ transform: none !important;
262
+ }
263
+
264
+ &.is-loading {
265
+ cursor: progress;
266
+ }
267
+
268
+ &__label {
269
+ display: inline-flex;
270
+ align-items: center;
271
+ }
272
+
273
+ &__spinner {
274
+ width: 1em;
275
+ height: 1em;
276
+ border: 2px solid currentColor;
277
+ border-right-color: transparent;
278
+ border-radius: 50%;
279
+ animation: ylf-button-spin 0.7s linear infinite;
280
+ }
281
+ }
282
+
283
+ @keyframes ylf-button-spin {
284
+ to {
285
+ transform: rotate(360deg);
286
+ }
287
+ }
288
+
289
+ @media (prefers-reduced-motion: reduce) {
290
+ .ylf-button {
291
+ transition: none;
292
+ }
12
293
 
13
- border-radius: 4px;
294
+ .ylf-button__spinner {
295
+ animation-duration: 1.4s;
296
+ }
14
297
  }
15
298
  </style>
@@ -1,9 +1,80 @@
1
+ <script lang="ts" setup>
2
+ import type { YlfAccentTone } from './theme'
3
+
4
+ type CardVariant
5
+ = | 'soft' // 柔影:默认表面 + 色染柔影
6
+ | 'glass' // 云玻璃:背景模糊通透
7
+ | 'accent' // 纯色顶部强调
8
+ | 'tinted' // 柔色表面,用于内容分组
9
+ | 'gradient' // @deprecated 使用 accent
10
+
11
+ withDefaults(defineProps<{
12
+ variant?: CardVariant
13
+ tone?: YlfAccentTone
14
+ /** 悬停时轻轻抬升 */
15
+ hoverable?: boolean
16
+ /** 内边距,接受任意 CSS 值 */
17
+ padding?: string
18
+ }>(), {
19
+ variant: 'soft',
20
+ tone: 'blue',
21
+ hoverable: true,
22
+ padding: '22px',
23
+ })
24
+ </script>
25
+
1
26
  <template>
2
27
  <div
3
- class="flex items-center justify-center bg-white p-2 dark:bg-gray-800"
4
-
5
- rounded shadow transition hover:shadow-md
28
+ class="ylf-card"
29
+ :data-ylf-tone="tone"
30
+ :class="[`ylf-card--${variant}`, { 'is-hoverable': hoverable }]"
31
+ :style="{ padding }"
6
32
  >
7
33
  <slot />
8
34
  </div>
9
35
  </template>
36
+
37
+ <style lang="scss">
38
+ .ylf-card {
39
+ position: relative;
40
+ border-radius: var(--ylf-radius-lg, 20px);
41
+ color: var(--ylf-c-text, #0f172a);
42
+ background: var(--ylf-c-surface, #fff);
43
+ border: 1px solid var(--ylf-c-border, #e2e8f0);
44
+ box-shadow: var(--ylf-shadow-sm, 0 2px 8px -2px rgba(15, 23, 42, 0.12));
45
+ transition:
46
+ transform var(--ylf-duration-normal, 240ms) var(--ylf-ease-standard, ease),
47
+ box-shadow var(--ylf-duration-normal, 240ms) var(--ylf-ease-standard, ease),
48
+ background var(--ylf-duration-normal, 240ms) var(--ylf-ease-standard, ease);
49
+
50
+ &.is-hoverable:hover {
51
+ transform: translateY(-4px);
52
+ box-shadow: var(--ylf-shadow, 0 10px 28px -8px rgba(15, 23, 42, 0.16));
53
+ }
54
+
55
+ // 云玻璃
56
+ &--glass {
57
+ background: var(--ylf-glass-bg, rgba(255, 255, 255, 0.62));
58
+ border-color: var(--ylf-glass-border, rgba(255, 255, 255, 0.7));
59
+ -webkit-backdrop-filter: var(--ylf-glass-blur, saturate(160%) blur(16px));
60
+ backdrop-filter: var(--ylf-glass-blur, saturate(160%) blur(16px));
61
+ }
62
+
63
+ &--accent,
64
+ &--gradient {
65
+ border-top: 3px solid var(--ylf-accent, var(--ylf-c-brand, #2563eb));
66
+ }
67
+
68
+ &--tinted {
69
+ background: var(--ylf-accent-soft, var(--ylf-c-brand-soft, #eff6ff));
70
+ border-color: color-mix(in srgb, var(--ylf-accent, var(--ylf-c-brand, #2563eb)) 35%, var(--ylf-c-surface, #fff));
71
+ box-shadow: none;
72
+ }
73
+ }
74
+
75
+ @media (prefers-reduced-motion: reduce) {
76
+ .ylf-card {
77
+ transition: none;
78
+ }
79
+ }
80
+ </style>
@@ -0,0 +1,84 @@
1
+ <script lang="ts" setup>
2
+ // 行为 / 可访问性由 reka-ui 提供(role=checkbox、键盘、三态 indeterminate);皮肤走 token。
3
+ import { CheckboxIndicator, CheckboxRoot } from 'reka-ui'
4
+
5
+ withDefaults(defineProps<{
6
+ disabled?: boolean
7
+ /** 表单提交值 */
8
+ value?: string
9
+ }>(), {
10
+ disabled: false,
11
+ })
12
+
13
+ const checked = defineModel<boolean | 'indeterminate'>({ default: false })
14
+ </script>
15
+
16
+ <template>
17
+ <CheckboxRoot
18
+ v-model="checked"
19
+ :disabled="disabled"
20
+ :value="value"
21
+ class="ylf-checkbox"
22
+ >
23
+ <CheckboxIndicator class="ylf-checkbox__indicator">
24
+ <svg v-if="checked === 'indeterminate'" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round">
25
+ <path d="M5 12h14" />
26
+ </svg>
27
+ <svg v-else viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round">
28
+ <path d="M5 12.5l4.5 4.5L19 7" />
29
+ </svg>
30
+ </CheckboxIndicator>
31
+ </CheckboxRoot>
32
+ </template>
33
+
34
+ <style lang="scss">
35
+ .ylf-checkbox {
36
+ --_size: 20px;
37
+
38
+ flex: none;
39
+ width: var(--_size);
40
+ height: var(--_size);
41
+ display: inline-grid;
42
+ place-items: center;
43
+ padding: 0;
44
+ border: 2px solid var(--ylf-c-border-strong, #cbd5e1);
45
+ border-radius: 7px;
46
+ background: var(--ylf-c-surface, #fff);
47
+ color: var(--ylf-c-text-on-accent, #fff);
48
+ cursor: pointer;
49
+ -webkit-tap-highlight-color: transparent;
50
+ transition:
51
+ background 0.18s ease,
52
+ border-color 0.18s ease,
53
+ box-shadow 0.2s ease;
54
+
55
+ &:hover:not([data-disabled]) {
56
+ border-color: var(--ylf-c-brand, #2563eb);
57
+ }
58
+
59
+ &:focus-visible {
60
+ outline: none;
61
+ box-shadow: 0 0 0 3px var(--ylf-c-brand-soft, #eff6ff);
62
+ }
63
+
64
+ &[data-state='checked'],
65
+ &[data-state='indeterminate'] {
66
+ background: var(--ylf-c-brand, #2563eb);
67
+ border-color: var(--ylf-c-brand, #2563eb);
68
+ }
69
+
70
+ &[data-disabled] {
71
+ opacity: 0.5;
72
+ cursor: not-allowed;
73
+ }
74
+
75
+ &__indicator {
76
+ display: inline-flex;
77
+ }
78
+
79
+ &__indicator svg {
80
+ width: 14px;
81
+ height: 14px;
82
+ }
83
+ }
84
+ </style>
@@ -0,0 +1,174 @@
1
+ <script lang="ts" setup>
2
+ // 行为 / 可访问性由 reka-ui 提供(遮罩、焦点陷阱、ESC / 点击外部关闭、滚动锁、aria);皮肤走 token。
3
+ import {
4
+ DialogClose,
5
+ DialogContent,
6
+ DialogDescription,
7
+ DialogOverlay,
8
+ DialogPortal,
9
+ DialogRoot,
10
+ DialogTitle,
11
+ DialogTrigger,
12
+ VisuallyHidden,
13
+ } from 'reka-ui'
14
+ import { computed, useSlots } from 'vue'
15
+
16
+ const props = withDefaults(defineProps<{
17
+ title?: string
18
+ description?: string
19
+ /** 没有可见标题时供屏幕阅读器使用 */
20
+ accessibleTitle?: string
21
+ }>(), {
22
+ accessibleTitle: '对话框',
23
+ })
24
+
25
+ const open = defineModel<boolean>('open', { default: false })
26
+ const slots = useSlots()
27
+ const hasVisibleTitle = computed(() => Boolean(props.title || slots.title))
28
+ const hasDescription = computed(() => Boolean(props.description || slots.description))
29
+ const descriptionProps = computed(() => hasDescription.value
30
+ ? {}
31
+ : { 'aria-describedby': undefined })
32
+ </script>
33
+
34
+ <template>
35
+ <DialogRoot v-model:open="open">
36
+ <DialogTrigger v-if="$slots.trigger" as-child>
37
+ <slot name="trigger" />
38
+ </DialogTrigger>
39
+
40
+ <DialogPortal>
41
+ <DialogOverlay class="ylf-dialog__overlay" />
42
+ <DialogContent class="ylf-dialog__content" v-bind="descriptionProps">
43
+ <DialogTitle v-if="hasVisibleTitle" class="ylf-dialog__title">
44
+ <slot name="title">
45
+ {{ title }}
46
+ </slot>
47
+ </DialogTitle>
48
+ <VisuallyHidden v-else as-child>
49
+ <DialogTitle>{{ accessibleTitle }}</DialogTitle>
50
+ </VisuallyHidden>
51
+
52
+ <DialogDescription v-if="hasDescription" class="ylf-dialog__desc">
53
+ <slot name="description">
54
+ {{ description }}
55
+ </slot>
56
+ </DialogDescription>
57
+
58
+ <div class="ylf-dialog__body">
59
+ <slot />
60
+ </div>
61
+
62
+ <DialogClose class="ylf-dialog__close" aria-label="关闭">
63
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round">
64
+ <path d="M6 6l12 12M18 6L6 18" />
65
+ </svg>
66
+ </DialogClose>
67
+ </DialogContent>
68
+ </DialogPortal>
69
+ </DialogRoot>
70
+ </template>
71
+
72
+ <style lang="scss">
73
+ .ylf-dialog__overlay {
74
+ position: fixed;
75
+ inset: 0;
76
+ z-index: 100;
77
+ background: var(--ylf-c-overlay, rgba(13, 17, 32, 0.45));
78
+ -webkit-backdrop-filter: blur(4px);
79
+ backdrop-filter: blur(4px);
80
+
81
+ &[data-state='open'] {
82
+ animation: ylf-dialog-overlay-in 0.2s ease;
83
+ }
84
+ }
85
+
86
+ .ylf-dialog__content {
87
+ position: fixed;
88
+ z-index: 101;
89
+ top: 50%;
90
+ left: 50%;
91
+ width: calc(100vw - 32px);
92
+ max-width: 460px;
93
+ max-height: calc(100vh - 32px);
94
+ overflow: auto;
95
+ padding: 24px;
96
+ transform: translate(-50%, -50%);
97
+ border-radius: var(--ylf-radius-lg, 20px);
98
+ background: var(--ylf-c-surface, #fff);
99
+ border: 1px solid var(--ylf-c-border, #e2e8f0);
100
+ box-shadow: var(--ylf-shadow-lg, 0 22px 56px -14px rgba(15, 23, 42, 0.22));
101
+
102
+ &[data-state='open'] {
103
+ animation: ylf-dialog-in 0.22s var(--ylf-ease-bounce, cubic-bezier(0.34, 1.56, 0.64, 1));
104
+ }
105
+
106
+ &:focus-visible {
107
+ outline: none;
108
+ }
109
+ }
110
+
111
+ .ylf-dialog__title {
112
+ margin: 0 0 6px;
113
+ font-family: var(--ylf-font-heading, inherit);
114
+ font-size: 19px;
115
+ font-weight: 700;
116
+ color: var(--ylf-c-text, #0f172a);
117
+ }
118
+
119
+ .ylf-dialog__desc {
120
+ margin: 0 0 16px;
121
+ font-size: 14px;
122
+ line-height: 1.6;
123
+ color: var(--ylf-c-text-2, #475569);
124
+ }
125
+
126
+ .ylf-dialog__close {
127
+ position: absolute;
128
+ top: 16px;
129
+ right: 16px;
130
+ display: inline-grid;
131
+ place-items: center;
132
+ width: 32px;
133
+ height: 32px;
134
+ padding: 0;
135
+ border: none;
136
+ border-radius: var(--ylf-radius-sm, 10px);
137
+ background: transparent;
138
+ color: var(--ylf-c-text-3, #64748b);
139
+ cursor: pointer;
140
+ transition:
141
+ background 0.18s ease,
142
+ color 0.18s ease;
143
+
144
+ &:hover {
145
+ background: var(--ylf-c-bg-mute, #e2e8f0);
146
+ color: var(--ylf-c-text, #0f172a);
147
+ }
148
+
149
+ svg {
150
+ width: 18px;
151
+ height: 18px;
152
+ }
153
+ }
154
+
155
+ @keyframes ylf-dialog-overlay-in {
156
+ from {
157
+ opacity: 0;
158
+ }
159
+ }
160
+
161
+ @keyframes ylf-dialog-in {
162
+ from {
163
+ opacity: 0;
164
+ transform: translate(-50%, -46%) scale(0.96);
165
+ }
166
+ }
167
+
168
+ @media (prefers-reduced-motion: reduce) {
169
+ .ylf-dialog__overlay[data-state='open'],
170
+ .ylf-dialog__content[data-state='open'] {
171
+ animation: none;
172
+ }
173
+ }
174
+ </style>