Starting style

Overview

The @starting-style at-rule is a CSS feature that allows for the creation of entry transitions for elements without using JS. When an element is displayed for the first time (for example when appended to the DOM or changing the display property from none) the rules inside a @starting-style that match the given element serve as the starting point of transitions. This allows for easy CSS-only creation of entry transitions. For example:

.my-element {
	transition: opacity 300ms ease;
	opacity: 1;
}

@starting-style {
	.my-element {
		opacity: 0;
	}
}

In the example above, elements will smoothly transition from the starting style (opacity 0) to their defined final state when they appear in the document.


Syntax

@starting-style {
	/* CSS body */
}

Description

The @starting-style rule defines entry transition starting values that apply only when an element enters the DOM or becomes matched by a selector for the first time. This enables developers to create “intro” transitions purely in CSS without needing to set temporary inline styles via JavaScript.

  • It only applies at the time of appearance — not on subsequent style changes.

  • Rules inside a starting style at-rule are treated as separate rules and have their own rule indices.

  • Inside the rule, you can write any valid CSS body, similar to a standard stylesheet. This means you can have other at-rules, media queries, rulesets, etc.

  • It works in conjunction with CSS transitions to define a smooth entry effect.

Behavior and Limitations

  • Gameface does not support the starting styles with out of line SVGs.

  • Custom expressions are not supported inside starting style rules.

  • Currently, Gameface does not support the selector-scoped usage such as:

.fade-in {
	opacity: 1;
	@starting-style {
		opacity: 0;
	}
}
  • @starting-style can also transition to initial values of properties, as in this example:
.my-element {
	/* This is the initial state of the opacity property */
	/* opacity: 1; */
}

@starting-style {
	.my-element {
		opacity: 0;
	}
}
  • Starting style can also be skipped in some cases when used with pseudo elements, as explained below.

Skipping starting style with pseudo elements

Transitions for pseudo elements which use @starting-style, could potentially not happen if you have a pseudo element class without any content in it, before you add the content to the pseudo element. Here is an example on when this issue can occur:

<style>
	.pseudo::after {
		content: "after";
		display: block;
		transition: color 5s ease;
	}

	/* This causes the pseudo element to be style solved before it is actually visible
	and this will skip the whole starting style flow. */
	.frozen::after {
		transition: color 99999999s !important;
	}

	@starting-style {
		.pseudo::after {
			color: red;
		}
	}
</style>
<div class="frozen"></div>
<script>
	requestAnimationFrame(() => {
		document.querySelector(".frozen").classList.add("pseudo");
	});
</script>

This is happening, because Gameface currently creates and solves pseudo elements even if they have no content, which in turn skips the whole starting style flow. By the time the pseudo element has content and it becomes visible it has already been solved which means this is not its first style update and then the starting style is no longer effective.

The workaround for this is to not create pseudo elements until you would like to transition them, meaning you should add all of the properties into a single rule:

<style>
	.pseudo::after {
		content: "after";
		display: block;
		/* transition: color 5s ease; */
		/* The transition property moved to here */
		transition: color 99999999s !important;
	}

	@starting-style {
		.pseudo::after {
			color: red;
		}
	}
</style>
<div class="frozen"></div>
<script>
	requestAnimationFrame(() => {
		document.querySelector(".frozen").classList.add("pseudo");
	});
</script>