Advanced CSS syntax validation and matching against W3C specifications with support for properties, at-rules, and value types.
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;
}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);
}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);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');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);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>' }
}
});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}`);
});