Skip to content

Commit 4448b00

Browse files
committed
feat: new apps
1 parent 32d5885 commit 4448b00

File tree

4 files changed

+230
-0
lines changed

4 files changed

+230
-0
lines changed

src/components/AnimatedRoutes.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import AsciiConverterPage from '../pages/apps/AsciiConverterPage';
2727
import HashGeneratorPage from '../pages/apps/HashGeneratorPage';
2828
import UuidGeneratorPage from '../pages/apps/UuidGeneratorPage';
2929
import ColorPaletteGeneratorPage from '../pages/apps/ColorPaletteGeneratorPage';
30+
import CssUnitConverterPage from '../pages/apps/CssUnitConverterPage';
3031

3132
import UsefulLinksPage from '../pages/UsefulLinksPage';
3233

@@ -435,6 +436,20 @@ function AnimatedRoutes() {
435436
</motion.div>
436437
}
437438
/>
439+
<Route
440+
path="/apps/css-unit-converter"
441+
element={
442+
<motion.div
443+
initial="initial"
444+
animate="in"
445+
exit="out"
446+
variants={pageVariants}
447+
transition={pageTransition}
448+
>
449+
<CssUnitConverterPage />
450+
</motion.div>
451+
}
452+
/>
438453
{/* D&D specific 404 page */}
439454
<Route
440455
path="/dnd/*"

src/pages/AppPage.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,11 @@ const apps = [
5050
title: 'Color Palette Generator',
5151
description: 'Generate random color palettes.',
5252
},
53+
{
54+
to: '/apps/css-unit-converter',
55+
title: 'CSS Unit Converter',
56+
description: 'Convert between px, em, rem, vw, vh, and % units.',
57+
},
5358
];
5459

