@devalok/shilp-sutra 0.45.1 → 0.47.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.
Files changed (62) hide show
  1. package/AGENTS.md +13 -1
  2. package/MIGRATION.md +846 -808
  3. package/dist/_chunks/chat.js +6 -6
  4. package/dist/_chunks/chat.js.map +1 -1
  5. package/dist/_chunks/document-preview.js +5 -5
  6. package/dist/_chunks/image-preview.js +7 -7
  7. package/dist/_chunks/success.js +16 -16
  8. package/dist/composed/activity-feed.js +19 -19
  9. package/dist/composed/deadline-indicator.d.ts.map +1 -1
  10. package/dist/composed/deadline-indicator.js +31 -31
  11. package/dist/composed/deadline-indicator.js.map +1 -1
  12. package/dist/composed/emoji-picker.js +15 -15
  13. package/dist/composed/file-preview.js +18 -18
  14. package/dist/composed/file-preview.js.map +1 -1
  15. package/dist/composed/filter-bar.js +1 -1
  16. package/dist/composed/filter-bar.js.map +1 -1
  17. package/dist/composed/multi-select-popover.js +1 -1
  18. package/dist/composed/multi-select-popover.js.map +1 -1
  19. package/dist/tokens/base.css +6 -0
  20. package/dist/tokens/index.css +1 -1
  21. package/dist/tokens/semantic.css +1 -1
  22. package/dist/tokens/typography.css +10 -0
  23. package/dist/tokens/utilities.css +6 -0
  24. package/dist/ui/avatar.d.ts.map +1 -1
  25. package/dist/ui/avatar.js +57 -56
  26. package/dist/ui/avatar.js.map +1 -1
  27. package/dist/ui/badge-indicator.js +1 -1
  28. package/dist/ui/badge-indicator.js.map +1 -1
  29. package/dist/ui/data-table-body.js +27 -27
  30. package/dist/ui/data-table-card.js +20 -20
  31. package/dist/ui/data-table-toolbar.d.ts.map +1 -1
  32. package/dist/ui/data-table-toolbar.js +40 -36
  33. package/dist/ui/data-table-toolbar.js.map +1 -1
  34. package/dist/ui/index.d.ts +1 -0
  35. package/dist/ui/index.d.ts.map +1 -1
  36. package/dist/ui/index.js +37 -36
  37. package/dist/ui/index.js.map +1 -1
  38. package/dist/ui/radio.js +1 -1
  39. package/dist/ui/radio.js.map +1 -1
  40. package/dist/ui/sidebar.js +7 -7
  41. package/dist/ui/stat-card.d.ts.map +1 -1
  42. package/dist/ui/stat-card.js +48 -47
  43. package/dist/ui/stat-card.js.map +1 -1
  44. package/dist/ui/surface.d.ts +46 -0
  45. package/dist/ui/surface.d.ts.map +1 -0
  46. package/dist/ui/surface.js +51 -0
  47. package/dist/ui/surface.js.map +1 -0
  48. package/dist/ui/text.js +6 -6
  49. package/dist/ui/text.js.map +1 -1
  50. package/docs/components/ui/surface.md +55 -0
  51. package/docs/recipes/install-tanstack-start.md +91 -44
  52. package/fonts/Manrope-Variable.woff2 +0 -0
  53. package/llms.txt +2 -1
  54. package/make-kit/components/overview.md +7 -3
  55. package/make-kit/components/surface.md +60 -0
  56. package/mcp-manifest.json +135 -3
  57. package/mcp-manifest.schema.json +6 -0
  58. package/package.json +7 -2
  59. package/scripts/welcome.mjs +4 -2
  60. package/skill/SKILL.md +1 -1
  61. package/skill/references/components.md +2 -1
  62. package/skill/references/setup-tanstack-start.md +91 -44
