@knadh/oat 0.1.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.
package/css/dialog.css ADDED
@@ -0,0 +1,89 @@
1
+ @layer components {
2
+ dialog {
3
+ position: fixed;
4
+ inset: 0;
5
+ z-index: var(--z-modal);
6
+ width: 100%;
7
+ max-width: 32rem;
8
+ max-height: 85vh;
9
+ margin: auto;
10
+ padding: 0;
11
+ background-color: var(--card);
12
+ border: 1px solid var(--border);
13
+ border-radius: var(--radius-large);
14
+ box-shadow: var(--shadow-large);
15
+ overflow: hidden;
16
+
17
+ opacity: 0;
18
+ transform: scale(0.95);
19
+ transition:
20
+ opacity 150ms ease,
21
+ transform 150ms ease,
22
+ overlay 150ms ease allow-discrete,
23
+ display 150ms ease allow-discrete;
24
+
25
+ &[open] {
26
+ opacity: 1;
27
+ transform: scale(1);
28
+ }
29
+
30
+ @starting-style {
31
+ &[open] {
32
+ opacity: 0;
33
+ transform: scale(0.95);
34
+ }
35
+ }
36
+
37
+ &::backdrop {
38
+ background-color: rgb(0 0 0 / 0);
39
+ transition:
40
+ background-color 150ms ease,
41
+ overlay 150ms ease allow-discrete,
42
+ display 150ms ease allow-discrete;
43
+ }
44
+
45
+ &[open]::backdrop {
46
+ background-color: rgb(0 0 0 / 0.5);
47
+ }
48
+
49
+ @starting-style {
50
+ &[open]::backdrop {
51
+ background-color: rgb(0 0 0 / 0);
52
+ }
53
+ }
54
+
55
+ & > header,
56
+ & > form > header {
57
+ display: flex;
58
+ flex-direction: column;
59
+ gap: var(--space-1);
60
+ padding: var(--space-6);
61
+ padding-bottom: 0;
62
+
63
+ & > h1, & > h2, & > h3, & > h4, & > h5, & > h6 {
64
+ margin-bottom: 0;
65
+ }
66
+
67
+ & > p {
68
+ font-size: var(--text-7);
69
+ color: var(--muted-foreground);
70
+ margin-bottom: 0;
71
+ }
72
+ }
73
+
74
+ & > p, & > div, & > section,
75
+ & > form > p, & > form > div, & > form > section {
76
+ padding: var(--space-6);
77
+ overflow-y: auto;
78
+ }
79
+
80
+ & > footer,
81
+ & > form > footer {
82
+ display: flex;
83
+ justify-content: flex-end;
84
+ gap: var(--space-2);
85
+ padding: var(--space-6);
86
+ padding-top: 0;
87
+ }
88
+ }
89
+ }
@@ -0,0 +1,56 @@
1
+ @layer components {
2
+ menu[popover] {
3
+ position: fixed;
4
+ margin: 0;
5
+ padding: var(--space-1);
6
+ min-width: 12rem;
7
+ background-color: var(--background);
8
+ border: 1px solid var(--border);
9
+ border-radius: var(--radius-medium);
10
+ box-shadow: var(--shadow-medium);
11
+ opacity: 0;
12
+ transform: translateY(-4px);
13
+ transition:
14
+ opacity 150ms ease-out,
15
+ transform 150ms ease-out,
16
+ display 150ms allow-discrete,
17
+ overlay 150ms allow-discrete;
18
+
19
+ &:popover-open {
20
+ opacity: 1;
21
+ transform: translateY(0);
22
+ }
23
+
24
+ @starting-style {
25
+ &:popover-open {
26
+ opacity: 0;
27
+ transform: translateY(-4px);
28
+ }
29
+ }
30
+
31
+ hr {
32
+ margin: var(--space-1) 0;
33
+ }
34
+ }
35
+
36
+ [role="menuitem"] {
37
+ display: flex;
38
+ align-items: center;
39
+ justify-content: left;
40
+ gap: var(--space-2);
41
+ width: 100%;
42
+ padding: var(--space-2) var(--space-3);
43
+ font-size: var(--text-7);
44
+ text-align: left;
45
+ color: var(--foreground);
46
+ background: none;
47
+ border: none;
48
+ border-radius: var(--radius-small);
49
+ cursor: pointer;
50
+
51
+ &:hover, &:focus {
52
+ background-color: var(--accent);
53
+ outline: none;
54
+ }
55
+ }
56
+ }
package/css/form.css ADDED
@@ -0,0 +1,244 @@
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
+ font-weight: var(--font-normal);
12
+ }
13
+ }
14
+
15
+ :where(input:not([type=checkbox], [type=radio], [type=range], [type=file], [type=color]), textarea, select) {
16
+ width: 100%;
17
+ margin-top: var(--space-1);
18
+ padding: var(--space-2) var(--space-3);
19
+ font-size: var(--text-7);
20
+ line-height: var(--leading-normal);
21
+ background-color: var(--background);
22
+ color: var(--foreground);
23
+ border: 1px solid var(--input);
24
+ border-radius: var(--radius-medium);
25
+ transition: border-color var(--transition-fast), box-shadow var(--transition-fast);
26
+
27
+ &::placeholder {
28
+ color: var(--muted-foreground);
29
+ }
30
+
31
+ &:focus {
32
+ outline: none;
33
+ border-color: var(--ring);
34
+ box-shadow: 0 0 0 2px rgb(from var(--ring) r g b / 0.2);
35
+ }
36
+
37
+ &:disabled {
38
+ background-color: var(--muted);
39
+ }
40
+
41
+ &:is([aria-invalid=true], :user-invalid) {
42
+ border-color: var(--danger);
43
+
44
+ &:focus {
45
+ box-shadow: 0 0 0 2px rgb(from var(--danger) r g b / 0.2);
46
+ }
47
+ }
48
+ }
49
+
50
+ textarea {
51
+ height: auto;
52
+ min-height: 5rem;
53
+ padding: var(--space-3);
54
+ resize: vertical;
55
+ }
56
+
57
+ select {
58
+ appearance: none;
59
+ 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");
60
+ background-repeat: no-repeat;
61
+ background-position: right var(--space-2) center;
62
+ padding-right: var(--space-6);
63
+ }
64
+
65
+ input:where([type=checkbox], [type=radio]) {
66
+ appearance: none;
67
+ width: 1rem;
68
+ height: 1rem;
69
+ margin: 0;
70
+ background-color: var(--background);
71
+ border: 1px solid var(--input);
72
+ transition: background-color var(--transition-fast), border-color var(--transition-fast);
73
+
74
+ &:checked {
75
+ background-color: var(--primary);
76
+ border-color: var(--primary);
77
+ background-position: center;
78
+ background-repeat: no-repeat;
79
+ }
80
+ }
81
+
82
+ input[type=checkbox] {
83
+ border-radius: var(--radius-small);
84
+
85
+ &:checked {
86
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='3'%3E%3Cpolyline points='20 6 9 17 4 12'/%3E%3C/svg%3E");
87
+ background-size: 0.75rem;
88
+ }
89
+
90
+ &[role=switch] {
91
+ --switch-height: calc(var(--bar-height) * 3);
92
+ --switch-inset: 2px;
93
+ --switch-thumb: calc(var(--switch-height) - var(--switch-inset) * 3);
94
+
95
+ width: calc(var(--switch-height) * 2);
96
+ height: var(--switch-height);
97
+ border-radius: var(--radius-full);
98
+ background-color: var(--input);
99
+ position: relative;
100
+
101
+ &::before {
102
+ content: "";
103
+ position: absolute;
104
+ top: 50%;
105
+ left: var(--switch-inset);
106
+ transform: translateY(-50%);
107
+ width: var(--switch-thumb);
108
+ height: var(--switch-thumb);
109
+ background-color: var(--background);
110
+ border-radius: var(--radius-full);
111
+ transition: transform var(--transition);
112
+ box-shadow: var(--shadow-small);
113
+ }
114
+
115
+ &:checked {
116
+ background-color: var(--primary);
117
+ background-image: none;
118
+
119
+ &::before {
120
+ transform: translateY(-50%) translateX(var(--switch-height));
121
+ }
122
+ }
123
+ }
124
+ }
125
+
126
+ input[type=radio] {
127
+ border-radius: var(--radius-full);
128
+
129
+ &:checked {
130
+ background-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='white'/%3E%3C/svg%3E");
131
+ background-size: 100%;
132
+ }
133
+ }
134
+
135
+ :where(input:where([type=checkbox], [type=radio], [type=range]), select):not(:disabled),
136
+ label:has(input:where([type=checkbox], [type=radio]):not(:disabled)) {
137
+ cursor: pointer;
138
+ }
139
+
140
+ input[type=range] {
141
+ width: 100%;
142
+ height: var(--bar-height);
143
+ appearance: none;
144
+ background: var(--muted);
145
+ border-radius: var(--radius-full);
146
+
147
+ &::-webkit-slider-thumb {
148
+ appearance: none;
149
+ width: 1.25rem;
150
+ height: 1.25rem;
151
+ background: var(--primary);
152
+ border-radius: var(--radius-full);
153
+ transition: transform var(--transition-fast);
154
+
155
+ &:hover {
156
+ transform: scale(1.1);
157
+ }
158
+ }
159
+
160
+ &::-moz-range-thumb {
161
+ width: 1.25rem;
162
+ height: 1.25rem;
163
+ background: var(--primary);
164
+ border: none;
165
+ border-radius: var(--radius-full);
166
+ }
167
+ }
168
+
169
+ fieldset {
170
+ border: 1px solid var(--border);
171
+ border-radius: var(--radius-medium);
172
+ padding: var(--space-4);
173
+ margin-bottom: var(--space-4);
174
+ }
175
+
176
+ legend {
177
+ font-size: var(--text-7);
178
+ font-weight: var(--font-medium);
179
+ padding: 0 var(--space-2);
180
+ }
181
+ }
182
+
183
+ @layer components {
184
+ fieldset.group {
185
+ display: flex;
186
+ align-items: stretch;
187
+ border: none;
188
+ padding: 0;
189
+ margin: 0;
190
+
191
+ & > :is(input, textarea, select) {
192
+ flex: 1;
193
+ margin-top: 0;
194
+ border-right: 0;
195
+ }
196
+
197
+ & > :is(input, textarea, select, button) {
198
+ border-radius: 0;
199
+ &:first-child {
200
+ border-radius: var(--radius-medium) 0 0 var(--radius-medium);
201
+ }
202
+
203
+ &:last-child {
204
+ border-radius: 0 var(--radius-medium) var(--radius-medium) 0;
205
+ }
206
+ }
207
+
208
+ & > legend {
209
+ float: inline-start;
210
+ display: inline-flex;
211
+ align-items: center;
212
+ padding: 0 var(--space-3);
213
+ line-height: var(--leading-normal);
214
+ font-weight: var(--font-normal);
215
+ color: var(--muted-foreground);
216
+ background-color: var(--muted);
217
+ border: 1px solid var(--input);
218
+ border-inline-end: none;
219
+ border-radius: var(--radius-medium) 0 0 var(--radius-medium);
220
+ }
221
+ }
222
+
223
+ [data-field] {
224
+ margin-bottom: var(--space-4);
225
+
226
+ /* Hint text that succeeds an input with aria-describedby */
227
+ [data-hint], .error {
228
+ font-size: var(--text-8);
229
+ font-weight: var(--font-normal);
230
+ color: var(--muted-foreground);
231
+ margin-top: var(--space-1);
232
+ }
233
+
234
+ .error {
235
+ display: none;
236
+ }
237
+
238
+ /* Reveal error on data-field="error" */
239
+ &[data-field="error"] .error {
240
+ display: block;
241
+ color: var(--danger);
242
+ }
243
+ }
244
+ }
package/css/grid.css ADDED
@@ -0,0 +1,57 @@
1
+ @layer components {
2
+ :root {
3
+ --grid-cols: 12;
4
+ --grid-gap: 1.5rem;
5
+ --container-max: 1280px;
6
+ --container-pad: 1rem;
7
+ }
8
+
9
+ .container {
10
+ width: 100%;
11
+ max-width: var(--container-max);
12
+ margin-inline: auto;
13
+ padding-inline: var(--container-pad);
14
+ }
15
+
16
+ .row {
17
+ display: grid;
18
+ grid-template-columns: repeat(var(--grid-cols), 1fr);
19
+ gap: var(--grid-gap);
20
+ width: 100%;
21
+ }
22
+
23
+ /* Column spans using CSS custom property */
24
+ .col, [class*="col-"] { grid-column-end: span var(--span, var(--grid-cols)); }
25
+
26
+ .col-1 { --span: 1; }
27
+ .col-2 { --span: 2; }
28
+ .col-3 { --span: 3; }
29
+ .col-4 { --span: 4; }
30
+ .col-5 { --span: 5; }
31
+ .col-6 { --span: 6; }
32
+ .col-7 { --span: 7; }
33
+ .col-8 { --span: 8; }
34
+ .col-9 { --span: 9; }
35
+ .col-10 { --span: 10; }
36
+ .col-11 { --span: 11; }
37
+ .col-12 { --span: 12; }
38
+
39
+ /* Offsets via grid-column-start */
40
+ .offset-1 { grid-column-start: 2; }
41
+ .offset-2 { grid-column-start: 3; }
42
+ .offset-3 { grid-column-start: 4; }
43
+ .offset-4 { grid-column-start: 5; }
44
+ .offset-5 { grid-column-start: 6; }
45
+ .offset-6 { grid-column-start: 7; }
46
+
47
+ .col-end {
48
+ grid-column-start: span var(--span, 1);
49
+ grid-column-end: -1;
50
+ }
51
+
52
+ /* Responsive: stack on mobile */
53
+ @media (max-width: 768px) {
54
+ .row { --grid-cols: 4; --grid-gap: 1rem; }
55
+ .col, [class*="col-"] { --span: 4; } /* full width */
56
+ }
57
+ }
@@ -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
+ }
@@ -0,0 +1,177 @@
1
+ @layer components {
2
+ :root {
3
+ --topnav-height: var(--space-12);
4
+ --sidebar-width: 14rem;
5
+ }
6
+
7
+ [data-sidebar-layout] {
8
+ display: grid;
9
+ grid-template-columns: var(--sidebar-width) 1fr;
10
+ min-height: 100dvh;
11
+ gap: var(--space-4);
12
+
13
+ &:has(nav[data-topnav]) {
14
+ > aside[data-sidebar] {
15
+ top: var(--topnav-height);
16
+ height: calc(100dvh - var(--topnav-height));
17
+ }
18
+
19
+ /* <main> immediately after top-nav, adjust for topnav height */
20
+ > main {
21
+ min-width: 0;
22
+ margin-top: calc(var(--topnav-height));
23
+
24
+ /* Offset #anchor scrolling to not go behind the topnav. */
25
+ [id] {
26
+ scroll-margin-top: calc(var(--topnav-height) + var(--space-6));
27
+ }
28
+ }
29
+ }
30
+
31
+ > aside[data-sidebar] {
32
+ position: sticky;
33
+ top: 0;
34
+ z-index: 1;
35
+ height: 100dvh;
36
+ align-self: start;
37
+ background-color: var(--background);
38
+ border-right: 1px solid var(--border);
39
+ display: flex;
40
+ flex-direction: column;
41
+ width: var(--sidebar-width);
42
+
43
+ > header {
44
+ flex-shrink: 0;
45
+ padding: var(--space-3);
46
+ }
47
+
48
+ > footer {
49
+ flex-shrink: 0;
50
+ margin-top: auto;
51
+ padding: var(--space-3);
52
+ }
53
+
54
+ > nav {
55
+ flex: 1;
56
+ min-height: 0;
57
+ overflow-y: auto;
58
+ padding: var(--space-3) var(--space-2);
59
+ }
60
+
61
+ nav {
62
+ ul {
63
+ list-style: none;
64
+ padding: 0;
65
+ margin: 0;
66
+ display: flex;
67
+ flex-direction: column;
68
+ gap: var(--space-1);
69
+
70
+ li {
71
+ margin: 0;
72
+ }
73
+ }
74
+
75
+ a {
76
+ display: flex;
77
+ gap: var(--space-2);
78
+ padding: var(--space-1) var(--space-3);
79
+ font-size: var(--text-7);
80
+ color: var(--foreground);
81
+ text-decoration: none;
82
+ border-radius: var(--radius-small);
83
+ transition: background-color var(--transition-fast);
84
+
85
+ &:hover, &[aria-current] {
86
+ background-color: var(--accent);
87
+ }
88
+ }
89
+ }
90
+
91
+ details {
92
+ border: none;
93
+ overflow: visible;
94
+
95
+ & + details { margin-top: 0; }
96
+ &[open] summary { border-bottom: none; }
97
+ > ul { margin-left: var(--space-4); padding: var(--space-1) 0; }
98
+ }
99
+
100
+ summary {
101
+ justify-content: flex-start;
102
+ gap: var(--space-2);
103
+ padding: var(--space-2) var(--space-3);
104
+ font-size: var(--text-7);
105
+ font-weight: var(--font-medium);
106
+ color: var(--foreground);
107
+ border-radius: var(--radius-small);
108
+
109
+ &::after {
110
+ width: 0.75rem;
111
+ height: 0.75rem;
112
+ margin-inline-start: auto;
113
+ }
114
+ }
115
+ }
116
+ }
117
+
118
+ nav[data-topnav] {
119
+ position: fixed;
120
+ top: 0;
121
+ left: 0;
122
+ right: 0;
123
+ z-index: 5;
124
+ min-height: var(--topnav-height);
125
+ display: flex;
126
+ align-items: center;
127
+ gap: var(--space-3);
128
+ padding: var(--space-2) var(--space-4);
129
+ background-color: var(--background);
130
+ border-bottom: 1px solid var(--border);
131
+ box-shadow: var(--shadow-small);
132
+ }
133
+
134
+ [data-sidebar-toggle],
135
+ [data-sidebar-header] {
136
+ display: none;
137
+ }
138
+
139
+ [data-sidebar-toggle] {
140
+ padding: 0 var(--space-1);
141
+ background: none;
142
+ border: 1px solid var(--border);
143
+ border-radius: var(--radius-small);
144
+ cursor: pointer;
145
+ }
146
+
147
+ @media (max-width: 768px) {
148
+ [data-sidebar-layout] {
149
+ grid-template-columns: 1fr;
150
+
151
+ > aside[data-sidebar] {
152
+ position: fixed;
153
+ top: 0;
154
+ left: 0;
155
+ height: 100dvh;
156
+ width: 16rem;
157
+ transform: translateX(-100%);
158
+ transition: transform var(--transition);
159
+ box-shadow: var(--shadow-large);
160
+ }
161
+
162
+ &[data-sidebar-open] > aside[data-sidebar] {
163
+ transform: translateX(0);
164
+ }
165
+ }
166
+
167
+ [data-sidebar-toggle] { display: inline-block; }
168
+
169
+ [data-sidebar-header] {
170
+ display: flex;
171
+ align-items: center;
172
+ gap: var(--space-3);
173
+ padding: var(--space-3) var(--space-4);
174
+ border-bottom: 1px solid var(--border);
175
+ }
176
+ }
177
+ }