⭐ If you would like to buy me a coffee, well thank you very much that is mega kind! : https://www.buymeacoffee.com/honeyvig Hire a web Developer and Designer to upgrade and boost your online presence with cutting edge Technologies
Showing posts with label Case Studies. Show all posts
Showing posts with label Case Studies. Show all posts

Monday, June 23, 2025

Integrating Localization Into Design Systems

 

Learn how two designers tackled the challenges of building a localization-ready design system for a global audience. This case study dives into how Rebecca and Mark combined Figma Variables and design tokens to address multilingual design issues, such as text overflow, RTL layouts, and font inconsistencies. They share key lessons learned and the hurdles they faced — including Figma’s limitations — along with the solutions they developed to create dynamic, scalable designs that adapt seamlessly across languages, themes, and densities. If you’re navigating the complexities of internationalization in design systems, this article is for you.

Mark and I work as product designers for SAS, a leader in analytics and artificial intelligence recognized globally for turning data into valuable insights. Our primary role is to support the token packages and component libraries for the SAS Filament Design System. SAS’ customer base is global, meaning people from diverse countries, cultures, and languages interact with products built with the Filament Design System.

SAS designers use Figma libraries developed by the Filament Design System team to create UX specifications. These high-fidelity designs are typically crafted in English, unknowingly overlooking multilingual principles, which can result in layout issues, text overflow, and challenges with right-to-left (RTL) languages. These issues cascade into the application, ultimately creating usability issues for SAS customers. This highlights the need to prioritize localization from the start of the design process.

With the introduction of Figma Variables, alongside the advancements in design tokens, we saw an opportunity for designers. We imagined a system where a Figma design could dynamically switch between themes, densities, and even languages.

This would allow us to design and test multilingual capabilities more effectively, ensuring our design system was both flexible and adaptable.

While researching localization integration for design systems, we realized a significant gap in existing documentation on supporting localization and internationalization in design tokens and Figma Variables. Many of the challenges we faced, such as managing typography across locales or adapting layouts dynamically, were undocumented or only partially addressed in available resources.

Our story demonstrates how combining foundational principles of multilingual design with design tokens can help tackle the complexities of language switching in design systems. We are not arguing that our approach is the best, but given the lack of documentation available on the subject, we hope it will get the conversation started.

But before we start, it’s essential to understand the distinction between Localization (L10n) and Internationalization (I18n).

Localization (L10n) refers to the process of adapting designs for specific languages, regions, or cultures and involves the following:

  • Translating text;
  • Adjusting layouts to accommodate language-specific requirements, such as longer or shorter text strings or right-to-left (RTL) text for languages like Arabic;
  • Ensuring visual elements are culturally appropriate and resonate with the target audience.

Internationalization (I18n) is the preparation phase, ensuring designs are flexible and adaptable to different languages and regions. Key considerations in Figma include:

  • Using placeholder text to represent dynamic content;
  • Setting up constraints for dynamic resizing to handle text expansion or contraction;
  • Supporting bi-directional text for languages that require RTL layouts.

These concepts are not only foundational to multilingual design but also integral to delivering inclusive and accessible experiences to global users.

Pre-Figma Setup: Building A Framework

Understanding Our Design Token System

Before diving deeper, it’s crucial to understand that our design tokens are stored in JSON files. These JSON files are managed in an application we call “Token Depot,” hosted on our corporate GitHub.

We utilize the Tokens Studio plugin (pro plan) to transform these JSON files into Figma libraries. For us, design tokens are synonymous with variables — we don’t create additional variables that only exist in Figma. However, we do create styles in Figma that serve as “recipe cards” for specific HTML elements. For instance, an H2 might include a combination of font-family, font-size, and font-weight.

It’s important to note that our design token values are directly tied to CSS-based values.

Initial Setup: Theme Switching And Localization

In 2022, we took on the massive task of refactoring all our token names to be more semantic. At that time, we were only concerned with theme switching in our products.

Our tokens were re-categorized into the following groups:

  • Color
    • Brand colors (SAS brand colors)
    • Base colors (references to Brand colors)
  • Typography (e.g., fonts, spacing, styles)
  • Space (e.g., padding, margins)
  • Size (e.g., icons, borders)
  • Style (e.g., focus styles)
  • Motion (e.g., animations)
  • Shadow.

In our early setup:

  • A core folder contained JSON files for values unaffected by theme or brand.
  • Brand folders included three JSON files (one for each theme). These were considered “English” by default.
  • A separate languages folder contained overrides for other locales, stacked on top of brand files to replace specific token values.

Our JSON files were configured with English as the default. Other locales were managed with a set of JSON files that included overrides for English. These overrides were minimal, focusing mainly on font and typography adjustments. For example, bold typefaces often create issues because many languages like Chinese, Japanese, or Korean (CJK languages) fonts lack distinct bold versions. Thus, we replaced the font-weight token value from 700 to 400 in our CJK locales.

We also update the values for font-family, letter spacing, font-style, and font-variant tokens. In Figma, our application screens were originally designed in English, and in 2023, we only implemented theme-switching modes, not language options. Additionally, we created detailed lists to document which design tokens could be converted to Figma variables and which could not, as the initial release of variables supported only a limited set.

Introducing Density Switching

The introduction of density switching in our products marked a significant turning point. This change allowed us to revisit and improve how we handled localization and token management. The first thing we had to figure out was the necessary token sorting. We ended up with the following list:

Tokens Impact By Theme And Density

Unaffected by Theme or Density:

  • Color
  • Brand colors
  • Base colors
  • Motion
  • Shadow
  • Size
  • Border size
  • Outline size
  • Typography
  • Base font size
  • Letter spacing and word spacing
  • Overflow, text, and word style tokens.

