or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

data-structures.mdgeneration.mdindex.mdparsing.mdtokenization.mdtraversal.mdutilities.mdvalidation.md
tile.json

validation.mddocs/

CSS Syntax Validation

Advanced CSS syntax validation and matching against W3C specifications with support for properties, at-rules, and value types.

Capabilities

Lexer Instance

The default lexer provides CSS syntax validation based on W3C specifications and MDN data:

const lexer: Lexer;

class Lexer {
  /** CSS-wide keywords (inherit, initial, unset, revert) */
  readonly cssWideKeywords: string[];
  /** Whether generic type matching is enabled */
  readonly generic: boolean;
  /** Units configuration */
  readonly units: UnitsConfig;
  /** At-rules definitions */
  readonly atrules: AtRulesConfig;
  /** Properties definitions */
  readonly properties: PropertiesConfig;
  /** Type definitions */  
  readonly types: TypesConfig;
  /** Structure definitions for AST validation */
  readonly structure: StructureConfig;
}

Structure Validation

Validates AST node structure against expected schemas:

/**
 * Validates AST structure against expected schemas
 * @param ast - AST node to validate
 * @throws {Error} If structure is invalid
 */
checkStructure(ast: CssNode): void;

/**
 * Validates at-rule name
 * @param atruleName - Name of at-rule to validate
 * @throws {Error} If at-rule name is invalid
 */
checkAtruleName(atruleName: string): void;

/**
 * Validates property name
 * @param propertyName - Property name to validate
 * @throws {Error} If property name is invalid
 */
checkPropertyName(propertyName: string): void;

/**
 * Validates at-rule prelude syntax
 * @param atruleName - At-rule name
 * @param prelude - Prelude AST to validate
 * @throws {Error} If prelude is invalid for the at-rule
 */
checkAtrulePrelude(atruleName: string, prelude: CssNode): void;

/**
 * Validates at-rule descriptor name
 * @param atruleName - At-rule name
 * @param descriptorName - Descriptor name to validate
 * @throws {Error} If descriptor is invalid for the at-rule
 */
checkAtruleDescriptorName(atruleName: string, descriptorName: string): void;

Usage Examples:

import { lexer, parse } from 'css-tree';

// Validate AST structure
try {
  const ast = parse('.example { color: red; }');
  lexer.checkStructure(ast);
  console.log('AST structure is valid');
} catch (error) {
  console.error('Invalid AST structure:', error.message);
}

// Validate property names
try {
  lexer.checkPropertyName('color');
  lexer.checkPropertyName('invalid-property'); // throws error
} catch (error) {
  console.error('Invalid property:', error.message);
}

// Validate at-rule names
try {
  lexer.checkAtruleName('media');
  lexer.checkAtruleName('invalid-rule'); // throws error
} catch (error) {
  console.error('Invalid at-rule:', error.message);
}

Syntax Matching

Match CSS values against property and type definitions:

/**
 * Matches declaration against property syntax
 * @param declaration - Declaration AST node
 * @returns Match result with validation information
 */
matchDeclaration(declaration: CssNode): MatchResult;

/**
 * Matches value against property syntax
 * @param propertyName - CSS property name
 * @param value - Value AST to match
 * @returns Match result with validation information
 */
matchProperty(propertyName: string, value: CssNode): MatchResult;

/**
 * Matches value against type syntax
 * @param typeName - CSS type name (e.g., 'color', 'length')
 * @param value - Value AST to match
 * @returns Match result with validation information
 */
matchType(typeName: string, value: CssNode): MatchResult;

/**
 * Matches value against arbitrary syntax definition
 * @param syntax - CSS syntax definition string
 * @param value - Value AST to match
 * @returns Match result with validation information
 */
match(syntax: string, value: CssNode): MatchResult;

interface MatchResult {
  /** Successfully matched AST node or null */
  matched: CssNode | null;
  /** Error information if match failed */
  error: MatchError | null;
  
  /** Check if specific node matches a type */
  isType(node: CssNode, type: string): boolean;
  /** Get trace information for matched node */
  getTrace(node: CssNode): TraceNode[];
}

interface MatchError {
  message: string;
  syntax: string;
  css: string;
  mismatchOffset: number;
  mismatchLength: number;
}

interface TraceNode {
  type: 'Type' | 'Property' | 'Keyword' | 'Combinator';
  name: string;
}

Usage Examples:

import { lexer, parse } from 'css-tree';

// Match property values
const colorValue = parse('red', { context: 'value' });
const colorMatch = lexer.matchProperty('color', colorValue);

if (colorMatch.matched) {
  console.log('Valid color value');
  console.log('Is color type:', colorMatch.isType(colorValue.children.first, 'color'));
} else {
  console.error('Invalid color:', colorMatch.error.message);
}

// Match against specific types
const lengthValue = parse('10px', { context: 'value' });
const lengthMatch = lexer.matchType('length', lengthValue);

// Match complex properties
const borderValue = parse('1px solid red', { context: 'value' });
const borderMatch = lexer.matchProperty('border', borderValue);

// Get detailed trace
if (borderMatch.matched) {
  const trace = borderMatch.getTrace(borderValue.children.first);
  console.log('Match trace:', trace);
}

// Match declarations
const declaration = parse('color: red', { context: 'declaration' });
const declMatch = lexer.matchDeclaration(declaration);

Fragment Search

Find specific fragments within CSS values:

/**
 * Finds fragments of specific type in property value
 * @param propertyName - CSS property name
 * @param value - Value AST to search
 * @param type - Fragment type to find
 * @param name - Specific name to match (optional)
 * @returns Array of found fragments
 */
findValueFragments(
  propertyName: string, 
  value: CssNode, 
  type: string, 
  name?: string
): Fragment[];

/**
 * Finds fragments in declaration value
 * @param declaration - Declaration AST node
 * @param type - Fragment type to find
 * @param name - Specific name to match (optional)
 * @returns Array of found fragments
 */
findDeclarationValueFragments(
  declaration: CssNode, 
  type: string, 
  name?: string
): Fragment[];

/**
 * Finds all fragments of type in entire AST
 * @param ast - AST to search
 * @param type - Fragment type to find
 * @param name - Specific name to match (optional)
 * @returns Array of found fragments
 */
findAllFragments(
  ast: CssNode, 
  type: string, 
  name?: string
): Fragment[];

interface Fragment {
  node: CssNode;
  property: string;
  value: CssNode;
}

Usage Examples:

import { lexer, parse } from 'css-tree';

const ast = parse(`
  .example {
    color: red;
    background: url(image.png) no-repeat;
    border: 1px solid #ff0000;
  }
`);

// Find all color fragments
const colorFragments = lexer.findAllFragments(ast, 'color');
colorFragments.forEach(fragment => {
  console.log(`Color in ${fragment.property}:`, generate(fragment.node));
});

// Find URLs
const urlFragments = lexer.findAllFragments(ast, 'url');
urlFragments.forEach(fragment => {
  console.log('Found URL:', generate(fragment.node));
});

// Find specific colors
const redFragments = lexer.findAllFragments(ast, 'color', 'red');

// Find fragments in specific property
const declaration = parse('margin: 10px 20px', { context: 'declaration' });
const lengthFragments = lexer.findDeclarationValueFragments(declaration, 'length');

Configuration Access

Access lexer configuration and definitions:

/**
 * Gets at-rule definition
 * @param atruleName - At-rule name
 * @param fallbackBasename - Fallback name if not found
 * @returns At-rule definition or null
 */
getAtrule(atruleName: string, fallbackBasename?: string): AtRuleDefinition | null;

/**
 * Gets property definition
 * @param propertyName - Property name
 * @param fallbackBasename - Fallback name if not found
 * @returns Property definition or null
 */
getProperty(propertyName: string, fallbackBasename?: string): PropertyDefinition | null;

/**
 * Gets type definition
 * @param typeName - Type name
 * @returns Type definition or null
 */
getType(typeName: string): TypeDefinition | null;

interface PropertyDefinition {
  syntax: string;
  media?: string;
  inherited?: boolean;
  animationType?: string;
  percentages?: string;
  groups?: string[];
  initial?: string;
  appliesto?: string;
  computed?: string;
  order?: string;
  status?: string;
}

Usage Examples:

// Get property information
const colorDef = lexer.getProperty('color');
console.log('Color syntax:', colorDef.syntax);
console.log('Color inherited:', colorDef.inherited);

// Check property support
const customProp = lexer.getProperty('custom-property');
if (!customProp) {
  console.log('Custom property not defined');
}

// Get type information
const lengthType = lexer.getType('length');
console.log('Length type definition:', lengthType);

// Get at-rule information
const mediaDef = lexer.getAtrule('media');
console.log('Media rule syntax:', mediaDef.prelude);

Custom Lexer Creation

Create custom lexers with extended or modified syntax definitions:

/**
 * Creates new lexer with custom configuration
 * @param config - Custom lexer configuration
 * @returns New lexer instance
 */
function createLexer(config: LexerConfig): Lexer;

interface LexerConfig {
  /** Custom property definitions */
  properties?: { [propertyName: string]: PropertyDefinition };
  /** Custom type definitions */
  types?: { [typeName: string]: string };
  /** Custom at-rule definitions */
  atrules?: { [atruleName: string]: AtRuleDefinition };
  /** Enable/disable generic type matching */
  generic?: boolean;
  /** Units configuration */
  units?: string[];
}

Usage Examples:

import { createLexer } from 'css-tree';

// Create lexer with custom properties
const customLexer = createLexer({
  properties: {
    'custom-color': {
      syntax: '<color>',
      inherited: false
    },
    'custom-size': {
      syntax: '<length> | <percentage>',
      inherited: false
    }
  },
  types: {
    'custom-keyword': 'small | medium | large'
  }
});

// Use custom lexer
const value = parse('small', { context: 'value' });
const match = customLexer.matchType('custom-keyword', value);

// Extend existing lexer
const extendedLexer = lexer.fork({
  properties: {
    'new-property': { syntax: '<number>' }
  }
});

Validation Error Handling

Handle validation errors gracefully:

function validateCSS(css) {
  const errors = [];
  
  try {
    const ast = parse(css);
    
    walk(ast, {
      Declaration: (node) => {
        try {
          const match = lexer.matchDeclaration(node);
          if (!match.matched) {
            errors.push({
              type: 'InvalidValue',
              property: node.property.name,
              value: generate(node.value),
              message: match.error.message
            });
          }
        } catch (error) {
          errors.push({
            type: 'ValidationError',
            property: node.property.name,
            message: error.message
          });
        }
      }
    });
  } catch (error) {
    errors.push({
      type: 'ParseError',
      message: error.message
    });
  }
  
  return errors;
}

// Usage
const errors = validateCSS('.example { color: invalid; width: -10px; }');
errors.forEach(error => {
  console.error(`${error.type}: ${error.message}`);
});