5560
function AppPage() {
Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
import React, { useState } from 'react';
2+
import { Link } from 'react-router-dom';
3+
import { ArrowLeftIcon } from '@phosphor-icons/react';
4+
import colors from '../../config/colors';
5+
import usePageTitle from '../../utils/usePageTitle';
6+
7+
const CssUnitConverterPage = () => {
8+
usePageTitle('CSS Unit Converter');
9+
10+
const [inputValue, setInputValue] = useState('');
11+
const [inputUnit, setInputUnit] = useState('px');
12+
const [basePx, setBasePx] = useState(16); // Default base pixel for rem/em calculations
13+
14+
const convertUnits = (value, unit, base) => {
15+
const numValue = parseFloat(value);
16+
if (isNaN(numValue)) {
17+
return {
18+
px: '',
19+
em: '',
20+
rem: '',
21+
vw: '',
22+
vh: '',
23+
percent: ''
24+
};
25+
}
26+
27+
let pxValue;
28+
switch (unit) {
29+
case 'px':
30+
pxValue = numValue;
31+
break;
32+
case 'em':
33+
case 'rem':
34+
pxValue = numValue * base;
35+
break;
36+
case 'vw':
37+
// Assuming a viewport width of 1920px for a common reference
38+
pxValue = (numValue / 100) * 1920;
39+
break;
40+
case 'vh':
41+
// Assuming a viewport height of 1080px for a common reference
42+
pxValue = (numValue / 100) * 1080;
43+
break;
44+
case 'percent':
45+
// Percentage is relative, so we need a base. Let's assume 16px for 100%
46+
pxValue = (numValue / 100) * base;
47+
break;
48+
default:
49+
pxValue = numValue;
50+
}
51+
52+
return {
53+
px: pxValue.toFixed(2),
54+
em: (pxValue / base).toFixed(2),
55+
rem: (pxValue / base).toFixed(2),
56+
vw: ((pxValue / 1920) * 100).toFixed(2),
57+
vh: ((pxValue / 1080) * 100).toFixed(2),
58+
percent: ((pxValue / base) * 100).toFixed(2)
59+
};
60+
};
61+
62+
const conversions = convertUnits(inputValue, inputUnit, basePx);
63+
64+
const cardStyle = {
65+
backgroundColor: colors['app-alpha-10'],
66+
borderColor: colors['app-alpha-50'],
67+
color: colors.app,
68+
};
69+
70+
return (
71+
<div className="py-16 sm:py-24">
72+
<div className="mx-auto max-w-7xl px-6 lg:px-8 text-gray-300">
73+
<Link
74+
to="/apps"
75+
className="text-article hover:underline flex items-center justify-center gap-2 text-lg mb-4"
76+
>
77+
<ArrowLeftIcon size={24} /> Back to Apps
78+
</Link>
79+
<h1 className="text-4xl font-bold tracking-tight sm:text-6xl mb-4 flex items-center">
80+
<span className="codex-color">fc</span>
81+
<span className="separator-color">::</span>
82+
<span className="apps-color">apps</span>
83+
<span className="separator-color">::</span>
84+
<span className="single-app-color">css-units</span>
85+
</h1>
86+
<hr className="border-gray-700" />
87+
<div className="flex justify-center items-center mt-16">
88+
<div
89+
className="group bg-transparent border rounded-lg shadow-2xl p-6 flex flex-col justify-between relative transform transition-all duration-300 ease-in-out scale-105 overflow-hidden h-full w-full max-w-4xl"
90+
style={cardStyle}
91+
>
92+
<div
93+
className="absolute top-0 left-0 w-full h-full opacity-10"
94+
style={{
95+
backgroundImage:
96+
'radial-gradient(circle, white 1px, transparent 1px)',
97+
backgroundSize: '10px 10px',
98+
}}
99+
></div>
100+
<div className="relative z-10 p-1">
101+
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 mb-6">
102+
<div>
103+
<label htmlFor="inputValue" className="block text-sm font-medium text-gray-300">
104+
Value
105+
</label>
106+
<input
107+
type="number"
108+
id="inputValue"
109+
className="mt-1 block w-full p-2 border border-gray-600 rounded-md bg-gray-700 text-white focus:ring-blue-500 focus:border-blue-500"
110+
value={inputValue}
111+
onChange={(e) => setInputValue(e.target.value)}
112+
placeholder="Enter value"
113+
/>
114+
</div>
115+
<div>
116+
<label htmlFor="inputUnit" className="block text-sm font-medium text-gray-300">
117+
Unit
118+
</label>
119+
<select
120+
id="inputUnit"
121+
className="mt-1 block w-full p-2 border border-gray-600 rounded-md bg-gray-700 text-white focus:ring-blue-500 focus:border-blue-500"
122+
value={inputUnit}
123+
onChange={(e) => setInputUnit(e.target.value)}
124+
>
125+
<option value="px">px</option>
126+
<option value="em">em</option>
127+
<option value="rem">rem</option>
128+
<option value="vw">vw</option>
129+
<option value="vh">vh</option>
130+
<option value="percent">%</option>
131+
</select>
132+
</div>
133+
<div>
134+
<label htmlFor="basePx" className="block text-sm font-medium text-gray-300">
135+
Base Pixel (for em/rem)
136+
</label>
137+
<input
138+
type="number"
139+
id="basePx"
140+
className="mt-1 block w-full p-2 border border-gray-600 rounded-md bg-gray-700 text-white focus:ring-blue-500 focus:border-blue-500"
141+
value={basePx}
142+
onChange={(e) => setBasePx(parseFloat(e.target.value) || 16)}
143+
placeholder="16"
144+
/>
145+
</div>
146+
</div>
147+
148+
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
149+
{Object.entries(conversions).map(([unit, value]) => (
150+
<div key={unit} className="bg-gray-700 p-4 rounded-md">
151+
<p className="text-sm font-medium text-gray-400 uppercase">{unit}</p>
152+
<p className="text-xl font-semibold text-blue-400">{value}{unit === 'percent' ? '%' : unit}</p>
153+
</div>
154+
))}
155+
</div>
156+
</div>
157+
</div>
158+
</div>
159+
160+
<div className="flex justify-center items-center mt-16">
161+
<div
162+
className="group bg-transparent border rounded-lg shadow-2xl p-6 flex flex-col justify-between relative transform transition-all duration-300 ease-in-out scale-105 overflow-hidden h-full w-full max-w-4xl"
163+
style={cardStyle}
164+
>
165+
<div
166+
className="absolute top-0 left-0 w-full h-full opacity-10"
167+
style={{
168+
backgroundImage:
169+
'radial-gradient(circle, white 1px, transparent 1px)',
170+
backgroundSize: '10px 10px',
171+
}}
172+
></div>
173+
<div className="relative z-10 p-1">
174+
<div className="prose prose-invert max-w-none">
175+
<h2 className="text-2xl font-bold mb-4">Understanding CSS Units</h2>
176+
<p>CSS units are essential for defining lengths and sizes in web design. They can be broadly categorized into absolute and relative units.</p>
177+
178+
<h3 className="text-xl font-semibold mt-6 mb-2">Absolute Units</h3>
179+
<ul>
180+
<li><strong>px (Pixels):</strong> The most common absolute unit. 1px is typically 1/96th of an inch. While technically absolute, their rendering can vary slightly across devices due to pixel density.</li>
181+
</ul>
182+
183+
<h3 className="text-xl font-semibold mt-6 mb-2">Relative Units</h3>
184+
<p>Relative units scale relative to another length property, making them excellent for responsive design.</p>
185+
<ul>
186+
<li><strong>em:</strong> Relative to the font-size of the parent element. If the parent has `font-size: 16px`, then `1em` equals `16px`.</li>
187+
<li><strong>rem (Root em):</strong> Relative to the font-size of the root HTML element (`html tag`). This makes `rem` units more predictable than `em` because they don't compound with nested elements.</li>
188+
<li><strong>vw (Viewport Width):</strong> Relative to 1% of the viewport's width. If the viewport is 1000px wide, `1vw` is `10px`.</li>
189+
<li><strong>vh (Viewport Height):</strong> Relative to 1% of the viewport's height. If the viewport is 800px high, `1vh` is `8px`.</li>
190+
<li><strong>% (Percentage):</strong> Can be relative to various properties depending on the context. For font-size, it's relative to the parent's font-size. For width/height, it's relative to the parent's width/height.</li>
191+
</ul>
192+
193+
<h3 className="text-xl font-semibold mt-6 mb-2">When to Use Which?</h3>
194+
<ul>
195+
<li><strong>`px`</strong> for fixed-size elements or when precise pixel control is needed, though often less flexible for responsiveness.</li>
196+
<li><strong>`rem`</strong> for typography and spacing to maintain a consistent scale across the entire site, easily adjustable by changing the root font-size.</li>
197+
<li><strong>`em`</strong> for components where scaling relative to the immediate parent's font-size is desired (e.g., icons within a button).</li>
198+
<li><strong>`vw` and `vh`</strong> for elements that should scale directly with the viewport dimensions, useful for full-width/height sections or responsive typography.</li>
199+
<li><strong>`%`</strong> for widths, heights, padding, and margins that need to be relative to their parent container.</li>
200+
</ul>
201+
</div>
202+
</div>
203+
</div>
204+
</div>
205+
</div>
206+
</div> );
207+
};
208+
209+
export default CssUnitConverterPage;

src/pages/apps/UuidGeneratorPage.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ function UuidGeneratorPage() {
1515
// RFC 4122 v4 UUID generation
1616
const uuidV4 = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
1717
const r = crypto.getRandomValues(new Uint8Array(1))[0] % 16; // Use crypto.getRandomValues for better randomness
18+
// eslint-disable-next-line no-mixed-operators
1819
const v = c === 'x' ? r : (r & 0x3 | 0x8);
1920
return v.toString(16);
2021
});

0 commit comments

Comments
 (0)