@ambasada/ui 0.0.0-stage → 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/LICENSE +21 -0
- package/README.md +107 -2
- package/dist/ambasada-ui.css +615 -0
- package/dist/components.css +452 -0
- package/dist/fonts/OFL-inter.txt +93 -0
- package/dist/fonts/OFL-jetbrains-mono.txt +93 -0
- package/dist/fonts/inter-variable-latin-ext.woff2 +0 -0
- package/dist/fonts/inter-variable-latin.woff2 +0 -0
- package/dist/fonts/jetbrains-mono-variable-latin-ext.woff2 +0 -0
- package/dist/fonts/jetbrains-mono-variable-latin.woff2 +0 -0
- package/dist/fonts.css +39 -0
- package/dist/react.d.ts +70 -0
- package/dist/react.js +114 -0
- package/dist/tokens.css +121 -0
- package/package.json +66 -3
|
@@ -0,0 +1,615 @@
|
|
|
1
|
+
/* @ambasada/ui 0.6.0: fonts, tokens and components in one stylesheet. MIT, CENTYSO. https://ambasada.id */
|
|
2
|
+
/*
|
|
3
|
+
* Inter and JetBrains Mono, self-hosted (variable, SIL Open Font License 1.1: fonts/OFL-*.txt). Latin and
|
|
4
|
+
* Latin Extended, so Romanian ț and ș render. The URLs are relative to this file: bundlers copy the files,
|
|
5
|
+
* and the CDN build (dist/ambasada-ui.css) finds them in dist/fonts/. Already serving the fonts yourself?
|
|
6
|
+
* Import tokens.css and components.css instead of the whole package.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
@font-face {
|
|
10
|
+
font-family: "Inter";
|
|
11
|
+
font-style: normal;
|
|
12
|
+
font-weight: 100 900;
|
|
13
|
+
font-display: swap;
|
|
14
|
+
src: url("./fonts/inter-variable-latin-ext.woff2") format("woff2");
|
|
15
|
+
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
|
16
|
+
}
|
|
17
|
+
@font-face {
|
|
18
|
+
font-family: "Inter";
|
|
19
|
+
font-style: normal;
|
|
20
|
+
font-weight: 100 900;
|
|
21
|
+
font-display: swap;
|
|
22
|
+
src: url("./fonts/inter-variable-latin.woff2") format("woff2");
|
|
23
|
+
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
|
24
|
+
}
|
|
25
|
+
@font-face {
|
|
26
|
+
font-family: "JetBrains Mono";
|
|
27
|
+
font-style: normal;
|
|
28
|
+
font-weight: 100 800;
|
|
29
|
+
font-display: swap;
|
|
30
|
+
src: url("./fonts/jetbrains-mono-variable-latin-ext.woff2") format("woff2");
|
|
31
|
+
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
|
32
|
+
}
|
|
33
|
+
@font-face {
|
|
34
|
+
font-family: "JetBrains Mono";
|
|
35
|
+
font-style: normal;
|
|
36
|
+
font-weight: 100 800;
|
|
37
|
+
font-display: swap;
|
|
38
|
+
src: url("./fonts/jetbrains-mono-variable-latin.woff2") format("woff2");
|
|
39
|
+
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/*
|
|
43
|
+
* @ambasada/ui design tokens: the Ambasada look as CSS custom properties.
|
|
44
|
+
*
|
|
45
|
+
* Source of truth is the Ambasada hub (repo ambasada-landing, hub.ambasada.id / ambasada.pro):
|
|
46
|
+
* - src/app/globals.css the @theme block ("V5 light + brand red")
|
|
47
|
+
* - src/app/layout.tsx Inter + JetBrains Mono via next/font
|
|
48
|
+
* - src/lib/member/card-style.ts wallet pass palettes (Apple / Google Wallet)
|
|
49
|
+
* - src/app/card/frames.tsx WalletCard, the on-screen copy of the pass
|
|
50
|
+
* Light only: the Ambasada app is light, and so are its mini apps (docs/MINI-APPS.md §9).
|
|
51
|
+
* Usage and rules: docs/DESIGN-SYSTEM.md. The fonts themselves are in fonts.css.
|
|
52
|
+
*/
|
|
53
|
+
|
|
54
|
+
:root {
|
|
55
|
+
color-scheme: light;
|
|
56
|
+
|
|
57
|
+
/* Brand */
|
|
58
|
+
--color-accent: #ff002e; /* hub --color-accent, the logo red; "Ambasada red" pass palette rgb(255,0,46) */
|
|
59
|
+
--color-accent-hover: #e5002a; /* hub .prose-ambasada a:hover */
|
|
60
|
+
--color-accent-text: #e5002a; /* red for small text on light: #ff002e is 3.8:1 on bg, this is 4.6:1 (AA) */
|
|
61
|
+
--color-accent-press: #b30020; /* hub tier-theme "Active" gradient end */
|
|
62
|
+
--color-accent-2: #ff6b00; /* hub --color-accent-2, used sparingly (warm highlights) */
|
|
63
|
+
--color-accent-soft: #ffe9ec; /* hub --color-accent-soft, tinted backgrounds */
|
|
64
|
+
--color-accent-ring: rgba(255, 0, 46, 0.45); /* hub .card-hover:hover border */
|
|
65
|
+
--color-on-accent: #ffffff; /* hub primary button text, pass foreground */
|
|
66
|
+
|
|
67
|
+
/* Neutrals */
|
|
68
|
+
--color-bg: #fafaf9; /* hub --color-bg (stone-50) */
|
|
69
|
+
--color-surface: #ffffff; /* hub --color-surface */
|
|
70
|
+
--color-surface-2: #f5f5f4; /* stone-100, hub Clerk card footer and row hover */
|
|
71
|
+
--color-ink: #0c0a09; /* hub --color-ink (stone-950) */
|
|
72
|
+
--color-ink-2: #57534e; /* hub --color-ink-2 (stone-600), secondary text */
|
|
73
|
+
--color-ink-3: #78716c; /* hub --color-ink-3 (stone-500), labels, WCAG AA on bg */
|
|
74
|
+
--color-line: #e7e5e4; /* hub --color-line (stone-200), borders and dividers */
|
|
75
|
+
--color-line-strong: #d6d3d1; /* stone-300, input borders on hover */
|
|
76
|
+
--color-backdrop: rgba(12, 10, 9, 0.5); /* behind bottom sheets: ink at 50% */
|
|
77
|
+
|
|
78
|
+
/* Status */
|
|
79
|
+
--color-success: #16a34a; /* hub --color-success */
|
|
80
|
+
--color-warn: #f59e0b; /* hub --color-warn */
|
|
81
|
+
--color-danger: #dc2626; /* red-600: distinct from the brand red so errors never read as branding */
|
|
82
|
+
--color-info: #2563eb; /* hub --color-info */
|
|
83
|
+
--color-success-soft: #ecfdf3; /* tint of success for notices */
|
|
84
|
+
--color-warn-soft: #fff8eb; /* tint of warn for notices */
|
|
85
|
+
--color-danger-soft: #fef2f2; /* tint of danger for notices */
|
|
86
|
+
--color-warn-line: #fcd9a0;
|
|
87
|
+
--color-danger-line: #fecaca;
|
|
88
|
+
--color-success-line: #bbf7d0;
|
|
89
|
+
|
|
90
|
+
/* The member card (wallet pass, "Ambasada red" palette, src/lib/member/card-style.ts) */
|
|
91
|
+
--card-bg: #ff002e; /* pass backgroundColor rgb(255,0,46) */
|
|
92
|
+
--card-fg: #ffffff; /* pass foregroundColor rgb(255,255,255) */
|
|
93
|
+
--card-label: #ffc8d2; /* pass labelColor rgb(255,200,210) */
|
|
94
|
+
--card-divider: rgba(255, 255, 255, 0.28);
|
|
95
|
+
/* The dark card ("Midnight" palette, also the hub's .card-dark / terminal surface) */
|
|
96
|
+
--card-dark-bg: #0c0a09; /* pass Midnight rgb(12,10,9), hub --color-terminal-bg */
|
|
97
|
+
--card-dark-fg: #ffffff;
|
|
98
|
+
--card-dark-label: #a5a09b; /* pass Midnight labelColor rgb(165,160,155) */
|
|
99
|
+
|
|
100
|
+
/* Typography */
|
|
101
|
+
--font-sans: "Inter", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif; /* hub layout.tsx Inter */
|
|
102
|
+
--font-mono: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace; /* hub layout.tsx JetBrains_Mono 400/500/700 */
|
|
103
|
+
--font-native: -apple-system, BlinkMacSystemFont, system-ui, sans-serif; /* hub .native-shell body */
|
|
104
|
+
|
|
105
|
+
--text-label: 0.6875rem; /* 11px, hub .section-label (mono, uppercase, 0.1em): the mono label, .ui-label */
|
|
106
|
+
--text-micro: 0.625rem; /* 10px, hub card labels ("membership", business card rows) */
|
|
107
|
+
--text-xs: 0.75rem; /* 12px, hub nav links */
|
|
108
|
+
--text-sm: 0.875rem; /* 14px, hub body small / buttons */
|
|
109
|
+
--text-base: 1rem; /* 16px */
|
|
110
|
+
--text-lg: 1.0625rem; /* 17px, pass "Name" value */
|
|
111
|
+
--text-xl: 1.375rem; /* 22px, business card name */
|
|
112
|
+
--text-2xl: 1.75rem; /* 28px, page titles on mobile */
|
|
113
|
+
--text-display: 2.75rem; /* 44px, wallet balance (hub card numbers, text-3xl and up) */
|
|
114
|
+
--tracking-label: 0.1em; /* hub .section-label */
|
|
115
|
+
--tracking-tight: -0.02em; /* hub headings, tracking-tight */
|
|
116
|
+
--weight-regular: 400;
|
|
117
|
+
--weight-medium: 500; /* hub headings are font-medium */
|
|
118
|
+
--weight-semibold: 600; /* hub card names */
|
|
119
|
+
--weight-bold: 700; /* hub numbers (events count) */
|
|
120
|
+
|
|
121
|
+
/* Radii */
|
|
122
|
+
--radius-xs: 4px; /* hub QR plate on the wallet card */
|
|
123
|
+
--radius-sm: 8px; /* hub --radius-sm, Clerk borderRadius 0.5rem: buttons and inputs */
|
|
124
|
+
--radius-md: 12px; /* hub article images, notices */
|
|
125
|
+
--radius-card: 16px; /* hub --radius-card, WalletCard rounded-2xl */
|
|
126
|
+
--radius-hero: 28px; /* hub MemberCard and BusinessCard rounded-[28px], bottom sheets */
|
|
127
|
+
--radius-pill: 9999px; /* hub --radius-pill */
|
|
128
|
+
|
|
129
|
+
/* Shadows */
|
|
130
|
+
--shadow-card: 0 1px 2px rgba(12, 10, 9, 0.04); /* hub cards are flat: border first, barely any shadow */
|
|
131
|
+
--shadow-raised: 0 12px 40px -20px rgba(0, 0, 0, 0.35); /* hub BusinessCard */
|
|
132
|
+
--shadow-pass: 0 30px 60px -30px rgba(0, 0, 0, 0.55); /* hub WalletCard */
|
|
133
|
+
--shadow-glow: 0 0 0 1px rgba(255, 0, 46, 0.25), 0 12px 40px -8px rgba(255, 0, 46, 0.18); /* hub .glow-red */
|
|
134
|
+
--focus-ring: 0 0 0 2px var(--color-bg), 0 0 0 4px var(--color-accent); /* hub: 2px accent outline, offset 2px */
|
|
135
|
+
|
|
136
|
+
/* Spacing (4px grid, as Tailwind in the hub) */
|
|
137
|
+
--space-1: 4px;
|
|
138
|
+
--space-2: 8px;
|
|
139
|
+
--space-3: 12px;
|
|
140
|
+
--space-4: 16px;
|
|
141
|
+
--space-5: 20px; /* hub WalletCard horizontal padding (px-5) */
|
|
142
|
+
--space-6: 24px; /* hub MemberCard padding (p-6), page gutter (px-6) */
|
|
143
|
+
--space-8: 32px;
|
|
144
|
+
--space-10: 40px;
|
|
145
|
+
--page-max: 480px; /* the mini-app column; the hub's member pages use max-w-md..lg */
|
|
146
|
+
--tap: 44px; /* minimum touch target */
|
|
147
|
+
|
|
148
|
+
/* Motion */
|
|
149
|
+
--ease: cubic-bezier(0.2, 0, 0, 1);
|
|
150
|
+
--duration: 200ms; /* hub .card transitions */
|
|
151
|
+
|
|
152
|
+
/*
|
|
153
|
+
* The Ambasada app's shell (docs/MINI-APPS.md §6). Inside the app, @ambasada/miniapp (applyCssVars, run by
|
|
154
|
+
* ready()) sets these on <html> from the shell context, which wins over :root. On the web the safe areas
|
|
155
|
+
* fall back to the browser's own insets (non-zero only with viewport-fit=cover) and there is no host bar.
|
|
156
|
+
*/
|
|
157
|
+
--ambasada-safe-area-top: env(safe-area-inset-top, 0px);
|
|
158
|
+
--ambasada-safe-area-bottom: env(safe-area-inset-bottom, 0px);
|
|
159
|
+
--ambasada-safe-area-left: env(safe-area-inset-left, 0px);
|
|
160
|
+
--ambasada-safe-area-right: env(safe-area-inset-right, 0px);
|
|
161
|
+
--ambasada-host-bar: 0px; /* height of the shell's top bar (back, app name, "…" menu) */
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/*
|
|
165
|
+
* @ambasada/ui components, in plain CSS: page, card, buttons, list rows, pill, bottom sheet, stepper,
|
|
166
|
+
* empty state, skeleton, the mono label. Tokens only (tokens.css): no raw colours.
|
|
167
|
+
* Markup for each one: packages/ui/README.md, or the React wrappers in @ambasada/ui/react.
|
|
168
|
+
*/
|
|
169
|
+
|
|
170
|
+
/* ── base ───────────────────────────────────────────────────────────────────
|
|
171
|
+
* Zero specificity, so any rule of the app's own wins. Enough for a plain HTML page to look right. */
|
|
172
|
+
|
|
173
|
+
*,
|
|
174
|
+
*::before,
|
|
175
|
+
*::after {
|
|
176
|
+
box-sizing: border-box;
|
|
177
|
+
}
|
|
178
|
+
:where(html) {
|
|
179
|
+
font-family: var(--font-sans);
|
|
180
|
+
background: var(--color-bg);
|
|
181
|
+
color: var(--color-ink);
|
|
182
|
+
-webkit-font-smoothing: antialiased;
|
|
183
|
+
-moz-osx-font-smoothing: grayscale;
|
|
184
|
+
-webkit-text-size-adjust: 100%;
|
|
185
|
+
-webkit-tap-highlight-color: transparent;
|
|
186
|
+
}
|
|
187
|
+
:where(body) {
|
|
188
|
+
margin: 0;
|
|
189
|
+
font-size: var(--text-base);
|
|
190
|
+
line-height: 1.5;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/* ── page ───────────────────────────────────────────────────────────────────
|
|
194
|
+
* The mini-app column, clear of the shell's host bar and the safe areas (docs/MINI-APPS.md §6). */
|
|
195
|
+
|
|
196
|
+
.ui-page {
|
|
197
|
+
max-width: var(--page-max);
|
|
198
|
+
margin: 0 auto;
|
|
199
|
+
padding:
|
|
200
|
+
calc(var(--space-4) + var(--ambasada-safe-area-top, 0px) + var(--ambasada-host-bar, 0px))
|
|
201
|
+
calc(var(--space-4) + var(--ambasada-safe-area-right, 0px))
|
|
202
|
+
calc(var(--space-8) + var(--ambasada-safe-area-bottom, 0px))
|
|
203
|
+
calc(var(--space-4) + var(--ambasada-safe-area-left, 0px));
|
|
204
|
+
display: flex;
|
|
205
|
+
flex-direction: column;
|
|
206
|
+
gap: var(--space-4);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/* The mono label: the hub's .section-label. For small captions, never for sentences. */
|
|
210
|
+
.ui-label {
|
|
211
|
+
font-family: var(--font-mono);
|
|
212
|
+
font-size: var(--text-label);
|
|
213
|
+
font-weight: var(--weight-medium);
|
|
214
|
+
letter-spacing: var(--tracking-label);
|
|
215
|
+
text-transform: uppercase;
|
|
216
|
+
color: var(--color-ink-3);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/* ── card ───────────────────────────────────────────────────────────────────
|
|
220
|
+
* The hub's .card: white, hairline border, 16px radius, flat. */
|
|
221
|
+
|
|
222
|
+
.ui-card {
|
|
223
|
+
background: var(--color-surface);
|
|
224
|
+
border: 1px solid var(--color-line);
|
|
225
|
+
border-radius: var(--radius-card);
|
|
226
|
+
box-shadow: var(--shadow-card);
|
|
227
|
+
padding: var(--space-5);
|
|
228
|
+
}
|
|
229
|
+
.ui-card-title {
|
|
230
|
+
margin: 0 0 var(--space-3);
|
|
231
|
+
font-size: var(--text-lg);
|
|
232
|
+
font-weight: var(--weight-semibold);
|
|
233
|
+
letter-spacing: var(--tracking-tight);
|
|
234
|
+
line-height: 1.2;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/* ── buttons ────────────────────────────────────────────────────────────────
|
|
238
|
+
* The hub's Button. Primary: brand red, white text (one per screen). Secondary: surface, hairline, red on hover. */
|
|
239
|
+
|
|
240
|
+
.ui-button {
|
|
241
|
+
display: inline-flex;
|
|
242
|
+
align-items: center;
|
|
243
|
+
justify-content: center;
|
|
244
|
+
gap: var(--space-2);
|
|
245
|
+
min-height: var(--tap);
|
|
246
|
+
padding: 10px 18px;
|
|
247
|
+
border: 1px solid transparent;
|
|
248
|
+
border-radius: var(--radius-sm);
|
|
249
|
+
background: var(--color-accent);
|
|
250
|
+
color: var(--color-on-accent);
|
|
251
|
+
font: inherit;
|
|
252
|
+
font-size: 0.9375rem;
|
|
253
|
+
font-weight: var(--weight-medium);
|
|
254
|
+
line-height: 1.25;
|
|
255
|
+
text-align: center;
|
|
256
|
+
text-decoration: none;
|
|
257
|
+
cursor: pointer;
|
|
258
|
+
touch-action: manipulation;
|
|
259
|
+
transition: filter var(--duration) var(--ease), background-color var(--duration) var(--ease), border-color var(--duration) var(--ease), color var(--duration) var(--ease);
|
|
260
|
+
}
|
|
261
|
+
.ui-button:hover {
|
|
262
|
+
filter: brightness(0.92);
|
|
263
|
+
color: var(--color-on-accent);
|
|
264
|
+
}
|
|
265
|
+
.ui-button:active {
|
|
266
|
+
filter: brightness(0.85);
|
|
267
|
+
}
|
|
268
|
+
.ui-button:focus-visible {
|
|
269
|
+
outline: 2px solid var(--color-accent);
|
|
270
|
+
outline-offset: 2px;
|
|
271
|
+
}
|
|
272
|
+
.ui-button:disabled,
|
|
273
|
+
.ui-button[aria-disabled="true"] {
|
|
274
|
+
opacity: 0.45;
|
|
275
|
+
cursor: default;
|
|
276
|
+
filter: none;
|
|
277
|
+
}
|
|
278
|
+
.ui-button.secondary {
|
|
279
|
+
background: var(--color-surface);
|
|
280
|
+
color: var(--color-ink);
|
|
281
|
+
border-color: var(--color-line);
|
|
282
|
+
}
|
|
283
|
+
.ui-button.secondary:hover {
|
|
284
|
+
filter: none;
|
|
285
|
+
border-color: var(--color-accent-ring);
|
|
286
|
+
color: var(--color-accent-text);
|
|
287
|
+
}
|
|
288
|
+
.ui-button.block {
|
|
289
|
+
width: 100%;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/* ── list: a group of rows ──────────────────────────────────────────────── */
|
|
293
|
+
|
|
294
|
+
.ui-group {
|
|
295
|
+
display: flex;
|
|
296
|
+
flex-direction: column;
|
|
297
|
+
gap: var(--space-2);
|
|
298
|
+
}
|
|
299
|
+
.ui-group-head {
|
|
300
|
+
display: flex;
|
|
301
|
+
align-items: center;
|
|
302
|
+
justify-content: space-between;
|
|
303
|
+
gap: var(--space-3);
|
|
304
|
+
padding: 0 var(--space-1);
|
|
305
|
+
}
|
|
306
|
+
.ui-group-head h2 {
|
|
307
|
+
margin: 0;
|
|
308
|
+
font-size: var(--text-sm);
|
|
309
|
+
font-weight: var(--weight-semibold);
|
|
310
|
+
color: var(--color-ink-2);
|
|
311
|
+
}
|
|
312
|
+
.ui-group-body {
|
|
313
|
+
background: var(--color-surface);
|
|
314
|
+
border: 1px solid var(--color-line);
|
|
315
|
+
border-radius: var(--radius-card);
|
|
316
|
+
overflow: hidden;
|
|
317
|
+
}
|
|
318
|
+
.ui-group-body > .ui-row + .ui-row {
|
|
319
|
+
border-top: 1px solid var(--color-line);
|
|
320
|
+
}
|
|
321
|
+
.ui-group-body > .pad {
|
|
322
|
+
padding: var(--space-4);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/* List row: icon, title and subtitle, a value or a chevron. A <button> or <a> when tappable. */
|
|
326
|
+
.ui-row {
|
|
327
|
+
display: flex;
|
|
328
|
+
align-items: center;
|
|
329
|
+
gap: var(--space-3);
|
|
330
|
+
width: 100%;
|
|
331
|
+
min-height: 56px;
|
|
332
|
+
padding: var(--space-3) var(--space-4);
|
|
333
|
+
background: none;
|
|
334
|
+
border: 0;
|
|
335
|
+
border-radius: 0;
|
|
336
|
+
color: var(--color-ink);
|
|
337
|
+
text-align: left;
|
|
338
|
+
text-decoration: none;
|
|
339
|
+
font: inherit;
|
|
340
|
+
font-weight: var(--weight-regular);
|
|
341
|
+
}
|
|
342
|
+
.ui-row.tappable {
|
|
343
|
+
cursor: pointer;
|
|
344
|
+
transition: background-color var(--duration) var(--ease);
|
|
345
|
+
}
|
|
346
|
+
.ui-row.tappable:hover,
|
|
347
|
+
.ui-row.tappable:active {
|
|
348
|
+
background: var(--color-surface-2);
|
|
349
|
+
filter: none;
|
|
350
|
+
}
|
|
351
|
+
.ui-row.danger .row-title {
|
|
352
|
+
color: var(--color-danger);
|
|
353
|
+
}
|
|
354
|
+
.ui-row.muted {
|
|
355
|
+
opacity: 0.55;
|
|
356
|
+
}
|
|
357
|
+
.row-icon {
|
|
358
|
+
flex: none;
|
|
359
|
+
display: grid;
|
|
360
|
+
place-items: center;
|
|
361
|
+
width: 38px;
|
|
362
|
+
height: 38px;
|
|
363
|
+
border-radius: var(--radius-md);
|
|
364
|
+
background: var(--color-surface-2);
|
|
365
|
+
color: var(--color-ink-2);
|
|
366
|
+
}
|
|
367
|
+
.row-icon.accent {
|
|
368
|
+
background: var(--color-accent-soft);
|
|
369
|
+
color: var(--color-accent-text);
|
|
370
|
+
}
|
|
371
|
+
.row-icon.good {
|
|
372
|
+
background: var(--color-success-soft);
|
|
373
|
+
color: var(--color-success);
|
|
374
|
+
}
|
|
375
|
+
.row-icon.bad {
|
|
376
|
+
background: var(--color-danger-soft);
|
|
377
|
+
color: var(--color-danger);
|
|
378
|
+
}
|
|
379
|
+
.row-text {
|
|
380
|
+
flex: 1;
|
|
381
|
+
min-width: 0;
|
|
382
|
+
display: flex;
|
|
383
|
+
flex-direction: column;
|
|
384
|
+
gap: 2px;
|
|
385
|
+
}
|
|
386
|
+
.row-title {
|
|
387
|
+
font-weight: var(--weight-medium);
|
|
388
|
+
overflow-wrap: anywhere;
|
|
389
|
+
}
|
|
390
|
+
.row-sub {
|
|
391
|
+
font-size: var(--text-sm);
|
|
392
|
+
color: var(--color-ink-3);
|
|
393
|
+
overflow-wrap: anywhere;
|
|
394
|
+
}
|
|
395
|
+
.row-right {
|
|
396
|
+
flex: none;
|
|
397
|
+
font-variant-numeric: tabular-nums;
|
|
398
|
+
font-weight: var(--weight-semibold);
|
|
399
|
+
text-align: right;
|
|
400
|
+
}
|
|
401
|
+
.row-right.plus { color: var(--color-success); }
|
|
402
|
+
.row-chev {
|
|
403
|
+
flex: none;
|
|
404
|
+
color: var(--color-ink-3);
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
/* ── pill ───────────────────────────────────────────────────────────────── */
|
|
408
|
+
|
|
409
|
+
.ui-pill {
|
|
410
|
+
display: inline-flex;
|
|
411
|
+
align-items: center;
|
|
412
|
+
gap: 6px;
|
|
413
|
+
padding: 4px 10px 4px 8px;
|
|
414
|
+
border-radius: var(--radius-pill);
|
|
415
|
+
font-size: var(--text-xs);
|
|
416
|
+
font-weight: var(--weight-medium);
|
|
417
|
+
white-space: nowrap;
|
|
418
|
+
background: var(--color-surface-2);
|
|
419
|
+
color: var(--color-ink-2);
|
|
420
|
+
}
|
|
421
|
+
.ui-pill i {
|
|
422
|
+
width: 7px;
|
|
423
|
+
height: 7px;
|
|
424
|
+
border-radius: 50%;
|
|
425
|
+
background: currentColor;
|
|
426
|
+
}
|
|
427
|
+
.ui-pill.good { background: var(--color-success-soft); color: var(--color-success); }
|
|
428
|
+
.ui-pill.bad { background: var(--color-danger-soft); color: var(--color-danger); }
|
|
429
|
+
.ui-pill.warn { background: var(--color-warn-soft); color: var(--color-warn); }
|
|
430
|
+
.ui-pill.accent { background: var(--color-accent-soft); color: var(--color-accent-text); }
|
|
431
|
+
.ui-pill.pulse i {
|
|
432
|
+
animation: ui-pulse 1.6s ease-in-out infinite;
|
|
433
|
+
}
|
|
434
|
+
@keyframes ui-pulse {
|
|
435
|
+
0%, 100% { opacity: 1; transform: scale(1); }
|
|
436
|
+
50% { opacity: 0.4; transform: scale(0.7); }
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
/* ── bottom sheet ───────────────────────────────────────────────────────────
|
|
440
|
+
* Forms and details open in a sheet, not a new page. A centred dialog from 640px up. */
|
|
441
|
+
|
|
442
|
+
body.ui-sheet-open { overflow: hidden; }
|
|
443
|
+
.ui-sheet-backdrop {
|
|
444
|
+
position: fixed;
|
|
445
|
+
inset: 0;
|
|
446
|
+
z-index: 50;
|
|
447
|
+
background: var(--color-backdrop);
|
|
448
|
+
display: flex;
|
|
449
|
+
align-items: flex-end;
|
|
450
|
+
justify-content: center;
|
|
451
|
+
animation: ui-fade var(--duration) var(--ease);
|
|
452
|
+
}
|
|
453
|
+
.ui-sheet {
|
|
454
|
+
width: 100%;
|
|
455
|
+
max-width: var(--page-max);
|
|
456
|
+
max-height: 92dvh;
|
|
457
|
+
display: flex;
|
|
458
|
+
flex-direction: column;
|
|
459
|
+
background: var(--color-bg);
|
|
460
|
+
border-radius: var(--radius-hero) var(--radius-hero) 0 0;
|
|
461
|
+
box-shadow: var(--shadow-raised);
|
|
462
|
+
animation: ui-rise 260ms var(--ease);
|
|
463
|
+
padding-bottom: var(--ambasada-safe-area-bottom, 0px);
|
|
464
|
+
}
|
|
465
|
+
.ui-sheet-grip {
|
|
466
|
+
width: 36px;
|
|
467
|
+
height: 4px;
|
|
468
|
+
margin: 8px auto 0;
|
|
469
|
+
border-radius: 2px;
|
|
470
|
+
background: var(--color-line-strong);
|
|
471
|
+
}
|
|
472
|
+
.ui-sheet-head {
|
|
473
|
+
display: flex;
|
|
474
|
+
align-items: center;
|
|
475
|
+
justify-content: space-between;
|
|
476
|
+
padding: var(--space-3) var(--space-4) 0 var(--space-5);
|
|
477
|
+
}
|
|
478
|
+
.ui-sheet-head h2 {
|
|
479
|
+
margin: 0;
|
|
480
|
+
font-size: var(--text-xl);
|
|
481
|
+
}
|
|
482
|
+
.ui-sheet-body {
|
|
483
|
+
overflow-y: auto;
|
|
484
|
+
padding: var(--space-4) var(--space-5);
|
|
485
|
+
display: flex;
|
|
486
|
+
flex-direction: column;
|
|
487
|
+
gap: var(--space-4);
|
|
488
|
+
}
|
|
489
|
+
.ui-sheet-foot {
|
|
490
|
+
padding: var(--space-3) var(--space-5) var(--space-4);
|
|
491
|
+
border-top: 1px solid var(--color-line);
|
|
492
|
+
display: flex;
|
|
493
|
+
gap: var(--space-2);
|
|
494
|
+
}
|
|
495
|
+
.ui-sheet-foot > button,
|
|
496
|
+
.ui-sheet-foot > .ui-button { flex: 1; }
|
|
497
|
+
@keyframes ui-rise { from { transform: translateY(40px); opacity: 0; } to { transform: none; opacity: 1; } }
|
|
498
|
+
@keyframes ui-fade { from { opacity: 0; } to { opacity: 1; } }
|
|
499
|
+
@media (min-width: 640px) {
|
|
500
|
+
.ui-sheet-backdrop { align-items: center; padding: var(--space-6); }
|
|
501
|
+
.ui-sheet { border-radius: var(--radius-hero); max-height: 86dvh; }
|
|
502
|
+
.ui-sheet-grip { display: none; }
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
/* Round icon button (the sheet's close). */
|
|
506
|
+
.ui-icon-btn {
|
|
507
|
+
width: var(--tap);
|
|
508
|
+
height: var(--tap);
|
|
509
|
+
min-height: 0;
|
|
510
|
+
padding: 0;
|
|
511
|
+
display: grid;
|
|
512
|
+
place-items: center;
|
|
513
|
+
border-radius: 50%;
|
|
514
|
+
background: var(--color-surface-2);
|
|
515
|
+
color: var(--color-ink-2);
|
|
516
|
+
border: 0;
|
|
517
|
+
cursor: pointer;
|
|
518
|
+
}
|
|
519
|
+
.ui-icon-btn:hover { filter: none; color: var(--color-ink); }
|
|
520
|
+
|
|
521
|
+
/* ── stepper ────────────────────────────────────────────────────────────────
|
|
522
|
+
* − value + for amounts, with optional preset chips underneath. */
|
|
523
|
+
|
|
524
|
+
.ui-stepper-wrap { display: flex; flex-direction: column; gap: var(--space-2); }
|
|
525
|
+
.ui-stepper {
|
|
526
|
+
display: flex;
|
|
527
|
+
align-items: center;
|
|
528
|
+
justify-content: space-between;
|
|
529
|
+
border: 1px solid var(--color-line);
|
|
530
|
+
border-radius: var(--radius-md);
|
|
531
|
+
background: var(--color-surface);
|
|
532
|
+
padding: 4px;
|
|
533
|
+
}
|
|
534
|
+
.ui-stepper button {
|
|
535
|
+
width: 48px;
|
|
536
|
+
height: 44px;
|
|
537
|
+
min-height: 0;
|
|
538
|
+
padding: 0;
|
|
539
|
+
display: grid;
|
|
540
|
+
place-items: center;
|
|
541
|
+
border-radius: 9px;
|
|
542
|
+
background: var(--color-surface-2);
|
|
543
|
+
color: var(--color-ink);
|
|
544
|
+
border: 0;
|
|
545
|
+
cursor: pointer;
|
|
546
|
+
}
|
|
547
|
+
.ui-stepper button:hover { filter: none; background: var(--color-line); }
|
|
548
|
+
.ui-stepper button:disabled { opacity: 0.45; cursor: default; }
|
|
549
|
+
.ui-stepper-value {
|
|
550
|
+
font-size: 1.25rem;
|
|
551
|
+
font-weight: var(--weight-semibold);
|
|
552
|
+
font-variant-numeric: tabular-nums;
|
|
553
|
+
}
|
|
554
|
+
.ui-stepper-value small { font-size: var(--text-xs); color: var(--color-ink-3); font-weight: var(--weight-regular); }
|
|
555
|
+
.ui-presets { display: flex; gap: 6px; flex-wrap: wrap; }
|
|
556
|
+
.ui-presets button {
|
|
557
|
+
min-height: 34px;
|
|
558
|
+
padding: 4px 12px;
|
|
559
|
+
border-radius: var(--radius-pill);
|
|
560
|
+
background: var(--color-surface);
|
|
561
|
+
color: var(--color-ink-2);
|
|
562
|
+
border: 1px solid var(--color-line);
|
|
563
|
+
font-family: inherit;
|
|
564
|
+
font-size: var(--text-sm);
|
|
565
|
+
cursor: pointer;
|
|
566
|
+
}
|
|
567
|
+
.ui-presets button:hover { filter: none; border-color: var(--color-line-strong); }
|
|
568
|
+
.ui-presets button.on { background: var(--color-ink); color: var(--color-bg); border-color: var(--color-ink); }
|
|
569
|
+
|
|
570
|
+
/* ── empty state ────────────────────────────────────────────────────────── */
|
|
571
|
+
|
|
572
|
+
.ui-empty {
|
|
573
|
+
display: flex;
|
|
574
|
+
flex-direction: column;
|
|
575
|
+
align-items: center;
|
|
576
|
+
gap: var(--space-2);
|
|
577
|
+
padding: var(--space-8) var(--space-4);
|
|
578
|
+
text-align: center;
|
|
579
|
+
color: var(--color-ink-2);
|
|
580
|
+
}
|
|
581
|
+
.ui-empty-icon {
|
|
582
|
+
display: grid;
|
|
583
|
+
place-items: center;
|
|
584
|
+
width: 64px;
|
|
585
|
+
height: 64px;
|
|
586
|
+
border-radius: 50%;
|
|
587
|
+
background: var(--color-surface-2);
|
|
588
|
+
color: var(--color-ink-3);
|
|
589
|
+
margin-bottom: var(--space-2);
|
|
590
|
+
}
|
|
591
|
+
.ui-empty strong { color: var(--color-ink); font-size: var(--text-lg); }
|
|
592
|
+
.ui-empty > button, .ui-empty > a.button, .ui-empty > .ui-button { margin-top: var(--space-3); }
|
|
593
|
+
|
|
594
|
+
/* ── skeleton ───────────────────────────────────────────────────────────────
|
|
595
|
+
* Grey bars where the content will be: skeletons, not spinners. */
|
|
596
|
+
|
|
597
|
+
.ui-skeleton { display: flex; flex-direction: column; gap: 10px; padding: var(--space-4); }
|
|
598
|
+
.ui-skeleton span {
|
|
599
|
+
display: block;
|
|
600
|
+
height: 14px;
|
|
601
|
+
border-radius: 7px;
|
|
602
|
+
background: linear-gradient(90deg, var(--color-surface-2) 25%, var(--color-line) 50%, var(--color-surface-2) 75%);
|
|
603
|
+
background-size: 200% 100%;
|
|
604
|
+
animation: ui-shimmer 1.4s linear infinite;
|
|
605
|
+
}
|
|
606
|
+
@keyframes ui-shimmer { to { background-position: -200% 0; } }
|
|
607
|
+
|
|
608
|
+
@media (prefers-reduced-motion: reduce) {
|
|
609
|
+
.ui-sheet,
|
|
610
|
+
.ui-sheet-backdrop,
|
|
611
|
+
.ui-pill.pulse i,
|
|
612
|
+
.ui-skeleton span {
|
|
613
|
+
animation: none;
|
|
614
|
+
}
|
|
615
|
+
}
|