sheerly 0.0.1 → 0.0.3

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 (90) hide show
  1. package/lib/accordion.d.ts +13 -4
  2. package/lib/alert.d.ts +22 -7
  3. package/lib/avatar.css +1 -1
  4. package/lib/avatar.d.ts +21 -8
  5. package/lib/badge.d.ts +14 -4
  6. package/lib/breadcrumb.d.ts +49 -8
  7. package/lib/button.css +1 -1
  8. package/lib/button.d.ts +13 -4
  9. package/lib/card.d.ts +32 -12
  10. package/lib/carousel.d.ts +13 -7
  11. package/lib/checkbox.d.ts +6 -0
  12. package/lib/dialog.d.ts +20 -8
  13. package/lib/dropdown-menu.d.ts +39 -17
  14. package/lib/hover-card.d.ts +15 -6
  15. package/lib/index.css +1 -1
  16. package/lib/index.d.ts +1 -1
  17. package/lib/index.js +308 -300
  18. package/lib/input.css +1 -1
  19. package/lib/input.d.ts +6 -0
  20. package/lib/kbd.d.ts +10 -3
  21. package/lib/label.css +1 -1
  22. package/lib/label.d.ts +6 -0
  23. package/lib/pagination.d.ts +52 -13
  24. package/lib/popover.d.ts +15 -6
  25. package/lib/progress.d.ts +6 -0
  26. package/lib/radio.d.ts +13 -3
  27. package/lib/scrollbar.css +1 -0
  28. package/lib/scrollbar.d.ts +17 -0
  29. package/lib/select.css +1 -1
  30. package/lib/select.d.ts +6 -0
  31. package/lib/separator.d.ts +14 -4
  32. package/lib/sheet.d.ts +19 -6
  33. package/lib/skeleton.d.ts +12 -3
  34. package/lib/slider.d.ts +6 -0
  35. package/lib/spinner.d.ts +10 -4
  36. package/lib/switch.d.ts +6 -0
  37. package/lib/table.css +1 -1
  38. package/lib/table.d.ts +14 -3
  39. package/lib/tabs.d.ts +37 -11
  40. package/lib/textarea.d.ts +6 -0
  41. package/lib/toggle.d.ts +7 -0
  42. package/lib/tooltip.d.ts +15 -6
  43. package/lib/typography.d.ts +12 -4
  44. package/lib/utils/polymorphic.d.ts +14 -0
  45. package/package.json +34 -33
  46. package/src/accordion.tsx +20 -6
  47. package/src/alert.tsx +52 -14
  48. package/src/avatar.css +0 -1
  49. package/src/avatar.tsx +44 -15
  50. package/src/badge.tsx +34 -16
  51. package/src/breadcrumb.tsx +97 -26
  52. package/src/button.css +1 -0
  53. package/src/button.tsx +44 -27
  54. package/src/card.tsx +82 -23
  55. package/src/carousel.tsx +28 -11
  56. package/src/checkbox.tsx +7 -1
  57. package/src/dialog.tsx +55 -16
  58. package/src/dropdown-menu.tsx +115 -33
  59. package/src/hover-card.tsx +28 -9
  60. package/src/index.css +1 -0
  61. package/src/index.ts +1 -1
  62. package/src/input.css +1 -0
  63. package/src/input.tsx +7 -1
  64. package/src/kbd.tsx +17 -5
  65. package/src/label.css +1 -0
  66. package/src/label.tsx +7 -1
  67. package/src/pagination.tsx +107 -35
  68. package/src/popover.tsx +28 -9
  69. package/src/progress.tsx +7 -1
  70. package/src/radio.tsx +20 -5
  71. package/src/scrollbar.css +56 -0
  72. package/src/scrollbar.tsx +28 -0
  73. package/src/select.css +24 -0
  74. package/src/select.tsx +7 -1
  75. package/src/separator.tsx +23 -7
  76. package/src/sheet.tsx +53 -13
  77. package/src/skeleton.tsx +19 -5
  78. package/src/slider.tsx +7 -1
  79. package/src/spinner.tsx +21 -8
  80. package/src/switch.tsx +7 -1
  81. package/src/table.css +0 -1
  82. package/src/table.tsx +21 -5
  83. package/src/tabs.tsx +65 -25
  84. package/src/textarea.tsx +7 -1
  85. package/src/toggle.tsx +8 -1
  86. package/src/tooltip.tsx +28 -9
  87. package/src/typography.tsx +19 -5
  88. package/src/utils/polymorphic.ts +19 -0
  89. /package/lib/{cn.d.ts → utils/cn.d.ts} +0 -0
  90. /package/src/{cn.ts → utils/cn.ts} +0 -0