Tokens Impacted by Density:

  • Typography
  • Font sizes
  • Line Height
  • Font spacing
  • Size
  • Border radius
  • Icon sizes
  • Space
  • Base spacing.

Tokens Impacted by Theme:

  • Colors
  • Action, body, container, dataviz, display, heading, highlight, icon, label, status, syntax, tag, text, thumbnail, and zero-stat
  • Size
  • Border size
  • Typography
  • Font-family
  • Style
  • Action (focus styles).

With density, we expanded locale-specific value changes beyond font-family, letter spacing, font-style, and font-variant tokens to additionally include:

  • Font sizes
  • Icon sizes
  • Line height
  • Spacing
  • Border radius.

Revisiting our type scale and performing numerous calculations, we documented the required token value changes for all the locales across the density. This groundwork enabled us to tackle the restructuring of our JSON files effectively.

JSON File Restructuring

In our token repository, we:

  1. Updated the tokens in the core folder.
  2. Added a density folder and a language folder in each brand.

After collaborating with our front-end development team, we decided to minimize the number of JSON files. Too many files introduce complexity and bugs and hinder performance. Instead of creating a JSON file for each language-density combination, we defined the following language categories:

Language Categories

  • Western European and Slavic Languages
    • Polish, English, French, German, and Spanish
  • Chinese Languages
    • Simplified and traditional scripts
  • Middle Eastern and East Asian Languages
    • Arabic, Hebrew, Japanese, Korean, Thai, and Vietnamese
  • Global Diverse
    • Africa, South Asia, Pacific, and Indigenous languages, Uralic, and Turkic groups.

These categories became our JSON files, with one file per density level. Each file contained tokens for font size, icon size, line height, spacing, and border-radius values. For example, all Chinese locales shared consistent values regardless of font-family.

In addition, we added a folder containing JSON files per locale, overriding core values and theme folders, such as font-family.

Figma Setup: Bridging Tokens And Design

Token Studio Challenges

After restructuring our JSON files, we anticipated gaining support for typography variables in the Tokens Studio plugin. Instead, Tokens Studio released version 2.0, introducing a major shift in workflow. Previously, we imported JSON files directly into Figma and avoided pushing changes back through the plugin. Adjusting to the new version required us to relearn how to use the plugin effectively.

Our first challenge was navigating the complexity of the import process. The $metadata.json and $themes.json files failed to overwrite correctly during imports, resulting in duplicate collections in Figma when exporting variables. Despite recreating the required theme structure within the plugin, the issue persisted. To resolve this, we deleted the existing $metadata.json and $themes.json files from the repository before pulling the updated GitHub repo into the plugin. However, even with this solution, we had to manually remove redundant collections that appeared during the export process.

Once we successfully migrated our tokens from JSON files into Figma using the Tokens Studio plugin, we encountered our next challenge.

Initially, we used only “English” and theme modes in Figma, relying primarily on styles since Figma’s early variable releases lacked support for typography variables. Now, with the goal of implementing theme, density, and language switching, we needed to leverage variables — including typography variables. While the token migration successfully brought in the token names as variable names and the necessary modes, some values were missing.

Typography variables, though promising in concept, were underwhelming in practice. For example, Figma’s default line-height multiplier for “auto” was 1.2, below the WCAG minimum of 1.5. Additionally, our token values used line-height multipliers, which weren’t valid as Figma variable values. While a percentage-based line-height value is valid in CSS, Figma variables don’t support percentages.

Our solution involved manually calculating pixel values for line heights across all typography sizes, locale categories, and densities. These values were entered as local variables in Figma, independent of the design token system. This allowed us to implement correct line-height changes for density and locale switches. The process, however, was labor-intensive, requiring the manual creation of hundreds of local variables. Furthermore, grouping font sizes and line heights into Figma styles required additional manual effort due to the lack of support for line-height multipliers or percentage-based variables.

Examples:

  • For CJK locales, medium and low density use a base font size of 16px, while high density uses 18px.
  • Western European and Slavic languages use 14px for medium density, 16px for high, and 12px for low density.

Additional Challenges

  • Figma vs. Web Rendering
    In Figma, line height centers text visually within the text box. In CSS, it affects spacing differently depending on the box model. This mismatch required manual adjustments, especially in light of upcoming CSS properties like leading-trim.
  • Letter-Spacing Issues
    While CSS defaults to “normal” for letter-spacing, Figma requires numeric values. Locale-specific resets to “normal” couldn’t utilize variables, complicating implementation.
  • Font-Family Stacks
    • Example stack for Chinese:
      font-family-primary: 'AnovaUI', '微软雅黑体', 'Microsoft YaHei New', '微软雅黑', 'Microsoft Yahei', '宋体', 'SimSun', 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif.

Starting with a Western font ensured proper rendering of Latin characters and symbols while maintaining brand consistency. However, Figma’s designs using only AnovaUI (SAS Brand Custom font) couldn’t preview locale-based substitutions via system fonts, complicating evaluations of mixed-content designs.

Finally, as we prepared to publish our new library, we encountered yet another challenge: Figma Ghosts.

What Are Figma Ghost Variables? #

Figma “ghost variables” refer to variables that remain in a Figma project even after they are no longer linked to any design tokens, themes, or components.

These variables often arise due to incomplete deletions, improper imports, or outdated metadata files. Ghost variables may appear in Figma’s variable management panel but are effectively “orphaned,” as they are disconnected from any meaningful use or reference.

