or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

advanced-features.mdbatch-processing.mdcompatibility.mdconstructor-options.mdindex.mdinput-output.mdoptimization-levels.md
tile.json

advanced-features.mddocs/

Advanced Features

Clean-CSS provides advanced features including source map generation, @import inlining, URL rebasing, and a flexible plugin system for extending functionality.

Source Maps

Source maps enable debugging of minified CSS by mapping the compressed output back to the original source files.

Source Map Configuration

interface SourceMapOptions {
  sourceMap?: boolean;                    // Enable source map generation
  sourceMapInlineSources?: boolean;      // Embed sources in source map
}

Generating Source Maps

const CleanCSS = require('clean-css');

const sourceMapMinifier = new CleanCSS({
  sourceMap: true,
  sourceMapInlineSources: true  // Include original source content
});

const css = `
  .header {
    background-color: #ffffff;
    padding: 20px;
  }
`;

const result = sourceMapMinifier.minify(css);

console.log('Minified CSS:', result.styles);
console.log('Source map:', result.sourceMap);

// Write files
const fs = require('fs');
fs.writeFileSync('output.css', result.styles);
fs.writeFileSync('output.css.map', JSON.stringify(result.sourceMap));

Input Source Maps

Process existing source maps from preprocessors like Sass or Less:

const inputSourceMap = JSON.parse(fs.readFileSync('input.css.map', 'utf8'));
const css = fs.readFileSync('input.css', 'utf8');

const result = sourceMapMinifier.minify(css, inputSourceMap);

// The output source map will maintain the chain back to original sources

Source Map with File Hash

const fileHash = {
  'main.scss.css': {
    styles: compiledMainCSS,
    sourceMap: mainSourceMap
  },
  'components.scss.css': {
    styles: compiledComponentsCSS,
    sourceMap: componentsSourceMap
  }
};

const result = sourceMapMinifier.minify(fileHash);
// Combined source map maintains mapping to all original .scss files

@import Inlining

Clean-CSS can process @import rules and inline the referenced stylesheets directly into the output.

Import Inlining Configuration

interface ImportOptions {
  inline?: string | string[];        // Import inlining rules
  inlineRequest?: object;           // HTTP request options
  inlineTimeout?: number;           // Timeout for remote fetching
  fetch?: object;                   // Custom fetch function
}

Inline Option Values

// String values:
const localOnlyMinifier = new CleanCSS({ inline: 'local' });      // Local files only (default)
const remoteOnlyMinifier = new CleanCSS({ inline: 'remote' });    // Remote files only
const allMinifier = new CleanCSS({ inline: 'all' });              // All imports
const noneMinifier = new CleanCSS({ inline: 'none' });            // No inlining
const falseMinifier = new CleanCSS({ inline: false });            // Same as 'none'

// Array values for granular control:
const customMinifier = new CleanCSS({ 
  inline: ['local', 'fonts.googleapis.com']  // Local files and specific domain
});

Local Import Processing

const localInlineMinifier = new CleanCSS({ 
  inline: 'local',
  rebaseTo: 'dist/css/'
});

const css = `
  @import "normalize.css";
  @import "components/buttons.css";
  @import url("themes/default.css");
  
  body {
    font-family: Arial, sans-serif;
  }
`;

const result = localInlineMinifier.minify(css);
// All local @import rules are resolved and inlined
// result.inlinedStylesheets contains the file paths that were inlined

Remote Import Processing

const remoteInlineMinifier = new CleanCSS({
  inline: 'remote',
  inlineTimeout: 10000,  // 10 second timeout
  inlineRequest: {
    headers: {
      'User-Agent': 'CleanCSS/5.3.3'
    }
  }
});

const css = `
  @import url("https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;700");
  @import "local-styles.css";  // This will NOT be inlined
  
  body {
    font-family: 'Roboto', sans-serif;
  }
`;

const result = remoteInlineMinifier.minify(css);

Custom Fetch Function

/**
 * Custom fetch function for handling remote resource requests
 * @typedef {function} FetchFunction
 * @param {string} uri - Resource URI to fetch
 * @param {object} inlineRequest - HTTP request options
 * @param {number} inlineTimeout - Request timeout in milliseconds
 * @param {function} callback - Callback function (error, body)
 */

Using a Custom Fetch Function:

