|

CSS 44 💻 scripting, update and video-dynamic-range media features

These three media features let you adapt styles based on JavaScript availability, display refresh capability, and HDR video support.


Overview of Features

FeatureDescriptionValues
scriptingDetects JavaScript availabilitynone, initial-only, enabled
updateHow often the display can updatenone, slow, fast
video-dynamic-rangeDynamic range of the video planestandard, high

1. scripting

The scripting media feature detects whether JavaScript is available in the browser.

@media (scripting: enabled) {
    .enabled {
        color: red;
        font-weight: bold;
    }
}

Values

ValueDescription
noneScripting is not available (JavaScript disabled)
initial-onlyScripting available only during initial page load
enabledScripting is fully supported (default)

When to Use

  • Provide fallbacks for users with JavaScript disabled
  • Show/hide JavaScript-dependent UI elements
  • Style progressive enhancement layers
  • Display noscript alternatives with CSS instead of <noscript>

How Users Disable JavaScript

  • Chrome: Settings → Privacy → Site Settings → JavaScript → Blocked
  • Firefox: about:config → javascript.enabled → false
  • Safari: Develop → Disable JavaScript
  • Brave: Shields → Scripts blocked

Key Points:

  • enabled is the default in modern browsers
  • none means no JavaScript at all — plan for progressive enhancement
  • initial-only is rare (some email clients, print preview)
  • Use this to style fallback content instead of relying on <noscript>

2. update

The update media feature checks how frequently the device can update rendered content — essentially, its refresh capability.

@media (update: fast) {
    p {
        animation: slide 1s infinite alternate;
    }
}

Values

ValueDescriptionDevice Example
noneContent cannot be updated after renderingPrint, e-ink (static)
slowUpdates are possible but not fast enough for smooth animationE-ink readers, some low-power devices
fastContent can be updated quickly for smooth animationModern screens (60Hz+)

When to Use

Update ValueRecommendation
noneDisable all animations and transitions
slowUse slow, infrequent animations only
fastEnable smooth animations and transitions

Key Points:

  • fast is the default on modern displays
  • slow devices (e-ink) can’t handle smooth animation
  • none means the content is static after rendering
  • Use this to conditionally enable animations based on device capability

Practical Example

/* No animation on static devices */
@media (update: none) {
    p {
        animation: none;
    }
}

/* Slow animation on e-ink */
@media (update: slow) {
    p {
        animation: slide 5s infinite alternate;
    }
}

/* Fast animation on modern screens */
@media (update: fast) {
    p {
        animation: slide 1s infinite alternate;
    }
}

3. video-dynamic-range

The video-dynamic-range media feature queries the dynamic range of the video plane — specifically for HDR video content.

@media (video-dynamic-range: high) {
    h1 {
        color: blue;
    }
}

Values

ValueDescription
standardStandard Dynamic Range (SDR) — most displays
highHigh Dynamic Range (HDR) — supports HDR video

How It Differs from dynamic-range

FeatureTarget
dynamic-rangeThe whole output device (display)
video-dynamic-rangeThe video plane specifically

Key Points:

  • video-dynamic-range is for video content (HDR video playback)
  • dynamic-range is for the display itself
  • A device may support HDR video but not have a high-dynamic-range display
  • Limited browser support — always provide a standard fallback

HDR Requirements

RequirementMinimum
Color depth> 24-bit
Peak brightnessHigh (1000+ nits)
Contrast ratioHigh

