anchor positioning – CSS-Tricks https://css-tricks.com Tips, Tricks, and Techniques on using Cascading Style Sheets. Mon, 05 Aug 2024 16:58:40 +0000 en-US hourly 1 https://wordpress.org/?v=6.6.1 https://i0.wp.com/css-tricks.com/wp-content/uploads/2021/07/star.png?fit=32%2C32&ssl=1 anchor positioning – CSS-Tricks https://css-tricks.com 32 32 45537868 inset-area https://css-tricks.com/almanac/properties/i/inset-area/ Mon, 05 Aug 2024 16:48:42 +0000 https://css-tricks.com/?page_id=379453 The inset-area property creates an imaginary 3×3 grid surrounding an anchor element by declaring rows and columns, and uses it to set an anchor-positioned (or “target”) element in one region of the grid or span multiple regions of the grid.…


inset-area originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
The inset-area property creates an imaginary 3×3 grid surrounding an anchor element by declaring rows and columns, and uses it to set an anchor-positioned (or “target”) element in one region of the grid or span multiple regions of the grid.

.target {
  position: absolute;
  position-anchor: --my-anchor;
  inset-area: bottom end;
}

For most use cases, like creating a tooltip, using the inset-area property to place anchor-positioned elements will be enough. If you need more fine-grained control, you can use the anchor() function for additional ways to position the anchor-positioned element.

The inset-area property won’t have any effect on a common element. That’s because it is part of the CSS Anchor Positioning module, a set of many features that work together to position an element that we call a “target” to another element we call an “anchor”.

Note: Per spec, the inset-area property is now named position-area, but browsers that have shipped Position Anchor (Chromium so far) continue to use inset-area, so be wary when it may change!

Syntax

inset-area: auto | <inset-area>
  • Initial value: none
  • Applies to: positioned elements with a default anchor element
  • Inherited: No
  • Percentages: N/A
  • Computed value: As specified
  • Canonical order: Per grammar
  • Animation: TBD (not yet specced)

Values

/* Default value */
inset-area: none;

/* Logical values */
inset-area: end start;
inset-area: block-start inline-end;
inset-area: self-block-start self-inline-end;

/* Physical Values */
inset-area: top center;
inset-area: bottom left;
inset-area: x-start y-end;

/* Span Values */
inset-area: center span-left; /* covers anchor */
inset-area: span-bottom right;
inset-area: top span-all;

/* Single Values */
inset-area: center;
inset-area: left;

/* Global values */
inset-area: inherit;
inset-area: initial;
inset-area: revert;
inset-area: revert-layer;
inset-area: unset;
  • none: The .target element doesn’t move and is not positioned in relation to an anchor element.
  • <inset-area>: Places the .target on the regions of the grid selected by the rows and columns. If the target doesn’t have a matching positon-anchor/anchor-name relation with any anchor, then this value doesn’t have any effect.

Understanding what an inset-area is

As mentioned earlier, the inset-area property creates an imaginary 3×3 grid that we can use to position target elements along the edges of anchor elements.

Three-by-three grid with a yellow element in the center tile labeled 'anchor'.

As useful as this representation is for understanding the concept of anchor positioning, it isn’t an exact representation of how the grid looks. In reality, the grid isn’t symmetrical at all, but rather contained by four imaginary lines on each axis, which includes:

  1. The start of the target’s containing block.
  2. The start of the anchor element or anchor(start).
  3. The end of the anchor element or anchor(end)
  4. The end of the target’s containing block.

Since target elements must be absolutely positioned (i.e. position: absolute), their containing block is the viewport making the default grid look more like this

Asymmetrical three-by-three grid spanning all viewport with a yellow element in the center tile labeled 'anchor'.

Now that we know what our grid really looks like, and we have mastered linking elements with matching anchor-name/position-anchor properties, the next step is moving your target around your anchor. One way is to mingle with the target’s inset properties (top, right, bottom, left, etc) but sometimes we want to place it on the “anchor’s top left corner” or “right next to the anchor” without resorting to inset properties.