Why They Cause Issues for Designers:

  • Clutter and Confusion
    Ghost variables make the variable list longer and harder to navigate. Designers might struggle to identify which variables are actively in use and which are obsolete.
  • Redundant Work
    Designers might accidentally try to use these variables, leading to inefficiencies or design inconsistencies when the ghost variables don’t function as expected.
  • Export and Sync Problems
    When exporting or syncing variables with a design system or repository, ghost variables can introduce errors, duplicates, or conflicts. This complicates maintaining alignment between the design system and Figma.
  • Increased Maintenance Overhead
    Detecting and manually deleting ghost variables can be time-consuming, particularly in large-scale projects with extensive variable sets.
  • Thematic Inconsistencies
    Ghost variables can create inconsistencies across themes, as they might reference outdated or irrelevant styles, making it harder to ensure a unified look and feel.

Addressing ghost variables requires careful management of design tokens and variables, often involving clean-up processes to ensure only relevant variables remain in the system.

Cleaning Up Ghost Variables

To avoid the issues in our Figma libraries, we first had to isolate ghost variables component by component. By selecting a symbol in Figma and navigating the applied variable modes, we had a good sense of which older versions of variables the symbol was still connected to. We found disconnected variables in the component library and our icon library, which resulted in compounded ghost variables across the system. We found that by traversing the layer panel, along with a fantastic plug-in called “Swap Variables,” we were able to remap all the ghost variables in our symbols.

If we had not completed the clean-up step, designers would not be able to access the overrides for theme, density, and locale.

Designing Symbols For Localization

To ensure Figma symbols support language swapping, we linked all text layers to our new variables, including font-family, font-size, and line height.

We do not use Figma’s variable feature to define text strings for each locale (e.g., English, Spanish, French) because, given the sheer breadth and depth of our Products and solutions, it would simply be too daunting a task to undertake. For us, using an existing plug-in, such as “Translator,” gives us what we need.

After ensuring all text layers were remapped to variables, along with the “Translator” plug-in, we were able to swap entire screens to a new language. This allowed us to start testing our symbols for unforeseen layout issues.

We discovered that some symbols were not supporting text wrapping when needed (e.g., accommodating longer words in German or shorter ones in Japanese). We isolated those issues and updated them to auto-layout for flexible resizing. This approach ensured all our Figma symbols were scalable and adaptable for multilingual support.

Delivering The System

With our component libraries set up to support localization, we were ready to deliver our component libraries to product designers. As a part of this step, we crafted a “Multilingual Design Cheat Sheet” to help designers understand how to set up their application mockups with Localization and Internationalization in mind.

Multilingual Design Cheat Sheet:

  1. General Principles
    • Design flexible layouts that can handle text wrapping and language-specific requirements such as right-to-left orientations.
    • Use real content during design and development to identify localization issues such as spacing and wrapping.
    • Research the cultural expectations of your target audience to avoid faux pas.
  2. Text & Typography
    • Use Filament Design Systems fonts to ensure support of all languages.
    • Avoid custom fonts that lack bold or italic styles for non-Latin scripts like CJK languages.
    • Reserve additional space for languages like German or Finnish.
    • Avoid hardcoded widths for text containers and use auto-layout to ensure long text strings are readable.
    • The Filament Design System tokens adjust line height per language; make sure you are using variables for line-height.
    • Use bold sparingly, as Filament tokens override bold styling in some languages. Instead, opt for alternative emphasis methods (e.g., color or size).
  3. Layout & Design
    • Mirror layouts for RTL languages (e.g., Arabic, Hebrew). Align text, icons, and navigation appropriately for the flow of the language.
    • Use auto-layout to accommodate varying text lengths.
    • Avoid embedding text in images to simplify localization.
    • Allow ample spacing around text elements to prevent crowding.
  4. Language-Specific Adjustments
    • Adapt formats based on locale (e.g., YYYY/MM/DD vs. MM/DD/YYYY).
    • Use metric or imperial units based on the region.
    • Test alignments and flows for LTR and RTL languages.
  5. Localization Readiness
    • Avoid idioms, cultural references, or metaphors that may not translate well.
    • Provide space for localized images, if necessary.
    • Use Figma translation plug-ins to test designs for localization readiness and use real translations rather than Lorem Ipsum.
    • Test with native speakers for language-specific usability issues.
    • Check mirrored layouts and interactions for usability in RTL languages.

Lessons Learned And Future Directions

Lessons Learned

In summary, building a localization-ready design system was a complex yet rewarding process that taught Mark and me several critical lessons:

  • Localization and internationalization must be prioritized early.
    Ignoring multilingual principles in the early stages of design creates cascading issues that are costly to fix later.
  • Semantic tokens are key.
    Refactoring our tokens to be more semantic streamlined the localization process, reducing complexity and improving maintainability.
  • Figma variables are promising but limited.
    While Figma Variables introduced new possibilities, their current limitations — such as lack of percentage-based line-height values and manual setup requirements — highlight areas for improvement.
  • Automation is essential.
    Manual efforts, such as recalculating and inputting values for typography and density-specific tokens, are time-intensive and prone to error. Plugins like “Translator” and “Swap Variables” proved invaluable in streamlining this work.
  • Collaboration is crucial.
    Close coordination with front-end developers ensured that our JSON restructuring efforts aligned with performance and usability goals.
  • Testing with real content is non-negotiable.
    Design issues like text wrapping, RTL mirroring, and font compatibility only became apparent when testing with real translations and flexible layouts.

Future Directions

