phlex_kit 0.2.1 → 0.2.2

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 (100) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +81 -6
  3. data/THIRD_PARTY_LICENSES +104 -0
  4. data/app/assets/stylesheets/phlex_kit/_tokens.css +72 -0
  5. data/app/assets/stylesheets/phlex_kit/phlex_kit.css +1 -1
  6. data/app/assets/stylesheets/phlex_kit/themes/claude.css +86 -0
  7. data/app/assets/stylesheets/phlex_kit/themes/neutral.css +95 -0
  8. data/app/assets/stylesheets/phlex_kit/themes/zinc.css +85 -0
  9. data/app/components/phlex_kit/accordion/accordion.css +13 -0
  10. data/app/components/phlex_kit/accordion/accordion.rb +11 -2
  11. data/app/components/phlex_kit/accordion/accordion_icon.rb +1 -3
  12. data/app/components/phlex_kit/accordion/accordion_item.rb +5 -2
  13. data/app/components/phlex_kit/alert_dialog/alert_dialog.css +45 -4
  14. data/app/components/phlex_kit/alert_dialog/alert_dialog_action.rb +3 -2
  15. data/app/components/phlex_kit/alert_dialog/alert_dialog_content.rb +6 -2
  16. data/app/components/phlex_kit/alert_dialog/alert_dialog_media.rb +14 -0
  17. data/app/components/phlex_kit/attachment/attachment.css +102 -2
  18. data/app/components/phlex_kit/attachment/attachment.rb +18 -6
  19. data/app/components/phlex_kit/attachment/attachment_action.rb +1 -6
  20. data/app/components/phlex_kit/attachment/attachment_group.rb +21 -0
  21. data/app/components/phlex_kit/attachment/attachment_media.rb +13 -4
  22. data/app/components/phlex_kit/attachment/attachment_trigger.rb +22 -0
  23. data/app/components/phlex_kit/breadcrumb/breadcrumb_ellipsis.rb +1 -5
  24. data/app/components/phlex_kit/breadcrumb/breadcrumb_separator.rb +1 -1
  25. data/app/components/phlex_kit/calendar/calendar_next.rb +1 -15
  26. data/app/components/phlex_kit/calendar/calendar_prev.rb +1 -15
  27. data/app/components/phlex_kit/card/card.css +50 -14
  28. data/app/components/phlex_kit/card/card.rb +6 -2
  29. data/app/components/phlex_kit/card/card_action.rb +14 -0
  30. data/app/components/phlex_kit/carousel/carousel_next.rb +1 -15
  31. data/app/components/phlex_kit/carousel/carousel_previous.rb +1 -15
  32. data/app/components/phlex_kit/codeblock/codeblock.css +1 -1
  33. data/app/components/phlex_kit/combobox/combobox_clear_button.rb +1 -15
  34. data/app/components/phlex_kit/combobox/combobox_input_trigger.rb +1 -12
  35. data/app/components/phlex_kit/combobox/combobox_item_indicator.rb +1 -15
  36. data/app/components/phlex_kit/combobox/combobox_search_input.rb +1 -14
  37. data/app/components/phlex_kit/combobox/combobox_trigger.rb +1 -14
  38. data/app/components/phlex_kit/command/command.css +2 -2
  39. data/app/components/phlex_kit/command/command_input.rb +1 -13
  40. data/app/components/phlex_kit/context_menu/context_menu_item.rb +1 -3
  41. data/app/components/phlex_kit/data_table/data_table_column_toggle.rb +1 -13
  42. data/app/components/phlex_kit/data_table/data_table_expand_toggle.rb +1 -12
  43. data/app/components/phlex_kit/data_table/data_table_sort_head.rb +2 -28
  44. data/app/components/phlex_kit/dialog/dialog.css +8 -2
  45. data/app/components/phlex_kit/dialog/dialog_content.rb +1 -3
  46. data/app/components/phlex_kit/drawer/drawer.css +1 -0
  47. data/app/components/phlex_kit/dropdown_menu/dropdown_menu.css +64 -0
  48. data/app/components/phlex_kit/dropdown_menu/dropdown_menu_checkbox_item.rb +31 -0
  49. data/app/components/phlex_kit/dropdown_menu/dropdown_menu_group.rb +11 -0
  50. data/app/components/phlex_kit/dropdown_menu/dropdown_menu_item.rb +5 -2
  51. data/app/components/phlex_kit/dropdown_menu/dropdown_menu_radio_group.rb +10 -0
  52. data/app/components/phlex_kit/dropdown_menu/dropdown_menu_radio_item.rb +31 -0
  53. data/app/components/phlex_kit/dropdown_menu/dropdown_menu_shortcut.rb +10 -0
  54. data/app/components/phlex_kit/dropdown_menu/dropdown_menu_sub.rb +11 -0
  55. data/app/components/phlex_kit/dropdown_menu/dropdown_menu_sub_content.rb +12 -0
  56. data/app/components/phlex_kit/dropdown_menu/dropdown_menu_sub_trigger.rb +18 -0
  57. data/app/components/phlex_kit/icon/icon.rb +51 -0
  58. data/app/components/phlex_kit/marker/marker.css +25 -0
  59. data/app/components/phlex_kit/marker/marker.rb +18 -0
  60. data/app/components/phlex_kit/marker/marker_content.rb +7 -0
  61. data/app/components/phlex_kit/marker/marker_icon.rb +7 -0
  62. data/app/components/phlex_kit/native_select/native_select_icon.rb +1 -10
  63. data/app/components/phlex_kit/navigation_menu/navigation_menu_trigger.rb +1 -5
  64. data/app/components/phlex_kit/pagination/pagination_ellipsis.rb +1 -5
  65. data/app/components/phlex_kit/select/select_item.rb +1 -11
  66. data/app/components/phlex_kit/select/select_trigger.rb +1 -14
  67. data/app/components/phlex_kit/sheet/sheet.css +1 -1
  68. data/app/components/phlex_kit/sheet/sheet_content.rb +1 -3
  69. data/app/components/phlex_kit/sidebar/sidebar.css +129 -11
  70. data/app/components/phlex_kit/sidebar/sidebar.rb +19 -6
  71. data/app/components/phlex_kit/sidebar/sidebar_group_action.rb +14 -0
  72. data/app/components/phlex_kit/sidebar/sidebar_group_content.rb +13 -0
  73. data/app/components/phlex_kit/sidebar/sidebar_group_label.rb +13 -0
  74. data/app/components/phlex_kit/sidebar/sidebar_input.rb +14 -0
  75. data/app/components/phlex_kit/sidebar/sidebar_menu_action.rb +13 -0
  76. data/app/components/phlex_kit/sidebar/sidebar_menu_badge.rb +13 -0
  77. data/app/components/phlex_kit/sidebar/sidebar_menu_skeleton.rb +19 -0
  78. data/app/components/phlex_kit/sidebar/sidebar_menu_sub.rb +13 -0
  79. data/app/components/phlex_kit/sidebar/sidebar_menu_sub_button.rb +18 -0
  80. data/app/components/phlex_kit/sidebar/sidebar_menu_sub_item.rb +13 -0
  81. data/app/components/phlex_kit/sidebar/sidebar_separator.rb +14 -0
  82. data/app/components/phlex_kit/spinner/spinner.rb +5 -10
  83. data/app/components/phlex_kit/toast/toast_close.rb +1 -15
  84. data/app/components/phlex_kit/toast/toast_icon.rb +12 -36
  85. data/app/components/phlex_kit/typography/heading.rb +1 -1
  86. data/app/components/phlex_kit/typography/typography.css +7 -3
  87. data/app/javascript/phlex_kit/controllers/accordion_controller.js +17 -1
  88. data/app/javascript/phlex_kit/controllers/index.js +2 -0
  89. data/app/javascript/phlex_kit/controllers/scroll_fade_controller.js +31 -0
  90. data/lib/phlex_kit/configuration.rb +7 -0
  91. data/lib/phlex_kit/icons/lucide.rb +88 -0
  92. data/lib/phlex_kit/icons/phosphor.rb +88 -0
  93. data/lib/phlex_kit/icons/remix.rb +88 -0
  94. data/lib/phlex_kit/icons/tabler.rb +88 -0
  95. data/lib/phlex_kit/icons.rb +40 -0
  96. data/lib/phlex_kit/version.rb +1 -1
  97. data/lib/phlex_kit.rb +1 -0
  98. metadata +45 -8
  99. data/app/components/phlex_kit/stars/stars.css +0 -4
  100. data/app/components/phlex_kit/stars/stars.rb +0 -19
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 14f1ab06594f344904b8fbeecd23bca4c08005b35faf16325aca36edf6079c2b
4
- data.tar.gz: 9cbdd5092ce76896536a59de10dc9e23734ee68cce9f85acf5e88b11ebf3d092
3
+ metadata.gz: 0f6988a4f8e3f47462f9f83824ed6542cafded9d656adb10340e7eb618156063
4
+ data.tar.gz: ca293b01074cfa84962df3c80683e3df5ae36cf5a919b1e8c0bcd1861948b52c
5
5
  SHA512:
