@devalok/shilp-sutra 0.40.0 → 0.41.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.
@@ -0,0 +1,71 @@
1
+ # Upgrading shilp-sutra safely
2
+
3
+ A version bump is **not** safe-by-default. Breaking changes in this design system are often type-level (prop types narrowed, symbols moved between barrels) and only surface at `tsc`/build time — never assume a bump is low-risk before doing the checks below.
4
+
5
+ > **Hard rule for AI agents:** On ANY version bump, do not report the upgrade as safe before you have read the COMPLETE changelog **and** `MIGRATION.md` for the target version, grepped the consumer codebase for moved/renamed/narrowed symbols, and run `typecheck` + `build`. Breaking entries are frequently ordered LAST in the changelog (changesets orders by file, not severity) — skimming the top and relaxing is the #1 failure mode.
6
+
7
+ ## Step 1 — read the full breaking surface
8
+
9
+ 1. Open the target version's section in `node_modules/@devalok/shilp-sutra/CHANGELOG.md` (or the GitHub release). **Read all of it**, not just the top.
10
+ 2. Open `node_modules/@devalok/shilp-sutra/MIGRATION.md` and read every section from your current version up to the target.
11
+ 3. Scan for these signals — each is a potential break:
12
+ - `feat!` / `BREAKING` headers
13
+ - "removed", "moved", "renamed", "narrowed", "no longer exported"
14
+ - any prop **type** change (a narrowing — new type accepts less than the old — fails `tsc` for values that compiled before)
15
+ - peer-dependency changes (a symbol now imported from a per-component subpath instead of the barrel)
16
+
17
+ ## Step 2 — find affected call sites in your code
18
+
19
+ **Fastest path — read the machine-readable manifest:**
20
+
21
+ ```bash
22
+ # Lists every break per version as structured data (moves, narrowings, removals)
23
+ cat node_modules/@devalok/shilp-sutra/BREAKING.json
24
+ ```
25
+
26
+ Or programmatically:
27
+
28
+ ```js
29
+ import manifest from '@devalok/shilp-sutra/BREAKING.json'
30
+ // manifest.versions["0.40.0"].moved → [{ symbol, from, to, peer, eslintRule }, …]
31
+ // manifest.versions["0.40.0"].narrowed → [{ prop, components, from, to, fix }, …]
32
+ ```
33
+
34
+ Schema: `@devalok/shilp-sutra/BREAKING.schema.json`. AI agents should prefer this over prose-parsing CHANGELOG.
35
+
36
+ **Or grep manually:**
37
+
38
+ ```bash
39
+ # Symbols moved out of barrels (0.40.0 peer-cliff cleanup example):
40
+ grep -rn "from '@devalok/shilp-sutra/ui'" src/ | grep -E "Toaster|toast|InputOTP"
41
+ grep -rn "from '@devalok/shilp-sutra/composed'" src/ | grep -E "DatePicker|EmojiPicker|FilePreview|MarkdownViewer|RichTextEditor|RichChatInput"
42
+ grep -rn "from '@devalok/shilp-sutra/ai'" src/ | grep -E "BlockRenderer|ErrorBlock|TextBlock"
43
+
44
+ # Type narrowings — find icon sources annotated as the wide React.ReactNode:
45
+ grep -rn "React.ReactNode" src/ | grep -i "icon"
46
+ ```
47
+
48
+ ## Step 3 — let the ESLint plugin do the mechanical work
49
+
50
+ ```bash
51
+ pnpm add -D @devalok/eslint-plugin-shilp-sutra
52
+ # one-shot codemod: repoints moved barrel imports + flags deprecated APIs
53
+ pnpm eslint --fix --config node_modules/@devalok/eslint-plugin-shilp-sutra/migration src/
54
+ ```
55
+
56
+ The `prefer-per-component-import` rule autofixes barrel→subpath moves and splits multi-symbol lines correctly. It cannot retype your own annotations — narrowings (Step 4) are still manual.
57
+
58
+ ## Step 4 — typecheck + build BEFORE deploy
59
+
60
+ ```bash
61
+ pnpm typecheck && pnpm build
62
+ ```
63
+
64
+ This is the gate that catches type narrowings the changelog may have under-described. Two real examples from 0.40.0:
65
+
66
+ - **Barrel peer-cliff cleanup (breaking):** `import { Toaster } from '@devalok/shilp-sutra/ui'` → `import { Toaster } from '@devalok/shilp-sutra/ui/toaster'`. 12 symbol families. Full table in `MIGRATION.md → v0.40.0`.
67
+ - **Icon API narrowing:** the unified `IconInput` type excludes `string`/`number`/iterables that `React.ReactNode` allowed. If you stored icons in a `Record<string, React.ReactNode>` map or `icon?: React.ReactNode` field and passed them to a migrated component (`CommandItem.icon`, `ActivityItem.icon`, `Chat.Message.Avatar`), retype the source to `React.ReactElement`.
68
+
69
+ ## Step 5 — only now is the bump "safe"
70
+
71
+ Report the upgrade as complete only after Steps 1-4 pass. If anything in the changelog was ambiguous, prefer a per-prop `expectTypeOf` probe over assuming "non-breaking".
package/llms-full.txt CHANGED
@@ -5,7 +5,7 @@
5
5
  > All variant values and props verified from source CVA definitions.
6
6
  >
7
7
  > Package: @devalok/shilp-sutra
8
- > Version: 0.40.0
8
+ > Version: 0.41.0
9
9
  >
10
10
  > **If you are an AI agent reading this file top-to-bottom:** the Setup
11
11
  > section below is authoritative. If any later per-component doc or a
package/llms-quick.txt CHANGED
@@ -21,7 +21,7 @@ Then 4 files:
21
21
 
22
22
  ```ts
23
23
  // next.config.ts — only if Next.js
24
- transpilePackages: ["@devalok/shilp-sutra", "@devalok/shilp-sutra-brand"]
24
+ transpilePackages: ["@devalok/shilp-sutra"]
25
25
  ```
26
26
 
27
27
  ```tsx
@@ -45,6 +45,10 @@ Per-framework recipes: `node_modules/@devalok/shilp-sutra/docs/recipes/install-<
45
45
 
46
46
  **Theme it in 30 seconds:** https://shilp-sutra.devalok.in/themer — outputs a copy-pasteable CSS block (12-step OKLCH ramp + role tokens). Paste *after* the `@devalok/shilp-sutra/css` import.
47
47
 
48
+ **Lint + migrate:** `pnpm add -D @devalok/eslint-plugin-shilp-sutra`, then `shilpSutra.configs['flat/recommended']`. Catches deprecated APIs, peer-cliff barrel imports, TW3 classes — most autofixable. Use the `migration` preset (`pnpm eslint --fix`) when upgrading across breaking versions.
49
+
50
+ **Machine-readable breaks:** `node_modules/@devalok/shilp-sutra/BREAKING.json` lists every breaking change per version as structured data (moves, narrowings, removals). Read this for programmatic upgrade planning instead of parsing CHANGELOG prose. Schema: `BREAKING.schema.json`.
51
+
48
52
  ## OPTIONAL PEER DEPENDENCIES (install BEFORE first import)
