New Dec 18, 2024

The Future of CSS: Construct and values with ident()

More Front-end Bloggers All from Bram.us View The Future of CSS: Construct and values with ident() on bram.us

CSS uses a lot of so called idents to name things – think of the values that you put in for view-transition-name, view-timeline-name, container-name, etc.

Constructing these unique values for a lot of elements in one go is often a lot of repetitive work, in which you find yourself repeating selectors and *-name declarations for each and every element. Got 100 elements? That’s 100 declarations (as part of 100 rules) please. Ugh.

Hot off the press is a new CSS Working Group resolution to adopt a solution I proposed to solving this problem: the ident() function.

~

⚠️ This post is about a future CSS feature. You can’t use it … yet.

This feature only has a resolution saying that it should be part of the css-values-5 specification. It still needs to be formally specified and does not exist in any browser yet. This post is more of an explainer, outlining the problem space + solution, to show you what you will (~ should) be able to do with in the future.

~

Table of Contents

~

# The Problem

In many CSS features, you need to give elements a certain name so that you can later refer to those elements. Think of container-name, view-transition-name, view-timeline-name, scroll-timeline-name, etc.

Depending on the property these names are a <custom-ident> or a <dashed-ident>. These names need to be unique (within the feature that’s being used). In case of View Transitions and Scroll-Driven Animations this uniqueness can become burden for authors.

Take this Scroll-Driven Animations example, where I set a unique view-timeline-name – a <dashed-ident< for the occasion – per targeted element and also refer to that timeline later on:

.parent {
  timeline-scope: all;
}

main { div:nth-child(1) { view-timeline-name: --tl-1; } div:nth-child(2) { view-timeline-name: --tl-2; } div:nth-child(3) { view-timeline-name: --tl-3; } }

nav { li:nth-child(1) { animation-timeline: --tl-1; } li:nth-child(2) { animation-timeline: --tl-2; } li:nth-child(3) { animation-timeline: --tl-3; } }

Same with View Transitions, where I’ve seen this code in the wild:

&:nth-child(1) {
  view-transition-name: opt-1;
  & > label {
    view-transition-name: opt-1-label;
  }
  & > input {
    view-transition-name: opt-1-input;
  }
}
&:nth-child(2) {
  view-transition-name: opt-2;
  & > label {
    view-transition-name: opt-2-label;
  }
  & > input {
    view-transition-name: opt-2-input;
  }
}
&:nth-child(3) {
  view-transition-name: opt-3;
  & > label {
    view-transition-name: opt-3-label;
  }
  & > input {
    view-transition-name: opt-3-input;
  }
}

While both examples are limited to only 3 items, you can easily tell that this becomes a burden when there are more items at play.

~

# The Solution: ident()

To make things easier when it comes to naming elements en masse, I proposed the ident() function to the CSS Working Group. It’s a function to dynamically construct <custom-ident> and <dashed-ident> values.

<ident-fn> = ident(<ident-args>#);
<ident-args> = [<string> | <integer> | <ident>]+

The function accepts an arbitrary number of space-separated arguments, but needs at least 1. The arguments are of the type <string>, <integer> or another <ident>. The result of the function is an <ident> that consists of the passed in arguments concatenated together.

For example, with sibling-index(), the first example shared earlier can be rewritten as follows:

.parent {
  timeline-scope: all;
}

nav li { animation-timeline: ident("--tl-" sibling-index()); /* --tl-1, --tl-2, … */ }

main div { view-timeline-name: ident("--tl-" sibling-index()); /* --tl-1, --tl-2, … */ }

This code auto-scales, regardless of how many elements are being used in the markup.

~

# More Examples

The ident() function shines when you combine it with other functions/features. I’ve already mentioned sibling-index() and another such one is the attr() function which you can use to get values of HTML attributes into CSS.

For example:

.item { 
  view-timeline-name: ident("--item-" attr(id) "-tl");
}

label { animation-name: ident("--item-" attr(for) "-tl"); }

With .item elements that have ids like beach, house, and bike; the resulting view-timeline-names would be --item-beach-tl, --item-house-tl, and --item-bike-tl respectively.

Or here’s a more advanced example that first reads the id attribute from a .card and stores it into a custom property. Because custom properties compute before they inherit, the children can refer to that custom property just fine.

.card[data-view-transition-id] {
  --id: attr(data-view-transition-id); /* E.g. card1, card2, card3, … */

view-transition-name: var(--id); view-transition-class: card;

h1 { view-transition-name: ident(var(--id) "-title"); /* E.g. card1-title, card2-title, card3-title, … */ view-transition-class: card-title; }

.content { view-transition-name: ident(var(--id) "-content"); /* E.g. card1-content, card2-content, card3-content, … */ view-transition-class: card-content; } }

~

# FAQ

As part of the discussion at the CSS WG today, I prepared a short list of Frequently Asked Questions:

# Why do we need a function? Can’t one directly glue things together, e.g. view-transition-name: var(--id) "title";?

Parsing purposes. Think of shorthands, such as scroll-timeline, where it would be hard to detect the longhands it consists of.

Without ident() it’s not clear which parts make up the ident:

With ident() it’s clear what goes together:

~

# Why not use the redesigned attr() for this, which can parse to idents? E.g. attr(foo type(<custom-ident>))?

The ident() function goes beyond atrr() as it allows you to glue pieces of string together, e.g. ident("view-" attr(data-vt-id)) or ident("view-" attr(data-type) "-" attr(data-sequence)).

The pieces that need to be glued can come from other elements as well, by storing those into custom properties.

~

# What about constructing <dashed-ident>s?

Prepend "--" at the start, e.g. ident("--item-tl-" attr(data-itemnum))

~

# Doesn’t the attr() function need type(<custom-ident>) added to it (which now makes your examples look shorter)?

No, when no <attr-type> is given, the <attr-name> gets parsed to a CSS string (see spec). Because ident() is designed to accept <string> values as well, it works just fine.

~

# Why not rely on something like …-name: auto to auto-generate idents?

The problem with …-name: auto solutions is that:

The proposed ident() does not have these limitations; It transcends per-feature solutions like …-name: auto.

~

# Browser Support

đź’ˇ Although this post was originally published in December 2024, the list below is constantly being updated. Last update: Dec 18, 2024.

This feature is not supported in any browser. To follow along with the progress – if any – you can follow these browser issues:

Chromium (Blink)

❌ No Support

CrBug #384930424

Firefox (Gecko)

❌ No Support

(automatic issue creation is pending)

Safari (WebKit)

❌ No Support

Issue #284895

Don’t expect any movement on these soon, though. This feature came into existence just today and still needs to be formally specified (as part of css-values-5). This can take a long time.

The embed below will color green when ident() support is enabled in your browser.

See the Pen CSS ident() Support test by Bramus (@bramus) on CodePen.

 

~

# Spread the word

Feel free to repost one of my posts on social media to give them more reach, or link to the blogpost yourself one way or another 🙂

~

Scroll to top