odt-ui-rails 2.2.8 → 2.2.10

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ec66ce1401b2367f67b2895e51e993f3a25066c996bfd4134d724d9103ad55c9
4
- data.tar.gz: 5255d9461830b286c5eb04086151b3b9a6209c8316875e53711b8b00a26b4373
3
+ metadata.gz: a8122bf17489c5b3318f3dd2c7b90202e6d79f8252c90739121343c768b126a9
4
+ data.tar.gz: 36a59e4f27e1deee7bdbb8d92bffd93df11a21d97a9168a288e2041337ae8f3d
5
5
  SHA512:
6
- metadata.gz: 749c6a4fb7c52247cf79baabf9c92d6994094c56c2481333092677732347807128d3e8a6050dbbb7addeb1abd4c17e2921942a95d3b471a464965461f5f07f8c
7
- data.tar.gz: 8b03e7c0568bfe461069052fb37e3d64256ec52925d642257e9a1fae6721c58d148296340a36da7724c948dbdf67fa9b8d75e810453809dfa603986f23763770
6
+ metadata.gz: 047f42be5ce32f32fa7f618fa1a2929771c376f85f31a38ea517ebf06d231129f77acf001a42cc275a3c2593601be9f9ba9b82c62684eba82b97ab9e786c6f82
7
+ data.tar.gz: 25a92cb79041cdef37b4c4cf16676e36e988a9058d6913d4216c11167f8db5982184dd1cb9687c8f298a6b9b0e85d649f98d5f34bc84bda5048cac20326e2f84
data/README.md CHANGED
@@ -58,7 +58,7 @@
58
58
  Add the gem to your application's `Gemfile`:
59
59
 
60
60
  ```ruby
61
- gem "odt-ui-rails", "~> 2.2.7"
61
+ gem "odt-ui-rails", "~> 2.2.10"
62
62
  ```
63
63
 
64
64
  Install dependencies:
@@ -330,6 +330,16 @@ Accessible form components complete with labels, helper text, error validations,
330
330
  color: :primary
331
331
  ) %>
332
332
 
333
+ <%# Switch Toggle %>
334
+ <%= odt_switch(
335
+ label: "Enable Notifications",
336
+ description: "Receive instant updates in real-time",
337
+ name: "user[notifications]",
338
+ checked: true,
339
+ color: :primary,
340
+ size: :md
341
+ ) %>
342
+
333
343
  <%# Radio Group %>
334
344
  <%= odt_radio label: "Monthly Billing", name: "plan", value: "monthly", checked: true %>
335
345
  <%= odt_radio label: "Annual Billing (Save 20%)", name: "plan", value: "annual" %>
@@ -1471,7 +1471,7 @@
1471
1471
  .odt-select--md {
1472
1472
  height: 2.625rem;
1473
1473
  padding: 0 var(--spacing-4, 1rem);
1474
- font-size: var(--font-size-sm, 0.875rem);
1474
+ font-size: var(--font-size-base, 1rem);
1475
1475
  border-radius: var(--radius-xl, 0.75rem);
1476
1476
  }
1477
1477
 
@@ -1479,7 +1479,7 @@
1479
1479
  .odt-select--lg {
1480
1480
  height: 3.125rem;
1481
1481
  padding: 0 var(--spacing-5, 1.25rem);
1482
- font-size: var(--font-size-base, 1rem);
1482
+ font-size: var(--font-size-lg, 1.125rem);
1483
1483
  border-radius: var(--radius-2xl, 1rem);
1484
1484
  }
1485
1485
 
@@ -1498,14 +1498,14 @@
1498
1498
 
1499
1499
  .odt-textarea--md {
1500
1500
  padding: var(--spacing-3, 0.75rem) var(--spacing-4, 1rem);
1501
- font-size: var(--font-size-sm, 0.875rem);
1501
+ font-size: var(--font-size-base, 1rem);
1502
1502
  min-height: 5rem;
1503
1503
  border-radius: var(--radius-xl, 0.75rem);
1504
1504
  }
1505
1505
 
1506
1506
  .odt-textarea--lg {
1507
1507
  padding: var(--spacing-4, 1rem) var(--spacing-5, 1.25rem);
1508
- font-size: var(--font-size-base, 1rem);
1508
+ font-size: var(--font-size-lg, 1.125rem);
1509
1509
  min-height: 6.5rem;
1510
1510
  border-radius: var(--radius-2xl, 1rem);
1511
1511
  }
@@ -1758,34 +1758,36 @@
1758
1758
  margin-top: 0.0625rem;
1759
1759
  }
1760
1760
 
1761
- /* Checkmark indicator */
1762
- .odt-checkbox:checked::after {
1763
- content: "";
1764
- display: block;
1765
- width: 4px;
1766
- height: 8px;
1767
- border: solid currentColor;
1768
- border-width: 0 2px 2px 0;
1769
- transform: rotate(45deg);
1770
- margin-bottom: 2px;
1771
- animation: odt-pop 140ms cubic-bezier(0.16, 1, 0.3, 1) forwards;
1772
- }
1761
+ /* Checkmark indicator */
1762
+ .odt-checkbox:checked::after {
1763
+ content: "";
1764
+ display: block;
1765
+ width: 0.75rem;
1766
+ height: 0.75rem;
1767
+ background-color: currentColor;
1768
+ -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='3.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E");
1769
+ mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='3.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E");
1770
+ -webkit-mask-size: contain;
1771
+ mask-size: contain;
1772
+ -webkit-mask-repeat: no-repeat;
1773
+ mask-repeat: no-repeat;
1774
+ -webkit-mask-position: center;
1775
+ mask-position: center;
1776
+ animation: odt-pop 140ms cubic-bezier(0.16, 1, 0.3, 1) forwards;
1777
+ }
1778
+
1779
+ .odt-checkbox--sm:checked::after {
1780
+ width: 0.625rem;
1781
+ height: 0.625rem;
1782
+ }
1783
+
1784
+ .odt-checkbox--lg:checked::after {
1785
+ width: 0.875rem;
1786
+ height: 0.875rem;
1787
+ }
1788
+
1789
+ /* Radio Dot indicator */
1773
1790
 
