A simple SEO inspecter Tool, to get social media card previews
0
fork

Configure Feed

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

Add social media and search previews

+157 -20
+132
src/components/SEOTester.tsx
··· 681 681 </CardContent> 682 682 </Card> 683 683 )} 684 + 685 + {/* Preview Section */} 686 + <Card className="border border-border bg-card"> 687 + <CardHeader className="pb-3"> 688 + <div className="flex items-center gap-2"> 689 + <Eye className="h-4 w-4 text-primary" /> 690 + <span className="font-mono text-sm">Social & Search Previews</span> 691 + </div> 692 + </CardHeader> 693 + <CardContent className="space-y-6"> 694 + {/* Google Search Preview */} 695 + <div className="space-y-2"> 696 + <div className="text-xs font-mono text-muted-foreground mb-3 flex items-center gap-2"> 697 + <Search className="h-3 w-3" /> 698 + GOOGLE SEARCH RESULT 699 + </div> 700 + <div className="bg-white p-4 rounded border border-border/50"> 701 + <div className="space-y-1"> 702 + <div className="flex items-center gap-2 mb-1"> 703 + <Globe className="h-4 w-4 text-gray-500" /> 704 + <span className="text-xs text-gray-600">{new URL(seoData.url).hostname}</span> 705 + </div> 706 + <div className="text-blue-600 text-xl hover:underline cursor-pointer line-clamp-1"> 707 + {seoData.title || 'No title found'} 708 + </div> 709 + <div className="text-sm text-gray-600 line-clamp-2"> 710 + {seoData.description || 'No meta description available'} 711 + </div> 712 + </div> 713 + </div> 714 + </div> 715 + 716 + {/* Facebook/Open Graph Preview */} 717 + <div className="space-y-2"> 718 + <div className="text-xs font-mono text-muted-foreground mb-3 flex items-center gap-2"> 719 + <Share2 className="h-3 w-3" /> 720 + FACEBOOK / OPEN GRAPH 721 + </div> 722 + <div className="bg-white rounded border border-border/50 overflow-hidden"> 723 + {seoData.ogImage && ( 724 + <div className="w-full h-48 bg-gray-200 relative overflow-hidden"> 725 + <img 726 + src={seoData.ogImage} 727 + alt="OG Preview" 728 + className="w-full h-full object-cover" 729 + onError={(e) => { 730 + e.currentTarget.style.display = 'none'; 731 + e.currentTarget.parentElement!.innerHTML = '<div class="flex items-center justify-center h-full text-gray-400 text-sm">Image unavailable</div>'; 732 + }} 733 + /> 734 + </div> 735 + )} 736 + <div className="p-3 bg-gray-50"> 737 + <div className="text-xs text-gray-500 uppercase mb-1"> 738 + {new URL(seoData.url).hostname} 739 + </div> 740 + <div className="text-gray-900 font-semibold text-base line-clamp-1"> 741 + {seoData.ogTitle || seoData.title || 'No title'} 742 + </div> 743 + <div className="text-sm text-gray-600 line-clamp-2 mt-1"> 744 + {seoData.ogDescription || seoData.description || 'No description'} 745 + </div> 746 + </div> 747 + </div> 748 + </div> 749 + 750 + {/* Twitter Card Preview */} 751 + <div className="space-y-2"> 752 + <div className="text-xs font-mono text-muted-foreground mb-3 flex items-center gap-2"> 753 + <span className="text-[#1DA1F2]">𝕏</span> 754 + TWITTER / X CARD 755 + </div> 756 + <div className="bg-white rounded-2xl border border-border/50 overflow-hidden"> 757 + {(seoData.twitterImage || seoData.ogImage) && ( 758 + <div className="w-full h-56 bg-gray-200 relative overflow-hidden"> 759 + <img 760 + src={seoData.twitterImage || seoData.ogImage} 761 + alt="Twitter Preview" 762 + className="w-full h-full object-cover" 763 + onError={(e) => { 764 + e.currentTarget.style.display = 'none'; 765 + e.currentTarget.parentElement!.innerHTML = '<div class="flex items-center justify-center h-full text-gray-400 text-sm">Image unavailable</div>'; 766 + }} 767 + /> 768 + </div> 769 + )} 770 + <div className="p-3 border-t border-gray-200"> 771 + <div className="text-sm text-gray-600 line-clamp-1"> 772 + {new URL(seoData.url).hostname} 773 + </div> 774 + <div className="text-gray-900 font-semibold text-base line-clamp-1 mt-1"> 775 + {seoData.twitterTitle || seoData.ogTitle || seoData.title || 'No title'} 776 + </div> 777 + <div className="text-sm text-gray-600 line-clamp-2 mt-1"> 778 + {seoData.twitterDescription || seoData.ogDescription || seoData.description || 'No description'} 779 + </div> 780 + </div> 781 + </div> 782 + </div> 783 + 784 + {/* LinkedIn Preview */} 785 + <div className="space-y-2"> 786 + <div className="text-xs font-mono text-muted-foreground mb-3 flex items-center gap-2"> 787 + <span className="text-[#0A66C2]">in</span> 788 + LINKEDIN 789 + </div> 790 + <div className="bg-white rounded border border-border/50 overflow-hidden"> 791 + {seoData.ogImage && ( 792 + <div className="w-full h-52 bg-gray-200 relative overflow-hidden"> 793 + <img 794 + src={seoData.ogImage} 795 + alt="LinkedIn Preview" 796 + className="w-full h-full object-cover" 797 + onError={(e) => { 798 + e.currentTarget.style.display = 'none'; 799 + e.currentTarget.parentElement!.innerHTML = '<div class="flex items-center justify-center h-full text-gray-400 text-sm">Image unavailable</div>'; 800 + }} 801 + /> 802 + </div> 803 + )} 804 + <div className="p-3 bg-white"> 805 + <div className="text-gray-900 font-semibold text-sm line-clamp-2"> 806 + {seoData.ogTitle || seoData.title || 'No title'} 807 + </div> 808 + <div className="text-xs text-gray-500 mt-1"> 809 + {new URL(seoData.url).hostname} 810 + </div> 811 + </div> 812 + </div> 813 + </div> 814 + </CardContent> 815 + </Card> 684 816 </div> 685 817 )} 686 818 </div>
+25 -20
src/index.css
··· 80 80 } 81 81 82 82 .dark { 83 - --background: 215 28% 17%; 84 - --foreground: 210 20% 98%; 83 + --background: 0 0% 7%; 84 + --foreground: 0 0% 88%; 85 85 86 - --card: 215 28% 17%; 87 - --card-foreground: 210 20% 98%; 86 + --card: 0 0% 12%; 87 + --card-foreground: 0 0% 88%; 88 88 89 - --popover: 215 28% 17%; 90 - --popover-foreground: 210 20% 98%; 89 + --popover: 0 0% 12%; 90 + --popover-foreground: 0 0% 88%; 91 91 92 - --primary: 213 93% 67%; 93 - --primary-foreground: 215 28% 17%; 94 - --primary-glow: 214 84% 56%; 92 + --primary: 5 100% 48%; 93 + --primary-foreground: 0 0% 98%; 94 + --primary-glow: 5 100% 60%; 95 95 96 - --secondary: 215 25% 27%; 97 - --secondary-foreground: 210 20% 98%; 96 + --secondary: 0 0% 15%; 97 + --secondary-foreground: 0 0% 88%; 98 98 99 - --muted: 215 25% 27%; 100 - --muted-foreground: 217 10% 64%; 99 + --muted: 0 0% 15%; 100 + --muted-foreground: 0 0% 53%; 101 101 102 - --accent: 215 25% 27%; 103 - --accent-foreground: 210 20% 98%; 102 + --accent: 5 100% 48%; 103 + --accent-foreground: 0 0% 98%; 104 104 105 105 --destructive: 0 63% 31%; 106 - --destructive-foreground: 210 20% 98%; 106 + --destructive-foreground: 0 0% 98%; 107 107 108 108 --success: 142 71% 45%; 109 - --success-foreground: 210 20% 98%; 109 + --success-foreground: 0 0% 98%; 110 110 111 - --border: 215 25% 27%; 112 - --input: 215 25% 27%; 113 - --ring: 213 93% 67%; 111 + --warning: 38 92% 50%; 112 + --warning-foreground: 0 0% 12%; 113 + 114 + --border: 0 0% 20%; 115 + --input: 0 0% 20%; 116 + --ring: 5 100% 48%; 114 117 115 118 /* Gradients */ 116 119 --gradient-primary: linear-gradient(135deg, hsl(var(--primary)), hsl(var(--primary-glow))); ··· 119 122 /* Shadows */ 120 123 --shadow-elegant: 0 10px 30px -10px hsl(var(--primary) / 0.3); 121 124 --shadow-card: 0 4px 12px -2px hsl(0 0% 0% / 0.25); 125 + 126 + --radius: 12px; 122 127 --sidebar-background: 240 5.9% 10%; 123 128 --sidebar-foreground: 240 4.8% 95.9%; 124 129 --sidebar-primary: 224.3 76.3% 48%;