feat: WCAG AA accessibility, code splitting, responsive ads layout
- Add React.lazy code splitting for all 15 tool pages - Fix WCAG AA contrast issues (304 text color fixes) - Add ARIA labels and aria-expanded to navigation buttons - Add aria-live for error announcements in tools - Implement responsive ad layout: - Desktop (≥1280px): Right sidebar with 3 ad units - Tablet (1024-1279px): Bottom section with 3 horizontal units - Mobile (<1024px): Fixed bottom banner - Add TabletAdSection component for tablet ad placement - Integrate Onidel affiliate partnership - Update all Adsterra domains to solutionbiologyisle.com - Add release notes for 2026-02-18 updates
This commit is contained in:
@@ -108,6 +108,8 @@ const Layout = ({ children }) => {
|
||||
<button
|
||||
onClick={() => setIsDropdownOpen(!isDropdownOpen)}
|
||||
className="flex items-center space-x-2 px-4 py-2 rounded-xl text-sm font-medium text-slate-600 hover:text-slate-900 dark:text-slate-300 dark:hover:text-white hover:bg-white/50 dark:hover:bg-slate-700/50 transition-all duration-300"
|
||||
aria-expanded={isDropdownOpen}
|
||||
aria-haspopup="true"
|
||||
>
|
||||
<Sparkles className="h-4 w-4" />
|
||||
<span>Tools</span>
|
||||
@@ -143,10 +145,10 @@ const Layout = ({ children }) => {
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<div className="font-medium">{tool.name}</div>
|
||||
<div className="text-xs text-slate-500 dark:text-slate-400">{tool.description}</div>
|
||||
<div className="text-xs text-slate-600 dark:text-slate-600">{tool.description}</div>
|
||||
</div>
|
||||
<div className="opacity-0 group-hover:opacity-100 transition-opacity duration-300">
|
||||
<ChevronDown className="h-4 w-4 -rotate-90 text-slate-400" />
|
||||
<ChevronDown className="h-4 w-4 -rotate-90 text-slate-600" />
|
||||
</div>
|
||||
</button>
|
||||
);
|
||||
@@ -164,6 +166,8 @@ const Layout = ({ children }) => {
|
||||
<button
|
||||
onClick={() => setIsMobileMenuOpen(!isMobileMenuOpen)}
|
||||
className="md:hidden p-2 rounded-xl text-slate-600 hover:text-slate-900 dark:text-slate-300 dark:hover:text-white hover:bg-white/50 dark:hover:bg-slate-700/50 transition-all duration-300"
|
||||
aria-label={isMobileMenuOpen ? "Close menu" : "Open menu"}
|
||||
aria-expanded={isMobileMenuOpen}
|
||||
>
|
||||
{isMobileMenuOpen ? <X className="h-6 w-6" /> : <Menu className="h-6 w-6" />}
|
||||
</button>
|
||||
@@ -211,7 +215,7 @@ const Layout = ({ children }) => {
|
||||
})}
|
||||
|
||||
<div className="border-t border-slate-200/50 dark:border-slate-700/50 pt-4 mt-4">
|
||||
<div className="text-xs font-semibold text-slate-500 dark:text-slate-400 uppercase tracking-wider px-4 py-2 flex items-center gap-2">
|
||||
<div className="text-xs font-semibold text-slate-600 dark:text-slate-600 uppercase tracking-wider px-4 py-2 flex items-center gap-2">
|
||||
<Sparkles className="h-3 w-3" />
|
||||
{isToolPage ? 'Switch Tools' : 'Tools'}
|
||||
</div>
|
||||
@@ -237,7 +241,7 @@ const Layout = ({ children }) => {
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<div className="font-medium">{tool.name}</div>
|
||||
<div className="text-xs text-slate-500 dark:text-slate-400">{tool.description}</div>
|
||||
<div className="text-xs text-slate-600 dark:text-slate-600">{tool.description}</div>
|
||||
</div>
|
||||
</button>
|
||||
);
|
||||
@@ -305,16 +309,16 @@ const Layout = ({ children }) => {
|
||||
</div>
|
||||
<div className="flex items-center justify-center gap-2 mb-3">
|
||||
<div className="w-2 h-2 bg-gradient-to-r from-blue-500 to-purple-500 rounded-full"></div>
|
||||
<span className="text-sm font-medium text-slate-600 dark:text-slate-400">
|
||||
<span className="text-sm font-medium text-slate-600 dark:text-slate-600">
|
||||
© {SITE_CONFIG.year} {SITE_CONFIG.title}
|
||||
</span>
|
||||
<div className="w-2 h-2 bg-gradient-to-r from-purple-500 to-indigo-500 rounded-full"></div>
|
||||
</div>
|
||||
<p className="text-sm text-slate-500 dark:text-slate-500 mb-4">
|
||||
<p className="text-sm text-slate-600 dark:text-slate-600 mb-4">
|
||||
Built with ❤️ for developers worldwide
|
||||
</p>
|
||||
<div className="flex flex-col items-center gap-4">
|
||||
<div className="flex justify-center items-center gap-6 text-xs text-slate-400 dark:text-slate-500">
|
||||
<div className="flex justify-center items-center gap-6 text-xs text-slate-600 dark:text-slate-600">
|
||||
<div className="flex items-center gap-1">
|
||||
<div className="w-1.5 h-1.5 bg-green-500 rounded-full animate-pulse"></div>
|
||||
<span>100% Client-Side</span>
|
||||
@@ -331,21 +335,21 @@ const Layout = ({ children }) => {
|
||||
<div className="flex items-center gap-4 text-xs">
|
||||
<button
|
||||
onClick={() => navigateWithGuard('/release-notes')}
|
||||
className="text-slate-400 hover:text-slate-600 dark:hover:text-slate-300 transition-colors"
|
||||
className="text-slate-600 hover:text-slate-600 dark:hover:text-slate-300 transition-colors"
|
||||
>
|
||||
Release Notes
|
||||
</button>
|
||||
<span className="text-slate-300 dark:text-slate-600">•</span>
|
||||
<button
|
||||
onClick={() => navigateWithGuard('/privacy')}
|
||||
className="text-slate-400 hover:text-slate-600 dark:hover:text-slate-300 transition-colors"
|
||||
className="text-slate-600 hover:text-slate-600 dark:hover:text-slate-300 transition-colors"
|
||||
>
|
||||
Privacy Policy
|
||||
</button>
|
||||
<span className="text-slate-300 dark:text-slate-600">•</span>
|
||||
<button
|
||||
onClick={() => navigateWithGuard('/terms')}
|
||||
className="text-slate-400 hover:text-slate-600 dark:hover:text-slate-300 transition-colors"
|
||||
className="text-slate-600 hover:text-slate-600 dark:hover:text-slate-300 transition-colors"
|
||||
>
|
||||
Terms of Service
|
||||
</button>
|
||||
@@ -379,7 +383,7 @@ const Layout = ({ children }) => {
|
||||
</div>
|
||||
<div className="flex items-center justify-center gap-2 mb-2">
|
||||
<div className="w-2 h-2 bg-gradient-to-r from-blue-500 to-purple-500 rounded-full"></div>
|
||||
<span className="text-xs font-medium text-slate-600 dark:text-slate-400">
|
||||
<span className="text-xs font-medium text-slate-600 dark:text-slate-600">
|
||||
© {SITE_CONFIG.year} {SITE_CONFIG.title}
|
||||
</span>
|
||||
<div className="w-2 h-2 bg-gradient-to-r from-purple-500 to-indigo-500 rounded-full"></div>
|
||||
@@ -387,21 +391,21 @@ const Layout = ({ children }) => {
|
||||
<div className="flex items-center justify-center gap-4 text-xs">
|
||||
<button
|
||||
onClick={() => navigateWithGuard('/release-notes')}
|
||||
className="text-slate-400 hover:text-slate-600 dark:hover:text-slate-300 transition-colors"
|
||||
className="text-slate-600 hover:text-slate-600 dark:hover:text-slate-300 transition-colors"
|
||||
>
|
||||
Release Notes
|
||||
</button>
|
||||
<span className="text-slate-300 dark:text-slate-600">•</span>
|
||||
<button
|
||||
onClick={() => navigateWithGuard('/privacy')}
|
||||
className="text-slate-400 hover:text-slate-600 dark:hover:text-slate-300 transition-colors"
|
||||
className="text-slate-600 hover:text-slate-600 dark:hover:text-slate-300 transition-colors"
|
||||
>
|
||||
Privacy Policy
|
||||
</button>
|
||||
<span className="text-slate-300 dark:text-slate-600">•</span>
|
||||
<button
|
||||
onClick={() => navigateWithGuard('/terms')}
|
||||
className="text-slate-400 hover:text-slate-600 dark:hover:text-slate-300 transition-colors"
|
||||
className="text-slate-600 hover:text-slate-600 dark:hover:text-slate-300 transition-colors"
|
||||
>
|
||||
Terms of Service
|
||||
</button>
|
||||
|
||||
Reference in New Issue
Block a user