The inset-area property takes that weight off you and positions the target on the specified grid region. It works by setting the row and column of the grid using logical values like start and end (dependent on the writing mode); physical values like top, left, right, bottom and the center shared value.

Three-by-three grid with a yellow element in the center tile labeled 'anchor' and a legend for each tile

So if we want to place our target, say in the top right corner, we could write inset-area: top right or inset-area: start end on an LTR containing block.

.target {
	position: absolute;
	position-anchor: --my-anchor;

	inset-area: top right;
	/* or */
	inset-area: start end;
}

To place a target across two adjacent grid regions, we can use the prefix span- on any value (that isn’t center) a row or column at a time.

.target {
	position: absolute;
	position-anchor: --my-anchor;

	inset-area: span-top left;
	/* or */
	inset-area: span-start left;
}

Finally, we can span across three adjacent grid regions using the span-all value.

.target {
	position: absolute;
	position-anchor: --my-anchor;

	inset-area: bottom span-all;
	/* or */
	inset-area: end span-all;
}

The order of values doesn’t matter… unless we’re working with logical properties

There are more values than meets the eye. For example, logical values refer to the target’s containing block writing mode, so a self- prefix is needed to pick the target’s own writing mode.

.target {
  position: absolute;
  position-anchor: --my-anchor;

  inset-area: self-end self-start;
}

You may have also noticed that the inset-area property doesn’t have a strict order for physical values. So, we’re technically able to swap those values around because it all means the same thing. For example:

.target {
  inset-area: top left;
  /* same as: */
  inset-area: left top
}

But wait! The order is extremely important if we’re working instead with logical property values. That’s because the direction of logical property values is relative to a person’s writing mode (e.g. right-to-left).

.target {
  inset-area: start end; /* equates to "top" and "right" in LTR writing mode */
  /* is opposite of: */
  inset-area: end start /* equates to "bottom" and "left" in RTL writing mode */
}

This is because physical values aren’t ambiguous on which axis they refer: top will always refer to the grid’s “top row”, but logical values are ambiguous and depend on the context of the browser’s writing mode, so the first logical value sets the row, and the second value sets the column.

Using “ambiguous” and “explicit” logical values

The values are interchangeable so long as we’re explicit about which direction the values refer to by prepending inline- or block- to the value:

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

  inset-area: inline-end block-start;
  /* or */
  inset-area: block-start inline-end;
}

CSS knows which direction each value is going because we were very explicit about it: This one goes in the inline direction, and this other one goes in the block direction. So the order doesn’t matter to CSS at all because it’s the same either way!

But what if our values are less declarative and instead are more ambiguous about which direction they refer to? An “ambiguous” value would be start or end because they do not explicitly say whether it’s talking about the inline or block direction. So, for example, if we have mixed ambiguous and explicit values like this:

.target {
  inset-area: block-end start;
}

…CSS will evaluate the ambiguous start value as inline-start because the block direction has already been accounted for with the explicit block-end value.

And if we were to declare two ambiguous logical values:

.target {
  inset-area: start start;
}

…then CSS will set the first value in the block direction, and the second value in the inline direction, or:

.target {
  inset-area: block-start inline-start;
}

A single value implies the span-all keyword

But let’s say we only provide one value and it’s an explicit one, say top:

.target {
  inset-area: top;
}

CSS will position the .target element along the anchor element’s top edge, but span it across all of the columns to take up the full space. So, in effect, declaring a single explicit top value implicitly sets the span-all keyword on the element, which means it is the same as writing:

.target {
  inset-area: top span-all;
}

Demo

Specification

The position-anchor property is defined in the CSS Anchor Positioning Module Level 1 specification, which is currently in Working Draft status at the time of writing. That means a lot can change between now and when the feature becomes a formal Candidate Recommendation for implementation.

Browser support

Data on support for the css-anchor-positioning feature across the major browsers from caniuse.com

More information and tutorials


inset-area originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
379453
position-anchor https://css-tricks.com/almanac/properties/p/position-anchor/ Mon, 05 Aug 2024 15:09:35 +0000 https://css-tricks.com/?page_id=379433 The position-anchor property links an absolutely positioned element to an “anchor” element. This will define the default anchor and is used for the several anchor properties and functions on the “target” element.

