fix: improve skeleton loading accessibility and consistency
- Add role="status" and aria-label to all skeleton containers - Refactor ChannelSidebar from conditional siblings to ternary for consistent mutual exclusivity with MemberSidebar pattern
This commit is contained in:
@@ -260,7 +260,7 @@ export function MessageList({ channelId, jumpToMessageId, onJumpComplete }: Mess
|
|||||||
|
|
||||||
if (isLoading && messages.length === 0) {
|
if (isLoading && messages.length === 0) {
|
||||||
return (
|
return (
|
||||||
<div className="flex-1 flex flex-col justify-end px-4 pb-6">
|
<div className="flex-1 flex flex-col justify-end px-4 pb-6" role="status" aria-label="Loading messages">
|
||||||
{Array.from({ length: 7 }, (_, i) => (
|
{Array.from({ length: 7 }, (_, i) => (
|
||||||
<div key={i} className="flex gap-3 mb-5" style={{ animationDelay: `${i * 0.15}s` }}>
|
<div key={i} className="flex gap-3 mb-5" style={{ animationDelay: `${i * 0.15}s` }}>
|
||||||
<div className="skeleton skeleton-circle w-10 h-10 flex-shrink-0" style={{ animationDelay: `${i * 0.15}s` }} />
|
<div className="skeleton skeleton-circle w-10 h-10 flex-shrink-0" style={{ animationDelay: `${i * 0.15}s` }} />
|
||||||
@@ -285,7 +285,7 @@ export function MessageList({ channelId, jumpToMessageId, onJumpComplete }: Mess
|
|||||||
onScroll={handleScroll}
|
onScroll={handleScroll}
|
||||||
>
|
>
|
||||||
{isLoadingMore && (
|
{isLoadingMore && (
|
||||||
<div className="px-4 pt-4">
|
<div className="px-4 pt-4" role="status" aria-label="Loading older messages">
|
||||||
{Array.from({ length: 3 }, (_, i) => (
|
{Array.from({ length: 3 }, (_, i) => (
|
||||||
<div key={i} className="flex gap-3 mb-5" style={{ animationDelay: `${i * 0.15}s` }}>
|
<div key={i} className="flex gap-3 mb-5" style={{ animationDelay: `${i * 0.15}s` }}>
|
||||||
<div className="skeleton skeleton-circle w-10 h-10 flex-shrink-0" style={{ animationDelay: `${i * 0.15}s` }} />
|
<div className="skeleton skeleton-circle w-10 h-10 flex-shrink-0" style={{ animationDelay: `${i * 0.15}s` }} />
|
||||||
|
|||||||
@@ -264,7 +264,7 @@ export function AppLayout() {
|
|||||||
|
|
||||||
if (isLoading || !user) {
|
if (isLoading || !user) {
|
||||||
return (
|
return (
|
||||||
<div className="h-full flex bg-surface-base">
|
<div className="h-full flex bg-surface-base" role="status" aria-label="Loading Backspace">
|
||||||
{/* Space strip */}
|
{/* Space strip */}
|
||||||
<div className="w-[72px] hidden md:flex flex-col items-center gap-3 pt-4 bg-surface-base flex-shrink-0">
|
<div className="w-[72px] hidden md:flex flex-col items-center gap-3 pt-4 bg-surface-base flex-shrink-0">
|
||||||
{Array.from({ length: 5 }, (_, i) => (
|
{Array.from({ length: 5 }, (_, i) => (
|
||||||
|
|||||||
@@ -626,9 +626,8 @@ export function ChannelSidebar() {
|
|||||||
|
|
||||||
{/* Channels — dynamic category layout */}
|
{/* Channels — dynamic category layout */}
|
||||||
<div ref={scrollContainerRef} className="flex-1 overflow-y-auto pt-3 px-2 space-y-[2px] no-scrollbar" style={{ paddingBottom: floatingPanelHeight + 24 }} onDrop={containerHandlers.onDrop} onDragOver={containerHandlers.onDragOver} onContextMenu={handleSidebarContextMenu}>
|
<div ref={scrollContainerRef} className="flex-1 overflow-y-auto pt-3 px-2 space-y-[2px] no-scrollbar" style={{ paddingBottom: floatingPanelHeight + 24 }} onDrop={containerHandlers.onDrop} onDragOver={containerHandlers.onDragOver} onContextMenu={handleSidebarContextMenu}>
|
||||||
{/* Skeleton loading state */}
|
{isLoadingSpace ? (
|
||||||
{isLoadingSpace && (
|
<div className="px-2 pt-3" role="status" aria-label="Loading channels">
|
||||||
<div className="px-2 pt-3">
|
|
||||||
{/* Category group 1 */}
|
{/* Category group 1 */}
|
||||||
<div className="skeleton skeleton-bar h-2 w-[45%] ml-2 mb-3" />
|
<div className="skeleton skeleton-bar h-2 w-[45%] ml-2 mb-3" />
|
||||||
{Array.from({ length: 3 }, (_, i) => (
|
{Array.from({ length: 3 }, (_, i) => (
|
||||||
@@ -646,9 +645,8 @@ export function ChannelSidebar() {
|
|||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
)}
|
) : (<>
|
||||||
{/* Uncategorized channels */}
|
{/* Uncategorized channels */}
|
||||||
{!isLoadingSpace && (<>
|
|
||||||
{uncategorizedChannels.length > 0 && (
|
{uncategorizedChannels.length > 0 && (
|
||||||
<div className="mb-[19px]">
|
<div className="mb-[19px]">
|
||||||
{canManageChannels && sortedCategories.length === 0 && (
|
{canManageChannels && sortedCategories.length === 0 && (
|
||||||
|
|||||||
@@ -152,7 +152,7 @@ export function MemberSidebar() {
|
|||||||
return (
|
return (
|
||||||
<div className="w-60 bg-surface-members flex-shrink-0 overflow-y-auto select-none no-scrollbar hidden md:block border-l border-border-hard">
|
<div className="w-60 bg-surface-members flex-shrink-0 overflow-y-auto select-none no-scrollbar hidden md:block border-l border-border-hard">
|
||||||
{isLoadingSpace ? (
|
{isLoadingSpace ? (
|
||||||
<div className="px-3 pt-4">
|
<div className="px-3 pt-4" role="status" aria-label="Loading members">
|
||||||
{/* Role group 1 */}
|
{/* Role group 1 */}
|
||||||
<div className="skeleton skeleton-bar h-2 w-[40%] mb-3" style={{ animationDelay: '0s' }} />
|
<div className="skeleton skeleton-bar h-2 w-[40%] mb-3" style={{ animationDelay: '0s' }} />
|
||||||
{Array.from({ length: 2 }, (_, i) => (
|
{Array.from({ length: 2 }, (_, i) => (
|
||||||
|
|||||||
Reference in New Issue
Block a user