Support for advanced CSS features including @font-face declarations and @keyframes animations with automatic registration, deduplication, and name generation.
Registers @font-face declarations for custom fonts with automatic deduplication.
/**
* Registers a @font-face declaration
* @param font - Font face definition with src, family, and display properties
*/
function fontFace(font: IFontFace): void;
interface IFontFace extends IRawFontStyle {
/** Font display strategy for loading behavior */
fontDisplay?: 'auto' | 'block' | 'swap' | 'fallback' | 'optional';
/** OpenType feature settings */
fontFeatureSettings?: string;
/** Font source URLs or local references */
src?: string;
/** Unicode character range coverage */
unicodeRange?: ICSSRule | string;
}Usage Examples:
import { fontFace } from "@fluentui/merge-styles";
// Basic web font registration
fontFace({
fontFamily: "MyCustomFont",
src: `url('fonts/mycustomfont.woff2') format('woff2'),
url('fonts/mycustomfont.woff') format('woff')`,
fontWeight: "normal",
fontStyle: "normal",
fontDisplay: "swap"
});
// Font with multiple weights
fontFace({
fontFamily: "Roboto",
src: "url('https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;700')",
fontWeight: "300 700", // Variable weight range
fontDisplay: "fallback"
});
// Font with Unicode range
fontFace({
fontFamily: "IconFont",
src: "url('icons/iconfont.woff2') format('woff2')",
unicodeRange: "U+E000-E0FF", // Private use area
fontDisplay: "block"
});
// Now use the registered font in styles
import { mergeStyles } from "@fluentui/merge-styles";
const customFontStyle = mergeStyles({
fontFamily: "MyCustomFont, Arial, sans-serif"
});Creates and registers @keyframes animations, returning the generated animation name.
/**
* Registers keyframe definitions and returns animation name
* @param timeline - Object mapping keyframe selectors to style objects
* @returns Generated animation name for use in CSS animations
*/
function keyframes(timeline: IKeyframes): string;
type IKeyframes = Record<string, IRawStyle>;Usage Examples:
import { keyframes, mergeStyles } from "@fluentui/merge-styles";
// Simple fade-in animation
const fadeInAnimation = keyframes({
"0%": { opacity: 0 },
"100%": { opacity: 1 }
});
// Bounce animation with multiple keyframes
const bounceAnimation = 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)"
}
});
// Complex rotation and scale animation
const spinGrowAnimation = keyframes({
"0%": {
transform: "rotate(0deg) scale(1)",
opacity: 1
},
"50%": {
transform: "rotate(180deg) scale(1.2)",
opacity: 0.8
},
"100%": {
transform: "rotate(360deg) scale(1)",
opacity: 1
}
});
// Use animations in styles
const animatedStyle = mergeStyles({
// Apply fade-in animation
animation: `${fadeInAnimation} 0.3s ease-in-out`,
":hover": {
// Apply bounce on hover
animation: `${bounceAnimation} 0.6s ease-in-out`
}
});
const loadingStyle = mergeStyles({
animation: `${spinGrowAnimation} 2s linear infinite`
});Advanced keyframes with transforms, filters, and multi-property animations:
// Loading spinner
const spinnerAnimation = keyframes({
"0%": {
transform: "rotate(0deg)"
},
"100%": {
transform: "rotate(360deg)"
}
});
// Pulse effect
const pulseAnimation = keyframes({
"0%": {
transform: "scale(1)",
boxShadow: "0 0 0 0 rgba(0, 123, 255, 0.7)"
},
"70%": {
transform: "scale(1.05)",
boxShadow: "0 0 0 10px rgba(0, 123, 255, 0)"
},
"100%": {
transform: "scale(1)",
boxShadow: "0 0 0 0 rgba(0, 123, 255, 0)"
}
});
// Typewriter effect
const typewriterAnimation = keyframes({
"0%": {
width: "0"
},
"100%": {
width: "100%"
}
});
// Slide and fade
const slideFromLeftAnimation = keyframes({
"0%": {
transform: "translateX(-100%)",
opacity: 0
},
"100%": {
transform: "translateX(0)",
opacity: 1
}
});
// Apply complex animations
const complexAnimationStyle = mergeStyles({
// Base animation
animation: `${slideFromLeftAnimation} 0.5s ease-out`,
// Conditional animations with pseudo-classes
"&.loading": {
animation: `${spinnerAnimation} 1s linear infinite`
},
"&.pulsing": {
animation: `${pulseAnimation} 2s infinite`
},
// Multiple animations
"&.complex": {
animation: `
${fadeInAnimation} 0.3s ease-in,
${pulseAnimation} 2s 0.3s infinite
`
}
});interface IRawFontStyle {
font?: ICSSRule | string;
fontFamily?: ICSSRule | string;
fontKerning?: ICSSRule | string;
fontSize?: ICSSRule | 'xx-small' | 'x-small' | 'small' | 'medium' | 'large' | 'x-large' | 'xx-large' | 'larger' | 'smaller' | ICSSPixelUnitRule | string;
fontSizeAdjust?: ICSSRule | 'none' | number | string;
fontStretch?: ICSSRule | 'normal' | 'ultra-condensed' | 'extra-condensed' | 'condensed' | 'semi-condensed' | 'semi-expanded' | 'expanded' | 'extra-expanded' | 'ultra-expanded' | string;
fontStyle?: ICSSRule | 'normal' | 'italic' | 'oblique' | string;
fontSynthesis?: ICSSRule | string;
fontVariant?: ICSSRule | string;
fontVariantAlternates?: ICSSRule | string;
fontWeight?: IFontWeight | string;
}
type IFontWeight = ICSSRule | 'normal' | 'bold' | 'bolder' | 'lighter' | '100' | 100 | '200' | 200 | '300' | 300 | '400' | 400 | '500' | 500 | '600' | 600 | '700' | 700 | '800' | 800 | '900' | 900;
type ICSSRule = 'initial' | 'inherit' | 'unset';
type ICSSPixelUnitRule = string | number;Both fontFace and keyframes automatically deduplicate identical declarations:
// These calls will result in only one @font-face rule
fontFace({
fontFamily: "MyFont",
src: "url('font.woff2')"
});
fontFace({
fontFamily: "MyFont",
src: "url('font.woff2')" // Same as above - no duplicate rule created
});
// Same keyframes will return the same animation name
const anim1 = keyframes({ "0%": { opacity: 0 }, "100%": { opacity: 1 } });
const anim2 = keyframes({ "0%": { opacity: 0 }, "100%": { opacity: 1 } });
// anim1 === anim2 (same generated name)