Complete Example

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>scripting, update, and video-dynamic-range</title>
    <style>
        *, *::before, *::after {
            box-sizing: border-box;
        }

        :root {
            --bg-primary: #ffffff;
            --bg-secondary: #f8f9fa;
            --bg-tertiary: #e9ecef;
            --text-primary: #212529;
            --text-secondary: #6c757d;
            --accent: #007bff;
            --border: #dee2e6;
            --shadow: rgba(0, 0, 0, 0.1);
        }

        @media (prefers-color-scheme: dark) {
            :root {
                --bg-primary: #1a1a1a;
                --bg-secondary: #0d0d0d;
                --bg-tertiary: #2d2d2d;
                --text-primary: #f8f9fa;
                --text-secondary: #adb5bd;
                --accent: #4dabf7;
                --border: #444;
                --shadow: rgba(0, 0, 0, 0.5);
            }
        }

        body {
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            padding: 20px;
            background: var(--bg-secondary);
            color: var(--text-primary);
            max-width: 1200px;
            margin: 0 auto;
            line-height: 1.6;
            transition: background 0.3s, color 0.3s;
        }

        h1 {
            color: var(--accent);
            border-bottom: 3px solid var(--accent);
            padding-bottom: 10px;
        }

        h2 {
            color: var(--accent);
            border-left: 4px solid var(--accent);
            padding-left: 15px;
            margin-top: 30px;
        }

        h3 {
            color: var(--text-primary);
            margin-top: 20px;
        }

        section {
            background: var(--bg-primary);
            padding: 20px;
            border-radius: 8px;
            box-shadow: 0 2px 10px var(--shadow);
            margin: 20px 0;
            transition: all 0.3s;
        }

        .code-block {
            background: #1e1e1e;
            color: #d4d4d4;
            padding: 15px;
            border-radius: 8px;
            overflow-x: auto;
            font-family: 'Courier New', monospace;
            font-size: 0.9rem;
            line-height: 1.8;
            margin: 10px 0;
        }

        .reference-table {
            width: 100%;
            border-collapse: collapse;
            margin: 15px 0;
        }

        .reference-table th,
        .reference-table td {
            padding: 10px;
            border: 1px solid var(--border);
            text-align: left;
        }

        .reference-table th {
            background: var(--accent);
            color: white;
        }

        .reference-table tr:nth-child(even) {
            background: var(--bg-tertiary);
        }

        .note {
            font-size: 0.9rem;
            color: var(--text-secondary);
            margin-top: 5px;
        }

        .highlight {
            background: #ffc107;
            color: #333;
            padding: 2px 6px;
            border-radius: 4px;
            font-weight: bold;
        }

        /* ====== SCRIPTING DEMO ====== */
        .scripting-demo {
            padding: 20px;
            border-radius: 8px;
            margin: 15px 0;
            border: 2px solid var(--border);
            background: var(--bg-tertiary);
            text-align: center;
            font-weight: bold;
            font-size: 1.1rem;
            transition: all 0.3s;
        }

        /* No scripting */
        @media (scripting: none) {
            .scripting-demo {
                background: #dc3545;
                color: white;
                border-color: #a71d2a;
            }
            .scripting-demo::after {
                content: " (scripting disabled)";
                font-weight: normal;
                font-size: 0.85rem;
            }
        }

        /* Initial-only scripting */
        @media (scripting: initial-only) {
            .scripting-demo {
                background: #ffc107;
                color: #333;
                border-color: #d39e00;
            }
            .scripting-demo::after {
                content: " (scripting available only on initial load)";
                font-weight: normal;
                font-size: 0.85rem;
            }
        }

        /* Full scripting */
        @media (scripting: enabled) {
            .scripting-demo {
                background: #28a745;
                color: white;
                border-color: #1e7e34;
            }
            .scripting-demo::after {
                content: " (scripting fully enabled)";
                font-weight: normal;
                font-size: 0.85rem;
            }
        }

        /* ====== UPDATE DEMO ====== */
        .update-demo {
            padding: 20px;
            border-radius: 8px;
            margin: 15px 0;
            border: 2px solid var(--border);
            background: var(--bg-tertiary);
            text-align: center;
            font-weight: bold;
            font-size: 1.1rem;
            transition: all 0.3s;
            overflow: hidden;
            position: relative;
            min-height: 80px;
            display: flex;
            align-items: center;
            justify-content: center;
        }

        @keyframes slide {
            from {
                transform: translateX(-100%);
            }
            to {
                transform: translateX(100%);
            }
        }

        .update-demo .animated-content {
            display: inline-block;
            white-space: nowrap;
        }

        /* No update capability */
        @media (update: none) {
            .update-demo {
                background: #dc3545;
                color: white;
                border-color: #a71d2a;
            }
            .update-demo .animated-content {
                animation: none;
            }
            .update-demo::after {
                content: " (no update capability — animation disabled)";
                font-weight: normal;
                font-size: 0.85rem;
            }
        }

        /* Slow update */
        @media (update: slow) {
            .update-demo {
                background: #ffc107;
                color: #333;
                border-color: #d39e00;
            }
            .update-demo .animated-content {
                animation: slide 5s infinite alternate;
            }
            .update-demo::after {
                content: " (slow update — slow animation)";
                font-weight: normal;
                font-size: 0.85rem;
            }
        }

        /* Fast update */
        @media (update: fast) {
            .update-demo {
                background: #28a745;
                color: white;
                border-color: #1e7e34;
            }
            .update-demo .animated-content {
                animation: slide 1s infinite alternate;
            }
            .update-demo::after {
                content: " (fast update — smooth animation)";
                font-weight: normal;
                font-size: 0.85rem;
            }
        }

        /* ====== VIDEO-DYNAMIC-RANGE DEMO ====== */
        .hdr-demo {
            border-radius: 12px;
            overflow: hidden;
            margin: 15px 0;
        }

        .hdr-demo .video-container {
            width: 100%;
            height: 250px;
            display: flex;
            align-items: center;
            justify-content: center;
            font-size: 1.5rem;
            font-weight: bold;
            color: white;
            text-shadow: 0 2px 10px rgba(0,0,0,0.3);
            transition: all 0.3s;
            /* Standard dynamic range */
            background: linear-gradient(135deg, #007bff, #6c5ce7);
        }

        /* High dynamic range video */
        @media (video-dynamic-range: high) {
            .hdr-demo .video-container {
                background: linear-gradient(135deg,
                    oklch(69% 0.27 240),
                    oklch(69% 0.27 300),
                    oklch(69% 0.27 30));
                box-shadow: 0 0 40px rgba(108, 92, 231, 0.5);
            }
            .hdr-demo .video-container::after {
                content: " (HDR video enhanced ✨)";
                font-size: 1rem;
                font-weight: normal;
                display: block;
                margin-top: 5px;
            }
        }

        /* ====== DETECTION CARDS ====== */
        .detection-grid {
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
            gap: 15px;
            margin: 20px 0;
        }

        .detection-card {
            padding: 15px;
            border-radius: 8px;
            text-align: center;
            border: 2px solid var(--border);
            background: var(--bg-tertiary);
            transition: all 0.3s;
        }

        .detection-card .label {
            font-size: 0.85rem;
            color: var(--text-secondary);
            display: block;
            margin-bottom: 5px;
        }

        .detection-card .value {
            font-size: 1.1rem;
            font-weight: bold;
            color: var(--accent);
        }

        .detection-card.active {
            border-color: var(--accent);
            background: var(--bg-primary);
        }

        /* ====== LIVE INDICATOR ====== */
        .live-indicator {
            position: fixed;
            bottom: 20px;
            right: 20px;
            background: var(--accent);
            color: white;
            padding: 12px 20px;
            border-radius: 50px;
            font-weight: bold;
            font-size: 0.85rem;
            box-shadow: 0 4px 15px var(--shadow);
            z-index: 1000;
            transition: all 0.3s;
            display: flex;
            flex-direction: column;
            gap: 3px;
        }

        .live-indicator .detail {
            font-size: 0.75rem;
            opacity: 0.9;
        }

        /* ====== NOSCRIPT FALLBACK ====== */
        .noscript-message {
            display: none;
            padding: 15px;
            background: #f8d7da;
            border: 2px solid #dc3545;
            border-radius: 8px;
            color: #721c24;
            margin: 15px 0;
            font-weight: bold;
        }

        @media (scripting: none) {
            .noscript-message {
                display: block;
            }
        }

        /* ====== TIPS ====== */
        .tip-box {
            padding: 15px;
            border-radius: 8px;
            margin: 10px 0;
            border-left: 4px solid var(--accent);
            background: var(--bg-tertiary);
        }

        .tip-box.success { border-left-color: #28a745; }
        .tip-box.warning { border-left-color: #ffc107; }
        .tip-box.danger { border-left-color: #dc3545; }
    </style>
</head>
<body>

    <h1>scripting, update, and video-dynamic-range</h1>

    <!-- ====== NOSCRIPT MESSAGE ====== -->
    <div class="noscript-message">
        ⚠️ JavaScript is disabled in your browser. Some features may not work correctly.
    </div>

    <!-- ====== 1. SCRIPTING DEMO ====== -->
    <section>
        <h2>1. scripting</h2>
        <p>Detects whether <strong>JavaScript is available</strong> in the browser.</p>

        <div class="scripting-demo">
            Scripting Detection
        </div>

        <div class="code-block">
            /* No scripting */
            @media (scripting: none) {
                .demo {
                    background: #dc3545;
                }
            }

            /* Initial-only scripting */
            @media (scripting: initial-only) {
                .demo {
                    background: #ffc107;
                }
            }

            /* Full scripting */
            @media (scripting: enabled) {
                .demo {
                    background: #28a745;
                }
            }
        </div>

        <p class="note">Use this to style fallback content for users with JavaScript disabled.</p>
    </section>

    <!-- ====== 2. UPDATE DEMO ====== -->
    <section>
        <h2>2. update</h2>
        <p>Detects how <strong>frequently the display can update</strong> content.</p>

        <div class="update-demo">
            <span class="animated-content">🚀 Animation Speed Demo</span>
        </div>

        <div class="code-block">
            /* No update capability — disable animation */
            @media (update: none) {
                .demo .animated-content {
                    animation: none;
                }
            }

            /* Slow update — slow animation */
            @media (update: slow) {
                .demo .animated-content {
                    animation: slide 5s infinite alternate;
                }
            }

            /* Fast update — smooth animation */
            @media (update: fast) {
                .demo .animated-content {
                    animation: slide 1s infinite alternate;
                }
            }
        </div>

        <p class="note">E-ink displays have <code>update: slow</code> — animations should be infrequent. Print has <code>update: none</code> — no animation at all.</p>
    </section>

    <!-- ====== 3. VIDEO-DYNAMIC-RANGE DEMO ====== -->
    <section>
        <h2>3. video-dynamic-range</h2>
        <p>Detects the <strong>dynamic range of the video plane</strong> — specifically for HDR video.</p>

        <div class="hdr-demo">
            <div class="video-container">
                🎬 HDR Video Demo
            </div>
        </div>

        <div class="code-block">
            /* Standard dynamic range video */
            @media (video-dynamic-range: standard) {
                .video {
                    /* SDR styles */
                }
            }

            /* High dynamic range video */
            @media (video-dynamic-range: high) {
                .video {
                    background: linear-gradient(135deg,
                        oklch(69% 0.27 240),
                        oklch(69% 0.27 300));
                }
            }
        </div>

        <div class="tip-box warning">
            <strong>⚠️ Limited Availability:</strong>
            <code>video-dynamic-range</code> has limited browser support. Always provide a <code>standard</code> fallback.
        </div>
    </section>

    <!-- ====== 4. DETECTION SUMMARY ====== -->
    <section>
        <h2>4. Your Device's Capabilities</h2>
        <p>Below is a live detection of your device's capabilities.</p>

        <div class="detection-grid">
            <div class="detection-card" id="scripting-none">
                <span class="label">scripting: none</span>
                <span class="value" id="scripting-none-value">Detecting...</span>
            </div>
            <div class="detection-card" id="scripting-initial">
                <span class="label">scripting: initial-only</span>
                <span class="value" id="scripting-initial-value">Detecting...</span>
            </div>
            <div class="detection-card" id="scripting-enabled">
                <span class="label">scripting: enabled</span>
                <span class="value" id="scripting-enabled-value">Detecting...</span>
            </div>
            <div class="detection-card" id="update-none">
                <span class="label">update: none</span>
                <span class="value" id="update-none-value">Detecting...</span>
            </div>
            <div class="detection-card" id="update-slow">
                <span class="label">update: slow</span>
                <span class="value" id="update-slow-value">Detecting...</span>
            </div>
            <div class="detection-card" id="update-fast">
                <span class="label">update: fast</span>
                <span class="value" id="update-fast-value">Detecting...</span>
            </div>
            <div class="detection-card" id="vdr-standard">
                <span class="label">video-dynamic-range: standard</span>
                <span class="value" id="vdr-standard-value">Detecting...</span>
            </div>
            <div class="detection-card" id="vdr-high">
                <span class="label">video-dynamic-range: high</span>
                <span class="value" id="vdr-high-value">Detecting...</span>
            </div>
        </div>

        <div class="code-block">
            /* Detect scripting */
            @media (scripting: none) { /* No JavaScript */ }
            @media (scripting: initial-only) { /* Initial load only */ }
            @media (scripting: enabled) { /* Full JavaScript */ }

            /* Detect update capability */
            @media (update: none) { /* No update — static */ }
            @media (update: slow) { /* Slow update — e-ink */ }
            @media (update: fast) { /* Fast update — modern */ }

            /* Detect video dynamic range */
            @media (video-dynamic-range: standard) { /* SDR video */ }
            @media (video-dynamic-range: high) { /* HDR video */ }
        </div>
    </section>

    <!-- ====== 5. REFERENCE TABLES ====== -->
    <section>
        <h2>5. Reference Tables</h2>

        <h3>scripting Values</h3>
        <table class="reference-table">
            <tr>
                <th>Value</th>
                <th>Description</th>
                <th>Device Example</th>
            </tr>
            <tr>
                <td><code>none</code></td>
                <td>Scripting not available</td>
                <td>JavaScript disabled, some email clients</td>
            </tr>
            <tr>
                <td><code>initial-only</code></td>
                <td>Scripting only during initial load</td>
                <td>Print preview, some email clients</td>
            </tr>
            <tr>
                <td><code>enabled</code></td>
                <td>Scripting fully supported (default)</td>
                <td>Modern browsers</td>
            </tr>
        </table>

        <h3>update Values</h3>
        <table class="reference-table">
            <tr>
                <th>Value</th>
                <th>Description</th>
                <th>Device Example</th>
            </tr>
            <tr>
                <td><code>none</code></td>
                <td>Content cannot be updated after rendering</td>
                <td>Print, static e-ink</td>
            </tr>
            <tr>
                <td><code>slow</code></td>
                <td>Updates possible but not smooth</td>
                <td>E-ink readers, low-power devices</td>
            </tr>
            <tr>
                <td><code>fast</code></td>
                <td>Content updates quickly (default)</td>
                <td>Modern screens (60Hz+)</td>
            </tr>
        </table>

        <h3>video-dynamic-range Values</h3>
        <table class="reference-table">
            <tr>
                <th>Value</th>
                <th>Description</th>
                <th>Requirements</th>
            </tr>
            <tr>
                <td><code>standard</code></td>
                <td>Standard Dynamic Range (SDR)</td>
                <td>Typical displays</td>
            </tr>
            <tr>
                <td><code>high</code></td>
                <td>High Dynamic Range (HDR)</td>
                <td>Color depth > 24-bit, high brightness, high contrast</td>
            </tr>
        </table>

        <h3>dynamic-range vs video-dynamic-range</h3>
        <table class="reference-table">
            <tr>
                <th>Feature</th>
                <th>Target</th>
            </tr>
            <tr>
                <td><code>dynamic-range</code></td>
                <td>The whole output device (display)</td>
            </tr>
            <tr>
                <td><code>video-dynamic-range</code></td>
                <td>The video plane specifically</td>
            </tr>
        </table>
    </section>

    <!-- ====== 6. BEST PRACTICES ====== -->
    <section>
        <h2>6. Best Practices</h2>

        <div style="background: #d4edda; padding: 20px; border-radius: 8px; border-left: 4px solid #28a745;">
            <h3 style="margin-top: 0; color: #155724;">✅ Do This:</h3>
            <ul>
                <li>Use <code>scripting</code> to style fallbacks for users with JavaScript disabled</li>
                <li>Use <code>update</code> to conditionally enable animations based on device capability</li>
                <li>Use <code>video-dynamic-range</code> to enhance HDR video playback</li>
                <li>Always provide a working fallback for each feature</li>
                <li>Test with JavaScript disabled in your browser</li>
                <li>Test on e-ink devices or simulate slow updates</li>
            </ul>
        </div>

        <div style="background: #f8d7da; padding: 20px; border-radius: 8px; border-left: 4px solid #dc3545; margin-top: 15px;">
            <h3 style="margin-top: 0; color: #721c24;">❌ Don't Do This:</h3>
            <ul>
                <li>Don't rely solely on <code>scripting</code> for critical functionality</li>
                <li>Don't use fast animations on <code>update: slow</code> devices</li>
                <li>Don't use <code>video-dynamic-range</code> without a <code>standard</code> fallback</li>
                <li>Don't confuse <code>dynamic-range</code> with <code>video-dynamic-range</code></li>
                <li>Don't forget that <code>update: none</code> means no animation at all</li>
            </ul>
        </div>
    </section>

    <!-- ====== LIVE INDICATOR ====== -->
    <div class="live-indicator" id="live-indicator">
        <span id="indicator-scripting">Scripting: —</span>
        <span class="detail" id="indicator-update">Update: —</span>
        <span class="detail" id="indicator-vdr">Video DR: —</span>
    </div>

    <!-- ====== SCRIPT FOR DETECTION ====== -->
    <script>
        function updateIndicators() {
            function setCard(card, isActive, activeText, inactiveText) {
                const value = card.querySelector('.value');
                if (isActive) {
                    value.textContent = activeText;
                    card.classList.add('active');
                } else {
                    value.textContent = inactiveText;
                    card.classList.remove('active');
                }
            }

            // Scripting detection
            const scriptingNone = document.getElementById('scripting-none');
            const scriptingInitial = document.getElementById('scripting-initial');
            const scriptingEnabled = document.getElementById('scripting-enabled');

            const isScriptingNone = window.matchMedia('(scripting: none)').matches;
            const isScriptingInitial = window.matchMedia('(scripting: initial-only)').matches;
            const isScriptingEnabled = window.matchMedia('(scripting: enabled)').matches;

            setCard(scriptingNone, isScriptingNone, 'Active ✅', 'Not active');
            setCard(scriptingInitial, isScriptingInitial, 'Active ✅', 'Not active');
            setCard(scriptingEnabled, isScriptingEnabled, 'Active ✅', 'Not active');

            // Update detection
            const updateNone = document.getElementById('update-none');
            const updateSlow = document.getElementById('update-slow');
            const updateFast = document.getElementById('update-fast');

            const isUpdateNone = window.matchMedia('(update: none)').matches;
            const isUpdateSlow = window.matchMedia('(update: slow)').matches;
            const isUpdateFast = window.matchMedia('(update: fast)').matches;

            setCard(updateNone, isUpdateNone, 'Active ✅', 'Not active');
            setCard(updateSlow, isUpdateSlow, 'Active ✅', 'Not active');
            setCard(updateFast, isUpdateFast, 'Active ✅', 'Not active');

            // Video dynamic range detection
            const vdrStandard = document.getElementById('vdr-standard');
            const vdrHigh = document.getElementById('vdr-high');

            const isVdrStandard = window.matchMedia('(video-dynamic-range: standard)').matches;
            const isVdrHigh = window.matchMedia('(video-dynamic-range: high)').matches;

            setCard(vdrStandard, isVdrStandard, 'Active ✅', 'Not active');
            setCard(vdrHigh, isVdrHigh, 'Active ✅', 'Not active');

            // Live indicator
            const indicatorScripting = document.getElementById('indicator-scripting');
            const indicatorUpdate = document.getElementById('indicator-update');
            const indicatorVdr = document.getElementById('indicator-vdr');
            const indicator = document.getElementById('live-indicator');

            let scriptingText = 'Unknown';
            if (isScriptingEnabled) scriptingText = 'Enabled ✅';
            else if (isScriptingInitial) scriptingText = 'Initial only ⚠️';
            else if (isScriptingNone) scriptingText = 'None ❌';
            indicatorScripting.textContent = 'JS: ' + scriptingText;

            let updateText = 'Unknown';
            if (isUpdateFast) updateText = 'Fast ⚡';
            else if (isUpdateSlow) updateText = 'Slow 🐌';
            else if (isUpdateNone) updateText = 'None 🚫';
            indicatorUpdate.textContent = 'Update: ' + updateText;

            let vdrText = 'Unknown';
            if (isVdrHigh) vdrText = 'HDR ✨';
            else if (isVdrStandard) vdrText = 'SDR';
            indicatorVdr.textContent = 'Video DR: ' + vdrText;

            // Color indicator
            if (isUpdateFast && isScriptingEnabled) {
                indicator.style.background = '#28a745';
                indicator.style.color = 'white';
            } else if (isUpdateSlow || isScriptingNone) {
                indicator.style.background = '#ffc107';
                indicator.style.color = '#333';
            } else {
                indicator.style.background = '#007bff';
                indicator.style.color = 'white';
            }
        }

        window.addEventListener('load', updateIndicators);

        // Listen for changes
        window.matchMedia('(scripting: enabled)').addEventListener('change', updateIndicators);
        window.matchMedia('(update: fast)').addEventListener('change', updateIndicators);
        window.matchMedia('(video-dynamic-range: high)').addEventListener('change', updateIndicators);

        updateIndicators();
    </script>

</body>
</html>

Quick Reference

FeatureDescriptionValues
scriptingJavaScript availabilitynone, initial-only, enabled
updateDisplay update capabilitynone, slow, fast
video-dynamic-rangeDynamic range of video planestandard, high

scripting Values

ValueDescriptionDevice Example
noneNo scriptingJS disabled, some email clients
initial-onlyScripting only on initial loadPrint preview, some email clients
enabledFull scripting (default)Modern browsers

update Values

ValueDescriptionDevice Example
noneContent is static after renderingPrint, static e-ink
slowUpdates possible but not smoothE-ink readers
fastSmooth updates (default)Modern screens (60Hz+)

video-dynamic-range Values

ValueDescriptionRequirements
standardStandard Dynamic Range (SDR)Typical displays
highHigh Dynamic Range (HDR)Color depth > 24-bit, high brightness, high contrast

Best Practices

Do This:

/* Style fallbacks for no JavaScript */
@media (scripting: none) {
    .js-only {
        display: none;
    }
    .noscript-fallback {
        display: block;
    }
}

/* Conditionally enable animations */
@media (update: none) {
    .animated {
        animation: none;
    }
}

@media (update: slow) {
    .animated {
        animation: slide 5s infinite alternate;
    }
}

@media (update: fast) {
    .animated {
        animation: slide 1s infinite alternate;
    }
}

/* Enhance HDR video */
@media (video-dynamic-range: high) {
    .video {
        background: linear-gradient(135deg, oklch(69% 0.27 240), oklch(69% 0.27 300));
    }
}

Don’t Do This:

/* Don't rely on JavaScript for critical functionality */
@media (scripting: enabled) {
    .content {
        display: block;
        /* Content is hidden without JS! */
    }
}

/* Don't use fast animations on slow-update devices */
@media (update: slow) {
    .animated {
        animation: slide 0.1s infinite; /* Will flicker terribly! */
    }
}

/* Don't confuse dynamic-range with video-dynamic-range */
@media (video-dynamic-range: high) {
    /* This is for video, not the whole display */
}

Pro Tip: scripting is essential for progressive enhancement — always provide a working fallback for users with JavaScript disabled. update is great for device-adaptive animations — e-ink readers can’t handle smooth animation, so use slow, infrequent transitions. video-dynamic-range is a specialized feature for HDR video content — it has limited browser support, so always provide a standard fallback. Remember: dynamic-range is for the display, while video-dynamic-range is for the video plane specifically!


Stop using slow, ad-bloated tool sites! 🤮

🔎 Search “KandZ Tools” on Google to use many professional utilities for free.

KandZ.me is the ultimate minimalist hub for:
✅ Finance (Mortgage, Interest, Inflation)
✅ Tech (Base64, JSON, Dev Suite, IP)
✅ Health (BMI, BMR, TDEE)
✅ Productivity (Timer, Workspace, QR)

⚡️ Fast & Private
🔒 No data leaves your device
💎 100% Free

🔗 Use it now: https://tools.kandz.me
🔖 Bookmark it—you’ll need it later!