Come to the light-dark() Side

October 25th, 2024 No comments

You’d be forgiven for thinking coding up both a dark and a light mode at once is a lot of work. You have to remember @media queries based on prefers-color-scheme as well as extra complications that arise when letting visitors choose whether they want light or dark mode separately from the OS setting. And let’s not forget the color palette itself! Switching from a “light” mode to a “dark” mode may involve new variations to get the right amount of contrast for an accessible experience.

It is indeed a lot of work. But I’m here to tell you it’s now a lot simpler with modern CSS!

Default HTML color scheme(s)

We all know the “naked” HTML theme even if we rarely see it as we’ve already applied a CSS reset or our favorite boilerplate CSS before we even open localhost. But here’s a news flash: HTML doesn’t only have the standard black-on-white theme, there is also a native white-on-black version.

We have two color schemes available to use right out of the box!

If you want to create a dark mode interface, this is a great base to work with and saves you from having to account for annoying details, like dark inputs, buttons, and other interactive elements.

Screenshot of two forms, one with elements and background on light mode, the other all in dark mode.
Live Demo on CodePen

Switching color schemes automatically based on OS preference

Without any @media queries — or any other CSS at all — if all we did was declare color-scheme: light dark on the root element, the page will apply either the light or dark color scheme automatically by looking at the visitor’s operating system (OS) preferences. Most OSes have a built-in accessibility setting for your preferred color scheme — “light”, “dark”, or even “auto” — and browsers respect that setting.

html {
  color-scheme: light dark;
}

We can even accomplish this without CSS directly in the HTML document in a tag:

<meta name="color-scheme" content="light dark">

Whether you go with CSS or the HTML route, it doesn’t matter — they both work the same way: telling the browser to make both light and dark schemes available and apply the one that matches the visitor’s preferences. We don’t even need to litter our styles with prefers-color-scheme instances simply to swap colors because the logic is built right in!

You can apply light or dark values to the color-scheme property. At the same time, I’d say that setting color-scheme: light is redundant, as this is the default color scheme with or without declaring it.

You can, of course, control the tag or the CSS property with JavaScript.

There’s also the possibility of applying the color-scheme property on specific elements instead of the entire page in one fell swoop. Then again, that means you are required to explicitly declare an element’s color and background-color properties; otherwise the element is transparent and inherits its text color from its parent element.

What values should you give it? Try:

Default text and background color variables

The “black” colors of these native themes aren’t always completely black but are often off-black, making the contrast a little easier on the eyes. It’s worth noting, too, that there’s variation in the blackness of “black” between browsers.

What is very useful is that this default not-pure-black and maybe-not-pure-white background-color and text color are available as variables. They also flip their color values automatically with color-scheme!

They are: Canvas and CanvasText.

These two variables can be used anywhere in your CSS to call up the current default background color (Canvas) or text color (CanvasText) based on the current color scheme. If you’re familiar with the currentColor value in CSS, it seems to function similarly. CanvasText, meanwhile, remains the default text color in that it can’t be changed the way currentColor changes when you assign something to color.

In the following examples, the only change is the color-scheme property:

Screenshot of code and output area with color-scheme set to light, a large div of background color Canvas with text within set to color CanvasText, and a div within that with the Canvas and CanvasText switched.
Screenshot of code and output area with color-scheme set to dark, the rest of the code is all the same, and the light and dark areas have switched.

Not bad! There are many, many more of these system variables. They are case-insensitive, often written in camelCase or PascalCase for readability. MDN lists 19 variables and I’m dropping them in below for reference.

Open to view 19 system color names and descriptions
  • AccentColor: The background color for accented user interface controls
  • AccentColorText: The text color for accented user interface controls
  • ActiveText: The text color of active links
  • ButtonBorder: The base border color for controls
  • ButtonFace: The background color for controls
  • ButtonText: The text color for controls
  • Canvas: The background color of an application’s content or documents
  • CanvasText: The text color used in an application’s content or documents
  • Field: The background color for input fields
  • FieldText: The text color inside form input fields
  • GrayText: The text color for disabled items (e.g., a disabled control)
  • Highlight: The background color for selected items
  • HighlightText: The text color for selected items
  • LinkText: The text color used for non-active, non-visited links
  • Mark: The background color for text marked up in a element
  • MarkText: The text color for text marked up in a element
  • SelectedItem: The background color for selected items (e.g., a selected checkbox)
  • SelectedItemText: The text color for selected items
  • VisitedText: The text visited links

Cool, right? There are many of them! There are, unfortunately, also discrepancies as far as how these color keywords are used and rendered between different OSes and browsers. Even though “evergreen” browsers arguably support all of them, they don’t all actually match what they’re supposed to, and fail to flip with the CSS color-scheme property as they should.

Egor Kloos (also known as dutchcelt) is keeping an eye on the current status of system colors, including which ones exist and the browsers that support them, something he does as part of a classless CSS framework cleverly called system.css.

CodePen Embed Fallback

Declaring colors for both modes together

OK good, so now you have a page that auto-magically flips dark and light colors according to system preferences. Whether you choose to use these system colors or not is up to you. I just like to point out that “dark” doesn’t always have to mean pure “black” just as “light” doesn’t have to mean pure “white.” There are lots more colors to pair together!

But what’s the best or simplest way to declare colors so they work in both light and dark mode?

In my subjective reverse-best order:

Third place: Declare color opacity