6
- metadata.gz: 725e6b9a212702970cfedac729769373d378015409db9c56aee565b03c6d30dca6283567df66547e56b3d0271083e324ec73a97d2daa7baf74d93b9c2101119f
7
- data.tar.gz: 4a3edb915f2082b66b18d96f89cc990aebedf6241a8a2e28eacd60329e9b563a080fa15789fee0b8b7e040a274baecd2c28cc000b38b15721dbbaf0b8a2b8a62
6
+ metadata.gz: fd26ed3b6aef6bf015c7fbdf574516a68e8eb10fe1cfe1356a4292420d01917977428d2b35a301b65e6a8ff19c73daf5ac9ffdfb9af6abae6f96afd4818d7c81
7
+ data.tar.gz: 6d6c3d42730c2ce500a6f8c8e49afab8e11ec192f87bf9d87434761d334b40e1a762cc157ad7b38b2914edbffeae9fea501235f1cdc0ec6937c88a8f843399fa
data/README.md CHANGED
@@ -120,9 +120,51 @@ The full token surface: `--pk-bg`, `--pk-surface`, `--pk-surface-2`,
120
120
  `--pk-accent` (hover fills), `--pk-border`, `--pk-input` (control borders),
121
121
  `--pk-ring` (focus rings), `--pk-text`, `--pk-text-2`, `--pk-muted`,
122
122
  `--pk-brand`, `--pk-brand-ink`, `--pk-green`, `--pk-amber`, `--pk-red`,
123
- `--pk-chart-1..5`, `--pk-radius`. Want a fully custom palette? Drop the
123
+ `--pk-chart-1..5`, `--pk-radius`, plus `--pk-font-sans` / `--pk-font-mono`
124
+ (system stacks by default — override them after an @font-face to adopt Geist
125
+ like shadcn, as the docs site does). Want a fully custom palette? Drop the
124
126
  `_tokens.css` import and define them all yourself.
125
127
 
