figura-cli 0.16.1 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +160 -122
- package/dist/mcp.js +34 -34
- package/package.json +1 -1
- package/skills/fig/SKILL.md +24 -13
- package/skills/figura-brand/SKILL.md +140 -52
- package/skills/figura-brand/reference/brand-format.md +72 -78
package/package.json
CHANGED
package/skills/fig/SKILL.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: fig
|
|
3
3
|
description: "Mock up a screen as an on-brand, shareable HTML fig via your Figura team — render a UI state in seconds before writing the real code. Use when asked to visualize a screen, mock a UI, or 'make a fig'. iPhone-framed by default; browser frame for web/desktop."
|
|
4
4
|
argument-hint: '<what to visualize>'
|
|
5
|
-
allowed-tools: 'mcp__figura__figura_whoami, mcp__figura__figura_generate_fig, mcp__figura__figura_create_fig, mcp__figura__figura_revise_fig, mcp__figura__figura_publish_fig, mcp__figura__figura_commit_fig, Bash(figura *), Bash(bunx figura-cli *)'
|
|
5
|
+
allowed-tools: 'mcp__figura__figura_whoami, mcp__figura__figura_fig_scaffold, mcp__figura__figura_generate_fig, mcp__figura__figura_create_fig, mcp__figura__figura_revise_fig, mcp__figura__figura_publish_fig, mcp__figura__figura_commit_fig, Bash(figura *), Bash(bunx figura-cli *)'
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# /fig — figure it out before you build it
|
|
@@ -102,16 +102,26 @@ so you hand it the studied structure in the brief (see step 3, "Server render").
|
|
|
102
102
|
re-verify. If the response has no `binding` field (older CLI/MCP), confirm
|
|
103
103
|
the team slug matches this project with the user before proceeding.
|
|
104
104
|
|
|
105
|
-
2. **Default — build it yourself, with YOUR Claude.** Fetch the
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
105
|
+
2. **Default — build it yourself, with YOUR Claude.** Fetch the **canonical
|
|
106
|
+
authoring scaffold** — **`figura_fig_scaffold`** (CLI:
|
|
107
|
+
`bunx figura-cli fig scaffold --platform <mobile|web> [--device <id>] [--area <str>]`)
|
|
108
|
+
— and hand-build the fig's HTML against it, then land it with
|
|
109
|
+
**`figura_create_fig`**, setting `area`, `orientation` (`portrait` /
|
|
110
|
+
`landscape` / `both`), `mode` (`single` / `compare` / `variations` — default
|
|
111
|
+
to `compare` when Existing-first turned up a baseline), `title`, and a real
|
|
112
|
+
description. This uses the caller's own Claude (the model running this skill)
|
|
113
|
+
— no extra cost, full control for precise broken-vs-proposed diptychs.
|
|
114
|
+
|
|
115
|
+
The scaffold IS the source of truth: it's the **exact system prompt Figura
|
|
116
|
+
injects when it renders a fig server-side** — the design rules, exemplars,
|
|
117
|
+
accent hints, your team's composed brand, and the component/device kit
|
|
118
|
+
context, all in one. Follow it verbatim; never invent brand values or restate
|
|
119
|
+
design rules from memory. Match `--platform` to the target (a web fig's
|
|
120
|
+
scaffold composes the team's *web* brand surface, a mobile fig's the *mobile*
|
|
121
|
+
one). **Fallback if the scaffold fetch fails** (older engine/CLI, or the
|
|
122
|
+
`figura_fig_scaffold` tool is unavailable): fetch just the brand with
|
|
123
|
+
`bunx figura-cli brand --json` and author against that — the scaffold is the
|
|
124
|
+
preferred path, the raw brand is the degraded one.
|
|
115
125
|
|
|
116
126
|
**Landing IS hosting — leave `publish` and `commit` unset.** Creating a fig
|
|
117
127
|
always persists it to Figura (there is no separate "save it for real" step);
|
|
@@ -332,8 +342,9 @@ So when you **hand-build** a fig (the default path), do your part of the contrac
|
|
|
332
342
|
- **MCP unavailable** (headless / cron, or no interactive auth): fall back to the
|
|
333
343
|
CLI — `bunx figura-cli fig create` / `fig publish`, with `fig revise` /
|
|
334
344
|
`fig revisions` for versions, `fig commit` / `fig uncommit` (or `--commit` on
|
|
335
|
-
the final create/revise) for the team library,
|
|
336
|
-
`bunx figura-cli brand --json` for the
|
|
345
|
+
the final create/revise) for the team library, `bunx figura-cli fig scaffold`
|
|
346
|
+
for the authoring scaffold, and `bunx figura-cli brand --json` for the raw
|
|
347
|
+
brand (the scaffold's fallback).
|
|
337
348
|
- **`figura_create_fig`/`figura_revise_fig` itself fails** (no network, API down,
|
|
338
349
|
auth expired): the fig never entered Figura, so there is no `id`/`viewUrl` to
|
|
339
350
|
open. Only THEN write the HTML to a local scratch file (project-scoped —
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: figura-brand
|
|
3
|
-
description: "Introduce a repo to Figura — scan the codebase for its design system (design tokens, brand docs, app name) and set the team's Figura brand profile so generated figs are on-brand. Use when asked to import or sync a brand, set up Figura for a project,
|
|
3
|
+
description: "Introduce a repo to Figura — scan the codebase for its design system (design tokens, brand docs, app name) and set the team's Figura brand profile (STRUCTURED) so generated figs are on-brand. Use when asked to import or sync a brand, set up Figura for a project, 'introduce my repo to Figura', or upgrade an existing prose brand to structured."
|
|
4
4
|
argument-hint: '[path to the repo or design-system directory]'
|
|
5
|
-
allowed-tools: 'Read, Grep, Glob, Bash(figura *), Bash(bunx figura-cli *)'
|
|
5
|
+
allowed-tools: 'Read, Grep, Glob, mcp__figura__figura_whoami, mcp__figura__figura_brand_schema, mcp__figura__figura_set_brand, Bash(figura *), Bash(bunx figura-cli *)'
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# Figura Brand — introduce a repo to Figura
|
|
@@ -12,6 +12,18 @@ profile** from it, so every fig the `/fig` skill / generator produces is on-bran
|
|
|
12
12
|
You (Claude) do the synthesis — read the code and docs, then push the result. No
|
|
13
13
|
figura.so UI.
|
|
14
14
|
|
|
15
|
+
Author a **STRUCTURED brand** — a `{ identity, platforms }` object (colors, type,
|
|
16
|
+
voice, per-platform surfaces) — not freeform prose. Structured is addressable,
|
|
17
|
+
platform-partitioned, and versioned; prose is the fallback for when structured
|
|
18
|
+
synthesis genuinely isn't possible.
|
|
19
|
+
|
|
20
|
+
**The shape authority is the SERVER schema, not this doc.** Fetch it and author
|
|
21
|
+
against it so the brand validates first try:
|
|
22
|
+
`figura brand schema` (CLI) or the `figura_brand_schema` MCP tool → the JSON Schema
|
|
23
|
+
`PUT /brand` validates. `reference/brand-format.md` is a short orientation +
|
|
24
|
+
worked example only; the fetched schema is the source of truth for field
|
|
25
|
+
names/shape/caps.
|
|
26
|
+
|
|
15
27
|
`$ARGUMENTS` is an optional path to focus on (a repo root or a design-system
|
|
16
28
|
directory). Default to the current repo.
|
|
17
29
|
|
|
@@ -33,6 +45,9 @@ here first. The brand is **team-wide and overwrites** the current one — never
|
|
|
33
45
|
push without showing the user what you're about to set and getting an explicit
|
|
34
46
|
OK.
|
|
35
47
|
|
|
48
|
+
> **Upgrading an existing prose brand instead of a fresh import?** Jump to
|
|
49
|
+
> [Upgrade a legacy prose brand to structured](#upgrade-a-legacy-prose-brand-to-structured).
|
|
50
|
+
|
|
36
51
|
## Workflow
|
|
37
52
|
|
|
38
53
|
### 1. Scan the repo for design signals
|
|
@@ -47,72 +62,145 @@ Hunt across whatever stack this repo uses — do not assume one framework:
|
|
|
47
62
|
- **Brand voice + positioning** — `BRAND.md`, `STRATEGY.md`, design-system READMEs, marketing copy, the landing page hero. Capture the tone and any explicit voice lines / taglines.
|
|
48
63
|
- **Identity** — app/brand name + one-line description: `package.json` (name/description), `Info.plist` (`CFBundleName`), app-store copy, the README's first paragraph.
|
|
49
64
|
- **Role/usage notes** — inline comments next to tokens often state the role
|
|
50
|
-
(e.g. `// primary brand accent; record button`). Carry those into
|
|
65
|
+
(e.g. `// primary brand accent; record button`). Carry those into `color.role`/`meaning`.
|
|
66
|
+
- **Signature components** — the product's distinctive UI pieces (a scrubber, a
|
|
67
|
+
reaction button, a dense data table), from component files — become
|
|
68
|
+
per-platform `components[]`.
|
|
51
69
|
- **Product areas** — the app's own top-level sections, e.g. a tab bar, a
|
|
52
70
|
sidebar's nav items, or the router's top-level route folders (`app/(tabs)/*`,
|
|
53
|
-
`src/routes/*`, `src/screens/*`). These become `surfaces` —
|
|
54
|
-
real areas ("Feed", "Checkout"), never a generic placeholder
|
|
55
|
-
another product's
|
|
71
|
+
`src/routes/*`, `src/screens/*`). These become each platform's `surfaces[]` —
|
|
72
|
+
THIS product's real areas ("Feed", "Checkout"), never a generic placeholder
|
|
73
|
+
list and never another product's.
|
|
74
|
+
- **Which platform(s)** — detect the target platform(s) so you only emit the
|
|
75
|
+
surfaces that apply:
|
|
76
|
+
- `mobile` — an iOS/Android/React-Native/Expo app (`*.xcodeproj`, `Info.plist`,
|
|
77
|
+
`android/`, `app.json`/Expo, SF Symbols usage, safe-area/tab-bar tokens).
|
|
78
|
+
- `web` — a web app / site (`index.html`, a web router, CSS media queries,
|
|
79
|
+
hover states, a `tailwind.config`).
|
|
80
|
+
- `desktop` — an Electron/Tauri/native-desktop shell.
|
|
81
|
+
- A product can target more than one — emit a `platforms` key for each.
|
|
56
82
|
|
|
57
83
|
Use `Glob`/`Grep` to locate, `Read` to extract real values. Use the REAL hexes,
|
|
58
84
|
fonts, and sizes from the repo — never invent them.
|
|
59
85
|
|
|
60
|
-
### 2. Synthesize the brand
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
- **
|
|
75
|
-
`[
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
86
|
+
### 2. Synthesize the STRUCTURED brand
|
|
87
|
+
|
|
88
|
+
Fetch the schema (`figura brand schema` / `figura_brand_schema`) and produce a
|
|
89
|
+
`{ identity, platforms }` object plus a `name`, authoring against that schema
|
|
90
|
+
EXACTLY (strict keys, hex 3/4/6/8 digits, no blank array entries, only
|
|
91
|
+
`mobile`/`web`/`desktop` platform keys) so it validates first try.
|
|
92
|
+
`reference/brand-format.md` is a quick orientation + one worked example.
|
|
93
|
+
|
|
94
|
+
- **name** — the product/brand name (still required on the write).
|
|
95
|
+
- **identity** — the SHARED brand: `colors` (real palette, each
|
|
96
|
+
`{ name, hex, role?, meaning? }`), `gradient` (if any), `type`
|
|
97
|
+
(`{ families, ramp, rules }`), `spacing`, `radius`, `voice`
|
|
98
|
+
(`{ nouns, promise, lines, tone }`), `forbidden` (hard guardrails as
|
|
99
|
+
`[{ id?, rule }]`).
|
|
100
|
+
- **platforms** — a map with a key ONLY for each platform you detected in step 1.
|
|
101
|
+
Each surface: `components` (signature components as `[{ name, spec }]`), `icons`
|
|
102
|
+
(the platform's icon language), `layout` (its frame/shell law), `surfaces` (this
|
|
103
|
+
product's real areas), `bans` (platform-specific don'ts). Use the per-platform
|
|
104
|
+
**`tokensBlock`** escape-hatch ONLY for guidance that genuinely doesn't fit a
|
|
105
|
+
structured field — prefer a structured field.
|
|
80
106
|
|
|
81
107
|
This synthesis IS the value — enrich the raw tokens with the roles, voice, and
|
|
82
108
|
conventions you read from the repo. Keep it grounded in what's actually there.
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
109
|
+
`appDescription` (one clause: what it is + who it's for) is optional and sent
|
|
110
|
+
alongside.
|
|
111
|
+
|
|
112
|
+
**Also write a `proseSource`** — a short human-readable description (a few brief
|
|
113
|
+
paragraphs) of this brand: what a person would type to describe its colors, type,
|
|
114
|
+
voice, and conventions. This is NOT a generation input; it's the editable source
|
|
115
|
+
the web brand editor's "Author from a description" textarea seeds from. Send it on
|
|
116
|
+
the push (next step) so a scan-authored brand still has a populated editable
|
|
117
|
+
source, not a blank textarea. Ground it in the same signals you read from the
|
|
118
|
+
repo — it should read as the prose a founder would write, not a dump of the JSON.
|
|
119
|
+
|
|
120
|
+
### 3. Confirm, then push (structured)
|
|
121
|
+
|
|
122
|
+
Show the user the `name`, `appDescription`, and the synthesized `{ identity,
|
|
123
|
+
platforms }` (summarize the palette/type/voice + which platforms + their
|
|
124
|
+
surfaces), and confirm the target team (`figura whoami`). On approval:
|
|
125
|
+
|
|
126
|
+
Always send `proseSource` (the prose description from step 2) alongside the
|
|
127
|
+
structured write so the web editor's textarea is seeded:
|
|
128
|
+
|
|
129
|
+
- **MCP (preferred):** call `figura_set_brand` with `{ name, appDescription,
|
|
130
|
+
identity, platforms, proseSource }` (the `identity`/`platforms` objects directly).
|
|
131
|
+
- **CLI fallback:** write the `{ identity, platforms }` JSON to a file and the prose
|
|
132
|
+
description to a second file, then pass `--structured` + `--prose-source`.
|
|
133
|
+
`--name` is still required:
|
|
91
134
|
```bash
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
TOKENS
|
|
135
|
+
figura brand set --name "<name>" --description "<one clause>" \
|
|
136
|
+
--structured @brand.json --prose-source @brand-notes.txt
|
|
95
137
|
```
|
|
96
|
-
|
|
138
|
+
`brand.json` is the `{ "identity": {…}, "platforms": {…} }` object;
|
|
139
|
+
`brand-notes.txt` is the prose description. Pass both as **@files** — a single
|
|
140
|
+
command can't read stdin twice, so `--structured -` and `--prose-source` can't
|
|
141
|
+
share stdin. (Use `bunx figura-cli` in place of `figura` if it isn't on PATH.)
|
|
97
142
|
|
|
98
|
-
A user can always add/rename/remove
|
|
99
|
-
|
|
143
|
+
A user can always add/rename/remove areas or edit tokens later — this is a
|
|
144
|
+
starting point, not a one-shot lock-in.
|
|
100
145
|
|
|
101
146
|
### 4. Verify
|
|
102
147
|
|
|
103
|
-
`figura brand --json` (or
|
|
104
|
-
`isCustom: true
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
on
|
|
111
|
-
|
|
112
|
-
|
|
148
|
+
`figura brand --json` (or `figura brand --json --platform <mobile|web|desktop>`)
|
|
149
|
+
→ confirm `isCustom: true`, that the response carries the structured
|
|
150
|
+
`{ identity, platforms }` you set, and that the composed `tokensBlock`/`surfaces`
|
|
151
|
+
reflect them. (For a legacy prose fallback you'll see the prose `tokensBlock`
|
|
152
|
+
instead.) The response also carries `platformProfile` — mobile/web/both, which the
|
|
153
|
+
team can override later on the brand page (Studio defaults its device frame off
|
|
154
|
+
it). Then tell the user the brand is set and suggest `/fig <a screen>` to see it
|
|
155
|
+
render on-brand.
|
|
156
|
+
|
|
157
|
+
## Upgrade a legacy prose brand to structured
|
|
158
|
+
|
|
159
|
+
If the team already has a legacy PROSE brand (from an earlier onboarding), convert
|
|
160
|
+
it to structured through the **server transform** (the single transform mechanism —
|
|
161
|
+
same one the web brand editor uses; do NOT hand-parse the prose yourself). It's
|
|
162
|
+
reversible.
|
|
163
|
+
|
|
164
|
+
1. **Fetch the current brand** — `figura brand --json`. For a legacy brand this
|
|
165
|
+
returns the prose `tokensBlock` (+ `surfaces`, `forbidden`).
|
|
166
|
+
2. **Transform it on the server** — pipe that prose to `figura brand structure`:
|
|
167
|
+
```bash
|
|
168
|
+
figura brand --json | jq -r '.tokensBlock' | figura brand structure -
|
|
169
|
+
```
|
|
170
|
+
(or `figura brand structure --prose @notes.txt`). The server GLM returns
|
|
171
|
+
schema-valid `{ identity, platforms }` on stdout (it does NOT save). On a
|
|
172
|
+
transform failure it errors with a clear message — edit the prose and retry.
|
|
173
|
+
3. **Confirm the mapping with the user** — show the before (prose) → after
|
|
174
|
+
(structured `{ identity, platforms }`) and get an explicit OK. Flag anything the
|
|
175
|
+
transform dropped or that the prose didn't clearly specify.
|
|
176
|
+
4. **Push structured** — save the reviewed JSON, AND pass the original prose that
|
|
177
|
+
was transformed as `--prose-source` (that prose IS the editable source, so the
|
|
178
|
+
web editor seeds from it):
|
|
179
|
+
`figura brand set --name <name> --structured @file.json --prose-source @notes.txt`.
|
|
180
|
+
(`notes.txt` = the original prose from step 2; if you piped `.tokensBlock`, write
|
|
181
|
+
it to a file first so you can pass it here.) Over MCP, send the same original
|
|
182
|
+
prose as `proseSource` on `figura_set_brand`.
|
|
183
|
+
|
|
184
|
+
`brand set --structured` **snapshots a version first** (#707), so the upgrade is
|
|
185
|
+
reversible: every brand write appends to the team's version history, and the team
|
|
186
|
+
can restore the pre-upgrade prose version from the brand page's version history on
|
|
187
|
+
figura.so if the mapping was wrong. Reassure the user of this before pushing.
|
|
188
|
+
|
|
189
|
+
## Prose fallback (legacy — only if structured synthesis isn't possible)
|
|
190
|
+
|
|
191
|
+
If the repo's design system genuinely can't be structured (e.g. it's a single
|
|
192
|
+
freeform brand doc with no extractable tokens), fall back to a legacy PROSE brand
|
|
193
|
+
so onboarding never hard-fails: send `tokensBlock` (freeform plain text) +
|
|
194
|
+
`surfaces` (the product's areas) instead of `identity`/`platforms`.
|
|
195
|
+
|
|
196
|
+
- **MCP:** `figura_set_brand` with `{ name, appDescription, tokensBlock, surfaces }`.
|
|
197
|
+
- **CLI:** `cat tokens.txt | figura brand set --name "<name>" --tokens - --surfaces "Feed,Settings"`.
|
|
198
|
+
|
|
199
|
+
A prose brand still renders; it's just not addressable. Prefer structured whenever
|
|
200
|
+
you can extract real tokens.
|
|
113
201
|
|
|
114
202
|
## Output
|
|
115
203
|
|
|
116
204
|
Report the brand you set (name + a one-line summary of the palette/type/voice you
|
|
117
|
-
captured,
|
|
118
|
-
paste the entire
|
|
205
|
+
captured, the platforms + their surfaces) and confirm it's live for the team.
|
|
206
|
+
Don't paste the entire object again unless asked.
|
|
@@ -1,80 +1,74 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
3
|
-
The brand profile
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
1
|
+
# Structured brand format — orientation
|
|
2
|
+
|
|
3
|
+
The brand profile is a **structured JSON object** — `{ identity, platforms }`. The
|
|
4
|
+
**authoritative shape is the live JSON Schema**, not this file: fetch it with
|
|
5
|
+
`figura brand schema` (CLI) or the `figura_brand_schema` MCP tool and author
|
|
6
|
+
against THAT (it's derived server-side from the zod schema `PUT /brand` validates,
|
|
7
|
+
so it can't drift). This page is only orientation + one worked example.
|
|
8
|
+
|
|
9
|
+
Two halves:
|
|
10
|
+
- **`identity`** — the SHARED brand (same on every platform): `colors`
|
|
11
|
+
(`[{ name, hex, role?, meaning? }]`), `gradient?`, `type?`
|
|
12
|
+
(`{ families?: { slot: fontName }, ramp?: number[], rules? }`), `spacing?`,
|
|
13
|
+
`radius?`, `voice?` (`{ nouns?, promise?, lines?, tone? }`), `forbidden?`
|
|
14
|
+
(`[{ id?, rule }]`).
|
|
15
|
+
- **`platforms`** — a map keyed by `mobile` / `web` / `desktop`. Include ONLY the
|
|
16
|
+
platforms the product targets. Each surface: `components?` (`[{ name, spec }]`),
|
|
17
|
+
`icons?`, `layout?`, `surfaces?` (this product's real areas), `bans?`,
|
|
18
|
+
`tokensBlock?` (the prose ESCAPE HATCH for guidance that genuinely doesn't fit a
|
|
19
|
+
structured field — use sparingly).
|
|
20
|
+
|
|
21
|
+
Validation the schema enforces (a violation is a 400 at `PUT /brand`): **strict
|
|
22
|
+
keys** (an unknown/misspelled key is rejected); **hex** = `#` + exactly 3/4/6/8
|
|
23
|
+
digits; **no blank** array entries; platform keys exactly `mobile`/`web`/`desktop`;
|
|
24
|
+
caps on array lengths + string lengths (the fetched schema carries the exact
|
|
25
|
+
numbers). Author against the fetched schema, not these prose notes.
|
|
26
|
+
|
|
27
|
+
## Worked example — a web/desktop brand ("Ledgerline")
|
|
28
|
+
|
|
29
|
+
```json
|
|
30
|
+
{
|
|
31
|
+
"identity": {
|
|
32
|
+
"colors": [
|
|
33
|
+
{ "name": "Fern", "hex": "#1F6F54", "role": "accent", "meaning": "primary actions; gradient start" },
|
|
34
|
+
{ "name": "Brass", "hex": "#C9A227", "role": "accent", "meaning": "highlights + paid states; gradient end" },
|
|
35
|
+
{ "name": "Bone", "hex": "#F4F2EC", "role": "surface", "meaning": "light surfaces + text on dark" },
|
|
36
|
+
{ "name": "Ink", "hex": "#101312", "role": "ground", "meaning": "near-black base background" }
|
|
37
|
+
],
|
|
38
|
+
"gradient": "linear-gradient(90deg, #1F6F54, #C9A227)",
|
|
39
|
+
"type": {
|
|
40
|
+
"families": { "display": "Fraunces", "body": "Inter", "mono": "JetBrains Mono" },
|
|
41
|
+
"ramp": [12, 14, 16, 18, 20, 24, 30, 36, 48],
|
|
42
|
+
"rules": "Fraunces semibold at display size; Inter 400/600 for UI; mono for amounts + ids."
|
|
43
|
+
},
|
|
44
|
+
"spacing": [4, 8, 12, 16, 20, 24, 32, 40, 48, 64],
|
|
45
|
+
"radius": [4, 8, 12, 999],
|
|
46
|
+
"voice": {
|
|
47
|
+
"nouns": ["ledger", "balance", "month-end"],
|
|
48
|
+
"promise": "Books, balanced.",
|
|
49
|
+
"lines": ["Every cent, accounted.", "Close the month, not your eyes."],
|
|
50
|
+
"tone": "Terse, dry, precise — never salesy."
|
|
51
|
+
},
|
|
52
|
+
"forbidden": [
|
|
53
|
+
{ "id": "no-emoji", "rule": "Never use emoji; icons are inline stroke SVG." },
|
|
54
|
+
{ "id": "squared", "rule": "Chrome is squared (radius 8), never pill." }
|
|
55
|
+
]
|
|
56
|
+
},
|
|
57
|
+
"platforms": {
|
|
58
|
+
"web": {
|
|
59
|
+
"components": [
|
|
60
|
+
{ "name": "dataTable", "spec": "Dense zebra-striped table; right-aligned numeric columns in mono; sticky header." },
|
|
61
|
+
{ "name": "primaryButton", "spec": "Solid Fern fill, Bone text, radius 8, 40px tall." }
|
|
62
|
+
],
|
|
63
|
+
"icons": "Inline stroke SVG (receipt, arrow-right, check-circle). currentColor, ~1.6 width.",
|
|
64
|
+
"layout": "Desktop-first two-column app shell: left nav rail + dense content. Tables are the hero.",
|
|
65
|
+
"surfaces": ["Invoices", "Clients", "Reports", "Settings"],
|
|
66
|
+
"bans": ["No pill buttons", "No hero illustrations — numbers first"]
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
43
70
|
```
|
|
44
71
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
LEDGERLINE BRAND GROUND-TRUTH (locked):
|
|
49
|
-
|
|
50
|
-
WHAT LEDGERLINE IS
|
|
51
|
-
- A calm B2B invoicing web app. Dense tables, quiet confidence, desktop-first.
|
|
52
|
-
- Numbers first. Tagline: "Books, balanced."
|
|
53
|
-
|
|
54
|
-
COLORS (use ONLY these)
|
|
55
|
-
- #1F6F54 Fern — primary brand accent; primary actions; gradient start.
|
|
56
|
-
- #C9A227 Brass — secondary accent; highlights + paid states; gradient end.
|
|
57
|
-
- #F4F2EC Bone — light surfaces + foreground on dark (a warm off-white).
|
|
58
|
-
- #101312 Ink — near-black base background.
|
|
59
|
-
|
|
60
|
-
THE BRAND GRADIENT
|
|
61
|
-
- EXACTLY one: linear-gradient(90deg, #1F6F54, #C9A227). Wordmark/hero accents only.
|
|
62
|
-
|
|
63
|
-
TYPE
|
|
64
|
-
- Display: Fraunces — stack 'Fraunces', Georgia, serif. Semibold at display size.
|
|
65
|
-
- Body: Inter — stack Inter, -apple-system, 'SF Pro Text', system-ui, sans-serif.
|
|
66
|
-
- Mono: JetBrains Mono — amounts, invoice ids, dates.
|
|
67
|
-
- Scale: 12 / 14 / 16 / 18 / 20 / 24 / 30 / 36 / 48.
|
|
68
|
-
|
|
69
|
-
SPACING / RADIUS
|
|
70
|
-
- 4-based scale: 4 8 12 16 20 24 32 40 48 64. Radius: sm 4, md 8, lg 12, full 999.
|
|
71
|
-
|
|
72
|
-
ICON LANGUAGE
|
|
73
|
-
- Inline stroke SVG (receipt, arrow-right, check-circle). NEVER emoji.
|
|
74
|
-
|
|
75
|
-
VOICE
|
|
76
|
-
- "Every cent, accounted." "Close the month, not your eyes." Terse, dry, precise — never salesy.
|
|
77
|
-
|
|
78
|
-
CONVENTIONS
|
|
79
|
-
- Tables are the hero. Chrome is squared (radius md, never pill). Light-first with a dark ledger mode.
|
|
80
|
-
```
|
|
72
|
+
A brand can hold more than one platform at once (a product shipping iOS + web sets
|
|
73
|
+
both `platforms.mobile` and `platforms.web`, sharing one `identity`). Emit a
|
|
74
|
+
platform key ONLY for a platform the product actually targets.
|