As we look ahead, our focus is on enhancing the Filament Design System to better support global audiences and simplify the localization process for designers:

  • Automatic mirrored layouts for RTL languages.
    We plan to develop tools and workflows that enable seamless mirroring of layouts for right-to-left languages, ensuring usability for languages like Arabic and Hebrew.
  • Improved figma integration.
    Advocacy for Figma enhancements, such as percentage-based line-height support and better handling of variable imports, will remain a priority.
  • Advanced automation tools.
    Investing in more robust plugins and custom tools to automate the calculation and management of tokens across themes, densities, and locales will reduce manual overhead.
  • Scalable localization testing framework.
    Establishing a framework for native speaker testing and real-world content validation will help us identify localization issues earlier in the design process.
  • Expanding the multilingual design cheat sheet.
    We will continue to refine and expand the cheat sheet, incorporating feedback from designers to ensure it remains a valuable resource.
  • Community engagement.
    By sharing our findings and lessons, we aim to contribute to the broader design community, fostering discussions around integrating localization and internationalization in design systems.

Through these efforts, Mark and I hope to create a more inclusive, scalable, and efficient design system that meets the diverse needs of our global audience while empowering SAS designers to think beyond English-first designs.

Wednesday, February 28, 2024

Five-Second Testing: Taking A Closer Look At First Impressions (Case Study)

 Five-second testing is a popular method of usability research used in the industry. It is a quick and effective way to test concepts of visual designs, yet in essence, its core belief boils down to virtually a superstition. A notion barely backed up by concrete evidence, yet often taken at face value, that somehow five seconds are just the right time to gauge first impressions. In this article, Eduard Kuric looks under the hood of how first impressions are affected by time, the test participant’s cognitive abilities and the visual complexity of the tested picture, and how UX researchers and product owners can ensure that the user’s first steps can get off on the right foot.

In today’s world of shortening attention spans and omnipresent hustle, wasting even a second could mean losing the chance to earn more time from a person you want to impress. If your interests lie in creating good user experiences, there is a fair chance you have heard of five-second testing.

Five-second testing is an established technique of usability research used by UX researchers, designers, product managers, and in a variety of other professions, such as marketing or business analysis.

In short, you show a picture of whatever you are designing (site, app, pair of socks) to a member of your intended audience for exactly five seconds. Then, you hide the picture and ask the participant a couple of questions. The goal is to learn whether the reaction — the participants’s first impression — is what you wanted to see. Did you get the main message across? Do people remember the company’s name? Sounds like an efficient way to test your product without needing to turn to full-on usability testing, right?

Note: The word “participant” in this article is used to refer to users involved in five-second testing or related usability research methods. The word “user” is used in more general contexts since users form first impressions all the time, not just when you are testing it.

Illustration with a timer set for 5 seconds and a question, ‘What product do you think this company sells?’
Image source: Michal Opalek. (Large preview)

Why is it five seconds exactly, though? Are five seconds some magical moment when everything we see should become clear? And if it does not, does it automatically mean that a user experience is bad? Or are five seconds just the right amount of time for first impressions to brew in the user’s mind so that they’re neither undercooked nor overcooked?

These are some of the questions that we asked ourselves. Not satisfied with the answers written by others who covered the topic before us, we kept drilling and conducted an actual peer-reviewed scientific case study, exploring the hidden truths behind the testing of first impressions. The research paper examines the five-second test and discusses the results.

So strap in and read what science has to say about five-second testing. And then, what the implications are for you so that you can take practical advantage of this new knowledge to develop better first impressions of your services or products. But first, let us delve into what we know about five-second testing and its caveats so that you see the greater picture of the focal points of our investigation.

The Mythos Of Five Seconds And Its Gaps #

You may be familiar with the well-known statistic that a website has about ten seconds to communicate its key message to the user. Knowing that waiting only five seconds to ask testing participants about their first impressions may suddenly seem like an odd choice. If indeed visitors of a website have about ten seconds to grasp a message, are five seconds really enough time for users? There is an alleged justification, as we explain below.

Cutting a bit forward, though, the factual basis for it is admittedly a bit of a Wild West if you look for hard data to support it. There is an almost uncanny resemblance to another not wholly scientific five-second rule that says it’s okay to eat food off the ground if it’s within five seconds from when it dropped there.

The five-second testing method has its origins as a simplification of usability testing. The first references to five-second testing point to Christine Perfetti, who coined the term for the method in the mid-2000s. The answer to “Why five seconds exactly?” comes largely from anecdotal evidence in the form of the experience of usability researchers.

The common story is that if something is shown to participants for more than five seconds, their first impressions will start to deviate from the actual user’s genuine initial impressions. The participant’s perspective becomes more analytical and less task-driven. The five-second test lets you avoid overtly speculative feedback that nobody would give you under normal circumstances.

Fair enough, that could potentially be true. But five seconds is still quite a short period of time. Consider how different people can be when it comes to their cognitive abilities (and there is nothing wrong with that). For example, one user’s sharp perception may let them realistically form first impressions in five seconds or faster, but another user may barely have the time to blink, much less absorb any meaningful information–they need a moment to take it in at their own tempo.

The reasoning starts to fall apart a bit more at its seams once you also consider the visual complexity of the stimulus (a.k.a., the picture you show to the participants). The nature of the things you may want to test can range from very simple to very complex. If the stimulus is simple, it is possible to take even less than five seconds for participants to form their initial impressions. Would this mean that there would be the risk of them using the remaining time to get over-exceedingly analytical?

Conversely, there is the question of whether five seconds is enough time to let participants realistically visually scan a more complex stimulus. I can already hear staunch proponents of five-second testing saying that this last discrepancy is actually rightfully intentional. It’s a feature, not a bug, if you will.

After all, if a stimulus is too complex, that is exactly why you conduct five-second testing. It allows us to find out about things like complexity. It can help you find out if participants cannot extract the key information you want to communicate so that you can fix it.

However, we need to consider that not all user interfaces are the landing pages of websites. They serve to support different user tasks, some of which cannot avoid having a certain degree of complexity.

