Skip to content

Commit e17dbc9

Browse files
authored
Merge branch 'main' into onboarding
2 parents f33afba + 2065097 commit e17dbc9

4 files changed

Lines changed: 193 additions & 109 deletions

File tree

frontend/src/app/globals.css

Lines changed: 78 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,14 @@ html {
8787
--surface-subtle: #111111;
8888
--accent: #ffffff;
8989

90+
/* Typography scale — improved readability in dark mode */
91+
--heading-letter-spacing: -0.03em;
92+
--body-line-height: 1.7;
93+
94+
/* Spacing scale — tighter in dark mode for visual density */
95+
--section-gap: 1.5rem;
96+
--card-padding: 1.25rem;
97+
9098
/* Pluto palette — dark-mode shift (cooler, deeper) */
9199
--pluto-900: #e8f2fb;
92100
--pluto-800: #c4ddf4;
@@ -100,6 +108,42 @@ html {
100108
--pluto-50: #060f1e;
101109
}
102110

111+
/* Dark mode responsive overrides */
112+
@media (max-width: 640px) {
113+
.dark {
114+
--section-gap: 1rem;
115+
--card-padding: 1rem;
116+
}
117+
118+
.dark .docs-prose h1 { font-size: 1.5rem; }
119+
.dark .docs-prose h2 { font-size: 1.2rem; }
120+
.dark .docs-prose h3 { font-size: 1.05rem; }
121+
}
122+
123+
@media (min-width: 641px) and (max-width: 1024px) {
124+
.dark {
125+
--section-gap: 1.25rem;
126+
--card-padding: 1.125rem;
127+
}
128+
}
129+
130+
/* Dark mode accessibility — ensure WCAG AA contrast ratios */
131+
.dark {
132+
/* Text colors meet 4.5:1 contrast against #0a0a0a background */
133+
--text-primary: #f5f5f5; /* 16.7:1 ratio */
134+
--text-secondary: #b0b0b0; /* 8.6:1 ratio — upgraded from #a0a0a0 */
135+
--text-tertiary: #808080; /* 4.5:1 ratio — minimum for AA */
136+
137+
/* Interactive element contrast */
138+
--interactive-default: #c0c0c0; /* 9.7:1 ratio */
139+
--interactive-hover: #f5f5f5; /* 16.7:1 ratio */
140+
--interactive-focus: #78aade; /* 5.1:1 ratio — meets AA for large text */
141+
142+
/* Ensure border visibility */
143+
--border-subtle: #2a2a2a; /* 1.7:1 ratio — visible as decorative border */
144+
--border-interactive: #5a5a5a; /* 3.2:1 ratio — for interactive borders */
145+
}
146+
103147
/* Dark mode scrollbar */
104148
.dark .overflow-x-auto {
105149
scrollbar-color: #2a2a2a transparent;
@@ -115,18 +159,31 @@ html {
115159

116160
/* Dark mode docs-prose overrides */
117161
.dark .docs-prose {
118-
color: #c8c8c8;
162+
color: #b0b0b0;
163+
line-height: var(--body-line-height, 1.7);
119164
}
120165

121166
.dark .docs-prose h1,
122167
.dark .docs-prose h2,
123168
.dark .docs-prose h3,
124169
.dark .docs-prose h4 {
125170
color: #f0f0f0;
171+
letter-spacing: var(--heading-letter-spacing, -0.03em);
126172
}
127173

128-
.dark .docs-prose h2 {
129-
border-bottom-color: #1f1f1f;
174+
.dark .docs-prose h1 { font-size: 1.875rem; line-height: 1.2; margin-bottom: 1.25rem; }
175+
.dark .docs-prose h2 { font-size: 1.375rem; line-height: 1.25; margin-bottom: 1rem; padding-bottom: 0.5rem; border-bottom: 1px solid #1f1f1f; }
176+
.dark .docs-prose h3 { font-size: 1.125rem; line-height: 1.3; margin-bottom: 0.75rem; }
177+
.dark .docs-prose h4 { font-size: 1rem; line-height: 1.35; margin-bottom: 0.5rem; }
178+
179+
.dark .docs-prose p {
180+
margin-bottom: 1rem;
181+
line-height: var(--body-line-height, 1.7);
182+
}
183+
184+
.dark .docs-prose li {
185+
margin-bottom: 0.375rem;
186+
line-height: 1.65;
130187
}
131188

132189
.dark .docs-prose p,
@@ -182,9 +239,11 @@ html {
182239
border-left-color: var(--pluto-400);
183240
}
184241

185-
/* Dark mode focus ring */
242+
/* Dark mode focus ring — high-contrast for accessibility */
186243
.dark *:focus-visible {
187244
@apply ring-offset-black;
245+
outline: 2px solid var(--interactive-focus, #78aade);
246+
outline-offset: 2px;
188247
}
189248

190249
:root {
@@ -232,6 +291,21 @@ body {
232291
line-height: 1.6;
233292
}
234293

294+
.dark body {
295+
line-height: var(--body-line-height, 1.7);
296+
}
297+
298+
/* Dark mode heading typography */
299+
.dark h1,
300+
.dark h2,
301+
.dark h3,
302+
.dark h4,
303+
.dark h5,
304+
.dark h6 {
305+
letter-spacing: var(--heading-letter-spacing, -0.03em);
306+
line-height: 1.2;
307+
}
308+
235309
.glass {
236310
@apply bg-white/[0.03] backdrop-blur-xl border border-white/10 shadow-2xl;
237311
}

frontend/src/components/MultisigApprovalModal.tsx

Lines changed: 52 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -197,48 +197,53 @@ export default function MultisigApprovalModal({
197197

198198
// Step components with improved accessibility
199199
const ReviewStep = () => (
200-
<div className="space-y-6" role="region" aria-label={t("review.sectionAriaLabel")}>
200+
<div className="space-y-5" role="region" aria-label={t("review.sectionAriaLabel")}>
201201
<div className="text-center">
202-
<h3 className="text-xl font-bold text-white" id="review-title">{t("review.heading")}</h3>
203-
<p className="mt-2 text-sm text-slate-400" id="review-description">
202+
<div className="inline-flex items-center justify-center w-12 h-12 rounded-full bg-mint/10 mb-3">
203+
<svg className="w-6 h-6 text-mint" fill="none" stroke="currentColor" viewBox="0 0 24 24">
204+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z" />
205+
</svg>
206+
</div>
207+
<h3 className="text-lg font-bold text-white" id="review-title">{t("review.heading")}</h3>
208+
<p className="mt-1.5 text-sm text-slate-400" id="review-description">
204209
{t("review.description")}
205210
</p>
206211
</div>
207212

208213
{transaction && (
209214
<div className="space-y-4">
210215
{/* Transaction Details */}
211-
<div className="rounded-xl border border-white/10 bg-white/5 p-4 space-y-3">
212-
<div className="flex justify-between items-center">
216+
<div className="rounded-xl border border-white/10 bg-white/[0.03] p-4 space-y-3">
217+
<div className="flex justify-between items-center py-2 border-b border-white/5">
213218
<span className="text-xs font-medium uppercase tracking-wider text-slate-500">{t("review.amount")}</span>
214-
<span className="font-mono text-sm text-white">
219+
<span className="font-mono text-sm font-semibold text-white">
215220
{transaction.amount} {transaction.assetCode}
216221
</span>
217222
</div>
218-
<div className="flex justify-between items-center">
223+
<div className="flex justify-between items-center py-2 border-b border-white/5">
219224
<span className="text-xs font-medium uppercase tracking-wider text-slate-500">{t("review.to")}</span>
220225
<div className="flex items-center gap-2">
221-
<span className="font-mono text-sm text-slate-200 truncate max-w-[200px]">
226+
<span className="font-mono text-sm text-slate-200 truncate max-w-[180px]">
222227
{transaction.destination}
223228
</span>
224229
<CopyButton text={transaction.destination} />
225230
</div>
226231
</div>
227232
{transaction.memo && (
228-
<div className="flex justify-between items-center">
233+
<div className="flex justify-between items-center py-2">
229234
<span className="text-xs font-medium uppercase tracking-wider text-slate-500">{t("review.memo")}</span>
230235
<span className="font-mono text-sm text-slate-200">{transaction.memo}</span>
231236
</div>
232237
)}
233238
</div>
234239

235240
{/* Signature Progress */}
236-
<div className="space-y-2">
241+
<div className="space-y-2.5">
237242
<div className="flex justify-between items-center">
238243
<span className="text-xs font-medium uppercase tracking-wider text-slate-500">
239244
{t("review.signaturesLabel", { signed: signedCount, required: requiredSignatures })}
240245
</span>
241-
<span className="text-xs text-slate-400">{Math.round(progress)}%</span>
246+
<span className="text-xs font-semibold text-mint">{Math.round(progress)}%</span>
242247
</div>
243248
<motion.div
244249
className="w-full bg-white/10 rounded-full h-2"
@@ -259,7 +264,7 @@ export default function MultisigApprovalModal({
259264
</div>
260265

261266
{/* Signers List */}
262-
<div className="space-y-2" role="region" aria-label={t("review.signersListAriaLabel")}>
267+
<div className="space-y-2.5" role="region" aria-label={t("review.signersListAriaLabel")}>
263268
<span className="text-xs font-medium uppercase tracking-wider text-slate-500" id="signers-label">{t("review.signersLabel")}</span>
264269
<motion.ul
265270
className="space-y-2"
@@ -272,10 +277,10 @@ export default function MultisigApprovalModal({
272277
<motion.li
273278
key={signer.id}
274279
variants={!prefersReducedMotion ? signerItemVariants : undefined}
275-
className={`flex items-center justify-between rounded-lg border p-3 transition-colors ${
280+
className={`flex items-center justify-between rounded-lg border p-3 transition-all duration-200 ${
276281
signer.hasSigned
277-
? "border-mint/30 bg-mint/5"
278-
: "border-white/10 bg-white/5"
282+
? "border-mint/30 bg-mint/5 shadow-sm shadow-mint/5"
283+
: "border-white/10 bg-white/[0.03] hover:bg-white/[0.06]"
279284
}`}
280285
role="listitem"
281286
aria-label={t("review.signerAriaLabel", {
@@ -353,29 +358,29 @@ export default function MultisigApprovalModal({
353358
);
354359

355360
const ProcessingStep = () => (
356-
<div className="flex flex-col items-center justify-center py-12 text-center" role="status">
357-
<div className="relative mb-6" aria-hidden="true">
358-
<div className="w-16 h-16 border-4 border-mint border-t-transparent rounded-full animate-spin" />
359-
<div className="absolute inset-0 w-16 h-16 border-4 border-mint/20 rounded-full animate-ping" />
361+
<div className="flex flex-col items-center justify-center py-10 text-center" role="status">
362+
<div className="relative mb-5" aria-hidden="true">
363+
<div className="w-14 h-14 border-[3px] border-mint border-t-transparent rounded-full animate-spin" />
364+
<div className="absolute inset-0 w-14 h-14 border-[3px] border-mint/20 rounded-full animate-ping" />
360365
</div>
361-
<h3 className="text-xl font-bold text-white">{t("processing.heading")}</h3>
362-
<p className="mt-2 text-sm text-slate-400">
366+
<h3 className="text-lg font-bold text-white">{t("processing.heading")}</h3>
367+
<p className="mt-1.5 text-sm text-slate-400 max-w-xs">
363368
{t("processing.description")}
364369
</p>
365370
</div>
366371
);
367372

368373
const ConfirmStep = () => (
369-
<div className="text-center space-y-6">
374+
<div className="text-center space-y-5">
370375
{isPendingConfirmation ? (
371376
<>
372-
<div className="relative mx-auto w-16 h-16" aria-hidden="true">
373-
<div className="w-16 h-16 border-4 border-mint border-t-transparent rounded-full animate-spin" />
374-
<div className="absolute inset-0 w-16 h-16 border-4 border-mint/20 rounded-full animate-ping" />
377+
<div className="relative mx-auto w-14 h-14" aria-hidden="true">
378+
<div className="w-14 h-14 border-[3px] border-mint border-t-transparent rounded-full animate-spin" />
379+
<div className="absolute inset-0 w-14 h-14 border-[3px] border-mint/20 rounded-full animate-ping" />
375380
</div>
376381
<div>
377-
<h3 className="text-xl font-bold text-white">{t("confirm.pendingHeading")}</h3>
378-
<p className="mt-2 text-sm text-slate-400">
382+
<h3 className="text-lg font-bold text-white">{t("confirm.pendingHeading")}</h3>
383+
<p className="mt-1.5 text-sm text-slate-400">
379384
{t("confirm.pendingDescription")}
380385
</p>
381386
</div>
@@ -401,14 +406,14 @@ export default function MultisigApprovalModal({
401406
</>
402407
) : (
403408
<>
404-
<div className="w-16 h-16 bg-mint/20 rounded-full flex items-center justify-center mx-auto">
405-
<svg className="w-8 h-8 text-mint" fill="none" stroke="currentColor" viewBox="0 0 24 24">
406-
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
409+
<div className="w-14 h-14 bg-mint/15 rounded-full flex items-center justify-center mx-auto ring-4 ring-mint/10">
410+
<svg className="w-7 h-7 text-mint" fill="none" stroke="currentColor" viewBox="0 0 24 24">
411+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2.5} d="M5 13l4 4L19 7" />
407412
</svg>
408413
</div>
409414
<div>
410-
<h3 className="text-xl font-bold text-white">{t("confirm.approvedHeading")}</h3>
411-
<p className="mt-2 text-sm text-slate-400">
415+
<h3 className="text-lg font-bold text-white">{t("confirm.approvedHeading")}</h3>
416+
<p className="mt-1.5 text-sm text-slate-400">
412417
{t("confirm.approvedDescription")}
413418
</p>
414419
</div>
@@ -437,15 +442,15 @@ export default function MultisigApprovalModal({
437442
);
438443

439444
const ErrorStep = () => (
440-
<div className="text-center space-y-6" role="alert">
441-
<div className="w-16 h-16 bg-red-500/20 rounded-full flex items-center justify-center mx-auto">
442-
<svg className="w-8 h-8 text-red-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
445+
<div className="text-center space-y-5" role="alert">
446+
<div className="w-14 h-14 bg-red-500/15 rounded-full flex items-center justify-center mx-auto ring-4 ring-red-500/10">
447+
<svg className="w-7 h-7 text-red-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
443448
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
444449
</svg>
445450
</div>
446451
<div>
447-
<h3 className="text-xl font-bold text-white">{t("error.heading")}</h3>
448-
<p className="mt-2 text-sm text-slate-400">
452+
<h3 className="text-lg font-bold text-white">{t("error.heading")}</h3>
453+
<p className="mt-1.5 text-sm text-slate-400 max-w-xs mx-auto">
449454
{error || t("error.defaultMessage")}
450455
</p>
451456
</div>
@@ -507,7 +512,7 @@ export default function MultisigApprovalModal({
507512
initial="hidden"
508513
animate="visible"
509514
exit="exit"
510-
className="relative w-full max-w-2xl overflow-hidden rounded-t-2xl sm:rounded-2xl border border-white/10 bg-[#050608] shadow-2xl backdrop-blur-xl outline-none"
515+
className="relative w-full max-w-lg overflow-hidden rounded-t-2xl sm:rounded-2xl border border-white/10 bg-[#050608] shadow-2xl backdrop-blur-xl outline-none"
511516
tabIndex={-1}
512517
role="dialog"
513518
aria-modal="true"
@@ -516,12 +521,12 @@ export default function MultisigApprovalModal({
516521
aria-describedby="multisig-modal-description"
517522
>
518523
{/* Header */}
519-
<div className="flex items-center justify-between border-b border-white/10 p-6">
524+
<div className="flex items-center justify-between border-b border-white/10 px-6 py-4">
520525
<div>
521-
<h2 id="multisig-modal-title" className="text-xl font-bold text-white">
526+
<h2 id="multisig-modal-title" className="text-lg font-bold text-white tracking-tight">
522527
{t("title")}
523528
</h2>
524-
<p id="multisig-modal-description" className="text-sm text-slate-400">
529+
<p id="multisig-modal-description" className="text-xs text-slate-400 mt-0.5">
525530
{isExpired
526531
? t("expired.statusLabel")
527532
: t("stepOf", {
@@ -561,7 +566,7 @@ export default function MultisigApprovalModal({
561566

562567
{/* Content */}
563568
<motion.div
564-
className="p-6 max-h-[70vh] overflow-y-auto"
569+
className="px-6 py-5 max-h-[70vh] overflow-y-auto"
565570
aria-live="polite"
566571
key={currentStep}
567572
variants={stepVariants}
@@ -570,15 +575,15 @@ export default function MultisigApprovalModal({
570575
exit="exit"
571576
>
572577
{isExpired ? (
573-
<div className="text-center space-y-6">
574-
<div className="w-16 h-16 bg-amber-500/20 rounded-full flex items-center justify-center mx-auto">
575-
<svg className="w-8 h-8 text-amber-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
578+
<div className="text-center space-y-5">
579+
<div className="w-14 h-14 bg-amber-500/15 rounded-full flex items-center justify-center mx-auto ring-4 ring-amber-500/10">
580+
<svg className="w-7 h-7 text-amber-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
576581
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" />
577582
</svg>
578583
</div>
579584
<div>
580-
<h3 className="text-xl font-bold text-white">{t("expired.heading")}</h3>
581-
<p className="mt-2 text-sm text-slate-400">
585+
<h3 className="text-lg font-bold text-white">{t("expired.heading")}</h3>
586+
<p className="mt-1.5 text-sm text-slate-400">
582587
{t("expired.description")}
583588
</p>
584589
</div>

0 commit comments

Comments
 (0)