@devalok/shilp-sutra 0.52.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.
Files changed (41) hide show
  1. package/MIGRATION.md +6 -0
  2. package/dist/_chunks/motion-provider.js +7 -6
  3. package/dist/_chunks/motion-provider.js.map +1 -1
  4. package/dist/_chunks/success.js +53 -53
  5. package/dist/ai/command-bar.js +158 -158
  6. package/dist/ai/command-bar.js.map +1 -1
  7. package/dist/ai/conversation.js +5 -5
  8. package/dist/composed/command-palette.js +17 -17
  9. package/dist/composed/command-palette.js.map +1 -1
  10. package/dist/composed/priority-indicator.d.ts +15 -6
  11. package/dist/composed/priority-indicator.d.ts.map +1 -1
  12. package/dist/composed/priority-indicator.js +37 -88
  13. package/dist/composed/priority-indicator.js.map +1 -1
  14. package/dist/composed/schedule-view.d.ts +17 -2
  15. package/dist/composed/schedule-view.d.ts.map +1 -1
  16. package/dist/composed/schedule-view.js +163 -64
  17. package/dist/composed/schedule-view.js.map +1 -1
  18. package/dist/motion/motion-provider.d.ts.map +1 -1
  19. package/dist/shell/bottom-navbar.d.ts +32 -6
  20. package/dist/shell/bottom-navbar.d.ts.map +1 -1
  21. package/dist/shell/bottom-navbar.js +156 -129
  22. package/dist/shell/bottom-navbar.js.map +1 -1
  23. package/dist/tokens/primitives.css +6 -2
  24. package/dist/tokens/utilities.css +6 -0
  25. package/dist/ui/autocomplete.d.ts +34 -32
  26. package/dist/ui/autocomplete.d.ts.map +1 -1
  27. package/dist/ui/autocomplete.js +115 -110
  28. package/dist/ui/autocomplete.js.map +1 -1
  29. package/dist/ui/combobox.js +4 -4
  30. package/dist/ui/index.js +30 -30
  31. package/dist/ui/search-input.js +4 -4
  32. package/dist/ui/sidebar.js +7 -7
  33. package/docs/components/composed/priority-indicator.md +22 -11
  34. package/docs/components/composed/schedule-view.md +20 -5
  35. package/docs/components/shell/bottom-navbar.md +24 -5
  36. package/docs/components/ui/autocomplete.md +26 -10
  37. package/llms.txt +1 -1
  38. package/mcp-manifest.json +179 -26
  39. package/package.json +1 -1
  40. package/skill/SKILL.md +1 -1
  41. package/skill/references/components.md +1 -1
@@ -10,14 +10,19 @@
10
10
  events: ScheduleEvent[] (REQUIRED) — { id, title, start: Date, end: Date, color? }
11
11
  onEventClick?: (event: ScheduleEvent) => void
12
12
  onSlotClick?: (start: Date, end: Date) => void
13
- startHour: number (default: 8)
14
- endHour: number (default: 18, exclusive)
15
- slotDuration: number (minutes, default: 30)
13
+ startHour?: number (default: 8)
14
+ endHour?: number (default: 18, exclusive)
15
+ slotDuration?: number (minutes, default: 30)
16
+ selectedEventId?: string (rings the active event)
17
+ renderEvent?: (event) => ReactNode (custom event body)
18
+ header?: ReactNode (toolbar slot above the grid)
19
+ emptyState?: ReactNode (shown when events is empty)
20
+ height?: number | string (grid body height, default 480)
16
21
 
17
22
  Event colors: "accent" | "success" | "warning" | "error" | "info" | "neutral"
18
23
 
19
24
  ## Defaults
20
- startHour=8, endHour=18, slotDuration=30
25
+ startHour=8, endHour=18, slotDuration=30, height=480
21
26
 
22
27
  ## Example