Five-second testing guides typically avoid directly addressing testing of these types of user interfaces by saying that the method has the following limitation: it should not be used to test user interfaces with multiple purposes. If the same stimulus serves for more than one task, it is alleged that you should probably conduct full-fledged usability testing, which is technically correct (the best kind of correct).

Giving up on the five-second testing in the inherently more complex user interfaces, however, also gives up on its advantages for measuring and optimizing first impressions. For instance, the idea that a screenshot or a mockup is all you need to quickly find usability problems and iterate your designs. This is where five-second testing really shines.

Usability testing does not tell you accurately what the actual first impressions are without considerably interrupting the participant. And even then, you would encounter the same problem: At what moment from when the participant is exposed to a design should their first impressions be gauged so that they are genuine?

As we have discussed so far, there are certainly a fair number of question marks surrounding five-second testing. The method still undeniably has a number of merits, as proven by our experience at UXtweak, where we also provide our own Five Second Test tool. A lack of proper research on the topic is what drove us at UXtweak Research to conduct our very own case study.

The Science, Abridged #

Essentially, what we sought to investigate in our case study are the relations of a number of key factors that are absolutely crucial for five-second testing:

  • What are the cognitive abilities of the participant engaging in the five-second test?
  • How visually complex is the stimulus shown to the participant?
  • For how long is the stimulus shown to the participant?
  • What kinds of questions do we ask the participants afterward?
  • What is the feedback that participants give you?

As you may have noticed, time — that iconic yet controversial five-second threshold — is considered a variable factor. In our experiment, we investigate the differences in feedback between three separate groups of participants who are shown pictures for either five (5) or alternatively two (2) or (10) seconds (so a bit less and a bit more time, respectively). This means that it would not be correct to refer to it as just a five-second test anymore, but rather an N-second test (or a first impression test, if you do not wish to be too pedantic about the number of seconds).

Each participant first passes not just one but two cognitive ability tests. Human minds are multifaceted, and there is not just a single “cognitive ability” metric that would encompass everything that the mind can do. Among standard tests used by psychologists, we picked two that are linked to abilities that can be found as the most relevant to the formation and testing of first impressions:

  • Perceptual speed: How quickly you pick up visual information.
  • Working memory: How much information you can mentally process at the same time.

Working memory is the appropriate memory ability to focus on since it operates with information that receives the user’s attention. This distinguishes it from sensory memory (the memory processing information that our senses pick up) and long-term memory, where information is stored persistently for later use.

Illustration of how working memory governs the capacity of information that receives attention
Working memory governs the capacity of information that receives attention. (Large preview)

For the first impression test itself, six website screenshots were used as the stimuli. These screenshots were selected for possessing a broad range of visual complexity, from the simplest with just a few visual elements to the most complex with a number of distinct sections that serve different purposes.

Screenshots of real websites local to Czechia and Slovakia were translated into English, and their logos were replaced with fictional brand names so that, for all intents and purposes, the website screenshots would be authentic yet also unfamiliar to the participants who were recruited in the UK.

Website screenshots of varied visual complexity employed during the experiment
Website screenshots of varied visual complexity which employed during the experiment. (Large preview)

Finally, participants were asked to provide feedback by answering practically a complete portfolio of the various types of questions that can be typically asked during a first impression test. Each type of question tests a different aspect of the first impressions that the participants have formed inside their heads:

  • Attitudinal questions: Rating a perceived quality of the website (e.g., ugly vs. attractive) on a scale from 1 to 7.
  • Target identification questions: Questions directed at specific elements or aspects of the stimulus.
  • Memory dump questions: Asking participants to describe everything that they remember about what they saw.

The resulting answers were analyzed both quantitatively (with statistics) and qualitatively (by inspecting the contents of the received answers on an individual level). With it, a number of conclusions can be reached, some expected and some rather surprising.

Now that you have a picture of what our case study was about let’s dive into the actual, interesting implications for developing the first impressions of your product.

Note: If you would like to immerse yourself in further details of how our case study was conducted, you can learn more in our scientific paper.

Takeaways #

Statistically, all the variables we experimented with — the time duration of showing pictures to participants, the participants’ innate cognitive abilities, and the visual complexity of pictures — had a significant effect on the first impression answers.

For instance, between the groups that were shown screenshots of websites for two, five, and ten seconds, the number of answers that incorrectly identified what the websites were for dropped as time progressed. Notable is the difference between five and ten seconds. If the participants were really focusing on inconsequential details after five seconds, there should not be differences in recognition of such a key aspect as the website’s entire purpose.

A graph showing  how the ability to correctly identify the purpose of a webpage gradually rises within the first ten seconds, starting from two seconds
Ability to correctly identify the purpose of a webpage gradually rises within the first ten seconds. (Large preview)

