-
Notifications
You must be signed in to change notification settings - Fork 7.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(route): add A List Apart #12960
Conversation
Co-authored-by: Tony <TonyRL@users.noreply.github.com>
Successfully generated as following: http://localhost:1200/alistapart - Success ✔️<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"
>
<channel>
<title><![CDATA[A List Apart]]></title>
<link>https://alistapart.com/articles</link>
<atom:link href="http://localhost:1200/alistapart" rel="self" type="application/rss+xml" />
<description><![CDATA[Articles on aListApart.com - Made with love by RSSHub(https://github.com/DIYgod/RSSHub)]]></description>
<generator>RSSHub</generator>
<webMaster>i@diygod.me (DIYgod)</webMaster>
<language>en-us</language>
<lastBuildDate>Wed, 09 Aug 2023 12:29:14 GMT</lastBuildDate>
<ttl>5</ttl>
<item>
<title><![CDATA[Humility: An Essential Value]]></title>
<description><![CDATA[<p>Humility, a designer’s essential value—that has a nice ring to it. What about humility, an office manager’s essential value? Or a dentist’s? Or a librarian’s? They all sound great. When humility is our guiding light, the path is always open for fulfillment, evolution, connection, and engagement. In this chapter, we’re going to talk about why.</p>
<p>That said, this is a book for designers, and to that end, I’d like to start with a story—well, a journey, really. It’s a personal one, and I’m going to make myself a bit vulnerable along the way. I call it:</p>
<h2 id="section2" class="wp-block-heading"><strong>The Tale of Justin’s Preposterous Pate</strong><a class="subhead-anchor" href="https://alistapart.com/article/humility-an-essential-value/#section2">#section2</a></h2>
<p>When I was coming out of art school, a long-haired, goateed neophyte, print was a known quantity to me; design on the web, however, was rife with complexities to navigate and discover, a problem to be solved. Though I had been formally trained in graphic design, typography, and layout, what fascinated me was how these traditional skills might be applied to a fledgling digital landscape. This theme would ultimately shape the rest of my career.</p>
<p>So rather than graduate and go into print like many of my friends, I devoured HTML and JavaScript books into the wee hours of the morning and taught myself how to code during my senior year. I wanted—nay, needed—to better understand the underlying implications of what my design decisions would mean once rendered in a browser.</p>
<p>The late ’90s and early 2000s were the so-called “Wild West” of web design. Designers at the time were all figuring out how to apply design and visual communication to the digital landscape. What were the rules? How could we break them and still engage, entertain, and convey information? At a more macro level, how could my values, inclusive of humility, respect, and connection, align in tandem with that? I was hungry to find out.</p>
<p>Though I’m talking about a different era, those are timeless considerations between non-career interactions and the world of design. What are your core passions, or values, that transcend medium? It’s essentially the same concept we discussed earlier on the direct parallels between what fulfills you, agnostic of the tangible or digital realms; the core themes are all the same.</p>
<p>First within tables, animated GIFs, Flash, then with Web Standards, <code>div</code>s, and CSS, there was personality, raw unbridled creativity, and unique means of presentment that often defied any semblance of a visible grid. Splash screens and “browser requirement” pages aplenty. Usability and accessibility were typically victims of such a creation, but such paramount facets of any digital design were largely (and, in hindsight, unfairly) disregarded at the expense of experimentation.</p>
<p>For example, this iteration of my personal portfolio site (“the pseudoroom”) from that era was experimental, if not a bit heavy- handed, in the visual communication of the concept of a living sketchbook. Very skeuomorphic. I collaborated with fellow designer and dear friend Marc Clancy (now a co-founder of the creative project organizing app Milanote) on this one, where we’d first sketch and then pass a Photoshop file back and forth to trick things out and play with varied user interactions. Then, I’d break it down and code it into a digital layout.</p>
<figure id="figure1" class="wp-block-image size-full is-resized"><img src="https://i0.wp.com/alistapart.com/wp-content/uploads/2023/06/designers-journey-2-1.jpg?w=846&ssl=1" alt="" class="wp-image-7173967" data-recalc-dims="1" referrerpolicy="no-referrer"><figcaption class="wp-element-caption">Figure 1: “the pseudoroom” website, hitting the sketchbook metaphor hard.</figcaption></figure>
<p>Along with design folio pieces, the site also offered free downloads for Mac OS customizations: desktop wallpapers that were effectively design experimentation, custom-designed typefaces, and desktop icons.</p>
<p>From around the same time, GUI Galaxy was a design, pixel art, and Mac-centric news portal some graphic designer friends and I conceived, designed, developed, and deployed.</p>
<figure id="figure2" class="wp-block-image size-large is-resized"><img src="https://i0.wp.com/alistapart.com/wp-content/uploads/2023/06/designers-journey-2-2.jpg?w=846&ssl=1" alt="" class="wp-image-7173968" data-recalc-dims="1" referrerpolicy="no-referrer"><figcaption class="wp-element-caption">Figure 2: GUI Galaxy, web standards-compliant design news portal</figcaption></figure>
<p>Design news portals were incredibly popular during this period, featuring (what would now be considered) Tweet-size, small-format snippets of pertinent news from the categories I previously mentioned. If you took Twitter, curated it to a few categories, and wrapped it in a custom-branded experience, you’d have a design news portal from the late 90s / early 2000s.</p>
<p>We as designers had evolved and created a bandwidth-sensitive, web standards award-winning, much more accessibility-conscious website. Still ripe with experimentation, yet more mindful of equitable engagement. You can see a couple of content panes here, noting general news (tech, design) and Mac-centric news below. We also offered many of the custom downloads I cited before as present on my folio site but branded and themed to GUI Galaxy.</p>
<p>The site’s backbone was a homegrown CMS, with the presentation layer consisting of global design + illustration + news author collaboration. And the collaboration effort here, in addition to experimentation on a ‘brand’ and content delivery, was hitting my core. We were designing something bigger than any single one of us and connecting with a global audience.</p>
<p>Collaboration and connection transcend medium in their impact, immensely fulfilling me as a designer.</p>
<p>Now, why am I taking you down this trip of design memory lane? Two reasons.</p>
<p>First, there’s a reason for the nostalgia for that design era (the “Wild West” era, as I called it earlier): the inherent exploration, personality, and creativity that saturated many design portals and personal portfolio sites. Ultra-finely detailed pixel art UI, custom illustration, bespoke vector graphics, all underpinned by a strong design community.</p>
<p>Today’s web design has been in a period of stagnation. I suspect there’s a strong chance you’ve seen a site whose structure looks something like this: a hero image / banner with text overlaid, perhaps with a lovely rotating carousel of images (laying the snark on heavy there), a call to action, and three columns of sub-content directly beneath. Maybe an icon library is employed with selections that vaguely relate to their respective content.</p>
<p>Design, as it’s applied to the digital landscape, is in dire need of thoughtful layout, typography, and visual engagement that goes hand-in-hand with all the modern considerations we now know are paramount: usability. Accessibility. Load times and bandwidth- sensitive content delivery. A responsive presentation that meets human beings wherever they’re engaging from. We must be mindful of, and respectful toward, those concerns—but not at the expense of creativity of visual communication or via replicating cookie-cutter layouts.</p>
<h2 id="section3" class="wp-block-heading">Pixel Problems<a class="subhead-anchor" href="https://alistapart.com/article/humility-an-essential-value/#section3">#section3</a></h2>
<p>Websites during this period were often designed and built on Macs whose OS and desktops looked something like this. This is Mac OS 7.5, but 8 and 9 weren’t that different.</p>
<figure id="figure3" class="wp-block-image size-large is-resized"><img src="https://i0.wp.com/alistapart.com/wp-content/uploads/2023/06/designers-journey-2-3.jpg?w=846&ssl=1" alt="" class="wp-image-7173969" data-recalc-dims="1" referrerpolicy="no-referrer"><figcaption class="wp-element-caption">Figure 3: A Mac OS 7.5-centric desktop.</figcaption></figure>
<p>Desktop icons fascinated me: how could any single one, at any given point, stand out to get my attention? In this example, the user’s desktop is tidy, but think of a more realistic example with icon pandemonium. Or, say an icon was part of a larger system grouping (fonts, extensions, control panels)—how did it also maintain cohesion amongst a group?</p>
<p>These were 32 x 32 pixel creations, utilizing a 256-color palette, designed pixel-by-pixel as mini mosaics. To me, this was the embodiment of digital visual communication under such ridiculous constraints. And often, ridiculous restrictions can yield the purification of concept and theme.</p>
<p>So I began to research and do my homework. I was a student of this new medium, hungry to dissect, process, discover, and make it my own.</p>
<p>Expanding upon the notion of exploration, I wanted to see how I could push the limits of a 32×32 pixel grid with that 256-color palette. Those ridiculous constraints forced a clarity of concept and presentation that I found incredibly appealing. The digital gauntlet had been tossed, and that challenge fueled me. And so, in my dorm room into the wee hours of the morning, I toiled away, bringing conceptual sketches into mini mosaic fruition.</p>
<p>These are some of my creations, utilizing the only tool available at the time to create icons called ResEdit. ResEdit was a clunky, built-in Mac OS utility not really made for exactly what we were using it for. At the core of all of this work: Research. Challenge. Problem- solving. Again, these core connection-based values are agnostic of medium.</p>
<figure id="figure4" class="wp-block-image size-large is-resized"><img src="https://i0.wp.com/alistapart.com/wp-content/uploads/2023/06/designers-journey-2-4.png?w=846&ssl=1" alt="" class="wp-image-7173970" data-recalc-dims="1" referrerpolicy="no-referrer"><figcaption class="wp-element-caption">Figure 4: A selection of my pixel art design, 32×32 pixel canvas, 8-bit palette</figcaption></figure>
<p>There’s one more design portal I want to talk about, which also serves as the second reason for my story to bring this all together.</p>
<p>This is K10k, short for Kaliber 1000. K10k was founded in 1998 by Michael Schmidt and Toke Nygaard, and was <strong>the </strong>design news portal on the web during this period. With its pixel art-fueled presentation, ultra-focused care given to every facet and detail, and with many of the more influential designers of the time who were invited to be news authors on the site, well… it was the place to be, my friend. With respect where respect is due, GUI Galaxy’s concept was inspired by what these folks were doing.</p>
<figure id="figure5" class="wp-block-image size-full is-resized"><img src="https://i0.wp.com/alistapart.com/wp-content/uploads/2023/06/designers-journey-2-5.png?w=846&ssl=1" alt="" class="wp-image-7173971" data-recalc-dims="1" referrerpolicy="no-referrer"><figcaption class="wp-element-caption">Figure 5: The K10k website</figcaption></figure>
<p>For my part, the combination of my web design work and pixel art exploration began to get me some notoriety in the design scene. Eventually, K10k noticed and added me as one of their very select group of news authors to contribute content to the site.</p>
<p>Amongst my personal work and side projects—and now with this inclusion—in the design community, this put me on the map. My design work also began to be published in various printed collections, in magazines domestically and overseas, and featured on other design news portals. With that degree of success while in my early twenties, something else happened:</p>
<p>I evolved—devolved, really—into a colossal asshole (and in just about a year out of art school, no less). The press and the praise became what fulfilled me, and they went straight to my head. They inflated my ego. I actually felt somewhat superior to my fellow designers.</p>
<p>The casualties? My design stagnated. Its evolution—my evolution— stagnated.</p>
<p>I felt so supremely confident in my abilities that I effectively stopped researching and discovering. When previously sketching concepts or iterating ideas in lead was my automatic step one, I instead leaped right into Photoshop. I drew my inspiration from the smallest of sources (and with blinders on). Any critique of my work from my peers was often vehemently dismissed. The most tragic loss: I had lost touch with my values.</p>
<p>My ego almost cost me some of my friendships and burgeoning professional relationships. I was toxic in talking about design and in collaboration. But thankfully, those same friends gave me a priceless gift: candor. They called me out on my unhealthy behavior.</p>
<p>Admittedly, it was a gift I initially did not accept but ultimately was able to deeply reflect upon. I was soon able to accept, and process, and course correct. The realization laid me low, but the re-awakening was essential. I let go of the “reward” of adulation and re-centered upon what stoked the fire for me in art school. Most importantly: I got back to my core values.</p>
<h2 id="section4" class="wp-block-heading">Always Students<a class="subhead-anchor" href="https://alistapart.com/article/humility-an-essential-value/#section4">#section4</a></h2>
<p>Following that short-term regression, I was able to push forward in my personal design and career. And I could self-reflect as I got older to facilitate further growth and course correction as needed.</p>
<p>As an example, let’s talk about the Large Hadron Collider. The LHC was designed <em>“to help answer some of the fundamental open questions in physics, which concern the basic laws governing the interactions and forces among the elementary objects, the deep structure of space and time, and in particular the interrelation between quantum mechanics and general relativity.” </em>Thanks, Wikipedia.</p>
<p>Around fifteen years ago, in one of my earlier professional roles, I designed the interface for the application that generated the LHC’s particle collision diagrams. These diagrams are the rendering of what’s actually happening inside the Collider during any given particle collision event and are often considered works of art unto themselves.</p>
<p>Designing the interface for this application was a fascinating process for me, in that I worked with Fermilab physicists to understand what the application was trying to achieve, but also how the physicists themselves would be using it. To that end, in this role,</p>
<p>I cut my teeth on usability testing, working with the Fermilab team to iterate and improve the interface. How they spoke and what they spoke about was like an alien language to me. And by making myself humble and working under the mindset that I was but a student, I made myself available to be a part of their world to generate that vital connection.</p>
<p>I also had my first ethnographic observation experience: going to the Fermilab location and observing how the physicists used the tool in their actual environment, on their actual terminals. For example, one takeaway was that due to the level of ambient light-driven contrast within the facility, the data columns ended up using white text on a dark gray background instead of black text-on-white. This enabled them to pore over reams of data during the day and ease their eye strain. And Fermilab and CERN are government entities with rigorous accessibility standards, so my knowledge in that realm also grew. The barrier-free design was another essential form of connection.</p>
<p>So to those core drivers of my visual problem-solving soul and ultimate fulfillment: discovery, exposure to new media, observation, human connection, and evolution. What opened the door for those values was me checking my ego before I walked through it.</p>
<p>An evergreen willingness to listen, learn, understand, grow, evolve, and connect yields our best work. In particular, I want to focus on the words ‘grow’ and ‘evolve’ in that statement. If we are always students of our craft, we are also continually making ourselves available to evolve. Yes, we have years of applicable design study under our belt. Or the focused lab sessions from a UX bootcamp. Or the monogrammed portfolio of our work. Or, ultimately, decades of a career behind us.</p>
<p>But all that said: experience does not equal “expert.”</p>
<p>As soon as we close our minds via an inner monologue of ‘knowing it all’ or branding ourselves a “#thoughtleader” on social media, the designer we <strong>are </strong>is our final form. The designer we <strong>can be </strong>will never exist.</p>
Colin Eagan and Jeffrey MacIntyre offer a “ground-up” approach to implementing personalized digital experiences that are intentional, ethical, and technologically sound.
Is mobile-first CSS always the best option? Patrick Clancey explores the pros and cons and lays out an alternative.
Learn how to engage stakeholders, focus on impactful objectives, and measure the results in this template for ethical design.
What can we do with thirty pixels? Windows Controls Overlay frees us from 40 years of history telling us how apps should look.
Seriously, do not ever design screens again without first answering these questions: what are the objects and how do they relate?]]></description>
<pubDate>Thu, 22 Jun 2023 13:00:00 GMT</pubDate>
<guid isPermaLink="false">https://alistapart.com/article/humility-an-essential-value/</guid>
<link>https://alistapart.com/article/humility-an-essential-value/</link>
<author><![CDATA[Justin Dauer]]></author>
<category>Design</category>
</item>
<item>
<title><![CDATA[Personalization Pyramid: A Framework for Designing with User Data]]></title>
<description><![CDATA[<p>As a UX professional in today’s data-driven landscape, it’s increasingly likely that you’ve been asked to design a personalized digital experience, whether it’s a public website, user portal, or native application. Yet while there continues to be no shortage of marketing hype around personalization platforms, we still have very few standardized approaches for implementing personalized UX.</p>
<p>That’s where we come in. After completing dozens of personalization projects over the past few years, we gave ourselves a goal: could you create a holistic personalization framework specifically for UX practitioners? The <strong>Personalization Pyramid</strong> is a designer-centric model for standing up human-centered personalization programs, spanning data, segmentation, content delivery, and overall goals. By using this approach, you will be able to understand the core components of a contemporary, UX-driven personalization program (or at the very least know enough to get started). </p>
<figure id="figure1" class="wp-block-image size-large"><img width="960" height="558" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image20.png?resize=960%2C558&ssl=1" alt="A chart answering the question Do you have the resources you need to run personalization in your organization? Globally, 13% don’t 33% have limited access, 39% have it (on demand), and 15% have it dedicated." class="wp-image-7173666" data-recalc-dims="1" referrerpolicy="no-referrer"><figcaption class="wp-element-caption"><p><strong>Growing tools for personalization:</strong> According to a Dynamic Yield survey, 39% of respondents felt support is available on-demand when a business case is made for it (up 15% from 2020).</p><p><small>Source: “The State of Personalization Maturity – Q4 2021” Dynamic Yield conducted its annual maturity survey across roles and sectors in the Americas (AMER), Europe and the Middle East (EMEA), and the Asia-Pacific (APAC) regions. This marks the fourth consecutive year publishing our research, which includes more than 450 responses from individuals in the C-Suite, Marketing, Merchandising, CX, Product, and IT.</small></p></figcaption></figure>
<h2 id="section2" class="wp-block-heading"><strong>Getting Started</strong><a class="subhead-anchor" href="https://alistapart.com/article/personalization-pyramid/#section2">#section2</a></h2>
<p>For the sake of this article, we’ll assume you’re already familiar with the basics of digital personalization. A good overview can be found here: <a href="https://www.uxbooth.com/articles/website-personalization-planning/">Website Personalization Planning</a>. While UX projects in this area can take on many different forms, they often stem from similar starting points. </p>
<p><strong>Common scenarios for starting a personalization project:</strong></p>
<ul>
<li>Your organization or client purchased a content management system (CMS) or marketing automation platform (MAP) or related technology that supports personalization</li>
<li>The CMO, CDO, or CIO has identified personalization as a goal</li>
<li>Customer data is disjointed or ambiguous</li>
<li>You are running some isolated targeting campaigns or A/B testing</li>
<li>Stakeholders disagree on personalization approach</li>
<li>Mandate of customer privacy rules (e.g. GDPR) requires revisiting existing user targeting practices</li>
</ul>
<figure id="figure2" class="wp-block-image size-full is-resized"><img src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image22.png?resize=768%2C576&ssl=1" alt="Two men and a woman discussing personalization using a card deck. They are seated at a round table in a hotel conference room. The workshop leaders, two women, are at a podium in the background." class="wp-image-7173667" width="768" height="576" data-recalc-dims="1" referrerpolicy="no-referrer"><figcaption class="wp-element-caption">Workshopping personalization at a conference.</figcaption></figure>
<p>Regardless of where you begin, a successful personalization program will require the same core building blocks. We’ve captured these as the “levels” on the pyramid. Whether you are a UX designer, researcher, or strategist, understanding the core components can help make your contribution successful. </p>
<figure id="figure3" class="wp-block-image size-large is-resized"><img src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image14.png?resize=488%2C512&ssl=1" alt="The Personalization Pyramid visualized. The pyramid is stacks labeled, from the bottom, raw data (1m+), actionable data (100k+), user segments (1k+), contexts & campaigns (100s), touchpoints (dozens), goals (handful). The North Star (one) is above. An arrow for prescriptive, business driven data goes up the left side and an arrow for adaptive user-driven data goes down the right side." class="wp-image-7173665" width="488" height="512" data-recalc-dims="1" referrerpolicy="no-referrer"><figcaption class="wp-element-caption">From the ground up: Soup-to-nuts personalization, without going nuts.</figcaption></figure>
<p>From top to bottom, the levels include:</p>
<ol>
<li><strong>North Star: </strong>What larger strategic objective is driving the personalization program? </li>
<li><strong>Goals:</strong> What are the specific, measurable outcomes of the program? </li>
<li><strong>Touchpoints: </strong>Where will the personalized experience be served?</li>
<li><strong>Contexts and Campaigns: </strong>What personalization content will the user see?</li>
<li><strong>User Segments:</strong> What constitutes a unique, usable audience? </li>
<li><strong>Actionable Data: </strong>What reliable and authoritative data is captured by our technical platform to drive personalization? </li>
<li><strong>Raw Data: </strong>What wider set of data is conceivably available (already in our setting) allowing you to personalize?</li>
</ol>
<p>We’ll go through each of these levels in turn. To help make this actionable, we created an accompanying <strong>deck of cards</strong> to illustrate specific examples from each level. We’ve found them helpful in personalization brainstorming sessions, and will include examples for you here.</p>
<figure id="figure4" class="wp-block-image size-full"><img loading="lazy" width="320" height="215" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image2.png?resize=320%2C215&ssl=1" alt="A deck of personalization brainstorming cards (the size of playing cards) against a black background." class="wp-image-7173668" data-recalc-dims="1" referrerpolicy="no-referrer"><figcaption class="wp-element-caption"><strong>Personalization pack:</strong> Deck of cards to help kickstart your personalization brainstorming.</figcaption></figure>
<h2 id="section3" class="wp-block-heading"><strong>Starting at the Top</strong><a class="subhead-anchor" href="https://alistapart.com/article/personalization-pyramid/#section3">#section3</a></h2>
<p>The components of the pyramid are as follows:</p>
<h3 id="section4" class="wp-block-heading">North Star<a class="subhead-anchor" href="https://alistapart.com/article/personalization-pyramid/#section4">#section4</a></h3>
<p>A north star is what you are aiming for overall with your personalization program (big or small). The North Star defines the (one) overall mission of the personalization program. What do you wish to accomplish? North Stars cast a shadow. The bigger the star, the bigger the shadow. Example of North Starts might include: </p>
<ol>
<li><strong>Function:</strong> Personalize based on basic user inputs. Examples: “Raw” notifications, basic search results, system user settings and configuration options, general customization, basic optimizations</li>
<li><strong>Feature:</strong> Self-contained personalization componentry. Examples: “Cooked” notifications, advanced optimizations (geolocation), basic dynamic messaging, customized modules, automations, recommenders</li>
<li><strong>Experience:</strong> Personalized user experiences across multiple interactions and user flows. Examples: Email campaigns, landing pages, advanced messaging (i.e. C2C chat) or conversational interfaces, larger user flows and content-intensive optimizations (localization).</li>
<li><strong>Product:</strong> Highly differentiating personalized product experiences. Examples: Standalone, branded experiences with personalization at their core, like the “algotorial” playlists by Spotify such as Discover Weekly.</li>
</ol>
<figure id="figure5" class="wp-block-gallery has-nested-images columns-default is-cropped wp-block-gallery-1 is-layout-flex wp-block-gallery-is-layout-flex">
<figure id="figure6" class="wp-block-image size-large"><img loading="lazy" width="656" height="1024" data-id="7173669" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image7.jpg?resize=656%2C1024&ssl=1" alt="Function: React to basic user inputs" class="wp-image-7173669" data-recalc-dims="1" referrerpolicy="no-referrer"></figure>
<figure id="figure7" class="wp-block-image size-large"><img loading="lazy" width="656" height="1024" data-id="7173670" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image18.jpg?resize=656%2C1024&ssl=1" alt="Feature: personalized modules" class="wp-image-7173670" data-recalc-dims="1" referrerpolicy="no-referrer"></figure>
<figure id="figure8" class="wp-block-image size-large"><img loading="lazy" width="656" height="1024" data-id="7173671" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image8.jpg?resize=656%2C1024&ssl=1" alt="Experience: Integrated personalization" class="wp-image-7173671" data-recalc-dims="1" referrerpolicy="no-referrer"></figure>
<figcaption class="blocks-gallery-caption wp-element-caption"><strong>North star cards. </strong>These can help orient your team towards a common goal that personalization will help achieve; Also, these are useful for characterizing the end-state ambition of the presently stated personalization effort.</figcaption></figure>
<h3 id="section5" class="wp-block-heading">Goals<a class="subhead-anchor" href="https://alistapart.com/article/personalization-pyramid/#section5">#section5</a></h3>
<p>As in any good UX design, personalization can help accelerate <a href="https://www.uxbooth.com/articles/designing-for-customer-intentions-part-1/">designing with customer intentions</a><strong>. Goals</strong> are the tactical and measurable metrics that will prove the overall program is successful. A good place to start is with your current analytics and measurement program and metrics you can benchmark against. In some cases, new goals may be appropriate. The key thing to remember is that <em>personalization itself is not a goal</em>, rather it is a means to an end. Common goals include:</p>
<ul>
<li>Conversion</li>
<li>Time on task</li>
<li>Net promoter score (NPS)</li>
<li>Customer satisfaction </li>
</ul>
<figure id="figure9" class="wp-block-gallery has-nested-images columns-default is-cropped wp-block-gallery-3 is-layout-flex wp-block-gallery-is-layout-flex">
<figure id="figure10" class="wp-block-image size-large"><img loading="lazy" width="656" height="1024" data-id="7173674" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image12.jpg?resize=656%2C1024&ssl=1" alt="NPS: Net Promoter Score" class="wp-image-7173674" data-recalc-dims="1" referrerpolicy="no-referrer"></figure>
<figure id="figure11" class="wp-block-image size-large"><img loading="lazy" width="656" height="1024" data-id="7173672" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image23.jpg?resize=656%2C1024&ssl=1" alt="Time on Task: Users move quicker" class="wp-image-7173672" data-recalc-dims="1" referrerpolicy="no-referrer"></figure>
<figure id="figure12" class="wp-block-image size-large"><img loading="lazy" width="656" height="1024" data-id="7173673" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image13.jpg?resize=656%2C1024&ssl=1" alt="Conversion: Move more of the thing" class="wp-image-7173673" data-recalc-dims="1" referrerpolicy="no-referrer"></figure>
<figcaption class="blocks-gallery-caption wp-element-caption"><strong>Goal cards.</strong> Examples of some common KPIs related to personalization that are concrete and measurable.</figcaption></figure>
<h3 id="section6" class="wp-block-heading">Touchpoints<a class="subhead-anchor" href="https://alistapart.com/article/personalization-pyramid/#section6">#section6</a></h3>
<p>Touchpoints are where the personalization happens. As a UX designer, this will be one of your largest areas of responsibility. The touchpoints available to you will depend on how your personalization and associated technology capabilities are instrumented, and should be rooted in improving a user’s experience at a particular point in the journey. Touchpoints can be multi-device (mobile, in-store, website) but also more granular (web banner, web pop-up etc.). Here are some examples:</p>
<p><strong>Channel-level </strong>Touchpoints</p>
<ul>
<li>Email: Role</li>
<li>Email: Time of open</li>
<li>In-store display (JSON endpoint)</li>
<li>Native app</li>
<li>Search</li>
</ul>
<p><strong>Wireframe-level </strong>Touchpoints</p>
<ul>
<li>Web overlay</li>
<li>Web alert bar</li>
<li>Web banner</li>
<li>Web content block</li>
<li>Web menu</li>
</ul>
<figure id="figure13" class="wp-block-gallery has-nested-images columns-default is-cropped wp-block-gallery-5 is-layout-flex wp-block-gallery-is-layout-flex">
<figure id="figure14" class="wp-block-image size-large"><img loading="lazy" width="656" height="1024" data-id="7173677" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image25.jpg?resize=656%2C1024&ssl=1" alt="In-store Display: End-cap interfaces" class="wp-image-7173677" data-recalc-dims="1" referrerpolicy="no-referrer"></figure>
<figure id="figure15" class="wp-block-image size-large"><img loading="lazy" width="656" height="1024" data-id="7173675" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image11.jpg?resize=656%2C1024&ssl=1" alt="Email: Time, personalize at time of open" class="wp-image-7173675" data-recalc-dims="1" referrerpolicy="no-referrer"></figure>
<figure id="figure16" class="wp-block-image size-large"><img loading="lazy" width="656" height="1024" data-id="7173676" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image19.jpg?resize=656%2C1024&ssl=1" alt="Content Block: Into the woodwork" class="wp-image-7173676" data-recalc-dims="1" referrerpolicy="no-referrer"></figure>
<figcaption class="blocks-gallery-caption wp-element-caption"><strong>Touchpoint cards. </strong>Examples of common personalization touchpoints: these can vary from narrow (e.g., email) to broad (e.g., in-store).</figcaption></figure>
<p>If you’re designing for web interfaces, for example, you will likely need to include personalized “zones” in your wireframes. The content for these can be presented programmatically in touchpoints based on our next step, contexts and campaigns.</p>
<figure id="figure17" class="wp-block-gallery has-nested-images columns-default is-cropped wp-block-gallery-7 is-layout-flex wp-block-gallery-is-layout-flex">
<figure id="figure18" class="wp-block-image size-large"><img loading="lazy" width="732" height="974" data-id="7173678" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image21.png?resize=732%2C974&ssl=1" alt="" class="wp-image-7173678" data-recalc-dims="1" referrerpolicy="no-referrer"></figure>
<figure id="figure19" class="wp-block-image size-large"><img loading="lazy" width="708" height="922" data-id="7173679" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image17.png?resize=708%2C922&ssl=1" alt="" class="wp-image-7173679" data-recalc-dims="1" referrerpolicy="no-referrer"></figure>
<figcaption class="blocks-gallery-caption wp-element-caption"><strong>Targeted Zones:</strong> Examples from Kibo of personalized “zones” on page-level wireframes occurring at various stages of a user journey (Engagement phase at left and Purchase phase at right.)<br><br>Source: “Essential Guide to End-to-End Personaliztion” by Kibo.</figcaption></figure>
<h3 id="section7" class="wp-block-heading">Contexts and Campaigns<a class="subhead-anchor" href="https://alistapart.com/article/personalization-pyramid/#section7">#section7</a></h3>
<p>Once you’ve outlined some touchpoints, you can consider the actual personalized content a user will receive. Many personalization tools will refer to these as “campaigns” (so, for example, a campaign on a web banner for new visitors to the website). These will programmatically be shown at certain touchpoints to certain user segments, as defined by user data. At this stage, we find it helpful to consider two separate models: a <strong>context model</strong> and a <strong>content model</strong>. The context helps you consider the level of engagement of the user at the personalization moment, for example a user casually browsing information vs. doing a deep-dive. Think of it in terms of information retrieval behaviors. The content model can then help you determine what type of personalization to serve based on the context (for example, an “Enrich” campaign that shows related articles may be a suitable supplement to extant content).</p>
<p>Personalization <strong>Context</strong> Model:</p>
<ol>
<li>Browse</li>
<li>Skim</li>
<li>Nudge</li>
<li>Feast</li>
</ol>
<p>Personalization <strong>Content</strong> Model:</p>
<ol>
<li>Alert</li>
<li>Make Easier</li>
<li>Cross-Sell</li>
<li>Enrich</li>
</ol>
<p>We’ve written extensively about each of these models elsewhere, so if you’d like to read more you can check out Colin’s <a href="https://alistapart.com/article/emerging-ux-role-in-personalization/">Personalization Content Model</a> and Jeff’s <a href="https://bucket.circle.so/c/field-notes/progressive-personalization-a-decisionmaking-model-for-better-outcomes-in-personalized-ux">Personalization Context Model</a>. </p>
<figure id="figure20" class="wp-block-gallery has-nested-images columns-default is-cropped wp-block-gallery-9 is-layout-flex wp-block-gallery-is-layout-flex">
<figure id="figure21" class="wp-block-image size-large"><img loading="lazy" width="656" height="1024" data-id="7173681" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image28.jpg?resize=656%2C1024&ssl=1" alt="Cross Sell: You may also like…" class="wp-image-7173681" data-recalc-dims="1" referrerpolicy="no-referrer"></figure>
<figure id="figure22" class="wp-block-image size-large"><img loading="lazy" width="656" height="1024" data-id="7173682" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image3.jpg?resize=656%2C1024&ssl=1" alt="Enrich: You might find this interesting" class="wp-image-7173682" data-recalc-dims="1" referrerpolicy="no-referrer"></figure>
<figure id="figure23" class="wp-block-image size-large"><img loading="lazy" width="656" height="1024" data-id="7173680" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image27.jpg?resize=656%2C1024&ssl=1" alt="Browse: Lean back, shallow engagement" class="wp-image-7173680" data-recalc-dims="1" referrerpolicy="no-referrer"></figure>
<figcaption class="blocks-gallery-caption wp-element-caption"><strong>Campaign and Context cards:</strong> This level of the pyramid can help your team focus around the types of personalization to deliver end users and the use-cases in which they will experience it.</figcaption></figure>
<h3 id="section8" class="wp-block-heading">User Segments<a class="subhead-anchor" href="https://alistapart.com/article/personalization-pyramid/#section8">#section8</a></h3>
<p>User segments can be created prescriptively or adaptively, based on user research (e.g. via rules and logic tied to set user behaviors or via A/B testing). At a minimum you will likely need to consider how to treat the <em>unknown</em> or first-time visitor, the <em>guest</em> or returning visitor for whom you may have a stateful cookie (or equivalent post-cookie identifier), or the <em>authenticated</em> visitor who is logged in. Here are some examples from the personalization pyramid:</p>
<ul>
<li>Unknown</li>
<li>Guest</li>
<li>Authenticated</li>
<li>Default</li>
<li>Referred</li>
<li>Role</li>
<li>Cohort</li>
<li>Unique ID</li>
</ul>
<figure id="figure24" class="wp-block-gallery has-nested-images columns-default is-cropped wp-block-gallery-11 is-layout-flex wp-block-gallery-is-layout-flex">
<figure id="figure25" class="wp-block-image size-large"><img loading="lazy" width="656" height="1024" data-id="7173685" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image16.jpg?resize=656%2C1024&ssl=1" alt="Authenticated: Logged in with token" class="wp-image-7173685" data-recalc-dims="1" referrerpolicy="no-referrer"></figure>
<figure id="figure26" class="wp-block-image size-large"><img loading="lazy" width="656" height="1024" data-id="7173683" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image10.jpg?resize=656%2C1024&ssl=1" alt="Unknown: Could be anyone really" class="wp-image-7173683" data-recalc-dims="1" referrerpolicy="no-referrer"></figure>
<figure id="figure27" class="wp-block-image size-large"><img loading="lazy" width="656" height="1024" data-id="7173684" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image9.jpg?resize=656%2C1024&ssl=1" alt="Guest: Dropped a cookie" class="wp-image-7173684" data-recalc-dims="1" referrerpolicy="no-referrer"></figure>
<figcaption class="blocks-gallery-caption wp-element-caption"><strong>Segment cards.</strong> Examples of common personalization segments: at a minimum, you will need to consider the anonymous, guest, and logged in user types. Segmentation can get dramatically more complex from there.</figcaption></figure>
<h3 id="section9" class="wp-block-heading">Actionable Data<a class="subhead-anchor" href="https://alistapart.com/article/personalization-pyramid/#section9">#section9</a></h3>
<p>Every organization with any digital presence has data. It’s a matter of asking what data you can ethically collect on users, its inherent reliability and value, as to how can you use it (sometimes known as “data activation.”) Fortunately, the tide is turning to first-party data: a recent study by Twilio estimates some <strong>80% of businesses are using at least some type of first-party data</strong> to personalize the customer experience. </p>
<figure id="figure28" class="wp-block-image size-large"><img loading="lazy" width="960" height="669" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image6.png?resize=960%2C669&ssl=1" alt="Chart that answers the question "Why is your company focusing on using first-party data for personalization?" The top answer (at 53%) is "it’s higher quality." That is followed by "It’s easier to manage" (46%), "it provides better privacy" (45%), "it’s easier to obtain" (42%), "it’s more cost-effective" (40%), "it’s more ethical" (37%), "our customers want us to" (36%), "it’s the industry norm" (27%), "it’s easier to comply with regulations" (27%), and "we are phasing out 3rd party cookies" (21%)." class="wp-image-7173686" data-recalc-dims="1" referrerpolicy="no-referrer"><figcaption class="wp-element-caption"><small>Source: “The State of Personalization 2021” by Twilio. Survey respondents were n=2,700 adult consumers who have purchased something online in the past 6 months, and n=300 adult manager+ decision-makers at consumer-facing companies that provide goods and/or services online. Respondents were from the United States, United Kingdom, Australia, and New Zealand.Data was collected from April 8 to April 20, 2021.</small></figcaption></figure>
<p>First-party data represents multiple advantages on the UX front, including being relatively simple to collect, more likely to be accurate, and less susceptible to the “creep factor” of third-party data. So a key part of your UX strategy should be to determine what the best form of data collection is on your audiences. Here are some examples:</p>
<figure id="figure29" class="wp-block-gallery has-nested-images columns-default is-cropped wp-block-gallery-13 is-layout-flex wp-block-gallery-is-layout-flex">
<figure id="figure30" class="wp-block-image size-large"><img loading="lazy" width="656" height="1024" data-id="7173691" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image15.jpg?resize=656%2C1024&ssl=1" alt="Quizes: Tell us what you like" class="wp-image-7173691" data-recalc-dims="1" referrerpolicy="no-referrer"></figure>
<figure id="figure31" class="wp-block-image size-large"><img loading="lazy" width="656" height="1024" data-id="7173689" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image5.jpg?resize=656%2C1024&ssl=1" alt="Behavioral profiling: Males 40+ who wear fedoras" class="wp-image-7173689" data-recalc-dims="1" referrerpolicy="no-referrer"></figure>
<figure id="figure32" class="wp-block-image size-large"><img loading="lazy" width="656" height="1024" data-id="7173688" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image1.jpg?resize=656%2C1024&ssl=1" alt="Campaign Source: Your discount code 29780…" class="wp-image-7173688" data-recalc-dims="1" referrerpolicy="no-referrer"></figure>
</figure>
<figure id="figure33" class="wp-block-image size-full"><img loading="lazy" width="822" height="568" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image4.png?resize=822%2C568&ssl=1" alt="Chart showing the impact of personalization across different phases of personalization maturity. It shows that effort is high in the early phases, but drops off quickly starting in phase 3 (machine learning) while at the same time conversion rates, AOV, and ROI increase from a relatively low level to off the chart." class="wp-image-7173692" data-recalc-dims="1" referrerpolicy="no-referrer"><figcaption class="wp-element-caption"><strong>Figure 1.1.2:</strong> Example of a personalization maturity curve, showing progression from basic recommendations functionality to true individualization. Credit: https://kibocommerce.com/blog/kibos-personalization-maturity-chart/</figcaption></figure>
<p>There is a progression of profiling when it comes to recognizing and making decisioning about different audiences and their signals. It tends to move towards more granular constructs about smaller and smaller cohorts of users as time and confidence and data volume grow.</p>
<p>While some combination of <strong>implicit / explicit</strong> <strong>data</strong> is generally a prerequisite for any implementation (more commonly referred to as first party and third-party data) <strong>ML efforts</strong> are typically not cost-effective directly out of the box. This is because a strong data backbone and content repository is a prerequisite for optimization. But these approaches should be considered as part of the larger roadmap and may indeed help accelerate the organization’s overall progress. Typically at this point you will partner with key stakeholders and product owners to design a <strong>profiling model</strong>. The profiling model includes defining approach to configuring profiles, profile keys, profile cards and pattern cards. A multi-faceted approach to profiling which makes it scalable.</p>
<h2 id="section10" class="wp-block-heading">Pulling it Together<a class="subhead-anchor" href="https://alistapart.com/article/personalization-pyramid/#section10">#section10</a></h2>
<p>While the cards comprise the starting point to an inventory of sorts (we provide blanks for you to tailor your own), a set of potential levers and motivations for the style of personalization activities you aspire to deliver, they are more valuable when thought of in a grouping. </p>
<p>In assembling a card “hand”, one can begin to trace the entire trajectory from leadership focus down through a strategic and tactical execution. It is also at the heart of the way both co-authors have conducted workshops in assembling a program backlog—which is a fine subject for another article.</p>
<p>In the meantime, what is important to note is that each colored class of card is helpful to survey in understanding the range of choices potentially at your disposal, it is threading through and making concrete decisions about for whom this decisioning will be made: where, when, and how.</p>
<figure id="figure34" class="wp-block-image size-full"><img loading="lazy" width="960" height="578" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image26.jpg?resize=960%2C578&ssl=1" alt="Cards on a table. At the top: Function is the north star & customer satisfaction is the goal. User segment is unknown, the actionable data is a quiz, context is a nudge, campaign is to make something easier, and the touchpoint is a banner." class="wp-image-7173693" data-recalc-dims="1" referrerpolicy="no-referrer"><figcaption class="wp-element-caption"><strong>Scenario A:</strong> We want to use personalization to improve customer satisfaction on the website. For unknown users, we will create a short quiz to better identify what the user has come to do. This is sometimes referred to as “badging” a user in onboarding contexts, to better characterize their present intent and context.</figcaption></figure>
<h2 id="section11" class="wp-block-heading">Lay Down Your Cards<a class="subhead-anchor" href="https://alistapart.com/article/personalization-pyramid/#section11">#section11</a></h2>
<p>Any sustainable personalization strategy must consider near, mid and long-term goals. Even with the leading CMS platforms like Sitecore and Adobe or the most exciting composable CMS DXP out there, there is simply no “easy button” wherein a personalization program can be stood up and immediately view meaningful results. That said, there is a common grammar to all personalization activities, just like every sentence has nouns and verbs. These cards attempt to map that territory.</p>
<p></p>
In this excerpt from In Fulfillment: The Designer’s Journey, Justin Dauer ruminates on the past and the importance of keeping an open mind.
Is mobile-first CSS always the best option? Patrick Clancey explores the pros and cons and lays out an alternative.
Learn how to engage stakeholders, focus on impactful objectives, and measure the results in this template for ethical design.
What can we do with thirty pixels? Windows Controls Overlay frees us from 40 years of history telling us how apps should look.
Seriously, do not ever design screens again without first answering these questions: what are the objects and how do they relate?]]></description>
<pubDate>Thu, 08 Dec 2022 15:00:00 GMT</pubDate>
<guid isPermaLink="false">https://alistapart.com/article/personalization-pyramid/</guid>
<link>https://alistapart.com/article/personalization-pyramid/</link>
<author><![CDATA[Colin Eagan, Jeffrey MacIntyre]]></author>
<category>Content</category>
<category>Interaction Design</category>
</item>
<item>
<title><![CDATA[Mobile-First CSS: Is It Time for a Rethink?]]></title>
<description><![CDATA[<p class="intro">The mobile-first design methodology is great—it focuses on what really matters to the user, it’s well-practiced, and it’s been a common design pattern for years. So developing your CSS mobile-first should also be great, too…right? </p>
<p>Well, not necessarily. Classic mobile-first CSS development is based on the principle of overwriting style declarations: you begin your CSS with default style declarations, and overwrite and/or add new styles as you add breakpoints with <code>min-width</code> media queries for larger viewports (for a good overview see “<a href="https://www.mightyminnow.com/2013/11/what-is-mobile-first-css-and-why-does-it-rock/">What is Mobile First CSS and Why Does It Rock?</a>”). But all those exceptions create complexity and inefficiency, which in turn can lead to an increased testing effort and a code base that’s harder to maintain. Admit it—how many of us willingly want that?</p>
<p>On your own projects, mobile-first CSS may yet be the best tool for the job, but first you need to evaluate just how appropriate it is in light of the visual design and user interactions you’re working on. To help you get started, here’s how I go about tackling the factors you need to watch for, and I’ll discuss some alternate solutions if mobile-first doesn’t seem to suit your project.</p>
<h2 id="section2" class="wp-block-heading">Advantages of mobile-first<a class="subhead-anchor" href="https://alistapart.com/article/mobile-first-css-is-it-time-for-a-rethink/#section2">#section2</a></h2>
<p>Some of the things to like with mobile-first CSS development—and why it’s been the de facto development methodology for so long—make a lot of sense:</p>
<p><strong>Development hierarchy. </strong>One thing you undoubtedly get from mobile-first is a nice development hierarchy—you just focus on the mobile view and get developing. </p>
<p><strong>Tried and tested. </strong>It’s a tried and tested methodology that’s worked for years for a reason: it solves a problem really well.</p>
<p><strong>Prioritizes the mobile view</strong>. The mobile view is the<strong> </strong>simplest<strong> </strong>and arguably the most important, as it <strong>encompasses all the key user journeys</strong>, and often accounts for a <strong>higher proportion of user visits</strong> (depending on the project). </p>
<p><strong>Prevents desktop-centric development. </strong>As development is done using desktop computers, it can be tempting to initially focus on the desktop view. But thinking about mobile from the start prevents us from getting stuck later on; no one wants to spend their time retrofitting a desktop-centric site to work on mobile devices!</p>
<h2 id="section3" class="wp-block-heading">Disadvantages of mobile-first<a class="subhead-anchor" href="https://alistapart.com/article/mobile-first-css-is-it-time-for-a-rethink/#section3">#section3</a></h2>
<p>Setting style declarations and then overwriting them at higher breakpoints can lead to undesirable ramifications:</p>
<p><strong>More complexity. </strong>The farther up the breakpoint hierarchy you go, the more unnecessary code you inherit from lower breakpoints. </p>
<p><strong>Higher CSS specificity. </strong>Styles that have been reverted to their browser default value in a class name declaration now have a higher specificity. This can be a headache on large projects when you want to keep the CSS selectors as simple as possible.</p>
<p><strong>Requires more regression testing. </strong>Changes to the CSS at a lower view (like adding a new style) requires all higher breakpoints to be regression tested.</p>
<p><strong>The browser can’t prioritize CSS downloads. </strong>At wider breakpoints, classic mobile-first <code>min-width</code> media queries don’t leverage the browser’s capability to download CSS files in priority order.</p>
<h2 id="section4" class="wp-block-heading">The problem of property value overrides<a class="subhead-anchor" href="https://alistapart.com/article/mobile-first-css-is-it-time-for-a-rethink/#section4">#section4</a></h2>
<p>There is nothing inherently wrong with overwriting values; CSS was designed to do just that. Still, inheriting incorrect values is unhelpful and can be burdensome and inefficient. It can also lead to increased style specificity when you have to overwrite styles to reset them back to their defaults, something that may cause issues later on, especially if you are using a combination of bespoke CSS and utility classes. We won’t be able to use a utility class for a style that has been reset with a higher specificity.</p>
<p>With this in mind, I’m developing CSS with a focus on the default values much more these days. Since there’s no specific order, and no chains of specific values to keep track of, this frees me to develop breakpoints <em>simultaneously</em>. I concentrate on finding common styles and isolating the specific exceptions in closed media query ranges (that is, any range with a <code>max-width</code> set). </p>
<p>This approach opens up some opportunities, as you can look at each breakpoint as a clean slate. If a component’s layout looks like it should be based on Flexbox at all breakpoints, it’s fine and can be coded in the default style sheet. But if it looks like Grid would be much better for large screens and Flexbox for mobile, these can both be done entirely independently when the CSS is put into closed media query ranges. Also, developing simultaneously requires you to have a good understanding of any given component in all breakpoints up front. This can help surface issues in the design earlier in the development process. We don’t want to get stuck down a rabbit hole building a complex component for mobile, and then get the designs for desktop and find they are equally complex and incompatible with the HTML we created for the mobile view! </p>
<p>Though this approach isn’t going to suit everyone, I encourage you to give it a try. There are plenty of tools out there to help with concurrent development, such as <a href="https://responsively.app/">Responsively App</a>, <a href="https://blisk.io/">Blisk</a>, and many others. </p>
<p>Having said that, I don’t feel the order itself is particularly relevant. If you are comfortable with focusing on the mobile view, have a good understanding of the requirements for other breakpoints, and prefer to work on one device at a time, then by all means stick with the classic development order. The important thing is to identify common styles and exceptions so you can put them in the relevant stylesheet—a sort of manual tree-shaking process! Personally, I find this a little easier when working on a component across breakpoints, but that’s by no means a requirement.</p>
<h2 id="section5" class="wp-block-heading">Closed media query ranges in practice <a class="subhead-anchor" href="https://alistapart.com/article/mobile-first-css-is-it-time-for-a-rethink/#section5">#section5</a></h2>
<p>In classic mobile-first CSS we overwrite the styles, but we can avoid this by using media query ranges. To illustrate the difference (I’m using SCSS for brevity), let’s assume there are three visual designs: </p>
<ul><li>smaller than 768</li><li>from 768 to below 1024</li><li>1024 and anything larger </li></ul>
<p>Take a simple example where a block-level element has a default <code>padding</code> of “20px,” which is overwritten at tablet to be “40px” and set back to “20px” on desktop.</p>
<figure id="figure1" class="wp-block-table">
<table><tbody>
<tr>
<td valign="top"><p>Classic <code>min-width</code> mobile-first</p>
<pre id="snippet1"><code class="language-css">.my-block {
padding: 20px;
@media (min-width: 768px) {
padding: 40px;
}
@media (min-width: 1024px) {
padding: 20px;
}
}</code></pre></td>
<td valign="top"><p>Closed media query range</p>
<pre id="snippet2"><code class="language-css">.my-block {
padding: 20px;
@media (min-width: 768px) and (max-width: 1023.98px) {
padding: 40px;
}
}</code></pre></td>
</tr>
</tbody></table>
</figure>
<p>The subtle difference is that the mobile-first example sets the default <code>padding</code> to “20px” and then overwrites it at each breakpoint, setting it three times in total. In contrast, the second example sets the default <code>padding</code> to “20px” and only overrides it at the relevant breakpoint where it isn’t the default value (in this instance, tablet is the exception).</p>
<p>The goal is to: </p>
<ul><li>Only set styles when needed. </li><li>Not set them with the <em>expectation</em> of overwriting them later on, again and again. </li></ul>
<p>To this end, closed media query ranges are our best friend. If we need to make a change to any given view, we make it in the CSS media query range that applies to the specific breakpoint. We’ll be much less likely to introduce unwanted alterations, and our regression testing only needs to focus on the breakpoint we have actually edited. </p>
<p>Taking the above example, if we find that <code>.my-block</code> spacing on desktop is already accounted for by the margin at that breakpoint, and since we want to remove the padding altogether, we could do this by setting the mobile <code>padding</code> in a closed media query range.<br></p>
<figure id="figure2" class="wp-block-table">
<pre id="snippet3"><code class="language-css">.my-block {
@media (max-width: 767.98px) {
padding: 20px;
}
@media (min-width: 768px) and (max-width: 1023.98px) {
padding: 40px;
}
}</code></pre>
</figure>
<p>The browser default <code>padding</code> for our block is “0,” so instead of adding a desktop media query and using <code>unset</code> or “0” for the <code>padding</code> value (which we would need with mobile-first), we can wrap the mobile <code>padding</code> in a closed media query (since it is now also an exception) so it won’t get picked up at wider breakpoints. At the desktop breakpoint, we won’t need to set any <code>padding</code> style, as we want the browser default value.</p>
<h2 id="section6" class="wp-block-heading">Bundling versus separating the CSS<a class="subhead-anchor" href="https://alistapart.com/article/mobile-first-css-is-it-time-for-a-rethink/#section6">#section6</a></h2>
<p>Back in the day, keeping the number of requests to a minimum was very important due to the browser’s limit of concurrent requests (typically around six). As a consequence, the use of image sprites and CSS bundling was the norm, with all the CSS being downloaded in one go, as one stylesheet with highest priority. </p>
<p>With HTTP/2 and HTTP/3 now on the scene, the number of requests is no longer the big deal it used to be. This allows us to separate the CSS into multiple files by media query. The clear benefit of this is the browser can now request the CSS it currently needs with a higher priority than the CSS it doesn’t. This is more performant and can reduce the overall time <a href="https://web.dev/critical-rendering-path-render-blocking-css/">page rendering is blocked</a>.</p>
<h3 id="section7" class="wp-block-heading">Which HTTP version are you using?<a class="subhead-anchor" href="https://alistapart.com/article/mobile-first-css-is-it-time-for-a-rethink/#section7">#section7</a></h3>
<p>To determine which version of HTTP you’re using, go to your website and open your browser’s dev tools. Next, select the <strong>Network</strong> tab and make sure the <strong>Protocol</strong> column is visible. If “h2” is listed under <strong>Protocol</strong>, it means HTTP/2 is being used. </p>
<p><em>Note: to view the Protocol in your browser’s dev tools, go to the </em><strong><em>Network</em></strong><em> tab, reload your page, right-click any column header (e.g., </em><strong><em>Name</em></strong><em>), and check the </em><strong><em>Protocol</em></strong><em> column.</em></p>
<figure id="figure3" class="wp-block-image"><img src="https://lh4.googleusercontent.com/O8lxNeIY3Hb0YDs2EP7QFhGdGsBXOG7mSTCdAJBd5xkm-6RwrpkS1BN63W7RurVCP3nOH9sNpAR9JNGvIGnUTzG0NYm4sUqI5bU2QPhXYEawmKfeUJ_6YwWAIid2ZDHEdRzaQ1LxzUNTGbGk5g" alt="Chrome dev tools, Network tab filtered by document, Protocol column" referrerpolicy="no-referrer"><figcaption><em>Note: for a summarized comparison, see ImageKit’s “</em><a href="https://imagekit.io/blog/http2-vs-http1-performance/"><em>HTTP/2 vs. HTTP/1</em></a><em>.”</em></figcaption></figure>
<p>Also, if your site is still using HTTP/1...WHY?!! What are you waiting for? There is <a href="https://caniuse.com/http2">excellent user support for HTTP/2</a>.</p>
<h2 id="section8" class="wp-block-heading">Splitting the CSS<a class="subhead-anchor" href="https://alistapart.com/article/mobile-first-css-is-it-time-for-a-rethink/#section8">#section8</a></h2>
<p>Separating the CSS into individual files is a worthwhile task. Linking the separate CSS files using the relevant <code>media</code> attribute allows the browser to identify which files are needed immediately (because they’re render-blocking) and which can be deferred. Based on this, it allocates each file an appropriate priority.</p>
<p>In the following example of a website visited on a mobile breakpoint, we can see the mobile and default CSS are loaded with “Highest” priority, as they are currently needed to render the page. The remaining CSS files (print, tablet, and desktop) are still downloaded in case they’ll be needed later, but with “Lowest” priority. </p>
<figure id="figure4" class="wp-block-image"><img src="https://lh5.googleusercontent.com/RZOq-S7kbVsavDiFctQl6STFgGm6puwG8L22V6j6U1vUfo73Opq3Cspj2N94T2BU5lpYUD7Bb_4krFCXlePvBE8xXJVMFwbc_At8pzc-C5ug-6lrPViwMIIXgbKiJA-2fQ3beDoYfkCflCVgwg" alt="Chrome dev tools, Network tab filtered by css, Priority column" referrerpolicy="no-referrer"></figure>
<p>With<strong> bundled CSS</strong>, the browser will have to download the CSS file and parse it before rendering can start.<br><br>While, as noted, with the <strong>CSS separated into different files</strong> linked and marked up with the relevant <code>media</code> attribute, the browser can prioritize the files it currently needs. Using closed media query ranges allows the browser to do this at all widths, as opposed to classic mobile-first <code>min-width</code> queries, where the desktop browser would have to download all the CSS with Highest priority. We can’t assume that desktop users always have a fast connection. For instance, in many rural areas, internet connection speeds are still slow. </p>
<p>The media queries and number of separate CSS files will vary from project to project based on project requirements, but might look similar to the example below.</p>
<figure id="figure5" class="wp-block-table">
<table><tbody>
<tr>
<td valign="top">
<p>Bundled CSS</p>
<code><link href="site.css" rel="stylesheet"></code><br><br>
<p>This single file contains all the CSS ... |
http://localhost:1200/alistapart/code - Success ✔️<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"
>
<channel>
<title><![CDATA[A List Apart]]></title>
<link>https://alistapart.com/blog/topic/code</link>
<atom:link href="http://localhost:1200/alistapart/code" rel="self" type="application/rss+xml" />
<description><![CDATA[Articles on aListApart.com - Made with love by RSSHub(https://github.com/DIYgod/RSSHub)]]></description>
<generator>RSSHub</generator>
<webMaster>i@diygod.me (DIYgod)</webMaster>
<language>en-us</language>
<lastBuildDate>Wed, 09 Aug 2023 12:29:15 GMT</lastBuildDate>
<ttl>5</ttl>
<item>
<title><![CDATA[Mobile-First CSS: Is It Time for a Rethink?]]></title>
<description><![CDATA[<p class="intro">The mobile-first design methodology is great—it focuses on what really matters to the user, it’s well-practiced, and it’s been a common design pattern for years. So developing your CSS mobile-first should also be great, too…right? </p>
<p>Well, not necessarily. Classic mobile-first CSS development is based on the principle of overwriting style declarations: you begin your CSS with default style declarations, and overwrite and/or add new styles as you add breakpoints with <code>min-width</code> media queries for larger viewports (for a good overview see “<a href="https://www.mightyminnow.com/2013/11/what-is-mobile-first-css-and-why-does-it-rock/">What is Mobile First CSS and Why Does It Rock?</a>”). But all those exceptions create complexity and inefficiency, which in turn can lead to an increased testing effort and a code base that’s harder to maintain. Admit it—how many of us willingly want that?</p>
<p>On your own projects, mobile-first CSS may yet be the best tool for the job, but first you need to evaluate just how appropriate it is in light of the visual design and user interactions you’re working on. To help you get started, here’s how I go about tackling the factors you need to watch for, and I’ll discuss some alternate solutions if mobile-first doesn’t seem to suit your project.</p>
<h2 id="section2" class="wp-block-heading">Advantages of mobile-first<a class="subhead-anchor" href="https://alistapart.com/article/mobile-first-css-is-it-time-for-a-rethink/#section2">#section2</a></h2>
<p>Some of the things to like with mobile-first CSS development—and why it’s been the de facto development methodology for so long—make a lot of sense:</p>
<p><strong>Development hierarchy. </strong>One thing you undoubtedly get from mobile-first is a nice development hierarchy—you just focus on the mobile view and get developing. </p>
<p><strong>Tried and tested. </strong>It’s a tried and tested methodology that’s worked for years for a reason: it solves a problem really well.</p>
<p><strong>Prioritizes the mobile view</strong>. The mobile view is the<strong> </strong>simplest<strong> </strong>and arguably the most important, as it <strong>encompasses all the key user journeys</strong>, and often accounts for a <strong>higher proportion of user visits</strong> (depending on the project). </p>
<p><strong>Prevents desktop-centric development. </strong>As development is done using desktop computers, it can be tempting to initially focus on the desktop view. But thinking about mobile from the start prevents us from getting stuck later on; no one wants to spend their time retrofitting a desktop-centric site to work on mobile devices!</p>
<h2 id="section3" class="wp-block-heading">Disadvantages of mobile-first<a class="subhead-anchor" href="https://alistapart.com/article/mobile-first-css-is-it-time-for-a-rethink/#section3">#section3</a></h2>
<p>Setting style declarations and then overwriting them at higher breakpoints can lead to undesirable ramifications:</p>
<p><strong>More complexity. </strong>The farther up the breakpoint hierarchy you go, the more unnecessary code you inherit from lower breakpoints. </p>
<p><strong>Higher CSS specificity. </strong>Styles that have been reverted to their browser default value in a class name declaration now have a higher specificity. This can be a headache on large projects when you want to keep the CSS selectors as simple as possible.</p>
<p><strong>Requires more regression testing. </strong>Changes to the CSS at a lower view (like adding a new style) requires all higher breakpoints to be regression tested.</p>
<p><strong>The browser can’t prioritize CSS downloads. </strong>At wider breakpoints, classic mobile-first <code>min-width</code> media queries don’t leverage the browser’s capability to download CSS files in priority order.</p>
<h2 id="section4" class="wp-block-heading">The problem of property value overrides<a class="subhead-anchor" href="https://alistapart.com/article/mobile-first-css-is-it-time-for-a-rethink/#section4">#section4</a></h2>
<p>There is nothing inherently wrong with overwriting values; CSS was designed to do just that. Still, inheriting incorrect values is unhelpful and can be burdensome and inefficient. It can also lead to increased style specificity when you have to overwrite styles to reset them back to their defaults, something that may cause issues later on, especially if you are using a combination of bespoke CSS and utility classes. We won’t be able to use a utility class for a style that has been reset with a higher specificity.</p>
<p>With this in mind, I’m developing CSS with a focus on the default values much more these days. Since there’s no specific order, and no chains of specific values to keep track of, this frees me to develop breakpoints <em>simultaneously</em>. I concentrate on finding common styles and isolating the specific exceptions in closed media query ranges (that is, any range with a <code>max-width</code> set). </p>
<p>This approach opens up some opportunities, as you can look at each breakpoint as a clean slate. If a component’s layout looks like it should be based on Flexbox at all breakpoints, it’s fine and can be coded in the default style sheet. But if it looks like Grid would be much better for large screens and Flexbox for mobile, these can both be done entirely independently when the CSS is put into closed media query ranges. Also, developing simultaneously requires you to have a good understanding of any given component in all breakpoints up front. This can help surface issues in the design earlier in the development process. We don’t want to get stuck down a rabbit hole building a complex component for mobile, and then get the designs for desktop and find they are equally complex and incompatible with the HTML we created for the mobile view! </p>
<p>Though this approach isn’t going to suit everyone, I encourage you to give it a try. There are plenty of tools out there to help with concurrent development, such as <a href="https://responsively.app/">Responsively App</a>, <a href="https://blisk.io/">Blisk</a>, and many others. </p>
<p>Having said that, I don’t feel the order itself is particularly relevant. If you are comfortable with focusing on the mobile view, have a good understanding of the requirements for other breakpoints, and prefer to work on one device at a time, then by all means stick with the classic development order. The important thing is to identify common styles and exceptions so you can put them in the relevant stylesheet—a sort of manual tree-shaking process! Personally, I find this a little easier when working on a component across breakpoints, but that’s by no means a requirement.</p>
<h2 id="section5" class="wp-block-heading">Closed media query ranges in practice <a class="subhead-anchor" href="https://alistapart.com/article/mobile-first-css-is-it-time-for-a-rethink/#section5">#section5</a></h2>
<p>In classic mobile-first CSS we overwrite the styles, but we can avoid this by using media query ranges. To illustrate the difference (I’m using SCSS for brevity), let’s assume there are three visual designs: </p>
<ul><li>smaller than 768</li><li>from 768 to below 1024</li><li>1024 and anything larger </li></ul>
<p>Take a simple example where a block-level element has a default <code>padding</code> of “20px,” which is overwritten at tablet to be “40px” and set back to “20px” on desktop.</p>
<figure id="figure1" class="wp-block-table">
<table><tbody>
<tr>
<td valign="top"><p>Classic <code>min-width</code> mobile-first</p>
<pre id="snippet1"><code class="language-css">.my-block {
padding: 20px;
@media (min-width: 768px) {
padding: 40px;
}
@media (min-width: 1024px) {
padding: 20px;
}
}</code></pre></td>
<td valign="top"><p>Closed media query range</p>
<pre id="snippet2"><code class="language-css">.my-block {
padding: 20px;
@media (min-width: 768px) and (max-width: 1023.98px) {
padding: 40px;
}
}</code></pre></td>
</tr>
</tbody></table>
</figure>
<p>The subtle difference is that the mobile-first example sets the default <code>padding</code> to “20px” and then overwrites it at each breakpoint, setting it three times in total. In contrast, the second example sets the default <code>padding</code> to “20px” and only overrides it at the relevant breakpoint where it isn’t the default value (in this instance, tablet is the exception).</p>
<p>The goal is to: </p>
<ul><li>Only set styles when needed. </li><li>Not set them with the <em>expectation</em> of overwriting them later on, again and again. </li></ul>
<p>To this end, closed media query ranges are our best friend. If we need to make a change to any given view, we make it in the CSS media query range that applies to the specific breakpoint. We’ll be much less likely to introduce unwanted alterations, and our regression testing only needs to focus on the breakpoint we have actually edited. </p>
<p>Taking the above example, if we find that <code>.my-block</code> spacing on desktop is already accounted for by the margin at that breakpoint, and since we want to remove the padding altogether, we could do this by setting the mobile <code>padding</code> in a closed media query range.<br></p>
<figure id="figure2" class="wp-block-table">
<pre id="snippet3"><code class="language-css">.my-block {
@media (max-width: 767.98px) {
padding: 20px;
}
@media (min-width: 768px) and (max-width: 1023.98px) {
padding: 40px;
}
}</code></pre>
</figure>
<p>The browser default <code>padding</code> for our block is “0,” so instead of adding a desktop media query and using <code>unset</code> or “0” for the <code>padding</code> value (which we would need with mobile-first), we can wrap the mobile <code>padding</code> in a closed media query (since it is now also an exception) so it won’t get picked up at wider breakpoints. At the desktop breakpoint, we won’t need to set any <code>padding</code> style, as we want the browser default value.</p>
<h2 id="section6" class="wp-block-heading">Bundling versus separating the CSS<a class="subhead-anchor" href="https://alistapart.com/article/mobile-first-css-is-it-time-for-a-rethink/#section6">#section6</a></h2>
<p>Back in the day, keeping the number of requests to a minimum was very important due to the browser’s limit of concurrent requests (typically around six). As a consequence, the use of image sprites and CSS bundling was the norm, with all the CSS being downloaded in one go, as one stylesheet with highest priority. </p>
<p>With HTTP/2 and HTTP/3 now on the scene, the number of requests is no longer the big deal it used to be. This allows us to separate the CSS into multiple files by media query. The clear benefit of this is the browser can now request the CSS it currently needs with a higher priority than the CSS it doesn’t. This is more performant and can reduce the overall time <a href="https://web.dev/critical-rendering-path-render-blocking-css/">page rendering is blocked</a>.</p>
<h3 id="section7" class="wp-block-heading">Which HTTP version are you using?<a class="subhead-anchor" href="https://alistapart.com/article/mobile-first-css-is-it-time-for-a-rethink/#section7">#section7</a></h3>
<p>To determine which version of HTTP you’re using, go to your website and open your browser’s dev tools. Next, select the <strong>Network</strong> tab and make sure the <strong>Protocol</strong> column is visible. If “h2” is listed under <strong>Protocol</strong>, it means HTTP/2 is being used. </p>
<p><em>Note: to view the Protocol in your browser’s dev tools, go to the </em><strong><em>Network</em></strong><em> tab, reload your page, right-click any column header (e.g., </em><strong><em>Name</em></strong><em>), and check the </em><strong><em>Protocol</em></strong><em> column.</em></p>
<figure id="figure3" class="wp-block-image"><img src="https://lh4.googleusercontent.com/O8lxNeIY3Hb0YDs2EP7QFhGdGsBXOG7mSTCdAJBd5xkm-6RwrpkS1BN63W7RurVCP3nOH9sNpAR9JNGvIGnUTzG0NYm4sUqI5bU2QPhXYEawmKfeUJ_6YwWAIid2ZDHEdRzaQ1LxzUNTGbGk5g" alt="Chrome dev tools, Network tab filtered by document, Protocol column" referrerpolicy="no-referrer"><figcaption><em>Note: for a summarized comparison, see ImageKit’s “</em><a href="https://imagekit.io/blog/http2-vs-http1-performance/"><em>HTTP/2 vs. HTTP/1</em></a><em>.”</em></figcaption></figure>
<p>Also, if your site is still using HTTP/1...WHY?!! What are you waiting for? There is <a href="https://caniuse.com/http2">excellent user support for HTTP/2</a>.</p>
<h2 id="section8" class="wp-block-heading">Splitting the CSS<a class="subhead-anchor" href="https://alistapart.com/article/mobile-first-css-is-it-time-for-a-rethink/#section8">#section8</a></h2>
<p>Separating the CSS into individual files is a worthwhile task. Linking the separate CSS files using the relevant <code>media</code> attribute allows the browser to identify which files are needed immediately (because they’re render-blocking) and which can be deferred. Based on this, it allocates each file an appropriate priority.</p>
<p>In the following example of a website visited on a mobile breakpoint, we can see the mobile and default CSS are loaded with “Highest” priority, as they are currently needed to render the page. The remaining CSS files (print, tablet, and desktop) are still downloaded in case they’ll be needed later, but with “Lowest” priority. </p>
<figure id="figure4" class="wp-block-image"><img src="https://lh5.googleusercontent.com/RZOq-S7kbVsavDiFctQl6STFgGm6puwG8L22V6j6U1vUfo73Opq3Cspj2N94T2BU5lpYUD7Bb_4krFCXlePvBE8xXJVMFwbc_At8pzc-C5ug-6lrPViwMIIXgbKiJA-2fQ3beDoYfkCflCVgwg" alt="Chrome dev tools, Network tab filtered by css, Priority column" referrerpolicy="no-referrer"></figure>
<p>With<strong> bundled CSS</strong>, the browser will have to download the CSS file and parse it before rendering can start.<br><br>While, as noted, with the <strong>CSS separated into different files</strong> linked and marked up with the relevant <code>media</code> attribute, the browser can prioritize the files it currently needs. Using closed media query ranges allows the browser to do this at all widths, as opposed to classic mobile-first <code>min-width</code> queries, where the desktop browser would have to download all the CSS with Highest priority. We can’t assume that desktop users always have a fast connection. For instance, in many rural areas, internet connection speeds are still slow. </p>
<p>The media queries and number of separate CSS files will vary from project to project based on project requirements, but might look similar to the example below.</p>
<figure id="figure5" class="wp-block-table">
<table><tbody>
<tr>
<td valign="top">
<p>Bundled CSS</p>
<code><link href="site.css" rel="stylesheet"></code><br><br>
<p>This single file contains all the CSS, including all media queries, and it will be downloaded with Highest priority.</p>
</td>
<td valign="top">
<p>Separated CSS</p>
<code><link href="default.css" rel="stylesheet"><link href="mobile.css" media="screen and (max-width: 767.98px)" rel="stylesheet"><link href="tablet.css" media="screen and (min-width: 768px) and (max-width: 1083.98px)" rel="stylesheet"><link href="desktop.css" media="screen and (min-width: 1084px)" rel="stylesheet"><link href="print.css" media="print" rel="stylesheet"></code><br><br>
<p>Separating the CSS and specifying a <code>media</code> attribute value on each <code>link</code> tag allows the browser to prioritize what it currently needs. Out of the five files listed above, two will be downloaded with Highest priority: the default file, and the file that matches the current media query. The others will be downloaded with Lowest priority.</p>
</td>
</tr>
</tbody></table>
</figure>
<p>Depending on the project’s deployment strategy, a change to one file (<code>mobile.css</code>, for example) would only require the QA team to regression test on devices in that specific media query range. Compare that to the prospect of deploying the single bundled <code>site.css</code> file, an approach that would normally trigger a full regression test.</p>
<h2 id="section9" class="wp-block-heading">Moving on<a class="subhead-anchor" href="https://alistapart.com/article/mobile-first-css-is-it-time-for-a-rethink/#section9">#section9</a></h2>
<p>The uptake of mobile-first CSS was a really important milestone in web development; it has helped front-end developers focus on mobile web applications, rather than developing sites on desktop and then attempting to retrofit them to work on other devices.</p>
<p>I don’t think anyone wants to return to that development model again, but it’s important we don’t lose sight of the issue it highlighted: that things can easily get convoluted and less efficient if we prioritize one particular device—any device—over others. For this reason, focusing on the CSS in its own right, always mindful of what is the default setting and what’s an exception, seems like the natural next step. I’ve started noticing small simplifications in my own CSS, as well as other developers’, and that testing and maintenance work is also a bit more simplified and productive. </p>
<p>In general, simplifying CSS rule creation whenever we can is ultimately a cleaner approach than going around in circles of overrides. But whichever methodology you choose, it needs to suit the project. Mobile-first may—or may not—turn out to be the best choice for what’s involved, but first you need to solidly understand the trade-offs you’re stepping into.</p>
In this excerpt from In Fulfillment: The Designer’s Journey, Justin Dauer ruminates on the past and the importance of keeping an open mind.
Colin Eagan and Jeffrey MacIntyre offer a “ground-up” approach to implementing personalized digital experiences that are intentional, ethical, and technologically sound.
Learn how to engage stakeholders, focus on impactful objectives, and measure the results in this template for ethical design.
What can we do with thirty pixels? Windows Controls Overlay frees us from 40 years of history telling us how apps should look.
Seriously, do not ever design screens again without first answering these questions: what are the objects and how do they relate?]]></description>
<pubDate>Thu, 09 Jun 2022 02:13:10 GMT</pubDate>
<guid isPermaLink="false">https://alistapart.com/article/mobile-first-css-is-it-time-for-a-rethink/</guid>
<link>https://alistapart.com/article/mobile-first-css-is-it-time-for-a-rethink/</link>
<author><![CDATA[Patrick Clancey]]></author>
<category>Code</category>
<category>CSS</category>
</item>
<item>
<title><![CDATA[Breaking Out of the Box]]></title>
<description><![CDATA[<p>CSS is about styling boxes. In fact, the whole web is made of boxes, from the browser viewport to elements on a page. But every once in a while a new feature comes along that makes us rethink our design approach.</p>
<p><a href="https://www.w3.org/TR/css-round-display-1/">Round displays</a>, for example, make it fun to play with circular clip areas. <a href="https://css-tricks.com/the-notch-and-css/">Mobile screen notches</a> and <a href="https://www.w3.org/TR/virtual-keyboard/">virtual keyboards</a> offer challenges to best organize content that stays clear of them. And <a href="https://blogs.windows.com/msedgedev/2020/09/14/introducing-dual-screen-foldable-web-apis/">dual screen or foldable devices</a> make us rethink how to best use available space in a number of different <a href="https://w3c.github.io/device-posture/">device postures</a>.</p>
<figure id="figure1" class="wp-block-image size-large"><img width="960" height="452" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig1.png?resize=960%2C452&ssl=1" alt="" class="wp-image-7173226" data-recalc-dims="1" referrerpolicy="no-referrer"><figcaption><em>Sketches of a round display, a common rectangular mobile display, and a device with a foldable display.</em></figcaption></figure>
<p>These recent evolutions of the web platform made it both more challenging and more interesting to design products. They’re great opportunities for us to break out of our rectangular boxes.</p>
<p>I’d like to talk about a new feature similar to the above: the Window Controls Overlay for Progressive Web Apps (PWAs).</p>
<p><a href="https://alistapart.com/article/yes-that-web-project-should-be-a-pwa/">Progressive Web Apps</a> are blurring the lines between apps and websites. They combine the best of both worlds. On one hand, they’re stable, linkable, searchable, and responsive just like websites. On the other hand, they provide additional powerful capabilities, work offline, and read files just like native apps.</p>
<p>As a design surface, PWAs are really interesting because they challenge us to think about what mixing web and device-native user interfaces can be. On desktop devices in particular, we have more than <a href="https://en.wikipedia.org/wiki/History_of_the_graphical_user_interface">40 years of history</a> telling us what applications should look like, and it can be hard to break out of this mental model.</p>
<p>At the end of the day though, PWAs on desktop are constrained to the window they appear in: a rectangle with a title bar at the top.</p>
<p>Here’s what a typical desktop PWA app looks like:</p>
<figure id="figure2" class="wp-block-image size-large"><img width="960" height="303" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig2.png?resize=960%2C303&ssl=1" alt="" class="wp-image-7173227" data-recalc-dims="1" referrerpolicy="no-referrer"><figcaption><em>Sketches of two rectangular user interfaces representing the desktop Progressive Web App status quo on the macOS and Windows operating systems, respectively. </em></figcaption></figure>
<p>Sure, as the author of a PWA, you get to choose the color of the title bar (using the Web Application Manifest <a href="https://developer.mozilla.org/en-US/docs/Web/Manifest/theme_color"><strong>theme_color</strong></a> property), but that’s about it.</p>
<p>What if we could think outside this box, and reclaim the real estate of the app’s entire window? Doing so would give us a chance to make our apps more beautiful and feel more integrated in the operating system.</p>
<p>This is exactly what the <a href="https://web.dev/window-controls-overlay/">Window Controls Overlay</a> offers. This new PWA functionality makes it possible to take advantage of the full surface area of the app, including where the title bar normally appears.</p>
<h2 id="section2" class="wp-block-heading">About the title bar and window controls<a class="subhead-anchor" href="https://alistapart.com/article/breaking-out-of-the-box/#section2">#section2</a></h2>
<p>Let’s start with an explanation of what the title bar and window controls are.</p>
<p>The <em>title bar</em> is the area displayed at the top of an app window, which usually contains the app’s name. <em>Window controls</em> are the affordances, or buttons, that make it possible to minimize, maximize, or close the app’s window, and are also displayed at the top.</p>
<figure id="figure3" class="wp-block-image"><img src="https://lh5.googleusercontent.com/1s_lFmqiRm6Fv3wzeELtsDDiDvOkEJuSRH5K9YIrZZ8rh8rYCxUqbSnfd-f7YrsRvcDzF67fexnEJFlDtw53SKKmOgVk8sv_VUyCQveoR18HkNgACPxcQTtEOb6SmEuRIDlX3EcI" alt="" referrerpolicy="no-referrer"><figcaption><em>A sketch of a rectangular application user interface highlighting the title bar area and window control buttons.</em></figcaption></figure>
<p>Window Controls Overlay removes the physical constraint of the title bar and window controls areas. It frees up the full height of the app window, enabling the title bar and window control buttons to be overlaid on top of the application’s web content. </p>
<figure id="figure4" class="wp-block-image"><img src="https://lh6.googleusercontent.com/k_6o1fBePhbjvtmxoTW3tG1134Gvbo31r2fy7zxmOB39d_eKpjThbh7QL8pVXrA1aLvEWzkoJ_rY4af451BU9XyKZXbSouCTvDJMnRKGlcOhcEpXw_rjQAR8_SFjhrm_-22OxKiR" alt="" referrerpolicy="no-referrer"><figcaption><em>A sketch of a rectangular application user interface using Window Controls Overlay. The title bar and window controls are no longer in an area separated from the app’s content.</em></figcaption></figure>
<p>If you are reading this article on a desktop computer, take a quick look at other apps. Chances are they’re already doing something similar to this. In fact, the very web browser you are using to read this uses the top area to display tabs.</p>
<figure id="figure5" class="wp-block-image"><img src="https://lh6.googleusercontent.com/BLL9Rc5othPsw6xYApyyNOZ73j32wi4XkyoZpl4QOv0OL4MnxMe3bl1xLR0O7WSoAvi3KhyeP83hUh4-EezTmGg2axN4RiOVtgiF5ZiapcjUL6gtLqExZOHGCtkOBbthMTgh5Tmr" alt="" referrerpolicy="no-referrer"><figcaption><em>A screenshot of the top area of a browser’s user interface showing a group of tabs that share the same horizontal space as the app window controls.</em></figcaption></figure>
<p>Spotify displays album artwork all the way to the top edge of the application window.</p>
<figure id="figure6" class="wp-block-image"><img src="https://lh3.googleusercontent.com/SzPcq94_7Yu_ARf13Z6dRD0tpdlPM_MdrY-7CA_mv4Yu3fBIL3pJnXirP83cCDVoQxnnIEwDoBwbGzfftHmZ3PZZUfsw_oP-m4QLkB2SqekX8JupR9_xmI0tG1q65IfNFbnXIHUh" alt="" referrerpolicy="no-referrer"><figcaption><em>A screenshot of an album in Spotify’s desktop application. Album artwork spans the entire width of the main content area, all the way to the top and right edges of the window, and the right edge of the main navigation area on the left side. The application and album navigation controls are overlaid directly on top of the album artwork.</em></figcaption></figure>
<p>Microsoft Word uses the available title bar space to display the auto-save and search functionalities, and more.</p>
<figure id="figure7" class="wp-block-image"><img src="https://lh6.googleusercontent.com/VdREVwFFjYHxHF0Gg3l079hxsa8WKPEWiuvuL7cWbGnEDJ2yc3JiOWQK5lUyaeEgzpd1Przji0cNLeooPD7riPKbcMixa6IkXanprdqPJVkQrYSerxSaNmzbJPd1YsA55mlYd9xt" alt="" referrerpolicy="no-referrer"><figcaption><em>A screenshot of Microsoft Word’s toolbar interface. Document file information, search, and other functionality appear at the top of the window, sharing the same horizontal space as the app’s window controls.</em></figcaption></figure>
<p>The whole point of this feature is to allow you to make use of this space with your own content while providing a way to account for the window control buttons. And it enables you to offer this modified experience on a range of platforms while not adversely affecting the experience on browsers or devices that don’t support Window Controls Overlay. After all, PWAs are all about <a href="https://alistapart.com/article/understandingprogressiveenhancement/">progressive enhancement</a>, so this feature is a chance to enhance your app to use this extra space when it’s available.</p>
<h2 id="section3" class="wp-block-heading">Let’s use the feature<a class="subhead-anchor" href="https://alistapart.com/article/breaking-out-of-the-box/#section3">#section3</a></h2>
<p>For the rest of this article, we’ll be working on a demo app to learn more about using the feature.</p>
<p>The demo app is called <a href="https://stupefied-edison-a4ee55.netlify.app/">1DIV</a>. It’s a simple CSS playground where users can create designs using CSS and a single HTML element.</p>
<p>The app has two pages. The first lists the existing CSS designs you’ve created:</p>
<figure id="figure8" class="wp-block-image"><img src="https://lh3.googleusercontent.com/pW2iUTucKfwMJZrAlPGK19vVnEPaHjYT4N-18P-vm9qkhAdGJcRBMexOCu1q9nN9BAfZ7MH6itNP__kY4HPl9uVPucXkbmSX-E9g6AdVAI_uu6TyEsEdH0LUCXdN1f4kqZNgDr30" alt="" referrerpolicy="no-referrer"><figcaption><em>A screenshot of the 1DIV app displaying a thumbnail grid of CSS designs a user created.</em></figcaption></figure>
<p>The second page enables you to create and edit CSS designs:</p>
<figure id="figure9" class="wp-block-image"><img src="https://lh5.googleusercontent.com/faaJ3uHbzXg-TFinvOqR_7gyjVPvlk7fuVWuN4aIH6IUxXNAp4GXtIcuVPpo6bd1IOKO1_EMDt4pUgErUh_X2_2r3WnkQ4PzovPp6Zjg0l98W9NBrHA0xAuTNf0uNVBatRsMJzEm" alt="" referrerpolicy="no-referrer"><figcaption><em>A screenshot of the 1DIV app editor page. The top half of the window displays a rendered CSS design, and a text editor on the bottom half of the window displays the CSS used to create it.</em></figcaption></figure>
<p>Since I’ve added a simple web manifest and service worker, we can install the app as a PWA on desktop. Here is what it looks like on macOS:</p>
<figure id="figure10" class="wp-block-image size-large"><img loading="lazy" width="960" height="466" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig10.png?resize=960%2C466&ssl=1" alt="" class="wp-image-7173228" data-recalc-dims="1" referrerpolicy="no-referrer"><figcaption><em>Screenshots of the 1DIV app thumbnail view and CSS editor view on macOS. This version of the app’s window has a separate control bar at the top for the app name and window control buttons.</em></figcaption></figure>
<p>And on Windows:</p>
<figure id="figure11" class="wp-block-image size-large"><img loading="lazy" width="960" height="501" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig11.png?resize=960%2C501&ssl=1" alt="" class="wp-image-7173229" data-recalc-dims="1" referrerpolicy="no-referrer"><figcaption><em>Screenshots of the 1DIV app thumbnail view and CSS editor view on the Windows operating system. This version of the app’s window also has a separate control bar at the top for the app name and window control buttons.</em></figcaption></figure>
<p>Our app is looking good, but the white title bar in the first page is wasted space. In the second page, it would be really nice if the design area went all the way to the top of the app window.</p>
<p>Let’s use the Window Controls Overlay feature to improve this.</p>
<h2 id="section4" class="wp-block-heading">Enabling Window Controls Overlay<a class="subhead-anchor" href="https://alistapart.com/article/breaking-out-of-the-box/#section4">#section4</a></h2>
<p>The feature is still experimental at the moment. To try it, you need to enable it in one of the supported browsers.</p>
<p>As of now, it has been implemented in Chromium, as a collaboration between Microsoft and Google. We can therefore use it in Chrome or Edge by going to the internal <strong>about://flags</strong> page, and enabling the <strong>Desktop PWA Window Controls Overlay</strong> flag.</p>
<h2 id="section5" class="wp-block-heading">Using Window Controls Overlay<a class="subhead-anchor" href="https://alistapart.com/article/breaking-out-of-the-box/#section5">#section5</a></h2>
<p>To use the feature, we need to add the following <strong>display_override</strong> member to our web app’s manifest file:</p>
<pre id="snippet1"><code class="language-javascript">{
"name": "1DIV",
"description": "1DIV is a mini CSS playground",
"lang": "en-US",
"start_url": "/",
"theme_color": "#ffffff",
"background_color": "#ffffff",
"display_override": [
"window-controls-overlay"
],
"icons": [
...
]
}
</code></pre>
<p>On the surface, the feature is really simple to use. This manifest change is the only thing we need to make the title bar disappear and turn the window controls into an overlay.</p>
<p>However, to provide a great experience for all users regardless of what device or browser they use, and to make the most of the title bar area in our design, we’ll need a bit of CSS and JavaScript code.</p>
<p>Here is what the app looks like now:</p>
<figure id="figure12" class="wp-block-image"><img src="https://lh6.googleusercontent.com/YbSJ4vMtrc88Jr8sh7F8uWED-9OVFvLkXNT3xVP9gdmQt9XwC-wGHPmaspcKnfSpPMjSotYzRISGPag1Ugq3mxWTslaVhPK9iP8IHLjFnE_FcIkM0y3olJ4Gzw5ejrZFTRbz9avF" alt="" referrerpolicy="no-referrer"><figcaption><em>Screenshot of the 1DIV app thumbnail view using Window Controls Overlay on macOS. The separate top bar area is gone, but the window controls are now blocking some of the app’s interface</em></figcaption></figure>
<p>The title bar is gone, which is what we wanted, but our logo, search field, and <strong>NEW</strong> button are partially covered by the window controls because now our layout starts at the top of the window.</p>
<p>It’s similar on Windows, with the difference that the close, maximize, and minimize buttons appear on the right side, grouped together with the PWA control buttons:</p>
<figure id="figure13" class="wp-block-image"><img src="https://lh6.googleusercontent.com/ytqSauTsKKNI6N7YzxlIqhNatK7LwaPw6yY74jq2egOsBIHbzl2vFGPMRK6dqx6tE-UqSCCWS8f1YftsXZygxEB6KALUYfGU9XW4poE1NPpjYKV66bk1k6dy91rh6TMZ1qb3Rph-" alt="" referrerpolicy="no-referrer"><figcaption><em>Screenshot of the 1DIV app thumbnail display using Window Controls Overlay on the Windows operating system. The separate top bar area is gone, but the window controls are now blocking some of the app’s content.</em></figcaption></figure>
<h2 id="section6" class="wp-block-heading">Using CSS to keep clear of the window controls<a class="subhead-anchor" href="https://alistapart.com/article/breaking-out-of-the-box/#section6">#section6</a></h2>
<p>Along with the feature, new CSS environment variables have been introduced:</p>
<ul><li><strong><code>titlebar-area-x</code></strong></li><li><code><strong>titlebar-area-y</strong></code></li><li><code><strong>titlebar-area-width</strong></code></li><li><strong><code>titlebar-area-height</code></strong></li></ul>
<p>You use these variables with the CSS <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/env()"><strong>env()</strong></a> function to position your content where the title bar would have been while ensuring it won’t overlap with the window controls. In our case, we’ll use two of the variables to position our header, which contains the logo, search bar, and <strong>NEW</strong> button. </p>
<pre id="snippet2"><code class="language-css">header {
position: absolute;
left: env(titlebar-area-x, 0);
width: env(titlebar-area-width, 100%);
height: var(--toolbar-height);
}
</code></pre>
<p>The <code><strong>titlebar-area-x</strong> </code>variable gives us the distance from the left of the viewport to where the title bar would appear, and <strong><code>titlebar-area-width</code></strong> is its width. (Remember, this is not equivalent to the width of the entire viewport, just the title bar portion, which as noted earlier, doesn’t include the window controls.)</p>
<p>By doing this, we make sure our content remains fully visible. We’re also defining fallback values (the second parameter in the <strong><code>env()</code></strong> function) for when the variables are not defined (such as on non-supporting browsers, or when the Windows Control Overlay feature is disabled).</p>
<figure id="figure14" class="wp-block-image"><img src="https://lh5.googleusercontent.com/AYZ7D2ZqvPLip8FtF6IzI6XSAEoajjviCG5fo40_ynrksUesFQBjZVEN6dsTOA8F9CCqXbFWb32ZYUN73hEAkMlyzKnX_1Qzjy7kR6jl42TyyJOeg1FWK7A9WeWn-_7SD57-EOdt" alt="" referrerpolicy="no-referrer"><figcaption><em>Screenshot of the 1DIV app thumbnail view on macOS with Window Controls Overlay and our CSS updated. The app content that the window controls had been blocking has been repositioned.</em></figcaption></figure>
<figure id="figure15" class="wp-block-image"><img src="https://lh6.googleusercontent.com/ZxtH5F4v2io8ntHHi8V0YhqgBc_GD5pcq4g52zZy4_bEhbtjC3G7WdyZqQmwc6-D_NIp7Z8dvjsG8qz42DIg7RDhC6HbPHThXEFsknbOgcEfkF7d_cqx45T9vTi6z23pVe0-1nxA" alt="" referrerpolicy="no-referrer"><figcaption><em>Screenshot of the 1DIV app thumbnail view on the Windows operating system with Window Controls Overlay and our updated CSS. The app content that the window controls had been blocking has been repositioned.</em></figcaption></figure>
<p>Now our header adapts to its surroundings, and it doesn’t feel like the window control buttons have been added as an afterthought. The app looks a lot more like a native app.</p>
<h2 id="section7" class="wp-block-heading">Changing the window controls background color so it blends in<a class="subhead-anchor" href="https://alistapart.com/article/breaking-out-of-the-box/#section7">#section7</a></h2>
<p>Now let’s take a closer look at our second page: the CSS playground editor.</p>
<figure id="figure16" class="wp-block-image size-large"><img loading="lazy" width="960" height="486" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig16.png?resize=960%2C486&ssl=1" alt="" class="wp-image-7173230" data-recalc-dims="1" referrerpolicy="no-referrer"><figcaption><em>Screenshots of the 1DIV app CSS editor view with Window Controls Overlay in macOS and Windows, respectively. The window controls overlay areas have a solid white background color, which contrasts with the hot pink color of the example CSS design displayed in the editor.</em></figcaption></figure>
<p>Not great. Our CSS demo area does go all the way to the top, which is what we wanted, but the way the window controls appear as white rectangles on top of it is quite jarring.</p>
<p>We can fix this by changing the app’s theme color. There are a couple of ways to define it:</p>
<ul><li>PWAs can define a theme color in the web app manifest file using the <a href="https://developer.mozilla.org/en-US/docs/Web/Manifest/theme_color"><strong>theme_color</strong></a> manifest member. This color is then used by the OS in different ways. On desktop platforms, it is used to provide a background color to the title bar and window controls.</li><li>Websites can use the <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta/name/theme-color"><strong>theme-color</strong> meta tag</a> as well. It’s used by browsers to customize the color of the UI around the web page. For PWAs, this color can override the manifest <strong><code>theme_color</code></strong>.</li></ul>
<p>In our case, we can set the manifest <strong><code>theme_color</code></strong> to white to provide the right default color for our app. The OS will read this color value when the app is installed and use it to make the window controls background color white. This color works great for our main page with the list of demos.</p>
<p>The <strong><code>theme-color</code></strong> meta tag can be changed at runtime, using JavaScript. So we can do that to override the white with the right demo background color when one is opened.</p>
<p>Here is the function we’ll use:</p>
<pre id="snippet3"><code class="language-javascript">function themeWindow(bgColor) {
document.querySelector("meta[name=theme-color]").setAttribute('content', bgColor);
}</code></pre>
<p>With this in place, we can imagine how using color and CSS transitions can produce a smooth change from the list page to the demo page, and enable the window control buttons to blend in with the rest of the app’s interface.</p>
<figure id="figure17" class="wp-block-image"><img src="https://lh4.googleusercontent.com/YVYktaP8CkIQJFlCtWlwVU4dequS4MutbDJfm-vS8kGx_nedIgzziuHeZICeJ-vsu33VR0rydqwKH0JVIFKjWjlrvbWPYssNvxr7rBsCKKdag7PHMhA_NLV3w0nzBuBzurk1fr1i" alt="" referrerpolicy="no-referrer"><figcaption><em>Screenshot of the 1DIV app CSS editor view on the Windows operating system with Window Controls Overlay and updated CSS demonstrating how the window control buttons blend in with the rest of the app’s interface.</em></figcaption></figure>
<h2 id="section8" class="wp-block-heading">Dragging the window<a class="subhead-anchor" href="https://alistapart.com/article/breaking-out-of-the-box/#section8">#section8</a></h2>
<p>Now, getting rid of the title bar entirely does have an important accessibility consequence: it’s much more difficult to move the application window around.</p>
<p>The title bar provides a sizable area for users to click and drag, but by using the Window Controls Overlay feature, this area becomes limited to where the control buttons are, and users have to very precisely aim between these buttons to move the window.</p>
<p>Fortunately, this can be fixed using CSS with the <strong><code>app-region</code></strong> property. This property is, for now, only supported in Chromium-based browsers and needs the <strong><code>-webkit-</code></strong> vendor prefix. </p>
<p>To make any element of the app become a dragging target for the window, we can use the following: </p>
<p><strong><code>-webkit-app-region: drag;</code></strong></p>
<p>It is also possible to explicitly make an element non-draggable: </p>
<p><code>-<strong>webkit-app-region: no-drag;</strong> </code></p>
<p>These options can be useful for us. We can make the entire header a dragging target, but make the search field and <strong>NEW</strong> button within it non-draggable so they can still be used as normal.</p>
<p>However, because the editor page doesn’t display the header, users wouldn’t be able to drag the window while editing code. So let’s use a different approach. We’ll create another element before our header, also absolutely positioned, and dedicated to dragging the window.</p>
<pre id="snippet4"><code class="language-markup"><div class="drag"></div>
<header>...</header></code></pre>
<pre id="snippet5"><code class="language-css">.drag {
position: absolute;
top: 0;
width: 100%;
height: env(titlebar-area-height, 0);
-webkit-app-region: drag;
}</code></pre>
<p>With the above code, we’re making the draggable area span the entire viewport width, and using the <strong><code>titlebar-area-height</code></strong> variable to make it as tall as what the title bar would have been. This way, our draggable area is aligned with the window control buttons as shown below.</p>
<p>And, now, to make sure our search field and button remain usable:</p>
<pre id="snippet6"><code class="language-css">header .search,
header .new {
-webkit-app-region: no-drag;
}</code></pre>
<p>With the above code, users can click and drag where the title bar used to be. It is an area that users expect to be able to use to move windows on desktop, and we’re not breaking this expectation, which is good.</p>
<figure id="figure18" class="wp-block-image"><img src="https://lh5.googleusercontent.com/sU0QjlT2R7SrF91GI--WcdHRy0shD7CfnKpfzvgXGz5VptZY6hyoDX_SYFqxFG85dxMgbLidjb8cwJOcnqzd4OAWeNjIVgSiKpaz68orEZEU7DgKHHLkM3NXU5rkALkpUrEl7Pp_" alt="" referrerpolicy="no-referrer"><figcaption><em>An animated view of the 1DIV app being dragged across a Windows desktop with the mouse.</em></figcaption></figure>
<h2 id="section9" class="wp-block-heading">Adapting to window resize<a class="subhead-anchor" href="https://alistapart.com/article/breaking-out-of-the-box/#section9">#section9</a></h2>
<p>It may be useful for an app to know both whether the window controls overlay is visible and when its size changes. In our case, if the user made the window very narrow, there wouldn’t be enough space for the search field, logo, and button to fit, so we’d want to push them down a bit.</p>
<p>The Window Controls Overlay feature comes with a JavaScript API we can use to do this: <strong><code>navigator.windowControlsOverlay</code></strong>.</p>
<p>The API provides three interesting things:</p>
<ul><li><strong><code>navigator.windowControlsOverlay.visible</code></strong> lets us know whether the overlay is visible.</li><li><strong><code>navigator.windowControlsOverlay.getBoundingClientRect()</code></strong> lets us know the position and size of the title bar area.</li><li><strong><code>navigator.windowControlsOverlay.ongeometrychange</code></strong> lets us know when the size or visibility changes.</li></ul>
<p>Let’s use this to be aware of the size of the title bar area and move the header down if it’s too narrow.</p>
<pre id="snippet7"><code class="language-javascript">if (navigator.windowControlsOverlay) {
navigator.windowControlsOverlay.addEventListener('geometrychange', () => {
const { width } = navigator.windowControlsOverlay.getBoundingClientRect();
document.body.classList.toggle('narrow', width < 250);
});
}</code></pre>
<p>In the example above, we set the <strong><code>narrow</code></strong> class on the <strong><code>body</code></strong> of the app if the title bar area is narrower than 250px. We could do something similar with a media query, but using the <strong><code>windowControlsOverlay</code></strong> API has two advantages for our use case:</p>
<ul><li>It’s only fired when the feature is supported and used; we don’t want to adapt the design otherwise.</li><li>We get the size of the title bar area across operating systems, which is great because the size of the window controls is different on Mac and Windows. Using a media query wouldn’t make it possible for us to know exactly how much space remains.</li></ul>
<pre id="snippet8"><code class="language-css">.narrow header {
top: env(titlebar-area-height, 0);
left: 0;
width: 100%;
}</code></pre>
<p>Using the above CSS code, we can move our header down to stay clear of the window control buttons when the window is too narrow, and move the thumbnails down accordingly.</p>
<figure id="figure19" class="wp-block-image"><img src="https://lh5.googleusercontent.com/e4oVs-No9pSWdYyfqTJ0QKcKrDzlv11bsoTwSVvFBhi1bUo9dP2ub71MlWa90QLEFUc5C9e81mQtg3xwGpB5Kkfvu1dNqdBVhqetz74N_0TSWh7_RfZ5NkDNJEuhv5_ZVvw-vpDG" alt="" referrerpolicy="no-referrer"><figcaption><em>A screenshot of the 1DIV app on Windows showing the app’s content adjusted for a much narrower viewport.</em></figcaption></figure>
<h2 id="section10" class="wp-block-heading">Thirty pixels of exciting design opportunities<a class="subhead-anchor" href="https://alistapart.com/article/breaking-out-of-the-box/#section10">#section10</a></h2>
<p><br>Using the Window Controls Overlay feature, we were able to take our simple demo app and turn it into something that feels so much more integrated on desktop devices. Something that reaches out of the usual window constraints and provides a custom experience for its users.</p>
<p>In reality, this feature only gives us about 30 pixels of extra room and comes with challenges on how to deal with the window controls. And yet, this extra room and those challenges can be turned into exciting design opportunities.</p>
<p>More devices of all shapes and forms get invented all the time, and the web keeps on evolving to adapt to them. New features get added to the web platform to allow us, web authors, to integrate more and more deeply with those devices. From watches or foldable devices to desktop computers, we need to evolve our design approach for the web. Building for the web now lets us think outside the rectangular box.</p>
<p>So let’s embrace this. Let’s use the standard technologies already at our disposal, and experiment with new ideas to provide tailored experiences for all devices, all from a single codebase!</p>
<p><br>If you get a chance to try the Window Controls Overlay feature and have feedback about it, you can <a href="https://github.com/WICG/window-controls-overlay/issues">open issues on the spec’s repository</a>. It’s still early in the development of this feature, and you can help make it even better. Or, you can take a look at the <a href="https://docs.microsoft.com/en-us/microsoft-edge/progressive-web-apps-chromium/how-to/window-controls-overlay">feature’s existing documentation</a>, or this <a href="https://stupefied-edison-a4ee55.netlify.app/">demo app</a> and its <a href="https://github.com/captainbrosset/1DIV">source code</a>. </p>
In this excerpt from In Fulfillment: The Designer’s Journey, Justin Dauer ruminates on the past and the importance of keeping an open mind.
Colin Eagan and Jeffrey MacIntyre offer a “ground-up” approach to implementing personalized digital experiences that are intentional, ethical, and technologically sound.
Is mobile-first CSS always the best option? Patrick Clancey explores the pros and cons and lays out an alternative.
Learn how to engage stakeholders, focus on impactful objectives, and measure the results in this template for ethical design.
Seriously, do not ever design screens again without first answering these questions: what are the objects and how do they relate?]]></description>
<pubDate>Thu, 09 Dec 2021 15:00:00 GMT</pubDate>
<guid isPermaLink="false">https://alistapart.com/article/breaking-out-of-the-box/</guid>
<link>https://alistapart.com/article/breaking-out-of-the-box/</link>
<author><![CDATA[Patrick Brosset]]></author>
<category>Code</category>
<category>CSS</category>
</item>
<item>
<title><![CDATA[Human-Readable JavaScript: A Tale of Two Experts]]></title>
<description><![CDATA[<p>Everyone wants to be an expert. But what does that even mean? Over the years I’ve seen two types of people who are referred to as “experts.” Expert 1 is someone who knows every tool in the language and makes sure to use every bit of it, whether it helps or not. Expert 2 also knows every piece of syntax, but they’re pickier about what they employ to solve problems, considering a number of factors, both code-related and not. </p>
<p>Can you take a guess at which expert we want working on our team? If you said Expert 2, you’d be right. They’re a developer focused on delivering readable code—lines of JavaScript others can understand and maintain. Someone who can make the complex simple. But “readable” is rarely definitive—in fact, it’s largely based on the eyes of the beholder. So where does that leave us? What should experts aim for when writing readable code? Are there clear right and wrong choices? The answer is, it depends.</p>
<h2 id="section2" class="wp-block-heading">The obvious choice<a class="subhead-anchor" href="https://alistapart.com/article/human-readable-javascript/#section2">#section2</a></h2>
<p>In order to improve developer experience, TC39 has been adding lots of new features to ECMAScript in recent years, including many proven patterns borrowed from other languages. One such addition, added in ES2019, is <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/flat"><code>Array.prototype.flat()</code></a> It takes an argument of depth or <code>Infinity</code>, and flattens an array. If no argument is given, the depth defaults to 1.</p>
<p>Prior to this addition, we needed the following syntax to flatten an array to a single level.</p>
<pre id="snippet1"><code class="language-javascript">let arr = [1, 2, [3, 4]];
[].concat.apply([], arr);
// [1, 2, 3, 4]</code></pre>
<p>When we added <code>flat()</code>, that same functionality could be expressed using a single, descriptive function.</p>
<pre id="snippet2"><code class="language-javascript">arr.flat();
// [1, 2, 3, 4]</code></pre>
<p>Is the second line of code more readable? The answer is emphatically yes. In fact, both experts would agree.</p>
<p>Not every developer is going to be aware that <code>flat()</code> exists. But they don’t need to because <code>flat()</code> is a descriptive verb that conveys the meaning of what is happening. It’s a lot more intuitive than <code>concat.apply()</code>.</p>
<p>This is the rare case where there is a definitive answer to the question of whether new syntax is better than old. Both experts, each of whom is familiar with the two syntax options, will choose the second. They’ll choose the shorter, clearer, more easily maintained line of code.</p>
<p>But choices and trade-offs aren’t always so decisive.</p>
<h2 id="section3" class="wp-block-heading">The gut check<a class="subhead-anchor" href="https://alistapart.com/article/human-readable-javascript/#section3">#section3</a></h2>
<p>The wonder of JavaScript is that it’s incredibly versatile. There is a reason it’s all over the web. Whether you think that’s a good or <a href="https://alistapart.com/article/responsible-javascript-part-1/">bad</a> thing is another story.</p>
<p>But with that versatility comes the paradox of choice. You can write the same code in many different ways. How do you determine which way is “right”? You can’t even begin to make a decision unless you understand the available options and their limitations.</p>
<p>Let’s use functional programming with <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map"><code>map()</code></a> as the example. I’ll walk through various iterations that all yield the same result.</p>
<p>This is the tersest version of our <code>map()</code> examples. It uses the fewest characters, all fit into one line. This is our baseline.</p>
<pre id="snippet3"><code class="language-javascript">const arr = [1, 2, 3];
let multipliedByTwo = arr.map(el => el * 2);
// multipliedByTwo is [2, 4, 6]</code></pre>
<p>This next example adds only two characters: parentheses. Is anything lost? How about gained? Does it make a difference that a function with more than one parameter will always need to use the parentheses? I’d argue that it does. There is little to no detriment in adding them here, and it improves consistency when you inevitably write a function with multiple parameters. In fact, when I wrote this, <a href="https://prettier.io/">Prettier</a> enforced that constraint; it didn’t want me to create an arrow function without the parentheses.</p>
<pre id="snippet4"><code class="language-javascript">let multipliedByTwo = arr.map((el) => el * 2);</code></pre>
<p>Let’s take it a step further. We’ve added curly braces and a return. Now this is starting to look more like a traditional function definition. Right now, it may seem like overkill to have a keyword as long as the function logic. Yet, if the function is more than one line, this extra syntax is again required. Do we presume that we will not have any other functions that go beyond a single line? That seems dubious.</p>
<pre id="snippet5"><code class="language-javascript">let multipliedByTwo = arr.map((el) => {
return el * 2;
});</code></pre>
<p>Next we’ve removed the arrow function altogether. We’re using the same syntax as before, but we’ve swapped out for the <code>function</code> keyword. This is interesting because there is no scenario in which this syntax won’t work; no number of parameters or lines will cause problems, so consistency is on our side. It’s more verbose than our initial definition, but is that a bad thing? How does this hit a new coder, or someone who is well versed in something other than JavaScript? Is someone who knows JavaScript well going to be frustrated by this syntax in comparison?</p>
<pre id="snippet6"><code class="language-javascript">let multipliedByTwo = arr.map(function(el) {
return el * 2;
});</code></pre>
<p>Finally we get to the last option: passing just the function. And <code>timesTwo</code> can be written using any syntax we like. Again, there is no scenario in which passing the function name causes a problem. But step back for a moment and think about whether or not this could be confusing. If you’re new to this codebase, is it clear that <code>timesTwo</code> is a function and not an object? Sure, <code>map()</code> is there to give you a hint, but it’s not unreasonable to miss that detail. How about the location of where <code>timesTwo</code> is declared and initialized? Is it easy to find? Is it clear what it’s doing and how it’s affecting this result? All of these are important considerations.</p>
<pre id="snippet7"><code class="language-javascript">const timesTwo = (el) => el * 2;
let multipliedByTwo = arr.map(timesTwo);</code></pre>
<p>As you can see, there is no obvious answer here. But making the right choice for your codebase means understanding all the options and their limitations. And knowing that consistency requires parentheses and curly braces and <code>return</code> keywords.</p>
<p>There are a number of questions you have to ask yourself when writing code. Questions of <a href="https://alistapart.com/article/responsible-javascript-part-2/#section9">performance</a> are typically the most common. But when you’re looking at code that is functionally identical, your determination should be based on humans—how humans consume code.</p>
<h2 id="section4" class="wp-block-heading">Maybe newer isn’t always better<a class="subhead-anchor" href="https://alistapart.com/article/human-readable-javascript/#section4">#section4</a></h2>
<p>So far we’ve found a clear-cut example of where both experts would reach for the newest syntax, even if it’s not universally known. We’ve also looked at an example that poses a lot of questions but not as many answers.</p>
<p>Now it’s time to dive into code that I’ve written before…and removed. This is code that made me the first expert, using a little-known piece of syntax to solve a problem to the detriment of my colleagues and the maintainability of our codebase.</p>
<p><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment">Destructuring assignment</a> lets you unpack values from objects (or arrays). It typically looks something like this.</p>
<pre id="snippet8"><code class="language-javascript">const {node} = exampleObject;</code></pre>
<p>It initializes a variable and assigns it a value all in one line. But it doesn’t have to.</p>
<pre id="snippet9"><code class="language-javascript">let node
;({node} = exampleObject)</code></pre>
<p>The last line of code assigns a variable to a value using destructuring, but the variable declaration takes place one line before it. It’s not an uncommon thing to want to do, but many people don’t realize you can do it.</p>
<p>But look at that code closely. It forces an awkward semicolon for code that doesn’t use semicolons to terminate lines. It wraps the command in parentheses and adds the curly braces; it’s entirely unclear what this is doing. It’s not easy to read, and, as an expert, it shouldn’t be in code that I write.</p>
<pre id="snippet10"><code class="language-javascript">let node
node = exampleObject.node</code></pre>
<p>This code solves the problem. It works, it’s clear what it does, and my colleagues will understand it without having to look it up. With the destructuring syntax, just because I <em>can</em> doesn’t mean I <em>should</em>.</p>
<h2 id="section5" class="wp-block-heading">Code isn’t everything<a class="subhead-anchor" href="https://alistapart.com/article/human-readable-javascript/#section5">#section5</a></h2>
<p>As we’ve seen, the Expert 2 solution is rarely obvious based on code alone; yet there are still clear distinctions between which code each expert would write. That’s because code is for machines to read and humans to interpret. So there are non-code factors to consider!</p>
<p>The syntax choices you make for a team of JavaScript developers is different than those you should make for a team of polyglots who aren’t steeped in the minutiae. </p>
<p>Let’s take spread vs. <code>concat()</code> as an example.</p>
<p>Spread was added to ECMAScript a few years ago, and it’s enjoyed wide adoption. It’s sort of a utility syntax in that it can do a lot of different things. One of them is concatenating a number of arrays.</p>
<pre id="snippet11"><code class="language-javascript">const arr1 = [1, 2, 3];
const arr2 = [9, 11, 13];
const nums = [...arr1, ...arr2];</code></pre>
<p>As powerful as spread is, it isn’t a very intuitive symbol. So unless you already know what it does, it’s not super helpful. While both experts <em>may</em> safely assume a team of JavaScript specialists are familiar with this syntax, Expert 2 will probably question whether that’s true of a team of polyglot programmers. Instead, Expert 2 may select the <code>concat()</code> method instead, as it’s a descriptive verb that you can probably understand from the context of the code.</p>
<p>This code snippet gives us the same nums result as the spread example above.</p>
<pre id="snippet12"><code class="language-javascript">const arr1 = [1, 2, 3];
const arr2 = [9, 11, 13];
const nums = arr1.concat(arr2);</code></pre>
<p>And that’s but one example of how human factors influence code choices. A codebase that’s touched by a lot of different teams, for example, may have to hold more stringent standards that don’t necessarily keep up with the latest and greatest syntax. Then you move beyond the main source code and consider other factors in your tooling chain that make life easier, or harder, for the humans who work on that code. There is code that can be structured in a way that’s <a href="https://www.freecodecamp.org/news/how-to-write-testable-code/">hostile to testing</a>. There is code that backs you into a corner for <a href="https://www.codeproject.com/Articles/701862/How-Not-to-Back-Yourself-into-a-Corner">future scaling or feature addition</a>. There is code that’s <a href="https://laurieontech.com/posts/performance-diagnosis/">less performant</a>, doesn’t <a href="https://alistapart.com/article/fromswitchestotargets/">handle different browsers</a>, or <a href="https://a11y.coffee/">isn’t accessible</a>. All of these factor into the recommendations Expert 2 makes.</p>
<p>Expert 2 also considers the impact of naming. But let’s be honest, even <em>they</em> can’t get that right most of the time.</p>
<h2 id="section6" class="wp-block-heading">Conclusion<a class="subhead-anchor" href="https://alistapart.com/article/human-readable-javascript/#section6">#section6</a></h2>
<p>Experts don’t prove themselves by using every piece of the spec; they prove themselves by knowing the spec well enough to deploy syntax judiciously and make well-reasoned decisions. This is how experts become multipliers—how they make new experts.</p>
<p>So what does this mean for those of us who consider ourselves experts or aspiring experts? It means that writing code involves asking yourself a lot of questions. It means considering your developer audience in a real way. The best code you can write is code that accomplishes something complex, but is inherently understood by those who examine your codebase.</p>
<p>And no, it’s not easy. And there often isn’t a clear-cut answer. But it’s something you should consider with every function you write.</p>
In this excerpt from In Fulfillment: The Designer’s Journey, Justin Dauer ruminates on the past and the importance of keeping an open mind.
Colin Eagan and Jeffrey MacIntyre offer a “ground-up” approach to implementing personalized digital experiences that are intentional, ethical, and technologically sound.
Is mobile-first CSS always the best option? Patrick Clancey explores the pros and cons and lays out an alternative.
Learn how to engage stakeholders, focus on impactful objectives, and measure the results in this template for ethical design.
What can we do with thirty pixels? Windows Controls Overlay frees us from 40 years of history telling us how apps should look.]]></description>
<pubDate>Thu, 25 Mar 2021 14:00:00 GMT</pubDate>
<guid isPermaLink="false">https://alistapart.com/article/human-readable-javascript/</guid>
<link>https://alistapart.com/article/human-readable-javascript/</link>
<author><![CDATA[Laurie Barth]]></author>
<category>Code</category>
<category>JavaScript</category>
</item>
<item>
<title><![CDATA[Now THAT’S What I Call Service Worker!]]></title>
<description><![CDATA[<p>The <a href="https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API">Service Worker</a> API is the <a href="https://en.wikipedia.org/wiki/Dremel">Dremel</a> of the web platform. It offers incredibly broad utility while also yielding resiliency and better performance. If you’ve not used Service Worker yet—and you couldn’t be blamed if so, as <a href="https://almanac.httparchive.org/en/2020/pwa#service-workers">it hasn’t seen wide adoption as of 2020</a>—it goes something like this:</p>
<ol><li>On the initial visit to a website, the browser <a href="https://develope |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please use WordPress REST API instead.
The API endpoints are available at /wp-json/wp/v2
.
The endpoints that will be involved are /wp-json/wp/v2/article
and /wp-json/wp/v2/categories
It will be similar to #12824 (comment)
Successfully generated as following: http://localhost:1200/alistapart - Success ✔️<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"
>
<channel>
<title><![CDATA[A List Apart]]></title>
<link>https://alistapart.com/articles</link>
<atom:link href="http://localhost:1200/alistapart" rel="self" type="application/rss+xml" />
<description><![CDATA[Articles on aListApart.com - Made with love by RSSHub(https://github.com/DIYgod/RSSHub)]]></description>
<generator>RSSHub</generator>
<webMaster>i@diygod.me (DIYgod)</webMaster>
<language>en-us</language>
<lastBuildDate>Thu, 10 Aug 2023 13:28:03 GMT</lastBuildDate>
<ttl>5</ttl>
<item>
<title><![CDATA[Humility: An Essential Value]]></title>
<description><![CDATA[<p>Humility, a designer’s essential value—that has a nice ring to it. What about humility, an office manager’s essential value? Or a dentist’s? Or a librarian’s? They all sound great. When humility is our guiding light, the path is always open for fulfillment, evolution, connection, and engagement. In this chapter, we’re going to talk about why.</p>
<p>That said, this is a book for designers, and to that end, I’d like to start with a story—well, a journey, really. It’s a personal one, and I’m going to make myself a bit vulnerable along the way. I call it:</p>
<h2 class="wp-block-heading"><strong>The Tale of Justin’s Preposterous Pate</strong></h2>
<p>When I was coming out of art school, a long-haired, goateed neophyte, print was a known quantity to me; design on the web, however, was rife with complexities to navigate and discover, a problem to be solved. Though I had been formally trained in graphic design, typography, and layout, what fascinated me was how these traditional skills might be applied to a fledgling digital landscape. This theme would ultimately shape the rest of my career.</p>
<p>So rather than graduate and go into print like many of my friends, I devoured HTML and JavaScript books into the wee hours of the morning and taught myself how to code during my senior year. I wanted—nay, needed—to better understand the underlying implications of what my design decisions would mean once rendered in a browser.</p>
<p>The late ’90s and early 2000s were the so-called “Wild West” of web design. Designers at the time were all figuring out how to apply design and visual communication to the digital landscape. What were the rules? How could we break them and still engage, entertain, and convey information? At a more macro level, how could my values, inclusive of humility, respect, and connection, align in tandem with that? I was hungry to find out.</p>
<p>Though I’m talking about a different era, those are timeless considerations between non-career interactions and the world of design. What are your core passions, or values, that transcend medium? It’s essentially the same concept we discussed earlier on the direct parallels between what fulfills you, agnostic of the tangible or digital realms; the core themes are all the same.</p>
<p>First within tables, animated GIFs, Flash, then with Web Standards, <code>div</code>s, and CSS, there was personality, raw unbridled creativity, and unique means of presentment that often defied any semblance of a visible grid. Splash screens and “browser requirement” pages aplenty. Usability and accessibility were typically victims of such a creation, but such paramount facets of any digital design were largely (and, in hindsight, unfairly) disregarded at the expense of experimentation.</p>
<p>For example, this iteration of my personal portfolio site (“the pseudoroom”) from that era was experimental, if not a bit heavy- handed, in the visual communication of the concept of a living sketchbook. Very skeuomorphic. I collaborated with fellow designer and dear friend Marc Clancy (now a co-founder of the creative project organizing app Milanote) on this one, where we’d first sketch and then pass a Photoshop file back and forth to trick things out and play with varied user interactions. Then, I’d break it down and code it into a digital layout.</p>
<figure class="wp-block-image size-full is-resized"><img decoding="async" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2023/06/designers-journey-2-1.jpg?w=846&ssl=1" alt="" class="wp-image-7173967" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2023/06/designers-journey-2-1.jpg?w=1024&ssl=1 1024w, https://i0.wp.com/alistapart.com/wp-content/uploads/2023/06/designers-journey-2-1.jpg?resize=300%2C252&ssl=1 300w, https://i0.wp.com/alistapart.com/wp-content/uploads/2023/06/designers-journey-2-1.jpg?resize=768%2C644&ssl=1 768w" sizes="(max-width: 960px) 100vw, 960px" data-recalc-dims="1" referrerpolicy="no-referrer"><figcaption class="wp-element-caption">Figure 1: “the pseudoroom” website, hitting the sketchbook metaphor hard.</figcaption></figure>
<p>Along with design folio pieces, the site also offered free downloads for Mac OS customizations: desktop wallpapers that were effectively design experimentation, custom-designed typefaces, and desktop icons.</p>
<p>From around the same time, GUI Galaxy was a design, pixel art, and Mac-centric news portal some graphic designer friends and I conceived, designed, developed, and deployed.</p>
<figure class="wp-block-image size-large is-resized"><img decoding="async" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2023/06/designers-journey-2-2.jpg?w=846&ssl=1" alt="" class="wp-image-7173968" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2023/06/designers-journey-2-2.jpg?resize=1024%2C907&ssl=1 1024w, https://i0.wp.com/alistapart.com/wp-content/uploads/2023/06/designers-journey-2-2.jpg?resize=300%2C266&ssl=1 300w, https://i0.wp.com/alistapart.com/wp-content/uploads/2023/06/designers-journey-2-2.jpg?resize=768%2C680&ssl=1 768w, https://i0.wp.com/alistapart.com/wp-content/uploads/2023/06/designers-journey-2-2.jpg?resize=1536%2C1360&ssl=1 1536w, https://i0.wp.com/alistapart.com/wp-content/uploads/2023/06/designers-journey-2-2.jpg?w=1590&ssl=1 1590w" sizes="(max-width: 960px) 100vw, 960px" data-recalc-dims="1" referrerpolicy="no-referrer"><figcaption class="wp-element-caption">Figure 2: GUI Galaxy, web standards-compliant design news portal</figcaption></figure>
<p>Design news portals were incredibly popular during this period, featuring (what would now be considered) Tweet-size, small-format snippets of pertinent news from the categories I previously mentioned. If you took Twitter, curated it to a few categories, and wrapped it in a custom-branded experience, you’d have a design news portal from the late 90s / early 2000s.</p>
<p>We as designers had evolved and created a bandwidth-sensitive, web standards award-winning, much more accessibility-conscious website. Still ripe with experimentation, yet more mindful of equitable engagement. You can see a couple of content panes here, noting general news (tech, design) and Mac-centric news below. We also offered many of the custom downloads I cited before as present on my folio site but branded and themed to GUI Galaxy.</p>
<p>The site’s backbone was a homegrown CMS, with the presentation layer consisting of global design + illustration + news author collaboration. And the collaboration effort here, in addition to experimentation on a ‘brand’ and content delivery, was hitting my core. We were designing something bigger than any single one of us and connecting with a global audience.</p>
<p>Collaboration and connection transcend medium in their impact, immensely fulfilling me as a designer.</p>
<p>Now, why am I taking you down this trip of design memory lane? Two reasons.</p>
<p>First, there’s a reason for the nostalgia for that design era (the “Wild West” era, as I called it earlier): the inherent exploration, personality, and creativity that saturated many design portals and personal portfolio sites. Ultra-finely detailed pixel art UI, custom illustration, bespoke vector graphics, all underpinned by a strong design community.</p>
<p>Today’s web design has been in a period of stagnation. I suspect there’s a strong chance you’ve seen a site whose structure looks something like this: a hero image / banner with text overlaid, perhaps with a lovely rotating carousel of images (laying the snark on heavy there), a call to action, and three columns of sub-content directly beneath. Maybe an icon library is employed with selections that vaguely relate to their respective content.</p>
<p>Design, as it’s applied to the digital landscape, is in dire need of thoughtful layout, typography, and visual engagement that goes hand-in-hand with all the modern considerations we now know are paramount: usability. Accessibility. Load times and bandwidth- sensitive content delivery. A responsive presentation that meets human beings wherever they’re engaging from. We must be mindful of, and respectful toward, those concerns—but not at the expense of creativity of visual communication or via replicating cookie-cutter layouts.</p>
<h2 class="wp-block-heading">Pixel Problems</h2>
<p>Websites during this period were often designed and built on Macs whose OS and desktops looked something like this. This is Mac OS 7.5, but 8 and 9 weren’t that different.</p>
<figure class="wp-block-image size-large is-resized"><img decoding="async" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2023/06/designers-journey-2-3.jpg?w=846&ssl=1" alt="" class="wp-image-7173969" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2023/06/designers-journey-2-3.jpg?resize=1024%2C768&ssl=1 1024w, https://i0.wp.com/alistapart.com/wp-content/uploads/2023/06/designers-journey-2-3.jpg?resize=300%2C225&ssl=1 300w, https://i0.wp.com/alistapart.com/wp-content/uploads/2023/06/designers-journey-2-3.jpg?resize=768%2C576&ssl=1 768w, https://i0.wp.com/alistapart.com/wp-content/uploads/2023/06/designers-journey-2-3.jpg?resize=1536%2C1152&ssl=1 1536w, https://i0.wp.com/alistapart.com/wp-content/uploads/2023/06/designers-journey-2-3.jpg?w=1592&ssl=1 1592w" sizes="(max-width: 960px) 100vw, 960px" data-recalc-dims="1" referrerpolicy="no-referrer"><figcaption class="wp-element-caption">Figure 3: A Mac OS 7.5-centric desktop.</figcaption></figure>
<p>Desktop icons fascinated me: how could any single one, at any given point, stand out to get my attention? In this example, the user’s desktop is tidy, but think of a more realistic example with icon pandemonium. Or, say an icon was part of a larger system grouping (fonts, extensions, control panels)—how did it also maintain cohesion amongst a group?</p>
<p>These were 32 x 32 pixel creations, utilizing a 256-color palette, designed pixel-by-pixel as mini mosaics. To me, this was the embodiment of digital visual communication under such ridiculous constraints. And often, ridiculous restrictions can yield the purification of concept and theme.</p>
<p>So I began to research and do my homework. I was a student of this new medium, hungry to dissect, process, discover, and make it my own.</p>
<p>Expanding upon the notion of exploration, I wanted to see how I could push the limits of a 32×32 pixel grid with that 256-color palette. Those ridiculous constraints forced a clarity of concept and presentation that I found incredibly appealing. The digital gauntlet had been tossed, and that challenge fueled me. And so, in my dorm room into the wee hours of the morning, I toiled away, bringing conceptual sketches into mini mosaic fruition.</p>
<p>These are some of my creations, utilizing the only tool available at the time to create icons called ResEdit. ResEdit was a clunky, built-in Mac OS utility not really made for exactly what we were using it for. At the core of all of this work: Research. Challenge. Problem- solving. Again, these core connection-based values are agnostic of medium.</p>
<figure class="wp-block-image size-large is-resized"><img decoding="async" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2023/06/designers-journey-2-4.png?w=846&ssl=1" alt="" class="wp-image-7173970" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2023/06/designers-journey-2-4.png?resize=1024%2C977&ssl=1 1024w, https://i0.wp.com/alistapart.com/wp-content/uploads/2023/06/designers-journey-2-4.png?resize=300%2C286&ssl=1 300w, https://i0.wp.com/alistapart.com/wp-content/uploads/2023/06/designers-journey-2-4.png?resize=768%2C733&ssl=1 768w, https://i0.wp.com/alistapart.com/wp-content/uploads/2023/06/designers-journey-2-4.png?resize=1536%2C1465&ssl=1 1536w, https://i0.wp.com/alistapart.com/wp-content/uploads/2023/06/designers-journey-2-4.png?w=1602&ssl=1 1602w" sizes="(max-width: 960px) 100vw, 960px" data-recalc-dims="1" referrerpolicy="no-referrer"><figcaption class="wp-element-caption">Figure 4: A selection of my pixel art design, 32×32 pixel canvas, 8-bit palette</figcaption></figure>
<p>There’s one more design portal I want to talk about, which also serves as the second reason for my story to bring this all together.</p>
<p>This is K10k, short for Kaliber 1000. K10k was founded in 1998 by Michael Schmidt and Toke Nygaard, and was <strong>the </strong>design news portal on the web during this period. With its pixel art-fueled presentation, ultra-focused care given to every facet and detail, and with many of the more influential designers of the time who were invited to be news authors on the site, well… it was the place to be, my friend. With respect where respect is due, GUI Galaxy’s concept was inspired by what these folks were doing.</p>
<figure class="wp-block-image size-full is-resized"><img decoding="async" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2023/06/designers-journey-2-5.png?w=846&ssl=1" alt="" class="wp-image-7173971" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2023/06/designers-journey-2-5.png?w=1024&ssl=1 1024w, https://i0.wp.com/alistapart.com/wp-content/uploads/2023/06/designers-journey-2-5.png?resize=300%2C269&ssl=1 300w, https://i0.wp.com/alistapart.com/wp-content/uploads/2023/06/designers-journey-2-5.png?resize=768%2C688&ssl=1 768w" sizes="(max-width: 960px) 100vw, 960px" data-recalc-dims="1" referrerpolicy="no-referrer"><figcaption class="wp-element-caption">Figure 5: The K10k website</figcaption></figure>
<p>For my part, the combination of my web design work and pixel art exploration began to get me some notoriety in the design scene. Eventually, K10k noticed and added me as one of their very select group of news authors to contribute content to the site.</p>
<p>Amongst my personal work and side projects—and now with this inclusion—in the design community, this put me on the map. My design work also began to be published in various printed collections, in magazines domestically and overseas, and featured on other design news portals. With that degree of success while in my early twenties, something else happened:</p>
<p>I evolved—devolved, really—into a colossal asshole (and in just about a year out of art school, no less). The press and the praise became what fulfilled me, and they went straight to my head. They inflated my ego. I actually felt somewhat superior to my fellow designers.</p>
<p>The casualties? My design stagnated. Its evolution—my evolution— stagnated.</p>
<p>I felt so supremely confident in my abilities that I effectively stopped researching and discovering. When previously sketching concepts or iterating ideas in lead was my automatic step one, I instead leaped right into Photoshop. I drew my inspiration from the smallest of sources (and with blinders on). Any critique of my work from my peers was often vehemently dismissed. The most tragic loss: I had lost touch with my values.</p>
<p>My ego almost cost me some of my friendships and burgeoning professional relationships. I was toxic in talking about design and in collaboration. But thankfully, those same friends gave me a priceless gift: candor. They called me out on my unhealthy behavior.</p>
<p>Admittedly, it was a gift I initially did not accept but ultimately was able to deeply reflect upon. I was soon able to accept, and process, and course correct. The realization laid me low, but the re-awakening was essential. I let go of the “reward” of adulation and re-centered upon what stoked the fire for me in art school. Most importantly: I got back to my core values.</p>
<h2 class="wp-block-heading">Always Students</h2>
<p>Following that short-term regression, I was able to push forward in my personal design and career. And I could self-reflect as I got older to facilitate further growth and course correction as needed.</p>
<p>As an example, let’s talk about the Large Hadron Collider. The LHC was designed <em>“to help answer some of the fundamental open questions in physics, which concern the basic laws governing the interactions and forces among the elementary objects, the deep structure of space and time, and in particular the interrelation between quantum mechanics and general relativity.” </em>Thanks, Wikipedia.</p>
<p>Around fifteen years ago, in one of my earlier professional roles, I designed the interface for the application that generated the LHC’s particle collision diagrams. These diagrams are the rendering of what’s actually happening inside the Collider during any given particle collision event and are often considered works of art unto themselves.</p>
<p>Designing the interface for this application was a fascinating process for me, in that I worked with Fermilab physicists to understand what the application was trying to achieve, but also how the physicists themselves would be using it. To that end, in this role,</p>
<p>I cut my teeth on usability testing, working with the Fermilab team to iterate and improve the interface. How they spoke and what they spoke about was like an alien language to me. And by making myself humble and working under the mindset that I was but a student, I made myself available to be a part of their world to generate that vital connection.</p>
<p>I also had my first ethnographic observation experience: going to the Fermilab location and observing how the physicists used the tool in their actual environment, on their actual terminals. For example, one takeaway was that due to the level of ambient light-driven contrast within the facility, the data columns ended up using white text on a dark gray background instead of black text-on-white. This enabled them to pore over reams of data during the day and ease their eye strain. And Fermilab and CERN are government entities with rigorous accessibility standards, so my knowledge in that realm also grew. The barrier-free design was another essential form of connection.</p>
<p>So to those core drivers of my visual problem-solving soul and ultimate fulfillment: discovery, exposure to new media, observation, human connection, and evolution. What opened the door for those values was me checking my ego before I walked through it.</p>
<p>An evergreen willingness to listen, learn, understand, grow, evolve, and connect yields our best work. In particular, I want to focus on the words ‘grow’ and ‘evolve’ in that statement. If we are always students of our craft, we are also continually making ourselves available to evolve. Yes, we have years of applicable design study under our belt. Or the focused lab sessions from a UX bootcamp. Or the monogrammed portfolio of our work. Or, ultimately, decades of a career behind us.</p>
<p>But all that said: experience does not equal “expert.”</p>
<p>As soon as we close our minds via an inner monologue of ‘knowing it all’ or branding ourselves a “#thoughtleader” on social media, the designer we <strong>are </strong>is our final form. The designer we <strong>can be </strong>will never exist.</p>
]]></description>
<pubDate>Thu, 22 Jun 2023 13:00:00 GMT</pubDate>
<guid isPermaLink="false">https://alistapart.com/article/humility-an-essential-value/</guid>
<link>https://alistapart.com/article/humility-an-essential-value/</link>
<author><![CDATA[justindauer]]></author>
<category>Design</category>
</item>
<item>
<title><![CDATA[Personalization Pyramid: A Framework for Designing with User Data]]></title>
<description><![CDATA[<p>As a UX professional in today’s data-driven landscape, it’s increasingly likely that you’ve been asked to design a personalized digital experience, whether it’s a public website, user portal, or native application. Yet while there continues to be no shortage of marketing hype around personalization platforms, we still have very few standardized approaches for implementing personalized UX.</p>
<p>That’s where we come in. After completing dozens of personalization projects over the past few years, we gave ourselves a goal: could you create a holistic personalization framework specifically for UX practitioners? The <strong>Personalization Pyramid</strong> is a designer-centric model for standing up human-centered personalization programs, spanning data, segmentation, content delivery, and overall goals. By using this approach, you will be able to understand the core components of a contemporary, UX-driven personalization program (or at the very least know enough to get started). </p>
<figure class="wp-block-image size-large"><img decoding="async" loading="lazy" width="960" height="558" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image20.png?resize=960%2C558&ssl=1" alt="A chart answering the question Do you have the resources you need to run personalization in your organization? Globally, 13% don’t 33% have limited access, 39% have it (on demand), and 15% have it dedicated." class="wp-image-7173666" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image20.png?resize=1024%2C595&ssl=1 1024w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image20.png?resize=300%2C174&ssl=1 300w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image20.png?resize=768%2C446&ssl=1 768w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image20.png?w=1256&ssl=1 1256w" sizes="(max-width: 960px) 100vw, 960px" data-recalc-dims="1" referrerpolicy="no-referrer"><figcaption class="wp-element-caption"><p><strong>Growing tools for personalization:</strong> According to a Dynamic Yield survey, 39% of respondents felt support is available on-demand when a business case is made for it (up 15% from 2020).</p><p><small>Source: “The State of Personalization Maturity – Q4 2021” Dynamic Yield conducted its annual maturity survey across roles and sectors in the Americas (AMER), Europe and the Middle East (EMEA), and the Asia-Pacific (APAC) regions. This marks the fourth consecutive year publishing our research, which includes more than 450 responses from individuals in the C-Suite, Marketing, Merchandising, CX, Product, and IT.</small></p></figcaption></figure>
<h2 class="wp-block-heading"><strong>Getting Started</strong></h2>
<p>For the sake of this article, we’ll assume you’re already familiar with the basics of digital personalization. A good overview can be found here: <a href="https://www.uxbooth.com/articles/website-personalization-planning/">Website Personalization Planning</a>. While UX projects in this area can take on many different forms, they often stem from similar starting points. </p>
<p><strong>Common scenarios for starting a personalization project:</strong></p>
<ul>
<li>Your organization or client purchased a content management system (CMS) or marketing automation platform (MAP) or related technology that supports personalization</li>
<li>The CMO, CDO, or CIO has identified personalization as a goal</li>
<li>Customer data is disjointed or ambiguous</li>
<li>You are running some isolated targeting campaigns or A/B testing</li>
<li>Stakeholders disagree on personalization approach</li>
<li>Mandate of customer privacy rules (e.g. GDPR) requires revisiting existing user targeting practices</li>
</ul>
<figure class="wp-block-image size-full is-resized"><img decoding="async" loading="lazy" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image22.png?resize=768%2C576&ssl=1" alt="Two men and a woman discussing personalization using a card deck. They are seated at a round table in a hotel conference room. The workshop leaders, two women, are at a podium in the background." class="wp-image-7173667" width="768" height="576" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image22.png?w=1024&ssl=1 1024w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image22.png?resize=300%2C225&ssl=1 300w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image22.png?resize=768%2C576&ssl=1 768w" sizes="(max-width: 768px) 100vw, 768px" data-recalc-dims="1" referrerpolicy="no-referrer"><figcaption class="wp-element-caption">Workshopping personalization at a conference.</figcaption></figure>
<p>Regardless of where you begin, a successful personalization program will require the same core building blocks. We’ve captured these as the “levels” on the pyramid. Whether you are a UX designer, researcher, or strategist, understanding the core components can help make your contribution successful. </p>
<figure class="wp-block-image size-large is-resized"><img decoding="async" loading="lazy" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image14.png?resize=488%2C512&ssl=1" alt="The Personalization Pyramid visualized. The pyramid is stacks labeled, from the bottom, raw data (1m+), actionable data (100k+), user segments (1k+), contexts & campaigns (100s), touchpoints (dozens), goals (handful). The North Star (one) is above. An arrow for prescriptive, business driven data goes up the left side and an arrow for adaptive user-driven data goes down the right side." class="wp-image-7173665" width="488" height="512" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image14.png?resize=975%2C1024&ssl=1 975w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image14.png?resize=286%2C300&ssl=1 286w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image14.png?resize=768%2C807&ssl=1 768w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image14.png?w=1146&ssl=1 1146w" sizes="(max-width: 488px) 100vw, 488px" data-recalc-dims="1" referrerpolicy="no-referrer"><figcaption class="wp-element-caption">From the ground up: Soup-to-nuts personalization, without going nuts.</figcaption></figure>
<p>From top to bottom, the levels include:</p>
<ol>
<li><strong>North Star: </strong>What larger strategic objective is driving the personalization program? </li>
<li><strong>Goals:</strong> What are the specific, measurable outcomes of the program? </li>
<li><strong>Touchpoints: </strong>Where will the personalized experience be served?</li>
<li><strong>Contexts and Campaigns: </strong>What personalization content will the user see?</li>
<li><strong>User Segments:</strong> What constitutes a unique, usable audience? </li>
<li><strong>Actionable Data: </strong>What reliable and authoritative data is captured by our technical platform to drive personalization? </li>
<li><strong>Raw Data: </strong>What wider set of data is conceivably available (already in our setting) allowing you to personalize?</li>
</ol>
<p>We’ll go through each of these levels in turn. To help make this actionable, we created an accompanying <strong>deck of cards</strong> to illustrate specific examples from each level. We’ve found them helpful in personalization brainstorming sessions, and will include examples for you here.</p>
<figure class="wp-block-image size-full"><img decoding="async" loading="lazy" width="320" height="215" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image2.png?resize=320%2C215&ssl=1" alt="A deck of personalization brainstorming cards (the size of playing cards) against a black background." class="wp-image-7173668" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image2.png?w=320&ssl=1 320w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image2.png?resize=300%2C202&ssl=1 300w" sizes="(max-width: 320px) 100vw, 320px" data-recalc-dims="1" referrerpolicy="no-referrer"><figcaption class="wp-element-caption"><strong>Personalization pack:</strong> Deck of cards to help kickstart your personalization brainstorming.</figcaption></figure>
<h2 class="wp-block-heading"><strong>Starting at the Top</strong></h2>
<p>The components of the pyramid are as follows:</p>
<h3 class="wp-block-heading">North Star</h3>
<p>A north star is what you are aiming for overall with your personalization program (big or small). The North Star defines the (one) overall mission of the personalization program. What do you wish to accomplish? North Stars cast a shadow. The bigger the star, the bigger the shadow. Example of North Starts might include: </p>
<ol>
<li><strong>Function:</strong> Personalize based on basic user inputs. Examples: “Raw” notifications, basic search results, system user settings and configuration options, general customization, basic optimizations</li>
<li><strong>Feature:</strong> Self-contained personalization componentry. Examples: “Cooked” notifications, advanced optimizations (geolocation), basic dynamic messaging, customized modules, automations, recommenders</li>
<li><strong>Experience:</strong> Personalized user experiences across multiple interactions and user flows. Examples: Email campaigns, landing pages, advanced messaging (i.e. C2C chat) or conversational interfaces, larger user flows and content-intensive optimizations (localization).</li>
<li><strong>Product:</strong> Highly differentiating personalized product experiences. Examples: Standalone, branded experiences with personalization at their core, like the “algotorial” playlists by Spotify such as Discover Weekly.</li>
</ol>
<figure class="wp-block-gallery has-nested-images columns-default is-cropped wp-block-gallery-1 is-layout-flex wp-block-gallery-is-layout-flex">
<figure class="wp-block-image size-large"><img decoding="async" loading="lazy" width="656" height="1024" data-id="7173669" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image7.jpg?resize=656%2C1024&ssl=1" alt="Function: React to basic user inputs" class="wp-image-7173669" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image7.jpg?resize=656%2C1024&ssl=1 656w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image7.jpg?resize=192%2C300&ssl=1 192w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image7.jpg?resize=768%2C1198&ssl=1 768w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image7.jpg?resize=984%2C1536&ssl=1 984w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image7.jpg?w=1249&ssl=1 1249w" sizes="(max-width: 656px) 100vw, 656px" data-recalc-dims="1" referrerpolicy="no-referrer"></figure>
<figure class="wp-block-image size-large"><img decoding="async" loading="lazy" width="656" height="1024" data-id="7173670" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image18.jpg?resize=656%2C1024&ssl=1" alt="Feature: personalized modules" class="wp-image-7173670" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image18.jpg?resize=656%2C1024&ssl=1 656w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image18.jpg?resize=192%2C300&ssl=1 192w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image18.jpg?resize=768%2C1198&ssl=1 768w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image18.jpg?resize=984%2C1536&ssl=1 984w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image18.jpg?w=1249&ssl=1 1249w" sizes="(max-width: 656px) 100vw, 656px" data-recalc-dims="1" referrerpolicy="no-referrer"></figure>
<figure class="wp-block-image size-large"><img decoding="async" loading="lazy" width="656" height="1024" data-id="7173671" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image8.jpg?resize=656%2C1024&ssl=1" alt="Experience: Integrated personalization" class="wp-image-7173671" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image8.jpg?resize=656%2C1024&ssl=1 656w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image8.jpg?resize=192%2C300&ssl=1 192w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image8.jpg?resize=768%2C1198&ssl=1 768w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image8.jpg?resize=984%2C1536&ssl=1 984w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image8.jpg?w=1249&ssl=1 1249w" sizes="(max-width: 656px) 100vw, 656px" data-recalc-dims="1" referrerpolicy="no-referrer"></figure>
<figcaption class="blocks-gallery-caption wp-element-caption"><strong>North star cards. </strong>These can help orient your team towards a common goal that personalization will help achieve; Also, these are useful for characterizing the end-state ambition of the presently stated personalization effort.</figcaption></figure>
<h3 class="wp-block-heading">Goals</h3>
<p>As in any good UX design, personalization can help accelerate <a href="https://www.uxbooth.com/articles/designing-for-customer-intentions-part-1/">designing with customer intentions</a><strong>. Goals</strong> are the tactical and measurable metrics that will prove the overall program is successful. A good place to start is with your current analytics and measurement program and metrics you can benchmark against. In some cases, new goals may be appropriate. The key thing to remember is that <em>personalization itself is not a goal</em>, rather it is a means to an end. Common goals include:</p>
<ul>
<li>Conversion</li>
<li>Time on task</li>
<li>Net promoter score (NPS)</li>
<li>Customer satisfaction </li>
</ul>
<figure class="wp-block-gallery has-nested-images columns-default is-cropped wp-block-gallery-3 is-layout-flex wp-block-gallery-is-layout-flex">
<figure class="wp-block-image size-large"><img decoding="async" loading="lazy" width="656" height="1024" data-id="7173674" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image12.jpg?resize=656%2C1024&ssl=1" alt="NPS: Net Promoter Score" class="wp-image-7173674" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image12.jpg?resize=656%2C1024&ssl=1 656w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image12.jpg?resize=192%2C300&ssl=1 192w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image12.jpg?resize=768%2C1198&ssl=1 768w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image12.jpg?resize=984%2C1536&ssl=1 984w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image12.jpg?w=1249&ssl=1 1249w" sizes="(max-width: 656px) 100vw, 656px" data-recalc-dims="1" referrerpolicy="no-referrer"></figure>
<figure class="wp-block-image size-large"><img decoding="async" loading="lazy" width="656" height="1024" data-id="7173672" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image23.jpg?resize=656%2C1024&ssl=1" alt="Time on Task: Users move quicker" class="wp-image-7173672" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image23.jpg?resize=656%2C1024&ssl=1 656w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image23.jpg?resize=192%2C300&ssl=1 192w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image23.jpg?resize=768%2C1198&ssl=1 768w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image23.jpg?resize=984%2C1536&ssl=1 984w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image23.jpg?w=1249&ssl=1 1249w" sizes="(max-width: 656px) 100vw, 656px" data-recalc-dims="1" referrerpolicy="no-referrer"></figure>
<figure class="wp-block-image size-large"><img decoding="async" loading="lazy" width="656" height="1024" data-id="7173673" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image13.jpg?resize=656%2C1024&ssl=1" alt="Conversion: Move more of the thing" class="wp-image-7173673" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image13.jpg?resize=656%2C1024&ssl=1 656w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image13.jpg?resize=192%2C300&ssl=1 192w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image13.jpg?resize=768%2C1198&ssl=1 768w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image13.jpg?resize=984%2C1536&ssl=1 984w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image13.jpg?w=1249&ssl=1 1249w" sizes="(max-width: 656px) 100vw, 656px" data-recalc-dims="1" referrerpolicy="no-referrer"></figure>
<figcaption class="blocks-gallery-caption wp-element-caption"><strong>Goal cards.</strong> Examples of some common KPIs related to personalization that are concrete and measurable.</figcaption></figure>
<h3 class="wp-block-heading">Touchpoints</h3>
<p>Touchpoints are where the personalization happens. As a UX designer, this will be one of your largest areas of responsibility. The touchpoints available to you will depend on how your personalization and associated technology capabilities are instrumented, and should be rooted in improving a user’s experience at a particular point in the journey. Touchpoints can be multi-device (mobile, in-store, website) but also more granular (web banner, web pop-up etc.). Here are some examples:</p>
<p><strong>Channel-level </strong>Touchpoints</p>
<ul>
<li>Email: Role</li>
<li>Email: Time of open</li>
<li>In-store display (JSON endpoint)</li>
<li>Native app</li>
<li>Search</li>
</ul>
<p><strong>Wireframe-level </strong>Touchpoints</p>
<ul>
<li>Web overlay</li>
<li>Web alert bar</li>
<li>Web banner</li>
<li>Web content block</li>
<li>Web menu</li>
</ul>
<figure class="wp-block-gallery has-nested-images columns-default is-cropped wp-block-gallery-5 is-layout-flex wp-block-gallery-is-layout-flex">
<figure class="wp-block-image size-large"><img decoding="async" loading="lazy" width="656" height="1024" data-id="7173677" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image25.jpg?resize=656%2C1024&ssl=1" alt="In-store Display: End-cap interfaces" class="wp-image-7173677" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image25.jpg?resize=656%2C1024&ssl=1 656w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image25.jpg?resize=192%2C300&ssl=1 192w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image25.jpg?resize=768%2C1198&ssl=1 768w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image25.jpg?resize=984%2C1536&ssl=1 984w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image25.jpg?w=1249&ssl=1 1249w" sizes="(max-width: 656px) 100vw, 656px" data-recalc-dims="1" referrerpolicy="no-referrer"></figure>
<figure class="wp-block-image size-large"><img decoding="async" loading="lazy" width="656" height="1024" data-id="7173675" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image11.jpg?resize=656%2C1024&ssl=1" alt="Email: Time, personalize at time of open" class="wp-image-7173675" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image11.jpg?resize=656%2C1024&ssl=1 656w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image11.jpg?resize=192%2C300&ssl=1 192w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image11.jpg?resize=768%2C1198&ssl=1 768w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image11.jpg?resize=984%2C1536&ssl=1 984w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image11.jpg?w=1249&ssl=1 1249w" sizes="(max-width: 656px) 100vw, 656px" data-recalc-dims="1" referrerpolicy="no-referrer"></figure>
<figure class="wp-block-image size-large"><img decoding="async" loading="lazy" width="656" height="1024" data-id="7173676" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image19.jpg?resize=656%2C1024&ssl=1" alt="Content Block: Into the woodwork" class="wp-image-7173676" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image19.jpg?resize=656%2C1024&ssl=1 656w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image19.jpg?resize=192%2C300&ssl=1 192w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image19.jpg?resize=768%2C1198&ssl=1 768w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image19.jpg?resize=984%2C1536&ssl=1 984w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image19.jpg?w=1249&ssl=1 1249w" sizes="(max-width: 656px) 100vw, 656px" data-recalc-dims="1" referrerpolicy="no-referrer"></figure>
<figcaption class="blocks-gallery-caption wp-element-caption"><strong>Touchpoint cards. </strong>Examples of common personalization touchpoints: these can vary from narrow (e.g., email) to broad (e.g., in-store).</figcaption></figure>
<p>If you’re designing for web interfaces, for example, you will likely need to include personalized “zones” in your wireframes. The content for these can be presented programmatically in touchpoints based on our next step, contexts and campaigns.</p>
<figure class="wp-block-gallery has-nested-images columns-default is-cropped wp-block-gallery-7 is-layout-flex wp-block-gallery-is-layout-flex">
<figure class="wp-block-image size-large"><img decoding="async" loading="lazy" width="732" height="974" data-id="7173678" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image21.png?resize=732%2C974&ssl=1" alt="" class="wp-image-7173678" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image21.png?w=732&ssl=1 732w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image21.png?resize=225%2C300&ssl=1 225w" sizes="(max-width: 732px) 100vw, 732px" data-recalc-dims="1" referrerpolicy="no-referrer"></figure>
<figure class="wp-block-image size-large"><img decoding="async" loading="lazy" width="708" height="922" data-id="7173679" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image17.png?resize=708%2C922&ssl=1" alt="" class="wp-image-7173679" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image17.png?w=708&ssl=1 708w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image17.png?resize=230%2C300&ssl=1 230w" sizes="(max-width: 708px) 100vw, 708px" data-recalc-dims="1" referrerpolicy="no-referrer"></figure>
<figcaption class="blocks-gallery-caption wp-element-caption"><strong>Targeted Zones:</strong> Examples from Kibo of personalized “zones” on page-level wireframes occurring at various stages of a user journey (Engagement phase at left and Purchase phase at right.)<br><br>Source: “Essential Guide to End-to-End Personaliztion” by Kibo.</figcaption></figure>
<h3 class="wp-block-heading">Contexts and Campaigns</h3>
<p>Once you’ve outlined some touchpoints, you can consider the actual personalized content a user will receive. Many personalization tools will refer to these as “campaigns” (so, for example, a campaign on a web banner for new visitors to the website). These will programmatically be shown at certain touchpoints to certain user segments, as defined by user data. At this stage, we find it helpful to consider two separate models: a <strong>context model</strong> and a <strong>content model</strong>. The context helps you consider the level of engagement of the user at the personalization moment, for example a user casually browsing information vs. doing a deep-dive. Think of it in terms of information retrieval behaviors. The content model can then help you determine what type of personalization to serve based on the context (for example, an “Enrich” campaign that shows related articles may be a suitable supplement to extant content).</p>
<p>Personalization <strong>Context</strong> Model:</p>
<ol>
<li>Browse</li>
<li>Skim</li>
<li>Nudge</li>
<li>Feast</li>
</ol>
<p>Personalization <strong>Content</strong> Model:</p>
<ol>
<li>Alert</li>
<li>Make Easier</li>
<li>Cross-Sell</li>
<li>Enrich</li>
</ol>
<p>We’ve written extensively about each of these models elsewhere, so if you’d like to read more you can check out Colin’s <a href="https://alistapart.com/article/emerging-ux-role-in-personalization/">Personalization Content Model</a> and Jeff’s <a href="https://bucket.circle.so/c/field-notes/progressive-personalization-a-decisionmaking-model-for-better-outcomes-in-personalized-ux">Personalization Context Model</a>. </p>
<figure class="wp-block-gallery has-nested-images columns-default is-cropped wp-block-gallery-9 is-layout-flex wp-block-gallery-is-layout-flex">
<figure class="wp-block-image size-large"><img decoding="async" loading="lazy" width="656" height="1024" data-id="7173681" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image28.jpg?resize=656%2C1024&ssl=1" alt="Cross Sell: You may also like…" class="wp-image-7173681" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image28.jpg?resize=656%2C1024&ssl=1 656w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image28.jpg?resize=192%2C300&ssl=1 192w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image28.jpg?resize=768%2C1198&ssl=1 768w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image28.jpg?resize=984%2C1536&ssl=1 984w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image28.jpg?w=1249&ssl=1 1249w" sizes="(max-width: 656px) 100vw, 656px" data-recalc-dims="1" referrerpolicy="no-referrer"></figure>
<figure class="wp-block-image size-large"><img decoding="async" loading="lazy" width="656" height="1024" data-id="7173682" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image3.jpg?resize=656%2C1024&ssl=1" alt="Enrich: You might find this interesting" class="wp-image-7173682" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image3.jpg?resize=656%2C1024&ssl=1 656w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image3.jpg?resize=192%2C300&ssl=1 192w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image3.jpg?resize=768%2C1198&ssl=1 768w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image3.jpg?resize=984%2C1536&ssl=1 984w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image3.jpg?w=1249&ssl=1 1249w" sizes="(max-width: 656px) 100vw, 656px" data-recalc-dims="1" referrerpolicy="no-referrer"></figure>
<figure class="wp-block-image size-large"><img decoding="async" loading="lazy" width="656" height="1024" data-id="7173680" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image27.jpg?resize=656%2C1024&ssl=1" alt="Browse: Lean back, shallow engagement" class="wp-image-7173680" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image27.jpg?resize=656%2C1024&ssl=1 656w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image27.jpg?resize=192%2C300&ssl=1 192w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image27.jpg?resize=768%2C1198&ssl=1 768w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image27.jpg?resize=984%2C1536&ssl=1 984w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image27.jpg?w=1249&ssl=1 1249w" sizes="(max-width: 656px) 100vw, 656px" data-recalc-dims="1" referrerpolicy="no-referrer"></figure>
<figcaption class="blocks-gallery-caption wp-element-caption"><strong>Campaign and Context cards:</strong> This level of the pyramid can help your team focus around the types of personalization to deliver end users and the use-cases in which they will experience it.</figcaption></figure>
<h3 class="wp-block-heading">User Segments</h3>
<p>User segments can be created prescriptively or adaptively, based on user research (e.g. via rules and logic tied to set user behaviors or via A/B testing). At a minimum you will likely need to consider how to treat the <em>unknown</em> or first-time visitor, the <em>guest</em> or returning visitor for whom you may have a stateful cookie (or equivalent post-cookie identifier), or the <em>authenticated</em> visitor who is logged in. Here are some examples from the personalization pyramid:</p>
<ul>
<li>Unknown</li>
<li>Guest</li>
<li>Authenticated</li>
<li>Default</li>
<li>Referred</li>
<li>Role</li>
<li>Cohort</li>
<li>Unique ID</li>
</ul>
<figure class="wp-block-gallery has-nested-images columns-default is-cropped wp-block-gallery-11 is-layout-flex wp-block-gallery-is-layout-flex">
<figure class="wp-block-image size-large"><img decoding="async" loading="lazy" width="656" height="1024" data-id="7173685" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image16.jpg?resize=656%2C1024&ssl=1" alt="Authenticated: Logged in with token" class="wp-image-7173685" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image16.jpg?resize=656%2C1024&ssl=1 656w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image16.jpg?resize=192%2C300&ssl=1 192w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image16.jpg?resize=768%2C1198&ssl=1 768w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image16.jpg?resize=984%2C1536&ssl=1 984w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image16.jpg?w=1249&ssl=1 1249w" sizes="(max-width: 656px) 100vw, 656px" data-recalc-dims="1" referrerpolicy="no-referrer"></figure>
<figure class="wp-block-image size-large"><img decoding="async" loading="lazy" width="656" height="1024" data-id="7173683" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image10.jpg?resize=656%2C1024&ssl=1" alt="Unknown: Could be anyone really" class="wp-image-7173683" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image10.jpg?resize=656%2C1024&ssl=1 656w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image10.jpg?resize=192%2C300&ssl=1 192w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image10.jpg?resize=768%2C1198&ssl=1 768w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image10.jpg?resize=984%2C1536&ssl=1 984w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image10.jpg?w=1249&ssl=1 1249w" sizes="(max-width: 656px) 100vw, 656px" data-recalc-dims="1" referrerpolicy="no-referrer"></figure>
<figure class="wp-block-image size-large"><img decoding="async" loading="lazy" width="656" height="1024" data-id="7173684" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image9.jpg?resize=656%2C1024&ssl=1" alt="Guest: Dropped a cookie" class="wp-image-7173684" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image9.jpg?resize=656%2C1024&ssl=1 656w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image9.jpg?resize=192%2C300&ssl=1 192w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image9.jpg?resize=768%2C1198&ssl=1 768w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image9.jpg?resize=984%2C1536&ssl=1 984w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image9.jpg?w=1249&ssl=1 1249w" sizes="(max-width: 656px) 100vw, 656px" data-recalc-dims="1" referrerpolicy="no-referrer"></figure>
<figcaption class="blocks-gallery-caption wp-element-caption"><strong>Segment cards.</strong> Examples of common personalization segments: at a minimum, you will need to consider the anonymous, guest, and logged in user types. Segmentation can get dramatically more complex from there.</figcaption></figure>
<h3 class="wp-block-heading">Actionable Data</h3>
<p>Every organization with any digital presence has data. It’s a matter of asking what data you can ethically collect on users, its inherent reliability and value, as to how can you use it (sometimes known as “data activation.”) Fortunately, the tide is turning to first-party data: a recent study by Twilio estimates some <strong>80% of businesses are using at least some type of first-party data</strong> to personalize the customer experience. </p>
<figure class="wp-block-image size-large"><img decoding="async" loading="lazy" width="960" height="669" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image6.png?resize=960%2C669&ssl=1" alt="Chart that answers the question "Why is your company focusing on using first-party data for personalization?" The top answer (at 53%) is "it’s higher quality." That is followed by "It’s easier to manage" (46%), "it provides better privacy" (45%), "it’s easier to obtain" (42%), "it’s more cost-effective" (40%), "it’s more ethical" (37%), "our customers want us to" (36%), "it’s the industry norm" (27%), "it’s easier to comply with regulations" (27%), and "we are phasing out 3rd party cookies" (21%)." class="wp-image-7173686" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image6.png?resize=1024%2C714&ssl=1 1024w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image6.png?resize=300%2C209&ssl=1 300w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image6.png?resize=768%2C536&ssl=1 768w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image6.png?w=1500&ssl=1 1500w" sizes="(max-width: 960px) 100vw, 960px" data-recalc-dims="1" referrerpolicy="no-referrer"><figcaption class="wp-element-caption"><small>Source: “The State of Personalization 2021” by Twilio. Survey respondents were n=2,700 adult consumers who have purchased something online in the past 6 months, and n=300 adult manager+ decision-makers at consumer-facing companies that provide goods and/or services online. Respondents were from the United States, United Kingdom, Australia, and New Zealand.Data was collected from April 8 to April 20, 2021.</small></figcaption></figure>
<p>First-party data represents multiple advantages on the UX front, including being relatively simple to collect, more likely to be accurate, and less susceptible to the “creep factor” of third-party data. So a key part of your UX strategy should be to determine what the best form of data collection is on your audiences. Here are some examples:</p>
<figure class="wp-block-gallery has-nested-images columns-default is-cropped wp-block-gallery-13 is-layout-flex wp-block-gallery-is-layout-flex">
<figure class="wp-block-image size-large"><img decoding="async" loading="lazy" width="656" height="1024" data-id="7173691" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image15.jpg?resize=656%2C1024&ssl=1" alt="Quizes: Tell us what you like" class="wp-image-7173691" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image15.jpg?resize=656%2C1024&ssl=1 656w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image15.jpg?resize=192%2C300&ssl=1 192w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image15.jpg?resize=768%2C1198&ssl=1 768w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image15.jpg?resize=984%2C1536&ssl=1 984w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image15.jpg?w=1249&ssl=1 1249w" sizes="(max-width: 656px) 100vw, 656px" data-recalc-dims="1" referrerpolicy="no-referrer"></figure>
<figure class="wp-block-image size-large"><img decoding="async" loading="lazy" width="656" height="1024" data-id="7173689" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image5.jpg?resize=656%2C1024&ssl=1" alt="Behavioral profiling: Males 40+ who wear fedoras" class="wp-image-7173689" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image5.jpg?resize=656%2C1024&ssl=1 656w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image5.jpg?resize=192%2C300&ssl=1 192w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image5.jpg?resize=768%2C1198&ssl=1 768w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image5.jpg?resize=984%2C1536&ssl=1 984w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image5.jpg?w=1249&ssl=1 1249w" sizes="(max-width: 656px) 100vw, 656px" data-recalc-dims="1" referrerpolicy="no-referrer"></figure>
<figure class="wp-block-image size-large"><img decoding="async" loading="lazy" width="656" height="1024" data-id="7173688" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image1.jpg?resize=656%2C1024&ssl=1" alt="Campaign Source: Your discount code 29780…" class="wp-image-7173688" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image1.jpg?resize=656%2C1024&ssl=1 656w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image1.jpg?resize=192%2C300&ssl=1 192w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image1.jpg?resize=768%2C1198&ssl=1 768w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image1.jpg?resize=984%2C1536&ssl=1 984w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image1.jpg?w=1249&ssl=1 1249w" sizes="(max-width: 656px) 100vw, 656px" data-recalc-dims="1" referrerpolicy="no-referrer"></figure>
</figure>
<figure class="wp-block-image size-full"><img decoding="async" loading="lazy" width="822" height="568" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image4.png?resize=822%2C568&ssl=1" alt="Chart showing the impact of personalization across different phases of personalization maturity. It shows that effort is high in the early phases, but drops off quickly starting in phase 3 (machine learning) while at the same time conversion rates, AOV, and ROI increase from a relatively low level to off the chart." class="wp-image-7173692" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image4.png?w=822&ssl=1 822w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image4.png?resize=300%2C207&ssl=1 300w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image4.png?resize=768%2C531&ssl=1 768w" sizes="(max-width: 822px) 100vw, 822px" data-recalc-dims="1" referrerpolicy="no-referrer"><figcaption class="wp-element-caption"><strong>Figure 1.1.2:</strong> Example of a personalization maturity curve, showing progression from basic recommendations functionality to true individualization. Credit: https://kibocommerce.com/blog/kibos-personalization-maturity-chart/</figcaption></figure>
<p>There is a progression of profiling when it comes to recognizing and making decisioning about different audiences and their signals. It tends to move towards more granular constructs about smaller and smaller cohorts of users as time and confidence and data volume grow.</p>
<p>While some combination of <strong>implicit / explicit</strong> <strong>data</strong> is generally a prerequisite for any implementation (more commonly referred to as first party and third-party data) <strong>ML efforts</strong> are typically not cost-effective directly out of the box. This is because a strong data backbone and content repository is a prerequisite for optimization. But these approaches should be considered as part of the larger roadmap and may indeed help accelerate the organization’s overall progress. Typically at this point you will partner with key stakeholders and product owners to design a <strong>profiling model</strong>. The profiling model includes defining approach to configuring profiles, profile keys, profile cards and pattern cards. A multi-faceted approach to profiling which makes it scalable.</p>
<h2 class="wp-block-heading">Pulling it Together</h2>
<p>While the cards comprise the starting point to an inventory of sorts (we provide blanks for you to tailor your own), a set of potential levers and motivations for the style of personalization activities you aspire to deliver, they are more valuable when thought of in a grouping. </p>
<p>In assembling a card “hand”, one can begin to trace the entire trajectory from leadership focus down through a strategic and tactical execution. It is also at the heart of the way both co-authors have conducted workshops in assembling a program backlog—which is a fine subject for another article.</p>
<p>In the meantime, what is important to note is that each colored class of card is helpful to survey in understanding the range of choices potentially at your disposal, it is threading through and making concrete decisions about for whom this decisioning will be made: where, when, and how.</p>
<figure class="wp-block-image size-full"><img decoding="async" loading="lazy" width="960" height="578" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image26.jpg?resize=960%2C578&ssl=1" alt="Cards on a table. At the top: Function is the north star & customer satisfaction is the goal. User segment is unknown, the actionable data is a quiz, context is a nudge, campaign is to make something easier, and the touchpoint is a banner." class="wp-image-7173693" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image26.jpg?w=1024&ssl=1 1024w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image26.jpg?resize=300%2C180&ssl=1 300w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image26.jpg?resize=768%2C462&ssl=1 768w" sizes="(max-width: 960px) 100vw, 960px" data-recalc-dims="1" referrerpolicy="no-referrer"><figcaption class="wp-element-caption"><strong>Scenario A:</strong> We want to use personalization to improve customer satisfaction on the website. For unknown users, we will create a short quiz to better identify what the user has come to do. This is sometimes referred to as “badging” a user in onboarding contexts, to better characterize their present intent and context.</figcaption></figure>
<h2 class="wp-block-heading">Lay Down Your Cards</h2>
<p>Any sustainable personalization strategy must consider near, mid and long-term goals. Even with the leading CMS platforms like Sitecore and Adobe or the most exciting composable CMS DXP out there, there is simply no “easy button” wherein a personalization program can be stood up and immediately view meaningful results. That said, there is a common grammar to all personalization activities, just like every sentence has nouns and verbs. These cards attempt to map that territory.</p>
<p></p>
]]></description>
<pubDate>Thu, 08 Dec 2022 15:00:00 GMT</pubDate>
<guid isPermaLink="false">https://alistapart.com/article/personalization-pyramid/</guid>
<link>https://alistapart.com/article/personalization-pyramid/</link>
<author><![CDATA[colineagan, jeffmacintyre]]></author>
<category>Content</category>
<category>Interaction Design</category>
</item>
<item>
<title><![CDATA[Mobile-First CSS: Is It Time for a Rethink?]]></title>
<description><![CDATA[<p class="intro">The mobile-first design methodology is great—it focuses on what really matters to the user, it’s well-practiced, and it’s been a common design pattern for years. So developing your CSS mobile-first should also be great, too…right? </p>
<p>Well, not necessarily. Classic mobile-first CSS development is based on the principle of overwriting style declarations: you begin your CSS with default style declarations, and overwrite and/or add new styles as you add breakpoints with <code>min-width</code> media queries for larger viewports (for a good overview see “<a href="https://www.mightyminnow.com/2013/11/what-is-mobile-first-css-and-why-does-it-rock/">What is Mobile First CSS and Why Does It Rock?</a>”). But all those exceptions create complexity and inefficiency, which in turn can lead to an increased testing effort and a code base that’s harder to maintain. Admit it—how many of us willingly want that?</p>
<p>On your own projects, mobile-first CSS may yet be the best tool for the job, but first you need to evaluate just how appropriate it is in light of the visual design and user interactions you’re working on. To help you get started, here’s how I go about tackling the factors you need to watch for, and I’ll discuss some alternate solutions if mobile-first doesn’t seem to suit your project.</p>
<h2 class="wp-block-heading">Advantages of mobile-first</h2>
<p>Some of the things to like with mobile-first CSS development—and why it’s been the de facto development methodology for so long—make a lot of sense:</p>
<p><strong>Development hierarchy. </strong>One thing you undoubtedly get from mobile-first is a nice development hierarchy—you just focus on the mobile view and get developing. </p>
<p><strong>Tried and tested. </strong>It’s a tried and tested methodology that’s worke ... |
http://localhost:1200/alistapart/code - Success ✔️<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"
>
<channel>
<title><![CDATA[A List Apart]]></title>
<link>https://alistapart.com/blog/topic/code</link>
<atom:link href="http://localhost:1200/alistapart/code" rel="self" type="application/rss+xml" />
<description><![CDATA[Code Articles on aListApart.com - Made with love by RSSHub(https://github.com/DIYgod/RSSHub)]]></description>
<generator>RSSHub</generator>
<webMaster>i@diygod.me (DIYgod)</webMaster>
<language>en-us</language>
<lastBuildDate>Thu, 10 Aug 2023 13:28:05 GMT</lastBuildDate>
<ttl>5</ttl>
<item>
<title><![CDATA[Mobile-First CSS: Is It Time for a Rethink?]]></title>
<description><![CDATA[<p class="intro">The mobile-first design methodology is great—it focuses on what really matters to the user, it’s well-practiced, and it’s been a common design pattern for years. So developing your CSS mobile-first should also be great, too…right? </p>
<p>Well, not necessarily. Classic mobile-first CSS development is based on the principle of overwriting style declarations: you begin your CSS with default style declarations, and overwrite and/or add new styles as you add breakpoints with <code>min-width</code> media queries for larger viewports (for a good overview see “<a href="https://www.mightyminnow.com/2013/11/what-is-mobile-first-css-and-why-does-it-rock/">What is Mobile First CSS and Why Does It Rock?</a>”). But all those exceptions create complexity and inefficiency, which in turn can lead to an increased testing effort and a code base that’s harder to maintain. Admit it—how many of us willingly want that?</p>
<p>On your own projects, mobile-first CSS may yet be the best tool for the job, but first you need to evaluate just how appropriate it is in light of the visual design and user interactions you’re working on. To help you get started, here’s how I go about tackling the factors you need to watch for, and I’ll discuss some alternate solutions if mobile-first doesn’t seem to suit your project.</p>
<h2 class="wp-block-heading">Advantages of mobile-first</h2>
<p>Some of the things to like with mobile-first CSS development—and why it’s been the de facto development methodology for so long—make a lot of sense:</p>
<p><strong>Development hierarchy. </strong>One thing you undoubtedly get from mobile-first is a nice development hierarchy—you just focus on the mobile view and get developing. </p>
<p><strong>Tried and tested. </strong>It’s a tried and tested methodology that’s worked for years for a reason: it solves a problem really well.</p>
<p><strong>Prioritizes the mobile view</strong>. The mobile view is the<strong> </strong>simplest<strong> </strong>and arguably the most important, as it <strong>encompasses all the key user journeys</strong>, and often accounts for a <strong>higher proportion of user visits</strong> (depending on the project). </p>
<p><strong>Prevents desktop-centric development. </strong>As development is done using desktop computers, it can be tempting to initially focus on the desktop view. But thinking about mobile from the start prevents us from getting stuck later on; no one wants to spend their time retrofitting a desktop-centric site to work on mobile devices!</p>
<h2 class="wp-block-heading">Disadvantages of mobile-first</h2>
<p>Setting style declarations and then overwriting them at higher breakpoints can lead to undesirable ramifications:</p>
<p><strong>More complexity. </strong>The farther up the breakpoint hierarchy you go, the more unnecessary code you inherit from lower breakpoints. </p>
<p><strong>Higher CSS specificity. </strong>Styles that have been reverted to their browser default value in a class name declaration now have a higher specificity. This can be a headache on large projects when you want to keep the CSS selectors as simple as possible.</p>
<p><strong>Requires more regression testing. </strong>Changes to the CSS at a lower view (like adding a new style) requires all higher breakpoints to be regression tested.</p>
<p><strong>The browser can’t prioritize CSS downloads. </strong>At wider breakpoints, classic mobile-first <code>min-width</code> media queries don’t leverage the browser’s capability to download CSS files in priority order.</p>
<h2 class="wp-block-heading">The problem of property value overrides</h2>
<p>There is nothing inherently wrong with overwriting values; CSS was designed to do just that. Still, inheriting incorrect values is unhelpful and can be burdensome and inefficient. It can also lead to increased style specificity when you have to overwrite styles to reset them back to their defaults, something that may cause issues later on, especially if you are using a combination of bespoke CSS and utility classes. We won’t be able to use a utility class for a style that has been reset with a higher specificity.</p>
<p>With this in mind, I’m developing CSS with a focus on the default values much more these days. Since there’s no specific order, and no chains of specific values to keep track of, this frees me to develop breakpoints <em>simultaneously</em>. I concentrate on finding common styles and isolating the specific exceptions in closed media query ranges (that is, any range with a <code>max-width</code> set). </p>
<p>This approach opens up some opportunities, as you can look at each breakpoint as a clean slate. If a component’s layout looks like it should be based on Flexbox at all breakpoints, it’s fine and can be coded in the default style sheet. But if it looks like Grid would be much better for large screens and Flexbox for mobile, these can both be done entirely independently when the CSS is put into closed media query ranges. Also, developing simultaneously requires you to have a good understanding of any given component in all breakpoints up front. This can help surface issues in the design earlier in the development process. We don’t want to get stuck down a rabbit hole building a complex component for mobile, and then get the designs for desktop and find they are equally complex and incompatible with the HTML we created for the mobile view! </p>
<p>Though this approach isn’t going to suit everyone, I encourage you to give it a try. There are plenty of tools out there to help with concurrent development, such as <a href="https://responsively.app/">Responsively App</a>, <a href="https://blisk.io/">Blisk</a>, and many others. </p>
<p>Having said that, I don’t feel the order itself is particularly relevant. If you are comfortable with focusing on the mobile view, have a good understanding of the requirements for other breakpoints, and prefer to work on one device at a time, then by all means stick with the classic development order. The important thing is to identify common styles and exceptions so you can put them in the relevant stylesheet—a sort of manual tree-shaking process! Personally, I find this a little easier when working on a component across breakpoints, but that’s by no means a requirement.</p>
<h2 class="wp-block-heading">Closed media query ranges in practice </h2>
<p>In classic mobile-first CSS we overwrite the styles, but we can avoid this by using media query ranges. To illustrate the difference (I’m using SCSS for brevity), let’s assume there are three visual designs: </p>
<ul><li>smaller than 768</li><li>from 768 to below 1024</li><li>1024 and anything larger </li></ul>
<p>Take a simple example where a block-level element has a default <code>padding</code> of “20px,” which is overwritten at tablet to be “40px” and set back to “20px” on desktop.</p>
<figure class="wp-block-table">
<table><tbody>
<tr>
<td valign="top"><p>Classic <code>min-width</code> mobile-first</p>
<pre><code class="language-css">.my-block {
padding: 20px;
@media (min-width: 768px) {
padding: 40px;
}
@media (min-width: 1024px) {
padding: 20px;
}
}</code></pre></td>
<td valign="top"><p>Closed media query range</p>
<pre><code class="language-css">.my-block {
padding: 20px;
@media (min-width: 768px) and (max-width: 1023.98px) {
padding: 40px;
}
}</code></pre></td>
</tr>
</tbody></table>
</figure>
<p>The subtle difference is that the mobile-first example sets the default <code>padding</code> to “20px” and then overwrites it at each breakpoint, setting it three times in total. In contrast, the second example sets the default <code>padding</code> to “20px” and only overrides it at the relevant breakpoint where it isn’t the default value (in this instance, tablet is the exception).</p>
<p>The goal is to: </p>
<ul><li>Only set styles when needed. </li><li>Not set them with the <em>expectation</em> of overwriting them later on, again and again. </li></ul>
<p>To this end, closed media query ranges are our best friend. If we need to make a change to any given view, we make it in the CSS media query range that applies to the specific breakpoint. We’ll be much less likely to introduce unwanted alterations, and our regression testing only needs to focus on the breakpoint we have actually edited. </p>
<p>Taking the above example, if we find that <code>.my-block</code> spacing on desktop is already accounted for by the margin at that breakpoint, and since we want to remove the padding altogether, we could do this by setting the mobile <code>padding</code> in a closed media query range.<br></p>
<figure class="wp-block-table">
<pre><code class="language-css">.my-block {
@media (max-width: 767.98px) {
padding: 20px;
}
@media (min-width: 768px) and (max-width: 1023.98px) {
padding: 40px;
}
}</code></pre>
</figure>
<p>The browser default <code>padding</code> for our block is “0,” so instead of adding a desktop media query and using <code>unset</code> or “0” for the <code>padding</code> value (which we would need with mobile-first), we can wrap the mobile <code>padding</code> in a closed media query (since it is now also an exception) so it won’t get picked up at wider breakpoints. At the desktop breakpoint, we won’t need to set any <code>padding</code> style, as we want the browser default value.</p>
<h2 class="wp-block-heading">Bundling versus separating the CSS</h2>
<p>Back in the day, keeping the number of requests to a minimum was very important due to the browser’s limit of concurrent requests (typically around six). As a consequence, the use of image sprites and CSS bundling was the norm, with all the CSS being downloaded in one go, as one stylesheet with highest priority. </p>
<p>With HTTP/2 and HTTP/3 now on the scene, the number of requests is no longer the big deal it used to be. This allows us to separate the CSS into multiple files by media query. The clear benefit of this is the browser can now request the CSS it currently needs with a higher priority than the CSS it doesn’t. This is more performant and can reduce the overall time <a href="https://web.dev/critical-rendering-path-render-blocking-css/">page rendering is blocked</a>.</p>
<h3 class="wp-block-heading">Which HTTP version are you using?</h3>
<p>To determine which version of HTTP you’re using, go to your website and open your browser’s dev tools. Next, select the <strong>Network</strong> tab and make sure the <strong>Protocol</strong> column is visible. If “h2” is listed under <strong>Protocol</strong>, it means HTTP/2 is being used. </p>
<p><em>Note: to view the Protocol in your browser’s dev tools, go to the </em><strong><em>Network</em></strong><em> tab, reload your page, right-click any column header (e.g., </em><strong><em>Name</em></strong><em>), and check the </em><strong><em>Protocol</em></strong><em> column.</em></p>
<figure class="wp-block-image"><img decoding="async" src="https://lh4.googleusercontent.com/O8lxNeIY3Hb0YDs2EP7QFhGdGsBXOG7mSTCdAJBd5xkm-6RwrpkS1BN63W7RurVCP3nOH9sNpAR9JNGvIGnUTzG0NYm4sUqI5bU2QPhXYEawmKfeUJ_6YwWAIid2ZDHEdRzaQ1LxzUNTGbGk5g" alt="Chrome dev tools, Network tab filtered by document, Protocol column" referrerpolicy="no-referrer"><figcaption><em>Note: for a summarized comparison, see ImageKit’s “</em><a href="https://imagekit.io/blog/http2-vs-http1-performance/"><em>HTTP/2 vs. HTTP/1</em></a><em>.”</em></figcaption></figure>
<p>Also, if your site is still using HTTP/1...WHY?!! What are you waiting for? There is <a href="https://caniuse.com/http2">excellent user support for HTTP/2</a>.</p>
<h2 class="wp-block-heading">Splitting the CSS</h2>
<p>Separating the CSS into individual files is a worthwhile task. Linking the separate CSS files using the relevant <code>media</code> attribute allows the browser to identify which files are needed immediately (because they’re render-blocking) and which can be deferred. Based on this, it allocates each file an appropriate priority.</p>
<p>In the following example of a website visited on a mobile breakpoint, we can see the mobile and default CSS are loaded with “Highest” priority, as they are currently needed to render the page. The remaining CSS files (print, tablet, and desktop) are still downloaded in case they’ll be needed later, but with “Lowest” priority. </p>
<figure class="wp-block-image"><img decoding="async" src="https://lh5.googleusercontent.com/RZOq-S7kbVsavDiFctQl6STFgGm6puwG8L22V6j6U1vUfo73Opq3Cspj2N94T2BU5lpYUD7Bb_4krFCXlePvBE8xXJVMFwbc_At8pzc-C5ug-6lrPViwMIIXgbKiJA-2fQ3beDoYfkCflCVgwg" alt="Chrome dev tools, Network tab filtered by css, Priority column" referrerpolicy="no-referrer"></figure>
<p>With<strong> bundled CSS</strong>, the browser will have to download the CSS file and parse it before rendering can start.<br><br>While, as noted, with the <strong>CSS separated into different files</strong> linked and marked up with the relevant <code>media</code> attribute, the browser can prioritize the files it currently needs. Using closed media query ranges allows the browser to do this at all widths, as opposed to classic mobile-first <code>min-width</code> queries, where the desktop browser would have to download all the CSS with Highest priority. We can’t assume that desktop users always have a fast connection. For instance, in many rural areas, internet connection speeds are still slow. </p>
<p>The media queries and number of separate CSS files will vary from project to project based on project requirements, but might look similar to the example below.</p>
<figure class="wp-block-table">
<table><tbody>
<tr>
<td valign="top">
<p>Bundled CSS</p>
<code><link href="site.css" rel="stylesheet"></code><br><br>
<p>This single file contains all the CSS, including all media queries, and it will be downloaded with Highest priority.</p>
</td>
<td valign="top">
<p>Separated CSS</p>
<code><link href="default.css" rel="stylesheet"><link href="mobile.css" media="screen and (max-width: 767.98px)" rel="stylesheet"><link href="tablet.css" media="screen and (min-width: 768px) and (max-width: 1083.98px)" rel="stylesheet"><link href="desktop.css" media="screen and (min-width: 1084px)" rel="stylesheet"><link href="print.css" media="print" rel="stylesheet"></code><br><br>
<p>Separating the CSS and specifying a <code>media</code> attribute value on each <code>link</code> tag allows the browser to prioritize what it currently needs. Out of the five files listed above, two will be downloaded with Highest priority: the default file, and the file that matches the current media query. The others will be downloaded with Lowest priority.</p>
</td>
</tr>
</tbody></table>
</figure>
<p>Depending on the project’s deployment strategy, a change to one file (<code>mobile.css</code>, for example) would only require the QA team to regression test on devices in that specific media query range. Compare that to the prospect of deploying the single bundled <code>site.css</code> file, an approach that would normally trigger a full regression test.</p>
<h2 class="wp-block-heading">Moving on</h2>
<p>The uptake of mobile-first CSS was a really important milestone in web development; it has helped front-end developers focus on mobile web applications, rather than developing sites on desktop and then attempting to retrofit them to work on other devices.</p>
<p>I don’t think anyone wants to return to that development model again, but it’s important we don’t lose sight of the issue it highlighted: that things can easily get convoluted and less efficient if we prioritize one particular device—any device—over others. For this reason, focusing on the CSS in its own right, always mindful of what is the default setting and what’s an exception, seems like the natural next step. I’ve started noticing small simplifications in my own CSS, as well as other developers’, and that testing and maintenance work is also a bit more simplified and productive. </p>
<p>In general, simplifying CSS rule creation whenever we can is ultimately a cleaner approach than going around in circles of overrides. But whichever methodology you choose, it needs to suit the project. Mobile-first may—or may not—turn out to be the best choice for what’s involved, but first you need to solidly understand the trade-offs you’re stepping into.</p>
]]></description>
<pubDate>Thu, 09 Jun 2022 02:13:10 GMT</pubDate>
<guid isPermaLink="false">https://alistapart.com/article/mobile-first-css-is-it-time-for-a-rethink/</guid>
<link>https://alistapart.com/article/mobile-first-css-is-it-time-for-a-rethink/</link>
<author><![CDATA[patrick-clancey]]></author>
<category>Code</category>
<category>CSS</category>
</item>
<item>
<title><![CDATA[Breaking Out of the Box]]></title>
<description><![CDATA[<p>CSS is about styling boxes. In fact, the whole web is made of boxes, from the browser viewport to elements on a page. But every once in a while a new feature comes along that makes us rethink our design approach.</p>
<p><a href="https://www.w3.org/TR/css-round-display-1/">Round displays</a>, for example, make it fun to play with circular clip areas. <a href="https://css-tricks.com/the-notch-and-css/">Mobile screen notches</a> and <a href="https://www.w3.org/TR/virtual-keyboard/">virtual keyboards</a> offer challenges to best organize content that stays clear of them. And <a href="https://blogs.windows.com/msedgedev/2020/09/14/introducing-dual-screen-foldable-web-apis/">dual screen or foldable devices</a> make us rethink how to best use available space in a number of different <a href="https://w3c.github.io/device-posture/">device postures</a>.</p>
<figure class="wp-block-image size-large"><img decoding="async" loading="lazy" width="960" height="452" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig1.png?resize=960%2C452&ssl=1" alt="" class="wp-image-7173226" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig1.png?resize=1024%2C482&ssl=1 1024w, https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig1.png?resize=300%2C141&ssl=1 300w, https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig1.png?resize=768%2C362&ssl=1 768w, https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig1.png?resize=1536%2C724&ssl=1 1536w, https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig1.png?resize=2048%2C965&ssl=1 2048w, https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig1.png?w=1920&ssl=1 1920w, https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig1.png?w=2880&ssl=1 2880w" sizes="(max-width: 960px) 100vw, 960px" data-recalc-dims="1" referrerpolicy="no-referrer"><figcaption><em>Sketches of a round display, a common rectangular mobile display, and a device with a foldable display.</em></figcaption></figure>
<p>These recent evolutions of the web platform made it both more challenging and more interesting to design products. They’re great opportunities for us to break out of our rectangular boxes.</p>
<p>I’d like to talk about a new feature similar to the above: the Window Controls Overlay for Progressive Web Apps (PWAs).</p>
<p><a href="https://alistapart.com/article/yes-that-web-project-should-be-a-pwa/">Progressive Web Apps</a> are blurring the lines between apps and websites. They combine the best of both worlds. On one hand, they’re stable, linkable, searchable, and responsive just like websites. On the other hand, they provide additional powerful capabilities, work offline, and read files just like native apps.</p>
<p>As a design surface, PWAs are really interesting because they challenge us to think about what mixing web and device-native user interfaces can be. On desktop devices in particular, we have more than <a href="https://en.wikipedia.org/wiki/History_of_the_graphical_user_interface">40 years of history</a> telling us what applications should look like, and it can be hard to break out of this mental model.</p>
<p>At the end of the day though, PWAs on desktop are constrained to the window they appear in: a rectangle with a title bar at the top.</p>
<p>Here’s what a typical desktop PWA app looks like:</p>
<figure class="wp-block-image size-large"><img decoding="async" loading="lazy" width="960" height="303" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig2.png?resize=960%2C303&ssl=1" alt="" class="wp-image-7173227" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig2.png?resize=1024%2C323&ssl=1 1024w, https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig2.png?resize=300%2C95&ssl=1 300w, https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig2.png?resize=768%2C242&ssl=1 768w, https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig2.png?resize=1536%2C485&ssl=1 1536w, https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig2.png?resize=2048%2C646&ssl=1 2048w, https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig2.png?w=1920&ssl=1 1920w, https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig2.png?w=2880&ssl=1 2880w" sizes="(max-width: 960px) 100vw, 960px" data-recalc-dims="1" referrerpolicy="no-referrer"><figcaption><em>Sketches of two rectangular user interfaces representing the desktop Progressive Web App status quo on the macOS and Windows operating systems, respectively. </em></figcaption></figure>
<p>Sure, as the author of a PWA, you get to choose the color of the title bar (using the Web Application Manifest <a href="https://developer.mozilla.org/en-US/docs/Web/Manifest/theme_color"><strong>theme_color</strong></a> property), but that’s about it.</p>
<p>What if we could think outside this box, and reclaim the real estate of the app’s entire window? Doing so would give us a chance to make our apps more beautiful and feel more integrated in the operating system.</p>
<p>This is exactly what the <a href="https://web.dev/window-controls-overlay/">Window Controls Overlay</a> offers. This new PWA functionality makes it possible to take advantage of the full surface area of the app, including where the title bar normally appears.</p>
<h2 class="wp-block-heading">About the title bar and window controls</h2>
<p>Let’s start with an explanation of what the title bar and window controls are.</p>
<p>The <em>title bar</em> is the area displayed at the top of an app window, which usually contains the app’s name. <em>Window controls</em> are the affordances, or buttons, that make it possible to minimize, maximize, or close the app’s window, and are also displayed at the top.</p>
<figure class="wp-block-image"><img decoding="async" src="https://lh5.googleusercontent.com/1s_lFmqiRm6Fv3wzeELtsDDiDvOkEJuSRH5K9YIrZZ8rh8rYCxUqbSnfd-f7YrsRvcDzF67fexnEJFlDtw53SKKmOgVk8sv_VUyCQveoR18HkNgACPxcQTtEOb6SmEuRIDlX3EcI" alt="" referrerpolicy="no-referrer"><figcaption><em>A sketch of a rectangular application user interface highlighting the title bar area and window control buttons.</em></figcaption></figure>
<p>Window Controls Overlay removes the physical constraint of the title bar and window controls areas. It frees up the full height of the app window, enabling the title bar and window control buttons to be overlaid on top of the application’s web content. </p>
<figure class="wp-block-image"><img decoding="async" src="https://lh6.googleusercontent.com/k_6o1fBePhbjvtmxoTW3tG1134Gvbo31r2fy7zxmOB39d_eKpjThbh7QL8pVXrA1aLvEWzkoJ_rY4af451BU9XyKZXbSouCTvDJMnRKGlcOhcEpXw_rjQAR8_SFjhrm_-22OxKiR" alt="" referrerpolicy="no-referrer"><figcaption><em>A sketch of a rectangular application user interface using Window Controls Overlay. The title bar and window controls are no longer in an area separated from the app’s content.</em></figcaption></figure>
<p>If you are reading this article on a desktop computer, take a quick look at other apps. Chances are they’re already doing something similar to this. In fact, the very web browser you are using to read this uses the top area to display tabs.</p>
<figure class="wp-block-image"><img decoding="async" src="https://lh6.googleusercontent.com/BLL9Rc5othPsw6xYApyyNOZ73j32wi4XkyoZpl4QOv0OL4MnxMe3bl1xLR0O7WSoAvi3KhyeP83hUh4-EezTmGg2axN4RiOVtgiF5ZiapcjUL6gtLqExZOHGCtkOBbthMTgh5Tmr" alt="" referrerpolicy="no-referrer"><figcaption><em>A screenshot of the top area of a browser’s user interface showing a group of tabs that share the same horizontal space as the app window controls.</em></figcaption></figure>
<p>Spotify displays album artwork all the way to the top edge of the application window.</p>
<figure class="wp-block-image"><img decoding="async" src="https://lh3.googleusercontent.com/SzPcq94_7Yu_ARf13Z6dRD0tpdlPM_MdrY-7CA_mv4Yu3fBIL3pJnXirP83cCDVoQxnnIEwDoBwbGzfftHmZ3PZZUfsw_oP-m4QLkB2SqekX8JupR9_xmI0tG1q65IfNFbnXIHUh" alt="" referrerpolicy="no-referrer"><figcaption><em>A screenshot of an album in Spotify’s desktop application. Album artwork spans the entire width of the main content area, all the way to the top and right edges of the window, and the right edge of the main navigation area on the left side. The application and album navigation controls are overlaid directly on top of the album artwork.</em></figcaption></figure>
<p>Microsoft Word uses the available title bar space to display the auto-save and search functionalities, and more.</p>
<figure class="wp-block-image"><img decoding="async" src="https://lh6.googleusercontent.com/VdREVwFFjYHxHF0Gg3l079hxsa8WKPEWiuvuL7cWbGnEDJ2yc3JiOWQK5lUyaeEgzpd1Przji0cNLeooPD7riPKbcMixa6IkXanprdqPJVkQrYSerxSaNmzbJPd1YsA55mlYd9xt" alt="" referrerpolicy="no-referrer"><figcaption><em>A screenshot of Microsoft Word’s toolbar interface. Document file information, search, and other functionality appear at the top of the window, sharing the same horizontal space as the app’s window controls.</em></figcaption></figure>
<p>The whole point of this feature is to allow you to make use of this space with your own content while providing a way to account for the window control buttons. And it enables you to offer this modified experience on a range of platforms while not adversely affecting the experience on browsers or devices that don’t support Window Controls Overlay. After all, PWAs are all about <a href="https://alistapart.com/article/understandingprogressiveenhancement/">progressive enhancement</a>, so this feature is a chance to enhance your app to use this extra space when it’s available.</p>
<h2 class="wp-block-heading">Let’s use the feature</h2>
<p>For the rest of this article, we’ll be working on a demo app to learn more about using the feature.</p>
<p>The demo app is called <a href="https://stupefied-edison-a4ee55.netlify.app/">1DIV</a>. It’s a simple CSS playground where users can create designs using CSS and a single HTML element.</p>
<p>The app has two pages. The first lists the existing CSS designs you’ve created:</p>
<figure class="wp-block-image"><img decoding="async" src="https://lh3.googleusercontent.com/pW2iUTucKfwMJZrAlPGK19vVnEPaHjYT4N-18P-vm9qkhAdGJcRBMexOCu1q9nN9BAfZ7MH6itNP__kY4HPl9uVPucXkbmSX-E9g6AdVAI_uu6TyEsEdH0LUCXdN1f4kqZNgDr30" alt="" referrerpolicy="no-referrer"><figcaption><em>A screenshot of the 1DIV app displaying a thumbnail grid of CSS designs a user created.</em></figcaption></figure>
<p>The second page enables you to create and edit CSS designs:</p>
<figure class="wp-block-image"><img decoding="async" src="https://lh5.googleusercontent.com/faaJ3uHbzXg-TFinvOqR_7gyjVPvlk7fuVWuN4aIH6IUxXNAp4GXtIcuVPpo6bd1IOKO1_EMDt4pUgErUh_X2_2r3WnkQ4PzovPp6Zjg0l98W9NBrHA0xAuTNf0uNVBatRsMJzEm" alt="" referrerpolicy="no-referrer"><figcaption><em>A screenshot of the 1DIV app editor page. The top half of the window displays a rendered CSS design, and a text editor on the bottom half of the window displays the CSS used to create it.</em></figcaption></figure>
<p>Since I’ve added a simple web manifest and service worker, we can install the app as a PWA on desktop. Here is what it looks like on macOS:</p>
<figure class="wp-block-image size-large"><img decoding="async" loading="lazy" width="960" height="466" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig10.png?resize=960%2C466&ssl=1" alt="" class="wp-image-7173228" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig10.png?resize=1024%2C497&ssl=1 1024w, https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig10.png?resize=300%2C146&ssl=1 300w, https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig10.png?resize=768%2C373&ssl=1 768w, https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig10.png?resize=1536%2C745&ssl=1 1536w, https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig10.png?w=1540&ssl=1 1540w" sizes="(max-width: 960px) 100vw, 960px" data-recalc-dims="1" referrerpolicy="no-referrer"><figcaption><em>Screenshots of the 1DIV app thumbnail view and CSS editor view on macOS. This version of the app’s window has a separate control bar at the top for the app name and window control buttons.</em></figcaption></figure>
<p>And on Windows:</p>
<figure class="wp-block-image size-large"><img decoding="async" loading="lazy" width="960" height="501" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig11.png?resize=960%2C501&ssl=1" alt="" class="wp-image-7173229" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig11.png?resize=1024%2C534&ssl=1 1024w, https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig11.png?resize=300%2C157&ssl=1 300w, https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig11.png?resize=768%2C401&ssl=1 768w, https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig11.png?resize=1536%2C802&ssl=1 1536w, https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig11.png?resize=2048%2C1069&ssl=1 2048w, https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig11.png?w=1920&ssl=1 1920w" sizes="(max-width: 960px) 100vw, 960px" data-recalc-dims="1" referrerpolicy="no-referrer"><figcaption><em>Screenshots of the 1DIV app thumbnail view and CSS editor view on the Windows operating system. This version of the app’s window also has a separate control bar at the top for the app name and window control buttons.</em></figcaption></figure>
<p>Our app is looking good, but the white title bar in the first page is wasted space. In the second page, it would be really nice if the design area went all the way to the top of the app window.</p>
<p>Let’s use the Window Controls Overlay feature to improve this.</p>
<h2 class="wp-block-heading">Enabling Window Controls Overlay</h2>
<p>The feature is still experimental at the moment. To try it, you need to enable it in one of the supported browsers.</p>
<p>As of now, it has been implemented in Chromium, as a collaboration between Microsoft and Google. We can therefore use it in Chrome or Edge by going to the internal <strong>about://flags</strong> page, and enabling the <strong>Desktop PWA Window Controls Overlay</strong> flag.</p>
<h2 class="wp-block-heading">Using Window Controls Overlay</h2>
<p>To use the feature, we need to add the following <strong>display_override</strong> member to our web app’s manifest file:</p>
<pre><code class="language-javascript">{
"name": "1DIV",
"description": "1DIV is a mini CSS playground",
"lang": "en-US",
"start_url": "/",
"theme_color": "#ffffff",
"background_color": "#ffffff",
"display_override": [
"window-controls-overlay"
],
"icons": [
...
]
}
</code></pre>
<p>On the surface, the feature is really simple to use. This manifest change is the only thing we need to make the title bar disappear and turn the window controls into an overlay.</p>
<p>However, to provide a great experience for all users regardless of what device or browser they use, and to make the most of the title bar area in our design, we’ll need a bit of CSS and JavaScript code.</p>
<p>Here is what the app looks like now:</p>
<figure class="wp-block-image"><img decoding="async" src="https://lh6.googleusercontent.com/YbSJ4vMtrc88Jr8sh7F8uWED-9OVFvLkXNT3xVP9gdmQt9XwC-wGHPmaspcKnfSpPMjSotYzRISGPag1Ugq3mxWTslaVhPK9iP8IHLjFnE_FcIkM0y3olJ4Gzw5ejrZFTRbz9avF" alt="" referrerpolicy="no-referrer"><figcaption><em>Screenshot of the 1DIV app thumbnail view using Window Controls Overlay on macOS. The separate top bar area is gone, but the window controls are now blocking some of the app’s interface</em></figcaption></figure>
<p>The title bar is gone, which is what we wanted, but our logo, search field, and <strong>NEW</strong> button are partially covered by the window controls because now our layout starts at the top of the window.</p>
<p>It’s similar on Windows, with the difference that the close, maximize, and minimize buttons appear on the right side, grouped together with the PWA control buttons:</p>
<figure class="wp-block-image"><img decoding="async" src="https://lh6.googleusercontent.com/ytqSauTsKKNI6N7YzxlIqhNatK7LwaPw6yY74jq2egOsBIHbzl2vFGPMRK6dqx6tE-UqSCCWS8f1YftsXZygxEB6KALUYfGU9XW4poE1NPpjYKV66bk1k6dy91rh6TMZ1qb3Rph-" alt="" referrerpolicy="no-referrer"><figcaption><em>Screenshot of the 1DIV app thumbnail display using Window Controls Overlay on the Windows operating system. The separate top bar area is gone, but the window controls are now blocking some of the app’s content.</em></figcaption></figure>
<h2 class="wp-block-heading">Using CSS to keep clear of the window controls</h2>
<p>Along with the feature, new CSS environment variables have been introduced:</p>
<ul><li><strong><code>titlebar-area-x</code></strong></li><li><code><strong>titlebar-area-y</strong></code></li><li><code><strong>titlebar-area-width</strong></code></li><li><strong><code>titlebar-area-height</code></strong></li></ul>
<p>You use these variables with the CSS <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/env()"><strong>env()</strong></a> function to position your content where the title bar would have been while ensuring it won’t overlap with the window controls. In our case, we’ll use two of the variables to position our header, which contains the logo, search bar, and <strong>NEW</strong> button. </p>
<pre><code class="language-css">header {
position: absolute;
left: env(titlebar-area-x, 0);
width: env(titlebar-area-width, 100%);
height: var(--toolbar-height);
}
</code></pre>
<p>The <code><strong>titlebar-area-x</strong> </code>variable gives us the distance from the left of the viewport to where the title bar would appear, and <strong><code>titlebar-area-width</code></strong> is its width. (Remember, this is not equivalent to the width of the entire viewport, just the title bar portion, which as noted earlier, doesn’t include the window controls.)</p>
<p>By doing this, we make sure our content remains fully visible. We’re also defining fallback values (the second parameter in the <strong><code>env()</code></strong> function) for when the variables are not defined (such as on non-supporting browsers, or when the Windows Control Overlay feature is disabled).</p>
<figure class="wp-block-image"><img decoding="async" src="https://lh5.googleusercontent.com/AYZ7D2ZqvPLip8FtF6IzI6XSAEoajjviCG5fo40_ynrksUesFQBjZVEN6dsTOA8F9CCqXbFWb32ZYUN73hEAkMlyzKnX_1Qzjy7kR6jl42TyyJOeg1FWK7A9WeWn-_7SD57-EOdt" alt="" referrerpolicy="no-referrer"><figcaption><em>Screenshot of the 1DIV app thumbnail view on macOS with Window Controls Overlay and our CSS updated. The app content that the window controls had been blocking has been repositioned.</em></figcaption></figure>
<figure class="wp-block-image"><img decoding="async" src="https://lh6.googleusercontent.com/ZxtH5F4v2io8ntHHi8V0YhqgBc_GD5pcq4g52zZy4_bEhbtjC3G7WdyZqQmwc6-D_NIp7Z8dvjsG8qz42DIg7RDhC6HbPHThXEFsknbOgcEfkF7d_cqx45T9vTi6z23pVe0-1nxA" alt="" referrerpolicy="no-referrer"><figcaption><em>Screenshot of the 1DIV app thumbnail view on the Windows operating system with Window Controls Overlay and our updated CSS. The app content that the window controls had been blocking has been repositioned.</em></figcaption></figure>
<p>Now our header adapts to its surroundings, and it doesn’t feel like the window control buttons have been added as an afterthought. The app looks a lot more like a native app.</p>
<h2 class="wp-block-heading">Changing the window controls background color so it blends in</h2>
<p>Now let’s take a closer look at our second page: the CSS playground editor.</p>
<figure class="wp-block-image size-large"><img decoding="async" loading="lazy" width="960" height="486" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig16.png?resize=960%2C486&ssl=1" alt="" class="wp-image-7173230" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig16.png?resize=1024%2C518&ssl=1 1024w, https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig16.png?resize=300%2C152&ssl=1 300w, https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig16.png?resize=768%2C389&ssl=1 768w, https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig16.png?resize=1536%2C777&ssl=1 1536w, https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig16.png?resize=2048%2C1037&ssl=1 2048w, https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig16.png?w=1920&ssl=1 1920w" sizes="(max-width: 960px) 100vw, 960px" data-recalc-dims="1" referrerpolicy="no-referrer"><figcaption><em>Screenshots of the 1DIV app CSS editor view with Window Controls Overlay in macOS and Windows, respectively. The window controls overlay areas have a solid white background color, which contrasts with the hot pink color of the example CSS design displayed in the editor.</em></figcaption></figure>
<p>Not great. Our CSS demo area does go all the way to the top, which is what we wanted, but the way the window controls appear as white rectangles on top of it is quite jarring.</p>
<p>We can fix this by changing the app’s theme color. There are a couple of ways to define it:</p>
<ul><li>PWAs can define a theme color in the web app manifest file using the <a href="https://developer.mozilla.org/en-US/docs/Web/Manifest/theme_color"><strong>theme_color</strong></a> manifest member. This color is then used by the OS in different ways. On desktop platforms, it is used to provide a background color to the title bar and window controls.</li><li>Websites can use the <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta/name/theme-color"><strong>theme-color</strong> meta tag</a> as well. It’s used by browsers to customize the color of the UI around the web page. For PWAs, this color can override the manifest <strong><code>theme_color</code></strong>.</li></ul>
<p>In our case, we can set the manifest <strong><code>theme_color</code></strong> to white to provide the right default color for our app. The OS will read this color value when the app is installed and use it to make the window controls background color white. This color works great for our main page with the list of demos.</p>
<p>The <strong><code>theme-color</code></strong> meta tag can be changed at runtime, using JavaScript. So we can do that to override the white with the right demo background color when one is opened.</p>
<p>Here is the function we’ll use:</p>
<pre><code class="language-javascript">function themeWindow(bgColor) {
document.querySelector("meta[name=theme-color]").setAttribute('content', bgColor);
}</code></pre>
<p>With this in place, we can imagine how using color and CSS transitions can produce a smooth change from the list page to the demo page, and enable the window control buttons to blend in with the rest of the app’s interface.</p>
<figure class="wp-block-image"><img decoding="async" src="https://lh4.googleusercontent.com/YVYktaP8CkIQJFlCtWlwVU4dequS4MutbDJfm-vS8kGx_nedIgzziuHeZICeJ-vsu33VR0rydqwKH0JVIFKjWjlrvbWPYssNvxr7rBsCKKdag7PHMhA_NLV3w0nzBuBzurk1fr1i" alt="" referrerpolicy="no-referrer"><figcaption><em>Screenshot of the 1DIV app CSS editor view on the Windows operating system with Window Controls Overlay and updated CSS demonstrating how the window control buttons blend in with the rest of the app’s interface.</em></figcaption></figure>
<h2 class="wp-block-heading">Dragging the window</h2>
<p>Now, getting rid of the title bar entirely does have an important accessibility consequence: it’s much more difficult to move the application window around.</p>
<p>The title bar provides a sizable area for users to click and drag, but by using the Window Controls Overlay feature, this area becomes limited to where the control buttons are, and users have to very precisely aim between these buttons to move the window.</p>
<p>Fortunately, this can be fixed using CSS with the <strong><code>app-region</code></strong> property. This property is, for now, only supported in Chromium-based browsers and needs the <strong><code>-webkit-</code></strong> vendor prefix. </p>
<p>To make any element of the app become a dragging target for the window, we can use the following: </p>
<p><strong><code>-webkit-app-region: drag;</code></strong></p>
<p>It is also possible to explicitly make an element non-draggable: </p>
<p><code>-<strong>webkit-app-region: no-drag;</strong> </code></p>
<p>These options can be useful for us. We can make the entire header a dragging target, but make the search field and <strong>NEW</strong> button within it non-draggable so they can still be used as normal.</p>
<p>However, because the editor page doesn’t display the header, users wouldn’t be able to drag the window while editing code. So let’s use a different approach. We’ll create another element before our header, also absolutely positioned, and dedicated to dragging the window.</p>
<pre><code class="language-markup"><div class="drag"></div>
<header>...</header></code></pre>
<pre><code class="language-css">.drag {
position: absolute;
top: 0;
width: 100%;
height: env(titlebar-area-height, 0);
-webkit-app-region: drag;
}</code></pre>
<p>With the above code, we’re making the draggable area span the entire viewport width, and using the <strong><code>titlebar-area-height</code></strong> variable to make it as tall as what the title bar would have been. This way, our draggable area is aligned with the window control buttons as shown below.</p>
<p>And, now, to make sure our search field and button remain usable:</p>
<pre><code class="language-css">header .search,
header .new {
-webkit-app-region: no-drag;
}</code></pre>
<p>With the above code, users can click and drag where the title bar used to be. It is an area that users expect to be able to use to move windows on desktop, and we’re not breaking this expectation, which is good.</p>
<figure class="wp-block-image"><img decoding="async" src="https://lh5.googleusercontent.com/sU0QjlT2R7SrF91GI--WcdHRy0shD7CfnKpfzvgXGz5VptZY6hyoDX_SYFqxFG85dxMgbLidjb8cwJOcnqzd4OAWeNjIVgSiKpaz68orEZEU7DgKHHLkM3NXU5rkALkpUrEl7Pp_" alt="" referrerpolicy="no-referrer"><figcaption><em>An animated view of the 1DIV app being dragged across a Windows desktop with the mouse.</em></figcaption></figure>
<h2 class="wp-block-heading">Adapting to window resize</h2>
<p>It may be useful for an app to know both whether the window controls overlay is visible and when its size changes. In our case, if the user made the window very narrow, there wouldn’t be enough space for the search field, logo, and button to fit, so we’d want to push them down a bit.</p>
<p>The Window Controls Overlay feature comes with a JavaScript API we can use to do this: <strong><code>navigator.windowControlsOverlay</code></strong>.</p>
<p>The API provides three interesting things:</p>
<ul><li><strong><code>navigator.windowControlsOverlay.visible</code></strong> lets us know whether the overlay is visible.</li><li><strong><code>navigator.windowControlsOverlay.getBoundingClientRect()</code></strong> lets us know the position and size of the title bar area.</li><li><strong><code>navigator.windowControlsOverlay.ongeometrychange</code></strong> lets us know when the size or visibility changes.</li></ul>
<p>Let’s use this to be aware of the size of the title bar area and move the header down if it’s too narrow.</p>
<pre><code class="language-javascript">if (navigator.windowControlsOverlay) {
navigator.windowControlsOverlay.addEventListener('geometrychange', () => {
const { width } = navigator.windowControlsOverlay.getBoundingClientRect();
document.body.classList.toggle('narrow', width < 250);
});
}</code></pre>
<p>In the example above, we set the <strong><code>narrow</code></strong> class on the <strong><code>body</code></strong> of the app if the title bar area is narrower than 250px. We could do something similar with a media query, but using the <strong><code>windowControlsOverlay</code></strong> API has two advantages for our use case:</p>
<ul><li>It’s only fired when the feature is supported and used; we don’t want to adapt the design otherwise.</li><li>We get the size of the title bar area across operating systems, which is great because the size of the window controls is different on Mac and Windows. Using a media query wouldn’t make it possible for us to know exactly how much space remains.</li></ul>
<pre><code class="language-css">.narrow header {
top: env(titlebar-area-height, 0);
left: 0;
width: 100%;
}</code></pre>
<p>Using the above CSS code, we can move our header down to stay clear of the window control buttons when the window is too narrow, and move the thumbnails down accordingly.</p>
<figure class="wp-block-image"><img decoding="async" src="https://lh5.googleusercontent.com/e4oVs-No9pSWdYyfqTJ0QKcKrDzlv11bsoTwSVvFBhi1bUo9dP2ub71MlWa90QLEFUc5C9e81mQtg3xwGpB5Kkfvu1dNqdBVhqetz74N_0TSWh7_RfZ5NkDNJEuhv5_ZVvw-vpDG" alt="" referrerpolicy="no-referrer"><figcaption><em>A screenshot of the 1DIV app on Windows showing the app’s content adjusted for a much narrower viewport.</em></figcaption></figure>
<h2 class="wp-block-heading">Thirty pixels of exciting design opportunities</h2>
<p><br>Using the Window Controls Overlay feature, we were able to take our simple demo app and turn it into something that feels so much more integrated on desktop devices. Something that reaches out of the usual window constraints and provides a custom experience for its users.</p>
<p>In reality, this feature only gives us about 30 pixels of extra room and comes with challenges on how to deal with the window controls. And yet, this extra room and those challenges can be turned into exciting design opportunities.</p>
<p>More devices of all shapes and forms get invented all the time, and the web keeps on evolving to adapt to them. New features get added to the web platform to allow us, web authors, to integrate more and more deeply with those devices. From watches or foldable devices to desktop computers, we need to evolve our design approach for the web. Building for the web now lets us think outside the rectangular box.</p>
<p>So let’s embrace this. Let’s use the standard technologies already at our disposal, and experiment with new ideas to provide tailored experiences for all devices, all from a single codebase!</p>
<p><br>If you get a chance to try the Window Controls Overlay feature and have feedback about it, you can <a href="https://github.com/WICG/window-controls-overlay/issues">open issues on the spec’s repository</a>. It’s still early in the development of this feature, and you can help make it even better. Or, you can take a look at the <a href="https://docs.microsoft.com/en-us/microsoft-edge/progressive-web-apps-chromium/how-to/window-controls-overlay">feature’s existing documentation</a>, or this <a href="https://stupefied-edison-a4ee55.netlify.app/">demo app</a> and its <a href="https://github.com/captainbrosset/1DIV">source code</a>. </p>
]]></description>
<pubDate>Thu, 09 Dec 2021 15:00:00 GMT</pubDate>
<guid isPermaLink="false">https://alistapart.com/article/breaking-out-of-the-box/</guid>
<link>https://alistapart.com/article/breaking-out-of-the-box/</link>
<author><![CDATA[patrick-brosset]]></author>
<category>Code</category>
<category>CSS</category>
</item>
<item>
<title><![CDATA[Human-Readable JavaScript: A Tale of Two Experts]]></title>
<description><![CDATA[<p>Everyone wants to be an expert. But what does that even mean? Over the years I’ve seen two types of people who are referred to as “experts.” Expert 1 is someone who knows every tool in the language and makes sure to use every bit of it, whether it helps or not. Expert 2 also knows every piece of syntax, but they’re pickier about what they employ to solve problems, considering a number of factors, both code-related and not. </p>
<p>Can you take a guess at which expert we want working on our team? If you said Expert 2, you’d be right. They’re a developer focused on delivering readable code—lines of JavaScript others can understand and maintain. Someone who can make the complex simple. But “readable” is rarely definitive—in fact, it’s largely based on the eyes of the beholder. So where does that leave us? What should experts aim for when writing readable code? Are there clear right and wrong choices? The answer is, it depends.</p>
<h2 class="wp-block-heading">The obvious choice</h2>
<p>In order to improve developer experience, TC39 has been adding lots of new features to ECMAScript in recent years, including many proven patterns borrowed from other languages. One such addition, added in ES2019, is <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/flat"><code>Array.prototype.flat()</code></a> It takes an argument of depth or <code>Infinity</code>, and flattens an array. If no argument is given, the depth defaults to 1.</p>
<p>Prior to this addition, we needed the following syntax to flatten an array to a single level.</p>
<pre><code class="language-javascript">let arr = [1, 2, [3, 4]];
[].concat.apply([], arr);
// [1, 2, 3, 4]</code></pre>
<p>When we added <code>flat()</code>, that same functionality could be expressed using a single, descriptive function.</p>
<pre><code class="language-javascript">arr.flat();
// [1, 2, 3, 4]</code></pre>
<p>Is the second line of code more readable? The answer is emphatically yes. In fact, both experts would agree.</p>
<p>Not every developer is going to be aware that <code>flat()</code> exists. But they don’t need to because <code>flat()</code> is a descriptive verb that conveys the meaning of what is happening. It’s a lot more intuitive than <code>concat.apply()</code>.</p>
<p>This is the rare case where there is a definitive answer to the question of whether new syntax is better than old. Both experts, each of whom is familiar with the two syntax options, will choose the second. They’ll choose the shorter, clearer, more easily maintained line of code.</p>
<p>But choices and trade-offs aren’t always so decisive.</p>
<h2 class="wp-block-heading">The gut check</h2>
<p>The wonder of JavaScript is that it’s incredibly versatile. There is a reason it’s all over the web. Whether you think that’s a good or <a href="https://alistapart.com/article/responsible-javascript-part-1/">bad</a> thing is another story.</p>
<p>But with that versatility comes the paradox of choice. You can write the same code in many different ways. How do you determine which way is “right”? You can’t even begin to make a decision unless you understand the available options and their limitations.</p>
<p>Let’s use functional programming with <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map"><code>map()</code></a> as the example. I’ll walk through various iterations that all yield the same result.</p>
<p>This is the tersest version of our <code>map()</code> examples. It uses the fewest characters, all fit into one line. This is our baseline.</p>
<pre><code class="language-javascript">const arr = [1, 2, 3];
let multipliedByTwo = arr.map(el => el * 2);
// multipliedByTwo is [2, 4, 6]</code></pre>
<p>This next example adds only two characters: parentheses. Is anything lost? How about gained? Does it make a difference that a function with more than one parameter will always need to use the parentheses? I’d argue that it does. There is little to no detriment in adding them here, and it improves consistency when you inevitably write a function with multiple parameters. In fact, when I wrote this, <a href="https://prettier.io/">Prettier</a> enforced that constraint; it didn’t want me to create an arrow function without the parentheses.</p>
<pre><code class="language-javascript">let multipliedByTwo = arr.map((el) => el * 2);</code></pre>
<p>Let’s take it a step further. We’ve added curly braces and a return. Now this is starting to look more like a traditional function definition. Right now, it may seem like overkill to have a keyword as long as the function logic. Yet, if the function is more than one line, this extra syntax is again required. Do we presume that we will not have any other functions that go beyond a single line? That seems dubious.</p>
<pre><code class="language-javascript">let multipliedByTwo = arr.map((el) => {
return el * 2;
});</code></pre>
<p>Next we’ve removed the arrow function altogether. We’re using the same syntax as before, but we’ve swapped out for the <code>function</code> keyword. This is interesting because there is no scenario in which this syntax won’t work; no number of parameters or lines will cause problems, so consistency is on our side. It’s more verbose than our initial definition, but is that a bad thing? How does this hit a new coder, or someone who is well versed in something other than JavaScript? Is someone who knows JavaScript well going to be frustrated by this syntax in comparison?</p>
<pre><code class="language-javascript">let multipliedByTwo = arr.map(function(el) {
return el * 2;
});</code></pre>
<p>Finally we get to the last option: passing just the function. And <code>timesTwo</code> can be written using any syntax we like. Again, there is no scenario in which passing the function name causes a problem. But step back for a moment and think about whether or not this could be confusing. If you’re new to this codebase, is it clear that <code>timesTwo</code> is a function and not an object? Sure, <code>map()</code> is there to give you a hint, but it’s not unreasonable to miss that detail. How about the location of where <code>timesTwo</code> is declared and initialized? Is it easy to find? Is it clear what it’s doing and how it’s affecting this result? All of these are important considerations.</p>
<pre><code class="language-javascript">const timesTwo = (el) => el * 2;
let multipliedByTwo = arr.map(timesTwo);</code></pre>
<p>As you can see, there is no obvious answer here. But making the right choice for your codebase means understanding all the options and their limitations. And knowing that consistency requires parentheses and curly braces and <code>return</code> keywords.</p>
<p>There are a number of questions you have to ask yourself when writing code. Questions of <a href="https://alistapart.com/article/responsible-javascript-part-2/#section9">performance</a> are typically the most common. But when you’re looking at code that is functionally identical, your determination should be based on humans—how humans consume code.</p>
<h2 class="wp-block-heading">Maybe newer isn’t always better</h2>
<p>So far we’ve found a clear-cut example of where both experts would reach for the newest syntax, even if it’s not universally known. We’ve also looked at an example that poses a lot of questions but not as many answers.</p>
<p>Now it’s time to dive into code that I’ve written before…and removed. This is code that made me the first expert, using a little-known piece of syntax to solve a problem to the detriment of my colleagues and the maintainability of our codebase.</p>
<p><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment">Destructuring assignment</a> lets you unpack values from objects (or arrays). It typically looks something like this.</p>
<pre><code class="language-javascript">const {node} = exampleObject;</code></pre>
<p>It initializes a variable and assigns it a value all in one line. But it doesn’t have to.</p>
<pre><code class="language-javascript">let node
;({node} = exampleObject)</code></pre>
<p>The last line of code assigns a variable to a value using destructuring, but the variable declaration takes place one line before it. It’s not an uncommon thing to want to do, but many people don’t realize you can do it.</p>
<p>But look at that code closely. It forces an awkward semicolon for code that doesn’t use semicolons to terminate lines. It wraps the command in parentheses and adds the curly braces; it’s entirely unclear what this is doing. It’s not easy to read, and, as an expert, it shouldn’t be in code that I write.</p>
<pre><code class="language-javascript">let node
node = exampleObject.node</code></pre>
<p>This code solves the problem. It works, it’s clear what it does, and my colleagues will understand it without having to look it up. With the destructuring syntax, just because I <em>can</em> doesn’t mean I <em>should</em>.</p>
<h2 class="wp-block-heading">Code isn’t everything</h2>
<p>As we’ve seen, the Expert 2 solution is rarely obvious based on code alone; yet there are still clear distinctions between which code each expert would write. That’s because code is for machines to read and humans to interpret. So there are non-code factors to consider!</p>
<p>The syntax choices you make for a team of JavaScript developers is different than those you should make for a team of polyglots who aren’t steeped in the minutiae. </p>
<p>Let’s take spread vs. <code>concat()</code> as an example.</p>
<p>Spread was added to ECMAScript a few years ago, and it’s enjoyed wide adoption. It’s sort of a utility syntax in that it can do a lot of different things. One of them is concatenating a number of arrays.</p>
<pre><code class="language-javascript">const arr1 = [1, 2, 3];
const arr2 = [9, 11, 13];
const nums = [...arr1, ...arr2];</code></pre>
<p>As powerful as spread is, it isn’t a very intuitive symbol. So unless you already know what it does, it’s not super helpful. While both experts <em>may</em> safely assume a team of JavaScript specialists are familiar with this syntax, Expert 2 will probably question whether that’s true of a team of polyglot programmers. Instead, Expert 2 may select the <code>concat()</code> method instead, as it’s a descriptive verb that you can probably understand from the context of the code.</p>
<p>This code snippet gives us the same nums result as the spread example above.</p>
<pre><code class="language-javascript">const arr1 = [1, 2, 3];
const arr2 = [9, 11, 13];
const nums = arr1.concat(arr2);</code></pre>
<p>And that’s but one example of how human factors influence code choices. A codebase that’s touched by a lot of different teams, for example, may have to hold more stringent standards that don’t necessarily keep up with the latest and greatest syntax. Then you move beyond the main source code and consider other factors in your tooling chain that make life easier, or harder, for the humans who work on that code. There is code that can be structured in a way that’s <a href="https://www.freecodecamp.org/news/how-to-write-testable-code/">hostile to testing</a>. There is code that backs you into a corner for <a href="https://www.codeproject.com/Articles/701862/How-Not-to-Back-Yourself-into-a-Corner">future scaling or feature addition</a>. There is code that’s <a href="https://laurieontech.com/posts/performance-diagnosis/">less performant</a>, doesn’t <a href="https://alistapart.com/article/fromswitchestotargets/">handle different browsers</a>, or <a href="https://a11y.coffee/">isn’t accessible</a>. All of these factor into the recommendations Expert 2 makes.</p>
<p>Expert 2 also considers the impact of naming. But let’s be honest, even <em>they</em> can’t get that right most of the time.</p>
<h2 class="wp-block-heading">Conclusion</h2>
<p>Experts don’t prove themselves by using every piece of the spec; they prove themselves by knowing the spec well enough to deploy syntax judiciously and make well-reasoned decisions. This is how experts become multipliers—how they make new experts.</p>
<p>So what does this mean for those of us who consider ourselves experts or aspiring experts? It means that writing code involves asking yourself a lot of questions. It means considering your developer audience in a real way. The best code you can write is code that accomplishes something complex, but is inherently understood by those who examine your codebase.</p>
<p>And no, it’s not easy. And there often isn’t a clear-cut answer. But it’s something you should consider with every function you write.</p>
]]></description>
<pubDate>Thu, 25 Mar 2021 14:00:00 GMT</pubDate>
<guid isPermaLink="false">https://alistapart.com/article/human-readable-javascript/</guid>
<link>https://alistapart.com/article/human-readable-javascript/</link>
<author><![CDATA[laurie-barth]]></author>
<category>Code</category>
<category>JavaScript</category>
</item>
<item>
<title><![CDATA[Now THAT’S What I Call Service Worker!]]></title>
<description><![CDATA[<p>The <a href="https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API">Service Worker</a> API is the <a href="https://en.wikipedia.org/wiki/Dremel">Dremel</a> of the web platform. It offers incredibly broad utility while also yielding resiliency and better performance. If you’ve not used Service Worker yet—and you couldn’t be blamed if so, as <a href="https://almanac.httparchive.org/en/2020/pwa#service-workers">it hasn’t seen wide adoption as of 2020</a>—it goes something like this:</p>
<ol><li>On the initial visit to a website, the browser <a href="https://developers.google.com/web/fundamentals/primers/service-workers#register_a_service_worker">registers</a> what amounts to a client-side proxy powered by <a href="https://www.weeklytimber.com/sw.js">a comparably paltry amount of JavaScript</a> that—like a Web Worker—runs on its own thread.</li><li>After the Service Worker’s registration, you can intercept requests and decide how to respond to them in the <a href="https://developer.mozilla.org/en-US/docs/Web/API/FetchEvent">Service Worker’s <code>fetch()</code> event</a>.</li></ol>
<p>What you decide to do with requests you intercept is a) your call and b) depends on your website. You can <a href="https://alistapart.com/article/request-with-intent-caching-strategies-in-the-age-of-pwas/#section8">rewrite requests</a>, <a href="https://web.dev/offline-cookbook/#on-install-as-dependency">precache static assets</a> during install, <a href="https://www.madebymike.com.au/writing/service-workers/#a-better-offline-page-deeper-down-the-rabbit-hole">provide offline functionality</a>, and—as will be our eventual focus—<a href="https://philipwalton.com/articles/smaller-html-payloads-with-service-workers/">deliver smaller HTML payloads and better performance</a> for repeat visitors.</p>
<h2 class="wp-block-heading">Getting out of the woods</h2>
<p>Weekly Timber is a client of mine that provides logging services in central Wisconsin. For them, a fast website is vital. Their business is located in <a href="https://en.wikipedia.org/wiki/Waushara_County,_Wisconsin">Waushara County</a>, and like many rural stretches in the United States, <a href="https://maps.psc.wi.gov/apps/WisconsinBroadbandMap/">network quality and reliability isn’t great</a>.</p>
<div class="wp-block-image"><figure class="aligncenter size-large"><img decoding="async" loading="lazy" width="960" height="500" |
thank you being patient and guiding me :) |
Successfully generated as following: http://localhost:1200/alistapart - Success ✔️<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"
>
<channel>
<title><![CDATA[A List Apart]]></title>
<link>https://alistapart.com/articles</link>
<atom:link href="http://localhost:1200/alistapart" rel="self" type="application/rss+xml" />
<description><![CDATA[Articles on aListApart.com - Made with love by RSSHub(https://github.com/DIYgod/RSSHub)]]></description>
<generator>RSSHub</generator>
<webMaster>i@diygod.me (DIYgod)</webMaster>
<language>en-us</language>
<lastBuildDate>Thu, 10 Aug 2023 16:54:27 GMT</lastBuildDate>
<ttl>5</ttl>
<item>
<title><![CDATA[Humility: An Essential Value]]></title>
<description><![CDATA[<p>Humility, a designer’s essential value—that has a nice ring to it. What about humility, an office manager’s essential value? Or a dentist’s? Or a librarian’s? They all sound great. When humility is our guiding light, the path is always open for fulfillment, evolution, connection, and engagement. In this chapter, we’re going to talk about why.</p>
<p>That said, this is a book for designers, and to that end, I’d like to start with a story—well, a journey, really. It’s a personal one, and I’m going to make myself a bit vulnerable along the way. I call it:</p>
<h2 class="wp-block-heading"><strong>The Tale of Justin’s Preposterous Pate</strong></h2>
<p>When I was coming out of art school, a long-haired, goateed neophyte, print was a known quantity to me; design on the web, however, was rife with complexities to navigate and discover, a problem to be solved. Though I had been formally trained in graphic design, typography, and layout, what fascinated me was how these traditional skills might be applied to a fledgling digital landscape. This theme would ultimately shape the rest of my career.</p>
<p>So rather than graduate and go into print like many of my friends, I devoured HTML and JavaScript books into the wee hours of the morning and taught myself how to code during my senior year. I wanted—nay, needed—to better understand the underlying implications of what my design decisions would mean once rendered in a browser.</p>
<p>The late ’90s and early 2000s were the so-called “Wild West” of web design. Designers at the time were all figuring out how to apply design and visual communication to the digital landscape. What were the rules? How could we break them and still engage, entertain, and convey information? At a more macro level, how could my values, inclusive of humility, respect, and connection, align in tandem with that? I was hungry to find out.</p>
<p>Though I’m talking about a different era, those are timeless considerations between non-career interactions and the world of design. What are your core passions, or values, that transcend medium? It’s essentially the same concept we discussed earlier on the direct parallels between what fulfills you, agnostic of the tangible or digital realms; the core themes are all the same.</p>
<p>First within tables, animated GIFs, Flash, then with Web Standards, <code>div</code>s, and CSS, there was personality, raw unbridled creativity, and unique means of presentment that often defied any semblance of a visible grid. Splash screens and “browser requirement” pages aplenty. Usability and accessibility were typically victims of such a creation, but such paramount facets of any digital design were largely (and, in hindsight, unfairly) disregarded at the expense of experimentation.</p>
<p>For example, this iteration of my personal portfolio site (“the pseudoroom”) from that era was experimental, if not a bit heavy- handed, in the visual communication of the concept of a living sketchbook. Very skeuomorphic. I collaborated with fellow designer and dear friend Marc Clancy (now a co-founder of the creative project organizing app Milanote) on this one, where we’d first sketch and then pass a Photoshop file back and forth to trick things out and play with varied user interactions. Then, I’d break it down and code it into a digital layout.</p>
<figure class="wp-block-image size-full is-resized"><img decoding="async" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2023/06/designers-journey-2-1.jpg?w=846&ssl=1" alt="" class="wp-image-7173967" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2023/06/designers-journey-2-1.jpg?w=1024&ssl=1 1024w, https://i0.wp.com/alistapart.com/wp-content/uploads/2023/06/designers-journey-2-1.jpg?resize=300%2C252&ssl=1 300w, https://i0.wp.com/alistapart.com/wp-content/uploads/2023/06/designers-journey-2-1.jpg?resize=768%2C644&ssl=1 768w" sizes="(max-width: 960px) 100vw, 960px" data-recalc-dims="1" referrerpolicy="no-referrer"><figcaption class="wp-element-caption">Figure 1: “the pseudoroom” website, hitting the sketchbook metaphor hard.</figcaption></figure>
<p>Along with design folio pieces, the site also offered free downloads for Mac OS customizations: desktop wallpapers that were effectively design experimentation, custom-designed typefaces, and desktop icons.</p>
<p>From around the same time, GUI Galaxy was a design, pixel art, and Mac-centric news portal some graphic designer friends and I conceived, designed, developed, and deployed.</p>
<figure class="wp-block-image size-large is-resized"><img decoding="async" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2023/06/designers-journey-2-2.jpg?w=846&ssl=1" alt="" class="wp-image-7173968" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2023/06/designers-journey-2-2.jpg?resize=1024%2C907&ssl=1 1024w, https://i0.wp.com/alistapart.com/wp-content/uploads/2023/06/designers-journey-2-2.jpg?resize=300%2C266&ssl=1 300w, https://i0.wp.com/alistapart.com/wp-content/uploads/2023/06/designers-journey-2-2.jpg?resize=768%2C680&ssl=1 768w, https://i0.wp.com/alistapart.com/wp-content/uploads/2023/06/designers-journey-2-2.jpg?resize=1536%2C1360&ssl=1 1536w, https://i0.wp.com/alistapart.com/wp-content/uploads/2023/06/designers-journey-2-2.jpg?w=1590&ssl=1 1590w" sizes="(max-width: 960px) 100vw, 960px" data-recalc-dims="1" referrerpolicy="no-referrer"><figcaption class="wp-element-caption">Figure 2: GUI Galaxy, web standards-compliant design news portal</figcaption></figure>
<p>Design news portals were incredibly popular during this period, featuring (what would now be considered) Tweet-size, small-format snippets of pertinent news from the categories I previously mentioned. If you took Twitter, curated it to a few categories, and wrapped it in a custom-branded experience, you’d have a design news portal from the late 90s / early 2000s.</p>
<p>We as designers had evolved and created a bandwidth-sensitive, web standards award-winning, much more accessibility-conscious website. Still ripe with experimentation, yet more mindful of equitable engagement. You can see a couple of content panes here, noting general news (tech, design) and Mac-centric news below. We also offered many of the custom downloads I cited before as present on my folio site but branded and themed to GUI Galaxy.</p>
<p>The site’s backbone was a homegrown CMS, with the presentation layer consisting of global design + illustration + news author collaboration. And the collaboration effort here, in addition to experimentation on a ‘brand’ and content delivery, was hitting my core. We were designing something bigger than any single one of us and connecting with a global audience.</p>
<p>Collaboration and connection transcend medium in their impact, immensely fulfilling me as a designer.</p>
<p>Now, why am I taking you down this trip of design memory lane? Two reasons.</p>
<p>First, there’s a reason for the nostalgia for that design era (the “Wild West” era, as I called it earlier): the inherent exploration, personality, and creativity that saturated many design portals and personal portfolio sites. Ultra-finely detailed pixel art UI, custom illustration, bespoke vector graphics, all underpinned by a strong design community.</p>
<p>Today’s web design has been in a period of stagnation. I suspect there’s a strong chance you’ve seen a site whose structure looks something like this: a hero image / banner with text overlaid, perhaps with a lovely rotating carousel of images (laying the snark on heavy there), a call to action, and three columns of sub-content directly beneath. Maybe an icon library is employed with selections that vaguely relate to their respective content.</p>
<p>Design, as it’s applied to the digital landscape, is in dire need of thoughtful layout, typography, and visual engagement that goes hand-in-hand with all the modern considerations we now know are paramount: usability. Accessibility. Load times and bandwidth- sensitive content delivery. A responsive presentation that meets human beings wherever they’re engaging from. We must be mindful of, and respectful toward, those concerns—but not at the expense of creativity of visual communication or via replicating cookie-cutter layouts.</p>
<h2 class="wp-block-heading">Pixel Problems</h2>
<p>Websites during this period were often designed and built on Macs whose OS and desktops looked something like this. This is Mac OS 7.5, but 8 and 9 weren’t that different.</p>
<figure class="wp-block-image size-large is-resized"><img decoding="async" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2023/06/designers-journey-2-3.jpg?w=846&ssl=1" alt="" class="wp-image-7173969" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2023/06/designers-journey-2-3.jpg?resize=1024%2C768&ssl=1 1024w, https://i0.wp.com/alistapart.com/wp-content/uploads/2023/06/designers-journey-2-3.jpg?resize=300%2C225&ssl=1 300w, https://i0.wp.com/alistapart.com/wp-content/uploads/2023/06/designers-journey-2-3.jpg?resize=768%2C576&ssl=1 768w, https://i0.wp.com/alistapart.com/wp-content/uploads/2023/06/designers-journey-2-3.jpg?resize=1536%2C1152&ssl=1 1536w, https://i0.wp.com/alistapart.com/wp-content/uploads/2023/06/designers-journey-2-3.jpg?w=1592&ssl=1 1592w" sizes="(max-width: 960px) 100vw, 960px" data-recalc-dims="1" referrerpolicy="no-referrer"><figcaption class="wp-element-caption">Figure 3: A Mac OS 7.5-centric desktop.</figcaption></figure>
<p>Desktop icons fascinated me: how could any single one, at any given point, stand out to get my attention? In this example, the user’s desktop is tidy, but think of a more realistic example with icon pandemonium. Or, say an icon was part of a larger system grouping (fonts, extensions, control panels)—how did it also maintain cohesion amongst a group?</p>
<p>These were 32 x 32 pixel creations, utilizing a 256-color palette, designed pixel-by-pixel as mini mosaics. To me, this was the embodiment of digital visual communication under such ridiculous constraints. And often, ridiculous restrictions can yield the purification of concept and theme.</p>
<p>So I began to research and do my homework. I was a student of this new medium, hungry to dissect, process, discover, and make it my own.</p>
<p>Expanding upon the notion of exploration, I wanted to see how I could push the limits of a 32×32 pixel grid with that 256-color palette. Those ridiculous constraints forced a clarity of concept and presentation that I found incredibly appealing. The digital gauntlet had been tossed, and that challenge fueled me. And so, in my dorm room into the wee hours of the morning, I toiled away, bringing conceptual sketches into mini mosaic fruition.</p>
<p>These are some of my creations, utilizing the only tool available at the time to create icons called ResEdit. ResEdit was a clunky, built-in Mac OS utility not really made for exactly what we were using it for. At the core of all of this work: Research. Challenge. Problem- solving. Again, these core connection-based values are agnostic of medium.</p>
<figure class="wp-block-image size-large is-resized"><img decoding="async" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2023/06/designers-journey-2-4.png?w=846&ssl=1" alt="" class="wp-image-7173970" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2023/06/designers-journey-2-4.png?resize=1024%2C977&ssl=1 1024w, https://i0.wp.com/alistapart.com/wp-content/uploads/2023/06/designers-journey-2-4.png?resize=300%2C286&ssl=1 300w, https://i0.wp.com/alistapart.com/wp-content/uploads/2023/06/designers-journey-2-4.png?resize=768%2C733&ssl=1 768w, https://i0.wp.com/alistapart.com/wp-content/uploads/2023/06/designers-journey-2-4.png?resize=1536%2C1465&ssl=1 1536w, https://i0.wp.com/alistapart.com/wp-content/uploads/2023/06/designers-journey-2-4.png?w=1602&ssl=1 1602w" sizes="(max-width: 960px) 100vw, 960px" data-recalc-dims="1" referrerpolicy="no-referrer"><figcaption class="wp-element-caption">Figure 4: A selection of my pixel art design, 32×32 pixel canvas, 8-bit palette</figcaption></figure>
<p>There’s one more design portal I want to talk about, which also serves as the second reason for my story to bring this all together.</p>
<p>This is K10k, short for Kaliber 1000. K10k was founded in 1998 by Michael Schmidt and Toke Nygaard, and was <strong>the </strong>design news portal on the web during this period. With its pixel art-fueled presentation, ultra-focused care given to every facet and detail, and with many of the more influential designers of the time who were invited to be news authors on the site, well… it was the place to be, my friend. With respect where respect is due, GUI Galaxy’s concept was inspired by what these folks were doing.</p>
<figure class="wp-block-image size-full is-resized"><img decoding="async" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2023/06/designers-journey-2-5.png?w=846&ssl=1" alt="" class="wp-image-7173971" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2023/06/designers-journey-2-5.png?w=1024&ssl=1 1024w, https://i0.wp.com/alistapart.com/wp-content/uploads/2023/06/designers-journey-2-5.png?resize=300%2C269&ssl=1 300w, https://i0.wp.com/alistapart.com/wp-content/uploads/2023/06/designers-journey-2-5.png?resize=768%2C688&ssl=1 768w" sizes="(max-width: 960px) 100vw, 960px" data-recalc-dims="1" referrerpolicy="no-referrer"><figcaption class="wp-element-caption">Figure 5: The K10k website</figcaption></figure>
<p>For my part, the combination of my web design work and pixel art exploration began to get me some notoriety in the design scene. Eventually, K10k noticed and added me as one of their very select group of news authors to contribute content to the site.</p>
<p>Amongst my personal work and side projects—and now with this inclusion—in the design community, this put me on the map. My design work also began to be published in various printed collections, in magazines domestically and overseas, and featured on other design news portals. With that degree of success while in my early twenties, something else happened:</p>
<p>I evolved—devolved, really—into a colossal asshole (and in just about a year out of art school, no less). The press and the praise became what fulfilled me, and they went straight to my head. They inflated my ego. I actually felt somewhat superior to my fellow designers.</p>
<p>The casualties? My design stagnated. Its evolution—my evolution— stagnated.</p>
<p>I felt so supremely confident in my abilities that I effectively stopped researching and discovering. When previously sketching concepts or iterating ideas in lead was my automatic step one, I instead leaped right into Photoshop. I drew my inspiration from the smallest of sources (and with blinders on). Any critique of my work from my peers was often vehemently dismissed. The most tragic loss: I had lost touch with my values.</p>
<p>My ego almost cost me some of my friendships and burgeoning professional relationships. I was toxic in talking about design and in collaboration. But thankfully, those same friends gave me a priceless gift: candor. They called me out on my unhealthy behavior.</p>
<p>Admittedly, it was a gift I initially did not accept but ultimately was able to deeply reflect upon. I was soon able to accept, and process, and course correct. The realization laid me low, but the re-awakening was essential. I let go of the “reward” of adulation and re-centered upon what stoked the fire for me in art school. Most importantly: I got back to my core values.</p>
<h2 class="wp-block-heading">Always Students</h2>
<p>Following that short-term regression, I was able to push forward in my personal design and career. And I could self-reflect as I got older to facilitate further growth and course correction as needed.</p>
<p>As an example, let’s talk about the Large Hadron Collider. The LHC was designed <em>“to help answer some of the fundamental open questions in physics, which concern the basic laws governing the interactions and forces among the elementary objects, the deep structure of space and time, and in particular the interrelation between quantum mechanics and general relativity.” </em>Thanks, Wikipedia.</p>
<p>Around fifteen years ago, in one of my earlier professional roles, I designed the interface for the application that generated the LHC’s particle collision diagrams. These diagrams are the rendering of what’s actually happening inside the Collider during any given particle collision event and are often considered works of art unto themselves.</p>
<p>Designing the interface for this application was a fascinating process for me, in that I worked with Fermilab physicists to understand what the application was trying to achieve, but also how the physicists themselves would be using it. To that end, in this role,</p>
<p>I cut my teeth on usability testing, working with the Fermilab team to iterate and improve the interface. How they spoke and what they spoke about was like an alien language to me. And by making myself humble and working under the mindset that I was but a student, I made myself available to be a part of their world to generate that vital connection.</p>
<p>I also had my first ethnographic observation experience: going to the Fermilab location and observing how the physicists used the tool in their actual environment, on their actual terminals. For example, one takeaway was that due to the level of ambient light-driven contrast within the facility, the data columns ended up using white text on a dark gray background instead of black text-on-white. This enabled them to pore over reams of data during the day and ease their eye strain. And Fermilab and CERN are government entities with rigorous accessibility standards, so my knowledge in that realm also grew. The barrier-free design was another essential form of connection.</p>
<p>So to those core drivers of my visual problem-solving soul and ultimate fulfillment: discovery, exposure to new media, observation, human connection, and evolution. What opened the door for those values was me checking my ego before I walked through it.</p>
<p>An evergreen willingness to listen, learn, understand, grow, evolve, and connect yields our best work. In particular, I want to focus on the words ‘grow’ and ‘evolve’ in that statement. If we are always students of our craft, we are also continually making ourselves available to evolve. Yes, we have years of applicable design study under our belt. Or the focused lab sessions from a UX bootcamp. Or the monogrammed portfolio of our work. Or, ultimately, decades of a career behind us.</p>
<p>But all that said: experience does not equal “expert.”</p>
<p>As soon as we close our minds via an inner monologue of ‘knowing it all’ or branding ourselves a “#thoughtleader” on social media, the designer we <strong>are </strong>is our final form. The designer we <strong>can be </strong>will never exist.</p>
]]></description>
<pubDate>Thu, 22 Jun 2023 13:00:00 GMT</pubDate>
<guid isPermaLink="false">https://alistapart.com/article/humility-an-essential-value/</guid>
<link>https://alistapart.com/article/humility-an-essential-value/</link>
<author><![CDATA[agustafson]]></author>
<category>Design</category>
</item>
<item>
<title><![CDATA[Personalization Pyramid: A Framework for Designing with User Data]]></title>
<description><![CDATA[<p>As a UX professional in today’s data-driven landscape, it’s increasingly likely that you’ve been asked to design a personalized digital experience, whether it’s a public website, user portal, or native application. Yet while there continues to be no shortage of marketing hype around personalization platforms, we still have very few standardized approaches for implementing personalized UX.</p>
<p>That’s where we come in. After completing dozens of personalization projects over the past few years, we gave ourselves a goal: could you create a holistic personalization framework specifically for UX practitioners? The <strong>Personalization Pyramid</strong> is a designer-centric model for standing up human-centered personalization programs, spanning data, segmentation, content delivery, and overall goals. By using this approach, you will be able to understand the core components of a contemporary, UX-driven personalization program (or at the very least know enough to get started). </p>
<figure class="wp-block-image size-large"><img decoding="async" loading="lazy" width="960" height="558" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image20.png?resize=960%2C558&ssl=1" alt="A chart answering the question Do you have the resources you need to run personalization in your organization? Globally, 13% don’t 33% have limited access, 39% have it (on demand), and 15% have it dedicated." class="wp-image-7173666" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image20.png?resize=1024%2C595&ssl=1 1024w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image20.png?resize=300%2C174&ssl=1 300w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image20.png?resize=768%2C446&ssl=1 768w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image20.png?w=1256&ssl=1 1256w" sizes="(max-width: 960px) 100vw, 960px" data-recalc-dims="1" referrerpolicy="no-referrer"><figcaption class="wp-element-caption"><p><strong>Growing tools for personalization:</strong> According to a Dynamic Yield survey, 39% of respondents felt support is available on-demand when a business case is made for it (up 15% from 2020).</p><p><small>Source: “The State of Personalization Maturity – Q4 2021” Dynamic Yield conducted its annual maturity survey across roles and sectors in the Americas (AMER), Europe and the Middle East (EMEA), and the Asia-Pacific (APAC) regions. This marks the fourth consecutive year publishing our research, which includes more than 450 responses from individuals in the C-Suite, Marketing, Merchandising, CX, Product, and IT.</small></p></figcaption></figure>
<h2 class="wp-block-heading"><strong>Getting Started</strong></h2>
<p>For the sake of this article, we’ll assume you’re already familiar with the basics of digital personalization. A good overview can be found here: <a href="https://www.uxbooth.com/articles/website-personalization-planning/">Website Personalization Planning</a>. While UX projects in this area can take on many different forms, they often stem from similar starting points. </p>
<p><strong>Common scenarios for starting a personalization project:</strong></p>
<ul>
<li>Your organization or client purchased a content management system (CMS) or marketing automation platform (MAP) or related technology that supports personalization</li>
<li>The CMO, CDO, or CIO has identified personalization as a goal</li>
<li>Customer data is disjointed or ambiguous</li>
<li>You are running some isolated targeting campaigns or A/B testing</li>
<li>Stakeholders disagree on personalization approach</li>
<li>Mandate of customer privacy rules (e.g. GDPR) requires revisiting existing user targeting practices</li>
</ul>
<figure class="wp-block-image size-full is-resized"><img decoding="async" loading="lazy" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image22.png?resize=768%2C576&ssl=1" alt="Two men and a woman discussing personalization using a card deck. They are seated at a round table in a hotel conference room. The workshop leaders, two women, are at a podium in the background." class="wp-image-7173667" width="768" height="576" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image22.png?w=1024&ssl=1 1024w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image22.png?resize=300%2C225&ssl=1 300w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image22.png?resize=768%2C576&ssl=1 768w" sizes="(max-width: 768px) 100vw, 768px" data-recalc-dims="1" referrerpolicy="no-referrer"><figcaption class="wp-element-caption">Workshopping personalization at a conference.</figcaption></figure>
<p>Regardless of where you begin, a successful personalization program will require the same core building blocks. We’ve captured these as the “levels” on the pyramid. Whether you are a UX designer, researcher, or strategist, understanding the core components can help make your contribution successful. </p>
<figure class="wp-block-image size-large is-resized"><img decoding="async" loading="lazy" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image14.png?resize=488%2C512&ssl=1" alt="The Personalization Pyramid visualized. The pyramid is stacks labeled, from the bottom, raw data (1m+), actionable data (100k+), user segments (1k+), contexts & campaigns (100s), touchpoints (dozens), goals (handful). The North Star (one) is above. An arrow for prescriptive, business driven data goes up the left side and an arrow for adaptive user-driven data goes down the right side." class="wp-image-7173665" width="488" height="512" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image14.png?resize=975%2C1024&ssl=1 975w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image14.png?resize=286%2C300&ssl=1 286w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image14.png?resize=768%2C807&ssl=1 768w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image14.png?w=1146&ssl=1 1146w" sizes="(max-width: 488px) 100vw, 488px" data-recalc-dims="1" referrerpolicy="no-referrer"><figcaption class="wp-element-caption">From the ground up: Soup-to-nuts personalization, without going nuts.</figcaption></figure>
<p>From top to bottom, the levels include:</p>
<ol>
<li><strong>North Star: </strong>What larger strategic objective is driving the personalization program? </li>
<li><strong>Goals:</strong> What are the specific, measurable outcomes of the program? </li>
<li><strong>Touchpoints: </strong>Where will the personalized experience be served?</li>
<li><strong>Contexts and Campaigns: </strong>What personalization content will the user see?</li>
<li><strong>User Segments:</strong> What constitutes a unique, usable audience? </li>
<li><strong>Actionable Data: </strong>What reliable and authoritative data is captured by our technical platform to drive personalization? </li>
<li><strong>Raw Data: </strong>What wider set of data is conceivably available (already in our setting) allowing you to personalize?</li>
</ol>
<p>We’ll go through each of these levels in turn. To help make this actionable, we created an accompanying <strong>deck of cards</strong> to illustrate specific examples from each level. We’ve found them helpful in personalization brainstorming sessions, and will include examples for you here.</p>
<figure class="wp-block-image size-full"><img decoding="async" loading="lazy" width="320" height="215" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image2.png?resize=320%2C215&ssl=1" alt="A deck of personalization brainstorming cards (the size of playing cards) against a black background." class="wp-image-7173668" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image2.png?w=320&ssl=1 320w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image2.png?resize=300%2C202&ssl=1 300w" sizes="(max-width: 320px) 100vw, 320px" data-recalc-dims="1" referrerpolicy="no-referrer"><figcaption class="wp-element-caption"><strong>Personalization pack:</strong> Deck of cards to help kickstart your personalization brainstorming.</figcaption></figure>
<h2 class="wp-block-heading"><strong>Starting at the Top</strong></h2>
<p>The components of the pyramid are as follows:</p>
<h3 class="wp-block-heading">North Star</h3>
<p>A north star is what you are aiming for overall with your personalization program (big or small). The North Star defines the (one) overall mission of the personalization program. What do you wish to accomplish? North Stars cast a shadow. The bigger the star, the bigger the shadow. Example of North Starts might include: </p>
<ol>
<li><strong>Function:</strong> Personalize based on basic user inputs. Examples: “Raw” notifications, basic search results, system user settings and configuration options, general customization, basic optimizations</li>
<li><strong>Feature:</strong> Self-contained personalization componentry. Examples: “Cooked” notifications, advanced optimizations (geolocation), basic dynamic messaging, customized modules, automations, recommenders</li>
<li><strong>Experience:</strong> Personalized user experiences across multiple interactions and user flows. Examples: Email campaigns, landing pages, advanced messaging (i.e. C2C chat) or conversational interfaces, larger user flows and content-intensive optimizations (localization).</li>
<li><strong>Product:</strong> Highly differentiating personalized product experiences. Examples: Standalone, branded experiences with personalization at their core, like the “algotorial” playlists by Spotify such as Discover Weekly.</li>
</ol>
<figure class="wp-block-gallery has-nested-images columns-default is-cropped wp-block-gallery-1 is-layout-flex wp-block-gallery-is-layout-flex">
<figure class="wp-block-image size-large"><img decoding="async" loading="lazy" width="656" height="1024" data-id="7173669" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image7.jpg?resize=656%2C1024&ssl=1" alt="Function: React to basic user inputs" class="wp-image-7173669" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image7.jpg?resize=656%2C1024&ssl=1 656w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image7.jpg?resize=192%2C300&ssl=1 192w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image7.jpg?resize=768%2C1198&ssl=1 768w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image7.jpg?resize=984%2C1536&ssl=1 984w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image7.jpg?w=1249&ssl=1 1249w" sizes="(max-width: 656px) 100vw, 656px" data-recalc-dims="1" referrerpolicy="no-referrer"></figure>
<figure class="wp-block-image size-large"><img decoding="async" loading="lazy" width="656" height="1024" data-id="7173670" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image18.jpg?resize=656%2C1024&ssl=1" alt="Feature: personalized modules" class="wp-image-7173670" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image18.jpg?resize=656%2C1024&ssl=1 656w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image18.jpg?resize=192%2C300&ssl=1 192w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image18.jpg?resize=768%2C1198&ssl=1 768w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image18.jpg?resize=984%2C1536&ssl=1 984w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image18.jpg?w=1249&ssl=1 1249w" sizes="(max-width: 656px) 100vw, 656px" data-recalc-dims="1" referrerpolicy="no-referrer"></figure>
<figure class="wp-block-image size-large"><img decoding="async" loading="lazy" width="656" height="1024" data-id="7173671" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image8.jpg?resize=656%2C1024&ssl=1" alt="Experience: Integrated personalization" class="wp-image-7173671" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image8.jpg?resize=656%2C1024&ssl=1 656w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image8.jpg?resize=192%2C300&ssl=1 192w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image8.jpg?resize=768%2C1198&ssl=1 768w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image8.jpg?resize=984%2C1536&ssl=1 984w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image8.jpg?w=1249&ssl=1 1249w" sizes="(max-width: 656px) 100vw, 656px" data-recalc-dims="1" referrerpolicy="no-referrer"></figure>
<figcaption class="blocks-gallery-caption wp-element-caption"><strong>North star cards. </strong>These can help orient your team towards a common goal that personalization will help achieve; Also, these are useful for characterizing the end-state ambition of the presently stated personalization effort.</figcaption></figure>
<h3 class="wp-block-heading">Goals</h3>
<p>As in any good UX design, personalization can help accelerate <a href="https://www.uxbooth.com/articles/designing-for-customer-intentions-part-1/">designing with customer intentions</a><strong>. Goals</strong> are the tactical and measurable metrics that will prove the overall program is successful. A good place to start is with your current analytics and measurement program and metrics you can benchmark against. In some cases, new goals may be appropriate. The key thing to remember is that <em>personalization itself is not a goal</em>, rather it is a means to an end. Common goals include:</p>
<ul>
<li>Conversion</li>
<li>Time on task</li>
<li>Net promoter score (NPS)</li>
<li>Customer satisfaction </li>
</ul>
<figure class="wp-block-gallery has-nested-images columns-default is-cropped wp-block-gallery-3 is-layout-flex wp-block-gallery-is-layout-flex">
<figure class="wp-block-image size-large"><img decoding="async" loading="lazy" width="656" height="1024" data-id="7173674" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image12.jpg?resize=656%2C1024&ssl=1" alt="NPS: Net Promoter Score" class="wp-image-7173674" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image12.jpg?resize=656%2C1024&ssl=1 656w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image12.jpg?resize=192%2C300&ssl=1 192w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image12.jpg?resize=768%2C1198&ssl=1 768w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image12.jpg?resize=984%2C1536&ssl=1 984w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image12.jpg?w=1249&ssl=1 1249w" sizes="(max-width: 656px) 100vw, 656px" data-recalc-dims="1" referrerpolicy="no-referrer"></figure>
<figure class="wp-block-image size-large"><img decoding="async" loading="lazy" width="656" height="1024" data-id="7173672" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image23.jpg?resize=656%2C1024&ssl=1" alt="Time on Task: Users move quicker" class="wp-image-7173672" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image23.jpg?resize=656%2C1024&ssl=1 656w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image23.jpg?resize=192%2C300&ssl=1 192w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image23.jpg?resize=768%2C1198&ssl=1 768w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image23.jpg?resize=984%2C1536&ssl=1 984w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image23.jpg?w=1249&ssl=1 1249w" sizes="(max-width: 656px) 100vw, 656px" data-recalc-dims="1" referrerpolicy="no-referrer"></figure>
<figure class="wp-block-image size-large"><img decoding="async" loading="lazy" width="656" height="1024" data-id="7173673" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image13.jpg?resize=656%2C1024&ssl=1" alt="Conversion: Move more of the thing" class="wp-image-7173673" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image13.jpg?resize=656%2C1024&ssl=1 656w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image13.jpg?resize=192%2C300&ssl=1 192w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image13.jpg?resize=768%2C1198&ssl=1 768w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image13.jpg?resize=984%2C1536&ssl=1 984w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image13.jpg?w=1249&ssl=1 1249w" sizes="(max-width: 656px) 100vw, 656px" data-recalc-dims="1" referrerpolicy="no-referrer"></figure>
<figcaption class="blocks-gallery-caption wp-element-caption"><strong>Goal cards.</strong> Examples of some common KPIs related to personalization that are concrete and measurable.</figcaption></figure>
<h3 class="wp-block-heading">Touchpoints</h3>
<p>Touchpoints are where the personalization happens. As a UX designer, this will be one of your largest areas of responsibility. The touchpoints available to you will depend on how your personalization and associated technology capabilities are instrumented, and should be rooted in improving a user’s experience at a particular point in the journey. Touchpoints can be multi-device (mobile, in-store, website) but also more granular (web banner, web pop-up etc.). Here are some examples:</p>
<p><strong>Channel-level </strong>Touchpoints</p>
<ul>
<li>Email: Role</li>
<li>Email: Time of open</li>
<li>In-store display (JSON endpoint)</li>
<li>Native app</li>
<li>Search</li>
</ul>
<p><strong>Wireframe-level </strong>Touchpoints</p>
<ul>
<li>Web overlay</li>
<li>Web alert bar</li>
<li>Web banner</li>
<li>Web content block</li>
<li>Web menu</li>
</ul>
<figure class="wp-block-gallery has-nested-images columns-default is-cropped wp-block-gallery-5 is-layout-flex wp-block-gallery-is-layout-flex">
<figure class="wp-block-image size-large"><img decoding="async" loading="lazy" width="656" height="1024" data-id="7173677" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image25.jpg?resize=656%2C1024&ssl=1" alt="In-store Display: End-cap interfaces" class="wp-image-7173677" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image25.jpg?resize=656%2C1024&ssl=1 656w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image25.jpg?resize=192%2C300&ssl=1 192w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image25.jpg?resize=768%2C1198&ssl=1 768w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image25.jpg?resize=984%2C1536&ssl=1 984w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image25.jpg?w=1249&ssl=1 1249w" sizes="(max-width: 656px) 100vw, 656px" data-recalc-dims="1" referrerpolicy="no-referrer"></figure>
<figure class="wp-block-image size-large"><img decoding="async" loading="lazy" width="656" height="1024" data-id="7173675" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image11.jpg?resize=656%2C1024&ssl=1" alt="Email: Time, personalize at time of open" class="wp-image-7173675" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image11.jpg?resize=656%2C1024&ssl=1 656w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image11.jpg?resize=192%2C300&ssl=1 192w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image11.jpg?resize=768%2C1198&ssl=1 768w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image11.jpg?resize=984%2C1536&ssl=1 984w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image11.jpg?w=1249&ssl=1 1249w" sizes="(max-width: 656px) 100vw, 656px" data-recalc-dims="1" referrerpolicy="no-referrer"></figure>
<figure class="wp-block-image size-large"><img decoding="async" loading="lazy" width="656" height="1024" data-id="7173676" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image19.jpg?resize=656%2C1024&ssl=1" alt="Content Block: Into the woodwork" class="wp-image-7173676" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image19.jpg?resize=656%2C1024&ssl=1 656w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image19.jpg?resize=192%2C300&ssl=1 192w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image19.jpg?resize=768%2C1198&ssl=1 768w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image19.jpg?resize=984%2C1536&ssl=1 984w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image19.jpg?w=1249&ssl=1 1249w" sizes="(max-width: 656px) 100vw, 656px" data-recalc-dims="1" referrerpolicy="no-referrer"></figure>
<figcaption class="blocks-gallery-caption wp-element-caption"><strong>Touchpoint cards. </strong>Examples of common personalization touchpoints: these can vary from narrow (e.g., email) to broad (e.g., in-store).</figcaption></figure>
<p>If you’re designing for web interfaces, for example, you will likely need to include personalized “zones” in your wireframes. The content for these can be presented programmatically in touchpoints based on our next step, contexts and campaigns.</p>
<figure class="wp-block-gallery has-nested-images columns-default is-cropped wp-block-gallery-7 is-layout-flex wp-block-gallery-is-layout-flex">
<figure class="wp-block-image size-large"><img decoding="async" loading="lazy" width="732" height="974" data-id="7173678" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image21.png?resize=732%2C974&ssl=1" alt="" class="wp-image-7173678" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image21.png?w=732&ssl=1 732w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image21.png?resize=225%2C300&ssl=1 225w" sizes="(max-width: 732px) 100vw, 732px" data-recalc-dims="1" referrerpolicy="no-referrer"></figure>
<figure class="wp-block-image size-large"><img decoding="async" loading="lazy" width="708" height="922" data-id="7173679" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image17.png?resize=708%2C922&ssl=1" alt="" class="wp-image-7173679" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image17.png?w=708&ssl=1 708w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image17.png?resize=230%2C300&ssl=1 230w" sizes="(max-width: 708px) 100vw, 708px" data-recalc-dims="1" referrerpolicy="no-referrer"></figure>
<figcaption class="blocks-gallery-caption wp-element-caption"><strong>Targeted Zones:</strong> Examples from Kibo of personalized “zones” on page-level wireframes occurring at various stages of a user journey (Engagement phase at left and Purchase phase at right.)<br><br>Source: “Essential Guide to End-to-End Personaliztion” by Kibo.</figcaption></figure>
<h3 class="wp-block-heading">Contexts and Campaigns</h3>
<p>Once you’ve outlined some touchpoints, you can consider the actual personalized content a user will receive. Many personalization tools will refer to these as “campaigns” (so, for example, a campaign on a web banner for new visitors to the website). These will programmatically be shown at certain touchpoints to certain user segments, as defined by user data. At this stage, we find it helpful to consider two separate models: a <strong>context model</strong> and a <strong>content model</strong>. The context helps you consider the level of engagement of the user at the personalization moment, for example a user casually browsing information vs. doing a deep-dive. Think of it in terms of information retrieval behaviors. The content model can then help you determine what type of personalization to serve based on the context (for example, an “Enrich” campaign that shows related articles may be a suitable supplement to extant content).</p>
<p>Personalization <strong>Context</strong> Model:</p>
<ol>
<li>Browse</li>
<li>Skim</li>
<li>Nudge</li>
<li>Feast</li>
</ol>
<p>Personalization <strong>Content</strong> Model:</p>
<ol>
<li>Alert</li>
<li>Make Easier</li>
<li>Cross-Sell</li>
<li>Enrich</li>
</ol>
<p>We’ve written extensively about each of these models elsewhere, so if you’d like to read more you can check out Colin’s <a href="https://alistapart.com/article/emerging-ux-role-in-personalization/">Personalization Content Model</a> and Jeff’s <a href="https://bucket.circle.so/c/field-notes/progressive-personalization-a-decisionmaking-model-for-better-outcomes-in-personalized-ux">Personalization Context Model</a>. </p>
<figure class="wp-block-gallery has-nested-images columns-default is-cropped wp-block-gallery-9 is-layout-flex wp-block-gallery-is-layout-flex">
<figure class="wp-block-image size-large"><img decoding="async" loading="lazy" width="656" height="1024" data-id="7173681" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image28.jpg?resize=656%2C1024&ssl=1" alt="Cross Sell: You may also like…" class="wp-image-7173681" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image28.jpg?resize=656%2C1024&ssl=1 656w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image28.jpg?resize=192%2C300&ssl=1 192w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image28.jpg?resize=768%2C1198&ssl=1 768w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image28.jpg?resize=984%2C1536&ssl=1 984w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image28.jpg?w=1249&ssl=1 1249w" sizes="(max-width: 656px) 100vw, 656px" data-recalc-dims="1" referrerpolicy="no-referrer"></figure>
<figure class="wp-block-image size-large"><img decoding="async" loading="lazy" width="656" height="1024" data-id="7173682" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image3.jpg?resize=656%2C1024&ssl=1" alt="Enrich: You might find this interesting" class="wp-image-7173682" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image3.jpg?resize=656%2C1024&ssl=1 656w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image3.jpg?resize=192%2C300&ssl=1 192w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image3.jpg?resize=768%2C1198&ssl=1 768w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image3.jpg?resize=984%2C1536&ssl=1 984w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image3.jpg?w=1249&ssl=1 1249w" sizes="(max-width: 656px) 100vw, 656px" data-recalc-dims="1" referrerpolicy="no-referrer"></figure>
<figure class="wp-block-image size-large"><img decoding="async" loading="lazy" width="656" height="1024" data-id="7173680" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image27.jpg?resize=656%2C1024&ssl=1" alt="Browse: Lean back, shallow engagement" class="wp-image-7173680" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image27.jpg?resize=656%2C1024&ssl=1 656w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image27.jpg?resize=192%2C300&ssl=1 192w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image27.jpg?resize=768%2C1198&ssl=1 768w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image27.jpg?resize=984%2C1536&ssl=1 984w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image27.jpg?w=1249&ssl=1 1249w" sizes="(max-width: 656px) 100vw, 656px" data-recalc-dims="1" referrerpolicy="no-referrer"></figure>
<figcaption class="blocks-gallery-caption wp-element-caption"><strong>Campaign and Context cards:</strong> This level of the pyramid can help your team focus around the types of personalization to deliver end users and the use-cases in which they will experience it.</figcaption></figure>
<h3 class="wp-block-heading">User Segments</h3>
<p>User segments can be created prescriptively or adaptively, based on user research (e.g. via rules and logic tied to set user behaviors or via A/B testing). At a minimum you will likely need to consider how to treat the <em>unknown</em> or first-time visitor, the <em>guest</em> or returning visitor for whom you may have a stateful cookie (or equivalent post-cookie identifier), or the <em>authenticated</em> visitor who is logged in. Here are some examples from the personalization pyramid:</p>
<ul>
<li>Unknown</li>
<li>Guest</li>
<li>Authenticated</li>
<li>Default</li>
<li>Referred</li>
<li>Role</li>
<li>Cohort</li>
<li>Unique ID</li>
</ul>
<figure class="wp-block-gallery has-nested-images columns-default is-cropped wp-block-gallery-11 is-layout-flex wp-block-gallery-is-layout-flex">
<figure class="wp-block-image size-large"><img decoding="async" loading="lazy" width="656" height="1024" data-id="7173685" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image16.jpg?resize=656%2C1024&ssl=1" alt="Authenticated: Logged in with token" class="wp-image-7173685" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image16.jpg?resize=656%2C1024&ssl=1 656w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image16.jpg?resize=192%2C300&ssl=1 192w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image16.jpg?resize=768%2C1198&ssl=1 768w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image16.jpg?resize=984%2C1536&ssl=1 984w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image16.jpg?w=1249&ssl=1 1249w" sizes="(max-width: 656px) 100vw, 656px" data-recalc-dims="1" referrerpolicy="no-referrer"></figure>
<figure class="wp-block-image size-large"><img decoding="async" loading="lazy" width="656" height="1024" data-id="7173683" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image10.jpg?resize=656%2C1024&ssl=1" alt="Unknown: Could be anyone really" class="wp-image-7173683" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image10.jpg?resize=656%2C1024&ssl=1 656w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image10.jpg?resize=192%2C300&ssl=1 192w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image10.jpg?resize=768%2C1198&ssl=1 768w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image10.jpg?resize=984%2C1536&ssl=1 984w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image10.jpg?w=1249&ssl=1 1249w" sizes="(max-width: 656px) 100vw, 656px" data-recalc-dims="1" referrerpolicy="no-referrer"></figure>
<figure class="wp-block-image size-large"><img decoding="async" loading="lazy" width="656" height="1024" data-id="7173684" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image9.jpg?resize=656%2C1024&ssl=1" alt="Guest: Dropped a cookie" class="wp-image-7173684" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image9.jpg?resize=656%2C1024&ssl=1 656w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image9.jpg?resize=192%2C300&ssl=1 192w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image9.jpg?resize=768%2C1198&ssl=1 768w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image9.jpg?resize=984%2C1536&ssl=1 984w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image9.jpg?w=1249&ssl=1 1249w" sizes="(max-width: 656px) 100vw, 656px" data-recalc-dims="1" referrerpolicy="no-referrer"></figure>
<figcaption class="blocks-gallery-caption wp-element-caption"><strong>Segment cards.</strong> Examples of common personalization segments: at a minimum, you will need to consider the anonymous, guest, and logged in user types. Segmentation can get dramatically more complex from there.</figcaption></figure>
<h3 class="wp-block-heading">Actionable Data</h3>
<p>Every organization with any digital presence has data. It’s a matter of asking what data you can ethically collect on users, its inherent reliability and value, as to how can you use it (sometimes known as “data activation.”) Fortunately, the tide is turning to first-party data: a recent study by Twilio estimates some <strong>80% of businesses are using at least some type of first-party data</strong> to personalize the customer experience. </p>
<figure class="wp-block-image size-large"><img decoding="async" loading="lazy" width="960" height="669" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image6.png?resize=960%2C669&ssl=1" alt="Chart that answers the question "Why is your company focusing on using first-party data for personalization?" The top answer (at 53%) is "it’s higher quality." That is followed by "It’s easier to manage" (46%), "it provides better privacy" (45%), "it’s easier to obtain" (42%), "it’s more cost-effective" (40%), "it’s more ethical" (37%), "our customers want us to" (36%), "it’s the industry norm" (27%), "it’s easier to comply with regulations" (27%), and "we are phasing out 3rd party cookies" (21%)." class="wp-image-7173686" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image6.png?resize=1024%2C714&ssl=1 1024w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image6.png?resize=300%2C209&ssl=1 300w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image6.png?resize=768%2C536&ssl=1 768w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image6.png?w=1500&ssl=1 1500w" sizes="(max-width: 960px) 100vw, 960px" data-recalc-dims="1" referrerpolicy="no-referrer"><figcaption class="wp-element-caption"><small>Source: “The State of Personalization 2021” by Twilio. Survey respondents were n=2,700 adult consumers who have purchased something online in the past 6 months, and n=300 adult manager+ decision-makers at consumer-facing companies that provide goods and/or services online. Respondents were from the United States, United Kingdom, Australia, and New Zealand.Data was collected from April 8 to April 20, 2021.</small></figcaption></figure>
<p>First-party data represents multiple advantages on the UX front, including being relatively simple to collect, more likely to be accurate, and less susceptible to the “creep factor” of third-party data. So a key part of your UX strategy should be to determine what the best form of data collection is on your audiences. Here are some examples:</p>
<figure class="wp-block-gallery has-nested-images columns-default is-cropped wp-block-gallery-13 is-layout-flex wp-block-gallery-is-layout-flex">
<figure class="wp-block-image size-large"><img decoding="async" loading="lazy" width="656" height="1024" data-id="7173691" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image15.jpg?resize=656%2C1024&ssl=1" alt="Quizes: Tell us what you like" class="wp-image-7173691" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image15.jpg?resize=656%2C1024&ssl=1 656w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image15.jpg?resize=192%2C300&ssl=1 192w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image15.jpg?resize=768%2C1198&ssl=1 768w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image15.jpg?resize=984%2C1536&ssl=1 984w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image15.jpg?w=1249&ssl=1 1249w" sizes="(max-width: 656px) 100vw, 656px" data-recalc-dims="1" referrerpolicy="no-referrer"></figure>
<figure class="wp-block-image size-large"><img decoding="async" loading="lazy" width="656" height="1024" data-id="7173689" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image5.jpg?resize=656%2C1024&ssl=1" alt="Behavioral profiling: Males 40+ who wear fedoras" class="wp-image-7173689" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image5.jpg?resize=656%2C1024&ssl=1 656w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image5.jpg?resize=192%2C300&ssl=1 192w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image5.jpg?resize=768%2C1198&ssl=1 768w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image5.jpg?resize=984%2C1536&ssl=1 984w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image5.jpg?w=1249&ssl=1 1249w" sizes="(max-width: 656px) 100vw, 656px" data-recalc-dims="1" referrerpolicy="no-referrer"></figure>
<figure class="wp-block-image size-large"><img decoding="async" loading="lazy" width="656" height="1024" data-id="7173688" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image1.jpg?resize=656%2C1024&ssl=1" alt="Campaign Source: Your discount code 29780…" class="wp-image-7173688" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image1.jpg?resize=656%2C1024&ssl=1 656w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image1.jpg?resize=192%2C300&ssl=1 192w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image1.jpg?resize=768%2C1198&ssl=1 768w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image1.jpg?resize=984%2C1536&ssl=1 984w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image1.jpg?w=1249&ssl=1 1249w" sizes="(max-width: 656px) 100vw, 656px" data-recalc-dims="1" referrerpolicy="no-referrer"></figure>
</figure>
<figure class="wp-block-image size-full"><img decoding="async" loading="lazy" width="822" height="568" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image4.png?resize=822%2C568&ssl=1" alt="Chart showing the impact of personalization across different phases of personalization maturity. It shows that effort is high in the early phases, but drops off quickly starting in phase 3 (machine learning) while at the same time conversion rates, AOV, and ROI increase from a relatively low level to off the chart." class="wp-image-7173692" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image4.png?w=822&ssl=1 822w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image4.png?resize=300%2C207&ssl=1 300w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image4.png?resize=768%2C531&ssl=1 768w" sizes="(max-width: 822px) 100vw, 822px" data-recalc-dims="1" referrerpolicy="no-referrer"><figcaption class="wp-element-caption"><strong>Figure 1.1.2:</strong> Example of a personalization maturity curve, showing progression from basic recommendations functionality to true individualization. Credit: https://kibocommerce.com/blog/kibos-personalization-maturity-chart/</figcaption></figure>
<p>There is a progression of profiling when it comes to recognizing and making decisioning about different audiences and their signals. It tends to move towards more granular constructs about smaller and smaller cohorts of users as time and confidence and data volume grow.</p>
<p>While some combination of <strong>implicit / explicit</strong> <strong>data</strong> is generally a prerequisite for any implementation (more commonly referred to as first party and third-party data) <strong>ML efforts</strong> are typically not cost-effective directly out of the box. This is because a strong data backbone and content repository is a prerequisite for optimization. But these approaches should be considered as part of the larger roadmap and may indeed help accelerate the organization’s overall progress. Typically at this point you will partner with key stakeholders and product owners to design a <strong>profiling model</strong>. The profiling model includes defining approach to configuring profiles, profile keys, profile cards and pattern cards. A multi-faceted approach to profiling which makes it scalable.</p>
<h2 class="wp-block-heading">Pulling it Together</h2>
<p>While the cards comprise the starting point to an inventory of sorts (we provide blanks for you to tailor your own), a set of potential levers and motivations for the style of personalization activities you aspire to deliver, they are more valuable when thought of in a grouping. </p>
<p>In assembling a card “hand”, one can begin to trace the entire trajectory from leadership focus down through a strategic and tactical execution. It is also at the heart of the way both co-authors have conducted workshops in assembling a program backlog—which is a fine subject for another article.</p>
<p>In the meantime, what is important to note is that each colored class of card is helpful to survey in understanding the range of choices potentially at your disposal, it is threading through and making concrete decisions about for whom this decisioning will be made: where, when, and how.</p>
<figure class="wp-block-image size-full"><img decoding="async" loading="lazy" width="960" height="578" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image26.jpg?resize=960%2C578&ssl=1" alt="Cards on a table. At the top: Function is the north star & customer satisfaction is the goal. User segment is unknown, the actionable data is a quiz, context is a nudge, campaign is to make something easier, and the touchpoint is a banner." class="wp-image-7173693" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image26.jpg?w=1024&ssl=1 1024w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image26.jpg?resize=300%2C180&ssl=1 300w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image26.jpg?resize=768%2C462&ssl=1 768w" sizes="(max-width: 960px) 100vw, 960px" data-recalc-dims="1" referrerpolicy="no-referrer"><figcaption class="wp-element-caption"><strong>Scenario A:</strong> We want to use personalization to improve customer satisfaction on the website. For unknown users, we will create a short quiz to better identify what the user has come to do. This is sometimes referred to as “badging” a user in onboarding contexts, to better characterize their present intent and context.</figcaption></figure>
<h2 class="wp-block-heading">Lay Down Your Cards</h2>
<p>Any sustainable personalization strategy must consider near, mid and long-term goals. Even with the leading CMS platforms like Sitecore and Adobe or the most exciting composable CMS DXP out there, there is simply no “easy button” wherein a personalization program can be stood up and immediately view meaningful results. That said, there is a common grammar to all personalization activities, just like every sentence has nouns and verbs. These cards attempt to map that territory.</p>
<p></p>
]]></description>
<pubDate>Thu, 08 Dec 2022 15:00:00 GMT</pubDate>
<guid isPermaLink="false">https://alistapart.com/article/personalization-pyramid/</guid>
<link>https://alistapart.com/article/personalization-pyramid/</link>
<author><![CDATA[agustafson]]></author>
<category>Content</category>
<category>Interaction Design</category>
</item>
<item>
<title><![CDATA[Mobile-First CSS: Is It Time for a Rethink?]]></title>
<description><![CDATA[<p class="intro">The mobile-first design methodology is great—it focuses on what really matters to the user, it’s well-practiced, and it’s been a common design pattern for years. So developing your CSS mobile-first should also be great, too…right? </p>
<p>Well, not necessarily. Classic mobile-first CSS development is based on the principle of overwriting style declarations: you begin your CSS with default style declarations, and overwrite and/or add new styles as you add breakpoints with <code>min-width</code> media queries for larger viewports (for a good overview see “<a href="https://www.mightyminnow.com/2013/11/what-is-mobile-first-css-and-why-does-it-rock/">What is Mobile First CSS and Why Does It Rock?</a>”). But all those exceptions create complexity and inefficiency, which in turn can lead to an increased testing effort and a code base that’s harder to maintain. Admit it—how many of us willingly want that?</p>
<p>On your own projects, mobile-first CSS may yet be the best tool for the job, but first you need to evaluate just how appropriate it is in light of the visual design and user interactions you’re working on. To help you get started, here’s how I go about tackling the factors you need to watch for, and I’ll discuss some alternate solutions if mobile-first doesn’t seem to suit your project.</p>
<h2 class="wp-block-heading">Advantages of mobile-first</h2>
<p>Some of the things to like with mobile-first CSS development—and why it’s been the de facto development methodology for so long—make a lot of sense:</p>
<p><strong>Development hierarchy. </strong>One thing you undoubtedly get from mobile-first is a nice development hierarchy—you just focus on the mobile view and get developing. </p>
<p><strong>Tried and tested. </strong>It’s a tried and tested methodology that’s worked for years for ... |
http://localhost:1200/alistapart/code - Success ✔️<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"
>
<channel>
<title><![CDATA[A List Apart]]></title>
<link>https://alistapart.com/blog/topic/code</link>
<atom:link href="http://localhost:1200/alistapart/code" rel="self" type="application/rss+xml" />
<description><![CDATA[Code Articles on aListApart.com - Made with love by RSSHub(https://github.com/DIYgod/RSSHub)]]></description>
<generator>RSSHub</generator>
<webMaster>i@diygod.me (DIYgod)</webMaster>
<language>en-us</language>
<lastBuildDate>Thu, 10 Aug 2023 16:54:28 GMT</lastBuildDate>
<ttl>5</ttl>
<item>
<title><![CDATA[Mobile-First CSS: Is It Time for a Rethink?]]></title>
<description><![CDATA[<p class="intro">The mobile-first design methodology is great—it focuses on what really matters to the user, it’s well-practiced, and it’s been a common design pattern for years. So developing your CSS mobile-first should also be great, too…right? </p>
<p>Well, not necessarily. Classic mobile-first CSS development is based on the principle of overwriting style declarations: you begin your CSS with default style declarations, and overwrite and/or add new styles as you add breakpoints with <code>min-width</code> media queries for larger viewports (for a good overview see “<a href="https://www.mightyminnow.com/2013/11/what-is-mobile-first-css-and-why-does-it-rock/">What is Mobile First CSS and Why Does It Rock?</a>”). But all those exceptions create complexity and inefficiency, which in turn can lead to an increased testing effort and a code base that’s harder to maintain. Admit it—how many of us willingly want that?</p>
<p>On your own projects, mobile-first CSS may yet be the best tool for the job, but first you need to evaluate just how appropriate it is in light of the visual design and user interactions you’re working on. To help you get started, here’s how I go about tackling the factors you need to watch for, and I’ll discuss some alternate solutions if mobile-first doesn’t seem to suit your project.</p>
<h2 class="wp-block-heading">Advantages of mobile-first</h2>
<p>Some of the things to like with mobile-first CSS development—and why it’s been the de facto development methodology for so long—make a lot of sense:</p>
<p><strong>Development hierarchy. </strong>One thing you undoubtedly get from mobile-first is a nice development hierarchy—you just focus on the mobile view and get developing. </p>
<p><strong>Tried and tested. </strong>It’s a tried and tested methodology that’s worked for years for a reason: it solves a problem really well.</p>
<p><strong>Prioritizes the mobile view</strong>. The mobile view is the<strong> </strong>simplest<strong> </strong>and arguably the most important, as it <strong>encompasses all the key user journeys</strong>, and often accounts for a <strong>higher proportion of user visits</strong> (depending on the project). </p>
<p><strong>Prevents desktop-centric development. </strong>As development is done using desktop computers, it can be tempting to initially focus on the desktop view. But thinking about mobile from the start prevents us from getting stuck later on; no one wants to spend their time retrofitting a desktop-centric site to work on mobile devices!</p>
<h2 class="wp-block-heading">Disadvantages of mobile-first</h2>
<p>Setting style declarations and then overwriting them at higher breakpoints can lead to undesirable ramifications:</p>
<p><strong>More complexity. </strong>The farther up the breakpoint hierarchy you go, the more unnecessary code you inherit from lower breakpoints. </p>
<p><strong>Higher CSS specificity. </strong>Styles that have been reverted to their browser default value in a class name declaration now have a higher specificity. This can be a headache on large projects when you want to keep the CSS selectors as simple as possible.</p>
<p><strong>Requires more regression testing. </strong>Changes to the CSS at a lower view (like adding a new style) requires all higher breakpoints to be regression tested.</p>
<p><strong>The browser can’t prioritize CSS downloads. </strong>At wider breakpoints, classic mobile-first <code>min-width</code> media queries don’t leverage the browser’s capability to download CSS files in priority order.</p>
<h2 class="wp-block-heading">The problem of property value overrides</h2>
<p>There is nothing inherently wrong with overwriting values; CSS was designed to do just that. Still, inheriting incorrect values is unhelpful and can be burdensome and inefficient. It can also lead to increased style specificity when you have to overwrite styles to reset them back to their defaults, something that may cause issues later on, especially if you are using a combination of bespoke CSS and utility classes. We won’t be able to use a utility class for a style that has been reset with a higher specificity.</p>
<p>With this in mind, I’m developing CSS with a focus on the default values much more these days. Since there’s no specific order, and no chains of specific values to keep track of, this frees me to develop breakpoints <em>simultaneously</em>. I concentrate on finding common styles and isolating the specific exceptions in closed media query ranges (that is, any range with a <code>max-width</code> set). </p>
<p>This approach opens up some opportunities, as you can look at each breakpoint as a clean slate. If a component’s layout looks like it should be based on Flexbox at all breakpoints, it’s fine and can be coded in the default style sheet. But if it looks like Grid would be much better for large screens and Flexbox for mobile, these can both be done entirely independently when the CSS is put into closed media query ranges. Also, developing simultaneously requires you to have a good understanding of any given component in all breakpoints up front. This can help surface issues in the design earlier in the development process. We don’t want to get stuck down a rabbit hole building a complex component for mobile, and then get the designs for desktop and find they are equally complex and incompatible with the HTML we created for the mobile view! </p>
<p>Though this approach isn’t going to suit everyone, I encourage you to give it a try. There are plenty of tools out there to help with concurrent development, such as <a href="https://responsively.app/">Responsively App</a>, <a href="https://blisk.io/">Blisk</a>, and many others. </p>
<p>Having said that, I don’t feel the order itself is particularly relevant. If you are comfortable with focusing on the mobile view, have a good understanding of the requirements for other breakpoints, and prefer to work on one device at a time, then by all means stick with the classic development order. The important thing is to identify common styles and exceptions so you can put them in the relevant stylesheet—a sort of manual tree-shaking process! Personally, I find this a little easier when working on a component across breakpoints, but that’s by no means a requirement.</p>
<h2 class="wp-block-heading">Closed media query ranges in practice </h2>
<p>In classic mobile-first CSS we overwrite the styles, but we can avoid this by using media query ranges. To illustrate the difference (I’m using SCSS for brevity), let’s assume there are three visual designs: </p>
<ul><li>smaller than 768</li><li>from 768 to below 1024</li><li>1024 and anything larger </li></ul>
<p>Take a simple example where a block-level element has a default <code>padding</code> of “20px,” which is overwritten at tablet to be “40px” and set back to “20px” on desktop.</p>
<figure class="wp-block-table">
<table><tbody>
<tr>
<td valign="top"><p>Classic <code>min-width</code> mobile-first</p>
<pre><code class="language-css">.my-block {
padding: 20px;
@media (min-width: 768px) {
padding: 40px;
}
@media (min-width: 1024px) {
padding: 20px;
}
}</code></pre></td>
<td valign="top"><p>Closed media query range</p>
<pre><code class="language-css">.my-block {
padding: 20px;
@media (min-width: 768px) and (max-width: 1023.98px) {
padding: 40px;
}
}</code></pre></td>
</tr>
</tbody></table>
</figure>
<p>The subtle difference is that the mobile-first example sets the default <code>padding</code> to “20px” and then overwrites it at each breakpoint, setting it three times in total. In contrast, the second example sets the default <code>padding</code> to “20px” and only overrides it at the relevant breakpoint where it isn’t the default value (in this instance, tablet is the exception).</p>
<p>The goal is to: </p>
<ul><li>Only set styles when needed. </li><li>Not set them with the <em>expectation</em> of overwriting them later on, again and again. </li></ul>
<p>To this end, closed media query ranges are our best friend. If we need to make a change to any given view, we make it in the CSS media query range that applies to the specific breakpoint. We’ll be much less likely to introduce unwanted alterations, and our regression testing only needs to focus on the breakpoint we have actually edited. </p>
<p>Taking the above example, if we find that <code>.my-block</code> spacing on desktop is already accounted for by the margin at that breakpoint, and since we want to remove the padding altogether, we could do this by setting the mobile <code>padding</code> in a closed media query range.<br></p>
<figure class="wp-block-table">
<pre><code class="language-css">.my-block {
@media (max-width: 767.98px) {
padding: 20px;
}
@media (min-width: 768px) and (max-width: 1023.98px) {
padding: 40px;
}
}</code></pre>
</figure>
<p>The browser default <code>padding</code> for our block is “0,” so instead of adding a desktop media query and using <code>unset</code> or “0” for the <code>padding</code> value (which we would need with mobile-first), we can wrap the mobile <code>padding</code> in a closed media query (since it is now also an exception) so it won’t get picked up at wider breakpoints. At the desktop breakpoint, we won’t need to set any <code>padding</code> style, as we want the browser default value.</p>
<h2 class="wp-block-heading">Bundling versus separating the CSS</h2>
<p>Back in the day, keeping the number of requests to a minimum was very important due to the browser’s limit of concurrent requests (typically around six). As a consequence, the use of image sprites and CSS bundling was the norm, with all the CSS being downloaded in one go, as one stylesheet with highest priority. </p>
<p>With HTTP/2 and HTTP/3 now on the scene, the number of requests is no longer the big deal it used to be. This allows us to separate the CSS into multiple files by media query. The clear benefit of this is the browser can now request the CSS it currently needs with a higher priority than the CSS it doesn’t. This is more performant and can reduce the overall time <a href="https://web.dev/critical-rendering-path-render-blocking-css/">page rendering is blocked</a>.</p>
<h3 class="wp-block-heading">Which HTTP version are you using?</h3>
<p>To determine which version of HTTP you’re using, go to your website and open your browser’s dev tools. Next, select the <strong>Network</strong> tab and make sure the <strong>Protocol</strong> column is visible. If “h2” is listed under <strong>Protocol</strong>, it means HTTP/2 is being used. </p>
<p><em>Note: to view the Protocol in your browser’s dev tools, go to the </em><strong><em>Network</em></strong><em> tab, reload your page, right-click any column header (e.g., </em><strong><em>Name</em></strong><em>), and check the </em><strong><em>Protocol</em></strong><em> column.</em></p>
<figure class="wp-block-image"><img decoding="async" src="https://lh4.googleusercontent.com/O8lxNeIY3Hb0YDs2EP7QFhGdGsBXOG7mSTCdAJBd5xkm-6RwrpkS1BN63W7RurVCP3nOH9sNpAR9JNGvIGnUTzG0NYm4sUqI5bU2QPhXYEawmKfeUJ_6YwWAIid2ZDHEdRzaQ1LxzUNTGbGk5g" alt="Chrome dev tools, Network tab filtered by document, Protocol column" referrerpolicy="no-referrer"><figcaption><em>Note: for a summarized comparison, see ImageKit’s “</em><a href="https://imagekit.io/blog/http2-vs-http1-performance/"><em>HTTP/2 vs. HTTP/1</em></a><em>.”</em></figcaption></figure>
<p>Also, if your site is still using HTTP/1...WHY?!! What are you waiting for? There is <a href="https://caniuse.com/http2">excellent user support for HTTP/2</a>.</p>
<h2 class="wp-block-heading">Splitting the CSS</h2>
<p>Separating the CSS into individual files is a worthwhile task. Linking the separate CSS files using the relevant <code>media</code> attribute allows the browser to identify which files are needed immediately (because they’re render-blocking) and which can be deferred. Based on this, it allocates each file an appropriate priority.</p>
<p>In the following example of a website visited on a mobile breakpoint, we can see the mobile and default CSS are loaded with “Highest” priority, as they are currently needed to render the page. The remaining CSS files (print, tablet, and desktop) are still downloaded in case they’ll be needed later, but with “Lowest” priority. </p>
<figure class="wp-block-image"><img decoding="async" src="https://lh5.googleusercontent.com/RZOq-S7kbVsavDiFctQl6STFgGm6puwG8L22V6j6U1vUfo73Opq3Cspj2N94T2BU5lpYUD7Bb_4krFCXlePvBE8xXJVMFwbc_At8pzc-C5ug-6lrPViwMIIXgbKiJA-2fQ3beDoYfkCflCVgwg" alt="Chrome dev tools, Network tab filtered by css, Priority column" referrerpolicy="no-referrer"></figure>
<p>With<strong> bundled CSS</strong>, the browser will have to download the CSS file and parse it before rendering can start.<br><br>While, as noted, with the <strong>CSS separated into different files</strong> linked and marked up with the relevant <code>media</code> attribute, the browser can prioritize the files it currently needs. Using closed media query ranges allows the browser to do this at all widths, as opposed to classic mobile-first <code>min-width</code> queries, where the desktop browser would have to download all the CSS with Highest priority. We can’t assume that desktop users always have a fast connection. For instance, in many rural areas, internet connection speeds are still slow. </p>
<p>The media queries and number of separate CSS files will vary from project to project based on project requirements, but might look similar to the example below.</p>
<figure class="wp-block-table">
<table><tbody>
<tr>
<td valign="top">
<p>Bundled CSS</p>
<code><link href="site.css" rel="stylesheet"></code><br><br>
<p>This single file contains all the CSS, including all media queries, and it will be downloaded with Highest priority.</p>
</td>
<td valign="top">
<p>Separated CSS</p>
<code><link href="default.css" rel="stylesheet"><link href="mobile.css" media="screen and (max-width: 767.98px)" rel="stylesheet"><link href="tablet.css" media="screen and (min-width: 768px) and (max-width: 1083.98px)" rel="stylesheet"><link href="desktop.css" media="screen and (min-width: 1084px)" rel="stylesheet"><link href="print.css" media="print" rel="stylesheet"></code><br><br>
<p>Separating the CSS and specifying a <code>media</code> attribute value on each <code>link</code> tag allows the browser to prioritize what it currently needs. Out of the five files listed above, two will be downloaded with Highest priority: the default file, and the file that matches the current media query. The others will be downloaded with Lowest priority.</p>
</td>
</tr>
</tbody></table>
</figure>
<p>Depending on the project’s deployment strategy, a change to one file (<code>mobile.css</code>, for example) would only require the QA team to regression test on devices in that specific media query range. Compare that to the prospect of deploying the single bundled <code>site.css</code> file, an approach that would normally trigger a full regression test.</p>
<h2 class="wp-block-heading">Moving on</h2>
<p>The uptake of mobile-first CSS was a really important milestone in web development; it has helped front-end developers focus on mobile web applications, rather than developing sites on desktop and then attempting to retrofit them to work on other devices.</p>
<p>I don’t think anyone wants to return to that development model again, but it’s important we don’t lose sight of the issue it highlighted: that things can easily get convoluted and less efficient if we prioritize one particular device—any device—over others. For this reason, focusing on the CSS in its own right, always mindful of what is the default setting and what’s an exception, seems like the natural next step. I’ve started noticing small simplifications in my own CSS, as well as other developers’, and that testing and maintenance work is also a bit more simplified and productive. </p>
<p>In general, simplifying CSS rule creation whenever we can is ultimately a cleaner approach than going around in circles of overrides. But whichever methodology you choose, it needs to suit the project. Mobile-first may—or may not—turn out to be the best choice for what’s involved, but first you need to solidly understand the trade-offs you’re stepping into.</p>
]]></description>
<pubDate>Thu, 09 Jun 2022 02:13:10 GMT</pubDate>
<guid isPermaLink="false">https://alistapart.com/article/mobile-first-css-is-it-time-for-a-rethink/</guid>
<link>https://alistapart.com/article/mobile-first-css-is-it-time-for-a-rethink/</link>
<author><![CDATA[brandongregory]]></author>
<category>Code</category>
<category>CSS</category>
</item>
<item>
<title><![CDATA[Breaking Out of the Box]]></title>
<description><![CDATA[<p>CSS is about styling boxes. In fact, the whole web is made of boxes, from the browser viewport to elements on a page. But every once in a while a new feature comes along that makes us rethink our design approach.</p>
<p><a href="https://www.w3.org/TR/css-round-display-1/">Round displays</a>, for example, make it fun to play with circular clip areas. <a href="https://css-tricks.com/the-notch-and-css/">Mobile screen notches</a> and <a href="https://www.w3.org/TR/virtual-keyboard/">virtual keyboards</a> offer challenges to best organize content that stays clear of them. And <a href="https://blogs.windows.com/msedgedev/2020/09/14/introducing-dual-screen-foldable-web-apis/">dual screen or foldable devices</a> make us rethink how to best use available space in a number of different <a href="https://w3c.github.io/device-posture/">device postures</a>.</p>
<figure class="wp-block-image size-large"><img decoding="async" loading="lazy" width="960" height="452" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig1.png?resize=960%2C452&ssl=1" alt="" class="wp-image-7173226" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig1.png?resize=1024%2C482&ssl=1 1024w, https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig1.png?resize=300%2C141&ssl=1 300w, https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig1.png?resize=768%2C362&ssl=1 768w, https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig1.png?resize=1536%2C724&ssl=1 1536w, https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig1.png?resize=2048%2C965&ssl=1 2048w, https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig1.png?w=1920&ssl=1 1920w, https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig1.png?w=2880&ssl=1 2880w" sizes="(max-width: 960px) 100vw, 960px" data-recalc-dims="1" referrerpolicy="no-referrer"><figcaption><em>Sketches of a round display, a common rectangular mobile display, and a device with a foldable display.</em></figcaption></figure>
<p>These recent evolutions of the web platform made it both more challenging and more interesting to design products. They’re great opportunities for us to break out of our rectangular boxes.</p>
<p>I’d like to talk about a new feature similar to the above: the Window Controls Overlay for Progressive Web Apps (PWAs).</p>
<p><a href="https://alistapart.com/article/yes-that-web-project-should-be-a-pwa/">Progressive Web Apps</a> are blurring the lines between apps and websites. They combine the best of both worlds. On one hand, they’re stable, linkable, searchable, and responsive just like websites. On the other hand, they provide additional powerful capabilities, work offline, and read files just like native apps.</p>
<p>As a design surface, PWAs are really interesting because they challenge us to think about what mixing web and device-native user interfaces can be. On desktop devices in particular, we have more than <a href="https://en.wikipedia.org/wiki/History_of_the_graphical_user_interface">40 years of history</a> telling us what applications should look like, and it can be hard to break out of this mental model.</p>
<p>At the end of the day though, PWAs on desktop are constrained to the window they appear in: a rectangle with a title bar at the top.</p>
<p>Here’s what a typical desktop PWA app looks like:</p>
<figure class="wp-block-image size-large"><img decoding="async" loading="lazy" width="960" height="303" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig2.png?resize=960%2C303&ssl=1" alt="" class="wp-image-7173227" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig2.png?resize=1024%2C323&ssl=1 1024w, https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig2.png?resize=300%2C95&ssl=1 300w, https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig2.png?resize=768%2C242&ssl=1 768w, https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig2.png?resize=1536%2C485&ssl=1 1536w, https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig2.png?resize=2048%2C646&ssl=1 2048w, https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig2.png?w=1920&ssl=1 1920w, https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig2.png?w=2880&ssl=1 2880w" sizes="(max-width: 960px) 100vw, 960px" data-recalc-dims="1" referrerpolicy="no-referrer"><figcaption><em>Sketches of two rectangular user interfaces representing the desktop Progressive Web App status quo on the macOS and Windows operating systems, respectively. </em></figcaption></figure>
<p>Sure, as the author of a PWA, you get to choose the color of the title bar (using the Web Application Manifest <a href="https://developer.mozilla.org/en-US/docs/Web/Manifest/theme_color"><strong>theme_color</strong></a> property), but that’s about it.</p>
<p>What if we could think outside this box, and reclaim the real estate of the app’s entire window? Doing so would give us a chance to make our apps more beautiful and feel more integrated in the operating system.</p>
<p>This is exactly what the <a href="https://web.dev/window-controls-overlay/">Window Controls Overlay</a> offers. This new PWA functionality makes it possible to take advantage of the full surface area of the app, including where the title bar normally appears.</p>
<h2 class="wp-block-heading">About the title bar and window controls</h2>
<p>Let’s start with an explanation of what the title bar and window controls are.</p>
<p>The <em>title bar</em> is the area displayed at the top of an app window, which usually contains the app’s name. <em>Window controls</em> are the affordances, or buttons, that make it possible to minimize, maximize, or close the app’s window, and are also displayed at the top.</p>
<figure class="wp-block-image"><img decoding="async" src="https://lh5.googleusercontent.com/1s_lFmqiRm6Fv3wzeELtsDDiDvOkEJuSRH5K9YIrZZ8rh8rYCxUqbSnfd-f7YrsRvcDzF67fexnEJFlDtw53SKKmOgVk8sv_VUyCQveoR18HkNgACPxcQTtEOb6SmEuRIDlX3EcI" alt="" referrerpolicy="no-referrer"><figcaption><em>A sketch of a rectangular application user interface highlighting the title bar area and window control buttons.</em></figcaption></figure>
<p>Window Controls Overlay removes the physical constraint of the title bar and window controls areas. It frees up the full height of the app window, enabling the title bar and window control buttons to be overlaid on top of the application’s web content. </p>
<figure class="wp-block-image"><img decoding="async" src="https://lh6.googleusercontent.com/k_6o1fBePhbjvtmxoTW3tG1134Gvbo31r2fy7zxmOB39d_eKpjThbh7QL8pVXrA1aLvEWzkoJ_rY4af451BU9XyKZXbSouCTvDJMnRKGlcOhcEpXw_rjQAR8_SFjhrm_-22OxKiR" alt="" referrerpolicy="no-referrer"><figcaption><em>A sketch of a rectangular application user interface using Window Controls Overlay. The title bar and window controls are no longer in an area separated from the app’s content.</em></figcaption></figure>
<p>If you are reading this article on a desktop computer, take a quick look at other apps. Chances are they’re already doing something similar to this. In fact, the very web browser you are using to read this uses the top area to display tabs.</p>
<figure class="wp-block-image"><img decoding="async" src="https://lh6.googleusercontent.com/BLL9Rc5othPsw6xYApyyNOZ73j32wi4XkyoZpl4QOv0OL4MnxMe3bl1xLR0O7WSoAvi3KhyeP83hUh4-EezTmGg2axN4RiOVtgiF5ZiapcjUL6gtLqExZOHGCtkOBbthMTgh5Tmr" alt="" referrerpolicy="no-referrer"><figcaption><em>A screenshot of the top area of a browser’s user interface showing a group of tabs that share the same horizontal space as the app window controls.</em></figcaption></figure>
<p>Spotify displays album artwork all the way to the top edge of the application window.</p>
<figure class="wp-block-image"><img decoding="async" src="https://lh3.googleusercontent.com/SzPcq94_7Yu_ARf13Z6dRD0tpdlPM_MdrY-7CA_mv4Yu3fBIL3pJnXirP83cCDVoQxnnIEwDoBwbGzfftHmZ3PZZUfsw_oP-m4QLkB2SqekX8JupR9_xmI0tG1q65IfNFbnXIHUh" alt="" referrerpolicy="no-referrer"><figcaption><em>A screenshot of an album in Spotify’s desktop application. Album artwork spans the entire width of the main content area, all the way to the top and right edges of the window, and the right edge of the main navigation area on the left side. The application and album navigation controls are overlaid directly on top of the album artwork.</em></figcaption></figure>
<p>Microsoft Word uses the available title bar space to display the auto-save and search functionalities, and more.</p>
<figure class="wp-block-image"><img decoding="async" src="https://lh6.googleusercontent.com/VdREVwFFjYHxHF0Gg3l079hxsa8WKPEWiuvuL7cWbGnEDJ2yc3JiOWQK5lUyaeEgzpd1Przji0cNLeooPD7riPKbcMixa6IkXanprdqPJVkQrYSerxSaNmzbJPd1YsA55mlYd9xt" alt="" referrerpolicy="no-referrer"><figcaption><em>A screenshot of Microsoft Word’s toolbar interface. Document file information, search, and other functionality appear at the top of the window, sharing the same horizontal space as the app’s window controls.</em></figcaption></figure>
<p>The whole point of this feature is to allow you to make use of this space with your own content while providing a way to account for the window control buttons. And it enables you to offer this modified experience on a range of platforms while not adversely affecting the experience on browsers or devices that don’t support Window Controls Overlay. After all, PWAs are all about <a href="https://alistapart.com/article/understandingprogressiveenhancement/">progressive enhancement</a>, so this feature is a chance to enhance your app to use this extra space when it’s available.</p>
<h2 class="wp-block-heading">Let’s use the feature</h2>
<p>For the rest of this article, we’ll be working on a demo app to learn more about using the feature.</p>
<p>The demo app is called <a href="https://stupefied-edison-a4ee55.netlify.app/">1DIV</a>. It’s a simple CSS playground where users can create designs using CSS and a single HTML element.</p>
<p>The app has two pages. The first lists the existing CSS designs you’ve created:</p>
<figure class="wp-block-image"><img decoding="async" src="https://lh3.googleusercontent.com/pW2iUTucKfwMJZrAlPGK19vVnEPaHjYT4N-18P-vm9qkhAdGJcRBMexOCu1q9nN9BAfZ7MH6itNP__kY4HPl9uVPucXkbmSX-E9g6AdVAI_uu6TyEsEdH0LUCXdN1f4kqZNgDr30" alt="" referrerpolicy="no-referrer"><figcaption><em>A screenshot of the 1DIV app displaying a thumbnail grid of CSS designs a user created.</em></figcaption></figure>
<p>The second page enables you to create and edit CSS designs:</p>
<figure class="wp-block-image"><img decoding="async" src="https://lh5.googleusercontent.com/faaJ3uHbzXg-TFinvOqR_7gyjVPvlk7fuVWuN4aIH6IUxXNAp4GXtIcuVPpo6bd1IOKO1_EMDt4pUgErUh_X2_2r3WnkQ4PzovPp6Zjg0l98W9NBrHA0xAuTNf0uNVBatRsMJzEm" alt="" referrerpolicy="no-referrer"><figcaption><em>A screenshot of the 1DIV app editor page. The top half of the window displays a rendered CSS design, and a text editor on the bottom half of the window displays the CSS used to create it.</em></figcaption></figure>
<p>Since I’ve added a simple web manifest and service worker, we can install the app as a PWA on desktop. Here is what it looks like on macOS:</p>
<figure class="wp-block-image size-large"><img decoding="async" loading="lazy" width="960" height="466" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig10.png?resize=960%2C466&ssl=1" alt="" class="wp-image-7173228" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig10.png?resize=1024%2C497&ssl=1 1024w, https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig10.png?resize=300%2C146&ssl=1 300w, https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig10.png?resize=768%2C373&ssl=1 768w, https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig10.png?resize=1536%2C745&ssl=1 1536w, https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig10.png?w=1540&ssl=1 1540w" sizes="(max-width: 960px) 100vw, 960px" data-recalc-dims="1" referrerpolicy="no-referrer"><figcaption><em>Screenshots of the 1DIV app thumbnail view and CSS editor view on macOS. This version of the app’s window has a separate control bar at the top for the app name and window control buttons.</em></figcaption></figure>
<p>And on Windows:</p>
<figure class="wp-block-image size-large"><img decoding="async" loading="lazy" width="960" height="501" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig11.png?resize=960%2C501&ssl=1" alt="" class="wp-image-7173229" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig11.png?resize=1024%2C534&ssl=1 1024w, https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig11.png?resize=300%2C157&ssl=1 300w, https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig11.png?resize=768%2C401&ssl=1 768w, https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig11.png?resize=1536%2C802&ssl=1 1536w, https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig11.png?resize=2048%2C1069&ssl=1 2048w, https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig11.png?w=1920&ssl=1 1920w" sizes="(max-width: 960px) 100vw, 960px" data-recalc-dims="1" referrerpolicy="no-referrer"><figcaption><em>Screenshots of the 1DIV app thumbnail view and CSS editor view on the Windows operating system. This version of the app’s window also has a separate control bar at the top for the app name and window control buttons.</em></figcaption></figure>
<p>Our app is looking good, but the white title bar in the first page is wasted space. In the second page, it would be really nice if the design area went all the way to the top of the app window.</p>
<p>Let’s use the Window Controls Overlay feature to improve this.</p>
<h2 class="wp-block-heading">Enabling Window Controls Overlay</h2>
<p>The feature is still experimental at the moment. To try it, you need to enable it in one of the supported browsers.</p>
<p>As of now, it has been implemented in Chromium, as a collaboration between Microsoft and Google. We can therefore use it in Chrome or Edge by going to the internal <strong>about://flags</strong> page, and enabling the <strong>Desktop PWA Window Controls Overlay</strong> flag.</p>
<h2 class="wp-block-heading">Using Window Controls Overlay</h2>
<p>To use the feature, we need to add the following <strong>display_override</strong> member to our web app’s manifest file:</p>
<pre><code class="language-javascript">{
"name": "1DIV",
"description": "1DIV is a mini CSS playground",
"lang": "en-US",
"start_url": "/",
"theme_color": "#ffffff",
"background_color": "#ffffff",
"display_override": [
"window-controls-overlay"
],
"icons": [
...
]
}
</code></pre>
<p>On the surface, the feature is really simple to use. This manifest change is the only thing we need to make the title bar disappear and turn the window controls into an overlay.</p>
<p>However, to provide a great experience for all users regardless of what device or browser they use, and to make the most of the title bar area in our design, we’ll need a bit of CSS and JavaScript code.</p>
<p>Here is what the app looks like now:</p>
<figure class="wp-block-image"><img decoding="async" src="https://lh6.googleusercontent.com/YbSJ4vMtrc88Jr8sh7F8uWED-9OVFvLkXNT3xVP9gdmQt9XwC-wGHPmaspcKnfSpPMjSotYzRISGPag1Ugq3mxWTslaVhPK9iP8IHLjFnE_FcIkM0y3olJ4Gzw5ejrZFTRbz9avF" alt="" referrerpolicy="no-referrer"><figcaption><em>Screenshot of the 1DIV app thumbnail view using Window Controls Overlay on macOS. The separate top bar area is gone, but the window controls are now blocking some of the app’s interface</em></figcaption></figure>
<p>The title bar is gone, which is what we wanted, but our logo, search field, and <strong>NEW</strong> button are partially covered by the window controls because now our layout starts at the top of the window.</p>
<p>It’s similar on Windows, with the difference that the close, maximize, and minimize buttons appear on the right side, grouped together with the PWA control buttons:</p>
<figure class="wp-block-image"><img decoding="async" src="https://lh6.googleusercontent.com/ytqSauTsKKNI6N7YzxlIqhNatK7LwaPw6yY74jq2egOsBIHbzl2vFGPMRK6dqx6tE-UqSCCWS8f1YftsXZygxEB6KALUYfGU9XW4poE1NPpjYKV66bk1k6dy91rh6TMZ1qb3Rph-" alt="" referrerpolicy="no-referrer"><figcaption><em>Screenshot of the 1DIV app thumbnail display using Window Controls Overlay on the Windows operating system. The separate top bar area is gone, but the window controls are now blocking some of the app’s content.</em></figcaption></figure>
<h2 class="wp-block-heading">Using CSS to keep clear of the window controls</h2>
<p>Along with the feature, new CSS environment variables have been introduced:</p>
<ul><li><strong><code>titlebar-area-x</code></strong></li><li><code><strong>titlebar-area-y</strong></code></li><li><code><strong>titlebar-area-width</strong></code></li><li><strong><code>titlebar-area-height</code></strong></li></ul>
<p>You use these variables with the CSS <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/env()"><strong>env()</strong></a> function to position your content where the title bar would have been while ensuring it won’t overlap with the window controls. In our case, we’ll use two of the variables to position our header, which contains the logo, search bar, and <strong>NEW</strong> button. </p>
<pre><code class="language-css">header {
position: absolute;
left: env(titlebar-area-x, 0);
width: env(titlebar-area-width, 100%);
height: var(--toolbar-height);
}
</code></pre>
<p>The <code><strong>titlebar-area-x</strong> </code>variable gives us the distance from the left of the viewport to where the title bar would appear, and <strong><code>titlebar-area-width</code></strong> is its width. (Remember, this is not equivalent to the width of the entire viewport, just the title bar portion, which as noted earlier, doesn’t include the window controls.)</p>
<p>By doing this, we make sure our content remains fully visible. We’re also defining fallback values (the second parameter in the <strong><code>env()</code></strong> function) for when the variables are not defined (such as on non-supporting browsers, or when the Windows Control Overlay feature is disabled).</p>
<figure class="wp-block-image"><img decoding="async" src="https://lh5.googleusercontent.com/AYZ7D2ZqvPLip8FtF6IzI6XSAEoajjviCG5fo40_ynrksUesFQBjZVEN6dsTOA8F9CCqXbFWb32ZYUN73hEAkMlyzKnX_1Qzjy7kR6jl42TyyJOeg1FWK7A9WeWn-_7SD57-EOdt" alt="" referrerpolicy="no-referrer"><figcaption><em>Screenshot of the 1DIV app thumbnail view on macOS with Window Controls Overlay and our CSS updated. The app content that the window controls had been blocking has been repositioned.</em></figcaption></figure>
<figure class="wp-block-image"><img decoding="async" src="https://lh6.googleusercontent.com/ZxtH5F4v2io8ntHHi8V0YhqgBc_GD5pcq4g52zZy4_bEhbtjC3G7WdyZqQmwc6-D_NIp7Z8dvjsG8qz42DIg7RDhC6HbPHThXEFsknbOgcEfkF7d_cqx45T9vTi6z23pVe0-1nxA" alt="" referrerpolicy="no-referrer"><figcaption><em>Screenshot of the 1DIV app thumbnail view on the Windows operating system with Window Controls Overlay and our updated CSS. The app content that the window controls had been blocking has been repositioned.</em></figcaption></figure>
<p>Now our header adapts to its surroundings, and it doesn’t feel like the window control buttons have been added as an afterthought. The app looks a lot more like a native app.</p>
<h2 class="wp-block-heading">Changing the window controls background color so it blends in</h2>
<p>Now let’s take a closer look at our second page: the CSS playground editor.</p>
<figure class="wp-block-image size-large"><img decoding="async" loading="lazy" width="960" height="486" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig16.png?resize=960%2C486&ssl=1" alt="" class="wp-image-7173230" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig16.png?resize=1024%2C518&ssl=1 1024w, https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig16.png?resize=300%2C152&ssl=1 300w, https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig16.png?resize=768%2C389&ssl=1 768w, https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig16.png?resize=1536%2C777&ssl=1 1536w, https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig16.png?resize=2048%2C1037&ssl=1 2048w, https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig16.png?w=1920&ssl=1 1920w" sizes="(max-width: 960px) 100vw, 960px" data-recalc-dims="1" referrerpolicy="no-referrer"><figcaption><em>Screenshots of the 1DIV app CSS editor view with Window Controls Overlay in macOS and Windows, respectively. The window controls overlay areas have a solid white background color, which contrasts with the hot pink color of the example CSS design displayed in the editor.</em></figcaption></figure>
<p>Not great. Our CSS demo area does go all the way to the top, which is what we wanted, but the way the window controls appear as white rectangles on top of it is quite jarring.</p>
<p>We can fix this by changing the app’s theme color. There are a couple of ways to define it:</p>
<ul><li>PWAs can define a theme color in the web app manifest file using the <a href="https://developer.mozilla.org/en-US/docs/Web/Manifest/theme_color"><strong>theme_color</strong></a> manifest member. This color is then used by the OS in different ways. On desktop platforms, it is used to provide a background color to the title bar and window controls.</li><li>Websites can use the <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta/name/theme-color"><strong>theme-color</strong> meta tag</a> as well. It’s used by browsers to customize the color of the UI around the web page. For PWAs, this color can override the manifest <strong><code>theme_color</code></strong>.</li></ul>
<p>In our case, we can set the manifest <strong><code>theme_color</code></strong> to white to provide the right default color for our app. The OS will read this color value when the app is installed and use it to make the window controls background color white. This color works great for our main page with the list of demos.</p>
<p>The <strong><code>theme-color</code></strong> meta tag can be changed at runtime, using JavaScript. So we can do that to override the white with the right demo background color when one is opened.</p>
<p>Here is the function we’ll use:</p>
<pre><code class="language-javascript">function themeWindow(bgColor) {
document.querySelector("meta[name=theme-color]").setAttribute('content', bgColor);
}</code></pre>
<p>With this in place, we can imagine how using color and CSS transitions can produce a smooth change from the list page to the demo page, and enable the window control buttons to blend in with the rest of the app’s interface.</p>
<figure class="wp-block-image"><img decoding="async" src="https://lh4.googleusercontent.com/YVYktaP8CkIQJFlCtWlwVU4dequS4MutbDJfm-vS8kGx_nedIgzziuHeZICeJ-vsu33VR0rydqwKH0JVIFKjWjlrvbWPYssNvxr7rBsCKKdag7PHMhA_NLV3w0nzBuBzurk1fr1i" alt="" referrerpolicy="no-referrer"><figcaption><em>Screenshot of the 1DIV app CSS editor view on the Windows operating system with Window Controls Overlay and updated CSS demonstrating how the window control buttons blend in with the rest of the app’s interface.</em></figcaption></figure>
<h2 class="wp-block-heading">Dragging the window</h2>
<p>Now, getting rid of the title bar entirely does have an important accessibility consequence: it’s much more difficult to move the application window around.</p>
<p>The title bar provides a sizable area for users to click and drag, but by using the Window Controls Overlay feature, this area becomes limited to where the control buttons are, and users have to very precisely aim between these buttons to move the window.</p>
<p>Fortunately, this can be fixed using CSS with the <strong><code>app-region</code></strong> property. This property is, for now, only supported in Chromium-based browsers and needs the <strong><code>-webkit-</code></strong> vendor prefix. </p>
<p>To make any element of the app become a dragging target for the window, we can use the following: </p>
<p><strong><code>-webkit-app-region: drag;</code></strong></p>
<p>It is also possible to explicitly make an element non-draggable: </p>
<p><code>-<strong>webkit-app-region: no-drag;</strong> </code></p>
<p>These options can be useful for us. We can make the entire header a dragging target, but make the search field and <strong>NEW</strong> button within it non-draggable so they can still be used as normal.</p>
<p>However, because the editor page doesn’t display the header, users wouldn’t be able to drag the window while editing code. So let’s use a different approach. We’ll create another element before our header, also absolutely positioned, and dedicated to dragging the window.</p>
<pre><code class="language-markup"><div class="drag"></div>
<header>...</header></code></pre>
<pre><code class="language-css">.drag {
position: absolute;
top: 0;
width: 100%;
height: env(titlebar-area-height, 0);
-webkit-app-region: drag;
}</code></pre>
<p>With the above code, we’re making the draggable area span the entire viewport width, and using the <strong><code>titlebar-area-height</code></strong> variable to make it as tall as what the title bar would have been. This way, our draggable area is aligned with the window control buttons as shown below.</p>
<p>And, now, to make sure our search field and button remain usable:</p>
<pre><code class="language-css">header .search,
header .new {
-webkit-app-region: no-drag;
}</code></pre>
<p>With the above code, users can click and drag where the title bar used to be. It is an area that users expect to be able to use to move windows on desktop, and we’re not breaking this expectation, which is good.</p>
<figure class="wp-block-image"><img decoding="async" src="https://lh5.googleusercontent.com/sU0QjlT2R7SrF91GI--WcdHRy0shD7CfnKpfzvgXGz5VptZY6hyoDX_SYFqxFG85dxMgbLidjb8cwJOcnqzd4OAWeNjIVgSiKpaz68orEZEU7DgKHHLkM3NXU5rkALkpUrEl7Pp_" alt="" referrerpolicy="no-referrer"><figcaption><em>An animated view of the 1DIV app being dragged across a Windows desktop with the mouse.</em></figcaption></figure>
<h2 class="wp-block-heading">Adapting to window resize</h2>
<p>It may be useful for an app to know both whether the window controls overlay is visible and when its size changes. In our case, if the user made the window very narrow, there wouldn’t be enough space for the search field, logo, and button to fit, so we’d want to push them down a bit.</p>
<p>The Window Controls Overlay feature comes with a JavaScript API we can use to do this: <strong><code>navigator.windowControlsOverlay</code></strong>.</p>
<p>The API provides three interesting things:</p>
<ul><li><strong><code>navigator.windowControlsOverlay.visible</code></strong> lets us know whether the overlay is visible.</li><li><strong><code>navigator.windowControlsOverlay.getBoundingClientRect()</code></strong> lets us know the position and size of the title bar area.</li><li><strong><code>navigator.windowControlsOverlay.ongeometrychange</code></strong> lets us know when the size or visibility changes.</li></ul>
<p>Let’s use this to be aware of the size of the title bar area and move the header down if it’s too narrow.</p>
<pre><code class="language-javascript">if (navigator.windowControlsOverlay) {
navigator.windowControlsOverlay.addEventListener('geometrychange', () => {
const { width } = navigator.windowControlsOverlay.getBoundingClientRect();
document.body.classList.toggle('narrow', width < 250);
});
}</code></pre>
<p>In the example above, we set the <strong><code>narrow</code></strong> class on the <strong><code>body</code></strong> of the app if the title bar area is narrower than 250px. We could do something similar with a media query, but using the <strong><code>windowControlsOverlay</code></strong> API has two advantages for our use case:</p>
<ul><li>It’s only fired when the feature is supported and used; we don’t want to adapt the design otherwise.</li><li>We get the size of the title bar area across operating systems, which is great because the size of the window controls is different on Mac and Windows. Using a media query wouldn’t make it possible for us to know exactly how much space remains.</li></ul>
<pre><code class="language-css">.narrow header {
top: env(titlebar-area-height, 0);
left: 0;
width: 100%;
}</code></pre>
<p>Using the above CSS code, we can move our header down to stay clear of the window control buttons when the window is too narrow, and move the thumbnails down accordingly.</p>
<figure class="wp-block-image"><img decoding="async" src="https://lh5.googleusercontent.com/e4oVs-No9pSWdYyfqTJ0QKcKrDzlv11bsoTwSVvFBhi1bUo9dP2ub71MlWa90QLEFUc5C9e81mQtg3xwGpB5Kkfvu1dNqdBVhqetz74N_0TSWh7_RfZ5NkDNJEuhv5_ZVvw-vpDG" alt="" referrerpolicy="no-referrer"><figcaption><em>A screenshot of the 1DIV app on Windows showing the app’s content adjusted for a much narrower viewport.</em></figcaption></figure>
<h2 class="wp-block-heading">Thirty pixels of exciting design opportunities</h2>
<p><br>Using the Window Controls Overlay feature, we were able to take our simple demo app and turn it into something that feels so much more integrated on desktop devices. Something that reaches out of the usual window constraints and provides a custom experience for its users.</p>
<p>In reality, this feature only gives us about 30 pixels of extra room and comes with challenges on how to deal with the window controls. And yet, this extra room and those challenges can be turned into exciting design opportunities.</p>
<p>More devices of all shapes and forms get invented all the time, and the web keeps on evolving to adapt to them. New features get added to the web platform to allow us, web authors, to integrate more and more deeply with those devices. From watches or foldable devices to desktop computers, we need to evolve our design approach for the web. Building for the web now lets us think outside the rectangular box.</p>
<p>So let’s embrace this. Let’s use the standard technologies already at our disposal, and experiment with new ideas to provide tailored experiences for all devices, all from a single codebase!</p>
<p><br>If you get a chance to try the Window Controls Overlay feature and have feedback about it, you can <a href="https://github.com/WICG/window-controls-overlay/issues">open issues on the spec’s repository</a>. It’s still early in the development of this feature, and you can help make it even better. Or, you can take a look at the <a href="https://docs.microsoft.com/en-us/microsoft-edge/progressive-web-apps-chromium/how-to/window-controls-overlay">feature’s existing documentation</a>, or this <a href="https://stupefied-edison-a4ee55.netlify.app/">demo app</a> and its <a href="https://github.com/captainbrosset/1DIV">source code</a>. </p>
]]></description>
<pubDate>Thu, 09 Dec 2021 15:00:00 GMT</pubDate>
<guid isPermaLink="false">https://alistapart.com/article/breaking-out-of-the-box/</guid>
<link>https://alistapart.com/article/breaking-out-of-the-box/</link>
<author><![CDATA[brandongregory]]></author>
<category>Code</category>
<category>CSS</category>
</item>
<item>
<title><![CDATA[Human-Readable JavaScript: A Tale of Two Experts]]></title>
<description><![CDATA[<p>Everyone wants to be an expert. But what does that even mean? Over the years I’ve seen two types of people who are referred to as “experts.” Expert 1 is someone who knows every tool in the language and makes sure to use every bit of it, whether it helps or not. Expert 2 also knows every piece of syntax, but they’re pickier about what they employ to solve problems, considering a number of factors, both code-related and not. </p>
<p>Can you take a guess at which expert we want working on our team? If you said Expert 2, you’d be right. They’re a developer focused on delivering readable code—lines of JavaScript others can understand and maintain. Someone who can make the complex simple. But “readable” is rarely definitive—in fact, it’s largely based on the eyes of the beholder. So where does that leave us? What should experts aim for when writing readable code? Are there clear right and wrong choices? The answer is, it depends.</p>
<h2 class="wp-block-heading">The obvious choice</h2>
<p>In order to improve developer experience, TC39 has been adding lots of new features to ECMAScript in recent years, including many proven patterns borrowed from other languages. One such addition, added in ES2019, is <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/flat"><code>Array.prototype.flat()</code></a> It takes an argument of depth or <code>Infinity</code>, and flattens an array. If no argument is given, the depth defaults to 1.</p>
<p>Prior to this addition, we needed the following syntax to flatten an array to a single level.</p>
<pre><code class="language-javascript">let arr = [1, 2, [3, 4]];
[].concat.apply([], arr);
// [1, 2, 3, 4]</code></pre>
<p>When we added <code>flat()</code>, that same functionality could be expressed using a single, descriptive function.</p>
<pre><code class="language-javascript">arr.flat();
// [1, 2, 3, 4]</code></pre>
<p>Is the second line of code more readable? The answer is emphatically yes. In fact, both experts would agree.</p>
<p>Not every developer is going to be aware that <code>flat()</code> exists. But they don’t need to because <code>flat()</code> is a descriptive verb that conveys the meaning of what is happening. It’s a lot more intuitive than <code>concat.apply()</code>.</p>
<p>This is the rare case where there is a definitive answer to the question of whether new syntax is better than old. Both experts, each of whom is familiar with the two syntax options, will choose the second. They’ll choose the shorter, clearer, more easily maintained line of code.</p>
<p>But choices and trade-offs aren’t always so decisive.</p>
<h2 class="wp-block-heading">The gut check</h2>
<p>The wonder of JavaScript is that it’s incredibly versatile. There is a reason it’s all over the web. Whether you think that’s a good or <a href="https://alistapart.com/article/responsible-javascript-part-1/">bad</a> thing is another story.</p>
<p>But with that versatility comes the paradox of choice. You can write the same code in many different ways. How do you determine which way is “right”? You can’t even begin to make a decision unless you understand the available options and their limitations.</p>
<p>Let’s use functional programming with <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map"><code>map()</code></a> as the example. I’ll walk through various iterations that all yield the same result.</p>
<p>This is the tersest version of our <code>map()</code> examples. It uses the fewest characters, all fit into one line. This is our baseline.</p>
<pre><code class="language-javascript">const arr = [1, 2, 3];
let multipliedByTwo = arr.map(el => el * 2);
// multipliedByTwo is [2, 4, 6]</code></pre>
<p>This next example adds only two characters: parentheses. Is anything lost? How about gained? Does it make a difference that a function with more than one parameter will always need to use the parentheses? I’d argue that it does. There is little to no detriment in adding them here, and it improves consistency when you inevitably write a function with multiple parameters. In fact, when I wrote this, <a href="https://prettier.io/">Prettier</a> enforced that constraint; it didn’t want me to create an arrow function without the parentheses.</p>
<pre><code class="language-javascript">let multipliedByTwo = arr.map((el) => el * 2);</code></pre>
<p>Let’s take it a step further. We’ve added curly braces and a return. Now this is starting to look more like a traditional function definition. Right now, it may seem like overkill to have a keyword as long as the function logic. Yet, if the function is more than one line, this extra syntax is again required. Do we presume that we will not have any other functions that go beyond a single line? That seems dubious.</p>
<pre><code class="language-javascript">let multipliedByTwo = arr.map((el) => {
return el * 2;
});</code></pre>
<p>Next we’ve removed the arrow function altogether. We’re using the same syntax as before, but we’ve swapped out for the <code>function</code> keyword. This is interesting because there is no scenario in which this syntax won’t work; no number of parameters or lines will cause problems, so consistency is on our side. It’s more verbose than our initial definition, but is that a bad thing? How does this hit a new coder, or someone who is well versed in something other than JavaScript? Is someone who knows JavaScript well going to be frustrated by this syntax in comparison?</p>
<pre><code class="language-javascript">let multipliedByTwo = arr.map(function(el) {
return el * 2;
});</code></pre>
<p>Finally we get to the last option: passing just the function. And <code>timesTwo</code> can be written using any syntax we like. Again, there is no scenario in which passing the function name causes a problem. But step back for a moment and think about whether or not this could be confusing. If you’re new to this codebase, is it clear that <code>timesTwo</code> is a function and not an object? Sure, <code>map()</code> is there to give you a hint, but it’s not unreasonable to miss that detail. How about the location of where <code>timesTwo</code> is declared and initialized? Is it easy to find? Is it clear what it’s doing and how it’s affecting this result? All of these are important considerations.</p>
<pre><code class="language-javascript">const timesTwo = (el) => el * 2;
let multipliedByTwo = arr.map(timesTwo);</code></pre>
<p>As you can see, there is no obvious answer here. But making the right choice for your codebase means understanding all the options and their limitations. And knowing that consistency requires parentheses and curly braces and <code>return</code> keywords.</p>
<p>There are a number of questions you have to ask yourself when writing code. Questions of <a href="https://alistapart.com/article/responsible-javascript-part-2/#section9">performance</a> are typically the most common. But when you’re looking at code that is functionally identical, your determination should be based on humans—how humans consume code.</p>
<h2 class="wp-block-heading">Maybe newer isn’t always better</h2>
<p>So far we’ve found a clear-cut example of where both experts would reach for the newest syntax, even if it’s not universally known. We’ve also looked at an example that poses a lot of questions but not as many answers.</p>
<p>Now it’s time to dive into code that I’ve written before…and removed. This is code that made me the first expert, using a little-known piece of syntax to solve a problem to the detriment of my colleagues and the maintainability of our codebase.</p>
<p><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment">Destructuring assignment</a> lets you unpack values from objects (or arrays). It typically looks something like this.</p>
<pre><code class="language-javascript">const {node} = exampleObject;</code></pre>
<p>It initializes a variable and assigns it a value all in one line. But it doesn’t have to.</p>
<pre><code class="language-javascript">let node
;({node} = exampleObject)</code></pre>
<p>The last line of code assigns a variable to a value using destructuring, but the variable declaration takes place one line before it. It’s not an uncommon thing to want to do, but many people don’t realize you can do it.</p>
<p>But look at that code closely. It forces an awkward semicolon for code that doesn’t use semicolons to terminate lines. It wraps the command in parentheses and adds the curly braces; it’s entirely unclear what this is doing. It’s not easy to read, and, as an expert, it shouldn’t be in code that I write.</p>
<pre><code class="language-javascript">let node
node = exampleObject.node</code></pre>
<p>This code solves the problem. It works, it’s clear what it does, and my colleagues will understand it without having to look it up. With the destructuring syntax, just because I <em>can</em> doesn’t mean I <em>should</em>.</p>
<h2 class="wp-block-heading">Code isn’t everything</h2>
<p>As we’ve seen, the Expert 2 solution is rarely obvious based on code alone; yet there are still clear distinctions between which code each expert would write. That’s because code is for machines to read and humans to interpret. So there are non-code factors to consider!</p>
<p>The syntax choices you make for a team of JavaScript developers is different than those you should make for a team of polyglots who aren’t steeped in the minutiae. </p>
<p>Let’s take spread vs. <code>concat()</code> as an example.</p>
<p>Spread was added to ECMAScript a few years ago, and it’s enjoyed wide adoption. It’s sort of a utility syntax in that it can do a lot of different things. One of them is concatenating a number of arrays.</p>
<pre><code class="language-javascript">const arr1 = [1, 2, 3];
const arr2 = [9, 11, 13];
const nums = [...arr1, ...arr2];</code></pre>
<p>As powerful as spread is, it isn’t a very intuitive symbol. So unless you already know what it does, it’s not super helpful. While both experts <em>may</em> safely assume a team of JavaScript specialists are familiar with this syntax, Expert 2 will probably question whether that’s true of a team of polyglot programmers. Instead, Expert 2 may select the <code>concat()</code> method instead, as it’s a descriptive verb that you can probably understand from the context of the code.</p>
<p>This code snippet gives us the same nums result as the spread example above.</p>
<pre><code class="language-javascript">const arr1 = [1, 2, 3];
const arr2 = [9, 11, 13];
const nums = arr1.concat(arr2);</code></pre>
<p>And that’s but one example of how human factors influence code choices. A codebase that’s touched by a lot of different teams, for example, may have to hold more stringent standards that don’t necessarily keep up with the latest and greatest syntax. Then you move beyond the main source code and consider other factors in your tooling chain that make life easier, or harder, for the humans who work on that code. There is code that can be structured in a way that’s <a href="https://www.freecodecamp.org/news/how-to-write-testable-code/">hostile to testing</a>. There is code that backs you into a corner for <a href="https://www.codeproject.com/Articles/701862/How-Not-to-Back-Yourself-into-a-Corner">future scaling or feature addition</a>. There is code that’s <a href="https://laurieontech.com/posts/performance-diagnosis/">less performant</a>, doesn’t <a href="https://alistapart.com/article/fromswitchestotargets/">handle different browsers</a>, or <a href="https://a11y.coffee/">isn’t accessible</a>. All of these factor into the recommendations Expert 2 makes.</p>
<p>Expert 2 also considers the impact of naming. But let’s be honest, even <em>they</em> can’t get that right most of the time.</p>
<h2 class="wp-block-heading">Conclusion</h2>
<p>Experts don’t prove themselves by using every piece of the spec; they prove themselves by knowing the spec well enough to deploy syntax judiciously and make well-reasoned decisions. This is how experts become multipliers—how they make new experts.</p>
<p>So what does this mean for those of us who consider ourselves experts or aspiring experts? It means that writing code involves asking yourself a lot of questions. It means considering your developer audience in a real way. The best code you can write is code that accomplishes something complex, but is inherently understood by those who examine your codebase.</p>
<p>And no, it’s not easy. And there often isn’t a clear-cut answer. But it’s something you should consider with every function you write.</p>
]]></description>
<pubDate>Thu, 25 Mar 2021 14:00:00 GMT</pubDate>
<guid isPermaLink="false">https://alistapart.com/article/human-readable-javascript/</guid>
<link>https://alistapart.com/article/human-readable-javascript/</link>
<author><![CDATA[brandongregory]]></author>
<category>Code</category>
<category>JavaScript</category>
</item>
<item>
<title><![CDATA[Now THAT’S What I Call Service Worker!]]></title>
<description><![CDATA[<p>The <a href="https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API">Service Worker</a> API is the <a href="https://en.wikipedia.org/wiki/Dremel">Dremel</a> of the web platform. It offers incredibly broad utility while also yielding resiliency and better performance. If you’ve not used Service Worker yet—and you couldn’t be blamed if so, as <a href="https://almanac.httparchive.org/en/2020/pwa#service-workers">it hasn’t seen wide adoption as of 2020</a>—it goes something like this:</p>
<ol><li>On the initial visit to a website, the browser <a href="https://developers.google.com/web/fundamentals/primers/service-workers#register_a_service_worker">registers</a> what amounts to a client-side proxy powered by <a href="https://www.weeklytimber.com/sw.js">a comparably paltry amount of JavaScript</a> that—like a Web Worker—runs on its own thread.</li><li>After the Service Worker’s registration, you can intercept requests and decide how to respond to them in the <a href="https://developer.mozilla.org/en-US/docs/Web/API/FetchEvent">Service Worker’s <code>fetch()</code> event</a>.</li></ol>
<p>What you decide to do with requests you intercept is a) your call and b) depends on your website. You can <a href="https://alistapart.com/article/request-with-intent-caching-strategies-in-the-age-of-pwas/#section8">rewrite requests</a>, <a href="https://web.dev/offline-cookbook/#on-install-as-dependency">precache static assets</a> during install, <a href="https://www.madebymike.com.au/writing/service-workers/#a-better-offline-page-deeper-down-the-rabbit-hole">provide offline functionality</a>, and—as will be our eventual focus—<a href="https://philipwalton.com/articles/smaller-html-payloads-with-service-workers/">deliver smaller HTML payloads and better performance</a> for repeat visitors.</p>
<h2 class="wp-block-heading">Getting out of the woods</h2>
<p>Weekly Timber is a client of mine that provides logging services in central Wisconsin. For them, a fast website is vital. Their business is located in <a href="https://en.wikipedia.org/wiki/Waushara_County,_Wisconsin">Waushara County</a>, and like many rural stretches in the United States, <a href="https://maps.psc.wi.gov/apps/WisconsinBroadbandMap/">network quality and reliability isn’t great</a>.</p>
<div class="wp-block-image"><figure class="aligncenter size-large"><img decoding="async" loading="lazy" width="960" height="500" |
Successfully generated as following: http://localhost:1200/alistapart - Success ✔️<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"
>
<channel>
<title><![CDATA[A List Apart]]></title>
<link>https://alistapart.com/articles</link>
<atom:link href="http://localhost:1200/alistapart" rel="self" type="application/rss+xml" />
<description><![CDATA[Articles on aListApart.com - Made with love by RSSHub(https://github.com/DIYgod/RSSHub)]]></description>
<generator>RSSHub</generator>
<webMaster>i@diygod.me (DIYgod)</webMaster>
<language>en-us</language>
<lastBuildDate>Thu, 10 Aug 2023 17:54:56 GMT</lastBuildDate>
<ttl>5</ttl>
<item>
<title><![CDATA[Humility: An Essential Value]]></title>
<description><![CDATA[<p>Humility, a designer’s essential value—that has a nice ring to it. What about humility, an office manager’s essential value? Or a dentist’s? Or a librarian’s? They all sound great. When humility is our guiding light, the path is always open for fulfillment, evolution, connection, and engagement. In this chapter, we’re going to talk about why.</p>
<p>That said, this is a book for designers, and to that end, I’d like to start with a story—well, a journey, really. It’s a personal one, and I’m going to make myself a bit vulnerable along the way. I call it:</p>
<h2 class="wp-block-heading"><strong>The Tale of Justin’s Preposterous Pate</strong></h2>
<p>When I was coming out of art school, a long-haired, goateed neophyte, print was a known quantity to me; design on the web, however, was rife with complexities to navigate and discover, a problem to be solved. Though I had been formally trained in graphic design, typography, and layout, what fascinated me was how these traditional skills might be applied to a fledgling digital landscape. This theme would ultimately shape the rest of my career.</p>
<p>So rather than graduate and go into print like many of my friends, I devoured HTML and JavaScript books into the wee hours of the morning and taught myself how to code during my senior year. I wanted—nay, needed—to better understand the underlying implications of what my design decisions would mean once rendered in a browser.</p>
<p>The late ’90s and early 2000s were the so-called “Wild West” of web design. Designers at the time were all figuring out how to apply design and visual communication to the digital landscape. What were the rules? How could we break them and still engage, entertain, and convey information? At a more macro level, how could my values, inclusive of humility, respect, and connection, align in tandem with that? I was hungry to find out.</p>
<p>Though I’m talking about a different era, those are timeless considerations between non-career interactions and the world of design. What are your core passions, or values, that transcend medium? It’s essentially the same concept we discussed earlier on the direct parallels between what fulfills you, agnostic of the tangible or digital realms; the core themes are all the same.</p>
<p>First within tables, animated GIFs, Flash, then with Web Standards, <code>div</code>s, and CSS, there was personality, raw unbridled creativity, and unique means of presentment that often defied any semblance of a visible grid. Splash screens and “browser requirement” pages aplenty. Usability and accessibility were typically victims of such a creation, but such paramount facets of any digital design were largely (and, in hindsight, unfairly) disregarded at the expense of experimentation.</p>
<p>For example, this iteration of my personal portfolio site (“the pseudoroom”) from that era was experimental, if not a bit heavy- handed, in the visual communication of the concept of a living sketchbook. Very skeuomorphic. I collaborated with fellow designer and dear friend Marc Clancy (now a co-founder of the creative project organizing app Milanote) on this one, where we’d first sketch and then pass a Photoshop file back and forth to trick things out and play with varied user interactions. Then, I’d break it down and code it into a digital layout.</p>
<figure class="wp-block-image size-full is-resized"><img decoding="async" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2023/06/designers-journey-2-1.jpg?w=846&ssl=1" alt="" class="wp-image-7173967" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2023/06/designers-journey-2-1.jpg?w=1024&ssl=1 1024w, https://i0.wp.com/alistapart.com/wp-content/uploads/2023/06/designers-journey-2-1.jpg?resize=300%2C252&ssl=1 300w, https://i0.wp.com/alistapart.com/wp-content/uploads/2023/06/designers-journey-2-1.jpg?resize=768%2C644&ssl=1 768w" sizes="(max-width: 960px) 100vw, 960px" data-recalc-dims="1" referrerpolicy="no-referrer"><figcaption class="wp-element-caption">Figure 1: “the pseudoroom” website, hitting the sketchbook metaphor hard.</figcaption></figure>
<p>Along with design folio pieces, the site also offered free downloads for Mac OS customizations: desktop wallpapers that were effectively design experimentation, custom-designed typefaces, and desktop icons.</p>
<p>From around the same time, GUI Galaxy was a design, pixel art, and Mac-centric news portal some graphic designer friends and I conceived, designed, developed, and deployed.</p>
<figure class="wp-block-image size-large is-resized"><img decoding="async" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2023/06/designers-journey-2-2.jpg?w=846&ssl=1" alt="" class="wp-image-7173968" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2023/06/designers-journey-2-2.jpg?resize=1024%2C907&ssl=1 1024w, https://i0.wp.com/alistapart.com/wp-content/uploads/2023/06/designers-journey-2-2.jpg?resize=300%2C266&ssl=1 300w, https://i0.wp.com/alistapart.com/wp-content/uploads/2023/06/designers-journey-2-2.jpg?resize=768%2C680&ssl=1 768w, https://i0.wp.com/alistapart.com/wp-content/uploads/2023/06/designers-journey-2-2.jpg?resize=1536%2C1360&ssl=1 1536w, https://i0.wp.com/alistapart.com/wp-content/uploads/2023/06/designers-journey-2-2.jpg?w=1590&ssl=1 1590w" sizes="(max-width: 960px) 100vw, 960px" data-recalc-dims="1" referrerpolicy="no-referrer"><figcaption class="wp-element-caption">Figure 2: GUI Galaxy, web standards-compliant design news portal</figcaption></figure>
<p>Design news portals were incredibly popular during this period, featuring (what would now be considered) Tweet-size, small-format snippets of pertinent news from the categories I previously mentioned. If you took Twitter, curated it to a few categories, and wrapped it in a custom-branded experience, you’d have a design news portal from the late 90s / early 2000s.</p>
<p>We as designers had evolved and created a bandwidth-sensitive, web standards award-winning, much more accessibility-conscious website. Still ripe with experimentation, yet more mindful of equitable engagement. You can see a couple of content panes here, noting general news (tech, design) and Mac-centric news below. We also offered many of the custom downloads I cited before as present on my folio site but branded and themed to GUI Galaxy.</p>
<p>The site’s backbone was a homegrown CMS, with the presentation layer consisting of global design + illustration + news author collaboration. And the collaboration effort here, in addition to experimentation on a ‘brand’ and content delivery, was hitting my core. We were designing something bigger than any single one of us and connecting with a global audience.</p>
<p>Collaboration and connection transcend medium in their impact, immensely fulfilling me as a designer.</p>
<p>Now, why am I taking you down this trip of design memory lane? Two reasons.</p>
<p>First, there’s a reason for the nostalgia for that design era (the “Wild West” era, as I called it earlier): the inherent exploration, personality, and creativity that saturated many design portals and personal portfolio sites. Ultra-finely detailed pixel art UI, custom illustration, bespoke vector graphics, all underpinned by a strong design community.</p>
<p>Today’s web design has been in a period of stagnation. I suspect there’s a strong chance you’ve seen a site whose structure looks something like this: a hero image / banner with text overlaid, perhaps with a lovely rotating carousel of images (laying the snark on heavy there), a call to action, and three columns of sub-content directly beneath. Maybe an icon library is employed with selections that vaguely relate to their respective content.</p>
<p>Design, as it’s applied to the digital landscape, is in dire need of thoughtful layout, typography, and visual engagement that goes hand-in-hand with all the modern considerations we now know are paramount: usability. Accessibility. Load times and bandwidth- sensitive content delivery. A responsive presentation that meets human beings wherever they’re engaging from. We must be mindful of, and respectful toward, those concerns—but not at the expense of creativity of visual communication or via replicating cookie-cutter layouts.</p>
<h2 class="wp-block-heading">Pixel Problems</h2>
<p>Websites during this period were often designed and built on Macs whose OS and desktops looked something like this. This is Mac OS 7.5, but 8 and 9 weren’t that different.</p>
<figure class="wp-block-image size-large is-resized"><img decoding="async" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2023/06/designers-journey-2-3.jpg?w=846&ssl=1" alt="" class="wp-image-7173969" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2023/06/designers-journey-2-3.jpg?resize=1024%2C768&ssl=1 1024w, https://i0.wp.com/alistapart.com/wp-content/uploads/2023/06/designers-journey-2-3.jpg?resize=300%2C225&ssl=1 300w, https://i0.wp.com/alistapart.com/wp-content/uploads/2023/06/designers-journey-2-3.jpg?resize=768%2C576&ssl=1 768w, https://i0.wp.com/alistapart.com/wp-content/uploads/2023/06/designers-journey-2-3.jpg?resize=1536%2C1152&ssl=1 1536w, https://i0.wp.com/alistapart.com/wp-content/uploads/2023/06/designers-journey-2-3.jpg?w=1592&ssl=1 1592w" sizes="(max-width: 960px) 100vw, 960px" data-recalc-dims="1" referrerpolicy="no-referrer"><figcaption class="wp-element-caption">Figure 3: A Mac OS 7.5-centric desktop.</figcaption></figure>
<p>Desktop icons fascinated me: how could any single one, at any given point, stand out to get my attention? In this example, the user’s desktop is tidy, but think of a more realistic example with icon pandemonium. Or, say an icon was part of a larger system grouping (fonts, extensions, control panels)—how did it also maintain cohesion amongst a group?</p>
<p>These were 32 x 32 pixel creations, utilizing a 256-color palette, designed pixel-by-pixel as mini mosaics. To me, this was the embodiment of digital visual communication under such ridiculous constraints. And often, ridiculous restrictions can yield the purification of concept and theme.</p>
<p>So I began to research and do my homework. I was a student of this new medium, hungry to dissect, process, discover, and make it my own.</p>
<p>Expanding upon the notion of exploration, I wanted to see how I could push the limits of a 32×32 pixel grid with that 256-color palette. Those ridiculous constraints forced a clarity of concept and presentation that I found incredibly appealing. The digital gauntlet had been tossed, and that challenge fueled me. And so, in my dorm room into the wee hours of the morning, I toiled away, bringing conceptual sketches into mini mosaic fruition.</p>
<p>These are some of my creations, utilizing the only tool available at the time to create icons called ResEdit. ResEdit was a clunky, built-in Mac OS utility not really made for exactly what we were using it for. At the core of all of this work: Research. Challenge. Problem- solving. Again, these core connection-based values are agnostic of medium.</p>
<figure class="wp-block-image size-large is-resized"><img decoding="async" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2023/06/designers-journey-2-4.png?w=846&ssl=1" alt="" class="wp-image-7173970" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2023/06/designers-journey-2-4.png?resize=1024%2C977&ssl=1 1024w, https://i0.wp.com/alistapart.com/wp-content/uploads/2023/06/designers-journey-2-4.png?resize=300%2C286&ssl=1 300w, https://i0.wp.com/alistapart.com/wp-content/uploads/2023/06/designers-journey-2-4.png?resize=768%2C733&ssl=1 768w, https://i0.wp.com/alistapart.com/wp-content/uploads/2023/06/designers-journey-2-4.png?resize=1536%2C1465&ssl=1 1536w, https://i0.wp.com/alistapart.com/wp-content/uploads/2023/06/designers-journey-2-4.png?w=1602&ssl=1 1602w" sizes="(max-width: 960px) 100vw, 960px" data-recalc-dims="1" referrerpolicy="no-referrer"><figcaption class="wp-element-caption">Figure 4: A selection of my pixel art design, 32×32 pixel canvas, 8-bit palette</figcaption></figure>
<p>There’s one more design portal I want to talk about, which also serves as the second reason for my story to bring this all together.</p>
<p>This is K10k, short for Kaliber 1000. K10k was founded in 1998 by Michael Schmidt and Toke Nygaard, and was <strong>the </strong>design news portal on the web during this period. With its pixel art-fueled presentation, ultra-focused care given to every facet and detail, and with many of the more influential designers of the time who were invited to be news authors on the site, well… it was the place to be, my friend. With respect where respect is due, GUI Galaxy’s concept was inspired by what these folks were doing.</p>
<figure class="wp-block-image size-full is-resized"><img decoding="async" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2023/06/designers-journey-2-5.png?w=846&ssl=1" alt="" class="wp-image-7173971" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2023/06/designers-journey-2-5.png?w=1024&ssl=1 1024w, https://i0.wp.com/alistapart.com/wp-content/uploads/2023/06/designers-journey-2-5.png?resize=300%2C269&ssl=1 300w, https://i0.wp.com/alistapart.com/wp-content/uploads/2023/06/designers-journey-2-5.png?resize=768%2C688&ssl=1 768w" sizes="(max-width: 960px) 100vw, 960px" data-recalc-dims="1" referrerpolicy="no-referrer"><figcaption class="wp-element-caption">Figure 5: The K10k website</figcaption></figure>
<p>For my part, the combination of my web design work and pixel art exploration began to get me some notoriety in the design scene. Eventually, K10k noticed and added me as one of their very select group of news authors to contribute content to the site.</p>
<p>Amongst my personal work and side projects—and now with this inclusion—in the design community, this put me on the map. My design work also began to be published in various printed collections, in magazines domestically and overseas, and featured on other design news portals. With that degree of success while in my early twenties, something else happened:</p>
<p>I evolved—devolved, really—into a colossal asshole (and in just about a year out of art school, no less). The press and the praise became what fulfilled me, and they went straight to my head. They inflated my ego. I actually felt somewhat superior to my fellow designers.</p>
<p>The casualties? My design stagnated. Its evolution—my evolution— stagnated.</p>
<p>I felt so supremely confident in my abilities that I effectively stopped researching and discovering. When previously sketching concepts or iterating ideas in lead was my automatic step one, I instead leaped right into Photoshop. I drew my inspiration from the smallest of sources (and with blinders on). Any critique of my work from my peers was often vehemently dismissed. The most tragic loss: I had lost touch with my values.</p>
<p>My ego almost cost me some of my friendships and burgeoning professional relationships. I was toxic in talking about design and in collaboration. But thankfully, those same friends gave me a priceless gift: candor. They called me out on my unhealthy behavior.</p>
<p>Admittedly, it was a gift I initially did not accept but ultimately was able to deeply reflect upon. I was soon able to accept, and process, and course correct. The realization laid me low, but the re-awakening was essential. I let go of the “reward” of adulation and re-centered upon what stoked the fire for me in art school. Most importantly: I got back to my core values.</p>
<h2 class="wp-block-heading">Always Students</h2>
<p>Following that short-term regression, I was able to push forward in my personal design and career. And I could self-reflect as I got older to facilitate further growth and course correction as needed.</p>
<p>As an example, let’s talk about the Large Hadron Collider. The LHC was designed <em>“to help answer some of the fundamental open questions in physics, which concern the basic laws governing the interactions and forces among the elementary objects, the deep structure of space and time, and in particular the interrelation between quantum mechanics and general relativity.” </em>Thanks, Wikipedia.</p>
<p>Around fifteen years ago, in one of my earlier professional roles, I designed the interface for the application that generated the LHC’s particle collision diagrams. These diagrams are the rendering of what’s actually happening inside the Collider during any given particle collision event and are often considered works of art unto themselves.</p>
<p>Designing the interface for this application was a fascinating process for me, in that I worked with Fermilab physicists to understand what the application was trying to achieve, but also how the physicists themselves would be using it. To that end, in this role,</p>
<p>I cut my teeth on usability testing, working with the Fermilab team to iterate and improve the interface. How they spoke and what they spoke about was like an alien language to me. And by making myself humble and working under the mindset that I was but a student, I made myself available to be a part of their world to generate that vital connection.</p>
<p>I also had my first ethnographic observation experience: going to the Fermilab location and observing how the physicists used the tool in their actual environment, on their actual terminals. For example, one takeaway was that due to the level of ambient light-driven contrast within the facility, the data columns ended up using white text on a dark gray background instead of black text-on-white. This enabled them to pore over reams of data during the day and ease their eye strain. And Fermilab and CERN are government entities with rigorous accessibility standards, so my knowledge in that realm also grew. The barrier-free design was another essential form of connection.</p>
<p>So to those core drivers of my visual problem-solving soul and ultimate fulfillment: discovery, exposure to new media, observation, human connection, and evolution. What opened the door for those values was me checking my ego before I walked through it.</p>
<p>An evergreen willingness to listen, learn, understand, grow, evolve, and connect yields our best work. In particular, I want to focus on the words ‘grow’ and ‘evolve’ in that statement. If we are always students of our craft, we are also continually making ourselves available to evolve. Yes, we have years of applicable design study under our belt. Or the focused lab sessions from a UX bootcamp. Or the monogrammed portfolio of our work. Or, ultimately, decades of a career behind us.</p>
<p>But all that said: experience does not equal “expert.”</p>
<p>As soon as we close our minds via an inner monologue of ‘knowing it all’ or branding ourselves a “#thoughtleader” on social media, the designer we <strong>are </strong>is our final form. The designer we <strong>can be </strong>will never exist.</p>
]]></description>
<pubDate>Thu, 22 Jun 2023 13:00:00 GMT</pubDate>
<guid isPermaLink="false">https://alistapart.com/article/humility-an-essential-value/</guid>
<link>https://alistapart.com/article/humility-an-essential-value/</link>
<author><![CDATA[agustafson]]></author>
<category>Design</category>
</item>
<item>
<title><![CDATA[Personalization Pyramid: A Framework for Designing with User Data]]></title>
<description><![CDATA[<p>As a UX professional in today’s data-driven landscape, it’s increasingly likely that you’ve been asked to design a personalized digital experience, whether it’s a public website, user portal, or native application. Yet while there continues to be no shortage of marketing hype around personalization platforms, we still have very few standardized approaches for implementing personalized UX.</p>
<p>That’s where we come in. After completing dozens of personalization projects over the past few years, we gave ourselves a goal: could you create a holistic personalization framework specifically for UX practitioners? The <strong>Personalization Pyramid</strong> is a designer-centric model for standing up human-centered personalization programs, spanning data, segmentation, content delivery, and overall goals. By using this approach, you will be able to understand the core components of a contemporary, UX-driven personalization program (or at the very least know enough to get started). </p>
<figure class="wp-block-image size-large"><img decoding="async" loading="lazy" width="960" height="558" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image20.png?resize=960%2C558&ssl=1" alt="A chart answering the question Do you have the resources you need to run personalization in your organization? Globally, 13% don’t 33% have limited access, 39% have it (on demand), and 15% have it dedicated." class="wp-image-7173666" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image20.png?resize=1024%2C595&ssl=1 1024w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image20.png?resize=300%2C174&ssl=1 300w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image20.png?resize=768%2C446&ssl=1 768w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image20.png?w=1256&ssl=1 1256w" sizes="(max-width: 960px) 100vw, 960px" data-recalc-dims="1" referrerpolicy="no-referrer"><figcaption class="wp-element-caption"><p><strong>Growing tools for personalization:</strong> According to a Dynamic Yield survey, 39% of respondents felt support is available on-demand when a business case is made for it (up 15% from 2020).</p><p><small>Source: “The State of Personalization Maturity – Q4 2021” Dynamic Yield conducted its annual maturity survey across roles and sectors in the Americas (AMER), Europe and the Middle East (EMEA), and the Asia-Pacific (APAC) regions. This marks the fourth consecutive year publishing our research, which includes more than 450 responses from individuals in the C-Suite, Marketing, Merchandising, CX, Product, and IT.</small></p></figcaption></figure>
<h2 class="wp-block-heading"><strong>Getting Started</strong></h2>
<p>For the sake of this article, we’ll assume you’re already familiar with the basics of digital personalization. A good overview can be found here: <a href="https://www.uxbooth.com/articles/website-personalization-planning/">Website Personalization Planning</a>. While UX projects in this area can take on many different forms, they often stem from similar starting points. </p>
<p><strong>Common scenarios for starting a personalization project:</strong></p>
<ul>
<li>Your organization or client purchased a content management system (CMS) or marketing automation platform (MAP) or related technology that supports personalization</li>
<li>The CMO, CDO, or CIO has identified personalization as a goal</li>
<li>Customer data is disjointed or ambiguous</li>
<li>You are running some isolated targeting campaigns or A/B testing</li>
<li>Stakeholders disagree on personalization approach</li>
<li>Mandate of customer privacy rules (e.g. GDPR) requires revisiting existing user targeting practices</li>
</ul>
<figure class="wp-block-image size-full is-resized"><img decoding="async" loading="lazy" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image22.png?resize=768%2C576&ssl=1" alt="Two men and a woman discussing personalization using a card deck. They are seated at a round table in a hotel conference room. The workshop leaders, two women, are at a podium in the background." class="wp-image-7173667" width="768" height="576" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image22.png?w=1024&ssl=1 1024w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image22.png?resize=300%2C225&ssl=1 300w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image22.png?resize=768%2C576&ssl=1 768w" sizes="(max-width: 768px) 100vw, 768px" data-recalc-dims="1" referrerpolicy="no-referrer"><figcaption class="wp-element-caption">Workshopping personalization at a conference.</figcaption></figure>
<p>Regardless of where you begin, a successful personalization program will require the same core building blocks. We’ve captured these as the “levels” on the pyramid. Whether you are a UX designer, researcher, or strategist, understanding the core components can help make your contribution successful. </p>
<figure class="wp-block-image size-large is-resized"><img decoding="async" loading="lazy" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image14.png?resize=488%2C512&ssl=1" alt="The Personalization Pyramid visualized. The pyramid is stacks labeled, from the bottom, raw data (1m+), actionable data (100k+), user segments (1k+), contexts & campaigns (100s), touchpoints (dozens), goals (handful). The North Star (one) is above. An arrow for prescriptive, business driven data goes up the left side and an arrow for adaptive user-driven data goes down the right side." class="wp-image-7173665" width="488" height="512" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image14.png?resize=975%2C1024&ssl=1 975w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image14.png?resize=286%2C300&ssl=1 286w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image14.png?resize=768%2C807&ssl=1 768w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image14.png?w=1146&ssl=1 1146w" sizes="(max-width: 488px) 100vw, 488px" data-recalc-dims="1" referrerpolicy="no-referrer"><figcaption class="wp-element-caption">From the ground up: Soup-to-nuts personalization, without going nuts.</figcaption></figure>
<p>From top to bottom, the levels include:</p>
<ol>
<li><strong>North Star: </strong>What larger strategic objective is driving the personalization program? </li>
<li><strong>Goals:</strong> What are the specific, measurable outcomes of the program? </li>
<li><strong>Touchpoints: </strong>Where will the personalized experience be served?</li>
<li><strong>Contexts and Campaigns: </strong>What personalization content will the user see?</li>
<li><strong>User Segments:</strong> What constitutes a unique, usable audience? </li>
<li><strong>Actionable Data: </strong>What reliable and authoritative data is captured by our technical platform to drive personalization? </li>
<li><strong>Raw Data: </strong>What wider set of data is conceivably available (already in our setting) allowing you to personalize?</li>
</ol>
<p>We’ll go through each of these levels in turn. To help make this actionable, we created an accompanying <strong>deck of cards</strong> to illustrate specific examples from each level. We’ve found them helpful in personalization brainstorming sessions, and will include examples for you here.</p>
<figure class="wp-block-image size-full"><img decoding="async" loading="lazy" width="320" height="215" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image2.png?resize=320%2C215&ssl=1" alt="A deck of personalization brainstorming cards (the size of playing cards) against a black background." class="wp-image-7173668" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image2.png?w=320&ssl=1 320w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image2.png?resize=300%2C202&ssl=1 300w" sizes="(max-width: 320px) 100vw, 320px" data-recalc-dims="1" referrerpolicy="no-referrer"><figcaption class="wp-element-caption"><strong>Personalization pack:</strong> Deck of cards to help kickstart your personalization brainstorming.</figcaption></figure>
<h2 class="wp-block-heading"><strong>Starting at the Top</strong></h2>
<p>The components of the pyramid are as follows:</p>
<h3 class="wp-block-heading">North Star</h3>
<p>A north star is what you are aiming for overall with your personalization program (big or small). The North Star defines the (one) overall mission of the personalization program. What do you wish to accomplish? North Stars cast a shadow. The bigger the star, the bigger the shadow. Example of North Starts might include: </p>
<ol>
<li><strong>Function:</strong> Personalize based on basic user inputs. Examples: “Raw” notifications, basic search results, system user settings and configuration options, general customization, basic optimizations</li>
<li><strong>Feature:</strong> Self-contained personalization componentry. Examples: “Cooked” notifications, advanced optimizations (geolocation), basic dynamic messaging, customized modules, automations, recommenders</li>
<li><strong>Experience:</strong> Personalized user experiences across multiple interactions and user flows. Examples: Email campaigns, landing pages, advanced messaging (i.e. C2C chat) or conversational interfaces, larger user flows and content-intensive optimizations (localization).</li>
<li><strong>Product:</strong> Highly differentiating personalized product experiences. Examples: Standalone, branded experiences with personalization at their core, like the “algotorial” playlists by Spotify such as Discover Weekly.</li>
</ol>
<figure class="wp-block-gallery has-nested-images columns-default is-cropped wp-block-gallery-1 is-layout-flex wp-block-gallery-is-layout-flex">
<figure class="wp-block-image size-large"><img decoding="async" loading="lazy" width="656" height="1024" data-id="7173669" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image7.jpg?resize=656%2C1024&ssl=1" alt="Function: React to basic user inputs" class="wp-image-7173669" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image7.jpg?resize=656%2C1024&ssl=1 656w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image7.jpg?resize=192%2C300&ssl=1 192w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image7.jpg?resize=768%2C1198&ssl=1 768w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image7.jpg?resize=984%2C1536&ssl=1 984w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image7.jpg?w=1249&ssl=1 1249w" sizes="(max-width: 656px) 100vw, 656px" data-recalc-dims="1" referrerpolicy="no-referrer"></figure>
<figure class="wp-block-image size-large"><img decoding="async" loading="lazy" width="656" height="1024" data-id="7173670" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image18.jpg?resize=656%2C1024&ssl=1" alt="Feature: personalized modules" class="wp-image-7173670" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image18.jpg?resize=656%2C1024&ssl=1 656w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image18.jpg?resize=192%2C300&ssl=1 192w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image18.jpg?resize=768%2C1198&ssl=1 768w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image18.jpg?resize=984%2C1536&ssl=1 984w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image18.jpg?w=1249&ssl=1 1249w" sizes="(max-width: 656px) 100vw, 656px" data-recalc-dims="1" referrerpolicy="no-referrer"></figure>
<figure class="wp-block-image size-large"><img decoding="async" loading="lazy" width="656" height="1024" data-id="7173671" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image8.jpg?resize=656%2C1024&ssl=1" alt="Experience: Integrated personalization" class="wp-image-7173671" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image8.jpg?resize=656%2C1024&ssl=1 656w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image8.jpg?resize=192%2C300&ssl=1 192w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image8.jpg?resize=768%2C1198&ssl=1 768w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image8.jpg?resize=984%2C1536&ssl=1 984w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image8.jpg?w=1249&ssl=1 1249w" sizes="(max-width: 656px) 100vw, 656px" data-recalc-dims="1" referrerpolicy="no-referrer"></figure>
<figcaption class="blocks-gallery-caption wp-element-caption"><strong>North star cards. </strong>These can help orient your team towards a common goal that personalization will help achieve; Also, these are useful for characterizing the end-state ambition of the presently stated personalization effort.</figcaption></figure>
<h3 class="wp-block-heading">Goals</h3>
<p>As in any good UX design, personalization can help accelerate <a href="https://www.uxbooth.com/articles/designing-for-customer-intentions-part-1/">designing with customer intentions</a><strong>. Goals</strong> are the tactical and measurable metrics that will prove the overall program is successful. A good place to start is with your current analytics and measurement program and metrics you can benchmark against. In some cases, new goals may be appropriate. The key thing to remember is that <em>personalization itself is not a goal</em>, rather it is a means to an end. Common goals include:</p>
<ul>
<li>Conversion</li>
<li>Time on task</li>
<li>Net promoter score (NPS)</li>
<li>Customer satisfaction </li>
</ul>
<figure class="wp-block-gallery has-nested-images columns-default is-cropped wp-block-gallery-3 is-layout-flex wp-block-gallery-is-layout-flex">
<figure class="wp-block-image size-large"><img decoding="async" loading="lazy" width="656" height="1024" data-id="7173674" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image12.jpg?resize=656%2C1024&ssl=1" alt="NPS: Net Promoter Score" class="wp-image-7173674" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image12.jpg?resize=656%2C1024&ssl=1 656w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image12.jpg?resize=192%2C300&ssl=1 192w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image12.jpg?resize=768%2C1198&ssl=1 768w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image12.jpg?resize=984%2C1536&ssl=1 984w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image12.jpg?w=1249&ssl=1 1249w" sizes="(max-width: 656px) 100vw, 656px" data-recalc-dims="1" referrerpolicy="no-referrer"></figure>
<figure class="wp-block-image size-large"><img decoding="async" loading="lazy" width="656" height="1024" data-id="7173672" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image23.jpg?resize=656%2C1024&ssl=1" alt="Time on Task: Users move quicker" class="wp-image-7173672" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image23.jpg?resize=656%2C1024&ssl=1 656w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image23.jpg?resize=192%2C300&ssl=1 192w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image23.jpg?resize=768%2C1198&ssl=1 768w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image23.jpg?resize=984%2C1536&ssl=1 984w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image23.jpg?w=1249&ssl=1 1249w" sizes="(max-width: 656px) 100vw, 656px" data-recalc-dims="1" referrerpolicy="no-referrer"></figure>
<figure class="wp-block-image size-large"><img decoding="async" loading="lazy" width="656" height="1024" data-id="7173673" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image13.jpg?resize=656%2C1024&ssl=1" alt="Conversion: Move more of the thing" class="wp-image-7173673" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image13.jpg?resize=656%2C1024&ssl=1 656w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image13.jpg?resize=192%2C300&ssl=1 192w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image13.jpg?resize=768%2C1198&ssl=1 768w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image13.jpg?resize=984%2C1536&ssl=1 984w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image13.jpg?w=1249&ssl=1 1249w" sizes="(max-width: 656px) 100vw, 656px" data-recalc-dims="1" referrerpolicy="no-referrer"></figure>
<figcaption class="blocks-gallery-caption wp-element-caption"><strong>Goal cards.</strong> Examples of some common KPIs related to personalization that are concrete and measurable.</figcaption></figure>
<h3 class="wp-block-heading">Touchpoints</h3>
<p>Touchpoints are where the personalization happens. As a UX designer, this will be one of your largest areas of responsibility. The touchpoints available to you will depend on how your personalization and associated technology capabilities are instrumented, and should be rooted in improving a user’s experience at a particular point in the journey. Touchpoints can be multi-device (mobile, in-store, website) but also more granular (web banner, web pop-up etc.). Here are some examples:</p>
<p><strong>Channel-level </strong>Touchpoints</p>
<ul>
<li>Email: Role</li>
<li>Email: Time of open</li>
<li>In-store display (JSON endpoint)</li>
<li>Native app</li>
<li>Search</li>
</ul>
<p><strong>Wireframe-level </strong>Touchpoints</p>
<ul>
<li>Web overlay</li>
<li>Web alert bar</li>
<li>Web banner</li>
<li>Web content block</li>
<li>Web menu</li>
</ul>
<figure class="wp-block-gallery has-nested-images columns-default is-cropped wp-block-gallery-5 is-layout-flex wp-block-gallery-is-layout-flex">
<figure class="wp-block-image size-large"><img decoding="async" loading="lazy" width="656" height="1024" data-id="7173677" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image25.jpg?resize=656%2C1024&ssl=1" alt="In-store Display: End-cap interfaces" class="wp-image-7173677" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image25.jpg?resize=656%2C1024&ssl=1 656w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image25.jpg?resize=192%2C300&ssl=1 192w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image25.jpg?resize=768%2C1198&ssl=1 768w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image25.jpg?resize=984%2C1536&ssl=1 984w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image25.jpg?w=1249&ssl=1 1249w" sizes="(max-width: 656px) 100vw, 656px" data-recalc-dims="1" referrerpolicy="no-referrer"></figure>
<figure class="wp-block-image size-large"><img decoding="async" loading="lazy" width="656" height="1024" data-id="7173675" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image11.jpg?resize=656%2C1024&ssl=1" alt="Email: Time, personalize at time of open" class="wp-image-7173675" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image11.jpg?resize=656%2C1024&ssl=1 656w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image11.jpg?resize=192%2C300&ssl=1 192w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image11.jpg?resize=768%2C1198&ssl=1 768w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image11.jpg?resize=984%2C1536&ssl=1 984w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image11.jpg?w=1249&ssl=1 1249w" sizes="(max-width: 656px) 100vw, 656px" data-recalc-dims="1" referrerpolicy="no-referrer"></figure>
<figure class="wp-block-image size-large"><img decoding="async" loading="lazy" width="656" height="1024" data-id="7173676" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image19.jpg?resize=656%2C1024&ssl=1" alt="Content Block: Into the woodwork" class="wp-image-7173676" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image19.jpg?resize=656%2C1024&ssl=1 656w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image19.jpg?resize=192%2C300&ssl=1 192w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image19.jpg?resize=768%2C1198&ssl=1 768w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image19.jpg?resize=984%2C1536&ssl=1 984w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image19.jpg?w=1249&ssl=1 1249w" sizes="(max-width: 656px) 100vw, 656px" data-recalc-dims="1" referrerpolicy="no-referrer"></figure>
<figcaption class="blocks-gallery-caption wp-element-caption"><strong>Touchpoint cards. </strong>Examples of common personalization touchpoints: these can vary from narrow (e.g., email) to broad (e.g., in-store).</figcaption></figure>
<p>If you’re designing for web interfaces, for example, you will likely need to include personalized “zones” in your wireframes. The content for these can be presented programmatically in touchpoints based on our next step, contexts and campaigns.</p>
<figure class="wp-block-gallery has-nested-images columns-default is-cropped wp-block-gallery-7 is-layout-flex wp-block-gallery-is-layout-flex">
<figure class="wp-block-image size-large"><img decoding="async" loading="lazy" width="732" height="974" data-id="7173678" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image21.png?resize=732%2C974&ssl=1" alt="" class="wp-image-7173678" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image21.png?w=732&ssl=1 732w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image21.png?resize=225%2C300&ssl=1 225w" sizes="(max-width: 732px) 100vw, 732px" data-recalc-dims="1" referrerpolicy="no-referrer"></figure>
<figure class="wp-block-image size-large"><img decoding="async" loading="lazy" width="708" height="922" data-id="7173679" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image17.png?resize=708%2C922&ssl=1" alt="" class="wp-image-7173679" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image17.png?w=708&ssl=1 708w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image17.png?resize=230%2C300&ssl=1 230w" sizes="(max-width: 708px) 100vw, 708px" data-recalc-dims="1" referrerpolicy="no-referrer"></figure>
<figcaption class="blocks-gallery-caption wp-element-caption"><strong>Targeted Zones:</strong> Examples from Kibo of personalized “zones” on page-level wireframes occurring at various stages of a user journey (Engagement phase at left and Purchase phase at right.)<br><br>Source: “Essential Guide to End-to-End Personaliztion” by Kibo.</figcaption></figure>
<h3 class="wp-block-heading">Contexts and Campaigns</h3>
<p>Once you’ve outlined some touchpoints, you can consider the actual personalized content a user will receive. Many personalization tools will refer to these as “campaigns” (so, for example, a campaign on a web banner for new visitors to the website). These will programmatically be shown at certain touchpoints to certain user segments, as defined by user data. At this stage, we find it helpful to consider two separate models: a <strong>context model</strong> and a <strong>content model</strong>. The context helps you consider the level of engagement of the user at the personalization moment, for example a user casually browsing information vs. doing a deep-dive. Think of it in terms of information retrieval behaviors. The content model can then help you determine what type of personalization to serve based on the context (for example, an “Enrich” campaign that shows related articles may be a suitable supplement to extant content).</p>
<p>Personalization <strong>Context</strong> Model:</p>
<ol>
<li>Browse</li>
<li>Skim</li>
<li>Nudge</li>
<li>Feast</li>
</ol>
<p>Personalization <strong>Content</strong> Model:</p>
<ol>
<li>Alert</li>
<li>Make Easier</li>
<li>Cross-Sell</li>
<li>Enrich</li>
</ol>
<p>We’ve written extensively about each of these models elsewhere, so if you’d like to read more you can check out Colin’s <a href="https://alistapart.com/article/emerging-ux-role-in-personalization/">Personalization Content Model</a> and Jeff’s <a href="https://bucket.circle.so/c/field-notes/progressive-personalization-a-decisionmaking-model-for-better-outcomes-in-personalized-ux">Personalization Context Model</a>. </p>
<figure class="wp-block-gallery has-nested-images columns-default is-cropped wp-block-gallery-9 is-layout-flex wp-block-gallery-is-layout-flex">
<figure class="wp-block-image size-large"><img decoding="async" loading="lazy" width="656" height="1024" data-id="7173681" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image28.jpg?resize=656%2C1024&ssl=1" alt="Cross Sell: You may also like…" class="wp-image-7173681" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image28.jpg?resize=656%2C1024&ssl=1 656w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image28.jpg?resize=192%2C300&ssl=1 192w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image28.jpg?resize=768%2C1198&ssl=1 768w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image28.jpg?resize=984%2C1536&ssl=1 984w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image28.jpg?w=1249&ssl=1 1249w" sizes="(max-width: 656px) 100vw, 656px" data-recalc-dims="1" referrerpolicy="no-referrer"></figure>
<figure class="wp-block-image size-large"><img decoding="async" loading="lazy" width="656" height="1024" data-id="7173682" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image3.jpg?resize=656%2C1024&ssl=1" alt="Enrich: You might find this interesting" class="wp-image-7173682" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image3.jpg?resize=656%2C1024&ssl=1 656w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image3.jpg?resize=192%2C300&ssl=1 192w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image3.jpg?resize=768%2C1198&ssl=1 768w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image3.jpg?resize=984%2C1536&ssl=1 984w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image3.jpg?w=1249&ssl=1 1249w" sizes="(max-width: 656px) 100vw, 656px" data-recalc-dims="1" referrerpolicy="no-referrer"></figure>
<figure class="wp-block-image size-large"><img decoding="async" loading="lazy" width="656" height="1024" data-id="7173680" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image27.jpg?resize=656%2C1024&ssl=1" alt="Browse: Lean back, shallow engagement" class="wp-image-7173680" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image27.jpg?resize=656%2C1024&ssl=1 656w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image27.jpg?resize=192%2C300&ssl=1 192w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image27.jpg?resize=768%2C1198&ssl=1 768w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image27.jpg?resize=984%2C1536&ssl=1 984w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image27.jpg?w=1249&ssl=1 1249w" sizes="(max-width: 656px) 100vw, 656px" data-recalc-dims="1" referrerpolicy="no-referrer"></figure>
<figcaption class="blocks-gallery-caption wp-element-caption"><strong>Campaign and Context cards:</strong> This level of the pyramid can help your team focus around the types of personalization to deliver end users and the use-cases in which they will experience it.</figcaption></figure>
<h3 class="wp-block-heading">User Segments</h3>
<p>User segments can be created prescriptively or adaptively, based on user research (e.g. via rules and logic tied to set user behaviors or via A/B testing). At a minimum you will likely need to consider how to treat the <em>unknown</em> or first-time visitor, the <em>guest</em> or returning visitor for whom you may have a stateful cookie (or equivalent post-cookie identifier), or the <em>authenticated</em> visitor who is logged in. Here are some examples from the personalization pyramid:</p>
<ul>
<li>Unknown</li>
<li>Guest</li>
<li>Authenticated</li>
<li>Default</li>
<li>Referred</li>
<li>Role</li>
<li>Cohort</li>
<li>Unique ID</li>
</ul>
<figure class="wp-block-gallery has-nested-images columns-default is-cropped wp-block-gallery-11 is-layout-flex wp-block-gallery-is-layout-flex">
<figure class="wp-block-image size-large"><img decoding="async" loading="lazy" width="656" height="1024" data-id="7173685" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image16.jpg?resize=656%2C1024&ssl=1" alt="Authenticated: Logged in with token" class="wp-image-7173685" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image16.jpg?resize=656%2C1024&ssl=1 656w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image16.jpg?resize=192%2C300&ssl=1 192w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image16.jpg?resize=768%2C1198&ssl=1 768w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image16.jpg?resize=984%2C1536&ssl=1 984w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image16.jpg?w=1249&ssl=1 1249w" sizes="(max-width: 656px) 100vw, 656px" data-recalc-dims="1" referrerpolicy="no-referrer"></figure>
<figure class="wp-block-image size-large"><img decoding="async" loading="lazy" width="656" height="1024" data-id="7173683" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image10.jpg?resize=656%2C1024&ssl=1" alt="Unknown: Could be anyone really" class="wp-image-7173683" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image10.jpg?resize=656%2C1024&ssl=1 656w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image10.jpg?resize=192%2C300&ssl=1 192w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image10.jpg?resize=768%2C1198&ssl=1 768w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image10.jpg?resize=984%2C1536&ssl=1 984w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image10.jpg?w=1249&ssl=1 1249w" sizes="(max-width: 656px) 100vw, 656px" data-recalc-dims="1" referrerpolicy="no-referrer"></figure>
<figure class="wp-block-image size-large"><img decoding="async" loading="lazy" width="656" height="1024" data-id="7173684" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image9.jpg?resize=656%2C1024&ssl=1" alt="Guest: Dropped a cookie" class="wp-image-7173684" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image9.jpg?resize=656%2C1024&ssl=1 656w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image9.jpg?resize=192%2C300&ssl=1 192w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image9.jpg?resize=768%2C1198&ssl=1 768w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image9.jpg?resize=984%2C1536&ssl=1 984w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image9.jpg?w=1249&ssl=1 1249w" sizes="(max-width: 656px) 100vw, 656px" data-recalc-dims="1" referrerpolicy="no-referrer"></figure>
<figcaption class="blocks-gallery-caption wp-element-caption"><strong>Segment cards.</strong> Examples of common personalization segments: at a minimum, you will need to consider the anonymous, guest, and logged in user types. Segmentation can get dramatically more complex from there.</figcaption></figure>
<h3 class="wp-block-heading">Actionable Data</h3>
<p>Every organization with any digital presence has data. It’s a matter of asking what data you can ethically collect on users, its inherent reliability and value, as to how can you use it (sometimes known as “data activation.”) Fortunately, the tide is turning to first-party data: a recent study by Twilio estimates some <strong>80% of businesses are using at least some type of first-party data</strong> to personalize the customer experience. </p>
<figure class="wp-block-image size-large"><img decoding="async" loading="lazy" width="960" height="669" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image6.png?resize=960%2C669&ssl=1" alt="Chart that answers the question "Why is your company focusing on using first-party data for personalization?" The top answer (at 53%) is "it’s higher quality." That is followed by "It’s easier to manage" (46%), "it provides better privacy" (45%), "it’s easier to obtain" (42%), "it’s more cost-effective" (40%), "it’s more ethical" (37%), "our customers want us to" (36%), "it’s the industry norm" (27%), "it’s easier to comply with regulations" (27%), and "we are phasing out 3rd party cookies" (21%)." class="wp-image-7173686" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image6.png?resize=1024%2C714&ssl=1 1024w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image6.png?resize=300%2C209&ssl=1 300w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image6.png?resize=768%2C536&ssl=1 768w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image6.png?w=1500&ssl=1 1500w" sizes="(max-width: 960px) 100vw, 960px" data-recalc-dims="1" referrerpolicy="no-referrer"><figcaption class="wp-element-caption"><small>Source: “The State of Personalization 2021” by Twilio. Survey respondents were n=2,700 adult consumers who have purchased something online in the past 6 months, and n=300 adult manager+ decision-makers at consumer-facing companies that provide goods and/or services online. Respondents were from the United States, United Kingdom, Australia, and New Zealand.Data was collected from April 8 to April 20, 2021.</small></figcaption></figure>
<p>First-party data represents multiple advantages on the UX front, including being relatively simple to collect, more likely to be accurate, and less susceptible to the “creep factor” of third-party data. So a key part of your UX strategy should be to determine what the best form of data collection is on your audiences. Here are some examples:</p>
<figure class="wp-block-gallery has-nested-images columns-default is-cropped wp-block-gallery-13 is-layout-flex wp-block-gallery-is-layout-flex">
<figure class="wp-block-image size-large"><img decoding="async" loading="lazy" width="656" height="1024" data-id="7173691" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image15.jpg?resize=656%2C1024&ssl=1" alt="Quizes: Tell us what you like" class="wp-image-7173691" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image15.jpg?resize=656%2C1024&ssl=1 656w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image15.jpg?resize=192%2C300&ssl=1 192w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image15.jpg?resize=768%2C1198&ssl=1 768w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image15.jpg?resize=984%2C1536&ssl=1 984w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image15.jpg?w=1249&ssl=1 1249w" sizes="(max-width: 656px) 100vw, 656px" data-recalc-dims="1" referrerpolicy="no-referrer"></figure>
<figure class="wp-block-image size-large"><img decoding="async" loading="lazy" width="656" height="1024" data-id="7173689" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image5.jpg?resize=656%2C1024&ssl=1" alt="Behavioral profiling: Males 40+ who wear fedoras" class="wp-image-7173689" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image5.jpg?resize=656%2C1024&ssl=1 656w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image5.jpg?resize=192%2C300&ssl=1 192w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image5.jpg?resize=768%2C1198&ssl=1 768w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image5.jpg?resize=984%2C1536&ssl=1 984w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image5.jpg?w=1249&ssl=1 1249w" sizes="(max-width: 656px) 100vw, 656px" data-recalc-dims="1" referrerpolicy="no-referrer"></figure>
<figure class="wp-block-image size-large"><img decoding="async" loading="lazy" width="656" height="1024" data-id="7173688" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image1.jpg?resize=656%2C1024&ssl=1" alt="Campaign Source: Your discount code 29780…" class="wp-image-7173688" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image1.jpg?resize=656%2C1024&ssl=1 656w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image1.jpg?resize=192%2C300&ssl=1 192w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image1.jpg?resize=768%2C1198&ssl=1 768w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image1.jpg?resize=984%2C1536&ssl=1 984w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image1.jpg?w=1249&ssl=1 1249w" sizes="(max-width: 656px) 100vw, 656px" data-recalc-dims="1" referrerpolicy="no-referrer"></figure>
</figure>
<figure class="wp-block-image size-full"><img decoding="async" loading="lazy" width="822" height="568" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image4.png?resize=822%2C568&ssl=1" alt="Chart showing the impact of personalization across different phases of personalization maturity. It shows that effort is high in the early phases, but drops off quickly starting in phase 3 (machine learning) while at the same time conversion rates, AOV, and ROI increase from a relatively low level to off the chart." class="wp-image-7173692" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image4.png?w=822&ssl=1 822w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image4.png?resize=300%2C207&ssl=1 300w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image4.png?resize=768%2C531&ssl=1 768w" sizes="(max-width: 822px) 100vw, 822px" data-recalc-dims="1" referrerpolicy="no-referrer"><figcaption class="wp-element-caption"><strong>Figure 1.1.2:</strong> Example of a personalization maturity curve, showing progression from basic recommendations functionality to true individualization. Credit: https://kibocommerce.com/blog/kibos-personalization-maturity-chart/</figcaption></figure>
<p>There is a progression of profiling when it comes to recognizing and making decisioning about different audiences and their signals. It tends to move towards more granular constructs about smaller and smaller cohorts of users as time and confidence and data volume grow.</p>
<p>While some combination of <strong>implicit / explicit</strong> <strong>data</strong> is generally a prerequisite for any implementation (more commonly referred to as first party and third-party data) <strong>ML efforts</strong> are typically not cost-effective directly out of the box. This is because a strong data backbone and content repository is a prerequisite for optimization. But these approaches should be considered as part of the larger roadmap and may indeed help accelerate the organization’s overall progress. Typically at this point you will partner with key stakeholders and product owners to design a <strong>profiling model</strong>. The profiling model includes defining approach to configuring profiles, profile keys, profile cards and pattern cards. A multi-faceted approach to profiling which makes it scalable.</p>
<h2 class="wp-block-heading">Pulling it Together</h2>
<p>While the cards comprise the starting point to an inventory of sorts (we provide blanks for you to tailor your own), a set of potential levers and motivations for the style of personalization activities you aspire to deliver, they are more valuable when thought of in a grouping. </p>
<p>In assembling a card “hand”, one can begin to trace the entire trajectory from leadership focus down through a strategic and tactical execution. It is also at the heart of the way both co-authors have conducted workshops in assembling a program backlog—which is a fine subject for another article.</p>
<p>In the meantime, what is important to note is that each colored class of card is helpful to survey in understanding the range of choices potentially at your disposal, it is threading through and making concrete decisions about for whom this decisioning will be made: where, when, and how.</p>
<figure class="wp-block-image size-full"><img decoding="async" loading="lazy" width="960" height="578" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image26.jpg?resize=960%2C578&ssl=1" alt="Cards on a table. At the top: Function is the north star & customer satisfaction is the goal. User segment is unknown, the actionable data is a quiz, context is a nudge, campaign is to make something easier, and the touchpoint is a banner." class="wp-image-7173693" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image26.jpg?w=1024&ssl=1 1024w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image26.jpg?resize=300%2C180&ssl=1 300w, https://i0.wp.com/alistapart.com/wp-content/uploads/2022/12/image26.jpg?resize=768%2C462&ssl=1 768w" sizes="(max-width: 960px) 100vw, 960px" data-recalc-dims="1" referrerpolicy="no-referrer"><figcaption class="wp-element-caption"><strong>Scenario A:</strong> We want to use personalization to improve customer satisfaction on the website. For unknown users, we will create a short quiz to better identify what the user has come to do. This is sometimes referred to as “badging” a user in onboarding contexts, to better characterize their present intent and context.</figcaption></figure>
<h2 class="wp-block-heading">Lay Down Your Cards</h2>
<p>Any sustainable personalization strategy must consider near, mid and long-term goals. Even with the leading CMS platforms like Sitecore and Adobe or the most exciting composable CMS DXP out there, there is simply no “easy button” wherein a personalization program can be stood up and immediately view meaningful results. That said, there is a common grammar to all personalization activities, just like every sentence has nouns and verbs. These cards attempt to map that territory.</p>
<p></p>
]]></description>
<pubDate>Thu, 08 Dec 2022 15:00:00 GMT</pubDate>
<guid isPermaLink="false">https://alistapart.com/article/personalization-pyramid/</guid>
<link>https://alistapart.com/article/personalization-pyramid/</link>
<author><![CDATA[agustafson]]></author>
<category>Content</category>
<category>Interaction Design</category>
</item>
<item>
<title><![CDATA[Mobile-First CSS: Is It Time for a Rethink?]]></title>
<description><![CDATA[<p class="intro">The mobile-first design methodology is great—it focuses on what really matters to the user, it’s well-practiced, and it’s been a common design pattern for years. So developing your CSS mobile-first should also be great, too…right? </p>
<p>Well, not necessarily. Classic mobile-first CSS development is based on the principle of overwriting style declarations: you begin your CSS with default style declarations, and overwrite and/or add new styles as you add breakpoints with <code>min-width</code> media queries for larger viewports (for a good overview see “<a href="https://www.mightyminnow.com/2013/11/what-is-mobile-first-css-and-why-does-it-rock/">What is Mobile First CSS and Why Does It Rock?</a>”). But all those exceptions create complexity and inefficiency, which in turn can lead to an increased testing effort and a code base that’s harder to maintain. Admit it—how many of us willingly want that?</p>
<p>On your own projects, mobile-first CSS may yet be the best tool for the job, but first you need to evaluate just how appropriate it is in light of the visual design and user interactions you’re working on. To help you get started, here’s how I go about tackling the factors you need to watch for, and I’ll discuss some alternate solutions if mobile-first doesn’t seem to suit your project.</p>
<h2 class="wp-block-heading">Advantages of mobile-first</h2>
<p>Some of the things to like with mobile-first CSS development—and why it’s been the de facto development methodology for so long—make a lot of sense:</p>
<p><strong>Development hierarchy. </strong>One thing you undoubtedly get from mobile-first is a nice development hierarchy—you just focus on the mobile view and get developing. </p>
<p><strong>Tried and tested. </strong>It’s a tried and tested methodology that’s worked for years for ... |
http://localhost:1200/alistapart/code - Success ✔️<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"
>
<channel>
<title><![CDATA[A List Apart]]></title>
<link>https://alistapart.com/blog/topic/code</link>
<atom:link href="http://localhost:1200/alistapart/code" rel="self" type="application/rss+xml" />
<description><![CDATA[Code Articles on aListApart.com - Made with love by RSSHub(https://github.com/DIYgod/RSSHub)]]></description>
<generator>RSSHub</generator>
<webMaster>i@diygod.me (DIYgod)</webMaster>
<language>en-us</language>
<lastBuildDate>Thu, 10 Aug 2023 17:54:57 GMT</lastBuildDate>
<ttl>5</ttl>
<item>
<title><![CDATA[Mobile-First CSS: Is It Time for a Rethink?]]></title>
<description><![CDATA[<p class="intro">The mobile-first design methodology is great—it focuses on what really matters to the user, it’s well-practiced, and it’s been a common design pattern for years. So developing your CSS mobile-first should also be great, too…right? </p>
<p>Well, not necessarily. Classic mobile-first CSS development is based on the principle of overwriting style declarations: you begin your CSS with default style declarations, and overwrite and/or add new styles as you add breakpoints with <code>min-width</code> media queries for larger viewports (for a good overview see “<a href="https://www.mightyminnow.com/2013/11/what-is-mobile-first-css-and-why-does-it-rock/">What is Mobile First CSS and Why Does It Rock?</a>”). But all those exceptions create complexity and inefficiency, which in turn can lead to an increased testing effort and a code base that’s harder to maintain. Admit it—how many of us willingly want that?</p>
<p>On your own projects, mobile-first CSS may yet be the best tool for the job, but first you need to evaluate just how appropriate it is in light of the visual design and user interactions you’re working on. To help you get started, here’s how I go about tackling the factors you need to watch for, and I’ll discuss some alternate solutions if mobile-first doesn’t seem to suit your project.</p>
<h2 class="wp-block-heading">Advantages of mobile-first</h2>
<p>Some of the things to like with mobile-first CSS development—and why it’s been the de facto development methodology for so long—make a lot of sense:</p>
<p><strong>Development hierarchy. </strong>One thing you undoubtedly get from mobile-first is a nice development hierarchy—you just focus on the mobile view and get developing. </p>
<p><strong>Tried and tested. </strong>It’s a tried and tested methodology that’s worked for years for a reason: it solves a problem really well.</p>
<p><strong>Prioritizes the mobile view</strong>. The mobile view is the<strong> </strong>simplest<strong> </strong>and arguably the most important, as it <strong>encompasses all the key user journeys</strong>, and often accounts for a <strong>higher proportion of user visits</strong> (depending on the project). </p>
<p><strong>Prevents desktop-centric development. </strong>As development is done using desktop computers, it can be tempting to initially focus on the desktop view. But thinking about mobile from the start prevents us from getting stuck later on; no one wants to spend their time retrofitting a desktop-centric site to work on mobile devices!</p>
<h2 class="wp-block-heading">Disadvantages of mobile-first</h2>
<p>Setting style declarations and then overwriting them at higher breakpoints can lead to undesirable ramifications:</p>
<p><strong>More complexity. </strong>The farther up the breakpoint hierarchy you go, the more unnecessary code you inherit from lower breakpoints. </p>
<p><strong>Higher CSS specificity. </strong>Styles that have been reverted to their browser default value in a class name declaration now have a higher specificity. This can be a headache on large projects when you want to keep the CSS selectors as simple as possible.</p>
<p><strong>Requires more regression testing. </strong>Changes to the CSS at a lower view (like adding a new style) requires all higher breakpoints to be regression tested.</p>
<p><strong>The browser can’t prioritize CSS downloads. </strong>At wider breakpoints, classic mobile-first <code>min-width</code> media queries don’t leverage the browser’s capability to download CSS files in priority order.</p>
<h2 class="wp-block-heading">The problem of property value overrides</h2>
<p>There is nothing inherently wrong with overwriting values; CSS was designed to do just that. Still, inheriting incorrect values is unhelpful and can be burdensome and inefficient. It can also lead to increased style specificity when you have to overwrite styles to reset them back to their defaults, something that may cause issues later on, especially if you are using a combination of bespoke CSS and utility classes. We won’t be able to use a utility class for a style that has been reset with a higher specificity.</p>
<p>With this in mind, I’m developing CSS with a focus on the default values much more these days. Since there’s no specific order, and no chains of specific values to keep track of, this frees me to develop breakpoints <em>simultaneously</em>. I concentrate on finding common styles and isolating the specific exceptions in closed media query ranges (that is, any range with a <code>max-width</code> set). </p>
<p>This approach opens up some opportunities, as you can look at each breakpoint as a clean slate. If a component’s layout looks like it should be based on Flexbox at all breakpoints, it’s fine and can be coded in the default style sheet. But if it looks like Grid would be much better for large screens and Flexbox for mobile, these can both be done entirely independently when the CSS is put into closed media query ranges. Also, developing simultaneously requires you to have a good understanding of any given component in all breakpoints up front. This can help surface issues in the design earlier in the development process. We don’t want to get stuck down a rabbit hole building a complex component for mobile, and then get the designs for desktop and find they are equally complex and incompatible with the HTML we created for the mobile view! </p>
<p>Though this approach isn’t going to suit everyone, I encourage you to give it a try. There are plenty of tools out there to help with concurrent development, such as <a href="https://responsively.app/">Responsively App</a>, <a href="https://blisk.io/">Blisk</a>, and many others. </p>
<p>Having said that, I don’t feel the order itself is particularly relevant. If you are comfortable with focusing on the mobile view, have a good understanding of the requirements for other breakpoints, and prefer to work on one device at a time, then by all means stick with the classic development order. The important thing is to identify common styles and exceptions so you can put them in the relevant stylesheet—a sort of manual tree-shaking process! Personally, I find this a little easier when working on a component across breakpoints, but that’s by no means a requirement.</p>
<h2 class="wp-block-heading">Closed media query ranges in practice </h2>
<p>In classic mobile-first CSS we overwrite the styles, but we can avoid this by using media query ranges. To illustrate the difference (I’m using SCSS for brevity), let’s assume there are three visual designs: </p>
<ul><li>smaller than 768</li><li>from 768 to below 1024</li><li>1024 and anything larger </li></ul>
<p>Take a simple example where a block-level element has a default <code>padding</code> of “20px,” which is overwritten at tablet to be “40px” and set back to “20px” on desktop.</p>
<figure class="wp-block-table">
<table><tbody>
<tr>
<td valign="top"><p>Classic <code>min-width</code> mobile-first</p>
<pre><code class="language-css">.my-block {
padding: 20px;
@media (min-width: 768px) {
padding: 40px;
}
@media (min-width: 1024px) {
padding: 20px;
}
}</code></pre></td>
<td valign="top"><p>Closed media query range</p>
<pre><code class="language-css">.my-block {
padding: 20px;
@media (min-width: 768px) and (max-width: 1023.98px) {
padding: 40px;
}
}</code></pre></td>
</tr>
</tbody></table>
</figure>
<p>The subtle difference is that the mobile-first example sets the default <code>padding</code> to “20px” and then overwrites it at each breakpoint, setting it three times in total. In contrast, the second example sets the default <code>padding</code> to “20px” and only overrides it at the relevant breakpoint where it isn’t the default value (in this instance, tablet is the exception).</p>
<p>The goal is to: </p>
<ul><li>Only set styles when needed. </li><li>Not set them with the <em>expectation</em> of overwriting them later on, again and again. </li></ul>
<p>To this end, closed media query ranges are our best friend. If we need to make a change to any given view, we make it in the CSS media query range that applies to the specific breakpoint. We’ll be much less likely to introduce unwanted alterations, and our regression testing only needs to focus on the breakpoint we have actually edited. </p>
<p>Taking the above example, if we find that <code>.my-block</code> spacing on desktop is already accounted for by the margin at that breakpoint, and since we want to remove the padding altogether, we could do this by setting the mobile <code>padding</code> in a closed media query range.<br></p>
<figure class="wp-block-table">
<pre><code class="language-css">.my-block {
@media (max-width: 767.98px) {
padding: 20px;
}
@media (min-width: 768px) and (max-width: 1023.98px) {
padding: 40px;
}
}</code></pre>
</figure>
<p>The browser default <code>padding</code> for our block is “0,” so instead of adding a desktop media query and using <code>unset</code> or “0” for the <code>padding</code> value (which we would need with mobile-first), we can wrap the mobile <code>padding</code> in a closed media query (since it is now also an exception) so it won’t get picked up at wider breakpoints. At the desktop breakpoint, we won’t need to set any <code>padding</code> style, as we want the browser default value.</p>
<h2 class="wp-block-heading">Bundling versus separating the CSS</h2>
<p>Back in the day, keeping the number of requests to a minimum was very important due to the browser’s limit of concurrent requests (typically around six). As a consequence, the use of image sprites and CSS bundling was the norm, with all the CSS being downloaded in one go, as one stylesheet with highest priority. </p>
<p>With HTTP/2 and HTTP/3 now on the scene, the number of requests is no longer the big deal it used to be. This allows us to separate the CSS into multiple files by media query. The clear benefit of this is the browser can now request the CSS it currently needs with a higher priority than the CSS it doesn’t. This is more performant and can reduce the overall time <a href="https://web.dev/critical-rendering-path-render-blocking-css/">page rendering is blocked</a>.</p>
<h3 class="wp-block-heading">Which HTTP version are you using?</h3>
<p>To determine which version of HTTP you’re using, go to your website and open your browser’s dev tools. Next, select the <strong>Network</strong> tab and make sure the <strong>Protocol</strong> column is visible. If “h2” is listed under <strong>Protocol</strong>, it means HTTP/2 is being used. </p>
<p><em>Note: to view the Protocol in your browser’s dev tools, go to the </em><strong><em>Network</em></strong><em> tab, reload your page, right-click any column header (e.g., </em><strong><em>Name</em></strong><em>), and check the </em><strong><em>Protocol</em></strong><em> column.</em></p>
<figure class="wp-block-image"><img decoding="async" src="https://lh4.googleusercontent.com/O8lxNeIY3Hb0YDs2EP7QFhGdGsBXOG7mSTCdAJBd5xkm-6RwrpkS1BN63W7RurVCP3nOH9sNpAR9JNGvIGnUTzG0NYm4sUqI5bU2QPhXYEawmKfeUJ_6YwWAIid2ZDHEdRzaQ1LxzUNTGbGk5g" alt="Chrome dev tools, Network tab filtered by document, Protocol column" referrerpolicy="no-referrer"><figcaption><em>Note: for a summarized comparison, see ImageKit’s “</em><a href="https://imagekit.io/blog/http2-vs-http1-performance/"><em>HTTP/2 vs. HTTP/1</em></a><em>.”</em></figcaption></figure>
<p>Also, if your site is still using HTTP/1...WHY?!! What are you waiting for? There is <a href="https://caniuse.com/http2">excellent user support for HTTP/2</a>.</p>
<h2 class="wp-block-heading">Splitting the CSS</h2>
<p>Separating the CSS into individual files is a worthwhile task. Linking the separate CSS files using the relevant <code>media</code> attribute allows the browser to identify which files are needed immediately (because they’re render-blocking) and which can be deferred. Based on this, it allocates each file an appropriate priority.</p>
<p>In the following example of a website visited on a mobile breakpoint, we can see the mobile and default CSS are loaded with “Highest” priority, as they are currently needed to render the page. The remaining CSS files (print, tablet, and desktop) are still downloaded in case they’ll be needed later, but with “Lowest” priority. </p>
<figure class="wp-block-image"><img decoding="async" src="https://lh5.googleusercontent.com/RZOq-S7kbVsavDiFctQl6STFgGm6puwG8L22V6j6U1vUfo73Opq3Cspj2N94T2BU5lpYUD7Bb_4krFCXlePvBE8xXJVMFwbc_At8pzc-C5ug-6lrPViwMIIXgbKiJA-2fQ3beDoYfkCflCVgwg" alt="Chrome dev tools, Network tab filtered by css, Priority column" referrerpolicy="no-referrer"></figure>
<p>With<strong> bundled CSS</strong>, the browser will have to download the CSS file and parse it before rendering can start.<br><br>While, as noted, with the <strong>CSS separated into different files</strong> linked and marked up with the relevant <code>media</code> attribute, the browser can prioritize the files it currently needs. Using closed media query ranges allows the browser to do this at all widths, as opposed to classic mobile-first <code>min-width</code> queries, where the desktop browser would have to download all the CSS with Highest priority. We can’t assume that desktop users always have a fast connection. For instance, in many rural areas, internet connection speeds are still slow. </p>
<p>The media queries and number of separate CSS files will vary from project to project based on project requirements, but might look similar to the example below.</p>
<figure class="wp-block-table">
<table><tbody>
<tr>
<td valign="top">
<p>Bundled CSS</p>
<code><link href="site.css" rel="stylesheet"></code><br><br>
<p>This single file contains all the CSS, including all media queries, and it will be downloaded with Highest priority.</p>
</td>
<td valign="top">
<p>Separated CSS</p>
<code><link href="default.css" rel="stylesheet"><link href="mobile.css" media="screen and (max-width: 767.98px)" rel="stylesheet"><link href="tablet.css" media="screen and (min-width: 768px) and (max-width: 1083.98px)" rel="stylesheet"><link href="desktop.css" media="screen and (min-width: 1084px)" rel="stylesheet"><link href="print.css" media="print" rel="stylesheet"></code><br><br>
<p>Separating the CSS and specifying a <code>media</code> attribute value on each <code>link</code> tag allows the browser to prioritize what it currently needs. Out of the five files listed above, two will be downloaded with Highest priority: the default file, and the file that matches the current media query. The others will be downloaded with Lowest priority.</p>
</td>
</tr>
</tbody></table>
</figure>
<p>Depending on the project’s deployment strategy, a change to one file (<code>mobile.css</code>, for example) would only require the QA team to regression test on devices in that specific media query range. Compare that to the prospect of deploying the single bundled <code>site.css</code> file, an approach that would normally trigger a full regression test.</p>
<h2 class="wp-block-heading">Moving on</h2>
<p>The uptake of mobile-first CSS was a really important milestone in web development; it has helped front-end developers focus on mobile web applications, rather than developing sites on desktop and then attempting to retrofit them to work on other devices.</p>
<p>I don’t think anyone wants to return to that development model again, but it’s important we don’t lose sight of the issue it highlighted: that things can easily get convoluted and less efficient if we prioritize one particular device—any device—over others. For this reason, focusing on the CSS in its own right, always mindful of what is the default setting and what’s an exception, seems like the natural next step. I’ve started noticing small simplifications in my own CSS, as well as other developers’, and that testing and maintenance work is also a bit more simplified and productive. </p>
<p>In general, simplifying CSS rule creation whenever we can is ultimately a cleaner approach than going around in circles of overrides. But whichever methodology you choose, it needs to suit the project. Mobile-first may—or may not—turn out to be the best choice for what’s involved, but first you need to solidly understand the trade-offs you’re stepping into.</p>
]]></description>
<pubDate>Thu, 09 Jun 2022 02:13:10 GMT</pubDate>
<guid isPermaLink="false">https://alistapart.com/article/mobile-first-css-is-it-time-for-a-rethink/</guid>
<link>https://alistapart.com/article/mobile-first-css-is-it-time-for-a-rethink/</link>
<author><![CDATA[brandongregory]]></author>
<category>Code</category>
<category>CSS</category>
</item>
<item>
<title><![CDATA[Breaking Out of the Box]]></title>
<description><![CDATA[<p>CSS is about styling boxes. In fact, the whole web is made of boxes, from the browser viewport to elements on a page. But every once in a while a new feature comes along that makes us rethink our design approach.</p>
<p><a href="https://www.w3.org/TR/css-round-display-1/">Round displays</a>, for example, make it fun to play with circular clip areas. <a href="https://css-tricks.com/the-notch-and-css/">Mobile screen notches</a> and <a href="https://www.w3.org/TR/virtual-keyboard/">virtual keyboards</a> offer challenges to best organize content that stays clear of them. And <a href="https://blogs.windows.com/msedgedev/2020/09/14/introducing-dual-screen-foldable-web-apis/">dual screen or foldable devices</a> make us rethink how to best use available space in a number of different <a href="https://w3c.github.io/device-posture/">device postures</a>.</p>
<figure class="wp-block-image size-large"><img decoding="async" loading="lazy" width="960" height="452" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig1.png?resize=960%2C452&ssl=1" alt="" class="wp-image-7173226" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig1.png?resize=1024%2C482&ssl=1 1024w, https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig1.png?resize=300%2C141&ssl=1 300w, https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig1.png?resize=768%2C362&ssl=1 768w, https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig1.png?resize=1536%2C724&ssl=1 1536w, https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig1.png?resize=2048%2C965&ssl=1 2048w, https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig1.png?w=1920&ssl=1 1920w, https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig1.png?w=2880&ssl=1 2880w" sizes="(max-width: 960px) 100vw, 960px" data-recalc-dims="1" referrerpolicy="no-referrer"><figcaption><em>Sketches of a round display, a common rectangular mobile display, and a device with a foldable display.</em></figcaption></figure>
<p>These recent evolutions of the web platform made it both more challenging and more interesting to design products. They’re great opportunities for us to break out of our rectangular boxes.</p>
<p>I’d like to talk about a new feature similar to the above: the Window Controls Overlay for Progressive Web Apps (PWAs).</p>
<p><a href="https://alistapart.com/article/yes-that-web-project-should-be-a-pwa/">Progressive Web Apps</a> are blurring the lines between apps and websites. They combine the best of both worlds. On one hand, they’re stable, linkable, searchable, and responsive just like websites. On the other hand, they provide additional powerful capabilities, work offline, and read files just like native apps.</p>
<p>As a design surface, PWAs are really interesting because they challenge us to think about what mixing web and device-native user interfaces can be. On desktop devices in particular, we have more than <a href="https://en.wikipedia.org/wiki/History_of_the_graphical_user_interface">40 years of history</a> telling us what applications should look like, and it can be hard to break out of this mental model.</p>
<p>At the end of the day though, PWAs on desktop are constrained to the window they appear in: a rectangle with a title bar at the top.</p>
<p>Here’s what a typical desktop PWA app looks like:</p>
<figure class="wp-block-image size-large"><img decoding="async" loading="lazy" width="960" height="303" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig2.png?resize=960%2C303&ssl=1" alt="" class="wp-image-7173227" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig2.png?resize=1024%2C323&ssl=1 1024w, https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig2.png?resize=300%2C95&ssl=1 300w, https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig2.png?resize=768%2C242&ssl=1 768w, https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig2.png?resize=1536%2C485&ssl=1 1536w, https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig2.png?resize=2048%2C646&ssl=1 2048w, https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig2.png?w=1920&ssl=1 1920w, https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig2.png?w=2880&ssl=1 2880w" sizes="(max-width: 960px) 100vw, 960px" data-recalc-dims="1" referrerpolicy="no-referrer"><figcaption><em>Sketches of two rectangular user interfaces representing the desktop Progressive Web App status quo on the macOS and Windows operating systems, respectively. </em></figcaption></figure>
<p>Sure, as the author of a PWA, you get to choose the color of the title bar (using the Web Application Manifest <a href="https://developer.mozilla.org/en-US/docs/Web/Manifest/theme_color"><strong>theme_color</strong></a> property), but that’s about it.</p>
<p>What if we could think outside this box, and reclaim the real estate of the app’s entire window? Doing so would give us a chance to make our apps more beautiful and feel more integrated in the operating system.</p>
<p>This is exactly what the <a href="https://web.dev/window-controls-overlay/">Window Controls Overlay</a> offers. This new PWA functionality makes it possible to take advantage of the full surface area of the app, including where the title bar normally appears.</p>
<h2 class="wp-block-heading">About the title bar and window controls</h2>
<p>Let’s start with an explanation of what the title bar and window controls are.</p>
<p>The <em>title bar</em> is the area displayed at the top of an app window, which usually contains the app’s name. <em>Window controls</em> are the affordances, or buttons, that make it possible to minimize, maximize, or close the app’s window, and are also displayed at the top.</p>
<figure class="wp-block-image"><img decoding="async" src="https://lh5.googleusercontent.com/1s_lFmqiRm6Fv3wzeELtsDDiDvOkEJuSRH5K9YIrZZ8rh8rYCxUqbSnfd-f7YrsRvcDzF67fexnEJFlDtw53SKKmOgVk8sv_VUyCQveoR18HkNgACPxcQTtEOb6SmEuRIDlX3EcI" alt="" referrerpolicy="no-referrer"><figcaption><em>A sketch of a rectangular application user interface highlighting the title bar area and window control buttons.</em></figcaption></figure>
<p>Window Controls Overlay removes the physical constraint of the title bar and window controls areas. It frees up the full height of the app window, enabling the title bar and window control buttons to be overlaid on top of the application’s web content. </p>
<figure class="wp-block-image"><img decoding="async" src="https://lh6.googleusercontent.com/k_6o1fBePhbjvtmxoTW3tG1134Gvbo31r2fy7zxmOB39d_eKpjThbh7QL8pVXrA1aLvEWzkoJ_rY4af451BU9XyKZXbSouCTvDJMnRKGlcOhcEpXw_rjQAR8_SFjhrm_-22OxKiR" alt="" referrerpolicy="no-referrer"><figcaption><em>A sketch of a rectangular application user interface using Window Controls Overlay. The title bar and window controls are no longer in an area separated from the app’s content.</em></figcaption></figure>
<p>If you are reading this article on a desktop computer, take a quick look at other apps. Chances are they’re already doing something similar to this. In fact, the very web browser you are using to read this uses the top area to display tabs.</p>
<figure class="wp-block-image"><img decoding="async" src="https://lh6.googleusercontent.com/BLL9Rc5othPsw6xYApyyNOZ73j32wi4XkyoZpl4QOv0OL4MnxMe3bl1xLR0O7WSoAvi3KhyeP83hUh4-EezTmGg2axN4RiOVtgiF5ZiapcjUL6gtLqExZOHGCtkOBbthMTgh5Tmr" alt="" referrerpolicy="no-referrer"><figcaption><em>A screenshot of the top area of a browser’s user interface showing a group of tabs that share the same horizontal space as the app window controls.</em></figcaption></figure>
<p>Spotify displays album artwork all the way to the top edge of the application window.</p>
<figure class="wp-block-image"><img decoding="async" src="https://lh3.googleusercontent.com/SzPcq94_7Yu_ARf13Z6dRD0tpdlPM_MdrY-7CA_mv4Yu3fBIL3pJnXirP83cCDVoQxnnIEwDoBwbGzfftHmZ3PZZUfsw_oP-m4QLkB2SqekX8JupR9_xmI0tG1q65IfNFbnXIHUh" alt="" referrerpolicy="no-referrer"><figcaption><em>A screenshot of an album in Spotify’s desktop application. Album artwork spans the entire width of the main content area, all the way to the top and right edges of the window, and the right edge of the main navigation area on the left side. The application and album navigation controls are overlaid directly on top of the album artwork.</em></figcaption></figure>
<p>Microsoft Word uses the available title bar space to display the auto-save and search functionalities, and more.</p>
<figure class="wp-block-image"><img decoding="async" src="https://lh6.googleusercontent.com/VdREVwFFjYHxHF0Gg3l079hxsa8WKPEWiuvuL7cWbGnEDJ2yc3JiOWQK5lUyaeEgzpd1Przji0cNLeooPD7riPKbcMixa6IkXanprdqPJVkQrYSerxSaNmzbJPd1YsA55mlYd9xt" alt="" referrerpolicy="no-referrer"><figcaption><em>A screenshot of Microsoft Word’s toolbar interface. Document file information, search, and other functionality appear at the top of the window, sharing the same horizontal space as the app’s window controls.</em></figcaption></figure>
<p>The whole point of this feature is to allow you to make use of this space with your own content while providing a way to account for the window control buttons. And it enables you to offer this modified experience on a range of platforms while not adversely affecting the experience on browsers or devices that don’t support Window Controls Overlay. After all, PWAs are all about <a href="https://alistapart.com/article/understandingprogressiveenhancement/">progressive enhancement</a>, so this feature is a chance to enhance your app to use this extra space when it’s available.</p>
<h2 class="wp-block-heading">Let’s use the feature</h2>
<p>For the rest of this article, we’ll be working on a demo app to learn more about using the feature.</p>
<p>The demo app is called <a href="https://stupefied-edison-a4ee55.netlify.app/">1DIV</a>. It’s a simple CSS playground where users can create designs using CSS and a single HTML element.</p>
<p>The app has two pages. The first lists the existing CSS designs you’ve created:</p>
<figure class="wp-block-image"><img decoding="async" src="https://lh3.googleusercontent.com/pW2iUTucKfwMJZrAlPGK19vVnEPaHjYT4N-18P-vm9qkhAdGJcRBMexOCu1q9nN9BAfZ7MH6itNP__kY4HPl9uVPucXkbmSX-E9g6AdVAI_uu6TyEsEdH0LUCXdN1f4kqZNgDr30" alt="" referrerpolicy="no-referrer"><figcaption><em>A screenshot of the 1DIV app displaying a thumbnail grid of CSS designs a user created.</em></figcaption></figure>
<p>The second page enables you to create and edit CSS designs:</p>
<figure class="wp-block-image"><img decoding="async" src="https://lh5.googleusercontent.com/faaJ3uHbzXg-TFinvOqR_7gyjVPvlk7fuVWuN4aIH6IUxXNAp4GXtIcuVPpo6bd1IOKO1_EMDt4pUgErUh_X2_2r3WnkQ4PzovPp6Zjg0l98W9NBrHA0xAuTNf0uNVBatRsMJzEm" alt="" referrerpolicy="no-referrer"><figcaption><em>A screenshot of the 1DIV app editor page. The top half of the window displays a rendered CSS design, and a text editor on the bottom half of the window displays the CSS used to create it.</em></figcaption></figure>
<p>Since I’ve added a simple web manifest and service worker, we can install the app as a PWA on desktop. Here is what it looks like on macOS:</p>
<figure class="wp-block-image size-large"><img decoding="async" loading="lazy" width="960" height="466" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig10.png?resize=960%2C466&ssl=1" alt="" class="wp-image-7173228" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig10.png?resize=1024%2C497&ssl=1 1024w, https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig10.png?resize=300%2C146&ssl=1 300w, https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig10.png?resize=768%2C373&ssl=1 768w, https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig10.png?resize=1536%2C745&ssl=1 1536w, https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig10.png?w=1540&ssl=1 1540w" sizes="(max-width: 960px) 100vw, 960px" data-recalc-dims="1" referrerpolicy="no-referrer"><figcaption><em>Screenshots of the 1DIV app thumbnail view and CSS editor view on macOS. This version of the app’s window has a separate control bar at the top for the app name and window control buttons.</em></figcaption></figure>
<p>And on Windows:</p>
<figure class="wp-block-image size-large"><img decoding="async" loading="lazy" width="960" height="501" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig11.png?resize=960%2C501&ssl=1" alt="" class="wp-image-7173229" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig11.png?resize=1024%2C534&ssl=1 1024w, https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig11.png?resize=300%2C157&ssl=1 300w, https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig11.png?resize=768%2C401&ssl=1 768w, https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig11.png?resize=1536%2C802&ssl=1 1536w, https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig11.png?resize=2048%2C1069&ssl=1 2048w, https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig11.png?w=1920&ssl=1 1920w" sizes="(max-width: 960px) 100vw, 960px" data-recalc-dims="1" referrerpolicy="no-referrer"><figcaption><em>Screenshots of the 1DIV app thumbnail view and CSS editor view on the Windows operating system. This version of the app’s window also has a separate control bar at the top for the app name and window control buttons.</em></figcaption></figure>
<p>Our app is looking good, but the white title bar in the first page is wasted space. In the second page, it would be really nice if the design area went all the way to the top of the app window.</p>
<p>Let’s use the Window Controls Overlay feature to improve this.</p>
<h2 class="wp-block-heading">Enabling Window Controls Overlay</h2>
<p>The feature is still experimental at the moment. To try it, you need to enable it in one of the supported browsers.</p>
<p>As of now, it has been implemented in Chromium, as a collaboration between Microsoft and Google. We can therefore use it in Chrome or Edge by going to the internal <strong>about://flags</strong> page, and enabling the <strong>Desktop PWA Window Controls Overlay</strong> flag.</p>
<h2 class="wp-block-heading">Using Window Controls Overlay</h2>
<p>To use the feature, we need to add the following <strong>display_override</strong> member to our web app’s manifest file:</p>
<pre><code class="language-javascript">{
"name": "1DIV",
"description": "1DIV is a mini CSS playground",
"lang": "en-US",
"start_url": "/",
"theme_color": "#ffffff",
"background_color": "#ffffff",
"display_override": [
"window-controls-overlay"
],
"icons": [
...
]
}
</code></pre>
<p>On the surface, the feature is really simple to use. This manifest change is the only thing we need to make the title bar disappear and turn the window controls into an overlay.</p>
<p>However, to provide a great experience for all users regardless of what device or browser they use, and to make the most of the title bar area in our design, we’ll need a bit of CSS and JavaScript code.</p>
<p>Here is what the app looks like now:</p>
<figure class="wp-block-image"><img decoding="async" src="https://lh6.googleusercontent.com/YbSJ4vMtrc88Jr8sh7F8uWED-9OVFvLkXNT3xVP9gdmQt9XwC-wGHPmaspcKnfSpPMjSotYzRISGPag1Ugq3mxWTslaVhPK9iP8IHLjFnE_FcIkM0y3olJ4Gzw5ejrZFTRbz9avF" alt="" referrerpolicy="no-referrer"><figcaption><em>Screenshot of the 1DIV app thumbnail view using Window Controls Overlay on macOS. The separate top bar area is gone, but the window controls are now blocking some of the app’s interface</em></figcaption></figure>
<p>The title bar is gone, which is what we wanted, but our logo, search field, and <strong>NEW</strong> button are partially covered by the window controls because now our layout starts at the top of the window.</p>
<p>It’s similar on Windows, with the difference that the close, maximize, and minimize buttons appear on the right side, grouped together with the PWA control buttons:</p>
<figure class="wp-block-image"><img decoding="async" src="https://lh6.googleusercontent.com/ytqSauTsKKNI6N7YzxlIqhNatK7LwaPw6yY74jq2egOsBIHbzl2vFGPMRK6dqx6tE-UqSCCWS8f1YftsXZygxEB6KALUYfGU9XW4poE1NPpjYKV66bk1k6dy91rh6TMZ1qb3Rph-" alt="" referrerpolicy="no-referrer"><figcaption><em>Screenshot of the 1DIV app thumbnail display using Window Controls Overlay on the Windows operating system. The separate top bar area is gone, but the window controls are now blocking some of the app’s content.</em></figcaption></figure>
<h2 class="wp-block-heading">Using CSS to keep clear of the window controls</h2>
<p>Along with the feature, new CSS environment variables have been introduced:</p>
<ul><li><strong><code>titlebar-area-x</code></strong></li><li><code><strong>titlebar-area-y</strong></code></li><li><code><strong>titlebar-area-width</strong></code></li><li><strong><code>titlebar-area-height</code></strong></li></ul>
<p>You use these variables with the CSS <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/env()"><strong>env()</strong></a> function to position your content where the title bar would have been while ensuring it won’t overlap with the window controls. In our case, we’ll use two of the variables to position our header, which contains the logo, search bar, and <strong>NEW</strong> button. </p>
<pre><code class="language-css">header {
position: absolute;
left: env(titlebar-area-x, 0);
width: env(titlebar-area-width, 100%);
height: var(--toolbar-height);
}
</code></pre>
<p>The <code><strong>titlebar-area-x</strong> </code>variable gives us the distance from the left of the viewport to where the title bar would appear, and <strong><code>titlebar-area-width</code></strong> is its width. (Remember, this is not equivalent to the width of the entire viewport, just the title bar portion, which as noted earlier, doesn’t include the window controls.)</p>
<p>By doing this, we make sure our content remains fully visible. We’re also defining fallback values (the second parameter in the <strong><code>env()</code></strong> function) for when the variables are not defined (such as on non-supporting browsers, or when the Windows Control Overlay feature is disabled).</p>
<figure class="wp-block-image"><img decoding="async" src="https://lh5.googleusercontent.com/AYZ7D2ZqvPLip8FtF6IzI6XSAEoajjviCG5fo40_ynrksUesFQBjZVEN6dsTOA8F9CCqXbFWb32ZYUN73hEAkMlyzKnX_1Qzjy7kR6jl42TyyJOeg1FWK7A9WeWn-_7SD57-EOdt" alt="" referrerpolicy="no-referrer"><figcaption><em>Screenshot of the 1DIV app thumbnail view on macOS with Window Controls Overlay and our CSS updated. The app content that the window controls had been blocking has been repositioned.</em></figcaption></figure>
<figure class="wp-block-image"><img decoding="async" src="https://lh6.googleusercontent.com/ZxtH5F4v2io8ntHHi8V0YhqgBc_GD5pcq4g52zZy4_bEhbtjC3G7WdyZqQmwc6-D_NIp7Z8dvjsG8qz42DIg7RDhC6HbPHThXEFsknbOgcEfkF7d_cqx45T9vTi6z23pVe0-1nxA" alt="" referrerpolicy="no-referrer"><figcaption><em>Screenshot of the 1DIV app thumbnail view on the Windows operating system with Window Controls Overlay and our updated CSS. The app content that the window controls had been blocking has been repositioned.</em></figcaption></figure>
<p>Now our header adapts to its surroundings, and it doesn’t feel like the window control buttons have been added as an afterthought. The app looks a lot more like a native app.</p>
<h2 class="wp-block-heading">Changing the window controls background color so it blends in</h2>
<p>Now let’s take a closer look at our second page: the CSS playground editor.</p>
<figure class="wp-block-image size-large"><img decoding="async" loading="lazy" width="960" height="486" src="https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig16.png?resize=960%2C486&ssl=1" alt="" class="wp-image-7173230" srcset="https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig16.png?resize=1024%2C518&ssl=1 1024w, https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig16.png?resize=300%2C152&ssl=1 300w, https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig16.png?resize=768%2C389&ssl=1 768w, https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig16.png?resize=1536%2C777&ssl=1 1536w, https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig16.png?resize=2048%2C1037&ssl=1 2048w, https://i0.wp.com/alistapart.com/wp-content/uploads/2021/12/fig16.png?w=1920&ssl=1 1920w" sizes="(max-width: 960px) 100vw, 960px" data-recalc-dims="1" referrerpolicy="no-referrer"><figcaption><em>Screenshots of the 1DIV app CSS editor view with Window Controls Overlay in macOS and Windows, respectively. The window controls overlay areas have a solid white background color, which contrasts with the hot pink color of the example CSS design displayed in the editor.</em></figcaption></figure>
<p>Not great. Our CSS demo area does go all the way to the top, which is what we wanted, but the way the window controls appear as white rectangles on top of it is quite jarring.</p>
<p>We can fix this by changing the app’s theme color. There are a couple of ways to define it:</p>
<ul><li>PWAs can define a theme color in the web app manifest file using the <a href="https://developer.mozilla.org/en-US/docs/Web/Manifest/theme_color"><strong>theme_color</strong></a> manifest member. This color is then used by the OS in different ways. On desktop platforms, it is used to provide a background color to the title bar and window controls.</li><li>Websites can use the <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta/name/theme-color"><strong>theme-color</strong> meta tag</a> as well. It’s used by browsers to customize the color of the UI around the web page. For PWAs, this color can override the manifest <strong><code>theme_color</code></strong>.</li></ul>
<p>In our case, we can set the manifest <strong><code>theme_color</code></strong> to white to provide the right default color for our app. The OS will read this color value when the app is installed and use it to make the window controls background color white. This color works great for our main page with the list of demos.</p>
<p>The <strong><code>theme-color</code></strong> meta tag can be changed at runtime, using JavaScript. So we can do that to override the white with the right demo background color when one is opened.</p>
<p>Here is the function we’ll use:</p>
<pre><code class="language-javascript">function themeWindow(bgColor) {
document.querySelector("meta[name=theme-color]").setAttribute('content', bgColor);
}</code></pre>
<p>With this in place, we can imagine how using color and CSS transitions can produce a smooth change from the list page to the demo page, and enable the window control buttons to blend in with the rest of the app’s interface.</p>
<figure class="wp-block-image"><img decoding="async" src="https://lh4.googleusercontent.com/YVYktaP8CkIQJFlCtWlwVU4dequS4MutbDJfm-vS8kGx_nedIgzziuHeZICeJ-vsu33VR0rydqwKH0JVIFKjWjlrvbWPYssNvxr7rBsCKKdag7PHMhA_NLV3w0nzBuBzurk1fr1i" alt="" referrerpolicy="no-referrer"><figcaption><em>Screenshot of the 1DIV app CSS editor view on the Windows operating system with Window Controls Overlay and updated CSS demonstrating how the window control buttons blend in with the rest of the app’s interface.</em></figcaption></figure>
<h2 class="wp-block-heading">Dragging the window</h2>
<p>Now, getting rid of the title bar entirely does have an important accessibility consequence: it’s much more difficult to move the application window around.</p>
<p>The title bar provides a sizable area for users to click and drag, but by using the Window Controls Overlay feature, this area becomes limited to where the control buttons are, and users have to very precisely aim between these buttons to move the window.</p>
<p>Fortunately, this can be fixed using CSS with the <strong><code>app-region</code></strong> property. This property is, for now, only supported in Chromium-based browsers and needs the <strong><code>-webkit-</code></strong> vendor prefix. </p>
<p>To make any element of the app become a dragging target for the window, we can use the following: </p>
<p><strong><code>-webkit-app-region: drag;</code></strong></p>
<p>It is also possible to explicitly make an element non-draggable: </p>
<p><code>-<strong>webkit-app-region: no-drag;</strong> </code></p>
<p>These options can be useful for us. We can make the entire header a dragging target, but make the search field and <strong>NEW</strong> button within it non-draggable so they can still be used as normal.</p>
<p>However, because the editor page doesn’t display the header, users wouldn’t be able to drag the window while editing code. So let’s use a different approach. We’ll create another element before our header, also absolutely positioned, and dedicated to dragging the window.</p>
<pre><code class="language-markup"><div class="drag"></div>
<header>...</header></code></pre>
<pre><code class="language-css">.drag {
position: absolute;
top: 0;
width: 100%;
height: env(titlebar-area-height, 0);
-webkit-app-region: drag;
}</code></pre>
<p>With the above code, we’re making the draggable area span the entire viewport width, and using the <strong><code>titlebar-area-height</code></strong> variable to make it as tall as what the title bar would have been. This way, our draggable area is aligned with the window control buttons as shown below.</p>
<p>And, now, to make sure our search field and button remain usable:</p>
<pre><code class="language-css">header .search,
header .new {
-webkit-app-region: no-drag;
}</code></pre>
<p>With the above code, users can click and drag where the title bar used to be. It is an area that users expect to be able to use to move windows on desktop, and we’re not breaking this expectation, which is good.</p>
<figure class="wp-block-image"><img decoding="async" src="https://lh5.googleusercontent.com/sU0QjlT2R7SrF91GI--WcdHRy0shD7CfnKpfzvgXGz5VptZY6hyoDX_SYFqxFG85dxMgbLidjb8cwJOcnqzd4OAWeNjIVgSiKpaz68orEZEU7DgKHHLkM3NXU5rkALkpUrEl7Pp_" alt="" referrerpolicy="no-referrer"><figcaption><em>An animated view of the 1DIV app being dragged across a Windows desktop with the mouse.</em></figcaption></figure>
<h2 class="wp-block-heading">Adapting to window resize</h2>
<p>It may be useful for an app to know both whether the window controls overlay is visible and when its size changes. In our case, if the user made the window very narrow, there wouldn’t be enough space for the search field, logo, and button to fit, so we’d want to push them down a bit.</p>
<p>The Window Controls Overlay feature comes with a JavaScript API we can use to do this: <strong><code>navigator.windowControlsOverlay</code></strong>.</p>
<p>The API provides three interesting things:</p>
<ul><li><strong><code>navigator.windowControlsOverlay.visible</code></strong> lets us know whether the overlay is visible.</li><li><strong><code>navigator.windowControlsOverlay.getBoundingClientRect()</code></strong> lets us know the position and size of the title bar area.</li><li><strong><code>navigator.windowControlsOverlay.ongeometrychange</code></strong> lets us know when the size or visibility changes.</li></ul>
<p>Let’s use this to be aware of the size of the title bar area and move the header down if it’s too narrow.</p>
<pre><code class="language-javascript">if (navigator.windowControlsOverlay) {
navigator.windowControlsOverlay.addEventListener('geometrychange', () => {
const { width } = navigator.windowControlsOverlay.getBoundingClientRect();
document.body.classList.toggle('narrow', width < 250);
});
}</code></pre>
<p>In the example above, we set the <strong><code>narrow</code></strong> class on the <strong><code>body</code></strong> of the app if the title bar area is narrower than 250px. We could do something similar with a media query, but using the <strong><code>windowControlsOverlay</code></strong> API has two advantages for our use case:</p>
<ul><li>It’s only fired when the feature is supported and used; we don’t want to adapt the design otherwise.</li><li>We get the size of the title bar area across operating systems, which is great because the size of the window controls is different on Mac and Windows. Using a media query wouldn’t make it possible for us to know exactly how much space remains.</li></ul>
<pre><code class="language-css">.narrow header {
top: env(titlebar-area-height, 0);
left: 0;
width: 100%;
}</code></pre>
<p>Using the above CSS code, we can move our header down to stay clear of the window control buttons when the window is too narrow, and move the thumbnails down accordingly.</p>
<figure class="wp-block-image"><img decoding="async" src="https://lh5.googleusercontent.com/e4oVs-No9pSWdYyfqTJ0QKcKrDzlv11bsoTwSVvFBhi1bUo9dP2ub71MlWa90QLEFUc5C9e81mQtg3xwGpB5Kkfvu1dNqdBVhqetz74N_0TSWh7_RfZ5NkDNJEuhv5_ZVvw-vpDG" alt="" referrerpolicy="no-referrer"><figcaption><em>A screenshot of the 1DIV app on Windows showing the app’s content adjusted for a much narrower viewport.</em></figcaption></figure>
<h2 class="wp-block-heading">Thirty pixels of exciting design opportunities</h2>
<p><br>Using the Window Controls Overlay feature, we were able to take our simple demo app and turn it into something that feels so much more integrated on desktop devices. Something that reaches out of the usual window constraints and provides a custom experience for its users.</p>
<p>In reality, this feature only gives us about 30 pixels of extra room and comes with challenges on how to deal with the window controls. And yet, this extra room and those challenges can be turned into exciting design opportunities.</p>
<p>More devices of all shapes and forms get invented all the time, and the web keeps on evolving to adapt to them. New features get added to the web platform to allow us, web authors, to integrate more and more deeply with those devices. From watches or foldable devices to desktop computers, we need to evolve our design approach for the web. Building for the web now lets us think outside the rectangular box.</p>
<p>So let’s embrace this. Let’s use the standard technologies already at our disposal, and experiment with new ideas to provide tailored experiences for all devices, all from a single codebase!</p>
<p><br>If you get a chance to try the Window Controls Overlay feature and have feedback about it, you can <a href="https://github.com/WICG/window-controls-overlay/issues">open issues on the spec’s repository</a>. It’s still early in the development of this feature, and you can help make it even better. Or, you can take a look at the <a href="https://docs.microsoft.com/en-us/microsoft-edge/progressive-web-apps-chromium/how-to/window-controls-overlay">feature’s existing documentation</a>, or this <a href="https://stupefied-edison-a4ee55.netlify.app/">demo app</a> and its <a href="https://github.com/captainbrosset/1DIV">source code</a>. </p>
]]></description>
<pubDate>Thu, 09 Dec 2021 15:00:00 GMT</pubDate>
<guid isPermaLink="false">https://alistapart.com/article/breaking-out-of-the-box/</guid>
<link>https://alistapart.com/article/breaking-out-of-the-box/</link>
<author><![CDATA[brandongregory]]></author>
<category>Code</category>
<category>CSS</category>
</item>
<item>
<title><![CDATA[Human-Readable JavaScript: A Tale of Two Experts]]></title>
<description><![CDATA[<p>Everyone wants to be an expert. But what does that even mean? Over the years I’ve seen two types of people who are referred to as “experts.” Expert 1 is someone who knows every tool in the language and makes sure to use every bit of it, whether it helps or not. Expert 2 also knows every piece of syntax, but they’re pickier about what they employ to solve problems, considering a number of factors, both code-related and not. </p>
<p>Can you take a guess at which expert we want working on our team? If you said Expert 2, you’d be right. They’re a developer focused on delivering readable code—lines of JavaScript others can understand and maintain. Someone who can make the complex simple. But “readable” is rarely definitive—in fact, it’s largely based on the eyes of the beholder. So where does that leave us? What should experts aim for when writing readable code? Are there clear right and wrong choices? The answer is, it depends.</p>
<h2 class="wp-block-heading">The obvious choice</h2>
<p>In order to improve developer experience, TC39 has been adding lots of new features to ECMAScript in recent years, including many proven patterns borrowed from other languages. One such addition, added in ES2019, is <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/flat"><code>Array.prototype.flat()</code></a> It takes an argument of depth or <code>Infinity</code>, and flattens an array. If no argument is given, the depth defaults to 1.</p>
<p>Prior to this addition, we needed the following syntax to flatten an array to a single level.</p>
<pre><code class="language-javascript">let arr = [1, 2, [3, 4]];
[].concat.apply([], arr);
// [1, 2, 3, 4]</code></pre>
<p>When we added <code>flat()</code>, that same functionality could be expressed using a single, descriptive function.</p>
<pre><code class="language-javascript">arr.flat();
// [1, 2, 3, 4]</code></pre>
<p>Is the second line of code more readable? The answer is emphatically yes. In fact, both experts would agree.</p>
<p>Not every developer is going to be aware that <code>flat()</code> exists. But they don’t need to because <code>flat()</code> is a descriptive verb that conveys the meaning of what is happening. It’s a lot more intuitive than <code>concat.apply()</code>.</p>
<p>This is the rare case where there is a definitive answer to the question of whether new syntax is better than old. Both experts, each of whom is familiar with the two syntax options, will choose the second. They’ll choose the shorter, clearer, more easily maintained line of code.</p>
<p>But choices and trade-offs aren’t always so decisive.</p>
<h2 class="wp-block-heading">The gut check</h2>
<p>The wonder of JavaScript is that it’s incredibly versatile. There is a reason it’s all over the web. Whether you think that’s a good or <a href="https://alistapart.com/article/responsible-javascript-part-1/">bad</a> thing is another story.</p>
<p>But with that versatility comes the paradox of choice. You can write the same code in many different ways. How do you determine which way is “right”? You can’t even begin to make a decision unless you understand the available options and their limitations.</p>
<p>Let’s use functional programming with <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map"><code>map()</code></a> as the example. I’ll walk through various iterations that all yield the same result.</p>
<p>This is the tersest version of our <code>map()</code> examples. It uses the fewest characters, all fit into one line. This is our baseline.</p>
<pre><code class="language-javascript">const arr = [1, 2, 3];
let multipliedByTwo = arr.map(el => el * 2);
// multipliedByTwo is [2, 4, 6]</code></pre>
<p>This next example adds only two characters: parentheses. Is anything lost? How about gained? Does it make a difference that a function with more than one parameter will always need to use the parentheses? I’d argue that it does. There is little to no detriment in adding them here, and it improves consistency when you inevitably write a function with multiple parameters. In fact, when I wrote this, <a href="https://prettier.io/">Prettier</a> enforced that constraint; it didn’t want me to create an arrow function without the parentheses.</p>
<pre><code class="language-javascript">let multipliedByTwo = arr.map((el) => el * 2);</code></pre>
<p>Let’s take it a step further. We’ve added curly braces and a return. Now this is starting to look more like a traditional function definition. Right now, it may seem like overkill to have a keyword as long as the function logic. Yet, if the function is more than one line, this extra syntax is again required. Do we presume that we will not have any other functions that go beyond a single line? That seems dubious.</p>
<pre><code class="language-javascript">let multipliedByTwo = arr.map((el) => {
return el * 2;
});</code></pre>
<p>Next we’ve removed the arrow function altogether. We’re using the same syntax as before, but we’ve swapped out for the <code>function</code> keyword. This is interesting because there is no scenario in which this syntax won’t work; no number of parameters or lines will cause problems, so consistency is on our side. It’s more verbose than our initial definition, but is that a bad thing? How does this hit a new coder, or someone who is well versed in something other than JavaScript? Is someone who knows JavaScript well going to be frustrated by this syntax in comparison?</p>
<pre><code class="language-javascript">let multipliedByTwo = arr.map(function(el) {
return el * 2;
});</code></pre>
<p>Finally we get to the last option: passing just the function. And <code>timesTwo</code> can be written using any syntax we like. Again, there is no scenario in which passing the function name causes a problem. But step back for a moment and think about whether or not this could be confusing. If you’re new to this codebase, is it clear that <code>timesTwo</code> is a function and not an object? Sure, <code>map()</code> is there to give you a hint, but it’s not unreasonable to miss that detail. How about the location of where <code>timesTwo</code> is declared and initialized? Is it easy to find? Is it clear what it’s doing and how it’s affecting this result? All of these are important considerations.</p>
<pre><code class="language-javascript">const timesTwo = (el) => el * 2;
let multipliedByTwo = arr.map(timesTwo);</code></pre>
<p>As you can see, there is no obvious answer here. But making the right choice for your codebase means understanding all the options and their limitations. And knowing that consistency requires parentheses and curly braces and <code>return</code> keywords.</p>
<p>There are a number of questions you have to ask yourself when writing code. Questions of <a href="https://alistapart.com/article/responsible-javascript-part-2/#section9">performance</a> are typically the most common. But when you’re looking at code that is functionally identical, your determination should be based on humans—how humans consume code.</p>
<h2 class="wp-block-heading">Maybe newer isn’t always better</h2>
<p>So far we’ve found a clear-cut example of where both experts would reach for the newest syntax, even if it’s not universally known. We’ve also looked at an example that poses a lot of questions but not as many answers.</p>
<p>Now it’s time to dive into code that I’ve written before…and removed. This is code that made me the first expert, using a little-known piece of syntax to solve a problem to the detriment of my colleagues and the maintainability of our codebase.</p>
<p><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment">Destructuring assignment</a> lets you unpack values from objects (or arrays). It typically looks something like this.</p>
<pre><code class="language-javascript">const {node} = exampleObject;</code></pre>
<p>It initializes a variable and assigns it a value all in one line. But it doesn’t have to.</p>
<pre><code class="language-javascript">let node
;({node} = exampleObject)</code></pre>
<p>The last line of code assigns a variable to a value using destructuring, but the variable declaration takes place one line before it. It’s not an uncommon thing to want to do, but many people don’t realize you can do it.</p>
<p>But look at that code closely. It forces an awkward semicolon for code that doesn’t use semicolons to terminate lines. It wraps the command in parentheses and adds the curly braces; it’s entirely unclear what this is doing. It’s not easy to read, and, as an expert, it shouldn’t be in code that I write.</p>
<pre><code class="language-javascript">let node
node = exampleObject.node</code></pre>
<p>This code solves the problem. It works, it’s clear what it does, and my colleagues will understand it without having to look it up. With the destructuring syntax, just because I <em>can</em> doesn’t mean I <em>should</em>.</p>
<h2 class="wp-block-heading">Code isn’t everything</h2>
<p>As we’ve seen, the Expert 2 solution is rarely obvious based on code alone; yet there are still clear distinctions between which code each expert would write. That’s because code is for machines to read and humans to interpret. So there are non-code factors to consider!</p>
<p>The syntax choices you make for a team of JavaScript developers is different than those you should make for a team of polyglots who aren’t steeped in the minutiae. </p>
<p>Let’s take spread vs. <code>concat()</code> as an example.</p>
<p>Spread was added to ECMAScript a few years ago, and it’s enjoyed wide adoption. It’s sort of a utility syntax in that it can do a lot of different things. One of them is concatenating a number of arrays.</p>
<pre><code class="language-javascript">const arr1 = [1, 2, 3];
const arr2 = [9, 11, 13];
const nums = [...arr1, ...arr2];</code></pre>
<p>As powerful as spread is, it isn’t a very intuitive symbol. So unless you already know what it does, it’s not super helpful. While both experts <em>may</em> safely assume a team of JavaScript specialists are familiar with this syntax, Expert 2 will probably question whether that’s true of a team of polyglot programmers. Instead, Expert 2 may select the <code>concat()</code> method instead, as it’s a descriptive verb that you can probably understand from the context of the code.</p>
<p>This code snippet gives us the same nums result as the spread example above.</p>
<pre><code class="language-javascript">const arr1 = [1, 2, 3];
const arr2 = [9, 11, 13];
const nums = arr1.concat(arr2);</code></pre>
<p>And that’s but one example of how human factors influence code choices. A codebase that’s touched by a lot of different teams, for example, may have to hold more stringent standards that don’t necessarily keep up with the latest and greatest syntax. Then you move beyond the main source code and consider other factors in your tooling chain that make life easier, or harder, for the humans who work on that code. There is code that can be structured in a way that’s <a href="https://www.freecodecamp.org/news/how-to-write-testable-code/">hostile to testing</a>. There is code that backs you into a corner for <a href="https://www.codeproject.com/Articles/701862/How-Not-to-Back-Yourself-into-a-Corner">future scaling or feature addition</a>. There is code that’s <a href="https://laurieontech.com/posts/performance-diagnosis/">less performant</a>, doesn’t <a href="https://alistapart.com/article/fromswitchestotargets/">handle different browsers</a>, or <a href="https://a11y.coffee/">isn’t accessible</a>. All of these factor into the recommendations Expert 2 makes.</p>
<p>Expert 2 also considers the impact of naming. But let’s be honest, even <em>they</em> can’t get that right most of the time.</p>
<h2 class="wp-block-heading">Conclusion</h2>
<p>Experts don’t prove themselves by using every piece of the spec; they prove themselves by knowing the spec well enough to deploy syntax judiciously and make well-reasoned decisions. This is how experts become multipliers—how they make new experts.</p>
<p>So what does this mean for those of us who consider ourselves experts or aspiring experts? It means that writing code involves asking yourself a lot of questions. It means considering your developer audience in a real way. The best code you can write is code that accomplishes something complex, but is inherently understood by those who examine your codebase.</p>
<p>And no, it’s not easy. And there often isn’t a clear-cut answer. But it’s something you should consider with every function you write.</p>
]]></description>
<pubDate>Thu, 25 Mar 2021 14:00:00 GMT</pubDate>
<guid isPermaLink="false">https://alistapart.com/article/human-readable-javascript/</guid>
<link>https://alistapart.com/article/human-readable-javascript/</link>
<author><![CDATA[brandongregory]]></author>
<category>Code</category>
<category>JavaScript</category>
</item>
<item>
<title><![CDATA[Now THAT’S What I Call Service Worker!]]></title>
<description><![CDATA[<p>The <a href="https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API">Service Worker</a> API is the <a href="https://en.wikipedia.org/wiki/Dremel">Dremel</a> of the web platform. It offers incredibly broad utility while also yielding resiliency and better performance. If you’ve not used Service Worker yet—and you couldn’t be blamed if so, as <a href="https://almanac.httparchive.org/en/2020/pwa#service-workers">it hasn’t seen wide adoption as of 2020</a>—it goes something like this:</p>
<ol><li>On the initial visit to a website, the browser <a href="https://developers.google.com/web/fundamentals/primers/service-workers#register_a_service_worker">registers</a> what amounts to a client-side proxy powered by <a href="https://www.weeklytimber.com/sw.js">a comparably paltry amount of JavaScript</a> that—like a Web Worker—runs on its own thread.</li><li>After the Service Worker’s registration, you can intercept requests and decide how to respond to them in the <a href="https://developer.mozilla.org/en-US/docs/Web/API/FetchEvent">Service Worker’s <code>fetch()</code> event</a>.</li></ol>
<p>What you decide to do with requests you intercept is a) your call and b) depends on your website. You can <a href="https://alistapart.com/article/request-with-intent-caching-strategies-in-the-age-of-pwas/#section8">rewrite requests</a>, <a href="https://web.dev/offline-cookbook/#on-install-as-dependency">precache static assets</a> during install, <a href="https://www.madebymike.com.au/writing/service-workers/#a-better-offline-page-deeper-down-the-rabbit-hole">provide offline functionality</a>, and—as will be our eventual focus—<a href="https://philipwalton.com/articles/smaller-html-payloads-with-service-workers/">deliver smaller HTML payloads and better performance</a> for repeat visitors.</p>
<h2 class="wp-block-heading">Getting out of the woods</h2>
<p>Weekly Timber is a client of mine that provides logging services in central Wisconsin. For them, a fast website is vital. Their business is located in <a href="https://en.wikipedia.org/wiki/Waushara_County,_Wisconsin">Waushara County</a>, and like many rural stretches in the United States, <a href="https://maps.psc.wi.gov/apps/WisconsinBroadbandMap/">network quality and reliability isn’t great</a>.</p>
<div class="wp-block-image"><figure class="aligncenter size-large"><img decoding="async" loading="lazy" width="960" height="500" |
* feat(route): add Stock Edge * fix(parseTime): utils * Update docs/en/finance.md Co-authored-by: Tony <TonyRL@users.noreply.github.com> * fix(stockedge):minor changes * feat(route): add 'A list apart' * docs * parse time * use wordpress api * add _embedded * fix * docs: spoiler ---------
* feat(route): add Stock Edge * fix(parseTime): utils * Update docs/en/finance.md Co-authored-by: Tony <TonyRL@users.noreply.github.com> * fix(stockedge):minor changes * feat(route): add 'A list apart' * docs * parse time * use wordpress api * add _embedded * fix * docs: spoiler ---------
* style: auto format * fix(route): baozimh selector (#12790) * feat(route): add 长江大学动物科学学院 (#12795) * style: auto format * fix: 适配网信办页面更新 (#12792) * fix(route): 财新最新文章标题获取兼容火线评论 (#12798) * feat(route): add 火线 (#12784) * feat(route): add 火线 * fix pr * fix pr again * fix pr again and again. * fix(route): 更新联合早报时间获取方法 (#12789) * Update util.js * Update util.js * Update util.js * Update util.js * Update util.js * refactor: use cache.tryGet * Update util.js * Update util.js * Update util.js * fix: zaobao content order fix #10309 --------- Co-authored-by: TonyRL <TonyRL@users.noreply.github.com> * feat(router): add 东华大学学术信息 (#12791) * 增加学术信息 * 代码优化 * 测试性提交 * POST API * POST API * Update lib/v2/dhu/news/xsxx.js * Update lib/v2/dhu/radar.js --------- * style: auto format * fix(route): wxkol link (#12799) * fix(route): wxkol link * fix(core): fix wechat-mp date parsing `var ct = "timestamp"` can appear after `var ct=function()` * fix(route/bilibili): fix liveSearch (#12772) * fix(route/bilibili):fix liveSearch * style: auto format * Update liveSearch.js fix order not deliver to param remove qv_id remove fixed UA --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * fix(route): NMPA get accurate pubdate (#12801) * Update generic.js * Update index.js * fix: nmpa ditch puppeteer --------- * feat(add): SecIN信息安全社区 (#12800) * feat(add): SecIN信息安全社区 * fix lib/v2/sec-in/index.js * fix lib/v2/sec-in/radar.js --------- * fix(route/bilibili): user/followings & user/followers now require login (#12802) * Update router.js change followers/followings params * Update followers.js add login for followers * Update followings.js add login for followings * Update social-media.md update bilibili/followers bilibili/followings docs * Update followers.js Using camelCase. Adding Document link. Cookie check before declaring `uid `. * Update followings.js Using camelCase. Adding Document link. Cookie check before declaring `uid `. * Update maintainer.js * Update README.md Add docs for bilibili/user/followings & bilibili/user/followers * Update router.js Using camelCase * Update social-media.md * style: auto format * Update social-media.md --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * fix(route): copymanga with rate limit and cache (#12805) * feat(route): add 国家广播电视总局电视剧政务平台 (#12804) * feat(route): add 国家广播电视总局电视剧政务平台 * fix: set pool limit to 5 * refactor: remove direct dependent on xml2js (#12817) * fix(route): javdb (#12818) * fix(route): 2048 redirect (#12820) * fix(route): AP News获取方式调整 (#12812) * Resort to DOM parsing. * Update topics.js * Update topics.js * Remove unused code. * Fix cache error. * Remove "Other News". * Update lib/v2/apnews/topics.js --------- * chore(deps): bump pinyin-pro from 3.15.4 to 3.16.0 in /docs (#12825) Bumps [pinyin-pro](https://github.com/zh-lx/pinyin-pro) from 3.15.4 to 3.16.0. - [Release notes](https://github.com/zh-lx/pinyin-pro/releases) - [Changelog](https://github.com/zh-lx/pinyin-pro/blob/main/CHANGELOG.md) - [Commits](https://github.com/zh-lx/pinyin-pro/compare/3.15.4...3.16.0) --- updated-dependencies: - dependency-name: pinyin-pro dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * feat(route): add SecWiki-安全维基 (#12816) * feat(route): add SecWiki-安全维基 * fix pr * fix(route): duplicate entries in the Blizzard feed (#12828) * fix(route): duplicate entries in the Blizzard feed * refactor: migrate to v2 --------- * fix(route): shu 上海大学 (#12803) * fix(route): shu 上海大学 * Optimize code and update documentation - shu/index.js & shu/jwc.js - use @/utils/parse-date to parse date - change "item.description" from text to HTML - change "url.resolve" (deprecated) to "new URL" - change the way of mapping "ctx.params.type" to link - now it accepts more route types - university.md - update doc for new types - router.js - add an alias because the website changed its host * Update lib/routes/universities/shu/index.js * Update lib/routes/universities/shu/jwc.js * Update docs/university.md * refactor: migrate to v2 --------- * fix(route): 星岛日报链接获取方式调整,以去除重复项目 (#12830) * feat(route): add NPR (#12827) * feat(route): add NPR * fix(route/npr): docs * fix(route/npr): remove duplicate images * fix(route/npr): ignore item until audio is available * fix(route/npr): remove duplicate captions * fix(route/npr): caption * fix(route/npr): handle multiple audios * style: auto format * feat(route): add dhu/yjs/news 东华大学研究生院通知 (#12831) * add yjs-news * add yjs news * Update news.js * Update lib/v2/dhu/yjs/news.js * feat: add radar --------- * style: auto format * chore: update deps (#12833) * style: auto format * feat(route): add 纪妖 (#12824) * feat(route): add 纪妖 * fix: use wordpress restapi * update lib/v2/cbaigui/index.js --------- * style: auto format * fix(route): AP News时区调整 (#12842) * feat(route): add 爱思想专题 (#12845) * chore: bring back lint-staged (#12844) * chore: bring back lint-staged * fix: gha label * fix: yml format * fix(route): qidian chapter (#12850) * fix(route): qidian chapter * fix: chapter and forum author refs: https://github.com/DIYgod/RSSHub/pull/414 https://github.com/DIYgod/RSSHub/pull/501 https://github.com/DIYgod/RSSHub/pull/732 * fix(route): agefans (#12851) * feat(route): add V2rayShare (#12843) * add V2rayShare * style: auto format * add V2rayShare * style: auto format * Update docs/other.md * Update lib/v2/v2rayshare/maintainer.js * Update index.js * style: auto format * Update lib/v2/v2rayshare/index.js --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * chore(deps): bump fluxninja/openai-pr-reviewer from 0 to 1 (#12852) * chore(deps): bump fluxninja/openai-pr-reviewer from 0 to 1 Bumps [fluxninja/openai-pr-reviewer](https://github.com/fluxninja/openai-pr-reviewer) from 0 to 1. - [Release notes](https://github.com/fluxninja/openai-pr-reviewer/releases) - [Commits](https://github.com/fluxninja/openai-pr-reviewer/compare/v0...v1) --- updated-dependencies: - dependency-name: fluxninja/openai-pr-reviewer dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> * fix: don't relabel for `dependabot[bot]` --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix: twitter keyword * feat(route): 支持上海市文旅局审批公告 (#12862) * feat(route): 支持上海市文旅局审批公告 * fix(route): wgj * fix(route): currentUrl * fix(route): search params * feat: increase the timeout time for the fulltext_mode test * feat: support reverse proxy * style: auto format * fix: typo * test: fix empty request error * feat: bypass reverse proxy for requests with cookies * test: fix empty request error and remove node 16 support * fix: reverse proxy fails for some requests * feat: exclude some requests for reverse proxy * feat: expand the scope of retries and set proxy strategy * fix: logger.debug * fix(route/twitter): mixed media incomplete (#12863) * fix(route/twitter): media t.co links not purged (#12864) * fix(route): bloomberg api 404 error (#12834) * fix bloomberg api 404 error * fix(route): AP News时区调整 (#12842) * feat(route): add 爱思想专题 (#12845) * chore: bring back lint-staged (#12844) * chore: bring back lint-staged * fix: gha label * fix: yml format * newsletter ok * forget to remove debug * fix(route): qidian chapter (#12850) * fix(route): qidian chapter * fix: chapter and forum author refs: https://github.com/DIYgod/RSSHub/pull/414 https://github.com/DIYgod/RSSHub/pull/501 https://github.com/DIYgod/RSSHub/pull/732 * fix(route): agefans (#12851) * feat(route): add V2rayShare (#12843) * add V2rayShare * style: auto format * add V2rayShare * style: auto format * Update docs/other.md * Update lib/v2/v2rayshare/maintainer.js * Update index.js * style: auto format * Update lib/v2/v2rayshare/index.js --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * chore(deps): bump fluxninja/openai-pr-reviewer from 0 to 1 (#12852) * chore(deps): bump fluxninja/openai-pr-reviewer from 0 to 1 Bumps [fluxninja/openai-pr-reviewer](https://github.com/fluxninja/openai-pr-reviewer) from 0 to 1. - [Release notes](https://github.com/fluxninja/openai-pr-reviewer/releases) - [Commits](https://github.com/fluxninja/openai-pr-reviewer/compare/v0...v1) --- updated-dependencies: - dependency-name: fluxninja/openai-pr-reviewer dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> * fix: don't relabel for `dependabot[bot]` --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix: twitter keyword * feat(route): 支持上海市文旅局审批公告 (#12862) * feat(route): 支持上海市文旅局审批公告 * fix(route): wgj * fix(route): currentUrl * fix(route): search params * feat: increase the timeout time for the fulltext_mode test * feat: support reverse proxy * style: auto format * fix: typo * test: fix empty request error * feat: bypass reverse proxy for requests with cookies * test: fix empty request error and remove node 16 support * fix: reverse proxy fails for some requests * feat: exclude some requests for reverse proxy * feat: expand the scope of retries and set proxy strategy * fix: logger.debug * fix(route/twitter): mixed media incomplete (#12863) * fix(route/twitter): media t.co links not purged (#12864) * Add fix for bloomberg newsletter * Fix deepscan error --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: Andvari <31068367+dzx-dzx@users.noreply.github.com> Co-authored-by: Ethan Shen <42264778+nczitzk@users.noreply.github.com> Co-authored-by: 钛白 <86600901+77taibai@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: DIYgod <i@diygod.me> Co-authored-by: GideonSenku <39037656+GideonSenku@users.noreply.github.com> Co-authored-by: Rongrong <i@rong.moe> Co-authored-by: Chenfei Xu <chenfxu@ebay.com> * feat(route): 添加 Javdb 的清单获取, 更新了搜索的排序筛选 (#12866) * feat(route): 添加 Javdb 的清单获取, 更新了搜索的排序筛选 * fix: sort router --------- * style: auto format * Fix(route): bloomberg story for newslettes (#12869) * fix minor bugs * fix page sel * reformat * wordaround to fix the deepscan error * fix(route/twitter/user): missing latest tweets (#12870) * fix(route/twitter/user): missing latest tweets Signed-off-by: Rongrong <i@rong.moe> * Update lib/v2/twitter/web-api/twitter-api.js --------- Signed-off-by: Rongrong <i@rong.moe> * feat(route): add 中国海洋大学教务处 (#12872) * feat(route): add 中国海洋大学教务处 * fix: sort entries --------- * feat(route): add 国务院国有资产监督管理委员会 (#12875) * feat: replace the full text content of utgd with description according to site owner's request * feat: support mobile subdomain of jandan in radar rules (#12876) * feat: support mobile subdomain of jandan in radar rules * fix: format * style: auto format * fix(route): 壹蘋新聞網獲取失敗 (#12886) * chore(deps): bump pinyin-pro from 3.16.0 to 3.16.1 in /docs (#12890) Bumps [pinyin-pro](https://github.com/zh-lx/pinyin-pro) from 3.16.0 to 3.16.1. - [Release notes](https://github.com/zh-lx/pinyin-pro/releases) - [Changelog](https://github.com/zh-lx/pinyin-pro/blob/main/CHANGELOG.md) - [Commits](https://github.com/zh-lx/pinyin-pro/compare/3.16.0...3.16.1) --- updated-dependencies: - dependency-name: pinyin-pro dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * feat(route): add route for bluesky post search ("keyword") (#12893) * add route for bluesky post search ("keyword") * try and fix up routes * Apply suggestions from code review --------- * fix(route): bloomberg: add default handing of news in storymode based on react-renderer (#12878) * fix minor bugs * fix page sel * reformat * wordaround to fix the deepscan error * fix bloomberg when dealing with 404 direct link * fix processVideo in cyclic depend * remove the toggle * feat(route): add 中国海洋大学选课信息教务通知 (#12880) * feat(route): add 中国海洋大学教务处 * fix: sort entries * fix: 修复错误的entries排序 * feat(route): add 中国海洋大学选课信息教务通知 * add selfhost --------- * feat(route): utgd: adjust utgd apiUrl to new (#12894) * Update timeline.js * Update topic.js * fix(route): segment fault user (#12900) * feat(route): add github all issue/pull comments (#12899) * style: auto format * feat(route): shiep (#12907) * fix(route/shiep): fix jsjxy,tyb * feat(route/shiep): add jxfz,nydlzk,rpstec,spgc,ztjy * ci: enable experimental feature in gha * feat(route): add bilibili频道排行榜 (#12906) * feat(route): add bilibili频道排行榜 * fix * fix: typo --------- * fix(route): wxkol (#12909) * docs: fix bilibili wrong parent * fix(route): coolapk item type 13 (#12910) * fix(route): 18comic (#12911) * fix(route): 18comic * feat: allow user provided domain * fix(route/twitter/user): missing all replies (#12912) Signed-off-by: Rongrong <i@rong.moe> * feat(route): add Rawkuma (#12901) * feat(route): add Rawkuma * update lib/v2/rawkuma/manga.js * fix: eslint --------- * feat(route): add 湖南农业大学教务处等通知 (#12913) * feat(route): add 湖南农业大学教务处等通知 * feat(route): add hunau湖南农业大学教务处等通知 * fix: Incomplete multi-character sanitization * fix: sort entries * fix: name using lowerCamelCase * fix: case bugs * fix:中国政府网-最新政策 url变更(#12908) (#12914) * fix: bilibili route /bilibili/user/video/:uid bug (#12920) * fix: radar rule for Telegram channels (#12927) * feat(route/odaily): Odaily 星球日报, 添加指定关键字快讯 (#12926) * feat(route/odaily): Odaily 星球日报, 添加指定关键字快讯 * fix: sort entries * fix: radar rules --------- * fix(route): finviz news (#12857) * fix(route): finviz news * fix: remove unnecessary escape characters * fix: remove cache * fix: add filter of title * fix: filter empty value --------- * feat: 为html/json转化为rss提供通用支持 (#12882) * feat: add rss proxy * docs: 增加文档 * feat: 增加description和自动链接提取 * feat: 增加一些有用的radar * fix: 链接补全 * fix: lint * fix: request config * docs: example encode * fix: rename proxy to transform * refactor: move it under rsshub fix: split radar rules * fix: maintainer build * style: camelCase * docs: fix example --------- * docs: fix table in other.md * chore(deps): bump pinyin-pro from 3.16.1 to 3.16.2 in /docs (#12934) Bumps [pinyin-pro](https://github.com/zh-lx/pinyin-pro) from 3.16.1 to 3.16.2. - [Release notes](https://github.com/zh-lx/pinyin-pro/releases) - [Changelog](https://github.com/zh-lx/pinyin-pro/blob/main/CHANGELOG.md) - [Commits](https://github.com/zh-lx/pinyin-pro/compare/3.16.1...3.16.2) --- updated-dependencies: - dependency-name: pinyin-pro dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix(route): radar rule for xueqiu snb & stock_info (#12935) * feat(route): add 电子工程专辑芯语 (#12939) * feat(route): add 电子工程专辑芯语 * fix radar * style: auto format * fix(route/ximalaya): categoryInfo is undefined (#12940) * fix(route/ximalaya): categoryInfo is undefined * fix(route/ximalaya): categoryInfo is undefined * refactor: migrate to v2 --------- * fix(route): zhihu (#12942) * fix(route): bilibili vsearch (#12943) * feat(route): add dnaindia (#12941) * feat(route): add dnaindia * Update docs/en/traditional-media.md Co-authored-by: Tony <TonyRL@users.noreply.github.com> * Update docs/en/traditional-media.md Co-authored-by: Tony <TonyRL@users.noreply.github.com> * Update docs/en/traditional-media.md Co-authored-by: Tony <TonyRL@users.noreply.github.com> * Update docs/en/traditional-media.md Co-authored-by: Tony <TonyRL@users.noreply.github.com> * Update docs/en/traditional-media.md Co-authored-by: Tony <TonyRL@users.noreply.github.com> * Update lib/v2/dnaindia/radar.js Co-authored-by: Tony <TonyRL@users.noreply.github.com> * Update lib/v2/dnaindia/radar.js Co-authored-by: Tony <TonyRL@users.noreply.github.com> * Update lib/v2/dnaindia/radar.js Co-authored-by: Tony <TonyRL@users.noreply.github.com> * Update<category.js>: change logger and item.category * Update traditional-media.md * Update lib/v2/dnaindia/radar.js Co-authored-by: Tony <TonyRL@users.noreply.github.com> * Update lib/v2/dnaindia/category.js --------- * fix(route): elsevier (#12948) * fix(route): cnki/journals (#12949) * style: auto format * feat(route): Spotify show route (#12945) * Add spotify show route * refactor: sort entries * docs: remove dupe field * Update lib/v2/spotify/show.js --------- * docs: spelling mistake and description update (#12953) * feat(route): add Stock Edge (#12950) * feat(route): add Stock Edge * fix(parseTime): utils * Update docs/en/finance.md Co-authored-by: Tony <TonyRL@users.noreply.github.com> * fix(stockedge):minor changes --------- * feat(route): add runtrail (#12963) * feat(route): thehindu topic (#12964) * feat(route): add A List Apart (#12960) * feat(route): add Stock Edge * fix(parseTime): utils * Update docs/en/finance.md Co-authored-by: Tony <TonyRL@users.noreply.github.com> * fix(stockedge):minor changes * feat(route): add 'A list apart' * docs * parse time * use wordpress api * add _embedded * fix * docs: spoiler --------- * feat(route): add Smashing magazine (#12961) * initial commit * feat(route): add Smashing magazine * docs * docs update * add full article * fix docs * docs: spoiler --------- * feat(route): add 早报网 (#12981) * chore(deps-dev): bump @types/koa from 2.13.6 to 2.13.8 (#12972) * chore(deps-dev): bump @types/koa from 2.13.6 to 2.13.8 Bumps [@types/koa](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/koa) from 2.13.6 to 2.13.8. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/koa) --- updated-dependencies: - dependency-name: "@types/koa" dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump puppeteer from 20.9.0 to 21.0.2 (#12971) * chore(deps): bump puppeteer from 20.9.0 to 21.0.2 Bumps [puppeteer](https://github.com/puppeteer/puppeteer) from 20.9.0 to 21.0.2. - [Release notes](https://github.com/puppeteer/puppeteer/releases) - [Changelog](https://github.com/puppeteer/puppeteer/blob/main/release-please-config.json) - [Commits](https://github.com/puppeteer/puppeteer/compare/puppeteer-v20.9.0...puppeteer-v21.0.2) --- updated-dependencies: - dependency-name: puppeteer dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump @sentry/node from 7.59.3 to 7.62.0 (#12973) * chore(deps): bump @sentry/node from 7.59.3 to 7.62.0 Bumps [@sentry/node](https://github.com/getsentry/sentry-javascript) from 7.59.3 to 7.62.0. - [Release notes](https://github.com/getsentry/sentry-javascript/releases) - [Changelog](https://github.com/getsentry/sentry-javascript/blob/develop/CHANGELOG.md) - [Commits](https://github.com/getsentry/sentry-javascript/compare/7.59.3...7.62.0) --- updated-dependencies: - dependency-name: "@sentry/node" dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps-dev): bump prettier from 3.0.0 to 3.0.1 (#12975) * chore(deps-dev): bump prettier from 3.0.0 to 3.0.1 Bumps [prettier](https://github.com/prettier/prettier) from 3.0.0 to 3.0.1. - [Release notes](https://github.com/prettier/prettier/releases) - [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md) - [Commits](https://github.com/prettier/prettier/compare/3.0.0...3.0.1) --- updated-dependencies: - dependency-name: prettier dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump @notionhq/client from 2.2.8 to 2.2.10 (#12976) * chore(deps): bump @notionhq/client from 2.2.8 to 2.2.10 Bumps [@notionhq/client](https://github.com/makenotion/notion-sdk-js) from 2.2.8 to 2.2.10. - [Release notes](https://github.com/makenotion/notion-sdk-js/releases) - [Commits](https://github.com/makenotion/notion-sdk-js/compare/v2.2.8...v2.2.10) --- updated-dependencies: - dependency-name: "@notionhq/client" dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps-dev): bump jest from 29.6.1 to 29.6.2 (#12980) * chore(deps-dev): bump jest from 29.6.1 to 29.6.2 Bumps [jest](https://github.com/facebook/jest/tree/HEAD/packages/jest) from 29.6.1 to 29.6.2. - [Release notes](https://github.com/facebook/jest/releases) - [Changelog](https://github.com/jestjs/jest/blob/main/CHANGELOG.md) - [Commits](https://github.com/facebook/jest/commits/v29.6.2/packages/jest) --- updated-dependencies: - dependency-name: jest dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump re2 from 1.19.1 to 1.20.1 (#12977) * chore(deps): bump re2 from 1.19.1 to 1.20.1 Bumps [re2](https://github.com/uhop/node-re2) from 1.19.1 to 1.20.1. - [Release notes](https://github.com/uhop/node-re2/releases) - [Commits](https://github.com/uhop/node-re2/compare/1.19.1...1.20.1) --- updated-dependencies: - dependency-name: re2 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump @sentry/node from 7.62.0 to 7.63.0 (#12986) * chore(deps): bump @sentry/node from 7.62.0 to 7.63.0 Bumps [@sentry/node](https://github.com/getsentry/sentry-javascript) from 7.62.0 to 7.63.0. - [Release notes](https://github.com/getsentry/sentry-javascript/releases) - [Changelog](https://github.com/getsentry/sentry-javascript/blob/develop/CHANGELOG.md) - [Commits](https://github.com/getsentry/sentry-javascript/compare/7.62.0...7.63.0) --- updated-dependencies: - dependency-name: "@sentry/node" dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix(route): 少数派 -- 首页 ignores member articles previews (#12983) * Update index.js for route: sspai Add compatibility for member articles preview. * Update index.js of route sspai Clarify the code * style: auto format * feat(route): add 华理教务处 (#12982) * add ECUST-jwc rss route * fix code * fix review * style: auto format * fix(route): `/bilibili/user/video(article)` Broken for some uploaders (#12992) * Fix /bilibili/user/video Error message: Cannot read properties of undefined (reading 'name') * /bilibili/user/video returns error message if fail to fetch * feat(route): add 东南大学研究生院公告 (#12958) * feat(route): add 东南大学研究生院公告 * feat(route): add 东南大学研究生院公告 * add radar * format scripts * Update lib/v2/seu/radar.js * Update lib/v2/seu/yjs.js * Update lib/v2/seu/yjs.js * fix redundancy * refactor: migrate to v2 * fix: typo --------- * chore(deps): bump googleapis from 122.0.0 to 124.0.0 (#12984) * chore(deps): bump googleapis from 122.0.0 to 124.0.0 Bumps [googleapis](https://github.com/googleapis/google-api-nodejs-client) from 122.0.0 to 124.0.0. - [Release notes](https://github.com/googleapis/google-api-nodejs-client/releases) - [Changelog](https://github.com/googleapis/google-api-nodejs-client/blob/main/.release-please-manifest.json) - [Commits](https://github.com/googleapis/google-api-nodejs-client/compare/googleapis-v122.0.0...googleapis-v124.0.0) --- updated-dependencies: - dependency-name: googleapis dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps-dev): bump eslint from 8.45.0 to 8.46.0 (#12974) * chore(deps-dev): bump eslint from 8.45.0 to 8.46.0 Bumps [eslint](https://github.com/eslint/eslint) from 8.45.0 to 8.46.0. - [Release notes](https://github.com/eslint/eslint/releases) - [Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md) - [Commits](https://github.com/eslint/eslint/compare/v8.45.0...v8.46.0) --- updated-dependencies: - dependency-name: eslint dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> * chore: fix pnpm install * style: remove --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps-dev): bump eslint-config-prettier from 8.8.0 to 9.0.0 (#12988) * chore(deps-dev): bump eslint-config-prettier from 8.8.0 to 9.0.0 Bumps [eslint-config-prettier](https://github.com/prettier/eslint-config-prettier) from 8.8.0 to 9.0.0. - [Changelog](https://github.com/prettier/eslint-config-prettier/blob/main/CHANGELOG.md) - [Commits](https://github.com/prettier/eslint-config-prettier/compare/v8.8.0...v9.0.0) --- updated-dependencies: - dependency-name: eslint-config-prettier dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * refactor: replace re2 with re2-wasm (#12998) * refactor: replace re2 with re2-wasm * refactor: remove migrated routes * ci: remove foresight https://github.com/runforesight Foresight is no longer in service * fix: add back removed wasm chore: bump base OS image * fix(route): yuque (#13001) * chore(deps): bump twitter-api-v2 from 1.15.0 to 1.15.1 (#12987) * chore(deps): bump twitter-api-v2 from 1.15.0 to 1.15.1 Bumps [twitter-api-v2](https://github.com/plhery/node-twitter-api-v2) from 1.15.0 to 1.15.1. - [Release notes](https://github.com/plhery/node-twitter-api-v2/releases) - [Changelog](https://github.com/PLhery/node-twitter-api-v2/blob/master/changelog.md) - [Commits](https://github.com/plhery/node-twitter-api-v2/compare/1.15.0...1.15.1) --- updated-dependencies: - dependency-name: twitter-api-v2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump lru-cache from 10.0.0 to 10.0.1 (#12985) * chore(deps): bump lru-cache from 10.0.0 to 10.0.1 Bumps [lru-cache](https://github.com/isaacs/node-lru-cache) from 10.0.0 to 10.0.1. - [Changelog](https://github.com/isaacs/node-lru-cache/blob/main/CHANGELOG.md) - [Commits](https://github.com/isaacs/node-lru-cache/compare/v10.0.0...v10.0.1) --- updated-dependencies: - dependency-name: lru-cache dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps-dev): bump @vercel/nft from 0.22.6 to 0.23.0 (#12979) * chore(deps-dev): bump @vercel/nft from 0.22.6 to 0.23.0 Bumps [@vercel/nft](https://github.com/vercel/nft) from 0.22.6 to 0.23.0. - [Release notes](https://github.com/vercel/nft/releases) - [Commits](https://github.com/vercel/nft/compare/0.22.6...0.23.0) --- updated-dependencies: - dependency-name: "@vercel/nft" dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * build: fix vercel build (#13006) * chore(deps-dev): bump eslint from 8.46.0 to 8.47.0 (#13002) * chore(deps-dev): bump eslint from 8.46.0 to 8.47.0 Bumps [eslint](https://github.com/eslint/eslint) from 8.46.0 to 8.47.0. - [Release notes](https://github.com/eslint/eslint/releases) - [Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md) - [Commits](https://github.com/eslint/eslint/compare/v8.46.0...v8.47.0) --- updated-dependencies: - dependency-name: eslint dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * feat(route): /bilibili/user/dynamic add showEmoji parameter (#13009) * chore(deps): bump puppeteer from 21.0.2 to 21.0.3 (#13003) * chore(deps): bump puppeteer from 21.0.2 to 21.0.3 Bumps [puppeteer](https://github.com/puppeteer/puppeteer) from 21.0.2 to 21.0.3. - [Release notes](https://github.com/puppeteer/puppeteer/releases) - [Changelog](https://github.com/puppeteer/puppeteer/blob/main/release-please-config.json) - [Commits](https://github.com/puppeteer/puppeteer/compare/puppeteer-v21.0.2...puppeteer-v21.0.3) --- updated-dependencies: - dependency-name: puppeteer dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: TonyRL <TonyRL@users.noreply.github.com> * docs: update readme * docs: update image size * feat(route): add Grist (#12991) * initial commit * radar and maintainer updates * add route grist * add grist changes * Update docs/en/new-media.md * Update lib/v2/grist/featured.js * get featured article via APi * docs: spoiler --------- * feat(route): caixin weekly (#13017) * docs: fix caixin weekly * fix(route): Update bilibili maintainer.js (#13021) * feat(route): add Finology (#12993) * feat(route): add Finology * Update radar.js * Update docs/en/finance.md * Update lib/v2/finology/bullets.js * Update lib/v2/finology/mostViewed.js * Update lib/v2/finology/bullets.js * Update docs/en/finance.md * Update lib/v2/finology/tag.js * Update lib/v2/finology/mostViewed.js * Update lib/v2/finology/tag.js * add the suggested changes * use promise.allSettled * clean up * docs: add spoiler --------- * fix(route): Fix bloomberg story by using the latest story id api (#13013) * fix minor bugs * fix page sel * reformat * wordaround to fix the deepscan error * fix bloomberg when dealing with 404 direct link * fix processVideo in cyclic depend * remove the toggle * change into new story api * reorg the files * use prop not instead of lodash * remove useless exports * use parseVideo in diff files * refactor to remove deps * add try/catch for renderer page * use story api for newsletters * update features example * change audio * add tabularData --------- Co-authored-by: Chenfei Xu <chenfxu@ebay.com> * feat(route): Science.org Blogs (#13016) * initial commit * clean up code, run fmt, add docs * fix caching Co-authored-by: Tony <TonyRL@users.noreply.github.com> * Apply suggestions from code review Co-authored-by: Tony <TonyRL@users.noreply.github.com> * remove regex * Add config import * docs: cn docs * docs: better link --------- * chore: try fix arm64 build * chore(deps): bump @vuepress/plugin-pwa from 1.9.9 to 1.9.10 in /docs (#13033) Bumps [@vuepress/plugin-pwa](https://github.com/vuejs/vuepress/tree/HEAD/packages/@vuepress/plugin-pwa) from 1.9.9 to 1.9.10. - [Release notes](https://github.com/vuejs/vuepress/releases) - [Changelog](https://github.com/vuejs/vuepress/blob/master/CHANGELOG.md) - [Commits](https://github.com/vuejs/vuepress/commits/v1.9.10/packages/@vuepress/plugin-pwa) --- updated-dependencies: - dependency-name: "@vuepress/plugin-pwa" dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump vuepress from 1.9.9 to 1.9.10 in /docs (#13034) Bumps [vuepress](https://github.com/vuejs/vuepress/tree/HEAD/packages/vuepress) from 1.9.9 to 1.9.10. - [Release notes](https://github.com/vuejs/vuepress/releases) - [Changelog](https://github.com/vuejs/vuepress/blob/master/CHANGELOG.md) - [Commits](https://github.com/vuejs/vuepress/commits/v1.9.10/packages/vuepress) --- updated-dependencies: - dependency-name: vuepress dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump @vuepress/plugin-google-analytics in /docs (#13036) Bumps [@vuepress/plugin-google-analytics](https://github.com/vuejs/vuepress/tree/HEAD/packages/@vuepress/plugin-google-analytics) from 1.9.9 to 1.9.10. - [Release notes](https://github.com/vuejs/vuepress/releases) - [Changelog](https://github.com/vuejs/vuepress/blob/master/CHANGELOG.md) - [Commits](https://github.com/vuejs/vuepress/commits/v1.9.10/packages/@vuepress/plugin-google-analytics) --- updated-dependencies: - dependency-name: "@vuepress/plugin-google-analytics" dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump @vuepress/shared-utils from 1.9.9 to 1.9.10 in /docs (#13037) Bumps [@vuepress/shared-utils](https://github.com/vuejs/vuepress/tree/HEAD/packages/@vuepress/shared-utils) from 1.9.9 to 1.9.10. - [Release notes](https://github.com/vuejs/vuepress/releases) - [Changelog](https://github.com/vuejs/vuepress/blob/master/CHANGELOG.md) - [Commits](https://github.com/vuejs/vuepress/commits/v1.9.10/packages/@vuepress/shared-utils) --- updated-dependencies: - dependency-name: "@vuepress/shared-utils" dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump @vuepress/plugin-back-to-top in /docs (#13035) Bumps [@vuepress/plugin-back-to-top](https://github.com/vuejs/vuepress/tree/HEAD/packages/@vuepress/plugin-back-to-top) from 1.9.9 to 1.9.10. - [Release notes](https://github.com/vuejs/vuepress/releases) - [Changelog](https://github.com/vuejs/vuepress/blob/master/CHANGELOG.md) - [Commits](https://github.com/vuejs/vuepress/commits/v1.9.10/packages/@vuepress/plugin-back-to-top) --- updated-dependencies: - dependency-name: "@vuepress/plugin-back-to-top" dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * feat: add docusaurus website (#13039) * fix: github workflows * feat(route): `/zhihu/daily/section/:sectionId` add pubDate (#13020) * docs: clear service worker * docs: fix editUrl * docs: fix i18n * fix(radar): stockedge sub-domain (#13038) * docs: compatible with the old logo link * docs: add umami * docs: add back meilisearch (#13041) * docs: add back meilisearch * fix: remove unused variable * Revert "fix: remove unused variable" This reverts commit 52bec57a1ec8c54166793b31cf972131555dc6bc. What is React * fix(route): 更换财新最新文章获取方式 (#13023) * 更换财新最新文章获取方式。 * Update latest.js * Update latest.js * Update latest.js * style: auto format * docs: recover DocSidebarItem * docs: fix new docs link * fix(route): Foresight News (#13026) * fix(route): Foresight News * fix docs * fix tags * fix index items --------- * fix: rsshub routes update * fix: rsshub routes update * fix(route): Aqara News (#12921) * fix(route): Aqara News * feat(route): add Aqara category & tag * fix: crlf to lf * fix broken images * fix url --------- * chore(deps): bump @notionhq/client from 2.2.10 to 2.2.11 (#13030) * chore(deps): bump @notionhq/client from 2.2.10 to 2.2.11 Bumps [@notionhq/client](https://github.com/makenotion/notion-sdk-js) from 2.2.10 to 2.2.11. - [Release notes](https://github.com/makenotion/notion-sdk-js/releases) - [Commits](https://github.com/makenotion/notion-sdk-js/compare/v2.2.10...v2.2.11) --- updated-dependencies: - dependency-name: "@notionhq/client" dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps-dev): bump prettier from 3.0.1 to 3.0.2 (#13042) * chore(deps-dev): bump prettier from 3.0.1 to 3.0.2 Bumps [prettier](https://github.com/prettier/prettier) from 3.0.1 to 3.0.2. - [Release notes](https://github.com/prettier/prettier/releases) - [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md) - [Commits](https://github.com/prettier/prettier/compare/3.0.1...3.0.2) --- updated-dependencies: - dependency-name: prettier dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump @sentry/node from 7.63.0 to 7.64.0 (#13032) * chore(deps): bump @sentry/node from 7.63.0 to 7.64.0 Bumps [@sentry/node](https://github.com/getsentry/sentry-javascript) from 7.63.0 to 7.64.0. - [Release notes](https://github.com/getsentry/sentry-javascript/releases) - [Changelog](https://github.com/getsentry/sentry-javascript/blob/develop/CHANGELOG.md) - [Commits](https://github.com/getsentry/sentry-javascript/compare/7.63.0...7.64.0) --- updated-dependencies: - dependency-name: "@sentry/node" dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump googleapis from 124.0.0 to 125.0.0 (#13043) * chore(deps): bump googleapis from 124.0.0 to 125.0.0 Bumps [googleapis](https://github.com/googleapis/google-api-nodejs-client) from 124.0.0 to 125.0.0. - [Release notes](https://github.com/googleapis/google-api-nodejs-client/releases) - [Changelog](https://github.com/googleapis/google-api-nodejs-client/blob/main/.release-please-manifest.json) - [Commits](https://github.com/googleapis/google-api-nodejs-client/compare/googleapis-v124.0.0...googleapis-v125.0.0) --- updated-dependencies: - dependency-name: googleapis dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps-dev): bump lint-staged from 13.2.3 to 14.0.0 (#13031) * chore(deps-dev): bump lint-staged from 13.2.3 to 14.0.0 Bumps [lint-staged](https://github.com/okonet/lint-staged) from 13.2.3 to 14.0.0. - [Release notes](https://github.com/okonet/lint-staged/releases) - [Commits](https://github.com/okonet/lint-staged/compare/v13.2.3...v14.0.0) --- updated-dependencies: - dependency-name: lint-staged dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps-dev): bump @tsconfig/docusaurus in /website (#13047) Bumps [@tsconfig/docusaurus](https://github.com/tsconfig/bases/tree/HEAD/bases) from 1.0.5 to 2.0.0. - [Commits](https://github.com/tsconfig/bases/commits/HEAD/bases) --- updated-dependencies: - dependency-name: "@tsconfig/docusaurus" dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump pinyin-pro from 3.16.2 to 3.16.3 in /website (#13059) Bumps [pinyin-pro](https://github.com/zh-lx/pinyin-pro) from 3.16.2 to 3.16.3. - [Release notes](https://github.com/zh-lx/pinyin-pro/releases) - [Changelog](https://github.com/zh-lx/pinyin-pro/blob/main/CHANGELOG.md) - [Commits](https://github.com/zh-lx/pinyin-pro/commits) --- updated-dependencies: - dependency-name: pinyin-pro dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * docs: redirect old docs urls (#13060) * docs: redirect old docs urls * fix: fix start en docs script * docs: fix redirects in netlify * docs: remove redirect for non matching /en/* to /en/404 This proceeds before docusaurus' redirects which breaks /en/category.html -> /en/category -> /en/routes/category * feat(route): IGDB - 学术活动 (#13027) * feat(route): IGDB - 学术活动 * fix: sort router & use https * fix: sort radar --------- * chore(deps-dev): bump @vercel/nft from 0.23.0 to 0.23.1 (#13029) * chore(deps-dev): bump @vercel/nft from 0.23.0 to 0.23.1 Bumps [@vercel/nft](https://github.com/vercel/nft) from 0.23.0 to 0.23.1. - [Release notes](https://github.com/vercel/nft/releases) - [Commits](https://github.com/vercel/nft/compare/0.23.0...0.23.1) --- updated-dependencies: - dependency-name: "@vercel/nft" dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * docs: pwa (#13064) * chore(deps-dev): bump nock from 13.3.2 to 13.3.3 (#13058) * chore(deps-dev): bump nock from 13.3.2 to 13.3.3 Bumps [nock](https://github.com/nock/nock) from 13.3.2 to 13.3.3. - [Release notes](https://github.com/nock/nock/releases) - [Changelog](https://github.com/nock/nock/blob/main/CHANGELOG.md) - [Commits](https://github.com/nock/nock/compare/v13.3.2...v13.3.3) --- updated-dependencies: - dependency-name: nock dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix(route): fix `/bilibili/followings/dynamic` Cannot convert object to primitive value (#13065) * feat(route): tiktok iframe (#13066) * chore: add warp in docker-compose * feat(router): 增加 xiaote.com 论坛路由 (#13062) * feat: 增加 xiaote.com 论坛路由 * feat: add radar.js * feat: format code * bugfix: radar error * fix: all marge issuse * feat(route): add descriptions for nwafu (#13067) * feat(route): add descriptions for nwafu docs: update the docs scriptsformat scripts refactor: migrate to v2 * Create radar.js * Update radar.js * feat(route): fix the route * Update website/docs/routes/university.md * Update website/docs/routes/university.md * Update all.js * Update all.js * Update lib/v2/nwafu/all.js * Update all.js --------- * style: auto format * feat(parameter): use re2js instead (#13072) * feat(parameter): use re2js instead * fix: add back comment * chore(deps): bump clsx from 1.2.1 to 2.0.0 in /website (#13045) Bumps [clsx](https://github.com/lukeed/clsx) from 1.2.1 to 2.0.0. - [Release notes](https://github.com/lukeed/clsx/releases) - [Commits](https://github.com/lukeed/clsx/compare/v1.2.1...v2.0.0) --- updated-dependencies: - dependency-name: clsx dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump prism-react-renderer from 1.3.5 to 2.0.6 in /website (#13046) * chore(deps): bump prism-react-renderer from 1.3.5 to 2.0.6 in /website Bumps [prism-react-renderer](https://github.com/FormidableLabs/prism-react-renderer) from 1.3.5 to 2.0.6. - [Release notes](https://github.com/FormidableLabs/prism-react-renderer/releases) - [Commits](https://github.com/FormidableLabs/prism-react-renderer/compare/v1.3.5...prism-react-renderer@2.0.6) --- updated-dependencies: - dependency-name: prism-react-renderer dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> * fix: `prism-react-renderer` v2 changes --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps-dev): bump typescript from 4.7.4 to 5.1.6 in /website (#13044) * chore(deps-dev): bump typescript from 4.7.4 to 5.1.6 in /website Bumps [typescript](https://github.com/Microsoft/TypeScript) from 4.7.4 to 5.1.6. - [Release notes](https://github.com/Microsoft/TypeScript/releases) - [Commits](https://github.com/Microsoft/TypeScript/compare/v4.7.4...v5.1.6) --- updated-dependencies: - dependency-name: typescript dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> * chore: use group updates --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix(route): fix /baidu/tieba/forum time (#13077) * feat(route): add xhu (#13055) * feat(route): add xhu * add xhu doc * Remove unnecessary URL parameter * Store xhu cookie in cache * Add xhu to radar.js * Update website/docs/routes/social-media.md --------- * style: auto format * fix(route): update v2/scitation to v2/aip (#12955) * fix(route): scitation/journal * fix(docs): scitation/journal * fix(docs): (en) scitation/journal * chores * fix(radar): scitation/journal * fix(radar): scitation/journal * fix(radar): scitation/journal * style: auto format * fix(route): mv scitation to aip * fix(route): v2/AIP chores * fix(docs): v2/aip docs * fix(docs): chores: jounus/advanced/advanced-feed * fix(docs): update en docs * fix(docs): not right close the <RouteEn> * fix(docs): correct some mistake * Update lib/v2/aip/radar.js --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * chore(deps): bump googleapis from 125.0.0 to 126.0.0 (#13073) * chore(deps): bump googleapis from 125.0.0 to 126.0.0 Bumps [googleapis](https://github.com/googleapis/google-api-nodejs-client) from 125.0.0 to 126.0.0. - [Release notes](https://github.com/googleapis/google-api-nodejs-client/releases) - [Changelog](https://github.com/googleapis/google-api-nodejs-client/blob/main/release-please-config.json) - [Commits](https://github.com/googleapis/google-api-nodejs-client/compare/googleapis-v125.0.0...googleapis-v126.0.0) --- updated-dependencies: - dependency-name: googleapis dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump puppeteer from 21.0.3 to 21.1.0 (#13074) * chore(deps): bump puppeteer from 21.0.3 to 21.1.0 Bumps [puppeteer](https://github.com/puppeteer/puppeteer) from 21.0.3 to 21.1.0. - [Release notes](https://github.com/puppeteer/puppeteer/releases) - [Changelog](https://github.com/puppeteer/puppeteer/blob/main/release-please-config.json) - [Commits](https://github.com/puppeteer/puppeteer/compare/puppeteer-v21.0.3...puppeteer-v21.1.0) --- updated-dependencies: - dependency-name: puppeteer dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix(route): 2048 (#13079) * fix(route): 2048 * fix: codeql * fix: empty link * fix: url condition * feat(route): add more routes for nwafu (#13076) * feat(route): add more routes for nwafu * update all.js * Update all.js * style: auto format * fix(route): Fix bloomberg story when parsing react rendered page and videos missing id (#13082) * fix minor bugs * fix page sel * reformat * wordaround to fix the deepscan error * fix bloomberg when dealing with 404 direct link * fix processVideo in cyclic depend * remove the toggle * change into new story api * reorg the files * use prop not instead of lodash * remove useless exports * use parseVideo in diff files * refactor to remove deps * add try/catch for renderer page * use story api for newsletters * update features example * change audio * add tabularData * fix bug on bloomberg react page --------- Co-authored-by: Chenfei Xu <chenfxu@ebay.com> * docs: documentation prioritizes English and maintains only one route list (#13075) * chore: fix format.js * docs: move the Netlify config to the right place * Revert "docs: move the Netlify config to the right place" This reverts commit 3b588021b56b26559299209c2323445711aff48c. * docs: try language redirection * docs: try language redirection * fix: miniflux optional parameter * docs: i18nable component (#13084) * docs: try language redirection * docs: update README * fix: pixiv bypassCdn default value * docs: i18n sidebar, fix code block (#13085) * docs: i18n sidebar * docs: `code-group` to `tabs` * docs: admonitions use default title * docs: fix route * docs: fix build * docs: fix add-docs admonitions * docs: unwrap `details` wrapped content * docs: mention no i18n switch in dev mode * docs: fix add-docs rendering * docs: fix typo * chore: fix format * feat(route): add xhu collection, question and zhuanlan (#13087) * feat(route): add /zhihu/xhu/question * feat(route): add /zhihu/xhu/zhuanlan * feat(route): add /zhihu/xhu/collection * feat(route): add dlnews (#12997) * feat(route):add DLNEWS * clean up * use embedded JSON * fix: docusaurus style md * add description.art and rate limit * increase the concurency * fix: fix radar docs link * docs: move to finance --------- * chore: update meilisearch config `p`: general content `.params`: route params `table`: route params table `code`: env * feat(route): 百度贴吧按帖子创建时间排序 (#13102) * feat(route): add tieba sort by created time * Put /tieba/forum/good before /tieba/forum * Add parse for creation dates of format YYYY-MM * fix(route): NEW字幕组 (#12951) * fix(route): NEW字幕组 * add comments * fix: remove unnecessary await * fix: remove unnecessary async * fix: remove unnecessary async * fix: add cache for first response * fix typo * fix: new cache key * docs: use default title --------- * chore(deps-dev): bump lint-staged from 14.0.0 to 14.0.1 (#13094) * chore(deps-dev): bump lint-staged from 14.0.0 to 14.0.1 Bumps [lint-staged](https://github.com/okonet/lint-staged) from 14.0.0 to 14.0.1. - [Release notes](https://github.com/okonet/lint-staged/releases) - [Commits](https://github.com/okonet/lint-staged/compare/v14.0.0...v14.0.1) --- updated-dependencies: - dependency-name: lint-staged dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump chrono-node from 2.6.4 to 2.6.5 (#13096) * chore(deps): bump chrono-node from 2.6.4 to 2.6.5 Bumps [chrono-node](https://github.com/wanasit/chrono) from 2.6.4 to 2.6.5. - [Release notes](https://github.com/wanasit/chrono/releases) - [Commits](https://github.com/wanasit/chrono/compare/v2.6.4...v2.6.5) --- updated-dependencies: - dependency-name: chrono-node dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps-dev): bump jest from 29.6.2 to 29.6.3 (#13095) * chore(deps-dev): bump jest from 29.6.2 to 29.6.3 Bumps [jest](https://github.com/jestjs/jest/tree/HEAD/packages/jest) from 29.6.2 to 29.6.3. - [Release notes](https://github.com/jestjs/jest/releases) - [Changelog](https://github.com/jestjs/jest/blob/main/CHANGELOG.md) - [Commits](https://github.com/jestjs/jest/commits/v29.6.3/packages/jest) --- updated-dependencies: - dependency-name: jest dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump re2js from 0.3.2 to 0.3.3 (#13105) * chore(deps): bump re2js from 0.3.2 to 0.3.3 Bumps [re2js](https://github.com/le0pard/re2js) from 0.3.2 to 0.3.3. - [Release notes](https://github.com/le0pard/re2js/releases) - [Commits](https://github.com/le0pard/re2js/compare/0.3.2...0.3.3) --- updated-dependencies: - dependency-name: re2js dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps-dev): bump eslint-plugin-n from 16.0.1 to 16.0.2 (#13106) * chore(deps-dev): bump eslint-plugin-n from 16.0.1 to 16.0.2 Bumps [eslint-plugin-n](https://github.com/eslint-community/eslint-plugin-n) from 16.0.1 to 16.0.2. - [Release notes](https://github.com/eslint-community/eslint-plugin-n/releases) - [Commits](https://github.com/eslint-community/eslint-plugin-n/compare/16.0.1...16.0.2) --- updated-dependencies: - dependency-name: eslint-plugin-n dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix(route): add tophub cookie (#13104) * fix(route): add tophub cookie * add docs for tophub cookie * Update docs * docs: reorder env * feat(route): Start on support for generic discourse forums. (#13063) * Start on support for generic discourse forums. * Add doc. * Update doc. * Add exception. * Update doc. * Update bbs.md * test: add discourse config * docs: fix typo * fix: guard condition --------- * fix(route): leetcode articles (#13108) * refactor(route): move tophub to v2 * fix(route): 晚点报道 (#13092) * fix(route): 晚点报道 * fix: add programa * fix typo * update lib/v2/latepost/index.js --------- * fix(route): 南京大学研究生院 (#13116) * feat(route): Add Discourse notifications support (#13114) * Adding discourse notifications support. * Refactor & add doc. * feat(route): add router YouTube Live (#13110) * feat(route): add router YouTube Live * add docs * Insert new entries in alphabetical order. --------- * chore(deps-dev): bump typescript from 5.1.6 to 5.2.2 in /website (#13119) Bumps [typescript](https://github.com/Microsoft/TypeScript) from 5.1.6 to 5.2.2. - [Release notes](https://github.com/Microsoft/TypeScript/releases) - [Commits](https://github.com/Microsoft/TypeScript/commits) --- updated-dependencies: - dependency-name: typescript dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix(route): fix the title error for the YouTube Live (#13120) * feat(route): add router YouTube Live * add docs * Insert new entries in alphabetical order. * Fix the error in the title. --------- * feat(route): Bilibili UP主动态 参数格式更新 & 增加显示专栏全文参数 (#13109) * feat(route): /bilibili/user(followings)/dynamic change to routeParams * feat(route): Add displayArticle parameter * small change of uid * Original link can click to jump * chore(deps-dev): bump jest from 29.6.3 to 29.6.4 (#13118) * chore(deps-dev): bump jest from 29.6.3 to 29.6.4 Bumps [jest](https://github.com/jestjs/jest/tree/HEAD/packages/jest) from 29.6.3 to 29.6.4. - [Release notes](https://github.com/jestjs/jest/releases) - [Changelog](https://github.com/jestjs/jest/blob/main/CHANGELOG.md) - [Commits](https://github.com/jestjs/jest/commits/v29.6.4/packages/jest) --- updated-dependencies: - dependency-name: jest dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * feat(route): bilibili 动态中的视频链接默认改为 BV 号并增加配置参数;修改 专栏显示全文 的参数来源 (#13122) * feat(route): 添加 百度股市通 * fix: 修改 百度股市通 路径 * docs: 添加 百度股市通 文档 * feat: 添加 radar 支持 * fix: 按字母顺序插入新路由 * feat(route): 新增 腾讯新闻 - 新型冠状病毒肺炎疫情实时追踪 * fix: 优化 腾讯新闻 - 新型冠状病毒肺炎疫情实时追踪 的标题 * fix: 修复 腾讯新闻 - 新型冠状病毒肺炎疫情实时追踪 部分情况下的非空判断 * fix: 修复 地区名称标题的问题 * fix: 腾讯新闻 - 新型冠状病毒肺炎疫情实时追踪 的 guid 增加 pubDate * fix: 修复 腾讯新闻 - 新型冠状病毒肺炎疫情实时追踪 guid 中添加 pubDate * fix: 修改 腾讯新闻 - 新型冠状病毒肺炎疫情实时追踪 的 title * feat(route): 修复 HelloGitHub 的 月刊 路由 * fix: remove guid * feat(route): 新增 bilibili 热搜 * feat(route): 完善 bilibili热搜 的 radar * fix: 优化 bilibili热搜 list 的非空判断 * fix: 修复 bilibili热搜路由失效 fix #12632 * fix(route): 修复 bilibili热搜路由 的校验逻辑计算 * fix: 优化 bilibili/utils 的 addVerifyInfo 逻辑 * feat(route): bilibili 动态中的视频链接默认改为 BV 号并增加配置参数;修改 专栏显示全文 的参数来源 * fix(route): 修复 fulltext 来源错误;修复 addVerifyInfo 逻辑错误 * fix(route): 移除不必要的参数 * chore(deps-dev): bump eslint from 8.47.0 to 8.48.0 (#13124) * chore(deps-dev): bump eslint from 8.47.0 to 8.48.0 Bumps [eslint](https://github.com/eslint/eslint) from 8.47.0 to 8.48.0. - [Release notes](https://github.com/eslint/eslint/releases) - [Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md) - [Commits](https://github.com/eslint/eslint/compare/v8.47.0...v8.48.0) --- updated-dependencies: - dependency-name: eslint dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> * chore: fix pnpm install --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.no * chore(deps): bump googleapis from 126.0.0 to 126.0.1 (#13125) * chore(deps): bump googleapis from 126.0.0 to 126.0.1 Bumps [googleapis](https://github.com/googleapis/google-api-nodejs-client) from 126.0.0 to 126.0.1. - [Release notes](https://github.com/googleapis/google-api-nodejs-client/releases) - [Changelog](https://github.com/googleapis/google-api-nodejs-client/blob/main/release-pl…
该 PR 相关 Issue / Involved Issue
Close #
路由地址示例 / Example for the Proposed Route(s)
新 RSS 路由检查表 / New RSS Route Checklist
Puppeteer
说明 / Note