@@ -0,0 +1,51 @@
1
+ import { Hn as e } from "../_chunks/primitives.js";
2
+ import { t } from "../_chunks/vendor-utils.js";
3
+ import { cn as n } from "./lib/utils.js";
4
+ import * as r from "react";
5
+ import { jsx as i } from "react/jsx-runtime";
6
+ //#region src/ui/surface.tsx
7
+ var a = t("", {
8
+ variants: {
9
+ elevation: {
10
+ flat: "bg-surface-raised",
11
+ raised: "bg-surface-raised shadow-raised",
12
+ floating: "bg-surface-overlay shadow-floating",
13
+ overlay: "bg-surface-overlay shadow-overlay"
14
+ },
15
+ padding: {
16
+ none: "",
17
+ sm: "p-ds-04",
18
+ md: "p-ds-05",
19
+ lg: "p-ds-06"
20
+ },
21
+ radius: {
22
+ none: "rounded-none",
23
+ control: "rounded-control",
24
+ surface: "rounded-surface",
25
+ overlay: "rounded-overlay",
26
+ pill: "rounded-pill"
27
+ },
28
+ bordered: {
29
+ true: "border border-surface-border-strong",
30
+ false: ""
31
+ }
32
+ },
33
+ defaultVariants: {
34
+ elevation: "raised",
35
+ padding: "none",
36
+ radius: "surface",
37
+ bordered: !1
38
+ }
39
+ }), o = r.forwardRef(({ className: t, elevation: r, padding: o, radius: s, bordered: c, asChild: l = !1, ...u }, d) => (typeof process < "u" && process.env.NODE_ENV !== "production" && c && (r ?? "raised") !== "flat" && console.warn("[shilp-sutra] <Surface> combines `bordered` with a raised `elevation` — the double-edge anti-pattern. Pick one: use elevation=\"flat\" with `bordered`, or drop `bordered` and let the elevation carry the edge."), /* @__PURE__ */ i(l ? e : "div", {
40
+ ref: d,
41
+ className: n(a({
42
+ elevation: r,
43
+ padding: o,
44
+ radius: s,
45
+ bordered: c
46
+ }), t),
47
+ ...u
48
+ })));
49
+ o.displayName = "Surface";
50
+ //#endregion
51
+ export { o as Surface, a as surfaceVariants };
@@ -0,0 +1 @@
1
+ {"version":3,"file":"surface.js","names":[],"sources":["../../src/ui/surface.tsx"],"sourcesContent":["// @server-safe\nimport { Slot } from '@primitives/react-slot'\nimport { cva, type VariantProps } from 'class-variance-authority'\nimport * as React from 'react'\n\nimport { cn } from './lib/utils'\n\n// Bundlers (Vite, Next.js, webpack) define process.env.NODE_ENV; guard for raw ESM.\ndeclare const process: { env: { NODE_ENV?: string } } | undefined\n\nconst surfaceVariants = cva('', {\n variants: {\n // Elevation binds a surface-bg token to a shadow token — the one axis that\n // matters. `flat` sits on the page with no shadow (pair with `bordered` for a\n // Carbon-Tile edge); `raised` is the card level; `floating`/`overlay` are the\n // popover/menu/toast levels. bg + shadow always move together, so there is no\n // way to produce an un-tokened surface.\n elevation: {\n flat: 'bg-surface-raised',\n raised: 'bg-surface-raised shadow-raised',\n floating: 'bg-surface-overlay shadow-floating',\n overlay: 'bg-surface-overlay shadow-overlay',\n },\n // Simple, symmetric all-side padding — NOT Card's gap model. This is what keeps\n // Surface low-level: a plain box you can drop content into. Reach for Card when\n // you want the header/content/footer slot rhythm.\n padding: {\n none: '',\n sm: 'p-ds-04',\n md: 'p-ds-05',\n lg: 'p-ds-06',\n },\n radius: {\n none: 'rounded-none',\n control: 'rounded-control',\n surface: 'rounded-surface',\n overlay: 'rounded-overlay',\n pill: 'rounded-pill',\n },\n // Border-led edge (Carbon-Tile style). Meant for `elevation=\"flat\"`; combining it\n // with a shadowed elevation is the double-edge anti-pattern and dev-warns below.\n bordered: {\n true: 'border border-surface-border-strong',\n false: '',\n },\n },\n defaultVariants: {\n elevation: 'raised',\n padding: 'none',\n radius: 'surface',\n bordered: false,\n },\n})\n\nexport interface SurfaceProps\n extends React.HTMLAttributes<HTMLDivElement>,\n VariantProps<typeof surfaceVariants> {\n /** Render as the passed child element (Radix Slot) instead of a `div` — wrap a link,\n * section, or `motion.div` without adding a wrapper node. */\n asChild?: boolean\n}\n\n/**\n * Surface — the low-level elevated container primitive.\n *\n * The building block every panel, card, popover, and toast surface sits on. It owns\n * exactly one job: paint a tokened surface (background + shadow), with optional radius,\n * padding, and border. It has no slots, no color axis, and no motion — those belong to\n * the components composed on top of it (Card = Surface + gap-model padding + slots).\n *\n * **Elevation:** `flat` (bg, no shadow — pair with `bordered`) | `raised` (card level,\n * default) | `floating` (toasts, floating toolbars) | `overlay` (popovers, menus, dialogs).\n *\n * **Padding:** `none` (default) | `sm` (12px) | `md` (16px) | `lg` (24px) — simple all-side.\n *\n * **Edge vs. elevation:** use a shadow (`raised`/`floating`/`overlay`) OR a `bordered`\n * `flat` — never both (the double-edge anti-pattern; dev-warns).\n *\n * @example\n * // A raised panel with comfortable padding:\n * <Surface elevation=\"raised\" padding=\"md\">…</Surface>\n *\n * @example\n * // An on-page, border-led tile (no shadow):\n * <Surface elevation=\"flat\" bordered padding=\"sm\">…</Surface>\n *\n * @example\n * // Wrap a link as the surface itself:\n * <Surface asChild elevation=\"raised\" padding=\"sm\">\n * <a href=\"/upgrade\">…</a>\n * </Surface>\n */\nconst Surface = React.forwardRef<HTMLDivElement, SurfaceProps>(\n (\n { className, elevation, padding, radius, bordered, asChild = false, ...props },\n ref,\n ) => {\n if (\n typeof process !== 'undefined' &&\n process?.env.NODE_ENV !== 'production' &&\n bordered &&\n (elevation ?? 'raised') !== 'flat'\n ) {\n console.warn(\n '[shilp-sutra] <Surface> combines `bordered` with a raised `elevation` — the double-edge anti-pattern. Pick one: use elevation=\"flat\" with `bordered`, or drop `bordered` and let the elevation carry the edge.',\n )\n }\n const Comp = asChild ? Slot : 'div'\n return (\n <Comp\n ref={ref}\n className={cn(\n surfaceVariants({ elevation, padding, radius, bordered }),\n className,\n )}\n {...props}\n />\n )\n },\n)\nSurface.displayName = 'Surface'\n\nexport { Surface, surfaceVariants }\n"],"mappings":";;;;;;AAUA,IAAM,IAAkB,EAAI,IAAI;CAC9B,UAAU;EAMR,WAAW;GACT,MAAM;GACN,QAAQ;GACR,UAAU;GACV,SAAS;GACV;EAID,SAAS;GACP,MAAM;GACN,IAAI;GACJ,IAAI;GACJ,IAAI;GACL;EACD,QAAQ;GACN,MAAM;GACN,SAAS;GACT,SAAS;GACT,SAAS;GACT,MAAM;GACP;EAGD,UAAU;GACR,MAAM;GACN,OAAO;GACR;EACF;CACD,iBAAiB;EACf,WAAW;EACX,SAAS;EACT,QAAQ;EACR,UAAU;EACX;CACF,CAAC,EAwCI,IAAU,EAAM,YAElB,EAAE,cAAW,cAAW,YAAS,WAAQ,aAAU,aAAU,IAAO,GAAG,KACvE,OAGE,OAAO,UAAY,OAAA,QAAA,IAAA,aACO,gBAC1B,MACC,KAAa,cAAc,UAE5B,QAAQ,KACN,mNACD,EAID,kBAFW,IAAU,IAAO,OAE5B;CACO;CACL,WAAW,EACT,EAAgB;EAAE;EAAW;EAAS;EAAQ;EAAU,CAAC,EACzD,EACD;CACD,GAAI;CACJ,CAAA,EAGP;AACD,EAAQ,cAAc"}
package/dist/ui/text.js CHANGED
@@ -4,12 +4,12 @@ import * as n from "react";
4
4
  //#region src/ui/text.tsx
