ashlar_ui 0.1.0
ashlar_ui: ^0.1.0 copied to clipboard
Ashlar UI for Flutter: components built on the widgets layer alone, copied into your app by the ashlar command, yours to change.
ashlar_ui #
The ashlar command for Ashlar UI: components for Flutter built
on the widgets layer alone, copied into your app as source files you own and change.
The command works offline: each version carries its components, so a version of the command is a version of the components.
Install #
dart pub global activate ashlar_ui
Run it in the root of a Flutter project.
Commands #
| Command | What it does |
|---|---|
ashlar init |
Copies the theme and AshlarApp, and writes app_theme.dart with your choices: --base (neutral, stone, zinc, mauve, olive, mist, taupe), --accent (one of seventeen colours), --style (standard, compact, rounded, sharp, dense, soft), --path (where components go, lib/ui unless you say). --prefix SL puts your own prefix on every name: SLButton, SLTheme. |
ashlar apply |
Chooses the theme again with the same options; --accent none takes the accent away. Your components are left as they are. --prefix renames everything to a new prefix, your own code included; --prefix none takes it away. |
ashlar add <item>... |
Copies components, and what they need. --all adds every component, --blocks every block (whole screens, written to lib/blocks). A file you already have is kept unless you pass --overwrite. Each comes with its tests, in test/ashlar; --no-tests leaves them out. |
ashlar view <item>... |
Shows what an item is, what it needs and its files, before you add it. |
ashlar search [words] |
Finds items by name or description. ashlar list lists them all, ticking the ones you have. |
ashlar diff [item...] |
For each item you have: whether your copy is as it was written, changed by you, or older than this version. Name items to see the lines. |
ashlar update [item...] |
Takes what is newer in this version and keeps your changes: a file you have not changed is replaced; one you have is merged, line by line, from the copy ashlar wrote. Where you and this version changed the same lines, both are kept between <<<<<<< yours and >>>>>>> ashlar for you to choose. --dry-run says what it would do. --preview draws each component the update would change, in your theme, as it is and as it would be, side by side on a page in .ashlar/review, changing nothing. |
ashlar check |
Reports what can be proved about your app's accessibility: your theme's contrast, measured, and in your source icon-only buttons without a label, left and right where start and end belong, and touch targets turned off. Exits 1 when it finds something, for CI. --no-contrast skips the measuring. |
ashlar book |
Writes a catalog of the components you have, in your theme, to .ashlar/book: an app of its own with a page for each, and switches for dark, text at 200% and right to left. flutter run -t .ashlar/book/main.dart runs it on any device. |
ashlar mcp |
These commands for a coding agent, over the Model Context Protocol on standard input and output. |
ashlar build [registry.json] |
Makes the files of a registry of your own, in public/r unless --out says otherwise. |
Your own prefix #
A design system marks its parts as its own: SLButton at one company, MzButton at
another. ashlar init --prefix SL does that for every name the components declare:
Button becomes SLButton, AshlarTheme becomes SLTheme, showToast becomes
showSLToast. Names then never clash with Material's Card, Checkbox or Dialog in
a file that imports both. Text and prose are left alone; code, [names] in doc comments
and their examples are renamed. The blocks, whole screens of your app, keep their own names.
ashlar apply --prefix Mz renames everything to a new prefix: the components, ashlar's
copies of them, and every use in your own code under lib and test, so the app still
builds. ashlar update and ashlar diff work as before.
Tests that come with each component #
A component comes with the tests that check what it promises: that a screen reader can
reach Swipe Actions without swiping, that a dialog rises above the keyboard, that a select
list is as wide as its trigger. They are written to test/ashlar, importing the
component from your package, and flutter test runs them with your own.
The files are yours, so change a component and its tests tell you which promise your
change broke; change the tests when you meant to break it. Tests that use another
component (most use a Button as a trigger) are written once you have that one too.
A test you delete stays deleted. ashlar update merges tests as it merges components.
tests: false in ashlar.yaml leaves them out, and tests: test/ui puts them
elsewhere. Components outside lib get no tests, since a test cannot import them.
Checking your app #
ashlar check reports only what it can prove, each with where it is and what to do:
- Contrast, measured: it builds
appThemeandappDarkThemefrom yourapp_theme.dartin a flutter test and measures each pair the components draw, text against WCAG 1.4.3 (4.5:1), the focus ring and field borders against 1.4.11 (3:1). - Labels: a Button or Toggle holding only an icon, with no
semanticLabel. - Right to left:
EdgeInsets.only(left:),Alignment.centerRight,TextAlign.leftand the like, where the directional form follows the reading direction. Values that follow a physical side or the text direction are not reported. - Touch targets:
tapTarget: 0, which turns off the room for a finger.
It says what it did not check, as source cannot show it: the size of your own tap areas
and labels on your own controls. A widget test with meetsGuideline checks a screen for
those. A line that means it can say // ashlar: ignore; a file, // ashlar: ignore-file
with its reason.
The field borders Ashlar and shadcn draw are 1.2 to 1.5:1, a choice of look that
ashlar check reports.
For coding agents #
ashlar mcp serves the commands to a coding agent over MCP: search_components,
view_component (the source, whose doc comments are the API), add_components,
update_components and check_app. Each runs the command a person would, in the folder
the agent starts it in. In Claude Code:
claude mcp add ashlar -- ashlar mcp
Other agents take the same command in their MCP settings:
{ "mcpServers": { "ashlar": { "command": "ashlar", "args": ["mcp"] } } }
Your choices, and what ashlar wrote #
ashlar.yaml holds your choices: where components go, the theme, your registries, whether
tests come along. ashlar init and ashlar apply change it and keep your comments; you can
edit it too.
path: lib/ui
base: stone
accent: teal
style: standard
Keeping your changes through updates #
Each file ashlar writes is also copied to .ashlar/base/, with its fingerprint in
.ashlar/files.json, so ashlar update knows what you started from. Commit that folder
with your code, so your whole team can update. Both are compared as your own formatter lays
out code, so a different line length is never taken for a change of yours.
Registries of your own #
Describe your items in a registry.json, the way Ashlar's own is written:
{
"brand-card": {
"description": "A card with our mark.",
"files": ["brand_card.dart"],
"requires": ["card", "brand"]
}
}
ashlar build writes registry.json (the list) and brand-card.json (the item and its
files) to host anywhere, or to keep in a folder. Name the registry in a project's
ashlar.yaml:
registries:
"@acme": https://acme.dev/r
Then ashlar add @acme/brand-card, ashlar view @acme/brand-card and
ashlar search @acme work as they do for Ashlar's items. An item can need Ashlar's
components by name and its own registry's items by name. A file that would land outside
the components or blocks folder is refused.
Licence #
MIT.