or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

configuration.mdcss-features.mdindex.mdstyle-merging.mdstyle-sets.mdstylesheet-management.md
tile.json

css-features.mddocs/

CSS Features

Support for advanced CSS features including @font-face declarations and @keyframes animations with automatic registration, deduplication, and name generation.

Capabilities

Font Face Registration

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

Keyframes Animation

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

Complex Animation Examples

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

Font Style Types

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;

Performance and Deduplication

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)