From d134702da7fa013f40ef966eed22f2ec65ca60c6 Mon Sep 17 00:00:00 2001
From: Jure <44338+hoornet@users.noreply.github.com>
Date: Thu, 9 Apr 2026 13:32:59 +0200
Subject: [PATCH] =?UTF-8?q?Polish=20pass=20=E2=80=94=20consistency,=20a11y?=
=?UTF-8?q?,=20theme=20correctness?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- Fix bg-white toggle thumbs in Settings (broke on dark themes)
- Eliminate 2px layout shift when switching Media feed tabs
- Unify "Follow"/"Mute" capitalization in NoteCard context menu
- Replace ASCII "..." with unicode ellipsis across compose/search/article
- Add rounded-sm to dropdowns, emoji picker, post/reply buttons
- Add aria-labels to sidebar toggle and onboarding copy buttons
- Add role=tablist/tab/aria-selected to login mode tabs
- Replace inline width/height styles with Tailwind w-16 h-16 in ProfileView
- Replace inline transform style with rotate-90 class in SettingsView
- Unify sidebar active state opacity (bg-accent/8 → bg-accent/10)
- Pad sidebar badges with py-0.5 for consistent pill height
- Match thread reply button sizing to compose post button
- Use var(--font-reading) on non-zen article title for consistency
- Format 'saved Xs ago' as minutes/hours after 60s
- Unify expand chevron to ▶ + rotate-90 pattern
- PollWidget: transition-all → transition-colors (no layout animation)
- Remove cryptic 'Ctrl+Enter' hint from compose and thread reply
---
src/components/article/ArticleEditor.tsx | 13 ++++++++++++-
src/components/article/ArticleView.tsx | 2 +-
src/components/feed/ComposeBox.tsx | 7 +++----
src/components/feed/NoteActions.tsx | 5 +++--
src/components/feed/NoteCard.tsx | 6 +++---
src/components/media/MediaFeed.tsx | 6 +++---
src/components/onboarding/OnboardingFlow.tsx | 6 +++++-
src/components/poll/PollCompose.tsx | 2 +-
src/components/poll/PollWidget.tsx | 2 +-
src/components/profile/ProfileView.tsx | 8 +++-----
src/components/search/SearchView.tsx | 4 ++--
src/components/shared/RelaysView.tsx | 2 +-
src/components/shared/SettingsView.tsx | 6 +++---
src/components/sidebar/Sidebar.tsx | 8 +++++---
src/components/thread/ThreadNode.tsx | 7 +++----
15 files changed, 49 insertions(+), 35 deletions(-)
diff --git a/src/components/article/ArticleEditor.tsx b/src/components/article/ArticleEditor.tsx
index a7a66cb..e57b2cb 100644
--- a/src/components/article/ArticleEditor.tsx
+++ b/src/components/article/ArticleEditor.tsx
@@ -20,6 +20,16 @@ function extractImages(md: string): { alt: string; url: string }[] {
return images;
}
+function formatSavedAgo(elapsedMs: number): string {
+ const s = Math.floor(elapsedMs / 1000);
+ if (s < 5) return "just now";
+ if (s < 60) return `${s}s ago`;
+ const m = Math.floor(s / 60);
+ if (m < 60) return `${m}m ago`;
+ const h = Math.floor(m / 60);
+ return `${h}h ago`;
+}
+
export function ArticleEditor() {
const { goBack } = useUIStore();
const { activeDraftId, drafts, updateDraft, deleteDraft, setActiveDraft, createDraft } = useDraftStore();
@@ -279,7 +289,7 @@ export function ArticleEditor() {
{wordCount > 0 ? `${wordCount} words` : "New article"}
{activeDraft && !published && lastSaved && (
- · saved {Math.floor((Date.now() - lastSaved) / 1000) < 5 ? "just now" : `${Math.floor((Date.now() - lastSaved) / 1000)}s ago`}
+ · saved {formatSavedAgo(Date.now() - lastSaved)}
)}
{published && publishedRelays > 0 && (
@@ -396,6 +406,7 @@ export function ArticleEditor() {
onChange={(e) => setTitle(e.target.value)}
placeholder="Title"
className="w-full bg-transparent text-text text-2xl font-bold placeholder:text-text-dim focus:outline-none"
+ style={{ fontFamily: "var(--font-reading)" }}
/>
diff --git a/src/components/article/ArticleView.tsx b/src/components/article/ArticleView.tsx
index 75b49aa..eb28861 100644
--- a/src/components/article/ArticleView.tsx
+++ b/src/components/article/ArticleView.tsx
@@ -306,7 +306,7 @@ export function ArticleView() {
value={commentText}
onChange={(e) => { setCommentText(e.target.value); autoResize(e); }}
onKeyDown={(e) => { if (e.key === "Enter" && !e.shiftKey) { e.preventDefault(); handleComment(); } }}
- placeholder="Write a comment about this article..."
+ placeholder="Write a comment…"
rows={3}
className="w-full bg-bg-raised border border-border rounded-sm px-3 py-2 text-[12px] text-text placeholder:text-text-dim resize-none focus:outline-none focus:border-accent leading-relaxed"
autoFocus
diff --git a/src/components/feed/ComposeBox.tsx b/src/components/feed/ComposeBox.tsx
index ad838b4..e280660 100644
--- a/src/components/feed/ComposeBox.tsx
+++ b/src/components/feed/ComposeBox.tsx
@@ -270,7 +270,7 @@ export function ComposeBox({ onPublished, onNoteInjected }: { onPublished?: () =
)}
@@ -125,7 +126,7 @@ export function NoteActions({ event, onReplyToggle, showReply }: NoteActionsProp
{showEmojiPicker && (
<>
setShowEmojiPicker(false)} />
-
+
{REACTION_EMOJIS.map((emoji) => (
+
{replyCount !== null && replyCount > 0 && (
↩ {replyCount}
)}
diff --git a/src/components/feed/NoteCard.tsx b/src/components/feed/NoteCard.tsx
index b8a5d8c..a4faf5d 100644
--- a/src/components/feed/NoteCard.tsx
+++ b/src/components/feed/NoteCard.tsx
@@ -121,18 +121,18 @@ export const NoteCard = memo(function NoteCard({ event, focused, onReplyInThread
{menuOpen && (
<>
setMenuOpen(false)} />
-
+
{ setMenuOpen(false); follows.includes(event.pubkey) ? unfollow(event.pubkey) : follow(event.pubkey); }}
className="w-full text-left px-3 py-2 text-[11px] text-text-muted hover:text-accent hover:bg-bg-hover transition-colors"
>
- {follows.includes(event.pubkey) ? `unfollow` : `follow`}
+ {follows.includes(event.pubkey) ? "Unfollow" : "Follow"}
{ setMenuOpen(false); isMuted ? unmute(event.pubkey) : mute(event.pubkey); }}
className="w-full text-left px-3 py-2 text-[11px] text-text-muted hover:text-danger hover:bg-bg-hover transition-colors"
>
- {isMuted ? `unmute` : `mute`}
+ {isMuted ? "Unmute" : "Mute"}
>
diff --git a/src/components/media/MediaFeed.tsx b/src/components/media/MediaFeed.tsx
index 1c07a6f..3df1429 100644
--- a/src/components/media/MediaFeed.tsx
+++ b/src/components/media/MediaFeed.tsx
@@ -51,10 +51,10 @@ export function MediaFeed() {
setTab(t)}
- className={`px-3 py-1 text-[12px] transition-colors ${
+ className={`px-3 py-1 text-[12px] transition-colors border-b-2 ${
tab === t
- ? "text-text border-b-2 border-accent"
- : "text-text-muted hover:text-text"
+ ? "text-text border-accent"
+ : "text-text-muted hover:text-text border-transparent"
}`}
>
{t.charAt(0).toUpperCase() + t.slice(1)}
diff --git a/src/components/onboarding/OnboardingFlow.tsx b/src/components/onboarding/OnboardingFlow.tsx
index f5902f5..4969b18 100644
--- a/src/components/onboarding/OnboardingFlow.tsx
+++ b/src/components/onboarding/OnboardingFlow.tsx
@@ -91,6 +91,7 @@ function CreateStep({ onNext }: { onNext: (signer: NDKPrivateKeySigner) => void
{signer.npub}
{copied ? "copied ✓" : "copy"}
@@ -158,6 +159,7 @@ function BackupStep({ signer, onComplete }: { signer: NDKPrivateKeySigner; onCom
{copied ? "copied ✓" : "copy"}
@@ -229,10 +231,12 @@ function LoginStep({ onBack, onComplete }: { onBack: () => void; onComplete: ()
Log in with your key.
-
+
{(["nsec", "npub", "bunker"] as const).map((m) => (
{ setMode(m); setValue(""); }}
className={`flex-1 py-2 text-[11px] transition-colors ${
mode === m ? "bg-accent/10 text-accent" : "text-text-dim hover:text-text"
diff --git a/src/components/poll/PollCompose.tsx b/src/components/poll/PollCompose.tsx
index 3fabb75..2231022 100644
--- a/src/components/poll/PollCompose.tsx
+++ b/src/components/poll/PollCompose.tsx
@@ -52,7 +52,7 @@ export function PollCompose({ options, onChange }: PollComposeProps) {
onClick={addOption}
className="text-accent hover:text-accent-hover text-[11px] transition-colors"
>
- + Add option
+ + Add option
)}
diff --git a/src/components/poll/PollWidget.tsx b/src/components/poll/PollWidget.tsx
index 3fe8950..2ceefed 100644
--- a/src/components/poll/PollWidget.tsx
+++ b/src/components/poll/PollWidget.tsx
@@ -63,7 +63,7 @@ export const PollWidget = memo(function PollWidget({ event }: { event: NDKEvent
disabled={showResults}
className={`
relative w-full text-left px-3 py-2 rounded-sm overflow-hidden
- transition-all duration-200
+ transition-colors duration-200
${showResults
? isMyVote
? "border border-accent/60"
diff --git a/src/components/profile/ProfileView.tsx b/src/components/profile/ProfileView.tsx
index b9163db..769cd3b 100644
--- a/src/components/profile/ProfileView.tsx
+++ b/src/components/profile/ProfileView.tsx
@@ -229,19 +229,17 @@ export function ProfileView() {
{/* Avatar + info */}
-
+
{avatar ? (

{ (e.target as HTMLImageElement).style.display = "none"; }}
/>
) : (
{name.charAt(0).toUpperCase()}
diff --git a/src/components/search/SearchView.tsx b/src/components/search/SearchView.tsx
index 39881d5..dc69c44 100644
--- a/src/components/search/SearchView.tsx
+++ b/src/components/search/SearchView.tsx
@@ -117,7 +117,7 @@ function SuggestionFollowButton({ pubkey }: { pubkey: string }) {
: "border-accent/60 text-accent hover:bg-accent hover:text-accent-text"
}`}
>
- {pending ? "..." : isFollowing ? "unfollow" : "follow"}
+ {pending ? "…" : isFollowing ? "Unfollow" : "Follow"}
);
}
@@ -410,7 +410,7 @@ export function SearchView() {
Based on who your follows follow
{suggestionsLoading && (
-
Finding suggestions...
+
Finding suggestions…
)}
{visibleSuggestions.map((s) => s.profile && (
diff --git a/src/components/shared/RelaysView.tsx b/src/components/shared/RelaysView.tsx
index 5070f44..ab1dbfa 100644
--- a/src/components/shared/RelaysView.tsx
+++ b/src/components/shared/RelaysView.tsx
@@ -80,7 +80,7 @@ function RelayHealthCard({ result, poolConnected, onRemove }: { result: RelayHea
>
remove
- {expanded ? "▾" : "▸"}
+ ▶
diff --git a/src/components/shared/SettingsView.tsx b/src/components/shared/SettingsView.tsx
index 036a095..9924fc7 100644
--- a/src/components/shared/SettingsView.tsx
+++ b/src/components/shared/SettingsView.tsx
@@ -50,7 +50,7 @@ function MuteSection() {
onClick={() => setExpanded(!expanded)}
className="flex items-center gap-2 w-full text-left group"
>
-
+
▶
@@ -280,7 +280,7 @@ function NotificationSection() {
}`}
>
@@ -412,7 +412,7 @@ function ExperimentalSection() {
}`}
>
diff --git a/src/components/sidebar/Sidebar.tsx b/src/components/sidebar/Sidebar.tsx
index d410a4c..c7b207a 100644
--- a/src/components/sidebar/Sidebar.tsx
+++ b/src/components/sidebar/Sidebar.tsx
@@ -46,6 +46,7 @@ export function Sidebar() {
›
@@ -60,6 +61,7 @@ export function Sidebar() {
‹
@@ -89,7 +91,7 @@ export function Sidebar() {
{!c &&
write article}
{!c && draftCount > 0 && (
-
{draftCount}
+
{draftCount}
)}
)}
@@ -103,7 +105,7 @@ export function Sidebar() {
title={c ? item.label : undefined}
className={`w-full text-left px-3 py-1.5 flex items-center gap-2 text-[12px] transition-colors ${
currentView === item.id
- ? "text-accent bg-accent/8"
+ ? "text-accent bg-accent/10"
: "text-text-muted hover:text-text hover:bg-bg-hover"
}`}
>
@@ -115,7 +117,7 @@ export function Sidebar() {
{!c &&
{item.label}}
{!c && badge > 0 && (
-
{badge}
+
{badge}
)}
);
diff --git a/src/components/thread/ThreadNode.tsx b/src/components/thread/ThreadNode.tsx
index 35291b5..2ba2a03 100644
--- a/src/components/thread/ThreadNode.tsx
+++ b/src/components/thread/ThreadNode.tsx
@@ -57,7 +57,7 @@ function InlineThreadReply({ replyTo, rootEvent, onPublished }: {
};
return (
-
+
-
Ctrl+Enter
- {sent ? "replied ✓" : replying ? "posting..." : "reply"}
+ {sent ? "replied ✓" : replying ? "posting…" : "reply"}