synthesisui 0.1.5 → 0.1.6
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/guide.js +85 -27
- package/package.json +1 -1
package/dist/guide.js
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
const kebab = (v) => v.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
|
|
2
2
|
const list = (items) => items.length ? items.map((i) => `\`${i}\``).join(", ") : "_(none)_";
|
|
3
|
+
const dataAttrs = (variants) => Object.entries(variants).map(([axis, opts]) => `data-${kebab(axis)}="${Object.keys(opts).join("|")}"`);
|
|
4
|
+
/** One entry per component: class, variant data-*, states, and multi-part anatomy. */
|
|
5
|
+
function componentEntry(cname, recipe) {
|
|
6
|
+
const cls = `.ds-${kebab(cname)}`;
|
|
7
|
+
const axes = dataAttrs(recipe.variants).map((a) => `\`${a}\``);
|
|
8
|
+
const variantsText = axes.length ? ` — variants: ${axes.join(", ")}` : "";
|
|
9
|
+
const states = Object.keys(recipe.states ?? {});
|
|
10
|
+
const statesText = states.length ? `\n states: ${list(states)}` : "";
|
|
11
|
+
const partEntries = Object.entries(recipe.parts ?? {});
|
|
12
|
+
let partsText = "";
|
|
13
|
+
if (partEntries.length) {
|
|
14
|
+
const items = partEntries.map(([pname, part]) => {
|
|
15
|
+
const pcls = `.ds-${kebab(cname)}-${kebab(pname)}`;
|
|
16
|
+
const paxes = dataAttrs(part.variants ?? {});
|
|
17
|
+
return paxes.length ? `\`${pcls}\` (${paxes.join(", ")})` : `\`${pcls}\``;
|
|
18
|
+
});
|
|
19
|
+
partsText = `\n parts: ${items.join(", ")}`;
|
|
20
|
+
}
|
|
21
|
+
return `- **${cname}** (\`${cls}\`)${variantsText}\n ${recipe.description}${partsText}${statesText}`;
|
|
22
|
+
}
|
|
3
23
|
/**
|
|
4
24
|
* Builds GUIDE.md — instructions *for the agent* on how to build components
|
|
5
25
|
* that follow the design system. This is the piece that makes "I create the
|
|
@@ -10,19 +30,13 @@ export function buildGuide(payload) {
|
|
|
10
30
|
const { document: doc, slug, name, version } = payload;
|
|
11
31
|
const { meta, foundations, motion, components } = doc;
|
|
12
32
|
const semanticRoles = Object.keys(foundations.color.semantic);
|
|
33
|
+
const weights = Object.keys(foundations.typography.weights);
|
|
13
34
|
const hasAlt = foundations.color.semanticAlt &&
|
|
14
35
|
Object.keys(foundations.color.semanticAlt).length > 0;
|
|
15
36
|
const altScheme = meta.scheme === "light" ? "dark" : "light";
|
|
16
37
|
const hasTailwind = "theme.css" in payload.artifacts;
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
const axes = Object.entries(recipe.variants).map(([axis, opts]) => {
|
|
20
|
-
const options = Object.keys(opts);
|
|
21
|
-
return `\`data-${kebab(axis)}="${options.join("|")}"\``;
|
|
22
|
-
});
|
|
23
|
-
const variantsText = axes.length ? ` — variants: ${axes.join(", ")}` : "";
|
|
24
|
-
return `- **${cname}** (\`${cls}\`)${variantsText}\n ${recipe.description}`;
|
|
25
|
-
});
|
|
38
|
+
const hasParts = Object.values(components).some((r) => r.parts && Object.keys(r.parts).length > 0);
|
|
39
|
+
const componentLines = Object.entries(components).map(([cname, recipe]) => componentEntry(cname, recipe));
|
|
26
40
|
const artifactList = Object.keys(payload.artifacts)
|
|
27
41
|
.map((f) => `\`${f}\``)
|
|
28
42
|
.join(", ");
|
|
@@ -54,12 +68,18 @@ ${meta.narrative}
|
|
|
54
68
|
<div data-ds="${slug}">…your UI here…</div>
|
|
55
69
|
\`\`\`
|
|
56
70
|
All \`--ds-*\` custom properties and \`.ds-*\` classes only apply inside that scope.
|
|
71
|
+
Applying \`data-ds="${slug}"\` at the app root (e.g. \`<body>\` or the root layout)
|
|
72
|
+
is the simplest choice — the whole app then wears the system.
|
|
57
73
|
${hasAlt
|
|
58
74
|
? `
|
|
59
75
|
3. Light/dark: an ancestor with \`data-scheme="${altScheme}"\` switches the neutral roles to the opposite mode.
|
|
60
|
-
\`\`\`
|
|
76
|
+
\`\`\`tsx
|
|
61
77
|
<div data-scheme="${altScheme}"><div data-ds="${slug}">…</div></div>
|
|
62
78
|
\`\`\`
|
|
79
|
+
A theme toggle just adds/removes that attribute on the scope element:
|
|
80
|
+
\`\`\`tsx
|
|
81
|
+
root.toggleAttribute("data-scheme"); // present = ${altScheme}, absent = ${meta.scheme}
|
|
82
|
+
\`\`\`
|
|
63
83
|
`
|
|
64
84
|
: ""}${hasTailwind
|
|
65
85
|
? `
|
|
@@ -73,7 +93,7 @@ Import \`theme.css\` after \`tailwindcss\` and \`tokens.css\`:
|
|
|
73
93
|
\`\`\`
|
|
74
94
|
This maps the DS tokens onto Tailwind's theme, so inside \`[data-ds="${slug}"]\` you get utilities
|
|
75
95
|
backed by the design system: \`bg-*\`/\`text-*\`/\`border-*\` (semantic colors), \`p-*\`/\`m-*\`/\`gap-*\`
|
|
76
|
-
(spacing), \`rounded-*\`, \`shadow-*\`, \`font
|
|
96
|
+
(spacing), \`rounded-*\`, \`shadow-*\`, \`font-*\` (families **and** weights), \`text-*\` (type scale), \`ease-*\`.
|
|
77
97
|
|
|
78
98
|
**Prefer these utilities for layout and new composition** — they are this project's idiom and read
|
|
79
99
|
far better than inline \`style\`. Reach for inline \`var(--ds-*)\` only when no utility fits.
|
|
@@ -99,13 +119,59 @@ those, not the versioned ones. The pinned files for this version — ${artifactL
|
|
|
99
119
|
|
|
100
120
|
---
|
|
101
121
|
|
|
122
|
+
## Building with the system
|
|
123
|
+
|
|
124
|
+
**This system is for building real product UI** — pages, layouts, dashboards, whole flows.
|
|
125
|
+
Compose the \`.ds-*\` recipes (and their parts) together with the DS-backed utilities to assemble
|
|
126
|
+
actual screens. There is **no "samples only" rule**: build the real app. An
|
|
127
|
+
\`app/synthesisui-samples/<component>/\` page is a fine *optional* scratch space to eyeball a single
|
|
128
|
+
component, but it is never required.
|
|
129
|
+
|
|
130
|
+
### Layout & composition
|
|
131
|
+
The system defines the scale; these are sensible defaults for spending it:
|
|
132
|
+
- **Page gutter / container padding:** a large spacing step — ${list(Object.keys(foundations.spacing).filter((k) => /xl/.test(k)))}.
|
|
133
|
+
- **Section gaps:** \`lg\` (or the nearest large step). **Card/panel padding:** \`md\`.
|
|
134
|
+
- **Field / tight gaps:** \`2xs\`/\`3xs\`.
|
|
135
|
+
- The system imposes no content max-width — cap long-form/text columns yourself for readability.
|
|
136
|
+
${hasParts
|
|
137
|
+
? `
|
|
138
|
+
### Multi-part components
|
|
139
|
+
Components that have **parts** compile to \`.ds-<name>-<part>\` classes you nest yourself; the exact
|
|
140
|
+
part classes and their \`data-*\` are listed per component below. Example — a table:
|
|
141
|
+
\`\`\`tsx
|
|
142
|
+
<table className="ds-table">
|
|
143
|
+
<thead className="ds-table-head">
|
|
144
|
+
<tr>
|
|
145
|
+
<th className="ds-table-cell-head">Name</th>
|
|
146
|
+
<th className="ds-table-cell-head" data-align="end">Updated</th>
|
|
147
|
+
</tr>
|
|
148
|
+
</thead>
|
|
149
|
+
<tbody>
|
|
150
|
+
<tr className="ds-table-row">
|
|
151
|
+
<td className="ds-table-cell">Halogen</td>
|
|
152
|
+
<td className="ds-table-cell" data-align="end">2h ago</td>
|
|
153
|
+
</tr>
|
|
154
|
+
</tbody>
|
|
155
|
+
</table>
|
|
156
|
+
\`\`\`
|
|
157
|
+
`
|
|
158
|
+
: ""}
|
|
159
|
+
### Overlays & portals
|
|
160
|
+
Dialogs, menus and toasts are often rendered through a portal at the end of \`<body>\` — **outside**
|
|
161
|
+
your \`data-ds\` scope. Since \`.ds-*\`/\`--ds-*\` only resolve inside the scope, wrap any portalled UI
|
|
162
|
+
in its own \`<div data-ds="${slug}"${hasAlt ? ` data-scheme="…"` : ""}>\`, or apply \`data-ds\` at the
|
|
163
|
+
app root so everything (portals included) inherits it. Behavior (open/close, focus trap, positioning,
|
|
164
|
+
keyboard) is yours to wire — the system ships the **looks**, not the JavaScript.
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
102
168
|
## Rules (follow them when creating components)
|
|
103
169
|
${hasTailwind
|
|
104
170
|
? `
|
|
105
171
|
- **Styling mechanism:** prefer Tailwind utilities backed by the DS (\`bg-primary\`, \`p-md\`,
|
|
106
|
-
\`font-display\`, …) for layout and new composition, and reuse the \`.ds-*\` recipes
|
|
107
|
-
the DS already covers. Use inline \`style\` with \`var(--ds-*)\` only as a last resort.
|
|
108
|
-
names below are the source vocabulary — every utility derives from them.`
|
|
172
|
+
\`font-display\`, \`font-medium\`, …) for layout and new composition, and reuse the \`.ds-*\` recipes
|
|
173
|
+
for components the DS already covers. Use inline \`style\` with \`var(--ds-*)\` only as a last resort.
|
|
174
|
+
The token names below are the source vocabulary — every utility derives from them.`
|
|
109
175
|
: ""}
|
|
110
176
|
- **Always use semantic tokens**, never raw values nor primitives directly.
|
|
111
177
|
Color: \`var(--ds-color-semantic-<role>)\`${hasTailwind ? " (utility: `bg-<role>`/`text-<role>`)" : ""}. The roles are: ${list(semanticRoles)}.
|
|
@@ -115,7 +181,8 @@ ${hasTailwind
|
|
|
115
181
|
- Radius → \`var(--ds-radius-<key>)\`: ${list(Object.keys(foundations.radius))}.
|
|
116
182
|
- Shadow → \`var(--ds-shadow-<key>)\`: ${list(Object.keys(foundations.shadow))}.
|
|
117
183
|
- Typography: families \`--ds-typography-families-{display,body,mono}\` (${foundations.typography.families.display}, ${foundations.typography.families.body}, ${foundations.typography.families.mono});
|
|
118
|
-
|
|
184
|
+
weights${hasTailwind ? " (utility: `font-<key>`)" : ""}: ${list(weights)};
|
|
185
|
+
scale \`--ds-typography-scale-<key>-font-size\`${hasTailwind ? " (utility: `text-<key>`)" : ""}: ${list(Object.keys(foundations.typography.scale))}.
|
|
119
186
|
- Motion: durations \`--ds-motion-durations-<key>\` (${list(Object.keys(motion.durations))}) and
|
|
120
187
|
easings \`--ds-motion-easings-<key>\` (${list(Object.keys(motion.easings))}).
|
|
121
188
|
- When **creating a new component** the DS does not cover yet: compose it from these semantic
|
|
@@ -123,20 +190,11 @@ ${hasTailwind
|
|
|
123
190
|
|
|
124
191
|
---
|
|
125
192
|
|
|
126
|
-
## Where to preview
|
|
127
|
-
|
|
128
|
-
**Preview in isolation, never on a real page.** When creating or demoing a component, generate a
|
|
129
|
-
dedicated sample page — \`app/synthesisui-samples/<component>/\` in the Next.js App Router (or the
|
|
130
|
-
equivalent samples route/folder in the project's stack). **Do not** apply the component to real
|
|
131
|
-
production pages (home, layout, existing routes) unless explicitly asked. Samples let you review the
|
|
132
|
-
component in the context of the design system without touching the app.
|
|
133
|
-
|
|
134
|
-
---
|
|
135
|
-
|
|
136
193
|
## Ready-made components
|
|
137
194
|
|
|
138
|
-
Each recipe becomes a \`.ds-<name>\` class (inside the \`[data-ds="${slug}"]\` scope).
|
|
139
|
-
|
|
195
|
+
Each recipe becomes a \`.ds-<name>\` class (inside the \`[data-ds="${slug}"]\` scope). Variants are
|
|
196
|
+
\`data-<axis>="<option>"\` attributes; states (hover/focus/active/disabled) ship in the CSS;
|
|
197
|
+
multi-part components expose \`.ds-<name>-<part>\` classes (listed under each).
|
|
140
198
|
|
|
141
199
|
${componentLines.join("\n\n")}
|
|
142
200
|
|