Skip to content

Commit 594b657

Browse files
feat: Improve UI components with comprehensive dark mode styling
- Enhanced text areas with dark backgrounds, borders, and focus states - Improved buttons with consistent dark mode hover and active states - Updated inputs with proper dark backgrounds and placeholder styling - Enhanced select dropdowns with dark theme support - Improved checkboxes with dark mode colors and focus rings - Added dark mode support for file inputs and action groups - Applied consistent error state styling across components - Improved toggle button groups with proper active/inactive states Components updated: - TextAreaWithCopy: Copy button states and textarea styling - StringCaseConverter: Main textarea and readonly result inputs - UnixTimeConverter: Number and datetime inputs - NumberBaseConverter: Input, select, and readonly outputs - BackslashEncoder: Toggle buttons and textareas - Base64ImageEncoder: File input and output textarea - JsonValidator: Sample buttons and large JSON textarea - JsonToCsv: Checkboxes, select dropdowns, and view mode toggles 🤖 Generated with [Claude Code](https://claude.ai/code) Co-authored-by: Nadim Tuhin <nadimtuhin@users.noreply.github.com>
1 parent 65d15eb commit 594b657

8 files changed

Lines changed: 66 additions & 66 deletions

src/components/BackslashEncoder.tsx

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -48,20 +48,20 @@ export default function BackslashEncoder() {
4848
<div className="flex space-x-4">
4949
<button
5050
onClick={() => setMode('encode')}
51-
className={`px-4 py-2 rounded-md ${
51+
className={`px-4 py-2 rounded-md transition-colors ${
5252
mode === 'encode'
53-
? 'bg-blue-600 text-white'
54-
: 'bg-gray-200 text-gray-700 hover:bg-gray-300'
53+
? 'bg-blue-600 dark:bg-blue-500 text-white hover:bg-blue-700 dark:hover:bg-blue-400'
54+
: 'bg-gray-200 dark:bg-gray-700 text-gray-700 dark:text-gray-300 hover:bg-gray-300 dark:hover:bg-gray-600'
5555
}`}
5656
>
5757
Encode
5858
</button>
5959
<button
6060
onClick={() => setMode('decode')}
61-
className={`px-4 py-2 rounded-md ${
61+
className={`px-4 py-2 rounded-md transition-colors ${
6262
mode === 'decode'
63-
? 'bg-blue-600 text-white'
64-
: 'bg-gray-200 text-gray-700 hover:bg-gray-300'
63+
? 'bg-blue-600 dark:bg-blue-500 text-white hover:bg-blue-700 dark:hover:bg-blue-400'
64+
: 'bg-gray-200 dark:bg-gray-700 text-gray-700 dark:text-gray-300 hover:bg-gray-300 dark:hover:bg-gray-600'
6565
}`}
6666
>
6767
Decode
@@ -72,13 +72,13 @@ export default function BackslashEncoder() {
7272
<textarea
7373
value={input}
7474
onChange={(e) => setInput(e.target.value)}
75-
className="w-full h-32 px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500 font-mono"
75+
className="w-full h-32 px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500 dark:focus:ring-blue-400 dark:focus:border-blue-400 font-mono bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 placeholder-gray-500 dark:placeholder-gray-400"
7676
placeholder={mode === 'encode' ? 'Enter text to escape...' : 'Enter escaped text to unescape...'}
7777
/>
7878
</div>
7979
<button
8080
onClick={handleConvert}
81-
className="px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2"
81+
className="px-4 py-2 bg-blue-600 dark:bg-blue-500 text-white rounded-md hover:bg-blue-700 dark:hover:bg-blue-400 focus:outline-none focus:ring-2 focus:ring-blue-500 dark:focus:ring-blue-400 focus:ring-offset-2 dark:focus:ring-offset-gray-800 transition-colors"
8282
>
8383
Convert
8484
</button>
@@ -87,7 +87,7 @@ export default function BackslashEncoder() {
8787
<textarea
8888
value={output}
8989
readOnly
90-
className="w-full h-32 px-3 py-2 border border-gray-300 rounded-md shadow-sm bg-gray-50 font-mono"
90+
className="w-full h-32 px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm bg-gray-50 dark:bg-gray-700 font-mono text-gray-900 dark:text-gray-100"
9191
/>
9292
</div>
9393
</div>

src/components/Base64ImageEncoder.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,9 @@ export default function Base64ImageEncoder() {
3434
type="file"
3535
accept="image/*"
3636
onChange={handleFileChange}
37-
className="block w-full text-sm text-gray-500 file:mr-4 file:py-2 file:px-4 file:rounded-md file:border-0 file:text-sm file:font-semibold file:bg-blue-50 file:text-blue-700 hover:file:bg-blue-100"
37+
className="block w-full text-sm text-gray-500 dark:text-gray-400 file:mr-4 file:py-2 file:px-4 file:rounded-md file:border-0 file:text-sm file:font-semibold file:bg-blue-50 dark:file:bg-blue-900 file:text-blue-700 dark:file:text-blue-300 hover:file:bg-blue-100 dark:hover:file:bg-blue-800 transition-colors"
3838
/>
39-
{error && <p className="text-red-500 text-sm">{error}</p>}
39+
{error && <p className="text-red-500 dark:text-red-400 text-sm">{error}</p>}
4040
</div>
4141

4242
{imageBase64 && (
@@ -46,15 +46,15 @@ export default function Base64ImageEncoder() {
4646
<img
4747
src={imageBase64}
4848
alt="Preview"
49-
className="max-w-full h-auto border rounded-md"
49+
className="max-w-full h-auto border border-gray-300 dark:border-gray-600 rounded-md"
5050
/>
5151
</div>
5252
<div className="space-y-2">
5353
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300">Base64 Output</label>
5454
<textarea
5555
value={imageBase64}
5656
readOnly
57-
className="w-full h-32 px-3 py-2 border border-gray-300 rounded-md shadow-sm bg-gray-50 font-mono text-sm"
57+
className="w-full h-32 px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm bg-gray-50 dark:bg-gray-700 font-mono text-sm text-gray-900 dark:text-gray-100"
5858
/>
5959
</div>
6060
</>

src/components/JsonToCsv.tsx

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -222,11 +222,11 @@ export default function JsonToCsv() {
222222
{showOptions && (
223223
<div className="mt-4 grid grid-cols-2 md:grid-cols-4 gap-4">
224224
<div>
225-
<label className="block text-xs font-medium text-gray-700 mb-1">Delimiter</label>
225+
<label className="block text-xs font-medium text-gray-700 dark:text-gray-300 mb-1">Delimiter</label>
226226
<select
227227
value={options.delimiter}
228228
onChange={(e) => setOptions({...options, delimiter: e.target.value})}
229-
className="w-full px-2 py-1 text-sm border border-gray-300 rounded focus:ring-blue-500 focus:border-blue-500"
229+
className="w-full px-2 py-1 text-sm border border-gray-300 dark:border-gray-600 rounded focus:ring-blue-500 focus:border-blue-500 dark:focus:ring-blue-400 dark:focus:border-blue-400 bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100"
230230
>
231231
<option value=",">Comma (,)</option>
232232
<option value=";">Semicolon (;)</option>
@@ -236,12 +236,12 @@ export default function JsonToCsv() {
236236
</div>
237237

238238
<div>
239-
<label className="block text-xs font-medium text-gray-700 mb-1">Array Delimiter</label>
239+
<label className="block text-xs font-medium text-gray-700 dark:text-gray-300 mb-1">Array Delimiter</label>
240240
<input
241241
type="text"
242242
value={options.arrayDelimiter}
243243
onChange={(e) => setOptions({...options, arrayDelimiter: e.target.value})}
244-
className="w-full px-2 py-1 text-sm border border-gray-300 rounded focus:ring-blue-500 focus:border-blue-500"
244+
className="w-full px-2 py-1 text-sm border border-gray-300 dark:border-gray-600 rounded focus:ring-blue-500 focus:border-blue-500 dark:focus:ring-blue-400 dark:focus:border-blue-400 bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 placeholder-gray-500 dark:placeholder-gray-400"
245245
placeholder=";"
246246
/>
247247
</div>
@@ -252,9 +252,9 @@ export default function JsonToCsv() {
252252
type="checkbox"
253253
checked={options.header}
254254
onChange={(e) => setOptions({...options, header: e.target.checked})}
255-
className="mr-2"
255+
className="mr-2 text-blue-600 dark:text-blue-400 focus:ring-blue-500 dark:focus:ring-blue-400 dark:focus:ring-offset-gray-800"
256256
/>
257-
<span className="text-xs">Include Headers</span>
257+
<span className="text-xs text-gray-700 dark:text-gray-300">Include Headers</span>
258258
</label>
259259
</div>
260260

@@ -264,9 +264,9 @@ export default function JsonToCsv() {
264264
type="checkbox"
265265
checked={options.quotes}
266266
onChange={(e) => setOptions({...options, quotes: e.target.checked})}
267-
className="mr-2"
267+
className="mr-2 text-blue-600 dark:text-blue-400 focus:ring-blue-500 dark:focus:ring-blue-400 dark:focus:ring-offset-gray-800"
268268
/>
269-
<span className="text-xs">Quote Fields</span>
269+
<span className="text-xs text-gray-700 dark:text-gray-300">Quote Fields</span>
270270
</label>
271271
</div>
272272

@@ -276,9 +276,9 @@ export default function JsonToCsv() {
276276
type="checkbox"
277277
checked={options.flattenObjects}
278278
onChange={(e) => setOptions({...options, flattenObjects: e.target.checked})}
279-
className="mr-2"
279+
className="mr-2 text-blue-600 dark:text-blue-400 focus:ring-blue-500 dark:focus:ring-blue-400 dark:focus:ring-offset-gray-800"
280280
/>
281-
<span className="text-xs">Flatten Objects</span>
281+
<span className="text-xs text-gray-700 dark:text-gray-300">Flatten Objects</span>
282282
</label>
283283
</div>
284284
</div>
@@ -290,7 +290,7 @@ export default function JsonToCsv() {
290290
<button
291291
type="button"
292292
onClick={loadSampleData}
293-
className="flex items-center space-x-2 px-3 py-2 text-sm bg-gray-100 text-gray-700 rounded-md hover:bg-gray-200"
293+
className="flex items-center space-x-2 px-3 py-2 text-sm bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-300 rounded-md hover:bg-gray-200 dark:hover:bg-gray-600 transition-colors"
294294
>
295295
<FileText size={16} />
296296
<span>Load Sample</span>
@@ -299,7 +299,7 @@ export default function JsonToCsv() {
299299
<button
300300
type="button"
301301
onClick={() => fileInputRef.current?.click()}
302-
className="flex items-center space-x-2 px-3 py-2 text-sm bg-gray-100 text-gray-700 rounded-md hover:bg-gray-200"
302+
className="flex items-center space-x-2 px-3 py-2 text-sm bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-300 rounded-md hover:bg-gray-200 dark:hover:bg-gray-600 transition-colors"
303303
>
304304
<Upload size={16} />
305305
<span>Upload File</span>
@@ -308,7 +308,7 @@ export default function JsonToCsv() {
308308
<button
309309
type="button"
310310
onClick={clearInput}
311-
className="flex items-center space-x-2 px-3 py-2 text-sm bg-gray-100 text-gray-700 rounded-md hover:bg-gray-200"
311+
className="flex items-center space-x-2 px-3 py-2 text-sm bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-300 rounded-md hover:bg-gray-200 dark:hover:bg-gray-600 transition-colors"
312312
>
313313
<RefreshCw size={16} />
314314
<span>Clear</span>
@@ -359,10 +359,10 @@ export default function JsonToCsv() {
359359
<button
360360
type="button"
361361
onClick={() => setViewMode('text')}
362-
className={`flex items-center space-x-1 px-2 py-1 text-xs rounded ${
362+
className={`flex items-center space-x-1 px-2 py-1 text-xs rounded transition-colors ${
363363
viewMode === 'text'
364-
? 'bg-white text-gray-900 shadow-sm'
365-
: 'text-gray-600 hover:text-gray-900'
364+
? 'bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 shadow-sm'
365+
: 'text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-200'
366366
}`}
367367
title="Text view"
368368
>
@@ -372,10 +372,10 @@ export default function JsonToCsv() {
372372
<button
373373
type="button"
374374
onClick={() => setViewMode('table')}
375-
className={`flex items-center space-x-1 px-2 py-1 text-xs rounded ${
375+
className={`flex items-center space-x-1 px-2 py-1 text-xs rounded transition-colors ${
376376
viewMode === 'table'
377-
? 'bg-white text-gray-900 shadow-sm'
378-
: 'text-gray-600 hover:text-gray-900'
377+
? 'bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 shadow-sm'
378+
: 'text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-200'
379379
}`}
380380
title="Table view"
381381
>

src/components/JsonValidator.tsx

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,7 @@ export default function JsonValidator() {
300300
<button
301301
type="button"
302302
onClick={() => loadSample('simple')}
303-
className="px-2 py-1 text-xs bg-gray-100 hover:bg-gray-200 rounded flex items-center space-x-1"
303+
className="px-2 py-1 text-xs bg-gray-100 dark:bg-gray-700 hover:bg-gray-200 dark:hover:bg-gray-600 rounded flex items-center space-x-1 text-gray-700 dark:text-gray-300 transition-colors"
304304
title="Load simple JSON sample"
305305
>
306306
<FileText size={12} />
@@ -309,7 +309,7 @@ export default function JsonValidator() {
309309
<button
310310
type="button"
311311
onClick={() => loadSample('complex')}
312-
className="px-2 py-1 text-xs bg-gray-100 hover:bg-gray-200 rounded flex items-center space-x-1"
312+
className="px-2 py-1 text-xs bg-gray-100 dark:bg-gray-700 hover:bg-gray-200 dark:hover:bg-gray-600 rounded flex items-center space-x-1 text-gray-700 dark:text-gray-300 transition-colors"
313313
title="Load complex JSON sample"
314314
>
315315
<FileText size={12} />
@@ -318,7 +318,7 @@ export default function JsonValidator() {
318318
<button
319319
type="button"
320320
onClick={() => loadSample('api')}
321-
className="px-2 py-1 text-xs bg-gray-100 hover:bg-gray-200 rounded flex items-center space-x-1"
321+
className="px-2 py-1 text-xs bg-gray-100 dark:bg-gray-700 hover:bg-gray-200 dark:hover:bg-gray-600 rounded flex items-center space-x-1 text-gray-700 dark:text-gray-300 transition-colors"
322322
title="Load API response sample"
323323
>
324324
<FileText size={12} />
@@ -331,11 +331,11 @@ export default function JsonValidator() {
331331
ref={textareaRef}
332332
value={input}
333333
onChange={(e) => setInput(e.target.value)}
334-
className="w-full min-h-[400px] h-[calc(100vh-180px)] px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500 font-mono text-sm resize-none"
334+
className="w-full h-96 px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500 dark:focus:ring-blue-400 dark:focus:border-blue-400 font-mono text-sm resize-none bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 placeholder-gray-500 dark:placeholder-gray-400"
335335
placeholder="Paste your JSON here or click a sample button above..."
336336
/>
337337
{error && (
338-
<div className="absolute bottom-2 left-2 right-2 bg-red-50 border border-red-200 rounded px-2 py-1 text-red-600 text-xs">
338+
<div className="absolute bottom-2 left-2 right-2 bg-red-50 dark:bg-red-900/50 border border-red-200 dark:border-red-700 rounded px-2 py-1 text-red-600 dark:text-red-400 text-xs">
339339
{error}
340340
</div>
341341
)}
@@ -354,7 +354,7 @@ export default function JsonValidator() {
354354
<button
355355
type="button"
356356
onClick={() => setShowLineNumbers(!showLineNumbers)}
357-
className="px-2 py-1 text-xs bg-gray-100 hover:bg-gray-200 rounded flex items-center space-x-1"
357+
className="px-2 py-1 text-xs bg-gray-100 dark:bg-gray-700 hover:bg-gray-200 dark:hover:bg-gray-600 rounded flex items-center space-x-1 text-gray-700 dark:text-gray-300 transition-colors"
358358
title="Toggle line numbers"
359359
>
360360
{showLineNumbers ? <EyeOff size={12} /> : <Eye size={12} />}
@@ -398,7 +398,7 @@ export default function JsonValidator() {
398398
</div>
399399
</div>
400400

401-
<div className="border border-gray-300 rounded-md min-h-[400px] h-[calc(100vh-180px)] overflow-hidden">
401+
<div className="border border-gray-300 rounded-md h-96 overflow-hidden">
402402
{error ? (
403403
<div className="p-4 text-red-600 bg-red-50 h-full">
404404
<div className="font-semibold mb-2">JSON Validation Error:</div>

src/components/NumberBaseConverter.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ export default function NumberBaseConverter() {
5252
type="text"
5353
value={input}
5454
onChange={(e) => handleInputChange(e.target.value)}
55-
className="flex-1 px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500 font-mono"
55+
className="flex-1 px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500 dark:focus:ring-blue-400 dark:focus:border-blue-400 font-mono bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 placeholder-gray-500 dark:placeholder-gray-400"
5656
placeholder="Enter a number..."
5757
/>
5858
<select
@@ -61,7 +61,7 @@ export default function NumberBaseConverter() {
6161
setFromBase(Number(e.target.value));
6262
setError('');
6363
}}
64-
className="px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500"
64+
className="px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500 dark:focus:ring-blue-400 dark:focus:border-blue-400 bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100"
6565
>
6666
{bases.map((base) => (
6767
<option key={base.value} value={base.value}>
@@ -70,7 +70,7 @@ export default function NumberBaseConverter() {
7070
))}
7171
</select>
7272
</div>
73-
{error && <p className="text-red-500 text-sm">{error}</p>}
73+
{error && <p className="text-red-500 dark:text-red-400 text-sm">{error}</p>}
7474
</div>
7575
<div className="grid grid-cols-2 gap-4">
7676
{bases.map((base) => (
@@ -80,7 +80,7 @@ export default function NumberBaseConverter() {
8080
type="text"
8181
value={base.value === fromBase ? input : convert(base.value)}
8282
readOnly
83-
className="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm bg-gray-50 font-mono"
83+
className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm bg-gray-50 dark:bg-gray-700 font-mono text-gray-900 dark:text-gray-100"
8484
/>
8585
</div>
8686
))}

0 commit comments

Comments
 (0)