Functions for registering custom fonts and CSS animations using JavaScript objects. These utilities enable you to define fonts and keyframe animations programmatically and use them in your styles.
Registers a custom font face definition that can be used throughout your application. The font is immediately available after registration.
/**
* Registers a font face for use in styles
* @param font - Font face definition with family, source, and properties
*/
function fontFace(font: IFontFace): void;Usage Examples:
import { fontFace, mergeStyles } from '@uifabric/merge-styles';
// Register a custom font
fontFace({
fontFamily: 'MyCustomFont',
src: `url('fonts/MyCustomFont.woff2') format('woff2'),
url('fonts/MyCustomFont.woff') format('woff')`,
fontWeight: 'normal',
fontStyle: 'normal',
fontDisplay: 'swap'
});
// Use the registered font in styles
const textClass = mergeStyles({
fontFamily: 'MyCustomFont, Arial, sans-serif',
fontSize: '16px'
});Google Fonts Integration:
// Register Google Font
fontFace({
fontFamily: 'Roboto',
src: `url('https://fonts.gstatic.com/s/roboto/v27/KFOmCnqEu92Fr1Mu4mxKKTU1Kg.woff2') format('woff2')`,
fontWeight: '400',
fontStyle: 'normal',
fontDisplay: 'swap'
});
// Register bold variant
fontFace({
fontFamily: 'Roboto',
src: `url('https://fonts.gstatic.com/s/roboto/v27/KFOlCnqEu92Fr1MmWUlfBBc4AMP6lQ.woff2') format('woff2')`,
fontWeight: '700',
fontStyle: 'normal',
fontDisplay: 'swap'
});
// Use with different weights
const titleClass = mergeStyles({
fontFamily: 'Roboto, sans-serif',
fontWeight: '700',
fontSize: '24px'
});
const bodyClass = mergeStyles({
fontFamily: 'Roboto, sans-serif',
fontWeight: '400',
fontSize: '16px'
});Icon Fonts:
// Register icon font
fontFace({
fontFamily: 'MyIcons',
src: `url('fonts/icons.woff2') format('woff2')`,
fontWeight: 'normal',
fontStyle: 'normal'
});
// Create icon classes
const iconClass = mergeStyles({
fontFamily: 'MyIcons',
fontSize: '20px',
lineHeight: 1,
'&::before': {
content: '"\\e001"' // Unicode for specific icon
}
});Registers CSS keyframe animations and returns the animation name that can be used in styles. Supports all CSS animation properties and timing functions.
/**
* Registers keyframe definitions for CSS animations
* @param timeline - Object defining keyframe percentages and their styles
* @returns Animation name string that can be used in animationName property
*/
function keyframes(timeline: IKeyframes): string;Usage Examples:
import { keyframes, mergeStyles } from '@uifabric/merge-styles';
// Define fade in animation
const fadeIn = keyframes({
from: {
opacity: 0
},
to: {
opacity: 1
}
});
// Use animation in styles
const fadeInClass = mergeStyles({
animationName: fadeIn,
animationDuration: '0.3s',
animationTimingFunction: 'ease-in-out'
});
// Alternative percentage syntax
const slideIn = keyframes({
'0%': {
transform: 'translateX(-100%)',
opacity: 0
},
'50%': {
transform: 'translateX(-10%)',
opacity: 0.7
},
'100%': {
transform: 'translateX(0)',
opacity: 1
}
});
const slideInClass = mergeStyles({
animationName: slideIn,
animationDuration: '0.5s',
animationFillMode: 'both'
});Complex Animations:
// Bounce animation with multiple properties
const bounce = keyframes({
'0%, 20%, 53%, 80%, 100%': {
transform: 'translate3d(0, 0, 0)'
},
'40%, 43%': {
transform: 'translate3d(0, -30px, 0)'
},
'70%': {
transform: 'translate3d(0, -15px, 0)'
},
'90%': {
transform: 'translate3d(0, -4px, 0)'
}
});
// Pulse animation with scale and opacity
const pulse = keyframes({
'0%': {
transform: 'scale(1)',
opacity: 1
},
'50%': {
transform: 'scale(1.05)',
opacity: 0.8
},
'100%': {
transform: 'scale(1)',
opacity: 1
}
});
// Use multiple animations
const animatedButtonClass = mergeStyles({
background: 'blue',
color: 'white',
border: 'none',
padding: '10px 20px',
borderRadius: '4px',
cursor: 'pointer',
// Apply animations on hover
':hover': {
animationName: pulse,
animationDuration: '0.6s',
animationIterationCount: 'infinite'
},
// Apply different animation when active
':active': {
animationName: bounce,
animationDuration: '0.8s'
}
});Loading Spinners:
// Rotating spinner
const spin = keyframes({
from: {
transform: 'rotate(0deg)'
},
to: {
transform: 'rotate(360deg)'
}
});
const spinnerClass = mergeStyles({
width: '20px',
height: '20px',
border: '2px solid #f3f3f3',
borderTop: '2px solid #3498db',
borderRadius: '50%',
animationName: spin,
animationDuration: '1s',
animationIterationCount: 'infinite',
animationTimingFunction: 'linear'
});
// Pulsing dots loader
const pulseDot = keyframes({
'0%, 80%, 100%': {
transform: 'scale(0)',
opacity: 0.5
},
'40%': {
transform: 'scale(1)',
opacity: 1
}
});
const dotLoaderClass = mergeStyles({
display: 'inline-block',
width: '8px',
height: '8px',
borderRadius: '50%',
background: '#3498db',
animationName: pulseDot,
animationDuration: '1.4s',
animationIterationCount: 'infinite',
animationFillMode: 'both',
// Delay for multiple dots
'&:nth-child(1)': { animationDelay: '-0.32s' },
'&:nth-child(2)': { animationDelay: '-0.16s' }
});Animation Classes with Parameters:
// Create reusable animation function
const createSlideAnimation = (direction: 'left' | 'right' | 'up' | 'down') => {
const transforms = {
left: 'translateX(-100%)',
right: 'translateX(100%)',
up: 'translateY(-100%)',
down: 'translateY(100%)'
};
return keyframes({
from: {
transform: transforms[direction],
opacity: 0
},
to: {
transform: 'translate(0, 0)',
opacity: 1
}
});
};
// Use parameterized animations
const slideLeftAnimation = createSlideAnimation('left');
const slideRightAnimation = createSlideAnimation('right');
const modalClass = mergeStyles({
position: 'fixed',
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)',
background: 'white',
padding: '20px',
borderRadius: '8px',
boxShadow: '0 4px 6px rgba(0, 0, 0, 0.1)',
// Enter animation
animationName: slideLeftAnimation,
animationDuration: '0.3s',
animationFillMode: 'both'
});Responsive Animations:
// Different animations for different screen sizes
const mobileSlide = keyframes({
from: { transform: 'translateY(100%)' },
to: { transform: 'translateY(0)' }
});
const desktopFade = keyframes({
from: { opacity: 0, transform: 'scale(0.9)' },
to: { opacity: 1, transform: 'scale(1)' }
});
const responsiveModalClass = mergeStyles({
// Base styles
background: 'white',
padding: '20px',
// Desktop animation
animationName: desktopFade,
animationDuration: '0.3s',
// Mobile animation
'@media (max-width: 768px)': {
animationName: mobileSlide,
animationDuration: '0.4s'
}
});interface IFontFace {
fontFamily: string;
src: string;
fontWeight?: IFontWeight;
fontStyle?: 'normal' | 'italic' | 'oblique';
fontDisplay?: 'auto' | 'block' | 'swap' | 'fallback' | 'optional';
unicodeRange?: string;
fontVariant?: string;
fontFeatureSettings?: string;
fontStretch?: string;
}
type IFontWeight =
| 'normal' | 'bold' | 'bolder' | 'lighter'
| '100' | '200' | '300' | '400' | '500' | '600' | '700' | '800' | '900'
| 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900;
type IKeyframes = Record<string, IRawStyle>;
interface IRawStyle {
[key: string]: any;
displayName?: string;
}Font Loading Strategy:
// Preload critical fonts
fontFace({
fontFamily: 'CriticalFont',
src: `url('fonts/critical.woff2') format('woff2')`,
fontDisplay: 'swap' // Shows fallback font while loading
});
// Load non-critical fonts with fallback
fontFace({
fontFamily: 'DecoratieFont',
src: `url('fonts/decorative.woff2') format('woff2')`,
fontDisplay: 'optional' // Only shows if already cached
});Performance-Conscious Animations:
// Use transform and opacity for better performance
const efficientSlide = keyframes({
from: {
transform: 'translateX(-100%)', // Uses GPU acceleration
opacity: 0
},
to: {
transform: 'translateX(0)',
opacity: 1
}
});
// Avoid animating layout properties
const inefficientSlide = keyframes({
from: {
left: '-100px', // ❌ Causes layout recalculation
width: '0px' // ❌ Causes layout recalculation
},
to: {
left: '0px',
width: '200px'
}
});