From 75bfc0aa85f88513173ad7eacf509e046a4ac805 Mon Sep 17 00:00:00 2001 From: Jascha Date: Sun, 24 May 2026 21:54:42 -0700 Subject: [PATCH 1/2] fix(snd): make how-it-works close button visible on mobile The X was absolutely-positioned with negative top/right offsets and muted color, which on small viewports could land outside the visible modal-card or get visually lost. Move it into a flex header row next to the title as a proper 44x44 SVG button with text-color contrast and a visible 1px border on hover/focus. --- app/ui/howItWorksDialog.js | 26 +++++++++++++++-------- app/ui/tokens.css | 42 +++++++++++++++++++++++--------------- 2 files changed, 43 insertions(+), 25 deletions(-) diff --git a/app/ui/howItWorksDialog.js b/app/ui/howItWorksDialog.js index 1987a8bf..ae0983b5 100644 --- a/app/ui/howItWorksDialog.js +++ b/app/ui/howItWorksDialog.js @@ -6,15 +6,25 @@ module.exports = function howItWorksDialog() { return html` - +
+

${t('howItWorksTitle')}

+ +
-

${t('howItWorksTitle')}

${t('howItWorksLede')}

${diagram(t)} diff --git a/app/ui/tokens.css b/app/ui/tokens.css index c2ff2047..5ab7e7ff 100644 --- a/app/ui/tokens.css +++ b/app/ui/tokens.css @@ -644,26 +644,39 @@ send-upload-area { position: relative; } +.snd-how-head { + display: flex; + align-items: flex-start; + justify-content: space-between; + gap: 12px; + margin: 0 0 8px; +} + .snd-how-close { - position: absolute; - top: -8px; - right: -8px; + flex: 0 0 auto; + width: 44px; + height: 44px; + margin: -10px -10px 0 0; + display: inline-flex; + align-items: center; + justify-content: center; background: transparent; - border: 0; - color: var(--snd-mute); - font-size: 24px; - line-height: 1; - padding: 8px; + border: 1px solid var(--snd-line); + color: var(--snd-text); cursor: pointer; - transition: color 120ms ease; + transition: border-color 120ms ease, color 120ms ease, background 120ms ease; } -.snd-how-close:hover { - color: var(--snd-text); +.snd-how-close:hover, +.snd-how-close:focus-visible { + border-color: var(--snd-accent); + color: var(--snd-accent); + outline: 0; } .snd-how-title { - margin: 0 0 8px; + margin: 0; + flex: 1 1 auto; } .snd-how-lede { @@ -807,11 +820,6 @@ send-upload-area { } @media (max-width: 480px) { - .snd-how-close { - top: -4px; - right: -4px; - } - .snd-how-step { gap: 12px; } From fa51345d90c0431e137a4988bb2a24737ef1112f Mon Sep 17 00:00:00 2001 From: Jascha Date: Mon, 25 May 2026 15:08:49 -0700 Subject: [PATCH 2/2] fix(snd): tighten notFound/404 page alignment and broaden copy .snd-complete was using gap: 20px without display: flex, so children fell back to default block alignment and inherited a mix of text-center / no-align rules, which on mobile produced the visible misalignment between the hero (left), tag (left), title (centered), caption (left and wrapping awkwardly across "from server"), and button (left). - Make .snd-complete a centered flex column with text-align: center, zero-out child margins so the gap is consistent, and add small-viewport overrides (gap, padding, heading size). - Drop the redundant text-center / paragraph-wrapped-button markup in notFound.js. - Replace the hand-typed "> share no longer..." caption with a translated string and remove the leading > glyph that was forcing a bad wrap on narrow screens. - Broaden the copy (expiredTitle/expiredTag/expiredCaption) so the same page reads honestly both for expired share links and for literal mistyped URLs (the SPA's catch-all route). --- app/ui/notFound.js | 16 +++++++--------- app/ui/tokens.css | 30 +++++++++++++++++++++++++++++- public/locales/en-US/send.ftl | 4 +++- 3 files changed, 39 insertions(+), 11 deletions(-) diff --git a/app/ui/notFound.js b/app/ui/notFound.js index a3108666..ac23b0dd 100644 --- a/app/ui/notFound.js +++ b/app/ui/notFound.js @@ -15,21 +15,19 @@ module.exports = function(state, emit) { -

EXPIRED

-

+

${state.translate('expiredTag')}

+

${state.translate('expiredTitle')}

- > share no longer available · file removed from server + ${state.translate('expiredCaption')}

-

+

${state.translate('trySendDescription')}

-

- - ${state.translate(btnText)} - -

+ + ${state.translate(btnText)} + `; diff --git a/app/ui/tokens.css b/app/ui/tokens.css index 5ab7e7ff..7778c2d4 100644 --- a/app/ui/tokens.css +++ b/app/ui/tokens.css @@ -868,12 +868,20 @@ send-upload-area { /* Download-complete hero — nested squares with sage check */ .snd-complete { - gap: 20px; + display: flex; + flex-direction: column; + align-items: center; + text-align: center; + gap: 16px; padding: 40px 24px; max-width: 560px; margin: 0 auto; } +.snd-complete > * { + margin: 0; +} + .snd-complete-hero { display: inline-flex; align-items: center; @@ -881,6 +889,26 @@ send-upload-area { padding-bottom: 4px; } +.snd-complete .snd-caption { + max-width: 36ch; + line-height: 1.6; +} + +@media (max-width: 480px) { + .snd-complete { + padding: 32px 20px; + gap: 14px; + } + + .snd-complete .snd-display { + font-size: 24px; + } + + .snd-complete .snd-caption { + font-size: 10px; + } +} + /* Inline QR — hidden on desktop, visible on mobile (<=768px) */ .snd-qr-inline { display: none; diff --git a/public/locales/en-US/send.ftl b/public/locales/en-US/send.ftl index 4cefe0dd..b16dec3d 100644 --- a/public/locales/en-US/send.ftl +++ b/public/locales/en-US/send.ftl @@ -118,7 +118,9 @@ tooManyArchives = { $count -> [one] Only 1 archive is allowed. *[other] Only { $count } archives are allowed. } -expiredTitle = This link has expired. +expiredTitle = This link isn’t available. +expiredTag = expired or missing +expiredCaption = The share may have expired, hit its download limit, or never existed. notSupportedDescription = { -send-brand } will not work with this browser. { -send-short-brand } works best with the latest version of { -firefox }, and will work with the current version of most browsers. downloadFirefox = Download { -firefox } legalTitle = { -send-short-brand } Privacy Notice