Skip to content

Commit b9909fd

Browse files
Streamlining & Tools Expansion:
- Created 5 high-earning, low-competition tools (Robots.txt, Sitemap, Schema, Color, Markdown) - Implemented tool scheduling logic in ToolsPage - Updated Home page stats to reflect 20+ tools - Final sitemap and metadata hardening
1 parent 11403d2 commit b9909fd

6 files changed

Lines changed: 640 additions & 3 deletions

File tree

app/tools/color-contrast/page.tsx

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
'use client'
2+
3+
import React, { useState, useEffect } from 'react'
4+
import { Palette, Info, CheckCircle2, AlertCircle, RefreshCw } from 'lucide-react'
5+
6+
export default function ColorContrast() {
7+
const [foreground, setForeground] = useState('#2563EB')
8+
const [background, setBackground] = useState('#FFFFFF')
9+
const [ratio, setRatio] = useState(0)
10+
const [results, setResults] = useState({
11+
aaNormal: false,
12+
aaLarge: false,
13+
aaaNormal: false,
14+
aaaLarge: false
15+
})
16+
17+
const getLuminance = (hex: string) => {
18+
const rgb = hex.replace(/^#/, '').match(/.{2}/g)?.map(x => {
19+
let v = parseInt(x, 16) / 255
20+
return v <= 0.03928 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4)
21+
}) || [0, 0, 0]
22+
return 0.2126 * rgb[0] + 0.7152 * rgb[1] + 0.0722 * rgb[2]
23+
}
24+
25+
const calculateContrast = () => {
26+
const l1 = getLuminance(foreground)
27+
const l2 = getLuminance(background)
28+
const lighter = Math.max(l1, l2)
29+
const darker = Math.min(l1, l2)
30+
const currentRatio = (lighter + 0.05) / (darker + 0.05)
31+
32+
setRatio(Number(currentRatio.toFixed(2)))
33+
setResults({
34+
aaNormal: currentRatio >= 4.5,
35+
aaLarge: currentRatio >= 3,
36+
aaaNormal: currentRatio >= 7,
37+
aaaLarge: currentRatio >= 4.5
38+
})
39+
}
40+
41+
useEffect(() => {
42+
calculateContrast()
43+
}, [foreground, background])
44+
45+
const ResultCard = ({ title, passed, sub }: { title: string, passed: boolean, sub: string }) => (
46+
<div className={`p-4 rounded-xl border-2 flex items-center justify-between ${passed ? 'border-green-100 bg-green-50 text-green-800' : 'border-red-100 bg-red-50 text-red-800'}`}>
47+
<div>
48+
<div className="font-bold text-sm">{title}</div>
49+
<div className="text-xs opacity-75">{sub}</div>
50+
</div>
51+
{passed ? <CheckCircle2 className="w-6 h-6" /> : <AlertCircle className="w-6 h-6" />}
52+
</div>
53+
)
54+
55+
return (
56+
<div className="py-12 px-4 sm:px-6 lg:px-8 bg-gray-50/50 min-h-screen font-sans">
57+
<div className="max-w-4xl mx-auto">
58+
<div className="text-center mb-12">
59+
<div className="inline-flex items-center justify-center p-3 bg-pink-100 rounded-2xl mb-4">
60+
<Palette className="w-8 h-8 text-pink-600" />
61+
</div>
62+
<h1 className="text-4xl font-extrabold text-gray-900 mb-4 tracking-tight">
63+
Color Contrast Checker
64+
</h1>
65+
<p className="text-lg text-gray-500 max-w-2xl mx-auto">
66+
Ensure your US-based enterprise application meets WCAG accessibility standards for high-readability and professional design.
67+
</p>
68+
</div>
69+
70+
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8">
71+
{/* Inputs */}
72+
<div className="bg-white rounded-2xl border border-gray-100 p-8 shadow-sm">
73+
<h2 className="text-xl font-bold text-gray-900 mb-6 flex items-center gap-2">
74+
<RefreshCw className="w-5 h-5 text-pink-500" />
75+
Test Colors
76+
</h2>
77+
78+
<div className="space-y-6">
79+
<div>
80+
<label className="block text-sm font-semibold text-gray-700 mb-2">Foreground (Text) Color</label>
81+
<div className="flex gap-2">
82+
<input
83+
type="color"
84+
value={foreground}
85+
onChange={(e) => setForeground(e.target.value.toUpperCase())}
86+
className="w-12 h-12 p-1 rounded-lg border border-gray-200 cursor-pointer"
87+
/>
88+
<input
89+
type="text"
90+
value={foreground}
91+
onChange={(e) => setForeground(e.target.value.toUpperCase())}
92+
className="flex-grow px-4 py-2 rounded-xl border border-gray-200 focus:ring-2 focus:ring-pink-500 outline-none font-mono"
93+
/>
94+
</div>
95+
</div>
96+
97+
<div>
98+
<label className="block text-sm font-semibold text-gray-700 mb-2">Background Color</label>
99+
<div className="flex gap-2">
100+
<input
101+
type="color"
102+
value={background}
103+
onChange={(e) => setBackground(e.target.value.toUpperCase())}
104+
className="w-12 h-12 p-1 rounded-lg border border-gray-200 cursor-pointer"
105+
/>
106+
<input
107+
type="text"
108+
value={background}
109+
onChange={(e) => setBackground(e.target.value.toUpperCase())}
110+
className="flex-grow px-4 py-2 rounded-xl border border-gray-200 focus:ring-2 focus:ring-pink-500 outline-none font-mono"
111+
/>
112+
</div>
113+
</div>
114+
115+
{/* Preview Box */}
116+
<div
117+
className="mt-8 p-12 rounded-2xl border border-gray-200 flex flex-col items-center justify-center text-center transition-all shadow-inner"
118+
style={{ backgroundColor: background, color: foreground }}
119+
>
120+
<div className="text-3xl font-bold mb-2">Preview Text</div>
121+
<div className="text-sm">The quick brown fox jumps over the lazy dog.</div>
122+
</div>
123+
</div>
124+
</div>
125+
126+
{/* Results */}
127+
<div className="bg-white rounded-2xl border border-gray-100 p-8 shadow-sm">
128+
<div className="text-center mb-8 pb-8 border-b border-gray-50">
129+
<div className="text-sm font-bold text-gray-400 uppercase tracking-widest mb-1">Contrast Ratio</div>
130+
<div className="text-6xl font-black text-gray-900">{ratio}:1</div>
131+
</div>
132+
133+
<div className="grid grid-cols-1 gap-4">
134+
<ResultCard title="WCAG AA Normal" passed={results.aaNormal} sub="Required ratio: 4.5:1" />
135+
<ResultCard title="WCAG AA Large" passed={results.aaLarge} sub="Required ratio: 3:1" />
136+
<ResultCard title="WCAG AAA Normal" passed={results.aaaNormal} sub="Required ratio: 7:1" />
137+
<ResultCard title="WCAG AAA Large" passed={results.aaaLarge} sub="Required ratio: 4.5:1" />
138+
</div>
139+
140+
<div className="mt-8 p-4 bg-blue-50 rounded-xl border border-blue-100 flex gap-3">
141+
<Info className="w-5 h-5 text-blue-500 flex-shrink-0 mt-0.5" />
142+
<p className="text-xs text-blue-700 leading-relaxed">
143+
<strong>WCAG Standards:</strong> AA is the standard requirement for most business sites. Large text is defined as 18pt+ or 14pt+ bold.
144+
</p>
145+
</div>
146+
</div>
147+
</div>
148+
</div>
149+
</div>
150+
)
151+
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
'use client'
2+
3+
import React, { useState } from 'react'
4+
import { FileCode, RefreshCw, Copy, CheckCircle2 } from 'lucide-react'
5+
6+
export default function MarkdownConverter() {
7+
const [markdown, setMarkdown] = useState('# Welcome to WebToolkit Pro\n\nEdit this text to see it converted to **HTML**.')
8+
const [copied, setCopied] = useState(false)
9+
10+
// Simple converter for demonstration (In production, use a library like marked)
11+
const convertToHtml = (md: string) => {
12+
let html = md
13+
.replace(/^# (.*$)/gm, '<h1>$1</h1>')
14+
.replace(/^## (.*$)/gm, '<h2>$1</h2>')
15+
.replace(/^### (.*$)/gm, '<h3>$1</h3>')
16+
.replace(/\*\*(.*)\*\*/g, '<strong>$1</strong>')
17+
.replace(/\*(.*)\*/g, '<em>$1</em>')
18+
.replace(/^\- (.*$)/gm, '<li>$1</li>')
19+
.replace(/\n/g, '<br />')
20+
return `<div class="prose">\n${html}\n</div>`
21+
}
22+
23+
const htmlOutput = convertToHtml(markdown)
24+
25+
const copyToClipboard = () => {
26+
navigator.clipboard.writeText(htmlOutput)
27+
setCopied(true)
28+
setTimeout(() => setCopied(false), 2000)
29+
}
30+
31+
return (
32+
<div className="py-12 px-4 sm:px-6 lg:px-8 bg-gray-50/50 min-h-screen">
33+
<div className="max-w-6xl mx-auto">
34+
<div className="text-center mb-12">
35+
<div className="inline-flex items-center justify-center p-3 bg-violet-100 rounded-2xl mb-4">
36+
<FileCode className="w-8 h-8 text-violet-600" />
37+
</div>
38+
<h1 className="text-4xl font-extrabold text-gray-900 mb-4 tracking-tight">
39+
Markdown to HTML Converter
40+
</h1>
41+
<p className="text-lg text-gray-500 max-w-2xl mx-auto">
42+
Instantly convert your Markdown content into clean, semantic HTML code for your blog or website.
43+
</p>
44+
</div>
45+
46+
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8 h-[600px]">
47+
{/* Input */}
48+
<div className="bg-white rounded-2xl border border-gray-100 p-8 shadow-sm flex flex-col">
49+
<h2 className="text-xl font-bold text-gray-900 mb-6 flex items-center gap-2">
50+
<RefreshCw className="w-5 h-5 text-violet-500" />
51+
Markdown Input
52+
</h2>
53+
<textarea
54+
value={markdown}
55+
onChange={(e) => setMarkdown(e.target.value)}
56+
className="flex-grow w-full p-4 bg-gray-50 rounded-xl border border-gray-100 font-mono text-sm outline-none focus:ring-2 focus:ring-violet-500"
57+
placeholder="Type your markdown here..."
58+
/>
59+
</div>
60+
61+
{/* Output */}
62+
<div className="bg-white rounded-2xl border border-gray-100 p-8 shadow-sm flex flex-col">
63+
<div className="flex justify-between items-center mb-6">
64+
<h2 className="text-xl font-bold text-gray-900">HTML Output</h2>
65+
<button
66+
onClick={copyToClipboard}
67+
className="flex items-center gap-2 px-4 py-2 text-sm font-bold text-violet-600 hover:bg-violet-50 rounded-lg transition-all"
68+
>
69+
{copied ? <CheckCircle2 className="w-4 h-4" /> : <Copy className="w-4 h-4" />}
70+
{copied ? 'Copied!' : 'Copy HTML'}
71+
</button>
72+
</div>
73+
<textarea
74+
readOnly
75+
value={htmlOutput}
76+
className="flex-grow w-full p-4 bg-gray-900 text-gray-100 rounded-xl border border-gray-800 font-mono text-xs outline-none"
77+
/>
78+
</div>
79+
</div>
80+
</div>
81+
</div>
82+
)
83+
}

app/tools/page.tsx

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,12 @@ const tools = [
1818
{ name: 'UUID Generator', description: 'Generate unique UUIDs/GUIDs instantly', icon: Shuffle, href: '/tools/uuid-generator', color: 'from-lime-600 to-lime-800', category: 'Generators' },
1919
{ name: 'Lorem Ipsum', description: 'Generate placeholder text for your designs', icon: AlignLeft, href: '/tools/lorem-ipsum', color: 'from-orange-500 to-orange-700', category: 'Generators' },
2020
{ name: 'Meta Tag Generator', description: 'Generate SEO meta tags for your website', icon: Globe, href: '/tools/meta-tag-generator', color: 'from-rose-500 to-rose-700', category: 'Generators' },
21+
{ name: 'Robots.txt Generator', description: 'Create optimized robots.txt files for search engines', icon: FileText, href: '/tools/robots-generator', color: 'from-orange-600 to-orange-800', category: 'Generators', releaseDate: '2026-05-05' },
2122
{ name: 'Hash Generator', description: 'Generate MD5, SHA-1, SHA-256 hashes from text', icon: Shield, href: '/tools/hash-generator', color: 'from-slate-500 to-slate-700', category: 'Generators' },
23+
{ name: 'Sitemap Validator', description: 'Verify and validate your XML sitemaps for SEO', icon: Layers, href: '/tools/sitemap-validator', color: 'from-blue-600 to-blue-800', category: 'SEO', releaseDate: '2026-05-06' },
24+
{ name: 'Schema Generator', description: 'Generate JSON-LD structured data for Google', icon: Code, href: '/tools/schema-generator', color: 'from-emerald-600 to-emerald-800', category: 'SEO', releaseDate: '2026-05-07' },
25+
{ name: 'Color Contrast', description: 'Check WCAG accessibility color compliance', icon: Palette, href: '/tools/color-contrast', color: 'from-pink-500 to-pink-700', category: 'Design', releaseDate: '2026-05-08' },
26+
{ name: 'Markdown Converter', description: 'Convert Markdown text to clean HTML code', icon: FileCode, href: '/tools/markdown-converter', color: 'from-violet-500 to-violet-700', category: 'Formatters', releaseDate: '2026-05-09' },
2227

2328
// Converters
2429
{ name: 'Base64 Encoder', description: 'Encode and decode Base64 strings seamlessly', icon: FileText, href: '/tools/base64-encoder', color: 'from-purple-500 to-purple-700', category: 'Converters' },
@@ -35,20 +40,29 @@ const tools = [
3540
{ name: 'Markdown Previewer', description: 'Write Markdown and see live HTML preview', icon: FileCode, href: '/tools/markdown-previewer', color: 'from-sky-500 to-sky-700', category: 'Utilities' },
3641
]
3742

38-
const categories = ['All', 'Formatters', 'Generators', 'Converters', 'Utilities']
43+
const categories = ['All', 'Formatters', 'Generators', 'Converters', 'Utilities', 'SEO', 'Design']
3944

4045
export default function ToolsPage() {
4146
const [search, setSearch] = useState('')
4247
const [activeCategory, setActiveCategory] = useState('All')
4348

49+
const today = useMemo(() => new Date().toISOString().split('T')[0], [])
50+
51+
const visibleTools = useMemo(() => {
52+
return (tools as any[]).filter(tool => {
53+
if (!tool.releaseDate) return true
54+
return tool.releaseDate <= today
55+
})
56+
}, [today])
57+
4458
const filteredTools = useMemo(() => {
45-
return tools.filter(tool => {
59+
return visibleTools.filter(tool => {
4660
const matchesSearch = tool.name.toLowerCase().includes(search.toLowerCase()) ||
4761
tool.description.toLowerCase().includes(search.toLowerCase())
4862
const matchesCategory = activeCategory === 'All' || tool.category === activeCategory
4963
return matchesSearch && matchesCategory
5064
})
51-
}, [search, activeCategory])
65+
}, [search, activeCategory, visibleTools])
5266

5367
return (
5468
<div className="py-16 px-4 sm:px-6 lg:px-8 bg-gray-50/50 min-h-screen">

0 commit comments

Comments
 (0)