panda-core 0.11.0 → 0.12.5

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 (56) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +15 -0
  3. data/app/assets/tailwind/application.css +196 -109
  4. data/app/builders/panda/core/form_builder.rb +9 -9
  5. data/app/components/panda/core/UI/button.rb +4 -4
  6. data/app/components/panda/core/admin/button_component.rb +2 -2
  7. data/app/components/panda/core/admin/form_input_component.rb +2 -2
  8. data/app/components/panda/core/admin/form_select_component.rb +2 -2
  9. data/app/components/panda/core/admin/panel_component.rb +1 -1
  10. data/app/components/panda/core/admin/statistics_component.rb +3 -3
  11. data/app/components/panda/core/admin/tab_bar_component.rb +3 -3
  12. data/app/components/panda/core/admin/table_component.rb +3 -3
  13. data/app/controllers/panda/core/admin/sessions_controller.rb +4 -1
  14. data/app/javascript/panda/core/controllers/navigation_toggle_controller.js +1 -1
  15. data/app/models/panda/core/user.rb +23 -14
  16. data/app/views/layouts/panda/core/admin.html.erb +10 -1
  17. data/app/views/panda/core/admin/dashboard/_default_content.html.erb +3 -3
  18. data/app/views/panda/core/admin/sessions/new.html.erb +2 -3
  19. data/app/views/panda/core/admin/shared/_sidebar.html.erb +5 -5
  20. data/config/brakeman.ignore +51 -0
  21. data/db/migrate/20250809000001_create_panda_core_users.rb +1 -1
  22. data/db/migrate/20251203100000_rename_is_admin_to_admin_in_panda_core_users.rb +18 -0
  23. data/lib/panda/core/asset_loader.rb +1 -1
  24. data/lib/panda/core/configuration.rb +4 -0
  25. data/lib/panda/core/engine/admin_controller_config.rb +6 -2
  26. data/lib/panda/core/engine/autoload_config.rb +9 -10
  27. data/lib/panda/core/engine/omniauth_config.rb +92 -34
  28. data/lib/panda/core/engine/route_config.rb +37 -0
  29. data/lib/panda/core/engine.rb +46 -41
  30. data/lib/panda/core/middleware.rb +146 -0
  31. data/lib/panda/core/module_registry.rb +21 -13
  32. data/lib/panda/core/testing/rails_helper.rb +17 -8
  33. data/lib/panda/core/testing/support/authentication_helpers.rb +6 -6
  34. data/lib/panda/core/testing/support/authentication_test_helpers.rb +2 -12
  35. data/lib/panda/core/testing/support/system/browser_console_logger.rb +1 -2
  36. data/lib/panda/core/testing/support/system/chrome_path.rb +38 -0
  37. data/lib/panda/core/testing/support/system/cuprite_helpers.rb +79 -0
  38. data/lib/panda/core/testing/support/system/cuprite_setup.rb +61 -66
  39. data/lib/panda/core/testing/support/system/system_test_helpers.rb +11 -11
  40. data/lib/panda/core/version.rb +1 -1
  41. data/lib/panda/core.rb +11 -0
  42. data/lib/tasks/panda/core/users.rake +3 -3
  43. data/lib/tasks/panda/shared.rake +31 -5
  44. data/public/panda-core-assets/favicons/browserconfig.xml +1 -1
  45. data/public/panda-core-assets/favicons/site.webmanifest +1 -1
  46. data/public/panda-core-assets/panda-core-0.11.0.css +2 -0
  47. data/public/panda-core-assets/panda-core-0.12.2.css +2 -0
  48. data/public/panda-core-assets/panda-core-0.12.3.css +2 -0
  49. data/public/panda-core-assets/panda-core.css +2 -2
  50. metadata +10 -8
  51. data/lib/panda/core/engine/middleware_config.rb +0 -17
  52. data/lib/panda/core/testing/support/system/better_system_tests.rb +0 -180
  53. data/lib/panda/core/testing/support/system/capybara_config.rb +0 -64
  54. data/lib/panda/core/testing/support/system/ci_capybara_config.rb +0 -77
  55. data/public/panda-core-assets/panda-core-0.10.6.css +0 -2
  56. data/public/panda-core-assets/panda-core-0.10.7.css +0 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ecbb8f9e4f925e37db74cbb9bec9b45277feceafc990fbab333433adf86e8e36
4
- data.tar.gz: ab419ee62bb77a3c382b462997439853ec07e438a37ea0d02935eb52d69d7169
3
+ metadata.gz: e6410ecd6025f6f46628cab53dbb0d19fb7d5fc01fddbdfb983fd6fe4907ef74
4
+ data.tar.gz: ed99ca839355558507da68f89723cd80c93451265bf9d93e5a24be44b22253fd
5
5
  SHA512:
6
- metadata.gz: 79f71a37792d0c3b85edb9d211459e2b1433c3fab96bc8adfa707bb8ce443f11c80cdb12c05a75a03154912908c1bb759fdc2031b3ae1840a045f1629d3f0d14
7
- data.tar.gz: 1d4f21c85d14ee7a474d8a83f1d99bccac31761a2803372a9d10d49156618cc40331d598ea805273040f8b56b5f4562bd873765bde423c76e358a76aab8ca56a
6
+ metadata.gz: 22640c748ea5cdaeba3e69cceffbccfbd81f127057036ccaa69f129566866f1b465ec92f24f54faff56a6ab1ec32734de8b4503546407f6d5166cdb1e9dd404a
7
+ data.tar.gz: c39cb2a7f4659e26b04612ad78a8844e296850a4477f5725dad2699b9c5b62f0859ab9ffdabbbbf240961fb0fa9d5ddf065cd3d8b60ae65c319f658b8f540cc4
data/README.md CHANGED
@@ -271,6 +271,21 @@ bundle exec rspec spec/generators
271
271
  bundle exec rspec spec/system
