@papernote/ui 2.0.2 → 2.0.4

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/dist/styles.css CHANGED
@@ -1424,6 +1424,10 @@
1424
1424
  --tw-translate-x: calc(var(--spacing) * 0);
1425
1425
  translate: var(--tw-translate-x) var(--tw-translate-y);
1426
1426
  }
1427
+ .translate-x-1 {
1428
+ --tw-translate-x: calc(var(--spacing) * 1);
1429
+ translate: var(--tw-translate-x) var(--tw-translate-y);
1430
+ }
1427
1431
  .translate-x-4 {
1428
1432
  --tw-translate-x: calc(var(--spacing) * 4);
1429
1433
  translate: var(--tw-translate-x) var(--tw-translate-y);
@@ -1432,6 +1436,10 @@
1432
1436
  --tw-translate-x: calc(var(--spacing) * 5);
1433
1437
  translate: var(--tw-translate-x) var(--tw-translate-y);
1434
1438
  }
1439
+ .translate-x-6 {
1440
+ --tw-translate-x: calc(var(--spacing) * 6);
1441
+ translate: var(--tw-translate-x) var(--tw-translate-y);
1442
+ }
1435
1443
  .translate-x-7 {
1436
1444
  --tw-translate-x: calc(var(--spacing) * 7);
1437
1445
  translate: var(--tw-translate-x) var(--tw-translate-y);
@@ -5081,27 +5089,27 @@
5081
5089
  }
5082
5090
  }
5083
5091
  .dark\:border-blue-800 {
5084
- @media (prefers-color-scheme: dark) {
5092
+ &:where(.dark, .dark *) {
5085
5093
  border-color: var(--color-blue-800);
5086
5094
  }
5087
5095
  }
5088
5096
  .dark\:border-error-800 {
5089
- @media (prefers-color-scheme: dark) {
5097
+ &:where(.dark, .dark *) {
5090
5098
  border-color: var(--color-error-800);
5091
5099
  }
5092
5100
  }
5093
5101
  .dark\:border-ink-700 {
5094
- @media (prefers-color-scheme: dark) {
5102
+ &:where(.dark, .dark *) {
5095
5103
  border-color: var(--color-ink-700);
5096
5104
  }
5097
5105
  }
5098
5106
  .dark\:border-warning-800 {
5099
- @media (prefers-color-scheme: dark) {
5107
+ &:where(.dark, .dark *) {
5100
5108
  border-color: var(--color-warning-800);
5101
5109
  }
5102
5110
  }
5103
5111
  .dark\:bg-blue-900\/20 {
5104
- @media (prefers-color-scheme: dark) {
5112
+ &:where(.dark, .dark *) {
5105
5113
  background-color: color-mix(in srgb, oklch(37.9% 0.146 265.522) 20%, transparent);
5106
5114
  @supports (color: color-mix(in lab, red, red)) {
5107
5115
  background-color: color-mix(in oklab, var(--color-blue-900) 20%, transparent);
@@ -5109,7 +5117,7 @@
5109
5117
  }
5110
5118
  }
5111
5119
  .dark\:bg-error-900\/20 {
5112
- @media (prefers-color-scheme: dark) {
5120
+ &:where(.dark, .dark *) {
5113
5121
  background-color: color-mix(in srgb, #7f1d1d 20%, transparent);
5114
5122
  @supports (color: color-mix(in lab, red, red)) {
5115
5123
  background-color: color-mix(in oklab, var(--color-error-900) 20%, transparent);
@@ -5117,27 +5125,27 @@
5117
5125
  }
5118
5126
  }
5119
5127
  .dark\:bg-ink-700 {
5120
- @media (prefers-color-scheme: dark) {
5128
+ &:where(.dark, .dark *) {
5121
5129
  background-color: var(--color-ink-700);
5122
5130
  }
5123
5131
  }
5124
5132
  .dark\:bg-ink-800 {
5125
- @media (prefers-color-scheme: dark) {
5133
+ &:where(.dark, .dark *) {
5126
5134
  background-color: var(--color-ink-800);
5127
5135
  }
5128
5136
  }
5129
5137
  .dark\:bg-ink-900 {
5130
- @media (prefers-color-scheme: dark) {
5138
+ &:where(.dark, .dark *) {
5131
5139
  background-color: var(--color-ink-900);
5132
5140
  }
5133
5141
  }
5134
5142
  .dark\:bg-primary-900 {
5135
- @media (prefers-color-scheme: dark) {
5143
+ &:where(.dark, .dark *) {
5136
5144
  background-color: var(--color-primary-900);
5137
5145
  }
5138
5146
  }
5139
5147
  .dark\:bg-primary-900\/20 {
5140
- @media (prefers-color-scheme: dark) {
5148
+ &:where(.dark, .dark *) {
5141
5149
  background-color: color-mix(in srgb, #0f172a 20%, transparent);
5142
5150
  @supports (color: color-mix(in lab, red, red)) {
5143
5151
  background-color: color-mix(in oklab, var(--color-primary-900) 20%, transparent);
@@ -5145,17 +5153,17 @@
5145
5153
  }
5146
5154
  }
5147
5155
  .dark\:bg-slate-800 {
5148
- @media (prefers-color-scheme: dark) {
5156
+ &:where(.dark, .dark *) {
5149
5157
  background-color: var(--color-slate-800);
5150
5158
  }
5151
5159
  }
5152
5160
  .dark\:bg-warning-900 {
5153
- @media (prefers-color-scheme: dark) {
5161
+ &:where(.dark, .dark *) {
5154
5162
  background-color: var(--color-warning-900);
5155
5163
  }
5156
5164
  }
5157
5165
  .dark\:bg-warning-900\/20 {
5158
- @media (prefers-color-scheme: dark) {
5166
+ &:where(.dark, .dark *) {
5159
5167
  background-color: color-mix(in srgb, #78350f 20%, transparent);
5160
5168
  @supports (color: color-mix(in lab, red, red)) {
5161
5169
  background-color: color-mix(in oklab, var(--color-warning-900) 20%, transparent);
@@ -5163,62 +5171,62 @@
5163
5171
  }
5164
5172
  }
5165
5173
  .dark\:fill-ink-300 {
5166
- @media (prefers-color-scheme: dark) {
5174
+ &:where(.dark, .dark *) {
5167
5175
  fill: var(--color-ink-300);
5168
5176
  }
5169
5177
  }
5170
5178
  .dark\:text-blue-200 {
5171
- @media (prefers-color-scheme: dark) {
5179
+ &:where(.dark, .dark *) {
5172
5180
  color: var(--color-blue-200);
5173
5181
  }
5174
5182
  }
5175
5183
  .dark\:text-error-200 {
5176
- @media (prefers-color-scheme: dark) {
5184
+ &:where(.dark, .dark *) {
5177
5185
  color: var(--color-error-200);
5178
5186
  }
5179
5187
  }
5180
5188
  .dark\:text-ink-100 {
5181
- @media (prefers-color-scheme: dark) {
5189
+ &:where(.dark, .dark *) {
5182
5190
  color: var(--color-ink-100);
5183
5191
  }
5184
5192
  }
5185
5193
  .dark\:text-ink-300 {
5186
- @media (prefers-color-scheme: dark) {
5194
+ &:where(.dark, .dark *) {
5187
5195
  color: var(--color-ink-300);
5188
5196
  }
5189
5197
  }
5190
5198
  .dark\:text-ink-400 {
5191
- @media (prefers-color-scheme: dark) {
5199
+ &:where(.dark, .dark *) {
5192
5200
  color: var(--color-ink-400);
5193
5201
  }
5194
5202
  }
5195
5203
  .dark\:text-primary-300 {
5196
- @media (prefers-color-scheme: dark) {
5204
+ &:where(.dark, .dark *) {
5197
5205
  color: var(--color-primary-300);
5198
5206
  }
5199
5207
  }
5200
5208
  .dark\:text-primary-400 {
5201
- @media (prefers-color-scheme: dark) {
5209
+ &:where(.dark, .dark *) {
5202
5210
  color: var(--color-primary-400);
5203
5211
  }
5204
5212
  }
5205
5213
  .dark\:text-slate-400 {
5206
- @media (prefers-color-scheme: dark) {
5214
+ &:where(.dark, .dark *) {
5207
5215
  color: var(--color-slate-400);
5208
5216
  }
5209
5217
  }
5210
5218
  .dark\:text-warning-200 {
5211
- @media (prefers-color-scheme: dark) {
5219
+ &:where(.dark, .dark *) {
5212
5220
  color: var(--color-warning-200);
5213
5221
  }
5214
5222
  }
5215
5223
  .dark\:text-warning-300 {
5216
- @media (prefers-color-scheme: dark) {
5224
+ &:where(.dark, .dark *) {
5217
5225
  color: var(--color-warning-300);
5218
5226
  }
5219
5227
  }
5220
5228
  .dark\:hover\:bg-slate-700 {
5221
- @media (prefers-color-scheme: dark) {
5229
+ &:where(.dark, .dark *) {
5222
5230
  &:hover {
5223
5231
  @media (hover: hover) {
5224
5232
  background-color: var(--color-slate-700);
@@ -5227,7 +5235,7 @@
5227
5235
  }
5228
5236
  }
5229
5237
  .dark\:hover\:bg-slate-800 {
5230
- @media (prefers-color-scheme: dark) {
5238
+ &:where(.dark, .dark *) {
5231
5239
  &:hover {
5232
5240
  @media (hover: hover) {
5233
5241
  background-color: var(--color-slate-800);
@@ -5236,7 +5244,7 @@
5236
5244
  }
5237
5245
  }
5238
5246
  .dark\:hover\:text-slate-100 {
5239
- @media (prefers-color-scheme: dark) {
5247
+ &:where(.dark, .dark *) {
5240
5248
  &:hover {
5241
5249
  @media (hover: hover) {
5242
5250
  color: var(--color-slate-100);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@papernote/ui",
3
- "version": "2.0.2",
3
+ "version": "2.0.4",
4
4
  "type": "module",
5
5
  "description": "A modern React component library with a paper notebook aesthetic - minimal, professional, and expressive",
6
6
  "main": "dist/index.js",
@@ -1,12 +1,22 @@
1
- import { X, Search } from 'lucide-react';
2
- import Input from './Input';
3
- import Select, { type SelectOption } from './Select';
4
- import Button from './Button';
1
+ import { X, Search } from "lucide-react";
2
+ import Input from "./Input";
3
+ import Select, { type SelectOption } from "./Select";
4
+ import Button from "./Button";
5
5
 
6
6
  export interface FilterConfig {
7
7
  key: string;
8
8
  label: string;
9
- type: 'text' | 'search' | 'select' | 'date' | 'number' | 'boolean' | 'dateRange' | 'toggle' | 'multiSelect';
9
+ type:
10
+ | "text"
11
+ | "search"
12
+ | "select"
13
+ | "date"
14
+ | "number"
15
+ | "boolean"
16
+ | "dateRange"
17
+ | "toggle"
18
+ | "switch"
19
+ | "multiSelect";
10
20
  placeholder?: string;
11
21
  options?: Array<{ label: string; value: unknown }>;
12
22
  }
@@ -24,7 +34,7 @@ export default function FilterBar({
24
34
  filters,
25
35
  values,
26
36
  onChange,
27
- className = '',
37
+ className = "",
28
38
  onClear,
29
39
  showClearButton = false,
30
40
  }: FilterBarProps) {
@@ -41,13 +51,15 @@ export default function FilterBar({
41
51
  } else {
42
52
  // Default clear: set all values to null/empty
43
53
  const clearedValues: Record<string, unknown> = {};
44
- filters.forEach(filter => {
45
- if (filter.type === 'text' || filter.type === 'search') {
46
- clearedValues[filter.key] = '';
47
- } else if (filter.type === 'dateRange') {
54
+ filters.forEach((filter) => {
55
+ if (filter.type === "text" || filter.type === "search") {
56
+ clearedValues[filter.key] = "";
57
+ } else if (filter.type === "dateRange") {
48
58
  clearedValues[filter.key] = { from: undefined, to: undefined };
49
- } else if (filter.type === 'multiSelect') {
59
+ } else if (filter.type === "multiSelect") {
50
60
  clearedValues[filter.key] = [];
61
+ } else if (filter.type === "switch") {
62
+ clearedValues[filter.key] = false;
51
63
  } else {
52
64
  clearedValues[filter.key] = null;
53
65
  }
@@ -60,20 +72,20 @@ export default function FilterBar({
60
72
  const value = values[filter.key];
61
73
 
62
74
  switch (filter.type) {
63
- case 'text':
75
+ case "text":
64
76
  return (
65
77
  <Input
66
78
  type="text"
67
79
  placeholder={filter.placeholder || `Filter by ${filter.label}`}
68
- value={(value as string) || ''}
80
+ value={(value as string) || ""}
69
81
  onChange={(e) => handleFilterChange(filter.key, e.target.value)}
70
82
  />
71
83
  );
72
84
 
73
- case 'select': {
85
+ case "select": {
74
86
  const selectOptions: SelectOption[] = [
75
- { value: '', label: `All ${filter.label}` },
76
- ...(filter.options?.map(opt => ({
87
+ { value: "", label: `All ${filter.label}` },
88
+ ...(filter.options?.map((opt) => ({
77
89
  value: String(opt.value),
78
90
  label: opt.label,
79
91
  })) || []),
@@ -82,60 +94,62 @@ export default function FilterBar({
82
94
  return (
83
95
  <Select
84
96
  options={selectOptions}
85
- value={String(value || '')}
86
- onChange={(newValue) => handleFilterChange(filter.key, newValue || null)}
97
+ value={String(value || "")}
98
+ onChange={(newValue) =>
99
+ handleFilterChange(filter.key, newValue || null)
100
+ }
87
101
  />
88
102
  );
89
103
  }
90
104
 
91
- case 'date':
105
+ case "date":
92
106
  return (
93
107
  <input
94
108
  type="date"
95
- value={(value as string) || ''}
109
+ value={(value as string) || ""}
96
110
  onChange={(e) => handleFilterChange(filter.key, e.target.value)}
97
111
  className="input"
98
112
  />
99
113
  );
100
114
 
101
- case 'number':
115
+ case "number":
102
116
  return (
103
117
  <input
104
118
  type="number"
105
119
  placeholder={filter.placeholder || `Filter by ${filter.label}`}
106
- value={value !== null && value !== undefined ? String(value) : ''}
120
+ value={value !== null && value !== undefined ? String(value) : ""}
107
121
  onChange={(e) =>
108
122
  handleFilterChange(
109
123
  filter.key,
110
- e.target.value ? Number(e.target.value) : null
124
+ e.target.value ? Number(e.target.value) : null,
111
125
  )
112
126
  }
113
127
  className="input"
114
128
  />
115
129
  );
116
130
 
117
- case 'boolean': {
131
+ case "boolean": {
118
132
  const boolOptions: SelectOption[] = [
119
- { value: '', label: 'All' },
120
- { value: 'true', label: 'Yes' },
121
- { value: 'false', label: 'No' },
133
+ { value: "", label: "All" },
134
+ { value: "true", label: "Yes" },
135
+ { value: "false", label: "No" },
122
136
  ];
123
137
 
124
138
  return (
125
139
  <Select
126
140
  options={boolOptions}
127
- value={value === null || value === undefined ? '' : String(value)}
141
+ value={value === null || value === undefined ? "" : String(value)}
128
142
  onChange={(newValue) =>
129
143
  handleFilterChange(
130
144
  filter.key,
131
- newValue === '' ? null : newValue === 'true'
145
+ newValue === "" ? null : newValue === "true",
132
146
  )
133
147
  }
134
148
  />
135
149
  );
136
150
  }
137
151
 
138
- case 'search':
152
+ case "search":
139
153
  return (
140
154
  <div className="relative">
141
155
  <div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
@@ -144,22 +158,25 @@ export default function FilterBar({
144
158
  <input
145
159
  type="text"
146
160
  placeholder={filter.placeholder || `Search ${filter.label}...`}
147
- value={(value as string) || ''}
161
+ value={(value as string) || ""}
148
162
  onChange={(e) => handleFilterChange(filter.key, e.target.value)}
149
163
  className="input pl-9"
150
164
  />
151
165
  </div>
152
166
  );
153
167
 
154
- case 'dateRange': {
168
+ case "dateRange": {
155
169
  const rangeValue = (value as { from?: string; to?: string }) || {};
156
170
  return (
157
171
  <div className="flex items-center gap-2">
158
172
  <input
159
173
  type="date"
160
- value={rangeValue.from || ''}
174
+ value={rangeValue.from || ""}
161
175
  onChange={(e) =>
162
- handleFilterChange(filter.key, { ...rangeValue, from: e.target.value || undefined })
176
+ handleFilterChange(filter.key, {
177
+ ...rangeValue,
178
+ from: e.target.value || undefined,
179
+ })
163
180
  }
164
181
  className="input text-sm"
165
182
  aria-label={`${filter.label} from`}
@@ -167,9 +184,12 @@ export default function FilterBar({
167
184
  <span className="text-ink-400 text-xs">to</span>
168
185
  <input
169
186
  type="date"
170
- value={rangeValue.to || ''}
187
+ value={rangeValue.to || ""}
171
188
  onChange={(e) =>
172
- handleFilterChange(filter.key, { ...rangeValue, to: e.target.value || undefined })
189
+ handleFilterChange(filter.key, {
190
+ ...rangeValue,
191
+ to: e.target.value || undefined,
192
+ })
173
193
  }
174
194
  className="input text-sm"
175
195
  aria-label={`${filter.label} to`}
@@ -178,25 +198,34 @@ export default function FilterBar({
178
198
  );
179
199
  }
180
200
 
181
- case 'toggle': {
201
+ case "toggle": {
182
202
  const toggleOptions: SelectOption[] = [
183
- { value: '', label: 'All' },
184
- { value: 'true', label: 'Yes' },
185
- { value: 'false', label: 'No' },
203
+ { value: "", label: "All" },
204
+ { value: "true", label: "Yes" },
205
+ { value: "false", label: "No" },
186
206
  ];
187
- const currentVal = value === null || value === undefined ? '' : String(value);
207
+ const currentVal =
208
+ value === null || value === undefined ? "" : String(value);
188
209
  return (
189
- <div className="flex rounded-lg border border-paper-300 overflow-hidden" role="group">
210
+ <div
211
+ className="flex rounded-lg border border-paper-300 overflow-hidden"
212
+ role="group"
213
+ >
190
214
  {toggleOptions.map((opt) => (
191
215
  <button
192
216
  key={opt.value}
193
217
  type="button"
194
- onClick={() => handleFilterChange(filter.key, opt.value === '' ? null : opt.value === 'true')}
218
+ onClick={() =>
219
+ handleFilterChange(
220
+ filter.key,
221
+ opt.value === "" ? null : opt.value === "true",
222
+ )
223
+ }
195
224
  className={`px-3 py-1.5 text-xs font-medium transition-colors ${
196
225
  currentVal === opt.value
197
- ? 'bg-accent-500 text-white'
198
- : 'bg-white text-ink-600 hover:bg-paper-50'
199
- } ${opt.value !== '' ? 'border-l border-paper-300' : ''}`}
226
+ ? "bg-accent-500 text-white"
227
+ : "bg-white text-ink-600 hover:bg-paper-50"
228
+ } ${opt.value !== "" ? "border-l border-paper-300" : ""}`}
200
229
  >
201
230
  {opt.label}
202
231
  </button>
@@ -205,13 +234,44 @@ export default function FilterBar({
205
234
  );
206
235
  }
207
236
 
208
- case 'multiSelect': {
237
+ case "switch": {
238
+ // Single binary toggle — use when the filter is naturally on/off
239
+ // (e.g. "Mine only", "Archived"), unlike `boolean` / `toggle` which
240
+ // present an All/Yes/No tri-state. Stored value is a plain boolean.
241
+ const checked = value === true;
242
+ return (
243
+ <button
244
+ type="button"
245
+ role="switch"
246
+ aria-checked={checked}
247
+ onClick={() => handleFilterChange(filter.key, !checked)}
248
+ className={`relative inline-flex h-6 w-11 items-center rounded-full transition-colors focus:outline-none focus:ring-2 focus:ring-accent-400 focus:ring-offset-2 ${
249
+ checked ? "bg-accent-500" : "bg-paper-300"
250
+ }`}
251
+ >
252
+ <span
253
+ className={`inline-block h-4 w-4 transform rounded-full bg-white shadow transition-transform ${
254
+ checked ? "translate-x-6" : "translate-x-1"
255
+ }`}
256
+ />
257
+ <span className="sr-only">{filter.label}</span>
258
+ </button>
259
+ );
260
+ }
261
+
262
+ case "multiSelect": {
209
263
  const selectedValues = Array.isArray(value) ? (value as string[]) : [];
210
264
  const msOptions = filter.options || [];
211
265
  return (
212
266
  <div className="relative">
213
267
  <Select
214
- options={[{ value: '', label: `All ${filter.label}` }, ...msOptions.map(o => ({ value: String(o.value), label: o.label }))]}
268
+ options={[
269
+ { value: "", label: `All ${filter.label}` },
270
+ ...msOptions.map((o) => ({
271
+ value: String(o.value),
272
+ label: o.label,
273
+ })),
274
+ ]}
215
275
  value=""
216
276
  onChange={(newValue) => {
217
277
  if (!newValue) {
@@ -224,11 +284,23 @@ export default function FilterBar({
224
284
  {selectedValues.length > 0 && (
225
285
  <div className="flex flex-wrap gap-1 mt-1">
226
286
  {selectedValues.map((sv) => {
227
- const opt = msOptions.find(o => String(o.value) === sv);
287
+ const opt = msOptions.find((o) => String(o.value) === sv);
228
288
  return (
229
- <span key={sv} className="inline-flex items-center gap-1 px-2 py-0.5 text-xs bg-accent-100 text-accent-700 rounded-full">
289
+ <span
290
+ key={sv}
291
+ className="inline-flex items-center gap-1 px-2 py-0.5 text-xs bg-accent-100 text-accent-700 rounded-full"
292
+ >
230
293
  {opt?.label || sv}
231
- <button type="button" onClick={() => handleFilterChange(filter.key, selectedValues.filter(v => v !== sv))} className="hover:text-accent-900">
294
+ <button
295
+ type="button"
296
+ onClick={() =>
297
+ handleFilterChange(
298
+ filter.key,
299
+ selectedValues.filter((v) => v !== sv),
300
+ )
301
+ }
302
+ className="hover:text-accent-900"
303
+ >
232
304
  <X className="h-3 w-3" />
233
305
  </button>
234
306
  </span>
@@ -248,12 +320,17 @@ export default function FilterBar({
248
320
  if (filters.length === 0) return null;
249
321
 
250
322
  return (
251
- <div className={`bg-white bg-subtle-grain border border-paper-200 rounded-lg shadow-sm p-4 ${className}`}>
323
+ <div
324
+ className={`bg-white bg-subtle-grain border border-paper-200 rounded-lg shadow-sm p-4 ${className}`}
325
+ >
252
326
  <div className="flex items-start justify-between gap-4 flex-wrap">
253
327
  {/* Filters */}
254
328
  <div className="flex-1 flex flex-wrap gap-4">
255
329
  {filters.map((filter) => (
256
- <div key={filter.key} className="flex flex-col space-y-1 min-w-[200px]">
330
+ <div
331
+ key={filter.key}
332
+ className="flex flex-col space-y-1 min-w-[200px]"
333
+ >
257
334
  <label className="label">{filter.label}</label>
258
335
  {renderFilter(filter)}
259
336
  </div>
@@ -6,6 +6,21 @@
6
6
  @import "tailwindcss";
7
7
  @import "./theme.css";
8
8
 
9
+ /* Class-based dark mode.
10
+ *
11
+ * Without this, Tailwind v4 compiles every `dark:` utility into a
12
+ * `@media (prefers-color-scheme: dark)` block — so dark styles activate
13
+ * from the OS theme setting rather than app state. Consumers that want
14
+ * to control dark mode programmatically (e.g. a theme toggle that adds
15
+ * `<html class="dark">`) can't override the OS preference.
16
+ *
17
+ * This variant makes `dark:` resolve to a `.dark` class selector
18
+ * instead. Apps that want media-query behavior can opt back in via
19
+ * their own `@custom-variant dark (@media (prefers-color-scheme: dark));`
20
+ * override.
21
+ */
22
+ @custom-variant dark (&:where(.dark, .dark *));
23
+
9
24
  /* Content sources for class detection */
10
25
  @source "../components/**/*.{tsx,ts}";
11
26
  @source inline("bg-sky-100 bg-sky-50 bg-sky-100/50 bg-amber-100 bg-amber-50 bg-amber-100/50 bg-emerald-100 bg-emerald-50 bg-emerald-100/50 bg-pink-100 bg-pink-50 bg-pink-100/50");