Use Adaptive mode if you want Obs.js to add CSS classes to your <html> element so you can adapt your UI via CSS or JavaScript.
Requirements:
- It MUST be placed in an inline
<script> tag in the <head> of your document. - It must run before any other scripts, stylesheets, or HTML that depend on it.
Copy and paste the following block as close to the top of your <head> as possible:
<script>
/*! Obs.js | (c) Harry Roberts, csswizardry.com | MIT */
;(()=>{const e=document.currentScript,t=window.obs,i=null!==t&&"object"==typeof t&&t.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("[Obs.js] Skipping: must be an inline, classic <script> in <head>.",e?e.src:"type=module");const a=null!==t&&"object"==typeof t?t:{};window.obs=a;const r=!0===i.observeChanges,o=["low","medium","high"],l=["low","medium","high"],c=["very-low","low","medium","high"],d=["low","medium","high"],s=["strong","moderate","weak"],u=["strong","moderate","weak"],g=["conserve","neutral"],h=["rich","cautious","lite"];let y,m,v=!1;const w=(e,t,i)=>{n&&(t.forEach(t=>y.classList.remove(`has-${e}-${t}`)),null!==i&&y.classList.add(`has-${e}-${i}`))},b=(e,t)=>{n&&y.classList.toggle(e,t)},p=()=>{a.connectionCapability="low"===a.rttCategory&&"high"===a.downlinkCategory?"strong":"high"===a.rttCategory||"low"===a.downlinkCategory?"weak":"moderate";const e=!0===a.dataSaver||!0===a.batteryLow||!0===a.batteryCritical;a.conservationPreference=e?"conserve":"neutral",a.deliveryMode="weak"===a.connectionCapability||!0===a.dataSaver||!0===a.batteryCritical?"lite":"strong"!==a.connectionCapability||e?"cautious":"rich",a.canShowRichMedia="lite"!==a.deliveryMode,a.shouldAvoidRichMedia="lite"===a.deliveryMode,w("connection-capability",u,a.connectionCapability),w("conservation-preference",g,a.conservationPreference),w("delivery-mode",h,a.deliveryMode)},C=()=>{if(!m)return;const{saveData:e,rtt:t,downlink:i}=m;a.dataSaver=!!e,b("has-data-saver",a.dataSaver);const n=(e=>Number.isFinite(e)?25*Math.ceil(e/25):null)(t),r=(e=>Number.isFinite(e)?e<75?"low":e<275?"medium":"high":null)(t);null===n?(delete a.rttBucket,delete a.rttCategory):(a.rttBucket=n,a.rttCategory=r),w("latency",o,r);const c=(e=>Number.isFinite(e)?Math.ceil(e):null)(i),d=(e=>null===c?null:e<=5?"low":e>=8?"high":"medium")(c);null===c?(delete a.downlinkBucket,delete a.downlinkCategory):(a.downlinkBucket=c,a.downlinkCategory=d),w("bandwidth",l,d),"downlinkMax"in m&&(a.downlinkMax=m.downlinkMax),p()},k=()=>{v||(v=!0,y=document.documentElement,m=navigator.connection,C(),r&&m&&"function"==typeof m.addEventListener&&m.addEventListener("change",C),"getBattery"in navigator&&navigator.getBattery().then(e=>{const t=()=>(e=>{if(!e)return;const{level:t,charging:i}=e,r=Number.isFinite(t)?t<=.05:null,o=Number.isFinite(t)?t<=.2:null;a.batteryCritical=r,a.batteryLow=o,w("battery",["critical","low"],null),n&&o&&y.classList.add("has-battery-low"),n&&r&&y.classList.add("has-battery-critical"),a.batteryCharging=!!i,b("has-battery-charging",a.batteryCharging),p()})(e);t(),r&&"function"==typeof e.addEventListener&&(e.addEventListener("levelchange",t),e.addEventListener("chargingchange",t))}).catch(()=>{}),(()=>{if("deviceMemory"in navigator){const e=Number(navigator.deviceMemory),t=Number.isFinite(e)?e:null,i=(e=>Number.isFinite(e)?e<=1?"very-low":e<=2?"low":e<=4?"medium":"high":null)(t);a.ramBucket=t,null===i?delete a.ramCategory:a.ramCategory=i,w("ram",c,i)}if("hardwareConcurrency"in navigator){const e=Number(navigator.hardwareConcurrency),t=Number.isFinite(e)?e:null,i=(e=>Number.isFinite(e)?e<=2?"low":e<=5?"medium":"high":null)(t);a.cpuBucket=t,null===i?delete a.cpuCategory:a.cpuCategory=i,w("cpu",d,i)}})(),(()=>{const e=a.ramCategory,t=a.cpuCategory;let i="moderate";"medium"!==e&&"high"!==e||"high"!==t?("very-low"===e||"low"===e||"low"===t)&&(i="weak"):i="strong",a.deviceCapability=i,w("device-capability",s,i)})())};if(!0===document.prerendering){const e=()=>{document.removeEventListener("prerenderingchange",e),document.removeEventListener("visibilitychange",t),k()},t=()=>{"visible"===document.visibilityState&&e()};document.addEventListener("prerenderingchange",e,{once:!0}),document.addEventListener("visibilitychange",t)}else k()})();
//# sourceURL=obs.inline.js
</script>