Gale
An extremely fast CSS linter. Drop-in replacement for Stylelint.
Gale reads your existing .stylelintrc, runs the same rules, and produces the same output. Just 100x-400x faster.
One line change in your package.json. No config migration.
Benchmarks
Real-world benchmarks using hyperfine (10 runs, 3 warmup). Each repo uses its own Stylelint config.
| Repository | Files | Stylelint | Gale | Speedup |
|---|---|---|---|---|
| Bootstrap | 99 | 1.569s | 0.011s | 143x |
| Carbon | 1,116 | 8.767s | 0.022s | 399x |
| PatternFly | 204 | 5.298s | 0.015s | 353x |
| Spectrum CSS | 241 | 2.952s | 0.011s | 268x |
| GOV.UK Frontend | 163 | 1.614s | 0.011s | 147x |
| Primer CSS | 113 | 1.285s | 0.010s | 129x |
| Gutenberg | 775 | 4.715s | 0.042s | 112x |
| wp-calypso | 2,238 | 13.223s | 0.131s | 101x |
| Angular Components | 620 | 1.843s | 0.021s | 88x |
| Discourse | 355 | 1.438s | 0.021s | 69x |
Parity: 0 false positives and 0 false negatives across all 16 tested repositories (6,673 files).
Reproduce these results: ./benchmarks/benchmark.sh
Quick start
# Install
# Lint (uses your existing .stylelintrc)
# Autofix
Migrate from Stylelint
Change one line in package.json:
{
"scripts": {
- "lint:css": "stylelint 'src/**/*.css'"
+ "lint:css": "gale 'src/**/*.css'"
}
}
Your .stylelintrc stays exactly the same. Gale reads the same config files, follows the same extends chains, honors /* stylelint-disable */ comments, and produces the same JSON output format.
Installation
npm (recommended)
The npm package automatically downloads the correct platform binary on install. Supported platforms: macOS (arm64, x64), Linux (x64, arm64).
Cargo
The crate is named gale-lint on crates.io (since gale was taken), but the installed binary is called gale.
From source
# Binary at target/release/gale
GitHub releases
Download pre-built binaries from GitHub Releases.
What's supported
250 built-in rules
Gale ships 250 built-in rules across four categories:
| Category | Count | Examples |
|---|---|---|
| Core Stylelint | 144 | block-no-empty, color-no-invalid-hex, property-no-unknown |
SCSS (scss/*) |
44 | scss/at-rule-no-unknown, scss/no-duplicate-mixins, scss/dollar-variable-pattern |
Stylistic (@stylistic/*) |
59 | stylistic/indentation, stylistic/declaration-colon-space-after, stylistic/no-eol-whitespace |
Order (order/*) |
3 | order/order, order/properties-order, order/properties-alphabetical-order |
SCSS and stylistic rules are built in -- no extra plugins required.
Config compatibility
All Stylelint config formats are supported:
| File | Format |
|---|---|
gale.json |
JSON (native) |
gale.toml |
TOML (native) |
.stylelintrc |
JSON or YAML |
.stylelintrc.json |
JSON |
.stylelintrc.yml / .yaml |
YAML |
stylelint.config.js / .cjs |
JavaScript |
Feature overview
- SCSS and Less out of the box (no plugins needed)
- Autofix via
--fix - File caching via
--cache(skips unchanged files) - LSP server for editor integration (
--lsp) - Parallel linting using all CPU cores
- Inline disable comments (
stylelint-disableandgale-disable) - JSON, text, and compact output formatters matching Stylelint's format
extendswith built-in presets, npm packages, and relative paths.galeignorefiles (gitignore syntax) for custom exclusions
Not yet supported
- Custom JavaScript plugins. Third-party rule packages (community plugins) are not supported. Gale only runs its built-in rules.
package.jsonconfig. The"stylelint"field inpackage.jsonis not read.- Sass indented syntax.
.sassfiles are not supported (.scssworks fine).
Configuration
Gale searches for config files walking up from the working directory. To generate a starter config:
Example config
Rule value formats
| Format | Meaning |
|---|---|
true |
Enable at error severity |
false or "off" |
Disable |
"error" |
Enable at error severity |
"warning" |
Enable at warning severity |
["error", { options }] |
Enable with options |
Built-in presets
| Preset | Description |
|---|---|
gale:recommended |
Sensible defaults (29 rules: 15 error + 14 warning) |
gale:all |
All rules enabled at warning severity |
You can also extend npm packages like stylelint-config-standard directly.
Extends resolution
The extends field supports:
| Value | Resolution |
|---|---|
"gale:recommended" |
Built-in preset |
"gale:all" |
Built-in preset |
"./path/to/config.json" |
Relative path to another config file |
"stylelint-config-standard" |
npm package (resolved from node_modules/) |
Resolution is recursive with cycle detection. Later extends entries override earlier ones. User rules always override extended rules.
CLI reference
gale [OPTIONS] [FILES]...
| Flag | Description |
|---|---|
<files> |
Files, directories, or glob patterns to lint |
--fix |
Automatically fix problems |
-q, --quiet |
Only report errors |
-f, --formatter <type> |
Output: text (default), json, compact |
-c, --config <path> |
Config file path |
--max-warnings <n> |
Error if warnings exceed threshold |
--cache |
Skip unchanged files |
--cache-location <path> |
Custom cache file path (default: .gale_cache) |
--stdin |
Read from stdin |
--stdin-filename <name> |
Virtual filename for stdin (default: stdin.css) |
--ignore-path <file> |
Custom ignore file (gitignore syntax) |
--no-ignore |
Disable all ignore file processing |
--print-config <file> |
Print resolved config as JSON |
--init |
Generate starter config |
--lsp |
Start LSP server |
Editor integration
VS Code
A VS Code extension is included at editors/vscode/.
Any LSP-compatible editor
Works with Neovim, Helix, Zed, and any editor supporting the Language Server Protocol.
Development
Prerequisites
- Rust 2024 edition (1.85+)
- Python 3 (for differential tests)
- Node.js 16+ (for differential tests and npm packaging)
Build and test
Run the linter
Debug and profiling
GALE_DEBUG_PERF=1 GALE_LOG=debug
Differential testing
Compare Gale output against Stylelint on real-world repositories:
The test corpus includes Bootstrap, Gutenberg, Carbon, Angular Components, wp-calypso, Discourse, GOV.UK Frontend, Spectrum CSS, Docusaurus, Grafana, Material UI, freeCodeCamp, PatternFly, Primer CSS, Elastic EUI, and Mattermost.
Benchmarks
Releasing
Releases are automated via GitHub Actions when you push a version tag:
# 1. Update the version in Cargo.toml (workspace.package.version)
# 2. Commit the version bump
# 3. Tag and push
&&
The release workflow will:
- Build binaries for Linux x64 and arm64
- Create a GitHub Release with the binaries
- Publish the npm package (
@lyricalstring/gale) with the matching version
Manual npm build
# Build for current platform
# Build for all platforms (requires cross + Docker)
# Set npm package version before building
Architecture
Gale is organized as a Cargo workspace with seven crates:
gale (binary)
|
v
gale_cli CLI definition (clap), file discovery, orchestration
|
+-- gale_config Config loading, resolution, presets
+-- gale_linter Rule trait, registry, runner, 250 built-in rules
| +-- gale_css_parser CSS/SCSS/Less parser (lightningcss + raffia)
| +-- gale_diagnostics Span, Diagnostic, LintResult, Fix/Edit types
+-- gale_formatter Output formatters (text, json, compact)
+-- gale_lsp Language Server Protocol server
| Crate | Responsibility |
|---|---|
gale_css_parser |
Wraps lightningcss (CSS) and raffia (SCSS/Less) into a unified, owned AST |
gale_diagnostics |
Core types: Span, Diagnostic, LintResult, Fix, Edit |
gale_linter |
Rule trait, RuleRegistry, LintRunner, inline disable comments, all rule implementations |
gale_config |
Config file discovery, parsing (JSON/YAML/TOML/JS), extends resolution, built-in presets |
gale_formatter |
TextFormatter, JsonFormatter, CompactFormatter matching Stylelint output |
gale_cli |
Clap CLI, file discovery with ignore support, cache layer, --fix orchestration |
gale_lsp |
LSP server for real-time editor diagnostics |
See CLAUDE.md for detailed architecture docs and how to add rules.