canny-ui 6.7.0 → 7.0.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.
- package/dist/canny-ui.min.css +1 -1
- package/dist/table-resize.js +16 -1
- package/package.json +1 -1
- package/src/_utilities.scss +4 -4
- package/src/base/_motion.scss +32 -0
- package/src/base/_reset.scss +7 -0
- package/src/base/_scrollbar-mixin.scss +84 -0
- package/src/base/_touch.scss +60 -0
- package/src/canny-ui.scss +5 -0
- package/src/components/_accordion.scss +10 -1
- package/src/components/_alerts.scss +18 -8
- package/src/components/_autocomplete.scss +16 -10
- package/src/components/_avatar.scss +22 -1
- package/src/components/_breadcrumb.scss +36 -2
- package/src/components/_buttons.scss +35 -24
- package/src/components/_charts-extra.scss +432 -0
- package/src/components/_charts.scss +90 -0
- package/src/components/_chip.scss +8 -7
- package/src/components/_command-palette.scss +27 -5
- package/src/components/_confirm.scss +14 -0
- package/src/components/_datatable.scss +383 -53
- package/src/components/_datepicker.scss +44 -21
- package/src/components/_dialog.scss +12 -0
- package/src/components/_drawer.scss +12 -0
- package/src/components/_fields.scss +115 -27
- package/src/components/_forms.scss +28 -10
- package/src/components/_gantt.scss +554 -0
- package/src/components/_inputs.scss +12 -7
- package/src/components/_list.scss +37 -3
- package/src/components/_menu.scss +23 -9
- package/src/components/_navbar.scss +98 -7
- package/src/components/_pagination.scss +105 -2
- package/src/components/_panels.scss +19 -5
- package/src/components/_popover.scss +3 -5
- package/src/components/_progress.scss +31 -0
- package/src/components/_scrollbar.scss +16 -159
- package/src/components/_sidebar.scss +24 -20
- package/src/components/_skeleton.scss +40 -1
- package/src/components/_slider.scss +8 -2
- package/src/components/_stepper.scss +22 -4
- package/src/components/_table.scss +23 -3
- package/src/components/_tabs.scss +120 -4
- package/src/components/_timeline.scss +26 -0
- package/src/components/_toast.scss +24 -12
- package/src/components/_tooltip-radix.scss +25 -4
- package/src/components/_tree.scss +51 -3
- package/src/components/_uploader.scss +97 -1
- package/src/scripts/table-resize.js +16 -1
- package/src/tokens/_semantic.scss +85 -21
package/dist/table-resize.js
CHANGED
|
@@ -40,9 +40,24 @@
|
|
|
40
40
|
if (!resizer) {
|
|
41
41
|
resizer = document.createElement('span');
|
|
42
42
|
resizer.className = 'th-resizer';
|
|
43
|
-
resizer.setAttribute('aria-hidden', 'true');
|
|
44
43
|
th.appendChild(resizer);
|
|
45
44
|
}
|
|
45
|
+
// keyboard-operable separator: Left / Right resize the column (Shift = bigger steps)
|
|
46
|
+
resizer.setAttribute('role', 'separator');
|
|
47
|
+
resizer.setAttribute('aria-orientation', 'vertical');
|
|
48
|
+
resizer.setAttribute('aria-label', 'Resize ' + (th.textContent || '').trim() + ' column');
|
|
49
|
+
resizer.setAttribute('aria-valuemin', String(MIN_WIDTH));
|
|
50
|
+
resizer.setAttribute('tabindex', '0');
|
|
51
|
+
|
|
52
|
+
function keyDown(e) {
|
|
53
|
+
if (e.key !== 'ArrowLeft' && e.key !== 'ArrowRight') return;
|
|
54
|
+
e.preventDefault();
|
|
55
|
+
var step = (e.shiftKey ? 40 : 10) * (e.key === 'ArrowRight' ? 1 : -1);
|
|
56
|
+
var next = Math.max(MIN_WIDTH, th.offsetWidth + step);
|
|
57
|
+
th.style.width = next + 'px';
|
|
58
|
+
resizer.setAttribute('aria-valuenow', String(next));
|
|
59
|
+
}
|
|
60
|
+
resizer.addEventListener('keydown', keyDown);
|
|
46
61
|
|
|
47
62
|
function pointerDown(e) {
|
|
48
63
|
e.preventDefault();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "canny-ui",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "7.0.0",
|
|
4
4
|
"description": "Pure CSS foundation for the Canny-UI design system — tokens, layout utilities, and a battery of enterprise components, shipped as one flat stylesheet.",
|
|
5
5
|
"main": "dist/canny-ui.min.css",
|
|
6
6
|
"style": "dist/canny-ui.min.css",
|
package/src/_utilities.scss
CHANGED
|
@@ -123,10 +123,10 @@ $spaces: (
|
|
|
123
123
|
.text-secondary { color: var(--text-secondary); }
|
|
124
124
|
.text-muted { color: var(--text-muted); }
|
|
125
125
|
.text-accent { color: var(--text-accent); }
|
|
126
|
-
.text-danger { color: var(--color-danger); }
|
|
127
|
-
.text-success { color: var(--color-success); }
|
|
128
|
-
.text-warning { color: var(--color-warning); }
|
|
129
|
-
.text-info { color: var(--color-info); }
|
|
126
|
+
.text-danger { color: var(--color-danger-text); }
|
|
127
|
+
.text-success { color: var(--color-success-text); }
|
|
128
|
+
.text-warning { color: var(--color-warning-text); }
|
|
129
|
+
.text-info { color: var(--color-info-text); }
|
|
130
130
|
|
|
131
131
|
// ── Primitives ───────────────────────────────────────────────────────────
|
|
132
132
|
.cny-label {
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// ── Reduced motion ───────────────────────────────────────────────
|
|
2
|
+
// One global opt-out for every Canny-UI surface. Durations collapse to ~0
|
|
3
|
+
// (not `none`) so `transitionend` / `animationend` still fire and state
|
|
4
|
+
// still lands on its final value. Component-level rules keep working.
|
|
5
|
+
@media (prefers-reduced-motion: reduce) {
|
|
6
|
+
[class*='cny-'],
|
|
7
|
+
[class*='cny-']::before,
|
|
8
|
+
[class*='cny-']::after,
|
|
9
|
+
.btn,
|
|
10
|
+
.btn::before,
|
|
11
|
+
.btn::after,
|
|
12
|
+
.btn-ripple,
|
|
13
|
+
.checkbox,
|
|
14
|
+
.radio,
|
|
15
|
+
.switch,
|
|
16
|
+
.switch::before,
|
|
17
|
+
.switch-thumb,
|
|
18
|
+
.input {
|
|
19
|
+
animation-duration: 0.01ms !important;
|
|
20
|
+
animation-delay: 0ms !important;
|
|
21
|
+
animation-iteration-count: 1 !important;
|
|
22
|
+
transition-duration: 0.01ms !important;
|
|
23
|
+
transition-delay: 0ms !important;
|
|
24
|
+
scroll-behavior: auto !important;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Scrollbar thumb transitions (see scrollbar-mixin) — pseudo-elements
|
|
28
|
+
// on any scroll container, so they can't be matched by a class prefix.
|
|
29
|
+
*::-webkit-scrollbar-thumb {
|
|
30
|
+
transition-duration: 0.01ms !important;
|
|
31
|
+
}
|
|
32
|
+
}
|
package/src/base/_reset.scss
CHANGED
|
@@ -14,3 +14,10 @@ body {
|
|
|
14
14
|
background: var(--bg-base);
|
|
15
15
|
color: var(--text-primary);
|
|
16
16
|
}
|
|
17
|
+
|
|
18
|
+
// Themed text selection — readable in light and dark (the browser default is a
|
|
19
|
+
// fixed blue that clashes with dark surfaces).
|
|
20
|
+
::selection {
|
|
21
|
+
background: color-mix(in srgb, var(--color-primary) 32%, transparent);
|
|
22
|
+
color: inherit;
|
|
23
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
// ── Expand-on-hover scrollbar ────────────────────────────────────
|
|
2
|
+
// The one scrollbar look used across Canny-UI. A hairline pill at rest that
|
|
3
|
+
// fades back while the container is idle, brightens when the container is
|
|
4
|
+
// hovered, and swells to a chunky grab handle when the
|
|
5
|
+
// pointer reaches the scrollbar itself.
|
|
6
|
+
//
|
|
7
|
+
// Built on ::-webkit-scrollbar, which Chromium ignores on any element whose
|
|
8
|
+
// scrollbar-width / scrollbar-color is not `auto` — so both are reset here.
|
|
9
|
+
// Firefox has no pseudo-element styling; it gets the standard thin scrollbar.
|
|
10
|
+
//
|
|
11
|
+
// Geometry: the 14px gutter is the grab target; the transparent border is
|
|
12
|
+
// what's subtracted from it to get the visible pill. The pill is anchored to
|
|
13
|
+
// the outer edge with a 2px margin: 14 − 8 − 2 = 4px at rest, and the inner
|
|
14
|
+
// border shrinks to 2px on hover, so it grows inward to 14 − 2 − 2 = 10px.
|
|
15
|
+
@mixin expand-scrollbar {
|
|
16
|
+
scrollbar-width: auto;
|
|
17
|
+
scrollbar-color: auto;
|
|
18
|
+
|
|
19
|
+
@supports (-moz-appearance: none) {
|
|
20
|
+
scrollbar-width: thin;
|
|
21
|
+
scrollbar-color: var(--border-color-strong) transparent;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
&::-webkit-scrollbar {
|
|
25
|
+
width: 14px;
|
|
26
|
+
height: 14px;
|
|
27
|
+
background: transparent;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
&::-webkit-scrollbar-corner {
|
|
31
|
+
background: transparent;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
&::-webkit-scrollbar-track {
|
|
35
|
+
background: transparent;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
&::-webkit-scrollbar-thumb {
|
|
39
|
+
border: 0 solid transparent;
|
|
40
|
+
border-radius: var(--radius-full);
|
|
41
|
+
background-clip: content-box;
|
|
42
|
+
background-color: color-mix(in srgb, var(--text-muted) 40%, transparent);
|
|
43
|
+
// Ease-out-quint: fast start, long soft landing — reads as a glide, not a snap.
|
|
44
|
+
transition:
|
|
45
|
+
border-width 0.32s cubic-bezier(0.22, 1, 0.36, 1),
|
|
46
|
+
background-color 0.24s ease-out;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// The pill hugs the outer edge (right for vertical, bottom for horizontal)
|
|
50
|
+
// and grows inward: only the inner border shrinks on hover.
|
|
51
|
+
&::-webkit-scrollbar-thumb:vertical {
|
|
52
|
+
border-width: 3px 2px 3px 8px;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
&::-webkit-scrollbar-thumb:horizontal {
|
|
56
|
+
border-width: 8px 3px 2px;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Container is hovered or being keyboard-scrolled → the pill firms up.
|
|
60
|
+
&:hover::-webkit-scrollbar-thumb,
|
|
61
|
+
&:focus-within::-webkit-scrollbar-thumb {
|
|
62
|
+
background-color: color-mix(in srgb, var(--text-muted) 65%, transparent);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Pointer on the scrollbar → the thumb swells and darkens.
|
|
66
|
+
&::-webkit-scrollbar-thumb:vertical:hover,
|
|
67
|
+
&::-webkit-scrollbar-thumb:vertical:active {
|
|
68
|
+
border-left-width: 2px;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
&::-webkit-scrollbar-thumb:horizontal:hover,
|
|
72
|
+
&::-webkit-scrollbar-thumb:horizontal:active {
|
|
73
|
+
border-top-width: 2px;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
&::-webkit-scrollbar-thumb:hover,
|
|
77
|
+
&::-webkit-scrollbar-thumb:active {
|
|
78
|
+
background-color: var(--text-muted);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
&::-webkit-scrollbar-thumb:active {
|
|
82
|
+
background-color: var(--text-secondary);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// ── Touch targets ────────────────────────────────────────────────
|
|
2
|
+
// Small controls get an invisible >=44px hit area on coarse pointers only;
|
|
3
|
+
// visual size on desktop is untouched. Pseudo-element where the host allows
|
|
4
|
+
// it, a min-size where the host clips overflow (.btn).
|
|
5
|
+
@media (pointer: coarse) {
|
|
6
|
+
// Hit pseudo needs a positioned host.
|
|
7
|
+
.checkbox,
|
|
8
|
+
.radio,
|
|
9
|
+
.switch,
|
|
10
|
+
.cny-slider-thumb,
|
|
11
|
+
.cny-pagination-btn,
|
|
12
|
+
.cny-chip-delete,
|
|
13
|
+
.cny-chip-remove,
|
|
14
|
+
.cny-chip.is-interactive,
|
|
15
|
+
.cny-dialog-close,
|
|
16
|
+
.cny-drawer-close,
|
|
17
|
+
.alert-close,
|
|
18
|
+
.cny-select-clear,
|
|
19
|
+
.cny-ac-clear {
|
|
20
|
+
position: relative;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// .cny-input-action and .cny-toast-close are already positioned; they get only the pseudo below.
|
|
24
|
+
|
|
25
|
+
.checkbox,
|
|
26
|
+
.radio,
|
|
27
|
+
.switch,
|
|
28
|
+
.cny-slider-thumb,
|
|
29
|
+
.cny-pagination-btn,
|
|
30
|
+
.cny-chip-delete,
|
|
31
|
+
.cny-chip-remove,
|
|
32
|
+
.cny-chip.is-interactive,
|
|
33
|
+
.cny-input-action,
|
|
34
|
+
.cny-dialog-close,
|
|
35
|
+
.cny-drawer-close,
|
|
36
|
+
.alert-close,
|
|
37
|
+
.cny-toast-close,
|
|
38
|
+
.cny-select-clear,
|
|
39
|
+
.cny-multiselect-clear,
|
|
40
|
+
.cny-ac-clear {
|
|
41
|
+
&::after {
|
|
42
|
+
content: '';
|
|
43
|
+
position: absolute;
|
|
44
|
+
top: 50%;
|
|
45
|
+
left: 50%;
|
|
46
|
+
width: max(100%, 44px);
|
|
47
|
+
height: max(100%, 44px);
|
|
48
|
+
transform: translate(-50%, -50%);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// .btn clips overflow for the ripple — grow the box instead. Link buttons are inline text.
|
|
53
|
+
.btn:not(.link) { min-height: 44px; }
|
|
54
|
+
.btn.icon,
|
|
55
|
+
.btn.circle { min-width: 44px; }
|
|
56
|
+
|
|
57
|
+
// Neighbouring cells/rows would fight over an overlapping hit pseudo — grow the box.
|
|
58
|
+
.cny-datepicker-cell { height: 44px; }
|
|
59
|
+
.cny-menu-item { min-height: 44px; }
|
|
60
|
+
}
|
package/src/canny-ui.scss
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
@use 'tokens/semantic';
|
|
4
4
|
@use 'base/reset';
|
|
5
5
|
@use 'base/typography';
|
|
6
|
+
@use 'base/motion';
|
|
6
7
|
@use 'components/buttons';
|
|
7
8
|
@use 'components/inputs';
|
|
8
9
|
@use 'components/cards';
|
|
@@ -35,6 +36,8 @@
|
|
|
35
36
|
@use 'components/panels';
|
|
36
37
|
@use 'components/datatable';
|
|
37
38
|
@use 'components/charts';
|
|
39
|
+
@use 'components/gantt';
|
|
40
|
+
@use 'components/charts-extra';
|
|
38
41
|
@use 'components/list';
|
|
39
42
|
@use 'components/tabs';
|
|
40
43
|
@use 'components/autocomplete';
|
|
@@ -43,4 +46,6 @@
|
|
|
43
46
|
@use 'components/accordion';
|
|
44
47
|
@use 'components/slider';
|
|
45
48
|
@use 'components/stepper';
|
|
49
|
+
// Touch targets override component sizes, so they load after every component.
|
|
50
|
+
@use 'base/touch';
|
|
46
51
|
@use 'utilities';
|
|
@@ -44,10 +44,19 @@
|
|
|
44
44
|
|
|
45
45
|
&:hover { background: var(--bg-surface-low); }
|
|
46
46
|
&:focus-visible {
|
|
47
|
-
outline: 2px solid var(--
|
|
47
|
+
outline: 2px solid var(--border-focus);
|
|
48
48
|
outline-offset: -2px;
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
+
&:disabled,
|
|
52
|
+
&[data-disabled] {
|
|
53
|
+
color: var(--text-disabled);
|
|
54
|
+
cursor: not-allowed;
|
|
55
|
+
&:hover { background: transparent; }
|
|
56
|
+
.cny-accordion-trigger-icon,
|
|
57
|
+
.cny-accordion-trigger-chevron { color: var(--text-disabled); }
|
|
58
|
+
}
|
|
59
|
+
|
|
51
60
|
&-icon {
|
|
52
61
|
display: inline-flex;
|
|
53
62
|
align-items: center;
|
|
@@ -8,6 +8,10 @@
|
|
|
8
8
|
to { opacity: 1; transform: translateY(0); }
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
+
@keyframes alert-exit {
|
|
12
|
+
to { opacity: 0; transform: translateY(-4px) scale(0.98); }
|
|
13
|
+
}
|
|
14
|
+
|
|
11
15
|
.alert {
|
|
12
16
|
--alert-accent: var(--color-info);
|
|
13
17
|
--alert-accent-soft: var(--color-info-soft);
|
|
@@ -31,6 +35,12 @@
|
|
|
31
35
|
animation: none;
|
|
32
36
|
}
|
|
33
37
|
|
|
38
|
+
// dismiss: short fade-out before the consumer's onDismiss unmounts it
|
|
39
|
+
&.is-leaving {
|
|
40
|
+
animation: alert-exit 160ms ease-in forwards;
|
|
41
|
+
pointer-events: none;
|
|
42
|
+
}
|
|
43
|
+
|
|
34
44
|
&-icon {
|
|
35
45
|
flex: none;
|
|
36
46
|
display: inline-flex;
|
|
@@ -91,7 +101,7 @@
|
|
|
91
101
|
background: var(--bg-surface-low);
|
|
92
102
|
}
|
|
93
103
|
&:focus-visible {
|
|
94
|
-
outline: 2px solid var(--
|
|
104
|
+
outline: 2px solid var(--border-focus);
|
|
95
105
|
outline-offset: 1px;
|
|
96
106
|
}
|
|
97
107
|
}
|
|
@@ -103,7 +113,7 @@
|
|
|
103
113
|
--alert-accent-on: var(--color-info-on);
|
|
104
114
|
background: var(--color-info-soft);
|
|
105
115
|
border-color: color-mix(in srgb, var(--color-info) 22%, transparent);
|
|
106
|
-
.alert-title { color: var(--color-info); }
|
|
116
|
+
.alert-title { color: var(--color-info-text); }
|
|
107
117
|
}
|
|
108
118
|
&.success {
|
|
109
119
|
--alert-accent: var(--color-success);
|
|
@@ -111,7 +121,7 @@
|
|
|
111
121
|
--alert-accent-on: var(--color-success-on);
|
|
112
122
|
background: var(--color-success-soft);
|
|
113
123
|
border-color: color-mix(in srgb, var(--color-success) 22%, transparent);
|
|
114
|
-
.alert-title { color: var(--color-success); }
|
|
124
|
+
.alert-title { color: var(--color-success-text); }
|
|
115
125
|
}
|
|
116
126
|
&.warning {
|
|
117
127
|
--alert-accent: var(--color-warning);
|
|
@@ -119,7 +129,7 @@
|
|
|
119
129
|
--alert-accent-on: var(--color-warning-on);
|
|
120
130
|
background: var(--color-warning-soft);
|
|
121
131
|
border-color: color-mix(in srgb, var(--color-warning) 24%, transparent);
|
|
122
|
-
.alert-title { color: var(--color-warning); }
|
|
132
|
+
.alert-title { color: var(--color-warning-text); }
|
|
123
133
|
}
|
|
124
134
|
&.danger {
|
|
125
135
|
--alert-accent: var(--color-danger);
|
|
@@ -127,7 +137,7 @@
|
|
|
127
137
|
--alert-accent-on: var(--color-danger-on);
|
|
128
138
|
background: var(--color-danger-soft);
|
|
129
139
|
border-color: color-mix(in srgb, var(--color-danger) 26%, transparent);
|
|
130
|
-
.alert-title { color: var(--color-danger); }
|
|
140
|
+
.alert-title { color: var(--color-danger-text); }
|
|
131
141
|
}
|
|
132
142
|
|
|
133
143
|
// Solid variant — flat accent fill
|
|
@@ -140,15 +150,15 @@
|
|
|
140
150
|
.alert-body { color: var(--alert-accent-on); opacity: 0.88; }
|
|
141
151
|
|
|
142
152
|
.alert-icon {
|
|
143
|
-
background:
|
|
153
|
+
background: color-mix(in srgb, var(--alert-accent-on) 18%, transparent);
|
|
144
154
|
color: var(--alert-accent-on);
|
|
145
155
|
}
|
|
146
156
|
|
|
147
157
|
.alert-close {
|
|
148
158
|
color: var(--alert-accent-on);
|
|
149
159
|
opacity: 0.78;
|
|
150
|
-
&:hover { opacity: 1; background:
|
|
151
|
-
&:focus-visible { outline-color:
|
|
160
|
+
&:hover { opacity: 1; background: color-mix(in srgb, var(--alert-accent-on) 14%, transparent); }
|
|
161
|
+
&:focus-visible { outline-color: var(--alert-accent-on); }
|
|
152
162
|
}
|
|
153
163
|
}
|
|
154
164
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
@use '../base/scrollbar-mixin' as sb;
|
|
1
2
|
@use '../tokens/primitives' as *;
|
|
2
3
|
|
|
3
4
|
// Autocomplete — searchable combobox built on cmdk + Radix Popover.
|
|
@@ -18,7 +19,7 @@
|
|
|
18
19
|
cursor: pointer;
|
|
19
20
|
padding: 0 $space-3;
|
|
20
21
|
|
|
21
|
-
&.is-open { border-color: var(--border-focus); }
|
|
22
|
+
&.is-open { border-color: var(--border-input-focus); }
|
|
22
23
|
&[disabled] { cursor: not-allowed; opacity: 0.6; }
|
|
23
24
|
}
|
|
24
25
|
|
|
@@ -49,8 +50,8 @@
|
|
|
49
50
|
|
|
50
51
|
&:hover { color: var(--text-primary); background: var(--bg-surface-low); }
|
|
51
52
|
&:focus-visible {
|
|
52
|
-
outline:
|
|
53
|
-
|
|
53
|
+
outline: var(--ring-width) solid var(--ring-color);
|
|
54
|
+
outline-offset: var(--ring-offset);
|
|
54
55
|
}
|
|
55
56
|
}
|
|
56
57
|
|
|
@@ -61,13 +62,11 @@
|
|
|
61
62
|
|
|
62
63
|
// ── Popover content ──────────────────────────────────────────
|
|
63
64
|
.cny-ac-content {
|
|
64
|
-
z-index: 1010;
|
|
65
|
+
z-index: var(--z-popover, 1010);
|
|
65
66
|
background: var(--bg-surface-lowest);
|
|
66
|
-
border: 1px solid var(--border-color);
|
|
67
|
+
border: 1px solid var(--border-color-strong);
|
|
67
68
|
border-radius: var(--radius-sm);
|
|
68
|
-
box-shadow:
|
|
69
|
-
0 1px 0 rgba(15, 23, 42, 0.04),
|
|
70
|
-
0 12px 28px -12px rgba(15, 23, 42, 0.22);
|
|
69
|
+
box-shadow: var(--shadow-popover);
|
|
71
70
|
overflow: hidden;
|
|
72
71
|
font-family: var(--font-body);
|
|
73
72
|
animation: cny-ac-in 140ms cubic-bezier(0.2, 0.8, 0.2, 1);
|
|
@@ -98,7 +97,7 @@
|
|
|
98
97
|
overflow-y: auto;
|
|
99
98
|
max-height: 280px;
|
|
100
99
|
padding: 6px;
|
|
101
|
-
scrollbar
|
|
100
|
+
@include sb.expand-scrollbar;
|
|
102
101
|
}
|
|
103
102
|
|
|
104
103
|
.cny-ac-empty,
|
|
@@ -164,7 +163,7 @@
|
|
|
164
163
|
justify-content: center;
|
|
165
164
|
width: 16px;
|
|
166
165
|
height: 16px;
|
|
167
|
-
color: var(--
|
|
166
|
+
color: var(--text-accent);
|
|
168
167
|
}
|
|
169
168
|
|
|
170
169
|
.cny-ac-checkbox {
|
|
@@ -190,3 +189,10 @@
|
|
|
190
189
|
.cny-ac-content { animation: none; }
|
|
191
190
|
.cny-ac-loading-spinner { animation: none; }
|
|
192
191
|
}
|
|
192
|
+
|
|
193
|
+
// Matched substring inside an option label (`highlightMatch`)
|
|
194
|
+
.cny-ac-mark {
|
|
195
|
+
background: transparent;
|
|
196
|
+
color: var(--text-accent);
|
|
197
|
+
font-weight: 600;
|
|
198
|
+
}
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
width: 32px;
|
|
12
12
|
height: 32px;
|
|
13
13
|
background: var(--color-primary-soft);
|
|
14
|
-
color: var(--
|
|
14
|
+
color: var(--text-accent);
|
|
15
15
|
border-radius: 50%;
|
|
16
16
|
font-family: var(--font-body);
|
|
17
17
|
font-size: 0.8125rem;
|
|
@@ -29,6 +29,21 @@
|
|
|
29
29
|
border-radius: inherit;
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
// Empty avatar (no image / name / children): a person glyph instead of a blank disc.
|
|
33
|
+
.avatar-fallback {
|
|
34
|
+
display: inline-flex;
|
|
35
|
+
width: 58%;
|
|
36
|
+
height: 58%;
|
|
37
|
+
color: inherit;
|
|
38
|
+
opacity: 0.8;
|
|
39
|
+
|
|
40
|
+
svg {
|
|
41
|
+
width: 100%;
|
|
42
|
+
height: 100%;
|
|
43
|
+
border-radius: 0;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
32
47
|
// Presence dot — absolutely positioned, escapes the circle by ~15%
|
|
33
48
|
// and is ringed in the page surface color so it reads on any background.
|
|
34
49
|
&.online::after,
|
|
@@ -90,3 +105,9 @@
|
|
|
90
105
|
box-shadow: 0 0 0 2px var(--bg-surface-lowest);
|
|
91
106
|
}
|
|
92
107
|
}
|
|
108
|
+
|
|
109
|
+
// "+N" overflow avatar in an AvatarGroup
|
|
110
|
+
.avatar.avatar-overflow {
|
|
111
|
+
background: var(--bg-surface-low);
|
|
112
|
+
color: var(--text-muted);
|
|
113
|
+
}
|
|
@@ -33,8 +33,24 @@
|
|
|
33
33
|
transition: color 120ms ease, background-color 120ms ease;
|
|
34
34
|
&:hover { color: var(--text-primary); background: var(--bg-surface-low); }
|
|
35
35
|
&:focus-visible {
|
|
36
|
-
outline: 2px solid var(--
|
|
37
|
-
outline-offset:
|
|
36
|
+
outline: 2px solid var(--border-focus);
|
|
37
|
+
outline-offset: 2px;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
&-ellipsis {
|
|
42
|
+
display: inline-flex;
|
|
43
|
+
align-items: center;
|
|
44
|
+
padding: 2px 4px;
|
|
45
|
+
background: transparent;
|
|
46
|
+
border: 0;
|
|
47
|
+
border-radius: var(--radius-sm);
|
|
48
|
+
color: var(--text-secondary);
|
|
49
|
+
cursor: pointer;
|
|
50
|
+
&:hover { color: var(--text-primary); background: var(--bg-surface-low); }
|
|
51
|
+
&:focus-visible {
|
|
52
|
+
outline: 2px solid var(--border-focus);
|
|
53
|
+
outline-offset: 2px;
|
|
38
54
|
}
|
|
39
55
|
}
|
|
40
56
|
|
|
@@ -54,3 +70,21 @@
|
|
|
54
70
|
.cny-breadcrumb-list { gap: 4px; }
|
|
55
71
|
}
|
|
56
72
|
}
|
|
73
|
+
|
|
74
|
+
// Coarse pointers: invisible >=44px hit area around the small link / ellipsis targets.
|
|
75
|
+
@media (pointer: coarse) {
|
|
76
|
+
.cny-breadcrumb-link,
|
|
77
|
+
.cny-breadcrumb-ellipsis {
|
|
78
|
+
position: relative;
|
|
79
|
+
|
|
80
|
+
&::after {
|
|
81
|
+
content: '';
|
|
82
|
+
position: absolute;
|
|
83
|
+
top: 50%;
|
|
84
|
+
left: 50%;
|
|
85
|
+
width: max(100%, 44px);
|
|
86
|
+
height: 44px;
|
|
87
|
+
transform: translate(-50%, -50%);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
@@ -30,6 +30,8 @@
|
|
|
30
30
|
cursor: pointer;
|
|
31
31
|
user-select: none;
|
|
32
32
|
white-space: nowrap;
|
|
33
|
+
max-width: 100%;
|
|
34
|
+
min-width: 0;
|
|
33
35
|
-webkit-tap-highlight-color: transparent;
|
|
34
36
|
transition:
|
|
35
37
|
background-color var(--transition-fast),
|
|
@@ -45,8 +47,8 @@
|
|
|
45
47
|
}
|
|
46
48
|
|
|
47
49
|
&:focus-visible {
|
|
48
|
-
outline:
|
|
49
|
-
|
|
50
|
+
outline: var(--ring-width) solid var(--ring-color);
|
|
51
|
+
outline-offset: var(--ring-offset);
|
|
50
52
|
}
|
|
51
53
|
|
|
52
54
|
// ── Content wrapper — keeps children above the ripple layer ───────────
|
|
@@ -57,6 +59,15 @@
|
|
|
57
59
|
align-items: center;
|
|
58
60
|
justify-content: center;
|
|
59
61
|
gap: inherit;
|
|
62
|
+
min-width: 0;
|
|
63
|
+
max-width: 100%;
|
|
64
|
+
|
|
65
|
+
// A long label truncates instead of overflowing the button.
|
|
66
|
+
> .btn-label {
|
|
67
|
+
min-width: 0;
|
|
68
|
+
overflow: hidden;
|
|
69
|
+
text-overflow: ellipsis;
|
|
70
|
+
}
|
|
60
71
|
|
|
61
72
|
// An icon is a flex item here, so it inherits flex-shrink: 1 and squashes
|
|
62
73
|
// out of square as soon as the label needs the room — a 15px lucide icon
|
|
@@ -144,10 +155,10 @@
|
|
|
144
155
|
box-shadow: inset 0 0 0 1px var(--border-color-strong), inset 0 1px 0 0 rgba(0, 0, 0, 0.04);
|
|
145
156
|
}
|
|
146
157
|
|
|
147
|
-
&.danger { color: var(--color-danger); }
|
|
148
|
-
&.warning { color: var(--color-warning); }
|
|
149
|
-
&.success { color: var(--color-success); }
|
|
150
|
-
&.info { color: var(--color-info); }
|
|
158
|
+
&.danger { color: var(--color-danger-text); }
|
|
159
|
+
&.warning { color: var(--color-warning-text); }
|
|
160
|
+
&.success { color: var(--color-success-text); }
|
|
161
|
+
&.info { color: var(--color-info-text); }
|
|
151
162
|
}
|
|
152
163
|
|
|
153
164
|
// ── Dashed — Ant Design-style, border intent-colors on hover ───────────
|
|
@@ -164,17 +175,17 @@
|
|
|
164
175
|
&:hover:not(:disabled) {
|
|
165
176
|
background: var(--bg-surface-low);
|
|
166
177
|
border-color: var(--color-primary);
|
|
167
|
-
color: var(--
|
|
178
|
+
color: var(--text-accent);
|
|
168
179
|
}
|
|
169
180
|
&:active:not(:disabled) {
|
|
170
181
|
background: var(--bg-surface-edge);
|
|
171
182
|
border-color: var(--color-primary-active);
|
|
172
183
|
}
|
|
173
184
|
|
|
174
|
-
&.danger { &:hover:not(:disabled) { border-color: var(--color-danger); color: var(--color-danger); } }
|
|
175
|
-
&.warning { &:hover:not(:disabled) { border-color: var(--color-warning); color: var(--color-warning); } }
|
|
176
|
-
&.success { &:hover:not(:disabled) { border-color: var(--color-success); color: var(--color-success); } }
|
|
177
|
-
&.info { &:hover:not(:disabled) { border-color: var(--color-info); color: var(--color-info); } }
|
|
185
|
+
&.danger { &:hover:not(:disabled) { border-color: var(--color-danger); color: var(--color-danger-text); } }
|
|
186
|
+
&.warning { &:hover:not(:disabled) { border-color: var(--color-warning); color: var(--color-warning-text); } }
|
|
187
|
+
&.success { &:hover:not(:disabled) { border-color: var(--color-success); color: var(--color-success-text); } }
|
|
188
|
+
&.info { &:hover:not(:disabled) { border-color: var(--color-info); color: var(--color-info-text); } }
|
|
178
189
|
}
|
|
179
190
|
|
|
180
191
|
// ── Ghost — text-only, tints on hover ──────────────────────────────────
|
|
@@ -185,23 +196,23 @@
|
|
|
185
196
|
&:hover:not(:disabled) { background: var(--bg-surface-low); }
|
|
186
197
|
&:active:not(:disabled) { background: var(--bg-surface-edge); }
|
|
187
198
|
|
|
188
|
-
&.danger { color: var(--color-danger); &:hover:not(:disabled) { background: var(--color-danger-soft); } }
|
|
189
|
-
&.warning { color: var(--color-warning); &:hover:not(:disabled) { background: var(--color-warning-soft); } }
|
|
190
|
-
&.success { color: var(--color-success); &:hover:not(:disabled) { background: var(--color-success-soft); } }
|
|
191
|
-
&.info { color: var(--color-info); &:hover:not(:disabled) { background: var(--color-info-soft); } }
|
|
199
|
+
&.danger { color: var(--color-danger-text); &:hover:not(:disabled) { background: var(--color-danger-soft); } }
|
|
200
|
+
&.warning { color: var(--color-warning-text); &:hover:not(:disabled) { background: var(--color-warning-soft); } }
|
|
201
|
+
&.success { color: var(--color-success-text); &:hover:not(:disabled) { background: var(--color-success-soft); } }
|
|
202
|
+
&.info { color: var(--color-info-text); &:hover:not(:disabled) { background: var(--color-info-soft); } }
|
|
192
203
|
}
|
|
193
204
|
|
|
194
205
|
// ── Soft — tonal fill, gentle presence ─────────────────────────────────
|
|
195
206
|
&.soft {
|
|
196
207
|
background: var(--color-primary-soft);
|
|
197
|
-
color: var(--
|
|
208
|
+
color: var(--text-accent);
|
|
198
209
|
|
|
199
210
|
&:hover:not(:disabled) { background: var(--bg-surface-edge); }
|
|
200
211
|
|
|
201
|
-
&.danger { background: var(--color-danger-soft); color: var(--color-danger); }
|
|
202
|
-
&.warning { background: var(--color-warning-soft); color: var(--color-warning); }
|
|
203
|
-
&.success { background: var(--color-success-soft); color: var(--color-success); }
|
|
204
|
-
&.info { background: var(--color-info-soft); color: var(--color-info); }
|
|
212
|
+
&.danger { background: var(--color-danger-soft); color: var(--color-danger-text); }
|
|
213
|
+
&.warning { background: var(--color-warning-soft); color: var(--color-warning-text); }
|
|
214
|
+
&.success { background: var(--color-success-soft); color: var(--color-success-text); }
|
|
215
|
+
&.info { background: var(--color-info-soft); color: var(--color-info-text); }
|
|
205
216
|
}
|
|
206
217
|
|
|
207
218
|
// ── Link — inline text action (no ripple, no overflow clip) ────────────
|
|
@@ -216,7 +227,7 @@
|
|
|
216
227
|
text-decoration-thickness: 1px;
|
|
217
228
|
text-underline-offset: 0.2em;
|
|
218
229
|
|
|
219
|
-
&:hover:not(:disabled) { color: var(--color-primary-
|
|
230
|
+
&:hover:not(:disabled) { color: var(--color-primary-active); }
|
|
220
231
|
|
|
221
232
|
.btn-ripple { display: none; }
|
|
222
233
|
}
|
|
@@ -269,10 +280,10 @@
|
|
|
269
280
|
// ── Toggle (pressed) state ─ aria-pressed="true" driven by Radix Toggle
|
|
270
281
|
&[aria-pressed='true'],
|
|
271
282
|
&[data-state='on'] {
|
|
272
|
-
&.ghost { background: var(--color-primary-soft); color: var(--
|
|
273
|
-
&.outline { background: var(--color-primary-soft); color: var(--
|
|
283
|
+
&.ghost { background: var(--color-primary-soft); color: var(--text-accent); }
|
|
284
|
+
&.outline { background: var(--color-primary-soft); color: var(--text-accent); box-shadow: inset 0 0 0 1px var(--color-primary), var(--shadow-xs); }
|
|
274
285
|
&.soft { background: var(--color-primary); color: var(--color-primary-on); }
|
|
275
|
-
&.dashed { background: var(--color-primary-soft); color: var(--
|
|
286
|
+
&.dashed { background: var(--color-primary-soft); color: var(--text-accent); border-color: var(--color-primary); }
|
|
276
287
|
}
|
|
277
288
|
}
|
|
278
289
|
|