CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-lightningcss

A CSS parser, transformer, and minifier written in Rust with Node.js bindings

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

visitors.mddocs/

AST Visitors

Extensible AST visitor pattern for custom CSS transformations and analysis with type-safe interfaces, enabling JavaScript-based CSS processing and manipulation.

Capabilities

Compose Visitors Function

Combine multiple visitor objects into a single visitor for modular transformation pipelines.

/**
 * Composes multiple visitor objects into a single one
 * @param visitors - Array of visitor objects to combine
 * @returns Single composed visitor with all transformations
 */
function composeVisitors<C extends CustomAtRules>(
  visitors: Visitor<C>[]
): Visitor<C>;

Usage Examples:

import { composeVisitors, transform } from "lightningcss";

// Create individual visitors
const colorVisitor = {
  Color(color) {
    // Convert all red colors to brand color
    if (color.type === 'rgb' && color.r === 255 && color.g === 0 && color.b === 0) {
      return { type: 'rgb', r: 42, g: 86, b: 153 }; // Brand blue
    }
    return color;
  }
};

const urlVisitor = {
  Url(url) {
    // Rewrite relative URLs to absolute
    if (!url.url.startsWith('http') && !url.url.startsWith('data:')) {
      return { ...url, url: `https://cdn.example.com/${url.url}` };
    }
    return url;
  }
};

// Compose visitors
const combinedVisitor = composeVisitors([colorVisitor, urlVisitor]);

const result = transform({
  filename: "styles.css",
  code: new TextEncoder().encode(`
    .logo { background: url('./logo.png'); color: red; }
    .button { background: blue; color: red; }
  `),
  visitor: combinedVisitor,
  minify: true
});

Visitor Interface

Comprehensive visitor interface for intercepting and transforming all CSS AST nodes.

interface Visitor<C extends CustomAtRules> {
  /** Visit stylesheet before processing rules */
  StyleSheet?(stylesheet: StyleSheet): StyleSheet<ReturnedDeclaration, ReturnedMediaQuery> | void;
  /** Visit stylesheet after processing rules */
  StyleSheetExit?(stylesheet: StyleSheet): StyleSheet<ReturnedDeclaration, ReturnedMediaQuery> | void;
  
  /** Visit CSS rules (can be function or object with rule-type-specific visitors) */
  Rule?: RuleVisitor | RuleVisitors<C>;
  /** Visit CSS rules after processing contents */
  RuleExit?: RuleVisitor | RuleVisitors<C>;
  
  /** Visit CSS declarations (can be function or object with property-specific visitors) */
  Declaration?: DeclarationVisitor | DeclarationVisitors;
  /** Visit CSS declarations after processing values */
  DeclarationExit?: DeclarationVisitor | DeclarationVisitors;
  
  /** Visit URL values */
  Url?(url: Url): Url | void;
  /** Visit color values */
  Color?(color: CssColor): CssColor | void;
  /** Visit image values */
  Image?(image: Image): Image | void;
  /** Visit image values after processing */
  ImageExit?(image: Image): Image | void;
  
  /** Visit length values */
  Length?(length: LengthValue): LengthValue | void;
  /** Visit angle values */
  Angle?(angle: Angle): Angle | void;
  /** Visit ratio values */
  Ratio?(ratio: Ratio): Ratio | void;
  /** Visit resolution values */
  Resolution?(resolution: Resolution): Resolution | void;
  /** Visit time values */
  Time?(time: Time): Time | void;
  
  /** Visit custom identifier values */
  CustomIdent?(ident: string): string | void;
  /** Visit dashed identifier values */
  DashedIdent?(ident: string): string | void;
  
  /** Visit media queries */
  MediaQuery?(query: MediaQuery): ReturnedMediaQuery | ReturnedMediaQuery[] | void;
  /** Visit media queries after processing */
  MediaQueryExit?(query: MediaQuery): ReturnedMediaQuery | ReturnedMediaQuery[] | void;
  
  /** Visit @supports conditions */
  SupportsCondition?(condition: SupportsCondition): SupportsCondition;
  /** Visit @supports conditions after processing */
  SupportsConditionExit?(condition: SupportsCondition): SupportsCondition;
  
  /** Visit selectors */
  Selector?(selector: Selector): Selector | Selector[] | void;
  
  /** Visit CSS tokens (can be function or object with token-type-specific visitors) */
  Token?: TokenVisitor | TokenVisitors;
  /** Visit CSS functions (can be function or object with function-name-specific visitors) */
  Function?: FunctionVisitor | { [name: string]: FunctionVisitor };
  /** Visit CSS functions after processing arguments */
  FunctionExit?: FunctionVisitor | { [name: string]: FunctionVisitor };
  
