mirror of
https://github.com/danog/sass-site.git
synced 2024-11-27 04:24:50 +01:00
Document first-class calc() (#575)
See sass/sass#818 Co-authored-by: Awjin Ahn <awjin@google.com>
This commit is contained in:
parent
e55b07cdd4
commit
d0e401e3f9
@ -210,6 +210,14 @@ if (window.location.hash) {
|
||||
"#configuring-modules": "/documentation/at-rules/use#configuration"
|
||||
};
|
||||
|
||||
var redirect = redirects[window.location.hash];
|
||||
if (redirect) window.location.href = redirect;
|
||||
} else if (window.location.pathname == "/documentation/syntax/special-functions") {
|
||||
var redirects = {
|
||||
"#calc-clamp-element-progid-and-expression": "/documentation/syntax/special-functions#element-progid-and-expression",
|
||||
"#min-and-max": "/documentation/values/calculations#min-and-max"
|
||||
};
|
||||
|
||||
var redirect = redirects[window.location.hash];
|
||||
if (redirect) window.location.href = redirect;
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ title: sass:meta
|
||||
## Mixins
|
||||
|
||||
<% function 'meta.load-css($url, $with: null)' do %>
|
||||
<% impl_status dart: '(unreleased)', libsass: false, ruby: false do %>
|
||||
<% impl_status dart: '1.23.0', libsass: false, ruby: false do %>
|
||||
Only Dart Sass currently supports this mixin.
|
||||
<% end %>
|
||||
|
||||
@ -97,6 +97,43 @@ title: sass:meta
|
||||
|
||||
## Functions
|
||||
|
||||
<% function 'meta.calc-args($calc)', returns: 'list' do %>
|
||||
<% impl_status dart: '1.40.0', libsass: false, ruby: false %>
|
||||
|
||||
Returns the arguments for the given [calculation].
|
||||
|
||||
[calculation]: ../values/calculations
|
||||
|
||||
If an argument is a number or a nested calculation, it's returned as that
|
||||
type. Otherwise, it's returned as an unquoted string.
|
||||
|
||||
<% example(autogen_css: false) do %>
|
||||
@debug meta.calc-args(calc(100px + 10%)); // unquote("100px + 10%")
|
||||
@debug meta.calc-args(clamp(50px, var(--width), 1000px)); // 50px, unquote("var(--width)"), 1000px
|
||||
===
|
||||
@debug meta.calc-args(calc(100px + 10%)) // unquote("100px + 10%")
|
||||
@debug meta.calc-args(clamp(50px, var(--width), 1000px)) // 50px, unquote("var(--width)"), 1000px
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
|
||||
<% function 'meta.calc-name($calc)', returns: 'quoted string' do %>
|
||||
<% impl_status dart: '1.40.0', libsass: false, ruby: false %>
|
||||
|
||||
Returns the name of the given [calculation].
|
||||
|
||||
[calculation]: ../values/calculations
|
||||
|
||||
<% example(autogen_css: false) do %>
|
||||
@debug meta.calc-name(calc(100px + 10%)); // "calc"
|
||||
@debug meta.calc-name(clamp(50px, var(--width), 1000px)); // "clamp"
|
||||
===
|
||||
@debug meta.calc-name(calc(100px + 10%)) // "calc"
|
||||
@debug meta.calc-name(clamp(50px, var(--width), 1000px)) // "clamp"
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
|
||||
<% function 'meta.call($function, $args...)', 'call($function, $args...)' do %>
|
||||
<%= partial 'documentation/snippets/call-impl-status' %>
|
||||
|
||||
@ -476,6 +513,7 @@ title: sass:meta
|
||||
* [`color`](../values/colors)
|
||||
* [`list`](../values/lists)
|
||||
* [`map`](../values/maps)
|
||||
* [`calculation`](../values/calculations)
|
||||
* [`bool`](../values/booleans)
|
||||
* [`null`](../values/null)
|
||||
* [`function`](../values/functions)
|
||||
|
@ -28,6 +28,8 @@ different types:
|
||||
equal to space-separated lists, and bracketed lists aren't equal to
|
||||
unbracketed lists.
|
||||
* [Maps][] are equal if their keys and values are both equal.
|
||||
* [Calculations] are equal if their names and arguments are all equal.
|
||||
Operation arguments are compared textually.
|
||||
* [`true`, `false`][], and [`null`][] are only equal to themselves.
|
||||
* [Functions][] are equal to the same function. Functions are compared *by
|
||||
reference*, so even if two functions have the same name and definition they're
|
||||
@ -42,6 +44,7 @@ different types:
|
||||
[`true`, `false`]: ../values/booleans
|
||||
[`null`]: ../values/null
|
||||
[Maps]: ../values/maps
|
||||
[Calculations]: ../values/calculations
|
||||
[Functions]: ../values/functions
|
||||
|
||||
<% example(autogen_css: false) do %>
|
||||
@ -92,6 +95,9 @@ different types:
|
||||
@debug $theme == ("venus": #998099, "nebula": #d2e1dd) // true
|
||||
@debug $theme != ("venus": #998099, "iron": #dadbdf) // true
|
||||
|
||||
@debug calc(10px + 10%) == calc(10px + 10%) // true
|
||||
@debug calc(10% + 10px) == calc(10px + 10%) // false
|
||||
|
||||
@debug true == true // true
|
||||
@debug true != false // true
|
||||
@debug null != false // true
|
||||
|
@ -84,22 +84,34 @@ calls][]—it's parsed as a normal [plain CSS function call][].
|
||||
font-weight: 400
|
||||
<% end %>
|
||||
|
||||
## `calc()`, `clamp()`, `element()`, `progid:...()`, and `expression()`
|
||||
## `element()`, `progid:...()`, and `expression()`
|
||||
|
||||
<% impl_status dart: "1.31.0", libsass: false, ruby: false, feature: "clamp()" do %>
|
||||
LibSass, Ruby Sass, and older versions of Dart Sass treat `clamp()` as a
|
||||
[plain CSS function] rather than supporting special syntax within it.
|
||||
<% impl_status dart: "<1.40.0", libsass: false, ruby: false, feature: "calc()" do %>
|
||||
LibSass, Ruby Sass, and versions of Dart Sass prior to 1.40.0 parse `calc()`
|
||||
as special syntactic function like `element()`.
|
||||
|
||||
[plain CSS function]: ../at-rules/function#plain-css-functions
|
||||
Dart Sass versions 1.40.0 and later parse `calc()` as a [calculation].
|
||||
|
||||
[calculation]: ../values/calculations
|
||||
<% end %>
|
||||
|
||||
The [`calc()`], [`clamp()`] and [`element()`] functions are defined in the CSS
|
||||
spec. Because `calc()`'s mathematical expressions conflict with Sass's
|
||||
arithmetic, and `element()`'s IDs could be parsed as colors, they need special
|
||||
parsing.
|
||||
<% impl_status dart: ">=1.31.0 <1.40.0", libsass: false, ruby: false, feature: "clamp()" do %>
|
||||
LibSass, Ruby Sass, and versions of Dart Sass prior to 1.31.0 parse `clamp()`
|
||||
as a [plain CSS function] rather than supporting special syntax within it.
|
||||
|
||||
[plain CSS function]: ../at-rules/function#plain-css-functions
|
||||
|
||||
Dart Sass versions between 1.31.0 and 1.40.0 parse `clamp()` as special
|
||||
syntactic function like `element()`.
|
||||
|
||||
Dart Sass versions 1.40.0 and later parse `clamp()` as a [calculation].
|
||||
|
||||
[calculation]: ../values/calculations
|
||||
<% end %>
|
||||
|
||||
The [`element()`] function is defined in the CSS spec, and because its IDs could
|
||||
be parsed as colors, they need special parsing.
|
||||
|
||||
[`calc()`]: https://developer.mozilla.org/en-US/docs/Web/CSS/calc
|
||||
[`clamp()`]: https://developer.mozilla.org/en-US/docs/Web/CSS/clamp
|
||||
[`element()`]: https://developer.mozilla.org/en-US/docs/Web/CSS/element
|
||||
|
||||
[`expression()`][] and functions beginning with [`progid:`][] are legacy
|
||||
@ -115,105 +127,18 @@ Nothing is interpreted as a SassScript expression, with the exception that
|
||||
[interpolation][] can be used to inject dynamic values.
|
||||
|
||||
<% example do %>
|
||||
@use "sass:math";
|
||||
$logo-element: logo-bg;
|
||||
|
||||
.logo {
|
||||
$width: 800px;
|
||||
width: $width;
|
||||
position: absolute;
|
||||
left: calc(50% - #{math.div($width, 2)});
|
||||
top: 0;
|
||||
background: element(##{$logo-element});
|
||||
}
|
||||
===
|
||||
@use "sass:math"
|
||||
$logo-element: logo-bg
|
||||
|
||||
.logo
|
||||
$width: 800px
|
||||
width: $width
|
||||
position: absolute
|
||||
left: calc(50% - #{math.div($width, 2)})
|
||||
top: 0
|
||||
background: element(##{$logo-element})
|
||||
===
|
||||
.logo {
|
||||
width: 800px;
|
||||
position: absolute;
|
||||
left: calc(50% - 400px);
|
||||
top: 0;
|
||||
}
|
||||
<% end %>
|
||||
|
||||
## `min()` and `max()`
|
||||
|
||||
<% impl_status dart: "1.11.0", libsass: false, ruby: false do %>
|
||||
LibSass and Ruby Sass currently *always* parse `min()` and `max()` as Sass
|
||||
functions. To create a plain CSS `min()` or `max()` call for those
|
||||
implementations, you can write something like `unquote("min(#{$padding},
|
||||
env(safe-area-inset-left))")` instead.
|
||||
<% end %>
|
||||
|
||||
CSS added support for [`min()` and `max()` functions][] in Values and Units
|
||||
Level 4, from where they were quickly adopted by Safari [to support the
|
||||
iPhoneX][]. But Sass supported its own [`min()`][] and [`max()`][] functions
|
||||
long before this, and it needed to be backwards-compatible with all those
|
||||
existing stylesheets. This led for the need for extra-special syntactic
|
||||
cleverness.
|
||||
|
||||
[`min()` and `max()` functions]: https://drafts.csswg.org/css-values-4/#calc-notation
|
||||
[to support the iPhoneX]: https://webkit.org/blog/7929/designing-websites-for-iphone-x/
|
||||
[`min()`]: ../modules/math#min
|
||||
[`max()`]: ../modules/math#max
|
||||
|
||||
If a `min()` or `max()` function call is valid plain CSS, it will be compiled to
|
||||
a CSS `min()` or `max()` call. "Plain CSS" includes nested calls to
|
||||
[`calc()`][], [`env()`][], [`var()`][], `min()`, or `max()`, as well as
|
||||
[interpolation][]. As soon as any part of the call contains a SassScript feature
|
||||
like [variables][] or [function calls][], though, it's parsed as a call to
|
||||
Sass's core `min()` or `max()` function instead.
|
||||
|
||||
[`env()`]: https://developer.mozilla.org/en-US/docs/Web/CSS/env
|
||||
[`var()`]: https://developer.mozilla.org/en-US/docs/Web/CSS/var
|
||||
|
||||
<!-- TODO(nweiz): auto-generate this CSS once we're compiling with Dart Sass -->
|
||||
|
||||
<% example do %>
|
||||
$padding: 12px;
|
||||
|
||||
.post {
|
||||
// Since these max() calls don't use any Sass features other than
|
||||
// interpolation, they're compiled to CSS max() calls.
|
||||
padding-left: max(#{$padding}, env(safe-area-inset-left));
|
||||
padding-right: max(#{$padding}, env(safe-area-inset-right));
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
// Since these refer to a Sass variable without interpolation, they call
|
||||
// Sass's built-in max() function.
|
||||
padding-left: max($padding, 20px);
|
||||
padding-right: max($padding, 20px);
|
||||
}
|
||||
===
|
||||
$padding: 12px
|
||||
|
||||
.post
|
||||
// Since these max() calls don't use any Sass features other than
|
||||
// interpolation, they're compiled to CSS max() calls.
|
||||
padding-left: max(#{$padding}, env(safe-area-inset-left))
|
||||
padding-right: max(#{$padding}, env(safe-area-inset-right))
|
||||
|
||||
|
||||
.sidebar
|
||||
// Since these refer to a Sass variable without interpolation, they call
|
||||
// Sass's built-in max() function.
|
||||
padding-left: max($padding, 20px)
|
||||
padding-right: max($padding, 20px)
|
||||
===
|
||||
.post {
|
||||
padding-left: max(12px, env(safe-area-inset-left));
|
||||
padding-right: max(12px, env(safe-area-inset-right));
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
padding-left: 20px;
|
||||
padding-right: 20px;
|
||||
background: element(#logo-bg);
|
||||
}
|
||||
<% end %>
|
||||
|
201
source/documentation/values/calculations.html.md.erb
Normal file
201
source/documentation/values/calculations.html.md.erb
Normal file
@ -0,0 +1,201 @@
|
||||
---
|
||||
title: Calculations
|
||||
introduction: >
|
||||
Calculations are how Sass represents the `calc()` function, as well as similar
|
||||
functions like `clamp()`, `min()`, and `max()`. Sass will simplify these as
|
||||
much as possible, even if they're combined with one another.
|
||||
---
|
||||
|
||||
<% impl_status dart: '1.40.0', libsass: false, ruby: false do %>
|
||||
LibSass, Ruby Sass, and versions of Dart Sass prior to 1.40.0 parse `calc()`
|
||||
as a [special function] like `element()`.
|
||||
|
||||
[special function]: ../syntax/special-functions#element-progid-and-expression
|
||||
|
||||
LibSass, Ruby Sass, and versions of Dart Sass prior to 1.31.0 parse `clamp()`
|
||||
as a [plain CSS function] rather than supporting special syntax within it.
|
||||
Versions of Dart Sass between 1.31.0 and 1.40.0 parse `clamp()` as a [special
|
||||
function] like `element()`.
|
||||
|
||||
[plain CSS function]: ../at-rules/function#plain-css-functions
|
||||
<% end %>
|
||||
|
||||
<% example(autogen_css: false) do %>
|
||||
@debug calc(400px + 10%); // calc(400px + 10%)
|
||||
@debug calc(400px / 2); // 200px
|
||||
@debug min(100px, calc(1rem + 10%)); // min(100px, 1rem + 10%)
|
||||
===
|
||||
@debug calc(400px + 10%) // calc(400px + 10%)
|
||||
@debug calc(400px / 2) // 200px
|
||||
@debug min(100px, calc(1rem + 10%) ; // min(100px, 1rem + 10%)
|
||||
<% end %>
|
||||
|
||||
Calculations use a special syntax that's different from normal SassScript. It's
|
||||
the same syntax as the CSS `calc()`, but with the additional ability to use
|
||||
[Sass variables] and call [Sass functions]. This means that `/` is always a
|
||||
division operator within a calculation!
|
||||
|
||||
[Sass variables]: ../variables
|
||||
[Sass functions]: ../modules
|
||||
|
||||
<% fun_fact do %>
|
||||
The arguments to a Sass function call use the normal Sass syntax, rather than
|
||||
the special calculation syntax!
|
||||
<% end %>
|
||||
|
||||
You can also use [interpolation] in a calculation. However, if you do, nothing
|
||||
in the parentheses that surround that interpolation will be simplified or
|
||||
type-checked, so it's easy to end up with extra verbose or even invalid CSS.
|
||||
Rather than writing `calc(10px + #{$var})`, just write `calc(10px + $var)`!
|
||||
|
||||
[interpolation]: ../interpolation
|
||||
|
||||
## Simplification
|
||||
|
||||
Sass will simplify adjacent operations in calculations if they use units that
|
||||
can be combined at compile-time, such as `1in + 10px` or `5s * 2`. If possible,
|
||||
it'll even simplify the whole calculation to a single number—for example,
|
||||
`clamp(0px, 30px, 20px)` will return `20px`.
|
||||
|
||||
<% heads_up do %>
|
||||
This means that a calculation expression won't necessarily always return a
|
||||
calculation! If you're writing a Sass library, you can always use the
|
||||
[`meta.type-of()`] function to determine what type you're dealing with.
|
||||
|
||||
[`meta.type-of()`]: ../modules/meta#type-of
|
||||
<% end %>
|
||||
|
||||
Calculations will also be simplified within other calculations. In particular,
|
||||
if a `calc()` end up inside any other calculation, the function call will be
|
||||
removed and it'll be replaced by a plain old operation.
|
||||
|
||||
<% example do %>
|
||||
$width: calc(400px + 10%);
|
||||
|
||||
.sidebar {
|
||||
width: $width;
|
||||
padding-left: calc($width / 4);
|
||||
}
|
||||
===
|
||||
$width: calc(400px + 10%)
|
||||
|
||||
.sidebar
|
||||
width: $width
|
||||
padding-left: calc($width / 4)
|
||||
==
|
||||
.sidebar {
|
||||
width: calc(400px + 10%);
|
||||
padding-left: calc((400px + 10%) / 4);
|
||||
}
|
||||
<% end %>
|
||||
|
||||
## Operations
|
||||
|
||||
You can't use calculations with normal SassScript operations like `+` and `*`.
|
||||
If you want to write some math functions that allow calculations just write them
|
||||
within their own `calc()` expressions—if they're passed a bunch of numbers with
|
||||
compatible units, they'll return plain numbers as well, and if they're passed
|
||||
calculations they'll return calculations.
|
||||
|
||||
This restriction is in place to make sure that if calculations *aren't* wanted,
|
||||
they throw an error as soon as possible. Calculations can't be used everywhere
|
||||
plain numbers can: they can't be injected into CSS identifiers (such as
|
||||
`.item-#{$n}`), for example, and they can't be passed to Sass's built-in [math
|
||||
functions]. Reserving SassScript operations for plain numbers makes it clear
|
||||
exactly where calculations are allowed and where they aren't.
|
||||
|
||||
[math functions]: ../modules/math
|
||||
|
||||
<% example(autogen_css: false) do %>
|
||||
$width: calc(100% + 10px);
|
||||
@debug $width * 2; // Error!
|
||||
@debug calc($width * 2); // calc((100% + 10px) * 2);
|
||||
===
|
||||
$width: calc(100% + 10px);
|
||||
@debug $width * 2; // Error!
|
||||
@debug calc($width * 2); // calc((100% + 10px) * 2);
|
||||
<% end %>
|
||||
|
||||
## `min()` and `max()`
|
||||
|
||||
<% impl_status dart: ">=1.11.0 <1.40.0", libsass: false, ruby: false, feature: "Special function syntax" do %>
|
||||
LibSass, Ruby Sass, and versions of Dart Sass prior to 1.11.0 *always* parse
|
||||
`min()` and `max()` as Sass functions. To create a plain CSS `min()` or
|
||||
`max()` call for those implementations, you can write something like
|
||||
`unquote("min(#{$padding}, env(safe-area-inset-left))")` instead.
|
||||
|
||||
Versions of Dart Sass between 1.11.0 and 1.40.0 parse `min()` and `max()`
|
||||
functions as [special functions] if they're valid plain CSS, but parses them
|
||||
as Sass functions if they contain Sass features other than interpolation, like
|
||||
variables or function calls.
|
||||
|
||||
[special functions]: ../syntax/special-functions
|
||||
<% end %>
|
||||
|
||||
CSS added support for [`min()` and `max()` functions] in Values and Units Level
|
||||
4, from where they were quickly adopted by Safari [to support the iPhoneX]. But
|
||||
Sass supported its own [`min()`] and [`max()`] functions long before this, and
|
||||
it needed to be backwards-compatible with all those existing stylesheets. This
|
||||
led to the need for extra-special syntactic cleverness.
|
||||
|
||||
[`min()` and `max()` functions]: https://drafts.csswg.org/css-values-4/#calc-notation
|
||||
[to support the iPhoneX]: https://webkit.org/blog/7929/designing-websites-for-iphone-x/
|
||||
[`min()`]: ../modules/math#min
|
||||
[`max()`]: ../modules/math#max
|
||||
|
||||
If a `min()` or `max()` function call is a valid calculation expression, it will
|
||||
be parsed as a calculation. But as soon as any part of the call contains a
|
||||
SassScript feature that isn't supported in a calculation, like the [modulo
|
||||
operator], it's parsed as a call to Sass's core `min()` or `max()` function
|
||||
instead.
|
||||
|
||||
Since calculations are simplified to numbers when possible anyway, the only
|
||||
substantive difference is that the Sass functions only support units that can be
|
||||
combined at build time, so `min(12px % 10, 10%)` will throw an error.
|
||||
|
||||
[modulo operator]: ../operators/numeric
|
||||
|
||||
<!-- TODO(nweiz): auto-generate this CSS once we're compiling with Dart Sass -->
|
||||
|
||||
<% example do %>
|
||||
$padding: 12px;
|
||||
|
||||
.post {
|
||||
// Since these max() calls are valid calculation expressions, they're
|
||||
// parsed as calculations.
|
||||
padding-left: max($padding, env(safe-area-inset-left));
|
||||
padding-right: max($padding, env(safe-area-inset-right));
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
// Since these use the SassScript-only modulo operator, they're parsed as
|
||||
// SassScript function calls.
|
||||
padding-left: max($padding % 10, 20px);
|
||||
padding-right: max($padding % 10, 20px);
|
||||
}
|
||||
===
|
||||
$padding: 12px
|
||||
|
||||
.post
|
||||
// Since these max() calls are valid calculation expressions, they're
|
||||
// parsed as calculations.
|
||||
padding-left: max($padding, env(safe-area-inset-left))
|
||||
padding-right: max($padding, env(safe-area-inset-right))
|
||||
|
||||
|
||||
.sidebar
|
||||
// Since these use the SassScript-only modulo operator, they're parsed as
|
||||
// SassScript function calls.
|
||||
padding-left: max($padding % 10, 20px)
|
||||
padding-right: max($padding % 10, 20px)
|
||||
===
|
||||
.post {
|
||||
padding-left: max(12px, env(safe-area-inset-left));
|
||||
padding-right: max(12px, env(safe-area-inset-right));
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
padding-left: 20px;
|
||||
padding-right: 20px;
|
||||
}
|
||||
<% end %>
|
Loading…
Reference in New Issue
Block a user