Every codebase has house rules. Use the design tokens, not Colors.grey. Don't let domain/ import presentation/. Never call ref.watch from a button handler.
They live in a style guide or a wiki page, and nothing checks them. So they drift. The worst kind is a design token typo: 'Text/bdy' instead of 'Text/body'. Token lookups usually fall back to a default color, so nothing fails. The diff looks fine in review, and a pink widget ships.
The Dart analyzer can't express rules like these. The usual answer is a custom analyzer plugin: a second package, Dart code per rule, and upkeep every time the analyzer changes. For a handful of project conventions, that felt like too much. So I wrote house_rules, where a rule is a few lines of YAML.
What it does
rules:
avoid_palette_colors:
severity: error
exclude: [lib/theme/**]
palette: Colors
allowed: [transparent, white]
That's the whole rule. It ships with ten of them, all configured from one house_rules.yaml:
-
known_design_tokens: every token you look up by string must exist -
layer_boundaries: which folders may import which -
no_watch_in_callback: noref.watchinside handlers and listeners -
no_widget_returning_functionandno_method_in_build -
avoid_palette_colors,avoid_raw_colors,avoid_inline_text_stylesandmagic_spacingfor design system discipline -
unjustified_ignore: a suppression comment has to say why
Try it
dart pub global activate house_rules
house_rules init # writes a starter house_rules.yaml
house_rules # see what it finds
house_rules --update-baseline # accept what is already there
After the baseline, only new problems fail. It needs Dart 3.11 or newer.
What a finding looks like
This is the output on the example project in the repo:
lib/features/cart/presentation/widgets/cart_row.dart
12:12 ERROR avoid_palette_colors
`Colors.grey` bypasses the design tokens
21:48 ERROR known_design_tokens
'Text/bdy' is not a defined design token
did you mean:
'Text/body'
'Text/headline'
Each finding says why the rule exists and, where it can, what you probably meant. known_design_tokens reads your token map, so it knows the real names and catches the typo before it ships.
Fast enough to run on every save
It only parses files and never resolves types. That is a real limit, but it keeps a scan cheap. On a 2,937-file Flutter app with all ten rules, the compiled binary checks the whole project in 0.285 seconds, and a single file in 0.08 seconds.
That makes it practical as an editor or agent hook:
house_rules --hook "$CHANGED_FILE"
It stays silent when the file is clean and exits with code 2 when it isn't. If the linter itself crashes, it exits 0. A broken linter should never be the reason someone can't commit.
Adopting it on an old codebase
Turning on a new rule in an existing project usually means hundreds of findings on day one. --update-baseline records them, so CI stays green and only new violations fail.
The baseline is keyed by a hash of the offending code, not by line numbers, so it survives dart format and unrelated edits. For rules where every finding is a real bug, set baseline: false and they can never be hidden.
There are also Markdown, HTML, JSON and GitLab Code Quality reports, and a --check-docs mode that fails when your written guidelines and your config disagree.
Try it on your project
Run house_rules init, keep the two or three rules you already agree on, and baseline the rest. You can tighten it later. The code and the docs for every rule are here:
NoaTubic
/
house_rules
Write your project's lint rules in YAML instead of building a custom_lint plugin. Design tokens, layering, widget and state-management rules.
house_rules
Write your project's lint rules in YAML.
Every codebase has house rules. Use the design tokens, not Colors.grey
Don't let domain/ import presentation/. Never call ref.watch from a
button handler. They end up in a style guide, a wiki page, a CONTRIBUTING.md
— and nothing checks any of them, so they drift.
The Dart analyzer can't express rules like these. The usual answer is to
write a custom_lint plugin, which
means a second package, Dart code per rule, and a plugin that has to be kept
in step with the analyzer. For a handful of project conventions, that is a lot
of ceremony.
house_rules gives you the same enforcement as four lines of YAML:
rules:
avoid_palette_colors:
severity: error
exclude: [lib/theme/**]
palette: Colors
allowed: [transparent, white]
lib/features/cart/widget.dart
88:14 ERROR avoid_palette_colors
`Colors.grey` bypasses the design tokens
A literal palette colour will not follow…https://pub.dev/packages/house_rules
Feedback and issues are welcome on GitHub.
Top comments (0)