or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

configuration.mdfont-animation.mdindex.mdserver-rendering.mdstyle-sets.mdstyling.md
tile.json

font-animation.mddocs/

Font and Animation Utilities

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.

Capabilities

fontFace Function

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
  }
});

keyframes Function

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 Utilities and Patterns

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'
  }
});

Font and Animation Types

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;
}

Best Practices

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'
  }
});