const customFetchMinifier = new CleanCSS({
  inline: 'all',
  fetch: function(uri, inlineRequest, inlineTimeout, callback) {
    // Custom fetch implementation
    const request = require('https').get(uri, {
      timeout: inlineTimeout,
      headers: inlineRequest.headers || {}
    }, (response) => {
      let body = '';
      response.on('data', chunk => body += chunk);
      response.on('end', () => callback(null, body));
    });
    
    request.on('error', callback);
    request.on('timeout', () => callback(new Error('Request timeout')));
  }
});

Fetch Function with Proxy Support:

const proxyFetchMinifier = new CleanCSS({
  inline: 'remote',
  fetch: function(uri, inlineRequest, inlineTimeout, callback) {
    const proxyOptions = {
      hostname: 'proxy.company.com',
      port: 8080,
      path: uri,
      headers: {
        'User-Agent': 'CleanCSS/5.3.3',
        'Proxy-Authorization': 'Basic ' + Buffer.from('user:pass').toString('base64')
      }
    };
    
    const request = require('http').get(proxyOptions, (response) => {
      let body = '';
      response.on('data', chunk => body += chunk);
      response.on('end', () => callback(null, body));
    });
    
    request.on('error', callback);
    request.setTimeout(inlineTimeout, () => {
      request.abort();
      callback(new Error('Fetch timeout'));
    });
  }
});

URL Rebasing

URL rebasing adjusts relative paths in CSS url() functions to work correctly from the output location.

URL Rebasing Configuration

interface RebaseOptions {
  rebase?: boolean;        // Enable URL rebasing
  rebaseTo?: string;       // Target directory for rebased URLs
}

Basic URL Rebasing

const rebaseMinifier = new CleanCSS({
  rebase: true,
  rebaseTo: 'dist/css/'  // Output directory
});

// Input CSS from src/styles/main.css:
const css = `
  .logo {
    background-image: url('../images/logo.png');
  }
  
  .icon {
    background-image: url('../../assets/icons/menu.svg');
  }
`;

const result = rebaseMinifier.minify(css);
// URLs are rebased relative to dist/css/
// url('../images/logo.png') becomes url('../../src/images/logo.png')

Absolute Path Rebasing

const absoluteRebaseMinifier = new CleanCSS({
  rebase: true,
  rebaseTo: '/var/www/html/assets/css/'
});

// All relative URLs are rebased to work from the absolute target path

Rebasing with Import Inlining

const rebaseInlineMinifier = new CleanCSS({
  inline: 'local',
  rebase: true,
  rebaseTo: 'build/css/'
});

// When files are inlined, their URLs are also rebased appropriately

Plugin System

The plugin system allows you to extend Clean-CSS with custom optimizations at different levels.

Plugin Configuration

/**
 * Plugin configuration for extending Clean-CSS functionality
 * @typedef {object} PluginObject
 * @property {Level1Plugin[]} [plugins] - Array of plugin objects
 */

/**
 * Level 1 plugin for property and value transformations
 * @typedef {object} Level1Plugin
 * @property {object} [level1] - Level 1 plugin methods
 * @property {function} [level1.property] - Property transformation function
 * @property {function} [level1.value] - Value transformation function
 */

/**
 * Level 2 plugin for block-level transformations
 * @typedef {object} Level2Plugin  
 * @property {object} [level2] - Level 2 plugin methods
 * @property {function} [level2.block] - Block transformation function
 */

/**
 * Complete plugin structure
 * @typedef {object} Plugin
 * @property {object} [level1] - Level 1 transformations
 * @property {function} [level1.property] - Transform properties: (rule, property) => void
 * @property {function} [level1.value] - Transform values: (propertyName, propertyValue) => string
 * @property {object} [level2] - Level 2 transformations
 * @property {function} [level2.block] - Transform token blocks: (tokens) => void
 */

Level 1 Plugins

Level 1 plugins operate on individual properties and values:

const pluginMinifier = new CleanCSS({
  level: {
    1: {
      // Enable all default level 1 optimizations
    }
  },
  plugins: {
    level1: {
      // Custom property transformation
      property: function(rule, property) {
        // Transform property based on rule context
        if (property.name === 'color' && property.value === 'black') {
          property.value = '#000';
        }
      },
      
      // Custom value transformation  
      value: function(propertyName, propertyValue) {
        // Transform specific property values
        if (propertyName === 'font-weight' && propertyValue === 'normal') {
          return '400';
        }
        return propertyValue;
      }
    }
  }
});

Level 2 Plugins

Level 2 plugins operate on token blocks and rules:

const blockPluginMinifier = new CleanCSS({
  level: {
    2: {
      // Enable default level 2 optimizations
    }
  },
  plugins: {
    level2: {
      block: function(tokens) {
        // Custom block-level transformations
        tokens.forEach(token => {
          if (token.kind === 'rule') {
            // Custom rule processing
            optimizeCustomRules(token);
          }
        });
      }
    }
  }
});

Complete Plugin Example

const customOptimizer = new CleanCSS({
  level: {
    1: {
      variableValueOptimizers: ['color', 'fraction']
    },
    2: {
      mergeAdjacentRules: true,
      mergeIntoShorthands: true
    }
  },
  plugins: {
    level1: {
      property: function(rule, property) {
        // Remove vendor prefixes for modern browsers
        if (property.name.startsWith('-webkit-') ||
            property.name.startsWith('-moz-') ||
            property.name.startsWith('-ms-')) {
          
          const standardProperty = property.name.replace(/^-\w+-/, '');
          if (hasWideSupport(standardProperty)) {
            property.unused = true;  // Mark for removal
          }
        }
      },
      
      value: function(propertyName, propertyValue) {
        // Convert legacy color formats
        if (propertyName.includes('color')) {
          return optimizeColorValue(propertyValue);
        }
        return propertyValue;
      }
    },
    
    level2: {
      block: function(tokens) {
        // Remove duplicate media queries
        removeDuplicateMediaQueries(tokens);
        
        // Merge similar selectors
        mergeSimilarSelectors(tokens);
      }
    }
  }
});

function hasWideSupport(property) {
  // Check if property has wide browser support
  const wellSupportedProperties = [
    'transform', 'transition', 'animation', 'border-radius'
  ];
  return wellSupportedProperties.includes(property);
}

function optimizeColorValue(value) {
  // Custom color optimization logic
  const colorMap = {
    '#ffffff': '#fff',
    '#000000': '#000',
    'rgba(0,0,0,1)': '#000'
  };
  return colorMap[value] || value;
}

Format and Output Control

Format Configuration

interface FormatOptions {
  breaks?: BreakOptions;
  breakWith?: 'lf' | 'crlf' | 'system';
  indentBy?: number;
  indentWith?: 'space' | 'tab';
  spaces?: SpaceOptions;
  wrapAt?: number;
  semicolonAfterLastProperty?: boolean;
}

interface BreakOptions {
  afterAtRule?: boolean | number;
  afterBlockBegins?: boolean | number;
  afterBlockEnds?: boolean | number;
  afterComment?: boolean | number;
  afterProperty?: boolean | number;
  afterRuleBegins?: boolean | number;
  afterRuleEnds?: boolean | number;
  beforeBlockEnds?: boolean | number;
  betweenSelectors?: boolean | number;
}

Beautified Output

const beautifyMinifier = new CleanCSS({
  format: 'beautify'  // Shortcut for readable formatting
});

// Or custom formatting:
const customFormatMinifier = new CleanCSS({
  format: {
    breaks: {
      afterProperty: true,
      afterRuleEnds: 2,      // Two line breaks after rules
      afterComment: 1
    },
    spaces: {
      beforeBlockBegins: true
    },
    indentBy: 2,
    indentWith: 'space',
    breakWith: 'lf'
  }
});

Keep Breaks Format

const keepBreaksMinifier = new CleanCSS({
  format: 'keep-breaks'  // Preserve existing line breaks
});

Error Handling and Debugging

Comprehensive Error Processing

async function processCSS(input) {
  const minifier = new CleanCSS({
    returnPromise: true,
    level: 2,
    sourceMap: true,
    inline: 'all',
    rebase: true
  });
  
  try {
    const result = await minifier.minify(input);
    
    // Analyze results
    const analysis = {
      success: result.errors.length === 0,
      warnings: result.warnings,
      errors: result.errors,
      stats: result.stats,
      inlinedFiles: result.inlinedStylesheets,
      hasSourceMap: !!result.sourceMap
    };
    
    // Log performance metrics
    console.log(`Minification completed in ${result.stats.timeSpent}ms`);
    console.log(`Size reduction: ${(result.stats.efficiency * 100).toFixed(1)}%`);
    
    if (result.warnings.length > 0) {
      console.warn('Warnings:', result.warnings);
    }
    
    if (result.errors.length > 0) {
      console.error('Errors:', result.errors);
    }
    
    return {
      css: result.styles,
      sourceMap: result.sourceMap,
      analysis
    };
    
  } catch (errors) {
    throw new Error(`Minification failed: ${errors.join(', ')}`);
  }
}