  /** Visit CSS variables */
  Variable?(variable: Variable): TokenReturnValue;
  /** Visit CSS variables after processing */
  VariableExit?(variable: Variable): TokenReturnValue;
  
  /** Visit environment variables (can be function or object with env-name-specific visitors) */
  EnvironmentVariable?: EnvironmentVariableVisitor | EnvironmentVariableVisitors;
  /** Visit environment variables after processing */
  EnvironmentVariableExit?: EnvironmentVariableVisitor | EnvironmentVariableVisitors;
}

Rule-Specific Visitors

Visit specific types of CSS rules with type-safe interfaces.

type RuleVisitors<C extends CustomAtRules> = {
  /** Visit @media rules */
  media?: RuleVisitor<MediaRule>;
  /** Visit @import rules */
  import?: RuleVisitor<ImportRule>;
  /** Visit style rules (selectors + declarations) */
  style?: RuleVisitor<StyleRule>;
  /** Visit @keyframes rules */
  keyframes?: RuleVisitor<KeyframesRule>;
  /** Visit @font-face rules */
  'font-face'?: RuleVisitor<FontFaceRule>;
  /** Visit @page rules */
  page?: RuleVisitor<PageRule>;
  /** Visit @supports rules */
  supports?: RuleVisitor<SupportsRule>;
  /** Visit @counter-style rules */
  'counter-style'?: RuleVisitor<CounterStyleRule>;
  /** Visit @namespace rules */
  namespace?: RuleVisitor<NamespaceRule>;
  /** Visit @layer rules */
  layer?: RuleVisitor<LayerRule>;
  /** Visit @container rules */
  container?: RuleVisitor<ContainerRule>;
  /** Visit unknown at-rules */
  unknown?: UnknownVisitors<UnknownAtRule>;
  /** Visit custom at-rules */
  custom?: CustomVisitors<C>;
};

type RuleVisitor<R> = (rule: R) => ReturnedRule | ReturnedRule[] | void;

Usage Examples:

// Rule-specific transformation
const ruleVisitor = {
  Rule: {
    // Transform media queries
    media(rule) {
      // Convert old max-width syntax to modern range syntax
      if (rule.value.query.mediaType === 'screen') {
        // Transform query...
        return rule;
      }
      return rule;
    },
    
    // Transform style rules
    style(rule) {
      // Add vendor prefixes to flex properties
      const hasFlexDisplay = rule.value.declarations.declarations.some(
        decl => decl.property === 'display' && decl.value === 'flex'
      );
      
      if (hasFlexDisplay) {
        // Add -webkit-box, -moz-box, etc.
        return {
          ...rule,
          value: {
            ...rule.value,
            declarations: {
              ...rule.value.declarations,
              declarations: [
                { property: 'display', value: '-webkit-box' },
                { property: 'display', value: '-moz-box' },
                ...rule.value.declarations.declarations
              ]
            }
          }
        };
      }
      return rule;
    },
    
    // Remove @import rules
    import(rule) {
      console.log(`Removing import: ${rule.value.url}`);
      return void 0; // Remove rule
    }
  }
};

Declaration-Specific Visitors

Visit specific CSS properties with type-safe value access.

type DeclarationVisitors = {
  /** Visit background properties */
  background?: DeclarationVisitor<BackgroundDeclaration>;
  /** Visit color properties */
  color?: DeclarationVisitor<ColorDeclaration>;
  /** Visit display properties */
  display?: DeclarationVisitor<DisplayDeclaration>;
  /** Visit margin properties */
  margin?: DeclarationVisitor<MarginDeclaration>;
  /** Visit padding properties */
  padding?: DeclarationVisitor<PaddingDeclaration>;
  /** Visit transform properties */
  transform?: DeclarationVisitor<TransformDeclaration>;
  /** Visit custom properties */
  custom?: CustomPropertyVisitors | DeclarationVisitor<CustomProperty>;
  // ... many more property-specific visitors
};

type DeclarationVisitor<P> = (property: P) => ReturnedDeclaration | ReturnedDeclaration[] | void;

Usage Examples:

// Property-specific transformations
const declarationVisitor = {
  Declaration: {
    // Transform background properties
    background(decl) {
      // Convert background shorthand to individual properties for IE support
      if (decl.property === 'background' && typeof decl.value === 'object') {
        const individual = [];
        if (decl.value.color) {
          individual.push({ property: 'background-color', value: decl.value.color });
        }
        if (decl.value.image) {
          individual.push({ property: 'background-image', value: decl.value.image });
        }
        return individual;
      }
      return decl;
    },
    
    // Transform custom properties
    custom: {
      '--primary-color'(decl) {
        // Replace CSS variable with computed value
        return {
          property: 'color',
          value: { type: 'rgb', r: 42, g: 86, b: 153 }
        };
      }
    },
    
    // Transform display properties
    display(decl) {
      // Add fallbacks for CSS Grid
      if (decl.value === 'grid') {
        return [
          { property: 'display', value: 'block' }, // Fallback
          decl // Original
        ];
      }
      return decl;
    }
  }
};

