mdn / content

The content behind MDN Web Docs
https://developer.mozilla.org
Other
9.19k stars 22.47k forks source link

What are possible result types of calc() #35554

Open David263 opened 2 months ago

David263 commented 2 months ago

MDN URL

https://developer.mozilla.org/en-US/docs/Web/CSS/calc

What specific section or headline is this issue about?

Entire document

What information was incorrect, unhelpful, or incomplete?

I visited this page to find out the exact datatype of the result of calc. The document states "the expression's result is used as the value for a CSS property", but this is unclear as to the datatype.

What did you expect to see?

The document should clearly state that the result of calc is a a css integer length in pixels, for example 24px, if this is true.

Do you have any supporting links, references, or citations?

I could not find this information in a quick Web search.

Do you have anything more you want to share?

No response

MDN metadata

Page report details * Folder: `en-us/web/css/calc` * MDN URL: https://developer.mozilla.org/en-US/docs/Web/CSS/calc * GitHub URL: https://github.com/mdn/content/blob/main/files/en-us/web/css/calc/index.md * Last commit: https://github.com/mdn/content/commit/bb48907e64eb4bf60f17efd7d39b46c771d220a0 * Document last modified: 2024-08-13T13:25:16.000Z
Josh-Cena commented 2 months ago

It can be anything. margin: calc(1em + 1em) gives you margin: 2em (which will resolve to px based on the font-size). margin: calc(1px + 1px) gives you margin: 2px. flex: calc(1 + 1) gives you flex: 2. There's no restrictions on what the result's type must be.

David263 commented 2 months ago

What is the result type of calc(1rem + 1px)? What is the result type of calc(1px + 1rem)? My claim is that the result datatype is undefined in the document.

Josh-Cena commented 2 months ago

There is nothing more granular in CSS than <length>. The answer is that 1px represents a physical length and 1rem also represents a physical length, so the result is whatever those two lengths add up to. In fact, all length units you use in CSS are underlyingly the same thing—just that they are relative to different units.

Think about this way: what's 1 meter + 1 inch? Is the result in meters or inches? The answer is it doesn't matter because you can write it both ways and get the same physical meaning.

David263 commented 2 months ago

So how is <length> represented in the computer? Is it a single-precision floating point number? If it is, then the document should be specific in saying that the result of calc is an internal IEEE real number, rounded to an integer (apparently), that represents a length value. In other words, a similar datatype to px. Thus, calccan be nested in calcexpressions just like pxvalues can.

Josh-Cena commented 2 months ago

If it is, then the document should be specific in saying that the result of calc is an internal IEEE real number, rounded to an integer (apparently)

It's not an integer if you have any unit—1 and 1px are two entirely different types in CSS. Every length is underlyingly px, because that's what actually matters to the computer when it draws on the screen. It can be a fractional pixel value—many devices can have subpixel graphics. You can retrieve the actual used value using getComputedValue, and you will get a value in px. Anything deeper than saying "lengths are fundamentally numbers of pixels" would be poking into the implementation details, which we are not interested in—CSS is a declarative high level language and talking about how it's represented in the computer doesn't help.

I don't think the information about "it's always px" is relevant for calc either. If you specify width: 1rem, it's also going to resolve to px when you retrieve the computed value; that's just how absolute lengths work.

About nested calc: we already mention this:

It is permitted to nest calc() functions, in which case, the inner ones are treated as simple parentheses.

Maybe @estelle can decide what's worth to be added to this page.

David263 commented 2 months ago

I have no idea who estelle is. So, you seem to finally admit that calc in fact does have a result value, and that it cannot be fundamentally different from some integer number of pixels. You don't seem to realize that the DOM keeps track of pixel distances to a fairly precise fraction of a pixel. (Precision means the number of decimal places in IEEE arithmetic.) So, if calc really rounds to an integer, it may not be as useful to the user as they hope or expect that it is. That much (the rounding) is already spelled out in the document.

So, we've come around in a circle to my original surmise, which was that calc generates a result in the px datatype, even if the expression is calc(1.23245rem).

This is the information I'd like to see added to the documentation, as I said in the OP, because currently nothing is said clearly about what the result of calc really is, as a datatype.

Perhaps you enjoy arguing, but I do not. You could have simply agreed from the start.

Josh-Cena commented 2 months ago

px is not a datatype. <length> is. I'm saying the entire assumption that there's anything different about rem and px is misleading, because width: 1.23245rem and width: calc(1.23245rem) are exactly equivalent and both will result in "a length value"—and CSS uses px as the canonical unit for lengths. calc doesn't change how length units work. It's like asking whether "1m + 1in" results in "a value of type meters" or "a value of type inch"—the answer is that it doesn't matter, it's just a length. The computer just happens to measure everything in meters but that's an implementation detail unrelated to how calc works.

