Skip to content

[css-animations-2] [web-animations-2] Animation trigger event #12314

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 12 commits into
base: main
Choose a base branch
from
Open
66 changes: 61 additions & 5 deletions css-animations-2/Overview.bs
Original file line number Diff line number Diff line change
Expand Up @@ -770,6 +770,8 @@ for an [=animation trigger=] that enters its [=animation trigger/active interval

The behavior of each value is defined in [[web-animations-2#trigger-behaviors]].

Issue: Should 'animation-trigger-behavior' allow multiple values (currently it does)?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, I don't think it does?

Copy link

@szager szager Jun 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The '#' in the syntax description from line 734 means it accepts a comma-separated list of behaviors.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh I think that has to do with the fact that multiple animations can be specified in a comma-separated list. Each animation will then be mapped 1:1 with a comma-separated list of triggers. Each trigger still only gets one behavior. Though idk if this has been incorrectly represented in the spec text.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, what @DavMila said is correct. I think this is called a coordinated list.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it; I will update the PR...


## The 'animation-trigger-timeline' property ## {#animation-trigger-timeline}

The 'animation-trigger-timeline' property specifies the <a>timeline</a>
Expand Down Expand Up @@ -962,18 +964,70 @@ The values of 'animation-trigger-exit-range-start' have the following meaning:
it defaults to 100%.
</dl>

## The 'animation-trigger-name' property ## {#animation-trigger-name}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this property is not a longhand sub-property of animation-trigger then IMO it should not be named using the scheme of animation-trigger-*. IIUC you meant to define this property as the event source declaration, am I right?
In that case, this raises some questions:

  1. Is that declaration tree scoped?
  2. Is the name reused for all event types that are emitted from this target?
  3. Does containment properties affect its scope?
  4. Can we lift this scope, same as the timeline-scope property does?
  5. Do names collide and override eachother? Or can they be used multiple times?
  6. Will these names be ever used only for the purpose (and scope) of animations?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After discussion with @flackr and @DavMila, I think the plan is to use a scoping mechanism similar to anchor positioning -- specifically, there will be a trigger-scope property to apply tree scoping, but in the absense of any applicable trigger-scope the scope will be global. As with anchor positioning, containment will not affect scope.

The name is only used to specify the event target; the event type is specified via animation-trigger-event or the animation-trigger shorthand.

I will change the property to trigger-name, to avoid confusion with the properties that can be set via animation-trigger shorthand.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, I still think that the type should be part of the event declaration on the target.
Is there a specific reason you want to put the type on the animation target? IMHO, from design perspective this seems to belong on the event target.


The 'animation-trigger-name' property associates an element with
all elements having an 'animation-trigger-event' property with
Comment on lines +969 to +970
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's not accurate to say we "associate elements". The event needs to be associated with a target, and the target here is an animation(s).

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't quite follow you here, but I will point out firstly that the term "target" appears to be overloaded here. Because we're working with events, and "event target" is a very well-understood concept, I'm using the term "target" to refer to the event target and not the element that will animate.

There is definitely an "association" happening here:

#event-target {
  trigger-name: --my-trigger;
}
#some-other-event-target {
  trigger-name: --my-trigger;
}
#animatable {
  animation-trigger-name: --my-trigger;
  animation-trigger-event: click;
}
#some-other-animatable {
  animation-trigger-name: --my-trigger;
  animation-trigger-event: keydown;
}

If a click is dispatched to either #event-target or #some-other-event-target, then #animatable will run its animations. If a keydown is disaptched to either #event-target or #some-other-event-target, #some-other-animatable will run its animations.

Is there some other term besides "association" that better describes the relationships between these elements?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because we have 2 elements in play here, I was trying to use "source" for the event target, and "target" for the animated element, but let's stick with event target.
I understand the association, we definitely need that mechanism.
I'm saying we associate the event target with an animation, and not really with another element. The animation is associated with its own target, which can be nulled or changed.

a trigger name matching a value in this element's 'animation-trigger-name'.
When an {{Event}} is dispatched with this element as {{Event/target}},
and with {{Event/type}} matching the <<trigger-event-type>> of a clause in the
'animation-trigger-event' property of another element, animation
playback on the other element is modified according to its
'animation-trigger-behavior' property, as described for
[=event-based animation triggers=].

<pre class='propdef'>
Name: animation-trigger-name
Value: none | [ <<trigger-name>> ]#
Initial: none
Applies to: all elements
Inherited: no
Percentages: N/A
Computed value: as specified
Animation type: not animatable
</pre>

<pre class=prod><dfn>&lt;trigger-name></dfn> = <<dashed-ident>></pre>

