@apliteni/apliteni-ui 0.4.0 → 0.6.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/package.json +8 -3
- package/src/assets/brand.generated/apliteni-logo-dark.svg +27 -0
- package/src/assets/brand.generated/apliteni-logo.svg +27 -0
- package/src/assets/brand.generated/apliteni-mark.svg +19 -0
- package/src/assets/brand.generated/index.js +5 -0
- package/src/assets/icons.js +106 -24
- package/src/assets/illustrations.js +68 -0
- package/src/components/drawer.js +200 -0
- package/src/components/dropdown.js +229 -0
- package/src/components/feedback.js +6 -0
- package/src/components/footer.js +95 -0
- package/src/components/index.js +46 -26
- package/src/components/nav.js +190 -0
- package/src/components/success.js +126 -0
- package/src/components/toasts.js +94 -0
- package/src/components/topbar.js +16 -15
- package/src/index.css +8 -1
- package/src/index.js +5 -0
- package/src/inline.js +19 -5
- package/src/motion.js +68 -0
- package/src/styles/base.css +0 -6
- package/src/styles/button.css +40 -11
- package/src/styles/callout.css +79 -12
- package/src/styles/drawer.css +166 -0
- package/src/styles/dropdown.css +133 -0
- package/src/styles/empty.css +84 -0
- package/src/styles/feedback.css +1 -1
- package/src/styles/footer.css +123 -0
- package/src/styles/motion.css +166 -0
- package/src/styles/nav.css +218 -0
- package/src/styles/success.css +189 -0
- package/src/styles/table.css +2 -15
- package/src/tokens/brand.generated.css +121 -0
- package/src/tokens/tokens.css +18 -5
- package/src/styles/aurora.css +0 -93
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
/* ============================================================================
|
|
2
|
+
* apliteni-ui — MOTION LIBRARY
|
|
3
|
+
*
|
|
4
|
+
* A small, token-driven, framework-agnostic motion system: reusable entrance,
|
|
5
|
+
* micro-interaction, attention, and scroll-reveal effects as plain classes.
|
|
6
|
+
* Every timing reads the brand motion vocabulary (--duration-* / --easing-*,
|
|
7
|
+
* synced from design-system) so motion re-themes and stays consistent.
|
|
8
|
+
*
|
|
9
|
+
* Per-effect overrides via custom properties on the element:
|
|
10
|
+
* --m-dur duration (default: per effect)
|
|
11
|
+
* --m-ease timing function (default: per effect)
|
|
12
|
+
* --m-delay start delay (default: 0s)
|
|
13
|
+
* --m-dist travel distance for slides (default: 16px)
|
|
14
|
+
*
|
|
15
|
+
* Scroll reveals need one line of JS — the optional `initReveal()` hook from
|
|
16
|
+
* apliteni-ui/motion. Without JS, [data-reveal] content stays fully visible
|
|
17
|
+
* (progressive enhancement, never hidden behind a script that didn't run).
|
|
18
|
+
*
|
|
19
|
+
* A single global prefers-reduced-motion block at the end neutralises ALL of
|
|
20
|
+
* the above — plus any component animation in the kit — as the safety net.
|
|
21
|
+
* ========================================================================== */
|
|
22
|
+
|
|
23
|
+
/* -- Entrances -------------------------------------------------------------
|
|
24
|
+
* `both` fill so the element starts at the "from" frame (hidden) and holds
|
|
25
|
+
* the "to" frame. Compose with a delay for staggered groups. */
|
|
26
|
+
.m-fade-in,
|
|
27
|
+
.m-slide-up,
|
|
28
|
+
.m-slide-down,
|
|
29
|
+
.m-slide-left,
|
|
30
|
+
.m-slide-right,
|
|
31
|
+
.m-scale-in,
|
|
32
|
+
.m-blur-in {
|
|
33
|
+
animation-duration: var(--m-dur, var(--dur-slow));
|
|
34
|
+
animation-timing-function: var(--m-ease, var(--easing-ease-out, cubic-bezier(0, 0, 0.2, 1)));
|
|
35
|
+
animation-delay: var(--m-delay, 0s);
|
|
36
|
+
animation-fill-mode: both;
|
|
37
|
+
}
|
|
38
|
+
.m-fade-in { animation-name: m-fade; }
|
|
39
|
+
.m-slide-up { animation-name: m-slide-up; }
|
|
40
|
+
.m-slide-down { animation-name: m-slide-down; }
|
|
41
|
+
.m-slide-left { animation-name: m-slide-left; }
|
|
42
|
+
.m-slide-right{ animation-name: m-slide-right; }
|
|
43
|
+
.m-scale-in { animation-name: m-scale-in; }
|
|
44
|
+
.m-blur-in { animation-name: m-blur-in; }
|
|
45
|
+
|
|
46
|
+
@keyframes m-fade { from { opacity: 0; } to { opacity: 1; } }
|
|
47
|
+
@keyframes m-slide-up {
|
|
48
|
+
from { opacity: 0; transform: translateY(var(--m-dist, 16px)); }
|
|
49
|
+
to { opacity: 1; transform: none; }
|
|
50
|
+
}
|
|
51
|
+
@keyframes m-slide-down {
|
|
52
|
+
from { opacity: 0; transform: translateY(calc(-1 * var(--m-dist, 16px))); }
|
|
53
|
+
to { opacity: 1; transform: none; }
|
|
54
|
+
}
|
|
55
|
+
@keyframes m-slide-left {
|
|
56
|
+
from { opacity: 0; transform: translateX(var(--m-dist, 16px)); }
|
|
57
|
+
to { opacity: 1; transform: none; }
|
|
58
|
+
}
|
|
59
|
+
@keyframes m-slide-right {
|
|
60
|
+
from { opacity: 0; transform: translateX(calc(-1 * var(--m-dist, 16px))); }
|
|
61
|
+
to { opacity: 1; transform: none; }
|
|
62
|
+
}
|
|
63
|
+
@keyframes m-scale-in {
|
|
64
|
+
from { opacity: 0; transform: scale(0.94); }
|
|
65
|
+
to { opacity: 1; transform: none; }
|
|
66
|
+
}
|
|
67
|
+
@keyframes m-blur-in {
|
|
68
|
+
from { opacity: 0; filter: blur(8px); }
|
|
69
|
+
to { opacity: 1; filter: blur(0); }
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/* -- Micro-interactions ---------------------------------------------------- */
|
|
73
|
+
.m-lift {
|
|
74
|
+
transition: transform var(--dur-fast) var(--ease), box-shadow var(--dur-med) var(--ease);
|
|
75
|
+
}
|
|
76
|
+
.m-lift:hover {
|
|
77
|
+
transform: translateY(-3px);
|
|
78
|
+
box-shadow: var(--shadow-md, 0 8px 24px rgba(0, 0, 0, 0.18));
|
|
79
|
+
}
|
|
80
|
+
.m-press {
|
|
81
|
+
transition: transform var(--duration-instant, 80ms) var(--easing-sharp, cubic-bezier(0.4, 0, 0.6, 1));
|
|
82
|
+
}
|
|
83
|
+
.m-press:active { transform: scale(0.97); }
|
|
84
|
+
|
|
85
|
+
/* Shimmer / skeleton — a loading placeholder that reads the accent-neutral. */
|
|
86
|
+
.m-skeleton {
|
|
87
|
+
position: relative;
|
|
88
|
+
overflow: hidden;
|
|
89
|
+
background: color-mix(in srgb, var(--text) 8%, transparent);
|
|
90
|
+
border-radius: var(--radius-sm, 8px);
|
|
91
|
+
}
|
|
92
|
+
.m-skeleton::after {
|
|
93
|
+
content: "";
|
|
94
|
+
position: absolute;
|
|
95
|
+
inset: 0;
|
|
96
|
+
transform: translateX(-100%);
|
|
97
|
+
background: linear-gradient(
|
|
98
|
+
90deg,
|
|
99
|
+
transparent,
|
|
100
|
+
color-mix(in srgb, var(--text) 12%, transparent),
|
|
101
|
+
transparent
|
|
102
|
+
);
|
|
103
|
+
animation: m-shimmer var(--duration-crawl, 1s) var(--easing-linear, linear) infinite;
|
|
104
|
+
}
|
|
105
|
+
@keyframes m-shimmer { to { transform: translateX(100%); } }
|
|
106
|
+
|
|
107
|
+
/* -- Attention ------------------------------------------------------------- */
|
|
108
|
+
.m-pulse { animation: m-pulse 2s var(--ease) infinite; }
|
|
109
|
+
@keyframes m-pulse {
|
|
110
|
+
0%, 100% { transform: scale(1); opacity: 1; }
|
|
111
|
+
50% { transform: scale(1.04); opacity: 0.86; }
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.m-shake { animation: m-shake var(--dur-slow) var(--ease) both; }
|
|
115
|
+
@keyframes m-shake {
|
|
116
|
+
0%, 100% { transform: translateX(0); }
|
|
117
|
+
20% { transform: translateX(-6px); }
|
|
118
|
+
40% { transform: translateX(5px); }
|
|
119
|
+
60% { transform: translateX(-3px); }
|
|
120
|
+
80% { transform: translateX(2px); }
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/* Success draw-in — put on an SVG; strokes draw themselves. Set --m-len to the
|
|
124
|
+
* path length (getTotalLength) for an exact draw; 48 is a sane default. */
|
|
125
|
+
.m-draw path,
|
|
126
|
+
.m-draw [data-draw] {
|
|
127
|
+
stroke-dasharray: var(--m-len, 48);
|
|
128
|
+
stroke-dashoffset: var(--m-len, 48);
|
|
129
|
+
animation: m-draw var(--dur-slow) var(--easing-ease-out, cubic-bezier(0, 0, 0.2, 1)) forwards;
|
|
130
|
+
animation-delay: var(--m-delay, 0s);
|
|
131
|
+
}
|
|
132
|
+
@keyframes m-draw { to { stroke-dashoffset: 0; } }
|
|
133
|
+
|
|
134
|
+
/* -- Scroll reveals --------------------------------------------------------
|
|
135
|
+
* Progressive enhancement: [data-reveal] is only hidden once JS marks the
|
|
136
|
+
* document (<html class="js-reveal">). initReveal() then adds .is-revealed as
|
|
137
|
+
* each element enters the viewport. Stagger a group with --reveal-i (0,1,2…).
|
|
138
|
+
* If JS never runs, content shows normally — nothing is trapped hidden. */
|
|
139
|
+
.js-reveal [data-reveal] {
|
|
140
|
+
opacity: 0;
|
|
141
|
+
transform: translateY(18px);
|
|
142
|
+
transition:
|
|
143
|
+
opacity var(--dur-slow) var(--easing-ease-out, cubic-bezier(0, 0, 0.2, 1)),
|
|
144
|
+
transform var(--dur-slow) var(--easing-ease-out, cubic-bezier(0, 0, 0.2, 1));
|
|
145
|
+
transition-delay: calc(var(--reveal-i, 0) * var(--delay-2));
|
|
146
|
+
}
|
|
147
|
+
.js-reveal [data-reveal].is-revealed {
|
|
148
|
+
opacity: 1;
|
|
149
|
+
transform: none;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/* -- Global reduced-motion safety net -------------------------------------
|
|
153
|
+
* Neutralises every animation/transition in the kit — the library's effects,
|
|
154
|
+
* component motion (button loader, badge pulse, feedback check), and smooth
|
|
155
|
+
* scroll. iteration-count:1 lets one-shot animations settle on their final
|
|
156
|
+
* frame (e.g. a checkmark stays drawn) instead of snapping to the start. */
|
|
157
|
+
@media (prefers-reduced-motion: reduce) {
|
|
158
|
+
html { scroll-behavior: auto !important; }
|
|
159
|
+
*, ::before, ::after {
|
|
160
|
+
animation-duration: 0.01ms !important;
|
|
161
|
+
animation-iteration-count: 1 !important;
|
|
162
|
+
transition-duration: 0.01ms !important;
|
|
163
|
+
scroll-behavior: auto !important;
|
|
164
|
+
}
|
|
165
|
+
[data-reveal] { opacity: 1 !important; transform: none !important; }
|
|
166
|
+
}
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
/* ============================================================================
|
|
2
|
+
* Navigation — sidebar rail, horizontal tabs, breadcrumbs. One token-driven
|
|
3
|
+
* class family (ui-nav__…) so every variant re-themes across accents +
|
|
4
|
+
* light/dark. Mirrors the look the app shells draw inline (.ui-side) but as a
|
|
5
|
+
* first-class, standalone component. Active/hover surfaces are pure tokens.
|
|
6
|
+
* ========================================================================== */
|
|
7
|
+
|
|
8
|
+
.ui-nav { font-family: var(--font-sans); }
|
|
9
|
+
.ui-nav ul, .ui-nav ol { list-style: none; margin: 0; padding: 0; }
|
|
10
|
+
|
|
11
|
+
/* -- Sidebar rail -------------------------------------------------------- */
|
|
12
|
+
.ui-nav--side {
|
|
13
|
+
display: flex;
|
|
14
|
+
flex-direction: column;
|
|
15
|
+
gap: 4px;
|
|
16
|
+
width: 216px;
|
|
17
|
+
}
|
|
18
|
+
.ui-nav__section + .ui-nav__section { margin-top: 14px; }
|
|
19
|
+
.ui-nav__list { display: flex; flex-direction: column; gap: 2px; }
|
|
20
|
+
.ui-nav__cap {
|
|
21
|
+
font-size: 11px;
|
|
22
|
+
font-weight: 600;
|
|
23
|
+
letter-spacing: 0.16em;
|
|
24
|
+
text-transform: uppercase;
|
|
25
|
+
color: var(--muted);
|
|
26
|
+
padding: 4px 12px 8px;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/* Item row — link, group toggle, and leaf share the same skin */
|
|
30
|
+
.ui-nav__item {
|
|
31
|
+
position: relative;
|
|
32
|
+
display: flex;
|
|
33
|
+
align-items: center;
|
|
34
|
+
gap: 11px;
|
|
35
|
+
width: 100%;
|
|
36
|
+
padding: 9px 12px;
|
|
37
|
+
border-radius: 10px;
|
|
38
|
+
color: var(--text);
|
|
39
|
+
text-decoration: none;
|
|
40
|
+
font-size: 14.5px;
|
|
41
|
+
font-weight: 400;
|
|
42
|
+
line-height: 1.2;
|
|
43
|
+
background: none;
|
|
44
|
+
border: 0;
|
|
45
|
+
cursor: pointer;
|
|
46
|
+
text-align: left;
|
|
47
|
+
font-family: inherit;
|
|
48
|
+
transition: background 0.16s var(--ease), color 0.16s var(--ease);
|
|
49
|
+
}
|
|
50
|
+
.ui-nav__item:hover { background: var(--surface-2); color: var(--strong); }
|
|
51
|
+
.ui-nav__item.is-active {
|
|
52
|
+
background: var(--surface);
|
|
53
|
+
color: var(--strong);
|
|
54
|
+
font-weight: 500;
|
|
55
|
+
}
|
|
56
|
+
.ui-nav--side .ui-nav__item.is-active {
|
|
57
|
+
box-shadow: inset 0 0 0 1px var(--border);
|
|
58
|
+
}
|
|
59
|
+
/* Accent rail marker on the active item */
|
|
60
|
+
.ui-nav--side .ui-nav__item.is-active::before {
|
|
61
|
+
content: "";
|
|
62
|
+
position: absolute;
|
|
63
|
+
left: 4px; top: 50%;
|
|
64
|
+
width: 3px; height: 17px;
|
|
65
|
+
border-radius: 3px;
|
|
66
|
+
background: var(--accent);
|
|
67
|
+
transform: translateY(-50%);
|
|
68
|
+
}
|
|
69
|
+
.ui-nav__item:focus-visible { outline: none; box-shadow: var(--ring); }
|
|
70
|
+
.ui-nav__item.is-active:focus-visible { box-shadow: var(--ring); }
|
|
71
|
+
|
|
72
|
+
.ui-nav__ic { display: inline-flex; flex: none; }
|
|
73
|
+
.ui-nav__ic svg { width: 17px; height: 17px; stroke: currentColor; fill: none; stroke-width: 1.7; opacity: 0.72; }
|
|
74
|
+
.ui-nav__item.is-active .ui-nav__ic svg,
|
|
75
|
+
.ui-nav__item.is-current .ui-nav__ic svg { opacity: 1; stroke: var(--accent); }
|
|
76
|
+
.ui-nav__label { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
77
|
+
|
|
78
|
+
.ui-nav__item.is-danger { color: var(--pink); }
|
|
79
|
+
.ui-nav__item.is-danger:hover { background: var(--glow-pink); color: var(--pink); }
|
|
80
|
+
.ui-nav__item.is-disabled { color: var(--muted); opacity: 0.55; cursor: default; pointer-events: none; }
|
|
81
|
+
|
|
82
|
+
/* Per-item badge / counter */
|
|
83
|
+
.ui-nav__badge {
|
|
84
|
+
flex: none;
|
|
85
|
+
min-width: 20px;
|
|
86
|
+
height: 19px;
|
|
87
|
+
padding: 0 6px;
|
|
88
|
+
border-radius: var(--radius-pill);
|
|
89
|
+
display: inline-flex;
|
|
90
|
+
align-items: center;
|
|
91
|
+
justify-content: center;
|
|
92
|
+
font-size: 11px;
|
|
93
|
+
font-weight: 600;
|
|
94
|
+
line-height: 1;
|
|
95
|
+
background: var(--surface-3);
|
|
96
|
+
color: var(--dim);
|
|
97
|
+
}
|
|
98
|
+
.ui-nav__badge.is-accent { background: var(--glow-purple); color: var(--accent); }
|
|
99
|
+
.ui-nav__badge.is-live { background: var(--glow-green); color: var(--green); }
|
|
100
|
+
.ui-nav__badge.is-danger { background: var(--glow-pink); color: var(--pink); }
|
|
101
|
+
.ui-nav__item.is-active .ui-nav__badge.is-neutral { background: var(--surface-3); color: var(--strong); }
|
|
102
|
+
|
|
103
|
+
/* Collapsible group */
|
|
104
|
+
.ui-nav__group { display: flex; flex-direction: column; }
|
|
105
|
+
.ui-nav__caret {
|
|
106
|
+
flex: none;
|
|
107
|
+
width: 7px; height: 7px;
|
|
108
|
+
border-right: 1.6px solid var(--muted);
|
|
109
|
+
border-bottom: 1.6px solid var(--muted);
|
|
110
|
+
transform: rotate(-45deg);
|
|
111
|
+
transition: transform 0.18s var(--ease);
|
|
112
|
+
}
|
|
113
|
+
.ui-nav__group.is-open > .ui-nav__toggle .ui-nav__caret { transform: rotate(45deg); }
|
|
114
|
+
.ui-nav__sub {
|
|
115
|
+
display: flex;
|
|
116
|
+
flex-direction: column;
|
|
117
|
+
gap: 2px;
|
|
118
|
+
margin: 2px 0 2px 15px;
|
|
119
|
+
padding-left: 11px;
|
|
120
|
+
border-left: 1px solid var(--border);
|
|
121
|
+
}
|
|
122
|
+
.ui-nav__sub[hidden] { display: none; }
|
|
123
|
+
.ui-nav__item--sub { font-size: 14px; padding: 7px 12px; }
|
|
124
|
+
.ui-nav--side .ui-nav__item--sub.is-active { box-shadow: none; }
|
|
125
|
+
.ui-nav--side .ui-nav__item--sub.is-active::before { left: -12px; height: 15px; }
|
|
126
|
+
|
|
127
|
+
/* Footer slot (divider + trailing links, e.g. sign-out) */
|
|
128
|
+
.ui-nav__foot { margin-top: 12px; padding-top: 10px; border-top: 1px solid var(--border); }
|
|
129
|
+
|
|
130
|
+
/* Collapsed (icon-only) rail */
|
|
131
|
+
.ui-nav--side.is-collapsed { width: 60px; align-items: stretch; }
|
|
132
|
+
.ui-nav--side.is-collapsed .ui-nav__cap,
|
|
133
|
+
.ui-nav--side.is-collapsed .ui-nav__label,
|
|
134
|
+
.ui-nav--side.is-collapsed .ui-nav__badge,
|
|
135
|
+
.ui-nav--side.is-collapsed .ui-nav__caret,
|
|
136
|
+
.ui-nav--side.is-collapsed .ui-nav__sub { display: none; }
|
|
137
|
+
.ui-nav--side.is-collapsed .ui-nav__item { justify-content: center; padding: 11px 0; gap: 0; }
|
|
138
|
+
.ui-nav--side.is-collapsed .ui-nav__ic svg { width: 19px; height: 19px; }
|
|
139
|
+
.ui-nav--side.is-collapsed .ui-nav__item.is-active::before { left: 3px; }
|
|
140
|
+
.ui-nav--side.is-collapsed .ui-nav__foot { text-align: center; }
|
|
141
|
+
|
|
142
|
+
/* -- Horizontal tabs ----------------------------------------------------- */
|
|
143
|
+
.ui-nav--tabs {
|
|
144
|
+
display: flex;
|
|
145
|
+
align-items: stretch;
|
|
146
|
+
gap: 4px;
|
|
147
|
+
border-bottom: 1px solid var(--border);
|
|
148
|
+
}
|
|
149
|
+
.ui-nav--tabs.is-pill { border-bottom: 0; gap: 6px; }
|
|
150
|
+
.ui-nav__tab {
|
|
151
|
+
position: relative;
|
|
152
|
+
display: inline-flex;
|
|
153
|
+
align-items: center;
|
|
154
|
+
gap: 8px;
|
|
155
|
+
padding: 11px 14px;
|
|
156
|
+
color: var(--dim);
|
|
157
|
+
text-decoration: none;
|
|
158
|
+
font-size: 14px;
|
|
159
|
+
font-weight: 500;
|
|
160
|
+
border-radius: 10px 10px 0 0;
|
|
161
|
+
transition: color 0.16s var(--ease), background 0.16s var(--ease);
|
|
162
|
+
}
|
|
163
|
+
.ui-nav__tab:hover { color: var(--strong); }
|
|
164
|
+
.ui-nav__tab:focus-visible { outline: none; box-shadow: var(--ring); border-radius: 8px; }
|
|
165
|
+
.ui-nav__tab.is-active { color: var(--strong); }
|
|
166
|
+
|
|
167
|
+
/* underline variant */
|
|
168
|
+
.ui-nav--tabs.is-underline .ui-nav__tab::after {
|
|
169
|
+
content: "";
|
|
170
|
+
position: absolute;
|
|
171
|
+
left: 10px; right: 10px; bottom: -1px;
|
|
172
|
+
height: 2px;
|
|
173
|
+
border-radius: 2px;
|
|
174
|
+
background: var(--accent);
|
|
175
|
+
opacity: 0;
|
|
176
|
+
transform: scaleX(0.4);
|
|
177
|
+
transition: opacity 0.16s var(--ease), transform 0.16s var(--ease);
|
|
178
|
+
}
|
|
179
|
+
.ui-nav--tabs.is-underline .ui-nav__tab.is-active::after { opacity: 1; transform: scaleX(1); }
|
|
180
|
+
|
|
181
|
+
/* pill variant */
|
|
182
|
+
.ui-nav--tabs.is-pill .ui-nav__tab { border-radius: var(--radius-pill); padding: 8px 15px; }
|
|
183
|
+
.ui-nav--tabs.is-pill .ui-nav__tab:hover { background: var(--surface-2); }
|
|
184
|
+
.ui-nav--tabs.is-pill .ui-nav__tab.is-active {
|
|
185
|
+
background: var(--glow-purple);
|
|
186
|
+
color: var(--accent);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
.ui-nav__tab.is-disabled { color: var(--muted); opacity: 0.5; cursor: default; }
|
|
190
|
+
.ui-nav__tab .ui-nav__badge { height: 18px; }
|
|
191
|
+
|
|
192
|
+
/* -- Breadcrumbs --------------------------------------------------------- */
|
|
193
|
+
.ui-nav__crumbs { display: flex; flex-wrap: wrap; align-items: center; gap: 2px; }
|
|
194
|
+
.ui-nav__crumb-item { display: inline-flex; align-items: center; }
|
|
195
|
+
.ui-nav__crumb-item + .ui-nav__crumb-item::before {
|
|
196
|
+
content: "";
|
|
197
|
+
width: 6px; height: 6px;
|
|
198
|
+
margin: 0 8px;
|
|
199
|
+
border-right: 1.4px solid var(--muted);
|
|
200
|
+
border-bottom: 1.4px solid var(--muted);
|
|
201
|
+
transform: rotate(-45deg);
|
|
202
|
+
opacity: 0.7;
|
|
203
|
+
}
|
|
204
|
+
.ui-nav__crumb {
|
|
205
|
+
display: inline-flex;
|
|
206
|
+
align-items: center;
|
|
207
|
+
gap: 6px;
|
|
208
|
+
padding: 3px 6px;
|
|
209
|
+
border-radius: 7px;
|
|
210
|
+
color: var(--dim);
|
|
211
|
+
text-decoration: none;
|
|
212
|
+
font-size: 13px;
|
|
213
|
+
transition: color 0.16s var(--ease), background 0.16s var(--ease);
|
|
214
|
+
}
|
|
215
|
+
a.ui-nav__crumb:hover { color: var(--strong); background: var(--surface-2); }
|
|
216
|
+
a.ui-nav__crumb:focus-visible { outline: none; box-shadow: var(--ring); }
|
|
217
|
+
.ui-nav__crumb.is-current { color: var(--strong); font-weight: 500; }
|
|
218
|
+
.ui-nav__crumb .ui-nav__ic svg { width: 15px; height: 15px; stroke: currentColor; fill: none; stroke-width: 1.7; }
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
/* ============================================================================
|
|
2
|
+
* Success / confirmation surface — .ui-sx*
|
|
3
|
+
* The emotional high point of a flow, with craft: a self-drawing SVG check
|
|
4
|
+
* (ring burst + spring), an aurora / glow backdrop, follow-up actions, an
|
|
5
|
+
* optional confetti field and an optional auto-redirect countdown. Three
|
|
6
|
+
* layouts (hero / split / compact) and three backdrops (aurora / glow / flat).
|
|
7
|
+
*
|
|
8
|
+
* Accent-aware via the kit tokens; the success mark stays on the --green
|
|
9
|
+
* family (green = success), the surrounds follow --accent so it re-themes with
|
|
10
|
+
* the sub-theme. Every motion path is reduced-motion safe — see the block at
|
|
11
|
+
* the foot. Markup comes from success() in components/index (re-exported).
|
|
12
|
+
* ========================================================================== */
|
|
13
|
+
|
|
14
|
+
.ui-sx {
|
|
15
|
+
--sx-green: var(--green);
|
|
16
|
+
--sx-tint: color-mix(in srgb, var(--green) 18%, transparent);
|
|
17
|
+
--sx-track: color-mix(in srgb, var(--green) 32%, transparent);
|
|
18
|
+
|
|
19
|
+
position: relative;
|
|
20
|
+
overflow: hidden;
|
|
21
|
+
border-radius: 20px;
|
|
22
|
+
background: var(--bg-elevated);
|
|
23
|
+
box-shadow: var(--shadow-md);
|
|
24
|
+
isolation: isolate;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/* ---- Backdrops ----------------------------------------------------------- */
|
|
28
|
+
/* Aurora: two soft blurred blobs — a green wash under the check + an accent
|
|
29
|
+
blob for identity — so the success matches the rest of the kit's warmth. */
|
|
30
|
+
.ui-sx__aurora { position: absolute; inset: 0; z-index: 0; pointer-events: none; overflow: hidden; }
|
|
31
|
+
.ui-sx__glow {
|
|
32
|
+
position: absolute; border-radius: 50%; filter: blur(70px); opacity: 0.9;
|
|
33
|
+
aspect-ratio: 1; will-change: transform;
|
|
34
|
+
}
|
|
35
|
+
.ui-sx__glow--a {
|
|
36
|
+
width: 58%; top: -14%; left: 50%; translate: -50% 0;
|
|
37
|
+
background: radial-gradient(circle at center, var(--sx-tint), transparent 68%);
|
|
38
|
+
animation: ui-sx-float 14s ease-in-out infinite alternate;
|
|
39
|
+
}
|
|
40
|
+
.ui-sx__glow--b {
|
|
41
|
+
width: 52%; bottom: -20%; left: 12%;
|
|
42
|
+
background: radial-gradient(circle at center,
|
|
43
|
+
color-mix(in srgb, var(--accent) 30%, transparent), transparent 68%);
|
|
44
|
+
animation: ui-sx-float 18s ease-in-out -6s infinite alternate;
|
|
45
|
+
}
|
|
46
|
+
:root[data-theme="light"] .ui-sx__glow { opacity: 0.7; }
|
|
47
|
+
@keyframes ui-sx-float {
|
|
48
|
+
from { transform: translate(-2%, -2%) scale(1); }
|
|
49
|
+
to { transform: translate(3%, 3%) scale(1.14); }
|
|
50
|
+
}
|
|
51
|
+
/* Glow backdrop: reuse the shared .ui-glow--green, parked behind the check. */
|
|
52
|
+
.ui-sx__bg-glow { top: -40%; left: 50%; translate: -50% 0; width: 360px; height: 360px; }
|
|
53
|
+
|
|
54
|
+
/* ---- Layout shell -------------------------------------------------------- */
|
|
55
|
+
.ui-sx__inner { position: relative; z-index: 1; }
|
|
56
|
+
.ui-sx__content { min-width: 0; }
|
|
57
|
+
.ui-sx__eyebrow {
|
|
58
|
+
color: var(--green); font-size: 12px; font-weight: var(--weight-semibold);
|
|
59
|
+
letter-spacing: .08em; text-transform: uppercase; margin-bottom: 10px;
|
|
60
|
+
}
|
|
61
|
+
.ui-sx__title {
|
|
62
|
+
color: var(--strong); font-size: 22px; font-weight: var(--weight-semibold);
|
|
63
|
+
letter-spacing: -.02em; line-height: 1.2; margin: 0;
|
|
64
|
+
}
|
|
65
|
+
.ui-sx__body {
|
|
66
|
+
color: var(--dim); font-size: 14.5px; line-height: 1.6; margin: 9px 0 0; max-width: 44ch;
|
|
67
|
+
}
|
|
68
|
+
.ui-sx__actions { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 20px; }
|
|
69
|
+
|
|
70
|
+
/* hero — centred column, the upgraded default */
|
|
71
|
+
.ui-sx--hero { padding: 44px 32px 40px; text-align: center; }
|
|
72
|
+
.ui-sx--hero .ui-sx__inner { display: flex; flex-direction: column; align-items: center; }
|
|
73
|
+
.ui-sx--hero .ui-sx__visual { margin-bottom: 22px; }
|
|
74
|
+
.ui-sx--hero .ui-sx__body { margin-left: auto; margin-right: auto; }
|
|
75
|
+
.ui-sx--hero .ui-sx__actions { justify-content: center; }
|
|
76
|
+
.ui-sx--hero .ui-sx__check { width: 96px; height: 96px; }
|
|
77
|
+
|
|
78
|
+
/* split — visual panel beside the copy */
|
|
79
|
+
.ui-sx--split .ui-sx__inner {
|
|
80
|
+
display: grid; grid-template-columns: minmax(200px, 300px) 1fr; align-items: stretch;
|
|
81
|
+
}
|
|
82
|
+
.ui-sx--split .ui-sx__visual {
|
|
83
|
+
display: grid; place-items: center; padding: 40px 24px;
|
|
84
|
+
background: color-mix(in srgb, var(--green) 8%, transparent);
|
|
85
|
+
border-right: 1px solid var(--border);
|
|
86
|
+
}
|
|
87
|
+
.ui-sx--split .ui-sx__check { width: 120px; height: 120px; }
|
|
88
|
+
.ui-sx--split .ui-sx__content { padding: 40px 36px; align-self: center; }
|
|
89
|
+
|
|
90
|
+
/* compact — inline, panel/toast-adjacent */
|
|
91
|
+
.ui-sx--compact { padding: 18px 20px; }
|
|
92
|
+
.ui-sx--compact .ui-sx__inner { display: flex; align-items: center; gap: 16px; }
|
|
93
|
+
.ui-sx--compact .ui-sx__visual { flex: none; }
|
|
94
|
+
.ui-sx--compact .ui-sx__check { width: 44px; height: 44px; }
|
|
95
|
+
.ui-sx--compact .ui-sx__title { font-size: 16px; }
|
|
96
|
+
.ui-sx--compact .ui-sx__body { font-size: 13px; margin-top: 3px; }
|
|
97
|
+
.ui-sx--compact .ui-sx__actions { margin-top: 0; margin-left: auto; flex: none; }
|
|
98
|
+
.ui-sx--compact .ui-sx__content { flex: 1; display: flex; align-items: center; gap: 16px; }
|
|
99
|
+
.ui-sx--compact .ui-sx__content > div:first-child,
|
|
100
|
+
.ui-sx--compact .ui-sx__title { min-width: 0; }
|
|
101
|
+
|
|
102
|
+
/* ---- The self-drawing check --------------------------------------------- */
|
|
103
|
+
.ui-sx__check { display: block; overflow: visible; }
|
|
104
|
+
/* Soft tinted disc that springs in behind the tick. */
|
|
105
|
+
.ui-sx__disc {
|
|
106
|
+
fill: var(--sx-tint); stroke: var(--sx-track); stroke-width: 1.5;
|
|
107
|
+
transform-origin: center; animation: ui-sx-pop .5s cubic-bezier(.22,1,.3,1) both;
|
|
108
|
+
}
|
|
109
|
+
/* The tick, stroked on via dash-offset (path length ~34). */
|
|
110
|
+
.ui-sx__tick {
|
|
111
|
+
fill: none; stroke: var(--sx-green); stroke-width: 5;
|
|
112
|
+
stroke-linecap: round; stroke-linejoin: round;
|
|
113
|
+
stroke-dasharray: 34; stroke-dashoffset: 34;
|
|
114
|
+
animation: ui-sx-draw .5s cubic-bezier(.6,0,.35,1) .28s forwards;
|
|
115
|
+
filter: drop-shadow(0 0 5px color-mix(in srgb, var(--green) 55%, transparent));
|
|
116
|
+
}
|
|
117
|
+
/* Burst ring that expands and fades once, framing the moment. */
|
|
118
|
+
.ui-sx__ring {
|
|
119
|
+
fill: none; stroke: var(--sx-green); stroke-width: 2;
|
|
120
|
+
transform-origin: center; opacity: 0;
|
|
121
|
+
animation: ui-sx-burst .7s cubic-bezier(.3,.7,.4,1) .2s forwards;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
@keyframes ui-sx-pop {
|
|
125
|
+
0% { transform: scale(.4); opacity: 0; }
|
|
126
|
+
60% { transform: scale(1.06); opacity: 1; }
|
|
127
|
+
100% { transform: scale(1); }
|
|
128
|
+
}
|
|
129
|
+
@keyframes ui-sx-draw { to { stroke-dashoffset: 0; } }
|
|
130
|
+
@keyframes ui-sx-burst {
|
|
131
|
+
0% { transform: scale(.55); opacity: .7; }
|
|
132
|
+
100% { transform: scale(1.7); opacity: 0; }
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/* ---- Confetti (opt-in) --------------------------------------------------- */
|
|
136
|
+
.ui-sx__confetti { position: absolute; inset: 0; z-index: 2; pointer-events: none; overflow: hidden; }
|
|
137
|
+
.ui-sx__piece {
|
|
138
|
+
position: absolute; top: -8%; width: 8px; height: 8px; border-radius: 2px;
|
|
139
|
+
opacity: 0; transform-origin: center;
|
|
140
|
+
animation: ui-sx-fall 2.4s cubic-bezier(.2,.6,.35,1) var(--sx-d, 0s) forwards;
|
|
141
|
+
scale: var(--sx-s, 1);
|
|
142
|
+
}
|
|
143
|
+
.ui-sx__piece--a { background: var(--accent); }
|
|
144
|
+
.ui-sx__piece--g { background: var(--green); }
|
|
145
|
+
.ui-sx__piece--c { background: var(--cyan); }
|
|
146
|
+
.ui-sx__piece--p { background: var(--purple-light); }
|
|
147
|
+
.ui-sx__piece--k { background: var(--pink); border-radius: 50%; }
|
|
148
|
+
@keyframes ui-sx-fall {
|
|
149
|
+
0% { opacity: 0; transform: translateY(-10px) rotate(0deg); }
|
|
150
|
+
12% { opacity: 1; }
|
|
151
|
+
100% { opacity: 0; transform: translateY(340px) rotate(var(--sx-r, 40deg)); }
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/* ---- Countdown ----------------------------------------------------------- */
|
|
155
|
+
/* Registered angle so the conic sweep can actually interpolate (a plain
|
|
156
|
+
conic-gradient background is not animatable). */
|
|
157
|
+
@property --sx-a { syntax: "<angle>"; inherits: false; initial-value: 360deg; }
|
|
158
|
+
.ui-sx__count { display: inline-flex; align-items: center; gap: 9px; margin-top: 18px; }
|
|
159
|
+
.ui-sx--hero .ui-sx__count { justify-content: center; }
|
|
160
|
+
.ui-sx__count-ring {
|
|
161
|
+
flex: none; width: 16px; height: 16px; border-radius: 50%;
|
|
162
|
+
background: conic-gradient(var(--green) var(--sx-a, 360deg), var(--surface-2) 0);
|
|
163
|
+
mask: radial-gradient(circle, transparent 5px, #000 6px);
|
|
164
|
+
-webkit-mask: radial-gradient(circle, transparent 5px, #000 6px);
|
|
165
|
+
animation: ui-sx-sweep var(--sx-secs, 5s) linear forwards;
|
|
166
|
+
}
|
|
167
|
+
@keyframes ui-sx-sweep { from { --sx-a: 360deg; } to { --sx-a: 0deg; } }
|
|
168
|
+
.ui-sx__count-text { color: var(--muted); font-size: 13px; }
|
|
169
|
+
.ui-sx__count-text b { color: var(--dim); font-weight: var(--weight-semibold); font-variant-numeric: tabular-nums; }
|
|
170
|
+
|
|
171
|
+
/* ---- Reduced motion — static check, no burst / confetti / sweep ---------- */
|
|
172
|
+
@media (prefers-reduced-motion: reduce) {
|
|
173
|
+
.ui-sx__glow--a, .ui-sx__glow--b { animation: none; }
|
|
174
|
+
.ui-sx__disc { animation: none; }
|
|
175
|
+
.ui-sx__tick { animation: none; stroke-dashoffset: 0; }
|
|
176
|
+
.ui-sx__ring { display: none; }
|
|
177
|
+
.ui-sx__confetti { display: none; }
|
|
178
|
+
.ui-sx__count-ring { animation: none; background: var(--surface-2); }
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/* ---- Narrow — split stacks -------------------------------------------- */
|
|
182
|
+
@media (max-width: 560px) {
|
|
183
|
+
.ui-sx--split .ui-sx__inner { grid-template-columns: 1fr; }
|
|
184
|
+
.ui-sx--split .ui-sx__visual { border-right: 0; border-bottom: 1px solid var(--border); padding: 32px 24px; }
|
|
185
|
+
.ui-sx--split .ui-sx__content { padding: 28px 26px; text-align: center; }
|
|
186
|
+
.ui-sx--split .ui-sx__actions { justify-content: center; }
|
|
187
|
+
.ui-sx--compact .ui-sx__content { flex-direction: column; align-items: flex-start; gap: 10px; }
|
|
188
|
+
.ui-sx--compact .ui-sx__actions { margin-left: 0; }
|
|
189
|
+
}
|
package/src/styles/table.css
CHANGED
|
@@ -59,18 +59,5 @@
|
|
|
59
59
|
.ui-table__num--strong { color: var(--strong); font-weight: var(--weight-semibold); }
|
|
60
60
|
.ui-table__code { font-family: var(--font-mono); font-size: var(--text-sm); color: var(--muted); }
|
|
61
61
|
|
|
62
|
-
/*
|
|
63
|
-
.
|
|
64
|
-
text-align: center;
|
|
65
|
-
padding: 48px 24px;
|
|
66
|
-
color: var(--muted);
|
|
67
|
-
}
|
|
68
|
-
.ui-empty__icon {
|
|
69
|
-
width: 44px; height: 44px;
|
|
70
|
-
margin: 0 auto 14px;
|
|
71
|
-
color: var(--muted);
|
|
72
|
-
opacity: 0.6;
|
|
73
|
-
}
|
|
74
|
-
.ui-empty__icon svg { width: 100%; height: 100%; stroke: currentColor; fill: none; stroke-width: 1.5; }
|
|
75
|
-
.ui-empty__title { color: var(--text); font-size: var(--text-md); font-weight: var(--weight-medium); margin-bottom: 5px; }
|
|
76
|
-
.ui-empty__sub { font-size: var(--text-sm); max-width: 320px; margin: 0 auto; line-height: 1.55; }
|
|
62
|
+
/* The empty state (.ui-empty) — shown when a table/list has no rows — now
|
|
63
|
+
lives in styles/empty.css as a standalone component (illustration support). */
|