-
Notifications
You must be signed in to change notification settings - Fork 143
[css-typed-om] Inputs for the CSSColorValue constructors #1014
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
Comments
Oh, forgot to list the big downside of (1) - if you wanted to make a lookalike custom function for rgb() that took 0-255 arguments (in the CSS) in the same way, you'd have to remember to translate any "number" arguments into JS numbers, rather than passing them thru directly. Well, calling it "big" is perhaps a bit much, but it's the largest downside for (1), at least. |
Another option (and a bit more modern JS) could be to use a dictionary initializer as a single argument to the constructor. It also buys you some extensibility and flexibility. Something like: |
That is mostly going to work, except of course for CIE Lightness where 100% maps to 100, not 1. |
This is a very common issue with any library working with RGB colors. Typically the decision is to go with one of the two and if one wants to use a different range, they need to convert. In Color.js we went with 0-1 for reasons I will detail below, chroma.js and d3 Color went with 0-255. I would suggest going with numbers in the 0-1 range, as the 0-255 range is a relic. It originates in using 8 bit for each color component, and seems completely arbitrary otherwise. Note that
This creates unnecessary error conditions: what happens when both Stepping back for a bit, speccing Typed OM for colors is a fairly substantial undertaking and it would be good if we could get some consensus on the overall architecture and design decisions before discussing the minutiae of constructor arguments. I know there are many people in the group who would have input on said design decisions and would love to partake, myself and @svgeesus included. |
That could be trivially resolved. You either raise an exception or decide which one wins. Using an initialization dictionary also allows you to mix and match, e.g. That said, I agree that 0-255 is a relic, but it is in common use. I'm in favor of making the API more forward-looking, but want to maintain backward compatibility with legacy code as well to ease the transition. I'm also concerned that if we go with pure 0-1 floating point that we make sure all the (0-255)/255 values can be accurately represented by JS numbers so they at least can round-trip without rounding errors (I think that's the case, but someone should check). e.g. if someone does Even if we decide that the only values are r, g, and b in the 0-1 range, using an initialization dictionary is more readable, and is much more future-proof. It's simply a more forgiving pattern. Not to mention, it's endorsed by the TAG: https://www.w3.org/TR/2020/NOTE-design-principles-20201110/#prefer-dict-to-bool and https://www.w3.org/TR/2020/NOTE-design-principles-20201110/#naming-optional-parameters Actually, the more I think about the serialization issue, the more I think the CSSRGB constructor should only take values comparable with the CSS |
chroma.js and d3 Color are both sRGB-only, so that was a reasonable decision at the time but will bite them once they extend to other RGB spaces. Some spaces, like rec2020 and rec2100, don't even allow 8 bits per component; the choices are 10, or 12 (recommended). Also, when quantized, there are two encoding ranges (narrow and wide) but the float 0..1 representation is always wide, which removes one source of error. For example, in 10 bit narrow-range encoding, color component values range from 64 to 940; in 12 bit narrow-range encoding, they range from 256 to 3760. |
(first contribution, hi! and apologies for any conventions I'm breaking). Design / dev tool author here. I favour (1) for the same reasons as @tabatkins and @LeaVerou Lea (0-255 is legacy, 0-1 is computationally simpler). Some additional data points / perspectives:
|
Indeed, you can solve all unnecessary error conditions by raising an error, but that doesn't make them good practice. It's far better to avoid creating the error condition in the first place.
I just ran
Please note that the TAG guidelines you link to (both of which I'm aware of and have even taught) do not apply here. They are both about optional arguments, which these are not. Also, the first one is mainly about booleans, edited recently to generalize to all optional primitives. Indeed, avoiding boolean positional arguments is one of the most basic API design principles (they are called "boolean traps" for good reason). However, red, green, and blue, are numbers with an established meaning, an established order, and all of which are mandatory. That said, there is less of an established order in other color spaces (e.g. LCH vs HCL), so for consistency, it would make sense to use named parameters here too. |
Hi @maltenuhn! Welcome! 👋🏼
That's fascinating. Do you have a source for these user interviews? I would love to read more about this study! Not proprietary, but perfectly valid CSS Color 4 syntax! Unfortunately, they only implemented part of it, no other spaces besides P3, and no actual |
We can make many more APIs avoid errors by severely restricting what they can do, that doesn't help authors either. There's a balance needed. Allowing authors more flexibility at the risk of allowing them to make mistakes is a common trade off.
The principles they represent do apply.
First, there is an optional argument to the CSS
It used to be about booleans, and despite the anchor (which was left intact to not break links) now talks about the general case, which is why I included the link (and the links were for the benefit of other readers of the thread, I'm aware that you know them). Back to Tab's original question, I'm now fairly well convinced that bare numbers passed into the constructor of a |
We have numeric types already for precisely this reason - if you want percentages, you can just pass a I don't think an initialization dict, in general, is the best pattern here - most of the arguments are required, and there's already a well-known and established order for them, coming from CSS (and re-expressed right in the name). So I'm pretty confident positional is still the right way to go here, at least for the simple functions. (
Given that taking raw numbers is a pure convenience (rather than only taking the CSSStyleValue objects that it'll expose post-construction), I'm really loathe to do tricky stuff like that and have the numbers be interpreted in different ways depending on the function. It's just begging for people to hold wrong. My current spec text is doing option 2 (raw JS numbers are always 0-1 percentages, but CSSRGB accepts |
Fair enough.
I disagree. I have no issue with also accepting positional arguments. But I want to push back on them all being required. e.g. does
But that's actually inconsistent with your "passing a raw JS number is equivalent to passing a CSSUnitValue(x, "number")" so will cause even more author confusion IMO. I see no reason why Frankly if you interpret bare numbers as 0-1 you are doing something tricky and interpreting them in a different way for this function, interpreting 0.5 as equivalent to CSS.number(127) rather than CSS.number(0.5) like you do everywhere else. So let me make a concrete proposal:
Both bare numbers and CSS.number() are interpreted the same as if they were specified in This allows for author convenience, is succinct, is consistent with CSS and should be consistent with other CSS color object constructors. (I don't have an issue if you want to make a new type restricting the values to double (or even octet), number, and percent types, rather than CSSNumberish, or just define other values to throw exceptions.) |
True, though I'm not convinced saving the user one division with 255 is a worthy trade off.
Both Tab and I have explained why they do not:
You feel so strongly that the function should mirror the semantics of Please note that |
As @plinss points out, another solution would be polymorphism. We could accept an array OR an object literal: new CSSRGB([1, 0, .6])
new CSSRGB({r: 1, g: 0, b: .6}); Aside: can we please name the constructor something other than CSSRGB, i.e. two different acronyms smushed together? A namespace, and/or the word "Color" somewhere would help. @tabatkins Re-iterating as I think it was lost in the argument discussion:
Where can we find this draft? It's not anywhere in this repo. Searching for |
Both throw. Those are required arguments in CSS, and there's no particularly good reason to change that in the JS representation, I believe. There's not really a meaningful sense in which 0 is a "default" value for any of the color channels.
Yes, it is inconsistent, but in a different realm. My current spec text has JS numbers be
It's still an in-progress edit right now, I'll have first draft up soon. |
In JS all arguments are effectively optional. One way or another you have to define what happens when an author uses
I'm explicitly not opposed to positional arguments, see my proposal above. I just want the option to use an initialization dictionary. What I do feel strongly about is not redefining the behavior of a bare number for this one constructor. Keeping it consistent with a theoretical color object model isn't as valuable as keeping it consistent with CSS syntax that's been in use for over 20 years. Another advantage of an initialization dictionary is that it does let the author opt-in to a different behavior for bare numbers, by specifying them in a different dictionary slot (If that's a case we really need to support, after all, is it worth adding it to save them writing
Yes. CSS already has them. This is the CSSOM, not a color OM, the need here is to provide an object model for CSS constructs, in this case I'd love to see a generic color object model. And if we create one I'm happy for that to use floats and do away with the 0-255 legacy there. But that's not the scope of this issue or module and we shouldn't let this scope-creep into one.
I don't see any value in accepting an array. I'm not seriously opposed, but unless there's precedent or alignment with other CSS color constructors I'd rather not invent another new thing here. In fact, we may want to reserve supporting an array in case the |
I don't see any value in making those throw unless the objects are immutable. Why can't I construct an object and then populate the attributes later? Forcing authors to have all the values before constructing an object is an anti-pattern. What if I want to pass an object into a function to get the values for the channels? |
Wait, is that all it is doing? An object model of the sRGB-only Does the |
Having not seen the actual spec prose Tab is working on, I can't say. But I can say what I believe it should be doing. Yes, there should be a CSSRGB (modulo bikeshedding) class whose primary role is to model a CSS
Yes, that should be a CSSHexColor (or whatever we call it). We should also have a CSSLAB (for
Right, and that should be a Color class (and whatever appropriate subclasses, I trust you and Lea to have the best input on the shape of that API). But it should not be a CSS color class as it's representing a color, not a CSS construct. And it should not be in this module (and arguably should not be a CSS module either as it should also be used in Canvas APIs, etc). We should be able to convert from the CSS color constructs to a Color object and vice-versa. The Color class should have all the conversion, manipulation, and extra functionality, not the CSS color objects (though I'm happy to add convenience methods to the CSS color classes once we've defined the Color class(es)). I fear the two concepts have become conflated here. |
Hi all. Author of Popmotion and Framer Motion here, I've spent a fair amount of time working with value ranges and I'd love to ditch a bunch of code in favour of a native color API. Here's some thoughts after reading this discussion. First of all I recognise there's a huge value in maintaining familiar APIs like the RGB 0-255 range. But for me, that doesn't override the potential wins from option 1. All finite ranges are IMO best described as ranges 0-1. It's a range that has intrinsically semantic meaning and is easy to visualise mentally. Standardising it between percentage/bounded time/8 bit color makes interpolating between these ranges straightforward. On the keyed object idea, it'd of course be possible to make
I totally agree with this, it's even in the name!
I agree with this too. But Houdini has been developing for a while and I can easily imagine another 5(ish?) years waiting for a proper Color API. This should and can be it. It can be renamed, or not, I don't overly care. I just want a sane, native color API. The semantics in this sense really don't have me concerned. Ultimately I think if the existing standard was |
Also, to be clear, I'm perfectly happy for the generic Color classes to take simple bare numbers in their constructors. e.g. should there be a RGB Color subclass, The Color class(es) should have a clean, uniform, API that makes the most sense for handling colors. The CSSOM classes however, should take the same inputs and match the semantics of their CSS counterparts. |
That's not now JS works, if the constructor is expecting
And Houdini will continue developing for many years to come. It's all modules that can be implemented independently. There's no reason to expect defining a proper Color API, independent of the CSSOM would take another 5 years, or that it would, in fact take any longer than trying to define a CSSOM that conflates CSS constructs with generic colors. In fact, as this thread demonstrates, mixing the two concepts is (and will continue to be) a source of contention that will dramatically slow the process. Having a CSSOM that's simply CSS, and a Color API that's just color, splits the concerns and lets each develop at their own pace, focusing on their own needs. Believe me, we'll get a Color API much faster if it's not carrying around 25 years of CSS baggage. We'll also get a reasonable CSSOM faster if we don't try to solve all of color in it. |
Fairly familiar with how JS works. If you pass it RGB(1) you can reasonably forward fill GBA with 0,0,1. There’s no shifting. I then went on to say RGB({ b: 1 }) isn’t shorter than just writing RGB(0,0,1) so there’s no real benefit to this either in terms of shorthand. |
(sigh) I'm not saying it can't be done, I'm saying it shouldn't. It violates the principle of least surprise, e.g. any JS programmer unfamiliar with the API will presume you're passing Furthermore, the original intent of offering an initialization dict was to pass different types of values, e.g. numbers vs percentages. The advantages in using one for optional arguments is for clarity more than brevity. It also allows unspecified argument defaults to be controlled by the receiving function rather than the caller. |
We’ve crossed wires here. I may have incorrectly used “forward” fill? But said “GBA” get filled. Red is assigned 1, as provided. The rest get assigned their default values 0,0 and 1, as is standard JS. And that this syntax is so short we don’t need a shorthand for setting, for instance, blue, with named objects, as was earlier proposed. |
Agreed, I read "forward fill" as filling in missing arguments from the front, I also misread your example as The behavior you describe is precisely the same as the proposal I offered in #1014 (comment) |
Note that what @svgeesus and I were concerned about, that people will start using |
Which I'm still fine with, fwiw. I never understood why we'd want to produce a secondary version of the color classes; either we exclusively put a bunch of useful color manipulation methods over there (leaving the TypedOM variants extremely low-power and requiring conversion back and forth by authors to do useful things) or we put them on both (making it unclear why we have two class hierarchies in the first place). |
Let me try to be clear about my feelings on the matter, I want two things: 1) a color class (or class hierarchy) that is used to represent color values across the entire platform, and has color space conversion, color manipulation functions, etc; 2) A set of CSS OM classes that represent the various CSS color constructs well allowing creation and manipulation of CSS style sheets without resorting to string manipulation and parsing. If it turns out that we can serve both of those needs with a single class (or class hierarchy), then fine. But they have different goals and different needs as to API surface. Yes there's some overlap, but there's also a lot of orthogonal functionality. My preference is that we design the two independently, and then see how they overlap and if it makes sense to combine them. What I don't want is to see a bunch of compromises in the API surface in order to serve two orthogonal purposes, making one (or both) less suited to task. Especially in the early design stages. This thread started because of just such an impedance mismatch between the two use cases. A similar example is DOMMatrix, we have CSSOM classes to represent the various CSS constructs, and a Matrix class to handle the actual math. It didn't make sense to conflate them, it likely doesn't make sense to conflate CSS color constructs and a generic color class either. |
The OP's "impedance mismatch" wasn't a big deal, it was just a design question. (And I solved it, by just saying that the Typed OM always represents values as 0-1 or %s; CSS's 0-255 range on RGB is just a weird CSS text-syntax thing.) Notably, this ends up being the same solution that a native JS Color class would do, like in Lea/Chris's library. My experiences so far don't suggest that we'll have much, if any, use-case mismatch. When we do, we can design around it, perhaps with an independent class doing something more directly when the CSS version is too weird/restricted. But for colors? The CSS version appears to be just fine. (The DOMMatrix stuff is an example where the CSS version is indeed too weird/restricted; CSS only uses 3x2 or 4x4 matrixes, but the general use-case wants arbitrary sizes and dimensions. It makes sense that handling the arbitrary case in a CSS-specialized class is probably overkill. I'll note, tho, that we haven't produced an arbitrary Matrix class for the web, even tho DOMMatrix has been around for years. Hooking things together even when there is a slight mismatch at least ensures there's progress on both cases, rather than us only solving the one and leaving the other to languish.) |
And that's not a good solution. As I said before, the goal of the CSSTypedOM is to represent CSS constructs accurately and faithfully. A class to represent a CSS The design question here isn't "which range of numbers do we take in this one constructor", it's "what's the purpose of this API". It's the second question that should be informing the answer to the first. Trying to combine CSS OM with a generic Color class at this point (i.e. without having a well-designed API surface of a generic color class, or even a fully-formed CSSTypedOM API), is a premature optimization that leads to bad design decisions. Those are two very different beasts. Combining them without the big-picture of the best API surface for each is a mistake. Adding color manipulation functions to the CSSTypedOM color classes may very well be a nice convenience for authors, it also might make an API surface that's not performing either job well because it's trying to serve two different functions. As I said, I'm not opposed to combining the functionality in principle, but I am very much opposed to starting with the premise that we must do so. And any compromises we make now trying to serve that end are a mistake. |
@plinss this sounds like purposefully downgrading a new API because of the poor or outdated decisions of the past. |
@mattgperry No, it's purposefully designing a new API to be properly suited to its task. |
I'm strongly with @plinss here. |
Agreeing with @plinss and @LeaVerou here.
So people are assuming that the color type in CSS Type OM is the color type for the Web platform. And honestly, Canvas people should not be penalized for trying hard to align with CSS Color 4 here and re-using CSS stuff in Canvas. Yes, Typed OM needs to reflect whatever was used to construct a given color. But at the end of the day, it is being used to set and query colors; and colors have meaning, and the same actual color can be set in multiple syntactic ways and still be the same color. So there is a need for a color type. Colors can also be compared with other colors, and whatever syntax was used to create that pair of colors is irrelevant to that comparison operation. |
IMO that is one of the reasons why we shouldn't use CSSColorValue as a general Color object. CSSColorValue needs to reflect syntax, whereas a Color object only needs to reflect color space & representation thereof. E.g. the distinction between Nobody is complaining that we have |
Someone here might want to butt in on whatwg/html#6609 to say these types are not stable (if I understand this discussion correctly). |
We just did in the response to their TAG review request (which is how I came across this in the first place), here: w3ctag/design-reviews#627 (comment) |
That's how I arrived here, but TAG review is somewhat orthogonal to things landing in a WHATWG standard. |
I strongly disagree on several of these points. The Typed OM needs to reflect CSS, yes. That does not mean it needs to be perfect 1:1 direct translation of CSS syntax into JS constructs; I'm explicitly not doing that in several spots, and expect to continue not doing that for the future. When possible, I'm simplifying and following canonical JS practices more closely, because sticking closely to CSS would be a net loss for the API; direct translation helps in a theoretical purity sense only, but harms authors in practice. Directly: my goal with Typed OM is not and never has been to be a 100% reflection of what authors typed into their CSS. It needs to be able to represent equivalent values, but where JS and CSS idioms clash, or where I can make some minor useful changes that faithfully reflect the underlying value but fold away some aspects of the surface syntax, I will do so. This should be, as much as we can make it, a convenient API to use for authors; "faithful" is not a goal beyond the minimum that needs to be maintained to make it worthwhile at all. This precise case is a great example. Currently,
I think #4 is immediately a no-go. The TypedOM already allows raw JS numbers and strings in several places, and translates them into the appropriate TypedOM objects (CSSKeywordValue and CSSUnitValue). Disallowing it here would be inconsistent and author-hostile. I think #3 has theoretical consistency going for it, and nothing else. Forcing people to write #2 kinda works, but it means authors have to remember that they can use JS numbers for %s in all the color functions except rgb(), where JS numbers mean something completely different. On the other hand, it means that it's easier to mentally translate back and forth between CSS and Typed OM - you see an #1 is what I went with, because tho it means
I'm not starting from the premise that we must do so. Many parts of Typed OM will be specialized for CSS and not suitable for generalization; earlier in this thread matrix APIs were mentioned, which is definitely one such case. I am, instead, affirmatively stating that I think the Typed OM color APIs are reasonably suitable as a general-purpose color library for the web, and while we'd make slightly different decisions if we were designing a JS color API from scratch, the differences between our real and ideal library are small enough that it won't be worthwhile trying to make a second color library. If we tried to make second dedicated color library for the web, either it'll duplicate the large majority of functionality that TypedOM's color types will have, or to avoid that duplication we'll have to hobble the TypedOM classes to be very strictly about solely producing CSS values, such that authors will have to do a two-step converting between TypedOM and the JSColor objects. If someone wants to prove me wrong, and make a Color library proposal that has tons of bells and whistles that we won't want to put into TypedOM, and can get implementor interest behind such a thing, feel free! But I won't be spending my time on it, and in the meantime various APIs on the web want to be able to use something other than strings of CSS text to work with colors, and TypedOM will be here for that.
Not quite. I think it's useful to preserve the distinction between the first and the other two (for some usability and forward-compat reasons), but I don't think the latter two need to be distinguished by the Typed OM. They're definitely not currently preserved; both get represented as a
That first pair is actually conflated in the TypedOM; in several places a plain JS number is allowed and auto-converted into a TypedOM value. The rest are significantly different from each other, in use-case and desirable API. We definitely wouldn't want to try and combine them. I don't think colors are the same, as I argue earlier in this message. This is something that varies case-by-case, and I think trying to draw a general principle out will result in a worse API for authors for minor theoretical purity benefits. |
It sounds like you have specific differences in mind, since you are asserting that they are small. Could you please share them with us?
Nobody is asking for unimplementable, complicated "tons of bells and whistles", but for the existing author use cases to be considered. There is a lot of prior art on this. |
I think the biggest obvious fix would be to use raw JS numbers for channel values, rather than CSSNumericValue objects. TypedOM numbers aren't too annoying to use, so long as they're CSSUnitValues (just an extra indirection thru the (Tho, having a real angle type, instead of having to choose between setting a raw JS number representing either radians or degrees, is nice.) Possibly a change would be to have all the color spaces live in one class, rather than separate classes per function. That would probably require doing some magic tear-offs, tho, like your Color.js does, which we try to avoid when possible in JS APIs, as it makes lifetime management more complicated. Unsure if this would be a net positive or not. Turning it around tho, it seems like you're asserting the differences would be large? If so, what would they be? It's very possible that we could add most or even all of what you want to see!
I didn't say "unimplementable" or "complicated". ^_^ I'm referring to the implicit assertion y'all are making that there are a lot of things we'd want in a Color library that, for whatever reason, aren't appropriate for the Typed OM classes. If you or anyone else would like to demonstrate those things, and we can see that we, indeed, would not want to include them in the Typed OM classes, feel free! Currently I don't believe there are, but I'm not the most knowledgeable about this space; I'm not, however, willing to accept a priori that the Typed OM classes must be more limited by their very nature, making them unsuitable for this sort of thing. I'm not being sarcastic in any way - please prove me wrong. I'm just operating off of the information I currently have and my own intuitions, same as any of us are. |
FWIW, I think that CSS types should not be about input syntax. Some input syntax aspects might have to be preserved in certain cases because of legacy roundtripping behavior, but ideally that is minimized as it there is not a lot of benefit to it. I would actually have expected I also agree that we should not have separate (non-CSS) color and CSS color types. We don't have that today with string-based colors either and thus far that has been great and allowed for reuse of a great deal of logic between canvas, CSS, and SVG. |
@annevk wrote
I agree that, apart from serialization (which use of Typed OM should minimize) preserving the minutiae of input syntax, particularly the mess of sRGB legacy syntaxes, has not much value and should not be the primary goal of a CSS Color model, far less a Web-wide color model if that is what we are designing (still unclear on that). Color serialization was moved from CSSOM, where it was sRGB only and 8bit only, to CSS Color 4 by CSS WG resolution. Currently all the legacy sRGB syntaxes - However, the
Re-use of concepts has certainly been great, for example the Canvas HDR proposal re-uses a lot from CSS Color 4. As you say, some syntax details are already not preserved and are not the most important aspect anyway. But one reason the string-based syntax stuff worked okay was that it didn't have much to do. The only colorspace was sRGB so there was no need for colorspace conversion when mixing, interpolating, compositing or animating. As we move into a Wide Clor Gamut and High Dynamic Range world, those conversions are necessary and commonplace. |
So all the changes you can think of are about syntax, not functionality?! No wonder you think this would be sufficient (though having to deal with objects instead of numbers would be really painful, think of how bad the SVG DOM is — and that wasn't even on my radar!) Off the top of my head (and based on my work and research on this with @svgeesus for the last year or so), I think that to cater to enough author use cases, a color object for the Web would need to at least:
I honestly don't think it would be a good idea to hang all of that off of a Typed OM object, nor are they in scope for what the Typed OM does. And especially now that you pointed out this issue with dealing with numbers, I'm even more convinced this is a bad idea that will lead to a lot more developer pain than just having to do What do you mean by "magic tear-offs"? The difference needs to be preserved for Typed OM, since it's preserved for serialization. Not to mention that |
No, I was just focusing on things that I might change from our current model. Things that would be useful to add could definitely be added! In particular, most of the bullet points you listed are absolutely reasonable to go in Typed OM; a few are slightly more questionable for this API but definitely within the realm of possibility. Just because the list is pretty long and getting off-topic for this issue, I've filed #1040 to track them.
Whenever an object has a sub-object that is intimately tied to it, so that manipulating the sub-object fiddles with the state of the super-object, it's a "tear-off". Compare to a sub-object that is just its own encapsulated state, which the super-object can reference at some well-defined time to do some operation. A tear-off example is y'all's Color.js object, with its .rgb and .lab sub-objects, where the sub-objects are just "windows" into the Color object's state; any changes to the Color object are immediately reflected in the sub-objects, and mutating the sub-object immediately changes the Color object. You can't (I assume?) take the .rgb object from one Color instance and set it to another Color instance and get something useful out of that. A non-tear-off example is the current TypedOM classes. Some of them have sub-objects, like the list of transform components in CSSTransformValue. But they're not linked to each other in any intimate way; the super-object doesn't even look at the sub-objects until they're passed to .set(). Between those times, you can fiddle with either and the other is unaffected; if you reuse an transform component across multiple CSSTransformValue objects, that's fine. This is an important part of Typed OM objects being "not live" that we decided on very early. Tear-offs have lifetime implications: the super- and sub-objects keep each other alive, and they have to maintain a 1:1 correspondence; this complicates implementation. Their behavior is often mildly confusing to authors and can cause subtle bugs, when authors don't anticipate the long-term connection they have with each other, and mutating one of them unexpectedly mutates the other. The sub-objects usually can't be constructed on their own; they only come as a package with the super-object. (There are some cases which look kinda halfway between, but are for the most part in the "non" case, like URL and URLSearchParams. A URL object comes with its own URLSearchParams, which you can't replace. But they don't communicate with each other except when you actually use the URL, at which point it reads all of its attributes; they don't keep each other alive (if you just grab the URLSearchParams object and drop the URL, the URL can get collected safely); and you can construct independent URLSearchParams objects without referencing a URL at all.) So, they're not verboten, they're just good to avoid when we can shape the API in another way. Sometimes they're unavoidable, but usually they're not. |
I'm writing the spec for color values right now, and the overall structure is straightforward: a CSSColorValue superclass, and subclasses for each type of color function.
I'm struggling a bit with the best design for the constructor arguments and/or the shorthand functions.
Some arguments, like hue angles, are easy - they're just a CSSNumericValue that needs to match
<angle>
. But most of the arguments to color functions are percentages. Obviously I'll accept a CSSNumericValue that matches<percentage>
, but I'd like easier ways to invoke these - writingnew CSSRGB(CSS.percent(10), CSS.percent(0), CSS.percent(100))
isn't great.In particular, I'd like to allow them to accept JS numbers, interpreting the range 0-1 as being equivalent to a percent between 0% and 100%, so you could instead write the preceding function as
new CSSRGB(.1, 0, 1)
.This would generally be unproblematic and straightforward, except that
rgb()
accepts<number>
as well, in the range 0-255. And elsewhere in TypedOM (such asnew CSSScale()
), passing a raw JS number is equivalent to passing aCSSUnitValue(x, "number")
(see the "rectify a numberish value" algo). So this leaves me with several possibilities, none of which I find great:Always allow JS numbers in the color functions in the 0-1 range. There's no constructor form that's a direct equivalent to the
rgb(0, 128, 255)
syntax.Allow JS numbers in the color functions in the 0-1 range, but also allow
CSSUnitValue(x, "number")
to be passed to CSSRGB, with the 0-255 range.Don't allow JS numbers at all, only CSSNumericValues. CSSRGB accepts
CSSUnitValue(x, "number")
in the 0-255 range.(Definitely bad, not doing this) Allow JS numbers in the 0-1 range for all the color functions except CSSRGB, allow them in the 0-255 range for CSSRGB (in addition to
CSSUnitValue(x, "number")
).Same as (3), but also have shorthand functions like
CSS.rgb()
(akin to theCSS.px()
family for numeric values) that act like (1).I'm considering going with (1), but (5) would be fine as well. I don't like the others very much. Anyone else have opinions?
The text was updated successfully, but these errors were encountered: