@duskmoon-dev/core 1.1.0 → 1.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.
Files changed (33) hide show
  1. package/dist/components/button.css +47 -46
  2. package/dist/components/checkbox.css +231 -0
  3. package/dist/components/datepicker.css +404 -10
  4. package/dist/components/form-group.css +308 -0
  5. package/dist/components/index.css +4077 -687
  6. package/dist/components/multi-select.css +491 -0
  7. package/dist/components/navigation.css +153 -2
  8. package/dist/components/otp-input.css +195 -0
  9. package/dist/components/pin-input.css +184 -0
  10. package/dist/components/radio.css +183 -0
  11. package/dist/components/segment-control.css +186 -0
  12. package/dist/components/select.css +205 -0
  13. package/dist/components/switch.css +150 -193
  14. package/dist/components/textarea.css +202 -0
  15. package/dist/components/time-input.css +252 -0
  16. package/dist/components/tree-select.css +439 -0
  17. package/dist/esm/components/button.js +47 -46
  18. package/dist/esm/components/checkbox.js +238 -0
  19. package/dist/esm/components/datepicker.js +404 -10
  20. package/dist/esm/components/form-group.js +315 -0
  21. package/dist/esm/components/multi-select.js +498 -0
  22. package/dist/esm/components/navigation.js +153 -2
  23. package/dist/esm/components/otp-input.js +202 -0
  24. package/dist/esm/components/pin-input.js +191 -0
  25. package/dist/esm/components/radio.js +190 -0
  26. package/dist/esm/components/segment-control.js +193 -0
  27. package/dist/esm/components/select.js +212 -0
  28. package/dist/esm/components/switch.js +150 -193
  29. package/dist/esm/components/textarea.js +209 -0
  30. package/dist/esm/components/time-input.js +259 -0
  31. package/dist/esm/components/tree-select.js +446 -0
  32. package/dist/index.css +4073 -683
  33. package/package.json +56 -1
