lostcoords-ui 0.4.1 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +303 -89
  3. data/app/assets/tailwind/lostcoords/ui/avatar.css +6 -6
  4. data/app/assets/tailwind/lostcoords/ui/base.css +45 -0
  5. data/app/assets/tailwind/lostcoords/ui/card.css +20 -22
  6. data/app/assets/tailwind/lostcoords/ui/choice.css +39 -0
  7. data/app/assets/tailwind/lostcoords/ui/code-block.css +10 -0
  8. data/app/assets/tailwind/lostcoords/ui/components.css +25 -0
  9. data/app/assets/tailwind/lostcoords/ui/copy-button.css +9 -9
  10. data/app/assets/tailwind/lostcoords/ui/dialog.css +17 -17
  11. data/app/assets/tailwind/lostcoords/ui/disclosure.css +5 -5
  12. data/app/assets/tailwind/lostcoords/ui/emoji-picker.css +43 -43
  13. data/app/assets/tailwind/lostcoords/ui/field.css +2 -2
  14. data/app/assets/tailwind/lostcoords/ui/glass.css +9 -8
  15. data/app/assets/tailwind/lostcoords/ui/layout.css +2 -2
  16. data/app/assets/tailwind/lostcoords/ui/popover.css +14 -14
  17. data/app/assets/tailwind/lostcoords/ui/reading.css +24 -24
  18. data/app/assets/tailwind/lostcoords/ui/sheet.css +60 -60
  19. data/app/assets/tailwind/lostcoords/ui/squircle.css +7 -27
  20. data/app/assets/tailwind/lostcoords/ui/table.css +51 -0
  21. data/app/assets/tailwind/lostcoords/ui/theme.css +194 -225
  22. data/app/assets/tailwind/lostcoords/ui/tooltip.css +50 -0
  23. data/app/assets/tailwind/lostcoords/ui/typography.css +37 -61
  24. data/app/assets/tailwind/lostcoords/ui/ui.css +2 -21
  25. data/app/assets/tailwind/lostcoords/ui/utilities.css +14 -22
  26. data/app/helpers/lostcoords/ui/badge_helper.rb +67 -110
  27. data/app/helpers/lostcoords/ui/button_helper.rb +36 -33
  28. data/app/helpers/lostcoords/ui/card_helper.rb +9 -52
  29. data/app/helpers/lostcoords/ui/choice_helper.rb +83 -0
  30. data/app/helpers/lostcoords/ui/code_block_helper.rb +22 -0
  31. data/app/helpers/lostcoords/ui/copy_button_helper.rb +7 -7
  32. data/app/helpers/lostcoords/ui/description_list_helper.rb +12 -0
  33. data/app/helpers/lostcoords/ui/dialog_helper.rb +6 -13
  34. data/app/helpers/lostcoords/ui/feedback_helper.rb +25 -6
  35. data/app/helpers/lostcoords/ui/form_helper.rb +36 -59
  36. data/app/helpers/lostcoords/ui/glass_helper.rb +7 -7
  37. data/app/helpers/lostcoords/ui/layout_helper.rb +13 -11
  38. data/app/helpers/lostcoords/ui/menu_helper.rb +8 -8
  39. data/app/helpers/lostcoords/ui/navigation_helper.rb +131 -0
  40. data/app/helpers/lostcoords/ui/overlay_helper.rb +17 -0
  41. data/app/helpers/lostcoords/ui/primitives_helper.rb +39 -61
  42. data/app/helpers/lostcoords/ui/reading_helper.rb +2 -2
  43. data/app/helpers/lostcoords/ui/sheet_helper.rb +3 -45
  44. data/app/helpers/lostcoords/ui/structures_helper.rb +55 -64
  45. data/app/helpers/lostcoords/ui/table_helper.rb +11 -0
  46. data/app/helpers/lostcoords/ui/tailwind_helper.rb +13 -4
  47. data/app/helpers/lostcoords/ui/typography_helper.rb +11 -11
  48. data/app/javascript/lostcoords/ui/controllers/lc_code_block_controller.js +23 -0
  49. data/app/javascript/lostcoords/ui/controllers/{disclosure_controller.js → lc_collapsible_controller.js} +3 -3
  50. data/app/javascript/lostcoords/ui/controllers/{confirm_dialog_controller.js → lc_confirm_dialog_controller.js} +4 -4
  51. data/app/javascript/lostcoords/ui/controllers/{modal_controller.js → lc_dialog_controller.js} +1 -1
  52. data/app/javascript/lostcoords/ui/controllers/{emoji_picker_controller.js → lc_emoji_picker_controller.js} +4 -4
  53. data/app/javascript/lostcoords/ui/controllers/{row_menu_controller.js → lc_menu_controller.js} +3 -3
  54. data/app/javascript/lostcoords/ui/controllers/{ui_popover_controller.js → lc_popover_controller.js} +1 -1
  55. data/app/javascript/lostcoords/ui/controllers/{ui_selection_controller.js → lc_selection_controller.js} +1 -1
  56. data/app/javascript/lostcoords/ui/controllers/{sheet_controller.js → lc_sheet_controller.js} +15 -15
  57. data/app/javascript/lostcoords/ui/controllers/lc_tabs_controller.js +39 -0
  58. data/app/javascript/lostcoords/ui/controllers/{toast_controller.js → lc_toast_controller.js} +2 -2
  59. data/app/javascript/lostcoords/ui/controllers/lc_tooltip_controller.js +14 -0
  60. data/app/javascript/lostcoords/ui/helpers/catalog_picker.js +5 -5
  61. data/app/javascript/lostcoords/ui/helpers/context_dialog.js +6 -6
  62. data/app/javascript/lostcoords/ui/helpers/dialog.js +2 -2
  63. data/app/javascript/lostcoords/ui/helpers/dismiss_stack.js +7 -7
  64. data/app/javascript/lostcoords/ui/helpers/floating_panel.js +2 -2
  65. data/app/javascript/lostcoords/ui/helpers/sheet_motion.js +4 -4
  66. data/app/javascript/lostcoords/ui/index.js +51 -0
  67. data/app/views/lostcoords/ui/components/_emoji_picker.html.erb +30 -30
  68. data/app/views/lostcoords/ui/components/_lc_code_block.html.erb +18 -0
  69. data/app/views/lostcoords/ui/components/_lc_collapsible.html.erb +5 -0
  70. data/app/views/lostcoords/ui/components/_lc_dialog.html.erb +11 -8
  71. data/app/views/lostcoords/ui/components/_lc_floating_panel.html.erb +8 -0
  72. data/config/helper_renames.json +23 -25
  73. data/config/importmap.rb +10 -2
  74. data/config/locales/en.yml +23 -0
  75. data/config/locales/es.yml +23 -0
  76. data/config/locales/ja.yml +23 -0
  77. data/lib/lostcoords/ui/action_view.rb +5 -3
  78. data/lib/lostcoords/ui/builders.rb +1 -1
  79. data/lib/lostcoords/ui/form_builder.rb +113 -0
  80. data/lib/lostcoords/ui/parts.rb +154 -0
  81. data/lib/lostcoords/ui/tokens.rb +24 -1
  82. data/lib/lostcoords/ui/version.rb +1 -1
  83. metadata +33 -17
  84. data/app/views/lostcoords/ui/components/_form_alert.html.erb +0 -4
  85. data/app/views/lostcoords/ui/components/_lc_disclosure.html.erb +0 -5
  86. data/app/views/lostcoords/ui/components/_skeleton.html.erb +0 -4
  87. data/app/views/lostcoords/ui/components/_spinner.html.erb +0 -10
  88. data/app/views/lostcoords/ui/components/_ui_floating_panel.html.erb +0 -8
  89. /data/app/javascript/lostcoords/ui/controllers/{ui_copy_controller.js → lc_copy_controller.js} +0 -0
  90. /data/app/javascript/lostcoords/ui/controllers/{ui_toggle_controller.js → lc_toggle_controller.js} +0 -0
