feat: implement Figlet, Pastel, and Unit tools with a unified layout
- Add Figlet text converter with font selection and history - Add Pastel color palette generator and manipulation suite - Add comprehensive Units converter with category-based logic - Introduce AppShell with Sidebar and Header for navigation - Modernize theme system with CSS variables and new animations - Update project configuration and dependencies
This commit is contained in:
6
lib/pastel/utils/cn.ts
Normal file
6
lib/pastel/utils/cn.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { clsx, type ClassValue } from 'clsx';
|
||||
import { twMerge } from 'tailwind-merge';
|
||||
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs));
|
||||
}
|
||||
57
lib/pastel/utils/color.ts
Normal file
57
lib/pastel/utils/color.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
/**
|
||||
* Calculate relative luminance of a color
|
||||
* Based on WCAG 2.1 specification
|
||||
*/
|
||||
export function getRelativeLuminance(r: number, g: number, b: number): number {
|
||||
const [rs, gs, bs] = [r, g, b].map((c) => {
|
||||
const sRGB = c / 255;
|
||||
return sRGB <= 0.03928 ? sRGB / 12.92 : Math.pow((sRGB + 0.055) / 1.055, 2.4);
|
||||
});
|
||||
return 0.2126 * rs + 0.7152 * gs + 0.0722 * bs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate contrast ratio between two colors
|
||||
* Returns ratio from 1 to 21
|
||||
*/
|
||||
export function getContrastRatio(
|
||||
fg: { r: number; g: number; b: number },
|
||||
bg: { r: number; g: number; b: number }
|
||||
): number {
|
||||
const l1 = getRelativeLuminance(fg.r, fg.g, fg.b);
|
||||
const l2 = getRelativeLuminance(bg.r, bg.g, bg.b);
|
||||
const lighter = Math.max(l1, l2);
|
||||
const darker = Math.min(l1, l2);
|
||||
return (lighter + 0.05) / (darker + 0.05);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse hex color to RGB
|
||||
*/
|
||||
export function hexToRgb(hex: string): { r: number; g: number; b: number } | null {
|
||||
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
|
||||
return result
|
||||
? {
|
||||
r: parseInt(result[1], 16),
|
||||
g: parseInt(result[2], 16),
|
||||
b: parseInt(result[3], 16),
|
||||
}
|
||||
: null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a contrast ratio meets WCAG standards
|
||||
*/
|
||||
export function checkWCAGCompliance(ratio: number) {
|
||||
return {
|
||||
aa: {
|
||||
normalText: ratio >= 4.5,
|
||||
largeText: ratio >= 3,
|
||||
ui: ratio >= 3,
|
||||
},
|
||||
aaa: {
|
||||
normalText: ratio >= 7,
|
||||
largeText: ratio >= 4.5,
|
||||
},
|
||||
};
|
||||
}
|
||||
83
lib/pastel/utils/export.ts
Normal file
83
lib/pastel/utils/export.ts
Normal file
@@ -0,0 +1,83 @@
|
||||
/**
|
||||
* Export utilities for color palettes
|
||||
*/
|
||||
|
||||
export interface ExportColor {
|
||||
name?: string;
|
||||
hex: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Export colors as CSS variables
|
||||
*/
|
||||
export function exportAsCSS(colors: ExportColor[]): string {
|
||||
const variables = colors
|
||||
.map((color, index) => {
|
||||
const name = color.name || `color-${index + 1}`;
|
||||
return ` --${name}: ${color.hex};`;
|
||||
})
|
||||
.join('\n');
|
||||
|
||||
return `:root {\n${variables}\n}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Export colors as SCSS variables
|
||||
*/
|
||||
export function exportAsSCSS(colors: ExportColor[]): string {
|
||||
return colors
|
||||
.map((color, index) => {
|
||||
const name = color.name || `color-${index + 1}`;
|
||||
return `$${name}: ${color.hex};`;
|
||||
})
|
||||
.join('\n');
|
||||
}
|
||||
|
||||
/**
|
||||
* Export colors as Tailwind config
|
||||
*/
|
||||
export function exportAsTailwind(colors: ExportColor[]): string {
|
||||
const colorEntries = colors
|
||||
.map((color, index) => {
|
||||
const name = color.name || `color-${index + 1}`;
|
||||
return ` '${name}': '${color.hex}',`;
|
||||
})
|
||||
.join('\n');
|
||||
|
||||
return `module.exports = {\n theme: {\n extend: {\n colors: {\n${colorEntries}\n },\n },\n },\n};`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Export colors as JSON
|
||||
*/
|
||||
export function exportAsJSON(colors: ExportColor[]): string {
|
||||
const colorObjects = colors.map((color, index) => ({
|
||||
name: color.name || `color-${index + 1}`,
|
||||
hex: color.hex,
|
||||
}));
|
||||
|
||||
return JSON.stringify({ colors: colorObjects }, null, 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Export colors as JavaScript array
|
||||
*/
|
||||
export function exportAsJavaScript(colors: ExportColor[]): string {
|
||||
const colorArray = colors.map((c) => `'${c.hex}'`).join(', ');
|
||||
return `const colors = [${colorArray}];`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Download text as file
|
||||
*/
|
||||
export function downloadAsFile(content: string, filename: string, mimeType: string = 'text/plain') {
|
||||
const blob = new Blob([content], { type: mimeType });
|
||||
const url = URL.createObjectURL(blob);
|
||||
const link = document.createElement('a');
|
||||
link.href = url;
|
||||
link.download = filename;
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
URL.revokeObjectURL(url);
|
||||
}
|
||||
Reference in New Issue
Block a user