New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[css-properties-values-api] Allow custom property descriptors with a CSS @-rule #137

Open
AmeliaBR opened this Issue Mar 27, 2016 · 15 comments

Comments

Projects
None yet
@AmeliaBR

AmeliaBR commented Mar 27, 2016

The incredible flexibility of CSS Variables / Custom Properties introduces limitations compared to regular properties. By default they:

  • cannot be interpolated in animations & transitions
  • disable any parser-based type checking when used as a var()
  • cannot have a default value, fallbacks must be specified each time a var() is used

The CSS Properties Values API addresses all these limitations, but requires script to run to do so. This will cause async issues (CSS having to be re-parsed after script runs) and is no use at all in cases where scripts are disabled for security reasons (e.g., SVG-as-image).

While the general argument for using JS APIs for Houdini has been discussed in #86, I think property declarations are a special case. This isn't about declaring functionality that needs to be represented as script functions; it's merely declaring a data object. An @-rule of descriptors and values seems a perfect fit:

@property --highlight-color {
  syntax: "<color>";
  initial-value: red;
  inherits: true;
}

@property --gap-spacing {
  syntax: "<length-percentage>";
  initial-value: 1em;
  inherits: false;
}

It would of course be preferable to always have @property rules earlier in the CSS parsing sequence than any declarations using those properties. However, since it is also going to be possible to set property declarations asynchronously with the API, I don't think this would need to be an enforced syntax rule, just a performance recommendation.

The effects of this declaration would be the same as for a declaration via the API. E.g., If initial-value isn't specified, it would get the "nothing" initial value described in CSS Custom Properties, with the resulting implication for var() function fallbacks.

The API currently advises throwing an error if the provided initial value doesn't match the provided syntax. For a @property declaration, I would recommended simply dropping the non-conforming initial value declaration. This could allow multiple declarations with standard CSS fallback methods, e.g., for new functions that might not be recognized:

@property --highlight-color {
  syntax: "<color>";
  initial-value: red;
  initial-value: lighten(maroon);
  inherits: true;
}
@FremyCompany

This comment has been minimized.

Contributor

FremyCompany commented Mar 27, 2016

Thanks Amelia for bringing this in.

I am 100% in favor of this. In fact, I am 100% in favor of removing the JavaScript API and replace it by this CSSOM API (or, alternatively, have the JavaScript API create such a declaration for you, and append it to some special author stylesheet).

The reason I am 100% in favor of this change is that, if we want to allow the CSS Parser to understand new CSS syntax constructions, it will be impossible to do that via script properly, we will need to do this in the CSS file. I believe we should therefore be coherent and move every CSS extension declaration in the CSS file itself.

/!\ Potential issue
A point your proposal does not touch is what happens if multiple declarations cover the same property. I think it should be an error whose effect is that none of the declarations are applied, if any of the declarations is different from the other (as in, only perfect duplicates are allowed). Authors must ensure they don't include multiple conflicting declarations in their stylesheets. User agents should warn when dropping such conflicting declarations in their console.

A possible variant would be that only mission-critical fields would cause the drop (so, while "syntax" and "inherits" must be the same, the "initial-value" can vary, in which case the initial-value defined last would win).

What do you think?

@AmeliaBR

This comment has been minimized.

AmeliaBR commented Mar 27, 2016

Regarding duplicates: why not adopt the usual CSS "last rule wins" approach? That's what happens with overloaded @keyframes rules. Note that the entire @-rule would be replaced, no attempt to merge two different versions.

As far as integrating the API with declarative syntax: I think it would be preferable to still have an API option, but it would definitely need to be integrated in a CSSOM-friendly way. This could effectively mean creating an extra system stylesheet of generated @property rules (at the end of the cascade, so having precedence).

A few more issues:

  • Would an @property rule in a CORS-protected stylesheet affect stylesheets from a different domain (or vice versa)?
  • What other API calls are needed? E.g., it would be useful to include a way to retrieve all registered property declarations (whether registered via CSS or JS) & a way to delete or modify them (or maybe just over-ride them with a new rule, to avoid issues if the original is CORS-protected).
  • Should this API also be able to retrieve (read-only) property declarations for native properties? Could be useful for building dev tool extensions.
@tabatkins

This comment has been minimized.

Member

tabatkins commented Mar 27, 2016

Yup, a declarative syntax is definitely in the cards, more or less matching what you have here. We'll prove it out in the JS library first, making sure we're not missing anything, then backfill it so you can use typed custom properties in Animations and the like without having to invoke JS.

@AmeliaBR

This comment has been minimized.

AmeliaBR commented Mar 29, 2016

Thanks for the update @tabatkins. So this would be a CSS Custom Properties Level 2 feature?

I'll have to do a more careful read-through of the Houdini spec to identify any fussy issues; I really want this to move along fast! CSS Variables without animation & transitions are very limited for many of the SVG paramaterization uses I'd like to see. (And for UX theming, too, of course.)

@tabatkins

This comment has been minimized.

Member