1774
- .odt-checkbox--sm:checked::after {
1775
- width: 3px;
1776
- height: 6px;
1777
- border-width: 0 1.5px 1.5px 0;
1778
- margin-bottom: 1px;
1779
- }
1780
-
1781
- .odt-checkbox--lg:checked::after {
1782
- width: 5px;
1783
- height: 10px;
1784
- border-width: 0 2.5px 2.5px 0;
1785
- margin-bottom: 2px;
1786
- }
1787
-
1788
- /* Radio Dot indicator */
1789
1791
  .odt-radio:checked::after {
1790
1792
  content: "";
1791
1793
  display: block;
@@ -1899,6 +1901,196 @@
1899
1901
  line-height: 1rem;
1900
1902
  }
1901
1903
 
1904
+ /* ==========================================================================
1905
+ * Switch Component (.odt-switch)
1906
+ * ========================================================================== */
1907
+ .odt-switch-wrapper {
1908
+ display: inline-flex;
1909
+ flex-direction: column;
1910
+ gap: 0.25rem;
1911
+ }
1912
+
1913
+ .odt-switch-container {
1914
+ display: inline-flex;
1915
+ align-items: center;
1916
+ gap: var(--spacing-3, 0.75rem);
1917
+ cursor: pointer;
1918
+ user-select: none;
1919
+ }
1920
+
1921
+ .odt-switch {
1922
+ appearance: none;
1923
+ -webkit-appearance: none;
1924
+ position: relative;
1925
+ display: inline-flex;
1926
+ align-items: center;
1927
+ flex-shrink: 0;
1928
+ width: 2.75rem;
1929
+ height: 1.5rem;
1930
+ border-radius: var(--radius-full, 9999px);
1931
+ background-color: var(--color-neutral-300);
1932
+ border: none;
1933
+ cursor: pointer;
1934
+ padding: 0;
1935
+ margin: 0;
1936
+ outline: none;
1937
+ box-sizing: border-box;
1938
+ transition: background-color 0.2s ease-in-out, box-shadow 0.2s ease-in-out;
1939
+ }
1940
+
1941
+ .odt-switch::after {
1942
+ content: "";
1943
+ pointer-events: none;
1944
+ display: block;
1945
+ width: 1rem;
1946
+ height: 1rem;
1947
+ border-radius: var(--radius-full, 9999px);
1948
+ background-color: var(--color-switch-thumb, #ffffff);
1949
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
1950
+ transform: translateX(0.25rem);
1951
+ transition: transform 0.2s cubic-bezier(0.4, 0, 0.2, 1);
1952
+ }
1953
+
1954
+ .odt-switch:checked::after {
1955
+ transform: translateX(1.5rem);
1956
+ }
1957
+
1958
+ /* Switch Sizes */
1959
+ .odt-switch--sm {
1960
+ width: 2rem;
1961
+ height: 1.125rem;
1962
+ }
1963
+
1964
+ .odt-switch--sm::after {
1965
+ width: 0.75rem;
1966
+ height: 0.75rem;
1967
+ transform: translateX(0.1875rem);
1968
+ }
1969
+
1970
+ .odt-switch--sm:checked::after {
1971
+ transform: translateX(1.0625rem);
1972
+ }
1973
+
1974
+ .odt-switch--md {
1975
+ width: 2.75rem;
1976
+ height: 1.5rem;
1977
+ }
1978
+
1979
+ .odt-switch--md::after {
1980
+ width: 1rem;
1981
+ height: 1rem;
1982
+ transform: translateX(0.25rem);
1983
+ }
1984
+
1985
+ .odt-switch--md:checked::after {
1986
+ transform: translateX(1.5rem);
1987
+ }
1988
+
1989
+ .odt-switch--lg {
1990
+ width: 3.5rem;
1991
+ height: 2rem;
1992
+ }
1993
+
1994
+ .odt-switch--lg::after {
1995
+ width: 1.5rem;
1996
+ height: 1.5rem;
1997
+ transform: translateX(0.25rem);
1998
+ }
1999
+
2000
+ .odt-switch--lg:checked::after {
2001
+ transform: translateX(1.75rem);
2002
+ }
2003
+
2004
+ /* Switch Colors */
2005
+ .odt-switch--primary:checked {
2006
+ background-color: var(--color-primary-500);
2007
+ }
2008
+
2009
+ .odt-switch--primary:focus-visible {
2010
+ box-shadow: 0 0 0 3px hsl(from var(--color-primary-500) h s l / 0.35);
2011
+ }
2012
+
2013
+ .odt-switch--secondary:checked {
2014
+ background-color: var(--color-secondary-500);
2015
+ }
2016
+
2017
+ .odt-switch--secondary:focus-visible {
2018
+ box-shadow: 0 0 0 3px hsl(from var(--color-secondary-500) h s l / 0.35);
2019
+ }
2020
+
2021
+ .odt-switch--success:checked {
2022
+ background-color: var(--color-success);
2023
+ }
2024
+
2025
+ .odt-switch--success:focus-visible {
2026
+ box-shadow: 0 0 0 3px hsl(from var(--color-success) h s l / 0.35);
2027
+ }
2028
+
2029
+ .odt-switch--warning:checked {
2030
+ background-color: var(--color-warning);
2031
+ }
2032
+
2033
+ .odt-switch--warning:focus-visible {
2034
+ box-shadow: 0 0 0 3px hsl(from var(--color-warning) h s l / 0.35);
2035
+ }
2036
+
2037
+ .odt-switch--danger:checked {
2038
+ background-color: var(--color-danger);
2039
+ }
2040
+
2041
+ .odt-switch--danger:focus-visible {
2042
+ box-shadow: 0 0 0 3px hsl(from var(--color-danger) h s l / 0.35);
2043
+ }
2044
+
2045
+ .odt-switch--info:checked {
2046
+ background-color: var(--color-info);
2047
+ }
2048
+
2049
+ .odt-switch--info:focus-visible {
2050
+ box-shadow: 0 0 0 3px hsl(from var(--color-info) h s l / 0.35);
2051
+ }
2052
+
2053
+ .odt-switch--dark:checked,
2054
+ .odt-switch--neutral:checked {
2055
+ background-color: var(--color-neutral-900);
2056
+ }
2057
+
2058
+ .odt-switch--dark:focus-visible,
2059
+ .odt-switch--neutral:focus-visible {
2060
+ box-shadow: 0 0 0 3px hsl(from var(--color-neutral-900) h s l / 0.35);
2061
+ }
2062
+
2063
+ /* Switch States */
2064
+ .odt-switch:disabled,
2065
+ .odt-switch-container--disabled {
2066
+ opacity: 0.45;
2067
+ cursor: not-allowed;
2068
+ pointer-events: none;
2069
+ }
2070
+
2071
+ .odt-switch--error:not(:checked) {
2072
+ box-shadow: 0 0 0 2px var(--color-danger);
2073
+ }
2074
+
2075
+ .odt-switch__label-content {
2076
+ display: flex;
2077
+ flex-direction: column;
2078
+ gap: 0.125rem;
2079
+ }
2080
+
2081
+ .odt-switch__label {
2082
+ font-size: var(--font-size-sm, 0.875rem);
2083
+ color: var(--color-fg);
2084
+ font-weight: 500;
2085
+ line-height: 1.25rem;
2086
+ }
2087
+
2088
+ .odt-switch__description {
2089
+ font-size: var(--font-size-xs, 0.75rem);
2090
+ color: var(--color-fg-muted);
2091
+ line-height: 1rem;
2092
+ }
2093
+
1902
2094
  /* ==========================================================================
1903
2095
  * 10. Toast & Toaster Component (.odt-toaster, .odt-toast)
1904
2096
  * ========================================================================== */
@@ -2450,13 +2642,13 @@
2450
2642
  .odt-dropdown__trigger--md {
2451
2643
  height: 2.625rem;
2452
2644
  padding: 0 var(--spacing-4, 1rem);
2453
- font-size: var(--font-size-sm, 0.875rem);
2645
+ font-size: var(--font-size-base, 1rem);
2454
2646
  }
2455
2647
 
2456
2648
  .odt-dropdown__trigger--lg {
2457
2649
  height: 3.125rem;
2458
2650
  padding: 0 var(--spacing-5, 1.25rem);
2459
- font-size: var(--font-size-base, 1rem);
2651
+ font-size: var(--font-size-lg, 1.125rem);
2460
2652
  }
2461
2653
 
2462
2654
  .odt-dropdown__trigger-text {
@@ -284,32 +284,32 @@
284
284
  /* ==========================================================================
285
285
  * Primary Brand Colors (Dark Scale)
286
286
  * ========================================================================== */
287
- --color-primary-0: hsl(240 78% 10%);
288
- --color-primary-50: hsla(225.1, 58.6%, 58.4%, 0.18);
289
- --color-primary-100: hsla(225.1, 58.6%, 58.4%, 0.28);
290
- --color-primary-200: hsl(240 78% 34%);
291
- --color-primary-300: hsl(234.2 73.9% 37.4%);
292
- --color-primary-400: hsl(229.7 65% 43%);
293
- --color-primary-500: hsl(226.9 53.8% 50.1%);
294
- --color-primary-600: hsl(225.1 58.6% 58.4%);
295
- --color-primary-700: hsl(224 63.5% 67.6%);
296
- --color-primary-800: hsl(223.3 68.7% 76.7%);
297
- --color-primary-900: hsl(222.5 100% 96.2%);
287
+ --color-primary-0: hsla(225.1, 58.6%, 58.4%, 0.05);
288
+ --color-primary-50: hsla(225.1, 58.6%, 58.4%, 0.12);
289
+ --color-primary-100: hsla(225.1, 58.6%, 58.4%, 0.20);
290
+ --color-primary-200: hsla(225.1, 58.6%, 58.4%, 0.32);
291
+ --color-primary-300: hsla(225.1, 58.6%, 58.4%, 0.48);
292
+ --color-primary-400: hsl(224 63.5% 67.6%);
293
+ --color-primary-500: hsl(225.1 58.6% 58.4%);
294
+ --color-primary-600: hsl(226.9 53.8% 50.1%);
295
+ --color-primary-700: hsl(229.7 65% 43%);
296
+ --color-primary-800: hsl(234.2 73.9% 37.4%);
297
+ --color-primary-900: hsl(240 78% 34%);
298
298
 
299
299
  /* ==========================================================================
300
300
  * Secondary Brand Colors (Dark Scale)
301
301
  * ========================================================================== */
302
- --color-secondary-0: hsl(47 100% 10%);
303
- --color-secondary-50: hsla(47, 100%, 68.6%, 0.16);
304
- --color-secondary-100: hsla(47, 100%, 68.6%, 0.25);
305
- --color-secondary-200: hsl(47 100% 50%);
306
- --color-secondary-300: hsl(47 100% 52.9%);
307
- --color-secondary-400: hsl(47 100% 57.3%);
308
- --color-secondary-500: hsl(47 100% 62.6%);
309
- --color-secondary-600: hsl(47 100% 68.6%);
310
- --color-secondary-700: hsl(47 100% 75.2%);
311
- --color-secondary-800: hsl(47 100% 81.6%);
312
- --color-secondary-900: hsl(47 100% 95.6%);
302
+ --color-secondary-0: hsla(47, 100%, 68.6%, 0.05);
303
+ --color-secondary-50: hsla(47, 100%, 68.6%, 0.12);
304
+ --color-secondary-100: hsla(47, 100%, 68.6%, 0.20);
305
+ --color-secondary-200: hsla(47, 100%, 68.6%, 0.32);
306
+ --color-secondary-300: hsla(47, 100%, 68.6%, 0.48);
307
+ --color-secondary-400: hsl(47 100% 75.2%);
308
+ --color-secondary-500: hsl(47 100% 68.6%);
309
+ --color-secondary-600: hsl(47 100% 62.6%);
310
+ --color-secondary-700: hsl(47 100% 57.3%);
311
+ --color-secondary-800: hsl(47 100% 52.9%);
312
+ --color-secondary-900: hsl(47 100% 50%);
313
313
 
314
314
  /* ==========================================================================
315
315
  * Neutral Palette (Dark Scale)
@@ -369,8 +369,8 @@
369
369
  --color-fg-inverse-disabled: var(--color-neutral-400);
370
370
 
371
371
  /* Brand Foreground */
372
- --color-fg-primary: var(--color-primary-600);
373
- --color-fg-secondary: var(--color-secondary-600);
372
+ --color-fg-primary: var(--color-primary-500);
373
+ --color-fg-secondary: var(--color-secondary-500);
374
374
 
375
375
  /* Semantic Foreground */
376
376
  --color-fg-success: var(--color-success);
@@ -406,7 +406,7 @@
406
406
  /* ==========================================================================
407
407
  * Gradients
408
408
  * ========================================================================== */
409
- --gradient-subtle: linear-gradient(to top right, var(--color-primary-50), var(--color-primary-0) 60%, var(--color-secondary-50));
409
+ --gradient-subtle: linear-gradient(to top right, var(--color-primary-50), var(--color-neutral-0) 60%, var(--color-secondary-50));
410
410
  --gradient-hero-primary: var(--gradient-subtle);
411
411
 
412
412
  /* Vibrant accent gradient */
@@ -414,7 +414,7 @@
414
414
  --gradient-hero-vibrant: var(--gradient-vibrant);
415
415
 
416
416
  /* Card surface gradients */
417
- --gradient-card: linear-gradient(to bottom, hsla(225.1, 58.6%, 58.4%, 0.25), var(--color-neutral-50));
417
+ --gradient-card: linear-gradient(to bottom, var(--color-primary-50), var(--color-neutral-50));
418
418
  --gradient-primary-to-white: linear-gradient(to bottom, var(--color-primary-50), var(--color-surface));
419
419
  --gradient-secondary-to-white: linear-gradient(to bottom, var(--color-secondary-50), var(--color-surface));
420
420
  --gradient-primary-h: linear-gradient(to right, var(--color-primary-50), var(--color-surface));
@@ -442,4 +442,3 @@
442
442
  --frosted-gradient-primary: linear-gradient(to bottom right, hsla(240, 78.3%, 65%, 0.18), hsl(224 25% 6% / 0.60));
443
443
  --frosted-gradient-secondary: linear-gradient(to bottom right, hsla(47, 100%, 60%, 0.16), hsl(224 25% 6% / 0.60));
444
444
  }
445
-
@@ -209,22 +209,14 @@
209
209
  /* ==========================================================================
210
210
  * Border Radius Scale
211
211
  * ========================================================================== */
212
- --radius-xs: 0.125rem;
213
- /* 2px */
214
- --radius-sm: 0.25rem;
215
- /* 4px */
216
- --radius-md: 0.375rem;
217
- /* 6px */
218
- --radius-lg: 0.5rem;
219
- /* 8px */
220
- --radius-xl: 0.75rem;
221
- /* 12px */
222
- --radius-2xl: 1rem;
223
- /* 16px */
224
- --radius-3xl: 1.5rem;
225
- /* 24px */
226
- --radius-4xl: 2rem;
227
- /* 32px */
212
+ --radius-xs: 0.125rem; /* 2px */
213
+ --radius-sm: 0.25rem; /* 4px */
214
+ --radius-md: 0.375rem; /* 6px */
215
+ --radius-lg: 0.5rem; /* 8px */
216
+ --radius-xl: 0.75rem; /* 12px */
217
+ --radius-2xl: 1rem; /* 16px */
218
+ --radius-3xl: 1.5rem; /* 24px */
219
+ --radius-4xl: 2rem; /* 32px */
228
220
  --radius-full: 9999px;
229
221
  }