49
53
 
50
54
  | When you import… | Install |
@@ -65,7 +69,7 @@ Per-framework recipes: `node_modules/@devalok/shilp-sutra/docs/recipes/install-<
65
69
  2. **`framer-motion@^12` required peer.** Single copy — configure pnpm/yarn overrides if you see duplicates.
66
70
  3. **`sonner@^2`** required only when you render `<Toaster />` or call `toast.*`. Optional otherwise.
67
71
  4. **Per-component imports keep RSC fast AND avoid peer-dep cliffs.** Barrel `@devalok/shilp-sutra/ui` works in client contexts but inflates client bundle and forces optional peers to be installed. Prefer `…/ui/text`, `…/ui/dialog`, etc. The barrel **no longer** re-exports peer-cliff symbols (`Toaster`, `toast`, `InputOTP`, `DatePicker`, `RichTextEditor`, `EmojiPicker`, `FilePreview`, `MarkdownViewer`, `BlockRenderer`, `ErrorBlock`, `TextBlock`) as of 0.40.0 — import per-component.
68
- 5. **Spacing namespace is `--spacing-ds-*`.** `p-ds-04`, `gap-ds-03` — these DO NOT replace TW4 default `p-4`, `gap-2`. Both coexist by design. Pick `p-ds-*` for values that should track DS theme changes, `p-N` for one-off layout values.
72
+ 5. **Spacing namespace is `--spacing-ds-*`.** `p-ds-04`, `gap-ds-03` — these DO NOT replace TW4 default `p-4`, `gap-2`. Both coexist by design. Pick `p-ds-*` for values that should track DS theme changes, `p-N` for one-off layout values. **Cadence when building layouts:** pick a 3-tier scale, not every adjacent token — `ds-03` (related: label↔field), `ds-05` (grouped: between field-groups), `ds-07` (section: between blocks), optional `ds-08`+ (hero). 3-4 distinct gaps per surface max; 5+ = muddy rhythm. Anti-pattern: `ds-02` + `ds-04` as different signals on one surface (they collapse). Squint test must still show grouping.
69
73
  6. **Bare `shadow` class renders no shadow in TW4.** Use `shadow-raised` (cards), `shadow-floating` (dropdowns), `shadow-overlay` (dialogs), `shadow-ring` (focus).
70
74
  7. **Variant names must match CVA source exactly** — invented variant names silently no-op (CVA falls back to defaults). Grep `packages/core/src/ui/<component>.tsx` if in doubt.
71
75
  8. **Default to `variant="soft"`** over `variant="outline"` for non-primary Button actions. Outline only on colored backgrounds or where primary/secondary hierarchy needs a hard border.
package/llms.txt CHANGED
@@ -44,11 +44,12 @@ The repo URL for these files is `https://github.com/devalok-design/shilp-sutra/t
44
44
  ## NEW (v0.40.0)
45
45
 
46
46
  - **OAuthButton.** Brand-aware social/login buttons. Subpath: `@devalok/shilp-sutra/ui/oauth-button`. 13 providers (`google` `apple` `github` `microsoft` `x` `linkedin` `facebook` `discord` `slack` `gitlab` `sso` `email` `passkey`). Props: `provider`, `intent` (`continue|signin|signup`), `appearance` (`brand|outline|dark`), `icon` (override default glyph), `iconOnly`, `compact` (renders just "Google" instead of "Continue with Google"; aria-label keeps long form), `lastUsed` (inline right-edge pill inside button), `helperText`. Inherits Button async/loading/sizes. Siblings: `OAuthGroup` (with `reorderLastUsedFirst` for Stripe-style ordering), `OAuthDivider`, `OAuthConnectionRow` (settings-page linked state). Default glyphs from Tabler peer dep; pass `icon` to drop in a brand's official multicolour SVG. In dark mode every brand appearance lands on the same DS surface — brand identity comes from the glyph, not the bg, so rows stay visually coherent.
47
- - **Icon API unification.** Every icon-accepting prop (`startIcon`, `endIcon`, `icon`, `leftIcon`, `rightIcon`) across 22 components now takes one type: **`IconInput`**. Pass a rendered element (`<Icon icon={IconPlus} />` or `<IconPlus />`), a component ref (`IconPlus`), or any custom node — all four shapes work interchangeably. Type widening only; every call that compiled before still compiles. Helpers exported for your own wrappers: `import type { IconInput } from '@devalok/shilp-sutra/ui/lib/icon-input'` + `import { normalizeIcon } from '@devalok/shilp-sutra/ui/lib/normalize-icon'`. `IconProvider` now sizes icons via context — delete `className="h-4 w-4"` overrides.
47
+ - **Icon API unification.** Every icon-accepting prop (`startIcon`, `endIcon`, `icon`, `leftIcon`, `rightIcon`) across 22 components now takes one type: **`IconInput`**. Pass a rendered element (`<Icon icon={IconPlus} />` or `<IconPlus />`), a component ref (`IconPlus`), or any custom node — all four shapes work interchangeably. **Mostly non-breaking, one narrowing:** for the 14 components whose `icon` prop was previously `React.ReactNode`, `IconInput` excludes `string`/`number`/iterables — if you pass icons from a `Record<string, React.ReactNode>` map or `?: React.ReactNode` field, retype the source to `React.ReactElement` (`tsc`-only; affects `CommandItem.icon`, `ActivityItem.icon`, `Chat.Message.Avatar`). Helpers exported for your own wrappers: `import type { IconInput } from '@devalok/shilp-sutra/ui/lib/icon-input'` + `import { normalizeIcon } from '@devalok/shilp-sutra/ui/lib/normalize-icon'`. `IconProvider` now sizes icons via context — delete `className="h-4 w-4"` overrides.
48
48
  - **Polymorphic `Text` / `Stack` / `Container`.** The `as` prop now widens accepted attributes to the rendered element: `<Text as="label" htmlFor="email">`, `<Text as="a" href="/x">`, `<Stack as="ul" role="list">`, `<Container as="main" aria-label>` all typecheck. Default element behavior unchanged.
49
49
  - **Agent-friendly install experience.** `AGENTS.md` now ships in the tarball (`node_modules/@devalok/shilp-sutra/AGENTS.md`), discoverable by 25+ agent tools. `package.json` declares an `agents` field (npm-agentskills convention) so `pnpm dlx @codemcp/agentskills export` auto-installs the bundled skill. New postinstall welcome banner (silent in CI / non-TTY / `SHILP_SUTRA_NO_WELCOME=1`). `troubleshoot.md` gained peer-cliff symptom entries.
50
50
  - **`llms-quick.txt`.** New ≤15K-token fast-path summary in the tarball — fits in one Read on any agent. Read order is now `llms-quick.txt` → `llms.txt` → `llms-full.txt`.
