w3c / w3c/csswg-drafts

Declarative custom functions

Open
#7,490 27 comments 9 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

css-variables-2
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:

  1. It's not possible to read from the rule that --min-width and --max-width are arguments for --fluid-ratio, nor that --fluid-ratio is a function like custom property.
  2. 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

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.