In other words, the property positions (or anchors


position-anchor originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
The position-anchor property links an absolutely positioned element to an “anchor” element. This will define the default anchor and is used for the several anchor properties and functions on the “target” element.

In other words, the property positions (or anchors) an element to another element that we set as the property’s value

.target {
  position-anchor: --my-anchor
}

By itself, this example does not do anything other than tell the .target element that it’s positioned (or anchored) to some other element that is represented by a dashed ident called --my-anchor. The problem is that we haven’t actually defined what --my-anchor actually is.

We’ll get to a fully working example after we get some more context about the property, including its syntax and accepted values.

Syntax

position-anchor: auto | <anchor-element>;
  • Initial value: implicit
  • Applies to: Absolutely positioned elements
  • Inherited: No
  • Percentages: N/A
  • Computed value: As specified
  • Canonical order: Per grammar
  • Animation: Discrete

Values

/* Keyword values */
position-anchor: auto;

/* <dashed-ident> examples */
position-anchor: --anchor;
position-anchor: --my-anchor;
position-anchor: --myAnchor;

/* Global values */
position-anchor: inherit;
position-anchor: initial;
position-anchor: revert;
position-anchor: revert-layer;
position-anchor: unset;
  • auto: Links a target element to its implicit anchor. An implicit anchor can be found using the non-standard anchor global attribute or using the Popover API.
  • <dashed-ident>: The name of the desired anchor element. The value is called a “dashed” ident because it must be prefixed with two dashes (–) exactly like we do for CSS custom properties, e.g. –my-anchor.

Basic usage

In the first example, using position-anchor alone won’t have any effect. We’ve told our .target element that it is anchored to some other element we’re calling --my-anchor. But what element is --my-anchor representing? We need that, or else we’re not anchored to anything that actually exists.

The easiest way to define an element as an “anchor” for other elements is to slap the anchor-name property on the element we want to be the anchor.

Say we have two unrelated elements in HTML:

<div class="anchor">My Anchor</div>
<div class="target">My Target</div>

We want the .anchor element to be the anchor, and then position the .target element’s position based on the .anchor element’s position.

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

.target {
  position: absolute; /* or position: fixed; */
  position-anchor: --my-anchor;
}

That’s it! There is now an association between the .anchor and .target elements. When we change the .target element’s position, it will use the .anchor element’s position as its starting basis.

But again, we don’t see much happening in this example. That’s because we haven’t changed the .target element’s position to see that relationship in action. So, let’s turn our attention to two different approaches for updating that element’s position: using the anchor() function along inset properties or using the inset-area property.

Method 1: anchor() function and inset properties

The .target element being absolutely positioned means it can be moved around using inset properties (top, right, bottom, left). It would be ideal to position our .target next to the .anchor, but how can we know where the .anchor is to take advantage of inset properties? The anchor function does exactly that. It takes one side of the anchor and resolves to the <length> of where it is positioned.

Now we can attach one side of the .target to another side of the .anchor.

.target {
  position: absolute;
  position-anchor: --my-anchor;

  top: anchor(bottom);
}

This is basically saying, “Stitch the .target top side to the .anchor element’s bottom edge.” Pictures are worth a thousand words:

Two boxes stacked vertically, the top one labeled anchor and the bottom one labeled target.

The anchor() function takes the default anchor linked to the target element, but we can also directly reference an anchor-name if we’re being more explicit about what we’re anchoring to.

.target {
  position: absolute;

  top: anchor(--my-anchor bottom);
}

…or even reference other anchors that are on the page! For example, we can tell the .target element to anchor itself to the --my-anchor element, but we can still position it based on the position of any other anchor on the page simply by calling that in the anchor() function:

.target {
  position: absolute;
  position-anchor: --my-anchor;

  top: anchor(--my-other-anchor bottom);
}

Method 2: inset-area property

Perhaps an “easier” way to re-position an anchor is using the inset-area property. You can think of an imaginary 3×3 grid surrounding the .anchor element:

Three by three grid with a yellow element in the center tile labeled 'anchor'.

We then position the .target element it is anchored to on the grid by column and row, using:

  • physical values like left, right, top and bottom,
  • logical values that respect the current writing mode, like start and end), and
  • the center shared value.