128
+ Prefer a ready-made palette? The gem ships theme files under
129
+ `app/assets/stylesheets/phlex_kit/themes/` (`neutral`, `zinc`, `claude`) —
130
+ link one after the manifest and the cascade re-themes everything:
131
+
132
+ ```ruby
133
+ stylesheet_link_tag "phlex_kit/phlex_kit"
134
+ stylesheet_link_tag "phlex_kit/themes/neutral"
135
+ ```
136
+
137
+ The three knobs from ui.shadcn.com/create map directly: **Radius** →
138
+ `--pk-radius`; **Icon Library** → `PhlexKit.config.icon_library` (see Icons
139
+ below); **Menu** → `PhlexKit::Sidebar.new(menu: :default | :solid)`.
140
+
141
+ ## Icons
142
+
143
+ Every built-in glyph renders through `PhlexKit::Icon`, which draws from a
144
+ vendored icon library — pick one kit-wide in an initializer:
145
+
146
+ ```ruby
147
+ PhlexKit.configure do |c|
148
+ c.icon_library = :tabler # :lucide (default), :tabler, :phosphor, :remix
149
+ end
150
+ ```
151
+
152
+ Use it directly with canonical semantic names (the per-library vocabulary is
153
+ pre-resolved — `:chevron_down` is Phosphor's `caret-down`, Remix's
154
+ `arrow-down-s-line`, and so on):
155
+
156
+ ```ruby
157
+ render PhlexKit::Icon.new(:calendar) # 16px, configured library
158
+ render PhlexKit::Icon.new(:search, size: 24)
159
+ render PhlexKit::Icon.new(:check, library: :phosphor) # per-instance override
160
+ ```
161
+
162
+ `PhlexKit::Icons.names` lists the full catalog (~70 glyphs, all guaranteed to
163
+ resolve in all four libraries); unknown names fail loud. HugeIcons is not
164
+ included — its free set forbids redistributing the artwork inside a gem. Icon
165
+ path data licenses ship in `THIRD_PARTY_LICENSES` (Lucide ISC, Tabler MIT,
166
+ Phosphor MIT, Remix Apache-2.0).
167
+
126
168
  ## Charts
127
169
 
128
170
  `PhlexKit::Chart` is deliberately a thin wrapper — **no charting library is
@@ -168,10 +210,12 @@ bin/rails g phlex_kit:component button
168
210
  This copies `button.rb` + `button.css` into `app/components/phlex_kit/button/`
169
211
  and wires its `@import`. Your copy shadows the gem's.
170
212
 
171
- ## The gallery
213
+ ## The docs site
172
214
 
173
- A dummy Rails app renders every component on one page useful as living
174
- documentation and for eyeballing theme changes:
215
+ The dummy Rails app is a full shadcn-style docs site: a sidebar menu with one
216
+ page per component, every use case demoed with a Preview/Code toggle (the code
217
+ shown is extracted from the running example source, so it can never drift).
218
+ A one-page kitchen-sink gallery lives at /gallery:
175
219
 
176
220
  ```bash
177
221
  git clone https://github.com/MatthewKennedy/phlex_kit && cd phlex_kit
@@ -180,6 +224,26 @@ bundle exec puma -p 3999 test/dummy/config.ru
180
224
  # open http://127.0.0.1:3999
181
225
  ```
182
226
 
227
+ By default the docs site renders the shadcn-parity baseline (default tokens,
228
+ lucide icons). Two env vars re-skin the whole site, handy for eyeballing a
229
+ theme or icon set across every component:
230
+
231
+ ```bash
232
+ # Boot with a bundled theme (neutral, zinc, or claude)
233
+ PK_THEME=claude bundle exec puma -p 3999 test/dummy/config.ru
234
+
235
+ # Boot with a different icon library (lucide is the default; tabler, phosphor, remix)
236
+ PK_ICONS=tabler bundle exec puma -p 3999 test/dummy/config.ru
237
+
238
+ # Or both
239
+ PK_THEME=zinc PK_ICONS=phosphor bundle exec puma -p 3999 test/dummy/config.ru
240
+ ```
241
+
242
+ `PK_THEME` links the matching file from
243
+ `app/assets/stylesheets/phlex_kit/themes/` after the manifest — the same
244
+ mechanism a host app uses. Light/dark/system switching is in the site's
245
+ header regardless of theme.
246
+
183
247
  ## Components
184
248
 
185
249
  Everything in ruby_ui (53/53) plus shadcn/ui's own catalog: accordion, alert,
@@ -190,14 +254,25 @@ triggers), command palette, context menu, data table, date picker, dialog,
190
254
  drawer, dropdown menu, empty, form + form field, hover card, input, input
191
255
  group, input OTP, item, kbd, label, link, masked input, menubar, message,
192
256
  message scroller, native select, navigation menu, pagination, popover,
193
- progress, radio button + radio group, resizable, scroll area, select,
194
- separator, sheet, shortcut key, sidebar, skeleton, slider, spinner, stars,
257
+ marker, progress, radio button + radio group, resizable, scroll area, select,
258
+ separator, sheet, shortcut key, sidebar, skeleton, slider, spinner,
195
259
  switch, table, tabs, textarea, theme toggle, toast (sonner-style), toggle,
196
260
  toggle group, tooltip, typography.
197
261
 
262
+ Plus two utility classes: `.pk-shimmer` (gradient sweep for loading text) and
263
+ `.pk-scroll-fade` (edge-masked scroll containers, position-aware via the
264
+ `phlex-kit--scroll-fade` controller).
265
+
198
266
  Inventory and porting notes: [ROADMAP.md](ROADMAP.md). Architecture and
199
267
  conventions: [docs/](docs/).
200
268
 