272
272
  ```
273
273
 
274
+ ### CI/Docker parity
275
+
276
+ The GitHub Actions workflow now builds a reusable CI image (Chrome + PostgreSQL + Ruby). You can reuse it locally:
277
+
278
+ ```
279
+ bin/ci build # build the CI image locally
280
+ bin/ci test # run the suite inside the container
281
+ ```
282
+
283
+ To dry-run the workflow with `act` (uses the locally built image tagged `:local`):
284
+
285
+ ```
286
+ act -j test-stable
287
+ ```
288
+
274
289
  ## Contributing
275
290
 
276
291
  1. Fork it
@@ -1,30 +1,59 @@
1
1
  @import 'tailwindcss';
2
2
 
3
3
  @theme {
4
- /* Legacy colors */
5
- --color-white: var(--color-white);
6
- --color-black: var(--color-black);
7
- --color-light: var(--color-light);
8
- --color-mid: var(--color-mid);
9
- --color-dark: var(--color-dark);
10
- --color-highlight: var(--color-highlight);
11
- --color-active: var(--color-active);
12
- --color-inactive: var(--color-inactive);
13
- --color-warning: var(--color-warning);
14
- --color-error: var(--color-error);
15
-
16
- /* Tailwind UI compatible primary scale */
17
- --color-primary-50: var(--color-primary-50);
18
- --color-primary-100: var(--color-primary-100);
19
- --color-primary-200: var(--color-primary-200);
20
- --color-primary-300: var(--color-primary-300);
21
- --color-primary-400: var(--color-primary-400);
22
- --color-primary-500: var(--color-primary-500);
23
- --color-primary-600: var(--color-primary-600);
24
- --color-primary-700: var(--color-primary-700);
25
- --color-primary-800: var(--color-primary-800);
26
- --color-primary-900: var(--color-primary-900);
27
- --color-primary-950: var(--color-primary-950);
4
+ /* Legacy colors - DEPRECATED: Use primary-* scale instead */
5
+ /* Fallback values ensure UI is visible even without data-theme attribute */
6
+ --color-white: #f9f9f9;
7
+ --color-black: #1a161d;
8
+ --color-light: #f1f9fa;
9
+ --color-mid: #3c879e;
10
+ --color-dark: #182934;
11
+ --color-highlight: #d04014;
12
+ --color-active: #16a34a;
13
+ --color-inactive: #dbeafe;
14
+ --color-warning: #f59e0b;
15
+ --color-error: #dc2626;
16
+
17
+ /* Primary color scale (Glacier/Teal) - with fallback values */
18
+ --color-primary-50: #f1f9fa;
19
+ --color-primary-100: #dbeef2;
20
+ --color-primary-200: #bcdde5;
21
+ --color-primary-300: #83bfcf;
22
+ --color-primary-400: #57a3b9;
23
+ --color-primary-500: #3c879e;
24
+ --color-primary-600: #346e86;
25
+ --color-primary-700: #305b6e;
26
+ --color-primary-800: #2e4d5c;
27
+ --color-primary-900: #2a424f;
28
+ --color-primary-950: #182934;
29
+
30
+ /* Semantic colors - Success (Green) */
31
+ --color-success-50: #f0fdf4;
32
+ --color-success-100: #dcfce7;
33
+ --color-success-500: #22c55e;
34
+ --color-success-600: #16a34a;
35
+ --color-success-700: #15803d;
36
+
37
+ /* Semantic colors - Error (Red) */
38
+ --color-error-50: #fef2f2;
39
+ --color-error-100: #fee2e2;
40
+ --color-error-500: #ef4444;
41
+ --color-error-600: #dc2626;
42
+ --color-error-700: #b91c1c;
43
+
44
+ /* Semantic colors - Warning (Amber) */
45
+ --color-warning-50: #fffbeb;
46
+ --color-warning-100: #fef3c7;
47
+ --color-warning-500: #f59e0b;
48
+ --color-warning-600: #d97706;
49
+ --color-warning-700: #b45309;
50
+
51
+ /* Semantic colors - Info (Blue) */
52
+ --color-info-50: #eff6ff;
53
+ --color-info-100: #dbeafe;
54
+ --color-info-500: #3b82f6;
55
+ --color-info-600: #2563eb;
56
+ --color-info-700: #1d4ed8;
28
57
 
29
58
  /* Map indigo to primary for Tailwind UI compatibility */
30
59
  --color-indigo-50: var(--color-primary-50);
@@ -39,18 +68,18 @@
39
68
  --color-indigo-900: var(--color-primary-900);
40
69
  --color-indigo-950: var(--color-primary-950);
41
70
 
42
- /* Gray scale */
43
- --color-gray-50: var(--color-gray-50);
44
- --color-gray-100: var(--color-gray-100);
45
- --color-gray-200: var(--color-gray-200);
46
- --color-gray-300: var(--color-gray-300);
47
- --color-gray-400: var(--color-gray-400);
48
- --color-gray-500: var(--color-gray-500);
49
- --color-gray-600: var(--color-gray-600);
50
- --color-gray-700: var(--color-gray-700);
51
- --color-gray-800: var(--color-gray-800);
52
- --color-gray-900: var(--color-gray-900);
53
- --color-gray-950: var(--color-gray-950);
71
+ /* Gray scale - with fallback values */
72
+ --color-gray-50: #f9fafb;
73
+ --color-gray-100: #f3f4f6;
74
+ --color-gray-200: #e5e7eb;
75
+ --color-gray-300: #d1d5db;
76
+ --color-gray-400: #9ca3af;
77
+ --color-gray-500: #6b7280;
78
+ --color-gray-600: #4b5563;
79
+ --color-gray-700: #374151;
80
+ --color-gray-800: #1f2937;
81
+ --color-gray-900: #111827;
82
+ --color-gray-950: #030712;
54
83
  }
55
84
 
56
85
  @layer base {
@@ -58,30 +87,58 @@
58
87
  --color-white: rgb(249, 249, 249); /* #F9F9F9 */
59
88
  --color-black: rgb(26, 22, 29); /* #1A161D */
60
89
 
61
- /* Legacy color variables (kept for backwards compatibility) */
62
- --color-light: rgb(238, 206, 230); /* #EECEE6 */
63
- --color-mid: rgb(141, 94, 183); /* #8D5EB7 */
64
- --color-dark: rgb(33, 29, 73); /* #211D49 */
90
+ /* Legacy color variables - DEPRECATED: Map to primary scale */
91
+ --color-light: rgb(241, 249, 250); /* Maps to primary-50 */
92
+ --color-mid: rgb(60, 135, 158); /* Maps to primary-500 */
93
+ --color-dark: rgb(24, 41, 52); /* Maps to primary-950 */
65
94
 
66
95
  --color-highlight: rgb(208, 64, 20); /* #D04014 */
67
96
 
68
- --color-active: rgb(0, 135, 85); /* #008755 */
69
- --color-warning: rgb(250, 207, 142); /* #FACF8E */
70
- --color-inactive: rgb(216, 247, 245); /* #d6e4f7 */
71
- --color-error: rgb(245, 129, 129); /* #F58181 */
72
-
73
- /* Full Tailwind UI compatible color scale - Primary (Purple palette) */
74
- --color-primary-50: rgb(250, 245, 255); /* Very light purple */
75
- --color-primary-100: rgb(243, 232, 255); /* Lighter purple */
76
- --color-primary-200: rgb(233, 213, 255); /* Light purple */
77
- --color-primary-300: rgb(216, 180, 254); /* Medium light purple */
78
- --color-primary-400: rgb(192, 132, 252); /* Medium purple */
79
- --color-primary-500: rgb(141, 94, 183); /* Main brand color (mid) */
80
- --color-primary-600: rgb(120, 80, 160); /* Darker purple */
81
- --color-primary-700: rgb(100, 65, 140); /* Dark purple */
82
- --color-primary-800: rgb(60, 45, 100); /* Very dark purple */
83
- --color-primary-900: rgb(33, 29, 73); /* Darkest (dark) */
84
- --color-primary-950: rgb(24, 20, 50); /* Ultra dark */
97
+ --color-active: rgb(22, 163, 74); /* green-600 - success */
98
+ --color-warning: rgb(245, 158, 11); /* amber-500 */
99
+ --color-inactive: rgb(219, 234, 254); /* blue-100 */
100
+ --color-error: rgb(220, 38, 38); /* red-600 */
101
+
102
+ /* Primary color scale - Glacier (Teal) */
103
+ --color-primary-50: rgb(241, 249, 250); /* #f1f9fa */
104
+ --color-primary-100: rgb(219, 238, 242); /* #dbeef2 */
105
+ --color-primary-200: rgb(188, 221, 229); /* #bcdde5 */
106
+ --color-primary-300: rgb(131, 191, 207); /* #83bfcf */
107
+ --color-primary-400: rgb(87, 163, 185); /* #57a3b9 */
108
+ --color-primary-500: rgb(60, 135, 158); /* #3c879e */
109
+ --color-primary-600: rgb(52, 110, 134); /* #346e86 */
110
+ --color-primary-700: rgb(48, 91, 110); /* #305b6e */
111
+ --color-primary-800: rgb(46, 77, 92); /* #2e4d5c */
112
+ --color-primary-900: rgb(42, 66, 79); /* #2a424f */
113
+ --color-primary-950: rgb(24, 41, 52); /* #182934 */
114
+
115
+ /* Semantic colors - Success (Green) */
116
+ --color-success-50: rgb(240, 253, 244); /* green-50 */
117
+ --color-success-100: rgb(220, 252, 231); /* green-100 */
118
+ --color-success-500: rgb(34, 197, 94); /* green-500 */
119
+ --color-success-600: rgb(22, 163, 74); /* green-600 */
120
+ --color-success-700: rgb(21, 128, 61); /* green-700 */
121
+
122
+ /* Semantic colors - Error (Red) */
123
+ --color-error-50: rgb(254, 242, 242); /* red-50 */
124
+ --color-error-100: rgb(254, 226, 226); /* red-100 */
125
+ --color-error-500: rgb(239, 68, 68); /* red-500 */
126
+ --color-error-600: rgb(220, 38, 38); /* red-600 */
127
+ --color-error-700: rgb(185, 28, 28); /* red-700 */
128
+
129
+ /* Semantic colors - Warning (Amber) */
130
+ --color-warning-50: rgb(255, 251, 235); /* amber-50 */
131
+ --color-warning-100: rgb(254, 243, 199); /* amber-100 */
132
+ --color-warning-500: rgb(245, 158, 11); /* amber-500 */
133
+ --color-warning-600: rgb(217, 119, 6); /* amber-600 */
134
+ --color-warning-700: rgb(180, 83, 9); /* amber-700 */
135
+
136
+ /* Semantic colors - Info (Blue) */
137
+ --color-info-50: rgb(239, 246, 255); /* blue-50 */
138
+ --color-info-100: rgb(219, 234, 254); /* blue-100 */
139
+ --color-info-500: rgb(59, 130, 246); /* blue-500 */
140
+ --color-info-600: rgb(37, 99, 235); /* blue-600 */
141
+ --color-info-700: rgb(29, 78, 216); /* blue-700 */
85
142
 
86
143
  /* Gray scale for UI elements */
87
144
  --color-gray-50: rgb(249, 250, 251);
@@ -97,33 +154,62 @@
97
154
  --color-gray-950: rgb(3, 7, 18);
98
155
  }
99
156
 
100
- html[data-theme='sky'] {
157
+ html[data-theme='ocean'] {
101
158
  --color-white: rgb(249, 249, 249); /* #F9F9F9 */
102
159
  --color-black: rgb(26, 22, 29); /* #1A161D */
103
160
 
104
- /* Legacy color variables (kept for backwards compatibility) */
105
- --color-light: rgb(204, 238, 242); /* #CCEEF2 */
106
- --color-mid: rgb(42, 102, 159); /* #2A669F */
107
- --color-dark: rgb(20, 32, 74); /* #14204A */
161
+ /* Legacy color variables - DEPRECATED: Map to primary scale */
162
+ --color-light: rgb(239, 246, 255); /* Maps to primary-50 (blue-50) */
163
+ --color-mid: rgb(59, 130, 246); /* Maps to primary-500 (blue-500) */
164
+ --color-dark: rgb(30, 58, 138); /* Maps to primary-950 (blue-950) */
165
+
108
166
  --color-highlight: rgb(208, 64, 20); /* #D04014 */
109
167
 
110
- --color-active: rgb(69, 154, 89); /* #459A59 - darker green with better contrast */
111
- --color-warning: rgb(244, 190, 102); /* #F4BE66 */
112
- --color-inactive: rgb(216, 247, 245); /* #d6e4f7 */
113
- --color-error: rgb(208, 64, 20); /* #D04014 */
114
-
115
- /* Full Tailwind UI compatible color scale - Primary (Blue palette) */
116
- --color-primary-50: rgb(240, 249, 255); /* Very light blue */
117
- --color-primary-100: rgb(224, 242, 254); /* Lighter blue */
118
- --color-primary-200: rgb(186, 230, 253); /* Light blue */
119
- --color-primary-300: rgb(125, 211, 252); /* Medium light blue */
120
- --color-primary-400: rgb(56, 189, 248); /* Medium blue */
121
- --color-primary-500: rgb(42, 102, 159); /* Main brand color (mid) */
122
- --color-primary-600: rgb(35, 85, 132); /* Darker blue */
123
- --color-primary-700: rgb(28, 68, 106); /* Dark blue */
124
- --color-primary-800: rgb(22, 52, 82); /* Very dark blue */
125
- --color-primary-900: rgb(20, 32, 74); /* Darkest (dark) */
126
- --color-primary-950: rgb(15, 24, 55); /* Ultra dark */
168
+ --color-active: rgb(22, 163, 74); /* green-600 - success */
169
+ --color-warning: rgb(245, 158, 11); /* amber-500 */
170
+ --color-inactive: rgb(219, 234, 254); /* blue-100 */
171
+ --color-error: rgb(220, 38, 38); /* red-600 */
172
+
173
+ /* Primary color scale - Ocean (Blue) */
174
+ --color-primary-50: rgb(239, 246, 255); /* blue-50 */
175
+ --color-primary-100: rgb(219, 234, 254); /* blue-100 */
176
+ --color-primary-200: rgb(191, 219, 254); /* blue-200 */
177
+ --color-primary-300: rgb(147, 197, 253); /* blue-300 */
178
+ --color-primary-400: rgb(96, 165, 250); /* blue-400 */
179
+ --color-primary-500: rgb(59, 130, 246); /* blue-500 */
180
+ --color-primary-600: rgb(37, 99, 235); /* blue-600 */
181
+ --color-primary-700: rgb(29, 78, 216); /* blue-700 */
182
+ --color-primary-800: rgb(30, 64, 175); /* blue-800 */
183
+ --color-primary-900: rgb(30, 58, 138); /* blue-900 */
184
+ --color-primary-950: rgb(23, 37, 84); /* blue-950 */
185
+
186
+ /* Semantic colors - Success (Green) */
187
+ --color-success-50: rgb(240, 253, 244);
188
+ --color-success-100: rgb(220, 252, 231);
189
+ --color-success-500: rgb(34, 197, 94);
190
+ --color-success-600: rgb(22, 163, 74);
191
+ --color-success-700: rgb(21, 128, 61);
192
+
193
+ /* Semantic colors - Error (Red) */
194
+ --color-error-50: rgb(254, 242, 242);
195
+ --color-error-100: rgb(254, 226, 226);
196
+ --color-error-500: rgb(239, 68, 68);
197
+ --color-error-600: rgb(220, 38, 38);
198
+ --color-error-700: rgb(185, 28, 28);
199
+
200
+ /* Semantic colors - Warning (Amber) */
201
+ --color-warning-50: rgb(255, 251, 235);
202
+ --color-warning-100: rgb(254, 243, 199);
203
+ --color-warning-500: rgb(245, 158, 11);
204
+ --color-warning-600: rgb(217, 119, 6);
205
+ --color-warning-700: rgb(180, 83, 9);
206
+
207
+ /* Semantic colors - Info (Teal - different from primary for ocean theme) */
208
+ --color-info-50: rgb(241, 249, 250);
209
+ --color-info-100: rgb(219, 238, 242);
210
+ --color-info-500: rgb(60, 135, 158);
211
+ --color-info-600: rgb(52, 110, 134);
212
+ --color-info-700: rgb(48, 91, 110);
127
213
 
128
214
  /* Gray scale for UI elements (same across themes) */
129
215
  --color-gray-50: rgb(249, 250, 251);
@@ -145,19 +231,16 @@
145
231
  inset: 0;
146
232
  }
147
233
 
148
- /* Admin gradient backgrounds */
149
- html[data-theme='default'] .bg-gradient-admin {
150
- background: linear-gradient(to bottom right, rgb(33, 29, 73), rgb(141, 94, 183));
151
- }
152
-
153
- html[data-theme='sky'] .bg-gradient-admin {
154
- background: linear-gradient(to bottom right, rgb(20, 32, 74), rgb(42, 102, 159));
234
+ /* Admin gradient backgrounds - neutral gray for chrome */
235
+ .bg-gradient-admin {
236
+ background: linear-gradient(to bottom right, rgb(17, 24, 39), rgb(55, 65, 81));
237
+ border-bottom-left-radius: 0;
155
238
  }
156
239
  }
157
240
 
158
241
  /* Form input styles */
159
242
  @layer components {
160
- /* Base form field styles - matches panda-cms admin pattern */
243
+ /* Base form field styles */
161
244
  input[type='text'],
162
245
  input[type='email'],
163
246
  input[type='password'],
@@ -172,17 +255,17 @@
172
255
  input[type='search'],
173
256
  textarea {
174
257
  @apply block w-full rounded-md border-0 p-2 text-gray-900 bg-white;
175
- @apply ring-1 ring-inset ring-mid placeholder:text-gray-300;
176
- @apply focus:ring-1 focus:ring-inset focus:ring-dark;
258
+ @apply ring-1 ring-inset ring-primary-400 placeholder:text-gray-400;
259
+ @apply focus:ring-2 focus:ring-inset focus:ring-primary-600;
177
260
  @apply hover:cursor-pointer sm:leading-6;
178
261
  @apply disabled:ring-gray-300 disabled:focus:ring-gray-300 disabled:bg-gray-50 disabled:cursor-not-allowed;
179
262
  }
180
263
 
181
- /* Select specific styling - matches panda-cms admin pattern */
264
+ /* Select specific styling */
182
265
  select {
183
266
  @apply block w-full rounded-md border-0 py-1.5 pl-3 pr-10 text-gray-900 bg-white;
184
- @apply ring-1 ring-inset ring-mid;
185
- @apply focus:ring-1 focus:ring-inset focus:ring-dark;
267
+ @apply ring-1 ring-inset ring-primary-400;
268
+ @apply focus:ring-2 focus:ring-inset focus:ring-primary-600;
186
269
  @apply hover:cursor-pointer sm:leading-6;
187
270
  @apply disabled:ring-gray-300 disabled:focus:ring-gray-300 disabled:bg-gray-50 disabled:cursor-not-allowed;
188
271
  @apply appearance-none bg-right bg-no-repeat;
@@ -194,8 +277,8 @@
194
277
  /* Checkbox and radio styling */
195
278
  input[type='checkbox'],
196
279
  input[type='radio'] {
197
- @apply w-4 h-4 text-indigo-600 bg-white border-gray-300 rounded transition-colors duration-200;
198
- @apply focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2;
280
+ @apply w-4 h-4 text-primary-600 bg-white border-gray-300 rounded transition-colors duration-200;
281
+ @apply focus:ring-2 focus:ring-primary-500 focus:ring-offset-2;
199
282
  @apply disabled:bg-gray-100 disabled:cursor-not-allowed;
200
283
  }
201
284
 
@@ -217,30 +300,34 @@
217
300
  input.error,
218
301
  textarea.error,
219
302
  select.error {
220
- @apply ring-red-500 focus:ring-red-500;
303
+ @apply ring-error-500 focus:ring-error-500;
221
304
  }
222
305
 
223
306
  .field-error {
224
- @apply text-sm text-red-600 mt-1;
307
+ @apply text-sm text-error-600 mt-1;
225
308
  }
226
309
 
227
310
  /* Button styling */
228
311
  .btn {
229
312
  @apply inline-flex items-center justify-center px-6 py-3 text-base font-semibold rounded-md transition-colors duration-200;
230
- @apply focus:outline-none focus:ring-2 focus:ring-offset-2;
313
+ @apply focus-visible:outline-2 focus-visible:outline-offset-2;
231
314
  @apply disabled:opacity-50 disabled:cursor-not-allowed;
232
315
  }
233
316
 
234
317
  .btn-primary {
235
- @apply bg-mid text-white hover:opacity-90 focus:ring-mid;
318
+ @apply bg-primary-500 text-white hover:bg-primary-600 focus-visible:outline-primary-600;
236
319
  }
237
320
 
238
321
  .btn-secondary {
239
- @apply bg-gray-200 text-gray-900 hover:bg-gray-300 focus:ring-gray-500;
322
+ @apply bg-gray-200 text-gray-900 hover:bg-gray-300 focus-visible:outline-gray-500;
240
323
  }
241
324
 
242
325
  .btn-danger {
243
- @apply bg-red-600 text-white hover:bg-red-700 focus:ring-red-500;
326
+ @apply bg-error-600 text-white hover:bg-error-700 focus-visible:outline-error-600;
327
+ }
328
+
329
+ .btn-success {
330
+ @apply bg-success-600 text-white hover:bg-success-700 focus-visible:outline-success-600;
244
331
  }
245
332
 
246
333
  a.block-link:after {
@@ -253,7 +340,7 @@
253
340
  /* EditorJS content styles */
254
341
  @layer components {
255
342
  .codex-editor__redactor .ce-block .ce-block__content {
256
- @apply text-base font-normal font-sans text-dark leading-[1.6] space-y-[1.6rem];
343
+ @apply text-base font-normal font-sans text-gray-900 leading-[1.6] space-y-[1.6rem];
257
344
 
258
345
  h1.ce-header {
259
346
  @apply text-3xl md:text-4xl font-semibold font-sans leading-[1.2];
@@ -272,7 +359,7 @@
272
359
  @apply leading-[1.6] tracking-wide max-w-[85ch];
273
360
 
274
361
  a {
275
- @apply text-[#1A9597] underline underline-offset-2 hover:text-[#158486] focus:outline-2 focus:outline-offset-2 focus:outline-[#1A9597];
362
+ @apply text-primary-600 underline underline-offset-2 hover:text-primary-700 focus:outline-2 focus:outline-offset-2 focus:outline-primary-600;
276
363
  }
277
364
 
278
365
  strong,
@@ -286,10 +373,10 @@
286
373
  }
287
374
 
288
375
  .cdx-quote {
289
- @apply bg-[#eef0f3] border-l-inactive border-l-8 p-6 mb-4;
376
+ @apply bg-gray-100 border-l-primary-300 border-l-8 p-6 mb-4;
290
377
 
291
378
  .cdx-quote__caption {
292
- @apply block ml-6 mt-2 text-sm text-dark;
379
+ @apply block ml-6 mt-2 text-sm text-gray-600;
293
380
  }
294
381
 
295
382
  .cdx-quote__text {
@@ -297,7 +384,7 @@
297
384
  @apply pl-6;
298
385
 
299
386
  &:before {
300
- @apply -ml-8 mr-2 text-dark text-6xl leading-4 align-text-bottom font-serif;
387
+ @apply -ml-8 mr-2 text-primary-600 text-6xl leading-4 align-text-bottom font-serif;
301
388
  content: open-quote;
302
389
  }
303
390
 
@@ -348,18 +435,18 @@
348
435
  }
349
436
 
350
437
  .cdx-table {
351
- @apply w-full border-collapse border-2 border-dark my-6;
438
+ @apply w-full border-collapse border-2 border-gray-300 my-6;
352
439
 
353
440
  &__head {
354
- @apply font-semibold border-dark border-r-2 p-3 bg-light;
441
+ @apply font-semibold border-gray-300 border-r-2 p-3 bg-gray-100;
355
442
  }
356
443
 
357
444
  &__row {
358
- @apply border-dark border-b-2;
445
+ @apply border-gray-300 border-b-2;
359
446
  }
360
447
 
361
448
  &__cell {
362
- @apply border-dark border-r-2 p-3;
449
+ @apply border-gray-300 border-r-2 p-3;
363
450
  }
364
451
  }
365
452
 
@@ -149,7 +149,7 @@ module Panda
149
149
  @template.image_tag("", alt: "Crop preview", data: {image_cropper_target: "preview"}, class: "max-w-full") +
150
150
  # Cropper controls
151
151
  content_tag(:div, class: "mt-4 flex gap-2 flex-wrap") do
152
- @template.button_tag("Crop & Save", type: "button", class: "inline-flex items-center gap-x-1.5 rounded-md bg-indigo-600 px-3 py-2 text-sm font-semibold text-white shadow-xs hover:bg-indigo-500", data: {action: "click->image-cropper#crop"}) +
152
+ @template.button_tag("Crop & Save", type: "button", class: "inline-flex items-center gap-x-1.5 rounded-md bg-primary-500 px-3 py-2 text-sm font-semibold text-white shadow-xs hover:bg-primary-600", data: {action: "click->image-cropper#crop"}) +
153
153
  @template.button_tag("Cancel", type: "button", class: "inline-flex items-center gap-x-1.5 rounded-md bg-white px-3 py-2 text-sm font-semibold text-gray-900 shadow-xs inset-ring inset-ring-gray-300 hover:bg-gray-50", data: {action: "click->image-cropper#cancel"}) +
154
154
  @template.button_tag(type: "button", class: "inline-flex items-center gap-x-1.5 rounded-md bg-white px-3 py-2 text-sm font-semibold text-gray-900 shadow-xs inset-ring inset-ring-gray-300 hover:bg-gray-50", data: {action: "click->image-cropper#reset"}) do
155
155
  @template.content_tag(:i, "", class: "fa-solid fa-rotate-left") +
@@ -200,7 +200,7 @@ module Panda
200
200
  end +
201
201
  # Upload area
202
202
  content_tag(:div, class: "mt-4 flex items-baseline justify-center text-sm leading-6 text-gray-600 dark:text-gray-400") do
203
- content_tag(:label, for: field_id, class: "relative cursor-pointer rounded-md bg-transparent font-semibold text-indigo-600 focus-within:outline-2 focus-within:outline-offset-2 focus-within:outline-indigo-600 hover:text-indigo-500 dark:text-indigo-400 dark:focus-within:outline-indigo-500 dark:hover:text-indigo-300") do
203
+ content_tag(:label, for: field_id, class: "relative cursor-pointer rounded-md bg-transparent font-semibold text-primary-600 focus-within:outline-2 focus-within:outline-offset-2 focus-within:outline-primary-600 hover:text-primary-500 dark:text-primary-400 dark:focus-within:outline-primary-500 dark:hover:text-primary-300") do
204
204
  content_tag(:span, "Upload a file") +
205
205
  super(method, options.reverse_merge(
206
206
  id: field_id,
@@ -261,11 +261,11 @@ module Panda
261
261
  def submit(value = nil, options = {})
262
262
  value ||= submit_default_value
263
263
 
264
- # Use the primary mid color for save/create actions
264
+ # Use the primary color for save/create actions
265
265
  action = object.persisted? ? :save : :create
266
266
  button_classes = case action
267
267
  when :save, :create
268
- "text-white bg-mid hover:bg-mid/80"
268
+ "text-white bg-primary-500 hover:bg-primary-600"
269
269
  when :save_inactive
270
270
  "text-white bg-gray-400"
271
271
  when :secondary
@@ -275,7 +275,7 @@ module Panda
275
275
  end
276
276
 
277
277
  # Combine with common button classes
278
- classes = "inline-flex items-center rounded-md font-medium shadow-sm focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 px-3 py-2 #{button_classes}"
278
+ classes = "inline-flex items-center rounded-md font-medium shadow-sm focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary-600 px-3 py-2 #{button_classes}"
279
279
 
280
280
  options[:class] = options[:class] ? "#{options[:class]} #{classes}" : classes
281
281
 
@@ -317,7 +317,7 @@ module Panda
317
317
  is_checked = (current_value.to_s == choice_value.to_s)
318
318
 
319
319
  content_tag(:label, class: "flex items-center gap-x-3 rounded-lg border border-gray-300 px-3 py-3 text-sm/6 font-medium cursor-pointer hover:bg-gray-50 dark:border-white/10 dark:hover:bg-white/5") do
320
- radio_button(method, choice_value, {id: choice_id, checked: is_checked, class: "size-4 border-gray-300 text-indigo-600 focus:ring-indigo-600 dark:border-white/10 dark:bg-white/5"}) +
320
+ radio_button(method, choice_value, {id: choice_id, checked: is_checked, class: "size-4 border-gray-300 text-primary-600 focus:ring-primary-600 dark:border-white/10 dark:bg-white/5"}) +
321
321
  content_tag(:span, choice_label, class: "text-gray-900 dark:text-white")
322
322
  end
323
323
  end.join.html_safe
@@ -344,7 +344,7 @@ module Panda
344
344
  end
345
345
 
346
346
  def base_input_styles
347
- "block w-full rounded-md bg-white px-3 py-1.5 text-base text-gray-900 outline-1 -outline-offset-1 outline-gray-300 placeholder:text-gray-400 focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-indigo-600 sm:text-sm/6 dark:bg-white/5 dark:text-white dark:outline-white/10 dark:placeholder:text-gray-500 dark:focus:outline-indigo-500"
347
+ "block w-full rounded-md bg-white px-3 py-1.5 text-base text-gray-900 outline-1 -outline-offset-1 outline-gray-300 placeholder:text-gray-400 focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-primary-600 sm:text-sm/6 dark:bg-white/5 dark:text-white dark:outline-white/10 dark:placeholder:text-gray-500 dark:focus:outline-primary-500"
348
348
  end
349
349
 
350
350
  def input_styles
@@ -360,7 +360,7 @@ module Panda
360
360
  end
361
361
 
362
362
  def select_styles
363
- "block w-full rounded-md bg-white px-3 py-1.5 pr-8 text-base text-gray-900 outline-1 -outline-offset-1 outline-gray-300 appearance-none focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-indigo-600 sm:text-sm/6 dark:bg-white/5 dark:text-white dark:outline-white/10 dark:focus:outline-indigo-500"
363
+ "block w-full rounded-md bg-white px-3 py-1.5 pr-8 text-base text-gray-900 outline-1 -outline-offset-1 outline-gray-300 appearance-none focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-primary-600 sm:text-sm/6 dark:bg-white/5 dark:text-white dark:outline-white/10 dark:focus:outline-primary-500"
364
364
  end
365
365
 
366
366
  def select_svg
@@ -401,7 +401,7 @@ module Panda
401
401
  def error_message(attribute)
402
402
  return unless object.respond_to?(:errors) && object.errors[attribute]&.any?
403
403
 
404
- content_tag(:p, class: "mt-2 text-sm text-red-600") do
404
+ content_tag(:p, class: "mt-2 text-sm text-error-600") do
405
405
  object.errors[attribute].join(", ")
406
406
  end
407
407
  end
@@ -89,15 +89,15 @@ module Panda
89
89
  def variant_classes
90
90
  case @variant
91
91
  when :primary
92
- # Blue primary button with dark mode support
93
- "bg-blue-600 text-white shadow-xs hover:bg-blue-500 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-600 dark:bg-blue-500 dark:shadow-none dark:hover:bg-blue-400 dark:focus-visible:outline-blue-500"
92
+ # Primary button with dark mode support
93
+ "bg-primary-500 text-white shadow-xs hover:bg-primary-600 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary-600 dark:bg-primary-400 dark:shadow-none dark:hover:bg-primary-500 dark:focus-visible:outline-primary-500"
94
94
  when :secondary
95
95
  # White/gray secondary button with ring and dark mode support
96
96
  "bg-white text-gray-900 shadow-xs inset-ring inset-ring-gray-300 hover:bg-gray-50 dark:bg-white/10 dark:text-white dark:shadow-none dark:inset-ring-white/5 dark:hover:bg-white/20"
97
97
  when :success
98
- "bg-green-600 text-white shadow-xs hover:bg-green-500 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-green-600 dark:bg-green-500 dark:shadow-none dark:hover:bg-green-400 dark:focus-visible:outline-green-500"
98
+ "bg-success-600 text-white shadow-xs hover:bg-success-700 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-success-600 dark:bg-success-500 dark:shadow-none dark:hover:bg-success-600 dark:focus-visible:outline-success-500"
99
99
  when :danger
100
- "bg-red-600 text-white shadow-xs hover:bg-red-500 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-red-600 dark:bg-red-500 dark:shadow-none dark:hover:bg-red-400 dark:focus-visible:outline-red-500"
100
+ "bg-error-600 text-white shadow-xs hover:bg-error-700 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-error-600 dark:bg-error-500 dark:shadow-none dark:hover:bg-error-600 dark:focus-visible:outline-error-500"
101
101
  when :ghost
102
102
  "bg-transparent text-gray-700 hover:bg-gray-100 dark:text-gray-300 dark:hover:bg-gray-800"
103
103
  else # :default
@@ -71,13 +71,13 @@ module Panda
71
71
  def action_classes
72
72
  case @action
73
73
  when :save, :create
74
- "text-white bg-mid hover:bg-mid/80"
74
+ "text-white bg-primary-500 hover:bg-primary-600 focus-visible:outline-primary-600"
75
75
  when :save_inactive
76
76
  "text-white bg-gray-400"
77
77
  when :secondary
78
78
  "text-gray-700 border-2 border-gray-500 bg-white hover:bg-gray-100 active:bg-gray-200 transition-colors"
79
79
  when :delete, :destroy, :danger
80
- "text-red-600 border border-red-600 bg-red-100 hover:bg-red-200 hover:text-red-700 focus-visible:outline-red-300"
80
+ "text-error-600 border border-error-600 bg-error-100 hover:bg-error-200 hover:text-error-700 focus-visible:outline-error-300"
81
81
  else
82
82
  "text-gray-700 border-2 border-gray-500 bg-white hover:bg-gray-100 active:bg-gray-200 transition-colors"
83
83
  end
@@ -36,12 +36,12 @@ module Panda
36
36
  private
37
37
 
38
38
  def input_classes
39
- classes = "block w-full rounded-md border-0 p-2 text-gray-900 ring-1 ring-inset placeholder:text-gray-300 focus:ring-1 focus:ring-inset sm:leading-6"
39
+ classes = "block w-full rounded-md border-0 p-2 text-gray-900 ring-1 ring-inset placeholder:text-gray-400 focus:ring-2 focus:ring-inset sm:leading-6"
40
40
 
41
41
  if @disabled
42
42
  classes + " ring-gray-300 focus:ring-gray-300 bg-gray-50 cursor-not-allowed"
43
43
  else
44
- classes + " ring-mid focus:ring-dark hover:cursor-pointer"
44
+ classes + " ring-primary-400 focus:ring-primary-600 hover:cursor-pointer"
45
45
  end
46
46
  end
47
47
  end
@@ -36,12 +36,12 @@ module Panda
36
36
  private
37
37
 
38
38
  def select_classes
39
- classes = "block w-full rounded-md border-0 py-1.5 pl-3 pr-10 text-gray-900 ring-1 ring-inset focus:ring-1 focus:ring-inset sm:leading-6"
39
+ classes = "block w-full rounded-md border-0 py-1.5 pl-3 pr-10 text-gray-900 ring-1 ring-inset focus:ring-2 focus:ring-inset sm:leading-6"
40
40
 
41
41
  if @disabled
42
42
  classes + " ring-gray-300 focus:ring-gray-300 bg-gray-50 cursor-not-allowed"
43
43
  else
44
- classes + " ring-mid focus:ring-dark hover:cursor-pointer"
44
+ classes + " ring-primary-400 focus:ring-primary-600 hover:cursor-pointer"
45
45
  end
46
46
  end
47
47
 
@@ -16,7 +16,7 @@ module Panda
16
16
  end
17
17
  end
18
18
 
19
- div(class: "col-span-3 mt-5 rounded-lg shadow-md bg-dark shadow-inherit/20") do
19
+ div(class: "col-span-3 mt-5 rounded-lg shadow-md bg-gray-800 shadow-inherit/20") do
20
20
  @heading_content&.call
21
21
 
22
22
  div(class: "p-4 text-black bg-white rounded-b-lg") do