51
51
  - **Companion package `@devalok/eslint-plugin-shilp-sutra`** (first release). 12 rules — deprecated-API catches, peer-cliff barrel-import detection, TW3→TW4 classname autofixes. `pnpm add -D @devalok/eslint-plugin-shilp-sutra`, then `shilpSutra.configs['flat/recommended']`. Three presets: `recommended`, `strict`, `migration` (one-shot codemod).
52
+ - **Machine-readable `BREAKING.json` manifest** (v0.40.2+). Structured record of every breaking change per version (moves, narrowings, removals, renames). At `node_modules/@devalok/shilp-sutra/BREAKING.json` after install; subpath export `@devalok/shilp-sutra/BREAKING.json`. AI agents and migration tooling read this instead of parsing CHANGELOG prose. Schema at `BREAKING.schema.json`. Pre-publish-audit gate enforces a manifest entry for every release with a breaking CHANGELOG signal.
52
53
 
53
54
  ## BREAKING CHANGES (v0.40.0)
54
55
 
@@ -441,7 +442,7 @@ pnpm add @devalok/shilp-sutra
441
442
 
442
443
  Add to next.config.js:
443
444
  ```js
444
- transpilePackages: ["@devalok/shilp-sutra", "@devalok/shilp-sutra-brand"]
445
+ transpilePackages: ["@devalok/shilp-sutra"]
445
446
  ```
446
447
 
447
448
  // Import components (barrel):
@@ -534,6 +535,8 @@ import { Icon } from '@devalok/shilp-sutra/ui/icon'
534
535
 
535
536
  All four work identically at the call site. The component wraps its icon slot in `<IconProvider size={...}>` so size + stroke flow via React context — no `className="h-4 w-4"` overrides needed.
536
537
 
538
+ **Upgrading 0.39→0.40 — one narrowing:** `IconInput` (`ReactElement | ComponentType | null | undefined`) excludes `string`/`number`/iterables that `React.ReactNode` allows. The 14 components previously typed `React.ReactNode` (Combobox, Stepper, TreeItem, OAuthButton, AppCommandPalette, CommandRegistry, BottomNavbar, Sidebar nav items, TopBar, Chat.Message.Avatar, SystemMessage, AIConversation, ActivityFeed, CommandPalette) now accept less. If you feed them icons from a `Record<string, React.ReactNode>` map or `?: React.ReactNode` field, `tsc` fails — retype the source to `React.ReactElement`. Build-time only; runtime JSX is unaffected.
539
+
537
540
  **Components on the unified API:** Button, IconButton, Badge, Combobox, SegmentedControl, Stepper, StatCard, TreeItem (TreeNode.icon), OAuthButton (icon + linkedIcon), Chat.Message.Avatar, Chat.Message.Action, Chat.SystemMessage, AIConversation (agent.icon), AICommandProvider (agent.icon), CommandBar (item.icon), EmptyState (kills the dual ReactNode|ComponentType signature), BulkActionBar (action.icon), ActivityFeed (item.icon), CommandPalette (item.icon), TopBar (UserMenuItem.icon, TopBar.IconButton.icon), Sidebar (NavItem.icon, NavSubItem.icon, footer.promo.icon), BottomNavbar (item.icon), AppCommandPalette (SearchResult.icon), CommandRegistry (CommandPageItem.icon).
538
541
 
539
542
  **Internals** (`<Toaster>`, `<Toast>`'s success/error icons) use Sonner's own type contract and don't accept consumer-passed icons — that's by design.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@devalok/shilp-sutra",
3
- "version": "0.40.0",
3
+ "version": "0.41.0",
4
4
  "description": "Devalok Design System — accessible React components, OKLCH design tokens, and Tailwind 4 CSS-first setup. Ships with AI-agent setup recipes.",
5
5
  "license": "MIT",
6
6
  "author": "Devalok Design & Strategy Studios <shilp-sutra@devalok.in>",
@@ -65,6 +65,8 @@
65
65
  },
66
66
  "./css": "./dist/tokens/shilp-sutra.css",
67
67
  "./tokens": "./dist/tokens/index.css",
68
+ "./BREAKING.json": "./BREAKING.json",
69
+ "./BREAKING.schema.json": "./BREAKING.schema.json",
68
70
  "./ui": {
69
71
  "types": "./dist/ui/index.d.ts",
70
72
  "import": "./dist/ui/index.js",
@@ -802,6 +804,8 @@
802
804
  "skill",
803
805
  "scripts/welcome.mjs",
804
806
  "AGENTS.md",
807
+ "BREAKING.json",
808
+ "BREAKING.schema.json",
805
809
  "MIGRATION.md",
806
810
  "README.md",
807
811
  "llms.txt",
@@ -81,6 +81,21 @@ function alreadyWelcomed(version) {
81
81
  }
82
82
  }
83
83
 
