fintechdarkpatterns / src /app /index.html
yujisium's picture
Fintech Dark Patterns NLP Detector - full project upload
5752a28 verified
Raw
History Blame Contribute Delete
20.8 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>FinAudit AI - Pro Auditor</title>
<script src="https://cdn.tailwindcss.com"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
<style>
/* Modern Glassmorphism & Animations */
body {
background: linear-gradient(135deg, #0f172a 0%, #1e1b4b 50%, #0f172a 100%);
background-size: 200% 200%;
animation: gradientMove 15s ease infinite;
}
@keyframes gradientMove {
0% { background-position: 0% 50%; }
50% { background-position: 100% 50%; }
100% { background-position: 0% 50%; }
}
.glass-panel {
background: rgba(17, 24, 39, 0.7);
backdrop-filter: blur(12px);
-webkit-backdrop-filter: blur(12px);
border: 1px solid rgba(255, 255, 255, 0.1);
}
/* Realistic Phone Mockup */
.phone-bezel {
box-shadow: 0 0 0 2px #1f2937, 0 25px 50px -12px rgba(0, 0, 0, 0.8), inset 0 0 20px rgba(255,255,255,0.1);
}
.dynamic-island {
position: absolute;
top: 10px; left: 50%;
transform: translateX(-50%);
width: 100px; height: 30px;
background: #000;
border-radius: 20px;
z-index: 40;
}
/* Dynamic XAI Heatmap */
.heatmap-layer {
position: absolute; inset: 0;
pointer-events: none; opacity: 0;
transition: opacity 0.6s cubic-bezier(0.4, 0, 0.2, 1);
z-index: 50; background: rgba(0,0,10,0.6);
}
.heatmap-active .heatmap-layer { opacity: 1; }
.hotspot {
position: absolute; border-radius: 50%;
filter: blur(25px);
animation: pulseGlow 2s infinite alternate;
mix-blend-mode: screen;
}
.hotspot-severe { background: radial-gradient(circle, rgba(255,40,40,0.9) 0%, rgba(255,0,0,0) 70%); }
.hotspot-warn { background: radial-gradient(circle, rgba(255,180,0,0.9) 0%, rgba(255,140,0,0) 70%); }
@keyframes pulseGlow {
0% { transform: scale(1) translate(-50%, -50%); opacity: 0.8; }
100% { transform: scale(1.15) translate(-45%, -45%); opacity: 1; }
}
/* Custom Scrollbar */
::-webkit-scrollbar { width: 8px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.2); border-radius: 4px; }
</style>
</head>
<body class="text-gray-100 h-screen overflow-hidden flex font-sans">
<aside class="w-72 glass-panel flex flex-col z-10 border-r border-white/10">
<div class="p-6 border-b border-white/10">
<h1 class="text-2xl font-black bg-clip-text text-transparent bg-gradient-to-r from-blue-400 to-purple-500">
<i class="fas fa-fingerprint text-blue-400 mr-2"></i>FinAudit
</h1>
<p class="text-xs text-blue-200 mt-1 uppercase tracking-widest">GenAI Auditor</p>
</div>
<div class="p-5 flex-1 overflow-y-auto">
<h2 class="text-sm font-bold text-gray-300 mb-4 uppercase tracking-wider"><i class="fas fa-database mr-2"></i>Custom Scenario Data</h2>
<div class="space-y-4">
<div>
<label class="block text-xs text-gray-400 mb-1">UI Headline</label>
<input type="text" id="customHeadline" value="Claim Your Bonus!" class="w-full bg-gray-900/50 border border-gray-700 rounded p-2 text-sm text-white focus:border-blue-500 focus:outline-none transition">
</div>
<div>
<label class="block text-xs text-gray-400 mb-1">Primary Button Text</label>
<input type="text" id="customBtn" value="Start Free Trial" class="w-full bg-gray-900/50 border border-gray-700 rounded p-2 text-sm text-white focus:border-blue-500 focus:outline-none transition">
</div>
<div>
<label class="block text-xs text-gray-400 mb-1">Fine Print (Deceptive Element)</label>
<textarea id="customFinePrint" rows="2" class="w-full bg-gray-900/50 border border-gray-700 rounded p-2 text-sm text-white focus:border-blue-500 focus:outline-none transition">By clicking, you agree to a $149/year auto-renewing subscription.</textarea>
</div>
<div>
<label class="block text-xs text-gray-400 mb-1">Target Dark Pattern</label>
<select id="customPatternType" class="w-full bg-gray-900/50 border border-gray-700 rounded p-2 text-sm text-white focus:border-blue-500 focus:outline-none transition">
<option value="subscription_trap">Subscription Trap (Hidden Cost)</option>
<option value="confirmshaming">Confirmshaming (Guilt Trip)</option>
<option value="urgency">Artificial Urgency (Timers)</option>
</select>
</div>
<button id="generateMockupBtn" class="w-full mt-4 bg-gray-800 hover:bg-gray-700 border border-gray-600 text-white px-4 py-2 rounded font-medium text-sm transition">
<i class="fas fa-sync-alt mr-2"></i>Render Custom UI
</button>
</div>
</div>
</aside>
<main class="flex-1 flex flex-col h-full relative">
<header class="h-16 glass-panel border-b border-white/10 flex items-center justify-between px-8 z-10">
<div class="flex space-x-2">
<span class="px-3 py-1 bg-green-900/40 text-green-400 border border-green-800 rounded-full text-xs font-mono">ViT Model: Online</span>
<span class="px-3 py-1 bg-purple-900/40 text-purple-400 border border-purple-800 rounded-full text-xs font-mono">CFPB Database: Synced</span>
</div>
<button id="runAuditBtn" class="bg-gradient-to-r from-blue-600 to-indigo-600 hover:from-blue-500 hover:to-indigo-500 text-white px-6 py-2 rounded-lg font-bold text-sm transition-all shadow-[0_0_15px_rgba(79,70,229,0.4)] flex items-center">
<i class="fas fa-crosshairs mr-2"></i> Execute XAI Scan
</button>
</header>
<div class="flex-1 flex p-8 gap-8 overflow-hidden items-center justify-center">
<div class="relative group">
<div class="absolute -inset-1 bg-gradient-to-r from-blue-600 to-purple-600 rounded-[3rem] blur opacity-25 group-hover:opacity-40 transition duration-1000"></div>
<div id="phoneMockup" class="w-[340px] h-[700px] bg-white text-black phone-bezel rounded-[2.5rem] border-[14px] border-black relative overflow-hidden transition-all">
<div class="dynamic-island"></div>
<div id="dynamicUI" class="h-full w-full flex flex-col pt-16 pb-10 px-6 bg-slate-50 relative z-10">
<div class="mb-auto mt-8 text-center">
<div class="w-16 h-16 bg-blue-100 text-blue-600 rounded-2xl flex items-center justify-center mx-auto mb-6 text-3xl shadow-sm"><i class="fas fa-gift"></i></div>
<h2 id="uiHeadline" class="text-2xl font-extrabold text-gray-900 mb-2 leading-tight">Claim Your Bonus!</h2>
<p class="text-gray-500 text-sm">Exclusive offer for new users today.</p>
</div>
<div class="mt-auto space-y-3 relative">
<div id="uiTimer" class="hidden text-center text-red-600 font-bold mb-4 animate-pulse">
Offer Expires in 04:59
</div>
<button id="uiBtn" class="w-full bg-blue-600 hover:bg-blue-700 text-white font-bold py-4 rounded-xl shadow-lg text-lg transition-transform active:scale-95">Start Free Trial</button>
<div class="text-center pt-2">
<p id="uiFinePrint" class="text-[10px] text-gray-400 leading-tight opacity-70">By clicking, you agree to a $149/year auto-renewing subscription.</p>
</div>
</div>
</div>
<div id="heatmapLayer" class="heatmap-layer"></div>
<div id="scanLaser" class="absolute top-0 left-0 w-full h-1 bg-blue-500 shadow-[0_0_15px_#3b82f6] z-50 hidden"></div>
</div>
</div>
<div class="w-[450px] h-[650px] glass-panel rounded-2xl flex flex-col shadow-2xl relative overflow-hidden">
<div class="p-5 border-b border-white/10 bg-black/20 flex justify-between items-center">
<h2 class="font-bold text-white"><i class="fas fa-brain mr-2 text-purple-400"></i>Audit Results</h2>
<div class="flex items-center space-x-2">
<span class="text-xs text-gray-400">Heatmap Overlay</span>
<label class="relative inline-flex items-center cursor-pointer">
<input type="checkbox" id="heatmapToggle" class="sr-only peer" checked disabled>
<div class="w-9 h-5 bg-gray-600 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-4 after:w-4 after:transition-all peer-checked:bg-purple-500"></div>
</label>
</div>
</div>
<div id="resultsContainer" class="flex-1 p-6 overflow-y-auto flex flex-col">
<div id="emptyState" class="flex-1 flex flex-col items-center justify-center text-center opacity-50">
<i class="fas fa-layer-group text-5xl mb-4 text-gray-500"></i>
<p class="text-sm">Configure your custom scenario on the left, then execute the XAI scan to analyze the UI structure.</p>
</div>
<div id="analysisOutput" class="hidden space-y-6">
<div class="bg-red-500/10 border border-red-500/30 rounded-xl p-4">
<div class="flex justify-between items-start mb-2">
<p class="text-xs text-red-200 uppercase font-bold tracking-wider">CFPB Violation Risk</p>
<span class="bg-red-500 text-white text-xs px-2 py-1 rounded font-bold animate-pulse">HIGH</span>
</div>
<p id="patternViolationName" class="text-xl font-black text-red-400">Subscription Trap Detected</p>
</div>
<div>
<h3 class="text-xs font-bold text-gray-400 uppercase tracking-widest mb-3 border-b border-gray-700 pb-2">Identified Anomalies</h3>
<div class="space-y-3">
<div class="bg-white/5 p-3 rounded-lg border border-white/10 border-l-4 border-l-red-500">
<span class="font-bold text-sm text-gray-200 block mb-1">Visual Obfuscation</span>
<p class="text-xs text-gray-400">Critical financial terms are rendered with insufficient contrast and microscopic font size relative to the primary call-to-action.</p>
</div>
<div class="bg-white/5 p-3 rounded-lg border border-white/10 border-l-4 border-l-yellow-500">
<span class="font-bold text-sm text-gray-200 block mb-1">Asymmetric Action</span>
<p class="text-xs text-gray-400">The action to accept is highly salient (large, colored button), while the mechanism to understand the cost is suppressed.</p>
</div>
</div>
</div>
<div class="bg-blue-900/20 border border-blue-500/30 rounded-xl p-4">
<h3 class="text-xs font-bold text-blue-300 uppercase tracking-widest mb-2"><i class="fas fa-network-wired mr-2"></i>Model Rationale</h3>
<p id="xaiExplanationText" class="text-xs text-blue-100/70 leading-relaxed">
The Vision Transformer (ViT) saliency map indicates a 94% attention skew towards the primary button. The financial disclosure text falls outside the primary visual hierarchy, triggering a heuristic flag for deceptive pattern structuring.
</p>
</div>
</div>
</div>
</div>
</div>
</main>
<script>
// DOM Elements
const customHeadline = document.getElementById('customHeadline');
const customBtn = document.getElementById('customBtn');
const customFinePrint = document.getElementById('customFinePrint');
const customPatternType = document.getElementById('customPatternType');
const generateMockupBtn = document.getElementById('generateMockupBtn');
const uiHeadline = document.getElementById('uiHeadline');
const uiBtn = document.getElementById('uiBtn');
const uiFinePrint = document.getElementById('uiFinePrint');
const uiTimer = document.getElementById('uiTimer');
const runAuditBtn = document.getElementById('runAuditBtn');
const phoneMockup = document.getElementById('phoneMockup');
const heatmapLayer = document.getElementById('heatmapLayer');
const scanLaser = document.getElementById('scanLaser');
const emptyState = document.getElementById('emptyState');
const analysisOutput = document.getElementById('analysisOutput');
const patternViolationName = document.getElementById('patternViolationName');
const xaiExplanationText = document.getElementById('xaiExplanationText');
const heatmapToggle = document.getElementById('heatmapToggle');
let currentPattern = 'subscription_trap';
// 1. Handle Generating Custom UI
generateMockupBtn.addEventListener('click', () => {
// Apply text
uiHeadline.textContent = customHeadline.value;
uiBtn.textContent = customBtn.value;
uiFinePrint.textContent = customFinePrint.value;
currentPattern = customPatternType.value;
// Handle specific UI logic based on pattern
if(currentPattern === 'urgency') {
uiTimer.classList.remove('hidden');
uiBtn.classList.replace('bg-blue-600', 'bg-red-600');
uiBtn.classList.replace('hover:bg-blue-700', 'hover:bg-red-700');
} else {
uiTimer.classList.add('hidden');
uiBtn.classList.replace('bg-red-600', 'bg-blue-600');
uiBtn.classList.replace('hover:bg-red-700', 'hover:bg-blue-700');
}
// Reset states
resetAuditState();
generateMockupBtn.innerHTML = '<i class="fas fa-check mr-2"></i>UI Rendered';
generateMockupBtn.classList.add('bg-green-600', 'border-green-500');
setTimeout(() => {
generateMockupBtn.innerHTML = '<i class="fas fa-sync-alt mr-2"></i>Render Custom UI';
generateMockupBtn.classList.remove('bg-green-600', 'border-green-500');
}, 1500);
});
// 2. Handle Running the Audit
runAuditBtn.addEventListener('click', () => {
resetAuditState();
// Start scan animation
scanLaser.style.display = 'block';
scanLaser.style.animation = 'scan 1.5s linear infinite';
runAuditBtn.innerHTML = '<i class="fas fa-spinner fa-spin mr-2"></i> Analyzing...';
runAuditBtn.disabled = true;
setTimeout(() => {
// Stop animation
scanLaser.style.display = 'none';
runAuditBtn.innerHTML = '<i class="fas fa-check mr-2"></i> Scan Complete';
runAuditBtn.disabled = false;
// Show Results
emptyState.classList.add('hidden');
analysisOutput.classList.remove('hidden');
heatmapToggle.disabled = false;
// Update text based on pattern
if(currentPattern === 'subscription_trap') {
patternViolationName.textContent = "Subscription Trap Detected";
xaiExplanationText.textContent = "The Vision Transformer (ViT) saliency map indicates a 94% attention skew towards the primary button. The financial disclosure text falls outside the primary visual hierarchy, triggering a heuristic flag for deceptive pattern structuring.";
generateHeatmap([{top: '80%', type: 'severe'}, {top: '92%', type: 'warn', width: '250px', height: '40px'}]);
} else if (currentPattern === 'confirmshaming') {
patternViolationName.textContent = "Confirmshaming Detected";
xaiExplanationText.textContent = "Linguistic analysis of the fine-print detects emotive, guilt-inducing phrasing designed to manipulate user agency. Saliency maps show severe visual imbalance favoring the affirmative action.";
generateHeatmap([{top: '80%', type: 'severe'}, {top: '92%', type: 'severe', width: '280px', height: '50px'}]);
} else if (currentPattern === 'urgency') {
patternViolationName.textContent = "Artificial Urgency Detected";
xaiExplanationText.textContent = "Heuristic scanner identified an active countdown timer paired with high-friction transaction elements. This violates transparency guidelines by artificially accelerating financial decision-making.";
generateHeatmap([{top: '68%', type: 'severe', width: '200px', height: '40px'}, {top: '82%', type: 'severe'}]);
}
// Activate Heatmap
phoneMockup.classList.add('heatmap-active');
}, 2000);
});
// Toggle heatmap
heatmapToggle.addEventListener('change', (e) => {
if(e.target.checked) phoneMockup.classList.add('heatmap-active');
else phoneMockup.classList.remove('heatmap-active');
});
// Helper: Generate Heatmap Spots
function generateHeatmap(spots) {
heatmapLayer.innerHTML = spots.map(s => `
<div class="hotspot ${s.type === 'severe' ? 'hotspot-severe' : 'hotspot-warn'}"
style="top: ${s.top}; left: 50%; width: ${s.width || '150px'}; height: ${s.height || '80px'};">
</div>
`).join('');
}
// Helper: Reset State
function resetAuditState() {
phoneMockup.classList.remove('heatmap-active');
heatmapLayer.innerHTML = '';
emptyState.classList.remove('hidden');
analysisOutput.classList.add('hidden');
heatmapToggle.checked = true;
heatmapToggle.disabled = true;
runAuditBtn.innerHTML = '<i class="fas fa-crosshairs mr-2"></i> Execute XAI Scan';
// CSS Animation dynamically added for scanner
const style = document.createElement('style');
style.innerHTML = `
@keyframes scan {
0% { top: 0%; opacity: 0; }
10% { opacity: 1; }
90% { opacity: 1; }
100% { top: 100%; opacity: 0; }
}
`;
document.head.appendChild(style);
}
</script>
</body>
</html>