tabatkins commented Mar 29, 2016

So this would be a CSS Custom Properties Level 2 feature?

Most likely, yeah, unless we feel like we've nailed it for sure real quick.

CSS Variables without animation & transitions are very limited for many of the SVG paramaterization uses I'd like to see. (And for UX theming, too, of course.)

Don't I know it!

To hit some of your other issues:

Would an @Property rule in a CORS-protected stylesheet affect stylesheets from a different domain (or vice versa)?

Yeah, should work just fine. The only thing we do differently for cross-origin sheets is hide their precise text; we apply their effects just like normal.

What other API calls are needed? E.g., it would be useful to include a way to retrieve all registered property declarations (whether registered via CSS or JS) & a way to delete or modify them (or maybe just over-ride them with a new rule, to avoid issues if the original is CORS-protected).

You can just walk the sheets to find ones that show up in same-origin or CORS'd sheets. This doesn't help you with cross-origin (non-CORS'd), but eh, same is true of every other CSS feature.

You can override declarations by just putting in another one, after the one you want to override, regardless of where it came from. (That is, last will always win, with full overriding, not cascading individually. Same as, for example, @counter-style.)

Should this API also be able to retrieve (read-only) property declarations for native properties? Could be useful for building dev tool extensions.

Don't think we need any special API beyond the existing OM-walking. (Or if we do, we need it in a generic sense anyway, so it's not specific to this proposal.)

@vitaliy-bobrov

This comment has been minimized.

vitaliy-bobrov commented Jul 13, 2018

I've published PostCSS plugin that transforms custom property declaration in CSS file to JavaScript CSS.registerProperty.

https://github.com/vitaliy-bobrov/postcss-register-custom-props
https://www.npmjs.com/package/postcss-register-custom-props

@LeaVerou

This comment has been minimized.

LeaVerou commented Sep 26, 2018

Now that it's been 2.5 years since we last discussed this, the Properties and Values API has been tested by more developers, and it doesn’t look like we've missed anything huge, any chance we could revisit this? Perhaps in the upcoming TPAC F2F?

I've lost count of how many authors have asked me about this in the last year or so that custom properties have increased in popularity.

@tabatkins tabatkins added the Agenda+ label Oct 25, 2018

@css-meeting-bot

This comment has been minimized.

Member

css-meeting-bot commented Oct 25, 2018

The Houdini Task Force just discussed Declarative property registration.

  • Resolved: Add declaritive registration into properties and values API
The full IRC log of that discussion <TabAtkins> Topic: Declarative property registration
<TabAtkins> github: https://github.com//issues/137
<gregwhitworth> scribenick: gregwhitworth
<gregwhitworth> TabAtkins: early on in this process, when we setting up the props vals API
<gregwhitworth> TabAtkins: we would want a more declaritive version
<gregwhitworth> TabAtkins: you just want to animate a custom prop
<gregwhitworth> TabAtkins: you don't want JS
<gregwhitworth> TabAtkins: we delayed doing that
<gregwhitworth> TabAtkins: at this point, we're pretty stable with L1
<gregwhitworth> TabAtkins: that interface is working well
<gregwhitworth> TabAtkins: time to revive this declaritive proposal
<gregwhitworth> TabAtkins: have some way to store it in your stylesheet
<gregwhitworth> TabAtkins: the shape of it is more or less what amelia says in the issue
<gregwhitworth> TabAtkins: there are some minor changes for registration due to CSS syntax
<gregwhitworth> TabAtkins: other than that - this appears to be relatively straight forward
<gregwhitworth> TabAtkins: there are some potential issues with timing
<gregwhitworth> TabAtkins: like a late registration not causing reparsing
<gregwhitworth> TabAtkins: it's easier in a declaritive scenario - no need to go reparse
<gregwhitworth> TabAtkins: overall I think it's reasonable and I want to deliver on providing declaritive APIs
<gregwhitworth> SimonSapin: just now you talked about stylesheets, if the docuement contains 4 stylesheets does order matter?
<gregwhitworth> TabAtkins: if you have 4 registrations then the order matters
<gregwhitworth> fremy: it's fine
<gregwhitworth> fremy: I'm super excited
<gregwhitworth> futhark: just a concern about timing and at-rules when they start collecting
<SimonSapin> s/about stylesheets/about early and late stylesheets/
<gregwhitworth> futhark: you delay them, the latest would apply but what if one browser applies after two stylesheets but there's a new one coming in?
<gregwhitworth> TabAtkins: that's fine - you'll only be able to see it if you look at the GCS in between those loading docs
<gregwhitworth> futhark: so you would normally register the first time you collect the rules, the second time you would just ignore it?
<gregwhitworth> TabAtkins: doc order is normally how it works? but good question
<gregwhitworth> futhark: that's a little bit different how the JS version works
<gregwhitworth> TabAtkins: yes because JS can be definitive
<gregwhitworth> heycam: what happens if you do both JS and declaritive?
<gregwhitworth> TabAtkins: the script should just win IMO
<gregwhitworth> TabAtkins: that seems much more likely to have timing issues
<gregwhitworth> fremy: yeah
<gregwhitworth> TabAtkins: plus then - it saves the problem of dealing with multiple JS registrations
<gregwhitworth> heycam: not sure what the current status is of @font-face of counter-styles in shadow styles
<gregwhitworth> TabAtkins: as far as I remember, registering a property is global, so it will apply to all shadows as well - I THINK
<gregwhitworth> TabAtkins: I wouldn't want it to be different. font-face and what not are scoped to their own root so it's different
<Rossen> q?
<gregwhitworth> heycam: it's a bit unclear what "later in the document" means
<gregwhitworth> TabAtkins: it shouldnt' be - you have a flat tree ordering
<gregwhitworth> TabAtkins: style rules that don't show up in the flat tree don't get applied
<gregwhitworth> emilio: they do apply
<gregwhitworth> emilio: if you have an unslotted style tree then it does apply
<gregwhitworth> TabAtkins: this is more complicated
<gregwhitworth> TabAtkins: [repeats question]
<gregwhitworth> TabAtkins: but if you have unslotted styles still applying, then the same question applies
<gregwhitworth> emilio: you use DOM tree order inside of the style root
<gregwhitworth> emilio: I'm not sure why you can't use DOM tree order based on Host for example, that's what I think is most reasonable
<gregwhitworth> TabAtkins: it sounds complicated - maybe we don't allow this in scoped stylesheets
<gregwhitworth> TabAtkins: you already have a shadow root, so you're already in JS
<gregwhitworth> gregwhitworth: is there a plan for declaritive creation of shadow root?
<gregwhitworth> TabAtkins: I've pushed for it - but so far no
<gregwhitworth> TabAtkins: we'll deal with it then I guess
<gregwhitworth> TabAtkins: do people want to proceed with this?
<gregwhitworth> heycam: I think it's worth adding somewhere
<bkardell_> +1
<gregwhitworth> Rossen: there's enough excitement
<gregwhitworth> Rossen: let's persue it somewhere, properties and values API?
<gregwhitworth> Proposed Resolution: Add it to the Properties and Values API
<gregwhitworth> Rossen: objections?
<gregwhitworth> Resolved: Add declaritive registration into properties and values API
@chharvey

