maquina-components 0.1.1 → 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.
- checksums.yaml +4 -4
- data/README.md +410 -13
- data/app/assets/images/maquina.svg +1 -0
- data/app/assets/stylesheets/alert.css +143 -0
- data/app/assets/stylesheets/badge.css +145 -0
- data/app/assets/stylesheets/breadcrumbs.css +163 -0
- data/app/assets/stylesheets/card.css +128 -0
- data/app/assets/stylesheets/dropdown_menu.css +248 -0
- data/app/assets/stylesheets/empty.css +133 -0
- data/app/assets/stylesheets/form.css +617 -0
- data/app/assets/stylesheets/header.css +61 -0
- data/app/assets/stylesheets/maquina_components.css +178 -0
- data/app/assets/stylesheets/pagination.css +154 -0
- data/app/assets/stylesheets/sidebar.css +477 -0
- data/app/assets/stylesheets/table.css +205 -0
- data/app/assets/stylesheets/toggle_group.css +151 -0
- data/app/assets/tailwind/maquina_components_engine/engine.css +16 -0
- data/app/helpers/maquina_components/breadcrumbs_helper.rb +118 -0
- data/app/helpers/maquina_components/dropdown_menu_helper.rb +249 -0
- data/app/helpers/maquina_components/empty_helper.rb +102 -0
- data/app/helpers/maquina_components/icons_helper.rb +161 -0
- data/app/helpers/maquina_components/pagination_helper.rb +153 -0
- data/app/helpers/maquina_components/sidebar_helper.rb +63 -0
- data/app/helpers/maquina_components/table_helper.rb +144 -0
- data/app/helpers/maquina_components/toggle_group_helper.rb +172 -0
- data/app/javascript/controllers/breadcrumb_controller.js +71 -0
- data/app/javascript/controllers/dropdown_menu_controller.js +203 -0
- data/app/javascript/controllers/menu_button_controller.js +59 -0
- data/app/javascript/controllers/sidebar_controller.js +316 -0
- data/app/javascript/controllers/sidebar_trigger_controller.js +32 -0
- data/app/javascript/controllers/toggle_group_controller.js +178 -0
- data/app/views/components/_alert.html.erb +12 -0
- data/app/views/components/_badge.html.erb +10 -0
- data/app/views/components/_breadcrumbs.html.erb +16 -0
- data/app/views/components/_card.html.erb +6 -0
- data/app/views/components/_dropdown.html.erb +25 -0
- data/app/views/components/_dropdown_menu.html.erb +9 -0
- data/app/views/components/_empty.html.erb +10 -0
- data/app/views/components/_header.html.erb +8 -0
- data/app/views/components/_menu_button.html.erb +44 -0
- data/app/views/components/_pagination.html.erb +13 -0
- data/app/views/components/_separator.html.erb +11 -0
- data/app/views/components/_sidebar.html.erb +40 -0
- data/app/views/components/_simple_table.html.erb +49 -0
- data/app/views/components/_table.html.erb +21 -0
- data/app/views/components/_toggle_group.html.erb +24 -0
- data/app/views/components/alert/_description.html.erb +6 -0
- data/app/views/components/alert/_title.html.erb +6 -0
- data/app/views/components/breadcrumbs/_ellipsis.html.erb +9 -0
- data/app/views/components/breadcrumbs/_item.html.erb +8 -0
- data/app/views/components/breadcrumbs/_link.html.erb +8 -0
- data/app/views/components/breadcrumbs/_list.html.erb +8 -0
- data/app/views/components/breadcrumbs/_page.html.erb +8 -0
- data/app/views/components/breadcrumbs/_separator.html.erb +17 -0
- data/app/views/components/card/_action.html.erb +6 -0
- data/app/views/components/card/_content.html.erb +9 -0
- data/app/views/components/card/_description.html.erb +6 -0
- data/app/views/components/card/_footer.html.erb +17 -0
- data/app/views/components/card/_header.html.erb +9 -0
- data/app/views/components/card/_title.html.erb +9 -0
- data/app/views/components/dropdown_menu/_content.html.erb +20 -0
- data/app/views/components/dropdown_menu/_group.html.erb +12 -0
- data/app/views/components/dropdown_menu/_item.html.erb +29 -0
- data/app/views/components/dropdown_menu/_label.html.erb +13 -0
- data/app/views/components/dropdown_menu/_separator.html.erb +11 -0
- data/app/views/components/dropdown_menu/_shortcut.html.erb +12 -0
- data/app/views/components/dropdown_menu/_trigger.html.erb +24 -0
- data/app/views/components/empty/_content.html.erb +8 -0
- data/app/views/components/empty/_description.html.erb +12 -0
- data/app/views/components/empty/_header.html.erb +8 -0
- data/app/views/components/empty/_media.html.erb +13 -0
- data/app/views/components/empty/_title.html.erb +12 -0
- data/app/views/components/pagination/_content.html.erb +8 -0
- data/app/views/components/pagination/_ellipsis.html.erb +28 -0
- data/app/views/components/pagination/_item.html.erb +8 -0
- data/app/views/components/pagination/_link.html.erb +23 -0
- data/app/views/components/pagination/_next.html.erb +57 -0
- data/app/views/components/pagination/_previous.html.erb +57 -0
- data/app/views/components/sidebar/_content.html.erb +8 -0
- data/app/views/components/sidebar/_footer.html.erb +8 -0
- data/app/views/components/sidebar/_group.html.erb +12 -0
- data/app/views/components/sidebar/_header.html.erb +8 -0
- data/app/views/components/sidebar/_inset.html.erb +8 -0
- data/app/views/components/sidebar/_menu.html.erb +8 -0
- data/app/views/components/sidebar/_menu_button.html.erb +14 -0
- data/app/views/components/sidebar/_menu_item.html.erb +7 -0
- data/app/views/components/sidebar/_menu_link.html.erb +32 -0
- data/app/views/components/sidebar/_provider.html.erb +16 -0
- data/app/views/components/sidebar/_trigger.html.erb +12 -0
- data/app/views/components/stats/_stats_card.html.erb +100 -0
- data/app/views/components/stats/_stats_grid.html.erb +38 -0
- data/app/views/components/table/_body.html.erb +5 -0
- data/app/views/components/table/_caption.html.erb +5 -0
- data/app/views/components/table/_cell.html.erb +5 -0
- data/app/views/components/table/_footer.html.erb +5 -0
- data/app/views/components/table/_head.html.erb +8 -0
- data/app/views/components/table/_header.html.erb +8 -0
- data/app/views/components/table/_row.html.erb +8 -0
- data/app/views/components/toggle_group/_item.html.erb +19 -0
- data/config/importmap.rb +1 -0
- data/lib/generators/maquina_components/install/USAGE +39 -0
- data/lib/generators/maquina_components/install/install_generator.rb +123 -0
- data/lib/generators/maquina_components/install/templates/maquina_components_helper.rb.tt +68 -0
- data/lib/generators/maquina_components/install/templates/theme.css.tt +179 -0
- data/lib/maquina_components/engine.rb +10 -0
- data/lib/maquina_components/version.rb +1 -1
- metadata +121 -5
|
@@ -0,0 +1,617 @@
|
|
|
1
|
+
/* ===================================================================
|
|
2
|
+
FORM COMPONENTS - CSS-only with data attributes
|
|
3
|
+
Matches shadcn/ui Tailwind v4 styles
|
|
4
|
+
Usage: Add data-component="..." to Rails form helpers
|
|
5
|
+
=================================================================== */
|
|
6
|
+
|
|
7
|
+
/* ===================================================================
|
|
8
|
+
FORM CONTAINER
|
|
9
|
+
Usage: <%= form_with model: @user, data: { component: "form" } do |f| %>
|
|
10
|
+
=================================================================== */
|
|
11
|
+
[data-component="form"] {
|
|
12
|
+
@apply grid gap-6;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/* Form Group - wraps label + input + help/error text */
|
|
16
|
+
[data-form-part="group"] {
|
|
17
|
+
@apply grid gap-2;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/* Inline form group (horizontal layout) */
|
|
21
|
+
[data-form-part="group"][data-layout="inline"] {
|
|
22
|
+
@apply flex items-center gap-3;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/* ===================================================================
|
|
26
|
+
LABEL
|
|
27
|
+
Usage: <%= f.label :email, data: { component: "label" } %>
|
|
28
|
+
=================================================================== */
|
|
29
|
+
[data-component="label"] {
|
|
30
|
+
@apply flex items-center gap-2;
|
|
31
|
+
@apply text-sm leading-none font-medium select-none;
|
|
32
|
+
color: var(--foreground);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/* Label disabled state (when used with peer) */
|
|
36
|
+
[data-component="label"]:has(+ :disabled),
|
|
37
|
+
:disabled + [data-component="label"] {
|
|
38
|
+
@apply cursor-not-allowed opacity-50;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/* Required indicator */
|
|
42
|
+
[data-component="label"][data-required]::after {
|
|
43
|
+
content: "*";
|
|
44
|
+
@apply ml-0.5;
|
|
45
|
+
color: var(--destructive);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/* ===================================================================
|
|
49
|
+
INPUT
|
|
50
|
+
Usage: <%= f.text_field :email, data: { component: "input" } %>
|
|
51
|
+
Matches: h-9 w-full min-w-0 rounded-md border border-input bg-transparent
|
|
52
|
+
px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none
|
|
53
|
+
md:text-sm placeholder:text-muted-foreground
|
|
54
|
+
focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]
|
|
55
|
+
disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50
|
|
56
|
+
aria-invalid:ring-destructive/20 aria-invalid:border-destructive
|
|
57
|
+
=================================================================== */
|
|
58
|
+
[data-component="input"] {
|
|
59
|
+
@apply h-9 w-full min-w-0;
|
|
60
|
+
@apply rounded-md border px-3 py-1;
|
|
61
|
+
@apply text-base md:text-sm;
|
|
62
|
+
@apply outline-none;
|
|
63
|
+
@apply transition-[color,box-shadow];
|
|
64
|
+
|
|
65
|
+
background-color: transparent;
|
|
66
|
+
border-color: var(--input);
|
|
67
|
+
color: var(--foreground);
|
|
68
|
+
box-shadow: var(--shadow-xs, 0 1px 2px 0 rgb(0 0 0 / 0.05));
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/* Placeholder */
|
|
72
|
+
[data-component="input"]::placeholder {
|
|
73
|
+
color: var(--muted-foreground);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/* Selection */
|
|
77
|
+
[data-component="input"]::selection {
|
|
78
|
+
background-color: var(--primary);
|
|
79
|
+
color: var(--primary-foreground);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/* Focus state */
|
|
83
|
+
[data-component="input"]:focus,
|
|
84
|
+
[data-component="input"]:focus-visible {
|
|
85
|
+
border-color: var(--ring);
|
|
86
|
+
box-shadow:
|
|
87
|
+
var(--shadow-xs, 0 1px 2px 0 rgb(0 0 0 / 0.05)),
|
|
88
|
+
0 0 0 3px color-mix(in oklch, var(--ring) 50%, transparent);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/* Disabled state */
|
|
92
|
+
[data-component="input"]:disabled {
|
|
93
|
+
@apply pointer-events-none cursor-not-allowed opacity-50;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/* Invalid/error state */
|
|
97
|
+
[data-component="input"][aria-invalid="true"],
|
|
98
|
+
[data-component="input"]:invalid:not(:placeholder-shown) {
|
|
99
|
+
border-color: var(--destructive);
|
|
100
|
+
box-shadow:
|
|
101
|
+
var(--shadow-xs, 0 1px 2px 0 rgb(0 0 0 / 0.05)),
|
|
102
|
+
0 0 0 3px color-mix(in oklch, var(--destructive) 20%, transparent);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/* File input */
|
|
106
|
+
[data-component="input"][type="file"] {
|
|
107
|
+
@apply py-1.5;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
[data-component="input"][type="file"]::file-selector-button {
|
|
111
|
+
@apply inline-flex h-7 items-center border-0 bg-transparent;
|
|
112
|
+
@apply text-sm font-medium;
|
|
113
|
+
color: var(--foreground);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/* Input sizes */
|
|
117
|
+
[data-component="input"][data-size="sm"] {
|
|
118
|
+
@apply h-8 px-2.5 text-xs;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
[data-component="input"][data-size="lg"] {
|
|
122
|
+
@apply h-11 px-4 text-base;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/* Dark mode */
|
|
126
|
+
.dark [data-component="input"] {
|
|
127
|
+
background-color: color-mix(in oklch, var(--input) 30%, transparent);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/* ===================================================================
|
|
131
|
+
TEXTAREA
|
|
132
|
+
Usage: <%= f.text_area :bio, data: { component: "textarea" } %>
|
|
133
|
+
Matches: min-h-[60px] w-full rounded-md border border-input bg-transparent
|
|
134
|
+
px-3 py-2 text-base shadow-xs outline-none md:text-sm
|
|
135
|
+
placeholder:text-muted-foreground
|
|
136
|
+
focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]
|
|
137
|
+
disabled:cursor-not-allowed disabled:opacity-50
|
|
138
|
+
aria-invalid:ring-destructive/20 aria-invalid:border-destructive
|
|
139
|
+
=================================================================== */
|
|
140
|
+
[data-component="textarea"] {
|
|
141
|
+
@apply w-full min-w-0 min-h-16;
|
|
142
|
+
@apply rounded-md border px-3 py-2;
|
|
143
|
+
@apply text-base md:text-sm;
|
|
144
|
+
@apply outline-none resize-y;
|
|
145
|
+
@apply transition-[color,box-shadow];
|
|
146
|
+
|
|
147
|
+
background-color: transparent;
|
|
148
|
+
border-color: var(--input);
|
|
149
|
+
color: var(--foreground);
|
|
150
|
+
box-shadow: var(--shadow-xs, 0 1px 2px 0 rgb(0 0 0 / 0.05));
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
[data-component="textarea"]::placeholder {
|
|
154
|
+
color: var(--muted-foreground);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
[data-component="textarea"]:focus,
|
|
158
|
+
[data-component="textarea"]:focus-visible {
|
|
159
|
+
border-color: var(--ring);
|
|
160
|
+
box-shadow:
|
|
161
|
+
var(--shadow-xs, 0 1px 2px 0 rgb(0 0 0 / 0.05)),
|
|
162
|
+
0 0 0 3px color-mix(in oklch, var(--ring) 50%, transparent);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
[data-component="textarea"]:disabled {
|
|
166
|
+
@apply cursor-not-allowed opacity-50;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
[data-component="textarea"][aria-invalid="true"],
|
|
170
|
+
[data-component="textarea"]:invalid:not(:placeholder-shown) {
|
|
171
|
+
border-color: var(--destructive);
|
|
172
|
+
box-shadow:
|
|
173
|
+
var(--shadow-xs, 0 1px 2px 0 rgb(0 0 0 / 0.05)),
|
|
174
|
+
0 0 0 3px color-mix(in oklch, var(--destructive) 20%, transparent);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/* Dark mode */
|
|
178
|
+
.dark [data-component="textarea"] {
|
|
179
|
+
background-color: color-mix(in oklch, var(--input) 30%, transparent);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/* ===================================================================
|
|
183
|
+
SELECT (Native)
|
|
184
|
+
Usage: <%= f.select :country, options, {}, data: { component: "select" } %>
|
|
185
|
+
=================================================================== */
|
|
186
|
+
[data-component="select"] {
|
|
187
|
+
@apply h-9 w-full min-w-0;
|
|
188
|
+
@apply rounded-md border px-3 py-1 pr-8;
|
|
189
|
+
@apply text-base md:text-sm;
|
|
190
|
+
@apply outline-none appearance-none cursor-pointer;
|
|
191
|
+
@apply transition-[color,box-shadow];
|
|
192
|
+
|
|
193
|
+
background-color: var(--background);
|
|
194
|
+
border-color: var(--input);
|
|
195
|
+
color: var(--foreground);
|
|
196
|
+
box-shadow: var(--shadow-xs, 0 1px 2px 0 rgb(0 0 0 / 0.05));
|
|
197
|
+
|
|
198
|
+
/* Chevron icon */
|
|
199
|
+
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e");
|
|
200
|
+
background-position: right 0.5rem center;
|
|
201
|
+
background-repeat: no-repeat;
|
|
202
|
+
background-size: 1.25rem 1.25rem;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
[data-component="select"]:focus,
|
|
206
|
+
[data-component="select"]:focus-visible {
|
|
207
|
+
border-color: var(--ring);
|
|
208
|
+
box-shadow:
|
|
209
|
+
var(--shadow-xs, 0 1px 2px 0 rgb(0 0 0 / 0.05)),
|
|
210
|
+
0 0 0 3px color-mix(in oklch, var(--ring) 50%, transparent);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
[data-component="select"]:disabled {
|
|
214
|
+
@apply pointer-events-none cursor-not-allowed opacity-50;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
[data-component="select"][aria-invalid="true"] {
|
|
218
|
+
border-color: var(--destructive);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/* Dark mode */
|
|
222
|
+
.dark [data-component="select"] {
|
|
223
|
+
background-color: color-mix(in oklch, var(--input) 30%, transparent);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/* ===================================================================
|
|
227
|
+
CHECKBOX
|
|
228
|
+
Usage: <%= f.check_box :terms, data: { component: "checkbox" } %>
|
|
229
|
+
Matches: peer size-4 shrink-0 rounded-[4px] border border-input shadow-xs
|
|
230
|
+
transition-shadow outline-none
|
|
231
|
+
data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground
|
|
232
|
+
data-[state=checked]:border-primary
|
|
233
|
+
focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]
|
|
234
|
+
disabled:cursor-not-allowed disabled:opacity-50
|
|
235
|
+
aria-invalid:ring-destructive/20 aria-invalid:border-destructive
|
|
236
|
+
=================================================================== */
|
|
237
|
+
[data-component="checkbox"] {
|
|
238
|
+
@apply size-4 shrink-0;
|
|
239
|
+
@apply rounded-[4px] border;
|
|
240
|
+
@apply cursor-pointer appearance-none;
|
|
241
|
+
@apply transition-shadow;
|
|
242
|
+
|
|
243
|
+
border-color: var(--input);
|
|
244
|
+
background-color: transparent;
|
|
245
|
+
box-shadow: var(--shadow-xs, 0 1px 2px 0 rgb(0 0 0 / 0.05));
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
[data-component="checkbox"]:focus,
|
|
249
|
+
[data-component="checkbox"]:focus-visible {
|
|
250
|
+
@apply outline-none;
|
|
251
|
+
border-color: var(--ring);
|
|
252
|
+
box-shadow:
|
|
253
|
+
var(--shadow-xs, 0 1px 2px 0 rgb(0 0 0 / 0.05)),
|
|
254
|
+
0 0 0 3px color-mix(in oklch, var(--ring) 50%, transparent);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
[data-component="checkbox"]:checked {
|
|
258
|
+
background-color: var(--primary);
|
|
259
|
+
border-color: var(--primary);
|
|
260
|
+
|
|
261
|
+
/* Checkmark icon */
|
|
262
|
+
background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M12.207 4.793a1 1 0 010 1.414l-5 5a1 1 0 01-1.414 0l-2-2a1 1 0 011.414-1.414L6.5 9.086l4.293-4.293a1 1 0 011.414 0z'/%3e%3c/svg%3e");
|
|
263
|
+
background-position: center;
|
|
264
|
+
background-repeat: no-repeat;
|
|
265
|
+
background-size: 100% 100%;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
[data-component="checkbox"]:disabled {
|
|
269
|
+
@apply cursor-not-allowed opacity-50;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
[data-component="checkbox"][aria-invalid="true"] {
|
|
273
|
+
border-color: var(--destructive);
|
|
274
|
+
box-shadow:
|
|
275
|
+
var(--shadow-xs, 0 1px 2px 0 rgb(0 0 0 / 0.05)),
|
|
276
|
+
0 0 0 3px color-mix(in oklch, var(--destructive) 20%, transparent);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
[data-component="checkbox"]:indeterminate {
|
|
280
|
+
background-color: var(--primary);
|
|
281
|
+
border-color: var(--primary);
|
|
282
|
+
background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3crect x='3' y='7' width='10' height='2' rx='1'/%3e%3c/svg%3e");
|
|
283
|
+
background-position: center;
|
|
284
|
+
background-repeat: no-repeat;
|
|
285
|
+
background-size: 100% 100%;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/* Dark mode */
|
|
289
|
+
.dark [data-component="checkbox"]:not(:checked) {
|
|
290
|
+
background-color: color-mix(in oklch, var(--input) 30%, transparent);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/* ===================================================================
|
|
294
|
+
RADIO
|
|
295
|
+
Usage: <%= f.radio_button :plan, "basic", data: { component: "radio" } %>
|
|
296
|
+
=================================================================== */
|
|
297
|
+
[data-component="radio"] {
|
|
298
|
+
@apply size-4 shrink-0;
|
|
299
|
+
@apply rounded-full border;
|
|
300
|
+
@apply cursor-pointer appearance-none;
|
|
301
|
+
@apply transition-shadow;
|
|
302
|
+
|
|
303
|
+
border-color: var(--input);
|
|
304
|
+
background-color: transparent;
|
|
305
|
+
box-shadow: var(--shadow-xs, 0 1px 2px 0 rgb(0 0 0 / 0.05));
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
[data-component="radio"]:focus,
|
|
309
|
+
[data-component="radio"]:focus-visible {
|
|
310
|
+
@apply outline-none;
|
|
311
|
+
border-color: var(--ring);
|
|
312
|
+
box-shadow:
|
|
313
|
+
var(--shadow-xs, 0 1px 2px 0 rgb(0 0 0 / 0.05)),
|
|
314
|
+
0 0 0 3px color-mix(in oklch, var(--ring) 50%, transparent);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
[data-component="radio"]:checked {
|
|
318
|
+
border-color: var(--primary);
|
|
319
|
+
background-color: var(--primary);
|
|
320
|
+
|
|
321
|
+
/* Inner dot */
|
|
322
|
+
background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3ccircle cx='8' cy='8' r='3'/%3e%3c/svg%3e");
|
|
323
|
+
background-position: center;
|
|
324
|
+
background-repeat: no-repeat;
|
|
325
|
+
background-size: 100% 100%;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
[data-component="radio"]:disabled {
|
|
329
|
+
@apply cursor-not-allowed opacity-50;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/* Dark mode */
|
|
333
|
+
.dark [data-component="radio"]:not(:checked) {
|
|
334
|
+
background-color: color-mix(in oklch, var(--input) 30%, transparent);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/* ===================================================================
|
|
338
|
+
SWITCH (Toggle)
|
|
339
|
+
Usage: <%= f.check_box :notifications, data: { component: "switch" } %>
|
|
340
|
+
=================================================================== */
|
|
341
|
+
[data-component="switch"] {
|
|
342
|
+
@apply h-5 w-9 shrink-0;
|
|
343
|
+
@apply rounded-full border-2 border-transparent;
|
|
344
|
+
@apply cursor-pointer appearance-none;
|
|
345
|
+
@apply transition-all duration-200;
|
|
346
|
+
|
|
347
|
+
background-color: var(--input);
|
|
348
|
+
|
|
349
|
+
/* Thumb */
|
|
350
|
+
background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 20 20' xmlns='http://www.w3.org/2000/svg'%3e%3ccircle cx='10' cy='10' r='8' fill='white'/%3e%3c/svg%3e");
|
|
351
|
+
background-position: left 2px center;
|
|
352
|
+
background-repeat: no-repeat;
|
|
353
|
+
background-size: 16px 16px;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
[data-component="switch"]:focus,
|
|
357
|
+
[data-component="switch"]:focus-visible {
|
|
358
|
+
@apply outline-none;
|
|
359
|
+
box-shadow: 0 0 0 3px color-mix(in oklch, var(--ring) 50%, transparent);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
[data-component="switch"]:checked {
|
|
363
|
+
background-color: var(--primary);
|
|
364
|
+
background-position: right 2px center;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
[data-component="switch"]:disabled {
|
|
368
|
+
@apply cursor-not-allowed opacity-50;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
/* ===================================================================
|
|
372
|
+
BUTTON
|
|
373
|
+
Usage:
|
|
374
|
+
<%= f.submit "Save", data: { component: "button", variant: "primary" } %>
|
|
375
|
+
<%= button_to "Delete", path, data: { component: "button", variant: "destructive" } %>
|
|
376
|
+
<%= link_to "Cancel", path, data: { component: "button", variant: "outline" } %>
|
|
377
|
+
Matches: inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md
|
|
378
|
+
text-sm font-medium transition-colors outline-none
|
|
379
|
+
focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2
|
|
380
|
+
disabled:pointer-events-none disabled:opacity-50
|
|
381
|
+
[&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0
|
|
382
|
+
=================================================================== */
|
|
383
|
+
|
|
384
|
+
/* Base button styles */
|
|
385
|
+
[data-component="button"] {
|
|
386
|
+
@apply inline-flex items-center justify-center gap-2;
|
|
387
|
+
@apply whitespace-nowrap rounded-md;
|
|
388
|
+
@apply text-sm font-medium;
|
|
389
|
+
@apply h-9 px-4 py-2;
|
|
390
|
+
@apply cursor-default;
|
|
391
|
+
@apply outline-none;
|
|
392
|
+
@apply transition-colors;
|
|
393
|
+
@apply select-none;
|
|
394
|
+
|
|
395
|
+
/* SVG icons inside buttons */
|
|
396
|
+
& svg {
|
|
397
|
+
@apply pointer-events-none size-4 shrink-0;
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
/* Disabled state - applies to all variants */
|
|
402
|
+
[data-component="button"]:disabled,
|
|
403
|
+
[data-component="button"][aria-disabled="true"] {
|
|
404
|
+
@apply pointer-events-none opacity-50;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
/* Focus ring - applies to all variants */
|
|
408
|
+
[data-component="button"]:focus-visible {
|
|
409
|
+
box-shadow:
|
|
410
|
+
0 0 0 2px var(--background),
|
|
411
|
+
0 0 0 4px var(--ring);
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
/* ----- Variants ----- */
|
|
415
|
+
|
|
416
|
+
/* Default (Primary) */
|
|
417
|
+
[data-component="button"],
|
|
418
|
+
[data-component="button"][data-variant="default"],
|
|
419
|
+
[data-component="button"][data-variant="primary"] {
|
|
420
|
+
background-color: var(--primary);
|
|
421
|
+
color: var(--primary-foreground);
|
|
422
|
+
box-shadow: var(--shadow-sm, 0 1px 2px 0 rgb(0 0 0 / 0.05));
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
[data-component="button"]:hover,
|
|
426
|
+
[data-component="button"][data-variant="default"]:hover,
|
|
427
|
+
[data-component="button"][data-variant="primary"]:hover {
|
|
428
|
+
background-color: color-mix(in oklch, var(--primary) 90%, black);
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
/* Destructive */
|
|
432
|
+
[data-component="button"][data-variant="destructive"] {
|
|
433
|
+
background-color: var(--destructive);
|
|
434
|
+
color: var(--destructive-foreground);
|
|
435
|
+
box-shadow: var(--shadow-sm, 0 1px 2px 0 rgb(0 0 0 / 0.05));
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
[data-component="button"][data-variant="destructive"]:hover {
|
|
439
|
+
background-color: color-mix(in oklch, var(--destructive) 90%, black);
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
[data-component="button"][data-variant="destructive"]:focus-visible {
|
|
443
|
+
box-shadow:
|
|
444
|
+
0 0 0 2px var(--background),
|
|
445
|
+
0 0 0 4px var(--destructive);
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
/* Outline */
|
|
449
|
+
[data-component="button"][data-variant="outline"] {
|
|
450
|
+
background-color: var(--background);
|
|
451
|
+
color: var(--foreground);
|
|
452
|
+
border: 1px solid var(--input);
|
|
453
|
+
box-shadow: var(--shadow-sm, 0 1px 2px 0 rgb(0 0 0 / 0.05));
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
[data-component="button"][data-variant="outline"]:hover {
|
|
457
|
+
background-color: var(--accent);
|
|
458
|
+
color: var(--accent-foreground);
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
/* Secondary */
|
|
462
|
+
[data-component="button"][data-variant="secondary"] {
|
|
463
|
+
background-color: var(--secondary);
|
|
464
|
+
color: var(--secondary-foreground);
|
|
465
|
+
box-shadow: var(--shadow-sm, 0 1px 2px 0 rgb(0 0 0 / 0.05));
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
[data-component="button"][data-variant="secondary"]:hover {
|
|
469
|
+
background-color: color-mix(in oklch, var(--secondary) 80%, black);
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
/* Ghost */
|
|
473
|
+
[data-component="button"][data-variant="ghost"] {
|
|
474
|
+
background-color: transparent;
|
|
475
|
+
color: var(--foreground);
|
|
476
|
+
box-shadow: none;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
[data-component="button"][data-variant="ghost"]:hover {
|
|
480
|
+
background-color: var(--accent);
|
|
481
|
+
color: var(--accent-foreground);
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
/* Link */
|
|
485
|
+
[data-component="button"][data-variant="link"] {
|
|
486
|
+
background-color: transparent;
|
|
487
|
+
color: var(--primary);
|
|
488
|
+
text-decoration-line: underline;
|
|
489
|
+
text-underline-offset: 4px;
|
|
490
|
+
box-shadow: none;
|
|
491
|
+
@apply h-auto px-0 py-0;
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
[data-component="button"][data-variant="link"]:hover {
|
|
495
|
+
text-decoration-line: underline;
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
/* ----- Sizes ----- */
|
|
499
|
+
|
|
500
|
+
[data-component="button"][data-size="sm"] {
|
|
501
|
+
@apply h-8 px-3 text-xs rounded-md;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
[data-component="button"][data-size="lg"] {
|
|
505
|
+
@apply h-10 px-8 rounded-md;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
[data-component="button"][data-size="icon"] {
|
|
509
|
+
@apply size-9 p-0;
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
[data-component="button"][data-size="icon-sm"] {
|
|
513
|
+
@apply size-8 p-0;
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
[data-component="button"][data-size="icon-lg"] {
|
|
517
|
+
@apply size-10 p-0;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
/* ===================================================================
|
|
521
|
+
HELP TEXT / DESCRIPTION
|
|
522
|
+
Usage: <p data-form-part="description">We'll never share your email.</p>
|
|
523
|
+
=================================================================== */
|
|
524
|
+
[data-form-part="description"] {
|
|
525
|
+
@apply text-sm;
|
|
526
|
+
color: var(--muted-foreground);
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
/* ===================================================================
|
|
530
|
+
ERROR TEXT / MESSAGE
|
|
531
|
+
Usage: <p data-form-part="error">Email is required</p>
|
|
532
|
+
=================================================================== */
|
|
533
|
+
[data-form-part="error"] {
|
|
534
|
+
@apply text-sm font-medium;
|
|
535
|
+
color: var(--destructive);
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
/* Rails error wrapper compatibility */
|
|
539
|
+
.field_with_errors {
|
|
540
|
+
@apply contents;
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
.field_with_errors [data-component="input"],
|
|
544
|
+
.field_with_errors [data-component="textarea"],
|
|
545
|
+
.field_with_errors [data-component="select"] {
|
|
546
|
+
border-color: var(--destructive);
|
|
547
|
+
box-shadow:
|
|
548
|
+
var(--shadow-xs, 0 1px 2px 0 rgb(0 0 0 / 0.05)),
|
|
549
|
+
0 0 0 3px color-mix(in oklch, var(--destructive) 20%, transparent);
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
/* ===================================================================
|
|
553
|
+
FORM ACTIONS (Submit area)
|
|
554
|
+
Usage: <div data-form-part="actions">
|
|
555
|
+
=================================================================== */
|
|
556
|
+
[data-form-part="actions"] {
|
|
557
|
+
@apply flex items-center gap-3 pt-4;
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
[data-form-part="actions"][data-align="end"] {
|
|
561
|
+
@apply justify-end;
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
[data-form-part="actions"][data-align="between"] {
|
|
565
|
+
@apply justify-between;
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
/* ===================================================================
|
|
569
|
+
FIELDSET & LEGEND
|
|
570
|
+
=================================================================== */
|
|
571
|
+
[data-component="fieldset"] {
|
|
572
|
+
@apply space-y-4 rounded-lg border p-4;
|
|
573
|
+
border-color: var(--border);
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
[data-component="legend"],
|
|
577
|
+
[data-component="fieldset"] > legend {
|
|
578
|
+
@apply px-2 text-sm font-medium;
|
|
579
|
+
color: var(--foreground);
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
/* ===================================================================
|
|
583
|
+
INPUT GROUP (Prefix/Suffix support)
|
|
584
|
+
=================================================================== */
|
|
585
|
+
[data-component="input-group"] {
|
|
586
|
+
@apply relative flex items-center;
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
[data-component="input-group"] [data-component="input"] {
|
|
590
|
+
@apply flex-1;
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
[data-input-group-part="prefix"],
|
|
594
|
+
[data-input-group-part="suffix"] {
|
|
595
|
+
@apply absolute flex items-center justify-center;
|
|
596
|
+
@apply h-full px-3;
|
|
597
|
+
@apply text-sm;
|
|
598
|
+
color: var(--muted-foreground);
|
|
599
|
+
pointer-events: none;
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
[data-input-group-part="prefix"] {
|
|
603
|
+
@apply left-0;
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
[data-input-group-part="suffix"] {
|
|
607
|
+
@apply right-0;
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
/* Adjust input padding when prefix/suffix present */
|
|
611
|
+
[data-component="input-group"]:has([data-input-group-part="prefix"]) [data-component="input"] {
|
|
612
|
+
@apply pl-9;
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
[data-component="input-group"]:has([data-input-group-part="suffix"]) [data-component="input"] {
|
|
616
|
+
@apply pr-9;
|
|
617
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/* ===== Header Component Styles ===== */
|
|
2
|
+
/*
|
|
3
|
+
* Page header component for use with sidebar layouts.
|
|
4
|
+
* Provides a consistent header with proper height and alignment.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/* ===== Base Styles ===== */
|
|
8
|
+
[data-component="header"] {
|
|
9
|
+
display: flex;
|
|
10
|
+
align-items: center;
|
|
11
|
+
flex-shrink: 0;
|
|
12
|
+
height: var(--header-height, 3.5rem);
|
|
13
|
+
border-bottom: 1px solid var(--border);
|
|
14
|
+
background-color: var(--background);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/* Inner container */
|
|
18
|
+
[data-component="header"] [data-header-part="inner"] {
|
|
19
|
+
display: flex;
|
|
20
|
+
width: 100%;
|
|
21
|
+
align-items: center;
|
|
22
|
+
@apply gap-1 px-4 lg:gap-2 lg:px-6;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/* ===== Inset Variant (rounded top corners) ===== */
|
|
26
|
+
/*
|
|
27
|
+
* When header is inside sidebar/inset, add rounded top corners
|
|
28
|
+
* to match the inset content area styling.
|
|
29
|
+
*/
|
|
30
|
+
[data-sidebar-part="inset"] [data-component="header"] {
|
|
31
|
+
@apply rounded-t-xl;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/* ===== Header Elements ===== */
|
|
35
|
+
|
|
36
|
+
/* Separator between trigger and content */
|
|
37
|
+
[data-component="header"] [data-component="separator"][data-orientation="vertical"] {
|
|
38
|
+
height: 1rem;
|
|
39
|
+
@apply mx-2;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/* Title text */
|
|
43
|
+
[data-component="header"] [data-header-part="title"] {
|
|
44
|
+
@apply text-sm font-medium;
|
|
45
|
+
color: var(--foreground);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/* Actions container (right side) */
|
|
49
|
+
[data-component="header"] [data-header-part="actions"] {
|
|
50
|
+
display: flex;
|
|
51
|
+
align-items: center;
|
|
52
|
+
margin-left: auto;
|
|
53
|
+
@apply gap-2;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/* ===== Responsive ===== */
|
|
57
|
+
@media (max-width: 1024px) {
|
|
58
|
+
[data-component="header"] [data-header-part="inner"] {
|
|
59
|
+
@apply gap-1 px-4;
|
|
60
|
+
}
|
|
61
|
+
}
|