269
+ ## RTL
270
+
271
+ shadcn's `Direction` is a React context provider; PhlexKit needs none — set
272
+ `dir="rtl"` on `<html>` and browser layout handles the flex/grid flows. Note
273
+ the caveat: popover-style panels anchor with physical `left`/`top` offsets, so
274
+ fully mirrored overlay alignment in RTL may need small host-side overrides.
275
+
201
276
  ## How the asset wiring works
202
277
 
203
278
  Four engine initializers make the zero-build story hold:
@@ -0,0 +1,104 @@
1
+ Third-party licenses for icon path data vendored under lib/phlex_kit/icons/
2
+ (generated by scripts/generate_icons.rb; the gem's own code is MIT — see
3
+ MIT-LICENSE). HugeIcons is deliberately not included: its free set's usage
4
+ terms forbid editing and redistribution of the artwork.
5
+
6
+ --------------------------------------------------------------------------------
7
+ Lucide (lib/phlex_kit/icons/lucide.rb)
8
+ Source: https://github.com/lucide-icons/lucide
9
+ License: ISC
10
+
11
+ ISC License
12
+
13
+ Copyright (c) for portions of Lucide are held by Cole Bemis 2013-2022 as part
14
+ of Feather (MIT). All other copyright (c) for Lucide are held by Lucide
15
+ Contributors 2022.
16
+
17
+ Permission to use, copy, modify, and/or distribute this software for any
18
+ purpose with or without fee is hereby granted, provided that the above
19
+ copyright notice and this permission notice appear in all copies.
20
+
21
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
22
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
23
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
24
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
25
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
26
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
27
+ PERFORMANCE OF THIS SOFTWARE.
28
+
29
+ --------------------------------------------------------------------------------
30
+ Tabler Icons (lib/phlex_kit/icons/tabler.rb)
31
+ Source: https://github.com/tabler/tabler-icons
32
+ License: MIT
33
+
34
+ MIT License
35
+
36
+ Copyright (c) 2020-2024 Paweł Kuna
37
+
38
+ Permission is hereby granted, free of charge, to any person obtaining a copy
39
+ of this software and associated documentation files (the "Software"), to deal
40
+ in the Software without restriction, including without limitation the rights
41
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
42
+ copies of the Software, and to permit persons to whom the Software is
43
+ furnished to do so, subject to the following conditions:
44
+
45
+ The above copyright notice and this permission notice shall be included in all
46
+ copies or substantial portions of the Software.
47
+
48
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
49
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
50
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
51
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
52
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
53
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
54
+ SOFTWARE.
55
+
56
+ --------------------------------------------------------------------------------
57
+ Phosphor Icons (lib/phlex_kit/icons/phosphor.rb)
58
+ Source: https://github.com/phosphor-icons/core
59
+ License: MIT
60
+
61
+ MIT License
62
+
63
+ Copyright (c) 2023 Phosphor Icons
64
+
65
+ Permission is hereby granted, free of charge, to any person obtaining a copy
66
+ of this software and associated documentation files (the "Software"), to deal
67
+ in the Software without restriction, including without limitation the rights
68
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
69
+ copies of the Software, and to permit persons to whom the Software is
70
+ furnished to do so, subject to the following conditions:
71
+
72
+ The above copyright notice and this permission notice shall be included in all
73
+ copies or substantial portions of the Software.
74
+
75
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
76
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
77
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
78
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
79
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
80
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
81
+ SOFTWARE.
82
+
83
+ --------------------------------------------------------------------------------
84
+ Remix Icon (lib/phlex_kit/icons/remix.rb)
85
+ Source: https://github.com/Remix-Design/RemixIcon
86
+ License: Apache License 2.0
87
+
88
+ Copyright RemixIcon contributors.
89
+
90
+ Licensed under the Apache License, Version 2.0 (the "License"); you may not
91
+ use the vendored icon data except in compliance with the License. You may
92
+ obtain a copy of the License at
93
+
94
+ http://www.apache.org/licenses/LICENSE-2.0
95
+
96
+ Unless required by applicable law or agreed to in writing, software
97
+ distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
98
+ WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
99
+ License for the specific language governing permissions and limitations under
100
+ the License.
101
+
102
+ NOTICE (statement of changes, per Apache-2.0 §4): the vendored data is
103
+ extracted SVG path geometry only, converted to Ruby literals and keyed by
104
+ PhlexKit's canonical glyph names; visual geometry is unmodified.
@@ -1,6 +1,69 @@
1
1
  /* Kit utilities used by the interactive controllers (Stimulus toggles these). */
2
2
  .pk-hidden { display: none !important; }
3
3
 
