Skip to content

echo.json

echo.json is the one file that says how a project uses Echo: where the components go, which registry they come from, the seed its theme is made from, the style it wears, and what the check and Echo's tests do. init writes it, and this is what it writes:

echo.json
{
  "$schema": "https://echo-ui.com/e/schema/config.json",
  "dir": "src/components/echo",
  "framework": "next",
  "packageManager": "pnpm",
  "registry": "https://echo-ui.com/e"
}

Everything else is added by a command (init --seed, style use, style set) or by you (check and test). Nothing past these is needed. The file points at its own schema, so an editor completes each field and says what it is, in the same words as this page.

It's plain JSON, so a trailing comma or a comment makes it unreadable. Every command, the hook and the Stop gate included, then stops and says which file and where it stopped reading, and nothing is written over it, init --yes included. echo-lock.json is held the same way.

Every field

FieldTakesWritten byWhat it is
$schemastringinitThis file's schema.
dir (required)stringinit (--dir)The one folder every Echo item is copied into, relative to the project root.
framework (required)"next" | "vite" | "unknown"init, as it detected itWhat the project is built with, as init detected it.
packageManager (required)"npm" | "pnpm" | "bun" | "yarn"init, as it detected itWhat installs the packages Echo's items need.
registry (required)a URLinit (--registry)Where items come from: Echo's /e/ registry, or a copy of it.
seedan objectinit --seed, style use, style setThe seed the theme and DESIGN.md are made from, as createEchoTheme takes it. init --seed writes it; Echo's defaults when absent.
seed.accentstringThe one saturated colour, as a hex.
seed.chassisstringThe plastic the device is molded from, as a hex.
seed.paperstringThe colour everything is written in, as a hex.
seed.lcdTintstringThe screens' tint, as a hex; worked out from the chassis when absent.
seed.finish0 to 1Matte (0) to glossy (1).
seed.radius0 to 1Crisp (0) to soft (1).
seed.density"compact" | "default" | "comfortable"How tightly parts are packed.
seed.pillsbooleanButtons and segments as pills (true) or following the radius (false), whatever the radius.
seed.render"molded" | "drawn"Molded and lit (plastic under the key light), or drawn in the ink (outlined, hollow, standing on a hard ink edge).
seed.fontMachinestringLabels, caps and captions: a font stack.
seed.fontSansstringBody text: a font stack.
seed.fontMonostringData and code: a font stack.
seed.fontLcdstringThe screens' face: a font stack.
stylean objectstyle use and init --style; style set adds to changedThe style this project wears, as npx echo-ui-cli style use applied it: what DESIGN.md tells the agent. Its colours, fonts and shape are the seed.
style.namestring
style.titlestring
style.essencestringOne line: what the style is.
style.from"echo" | "yours"Echo's library, or your own (your Documents' styles folder).
style.rulesa list of stringShort lines for the agent: how screens in this style are put together.
style.refusalsa list of { wants, instead }What the style wanted that Echo's grammar refuses, and the closest Echo gets.
style.fontsa list of { role, family, source, url }The fonts the style uses, and where to get them.
style.changeda list of "accent" | "chassis" | "paper" | "lcdTint" | "finish" | "radius" | "density" | "render" | "pills" | "fontMachine" | "fontSans" | "fontMono" | "fontLcd"The settings npx echo-ui-cli style set has moved since the style was worn: the project wears the style with these changed. style use clears it.
checkan objectYouThe team's own additions to npx echo-ui-cli check. Nothing here is needed; it only adds.
check.rulesa rule name → "off" | "info"Turn a rule off, or to info: said, never counted. axe is every axe rule test runs, and axe:<id> one of them (axe:target-size). Problems a project already has are kept rule by rule, "axe:target-size": "info", so new ones still count; "axe": "info" hides those too.
check.bana list of { import, message }Imports the team doesn't use, each with what to use instead.
check.coloursa list of stringBrand colours the seed doesn't produce (a logo's colour), allowed where the check would flag a colour written by hand.
testan objectYouWhat npx echo-ui-cli test and the Stop gate run Echo's tests on (the design tests, contrast, names and axe). Nothing here is needed: without it they find the app's routes and the port its dev script runs on.
test.urla URLWhere the app runs, http://localhost:3000.
test.routesa list of stringThe pages to test, as paths (/, /pricing): for a page with a [slug], name one.
test.widthsa list of 240 to 3840Widths to test each page at, in pixels. Default [1280].
test.stopbooleanfalse turns the Stop gate off: Claude Code no longer runs Echo's tests when the agent stops. test still runs.

