
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/">
    <channel>
        <title><![CDATA[ The Cloudflare Blog ]]></title>
        <description><![CDATA[ Get the latest news on how products at Cloudflare are built, technologies used, and join the teams helping to build a better Internet. ]]></description>
        <link>https://blog.cloudflare.com</link>
        <atom:link href="https://blog.cloudflare.com/" rel="self" type="application/rss+xml"/>
        <language>en-us</language>
        <image>
            <url>https://blog.cloudflare.com/favicon.png</url>
            <title>The Cloudflare Blog</title>
            <link>https://blog.cloudflare.com</link>
        </image>
        <lastBuildDate>Sat, 04 Apr 2026 12:49:50 GMT</lastBuildDate>
        <item>
            <title><![CDATA[The most-seen UI on the Internet? Redesigning Turnstile and Challenge Pages]]></title>
            <link>https://blog.cloudflare.com/the-most-seen-ui-on-the-internet-redesigning-turnstile-and-challenge-pages/</link>
            <pubDate>Fri, 27 Feb 2026 06:00:00 GMT</pubDate>
            <description><![CDATA[ We serve 7.6 billion challenges daily. Here’s how we used research, AAA accessibility standards, and a unified architecture to redesign the Internet’s most-seen user interface. ]]></description>
            <content:encoded><![CDATA[ <p>You've seen it. Maybe you didn't register it consciously, but you've seen it. That little widget asking you to verify you're human. That full-page security check before accessing a website. If you've spent any time on the Internet, you've encountered Cloudflare's Turnstile widget or Challenge Pages — likely more times than you can count.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5YaxxmA9nz7AufmcJmhagL/0db6b65ec7456bc8091affc6beaf3ec2/Image_1_-_Turnstile.png" />
          </figure><p><sup><i>The Turnstile widget – a familiar sight across millions of websites</i></sup></p><p>When we say that a large portion of the Internet sits behind Cloudflare, we mean it. Our Turnstile widget and Challenge Pages are served 7.67 billion times every single day. That's not a typo. Billions. This might just be the most-seen user interface on the Internet.</p><p>And that comes with enormous responsibility.</p><p>Designing a product with billions of eyeballs on it isn't just challenging — it requires a fundamentally different approach. Every pixel, every word, every interaction has to work for someone's grandmother in rural Japan, a teenager in São Paulo, a visually impaired developer in Berlin, and a busy executive in Lagos. All at the same time. In moments of frustration.</p><p>Today we’re sharing the story of how we redesigned Turnstile and Challenge Pages. It's a story told in three parts, by three of us: the design process and research that shaped our decisions (Leo), the engineering challenge of deploying changes at unprecedented scale (Ana), and the measurable impact on billions of users (Marina).</p><p>Let's start with how we approached the problem from a design perspective.</p>
    <div>
      <h2>Part 1: The design process</h2>
      <a href="#part-1-the-design-process">
        
      </a>
    </div>
    
    <div>
      <h3>The problem</h3>
      <a href="#the-problem">
        
      </a>
    </div>
    <p>Let's be honest: nobody likes being asked to prove they're human. You know you're human. I know I'm human. The only one who doesn't seem convinced is that little widget standing between you and the website you're trying to access. At best, it's a minor inconvenience. At worst? You've probably wanted to throw your computer out the window in a fit of rage. We've all been there. And no one would blame you.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/640zjNaqDcNdJy4mYN6H14/ce184df68c9612d77f0767726bf27822/2.png" />
          </figure><p><sup><i>Turnstile integrated into a login flow</i></sup></p><p>As the world warms up to what appears to be an inevitable AI revolution, the need for security verification is only increasing. At Cloudflare, we've seen a significant rise in bot attacks — and in response, organizations are investing more heavily in security measures. That means more challenges being issued to more end users, more often.</p><p>The numbers tell the story:</p><p>2023: 2.14B daily</p><p>2024: 3B daily</p><p>2025: 5.35B daily</p><p>That's a 58.1% average increase in security checks, year over year. More security checks mean more opportunities for end user frustration. The more companies integrate these verification systems to protect themselves and their customers, the higher the chance that someone, somewhere, is going to have a bad experience.</p><p>We knew it was time to take a hard look at our flagship products and ask ourselves: Are we doing right by the billions of people who encounter these experiences? Are we fulfilling our mission to build a better Internet — not just a more secure one, but a more human one?</p><p>The answer, we discovered, was: we could do better.</p>
    <div>
      <h3>The design audit</h3>
      <a href="#the-design-audit">
        
      </a>
    </div>
    <p>Before redesigning anything, we needed to understand what we were working with. We started by conducting a comprehensive audit of every state, every error message, and every interaction across both Turnstile and Challenge Pages.</p><p>What we found wasn't the best.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1g1exDgeRH9QlApBXItcfL/fb0051d1dabaa6c91cf976ef64793502/3.png" />
          </figure><p><sup><i>The state of inconsistency in the Turnstile widget. Multiple states with no unified approach</i></sup></p><p>The inconsistencies were glaring. We had no unified approach across the multitude of different error scenarios. Some messages were overly verbose and technical ("Your device clock is set to a wrong time or this challenge page was accidentally cached by an intermediary and is no longer available"). Others were too vague to be helpful ("Timed out"). The visual language varied wildly — different layouts, different hierarchies, different tones of voice.</p><p>We also examined the feedback we'd received online. Social media, support tickets, community forums — we read it all. The frustration was palpable, and much of it was avoidable.</p><p>Take our feedback mechanism, for example. We offered users feedback options like "The widget sometimes fails" versus "The widget fails all the time." But what's the difference, really? And how were they supposed to know how often it failed? We were asking users to interpret ambiguous options during their most frustrated moments. The more we left open to interpretation, the less useful the feedback became — and the more frustration we saw across social channels.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5xKRSM0FfDZikEECwgHoof/ad55208973698cb237444c21d384aff8/4.png" />
          </figure><p><sup><i>The previous feedback screen: "The widget sometimes fails" vs "The widget fails all the time" — what's the difference?</i></sup></p><p>Our Challenge Pages — the full-page security blocks that appear when we detect suspicious activity or when site owners have heightened security settings — had similar issues. Some states were confusing. Others used too much technical jargon. Many failed to provide actionable guidance when users needed it most.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5JUxHjJ4VG13F7QfLONJEQ/fa443e5dd24f10d0c256864cd3f42734/5.png" />
          </figure><p><sup><i>The state of inconsistency on the Challenge pages. Multiple states with no unified approach</i></sup></p><p>The audit was humbling. But it gave us a clear picture of where we needed to focus.</p>
    <div>
      <h2>Mapping the user journey</h2>
      <a href="#mapping-the-user-journey">
        
      </a>
    </div>
    <p>To design better experiences, we first needed to understand every possible path a user could take. What was the happy path? Was there even one? And what were the unhappy paths that led to escalating frustration?</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1oTbFZoRu7guIxzoe64qcm/4f579fe2e70d6225a51504b3de10030f/6.png" />
          </figure><p><sup><i>Mapping the complete user journey — from initial encounter through error scenarios, with sentiment tracking</i></sup></p><p>This was a true cross-functional effort. We worked closely with engineers like Ana who knew the technical ins and outs of every edge case, and with Marina on the product side who understood not just how the product worked, but how users felt about it — the love and the hate we'd see online.</p><p>We have some of the smartest people working on bot protection at Cloudflare. But intelligence and clarity aren't the same thing. There's a delicate balance between technical complexity and user simplicity. Only when these two dance together successfully can we communicate information in a way that actually makes sense to people.</p><p>And here's the thing: the messaging has to work for everyone. A person of any age. Any mental or physical capability. Any cultural background. Any level of technical sophistication. That's what designing at scale really means — you can’t ignore edge cases, since, at such scale, they are no longer edge cases.</p>
    <div>
      <h2>Establishing a unified information architecture</h2>
      <a href="#establishing-a-unified-information-architecture">
        
      </a>
    </div>
    <p>One of the most influential books in UX design is Steve Krug's <a href="https://sensible.com/dont-make-me-think/"><u>Don't Make Me Think</u></a>. The core principle is simple: every moment a user spends trying to interpret, understand, or decode your interface is a moment of friction. And friction, especially in moments of frustration, leads to abandonment.</p><p>Our audit revealed that we were asking users to think far too much. Different pieces of information occupied the same space in the UI across different states. There was no consistent visual hierarchy. Users encountering an error state in Turnstile would find information in a completely different place than they would on a Challenge Page.</p><p>We made a fundamental decision: <b>one information architecture to rule them all</b>.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3runU0ihKhNpgdw3LxNZUv/aa4bd76efb5847fde0659bccdae7242d/7.png" />
          </figure><p><sup><i>Visual diagram displaying a unified information architecture with a consistent structure across Turnstile widget and Challenge pages</i></sup></p><p>Both Turnstile and Challenge Pages would now follow the same structural pattern. The same visual hierarchy. The same placement for actions, for explanatory text, for links to documentation.</p><p>Did this constrain our design options? Absolutely. We had to say no to a lot of creative ideas that didn't fit the framework. But constraints aren't the enemy of good design — they're often its best friend. By limiting our options, we could go deeper on the details that actually mattered.</p><p>For users, the benefit is profound: they don't need to re-learn what each piece of the UI means. Error states look consistent. Help links are always in the same place. Once you understand one state, you understand them all. That's cognitive load reduced to a minimum — exactly where it should be during a security verification.</p>
    <div>
      <h2>What user research taught us</h2>
      <a href="#what-user-research-taught-us">
        
      </a>
    </div>
    <p>How do you keep yourself accountable when redesigning something that billions of people see? You test. A lot.</p><p>We recruited 8 participants across 8 different countries, deliberately seeking diversity in age, digital savviness, and cultural background. We weren't looking for tech-savvy early adopters — we wanted to understand how the redesign would work for everyone.</p><p>Our approach was rigorous: participants saw both the current experience and proposed changes, without knowing which was "old" or "new." We counterbalanced positioning to eliminate bias. And we did not just test our new ideas, but also challenged our assumptions about what needed changing in the first place.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/59mmLHihbM9TewXmlYQwbO/e5db88efca948de1b31e9dc499195eb8/8.png" />
          </figure><p><sup><i>Two different versions of a Turnstile being tested in an A/B test</i></sup></p>
    <div>
      <h3>Some things didn’t need fixing</h3>
      <a href="#some-things-didnt-need-fixing">
        
      </a>
    </div>
    <p>One hypothesis: should we align with competitors? Most CAPTCHA providers show "I am human" across all states. We use distinct content — "Verify you are human," then "Verifying...," then "Success!"</p><p>Were we overcomplicating things? We tested it head-to-head.</p><p>Our approach won decisively. For the interactivity state, "Verify you are human" scored 5 out of 8 points versus just 3 for "I am human." For the verifying state, it was even more dramatic — 7.5 versus 0.5. Users wanted to know what was happening, not just be told what they were.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6ke1kO0i7EZxZm6voQBpyn/f489bef9b66d1221aa89adb5746559b7/9.png" />
          </figure><p><sup><i>User testing results: users strongly favored our approach over the competitor-style design</i></sup></p><p>This experiment didn't ship as a feature, but it was invaluable. It gave us confidence we weren't just being different for the sake of it. Some things were already right.</p>
    <div>
      <h3>But these needed to change</h3>
      <a href="#but-these-needed-to-change">
        
      </a>
    </div>
    <p>The research surfaced four areas where we were failing users:</p><p><b>Help, not bureaucracy</b>. When users encountered errors, we offered "Send Feedback." In testing, they were baffled. "Who am I sending this to? The website? Cloudflare? My ISP?" More importantly, we discovered something fundamental: at the moment of maximum frustration, people don't want to file a report — they want to fix the problem. We replaced "Send Feedback" with "Troubleshoot" — a single word that promises action rather than bureaucracy.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2jN2reUR55qCbssCDFTZfB/fb5396ec853ee549ebfec5d0d94b901f/10.png" />
          </figure><p><sup><i>The problematic "Send Feedback" prompt: users didn't know who they were sending feedback to</i></sup></p><p><b>Attention, not alarm</b>. We'd used red backgrounds liberally for errors. The reaction in testing was visceral — participants felt they had failed, felt powerless. Even for simple issues that would resolve with a retry, users assumed the worst and gave up. Red at full saturation wasn't communicating "Here's something to address." It was communicating "You have failed, and there's nothing you can do." The fix: red only for icons, never for text or backgrounds.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5seE6Xcrj9lvSpBYDEkk6N/7f0c1c17fd86b05397d35b685b0addfb/11.png" />
          </figure><p><sup><i>The evolution: from the states with unclear error state description in red to much clearer and concise error communication in neutral-color text.</i></sup></p><p><b>Scannable, not verbose</b>. We'd tried to be thorough, explaining errors in technical detail. It backfired. Non-technical users found it alienating. Technical users didn't need it. Everyone was trying to read it in the tiny real estate of a widget. The lesson: less is more, especially in constrained spaces during stressful moments.</p><p><b>Accessible to everyone</b>. Our audit revealed 10px fonts in some states. Grey text that technically met AA (at least 4.5:1 for normal text and 3:1 for large text) compliance but was difficult to read in practice. "Technically compliant" isn't good enough when you're serving the entire Internet.</p><p>We set a clear goal: to meet the <a href="https://www.w3.org/TR/WCAG22/"><u>WCAG 2.2 AAA</u></a> standard— the highest and most stringent level of web accessibility compliance, designed to make content accessible to the broadest range of users, including those with severe disabilities. Throughout the redesign, when visual consistency conflicted with readability, readability won. Every time.</p><p>This extended beyond vision. We designed for screen reader users, keyboard-only navigators, and people with color vision variations — going beyond what automated compliance tools can catch.</p><p>And accessibility isn't just about impairments — it's about language. What fits in English, overflows in German. What's concise in Spanish is ambiguous in Japanese. Supporting over 40 languages forced us to radically simplify. The same "Unable to connect to website / Troubleshoot" pattern now works across English, Bulgarian, Danish, German, Greek, Japanese, Indonesian, Russian, Slovak, Slovenian, Serbian, Filipino, and many more.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6e4pvgMUS4BUXsPqi1qV6l/b6ffdc0d5f1e8e90394169db7162d10c/12.png" />
          </figure><p><sup><i>The redesigned error state across 12 languages — consistent layout despite varying text lengths </i></sup></p>
    <div>
      <h2>Final redesign</h2>
      <a href="#final-redesign">
        
      </a>
    </div>
    <p>So what did we actually ship?</p><p>First, let's talk about what we didn't change. The happy path — "Verify you are human" → "Verifying..." → "Success!" — tested exceptionally well. Users understood what was happening at each stage. The distinct content for each state, which we'd worried might be overcomplicating things, was actually our competitive advantage.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2R4QJ04uz9r1TVZjuqsHG9/61c1023eaa105b4841456258f3370220/13.png" />
          </figure><p><i><sup> The happy path: Verify you are human → Verifying → Success! These states tested well and remained largely unchanged</sup></i></p><p>But for the states that needed work, we made significant changes guided by everything we learned.</p>
    <div>
      <h3>Simplified, scannable content</h3>
      <a href="#simplified-scannable-content">
        
      </a>
    </div>
    <p>We radically reduced the amount of text in error states. Instead of verbose explanations like "Your device clock is set to a wrong time or this challenge page was accidentally cached by an intermediary and is no longer available," we now show:</p><ol><li><p>A clear, simple state name (e.g., "Incorrect device time")</p></li><li><p>A prominent "Troubleshoot" link</p></li></ol><p>That's it. The detailed guidance now lives in a dedicated modal screen that opens when users need it — giving them room to actually read and follow troubleshooting steps.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4ZYjlJgw6DOiTuJBFXpewn/5d714c3a19723dfe9fa9802d0d5926b8/14.png" />
          </figure><p><sup><i>The troubleshooting modal: detailed guidance when users need it, without cluttering the widget</i></sup></p><p>The troubleshooting modal provides context ("This error occurs when your device's clock or calendar is inaccurate. To complete this website’s security verification process, your device must be set to the correct date and time in your time zone."), numbered steps to try, links to documentation, and — only after the user has tried to resolve the issue — an option to submit feedback to Cloudflare. Help first, feedback second.</p>
    <div>
      <h3>AAA accessibility compliance</h3>
      <a href="#aaa-accessibility-compliance">
        
      </a>
    </div>
    <p>Every state now meets WCAG 2.2 AAA standards for contrast and readability. Font sizes have established minimums. Interactive elements are clearly focusable and properly announced by screen readers.</p>
    <div>
      <h3>Unified experience across Turnstile and Challenge pages</h3>
      <a href="#unified-experience-across-turnstile-and-challenge-pages">
        
      </a>
    </div>
    <p>Whether users encounter the compact Turnstile widget or a full Challenge Page, the information architecture is now consistent. Same hierarchy. Same placement. Same mental model.</p><p>Challenge Pages now follow a clean structure: the website name and favicon at the top, a clear status message (like "Verification successful" or "Your browser is out of date"), and actionable guidance below. No more walls of orange or red text. No more technical jargon without context.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4PuWePTOaLihpfqm2iimJW/e34c4a009c36524a6d72c15ae0f78d00/15.png" />
          </figure><p><sup><i>Re-designed Challenge page states with clear troubleshooting instructions.</i></sup></p>
    <div>
      <h3>Validated across languages</h3>
      <a href="#validated-across-languages">
        
      </a>
    </div>
    <p>Every piece of content was tested in over 40 supported languages. Our process involved three layers of validation:</p><ol><li><p>Initial design review by the design team</p></li><li><p>Professional translation by our qualified vendor</p></li><li><p>Final review by native-speaking Cloudflare employees</p></li></ol><p>This wasn't just about translation accuracy — it was about ensuring the visual design held up when content length varied dramatically between languages.</p>
    <div>
      <h3>The complete picture</h3>
      <a href="#the-complete-picture">
        
      </a>
    </div>
    <p>The result is a security verification experience that's clearer, more accessible, less frustrating, and — crucially — just as secure. We didn't compromise on protection to improve the experience. We proved that good design and strong security aren't in conflict.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5t6FRRzLamGaTbEiZqVpnf/92b688679d1c8265ba3c6fd4159061bf/16.png" />
          </figure><p><sup><i>Re-designed Turnstile widgets on the left and a re-designed Challenge page on the right</i></sup></p><p>But designing the experience was only half the battle. Shipping it to billions of users? That's where Ana comes in.</p>
    <div>
      <h2>Part 2: Shipping to billions</h2>
      <a href="#part-2-shipping-to-billions">
        
      </a>
    </div>
    
    <div>
      <h4><b>Beyond centering a div</b></h4>
      <a href="#beyond-centering-a-div">
        
      </a>
    </div>
    <p>Some may say the hardest part of being a Frontend Engineer is centering a div. In reality, the real challenge often lies much deeper, especially when working close to the platform primitives. Building a critical piece of Internet infrastructure using native APIs forces you to think differently about UI development, tradeoffs, and long-term maintainability.</p><p>In our case, we use Rust to handle the UI for both the Turnstile widget and the Challenge page. This decision brought clear benefits in terms of safety and consistency across platforms, but it also increased frontend complexity. Many of us are used to the ergonomics of modern frameworks like React, where common UI interactions come almost for free. Working with Rust meant reimplementing even simple interactions using lower level constructs like <i>document.getElementById</i>, <i>createElement</i>, and <i>appendChild</i>.</p><p>On top of that, compile times and strict checks naturally slowed down rapid UI iteration compared to JavaScript based frameworks. Debugging was also more involved, as the tooling ecosystem is still evolving. These constraints pushed us to be more deliberate, more thoughtful, and ultimately more disciplined in how we approached UI development.</p>
    <div>
      <h4><b>Small visual changes, big global impact</b></h4>
      <a href="#small-visual-changes-big-global-impact">
        
      </a>
    </div>
    <p>What initially looked like small visual tweaks such as padding adjustments or alignment changes quickly revealed a much bigger challenge: internationalization.</p><p>Once translations were available, we had to ensure that content remained readable and usable across 38 languages and 16 different UI states. Text length variability alone required careful design decisions. Some translations can be 30 to 300 percent longer than English. A short English string like “Stuck?” becomes “Tidak bisa melanjutkan?” in Indonesian or “Es geht nicht weiter?” in German, dramatically changing layout requirements.</p><p>Right-to-left language support added another layer of complexity. Supporting Arabic, Persian or Farsi, and Hebrew meant more than flipping text direction. Entire layouts had to be mirrored, including alignment, navigation patterns, directional icons, and animation flows. Many of these elements are implicitly designed with left-to-right assumptions, so we had to revisit those decisions and make them truly bidirectional.</p><p>Ordered lists also required special care. Not every culture uses the Western 1, 2, 3 numbering system, and hardcoding numeric sequences can make interfaces feel foreign or incorrect. We leaned on locale-aware numbering and fully translatable list formats to ensure ordering felt natural and culturally appropriate in every language.</p>
    <div>
      <h4><b>Building confidence through testing</b></h4>
      <a href="#building-confidence-through-testing">
        
      </a>
    </div>
    <p>As we started listing action points in feedback reports, correctness became even more critical. Every action needed to render properly, trigger the right flow, and behave consistently across states, languages, and edge cases.</p><p>To get there, we invested heavily in testing. Unit tests helped us validate logic in isolation, while end-to-end tests ensured that new states and languages worked as expected in real scenarios. This testing foundation gave us confidence to iterate safely, prevented regressions, and ensured that feedback reports remained reliable and actionable for users.</p>
    <div>
      <h4><b>The outcome</b></h4>
      <a href="#the-outcome">
        
      </a>
    </div>
    <p>What began as a set of technical constraints turned into an opportunity to build a more robust, inclusive, and well-tested UI system. Working with fewer abstractions and closer to the browser primitives forced us to rethink assumptions, improve our internationalization strategy, and raise the overall quality bar.</p><p>The result is not just a solution that works, but one we trust. And that trust is what allows us to keep improving, even when centering a div turns out to be the easy part.</p>
    <div>
      <h2>Part 3: The impact</h2>
      <a href="#part-3-the-impact">
        
      </a>
    </div>
    <p>Designing for billions of people is a responsibility we take seriously. At this scale, it is essential to leverage measurable data to tell us the real impact of our design choices. As we prepare to roll out these changes, we are focusing on <b>five key metrics</b> that will tell us if we’ve truly succeeded in making the Internet’s most-seen UI more human.</p>
    <div>
      <h4><b>1. Challenge Completion Rate</b></h4>
      <a href="#1-challenge-completion-rate">
        
      </a>
    </div>
    <p>Our primary north star is the <b>Challenge Solve Rate: </b>the percentage of issued challenges that are successfully completed. By moving away from technical jargon like "intermediary caching" and toward simple, actionable labels like "Incorrect device time," we expect a significant uptick in CSR. A higher CSR doesn't mean we're being easier on bots; it means we’re removing the hurdles that were accidentally tripping up legitimate human users.</p>
    <div>
      <h4><b>2. Time to Complete</b></h4>
      <a href="#2-time-to-complete">
        
      </a>
    </div>
    <p>Every second a user spends on a challenge page is a second they aren't getting the information that they need. Our research showed that users were often paralyzed by choice when seeing a wall of red text. With our new scannable, neutral-color design, we are tracking <b>Time to Complete</b> to ensure users can identify and resolve issues in seconds rather than minutes.</p>
    <div>
      <h4><b>3. Abandonment Rate Changes</b></h4>
      <a href="#3-abandonment-rate-changes">
        
      </a>
    </div>
    <p>In the past, our liberal use of "saturated red" caused a visceral reaction: users felt they had failed and simply gave up. By reserving red only for icons and using a unified architecture, we aim to reduce Abandonment Rates. We want users to feel empowered to click Troubleshoot rather than feeling powerless and clicking away.</p>
    <div>
      <h4><b>4. Support Ticket Volume</b></h4>
      <a href="#4-support-ticket-volume">
        
      </a>
    </div>
    <p>One of the bigger shifts from a product perspective is our new Troubleshooting Modal. By providing clear, numbered steps directly within the widget, we are building self-service support into the UI. We expect this to result in a measurable decrease in support ticket volume for both our customers and our own internal teams.</p>
    <div>
      <h4><b>5. Social Sentiment</b></h4>
      <a href="#5-social-sentiment">
        
      </a>
    </div>
    <p>We know that security challenges are rarely loved, but they shouldn't be hated because they are confusing. We are monitoring <b>Social Sentiment</b> across community forums, feedback reports, and social channels to see if the conversation shifts from "this widget is broken" to "I had an issue, but I fixed it".</p><p>As a Product Manager, my goal is often invisible security — the best challenge is the one the user never sees. But when a challenge <i>must</i> be seen, it should be an assistant, not a bouncer. This redesign proves that <b>AAA accessibility</b> and <b>high-security standards</b> aren't in competition; they are two sides of the same coin. By unifying the architecture of Turnstile and Challenge Pages, we’ve built a foundation that allows us to iterate faster and protect the Internet more humanely than ever before.</p>
    <div>
      <h2>Looking ahead</h2>
      <a href="#looking-ahead">
        
      </a>
    </div>
    <p>This redesign is a foundation, not a finish line.</p><p>We're continuing to monitor how users interact with the new experience, and we're committed to iterating based on what we learn. The feedback mechanisms we've built into the new design — the ones that actually help users troubleshoot, rather than just asking them to report problems — will give us richer insights than we've ever had before.</p><p>We're also watching how the security landscape evolves. As bot attacks grow more sophisticated, and as AI continues to blur the line between human and automated behavior, the challenge of verification will only get harder. Our job is to stay ahead — to keep improving security without making the human experience worse.</p><p>If you encounter the new Turnstile or Challenge Pages and have feedback, we want to hear it. Reach out through our <a href="https://community.cloudflare.com/"><u>community forums</u></a> or use the feedback mechanisms built into the experience itself.</p> ]]></content:encoded>
            <category><![CDATA[Security Week]]></category>
            <category><![CDATA[Turnstile]]></category>
            <category><![CDATA[Challenge Page]]></category>
            <category><![CDATA[Design]]></category>
            <category><![CDATA[Product Design]]></category>
            <category><![CDATA[User Research]]></category>
            <category><![CDATA[Bots]]></category>
            <category><![CDATA[Bot Management]]></category>
            <category><![CDATA[WAF]]></category>
            <category><![CDATA[Engineering]]></category>
            <category><![CDATA[Product News]]></category>
            <category><![CDATA[Accessibility]]></category>
            <guid isPermaLink="false">19fiiQAG0XsaS9p0daOBus</guid>
            <dc:creator>Leo Bacevicius</dc:creator>
            <dc:creator>Ana Foppa</dc:creator>
            <dc:creator>Marina Elmore</dc:creator>
        </item>
        <item>
            <title><![CDATA[Building Jetflow: a framework for flexible, performant data pipelines at Cloudflare]]></title>
            <link>https://blog.cloudflare.com/building-jetflow-a-framework-for-flexible-performant-data-pipelines-at-cloudflare/</link>
            <pubDate>Wed, 23 Jul 2025 14:00:00 GMT</pubDate>
            <description><![CDATA[ Faced with a data-ingestion challenge at a massive scale, Cloudflare's Business Intelligence team built a new framework called Jetflow. ]]></description>
            <content:encoded><![CDATA[ <p>The Cloudflare Business Intelligence team manages a <a href="https://simple.wikipedia.org/wiki/Petabyte"><u>petabyte</u></a>-scale <a href="https://www.cloudflare.com/learning/cloud/what-is-a-data-lake/"><u>data lake</u></a> and ingests thousands of tables every day from many different sources. These include internal databases such as Postgres and ClickHouse, as well as external SaaS applications such as Salesforce. These tasks are often complex and tables may have hundreds of millions or billions of rows of new data each day. They are also business-critical for product decisions, growth plannings, and internal monitoring. In total, about <b>141 billion rows</b> are ingested every day.</p><p>As Cloudflare has grown, the data has become ever larger and more complex. Our existing <a href="https://www.ibm.com/think/topics/elt"><u>Extract Load Transform (ELT)</u></a> solution could no longer meet our technical and business requirements. After evaluating other common ELT solutions, we concluded that their performance generally did not surpass our current system, either.</p><p>It became clear that we needed to build our own framework to cope with our unique requirements — and so <b>Jetflow</b> was born. </p>
    <div>
      <h2>What we achieved</h2>
      <a href="#what-we-achieved">
        
      </a>
    </div>
    <p><b>Over 100x efficiency improvement in GB-s</b>:</p><ul><li><p>Our longest running job with 19 billion rows was taking <b>48 hours</b> using <b>300 GB of memory</b>, and now completes in <b>5.5 hours</b> using <b>4 GB of memory</b></p></li><li><p>We estimate that ingestion of 50 TB from Postgres via <b>Jetflow</b> could cost under $100 based on rates published by commercial cloud providers</p></li></ul><p><b>&gt;10x performance improvement:</b></p><ul><li><p>Our largest dataset was ingesting <b>60-80,000</b> rows per second, this is now <b>2-5 million</b> rows per second per database connection.</p></li><li><p>In addition, these numbers scale well with multiple database connections for some databases.</p></li></ul><p><b>Extensibility: </b></p><ul><li><p>The modular design makes it easy to extend and test<b>. </b>Today<b> Jetflow</b> works with ClickHouse, Postgres, Kafka, many different SaaS APIs, Google BigQuery and many others. It has continued to work well and remain flexible with the addition of new use cases.</p></li></ul>
    <div>
      <h2>How did we do this?</h2>
      <a href="#how-did-we-do-this">
        
      </a>
    </div>
    
    <div>
      <h3>Requirements</h3>
      <a href="#requirements">
        
      </a>
    </div>
    <p>The first step to designing our new framework had to be a clear understanding of the problems we were aiming to solve, with clear requirements to stop us creating new ones.</p>
    <div>
      <h5>Performant &amp; efficient</h5>
      <a href="#performant-efficient">
        
      </a>
    </div>
    <p>We needed to be able to move more data in less time as some ingestion jobs were taking ~24 hours, and our data will only grow. The data should be ingested in a streaming fashion and use less memory and compute resources than our existing solution.</p>
    <div>
      <h5>Backwards compatible </h5>
      <a href="#backwards-compatible">
        
      </a>
    </div>
    <p>Given the daily ingestion of thousands of tables, the chosen solution needed to allow for the migration of individual tables as needed. Due to our usage of <a href="https://spark.apache.org/"><u>Spark</u></a> downstream and Spark's limitations in merging disparate <a href="https://parquet.apache.org/"><u>Parquet</u></a> schemas, the chosen solution had to offer the flexibility to generate the precise schemas needed for each case to match legacy.</p><p>We also required seamless integration with our custom metadata system, used for dependency checks and job status information.</p>
    <div>
      <h5>Ease of use</h5>
      <a href="#ease-of-use">
        
      </a>
    </div>
    <p>We want a configuration file that can be version-controlled, without introducing bottlenecks on repositories with many concurrent changes.</p><p>To increase accessibility for different roles within the team, another requirement was no-code (or configuration as code) in the vast majority of cases. Users should not have to worry about availability or translation of data types between source and target systems, or writing new code for each new ingestion. The configuration needed should also be minimal — for example, data schema should be inferred from the source system and not need to be supplied by the user.</p>
    <div>
      <h5>Customizable</h5>
      <a href="#customizable">
        
      </a>
    </div>
    <p>Striking a balance with the no-code requirement above, although we want a low bar of entry we also want to have the option to tune and override options if desired, with a flexible and optional configuration layer. For example, writing Parquet files is often more expensive than reading from the database, so we want to be able to allocate more resources and concurrency as needed. </p><p>Additionally, we wanted to allow for control over where the work is executed, with the ability to spin up concurrent workers in different threads, different containers, or on different machines. The execution of workers and communication of data was abstracted away with an interface, and different implementations can be written and injected, controlled via the job configuration. </p>
    <div>
      <h5>Testable</h5>
      <a href="#testable">
        
      </a>
    </div>
    <p>We wanted a solution capable of running locally in a containerized environment, which would allow us to write tests for every stage of the pipeline. With “black box” solutions, testing often means validating the output after making a change, which is a slow feedback loop, risks not testing all edge cases as there isn’t good visibility of all code paths internally, and makes debugging issues painful.</p>
    <div>
      <h3>Designing a flexible framework </h3>
      <a href="#designing-a-flexible-framework">
        
      </a>
    </div>
    <p>To build a truly flexible framework, we broke the pipeline down into distinct stages, and then create a config layer to define the composition of the pipeline from these stages, and any configuration overrides. Every pipeline configuration that makes sense logically should execute correctly, and users should not be able to create pipeline configs that do not work. </p>
    <div>
      <h5>Pipeline configuration</h5>
      <a href="#pipeline-configuration">
        
      </a>
    </div>
    <p>This led us to a design where we created stages which were classified according to the meaningfully different categories of:</p><ul><li><p>Consumers</p></li><li><p>Transformers</p></li><li><p>Loaders</p></li></ul>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1ALi9AXyo5v1Y7cIjK619V/3c0735e2d5b36d5660072f92fe551ed3/image3.png" />
          </figure><p>The pipeline was constructed via a <a href="https://yaml.org/"><u>YAML</u></a> file that required a consumer, zero or more transformers, and at least one loader. Consumers create a data stream (via reading from the source system), Transformers (e.g. data transformations, validations) take a data stream input and output a data stream conforming to the same API so that they can be chained, and Loaders have the same data streaming interface, but are the stages with persistent effects — i.e. stages where data is saved to an external system. </p><p>This modular design means that each stage is independently testable, with shared behaviour (such as error handling and concurrency) inherited from shared base stages, significantly decreasing development time for new use cases and increasing confidence in code correctness.</p>
    <div>
      <h5>Data divisions</h5>
      <a href="#data-divisions">
        
      </a>
    </div>
    <p>Next, we designed a breakdown for the data that would allow the pipeline to be idempotent both on whole pipeline re-run and also on internal retry of any data partition due to transient error. We decided on a design that let us parallelize processing, while maintaining meaningful data divisions that allowed the pipeline to perform cleanups of data where required for a retry.</p><ul><li><p><b>RunInstance</b>: the least granular division, corresponding to a business unit for a single run of the pipeline (e.g. one month/day/hour of data). </p></li><li><p><b>Partition</b>: a division of the RunInstance that allows each row to be allocated to a partition in a way that is deterministic and self-evident from the row data without external state, and is therefore idempotent on retry. (e.g. an accountId range, a 10-minute interval)</p></li><li><p><b>Batch</b>: a division of the partition data that is non-deterministic and used only to break the data down into smaller chunks for streaming/parallel processing for faster processing with fewer resources. (e.g. 10k rows, 50 MB)</p></li></ul><p>The options that the user configures in the consumer stage YAML both construct the query that is used to retrieve the data from the source system, and also encode the semantic meaning of this data division in a system agnostic way, so that later stages understand what this data represents — e.g. this partition contains the data for all accounts IDs 0-500. This means that we can do targeted data cleanup and avoid, for example, duplicate data entries if a single data partition is retried due to error.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1NQsitJmwRwSpiLkj2Hoig/81db1750523268bb427d51e1d2746a46/image2.png" />
          </figure>
    <div>
      <h3>Framework implementation</h3>
      <a href="#framework-implementation">
        
      </a>
    </div>
    
    <div>
      <h5>Standard internal state for stage compatibility </h5>
      <a href="#standard-internal-state-for-stage-compatibility">
        
      </a>
    </div>
    <p>Our most common use case is something like read from a database, convert to Parquet format, and then save to <a href="https://www.cloudflare.com/learning/cloud/what-is-object-storage/">object storage,</a> with each of these steps being a separate stage. As more use cases were onboarded to <b>Jetflow,</b> we had to make sure that if someone wrote a new stage it would be compatible with the other stages. We don’t want to create a situation where new code needs to be written for every output format and target system, or you end up with a custom pipeline for every different use case.</p><p>The way we have solved this problem is by having our stage extractor class only allow output data in a single format. This means as long as any downstream stages support this format as in the input and output format they would be compatible with the rest of the pipeline. This seems obvious in retrospect, but internally was a painful learning experience, as we originally created a custom type system and struggled with stage interoperability. </p><p>For this internal format, we chose to use <a href="https://arrow.apache.org/"><u>Arrow</u></a>, an in-memory columnar data format. The key benefits of this format for us are:</p><ul><li><p><b>Arrow ecosystem</b>: Many data projects now support Arrow as an output format. This means when we write extractor stages for new data sources, it is often trivial to produce Arrow output.</p></li><li><p><b>No serialisation overhead</b>: This makes it easy to move Arrow data between machines and even programming languages with minimum overhead. <b>Jetflow</b> was designed from the start to have the flexibility to be able to run in a wide range of systems via a job controller interface, so this efficiency in data transmission means there’s minimal compromise on performance when creating distributed implementations.</p></li><li><p><b>Reserve memory in large fixed-size batches to avoid memory allocations</b>: As Go is a garbage collected (GC) language and GC cycle times are affected mostly by the number of objects rather than the sizes of those objects, fewer heap objects reduces CPU time spent garbage collecting significantly, even if the total size is the same. As the number of objects to scan, and possibly collect, during a GC cycle increases with the number of allocations, if we have 8192 rows with 10 columns each, Arrow would only require us to do 10 allocations versus the 8192 allocations of most drivers that allocate on a row by row basis, meaning fewer objects and lower GC cycle times with Arrow.</p></li></ul>
    <div>
      <h5>Converting rows to columns</h5>
      <a href="#converting-rows-to-columns">
        
      </a>
    </div>
    <p>Another important performance optimization was reducing the number of conversion steps that happen when reading and processing data. Most data ingestion frameworks internally represent data as rows. In our case, we are mostly writing data in Parquet format, which is column based. When reading data from column-based sources (e.g. ClickHouse, where most drivers receive RowBinary format), converting into row-based memory representations for the specific language implementation is inefficient. This is then converted again from rows to columns to write Parquet files. These conversions result in a significant performance impact.</p><p><b>Jetflow</b> instead reads data from column-based sources in columnar formats (e.g. for ClickHouse-native Block format) and then copies this data into Arrow column format. Parquet files are then written directly from Arrow columns. The simplification of this process improves performance.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5HEuO0Cn6Wob7tuR9hjnSP/852cdc44244f107b4289fb3b3553d213/image1.png" />
          </figure>
    <div>
      <h3>Writing each pipelines stage</h3>
      <a href="#writing-each-pipelines-stage">
        
      </a>
    </div>
    
    <div>
      <h5>Case study: ClickHouse</h5>
      <a href="#case-study-clickhouse">
        
      </a>
    </div>
    <p>When testing an initial version of <b>Jetflow</b>, we discovered<b> </b>that due to the architecture of ClickHouse, using additional connections would not be of any benefit, since ClickHouse was reading faster than we were receiving data. It should then be possible, with a more optimized database driver, to take better advantage of that single connection to read a much larger number of rows per second, without needing additional connections.</p><p>Initially, a custom database driver was written for ClickHouse, but we ended up switching to the excellent <a href="https://github.com/ClickHouse/ch-go"><u>ch-go low level library</u></a>, which directly reads <a href="https://clickhouse.com/docs/development/architecture#block"><u>Blocks</u></a> from ClickHouse in a columnar format. This had a dramatic effect on performance in comparison to the standard Go driver. Combined with the framework optimisations above, we now <b>ingest millions of rows per second</b> with a single ClickHouse connection.</p><p>A valuable lesson learned is that as with any software, tradeoffs are often made for the sake of convenience or a common use case that may not match your own. Most database drivers tend not to be optimized for reading large batches of rows, and have high per-row overhead.</p>
    <div>
      <h5>Case study: Postgres</h5>
      <a href="#case-study-postgres">
        
      </a>
    </div>
    <p>For Postgres, we use the excellent <a href="https://github.com/jackc/pgx"><u>jackc/pgx</u></a> driver, but instead of using the database/sql Scan interface, we directly receive the raw bytes for each row and use the jackc/pgx internal scan functions for each Postgres OID (Object Identifier) type.</p><p>The database/sql Scan interface in Go uses reflection to understand the type passed to the function and then also uses reflection to set each field with the column value received from Postgres. In typical scenarios, this is fast enough and easy to use, but falls short for our use cases in terms of performance. The <a href="https://github.com/jackc/pgx"><u>jackc/pgx</u></a> driver reuses the row bytes produced each time the next Postgres row is requested, resulting in zero allocations per row. This allows us to write high-performance, low-allocation code within Jetflow. With this design, we are able to achieve nearly <b>600,000 rows per second</b> per Postgres connection for most tables, with very low memory usage.</p>
    <div>
      <h2>Conclusion</h2>
      <a href="#conclusion">
        
      </a>
    </div>
    <p>As of early July 2025, the team ingests <b>77 billion</b> records per day via <b>Jetflow</b>. The remaining jobs are in the process of being migrated to <b>Jetflow</b>, which will bring the total daily ingestion to 141 billion records. The framework has allowed us to ingest tables in cases that would not otherwise have been possible, and provided significant cost savings due to ingestions running for less time and with fewer resources. </p><p>In the future, we plan to open source the project, and if you are interested in joining our team to help develop tools like this, then open roles can be found at <a href="https://www.cloudflare.com/en-gb/careers/jobs/"><u>https://www.cloudflare.com/careers/jobs/</u></a>.</p> ]]></content:encoded>
            <category><![CDATA[Data]]></category>
            <category><![CDATA[Go]]></category>
            <category><![CDATA[Performance]]></category>
            <category><![CDATA[Design]]></category>
            <category><![CDATA[Engineering]]></category>
            <guid isPermaLink="false">4wAX6JGDuRNIJwVqwdgrP8</guid>
            <dc:creator>Harry Hough</dc:creator>
            <dc:creator>Rebecca Walton-Jones </dc:creator>
            <dc:creator>Andy Fan</dc:creator>
            <dc:creator>Ricardo Margalhau</dc:creator>
            <dc:creator>Uday Sharma</dc:creator>
        </item>
        <item>
            <title><![CDATA[A new WAF experience]]></title>
            <link>https://blog.cloudflare.com/new-waf-experience/</link>
            <pubDate>Tue, 15 Mar 2022 12:59:06 GMT</pubDate>
            <description><![CDATA[ The security landscape is moving fast. We invited users to help us shape a new WAF experience that enables us to evolve WAF to meet their demands and use cases ]]></description>
            <content:encoded><![CDATA[ 
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5AshmKNvJcvQs9VcqUCAp8/b5128c88e4eb56e13d06b710e2b9861b/image2-28.png" />
            
            </figure><p>Around three years ago, we brought multiple features into the <a href="/new-firewall-tab-and-analytics/">Firewall tab</a> in our dashboard navigation, with the motivation “to make our products and services intuitive.” With our hard work in <a href="/tag/waf/">expanding capabilities offerings</a> in the past three years, we want to take another opportunity to evaluate the intuitiveness of <a href="https://www.cloudflare.com/waf/">Cloudflare WAF (Web Application Firewall)</a>.</p>
    <div>
      <h3>Our customers lead the way to new WAF</h3>
      <a href="#our-customers-lead-the-way-to-new-waf">
        
      </a>
    </div>
    <p>The security landscape is moving fast; types of web applications are growing rapidly; and within the industry there are various approaches to what a <a href="https://www.cloudflare.com/learning/ddos/glossary/web-application-firewall-waf/">WAF</a> includes and can offer. Cloudflare not only proxies enterprise applications, but also millions of personal blogs, community sites, and small businesses stores. The diversity of use cases are covered by various products we offer; however, these products are currently scattered and that makes visibility of active protection rules unclear. This pushes us to reflect on how we can best support our customers in getting the most value out of WAF by providing a clearer offering that meets expectations.</p><p>A few months ago, we reached out to our customers to answer a simple question: what do you consider to be part of WAF? We employed a range of user research methods including card sorting, tree testing, design evaluation, and surveys to help with this. The results of this research illustrated how our customers think about WAF, what it means to them, and how it supports their use cases. This inspired the product team to expand scope and contemplate what (Web Application) Security means, beyond merely the WAF.</p><p>Based on what hundreds of customers told us, our user research and product design teams collaborated with product management to rethink the security experience. We examined our assumptions and assessed the effectiveness of design concepts to create a structure (or information architecture) that reflected our customers’ mental models.</p><p>This new structure consolidates firewall rules, managed rules, and rate limiting rules to become a part of WAF. The new WAF strives to be the one-stop shop for web application security as it pertains to differentiating malicious from clean traffic.</p><p>As of today, you will see the following changes to our navigation:</p><ol><li><p><b>Firewall</b> is being renamed to <b>Security.</b></p></li><li><p>Under <b>Security,</b> you will now find <b>WAF.</b></p></li><li><p>Firewall rules, managed rules, and rate limiting rules will now appear under <b>WAF</b>.</p></li></ol><blockquote><p>From now on, when we refer to <b>WAF,</b> we will be referring to above three features.</p></blockquote><p>Further, some important updates are coming for these features. Advanced rate limiting rules will be launched as part of <a href="/welcome-security-week-2022/">Security Week</a>, and every customer will also get a free set of managed rules to <a href="/waf-for-everyone">protect all traffic from high profile vulnerabilities</a>. And finally, in the next few months, firewall rules will move to the <a href="https://developers.cloudflare.com/ruleset-engine/">Ruleset Engine</a>, adding more powerful capabilities thanks to the new Ruleset API. Feeling excited?</p>
    <div>
      <h3>How customers shaped the future of WAF</h3>
      <a href="#how-customers-shaped-the-future-of-waf">
        
      </a>
    </div>
    <p>Almost 500 customers participated in this user research study that helped us learn about needs and context of use. We employed four research methods, all of which were conducted in an unmoderated manner; this meant people around the world could participate remotely at a time and place of their choosing.</p><ul><li><p>Card sorting involved participants grouping navigational elements into categories that made sense to them.</p></li><li><p>Tree testing assessed how well or poorly a proposed navigational structure performed for our target audience.</p></li><li><p>Design evaluation involved a task-based approach to measure effectiveness and utility of design concepts.</p></li><li><p>Survey questions helped us dive deeper into results, as well as painting a picture of our participants.</p></li></ul><p>Results of this four-pronged study informed changes to both WAF and Security that are detailed below.</p>
    <div>
      <h3>The new WAF experience</h3>
      <a href="#the-new-waf-experience">
        
      </a>
    </div>
    <p>The final result reveals the WAF as part of a broader <a href="https://dash.cloudflare.com/?to=/:account/:zone/security">Security category</a>, which also includes Bots, DDoS, API Shield and Page Shield. This destination enables you to create your rules (a.k.a. firewall rules), deploy Cloudflare managed rules, set rate limit conditions, and includes handy tools to protect your web applications.</p><p>All customers across <a href="https://www.cloudflare.com/plans/">all plans</a> will now see the WAF products organized as below:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/777dWCpcmkac0c5KHZz4jp/6728da9e7d713d567a524faeb7f0b905/image1-29.png" />
            
            </figure><ol><li><p><b>Firewall rules</b> allow you to create custom, user-defined logic by blocking or allowing traffic that leverages all the components of the HTTP requests and dynamic fields computed by Cloudflare, such as Bot score.</p></li><li><p><b>Rate limiting rules</b> include the traditional IP-based product we launched back in 2018 and the newer Advanced Rate Limiting for ENT customers on the Advanced plan (coming soon).</p></li><li><p><b>Managed rules</b> allows customers to deploy sets of rules managed by the Cloudflare analyst team. These rulesets include a “Cloudflare Free Managed Ruleset” currently being rolled out <a href="/waf-for-everyone">for all plans</a> including FREE, as well as Cloudflare Managed, OWASP implementation, and Exposed Credentials Check for all paying plans.</p></li><li><p><b>Tools</b> give access to IP Access Rules, Zone Lockdown and User Agent Blocking. Although still actively supported, these products cover specific use cases that can be covered using firewall rules. However, they remain a part of the WAF toolbox for convenience.</p></li></ol>
    <div>
      <h3>Redesigning the WAF experience</h3>
      <a href="#redesigning-the-waf-experience">
        
      </a>
    </div>
    <p>Gestalt design principles suggest that “elements which are close in proximity to each other are perceived to share similar functionality or traits.” This principle in addition to the input from our customers informed our design decisions.</p><p>After reviewing the responses of the study, we understood the importance of making it easy to find the security products in the Dashboard, and the need to make it clear how particular products were related to or worked together with each other.</p><p>Crucially, the page needed to:</p><ul><li><p>Display each type of rule we support, i.e. firewall rules, rate limiting rules and managed rules</p></li><li><p>Show the usage amount of each type</p></li><li><p>Give the customer the ability to add a new rule and manage existing rules</p></li><li><p>Allow the customer to reprioritise rules using the existing drag and drop behavior</p></li><li><p>Be flexible enough to accommodate future additions and consolidations of WAF features</p></li></ul><p>We iterated on multiple options, including predominantly vertical page layouts, table based page layouts, and even accordion based page layouts. Each of these options, however, would force us to replicate buttons of similar functionality on the page. With the risk of causing additional confusion, we abandoned these options in favor of a horizontal, tabbed page layout.</p>
    <div>
      <h3>How can I get it?</h3>
      <a href="#how-can-i-get-it">
        
      </a>
    </div>
    <p>As of today, we are launching this new design of WAF to everyone! In the meantime, we are updating documentation to walk you through how to maximize the power of Cloudflare WAF.</p>
    <div>
      <h3>Looking forward</h3>
      <a href="#looking-forward">
        
      </a>
    </div>
    <p>This is a starting point of our journey to make Cloudflare WAF not only powerful but also easy to adapt to your needs. We are evaluating approaches to empower your decision-making process when protecting your web applications. Among growing intel information and more rules creation possibilities, we want to shorten your path from a possible threat detection (such as by security overview) to setting up the right rule to mitigate such threat. Stay tuned!</p> ]]></content:encoded>
            <category><![CDATA[Security Week]]></category>
            <category><![CDATA[WAF]]></category>
            <category><![CDATA[Firewall]]></category>
            <category><![CDATA[Security]]></category>
            <category><![CDATA[Product Design]]></category>
            <category><![CDATA[Design]]></category>
            <guid isPermaLink="false">2UUR6KEw3qV6N5GMCAV7eS</guid>
            <dc:creator>Zhiyuan Zheng</dc:creator>
            <dc:creator>Mru Kodali</dc:creator>
            <dc:creator>Syeef Karim</dc:creator>
            <dc:creator>Daniele Molteni</dc:creator>
        </item>
        <item>
            <title><![CDATA[Designing the new Cloudflare Web Application Firewall]]></title>
            <link>https://blog.cloudflare.com/designing-the-new-cloudflare-waf/</link>
            <pubDate>Tue, 11 May 2021 13:00:00 GMT</pubDate>
            <description><![CDATA[ The Cloudflare Web Application Firewall (WAF) protects websites and applications from malicious traffic attempting to exploit vulnerabilities in server software. It’s a critical piece of the broader security posture of your application. ]]></description>
            <content:encoded><![CDATA[ <p></p><p>The Cloudflare Web Application Firewall (WAF) protects websites and applications from malicious traffic attempting to exploit vulnerabilities in server software. It’s a critical piece of the broader security posture of your application. With that in mind, we made sure improvements to the Web Application Firewall dashboard experience made it easier to enable the WAF and configure rules to match the specific requirements of an application. In this post, I’ll share parts of the process we followed and the rationale behind the decisions we took when designing the <a href="/new-cloudflare-waf/">new Web Application Firewall</a> dashboard experience.</p><p>I’ve separated out my design process into three stages:</p><ol><li><p>Identify the tasks customers are trying to complete using the WAF</p></li><li><p>Prioritise the tasks in such a way that it’s clear what the most common tasks are vs what the more involved tasks are</p></li><li><p>Define, create, and refine the interface and interactions</p></li></ol>
    <div>
      <h2>Identifying the tasks customers are trying to complete</h2>
      <a href="#identifying-the-tasks-customers-are-trying-to-complete">
        
      </a>
    </div>
    <p>We support a range of customers — individual developers or hobbyists, small/medium-sized businesses where it’s common for a developer to fulfil multiple roles and responsibilities, through to large global enterprises where often there is an entire department dedicated to information security. Traditionally, product development teams use techniques such as the use of a user persona or a user story to help them focus on a specific problem they’re trying to solve; however each of these methods have their own inefficiencies and importantly aren’t able to scale to match the breadth of our customers. For example, a user persona is typically made by taking the average from a group or a specific selection of demographic indicators (such as age, gender, marital status, and hobbies), presenting it in a document and referring to it as one of multiple user archetypes of the application, but crucially fails to explain why the user decided to use or not use a specific feature. Similarly, user stories make use of user personas, but conflate them with implementation details and desired outcomes all the while failing to describe the situation the user is in.</p><p>To help the product development team better empathise with our range of customers, we use a technique known as Job Stories. Job Stories, unlike user persona or user stories allow us to focus on the users situation, motivation, and desired outcome.</p><p>We conducted interviews with a handful of customers directly, but we also supplemented them by interviewing members of our Solutions Engineering team. They help customers configure Cloudflare to meet their requirements and therefore have a direct connection to multiple customers themselves. They are in a position of being able to aggregate feedback from multiple customers. From the various interviews we identified the following job stories among many:</p><ul><li><p>When onboarding with Cloudflare, I want to quickly turn on the WAF and use the default settings so I can proceed to configuring the rest of the Cloudflare features.</p></li><li><p>When refining and tuning the configuration of my zone, I only want to configure the rules I’m interested in so I can reduce the potential number of false positive results.</p></li></ul><p>Next, we began to analyse each of these uses further. We wanted to understand what was working well from the existing interface and more importantly, what was causing confusion or impacting efficiency.</p>
    <div>
      <h3><i>“I want to turn on the WAF and use the default settings.”</i></h3>
      <a href="#i-want-to-turn-on-the-waf-and-use-the-default-settings">
        
      </a>
    </div>
    <p>With the legacy dashboard experience, a customer had a choice of different managed rulesets (Cloudflare Managed Ruleset or OWASP ModSecurity Core Rule Set.) Making a ruleset actually run however required some tedious configuration. The specific groups within a ruleset would need to be enabled and a separate switch controlling the overall WAF would also need to be enabled. Now, imagine the use case of wanting to use the Cloudflare Managed Ruleset. The current experience would require the user to enable at least two switches:</p><ol><li><p>The overall Web Application Firewall switch must be enabled</p></li><li><p>At least a single group from Cloudflare Managed Ruleset must be enabled</p></li></ol><p>As two distinct options needed to be configured, we concluded that this could cause customers to put their applications at an increased risk of being unprotected. They might have enabled the particular groups they’re interested in from the Cloudflare Managed Ruleset. However, if the switch of the Web Application Firewall is in the off position, the configuration of the Cloudflare Managed Ruleset is ignored. Essentially, they’ve misconfigured the WAF into a vulnerable state.</p><p>This is where we identified our first opportunity of improvement. The existing user journey had too many opportunities for misconfiguration and should have been very straightforward.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4xx2d5p8bJjCvKAUbnCSdw/e4f08afe8fd935d6f1cdb05e756e276b/image4-2.png" />
            
            </figure><p>The legacy UI of the Web Application Firewall.</p>
    <div>
      <h3><i>“I only want to configure the rules I’m interested in.”</i></h3>
      <a href="#i-only-want-to-configure-the-rules-im-interested-in">
        
      </a>
    </div>
    <p>On the legacy Managed Rules page, all the groups for each of the rulesets were listed beneath the card. This made it super easy to enable or disable specific rule groups and, from our user research sessions, we learned it was actually a very common workflow. However, scratch beneath the surface slightly and you’ll start to see the complexities of the system. Each group is made up of individual rules. By clicking on a group, a new modal would appear listing all the rules belonging to that group. The legacy UI was built to make it easy to change the action of a single rule — select an option from the dropdown and off you go. Simple. Most users probably even have the patience to change the action of a couple of rules like this. But what if it isn’t just a couple? Some groups contain hundreds of rules. A particularly complicated setup of the WAF could mean having to change the action of each rule. With the capabilities of the legacy UI, that would require hundreds of clicks and waste invaluable time. I’m embarrassed to admit that this was the suggested workflow and users were often required to use our APIs instead.</p><p>We now identified our next area of improvement — create the tools necessary for completing bulk edits and easy rule selection.</p><p>In summary, users want to easily turn on the WAF and move along. However, the legacy UI had a cumbersome and error-prone process to do so. Secondly, if a user wanted to enable or disable a specific rule of a ruleset, the legacy UI actually made that very simple. However, when it came to changing the action of multiple rules at once, the legacy UI was a time sink.</p>
    <div>
      <h2>Prioritising the identified tasks</h2>
      <a href="#prioritising-the-identified-tasks">
        
      </a>
    </div>
    <p>We’ve identified the tasks or jobs customers are trying to accomplish and understand where the current difficulties and inefficiencies are within the experience. Using our telemetry and analytics tools, such as Amplitude, we determine how often customers are performing each of the job stories. This is a critical step, as the output will help us decide which job stories we should be optimising the interface for. About 76% of zones that are using the WAF today are doing so with its default configuration. From this we can infer that most customers simply turn on the WAF and continue on with their business, potentially continuing to configure other Cloudflare features.</p><p>It’s worth pointing out the importance of having sensible defaults within applications. Oftentimes users will continue down the path of least resistance or to whatever helps them complete their goal the quickest — ergo, they’ll usually stick to the default settings of an application as these are usually created with the most common use cases in mind. For this reason, the default state of the Cloudflare Managed Ruleset is such that it exceeds the security requirements of most applications whilst balancing a relatively low false positive rate.</p>
    <div>
      <h2>Define, create, and refine the interface and interactions</h2>
      <a href="#define-create-and-refine-the-interface-and-interactions">
        
      </a>
    </div>
    <p>We use Figma to design the user interface of the dashboard. Using our Component Library, it allows us to quickly create mockups of what an interface could look like. At this stage of the project, a tool like Figma makes it easy for us to iterate through numerous ideas or permutations of a particular interaction.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6kKZwxbciujiWCCvrUhq3D/180fbd0eaad8b8993c6285b59e4e82d4/image3-1.png" />
            
            </figure><p>The Figma file used during the development of the new UI.</p><p>From the user research and data collection we conducted earlier, it’s clear that we needed to make enabling a particular ruleset better than the legacy experience. It is a very common workflow, but prone to potentially dangerous errors — certainly not a good combination.</p><p>Part of our job as designers at Cloudflare is to make the complex and intricate aspects of configuration ridiculously simple and increasing the confidence customers have with the UI and the actions they’re performing. With that in mind, to enable a ruleset like the Cloudflare Managed Ruleset with the new Managed Rules a customer only needs to do a single click.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3X04C1fZsEhGzVEtdVsufz/d9287df00e99900fe4e36b98a6eda6c3/image1-2.png" />
            
            </figure><p>The new Web Application Firewall page.</p><p>We wanted to improve the method of having all rules execute the same specific action. With the legacy UI, this required customers to select from a drop-down menu on all the individual rules. This was an extremely tedious and time-consuming process. Sticking to our design principles of maintaining ease of use and increasing simplicity and efficiency, the new Managed Rules allows for a single action to execute across the entire ruleset. We call this a Ruleset Action. A Ruleset Action is an action which all the rules within a ruleset will adhere to.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/Fyn5W5ghPi1rJVhnRzsGM/a841b9b647e0b9cd76097432719ac7f7/image2-1.png" />
            
            </figure><p>Review page with Ruleset Action configured.</p><p>The next capability we focused on was having all the rules execute the same specific action. In the legacy UI, changing the mode of multiple rules at once wasn’t possible. With the new dashboard experience, customers can browse through all the rules within the ruleset. Multiple rules can be selected at once using the select box on the left-hand side and the action or status can be set for the entire selection.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4MaD63Qoi4HVw9iCJTXXsW/bf2494eb2b65483b6e8b57a031ba5252/image5-1.png" />
            
            </figure><p>Rule Browser page with multiple rules selected and Set Action drop-down menu open.</p>
    <div>
      <h2>We’re just getting started</h2>
      <a href="#were-just-getting-started">
        
      </a>
    </div>
    <p>We didn’t get to these interactions straightaway, but rather by taking part in numerous design critiques and constantly evaluating the effectiveness of the new interactions against the identified job stories. We’ll be utilising our telemetry and analytics tools to understand how customers are using the new features and continuing to refine the experience further. Watch this space, because more updates are on the way!</p> ]]></content:encoded>
            <category><![CDATA[WAF]]></category>
            <category><![CDATA[Design]]></category>
            <category><![CDATA[Product Design]]></category>
            <guid isPermaLink="false">4qZ0jRzShvDas5tQ3K5S0K</guid>
            <dc:creator>Syeef Karim</dc:creator>
        </item>
        <item>
            <title><![CDATA[The Teams Dashboard: The Design Story]]></title>
            <link>https://blog.cloudflare.com/teams-dashboard-design-story/</link>
            <pubDate>Thu, 18 Mar 2021 12:00:00 GMT</pubDate>
            <description><![CDATA[ Here is the story of how we took Cloudflare for Teams from initial concepts, to an MVP, to now a comprehensive security platform that secures networks, users, devices, and applications. ]]></description>
            <content:encoded><![CDATA[ <p></p>
    <div>
      <h2>Intro</h2>
      <a href="#intro">
        
      </a>
    </div>
    <p>Cloudflare for Teams was first announced in <a href="/introducing-cloudflare-for-teams/">January 2020</a>, along with our acquisition of S2 Systems. It was an exciting day for everyone at Cloudflare, but especially my team, who was in charge of building Teams.</p><p>Here is the story of how we took Cloudflare for Teams from initial concepts, to an MVP, to now a comprehensive security platform that secures networks, users, devices, and applications.</p>
    <div>
      <h2>Background</h2>
      <a href="#background">
        
      </a>
    </div>
    <p>When I joined Cloudflare in April 2019, I was excited to have an impact on helping to build a better Internet. I was fascinated by the intricacy of how the Internet works, and wanted to untangle that complexity to provide our users with the best in class experience, with a simple and concise design approach. Little did I know that I would have the opportunity to launch a product that would impact thousands during a time when people need the Internet the most.</p><p>We started conceptualizing what would eventually become Cloudflare for Teams in July 2019, with a big vision and a small team. Coming off the excitement of <a href="https://1.1.1.1/">1.1.1.1</a>, the team began thinking about how to bring this functionality to small, medium, and enterprise businesses. Our goal was to bring protection to anyone and everyone by extending the same security technology the app offered. After months of brainstorming sessions, design iterations, and testing, we had an MVP version of Teams: offering customers a way to protect their network from security threats on the web using DNS filtering.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5zXyghMZr3q6FDiDWLuQHp/f0bef5b6253f2836c52c254e4a463c32/image4-14.png" />
            
            </figure>
    <div>
      <h2>Ramping up</h2>
      <a href="#ramping-up">
        
      </a>
    </div>
    <p>But we didn’t stop there. Access had been helping customers secure their applications using a zero-trust security model since 2018. This functionality existed in Cloudflare’s core dashboard, but was constrained to a <a href="https://support.cloudflare.com/hc/en-us/articles/201720164-Creating-a-Cloudflare-account-and-adding-a-website">site-based model</a>, while customers used Access as an account-based platform. This led to lots of confusion for many of our users. Bringing this functionality into Teams felt like a natural fit — Access would act as a bouncer standing in front of the door, checking identity, while Gateway would be a bodyguard, keeping your team safe as you navigate the Internet.</p><p>Bringing existing functionality into a new experience is no easy feat. The largest question to answer was: how can these two powerful security technologies not only cohabitate, but complement each other? We started by taking a step back and auditing what user problem we were solving with each piece of Access. This helped us understand where Access would fit under the Teams family, and how it could best integrate with Gateway.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4SoHCxVbrAytB5bYATV9F6/a0ddd8bb11b0b446536c3eebd852b363/image3-16.png" />
            
            </figure><p>From there, visual and design iterations began. Using the existing patterns and styles we created in Teams, we modified the look and feel of Access. However, not all of these changes were cosmetic. We focused on improved task flow, accessibility, and creating a seamless user experience.</p><p>Little did we know that it was just the beginning of what Teams would become.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/bBBG0gXpnKJEVfmklkQ7i/eec485f299c627d3642e820e7724eaa1/image2-14.png" />
            
            </figure>
    <div>
      <h2>Full speed</h2>
      <a href="#full-speed">
        
      </a>
    </div>
    <p>During <a href="/tag/zero-trust-week/">Zero Trust week</a>, we introduced three new capabilities into Teams. We expanded on our DNS filtering capability by adding in L7 inspection of traffic for threats that hide below the surface. We launched the Teams WARP client, extending the same security Teams offers to our end-users’ corporate devices. And lastly, we expanded our Zero Trust offering to support SaaS applications, giving users a consistent level of visibility and security across all of their applications.</p><p>So what does massive product growth look like behind the scenes, from a design perspective? Rapid iteration, testing, and lots of collaboration with Engineering. A key goal was to design with scale in mind. How will these experiences grow in six months, one year, or three years? Keeping this at the forefront of how we design means integrating UX patterns that can account for that scale and growth.</p><p>While Teams as a product was growing, I was also focused on hiring my own team to design the future of our product. The vision I had for Teams was greater than what I could accomplish on my own. Being strategic about how and where I was hiring was a key goal of mine — how could I enable each designer to be successful, while also contributing to the growth and success of Teams? By November 2020, I had hired three designers to partner with me on crafting the rest of the Teams story.</p>
    <div>
      <h2>Looking ahead</h2>
      <a href="#looking-ahead">
        
      </a>
    </div>
    <p>This brings us to the end of 2020, both a tumultuous for all and traumatic year for many. Through it all, I grew two things that are near and dear to my heart: my own design team and the Teams product. I began to think about where both of them would be in 1-3 years, and how a deep partnership between Product, Design, and Engineering could help get us there.</p><p>Being strategic about our product growth would help us with three things:</p><ul><li><p><b>Collaboration</b>. Strategic thinking helps the entire team aim for a common goal, which means working together, as opposed to developing a myopic view of the outcome and working separately.</p></li><li><p><b>Efficiency.</b> A shared vision gets us both quality and velocity. If everyone's rowing in the same direction, we get there quicker.</p></li><li><p><b>Longevity</b>. Lays the foundation for a scalable product that grows to include additional experiences over time.</p></li></ul>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/64dzjfIBtY1pb8SPM8W8mC/ce7739d826ad09449fa313dc61278786/image5-17.png" />
            
            </figure>
    <div>
      <h2>Closing</h2>
      <a href="#closing">
        
      </a>
    </div>
    <p>Getting the opportunity to build a product from the ground up has been exciting, rewarding, challenging, and thrilling all at once. I’m proud of what we have been able to accomplish, and can’t wait to share with y’all what we’ve been working on over the past few months. Stay tuned!</p> ]]></content:encoded>
            <category><![CDATA[Design]]></category>
            <category><![CDATA[Product Design]]></category>
            <category><![CDATA[Teams Dashboard]]></category>
            <category><![CDATA[Cloudflare Zero Trust]]></category>
            <category><![CDATA[Zero Trust]]></category>
            <category><![CDATA[Security]]></category>
            <guid isPermaLink="false">4Su45vjD64MHMHpTl60iR</guid>
            <dc:creator>Bethany Sonefeld</dc:creator>
        </item>
        <item>
            <title><![CDATA[Introducing Secrets and Environment  Variables to Cloudflare Workers]]></title>
            <link>https://blog.cloudflare.com/workers-secrets-environment/</link>
            <pubDate>Wed, 26 Feb 2020 15:00:00 GMT</pubDate>
            <description><![CDATA[ The Workers team here at Cloudflare has been hard at work shipping a bunch of new features in the last year and we’ve seen some amazing things built with the tools we’ve provided. ]]></description>
            <content:encoded><![CDATA[ <p></p><p>The Workers team here at Cloudflare has been hard at work shipping a bunch of new features in the last year and we’ve seen some <a href="https://workers.cloudflare.com/built-with">amazing things</a> built with the tools we’ve provided. However, as my uncle once said, with great serverless platform growth comes great responsibility.</p><p>One of the ways we can help is by ensuring that deploying and maintaining your Workers scripts is a low risk endeavor. Rotating a set of API keys shouldn’t require risking downtime through code edits and redeployments and in some cases it may not make sense for the developer writing the script to know the actual API key value at all. To help tackle this problem, we’re releasing Secrets and Environment Variables to the Wrangler CLI and Workers Dashboard.</p>
    <div>
      <h2>Supporting secrets</h2>
      <a href="#supporting-secrets">
        
      </a>
    </div>
    <p>As we started to design support for secrets in Workers we had a sense that this was already a big concern for a lot of our users but we wanted to learn about all of the use cases to ensure we were building the right thing. We headed to the community forums, twitter, and the inbox of Louis Grace, business development representative extraordinaire, for some anecdotes about Secrets usage. We also sent out a survey to our existing users to learn about use cases and pain points.</p><p>We learned that even though there was already a way to store secrets without exposing them via Workers KV, the solution was not very intuitive, nor did it meet all the needs of our users. Many users didn’t even know we had an interim solution in place. Recognizing that we were not the first platform to encounter this problem, we surveyed the existing landscape of Platform as a Service offerings to get a better sense for what our users would expect of us.</p>
    <div>
      <h2>Deciding on a solution</h2>
      <a href="#deciding-on-a-solution">
        
      </a>
    </div>
    <p>One of the first things we found was that not all environment variables are created equal. While the simplest use case for having a defined environment variable may be storing a piece of text that can be updated no matter where it is referenced in a script, sometimes those variables may have higher stakes associated with them. If you’re storing an API key that controls access to an important system, you may not want to allow anyone with dashboard access to see it, maybe not even the developers themselves.</p><p>With this in mind, we had to ensure the feature covered two different use cases: one for storing variables in plain text where you could see the variable being referenced and make edits to it and another where the variable would be encrypted as soon as you save it, never to be seen again. This way, we were able to serve both needs of our users, side by side, without one compromising for the other.</p>
    <div>
      <h2>Testing our prototypes</h2>
      <a href="#testing-our-prototypes">
        
      </a>
    </div>
    <p>Once we had a fairly good idea of what we wanted to build, we built some prototypes and rough implementations in staging environments so we would be able to perform some usability testing. We wrangled up some developers and observed them as they performed a series of tasks where they were asked to add some secrets and plain-text environment variables, reference them in one of their Workers, and bind their Worker to a Worker KV namespace.</p><p>Along the way we also asked questions to understand the developer’s professional background, familiarity with the product, and the use cases they’ve had for using Workers in the past along with any pain points they experienced.</p><p>While we were testing the new dashboard interface we also began testing the usability of the Wrangler CLI. We had Wrangler users perform the same tasks as the Workers dashboard users to help us find out if users are expecting different things out of their command-line tooling.</p>
    <div>
      <h2>Findings and fixes</h2>
      <a href="#findings-and-fixes">
        
      </a>
    </div>
    <p>Through our testing we were able to make a number of changes before the final release. Some of the smaller changes included things like adjusting the behavior of form fields to ensure users knew which variable would be associated with each value. We also made larger changes like electing to separate the KV namespace bindings from the other environment variables as a way to emphasize that KV namespace bindings are not the keys and values themselves but a reference to a namespace where those keys are stored.</p><p>Cina, one of our engineers, put together a proposal to align some of our terminology with the terms that our developers were naturally using to describe their workflow. In Wrangler users were accustomed to referencing their KV namespaces by adding a KV namespace binding so when they came to the Workers dashboard interface and saw a field called “KV Variables” they were often confused, thinking they were adding keys and values to the namespace itself instead of establishing a variable that could be used to reference the namespace. As a fix, we decided to call it a “KV namespace binding” throughout the experience.</p>
    <div>
      <h2>Try it out</h2>
      <a href="#try-it-out">
        
      </a>
    </div>
    <p>Environment variables are available now with the <a href="https://developers.cloudflare.com/workers/quickstart/">Wrangler CLI</a> and in the <a href="https://dash.cloudflare.com/">Workers Dashboard</a> so go ahead and give them a shot today!</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/j8zJOFaoQFe4nIbzaSFmG/473957f2203af94b4d736697e9587db1/wrangler-add-secret-1.svg" />
            
            </figure><p>Adding a secret with Wrangler</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4552by5LG5KpgFbMJPaLG8/f9b74b091a3050500cc42868c3002dea/worker-detail-page.jpg" />
            
            </figure><p>Managing environment variables and KV bindings in the Workers Dashboard</p><p>As we continue to build out the Workers platform we’d love to hear from you. Let us know if you’re interested in <a href="https://docs.google.com/forms/d/e/1FAIpQLSd76N9gZFo_hxhHPHtTtwSkMAC7rfYD1TU6CPmLe2iKKlKWLA/viewform">participating in user research</a> or just <a>have something to say</a> as we’d love to hear from you.</p> ]]></content:encoded>
            <category><![CDATA[Serverless]]></category>
            <category><![CDATA[Cloudflare Workers]]></category>
            <category><![CDATA[API]]></category>
            <category><![CDATA[Cloudflare Workers KV]]></category>
            <category><![CDATA[Design]]></category>
            <category><![CDATA[Security]]></category>
            <category><![CDATA[Developers]]></category>
            <category><![CDATA[Developer Platform]]></category>
            <guid isPermaLink="false">38BlNuhadpp4EF1IJj5Xc7</guid>
            <dc:creator>John Donmoyer</dc:creator>
            <dc:creator>Nena</dc:creator>
        </item>
        <item>
            <title><![CDATA[Thinking about color]]></title>
            <link>https://blog.cloudflare.com/thinking-about-color/</link>
            <pubDate>Thu, 05 Dec 2019 07:28:00 GMT</pubDate>
            <description><![CDATA[ An exploration of building accessible color systems for flexible UI theming.  ]]></description>
            <content:encoded><![CDATA[ 
    <div>
      <h4>Color is my day-long obsession, joy and torment - Claude Monet</h4>
      <a href="#color-is-my-day-long-obsession-joy-and-torment-claude-monet">
        
      </a>
    </div>
    
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3co2UUhlUcWwDnTlj0D0vK/f9d2b84e7522b97776ed33b081ff0ee6/gradient.jpg" />
            
            </figure><p>Over the last two years we’ve tried to improve our usage of color at Cloudflare. There were a number of forcing functions that made this work a priority. As a small team of designers and engineers we had inherited a bunch of design work that was a mix of values built by multiple teams. As a result it was difficult and unnecessarily time consuming to add new colors when building new components.</p><p>We also wanted to improve our accessibility. While we were doing pretty well, we had room for improvement, largely around how we used green. As our UI is increasingly centered around visualizations of large data sets we wanted to push the boundaries of making our analytics as visually accessible as possible.</p><p>Cloudflare had also undergone a rebrand around 2016. While our marketing site had rolled out an updated set of visuals, our product ui as well as a number of existing web properties were still using various versions of our old palette.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/iQCiIRJAOmk9biv9HJJEG/3012bcd3af3b18761e64411afa7817da/cf-2016-1.png" />
            
            </figure>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/47HyWcLQE7NuarhQsKKTYq/71e87b12a7695ddce7f543e0a33779f0/cf-2019-1.png" />
            
            </figure><p>Cloudflare.com in 2016 &amp; Cloudflare.com in 2019</p><p>Our product palette wasn’t well balanced by itself. Many colors had been chosen one or two at a time. You can see how we chose blueberry, ice, and water at a different point in time than marine and thunder.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3mOKpnbC9dlWc7e8A7bCoY/c78bf5c2dbb3ad74fee50feeb789ebbc/cf-color-palette.png" />
            
            </figure><p>The color section of our theme file was partially ordered chronologically</p><p>Lacking visual cohesion within our own product, we definitely weren’t providing a cohesive visual experience between our marketing site and our product. The transition from the nice blues and purples to our green CTAs wasn’t the streamlined experience we wanted to afford our users.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/12v5yOvqsPpH52piiDqPTo/a237cd6b8dda9bc133838b430f68c556/cf-2017.png" />
            
            </figure>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2w8kdPFsfVLvLU0r4E6rjL/1ab936064befaa77f1cfb15083848d8e/cf-login-2017.png" />
            
            </figure><p>Cloudflare.com 2017 &amp; Sign in page 2017</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/71DV64ihmXYsAWe60a0alz/e0a0c07e8d182f6f3d3b137298986bc3/cf-dash-2017.png" />
            
            </figure><p>Our app dashboard in 2017</p>
    <div>
      <h2>Reworking our Palette</h2>
      <a href="#reworking-our-palette">
        
      </a>
    </div>
    <p>Our first step was to audit what we already had. Cloudflare has been around long enough to have more than one website. Beyond cloudflare.com we have dozens of web properties that are publicly accessible. From our community forums, support docs, blog, status page, to numerous micro-sites.</p><p>All-in-all we have dozens of front-end codebases that each represent one more chance to introduce entropy to our visual language. So we were curious to answer the question - what colors were we currently using? Were there consistent patterns we could document for further reuse? Could we build a living style guide that didn’t cover just one site, but all of them?</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1SFdyLsuBGWMrPPco6FkX6/3cbb30c7b6d285f407e7f7d72b1bb68f/cf-screenshots.png" />
            
            </figure><p>Screenshots of pages from cloudflare.com contrasted with screenshots from our product in 2017</p><p>Our curiosity got the best of us and we went about exploring ways we could visualize our design language across all of our sites.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/yYW4Lpev96PQm7IS1AjYr/5f9d79ce3ab7380f1ad9e99e50f9f21a/color-sort.png" />
            
            </figure><p>Above - our product palette. Below - our marketing palette.</p>
    <div>
      <h3>A time machine for color</h3>
      <a href="#a-time-machine-for-color">
        
      </a>
    </div>
    <p>As we first started to identify the scale of our color problems, we tried to think outside the box on how we might explore the problem space. After an initial brainstorming session we combined the <a href="https://archive.org/web/">Internet Archive's Wayback Machine</a> with the <a href="https://cssstats.com">Css Stats</a> <a href="https://cssstats.com/docs/api">API</a> to build an audit tool that shows how our various websites visual properties change over time. We can dynamically select which sites we want to compare and scrub through time to see changes.</p><p>Below is a visualization of palettes from 9 different websites changing over a period of 6 years. Above the palettes is a component that spits out common colors, across all of these sites. The only two common colors across all properties (appearing for only a brief flash) were #ffffff (white) and transparent. Over time we haven’t been very consistent with ourselves.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3HcFc8uup0GMz3WoNEPFf1/9c9fd86b01132755a0ac860289d4501f/palette-over-time.gif" />
            
            </figure><p>If we drill in to look at our marketing site compared to our dashboard app - it looks like the video below. We see a bit more overlap at first and then a significant divergence at the 16 second mark when our product palette grew significantly. At the 22 second mark you can see the marketing palette completely change as a result of the rebrand while our product palette stays the same. As time goes on you can see us becoming more and more inconsistent across the two code bases.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/44zKrA4K4FLa1JHiGXbdBl/5c08bb4a78308fce4b2ef8fd612f92c4/comparison-over-time.gif" />
            
            </figure><p>As a product team we had some catching up to do improve our usage of color and to align ourselves with the company brand. The good news was, there was no where to go but up.</p><p>This style of historical audit gives us a visual indication with real data. We can visualize for stakeholders how consistent and similar our usage of color is across products and if we are getting better or worse over time. Having this type of feedback loop was invaluable for us - as auditing this manually is incredibly time consuming so it often doesn’t get done. Hopefully in the future as it’s standard to track various performance metrics over time at a company it will be standard to be able to visualize your current levels of design entropy.</p>
    <div>
      <h3>Picking colors</h3>
      <a href="#picking-colors">
        
      </a>
    </div>
    <p>After our initial audit revealed there wasn’t a lot of consistency across sites, we went to work to try and construct a color palette that could potentially be used for sites the product team owned. It was time to get our hands dirty and start “picking colors.”</p><p>Hindsight of course is always 20/20. We didn’t start out on day one trying to generate scales based on our brand palette. No, our first bright idea, was to generate the entire palette from a single color.</p><p>Our logo is made up of two oranges. Both of these seemed like prime candidates to generate a palette from.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2g3KWEf91W2bPQpdsBbY5D/64913d35499c6d302269adbd22a13ac9/logo.png" />
            
            </figure><p>We played around with a number of algorithms that took a single color and created a palette. From the initial color we generated an array scales for each hue. Initial attempts found us applying the exact same curves for luminosity to each hue, but as visual perception of hues is so different, this resulted in wildly different contrasts at each step of the scale.</p><p>Below are a few of our initial attempts at palette generation. <a href="https://uxplanet.org/designing-systematic-colors-b5d2605b15c">Jeeyoung Jung did a brilliant writeup around designing palettes last year</a>.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/79gHpPWYld3RMraFEBav85/45a10df18633967449201bfdf33ebb24/peaks-of-color.jpg" />
            
            </figure><p>Visualizing peaks of intensity across hues</p><p>We can see the intensity of the colors change across hue in peaks, with yellow and green being the most dominant. One of the downsides of this, is when you are rapidly iterating through theming options, the inconsistent relationships between steps across hues can make it time consuming or impossible to keep visual harmony in your interface.</p><p>The video below is another way to visualize this phenomenon. The dividing line in the color picker indicates which part of the palette will be accessible with black and white. Notice how drastically the line changes around green and yellow. And then look back at the charts above.</p>
            <figure>
            
            <img src="https://downloads.ctfassets.net/zkvhlag99gkb/4JZzMO1N5i8EGLVkKai3F2/f1f01077ed8711cbb42c3ddbc1281289/colors.gif" />
            
            </figure><p>Demo of <a href="https://kevingutowski.github.io/color.html">https://kevingutowski.github.io/color.html</a></p><p>After fiddling with a few different generative algorithms (we made a lot of ugly palettes…) we decided to try a more manual approach. We pursued creating custom curves for each hue in an effort to keep the contrast scales as optically balanced as possible.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7Dlfopss7ZXAB8dMLRZMCP/f4691d67415dfcfdad72332679dd6dba/palette-generator.gif" />
            
            </figure>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/e59S3HogRRcA2hxoG3K3Q/15ddc18210459f64e245f930e5a9e186/generative-palette-1.png" />
            
            </figure>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2nemldnMgsCZfG6hLRdf7v/007943660f1ee3c7956741e49f972bbd/cf-palette-1.png" />
            
            </figure>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/59oeMlcw5gjAI0ukM2n5Rp/ca078063ba8c21d7589d468a39387362/palette-generator-concept-1.png" />
            
            </figure>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/54aTXwLBolTAxpB95M6GzD/945b9c0a782ba5df69f47fe9b3802308/muted-palette.png" />
            
            </figure><p>Heavily muted palette</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4xBV6RiuoNeNH9DOKH9eOt/96ffce84f94afd94901953941b5d19ad/palette-view-concept.png" />
            
            </figure><p>Generating different color palettes makes you confront a basic question. How do you tell if a palette is good? Are some palettes better than others? In an effort to answer this question we constructed various feedback loops to help us evaluate palettes as quickly as possible. We tried a few methods to stress test a palette. At first we attempted to grab the “nearest color” for a bunch of our common UI colors. This wasn't always helpful as sometimes you actually want the step above or below the closest existing color. But it was helpful to visualize for a few reasons.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2HHes7agbHWgvAsL7zrNBG/e7fc802a6bcdaacf76ef0b8686529462/old-vs-new.png" />
            
            </figure><p>Generated palette above a set of components previewing the old and new palette for comparison</p><p>Sometime during our exploration in this space, <a href="https://twitter.com/paniq/status/1036299654378467329">we stumbled across this tweet thread</a> about building a palette for pixel art. There are a lot of places web and product designers can draw inspiration from game designers.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6m6T2Undt3pGJUjxppzXoN/6e6f2da92ff47d30970a2f348d6e45d3/palettes-for-pixel-art.jpeg.jpeg" />
            
            </figure><p><a href="https://twitter.com/paniq/status/1036299654378467329">Two color palettes visualized to create 3d objects</a></p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1oO9IHwjR72XXsMJBAF3ji/13a854b02301cb77598189d86dc04ff9/pixel-art-palette-concept.jpeg.jpeg" />
            
            </figure><p><a href="https://twitter.com/paniq/status/1037406840999567360">A color palette applied in a few different contexts</a></p><p>Here we see a similar concept where a number of different palettes are applied to the same component. This view shows us two things, the different ways a single palette can be applied to a sphere, and also the different aesthetics across color palettes.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5oAfsaM1TioGxRiECF5b40/4590e70fd674a4da9c0244bf0859fa64/multiple-palettes-applied.png" />
            
            </figure><p><a href="https://twitter.com/JstFredrik/status/1145753143852064768">Different color palettes previewed against a common component</a></p><p>It’s almost surprising that the default way to construct a color palette for apps and sites isn’t to build it while previewing its application against the most common UI patterns. As designers, there are a lot of consistent uses of color we could have baselines for. Many enterprise apps are centered around white background with blue as the primary color with mixtures of grays to add depth around cards and page sections. Red is often used for destructive actions like deleting some type of record. Gray for secondary actions. Maybe it’s an outline button with the primary color for secondary actions. Either way - the margins between the patterns aren’t that large in the grand scheme of things.</p><p>Consider the use case of designing UI while the palette or usage of color hasn't been established. Given a single palette, you might want to experiment with applying that palette in a variety of ways that will output a wide variety of aesthetics. Alternatively you may need to test out several different palettes. These are two different modes of exploration that can be extremely time consuming to work through . It can be non-trivial to keep an in progress design synced with several different options for color application, even with the best use of layer comps or symbols.</p><p>How do we visualize the various ways a palette will look when applied to an interface? Here are examples of how palettes are shown on a palette list for pixel artists.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2merOeD2D0Lfl6MjcLOa1S/398643e0029f2222fd38ebd9c35978a7/random-palette.png" />
            
            </figure>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7dFlaHwsphBIzMDlErLIm3/ed06041fac372b5628228ae40082bd08/palette-used-in-scene-1.png" />
            
            </figure>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6Phs6WtKOnNjhIueheiRnQ/630cf1366ef2c6a4edbc12926da5eeef/example-palette-for-pixel-art-1.png" />
            
            </figure>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3flxp8SF97nfZfGTXyFPRL/7c61952199d9d3b7b3406078873982cb/pixel-art-palette-1.png" />
            
            </figure><p><a href="https://lospec.com/palette-list/sweetie-16">https://lospec.com/palette-list/sweetie-16</a></p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2QnCtnrp9mEILMj8XClCpr/b2bdf893d2ba7b1dc2c506668dc154ca/random-palette-1.png" />
            
            </figure><p><a href="https://lospec.com/palette-list/vines-flexible-linear-ramps">https://lospec.com/palette-list/vines-flexible-linear-ramps</a></p><p>One method of visualization is to define a common set of primitive ui elements and show each one of them with a single set of colors applied. In isolation this can be helpful. This mode would make it easy to vet a single combination of colors and which ui elements it might be best applied to.</p><p>Alternatively we might want to see a composed interface with the closest colors from the palette applied. Consider a set of buttons that includes red, green, blue, and gray button styles. Seeing all of these together can help us visualize the relative nature of these buttons side by side. Given a baseline palette for common UI, we could swap to a new palette and replace each color with the "closest" color. This isn't always a full-proof solution as there are many edge cases to cover. e.g. what happens when replacing a palette of 134 colors with a palette of 24 colors? Even still, this could allow us to quickly take a stab at automating how existing interfaces would change their appearance given a change to the underlying system. Whether locally or against a live site, this mode of working would allow for designers to view a color in multiple contets to truly asses its quality.</p>
            <figure>
            
            <img src="https://downloads.ctfassets.net/zkvhlag99gkb/4kwKVTDdfy2RRayxIzODhx/de69628e6c083c4c25f838a6785cebfa/cloudflare-demo.gif" />
            
            </figure><p>After moving on from the idea of generating a palette from a single color, we attempted to use our logo colors as well as our primary brand colors to drive the construction of modular scales. Our goal was to create a palette that would improve contrast for accessibility, stay true to our visual brand, work predictably for developers, work for data visualizations, and provide the ability to design visually balanced and attractive interfaces. No sweat.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5XLkfqu7DlLuzGfWtXhHb/15fcf46876e66bccddd95fb28aeedbde/hue-and-sat-palette.png" />
            
            </figure><p>Brand colors showing Hue and Saturation level</p><p>While we knew going in we might not use every step in every hue, we wanted full coverage across the spectrum so that each hue had a consistent optical difference between each step. We also had no idea which steps across which hues we were going to need just yet. As they would just be variables in a theme file it didn’t add any significant code footprint to expose the full generated palette either.</p><p>One of the more difficult parts, was deciding on a number of steps for the scales. This would allow us to edit the palette in the future to a variety of aesthetics and swap the palette out at the theme level without needing to update anything else.</p><p>In the future if when we did need to augment the available colors, we could edit the entire palette instead of adding a one-off addition as we had found this was a difficult way to work over time. In addition to our primary brand colors we also explored adding scales for yellow / gold, violet, teal as well as a gray scale.</p><p>The first interface we built for this work was to output all of the scales vertically, with their contrast scores with both white and black on the right hand side. To aid scannability we bolded the values that were above the 4.5 threshold. As we edited the curves, we could see how the contrast ratios were affected at each step. Below you can see an early starting point before the scales were balanced. Red has 6 accessible combos with white, while yellow only has 1. We initially explored having the gray scale be larger than the others.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1rrDJOPhFY9z11t5IKSpHj/96a35df74c3f94f7064137486346a5cb/early-palette-preview.png" />
            
            </figure><p>Early iteration of palette preview during development</p><p>As both screen luminosity and ambient light can affect perception of color we developed on two monitors, one set to maximum and one set to minimum brightness levels. We also replicated the color scales with a grayscale filter immediately below to help illustrate visual contrast between steps AND across hues. Bouncing back and forth between the grayscale and saturated version of the scale serves as a great baseline reference. We found that going beyond 10 steps made it difficult to keep enough contrast between each step to keep them distinguishable from one another.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4Utffg4DIrvLVNvccLVZpv/9a55d29bf131874dfbb187ed886d3c4b/lum-vs-lum-1.jpeg.jpeg" />
            
            </figure>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5QSIfDFjFtzPC5BoC1CrGz/b867bae2f975444c8cccee912cfe57c8/lum-vs-lum-upclose-1.jpeg.jpeg" />
            
            </figure><p>Monitors set to different luminosity &amp; a close up of the visual difference</p><p>Taking a page from our game design friends - as we were balancing the scales and exploring how many steps we wanted in the scales, we were also stress testing the generated colors against various analytics components from our component library.</p><p>Our slightly random collection of grays had been a particular pain point as they appeared muddy in a number of places within our interface. For our new palette we used the slightest hint of blue to keep our grays consistent and just a bit off from being purely neutral.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/54qwfeLHQna65zJsKWHIO4/979815a66557a8cfee27869b507f1b9e/balanced-palette.png" />
            
            </figure><p>Optically balanced scales</p><p>With a palette consisting of 90 colors, the amount of combinations and permutations that can be applied to data visualizations is vast and can result in a wide variety of aesthetic directions. The same palette applied to both line and bar charts with different data sets can look substantially different, enough that they might not be distinguishable as being the exact same palette. Working with some of our engineering counterparts, we built a pipeline that would put up the same components rendered against different data sets, to simulate the various shapes and sizes the graph elements would appear in. This allowed us to rapidly test the appearance of different palettes. This workflow gave us amazing insights into how a palette would look in our interface. No matter how many hours we spent staring at a palette, we couldn't get an accurate sense of how the colors would look when composed within an interface.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3CZ0BBnY69MKYvt8XzXOGZ/bca06f9091c01d3d9ec423c9d4399b71/theme-minus-green-1.png" />
            
            </figure>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1DQnxPWSV6WVVCD81LSgcQ/32170c423ea156acd194d9fe30171e61/theme-blues-oranges-1.png" />
            
            </figure><p>Full theme minus green &amp; blues and oranges</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5y6yo1WNol6kgNmOFBAjAU/bbc4ef95318274f44bb6d70de9246560/theme-grayscale-1.png" />
            
            </figure>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6rKXktHvrcWWzHx1JYSQe0/c6ada22cdc7b85d48611c282d1da8f9d/theme-indigo-1.png" />
            
            </figure><p>Grayscale example &amp; monochromatic Indigo</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7r4kTmD204E1jpDG4RJIag/b5e4092d55d7a0ef111adb9e3b10bc1f/chart-blue-2.png" />
            
            </figure>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5Sy3KTL9hBg7GaBXfaiPih/26a79f5cf8b714721b590dcc2fc3d050/chart-blue-1-1.png" />
            
            </figure>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2oTtgNNTAMt7Knvjbpuqdb/2f6ddb57b9bfb1dd170fac6c76501ac4/charts-with-blue-purple-1.png" />
            
            </figure>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3L8zV8uiPlO0pIlmian7L5/b570e71def808c77ac11673b54c21173/charts-blue-green-1.png" />
            
            </figure><p>Analytics charts with blues and purples &amp; analytics charts with blues and greens</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4ytbyjsaYcdiMZc6qoh1Rh/56c8d7c5c1e61c57d9200d181674ede5/charts-blue-oranges.png" />
            
            </figure><p>Analytics charts with a blues and oranges. Telling the colors of the lines apart is a different visual experience than separating out the dots in sequential order as they appear in the legend.</p><p>We experimented with a number of ideas on visualizing different sizes and shapes of colors and how they affected our perception of how much a color was changing element to element. In the first frame it is most difficult to tell the values at 2% and 6% apart given the size and shape of the elements.</p>
            <figure>
            
            <img src="https://downloads.ctfassets.net/zkvhlag99gkb/4rXnVDR3ah5vZlTz9EF0zv/51977dba43fbe5164bce2bc52ded97cf/generative-charts.gif" />
            
            </figure><p>Stress testing the application of a palette to many shapes and sizes</p><p>We’ve begun to <a href="https://cloudflare.design/color">package up some of this work into a web app</a> others can use to create or import a palette and preview multiple depths of accessible combinations against a set of UI elements.</p><p>The goal is to make it easier for <a href="https://cloudflare.design/color/about">anyone to work seamlessly with color</a> and build beautiful interfaces with accessible color contrasts.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4YLTTM7rdeK9iWiOAxju1k/6117350bef4e1093e0ddaacf691a4888/color-tool.jpg" />
            
            </figure><p>Color by Cloudflare Design</p><p>In an effort to make sure everything we are building will be visually accessible - we built a react component that will preview how a design would look if you were colorblind. The component overlays SVG filters to simulate alternate ways someone can perceive color.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/774G8X1vf6DF2UiqNyTXoQ/9705c5eca95f77f7e42805bf1e0dcd7c/charts-previewed.png" />
            
            </figure><p>Analytics component previewed against 8 different types of color blindness</p><p>While this is previewing an analytics component, really any component or page can be previewed with this method.</p>
            <pre><code>import React from "react"

const filters = [
  'achromatopsia',
  'protanomaly',
  'protanopia',
  'deuteranomaly',
  'deuteranopia',
  'tritanomaly',
  'tritanopia',
  'achromatomaly',
]

const ColorBlindFilter = ({itemPadding, itemWidth, ...props }) =&gt; {
  return (
      &lt;div  {...props}&gt;
        {filters.map((filter, i) =&gt; (
          &lt;div
            style={{filter: 'url(/filters.svg#'+filter+')'}}
            width={itemWidth}
            px={itemPadding}
            key={i+filter}
          &gt;
            {props.children}
          &lt;/div&gt;
        ))}
      &lt;/div&gt;
  )
}

ColorBlindFilter.defaultProps = {
  display: 'flex',
  justifyContent: 'space-around',
  flexWrap: 'wrap',
  width: 1,
  itemWidth: 1/4
}

export default ColorBlindFilter</code></pre>
            <p><a href="https://www.figma.com/blog/behind-the-plugins-sam-mason-de-caires-cloudflare/">We’ve also released a Figma plugin that simulates this visualization for a component</a>.</p><p>After quite a few iterations, we had finally come up with a color palette. Each scale was optically aligned with our brand colors. The 5th step in each scale is the closest to the original brand color, but adjusted slightly so it’s accessible with both black and white.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2njmOf39VZJLzvrTjWWh7J/f906c550f32085bc4312274a54c39107/palette-desaturated.png" />
            
            </figure><p>Our preview panel for palette development, showing a fully desaturated version of the palette for reference</p><p><a href="https://design.lyft.com/re-approaching-color-9e604ba22c88and">Lyft’s writeup <b>“Re-approaching color”</b></a> and <a href="https://uxplanet.org/designing-systematic-colors-b5d2605b15c">Jeeyoung Jung’s “<b>Designing Systematic Colors</b>”</a> are some of the best write-ups on how to work with color at scale you can find.</p>
    <div>
      <h2>Color migrations</h2>
      <a href="#color-migrations">
        
      </a>
    </div>
    
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2EBCosqCOdHd60bFtWE6rf/65987d10f011c5ae8622c7e81ebec241/palette-migration.png" />
            
            </figure><p>A visual representation of how the legacy palette colors would translate to the new scales.</p><p>Getting a team of people to agree on a new color palette is a journey in and of itself. By the time you get everyone to consensus it’s tempting to just collapse into a heap and never think about colors ever again. Unfortunately the work doesn’t stop at this point. Now that we’ve picked our palette, it’s time to get it implemented so this bike shed is painted once and for all.</p><p>If you are porting an old legacy part of your app to be updated to the new style guide like we were, even the best color documentation can fall short in helping someone make the necessary changes.</p><p>We found it was more common than expected that engineers and designers wanted to know what the new version of a color they were familiar with was. During the transition between palettes we had an interface people could input any color and get the closest color within our palette.</p><p>There are times when migrating colors, the closest color isn't actually what you want. Given the scenario where your brand color has changed from blue to purple, you might want to be porting all blues to the closest purple within the palette, not the closest blues which might still exist in your palette. To help visualize migrations as well as get suggestions on how to consolidate values within the old scale, we of course built a little tool. Here we can define those translations and import a color palette from a URL import. As we still have. a number of web properties to update to our palette, this simple tool has continued to prove useful.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6Yq4gC5zxAVU8DkfIqhcmq/2a71b4bf6318bef05f969e4237816931/fetch-palette.gif" />
            
            </figure><p>We wanted to be as gentle as possible in transitioning to the new palette in usage. While the developers found string names for colors brittle and unpredictable, it was still a more familiar system for some than this new one. We first just added in our new palette to the existing theme for usage moving forward. Then we started to port colors for existing components and pages.</p><p>For our colleagues, we wrote out desired translations and offered warnings in the console that a color was deprecated, with a reference to the new theme value to use.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/FO2JQhwAwB7cxiH6FmiQ9/6a63bf5fc6a1fe30debf567525fcc29a/invalid-colors.png" />
            
            </figure><p>Example of console warning when using deprecated color</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3n79ajNed1aLnSrPGDT8th/002593f992bac04c4f86df31b367dfa6/invalid-colors-code.png" />
            
            </figure><p>Example of how to check for usage of deprecated values</p><p>While we had a few bugs along the way, the team was supportive and helped us fix bugs almost as quickly as we could find them.</p><p>We’re still in the process of updating our web properties with our new palette, largely prioritizing accessibility first while trying to create a more consistent visual brand as a nice by-product of the work. A small example of this is our system status page. In the first image, the blue links in the header, the green status bar, and the about copy, were all inaccessible against their backgrounds.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1UGEwwdKxki3OtQj1wVZff/5b1db7ea2426ec28f7172c0e40aed606/cloudflare-status-2017.png" />
            
            </figure>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1HzFPccOjKVBBkAfESXAyz/e3f4cb862d241fd492b09d90b207cee2/cloudflare-status-2019.png" />
            
            </figure><p>cloudflarestatus.com in 2017 &amp; cloudflarestatus.com in 2019 with an accessible green</p><p>A lot of the changes have been subtle. Most notably the green we use in the dashboard is a lot more inline with our brand colors than before. In addition we’ve also been able to add visual balance by not just using straight black text on background colors. Here we added one of the darker steps from the corresponding scale, to give it a bit more visual balance.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7h4BT0Ronx9ubMImVURssF/68178d84d99a908c6a9a6c2862e3546d/cf-notifications-2017.png" />
            
            </figure>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/HiAbtyy2EDO9ONFRwqX3Q/f0567ecbbb92032557c49083669fa4b7/cf-notifications-2018.png" />
            
            </figure><p>Notifications 2017 - Black text &amp; Notifications 2018 - Updated palette. Text is set to 1st step in corresponding scale of background color</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/45JukugvVofJNPQJjAQonl/e2b613bd30f2d211103f9046d57f692f/2017-2019-1.png" />
            
            </figure><p>Example page within our Dashboard in 2017 vs 2019</p><p>While we aren’t perfect yet, we’re making progress towards more visual cohesion across our marketing materials and products.</p>
    <div>
      <h4>2017</h4>
      <a href="#2017">
        
      </a>
    </div>
    
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3vJEoryew9DSNrFLOwUWF5/2d41389b38041141fb58c72c29d78e0b/cf-2017-1.png" />
            
            </figure>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1HJtE0DrXqkYMMrZ1LJWBv/9ed239a91cdfa059a3a39d8bf968e19a/cf-login-2017-1.png" />
            
            </figure><p>Cloudflare.com &amp; Sign in page 2017</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1plmk4DXxK96hBC1YViAK9/4ad7ecfcf2482c7c27062b9d8ec5a58c/cf-dash-2017-1.png" />
            
            </figure><p>Our app dashboard in 2017</p>
    <div>
      <h4>2019</h4>
      <a href="#2019">
        
      </a>
    </div>
    
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2JBnD98asW2TqvggaFJQJl/1eacd96f45521546fa2cb0b50c81b296/cf-home-2019.png" />
            
            </figure>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5g3C4dpe5gG0hOuSlmavxh/723e01421dff284f53dccdbeec4cb166/cf-dash-2019.png" />
            
            </figure><p>Cloudflare homepage &amp; updated Dashboard in 2019</p>
    <div>
      <h2>Next steps</h2>
      <a href="#next-steps">
        
      </a>
    </div>
    <p>Trying to keep dozens of sites all using the same palette in a consistent manner across time is a task that you can never complete. It’s an ongoing maintenance problem. Engineers familiar with the color system leave, new engineers join and need to learn how the system works. People still launch sites using a different palette that doesn’t meet accessibility standards. Our work continues to be cut out for us. As they say, a garden doesn’t tend itself.</p><p>If we do ever revisit our brand colors, we're excited to have infrastructure in place to update our apps and several of our satellite sites with significantly less effort than our first time around.</p>
    <div>
      <h2>Resources</h2>
      <a href="#resources">
        
      </a>
    </div>
    <p>Some of our favorite materials and resources we found while exploring this problem space.</p>
    <div>
      <h3>Apps</h3>
      <a href="#apps">
        
      </a>
    </div>
    <ul><li><p><a href="https://cloudflare.design/color">Color - Cloudflare Design</a></p></li><li><p><a href="https://color.method.ac/">Color matching game - Method</a></p></li><li><p><a href="http://colorbox.io">Colorbox - Lyft</a></p></li><li><p><a href="https://jxnblk.github.io/colorable/demos/matrix/">Colorable - Brent Jackson</a></p></li><li><p><a href="https://kevingutowski.github.io/color.html">Color contrast picker - Kevin Gutowski</a></p></li></ul>
    <div>
      <h3>Writing</h3>
      <a href="#writing">
        
      </a>
    </div>
    <ul><li><p><a href="https://stripe.com/en-ca/blog/accessible-color-systems">Designing accessible color systems - Stripe</a></p></li><li><p><a href="https://design.lyft.com/re-approaching-color-9e604ba22c88">Re-approaching Color - Lyft</a></p></li><li><p><a href="http://www.psy.ritsumei.ac.jp/~akitaoka/colorconstancy7e.html">Color constancy - Akiyoshi Kitaoka</a></p></li><li><p><a href="https://en.wikipedia.org/wiki/Weber–Fechner_law">Weber-Fechner law</a></p></li></ul>
    <div>
      <h3>Code</h3>
      <a href="#code">
        
      </a>
    </div>
    <ul><li><p><a href="https://github.com/jxnblk/colorable">Colorable</a></p></li><li><p><a href="https://vis4.net/chromajs/">Chroma.js</a></p></li><li><p><a href="https://github.com/lyft/coloralgorithm">Color algorithm</a></p></li></ul>
    <div>
      <h3>Videos</h3>
      <a href="#videos">
        
      </a>
    </div>
    <ul><li><p><a href="https://www.youtube.com/watch?v=AbpS3Z3xXE0">Interaction of color systems - Diana Mounter, GitHub</a></p></li><li><p><a href="https://www.youtube.com/watch?v=bmeo_13QtrU">Designing a comprehensive color system - Linda Dong, Lyft</a></p></li></ul> ]]></content:encoded>
            <category><![CDATA[Deep Dive]]></category>
            <category><![CDATA[Design]]></category>
            <category><![CDATA[Dashboard]]></category>
            <guid isPermaLink="false">7gxf0yLEPoM0AmOvrghCyd</guid>
            <dc:creator>Sam Mason de Caires</dc:creator>
            <dc:creator>Adam Morse</dc:creator>
        </item>
    </channel>
</rss>