4
+ /* Shimmer — shadcn's shimmer util: a highlight band sweeping across text, for
5
+ loading / "thinking…" states. Pure CSS, built on currentColor: the text keeps
6
+ its own color (any color) and the highlight derives from it via relative
7
+ color syntax, so it brightens automatically in dark mode. Tune per element
8
+ with inline custom properties:
9
+ --pk-shimmer-color highlight (default: brightened currentColor)
10
+ --pk-shimmer-duration one sweep (default 2s)
11
+ --pk-shimmer-spread band width (default 3ch + 40px)
12
+ --pk-shimmer-angle band tilt (default 20deg)
13
+ Modifiers: .once (single sweep), .reverse. Honors prefers-reduced-motion and
14
+ falls back to plain text in browsers without relative color support. */
15
+ .pk-shimmer {
16
+ --pk-shimmer-color: oklch(from currentColor calc(l + .25) c h);
17
+ --pk-shimmer-duration: 2s;
18
+ --pk-shimmer-spread: calc(3ch + 40px);
19
+ --pk-shimmer-angle: 20deg;
20
+ background-image:
21
+ linear-gradient(calc(90deg + var(--pk-shimmer-angle)),
22
+ transparent calc(50% - var(--pk-shimmer-spread) / 2),
23
+ var(--pk-shimmer-color) 50%,
24
+ transparent calc(50% + var(--pk-shimmer-spread) / 2)),
25
+ linear-gradient(currentColor, currentColor);
26
+ background-size: 250% 100%, auto;
27
+ background-repeat: no-repeat;
28
+ -webkit-background-clip: text;
29
+ background-clip: text;
30
+ -webkit-text-fill-color: transparent;
31
+ animation: pk-shimmer var(--pk-shimmer-duration) linear infinite;
32
+ }
33
+ .pk-shimmer.once { animation-iteration-count: 1; animation-fill-mode: forwards; }
34
+ .pk-shimmer.reverse { animation-direction: reverse; }
35
+ [dir="rtl"] .pk-shimmer { animation-direction: reverse; }
36
+ [dir="rtl"] .pk-shimmer.reverse { animation-direction: normal; }
37
+ @keyframes pk-shimmer {
38
+ from { background-position: 250% 0, 0 0; }
39
+ to { background-position: -150% 0, 0 0; }
40
+ }
41
+ @media (prefers-reduced-motion: reduce) {
42
+ .pk-shimmer { animation: none; background-image: none; -webkit-text-fill-color: currentColor; }
43
+ }
44
+ @supports not (color: oklch(from white l c h)) {
45
+ .pk-shimmer { animation: none; background-image: none; -webkit-text-fill-color: currentColor; }
46
+ }
47
+
48
+ /* Scroll fade — shadcn's scroll-fade util: masks the edges of a scrollable
49
+ container while there is more content in that direction. Pair with the
50
+ phlex-kit--scroll-fade controller, which toggles data-at-start/data-at-end. */
51
+ .pk-scroll-fade {
52
+ --pk-scroll-fade-size: 2rem;
53
+ overflow-y: auto;
54
+ mask-image: linear-gradient(to bottom,
55
+ transparent 0, black var(--pk-scroll-fade-size),
56
+ black calc(100% - var(--pk-scroll-fade-size)), transparent 100%);
57
+ }
58
+ .pk-scroll-fade[data-at-start] {
59
+ mask-image: linear-gradient(to bottom,
60
+ black calc(100% - var(--pk-scroll-fade-size)), transparent 100%);
61
+ }
62
+ .pk-scroll-fade[data-at-end] {
63
+ mask-image: linear-gradient(to bottom, transparent 0, black var(--pk-scroll-fade-size));
64
+ }
65
+ .pk-scroll-fade[data-at-start][data-at-end] { mask-image: none; }
66
+
4
67
  /* Kit elements size predictably: width includes padding + border, so
5
68
  `width: 100%` controls (inputs, triggers, option rows) never overflow their
6
69
  container. Scoped to pk-* classes and their subtrees — the host's own box
@@ -22,6 +85,13 @@
22
85
  var(--pk-*), so redefining :root after the import re-themes the whole kit
23
86
  with no rebuild.
24
87
 
88
+ Optional sidebar overrides (shadcn sidebar-* parity) — deliberately NOT
89
+ defined here: --pk-sidebar / -text / -accent / -border / -primary /
90
+ -primary-ink. sidebar.css consumes them via token→token fallbacks
91
+ (e.g. var(--pk-sidebar-primary, var(--pk-brand))), so unset they fall back
92
+ to surface/text/accent/border/brand/brand-ink; a theme sets one only when
93
+ its value differs from the shared token.
94
+
25
95
  Dark is the default; light is opt-in via <html data-theme="light"> (set it
26
96
  server-side to avoid a flash). "system" follows the OS via prefers-color-scheme.
27
97
  Keep the two light token sets in sync. */
@@ -47,6 +117,8 @@
47
117
  --pk-chart-4: #1d4ed8;
48
118
  --pk-chart-5: #1e40af;
49
119
  --pk-radius: 0.625rem;
120
+ --pk-font-sans: ui-sans-serif, system-ui, -apple-system, sans-serif;
121
+ --pk-font-mono: ui-monospace, SFMono-Regular, Menlo, monospace;
50
122
  color-scheme: dark;
51
123
  }
52
124
 
@@ -50,6 +50,7 @@
50
50
  @import url("kbd/kbd.css");
51
51
  @import url("label/label.css");
52
52
  @import url("link/link.css");
53
+ @import url("marker/marker.css");
53
54
  @import url("menubar/menubar.css");
54
55
  @import url("message/message.css");
55
56
  @import url("message_scroller/message_scroller.css");
@@ -70,7 +71,6 @@
70
71
  @import url("skeleton/skeleton.css");
71
72
  @import url("slider/slider.css");
72
73
  @import url("spinner/spinner.css");
73
- @import url("stars/stars.css");
74
74
  @import url("switch/switch.css");
75
75
  @import url("table/table.css");
76
76
  @import url("tabs/tabs.css");
