From 1abec6f3a2fe954dda4d919e8e81b677c9578e27 Mon Sep 17 00:00:00 2001 From: Kevin Segaud Date: Sun, 1 Mar 2026 11:10:17 +0100 Subject: [PATCH 1/4] feat: add typed CssAnimation property support Add strongly-typed CssAnimation class and related sub-types (CssAnimationDirection, CssAnimationFillMode, CssAnimationPlayState, CssAnimationIterationCount) for the animation CSS shorthand property. Reuses existing CssDuration and CssTimingFunction from css_transition. Closes #66 --- .../lib/src/css_types/css_animation.dart | 344 ++++++++++++++++++ .../lib/src/css_types/css_types.dart | 1 + packages/spark_css/lib/src/style.dart | 2 + .../spark_css/test/css_animation_test.dart | 177 +++++++++ 4 files changed, 524 insertions(+) create mode 100644 packages/spark_css/lib/src/css_types/css_animation.dart create mode 100644 packages/spark_css/test/css_animation_test.dart diff --git a/packages/spark_css/lib/src/css_types/css_animation.dart b/packages/spark_css/lib/src/css_types/css_animation.dart new file mode 100644 index 0000000..3305a08 --- /dev/null +++ b/packages/spark_css/lib/src/css_types/css_animation.dart @@ -0,0 +1,344 @@ +import 'css_transition.dart'; +import 'css_value.dart'; + +/// CSS animation-direction values. +sealed class CssAnimationDirection implements CssValue { + const CssAnimationDirection._(); + + static const CssAnimationDirection normal = + _CssAnimationDirectionKeyword('normal'); + static const CssAnimationDirection reverse = + _CssAnimationDirectionKeyword('reverse'); + static const CssAnimationDirection alternate = + _CssAnimationDirectionKeyword('alternate'); + static const CssAnimationDirection alternateReverse = + _CssAnimationDirectionKeyword('alternate-reverse'); + + /// CSS variable reference. + factory CssAnimationDirection.variable(String varName) = + _CssAnimationDirectionVariable; + + /// Raw CSS value escape hatch. + factory CssAnimationDirection.raw(String value) = _CssAnimationDirectionRaw; + + /// Global keyword (inherit, initial, unset, revert). + factory CssAnimationDirection.global(CssGlobal global) = + _CssAnimationDirectionGlobal; +} + +final class _CssAnimationDirectionKeyword extends CssAnimationDirection { + final String keyword; + const _CssAnimationDirectionKeyword(this.keyword) : super._(); + + @override + String toCss() => keyword; +} + +final class _CssAnimationDirectionVariable extends CssAnimationDirection { + final String varName; + const _CssAnimationDirectionVariable(this.varName) : super._(); + + @override + String toCss() => 'var(--$varName)'; +} + +final class _CssAnimationDirectionRaw extends CssAnimationDirection { + final String value; + const _CssAnimationDirectionRaw(this.value) : super._(); + + @override + String toCss() => value; +} + +final class _CssAnimationDirectionGlobal extends CssAnimationDirection { + final CssGlobal global; + const _CssAnimationDirectionGlobal(this.global) : super._(); + + @override + String toCss() => global.toCss(); +} + +/// CSS animation-fill-mode values. +sealed class CssAnimationFillMode implements CssValue { + const CssAnimationFillMode._(); + + static const CssAnimationFillMode none = + _CssAnimationFillModeKeyword('none'); + static const CssAnimationFillMode forwards = + _CssAnimationFillModeKeyword('forwards'); + static const CssAnimationFillMode backwards = + _CssAnimationFillModeKeyword('backwards'); + static const CssAnimationFillMode both = + _CssAnimationFillModeKeyword('both'); + + /// CSS variable reference. + factory CssAnimationFillMode.variable(String varName) = + _CssAnimationFillModeVariable; + + /// Raw CSS value escape hatch. + factory CssAnimationFillMode.raw(String value) = _CssAnimationFillModeRaw; + + /// Global keyword (inherit, initial, unset, revert). + factory CssAnimationFillMode.global(CssGlobal global) = + _CssAnimationFillModeGlobal; +} + +final class _CssAnimationFillModeKeyword extends CssAnimationFillMode { + final String keyword; + const _CssAnimationFillModeKeyword(this.keyword) : super._(); + + @override + String toCss() => keyword; +} + +final class _CssAnimationFillModeVariable extends CssAnimationFillMode { + final String varName; + const _CssAnimationFillModeVariable(this.varName) : super._(); + + @override + String toCss() => 'var(--$varName)'; +} + +final class _CssAnimationFillModeRaw extends CssAnimationFillMode { + final String value; + const _CssAnimationFillModeRaw(this.value) : super._(); + + @override + String toCss() => value; +} + +final class _CssAnimationFillModeGlobal extends CssAnimationFillMode { + final CssGlobal global; + const _CssAnimationFillModeGlobal(this.global) : super._(); + + @override + String toCss() => global.toCss(); +} + +/// CSS animation-play-state values. +sealed class CssAnimationPlayState implements CssValue { + const CssAnimationPlayState._(); + + static const CssAnimationPlayState running = + _CssAnimationPlayStateKeyword('running'); + static const CssAnimationPlayState paused = + _CssAnimationPlayStateKeyword('paused'); + + /// CSS variable reference. + factory CssAnimationPlayState.variable(String varName) = + _CssAnimationPlayStateVariable; + + /// Raw CSS value escape hatch. + factory CssAnimationPlayState.raw(String value) = _CssAnimationPlayStateRaw; + + /// Global keyword (inherit, initial, unset, revert). + factory CssAnimationPlayState.global(CssGlobal global) = + _CssAnimationPlayStateGlobal; +} + +final class _CssAnimationPlayStateKeyword extends CssAnimationPlayState { + final String keyword; + const _CssAnimationPlayStateKeyword(this.keyword) : super._(); + + @override + String toCss() => keyword; +} + +final class _CssAnimationPlayStateVariable extends CssAnimationPlayState { + final String varName; + const _CssAnimationPlayStateVariable(this.varName) : super._(); + + @override + String toCss() => 'var(--$varName)'; +} + +final class _CssAnimationPlayStateRaw extends CssAnimationPlayState { + final String value; + const _CssAnimationPlayStateRaw(this.value) : super._(); + + @override + String toCss() => value; +} + +final class _CssAnimationPlayStateGlobal extends CssAnimationPlayState { + final CssGlobal global; + const _CssAnimationPlayStateGlobal(this.global) : super._(); + + @override + String toCss() => global.toCss(); +} + +/// CSS animation-iteration-count values. +sealed class CssAnimationIterationCount implements CssValue { + const CssAnimationIterationCount._(); + + static const CssAnimationIterationCount infinite = + _CssAnimationIterationCountKeyword('infinite'); + + /// Numeric iteration count. + factory CssAnimationIterationCount.count(num value) = + _CssAnimationIterationCountNumber; + + /// CSS variable reference. + factory CssAnimationIterationCount.variable(String varName) = + _CssAnimationIterationCountVariable; + + /// Raw CSS value escape hatch. + factory CssAnimationIterationCount.raw(String value) = + _CssAnimationIterationCountRaw; + + /// Global keyword (inherit, initial, unset, revert). + factory CssAnimationIterationCount.global(CssGlobal global) = + _CssAnimationIterationCountGlobal; +} + +final class _CssAnimationIterationCountKeyword + extends CssAnimationIterationCount { + final String keyword; + const _CssAnimationIterationCountKeyword(this.keyword) : super._(); + + @override + String toCss() => keyword; +} + +final class _CssAnimationIterationCountNumber + extends CssAnimationIterationCount { + final num value; + const _CssAnimationIterationCountNumber(this.value) : super._(); + + @override + String toCss() => '$value'; +} + +final class _CssAnimationIterationCountVariable + extends CssAnimationIterationCount { + final String varName; + const _CssAnimationIterationCountVariable(this.varName) : super._(); + + @override + String toCss() => 'var(--$varName)'; +} + +final class _CssAnimationIterationCountRaw extends CssAnimationIterationCount { + final String value; + const _CssAnimationIterationCountRaw(this.value) : super._(); + + @override + String toCss() => value; +} + +final class _CssAnimationIterationCountGlobal + extends CssAnimationIterationCount { + final CssGlobal global; + const _CssAnimationIterationCountGlobal(this.global) : super._(); + + @override + String toCss() => global.toCss(); +} + +/// CSS animation shorthand value. +sealed class CssAnimation implements CssValue { + const CssAnimation._(); + + static const CssAnimation none = _CssAnimationKeyword('none'); + + /// Single animation. + factory CssAnimation({ + required String name, + CssDuration? duration, + CssTimingFunction? timingFunction, + CssDuration? delay, + CssAnimationIterationCount? iterationCount, + CssAnimationDirection? direction, + CssAnimationFillMode? fillMode, + CssAnimationPlayState? playState, + }) = _CssAnimationSingle; + + /// Multiple animations. + factory CssAnimation.multiple(List animations) = + _CssAnimationMultiple; + + /// CSS variable reference. + factory CssAnimation.variable(String varName) = _CssAnimationVariable; + + /// Raw CSS value escape hatch. + factory CssAnimation.raw(String value) = _CssAnimationRaw; + + /// Global keyword (inherit, initial, unset, revert). + factory CssAnimation.global(CssGlobal global) = _CssAnimationGlobal; +} + +final class _CssAnimationKeyword extends CssAnimation { + final String keyword; + const _CssAnimationKeyword(this.keyword) : super._(); + + @override + String toCss() => keyword; +} + +final class _CssAnimationSingle extends CssAnimation { + final String name; + final CssDuration? duration; + final CssTimingFunction? timingFunction; + final CssDuration? delay; + final CssAnimationIterationCount? iterationCount; + final CssAnimationDirection? direction; + final CssAnimationFillMode? fillMode; + final CssAnimationPlayState? playState; + + const _CssAnimationSingle({ + required this.name, + this.duration, + this.timingFunction, + this.delay, + this.iterationCount, + this.direction, + this.fillMode, + this.playState, + }) : super._(); + + @override + String toCss() { + final parts = [name]; + if (duration != null) parts.add(duration!.toCss()); + if (timingFunction != null) parts.add(timingFunction!.toCss()); + if (delay != null) parts.add(delay!.toCss()); + if (iterationCount != null) parts.add(iterationCount!.toCss()); + if (direction != null) parts.add(direction!.toCss()); + if (fillMode != null) parts.add(fillMode!.toCss()); + if (playState != null) parts.add(playState!.toCss()); + return parts.join(' '); + } +} + +final class _CssAnimationMultiple extends CssAnimation { + final List animations; + const _CssAnimationMultiple(this.animations) : super._(); + + @override + String toCss() => animations.map((a) => a.toCss()).join(', '); +} + +final class _CssAnimationVariable extends CssAnimation { + final String varName; + const _CssAnimationVariable(this.varName) : super._(); + + @override + String toCss() => 'var(--$varName)'; +} + +final class _CssAnimationRaw extends CssAnimation { + final String value; + const _CssAnimationRaw(this.value) : super._(); + + @override + String toCss() => value; +} + +final class _CssAnimationGlobal extends CssAnimation { + final CssGlobal global; + const _CssAnimationGlobal(this.global) : super._(); + + @override + String toCss() => global.toCss(); +} diff --git a/packages/spark_css/lib/src/css_types/css_types.dart b/packages/spark_css/lib/src/css_types/css_types.dart index 59b4313..bd5d281 100644 --- a/packages/spark_css/lib/src/css_types/css_types.dart +++ b/packages/spark_css/lib/src/css_types/css_types.dart @@ -1,6 +1,7 @@ /// CSS type system for type-safe style declarations. library; +export 'css_animation.dart'; export 'css_angle.dart'; export 'css_background.dart'; export 'css_background_attachment.dart'; diff --git a/packages/spark_css/lib/src/style.dart b/packages/spark_css/lib/src/style.dart index 613a021..d66784f 100644 --- a/packages/spark_css/lib/src/style.dart +++ b/packages/spark_css/lib/src/style.dart @@ -312,6 +312,7 @@ class Style implements CssStyle { CssBackgroundOrigin? backgroundOrigin, CssBackgroundAttachment? backgroundAttachment, // Effects + CssAnimation? animation, CssTransition? transition, CssTransform? transform, // Background shorthand @@ -456,6 +457,7 @@ class Style implements CssStyle { } // Effects + if (animation != null) _properties['animation'] = animation.toCss(); if (transition != null) _properties['transition'] = transition.toCss(); if (transform != null) _properties['transform'] = transform.toCss(); diff --git a/packages/spark_css/test/css_animation_test.dart b/packages/spark_css/test/css_animation_test.dart new file mode 100644 index 0000000..d880ca3 --- /dev/null +++ b/packages/spark_css/test/css_animation_test.dart @@ -0,0 +1,177 @@ +import 'package:spark_css/src/css_types/css_animation.dart'; +import 'package:spark_css/src/css_types/css_transition.dart'; +import 'package:spark_css/src/css_types/css_value.dart'; +import 'package:test/test.dart'; + +void main() { + group('CssAnimationDirection', () { + test('keywords output correct CSS', () { + expect(CssAnimationDirection.normal.toCss(), equals('normal')); + expect(CssAnimationDirection.reverse.toCss(), equals('reverse')); + expect(CssAnimationDirection.alternate.toCss(), equals('alternate')); + expect( + CssAnimationDirection.alternateReverse.toCss(), + equals('alternate-reverse'), + ); + }); + test('variable outputs correct CSS', () { + expect( + CssAnimationDirection.variable('dir').toCss(), + equals('var(--dir)'), + ); + }); + test('raw outputs value as-is', () { + expect(CssAnimationDirection.raw('normal').toCss(), equals('normal')); + }); + test('global outputs correct CSS', () { + expect( + CssAnimationDirection.global(CssGlobal.inherit).toCss(), + equals('inherit'), + ); + }); + }); + + group('CssAnimationFillMode', () { + test('keywords output correct CSS', () { + expect(CssAnimationFillMode.none.toCss(), equals('none')); + expect(CssAnimationFillMode.forwards.toCss(), equals('forwards')); + expect(CssAnimationFillMode.backwards.toCss(), equals('backwards')); + expect(CssAnimationFillMode.both.toCss(), equals('both')); + }); + test('variable outputs correct CSS', () { + expect( + CssAnimationFillMode.variable('fill').toCss(), + equals('var(--fill)'), + ); + }); + test('raw outputs value as-is', () { + expect(CssAnimationFillMode.raw('both').toCss(), equals('both')); + }); + test('global outputs correct CSS', () { + expect( + CssAnimationFillMode.global(CssGlobal.initial).toCss(), + equals('initial'), + ); + }); + }); + + group('CssAnimationPlayState', () { + test('keywords output correct CSS', () { + expect(CssAnimationPlayState.running.toCss(), equals('running')); + expect(CssAnimationPlayState.paused.toCss(), equals('paused')); + }); + test('variable outputs correct CSS', () { + expect( + CssAnimationPlayState.variable('state').toCss(), + equals('var(--state)'), + ); + }); + test('raw outputs value as-is', () { + expect(CssAnimationPlayState.raw('paused').toCss(), equals('paused')); + }); + test('global outputs correct CSS', () { + expect( + CssAnimationPlayState.global(CssGlobal.unset).toCss(), + equals('unset'), + ); + }); + }); + + group('CssAnimationIterationCount', () { + test('infinite outputs correct CSS', () { + expect( + CssAnimationIterationCount.infinite.toCss(), + equals('infinite'), + ); + }); + test('count outputs correct CSS', () { + expect(CssAnimationIterationCount.count(3).toCss(), equals('3')); + expect(CssAnimationIterationCount.count(1.5).toCss(), equals('1.5')); + }); + test('variable outputs correct CSS', () { + expect( + CssAnimationIterationCount.variable('count').toCss(), + equals('var(--count)'), + ); + }); + test('raw outputs value as-is', () { + expect( + CssAnimationIterationCount.raw('infinite').toCss(), + equals('infinite'), + ); + }); + test('global outputs correct CSS', () { + expect( + CssAnimationIterationCount.global(CssGlobal.revert).toCss(), + equals('revert'), + ); + }); + }); + + group('CssAnimation', () { + test('none outputs correct CSS', () { + expect(CssAnimation.none.toCss(), equals('none')); + }); + test('name-only outputs correct CSS', () { + expect(CssAnimation(name: 'fadeIn').toCss(), equals('fadeIn')); + }); + test('single with all properties outputs correct CSS', () { + expect( + CssAnimation( + name: 'slideIn', + duration: CssDuration.s(1), + timingFunction: CssTimingFunction.easeInOut, + delay: CssDuration.ms(500), + iterationCount: CssAnimationIterationCount.infinite, + direction: CssAnimationDirection.alternate, + fillMode: CssAnimationFillMode.forwards, + playState: CssAnimationPlayState.running, + ).toCss(), + equals( + 'slideIn 1s ease-in-out 500ms infinite alternate forwards running', + ), + ); + }); + test('single with partial properties outputs correct CSS', () { + expect( + CssAnimation( + name: 'fadeIn', + duration: CssDuration.s(0.3), + fillMode: CssAnimationFillMode.forwards, + ).toCss(), + equals('fadeIn 0.3s forwards'), + ); + }); + test('multiple outputs correct CSS', () { + expect( + CssAnimation.multiple([ + CssAnimation(name: 'fadeIn', duration: CssDuration.s(1)), + CssAnimation( + name: 'slideUp', + duration: CssDuration.ms(500), + fillMode: CssAnimationFillMode.both, + ), + ]).toCss(), + equals('fadeIn 1s, slideUp 500ms both'), + ); + }); + test('variable outputs correct CSS', () { + expect( + CssAnimation.variable('anim').toCss(), + equals('var(--anim)'), + ); + }); + test('raw outputs value as-is', () { + expect( + CssAnimation.raw('fadeIn 1s ease-in-out').toCss(), + equals('fadeIn 1s ease-in-out'), + ); + }); + test('global outputs correct CSS', () { + expect( + CssAnimation.global(CssGlobal.inherit).toCss(), + equals('inherit'), + ); + }); + }); +} From 74965a200be925103510a688302a2e7f2b2a8fa9 Mon Sep 17 00:00:00 2001 From: Kevin Segaud Date: Sun, 1 Mar 2026 11:11:15 +0100 Subject: [PATCH 2/4] format --- .../lib/src/css_types/css_animation.dart | 41 +++++++++++-------- .../spark_css/test/css_animation_test.dart | 15 ++----- 2 files changed, 26 insertions(+), 30 deletions(-) diff --git a/packages/spark_css/lib/src/css_types/css_animation.dart b/packages/spark_css/lib/src/css_types/css_animation.dart index 3305a08..7c657ce 100644 --- a/packages/spark_css/lib/src/css_types/css_animation.dart +++ b/packages/spark_css/lib/src/css_types/css_animation.dart @@ -5,12 +5,15 @@ import 'css_value.dart'; sealed class CssAnimationDirection implements CssValue { const CssAnimationDirection._(); - static const CssAnimationDirection normal = - _CssAnimationDirectionKeyword('normal'); - static const CssAnimationDirection reverse = - _CssAnimationDirectionKeyword('reverse'); - static const CssAnimationDirection alternate = - _CssAnimationDirectionKeyword('alternate'); + static const CssAnimationDirection normal = _CssAnimationDirectionKeyword( + 'normal', + ); + static const CssAnimationDirection reverse = _CssAnimationDirectionKeyword( + 'reverse', + ); + static const CssAnimationDirection alternate = _CssAnimationDirectionKeyword( + 'alternate', + ); static const CssAnimationDirection alternateReverse = _CssAnimationDirectionKeyword('alternate-reverse'); @@ -62,14 +65,14 @@ final class _CssAnimationDirectionGlobal extends CssAnimationDirection { sealed class CssAnimationFillMode implements CssValue { const CssAnimationFillMode._(); - static const CssAnimationFillMode none = - _CssAnimationFillModeKeyword('none'); - static const CssAnimationFillMode forwards = - _CssAnimationFillModeKeyword('forwards'); - static const CssAnimationFillMode backwards = - _CssAnimationFillModeKeyword('backwards'); - static const CssAnimationFillMode both = - _CssAnimationFillModeKeyword('both'); + static const CssAnimationFillMode none = _CssAnimationFillModeKeyword('none'); + static const CssAnimationFillMode forwards = _CssAnimationFillModeKeyword( + 'forwards', + ); + static const CssAnimationFillMode backwards = _CssAnimationFillModeKeyword( + 'backwards', + ); + static const CssAnimationFillMode both = _CssAnimationFillModeKeyword('both'); /// CSS variable reference. factory CssAnimationFillMode.variable(String varName) = @@ -119,10 +122,12 @@ final class _CssAnimationFillModeGlobal extends CssAnimationFillMode { sealed class CssAnimationPlayState implements CssValue { const CssAnimationPlayState._(); - static const CssAnimationPlayState running = - _CssAnimationPlayStateKeyword('running'); - static const CssAnimationPlayState paused = - _CssAnimationPlayStateKeyword('paused'); + static const CssAnimationPlayState running = _CssAnimationPlayStateKeyword( + 'running', + ); + static const CssAnimationPlayState paused = _CssAnimationPlayStateKeyword( + 'paused', + ); /// CSS variable reference. factory CssAnimationPlayState.variable(String varName) = diff --git a/packages/spark_css/test/css_animation_test.dart b/packages/spark_css/test/css_animation_test.dart index d880ca3..f7b01af 100644 --- a/packages/spark_css/test/css_animation_test.dart +++ b/packages/spark_css/test/css_animation_test.dart @@ -79,10 +79,7 @@ void main() { group('CssAnimationIterationCount', () { test('infinite outputs correct CSS', () { - expect( - CssAnimationIterationCount.infinite.toCss(), - equals('infinite'), - ); + expect(CssAnimationIterationCount.infinite.toCss(), equals('infinite')); }); test('count outputs correct CSS', () { expect(CssAnimationIterationCount.count(3).toCss(), equals('3')); @@ -156,10 +153,7 @@ void main() { ); }); test('variable outputs correct CSS', () { - expect( - CssAnimation.variable('anim').toCss(), - equals('var(--anim)'), - ); + expect(CssAnimation.variable('anim').toCss(), equals('var(--anim)')); }); test('raw outputs value as-is', () { expect( @@ -168,10 +162,7 @@ void main() { ); }); test('global outputs correct CSS', () { - expect( - CssAnimation.global(CssGlobal.inherit).toCss(), - equals('inherit'), - ); + expect(CssAnimation.global(CssGlobal.inherit).toCss(), equals('inherit')); }); }); } From 182a5bbd6fc305e44a81c0b1c4c5a590eb09fd99 Mon Sep 17 00:00:00 2001 From: Kevin Segaud Date: Sun, 1 Mar 2026 11:12:19 +0100 Subject: [PATCH 3/4] docs: add CssAnimation to changelog --- packages/spark_css/CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/spark_css/CHANGELOG.md b/packages/spark_css/CHANGELOG.md index cd3ed6f..5df5a0f 100644 --- a/packages/spark_css/CHANGELOG.md +++ b/packages/spark_css/CHANGELOG.md @@ -8,6 +8,8 @@ - **Feat**: Added `CssGridTemplateColumns` sealed class for `grid-template-columns` with `.none`, `.subgrid`, `.tracks()`, `.repeat()`, `.autoFill()`, `.autoFit()`, plus `.variable()`, `.raw()`, and `.global()`. - **Feat**: Added `CssTrackSize` sealed class for individual grid track sizes with `.fr()`, `.length()`, `.minmax()`, `.fitContent()`, and `.raw()`. - **Feat**: Added `accent-color` CSS property support via `CssColor? accentColor` parameter in `Style.typed()`. +- **Feat**: Added `CssAnimation` sealed class for the `animation` shorthand property with `.none`, single animation (name, duration, timingFunction, delay, iterationCount, direction, fillMode, playState), `.multiple()`, plus `.variable()`, `.raw()`, and `.global()` escape hatches. +- **Feat**: Added `CssAnimationDirection`, `CssAnimationFillMode`, `CssAnimationPlayState`, and `CssAnimationIterationCount` sealed classes for animation sub-properties. Reuses existing `CssDuration` and `CssTimingFunction` from `CssTransition`. ### Changed From a42b3b0fd4aa7c8dcec90413e6f77d242ee0153c Mon Sep 17 00:00:00 2001 From: Kevin Segaud Date: Sun, 1 Mar 2026 11:15:35 +0100 Subject: [PATCH 4/4] test: add Style.typed animation integration test --- packages/spark_css/test/css_animation_test.dart | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/packages/spark_css/test/css_animation_test.dart b/packages/spark_css/test/css_animation_test.dart index f7b01af..bc382c1 100644 --- a/packages/spark_css/test/css_animation_test.dart +++ b/packages/spark_css/test/css_animation_test.dart @@ -1,6 +1,4 @@ -import 'package:spark_css/src/css_types/css_animation.dart'; -import 'package:spark_css/src/css_types/css_transition.dart'; -import 'package:spark_css/src/css_types/css_value.dart'; +import 'package:spark_css/spark_css.dart'; import 'package:test/test.dart'; void main() { @@ -164,5 +162,15 @@ void main() { test('global outputs correct CSS', () { expect(CssAnimation.global(CssGlobal.inherit).toCss(), equals('inherit')); }); + test('Style.typed renders animation property', () { + final style = Style.typed( + animation: CssAnimation( + name: 'fadeIn', + duration: CssDuration.s(1), + fillMode: CssAnimationFillMode.forwards, + ), + ); + expect(style.toCss(), contains('animation: fadeIn 1s forwards;')); + }); }); }