sheerly 0.0.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.
Files changed (140) hide show
  1. package/README.md +3 -0
  2. package/lib/accordion.css +1 -0
  3. package/lib/accordion.d.ts +6 -0
  4. package/lib/alert.css +1 -0
  5. package/lib/alert.d.ts +8 -0
  6. package/lib/avatar.css +1 -0
  7. package/lib/avatar.d.ts +13 -0
  8. package/lib/badge.css +1 -0
  9. package/lib/badge.d.ts +5 -0
  10. package/lib/breadcrumb.css +1 -0
  11. package/lib/breadcrumb.d.ts +8 -0
  12. package/lib/button.css +1 -0
  13. package/lib/button.d.ts +7 -0
  14. package/lib/card.css +1 -0
  15. package/lib/card.d.ts +14 -0
  16. package/lib/carousel.css +1 -0
  17. package/lib/carousel.d.ts +21 -0
  18. package/lib/checkbox.css +1 -0
  19. package/lib/checkbox.d.ts +3 -0
  20. package/lib/cn.d.ts +5 -0
  21. package/lib/dialog.css +1 -0
  22. package/lib/dialog.d.ts +15 -0
  23. package/lib/dropdown-menu.css +1 -0
  24. package/lib/dropdown-menu.d.ts +49 -0
  25. package/lib/hover-card.css +1 -0
  26. package/lib/hover-card.d.ts +7 -0
  27. package/lib/index.css +1 -0
  28. package/lib/index.d.ts +33 -0
  29. package/lib/index.js +652 -0
  30. package/lib/input.css +1 -0
  31. package/lib/input.d.ts +5 -0
  32. package/lib/kbd.css +1 -0
  33. package/lib/kbd.d.ts +5 -0
  34. package/lib/label.css +1 -0
  35. package/lib/label.d.ts +5 -0
  36. package/lib/pagination.css +1 -0
  37. package/lib/pagination.d.ts +17 -0
  38. package/lib/popover.css +1 -0
  39. package/lib/popover.d.ts +7 -0
  40. package/lib/progress.css +1 -0
  41. package/lib/progress.d.ts +6 -0
  42. package/lib/radio.css +1 -0
  43. package/lib/radio.d.ts +5 -0
  44. package/lib/root.css +1 -0
  45. package/lib/select.css +1 -0
  46. package/lib/select.d.ts +10 -0
  47. package/lib/separator.css +1 -0
  48. package/lib/separator.d.ts +5 -0
  49. package/lib/sheet.css +1 -0
  50. package/lib/sheet.d.ts +16 -0
  51. package/lib/skeleton.css +1 -0
  52. package/lib/skeleton.d.ts +3 -0
  53. package/lib/slider.css +1 -0
  54. package/lib/slider.d.ts +3 -0
  55. package/lib/spinner.css +1 -0
  56. package/lib/spinner.d.ts +10 -0
  57. package/lib/switch.css +1 -0
  58. package/lib/switch.d.ts +5 -0
  59. package/lib/table.css +1 -0
  60. package/lib/table.d.ts +5 -0
  61. package/lib/tabs.css +1 -0
  62. package/lib/tabs.d.ts +13 -0
  63. package/lib/textarea.css +1 -0
  64. package/lib/textarea.d.ts +3 -0
  65. package/lib/toggle.css +1 -0
  66. package/lib/toggle.d.ts +16 -0
  67. package/lib/tooltip.css +1 -0
  68. package/lib/tooltip.d.ts +7 -0
  69. package/lib/typography.css +1 -0
  70. package/lib/typography.d.ts +9 -0
  71. package/package.json +33 -0
  72. package/src/accordion.css +108 -0
  73. package/src/accordion.tsx +15 -0
  74. package/src/alert.css +126 -0
  75. package/src/alert.tsx +25 -0
  76. package/src/avatar.css +75 -0
  77. package/src/avatar.tsx +30 -0
  78. package/src/badge.css +71 -0
  79. package/src/badge.tsx +19 -0
  80. package/src/breadcrumb.css +166 -0
  81. package/src/breadcrumb.tsx +71 -0
  82. package/src/button.css +214 -0
  83. package/src/button.tsx +30 -0
  84. package/src/card.css +99 -0
  85. package/src/card.tsx +41 -0
  86. package/src/carousel.css +183 -0
  87. package/src/carousel.tsx +30 -0
  88. package/src/checkbox.css +56 -0
  89. package/src/checkbox.tsx +10 -0
  90. package/src/cn.ts +7 -0
  91. package/src/dialog.css +175 -0
  92. package/src/dialog.tsx +47 -0
  93. package/src/dropdown-menu.css +257 -0
  94. package/src/dropdown-menu.tsx +88 -0
  95. package/src/hover-card.css +75 -0
  96. package/src/hover-card.tsx +16 -0
  97. package/src/index.css +33 -0
  98. package/src/index.ts +37 -0
  99. package/src/input.css +62 -0
  100. package/src/input.tsx +10 -0
  101. package/src/kbd.css +32 -0
  102. package/src/kbd.tsx +14 -0
  103. package/src/label.css +17 -0
  104. package/src/label.tsx +10 -0
  105. package/src/pagination.css +150 -0
  106. package/src/pagination.tsx +95 -0
  107. package/src/popover.css +65 -0
  108. package/src/popover.tsx +16 -0
  109. package/src/progress.css +79 -0
  110. package/src/progress.tsx +19 -0
  111. package/src/radio.css +61 -0
  112. package/src/radio.tsx +14 -0
  113. package/src/root.css +78 -0
  114. package/src/select.css +164 -0
  115. package/src/select.tsx +22 -0
  116. package/src/separator.css +14 -0
  117. package/src/separator.tsx +10 -0
  118. package/src/sheet.css +225 -0
  119. package/src/sheet.tsx +48 -0
  120. package/src/skeleton.css +15 -0
  121. package/src/skeleton.tsx +8 -0
  122. package/src/slider.css +68 -0
  123. package/src/slider.tsx +8 -0
  124. package/src/spinner.css +43 -0
  125. package/src/spinner.tsx +24 -0
  126. package/src/switch.css +79 -0
  127. package/src/switch.tsx +18 -0
  128. package/src/table.css +77 -0
  129. package/src/table.tsx +14 -0
  130. package/src/tabs.css +110 -0
  131. package/src/tabs.tsx +38 -0
  132. package/src/textarea.css +38 -0
  133. package/src/textarea.tsx +8 -0
  134. package/src/toggle.css +148 -0
  135. package/src/toggle.tsx +47 -0
  136. package/src/tooltip.css +60 -0
  137. package/src/tooltip.tsx +16 -0
  138. package/src/types.d.ts +42 -0
  139. package/src/typography.css +176 -0
  140. package/src/typography.tsx +13 -0