For example, if we were to span the .target element across two tiles on our imaginary grid, we can use the span- prefix on any value, or even span the .target element across the entire grid using the span-all value.

.target {
  position: absolute;
  position-anchor: --my-anchor;

  inset-area: start center,
  /* or */
  inset-area: bottom left,
  /* or */
  inset-area: span-top right,
}

Demo

Specification

The position-anchor property is defined in the CSS Anchor Positioning Module Level 1 specification, which is currently in Working Draft status at the time of writing. That means a lot can change between now and when the feature becomes a formal Candidate Recommendation for implementation, so be careful about using the property on a live website until the specification is adopted by the W3C and implemented by browsers.

Browser support

Data on support for the css-anchor-positioning feature across the major browsers from caniuse.com

Dealing with legacy browser support

Anchor positioning is a relatively new CSS module that doesn’t have full browser support and may have issues with older versions. Its most common use case will be for better tooltips, so a polyfill for a simple tooltip element can be found below. It checks if anchor positioning is supported and creates an old-school tooltip below our anchor element.

@supports not (position-anchor: --my-anchor) {
  .target {
    display: none;
  }

  .anchor {
    position: relative;
    display: flex;
    justify-content: center;
  }

  .anchor::before {
    content: "My Target";
    position: absolute;
    top: 100%;
  }
}

More information and tutorials


position-anchor originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
379433
CSS Stuff I’m Excited After the Last CSSWG Meeting https://css-tricks.com/css-stuff-im-excited-after-the-last-csswg-meeting/ https://css-tricks.com/css-stuff-im-excited-after-the-last-csswg-meeting/#comments Fri, 19 Jul 2024 17:16:57 +0000 https://css-tricks.com/?p=379180 From June 11–13, the CSS Working Group (CSSWG) held its second face-to-face meeting of the year in Coruña, Spain, with a long agenda of new features and improvements coming to language. If 2023 brought us incredible advances like …


CSS Stuff I’m Excited After the Last CSSWG Meeting originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
From , the CSS Working Group (CSSWG) held its second face-to-face meeting of the year in Coruña, Spain, with a long agenda of new features and improvements coming to language. If 2023 brought us incredible advances like out-of-the-box nesting, container and style queries, or the has: selector, then 2024 is going to be even more packed with even more ground-breaking additions. Whether a new feature like inline conditionals is just starting or long-term projects are wrapping up, 2024 is already filled with exciting developments — and we’re still in July!

I wanted to share what I think are some of the most interesting and significant features coming to CSS that were examined in the meeting. However, I don’t want you to take the following as an exact recap of the discussions. Instead, I want to bring up the broader topics coming to CSS that had a spotlight at the last meeting. In reality, the features examined have been cooking up for even years and the discussions are geared towards specific cases and new enhancements, rather than defining a whole specification; a work that would be impossible in one meeting.

You can see the exact issues discussed on the CSSWG meeting agenda.

Feature 1: What if we get if()?

Since CSS custom properties gained reliable support around 2016, there have been many attempts to apply certain styles depending on a custom property value without, of course, appealing to JavaScript. One of the earliest workarounds for conditional styles was posted by Roman Komarov back in 2016 in “Conditions for CSS Variables”. From there, many other hacks have been documented for making conditional declarations in CSS (including this extremely clever one by Ana Tudor here on CSS-Tricks). In fact, you can find a full list that discusses and compares those workarounds by CSSWG member Lea Verou in her recent article, “Inline conditionals in CSS, now?”.

What’s for sure is that the community has craved a conditional way to apply styles using custom properties. Nowadays, we have a specification for Style Queries that’s capable of the task, but they come with limitations not related to browser support. The biggest of those limitations? We can’t directly style the container that’s queried, so we need some sort of wrapper element around that wrapper in HTML.

<div class="news-container" style="--variant: info">
  <p>Here is some good <strong>news</strong></p>
</div>

…in addition to writing the style query:

.news-container {
  container-name: news-container;
}

@container news-container style(--variant: info) {
  p {
    color: blue;
    border: 1px solid blue;
  }
}

What if() might look like

On the CSSWG side, there have been discussions about adding an if() function as far back as 2018. It was of this year — yes, six years later — that the CSSWG resolved to begin working on if() for CSS. As good as it may look, don’t expect to see if() in a browser in at least two years! (That’s Lea’s unofficial estimate.) We’ll likely need to wait even longer for enough browser support to begin using it reliably in production. The spec draft is only barely getting started and many things have to pass a test first. For context, the CSS variables working draft began in 2012 and only received wide browser support in 2016.

Syntax-wise, if() is probably going to borrow the ternary operator from JavaScript and other programming languages, structured like this:

if(a ? b : c)

…where a is the custom property we are checking and b are c are the possible conditional return values. To check for styles, an inline style(--my-property: value) would be used.

.forecast {
  background-color: if(style(--weather: clouds) ? var(--clouds-color): var(--default-color));
}

Even if ? isn’t used in CSS and : has a different meaning everywhere else, I think this syntax is the one most people are familiar with, not to mention it also allows seamless conditional chaining.

.forecast {
  background-color: if(
    style(--weather: clouds) ? var(--clouds-color): 
    style(--weather: sunny) ? var(--sunny-color);
    style( --weather: rain) ? var(--rain-color): var(--default-color)
  );
}

Future if() improvements

Although these probably won’t make it in the initial release, it’s interesting to see how if() might change between now and sometime further in the future:

  • Support for other inline conditionals. We are supposed to check for custom properties using the style() query, but we may as well check for media features with an inline media() query or if a user agent supports a specific property with an inline support().
.my-element {
  width: if(media(width > 1200px) ? var(--size-l): var(--size-m));
}
  • Using conditional inside other CSS functions. In future drafts, we may use ternaries inside other functions without having to wrap them around if(), e.g. just as we can make calculations without calc() if we are inside a clamp() or round() function.

Feature 2: Cross-document view transitions

Last year, the View Transition API gave us the power to create seamless transitions when navigating between web pages and states. No components or frameworks, no animation libraries — just vanilla HTML and CSS with a light sprinkle of JavaScript. The first implementation of View Transitions was baked into browsers a while back, but it was based on an experimental function defined by Chrome and was limited to transitions between two states (single-page view transitions) without support for transitioning between different pages (i.e., multi-page view transitions), which is what most of us developers are clamoring for. The possibilities for mimicking the behavior of native apps are exciting!

That’s why the CSS View Transitions Module Level 2 is so amazing and why it’s my favorite of all the CSS additions we’re covering in this article. Yes, the feature brings out-of-the-box seamless transitions between pages, but the real deal is it removes the need for a framework to achieve it. Instead of using a library — say React + some routing library — we can backtrack into plain CSS and JavaScript.

Of course, there are levels of complexity where the View Transition API may fall short, but it’s great for countless cases where we just want page transitions without the performance cost of dropping in a framework.

Opting into view transitions

View transitions are triggered when we navigate between two pages from the same-origin. In this context, navigation might be clicking a link, submitting a form, or going back and forth with browser buttons. By contrast, something like using a search bar between same-origin pages won’t trigger a page transition.

Both pages — the one we’re navigating away from and the one we’re navigating to — need to opt into the transition using the @view-transition at-rule and setting the navigation property to auto

@view-transition {
  navigation: auto;
}

When both pages opt into a transition, the browser takes a “snapshot” of both pages and smoothly fades the “before” page into the “after” page.

Transitioning between “snapshots”

In that video, you can see how the old page fades into the new page, and it works thanks to an entire tree of new pseudo-elements that persist through the transition and use CSS animations to produce the effect. The browser will group snapshots of elements with a unique view-transition-name property that sets a unique identifier on the transition that we can reference, and which is captured in the ::view-transition pseudo-element holding all of the transitions on the page.

You can think of ::view-transition as the :root element for all page transitions, grouping all of the parts of a view transition on the same default animation.

