The Crystal release refines TriFrost’s styling core with expressive animation support, dynamic composition, and smoother ergonomics across the board.
With css.animation(...)
, prebuilt keyframes are now first-class, fully typed, overrideable, and composable.
Style definitions have evolved into callable units via css.defs
, unlocking dynamic, runtime-aware styling while keeping the authoring experience pure and declarative.
Added
- feat: Register prebuilt animations via
createCss({animations: {...}})
for reusable keyframe definitions - feat:
css.animation(name, overrides?)
method oncss
instance, fully typed, override-capable, returns granular animation CSS props - feat:
css.defs
now exposes all registereddefinitions
as direct, callable methods (e.g.css.defs.card()
), improving ergonomics and enabling dynamic, fully typed, parameterized style generation.
Improved
- qol: Atomic removal now applies depth-first traversal when unmounting removed dom nodes.
- feat:
definitions
increateCss
now allow for usage ofkeyframes
andanimation
in definitions.
Breaking
- feat:
definitions
increateCss
now need to be passed as functions returning the definition. (see Added and improved)
Animation Examples
Baseline registration and use
const css = createCss({
animations: {
pulse: {
keyframes: {
from: { opacity: 0.5 },
to: { opacity: 1 },
},
duration: '0.6s',
easingFunction: 'ease-in-out',
},
},
});
const cls = css({
...css.animation('pulse'),
opacity: 0,
});
const cls2 = css({
...css.animation('pulse', { duration: '1s', iterationCount: 'infinite' }),
opacity: 0,
});
In combination with css.use
// css.ts
const css = createCss({
animations: {
fadeInUp: {
keyframes: {
from: { opacity: 0, transform: 'translateY(10px)' },
to: { opacity: 1, transform: 'translateY(0)' },
},
duration: '0.4s',
easingFunction: 'ease-out',
},
},
definitions: css => ({
card: () => ({
padding: '1rem',
borderRadius: '0.5rem',
backgroundColor: '#fff',
boxShadow: '0 2px 8px rgba(0,0,0,0.1)',
}),
}),
});
// Component.tsx
const cls = css.use('card', css.animation('fadeInUp', { delay: '100ms' }));
return <div className={cls}>Animated Card</div>;
🎬 Using .animation(...) inside a definition
// css.ts
const css = createCss({
animations: {
fadeIn: {
keyframes: {
from: { opacity: 0 },
to: { opacity: 1 },
},
duration: '0.3s',
easingFunction: 'ease-in',
},
},
definitions: css => ({
// Animation-enhanced button definition
button: () => ({
...css.animation('fadeIn'),
padding: '0.5rem 1rem',
borderRadius: '4px',
backgroundColor: css.$t.bg_button,
color: css.$t.text_button,
[css.hover]: {
filter: 'brightness(1.1)',
},
}),
}),
});
// Component.tsx
const cls = css.use('button');
return <button className={cls}>Click Me</button>;
Dynamic definition
const css = createCss({
definitions: css => ({
alert: (variant: 'info' | 'warning' | 'danger') => ({
backgroundColor: {
info: 'lightblue',
warning: 'gold',
danger: 'crimson',
}[variant],
color: '#fff',
padding: '1rem',
}),
}),
});
const cls = css(css.defs.alert('warning'));
Trifrost’s style system now breathes. Animations are first-class, definitions are dynamic, and your design tokens flow seamlessly from logic to UI. Fewer barriers. More expressiveness.
Let the authoring feel as fluid as the interface it creates.
As always, stay frosty ❄️