23
28
  ```jsx
@@ -32,7 +37,12 @@ Event colors: "accent" | "success" | "warning" | "error" | "info" | "neutral"
32
37
  ## Composability
33
38
  - **Day / Week calendar view** for time-block display (meetings, shifts, availability). Not a full calendar app — no month view, no drag-to-create.
34
39
  - **Event data is consumer-owned:** You pass `events` as an array; ScheduleView doesn't fetch, doesn't cache, doesn't expand recurring events. All scheduling logic lives in your app.
35
- - **Event click + slot click** — `onEventClick` for existing events; `onSlotClick` for creating new events (fires with start/end of the empty slot).
40
+ - **Event click + slot click** — `onEventClick` for existing events; `onSlotClick` for creating new events (fires with start/end of the empty slot). **Slots are only interactive (focusable + keyboard-navigable) when `onSlotClick` is set** — otherwise they render as inert grid lines, so a read-only schedule adds no keyboard/AT tab stops.
41
+ - **Keyboard (interactive slots):** roving tabindex — Arrow keys move between slots (up/down within a day, left/right across days, RTL-aware), Home/End jump within the day; only one slot is in the tab order at a time.
42
+ - **Overlapping events** are partitioned into side-by-side columns automatically so double-booked times stay legible.
43
+ - **Live now-line** ticks every minute and scrolls into view on mount.
44
+ - **`renderEvent`** customizes the event block body; **`header`** adds a toolbar; **`selectedEventId`** rings the active event; **`emptyState`** shows when there are no events.
45
+ - **Composes the DS card shell tokens** (`surface-2` + `rounded-surface` + border) and is RTL-safe (logical properties throughout).
36
46
  - **Color vocabulary matches the DS** — `accent/success/warning/error/info/neutral`. Map your event types to these at the data layer.
37
47
  - **endHour is exclusive:** `endHour=18` means the last visible slot starts at 17:30 (with 30min slots). Match your UX expectation: 9-5 typically means `startHour=9, endHour=18`.
38
48
  - **Pairs with date-picker/composed** — use DatePicker or DateRangePicker to choose which date to show; pass that as ScheduleView's `date`.
@@ -43,6 +53,11 @@ Event colors: "accent" | "success" | "warning" | "error" | "info" | "neutral"
43
53
  - Events that span outside `startHour`/`endHour` may be clipped
44
54
 
45
55
  ## Changes
56
+ ### v0.53.0
57
+ - **Changed** Read-only schedules no longer flood the tab order — slots are interactive only when `onSlotClick` is set; otherwise inert grid lines. Interactive slots use roving tabindex + Arrow/Home/End keyboard navigation (RTL-aware).
58
+ - **Changed** Overlapping events now lay out in side-by-side columns instead of stacking illegibly. The now-line ticks live (per-minute) and scrolls into view; shell uses the `surface-2` card tier (fixed the prior `surface-raised` + dead-`border-card-strong` regression); layout uses logical (RTL-safe) properties; magic-number sizes tokenized.
59
+ - **Added** `selectedEventId`, `renderEvent`, `header`, `emptyState`, and `height` props.
60
+
46
61
  ### v0.49.0
47
62
  - **BREAKING** `ScheduleEvent.color` value `"primary"` renamed `"accent"` (DS colour vocabulary). It was the default, so untyped events are unaffected.
48
63
  - **Added** keyboard focus rings on slot cells + event blocks; current-time indicator uses the shared `<Dot>`.
@@ -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" menu, optional)
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: ReactNode, exact?: boolean, badge?: number }
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
- None
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+ (same as BadgeIndicator pattern).
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: AutocompleteOption | null
9
+ value?: AutocompleteOption | null (controlled)
10
+ defaultValue?: AutocompleteOption | null (uncontrolled initial)
10
11
  onValueChange?: (option: AutocompleteOption) => void
11
- placeholder: string
12
- emptyText: string (default: "No options")
13
- disabled: boolean
14
- className: string
15
- id: string
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
- - **Autocomplete vs Combobox:** Autocomplete allows free-text input (users can type anything); Combobox enforces selection from the list. Pick by whether "off-list" values are valid (e.g. city field that accepts typos → Autocomplete; tag picker from a fixed vocabulary → Combobox).
32
- - **Value shape is an object** (`{ value, label }`), not a plain string this preserves label/value decoupling for display-vs-storage.
33
- - **FormField:** Does NOT auto-consume FormField state. Set explicit error styling via className if needed.
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 suggestions, Enter selects, Esc closes. Typeahead is the input's native filtering.
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
 
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.52.0.
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
 
package/mcp-manifest.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "$schema": "./mcp-manifest.schema.json",
3
3
  "manifestVersion": "1.2.0",
4
4
  "package": "@devalok/shilp-sutra",
5
- "packageVersion": "0.52.0",
5
+ "packageVersion": "0.53.0",
6
6
  "components": {
7
7
  "accordion": {
8
8
  "displayName": "Accordion",
@@ -386,7 +386,16 @@
386
386
  "name": "union",
387
387
  "raw": "AutocompleteOption | null"
388
388
  },
389
- "required": false
389
+ "required": false,
390
+ "description": "controlled"
391
+ },
392
+ "defaultValue": {
393
+ "type": {
394
+ "name": "union",
395
+ "raw": "AutocompleteOption | null"
396
+ },
397
+ "required": false,
398
+ "description": "uncontrolled initial"
390
399
  },
391
400
  "onValueChange": {
392
401
  "type": {
@@ -414,6 +423,46 @@
414
423
  },
415
424
  "required": false
416
425
  },
426
+ "size": {
427
+ "type": {
428
+ "name": "object",
429
+ "raw": "(forwarded to Input)"
430
+ },
431
+ "required": false
432
+ },
433
+ "state": {
434
+ "type": {
435
+ "name": "enum",
436
+ "value": [
437
+ "default",
438
+ "error",
439
+ "warning",
440
+ "success"
441
+ ]
442
+ },
443
+ "required": false,
444
+ "description": "forwarded to Input"
445
+ },
446
+ "isLoading": {
447
+ "type": {
448
+ "name": "boolean"
449
+ },
450
+ "required": false
451
+ },
452
+ "loadingText": {
453
+ "type": {
454
+ "name": "string"
455
+ },
456
+ "required": false,
457
+ "defaultValue": "Loading…"
458
+ },
459
+ "renderOption": {
460
+ "type": {
461
+ "name": "function",
462
+ "raw": "(option, query) => ReactNode"
463
+ },
464
+ "required": false
465
+ },
417
466
  "className": {
418
467
  "type": {
419
468
  "name": "string"
@@ -429,25 +478,34 @@
429
478
  },
430
479
  "composition": {
431
480
  "notes": [
432
- "**Autocomplete vs Combobox:** Autocomplete allows free-text input (users can type anything); Combobox enforces selection from the list. Pick by whether \"off-list\" values are valid (e.g. city field that accepts typos → Autocomplete; tag picker from a fixed vocabulary → Combobox).",
433
- "**Value shape is an object** (`{ value, label }`), not a plain string this preserves label/value decoupling for display-vs-storage.",
434
- "**FormField:** Does NOT auto-consume FormField state. Set explicit error styling via className if needed.",
481
+ "**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.",
482
+ "**Autocomplete vs Combobox:** Autocomplete allows free-text input (users can type anything); Combobox enforces selection from the list.",
483
+ "**Value shape is an object** (`{ value, label }`), not a plain string. Controlled via `value`, or uncontrolled via `defaultValue`.",
484
+ "**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.",
485
+ "**Async / \"type to search\":** set `isLoading` to show a spinner (in the field + the listbox) with `loadingText`.",
486
+ "**Matched-text highlight:** the query substring is bolded in each option by default; override the whole row with `renderOption`.",
435
487
  "**Portal rendering:** Dropdown portals to body with z-popover (1400) — stacks above Dialog/Sheet.",
436
- "**Keyboard:** ArrowDown/Up navigate suggestions, Enter selects, Esc closes. Typeahead is the input's native filtering."
488
+ "**Keyboard:** ArrowDown/Up/Home/End navigate, Enter selects, Esc closes."
437
489
  ]
438
490
  },
439
491
  "docPath": "docs/components/ui/autocomplete.md",
440
492
  "defaults": {
441
- "emptyText": "No options"
493
+ "emptyText": "No options",
494
+ "loadingText": "Loading…"
442
495
  },
443
496
  "examples": [
444
497
  "<Autocomplete\n options={[{ value: 'mumbai', label: 'Mumbai' }]}\n value={selectedCity}\n onValueChange={setSelectedCity}\n placeholder=\"Search cities...\"\n/>"
445
498
  ],
446
499
  "gotchas": [
447
500
  "Allows free-text input (no forced selection) — use Combobox for forced selection",
448
- "value is an object { value, label }, NOT just a string"
501
+ "value is an object { value, label }, NOT just a string",
502
+ "Client-side filtering only (known list). For huge/remote lists, drive `options` yourself with `isLoading` — no built-in virtualization."
449
503
  ],
450
504
  "changes": [
505
+ {
506
+ "version": "0.53.0",
507
+ "summary": "**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)."
508
+ },
451
509
  {
452
510
  "version": "0.18.0",
453
511
  "summary": "**Fixed** Added `useEffect` to sync query when external value changes"
@@ -10968,7 +11026,7 @@
10968
11026
  "displayName": "PriorityIndicator",
10969
11027
  "tier": "composed",
10970
11028
  "import": "@devalok/shilp-sutra/composed/priority-indicator",
10971
- "serverSafe": true,
11029
+ "serverSafe": false,
10972
11030
  "description": "",
10973
11031
  "props": {
10974
11032
  "priority": {
@@ -10978,6 +11036,13 @@
10978
11036
  },
10979
11037
  "required": false
10980
11038
  },
11039
+ "iconOnly": {
11040
+ "type": {
11041
+ "name": "boolean"
11042
+ },
11043
+ "required": false,
11044
+ "description": "icon-only chip, no visible text"
11045
+ },
10981
11046
  "display": {
10982
11047
  "type": {
10983
11048
  "name": "enum",
@@ -10987,31 +11052,41 @@
10987
11052
  ]
10988
11053
  },
10989
11054
  "required": false,
10990
- "defaultValue": "full"
11055
+ "description": "@deprecated — use iconOnly"
11056
+ },
11057
+ "children": {
11058
+ "type": {
11059
+ "name": "ReactNode"
11060
+ },
11061
+ "required": false,
11062
+ "description": "override the label, e.g. i18n"
10991
11063
  }
10992
11064
  },
10993
11065
  "composition": {
10994
11066
  "notes": [
10995
- "**Server-safe priority label** icon + color + text for task / issue priority.",
11067
+ "**Composes `Badge`**radius, color semantics, a11y labelling, and reduced-motion handling all come from one place (no bespoke re-roll).",
10996
11068
  "**Composes inside list rows, DataTable cells, Card headers, task panels** — anywhere a priority flag fits.",
10997
- "**display=\"compact\"** shows only the icon (with priority text as title attribute for tooltip). Use in tight cells; use `display=\"full\"` (default) in free space.",
10998
- "**Case-insensitive priority** — accepts both UPPERCASE (LOW/MEDIUM/HIGH/URGENT) and lowercase. Designed to match both backend conventions without manual coercion.",
10999
- "Color semantics: LOW=success, MEDIUM=warning, HIGH=error, URGENT=error with bolder icon."
11069
+ "**`iconOnly`** shows only the icon with a real accessible name (`role=\"img\"` + `aria-label`), for tight cells. Omit it (default) for icon + label.",
11070
+ "**Severity by weight, not motion** — URGENT renders as a solid fill so the top tier reads at a glance; the others are soft. No animation (removes the prior perpetual pulse).",
11071
+ "**Case-insensitive priority** accepts both UPPERCASE and lowercase; unknown values fall back to MEDIUM instead of throwing.",
11072
+ "**`children`** overrides the label for i18n / custom copy.",
11073
+ "Color semantics: LOW = slate (neutral), MEDIUM = warning, HIGH = error (soft), URGENT = error (solid)."
11000
11074
  ]
