oddbird / oddbird/css-anchor-positioning

[BUG] Inset shorthand expansion overrides non-shorthands

Open
#456 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug
Dominant language
TypeScript
Stars
493
Forks
18
Avg merge
12h 37m
Merged PRs (30d)
5

Description

I think this is broader than the title suggests. position-try-fallbacks isn't needed. A plain rule with inset: auto followed by anchor() insets is all it needs to fail.

I tested in Firefox 152 with layout.css.anchor-positioning.enabled = false.

In my test (CodePen link), I have div.wrap, which has 100px padding on either side. Inside is div.band. Then div.panel should be anchored to it.

/* A — panel 0 → 66      ✗ does not span */
.panel {
  position: fixed;
  inset: auto;                                /* reset the [popover] UA inset: 0 */
  inset-block-start:  anchor(--band end);
  inset-inline-start: anchor(--band start);
  inset-inline-end:   anchor(--band end);
  inline-size: auto;
}

/* B — panel 100 → 800   ✓ spans, `inset: auto` deleted            */
/* C — panel 100 → 800   ✓ spans, `top/right/bottom/left: auto`    */

Deleting that one declaration is the whole difference. In every case the polyfill writes identical, correct custom properties. The anchor values are never wrong; they just get overridden.

expandInsetShorthands() in src/cascade.ts expands the shorthand with block.children.appendData(...), which appends the longhands to the end of the rule regardless of where the shorthand appeared in source. The installed rule becomes:

.panel{position:fixed;inset:auto;
  inset-block-start:var(--anchor-…);
  inset-inline-start:var(--anchor-…);
  inset-inline-end:var(--anchor-…);
  inline-size:auto;
  top:auto;right:auto;bottom:auto;left:auto;   /* ← appended, wins on source order */
}

Physical and logical longhands resolve to the same properties, so the appended autos win. Trigger conditions measured: inset: auto breaks both axes; inset: 0 breaks both; inset-inline: auto breaks only the inline axis; inset-block: auto only the block axis. A shorthand in a separate earlier rule is harmless.

inset: auto seems like the natural way to reset the [popover] UA inset: 0 before applying anchor() insets, so it shows up in popover-anchored panels, which seems like a common case.

The longhand workaround doesn't survive minification. C looks like a clean fix but LightningCSS 1.32.0 collapses top/right/bottom/left: auto straight back into inset: auto and hoists it above the anchor longhands. Authoring the longhands in source doesn't reach the browser. (Native engines are unaffected: the hoisting puts inset:auto before the anchor longhands, which is the order native needs.) I'm guessing other minifiers will do the same. A workaround that does work with minification is to reset only the specific side you need — e.g. inset-block-end: auto alone — since there's then no shorthand to collapse or expand.

Happy to test a fix if that helps.

Originally posted by @freshyill in #319

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in src/cascade.ts at expandInsetShorthands(), then reproduce the inset: auto case from the issue with the anchor-positioning polyfill. Done means expanded inset longhands retain the shorthand’s source position so they do not override later logical anchor insets; verify the reported physical and logical shorthand cases.

Written by the indexing model from the issue text.

Assessment

Tech stack
css, typescript
Domain
frontend, web-dev
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
78/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.