w3c / w3c/csswg-drafts

[css-values] Add a way to set longhands to the corresponding expansion of a shorthand value

Open
#8,055 11 comments 4 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

css-values-5 Needs Edits
Dominant language
Bikeshed
Stars
4.9k
Forks
816
PR merge metrics
PR metrics pending

Description

Rationale

There are some ways to set longhands to values that can't be represented at specified-value time, so they just serialize as empty string. In particular:

  • Pending-substitution values:
    document.body.style.margin = "var(--m)";
    document.body.style.marginTop; // "" per spec
    
  • Expanded toggle() values (see #6764)
    document.body.style.margin = "toggle(1px, 2px)";
    document.body.style.marginTop; // "" per #6764
    
  • Expanded system fonts
    document.body.style.font = "small-caption";
    document.body.style.fontFamily; // "" in Gecko, Blink, WebKit
    
  • Values set by a legacy shorthand with broader grammar (see #7195)
    document.body.style.webkitBackgroundClip = "text";
    document.body.style.backgroundClip; // "" in Backgrounds L3 (presumably, see #7195)
    
    document.body.style.webkitBorderImage = "100% / 1px";
    document.body.style.borderImageWidth; // "" in WebKit, since 1px forces border-width when set via -webkit-border-image
    

This doesn't seem ideal, since we lose round-tripping (assigning empty string in CSSOM will just remove the declaration) and it's hard to tell what the value is. All these cases have in common that the value is set from some shorthand, so cssText can help serializing such shorthand, but this produces poor results when some longhand has been modified afterwards (see #2515):

document.body.style.cssText = "margin: var(--m); margin-top: 1px";
document.body.style.marginLeft; // "" :( and the following don't help:
document.body.style.cssText; // "margin-right: ; margin-bottom: ; margin-left: ; margin-top: 1px;"
document.body.style.margin; // ""
Proposal

So I had an idea to address all of this: introduce from-shorthand( <custom-ident> ':' <whole-value>):

  • from-shorthand() is a <whole-value>.
  • It's invalid if the <custom-ident> doesn't match the name of some shorthand ASCII case-insensitively.
  • When used on a longhand, it's invalid if the referred shorthand is not a shorthand of the longhand.
    When used on a shorthand, it's invalid if the referred shorthand is not a "super-shorthand" (i.e. it's invalid if there is a property which is a longhand of the current shorthand but is not a longhand of the referred shorthand).
  • It's invalid if the <whole-value> is an invalid value for the referred shorthand.
  • Custom properties don't have shorthands, so when used on an unregistered prop it should just be tokenized instead of resolving there.
  • At computed-value time, it basically resolves to the value that the current property would get if the referred shorthand had been set to the <whole-value>.
  • I'm using a : since it looks clearer, like a declaration. Function arguments typically use , but that could get confusing in case the <whole-value> has commas. (Edited: initially suggested ;)

Then:

document.body.style.margin = "var(--m)";
document.body.style.marginTop; // "from-shorthand(margin: var(--m))"
document.body.style.margin = "toggle(1px, 2px)";
document.body.style.marginTop; // "from-shorthand(margin: toggle(1px, 2px))"
document.body.style.font = "small-caption";
document.body.style.fontFamily; // "from-shorthand(font: small-caption)"
document.body.style.webkitBackgroundClip = "text";
document.body.style.backgroundClip; // "from-shorthand(-webkit-background-clip: text)" in L3
document.body.style.webkitBorderImage = "100% / 1px";
document.body.style.borderImageWidth; // "from-shorthand(-webkit-border-image: 100% / 1px)" in WebKit
                                      // (the 100% is discarded and may be something else)
document.body.style.cssText = "margin: var(--m); margin-top: 1px";
document.body.style.marginLeft; // "from-shorthand(margin: var(--m))"
document.body.style.cssText; // "margin-right: from-shorthand(margin: var(--m)); margin-bottom: from-shorthand(margin: var(--m)); margin-left: from-shorthand(margin: var(--m)); margin-top: 1px;"
document.body.style.margin; // ""

I guess there may be compat issues in the var() case, but hopefully people are not relying on getting an empty string.

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 by reading the proposal and its examples, then compare the related discussions in #6764, #7195, and #2515. Work is done when the CSS Values behavior and serialization rules for from-shorthand() are resolved and specified, including shorthand/longhand validity cases and custom properties.

Written by the indexing model from the issue text.

Assessment

Tech stack
css
Domain
frontend
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
30/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.