11001
11075
  },
11002
11076
  "docPath": "docs/components/composed/priority-indicator.md",
11003
- "defaults": {
11004
- "display": "full"
11005
- },
11006
11077
  "examples": [
11007
- "<PriorityIndicator priority=\"HIGH\" />\n<PriorityIndicator priority=\"low\" display=\"compact\" />"
11078
+ "<PriorityIndicator priority=\"HIGH\" />\n<PriorityIndicator priority=\"low\" iconOnly />\n<PriorityIndicator priority=\"URGENT\">Critical</PriorityIndicator>"
11008
11079
  ],
11009
11080
  "gotchas": [
11010
- "Case-insensitive — \"low\" and \"LOW\" both work",
11011
- "Server-safe: can be imported directly in Next.js Server Components",
11012
- "`compact` display shows only the icon; `full` shows icon + text label"
11081
+ "Case-insensitive — \"low\" and \"LOW\" both work; unknown values fall back to MEDIUM",
11082
+ "`iconOnly` shows only the icon (accessible-named); default shows icon + text label",
11083
+ "`display` is deprecated use `iconOnly`"
11013
11084
  ],
11014
11085
  "changes": [
11086
+ {
11087
+ "version": "0.53.0",
11088
+ "summary": "**Changed** Recomposed on the `Badge` primitive (was a bespoke re-rolled chip): inherits pill radius, color semantics, accessible labelling."
11089
+ },
11015
11090
  {
11016
11091
  "version": "0.2.0",
11017
11092
  "summary": "**Added** Identified as server-safe component"
@@ -11596,13 +11671,55 @@
11596
11671
  "required": false,
11597
11672
  "description": "minutes, default: 30",
11598
11673
  "defaultValue": 30
11674
+ },
11675
+ "selectedEventId": {
11676
+ "type": {
11677
+ "name": "string"
11678
+ },
11679
+ "required": false,
11680
+ "description": "rings the active event"
11681
+ },
11682
+ "renderEvent": {
11683
+ "type": {
11684
+ "name": "function",
11685
+ "raw": "(event) => ReactNode (custom event body)"
11686
+ },
11687
+ "required": false
11688
+ },
11689
+ "header": {
11690
+ "type": {
11691
+ "name": "ReactNode"
11692
+ },
11693
+ "required": false,
11694
+ "description": "toolbar slot above the grid"
11695
+ },
11696
+ "emptyState": {
11697
+ "type": {
11698
+ "name": "ReactNode"
11699
+ },
11700
+ "required": false,
11701
+ "description": "shown when events is empty"
11702
+ },
11703
+ "height": {
11704
+ "type": {
11705
+ "name": "union",
11706
+ "raw": "number | string"
11707
+ },
11708
+ "required": false,
11709
+ "description": "grid body height, default 480",
11710
+ "defaultValue": 480
11599
11711
  }
11600
11712
  },