@@ -1,9 +1,31 @@
1
- import type { AnchorHTMLAttributes, HTMLAttributes } from 'react'
2
- import { cn } from './cn'
1
+ import type { ElementType } from 'react'
2
+ import { cn } from './utils/cn'
3
+ import type { PolymorphicProps } from './utils/polymorphic'
3
4
 
4
- export function Pagination({ className, ...props }: HTMLAttributes<HTMLElement>) {
5
+ export type PaginationProps<T extends ElementType = 'nav'> = PolymorphicProps<T>
6
+
7
+ /**
8
+ * Page navigation. Mark the current page's link with `isActive`.
9
+ *
10
+ * Polymorphic: pass `as` to change the rendered tag (defaults to `nav`).
11
+ *
12
+ * @example
13
+ * <Pagination>
14
+ * <PaginationContent>
15
+ * <PaginationItem><PaginationPrevious href="#" /></PaginationItem>
16
+ * <PaginationItem><PaginationLink href="#" isActive>1</PaginationLink></PaginationItem>
17
+ * <PaginationItem><PaginationNext href="#" /></PaginationItem>
18
+ * </PaginationContent>
19
+ * </Pagination>
20
+ */
21
+ export function Pagination<T extends ElementType = 'nav'>({
22
+ as,
23
+ className,
24
+ ...props
25
+ }: PaginationProps<T>) {
26
+ const Tag = as ?? 'nav'
5
27
  return (
6
- <nav
28
+ <Tag
7
29
  role="navigation"
8
30
  aria-label="pagination"
9
31
  className={cn('sheerly-pagination', className)}
@@ -12,30 +34,56 @@ export function Pagination({ className, ...props }: HTMLAttributes<HTMLElement>)
12
34
  )
13
35
  }
14
36
 
15
- export function PaginationContent({
37
+ export type PaginationContentProps<T extends ElementType = 'ul'> = PolymorphicProps<T>
38
+
39
+ /**
40
+ * Polymorphic: pass `as` to change the rendered tag (defaults to `ul`).
41
+ */
42
+ export function PaginationContent<T extends ElementType = 'ul'>({
43
+ as,
16
44
  className,
17
45
  ...props
18
- }: HTMLAttributes<HTMLUListElement>) {
19
- return <ul className={cn('sheerly-pagination-content', className)} {...props} />
46
+ }: PaginationContentProps<T>) {
47
+ const Tag = as ?? 'ul'
48
+ return <Tag className={cn('sheerly-pagination-content', className)} {...props} />
20
49
  }
21
50
 
22
- export function PaginationItem({ className, ...props }: HTMLAttributes<HTMLLIElement>) {
23
- return <li className={cn('sheerly-pagination-item', className)} {...props} />
24
- }
51
+ export type PaginationItemProps<T extends ElementType = 'li'> = PolymorphicProps<T>
25
52
 
26
- export interface PaginationLinkProps extends AnchorHTMLAttributes<HTMLAnchorElement> {
27
- /** Marks the current page (renders like the outline button). */
28
- isActive?: boolean
29
- 'data-role'?: 'previous' | 'next'
53
+ /**
54
+ * Polymorphic: pass `as` to change the rendered tag (defaults to `li`).
55
+ */
56
+ export function PaginationItem<T extends ElementType = 'li'>({
57
+ as,
58
+ className,
59
+ ...props
60
+ }: PaginationItemProps<T>) {
61
+ const Tag = as ?? 'li'
62
+ return <Tag className={cn('sheerly-pagination-item', className)} {...props} />
30
63
  }
31
64
 
32
- export function PaginationLink({
65
+ export type PaginationLinkProps<T extends ElementType = 'a'> = PolymorphicProps<
66
+ T,
67
+ {
68
+ /** Marks the current page (renders like the outline button). */
69
+ isActive?: boolean
70
+ 'data-role'?: 'previous' | 'next'
71
+ }
72
+ >
73
+
74
+ /**
75
+ * Polymorphic: pass `as` to change the rendered tag (defaults to `a`, e.g.
76
+ * `as={Link}` for a router link).
77
+ */
78
+ export function PaginationLink<T extends ElementType = 'a'>({
79
+ as,
33
80
  className,
34
81
  isActive,
35
82
  ...props
36
- }: PaginationLinkProps) {
83
+ }: PaginationLinkProps<T>) {
84
+ const Tag = as ?? 'a'
37
85
  return (
38
- <a
86
+ <Tag
39
87
  aria-current={isActive ? 'page' : undefined}
40
88
  className={cn('sheerly-pagination-link', className)}
41
89
  {...props}
@@ -43,53 +91,77 @@ export function PaginationLink({
43
91
  )
44
92
  }
45
93
 
46
- export function PaginationPrevious({
94
+ export type PaginationPreviousProps<T extends ElementType = 'a'> = PaginationLinkProps<T> & {
95
+ /** Visible label. Children override it. */
96
+ text?: string
97
+ }
98
+
99
+ /**
100
+ * Polymorphic: pass `as` to change the rendered tag (defaults to `a`).
101
+ */
102
+ export function PaginationPrevious<T extends ElementType = 'a'>({
103
+ as,
47
104
  className,
105
+ isActive,
48
106
  text = 'Previous',
49
107
  children,
50
108
  ...props
51
- }: PaginationLinkProps & { text?: string }) {
109
+ }: PaginationPreviousProps<T>) {
110
+ const Tag = as ?? 'a'
52
111
  return (
53
- <PaginationLink
112
+ <Tag
54
113
  aria-label="Go to previous page"
114
+ aria-current={isActive ? 'page' : undefined}
55
115
  data-role="previous"
56
- className={className}
116
+ className={cn('sheerly-pagination-link', className)}
57
117
  {...props}
58
118
  >
59
119
  {children ?? text}
60
- </PaginationLink>
120
+ </Tag>
61
121
  )
62
122
  }
63
123
 
64
- export function PaginationNext({
124
+ export type PaginationNextProps<T extends ElementType = 'a'> = PaginationPreviousProps<T>
125
+
126
+ /**
127
+ * Polymorphic: pass `as` to change the rendered tag (defaults to `a`).
128
+ */
129
+ export function PaginationNext<T extends ElementType = 'a'>({
130
+ as,
65
131
  className,
132
+ isActive,
66
133
  text = 'Next',
67
134
  children,
68
135
  ...props
69
- }: PaginationLinkProps & { text?: string }) {
136
+ }: PaginationNextProps<T>) {
137
+ const Tag = as ?? 'a'
70
138
  return (
71
- <PaginationLink
139
+ <Tag
72
140
  aria-label="Go to next page"
141
+ aria-current={isActive ? 'page' : undefined}
73
142
  data-role="next"
74
- className={className}
143
+ className={cn('sheerly-pagination-link', className)}
75
144
  {...props}
76
145
  >
77
146
  {children ?? text}
78
- </PaginationLink>
147
+ </Tag>
79
148
  )
80
149
  }
81
150
 
82
- export function PaginationEllipsis({
151
+ export type PaginationEllipsisProps<T extends ElementType = 'span'> = PolymorphicProps<T>
152
+
153
+ /**
154
+ * Polymorphic: pass `as` to change the rendered tag (defaults to `span`).
155
+ */
156
+ export function PaginationEllipsis<T extends ElementType = 'span'>({
157
+ as,
83
158
  className,
84
159
  ...props
85
- }: HTMLAttributes<HTMLSpanElement>) {
160
+ }: PaginationEllipsisProps<T>) {
161
+ const Tag = as ?? 'span'
86
162
  return (
87
- <span
88
- aria-hidden
89
- className={cn('sheerly-pagination-ellipsis', className)}
90
- {...props}
91
- >
163
+ <Tag aria-hidden className={cn('sheerly-pagination-ellipsis', className)} {...props}>
92
164
  &hellip;
93
- </span>
165
+ </Tag>
94
166
  )
95
167
  }
package/src/popover.tsx CHANGED
@@ -1,16 +1,35 @@
1
- import type { HTMLAttributes } from 'react'
2
- import { cn } from './cn'
1
+ import type { ElementType } from 'react'
2
+ import { cn } from './utils/cn'
3
+ import type { PolymorphicProps } from './utils/polymorphic'
3
4
 
4
- export type PopoverProps = HTMLAttributes<HTMLSpanElement>
5
+ export type PopoverProps<T extends ElementType = 'span'> = PolymorphicProps<T>
5
6
 
6
- /** Wrapper carrying the anchor. Nest the trigger button + content inside. */
7
- export function Popover({ className, ...props }: PopoverProps) {
8
- return <span className={cn('sheerly-popover', className)} {...props} />
7
+ /**
8
+ * Wrapper carrying the anchor. Nest the trigger button + content inside.
9
+ *
10
+ * @example
11
+ * <Popover>
12
+ * <Button commandfor="p" command="toggle-popover">Open</Button>
13
+ * <PopoverContent id="p" popover>Place content here.</PopoverContent>
14
+ * </Popover>
15
+ */
16
+ export function Popover<T extends ElementType = 'span'>({
17
+ as,
18
+ className,
19
+ ...props
20
+ }: PopoverProps<T>) {
21
+ const Tag = as ?? 'span'
22
+ return <Tag className={cn('sheerly-popover', className)} {...props} />
9
23
  }
10
24
 
11
- export type PopoverContentProps = HTMLAttributes<HTMLDivElement>
25
+ export type PopoverContentProps<T extends ElementType = 'div'> = PolymorphicProps<T>
12
26
 
13
27
  /** The floating panel. Give it `popover` and an `id` matched by the trigger's `commandfor`. */
14
- export function PopoverContent({ className, ...props }: PopoverContentProps) {
15
- return <div className={cn('sheerly-popover-content', className)} {...props} />
28
+ export function PopoverContent<T extends ElementType = 'div'>({
29
+ as,
30
+ className,
31
+ ...props
32
+ }: PopoverContentProps<T>) {
33
+ const Tag = as ?? 'div'
34
+ return <Tag className={cn('sheerly-popover-content', className)} {...props} />
16
35
  }
package/src/progress.tsx CHANGED
@@ -1,5 +1,5 @@
1
1
  import type { ProgressHTMLAttributes } from 'react'
2
- import { cn } from './cn'
2
+ import { cn } from './utils/cn'
3
3
 
4
4
  export interface ProgressProps extends ProgressHTMLAttributes<HTMLProgressElement> {
5
5
  'data-variant'?:
@@ -14,6 +14,12 @@ export interface ProgressProps extends ProgressHTMLAttributes<HTMLProgressElemen
14
14
  'data-size'?: 'sm' | 'default' | 'lg'
15
15
  }
16
16
 
17
+ /**
18
+ * A determinate progress bar. Set `value` (and `max`, default 1).
19
+ *
20
+ * @example
21
+ * <Progress value={60} max={100} data-variant="success" />
22
+ */
17
23
  export function Progress({ className, ...props }: ProgressProps) {
18
24
  return <progress className={cn('sheerly-progress', className)} {...props} />
19
25
  }
package/src/radio.tsx CHANGED
@@ -1,14 +1,29 @@
1
- import type { HTMLAttributes, InputHTMLAttributes } from 'react'
2
- import { cn } from './cn'
1
+ import type { ElementType, InputHTMLAttributes } from 'react'
2
+ import { cn } from './utils/cn'
3
+ import type { PolymorphicProps } from './utils/polymorphic'
3
4
 
4
- export type RadioGroupProps = HTMLAttributes<HTMLDivElement>
5
+ export type RadioGroupProps<T extends ElementType = 'div'> = PolymorphicProps<T>
5
6
 
6
- export function RadioGroup({ className, ...props }: RadioGroupProps) {
7
- return <div className={cn('sheerly-radio-group', className)} {...props} />
7
+ export function RadioGroup<T extends ElementType = 'div'>({
8
+ as,
9
+ className,
10
+ ...props
11
+ }: RadioGroupProps<T>) {
12
+ const Tag = as ?? 'div'
13
+ return <Tag className={cn('sheerly-radio-group', className)} {...props} />
8
14
  }
9
15
 
10
16
  export type RadioProps = Omit<InputHTMLAttributes<HTMLInputElement>, 'type'>
11
17
 
18
+ /**
19
+ * A radio `<input>`. Share a `name` and wrap options in a <RadioGroup>.
20
+ *
21
+ * @example
22
+ * <RadioGroup>
23
+ * <Label><Radio name="plan" value="free" defaultChecked /> Free</Label>
24
+ * <Label><Radio name="plan" value="pro" /> Pro</Label>
25
+ * </RadioGroup>
26
+ */
12
27
  export function Radio({ className, ...props }: RadioProps) {
13
28
  return <input type="radio" className={cn('sheerly-radio', className)} {...props} />
14
29
  }
@@ -0,0 +1,56 @@
1
+ /*
2
+ * Scrollbar — a sheerly-styled scrollbar for any overflow container.
3
+ * Apply `.sheerly-scrollbar` to a scrollable element to replace the native
4
+ * scrollbar with a thin, theme-tinted one (matching the select picker).
5
+ * You still set the element's own size + overflow (e.g. `max-height` +
6
+ * `overflow-y: auto`), or use a `data-orientation` helper below.
7
+ *
8
+ * Usage:
9
+ * <div class="sheerly-scrollbar" style="max-height: 200px; overflow-y: auto">…</div>
10
+ * <div class="sheerly-scrollbar" data-orientation="vertical" style="max-height: 200px">…</div>
11
+ * <div class="sheerly-scrollbar" data-orientation="horizontal">…</div>
12
+ *
13
+ * Latest Chrome only — uses standard `scrollbar-width`/`scrollbar-color` plus
14
+ * `::-webkit-scrollbar-*` for the fuller styled look.
15
+ */
16
+ .sheerly-scrollbar {
17
+ /* Standard properties (thin thumb tinted from the foreground). */
18
+ scrollbar-width: thin;
19
+ scrollbar-color: oklch(from var(--sheerly-foreground) l c h / 20%) transparent;
20
+
21
+ /* WebKit/Blink styled scrollbar. */
22
+ &::-webkit-scrollbar {
23
+ width: 8px;
24
+ height: 8px;
25
+ }
26
+
27
+ &::-webkit-scrollbar-track {
28
+ background: transparent;
29
+ }
30
+
31
+ &::-webkit-scrollbar-thumb {
32
+ border: 2px solid transparent;
33
+ border-radius: var(--sheerly-radius-full);
34
+ background-clip: padding-box;
35
+ background-color: oklch(from var(--sheerly-foreground) l c h / 20%);
36
+ }
37
+
38
+ &::-webkit-scrollbar-thumb:hover {
39
+ background-color: oklch(from var(--sheerly-foreground) l c h / 35%);
40
+ }
41
+
42
+ &::-webkit-scrollbar-corner {
43
+ background: transparent;
44
+ }
45
+
46
+ /* Orientation helpers set the matching overflow so the class is enough. */
47
+ &[data-orientation="vertical"] {
48
+ overflow-x: hidden;
49
+ overflow-y: auto;
50
+ }
51
+
52
+ &[data-orientation="horizontal"] {
53
+ overflow-x: auto;
54
+ overflow-y: hidden;
55
+ }
56
+ }
@@ -0,0 +1,28 @@
1
+ import type { ElementType } from 'react'
2
+ import { cn } from './utils/cn'
3
+ import type { PolymorphicProps } from './utils/polymorphic'
4
+
5
+ export type ScrollbarProps<T extends ElementType = 'div'> = PolymorphicProps<
6
+ T,
7
+ { 'data-orientation'?: 'vertical' | 'horizontal' }
8
+ >
9
+
10
+ /**
11
+ * A scroll container with a thin, theme-tinted scrollbar (matching the select
12
+ * picker). Set the element's own size + overflow inline, or use
13
+ * `data-orientation` to have the matching overflow applied for you.
14
+ *
15
+ * Polymorphic: pass `as` to change the rendered tag (defaults to `div`).
16
+ *
17
+ * @example
18
+ * <Scrollbar data-orientation="vertical" style={{ maxHeight: 200 }}>…</Scrollbar>
19
+ * <Scrollbar style={{ maxHeight: 200, overflowY: 'auto' }}>…</Scrollbar>
20
+ */
21
+ export function Scrollbar<T extends ElementType = 'div'>({
22
+ as,
23
+ className,
24
+ ...props
25
+ }: ScrollbarProps<T>) {
26
+ const Tag = as ?? 'div'
27
+ return <Tag className={cn('sheerly-scrollbar', className)} {...props} />
28
+ }
package/src/select.css CHANGED
@@ -12,6 +12,7 @@
12
12
  gap: 8px;
13
13
  border-radius: var(--sheerly-radius-lg);
14
14
  border: 1px solid var(--sheerly-input);
15
+ box-sizing: border-box;
15
16
  background-color: transparent;
16
17
  padding: 0 12px;
17
18
  font-family: "Geist Sans", system-ui, -apple-system, sans-serif;
@@ -97,6 +98,29 @@
97
98
  overflow-y: auto;
98
99
  opacity: 0;
99
100
  transition: all 0.2s allow-discrete;
101
+
102
+ /* Sheerly-styled scrollbar (replaces the native one) */
103
+ scrollbar-width: thin;
104
+ scrollbar-color: oklch(from var(--sheerly-foreground) l c h / 20%) transparent;
105
+
106
+ &::-webkit-scrollbar {
107
+ width: 8px;
108
+ }
109
+
110
+ &::-webkit-scrollbar-track {
111
+ background: transparent;
112
+ }
113
+
114
+ &::-webkit-scrollbar-thumb {
115
+ border: 2px solid transparent;
116
+ border-radius: var(--sheerly-radius-full);
117
+ background-clip: padding-box;
118
+ background-color: oklch(from var(--sheerly-foreground) l c h / 20%);
119
+ }
120
+
121
+ &::-webkit-scrollbar-thumb:hover {
122
+ background-color: oklch(from var(--sheerly-foreground) l c h / 35%);
123
+ }
100
124
  }
101
125
 
102
126
  .sheerly-select:open::picker(select) {
package/src/select.tsx CHANGED
@@ -1,5 +1,5 @@
1
1
  import type { SelectHTMLAttributes } from 'react'
2
- import { cn } from './cn'
2
+ import { cn } from './utils/cn'
3
3
 
4
4
  export interface SelectProps extends SelectHTMLAttributes<HTMLSelectElement> {
5
5
  'data-size'?: 'sm' | 'default' | 'lg'
@@ -9,6 +9,12 @@ export interface SelectProps extends SelectHTMLAttributes<HTMLSelectElement> {
9
9
  * Uses the customizable-select API. Requires
10
10
  * `<button><selectedcontent></selectedcontent></button>` as the first child,
11
11
  * which this wrapper injects before the consumer's <option>s.
12
+ *
13
+ * @example
14
+ * <Select defaultValue="apple">
15
+ * <option value="apple">Apple</option>
16
+ * <option value="banana">Banana</option>
17
+ * </Select>
12
18
  */
13
19
  export function Select({ className, children, ...props }: SelectProps) {
14
20
  return (
package/src/separator.tsx CHANGED
@@ -1,10 +1,26 @@
1
- import type { HTMLAttributes } from 'react'
2
- import { cn } from './cn'
1
+ import type { ElementType } from 'react'
2
+ import { cn } from './utils/cn'
3
+ import type { PolymorphicProps } from './utils/polymorphic'
3
4
 
4
- export interface SeparatorProps extends HTMLAttributes<HTMLDivElement> {
5
- 'data-orientation'?: 'horizontal' | 'vertical'
6
- }
5
+ export type SeparatorProps<T extends ElementType = 'div'> = PolymorphicProps<
6
+ T,
7
+ { 'data-orientation'?: 'horizontal' | 'vertical' }
8
+ >
7
9
 
8
- export function Separator({ className, ...props }: SeparatorProps) {
9
- return <div className={cn('sheerly-separator', className)} {...props} />
10
+ /**
11
+ * A divider line. Set `data-orientation="vertical"` for a vertical rule.
12
+ *
13
+ * Polymorphic: pass `as` to change the rendered tag (defaults to `div`, e.g.
14
+ * `as="hr"`).
15
+ *
16
+ * @example
17
+ * <Separator />
18
+ */
19
+ export function Separator<T extends ElementType = 'div'>({
20
+ as,
21
+ className,
22
+ ...props
23
+ }: SeparatorProps<T>) {
24
+ const Tag = as ?? 'div'
25
+ return <Tag className={cn('sheerly-separator', className)} {...props} />
10
26
  }
package/src/sheet.tsx CHANGED
@@ -1,5 +1,6 @@
1
- import type { ButtonHTMLAttributes, DialogHTMLAttributes, HTMLAttributes } from 'react'
2
- import { cn } from './cn'
1
+ import type { ButtonHTMLAttributes, DialogHTMLAttributes, ElementType } from 'react'
2
+ import { cn } from './utils/cn'
3
+ import type { PolymorphicProps } from './utils/polymorphic'
3
4
 
4
5
  export interface SheetProps extends DialogHTMLAttributes<HTMLDialogElement> {
5
6
  'data-side'?: 'right' | 'left' | 'top' | 'bottom'
@@ -9,32 +10,71 @@ export interface SheetProps extends DialogHTMLAttributes<HTMLDialogElement> {
9
10
  * Native <dialog> that slides in from an edge. Open via invoker command
10
11
  * (`command="show-modal"`) or `.showModal()`. Add `closedby="any"` for
11
12
  * light-dismiss.
13
+ *
14
+ * @example
15
+ * <button commandfor="s" command="show-modal">Open</button>
16
+ * <Sheet id="s" data-side="right" closedby="any">
17
+ * <SheetHeader><SheetTitle>Filters</SheetTitle></SheetHeader>
18
+ * <SheetBody>…</SheetBody>
19
+ * </Sheet>
12
20
  */
13
21
  export function Sheet({ className, ...props }: SheetProps) {
14
22
  return <dialog className={cn('sheerly-sheet', className)} {...props} />
15
23
  }
16
24
 
17
- export function SheetHeader({ className, ...props }: HTMLAttributes<HTMLDivElement>) {
18
- return <div className={cn('sheerly-sheet-header', className)} {...props} />
25
+ export type SheetHeaderProps<T extends ElementType = 'div'> = PolymorphicProps<T>
26
+
27
+ export function SheetHeader<T extends ElementType = 'div'>({
28
+ as,
29
+ className,
30
+ ...props
31
+ }: SheetHeaderProps<T>) {
32
+ const Tag = as ?? 'div'
33
+ return <Tag className={cn('sheerly-sheet-header', className)} {...props} />
19
34
  }
20
35
 
21
- export function SheetTitle({ className, ...props }: HTMLAttributes<HTMLHeadingElement>) {
22
- return <h3 className={cn('sheerly-sheet-title', className)} {...props} />
36
+ export type SheetTitleProps<T extends ElementType = 'h3'> = PolymorphicProps<T>
37
+
38
+ export function SheetTitle<T extends ElementType = 'h3'>({
39
+ as,
40
+ className,
41
+ ...props
42
+ }: SheetTitleProps<T>) {
43
+ const Tag = as ?? 'h3'
44
+ return <Tag className={cn('sheerly-sheet-title', className)} {...props} />
23
45
  }
24
46
 
25
- export function SheetDescription({
47
+ export type SheetDescriptionProps<T extends ElementType = 'p'> = PolymorphicProps<T>
48
+
49
+ export function SheetDescription<T extends ElementType = 'p'>({
50
+ as,
26
51
  className,
27
52
  ...props
28
- }: HTMLAttributes<HTMLParagraphElement>) {
29
- return <p className={cn('sheerly-sheet-description', className)} {...props} />
53
+ }: SheetDescriptionProps<T>) {
54
+ const Tag = as ?? 'p'
55
+ return <Tag className={cn('sheerly-sheet-description', className)} {...props} />
30
56
  }
31
57
 
32
- export function SheetBody({ className, ...props }: HTMLAttributes<HTMLDivElement>) {
33
- return <div className={cn('sheerly-sheet-body', className)} {...props} />
58
+ export type SheetBodyProps<T extends ElementType = 'div'> = PolymorphicProps<T>
59
+
60
+ export function SheetBody<T extends ElementType = 'div'>({
61
+ as,
62
+ className,
63
+ ...props
64
+ }: SheetBodyProps<T>) {
65
+ const Tag = as ?? 'div'
66
+ return <Tag className={cn('sheerly-sheet-body', className)} {...props} />
34
67
  }
35
68
 
36
- export function SheetFooter({ className, ...props }: HTMLAttributes<HTMLDivElement>) {
37
- return <div className={cn('sheerly-sheet-footer', className)} {...props} />
69
+ export type SheetFooterProps<T extends ElementType = 'div'> = PolymorphicProps<T>
70
+
71
+ export function SheetFooter<T extends ElementType = 'div'>({
72
+ as,
73
+ className,
74
+ ...props
75
+ }: SheetFooterProps<T>) {
76
+ const Tag = as ?? 'div'
77
+ return <Tag className={cn('sheerly-sheet-footer', className)} {...props} />
38
78
  }
39
79
 
40
80
  export function SheetClose({
package/src/skeleton.tsx CHANGED
@@ -1,8 +1,22 @@
1
- import type { HTMLAttributes } from 'react'
2
- import { cn } from './cn'
1
+ import type { ElementType } from 'react'
2
+ import { cn } from './utils/cn'
3
+ import type { PolymorphicProps } from './utils/polymorphic'
3
4
 
4
- export type SkeletonProps = HTMLAttributes<HTMLDivElement>
5
+ export type SkeletonProps<T extends ElementType = 'div'> = PolymorphicProps<T>
5
6
 
6
- export function Skeleton({ className, ...props }: SkeletonProps) {
7
- return <div className={cn('sheerly-skeleton', className)} {...props} />
7
+ /**
8
+ * A pulsing placeholder. Set width/height/border-radius inline.
9
+ *
10
+ * Polymorphic: pass `as` to change the rendered tag (defaults to `div`).
11
+ *
12
+ * @example
13
+ * <Skeleton style={{ width: 200, height: 20, borderRadius: 6 }} />
14
+ */
15
+ export function Skeleton<T extends ElementType = 'div'>({
16
+ as,
17
+ className,
18
+ ...props
19
+ }: SkeletonProps<T>) {
20
+ const Tag = as ?? 'div'
21
+ return <Tag className={cn('sheerly-skeleton', className)} {...props} />
8
22
  }
package/src/slider.tsx CHANGED
@@ -1,8 +1,14 @@
1
1
  import type { InputHTMLAttributes } from 'react'
2
- import { cn } from './cn'
2
+ import { cn } from './utils/cn'
3
3
 
4
4
  export type SliderProps = Omit<InputHTMLAttributes<HTMLInputElement>, 'type'>
5
5
 
6
+ /**
7
+ * A range `<input>`. Set `min` / `max` / `step` / `value`.
8
+ *
9
+ * @example
10
+ * <Slider min={0} max={100} defaultValue={50} />
11
+ */
6
12
  export function Slider({ className, ...props }: SliderProps) {
7
13
  return <input type="range" className={cn('sheerly-slider', className)} {...props} />
8
14
  }
package/src/spinner.tsx CHANGED
@@ -1,24 +1,37 @@
1
- import type { HTMLAttributes } from 'react'
2
- import { cn } from './cn'
1
+ import type { ElementType } from 'react'
2
+ import { cn } from './utils/cn'
3
+ import type { PolymorphicProps } from './utils/polymorphic'
3
4
 
4
- export interface SpinnerProps extends HTMLAttributes<HTMLSpanElement> {
5
- 'data-size'?: 'sm' | 'default' | 'md' | 'lg'
6
- }
5
+ export type SpinnerProps<T extends ElementType = 'span'> = PolymorphicProps<
6
+ T,
7
+ { 'data-size'?: 'sm' | 'default' | 'md' | 'lg' }
8
+ >
7
9
 
8
10
  /**
9
11
  * Loading spinner. The glyph is embedded in CSS (masked ::before), so no SVG
10
12
  * child is needed — this renders a bare <span>. It inherits the current text
11
13
  * color. Pass an <svg> child if you prefer to ship your own icon.
14
+ *
15
+ * Polymorphic: pass `as` to change the rendered tag (defaults to `span`).
16
+ *
17
+ * @example
18
+ * <Spinner data-size="lg" />
12
19
  */
13
- export function Spinner({ className, children, ...props }: SpinnerProps) {
20
+ export function Spinner<T extends ElementType = 'span'>({
21
+ as,
22
+ className,
23
+ children,
24
+ ...props
25
+ }: SpinnerProps<T>) {
26
+ const Tag = as ?? 'span'
14
27
  return (
15
- <span
28
+ <Tag
16
29
  className={cn('sheerly-spinner', className)}
17
30
  role="status"
18
31
  aria-label="Loading"
19
32
  {...props}
20
33
  >
21
34
  {children}
22
- </span>
35
+ </Tag>
23
36
  )
24
37
  }