230
222
 
@@ -233,38 +225,22 @@
233
225
  * Spacing Scale
234
226
  * ========================================================================== */
235
227
  --spacing-0: 0px;
236
- --spacing-0-5: 0.125rem;
237
- /* 2px */
238
- --spacing-1: 0.25rem;
239
- /* 4px */
240
- --spacing-1-5: 0.375rem;
241
- /* 6px */
242
- --spacing-2: 0.5rem;
243
- /* 8px */
244
- --spacing-2-5: 0.625rem;
245
- /* 10px */
246
- --spacing-3: 0.75rem;
247
- /* 12px */
248
- --spacing-3-5: 0.875rem;
249
- /* 14px */
250
- --spacing-4: 1rem;
251
- /* 16px */
252
- --spacing-5: 1.25rem;
253
- /* 20px */
254
- --spacing-6: 1.5rem;
255
- /* 24px */
256
- --spacing-8: 2rem;
257
- /* 32px */
258
- --spacing-10: 2.5rem;
259
- /* 40px */
260
- --spacing-12: 3rem;
261
- /* 48px */
262
- --spacing-16: 4rem;
263
- /* 64px */
264
- --spacing-20: 5rem;
265
- /* 80px */
266
- --spacing-24: 6rem;
267
- /* 96px */
228
+ --spacing-0-5: 0.125rem; /* 2px */
229
+ --spacing-1: 0.25rem; /* 4px */
230
+ --spacing-1-5: 0.375rem; /* 6px */
231
+ --spacing-2: 0.5rem; /* 8px */
232
+ --spacing-2-5: 0.625rem; /* 10px */
233
+ --spacing-3: 0.75rem; /* 12px */
234
+ --spacing-3-5: 0.875rem; /* 14px */
235
+ --spacing-4: 1rem; /* 16px */
236
+ --spacing-5: 1.25rem; /* 20px */
237
+ --spacing-6: 1.5rem; /* 24px */
238
+ --spacing-8: 2rem; /* 32px */
239
+ --spacing-10: 2.5rem; /* 40px */
240
+ --spacing-12: 3rem; /* 48px */
241
+ --spacing-16: 4rem; /* 64px */
242
+ --spacing-20: 5rem; /* 80px */
243
+ --spacing-24: 6rem; /* 96px */
268
244
  }