You could keep all the same background colors in dark and light modes, but declare them with an opacity (i.e. rgb(128 0 0 / 0.5) or #80000080). Then they’ll have the Canvas color shine through.

It’s unusable in this way for text colors, and you may end up with somewhat muted colors. But it is a nice easy way to get some theming done fast. I did this for the code blocks on this old light and dark mode demo.

Screenshot of a website split into its dark and light modes, showing code blocks with gentle background colors split across both

Second place: Use color-mix()

Like this:

color-mix(in oklab, Canvas 75%, RebeccaPurple);

Similar (but also different) to using opacity to mute a color is mixing colors in CSS. We can even mix the system color variables! For example, one of the colors can be either Canvas or CanvasText so that the background color always mixes with Canvas and the text color always mixes with CanvasText.

We now have the CSS color-mix() function to help us with this. The first argument in the function defines the color space where the color mixing happens. For example, we can tell the function that we are working in the OKLAB color space, which is a rectangular color space like sRGB making it ideal to mix with sRGB color values for predictable results. You can certainly mix colors from different color spaces — the OKLAB/sRGB combination happens to work for me in this instance.

The second and third arguments are the colors you want to mix, and in what proportion. Proportions are optional but expressed in percentages. Without declaring a proportion, the mix is an even 50%-50% split. If you add percentages for both colors and they don’t match up to 100%, it does a little math for you to prevent breakages.

The color-mix() approach is useful if you’re happy to keep the same hues and color saturations regardless of whether the mode is light or dark.

A screenshot of whimsica11y.net, where the color-mix() method for making the theme is in use

In this example, as you change the value of the hue slider, you’ll see color changes in the themed boxes, following the theme color but mixed with Canvas and CanvasText:

CodePen Embed Fallback

You may have noticed that I used OKLCH and HSL color spaces in that last example. You may also have noticed that the HSL-based theme color and the themed paragraph were a lot more “flashy” as you moved the hue slider.

I’ve declared colors using a polar color space, like HSL, for years, loving that you can easily take a hue and go up or down the saturation and lightness scales based on need. But, I concede that it’s problematic if you’re working with multiple hues while trying to achieve consistent perceived lightness and saturation across them all. It can be difficult to provide ample contrast across a spectrum of colors with HSL.

The OKLCH color space is also polar just like HSL, with the same benefits. You can pick your hue and use the chroma value (which is a bit like saturation in HSL) and the lightness scales accurately in the same way. Both OKLCH and OKLAB are designed to better match what our eyes perceive in terms of brightness and color compared to transitioning between colors in the sRGB space.

While these color spaces may not explicitly answer the age-old question, Is my blue the same as your blue? the colors are much more consistent and require less finicking when you decide to base your whole website’s palette on a different theme color. With these color spaces, the contrasts between the computed colors remain much the same.

First place (winner!): Use light-dark()

Like this:

light-dark(lavender, saddlebrown);

With the previous color-mix() example, if you choose a pale lavender in light mode, its dark mode counterpart is very dark lavender.

The light-dark() function, conversely, provides complete control. You might want that element to be pale lavender in light mode and a deep burnt sienna brown in dark mode. Why not? You can still use color-mix() within light-dark() if you like — declare the colors however you like, and gain much more fine-grained control over your colors.

Feel free to experiment in the following editable demo:

CodePen Embed Fallback

Using color-scheme: light dark; — or the corresponding meta tag in HTML on your page —is a prerequisite for the light-dark() function because it allows the function to respect a person’s system preference, or whichever single light or dark value you have set on color-scheme.

Another consideration is that light-dark() is newly available across browsers, with just over 80% coverage across all users at the time I’m writing this. So, you might consider including a fallback in your CSS for browsers that lack support for the function.

What makes using color-scheme and light-dark() better than using @media queries?

@media queries have been excellent tools, but using them to query prefers-color-scheme only ever follows the preference set within the person’s operating system. This is fine until you (rightfully) want to offer the visitor more choices, decoupled from whether they prefer the UI on their device to be dark or light.

We’re already capable of doing that, of course. We’ve become used to a lot of jiggery-pokery with extra CSS classes, using duplicated styles, or employing custom properties to make it happen.

The joy of using color-scheme is threefold:

  • It gives you the basic monochrome dark mode for free!
  • It can natively do the mode switching based on OS mode preference.
  • You can use JavaScript to toggle between light and dark mode, and the colors declared in the light-dark() functions will follow it.

Light, dark, and auto mode controls

Essentially, all we are doing is setting one of three options for whether the color-scheme is light, dark, or updates auto-matically.

I advise offering all three as discrete options, as it removes some complications for you! Any new visitor to the site will likely be in auto mode because accepting the visitor’s OS setting is the least jarring default state. You then give that person the choice to stay with that or swap it out for a different color scheme. This way, there’s no need to sniff out what mode someone prefers to, for example, display the correct icon on a toggle and make it perform the correct action. There is also no need to keep an event listener on prefers-color-scheme in case of changes — your color-scheme: light dark declaration in CSS handles that for you.

Three examples of mode switches, each with the three options of Auto, Light and Dark. Buttons, a fieldset with radio buttons, and a select element.

Adjusting color-scheme in pure CSS

Yes, this is totally possible! But the approach comes with a few caveats:

  • You can’t use
  • It only works on a per page basis, not per website, which means changes are lost on reload or refresh.
  • The browser needs to support the :has() pseudo-selector. Most modern browsers do, but some folks using older devices might miss out on the experience.

Using the :has() pseudo-selector

This approach is almost alarmingly simple and is fantastic for a simple one-pager! Most of the heavy lifting is done with this:

/* default, or 'auto' */
html {
  color-scheme: light dark;
}

html:has([value="light"]:checked {
  color-scheme: light;
}

html:has([value="dark"]:checked {
  color-scheme: dark;
}

The second and third rulesets above look for an attribute called value on any element that has “light” or “dark” assigned to it, then change the color-scheme to match only if that element is :checked.

This approach is not very efficient if you have a huge page full of elements. In those cases, it’s better to be more specific. In the following two examples, the CSS selectors check for value only within an element containing id="mode-switcher".

html:has(#mode-switcher [value="light"]:checked) { color-scheme: light }
/* Did you know you don't need the ";" for a one-liner? Now you do! */

Using a element:

CodePen Embed Fallback

Using :

CodePen Embed Fallback

We could theoretically use checkboxes for this, but since checkboxes are not supposed to be used for mutually exclusive options, I won’t provide an example here. What happens in the case of more than one option being checked? The last matching CSS declaration wins (which is dark in the examples above).

Adjusting color-scheme in HTML with JavaScript

I subscribe to Jeremy Keith’s maxim when it comes to reaching for JavaScript:

JavaScript should only do what only JavaScript can do.

This is exactly that kind of situation.

If you want to allow visitors to change the color scheme using buttons, or you would like the option to be saved the next time the visitor comes to the site, then we do need at least some JavaScript. Rather than using the :has() pseudo-selector in CSS, we have a few alternative approaches for changing the color-scheme when we add JavaScript to the mix.

Using tags

If you have set your color-scheme within a meta tag in the of your HTML:

<meta name="color-scheme" content="light dark">

…you might start by making a useful constant like so:

const colorScheme = document.querySelector('meta[name="color-scheme"]');

And then you can manipulate that, assigning it light or dark as you see fit:

colorScheme.setAttribute("content", "light"); // to light mode
colorScheme.setAttribute("content", "dark"); // to dark mode
colorScheme.setAttribute("content", "light dark"); // to auto mode

This is a very similar approach to using tags but is different if you are setting the color-scheme property in CSS:

html { color-scheme: light dark; }

Instead of setting a colorScheme constant as we just did in the last example with the tag, you might select the element instead:

const html = document.querySelector('html');

Now your manipulations look like this:

html.style.setProperty("color-scheme", "light"); // to light mode
html.style.setProperty("color-scheme", "dark"); // to dark mode
html.style.setProperty("color-scheme", "light dark"); // to auto mode

I like to turn those manipulations into functions so that I can reuse them:

function switchAuto() {
  html.style.setProperty("color-scheme", "light dark");
}
function switchLight() {
  html.style.setProperty("color-scheme", "light");
}
function switchDark() {
  html.style.setProperty("color-scheme", "dark");
}

Alternatively, you might like to stay as DRY as possible and do something like this:

function switchMode(mode) {
  html.style.setProperty("color-scheme", mode === "auto" ? "light dark" : mode);
}

The following demo shows how this JavaScript-based approach can be used with buttons, radio buttons, and a element. Please note that not all of the controls are hooked up to update the UI — the demo would end up too complicated since there’s no world where all three types of controls would be used in the same UI!

CodePen Embed Fallback

I opted to use onchange and onclick in the HTML elements mainly because I find them readable and neat. There’s nothing wrong with instead attaching a change event listener to your controls, especially if you need to trigger other actions when the options change. Using onclick on a button doesn’t only work for clicks, the button is still keyboard-focusable and can be triggered with Spacebar and Enter too, as usual.

Remembering the selection for repeat visits

The biggest caveat to everything we’ve covered so far is that this only works once. In other words, once the visitor has left the site, we’re doing nothing to remember their color scheme preference. It would be a better user experience to store that preference and respect it anytime the visitor returns.

The Web Storage API is our go-to for this. And there are two available ways for us to store someone’s color scheme preference for future visits.

localStorage

Local storage saves values directly on the visitor’s device. This makes it a nice way to keep things off your server, as the stored data never expires, allowing us to call it anytime. That said, we’re prone to losing that data whenever the visitor clears cookies and cache and they’ll have to make a new selection that is freshly stored in localStorage.

You pick a key name and give it a value with .setItem():

localStorage.setItem("mode", "dark");

The key and value are saved by the browser, and can be called up again for future visits:

const mode = localStorage.getItem("mode");

You can then use the value stored in this key to apply the person’s preferred color scheme.

sessionStorage

Session storage is thrown away as soon as a visitor browses away to another site or closes the current window/tab. However, the data we capture in sessionStorage persists while the visitor navigates between pages or views on the same domain.

It looks a lot like localStorage:

sessionStorage.setItem("mode", "dark");
const mode = sessionStorage.getItem("mode");

Which storage method should I use?

Personally, I started with sessionStorage because I wanted my site to be as simple as possible, and to avoid anything that would trigger the need for a GDPR-compliant cookie banner if we were holding onto the person’s preference after their session ends. If most of your traffic comes from new visitors, then I suggest using sessionStorage to prevent having to do extra work on the GDPR side of things.

That said, if your traffic is mostly made up of people who return to the site again and again, then localStorage is likely a better approach. The convenience benefits your visitors, making it worth the GDPR work.

The following example shows the localStorage approach. Open it up in a new window or tab, pick a theme other than what’s set in your operating system’s preferences, close the window or tab, then re-open the demo in a new window or tab. Does the demo respect the color scheme you selected? It should!

CodePen Embed Fallback

Choose the “Auto” option to go back to normal.

If you want to look more closely at what is going on, you can open up the developer tools in your browser (F12 for Windows, CTRL+ click and select “Inspect” for macOS). From there, go into the “Application” tab and locate https://cdpn.io in the list of items stored in localStorage. You should see the saved key (mode) and the value (dark or light). Then start clicking on the color scheme options again and watch the mode update in real-time.

Screenshot of the top of Edge devtools, with Application tab open. The key “mode” and value “dark” saved in cdpn.io’s local storage is shown.

Accessibility

Congratulations! If you have got this far, you are considering or already providing versions of your website that are more comfortable for different people to use.

For example:

  • People with strong floaters in their eyes may prefer to use dark mode.
  • People with astigmatism may be able to focus more easily in light mode.

So, providing both versions leaves fewer people straining their eyes to access the content.

Contrast levels

I want to include a small addendum to this provision of a light and dark mode. An easy temptation is to go full monochrome black-on-white or white-on-black. It’s striking and punchy! I get it. But that’s just it — striking and punchy can also trigger migraines for some people who do a lot better with lower contrasts.

Providing high contrast is great for the people who need it. Some visual impairments do make it impossible to focus and get a sharp image, and a high contrast level can help people to better make out the word shapes through a blur. Minimum contrast levels are important and should be exceeded.

Thankfully, alongside other media queries, we can also query prefers-contrast which accepts values for no-preference, more, less, or custom.

In the following example (which uses :has() and color-mix()), a element is displayed to offer contrast settings. When “Low” is selected, a filter of contrast(75%) is placed across the page. When “High” is selected, CanvasText and Canvas are used unmixed for text color and background color:

CodePen Embed Fallback

Adding a quick high and low contrast theme gives your visitors even more choice for their reading comfort. Look at that — now you have three contrast levels in both dark and light modes — six color schemes to choose from!

ARIA-pressed

ARIA stands for Accessible Rich Internet Applications and is designed for adding a bit of extra info where needed to screen readers and other assistive tech.

The words “where needed” do heavy lifting here. It has been said that, like apostrophes, no ARIA is better than bad ARIA. So, best practice is to avoid putting it everywhere. For the most part (with only a few exceptions) native HTML elements are good to go out of the box, especially if you put useful text in your buttons!

The little bit of ARIA I use in this demo is for adding the aria-pressed attribute to the buttons, as unlike a radio group or select element, it’s otherwise unclear to anyone which button is the “active” one, and ARIA helps nicely with this use case. Now a screen reader will announce both its accessible name and whether it is in a pressed or unpressed state along with a button.

Following is an example code snippet with all the ARIA code bolded — yes, suddenly there’s lots more! You may find more elegant (or DRY-er) ways to do this, but showing it this way first makes it more clear to demonstrate what’s happening.

Our buttons have ids, which we have used to target them with some more handy consts at the top. Each time we switch mode, we make the button’s aria-pressed value for the selected mode true, and the other two false:

const html = document.querySelector("html");
const mode = localStorage.getItem("mode");
const lightSwitch = document.querySelector('#lightSwitch');
const darkSwitch = document.querySelector('#darkSwitch');
const autoSwitch = document.querySelector('#autoSwitch');

if (mode === "light") switchLight();
if (mode === "dark") switchDark();

function switchAuto() {
  html.style.setProperty("color-scheme", "light dark");
  localStorage.removeItem("mode");
  lightSwitch.setAttribute("aria-pressed","false");
  darkSwitch.setAttribute("aria-pressed","false");
  autoSwitch.setAttribute("aria-pressed","true");
}

function switchLight() {
  html.style.setProperty("color-scheme", "light");
  localStorage.setItem("mode", "light");
  lightSwitch.setAttribute("aria-pressed","true");
  darkSwitch.setAttribute("aria-pressed","false");
  autoSwitch.setAttribute("aria-pressed","false");
}

function switchDark() {
  html.style.setProperty("color-scheme", "dark");
  localStorage.setItem("mode", "dark");
  lightSwitch.setAttribute("aria-pressed","false");
  darkSwitch.setAttribute("aria-pressed","true");
  autoSwitch.setAttribute("aria-pressed","false");
}

On load, the buttons have a default setting, which is when the “Auto” mode button is active. Should there be any other mode in the localStorage, we pick it up immediately and run either switchLight() or switchDark(), both of which contain the aria-pressed changes relevant to that mode.

<button id="autoSwitch" aria-pressed="true" type="button" onclick="switchAuto()">Auto</button>
<button id="lightSwitch" aria-pressed="false" type="button" onclick="switchLight()">Light</button>
<button id="darkSwitch" aria-pressed="false" type="button" onclick="switchDark()">Dark</button>

The last benefit of aria-pressed is that we can also target it for styling purposes:

button[aria-pressed="true"] {
  background-color: transparent;
  border-width: 2px;
}

Finally, we have a nice little button switcher, with its state clearly shown and announced, that remembers your choice when you come back to it. Done!

CodePen Embed Fallback

Outroduction

Or whatever the opposite of an introduction is…

…don’t let yourself get dragged into the old dark vs light mode argument. Both are good. Both are great! And both modes are now easy to create at once. At the start of your next project, work or hobby, do not give in to fear and pick a side — give both a try, and give in to choice.

Darth Vader clenching his fist, saying “If you only knew the power of the Dark Side.”

Come to the light-dark() Side originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

Categories: Designing, Others Tags:

The Impact of Generative AI on Content Creation

October 25th, 2024 No comments

Thanks to generative AI, content creation has become one of the most important trends in today’s world, with a completely new approach. It is revolutionizing the creative field in many different ways, from advertising to marketing to automated newspapers and magazines. Users of this technology must note its opportunities and problems. This blog discusses generative AI in content generation. It focuses on its potential to boost efficiency, creativity, and enrichment. It also examines moral issues.

Getting to Know About Generative AI

AI has existing categories. One is ‘generative AI’. It refers to systems that can generate text, images, and videos in response to an input. The public release of ChatGPT and other models in late 2022 made them very popular. Indeed, we are living in the “Year of Generative AI.” 

The global generative AI market is now valued at $45 billion in 2023, up from $29 billion in 2022. Two factors caused the rise in content creation. First, the enterprise content market has matured. Second, advances in machine learning and NLP now enable content automation. As more firms use generative AI tools, their effects grow across industries. This toolkit changes how Internet content is created and shared.

Source

Features of Generative AI

The table below shows the features of generative AI:

Feature Description
Automated Content Generation AI tools can now instantly and efficiently create articles, blogs, and marketing materials.
Personalization Generative AI can customize the generated content according to the preferences of the individual user by studying past interactions and data.
Idea Brainstorming AI will be able to work with content creators who can create novel ideas and outlines, thus being a creative assistant when brainstorming.
Multilingual Capabilities AI can translate content into several different languages, increasing the audience and reach across borders and languages.
SEO Optimization Generative AI tools allow optimization of content for search by auto-completing keywords and improving readable flow.
Quality Control AI will proofread as well as check grammar to ensure that there are no errors of typos or types in the generated content.
Rapid Prototyping AI lets you do quick iterations on content drafts, hence permitting faster feedback and adjustments in the creative process.
Data-Informed Observations Generative AI processes big data to derive insights which could be used for the content strategy, as well as marketing campaigns.

Generative AI’s Benefits for Content Creation

Ripple effects are coming for content creation. It seeks to boost productivity, cut costs, and spark creativity. It also aims to improve content marketing with generative AI. Here is a thorough examination of these advantages:

Speed and Effectiveness

  • Quick Content Creation: Another factor is that generative AI content writing can occur so much faster than by a human writer. Through such AI systems, you get product descriptions, social media posts, and articles within a few minutes.
  • Applications in Industry:

    • Journalism: Since AI provides quick news updates, news organizations help their journalists focus on investigative journalism.
    • Marketing: Organizations can create a range of marketing materials at once with the help of top artificial intelligence companies. Thereby, ensuring that the available information constantly engages the audience.
  • Scalability: Content is vital for large businesses, as seen in a Starbucks China case study. Hiring is a significant process that can take a lot of time and money. At the same time, generative AI can help businesses scale content production.

Cost-effectiveness

  • Lower Labor Costs: Freelancers are usually very expensive. Their writing and design skills are in demand. So, it is cheap to produce lots of content. Generative AI automates much of the process. Most of these tools might not cut labor costs. But, once implemented, they could generate content at fixed intervals.
  • Increased ROI: Generative AI reduces production costs and boosts output. So, users often see a big rise in ROI. This can especially benefit startups or small enterprises that work with limited capital.

Increased Originality

  • Brainstorming Support: Generative AI will be valid for content creators who need preliminary drafts and ideas. Based on the information and patterns of the present moment, it provides recommendations that encourage the authors to develop unique concepts.
  • Collaborative Tools: In other words, tools like ChatGPT enrich creativity. They do this when humans and AIs collaborate. The programs may help authors improve the flow of their work. They show multiple options or paths in the piece. Most AI Developers focus on creating innovative applications in content development.

Customization and Targeting

  • Customized Material Production: It creates content that is relevant for specific target audiences. AI can analyze users’ data. It can then design a better way to deliver messages to diverse groups. This would improve communication with them.
  • Personalized Campaign Examples:

    • Netflix: Regarding the efficiency of the proposed concept, algorithms that suggest films and TV series based on each user’s preferences enhance users’ engagement.
    • Email marketing: For open and conversion rates, businesses use generative AI to craft email marketing messages tailored to the individual recipient’s interest.
Source

Generative AI and Content Creation in The Future

Influence and Impacts of Big Data on Future Trends

  • Hyper-Personalization: The application of generative AI to marketers will make it easier for them to deliver content that meets the specific user’s needs rather than providing a one-size-fits-all alternative.
  • Large-Scale Automation: Acts or events such as news writing are already underway, with organizations like Google automating the process and thereby enabling human Journalists to focus on features such as investigations and detailed reports.
  • Immersion Content Creation: Technological advancements in AI will provide capabilities for dynamic and interactive audio-video presentations of media and games, enhancing media and interactive game narratives.

Possible technological developments

  • Improved Natural Language Processing: Subsequent generative AI renditions should produce language that is quantitatively much more sophisticated and qualitatively more context-sensitive, thereby considerably improving the quality of generated language.
  • Visual AI Capabilities: Growth in the authentic production of realistic pictures and moving pictures from print text will increase the creative possibilities of several industries, including sales promotion and the film and television industries.
  • AI Companies’ Role: The role of leading machine intelligence companies is to continue to extend capital expenditure in the enhancement of more elaborate models that can both understand and create material.

Why Should Ethical Standards be Necessary?

As generative AI develops, the issue of ethical regulation goes to the forefront, and moral standards and laws start to emerge. Possible issues over fake news, piracy, and potential prejudice in AI outputs are discussed by the rapid dissemination of AI content. It demonstrates that appropriate usage of these technologies will require close cooperation between policymakers, content producers, and AI businesses. 

The opportunity of utilizing generative AI in content creation is, therefore, great while managing risks lower by designing the structures that enforce those that ensure accountability and transparency.

Challenges and Ethical Concerns

To address the challenges mentioned above, stakeholders can approach the challenges of integrating generative AI in content creation while boosting its advantages, as follows:

Quality Control 

  • Issues with Accuracy: Some of them are features like One of the biggest problems is ensuring the quality and accuracy of the generated material. While the generative AI system can write writing that appears to be coherent, it could not be insightful or accurate.
  • Human Oversight: For these issues, human control and intervention are essential. Still, once competent professionals edit the generated content, the quality will be adequate for the task and convey the overall intended message.

Issues Related to Intellectual Property

  • Ownership Issues: As with most things generative AI does, the question of ownership arises. It is still unclear who owns the content generated by artificial intelligence. To artists and companies, this ambiguity is not helpful and makes legal issues more complicated.
  • Legal Implications: Copyright laws and related risks the companies face when employing the material created by AI must be understood to bring a proper approach to the legal aspect of this phenomenon.

Bias and False Information

  • Possibility of Bias: Strangely enough, in the best-case scenario, such biases reproduce in the training data and can become ingrained in AI algorithms. This in-created content may reinforce stereotypes or may lead to distorted representations.
  • Misinformation Risks: One of the greatest threats of using automated content creation is that it serves as a channel for spreading fake news. This could be of significant concern since AI generates information that is sometimes inaccurate or misleading when not monitored regularly and could erode credibility and confidence.

About job insecurity:

  • Fears of Employment Loss: A recent problem of the generative AI model is the concern that it brings about the loss of jobs in creative professions. Most are convinced that automation eliminates the need for human content production, thus leading to unemployment.
  • Emerging Job Roles: Using AI tools will create new jobs. To avoid this, humans will remain in charge and responsible for AI as well as its implementation. Some will use AI in their productions—more details are at this link. Others will use it in art. As more firms use AI, the chance of having these roles will rise.

Conclusion

AI is more than search technology. It boosts productivity, creativity, and personalization in content creation. Care management can increase personal spending in a custom way. But, it has some issues worth discussing. Companies with AI solutions must also find solutions to ethical questions that may arise. By cautiously using generative AI, we can develop more vibrant digital communities and expand the possibilities of content creation.

Featured image by Jakub ?erdzicki on Unsplash

The post The Impact of Generative AI on Content Creation appeared first on noupe.

Categories: Others Tags:

Left Half and Right Half Layout – Many Different Ways

October 25th, 2024 No comments

A whole bunch of years ago, we posted on this idea here on CSS-Tricks. We figured it was time to update that and do the subject justice.

Imagine a scenario where you need to split a layout in half. Content on the left and content on the right. Basically two equal height columns are needed inside of a container. Each side takes up exactly half of the container, creating a distinct break between one. Like many things in CSS, there are a number of ways to go about this and we’re going to go over many of them right now!

Update (Oct. 25, 2024): Added an example that uses CSS Anchor Positioning.

Using Background Gradient

One simple way we can create the appearance of a changing background is to use gradients. Half of the background is set to one color and the other half another color. Rather than fade from one color to another, a zero-space color stop is set in the middle.

.container {
  background: linear-gradient(
    to right, 
    #ff9e2c 0%, 
    #ff9e2c 50%, 
    #b6701e 50%, 
    #b6701e 100%
  );
}

This works with a single container element. However, that also means that it will take working with floats or possibly some other layout method if content needs to fill both sides of the container.

CodePen Embed Fallback

Using Absolute Positioning

Another route might be to set up two containers inside of a parent container, position them absolutely, split them up in halves using percentages, then apply the backgrounds. The benefit here is that now we have two separate containers that can hold their own content.

CodePen Embed Fallback

Absolute positioning is sometimes a perfect solution, and sometimes untenable. The parent container here will need to have a set height, and setting heights is often bad news for content (content changes!). Not to mention absolute positioned elements are out of the document flow. So it would be hard to get this to work while, say, pushing down other content below it.

Using (fake) Tables

Yeah, yeah, tables are so old school (not to mention fraught with accessibility issues and layout inflexibility). Well, using the display: table-cell; property can actually be a handy way to create this layout without writing table markup in HTML. In short, we turn our semantic parent container into a table, then the child containers into cells inside the table — all in CSS!

CodePen Embed Fallback

You could even change the display properties at breakpoints pretty easily here, making the sides stack on smaller screens. display: table; (and friends) is supported as far back as IE 8 and even old Android, so it’s pretty safe!

Using Floats

We can use our good friend the float to arrange the containers beside each other. The benefit here is that it avoids absolute positioning (which as we noted, can be messy).

CodePen Embed Fallback

In this example, we’re explicitly setting heights to get them to be even. But you don’t really get that ability with floats by default. You could use the background gradient trick we already covered so they just look even. Or look at fancy negative margin tricks and the like.

Also, remember you may need to clear the floats on the parent element to keep the document flow happy.

Using Inline-Block

If clearing elements after floats seems like a burden, then using display: inline-block is another option. The trick here is to make sure that the elements for the individual sides have no breaks or whitespace in between them in the HTML. Otherwise, that space will be rendered as a literal space and the second half will break and fall.

CodePen Embed Fallback

Again there is nothing about inline-block that helps us equalize the heights of the sides, so you’ll have to be explicit about that.

There are also other potential ways to deal with that spacing problem described above.

Using Flexbox

Flexbox is a pretty fantastic way to do this, just note that it’s limited to IE 10 and up and you may need to get fancy with the prefixes and values to get the best support.

Using this method, we turn our parent container into a flexible box with the child containers taking up an equal share of the space. No need to set widths or heights! Flexbox just knows what to do, because the defaults are set up perfectly for this. For instance, flex-direction: row; and align-items: stretch; is what we’re after, but those are the defaults so we don’t have to set them. To make sure they are even though, setting flex: 1; on the sides is a good plan. That forces them to take up equal shares of the space.

CodePen Embed Fallback

In this demo we’re making the side flex containers as well, just for fun, to handle the vertical and horizontal centering.

Using Grid Layout

For those living on the bleeding edge, the CSS Grid Layout technique is like the Flexbox and Table methods merged into one. In other words, a container is defined, then split into columns and cells which can be filled flexibly with child elements.

CodePen Embed Fallback

CSS Anchor Positioning

This started rolling out in 2024 and we’re still waiting for full browser support. But we can use CSS Anchor Positioning to “attach” one element to another — even if those two elements are completely unrelated in the markup.

The idea is that we have one element that’s registered as an “anchor” and another element that’s the “target” of that anchor. It’s like the target element is pinned to the anchor. And we get to control where we pin it!

.anchor {
  anchor-name: --anchor;
}

.target {
  anchor-position: --anchor;
  position: absolute; /* required */
}

This sets up an .anchor and establishes a relationship with a .target element. From here, we can tell the target which side of the anchor it should pin to.

.anchor {
  anchor-name: --anchor;
}

.target {
  anchor-position: --anchor;
  position: absolute; /* required */
  left: anchor(right);
}
CodePen Embed Fallback

Isn’t it cool how many ways there are to do things in CSS?


Left Half and Right Half Layout – Many Different Ways originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

Categories: Designing, Others Tags:

You can use text-wrap: balance; on icons

October 24th, 2024 No comments

Terence Eden on using text-wrap: balance for more than headings:

But the name is, I think, slightly misleading. It doesn’t only work on text. It will work on any content. For example – I have a row of icons at the bottom of this page. If the viewport is too narrow, a single icon might drop to the next line. That can look a bit weird.

Heck yeah. I may have reached for some sort of auto-fitting grid approach, but hey, may as well go with a one-liner if you can! And while we’re on the topic, I just wanna mention that, yes, text-wrap: balance will work on any content. — just know that the spec is a little opinionated on this and make sure that the content is fewer than five lines.

There’s likely more nuance to come if the note for Issue 6 in the spec is any indication about possibly allowing for a line length minimum:

Suggestion for value space is match-indent | |  (with Xch given as an example to make that use case clear). Alternately  could actually count the characters.


You can use text-wrap: balance; on icons originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

Categories: Designing, Others Tags:

8 Best SEO Tools for AI Automation

October 24th, 2024 No comments

When it comes to choosing AI SEO tools, the sheer variety of options can be overwhelming. The popular ones may not always be the best fit for your specific SEO workflow and processes. There’s no one-size-fits-all solution for finding the perfect AI tool for SEO automation.

The right choice depends on your unique objectives, budget, and processes. This guide explores the 8 best SEO tools for AI automation and provides insights on how to use them effectively to help you make an informed decision.

You will gain an understanding of each tool’s core features, which will help you identify the ones that align with your goals.

What is SEO Automation?

SEO automation is the process of using software tools to streamline and simplify various search engine optimization tasks. By automating repetitive activities, marketers can save time and reduce manual work. This allows them to focus on more strategic aspects of the campaign, ultimately improving efficiency and effectiveness in boosting the website’s visibility on search engines. 

What SEO Tasks Can You Do With Automation Tools?

Here are some key SEO tasks you can perform with automation tools.

1. Site Monitoring

Track your website’s health automatically, including uptime, load speed, and broken links. This helps you identify issues quickly.

2. Rank Tracking

Monitor your website’s ranking for specific keywords over time. This allows you to see how your SEO efforts are paying off.

3. Backlink Analysis

Analyze your backlinks to understand their quality and identify new link-building opportunities. This is crucial for improving your site’s authority.

4. Competitor Monitoring

Monitor your competitors’ strategies, including their rankings and backlink profiles. This information can help you adjust your tactics.

5. Report Creation

Generate automated reports summarizing your SEO performance, including traffic trends and keyword rankings. These reports save time and provide valuable insights.

6. Error Tracking

Automatically detect technical errors on your site, such as 404 pages or server issues, allowing you to fix them promptly.

8 Best AI Tools for SEO Automation

1. Jasper

Price: Starts at $49 per month

Useful For: Streamlining content creation and marketing strategies

Features:

  • Brand voice customization allows you to tailor content to reflect your unique style.
  • Access to over 50 AI-powered templates for various writing needs.
  • Create unique images with its image generation tool to complement your text content.

Jasper is an AI writing assistant designed to help marketers and content creators produce high-quality content quickly. Its ability to customize brand voice and provide a wide range of templates makes it easy to generate engaging copy for blogs, social media, and more. Additionally, its image generation feature allows users to create visuals that enhance their written content, making it a versatile tool for anyone looking to boost their marketing efforts efficiently.

2. ChatGPT

Price: Starts at $20 per month

Useful For: Generating human-like text for various applications

Features:

  • Contextual understanding that provides relevant responses based on previous messages.
  • Multilingual support allows efficient communication across different cultures and breaks language barriers.
  • Creative text generation to produce engaging content, including stories, poems, and marketing copy.

ChatGPT is a powerful AI tool designed to assist users in generating natural language responses. It can be used for a variety of tasks, such as answering questions, creating content, and even translating languages. Its ability to understand context and produce coherent text makes it valuable for marketers and a digital marketing agency looking for quick, high-quality writing. With its user-friendly interface, ChatGPT simplifies complex tasks and enhances productivity across different fields.

3. Link Whisper

Price: Starts at $77 per year

Useful For: Enhancing internal linking strategies for better SEO

Features:

  • Get automated internal link recommendations while writing content.
  • Detect orphan pages lacking internal links and easily add connections.
  • Manage multiple links effortlessly by adding, editing, or removing them from a centralized dashboard.

LinkWhisper is an AI-powered tool designed to optimize your website’s internal linking structure. It simplifies the process of adding internal links by suggesting relevant options while you write, saving time and effort. With features like orphan page detection, you can ensure all your content is interconnected, improving your site’s SEO performance. The bulk link management feature allows for efficient updates, making it a valuable asset for bloggers and marketers looking to enhance their online presence.

4. Alli AI

Price: Starts at $299 per month

Useful For: Optimizing website experience for SERPs

Features:

  • Bulk on-page optimization is ideal for setting up SEO rules for the entire website.
  • Automated SEO A/B testing that uses user behavior data to boost traffic and optimize pages
  • Real-time deployment allows easy application of code changes across your site.

Alli AI offers a comprehensive platform that helps users manage their SEO strategies efficiently. Its user-friendly interface and powerful features allow for bulk optimization, competitor analysis, and performance tracking. This tool not only saves time but also enhances the effectiveness of your SEO efforts, making it a valuable asset for anyone looking to improve their search engine rankings.

5. Frase

Price: Starts at $15 per month

Useful For: Streamlining content creation and improving search engine rankings.

Features:

  • Content briefs generate detailed outlines based on top-ranking articles for your target keywords.
  • Get real-time suggestions to improve your content’s SEO performance as you write.
  • Find answers to common questions in your niche, enhancing your content’s relevance.

Frase simplifies the content creation process by providing valuable insights and suggestions that help writers produce engaging and optimized articles. Its ability to generate content briefs ensures you cover essential topics, while real-time SEO optimization aligns your writing with best practices. This makes Frase a powerful tool for marketers, bloggers, and businesses looking to boost their online visibility and attract more traffic.

6. SE Ranking

Price: Starts at $65 per month

Useful For: Automating various SEO tasks for improved website performance

Features:

  • Schedule regular automated audits to detect technical SEO issues.
  • Track keyword rankings across major search engines and devices regularly.
  • Monitor and analyze backlinks to boost your site’s authority.

SE Ranking is a comprehensive SEO automation tool that simplifies the optimization process for businesses and agencies. It provides features like automatic website audits, which help identify and resolve technical issues efficiently. With its rank-tracking capabilities, users can monitor their keyword performance in real-time. Additionally, the backlink monitoring feature allows for effective analysis of link profiles, enhancing overall SEO strategies. This makes SE Ranking a valuable resource for anyone looking to boost their online visibility and streamline their SEO efforts.

7. PopupSmart

Price: Starts at $29 per month

Useful For: Boosting conversions through effective pop-up strategies

Features:

  • Design customizable pop-ups that align with your brand style and messaging.
  • Set targeted triggers to control when and where pop-ups appear on your site.
  • Use A/B testing to find the most effective pop-up designs and messages.

This AI tool enhances user engagement and increases conversions through effective pop-up strategies. It allows users to create customizable pop-ups that align with their brand, ensuring a seamless experience for visitors. You can control when and where pop-ups appear with targeted triggers, maximizing their effectiveness. The A/B testing feature helps you experiment with different designs and messages, enabling you to refine your approach based on real results. This makes PopupSmart a valuable tool for businesses looking to improve their website’s performance and user experience.

8. SurferSEO

Price: $219 per month

Useful For: Streamlining content creation and optimization for SEO

Features:

  • Leverage AI-powered content generation to craft fully optimized articles based on top-ranking pages.
  • Conduct real-time SERP analysis to refine your content strategy.
  • Create customizable, SEO-friendly outlines tailored to your target keywords and audience.

SurferSEO is an AI SEO tool designed to enhance your content creation process by providing data-driven insights for SEO optimization. With its AI-powered content generation, users can quickly produce articles that align with search intent and rank well on search engines. The real-time SERP analysis feature helps you understand what works for competitors, while customizable outlines ensure that your content is structured effectively. This makes SurferSEO a valuable asset for marketers and content creators aiming to improve their online visibility and engagement.

Bonus Tool: Perplexity AI

Price: Starts at $10 per month

Useful For: Automating various SEO tasks to improve website performance

Features:

  • Automate keyword research by quickly generating ideas based on current trends.
  • Produce SEO-optimized articles tailored to specific target keywords.
  • Verify the accuracy of AI-generated content with real-time, reliable fact-checking.

Perplexity is an AI tool designed to streamline SEO automation for businesses and content creators. It helps users efficiently conduct keyword research and generate high-quality content that ranks well on search engines. With real-time fact-checking, you can trust the information provided, reducing errors in your content. Perplexity is valuable for anyone looking to enhance their SEO strategies and improve their online visibility.

Benefits of Using AI Tools for SEO Automation

Using AI tools for SEO can offer you the following benefits:

Time-Saving

AI tools automate repetitive tasks like keyword research and performance tracking, allowing marketers to focus on more strategic activities instead of manual work.

Improved Accuracy

By minimizing human error, AI tools ensure that data analysis and optimization processes are more precise, leading to better decision-making in SEO strategies.

Enhanced Keyword Research

AI can quickly analyze search trends and user intent, helping identify the most relevant keywords to target for improved visibility and traffic.

Real-Time Performance Monitoring

AI tools continuously track website performance, providing instant feedback and insights that help you adapt your strategies promptly to changing conditions.

Content Optimization

AI can analyze existing content and suggest improvements, such as better keyword placement and structure, ensuring your content remains engaging and aligned with SEO best practices.

Enhance Your Marketing with AI SEO Tools

AI SEO tools offer incredible power to supercharge your SEO strategy but remember, they’re here to complement your expertise, not replace it. Finding the right balance between leveraging AI capabilities and applying your strategic insight and creativity.

How will you blend these tools with your unique approach to maximize your SEO potential? Consider partnering with a reliable and experienced agency for unmatched outcomes. Are you ready to explore how advanced AI tools and your smart planning can work together? Start with SEO automation today!

Featured image by Hatice Baran

The post 8 Best SEO Tools for AI Automation appeared first on noupe.

Categories: Others Tags:

Mastering the Art of Web Typography: Enhancing Readability and Brand Identity

October 24th, 2024 No comments

Typography is a core element of web design that impacts how easily people can read and engage with your content. 

Your message and brand identity stand out when you have a font that draws the eye in. 

When you know how to use web typography principles to grab attention, you can transform a good-looking website into one that is captivating. 

Let’s take a closer look at why your website typography matters — and how you can use it to enhance readability and brand identity.

Understanding typography

Web typography best practices focus on how text looks and feels on a screen. Unlike print typography, where the final product is static, web typography must adapt to various screen sizes and resolutions. 

This means you’ll need to choose fonts that are both aesthetic and function properly on different devices.

But before you dive into choosing fonts, you need to understand a few basic terms.

Fundamental typography terms

Here are some basics to know:

  • Typeface vs. font: A typeface is an overall design, like Helvetica. A font is a specific style within that typeface, such as Helvetica Bold or Helvetica Light.

    • So, when you choose Helvetica, you’re picking a typeface. When you choose Helvetica Bold, you’re selecting a font.
Source
  • Serif vs. sans-serif fonts: Serif fonts have small lines at the end of characters (like Times New Roman), enhancing print readability. *Sans-serif fonts (like Helvetica) are cleaner and generally easier to read on screens, making them a popular choice for web design.
Source
  • Kerning, tracking, leading, and hierarchy: Kerning adjusts the space between individual letters, tracking adjusts the space across a block of text, and leading is the space between lines of text. Hierarchy is how you use different sizes and styles to guide readers through content.
Source

Font selection for web design

Here’s how to select the best fonts for your website:

Consider readability and brand identity

Choose a font that aligns with your brand’s personality. For instance, Helvetica’s clean lines suit professional sites like Beaches of Normandy Tours, where its articles, such as the Liberation of Paris, must clearly convey historical information. On the other hand, a playful font might suit a creative agency.

Use web-safe and standard fonts

Stick to fonts that are widely supported across modern browsers. Fonts like Arial, Verdana, and Georgia are safe choices. 

Implement font stacks

If you prefer unique fonts, use font stacks. This means listing several fallback fonts in your CSS, so if one isn’t available, it’ll use another one of your choices.

This helps make sure that your text looks good across different devices. For example, you might use “Helvetica, Arial, sans-serif” in your CSS. If Helvetica isn’t available, Arial or a generic sans-serif will take its place.

Designing for readability

Readability is key to a successful website. 

Here’s how to make sure your text is easy to read:

  1. Text size and line length: Aim for a base font size of 16px. For line length, keep lines between 40 and 80 characters to avoid reader fatigue. Larger fonts are easier to read on smaller screens, so adjust sizes for mobile devices.
  2. Spacing and alignment: Make sure there’s enough space between lines and paragraphs. This vertical spacing, or leading, should generally be 1.5 times the font size for body text.

    1. Use white space strategically to make your content more digestible and visually appealing.
  3. Color contrast: High contrast between text and background color is essential. Use tools like a contrast checker to make sure your color choices meet accessibility standards. For example, black text on a white background is typically a safe bet.

Advanced typography considerations

Adapt your typography to various screen sizes and settings. 

Consider: 

Screen size and resolution

Text may look different on various devices. Responsive design helps adjust typography based on screen size. (Test your designs on different devices to promote consistent readability.)

Adjustments 

Experiment with typography settings. 

Compare how changes to line height, letter spacing, and other aspects affect readability.

X-height and counter-openness

The x-height is the height of lowercase letters, which affects readability. Fonts with larger x-heights are generally easier to read. Counter-openness refers to the space inside letters like “e” and “a” and should be ample for legibility.

Enhancing brand identity through typography

Choose fonts that reflect your brand’s character and values. 

For instance, Henry Meds uses clean, sans-serif fonts to establish a modern, professional image. This aligns with its commitment to clear and accessible health information. It also uses consistent formatting, clear headings for navigation — and white space around text blocks to make the content more engaging and digestible. 

This look is congruent across its blog, which helps readers more easily digest its medical topics, such as the following one about semaglutide costs without insurance.

Other successful brands, like Nike and Coca-Cola, also demonstrate how typography can strengthen brand identity. 

Nike uses bold fonts that communicate strength and innovation. This perfectly aligns with its dynamic and forward-thinking brand image. On the other hand, Coca-Cola uses a script font that evokes a sense of nostalgia and tradition. This reinforces its classic and beloved brand persona. (Who could ever forget its beloved polar bears and Christmas-themed branding?)

Long story short: Choose fonts that resonate with your core message to leave a lasting impression and connect deeper with your audience.

Wrap up

Mastering web typography means ensuring that your text is readable, your brand identity is clear, and your content effectively engages your audience. 

Understand typography basics, choose the right fonts, and continuously test and optimize your website to create a web experience that looks great and performs well. Remember, great typography enhances readability and strengthens brand identity. So, take the time to perfect it and watch your website thrive.

Featured image by Florian Klauer on Unsplash

The post Mastering the Art of Web Typography: Enhancing Readability and Brand Identity appeared first on noupe.

Categories: Others Tags:

Clarifying the Relationship Between Popovers and Dialogs

October 23rd, 2024 No comments
Line diagram connecting the popover attribute to six types of accessible roles, including dialog.

The difference between Popovers (i.e., the popover attribute) and Dialogs (i.e., both the element and the dialog accessible role) is incredibly confusing — so much that many articles (like this, this, and this) have tried to shed some light on the issue.

If you’re still feeling confused, I hope this one clears up that confusion once and for all.

Distinguishing Popovers From Dialogs

Let’s pull back on the technical implementations and consider the greater picture that makes more sense and puts everything into perspective.

The reason for this categorization comes from a couple of noteworthy points.

First, we know that a popover is content that “pops” up when a user clicks a button (or hovers over it, or focuses on it). In the ARIA world, there is a useful attribute called aria-haspopup that categorizes such popups into five different roles:

  • menu
  • listbox
  • tree
  • grid
  • dialog

Strictly speaking, there’s a sixth value, true, that evaluates to menu. I didn’t include it above since it’s effectively just menu.

By virtue of dialog being on this list, we already know that dialog is a type of popover. But there’s more evidence behind this too.

The Three Types of Dialogues

Since we’re already talking about the dialog role, let’s further expand that into its subcategories:

Lone diagram connecting the popover attribute to six accessible roles, including dialog, which is broken out into three categories.

Dialogs can be categorized into three main kinds:

  • Modal: A dialog with an overlay and focus trapping
  • Non-Modal: A dialog with neither an overlay nor focus trapping
  • Alert Dialog: A dialog that alerts screen readers when shown. It can be either modal or non-modal.

This brings us to another reason why a dialog is considered a popover.

Some people may say that popovers are strictly non-modal, but this seems to be a major misunderstanding — because popovers have a ::backdrop pseudo-element on the top layer. The presence of ::backdrop indicates that popovers are modal. Quoting the CSS-Tricks almanac:

The ::backdrop CSS pseudo-element creates a backdrop that covers the entire viewport and is rendered immediately below a , an element with the popup attribute, or any element that enters fullscreen mode using the Fullscreen API.

That said, I don’t recommend using the Popover API for modality because it doesn’t have a showModal() method (that has) that creates inertness, focus trapping, and other necessary features to make it a real modal. If you only use the Popover API, you’ll need to build those features from scratch.

So, the fact that popovers can be modal means that a dialog is simply one kind of popover.

A Popover Needs an Accessible Role

Popovers need a role to be accessible. Hidde has a great article on selecting the right role, but I’m going to provide some points in this article as well.

To start, you can use one of the aria-haspopup roles mentioned above:

  • menu
  • listbox
  • tree
  • grid
  • dialog

You could also use one of the more complex roles like:

  • treegrid
  • alertdialog

There are two additional roles that are slightly more contentious but may do just fine.

  • tooltip
  • status

To understand why tooltip and status could be valid popover roles, we need to take a detour into the world of tooltips.

A Note on Tooltips

From a visual perspective, a tooltip is a popover because it contains a tiny window that pops up when the tooltip is displayed.

I included tooltip in the mental model because it is reasonable to implement tooltip with the Popover API.

<div popver role="tooltip">...</div>

The tooltip role doesn’t do much in screen readers today so you need to use aria-describedby to create accessible tooltips. But it is still important because it may extend accessibility support for some software.

But, from an accessibility standpoint, tooltips are not popovers. In the accessibility world, tooltips must not contain interactive content. If they contain interactive content, you’re not looking at a tooltip, but a dialog.

You’re thinking of dialogs. Use a dialog.

Heydon Pickering, “Your Tooltips are Bogus”

This is also why aria-haspopup doesn’t include tooltiparia-haspopup is supposed to signify interactive content but a tooltip must not contain interactive content.

With that, let’s move on to status which is an interesting role that requires some explanation.

Why status?

Tooltips have a pretty complex history in the world of accessible interfaces so there’s a lot of discussion and contention over it.

To keep things short (again), there’s an accessibility issue with tooltips since tooltips should only show on hover. This means screen readers and mobile phone users won’t be able to see those tooltips (since they can’t hover on the interface).

Steve Faulkner created an alternative — toggletips — to fill the gap. In doing so, he explained that toggletip content must be announced by screen readers through live regions.

When initially displayed content is announced by (most) screen readers that support aria-live

Heydon Pickering later added that status can be used in his article on toggletips.

We can supply an empty live region, and populate it with the toggletip “bubble” when it is invoked. This will both make the bubble appear visually and cause the live region to announce the tooltip’s information.

<!-- Code example by Heydon -->
<span class="tooltip-container"> 
  <button type="button" aria-label="more info" data-toggletip-content="This clarifies whatever needs clarifying">i</button> 
  <span role="status"> 
    <span class="toggletip-bubble">This clarifies whatever needs clarifying</span> 
  </span>
</span>

This is why status can be a potential role for a popover, but you must use discretion when creating it.

That said, I’ve chosen not to include the status role in the Popover mental model because status is a live region role and hence different from the rest.

In Summary

Here’s a quick summary of the mental model:

  • Popover is an umbrella term for any kind of on-demand popup.
  • Dialog is one type of popover — a kind that creates a new window (or card) to contain some content.

When you internalize this, it’s not hard to see why the Popover API can be used with the dialog element.

<!-- Uses the popover API. Role needs to be determined manually -->
<div popover>...</div>

<!-- Dialog with the popover API. Role is dialog -->
<dialog popover>...</dialog>

<!-- Dialog that doesn't use the popover API. Role is dialog -->
<dialog>...</dialog>

When choosing a role for your popover, you can use one of these roles safely.

  • menu
  • listbox
  • tree
  • grid
  • treegrid
  • dialog
  • alertdialog

The added benefit is most of these roles work together with aria-haspopup which gained decent support in screen readers last year.

Of course, there are a couple more you can use like status and tooltip, but you won’t be able to use them together with aria-haspopup.

Further Reading


Clarifying the Relationship Between Popovers and Dialogs originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

Categories: Designing, Others Tags:

Clamp it! VS Code extension

October 23rd, 2024 No comments

There’s a lot of math behind fluid typography. CSS does make the math a lot easier these days, but even if you’re comfortable with that, writing the full declaration can be verbose and tough to remember. I know I often have to look it back up, despite having written it maybe a hundred times.

Silvestar made a little VS Code helper to abstract all that. Type in the target values you’re aiming for and the helper expands it on the spot.

He says ChatGPT did the initial lifting before he refined it. I can get behind this sort of AI-flavored usage. Start with an idea, find a starting point, look deeper at it, and shape it into something incredibly useful for a small, single purpose.


Clamp it! VS Code extension originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

Categories: Designing, Others Tags:

How to Make Your Content Marketing Pieces Work Harder

October 23rd, 2024 No comments

As a creator, you know that you’ve got to be consistent to make it in the content game. But consistency isn’t enough on its own. In order to stand out from the crowd, your consistently produced content has to be quality. You’ve got to make stuff that your target audience will genuinely enjoy, find helpful, or, ideally, both. To do this on a regular basis, you need your content to work harder. Here are a few tips you should know.

Transform Your Videos

Repurposing content is one of the fundamental ways to make your content work harder. It can also be one of the easiest and most straightforward. Rather than create entirely new content every time, transform work you’ve already done to use it in new ways. This could be as simple as clipping key moments from your YouTube videos and editing them into a Short. You can then repurpose that same Short again by posting it on Instagram and TikTok.

Take it one step further by turning your videos into written content. You can create an article from a video using AI technology that can summarize the info. Include the video in the article itself to give your audience the option to watch your content, read it, or both. With this strategy, you get at least two pieces of content for little more than the work of one.

Go the Extra Mile With SEO

As you expand the written content you have available, it’s crucial to ensure your SEO is up to snuff. Optimizing SEO involves various strategies to enhance your website’s visibility and ranking on search engine results pages. This includes keyword research to identify relevant terms users are searching for, optimizing meta tags to accurately describe content, improving website speed and mobile-friendliness for better user experience, and earning backlinks from authoritative sites to increase your credibility in the eyes of search engines (and your audience).

To go the extra mile with SEO, you can implement more advanced strategies. Make sure you’re creating high-quality, in-depth content that thoroughly addresses user queries and provides unique insights. Conducting competitor analysis to identify gaps in their SEO strategies and capitalize on opportunities they may have missed can also be beneficial. Continuously monitoring and analyzing performance metrics, and adjusting strategies accordingly, ensures improvement over time.

Create Evergreen Content

Speaking of longevity, jumping on the bandwagon of a niche or industry-specific trend can be great for getting short-term exposure. After all, there’s always that chance you ride the crest of that wave and go viral. However, if trend-chasing is all you do, your content isn’t going to be able to stand the test of time. This is true no matter how big one post gets. 

For your brand to be useful, informative, and engaging in the long run, the content you produce must be too. So focus on creating helpful content like tutorials and how-tos, or address frequently asked questions on subjects of your expertise. Case studies, spotlights, and product comparisons can also serve as anchors to inform and empower your audience. The bandwagon is  great to jump on now and then. But, as a professional creator, keep producing quality content that lasts as your primary focus.

Influence Your Audience

You can create the best content in the world, but no one will care if they can’t see it. Besides quality and consistency, visibility is one of the greatest challenges for any content creator. Now, optimizing your SEO will always help. But you need to be more proactive to truly get your feet off the ground. Try expanding your reach and your audience by piggybacking off someone else’s. 

Identify individuals whose expertise or industry aligns with your niche. Then reach out to those influencers to collaborate or otherwise share and repurpose content. This could look like guest posting on an industry expert’s blog for free, for example. They get free, high-quality content to post, and you get more eyes on your work. Or you could do a true collaboration, working together to produce something new. The key thing here is to provide real value to the person you’re reaching out to so you both benefit. 

Actually Engage Your Audience

As your content-creation career matures, you may begin to lose sight of your audience. Remember that, like a good conversation, content creation is a two-way street. And producing content without paying attention to your audience’s reaction to it is like lecturing someone during a “conversation.” So be sure to redouble your efforts to engage with your audience.

This can be as simple as including a comments section on your blog and thoughtfully responding as much as possible. It could involve creating and hosting events via a community Discord server or Twitch stream. You could even crowdsource content ideas by conducting polls to see what your audience wants next. As the creator, you’ll always be the one driving the car when it comes to content creation. But, as the driver, find ways to engage your audience to ensure they’re actively enjoying the ride. 

Harder and Smarter

Being a content creator can be one of the best jobs in the world. But, at the end of the day, it’s still a job. It’s important to make sure you and your audience are getting the most value out of everything you’re producing. 

In this light you want your content to work harder for you. Be smart about what kind of material you’re producing and how your audience receives it. Find social networking opportunities to leverage. And, no matter what, always ensure you’re producing quality content as consistently as possible. 

Featured Image by Callum Shaw on Unsplash

The post How to Make Your Content Marketing Pieces Work Harder appeared first on noupe.

Categories: Others Tags:

Unleash the Power of Scroll-Driven Animations

October 21st, 2024 No comments

I’m utterly behind in learning about scroll-driven animations apart from the “reading progress bar” experiments all over CodePen. Well, I’m not exactly “green” on the topic; we’ve published a handful of articles on it including this neat-o one by Lee Meyer published the other week.

Our “oldest” article about the feature is by Bramus, dated back to July 2021. We were calling it “scroll-linked” animation back then. I specifically mention Bramus because there’s no one else working as hard as he is to discover practical use cases where scroll-driven animations shine while helping everyone understand the concept. He writes about it exhaustively on his personal blog in addition to writing the Chrome for Developers documentation on it.

But there’s also this free course he calls “Unleash the Power of Scroll-Driven Animations” published on YouTube as a series of 10 short videos. I decided it was high time to sit, watch, and learn from one of the best. These are my notes from it.


Introduction

  • A scroll-driven animation is an animation that responds to scrolling. There’s a direct link between scrolling progress and the animation’s progress.
  • Scroll-driven animations are different than scroll-triggered animations, which execute on scroll and run in their entirety. Scroll-driven animations pause, play, and run with the direction of the scroll. It sounds to me like scroll-triggered animations are a lot like the CSS version of the JavaScript intersection observer that fires and plays independently of scroll.
  • Why learn this? It’s super easy to take an existing CSS animation or a WAAPI animation and link it up to scrolling. The only “new” thing to learn is how to attach an animation to scrolling. Plus, hey, it’s the platform!
  • There are also performance perks. JavsScript libraries that establish scroll-driven animations typically respond to scroll events on the main thread, which is render-blocking… and JANK! We’re working with hardware-accelerated animations… and NO JANK. Yuriko Hirota has a case study on the performance of scroll-driven animations published on the Chrome blog.
  • Supported in Chrome 115+. Can use @supports (animation-timeline: scroll()). However, I recently saw Bramus publish an update saying we need to look for animation-range support as well.
@supports ((animation-timeline: scroll()) and (animation-range: 0% 100%)) {
  /* Scroll-Driven Animations related styles go here */
  /* This check excludes Firefox Nightly which only has a partial implementation at the moment of posting (mid-September 2024). */
}
  • Remember to use prefers-reduced-motion and be mindful of those who may not want them.

Core Concepts: scroll() and ScrollTimeline

Let’s take an existing CSS animation.

@keyframes grow-progress {
  from {
    transform: scaleX(0);
  }
  to {
    transform: scaleX(1);
  }
}

#progress {
  animation: grow-progress 2s linear forwards;
}

Translation: Start with no width and scale it to its full width. When applied, it takes two seconds to complete and moves with linear easing just in the forwards direction.

This just runs when the #progress element is rendered. Let’s attach it to scrolling.

  • animation-timeline: The timeline that controls the animation’s progress.
  • scroll(): Creates a new scroll timeline set up to track the nearest ancestor scroller in the block direction.
#progress {
  animation: grow-progress 2s linear forwards;
  animation-timeline: scroll();
}

That’s it! We’re linked up. Now we can remove the animation-duration value from the mix (or set it to auto):

#progress {
  animation: grow-progress linear forwards;
  animation-timeline: scroll();
}

Note that we’re unable to plop the animation-timeline property on the animation shorthand, at least for now. Bramus calls it a “reset-only sub-property of the shorthand” which is a new term to me. Its value gets reset when you use the shorthand the same way background-color is reset by background. That means the best practice is to declare animation-timeline after animation.

/* YEP! */
#progress {
  animation: grow-progress linear forwards;
  animation-timeline: scroll();
}

