### Obs.js Adaptive Installation Script Source: https://github.com/csswizardry/obs.js/blob/main/README.md Place this inline script in the of your document before any other scripts or stylesheets. It must be an inline, classic ``` -------------------------------- ### Analytics Installation for Obs.js Source: https://github.com/csswizardry/obs.js/blob/main/README.md Install Obs.js to collect analytics signals by disabling adaptive mode. This script should be placed before the main obs.js script. ```html ``` -------------------------------- ### Initialize LUX object and performance variables Source: https://github.com/csswizardry/obs.js/blob/main/demo/index.html Initializes the LUX object and its associated arrays for marks and measures. It determines the starting point for performance measurements, using `window.performance.timing.navigationStart` if available, otherwise falling back to the current time. ```javascript var a=("undefined"!==typeof(LUX)&&"undefined"!==typeof(LUX.gaMarks)?LUX.gaMarks:[]);var d=("undefined"!==typeof(LUX)&&"undefined"!==typeof(LUX.gaMeasures)?LUX.gaMeasures:[]);var j="LUX_start";var k=window.performance;var l=("undefined"!==typeof(LUX)&&LUX.ns?LUX.ns:(Date.now?Date.now():+(new Date())));if(k&&k.timing&&k.timing.navigationStart){l=k.timing.navigationStart} ``` -------------------------------- ### Get current high-resolution time Source: https://github.com/csswizardry/obs.js/blob/main/demo/index.html Provides a function to get the current high-resolution time using `window.performance.now()` if available. If not, it falls back to calculating the difference between the current time and the initial start time `l`. ```javascript function f(){if(k&&k.now){return k.now()}var o=Date.now?Date.now():+(new Date());return o-l} ``` -------------------------------- ### Obs.js Data Object Source: https://github.com/csswizardry/obs.js/blob/main/README.md An example of the `window.obs` object structure, detailing configuration, data saver status, network round-trip time (RTT) and downlink buckets, connection capabilities, conservation preferences, delivery mode, and battery status. ```js { "config": { "adaptive": true, "observeChanges": false }, "dataSaver": false, "rttBucket": 50, "rttCategory": "low", "downlinkBucket": 10, "connectionCapability": "strong", "conservationPreference": "neutral", "deliveryMode": "rich", "canShowRichMedia": true, "shouldAvoidRichMedia": false, "batteryCritical": false, "batteryLow": false, "batteryCharging": true } ``` -------------------------------- ### HTML with Obs.js Classes Source: https://github.com/csswizardry/obs.js/blob/main/README.md Example of an HTML element with CSS classes added by Obs.js, indicating low latency, high bandwidth, charging battery, strong connection capability, neutral conservation preference, and rich delivery mode. ```html ``` -------------------------------- ### Measure time between two points Source: https://github.com/csswizardry/obs.js/blob/main/demo/index.html Measures the duration between two performance points (start and end) using `performance.measure` or `performance.webkitMeasure`. It supports specifying start and end marks by name or using navigation timing attributes. Fallback recording in array `d` is included. ```javascript function m(p,t,n){if("undefined"===typeof(t)&&h(j)){t=j}if(k){if(k.measure){if(t){if(n){return k.measure(p,t,n)}else{return k.measure(p,t)}}else{return k.measure(p)}}else{if(k.webkitMeasure){return k.webkitMeasure(p,t,n)}}}var r=0,o=f();if(t){var s=h(t);if(s){r=s.startTime}else{if(k&&k.timing&&k.timing[t]){r=k.timing[t]-k.timing.navigationStart}else{return}}}if(n){var q=h(n);if(q){o=q.startTime}else{if(k&&k.timing&&k.timing[n]){o=k.timing[n]-k.timing.navigationStart}else{return}}}d.push({name:p,entryType:"measure",startTime:r,dura ``` -------------------------------- ### Classify CSS Classes for Performance Indicators Source: https://github.com/csswizardry/obs.js/blob/main/demo/index.html Maps CSS class names starting with 'has-' to specific visual styles ('c-pill u-bad', 'c-pill u-warn', 'c-pill u-good') based on performance-related keywords like battery, latency, bandwidth, RAM, CPU, and connection capability. Returns a neutral 'c-pill' if no specific category matches. ```javascript const classify = (name) => { if ( /battery-critical/.test(name) || /latency-high/.test(name) || /bandwidth-low/.test(name) || /ram-very-low/.test(name) || /ram-low/.test(name) || /cpu-low/.test(name) || /connection-capability-weak/.test(name) || /device-capability-weak/.test(name) || /delivery-mode-lite/.test(name) ) return 'c-pill u-bad'; if ( /battery-low/.test(name) || /data-saver/.test(name) || /device-capability-moderate/.test(name) || /ram-medium/.test(name) || /cpu-medium/.test(name) || /bandwidth-medium/.test(name) || /latency-medium/.test(name) || /conservation-preference-conserve/.test(name) || /connection-capability-moderate/.test(name) || /delivery-mode-cautious/.test(name) ) return 'c-pill u-warn'; if ( /connection-capability-strong/.test(name) || /device-capability-strong/.test(name) || /delivery-mode-rich/.test(name) || /bandwidth-high/.test(name) || /ram-high/.test(name) || /cpu-high/.test(name) || /latency-low/.test(name) ) return 'c-pill u-good'; return 'c-pill'; }; ``` -------------------------------- ### Obs.js Performance Measurement Source: https://github.com/csswizardry/obs.js/blob/main/demo/index.html This snippet measures the execution time of the Obs.js script itself using the Performance API. It marks the start and end points and logs the duration to the console. ```javascript performance.mark('obs\_end'); const obs\_time = performance.measure('obs\_time', 'obs\_start', 'obs\_end'); console.log('Obs.js time: ' + obs\_time.duration) ``` -------------------------------- ### Obs.js Initialization and Core Logic Source: https://github.com/csswizardry/obs.js/blob/main/demo/index.html This snippet initializes Obs.js, sets up configuration, and contains the core logic for detecting and applying performance-related context. It includes adaptive logic, network and battery status checks, and device capability analysis. It's designed to run inline in the `` of an HTML document. ```javascript window.obs = { config: { observeChanges: true } }; performance.mark('obs\_start'); /*! Obs.js | (c) Harry Roberts, csswizardry.com | MIT */ ;(()=>{const e=document.currentScript,i=window.obs&&window.obs.config||{},n=!1!==i.adaptive;if(n&&(!e||e.src||e.type&&"module"===e.type.toLowerCase())&&!1===/^(localhost|127\.0\.0\.1|::1)$/.test(location.hostname))return void console.warn("\n[Obs.js] Skipping: must be an inline, classic ``` -------------------------------- ### Serve Lite Version to Slow Browsers Source: https://github.com/csswizardry/obs.js/blob/main/README.md Use this snippet to serve the lite version of media to browsers that support Obs.js but are detected as slow. Otherwise, serve the rich version to fast supportive browsers and Safari. ```javascript if (window.obs?.shouldAvoidRichMedia === true) { // Serve lite version to slow supportive browsers. } else { // Serve rich version to fast supportive browsers and Safari. } ``` -------------------------------- ### Serve Rich Version to Fast Browsers Source: https://github.com/csswizardry/obs.js/blob/main/README.md Use this snippet to serve the rich version of media to browsers that support Obs.js and are detected as fast. Otherwise, serve the lite version to slow supportive browsers and Safari. ```javascript if (window.obs?.canShowRichMedia === true) { // Serve rich version to fast supportive browsers. } else { // Serve lite version to slow supportive browsers and Safari. } ``` -------------------------------- ### CSS to Serve Low-Resolution Images Source: https://github.com/csswizardry/obs.js/blob/main/README.md This CSS demonstrates how to conditionally serve low-resolution images by applying a different background image when the delivery mode is set to 'lite' by Obs.js. ```css body { background-image: url('hi-res.jpg'); } /** * Show low-resolution images if the user can’t take rich media right now. */ .has-delivery-mode-lite body { background-image: url('lo-res.jpg'); } ``` -------------------------------- ### Listen for Changes in Obs.js Source: https://github.com/csswizardry/obs.js/blob/main/README.md Configure Obs.js to observe changes in connection and battery status for long-lived pages or SPAs. The default is false. ```html ``` -------------------------------- ### Obs.js Device and RAM Detection Source: https://github.com/csswizardry/obs.js/blob/main/demo/index.html This snippet focuses on detecting device memory (RAM) and CPU core count. It categorizes these into buckets (e.g., 'low', 'medium', 'high') and applies corresponding CSS classes to the `` element. It also determines an overall 'device capability' based on RAM and CPU. ```javascript h=()=>{if(!l){if(l=!0,w(),a&&o&&"function"==typeof o.addEventListener&&o.addEventListener("change",w),"getBattery"in navigator&&navigator.getBattery().then(e=>{u(e),a&&"function"==typeof e.addEventListener&&(e.addEventListener("levelchange",()=>u(e)),e.addEventListener("chargingchange",()=>u(e)))}).catch(()=>{}),"deviceMemory"in navigator){const i=Number(navigator.deviceMemory),n=Number.isFinite(i)?i:null;window.obs.ramBucket=n;const t=(e=n,Number.isFinite(e)?e<=1?"very-low":e<=2?"low":e<=4?"medium":"high":null);t&&(window.obs.ramCategory=t,r(["very-low","low","medium","high"].map(e=>`has-ram-${e}`)),c(`has-ram-${t}`))}}var e;if("hardwareConcurrency"in navigator){const e=Number(navigator.hardwareConcurrency),i=Number.isFinite(e)?e:null;window.obs.cpuBucket=i;const n=(e=>Number.isFinite(e)?e<=2?"low":e<=5?"medium":"high":null)(i);n&&(window.obs.cpuCategory=n,r(["low","medium","high"].map(e=>`has-cpu-${e}`)),c(`has-cpu-${n}`))}(()=>{const e=window.obs||{},i=e.ramCategory,n=e.cpuCategory;let t="moderate";"high"!==i&&"medium"!==i||"high"!==n?("very-low"===i||"low"===i||"low"===n)&&(t="weak"):t="strong";e.deviceCapability=t,r(["strong","moderate","weak"].map(e=>`has-device-capability-${e}`)),c(`has-device-capability-${t}`)})()}}() ``` -------------------------------- ### Render Observed Data Source: https://github.com/csswizardry/obs.js/blob/main/demo/index.html Renders the current state of the `window.obs` object as a JSON string. It attempts to create a deep copy before stringifying to ensure a clean snapshot. If stringification fails, it falls back to displaying the raw `window.obs` value. ```javascript function renderObs() { try { const snapshot = window.obs ? JSON.parse(JSON.stringify(window.obs)) : {}; obsEl.textContent = JSON.stringify(snapshot, null, 2); } catch { obsEl.textContent = String(window.obs); } } ``` -------------------------------- ### Obs.js Basic Layout Styling Source: https://github.com/csswizardry/obs.js/blob/main/demo/index.html This snippet applies basic margin and padding rules for common HTML elements like headings, paragraphs, and code blocks. It also includes a media query for adjusting left margin on ordered lists for larger screens. ```css h1, h2, p, details, pre, ul, .c-snippet, .c-hero, .c-video > video, .c-video >img { margin-bottom: 1.5rem; } ol { margin-left: 1.5rem; } @media (min-width: 45em) { ol { margin-left: 0; } } ``` -------------------------------- ### Conditionally Inject Video or Image Source: https://github.com/csswizardry/obs.js/blob/main/demo/index.html Injects a video element if the connection is good, otherwise injects a static image. Handles autoplay, looping, and muted properties, with specific adjustments for 'cautious' delivery modes or low battery. ```javascript (() => { const videoWrapper = document.getElementById('jsVideo'); if (window.obs?.shouldAvoidRichMedia === true) { const img = new Image(); img.src = './assets/poster.png'; img.alt = ''; img.width = '1500'; img.height = '966'; img.classList.add('c-hero'); videoWrapper.appendChild(img); } else { const video = document.createElement('video'); video.src = './assets/video.min.mp4'; video.poster = './assets/poster.png'; video.autoplay = true; video.loop = true; video.muted = true; video.controls = true; video.preload = 'auto'; if (window.obs?.deliveryMode === 'cautious') { video.preload = 'metadata'; } if (window.obs?.batteryLow === true) { video.autoplay = false; } videoWrapper.appendChild(video); } })(); ``` -------------------------------- ### Obs.js CSS Variables and Reset Source: https://github.com/csswizardry/obs.js/blob/main/demo/index.html This snippet defines CSS variables for colors and includes a basic CSS reset. It sets up the fundamental styling for the page, ensuring consistent appearance across different browsers. ```css :root { --fg: #333; --bg: #f9f9f9; --brand: #009aa2; --csswizardry: #f43059; --ok: #0a0; --warn: #a60; --bad: #a00; } * { box-sizing: border-box; margin: 0; padding: 0; } ``` -------------------------------- ### Render Performance Classes Source: https://github.com/csswizardry/obs.js/blob/main/demo/index.html Filters and displays all 'has-*' CSS classes present on the document element, applying a 'classify' function to style them based on performance indicators. If no 'has-*' classes are found, it displays a message indicating that APIs may be unavailable. ```javascript function renderClasses() { const list = (document.documentElement.className || '') .split(/\s+/) .filter(Boolean) .filter(interesting) .sort((a,b)=>a.localeCompare(b)); if (!list.length) { classesEl.innerHTML = '
No has-\* classes present (APIs may be unavailable).
'; return; } classesEl.innerHTML = ''; list.forEach(cls => { const span = document.createElement('samp'); span.className = classify(cls); span.textContent = '.' + cls; classesEl.appendChild(span); }); } ``` -------------------------------- ### Obs.js Battery Status Detection Source: https://github.com/csswizardry/obs.js/blob/main/demo/index.html This snippet handles the detection of battery status, including critical levels, low levels, and charging state. It updates the `window.obs` object and applies corresponding CSS classes to the `` element. ```javascript u=e=>{if(!e)return;const{level:i,charging:n}=e,t=Number.isFinite(i)?i<=.05:null;window.obs.batteryCritical=t;const o=Number.isFinite(i)?i<=.2:null;window.obs.batteryLow=o,r(["critical","low"].map(e=>`has-battery-${e}`)),o&&c("has-battery-low"),t&&c("has-battery-critical");const a=!!n;window.obs.batteryCharging=a,s("has-battery-charging",a),d()} ``` -------------------------------- ### Obs.js HTML Base Styling Source: https://github.com/csswizardry/obs.js/blob/main/demo/index.html This snippet sets the base font size, line height, and font family for the HTML element, ensuring a readable and consistent typographic foundation for the entire page. ```css html { color: var(--fg); background-color: var(--bg); font: 1em/1.5 syst ``` -------------------------------- ### Mark a performance point Source: https://github.com/csswizardry/obs.js/blob/main/demo/index.html Marks a specific point in time with a given name using the browser's Performance API (`performance.mark` or `performance.webkitMark`). If the API is not available, it records the mark in a fallback array `a`. ```javascript function b(n){if(k){if(k.mark){return k.mark(n)}else{if(k.webkitMark){return k.webkitMark(n)}}}a.push({name:n,entryType:"mark",startTime:f(),duration:0});return} ``` -------------------------------- ### Disable Adaptive Mode in Obs.js Source: https://github.com/csswizardry/obs.js/blob/main/demo/index.html Disables the adaptive content loading feature of Obs.js by setting the 'adaptive' configuration option to false. This should be done before Obs.js runs if you only intend to use it for observational analytics. ```javascript window.obs = { config: { adaptive: false } }; ``` -------------------------------- ### Shuffle Showcase Items Source: https://github.com/csswizardry/obs.js/blob/main/demo/index.html This JavaScript code shuffles the list of showcase items to ensure no preferential treatment. It selects an element with the ID 'jsShowcase' and randomizes the order of its list items. ```javascript (() => { // Shuffle the Showcase so there’s no preferential treatment. const showcase = document.querySelector('#jsShowcase'); const items = [...showcase.querySelectorAll('li')]; // Bail out if there is nothing to shuffle. if (items.length === 1) { return; } else { for (let i = items.length - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)); [items[i], items[j]] = [items[j], items[i]]; } } showcase.append(...items); })(); ``` -------------------------------- ### Send Obs.js Data to SpeedCurve Source: https://github.com/csswizardry/obs.js/blob/main/demo/index.html This snippet sends captured Obs.js data to SpeedCurve. Ensure that the LUX object and its addData function are available before execution. ```javascript /** * All the data previously captured by Obs.js is now sent to SpeedCurve! * I ❤️ SpeedCurve! */ (() => { // Bail out if SpeedCurve is not available. if (!window.LUX || typeof window.LUX.addData !== 'function') return; const obs = window.obs || Object.create(null); // Keys we intend to send. Keep in sync with Obs.js const keys = [ 'canShowRichMedia', 'connectionCapability', 'conservationPreference', 'cpuBucket', 'cpuCategory', 'dataSaver', 'deliveryMode', 'deviceCapability', 'downlinkBucket', 'downlinkCategory', 'ramBucket', 'ramCategory', 'rttBucket', 'rttCategory', 'shouldAvoidRichMedia' ]; for (const key of keys) { if (Object.prototype.hasOwnProperty.call(obs, key)) { window.LUX.addData(key, obs[key]); } } })(); ``` -------------------------------- ### Conditional Google Fonts Loading Source: https://github.com/csswizardry/obs.js/blob/main/demo/index.html This script loads Google Fonts only if rich media can be displayed, based on browser API support. It appends the font link to the document body. ```javascript (() => { // Only load Google Fonts if we can show rich media. In browsers that // don’t support the relevant APIs, we just go ahead and load it. // // Attach it to the end of the `body` rather than the `head` so it doesn’t // inadvertently block rendering. if (window.obs?.shouldAvoidRichMedia === true) { return; } else { const gf = document.createElement('link'); gf.rel = 'stylesheet'; gf.href = 'https://fonts.googleapis.com/css2?family=Fredoka:wght@300..700&display=swap'; document.body.appendChild(gf); } // Fun fact: because this `script` is a classic, inline, synchronous // `