269
245
 
270
246
  :root {
@@ -275,24 +251,15 @@
275
251
  --font-family-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
276
252
 
277
253
  /* Font Sizes */
278
- --font-size-xs: 0.75rem;
279
- /* 12px */
280
- --font-size-sm: 0.875rem;
281
- /* 14px */
282
- --font-size-base: 1rem;
283
- /* 16px */
284
- --font-size-lg: 1.125rem;
285
- /* 18px */
286
- --font-size-xl: 1.25rem;
287
- /* 20px */
288
- --font-size-2xl: 1.5rem;
289
- /* 24px */
290
- --font-size-3xl: 1.875rem;
291
- /* 30px */
292
- --font-size-4xl: 2.25rem;
293
- /* 36px */
294
- --font-size-5xl: 3rem;
295
- /* 48px */
254
+ --font-size-xs: 0.75rem; /* 12px */
255
+ --font-size-sm: 0.875rem; /* 14px */
256
+ --font-size-base: 1rem; /* 16px */
257
+ --font-size-lg: 1.125rem; /* 18px */
258
+ --font-size-xl: 1.25rem; /* 20px */
259
+ --font-size-2xl: 1.5rem; /* 24px */
260
+ --font-size-3xl: 1.875rem; /* 30px */
261
+ --font-size-4xl: 2.25rem; /* 36px */
262
+ --font-size-5xl: 3rem; /* 48px */
296
263
 
297
264
  /* Line Heights */
298
265
  --line-height-none: 1;
@@ -322,32 +289,32 @@
322
289
  /* ==========================================================================
323
290
  * Primary Brand Colors (Dark Scale)
324
291
  * ========================================================================== */
325
- --color-primary-0: hsl(240 78% 10%);
326
- --color-primary-50: hsla(225.1, 58.6%, 58.4%, 0.18);
327
- --color-primary-100: hsla(225.1, 58.6%, 58.4%, 0.28);
328
- --color-primary-200: hsl(240 78% 34%);
329
- --color-primary-300: hsl(234.2 73.9% 37.4%);
330
- --color-primary-400: hsl(229.7 65% 43%);
331
- --color-primary-500: hsl(226.9 53.8% 50.1%);
332
- --color-primary-600: hsl(225.1 58.6% 58.4%);
333
- --color-primary-700: hsl(224 63.5% 67.6%);
334
- --color-primary-800: hsl(223.3 68.7% 76.7%);
335
- --color-primary-900: hsl(222.5 100% 96.2%);
292
+ --color-primary-0: hsla(225.1, 58.6%, 58.4%, 0.05);
293
+ --color-primary-50: hsla(225.1, 58.6%, 58.4%, 0.12);
294
+ --color-primary-100: hsla(225.1, 58.6%, 58.4%, 0.20);
295
+ --color-primary-200: hsla(225.1, 58.6%, 58.4%, 0.32);
296
+ --color-primary-300: hsla(225.1, 58.6%, 58.4%, 0.48);
297
+ --color-primary-400: hsl(224 63.5% 67.6%);
298
+ --color-primary-500: hsl(225.1 58.6% 58.4%);
299
+ --color-primary-600: hsl(226.9 53.8% 50.1%);
300
+ --color-primary-700: hsl(229.7 65% 43%);
301
+ --color-primary-800: hsl(234.2 73.9% 37.4%);
302
+ --color-primary-900: hsl(240 78% 34%);
336
303
 
337
304
  /* ==========================================================================
338
305
  * Secondary Brand Colors (Dark Scale)
339
306
  * ========================================================================== */
340
- --color-secondary-0: hsl(47 100% 10%);
341
- --color-secondary-50: hsla(47, 100%, 68.6%, 0.16);
342
- --color-secondary-100: hsla(47, 100%, 68.6%, 0.25);
343
- --color-secondary-200: hsl(47 100% 50%);
344
- --color-secondary-300: hsl(47 100% 52.9%);
345
- --color-secondary-400: hsl(47 100% 57.3%);
346
- --color-secondary-500: hsl(47 100% 62.6%);
347
- --color-secondary-600: hsl(47 100% 68.6%);
348
- --color-secondary-700: hsl(47 100% 75.2%);
349
- --color-secondary-800: hsl(47 100% 81.6%);
350
- --color-secondary-900: hsl(47 100% 95.6%);
307
+ --color-secondary-0: hsla(47, 100%, 68.6%, 0.05);
308
+ --color-secondary-50: hsla(47, 100%, 68.6%, 0.12);
309
+ --color-secondary-100: hsla(47, 100%, 68.6%, 0.20);
310
+ --color-secondary-200: hsla(47, 100%, 68.6%, 0.32);
311
+ --color-secondary-300: hsla(47, 100%, 68.6%, 0.48);
312
+ --color-secondary-400: hsl(47 100% 75.2%);
313
+ --color-secondary-500: hsl(47 100% 68.6%);
314
+ --color-secondary-600: hsl(47 100% 62.6%);
315
+ --color-secondary-700: hsl(47 100% 57.3%);
316
+ --color-secondary-800: hsl(47 100% 52.9%);
317
+ --color-secondary-900: hsl(47 100% 50%);
351
318
 
352
319
  /* ==========================================================================
353
320
  * Neutral Palette (Dark Scale)
@@ -407,8 +374,8 @@
407
374
  --color-fg-inverse-disabled: var(--color-neutral-400);
408
375
 
409
376
  /* Brand Foreground */
410
- --color-fg-primary: var(--color-primary-600);
411
- --color-fg-secondary: var(--color-secondary-600);
377
+ --color-fg-primary: var(--color-primary-500);
378
+ --color-fg-secondary: var(--color-secondary-500);
412
379
 
413
380
  /* Semantic Foreground */
414
381
  --color-fg-success: var(--color-success);
@@ -444,7 +411,7 @@
444
411
  /* ==========================================================================
445
412
  * Gradients
446
413
  * ========================================================================== */
447
- --gradient-subtle: linear-gradient(to top right, var(--color-primary-50), var(--color-primary-0) 60%, var(--color-secondary-50));
414
+ --gradient-subtle: linear-gradient(to top right, var(--color-primary-50), var(--color-neutral-0) 60%, var(--color-secondary-50));
448
415
  --gradient-hero-primary: var(--gradient-subtle);
449
416
 
450
417
  /* Vibrant accent gradient */
@@ -452,7 +419,7 @@
452
419
  --gradient-hero-vibrant: var(--gradient-vibrant);
453
420
 
454
421
  /* Card surface gradients */
455
- --gradient-card: linear-gradient(to bottom, hsla(225.1, 58.6%, 58.4%, 0.25), var(--color-neutral-50));
422
+ --gradient-card: linear-gradient(to bottom, var(--color-primary-50), var(--color-neutral-50));
456
423
  --gradient-primary-to-white: linear-gradient(to bottom, var(--color-primary-50), var(--color-surface));
457
424
  --gradient-secondary-to-white: linear-gradient(to bottom, var(--color-secondary-50), var(--color-surface));
458
425
  --gradient-primary-h: linear-gradient(to right, var(--color-primary-50), var(--color-surface));
@@ -482,7 +449,6 @@
482
449
  }