You don't seem to realize that the DOM keeps track of pixel distances to a fairly precise fraction of a pixel. (Precision means the number of decimal places in IEEE arithmetic.) So, if calc really rounds to an integer, it may not be as useful to the user as they hope or expect that it is.

I explicitly said that "It can be a fractional pixel value" in my last comment and I have verified it with the following code:

<div style="width: 15.5px" id="a"></div>
<div style="width: calc(15px + 0.5px)" id="b"></div>
<script>
  console.log(getComputedStyle(document.getElementById('a')).width);
  console.log(getComputedStyle(document.getElementById('b')).width);
</script>

Both of them print 15.5px, so I don't know where the claim that it rounds the value comes from.

By the way, here's what I was saying when I said everything is px:

<div style="width: 1rem" id="a"></div>
<div style="width: calc(1rem)" id="b"></div>
<div style="width: calc(1rem + 1rem)" id="c"></div>
<script>
  console.log(getComputedStyle(document.getElementById('a')).width);
  console.log(getComputedStyle(document.getElementById('b')).width);
  console.log(getComputedStyle(document.getElementById('c')).width);
</script>

They print 16px, 16px, and 32px, respectively. Non-px units simply do not exist beyond the CSS source code.

Josh-Cena commented 2 months ago

Also the question of "what's the result type of calc" is also not "it's a <length>". As I said from the start, it's whatever the operands' types are. If you use calc on integers, you get an integer. If you use it on angles, you get an angle. If you use it on percentages, you get a percentage. All of these are distinct datatypes unrelated to length. Therefore I don't think it's ever meaningful to explicitly specify what the return type of calc is since "it's everything calculatable, and you know it when you see it".

David263 commented 2 months ago

I'm afraid I don't understand the article and your explanations. The article doesn't explain what calc is equivalent to in result value (I used "datatype" to mean this).

Is calc(2px) equivalent to 2px? I assume, yes. Is calc(2.5px) equivalent to 2.5px or 3px? The article doesn't say. What is calc(2.5px + 10pt)equivalent to? The article doesn't say. What is calc(100% - 1rem) equivalent to? The article doesn't say.

As to rounding, the article states:

When calc() is used where an [<integer>](https://developer.mozilla.org/en-US/docs/Web/CSS/integer) is expected, the value will be rounded to the nearest integer. So, calc(1.4) will result in a value of 1. If the fractional part of the value is exactly 0.5, the value will be rounded up. For example, calc(1.5) will result in a value of 2, while calc(-1.5) will round to -1.

Josh-Cena commented 2 months ago

As to rounding, the article states:

When calc() is used where an [<integer>](https://developer.mozilla.org/en-US/docs/Web/CSS/integer) is expected, the value will be rounded to the nearest integer. So, calc(1.4) will result in a value of 1. If the fractional part of the value is exactly 0.5, the value will be rounded up. For example, calc(1.5) will result in a value of 2, while calc(-1.5) will round to -1.

That specifically refers to calc used where an <integer> is expected. Some CSS properties take integers as values, such as grid-row-start and z-index. In these cases, it doesn't make sense to pass a floating point number to them, so the value has to be rounded. These are unitless numbers and have nothing to do with lengths, which we are discussing here.

Is calc(2px) equivalent to 2px? I assume, yes.

Yes.

Is calc(2.5px) equivalent to 2.5px or 3px? The article doesn't say.

2.5px. It would be truly surprising if it's anything else and I don't know why we would need to specifically point out that "the result of calc is the result of doing that math".

What is calc(2.5px + 10pt)equivalent to? The article doesn't say.

10pt represents a physical length in px. The result is 2.5px + 10pt * [the number of px in a pt].

What is calc(100% - 1rem) equivalent to? The article doesn't say.

100% represents a physical length. The result is 100% * [the length to which percentage values are relative] - 1rem.

All of these examples are calculating based on <length>. The result is just addition and subtraction of length values. The unit doesn't matter.

Josh-Cena commented 2 months ago

calc is a function overloaded across many data types. calc(1.5 + 1) and calc(1.5px + 1px) are two distinct operations that have distinct result types. If it helps, you can even think of them as separate implementations: calcInteger(1.5 + 1) and calcLength(1.5px + 1px). CSS selects the correct implementation for each calc based on what data type it expects in each context. If you use it in flex: calc(...) calc(...) calc(...), then based on the syntax of flex, which is <number> <number> <length> (simplified), it would substitute the original with flex: calcNumber(...) calcNumber(...) calcLength(...), and then perform each calculation. The only calc implementation that does rounding is calcInteger because, well, the result has to be an integer.

David263 commented 2 months ago

I think you have almost converged to the text that needs to be added to the article to make what calc does clear. The article should describe overloading clearly, and list the separate datatypes.

It should list all integer styles, such as grid-row-start, and say that calc generates an integer in this case. It should also state that integers cannot be mixed with lengths in the same calc expression.

Quite a bit of additional text is needed to make the article clear, although it can be orthogonalized by the use of tables. For example, grid-row-start and the other integer styles should be listed in the table.

Your concept that no one needs to know what datatype calc results in should be explained clearly, because it is confusing as you first stated it. For example, here is my attempt at the explanation:

"Calc expressions can only appear in style values. Each such style value has its own datatype. The supported datatypes are shown in the following table (...). Calc expressions can only contain the datatypes appropriate for the style in which they appear, as shown in the table. The result of any calc expression is of that datatype, so, for example, for all styles, calc results in a decimal point representation of the number of pixels (same as the px literal). This means that calc cannot mix together different datatypes, since this situation would never occur for actual style datatypes. For example, calc(grid-row-start + 3px) is invalid."

I wish I had time to do this, but I'm working on an important website that needs to be released as soon as possible, so my time for writing, checking, and revising is quite limited.

Josh-Cena commented 2 months ago

I that that makes sense to be added to the page. We can list all datatypes that are calculable and give examples of each.

David263 commented 2 months ago

I think we have reached agreement. Thanks for the discussion; it has clarified the datatype implications of calc for me.

pransh15 commented 2 months ago

Hi @David263. I want to remind you of Mozilla's Community Participation Guidelines (CPG), which ensure that our community remains a welcoming and respectful place for all participants. Being impolite or rude will not be tolerated in any MDN space, including online spaces like this repository. We must communicate with each other in a constructive and considerate manner, especially when we disagree. If you have concerns, let's work together respectfully to address them.

Please be aware that violations of the CPG may result in consequences, including temporary or permanent suspension from participating in discussions or contributing to our projects. You can review the CPG here. Let's keep our conversations aligned with these values so everyone feels comfortable and respected. Thanks for understanding!

David263 commented 2 months ago

I don't think I was rude or abusive in any way or form. First, I stated, "I think we have reached agreement." We had been having a lively discussion about the datatypes of the result of calc. This discussion was held completely objectively, such that only facts were in discussion, and agreement was finally reached between our two points of view. I was thanking you for the discussion, which is the opposite of being rude. It was being clear and objective.

Second, I stated, "Thanks for the discussion; it has clarified the datatype implications of calc for me." This was neither rude nor abusive, but grateful to you for our discussion.

I'm frankly puzzled how any of my statements here could be considered an abuse of the community standards for contributions, and I'd be grateful if you could reply and explain to me how what I said was rude or abusive. However, if you do not wish to answer this puzzlement, I'm fine.

Finally, I'd like again to gently suggest editing the document to add the clarifications that we discussed. I'm willing to do this myself, if that would be helpful. I would just need instructions on how to make document changes.

I have tried in this and other comments in this thread to be respectful and civil, and again I am puzzled as to how any other interpretation could happen.

pransh15 commented 2 months ago

I'm happy to share clarification here, David.

My comment was not for anything that happened at the end of the conversation. To be clear, the tone of certain messages in the discussion comes across as frustrated, confrontational, and somewhat condescending. More particularly phrases like "you seem to finally admit," "you don't seem to realize," and "you could have simply agreed from the start," share a sense of irritation and a lack of openness to discussion, which is conveyed through the language. I understand if there is intent on proving a point but we encourage fostering a constructive conversation.

It's important that we communicate with each other in a constructive and considerate manner, even when we disagree. There are no consequences right now because the overall conduct has been fine, but I would not want anyone in the community to have a sour taste while talking to folks in the Mozilla community.

David263 commented 2 months ago

Thank you for providing specific feedback. I now understand your point, and I will try to be more careful of the words I use in the future, especially of not expressing frustration, irritation, confrontation, condescension, or a lack of openness. I will print a sign and keep it next to my computer. Unfortunately, I was raised by parents who did not teach me good social skills, so I have to learn this at age 78. Better late than never.

Again, I hope the article is updated to make the datatype of calc crystal clear.

OnkarRuikar commented 2 months ago

I'm willing to do this myself, if that would be helpful. I would just need instructions on how to make document changes.

@David263, we are always eager to have new contributors. There are two ways the document can be changed:

  1. Online on GitHub: It is an easier way to do minor modifications like updating sections, correcting typos etc.

    https://github.com/user-attachments/assets/3279df77-ee25-4172-bf56-f3ca91048f24

    In some cases, you may need a little understanding of the Markdown language.

  2. From your computer using an IDE (VSCode, Eclipse, IntelliJ IDEA, etc.). This is required to make major changes like complete page revamps and add, move, and delete documents. The initial setup may look big, but it's a one-time thing. To create new documents, you may refer to the page templates.

hamishwillee commented 2 months ago

FWIW you don't even need an IDE - I often use Notepad++. VSCode does make it easier to format according to MDN style and run prettier etc.

@David263 It really is pretty easy to set up. The one thing missing from the guide above though is that the toolchain runs on nodejs/typescript. it is worth installing nvm to manage your node versions because the supported version of node gets updated often enough that you want it to be easy to update.