1<!doctype html> 2<html lang="en"> 3 <head> 4 <meta charset="utf-8" /> 5 <meta name="viewport" content="width=device-width, initial-scale=1" /> 6 <title>Privacy and data use | CSS Radar</title> 7 <meta 8 name="description" 9 content="How CSS Radar handles submitted URLs, report email addresses, temporary audit records, public report links, and landing-page analytics." 10 /> 11 <link rel="icon" type="image/svg+xml" href="favicon.svg" /> 12 <link rel="canonical" href="https://cssradar.com/privacy.html" /> 13 <link rel="alternate" type="application/rss+xml" title="CSS Radar articles" href="https://cssradar.com/feed.xml" /> 14 15 <!-- Open Graph --> 16 <meta property="og:type" content="article" /> 17 <meta property="og:site_name" content="Modern CSS Radar" /> 18 <meta property="og:title" content="Privacy and data use | CSS Radar" /> 19 <meta 20 property="og:description" 21 content="How CSS Radar handles submitted URLs, report email addresses, temporary audit records, public report links, and landing-page analytics." 22 /> 23 <meta property="og:url" content="https://cssradar.com/privacy.html" /> 24 <meta property="og:image" content="https://cssradar.com/assets/og.png" /> 25 <meta property="og:image:width" content="1200" /> 26 <meta property="og:image:height" content="630" /> 27 <meta property="og:image:alt" content="Modern CSS Radar: audit one page, ship less custom code." /> 28 <meta property="og:locale" content="en_US" /> 29 30 <!-- X / Twitter card --> 31 <meta name="twitter:card" content="summary_large_image" /> 32 <meta name="twitter:title" content="Privacy and data use | CSS Radar" /> 33 <meta 34 name="twitter:description" 35 content="How CSS Radar handles submitted URLs, report email addresses, temporary audit records, public report links, and landing-page analytics." 36 /> 37 <meta name="twitter:image" content="https://cssradar.com/assets/og.png" /> 38 <meta name="twitter:image:alt" content="Modern CSS Radar: audit one page, ship less custom code." /> 39 40
40<script> 41 try { 42 const requestedTheme = new URLSearchParams(window.location.search).get('theme'); 43 const storedTheme = window.localStorage.getItem('css-radar-theme'); 44 const initialTheme = ['light', 'dark'].includes(requestedTheme) 45 ? requestedTheme 46 : ['light', 'dark'].includes(storedTheme) 47 ? storedTheme 48 : null; 49 50 if (initialTheme) document.documentElement.style.colorScheme = initialTheme; 51 } catch {} 52 </script>
52 53 <link rel="stylesheet" href="/styles.css?v=303c7bd6d52a" /> 54 </head> 55 <body> 56 <header class="site-header" id="top"> 57 <a class="skip-link" href="#main">Skip to content</a> 58 <div class="wrap site-header__row"> 59 <a class="site-header__brand" href="/"> 60 <svg class="radar-mark" viewBox="0 0 32 32" width="28" height="28" aria-hidden="true"> 61 <circle cx="16" cy="16" r="14" fill="none" stroke="currentColor" stroke-width="1.5" /> 62 <circle cx="16" cy="16" r="8" fill="none" stroke="currentColor" stroke-width="1" opacity="0.45" /> 63 <line 64 class="radar-mark__sweep" 65 x1="16" 66 y1="16" 67 x2="16" 68 y2="3" 69 stroke="var(--accent)" 70 stroke-width="2" 71 stroke-linecap="round" 72 /> 73 <circle cx="22" cy="10" r="2" fill="var(--accent)" /> 74 </svg> 75 <span class="site-header__brand-text">CSS Radar</span> 76 </a> 77 <div class="site-header__actions"> 78 <div class="theme-toggle"> 79 <input 80 class="theme-toggle__input" 81 id="theme-toggle" 82 type="checkbox" 83 role="switch" 84 aria-label="Switch to dark mode" 85 /> 86 <label class="theme-toggle__label" for="theme-toggle" title="Switch to dark mode"> 87 <span class="theme-toggle__icon" aria-hidden="true"> 88 <svg 89 class="theme-toggle__moon" 90 xmlns="http://www.w3.org/2000/svg" 91 width="100%" 92 height="100%" 93 viewBox="0 0 100 100" 94 preserveAspectRatio="xMidYMid meet" 95 > 96 <path 97 d="M82.1,61.2a31.9,31.9,0,0,1-12.4,2.4A33.3,33.3,0,0,1,36.4,30.3a31.9,31.9,0,0,1,2.4-12.4A33.3,33.3,0,1,0,82.1,61.2Z" 98 fill="none" 99 stroke="currentColor" 100 stroke-linecap="round" 101 stroke-linejoin="round" 102 stroke-width="5" 103 /> 104 </svg> 105 <svg 106 class="theme-toggle__sun" 107 xmlns="http://www.w3.org/2000/svg" 108 width="100%" 109 height="100%" 110 viewBox="0 0 100 100" 111 preserveAspectRatio="xMidYMid meet" 112 > 113 <path 114 d="M50,18v3.6m0,56.8V82M82,50H78.4M21.6,50H18M72.6,72.6l-2.5-2.5M29.9,29.9l-2.5-2.5m45.2,0-2.5,2.5M29.9,70.1l-2.5,2.5M64.2,50A14.2,14.2,0,1,1,50,35.8,14.3,14.3,0,0,1,64.2,50Z" 115 fill="none" 116 stroke="currentColor" 117 stroke-linecap="round" 118 stroke-linejoin="round" 119 stroke-width="5" 120 /> 121 </svg> 122 </span> 123 </label> 124 </div> 125 <nav class="site-header__nav" aria-label="Main"> 126 <a class="site-header__link" href="/blog/" aria-current="false">Blog</a> 127 <a class="site-header__link site-header__link--cta" href="/">Audit my page</a> 128 </nav> 129 </div> 130 </div> 131 </header> 132 133 <main class="privacy-page wrap" id="main"> 134 <header class="privacy-page__header"> 135 <p class="privacy-page__eyebrow">Privacy</p> 136 <h1>What CSS Radar handles.</h1> 137 <p class="privacy-page__intro"> 138 CSS Radar audits public pages without an account. This page explains how the submitted URL and required 139 report email are used, who can open a report, and when CSS Radar's temporary records expire. 140 </p> 141 <p class="privacy-page__updated">Last updated August 13, 2026</p> 142 </header> 143 144 <div class="privacy-page__content"> 145 <section class="privacy-section" aria-labelledby="privacy-audit"> 146 <h2 id="privacy-audit">When you start an audit</h2> 147 <p> 148 Your browser sends the submitted public URL and email address to <code>api.cssradar.com</code>. The audit 149 service opens that URL in Chrome, inspects the rendered page and relevant 150 HTML, CSS, and JavaScript, observes safe interactions, and builds the report. It does not sign in or try to 151 bypass access controls. 152 </p> 153 <p class="privacy-section__note"> 154 Do not submit private URLs, signed links, or URLs containing passwords, access tokens, or other secrets. 155 </p> 156 </section> 157 158 <section class="privacy-section" aria-labelledby="privacy-storage"> 159 <h2 id="privacy-storage">What is stored</h2> 160 <ul> 161 <li> 162 <strong>Private audit record:</strong> the submitted URL, report email address, audit status, 163 timestamps, and technical result references are stored in Google Cloud Firestore. This record is scheduled 164 to expire 31 days after the audit finishes, fails, or is canceled. 165 </li> 166 <li> 167 <strong>Report file:</strong> a completed report is stored as an HTML file in Google Cloud Storage. It is 168 scheduled for automatic deletion after 30 days. 169 </li> 170 <li> 171 <strong>Kit subscriber:</strong> the email address is added to Kit with the 172 <code>radar</code> tag for occasional CSS Radar and modern CSS emails. Kit keeps that subscriber record until 173 the person unsubscribes or asks CSS Radar to delete it. 174 </li> 175 <li> 176 <strong>Daily limit:</strong> CSS Radar derives a pseudonymous identifier from the visitor's IP address with 177 a keyed hash. The raw IP address is not stored in the application's rate-limit record. That record
177is 178 scheduled to expire within 48 hours. 179 </li> 180 </ul> 181 <p> 182 The 30-day deletion rule covers the generated report file. Standard infrastructure logs kept by the 183 service providers below are separate and follow each provider's own retention terms. 184 </p> 185 </section> 186 187 <section class="privacy-section" aria-labelledby="privacy-access"> 188 <h2 id="privacy-access">Who can open a report</h2> 189 <p> 190 Anyone with the report URL can open it without an account. CSS Radar does not list reports on the site or 191 include them in its sitemap, but the link is not an access-control mechanism. Do not share it with people 192 who should not see the report. 193 </p> 194 <p> 195 To request deletion before the automatic deadline, email 196 <a href="mailto:[email protected]?subject=Delete%20a%20CSS%20Radar%20report">[email protected]</a> and include 197 the report URL. 198 </p> 199 </section> 200 201 <section class="privacy-section" aria-labelledby="privacy-email"> 202 <h2 id="privacy-email">How the email address is used</h2> 203 <p> 204 CSS Radar uses the submitted email address to send the requested report or to say when the audit could not 205 be completed. It also adds the address to Kit with the <code>radar</code> tag for occasional CSS 206 Radar and modern CSS emails. Marketing emails include an unsubscribe option. The address does not appear 207 in the generated report. 208 </p> 209 <p> 210 Mailjet, a Sinch brand, receives the address and message content to deliver the 211 report or failure notice. Its delivery records are separate from CSS Radar's 30-day report file and follow 212 Mailjet's own retention terms. 213 </p> 214 <p> 215 Kit receives the email address to maintain the subscriber list and apply the 216 <code>radar</code> tag. To request complete deletion instead of unsubscribing, email 217 <a href="mailto:[email protected]">[email protected]</a>. 218 </p> 219 </section> 220 221 <section class="privacy-section" aria-labelledby="privacy-analytics"> 222 <h2 id="privacy-analytics">Landing-page analytics</h2> 223 <p> 224 The public landing page loads Fairlytics to count visits and understand which pages are used. According to 225 Fairlytics, its hosted service uses no cookies. It receives the current CSS Radar page URL, the referrer, 226 and request data such as the IP address, browser, and operating system. Fairlytics says it uses the IP 227 address to derive approximate location and a daily hashed visitor identifier, then discards the raw IP. 228 </p> 229 <p> 230 The URL and email address typed into the audit form are not sent to Fairlytics as analytics events, and 231 generated reports do not load Fairlytics. Fairlytics stores its audience records separately. Its public 232 documentation does not state a fixed retention period, so the 30-day report rule does not cover those 233 analytics records. 234 </p> 235 <p> 236 Read Fairlytics' 237 <a href="https://fairlytics.tech/doc/fonctionnement" target="_blank" rel="noopener">data explanation</a> 238 and <a href="https://fairlytics.tech/doc/cgu" target="_blank" rel="noopener">terms</a>. 239 </p> 240 </section> 241 242 <section class="privacy-section" aria-labelledby="privacy-providers"> 243 <h2 id="privacy-providers">Service providers and contact</h2> 244 <ul> 245 <li> 246 <a href="https://www.netlify.com/privacy/" target="_blank" rel="noopener">Netlify</a> serves the public 247 website. 248 </li> 249 <li> 250 <a href="https://cloud.google.com/terms/cloud-privacy-notice" target="_blank" rel="noopener">Google Cloud</a> 251 runs the audit service and stores temporary audit records and reports. 252 </li> 253 <li> 254 <a href="https://fairlytics.tech/" target="_blank" rel="noopener">Fairlytics</a> provides the 255 landing-page audience measurement described above. 256 </li> 257 <li> 258 <a href="https://www.mailjet.com/legal/privacy-policy/" target="_blank" rel="noopener">Mailjet</a> 259 delivers report emails and audit failure notices. 260 </li> 261 <li> 262 <a href="https://kit.com/privacy" target="_blank" rel="noopener">Kit</a> stores report recipients as 263 subscribers and manages later email updates and unsubscribes. 264 </li> 265 </ul> 266 <p> 267 CSS Radar is operated by Theo Soti. For a privacy question, subscriber-deletion request, or an early 268 report-deletion request, email
269 <a href="mailto:[email protected]">[email protected]</a>. 270 </p> 271 </section> 272 </div> 273 </main> 274 275 <footer class="site-footer"> 276 <div class="wrap site-footer__row"> 277 <p>Modern CSS Radar. Audit one page, ship less custom code.</p> 278 <div class="site-footer__meta"> 279 <p class="site-footer__credit"> 280 Created by <a href="https://theosoti.com/">Theo Soti</a> · 281 <a href="/blog/">Blog</a> · 282 <a href="/privacy.html">Privacy and data use</a> 283 </p> 284 <div class="site-footer__badges"> 285 <a 286 class="site-footer__badge" 287 href="https://codetrendy.com/?utm_source=cssradar.com&utm_medium=badge" 288 target="_blank" 289 rel="nofollow noopener noreferrer" 290 > 291 <img 292 src="https://codetrendy.com/api/badge?style=classic" 293 alt="codetrendy.com listing" 294 width="220" 295 height="56" 296 loading="lazy" 297 decoding="async" 298 referrerpolicy="no-referrer" 299 /> 300 </a> 301 <a 302 class="site-footer__badge" 303 href="https://sitepatent.com/?utm_source=cssradar.com&utm_medium=badge" 304 target="_blank" 305 rel="nofollow noopener noreferrer" 306 > 307 <img 308 src="https://sitepatent.com/api/badge?style=classic" 309 alt="sitepatent.com listing" 310 width="220" 311 height="56" 312 loading="lazy" 313 decoding="async" 314 referrerpolicy="no-referrer" 315 /> 316 </a> 317 </div> 318 </div> 319 </div> 320 </footer> 321 322
322<script> 323 (() => { 324 const themeToggle = document.querySelector('#theme-toggle'); 325 const themeLabel = document.querySelector('.theme-toggle__label'); 326 const requestedTheme = new URLSearchParams(window.location.search).get('theme'); 327 const systemTheme = window.matchMedia('(prefers-color-scheme: dark)'); 328 let storedTheme = null; 329 330 try { 331 storedTheme = window.localStorage.getItem('css-radar-theme'); 332 } catch {} 333 334 const hasRequestedTheme = requestedTheme === 'light' || requestedTheme === 'dark'; 335 let hasManualTheme = storedTheme === 'light' || storedTheme === 'dark'; 336 337 const applyTheme = (theme, persist = false) => { 338 const nextTheme = theme === 'dark' ? 'light' : 'dark'; 339 document.documentElement.style.colorScheme = theme; 340 341 if (themeToggle) { 342 themeToggle.checked = theme === 'dark'; 343 themeToggle.setAttribute('aria-label', `Switch to ${nextTheme} mode`); 344 } 345 346 if (themeLabel) themeLabel.title = `Switch to ${nextTheme} mode`; 347 348 if (persist) { 349 hasManualTheme = true; 350 try { 351 window.localStorage.setItem('css-radar-theme', theme); 352 } catch {} 353 } 354 }; 355 356 const initialTheme = hasRequestedTheme 357 ? requestedTheme 358 : hasManualTheme 359 ? storedTheme 360 : systemTheme.matches 361 ? 'dark' 362 : 'light'; 363 364 applyTheme(initialTheme); 365 366 themeToggle?.addEventListener('change', (event) => { 367 applyTheme(event.currentTarget.checked ? 'dark' : 'light', true); 368 }); 369 370 systemTheme.addEventListener?.('change', (event) => { 371 if (!hasRequestedTheme && !hasManualTheme) applyTheme(event.matches ? 'dark' : 'light'); 372 }); 373 374 if (hasRequestedTheme) { 375 document.documentElement.style.colorScheme = requestedTheme; 376 } 377 })(); 378 </script>
378 379 </body> 380</html>
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.