5
5
  var r = e("font-sans", {
6
6
  variants: { variant: {
7
- "heading-2xl": "text-[length:var(--typo-heading-2xl-size)] font-[number:var(--typo-heading-2xl-weight)] leading-[var(--typo-heading-2xl-leading)] tracking-[var(--typo-heading-2xl-tracking)]",
8
- "heading-xl": "text-[length:var(--typo-heading-xl-size)] font-[number:var(--typo-heading-xl-weight)] leading-[var(--typo-heading-xl-leading)] tracking-[var(--typo-heading-xl-tracking)]",
9
- "heading-lg": "text-[length:var(--typo-heading-lg-size)] font-[number:var(--typo-heading-lg-weight)] leading-[var(--typo-heading-lg-leading)] tracking-[var(--typo-heading-lg-tracking)]",
10
- "heading-md": "text-[length:var(--typo-heading-md-size)] font-[number:var(--typo-heading-md-weight)] leading-[var(--typo-heading-md-leading)] tracking-[var(--typo-heading-md-tracking)]",
11
- "heading-sm": "text-[length:var(--typo-heading-sm-size)] font-[number:var(--typo-heading-sm-weight)] leading-[var(--typo-heading-sm-leading)] tracking-[var(--typo-heading-sm-tracking)]",
12
- "heading-xs": "text-[length:var(--typo-heading-xs-size)] font-[number:var(--typo-heading-xs-weight)] leading-[var(--typo-heading-xs-leading)] tracking-[var(--typo-heading-xs-tracking)]",
7
+ "heading-2xl": "font-display text-[length:var(--typo-heading-2xl-size)] font-[number:var(--typo-heading-2xl-weight)] leading-[var(--typo-heading-2xl-leading)] tracking-[var(--typo-heading-2xl-tracking)]",
8
+ "heading-xl": "font-display text-[length:var(--typo-heading-xl-size)] font-[number:var(--typo-heading-xl-weight)] leading-[var(--typo-heading-xl-leading)] tracking-[var(--typo-heading-xl-tracking)]",
9
+ "heading-lg": "font-display text-[length:var(--typo-heading-lg-size)] font-[number:var(--typo-heading-lg-weight)] leading-[var(--typo-heading-lg-leading)] tracking-[var(--typo-heading-lg-tracking)]",
10
+ "heading-md": "font-display text-[length:var(--typo-heading-md-size)] font-[number:var(--typo-heading-md-weight)] leading-[var(--typo-heading-md-leading)] tracking-[var(--typo-heading-md-tracking)]",
11
+ "heading-sm": "font-display text-[length:var(--typo-heading-sm-size)] font-[number:var(--typo-heading-sm-weight)] leading-[var(--typo-heading-sm-leading)] tracking-[var(--typo-heading-sm-tracking)]",
12
+ "heading-xs": "font-display text-[length:var(--typo-heading-xs-size)] font-[number:var(--typo-heading-xs-weight)] leading-[var(--typo-heading-xs-leading)] tracking-[var(--typo-heading-xs-tracking)]",
13
13
  "body-lg": "text-[length:var(--typo-body-lg-size)] font-[number:var(--typo-body-lg-weight)] leading-[var(--typo-body-lg-leading)] tracking-[var(--typo-body-lg-tracking)]",
14
14
  "body-md": "text-[length:var(--typo-body-md-size)] font-[number:var(--typo-body-md-weight)] leading-[var(--typo-body-md-leading)] tracking-[var(--typo-body-md-tracking)]",
15
15
  "body-sm": "text-[length:var(--typo-body-sm-size)] font-[number:var(--typo-body-sm-weight)] leading-[var(--typo-body-sm-leading)] tracking-[var(--typo-body-sm-tracking)]",
@@ -1 +1 @@
1
- {"version":3,"file":"text.js","names":[],"sources":["../../src/ui/text.tsx"],"sourcesContent":["// @server-safe\nimport { cva, type VariantProps } from 'class-variance-authority'\nimport * as React from 'react'\n\nimport { cn } from './lib/utils'\n\nconst textVariants = cva('font-sans', {\n variants: {\n variant: {\n 'heading-2xl': 'text-[length:var(--typo-heading-2xl-size)] font-[number:var(--typo-heading-2xl-weight)] leading-[var(--typo-heading-2xl-leading)] tracking-[var(--typo-heading-2xl-tracking)]',\n 'heading-xl': 'text-[length:var(--typo-heading-xl-size)] font-[number:var(--typo-heading-xl-weight)] leading-[var(--typo-heading-xl-leading)] tracking-[var(--typo-heading-xl-tracking)]',\n 'heading-lg': 'text-[length:var(--typo-heading-lg-size)] font-[number:var(--typo-heading-lg-weight)] leading-[var(--typo-heading-lg-leading)] tracking-[var(--typo-heading-lg-tracking)]',\n 'heading-md': 'text-[length:var(--typo-heading-md-size)] font-[number:var(--typo-heading-md-weight)] leading-[var(--typo-heading-md-leading)] tracking-[var(--typo-heading-md-tracking)]',\n 'heading-sm': 'text-[length:var(--typo-heading-sm-size)] font-[number:var(--typo-heading-sm-weight)] leading-[var(--typo-heading-sm-leading)] tracking-[var(--typo-heading-sm-tracking)]',\n 'heading-xs': 'text-[length:var(--typo-heading-xs-size)] font-[number:var(--typo-heading-xs-weight)] leading-[var(--typo-heading-xs-leading)] tracking-[var(--typo-heading-xs-tracking)]',\n 'body-lg': 'text-[length:var(--typo-body-lg-size)] font-[number:var(--typo-body-lg-weight)] leading-[var(--typo-body-lg-leading)] tracking-[var(--typo-body-lg-tracking)]',\n 'body-md': 'text-[length:var(--typo-body-md-size)] font-[number:var(--typo-body-md-weight)] leading-[var(--typo-body-md-leading)] tracking-[var(--typo-body-md-tracking)]',\n 'body-sm': 'text-[length:var(--typo-body-sm-size)] font-[number:var(--typo-body-sm-weight)] leading-[var(--typo-body-sm-leading)] tracking-[var(--typo-body-sm-tracking)]',\n 'body-xs': 'text-[length:var(--typo-body-xs-size)] font-[number:var(--typo-body-xs-weight)] leading-[var(--typo-body-xs-leading)] tracking-[var(--typo-body-xs-tracking)]',\n 'label-lg': 'text-[length:var(--typo-label-lg-size)] font-[number:var(--typo-label-lg-weight)] leading-[var(--typo-label-lg-leading)] tracking-[var(--typo-label-lg-tracking)] uppercase',\n 'label-md': 'text-[length:var(--typo-label-md-size)] font-[number:var(--typo-label-md-weight)] leading-[var(--typo-label-md-leading)] tracking-[var(--typo-label-md-tracking)] uppercase',\n 'label-sm': 'text-[length:var(--typo-label-sm-size)] font-[number:var(--typo-label-sm-weight)] leading-[var(--typo-label-sm-leading)] tracking-[var(--typo-label-sm-tracking)] uppercase',\n 'label-xs': 'text-[length:var(--typo-label-xs-size)] font-[number:var(--typo-label-xs-weight)] leading-[var(--typo-label-xs-leading)] tracking-[var(--typo-label-xs-tracking)] uppercase',\n caption: 'text-[length:var(--typo-caption-size)] font-[number:var(--typo-caption-weight)] leading-[var(--typo-caption-leading)] tracking-[var(--typo-caption-tracking)]',\n overline: 'text-[length:var(--typo-overline-size)] font-[number:var(--typo-overline-weight)] leading-[var(--typo-overline-leading)] tracking-[var(--typo-overline-tracking)] uppercase',\n code: 'text-[length:var(--typo-code-size)] font-[number:var(--typo-code-weight)] leading-[var(--typo-code-leading)] tracking-[var(--typo-code-tracking)] font-mono',\n 'label-plain-lg': 'text-[length:var(--typo-label-plain-lg-size)] font-[number:var(--typo-label-plain-lg-weight)] leading-[var(--typo-label-plain-lg-leading)] tracking-[var(--typo-label-plain-lg-tracking)]',\n 'label-plain-md': 'text-[length:var(--typo-label-plain-md-size)] font-[number:var(--typo-label-plain-md-weight)] leading-[var(--typo-label-plain-md-leading)] tracking-[var(--typo-label-plain-md-tracking)]',\n 'label-plain-sm': 'text-[length:var(--typo-label-plain-sm-size)] font-[number:var(--typo-label-plain-sm-weight)] leading-[var(--typo-label-plain-sm-leading)] tracking-[var(--typo-label-plain-sm-tracking)]',\n },\n },\n defaultVariants: {\n variant: 'body-md',\n },\n})\n\ntype TextVariant = NonNullable<VariantProps<typeof textVariants>['variant']>\n\nconst defaultElementMap: Record<TextVariant, keyof React.JSX.IntrinsicElements> = {\n 'heading-2xl': 'h1',\n 'heading-xl': 'h2',\n 'heading-lg': 'h3',\n 'heading-md': 'h4',\n 'heading-sm': 'h5',\n 'heading-xs': 'h6',\n 'body-lg': 'p',\n 'body-md': 'p',\n 'body-sm': 'p',\n 'body-xs': 'p',\n 'label-lg': 'span',\n 'label-md': 'span',\n 'label-sm': 'span',\n 'label-xs': 'span',\n caption: 'span',\n overline: 'span',\n code: 'code',\n 'label-plain-lg': 'span',\n 'label-plain-md': 'span',\n 'label-plain-sm': 'span',\n}\n\n/**\n * Props for Text — a polymorphic typography component covering the full type scale:\n * headings, body, labels, captions, and overlines — with automatic semantic HTML element selection.\n *\n * **Variants (grouped):**\n * - Headings: `heading-2xl` → `h1`, `heading-xl` → `h2`, `heading-lg` → `h3`, etc.\n * - Body: `body-lg` | `body-md` (default) | `body-sm` | `body-xs` → `<p>`\n * - Labels: `label-lg` | `label-md` | `label-sm` | `label-xs` → `<span>` (uppercase)\n * - Misc: `caption` | `overline` → `<span>` (overline is uppercase)\n *\n * **`as` prop:** Override the rendered element. E.g. `<Text variant=\"heading-lg\" as=\"div\">` renders\n * `<div class=\"text-heading-lg ...\">` — useful when you need heading styles on a non-heading element.\n *\n * @example\n * // Page heading:\n * <Text variant=\"heading-2xl\">Welcome to Shilp Sutra</Text>\n *\n * @example\n * // Section label (uppercase, small):\n * <Text variant=\"label-sm\" className=\"text-surface-fg-muted\">Recent activity</Text>\n *\n * @example\n * // Body copy with custom element (renders as <span> for inline use):\n * <Text variant=\"body-sm\" as=\"span\" className=\"text-surface-fg-muted\">\n * Last updated 3 hours ago\n * </Text>\n *\n * @example\n * // Caption under an image or figure:\n * <Text variant=\"caption\" className=\"text-surface-fg-subtle\">Figure 1: System architecture</Text>\n * // These are just a few ways — feel free to combine props creatively!\n */\ntype TextProps<T extends React.ElementType = 'p'> = {\n variant?: TextVariant\n as?: T\n className?: string\n children?: React.ReactNode\n} & Omit<React.ComponentPropsWithRef<T>, 'as' | 'variant' | 'className' | 'children'>\n\n// Polymorphic component type — preserves T across the call site so element-\n// specific props (e.g. `htmlFor` on `as=\"label\"`, `href` on `as=\"a\"`) typecheck.\n// React.forwardRef can't keep a generic parameter live at its export, so we\n// type the implementation loosely and cast the public export to a generic\n// callable. Standard polymorphic-component pattern (same shape as Radix).\ntype TextComponent = (<T extends React.ElementType = 'p'>(\n props: TextProps<T> & { ref?: React.ComponentPropsWithRef<T>['ref'] },\n) => React.ReactElement | null) & { displayName?: string }\n\nconst TextImpl = React.forwardRef<HTMLElement, TextProps>(\n ({ variant = 'body-md', as, className, children, ...props }, ref) => {\n const Component = as || defaultElementMap[variant] || 'p'\n return React.createElement(\n Component,\n { ref, className: cn(textVariants({ variant }), className), ...props },\n children,\n )\n },\n)\nTextImpl.displayName = 'Text'\n\nconst Text = TextImpl as unknown as TextComponent\n\nexport { Text, type TextProps, type TextVariant,textVariants }\n"],"mappings":";;;;AAMA,IAAM,IAAe,EAAI,aAAa;CACpC,UAAU,EACR,SAAS;EACP,eAAe;EACf,cAAe;EACf,cAAe;EACf,cAAe;EACf,cAAe;EACf,cAAe;EACf,WAAe;EACf,WAAe;EACf,WAAe;EACf,WAAe;EACf,YAAe;EACf,YAAe;EACf,YAAe;EACf,YAAe;EACf,SAAe;EACf,UAAe;EACf,MAAmB;EACnB,kBAAmB;EACnB,kBAAmB;EACnB,kBAAmB;EACpB,EACF;CACD,iBAAiB,EACf,SAAS,WACV;CACF,CAAC,EAII,IAA4E;CAChF,eAAe;CACf,cAAe;CACf,cAAe;CACf,cAAe;CACf,cAAe;CACf,cAAe;CACf,WAAe;CACf,WAAe;CACf,WAAe;CACf,WAAe;CACf,YAAe;CACf,YAAe;CACf,YAAe;CACf,YAAe;CACf,SAAe;CACf,UAAe;CACf,MAAmB;CACnB,kBAAmB;CACnB,kBAAmB;CACnB,kBAAmB;CACpB,EAkDK,IAAW,EAAM,YACpB,EAAE,aAAU,WAAW,OAAI,cAAW,aAAU,GAAG,KAAS,MAAQ;CACnE,IAAM,IAAY,KAAM,EAAkB,MAAY;AACtD,QAAO,EAAM,cACX,GACA;EAAE;EAAK,WAAW,EAAG,EAAa,EAAE,YAAS,CAAC,EAAE,EAAU;EAAE,GAAG;EAAO,EACtE,EACD;EAEJ;AACD,EAAS,cAAc;AAEvB,IAAM,IAAO"}
1
+ {"version":3,"file":"text.js","names":[],"sources":["../../src/ui/text.tsx"],"sourcesContent":["// @server-safe\nimport { cva, type VariantProps } from 'class-variance-authority'\nimport * as React from 'react'\n\nimport { cn } from './lib/utils'\n\nconst textVariants = cva('font-sans', {\n variants: {\n variant: {\n 'heading-2xl': 'font-display text-[length:var(--typo-heading-2xl-size)] font-[number:var(--typo-heading-2xl-weight)] leading-[var(--typo-heading-2xl-leading)] tracking-[var(--typo-heading-2xl-tracking)]',\n 'heading-xl': 'font-display text-[length:var(--typo-heading-xl-size)] font-[number:var(--typo-heading-xl-weight)] leading-[var(--typo-heading-xl-leading)] tracking-[var(--typo-heading-xl-tracking)]',\n 'heading-lg': 'font-display text-[length:var(--typo-heading-lg-size)] font-[number:var(--typo-heading-lg-weight)] leading-[var(--typo-heading-lg-leading)] tracking-[var(--typo-heading-lg-tracking)]',\n 'heading-md': 'font-display text-[length:var(--typo-heading-md-size)] font-[number:var(--typo-heading-md-weight)] leading-[var(--typo-heading-md-leading)] tracking-[var(--typo-heading-md-tracking)]',\n 'heading-sm': 'font-display text-[length:var(--typo-heading-sm-size)] font-[number:var(--typo-heading-sm-weight)] leading-[var(--typo-heading-sm-leading)] tracking-[var(--typo-heading-sm-tracking)]',\n 'heading-xs': 'font-display text-[length:var(--typo-heading-xs-size)] font-[number:var(--typo-heading-xs-weight)] leading-[var(--typo-heading-xs-leading)] tracking-[var(--typo-heading-xs-tracking)]',\n 'body-lg': 'text-[length:var(--typo-body-lg-size)] font-[number:var(--typo-body-lg-weight)] leading-[var(--typo-body-lg-leading)] tracking-[var(--typo-body-lg-tracking)]',\n 'body-md': 'text-[length:var(--typo-body-md-size)] font-[number:var(--typo-body-md-weight)] leading-[var(--typo-body-md-leading)] tracking-[var(--typo-body-md-tracking)]',\n 'body-sm': 'text-[length:var(--typo-body-sm-size)] font-[number:var(--typo-body-sm-weight)] leading-[var(--typo-body-sm-leading)] tracking-[var(--typo-body-sm-tracking)]',\n 'body-xs': 'text-[length:var(--typo-body-xs-size)] font-[number:var(--typo-body-xs-weight)] leading-[var(--typo-body-xs-leading)] tracking-[var(--typo-body-xs-tracking)]',\n 'label-lg': 'text-[length:var(--typo-label-lg-size)] font-[number:var(--typo-label-lg-weight)] leading-[var(--typo-label-lg-leading)] tracking-[var(--typo-label-lg-tracking)] uppercase',\n 'label-md': 'text-[length:var(--typo-label-md-size)] font-[number:var(--typo-label-md-weight)] leading-[var(--typo-label-md-leading)] tracking-[var(--typo-label-md-tracking)] uppercase',\n 'label-sm': 'text-[length:var(--typo-label-sm-size)] font-[number:var(--typo-label-sm-weight)] leading-[var(--typo-label-sm-leading)] tracking-[var(--typo-label-sm-tracking)] uppercase',\n 'label-xs': 'text-[length:var(--typo-label-xs-size)] font-[number:var(--typo-label-xs-weight)] leading-[var(--typo-label-xs-leading)] tracking-[var(--typo-label-xs-tracking)] uppercase',\n caption: 'text-[length:var(--typo-caption-size)] font-[number:var(--typo-caption-weight)] leading-[var(--typo-caption-leading)] tracking-[var(--typo-caption-tracking)]',\n overline: 'text-[length:var(--typo-overline-size)] font-[number:var(--typo-overline-weight)] leading-[var(--typo-overline-leading)] tracking-[var(--typo-overline-tracking)] uppercase',\n code: 'text-[length:var(--typo-code-size)] font-[number:var(--typo-code-weight)] leading-[var(--typo-code-leading)] tracking-[var(--typo-code-tracking)] font-mono',\n 'label-plain-lg': 'text-[length:var(--typo-label-plain-lg-size)] font-[number:var(--typo-label-plain-lg-weight)] leading-[var(--typo-label-plain-lg-leading)] tracking-[var(--typo-label-plain-lg-tracking)]',\n 'label-plain-md': 'text-[length:var(--typo-label-plain-md-size)] font-[number:var(--typo-label-plain-md-weight)] leading-[var(--typo-label-plain-md-leading)] tracking-[var(--typo-label-plain-md-tracking)]',\n 'label-plain-sm': 'text-[length:var(--typo-label-plain-sm-size)] font-[number:var(--typo-label-plain-sm-weight)] leading-[var(--typo-label-plain-sm-leading)] tracking-[var(--typo-label-plain-sm-tracking)]',\n },\n },\n defaultVariants: {\n variant: 'body-md',\n },\n})\n\ntype TextVariant = NonNullable<VariantProps<typeof textVariants>['variant']>\n\nconst defaultElementMap: Record<TextVariant, keyof React.JSX.IntrinsicElements> = {\n 'heading-2xl': 'h1',\n 'heading-xl': 'h2',\n 'heading-lg': 'h3',\n 'heading-md': 'h4',\n 'heading-sm': 'h5',\n 'heading-xs': 'h6',\n 'body-lg': 'p',\n 'body-md': 'p',\n 'body-sm': 'p',\n 'body-xs': 'p',\n 'label-lg': 'span',\n 'label-md': 'span',\n 'label-sm': 'span',\n 'label-xs': 'span',\n caption: 'span',\n overline: 'span',\n code: 'code',\n 'label-plain-lg': 'span',\n 'label-plain-md': 'span',\n 'label-plain-sm': 'span',\n}\n\n/**\n * Props for Text — a polymorphic typography component covering the full type scale:\n * headings, body, labels, captions, and overlines — with automatic semantic HTML element selection.\n *\n * **Variants (grouped):**\n * - Headings: `heading-2xl` → `h1`, `heading-xl` → `h2`, `heading-lg` → `h3`, etc.\n * - Body: `body-lg` | `body-md` (default) | `body-sm` | `body-xs` → `<p>`\n * - Labels: `label-lg` | `label-md` | `label-sm` | `label-xs` → `<span>` (uppercase)\n * - Misc: `caption` | `overline` → `<span>` (overline is uppercase)\n *\n * **`as` prop:** Override the rendered element. E.g. `<Text variant=\"heading-lg\" as=\"div\">` renders\n * `<div class=\"text-heading-lg ...\">` — useful when you need heading styles on a non-heading element.\n *\n * @example\n * // Page heading:\n * <Text variant=\"heading-2xl\">Welcome to Shilp Sutra</Text>\n *\n * @example\n * // Section label (uppercase, small):\n * <Text variant=\"label-sm\" className=\"text-surface-fg-muted\">Recent activity</Text>\n *\n * @example\n * // Body copy with custom element (renders as <span> for inline use):\n * <Text variant=\"body-sm\" as=\"span\" className=\"text-surface-fg-muted\">\n * Last updated 3 hours ago\n * </Text>\n *\n * @example\n * // Caption under an image or figure:\n * <Text variant=\"caption\" className=\"text-surface-fg-subtle\">Figure 1: System architecture</Text>\n * // These are just a few ways — feel free to combine props creatively!\n */\ntype TextProps<T extends React.ElementType = 'p'> = {\n variant?: TextVariant\n as?: T\n className?: string\n children?: React.ReactNode\n} & Omit<React.ComponentPropsWithRef<T>, 'as' | 'variant' | 'className' | 'children'>\n\n// Polymorphic component type — preserves T across the call site so element-\n// specific props (e.g. `htmlFor` on `as=\"label\"`, `href` on `as=\"a\"`) typecheck.\n// React.forwardRef can't keep a generic parameter live at its export, so we\n// type the implementation loosely and cast the public export to a generic\n// callable. Standard polymorphic-component pattern (same shape as Radix).\ntype TextComponent = (<T extends React.ElementType = 'p'>(\n props: TextProps<T> & { ref?: React.ComponentPropsWithRef<T>['ref'] },\n) => React.ReactElement | null) & { displayName?: string }\n\nconst TextImpl = React.forwardRef<HTMLElement, TextProps>(\n ({ variant = 'body-md', as, className, children, ...props }, ref) => {\n const Component = as || defaultElementMap[variant] || 'p'\n return React.createElement(\n Component,\n { ref, className: cn(textVariants({ variant }), className), ...props },\n children,\n )\n },\n)\nTextImpl.displayName = 'Text'\n\nconst Text = TextImpl as unknown as TextComponent\n\nexport { Text, type TextProps, type TextVariant,textVariants }\n"],"mappings":";;;;AAMA,IAAM,IAAe,EAAI,aAAa;CACpC,UAAU,EACR,SAAS;EACP,eAAe;EACf,cAAe;EACf,cAAe;EACf,cAAe;EACf,cAAe;EACf,cAAe;EACf,WAAe;EACf,WAAe;EACf,WAAe;EACf,WAAe;EACf,YAAe;EACf,YAAe;EACf,YAAe;EACf,YAAe;EACf,SAAe;EACf,UAAe;EACf,MAAmB;EACnB,kBAAmB;EACnB,kBAAmB;EACnB,kBAAmB;EACpB,EACF;CACD,iBAAiB,EACf,SAAS,WACV;CACF,CAAC,EAII,IAA4E;CAChF,eAAe;CACf,cAAe;CACf,cAAe;CACf,cAAe;CACf,cAAe;CACf,cAAe;CACf,WAAe;CACf,WAAe;CACf,WAAe;CACf,WAAe;CACf,YAAe;CACf,YAAe;CACf,YAAe;CACf,YAAe;CACf,SAAe;CACf,UAAe;CACf,MAAmB;CACnB,kBAAmB;CACnB,kBAAmB;CACnB,kBAAmB;CACpB,EAkDK,IAAW,EAAM,YACpB,EAAE,aAAU,WAAW,OAAI,cAAW,aAAU,GAAG,KAAS,MAAQ;CACnE,IAAM,IAAY,KAAM,EAAkB,MAAY;AACtD,QAAO,EAAM,cACX,GACA;EAAE;EAAK,WAAW,EAAG,EAAa,EAAE,YAAS,CAAC,EAAE,EAAU;EAAE,GAAG;EAAO,EACtE,EACD;EAEJ;AACD,EAAS,cAAc;AAEvB,IAAM,IAAO"}
@@ -0,0 +1,55 @@
1
+ # Surface
2
+
3
+ - Import: @devalok/shilp-sutra/ui/surface
4
+ - Server-safe: Yes
5
+ - Category: ui
6
+
7
+ The low-level elevated container primitive. Owns background + shadow + radius + optional padding and border — nothing else. Card, Popover, Toast, Sheet, and other surfaces compose it.
8
+
9
+ ## Props
10
+ ### Surface
11
+ elevation: "flat" | "raised" | "floating" | "overlay"
12
+ padding: "none" | "sm" | "md" | "lg"
13
+ radius: "none" | "control" | "surface" | "overlay" | "pill"
14
+ bordered: "true" | "false"
15
+ asChild: boolean — render as the passed child element (Radix Slot) instead of a div
16
+
17
+ ## Defaults
18
+ Surface: elevation="raised", padding="none", radius="surface", bordered=false
19
+
20
+ ## Elevation
21
+ flat — bg-surface-raised, no shadow (pair with `bordered` for an on-page tile)
22
+ raised — bg-surface-raised + shadow-raised (card level)
23
+ floating — bg-surface-overlay + shadow-floating (toasts, floating toolbars)
24
+ overlay — bg-surface-overlay + shadow-overlay (popovers, menus, dialogs)
25
+
26
+ ## Padding
27
+ none=0 · sm=ds-04 (12px) · md=ds-05 (16px) · lg=ds-06 (24px) — simple all-side (not Card's gap model)
28
+
29
+ ## Example
30
+ ```jsx
31
+ // Raised panel with padding
32
+ <Surface elevation="raised" padding="md">…</Surface>
33
+
34
+ // On-page, border-led tile (no shadow)
35
+ <Surface elevation="flat" bordered padding="sm">…</Surface>
36
+
37
+ // The whole surface is a link
38
+ <Surface asChild elevation="raised" padding="sm">
39
+ <a href="/upgrade">Upgrade to Pro</a>
40
+ </Surface>
41
+ ```
42
+
43
+ ## Composability
44
+ - Server-safe — renders in RSC trees.
45
+ - The base layer: `Card` = Surface + gap-model padding + color edge + header/content/footer slots. Reach for Card when you want that slot rhythm; reach for Surface for a plain elevated box.
46
+ - `asChild` merges the surface classes onto your own element (a link, section, or `motion.div`) with no extra wrapper node.
47
+ - Padding is symmetric all-side. For asymmetric or gap-model spacing, use Card or add your own utilities via `className`.
48
+
49
+ ## Gotchas
50
+ - **Edge OR elevation, never both.** Combining `bordered` with a shadowed elevation (raised/floating/overlay) is the double-edge anti-pattern and dev-warns. Use `elevation="flat"` with `bordered`, or drop `bordered` and let the shadow be the edge.
51
+ - `flat` still uses `bg-surface-raised` (a card without a shadow), not the page background — it is a surface, not a hole.
52
+
53
+ ## Changes
54
+ ### Unreleased
55
+ - **Added** Initial release — `elevation` / `padding` / `radius` / `bordered` / `asChild`. The low-level surface primitive that Card and other surfaces compose.
@@ -1,20 +1,29 @@
1
1
  # Install: TanStack Start
2
2
 
3
- > Setup recipe for adding `@devalok/shilp-sutra` to a TanStack Start project (the React full-stack framework built on Vinxi/Vite).
3
+ > Setup recipe for adding `@devalok/shilp-sutra` to a TanStack Start project (the React full-stack framework built on Vite).
4
+
5
+ > **Updated 2026-07-10 for the Vite-plugin era.** TanStack Start moved off Vinxi: the package is now `@tanstack/react-start` (not `@tanstack/start`), config lives in `vite.config.ts` (not `app.config.ts`), and the app root is `src/` (not `app/`). If you are on an old Vinxi-based project (`app.config.ts`, `@tanstack/start`), migrate to the Vite plugin first — see the TanStack Start docs.
4
6
 
5
7
  ## 1. Detect
6
8
 
7
9
  You are in this recipe if:
8
10
 
9
- - `package.json` lists `"@tanstack/start"` and `"@tanstack/react-router"`
10
- - `app.config.{ts,js}` (Vinxi) exists at the project root
11
- - `app/router.tsx` and `app/routes/__root.tsx` exist
11
+ - `package.json` lists `"@tanstack/react-start"` and `"@tanstack/react-router"`
12
+ - `vite.config.{ts,js}` exists and uses the `tanstackStart` plugin from `@tanstack/react-start/plugin/vite`
13
+ - `src/router.tsx` and `src/routes/__root.tsx` exist (a `src/routeTree.gen.ts` is generated on first run)
14
+
15
+ If instead you see `app.config.ts` + `@tanstack/start`, that is the legacy Vinxi setup — this recipe does not apply until you migrate.
12
16
 
13
17
  ## 2. Install
14
18
 
15
19
  ```bash
20
+ # pnpm
16
21
  pnpm add @devalok/shilp-sutra framer-motion
17
22
  pnpm add -D tailwindcss@^4 @tailwindcss/vite
23
+
24
+ # npm
25
+ npm install @devalok/shilp-sutra framer-motion
26
+ npm install -D tailwindcss@^4 @tailwindcss/vite
18
27
  ```
19
28
 
20
29
  Optional:
@@ -38,39 +47,79 @@ Some components ship hard peers as optional. **Install BEFORE first import** or
38
47
  | `@devalok/shilp-sutra/composed/markdown-viewer` | `pnpm add react-markdown react-syntax-highlighter` |
39
48
  | Any `Icon` / `IconButton` with Tabler icons | `pnpm add @tabler/icons-react` |
40
49
 
41
- ## 3. Wire Tailwind 4 in `app.config.ts`
50
+ ## 3. Wire Tailwind 4 in `vite.config.ts`
51
+
52
+ Add `@tailwindcss/vite` to the existing plugins array. `tanstackStart()` must come before `viteReact()`; `tailwindcss()` can go first so tokens are processed early.
42
53
 
43
54
  ```ts
44
- import { defineConfig } from "@tanstack/start/config";
55
+ import { defineConfig } from "vite";
56
+ import { tanstackStart } from "@tanstack/react-start/plugin/vite";
57
+ import viteReact from "@vitejs/plugin-react";
45
58
  import tailwindcss from "@tailwindcss/vite";
46
59
 
47
60
  export default defineConfig({
48
- vite: {
49
- plugins: [tailwindcss()],
50
- },
61
+ plugins: [
62
+ tailwindcss(),
63
+ tanstackStart(),
64
+ viteReact(),
65
+ ],
51
66
  });
52
67
  ```
53
68
 
69
+ Do **not** add a `tailwind.config.{ts,js}` — Tailwind 4 is CSS-first.
70
+
54
71
  ## 4. Wire tokens
55
72
 
56
- Create `app/styles/globals.css`:
73
+ Create `src/styles/globals.css`:
57
74
 
58
75
  ```css
59
76
  @import "tailwindcss";
60
77
  @import "@devalok/shilp-sutra/css";
61
78
  ```
62
79
 
63
- Import it from `app/routes/__root.tsx`:
80
+ Wire it as an asset-URL stylesheet from the root route's `head` (the TanStack Start idiom — the `?url` suffix emits the file as an asset instead of inlining it):
64
81
 
65
82
  ```tsx
66
- import "../styles/globals.css";
67
- ```
83
+ // src/routes/__root.tsx
84
+ import {
85
+ createRootRoute,
86
+ HeadContent,
87
+ Outlet,
88
+ Scripts,
89
+ } from "@tanstack/react-router";
90
+ import globalsCss from "../styles/globals.css?url";
68
91
 
69
- If you prefer asset-URL imports the way Remix does it, use `import css from "../styles/globals.css?url"` and add `<link rel="stylesheet" href={css} />` to the `<head>` returned by `__root.tsx`.
92
+ export const Route = createRootRoute({
93
+ head: () => ({
94
+ meta: [
95
+ { charSet: "utf-8" },
96
+ { name: "viewport", content: "width=device-width, initial-scale=1" },
97
+ ],
98
+ links: [{ rel: "stylesheet", href: globalsCss }],
99
+ }),
100
+ component: RootComponent,
101
+ });
102
+
103
+ function RootComponent() {
104
+ return (
105
+ <html lang="en" suppressHydrationWarning>
106
+ <head>
107
+ <HeadContent />
108
+ </head>
109
+ <body>
110
+ <Outlet />
111
+ <Scripts />
112
+ </body>
113
+ </html>
114
+ );
115
+ }
116
+ ```
70
117
 
71
118
  ## 5. Theme toggle
72
119
 
73
- Create `public/theme-bootstrap.js` (a static asset served verbatim):
120
+ Add a pre-hydration bootstrap so there is no flash of the wrong theme. The cleanest place is a `scripts` entry on the root route (runs before hydration); a static `public/theme-bootstrap.js` referenced from `<head>` also works and is CSP-friendly.
121
+
122
+ Static-asset approach — create `public/theme-bootstrap.js`:
74
123
 
75
124
  ```js
76
125
  (function () {
@@ -84,44 +133,36 @@ Create `public/theme-bootstrap.js` (a static asset served verbatim):
84
133
  })();
85
134
  ```
86
135
 
87
- Reference it from `app/routes/__root.tsx`:
136
+ Reference it from the root route's `head` scripts:
88
137
 
89
138
  ```tsx
90
- import { createRootRoute, Outlet } from "@tanstack/react-router";
91
-
92
139
  export const Route = createRootRoute({
93
- component: () => (
94
- <html lang="en" suppressHydrationWarning>
95
- <head>
96
- <script src="/theme-bootstrap.js" />
97
- </head>
98
- <body>
99
- <Outlet />
100
- </body>
101
- </html>
102
- ),
140
+ head: () => ({
141
+ // ...meta, links as above...
142
+ scripts: [{ src: "/theme-bootstrap.js" }],
143
+ }),
144
+ component: RootComponent,
103
145
  });
104
146
  ```
105
147
 
106
- For runtime toggling, use the `useColorMode` hook — see [install-vite.md § 5](./install-vite.md#5-theme-toggle-no-next-themes-here).
148
+ For runtime toggling inside components, use the `useColorMode` hook — see [install-vite.md § 5](./install-vite.md#5-theme-toggle-no-next-themes-here).
107
149
 
108
150
  ## 6. Toaster (optional)
109
151
 
110
- Mount in `__root.tsx` next to `<Outlet />`:
152
+ Mount once in `__root.tsx`'s `RootComponent`, next to `<Outlet />`:
111
153
 
112
154
  ```tsx
113
155
  import { Toaster } from "@devalok/shilp-sutra/ui/toaster";
114
156
 
115
157
  // inside <body>
116
- <>
117
- <Outlet />
118
- <Toaster />
119
- </>
158
+ <Outlet />
159
+ <Toaster />
160
+ <Scripts />
120
161
  ```
121
162
 
122
163
  ## 7. Verify
123
164
 
124
- Create or replace `app/routes/index.tsx`:
165
+ Create or replace `src/routes/index.tsx`:
125
166
 
126
167
  ```tsx
127
168
  import { createFileRoute } from "@tanstack/react-router";
@@ -130,7 +171,11 @@ import { Stack } from "@devalok/shilp-sutra/ui/stack";
130
171
  import { Text } from "@devalok/shilp-sutra/ui/text";
131
172
 
132
173
  export const Route = createFileRoute("/")({
133
- component: () => (
174
+ component: Home,
175
+ });
176
+
177
+ function Home() {
178
+ return (
134
179
  <Stack className="p-ds-08" gap="ds-04">
135
180
  <Text variant="heading-2xl">Hello, Shilp Sutra</Text>
136
181
  <Stack direction="row" gap="ds-03">
@@ -138,21 +183,23 @@ export const Route = createFileRoute("/")({
138
183
  <Button variant="soft">Soft</Button>
139
184
  </Stack>
140
185
  </Stack>
141
- ),
142
- });
186
+ );
187
+ }
143
188
  ```
144
189
 
145
- Run `pnpm dev` and open the URL.
190
+ Run `pnpm dev` and open the printed URL. Expected output matches [Next App Router § 7](./install-next-app-router.md#7-verify-the-install).
146
191
 
147
192
  ## 8. TanStack Start specifics
148
193
 
194
+ - **No `transpilePackages` equivalent — and you do not need one.** TanStack Start's Vite resolves `@devalok/shilp-sutra` from `node_modules` as native ESM.
149
195
  - **Server functions** (`createServerFn`) — do not import shilp-sutra components inside server functions; they run server-only.
150
- - **Streaming SSR** is the default. All shilp-sutra components SSR cleanly because they have no client-only side effects at module top-level.
196
+ - **Streaming SSR** is the default. Shilp Sutra components SSR cleanly (no client-only side effects at module top-level).
151
197
  - **`framer-motion` SSR** — animations gracefully degrade on the initial render.
152
- - **CSP.** The static `theme-bootstrap.js` asset complies with strict CSP (no inline scripts required).
198
+ - **`routeTree.gen.ts` is generated** do not edit it by hand; it regenerates on dev/build.
153
199
 
154
200
  ## 9. What NOT to do
155
201
 
156
- - ❌ Add `tailwind.config.{ts,js}` — Tailwind 4 is CSS-first.
157
- - ❌ Mount `<Toaster />` inside route componentsit should live once at the `__root`.
158
- - ❌ Mix `@tailwindcss/postcss` and `@tailwindcss/vite` pick one (Vite plugin recommended for TanStack Start).
202
+ - ❌ Add a `tailwind.config.{ts,js}` — Tailwind 4 is CSS-first.
203
+ - ❌ Use `@tanstack/start` / `app.config.ts` / `@tanstack/start/config`that is the retired Vinxi setup. Current TanStack Start is `@tanstack/react-start` + `vite.config.ts`.
204
+ - ❌ Mount `<Toaster />` inside route components it lives once at the `__root`.
205
+ - ❌ Mix `@tailwindcss/postcss` and `@tailwindcss/vite` — pick the Vite plugin.
Binary file
package/llms.txt CHANGED
@@ -1,6 +1,6 @@
1
1
  # @devalok/shilp-sutra
2
2
 
3
- > Radix UI + Tailwind 4 (CSS-first) + CVA design system for Devalok apps, v0.45.1.
3
+ > Radix UI + Tailwind 4 (CSS-first) + CVA design system for Devalok apps, v0.47.0.
4
4
  > Built on the same primitives as shadcn/ui but with DIFFERENT prop APIs — never guess from shadcn knowledge; verify every prop.
5
5
  > This file is a ROUTER: it tells you what exists and where to get details. Do not look for prop tables here — fetch them per component (MCP tool or per-component doc file below).
6
6
 
@@ -100,6 +100,7 @@ Format: `[name](doc path): summary`. Import paths follow `@devalok/shilp-sutra/<
100
100
  - [stat-flash](docs/components/ui/stat-flash.md)
101
101
  - [status-dot](docs/components/ui/status-dot.md)
102
102
  - [stepper](docs/components/ui/stepper.md)
103
+ - [surface](docs/components/ui/surface.md): The low-level elevated container primitive
103
104
  - [switch](docs/components/ui/switch.md)
104
105
  - [table](docs/components/ui/table.md)
105
106
  - [table-row-link](docs/components/ui/table-row-link.md)
@@ -144,8 +144,11 @@ Page wrapper?
144
144
  Vertical / horizontal stack?
145
145
  → <Stack direction="col|row" gap="ds-*">
146
146
 
147
- A card / panel?
148
- → <Card> — surface-raised + shadow-raised
147
+ A plain elevated box (no slots)?
148
+ → <Surface elevation="raised" padding="md"> — the low-level surface Card is built on
149
+
150
+ A card / panel (header / content / footer rhythm)?
151
+ → <Card> — Surface + gap-model padding + slots
149
152
  → <Card variant="..."> — tinted by color prop
150
153
 
151
154
  A grid?
@@ -261,6 +264,7 @@ Code?
261
264
  |---|---|
262
265
  | Container | `/ui/container` |
263
266
  | Stack | `/ui/stack` |
267
+ | Surface | `/ui/surface` |
264
268
  | Card | `/ui/card` |
265
269
  | AspectRatio | `/ui/aspect-ratio` |
266
270
  | Separator | `/ui/separator` |
@@ -305,4 +309,4 @@ Code?
305
309
 
306
310
  ## Per-component guides
307
311
 
308
- See `components/{button|card|input|dialog|badge|select|tabs|toast|form|table|dropdown-menu|popover|text|stack|icon}.md` for prop tables, examples, and rules.
312
+ See `components/{button|card|surface|input|dialog|badge|select|tabs|toast|form|table|dropdown-menu|popover|text|stack|icon}.md` for prop tables, examples, and rules.
@@ -0,0 +1,60 @@
1
+ # Surface
2
+
3
+ ```tsx
4
+ import { Surface } from '@devalok/shilp-sutra/ui/surface'
5
+ ```
6
+
7
+ The low-level elevated container. It paints a tokened surface — background + shadow + radius, with optional padding and border — and nothing else. Card, Popover, Toast, and Sheet are built on it.
8
+
9
+ ## When to use
10
+
11
+ - You need a plain elevated box (a promo, a callout, a small panel) and don't need Card's header/content/footer slots.
12
+ - You're building a new component that sits on a surface — compose `Surface`, never hand-roll `bg-surface-raised … shadow-raised`.
13
+
14
+ Use `<Card>` instead when you want the gap-model padding rhythm and the `CardHeader`/`CardContent`/`CardFooter` slots. Use raw utilities for nothing — if it's a surface, it's a `Surface`.
15
+
16
+ ## Elevation — and which to pick
17
+
18
+ | `elevation` | Surface | Use for |
19
+ |---|---|---|
20
+ | `flat` | `bg-surface-raised`, no shadow | On-page tiles. Pair with `bordered` for an edge. |
21
+ | `raised` *(default)* | `bg-surface-raised` + `shadow-raised` | Cards, panels — anything sitting on the page. |
22
+ | `floating` | `bg-surface-overlay` + `shadow-floating` | Toasts, floating toolbars. |
23
+ | `overlay` | `bg-surface-overlay` + `shadow-overlay` | Popovers, menus, dialogs. |
24
+
25
+ ## Props
26
+
27
+ | Prop | Type | Default | Notes |
28
+ |---|---|---|---|
29
+ | `elevation` | `flat \| raised \| floating \| overlay` | `raised` | bg + shadow together. |
30
+ | `padding` | `none \| sm \| md \| lg` | `none` | Simple all-side: 0 / 12 / 16 / 24px. Not Card's gap model. |
31
+ | `radius` | `none \| control \| surface \| overlay \| pill` | `surface` | Maps `rounded-*` tokens. |
32
+ | `bordered` | `boolean` | `false` | Border-led edge. Only for `flat` — see Rules. |
33
+ | `asChild` | `boolean` | `false` | Render as the child element (Slot). |
34
+
35
+ Plus all native `div` props. Server-safe; forwards ref.
36
+
37
+ ## Examples
38
+
39
+ ```tsx
40
+ // Raised panel with padding
41
+ <Surface elevation="raised" padding="md">…</Surface>
42
+
43
+ // On-page, border-led tile (no shadow)
44
+ <Surface elevation="flat" bordered padding="sm">…</Surface>
45
+
46
+ // Overlay chrome
47
+ <Surface elevation="overlay" padding="sm" radius="overlay">…</Surface>
48
+
49
+ // The whole surface is a link — no wrapper node
50
+ <Surface asChild elevation="raised" padding="sm">
51
+ <a href="/upgrade">Upgrade to Pro</a>
52
+ </Surface>
53
+ ```
54
+
55
+ ## Rules
56
+
57
+ - **Edge OR elevation, never both.** `bordered` + a shadowed `elevation` is the double-edge anti-pattern (dev-warns). Use `elevation="flat" bordered`, or drop `bordered`.
58
+ - `flat` is still a surface (`bg-surface-raised`), not the page — a card without a shadow, not a hole in the page.
59
+ - Don't reach for `Surface` when you mean `Card` — if you're rebuilding header/content/footer spacing by hand, use `Card`.
60
+ - Padding is symmetric. For anything asymmetric, use `className` or `Card`.