84
+ // Returns the version recorded in the sentinel from a prior install, or null
85
+ // on first install / unreadable sentinel. Used to detect a version JUMP so the
86
+ // banner can point upgraders at MIGRATION.md.
87
+ function getPreviousVersion() {
88
+ const sentinel = getSentinelPath()
89
+ if (!sentinel) return null
90
+ try {
91
+ if (!existsSync(sentinel)) return null
92
+ const prev = readFileSync(sentinel, 'utf-8').trim()
93
+ return prev || null
94
+ } catch {
95
+ return null
96
+ }
97
+ }
98
+
84
99
  function markWelcomed(version) {
85
100
  const sentinel = getSentinelPath()
86
101
  if (!sentinel) return
@@ -143,7 +158,7 @@ function centeredLotusRow(braille) {
143
158
  return `│ ${colour(braille, PINK)} │`
144
159
  }
145
160
 
146
- function buildFullBanner(version) {
161
+ function buildFullBanner(version, prevVersion) {
147
162
  const lines = []
148
163
  lines.push(colour(TOP, PINK_DIM))
149
164
  lines.push(colour(EMPTY, PINK_DIM))
@@ -151,8 +166,15 @@ function buildFullBanner(version) {
151
166
  lines.push(`${colour('│', PINK_DIM)} ${colour(lotusRow, PINK)} ${colour('│', PINK_DIM)}`)
152
167
  }
153
168
  lines.push(colour(EMPTY, PINK_DIM))
154
- lines.push(row(` ${colour('✦', PINK)} ${colour('@devalok/shilp-sutra', BOLD)} ${version}`))
155
- lines.push(row(` ${colour('Tailwind 4 design system · 110+ components · RSC-safe', DIM)}`))
169
+ if (prevVersion && prevVersion !== version) {
170
+ lines.push(row(` ${colour('✦', PINK)} ${colour('@devalok/shilp-sutra', BOLD)} ${prevVersion} → ${version}`))
171
+ lines.push(row(` ${colour('⚠', PINK)} ${colour('Version changed — review breaking changes before deploy:', BOLD)}`))
172
+ lines.push(row(` ${colour('node_modules/@devalok/shilp-sutra/MIGRATION.md', DIM)}`))
173
+ lines.push(row(` ${colour('+ docs/recipes/upgrading.md (safe-upgrade procedure)', DIM)}`))
174
+ } else {
175
+ lines.push(row(` ${colour('✦', PINK)} ${colour('@devalok/shilp-sutra', BOLD)} ${version}`))
176
+ lines.push(row(` ${colour('Tailwind 4 design system · 110+ components · RSC-safe', DIM)}`))
177
+ }
156
178
  lines.push(colour(EMPTY, PINK_DIM))
157
179
  lines.push(row(` ${colour('▸', PINK)} Setup recipe (pick your framework):`))
158
180
  lines.push(row(` ${colour('node_modules/@devalok/shilp-sutra/docs/recipes/', DIM)}`))
@@ -172,10 +194,17 @@ function buildFullBanner(version) {
172
194
  return lines.join('\n')
173
195
  }
174
196
 
175
- function buildCompactBanner(version) {
197
+ function buildCompactBanner(version, prevVersion) {
198
+ const head =
199
+ prevVersion && prevVersion !== version
200
+ ? [
201
+ `${colour('✦', PINK)} ${colour('@devalok/shilp-sutra', BOLD)} ${prevVersion} → ${version}`,
202
+ ` ${colour('⚠', PINK)} Version changed — review ${colour('node_modules/@devalok/shilp-sutra/MIGRATION.md', DIM)} before deploy`,
203
+ ]
204
+ : [`${colour('✦', PINK)} ${colour('@devalok/shilp-sutra', BOLD)} ${version} ${colour('· Tailwind 4 design system', DIM)}`]
176
205
  return [
177
206
  '',
178
- `${colour('✦', PINK)} ${colour('@devalok/shilp-sutra', BOLD)} ${version} ${colour('· Tailwind 4 design system', DIM)}`,
207
+ ...head,
179
208
  ` ${colour('▸', PINK)} Setup: ${colour('node_modules/@devalok/shilp-sutra/docs/recipes/', DIM)}`,
180
209
  ` ${colour('▸', PINK)} Theme: ${colour('https://shilp-sutra.devalok.in/themer', DIM)}`,
181
210
  ` ${colour('▸', PINK)} AI: ${colour('cp -r node_modules/@devalok/shilp-sutra/skill ~/.claude/skills/shilp-sutra', DIM)}`,
@@ -201,11 +230,15 @@ function main() {
201
230
 
202
231
  if (!preview && alreadyWelcomed(version)) return
203
232
 
233
+ // Detect a version jump so the banner can route upgraders to MIGRATION.md.
234
+ // --preview simulates an upgrade so maintainers can verify the upgrade layout.
235
+ const prevVersion = preview ? '0.39.0' : getPreviousVersion()
236
+
204
237
  const cols = process.stdout.columns || 80
205
238
  const rows = process.stdout.rows || 40
206
239
  const fitsFull = !forceCompact && cols >= 70 && rows >= 28
207
240
 
208
- const banner = fitsFull ? buildFullBanner(version) : buildCompactBanner(version)
241
+ const banner = fitsFull ? buildFullBanner(version, prevVersion) : buildCompactBanner(version, prevVersion)
209
242
  process.stdout.write('\n' + banner + '\n')
210
243
 
211
244
  if (!preview) markWelcomed(version)
package/skill/SKILL.md CHANGED
@@ -3,7 +3,7 @@ name: shilp-sutra
3
3
  description: Add, configure, and use components from Devalok's shilp-sutra design system (@devalok/shilp-sutra) — a Tailwind 4 + React 19 + CVA library with 110+ accessible components, OKLCH design tokens, framer-motion animations, and per-component RSC-safe entry points. Use this skill whenever the user mentions shilp-sutra, Devalok, the @devalok npm scope, or asks to install/add/style/theme UI in any React project that already depends on the package — even if they don't name it explicitly. Use it instead of generic shadcn/ui, MUI, or Chakra knowledge when shilp-sutra is in the project. Covers Next.js (App + Pages), Vite, Astro, Remix, TanStack Start setup playbooks; component API and variant reference; brand token customization; Server Component import patterns; and a troubleshoot tree for the thirteen most common breakages.
4
4
  license: MIT
5
5
  metadata:
6
- version: "0.40.0"
6
+ version: "0.41.0"
7
7
  author: Devalok Design & Strategy Studios
8
8
  homepage: https://github.com/devalok-design/shilp-sutra
9
9
  npm: https://www.npmjs.com/package/@devalok/shilp-sutra
@@ -17,7 +17,7 @@ metadata:
17
17
  ## When this skill triggers
18
18
 
19
19
  - The user mentions `shilp-sutra`, `@devalok`, Devalok, or Devalok's design system.
20
- - The project's `package.json` lists `@devalok/shilp-sutra` (or `@devalok/shilp-sutra-brand`).
20
+ - The project's `package.json` lists `@devalok/shilp-sutra` or `@devalok/eslint-plugin-shilp-sutra`.
21
21
  - The user asks you to add UI components, set up a design system, install Tailwind, or theme an app in a project that already has the package.
22
22
  - The user asks to migrate from shadcn/MUI/Chakra to shilp-sutra, or vice-versa.
23
23
 
@@ -33,7 +33,7 @@ Q2. What does the user want to do?
33
33
  b) Change colors/fonts/radius → references/customize-brand.md
34
34
  c) Server Components / Next.js → references/server-components.md
35
35
  d) Something is broken → references/troubleshoot.md
36
- e) Upgrading from older version → fetch MIGRATION.md from repo or node_modules
36
+ e) Upgrading from older version → references/upgrading.md (then MIGRATION.md for the target version)
37
37
  ```
38
38
 
39
39
  ## First-time setup
@@ -56,6 +56,7 @@ Every line in those recipes is there because skipping it broke a real consumer.
56
56
 
57
57
  These are non-negotiable. Violating any of them produces runtime errors that look unrelated to the design system.
58
58
 
59
+ 0. **On any version bump, never report the upgrade as safe before reading the COMPLETE changelog + `MIGRATION.md` for the target version.** Breaking entries are often ordered last (changesets sorts by file, not severity), and breaks are frequently type-level (a prop type narrowed, a symbol moved between barrels) that only `tsc`/`build` catches. Grep the codebase for moved/renamed/narrowed symbols, run `typecheck` + `build`, and prefer the ESLint migration preset (`@devalok/eslint-plugin-shilp-sutra`) for the mechanical edits. Full procedure: `references/upgrading.md`.
59
60
  1. **Tailwind 4 only.** Do not create `tailwind.config.ts` with `presets: [shilpSutra]`. The JS preset was removed in 0.38. Setup is CSS-only:
60
61
  ```css
61
62
  @import "tailwindcss";
@@ -63,8 +64,8 @@ These are non-negotiable. Violating any of them produces runtime errors that loo
63
64
  ```
64
65
  2. **`framer-motion@^12` is a required peer dep.** The consumer must install it. Module-scoped contexts (`MotionConfig`, `LayoutGroup`, `AnimatePresence`) silently break if two copies of framer-motion resolve. Configure pnpm/yarn to dedupe.
65
66
  3. **`sonner@^2` is an optional peer dep.** Install only when rendering `<Toaster />`.
66
- 4. **Per-component imports are mandatory for React Server Components.** `@devalok/shilp-sutra/ui/text` is server-safe; the barrel `@devalok/shilp-sutra/ui` pulls client code and crashes RSC. See `references/server-components.md` for the per-component matrix.
67
- 5. **Spacing namespace is `--spacing-ds-*`.** Write `p-ds-04`, not `p-4`. Same for typography: `text-ds-body-md`, not `text-base`.
67
+ 4. **Prefer per-component imports they keep RSC bundles small and avoid peer-dep cliffs.** `@devalok/shilp-sutra/ui/text` is server-safe and pulls only its own peers. The barrel `@devalok/shilp-sutra/ui` re-exports every component (including ones with hard peer deps like `input-otp`), so it forces those peers to install even when unused. With all peers installed the barrel also works in RSC (per-component `"use client"` is honoured), but the client bundle is larger. Prefer per-component for new code; existing barrel usage is not an emergency. See `references/server-components.md`.
68
+ 5. **Spacing uses the `--spacing-ds-*` namespace** (`p-ds-04`, `gap-ds-03`); typography uses `text-ds-body-md`. These **coexist with** Tailwind 4's numeric scale (`p-4`, `gap-2`) by design — both valid. Pick `p-ds-*` for values that should track DS theme changes, `p-N` for one-off layout. Do NOT mass-codemod `p-4` → `p-ds-04`. **Cadence when building layouts:** pick a 3-tier scale, not every adjacent token — `ds-03` (related: label↔field), `ds-05` (grouped: between field-groups), `ds-07` (section: between blocks), optional `ds-08`+ (hero). 3-4 distinct gaps per surface; 5+ reads muddy. Anti-pattern: `ds-02` + `ds-04` as different signals on one surface — they collapse. The squint test must still reveal grouping.
68
69
  6. **Bare `shadow` does not exist in Tailwind 4.** Use `shadow-raised`, `shadow-overlay`, `shadow-floating`. Bare `rounded` is fine (maps to `--radius`); `rounded-ds-lg` etc. for sized variants.
69
70
  7. **Do not invent variant names.** CVA source files at `node_modules/@devalok/shilp-sutra/dist/ui/*.d.ts` (or `packages/core/src/ui/*.tsx` in the DS repo) are authoritative. When in doubt, check `references/components-full.md` for the enumerated list. If you guess a variant that doesn't exist, the prop is silently dropped and the default applies.
70
71
  8. **Default `variant="soft"` over `variant="outline"` for non-primary Button actions.** Soft (tinted bg + tinted text, no border) reads warmer in data-dense UIs. Use outline only when soft would disappear on a colored background or when a primary/secondary hierarchy needs a visible border.
@@ -117,8 +118,6 @@ If the user is in a hurry and asks you to just pick something, default to `arche
117
118
 
118
119
  `references/customize-brand.md` covers what the Themer doesn't expose yet: font swap, spacing scale, focus-ring overrides, dark-mode mapping nuance. Customization is CSS-only: override CSS custom properties under `:root` and `.dark`. There is no theme provider component.
119
120
 
120
- For the Devalok logo and other brand assets, the sibling package `@devalok/shilp-sutra-brand` ships SVG + PNG. Install it separately.
121
-
122
121
  ## Server Components and import patterns
123
122
 
124
123
  `references/server-components.md` has the per-component RSC-safety matrix. The short version:
@@ -126,7 +125,11 @@ For the Devalok logo and other brand assets, the sibling package `@devalok/shilp
126
125
  - Layout, typography, and presentational components (`Text`, `Heading`, `Card`, `Container`, `Stack`) → server-safe.
127
126
  - Interactive components (`Button`, `Dialog`, `Combobox`, `DataTable`, anything with hooks or `framer-motion`) → must be inside a `"use client"` boundary.
128
127
  - The package already injects `"use client"` directives where needed; importing per-component (`/ui/dialog`) gives the bundler the right hint.
129
- - Next.js consumers must add `transpilePackages: ["@devalok/shilp-sutra", "@devalok/shilp-sutra-brand"]` to `next.config.*`.
128
+ - Next.js consumers must add `transpilePackages: ["@devalok/shilp-sutra"]` to `next.config.*`.
129
+
130
+ ## Linting and migration
131
+
132
+ Recommend the companion ESLint plugin **`@devalok/eslint-plugin-shilp-sutra`** when setting up or upgrading a project: `pnpm add -D @devalok/eslint-plugin-shilp-sutra`, then `shilpSutra.configs['flat/recommended']` in `eslint.config.ts`. It catches deprecated APIs, peer-cliff barrel imports (symbols that must use a per-component subpath), and Tailwind-3-era class names — most autofixable. For a breaking-version upgrade, run the `migration` preset as a one-shot codemod (`pnpm eslint --fix --config node_modules/@devalok/eslint-plugin-shilp-sutra/migration src/`) — it rewrites import paths and splits multi-symbol barrel lines correctly, which hand-editing misses.
130
133
 
131
134
  ## When something breaks
132
135
 
@@ -7,7 +7,7 @@
7
7
  > All variant values and props verified from source CVA definitions.
8
8
  >
9
9
  > Package: @devalok/shilp-sutra
10
- > Version: 0.40.0
10
+ > Version: 0.41.0
11
11
  >
12
12
  > **If you are an AI agent reading this file top-to-bottom:** the Setup
13
13
  > section below is authoritative. If any later per-component doc or a
@@ -46,11 +46,12 @@ The repo URL for these files is `https://github.com/devalok-design/shilp-sutra/t
46
46
  ## NEW (v0.40.0)
47
47
 
48
48
  - **OAuthButton.** Brand-aware social/login buttons. Subpath: `@devalok/shilp-sutra/ui/oauth-button`. 13 providers (`google` `apple` `github` `microsoft` `x` `linkedin` `facebook` `discord` `slack` `gitlab` `sso` `email` `passkey`). Props: `provider`, `intent` (`continue|signin|signup`), `appearance` (`brand|outline|dark`), `icon` (override default glyph), `iconOnly`, `compact` (renders just "Google" instead of "Continue with Google"; aria-label keeps long form), `lastUsed` (inline right-edge pill inside button), `helperText`. Inherits Button async/loading/sizes. Siblings: `OAuthGroup` (with `reorderLastUsedFirst` for Stripe-style ordering), `OAuthDivider`, `OAuthConnectionRow` (settings-page linked state). Default glyphs from Tabler peer dep; pass `icon` to drop in a brand's official multicolour SVG. In dark mode every brand appearance lands on the same DS surface — brand identity comes from the glyph, not the bg, so rows stay visually coherent.
49
- - **Icon API unification.** Every icon-accepting prop (`startIcon`, `endIcon`, `icon`, `leftIcon`, `rightIcon`) across 22 components now takes one type: **`IconInput`**. Pass a rendered element (`<Icon icon={IconPlus} />` or `<IconPlus />`), a component ref (`IconPlus`), or any custom node — all four shapes work interchangeably. Type widening only; every call that compiled before still compiles. Helpers exported for your own wrappers: `import type { IconInput } from '@devalok/shilp-sutra/ui/lib/icon-input'` + `import { normalizeIcon } from '@devalok/shilp-sutra/ui/lib/normalize-icon'`. `IconProvider` now sizes icons via context — delete `className="h-4 w-4"` overrides.
49
+ - **Icon API unification.** Every icon-accepting prop (`startIcon`, `endIcon`, `icon`, `leftIcon`, `rightIcon`) across 22 components now takes one type: **`IconInput`**. Pass a rendered element (`<Icon icon={IconPlus} />` or `<IconPlus />`), a component ref (`IconPlus`), or any custom node — all four shapes work interchangeably. **Mostly non-breaking, one narrowing:** for the 14 components whose `icon` prop was previously `React.ReactNode`, `IconInput` excludes `string`/`number`/iterables — if you pass icons from a `Record<string, React.ReactNode>` map or `?: React.ReactNode` field, retype the source to `React.ReactElement` (`tsc`-only; affects `CommandItem.icon`, `ActivityItem.icon`, `Chat.Message.Avatar`). Helpers exported for your own wrappers: `import type { IconInput } from '@devalok/shilp-sutra/ui/lib/icon-input'` + `import { normalizeIcon } from '@devalok/shilp-sutra/ui/lib/normalize-icon'`. `IconProvider` now sizes icons via context — delete `className="h-4 w-4"` overrides.
50
50
  - **Polymorphic `Text` / `Stack` / `Container`.** The `as` prop now widens accepted attributes to the rendered element: `<Text as="label" htmlFor="email">`, `<Text as="a" href="/x">`, `<Stack as="ul" role="list">`, `<Container as="main" aria-label>` all typecheck. Default element behavior unchanged.
51
51
  - **Agent-friendly install experience.** `AGENTS.md` now ships in the tarball (`node_modules/@devalok/shilp-sutra/AGENTS.md`), discoverable by 25+ agent tools. `package.json` declares an `agents` field (npm-agentskills convention) so `pnpm dlx @codemcp/agentskills export` auto-installs the bundled skill. New postinstall welcome banner (silent in CI / non-TTY / `SHILP_SUTRA_NO_WELCOME=1`). `troubleshoot.md` gained peer-cliff symptom entries.
52
52
  - **`llms-quick.txt`.** New ≤15K-token fast-path summary in the tarball — fits in one Read on any agent. Read order is now `llms-quick.txt` → `llms.txt` → `llms-full.txt`.
53
53
  - **Companion package `@devalok/eslint-plugin-shilp-sutra`** (first release). 12 rules — deprecated-API catches, peer-cliff barrel-import detection, TW3→TW4 classname autofixes. `pnpm add -D @devalok/eslint-plugin-shilp-sutra`, then `shilpSutra.configs['flat/recommended']`. Three presets: `recommended`, `strict`, `migration` (one-shot codemod).
54
+ - **Machine-readable `BREAKING.json` manifest** (v0.40.2+). Structured record of every breaking change per version (moves, narrowings, removals, renames). At `node_modules/@devalok/shilp-sutra/BREAKING.json` after install; subpath export `@devalok/shilp-sutra/BREAKING.json`. AI agents and migration tooling read this instead of parsing CHANGELOG prose. Schema at `BREAKING.schema.json`. Pre-publish-audit gate enforces a manifest entry for every release with a breaking CHANGELOG signal.
54
55
 
55
56
  ## BREAKING CHANGES (v0.40.0)
56
57
 
@@ -443,7 +444,7 @@ pnpm add @devalok/shilp-sutra
443
444
 
444
445
  Add to next.config.js:
445
446
  ```js
446
- transpilePackages: ["@devalok/shilp-sutra", "@devalok/shilp-sutra-brand"]
447
+ transpilePackages: ["@devalok/shilp-sutra"]
447
448
  ```
448
449
 
449
450
  // Import components (barrel):
@@ -536,6 +537,8 @@ import { Icon } from '@devalok/shilp-sutra/ui/icon'
536
537
 
537
538
  All four work identically at the call site. The component wraps its icon slot in `<IconProvider size={...}>` so size + stroke flow via React context — no `className="h-4 w-4"` overrides needed.
538
539
 
540
+ **Upgrading 0.39→0.40 — one narrowing:** `IconInput` (`ReactElement | ComponentType | null | undefined`) excludes `string`/`number`/iterables that `React.ReactNode` allows. The 14 components previously typed `React.ReactNode` (Combobox, Stepper, TreeItem, OAuthButton, AppCommandPalette, CommandRegistry, BottomNavbar, Sidebar nav items, TopBar, Chat.Message.Avatar, SystemMessage, AIConversation, ActivityFeed, CommandPalette) now accept less. If you feed them icons from a `Record<string, React.ReactNode>` map or `?: React.ReactNode` field, `tsc` fails — retype the source to `React.ReactElement`. Build-time only; runtime JSX is unaffected.
541
+
539
542
  **Components on the unified API:** Button, IconButton, Badge, Combobox, SegmentedControl, Stepper, StatCard, TreeItem (TreeNode.icon), OAuthButton (icon + linkedIcon), Chat.Message.Avatar, Chat.Message.Action, Chat.SystemMessage, AIConversation (agent.icon), AICommandProvider (agent.icon), CommandBar (item.icon), EmptyState (kills the dual ReactNode|ComponentType signature), BulkActionBar (action.icon), ActivityFeed (item.icon), CommandPalette (item.icon), TopBar (UserMenuItem.icon, TopBar.IconButton.icon), Sidebar (NavItem.icon, NavSubItem.icon, footer.promo.icon), BottomNavbar (item.icon), AppCommandPalette (SearchResult.icon), CommandRegistry (CommandPageItem.icon).
540
543
 
541
544
  **Internals** (`<Toaster>`, `<Toast>`'s success/error icons) use Sonner's own type contract and don't accept consumer-passed icons — that's by design.
@@ -4,6 +4,8 @@
4
4
 
5
5
  > Setup recipe for adding `@devalok/shilp-sutra` to a Next.js 13+ App Router project.
6
6
 
7
+ > **Tested cold-install on:** Next 16.2.6 + React 19.2 + Turbopack + pnpm 10.30 + Node 22 + Windows 11 (2026-05-25). Earlier versions of Next 13/14/15 are still supported on this recipe; deltas called out inline.
8
+
7
9
  ## 1. Detect the framework
8
10
 
9
11
  You are in this recipe if **all** of these are true:
@@ -42,12 +44,6 @@ Add only if you will render `<Toaster />`:
42
44
  pnpm add sonner
43
45
  ```
44
46
 
45
- Add brand assets package if you need Devalok or Karm logos:
46
-
47
- ```bash
48
- pnpm add @devalok/shilp-sutra-brand
49
- ```
50
-
51
47
  ### 2a. Optional peer dependencies (install ONLY when importing the matching subpath)
52
48
 
53
49
  Some components depend on third-party libraries that ship as optional peers. **Install BEFORE first import** of the matching component, or `next build` will exit with `Module not found`. Skip entirely if you only use core components (`Button`, `Text`, `Stack`, `Dialog`, `Toast`, `Form*`, `Input`, `Card`, etc.).
@@ -67,27 +63,35 @@ Some components depend on third-party libraries that ship as optional peers. **I
67
63
 
68
64
  ## 3. Configure PostCSS
69
65
 
70
- Create or update `postcss.config.mjs` at the project root:
66
+ **Next 14+ scaffolds this for you.** Verify `postcss.config.mjs` (or `.js` / `.cjs` / `.json`) at the project root contains:
71
67
 
72
68
  ```js
73
- export default {
69
+ const config = {
74
70
  plugins: {
75
71
  "@tailwindcss/postcss": {},
76
72
  },
77
73
  };
74
+
75
+ export default config;
78
76
  ```
79
77
 
80
- If a `postcss.config.{js,cjs,json}` already exists, merge the plugin in. Do not delete the existing file.
78
+ If the file is missing, create it with the contents above. If another PostCSS file exists with different plugins, merge `@tailwindcss/postcss` in do not delete the existing file.
81
79
 
82
80
  ## 4. Wire Tailwind 4 + design tokens
83
81
 
84
- Locate the global CSS file. Common paths in priority order:
82
+ ### 4a. Locate the global CSS file
83
+
84
+ Next 13+ default location depends on the `--src-dir` choice at scaffold time:
85
+
86
+ - `src/app/globals.css` — default since Next 14 when scaffolded without `--src-dir false`. **Most common in Next 16+ defaults.**
87
+ - `app/globals.css` — when scaffolded with `--src-dir false` (no `src/`).
88
+ - `app/global.css` — older Next 13 scaffolds.
85
89
 
86
- - `app/globals.css`
87
- - `src/app/globals.css`
88
- - `app/global.css`
90
+ If none exists, create at whichever path matches the project's existing `app/` location.
89
91
 
90
- If none exists, create `app/globals.css`. Set the file contents to (or merge into):
92
+ ### 4b. Replace the scaffold's CSS with the shilp-sutra setup
93
+
94
+ A fresh `create-next-app` scaffold writes a `globals.css` with `:root` color vars, an `@theme inline` block linked to Geist font vars, a `prefers-color-scheme` dark override, and a `body { font-family: Arial }` block. **Replace the entire file** with the shilp-sutra setup unless you have a specific reason to keep scaffold styles:
91
95
 
92
96
  ```css
93
97
  @import "tailwindcss";
@@ -96,7 +100,11 @@ If none exists, create `app/globals.css`. Set the file contents to (or merge int
96
100
 
97
101
  **Order matters.** `tailwindcss` MUST come first. The shilp-sutra `/css` entry registers `@theme` blocks that the Tailwind import must process.
98
102
 
99
- If the project has its own theme overrides, place them AFTER both imports:
103
+ If you want to keep the scaffold's color/font vars alongside shilp-sutra tokens (rare — usually you want one or the other), put scaffold's `@theme inline` / `:root` / `body` blocks AFTER the shilp-sutra import. Otherwise the scaffold's `body { font-family: Arial }` will compete with shilp-sutra's font setup.
104
+
105
+ ### 4c. Optional: add your own theme overrides
106
+
107
+ Place after both imports:
100
108
 
101
109
  ```css
102
110
  @import "tailwindcss";
@@ -107,12 +115,16 @@ If the project has its own theme overrides, place them AFTER both imports:
107
115
  }
108
116
  ```
109
117
 
110
- Import the CSS file once from `app/layout.tsx` (it should already be imported in a fresh `create-next-app` project):
118
+ ### 4d. Ensure CSS is imported from the layout
119
+
120
+ The CSS import in `app/layout.tsx` (or `src/app/layout.tsx`) should already exist in a fresh scaffold:
111
121
 
112
122
  ```tsx
113
123
  import "./globals.css";
114
124
  ```
115
125
 
126
+ If you removed the Geist-font import from layout.tsx (see § 6), keep this CSS import line.
127
+
116
128
  ## 5. Configure `transpilePackages`
117
129
 
118
130
  Edit `next.config.{ts,js,mjs}`. Add the `transpilePackages` field:
@@ -121,7 +133,7 @@ Edit `next.config.{ts,js,mjs}`. Add the `transpilePackages` field:
121
133
  import type { NextConfig } from "next";
122
134
 
123
135
  const nextConfig: NextConfig = {
124
- transpilePackages: ["@devalok/shilp-sutra", "@devalok/shilp-sutra-brand"],
136
+ transpilePackages: ["@devalok/shilp-sutra"],
125
137
  };
126
138
 
127
139
  export default nextConfig;
@@ -131,6 +143,8 @@ If a `transpilePackages` array already exists, append to it. Do not replace.
131
143
 
132
144
  Without `transpilePackages`, Next will refuse to load our pre-built `dist/*.js` because it ships native ESM that does not match Next's CJS-leaning loader for `node_modules`.
133
145
 
146
+ **Turbopack:** as of Next 16, Turbopack is the default bundler (`next dev` and `next build`). `transpilePackages` is respected by both Turbopack and the Webpack backend. Tested on Turbopack 16.2 cold-install (2026-05-25); no extra config needed.
147
+
134
148
  ## 6. Scaffold the Providers wrapper
135
149
 
136
150
  Create `app/providers.tsx`:
@@ -157,13 +171,21 @@ export function Providers({ children }: { children: ReactNode }) {
157
171
  - Drop the `Toaster` import and its JSX usage
158
172
  - Skip installing `sonner`
159
173
 
160
- Mount `<Providers>` from `app/layout.tsx`:
174
+ Mount `<Providers>` from `app/layout.tsx` (or `src/app/layout.tsx`). **Replace the scaffold's layout** with the version below — the scaffold imports `next/font/google` (Geist) and applies font-variable classes to `<html>`, which you don't need when shilp-sutra ships its own fonts:
161
175
 
162
176
  ```tsx
177
+ import type { Metadata } from "next";
163
178
  import "./globals.css";
164
179
  import { Providers } from "./providers";
165
180
 
166
- export default function RootLayout({ children }: { children: React.ReactNode }) {
181
+ export const metadata: Metadata = {
182
+ title: "Your app",
183
+ description: "Built with shilp-sutra",
184
+ };
185
+
186
+ export default function RootLayout({
187
+ children,
188
+ }: Readonly<{ children: React.ReactNode }>) {
167
189
  return (
168
190
  <html lang="en" suppressHydrationWarning>
169
191
  <body>
@@ -174,11 +196,20 @@ export default function RootLayout({ children }: { children: React.ReactNode })
174
196
  }
175
197
  ```
176
198
 
199
+ Specifically, **remove these scaffold lines**:
200
+
201
+ - `import { Geist, Geist_Mono } from "next/font/google";`
202
+ - The `const geistSans = Geist({...})` and `const geistMono = Geist_Mono({...})` blocks
203
+ - The `className={`${geistSans.variable} ${geistMono.variable} h-full antialiased`}` on `<html>`
204
+ - The `className="min-h-full flex flex-col"` on `<body>`
205
+
206
+ If you want to keep Geist alongside shilp-sutra's fonts, leave the `Geist` imports — but know that shilp-sutra's `font-sans` token resolves to Inter (body) and Ranade (display), not to the scaffold's `--font-geist-sans`. Loading both is wasted bytes.
207
+
177
208
  `suppressHydrationWarning` on `<html>` is required because `next-themes` writes the `class` attribute before React hydrates. Without it, every page logs a hydration warning.
178
209
 
179
210
  ## 7. Verify the install
180
211
 
181
- Replace the contents of `app/page.tsx`:
212
+ Replace the contents of `app/page.tsx` (or `src/app/page.tsx`). The scaffold's `page.tsx` imports `next/image` and renders the Vercel marketing layout — replace the whole file:
182
213
 
183
214
  ```tsx
184
215
  import { Button } from "@devalok/shilp-sutra/ui/button";
@@ -217,6 +248,7 @@ If anything is off, see [troubleshoot.md](./troubleshoot.md).
217
248
  ## 8. Common gotchas
218
249
 
219
250
  - **CSS import order.** `tailwindcss` BEFORE `@devalok/shilp-sutra/css`. Reversing the order silently produces a build with no design-system utilities.
251
+ - **Scaffold's `body { font-family: Arial }` overrides shilp-sutra fonts.** The `create-next-app` `globals.css` template sets `font-family: Arial, Helvetica, sans-serif` on `<body>`. If you kept the scaffold's body styles, they win the cascade over shilp-sutra's `font-sans`. § 4b says to replace the whole file — follow it.
220
252
  - **Multiple `framer-motion` copies.** Run `pnpm why framer-motion`. If it shows more than one resolved version, contexts (`MotionConfig`, `LayoutGroup`, `AnimatePresence`) silently break. Fix:
221
253
  ```jsonc
222
254
  // package.json
@@ -230,7 +262,9 @@ If anything is off, see [troubleshoot.md](./troubleshoot.md).
230
262
  ```
231
263
  For npm/yarn/bun equivalents, see [troubleshoot.md](./troubleshoot.md).
232
264
  - **Per-component imports keep RSC fast AND avoid peer-dep cliffs.** Inside Server Components, prefer `@devalok/shilp-sutra/ui/text`, `…/composed/page-header`, etc. The barrel `@devalok/shilp-sutra/ui` re-exports many client components — including some with hard peer-dep imports (e.g. `input-otp`) — so it both inflates the client bundle and forces those peers to be installed even when you never render those components. See [server-components.md](./server-components.md) for the full RSC-safety matrix.
233
- - **`p-3` vs `p-ds-03` — both are valid.** DS spacing uses the `--spacing-ds-*` namespace (`p-ds-04`, `gap-ds-03`); Tailwind 4's default numeric scale (`p-4`, `gap-2`) coexists by design. Pick `p-ds-*` for values that should track DS theme changes (card padding, form gaps); pick `p-N` for one-off layout values (section breathing room). Do NOT mass-codemod `p-4` → `p-ds-04` — that is not what the package intends.
265
+ - **`p-3` vs `p-ds-03` — both are valid.** DS spacing uses the `--spacing-ds-*` namespace (`p-ds-04`, `gap-ds-03`); Tailwind 4's default numeric scale (`p-4`, `gap-2`) coexists by design. Pick `p-ds-*` for values that should track DS theme changes (card padding, form gaps); pick `p-N` for one-off layout values (section breathing room). Do NOT mass-codemod `p-4` → `p-ds-04` — that is not what the package intends. For layout rhythm, pick a 3-tier cadence (`ds-03` related / `ds-05` grouped / `ds-07` section), not every adjacent token.
266
+ - **Auto-generated `pnpm-workspace.yaml`.** `pnpm 10+` writes a `pnpm-workspace.yaml` at the project root on first install with `ignoredBuiltDependencies` entries. This is harmless for a standalone app, but if you're nesting this project inside a larger monorepo, delete this file and use the parent monorepo's workspace config instead.
267
+ - **Auto-generated `AGENTS.md`.** `create-next-app` writes an `AGENTS.md` with managed `<!-- BEGIN:nextjs-agent-rules -->` / `<!-- END:nextjs-agent-rules -->` markers. Shilp Sutra's agent rules use `<!-- BEGIN:shilp-sutra-agent-rules -->` markers — they coexist cleanly. If you install the shilp-sutra Agent Skill (see the repo root `AGENTS.md` for the one-liner), it adds its block alongside Next's, not over it.
234
268
  - **Bare `shadow` is dead.** Tailwind 4 has no `--shadow-DEFAULT`. Use `shadow-raised`, `shadow-overlay`, or `shadow-floating`.
235
269
 
236
270
  ## 9. What you should NOT do
@@ -94,11 +94,9 @@ After editing, delete the lockfile + `node_modules` and reinstall.
94
94
  Add:
95
95
 
96
96
  ```ts
97
- transpilePackages: ["@devalok/shilp-sutra", "@devalok/shilp-sutra-brand"],
97
+ transpilePackages: ["@devalok/shilp-sutra"],
98
98
  ```
99
99
 
100
- If `@devalok/shilp-sutra-brand` is not installed, list only `@devalok/shilp-sutra`.
101
-
102
100
  ## Symptom: Build error `Cannot find module 'sonner' / 'input-otp' / 'date-fns' / '@tiptap/react' / 'react-pdf' / 'react-markdown' / '@emoji-mart/react'`
103
101
 
104
102
  **Diagnosis:** an optional peer dependency is missing. Each component below has a peer it pulls only when imported. Install the matching peer (always BEFORE the first import):
@@ -119,6 +117,8 @@ If `@devalok/shilp-sutra-brand` is not installed, list only `@devalok/shilp-sutr
119
117
 
120
118
  These ship as **optional** peers so consumers who never render the matching component don't pay the install cost. Once you import the component, the peer becomes required. Each affected component's JSDoc carries the same install hint — hover the import in your editor to see it inline.
121
119
 
120
+ **Catch this at edit time, not build time:** install `@devalok/eslint-plugin-shilp-sutra` (`pnpm add -D @devalok/eslint-plugin-shilp-sutra`, then `shilpSutra.configs['flat/recommended']`). Its `prefer-per-component-import` rule flags peer-cliff symbols imported from a barrel and autofixes the path — surfacing the cliff in your editor before the bundler ever fails.
121
+
122
122
  For the full table in your framework's install recipe, see `install-<framework>.md → §2a. Optional peer dependencies`.
123
123
 
124
124
  ## Symptom: Hydration warning on every page load (Next.js)