Declarative custom functions
Nobody has claimed this yet.
- Dominant language
- Bikeshed
- Stars
- 4.9k
- Forks
- 816
- PR merge metrics
- PR metrics pending
Description
@tabatkins mentioned the idea of declarative custom functions in a comment on the custom units issue:
Like, pretend for a moment that we have simple custom functions, like:
@custom-function --fluid(--value) { arg-syntax: --value "<number>"; result: clamp(1rem, var(--value) * 1vw, var(--value) * 1rem); }
This seems too good to not follow up on. Custom functions would make it possible to write more readable css with less repetition.
Background
We can already increase readability and reduce repetition of expressions by using function like custom properties, as @mirisuzanne describes in her excellent article on CSS Custom Properties In The Cascade. This is available in browsers today.
There are however limits to what we can do with function-like custom properties, and there are issues with their readability.
Example
The following expression returns 0% at a minimum viewport size of 375px, and 100% at a maximum viewport size of 1920px.
clamp(0%, 100% * (100vw - 375px) / (1920px - 375px), 100%)
We could give this expression semantic meaning and reuse it almost everywhere by declaring it as the value of a custom property:
* {
--min-width: 0px;
--max-width: 2560px;
--fluid-ratio: clamp(0%, 100% * (100vw - var(--min-width)) / (var(--max-width) - var(--min-width)), 100%);
}
This custom property could then be used with "arguments" where needed:
p {
--min-width: 375px;
--max-width: 1920px;
font-size: mix(var(--fluid-ratio), 1rem, 1.25rem);
}
This is great, and should work in browsers today (except for divide by unit, that is specified in css-units-4 but not supported yet).
There are some limitations to this approach:
- It's not possible to read from the rule that
--min-widthand--max-widthare arguments for--fluid-ratio, nor that--fluid-ratiois a function like custom property. - We would run into trouble if we wanted to reuse the same "function" with separate parameters.
p {
/* This would not work. (Arguably a constructed example) */
--min-width: 375px;
--max-width: 1920px;
font-size: mix(var(--fluid-ratio), 1rem, 1.25rem);
--min-width: 375px;
--max-width: 700px;
padding: mix(var(--fluid-ratio), 1rem, 2rem);
}
Proposal
Updated based on feedback. The original text is available below for context.
Add syntax for declarative custom functions. The syntax could follow the example from @tabatkins comment with an at-rule to declare the function and the syntax of its arguments:
@custom-function --fluid-ratio(--min-width "<length>", --max-width "<length>") {
result: clamp(0%, 100% * (100vw - var(--min-width)) / (var(--max-width) - var(--min-width)), 100%);
}
This would work through regular var substitution with a slightly different handling of the arguments. The custom function could then be used anywhere a var function can be used:
p {
font-size: mix(--fluid-ratio(375px, 1920px), 1rem, 1.25rem);
padding: mix(--fluid-ratio(375px, 700px), 1rem, 2rem);
}
As this is based on variable substitution it is not limited to math functions and can return any value. It could be used to return one of background patterns from @LeaVerou's pattern gallery:
@custom-function --checkerboard(--size "<length>") {
result: linear-gradient(
45deg,
silver 25%,
transparent 25%,
transparent 75%,
silver 75%
)
0px 0px / var(--size) var(--size),
linear-gradient(
45deg,
silver 25%,
transparent 25%,
transparent 75%,
silver 75%
)
calc(var(--size) / 2) calc(var(--size) / 2) / var(--size) var(--size);
}
.used {
background: --checkerboard(32px);
}
There's an existing proposal for JS backed custom functions in Houdini. This proposal is related and would be a simple declarative version of that proposal, where in Houdini the substituted value would be provided by JS.
Original proposal text
### Proposal
Add syntax for declarative mathematical functions. The syntax could follow the example from @tabatkins comment with an at-rule to declare the function and the syntax of its arguments:
@custom-function --fluid-ratio(--min-width, --max-width) {
arg-syntax: --min-width "<length>", --max-width "<length>";
result: clamp(0%, 100% * (100vw - arg(--min-width)) / (arg(--max-width) - arg(--min-width)), 100%);
}
The custom function could then be used where mathematical expressions can be used:
p {
font-size: mix(--fluid-ratio(375px, 1920px), 1rem, 1.25rem);
padding: mix(--fluid-ratio(375px, 700px), 1rem, 2rem);
}
There are probably many reasons why this would be annoying or impossible to implement, but hopefully there's smarter people than me out there that can come up with a way to solve custom functions.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the proposal in issue #7490, then read the linked custom units discussion in issue #7379 and the Houdini custom-functions proposal in issue #857. Compare the suggested @custom-function syntax and argument substitution with the linked CSS Variables specification. Done would require an agreed, technically complete declarative custom-functions design.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- css
- Domain
- web-dev
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 25/100