Skip to main content
Dublin Library

The Publishing Project

Defining multiple syntax in CSS variables

 

In Revisiting Custom Properties we looked at how to define custom properties using the @property at-rule.

One aspect of @property that we didn't discuss are the syntax multipliers and combinators.

These two gives us more flexibility when it comes to defining what our @property-defined variables can do.

Quick Review #

A CSS variable defined with @property looks like this:

@property --border-block {
	syntax: "<length-percentage>";
	inherits: false;
	initial-value: 2rem;
}

The name of the property follows the @property at rule and must have two dashes (--)

The following list, taken from the Supported Names section of the CSS Properties and Values API Level 1, shows the allowed values for the syntax attribute.

"<length>"
Any valid <length> value
"<number>"
<number> values
"<percentage>"
Any valid <percentage> value
"<length-percentage>"
Any valid <length> or <percentage> value, any valid <calc()> expression combining <length> and <percentage> components.
"<color>"
Any valid <color> value
"<image>"
Any valid <image> value
"<url>"
Any valid <url> value
"<integer>"
Any valid <integer> value
"<angle>"
Any valid <angle> value
"<time>"
Any valid <time> value
"<resolution>"
Any valid <resolution> value
"<transform-function>"
Any valid <transform-function> value
"<custom-ident>"
Any valid <custom-ident> value
Any sequence which starts an identifier, can be consumed as a name, and matches the <custom-ident> production
That identifier
"<transform-list>"
A list of valid <transform-function> values.
"<transform-list>" is a pre-multiplied data type name equivalent to "<transform-function>+"

The '+' and '#' Multipliers #

There are situations where we might want more than one value. For example, we might want to provide values for margins or padding, which can take one to four discreete values.

The two different multiplier values work in different context.

  • The + multiplier allows a space-separated list of values
  • The # multiplier allows a comma-separated list of values

Which one you use will depend on the attribute that you want to emulate. For examples using margins you can define them like so:

@property --margin-block {
	syntax: "<length-percentage>+";
	inherits: false;
	initial-value: 2rem;
}

margin-block can take one or two values.

If you use one value, the browser will use for both margin-block-start and margin-block-end.

If you use two values the first one becomes margin-block-start and the second margin-block-end.

First we change the @property definition to take one or more values by adding the + sign right after the syntax we want to repeat.

@property --margin-inline {
  syntax: "<length-percentage>+";
  inherits: true;
  initial-value: 2rem;
}

Next we define a generic class to hold settings common to all the examples.

.block {
  border: 2px solid hotpink;
  inline-size: 10rem;
  block-size: 4rem;

  margin-block-start: 4rem;
}

If we use the variable as is, it will use the initial value.

.block00 {
  margin-inline: 2rem

  /* These are equivalent */
  margin-inline: var(--margin-inline)
}

We can also change the value or values for the variable we created.

.block01 updates the variable with one value and .block02 provides to values to update the variable with.

.block01 {
  --margin-inline: 4rem;

  margin-inline: var(--margin-inline);
}

.block02 {
  --margin-inline: 6rem 2rem;

  margin-inline: var(--margin-inline);
}

Logical properties also account for writing modes. If we change the writing mode, the position of the inline margins will change too.

.block03 {
  --margin-inline: 4rem 2rem;

  writing-mode: vertical-lr;
  margin-inline: var(--margin-inline);
}

The '|' Combinator #

There are a few situations in CSS where one syntax descriptor is not going to be enough.

The case that comes to mind is line height. The property can take four different types of values:

  • normal (the default)
  • length
  • percentage
  • number

so how do we account for this when defining a property?

We could be pedantic and say that it only accepts numbers. If we do this then the number syntax matches our needs.

The length-percentage syntax handles both length and percentage values.

If we want to allow both number and length-percecntage syntaxes we can use the | combinator like this

@property --lh {
  syntax: '<number> | <length-percentage>';
  inherits: false;
  initial-value: 1;
}

This means that we can use any of the examples below.

  • p01 uses a number
  • p02 uses a length
  • p03 uses a percentage

If we don't update the value then it'll use the initial value we provided.

p01 {
  --lh: 1.2;
  line-height: var(--lh);
  font-size: 10pt
}

p02 {
  --lh: 1.2em;
  line-height: var(--lh);
  font-size: 10pt
}

p03 {
  --lh: 120%;
  line-height: var(--lh);
  font-size: 10pt
}

We can go further in our custom elements by using another combinator character to allow the auto keyword like this:

@property --lh {
  syntax: 'auto | <number> | <length-percentage>';
  inherits: false;
  initial-value: 1;
}

But the browser already does this and auto is the default value for the line-height property so it's redundant.

For our own CSS variables we may think of more complex combinators but I would always counsel for to KISS.

Edit on Github