483
450
 
484
451
 
485
-
486
452
  /* Component Styles */
487
453
  /* ==========================================================================
488
454
  * ODT Lightweight UI - Standalone Component Styles (Rails)
@@ -1957,7 +1923,7 @@
1957
1923
  .odt-select--md {
1958
1924
  height: 2.625rem;
1959
1925
  padding: 0 var(--spacing-4, 1rem);
1960
- font-size: var(--font-size-sm, 0.875rem);
1926
+ font-size: var(--font-size-base, 1rem);
1961
1927
  border-radius: var(--radius-xl, 0.75rem);
1962
1928
  }
1963
1929
 
@@ -1965,7 +1931,7 @@
1965
1931
  .odt-select--lg {
1966
1932
  height: 3.125rem;
1967
1933
  padding: 0 var(--spacing-5, 1.25rem);
1968
- font-size: var(--font-size-base, 1rem);
1934
+ font-size: var(--font-size-lg, 1.125rem);
1969
1935
  border-radius: var(--radius-2xl, 1rem);
1970
1936
  }
1971
1937
 
@@ -1984,14 +1950,14 @@
1984
1950
 
1985
1951
  .odt-textarea--md {
1986
1952
  padding: var(--spacing-3, 0.75rem) var(--spacing-4, 1rem);
1987
- font-size: var(--font-size-sm, 0.875rem);
1953
+ font-size: var(--font-size-base, 1rem);
1988
1954
  min-height: 5rem;
1989
1955
  border-radius: var(--radius-xl, 0.75rem);
1990
1956
  }
1991
1957
 
1992
1958
  .odt-textarea--lg {
1993
1959
  padding: var(--spacing-4, 1rem) var(--spacing-5, 1.25rem);
1994
- font-size: var(--font-size-base, 1rem);
1960
+ font-size: var(--font-size-lg, 1.125rem);
1995
1961
  min-height: 6.5rem;
1996
1962
  border-radius: var(--radius-2xl, 1rem);
1997
1963
  }
@@ -2244,34 +2210,36 @@
2244
2210
  margin-top: 0.0625rem;
2245
2211
  }
2246
2212
 
2247
- /* Checkmark indicator */
2248
- .odt-checkbox:checked::after {
2249
- content: "";
2250
- display: block;
2251
- width: 4px;
2252
- height: 8px;
2253
- border: solid currentColor;
2254
- border-width: 0 2px 2px 0;
2255
- transform: rotate(45deg);
2256
- margin-bottom: 2px;
2257
- animation: odt-pop 140ms cubic-bezier(0.16, 1, 0.3, 1) forwards;
2258
- }
2213
+ /* Checkmark indicator */
2214
+ .odt-checkbox:checked::after {
2215
+ content: "";
2216
+ display: block;
2217
+ width: 0.75rem;
2218
+ height: 0.75rem;
2219
+ background-color: currentColor;
2220
+ -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='3.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E");
2221
+ mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='3.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E");
2222
+ -webkit-mask-size: contain;
2223
+ mask-size: contain;
2224
+ -webkit-mask-repeat: no-repeat;
2225
+ mask-repeat: no-repeat;
2226
+ -webkit-mask-position: center;
2227
+ mask-position: center;
2228
+ animation: odt-pop 140ms cubic-bezier(0.16, 1, 0.3, 1) forwards;
2229
+ }
2259
2230
 
