jdz / cssmaker
Build minified css file from less sources
Requires
- php: >=8.2
- jdz/data: ^2.0
- jdz/output: ^1.0 || ^2.0
- symfony/process: ^7.4
- symfony/yaml: ^7.4
Requires (Dev)
- jdz/fontmanager: ^2.0
- phpunit/phpunit: ^11.0
Suggests
- jdz/fontmanager: To manage local fonts and fontfaces
Provides
None
Conflicts
None
Replaces
None
README
A modern PHP library for building optimized CSS files from LESS sources with advanced preprocessing, autoprefixing, and minification capabilities.
Features
- ๐จ LESS Compilation: Process LESS files with variables, mixins, and nesting
- ๐ PostCSS Integration: Automatic vendor prefix addition and CSS optimization
- ๐งน CSS Optimization: Comment removal, whitespace cleaning, and minification
- ๐ Modular Organization: Structured approach with categorized file types
- ๐ง Font Management: Built-in font loading and @font-face generation
- โก Performance: Efficient temporary file handling and process pipeline
- ๐งช Fully Tested: Comprehensive test suite with modular test architecture
- ๐ Fluent Interface: Method chaining for clean, readable code
Installation
composer require jdz/cssmaker
Node.js Dependencies
CssMaker requires Node.js tools for CSS processing. Install them automatically:
composer npm:local composer npm:global
Dependencies
jdz/data is required for data handling and processing.
jdz/output is required for output handling and logging.
jdz/fontmanager is optional (suggested): the bundled example uses it to manage local fonts before handing them to addFont(); the @font-face generation itself is built in.
Quick Start
<?php use JDZ\CssMaker\CssMaker; // Initialize with optional custom output handler $cssMaker = new CssMaker(); // Set build paths (creates directory structure automatically) $cssMaker->setBuildPaths('/path/to/project', 'build'); // Add LESS files by category $cssMaker->addLessFiles([ 'variables' => ['config/variables.yml'], 'mixins' => ['src/mixins.less'], 'normalize' => ['src/normalize.less'], 'structure' => ['src/layout.less', 'src/components.less'], 'mobile' => ['src/mobile.less'], 'print' => ['src/print.less'] ]); // Add fonts (optional) // see jdz/fontmanager for more infos $fontData = (object) [ 'id' => 'roboto', 'family' => 'Roboto', 'display' => 'swap', 'style' => 'normal', 'weight' => '400', 'files' => ['fonts/roboto.woff2', 'fonts/roboto.woff'] ]; $cssMaker->addFont($fontData); // Process and build CSS (creates: theme.less, theme.css, theme.min.css) $cssMaker->process('theme');
File Categories
CssMaker organizes LESS files into logical categories processed in order. You can extend the CssMaker class and add your own categories as needed.
Core Categories
variables: LESS/YAML variables and configurationmixins: Reusable LESS mixins and functionsnormalize: CSS reset and normalization rulesanimations: CSS animations and keyframesfonts: Font declarations and @font-face rules
Layout Categories
structure: Main layout and component stylesicons: Icon fonts and SVG styles
Responsive Categories
mobile: Mobile-first styles (wrapped in@media(max-width: @screen-breakpoint - 1px))screen: Desktop styles (wrapped in@media(min-width: @screen-breakpoint))queries: Custom media queriesprint: Print-specific styles (wrapped in@media print)
PostCSS
process() runs postcss on the compiled CSS with two plugins, autoprefixer and postcss-discard-comments, reading it with postcss-safe-parser. No configuration file is involved: the plugins are passed on the command line, and the safe parser goes through resources/postcss-safe-syntax.cjs (postcss-cli 11 cannot load it as --parser).
The autoprefixer target is a list of browserslist queries, handed to postcss in the BROWSERSLIST environment variable, which takes precedence over any .browserslistrc or package.json key:
$cssMaker->getBrowserslist(); // ['> .5%', 'last 2 versions', 'not dead'] (default) $cssMaker->setBrowserslist(['defaults']); $cssMaker->setBrowserslist(['> .1%', 'not dead']);
Advanced Features
Variable Management
use JDZ\CssMaker\Variables; // Variables support YAML files $variables = new Variables(); $variables->set('primary-color', '#3498db'); $variables->set('screen-breakpoint', '768px'); // YAML format (variables.yml): # primary_color: "#3498db" # screen_breakpoint: "768px" # font_sizes: # small: "12px" # medium: "16px" # large: "24px"
Custom Output Handler
use JDZ\Output\Output; $output = new Output(); $output->setVerbosity(32); // all: Output::VERBOSITY_ALL on jdz/output 1.x, Verbosity::ALL on 2.x $cssMaker = new CssMaker($output); // Now you'll see detailed processing information
Processing Pipeline
The CssMaker follows a structured build pipeline:
-
๐ง Preparation
- Load variables from YAML/LESS files
- Validate directory structure
- Initialize temporary file management
-
๐ Content Merging
- Merge variables into LESS format
- Combine mixins and normalize files
- Add font @font-face declarations
- Process files by category with media query wrapping
-
โ๏ธ LESS Compilation
- Compile merged LESS to CSS using
lessc - Handle variables, mixins, nesting, and functions
- Generate source CSS file
- Compile merged LESS to CSS using
-
๐ฏ PostCSS Processing
- Apply autoprefixer for vendor prefixes, for the
setBrowserslist()target - Drop comments with postcss-discard-comments
- Read the CSS with postcss-safe-parser
- Apply autoprefixer for vendor prefixes, for the
-
๐งน Optimization
- Remove CSS comments and unnecessary whitespace
- Clean and normalize formatting
- Apply minification
-
๐ฆ Output Generation
- Generate final CSS file
- Create minified version (.min.css)
- Clean up temporary files
Directory Structure
project/
โโโ build/ # Output directory
โโโ css/ # Generated CSS files
โ โโโ theme.less # Merged LESS
โ โโโ theme.css # Compiled CSS
โ โโโ theme.min.css # Minified CSS
โโโ fonts/ # Copied font files
โโโ images/ # Copied image files
โโโ fonts/ # Font source files (optional, used by the font manager)
โโโ less/ # LESS source files
โ โโโ variables.yml # LESS variables
โ โโโ mixins.less # LESS mixins
โ โโโ normalize.less # CSS normalization
โ โโโ structure.less # Main styles
โ โโโ mobile.less # Mobile styles
โ โโโ print.less # Print styles
โโโ tmp/ # Temporary files (auto-created)
Error Handling
CssMaker provides comprehensive error handling:
use JDZ\CssMaker\Exception\LessMakerException; try { $cssMaker->process('theme'); } catch (LessMakerException $e) { // Handle CssMaker-specific errors echo "CSS Build Error: " . $e->getMessage(); } catch (\Exception $e) { // Handle general errors echo "General Error: " . $e->getMessage(); }
Common Error Scenarios:
- Missing required directories
- Invalid LESS syntax
- Missing Node.js dependencies (
lessc,postcss,minify) - A font file that
addFont()cannot find or copy (Font file not found: โฆ,Font file not copied: โฆ to โฆ) - File permission issues
- Malformed configuration files
Testing
CssMaker includes a comprehensive test suite. The tests that run lessc, postcss and minify need the Node.js tools (composer npm:local or npm:global).
# Run all tests composer test # Run specific test classes composer test:cssmaker composer test:cleaner composer test:variables composer test:merger
Example Usage
See the complete working example:
composer example
The example demonstrates:
- Full project setup with directory structure
- Extended CssMaker class
- Configuration file usage
- Font integration
- Multiple file type processing
- Error handling
Browser Support
The default target, > .5%, last 2 versions, not dead:
- Modern Browsers: Chrome, Firefox, Safari, Edge (last 2 versions)
- Market Share: Browsers with > 0.5% usage
- Security: Excludes browsers without security updates
- Mobile: iOS Safari, Chrome Mobile
Customize with setBrowserslist():
// browserslist's own defaults $cssMaker->setBrowserslist(['defaults']); // Conservative approach $cssMaker->setBrowserslist(['> 1%', 'last 3 versions', 'not dead']); // Modern approach $cssMaker->setBrowserslist(['> 0.25%', 'last 2 versions', 'not dead']);
Requirements
- PHP: >= 8.2
- Composer: For dependency management
- Node.js + npm: for the
lessc,postcss(postcss-cli, autoprefixer, postcss-safe-parser, postcss-discard-comments) andminifycommand line tools โcomposer npm:localorcomposer npm:globalinstalls them. If they are not on thePATH, pass their directory (with a trailing slash) as the second constructor argument:new CssMaker($output, '/path/to/node_modules/.bin/').
PHP Dependencies
jdz/output: ^1.0 || ^2.0 (Output handling)jdz/data: ^2.0 (Data processing)symfony/yaml: ^7.4 (YAML parsing)symfony/process: ^7.4 (External process execution)jdz/fontmanager(Font management - optional, suggested)
Performance Tips
- Use file caching - Only rebuild when source files change
- Minimize file count - Combine related LESS files
- Optimize images - Use appropriate formats and compression
- Monitor build time - Profile with verbose output
- Cache font files - Reuse font declarations across builds
Troubleshooting
Common Issues
"lessc command not found"
npm install -g less
"postcss command not found"
npm install -g postcss postcss-cli autoprefixer postcss-safe-parser postcss-discard-comments
"minify command not found"
npm install -g minify
"Permission denied" errors
chmod 755 /path/to/build/directory
"LESS compilation failed"
- Check LESS syntax in source files
- Verify variable names and references
- Ensure all imported files exist
Debug Mode
Enable verbose output for troubleshooting:
use JDZ\Output\Output; $output = new Output(); $output->setVerbosity(32); // all: Output::VERBOSITY_ALL on jdz/output 1.x, Verbosity::ALL on 2.x $cssMaker = new CssMaker($output); // Detailed processing information will be displayed
License
MIT License - see LICENSE file for details.
Contributing
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Add tests for new functionality
- Ensure all tests pass (
composer test) - Follow PSR-12 coding standards
- Update documentation as needed
- Commit changes (
git commit -m 'Add amazing feature') - Push to branch (
git push origin feature/amazing-feature) - Open a Pull Request
Changelog
-
1.0.4 โ
postcss()applies its plugins: the arguments were out of order (--configswallowed--no-map,postcss.jsonwas read as a second input) and postcss-cli 8+ never reads apostcss.json, so autoprefixer and postcss-discard-comments never ran. The plugins are now passed on the command line, postcss-safe-parser through a syntax wrapper (resources/postcss-safe-syntax.cjs), and the autoprefixer target issetBrowserslist()(default> .5%, last 2 versions, not dead), passed inBROWSERSLIST;postcss.jsonand.browserslistrcare gone. The unminified CSS loses its comments and the spaces before{and after:. A font file thataddFont()cannot find or copy throws aLessMakerExceptionwith its path (it was logged and skipped, leaving an@font-facepointing at nothing).jdz/output^1.0 || ^2.0. -
1.0.3 โ The cleaner no longer strips
//"comments": lessc already removes them, and the pattern cut every URL (url(https://โฆ), SVG data URIs) to the end of its line. Each/* */comment ends at its own close (/**/used to swallow the next rule); no-break spaces and non-UTF-8 bytes survive (removeSpaces()was a TypeError on Latin-1).@font-facelists woff2 before ttf. A secondprocess()on one maker merges the font themes once; minify writes through a build path with spaces; a font without an id throws its own error.trim()replacesmb_trim()(PHP 8.4 only, underphp >=8.2). -
1.0.2 โ PHP >= 8.2,
jdz/data^2.0, Symfony ^7.4, PHPUnit 11; the cleaner also strips//single-line comments, and removes comments before collapsing whitespace. -
1.0.1 โ Output goes through
jdz/output(the bundledOutputclass is gone); test suite split per feature. -
1.0.0 โ Initial release.
Support
- ๐ง Email: joffrey.demetz@gmail.com
- ๐ Website: https://joffreydemetz.com
- ๐ฆ Package: https://packagist.org/packages/jdz/cssmaker
- ๐ Issues: GitHub Issues