This comment has been minimized.

chharvey commented Oct 25, 2018

The only thing I would suggest is change initial-value to initial and change inherits to inherited. Simply because (1) initial is shorter, and (2) the property itself is either inherited or not; it does not inherit anything.

@FremyCompany

This comment has been minimized.

Contributor

FremyCompany commented Oct 25, 2018

@chharvey We try to use the same name as the existing houdini function, which at this points are already frozen. https://drafts.css-houdini.org/css-properties-values-api/

@tabatkins

This comment has been minimized.

Member

tabatkins commented Oct 29, 2018

Yeah, using the exact same name as registerProperty() (modulo the camelCase to kebab-case conversion) is a hard requirement.

@astearns astearns removed the Agenda+ label Nov 13, 2018

@Snugug

This comment has been minimized.

Snugug commented Dec 6, 2018

Hey! It looks like this declarative syntax isn't in the current version of the draft (Nov 9, 2018) but looks like y'all agreed to add it on October 25th. Curious what the delta is. I really love this syntax!

andruud added a commit to andruud/css-houdini-drafts that referenced this issue Dec 12, 2018

[css-properties-values-api] Add @Property.
This adds basic support for @Property, without solving any of the
potentially complicated issues, like w3c#845 and w3c#846.

Resolves w3c#137, at least partially.
@andruud

This comment has been minimized.

Contributor

andruud commented Dec 12, 2018

@Snugug I'll propose something soon ... ish.

@andruud andruud referenced a pull request that will close this issue Dec 12, 2018

Open

[css-properties-values-api] Add @property. #847

@Snugug

This comment has been minimized.

Snugug commented Dec 13, 2018

@andruud saw your PR! 🎉! Question about using this new syntax, specifically around creating properties with specific allowable idents. I've got the following registered property:

CSS.registerProperty({
  name: '--theme-color',
  syntax: 'blue|green|red|yellow|grey',
  inherits: true,
  initialValue: 'blue',
});

Would that then convert in to

@property --theme-color {
  syntax: blue|green|red|yellow|grey;
  inherits: true;
  initial-value: blue;
}

Or would the separate possible idents be space separated, or be in quotes? What about the initial value? Should that be in quotes?

@andruud

This comment has been minimized.

Contributor

andruud commented Dec 13, 2018

@Snugug What @AmeliaBR originally suggested, and what I proposed in the PR, would be:

@property --theme-color {
  syntax: "blue|green|red|yellow|grey";
  inherits: true;
  initial-value: blue;
}

I.e. the syntax is parsed as a string, requiring quotes (I assume, haven't read every word of css-syntax). The initial-value is parsed as a token stream, just like custom property declarations. In your example blue is correct, without quotes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment