maquina-components 0.5.1 → 0.6.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 (55) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +76 -2
  3. data/app/assets/stylesheets/alert.css +169 -135
  4. data/app/assets/stylesheets/badge.css +143 -138
  5. data/app/assets/stylesheets/breadcrumbs.css +183 -166
  6. data/app/assets/stylesheets/calendar.css +219 -206
  7. data/app/assets/stylesheets/card.css +126 -120
  8. data/app/assets/stylesheets/combobox.css +208 -191
  9. data/app/assets/stylesheets/date_picker.css +144 -134
  10. data/app/assets/stylesheets/drawer.css +158 -125
  11. data/app/assets/stylesheets/dropdown_menu.css +203 -190
  12. data/app/assets/stylesheets/empty.css +125 -117
  13. data/app/assets/stylesheets/form.css +625 -525
  14. data/app/assets/stylesheets/header.css +63 -47
  15. data/app/assets/stylesheets/menu_button.css +121 -98
  16. data/app/assets/stylesheets/pagination.css +130 -125
  17. data/app/assets/stylesheets/separator.css +23 -11
  18. data/app/assets/stylesheets/sidebar.css +447 -401
  19. data/app/assets/stylesheets/stats.css +77 -67
  20. data/app/assets/stylesheets/table.css +184 -166
  21. data/app/assets/stylesheets/toast.css +287 -278
  22. data/app/assets/stylesheets/toggle_group.css +132 -139
  23. data/app/assets/tailwind/maquina_components_engine/engine.css +54 -15
  24. data/app/assets/tailwind/maquina_components_engine/tokens.css +101 -0
  25. data/app/helpers/maquina_components/combobox_helper.rb +32 -24
  26. data/app/helpers/maquina_components/components_helper.rb +30 -6
  27. data/app/helpers/maquina_components/dropdown_menu_helper.rb +12 -12
  28. data/app/helpers/maquina_components/icons_helper.rb +26 -2
  29. data/app/javascript/controllers/drawer_trigger_controller.js +24 -3
  30. data/app/views/components/_label.html.erb +14 -0
  31. data/app/views/components/_table.html.erb +2 -2
  32. data/app/views/components/calendar/_week.html.erb +4 -12
  33. data/app/views/components/drawer/_close.html.erb +1 -1
  34. data/app/views/components/drawer/_description.html.erb +6 -0
  35. data/app/views/components/drawer/_header.html.erb +1 -1
  36. data/app/views/components/drawer/_provider.html.erb +13 -4
  37. data/app/views/components/drawer/_section.html.erb +7 -0
  38. data/app/views/components/drawer/_separator.html.erb +11 -0
  39. data/app/views/components/drawer/_title.html.erb +6 -0
  40. data/app/views/components/drawer/_trigger.html.erb +24 -7
  41. data/app/views/components/sidebar/_group_action.html.erb +26 -0
  42. data/app/views/components/sidebar/_menu_action.html.erb +28 -0
  43. data/app/views/components/sidebar/_menu_badge.html.erb +10 -0
  44. data/app/views/components/sidebar/_menu_button.html.erb +3 -2
  45. data/app/views/components/sidebar/_menu_link.html.erb +3 -2
  46. data/app/views/components/sidebar/_separator.html.erb +12 -0
  47. data/lib/generators/maquina_components/install/install_generator.rb +36 -1
  48. data/lib/generators/maquina_components/install/templates/maquina_components_helper.rb.tt +5 -0
  49. data/lib/generators/maquina_components/install/templates/shape_state_tokens.css.tt +76 -0
  50. data/lib/generators/maquina_components/install/templates/theme.css.tt +10 -3
  51. data/lib/maquina-components.rb +29 -1
  52. data/lib/maquina_components/doctor.rb +280 -0
  53. data/lib/maquina_components/version.rb +1 -1
  54. data/lib/tasks/maquina_components_tasks.rake +16 -4
  55. metadata +14 -2
@@ -1,207 +1,224 @@
1
1
  /* ===== Breadcrumbs Component Styles ===== */
2
2
  /* Comprehensive CSS using data-attribute selectors */
3
3
  /* Uses @apply for theme-customizable properties */
4
+ /*
5
+ * Cascade order in this file (full contract documented in form.css):
6
+ * 1. base [data-component="breadcrumbs"], [data-breadcrumb-part="…"]
7
+ * 2. sizes :where([data-component="breadcrumbs"][data-size="sm"]) …
8
+ * 3. variants :where([data-component="breadcrumbs"][data-separator="…"]) …
9
+ * 4. states […]:where(:hover, :focus-visible, [data-state="open"], .hidden)
10
+ * 5. dark — none; dark-mode differences are tokens (see tokens.css)
11
+ * Every rule is flattened to 0,1,0 with :where() and lives in
12
+ * @layer components, so a caller's utility class wins and source order is
13
+ * the only tie-breaker. (Rules whose target is a bare element or a
14
+ * pseudo-element read 0,1,1 — an element selector cannot be zeroed away.)
15
+ *
16
+ * Imported LAST, after dropdown_menu.css: the overflow popover below has to
17
+ * beat dropdown_menu.css's `position: absolute`, and since every rule is now a
18
+ * flat 0,1,0 that is decided by import order alone (it used to lean on a
19
+ * doubled attribute selector). See engine.css.
20
+ */
21
+
22
+ @layer components {
23
+ /* ===== Breadcrumbs Container (nav) ===== */
24
+ [data-component="breadcrumbs"] {
25
+ /* Container has no default styles - allows flexible placement */
26
+ }
4
27
 
5
- /* ===== Breadcrumbs Container (nav) ===== */
6
- [data-component="breadcrumbs"] {
7
- /* Container has no default styles - allows flexible placement */
8
- }
9
-
10
- /* ===== Breadcrumbs List ===== */
11
- [data-breadcrumb-part="list"] {
12
- @apply flex flex-wrap items-center gap-1.5;
13
- @apply text-sm break-words;
14
- list-style: none;
15
- padding: 0;
16
- margin: 0;
17
- }
18
-
19
- /* Compact variant */
20
- [data-component="breadcrumbs"][data-size="sm"] [data-breadcrumb-part="list"] {
21
- @apply gap-1 text-xs;
22
- }
23
-
24
- /* ===== Breadcrumb Item ===== */
25
- [data-breadcrumb-part="item"] {
26
- @apply inline-flex items-center;
27
- }
28
-
29
- /* Hidden state for responsive collapsing */
30
- [data-breadcrumb-part="item"].hidden {
31
- @apply hidden;
32
- }
28
+ /* ===== Breadcrumbs List ===== */
29
+ [data-breadcrumb-part="list"] {
30
+ @apply flex flex-wrap items-center gap-1.5;
31
+ @apply text-sm break-words;
32
+ list-style: none;
33
+ padding: 0;
34
+ margin: 0;
35
+ }
33
36
 
34
- /* ===== Breadcrumb Link ===== */
35
- [data-breadcrumb-part="link"] {
36
- @apply inline-flex items-center;
37
- @apply text-sm font-medium;
38
- @apply underline-offset-4;
39
- @apply transition-colors;
40
- color: var(--muted-foreground);
41
- }
37
+ /* Compact variant */
38
+ :where([data-component="breadcrumbs"][data-size="sm"]) [data-breadcrumb-part="list"] {
39
+ @apply gap-1 text-xs;
40
+ }
42
41
 
43
- [data-breadcrumb-part="link"]:hover {
44
- color: var(--foreground);
45
- @apply underline;
46
- }
42
+ /* ===== Breadcrumb Item ===== */
43
+ [data-breadcrumb-part="item"] {
44
+ @apply inline-flex items-center;
45
+ }
47
46
 
48
- [data-breadcrumb-part="link"]:focus-visible {
49
- @apply outline-none underline;
50
- color: var(--foreground);
51
- }
47
+ /* Hidden state for responsive collapsing */
48
+ [data-breadcrumb-part="item"]:where(.hidden) {
49
+ @apply hidden;
50
+ }
52
51
 
53
- /* Link with icon */
54
- [data-breadcrumb-part="link"] svg {
55
- @apply size-3.5 shrink-0;
56
- }
52
+ /* ===== Breadcrumb Link ===== */
53
+ [data-breadcrumb-part="link"] {
54
+ @apply inline-flex items-center;
55
+ @apply text-sm;
56
+ font-weight: var(--label-weight, 500);
57
+ @apply underline-offset-4;
58
+ @apply transition-colors;
59
+ color: var(--muted-foreground);
60
+ }
57
61
 
58
- [data-breadcrumb-part="link"] svg:first-child {
59
- @apply mr-1.5;
60
- }
62
+ /* Link with icon */
63
+ [data-breadcrumb-part="link"] svg {
64
+ @apply size-3.5 shrink-0;
65
+ }
61
66
 
62
- /* ===== Breadcrumb Page (current) ===== */
63
- [data-breadcrumb-part="page"] {
64
- @apply inline-flex items-center;
65
- @apply text-sm font-medium;
66
- color: var(--foreground);
67
- }
67
+ [data-breadcrumb-part="link"] svg:where(:first-child) {
68
+ @apply mr-1.5;
69
+ }
68
70
 
69
- /* Page with icon */
70
- [data-breadcrumb-part="page"] svg {
71
- @apply size-3.5 shrink-0;
72
- }
71
+ [data-breadcrumb-part="link"]:where(:hover) {
72
+ color: var(--foreground);
73
+ @apply underline;
74
+ }
73
75
 
74
- [data-breadcrumb-part="page"] svg:first-child {
75
- @apply mr-1.5;
76
- }
76
+ [data-breadcrumb-part="link"]:where(:focus-visible) {
77
+ @apply outline-none underline;
78
+ color: var(--foreground);
79
+ }
77
80
 
78
- /* ===== Breadcrumb Separator ===== */
79
- [data-breadcrumb-part="separator"] {
80
- @apply inline-flex items-center;
81
- color: var(--muted-foreground);
82
- }
81
+ /* ===== Breadcrumb Page (current) ===== */
82
+ [data-breadcrumb-part="page"] {
83
+ @apply inline-flex items-center;
84
+ @apply text-sm;
85
+ font-weight: var(--label-weight, 500);
86
+ color: var(--foreground);
87
+ }
83
88
 
84
- [data-breadcrumb-part="separator"] svg {
85
- @apply size-3.5 shrink-0;
86
- }
89
+ /* Page with icon */
90
+ [data-breadcrumb-part="page"] svg {
91
+ @apply size-3.5 shrink-0;
92
+ }
87
93
 
88
- /* Hidden state for responsive collapsing */
89
- [data-breadcrumb-part="separator"].hidden {
90
- @apply hidden;
91
- }
94
+ [data-breadcrumb-part="page"] svg:where(:first-child) {
95
+ @apply mr-1.5;
96
+ }
92
97
 
93
- /* ===== Breadcrumb Ellipsis ===== */
94
- [data-breadcrumb-part="ellipsis"] {
95
- @apply inline-flex size-9 items-center justify-center;
96
- color: var(--muted-foreground);
97
- cursor: pointer;
98
- }
98
+ /* ===== Breadcrumb Separator ===== */
99
+ [data-breadcrumb-part="separator"] {
100
+ @apply inline-flex items-center;
101
+ color: var(--muted-foreground);
102
+ }
99
103
 
100
- [data-breadcrumb-part="ellipsis"] svg {
101
- @apply size-4 shrink-0;
102
- }
104
+ [data-breadcrumb-part="separator"] svg {
105
+ @apply size-3.5 shrink-0;
106
+ }
103
107
 
104
- /* Ellipsis as dropdown trigger */
105
- [data-breadcrumb-part="ellipsis"][data-state="open"],
106
- [data-breadcrumb-part="ellipsis"]:hover {
107
- color: var(--foreground);
108
- }
108
+ /* Hidden state for responsive collapsing */
109
+ [data-breadcrumb-part="separator"]:where(.hidden) {
110
+ @apply hidden;
111
+ }
109
112
 
110
- /* ===== Overflow Dropdown (top-layer popover) ===== */
111
- /* Visual styling comes from dropdown_menu.css via data-dropdown-menu-part.
112
- Anchor positioning places the popover in modern browsers;
113
- breadcrumb_controller falls back to measured coordinates elsewhere.
114
- The doubled attribute selector outranks dropdown_menu.css's
115
- position: absolute regardless of import order. */
116
- @supports (anchor-name: --a) and (position-area: block-end) {
113
+ /* ===== Breadcrumb Ellipsis ===== */
117
114
  [data-breadcrumb-part="ellipsis"] {
118
- anchor-name: --maquina-breadcrumbs;
115
+ @apply inline-flex size-9 items-center justify-center;
116
+ color: var(--muted-foreground);
117
+ cursor: pointer;
119
118
  }
120
119
 
121
- [data-breadcrumb-part="dropdown"][data-dropdown-menu-part="content"] {
122
- position: fixed;
123
- position-anchor: --maquina-breadcrumbs;
124
- position-area: block-end span-inline-end;
125
- margin-block-start: 4px;
126
- position-try-fallbacks: flip-block;
120
+ [data-breadcrumb-part="ellipsis"] svg {
121
+ @apply size-4 shrink-0;
127
122
  }
128
- }
129
123
 
130
- /* ===== Responsive Behavior ===== */
131
- /* Force nowrap when responsive controller is active so scrollWidth detects overflow */
132
- [data-controller="breadcrumb"] [data-breadcrumb-part="list"] {
133
- @apply flex-nowrap overflow-hidden;
134
- }
124
+ /* Ellipsis as dropdown trigger */
125
+ [data-breadcrumb-part="ellipsis"]:where([data-state="open"], :hover) {
126
+ color: var(--foreground);
127
+ }
135
128
 
136
- /* Items must not shrink or wrap text, otherwise flex compresses them and scrollWidth never exceeds clientWidth */
137
- [data-controller="breadcrumb"] [data-breadcrumb-part="item"],
138
- [data-controller="breadcrumb"] [data-breadcrumb-part="separator"] {
139
- @apply shrink-0 whitespace-nowrap;
140
- }
129
+ /* ===== Overflow Dropdown (top-layer popover) ===== */
130
+ /* Visual styling comes from dropdown_menu.css via data-dropdown-menu-part.
131
+ Anchor positioning places the popover in modern browsers;
132
+ breadcrumb_controller falls back to measured coordinates elsewhere. */
133
+ @supports (anchor-name: --a) and (position-area: block-end) {
134
+ [data-breadcrumb-part="ellipsis"] {
135
+ anchor-name: --maquina-breadcrumbs;
136
+ }
137
+
138
+ [data-breadcrumb-part="dropdown"]:where([data-dropdown-menu-part="content"]) {
139
+ position: fixed;
140
+ position-anchor: --maquina-breadcrumbs;
141
+ position-area: block-end span-inline-end;
142
+ margin-block-start: 4px;
143
+ position-try-fallbacks: flip-block;
144
+ }
145
+ }
141
146
 
142
- /* Allow the last item (current page) to truncate gracefully instead of hard-clipping */
143
- [data-controller="breadcrumb"] [data-breadcrumb-part="item"]:last-child {
144
- @apply shrink min-w-0 overflow-hidden;
145
- text-overflow: ellipsis;
146
- }
147
+ /* ===== Responsive Behavior ===== */
148
+ /* Force nowrap when responsive controller is active so scrollWidth detects overflow */
149
+ :where([data-controller="breadcrumb"]) [data-breadcrumb-part="list"] {
150
+ @apply flex-nowrap overflow-hidden;
151
+ }
147
152
 
148
- [data-controller="breadcrumb"] [data-breadcrumb-part="item"]:last-child [data-breadcrumb-part="page"],
149
- [data-controller="breadcrumb"] [data-breadcrumb-part="item"]:last-child [data-breadcrumb-part="link"] {
150
- @apply overflow-hidden;
151
- text-overflow: ellipsis;
152
- }
153
+ /* Items must not shrink or wrap text, otherwise flex compresses them and scrollWidth never exceeds clientWidth */
154
+ :where([data-controller="breadcrumb"]) [data-breadcrumb-part="item"],
155
+ :where([data-controller="breadcrumb"]) [data-breadcrumb-part="separator"] {
156
+ @apply shrink-0 whitespace-nowrap;
157
+ }
153
158
 
154
- /* Items hidden by Stimulus controller */
155
- [data-controller="breadcrumb"] [data-breadcrumb-target="item"].hidden,
156
- [data-controller="breadcrumb"] [data-breadcrumb-target="ellipsisSeparator"].hidden {
157
- @apply hidden;
158
- }
159
+ /* Allow the last item (current page) to truncate gracefully instead of hard-clipping */
160
+ :where([data-controller="breadcrumb"]) [data-breadcrumb-part="item"]:where(:last-child) {
161
+ @apply shrink min-w-0 overflow-hidden;
162
+ text-overflow: ellipsis;
163
+ }
159
164
 
160
- /* Ellipsis shown by Stimulus controller */
161
- [data-controller="breadcrumb"] [data-breadcrumb-target="ellipsis"]:not(.hidden) {
162
- @apply inline-flex;
163
- }
165
+ :where([data-controller="breadcrumb"] [data-breadcrumb-part="item"]:last-child) :is([data-breadcrumb-part="page"], [data-breadcrumb-part="link"]) {
166
+ @apply overflow-hidden;
167
+ text-overflow: ellipsis;
168
+ }
164
169
 
165
- /* ===== Mobile Adjustments ===== */
166
- @media (max-width: 640px) {
167
- [data-breadcrumb-part="list"] {
168
- @apply gap-1;
170
+ /* Items hidden by Stimulus controller */
171
+ :where([data-controller="breadcrumb"]) [data-breadcrumb-target="item"]:where(.hidden),
172
+ :where([data-controller="breadcrumb"]) [data-breadcrumb-target="ellipsisSeparator"]:where(.hidden) {
173
+ @apply hidden;
169
174
  }
170
175
 
171
- /* Hide middle items on very small screens by default */
172
- [data-component="breadcrumbs"][data-auto-collapse] [data-breadcrumb-part="item"]:not(:first-child):not(:last-child) {
173
- @apply hidden sm:inline-flex;
176
+ /* Ellipsis shown by Stimulus controller */
177
+ :where([data-controller="breadcrumb"]) [data-breadcrumb-target="ellipsis"]:where(:not(.hidden)) {
178
+ @apply inline-flex;
174
179
  }
175
- }
176
180
 
177
- /* ===== Variants ===== */
181
+ /* ===== Mobile Adjustments ===== */
182
+ @media (max-width: 640px) {
183
+ [data-breadcrumb-part="list"] {
184
+ @apply gap-1;
185
+ }
178
186
 
179
- /* Slash separator variant */
180
- [data-component="breadcrumbs"][data-separator="slash"] [data-breadcrumb-part="separator"]::before {
181
- content: "/";
182
- @apply px-1;
183
- }
187
+ /* Hide middle items on very small screens by default */
188
+ :where([data-component="breadcrumbs"][data-auto-collapse]) [data-breadcrumb-part="item"]:where(:not(:first-child):not(:last-child)) {
189
+ @apply hidden sm:inline-flex;
190
+ }
191
+ }
184
192
 
185
- [data-component="breadcrumbs"][data-separator="slash"] [data-breadcrumb-part="separator"] svg {
186
- @apply hidden;
187
- }
193
+ /* ===== Separator Variants ===== */
188
194
 
189
- /* Dot separator variant */
190
- [data-component="breadcrumbs"][data-separator="dot"] [data-breadcrumb-part="separator"]::before {
191
- content: "";
192
- @apply px-1;
193
- }
195
+ /* Slash separator variant */
196
+ :where([data-component="breadcrumbs"][data-separator="slash"]) [data-breadcrumb-part="separator"]::before {
197
+ content: "/";
198
+ @apply px-1;
199
+ }
194
200
 
195
- [data-component="breadcrumbs"][data-separator="dot"] [data-breadcrumb-part="separator"] svg {
196
- @apply hidden;
197
- }
201
+ :where([data-component="breadcrumbs"][data-separator="slash"]) [data-breadcrumb-part="separator"] svg {
202
+ @apply hidden;
203
+ }
198
204
 
199
- /* Arrow separator variant */
200
- [data-component="breadcrumbs"][data-separator="arrow"] [data-breadcrumb-part="separator"]::before {
201
- content: "";
202
- @apply px-1;
203
- }
205
+ /* Dot separator variant */
206
+ :where([data-component="breadcrumbs"][data-separator="dot"]) [data-breadcrumb-part="separator"]::before {
207
+ content: "";
208
+ @apply px-1;
209
+ }
210
+
211
+ :where([data-component="breadcrumbs"][data-separator="dot"]) [data-breadcrumb-part="separator"] svg {
212
+ @apply hidden;
213
+ }
214
+
215
+ /* Arrow separator variant */
216
+ :where([data-component="breadcrumbs"][data-separator="arrow"]) [data-breadcrumb-part="separator"]::before {
217
+ content: "→";
218
+ @apply px-1;
219
+ }
204
220
 
205
- [data-component="breadcrumbs"][data-separator="arrow"] [data-breadcrumb-part="separator"] svg {
206
- @apply hidden;
221
+ :where([data-component="breadcrumbs"][data-separator="arrow"]) [data-breadcrumb-part="separator"] svg {
222
+ @apply hidden;
223
+ }
207
224
  }