<script src="https://cdn.tailwindcss.com"></script> <script type="module" src="https://unpkg.com/ionicons@7.1.0/dist/ionicons/ionicons.esm.js"></script> <script nomodule src="https://unpkg.com/ionicons@7.1.0/dist/ionicons/ionicons.js"></script> <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet"> <body class="min-h-screen flex flex-col"> <!-- Navbar --> <nav class="w-full bg-white border-b border-slate-200 sticky top-0 z-50"> <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"> <div class="flex justify-between h-16"> <div class="flex items-center gap-2"> <div class="w-8 h-8 bg-indigo-600 rounded-lg flex items-center justify-center text-white"> <ion-icon name="layers-outline"></ion-icon> </div> <span class="font-bold text-lg text-slate-800 tracking-tight">CSS Masterclass</span> </div> <div class="flex items-center gap-4"> <a href="#" class="text-slate-500 hover:text-indigo-600 transition-colors text-sm font-medium">Docs</a> <a href="#" class="text-slate-500 hover:text-indigo-600 transition-colors text-sm font-medium">Examples</a> <a href="https://github.com" target="_blank" class="text-slate-400 hover:text-slate-800 transition-colors"> <ion-icon name="logo-github" class="text-xl"></ion-icon> </a> </div> </div> </div> </nav> <!-- Main Content --> <main class="flex-grow max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-12 w-full"> <!-- Header Section --> <div class="text-center mb-16"> <h1 class="text-4xl md:text-5xl font-extrabold text-slate-900 mb-4 tracking-tight"> Background Image Opacity </h1> <p class="text-lg text-slate-600 max-w-2xl mx-auto"> The definitive guide to handling background transparency without affecting your text content. </p> </div> <!-- Educational Grid: The Problem vs The Solution --> <div class="grid grid-cols-1 md:grid-cols-2 gap-8 mb-20"> <!-- The Wrong Way --> <div class="bg-white rounded-2xl shadow-sm border border-slate-200 overflow-hidden group hover:shadow-md transition-shadow duration-300"> <div class="p-6 border-b border-slate-100 flex justify-between items-center bg-red-50/50"> <h3 class="font-semibold text-red-600 flex items-center gap-2"> <ion-icon name="close-circle"></ion-icon> The Wrong Way </h3> <span class="text-xs font-mono bg-white px-2 py-1 rounded border border-slate-200">opacity: 0.5</span> </div> <div class="relative h-64 w-full bg-slate-900 flex items-center justify-center"> <!-- Using standard opacity property on the container --> <div class="w-full h-full flex flex-col items-center justify-center bg-cover bg-center" style="background-image: url('https://images.unsplash.com/photo-1497366216548-37526070297c?auto=format&fit=crop&w=800&q=80'); opacity: 0.4;"> <h2 class="text-3xl font-bold text-white">Unreadable Text</h2> <p class="text-white mt-2">Everything gets faded</p> </div> </div> <div class="p-6"> <p class="text-sm text-slate-600"> Setting <code class="bg-slate-100 px-1 rounded text-slate-800">opacity</code> on the container fades <strong>everything</strong>, including your text and buttons. </p> </div> </div> <!-- The Right Way --> <div class="bg-white rounded-2xl shadow-sm border border-slate-200 overflow-hidden group hover:shadow-md transition-shadow duration-300"> <div class="p-6 border-b border-slate-100 flex justify-between items-center bg-green-50/50"> <h3 class="font-semibold text-green-600 flex items-center gap-2"> <ion-icon name="checkmark-circle"></ion-icon> The Right Way </h3> <span class="text-xs font-mono bg-white px-2 py-1 rounded border border-slate-200">::before</span> </div> <div class="relative h-64 w-full bg-slate-900 flex items-center justify-center bg-opacity-container demo-static"> <div class="relative z-10 text-center"> <h2 class="text-3xl font-bold text-white drop-shadow-md">Crystal Clear</h2> <p class="text-white mt-2 font-medium drop-shadow-md">Only background is faded</p> </div> <!-- Pseudo-element logic applied via CSS class .bg-opacity-container --> </div> <div class="p-6"> <p class="text-sm text-slate-600"> Using a <code class="bg-slate-100 px-1 rounded text-slate-800">::before</code> pseudo-element lets you control image opacity independently of the content. </p> </div> </div> </div> <!-- Interactive Generator --> <div class="bg-white rounded-2xl shadow-xl border border-slate-200 overflow-hidden"> <div class="grid grid-cols-1 lg:grid-cols-12"> <!-- Controls & Code --> <div class="lg:col-span-5 bg-slate-50 p-8 border-r border-slate-200 flex flex-col gap-6"> <div> <h2 class="text-xl font-bold text-slate-800 mb-1">Generator</h2> <p class="text-sm text-slate-500">Tweak settings to generate your CSS.</p> </div> <!-- Slider --> <div class="space-y-3"> <div class="flex justify-between text-sm font-medium text-slate-700"> <label for="opacityRange">Image Opacity</label> <span id="opacityValue">0.5</span> </div> <input type="range" id="opacityRange" min="0" max="1" step="0.05" value="0.5"> </div> <!-- Backdrop Color --> <div class="space-y-3"> <div class="flex justify-between text-sm font-medium text-slate-700"> <label>Backdrop Color</label> <span id="colorValue" class="uppercase text-xs text-slate-400">#000000</span> </div> <div class="flex gap-3"> <button class="w-8 h-8 rounded-full bg-black border-2 border-indigo-500 ring-2 ring-indigo-200" onclick="changeColor('#000000', this)"></button> <button class="w-8 h-8 rounded-full bg-indigo-900 border-2 border-transparent hover:border-slate-300" onclick="changeColor('#312e81', this)"></button> <button class="w-8 h-8 rounded-full bg-slate-800 border-2 border-transparent hover:border-slate-300" onclick="changeColor('#1e293b', this)"></button> <button class="w-8 h-8 rounded-full bg-white border-2 border-gray-300 hover:border-indigo-500" onclick="changeColor('#ffffff', this)"></button> </div> </div> <!-- Generated Code --> <div class="bg-slate-900 rounded-lg p-4 font-mono text-xs leading-relaxed overflow-x-auto relative group mt-auto shadow-inner"> <button onclick="copyCode()" class="absolute top-3 right-3 p-1.5 bg-slate-700 text-white rounded hover:bg-indigo-600 transition-colors opacity-0 group-hover:opacity-100" title="Copy to Clipboard"> <ion-icon name="copy-outline"></ion-icon> </button> <pre><span class="code-token-sel">.container</span> <span class="code-token-punc">{</span> <span class="code-token-prop">position</span><span class="code-token-punc">:</span> <span class="code-token-val">relative</span><span class="code-token-punc">;</span> <span class="code-token-prop">background-color</span><span class="code-token-punc">:</span> <span class="code-token-val" id="codeBgColor">#000000</span><span class="code-token-punc">;</span> <span class="code-token-punc">}</span> <span class="code-token-sel">.container::before</span> <span class="code-token-punc">{</span> <span class="code-token-prop">content</span><span class="code-token-punc">:</span> <span class="code-token-val">""</span><span class="code-token-punc">;</span> <span class="code-token-prop">position</span><span class="code-token-punc">:</span> <span class="code-token-val">absolute</span><span class="code-token-punc">;</span> <span class="code-token-prop">inset</span><span class="code-token-punc">:</span> <span class="code-token-val">0</span><span class="code-token-punc">;</span> <span class="code-token-prop">background-image</span><span class="code-token-punc">:</span> <span class="code-token-val">url(...)</span><span class="code-token-punc">;</span> <span class="code-token-prop">background-size</span><span class="code-token-punc">:</span> <span class="code-token-val">cover</span><span class="code-token-punc">;</span> <span class="code-token-prop">opacity</span><span class="code-token-punc">:</span> <span class="code-token-val" id="codeOpacity">0.5</span><span class="code-token-punc">;</span> <span class="code-token-punc">}</span></pre> </div> </div> <!-- Live Preview --> <div class="lg:col-span-7 relative h-96 lg:h-auto"> <!-- This div represents the container background color --> <div id="previewContainer" class="absolute inset-0 bg-black transition-colors duration-300 overflow-hidden flex items-center justify-center"> <!-- This represents the pseudo element --> <div id="previewImage" class="absolute inset-0 bg-cover bg-center transition-opacity duration-75" style="background-image: url('https://images.unsplash.com/photo-1497215728101-856f4ea42174?ixlib=rb-1.2.1&auto=format&fit=crop&w=1950&q=80'); opacity: 0.5;"> </div> <!-- Content --> <div class="relative z-10 text-center px-6"> <div class="inline-flex items-center justify-center w-12 h-12 rounded-full bg-white/10 backdrop-blur-md text-white border border-white/20 mb-4 shadow-lg"> <ion-icon name="rocket" class="text-xl"></ion-icon> </div> <h2 id="previewTitle" class="text-4xl font-bold text-white mb-2 drop-shadow-lg tracking-tight">Professional Design</h2> <p id="previewText" class="text-white/90 text-lg max-w-md mx-auto drop-shadow-md"> Content remains perfectly legible while the background image fades into the color of your choice. </p> <div class="mt-8"> <button class="px-6 py-2.5 bg-indigo-600 hover:bg-indigo-700 text-white rounded-lg font-medium shadow-lg shadow-indigo-600/30 transition-all transform hover:-translate-y-0.5"> Call to Action </button> </div> </div> </div> </div> </div> </div> <!-- Simple Footer --> <footer class="mt-12 text-center text-slate-400 text-sm"> <p>Minimal Implementation © 2023. Built with HTML, CSS, & Tailwind.</p> </footer> </main>
body { font-family: 'Inter', sans-serif; background-color: #f8fafc; color: #1e293b; } /* Custom Range Slider Styling */ input[type=range] { -webkit-appearance: none; width: 100%; background: transparent; } input[type=range]::-webkit-slider-thumb { -webkit-appearance: none; height: 20px; width: 20px; border-radius: 50%; background: #4f46e5; cursor: pointer; margin-top: -8px; box-shadow: 0 2px 6px rgba(79, 70, 229, 0.4); transition: transform 0.1s; } input[type=range]::-webkit-slider-thumb:hover { transform: scale(1.1); } input[type=range]::-webkit-slider-runnable-track { width: 100%; height: 4px; cursor: pointer; background: #e2e8f0; border-radius: 2px; } /* Syntax Highlighting Simulation */ .code-token-prop { color: #0ea5e9; } .code-token-val { color: #a5b4fc; } .code-token-sel { color: #f472b6; } .code-token-punc { color: #94a3b8; } .code-token-comment { color: #64748b; font-style: italic; } /* The "Right Way" Technique Class */ .bg-opacity-container { position: relative; overflow: hidden; /* Ensures the pseudo-element doesn't spill out */ isolation: isolate; /* Creates a new stacking context */ } .bg-opacity-container::before { content: ""; position: absolute; top: 0; left: 0; width: 100%; height: 100%; background-image: url('https://images.unsplash.com/photo-1497366216548-37526070297c?ixlib=rb-1.2.1&auto=format&fit=crop&w=1950&q=80'); background-size: cover; background-position: center; z-index: -1; /* Places it behind the content */ transition: opacity 0.3s ease, background-color 0.3s ease; } <!-- Injecting styles for the static demo specifically --> .demo-static::before { opacity: 0.5; }
// Interactive Logic const rangeInput = document.getElementById('opacityRange'); const opacityValueDisplay = document.getElementById('opacityValue'); const previewImage = document.getElementById('previewImage'); const previewContainer = document.getElementById('previewContainer'); // Code spans const codeOpacity = document.getElementById('codeOpacity'); const codeBgColor = document.getElementById('codeBgColor'); // Text Color Logic (if white background, dark text) const previewTitle = document.getElementById('previewTitle'); const previewText = document.getElementById('previewText'); // Update Opacity rangeInput.addEventListener('input', (e) => { const val = e.target.value; opacityValueDisplay.textContent = val; previewImage.style.opacity = val; codeOpacity.textContent = val; }); // Update Background Color function changeColor(color, btn) { previewContainer.style.backgroundColor = color; codeBgColor.textContent = color; // Reset borders const buttons = btn.parentElement.querySelectorAll('button'); buttons.forEach(b => { b.classList.remove('border-indigo-500', 'ring-2', 'ring-indigo-200'); b.classList.add('border-transparent'); if(b.style.backgroundColor === 'white') b.classList.add('border-gray-300'); }); // Active state btn.classList.remove('border-transparent', 'border-gray-300'); btn.classList.add('border-indigo-500', 'ring-2', 'ring-indigo-200'); // Adjust Text Color for contrast if (color === '#ffffff') { previewTitle.classList.remove('text-white'); previewTitle.classList.add('text-slate-900'); previewText.classList.remove('text-white/90'); previewText.classList.add('text-slate-600'); } else { previewTitle.classList.add('text-white'); previewTitle.classList.remove('text-slate-900'); previewText.classList.add('text-white/90'); previewText.classList.remove('text-slate-600'); } } // Copy Code functionality function copyCode() { const codeText = ` .container { position: relative; background-color: ${codeBgColor.textContent}; } .container::before { content: ""; position: absolute; inset: 0; background-image: url(...); background-size: cover; opacity: ${codeOpacity.textContent}; }`; navigator.clipboard.writeText(codeText.trim()).then(() => { const btn = document.querySelector('button[onclick="copyCode()"]'); const originalHtml = btn.innerHTML; btn.innerHTML = '<ion-icon name="checkmark-outline"></ion-icon>'; setTimeout(() => btn.innerHTML = originalHtml, 2000); }); }
Login to leave a comment
No comments yet. Be the first!
View Project
No comments yet. Be the first!