Retro Bulletin Board Systems on atproto. Web app and TUI. lazy mirror of alyraffauf/atbbs atbbs.xyz
forums python tui atproto bbs
3
fork

Configure Feed

Select the types of activity you want to include in your feed.

web: improve contrast

+98 -98
+3 -3
web/src/components/ActivityList.tsx
··· 16 16 const [shown, setShown] = useState(PAGE_SIZE); 17 17 18 18 if (items.length === 0) 19 - return <p className="text-neutral-500">No activity yet.</p>; 19 + return <p className="text-neutral-400">No activity yet.</p>; 20 20 21 21 return ( 22 22 <div> ··· 31 31 className="block border border-neutral-800/50 rounded p-4 mb-2 hover:bg-neutral-800" 32 32 > 33 33 <PostMeta handle={item.handle} createdAt={item.createdAt} /> 34 - <p className="text-xs text-neutral-500 mb-1"> 34 + <p className="text-xs text-neutral-400 mb-1"> 35 35 {item.type === "quote" 36 36 ? "quoted your reply" 37 37 : `on: ${item.threadTitle}`} ··· 46 46 <div className="mt-4 text-center"> 47 47 <button 48 48 onClick={() => setShown((prev) => prev + PAGE_SIZE)} 49 - className="text-neutral-500 hover:text-neutral-300" 49 + className="text-neutral-400 hover:text-neutral-300" 50 50 > 51 51 show more 52 52 </button>
+6 -6
web/src/components/BBSPanel.tsx
··· 14 14 if (!hasBBS) { 15 15 return ( 16 16 <> 17 - <p className="text-neutral-500 mb-4"> 17 + <p className="text-neutral-400 mb-4"> 18 18 You haven't set up a BBS yet. 19 19 </p> 20 20 <ActionLink to="/account/create">create a bbs</ActionLink> ··· 26 26 <div className="grid grid-cols-2 gap-3 max-w-md"> 27 27 <Link to={`/bbs/${userHandle}`} className={cardStyle}> 28 28 <div className="text-neutral-200 mb-1">Browse</div> 29 - <div className="text-xs text-neutral-500">View your BBS.</div> 29 + <div className="text-xs text-neutral-400">View your BBS.</div> 30 30 </Link> 31 31 <Link to="/account/edit" className={cardStyle}> 32 32 <div className="text-neutral-200 mb-1">Edit</div> 33 - <div className="text-xs text-neutral-500">Name, boards, intro.</div> 33 + <div className="text-xs text-neutral-400">Name, boards, intro.</div> 34 34 </Link> 35 35 <Link to="/account/moderate" className={cardStyle}> 36 36 <div className="text-neutral-200 mb-1">Moderate</div> 37 - <div className="text-xs text-neutral-500">Bans and hidden posts.</div> 37 + <div className="text-xs text-neutral-400">Bans and hidden posts.</div> 38 38 </Link> 39 39 <button 40 40 onClick={onDelete} 41 41 className="text-left bg-neutral-900 border border-neutral-800 rounded px-4 py-3 hover:border-red-900" 42 42 > 43 - <div className="text-neutral-500 mb-1">Delete</div> 44 - <div className="text-xs text-neutral-600">Remove your BBS.</div> 43 + <div className="text-neutral-400 mb-1">Delete</div> 44 + <div className="text-xs text-neutral-400">Remove your BBS.</div> 45 45 </button> 46 46 </div> 47 47 );
+1 -1
web/src/components/DialBBS.tsx
··· 123 123 > 124 124 {entry.name} 125 125 {entry.name !== entry.handle && ( 126 - <span className="text-neutral-500 ml-2">{entry.handle}</span> 126 + <span className="text-neutral-400 ml-2">{entry.handle}</span> 127 127 )} 128 128 </Link> 129 129 ))}
+1 -1
web/src/components/DiscoveryList.tsx
··· 14 14 15 15 return ( 16 16 <div> 17 - <p className="text-neutral-500 text-xs uppercase tracking-wide mb-3"> 17 + <p className="text-neutral-400 text-xs uppercase tracking-wide mb-3"> 18 18 or try one of these 19 19 </p> 20 20 <div className="space-y-1">
+1 -1
web/src/components/ErrorPage.tsx
··· 28 28 return ( 29 29 <div className="py-16 text-center"> 30 30 <h1 className="text-lg text-neutral-200 mb-2">{title}</h1> 31 - {detail && <p className="text-neutral-500 mb-6">{detail}</p>} 31 + {detail && <p className="text-neutral-400 mb-6">{detail}</p>} 32 32 <ActionLink to="/">← back to home</ActionLink> 33 33 </div> 34 34 );
+1 -1
web/src/components/Localtime.tsx
··· 3 3 export default function Localtime({ iso }: { iso: string }) { 4 4 if (!iso) return null; 5 5 return ( 6 - <time className="text-xs text-neutral-500" title={formatFullDate(iso)}> 6 + <time className="text-xs text-neutral-400" title={formatFullDate(iso)}> 7 7 {relativeDate(iso)} 8 8 </time> 9 9 );
+6 -6
web/src/components/MyThreadList.tsx
··· 13 13 const [shown, setShown] = useState(PAGE_SIZE); 14 14 15 15 if (threads.length === 0) 16 - return <p className="text-neutral-500">You haven't posted any threads yet.</p>; 16 + return <p className="text-neutral-400">You haven't posted any threads yet.</p>; 17 17 18 18 return ( 19 19 <div> ··· 28 28 > 29 29 <div className="flex items-baseline gap-2"> 30 30 <span className="text-neutral-200">{thread.title}</span> 31 - <span className="text-neutral-600">·</span> 31 + <span className="text-neutral-400">·</span> 32 32 <time 33 - className="text-xs text-neutral-500" 33 + className="text-xs text-neutral-400" 34 34 title={formatFullDate(thread.createdAt)} 35 35 > 36 36 {relativeDate(thread.createdAt)} 37 37 </time> 38 38 </div> 39 - <p className="text-xs text-neutral-500 mt-1"> 39 + <p className="text-xs text-neutral-400 mt-1"> 40 40 on {thread.bbsHandle} 41 41 </p> 42 - <p className="text-neutral-500 text-sm line-clamp-2 mt-1"> 42 + <p className="text-neutral-400 text-sm line-clamp-2 mt-1"> 43 43 {thread.body.substring(0, 200)} 44 44 </p> 45 45 </Link> ··· 49 49 <div className="mt-4 text-center"> 50 50 <button 51 51 onClick={() => setShown((prev) => prev + PAGE_SIZE)} 52 - className="text-neutral-500 hover:text-neutral-300" 52 + className="text-neutral-400 hover:text-neutral-300" 53 53 > 54 54 show more 55 55 </button>
+2 -2
web/src/components/PinnedList.tsx
··· 13 13 14 14 if (pins.length === 0) 15 15 return ( 16 - <p className="text-neutral-500"> 16 + <p className="text-neutral-400"> 17 17 No pinned BBSes yet. Visit a BBS and pin it to see it here. 18 18 </p> 19 19 ); ··· 31 31 {shown < pins.length && ( 32 32 <button 33 33 onClick={() => setShown((prev) => prev + PAGE_SIZE)} 34 - className="text-xs text-neutral-500 hover:text-neutral-300 mt-2" 34 + className="text-xs text-neutral-400 hover:text-neutral-300 mt-2" 35 35 > 36 36 show more 37 37 </button>
+2 -2
web/src/components/form/BoardRowEditor.tsx
··· 26 26 return ( 27 27 <div> 28 28 <label className="block text-neutral-400 mb-1">Boards</label> 29 - <p className="text-neutral-500 text-xs mb-2"> 29 + <p className="text-neutral-400 text-xs mb-2"> 30 30 One board per row: slug, name, description 31 31 </p> 32 32 <div className="space-y-2"> ··· 58 58 <button 59 59 type="button" 60 60 onClick={() => onChange([...boards, { slug: "", name: "", desc: "" }])} 61 - className="mt-2 text-neutral-500 hover:text-neutral-300 text-xs" 61 + className="mt-2 text-neutral-400 hover:text-neutral-300 text-xs" 62 62 > 63 63 + add board 64 64 </button>
+2 -2
web/src/components/form/ComposeForm.tsx
··· 57 57 return ( 58 58 <form onSubmit={onSubmit} className={`space-y-3 ${className}`}> 59 59 {quote && onClearQuote && ( 60 - <div className="text-xs text-neutral-500"> 60 + <div className="text-xs text-neutral-400"> 61 61 <span>quoting {quote.handle}</span> 62 62 <button 63 63 type="button" 64 64 onClick={onClearQuote} 65 - className="text-neutral-500 hover:text-red-400 ml-2" 65 + className="text-neutral-400 hover:text-red-400 ml-2" 66 66 > 67 67 x 68 68 </button>
+2 -2
web/src/components/form/FileChips.tsx
··· 5 5 6 6 export default function FileChips({ files, onRemove }: FileChipsProps) { 7 7 return ( 8 - <div className="flex flex-wrap gap-2 text-xs text-neutral-500"> 8 + <div className="flex flex-wrap gap-2 text-xs text-neutral-400"> 9 9 {files.map((file, i) => ( 10 10 <span 11 11 key={i} ··· 15 15 <button 16 16 type="button" 17 17 onClick={() => onRemove(i)} 18 - className="text-neutral-500 hover:text-red-400" 18 + className="text-neutral-400 hover:text-red-400" 19 19 > 20 20 21 21 </button>
+2 -2
web/src/components/layout/Footer.tsx
··· 1 - const linkStyle = "text-neutral-500 hover:text-neutral-300"; 1 + const linkStyle = "text-neutral-400 hover:text-neutral-300"; 2 2 3 3 const links = [ 4 4 { href: "https://github.com/alyraffauf/atbbs", label: "github" }, ··· 8 8 export default function Footer() { 9 9 return ( 10 10 <footer className="border-t border-neutral-800 mt-auto"> 11 - <div className="max-w-2xl mx-auto px-4 py-4 flex items-center justify-between text-xs text-neutral-500"> 11 + <div className="max-w-2xl mx-auto px-4 py-4 flex items-center justify-between text-xs text-neutral-400"> 12 12 <span> 13 13 made by <a href="https://aly.codes" className={linkStyle}>aly.codes</a> 14 14 </span>
+2 -2
web/src/components/layout/Header.tsx
··· 4 4 import HeaderBreadcrumbs from "./HeaderBreadcrumbs"; 5 5 import MobileMenu from "./MobileMenu"; 6 6 7 - const linkStyle = "text-neutral-500 hover:text-neutral-300"; 7 + const linkStyle = "text-neutral-400 hover:text-neutral-300"; 8 8 9 9 export default function Header() { 10 10 const { user, logout } = useAuth(); ··· 18 18 return ( 19 19 <header className="border-b border-neutral-800"> 20 20 <div className="max-w-2xl mx-auto px-4 py-3 flex items-center justify-between"> 21 - <div className="hidden md:flex items-center gap-2 text-neutral-500 min-w-0 whitespace-nowrap"> 21 + <div className="hidden md:flex items-center gap-2 text-neutral-400 min-w-0 whitespace-nowrap"> 22 22 <Logo /> 23 23 <HeaderBreadcrumbs /> 24 24 </div>
+1 -1
web/src/components/layout/HeaderBreadcrumbs.tsx
··· 15 15 <Link 16 16 key={`crumb-${index}`} 17 17 to={crumb.to} 18 - className="text-neutral-500 hover:text-neutral-300" 18 + className="text-neutral-400 hover:text-neutral-300" 19 19 > 20 20 {crumb.label} 21 21 </Link>
+1 -1
web/src/components/layout/MobileMenu.tsx
··· 40 40 <Link to={`/profile/${encodeURIComponent(user.handle)}`} onClick={close} className="text-neutral-300 hover:text-neutral-200"> 41 41 {user.handle} 42 42 </Link> 43 - <button type="button" onClick={() => { close(); onLogout(); }} className="text-neutral-500 hover:text-neutral-300"> 43 + <button type="button" onClick={() => { close(); onLogout(); }} className="text-neutral-400 hover:text-neutral-300"> 44 44 log out 45 45 </button> 46 46 </>
+1 -1
web/src/components/nav/ListLink.tsx
··· 14 14 > 15 15 <span className="text-neutral-200">{name}</span> 16 16 {description && ( 17 - <span className="text-neutral-500 text-xs sm:text-sm"> 17 + <span className="text-neutral-400 text-xs sm:text-sm"> 18 18 {description} 19 19 </span> 20 20 )}
+2 -2
web/src/components/nav/PageNav.tsx
··· 66 66 <button 67 67 key={slot.key} 68 68 onClick={() => onGo(slot.page!)} 69 - className="text-neutral-500 hover:text-neutral-300 hover:bg-neutral-800 rounded px-3 py-1" 69 + className="text-neutral-400 hover:text-neutral-300 hover:bg-neutral-800 rounded px-3 py-1" 70 70 > 71 71 {slot.label} 72 72 </button> 73 73 ); 74 74 return ( 75 - <span key={slot.key} className="text-neutral-600 px-2 py-1"> 75 + <span key={slot.key} className="text-neutral-400 px-2 py-1"> 76 76 {slot.label} 77 77 </span> 78 78 );
+2 -2
web/src/components/nav/ThreadLink.tsx
··· 20 20 > 21 21 <div className="flex items-baseline justify-between gap-4"> 22 22 <span className="text-neutral-300 truncate">{title}</span> 23 - <span className="shrink-0 text-xs text-neutral-500">{meta}</span> 23 + <span className="shrink-0 text-xs text-neutral-400">{meta}</span> 24 24 </div> 25 - <p className="text-neutral-500 text-xs mt-1 line-clamp-1">{preview}</p> 25 + <p className="text-neutral-400 text-xs mt-1 line-clamp-1">{preview}</p> 26 26 </Link> 27 27 ); 28 28 }
+1 -1
web/src/components/post/AttachmentLink.tsx
··· 37 37 <a 38 38 href={`${pds}/xrpc/com.atproto.sync.getBlob?did=${did}&cid=${cid}`} 39 39 onClick={download} 40 - className="text-xs text-neutral-500 hover:text-neutral-300 block mt-1 cursor-pointer" 40 + className="text-xs text-neutral-400 hover:text-neutral-300 block mt-1 cursor-pointer" 41 41 > 42 42 [{name}] 43 43 </a>
+2 -2
web/src/components/post/PostActions.tsx
··· 1 - const actionStyle = "text-xs text-neutral-500 hover:text-red-400"; 1 + const actionStyle = "text-xs text-neutral-400 hover:text-red-400"; 2 2 3 3 interface PostActionsProps { 4 4 isAuthor: boolean; ··· 20 20 return ( 21 21 <span className="reply-actions flex items-center gap-3"> 22 22 {onQuote && ( 23 - <button onClick={onQuote} className="text-xs text-neutral-500 hover:text-neutral-300"> 23 + <button onClick={onQuote} className="text-xs text-neutral-400 hover:text-neutral-300"> 24 24 quote 25 25 </button> 26 26 )}
+2 -2
web/src/components/post/PostMeta.tsx
··· 25 25 > 26 26 {handle} 27 27 </span> 28 - <span className="text-neutral-600">·</span> 28 + <span className="text-neutral-400">·</span> 29 29 <time 30 - className="text-xs text-neutral-500" 30 + className="text-xs text-neutral-400" 31 31 title={formatFullDate(createdAt)} 32 32 > 33 33 {relativeDate(createdAt)}
+1 -1
web/src/components/post/ReplyCard.tsx
··· 62 62 <button 63 63 type="button" 64 64 onClick={onQuoteClick} 65 - className="block w-full text-left border-l-2 border-neutral-700 pl-3 mb-3 py-1 text-sm text-neutral-500 hover:border-neutral-500 cursor-pointer" 65 + className="block w-full text-left border-l-2 border-neutral-700 pl-3 mb-3 py-1 text-sm text-neutral-400 hover:border-neutral-500 cursor-pointer" 66 66 > 67 67 <span className="text-neutral-400">{quoted.handle}:</span>{" "} 68 68 <PostBody>
+4 -4
web/src/components/profile/EditProfile.tsx
··· 28 28 return ( 29 29 <div className="space-y-4"> 30 30 <div> 31 - <label className="text-xs text-neutral-500 uppercase tracking-wide"> 31 + <label className="text-xs text-neutral-400 uppercase tracking-wide"> 32 32 Name 33 33 </label> 34 34 <Input ··· 39 39 /> 40 40 </div> 41 41 <div> 42 - <label className="text-xs text-neutral-500 uppercase tracking-wide"> 42 + <label className="text-xs text-neutral-400 uppercase tracking-wide"> 43 43 Pronouns 44 44 </label> 45 45 <Input ··· 50 50 /> 51 51 </div> 52 52 <div> 53 - <label className="text-xs text-neutral-500 uppercase tracking-wide"> 53 + <label className="text-xs text-neutral-400 uppercase tracking-wide"> 54 54 Bio 55 55 </label> 56 56 <Textarea ··· 65 65 <Button onClick={handleSubmit}>save</Button> 66 66 <button 67 67 onClick={onCancel} 68 - className="text-neutral-500 hover:text-neutral-300 text-xs" 68 + className="text-neutral-400 hover:text-neutral-300 text-xs" 69 69 > 70 70 cancel 71 71 </button>
+6 -6
web/src/components/profile/ViewProfile.tsx
··· 26 26 <ActionButton onClick={onEdit}>edit profile</ActionButton> 27 27 )} 28 28 </div> 29 - <p className="text-neutral-500"> 29 + <p className="text-neutral-400"> 30 30 {handle} 31 31 {profile?.pronouns && ( 32 32 <> 33 - <span className="text-neutral-600 mx-1">·</span> 33 + <span className="text-neutral-400 mx-1">·</span> 34 34 {profile.pronouns} 35 35 </> 36 36 )} ··· 42 42 )} 43 43 {profile?.bbsName && ( 44 44 <div className="mt-6"> 45 - <p className="text-xs text-neutral-500 uppercase tracking-wide mb-2"> 45 + <p className="text-xs text-neutral-400 uppercase tracking-wide mb-2"> 46 46 BBS 47 47 </p> 48 48 <Link ··· 52 52 <div> 53 53 <div className="text-neutral-200">{profile.bbsName}</div> 54 54 {profile.bbsDescription && ( 55 - <div className="text-xs text-neutral-500 mt-1"> 55 + <div className="text-xs text-neutral-400 mt-1"> 56 56 {profile.bbsDescription} 57 57 </div> 58 58 )} 59 59 </div> 60 - <span className="text-neutral-600 group-hover:text-neutral-300 text-lg ml-4"> 60 + <span className="text-neutral-400 group-hover:text-neutral-300 text-lg ml-4"> 61 61 62 62 </span> 63 63 </Link> 64 64 </div> 65 65 )} 66 66 {!profile?.name && !profile?.bio && !profile?.bbsName && !isOwner && ( 67 - <p className="text-neutral-500 mt-4">No profile yet.</p> 67 + <p className="text-neutral-400 mt-4">No profile yet.</p> 68 68 )} 69 69 </> 70 70 );
+9 -9
web/src/pages/BBS.tsx
··· 35 35 36 36 if (user && bbs.site.bannedDids.has(user.did)) 37 37 return ( 38 - <p className="text-neutral-500">You have been banned from this BBS.</p> 38 + <p className="text-neutral-400">You have been banned from this BBS.</p> 39 39 ); 40 40 41 41 const isSysop = user && user.did === bbs.identity.did; ··· 80 80 <> 81 81 <div className="mb-8"> 82 82 <h1 className="text-lg text-neutral-200 mb-1">{bbs.site.name}</h1> 83 - <p className="text-neutral-500 mb-3">{bbs.site.description}</p> 83 + <p className="text-neutral-400 mb-3">{bbs.site.description}</p> 84 84 <ActionBar> 85 85 <PinButton bbsDid={bbs.identity.did} initialRkey={pinRkey} /> 86 86 <ActionLink to={`/profile/${encodeURIComponent(handle)}`}> ··· 92 92 </div> 93 93 94 94 {bbs.site.intro && ( 95 - <pre className="bg-neutral-900 border border-neutral-800 rounded p-4 mb-8 overflow-x-auto text-neutral-500 text-xs leading-snug"> 95 + <pre className="bg-neutral-900 border border-neutral-800 rounded p-4 mb-8 overflow-x-auto text-neutral-400 text-xs leading-snug"> 96 96 {bbs.site.intro} 97 97 </pre> 98 98 )} 99 99 100 100 <section className="mb-8"> 101 - <h2 className="text-xs text-neutral-500 uppercase tracking-wide mb-3"> 101 + <h2 className="text-xs text-neutral-400 uppercase tracking-wide mb-3"> 102 102 Boards 103 103 </h2> 104 104 <div className="space-y-1"> ··· 114 114 </section> 115 115 116 116 <section> 117 - <h2 className="text-xs text-neutral-500 uppercase tracking-wide mb-3"> 117 + <h2 className="text-xs text-neutral-400 uppercase tracking-wide mb-3"> 118 118 News 119 119 </h2> 120 120 ··· 153 153 <div className="flex items-baseline justify-between mb-2"> 154 154 <div className="flex items-baseline gap-2"> 155 155 <span className="text-neutral-200">{item.title}</span> 156 - <span className="text-neutral-600">·</span> 156 + <span className="text-neutral-400">·</span> 157 157 <Localtime iso={item.createdAt} /> 158 158 </div> 159 159 {isSysop && ( ··· 164 164 e.preventDefault(); 165 165 removeNews(item.tid); 166 166 }} 167 - className="text-xs text-neutral-500 hover:text-red-400" 167 + className="text-xs text-neutral-400 hover:text-red-400" 168 168 > 169 169 delete 170 170 </button> ··· 180 180 {!showAllNews && allNews.length > 3 && ( 181 181 <button 182 182 onClick={() => setShowAllNews(true)} 183 - className="text-neutral-500 hover:text-neutral-300 text-xs mt-2" 183 + className="text-neutral-400 hover:text-neutral-300 text-xs mt-2" 184 184 > 185 185 show more 186 186 </button> 187 187 )} 188 188 </> 189 189 ) : ( 190 - <p className="text-neutral-500">No news yet.</p> 190 + <p className="text-neutral-400">No news yet.</p> 191 191 )} 192 192 </section> 193 193 </>
+3 -3
web/src/pages/Board.tsx
··· 106 106 <> 107 107 <div className="mb-6"> 108 108 <h1 className="text-lg text-neutral-200 mb-1">{board.name}</h1> 109 - <p className="text-neutral-500">{board.description}</p> 109 + <p className="text-neutral-400">{board.description}</p> 110 110 </div> 111 111 112 112 {user && ( ··· 142 142 /> 143 143 )) 144 144 ) : ( 145 - <p className="text-neutral-500">No threads yet.</p> 145 + <p className="text-neutral-400">No threads yet.</p> 146 146 )} 147 147 </div> 148 148 ··· 151 151 <button 152 152 onClick={loadMore} 153 153 disabled={loadingMore} 154 - className="text-neutral-500 hover:text-neutral-300" 154 + className="text-neutral-400 hover:text-neutral-300" 155 155 > 156 156 {loadingMore ? "loading…" : "next page →"} 157 157 </button>
+6 -6
web/src/pages/Dashboard.tsx
··· 25 25 const TAB_STYLE_ACTIVE = 26 26 "py-2 border-b-2 text-neutral-200 border-neutral-200 whitespace-nowrap"; 27 27 const TAB_STYLE_INACTIVE = 28 - "py-2 border-b-2 text-neutral-500 hover:text-neutral-300 border-transparent whitespace-nowrap"; 28 + "py-2 border-b-2 text-neutral-400 hover:text-neutral-300 border-transparent whitespace-nowrap"; 29 29 30 30 export default function Dashboard({ data }: { data: DashboardData }) { 31 31 const { user, hasBBS, pins, threads, activity } = data; ··· 83 83 { key: "bbs", label: "My BBS" }, 84 84 ]; 85 85 86 - const loading = <p className="text-neutral-500">Loading...</p>; 86 + const loading = <p className="text-neutral-400">Loading...</p>; 87 87 88 88 return ( 89 89 <> ··· 105 105 106 106 {tab === "inbox" && ( 107 107 <> 108 - <p className="text-neutral-500 text-xs mb-4"> 108 + <p className="text-neutral-400 text-xs mb-4"> 109 109 Recent replies and quotes from other users. 110 110 </p> 111 111 <Suspense fallback={loading}> ··· 120 120 121 121 {tab === "threads" && ( 122 122 <> 123 - <p className="text-neutral-500 text-xs mb-4"> 123 + <p className="text-neutral-400 text-xs mb-4"> 124 124 Threads you've posted across all BBSes. 125 125 </p> 126 126 <Suspense fallback={loading}> ··· 133 133 134 134 {tab === "pinned" && ( 135 135 <> 136 - <p className="text-neutral-500 text-xs mb-4"> 136 + <p className="text-neutral-400 text-xs mb-4"> 137 137 BBSes you've pinned for quick access. 138 138 </p> 139 139 <Suspense fallback={loading}> ··· 146 146 147 147 {tab === "bbs" && ( 148 148 <> 149 - <p className="text-neutral-500 text-xs mb-4"> 149 + <p className="text-neutral-400 text-xs mb-4"> 150 150 Manage your BBS. 151 151 </p> 152 152 <BBSPanel
+9 -9
web/src/pages/LoggedOutHome.tsx
··· 11 11 12 12 const activeTab = "py-2 border-b-2 text-neutral-200 border-neutral-200"; 13 13 const inactiveTab = 14 - "py-2 border-b-2 text-neutral-500 hover:text-neutral-300 border-transparent"; 14 + "py-2 border-b-2 text-neutral-400 hover:text-neutral-300 border-transparent"; 15 15 16 16 return ( 17 17 <div className="h-full flex flex-col justify-center overflow-hidden"> ··· 38 38 </a> 39 39 . 40 40 </h1> 41 - <p className="text-neutral-500 max-w-md mx-auto"> 41 + <p className="text-neutral-400 max-w-md mx-auto"> 42 42 Build a community from your existing account. Tightly curated, fully 43 43 portable, open by design. 44 44 </p> ··· 67 67 </div> 68 68 {tab === "pip" && ( 69 69 <pre className="bg-neutral-900 border border-neutral-800 rounded px-4 py-3 text-neutral-400 text-xs"> 70 - <span className="text-neutral-500 select-none">$ </span>pip install 70 + <span className="text-neutral-400 select-none">$ </span>pip install 71 71 atbbs 72 72 {"\n"} 73 - <span className="text-neutral-500 select-none">$ </span>atbbs 73 + <span className="text-neutral-400 select-none">$ </span>atbbs 74 74 </pre> 75 75 )} 76 76 {tab === "uv" && ( 77 77 <pre className="bg-neutral-900 border border-neutral-800 rounded px-4 py-3 text-neutral-400 text-xs"> 78 - <span className="text-neutral-500 select-none">$ </span>uv tool 78 + <span className="text-neutral-400 select-none">$ </span>uv tool 79 79 install atbbs 80 80 {"\n"} 81 - <span className="text-neutral-500 select-none">$ </span>atbbs 81 + <span className="text-neutral-400 select-none">$ </span>atbbs 82 82 </pre> 83 83 )} 84 84 {tab === "brew" && ( 85 85 <pre className="bg-neutral-900 border border-neutral-800 rounded px-4 py-3 text-neutral-400 text-xs"> 86 - <span className="text-neutral-500 select-none">$ </span>brew install 86 + <span className="text-neutral-400 select-none">$ </span>brew install 87 87 alyraffauf/tap/atbbs 88 88 {"\n"} 89 - <span className="text-neutral-500 select-none">$ </span>atbbs 89 + <span className="text-neutral-400 select-none">$ </span>atbbs 90 90 </pre> 91 91 )} 92 92 {tab === "telnet" && ( 93 93 <pre className="bg-neutral-900 border border-neutral-800 rounded px-4 py-3 text-neutral-400 text-xs"> 94 - <span className="text-neutral-500 select-none">$ </span>telnet 94 + <span className="text-neutral-400 select-none">$ </span>telnet 95 95 tel.atbbs.xyz 96 96 </pre> 97 97 )}
+5 -5
web/src/pages/Login.tsx
··· 57 57 /> 58 58 </picture> 59 59 <h1 className="text-lg text-neutral-200 mb-2">Log in to atbbs</h1> 60 - <p className="text-neutral-500">Use any atproto account.</p> 60 + <p className="text-neutral-400">Use any atproto account.</p> 61 61 </div> 62 62 63 63 {error && <p className="text-red-400 mb-4 text-center">{error}</p>} ··· 96 96 <div className="text-sm text-neutral-200 truncate"> 97 97 {match.displayName} 98 98 </div> 99 - <div className="text-xs text-neutral-500 truncate"> 99 + <div className="text-xs text-neutral-400 truncate"> 100 100 {match.handle} 101 101 </div> 102 102 </div> ··· 107 107 )} 108 108 </div> 109 109 110 - <div className="text-neutral-500 text-xs space-y-2"> 110 + <div className="text-neutral-400 text-xs space-y-2"> 111 111 <p>Once signed in, you can:</p> 112 - <ul className="list-disc list-inside space-y-1 text-neutral-600"> 112 + <ul className="list-disc list-inside space-y-1 text-neutral-400"> 113 113 <li>Post threads and replies</li> 114 114 <li>Pin boards you like</li> 115 115 <li>Set up a profile</li> ··· 117 117 </ul> 118 118 </div> 119 119 120 - <p className="text-neutral-600 text-xs mt-6"> 120 + <p className="text-neutral-400 text-xs mt-6"> 121 121 You'll be redirected to your hosting provider to continue. 122 122 </p> 123 123 </div>
+1 -1
web/src/pages/News.tsx
··· 27 27 ); 28 28 29 29 if (!item) { 30 - return <p className="text-neutral-500">News post not found.</p>; 30 + return <p className="text-neutral-400">News post not found.</p>; 31 31 } 32 32 33 33 const isSysop = !!(user && user.did === bbs.identity.did);
+1 -1
web/src/pages/NotFound.tsx
··· 1 1 export default function NotFound() { 2 - return <p className="text-neutral-500">Not found.</p>; 2 + return <p className="text-neutral-400">Not found.</p>; 3 3 }
+1 -1
web/src/pages/OAuthCallback.tsx
··· 20 20 }, [navigate]); 21 21 22 22 if (error) return <p className="text-red-400">{error}</p>; 23 - return <p className="text-neutral-500">Signing in…</p>; 23 + return <p className="text-neutral-400">Signing in…</p>; 24 24 }
+2 -2
web/src/pages/Profile.tsx
··· 45 45 onEdit={() => setEditing(true)} 46 46 /> 47 47 <div className="mt-8"> 48 - <p className="text-xs text-neutral-500 uppercase tracking-wide mb-3"> 48 + <p className="text-xs text-neutral-400 uppercase tracking-wide mb-3"> 49 49 Recent Threads 50 50 </p> 51 51 <Suspense 52 - fallback={<p className="text-neutral-500">Loading...</p>} 52 + fallback={<p className="text-neutral-400">Loading...</p>} 53 53 > 54 54 <Await resolve={threads}> 55 55 {(resolved: MyThread[]) => (
+1 -1
web/src/pages/SysopCreate.tsx
··· 63 63 return ( 64 64 <> 65 65 <h1 className="text-lg text-neutral-200 mb-1">Create a BBS</h1> 66 - <p className="text-neutral-500 mb-6"> 66 + <p className="text-neutral-400 mb-6"> 67 67 Set up your BBS. Your handle becomes the address. 68 68 </p> 69 69 {error && <p className="text-red-500 mb-4">{error}</p>}
+1 -1
web/src/pages/SysopEdit.tsx
··· 66 66 return ( 67 67 <> 68 68 <h1 className="text-lg text-neutral-200 mb-1">Edit BBS</h1> 69 - <p className="text-neutral-500 mb-6">Update your BBS.</p> 69 + <p className="text-neutral-400 mb-6">Update your BBS.</p> 70 70 {error && <p className="text-red-500 mb-4">{error}</p>} 71 71 <form onSubmit={onSubmit} className="space-y-6"> 72 72 <div>
+3 -3
web/src/pages/SysopModerate.tsx
··· 78 78 return ( 79 79 <> 80 80 <h1 className="text-lg text-neutral-200 mb-1">Moderate BBS</h1> 81 - <p className="text-neutral-500 mb-6"> 81 + <p className="text-neutral-400 mb-6"> 82 82 Manage banned users and hidden posts. 83 83 </p> 84 84 ··· 103 103 {banRkeys[did] && ( 104 104 <button 105 105 onClick={() => unban(banRkeys[did])} 106 - className="text-xs text-neutral-500 hover:text-red-400 shrink-0" 106 + className="text-xs text-neutral-400 hover:text-red-400 shrink-0" 107 107 > 108 108 unban 109 109 </button> ··· 142 142 {hideRkeys[p.uri] && ( 143 143 <button 144 144 onClick={() => unhide(hideRkeys[p.uri])} 145 - className="text-xs text-neutral-500 hover:text-red-400 shrink-0" 145 + className="text-xs text-neutral-400 hover:text-red-400 shrink-0" 146 146 > 147 147 unhide 148 148 </button>
+2 -2
web/src/pages/Thread.tsx
··· 162 162 163 163 <div className="space-y-2 mt-4"> 164 164 {loadingPage ? ( 165 - <p className="text-neutral-500">Loading replies...</p> 165 + <p className="text-neutral-400">Loading replies...</p> 166 166 ) : replies.length === 0 && !user ? ( 167 - <p className="text-neutral-500">No replies yet.</p> 167 + <p className="text-neutral-400">No replies yet.</p> 168 168 ) : ( 169 169 replies.map((reply) => ( 170 170 <ReplyCard