Statistical differences lay the grounds for further observations on how changing the conditions of a test can (or cannot) affect its results:

  • Attitudes crystallize faster than in five seconds.
    In attitudinal questions where participants are asked to rate how they view the picture’s various qualities (e.g., from clear to confusing, from captivating to dull), answers stay relatively consistent, regardless of how much time the participant has or how good their cognitive abilities are. If you are laser-focused on assessing participants’ attitudes about your product and nothing else, you could present pictures for two seconds, or possibly even less, as research done by others on a related topic also implies.
  • Logos are recognized earlier than in five seconds (with one exception).
    The target identification questions where participants are asked to recall the company name from the logo are, on the whole, impacted by time very little. This is to be expected: when viewing a website, our eyes are usually drawn to the top left corner to find out where we have found ourselves. There is an exception to this rule, however.
    Among participants with slower perceptual speed, significantly fewer identified the company name correctly at two seconds when compared to five seconds. This establishes five seconds as a more inclusive choice for timing your first impression test if you expect your target audience to have, on average, lower perceptual speed than the general populace and if the primary aim is to test contents of the header, such as logo design or company name identification. Otherwise, two seconds is a safe bet.
  • Irrelevant nitpicking? Yes, if visual complexity is low.
    In some cases, the popular narrative about five seconds being a good viewing time for testing first impressions is indeed true. Particularly for the simplest website screenshots, once five seconds have elapsed, participants start paying attention to minute details (e.g., the girl’s shirt color in the hero image).
    Curiously, though, having more time does not mean that participants would write longer or more complex answers. Instead, when participants have ten seconds to view the screenshots, the higher visual complexity of the screenshots is reflected in better-quality answers. Participants stay more on-topic–describing how the site is visually structured or justifying their criticisms of the page’s design. Different viewing times may be optimal in different situations. Especially since…
  • Low working memory warrants longer viewing time.
    When asked to reiterate what they saw in their own words, participants with low and high working memory provided significantly different answers. With low working memory, answers become shorter, less complex, and recall fewer concepts overall. However, when the viewing time is extended to ten seconds, these differences disappear. This implies that the same information is being processed — memory capacity just dictates how fast it can happen.
    Without knowing where each participant’s memory ability stands, it is difficult to tell what they would actually recall if we left them to work at their own pace. Consequently, assessment of working memory before testing first impressions (and adjusting viewing time accordingly) should be considered a good practice.
  • For cognitive powerhouses, five seconds are enough.
    A less practical point maybe, but if you are developing an app for people with reasonably high perceptual speed and working memory — be it the mentally gifted, hyperproductive hustle enthusiasts, or caffeine addicts — you could likely show them your screenshots for just two seconds and get similar results as in a five-second test.
  • Give participants the proper amount of time to form a first impression.
    When the visual stimulus is more visually complex in a first impression test, the task of mentally processing it becomes more difficult and time-consuming (just like in any normal scenario). This manifests in test results. Fewer people correctly identify the purpose of a more visually complex website, and they recall fewer elements and aspects of the website.
    This could be seen as a bit of a paradox since more complex stimuli mean there is actually more content that participants could potentially remember and comment on, but only if they had the time to absorb the information properly. Data shows that when participants are given ten seconds, the answers do actually normalize, becoming more similar to stimuli of lower visual complexity.

If the purpose of the particular first impression test is not to remove all visual complexity at any cost outright, we would suggest adjusting the viewing time to reflect the visual complexity of the stimulus.

Keep in mind there are still aspects of first impression testing that remain unknown. A reasonable question that you can ask now would be: “Okay, so how exactly do I time my first impression test?” While we can sum up our observations into a conceptual framework of how time can be treated in first-impression test planning, it is not an exact guideline; there may be other interpretations or exceptions.

Illustration showing how much time is needed for different kinds of questions. For example, attitudinal questions require less than five seconds, and target identification or memory dump requires an adjusted number of seconds
How to decide how many seconds to show pictures to participants in first impression testing? (Large preview)

Take this more as an eye-opener and a call to action. Indeed, in our study, ten seconds yielded more appropriate results for more complex websites than five seconds did. But there is nothing to say that for other websites, the best timing could not be fifteen or even twenty seconds. Even more so, once you also factor in the influence of the cognitive ability of each individual participant.

The key takeaway? When you gauge your audience’s first impressions about something, take a more holistic approach.

Consider your goals for your test. What kind of questions do you want to ask? Use some of the tools that are available to measure the visual complexity of the pictures that you want to present. Give your participants a short working memory test before you start bombarding them with pictures and questions.

Try to adjust the timing in your first impression test to match the situation. To give an analogy, by blindly following a different five-second rule and eating off the floor, you could end up getting sick. Be just as cautious about relying on myths in your usability research methods. This is not to discount five seconds. As we show, it is still good timing for first impression tests in plenty of cases, but it is not the be-all and end-all as far as first impression testing goes. By broadening your perspective, you can do even better.

Resources #

Friday, January 19, 2024

CSS Scroll Snapping Aligned With Global Page Layout: A Full-Width Slider Case Study

 Have you run into a situation where you need the padding of one element to align with the padding of another element? It’s a common debacle, especially when a page layout is set with global padding, and you need to break out of that layout and go full-width but still align the inner content with the rest of the page. In this article, Brecht De Ruyte demonstrates the issue with a full-width slider component that breaks out of the main page container and shares a couple of techniques to keep it visually aligned with other elements on the page.

You know what’s perhaps the “cheapest” way to make a slider of images, right? You set up a container, drop a bunch of inline image elements in it, then set overflow-x: auto on it, allowing us to swipe through them. The same idea applies nicely to a group of cards, too.

See the Pen Cheap Slider [forked] by Geoff Graham.

While that’s a quick and dirty way to get a slider up and running, there is more we can do to smooth it out a bit. That’s what we’re going to cover in this tutorial. Specifically, we’re improving our cheap slider with controlled scrolling, courtesy of CSS Scroll Snapping. The idea is that the slider naturally stops on an image during the scroll. Otherwise, we could blow through a bunch of images in a single swipe and lose our place.

Sketch of the slider with controlled scrolling which stops on an image
(Large preview)

But we’ll go deeper than scroll snapping. The thing with sliders is that it can be difficult to instruct them on where to “snap.” For example, what if we want to configure the slider in such a way that images always snap at the left (or inline-start) edge when swiping right to left?

Sketch of the slider with the images snapped at the left edge
(Large preview)