## The 'animation-trigger-event' property ## {#animation-trigger-event}

The 'animation-trigger-event' property is used to specify the criteria
for [=qualifying events=] that cause an [=event-based animation trigger=]
Comment on lines +994 to +995
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be the other way around? As in, the event source should declare the types that emit its name, and the event targets should register to the name alone?
This is normally how you would register an event. You add a handler per type, and then these handlers can publish the name to trigger behavior.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you give an example of the syntax you have in mind? It's not clear to me how to express the following using your suggestion:

#event-target {
  trigger-name: --my-trigger;
}
#some-other-event-target {
  trigger-name: --my-trigger;
}
#animatable {
  animation-trigger-name: --my-trigger;
  animation-trigger-event: click;
}
#some-other-animatable {
  animation-trigger-name: --my-trigger;
  animation-trigger-event: keydown;
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First of all, this example is rather confusing to me. Did you intentionally used an example with two identical names? Are they supposed to collide? Or to be aggregated?

So to simplify with 2 different names, I was thinking on something like:

#event-target {
  trigger-event: --my-trigger click;
}
#some-other-event-target {
  trigger-event: --my-other-trigger keydown;
}
#animatable {
  animation-trigger: repeat event(--my-other-trigger);
}
#some-other-animatable {
  animation-trigger: --my-other-trigger;
}

to perform its effect on its associated [=animation=]. It consists of two parts:
a <<trigger-event-type>> that is matched against the {{Event/type}} of a dispatched {{Event}},
and an <<trigger-name>> that is matched against the 'animation-trigger-name' property
of the event's {{Event/target}}. If both criteria match for a given event
the trigger will perform its effect on its associated [=animation=].

If the <<trigger-name>> is omitted from <<single-trigger-event>> declaration,
{{Event/target}} is matched against the element
having the 'animation-trigger-event' property itself.

<pre class='propdef'>
Name: animation-trigger-event
Value: none | [ <<single-trigger-event>> ]#
Initial: none
Applies to: all elements
Inherited: no
Percentages: N/A
Computed value: as specified
Animation type: not animatable
</pre>

<pre class=prod><dfn>&lt;single-trigger-event></dfn> = event ( <<trigger-event-type>> <<trigger-name>>? )</pre>
<pre class=prod><dfn>&lt;trigger-event-type></dfn> = <<custom-ident>></pre>

## The 'animation-trigger' property ## {#animation-trigger}

The 'animation-trigger' property is a [=shorthand property|shorthand=]
that sets 'animation-trigger-behavior', 'animation-trigger-timeline',
'animation-trigger-range-start', 'animation-trigger-range-end',
'animation-trigger-event', 'animation-trigger-range-start', 'animation-trigger-range-end',
'animation-trigger-exit-range-start', and 'animation-trigger-exit-range-end'
together in a single declaration,
specifying the [=animation trigger=] for an animation.
together in a single declaration, specifying the [=animation trigger=] for an animation.

<pre class='propdef'>
Name: animation-trigger
Value: <<single-animation-trigger>>#
Value: auto | none | <<single-animation-trigger>>#
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So far we intentionally didn't have a none nor auto values for the animation-trigger property, though this may change depending on what we resolve in #12119

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, probably if we do allow auto/none they should be part of the <single-animation-trigger> def and be allowed per trigger defintion.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I misunderstood before how coordinated lists were being used; I will fix this.

Initial: see individual properties
Applies to: all elements
Inherited: no
Expand All @@ -984,7 +1038,9 @@ Animation Type: not animatable
</pre>

<pre class=prod>
<dfn>&lt;single-animation-trigger></dfn> = <<single-animation-trigger-behavior>> || [ none | auto | [ [ <<dashed-ident>> | <<scroll()>> | <<view()>> ] [ normal | <<length-percentage>> | <<timeline-range-name>> <<length-percentage>>? ]{0,4} ] ]
<dfn>&lt;single-animation-trigger></dfn> = [ <<single-animation-trigger-behavior>> | [ <<single-trigger-event>> | <<single-timeline-trigger>> ] ] <<single-animation-trigger-behavior>>?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure I follow the logic here. Perhaps you meant to allow auto and none as possible alternatives and that's what got the syntax confusing here?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also me not understanding coordinated lists; will fix.


<dfn>&lt;single-timeline-trigger></dfn> = [ <<dashed-ident>> | <<scroll()>> | <<view()>> ] [ normal | <<length-percentage>> | <<timeline-range-name>> <<length-percentage>>? ]{0,4}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You changed the syntax of timeline here. timeline also accepts none and auto which you removed here.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also coordinated lists; will fix.

</pre>

# Animation Events # {#events}
Expand Down
Loading