w3c / w3c/css-houdini-drafts

[css-custom-functions] user-defined custom CSS functions

Open
#1,007 13 comments 22 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Bikeshed
Stars
1.9k
Forks
149
PR merge metrics
No merged PRs in 30d

Description

I wish there was a way to make re-usable CSS "functions".

Currently, we can't make re-usable logic without unfortunately coupling to HTML markup. See:

As you can tell from those pages, in order to make re-usable expressions ("functions"), we must couple the re-usability to the HTML markup, which is not ideal.

It would be great to be able to create re-usable logic/functions in a way that does not require a CSS author to ever touch any HTML code.

How might an proposal syntax look like, to work around the current limitation?

Perhaps:

:root {
  --sum: function( calc( var(--a) + var(--b) ) )
}

/* Then anywhere in any CSS rules, without dependence on HTML markup: */
.foo.bar.whatever {
  --a: 4px; --b: 6px;
  font-size: call(--sum);
}

^ In that example, the user does not have to worry about "evaluating CSS variables at the level where they are needed" (which requires the CSS author to touch the HTML markup as shown by @Afif13's nicely-written answer in the second SO question).

There's probably a bunch of other ways we could do it.

Here's another idea for sake of spinning up some ideas:

:root {
  --sum: function(--a, --b, calc( arg(--a) + arg(--b) ) )
}

/* Then anywhere in any CSS rules, without dependence on HTML markup: */
.foo.bar.whatever {
  font-size: call(--sum, 4px, 6px);
}

Or, maybe we can create a new special type of block (it may be cleaner):

@function sum(a, b) {
  return: calc(arg(a) + arg(b))
}

/* Then anywhere in any CSS rules, without dependence on HTML markup: */
.foo.bar.whatever {
  font-size: sum(4px, 6px);
}

/* Maybe `call()` or similar is required to distinguish from builtins? */
.foo.bar.whatever {
  font-size: call(sum, 4px, 6px);
}

Maybe functions can also return a rule expression, which could be "mixed" onto a rule (not sure about syntax though):

@function foo(a, b, c) {
  return {
    background: a b c;
  }
}

.whatever {
  call(foo, lightblue, url("foo.gif"), no-repeat); /* How to handle commas? */
  color: red;
}

results in equivalent of

.whatever {
  background: lightblue url("foo.gif") no-repeat;
  color: red;
}

or something.

With @function, maybe the return just has to match the context where the function is being used. For example in the previous two examples we saw it used to return a property value, and to return a rule that is mixed in the rule where it is used.

Maybe we could also use it at the top level to create top-level rules (not sure about the particular syntax, probably someone with CSS implementation expertise can weigh in):

@function makeKeyframes(name, a, b, c) {
  @keyframes arg(name) { ... something here ... what are a, b and c? ... }
}

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 by reviewing the issue's proposed custom-property, call(), and @function syntax examples and the linked discussions about CSS variable scoping. Done means reaching an agreed proposal for reusable CSS logic that does not require HTML coupling, including how value and rule results, arguments, and top-level rules should work.

Written by the indexing model from the issue text.

Assessment

Tech stack
css
Domain
frontend
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.