thomas-agentkit 0.4.2 → 0.5.2

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/README.md CHANGED
@@ -72,6 +72,7 @@ Standardize install defaults with `agentkit.config.json`:
72
72
  {
73
73
  "preset": "next",
74
74
  "templateSet": "standard",
75
+ "designSystem": "linear",
75
76
  "aiTools": ["codex", "cursor", "claude"],
76
77
  "personalization": {
77
78
  "projectName": "Acme CRM",
@@ -109,6 +110,12 @@ List available presets:
109
110
  npx thomas-agentkit --list-presets
110
111
  ```
111
112
 
113
+ List bundled design systems (source variants for `DESIGN-SYSTEM.md`):
114
+
115
+ ```bash
116
+ npx thomas-agentkit --list-design-systems
117
+ ```
118
+
112
119
  ## Installed Files
113
120
 
114
121
  AgentKit copies these bundled files into the target project:
@@ -126,6 +133,8 @@ AgentKit copies these bundled files into the target project:
126
133
 
127
134
  When a preset is selected, AgentKit also installs `STACK.md` and adds a note in `AGENTS.md` telling agents to read it before changing stack-specific code.
128
135
 
136
+ Bundled design system guidance is stored under `templates/design-systems/` in this repository (for example `linear.md`, `apple.md`). The CLI installs the chosen variant into `DESIGN-SYSTEM.md` in the target project; variant paths are not separate install targets.
137
+
129
138
  Existing files are skipped by default so local edits are preserved. Use `--force` when you intentionally want to refresh files from the bundled package version.
130
139
 
131
140
  New installs wrap generated template content in AgentKit managed block markers:
@@ -140,7 +149,7 @@ Generated content
140
149
 
141
150
  Interactive personalization only applies during `agentkit init` when files are created or overwritten. It does not write a config file, and `agentkit update` does not reapply personalized values.
142
151
 
143
- `agentkit.config.json` can set `preset`, `templateSet`, `aiTools`, and `personalization` defaults. `templateSet` may be `minimal`, `standard`, or `full`; `aiTools` may include `codex`, `cursor`, `claude`, and `copilot`. `agentkit update` only uses config `preset` and continues to update all managed bundled templates.
152
+ `agentkit.config.json` can set `preset`, `templateSet`, `designSystem`, `aiTools`, and `personalization` defaults. `templateSet` may be `minimal`, `standard`, or `full`; `designSystem` selects which bundled design-system variant fills `DESIGN-SYSTEM.md` (`linear` or `apple`); `aiTools` may include `codex`, `cursor`, `claude`, and `copilot`. `agentkit update` uses config `preset` and `designSystem` and continues to update all managed bundled templates.
144
153
 
145
154
  ## Presets
146
155
 
@@ -155,10 +164,11 @@ Presets add stack-specific guidance without scaffolding framework app files.
155
164
  ## CLI Reference
156
165
 
157
166
  ```text
158
- agentkit init [target] [--force] [--dry-run] [--interactive] [--yes] [--write-config] [--preset <name>]
159
- agentkit update [target] [--dry-run] [--preset <name>]
167
+ agentkit init [target] [--force] [--dry-run] [--interactive] [--yes] [--write-config] [--preset <name>] [--design-system <name>]
168
+ agentkit update [target] [--dry-run] [--preset <name>] [--design-system <name>]
160
169
  agentkit --list
161
170
  agentkit --list-presets
171
+ agentkit --list-design-systems
162
172
  agentkit --help
163
173
  agentkit --version
164
174
  ```
@@ -171,12 +181,14 @@ Options:
171
181
  - `-y, --yes`: accept defaults without prompts
172
182
  - `--write-config`: write resolved install defaults to `agentkit.config.json`
173
183
  - `--preset <name>`: install stack-specific guidance (`next`, `sveltekit`, `express`, `convex`, `fullstack`)
184
+ - `--design-system <name>`: design system variant for `DESIGN-SYSTEM.md` (`linear`, `apple`)
174
185
  - `--list`: list bundled template files
175
186
  - `--list-presets`: list available presets
187
+ - `--list-design-systems`: list available design systems
176
188
  - `-h, --help`: show help
177
189
  - `-v, --version`: show package version
178
190
 
179
- For `agentkit update`, `--preset <name>` refreshes preset-specific managed content, including `STACK.md`.
191
+ For `agentkit update`, `--preset <name>` refreshes preset-specific managed content, including `STACK.md`. `--design-system <name>` refreshes the managed `DESIGN-SYSTEM.md` body from the matching bundled variant.
180
192
 
181
193
  ## Local Development
182
194
 
package/dist/cli.js CHANGED
@@ -14,8 +14,13 @@ const validPresets = ["next", "sveltekit", "express", "convex", "fullstack"];
14
14
  const validProjectTypes = ["generic", ...validPresets];
15
15
  const validAiTools = ["codex", "cursor", "claude", "copilot"];
16
16
  const validTemplateSets = ["minimal", "standard", "full"];
17
+ const validDesignSystems = ["linear", "apple"];
18
+ const designSystemLabels = {
19
+ linear: "Linear-inspired",
20
+ apple: "Apple-inspired",
21
+ };
17
22
  const configFileName = "agentkit.config.json";
18
- const configKeys = ["preset", "templateSet", "aiTools", "personalization"];
23
+ const configKeys = ["preset", "templateSet", "aiTools", "designSystem", "personalization"];
19
24
  const personalizationKeys = [
20
25
  "projectName",
21
26
  "projectDescription",
@@ -107,23 +112,35 @@ async function readPackageVersion() {
107
112
  const packageJson = JSON.parse(await readFile(packageJsonPath, "utf8"));
108
113
  return packageJson.version ?? "0.0.0";
109
114
  }
110
- async function getTemplateFiles(dir = templatesDir, base = templatesDir) {
115
+ async function collectInstallableTemplatePaths(dir, base) {
111
116
  const dirStat = await stat(dir);
112
117
  if (!dirStat.isDirectory()) {
113
118
  throw new Error(`Bundled templates directory not found: ${templatesDir}`);
114
119
  }
115
120
  const entries = await readdir(dir, { withFileTypes: true });
116
- const files = await Promise.all(entries.map(async (entry) => {
121
+ const discovered = [];
122
+ for (const entry of entries) {
117
123
  const absolutePath = path.join(dir, entry.name);
118
124
  if (entry.isDirectory()) {
119
- return getTemplateFiles(absolutePath, base);
125
+ if (dir === templatesDir && entry.name === "design-systems") {
126
+ continue;
127
+ }
128
+ discovered.push(...(await collectInstallableTemplatePaths(absolutePath, base)));
129
+ continue;
120
130
  }
121
131
  if (!entry.isFile()) {
122
- return [];
132
+ continue;
123
133
  }
124
- return [path.relative(base, absolutePath).split(path.sep).join("/")];
125
- }));
126
- return files.flat().sort();
134
+ discovered.push(path.relative(base, absolutePath).split(path.sep).join("/"));
135
+ }
136
+ return discovered;
137
+ }
138
+ async function getTemplateFiles() {
139
+ const discovered = await collectInstallableTemplatePaths(templatesDir, templatesDir);
140
+ const withDesignSystem = discovered.includes("DESIGN-SYSTEM.md")
141
+ ? discovered
142
+ : [...discovered, "DESIGN-SYSTEM.md"];
143
+ return withDesignSystem.sort();
127
144
  }
128
145
  function isPresetName(value) {
129
146
  return validPresets.includes(value);
@@ -137,12 +154,32 @@ function isAiToolName(value) {
137
154
  function isTemplateSetName(value) {
138
155
  return validTemplateSets.includes(value);
139
156
  }
157
+ function isDesignSystemName(value) {
158
+ return validDesignSystems.includes(value);
159
+ }
140
160
  function formatPresetList() {
141
161
  return validPresets.join(", ");
142
162
  }
143
163
  function formatTemplateSetList() {
144
164
  return validTemplateSets.join(", ");
145
165
  }
166
+ function formatDesignSystemList() {
167
+ return validDesignSystems.join(", ");
168
+ }
169
+ function resolveDesignSystem(name) {
170
+ const normalized = name.toLowerCase();
171
+ if (!isDesignSystemName(normalized)) {
172
+ throw new Error(`Unknown design system "${name}". Valid design systems: ${formatDesignSystemList()}.`);
173
+ }
174
+ return normalized;
175
+ }
176
+ function effectiveDesignSystem(name) {
177
+ const trimmed = name?.trim();
178
+ if (!trimmed) {
179
+ return "linear";
180
+ }
181
+ return resolveDesignSystem(trimmed);
182
+ }
146
183
  function resolvePreset(preset) {
147
184
  if (!preset) {
148
185
  return undefined;
@@ -202,6 +239,12 @@ function parseConfig(rawConfig, configPath) {
202
239
  return aiTool;
203
240
  });
204
241
  }
242
+ if (rawConfig.designSystem !== undefined) {
243
+ if (typeof rawConfig.designSystem !== "string") {
244
+ throw new Error(`${configFileName} designSystem must be a string.`);
245
+ }
246
+ config.designSystem = resolveDesignSystem(rawConfig.designSystem);
247
+ }
205
248
  if (rawConfig.personalization !== undefined) {
206
249
  assertPlainObject(rawConfig.personalization, `${configFileName} personalization`);
207
250
  assertKnownKeys(rawConfig.personalization, personalizationKeys, `${configFileName} personalization`);
@@ -252,6 +295,7 @@ async function applyInitConfig(options, config) {
252
295
  return;
253
296
  }
254
297
  options.preset ??= config.preset;
298
+ options.designSystem ??= config.designSystem;
255
299
  options.personalization ??= config.personalization;
256
300
  options.templateSet ??= config.templateSet;
257
301
  options.aiTools ??= config.aiTools;
@@ -261,6 +305,7 @@ function applyUpdateConfig(options, config) {
261
305
  return;
262
306
  }
263
307
  options.preset ??= config.preset;
308
+ options.designSystem ??= config.designSystem;
264
309
  }
265
310
  export function resolveProjectPreset(projectType) {
266
311
  if (!projectType) {
@@ -328,6 +373,7 @@ function getResolvedConfig(options) {
328
373
  const config = {
329
374
  templateSet: options.templateSet ?? "full",
330
375
  aiTools: options.aiTools ?? [],
376
+ designSystem: effectiveDesignSystem(options.designSystem),
331
377
  };
332
378
  const preset = resolvePreset(options.preset);
333
379
  if (preset) {
@@ -532,8 +578,8 @@ async function promptForPersonalization(defaults) {
532
578
  stackSummary,
533
579
  };
534
580
  }
535
- async function buildInitTemplateContent(file, preset, personalization) {
536
- const content = await buildTemplateContent(file, preset);
581
+ async function buildInitTemplateContent(file, preset, personalization, designSystem) {
582
+ const content = await buildTemplateContent(file, preset, designSystem);
537
583
  return personalizeTemplateContent(file, content, personalization);
538
584
  }
539
585
  async function installTemplates(targetArg, options) {
@@ -544,6 +590,7 @@ async function installTemplates(targetArg, options) {
544
590
  ? getSelectedTemplateFiles(options.templateSet ?? "full", options.aiTools ?? [], allTemplateFiles)
545
591
  : allTemplateFiles);
546
592
  const preset = resolvePreset(options.preset);
593
+ const designSystem = effectiveDesignSystem(options.designSystem);
547
594
  const created = [];
548
595
  const skipped = [];
549
596
  if (!options.dryRun) {
@@ -572,14 +619,8 @@ async function installTemplates(targetArg, options) {
572
619
  created.push(file);
573
620
  if (!options.dryRun) {
574
621
  await mkdir(path.dirname(destination), { recursive: true });
575
- if (preset && file === "AGENTS.md") {
576
- const content = await buildInitTemplateContent(file, preset, options.personalization);
577
- await writeFile(destination, wrapManagedBlock(file, content));
578
- }
579
- else {
580
- const content = await buildInitTemplateContent(file, preset, options.personalization);
581
- await writeFile(destination, wrapManagedBlock(file, content));
582
- }
622
+ const content = await buildInitTemplateContent(file, preset, options.personalization, designSystem);
623
+ await writeFile(destination, wrapManagedBlock(file, content));
583
624
  }
584
625
  }
585
626
  if (preset) {
@@ -614,13 +655,18 @@ function replaceManagedBlock(file, existingContent, nextContent) {
614
655
  const replacement = wrapManagedBlock(file, nextContent).trimEnd();
615
656
  return `${existingContent.slice(0, startIndex)}${replacement}${existingContent.slice(afterEndIndex)}`;
616
657
  }
617
- async function buildTemplateContent(file, preset) {
658
+ async function buildTemplateContent(file, preset, designSystem) {
618
659
  if (file === "STACK.md") {
619
660
  if (!preset) {
620
661
  throw new Error("STACK.md requires a preset.");
621
662
  }
622
663
  return getStackGuidance(preset);
623
664
  }
665
+ if (file === "DESIGN-SYSTEM.md") {
666
+ const source = path.join(templatesDir, "design-systems", `${designSystem}.md`);
667
+ const content = await readFile(source, "utf8");
668
+ return addStackReference(file, content, preset);
669
+ }
624
670
  const source = path.join(templatesDir, file);
625
671
  const content = await readFile(source, "utf8");
626
672
  return addStackReference(file, content, preset);
@@ -628,6 +674,7 @@ async function buildTemplateContent(file, preset) {
628
674
  async function updateTemplates(targetArg, options) {
629
675
  const targetDir = path.resolve(process.cwd(), targetArg || ".");
630
676
  const preset = resolvePreset(options.preset);
677
+ const designSystem = effectiveDesignSystem(options.designSystem);
631
678
  const files = preset ? [...(await getTemplateFiles()), "STACK.md"] : await getTemplateFiles();
632
679
  const created = [];
633
680
  const updated = [];
@@ -636,7 +683,7 @@ async function updateTemplates(targetArg, options) {
636
683
  const unchanged = [];
637
684
  for (const file of files) {
638
685
  const destination = path.join(targetDir, file);
639
- const nextContent = await buildTemplateContent(file, preset);
686
+ const nextContent = await buildTemplateContent(file, preset, designSystem);
640
687
  const destinationExists = await exists(destination);
641
688
  if (!destinationExists) {
642
689
  created.push(file);
@@ -777,6 +824,17 @@ async function resolveInteractiveTarget(target, options) {
777
824
  options.templateSet = templateSetResponse;
778
825
  options.aiTools = aiToolResponse;
779
826
  options.files = getSelectedTemplateFiles(templateSetResponse, aiToolResponse, templateFiles);
827
+ if (templateSetResponse === "standard" || templateSetResponse === "full") {
828
+ const designSystemResponse = await select({
829
+ message: "Which design system guidance?",
830
+ initialValue: effectiveDesignSystem(options.designSystem),
831
+ options: validDesignSystems.map((value) => ({ label: designSystemLabels[value], value })),
832
+ });
833
+ if (isCancel(designSystemResponse)) {
834
+ process.exit(130);
835
+ }
836
+ options.designSystem = designSystemResponse;
837
+ }
780
838
  if (!options.force) {
781
839
  const preset = resolvePreset(options.preset);
782
840
  const installFiles = preset ? [...options.files, "STACK.md"] : options.files;
@@ -819,6 +877,12 @@ async function main() {
819
877
  }
820
878
  return;
821
879
  }
880
+ if (process.argv.slice(2).includes("--list-design-systems")) {
881
+ for (const designSystem of validDesignSystems) {
882
+ console.log(designSystem);
883
+ }
884
+ return;
885
+ }
822
886
  const program = new Command();
823
887
  program
824
888
  .name("agentkit")
@@ -826,6 +890,7 @@ async function main() {
826
890
  .version(await readPackageVersion(), "-v, --version")
827
891
  .option("--list", "list bundled template files")
828
892
  .option("--list-presets", "list available presets")
893
+ .option("--list-design-systems", "list available design systems")
829
894
  .addHelpText("after", `
830
895
 
831
896
  Examples:
@@ -834,6 +899,7 @@ Examples:
834
899
  agentkit init --preset next
835
900
  agentkit init ./my-project --yes --dry-run
836
901
  agentkit --list-presets
902
+ agentkit --list-design-systems
837
903
  agentkit --list`);
838
904
  program
839
905
  .command("init")
@@ -845,6 +911,7 @@ Examples:
845
911
  .option("-y, --yes", "accept defaults for non-interactive runs")
846
912
  .option("--write-config", "write resolved install defaults to agentkit.config.json")
847
913
  .option("--preset <name>", `install stack-specific guidance (${formatPresetList()})`)
914
+ .option("--design-system <name>", `design system guidance for DESIGN-SYSTEM.md (${formatDesignSystemList()})`)
848
915
  .action(async (target, options) => {
849
916
  await applyInitConfig(options, await loadConfigForTarget(target));
850
917
  const resolvedTarget = await resolveInteractiveTarget(target, options);
@@ -857,6 +924,7 @@ Examples:
857
924
  .argument("[target]", "target project directory", ".")
858
925
  .option("--dry-run", "print planned changes without writing files")
859
926
  .option("--preset <name>", `update stack-specific guidance (${formatPresetList()})`)
927
+ .option("--design-system <name>", `design system guidance for DESIGN-SYSTEM.md (${formatDesignSystemList()})`)
860
928
  .action(async (target, options) => {
861
929
  applyUpdateConfig(options, await loadConfigForTarget(target));
862
930
  const result = await updateTemplates(target, options);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "thomas-agentkit",
3
- "version": "0.4.2",
3
+ "version": "0.5.2",
4
4
  "description": "Install AI-agent-ready development templates into a project.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -0,0 +1,236 @@
1
+ # [Project Name] Design System - Apple-Inspired Foundations
2
+
3
+ This design system codifies the visual and interaction principles for [Project Name].
4
+
5
+ It is the default source of truth for app shell layout, navigation, lists, controls, and page chrome in this repository.
6
+
7
+ All colors, typography families, and radii must be consumed through semantic tokens in `[theme stylesheet path, e.g. src/styles.css]` or a dedicated theme stylesheet imported there. Components must not hardcode literal color values or font family names.
8
+
9
+ ## 1) Surface And Color
10
+
11
+ ### Philosophy
12
+
13
+ The interface reads as a calm, spacious canvas. Separation comes from spacing, typography, subtle grouped inset surfaces when grouping controls, and structural seams—not stacked decorative containers.
14
+
15
+ ### Token Roles
16
+
17
+ Define semantic CSS custom properties and map them to the project's theme tokens:
18
+
19
+ - `--background`: app canvas
20
+ - `--foreground`: primary text
21
+ - `--muted-foreground`: secondary/meta text
22
+ - `--border`: structural seams
23
+ - `--card`: grouped inset surfaces (forms, settings groups, grouped lists) and dialogs/popovers
24
+ - `--popover`: overlay surfaces
25
+ - `--accent`: subtle hover/active in content areas
26
+ - `--sidebar`: sidebar canvas
27
+ - `--sidebar-accent`: hover/active in sidebar
28
+ - `--primary`: brand and primary CTA (system blue)
29
+ - `--destructive`: danger actions
30
+ - `--ring`: focus ring
31
+
32
+ ### Rules
33
+
34
+ - Canvas-first for browsing and routine reading; use grouped inset surfaces only where controls belong together (forms, settings, grouped lists).
35
+ - No decorative gradients on product surfaces.
36
+ - Use one accent color, `--primary`, for brand and primary actions. Map `--primary` to system blue (for example `#007AFF` in light, `#0A84FF` in dark), expressed via tokens—not literals in components.
37
+ - Prefer spacing and typographic hierarchy over extra backgrounds.
38
+ - Keep all colors tokenized and theme-ready for light and dark modes.
39
+
40
+ ## 2) Typography
41
+
42
+ ### Families
43
+
44
+ - Interface text: `font-sans` backed by `--font-sans` (map theme to `-apple-system, BlinkMacSystemFont, "SF Pro Text", "Inter", system-ui, sans-serif`).
45
+ - Code, IDs, and timestamps: `font-mono` backed by `--font-mono` (map theme to `"SF Mono", ui-monospace, "JetBrains Mono", monospace`).
46
+
47
+ Do not reference literal font families in component files.
48
+
49
+ ### Scale
50
+
51
+ - Page heading: `text-2xl`, `font-semibold`
52
+ - Section heading: `text-lg`, `font-semibold`
53
+ - Body / row label: `text-sm`, `font-normal` to `font-medium`
54
+ - Meta / secondary: `text-xs`, `font-normal`
55
+ - Nav section label: `text-xs uppercase tracking-wide`, `font-medium`
56
+ - Tiny helper: `text-[11px]`, `font-normal`
57
+
58
+ ### Rules
59
+
60
+ - Headlines are clear and restrained; prefer slightly tighter tracking on large display sizes (`tracking-tight` where appropriate).
61
+ - Use comfortable line height for body (`leading-normal` or `leading-relaxed`).
62
+ - Reserve uppercase for nav section labels and compact metadata.
63
+
64
+ ## 3) Navigation
65
+
66
+ ### Structure
67
+
68
+ 1. Header row: identity, search, and create action.
69
+ 2. Primary links.
70
+ 3. Collapsible grouped links.
71
+ 4. Lightweight footer affordance for help or docs.
72
+
73
+ ### Item Styling
74
+
75
+ - Default: `text-sm`, muted foreground.
76
+ - Hover: subtle `sidebar-accent` background.
77
+ - Active: full-width rounded rectangle, medium weight.
78
+ - Icon size: 14px to 16px.
79
+ - Nav row baseline: `rounded-lg h-9 px-3`.
80
+
81
+ ### Collapse Behavior
82
+
83
+ - Expanded width: `15rem` or 240px.
84
+ - Collapsed width: `3rem` or 48px, icon-only with tooltip.
85
+ - Mobile: sheet/drawer using expanded width.
86
+ - Keyboard shortcut: support the project's standard sidebar toggle shortcut, e.g. `Cmd+B`.
87
+
88
+ ## 4) Layout Regions
89
+
90
+ ### App Shell
91
+
92
+ - Sidebar: fixed left, full viewport height, right seam border.
93
+ - Content: `flex-1 min-w-0`, same background canvas.
94
+ - Optional inspector: right panel, `22rem` to `28rem`, collapsible.
95
+
96
+ ### Common Page Patterns
97
+
98
+ - List workspace: toolbar plus scrollable list.
99
+ - Detail view: breadcrumbs/title plus body and optional inspector.
100
+ - Editor split: left rail plus center editor.
101
+ - Settings split: nav plus forms inside grouped inset surfaces.
102
+
103
+ ### Toolbar Strip
104
+
105
+ - Height: `h-12`.
106
+ - Bottom seam: `border-b border-border/50`.
107
+ - No separate heavy background block.
108
+
109
+ ### Page padding
110
+
111
+ - Content padding: `p-4` to `p-6` (16px to 24px) for comfortable reading rhythm.
112
+
113
+ ## 5) Interactive Elements
114
+
115
+ ### Buttons
116
+
117
+ - Primary: solid `primary` (system blue).
118
+ - Secondary: neutral fill.
119
+ - Ghost: transparent with subtle hover fill.
120
+ - Destructive: `destructive` only for dangerous confirms.
121
+ - Icon-only: `size-9` or `size-10`, ghost, tooltip.
122
+
123
+ Rules:
124
+
125
+ - Default radius: `rounded-lg` using the tokenized radius scale.
126
+ - Sparse shadows; prefer borders for separation on web.
127
+ - Primary buttons are intentional—do not pepper every row with a primary.
128
+
129
+ ### Inputs
130
+
131
+ - Default height: `h-9`; relaxed contexts: `h-10` to `h-12`.
132
+ - Tokenized input background and border.
133
+ - Visible focus ring: `ring-2 ring-ring` with comfortable offset.
134
+ - Labels above fields. Do not use floating labels by default.
135
+
136
+ ### Menus / Popovers / Dialogs
137
+
138
+ - Tokenized `popover` surface and subtle border.
139
+ - Typography: `text-sm` for menus; `text-sm` to `text-base` for dialogs as needed.
140
+ - Comfortable spacing.
141
+ - Dialog actions align right: secondary then primary.
142
+
143
+ ## 6) Lists And Data
144
+
145
+ ### Row Anatomy
146
+
147
+ - Relaxed, single-line by default where possible.
148
+ - Hover uses subtle accent fill.
149
+ - Selected row uses tinted fill derived from `--primary` (for example ~12% opacity in light, ~24% in dark) plus medium weight.
150
+ - Avoid per-row borders; separate by spacing and padding rhythm.
151
+
152
+ ### Grouping And Tables
153
+
154
+ - Group labels are muted; children are indented or wrapped in grouped inset surfaces.
155
+ - Table headers: compact meta style; avoid heavy uppercase unless it aids scanability.
156
+ - No zebra striping or heavy gridlines.
157
+
158
+ ## 7) Status And Feedback
159
+
160
+ - Status: small dots or SF-symbol-style glyphs with semantic meaning (optional).
161
+ - Loading: skeletons for content, spinners only for inline actions.
162
+ - Toasts: soft shadow, corner-pinned or non-intrusive, auto-dismiss.
163
+ - Empty states: icon, short message, and optional single CTA.
164
+
165
+ ## 8) Motion
166
+
167
+ - Motion confirms state changes; it is never decorative.
168
+ - Micro interactions: about 200ms.
169
+ - Layout shifts: about 300ms to 350ms.
170
+ - Preferred easing: `cubic-bezier(0.25, 0.1, 0.25, 1)`.
171
+ - Sheets and popovers may use a subtle spring-like overshoot when it aids affordance; keep amplitude low on web.
172
+ - Respect `prefers-reduced-motion` with near-instant transitions.
173
+
174
+ ## 9) Spacing System
175
+
176
+ Base unit is 4px, using the project's spacing scale.
177
+
178
+ - `p-1` or 4px: tight internals.
179
+ - `p-2` or 8px: compact section padding.
180
+ - `p-3` or 12px: dense content blocks.
181
+ - `p-4` or 16px: page-level rhythm.
182
+ - `p-6` or 24px: relaxed sections when the layout calls for breathing room.
183
+
184
+ Density guidelines:
185
+
186
+ - Sidebar: relaxed, `h-9` rows.
187
+ - Content: moderate, `h-10` to `h-12` rows.
188
+ - Forms/settings: relaxed, `h-12` to `h-14` rows.
189
+
190
+ ## 10) Accessibility
191
+
192
+ - Full keyboard navigation for all controls.
193
+ - Consistent visible focus styles.
194
+ - Semantic landmarks such as `nav` and `main`.
195
+ - Collapsibles expose `aria-expanded`.
196
+ - Ensure WCAG AA contrast in both themes.
197
+
198
+ ## 11) Anti-Patterns
199
+
200
+ - Do not wrap routine browsing content in grouped inset surfaces.
201
+ - Do not introduce multiple accent colors.
202
+ - Do not add decorative gradients behind core product surfaces.
203
+ - Do not stack multiple competing toolbars.
204
+ - Do not color-code nav icons.
205
+ - Do not use heavy drop shadows for everyday chrome.
206
+ - Do not hardcode colors or font families in components.
207
+
208
+ ## 12) Applying This System
209
+
210
+ ### Where Values Live
211
+
212
+ - Theme tokens and base layer: `[theme stylesheet path, e.g. src/styles.css]`.
213
+ - App shell composition: `[route/layout path, e.g. src/routes or app]`.
214
+ - Reusable UI primitives: `[components path, e.g. src/components/ui]`.
215
+
216
+ ### New Component Checklist
217
+
218
+ 1. Canvas-first or justified grouped inset for grouped controls—not card-first for everything?
219
+ 2. Uses 2-3 text hierarchy levels max?
220
+ 3. Has subtle hover and clear active states?
221
+ 4. Uses primary color only where needed?
222
+ 5. Uses spacing rhythm in 4px increments?
223
+ 6. Has complete keyboard and focus behavior?
224
+ 7. Uses token-driven colors and fonts?
225
+ 8. Works in light and dark themes?
226
+
227
+ ### For Coding Agents
228
+
229
+ - Read this file before changing UI, layout, navigation, styling, or components.
230
+ - Prefer existing UI primitives and local layout patterns.
231
+ - Do not introduce new visual language without documenting the reason in the implementation brief or PR.
232
+ - Before handoff, check responsive behavior, focus states, text overflow, loading states, empty states, and token usage.
233
+
234
+ ### Working Rule
235
+
236
+ Default to these principles unless a deliberate exception is documented in a feature-specific spec.