@@ -0,0 +1,195 @@
1
+ /**
2
+ * OTP Input Component Styles
3
+ * DuskMoonUI - One-Time Password input for verification codes
4
+ */
5
+
6
+ @layer components {
7
+ /* Base OTP Container */
8
+ .otp-input {
9
+ display: inline-flex;
10
+ align-items: center;
11
+ gap: 0.5rem;
12
+ }
13
+
14
+ /* OTP Input Field */
15
+ .otp-input-field {
16
+ width: 3rem;
17
+ height: 3.5rem;
18
+ padding: 0;
19
+ font-size: 1.5rem;
20
+ font-weight: 600;
21
+ text-align: center;
22
+ color: var(--color-on-surface);
23
+ background-color: var(--color-surface);
24
+ border: 2px solid var(--color-outline);
25
+ border-radius: 0.5rem;
26
+ outline: none;
27
+ transition: border-color 150ms ease-in-out, box-shadow 150ms ease-in-out;
28
+ caret-color: var(--color-primary);
29
+ }
30
+
31
+ .otp-input-field::placeholder {
32
+ color: var(--color-on-surface-variant);
33
+ opacity: 0.5;
34
+ }
35
+
36
+ .otp-input-field:hover:not(:disabled):not(:focus) {
37
+ border-color: var(--color-on-surface-variant);
38
+ }
39
+
40
+ .otp-input-field:focus {
41
+ border-color: var(--color-primary);
42
+ box-shadow: 0 0 0 3px color-mix(in oklch, var(--color-primary) 10%, transparent);
43
+ }
44
+
45
+ .otp-input-field:disabled {
46
+ opacity: 0.38;
47
+ cursor: not-allowed;
48
+ background-color: var(--color-surface-container);
49
+ }
50
+
51
+ /* Filled State */
52
+ .otp-input-field-filled {
53
+ background-color: var(--color-surface-container);
54
+ border-color: var(--color-primary);
55
+ }
56
+
57
+ /* Separator */
58
+ .otp-separator {
59
+ display: flex;
60
+ align-items: center;
61
+ justify-content: center;
62
+ width: 1rem;
63
+ height: 0.25rem;
64
+ background-color: var(--color-outline);
65
+ border-radius: 0.125rem;
66
+ }
67
+
68
+ /* Size Variants */
69
+ .otp-input-sm .otp-input-field {
70
+ width: 2.5rem;
71
+ height: 3rem;
72
+ font-size: 1.25rem;
73
+ border-radius: 0.375rem;
74
+ }
75
+
76
+ .otp-input-lg .otp-input-field {
77
+ width: 3.5rem;
78
+ height: 4rem;
79
+ font-size: 1.75rem;
80
+ border-radius: 0.625rem;
81
+ }
82
+
83
+ /* Compact Gap */
84
+ .otp-input-compact {
85
+ gap: 0.25rem;
86
+ }
87
+
88
+ /* Wide Gap */
89
+ .otp-input-wide {
90
+ gap: 1rem;
91
+ }
92
+
93
+ /* Filled Variant */
94
+ .otp-input-filled .otp-input-field {
95
+ background-color: var(--color-surface-container);
96
+ border: none;
97
+ border-bottom: 2px solid var(--color-outline);
98
+ border-radius: 0.5rem 0.5rem 0 0;
99
+ }
100
+
101
+ .otp-input-filled .otp-input-field:focus {
102
+ border-bottom-color: var(--color-primary);
103
+ box-shadow: none;
104
+ }
105
+
106
+ /* Outlined Variant (default) */
107
+ .otp-input-outlined .otp-input-field {
108
+ background-color: transparent;
109
+ }
110
+
111
+ /* Underline Variant */
112
+ .otp-input-underline .otp-input-field {
113
+ background-color: transparent;
114
+ border: none;
115
+ border-bottom: 2px solid var(--color-outline);
116
+ border-radius: 0;
117
+ }
118
+
119
+ .otp-input-underline .otp-input-field:focus {
120
+ border-bottom-color: var(--color-primary);
121
+ box-shadow: none;
122
+ }
123
+
124
+ /* Color Variants */
125
+ .otp-input-primary .otp-input-field:focus {
126
+ border-color: var(--color-primary);
127
+ box-shadow: 0 0 0 3px color-mix(in oklch, var(--color-primary) 10%, transparent);
128
+ }
129
+
130
+ .otp-input-secondary .otp-input-field:focus {
131
+ border-color: var(--color-secondary);
132
+ box-shadow: 0 0 0 3px color-mix(in oklch, var(--color-secondary) 10%, transparent);
133
+ }
134
+
135
+ .otp-input-tertiary .otp-input-field:focus {
136
+ border-color: var(--color-tertiary);
137
+ box-shadow: 0 0 0 3px color-mix(in oklch, var(--color-tertiary) 10%, transparent);
138
+ }
139
+
140
+ /* Error State */
141
+ .otp-input-error .otp-input-field {
142
+ border-color: var(--color-error);
143
+ }
144
+
145
+ .otp-input-error .otp-input-field:focus {
146
+ border-color: var(--color-error);
147
+ box-shadow: 0 0 0 3px color-mix(in oklch, var(--color-error) 10%, transparent);
148
+ }
149
+
150
+ /* Success State */
151
+ .otp-input-success .otp-input-field {
152
+ border-color: var(--color-success);
153
+ }
154
+
155
+ .otp-input-success .otp-input-field:focus {
156
+ border-color: var(--color-success);
157
+ box-shadow: 0 0 0 3px color-mix(in oklch, var(--color-success) 10%, transparent);
158
+ }
159
+
160
+ /* Masked Input (dots) */
161
+ .otp-input-masked .otp-input-field {
162
+ -webkit-text-security: disc;
163
+ font-family: text-security-disc;
164
+ }
165
+
166
+ /* OTP Group with Label */
167
+ .otp-group {
168
+ display: flex;
169
+ flex-direction: column;
170
+ gap: 0.5rem;
171
+ }
172
+
173
+ .otp-label {
174
+ font-size: 0.875rem;
175
+ font-weight: 500;
176
+ color: var(--color-on-surface);
177
+ }
178
+
179
+ .otp-helper {
180
+ font-size: 0.75rem;
181
+ color: var(--color-on-surface-variant);
182
+ }
183
+
184
+ .otp-error-message {
185
+ font-size: 0.75rem;
186
+ color: var(--color-error);
187
+ }
188
+
189
+ /* Reduce Motion */
190
+ @media (prefers-reduced-motion: reduce) {
191
+ .otp-input-field {
192
+ transition: none;
193
+ }
194
+ }
195
+ }
@@ -0,0 +1,184 @@
1
+ /**
2
+ * PIN Input Component Styles
3
+ * DuskMoonUI - Secure PIN/password entry input
4
+ */
5
+
6
+ @layer components {
7
+ /* Base PIN Container */
8
+ .pin-input {
9
+ display: inline-flex;
10
+ align-items: center;
11
+ gap: 0.5rem;
12
+ }
13
+
14
+ /* PIN Input Field */
15
+ .pin-input-field {
16
+ width: 3rem;
17
+ height: 3.5rem;
18
+ padding: 0;
19
+ font-size: 1.5rem;
20
+ font-weight: 600;
21
+ text-align: center;
22
+ color: var(--color-on-surface);
23
+ background-color: var(--color-surface);
24
+ border: 2px solid var(--color-outline);
25
+ border-radius: 0.5rem;
26
+ outline: none;
27
+ transition: border-color 150ms ease-in-out, box-shadow 150ms ease-in-out;
28
+ caret-color: var(--color-primary);
29
+ -webkit-text-security: disc;
30
+ }
31
+
32
+ .pin-input-field::placeholder {
33
+ color: var(--color-on-surface-variant);
34
+ opacity: 0.3;
35
+ }
36
+
37
+ .pin-input-field:hover:not(:disabled):not(:focus) {
38
+ border-color: var(--color-on-surface-variant);
39
+ }
40
+
41
+ .pin-input-field:focus {
42
+ border-color: var(--color-primary);
43
+ box-shadow: 0 0 0 3px color-mix(in oklch, var(--color-primary) 10%, transparent);
44
+ }
45
+
46
+ .pin-input-field:disabled {
47
+ opacity: 0.38;
48
+ cursor: not-allowed;
49
+ background-color: var(--color-surface-container);
50
+ }
51
+
52
+ /* Show PIN (toggle visibility) */
53
+ .pin-input-visible .pin-input-field {
54
+ -webkit-text-security: none;
55
+ }
56
+
57
+ /* Size Variants */
58
+ .pin-input-sm .pin-input-field {
59
+ width: 2.5rem;
60
+ height: 3rem;
61
+ font-size: 1.25rem;
62
+ border-radius: 0.375rem;
63
+ }
64
+
65
+ .pin-input-lg .pin-input-field {
66
+ width: 3.5rem;
67
+ height: 4rem;
68
+ font-size: 1.75rem;
69
+ border-radius: 0.625rem;
70
+ }
71
+
72
+ /* Compact Gap */
73
+ .pin-input-compact {
74
+ gap: 0.25rem;
75
+ }
76
+
77
+ /* Filled Variant */
78
+ .pin-input-filled .pin-input-field {
79
+ background-color: var(--color-surface-container);
80
+ border: none;
81
+ border-bottom: 2px solid var(--color-outline);
82
+ border-radius: 0.5rem 0.5rem 0 0;
83
+ }
84
+
85
+ .pin-input-filled .pin-input-field:focus {
86
+ border-bottom-color: var(--color-primary);
87
+ box-shadow: none;
88
+ }
89
+
90
+ /* Circle Variant */
91
+ .pin-input-circle .pin-input-field {
92
+ width: 3rem;
93
+ height: 3rem;
94
+ border-radius: 50%;
95
+ }
96
+
97
+ .pin-input-circle.pin-input-sm .pin-input-field {
98
+ width: 2.5rem;
99
+ height: 2.5rem;
100
+ }
101
+
102
+ .pin-input-circle.pin-input-lg .pin-input-field {
103
+ width: 3.5rem;
104
+ height: 3.5rem;
105
+ }
106
+
107
+ /* Dot Display (shows dots instead of numbers) */
108
+ .pin-input-dots .pin-input-field {
109
+ font-size: 2rem;
110
+ letter-spacing: -0.25rem;
111
+ }
112
+
113
+ /* Color Variants */
114
+ .pin-input-primary .pin-input-field:focus {
115
+ border-color: var(--color-primary);
116
+ box-shadow: 0 0 0 3px color-mix(in oklch, var(--color-primary) 10%, transparent);
117
+ }
118
+
119
+ .pin-input-secondary .pin-input-field:focus {
120
+ border-color: var(--color-secondary);
121
+ box-shadow: 0 0 0 3px color-mix(in oklch, var(--color-secondary) 10%, transparent);
122
+ }
123
+
124
+ .pin-input-tertiary .pin-input-field:focus {
125
+ border-color: var(--color-tertiary);
126
+ box-shadow: 0 0 0 3px color-mix(in oklch, var(--color-tertiary) 10%, transparent);
127
+ }
128
+
129
+ /* Error State */
130
+ .pin-input-error .pin-input-field {
131
+ border-color: var(--color-error);
132
+ animation: pin-shake 300ms ease-in-out;
133
+ }
134
+
135
+ .pin-input-error .pin-input-field:focus {
136
+ border-color: var(--color-error);
137
+ box-shadow: 0 0 0 3px color-mix(in oklch, var(--color-error) 10%, transparent);
138
+ }
139
+
140
+ @keyframes pin-shake {
141
+ 0%, 100% { transform: translateX(0); }
142
+ 20%, 60% { transform: translateX(-4px); }
143
+ 40%, 80% { transform: translateX(4px); }
144
+ }
145
+
146
+ /* Success State */
147
+ .pin-input-success .pin-input-field {
148
+ border-color: var(--color-success);
149
+ }
150
+
151
+ /* PIN Group with Label */
152
+ .pin-group {
153
+ display: flex;
154
+ flex-direction: column;
155
+ gap: 0.5rem;
156
+ }
157
+
158
+ .pin-label {
159
+ font-size: 0.875rem;
160
+ font-weight: 500;
161
+ color: var(--color-on-surface);
162
+ }
163
+
164
+ .pin-helper {
165
+ font-size: 0.75rem;
166
+ color: var(--color-on-surface-variant);
167
+ }
168
+
169
+ .pin-error-message {
170
+ font-size: 0.75rem;
171
+ color: var(--color-error);
172
+ }
173
+
174
+ /* Reduce Motion */
175
+ @media (prefers-reduced-motion: reduce) {
176
+ .pin-input-field {
177
+ transition: none;
178
+ }
179
+
180
+ .pin-input-error .pin-input-field {
181
+ animation: none;
182
+ }
183
+ }
184
+ }
@@ -0,0 +1,183 @@
1
+ /**
2
+ * Radio Component Styles
3
+ * DuskMoonUI - Material Design 3 inspired radio button
4
+ *
5
+ * Usage: <input type="radio" name="group" class="radio" />
6
+ * With label: <label class="radio-label"><input type="radio" name="group" class="radio" /><span>Option</span></label>
7
+ */
8
+
9
+ @layer components {
10
+ /* Base Radio - applied directly to input[type="radio"] */
11
+ .radio {
12
+ --radio-size: 1.25rem;
13
+ --radio-color: var(--color-primary);
14
+ --radio-border-color: var(--color-on-surface-variant);
15
+
16
+ position: relative;
17
+ display: inline-grid;
18
+ place-content: center;
19
+ width: var(--radio-size);
20
+ height: var(--radio-size);
21
+ margin: 0;
22
+ cursor: pointer;
23
+ appearance: none;
24
+ background-color: transparent;
25
+ border: 2px solid var(--radio-border-color);
26
+ border-radius: 50%;
27
+ transition: border-color 150ms ease-in-out;
28
+ }
29
+
30
+ /* Inner dot using ::before pseudo-element */
31
+ .radio::before {
32
+ content: "";
33
+ width: 0.5em;
34
+ height: 0.5em;
35
+ border-radius: 50%;
36
+ background-color: var(--radio-color);
37
+ transform: scale(0);
38
+ transition: transform 150ms ease-in-out;
39
+ }
40
+
41
+ /* Checked State */
42
+ .radio:checked {
43
+ border-color: var(--radio-color);
44
+ }
45
+
46
+ .radio:checked::before {
47
+ transform: scale(1);
48
+ }
49
+
50
+ /* Hover State */
51
+ .radio:hover:not(:disabled):not(:checked) {
52
+ border-color: var(--color-on-surface);
53
+ }
54
+
55
+ .radio:checked:hover:not(:disabled) {
56
+ border-color: color-mix(in oklch, var(--radio-color), black 10%);
57
+ }
58
+
59
+ .radio:checked:hover:not(:disabled)::before {
60
+ background-color: color-mix(in oklch, var(--radio-color), black 10%);
61
+ }
62
+
63
+ /* Focus State */
64
+ .radio:focus-visible {
65
+ outline: 2px solid var(--radio-color);
66
+ outline-offset: 2px;
67
+ }
68
+
69
+ /* Disabled State */
70
+ .radio:disabled {
71
+ cursor: not-allowed;
72
+ opacity: 0.38;
73
+ }
74
+
75
+ /* ========================================
76
+ * Size Variants
77
+ * ======================================== */
78
+
79
+ .radio-xs {
80
+ --radio-size: 0.875rem;
81
+ }
82
+
83
+ .radio-sm {
84
+ --radio-size: 1rem;
85
+ }
86
+
87
+ .radio-md {
88
+ --radio-size: 1.25rem;
89
+ }
90
+
91
+ .radio-lg {
92
+ --radio-size: 1.5rem;
93
+ }
94
+
95
+ .radio-xl {
96
+ --radio-size: 1.75rem;
97
+ }
98
+
99
+ /* ========================================
100
+ * Color Variants
101
+ * ======================================== */
102
+
103
+ .radio-primary {
104
+ --radio-color: var(--color-primary);
105
+ }
106
+
107
+ .radio-secondary {
108
+ --radio-color: var(--color-secondary);
109
+ }
110
+
111
+ .radio-tertiary {
112
+ --radio-color: var(--color-tertiary);
113
+ }
114
+
115
+ .radio-success {
116
+ --radio-color: var(--color-success);
117
+ }
118
+
119
+ .radio-warning {
120
+ --radio-color: var(--color-warning);
121
+ }
122
+
123
+ .radio-error {
124
+ --radio-color: var(--color-error);
125
+ }
126
+
127
+ .radio-info {
128
+ --radio-color: var(--color-info);
129
+ }
130
+
131
+ /* ========================================
132
+ * Radio with Label (wrapper pattern)
133
+ * ======================================== */
134
+
135
+ .radio-label {
136
+ display: inline-flex;
137
+ align-items: center;
138
+ gap: 0.5rem;
139
+ cursor: pointer;
140
+ user-select: none;
141
+ font-size: 0.875rem;
142
+ color: var(--color-on-surface);
143
+ }
144
+
145
+ .radio-label:has(.radio:disabled) {
146
+ cursor: not-allowed;
147
+ opacity: 0.38;
148
+ }
149
+
150
+ /* ========================================
151
+ * Radio Group
152
+ * ======================================== */
153
+
154
+ .radio-group {
155
+ display: flex;
156
+ flex-direction: column;
157
+ gap: 0.75rem;
158
+ }
159
+
160
+ .radio-group-horizontal {
161
+ flex-direction: row;
162
+ flex-wrap: wrap;
163
+ gap: 1.5rem;
164
+ }
165
+
166
+ .radio-group-label {
167
+ font-size: 0.875rem;
168
+ font-weight: 500;
169
+ color: var(--color-on-surface);
170
+ margin-bottom: 0.5rem;
171
+ }
172
+
173
+ /* ========================================
174
+ * Reduce Motion
175
+ * ======================================== */
176
+
177
+ @media (prefers-reduced-motion: reduce) {
178
+ .radio,
179
+ .radio::before {
180
+ transition: none;
181
+ }
182
+ }
183
+ }