A project tracker for decentralized social media platforms, clients, and tools

feat: enhance project cards and UI improvements

- Add warning indicator for semi-platform projects with hover tooltip
- Remove automatic network and type badges, show only project tags
- Display full descriptions and all tags without truncation
- Update search placeholder to indicate tag search capability
- Add bottom padding for better scroll experience
- Update Bluesky project tags to include ios/android platforms

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>

+22 -21
+1 -1
public/data/projects.json
··· 8 8 "logoUrl": "/logos/bluesky.svg", 9 9 "description": "A new kind of social network that puts users in control of their experience and gives creators independence from platforms.", 10 10 "type": "platform", 11 - "tags": ["microblog", "flagship", "open-source", "mobile", "web"], 11 + "tags": ["microblog", "flagship", "open-source", "mobile", "web", "ios", "android"], 12 12 "languages": ["TypeScript", "React Native"], 13 13 "links": [ 14 14 { "kind": "homepage", "url": "https://bsky.app" },
+2 -2
src/App.tsx
··· 75 75 selectedNetwork={filters.network} 76 76 onNetworkChange={handleNetworkChange} 77 77 /> 78 - <div className="container mx-auto px-4 pt-20"> 78 + <div className="container mx-auto px-4 pt-20 pb-20"> 79 79 <FilterToolbar 80 80 query={filters.query} 81 81 selectedTags={filters.tags} ··· 86 86 onTagsChange={handleTagsChange} 87 87 onSortChange={handleSortChange} 88 88 /> 89 - <ProjectGrid 89 + <ProjectGrid 90 90 projects={filteredProjects} 91 91 loading={loading} 92 92 />
+1 -1
src/components/FilterToolbar.tsx
··· 79 79 type="text" 80 80 value={query} 81 81 onChange={(e) => onSearchChange(e.target.value)} 82 - placeholder="Search projects" 82 + placeholder="Search projects or tags" 83 83 className="w-full pl-10 pr-4 py-2 bg-gray-800 border border-gray-700 rounded-lg text-gray-100 placeholder-gray-500 focus:outline-none focus:border-blue-500" 84 84 /> 85 85 <svg className="absolute left-3 top-2.5 w-5 h-5 text-gray-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
+18 -17
src/components/ProjectCard.tsx
··· 84 84 <div className="flex items-start justify-between"> 85 85 <div className="flex items-center space-x-3"> 86 86 {project.logoUrl ? ( 87 - <img 88 - src={project.logoUrl} 87 + <img 88 + src={project.logoUrl} 89 89 alt={`${project.name} logo`} 90 90 className="w-10 h-10 rounded-lg object-cover" 91 91 /> ··· 106 106 )} 107 107 </div> 108 108 </div> 109 + {project.type === 'semi-platform' && ( 110 + <div className="relative group"> 111 + <svg 112 + className="w-5 h-5 text-yellow-500" 113 + fill="currentColor" 114 + viewBox="0 0 20 20" 115 + > 116 + <path fillRule="evenodd" d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z" clipRule="evenodd" /> 117 + </svg> 118 + <div className="absolute right-0 top-6 w-48 p-2 bg-gray-900 text-gray-200 text-xs rounded-lg opacity-0 group-hover:opacity-100 transition-opacity pointer-events-none z-10 border border-gray-700"> 119 + Has not implemented platform-based AT Protocol lexicon 120 + </div> 121 + </div> 122 + )} 109 123 </div> 110 124 111 125 {project.bannerUrl && ( ··· 118 132 </div> 119 133 )} 120 134 121 - <p className="text-gray-300 text-sm line-clamp-2"> 135 + <p className="text-gray-300 text-sm"> 122 136 {project.description} 123 137 </p> 124 138 125 139 <div className="flex flex-wrap gap-2"> 126 - <span className={`px-2 py-1 rounded-full text-xs font-medium ${networkColors[project.network]}`}> 127 - {project.network} 128 - </span> 129 - {project.type && ( 130 - <span className="px-2 py-1 bg-gray-700 text-gray-300 rounded-full text-xs"> 131 - {project.type} 132 - </span> 133 - )} 134 - {project.tags.slice(0, 3).map(tag => ( 140 + {project.tags.map(tag => ( 135 141 <span key={tag} className="px-2 py-1 bg-gray-700 text-gray-300 rounded-full text-xs"> 136 142 {tag} 137 143 </span> 138 144 ))} 139 - {project.tags.length > 3 && ( 140 - <span className="px-2 py-1 text-gray-500 text-xs"> 141 - +{project.tags.length - 3} 142 - </span> 143 - )} 144 145 </div> 145 146 146 147 <div className="flex-grow"></div>