fix: improve visual comparison bars with logarithmic scale

Enhanced the visual comparison chart for better readability:

📊 Logarithmic Scale:
- Use log10 scale instead of linear for better visualization
- Handles units with vastly different magnitudes (mm vs km)
- Minimum bar width of 5% for visibility
- Normalizes across the full range (minLog to maxLog)
- Prevents tiny bars that are hard to see

 Visual Improvements:
- Taller bars (h-3 instead of h-2) for better visibility
- Larger, bolder value display (text-lg font-bold)
- Better spacing (space-y-1.5)
- Percentage indicator on each bar
- White text on bars >50% filled
- Shadow on bars for depth
- Improved typography hierarchy
- Better gap spacing between label and value

🎨 Layout Enhancements:
- Unit name on left, value on right
- Value with unit abbreviation in muted color
- Flex layout with proper wrapping
- Tabular numbers for alignment
- Relative positioning for percentage labels

This makes the chart view much more useful for comparing units
with different orders of magnitude (e.g., nanometers to kilometers).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-11-08 10:23:50 +01:00
parent 7f7bc69d04
commit 5452da6725

View File

@@ -13,17 +13,36 @@ export default function VisualComparison({
conversions,
color,
}: VisualComparisonProps) {
// Calculate percentages for visual bars
// Calculate percentages for visual bars using logarithmic scale
const withPercentages = useMemo(() => {
if (conversions.length === 0) return [];
const maxValue = Math.max(...conversions.map(c => Math.abs(c.value)));
if (maxValue === 0) return conversions.map(c => ({ ...c, percentage: 0 }));
// Filter out zero or negative values for log scale
const validConversions = conversions.filter(c => c.value > 0);
if (validConversions.length === 0) {
return conversions.map(c => ({ ...c, percentage: 0 }));
}
return conversions.map(c => ({
...c,
percentage: (Math.abs(c.value) / maxValue) * 100,
}));
// Use logarithmic scale for better visualization across different magnitudes
const logValues = validConversions.map(c => Math.log10(c.value));
const minLog = Math.min(...logValues);
const maxLog = Math.max(...logValues);
const logRange = maxLog - minLog;
return conversions.map(c => {
if (c.value <= 0) return { ...c, percentage: 0 };
const logValue = Math.log10(c.value);
// Normalize to 0-100 range, with minimum bar width of 5%
const percentage = logRange === 0
? 100
: Math.max(5, ((logValue - minLog) / logRange) * 100);
return {
...c,
percentage,
};
});
}, [conversions]);
if (conversions.length === 0) {
@@ -37,21 +56,39 @@ export default function VisualComparison({
return (
<div className="space-y-3">
{withPercentages.map(item => (
<div key={item.unit} className="space-y-1">
<div className="flex items-center justify-between text-sm">
<span className="font-medium">{item.unitInfo.plural}</span>
<span className="tabular-nums">
{formatNumber(item.value)} {item.unit}
<div key={item.unit} className="space-y-1.5">
<div className="flex items-baseline justify-between gap-4">
<span className="text-sm font-medium text-foreground min-w-0 flex-shrink">
{item.unitInfo.plural}
</span>
<span className="text-lg font-bold tabular-nums flex-shrink-0">
{formatNumber(item.value)}
<span className="text-sm font-normal text-muted-foreground ml-1">
{item.unit}
</span>
</span>
</div>
<div className="h-2 bg-muted rounded-full overflow-hidden">
<div className="relative h-3 bg-muted rounded-full overflow-hidden">
<div
className="h-full rounded-full transition-all duration-500 ease-out"
className="h-full rounded-full transition-all duration-500 ease-out shadow-sm"
style={{
width: `${item.percentage}%`,
backgroundColor: `var(--color-${color})`,
}}
/>
{/* Percentage indicator */}
<div
className="absolute top-0 right-0 h-full flex items-center pr-2 text-[10px] font-medium"
style={{
color: item.percentage > 50 ? 'white' : 'inherit',
}}
>
{item.percentage < 100 && (
<span className={item.percentage > 50 ? 'text-white' : 'text-muted-foreground'}>
{Math.round(item.percentage)}%
</span>
)}
</div>
</div>
</div>
))}