@thinking.tools/teff 1.0.0 → 1.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.
package/css/form.css ADDED
@@ -0,0 +1,371 @@
1
+ @layer base {
2
+ label {
3
+ display: block;
4
+ font-size: var(--text-7);
5
+ font-weight: var(--font-medium);
6
+
7
+ &:has(input:where([type=checkbox], [type=radio])) {
8
+ display: inline-flex;
9
+ align-items: center;
10
+ gap: var(--space-2);
11
+ row-gap: 0;
12
+ font-weight: var(--font-normal);
13
+ }
14
+ }
15
+
16
+ :where(input:not([type=checkbox], [type=radio], [type=range], [type=file], [type=color]), textarea, select) {
17
+ width: 100%;
18
+ margin-block-start: var(--space-1);
19
+ padding-inline: var(--space-4);
20
+ /* Optical: lift the text 1px — lowercase x-height mass sits below the
21
+ pill's geometric center, otherwise reading 1–2px too low. */
22
+ padding-block: calc(var(--space-2) - 1px) calc(var(--space-2) + 1px);
23
+ font-size: var(--text-7);
24
+ line-height: var(--leading-normal);
25
+ background-color: var(--background);
26
+ color: var(--foreground);
27
+ border: 1px solid var(--input);
28
+ border-radius: var(--radius-field);
29
+ transition: border-color var(--transition-fast), box-shadow var(--transition-fast);
30
+
31
+ &::placeholder {
32
+ color: var(--muted-foreground);
33
+ }
34
+
35
+ &:focus {
36
+ outline: none;
37
+ border-color: var(--ring);
38
+ box-shadow: 0 0 0 3px rgb(from var(--ring) r g b / 0.35);
39
+ z-index: 1;
40
+ }
41
+
42
+ &:disabled {
43
+ background-color: var(--muted);
44
+ }
45
+
46
+ &:is([aria-invalid=true], :user-invalid) {
47
+ border-color: var(--danger);
48
+
49
+ &:focus {
50
+ box-shadow: 0 0 0 3px rgb(from var(--danger) r g b / 0.35);
51
+ }
52
+ }
53
+ }
54
+
55
+ textarea {
56
+ height: auto;
57
+ min-height: 5rem;
58
+ padding: var(--space-3) var(--space-4);
59
+ border-radius: var(--radius-large);
60
+ resize: vertical;
61
+ }
62
+
63
+ input[type=search] {
64
+ /* Strip UA search styling (Safari) so the pill renders consistently. */
65
+ appearance: none;
66
+ }
67
+
68
+ /* Non-standard, WebKit/Blink only — the unstylable cancel ×. */
69
+ input[type=search]::-webkit-search-cancel-button {
70
+ display: none;
71
+ }
72
+
73
+ input[type=file] {
74
+ width: 100%;
75
+ margin-block-start: var(--space-1);
76
+ font-size: var(--text-7);
77
+ color: var(--muted-foreground);
78
+ }
79
+
80
+ /* Styled as a button in button.css; just space it from the filename. */
81
+ input[type=file]::file-selector-button {
82
+ margin-inline-end: var(--space-3);
83
+ }
84
+
85
+ output {
86
+ font-weight: var(--font-medium);
87
+ font-variant-numeric: tabular-nums;
88
+ }
89
+
90
+ select {
91
+ appearance: none;
92
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%2371717a' stroke-width='2'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
93
+ background-repeat: no-repeat;
94
+ background-position: right var(--space-3) center;
95
+ padding-inline-end: var(--space-8);
96
+ }
97
+
98
+ input:where([type=checkbox], [type=radio]) {
99
+ appearance: none;
100
+ width: 1.125rem;
101
+ height: 1.125rem;
102
+ margin: 0;
103
+ position: relative;
104
+ background-color: var(--background);
105
+ border: 1px solid var(--input);
106
+ transition: background-color var(--transition-fast), border-color var(--transition-fast);
107
+
108
+ &:checked {
109
+ background-color: var(--primary);
110
+ border-color: var(--primary);
111
+
112
+ &::after {
113
+ content: "";
114
+ position: absolute;
115
+ inset: 0;
116
+ background-color: var(--primary-foreground);
117
+ mask-position: center;
118
+ mask-repeat: no-repeat;
119
+ mask-size: 100%;
120
+ }
121
+ }
122
+ }
123
+
124
+ input[type=checkbox] {
125
+ /* Scales with the radius presets but never turns circular. */
126
+ border-radius: calc(var(--radius-small) * 0.75);
127
+
128
+ &:checked::after {
129
+ mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='4'%3E%3Cpolyline points='20 6 9 17 4 12'/%3E%3C/svg%3E");
130
+ }
131
+
132
+ &[role=switch] {
133
+ --switch-height: calc(var(--bar-height) * 3);
134
+ --switch-inset: 2px;
135
+ --switch-thumb: calc(var(--switch-height) - var(--switch-inset) * 3);
136
+
137
+ width: calc(var(--switch-height) * 2);
138
+ height: var(--switch-height);
139
+ border-radius: var(--radius-full);
140
+ background-color: var(--input);
141
+ border-color: transparent;
142
+
143
+ &::before {
144
+ content: "";
145
+ position: absolute;
146
+ top: 50%;
147
+ left: var(--switch-inset);
148
+ transform: translateY(-50%);
149
+ width: var(--switch-thumb);
150
+ height: var(--switch-thumb);
151
+ background-color: var(--background);
152
+ border-radius: var(--radius-full);
153
+ transition: transform var(--transition);
154
+ box-shadow: 0 1px 2px rgb(0 0 0 / 0.2);
155
+ }
156
+
157
+ &:checked {
158
+ background-color: var(--primary);
159
+
160
+ &::after {
161
+ content: none;
162
+ }
163
+
164
+ &::before {
165
+ transform: translateY(-50%) translateX(var(--switch-height));
166
+ }
167
+ }
168
+ }
169
+ }
170
+
171
+ input[type=radio] {
172
+ border-radius: var(--radius-full);
173
+
174
+ &:checked::after {
175
+ mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Ccircle cx='8' cy='8' r='4' fill='currentColor'/%3E%3C/svg%3E");
176
+ }
177
+ }
178
+
179
+ :where(input:where([type=checkbox], [type=radio], [type=range]), select):not(:disabled),
180
+ label:has(input:where([type=checkbox], [type=radio]):not(:disabled)) {
181
+ cursor: pointer;
182
+ }
183
+
184
+ input[type=range] {
185
+ width: 100%;
186
+ height: var(--bar-height);
187
+ appearance: none;
188
+ background: var(--muted);
189
+ border-radius: var(--radius-full);
190
+
191
+ &::-webkit-slider-thumb {
192
+ appearance: none;
193
+ width: 1.25rem;
194
+ height: 1.25rem;
195
+ background: var(--primary);
196
+ border-radius: var(--radius-full);
197
+ transition: transform var(--transition-fast);
198
+
199
+ &:hover {
200
+ transform: scale(1.1);
201
+ }
202
+ }
203
+
204
+ &::-moz-range-thumb {
205
+ width: 1.25rem;
206
+ height: 1.25rem;
207
+ background: var(--primary);
208
+ border: none;
209
+ border-radius: var(--radius-full);
210
+ }
211
+ }
212
+
213
+ fieldset {
214
+ border: 1px solid var(--border);
215
+ border-radius: var(--radius-large);
216
+ padding: var(--space-4);
217
+ margin-block-end: var(--space-4);
218
+ }
219
+
220
+ legend {
221
+ font-size: var(--text-7);
222
+ font-weight: var(--font-medium);
223
+ padding: 0 var(--space-2);
224
+ }
225
+ }
226
+
227
+ @layer components {
228
+ /* Show/hide toggle for password inputs — the wrapper and button are
229
+ injected by password.js; this CSS is dormant without the script. */
230
+ [data-password] {
231
+ position: relative;
232
+ display: block;
233
+ margin-block-start: var(--space-1);
234
+
235
+ > input {
236
+ margin-block-start: 0;
237
+ padding-inline-end: var(--space-10);
238
+ }
239
+
240
+ > button {
241
+ position: absolute;
242
+ inset-inline-end: var(--space-1);
243
+ inset-block: 0;
244
+ margin-block: auto;
245
+ width: 2rem;
246
+ height: 2rem;
247
+ min-height: 0;
248
+ padding: 0;
249
+ display: grid;
250
+ place-items: center;
251
+ background: none;
252
+ border: none;
253
+ box-shadow: none;
254
+ border-radius: var(--radius-full);
255
+ color: var(--muted-foreground);
256
+
257
+ &:hover:not(:disabled) {
258
+ background-color: var(--accent);
259
+ color: var(--foreground);
260
+ }
261
+
262
+ /* 40px hit area on a 32px control. */
263
+ &::before {
264
+ content: "";
265
+ position: absolute;
266
+ inset: -4px;
267
+ border-radius: inherit;
268
+ }
269
+
270
+ /* Eye / eye-off cross-fade. */
271
+ & svg {
272
+ grid-area: 1 / 1;
273
+ width: 1.15rem;
274
+ height: 1.15rem;
275
+ transition:
276
+ opacity 200ms cubic-bezier(0.2, 0, 0, 1),
277
+ scale 200ms cubic-bezier(0.2, 0, 0, 1),
278
+ filter 200ms cubic-bezier(0.2, 0, 0, 1);
279
+ }
280
+
281
+ & svg.off {
282
+ opacity: 0;
283
+ scale: 0.25;
284
+ filter: blur(4px);
285
+ }
286
+
287
+ &[aria-pressed="true"] {
288
+ & svg.off {
289
+ opacity: 1;
290
+ scale: 1;
291
+ filter: blur(0);
292
+ }
293
+
294
+ & svg:not(.off) {
295
+ opacity: 0;
296
+ scale: 0.25;
297
+ filter: blur(4px);
298
+ }
299
+ }
300
+ }
301
+ }
302
+
303
+ fieldset.group {
304
+ display: flex;
305
+ align-items: stretch;
306
+ border: none;
307
+ padding: 0;
308
+ margin: 0;
309
+
310
+ & > :is(input, textarea, select) {
311
+ flex: 1;
312
+ margin-block-start: 0;
313
+
314
+ &:not(:focus):not(:last-child) {
315
+ border-inline-end-color: transparent;
316
+ }
317
+ }
318
+
319
+ & > :is(input, textarea, select, button) {
320
+ border-radius: 0;
321
+ &:first-child {
322
+ border-radius: var(--radius-field) 0 0 var(--radius-field);
323
+ }
324
+
325
+ &:last-child {
326
+ border-radius: 0 var(--radius-field) var(--radius-field) 0;
327
+ }
328
+ }
329
+
330
+ & > legend {
331
+ float: inline-start;
332
+ display: inline-flex;
333
+ align-items: center;
334
+ padding: 0 var(--space-4);
335
+ line-height: var(--leading-normal);
336
+ font-weight: var(--font-normal);
337
+ color: var(--muted-foreground);
338
+ background-color: var(--muted);
339
+ border: 1px solid var(--input);
340
+ border-inline-end: none;
341
+ border-radius: var(--radius-field) 0 0 var(--radius-field);
342
+ }
343
+ }
344
+
345
+ [data-field] {
346
+ margin-block-end: var(--space-4);
347
+ flex-wrap: wrap;
348
+
349
+ /* Hint text that succeeds an input with aria-describedby */
350
+ [data-hint], .error {
351
+ font-size: var(--text-8);
352
+ font-weight: var(--font-normal);
353
+ color: var(--muted-foreground);
354
+ margin-block-start: var(--space-1);
355
+ margin-inline-start: var(--space-4);
356
+ display: block;
357
+ flex-basis: 100%;
358
+ min-inline-size: 100%;
359
+ }
360
+
361
+ .error {
362
+ display: none;
363
+ }
364
+
365
+ /* Reveal error on data-field="error" */
366
+ &[data-field="error"] .error {
367
+ display: block;
368
+ color: var(--danger);
369
+ }
370
+ }
371
+ }
package/css/grid.css ADDED
@@ -0,0 +1,78 @@
1
+ @layer components {
2
+ :root {
3
+ --grid-cols: 12;
4
+ --grid-gap: var(--space-6);
5
+ --container-max: 1280px;
6
+ --container-pad: var(--space-4);
7
+ }
8
+
9
+ /* Auto-fit grid: as many columns as fit, each at least --autogrid-min wide. */
10
+ .autogrid {
11
+ display: grid;
12
+ grid-template-columns: repeat(auto-fill, minmax(min(var(--autogrid-min, 14rem), 100%), 1fr));
13
+ gap: var(--grid-gap);
14
+ }
15
+
16
+ .container {
17
+ width: 100%;
18
+ max-width: var(--container-max);
19
+ margin-inline: auto;
20
+ padding-inline: var(--container-pad);
21
+ }
22
+
23
+ .row {
24
+ display: grid;
25
+ grid-template-columns: repeat(var(--grid-cols), 1fr);
26
+ gap: var(--grid-gap);
27
+ width: 100%;
28
+ }
29
+
30
+ /* Column spans — only meaningful as direct children of .row. Scoped so the
31
+ generic names (.col, …) can't hijack placement in unrelated grids. */
32
+ .row > :is(.col, [class*="col-"]) { grid-column-end: span calc(var(--span, var(--grid-cols)) + var(--offset, 0)); }
33
+
34
+ .col-1 { --span: 1; }
35
+ .col-2 { --span: 2; }
36
+ .col-3 { --span: 3; }
37
+ .col-4 { --span: 4; }
38
+ .col-5 { --span: 5; }
39
+ .col-6 { --span: 6; }
40
+ .col-7 { --span: 7; }
41
+ .col-8 { --span: 8; }
42
+ .col-9 { --span: 9; }
43
+ .col-10 { --span: 10; }
44
+ .col-11 { --span: 11; }
45
+ .col-12 { --span: 12; }
46
+
47
+ /* Offsets */
48
+ .offset-1 { --offset: 1; }
49
+ .offset-2 { --offset: 2; }
50
+ .offset-3 { --offset: 3; }
51
+ .offset-4 { --offset: 4; }
52
+ .offset-5 { --offset: 5; }
53
+ .offset-6 { --offset: 6; }
54
+
55
+ .row > [class*="offset-"] {
56
+ margin-inline-start: calc(var(--offset) * (100% + var(--grid-gap)) / (var(--span) + var(--offset)));
57
+ }
58
+
59
+ .row > .col-end {
60
+ grid-column-start: span var(--span, 1);
61
+ grid-column-end: -1;
62
+ }
63
+
64
+ /* Responsive: stack on mobile */
65
+ @media (max-width: 768px) {
66
+ .row {
67
+ --grid-cols: 4;
68
+ --grid-gap: var(--space-4);
69
+ }
70
+ .row > :is(.col, [class*="col-"]) {
71
+ --span: 4;
72
+ }
73
+ .row > [class*="offset-"] {
74
+ --offset: 0;
75
+ margin-inline-start: 0;
76
+ }
77
+ }
78
+ }
package/css/nav.css ADDED
@@ -0,0 +1,118 @@
1
+ /**
2
+ * Navigation primitives.
3
+ *
4
+ * Breadcrumbs:
5
+ * <nav data-breadcrumbs aria-label="Breadcrumb">
6
+ * <ol>
7
+ * <li><a href="/">Home</a></li>
8
+ * <li><a href="/docs">Docs</a></li>
9
+ * <li aria-current="page">Buttons</li>
10
+ * </ol>
11
+ * </nav>
12
+ *
13
+ * Inline nav menu (horizontal links):
14
+ * <nav data-menu>
15
+ * <ul>
16
+ * <li><a href="#" aria-current="page">Overview</a></li>
17
+ * <li><a href="#">Reports</a></li>
18
+ * </ul>
19
+ * </nav>
20
+ */
21
+ @layer components {
22
+ nav[data-breadcrumbs] {
23
+ font-size: var(--text-7);
24
+
25
+ ol, ul {
26
+ display: flex;
27
+ flex-wrap: wrap;
28
+ align-items: center;
29
+ gap: var(--space-1);
30
+ list-style: none;
31
+ padding: 0;
32
+ margin: 0;
33
+ }
34
+
35
+ li {
36
+ display: inline-flex;
37
+ align-items: center;
38
+ gap: var(--space-1);
39
+ margin: 0;
40
+ color: var(--muted-foreground);
41
+
42
+ /* Chevron separator before every crumb but the first. */
43
+ & + li::before {
44
+ content: "";
45
+ width: 0.9em;
46
+ height: 0.9em;
47
+ flex-shrink: 0;
48
+ background-color: var(--faint-foreground);
49
+ mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2'%3E%3Cpath d='m9 18 6-6-6-6'/%3E%3C/svg%3E");
50
+ mask-size: contain;
51
+ mask-repeat: no-repeat;
52
+ }
53
+
54
+ &[aria-current] {
55
+ color: var(--foreground);
56
+ font-weight: var(--font-medium);
57
+ }
58
+ }
59
+
60
+ a {
61
+ color: var(--muted-foreground);
62
+ text-decoration: none;
63
+ border-radius: var(--radius-small);
64
+ transition: color var(--transition-fast);
65
+
66
+ &:hover {
67
+ color: var(--foreground);
68
+ }
69
+ }
70
+ }
71
+
72
+ nav[data-menu] {
73
+ ul, menu {
74
+ display: flex;
75
+ flex-wrap: wrap;
76
+ align-items: center;
77
+ gap: var(--space-1);
78
+ list-style: none;
79
+ padding: 0;
80
+ margin: 0;
81
+ }
82
+
83
+ li {
84
+ margin: 0;
85
+ }
86
+
87
+ a {
88
+ display: inline-flex;
89
+ align-items: center;
90
+ gap: var(--space-2);
91
+ min-height: 2.25rem;
92
+ padding: var(--space-1) var(--space-4);
93
+ font-size: var(--text-7);
94
+ color: var(--muted-foreground);
95
+ text-decoration: none;
96
+ border-radius: var(--radius-button);
97
+ transition: background-color var(--transition-fast), color var(--transition-fast);
98
+
99
+ &:hover {
100
+ background-color: var(--accent);
101
+ color: var(--foreground);
102
+ }
103
+
104
+ &[aria-current] {
105
+ background-color: var(--accent);
106
+ color: var(--foreground);
107
+ font-weight: var(--font-medium);
108
+ }
109
+
110
+ & svg {
111
+ display: block;
112
+ width: 1.15em;
113
+ height: 1.15em;
114
+ flex-shrink: 0;
115
+ }
116
+ }
117
+ }
118
+ }
@@ -0,0 +1,75 @@
1
+ @layer base {
2
+ progress {
3
+ appearance: none;
4
+ width: 100%;
5
+ height: var(--bar-height);
6
+ border: none;
7
+ border-radius: var(--radius-full);
8
+ overflow: hidden;
9
+ background-color: var(--muted);
10
+
11
+ &::-webkit-progress-bar {
12
+ background-color: var(--muted);
13
+ border-radius: var(--radius-full);
14
+ }
15
+
16
+ &::-webkit-progress-value {
17
+ background-color: var(--primary);
18
+ border-radius: var(--radius-full);
19
+ transition: width var(--transition);
20
+ }
21
+
22
+ &::-moz-progress-bar {
23
+ background-color: var(--primary);
24
+ border-radius: var(--radius-full);
25
+ }
26
+ }
27
+
28
+ meter {
29
+ appearance: none;
30
+ width: 100%;
31
+ height: var(--bar-height);
32
+ border: none;
33
+ border-radius: var(--radius-full);
34
+ overflow: hidden;
35
+ background: var(--muted);
36
+
37
+ &::-webkit-meter-bar {
38
+ background: var(--muted);
39
+ border: none;
40
+ border-radius: var(--radius-full);
41
+ height: var(--bar-height);
42
+ }
43
+
44
+ &::-webkit-meter-optimum-value,
45
+ &::-webkit-meter-suboptimum-value,
46
+ &::-webkit-meter-even-less-good-value {
47
+ border-radius: var(--radius-full);
48
+ }
49
+
50
+ &::-webkit-meter-optimum-value {
51
+ background: var(--success);
52
+ }
53
+
54
+ &::-webkit-meter-suboptimum-value {
55
+ background: var(--warning);
56
+ }
57
+
58
+ &::-webkit-meter-even-less-good-value {
59
+ background: var(--danger);
60
+ }
61
+
62
+ &::-moz-meter-bar {
63
+ background: var(--success);
64
+ border-radius: var(--radius-full);
65
+ }
66
+
67
+ &:-moz-meter-sub-optimum::-moz-meter-bar {
68
+ background: var(--warning);
69
+ }
70
+
71
+ &:-moz-meter-sub-sub-optimum::-moz-meter-bar {
72
+ background: var(--danger);
73
+ }
74
+ }
75
+ }