Token and Value Visitors

Visit individual CSS tokens and values for fine-grained transformations.

type TokenVisitors = {
  /** Visit identifier tokens */
  ident?: (token: IdentToken) => TokenReturnValue;
  /** Visit at-keyword tokens */
  'at-keyword'?: (token: AtKeywordToken) => TokenReturnValue;
  /** Visit hash tokens */
  hash?: (token: HashToken) => TokenReturnValue;
  /** Visit string tokens */
  string?: (token: StringToken) => TokenReturnValue;
  /** Visit number tokens */
  number?: (token: NumberToken) => TokenReturnValue;
  /** Visit percentage tokens */
  percentage?: (token: PercentageToken) => TokenReturnValue;
  /** Visit dimension tokens */
  dimension?: (token: DimensionToken) => TokenReturnValue;
};

type TokenReturnValue = TokenOrValue | TokenOrValue[] | RawValue | void;

interface RawValue {
  /** A raw string value which will be parsed like CSS. */
  raw: string;
}

Usage Examples:

// Token-level transformations  
const tokenVisitor = {
  Token: {
    // Transform dimension tokens
    dimension(token) {
      // Convert px to rem
      if (token.unit === 'px' && typeof token.value === 'number') {
        return {
          type: 'dimension',
          value: token.value / 16, // Assuming 16px = 1rem
          unit: 'rem'
        };
      }
      return token;
    },
    
    // Transform string tokens
    string(token) {
      // Replace font family names
      if (token.value === 'Arial') {
        return { type: 'string', value: 'system-ui' };
      }
      return token;
    },
    
    // Transform identifier tokens
    ident(token) {
      // Replace color names
      const colorMap = {
        'red': { raw: '#ff0000' },
        'blue': { raw: '#0000ff' }
      };
      return colorMap[token.value] || token;
    }
  },
  
  // Function-specific transformations
  Function: {
    // Transform calc() functions
    calc(fn) {
      // Simplify calc expressions
      if (fn.arguments.length === 1) {
        const arg = fn.arguments[0];
        if (arg.type === 'token' && arg.value.type === 'dimension') {
          return arg.value; // Remove unnecessary calc()
        }
      }
      return fn;
    },
    
    // Transform url() functions  
    url(fn) {
      // Rewrite URLs
      if (fn.arguments[0]?.type === 'token' && fn.arguments[0].value.type === 'string') {
        const url = fn.arguments[0].value.value;
        if (url.startsWith('./')) {
          return {
            ...fn,
            arguments: [{
              ...fn.arguments[0],
              value: {
                ...fn.arguments[0].value,
                value: `https://cdn.example.com/${url.slice(2)}`
              }
            }]
          };
        }
      }
      return fn;
    }
  }
};

Complete Visitor Example

Real-world example combining multiple visitor types for comprehensive CSS transformation.

import { transform, composeVisitors } from "lightningcss";

// Asset optimization visitor
const assetVisitor = {
  Url(url) {
    // Convert relative URLs to CDN URLs
    if (!url.url.startsWith('http') && !url.url.startsWith('data:')) {
      return { ...url, url: `https://cdn.example.com/assets/${url.url}` };
    }
    return url;
  }
};

// Color standardization visitor  
const colorVisitor = {
  Color(color) {
    // Standardize brand colors
    const brandColors = {
      '#ff0000': { type: 'rgb', r: 42, g: 86, b: 153 }, // Brand blue
      '#00ff00': { type: 'rgb', r: 40, g: 167, b: 69 }  // Brand green
    };
    
    if (color.type === 'rgb') {
      const hex = `#${color.r.toString(16).padStart(2, '0')}${color.g.toString(16).padStart(2, '0')}${color.b.toString(16).padStart(2, '0')}`;
      return brandColors[hex] || color;
    }
    return color;
  }
};

// Legacy support visitor
const legacyVisitor = {
  Declaration: {
    display(decl) {
      // Add IE fallbacks for flexbox
      if (decl.value === 'flex') {
        return [
          { property: 'display', value: '-ms-flexbox' },
          { property: 'display', value: '-webkit-flex' },
          decl
        ];
      }
      return decl;
    }
  },
  
  Rule: {
    style(rule) {
      // Add -webkit- prefixes for flexbox properties
      const needsPrefixing = rule.value.declarations.declarations.some(
        decl => ['align-items', 'justify-content', 'flex-direction'].includes(decl.property)
      );
      
      if (needsPrefixing) {
        const prefixedDeclarations = rule.value.declarations.declarations.flatMap(decl => {
          if (['align-items', 'justify-content', 'flex-direction'].includes(decl.property)) {
            return [
              { property: `-webkit-${decl.property}`, value: decl.value },
              decl
            ];
          }
          return [decl];
        });
        
        return {
          ...rule,
          value: {
            ...rule.value,
            declarations: {
              ...rule.value.declarations,
              declarations: prefixedDeclarations
            }
          }
        };
      }
      return rule;
    }
  }
};