@@ -0,0 +1,39 @@
1
+ @layer components {
2
+ /* The thumb is a gradient on the input itself, so the switch needs no extra
3
+ element. Forced colors fall back to the native checkbox. */
4
+ .lc-switch {
5
+ --lc-switch-thumb: var(--background);
6
+
7
+ appearance: none;
8
+ inline-size: calc(var(--spacing) * 9);
9
+ block-size: calc(var(--spacing) * 5.5);
10
+ border-radius: var(--radius-pill);
11
+ background-color: var(--input);
12
+ background-image: radial-gradient(circle closest-side, var(--lc-switch-thumb) calc(100% - var(--spacing) * 0.375), transparent 100%);
13
+ background-size: calc(var(--spacing) * 5.5) calc(var(--spacing) * 5.5);
14
+ background-repeat: no-repeat;
15
+ background-position: left center;
16
+ transition:
17
+ background-color var(--lc-motion-control) var(--lc-ease-out),
18
+ background-position var(--lc-motion-control) var(--lc-ease-out);
19
+
20
+ &:checked {
21
+ --lc-switch-thumb: var(--primary-foreground);
22
+
23
+ background-color: var(--primary);
24
+ background-position: right center;
25
+ }
26
+
27
+ &:dir(rtl) { background-position: right center; }
28
+ &:dir(rtl):checked { background-position: left center; }
29
+
30
+ @media (forced-colors: active) {
31
+ appearance: auto;
32
+ }
33
+ }
34
+
35
+ /* Rails wraps invalid controls in .field_with_errors; library controls ignore the wrapper. */
36
+ .field_with_errors:has(> [data-slot]) {
37
+ display: contents;
38
+ }
39
+ }
@@ -0,0 +1,10 @@
1
+ @layer components {
2
+ @media (hover: hover) and (pointer: fine) {
3
+ .lc-code-block-copy { opacity: 0; }
4
+
5
+ .lc-code-block:is(:hover, :focus-within) .lc-code-block-copy,
6
+ .lc-code-block-copy:has([data-state="copied"], [data-state="error"]) {
7
+ opacity: 1;
8
+ }
9
+ }
10
+ }
@@ -0,0 +1,25 @@
1
+ @import "./theme.css";
2
+ @source "../../../../helpers/**/*.rb";
3
+ @source "../../../../javascript/**/*.js";
4
+ @source "../../../../views/**/*.erb";
5
+ @source "../../../../../lib/lostcoords/ui/parts.rb";
6
+
7
+ @import "./glass.css";
8
+ @import "./copy-button.css";
9
+ @import "./code-block.css";
10
+ @import "./disclosure.css";
11
+ @import "./emoji-picker.css";
12
+ @import "./popover.css";
13
+ @import "./sheet.css";
14
+ @import "./card.css";
15
+ @import "./dialog.css";
16
+ @import "./avatar.css";
17
+ @import "./squircle.css";
18
+ @import "./utilities.css";
19
+ @import "./typography.css";
20
+ @import "./layout.css";
21
+ @import "./reading.css";
22
+ @import "./field.css";
23
+ @import "./table.css";
24
+ @import "./choice.css";
25
+ @import "./tooltip.css";
@@ -1,7 +1,7 @@
1
1
  @layer components {
2
- .ui-copy { display: inline-grid; }
2
+ .lc-copy { display: inline-grid; }
3
3
 
4
- .ui-copy-content {
4
+ .lc-copy-content {
5
5
  grid-area: 1 / 1;
6
6
  display: inline-flex;
7
7
  align-items: center;
@@ -11,21 +11,21 @@
11
11
  visibility: hidden;
12
12
  transform: translateY(0.2rem) scale(0.96);
13
13
  transition:
14
- opacity var(--ui-motion-feedback) var(--ease-out),
15
- transform var(--ui-motion-feedback) var(--ease-out),
16
- visibility var(--ui-motion-feedback);
14
+ opacity var(--lc-motion-feedback) var(--lc-ease-out),
15
+ transform var(--lc-motion-feedback) var(--lc-ease-out),
16
+ visibility var(--lc-motion-feedback);
17
17
  }
18
18
 
19
- .ui-copy[data-state="idle"] [data-copy-state="idle"],
20
- .ui-copy[data-state="copied"] [data-copy-state="copied"],
21
- .ui-copy[data-state="error"] [data-copy-state="error"] {
19
+ .lc-copy[data-state="idle"] [data-copy-state="idle"],
20
+ .lc-copy[data-state="copied"] [data-copy-state="copied"],
21
+ .lc-copy[data-state="error"] [data-copy-state="error"] {
22
22
  opacity: 1;
23
23
  visibility: visible;
24
24
  transform: none;
25
25
  }
26
26
 
27
27
  @media (prefers-reduced-motion: reduce) {
28
- .ui-copy-content {
28
+ .lc-copy-content {
29
29
  transform: none;
30
30
  transition: none;
31
31
  }
@@ -1,20 +1,20 @@
1
1
  @layer components {
2
- .app-dialog {
3
- --dialog-from: translateY(0.4rem) scale(0.98);
4
- --dialog-fade: 0.2s;
5
- --dialog-duration: 0.2s;
2
+ .lc-dialog {
3
+ --lc-dialog-from: translateY(0.4rem) scale(0.98);
4
+ --lc-dialog-fade: 0.2s;
5
+ --lc-dialog-duration: 0.2s;
6
6
 
7
7
  /* Preflight's `margin: 0` clobbers the UA `margin: auto` that centres a modal dialog. */
8
8
  margin: auto;
9
9
  overflow: hidden;
10
10
  overscroll-behavior: contain;
11
11
  opacity: 0;
12
- transform: var(--dialog-from);
12
+ transform: var(--lc-dialog-from);
13
13
  transition:
14
- opacity var(--dialog-fade) var(--ease-out-expo),
15
- transform var(--dialog-duration) var(--ease-out-expo),
16
- display var(--dialog-duration) allow-discrete,
17
- overlay var(--dialog-duration) allow-discrete;
14
+ opacity var(--lc-dialog-fade) var(--lc-ease-out-expo),
15
+ transform var(--lc-dialog-duration) var(--lc-ease-out-expo),
16
+ display var(--lc-dialog-duration) allow-discrete,
17
+ overlay var(--lc-dialog-duration) allow-discrete;
18
18
 
19
19
  &[open] {
20
20
  opacity: 1;
@@ -22,7 +22,7 @@
22
22
 
23
23
  @starting-style {
24
24
  opacity: 0;
25
- transform: var(--dialog-from);
25
+ transform: var(--lc-dialog-from);
26
26
  }
27
27
  }
28
28
 
@@ -32,14 +32,14 @@
32
32
  background: transparent;
33
33
  backdrop-filter: blur(0);
34
34
  transition:
35
- background-color var(--dialog-fade) var(--ease-out),
36
- backdrop-filter var(--dialog-fade) var(--ease-out),
37
- display var(--dialog-fade) allow-discrete,
38
- overlay var(--dialog-fade) allow-discrete;
35
+ background-color var(--lc-dialog-fade) var(--lc-ease-out),
36
+ backdrop-filter var(--lc-dialog-fade) var(--lc-ease-out),
37
+ display var(--lc-dialog-fade) allow-discrete,
38
+ overlay var(--lc-dialog-fade) allow-discrete;
39
39
  }
40
40
 
41
41
  &[open]::backdrop {
42
- background: var(--scrim);
42
+ background: var(--lc-scrim);
43
43
  backdrop-filter: blur(2px);
44
44
 
45
45
  @starting-style {
@@ -48,8 +48,8 @@
48
48
  }
49
49
  }
50
50
 
51
- &.context-dialog[open]::backdrop {
52
- background: var(--scrim-soft);
51
+ &.lc-dialog-context[open]::backdrop {
52
+ background: var(--lc-scrim-soft);
53
53
  backdrop-filter: none;
54
54
  }
55
55
 
@@ -2,7 +2,7 @@
2
2
  panel open under [data-closing] for the exit, then closes it. Transitioning
3
3
  content-visibility makes WebKit keep the panel hidden for part of the opening. */
4
4
  @layer components {
5
- .disclosure-animated {
5
+ .lc-collapsible {
6
6
  interpolate-size: allow-keywords; /* browser-compat: enhancement */
7
7
 
8
8
  &::details-content {
@@ -11,9 +11,9 @@
11
11
  opacity: 0;
12
12
  translate: 0 -0.375rem;
13
13
  transition:
14
- block-size 420ms var(--ease-out-expo),
15
- opacity 220ms var(--ease-out),
16
- translate 320ms var(--ease-out-expo);
14
+ block-size 420ms var(--lc-ease-out-expo),
15
+ opacity 220ms var(--lc-ease-out),
16
+ translate 320ms var(--lc-ease-out-expo);
17
17
  }
18
18
 
19
19
  &[open]::details-content {
@@ -28,7 +28,7 @@
28
28
  opacity: 0;
29
29
  translate: 0 -0.375rem;
30
30
  transition-duration: 260ms, 160ms, 200ms;
31
- transition-timing-function: var(--ease-out);
31
+ transition-timing-function: var(--lc-ease-out);
32
32
  }
33
33
 
34
34
  @supports (interpolate-size: allow-keywords) {
@@ -6,8 +6,8 @@
6
6
  align-items: center;
7
7
 
8
8
  /* Sit above the mobile app nav (z-100 + 25) so the sheet covers it. */
9
- --emoji-sheet-z: calc(100 + 30);
10
- --emoji-cell: 2.25rem;
9
+ --lc-emoji-sheet-z: calc(100 + 30);
10
+ --lc-emoji-cell: 2.25rem;
11
11
  }
12
12
 
13
13
  /* ---- Trigger + quick choices -------------------------------------- */
@@ -18,28 +18,28 @@
18
18
  inline-size: 44px;
19
19
  block-size: 44px;
20
20
  border-radius: var(--radius-2xl, 1rem);
21
- border: 1px solid var(--hairline);
22
- background: var(--paper);
23
- color: var(--ink);
21
+ border: 1px solid var(--border);
22
+ background: var(--background);
23
+ color: var(--foreground);
24
24
  cursor: pointer;
25
25
  font-size: var(--text-xl, 1.25rem);
26
26
  line-height: 1;
27
27
  transition:
28
28
  border-color 0.15s,
29
29
  background 0.15s,
30
- transform 0.15s var(--ease-out),
30
+ transform 0.15s var(--lc-ease-out),
31
31
  box-shadow 0.15s;
32
32
 
33
- &:hover { border-color: var(--ink-3); }
33
+ &:hover { border-color: var(--muted-foreground); }
34
34
 
35
35
  &[aria-current="true"] {
36
- border-color: var(--sky);
37
- background: var(--sky-soft);
36
+ border-color: var(--primary);
37
+ background: color-mix(in oklab, var(--primary) 12%, var(--background));
38
38
  }
39
39
  }
40
40
 
41
41
  .emoji-picker__trigger {
42
- color: var(--ink-3);
42
+ color: var(--muted-foreground);
43
43
  font-weight: var(--font-weight-normal, 400);
44
44
  }
45
45
 
@@ -59,10 +59,10 @@
59
59
  transitions keep it in the top layer while it eases out, so it fades
60
60
  both ways instead of snapping away on close. */
61
61
  .emoji-picker__popover::backdrop {
62
- background: var(--scrim);
62
+ background: var(--lc-scrim);
63
63
  opacity: 0;
64
64
  transition:
65
- opacity 220ms var(--ease-out),
65
+ opacity 220ms var(--lc-ease-out),
66
66
  display 220ms allow-discrete,
67
67
  overlay 220ms allow-discrete;
68
68
 
@@ -83,10 +83,10 @@
83
83
  floating surface (glass in wariwari), so the gem leaves it unpainted. */
84
84
  @media (width < 40rem) {
85
85
  .emoji-picker__popover {
86
- border: 1px solid var(--glass-edge, var(--hairline));
86
+ border: 1px solid var(--lc-glass-edge, var(--border));
87
87
  border-block-end: 0;
88
- background: var(--paper);
89
- box-shadow: 0 -12px 40px -12px color-mix(in oklab, var(--ink) 45%, transparent);
88
+ background: var(--background);
89
+ box-shadow: 0 -12px 40px -12px color-mix(in oklab, var(--foreground) 45%, transparent);
90
90
  }
91
91
  }
92
92
 
@@ -94,10 +94,10 @@
94
94
  position: fixed;
95
95
  inset: auto 0 0;
96
96
  margin: 0;
97
- z-index: var(--emoji-sheet-z);
97
+ z-index: var(--lc-emoji-sheet-z);
98
98
  display: none;
99
99
  max-inline-size: none;
100
- color: var(--ink);
100
+ color: var(--foreground);
101
101
  flex-direction: column;
102
102
  inline-size: 100%;
103
103
  max-block-size: min(72svh, 34rem);
@@ -107,7 +107,7 @@
107
107
  overscroll-behavior: contain;
108
108
  transform: translateY(100%);
109
109
  transition:
110
- transform 260ms var(--ease-out-expo),
110
+ transform 260ms var(--lc-ease-out-expo),
111
111
  display 260ms allow-discrete,
112
112
  overlay 260ms allow-discrete;
113
113
 
@@ -168,7 +168,7 @@
168
168
  inset-inline-start: 14px;
169
169
  inline-size: 18px;
170
170
  block-size: 18px;
171
- color: var(--ink-3);
171
+ color: var(--muted-foreground);
172
172
  pointer-events: none;
173
173
  }
174
174
 
@@ -178,13 +178,13 @@
178
178
  padding-inline: 40px 1rem;
179
179
  border: 1px solid transparent;
180
180
  border-radius: 999px;
181
- background: var(--paper-2);
182
- color: var(--ink);
181
+ background: var(--muted);
182
+ color: var(--foreground);
183
183
  font: inherit;
184
184
  font-size: 1rem;
185
185
  transition: border-color 150ms ease-out, box-shadow 150ms ease-out;
186
186
 
187
- &::placeholder { color: var(--ink-3); }
187
+ &::placeholder { color: var(--muted-foreground); }
188
188
  &::-webkit-search-cancel-button {
189
189
  /* stylelint-disable-next-line property-no-vendor-prefix -- WebKit search decoration */
190
190
  -webkit-appearance: none;
@@ -193,7 +193,7 @@
193
193
 
194
194
  &:focus {
195
195
  outline: none;
196
- box-shadow: var(--focus-halo, 0 0 0 2px color-mix(in oklab, var(--sky) 50%, transparent));
196
+ box-shadow: var(--focus-halo, 0 0 0 2px color-mix(in oklab, var(--primary) 50%, transparent));
197
197
  }
198
198
  }
199
199
 
@@ -210,15 +210,15 @@
210
210
  block-size: 44px;
211
211
  border: none;
212
212
  border-radius: 999px;
213
- background: var(--paper-2);
213
+ background: var(--muted);
214
214
  cursor: pointer;
215
215
  font-size: var(--text-lg, 1.125rem);
216
216
  line-height: 1;
217
217
  transition: background 150ms ease-out;
218
218
 
219
- &:hover { background: var(--paper-3); }
219
+ &:hover { background: var(--accent); }
220
220
 
221
- &[aria-expanded="true"] { background: var(--sky-soft); }
221
+ &[aria-expanded="true"] { background: color-mix(in oklab, var(--primary) 12%, var(--background)); }
222
222
  }
223
223
 
224
224
  .emoji-picker__tone-menu {
@@ -246,9 +246,9 @@
246
246
  font-size: var(--text-lg, 1.125rem);
247
247
  line-height: 1;
248
248
 
249
- &:hover { background: var(--paper-2); }
249
+ &:hover { background: var(--muted); }
250
250
 
251
- &[aria-current="true"] { background: var(--sky-soft); }
251
+ &[aria-current="true"] { background: color-mix(in oklab, var(--primary) 12%, var(--background)); }
252
252
  }
253
253
 
254
254
  /* ---- Category tab navigation ------------------------------------- */
@@ -264,7 +264,7 @@
264
264
  overflow-x: auto;
265
265
  scrollbar-width: none;
266
266
  border-radius: var(--radius-2xl, 1rem);
267
- background: var(--paper-2);
267
+ background: var(--muted);
268
268
 
269
269
  &::-webkit-scrollbar { display: none; }
270
270
  }
@@ -288,8 +288,8 @@
288
288
 
289
289
  &[aria-current="true"] {
290
290
  opacity: 1;
291
- background: var(--paper);
292
- box-shadow: var(--shadow-sm);
291
+ background: var(--background);
292
+ box-shadow: var(--lc-shadow-sm);
293
293
  }
294
294
  }
295
295
 
@@ -328,19 +328,19 @@
328
328
  margin: 0;
329
329
  font-size: var(--text-sm, 0.875rem);
330
330
  font-weight: var(--font-weight-semibold, 600);
331
- color: var(--ink-2);
331
+ color: var(--muted-foreground);
332
332
  }
333
333
 
334
334
  .count {
335
- font-family: var(--font-body, system-ui);
335
+ font-family: var(--font-sans, system-ui);
336
336
  font-size: var(--text-xs, 0.75rem);
337
- color: var(--ink-3);
337
+ color: var(--muted-foreground);
338
338
  }
339
339
  }
340
340
 
341
341
  .emoji-picker__grid {
342
342
  display: grid;
343
- grid-template-columns: repeat(auto-fill, minmax(var(--emoji-cell), 1fr));
343
+ grid-template-columns: repeat(auto-fill, minmax(var(--lc-emoji-cell), 1fr));
344
344
  gap: 2px;
345
345
  padding-block-start: 4px;
346
346
 
@@ -356,16 +356,16 @@
356
356
  font-size: var(--text-xl, 1.25rem);
357
357
  line-height: 1;
358
358
  padding: 0;
359
- transition: background 0.1s, transform 0.1s var(--ease-out);
359
+ transition: background 0.1s, transform 0.1s var(--lc-ease-out);
360
360
 
361
361
  &:hover,
362
362
  &:focus-visible {
363
- background: var(--paper-2);
363
+ background: var(--muted);
364
364
  transform: scale(1.12);
365
365
  outline: none;
366
366
  }
367
367
 
368
- &[aria-current="true"] { background: var(--sky-soft); }
368
+ &[aria-current="true"] { background: color-mix(in oklab, var(--primary) 12%, var(--background)); }
369
369
  }
370
370
  }
371
371
 
@@ -379,7 +379,7 @@
379
379
  .emoji-picker__empty {
380
380
  margin: 0;
381
381
  padding: 1rem 0.5rem;
382
- color: var(--ink-3);
382
+ color: var(--muted-foreground);
383
383
  font-size: 1rem;
384
384
  text-align: center;
385
385
  }
@@ -411,7 +411,7 @@
411
411
  .emoji-picker__preview-name {
412
412
  overflow: hidden;
413
413
  font-size: 1rem;
414
- color: var(--ink-3);
414
+ color: var(--muted-foreground);
415
415
  text-overflow: ellipsis;
416
416
  white-space: nowrap;
417
417
  }
@@ -433,8 +433,8 @@
433
433
  transform: translateY(-4px) scale(0.98);
434
434
  opacity: 0;
435
435
  transition:
436
- opacity 160ms var(--ease-out-expo),
437
- transform 160ms var(--ease-out-expo),
436
+ opacity 160ms var(--lc-ease-out-expo),
437
+ transform 160ms var(--lc-ease-out-expo),
438
438
  display 160ms allow-discrete,
439
439
  overlay 160ms allow-discrete;
440
440
 
@@ -456,7 +456,7 @@
456
456
 
457
457
  main:has(.emoji-picker__popover[data-open]),
458
458
  .emoji-picker:has(.emoji-picker__popover[data-open]) {
459
- z-index: 60;
459
+ z-index: var(--lc-z-overlay);
460
460
  }
461
461
  }
462
462
  }
@@ -1,6 +1,6 @@
1
1
  @layer components {
2
2
  /* A legend sits outside the fieldset's internal grid. */
3
- :where(fieldset.ui-field > legend) {
4
- margin-block-end: var(--ui-space-related, 0.5rem);
3
+ :where(fieldset.lc-field > legend) {
4
+ margin-block-end: var(--spacing-related);
5
5
  }
6
6
  }
@@ -1,17 +1,18 @@
1
1
  /* Glass — the floating control layer, used the way iOS uses it: only on
2
2
  chrome that floats over content (the nav pills, the phone CTA dock, menus
3
3
  and popovers), never on cards or sections. Menus lift further, on
4
- --shadow-lg. Written once here because Safari
4
+ --lc-shadow-lg. Written once here because Safari
5
5
  drops custom properties inside -webkit-backdrop-filter, so the blur needs
6
6
  literal values, and because the solid fallbacks need @supports and
7
7
  prefers-reduced-transparency. glass_class is the only writer of the name. */
8
8
  @layer components {
9
9
  /* The emoji and currency pickers are glass only as desktop popovers; on
10
10
  phones they're bottom sheets, whose rules come later in the cascade and repaint them solid. */
11
- .glass {
12
- background: var(--glass-fill);
13
- border: 1px solid var(--glass-edge);
14
- box-shadow: var(--glass-shadow);
11
+ .lc-glass {
12
+ --_lc-glass-fill: var(--lc-glass-fill);
13
+ background: var(--_lc-glass-fill);
14
+ border: 1px solid var(--lc-glass-edge);
15
+ box-shadow: var(--lc-glass-shadow);
15
16
  -webkit-backdrop-filter: blur(24px) saturate(160%);
16
17
  backdrop-filter: blur(24px) saturate(160%);
17
18
 
@@ -23,13 +24,13 @@
23
24
  backdrop-filter: blur(12px) saturate(180%);
24
25
  }
25
26
 
26
- /* --glass-solid lets inverted (HUD) glass fall back to its own colour. */
27
+ /* --lc-glass-solid lets inverted (HUD) glass fall back to its own colour. */
27
28
  @supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
28
- background: var(--glass-solid, var(--paper));
29
+ --_lc-glass-fill: var(--lc-glass-solid, var(--background));
29
30
  }
30
31
 
31
32
  @media (prefers-reduced-transparency: reduce) {
32
- background: var(--glass-solid, var(--paper));
33
+ --_lc-glass-fill: var(--lc-glass-solid, var(--background));
33
34
  -webkit-backdrop-filter: none;
34
35
  backdrop-filter: none;
35
36
  }
@@ -1,5 +1,5 @@
1
1
  @layer components {
2
- .ui-container-page {
3
- padding-inline: var(--ui-gutter, clamp(1rem, 3vw, 1.5rem));
2
+ .lc-container-page {
3
+ padding-inline: var(--spacing-gutter);
4
4
  }
5
5
  }
@@ -1,12 +1,12 @@
1
1
  /* Closed is the exit pose, so closing animates too; allow-discrete holds
2
2
  display/overlay until it ends. FloatingPanel sets [data-open]. */
3
- @utility popover-shell {
3
+ @utility lc-popover-shell {
4
4
  opacity: 0;
5
5
  transform: translateY(--spacing(-1)) scale(0.98);
6
6
  pointer-events: none;
7
7
  transition:
8
- opacity 160ms var(--ease-out-expo),
9
- transform 160ms var(--ease-out-expo),
8
+ opacity 160ms var(--lc-ease-out-expo),
9
+ transform 160ms var(--lc-ease-out-expo),
10
10
  display 160ms allow-discrete,
11
11
  overlay 160ms allow-discrete;
12
12
 
@@ -23,10 +23,10 @@
23
23
  }
24
24
 
25
25
  @layer components {
26
- /* Desktop: the .sheet becomes an anchored popover instead of a bottom sheet. */
26
+ /* Desktop: the .lc-sheet becomes an anchored popover instead of a bottom sheet. */
27
27
  @media (width >= 40rem) {
28
- .sheet--anchored {
29
- @apply popover-shell;
28
+ .lc-sheet--anchored {
29
+ @apply lc-popover-shell;
30
30
 
31
31
  display: none;
32
32
  flex-direction: column;
@@ -48,27 +48,27 @@
48
48
  /* Enter/exit for small overlays, whichever way they open: a [hidden] toggle
49
49
  (amount calculator, colour picker), the Popover API (phone nav menus) or a
50
50
  modal <dialog>. The closed state is the exit pose, so close animates too;
51
- allow-discrete holds display/overlay until it ends. --pop-y sets the
51
+ allow-discrete holds display/overlay until it ends. --lc-pop-y sets the
52
52
  direction: popovers drop from their trigger, dialogs rise. */
53
- .popover-pop {
54
- --pop-from: translateY(var(--pop-y, -0.25rem)) scale(0.96);
53
+ .lc-popover-pop {
54
+ --lc-pop-from: translateY(var(--lc-pop-y, -0.25rem)) scale(0.96);
55
55
 
56
- transform-origin: var(--pop-origin, top);
56
+ transform-origin: var(--lc-pop-origin, top);
57
57
  transition:
58
- opacity 160ms var(--ease-out-expo),
59
- transform 240ms var(--ease-out-expo),
58
+ opacity 160ms var(--lc-ease-out-expo),
59
+ transform 240ms var(--lc-ease-out-expo),
60
60
  display 240ms allow-discrete,
61
61
  overlay 240ms allow-discrete;
62
62
 
63
63
  &:is([hidden], [popover]:not(:popover-open), dialog:not([open])) {
64
64
  opacity: 0;
65
- transform: var(--pop-from);
65
+ transform: var(--lc-pop-from);
66
66
  }
67
67
 
68
68
  &:is(:not([hidden], [popover], dialog), :popover-open, dialog[open]) {
69
69
  @starting-style {
70
70
  opacity: 0;
71
- transform: var(--pop-from);
71
+ transform: var(--lc-pop-from);
72
72
  }
73
73
  }
74
74
  }