minka-ds 0.1.4 → 0.1.7

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "minka-ds",
3
- "version": "0.1.4",
3
+ "version": "0.1.7",
4
4
  "description": "Minka product design system — tokenized component library",
5
5
  "license": "MIT",
6
6
  "files": [
@@ -23,6 +23,7 @@
23
23
  "lucide-react": "^1.14.0",
24
24
  "radix-ui": "^1.4.3",
25
25
  "react-day-picker": "^10.0.0",
26
+ "sonner": "^2.0.7",
26
27
  "tailwind-merge": "^3.5.0",
27
28
  "tw-animate-css": "^1.4.0"
28
29
  }
@@ -10,12 +10,13 @@ import { cn } from "../../lib/utils"
10
10
  interface TextStackProps {
11
11
  primary: React.ReactNode
12
12
  secondary?: React.ReactNode
13
+ inline?: boolean
13
14
  className?: string
14
15
  }
15
16
 
16
- function TextStack({ primary, secondary, className }: TextStackProps) {
17
+ function TextStack({ primary, secondary, inline, className }: TextStackProps) {
17
18
  return (
18
- <div className={cn("flex flex-col gap-0.5", className)}>
19
+ <div className={cn(inline ? "flex flex-row items-baseline gap-1.5" : "flex flex-col gap-0.5", className)}>
19
20
  <span className="text-caption-light text-[var(--color-text-default)]">{primary}</span>
20
21
  {secondary && (
21
22
  <span className="text-caption-sm-light text-[var(--color-text-muted)]">{secondary}</span>
@@ -108,16 +108,19 @@ interface DataTableProps<TData, TValue> {
108
108
  data: TData[]
109
109
  batchSize?: number
110
110
  onRowClick?: (row: TData) => void
111
+ variant?: "default" | "compact"
111
112
  className?: string
112
113
  }
113
114
 
114
115
  function DataTable<TData, TValue>({
115
116
  columns,
116
117
  data,
117
- batchSize = 20,
118
+ batchSize = 40,
118
119
  onRowClick,
120
+ variant = "default",
119
121
  className,
120
122
  }: DataTableProps<TData, TValue>) {
123
+ const compact = variant === "compact"
121
124
  const [sorting, setSorting] = React.useState<SortingState>([])
122
125
  const [columnVisibility, setColumnVisibility] = React.useState<VisibilityState>({})
123
126
  const [displayCount, setDisplayCount] = React.useState(batchSize)
@@ -154,15 +157,21 @@ function DataTable<TData, TValue>({
154
157
  className="flex-1 min-h-0 overflow-auto rounded-[var(--radius-card)] border border-[var(--color-border-default)] bg-[var(--color-bg-raised)] [&_[data-slot=table-container]]:overflow-visible"
155
158
  >
156
159
 
157
- <Table className="[&_th:first-child]:pl-4 [&_td:first-child]:pl-4">
158
- <TableHeader className="sticky top-0 [z-index:var(--z-sticky)] bg-[var(--color-bg-base)]">
160
+ <Table className={cn(
161
+ "[&_th:first-child]:pl-4 [&_td:first-child]:pl-4",
162
+ compact && "[&_th]:h-7 [&_th]:text-caption [&_th]:text-[var(--color-text-default)] [&_td]:h-11 [&_td]:py-1.5 [&_td]:text-body-sm"
163
+ )}>
164
+ <TableHeader className={cn(
165
+ "sticky top-0 [z-index:var(--z-sticky)]",
166
+ "bg-[var(--color-bg-base)]"
167
+ )}>
159
168
  {table.getHeaderGroups().map((headerGroup) => (
160
169
  <TableRow key={headerGroup.id}>
161
170
  {headerGroup.headers.map((header, index) => (
162
171
  <TableHead key={header.id}>
163
172
  {index === headerGroup.headers.length - 1 ? (
164
173
  <div className="flex items-center justify-between gap-2">
165
- {header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext())}
174
+ <span>{header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext())}</span>
166
175
  <DataTableColumnToggle table={table} />
167
176
  </div>
168
177
  ) : (
@@ -42,7 +42,7 @@ function DropdownMenuContent({
42
42
  data-slot="dropdown-menu-content"
43
43
  sideOffset={sideOffset}
44
44
  className={cn(
45
- "[z-index:var(--z-dropdown)] max-h-(--radix-dropdown-menu-content-available-height) min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto [border-radius:var(--radius-popover)] border border-[var(--color-border-default)] bg-[var(--color-bg-overlay)] p-1 text-body-sm text-[var(--color-text-default)] shadow-[var(--shadow-popover)] data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",
45
+ "[z-index:var(--z-floating)] max-h-(--radix-dropdown-menu-content-available-height) min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto [border-radius:var(--radius-popover)] border border-[var(--color-border-default)] bg-[var(--color-bg-overlay)] p-1 text-body-sm text-[var(--color-text-default)] shadow-[var(--shadow-popover)] data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",
46
46
  className
47
47
  )}
48
48
  {...props}
@@ -230,7 +230,7 @@ function DropdownMenuSubContent({
230
230
  <DropdownMenuPrimitive.SubContent
231
231
  data-slot="dropdown-menu-sub-content"
232
232
  className={cn(
233
- "[z-index:var(--z-dropdown)] min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-hidden [border-radius:var(--radius-popover)] border border-[var(--color-border-default)] bg-[var(--color-bg-overlay)] p-1 text-[var(--color-text-default)] shadow-[var(--shadow-popover)] data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",
233
+ "[z-index:var(--z-floating)] min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-hidden [border-radius:var(--radius-popover)] border border-[var(--color-border-default)] bg-[var(--color-bg-overlay)] p-1 text-[var(--color-text-default)] shadow-[var(--shadow-popover)] data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",
234
234
  className
235
235
  )}
236
236
  {...props}
@@ -52,7 +52,7 @@ function FilterChip(props: FilterChipProps) {
52
52
  {values.map((value, i) => (
53
53
  <span
54
54
  key={i}
55
- className="inline-flex items-center gap-1 rounded-full border border-[var(--color-border-default)] px-2 py-0.5 text-caption text-[var(--color-text-default)]"
55
+ className="inline-flex items-center gap-1 rounded-full border border-[var(--color-border-default)] bg-[var(--color-bg-base)] px-2 py-0.5 text-caption text-[var(--color-text-default)]"
56
56
  >
57
57
  {value.label}
58
58
  <button
@@ -62,6 +62,7 @@ function FilterCombobox({
62
62
 
63
63
  const containerRef = React.useRef<HTMLDivElement>(null)
64
64
  const searchRef = React.useRef<HTMLInputElement>(null)
65
+ const isSingle = categories.length === 1
65
66
 
66
67
  // Close on outside click
67
68
  React.useEffect(() => {
@@ -91,6 +92,18 @@ function FilterCombobox({
91
92
  setAmountMax("")
92
93
  }
93
94
 
95
+ function handleToggle() {
96
+ if (open) {
97
+ handleClose()
98
+ return
99
+ }
100
+ // Single-category: skip step 1 and jump straight to options
101
+ if (isSingle) {
102
+ openCategory(categories[0])
103
+ }
104
+ setOpen(true)
105
+ }
106
+
94
107
  function openCategory(cat: FilterCategory) {
95
108
  const existing = activeFilters[cat.id] ?? []
96
109
 
@@ -193,8 +206,8 @@ function FilterCombobox({
193
206
 
194
207
  return (
195
208
  <div ref={containerRef} className={cn("relative inline-block", className)}>
196
- {trigger ? trigger({ open, onClick: () => setOpen(v => !v) }) : (
197
- <Button variant="default" size="sm" className="h-7 text-caption gap-1.5 px-2.5" onClick={() => setOpen(v => !v)}>
209
+ {trigger ? trigger({ open, onClick: handleToggle }) : (
210
+ <Button variant="default" size="sm" className="h-7 text-caption gap-1.5 px-2.5" onClick={handleToggle}>
198
211
  <PlusIcon className="size-3.5" />
199
212
  Add filter
200
213
  </Button>
@@ -204,11 +217,11 @@ function FilterCombobox({
204
217
  <div className={cn(
205
218
  dropdownAlign === "right" ? "absolute right-0 top-full mt-1.5 overflow-hidden [border-radius:var(--radius-popover)]" : "absolute left-0 top-full mt-1.5 overflow-hidden [border-radius:var(--radius-popover)]",
206
219
  "bg-[var(--color-bg-overlay)] shadow-[var(--shadow-popover)] ring-1 ring-[var(--color-border-subtle)]",
207
- "[z-index:var(--z-dropdown)]",
220
+ "[z-index:var(--z-floating)]",
208
221
  step === 3 && isDate ? "w-auto" : "w-56"
209
222
  )}>
210
223
 
211
- {/* Step 1 — category list */}
224
+ {/* Step 1 — category list (multi-category mode only) */}
212
225
  {step === 1 && (
213
226
  <ul className="p-1">
214
227
  {categories.map(cat => (
@@ -222,10 +235,12 @@ function FilterCombobox({
222
235
  {/* Step 2 — value list */}
223
236
  {step === 2 && selectedCategory && (
224
237
  <>
225
- <StepHeader
226
- title={selectedCategory.label}
227
- onBack={() => { setStep(1); setSearch("") }}
228
- />
238
+ {!isSingle && (
239
+ <StepHeader
240
+ title={selectedCategory.label}
241
+ onBack={() => { setStep(1); setSearch("") }}
242
+ />
243
+ )}
229
244
  {showSearch && (
230
245
  <div className="px-1 pt-1">
231
246
  <SearchInput ref={searchRef} value={search} onChange={setSearch} />
@@ -259,10 +274,12 @@ function FilterCombobox({
259
274
  {/* Step 3 — custom date range */}
260
275
  {step === 3 && isDate && (
261
276
  <>
262
- <StepHeader
263
- title={selectedCategory?.label ?? "Date"}
264
- onBack={() => setStep(1)}
265
- />
277
+ {!isSingle && (
278
+ <StepHeader
279
+ title={selectedCategory?.label ?? "Date"}
280
+ onBack={() => setStep(1)}
281
+ />
282
+ )}
266
283
  <div className="p-1">
267
284
  <Calendar mode="range" selected={dateRange} onSelect={setDateRange} numberOfMonths={1} />
268
285
  </div>
@@ -276,14 +293,16 @@ function FilterCombobox({
276
293
  </>
277
294
  )}
278
295
 
279
- {/* Step 3 — custom amount (no step 2 for amount — opens here directly) */}
296
+ {/* Step 3 — custom amount */}
280
297
  {step === 3 && isAmount && (
281
298
  <>
282
- <StepHeader
283
- title="Amount"
284
- onBack={() => { setStep(1) }}
285
- />
286
- <div className="px-2 pb-2 flex flex-col gap-2">
299
+ {!isSingle && (
300
+ <StepHeader
301
+ title="Amount"
302
+ onBack={() => setStep(1)}
303
+ />
304
+ )}
305
+ <div className="p-2 flex flex-col gap-2">
287
306
  <Tabs
288
307
  value={amountMode}
289
308
  onValueChange={v => setAmountMode(v as "exact" | "range")}
@@ -52,6 +52,7 @@ interface SearchBarProps {
52
52
  onRemoveFilter?: (categoryId: string, value: CategoryValue) => void
53
53
  onClearFilters?: () => void
54
54
  filterValueLabel?: (categoryId: string, value: CategoryValue) => string
55
+ alwaysShowFilterBar?: boolean
55
56
 
56
57
  children?: React.ReactNode
57
58
  className?: string
@@ -73,11 +74,13 @@ function SearchBar({
73
74
  onRemoveFilter,
74
75
  onClearFilters,
75
76
  filterValueLabel = defaultFilterValueLabel,
77
+ alwaysShowFilterBar = false,
76
78
  children,
77
79
  className,
78
80
  }: SearchBarProps) {
79
81
  const hasActiveFilters = Object.values(activeFilters).some(v => v.length > 0)
80
82
  const hasFilterCategories = filterCategories.length > 0
83
+ const showFilterBar = hasActiveFilters || alwaysShowFilterBar
81
84
 
82
85
  return (
83
86
  <div data-search-bar className={cn("relative flex flex-col", className)}>
@@ -86,7 +89,7 @@ function SearchBar({
86
89
  <InputGroup
87
90
  className={cn(
88
91
  "h-12",
89
- hasActiveFilters && "[border-bottom-left-radius:0] [border-bottom-right-radius:0]"
92
+ showFilterBar && "[border-bottom-left-radius:0] [border-bottom-right-radius:0]"
90
93
  )}
91
94
  >
92
95
  <InputGroupAddon align="inline-start">
@@ -101,14 +104,15 @@ function SearchBar({
101
104
  onFocus={onFocus}
102
105
  autoComplete="off"
103
106
  />
104
- {(!!value || (hasFilterCategories && !hasActiveFilters)) && (
107
+ {(!!value || (hasFilterCategories && !hasActiveFilters && !alwaysShowFilterBar) || (!value && !!kbdHint)) && (
105
108
  <InputGroupAddon align="inline-end">
106
109
  {value && (
107
110
  <InputGroupButton size="sm" variant="ghost" onClick={() => onChange("")} className="text-[var(--color-text-muted)] hover:text-[var(--color-text-default)]">
108
111
  <XIcon className="size-4" />
109
112
  </InputGroupButton>
110
113
  )}
111
- {hasFilterCategories && !hasActiveFilters && (
114
+ {!value && kbdHint && (!hasFilterCategories || alwaysShowFilterBar) && <Kbd>{kbdHint}</Kbd>}
115
+ {hasFilterCategories && !hasActiveFilters && !alwaysShowFilterBar && (
112
116
  <>
113
117
  {!value && kbdHint && <Kbd>{kbdHint}</Kbd>}
114
118
  <span className="h-4 w-px bg-[var(--color-border-default)]" />
@@ -129,41 +133,102 @@ function SearchBar({
129
133
  )}
130
134
  </InputGroup>
131
135
 
132
- {/* Filter bar — visible only when filters are active */}
133
- {hasActiveFilters && (
136
+ {/* Filter bar */}
137
+ {showFilterBar && (
134
138
  <div className="flex flex-wrap items-center gap-3 [border-bottom-left-radius:var(--radius-card)] [border-bottom-right-radius:var(--radius-card)] border border-t-0 border-[var(--color-border-default)] bg-[var(--color-bg-raised)] px-3 py-2.5">
135
- {Object.entries(activeFilters)
136
- .filter(([, vals]) => vals.length > 0)
137
- .map(([categoryId, values]) => {
138
- const cat = filterCategories.find(c => c.id === categoryId)
139
- return (
139
+ {alwaysShowFilterBar ? (
140
+ // Always-open mode: one trigger per category, active ones show chips
141
+ <>
142
+ <span className="text-caption text-[var(--color-text-default)] shrink-0">Filters:</span>
143
+ {filterCategories.map(cat => {
144
+ const activeVals = activeFilters[cat.id] ?? []
145
+ const hasActive = activeVals.length > 0
146
+
147
+ if (hasActive) {
148
+ return (
149
+ <FilterCombobox
150
+ key={cat.id}
151
+ categories={[cat]}
152
+ onApply={onApplyFilter ?? (() => {})}
153
+ activeFilters={activeFilters}
154
+ trigger={({ onClick }) => (
155
+ <FilterChip
156
+ label={cat.label}
157
+ values={activeVals.map(v => ({
158
+ label: filterValueLabel(cat.id, v),
159
+ onRemove: () => onRemoveFilter?.(cat.id, v),
160
+ }))}
161
+ onLabelClick={onClick}
162
+ />
163
+ )}
164
+ />
165
+ )
166
+ }
167
+
168
+ return (
169
+ <FilterCombobox
170
+ key={cat.id}
171
+ categories={[cat]}
172
+ onApply={onApplyFilter ?? (() => {})}
173
+ activeFilters={activeFilters}
174
+ trigger={({ onClick }) => (
175
+ <button
176
+ type="button"
177
+ onClick={onClick}
178
+ className="inline-flex items-center gap-1 rounded-full border border-[var(--color-border-default)] px-2 py-0.5 text-caption text-[var(--color-text-muted)] hover:text-[var(--color-text-default)] hover:border-[var(--color-border-hover,var(--color-border-default))] transition-colors"
179
+ >
180
+ {cat.label}
181
+ <PlusIcon className="size-3 shrink-0" />
182
+ </button>
183
+ )}
184
+ />
185
+ )
186
+ })}
187
+ {hasActiveFilters && (
140
188
  <FilterChip
141
- key={categoryId}
142
- label={cat?.label ?? categoryId}
143
- values={values.map(v => ({
144
- label: filterValueLabel(categoryId, v),
145
- onRemove: () => onRemoveFilter?.(categoryId, v),
146
- }))}
147
- onLabelClick={() => {}}
189
+ variant="clear-all"
190
+ className="ml-auto"
191
+ onClear={onClearFilters ?? (() => {})}
148
192
  />
149
- )
150
- })}
151
- <FilterCombobox
152
- categories={filterCategories}
153
- onApply={onApplyFilter ?? (() => {})}
154
- activeFilters={activeFilters}
155
- trigger={({ onClick }) => (
156
- <Button variant="default" size="sm" className="h-7 text-caption gap-1.5 px-2.5" onClick={onClick}>
157
- <PlusIcon className="size-3.5" />
158
- Add
159
- </Button>
160
- )}
161
- />
162
- <FilterChip
163
- variant="clear-all"
164
- className="ml-auto"
165
- onClear={onClearFilters ?? (() => {})}
166
- />
193
+ )}
194
+ </>
195
+ ) : (
196
+ // Legacy mode: active chips + generic Add button
197
+ <>
198
+ {Object.entries(activeFilters)
199
+ .filter(([, vals]) => vals.length > 0)
200
+ .map(([categoryId, values]) => {
201
+ const cat = filterCategories.find(c => c.id === categoryId)
202
+ return (
203
+ <FilterChip
204
+ key={categoryId}
205
+ label={cat?.label ?? categoryId}
206
+ values={values.map(v => ({
207
+ label: filterValueLabel(categoryId, v),
208
+ onRemove: () => onRemoveFilter?.(categoryId, v),
209
+ }))}
210
+ onLabelClick={() => {}}
211
+ />
212
+ )
213
+ })}
214
+ <FilterCombobox
215
+ categories={filterCategories}
216
+ onApply={onApplyFilter ?? (() => {})}
217
+ activeFilters={activeFilters}
218
+ trigger={({ onClick }) => (
219
+ <Button variant="default" size="sm" className="h-7 text-caption gap-1.5 px-2.5" onClick={onClick}>
220
+ <PlusIcon className="size-3.5" />
221
+ Add
222
+ </Button>
223
+ )}
224
+ />
225
+ <FilterChip
226
+ variant="clear-all"
227
+ className="ml-auto"
228
+ onClear={onClearFilters ?? (() => {})}
229
+ />
230
+ </>
231
+ )}
167
232
  </div>
168
233
  )}
169
234
 
@@ -69,7 +69,7 @@ function SelectContent({
69
69
  <SelectPrimitive.Content
70
70
  data-slot="select-content"
71
71
  className={cn(
72
- "relative z-[var(--z-dropdown)] max-h-(--radix-select-content-available-height) min-w-[8rem] origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto [border-radius:var(--radius-popover)] border border-[var(--color-border-default)] bg-[var(--color-bg-overlay)] text-[var(--color-text-default)] shadow-[var(--shadow-popover)]",
72
+ "relative z-[var(--z-floating)] max-h-(--radix-select-content-available-height) min-w-[8rem] origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto [border-radius:var(--radius-popover)] border border-[var(--color-border-default)] bg-[var(--color-bg-overlay)] text-[var(--color-text-default)] shadow-[var(--shadow-popover)]",
73
73
  "data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
74
74
  "data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95",
75
75
  "data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",
@@ -0,0 +1,64 @@
1
+ "use client"
2
+
3
+ import { Toaster as Sonner, toast } from "sonner"
4
+
5
+ type ToasterProps = React.ComponentProps<typeof Sonner>
6
+
7
+ function Toaster({ ...props }: ToasterProps) {
8
+ return (
9
+ <Sonner
10
+ className="toaster group"
11
+ style={
12
+ {
13
+ "--normal-bg": "var(--primitive-neutral-950)",
14
+ "--normal-text": "var(--primitive-neutral-50)",
15
+ "--normal-border": "var(--primitive-neutral-900)",
16
+
17
+ "--success-bg": "var(--primitive-neutral-950)",
18
+ "--success-text": "var(--primitive-green-400)",
19
+ "--success-border": "var(--primitive-neutral-900)",
20
+
21
+ "--error-bg": "var(--primitive-neutral-950)",
22
+ "--error-text": "var(--primitive-red-400)",
23
+ "--error-border": "var(--primitive-neutral-900)",
24
+
25
+ "--warning-bg": "var(--primitive-neutral-950)",
26
+ "--warning-text": "var(--primitive-yellow-300)",
27
+ "--warning-border": "var(--primitive-neutral-900)",
28
+
29
+ "--info-bg": "var(--primitive-neutral-950)",
30
+ "--info-text": "var(--primitive-blue-400)",
31
+ "--info-border": "var(--primitive-neutral-900)",
32
+
33
+ "--border-radius": "var(--radius-card)",
34
+ "--font": "inherit",
35
+ } as React.CSSProperties
36
+ }
37
+ toastOptions={{
38
+ classNames: {
39
+ toast: "!items-start",
40
+ icon: "mt-0.5",
41
+ title: "text-label !text-[var(--primitive-neutral-50)]",
42
+ description: "text-body-sm !text-[var(--primitive-neutral-300)]",
43
+ success: "[&_[data-icon]]:text-[var(--primitive-green-400)]",
44
+ error: "[&_[data-icon]]:text-[var(--primitive-red-400)]",
45
+ warning: "[&_[data-icon]]:text-[var(--primitive-yellow-300)]",
46
+ info: "[&_[data-icon]]:text-[var(--primitive-neutral-50)]",
47
+ },
48
+ actionButtonStyle: {
49
+ height: "2rem",
50
+ padding: "0 0.75rem",
51
+ borderRadius: "var(--radius-button)",
52
+ border: "1px solid var(--primitive-neutral-700)",
53
+ background: "var(--primitive-neutral-800)",
54
+ color: "var(--primitive-neutral-50)",
55
+ fontSize: "0.875rem",
56
+ fontWeight: "600",
57
+ },
58
+ }}
59
+ {...props}
60
+ />
61
+ )
62
+ }
63
+
64
+ export { Toaster, toast }
@@ -30,7 +30,7 @@ const tabsListVariants = cva(
30
30
  {
31
31
  variants: {
32
32
  variant: {
33
- default: "[border-radius:var(--radius-card)] bg-[var(--color-bg-disabled)]",
33
+ default: "[border-radius:var(--radius-card)] border border-[var(--color-border-default)] bg-[var(--color-bg-raised)] shadow-xs",
34
34
  line: "gap-1 rounded-none bg-transparent",
35
35
  },
36
36
  },
@@ -71,7 +71,7 @@ function TabsTrigger({
71
71
  "focus-visible:border-[var(--color-border-focus)] focus-visible:ring-[3px] focus-visible:ring-[var(--color-border-focus)]/50",
72
72
  "disabled:pointer-events-none disabled:text-[var(--color-text-disabled)]",
73
73
  // default variant — active tab gets raised surface
74
- "group-data-[variant=default]/tabs-list:data-[state=active]:bg-[var(--color-bg-raised)] group-data-[variant=default]/tabs-list:data-[state=active]:text-[var(--color-text-default)] group-data-[variant=default]/tabs-list:data-[state=active]:shadow-[var(--shadow-card)]",
74
+ "group-data-[variant=default]/tabs-list:data-[state=active]:bg-[var(--color-bg-inverted)] group-data-[variant=default]/tabs-list:data-[state=active]:text-[var(--color-bg-raised)] group-data-[variant=default]/tabs-list:data-[state=active]:shadow-[var(--shadow-card)]",
75
75
  // line variant — active tab is transparent, underline indicator
76
76
  "group-data-[variant=line]/tabs-list:bg-transparent group-data-[variant=line]/tabs-list:data-[state=active]:bg-transparent group-data-[variant=line]/tabs-list:data-[state=active]:text-[var(--color-text-default)] group-data-[variant=line]/tabs-list:data-[state=active]:shadow-none",
77
77
  // underline indicator for line variant
@@ -54,4 +54,30 @@ function TooltipContent({
54
54
  )
55
55
  }
56
56
 
57
- export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }
57
+ function TooltipLabel({
58
+ className,
59
+ ...props
60
+ }: React.ComponentProps<"p">) {
61
+ return (
62
+ <p
63
+ data-slot="tooltip-label"
64
+ className={cn("text-caption text-[var(--color-text-inverse)]", className)}
65
+ {...props}
66
+ />
67
+ )
68
+ }
69
+
70
+ function TooltipDescription({
71
+ className,
72
+ ...props
73
+ }: React.ComponentProps<"p">) {
74
+ return (
75
+ <p
76
+ data-slot="tooltip-description"
77
+ className={cn("text-caption text-[var(--color-text-inverse-muted)]", className)}
78
+ {...props}
79
+ />
80
+ )
81
+ }
82
+
83
+ export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider, TooltipLabel, TooltipDescription }
package/src/index.ts CHANGED
@@ -27,6 +27,7 @@ export * from "./components/ui/kbd"
27
27
  export * from "./components/ui/label"
28
28
  export * from "./components/ui/pagination"
29
29
  export * from "./components/ui/select"
30
+ export * from "./components/ui/sonner"
30
31
  export * from "./components/ui/separator"
31
32
  export * from "./components/ui/sheet"
32
33
  export * from "./components/ui/sidebar"
@@ -0,0 +1,13 @@
1
+ /*
2
+ * Gradient tokens — semantic gradient expressions.
3
+ * Import after primitives.css. References semantic tokens (e.g. --color-bg-canvas)
4
+ * which are resolved at paint time, so import order with app globals doesn't matter.
5
+ */
6
+
7
+ :root {
8
+ --gradient-page: linear-gradient(180deg, var(--color-bg-base) 0%, var(--color-bg-base) 20%, var(--primitive-neutral-0) 100%);
9
+ }
10
+
11
+ .dark {
12
+ --gradient-page: linear-gradient(180deg, var(--color-bg-base) 0%, var(--color-bg-base) 20%, var(--primitive-neutral-800) 100%);
13
+ }
@@ -22,6 +22,18 @@
22
22
 
23
23
  /* --- Color scales --- */
24
24
 
25
+ --primitive-beige-50: oklch(0.990 0.008 98);
26
+ --primitive-beige-100: oklch(0.982 0.030 98);
27
+ --primitive-beige-200: oklch(0.968 0.052 98);
28
+ --primitive-beige-300: oklch(0.948 0.072 98);
29
+ --primitive-beige-400: oklch(0.918 0.083 98);
30
+ --primitive-beige-500: oklch(0.876 0.085 98);
31
+ --primitive-beige-600: oklch(0.792 0.076 98);
32
+ --primitive-beige-700: oklch(0.680 0.062 98);
33
+ --primitive-beige-800: oklch(0.480 0.044 98);
34
+ --primitive-beige-900: oklch(0.310 0.028 98);
35
+ --primitive-beige-950: oklch(0.230 0.020 98);
36
+
25
37
  --primitive-red-50: oklch(0.95 0.03 28);
26
38
  --primitive-red-100: oklch(0.93 0.05 28);
27
39
  --primitive-red-200: oklch(0.89 0.07 28);
@@ -58,9 +70,9 @@
58
70
  --primitive-yellow-900: oklch(0.3 0.07 98);
59
71
  --primitive-yellow-950: oklch(0.25 0.06 98);
60
72
 
61
- --primitive-lulo-50: oklch(0.94 0.24 112);
62
- --primitive-lulo-100: oklch(0.92 0.23 112);
63
- --primitive-lulo-200: oklch(0.87 0.22 112);
73
+ --primitive-lulo-50: oklch(0.967 0.099 118);
74
+ --primitive-lulo-100: oklch(0.918 0.136 116);
75
+ --primitive-lulo-200: oklch(0.869 0.173 114);
64
76
  --primitive-lulo-300: oklch(0.82 0.21 112);
65
77
  --primitive-lulo-400: oklch(0.76 0.2 112);
66
78
  --primitive-lulo-500: oklch(0.7 0.18 112);
@@ -106,6 +118,18 @@
106
118
  --primitive-sea-900: oklch(0.29 0.07 220);
107
119
  --primitive-sea-950: oklch(0.24 0.06 220);
108
120
 
121
+ --primitive-slate-50: oklch(0.950 0.012 218);
122
+ --primitive-slate-100: oklch(0.932 0.020 218);
123
+ --primitive-slate-200: oklch(0.898 0.032 218);
124
+ --primitive-slate-300: oklch(0.848 0.042 218);
125
+ --primitive-slate-400: oklch(0.762 0.054 218);
126
+ --primitive-slate-500: oklch(0.656 0.063 218);
127
+ --primitive-slate-600: oklch(0.560 0.054 218);
128
+ --primitive-slate-700: oklch(0.472 0.046 218);
129
+ --primitive-slate-800: oklch(0.352 0.034 218);
130
+ --primitive-slate-900: oklch(0.248 0.023 218);
131
+ --primitive-slate-950: oklch(0.198 0.017 218);
132
+
109
133
  --primitive-blue-50: oklch(0.95 0.03 257);
110
134
  --primitive-blue-100: oklch(0.93 0.04 257);
111
135
  --primitive-blue-200: oklch(0.88 0.06 257);
@@ -222,7 +246,8 @@
222
246
  --primitive-z-raised: 10;
223
247
  --primitive-z-dropdown: 20;
224
248
  --primitive-z-overlay: 100;
225
- --primitive-z-modal: 200;
226
- --primitive-z-toast: 300;
249
+ --primitive-z-modal: 200;
250
+ --primitive-z-floating: 210;
251
+ --primitive-z-toast: 300;
227
252
  --primitive-z-tooltip: 400;
228
253
  }