@@ -0,0 +1,86 @@
1
+ /* PhlexKit theme: Claude — warm ivory/charcoal neutrals + terracotta brand,
2
+ after the claude.ai palette. Pure token override: load AFTER the
3
+ phlex_kit.css manifest (stylesheet_link_tag "phlex_kit/themes/claude")
4
+ and the cascade re-themes every component; no component CSS is touched.
5
+
6
+ Same contract as _tokens.css: dark is the default, light is opt-in via
7
+ <html data-theme="light">, "system" follows the OS. Keep the two light
8
+ blocks in sync.
9
+
10
+ Anthropic's brand faces (Styrene, Tiempos/Copernicus) are licensed and
11
+ not shipped; --pk-font-serif falls back through Georgia. A host that has
12
+ the real fonts re-points the token. */
13
+ :root {
14
+ --pk-bg: #262624;
15
+ --pk-surface: #30302e;
16
+ --pk-surface-2: #3a3a37;
17
+ --pk-accent: #45453f;
18
+ --pk-border: #faf9f51f;
19
+ --pk-input: #faf9f52e;
20
+ --pk-ring: #d97757;
21
+ --pk-text: #f5f4ee;
22
+ --pk-text-2: #d9d8d0;
23
+ --pk-muted: #a3a094;
24
+ --pk-brand: #d97757;
25
+ --pk-brand-ink: #ffffff;
26
+ --pk-green: #22c55e;
27
+ --pk-amber: #f59e0b;
28
+ --pk-red: #ff6568;
29
+ --pk-chart-1: #ecb4a0;
30
+ --pk-chart-2: #e19477;
31
+ --pk-chart-3: #d97757;
32
+ --pk-chart-4: #b85c3e;
33
+ --pk-chart-5: #8f4630;
34
+ --pk-radius: 0.75rem;
35
+ --pk-font-serif: "Copernicus", "Tiempos Text", Georgia, "Times New Roman", serif;
36
+ color-scheme: dark;
37
+ }
38
+
39
+ :root[data-theme="light"] {
40
+ --pk-bg: #faf9f5;
41
+ --pk-surface: #ffffff;
42
+ --pk-surface-2: #f0eee6;
43
+ --pk-accent: #f0eee6;
44
+ --pk-border: #e5e2d9;
45
+ --pk-input: #d9d6ca;
46
+ --pk-ring: #d97757;
47
+ --pk-text: #1f1e1d;
48
+ --pk-text-2: #4a4844;
49
+ --pk-muted: #83817b;
50
+ --pk-brand: #d97757;
51
+ --pk-brand-ink: #ffffff;
52
+ --pk-green: #16a34a;
53
+ --pk-amber: #d97706;
54
+ --pk-red: #d13415;
55
+ color-scheme: light;
56
+ }
57
+
58
+ @media (prefers-color-scheme: light) {
59
+ :root[data-theme="system"] {
60
+ --pk-bg: #faf9f5;
61
+ --pk-surface: #ffffff;
62
+ --pk-surface-2: #f0eee6;
63
+ --pk-accent: #f0eee6;
64
+ --pk-border: #e5e2d9;
65
+ --pk-input: #d9d6ca;
66
+ --pk-ring: #d97757;
67
+ --pk-text: #1f1e1d;
68
+ --pk-text-2: #4a4844;
69
+ --pk-muted: #83817b;
70
+ --pk-brand: #d97757;
71
+ --pk-brand-ink: #ffffff;
72
+ --pk-green: #16a34a;
73
+ --pk-amber: #d97706;
74
+ --pk-red: #d13415;
75
+ color-scheme: light;
76
+ }
77
+ }
78
+
79
+ /* The one non-token rule: Claude's signature serif display over a sans body.
80
+ Serifs carry less inherent weight than the sans they replace, so headings
81
+ ease off the tracking the sans sizes wanted. */
82
+ .pk-heading {
83
+ font-family: var(--pk-font-serif);
84
+ font-weight: 500;
85
+ letter-spacing: 0;
86
+ }
@@ -0,0 +1,95 @@
1
+ /* PhlexKit theme: Neutral — translated from a ui.shadcn.com/create export
2
+ (shadcn's stock neutral palette: pure grayscale, zero chroma throughout).
3
+ Pure token override: load AFTER the phlex_kit.css manifest and the
4
+ cascade re-themes every component.
5
+
6
+ Translation notes (the recipe for any shadcn/create export):
7
+ - background → bg; card/popover → surface; secondary/muted/accent →
8
+ surface-2/accent; foreground → text; secondary-foreground → text-2;
9
+ muted-foreground → muted; primary/primary-foreground → brand/brand-ink;
10
+ destructive → red; border/input/ring/chart-1..5/radius map 1:1.
11
+ - shadcn is light-default with a .dark class; PhlexKit themes are
12
+ dark-default with data-theme="light" opt-in — blocks swapped.
13
+ - sidebar-* maps onto the kit's optional --pk-sidebar-* overrides, set
14
+ only where the export differs from the shared tokens: here that's the
15
+ dark sidebar-primary (a distinct blue) + its ink. Like the shadcn create
16
+ page, the blue only shows on active items under Sidebar's menu: :solid —
17
+ the :default menu highlights with the accent role. shadcn/create exports
18
+ no green/amber — those fall through to _tokens.css defaults.
19
+ - dark text-2 has no shadcn source (every dark *-foreground is 0.985);
20
+ chart-1's 0.87 step (neutral-300) is used, matching the kit's own dark
21
+ text-2 weight. */
22
+ :root {
23
+ --pk-bg: oklch(0.145 0 0);
24
+ --pk-surface: oklch(0.205 0 0);
25
+ --pk-surface-2: oklch(0.269 0 0);
26
+ --pk-accent: oklch(0.269 0 0);
27
+ --pk-border: oklch(1 0 0 / 10%);
28
+ --pk-input: oklch(1 0 0 / 15%);
29
+ --pk-ring: oklch(0.556 0 0);
30
+ --pk-text: oklch(0.985 0 0);
31
+ --pk-text-2: oklch(0.87 0 0);
32
+ --pk-muted: oklch(0.708 0 0);
33
+ --pk-brand: oklch(0.922 0 0);
34
+ --pk-brand-ink: oklch(0.205 0 0);
35
+ --pk-red: oklch(0.704 0.191 22.216);
36
+ --pk-chart-1: oklch(0.87 0 0);
37
+ --pk-chart-2: oklch(0.556 0 0);
38
+ --pk-chart-3: oklch(0.439 0 0);
39
+ --pk-chart-4: oklch(0.371 0 0);
40
+ --pk-chart-5: oklch(0.269 0 0);
41
+ --pk-sidebar-primary: oklch(0.488 0.243 264.376);
42
+ --pk-sidebar-primary-ink: oklch(0.985 0 0);
43
+ --pk-radius: 0.625rem;
44
+ color-scheme: dark;
45
+ }
46
+
47
+ :root[data-theme="light"] {
48
+ --pk-bg: oklch(1 0 0);
49
+ --pk-sidebar-primary: oklch(0.205 0 0);
50
+ --pk-sidebar-primary-ink: oklch(0.985 0 0);
51
+ --pk-surface: oklch(1 0 0);
52
+ --pk-surface-2: oklch(0.97 0 0);
53
+ --pk-accent: oklch(0.97 0 0);
54
+ --pk-border: oklch(0.922 0 0);
55
+ --pk-input: oklch(0.922 0 0);
56
+ --pk-ring: oklch(0.708 0 0);
57
+ --pk-text: oklch(0.145 0 0);
58
+ --pk-text-2: oklch(0.205 0 0);
59
+ --pk-muted: oklch(0.556 0 0);
60
+ --pk-brand: oklch(0.205 0 0);
61
+ --pk-brand-ink: oklch(0.985 0 0);
62
+ --pk-red: oklch(0.577 0.245 27.325);
63
+ --pk-chart-1: oklch(0.87 0 0);
64
+ --pk-chart-2: oklch(0.556 0 0);
65
+ --pk-chart-3: oklch(0.439 0 0);
66
+ --pk-chart-4: oklch(0.371 0 0);
67
+ --pk-chart-5: oklch(0.269 0 0);
68
+ color-scheme: light;
69
+ }
70
+
71
+ @media (prefers-color-scheme: light) {
72
+ :root[data-theme="system"] {
73
+ --pk-bg: oklch(1 0 0);
74
+ --pk-sidebar-primary: oklch(0.205 0 0);
75
+ --pk-sidebar-primary-ink: oklch(0.985 0 0);
76
+ --pk-surface: oklch(1 0 0);
77
+ --pk-surface-2: oklch(0.97 0 0);
78
+ --pk-accent: oklch(0.97 0 0);
79
+ --pk-border: oklch(0.922 0 0);
80
+ --pk-input: oklch(0.922 0 0);
81
+ --pk-ring: oklch(0.708 0 0);
82
+ --pk-text: oklch(0.145 0 0);
83
+ --pk-text-2: oklch(0.205 0 0);
84
+ --pk-muted: oklch(0.556 0 0);
85
+ --pk-brand: oklch(0.205 0 0);
86
+ --pk-brand-ink: oklch(0.985 0 0);
87
+ --pk-red: oklch(0.577 0.245 27.325);
88
+ --pk-chart-1: oklch(0.87 0 0);
89
+ --pk-chart-2: oklch(0.556 0 0);
90
+ --pk-chart-3: oklch(0.439 0 0);
91
+ --pk-chart-4: oklch(0.371 0 0);
92
+ --pk-chart-5: oklch(0.269 0 0);
93
+ color-scheme: light;
94
+ }
95
+ }
@@ -0,0 +1,85 @@
1
+ /* PhlexKit theme: Zinc — translated from a ui.shadcn.com/create export
2
+ (preset b6mHLWLL2u, shadcn's stock zinc palette). Pure token override:
3
+ load AFTER the phlex_kit.css manifest and the cascade re-themes every
4
+ component.
5
+
6
+ Translation notes (the recipe for any shadcn/create export):
7
+ - background → bg; card/popover → surface; secondary/muted/accent →
8
+ surface-2/accent; foreground → text; secondary-foreground → text-2;
9
+ muted-foreground → muted; primary/primary-foreground → brand/brand-ink;
10
+ destructive → red; border/input/ring/chart-1..5/radius map 1:1.
11
+ - shadcn is light-default with a .dark class; PhlexKit themes are
12
+ dark-default with data-theme="light" opt-in — blocks swapped.
13
+ - sidebar-* has no --pk-* slots (the kit's sidebar reads the shared
14
+ surface tokens) and shadcn/create exports no green/amber — both fall
15
+ through to _tokens.css defaults.
16
+ - dark text-2 has no shadcn source (every dark *-foreground is 0.985);
17
+ zinc-300 is used, matching the kit's own dark text-2 weight. */
18
+ :root {
19
+ --pk-bg: oklch(0.141 0.005 285.823);
20
+ --pk-surface: oklch(0.21 0.006 285.885);
21
+ --pk-surface-2: oklch(0.274 0.006 286.033);
22
+ --pk-accent: oklch(0.274 0.006 286.033);
23
+ --pk-border: oklch(1 0 0 / 10%);
24
+ --pk-input: oklch(1 0 0 / 15%);
25
+ --pk-ring: oklch(0.552 0.016 285.938);
26
+ --pk-text: oklch(0.985 0 0);
27
+ --pk-text-2: oklch(0.871 0.006 286.286);
28
+ --pk-muted: oklch(0.705 0.015 286.067);
29
+ --pk-brand: oklch(0.92 0.004 286.32);
30
+ --pk-brand-ink: oklch(0.21 0.006 285.885);
31
+ --pk-red: oklch(0.704 0.191 22.216);
32
+ --pk-chart-1: oklch(0.871 0.006 286.286);
33
+ --pk-chart-2: oklch(0.552 0.016 285.938);
34
+ --pk-chart-3: oklch(0.442 0.017 285.786);
35
+ --pk-chart-4: oklch(0.37 0.013 285.805);
36
+ --pk-chart-5: oklch(0.274 0.006 286.033);
37
+ --pk-radius: 0.625rem;
38
+ color-scheme: dark;
39
+ }
40
+
41
+ :root[data-theme="light"] {
42
+ --pk-bg: oklch(1 0 0);
43
+ --pk-surface: oklch(1 0 0);
44
+ --pk-surface-2: oklch(0.967 0.001 286.375);
45
+ --pk-accent: oklch(0.967 0.001 286.375);
46
+ --pk-border: oklch(0.92 0.004 286.32);
47
+ --pk-input: oklch(0.92 0.004 286.32);
48
+ --pk-ring: oklch(0.705 0.015 286.067);
49
+ --pk-text: oklch(0.141 0.005 285.823);
50
+ --pk-text-2: oklch(0.21 0.006 285.885);
51
+ --pk-muted: oklch(0.552 0.016 285.938);
52
+ --pk-brand: oklch(0.21 0.006 285.885);
53
+ --pk-brand-ink: oklch(0.985 0 0);
54
+ --pk-red: oklch(0.577 0.245 27.325);
55
+ --pk-chart-1: oklch(0.871 0.006 286.286);
56
+ --pk-chart-2: oklch(0.552 0.016 285.938);
57
+ --pk-chart-3: oklch(0.442 0.017 285.786);
58
+ --pk-chart-4: oklch(0.37 0.013 285.805);
59
+ --pk-chart-5: oklch(0.274 0.006 286.033);
60
+ color-scheme: light;
61
+ }
62
+
63
+ @media (prefers-color-scheme: light) {
64
+ :root[data-theme="system"] {
65
+ --pk-bg: oklch(1 0 0);
66
+ --pk-surface: oklch(1 0 0);
67
+ --pk-surface-2: oklch(0.967 0.001 286.375);
68
+ --pk-accent: oklch(0.967 0.001 286.375);
69
+ --pk-border: oklch(0.92 0.004 286.32);
70
+ --pk-input: oklch(0.92 0.004 286.32);
71
+ --pk-ring: oklch(0.705 0.015 286.067);
72
+ --pk-text: oklch(0.141 0.005 285.823);
73
+ --pk-text-2: oklch(0.21 0.006 285.885);
74
+ --pk-muted: oklch(0.552 0.016 285.938);
75
+ --pk-brand: oklch(0.21 0.006 285.885);
76
+ --pk-brand-ink: oklch(0.985 0 0);
77
+ --pk-red: oklch(0.577 0.245 27.325);
78
+ --pk-chart-1: oklch(0.871 0.006 286.286);
79
+ --pk-chart-2: oklch(0.552 0.016 285.938);
80
+ --pk-chart-3: oklch(0.442 0.017 285.786);
81
+ --pk-chart-4: oklch(0.37 0.013 285.805);
82
+ --pk-chart-5: oklch(0.274 0.006 286.033);
83
+ color-scheme: light;
84
+ }
85
+ }
@@ -12,3 +12,16 @@
12
12
  .pk-accordion-icon svg { width: 1rem; height: 1rem; }
