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:
dwindown
2026-02-18 18:57:31 +07:00
parent 9dc3285adb
commit 3a475e9df2
41 changed files with 391 additions and 318 deletions

View File

@@ -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>