About this snippet

Lock any content behind a social share with this modern, dependency-free snippet: it blurs the locked content, opens the share dialog in a popup, unlocks when the window closes, and remembers the unlock with localStorage.

Size
11.6 KB
Uses
HTML, CSS, JavaScript
Loads
Nothing external
Views
419
<div class="locker" data-locker="css-cheat-sheet">
    <!-- The content you want to lock goes in here -->
    <div class="locker-content">
        <h3>The Complete CSS Cheat Sheet (PDF)</h3>
        <p>Every selector, property, and shorthand on two printable pages. Keep it next to your keyboard and stop googling flexbox.</p>
        <a class="download-button" href="css-cheat-sheet.pdf" download>Download the PDF</a>
    </div>
    <!-- The overlay visitors see while the content is locked -->
    <div class="locker-overlay">
        <svg class="locker-icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M12,17C10.89,17 10,16.1 10,15C10,13.89 10.89,13 12,13A2,2 0 0,1 14,15A2,2 0 0,1 12,17M18,20V10H6V20H18M18,8A2,2 0 0,1 20,10V20A2,2 0 0,1 18,22H6C4.89,22 4,21.1 4,20V10C4,8.89 4.89,8 6,8H7V6A5,5 0 0,1 12,1A5,5 0 0,1 17,6V8H18M12,3A3,3 0 0,0 9,6V8H15V6A3,3 0 0,0 12,3Z"/></svg>
        <p class="locker-title">This content is locked</p>
        <p class="locker-hint">Share this page on any network below to unlock the download instantly.</p>
        <!-- Icons: Font Awesome Free 6 (fontawesome.com) - CC BY 4.0 -->
        <div class="locker-share">
            <a href="#" data-share="x" aria-label="Share on X">
                <svg viewBox="0 0 512 512" aria-hidden="true"><path d="M389.2 48h70.6L305.6 224.2 487 464H345L233.7 318.6 106.5 464H35.8L200.7 275.5 26.8 48H172.4L272.9 180.9 389.2 48zM364.4 421.8h39.1L151.1 88h-42L364.4 421.8z"/></svg>
            </a>
            <a href="#" data-share="facebook" aria-label="Share on Facebook">
                <svg viewBox="0 0 320 512" aria-hidden="true"><path d="M80 299.3V512H196V299.3h86.5l18-97.8H196V166.9c0-51.7 20.3-71.5 72.7-71.5c16.3 0 29.4 .4 37 1.2V7.9C291.4 4 256.4 0 236.2 0C129.3 0 80 50.5 80 159.4v42.1H14v97.8H80z"/></svg>
            </a>
            <a href="#" data-share="linkedin" aria-label="Share on LinkedIn">
                <svg viewBox="0 0 448 512" aria-hidden="true"><path d="M100.28 448H7.4V148.9h92.88zM53.79 108.1C24.09 108.1 0 83.5 0 53.8a53.79 53.79 0 0 1 107.58 0c0 29.7-24.1 54.3-53.79 54.3zM447.9 448h-92.68V302.4c0-34.7-.7-79.2-48.29-79.2-48.29 0-55.69 37.7-55.69 76.7V448h-92.78V148.9h89.08v40.8h1.3c12.4-23.5 42.69-48.3 87.88-48.3 94 0 111.28 61.9 111.28 142.3V448z"/></svg>
            </a>
            <a href="#" data-share="pinterest" aria-label="Share on Pinterest">
                <svg viewBox="0 0 384 512" aria-hidden="true"><path d="M204 6.5C101.4 6.5 0 74.9 0 185.6 0 256 39.6 296 63.6 296c9.9 0 15.6-27.6 15.6-35.4 0-9.3-23.7-29.1-23.7-67.8 0-80.4 61.2-137.4 140.4-137.4 68.1 0 118.5 38.7 118.5 109.8 0 53.1-21.3 152.7-90.3 152.7-24.9 0-46.2-18-46.2-43.8 0-37.8 26.4-74.4 26.4-113.4 0-66.2-93.9-54.2-93.9 25.8 0 16.8 2.1 35.4 9.6 50.7-13.8 59.4-42 147.9-42 209.1 0 18.9 2.7 37.5 4.5 56.4 3.4 3.8 1.7 3.4 6.9 1.5 50.4-69 48.6-82.5 71.4-172.8 12.3 23.4 44.1 36 69.3 36 106.2 0 153.9-103.5 153.9-196.8C384 71.3 298.2 6.5 204 6.5z"/></svg>
            </a>
            <a href="#" data-share="reddit" aria-label="Share on Reddit">
                <svg viewBox="0 0 512 512" aria-hidden="true"><path d="M373 138.6c-25.2 0-46.3-17.5-51.9-41l0 0c-30.6 4.3-54.2 30.7-54.2 62.4l0 .2c47.4 1.8 90.6 15.1 124.9 36.3c12.6-9.7 28.4-15.5 45.5-15.5c41.3 0 74.7 33.4 74.7 74.7c0 29.8-17.4 55.5-42.7 67.5c-2.4 86.8-97 156.6-213.2 156.6S45.5 410.1 43 323.4C17.6 311.5 0 285.7 0 255.7c0-41.3 33.4-74.7 74.7-74.7c17.2 0 33 5.8 45.7 15.6c34-21.1 76.8-34.4 123.7-36.4l0-.3c0-44.3 33.7-80.9 76.8-85.5C325.8 50.2 347.2 32 373 32c29.4 0 53.3 23.9 53.3 53.3s-23.9 53.3-53.3 53.3zM157.5 255.3c-20.9 0-38.9 20.8-40.2 47.9s17.1 38.1 38 38.1s36.6-9.8 37.8-36.9s-14.7-49.1-35.7-49.1zM395 303.1c-1.2-27.1-19.2-47.9-40.2-47.9s-36.9 22-35.7 49.1c1.2 27.1 16.9 36.9 37.8 36.9s39.3-11 38-38.1zm-60.1 70.8c1.5-3.6-1-7.7-4.9-8.1c-23-2.3-47.9-3.6-73.8-3.6s-50.8 1.3-73.8 3.6c-3.9 .4-6.4 4.5-4.9 8.1c12.9 30.8 43.3 52.4 78.7 52.4s65.8-21.6 78.7-52.4z"/></svg>
            </a>
            <a href="#" data-share="whatsapp" aria-label="Share on WhatsApp">
                <svg viewBox="0 0 448 512" aria-hidden="true"><path d="M380.9 97.1C339 55.1 283.2 32 223.9 32c-122.4 0-222 99.6-222 222 0 39.1 10.2 77.3 29.6 111L0 480l117.7-30.9c32.4 17.7 68.9 27 106.1 27h.1c122.3 0 224.1-99.6 224.1-222 0-59.3-25.2-115-67.1-157zm-157 341.6c-33.2 0-65.7-8.9-94-25.7l-6.7-4-69.8 18.3L72 359.2l-4.4-7c-18.5-29.4-28.2-63.3-28.2-98.2 0-101.7 82.8-184.5 184.6-184.5 49.3 0 95.6 19.2 130.4 54.1 34.8 34.9 56.2 81.2 56.1 130.5 0 101.8-84.9 184.6-186.6 184.6zm101.2-138.2c-5.5-2.8-32.8-16.2-37.9-18-5.1-1.9-8.8-2.8-12.5 2.8-3.7 5.6-14.3 18-17.6 21.8-3.2 3.7-6.5 4.2-12 1.4-32.6-16.3-54-29.1-75.5-66-5.7-9.8 5.7-9.1 16.3-30.3 1.8-3.7.9-6.9-.5-9.7-1.4-2.8-12.5-30.1-17.1-41.2-4.5-10.8-9.1-9.3-12.5-9.5-3.2-.2-6.9-.2-10.6-.2-3.7 0-9.7 1.4-14.8 6.9-5.1 5.6-19.4 19-19.4 46.3 0 27.3 19.9 53.7 22.6 57.4 2.8 3.7 39.1 59.7 94.8 83.8 35.2 15.2 49 16.5 66.6 13.9 10.7-1.6 32.8-13.4 37.4-26.4 4.6-13 4.6-24.1 3.2-26.4-1.3-2.5-5-3.9-10.5-6.6z"/></svg>
            </a>
        </div>
    </div>
</div>
/* Demo page styling */
body {
    margin: 0;
    min-height: 100vh;
    display: grid;
    place-items: center;
    font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
    background-color: #f1f5f9;
}
.locker-content h3 {
    margin: 0 0 10px;
    font-size: 22px;
    color: #0f172a;
}
.locker-content p {
    margin: 0 0 20px;
    font-size: 15px;
    line-height: 1.6;
    color: #475569;
}
.download-button {
    display: inline-block;
    padding: 12px 22px;
    font-weight: 700;
    text-decoration: none;
    color: #fff;
    background-color: #3e7bd6;
    border-radius: 8px;
}
.download-button:hover {
    background-color: #3069c2;
}

/* The content locker component */
/* Every knob you might want to tweak lives in one place */
.locker {
    --locker-radius: 12px;
    --locker-blur: 8px;
    --locker-speed: .4s;
    --locker-accent: #3e7bd6;
    --locker-overlay: linear-gradient(160deg, rgba(30, 41, 59, .92), rgba(15, 23, 42, .96));
    --locker-button: 44px;
    position: relative;
    max-width: 520px;
    border-radius: var(--locker-radius);
    overflow: hidden;
    box-shadow: 0 4px 24px rgba(15, 23, 42, .12);
}
/* The content being locked; the blur fades away smoothly on unlock */
.locker-content {
    padding: 30px;
    background-color: #fff;
    transition: filter var(--locker-speed) ease;
}
/* Blur the content and block clicks and text selection while it is locked */
.locker:not(.unlocked) .locker-content {
    filter: blur(var(--locker-blur));
    pointer-events: none;
    user-select: none;
}
/* The dark overlay that sits on top of the locked content */
.locker-overlay {
    position: absolute;
    inset: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 6px;
    padding: 20px;
    text-align: center;
    color: #fff;
    background: var(--locker-overlay);
    transition: opacity var(--locker-speed) ease, visibility var(--locker-speed);
}
/* Fade the overlay away once the locker is unlocked */
.locker.unlocked .locker-overlay {
    opacity: 0;
    visibility: hidden;
}
.locker-icon {
    width: 34px;
    height: 34px;
    fill: #94a3b8;
}
.locker-title {
    margin: 4px 0 0;
    font-size: 20px;
    font-weight: 700;
}
.locker-hint {
    margin: 0 0 10px;
    font-size: 14px;
    line-height: 1.5;
    color: #cbd5e1;
}
/* The row of round share buttons; each button reads its own --brand color */
.locker-share {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 10px;
}
.locker-share a {
    display: flex;
    align-items: center;
    justify-content: center;
    width: var(--locker-button);
    height: var(--locker-button);
    border-radius: 50%;
    background-color: var(--brand);
    transition: transform .15s ease;
}
.locker-share a:hover,
.locker-share a:focus-visible {
    transform: scale(1.12);
}
.locker-share svg {
    width: 18px;
    height: 18px;
    fill: #fff;
}
/* The official brand color for each network */
[data-share=x] { --brand: #000000; }
[data-share=facebook] { --brand: #0866ff; }
[data-share=linkedin] { --brand: #0a66c2; }
[data-share=pinterest] { --brand: #e60023; }
[data-share=reddit] { --brand: #ff4500; }
[data-share=whatsapp] { --brand: #25d366; }
/* The email form used by the email signup variant */
.locker-form {
    display: flex;
    gap: 8px;
    width: 100%;
    max-width: 340px;
}
.locker-form input {
    flex: 1;
    min-width: 0;
    padding: 10px 14px;
    font-size: 14px;
    color: #fff;
    background-color: #1e293b;
    border: 1px solid #475569;
    border-radius: 6px;
}
.locker-form input::placeholder {
    color: #94a3b8;
}
.locker-form button {
    padding: 10px 18px;
    font-size: 14px;
    font-weight: 700;
    color: #fff;
    background-color: var(--locker-accent);
    border: 0;
    border-radius: 6px;
    cursor: pointer;
}
.locker-form button:hover {
    filter: brightness(.9);
}
/* Keep things calm for visitors who prefer reduced motion */
@media (prefers-reduced-motion: reduce) {
    .locker-content, .locker-overlay, .locker-share a {
        transition: none;
    }
}
// The share dialog URL for every supported network; {url} becomes the page's address
const networks = {
    x: 'https://x.com/intent/post?url={url}',
    facebook: 'https://www.facebook.com/sharer/sharer.php?u={url}',
    linkedin: 'https://www.linkedin.com/sharing/share-offsite/?url={url}',
    pinterest: 'https://pinterest.com/pin/create/button/?url={url}',
    reddit: 'https://www.reddit.com/submit?url={url}',
    whatsapp: 'https://wa.me/?text={url}'
};
// Read or save an unlock flag; storage can be blocked in private or embedded pages
const remember = (key, value) => {
    try { return value ? localStorage.setItem(key, value) : localStorage.getItem(key); } catch (e) { return null; }
};
// Reveal the content and remember the unlock so it stays open on return visits
const unlock = locker => {
    locker.classList.add('unlocked');
    locker.querySelector('.locker-content').inert = false;
    remember('locker_' + locker.dataset.locker, 'unlocked');
};
// Set up every locker on the page
document.querySelectorAll('.locker').forEach(locker => {
    // Keep clicks and keyboard focus out of the content while it is locked
    locker.querySelector('.locker-content').inert = true;
    // Unlocked on a previous visit? Reveal it immediately and skip the rest
    if (remember('locker_' + locker.dataset.locker)) return unlock(locker);
    // Unlock by sharing: one click handler per share button
    locker.querySelectorAll('[data-share]').forEach(button => button.addEventListener('click', event => {
        event.preventDefault();
        // Build the share link for the clicked network with this page's URL filled in
        const url = networks[button.dataset.share].replace('{url}', encodeURIComponent(location.href));
        // Open the network's share dialog in a small centered popup window
        const popup = window.open(url, 'share', 'popup,width=600,height=600,left=' + (screen.width - 600) / 2 + ',top=' + (screen.height - 600) / 2);
        // Popup blocked? Fall back to a new tab and unlock after a short delay
        if (!popup) { window.open(url, '_blank'); setTimeout(() => unlock(locker), 5000); return; }
        // Watch the popup; once it closes, the share is done and we can unlock
        const watcher = setInterval(() => {
            if (popup.closed) { clearInterval(watcher); unlock(locker); }
        }, 500);
    }));
    // Unlock by email: used by the email signup variant if the form exists
    const form = locker.querySelector('.locker-form');
    if (form) form.addEventListener('submit', event => {
        event.preventDefault();
        // Send the address to your newsletter system here, then reveal the content
        unlock(locker);
    });
});
Esc
↑↓ moveEnter select

Start from a template

Open an .html file, or HTML, CSS, JS, Markdown, Less or TypeScript files. You can also drop them here. Styles and scripts go into their panes.

Share

Source

Keyboard shortcuts

Save