package/src/switch.css ADDED
@@ -0,0 +1,79 @@
1
+ /* Switch — <input type="checkbox" role="switch"> */
2
+ .sheerly-switch {
3
+ display: inline-flex;
4
+ flex-shrink: 0;
5
+ align-items: center;
6
+ border-radius: var(--sheerly-radius-full);
7
+ border: none;
8
+ transition: background-color 0.15s ease;
9
+ outline: none;
10
+ cursor: pointer;
11
+ appearance: none;
12
+ position: relative;
13
+
14
+ /* Default size */
15
+ width: 32px;
16
+ height: 18px;
17
+ background-color: var(--sheerly-input);
18
+
19
+ &::before {
20
+ content: "";
21
+ display: block;
22
+ border-radius: var(--sheerly-radius-full);
23
+ background-color: var(--sheerly-background);
24
+ transition: transform 0.15s ease;
25
+ pointer-events: none;
26
+ position: absolute;
27
+ left: 2px;
28
+ top: 50%;
29
+ transform: translateY(-50%);
30
+
31
+ /* Default size thumb */
32
+ width: 14px;
33
+ height: 14px;
34
+ }
35
+
36
+ &:hover {
37
+ background-color: var(--sheerly-ring);
38
+ }
39
+
40
+ &:checked,
41
+ &[data-state="checked"] {
42
+ background-color: var(--sheerly-foreground);
43
+
44
+ &::before {
45
+ transform: translateX(14px) translateY(-50%);
46
+ background-color: var(--sheerly-background);
47
+ }
48
+ }
49
+
50
+ &:focus-visible {
51
+ box-shadow: var(--sheerly-focus-ring);
52
+ }
53
+
54
+ &:disabled {
55
+ background-color: var(--sheerly-muted);
56
+ cursor: not-allowed;
57
+
58
+ &::before {
59
+ background-color: var(--sheerly-input);
60
+ }
61
+ }
62
+
63
+ &[data-size="sm"] {
64
+ width: 24px;
65
+ height: 14px;
66
+
67
+ &::before {
68
+ width: 10px;
69
+ height: 10px;
70
+ }
71
+
72
+ &:checked,
73
+ &[data-state="checked"] {
74
+ &::before {
75
+ transform: translateX(10px) translateY(-50%);
76
+ }
77
+ }
78
+ }
79
+ }
package/src/switch.tsx ADDED
@@ -0,0 +1,18 @@
1
+ import type { InputHTMLAttributes } from 'react'
2
+ import { cn } from './cn'
3
+
4
+ export interface SwitchProps
5
+ extends Omit<InputHTMLAttributes<HTMLInputElement>, 'type' | 'role'> {
6
+ 'data-size'?: 'default' | 'sm'
7
+ }
8
+
9
+ export function Switch({ className, ...props }: SwitchProps) {
10
+ return (
11
+ <input
12
+ type="checkbox"
13
+ role="switch"
14
+ className={cn('sheerly-switch', className)}
15
+ {...props}
16
+ />
17
+ )
18
+ }
package/src/table.css ADDED
@@ -0,0 +1,77 @@
1
+ /* Wrapper enabling horizontal scroll on overflow */
2
+ .sheerly-table-container {
3
+ position: relative;
4
+ width: 100%;
5
+ overflow-x: auto;
6
+ }
7
+
8
+ /* Table — semantic <table>, styled via element selectors */
9
+ .sheerly-table {
10
+ width: 100%;
11
+ caption-side: bottom;
12
+ border-collapse: collapse;
13
+ font-family: "Geist Sans", system-ui, -apple-system, sans-serif;
14
+ font-size: 14px;
15
+ line-height: 20px;
16
+ color: var(--sheerly-foreground);
17
+ }
18
+
19
+ .sheerly-table caption {
20
+ margin-top: 16px;
21
+ font-size: 14px;
22
+ line-height: 20px;
23
+ color: var(--sheerly-muted-foreground);
24
+ }
25
+
26
+ .sheerly-table thead tr {
27
+ border-bottom: 1px solid var(--sheerly-border);
28
+ }
29
+
30
+ .sheerly-table th {
31
+ height: 40px;
32
+ padding: 0 8px;
33
+ text-align: left;
34
+ vertical-align: middle;
35
+ font-weight: 500;
36
+ white-space: nowrap;
37
+ color: var(--sheerly-foreground);
38
+ }
39
+
40
+ .sheerly-table tbody tr {
41
+ border-bottom: 1px solid var(--sheerly-border);
42
+ transition: background-color 0.1s ease;
43
+
44
+ &:last-child {
45
+ border-bottom: none;
46
+ }
47
+
48
+ &:hover {
49
+ background-color: var(--sheerly-accent);
50
+ }
51
+
52
+ &[data-state="selected"] {
53
+ background-color: var(--sheerly-muted);
54
+ }
55
+ }
56
+
57
+ .sheerly-table td {
58
+ padding: 8px;
59
+ vertical-align: middle;
60
+ white-space: nowrap;
61
+ }
62
+
63
+ .sheerly-table tfoot {
64
+ border-top: 1px solid var(--sheerly-border);
65
+ background-color: var(--sheerly-accent);
66
+ font-weight: 500;
67
+
68
+ & tr:last-child {
69
+ border-bottom: none;
70
+ }
71
+ }
72
+
73
+ .sheerly-table tfoot td,
74
+ .sheerly-table tfoot th {
75
+ padding: 8px;
76
+ vertical-align: middle;
77
+ }
package/src/table.tsx ADDED
@@ -0,0 +1,14 @@
1
+ import type { HTMLAttributes, TableHTMLAttributes } from 'react'
2
+ import { cn } from './cn'
3
+
4
+ export type TableContainerProps = HTMLAttributes<HTMLDivElement>
5
+
6
+ export function TableContainer({ className, ...props }: TableContainerProps) {
7
+ return <div className={cn('sheerly-table-container', className)} {...props} />
8
+ }
9
+
10
+ export type TableProps = TableHTMLAttributes<HTMLTableElement>
11
+
12
+ export function Table({ className, ...props }: TableProps) {
13
+ return <table className={cn('sheerly-table', className)} {...props} />
14
+ }
package/src/tabs.css ADDED
@@ -0,0 +1,110 @@
1
+ /* Tabs — <div> container; list + triggers + content panels */
2
+ .sheerly-tabs {
3
+ display: flex;
4
+ flex-direction: column;
5
+ gap: 8px;
6
+ }
7
+
8
+ /* The trigger bar */
9
+ .sheerly-tabs-list {
10
+ display: inline-flex;
11
+ width: fit-content;
12
+ align-items: center;
13
+ justify-content: center;
14
+ border-radius: 8px;
15
+ padding: 3px;
16
+ background-color: var(--sheerly-muted);
17
+ height: 36px;
18
+
19
+ &[data-variant="line"] {
20
+ background-color: transparent;
21
+ border-radius: 0;
22
+ gap: 4px;
23
+ padding: 0;
24
+ border-bottom: 1px solid var(--sheerly-border);
25
+ height: auto;
26
+ padding-bottom: 0;
27
+ }
28
+ }
29
+
30
+ .sheerly-tabs-trigger {
31
+ position: relative;
32
+ display: inline-flex;
33
+ flex: 1;
34
+ align-items: center;
35
+ justify-content: center;
36
+ gap: 6px;
37
+ border-radius: 6px;
38
+ border: 1px solid transparent;
39
+ padding: 4px 8px;
40
+ font-family: "Geist Sans", system-ui, -apple-system, sans-serif;
41
+ font-size: 14px;
42
+ font-weight: 500;
43
+ line-height: 20px;
44
+ white-space: nowrap;
45
+ color: var(--sheerly-muted-foreground);
46
+ background-color: transparent;
47
+ cursor: pointer;
48
+ outline: none;
49
+ transition: color 0.15s ease, background-color 0.15s ease;
50
+
51
+ &:hover {
52
+ color: var(--sheerly-foreground);
53
+ }
54
+
55
+ &:focus-visible {
56
+ box-shadow: var(--sheerly-focus-ring);
57
+ }
58
+
59
+ &:disabled {
60
+ pointer-events: none;
61
+ opacity: 0.5;
62
+ }
63
+
64
+ /* Active state */
65
+ &[aria-selected="true"],
66
+ &[data-state="active"] {
67
+ background-color: var(--sheerly-background);
68
+ color: var(--sheerly-foreground);
69
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
70
+ }
71
+
72
+ /* Line variant trigger */
73
+ .sheerly-tabs-list[data-variant="line"] & {
74
+ border-radius: 0;
75
+ padding: 8px 12px;
76
+ box-shadow: none;
77
+
78
+ &[aria-selected="true"],
79
+ &[data-state="active"] {
80
+ background-color: transparent;
81
+ box-shadow: none;
82
+
83
+ &::after {
84
+ content: "";
85
+ position: absolute;
86
+ bottom: -1px;
87
+ left: 0;
88
+ right: 0;
89
+ height: 2px;
90
+ background-color: var(--sheerly-foreground);
91
+ }
92
+ }
93
+ }
94
+
95
+ & svg {
96
+ pointer-events: none;
97
+ flex-shrink: 0;
98
+ width: 16px;
99
+ height: 16px;
100
+ }
101
+ }
102
+
103
+ .sheerly-tabs-content {
104
+ flex: 1;
105
+ outline: none;
106
+
107
+ &[hidden] {
108
+ display: none;
109
+ }
110
+ }
package/src/tabs.tsx ADDED
@@ -0,0 +1,38 @@
1
+ import type { ButtonHTMLAttributes, HTMLAttributes } from 'react'
2
+ import { cn } from './cn'
3
+
4
+ export type TabsProps = HTMLAttributes<HTMLDivElement>
5
+
6
+ export function Tabs({ className, ...props }: TabsProps) {
7
+ return <div className={cn('sheerly-tabs', className)} {...props} />
8
+ }
9
+
10
+ export interface TabsListProps extends HTMLAttributes<HTMLDivElement> {
11
+ 'data-variant'?: 'line'
12
+ }
13
+
14
+ export function TabsList({ className, role = 'tablist', ...props }: TabsListProps) {
15
+ return <div role={role} className={cn('sheerly-tabs-list', className)} {...props} />
16
+ }
17
+
18
+ export interface TabsTriggerProps extends ButtonHTMLAttributes<HTMLButtonElement> {
19
+ 'data-state'?: 'active'
20
+ }
21
+
22
+ export function TabsTrigger({ className, role = 'tab', ...props }: TabsTriggerProps) {
23
+ return (
24
+ <button role={role} className={cn('sheerly-tabs-trigger', className)} {...props} />
25
+ )
26
+ }
27
+
28
+ export type TabsContentProps = HTMLAttributes<HTMLDivElement>
29
+
30
+ export function TabsContent({
31
+ className,
32
+ role = 'tabpanel',
33
+ ...props
34
+ }: TabsContentProps) {
35
+ return (
36
+ <div role={role} className={cn('sheerly-tabs-content', className)} {...props} />
37
+ )
38
+ }
@@ -0,0 +1,38 @@
1
+ /* Textarea — <textarea> that auto-grows via field-sizing: content */
2
+ .sheerly-textarea {
3
+ display: block;
4
+ width: 100%;
5
+ min-height: 64px;
6
+ border-radius: var(--sheerly-radius-lg);
7
+ border: 1px solid var(--sheerly-input);
8
+ background-color: transparent;
9
+ padding: 6px 10px;
10
+ font-family: "Geist Sans", system-ui, -apple-system, sans-serif;
11
+ font-size: 14px;
12
+ font-weight: 400;
13
+ line-height: 20px;
14
+ color: var(--sheerly-foreground);
15
+ transition: border-color 0.15s ease, box-shadow 0.15s ease;
16
+ outline: none;
17
+ resize: vertical;
18
+ field-sizing: content;
19
+
20
+ &::placeholder {
21
+ color: var(--sheerly-muted-foreground);
22
+ }
23
+
24
+ &:focus-visible {
25
+ border-color: var(--sheerly-ring);
26
+ box-shadow: var(--sheerly-focus-ring);
27
+ }
28
+
29
+ &:disabled {
30
+ cursor: not-allowed;
31
+ opacity: 0.5;
32
+ }
33
+
34
+ &[aria-invalid="true"] {
35
+ border-color: var(--sheerly-destructive);
36
+ box-shadow: 0 0 0 3px oklch(from var(--sheerly-destructive) l c h / 20%);
37
+ }
38
+ }
@@ -0,0 +1,8 @@
1
+ import type { TextareaHTMLAttributes } from 'react'
2
+ import { cn } from './cn'
3
+
4
+ export type TextareaProps = TextareaHTMLAttributes<HTMLTextAreaElement>
5
+
6
+ export function Textarea({ className, ...props }: TextareaProps) {
7
+ return <textarea className={cn('sheerly-textarea', className)} {...props} />
8
+ }
package/src/toggle.css ADDED
@@ -0,0 +1,148 @@
1
+ /*
2
+ * Toggle — a two-state (on / off) button. Mirrors shadcn base-nova.
3
+ *
4
+ * State is driven by a nested checkbox, so no JS is needed:
5
+ * <label class="sheerly-toggle">
6
+ * <input type="checkbox" />
7
+ * <svg data-icon="inline-start">…</svg>
8
+ * Italic
9
+ * </label>
10
+ *
11
+ * The checkbox is visually hidden; the label reflects `:checked` via
12
+ * `:has(:checked)`. A plain <button> with aria-pressed="true" /
13
+ * data-state="on" is still supported for command-driven / controlled use.
14
+ */
15
+ .sheerly-toggle {
16
+ display: inline-flex;
17
+ align-items: center;
18
+ justify-content: center;
19
+ gap: 4px;
20
+ border-radius: var(--sheerly-radius-lg);
21
+ font-family: "Geist Sans", system-ui, -apple-system, sans-serif;
22
+ font-size: 14px;
23
+ font-weight: 500;
24
+ line-height: 20px;
25
+ white-space: nowrap;
26
+ background-color: transparent;
27
+ color: var(--sheerly-foreground);
28
+ border: 1px solid transparent;
29
+ cursor: pointer;
30
+ outline: none;
31
+ transition: all 0.15s ease;
32
+ user-select: none;
33
+
34
+ /* Default size (32px) */
35
+ height: 32px;
36
+ min-width: 32px;
37
+ padding: 0 10px;
38
+
39
+ /* Visually hide the state-bearing checkbox; keep it focusable/accessible. */
40
+ & > input[type="checkbox"] {
41
+ position: absolute;
42
+ width: 1px;
43
+ height: 1px;
44
+ padding: 0;
45
+ margin: -1px;
46
+ overflow: hidden;
47
+ clip: rect(0, 0, 0, 0);
48
+ white-space: nowrap;
49
+ border: 0;
50
+ }
51
+
52
+ &:hover {
53
+ background-color: var(--sheerly-muted);
54
+ color: var(--sheerly-foreground);
55
+ }
56
+
57
+ /* Keyboard focus — on the label (via the nested input) or a plain button. */
58
+ &:has(> input:focus-visible),
59
+ &:focus-visible {
60
+ border-color: var(--sheerly-ring);
61
+ box-shadow: var(--sheerly-focus-ring);
62
+ }
63
+
64
+ /* Disabled — either a disabled checkbox inside or a disabled button. */
65
+ &:has(> input:disabled),
66
+ &:disabled {
67
+ pointer-events: none;
68
+ opacity: 0.5;
69
+ }
70
+
71
+ &[aria-invalid="true"] {
72
+ border-color: var(--sheerly-destructive);
73
+ box-shadow: 0 0 0 3px oklch(from var(--sheerly-destructive) l c h / 20%);
74
+ }
75
+
76
+ /* Active / pressed state */
77
+ &:has(> input:checked),
78
+ &[aria-pressed="true"],
79
+ &[data-state="on"] {
80
+ background-color: var(--sheerly-muted);
81
+ }
82
+
83
+ /* ========== Variants ========== */
84
+
85
+ &[data-variant="default"] {
86
+ background-color: transparent;
87
+ }
88
+
89
+ &[data-variant="outline"] {
90
+ border-color: var(--sheerly-input);
91
+ background-color: transparent;
92
+
93
+ &:hover {
94
+ background-color: var(--sheerly-muted);
95
+ }
96
+ }
97
+
98
+ /* ========== Sizes ========== */
99
+
100
+ &[data-size="default"] {
101
+ height: 32px;
102
+ min-width: 32px;
103
+ padding: 0 10px;
104
+ }
105
+
106
+ &[data-size="sm"] {
107
+ height: 28px;
108
+ min-width: 28px;
109
+ padding: 0 10px;
110
+ font-size: 12.8px;
111
+ border-radius: min(var(--sheerly-radius-md), 12px);
112
+
113
+ & svg {
114
+ width: 14px;
115
+ height: 14px;
116
+ }
117
+ }
118
+
119
+ &[data-size="lg"] {
120
+ height: 36px;
121
+ min-width: 36px;
122
+ padding: 0 10px;
123
+ }
124
+
125
+ /* Icon spacing (matches shadcn has-data-[icon=…] paddings) */
126
+ &:has([data-icon="inline-start"]) {
127
+ padding-inline-start: 8px;
128
+ }
129
+
130
+ &:has([data-icon="inline-end"]) {
131
+ padding-inline-end: 8px;
132
+ }
133
+
134
+ &[data-size="sm"]:has([data-icon="inline-start"]) {
135
+ padding-inline-start: 6px;
136
+ }
137
+
138
+ &[data-size="sm"]:has([data-icon="inline-end"]) {
139
+ padding-inline-end: 6px;
140
+ }
141
+
142
+ & svg {
143
+ pointer-events: none;
144
+ flex-shrink: 0;
145
+ width: 16px;
146
+ height: 16px;
147
+ }
148
+ }
package/src/toggle.tsx ADDED
@@ -0,0 +1,47 @@
1
+ import type { ChangeEventHandler, LabelHTMLAttributes, ReactNode } from 'react'
2
+ import { cn } from './cn'
3
+
4
+ export interface ToggleProps
5
+ extends Omit<LabelHTMLAttributes<HTMLLabelElement>, 'onChange'> {
6
+ 'data-variant'?: 'default' | 'outline'
7
+ 'data-size'?: 'sm' | 'default' | 'lg'
8
+ /** Controlled pressed state (drives the nested checkbox). */
9
+ pressed?: boolean
10
+ /** Uncontrolled initial pressed state. */
11
+ defaultPressed?: boolean
12
+ /** Fires with the next pressed state when toggled. */
13
+ onPressedChange?: (pressed: boolean) => void
14
+ disabled?: boolean
15
+ name?: string
16
+ value?: string
17
+ children?: ReactNode
18
+ }
19
+
20
+ export function Toggle({
21
+ className,
22
+ pressed,
23
+ defaultPressed,
24
+ onPressedChange,
25
+ disabled,
26
+ name,
27
+ value,
28
+ children,
29
+ ...props
30
+ }: ToggleProps) {
31
+ const onChange: ChangeEventHandler<HTMLInputElement> = (e) =>
32
+ onPressedChange?.(e.target.checked)
33
+ return (
34
+ <label className={cn('sheerly-toggle', className)} {...props}>
35
+ <input
36
+ type="checkbox"
37
+ checked={pressed}
38
+ defaultChecked={defaultPressed}
39
+ onChange={onChange}
40
+ disabled={disabled}
41
+ name={name}
42
+ value={value}
43
+ />
44
+ {children}
45
+ </label>
46
+ )
47
+ }
@@ -0,0 +1,60 @@
1
+ /*
2
+ * Tooltip — uses popover="hint" + anchor positioning + interestfor
3
+ *
4
+ * Usage:
5
+ * <span class="sheerly-tooltip">
6
+ * <button interestfor="my-tip">Hover me</button>
7
+ * <span class="sheerly-tooltip-content" id="my-tip" popover="hint">Tooltip text</span>
8
+ * </span>
9
+ *
10
+ * Position via --sheerly-tooltip-position: block-start | block-end | inline-start | inline-end
11
+ */
12
+
13
+ .sheerly-tooltip {
14
+ anchor-name: --sheerly-tip;
15
+ anchor-scope: --sheerly-tip;
16
+ display: inline-block;
17
+ width: fit-content;
18
+ }
19
+
20
+ .sheerly-tooltip-content {
21
+ position: fixed;
22
+ position-anchor: --sheerly-tip;
23
+ position-area: var(--sheerly-tooltip-position, block-start);
24
+ position-try-fallbacks: flip-block, flip-inline, flip-block flip-inline;
25
+ position-visibility: anchors-visible;
26
+ inset: unset;
27
+ margin: 6px;
28
+
29
+ /* Visual style */
30
+ background-color: var(--sheerly-foreground);
31
+ color: var(--sheerly-background);
32
+ font-family: "Geist Sans", system-ui, -apple-system, sans-serif;
33
+ font-size: 12px;
34
+ font-weight: 400;
35
+ line-height: 16px;
36
+ text-align: center;
37
+ white-space: normal;
38
+ text-wrap: pretty;
39
+ max-width: 200px;
40
+ padding: 4px 8px;
41
+ border-radius: var(--sheerly-radius-sm);
42
+ border: none;
43
+ overflow: visible;
44
+ pointer-events: none;
45
+
46
+ /* Popover animation */
47
+ opacity: 0;
48
+ transition:
49
+ opacity 0.15s ease,
50
+ display 0.15s allow-discrete,
51
+ overlay 0.15s allow-discrete;
52
+
53
+ &:popover-open {
54
+ opacity: 1;
55
+
56
+ @starting-style {
57
+ opacity: 0;
58
+ }
59
+ }
60
+ }
@@ -0,0 +1,16 @@
1
+ import type { HTMLAttributes } from 'react'
2
+ import { cn } from './cn'
3
+
4
+ export type TooltipProps = HTMLAttributes<HTMLSpanElement>
5
+
6
+ /** Wrapper carrying the anchor. Nest the trigger + content inside. */
7
+ export function Tooltip({ className, ...props }: TooltipProps) {
8
+ return <span className={cn('sheerly-tooltip', className)} {...props} />
9
+ }
10
+
11
+ export type TooltipContentProps = HTMLAttributes<HTMLSpanElement>
12
+
13
+ /** The floating label. Give it `popover="hint"` and an `id` matched by the trigger's `interestfor`. */
14
+ export function TooltipContent({ className, ...props }: TooltipContentProps) {
15
+ return <span className={cn('sheerly-tooltip-content', className)} {...props} />
16
+ }
package/src/types.d.ts ADDED
@@ -0,0 +1,42 @@
1
+ /// <reference types="vite/client" />
2
+
3
+ import 'react'
4
+
5
+ // Sheerly leans on cutting-edge HTML that React's types don't cover yet:
6
+ // - <selectedcontent> (customizable <select> API)
7
+ // - interestfor (invoker for tooltips/popovers)
8
+ // - popover="hint" (React types allow the popover attr but not "hint")
9
+ // - command/commandfor (Invoker Commands API) on buttons
10
+ // - <dialog closedby> (light-dismiss)
11
+ // - focusgroup (arrow-key roving focus)
12
+ // This single declaration file is shared by both the library wrappers (src/)
13
+ // and the docs explorer (docs/) — both are compiled by tsconfig.app.json.
14
+ declare module 'react' {
15
+ interface HTMLAttributes<T> {
16
+ interestfor?: string
17
+ popover?: '' | 'auto' | 'manual' | 'hint'
18
+ // Focusgroup API (arrow-key roving focus) — not yet in React's types.
19
+ focusgroup?: string
20
+ }
21
+ // Invoker Commands API (command/commandfor) used by dialog, sheet, popover.
22
+ interface ButtonHTMLAttributes<T> {
23
+ command?: string
24
+ commandfor?: string
25
+ }
26
+ // <dialog closedby> (light-dismiss) — not yet in React's types.
27
+ interface DialogHTMLAttributes<T> {
28
+ closedby?: 'any' | 'closerequest' | 'none'
29
+ }
30
+ }
31
+
32
+ declare module 'react/jsx-runtime' {
33
+ namespace JSX {
34
+ interface IntrinsicElements {
35
+ // <selectedcontent> from the customizable <select> API.
36
+ selectedcontent: React.DetailedHTMLProps<
37
+ React.HTMLAttributes<HTMLElement>,
38
+ HTMLElement
39
+ >
40
+ }
41
+ }
42
+ }