@usevyre/ai-context 1.3.0 → 1.4.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.
@@ -1,5 +1,5 @@
1
1
  # useVyre Design System Context
2
- # Version: 1.6.0
2
+ # Version: 1.16.0
3
3
 
4
4
  You are working in a codebase that uses the useVyre design system.
5
5
  Follow the rules below strictly when writing any UI code.
@@ -1537,6 +1537,565 @@ import { Box } from "@usevyre/react"
1537
1537
 
1538
1538
  ---
1539
1539
 
1540
+ ### Form
1541
+
1542
+ Controlled, data-driven form. Zero dependencies. Validation runs on submit and (after the first submit) on blur. Errors map into the wrapped Field automatically (state=error + hint=message). Compose with FormField, which wires name/value/onChange/onBlur into a single control child.
1543
+
1544
+ ```tsx
1545
+ import { Form, FormField } from "@usevyre/react"
1546
+
1547
+ // Props:
1548
+ // values = Record<string, any>
1549
+ // defaultValues = Record<string, any>
1550
+ // onChange = function
1551
+ // onSubmit = function
1552
+ // onInvalid = function
1553
+
1554
+ // Examples:
1555
+ const [values, setValues] = useState({ email: "", password: "" });
1556
+
1557
+ <Form values={values} onChange={setValues} onSubmit={(v) => signIn(v)}>
1558
+ <FormField name="email" label="Email" rules={{ required: true, email: true }}>
1559
+ <Input type="email" />
1560
+ </FormField>
1561
+ <FormField name="password" label="Password" rules={{ required: true, minLength: 8 }}>
1562
+ <Input type="password" />
1563
+ </FormField>
1564
+ <Button type="submit" variant="primary">Sign in</Button>
1565
+ </Form>
1566
+ <FormField
1567
+ name="confirm"
1568
+ label="Confirm password"
1569
+ rules={{
1570
+ required: true,
1571
+ validate: (v, all) => v === all.password ? null : "Passwords do not match",
1572
+ }}
1573
+ >
1574
+ <Input type="password" />
1575
+ </FormField>
1576
+ ```
1577
+
1578
+ **Common mistakes:**
1579
+ - ❌ `Manually tracking each field's error state with useState` → Wrap controls in <FormField name rules> and let Form manage errors
1580
+ - ❌ `Adding a validation library (zod/yup) just for basic rules` → Use rules={{ required, minLength, pattern, email, validate }}
1581
+ - ❌ `<FormField> with multiple control children` → Use one control per FormField (Input/Textarea/Select/etc.)
1582
+ - ❌ `<FormField> outside a <Form>` → Always nest FormField inside <Form>
1583
+
1584
+ ---
1585
+
1586
+ ### FormField
1587
+
1588
+ A single labelled, validated field inside <Form>. Injects name/value/onChange/onBlur into its one control child and wraps it in <Field> (label + error state + hint).
1589
+
1590
+ ```tsx
1591
+ import { FormField } from "@usevyre/react"
1592
+
1593
+ // Props:
1594
+ // name = string
1595
+ // label = string
1596
+ // hint = string
1597
+ // rules = object
1598
+
1599
+ // Examples:
1600
+ <FormField name="bio" label="Bio" hint="Max 200 characters" rules={{ maxLength: 200 }}>
1601
+ <Textarea />
1602
+ </FormField>
1603
+ ```
1604
+
1605
+ **Common mistakes:**
1606
+ - ❌ `Putting onChange/value manually on the control inside FormField` → Let FormField wire the control; only pass static props (type, placeholder)
1607
+
1608
+ ---
1609
+
1610
+ ### NumberInput
1611
+
1612
+ Controlled numeric input with −/+ stepper buttons. onChange emits a NUMBER (or null when empty) — NOT an event. Drops straight into <FormField> (Form handles the non-event value). Clamps to min/max on blur; keyboard ArrowUp/Down ±step, Shift+Arrow ±step×10.
1613
+
1614
+ ```tsx
1615
+ import { NumberInput } from "@usevyre/react"
1616
+
1617
+ // Props:
1618
+ // value = number | null
1619
+ // defaultValue = number | null (default: null)
1620
+ // onChange = function
1621
+ // min = number
1622
+ // max = number
1623
+ // step = number (default: 1)
1624
+ // precision = number
1625
+ // size = "sm" | "md" | "lg" (default: md)
1626
+ // disabled = boolean (default: false)
1627
+ // readOnly = boolean (default: false)
1628
+
1629
+ // Examples:
1630
+ const [qty, setQty] = useState<number | null>(1);
1631
+
1632
+ <NumberInput value={qty} onChange={setQty} min={1} max={99} />
1633
+ <FormField name="age" label="Age" rules={{ required: true, min: 18 }}>
1634
+ <NumberInput min={0} max={120} />
1635
+ </FormField>
1636
+ ```
1637
+
1638
+ **Common mistakes:**
1639
+ - ❌ `onChange={(e) => set(e.target.value)}` → onChange={(value) => set(value)} — value is number | null
1640
+ - ❌ `Using <Input type="number"> for numeric fields` → Use <NumberInput value onChange min max step />
1641
+ - ❌ `Parsing the value with Number() in form state` → Store the value directly; it is already number | null
1642
+
1643
+ ---
1644
+
1645
+ ### ToggleGroup
1646
+
1647
+ Segmented control. CONTROLLED — the group owns the value. onChange emits the VALUE (not an event). type=single → value:string|null; type=multiple → value:string[]. Provide options[] for simple lists or <ToggleItem value> children for custom content. Distinct from Switch (boolean), ButtonGroup (layout only), RadioGroup (form radios, single only).
1648
+
1649
+ ```tsx
1650
+ import { ToggleGroup, ToggleItem } from "@usevyre/react"
1651
+
1652
+ // Props:
1653
+ // type = "single" | "multiple" (default: single)
1654
+ // value = string | null | string[]
1655
+ // onChange = function
1656
+ // options = array
1657
+ // size = "sm" | "md" | "lg" (default: md)
1658
+ // orientation = "horizontal" | "vertical" (default: horizontal)
1659
+ // disabled = boolean (default: false)
1660
+
1661
+ // Examples:
1662
+ const [view, setView] = useState<string | null>("grid");
1663
+
1664
+ <ToggleGroup
1665
+ value={view}
1666
+ onChange={setView}
1667
+ options={[
1668
+ { value: "grid", label: "Grid" },
1669
+ { value: "list", label: "List" },
1670
+ ]}
1671
+ />
1672
+ const [fmt, setFmt] = useState<string[]>(["bold"]);
1673
+
1674
+ <ToggleGroup type="multiple" value={fmt} onChange={setFmt}>
1675
+ <ToggleItem value="bold">B</ToggleItem>
1676
+ <ToggleItem value="italic">I</ToggleItem>
1677
+ <ToggleItem value="underline">U</ToggleItem>
1678
+ </ToggleGroup>
1679
+ ```
1680
+
1681
+ **Common mistakes:**
1682
+ - ❌ `onChange={(e) => set(e.target.value)}` → onChange={(value) => set(value)} — string|null (single) or string[] (multiple)
1683
+ - ❌ `Using ToggleGroup for a single on/off setting` → Use <Switch> for on/off; ToggleGroup is for choosing among options
1684
+ - ❌ `type="multiple" with a string value` → value={['a','b']} and onChange receives string[]
1685
+ - ❌ `<ToggleItem> outside <ToggleGroup>` → Always nest ToggleItem inside ToggleGroup (or use options)
1686
+
1687
+ ---
1688
+
1689
+ ### ToggleItem
1690
+
1691
+ A single toggle button inside <ToggleGroup>. Reads selection state from the group via context.
1692
+
1693
+ ```tsx
1694
+ import { ToggleItem } from "@usevyre/react"
1695
+
1696
+ // Props:
1697
+ // value = string
1698
+ // icon = ReactNode
1699
+ // disabled = boolean (default: false)
1700
+
1701
+ // Examples:
1702
+ <ToggleGroup value={v} onChange={setV}>
1703
+ <ToggleItem value="left">Left</ToggleItem>
1704
+ <ToggleItem value="center">Center</ToggleItem>
1705
+ </ToggleGroup>
1706
+ ```
1707
+
1708
+ **Common mistakes:**
1709
+ - ❌ `Tracking selected state on ToggleItem yourself` → Only set value; the group controls selected state
1710
+
1711
+ ---
1712
+
1713
+ ### Stepper
1714
+
1715
+ Multi-step flow indicator + controller (onboarding/checkout/wizard). CONTROLLED by a 0-based index. Compose StepperNav (with Step indicators) and StepPanel (content shown when its index == active). Step/StepPanel take an explicit 0-based `index`. Not Tabs — Stepper is an ORDERED linear flow with completed/current/upcoming states.
1716
+
1717
+ ```tsx
1718
+ import { Stepper, StepperNav, Step, StepPanel } from "@usevyre/react"
1719
+
1720
+ // Props:
1721
+ // value = number
1722
+ // defaultValue = number (default: 0)
1723
+ // onChange = function
1724
+ // orientation = "horizontal" | "vertical" (default: horizontal)
1725
+ // clickable = boolean (default: false)
1726
+
1727
+ // Examples:
1728
+ const [step, setStep] = useState(0);
1729
+
1730
+ <Stepper value={step} onChange={setStep}>
1731
+ <StepperNav>
1732
+ <Step index={0} label="Account" />
1733
+ <Step index={1} label="Profile" />
1734
+ <Step index={2} label="Done" />
1735
+ </StepperNav>
1736
+ <StepPanel index={0}><AccountForm /></StepPanel>
1737
+ <StepPanel index={1}><ProfileForm /></StepPanel>
1738
+ <StepPanel index={2}><Summary /></StepPanel>
1739
+ <Stack direction="row" gap="sm" justify="between">
1740
+ <Button onClick={() => setStep((s) => s - 1)} disabled={step === 0}>Back</Button>
1741
+ <Button variant="primary" onClick={() => setStep((s) => s + 1)}>Next</Button>
1742
+ </Stack>
1743
+ </Stepper>
1744
+ <Stepper orientation="vertical" defaultValue={1}>
1745
+ <StepperNav>
1746
+ <Step index={0} label="Cart" description="2 items" />
1747
+ <Step index={1} label="Shipping" description="Enter address" />
1748
+ <Step index={2} label="Payment" />
1749
+ </StepperNav>
1750
+ </Stepper>
1751
+ ```
1752
+
1753
+ **Common mistakes:**
1754
+ - ❌ `Using Tabs for a wizard / checkout flow` → Use <Stepper> with StepperNav + Step + StepPanel
1755
+ - ❌ `onChange={(e) => set(e.target.value)}` → onChange={(index) => setStep(index)}
1756
+ - ❌ `Manually toggling which panel is visible` → Give each StepPanel an index; Stepper shows the active one
1757
+ - ❌ `<Step> or <StepPanel> outside <Stepper>` → Nest Step inside StepperNav, StepPanel inside Stepper
1758
+
1759
+ ---
1760
+
1761
+ ### StepperNav
1762
+
1763
+ Container for Step indicators inside <Stepper>. Lays them out per the Stepper's orientation.
1764
+
1765
+ ```tsx
1766
+ import { Stepper, StepperNav, Step, StepPanel } from "@usevyre/react"
1767
+
1768
+ ```
1769
+
1770
+ ---
1771
+
1772
+ ### Step
1773
+
1774
+ One step indicator inside <StepperNav>. State (completed/current/upcoming) derives from the Stepper's active index automatically.
1775
+
1776
+ ```tsx
1777
+ import { Stepper, StepperNav, Step, StepPanel } from "@usevyre/react"
1778
+
1779
+ // Props:
1780
+ // index = number
1781
+ // label = ReactNode
1782
+ // description = ReactNode
1783
+ // icon = ReactNode
1784
+
1785
+ ```
1786
+
1787
+ ---
1788
+
1789
+ ### StepPanel
1790
+
1791
+ Content for one step. Renders its children only when its index equals the Stepper's active step.
1792
+
1793
+ ```tsx
1794
+ import { Stepper, StepperNav, Step, StepPanel } from "@usevyre/react"
1795
+
1796
+ // Props:
1797
+ // index = number
1798
+
1799
+ ```
1800
+
1801
+ ---
1802
+
1803
+ ### EmptyState
1804
+
1805
+ Presentational placeholder for empty lists, tables, and search results. No state. title/description/variant/size are props; the optional call-to-action goes in children (React) or the default slot (Vue). variant picks a preset icon (default=box, search=magnifier, error=warning); pass `icon` (or #icon slot) to override.
1806
+
1807
+ ```tsx
1808
+ import { EmptyState } from "@usevyre/react"
1809
+
1810
+ // Props:
1811
+ // title = string
1812
+ // description = string
1813
+ // variant = "default" | "search" | "error" (default: default)
1814
+ // icon = ReactNode
1815
+ // size = "sm" | "md" | "lg" (default: md)
1816
+ // children = ReactNode
1817
+
1818
+ // Examples:
1819
+ <EmptyState
1820
+ variant="search"
1821
+ title="No results"
1822
+ description="Try a different search term."
1823
+ >
1824
+ <Button variant="secondary" onClick={reset}>Clear filters</Button>
1825
+ </EmptyState>
1826
+ <EmptyState
1827
+ size="lg"
1828
+ title="No projects yet"
1829
+ description="Create your first project to get started."
1830
+ >
1831
+ <Button variant="primary">New project</Button>
1832
+ </EmptyState>
1833
+ ```
1834
+
1835
+ **Common mistakes:**
1836
+ - ❌ `Building an empty placeholder with a bare <div> + centered text` → Use <EmptyState title description variant>
1837
+ - ❌ `action / cta prop` → Put the Button as children of EmptyState
1838
+ - ❌ `Using EmptyState for a loading state` → Use <Skeleton> while loading; EmptyState when the result set is empty
1839
+
1840
+ ---
1841
+
1842
+ ### Stat
1843
+
1844
+ Presentational dashboard KPI. No state. The arrow DIRECTION follows the sign of `delta` (the actual change: -0.4% → down arrow). The arrow/delta COLOR is set explicitly by `trend` (up=success, down=danger, neutral=muted) — so 'churn -0.4%, trend=up' shows a green DOWN arrow. Wrap several in StatGroup for an evenly-split row with dividers.
1845
+
1846
+ ```tsx
1847
+ import { Stat, StatGroup } from "@usevyre/react"
1848
+
1849
+ // Props:
1850
+ // label = string
1851
+ // value = string | number
1852
+ // delta = string | number
1853
+ // trend = "up" | "down" | "neutral" (default: neutral)
1854
+ // deltaLabel = string
1855
+ // icon = ReactNode
1856
+ // size = "sm" | "md" | "lg" (default: md)
1857
+
1858
+ // Examples:
1859
+ <StatGroup>
1860
+ <Stat label="Revenue" value="$48.2k" delta="+12%" trend="up" deltaLabel="vs last month" />
1861
+ <Stat label="Churn" value="2.1%" delta="-0.4%" trend="up" deltaLabel="lower is better" />
1862
+ <Stat label="Orders" value="1,204" delta="0%" trend="neutral" />
1863
+ </StatGroup>
1864
+ <Stat label="Active users" value="12,840" delta="+3.2%" trend="up"
1865
+ icon={<UsersIcon />} size="lg" />
1866
+ ```
1867
+
1868
+ **Common mistakes:**
1869
+ - ❌ `Assuming trend flips the arrow direction` → delta="-0.4%" always shows a down arrow; trend="up" just colors it green
1870
+ - ❌ `Building a KPI card with Card + manual layout` → Use <Stat label value delta trend />
1871
+ - ❌ `Laying out a KPI row with custom flex + dividers` → Wrap the Stats in <StatGroup>
1872
+
1873
+ ---
1874
+
1875
+ ### StatGroup
1876
+
1877
+ Evenly-split row of <Stat> with subtle dividers between items. Each Stat flexes to equal width.
1878
+
1879
+ ```tsx
1880
+ import { Stat, StatGroup } from "@usevyre/react"
1881
+
1882
+ // Examples:
1883
+ <StatGroup>
1884
+ <Stat label="MRR" value="$9.6k" delta="+5%" trend="up" />
1885
+ <Stat label="Refunds" value="32" delta="+8" trend="down" />
1886
+ </StatGroup>
1887
+ ```
1888
+
1889
+ **Common mistakes:**
1890
+ - ❌ `Putting non-Stat children in StatGroup` → Only place <Stat> elements inside StatGroup
1891
+
1892
+ ---
1893
+
1894
+ ### Timeline
1895
+
1896
+ Vertical activity feed for audit logs and history. Presentational — a status dot per item plus a connector line. Pass `items` for plain logs, or TimelineItem children for rich per-item content. Timeline does NOT reorder; pass items in the order you want shown.
1897
+
1898
+ ```tsx
1899
+ import { Timeline, TimelineItem } from "@usevyre/react"
1900
+
1901
+ // Props:
1902
+ // items = array
1903
+ // children = ReactNode
1904
+
1905
+ // Examples:
1906
+ <Timeline
1907
+ items={[
1908
+ { title: "Deployed v2.1", time: "2m ago", status: "success" },
1909
+ { title: "Build started", time: "5m ago", status: "info" },
1910
+ { title: "Push to main", time: "6m ago" },
1911
+ ]}
1912
+ />
1913
+ <Timeline>
1914
+ <TimelineItem title="Invoice paid" time="Apr 2" status="success">
1915
+ <Text size="sm">$1,200 — <a href="#">view receipt</a></Text>
1916
+ </TimelineItem>
1917
+ <TimelineItem title="Invoice sent" time="Mar 28" status="info" />
1918
+ </Timeline>
1919
+ ```
1920
+
1921
+ **Common mistakes:**
1922
+ - ❌ `Building an activity log with a <ul> + manual dots/lines` → Use <Timeline items={[...]} /> or TimelineItem children
1923
+ - ❌ `Using Stepper for a history/audit feed` → Use <Timeline> for logs/history; Stepper for wizards
1924
+ - ❌ `Expecting Timeline to sort by time` → Sort the array yourself (newest- or oldest-first)
1925
+
1926
+ ---
1927
+
1928
+ ### TimelineItem
1929
+
1930
+ One entry in a <Timeline>. Renders a status-colored dot (or a custom icon), a title, an optional time, and optional rich content.
1931
+
1932
+ ```tsx
1933
+ import { Timeline, TimelineItem } from "@usevyre/react"
1934
+
1935
+ // Props:
1936
+ // title = ReactNode
1937
+ // time = ReactNode
1938
+ // status = "default" | "success" | "warning" | "danger" | "info" (default: default)
1939
+ // icon = ReactNode
1940
+ // children = ReactNode
1941
+
1942
+ // Examples:
1943
+ <TimelineItem title="Comment added" time="1h ago" status="default">
1944
+ <Text size="sm">“Looks good to me 👍”</Text>
1945
+ </TimelineItem>
1946
+ ```
1947
+
1948
+ **Common mistakes:**
1949
+ - ❌ `<TimelineItem> outside <Timeline>` → Always nest TimelineItem inside Timeline
1950
+
1951
+ ---
1952
+
1953
+ ### Tree
1954
+
1955
+ Hierarchical tree view for file explorers and nested navigation. DATA-DRIVEN and CONTROLLED — pass a nested `data` array; the Tree renders recursively. Single selection. A node WITH children is a folder (click toggles expand); a leaf fires onSelect. Keyboard: ArrowUp/Down move, ArrowRight/Left expand/collapse, Enter/Space select.
1956
+
1957
+ ```tsx
1958
+ import { Tree } from "@usevyre/react"
1959
+
1960
+ // Props:
1961
+ // data = TreeNode[]
1962
+ // expandedIds = string[]
1963
+ // defaultExpandedIds = string[] (default: [])
1964
+ // onExpandedChange = function
1965
+ // selectedId = string | null
1966
+ // defaultSelectedId = string | null (default: null)
1967
+ // onSelect = function
1968
+
1969
+ // Examples:
1970
+ const [sel, setSel] = useState<string | null>("src/a.ts");
1971
+
1972
+ <Tree
1973
+ data={[
1974
+ { id: "src", label: "src", children: [
1975
+ { id: "src/a.ts", label: "a.ts" },
1976
+ { id: "src/b", label: "b", children: [
1977
+ { id: "src/b/c.ts", label: "c.ts" },
1978
+ ]},
1979
+ ]},
1980
+ { id: "README.md", label: "README.md" },
1981
+ ]}
1982
+ selectedId={sel}
1983
+ onSelect={setSel}
1984
+ defaultExpandedIds={["src"]}
1985
+ />
1986
+ const [open, setOpen] = useState<string[]>(["root"]);
1987
+
1988
+ <Tree data={tree} expandedIds={open} onExpandedChange={setOpen} />
1989
+ ```
1990
+
1991
+ **Common mistakes:**
1992
+ - ❌ `Rendering a nested <ul> tree by hand with manual expand state` → Pass a nested `data` array to <Tree> and control expandedIds/selectedId
1993
+ - ❌ `onSelect={(e) => ...}` → onSelect={(id) => setSelected(id)}
1994
+ - ❌ `Mutating the data array to expand/collapse` → Track expandedIds in state (or use defaultExpandedIds)
1995
+ - ❌ `Using DropdownMenu submenus for a file tree` → Use <Tree> for file explorers / nested nav
1996
+
1997
+ ---
1998
+
1999
+ ### OTPInput
2000
+
2001
+ Segmented one-time-code input for verification / 2FA. CONTROLLED. onChange emits the STRING value (not an event), and onComplete fires once when every slot is filled. Paste-aware (pasting a full code fills all slots), auto-advance on input, backspace moves to the previous slot, arrow keys navigate. Drops straight into <FormField>.
2002
+
2003
+ ```tsx
2004
+ import { OTPInput } from "@usevyre/react"
2005
+
2006
+ // Props:
2007
+ // value = string
2008
+ // defaultValue = string (default: "")
2009
+ // onChange = function
2010
+ // onComplete = function
2011
+ // length = number (default: 6)
2012
+ // type = "numeric" | "alphanumeric" (default: numeric)
2013
+ // mask = boolean (default: false)
2014
+ // size = "sm" | "md" | "lg" (default: md)
2015
+ // disabled = boolean (default: false)
2016
+ // autoFocus = boolean (default: false)
2017
+
2018
+ // Examples:
2019
+ const [code, setCode] = useState("");
2020
+
2021
+ <OTPInput
2022
+ value={code}
2023
+ onChange={setCode}
2024
+ onComplete={(c) => verify(c)}
2025
+ autoFocus
2026
+ />
2027
+ <FormField name="otp" label="Verification code"
2028
+ rules={{ required: true, minLength: 6 }}>
2029
+ <OTPInput length={6} />
2030
+ </FormField>
2031
+ ```
2032
+
2033
+ **Common mistakes:**
2034
+ - ❌ `onChange={(e) => set(e.target.value)}` → onChange={(value) => setCode(value)}
2035
+ - ❌ `Six separate <Input> boxes wired by hand` → Use <OTPInput length={6} value onChange />
2036
+ - ❌ `Reading completion by comparing length yourself` → Use onComplete={(code) => verify(code)}
2037
+ - ❌ `type="password" to hide digits` → Use mask (type stays numeric/alphanumeric)
2038
+
2039
+ ---
2040
+
2041
+ ### Carousel
2042
+
2043
+ Accessible content slider for galleries, onboarding, and testimonials. CONTROLLED by a 0-based slide index. Compose CarouselSlide children (slide order = index). Snap scrolling, clickable dot indicators, prev/next arrows, ArrowLeft/Right keyboard, optional loop and autoPlay (autoplay pauses on hover/focus). onChange emits the index (not an event).
2044
+
2045
+ ```tsx
2046
+ import { Carousel, CarouselSlide } from "@usevyre/react"
2047
+
2048
+ // Props:
2049
+ // value = number
2050
+ // defaultValue = number (default: 0)
2051
+ // onChange = function
2052
+ // loop = boolean (default: false)
2053
+ // autoPlay = boolean (default: false)
2054
+ // interval = number (default: 5000)
2055
+ // showArrows = boolean (default: true)
2056
+ // showIndicators = boolean (default: true)
2057
+
2058
+ // Examples:
2059
+ const [i, setI] = useState(0);
2060
+
2061
+ <Carousel value={i} onChange={setI} loop>
2062
+ <CarouselSlide><img src="/a.jpg" alt="A" /></CarouselSlide>
2063
+ <CarouselSlide><img src="/b.jpg" alt="B" /></CarouselSlide>
2064
+ <CarouselSlide><img src="/c.jpg" alt="C" /></CarouselSlide>
2065
+ </Carousel>
2066
+ <Carousel autoPlay interval={4000} showArrows={false}>
2067
+ <CarouselSlide><Welcome /></CarouselSlide>
2068
+ <CarouselSlide><Features /></CarouselSlide>
2069
+ <CarouselSlide><GetStarted /></CarouselSlide>
2070
+ </Carousel>
2071
+ ```
2072
+
2073
+ **Common mistakes:**
2074
+ - ❌ `onChange={(e) => set(e.target.value)}` → onChange={(index) => setIndex(index)}
2075
+ - ❌ `Putting raw elements directly in Carousel` → Wrap each slide in <CarouselSlide>
2076
+ - ❌ `Building a slider with manual scroll + dot state` → Use <Carousel> with CarouselSlide children
2077
+ - ❌ `autoPlay without considering reduced motion / pausing` → Carousel already pauses on hover/focus; keep interval reasonable or omit autoPlay
2078
+
2079
+ ---
2080
+
2081
+ ### CarouselSlide
2082
+
2083
+ One slide inside <Carousel>. Holds arbitrary content (image, Card, testimonial). Slide order determines its index.
2084
+
2085
+ ```tsx
2086
+ import { Carousel, CarouselSlide } from "@usevyre/react"
2087
+
2088
+ // Examples:
2089
+ <CarouselSlide>
2090
+ <Card><CardBody>“Best tool ever.” — Ada</CardBody></Card>
2091
+ </CarouselSlide>
2092
+ ```
2093
+
2094
+ **Common mistakes:**
2095
+ - ❌ `<CarouselSlide> outside <Carousel>` → Always nest CarouselSlide inside Carousel
2096
+
2097
+ ---
2098
+
1540
2099
  ### DateRangePicker
1541
2100
 
1542
2101
  Start/end date range picker. Built on Calendar (mode=range) with a friendlier { from, to } object API, a two-month side-by-side view, and preset shortcuts. Use this for report/filter date ranges; use DatePicker for a single date.
@@ -1660,6 +2219,47 @@ If you generate these, you are hallucinating.
1660
2219
  - ❌ `<Box <Box style={{ padding: 16 }}>>` → Use <Box padding="md"> (or paddingX/paddingTop/...)
1661
2220
  - ❌ `<Box Using Box for flex/grid layout>` → Use <Stack> or <Grid>
1662
2221
  - ❌ `<Box style={{ width: "100%" }} / style={{ height: 320 }}>` → Use the width / height prop: width="full", width="md", height="screen", etc.
2222
+ - ❌ `<Form Manually tracking each field's error state with useState>` → Wrap controls in <FormField name rules> and let Form manage errors
2223
+ - ❌ `<Form Adding a validation library (zod/yup) just for basic rules>` → Use rules={{ required, minLength, pattern, email, validate }}
2224
+ - ❌ `<Form <FormField> with multiple control children>` → Use one control per FormField (Input/Textarea/Select/etc.)
2225
+ - ❌ `<Form <FormField> outside a <Form>>` → Always nest FormField inside <Form>
2226
+ - ❌ `<FormField Putting onChange/value manually on the control inside FormField>` → Let FormField wire the control; only pass static props (type, placeholder)
2227
+ - ❌ `<NumberInput onChange={(e) => set(e.target.value)}>` → onChange={(value) => set(value)} — value is number | null
2228
+ - ❌ `<NumberInput Using <Input type="number"> for numeric fields>` → Use <NumberInput value onChange min max step />
2229
+ - ❌ `<NumberInput Parsing the value with Number() in form state>` → Store the value directly; it is already number | null
2230
+ - ❌ `<ToggleGroup onChange={(e) => set(e.target.value)}>` → onChange={(value) => set(value)} — string|null (single) or string[] (multiple)
2231
+ - ❌ `<ToggleGroup Using ToggleGroup for a single on/off setting>` → Use <Switch> for on/off; ToggleGroup is for choosing among options
2232
+ - ❌ `<ToggleGroup type="multiple" with a string value>` → value={['a','b']} and onChange receives string[]
2233
+ - ❌ `<ToggleGroup <ToggleItem> outside <ToggleGroup>>` → Always nest ToggleItem inside ToggleGroup (or use options)
2234
+ - ❌ `<ToggleItem Tracking selected state on ToggleItem yourself>` → Only set value; the group controls selected state
2235
+ - ❌ `<Stepper Using Tabs for a wizard / checkout flow>` → Use <Stepper> with StepperNav + Step + StepPanel
2236
+ - ❌ `<Stepper onChange={(e) => set(e.target.value)}>` → onChange={(index) => setStep(index)}
2237
+ - ❌ `<Stepper Manually toggling which panel is visible>` → Give each StepPanel an index; Stepper shows the active one
2238
+ - ❌ `<Stepper <Step> or <StepPanel> outside <Stepper>>` → Nest Step inside StepperNav, StepPanel inside Stepper
2239
+ - ❌ `<EmptyState Building an empty placeholder with a bare <div> + centered text>` → Use <EmptyState title description variant>
2240
+ - ❌ `<EmptyState action / cta prop>` → Put the Button as children of EmptyState
2241
+ - ❌ `<EmptyState Using EmptyState for a loading state>` → Use <Skeleton> while loading; EmptyState when the result set is empty
2242
+ - ❌ `<Stat Assuming trend flips the arrow direction>` → delta="-0.4%" always shows a down arrow; trend="up" just colors it green
2243
+ - ❌ `<Stat Building a KPI card with Card + manual layout>` → Use <Stat label value delta trend />
2244
+ - ❌ `<Stat Laying out a KPI row with custom flex + dividers>` → Wrap the Stats in <StatGroup>
2245
+ - ❌ `<StatGroup Putting non-Stat children in StatGroup>` → Only place <Stat> elements inside StatGroup
2246
+ - ❌ `<Timeline Building an activity log with a <ul> + manual dots/lines>` → Use <Timeline items={[...]} /> or TimelineItem children
2247
+ - ❌ `<Timeline Using Stepper for a history/audit feed>` → Use <Timeline> for logs/history; Stepper for wizards
2248
+ - ❌ `<Timeline Expecting Timeline to sort by time>` → Sort the array yourself (newest- or oldest-first)
2249
+ - ❌ `<TimelineItem <TimelineItem> outside <Timeline>>` → Always nest TimelineItem inside Timeline
2250
+ - ❌ `<Tree Rendering a nested <ul> tree by hand with manual expand state>` → Pass a nested `data` array to <Tree> and control expandedIds/selectedId
2251
+ - ❌ `<Tree onSelect={(e) => ...}>` → onSelect={(id) => setSelected(id)}
2252
+ - ❌ `<Tree Mutating the data array to expand/collapse>` → Track expandedIds in state (or use defaultExpandedIds)
2253
+ - ❌ `<Tree Using DropdownMenu submenus for a file tree>` → Use <Tree> for file explorers / nested nav
2254
+ - ❌ `<OTPInput onChange={(e) => set(e.target.value)}>` → onChange={(value) => setCode(value)}
2255
+ - ❌ `<OTPInput Six separate <Input> boxes wired by hand>` → Use <OTPInput length={6} value onChange />
2256
+ - ❌ `<OTPInput Reading completion by comparing length yourself>` → Use onComplete={(code) => verify(code)}
2257
+ - ❌ `<OTPInput type="password" to hide digits>` → Use mask (type stays numeric/alphanumeric)
2258
+ - ❌ `<Carousel onChange={(e) => set(e.target.value)}>` → onChange={(index) => setIndex(index)}
2259
+ - ❌ `<Carousel Putting raw elements directly in Carousel>` → Wrap each slide in <CarouselSlide>
2260
+ - ❌ `<Carousel Building a slider with manual scroll + dot state>` → Use <Carousel> with CarouselSlide children
2261
+ - ❌ `<Carousel autoPlay without considering reduced motion / pausing>` → Carousel already pauses on hover/focus; keep interval reasonable or omit autoPlay
2262
+ - ❌ `<CarouselSlide <CarouselSlide> outside <Carousel>>` → Always nest CarouselSlide inside Carousel
1663
2263
  - ❌ `<DateRangePicker value={[from, to]}>` → Use value={{ from, to }} and read range.from / range.to
1664
2264
  - ❌ `<DateRangePicker DateRangePicker for a single date>` → Use <DatePicker /> for a single date
1665
2265
  - ❌ `<DateRangePicker presets="true" (string)>` → Use the bare prop: presets (or presets={true})