13
13
  .pk-accordion-content { overflow-y: hidden; }
14
14
  .pk-accordion-default-content { padding-bottom: 1rem; padding-top: 0; font-size: .875rem; }
15
+
16
+ /* Disabled items: inert trigger, dimmed. */
17
+ .pk-accordion-item[data-disabled] .pk-accordion-trigger {
18
+ pointer-events: none;
19
+ opacity: .5;
20
+ }
21
+
22
+ /* Bordered flavour (shadcn's "Borders" example): outer border, inset rows. */
23
+ .pk-accordion.bordered {
24
+ border: 1px solid var(--pk-border);
25
+ border-radius: var(--pk-radius);
26
+ padding-inline: 1rem;
27
+ }
@@ -4,7 +4,16 @@ module PhlexKit
4
4
  # AccordionContent). Animated open/close via the phlex-kit--accordion controller
5
5
  # (native Web Animations API — no `motion` dependency).
6
6
  class Accordion < BaseComponent
7
- def initialize(**attrs) = (@attrs = attrs)
8
- def view_template(&) = div(**mix({ class: "pk-accordion" }, @attrs), &)
7
+ # single (default, shadcn's): opening an item closes the others.
8
+ TYPES = { single: "single", multiple: "multiple" }.freeze
9
+
10
+ def initialize(type: :single, **attrs)
11
+ @type = type.to_sym
12
+ @attrs = attrs
13
+ end
14
+
15
+ def view_template(&)
16
+ div(**mix({ class: "pk-accordion", data: { type: TYPES.fetch(@type) } }, @attrs), &)
17
+ end
9
18
  end
10
19
  end
@@ -6,9 +6,7 @@ module PhlexKit
6
6
  if block
7
7
  yield
8
8
  else
9
- svg(xmlns: "http://www.w3.org/2000/svg", viewbox: "0 0 20 20", fill: "currentColor") do |s|
10
- s.path(fill_rule: "evenodd", clip_rule: "evenodd", d: "M5.23 7.21a.75.75 0 011.06.02L10 11.168l3.71-3.938a.75.75 0 111.08 1.04l-4.25 4.5a.75.75 0 01-1.08 0l-4.25-4.5a.75.75 0 01.02-1.06z")
11
- end
9
+ render Icon.new(:chevron_down, size: nil)
12
10
  end
13
11
  end
14
12
  end