// Compose all visitors
const fullVisitor = composeVisitors([assetVisitor, colorVisitor, legacyVisitor]);

// Apply comprehensive transformations
const result = transform({
  filename: "app.css",
  code: new TextEncoder().encode(`
    .hero {
      display: flex;
      align-items: center;
      background: url('./hero-bg.jpg');
      color: #ff0000;
    }
    
    .button {
      background: #00ff00;
      justify-content: center;
    }
  `),
  visitor: fullVisitor,
  targets: { ie: 11 << 16 },
  minify: true
});

console.log(new TextDecoder().decode(result.code));
// Output includes CDN URLs, brand colors, and IE-compatible flexbox properties

Environment Variable Visitors

Visit CSS environment variables like env(safe-area-inset-top) for custom processing and polyfills.

type EnvironmentVariableVisitor = (env: EnvironmentVariable) => TokenReturnValue;

type EnvironmentVariableVisitors = {
  [name: string]: EnvironmentVariableVisitor;
};

Usage Examples:

// Environment variable transformations
const envVisitor = {
  EnvironmentVariable: {
    // Handle safe area insets for iOS
    'safe-area-inset-top'(env) {
      // Provide fallback value for browsers that don't support env()
      return { raw: 'max(env(safe-area-inset-top), 20px)' };
    },
    
    'safe-area-inset-bottom'(env) {
      return { raw: 'max(env(safe-area-inset-bottom), 20px)' };
    },
    
    // Handle custom environment variables
    'keyboard-height'(env) {
      // Polyfill custom env() variables
      return { raw: 'var(--keyboard-height, 0px)' };
    }
  },
  
  // Generic environment variable handler
  EnvironmentVariable(env) {
    console.log(`Processing env variable: ${env.name}`);
    // Add debug information or logging
    return env;
  }
};

const result = transform({
  filename: "mobile.css",
  code: new TextEncoder().encode(`
    .safe-area {
      padding-top: env(safe-area-inset-top);
      padding-bottom: env(safe-area-inset-bottom);
      margin-bottom: env(keyboard-height);
    }
  `),
  visitor: envVisitor,
  minify: true
});

Advanced Type Mapping Visitors

Lightning CSS provides detailed type mappings for rule and declaration visitors that enable precise targeting of specific CSS constructs.

// Rule type mapping for maximum specificity
type MappedRuleVisitors = {
  [Name in Exclude<Rule['type'], 'unknown' | 'custom'>]?: RuleVisitor<RequiredValue<FindByType<Rule, Name>>>;
}

// Declaration type mapping for property-specific handling
type MappedDeclarationVisitors = {
  [Name in Exclude<Declaration['property'], 'unparsed' | 'custom'>]?: DeclarationVisitor<FindProperty<Declaration, Name> | FindProperty<Declaration, 'unparsed'>>;
}

// Unknown rule visitors for handling non-standard at-rules
type UnknownVisitors<T> = {
  [name: string]: RuleVisitor<T>;
}

// Custom rule visitors for user-defined at-rules
type CustomVisitors<T extends CustomAtRules> = {
  [Name in keyof T]?: RuleVisitor<CustomAtRule<Name, T[Name]>>;
};

Usage Examples:

// Type-safe rule handling with comprehensive coverage
const advancedVisitor = {
  Rule: {
    // Type-safe media rule handling
    media(rule) {
      // rule is automatically typed as MediaRule
      if (rule.value.query.mediaType === 'print') {
        // Remove print-specific rules in web builds
        return void 0;
      }
      return rule;
    },
    
    // Type-safe keyframes handling
    keyframes(rule) {
      // rule is automatically typed as KeyframesRule
      const name = rule.value.name;
      if (name.startsWith('legacy-')) {
        // Rename legacy animations
        return {
          ...rule,
          value: {
            ...rule.value,
            name: name.replace('legacy-', 'modern-')
          }
        };
      }
      return rule;
    },
    
    // Handle unknown at-rules
    unknown: {
      'custom-layout'(rule) {
        // Convert custom at-rule to standard CSS
        console.log('Converting custom layout rule');
        return { raw: `/* Converted: ${rule.prelude} */` };
      }
    }
  }
};

docs

bundling.md

index.md

rust-api.md

style-attributes.md

targets.md

transformation.md

visitors.md

tile.json