Lint your Tailwind classes
style_lint is an analyzer plugin for the style package. It reads the Tailwind class strings you pass to Style(...) and reports typo’d classes and variants directly in your IDE — the same place you already see built-in Dart warnings.
Installation
Section titled “Installation”-
Enable the plugin in your
analysis_options.yaml.style_lintis an analyzer plugin — it’s declared only here (with its source and version), not as apubspec.yamldependency; the analysis server resolves it itself:analysis_options.yaml plugins:style_lint:hosted: https://pub.mariuti.comversion: ^1.0.0 # Replace with the latest version -
Restart the Dart Analysis Server (in most IDEs: “Dart: Restart Analysis Server” from the command palette, or just restart the IDE). Plugin isolates are only (re)loaded on startup, so this is required after every change to the
plugins:section — a bare save is not enough.
The rules
Section titled “The rules”| Rule | Default | What it catches |
|---|---|---|
unknown_class | On (warning) | Style('bg-blu-500') — typo’d class; today this silently renders nothing. |
unknown_variant | On (warning) | Style('hove:bg-blue-500') — typo’d variant; the base class still applies, silently dropping the variant. |
duplicate_class | On (warning) | Style('p-4 p-4') — the same token repeated verbatim in one classes string. |
conflicting_classes | On (warning) | Style('bg-red-500 bg-blue-500') — two classes touching the same property; StyleSpec merges classes last-wins, so the earlier one is silently dropped. |
unsorted_classes | On (info) | Style('text-white p-4') instead of Style('p-4 text-white') — classes not in canonical Tailwind order. |
All five rules are on by default. unknown_class and unknown_variant point at a genuine authoring mistake; the other three are style nits — a duplicate, conflicting, or unsorted class still renders — but they surface as you type so you can clean them up, and any of them can be turned off per-project (see below). unsorted_classes is reported at info severity rather than warning — it’s the noisiest rule (it fires on every out-of-order string), so it’s kept as a quiet hint.
duplicate_class and conflicting_classes each offer an IDE quick-fix that removes the offending token: “Style: Remove duplicate class” always, and “Style: Remove conflicting class” only when the overridden class is fully covered by the class that wins (so removing it can’t change anything the winner doesn’t already set). When it isn’t fully covered, the fix isn’t offered — decide which class to keep yourself.
Turning rules off (or changing severity)
Section titled “Turning rules off (or changing severity)”Every rule is enabled by default; the diagnostics: map turns individual rules off or overrides their severity — for example, silence the sort nag and demote unknown_class to a hint:
plugins: style_lint: hosted: https://pub.mariuti.com version: ^1.0.0 diagnostics: unsorted_classes: false # off entirely unknown_class: info # still reported, but as a hint, not a warningSuppressing diagnostics
Section titled “Suppressing diagnostics”// ignore: style_lint/unknown_classStyle('bg-blu-500', child: child);// ignore_for_file: works the same way, with the same qualified name:
// ignore_for_file: style_lint/unknown_class, style_lint/unsorted_classesstyle_lint: in analysis_options.yaml
Section titled “style_lint: in analysis_options.yaml”Custom design tokens registered on a StyleThemeData or StyleVariants (see Custom themes and Custom variants) aren’t visible to the analyzer — it never runs your app, so it can’t see what you registered at runtime. Declare them in a top-level style_lint: section of the analysis_options.yaml next to your pubspec.yaml and unknown_class/unknown_variant will recognize them.
| Key | Matches | Example |
|---|---|---|
palettes | Custom color family names | brand for bg-brand-500 |
spacing | Custom spacing scale keys | 18 for p-18 |
typography | Custom font-size scale keys | huge for text-huge |
letter_spacing | Custom tracking scale keys | |
leading | Custom line-height scale keys | |
radius | Custom border-radius scale keys | |
shadows | Custom box-shadow preset keys | |
max_widths | Custom named max-width keys | |
breakpoints | Custom responsive breakpoint names | tablet for tablet:flex |
custom_properties | Theme-defined CSS custom properties, with the leading -- | --brand-color for bg-(--brand-color) |
variants | Custom StyleVariants predicate names | hoverable for hoverable:opacity-100 |
A realistic example — a custom brand palette, a tablet breakpoint, a --brand-color custom property, and a compact variant:
style_lint: palettes: - brand
breakpoints: - tablet
custom_properties: - --brand-color
variants: - compactSort classes
Section titled “Sort classes”style_lint ships a Tailwind-canonical class sorter, wired up three ways:
-
unsorted_classesdiagnostic — reports out-of-order classes, with a quick-fix (“Style: Sort classes”) in IDEs that support plugin fixes. -
“Style: Sort classes” assist — available on demand with the cursor inside any
Style(...)classes string, regardless of whetherunsorted_classesis enabled. -
CLI — for bulk sorting and CI:
Terminal window dart run style_lint:sort_classes # rewrites files under lib/ in placedart run style_lint:sort_classes --check lib test # reports only, exit 1 if unsorted (CI)Add the
--checkinvocation to CI to fail a pull request that introduces unsorted classes:.github/workflows/ci.yml - name: Check class orderrun: dart run style_lint:sort_classes --check lib
Sorting reproduces Tailwind v4’s own class order exactly — the same order the prettier-plugin-tailwindcss produces. Because it matches Tailwind, it does reorder conflicting utilities: Style('px-2 p-4') becomes Style('p-4 px-2'), and since StyleSpec merges classes last-wins, that can change what renders. Sorting is a deliberate, user-invoked action — conflicting_classes (see above) flags such strings so you can resolve them first.
Caveats
Section titled “Caveats”- IDE only —
dart analyze/flutter analyzedon’t run plugins. Plugin diagnostics come from the analysis server your IDE talks to; the CLI commands skip plugin isolates entirely, so they won’t reportstyle_lintwarnings. For CI, use thesort_classes --checkCLI below — there’s no CLI equivalent for the other rules yet. - Fixes are not
dart fix --apply-appliable. Plugin-provided fixes surface as IDE quick-fixes (and the “Style: Sort classes” assist), but the current SDK plugin protocol doesn’t route them through thedart fixbulk-apply command. For bulk sorting, use thesort_classesCLI instead — there’s no bulk CLI forduplicate_class/conflicting_classesyet, so those are IDE-only or hand-fixed. - No format-on-save equivalent. Sorting only happens when you explicitly run the CLI or apply the assist/fix — saving a file does not auto-sort its classes.
- Restart after config changes. Changes to
plugins:,diagnostics:, or thestyle_lint:section require restarting the Dart Analysis Server (or the IDE) to take effect — the plugin isolate is not hot-reloaded. - Bracket contents aren’t validated.
unknown_class/unknown_variantcheck the utility root (bg-,p-,hover:, …), not what’s inside[...]— an arbitrary value with a bad bracket payload isn’t flagged.