@crewlethq/ui 0.2.0

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.
@@ -0,0 +1,312 @@
1
+ /*
2
+ * DateTimePicker, a themed replacement for <input type="datetime-local">.
3
+ * Trigger borrows the Select trigger's geometry so a DateTimePicker
4
+ * and a Select line up in the same toolbar; the popover paints its
5
+ * own calendar grid + (optional) hour / minute inputs.
6
+ */
7
+
8
+ /* The accent variables are declared on the trigger AND on the calendar
9
+ popover. The popover is portaled to <body>, so it inherits nothing
10
+ from the trigger; declaring them on the trigger alone leaves the
11
+ selected day, the focused time input and the primary footer button
12
+ resolving an undefined variable and painting transparent. Both read
13
+ the brand accent tokens, so an app retints the picker by rebinding
14
+ --color-brand-accent and --color-brand-accent-rgb on <body>. */
15
+ .crewlet-datetime,
16
+ .crewlet-datetime__popover {
17
+ --crewlet-datetime-accent: var(--color-brand-accent, #5469d4);
18
+ --crewlet-datetime-accent-rgb: var(--color-brand-accent-rgb, 84, 105, 212);
19
+ }
20
+
21
+ .crewlet-datetime {
22
+ width: 100%;
23
+ display: inline-flex;
24
+ align-items: center;
25
+ gap: 8px;
26
+ padding: 8px 10px 8px 12px;
27
+ background: var(--color-surface-elevated, #1f2128);
28
+ border: 1px solid var(--color-border-default, rgba(255, 255, 255, 0.1));
29
+ border-radius: 8px;
30
+ color: var(--color-text-primary, #f5f5f7);
31
+ font-family: inherit;
32
+ font-size: 13px;
33
+ text-align: left;
34
+ cursor: pointer;
35
+ transition: border-color 0.15s ease, box-shadow 0.15s ease;
36
+ }
37
+
38
+ .crewlet-datetime--sm {
39
+ padding: 6px 8px 6px 10px;
40
+ font-size: 12px;
41
+ }
42
+
43
+ .crewlet-datetime:hover:not(:disabled) {
44
+ border-color: var(--color-border-strong, rgba(255, 255, 255, 0.18));
45
+ }
46
+
47
+ .crewlet-datetime:focus,
48
+ .crewlet-datetime.is-open {
49
+ outline: none;
50
+ border-color: var(--crewlet-datetime-accent);
51
+ box-shadow: 0 0 0 3px rgba(var(--crewlet-datetime-accent-rgb), 0.15);
52
+ }
53
+
54
+ .crewlet-datetime.is-disabled,
55
+ .crewlet-datetime:disabled {
56
+ opacity: 0.55;
57
+ cursor: not-allowed;
58
+ }
59
+
60
+ .crewlet-datetime__icon {
61
+ font-size: 16px;
62
+ color: var(--color-text-secondary, #c9ced8);
63
+ flex-shrink: 0;
64
+ }
65
+
66
+ .crewlet-datetime__label {
67
+ flex: 1;
68
+ min-width: 0;
69
+ overflow: hidden;
70
+ text-overflow: ellipsis;
71
+ white-space: nowrap;
72
+ }
73
+
74
+ .crewlet-datetime__label.is-placeholder {
75
+ color: var(--color-text-tertiary, #8c99ad);
76
+ }
77
+
78
+ .crewlet-datetime__chevron {
79
+ font-size: 18px;
80
+ color: var(--color-text-tertiary, #8c99ad);
81
+ flex-shrink: 0;
82
+ }
83
+
84
+ /* ─── Popover panel ─────────────────────────────────────────── */
85
+ /*
86
+ * The base .crewlet-popover rule caps height at min(360px, 60vh) and
87
+ * scrolls overflow. The picker's content (quick-pick row + nav +
88
+ * weekday strip + 6-row grid + time inputs + footer) is taller than
89
+ * that, which made the time row sit below the scroll fold. Override
90
+ * max-height/max-width to none and disable inner scroll so every
91
+ * section is visible without scrolling.
92
+ */
93
+ /*
94
+ * Popover matches the trigger width via Popover width="match" so the
95
+ * calendar lines up edge-for-edge with the input that opened it. The
96
+ * min-width floor keeps the calendar grid usable when the trigger
97
+ * sits inside a narrow cell (e.g. a form row); the explicit overrides
98
+ * lift the base Popover's max-height / max-width / overflow so the
99
+ * calendar, time row, and footer render in full.
100
+ */
101
+ .crewlet-datetime__popover {
102
+ padding: 0;
103
+ background: var(--color-surface-subtle, var(--color-surface-elevated, #1c1e24));
104
+ border: 1px solid var(--color-border-default, rgba(255, 255, 255, 0.1));
105
+ border-radius: 10px;
106
+ box-shadow: var(--shadow-lg, 0 12px 28px rgba(0, 0, 0, 0.45));
107
+ overflow: visible;
108
+ min-width: 320px;
109
+ max-width: none;
110
+ max-height: none;
111
+ }
112
+
113
+ .crewlet-datetime__panel {
114
+ display: flex;
115
+ flex-direction: column;
116
+ padding: 12px;
117
+ gap: 8px;
118
+ }
119
+
120
+
121
+ /* ─── Month nav header ──────────────────────────────────────── */
122
+ .crewlet-datetime__nav {
123
+ display: flex;
124
+ align-items: center;
125
+ gap: 4px;
126
+ }
127
+
128
+ .crewlet-datetime__nav-btn {
129
+ background: transparent;
130
+ border: 0;
131
+ color: var(--color-text-secondary, #c9ced8);
132
+ width: 28px;
133
+ height: 28px;
134
+ border-radius: 6px;
135
+ display: inline-flex;
136
+ align-items: center;
137
+ justify-content: center;
138
+ cursor: pointer;
139
+ transition: background 0.12s ease, color 0.12s ease;
140
+ }
141
+
142
+ .crewlet-datetime__nav-btn:hover {
143
+ background: var(--color-surface-elevated, rgba(255, 255, 255, 0.08));
144
+ color: var(--color-text-primary, #fafafa);
145
+ }
146
+
147
+ .crewlet-datetime__nav-btn .material-symbols-outlined {
148
+ font-size: 18px;
149
+ }
150
+
151
+ .crewlet-datetime__title {
152
+ flex: 1;
153
+ text-align: center;
154
+ font-size: 13px;
155
+ font-weight: 600;
156
+ color: var(--color-text-primary, #fafafa);
157
+ }
158
+
159
+ /* ─── Weekday header strip ──────────────────────────────────── */
160
+ .crewlet-datetime__weekdays {
161
+ display: grid;
162
+ grid-template-columns: repeat(7, 1fr);
163
+ gap: 2px;
164
+ padding: 0 2px;
165
+ }
166
+
167
+ .crewlet-datetime__weekday {
168
+ text-align: center;
169
+ font-size: 11px;
170
+ font-weight: 600;
171
+ color: var(--color-text-tertiary, #8c99ad);
172
+ text-transform: uppercase;
173
+ letter-spacing: 0.04em;
174
+ padding: 4px 0;
175
+ }
176
+
177
+ /* ─── Calendar grid ─────────────────────────────────────────── */
178
+ .crewlet-datetime__grid {
179
+ display: grid;
180
+ grid-template-columns: repeat(7, 1fr);
181
+ gap: 2px;
182
+ }
183
+
184
+ .crewlet-datetime__cell {
185
+ height: 32px;
186
+ background: transparent;
187
+ border: 0;
188
+ border-radius: 6px;
189
+ color: var(--color-text-primary, #fafafa);
190
+ font: inherit;
191
+ font-size: 12px;
192
+ cursor: pointer;
193
+ transition: background 0.1s ease, color 0.1s ease, border-color 0.1s ease;
194
+ display: inline-flex;
195
+ align-items: center;
196
+ justify-content: center;
197
+ }
198
+
199
+ .crewlet-datetime__cell:hover:not(:disabled):not(.is-selected) {
200
+ background: var(--color-surface-elevated, rgba(255, 255, 255, 0.08));
201
+ }
202
+
203
+ .crewlet-datetime__cell.is-outside {
204
+ color: var(--color-text-tertiary, #8c99ad);
205
+ opacity: 0.55;
206
+ }
207
+
208
+ .crewlet-datetime__cell.is-today:not(.is-selected) {
209
+ /* Subtle outline so today's cell is always findable even when no
210
+ date is picked. The accent inset keeps it visible on every surface. */
211
+ box-shadow: inset 0 0 0 1px rgba(var(--crewlet-datetime-accent-rgb), 0.55);
212
+ }
213
+
214
+ .crewlet-datetime__cell.is-selected {
215
+ background: var(--crewlet-datetime-accent);
216
+ color: #ffffff;
217
+ font-weight: 600;
218
+ }
219
+
220
+ .crewlet-datetime__cell.is-disabled,
221
+ .crewlet-datetime__cell:disabled {
222
+ opacity: 0.3;
223
+ cursor: not-allowed;
224
+ }
225
+
226
+ /* ─── Time row ──────────────────────────────────────────────── */
227
+ .crewlet-datetime__time {
228
+ display: flex;
229
+ align-items: center;
230
+ gap: 6px;
231
+ padding: 8px 4px 0;
232
+ border-top: 1px solid var(--color-border-default, rgba(255, 255, 255, 0.06));
233
+ margin-top: 4px;
234
+ }
235
+
236
+ .crewlet-datetime__time-label {
237
+ font-size: 11px;
238
+ font-weight: 600;
239
+ text-transform: uppercase;
240
+ letter-spacing: 0.04em;
241
+ color: var(--color-text-tertiary, #8c99ad);
242
+ margin-right: auto;
243
+ }
244
+
245
+ /*
246
+ * Hour / minute / second inputs render as type="text" so leading
247
+ * zeros stay visible ("00 : 00 : 00" instead of "0 : 0 : 0"). The
248
+ * inputMode + pattern attrs keep mobile keyboards numeric.
249
+ */
250
+ .crewlet-datetime__time-input {
251
+ width: 36px;
252
+ background: var(--color-surface-elevated, #1f2128);
253
+ border: 1px solid var(--color-border-default, rgba(255, 255, 255, 0.1));
254
+ border-radius: 6px;
255
+ padding: 4px 6px;
256
+ font: inherit;
257
+ font-size: 13px;
258
+ font-variant-numeric: tabular-nums;
259
+ color: var(--color-text-primary, #fafafa);
260
+ text-align: center;
261
+ }
262
+
263
+ .crewlet-datetime__time-input:focus {
264
+ outline: none;
265
+ border-color: var(--crewlet-datetime-accent);
266
+ box-shadow: 0 0 0 2px rgba(var(--crewlet-datetime-accent-rgb), 0.18);
267
+ }
268
+
269
+ .crewlet-datetime__time-sep {
270
+ color: var(--color-text-tertiary, #8c99ad);
271
+ }
272
+
273
+ /* ─── Footer ────────────────────────────────────────────────── */
274
+ .crewlet-datetime__footer {
275
+ display: flex;
276
+ align-items: center;
277
+ gap: 4px;
278
+ padding-top: 8px;
279
+ border-top: 1px solid var(--color-border-default, rgba(255, 255, 255, 0.06));
280
+ }
281
+
282
+ .crewlet-datetime__footer-spacer {
283
+ flex: 1;
284
+ }
285
+
286
+ .crewlet-datetime__footer-btn {
287
+ background: transparent;
288
+ border: 0;
289
+ font: inherit;
290
+ font-size: 12px;
291
+ color: var(--color-text-secondary, #c9ced8);
292
+ cursor: pointer;
293
+ padding: 6px 10px;
294
+ border-radius: 6px;
295
+ transition: background 0.12s ease, color 0.12s ease;
296
+ }
297
+
298
+ .crewlet-datetime__footer-btn:hover {
299
+ background: var(--color-surface-elevated, rgba(255, 255, 255, 0.08));
300
+ color: var(--color-text-primary, #fafafa);
301
+ }
302
+
303
+ .crewlet-datetime__footer-btn.is-primary {
304
+ background: var(--crewlet-datetime-accent);
305
+ color: #ffffff;
306
+ font-weight: 600;
307
+ }
308
+
309
+ .crewlet-datetime__footer-btn.is-primary:hover {
310
+ background: var(--crewlet-datetime-accent);
311
+ filter: brightness(1.08);
312
+ }
@@ -0,0 +1,30 @@
1
+ /*
2
+ * Eyebrow (kicker), the uppercase section label. Monospace, tracked-out,
3
+ * and quiet so it names a section without competing with its title.
4
+ */
5
+
6
+ .crewlet-eyebrow {
7
+ display: inline-block;
8
+ font-family: var(--font-family-mono);
9
+ font-size: var(--font-size-xs);
10
+ font-weight: var(--font-weight-semibold);
11
+ letter-spacing: 0.14em;
12
+ text-transform: uppercase;
13
+ line-height: var(--font-line-height-tight);
14
+ }
15
+
16
+ .crewlet-eyebrow--muted {
17
+ color: var(--color-text-tertiary);
18
+ }
19
+
20
+ .crewlet-eyebrow--accent {
21
+ color: var(--color-brand-accent);
22
+ }
23
+
24
+ .crewlet-eyebrow--gradient {
25
+ background: var(--color-brand-gradient);
26
+ -webkit-background-clip: text;
27
+ background-clip: text;
28
+ -webkit-text-fill-color: transparent;
29
+ color: transparent;
30
+ }
@@ -0,0 +1,82 @@
1
+ /*
2
+ * IconButton, square icon-only affordance.
3
+ *
4
+ * Two square sizes: 22x22 fits inline beside a form label, and 28x28
5
+ * matches the min-height of a small Button. Variant accents use the brand accent (lavender) and the
6
+ * feedback-danger (rose) tokens.
7
+ */
8
+
9
+ .crewlet-icon-btn {
10
+ display: inline-flex;
11
+ align-items: center;
12
+ justify-content: center;
13
+ padding: 0;
14
+ background: transparent;
15
+ border: 1px solid transparent;
16
+ border-radius: 6px;
17
+ cursor: pointer;
18
+ line-height: 1;
19
+ flex-shrink: 0;
20
+ transition: background 0.15s ease, border-color 0.15s ease, color 0.15s ease;
21
+ }
22
+
23
+ .crewlet-icon-btn:focus-visible {
24
+ outline: none;
25
+ box-shadow: var(--shadow-focus, 0 0 0 3px rgba(var(--color-brand-accent-rgb, 84, 105, 212), 0.32));
26
+ }
27
+
28
+ .crewlet-icon-btn:disabled {
29
+ opacity: 0.35;
30
+ cursor: not-allowed;
31
+ pointer-events: none;
32
+ }
33
+
34
+ /* ── Sizes ──────────────────────────────────────────────────────── */
35
+ .crewlet-icon-btn--sm { width: 22px; height: 22px; }
36
+ .crewlet-icon-btn--md { width: 28px; height: 28px; }
37
+
38
+ /* The icon glyph inside scales with the button size. */
39
+ .crewlet-icon-btn--sm .material-symbols-outlined { font-size: 14px; }
40
+ .crewlet-icon-btn--md .material-symbols-outlined { font-size: 18px; }
41
+
42
+ /* ── ghost: transparent surface, neutral text, mild tint on hover ── */
43
+ .crewlet-icon-btn--ghost {
44
+ color: var(--color-text-secondary);
45
+ }
46
+ .crewlet-icon-btn--ghost:hover:not(:disabled) {
47
+ background: var(--color-surface-elevated, var(--color-surface-subtle));
48
+ color: var(--color-text-primary);
49
+ }
50
+
51
+ /* ── ghost-brand: transparent → lavender-soft on hover ─────────── */
52
+ .crewlet-icon-btn--ghost-brand {
53
+ color: var(--color-text-tertiary);
54
+ }
55
+ .crewlet-icon-btn--ghost-brand:hover:not(:disabled) {
56
+ background: var(--color-brand-accent-soft, rgba(84, 105, 212, 0.10));
57
+ border-color: var(--color-brand-accent-soft-strong, rgba(84, 105, 212, 0.32));
58
+ color: var(--color-brand-accent, #5469d4);
59
+ }
60
+
61
+ /* ── ghost-danger: transparent → rose-soft on hover ─────────────── */
62
+ .crewlet-icon-btn--ghost-danger {
63
+ color: var(--color-text-tertiary);
64
+ }
65
+ .crewlet-icon-btn--ghost-danger:hover:not(:disabled) {
66
+ background: rgba(244, 63, 94, 0.10);
67
+ color: var(--color-feedback-danger, #f43f5e);
68
+ }
69
+
70
+ /*
71
+ * ── soft-brand: lavender-tinted square with matching outline. The
72
+ * add affordance beside a form label (for example "Add a policy").
73
+ */
74
+ .crewlet-icon-btn--soft-brand {
75
+ background: rgba(var(--color-brand-accent-rgb, 84, 105, 212), 0.12);
76
+ border-color: rgba(var(--color-brand-accent-rgb, 84, 105, 212), 0.3);
77
+ color: var(--color-brand-accent, #5469d4);
78
+ }
79
+ .crewlet-icon-btn--soft-brand:hover:not(:disabled) {
80
+ background: rgba(var(--color-brand-accent-rgb, 84, 105, 212), 0.22);
81
+ border-color: var(--color-brand-accent, #5469d4);
82
+ }
@@ -0,0 +1,158 @@
1
+ /*
2
+ * ImageUpload, click-to-upload affordance built on Avatar.
3
+ *
4
+ * Colour properties go through the @crewlethq/tokens variables, with literal
5
+ * fallbacks so the control still renders when the token stylesheet has not
6
+ * loaded.
7
+ */
8
+
9
+ .crewlet-image-upload {
10
+ position: relative;
11
+ display: inline-flex;
12
+ flex-shrink: 0;
13
+ width: var(--crewlet-image-upload-size, 64px);
14
+ height: var(--crewlet-image-upload-size, 64px);
15
+ }
16
+
17
+ .crewlet-image-upload__trigger {
18
+ position: relative;
19
+ display: inline-flex;
20
+ width: 100%;
21
+ height: 100%;
22
+ padding: 0;
23
+ margin: 0;
24
+ border: none;
25
+ background: transparent;
26
+ cursor: pointer;
27
+ }
28
+
29
+ /* Shape modifiers keep the trigger, overlay, and Avatar corners in sync. */
30
+ .crewlet-image-upload--circle .crewlet-image-upload__trigger,
31
+ .crewlet-image-upload--circle .crewlet-image-upload__overlay {
32
+ border-radius: var(--radius-circle, 50%);
33
+ }
34
+
35
+ .crewlet-image-upload--square .crewlet-image-upload__trigger,
36
+ .crewlet-image-upload--square .crewlet-image-upload__overlay {
37
+ border-radius: var(--radius-lg, 12px);
38
+ }
39
+
40
+ .crewlet-image-upload__trigger:disabled {
41
+ cursor: default;
42
+ }
43
+
44
+ .crewlet-image-upload.is-uploading .crewlet-image-upload__trigger {
45
+ cursor: progress;
46
+ }
47
+
48
+ .crewlet-image-upload__trigger:focus-visible {
49
+ outline: none;
50
+ box-shadow: var(--shadow-focus, 0 0 0 3px rgba(84, 105, 212, 0.32));
51
+ }
52
+
53
+ .crewlet-image-upload__overlay {
54
+ position: absolute;
55
+ inset: 0;
56
+ display: flex;
57
+ flex-direction: column;
58
+ align-items: center;
59
+ justify-content: center;
60
+ gap: 2px;
61
+ background: rgba(0, 0, 0, 0.5);
62
+ color: #ffffff;
63
+ opacity: 0;
64
+ transition: opacity 0.2s ease, background 0.15s ease;
65
+ pointer-events: none;
66
+ }
67
+
68
+ .crewlet-image-upload__trigger:hover .crewlet-image-upload__overlay,
69
+ .crewlet-image-upload__trigger:focus-visible .crewlet-image-upload__overlay {
70
+ opacity: 1;
71
+ }
72
+
73
+ .crewlet-image-upload.is-uploading .crewlet-image-upload__overlay,
74
+ .crewlet-image-upload.is-empty .crewlet-image-upload__overlay {
75
+ opacity: 1;
76
+ }
77
+
78
+ .crewlet-image-upload__overlay .material-symbols-outlined {
79
+ font-size: calc(var(--crewlet-image-upload-size, 64px) * 0.28);
80
+ color: #ffffff;
81
+ transition: color 0.15s ease;
82
+ }
83
+
84
+ .crewlet-image-upload__overlay-text {
85
+ font-size: 10px;
86
+ font-weight: 500;
87
+ color: rgba(255, 255, 255, 0.85);
88
+ transition: color 0.15s ease;
89
+ }
90
+
91
+ /* Hovering an editable control brightens the overlay: a deeper scrim with the
92
+ label and icon at full white, so a control whose overlay is already showing
93
+ (nothing uploaded yet) still answers the pointer. */
94
+ .crewlet-image-upload__trigger:hover:not(:disabled) .crewlet-image-upload__overlay {
95
+ background: rgba(0, 0, 0, 0.66);
96
+ }
97
+
98
+ .crewlet-image-upload__trigger:hover:not(:disabled) .crewlet-image-upload__overlay .material-symbols-outlined,
99
+ .crewlet-image-upload__trigger:hover:not(:disabled) .crewlet-image-upload__overlay-text {
100
+ color: #ffffff;
101
+ }
102
+
103
+ .crewlet-image-upload__spinner {
104
+ width: calc(var(--crewlet-image-upload-size, 64px) * 0.3);
105
+ height: calc(var(--crewlet-image-upload-size, 64px) * 0.3);
106
+ border-radius: 50%;
107
+ border: 2px solid rgba(255, 255, 255, 0.35);
108
+ border-top-color: #ffffff;
109
+ animation: crewlet-image-upload-spin 0.7s linear infinite;
110
+ }
111
+
112
+ @keyframes crewlet-image-upload-spin {
113
+ to {
114
+ transform: rotate(360deg);
115
+ }
116
+ }
117
+
118
+ .crewlet-image-upload__remove {
119
+ position: absolute;
120
+ top: -6px;
121
+ right: -6px;
122
+ z-index: 2;
123
+ width: 20px;
124
+ height: 20px;
125
+ padding: 0;
126
+ display: flex;
127
+ align-items: center;
128
+ justify-content: center;
129
+ border-radius: 50%;
130
+ background: var(--color-feedback-danger, #f43f5e);
131
+ border: 2px solid var(--color-surface-background, #15171c);
132
+ color: #ffffff;
133
+ cursor: pointer;
134
+ opacity: 0;
135
+ transition: opacity 0.15s ease;
136
+ }
137
+
138
+ .crewlet-image-upload:hover .crewlet-image-upload__remove,
139
+ .crewlet-image-upload__remove:focus-visible {
140
+ opacity: 1;
141
+ }
142
+
143
+ .crewlet-image-upload__remove:disabled {
144
+ cursor: default;
145
+ }
146
+
147
+ .crewlet-image-upload__remove:focus-visible {
148
+ outline: none;
149
+ box-shadow: var(--shadow-focus, 0 0 0 3px rgba(84, 105, 212, 0.32));
150
+ }
151
+
152
+ .crewlet-image-upload__remove .material-symbols-outlined {
153
+ font-size: 12px;
154
+ }
155
+
156
+ .crewlet-image-upload__input {
157
+ display: none;
158
+ }