But that’s not even the “tricky” part we’re looking at. Say we are working within an existing page layout where the main container of the page has a set amount of padding applied to it. In this case, the slider should always begin at the inline starting edge of the inside of the container, and when scrolling, each image should snap to the edge rather than scroll past it.

Simply drop the slider in the layout container, right? It’s not as straightforward as you might think. If you notice in the illustrations, the slider is outside the page’s main container because we need it to go full-width. We do that in order to allow the images to scroll fully edge-to-edge and overflow the main body.

Our challenge is to make sure the slider snaps into place consistent with the page layout’s spacing, indicated by the dashed blue lines in the drawings. The green area represents the page container’s padding, and we want images to snap right at the blue line.

The Basic Layout #

Let’s start with some baseline HTML that includes a header and footer, each with an inner .container element that’s used for the page’s layout. Our slider will sit in between the header and footer but lack the same inner .container that applies padding and width to it so that the images scroll the full width of the page.

<header>
  <div class="container">
    <!-- some contained header with some nav items -->
  </div>
</header>
<main>
  <section class="slider">
    <!-- our slider -->
  </section>
  <section class="body-text">
    <div class="container">
      <!-- some contained text -->
    </div>
  </section>
</main>
<footer>
  <div class="container">
    <!-- a contained footer -->
  </div>
</footer>

Creating The Container #

In contrast to the emphasis I’ve put on scroll snapping for this demo, the real power in creating the slider does not actually start with scroll snapping. The trick to create something like this starts with the layout .container elements inside the header and footer. We’ll set up a few CSS variables and configure the .container’s properties, such as its width and padding.

The following bit of CSS defines a set of variables that are used to control the maximum width and padding of a container element. The @media rules are used to apply different values to these properties depending on the viewport’s width.


:root {
  --c-max-width: 100%;
  --c-padding: 10px;
  
  @media screen and (min-width: 768px) {
    --c-max-width: 800px;
    --c-padding: 12px;
  }
  @media screen and (min-width: 1000px) {
    --c-max-width: 940px;
    --c-padding: 24px;
  }
  @media screen and (min-width: 1200px) {
    --c-max-width: 1200px;
    --c-padding: 40px;
  }
}

The first couple of lines of the :root element’s ruleset define two CSS custom properties: --c-max-width and --c-padding. These properties are used to control the layout .container’s maximum width and padding.

Next up, we have our @media rules. These apply different values to the --c-max-width and --c-padding properties depending on the screen size. For example, the first @media rule updates the value of --c-max-width from 100% to 800px, as well as the --c-padding from 10px to 12px when the screen width is at least 768px.

Those are the variables. We then set up the style rules for the container, which we’ve creatively named .container, and apply those variables to it. The .container’s maximum width and inline padding are assigned to the also creatively-named -c-max-width and --c-padding variables. This opens up our container’s variables at a root level so that they can easily be accessed by other elements when we need them.

I am using pixels in these examples because I want this tutorial to be about the actual technique instead of using different sizing units. Also, please note that I will be using CSS nesting for the demos, as it is supported in every major browser at the time I’m writing this.

The Scroll-Snapping #

Let’s work on the scroll-snapping part of this slider. The first thing we’re going to do is update the HTML with the images. Remember that this slider is outside of the .container (we’ll take care of that later).

<header>
  <!-- .container -->
</header

<section class="slider">
  <div>
    <img src="..." alt="">
  </div>
  <div>
    <img src="..." alt="">
  </div>
  <div>
    <img src="..." alt="">
  </div>
  <!-- etc. -->
</section>

<footer>
  <!-- .container -->
</footer>

Now we have a a group of divs that are direct children of the .slider. And those, in turn, each contain one image element. With this intact, it’s time for us to style this as an actual slider. Flexbox is an efficient way to change the display behavior of the .slider’s divs so that they flow in the inline direction rather than stacking vertically as they naturally would as block-level elements. Using Flexbox also gives us access to the gap property to space things out a bit.

.slider {
  display: flex;
  gap: 24px;
}

Now we can let the images overflow the .slider in the horizontal, or inline, direction:

.slider {
  display: flex;
  gap: 24px;
  overflow-x: auto;
}

Before we apply scroll snapping, we ought to configure the divs so that the images are equally sized. A slider is so much better to use when the images are visually consistent rather than having a mix of portrait and landscape orientations, creating a jagged flow. We can use the flex property on the child divs, which is shorthand for the flex-shrink, flex-grow, and flex-basis properties:

.slider {
  display: flex;
  gap: 24px;
  overflow-x: auto;

  > * {
    flex: 0 0 300px;
  }
}

This way, the divs are only as big as the content they contain and will not exceed a width of 300px. But! In order to contain the images in the space, we will set them to take up the full 100% width of the divs, slap an aspect-ratio on them to maintain proportions, then use the object-fit property to to cover the div’s dimensions.

.slider {
  display: flex;
  gap: 24px;
  overflow-x: auto;

  > * {
    flex: 0 0 300px;
  }

  & img {
    aspect-ratio: 3 / 4;
    object-fit: cover;
    width: 100%;
  }
}

With this in place, we can now turn to scroll snapping:

