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,145 @@
|
|
|
1
|
+
/* ===== Badge Component Styles ===== */
|
|
2
|
+
/*
|
|
3
|
+
* Badge component for status indicators, tags, counts, and labels.
|
|
4
|
+
* Uses data attributes for styling to avoid inline utility classes.
|
|
5
|
+
* Fully compatible with dark mode and supports icons.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/* ===== Base Badge Styles ===== */
|
|
9
|
+
[data-component="badge"] {
|
|
10
|
+
/* Layout */
|
|
11
|
+
display: inline-flex;
|
|
12
|
+
align-items: center;
|
|
13
|
+
white-space: nowrap;
|
|
14
|
+
@apply gap-1 rounded-md border border-transparent font-medium leading-none;
|
|
15
|
+
|
|
16
|
+
/* Transitions */
|
|
17
|
+
@apply transition-[background-color,border-color,color,opacity] duration-150;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/* ===== Icon Support ===== */
|
|
21
|
+
[data-component="badge"] svg {
|
|
22
|
+
/* Scales with font size using em */
|
|
23
|
+
width: 0.875em;
|
|
24
|
+
height: 0.875em;
|
|
25
|
+
@apply shrink-0 pointer-events-none;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/* ===== Size Variants ===== */
|
|
29
|
+
|
|
30
|
+
/* Small size - compact badges for inline use */
|
|
31
|
+
[data-component="badge"][data-size="sm"] {
|
|
32
|
+
@apply text-xs px-2 py-0.5 h-5;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/* Medium size - default, balanced size */
|
|
36
|
+
[data-component="badge"][data-size="md"] {
|
|
37
|
+
@apply text-sm px-2.5 py-1 h-6;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/* Large size - prominent badges */
|
|
41
|
+
[data-component="badge"][data-size="lg"] {
|
|
42
|
+
@apply text-sm px-3 py-1.5 h-7;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/* ===== Visual Variants ===== */
|
|
46
|
+
|
|
47
|
+
/* Default variant - muted appearance */
|
|
48
|
+
[data-component="badge"][data-variant="default"] {
|
|
49
|
+
background-color: var(--muted);
|
|
50
|
+
color: var(--muted-foreground);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
[data-component="badge"][data-variant="default"]:hover {
|
|
54
|
+
@apply opacity-90;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/* Primary variant - brand color */
|
|
58
|
+
[data-component="badge"][data-variant="primary"] {
|
|
59
|
+
background-color: var(--primary-color);
|
|
60
|
+
color: var(--primary-foreground-color);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
[data-component="badge"][data-variant="primary"]:hover {
|
|
64
|
+
@apply opacity-90;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/* Secondary variant - subtle appearance */
|
|
68
|
+
[data-component="badge"][data-variant="secondary"] {
|
|
69
|
+
background-color: var(--secondary);
|
|
70
|
+
color: var(--secondary-foreground);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
[data-component="badge"][data-variant="secondary"]:hover {
|
|
74
|
+
@apply opacity-90;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/* Destructive variant - errors, deletions */
|
|
78
|
+
[data-component="badge"][data-variant="destructive"] {
|
|
79
|
+
background-color: var(--destructive);
|
|
80
|
+
color: var(--destructive-foreground);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
[data-component="badge"][data-variant="destructive"]:hover {
|
|
84
|
+
@apply opacity-90;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/* Success variant - positive status */
|
|
88
|
+
[data-component="badge"][data-variant="success"] {
|
|
89
|
+
background-color: var(--success);
|
|
90
|
+
color: var(--success-foreground);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
[data-component="badge"][data-variant="success"]:hover {
|
|
94
|
+
@apply opacity-90;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/* Warning variant - caution status */
|
|
98
|
+
[data-component="badge"][data-variant="warning"] {
|
|
99
|
+
background-color: var(--warning);
|
|
100
|
+
color: var(--warning-foreground);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
[data-component="badge"][data-variant="warning"]:hover {
|
|
104
|
+
@apply opacity-90;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/* Outline variant - transparent with border */
|
|
108
|
+
[data-component="badge"][data-variant="outline"] {
|
|
109
|
+
@apply bg-transparent;
|
|
110
|
+
color: var(--foreground);
|
|
111
|
+
border-color: var(--border);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
[data-component="badge"][data-variant="outline"]:hover {
|
|
115
|
+
background-color: var(--accent);
|
|
116
|
+
color: var(--accent-foreground);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/* ===== Interactive States (when used as link/button) ===== */
|
|
120
|
+
|
|
121
|
+
/* Focus state for keyboard navigation */
|
|
122
|
+
a[data-component="badge"]:focus-visible,
|
|
123
|
+
button[data-component="badge"]:focus-visible {
|
|
124
|
+
@apply outline-none;
|
|
125
|
+
box-shadow: 0 0 0 2px var(--background),
|
|
126
|
+
0 0 0 4px var(--border);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/* Active/pressed state */
|
|
130
|
+
a[data-component="badge"]:active,
|
|
131
|
+
button[data-component="badge"]:active {
|
|
132
|
+
@apply scale-[0.98];
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/* Disabled state (if used as button) */
|
|
136
|
+
button[data-component="badge"]:disabled {
|
|
137
|
+
@apply opacity-50 cursor-not-allowed pointer-events-none;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/* ===== Dark Mode ===== */
|
|
141
|
+
/*
|
|
142
|
+
* Dark mode is handled automatically through CSS variables.
|
|
143
|
+
* The theme variables change based on the .dark class on html/body.
|
|
144
|
+
* No additional dark mode styles needed here.
|
|
145
|
+
*/
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
/* ===== Breadcrumbs Component Styles ===== */
|
|
2
|
+
/* Comprehensive CSS using data-attribute selectors */
|
|
3
|
+
/* Uses @apply for theme-customizable properties */
|
|
4
|
+
|
|
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
|
+
}
|
|
33
|
+
|
|
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
|
+
}
|
|
42
|
+
|
|
43
|
+
[data-breadcrumb-part="link"]:hover {
|
|
44
|
+
color: var(--foreground);
|
|
45
|
+
@apply underline;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
[data-breadcrumb-part="link"]:focus-visible {
|
|
49
|
+
@apply outline-none underline;
|
|
50
|
+
color: var(--foreground);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/* Link with icon */
|
|
54
|
+
[data-breadcrumb-part="link"] svg {
|
|
55
|
+
@apply size-3.5 shrink-0;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
[data-breadcrumb-part="link"] svg:first-child {
|
|
59
|
+
@apply mr-1.5;
|
|
60
|
+
}
|
|
61
|
+
|
|
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
|
+
}
|
|
68
|
+
|
|
69
|
+
/* Page with icon */
|
|
70
|
+
[data-breadcrumb-part="page"] svg {
|
|
71
|
+
@apply size-3.5 shrink-0;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
[data-breadcrumb-part="page"] svg:first-child {
|
|
75
|
+
@apply mr-1.5;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/* ===== Breadcrumb Separator ===== */
|
|
79
|
+
[data-breadcrumb-part="separator"] {
|
|
80
|
+
@apply inline-flex items-center;
|
|
81
|
+
color: var(--muted-foreground);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
[data-breadcrumb-part="separator"] svg {
|
|
85
|
+
@apply size-3.5 shrink-0;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/* Hidden state for responsive collapsing */
|
|
89
|
+
[data-breadcrumb-part="separator"].hidden {
|
|
90
|
+
@apply hidden;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/* ===== Breadcrumb Ellipsis ===== */
|
|
94
|
+
[data-breadcrumb-part="ellipsis"] {
|
|
95
|
+
@apply inline-flex size-9 items-center justify-center;
|
|
96
|
+
color: var(--muted-foreground);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
[data-breadcrumb-part="ellipsis"] svg {
|
|
100
|
+
@apply size-4 shrink-0;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/* Ellipsis as dropdown trigger */
|
|
104
|
+
[data-breadcrumb-part="ellipsis"][data-state="open"],
|
|
105
|
+
[data-breadcrumb-part="ellipsis"]:hover {
|
|
106
|
+
color: var(--foreground);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/* ===== Responsive Behavior ===== */
|
|
110
|
+
/* Items hidden by Stimulus controller */
|
|
111
|
+
[data-controller="breadcrumb"] [data-breadcrumb-target="item"].hidden,
|
|
112
|
+
[data-controller="breadcrumb"] [data-breadcrumb-target="ellipsisSeparator"].hidden {
|
|
113
|
+
@apply hidden;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/* Ellipsis shown by Stimulus controller */
|
|
117
|
+
[data-controller="breadcrumb"] [data-breadcrumb-target="ellipsis"]:not(.hidden) {
|
|
118
|
+
@apply inline-flex;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/* ===== Mobile Adjustments ===== */
|
|
122
|
+
@media (max-width: 640px) {
|
|
123
|
+
[data-breadcrumb-part="list"] {
|
|
124
|
+
@apply gap-1;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/* Hide middle items on very small screens by default */
|
|
128
|
+
[data-component="breadcrumbs"][data-auto-collapse] [data-breadcrumb-part="item"]:not(:first-child):not(:last-child) {
|
|
129
|
+
@apply hidden sm:inline-flex;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/* ===== Variants ===== */
|
|
134
|
+
|
|
135
|
+
/* Slash separator variant */
|
|
136
|
+
[data-component="breadcrumbs"][data-separator="slash"] [data-breadcrumb-part="separator"]::before {
|
|
137
|
+
content: "/";
|
|
138
|
+
@apply px-1;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
[data-component="breadcrumbs"][data-separator="slash"] [data-breadcrumb-part="separator"] svg {
|
|
142
|
+
@apply hidden;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/* Dot separator variant */
|
|
146
|
+
[data-component="breadcrumbs"][data-separator="dot"] [data-breadcrumb-part="separator"]::before {
|
|
147
|
+
content: "•";
|
|
148
|
+
@apply px-1;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
[data-component="breadcrumbs"][data-separator="dot"] [data-breadcrumb-part="separator"] svg {
|
|
152
|
+
@apply hidden;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/* Arrow separator variant */
|
|
156
|
+
[data-component="breadcrumbs"][data-separator="arrow"] [data-breadcrumb-part="separator"]::before {
|
|
157
|
+
content: "→";
|
|
158
|
+
@apply px-1;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
[data-component="breadcrumbs"][data-separator="arrow"] [data-breadcrumb-part="separator"] svg {
|
|
162
|
+
@apply hidden;
|
|
163
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/* ===== Card Component Styles ===== */
|
|
2
|
+
/*
|
|
3
|
+
* Card component for grouping related content with header, body, and footer.
|
|
4
|
+
* Uses data attributes for styling to maintain consistency with other components.
|
|
5
|
+
* Fully compatible with dark mode via CSS variables.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/* ===== Base Card Styles ===== */
|
|
9
|
+
[data-component="card"] {
|
|
10
|
+
display: flex;
|
|
11
|
+
flex-direction: column;
|
|
12
|
+
|
|
13
|
+
/* Border & Radius */
|
|
14
|
+
@apply rounded-xl border shadow;
|
|
15
|
+
border-color: var(--border);
|
|
16
|
+
|
|
17
|
+
/* Colors */
|
|
18
|
+
background-color: var(--card);
|
|
19
|
+
color: var(--card-foreground);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/* ===== Card Header ===== */
|
|
23
|
+
[data-component="card"] [data-card-part="header"] {
|
|
24
|
+
display: flex;
|
|
25
|
+
flex-direction: column;
|
|
26
|
+
@apply gap-1.5 p-6;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/* Header with row layout (when using action) */
|
|
30
|
+
[data-component="card"] [data-card-part="header"][data-layout="row"] {
|
|
31
|
+
flex-direction: row;
|
|
32
|
+
align-items: center;
|
|
33
|
+
justify-content: space-between;
|
|
34
|
+
@apply gap-4;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/* ===== Card Title ===== */
|
|
38
|
+
[data-component="card"] [data-card-part="title"] {
|
|
39
|
+
@apply text-lg font-semibold leading-none tracking-tight;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/* Small title variant (for compact headers) */
|
|
43
|
+
[data-component="card"] [data-card-part="title"][data-size="sm"] {
|
|
44
|
+
@apply text-sm font-medium;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/* ===== Card Description ===== */
|
|
48
|
+
[data-component="card"] [data-card-part="description"] {
|
|
49
|
+
@apply text-sm;
|
|
50
|
+
color: var(--muted-foreground);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/* ===== Card Action ===== */
|
|
54
|
+
[data-component="card"] [data-card-part="action"] {
|
|
55
|
+
display: flex;
|
|
56
|
+
align-items: center;
|
|
57
|
+
flex-shrink: 0;
|
|
58
|
+
@apply gap-2;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/* ===== Card Content ===== */
|
|
62
|
+
[data-component="card"] [data-card-part="content"] {
|
|
63
|
+
@apply p-6 pt-0;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/* Content with top padding (when no header) */
|
|
67
|
+
[data-component="card"] [data-card-part="content"][data-spacing="full"] {
|
|
68
|
+
@apply pt-6;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/* ===== Card Footer ===== */
|
|
72
|
+
[data-component="card"] [data-card-part="footer"] {
|
|
73
|
+
display: flex;
|
|
74
|
+
align-items: center;
|
|
75
|
+
@apply p-6 pt-0;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/* Footer with top padding (when no content) */
|
|
79
|
+
[data-component="card"] [data-card-part="footer"][data-spacing="full"] {
|
|
80
|
+
@apply pt-6;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/* Footer alignment variants */
|
|
84
|
+
[data-component="card"] [data-card-part="footer"][data-align="between"] {
|
|
85
|
+
justify-content: space-between;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
[data-component="card"] [data-card-part="footer"][data-align="end"] {
|
|
89
|
+
justify-content: flex-end;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
[data-component="card"] [data-card-part="footer"][data-align="center"] {
|
|
93
|
+
justify-content: center;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/* ===== Icon Support ===== */
|
|
97
|
+
[data-component="card"] [data-card-part="header"] svg,
|
|
98
|
+
[data-component="card"] [data-card-part="action"] svg {
|
|
99
|
+
@apply size-4 shrink-0;
|
|
100
|
+
color: var(--muted-foreground);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/* ===== Interactive Card (when used as link/button) ===== */
|
|
104
|
+
a[data-component="card"],
|
|
105
|
+
button[data-component="card"] {
|
|
106
|
+
text-decoration: none;
|
|
107
|
+
cursor: pointer;
|
|
108
|
+
@apply transition-[border-color,box-shadow] duration-150;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
a[data-component="card"]:hover,
|
|
112
|
+
button[data-component="card"]:hover {
|
|
113
|
+
border-color: var(--accent);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
a[data-component="card"]:focus-visible,
|
|
117
|
+
button[data-component="card"]:focus-visible {
|
|
118
|
+
outline: none;
|
|
119
|
+
box-shadow: 0 0 0 2px var(--background),
|
|
120
|
+
0 0 0 4px var(--border);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/* ===== Dark Mode ===== */
|
|
124
|
+
/*
|
|
125
|
+
* Dark mode is handled automatically through CSS variables.
|
|
126
|
+
* The theme variables change based on the .dark class on html/body.
|
|
127
|
+
* No additional dark mode styles needed here.
|
|
128
|
+
*/
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
/* ===== Dropdown Menu Component Styles ===== */
|
|
2
|
+
/*
|
|
3
|
+
* Dropdown menu component for displaying actions triggered by a button.
|
|
4
|
+
* Uses data attributes for styling to avoid inline utility classes.
|
|
5
|
+
* Fully compatible with dark mode via CSS variables.
|
|
6
|
+
*
|
|
7
|
+
* Structure:
|
|
8
|
+
* - dropdown-menu (root with Stimulus controller)
|
|
9
|
+
* - trigger (button that opens menu)
|
|
10
|
+
* - content (positioned menu container)
|
|
11
|
+
* - label (section heading)
|
|
12
|
+
* - group (logical grouping)
|
|
13
|
+
* - item (clickable action)
|
|
14
|
+
* - separator (divider)
|
|
15
|
+
* - shortcut (keyboard hint)
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
/* ===== Root Container ===== */
|
|
19
|
+
[data-component="dropdown-menu"] {
|
|
20
|
+
position: relative;
|
|
21
|
+
display: inline-block;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/* ===== Trigger Button ===== */
|
|
25
|
+
/* Trigger uses button component styles via data-component="button" */
|
|
26
|
+
/* Additional dropdown-specific styles */
|
|
27
|
+
[data-component="dropdown-menu"] [data-dropdown-menu-target="trigger"] {
|
|
28
|
+
@apply gap-2;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/* Chevron icon rotation when open */
|
|
32
|
+
[data-component="dropdown-menu"] [data-dropdown-menu-target="chevron"] {
|
|
33
|
+
@apply size-4 shrink-0 opacity-50 transition-transform duration-200;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
[data-component="dropdown-menu"][data-state="open"] [data-dropdown-menu-target="chevron"] {
|
|
37
|
+
transform: rotate(180deg);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/* ===== Content Container ===== */
|
|
41
|
+
[data-dropdown-menu-part="content"] {
|
|
42
|
+
position: absolute;
|
|
43
|
+
z-index: 50;
|
|
44
|
+
display: none;
|
|
45
|
+
flex-direction: column;
|
|
46
|
+
overflow: hidden;
|
|
47
|
+
background-color: var(--popover, var(--background));
|
|
48
|
+
color: var(--popover-foreground, var(--foreground));
|
|
49
|
+
@apply min-w-[8rem] rounded-md border p-1 shadow-md;
|
|
50
|
+
border-color: var(--border);
|
|
51
|
+
|
|
52
|
+
/* Animation setup */
|
|
53
|
+
@apply transition-all duration-150 ease-out;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/* Visible state */
|
|
57
|
+
[data-dropdown-menu-part="content"][data-state="open"] {
|
|
58
|
+
display: flex;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/* Animation states */
|
|
62
|
+
[data-dropdown-menu-part="content"][data-state="open"] {
|
|
63
|
+
animation: dropdown-menu-in 150ms ease-out;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
[data-dropdown-menu-part="content"][data-state="closing"] {
|
|
67
|
+
display: flex;
|
|
68
|
+
animation: dropdown-menu-out 100ms ease-in forwards;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
@keyframes dropdown-menu-in {
|
|
72
|
+
from {
|
|
73
|
+
opacity: 0;
|
|
74
|
+
transform: scale(0.95) translateY(-4px);
|
|
75
|
+
}
|
|
76
|
+
to {
|
|
77
|
+
opacity: 1;
|
|
78
|
+
transform: scale(1) translateY(0);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
@keyframes dropdown-menu-out {
|
|
83
|
+
from {
|
|
84
|
+
opacity: 1;
|
|
85
|
+
transform: scale(1) translateY(0);
|
|
86
|
+
}
|
|
87
|
+
to {
|
|
88
|
+
opacity: 0;
|
|
89
|
+
transform: scale(0.95) translateY(-4px);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/* ===== Width Variants ===== */
|
|
94
|
+
[data-dropdown-menu-part="content"][data-width="default"] {
|
|
95
|
+
@apply min-w-[8rem];
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
[data-dropdown-menu-part="content"][data-width="sm"] {
|
|
99
|
+
@apply w-40;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
[data-dropdown-menu-part="content"][data-width="md"] {
|
|
103
|
+
@apply w-56;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
[data-dropdown-menu-part="content"][data-width="lg"] {
|
|
107
|
+
@apply w-72;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/* ===== Alignment ===== */
|
|
111
|
+
[data-dropdown-menu-part="content"][data-align="start"] {
|
|
112
|
+
left: 0;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
[data-dropdown-menu-part="content"][data-align="center"] {
|
|
116
|
+
left: 50%;
|
|
117
|
+
transform: translateX(-50%);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
[data-dropdown-menu-part="content"][data-align="center"][data-state="open"] {
|
|
121
|
+
animation: dropdown-menu-in-center 150ms ease-out;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
@keyframes dropdown-menu-in-center {
|
|
125
|
+
from {
|
|
126
|
+
opacity: 0;
|
|
127
|
+
transform: translateX(-50%) scale(0.95) translateY(-4px);
|
|
128
|
+
}
|
|
129
|
+
to {
|
|
130
|
+
opacity: 1;
|
|
131
|
+
transform: translateX(-50%) scale(1) translateY(0);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
[data-dropdown-menu-part="content"][data-align="end"] {
|
|
136
|
+
right: 0;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/* ===== Side Positioning ===== */
|
|
140
|
+
[data-dropdown-menu-part="content"][data-side="bottom"] {
|
|
141
|
+
top: 100%;
|
|
142
|
+
@apply mt-1;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
[data-dropdown-menu-part="content"][data-side="top"] {
|
|
146
|
+
bottom: 100%;
|
|
147
|
+
@apply mb-1;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
[data-dropdown-menu-part="content"][data-side="left"] {
|
|
151
|
+
right: 100%;
|
|
152
|
+
top: 0;
|
|
153
|
+
@apply mr-1;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
[data-dropdown-menu-part="content"][data-side="right"] {
|
|
157
|
+
left: 100%;
|
|
158
|
+
top: 0;
|
|
159
|
+
@apply ml-1;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/* ===== Menu Item ===== */
|
|
163
|
+
[data-dropdown-menu-part="item"] {
|
|
164
|
+
position: relative;
|
|
165
|
+
display: flex;
|
|
166
|
+
align-items: center;
|
|
167
|
+
cursor: default;
|
|
168
|
+
user-select: none;
|
|
169
|
+
color: var(--popover-foreground, var(--foreground));
|
|
170
|
+
@apply gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/* Reset link styles */
|
|
174
|
+
a[data-dropdown-menu-part="item"] {
|
|
175
|
+
text-decoration: none;
|
|
176
|
+
color: inherit;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/* Hover state */
|
|
180
|
+
[data-dropdown-menu-part="item"]:hover:not([disabled]):not([aria-disabled="true"]) {
|
|
181
|
+
background-color: var(--accent);
|
|
182
|
+
color: var(--accent-foreground);
|
|
183
|
+
cursor: pointer;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/* Focus state (keyboard navigation) */
|
|
187
|
+
[data-dropdown-menu-part="item"]:focus {
|
|
188
|
+
background-color: var(--accent);
|
|
189
|
+
color: var(--accent-foreground);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/* Disabled state */
|
|
193
|
+
[data-dropdown-menu-part="item"][disabled],
|
|
194
|
+
[data-dropdown-menu-part="item"][aria-disabled="true"] {
|
|
195
|
+
@apply opacity-50 cursor-not-allowed pointer-events-none;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/* Destructive variant */
|
|
199
|
+
[data-dropdown-menu-part="item"][data-variant="destructive"] {
|
|
200
|
+
color: var(--destructive);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
[data-dropdown-menu-part="item"][data-variant="destructive"]:hover:not([disabled]):not([aria-disabled="true"]),
|
|
204
|
+
[data-dropdown-menu-part="item"][data-variant="destructive"]:focus {
|
|
205
|
+
background-color: var(--destructive);
|
|
206
|
+
color: var(--destructive-foreground);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/* Icon support */
|
|
210
|
+
[data-dropdown-menu-part="item"] svg {
|
|
211
|
+
@apply size-4 shrink-0 pointer-events-none;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/* ===== Label ===== */
|
|
215
|
+
[data-dropdown-menu-part="label"] {
|
|
216
|
+
@apply px-2 py-1.5 text-sm font-semibold;
|
|
217
|
+
color: var(--foreground);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/* Inset variant (aligns with items that have icons) */
|
|
221
|
+
[data-dropdown-menu-part="label"][data-inset="true"] {
|
|
222
|
+
@apply pl-8;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/* ===== Separator ===== */
|
|
226
|
+
[data-dropdown-menu-part="separator"] {
|
|
227
|
+
@apply -mx-1 my-1 h-px;
|
|
228
|
+
background-color: var(--muted);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/* ===== Group ===== */
|
|
232
|
+
[data-dropdown-menu-part="group"] {
|
|
233
|
+
display: flex;
|
|
234
|
+
flex-direction: column;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/* ===== Shortcut ===== */
|
|
238
|
+
[data-dropdown-menu-part="shortcut"] {
|
|
239
|
+
@apply ml-auto text-xs tracking-widest;
|
|
240
|
+
color: var(--muted-foreground);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/* ===== Dark Mode ===== */
|
|
244
|
+
/*
|
|
245
|
+
* Dark mode is handled automatically through CSS variables.
|
|
246
|
+
* The theme variables change based on the .dark class on html/body.
|
|
247
|
+
* No additional dark mode styles needed here.
|
|
248
|
+
*/
|