11601
11713
  "composition": {
11602
11714
  "notes": [
11603
11715
  "**Day / Week calendar view** for time-block display (meetings, shifts, availability). Not a full calendar app — no month view, no drag-to-create.",
11604
11716
  "**Event data is consumer-owned:** You pass `events` as an array; ScheduleView doesn't fetch, doesn't cache, doesn't expand recurring events. All scheduling logic lives in your app.",
11605
- "**Event click + slot click** — `onEventClick` for existing events; `onSlotClick` for creating new events (fires with start/end of the empty slot).",
11717
+ "**Event click + slot click** — `onEventClick` for existing events; `onSlotClick` for creating new events (fires with start/end of the empty slot). **Slots are only interactive (focusable + keyboard-navigable) when `onSlotClick` is set** — otherwise they render as inert grid lines, so a read-only schedule adds no keyboard/AT tab stops.",
11718
+ "**Keyboard (interactive slots):** roving tabindex — Arrow keys move between slots (up/down within a day, left/right across days, RTL-aware), Home/End jump within the day; only one slot is in the tab order at a time.",
11719
+ "**Overlapping events** are partitioned into side-by-side columns automatically so double-booked times stay legible.",
11720
+ "**Live now-line** ticks every minute and scrolls into view on mount.",
11721
+ "**`renderEvent`** customizes the event block body; **`header`** adds a toolbar; **`selectedEventId`** rings the active event; **`emptyState`** shows when there are no events.",
11722
+ "**Composes the DS card shell tokens** (`surface-2` + `rounded-surface` + border) and is RTL-safe (logical properties throughout).",
11606
11723
  "**Color vocabulary matches the DS** — `accent/success/warning/error/info/neutral`. Map your event types to these at the data layer.",
11607
11724
  "**endHour is exclusive:** `endHour=18` means the last visible slot starts at 17:30 (with 30min slots). Match your UX expectation: 9-5 typically means `startHour=9, endHour=18`.",
11608
11725
  "**Pairs with date-picker/composed** — use DatePicker or DateRangePicker to choose which date to show; pass that as ScheduleView's `date`."
@@ -11612,7 +11729,8 @@
11612
11729
  "defaults": {
11613
11730
  "startHour": 8,
11614
11731
  "endHour": 18,
11615
- "slotDuration": 30
11732
+ "slotDuration": 30,
11733
+ "height": 480
11616
11734
  },
11617
11735
  "examples": [
11618
11736
  "<ScheduleView\n view=\"week\"\n date={new Date()}\n events={calendarEvents}\n onEventClick={(e) => openEvent(e.id)}\n/>"
@@ -11623,6 +11741,10 @@
11623
11741
  "Events that span outside `startHour`/`endHour` may be clipped"
11624
11742
  ],
11625
11743
  "changes": [
11744
+ {
11745
+ "version": "0.53.0",
11746
+ "summary": "**Changed** Read-only schedules no longer flood the tab order — slots are interactive only when `onSlotClick` is set; otherwise inert grid lines. Interactive slots use roving tabindex + Arrow/Home/End keyboard navigation (RTL-aware)."
11747
+ },
11626
11748
  {
11627
11749
  "version": "0.49.0",
11628
11750
  "summary": "**BREAKING** `ScheduleEvent.color` value `\"primary\"` renamed `\"accent\"` (DS colour vocabulary). It was the default, so untyped events are unaffected."
@@ -11967,7 +12089,7 @@
11967
12089
  "raw": "BottomNavbarUser | null"
11968
12090
  },
11969
12091
  "required": false,
11970
- "description": "optional"
12092
+ "description": "drives per-item role gating, optional"
11971
12093
  },
11972
12094
  "primaryItems": {
11973
12095
  "type": {
@@ -11983,7 +12105,30 @@
11983
12105
  "raw": "BottomNavItem[]"
11984
12106
  },
11985
12107
  "required": false,
11986
- "description": "overflow items in \"More\" menu, optional"
12108
+ "description": "overflow items in \"More\" sheet, optional"
12109
+ },
12110
+ "indicator": {
12111
+ "type": {
12112
+ "name": "enum",
12113
+ "value": [
12114
+ "pill",
12115
+ "underline",
12116
+ "tint",
12117
+ "none"
12118
+ ]
12119
+ },
12120
+ "required": false,
12121
+ "description": "active-item indicator; default 'pill'"
12122
+ },
12123
+ "labelVisibility": {
12124
+ "type": {
12125
+ "name": "enum",
12126
+ "value": [
12127
+ "always",
12128
+ "selected"
12129
+ ]
12130
+ },
12131
+ "required": false
11987
12132
  },
11988
12133
  "className": {
11989
12134
  "type": {
@@ -11998,7 +12143,11 @@
11998
12143
  "**Responsive switch pattern:** Use `useIsMobile()` hook to conditionally render AppSidebar (desktop) or BottomNavbar (mobile). Example: ```jsx const isMobile = useIsMobile() return isMobile ? <BottomNavbar ... /> : <AppSidebar ... /> ```",
11999
12144
  "**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.",
12000
12145
  "**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.",
12001
- "**Badge numbers** cap at 99+ (same as BadgeIndicator pattern).",
12146
+ "**Badge numbers** cap at 99+ (composes the `Badge` primitive).",
12147
+ "**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`.",
12148
+ "**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.",
12149
+ "**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.",
12150
+ "**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`.",
12002
12151
  "**Not for desktop:** The viewport-fixed positioning + touch-optimized sizing don't translate well to desktop. Hide behind `md:hidden`."
12003
12152
  ]
12004
12153
  },
@@ -12013,6 +12162,10 @@
12013
12162
  "Requires LinkProvider for framework-specific link components (e.g., Next.js Link)"
12014
12163
  ],
12015
12164
  "changes": [
12165
+ {
12166
+ "version": "0.53.0",
12167
+ "summary": "**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)."
12168
+ },
12016
12169
  {
12017
12170
  "version": "0.19.0",
12018
12171
  "summary": "**Changed** Background elevated from `bg-surface-1` to `bg-surface-2` for visual hierarchy above app background"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@devalok/shilp-sutra",
3
- "version": "0.52.0",
3
+ "version": "0.53.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>",
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.52.0"
6
+ version: "0.53.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
@@ -2,7 +2,7 @@
2
2
 
3
3
  # @devalok/shilp-sutra
4
4
 
5
- > Radix UI + Tailwind 4 (CSS-first) + CVA design system for Devalok apps, v0.52.0.
5
+ > Radix UI + Tailwind 4 (CSS-first) + CVA design system for Devalok apps, v0.53.0.
6
6
  > Built on the same primitives as shadcn/ui but with DIFFERENT prop APIs — never guess from shadcn knowledge; verify every prop.
7
7
  > 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).
8
8