::view-transition
├─ ::view-transition-group(name)
│  └─ ::view-transition-image-pair(name)
│     ├─ ::view-transition-old(name)
│     └─ ::view-transition-new(name)
├─ ::view-transition-group(name)
│  └─ ::view-transition-image-pair(name)
│     ├─ ::view-transition-old(name)
│     └─ ::view-transition-new(name)
└─ /* and so one... */

Notice that each transition lives in a ::view-transition-group that holds a ::view-transition-image-pair that, in turn, consists of the “old” and “new” page snapshots. We can have as many groups in there as we want, and they all contain an image pair with both snapshots.

Quick example: let’s use the ::view-transition “root” as a parameter to select all of the transitions on the page and create a sliding animation between the old and new snapshots.

@keyframes slide-from-right {
  from {
    transform: translateX(100vw);
  }
}

@keyframes slide-to-left {
  to {
    transform: translateX(-100vw);
  }
}

::view-transition-old(root) {
  animation: 300ms ease-in both slide-to-left;
}

::view-transition-new(root) {
  animation: 300ms ease-out both slide-from-right;
}

If we navigate between pages, the entire old page slides out to the left while the entire new page slides in from the right. But we may want to prevent some elements on the page from participating in the transition, where they persist between pages while everything else moves from the “old” snapshot to the “new” one.

That’s where the view-transition-name property is key because we can take snapshots of certain elements and put them in their own ::view-transition-group apart from everything else so that it is treated individually.

nav {
  view-transition-name: navigation;

  /* 
    ::view-transition
    ├─ ::view-transition-group(navigation)
    │  └─ ::view-transition-image-pair(navigation)
    │     ├─ ::view-transition-old(navigation)
    │     └─ ::view-transition-new(navigation)
    └─ other groups...
  */
}

You can find a live demo of it on GitHub. Just note that browser support is limited to Chromium browsers (i.e., Chrome, Edge, Opera) at the time I’m writing this.

There are many things we can look forward to with cross-document view transitions. For example, If we have several elements with a different view-transition-name, we could give them a shared view-transition-class to style their animations in one place — or even customize the view transitions further with JavaScript to check from which URL the page is transitioning and animate accordingly.

Feature 3: Anchor Positioning

Positioning an element relative to another element in CSS seems like one of those no-brainer, straightforward things, but in reality requires mingling with inset properties (top, bottom, left, right) based on a series of magic numbers to get things just right. For example, getting a little tooltip that pops in at the left of an element when hovered might look something like this in HTML:

<p class="text">
  Hover for a surprise
  <span class="tooltip">Surprise! I'm a tooltip</span>
</p>

…and in CSS with current approaches:

.text {
  position: relative;
}


.tooltip {
  position: absolute;
  display: none;

  /* vertical center */
  top: 50%;
  transform: translateY(-50%);

  /* move to the left */
  right: 100%;
  margin-right: 15px; */
}

.text:hover .tooltip {
  display: block;
}

Having to change the element’s positioning and inset values isn’t the end of the world, but it sure feels like there should be an easier way. Besides, the tooltip in that last example is extremely fragile; if the screen is too small or our element is too far to the left, then the tooltip will hide or overflow beyond the edge of the screen.

CSS Anchor Positioning is yet another new feature that was discussed in the CSSWG meetings and it promises to make this sort of thing much, much easier.

Creating an anchor

The basic idea is that we establish two elements:

  • one that acts as an anchor, and
  • one that is a “target” anchored to that element.

This way, we have a more declarative way to associate one element and position it relative to the anchored element.

To begin we need to create our anchor element using a new anchor-name property.

Changing our markup a little:

<p>
  <span class="anchor">Hover for a surprise</span>
  <span class="tooltip">Surprise! I'm a tooltip</span>
</p>

We give it a unique dashed-indent as its value (just like a custom property):

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

Then we relate the .tooltip to the .anchor using the position-anchor property with either fixed or absolute positioning.

.toolip {
  position: fixed;
  position-anchor: --tooltip;
}