seed is what Theming describes, and style is what style use writes. Move a setting with npx echo-ui-cli style set, which checks every value first and writes echo-theme.css and DESIGN.md again; alone, it lists where each setting is.

What you add

Your team's rules, in check. Turn a rule off, or to info (said, never counted); ban an import, in your own words; allow brand colours the seed doesn't make:

json
{
  "check": {
    "rules": { "space": "info", "axe:target-size": "info" },
    "ban": [{ "import": "lucide-react", "message": "our icons are in @/icons." }],
    "colours": ["#FF5A1F"]
  }
}

Where Echo's tests run, in test: the app's address, the pages, the widths, and whether Claude Code's Stop gate runs them:

json
{
  "test": { "url": "http://localhost:3000", "routes": ["/", "/pricing"], "widths": [390, 1280], "stop": true }
}

Without test, npx echo-ui-cli test finds the app on the port its dev script runs on and tests every route it has.

Rule names

These are the names check.rules takes. A name that isn't one of them stops the check and says so, rather than quietly turning nothing off.

RuleLooks atWhat it finds
colourYour code and a running pageA colour written by hand, or painted on the page, that the seed doesn't make. The theme's colours at any opacity, and check.colours, are allowed.
spaceYour codeSpacing off Echo's scale, or a step of it written as a number. Page layout past 32px is yours.
radiusYour codeA corner off Echo's radius scale.
shadowYour codeA shadow or focus ring drawn by hand instead of a --echo-shadow-* recipe.
timingYour codeA duration or easing invented instead of one of Echo's motion tokens.
motion-libraryYour codeA second animation library imported beside Echo.
restyleYour codeEcho restyled from outside: a class or style on a closed component, your CSS reaching into Echo's classes, a theme token set anywhere but the theme.
labelYour codeA field with no label.
error-colourYour codearia-invalid with no words tied to it: an error said only by colour.
motionYour codeGSAP used straight in a file that uses Echo but not its motion, or an animation with no reduced-motion fallback.
screenYour code and a running pageEcho's screen rules: another library's parts on an Echo screen, two primary caps in one decision, something destructive on one press, a confirmation that says "OK"; on the page, "you are here" lit twice.
weightYour codeA heavy part (WebGL, or a lot of code) loaded with the page instead of lazily.
lookalikeYour codeA raw element where Echo has the component: <button>, <select>, <input type="checkbox">, role="switch".
echo-classYour codeOne of Echo's classes borrowed onto a raw element.
propYour codeA prop an Echo component doesn't take, a value it doesn't have, or one it needs and didn't get, read from the version you installed.
nameYour code and a running pageA control with no accessible name, which a screen reader can only call "button" or "textbox".
keyboardYour codeA click on something a keyboard can't reach, like onClick on a div.
img-altYour codeAn <img> with no alt.
focus-ringYour codeThe focus ring taken away with nothing in its place.
tabindexYour codeA positive tabIndex, which takes a part out of the reading order.
hidden-focusableYour codearia-hidden on something the keyboard still lands on.
teamYour codeAn import the team banned in check.ban, with the team's own words for what to use.
setupA running pageThe theme, the page's ground or SoundProvider missing from the page.
contrastA running pageText under WCAG AA against what's actually behind it, gradients included; small text that passes AA but reads under APCA Lc 45.
alignmentA running pageText cut off with "…", cards side by side whose actions sit at different heights, a lone action off its column's edge, a grid that leaves columns empty.
depthA running pageA control that glows at rest, or a raised surface on a raised one with no well between.
cornersA running pageParts of one kind with different corners, or a corner inside a corner that doesn't follow it.
iconsA running pageOne group's icons at different sizes or stroke weights, or one filled among outlined ones.
slopYour codeWhat reads as made by AI: gradient text, an emoji as an icon, the sparkle for "AI", labels like "Get started", stand-in names and photos.
axeA running page, by axeaxe-core's WCAG 2.0, 2.1 and 2.2 A and AA rules. axe:<id> names one of them (axe:target-size).

axe:<id> takes one of the rules axe runs, by axe-core's own ID: axe:target-size, axe:image-alt. Keeping the problems a project already has, rule by rule, means new ones still count; "axe": "info" would hide those too. Checks and tests has which rule runs where.