@devalok/shilp-sutra 0.51.0 → 0.53.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +1 -1
- package/MIGRATION.md +12 -0
- package/dist/_chunks/motion-provider.js +7 -6
- package/dist/_chunks/motion-provider.js.map +1 -1
- package/dist/_chunks/success.js +53 -53
- package/dist/ai/command-bar.js +158 -158
- package/dist/ai/command-bar.js.map +1 -1
- package/dist/ai/conversation.js +5 -5
- package/dist/composed/command-palette.js +17 -17
- package/dist/composed/command-palette.js.map +1 -1
- package/dist/composed/priority-indicator.d.ts +15 -6
- package/dist/composed/priority-indicator.d.ts.map +1 -1
- package/dist/composed/priority-indicator.js +37 -88
- package/dist/composed/priority-indicator.js.map +1 -1
- package/dist/composed/schedule-view.d.ts +17 -2
- package/dist/composed/schedule-view.d.ts.map +1 -1
- package/dist/composed/schedule-view.js +163 -64
- package/dist/composed/schedule-view.js.map +1 -1
- package/dist/motion/motion-provider.d.ts.map +1 -1
- package/dist/shell/bottom-navbar.d.ts +32 -6
- package/dist/shell/bottom-navbar.d.ts.map +1 -1
- package/dist/shell/bottom-navbar.js +156 -129
- package/dist/shell/bottom-navbar.js.map +1 -1
- package/dist/tokens/primitives.css +6 -2
- package/dist/tokens/semantic.css +20 -1
- package/dist/tokens/utilities.css +6 -0
- package/dist/ui/autocomplete.d.ts +34 -32
- package/dist/ui/autocomplete.d.ts.map +1 -1
- package/dist/ui/autocomplete.js +115 -110
- package/dist/ui/autocomplete.js.map +1 -1
- package/dist/ui/button-group.js +2 -0
- package/dist/ui/button-group.js.map +1 -1
- package/dist/ui/button-processing.d.ts.map +1 -1
- package/dist/ui/button-processing.js +1 -0
- package/dist/ui/button-processing.js.map +1 -1
- package/dist/ui/button.d.ts +3 -3
- package/dist/ui/button.d.ts.map +1 -1
- package/dist/ui/button.js +26 -0
- package/dist/ui/button.js.map +1 -1
- package/dist/ui/color-input.d.ts.map +1 -1
- package/dist/ui/color-input.js +82 -82
- package/dist/ui/color-input.js.map +1 -1
- package/dist/ui/combobox.js +4 -4
- package/dist/ui/icon.d.ts +2 -0
- package/dist/ui/icon.d.ts.map +1 -1
- package/dist/ui/icon.js +31 -26
- package/dist/ui/icon.js.map +1 -1
- package/dist/ui/index.js +30 -30
- package/dist/ui/search-input.d.ts.map +1 -1
- package/dist/ui/search-input.js +5 -4
- package/dist/ui/search-input.js.map +1 -1
- package/dist/ui/segmented-control.d.ts +28 -6
- package/dist/ui/segmented-control.d.ts.map +1 -1
- package/dist/ui/segmented-control.js +61 -43
- package/dist/ui/segmented-control.js.map +1 -1
- package/dist/ui/sidebar.js +7 -7
- package/dist/ui/split-button.d.ts.map +1 -1
- package/dist/ui/split-button.js +7 -0
- package/dist/ui/split-button.js.map +1 -1
- package/docs/components/composed/priority-indicator.md +22 -11
- package/docs/components/composed/schedule-view.md +20 -5
- package/docs/components/shell/bottom-navbar.md +24 -5
- package/docs/components/ui/autocomplete.md +26 -10
- package/docs/components/ui/button.md +2 -2
- package/docs/components/ui/segmented-control.md +31 -11
- package/docs/recipes/install-remix.md +3 -3
- package/docs/recipes/install-vite.md +3 -3
- package/docs/recipes/server-components.md +2 -2
- package/llms.txt +1 -1
- package/make-kit/foundations/color.md +20 -0
- package/make-kit/foundations/dark-mode.md +6 -9
- package/make-kit/foundations/icons.md +3 -3
- package/make-kit/setup.md +4 -4
- package/mcp-manifest.json +239 -35
- package/package.json +1 -1
- package/scripts/welcome.mjs +59 -1
- package/skill/SKILL.md +1 -1
- package/skill/references/components.md +1 -1
- package/skill/references/server-components.md +2 -2
- package/skill/references/setup-remix.md +3 -3
- package/skill/references/setup-vite.md +3 -3
|
@@ -6,16 +6,23 @@
|
|
|
6
6
|
|
|
7
7
|
## Props
|
|
8
8
|
currentPath?: string (optional)
|
|
9
|
-
user?: BottomNavbarUser | null (optional)
|
|
9
|
+
user?: BottomNavbarUser | null (drives per-item role gating, optional)
|
|
10
10
|
primaryItems?: BottomNavItem[] (max 4 recommended, optional)
|
|
11
|
-
moreItems?: BottomNavItem[] (overflow items in "More"
|
|
11
|
+
moreItems?: BottomNavItem[] (overflow items in "More" sheet, optional)
|
|
12
|
+
indicator?: 'pill' | 'underline' | 'tint' | 'none' (active-item indicator; default 'pill')
|
|
13
|
+
labelVisibility?: 'always' | 'selected'
|
|
12
14
|
className?: string
|
|
13
15
|
|
|
14
|
-
BottomNavItem: { title: string, href: string, icon:
|
|
16
|
+
BottomNavItem: { title: string, href: string, icon: IconInput, activeIcon?: IconInput, exact?: boolean, badge?: number, roles?: string[], canView?: (user: BottomNavbarUser | null) => boolean }
|
|
15
17
|
BottomNavbarUser: { name: string, role?: string }
|
|
16
18
|
|
|
17
19
|
## Defaults
|
|
18
|
-
|
|
20
|
+
currentPath: '/'
|
|
21
|
+
user: null
|
|
22
|
+
primaryItems: []
|
|
23
|
+
moreItems: []
|
|
24
|
+
indicator: 'pill'
|
|
25
|
+
labelVisibility: 'always'
|
|
19
26
|
|
|
20
27
|
## Example
|
|
21
28
|
```jsx
|
|
@@ -37,7 +44,11 @@ BottomNavbarUser: { name: string, role?: string }
|
|
|
37
44
|
```
|
|
38
45
|
- **Primary vs overflow:** `primaryItems` (max 4) for the always-visible slots; `moreItems` go into a "More" sheet that opens on tap. Don't exceed 4 primary — the bar becomes cramped.
|
|
39
46
|
- **Router integration via LinkProvider:** Each nav item is rendered using the framework-specific Link component registered in LinkProvider. Without LinkProvider, you get full-page reloads on tap.
|
|
40
|
-
- **Badge numbers** cap at 99+ (
|
|
47
|
+
- **Badge numbers** cap at 99+ (composes the `Badge` primitive).
|
|
48
|
+
- **Role gating:** each item may declare `roles: string[]` (shown only when `user.role` matches) or a `canView(user)` predicate for arbitrary logic (`canView` wins). Items with neither are always visible. Gating applies to both `primaryItems` and `moreItems`.
|
|
49
|
+
- **Overflow sheet:** the "More" surface is the DS `Sheet` (`side="bottom"`) — it inherits focus trap, scroll lock, return-focus, and `aria-modal`; the trigger is wired with `aria-haspopup`/`aria-controls` automatically.
|
|
50
|
+
- **Indicator (animated):** the active indicator slides to the selected item (shared-element `layoutId`) and fades in on first appearance. Modes: `pill` (default, Material-3 tonal pill behind the icon), `underline` (top accent bar), `tint` (subtle bg on the whole active cell), `none` (no shape — pair with `activeIcon` for the iOS filled-icon look). `labelVisibility="selected"` shows labels only for the active item.
|
|
51
|
+
- **Filled-when-active:** set `activeIcon` on an item (e.g. a Tabler `*Filled` variant) to swap the icon for a filled version while it's the active route; falls back to `icon`.
|
|
41
52
|
- **Not for desktop:** The viewport-fixed positioning + touch-optimized sizing don't translate well to desktop. Hide behind `md:hidden`.
|
|
42
53
|
|
|
43
54
|
## Gotchas
|
|
@@ -47,6 +58,14 @@ BottomNavbarUser: { name: string, role?: string }
|
|
|
47
58
|
- Requires LinkProvider for framework-specific link components (e.g., Next.js Link)
|
|
48
59
|
|
|
49
60
|
## Changes
|
|
61
|
+
### v0.53.0
|
|
62
|
+
- **Changed** Overflow "More" menu re-founded on the DS `Sheet` primitive — inherits focus trap, scroll lock, return-focus, `aria-modal`, and trigger↔panel ARIA wiring (was a hand-rolled `role="dialog"` with none of these). Composes `Badge` for notification counts and the Sheet's built-in close (≥ touch target).
|
|
63
|
+
- **Added** Per-item role gating: `roles?: string[]` and `canView?: (user) => boolean` on `BottomNavItem`. The previously-inert `user` prop now drives it.
|
|
64
|
+
- **Added** `activeIcon` per item — a filled/alternate icon shown while active (falls back to `icon`). Tightened the icon lozenge padding so icon-only items read less airy.
|
|
65
|
+
- **Added** `indicator` (default **`pill`** — Material-3; plus `underline`, `tint`, `none`) and `labelVisibility` ('always' | 'selected'). The active indicator animates (slides) between items via a shared-element `layoutId`.
|
|
66
|
+
- **Added** Label truncation + logical (RTL-safe) properties; overflow grid adapts to item count.
|
|
67
|
+
- **Fixed** Notification badge `zoom-in` animation now reduced-motion gated.
|
|
68
|
+
|
|
50
69
|
### v0.19.0
|
|
51
70
|
- **Changed** Background elevated from `bg-surface-1` to `bg-surface-2` for visual hierarchy above app background
|
|
52
71
|
- **Changed** "More" menu and interactive items bumped accordingly
|
|
@@ -6,16 +6,23 @@
|
|
|
6
6
|
|
|
7
7
|
## Props
|
|
8
8
|
options: AutocompleteOption[] (REQUIRED) — { value: string, label: string }
|
|
9
|
-
value
|
|
9
|
+
value?: AutocompleteOption | null (controlled)
|
|
10
|
+
defaultValue?: AutocompleteOption | null (uncontrolled initial)
|
|
10
11
|
onValueChange?: (option: AutocompleteOption) => void
|
|
11
|
-
placeholder
|
|
12
|
-
emptyText
|
|
13
|
-
disabled
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
placeholder?: string
|
|
13
|
+
emptyText?: string (default: "No options")
|
|
14
|
+
disabled?: boolean
|
|
15
|
+
size?: (forwarded to Input)
|
|
16
|
+
state?: 'default' | 'error' | 'warning' | 'success' (forwarded to Input)
|
|
17
|
+
isLoading?: boolean
|
|
18
|
+
loadingText?: string (default: "Loading…")
|
|
19
|
+
renderOption?: (option, query) => ReactNode
|
|
20
|
+
className?: string
|
|
21
|
+
id?: string
|
|
16
22
|
|
|
17
23
|
## Defaults
|
|
18
24
|
emptyText="No options"
|
|
25
|
+
loadingText="Loading…"
|
|
19
26
|
|
|
20
27
|
## Example
|
|
21
28
|
```jsx
|
|
@@ -28,17 +35,26 @@
|
|
|
28
35
|
```
|
|
29
36
|
|
|
30
37
|
## Composability
|
|
31
|
-
- **
|
|
32
|
-
- **
|
|
33
|
-
- **
|
|
38
|
+
- **Composes `Input`** — the field is the DS `Input`, so it inherits `size`, error/`state` painting, read-only, hover, and FormField wiring. Autocomplete owns only the dropdown + behavior.
|
|
39
|
+
- **Autocomplete vs Combobox:** Autocomplete allows free-text input (users can type anything); Combobox enforces selection from the list.
|
|
40
|
+
- **Value shape is an object** (`{ value, label }`), not a plain string. Controlled via `value`, or uncontrolled via `defaultValue`.
|
|
41
|
+
- **FormField:** auto-consumes FormField state (via the composed Input) — inside a FormField, error border + `aria-invalid`/`aria-describedby`/`required` are wired automatically. Pass `state` to override.
|
|
42
|
+
- **Async / "type to search":** set `isLoading` to show a spinner (in the field + the listbox) with `loadingText`.
|
|
43
|
+
- **Matched-text highlight:** the query substring is bolded in each option by default; override the whole row with `renderOption`.
|
|
34
44
|
- **Portal rendering:** Dropdown portals to body with z-popover (1400) — stacks above Dialog/Sheet.
|
|
35
|
-
- **Keyboard:** ArrowDown/Up navigate
|
|
45
|
+
- **Keyboard:** ArrowDown/Up/Home/End navigate, Enter selects, Esc closes.
|
|
36
46
|
|
|
37
47
|
## Gotchas
|
|
38
48
|
- Allows free-text input (no forced selection) — use Combobox for forced selection
|
|
39
49
|
- value is an object { value, label }, NOT just a string
|
|
50
|
+
- Client-side filtering only (known list). For huge/remote lists, drive `options` yourself with `isLoading` — no built-in virtualization.
|
|
40
51
|
|
|
41
52
|
## Changes
|
|
53
|
+
### v0.53.0
|
|
54
|
+
- **Changed** Re-parented onto the DS `Input` primitive — inherits `size`, error/`state` painting, read-only, hover, and FormField auto-consumption (previously re-rolled its own `<input>` and read FormField error but never painted it).
|
|
55
|
+
- **Added** `defaultValue` (uncontrolled), `size`, `state`, `isLoading`/`loadingText` (async), `renderOption`, and matched-substring highlighting.
|
|
56
|
+
- **Fixed** Doc corrected — it DOES auto-consume FormField (via Input). Dropped a keystroke-frequency stagger animation + a dead effect.
|
|
57
|
+
|
|
42
58
|
### v0.18.0
|
|
43
59
|
- **Fixed** Added `useEffect` to sync query when external value changes
|
|
44
60
|
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
## Props
|
|
8
8
|
variant: "solid" | "soft" | "outline" | "ghost" | "link"
|
|
9
|
-
color: "accent" | "error" | "success" | "warning" | "neutral"
|
|
9
|
+
color: "accent" | "error" | "success" | "warning" | "info" | "neutral"
|
|
10
10
|
size: "xs" | "sm" | "md" | "lg" | "compact-xs" | "compact-sm" | "compact-md" | "icon" | "icon-xs" | "icon-sm" | "icon-md" | "icon-lg"
|
|
11
11
|
weight: "semibold" | "normal"
|
|
12
12
|
shape: "default" | "pill"
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
fullWidth: boolean
|
|
18
18
|
asChild: boolean
|
|
19
19
|
processing: boolean | 'ambient' | 'working' | 'urgent' (marching ants SVG border)
|
|
20
|
-
processingColor: 'accent' | 'error' | 'success' | 'warning' | 'neutral' (override animation color)
|
|
20
|
+
processingColor: 'accent' | 'error' | 'success' | 'warning' | 'info' | 'neutral' (override animation color)
|
|
21
21
|
processingDisabled: boolean (disable button during processing, default: true)
|
|
22
22
|
onClickAsync: (e: MouseEvent) => Promise<void> (auto loading->success/error->idle, auto-activates processing)
|
|
23
23
|
asyncFeedbackDuration: number (ms, default 1500)
|
|
@@ -6,32 +6,37 @@
|
|
|
6
6
|
|
|
7
7
|
## Props
|
|
8
8
|
size: "sm" | "md" | "lg"
|
|
9
|
-
variant: "
|
|
9
|
+
variant: "soft" | "solid"
|
|
10
10
|
options: SegmentedControlOption[] (REQUIRED)
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
value: string // controlled
|
|
12
|
+
defaultValue: string // uncontrolled initial
|
|
13
|
+
onValueChange: (id: string) => void
|
|
13
14
|
disabled: boolean
|
|
15
|
+
fullWidth: boolean
|
|
16
|
+
selectedId: string // @deprecated — use value
|
|
17
|
+
onSelect: (id: string) => void // @deprecated — use onValueChange
|
|
14
18
|
|
|
15
19
|
## Types
|
|
16
|
-
SegmentedControlOption = { id: string, text
|
|
20
|
+
SegmentedControlOption = { id: string, text?: React.ReactNode, icon?: IconInput, ariaLabel?: string }
|
|
17
21
|
SegmentedControlSize = 'sm' | 'md' | 'lg'
|
|
18
|
-
SegmentedControlVariant = '
|
|
22
|
+
SegmentedControlVariant = 'soft' | 'solid'
|
|
19
23
|
|
|
20
24
|
## Defaults
|
|
21
25
|
size: "md"
|
|
22
|
-
variant: "
|
|
26
|
+
variant: "soft"
|
|
27
|
+
fullWidth: false
|
|
23
28
|
|
|
24
29
|
## Example
|
|
25
30
|
```jsx
|
|
26
31
|
<SegmentedControl
|
|
27
32
|
size="md"
|
|
28
|
-
variant="
|
|
33
|
+
variant="soft"
|
|
29
34
|
options={[
|
|
30
35
|
{ id: 'list', text: 'List' },
|
|
31
36
|
{ id: 'grid', text: 'Grid' },
|
|
32
37
|
]}
|
|
33
|
-
|
|
34
|
-
|
|
38
|
+
value={viewMode}
|
|
39
|
+
onValueChange={setViewMode}
|
|
35
40
|
/>
|
|
36
41
|
```
|
|
37
42
|
|
|
@@ -39,15 +44,30 @@
|
|
|
39
44
|
- **Data-driven, not compound** — unlike Tabs/ToggleGroup, SegmentedControl takes an `options` array rather than children. This makes it easier to render from a list but harder to customize per-option styling; use Tabs if you need compound children.
|
|
40
45
|
- **When to use vs Tabs:** SegmentedControl is for mutually-exclusive VIEW-MODE toggles (List/Grid/Kanban) — short labels, no associated content panel. Tabs is for content switching where each tab has a corresponding TabsContent. SegmentedControl renders `role="radiogroup"` with `role="radio"` segments (a panel-less single-select); Tabs renders `role="tablist"`.
|
|
41
46
|
- **Option icons** auto-size based on the `size` prop — don't set explicit icon sizes.
|
|
42
|
-
-
|
|
47
|
+
- **`fullWidth`** switches segments from content-hug (default) to equal-fill: each segment takes an equal share of the container (a 2-item toggle splits 50/50, a 3-item switcher gives each a third). Use for view switchers and toolbar toggles that should fill their column; leave off for compact inline toolbars.
|
|
48
|
+
- **Visual model:** a rounded-rect track (not a full pill) — a translucent recessed groove with a single soft-shadowed sliding thumb. The track has no border/inset shadow; the thumb carries the only edge. Elevation inverts in dark so the groove stays visible.
|
|
49
|
+
- **Controlled or uncontrolled** — pass `value` + `onValueChange` to control it, or `defaultValue` (optional; falls back to the first option) to let it own state. Matches the Tabs/ToggleGroup vocabulary. `selectedId`/`onSelect` are deprecated aliases that still work.
|
|
50
|
+
- **Option labels accept `ReactNode`** — a segment can hold a count badge or custom node, not just a string. `text` is optional: omit it for an **icon-only** segment and set `ariaLabel` so the segment still has an accessible name.
|
|
51
|
+
- **Touch targets** — each segment has a 44px minimum hit area (via `touch-target`) even though the visual height stays dense.
|
|
52
|
+
- **RTL** — Arrow-key navigation tracks reading order: in a right-to-left context `ArrowLeft` moves to the next option and `ArrowRight` to the previous (detected from the nearest `dir` attribute).
|
|
43
53
|
- Built from scratch (no Radix primitive) — standard HTML buttons with `role="radio"` + `aria-checked` and roving tabindex.
|
|
44
54
|
|
|
45
55
|
## Gotchas
|
|
46
|
-
- Controlled
|
|
56
|
+
- Controlled (`value`) or uncontrolled (`defaultValue`) — `selectedId`/`onSelect` are deprecated aliases
|
|
47
57
|
- Uses data-driven API (options prop), not compound children
|
|
48
58
|
- Use Tabs (not SegmentedControl) when you need associated content panels per option
|
|
49
59
|
|
|
50
60
|
## Changes
|
|
61
|
+
### v0.52.0
|
|
62
|
+
- **Changed** Visual rebuild — rounded-rect track (was full pill), translucent recessed track with no border/inset, single ring-less soft-shadow thumb. Dark-mode elevation inverts so the groove stays visible. New tokens: `--color-segment-track`, `--color-segment-thumb`, `--shadow-segment`.
|
|
63
|
+
- **Added** `value` / `defaultValue` / `onValueChange` — canonical controlled+uncontrolled API (aligns with Tabs/ToggleGroup).
|
|
64
|
+
- **Added** `fullWidth` prop — segments split the container equally.
|
|
65
|
+
- **Added** 44px minimum touch targets (`touch-target`), keeping dense visual height.
|
|
66
|
+
- **Added** Crisp bounce-free thumb motion (reduced-motion aware) + `motion-safe` press-scale feedback.
|
|
67
|
+
- **Changed** Option `text` widened from `string` to `ReactNode`, and made optional (omit for icon-only segments).
|
|
68
|
+
- **Added** `ariaLabel` per option for icon-only segments; RTL-aware Arrow-key navigation.
|
|
69
|
+
- **Deprecated** `variant="default"` → `variant="soft"`; `selectedId` → `value`; `onSelect` → `onValueChange`. All old names still accepted as aliases; update call sites.
|
|
70
|
+
|
|
51
71
|
### v0.38.0
|
|
52
72
|
- **Removed** (BREAKING) deprecated `variant="accent"` alias. Use `variant="solid"`.
|
|
53
73
|
|
|
@@ -127,10 +127,10 @@ For runtime toggling inside React components, use `useColorMode`:
|
|
|
127
127
|
import { useColorMode } from "@devalok/shilp-sutra/hooks/use-color-mode";
|
|
128
128
|
|
|
129
129
|
export function ThemeToggle() {
|
|
130
|
-
const {
|
|
130
|
+
const { colorMode, toggleColorMode } = useColorMode();
|
|
131
131
|
return (
|
|
132
|
-
<button onClick={
|
|
133
|
-
{
|
|
132
|
+
<button onClick={toggleColorMode} aria-label="Toggle theme">
|
|
133
|
+
{colorMode === "dark" ? "☀" : "☾"}
|
|
134
134
|
</button>
|
|
135
135
|
);
|
|
136
136
|
}
|
|
@@ -121,10 +121,10 @@ Wire the runtime hook from anywhere in the app (e.g., a header button):
|
|
|
121
121
|
import { useColorMode } from "@devalok/shilp-sutra/hooks/use-color-mode";
|
|
122
122
|
|
|
123
123
|
export function ThemeToggle() {
|
|
124
|
-
const {
|
|
124
|
+
const { colorMode, toggleColorMode } = useColorMode();
|
|
125
125
|
return (
|
|
126
|
-
<button onClick={
|
|
127
|
-
{
|
|
126
|
+
<button onClick={toggleColorMode} aria-label="Toggle theme">
|
|
127
|
+
{colorMode === "dark" ? "☀" : "☾"}
|
|
128
128
|
</button>
|
|
129
129
|
);
|
|
130
130
|
}
|
|
@@ -151,8 +151,8 @@ Both `<Suspense>` and `<LoadingSkeleton>` are server-safe.
|
|
|
151
151
|
import { useColorMode } from "@devalok/shilp-sutra/hooks/use-color-mode";
|
|
152
152
|
|
|
153
153
|
export default function ServerPage() {
|
|
154
|
-
const {
|
|
155
|
-
return <div>{
|
|
154
|
+
const { colorMode } = useColorMode(); // breaks
|
|
155
|
+
return <div>{colorMode}</div>;
|
|
156
156
|
}
|
|
157
157
|
```
|
|
158
158
|
|
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.
|
|
3
|
+
> Radix UI + Tailwind 4 (CSS-first) + CVA design system for Devalok apps, v0.53.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
|
|
|
@@ -103,6 +103,26 @@ Available as `accent-1` … `accent-12`. Apply via `bg-accent-N`, `text-accent-N
|
|
|
103
103
|
|
|
104
104
|
The same 12-step pattern repeats for `error-*`, `success-*`, `warning-*`, `info-*` (subset: 2, 3, 4, 5, 6, 7, 9, 10, 11).
|
|
105
105
|
|
|
106
|
+
## Component `color` prop — support matrix
|
|
107
|
+
|
|
108
|
+
The `color` prop accepts a different set per component, because each serves a different job. **The shared intent set — safe to pass to all three from one token — is `accent` · `error` · `success` · `warning` · `info` · `neutral`.**
|
|
109
|
+
|
|
110
|
+
| color | Button | Card | Badge |
|
|
111
|
+
|---|:---:|:---:|:---:|
|
|
112
|
+
| `accent` | ✅ | ✅ | ✅ |
|
|
113
|
+
| `error` | ✅ | ✅ | ✅ |
|
|
114
|
+
| `success` | ✅ | ✅ | ✅ |
|
|
115
|
+
| `warning` | ✅ | ✅ | ✅ |
|
|
116
|
+
| `info` | ✅ | ✅ | ✅ |
|
|
117
|
+
| `neutral` | ✅ | ✅ | ✅ |
|
|
118
|
+
| `default` | — | ✅ | ✅ |
|
|
119
|
+
| `teal` `amber` `slate` `indigo` `cyan` `orange` `emerald` | — | — | ✅ |
|
|
120
|
+
| `custom` (`--badge-color`) | — | — | ✅ |
|
|
121
|
+
|
|
122
|
+
- **Button / Card** carry the six **semantic intents** — they communicate *state or emphasis* (a destructive action, a warning panel). That set is aligned across both.
|
|
123
|
+
- **Badge** adds a **category palette** (`teal`, `amber`, `indigo`, …) plus `custom`, because badges label/categorize many peer items where hue is a taxonomy, not a status. Those category hues are intentionally **not** on Button/Card — a "teal primary button" would read as an intent that doesn't exist.
|
|
124
|
+
- To tint a Card + Badge + Button set from one variable, constrain the token to the shared intent set above.
|
|
125
|
+
|
|
106
126
|
## Theming — never hardcode
|
|
107
127
|
|
|
108
128
|
Consumers swap the accent by overriding `--color-accent-1` through `--color-accent-12` in a `:root { }` block placed **after** the kit's CSS import. Dark mode is derived algorithmically — no separate dark overrides needed.
|
|
@@ -18,17 +18,14 @@ Dark tokens are **algorithmically derived** from the OKLCH primitives, not hand-
|
|
|
18
18
|
import { useColorMode } from '@devalok/shilp-sutra/hooks/use-color-mode'
|
|
19
19
|
|
|
20
20
|
function ThemeToggle() {
|
|
21
|
-
const {
|
|
22
|
-
//
|
|
23
|
-
//
|
|
21
|
+
const { colorMode, setColorMode, toggleColorMode } = useColorMode()
|
|
22
|
+
// colorMode: 'light' | 'dark' | 'system'
|
|
23
|
+
// setColorMode(next): set an explicit mode · toggleColorMode(): flip light↔dark
|
|
24
24
|
|
|
25
25
|
return (
|
|
26
|
-
<Button
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
>
|
|
30
|
-
<Icon icon={resolvedMode === 'dark' ? IconSun : IconMoon} />
|
|
31
|
-
{resolvedMode === 'dark' ? 'Light' : 'Dark'}
|
|
26
|
+
<Button variant="soft" onClick={toggleColorMode}>
|
|
27
|
+
<Icon icon={colorMode === 'dark' ? IconSun : IconMoon} />
|
|
28
|
+
{colorMode === 'dark' ? 'Light' : 'Dark'}
|
|
32
29
|
</Button>
|
|
33
30
|
)
|
|
34
31
|
}
|
|
@@ -17,7 +17,7 @@ import { Icon } from '@devalok/shilp-sutra/ui/icon'
|
|
|
17
17
|
import { IconHome, IconUser, IconSettings } from '@tabler/icons-react'
|
|
18
18
|
|
|
19
19
|
<Icon icon={IconHome} />
|
|
20
|
-
<Icon icon={IconUser} size=
|
|
20
|
+
<Icon icon={IconUser} size="lg" />
|
|
21
21
|
<Icon icon={IconSettings} className="text-fg-muted" />
|
|
22
22
|
```
|
|
23
23
|
|
|
@@ -55,7 +55,7 @@ Don't add `className="h-4 w-4"` on every Icon. Wrap a subtree:
|
|
|
55
55
|
```tsx
|
|
56
56
|
import { IconProvider } from '@devalok/shilp-sutra/ui/icon-context'
|
|
57
57
|
|
|
58
|
-
<IconProvider size=
|
|
58
|
+
<IconProvider size="sm">
|
|
59
59
|
<NavSection>
|
|
60
60
|
<Icon icon={IconHome} />
|
|
61
61
|
<Icon icon={IconUser} />
|
|
@@ -64,7 +64,7 @@ import { IconProvider } from '@devalok/shilp-sutra/ui/icon-context'
|
|
|
64
64
|
</IconProvider>
|
|
65
65
|
```
|
|
66
66
|
|
|
67
|
-
Override per-icon with `<Icon icon={...} size=
|
|
67
|
+
Override per-icon with `<Icon icon={...} size="xl" />`.
|
|
68
68
|
|
|
69
69
|
Default sizes by component context:
|
|
70
70
|
|
package/make-kit/setup.md
CHANGED
|
@@ -53,7 +53,7 @@ import { IconProvider } from '@devalok/shilp-sutra/ui/icon-context'
|
|
|
53
53
|
export default function App({ children }) {
|
|
54
54
|
return (
|
|
55
55
|
<MotionProvider reducedMotion="user">
|
|
56
|
-
<IconProvider size=
|
|
56
|
+
<IconProvider size="sm">
|
|
57
57
|
{children}
|
|
58
58
|
<Toaster />
|
|
59
59
|
</IconProvider>
|
|
@@ -63,7 +63,7 @@ export default function App({ children }) {
|
|
|
63
63
|
```
|
|
64
64
|
|
|
65
65
|
- `MotionProvider` — required. `reducedMotion="user"` respects OS preference. Without this provider, motion primitives still work but reduced-motion is ignored.
|
|
66
|
-
- `IconProvider` — optional but recommended. Sets default icon size for all `<Icon>` children. Override per-call with `<Icon size=
|
|
66
|
+
- `IconProvider` — optional but recommended. Sets default icon size for all `<Icon>` children. Size is a tier (`"xs" | "sm" | "md" | "lg" | "xl" | "2xl"`), not a pixel number. Override per-call with `<Icon size="lg" />`.
|
|
67
67
|
- `Toaster` — only needed if the app calls `toast(...)`. Mount exactly once.
|
|
68
68
|
|
|
69
69
|
## Dark mode toggle
|
|
@@ -74,9 +74,9 @@ Add the `.dark` class on `<html>` or `<body>` to flip the entire token system to
|
|
|
74
74
|
import { useColorMode } from '@devalok/shilp-sutra/hooks/use-color-mode'
|
|
75
75
|
|
|
76
76
|
function ThemeToggle() {
|
|
77
|
-
const {
|
|
77
|
+
const { colorMode, setColorMode } = useColorMode() // 'light' | 'dark' | 'system'
|
|
78
78
|
return (
|
|
79
|
-
<Button onClick={() =>
|
|
79
|
+
<Button onClick={() => setColorMode(colorMode === 'dark' ? 'light' : 'dark')}>
|
|
80
80
|
Toggle theme
|
|
81
81
|
</Button>
|
|
82
82
|
)
|