The .tooltip is currently positioned on top of the .anchor, but we ought to move it somewhere else to prevent that. The easiest way to move the .tooltip is using a new inset-area property. Let’s imagine that the .anchor is placed in the middle of a 3×3 grid and we can position the tooltip inside the grid by assigning it a row and column.

Three by three grid with yellow element in the center tile labeled 'anchor'.

The inset-area property takes two values for the .tooltip‘s in a specific row and column on the grid. It counts with physical values, like left, right, top and bottom, as well logical values depending on the user’s writing mode, like start and end, in addition to a center shared value. It also accepts values referencing x- and y-coordinates, like x-start and y-end. All these value types are ways of representing a space on the 3×3 grid.

For example, if we want the .tooltip to be positioned relative to the top-right edge of the anchor, we can set the inset-area property like this:

.toolip {
  /* physical values */
  inset-area: top right;

  /* logical values */
  inset-area: start end;

  /* mix-n-match values! */
  inset-area: top end;
}

Lastly, if we want our tooltip to span across two regions of the grid, we can use a span- prefix. For example, span-top will place the .tooltip in the grid’s top and center regions. If instead we want to span across an entire direction, we can use the span-all value.

Three by three grid with a yellow element in the center labeled 'anchor' surrounded by three tooltip examples demonstrating how tooltips are placed on the grid, including code examples for each example.

One of the problems with our anchor-less example is that the tooltip can overflow outside the screen. We can solve this using another new property, this time called position-try-options, in combination with a new inset-area() function.

(Yes, there is inset-area the property and inset-area() the function. That’s one we’ll have to commit to memory!)

The position-try-options property accepts a comma-separated list of fallback positions for the .tooltip when it overflows outside the screen. We can provide a list of inset-area() functions, each holding the same values that the inset-area property would. Now, each time the tooltip goes out off-screen, the next declared position is “tried”, and if that position causes an overflow, the next declared position is tried, and so on.

.toolip {
  inset-area: top left;
  position-try-options: inset-area(top), inset-area(top right);
}

This is a pretty wild concept that will take some time to grok. CSSWG member Miriam Suzanne sat down to discuss and tinker with anchor positioning with James Stuckey Weber in a video that’s well worth watching.

Geoff Graham took notes on the video if you’re looking for a TL;DW.

There are still many aspects to anchor positioning we aren’t covering here for brevity, notably the new anchor() function and @try-position at-rule. The anchor() function returns the computed position of the edge of an anchor, which provides more control over a tooltip’s inset properties. The @try-position at-rule is for defining custom positions to set on the position-try-options property.

My hunch is that using inset-area will be plenty robust for the vast majority of use cases.

The CSSWG is a collective effort

Earlier I said that this article wouldn’t be an exact retelling of the discussions that took place at the CSSWG meetings, but rather a broad representation of new specs coming to CSS that, due to their novelty, were bound to come up in those meetings. There are even some features that we simply hadn’t the time to review in this roundup that are still subject to debate (cough, masonry).

One thing is for sure: specs aren’t made in some vacuum over one or two meetings; it takes the joined effort of tens of amazing authors, developers, and user agents to bring to life what we use every day in our CSS work — not to mention the things we will use in the future.

I also had the opportunity to talk with some amazing developers from the CSSWG, and I found it interesting what their biggest takeaways were from the meetings. You might expect if() is at the top of their lists since that’s what is buzzing in socials. But CSSWG member Emilio Cobos told me, for example, that the letter-spacing property is essentially flawed and there isn’t a simple solution for fixing it that’s copasetic with how letter-spacing is currently defined by CSS and used in browsers. That includes the fact that converting normal properties into shorthand properties can be dangerous to a codebase.

Every tiny detail we might think of as trivial is carefully analyzed for the sake of the web and for the love of it. And, like I mentioned earlier, this stuff is not happening in a closed vacuum. If you’re at all interested in the future of CSS — whether that simply keeping up with it or getting actively involved — then consider any of the following resources.


