@zscloud/design 0.2.0 → 0.3.1

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.
@@ -6,12 +6,15 @@ declare const cardVariants: (props?: ({
6
6
  declare const cardContentVariants: (props?: ({
7
7
  variant?: "default" | "create" | "detail" | "plain" | null | undefined;
8
8
  } & import('class-variance-authority/types').ClassProp) | undefined) => string;
9
+ type CardVariant = "default" | "create" | "detail" | "plain" | null | undefined;
9
10
  export declare function useCardContext(): {
10
11
  isCollapsed: boolean;
11
12
  toggleCollapse: () => void;
13
+ variant: CardVariant;
12
14
  };
13
- export declare function CardProvider({ children }: {
15
+ export declare function CardProvider({ children, variant, }: {
14
16
  children: React.ReactNode;
17
+ variant?: CardVariant;
15
18
  }): import("react/jsx-runtime").JSX.Element;
16
19
  export interface CardProps extends React.HTMLAttributes<HTMLDivElement>, VariantProps<typeof cardVariants> {
17
20
  }
@@ -0,0 +1,28 @@
1
+ # @zscloud/design - Design System Guidelines
2
+
3
+ This is a React component library built on Radix UI primitives and Tailwind CSS v4. It provides 70+ production-ready components for building cloud management interfaces.
4
+
5
+ ## How to use these guidelines
6
+
7
+ 1. Always read all files whose name starts with `overview-` to understand available components and icons.
8
+ 2. When generating code for a specific component, read the corresponding file in `components/` for detailed API documentation.
9
+ 3. When applying colors, typography, spacing, or shadows, read the corresponding file in `design-tokens/`.
10
+
11
+ ## Quick start
12
+
13
+ ```tsx
14
+ import { Button, Dialog, Select, Input } from "@zscloud/design"
15
+ import "@zscloud/design/dist/style.css"
16
+ ```
17
+
18
+ ## Key rules
19
+
20
+ - Always import components from `@zscloud/design`. Do not use raw HTML elements when a component exists.
21
+ - Always import the CSS file `@zscloud/design/dist/style.css` once at the application root.
22
+ - Use Tailwind CSS v4 utility classes for layout and custom styling. Do not write inline styles.
23
+ - Use semantic color tokens (e.g., `text-danger-500`, `bg-theme-500`) instead of raw hex values.
24
+ - The default control height is `h-8` (32px). Do not override unless specifically required.
25
+ - The default border radius is `rounded-xs` (2px).
26
+ - Spacing follows a 4px grid. Use multiples of 4px (e.g., `gap-2` = 8px, `p-4` = 16px).
27
+ - Dark mode is supported via the `.dark` class on `<html>`. All semantic tokens automatically adapt.
28
+ - The component library includes bilingual i18n (zh-CN / en-US). Wrap your app with `LocaleContainer` to enable translations.
@@ -0,0 +1,73 @@
1
+ # Button
2
+
3
+ Use for all clickable actions. Do not use raw `<button>` elements.
4
+
5
+ ## Import
6
+
7
+ ```tsx
8
+ import { Button } from "@zscloud/design"
9
+ ```
10
+
11
+ ## Variants
12
+
13
+ | Variant | Use for |
14
+ |---------|---------|
15
+ | `primary` | Main action (submit, confirm). One per section. |
16
+ | `subtle` | Secondary actions (cancel, back) |
17
+ | `outline` | Alternative secondary, bordered |
18
+ | `danger` | Destructive actions (delete, remove) |
19
+ | `ghost` | Minimal emphasis, toolbar actions |
20
+ | `link` | Navigation-style action |
21
+
22
+ ## Sizes
23
+
24
+ | Size | Height | Use for |
25
+ |------|--------|---------|
26
+ | `default` | 32px | Standard controls |
27
+ | `sm` | 28px | Compact areas, tables |
28
+ | `lg` | 40px | Prominent actions |
29
+ | `icon` | 32x32px | Icon-only buttons |
30
+
31
+ ## Props
32
+
33
+ | Prop | Type | Default | Description |
34
+ |------|------|---------|-------------|
35
+ | `variant` | `"primary" \| "subtle" \| "outline" \| "secondary" \| "danger" \| "ghost" \| "ghost2" \| "link"` | `"primary"` | Visual style |
36
+ | `size` | `"default" \| "sm" \| "lg" \| "icon"` | `"default"` | Button size |
37
+ | `icon` | `ReactNode` | - | Icon displayed before text |
38
+ | `loading` | `boolean` | `false` | Shows spinner, disables button |
39
+ | `disabled` | `boolean` | `false` | Disables interaction |
40
+ | `asChild` | `boolean` | `false` | Render as child element (Slot) |
41
+
42
+ ## Examples
43
+
44
+ ```tsx
45
+ // Primary action
46
+ <Button>Submit</Button>
47
+
48
+ // With icon
49
+ <Button icon={<Icon type="add" />}>Create Resource</Button>
50
+
51
+ // Loading state
52
+ <Button loading>Saving...</Button>
53
+
54
+ // Danger action
55
+ <Button variant="danger">Delete</Button>
56
+
57
+ // Icon-only
58
+ <Button variant="ghost" size="icon"><Icon type="settings" /></Button>
59
+
60
+ // Button group
61
+ <div className="flex gap-2">
62
+ <Button variant="subtle">Cancel</Button>
63
+ <Button>Confirm</Button>
64
+ </div>
65
+ ```
66
+
67
+ ## Rules
68
+
69
+ - Use only one `primary` button per logical section.
70
+ - Always pair `primary` with `subtle` for confirm/cancel patterns.
71
+ - Use `danger` variant for destructive actions. Do not use `primary` with red styling.
72
+ - Always show a loading state during async operations. Do not leave the button enabled.
73
+ - Place the primary action on the right side in button groups.
@@ -0,0 +1,49 @@
1
+ # DatePicker
2
+
3
+ Use for date and date-time selection.
4
+
5
+ ## Import
6
+
7
+ ```tsx
8
+ import { DatePicker, RangePicker, TimePicker } from "@zscloud/design"
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```tsx
14
+ // Date only
15
+ <DatePicker value={date} onChange={setDate} />
16
+
17
+ // Date + time
18
+ <DatePicker value={date} onChange={setDate} showTime />
19
+
20
+ // Date range
21
+ <RangePicker value={range} onChange={setRange} />
22
+
23
+ // Time only
24
+ <TimePicker value={time} onChange={setTime} />
25
+ ```
26
+
27
+ ## Props (DatePicker)
28
+
29
+ | Prop | Type | Default | Description |
30
+ |------|------|---------|-------------|
31
+ | `value` | `Date \| Moment \| undefined` | - | Selected date |
32
+ | `onChange` | `(val) => void` | - | Change handler |
33
+ | `showTime` | `boolean` | `false` | Include time selection |
34
+ | `showNow` | `boolean` | `true` | Show "Now" button |
35
+ | `disabledDate` | `(date: Date) => boolean` | - | Disable specific dates |
36
+ | `disabledTime` | `(date: Date) => DisabledTimeConfig` | - | Disable specific times |
37
+ | `placeholder` | `string` | - | Placeholder text |
38
+ | `format` | `string` | - | Display format |
39
+ | `disabled` | `boolean` | `false` | Disable interaction |
40
+ | `allowClear` | `boolean` | `true` | Show clear button |
41
+ | `width` | `number \| string` | - | Custom width |
42
+
43
+ ## Rules
44
+
45
+ - Use `DatePicker` for single date selection.
46
+ - Use `RangePicker` for date range selection.
47
+ - Use `TimePicker` for time-only selection.
48
+ - Use `disabledDate` to prevent selecting past dates or future dates.
49
+ - Set `showTime` only when time precision is required.
@@ -0,0 +1,93 @@
1
+ # Dialog
2
+
3
+ Use for modal interactions: confirmations, forms, and detailed content that requires focus.
4
+
5
+ ## Import
6
+
7
+ ```tsx
8
+ import {
9
+ Dialog, DialogContent, DialogHeader, DialogFooter,
10
+ DialogTitle, DialogBody, DialogBanner, DialogScrollArea
11
+ } from "@zscloud/design"
12
+ ```
13
+
14
+ ## Structure
15
+
16
+ ```tsx
17
+ <Dialog open={open} onOpenChange={setOpen}>
18
+ <DialogContent>
19
+ <DialogHeader>
20
+ <DialogTitle>Title</DialogTitle>
21
+ </DialogHeader>
22
+ <DialogBody>
23
+ {/* Content */}
24
+ </DialogBody>
25
+ <DialogFooter>
26
+ <Button variant="subtle" onClick={() => setOpen(false)}>Cancel</Button>
27
+ <Button>Confirm</Button>
28
+ </DialogFooter>
29
+ </DialogContent>
30
+ </Dialog>
31
+ ```
32
+
33
+ ## Sub-components
34
+
35
+ | Component | Purpose |
36
+ |-----------|---------|
37
+ | `DialogContent` | Container with overlay, animation, close button |
38
+ | `DialogHeader` | Top section. Variants: primary, secondary |
39
+ | `DialogTitle` | Dialog heading. Variants: normal, weak |
40
+ | `DialogBody` | Main content area. Variants: primary, secondary |
41
+ | `DialogFooter` | Action buttons area |
42
+ | `DialogBanner` | Alert banner below header. Variants: danger, warning, info |
43
+ | `DialogScrollArea` | Scrollable content area within body |
44
+ | `DialogDivider` | Horizontal separator line |
45
+
46
+ ## Props (DialogContent)
47
+
48
+ | Prop | Type | Default | Description |
49
+ |------|------|---------|-------------|
50
+ | `zIndex` | `number` | auto | Override z-index |
51
+ | `disableAutoZIndex` | `boolean` | `false` | Disable automatic z-index |
52
+
53
+ ## Examples
54
+
55
+ ### With danger banner
56
+
57
+ ```tsx
58
+ <Dialog open={open} onOpenChange={setOpen}>
59
+ <DialogContent>
60
+ <DialogHeader>
61
+ <DialogTitle>Delete Resource</DialogTitle>
62
+ </DialogHeader>
63
+ <DialogBanner variant="danger">
64
+ This action cannot be undone.
65
+ </DialogBanner>
66
+ <DialogBody>
67
+ Are you sure you want to delete this resource?
68
+ </DialogBody>
69
+ <DialogFooter>
70
+ <Button variant="subtle" onClick={() => setOpen(false)}>Cancel</Button>
71
+ <Button variant="danger">Delete</Button>
72
+ </DialogFooter>
73
+ </DialogContent>
74
+ </Dialog>
75
+ ```
76
+
77
+ ### With scrollable content
78
+
79
+ ```tsx
80
+ <DialogBody>
81
+ <DialogScrollArea>
82
+ {/* Long content */}
83
+ </DialogScrollArea>
84
+ </DialogBody>
85
+ ```
86
+
87
+ ## Rules
88
+
89
+ - Always provide a `DialogTitle` for accessibility.
90
+ - Always include a cancel/close mechanism.
91
+ - Use `DialogBanner` with `variant="danger"` for destructive confirmations.
92
+ - The z-index is managed automatically. Do not set manual z-index unless dealing with stacking issues.
93
+ - Use `AlertDialog` instead of `Dialog` for simple confirm/cancel interactions without form content.
@@ -0,0 +1,48 @@
1
+ # Drawer
2
+
3
+ Use for side panel overlays displaying detail views, forms, or secondary content.
4
+
5
+ ## Import
6
+
7
+ ```tsx
8
+ import { Drawer, DrawerHeader, DrawerBody, DrawerFooter } from "@zscloud/design"
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```tsx
14
+ <Drawer open={open} setOpen={setOpen} placement="right">
15
+ <DrawerHeader>Detail View</DrawerHeader>
16
+ <DrawerBody>
17
+ {/* Content */}
18
+ </DrawerBody>
19
+ <DrawerFooter>
20
+ <Button variant="subtle" onClick={() => setOpen(false)}>Close</Button>
21
+ <Button>Save</Button>
22
+ </DrawerFooter>
23
+ </Drawer>
24
+ ```
25
+
26
+ ## Props (Drawer)
27
+
28
+ | Prop | Type | Default | Description |
29
+ |------|------|---------|-------------|
30
+ | `open` | `boolean` | required | Visibility state |
31
+ | `setOpen` | `(open: boolean) => void` | required | State setter |
32
+ | `placement` | `"right" \| "left" \| "top" \| "bottom"` | `"right"` | Slide direction |
33
+ | `zIndex` | `number` | auto | Override z-index |
34
+
35
+ ## Props (DrawerHeader)
36
+
37
+ | Prop | Type | Default | Description |
38
+ |------|------|---------|-------------|
39
+ | `closable` | `boolean` | `true` | Show close button |
40
+ | `onClose` | `() => void` | - | Close handler |
41
+
42
+ ## Rules
43
+
44
+ - Use `placement="right"` (default) for detail panels and forms.
45
+ - Use `placement="bottom"` for mobile-style bottom sheets.
46
+ - Always include a `DrawerHeader` with a title.
47
+ - For forms inside drawers, put action buttons in `DrawerFooter`.
48
+ - Do not nest drawers within drawers. Use a dialog instead.
@@ -0,0 +1,100 @@
1
+ # Form
2
+
3
+ Form system built on react-hook-form + zod for validation.
4
+
5
+ ## Import
6
+
7
+ ```tsx
8
+ import {
9
+ Form, FormField, FormItem, FormLabel,
10
+ FormControl, FormMessage, FormDescription,
11
+ FormRowContainer, FormHint, FormNestedContainer
12
+ } from "@zscloud/design"
13
+ import { useForm } from "react-hook-form"
14
+ import { zodResolver } from "@hookform/resolvers/zod"
15
+ import { z } from "zod"
16
+ ```
17
+
18
+ ## Standard pattern
19
+
20
+ ```tsx
21
+ const schema = z.object({
22
+ name: z.string().min(1, "Required"),
23
+ email: z.string().email("Invalid email"),
24
+ })
25
+
26
+ type FormValues = z.infer<typeof schema>
27
+
28
+ function MyForm() {
29
+ const form = useForm<FormValues>({
30
+ resolver: zodResolver(schema),
31
+ defaultValues: { name: "", email: "" },
32
+ })
33
+
34
+ return (
35
+ <Form {...form}>
36
+ <form onSubmit={form.handleSubmit(onSubmit)} className="flex flex-col gap-3">
37
+ <FormField
38
+ control={form.control}
39
+ name="name"
40
+ render={({ field }) => (
41
+ <FormItem>
42
+ <FormLabel required>Name</FormLabel>
43
+ <FormControl>
44
+ <Input {...field} />
45
+ </FormControl>
46
+ <FormMessage />
47
+ </FormItem>
48
+ )}
49
+ />
50
+ <FormField
51
+ control={form.control}
52
+ name="email"
53
+ render={({ field }) => (
54
+ <FormItem>
55
+ <FormLabel required>Email</FormLabel>
56
+ <FormControl>
57
+ <Input {...field} />
58
+ </FormControl>
59
+ <FormDescription>We will not share your email.</FormDescription>
60
+ <FormMessage />
61
+ </FormItem>
62
+ )}
63
+ />
64
+ </form>
65
+ </Form>
66
+ )
67
+ }
68
+ ```
69
+
70
+ ## Sub-components
71
+
72
+ | Component | Purpose |
73
+ |-----------|---------|
74
+ | `Form` | Provider wrapper (react-hook-form FormProvider) |
75
+ | `FormField` | Controller for a single field |
76
+ | `FormItem` | Container for label + control + message |
77
+ | `FormLabel` | Field label. Set `required` prop for asterisk |
78
+ | `FormControl` | Wraps the input, connects aria attributes |
79
+ | `FormMessage` | Validation error message |
80
+ | `FormDescription` | Helper text below the input |
81
+ | `FormHint` | Small hint text (text-xs) |
82
+ | `FormRowContainer` | Horizontal layout for inline fields |
83
+ | `FormNestedContainer` | Indented nested section with left border |
84
+ | `FormRequiredIndicator` | Red asterisk (*) |
85
+
86
+ ## FormLabel props
87
+
88
+ | Prop | Type | Description |
89
+ |------|------|-------------|
90
+ | `required` | `boolean` | Shows red asterisk |
91
+ | `info` | `ReactNode` | Shows Info icon with tooltip |
92
+
93
+ ## Rules
94
+
95
+ - Always use zod schemas for validation. Do not use manual validation logic.
96
+ - Always wrap forms with the `Form` component.
97
+ - Use `FormLabel` with `required` prop for mandatory fields. Do not manually add asterisks.
98
+ - Use `FormMessage` for validation errors. Do not render errors manually.
99
+ - Use `gap-3` (12px) between `FormItem` elements vertically.
100
+ - Use `FormRowContainer` for side-by-side fields, not custom flex layouts.
@@ -0,0 +1,81 @@
1
+ # Select
2
+
3
+ Use for single-value selection from a list of options.
4
+
5
+ ## Import
6
+
7
+ ```tsx
8
+ import { Select } from "@zscloud/design"
9
+ ```
10
+
11
+ ## Simple usage (recommended)
12
+
13
+ ```tsx
14
+ const options = [
15
+ { label: "Option A", value: "a" },
16
+ { label: "Option B", value: "b", description: "With helper text" },
17
+ ]
18
+
19
+ <Select
20
+ options={options}
21
+ value={value}
22
+ onValueChange={setValue}
23
+ placeholder="Select an option"
24
+ />
25
+ ```
26
+
27
+ ## Props
28
+
29
+ | Prop | Type | Default | Description |
30
+ |------|------|---------|-------------|
31
+ | `options` | `SelectOptions[]` | required | List of options |
32
+ | `value` | `string` | - | Controlled value |
33
+ | `defaultValue` | `string` | - | Uncontrolled default |
34
+ | `onValueChange` | `(value: string) => void` | required | Change handler |
35
+ | `placeholder` | `string` | - | Placeholder text |
36
+ | `disabled` | `boolean` | `false` | Disable interaction |
37
+ | `allowClear` | `boolean` | `false` | Show clear button |
38
+ | `onClear` | `() => void` | - | Clear handler |
39
+ | `emptyPlaceholderText` | `string` | - | Text when no options |
40
+
41
+ ## Option shape
42
+
43
+ ```typescript
44
+ interface SelectOptions {
45
+ label: ReactNode // Display text in dropdown
46
+ selectedLabel?: ReactNode // Display in trigger when selected (optional)
47
+ description?: string // Secondary text below label
48
+ value: string // Unique value
49
+ }
50
+ ```
51
+
52
+ ## Composable usage (advanced)
53
+
54
+ For custom layouts, use the primitive sub-components:
55
+
56
+ ```tsx
57
+ import {
58
+ SelectRoot, SelectTrigger, SelectValue,
59
+ SelectContent, SelectGroup, SelectItem
60
+ } from "@zscloud/design"
61
+
62
+ <SelectRoot value={value} onValueChange={setValue}>
63
+ <SelectTrigger>
64
+ <SelectValue placeholder="Choose..." />
65
+ </SelectTrigger>
66
+ <SelectContent>
67
+ <SelectGroup>
68
+ <SelectItem value="a">Option A</SelectItem>
69
+ <SelectItem value="b">Option B</SelectItem>
70
+ </SelectGroup>
71
+ </SelectContent>
72
+ </SelectRoot>
73
+ ```
74
+
75
+ ## Rules
76
+
77
+ - Use the simple `Select` with `options` prop for standard cases.
78
+ - Use the composable pattern only when you need custom item rendering.
79
+ - For multi-selection, use `MultiSelect` instead.
80
+ - For searchable selection, use `AutoComplete` instead.
81
+ - For hierarchical selection, use `Cascader` instead.
@@ -0,0 +1,73 @@
1
+ # Tabs
2
+
3
+ Use for switching between related content panels.
4
+
5
+ ## Import
6
+
7
+ ```tsx
8
+ import { Tabs } from "@zscloud/design"
9
+ ```
10
+
11
+ ## Simple usage
12
+
13
+ ```tsx
14
+ const tabsList = [
15
+ { value: "overview", label: "Overview", content: <OverviewPanel /> },
16
+ { value: "config", label: "Configuration", content: <ConfigPanel /> },
17
+ { value: "logs", label: "Logs", content: <LogsPanel />, disabled: true },
18
+ ]
19
+
20
+ <Tabs tabsList={tabsList} defaultValue="overview" />
21
+ ```
22
+
23
+ ## Props
24
+
25
+ | Prop | Type | Default | Description |
26
+ |------|------|---------|-------------|
27
+ | `tabsList` | `TabsListItem[]` | required | Tab definitions |
28
+ | `value` | `string` | - | Controlled active tab |
29
+ | `defaultValue` | `string` | - | Initial active tab |
30
+ | `onValueChange` | `(value: string) => void` | - | Tab change handler |
31
+ | `variant` | `"line" \| "card" \| "outline"` | `"line"` | Visual style |
32
+ | `forceMount` | `true` | - | Keep inactive tab DOM alive |
33
+ | `contentId` | `string` | - | Enable tab state persistence |
34
+
35
+ ## TabsListItem
36
+
37
+ ```typescript
38
+ interface TabsListItem {
39
+ value: string // Unique identifier
40
+ label?: ReactNode // Tab trigger text
41
+ content?: ReactNode // Tab panel content
42
+ disabled?: boolean // Disable this tab
43
+ }
44
+ ```
45
+
46
+ ## Variants
47
+
48
+ | Variant | Use for |
49
+ |---------|---------|
50
+ | `line` | Default. Underline indicator. Use in page sections. |
51
+ | `card` | Card-style tabs. Use for container switching. |
52
+ | `outline` | Bordered tabs. Use for compact layouts. |
53
+
54
+ ## Composable usage
55
+
56
+ ```tsx
57
+ import { TabsRoot, TabsList, TabsTrigger, TabsContent } from "@zscloud/design"
58
+
59
+ <TabsRoot defaultValue="tab1">
60
+ <TabsList>
61
+ <TabsTrigger value="tab1">Tab 1</TabsTrigger>
62
+ <TabsTrigger value="tab2">Tab 2</TabsTrigger>
63
+ </TabsList>
64
+ <TabsContent value="tab1">Content 1</TabsContent>
65
+ <TabsContent value="tab2">Content 2</TabsContent>
66
+ </TabsRoot>
67
+ ```
68
+
69
+ ## Rules
70
+
71
+ - Use the simple `Tabs` with `tabsList` prop for standard cases.
72
+ - Use `forceMount` when tab panels contain stateful content that should not be destroyed.
73
+ - Do not nest tabs within tabs. Use a different navigation pattern for deeper hierarchy.
@@ -0,0 +1,60 @@
1
+ # Toast
2
+
3
+ Use for temporary notification messages (success, error, info).
4
+
5
+ ## Import
6
+
7
+ ```tsx
8
+ import { toast, Toaster } from "@zscloud/design"
9
+ ```
10
+
11
+ ## Setup
12
+
13
+ Add `Toaster` once at your application root:
14
+
15
+ ```tsx
16
+ function App() {
17
+ return (
18
+ <>
19
+ <Toaster />
20
+ {/* App content */}
21
+ </>
22
+ )
23
+ }
24
+ ```
25
+
26
+ ## Usage
27
+
28
+ ```tsx
29
+ // Success
30
+ toast({ title: "Resource created successfully", indicator: "success" })
31
+
32
+ // Error
33
+ toast({ title: "Failed to create resource", variant: "destructive" })
34
+
35
+ // With description
36
+ toast({
37
+ title: "Operation complete",
38
+ description: "3 resources were updated.",
39
+ indicator: "success",
40
+ })
41
+
42
+ // With action
43
+ toast({
44
+ title: "Resource deleted",
45
+ action: <ToastAction altText="Undo">Undo</ToastAction>,
46
+ })
47
+
48
+ // Programmatic dismiss
49
+ const { dismiss } = toast({ title: "Processing..." })
50
+ // Later:
51
+ dismiss()
52
+ ```
53
+
54
+ ## Rules
55
+
56
+ - Always place `Toaster` at the application root, once.
57
+ - Use `indicator: "success"` for success messages. Use `variant: "destructive"` for errors.
58
+ - Keep toast messages concise (under 10 words for title).
59
+ - Do not use toasts for critical errors that require user action. Use `Dialog` or `Alert` instead.
60
+ - Toasts auto-dismiss. Do not set excessively long durations.
@@ -0,0 +1,40 @@
1
+ # Tooltip
2
+
3
+ Use for hover-triggered explanatory text. Do not use for interactive content.
4
+
5
+ ## Import
6
+
7
+ ```tsx
8
+ import { Tooltip } from "@zscloud/design"
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```tsx
14
+ <Tooltip title="This is a helpful explanation">
15
+ <Button variant="ghost" size="icon">
16
+ <Icon type="info-circle" />
17
+ </Button>
18
+ </Tooltip>
19
+ ```
20
+
21
+ ## Props
22
+
23
+ | Prop | Type | Default | Description |
24
+ |------|------|---------|-------------|
25
+ | `title` | `ReactNode` | required | Tooltip content |
26
+ | `placement` | Position string | `"top"` | Where to show the tooltip |
27
+ | `disappearOnClick` | `boolean` | `true` | Hide on click |
28
+ | `delayDuration` | `number` | `0` | Delay before showing (ms) |
29
+
30
+ ## Placement options
31
+
32
+ `topLeft`, `top`, `topRight`, `leftTop`, `left`, `leftBottom`, `rightTop`, `right`, `rightBottom`, `bottomLeft`, `bottom`, `bottomRight`
33
+
34
+ ## Rules
35
+
36
+ - Use `Tooltip` for non-interactive explanatory text only.
37
+ - For interactive content (links, buttons), use `Popover` instead.
38
+ - Keep tooltip text concise (1-2 sentences max).
39
+ - Do not put tooltips on disabled elements. Use the `Info` component for persistent hints.
40
+ - Delay is 0ms by default. Do not add delay unless showing many tooltips in a dense area.