.slider {
  display: flex;
  gap: 24px;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  
  > * {
    flex: 0 0 300px;
    scroll-snap-align: start;
  }

  /*
}

Here’s what’s up:

  • We’re using the scroll-snap-type property on the .slider container to initialize scroll snapping in the horizizontal (x) direction. The mandatory keyword means we’re forcing the slider to snap on items in the container instead of allowing it to scroll at will and land wherever it wants.
  • We’re using the scroll-snap-align property on the divs to set the snapping on the item’s start-ing edge (or “right” edge in a typical horizontal left-to-right writing mode).

Good so far? Here’s what we’ve made up to this point:

See the Pen Cheap Slider, Scroll Snapped [forked] by Geoff Graham.

Calculating The Offset Size #

Now that we have all of our pieces in place, it’s time to create the exact snapping layout we want. We already know what the maximum width of the page’s layout .container is because we set it up to change at different breakpoints with the variables we registered at the beginning. In other words, the .container’s width will never exceed the value of --c-max-width. We also know the container always has a padding equal to the value of --c-padding.

Again, our slider is outside of the .container, and yet, we want the scroll-snapped images to align with those values for a balanced page layout. Let’s create a new CSS variable, but this time scoped to the .slider and set it up to calculate the space between the viewport and the inside of the .container element.

.slider {
  --offset-width: calc(((100% - (min(var(--c-max-width), 100%) + (var(--c-padding) * 2))) / 2) + (var(--c-padding) * 2)
  );
}

That is a lot of math! First, we’re calculating the minimum value of either the .container element’s max-width or 100%, whichever is smaller, then increasing this minimum value with padding on the .slider. This result is then subtracted from 100%. From this, we get the total amount of space that is available to offset either side of the .slider to align with the layout .container.

We then divide this number by 2 to get the offset width for each specific side. And finally, we add the .container’s inline padding to the offset width so that the .slider is offset from the inside edges of the container rather than the outside edges. In the demo, I have used the universal selector (*) and its pseudos to measure the box-sizing of all elements by the border-box so that we are working inside the .slider’s borders rather than outside of it.

*, *::before, *::after {
  box-sizing: border-box;
}

Some Minor Cleanup #

If you think that our code is becoming a bit too chaotic, we can certainly improve it a bit. When I run into these situations, I sometimes like to organize things into multiple custom properties just for easy reading. For example, we could combine the inline paddings that are scoped to the :root and update the slider’s --offset-width variable with a calc() function that’s a bit easier on the eyes.

:root {
  /* previous container custom properties */

   --c-padding-inline: calc(var(--c-padding) * 2);
}

.slider {
  --offset-width: calc(((100% - (min(var(--c-max-width), 100%) + var(--c-padding-inline))) / 2) + var(--c-padding-inline));

  /* etc. */
}

That’s a smidge better, right?

Aligning The Slider With The Page Layout #

We have a fully-functioning scroll scroll-snapping container at this point! The last thing for us to do is apply padding to it that aligns with the layout .container. As a reminder, the challenge is for us to respect the page layout’s padding even though the .slider is a full-width element outside of that container.

This means we need to apply our newly-created --offset-width variable to the .slider. We’ve already scoped the variable to the .slider, so all we really need is to apply it to the right properties. Here’s what that looks like:

.slider {
  --offset-width: calc(
    ((100% - (min(var(--c-max-width), 100%) + (var(--c-padding) * 2))) / 2) + (var(--c-padding) * 2)
  );

  padding-inline: var(--offset-width);
  scroll-padding-inline-start: var(--offset-width);
  
  /* etc. */
  }

The padding-inline and scroll-padding-inline-start properties are used to offset the slider from the left and right sides of its container and to ensure that the slider is always fully visible when the user scrolls.

  • padding-inline
    This sets spacing inside the .slider’s inline edges. A nice thing about using this logical property instead of a physical property is that we can apply the padding in both directions in one fell swoop, as there is no physical property shorthand that combines padding-left and padding-right. This way, the .slider’s internal inline spacing matches that of the .container in a single declaration.
  • scroll-padding-inline-start
    This sets the scroll padding at the start of the slider’s inline dimension. This scroll padding is equal to the amount of space that is added to the left (i.e., inline start) side of the .slider’s content during the scroll.

Now that the padding-inline and scroll-padding-inline-start properties are both set to the value of the --offset-width variable, we can ensure that the slider is perfectly aligned with the start of our container and snaps with the start of that container when the user scrolls.

We could take all of this a step further by setting the gap of our slider items to be the same as our padding gap. We’re really creating a flexible system here:

.slider {
  --gap: var(--c-padding);
  gap: var(--gap);
}

Personally, I would scope this into a new custom property of the slider itself, but it’s more of a personal preference. The full demo can be found on CodePen. I added a toggle in the demo so you can easily track the maximum width and paddings while resizing.

See the Pen Full width scroll snap that snaps to the container [forked] by utilitybend.

But we don’t have to stop here! We can do all sorts of calculations with our custom properties. Maybe instead of adding a fixed width to the .slider’s flex children, we want to always display three images at a time inside of the container:

.slider {
  --gap: var(--c-padding);
  --flex-width: calc((100% - var(--gap) * 2) / 3);
  
  /* Previous scroll snap code */
  
  > * {
    flex: 0 0 var(--flex-width);
    scroll-snap-align: start;
  }
}

That --flex-width custom property takes 100% of the container the slider is in and subtracts it by two times the --gap. And, because we want three items in view at a time, we divide that result by 3.

See the Pen Updated scroll container with 3 items fitted in container [forked] by utilitybend.

Why Techniques Like This Are Important #

The best thing about using custom properties to handle calculations is that they are lighter and more performant than attempting to handle them in JavaScript. It takes some getting used to, but I believe that we should use these kinds of calculations a lot more often. Performance is such an important feature. Even seemingly minor optimizations like this can add up and really make a difference to the overall end-user experience.

And, as we’ve seen, we can plug in variables from other elements into the equation and use them to conform an element to the properties of another element. That’s exactly what we did to conform the .slider’s inner padding to the padding of a .container that is completely independent of the slider. That’s the power of CSS variables — reusability and modularity that can improve how elements interact within and outside other elements.