CSS Stuff I’m Excited After the Last CSSWG Meeting originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/css-stuff-im-excited-after-the-last-csswg-meeting/feed/ 4 https://www.youtube.com/embed/76hIB2L_vs4 CSS Anchor Positioning in Practice - Winging It Live nonadult 379180
anchor-name https://css-tricks.com/almanac/properties/a/anchor-name/ Fri, 31 May 2024 19:29:11 +0000 https://css-tricks.com/?page_id=378430 Declaring the CSS anchor-name property on an element registers it as an “anchor” that we can use to position as a reference for positioning other elements.

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

The property is one part of CSS Anchor Positioning


anchor-name originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
Declaring the CSS anchor-name property on an element registers it as an “anchor” that we can use to position as a reference for positioning other elements.

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

The property is one part of CSS Anchor Positioning, a set of features for linking the position of one element to another that are intended to be used together.

Syntax

anchor-name: none | <dashed-ident>#
  • Initial value: none
  • Applies to: All elements that generate a principal box
  • Inherited: No
  • Percentages: N/A
  • Computed value: As provided
  • Canonical order: Per grammar
  • Animation: Discrete

Property values

/* Keyword values */
anchor-name: none;

/* <dashed-ident> examples */
anchor-name: --anchor;
anchor-name: --my-anchor;
anchor-name: --myAnchor;
  • none: Removes anchor positioning from the element it is applied to. In other words, other elements are unable to be positioned in relation to it.
  • <dashed-ident>: A custom name given to an element intended to be used as an anchor, like a <custom-ident>, that declaratively identifies it and distinguishes it from other anchors. The value is called a “dashed” ident because it must be prefixed with two dashes (--) exactly like we do for CSS custom properties, e.g. --my-anchor.

Basic usage

Once we’ve registered an element as an anchor, we can reference it on another element and position that element relative to the anchor’s position. So, this relates some “target” element we’re selecting to the anchor we just defined:

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

.target {
  position-anchor: --my-anchor;
}

But before we do that, have to give the .target absolute positioning to take it out of the normal document flow:

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

.target {
  position: absolute;
  position-anchor: --my-anchor;
}

With that in place, we can start positioning the .target. And it’s position is now relative to the .anchor element’s flow. So, we could, for example, position the .target relative to the .anchor‘s bottom edge with the anchor() function.

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

.target {
  position: absolute;
  position-anchor: --my-anchor;
  top: anchor(bottom);
}

This is sort of like saying, “I want to position the top of my element along the bottom edge of this other element.” And the result is the .target sitting below the .anchor.

Two boxes stacked vertically, the top one labeled anchor and the bottom one labeled target.

If we were to switch it up to the .anchor‘s top edge instead:

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

.target {
  position: absolute;
  position-anchor: --my-anchor;
  bottom: anchor(top);
}
Two boxes stacked vertically, the top one labeled target and the bottom one labeled anchor.

Referencing anchor names

We just saw the basic usage for how we can register an “anchor” by declaring anchor-name on the element we’re anchoring to, then associate some “target” element with that anchor by referencing the anchor-name on the target with the position-anchor property:

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

.target {
  position-anchor: --my-anchor;
}

Another way to associate the target with the anchor is to reference the anchor-name directly in the anchor() function:

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

.target {
  top: anchor(--my-anchor end);
}

That’s a nice way to reduce the amount of code, as we can reference the anchor and how we want to position the target to it in a single declaration.

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

.target {
  top: anchor(--my-anchor end);

  /* Same as: */
  position-anchor: --my-anchor;
  top: anchor(end);
}

Referencing multiple anchor names

Yes, we can absolutely link one target element to many different anchors.

.target {
  position: absolute;
  top: anchor(--anchor-1 bottom);
  bottom: anchor(--anchor-2 top);
  left: anchor(--anchor-3 right);
  right: anbchor(--anchor-4 left);
}

Might not be the best idea in all the world, as it effectively spans the target’s position across multiple other elements on the page, allowing it to take different dimensions and shapes.

Specification

The anchor-name property is defined in the CSS Anchor Positioning Module Level 1 specification, which is currently in Working Draft status at the time of writing. That means a lot can change between now and when the feature becomes a formal Candidate Recommendation for implementation.

Browser support

Data on support for the css-anchor-positioning feature across the major browsers from caniuse.com

More information and tutorials


anchor-name originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
378430