/* NOPE! */
#progress {
  animation-timeline: scroll();
  animation: grow-progress linear forwards;
}

Let’s talk about the scroll() function. It creates an anonymous scroll timeline that “walks up” the ancestor tree from the target element to the nearest ancestor scroll. In this example, the nearest ancestor scroll is the :root element, which is tracked in the block direction.

We can name scroll timelines, but that’s in another video. For now, know that we can adjust which axis to track and which scroller to target in the scroll() function.

animation-timeline: scroll(<axis> <scroller>);
  • : The axis — be it block (default), inline, y, or x.
  • : The scroll container element that defines the scroll position that influences the timeline’s progress, which can be nearest (default), root (the document), or self.

If the root element does not have an overflow, then the animation becomes inactive. WAAPI gives us a way to establish scroll timelines in JavaScript with ScrollTimeline.

const $progressbar = document.querySelector(#progress);

$progressbar.style.transformOrigin = '0% 50%';
$progressbar.animate(
  {
    transform: ['scaleX(0)', 'scaleY()'],
  },
  {
    fill: 'forwards',
    timeline: new ScrollTimeline({
      source: document.documentElement, // root element
      // can control `axis` here as well
    }),
  }
)

Core Concepts: view() and ViewTimeline

First, we oughta distinguish a scroll container from a scroll port. Overflow can be visible or clipped. Clipped could be scrolling.

Diagram showing scrollport, scroll container, and scrollable overflow.

Those two bordered boxes show how easy it is to conflate scrollports and scroll containers. The scrollport is the visible part and coincides with the scroll container’s padding-box. When a scrollbar is present, that plus the scroll container is the root scroller, or the scroll container.

Diagram showing the root scroller.

A view timeline tracks the relative position of a subject within a scrollport. Now we’re getting into IntersectionObserver territory! So, for example, we can begin an animation on the scroll timeline when an element intersects with another, such as the target element intersecting the viewport, then it progresses with scrolling.

Bramus walks through an example of animating images in long-form content when they intersect with the viewport. First, a CSS animation to reveal an image from zero opacity to full opacity (with some added clipping).

@keyframes reveal {
  from {
    opacity: 0;
    clip-path: inset(45% 20% 45% 20%);
  }
  to {
    opacity: 1;
    clip-path: inset(0% 0% 0% 0%);
  }
}

.revealing-image {
  animation: reveal 1s linear both;
}

This currently runs on the document’s timeline. In the last video, we used scroll() to register a scroll timeline. Now, let’s use the view() function to register a view timeline instead. This way, we’re responding to when a .revealing-image element is in, well, view.

.revealing-image {
  animation: reveal 1s linear both;
  /* Rember to declare the timeline after the shorthand */
  animation-timeline: view();
}

At this point, however, the animation is nice but only completes when the element fully exits the viewport, meaning we don’t get to see the entire thing. There’s a recommended way to fix this that Bramus will cover in another video. For now, we’re speeding up the keyframes instead by completing the animation at the 50% mark.

@keyframes reveal {
  from {
    opacity: 0;
    clip-path: inset(45% 20% 45% 20%);
  }
  50% {
    opacity: 1;
    clip-path: inset(0% 0% 0% 0%);
  }
}

More on the view() function:

animation-timeline: view(<axis> <view-timeline-inset>);

We know from the scroll() function — it’s the same deal. The is a way of adjusting the visibility range of the view progress (what a mouthful!) that we can set to auto (default) or a . A positive inset moves in an outward adjustment while a negative value moves in an inward adjustment. And notice that there is no argument — a view timeline always tracks its subject’s nearest ancestor scroll container.

OK, moving on to adjusting things with ViewTimeline in JavaScript instead.

const $images = document.querySelectorAll(.revealing-image);

$images.forEach(($image) => {
  $image.animate(
    [
      { opacity: 0, clipPath: 'inset(45% 20% 45% 20%)', offset: 0 }
      { opacity: 1; clipPath: 'inset(0% 0% 0% 0%)', offset: 0.5 }
    ],
    {
      fill: 'both',
      timeline: new ViewTimeline({
        subject: $image,
        axis: 'block', // Do we have to do this if it's the default?
      }),
    }
  }
)

This has the same effect as the CSS-only approach with animation-timeline.

Timeline Ranges Demystified

Last time, we adjusted where the image’s reveal animation ends by tweaking the keyframes to end at 50% rather than 100%. We could have played with the inset(). But there is an easier way: adjust the animation attachment range,

Most scroll animations go from zero scroll to 100% scroll. The animation-range property adjusts that:

animation-range: normal normal;

Those two values: the start scroll and end scroll, default:

animation-range: 0% 100%;

Other length units, of course:

animation-range: 100px 80vh;

The example we’re looking at is a “full-height cover card to fixed header”. Mouthful! But it’s neat, going from an immersive full-page header to a thin, fixed header while scrolling down the page.

@keyframes sticky-header {
  from {
    background-position: 50% 0;
    height: 100vh;
    font-size: calc(4vw + 1em);
  }
  to {
    background-position: 50% 100%;
    height: 10vh;
    font-size: calc(4vw + 1em);
    background-color: #0b1584;
  }
}

If we run the animation during scroll, it takes the full animation range, 0%-100%.

.sticky-header {
  position: fixed;
  top: 0;

  animation: sticky-header linear forwards;
  animation-timeline: scroll();
}

Like the revealing images from the last video, we want the animation range a little narrower to prevent the header from animating out of view. Last time, we adjusted the keyframes. This time, we’re going with the property approach:

.sticky-header {
  position: fixed;
  top: 0;

  animation: sticky-header linear forwards;
  animation-timeline: scroll();
  animation-range: 0vh 90vh;
}

We had to subtract the full height (100vh) from the header’s eventual height (10vh) to get that 90vh value. I can’t believe this is happening in CSS and not JavaScript! Bramus sagely notes that font-size animation happens on the main thread — it is not hardware-accelerated — and the entire scroll-driven animation runs on the main as a result. Other properties cause this as well, notably custom properties.

Back to the animation range. It can be diagrammed like this:

Visual demo showing the animation's full range.
The animation “cover range”. The dashed area represents the height of the animated target element.

Notice that there are four points in there. We’ve only been chatting about the “start edge” and “end edge” up to this point, but the range covers a larger area in view timelines. So, this:

animation-range: 0% 100%; /* same as 'normal normal' */

…to this:

animation-range: cover 0% cover 100%; /* 'cover normal cover normal' */

…which is really this:

animation-range: cover;

So, yeah. That revealing image animation from the last video? We could have done this, rather than fuss with the keyframes or insets:

animation-range: cover 0% cover 50%;

So nice. The demo visualization is hosted at scroll-driven-animations.style. Oh, and we have keyword values available: contain, entry, exit, entry-crossing, and exit-crossing.

Showing a contained animation range.
contain
Showing an entry animation range.
entry
Showing an exit animation range.
exit

The examples so far are based on the scroller being the root element. What about ranges that are taller than the scrollport subject? The ranges become slightly different.

An element larger than the scrollport where contain equals 100% when out of range but 0% before it actually reaches the end of the animation.
Just have to be aware of the element’s size and how it impacts the scrollport.

This is where the entry-crossing and entry-exit values come into play. This is a little mind-bendy at first, but I’m sure it’ll get easier with use. It’s clear things can get complex really quickly… which is especially true when we start working with multiple scroll-driven animation with their own animation ranges. Yes, that’s all possible. It’s all good as long as the ranges don’t overlap. Bramus uses a contact list demo where contact items animate when they enter and exit the scrollport.

@keyframes animate-in {
  0% { opacity: 0; transform: translateY: 100%; }
  100% { opacity: 1; transform: translateY: 0%; }
}
@keyframes animate-out {
  0% { opacity: 1; transform: translateY: 0%; }
  100% { opacity: 0; transform: translateY: 100%; }
}

.list-view li {
  animation: animate-in linear forwards,
             animate-out linear forwards;
  animation-timeline: view();
  animation-range: entry, exit; /* animation-in, animation-out */
}

Another way, using entry and exit keywords directly in the keyframes:

@keyframes animate-in {
  entry 0% { opacity: 0; transform: translateY: 100%; }
  entry 100% { opacity: 1; transform: translateY: 0%; }
}
@keyframes animate-out {
  exit 0% { opacity: 1; transform: translateY: 0%; }
  exit 100% { opacity: 0; transform: translateY: 100%; }
}

.list-view li {
  animation: animate-in linear forwards,
             animate-out linear forwards;
  animation-timeline: view();
}

Notice that animation-range is no longer needed since its values are declared in the keyframes. Wow.

OK, ranges in JavaScript.:

const timeline = new ViewTimeline({
  subjext: $li,
  axis: 'block',
})

// Animate in
$li.animate({
  opacity: [ 0, 1 ],
  transform: [ 'translateY(100%)', 'translateY(0)' ],
}, {
  fill: 'forwards',
  // One timeline instance with multiple ranges
  timeline,
  rangeStart: 'entry: 0%',
  rangeEnd: 'entry 100%',
})

Core Concepts: Timeline Lookup and Named Timelines

This time, we’re learning how to attach an animation to any scroll container on the page without needing to be an ancestor of that element. That’s all about named timelines.

But first, anonymous timelines track their nearest ancestor scroll container.

<html> <!-- scroll -->
  <body>
    <div class="wrapper">
      <div style="animation-timeline: scroll();"></div>
    </div>
  </body>
</html>

Some problems might happen like when overflow is hidden from a container:

<html> <!-- scroll -->
  <body>
    <div class="wrapper" style="overflow: hidden;"> <!-- scroll -->
      <div style="animation-timeline: scroll();"></div>
    </div>
  </body>
</html>

Hiding overflow means that the element’s content block is clipped to its padding box and does not provide any scrolling interface. However, the content must still be scrollable programmatically meaning this is still a scroll container. That’s an easy gotcha if there ever was one! The better route is to use overflow: clipped rather than hidden because that prevents the element from becoming a scroll container.

Hiding oveflow = scroll container. Clipping overflow = no scroll container. Bramus says he no longer sees any need to use overflow: hidden these days unless you explicitly need to set a scroll container. I might need to change my muscle memory to make that my go-to for hiding clipping overflow.

Another funky thing to watch for: absolute positioning on a scroll animation target in a relatively-positioned container. It will never match an outside scroll container that is scroll(inline-nearest) since it is absolute to its container like it’s unable to see out of it.

We don’t have to rely on the “nearest” scroll container or fuss with different overflow values. We can set which container to track with named timelines.

.gallery {
  position: relative;
}
.gallery__scrollcontainer {
  overflow-x: scroll;
  scroll-timeline-name: --gallery__scrollcontainer;
  scroll-timeline-axis: inline; /* container scrolls in the inline direction */
}
.gallery__progress {
  position: absolute;
  animation: progress linear forwards;
  animation-timeline: scroll(inline nearest);
}

We can shorten that up with the scroll-timeline shorthand:

.gallery {
  position: relative;
}
.gallery__scrollcontainer {
  overflow-x: scroll;
  scroll-timeline: --gallery__scrollcontainer inline;
}
.gallery__progress {
  position: absolute;
  animation: progress linear forwards;
  animation-timeline: scroll(inline nearest);
}

Note that block is the scroll-timeline-axis initial value. Also, note that the named timeline is a dashed-ident, so it looks like a CSS variable.

That’s named scroll timelines. The same is true of named view timlines.

.scroll-container {
  view-timeline-name: --card;
  view-timeline-axis: inline;
  view-timeline-inset: auto;
  /* view-timeline: --card inline auto */
}

Bramus showed a demo that recreates Apple’s old cover-flow pattern. It runs two animations, one for rotating images and one for setting an image’s z-index. We can attach both animations to the same view timeline. So, we go from tracking the nearest scroll container for each element in the scroll:

.covers li {
  view-timeline-name: --li-in-and-out-of-view;
  view-timeline-axis: inline;

  animation: adjust-z-index linear both;
  animation-timeline: view(inline);
}
.cards li > img {
   animation: rotate-cover linear both;
   animation-timeline: view(inline);
}

…and simply reference the same named timelines:

.covers li {
  view-timeline-name: --li-in-and-out-of-view;
  view-timeline-axis: inline;

  animation: adjust-z-index linear both;
  animation-timeline: --li-in-and-out-of-view;;
}
.cards li > img {
   animation: rotate-cover linear both;
   animation-timeline: --li-in-and-out-of-view;;
}

In this specific demo, the images rotate and scale but the updated sizing does not affect the view timeline: it stays the same size, respecting the original box size rather than flexing with the changes.

Phew, we have another tool for attaching animations to timelines that are not direct ancestors: timeline-scope.

timeline-scope: --example;

This goes on an parent element that is shared by both the animated target and the animated timeline. This way, we can still attach them even if they are not direct ancestors.

<div style="timeline-scope: --gallery">
  <div style="scroll-timeline: --gallery-inline;">
     ...
  </div>
  <div style="animation-timeline: --gallery;"></div>
</div>
Illustrating the relationship between a scroll target and container when they are not ancestors, but siblings.

It accepts multiple comma-separated values:

timeline-scope: --one, --two, --three;
/* or */
timeline-scope: all; /* Chrome 116+ */

There’s no Safari or Firefox support for the all kewword just yet but we can watch for it at Caniuse (or the newer BCD Watch!).

This video is considered the last one in the series of “core concepts.” The next five are more focused on use cases and examples.

Add Scroll Shadows to a Scroll Container

In this example, we’re conditionally showing scroll shadows on a scroll container. Chris calls scroll shadows one his favorite CSS-Tricks of all time and we can nail them with scroll animations.

Here is the demo Chris put together a few years ago:

CodePen Embed Fallback

That relies on having a background with multiple CSS gradients that are pinned to the extremes with background-attachment: fixed on a single selector. Let’s modernize this, starting with a different approach using pseudos with sticky positioning:

.container::before,
.container::after {
  content: "";
  display: block;
  position: sticky;
  left: 0em; 
  right 0em;
  height: 0.75rem;

  &::before {
    top: 0;
    background: radial-gradient(...);
  }
  
  &::after {
    bottom: 0;
    background: radial-gradient(...);
  }
}

The shadows fade in and out with a CSS animation:

@keyframes reveal {
  0% { opacity: 0; }
  100% { opacity: 1; }
}

.container {
  overflow:-y auto;
  scroll-timeline: --scroll-timeline block; /* do we need `block`? */

  &::before,
  &::after {
    animation: reveal linear both;
    animation-timeline: --scroll-timeline;
  }
}

This example rocks a named timeline, but Bramus notes that an anonymous one would work here as well. Seems like anonymous timelines are somewhat fragile and named timelines are a good defensive strategy.

The next thing we need is to set the animation’s range so that each pseudo scrolls in where needed. Calculating the range from the top is fairly straightforward:

.container::before {
  animation-range: 1em 2em;
}

The bottom is a little tricker. It should start when there are 2em of scrolling and then only travel for 1em. We can simply reverse the animation and add a little calculation to set the range based on it’s bottom edge.

.container::after {
  animation-direction: reverse;
  animation-range: calc(100% - 2em) calc(100% - 1em);
}

Still one more thing. We only want the shadows to reveal when we’re in a scroll container. If, for example, the box is taller than the content, there is no scrolling, yet we get both shadows.

Shadows on the top and bottom edges of the content, but the content is shorter than the box height, resulting in the shadow being in the middle of the box.

This is where the conditional part comes in. We can detect whether an element is scrollable and react to it. Bramus is talking about an animation keyword that’s new to me: detect-scroll.

@keyframes detect-scroll {
  from,
  to {
     --can-scroll: ; /* value is a single space and acts as boolean */
  }
}

.container {
  animation: detect-scroll;
  animation-timeline: --scroll-timeline;
  animation-fill-mode: none;
}

Gonna have to wrap my head around this… but the general idea is that --can-scroll is a boolean value we can use to set visibility on the pseudos:

.content::before,
.content::after {
    --vis-if-can-scroll: var(--can-scroll) visible;
    --vis-if-cant-scroll: hidden;

  visibility: var(--vis-if-can-scroll, var(--vis-if-cant-scroll));
}

Bramus points to this CSS-Tricks article for more on the conditional toggle stuff.

Animate Elements in Different Directions

This should be fun! Let’s say we have a set of columns:

<div class="columns">
  <div class="column reverse">...</div>
  <div class="column">...</div>
  <div class="column reverse">...</div>
</div>

The goal is getting the two outer reverse columns to scroll in the opposite direction as the inner column scrolls in the other direction. Classic JavaScript territory!

The columns are set up in a grid container. The columns flex in the column direction.

/* run if the browser supports it */
@supports (animation-timeline: scroll()) {

  .column-reverse {
    transform: translateY(calc(-100% + 100vh));
    flex-direction: column-reverse; /* flows in reverse order */
  }

  .columns {
    overflow-y: clip; /* not a scroll container! */
  }

}
The bottom edge of the outer columns are aligned with the top edge of the viewport.

First, the outer columns are pushed all the way up so the bottom edges are aligned with the viewport’s top edge. Then, on scroll, the outer columns slide down until their top edges re aligned with the viewport’s bottom edge.

The CSS animation:

@keyframes adjust-position {
  from /* the top */ {
    transform: translateY(calc(-100% + 100vh));
  }
  to /* the bottom */ {
    transform: translateY(calc(100% - 100vh));
  }
}

.column-reverse {
  animation: adjust-position linear forwards;
  animation-timeline: scroll(root block); /* viewport in block direction */
}

The approach is similar in JavaScript:

const timeline = new ScrollTimeline({
  source: document.documentElement,
});

document.querySelectorAll(".column-reverse").forEach($column) => {
  $column.animate(
    {
      transform: [
        "translateY(calc(-100% + 100vh))",
        "translateY(calc(100% - 100vh))"
      ]
    },
    {
      fill: "both",
      timeline,
    }
  );
}

Animate 3D Models and More on Scroll

This one’s working with a custom element for a 3D model:

<model-viewer alt="Robot" src="robot.glb"></model-viewer>

First, the scroll-driven animation. We’re attaching an animation to the component but not defining the keyframes just yet.

@keyframes foo {

}

model-viewer {
  animation: foo linear both;
  animation-timeline: scroll(block root); /* root scroller in block direction */
}

There’s some JavaScript for the full rotation and orientation:

// Bramus made a little helper for handling the requested animation frames
import { trackProgress } from "https://esm.sh/@bramus/sda-utilities";

// Select the component
const $model = document.QuerySelector("model-viewer");
// Animation begins with the first iteration
const animation = $model.getAnimations()[0];

// Variable to get the animation's timing info
let progress = animation.effect.getComputedTiming().progress * 1;
// If when finished, $progress = 1
if (animation.playState === "finished") progress = 1;
progress = Math.max(0.0, Math.min(1.0, progress)).toFixed(2);

// Convert this to degrees
$model.orientation = `0deg 0deg $(progress * -360)deg`;

We’re using the effect to get the animation’s progress rather than the current timed spot. The current time value is always measured relative to the full range, so we need the effect to get the progress based on the applied animation.

Scroll Velocity Detection

The video description is helpful:

Bramus goes full experimental and uses Scroll-Driven Animations to detect the active scroll speed and the directionality of scroll. Detecting this allows you to style an element based on whether the user is scrolling (or not scrolling), the direction they are scrolling in, and the speed they are scrolling with … and this all using only CSS.

First off, this is a hack. What we’re looking at is expermental and not very performant. We want to detect the animations’s velocity and direction. We start with two custom properties.

@keyframes adjust-pos {
  from {
    --scroll-position: 0;
    --scroll-position-delayed: 0;
  }
  to {
    --scroll-position: 1;
    --scroll-position-delayed: 1;
  }
}

:root {
  animation: adjust-pos linear both;
  animation-timeline: scroll(root);
}

Let’s register those custom properties so we can interpolate the values:

@property --scroll-position {
  syntax: "<number>";
  inherits: true;
  initial-value: 0;
}

@property --scroll-position-delayed {
  syntax: "<number>";
  inherits: true;
  initial-value: 0;
}

As we scroll, those values change. If we add a little delay, then we can stagger things a bit:

:root {
  animation: adjust-pos linear both;
  animation-timeline: scroll(root);
}

body {
  transition: --scroll-position-delayed 0.15s linear;
}

The fact that we’re applying this to the body is part of the trick because it depends on the parent-child relationship between html and body. The parent element updates the values immediately while the child lags behind just a tad. The evaluate to the same value, but one is slower to start.

We can use the difference between the two values as they are staggered to get the velocity.

:root {
  animation: adjust-pos linear both;
  animation-timeline: scroll(root);
}

body {
  transition: --scroll-position-delayed 0.15s linear;
  --scroll-velocity: calc(
    var(--scroll-position) - var(--scroll-position-delayed)
  );
}

Clever! If --scroll-velocity is equal to 0, then we know that the user is not scrolling because the two values are in sync. A positive number indicates the scroll direction is down, while a negative number indicates scrolling up,.

Showing values for the scroll position, the delayed position, and the velocity when scrolling occurs.

There’s a little discrepancy when scrolling abruptly changes direction. We can fix this by tighening the transition delay of --scroll-position-delayed but then we’re increasing the velocity. We might need a multiplier to further correct that… that’s why this is a hack. But now we have a way to sniff the scrolling speed and direction!

Here’s the hack using math functions:

body {
  transition: --scroll-position-delayed 0.15s linear;
  --scroll-velocity: calc(
    var(--scroll-position) - var(--scroll-position-delayed)
  );
  --scroll-direction: sign(var(--scroll-velocity));
  --scroll-speed: abs(var(--scroll-velocity));
}

This is a little funny because I’m seeing that Chrome does not yet support sign() or abs(), at least at the time I’m watching this. Gotta enable chrome://flags. There’s a polyfill for the math brought to you by Ana Tudor right here on CSS-Tricks.

Showing values for the scroll position, the delayed position, the velocity, the scroll direction, and the scroll speed when scrolling occurs.

So, now we could theoretically do something like skew an element by a certain amount or give it a certain level of background color saturation depending on the scroll speed.

.box {
  transform: skew(calc(var(--scroll-velocity) * -25deg));
  transition: background 0.15s ease;
  background: hsl(
    calc(0deg + (145deg * var(--scroll-direction))) 50 % 50%
  );
}

We could do all this with style queries should we want to:

@container style(--scroll-direction: 0) { /* idle */
  .slider-item {
    background: crimson;
  }
}
@container style(--scroll-direction: 1) { /* scrolling down */
  .slider-item {
    background: forestgreen;
  }
}
@container style(--scroll-direction: -1) { /* scrolling down */
  .slider-item {
    background: lightskyblue;
  }
}

Custom properties, scroll-driven animations, and style queries — all in one demo! These are wild times for CSS, tell ya what.

Outro

The tenth and final video! Just a summary of the series, so no new notes here. But here’s a great demo to cap it off.

CodePen Embed Fallback

Unleash the Power of Scroll-Driven Animations originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

Categories: Designing, Others Tags: