🌿 Collaborative wiki on ATProto
0
fork

Configure Feed

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

Add some accessibility

+16 -10
+3 -2
src/views/layout.ts
··· 201 201 </div> 202 202 </div> 203 203 </nav> 204 - <dialog id="search-modal" style="margin-left:auto;margin-right:auto;margin-top:15vh;" class="p-0 rounded-xl shadow-2xl backdrop:bg-black/50 w-full max-w-lg" onclick="if(event.target===this)this.close()"> 204 + <dialog id="search-modal" style="margin-left:auto;margin-right:auto;margin-top:15vh;" class="p-0 rounded-xl shadow-2xl backdrop:bg-black/50 w-full max-w-lg" onclick="if(event.target===this)this.close()" aria-label="${msg.search.searchNotes}"> 205 205 <div class="p-3"> 206 206 <div class="relative"> 207 207 <input id="search-input" type="text" placeholder="${msg.search.searchNotes}" autocomplete="off" 208 + aria-label="${msg.search.searchNotes}" 208 209 class="w-full px-3 py-2 text-sm border ${THEME.borderInput} rounded-lg outline-none ${THEME.accentFocusBorder} focus:ring-2 ${THEME.accentSubtleRing}" 209 210 hx-get="/search${options?.wikiSlug ? `?wiki=${options.wikiSlug}` : ""}" 210 211 hx-trigger="input changed delay:150ms" ··· 212 213 name="q"> 213 214 </div> 214 215 </div> 215 - <div id="search-results" class="max-h-80 overflow-y-auto border-t ${THEME.borderSubtle} empty:hidden"></div> 216 + <div id="search-results" class="max-h-80 overflow-y-auto border-t ${THEME.borderSubtle} empty:hidden" aria-live="polite"></div> 216 217 </dialog> 217 218 <script> 218 219 document.addEventListener('click', (e) => {
+7 -3
src/views/settings.ts
··· 70 70 const roleCell = isOwner 71 71 ? `<span class="text-sm">${roleLabel("owner")}</span>` 72 72 : `<form method="POST" action="/wiki/${wikiSlug}/-/members/${encodeURIComponent(m.did)}/change-role" class="inline-flex items-center gap-1"> 73 - <select name="role" data-original="${m.role}" onchange="this.nextElementSibling.classList.toggle('hidden', this.value === this.dataset.original)" class="text-xs border ${THEME.borderDefault} rounded px-1 py-0.5"> 73 + <select name="role" aria-label="${msg.access.role}" data-original="${m.role}" onchange="this.nextElementSibling.classList.toggle('hidden', this.value === this.dataset.original)" class="text-xs border ${THEME.borderDefault} rounded px-1 py-0.5"> 74 74 ${roleOptions(m.role)} 75 75 </select> 76 76 <button type="submit" class="hidden p-1 ${THEME.accentBg} text-white rounded ${THEME.accentDarkHoverBg} cursor-pointer" title="${msg.access.saveRole}">${ICONS.check}</button> ··· 96 96 <td class="py-2 pr-4 text-sm ${THEME.textMuted}">${escapeHtml(r.created_at)}</td> 97 97 <td class="py-2 text-sm"> 98 98 <form method="POST" action="/wiki/${wikiSlug}/-/members/${encodeURIComponent(r.did)}/approve" class="inline-flex items-center gap-2"> 99 - <select name="role" class="text-xs border ${THEME.borderDefault} rounded px-1 py-0.5"> 99 + <select name="role" aria-label="${msg.access.role}" class="text-xs border ${THEME.borderDefault} rounded px-1 py-0.5"> 100 100 <option value="contributor">${roleLabel("contributor")}</option> 101 101 <option value="admin">${roleLabel("admin")}</option> 102 102 <option value="viewer">${roleLabel("viewer")}</option> ··· 111 111 return ` 112 112 <section class="mb-10"> 113 113 <h2 class="text-lg font-semibold mb-4">${msg.access.members}</h2> 114 + <div class="overflow-x-auto"> 114 115 <table class="w-full mb-6"> 115 116 <thead> 116 117 <tr class="border-b ${THEME.borderDefault} text-left text-xs font-semibold ${THEME.textMuted} uppercase tracking-wide"> ··· 124 125 ${memberRows || `<tr><td colspan="4" class="py-4 text-sm ${THEME.textMuted}">${msg.access.noMembers}</td></tr>`} 125 126 </tbody> 126 127 </table> 128 + </div> 127 129 128 130 ${ 129 131 requests.length > 0 130 132 ? `<h3 class="text-base font-semibold mb-3">${msg.access.pendingRequests}</h3> 133 + <div class="overflow-x-auto"> 131 134 <table class="w-full mb-6"> 132 135 <thead> 133 136 <tr class="border-b ${THEME.borderDefault} text-left text-xs font-semibold ${THEME.textMuted} uppercase tracking-wide"> ··· 137 140 </tr> 138 141 </thead> 139 142 <tbody>${requestRows}</tbody> 140 - </table>` 143 + </table> 144 + </div>` 141 145 : "" 142 146 } 143 147
+1 -1
src/views/wiki-card.ts
··· 18 18 ? `<span class="truncate">${escapeHtml(wiki.owner_handle)}</span>` 19 19 : ""; 20 20 const ownerAvatar = wiki.owner_avatar 21 - ? `<img src="${escapeHtml(wiki.owner_avatar)}" alt="" class="w-4 h-4 rounded-full shrink-0">` 21 + ? `<img src="${escapeHtml(wiki.owner_avatar)}" alt="${wiki.owner_handle ? `@${escapeHtml(wiki.owner_handle)}` : ""}" class="w-4 h-4 rounded-full shrink-0">` 22 22 : ""; 23 23 const ownerHtml = 24 24 ownerHandle || ownerAvatar
+5 -4
src/views/wiki-list.ts
··· 29 29 </select>`; 30 30 31 31 const searchInput = `<input id="wiki-search" type="text" placeholder="${msg.search.searchWikis}" autocomplete="off" 32 + aria-label="${msg.search.searchWikis}" 32 33 class="flex-1 min-w-0 px-3 py-1.5 text-sm border ${THEME.borderDefault} rounded-lg outline-none ${THEME.accentInputFocusBorder} focus:ring-1 ${THEME.accentSubtleRing} ${THEME.bgSurface}" 33 34 hx-get="/search" hx-trigger="input changed delay:150ms, filterChange" hx-target="#${gridTargetId}" name="q" 34 35 hx-include="#lang-select, #sort-select, #limit-input">`; ··· 115 116 const btnDisabled = `${btnBase} ${THEME.borderSubtle} ${THEME.textMuted} opacity-50 cursor-not-allowed`; 116 117 117 118 const prevBtn = hasPrev 118 - ? `<button type="button" class="${btnEnabled}" 119 + ? `<button type="button" class="${btnEnabled}" aria-label="${msg.pagination.previous}" 119 120 hx-get="/search?sort=${sort}&page=${page - 1}&limit=${limit}" 120 121 hx-target="#wiki-grid" hx-include="#lang-select, #wiki-search">&larr; ${msg.pagination.previous}</button>` 121 - : `<button type="button" class="${btnDisabled}" disabled>&larr; ${msg.pagination.previous}</button>`; 122 + : `<button type="button" class="${btnDisabled}" disabled aria-label="${msg.pagination.previous}">&larr; ${msg.pagination.previous}</button>`; 122 123 123 124 const nextBtn = hasNext 124 - ? `<button type="button" class="${btnEnabled}" 125 + ? `<button type="button" class="${btnEnabled}" aria-label="${msg.pagination.next}" 125 126 hx-get="/search?sort=${sort}&page=${page + 1}&limit=${limit}" 126 127 hx-target="#wiki-grid" hx-include="#lang-select, #wiki-search">&rarr; ${msg.pagination.next}</button>` 127 - : `<button type="button" class="${btnDisabled}" disabled>&rarr; ${msg.pagination.next}</button>`; 128 + : `<button type="button" class="${btnDisabled}" disabled aria-label="${msg.pagination.next}">&rarr; ${msg.pagination.next}</button>`; 128 129 129 130 return `<div class="mt-6 flex items-center justify-center gap-4"> 130 131 ${prevBtn}