2260
- .odt-checkbox--sm:checked::after {
2261
- width: 3px;
2262
- height: 6px;
2263
- border-width: 0 1.5px 1.5px 0;
2264
- margin-bottom: 1px;
2265
- }
2231
+ .odt-checkbox--sm:checked::after {
2232
+ width: 0.625rem;
2233
+ height: 0.625rem;
2234
+ }
2266
2235
 
2267
- .odt-checkbox--lg:checked::after {
2268
- width: 5px;
2269
- height: 10px;
2270
- border-width: 0 2.5px 2.5px 0;
2271
- margin-bottom: 2px;
2272
- }
2236
+ .odt-checkbox--lg:checked::after {
2237
+ width: 0.875rem;
2238
+ height: 0.875rem;
2239
+ }
2240
+
2241
+ /* Radio Dot indicator */
2273
2242
 
2274
- /* Radio Dot indicator */
2275
2243
  .odt-radio:checked::after {
2276
2244
  content: "";
2277
2245
  display: block;
@@ -2385,6 +2353,196 @@
2385
2353
  line-height: 1rem;
2386
2354
  }
2387
2355
 
2356
+ /* ==========================================================================
2357
+ * Switch Component (.odt-switch)
2358
+ * ========================================================================== */
2359
+ .odt-switch-wrapper {
2360
+ display: inline-flex;
2361
+ flex-direction: column;
2362
+ gap: 0.25rem;
2363
+ }
2364
+
2365
+ .odt-switch-container {
2366
+ display: inline-flex;
2367
+ align-items: center;
2368
+ gap: var(--spacing-3, 0.75rem);
2369
+ cursor: pointer;
2370
+ user-select: none;
2371
+ }
2372
+
2373
+ .odt-switch {
2374
+ appearance: none;
2375
+ -webkit-appearance: none;
2376
+ position: relative;
2377
+ display: inline-flex;
2378
+ align-items: center;
2379
+ flex-shrink: 0;
2380
+ width: 2.75rem;
2381
+ height: 1.5rem;
2382
+ border-radius: var(--radius-full, 9999px);
2383
+ background-color: var(--color-neutral-300);
2384
+ border: none;
2385
+ cursor: pointer;
2386
+ padding: 0;
2387
+ margin: 0;
2388
+ outline: none;
2389
+ box-sizing: border-box;
2390
+ transition: background-color 0.2s ease-in-out, box-shadow 0.2s ease-in-out;
2391
+ }
2392
+
2393
+ .odt-switch::after {
2394
+ content: "";
2395
+ pointer-events: none;
2396
+ display: block;
2397
+ width: 1rem;
2398
+ height: 1rem;
2399
+ border-radius: var(--radius-full, 9999px);
2400
+ background-color: var(--color-switch-thumb, #ffffff);
2401
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
2402
+ transform: translateX(0.25rem);
2403
+ transition: transform 0.2s cubic-bezier(0.4, 0, 0.2, 1);
2404
+ }
2405
+
2406
+ .odt-switch:checked::after {
2407
+ transform: translateX(1.5rem);
2408
+ }
2409
+
2410
+ /* Switch Sizes */
2411
+ .odt-switch--sm {
2412
+ width: 2rem;
2413
+ height: 1.125rem;
2414
+ }
2415
+
2416
+ .odt-switch--sm::after {
2417
+ width: 0.75rem;
2418
+ height: 0.75rem;
2419
+ transform: translateX(0.1875rem);
2420
+ }
2421
+
2422
+ .odt-switch--sm:checked::after {
2423
+ transform: translateX(1.0625rem);
2424
+ }
2425
+
2426
+ .odt-switch--md {
2427
+ width: 2.75rem;
2428
+ height: 1.5rem;
2429
+ }
2430
+
2431
+ .odt-switch--md::after {
2432
+ width: 1rem;
2433
+ height: 1rem;
2434
+ transform: translateX(0.25rem);
2435
+ }
2436
+
2437
+ .odt-switch--md:checked::after {
2438
+ transform: translateX(1.5rem);
2439
+ }
2440
+
2441
+ .odt-switch--lg {
2442
+ width: 3.5rem;
2443
+ height: 2rem;
2444
+ }
2445
+
2446
+ .odt-switch--lg::after {
2447
+ width: 1.5rem;
2448
+ height: 1.5rem;
2449
+ transform: translateX(0.25rem);
2450
+ }
2451
+
2452
+ .odt-switch--lg:checked::after {
2453
+ transform: translateX(1.75rem);
2454
+ }
2455
+
2456
+ /* Switch Colors */
2457
+ .odt-switch--primary:checked {
2458
+ background-color: var(--color-primary-500);
2459
+ }
2460
+
2461
+ .odt-switch--primary:focus-visible {
2462
+ box-shadow: 0 0 0 3px hsl(from var(--color-primary-500) h s l / 0.35);
2463
+ }
2464
+
2465
+ .odt-switch--secondary:checked {
2466
+ background-color: var(--color-secondary-500);
2467
+ }
2468
+
2469
+ .odt-switch--secondary:focus-visible {
2470
+ box-shadow: 0 0 0 3px hsl(from var(--color-secondary-500) h s l / 0.35);
2471
+ }
2472
+
2473
+ .odt-switch--success:checked {
2474
+ background-color: var(--color-success);
2475
+ }
2476
+
2477
+ .odt-switch--success:focus-visible {
2478
+ box-shadow: 0 0 0 3px hsl(from var(--color-success) h s l / 0.35);
2479
+ }
2480
+
2481
+ .odt-switch--warning:checked {
2482
+ background-color: var(--color-warning);
2483
+ }
2484
+
2485
+ .odt-switch--warning:focus-visible {
2486
+ box-shadow: 0 0 0 3px hsl(from var(--color-warning) h s l / 0.35);
2487
+ }
2488
+
2489
+ .odt-switch--danger:checked {
2490
+ background-color: var(--color-danger);
2491
+ }
2492
+
2493
+ .odt-switch--danger:focus-visible {
2494
+ box-shadow: 0 0 0 3px hsl(from var(--color-danger) h s l / 0.35);
2495
+ }
2496
+
2497
+ .odt-switch--info:checked {
2498
+ background-color: var(--color-info);
2499
+ }
2500
+
2501
+ .odt-switch--info:focus-visible {
2502
+ box-shadow: 0 0 0 3px hsl(from var(--color-info) h s l / 0.35);
2503
+ }
2504
+
2505
+ .odt-switch--dark:checked,
2506
+ .odt-switch--neutral:checked {
2507
+ background-color: var(--color-neutral-900);
2508
+ }
2509
+
2510
+ .odt-switch--dark:focus-visible,
2511
+ .odt-switch--neutral:focus-visible {
2512
+ box-shadow: 0 0 0 3px hsl(from var(--color-neutral-900) h s l / 0.35);
2513
+ }
2514
+
2515
+ /* Switch States */
2516
+ .odt-switch:disabled,
2517
+ .odt-switch-container--disabled {
2518
+ opacity: 0.45;
2519
+ cursor: not-allowed;
2520
+ pointer-events: none;
2521
+ }
2522
+
2523
+ .odt-switch--error:not(:checked) {
2524
+ box-shadow: 0 0 0 2px var(--color-danger);
2525
+ }
2526
+
2527
+ .odt-switch__label-content {
2528
+ display: flex;
2529
+ flex-direction: column;
2530
+ gap: 0.125rem;
2531
+ }
2532
+
2533
+ .odt-switch__label {
2534
+ font-size: var(--font-size-sm, 0.875rem);
2535
+ color: var(--color-fg);
2536
+ font-weight: 500;
2537
+ line-height: 1.25rem;
2538
+ }
2539
+
2540
+ .odt-switch__description {
2541
+ font-size: var(--font-size-xs, 0.75rem);
2542
+ color: var(--color-fg-muted);
2543
+ line-height: 1rem;
2544
+ }
2545
+
2388
2546
  /* ==========================================================================
2389
2547
  * 10. Toast & Toaster Component (.odt-toaster, .odt-toast)
2390
2548
  * ========================================================================== */
@@ -2936,13 +3094,13 @@
2936
3094
  .odt-dropdown__trigger--md {
2937
3095
  height: 2.625rem;
2938
3096
  padding: 0 var(--spacing-4, 1rem);
2939
- font-size: var(--font-size-sm, 0.875rem);
3097
+ font-size: var(--font-size-base, 1rem);
2940
3098
  }
2941
3099
 
2942
3100
  .odt-dropdown__trigger--lg {
2943
3101
  height: 3.125rem;
2944
3102
  padding: 0 var(--spacing-5, 1.25rem);
2945
- font-size: var(--font-size-base, 1rem);
3103
+ font-size: var(--font-size-lg, 1.125rem);
2946
3104
  }
2947
3105
 
2948
3106
  .odt-dropdown__trigger-text {
@@ -41,6 +41,9 @@ module OdtUi
41
41
  type radio_size = :sm | :md | :lg
42
42
  type radio_orientation = :horizontal | :vertical
43
43
 
44
+ type switch_color = :primary | :secondary | :success | :warning | :danger | :info | :dark
45
+ type switch_size = :sm | :md | :lg
46
+
44
47
  def odt_button: (
45
48
  ?String? text,
46
49
  ?variant: button_variant,
@@ -199,6 +202,17 @@ module OdtUi
199
202
  **untyped
200
203
  ) -> untyped
201
204
 
205
+ def odt_switch: (
206
+ ?label: String?,
207
+ ?description: String?,
208
+ ?checked: bool,
209
+ ?color: switch_color,
210
+ ?size: switch_size,
211
+ ?disabled: bool,
212
+ ?error: String?,
213
+ **untyped
214
+ ) ?{ () -> untyped } -> untyped
215
+
202
216
  def odt_form_group: (
203
217
  ?label: String?,
204
218
  ?error: String?,
data/lib/odt_ui/helper.rb CHANGED
@@ -457,6 +457,61 @@ module OdtUi
457
457
  end
458
458
  end
459
459
 
460
+ def odt_switch(label: nil, description: nil, checked: false, color: :primary, size: :md, disabled: false, error: nil, **html_options, &block)
461
+ color_key = color.to_sym == :dark ? "neutral" : color.to_s.tr("_", "-")
462
+ size_key = size.to_s.tr("_", "-")
463
+ has_error = error.present?
464
+
465
+ classes = [
466
+ "odt-switch",
467
+ "odt-switch--#{color_key}",
468
+ "odt-switch--#{size_key}"
469
+ ]
470
+ classes << "odt-switch--error" if has_error
471
+ classes << html_options.delete(:class)
472
+
473
+ opts = html_options.merge(
474
+ type: "checkbox",
475
+ role: "switch",
476
+ class: classes.compact.reject(&:blank?).join(" "),
477
+ disabled: disabled
478
+ )
479
+ opts[:checked] = "checked" if checked
480
+ opts["aria-checked"] = checked ? "true" : "false"
481
+
482
+ switch_input = tag.input(**opts)
483
+
484
+ resolved_label = block_given? ? capture(&block) : label
485
+
486
+ if resolved_label.blank? && description.blank? && !has_error
487
+ return switch_input
488
+ end
489
+
490
+ container_classes = ["odt-switch-container"]
491
+ container_classes << "odt-switch-container--disabled" if disabled
492
+
493
+ tag.div(class: "odt-switch-wrapper") do
494
+ parts = []
495
+ parts << tag.label(class: container_classes.join(" ")) do
496
+ label_parts = [switch_input]
497
+
498
+ if resolved_label.present? || description.present?
499
+ label_parts << tag.div(class: "odt-switch__label-content") do
500
+ d_parts = []
501
+ d_parts << tag.span(resolved_label, class: "odt-switch__label") if resolved_label.present?
502
+ d_parts << tag.span(description, class: "odt-switch__description") if description.present?
503
+ safe_join(d_parts)
504
+ end
505
+ end
506
+
507
+ safe_join(label_parts)
508
+ end
509
+
510
+ parts << tag.p(error, class: "odt-error-text", role: "alert") if has_error
511
+ safe_join(parts)
512
+ end
513
+ end
514
+
460
515
  def odt_form_group(label: nil, error: nil, helper: nil, helper_text: nil, success: nil, required: false, **html_options, &block)
461
516
  resolved_helper = helper.presence || helper_text.presence
462
517
  has_error = error.present?
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module OdtUi
4
- VERSION = "2.2.8"
4
+ VERSION = "2.2.10"
5
5
  end
data/sig/odt_ui.rbs CHANGED
@@ -41,6 +41,9 @@ module OdtUi
41
41
  type radio_size = :sm | :md | :lg
42
42
  type radio_orientation = :horizontal | :vertical
43
43
 
44
+ type switch_color = :primary | :secondary | :success | :warning | :danger | :info | :dark
45
+ type switch_size = :sm | :md | :lg
46
+
44
47
  def odt_button: (
45
48
  ?String? text,
46
49
  ?variant: button_variant,
@@ -199,6 +202,17 @@ module OdtUi
199
202
  **untyped
200
203
  ) -> untyped
201
204
 
205
+ def odt_switch: (
206
+ ?label: String?,
207
+ ?description: String?,
208
+ ?checked: bool,
209
+ ?color: switch_color,
210
+ ?size: switch_size,
211
+ ?disabled: bool,
212
+ ?error: String?,
213
+ **untyped
214
+ ) ?{ () -> untyped } -> untyped
215
+
202
216
  def odt_form_group: (
203
217
  ?label: String?,
204
218
  ?error: String?,
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: odt-ui-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.2.8
4
+ version: 2.2.10
5
5
  platform: ruby
6
6
  authors:
7
7
  - Pixel