phlex_kit 0.4.0 → 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.
- checksums.yaml +4 -4
- data/app/assets/stylesheets/phlex_kit/_tokens.css +16 -0
- data/app/assets/stylesheets/phlex_kit/phlex_kit.css +1 -0
- data/app/components/phlex_kit/alert/alert.css +60 -22
- data/app/components/phlex_kit/alert/alert.rb +11 -7
- data/app/components/phlex_kit/alert/alert_action.rb +14 -0
- data/app/components/phlex_kit/alert/alert_title.rb +3 -2
- data/app/components/phlex_kit/alert_dialog/alert_dialog.css +5 -2
- data/app/components/phlex_kit/avatar/avatar.css +80 -9
- data/app/components/phlex_kit/avatar/avatar.rb +6 -4
- data/app/components/phlex_kit/avatar/avatar_badge.rb +15 -0
- data/app/components/phlex_kit/avatar/avatar_group_count.rb +14 -0
- data/app/components/phlex_kit/badge/badge.css +61 -39
- data/app/components/phlex_kit/badge/badge.rb +24 -7
- data/app/components/phlex_kit/breadcrumb/breadcrumb.css +2 -2
- data/app/components/phlex_kit/bubble/bubble.css +49 -8
- data/app/components/phlex_kit/button/button.css +71 -26
- data/app/components/phlex_kit/button/button.rb +20 -13
- data/app/components/phlex_kit/button_group/button_group.css +59 -8
- data/app/components/phlex_kit/button_group/button_group.rb +11 -5
- data/app/components/phlex_kit/button_group/button_group_separator.rb +14 -0
- data/app/components/phlex_kit/button_group/button_group_text.rb +14 -0
- data/app/components/phlex_kit/calendar/calendar.css +66 -15
- data/app/components/phlex_kit/calendar/calendar.rb +87 -19
- data/app/components/phlex_kit/calendar/calendar_controller.js +226 -75
- data/app/components/phlex_kit/calendar/calendar_days.rb +7 -6
- data/app/components/phlex_kit/calendar/calendar_weekdays.rb +3 -1
- data/app/components/phlex_kit/checkbox/checkbox.css +52 -7
- data/app/components/phlex_kit/collapsible/collapsible.css +10 -1
- data/app/components/phlex_kit/collapsible/collapsible_controller.js +9 -2
- data/app/components/phlex_kit/combobox/combobox.css +37 -3
- data/app/components/phlex_kit/combobox/combobox.rb +3 -1
- data/app/components/phlex_kit/combobox/combobox_controller.js +9 -1
- data/app/components/phlex_kit/combobox/combobox_input_trigger.rb +7 -2
- data/app/components/phlex_kit/command/command.css +58 -22
- data/app/components/phlex_kit/command/command_controller.js +3 -1
- data/app/components/phlex_kit/command/command_input.rb +29 -25
- data/app/components/phlex_kit/command/command_separator.rb +18 -0
- data/app/components/phlex_kit/command/command_shortcut.rb +13 -0
- data/app/components/phlex_kit/context_menu/context_menu.css +78 -6
- data/app/components/phlex_kit/context_menu/context_menu_checkbox_item.rb +27 -0
- data/app/components/phlex_kit/context_menu/context_menu_group.rb +10 -0
- data/app/components/phlex_kit/context_menu/context_menu_item.rb +13 -3
- data/app/components/phlex_kit/context_menu/context_menu_radio_group.rb +10 -0
- data/app/components/phlex_kit/context_menu/context_menu_radio_item.rb +30 -0
- data/app/components/phlex_kit/context_menu/context_menu_shortcut.rb +11 -0
- data/app/components/phlex_kit/context_menu/context_menu_sub.rb +11 -0
- data/app/components/phlex_kit/context_menu/context_menu_sub_content.rb +12 -0
- data/app/components/phlex_kit/context_menu/context_menu_sub_trigger.rb +17 -0
- data/app/components/phlex_kit/dialog/dialog.css +34 -16
- data/app/components/phlex_kit/dialog/dialog_close.rb +13 -0
- data/app/components/phlex_kit/dialog/dialog_content.rb +13 -5
- data/app/components/phlex_kit/drawer/drawer.css +56 -17
- data/app/components/phlex_kit/drawer/drawer_content.rb +8 -4
- data/app/components/phlex_kit/empty/empty.css +13 -8
- data/app/components/phlex_kit/field/field.css +152 -0
- data/app/components/phlex_kit/field/field.rb +35 -0
- data/app/components/phlex_kit/field/field_content.rb +11 -0
- data/app/components/phlex_kit/field/field_description.rb +10 -0
- data/app/components/phlex_kit/field/field_error.rb +29 -0
- data/app/components/phlex_kit/field/field_group.rb +12 -0
- data/app/components/phlex_kit/field/field_label.rb +12 -0
- data/app/components/phlex_kit/field/field_legend.rb +17 -0
- data/app/components/phlex_kit/field/field_separator.rb +15 -0
- data/app/components/phlex_kit/field/field_set.rb +10 -0
- data/app/components/phlex_kit/field/field_title.rb +11 -0
- data/app/components/phlex_kit/hover_card/hover_card.css +12 -4
- data/app/components/phlex_kit/hover_card/hover_card_content.rb +11 -2
- data/app/components/phlex_kit/input/input.css +45 -11
- data/app/components/phlex_kit/input_group/input_group.css +88 -12
- data/app/components/phlex_kit/input_group/input_group_addon.rb +11 -4
- data/app/components/phlex_kit/input_group/input_group_button.rb +18 -0
- data/app/components/phlex_kit/input_otp/input_otp.css +29 -5
- data/app/components/phlex_kit/item/item.css +56 -8
- data/app/components/phlex_kit/item/item.rb +13 -3
- data/app/components/phlex_kit/item/item_footer.rb +8 -0
- data/app/components/phlex_kit/item/item_header.rb +8 -0
- data/app/components/phlex_kit/item/item_media.rb +13 -3
- data/app/components/phlex_kit/item/item_separator.rb +10 -0
- data/app/components/phlex_kit/kbd/kbd.css +10 -0
- data/app/components/phlex_kit/marker/marker.css +29 -9
- data/app/components/phlex_kit/marker/marker.rb +13 -3
- data/app/components/phlex_kit/menubar/menubar.css +88 -17
- data/app/components/phlex_kit/menubar/menubar_checkbox_item.rb +26 -0
- data/app/components/phlex_kit/menubar/menubar_group.rb +10 -0
- data/app/components/phlex_kit/menubar/menubar_item.rb +9 -4
- data/app/components/phlex_kit/menubar/menubar_label.rb +10 -0
- data/app/components/phlex_kit/menubar/menubar_radio_group.rb +10 -0
- data/app/components/phlex_kit/menubar/menubar_radio_item.rb +29 -0
- data/app/components/phlex_kit/menubar/menubar_shortcut.rb +10 -0
- data/app/components/phlex_kit/menubar/menubar_sub.rb +10 -0
- data/app/components/phlex_kit/menubar/menubar_sub_content.rb +11 -0
- data/app/components/phlex_kit/menubar/menubar_sub_trigger.rb +17 -0
- data/app/components/phlex_kit/message/message.css +8 -1
- data/app/components/phlex_kit/message_scroller/message_scroller.css +18 -0
- data/app/components/phlex_kit/native_select/native_select.css +25 -5
- data/app/components/phlex_kit/navigation_menu/navigation_menu.css +9 -6
- data/app/components/phlex_kit/pagination/pagination.css +10 -3
- data/app/components/phlex_kit/pagination/pagination_link.rb +21 -0
- data/app/components/phlex_kit/pagination/pagination_next.rb +20 -0
- data/app/components/phlex_kit/pagination/pagination_previous.rb +21 -0
- data/app/components/phlex_kit/popover/popover.css +15 -4
- data/app/components/phlex_kit/popover/popover_content.rb +11 -2
- data/app/components/phlex_kit/popover/popover_description.rb +10 -0
- data/app/components/phlex_kit/popover/popover_header.rb +10 -0
- data/app/components/phlex_kit/popover/popover_title.rb +10 -0
- data/app/components/phlex_kit/progress/progress.css +5 -5
- data/app/components/phlex_kit/radio_button/radio_button.css +46 -2
- data/app/components/phlex_kit/radio_group/radio_group.css +2 -2
- data/app/components/phlex_kit/resizable/resizable.css +17 -0
- data/app/components/phlex_kit/resizable/resizable_handle.rb +7 -3
- data/app/components/phlex_kit/select/select.css +46 -10
- data/app/components/phlex_kit/select/select_separator.rb +10 -0
- data/app/components/phlex_kit/select/select_trigger.rb +5 -2
- data/app/components/phlex_kit/sheet/sheet.css +18 -9
- data/app/components/phlex_kit/sheet/sheet_close.rb +10 -0
- data/app/components/phlex_kit/sheet/sheet_content.rb +8 -4
- data/app/components/phlex_kit/sidebar/sidebar.css +76 -9
- data/app/components/phlex_kit/sidebar/sidebar_controller.js +22 -7
- data/app/components/phlex_kit/sidebar/sidebar_menu_button.rb +8 -3
- data/app/components/phlex_kit/sidebar/sidebar_rail.rb +21 -0
- data/app/components/phlex_kit/sidebar/sidebar_wrapper.rb +25 -8
- data/app/components/phlex_kit/skeleton/skeleton.css +1 -1
- data/app/components/phlex_kit/slider/slider.css +33 -12
- data/app/components/phlex_kit/switch/switch.css +50 -13
- data/app/components/phlex_kit/switch/switch.rb +5 -2
- data/app/components/phlex_kit/table/table.css +8 -4
- data/app/components/phlex_kit/tabs/tabs.css +72 -8
- data/app/components/phlex_kit/tabs/tabs.rb +7 -2
- data/app/components/phlex_kit/tabs/tabs_list.rb +11 -2
- data/app/components/phlex_kit/textarea/textarea.css +33 -13
- data/app/components/phlex_kit/toast/toast.css +1 -1
- data/app/components/phlex_kit/toggle/toggle.css +25 -10
- data/app/components/phlex_kit/toggle_group/toggle_group.css +7 -6
- data/app/components/phlex_kit/tooltip/tooltip.css +37 -10
- data/app/components/phlex_kit/tooltip/tooltip_content.rb +9 -4
- data/lib/phlex_kit/version.rb +1 -1
- metadata +50 -1
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
module PhlexKit
|
|
2
|
+
# Validation message(s) for a Field, ported from shadcn/ui's FieldError.
|
|
3
|
+
# Pass `errors:` an array of message strings (deduped; one renders plain,
|
|
4
|
+
# several render a list) or give a block. Renders nothing when empty —
|
|
5
|
+
# matching theirs. Distinct from FormFieldError, which is the Stimulus
|
|
6
|
+
# live-validation slot; server-rendered errors belong here. See field.rb.
|
|
7
|
+
class FieldError < BaseComponent
|
|
8
|
+
def initialize(errors: nil, **attrs)
|
|
9
|
+
@errors = Array(errors).compact.uniq
|
|
10
|
+
@attrs = attrs
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def view_template(&block)
|
|
14
|
+
return if @errors.empty? && !block
|
|
15
|
+
|
|
16
|
+
div(**mix({ class: "pk-field-error", role: "alert", data: { slot: "field-error" } }, @attrs)) do
|
|
17
|
+
if block
|
|
18
|
+
yield
|
|
19
|
+
elsif @errors.length == 1
|
|
20
|
+
plain @errors.first
|
|
21
|
+
else
|
|
22
|
+
ul(class: "pk-field-error-list") do
|
|
23
|
+
@errors.each { |message| li { message } }
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
module PhlexKit
|
|
2
|
+
# Column of Fields with the canonical rhythm, ported from shadcn/ui's
|
|
3
|
+
# FieldGroup. Also the CONTAINER for responsive fields — a Field with
|
|
4
|
+
# orientation: :responsive goes horizontal when this group is ≥28rem wide.
|
|
5
|
+
# See field.rb.
|
|
6
|
+
class FieldGroup < BaseComponent
|
|
7
|
+
def initialize(**attrs) = (@attrs = attrs)
|
|
8
|
+
def view_template(&)
|
|
9
|
+
div(**mix({ class: "pk-field-group", data: { slot: "field-group" } }, @attrs), &)
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
module PhlexKit
|
|
2
|
+
# <label> for a Field's control, ported from shadcn/ui's FieldLabel (their
|
|
3
|
+
# Label + field styling — ours composes .pk-label the same way). Wrap a
|
|
4
|
+
# whole Field in it for the choice-card recipe: the label grows a border
|
|
5
|
+
# and highlights while its control is checked. See field.rb.
|
|
6
|
+
class FieldLabel < BaseComponent
|
|
7
|
+
def initialize(**attrs) = (@attrs = attrs)
|
|
8
|
+
def view_template(&)
|
|
9
|
+
label(**mix({ class: "pk-label pk-field-label", data: { slot: "field-label" } }, @attrs), &)
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
module PhlexKit
|
|
2
|
+
# <legend> of a FieldSet, ported from shadcn/ui's FieldLegend.
|
|
3
|
+
# variant: :legend (text-base, default) or :label (text-sm — matches
|
|
4
|
+
# FieldLabel when the set sits among plain fields). See field.rb.
|
|
5
|
+
class FieldLegend < BaseComponent
|
|
6
|
+
VARIANTS = { legend: "legend", label: "label" }.freeze
|
|
7
|
+
|
|
8
|
+
def initialize(variant: :legend, **attrs)
|
|
9
|
+
@variant = variant.to_sym
|
|
10
|
+
@attrs = attrs
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def view_template(&)
|
|
14
|
+
legend(**mix({ class: "pk-field-legend #{VARIANTS.fetch(@variant)}" }, @attrs), &)
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
module PhlexKit
|
|
2
|
+
# Hairline between FieldGroups, optionally with centered text riding the
|
|
3
|
+
# line ("Or continue with"), ported from shadcn/ui's FieldSeparator.
|
|
4
|
+
# See field.rb.
|
|
5
|
+
class FieldSeparator < BaseComponent
|
|
6
|
+
def initialize(**attrs) = (@attrs = attrs)
|
|
7
|
+
|
|
8
|
+
def view_template(&block)
|
|
9
|
+
div(**mix({ class: "pk-field-separator", data: { content: block ? "true" : "false" } }, @attrs)) do
|
|
10
|
+
render Separator.new(class: "pk-field-separator-line")
|
|
11
|
+
span(class: "pk-field-separator-content", &block) if block
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
module PhlexKit
|
|
2
|
+
# Semantic <fieldset> grouping several Fields under a FieldLegend, ported
|
|
3
|
+
# from shadcn/ui's FieldSet. See field.rb.
|
|
4
|
+
class FieldSet < BaseComponent
|
|
5
|
+
def initialize(**attrs) = (@attrs = attrs)
|
|
6
|
+
def view_template(&)
|
|
7
|
+
fieldset(**mix({ class: "pk-field-set" }, @attrs), &)
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
end
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
module PhlexKit
|
|
2
|
+
# Label-looking title that is NOT a <label> (for content inside a
|
|
3
|
+
# FieldLabel-wrapped choice card, where nesting labels is invalid HTML),
|
|
4
|
+
# ported from shadcn/ui's FieldTitle. See field.rb.
|
|
5
|
+
class FieldTitle < BaseComponent
|
|
6
|
+
def initialize(**attrs) = (@attrs = attrs)
|
|
7
|
+
def view_template(&)
|
|
8
|
+
div(**mix({ class: "pk-field-title" }, @attrs), &)
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
end
|
|
@@ -1,8 +1,16 @@
|
|
|
1
|
-
/* Co-located with hover_card.rb.
|
|
1
|
+
/* Co-located with hover_card.rb. Matched to shadcn/ui's current hover-card
|
|
2
|
+
(nova): 16rem popover panel, rounded-lg, p-2.5, text-sm, foreground-tint
|
|
3
|
+
ring. side: top/left/right flip the CSS position (bottom default).
|
|
4
|
+
Tokens from the global stylesheet. */
|
|
2
5
|
.pk-hover-card { position: relative; display: inline-block; }
|
|
3
6
|
.pk-hover-card-trigger { display: inline-block; }
|
|
4
7
|
.pk-hover-card-content {
|
|
5
|
-
position: absolute; z-index: 50; top: calc(100% + .
|
|
6
|
-
border: 1px solid
|
|
7
|
-
|
|
8
|
+
position: absolute; z-index: 50; top: calc(100% + .25rem); left: 0; width: 16rem;
|
|
9
|
+
border: 1px solid color-mix(in oklab, var(--pk-text) 10%, transparent);
|
|
10
|
+
border-radius: var(--pk-radius); background: var(--pk-surface);
|
|
11
|
+
color: var(--pk-text); padding: .625rem; font-size: .875rem;
|
|
12
|
+
box-shadow: 0 8px 24px rgb(0 0 0 / .3); outline: none;
|
|
8
13
|
}
|
|
14
|
+
.pk-hover-card-content.top { top: auto; bottom: calc(100% + .25rem); }
|
|
15
|
+
.pk-hover-card-content.left { top: 50%; left: auto; right: calc(100% + .25rem); transform: translateY(-50%); }
|
|
16
|
+
.pk-hover-card-content.right { top: 50%; left: calc(100% + .25rem); transform: translateY(-50%); }
|
|
@@ -1,8 +1,17 @@
|
|
|
1
1
|
module PhlexKit
|
|
2
|
+
# The floating panel of a PhlexKit::HoverCard. `side:` picks the edge it
|
|
3
|
+
# opens from (:bottom default, :top/:left/:right — shadcn's side prop).
|
|
2
4
|
class HoverCardContent < BaseComponent
|
|
3
|
-
|
|
5
|
+
SIDES = { bottom: nil, top: "top", left: "left", right: "right" }.freeze
|
|
6
|
+
|
|
7
|
+
def initialize(side: :bottom, **attrs)
|
|
8
|
+
@side = side.to_sym
|
|
9
|
+
@attrs = attrs
|
|
10
|
+
end
|
|
11
|
+
|
|
4
12
|
def view_template(&)
|
|
5
|
-
|
|
13
|
+
classes = [ "pk-hover-card-content", "pk-hidden", SIDES.fetch(@side) ].compact.join(" ")
|
|
14
|
+
div(**mix({ class: classes, data: { phlex_kit__hover_card_target: "content", state: "closed" } }, @attrs), &)
|
|
6
15
|
end
|
|
7
16
|
end
|
|
8
17
|
end
|
|
@@ -1,29 +1,63 @@
|
|
|
1
|
-
/* Co-located with input.rb —
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
/* Co-located with input.rb — PhlexKit::Input. Matched to shadcn/ui's current
|
|
2
|
+
input (nova): h-8, rounded-lg, px-2.5, translucent input fill in dark /
|
|
3
|
+
transparent in light, ring focus (border-ring, not brand), invalid ring
|
|
4
|
+
with the 20/40 light/dark split, tinted disabled fill, styled file
|
|
5
|
+
selector button. text-base on small screens (prevents iOS zoom), text-sm
|
|
6
|
+
from md up — their text-base md:text-sm. Theme tokens global. */
|
|
7
7
|
.pk-input {
|
|
8
8
|
height: 2rem;
|
|
9
9
|
width: 100%;
|
|
10
|
+
min-width: 0;
|
|
10
11
|
border-radius: var(--pk-radius);
|
|
11
12
|
border: 1px solid var(--pk-input);
|
|
12
13
|
background: color-mix(in oklab, var(--pk-input) 30%, transparent);
|
|
13
14
|
color: var(--pk-text);
|
|
14
15
|
padding: .25rem .625rem;
|
|
15
16
|
font: inherit;
|
|
16
|
-
font-size:
|
|
17
|
-
transition: color .15s ease, border-color .15s ease, box-shadow .15s ease;
|
|
17
|
+
font-size: 1rem;
|
|
18
|
+
transition: color .15s ease, border-color .15s ease, box-shadow .15s ease, background-color .15s ease;
|
|
19
|
+
}
|
|
20
|
+
@media (min-width: 768px) {
|
|
21
|
+
.pk-input { font-size: .875rem; }
|
|
18
22
|
}
|
|
19
23
|
.pk-input::placeholder { color: var(--pk-muted); }
|
|
24
|
+
.pk-input::file-selector-button {
|
|
25
|
+
display: inline-flex;
|
|
26
|
+
height: 1.5rem;
|
|
27
|
+
border: 0;
|
|
28
|
+
background: transparent;
|
|
29
|
+
color: var(--pk-text);
|
|
30
|
+
font: inherit;
|
|
31
|
+
font-size: .875rem;
|
|
32
|
+
font-weight: 500;
|
|
33
|
+
margin-right: .5rem;
|
|
34
|
+
}
|
|
20
35
|
.pk-input:focus-visible {
|
|
21
36
|
outline: none;
|
|
22
|
-
border-color: var(--pk-
|
|
37
|
+
border-color: var(--pk-ring);
|
|
23
38
|
box-shadow: 0 0 0 3px color-mix(in oklab, var(--pk-ring) 50%, transparent);
|
|
24
39
|
}
|
|
25
|
-
.pk-input:disabled {
|
|
40
|
+
.pk-input:disabled {
|
|
41
|
+
pointer-events: none;
|
|
42
|
+
cursor: not-allowed;
|
|
43
|
+
opacity: .5;
|
|
44
|
+
background: color-mix(in oklab, var(--pk-input) 80%, transparent);
|
|
45
|
+
}
|
|
26
46
|
.pk-input[aria-invalid="true"] {
|
|
47
|
+
border-color: color-mix(in oklab, var(--pk-red) 50%, transparent);
|
|
48
|
+
box-shadow: 0 0 0 3px color-mix(in oklab, var(--pk-red) 40%, transparent);
|
|
49
|
+
}
|
|
50
|
+
:root[data-theme="light"] .pk-input { background: transparent; }
|
|
51
|
+
:root[data-theme="light"] .pk-input:disabled { background: color-mix(in oklab, var(--pk-input) 50%, transparent); }
|
|
52
|
+
:root[data-theme="light"] .pk-input[aria-invalid="true"] {
|
|
27
53
|
border-color: var(--pk-red);
|
|
28
|
-
box-shadow: 0 0 0 3px color-mix(in oklab, var(--pk-red)
|
|
54
|
+
box-shadow: 0 0 0 3px color-mix(in oklab, var(--pk-red) 20%, transparent);
|
|
55
|
+
}
|
|
56
|
+
@media (prefers-color-scheme: light) {
|
|
57
|
+
:root[data-theme="system"] .pk-input { background: transparent; }
|
|
58
|
+
:root[data-theme="system"] .pk-input:disabled { background: color-mix(in oklab, var(--pk-input) 50%, transparent); }
|
|
59
|
+
:root[data-theme="system"] .pk-input[aria-invalid="true"] {
|
|
60
|
+
border-color: var(--pk-red);
|
|
61
|
+
box-shadow: 0 0 0 3px color-mix(in oklab, var(--pk-red) 20%, transparent);
|
|
62
|
+
}
|
|
29
63
|
}
|
|
@@ -1,35 +1,111 @@
|
|
|
1
|
-
/* Co-located with input_group.rb —
|
|
2
|
-
carries the border/fill and focus ring; the inner .pk-input
|
|
1
|
+
/* Co-located with input_group.rb — PhlexKit::InputGroup, shadcn parity: the
|
|
2
|
+
shell carries the border/fill and focus ring; the inner .pk-input /
|
|
3
|
+
.pk-textarea go naked. Matched to nova: fixed h-8 (auto with a textarea or
|
|
4
|
+
block addon), ring-colored focus, invalid + disabled states lifted from
|
|
5
|
+
the control via :has(), block addons stack the group into a column,
|
|
6
|
+
theme-forked fill (translucent input in dark / transparent in light). */
|
|
3
7
|
.pk-input-group {
|
|
8
|
+
position: relative;
|
|
4
9
|
display: flex;
|
|
5
10
|
align-items: center;
|
|
6
11
|
width: 100%;
|
|
12
|
+
min-width: 0;
|
|
13
|
+
height: 2rem;
|
|
7
14
|
border: 1px solid var(--pk-input);
|
|
8
15
|
border-radius: var(--pk-radius);
|
|
9
16
|
background: color-mix(in oklab, var(--pk-input) 30%, transparent);
|
|
10
|
-
transition: border-color .15s ease, box-shadow .15s ease;
|
|
17
|
+
transition: border-color .15s ease, box-shadow .15s ease, background-color .15s ease;
|
|
18
|
+
}
|
|
19
|
+
.pk-input-group:has(> textarea),
|
|
20
|
+
.pk-input-group:has(> .pk-textarea),
|
|
21
|
+
.pk-input-group:has(> .pk-input-group-addon.block-start),
|
|
22
|
+
.pk-input-group:has(> .pk-input-group-addon.block-end) {
|
|
23
|
+
height: auto;
|
|
24
|
+
flex-direction: column;
|
|
25
|
+
align-items: stretch;
|
|
11
26
|
}
|
|
12
27
|
.pk-input-group:focus-within {
|
|
13
|
-
border-color: var(--pk-
|
|
28
|
+
border-color: var(--pk-ring);
|
|
14
29
|
box-shadow: 0 0 0 3px color-mix(in oklab, var(--pk-ring) 50%, transparent);
|
|
15
30
|
}
|
|
16
|
-
.pk-input-group
|
|
31
|
+
.pk-input-group:has([aria-invalid="true"]) {
|
|
32
|
+
border-color: color-mix(in oklab, var(--pk-red) 50%, transparent);
|
|
33
|
+
box-shadow: 0 0 0 3px color-mix(in oklab, var(--pk-red) 40%, transparent);
|
|
34
|
+
}
|
|
35
|
+
.pk-input-group:has(:disabled) {
|
|
36
|
+
opacity: .5;
|
|
37
|
+
background: color-mix(in oklab, var(--pk-input) 80%, transparent);
|
|
38
|
+
}
|
|
39
|
+
.pk-input-group .pk-input,
|
|
40
|
+
.pk-input-group .pk-textarea {
|
|
17
41
|
border: 0;
|
|
18
42
|
background: transparent;
|
|
19
43
|
box-shadow: none;
|
|
20
44
|
flex: 1;
|
|
21
45
|
}
|
|
22
|
-
.pk-input-group .pk-input:focus-visible
|
|
46
|
+
.pk-input-group .pk-input:focus-visible,
|
|
47
|
+
.pk-input-group .pk-textarea:focus-visible { outline: none; box-shadow: none; border: 0; }
|
|
48
|
+
.pk-input-group .pk-input:disabled,
|
|
49
|
+
.pk-input-group .pk-textarea:disabled { background: transparent; }
|
|
50
|
+
/* Tighten the control's near-side padding beside an inline addon. */
|
|
51
|
+
.pk-input-group:has(> .pk-input-group-addon.start) > .pk-input { padding-left: .375rem; }
|
|
52
|
+
.pk-input-group:has(> .pk-input-group-addon.end) > .pk-input { padding-right: .375rem; }
|
|
53
|
+
|
|
23
54
|
.pk-input-group-addon {
|
|
24
55
|
display: flex;
|
|
25
56
|
align-items: center;
|
|
26
|
-
|
|
57
|
+
justify-content: center;
|
|
58
|
+
gap: .5rem;
|
|
27
59
|
flex: none;
|
|
28
|
-
|
|
60
|
+
cursor: text;
|
|
61
|
+
user-select: none;
|
|
62
|
+
padding-block: .375rem;
|
|
63
|
+
color: var(--pk-muted);
|
|
64
|
+
font-size: .875rem;
|
|
65
|
+
font-weight: 500;
|
|
66
|
+
}
|
|
67
|
+
.pk-input-group-addon.start { order: -1; padding-left: .5rem; }
|
|
68
|
+
.pk-input-group-addon.end { order: 99; padding-right: .5rem; }
|
|
69
|
+
.pk-input-group-addon.block-start,
|
|
70
|
+
.pk-input-group-addon.block-end {
|
|
71
|
+
width: 100%;
|
|
72
|
+
justify-content: flex-start;
|
|
73
|
+
padding-inline: .625rem;
|
|
74
|
+
}
|
|
75
|
+
.pk-input-group-addon.block-start { order: -1; padding-top: .5rem; }
|
|
76
|
+
.pk-input-group-addon.block-end { order: 99; padding-bottom: .5rem; }
|
|
77
|
+
.pk-input-group-addon.block-start.bordered { border-bottom: 1px solid var(--pk-border); padding-bottom: .5rem; }
|
|
78
|
+
.pk-input-group-addon.block-end.bordered { border-top: 1px solid var(--pk-border); padding-top: .5rem; }
|
|
79
|
+
.pk-input-group-addon svg:not([class*="size-"]) { width: 1rem; height: 1rem; }
|
|
80
|
+
.pk-input-group-addon > kbd { border-radius: calc(var(--pk-radius) - 5px); }
|
|
81
|
+
.pk-input-group-addon.start:has(> .pk-button) { margin-left: -0.3rem; }
|
|
82
|
+
.pk-input-group-addon.end:has(> .pk-button) { margin-right: -0.3rem; }
|
|
83
|
+
|
|
84
|
+
.pk-button.pk-input-group-button {
|
|
85
|
+
gap: .25rem;
|
|
86
|
+
border-radius: calc(var(--pk-radius) - 3px);
|
|
87
|
+
box-shadow: none;
|
|
88
|
+
}
|
|
89
|
+
.pk-input-group-text {
|
|
90
|
+
display: flex;
|
|
91
|
+
align-items: center;
|
|
92
|
+
gap: .5rem;
|
|
29
93
|
color: var(--pk-muted);
|
|
30
94
|
font-size: .875rem;
|
|
31
95
|
}
|
|
32
|
-
.pk-input-group-
|
|
33
|
-
|
|
34
|
-
.pk-input-group
|
|
35
|
-
.pk-input-group
|
|
96
|
+
.pk-input-group-text svg { pointer-events: none; }
|
|
97
|
+
|
|
98
|
+
:root[data-theme="light"] .pk-input-group { background: transparent; }
|
|
99
|
+
:root[data-theme="light"] .pk-input-group:has(:disabled) { background: color-mix(in oklab, var(--pk-input) 50%, transparent); }
|
|
100
|
+
:root[data-theme="light"] .pk-input-group:has([aria-invalid="true"]) {
|
|
101
|
+
border-color: var(--pk-red);
|
|
102
|
+
box-shadow: 0 0 0 3px color-mix(in oklab, var(--pk-red) 20%, transparent);
|
|
103
|
+
}
|
|
104
|
+
@media (prefers-color-scheme: light) {
|
|
105
|
+
:root[data-theme="system"] .pk-input-group { background: transparent; }
|
|
106
|
+
:root[data-theme="system"] .pk-input-group:has(:disabled) { background: color-mix(in oklab, var(--pk-input) 50%, transparent); }
|
|
107
|
+
:root[data-theme="system"] .pk-input-group:has([aria-invalid="true"]) {
|
|
108
|
+
border-color: var(--pk-red);
|
|
109
|
+
box-shadow: 0 0 0 3px color-mix(in oklab, var(--pk-red) 20%, transparent);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
module PhlexKit
|
|
2
|
-
#
|
|
3
|
-
#
|
|
2
|
+
# Addon inside an InputGroup — icons, text or small buttons. `align:` places
|
|
3
|
+
# it: :start / :end inline beside the control (shadcn's inline-start/-end),
|
|
4
|
+
# :block_start / :block_end as full-width header/footer rows (the group
|
|
5
|
+
# stacks into a column). See input_group.rb.
|
|
4
6
|
class InputGroupAddon < BaseComponent
|
|
5
|
-
ALIGNS = {
|
|
7
|
+
ALIGNS = {
|
|
8
|
+
start: "start",
|
|
9
|
+
end: "end",
|
|
10
|
+
block_start: "block-start",
|
|
11
|
+
block_end: "block-end"
|
|
12
|
+
}.freeze
|
|
6
13
|
|
|
7
14
|
def initialize(align: :start, **attrs)
|
|
8
15
|
@align = align.to_sym
|
|
@@ -10,7 +17,7 @@ module PhlexKit
|
|
|
10
17
|
end
|
|
11
18
|
|
|
12
19
|
def view_template(&)
|
|
13
|
-
div(**mix({ class: "pk-input-group-addon #{ALIGNS.fetch(@align)}" }, @attrs), &)
|
|
20
|
+
div(**mix({ class: "pk-input-group-addon #{ALIGNS.fetch(@align)}", role: "group" }, @attrs), &)
|
|
14
21
|
end
|
|
15
22
|
end
|
|
16
23
|
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
module PhlexKit
|
|
2
|
+
# Small button inside an InputGroupAddon, ported from shadcn/ui's
|
|
3
|
+
# InputGroupButton — a ghost xs Button tuned to sit flush in the shell
|
|
4
|
+
# (tighter radius, no shadow). Same knobs as Button (variant:/size:/icon:),
|
|
5
|
+
# defaulting to variant: :ghost, size: :xs. See input_group.rb.
|
|
6
|
+
class InputGroupButton < BaseComponent
|
|
7
|
+
def initialize(variant: :ghost, size: :xs, icon: false, **attrs)
|
|
8
|
+
@variant = variant
|
|
9
|
+
@size = size
|
|
10
|
+
@icon = icon
|
|
11
|
+
@attrs = attrs
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def view_template(&block)
|
|
15
|
+
render Button.new(variant: @variant, size: @size, icon: @icon, **mix({ class: "pk-input-group-button" }, @attrs), &block)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
/* Co-located with input_otp.rb —
|
|
2
|
-
(shared borders, outer corners rounded), ring on the active cell
|
|
1
|
+
/* Co-located with input_otp.rb — PhlexKit::InputOTP, shadcn parity: joined
|
|
2
|
+
cells (shared borders, outer corners rounded), ring on the active cell,
|
|
3
|
+
nova metrics (size-8 slots, ring-colored focus, theme-forked fill,
|
|
4
|
+
invalid states on slot + group). */
|
|
3
5
|
.pk-input-otp { display: flex; align-items: center; gap: .5rem; }
|
|
4
6
|
.pk-input-otp-group { display: flex; align-items: center; }
|
|
5
7
|
.pk-input-otp-slot {
|
|
6
|
-
width:
|
|
7
|
-
height:
|
|
8
|
+
width: 2rem;
|
|
9
|
+
height: 2rem;
|
|
8
10
|
border: 1px solid var(--pk-input);
|
|
9
11
|
background: color-mix(in oklab, var(--pk-input) 30%, transparent);
|
|
10
12
|
color: var(--pk-text);
|
|
@@ -26,7 +28,29 @@
|
|
|
26
28
|
.pk-input-otp-slot:focus {
|
|
27
29
|
position: relative;
|
|
28
30
|
z-index: 1;
|
|
29
|
-
border-color: var(--pk-
|
|
31
|
+
border-color: var(--pk-ring);
|
|
30
32
|
box-shadow: 0 0 0 3px color-mix(in oklab, var(--pk-ring) 50%, transparent);
|
|
31
33
|
}
|
|
34
|
+
.pk-input-otp-slot[aria-invalid="true"] { border-color: var(--pk-red); }
|
|
35
|
+
.pk-input-otp-slot[aria-invalid="true"]:focus {
|
|
36
|
+
border-color: var(--pk-red);
|
|
37
|
+
box-shadow: 0 0 0 3px color-mix(in oklab, var(--pk-red) 40%, transparent);
|
|
38
|
+
}
|
|
39
|
+
.pk-input-otp-group:has([aria-invalid="true"]) {
|
|
40
|
+
border-radius: var(--pk-radius);
|
|
41
|
+
box-shadow: 0 0 0 3px color-mix(in oklab, var(--pk-red) 40%, transparent);
|
|
42
|
+
}
|
|
43
|
+
.pk-input-otp-slot:disabled { cursor: not-allowed; opacity: .5; }
|
|
44
|
+
:root[data-theme="light"] .pk-input-otp-slot { background: transparent; }
|
|
45
|
+
:root[data-theme="light"] .pk-input-otp-slot[aria-invalid="true"]:focus,
|
|
46
|
+
:root[data-theme="light"] .pk-input-otp-group:has([aria-invalid="true"]) {
|
|
47
|
+
box-shadow: 0 0 0 3px color-mix(in oklab, var(--pk-red) 20%, transparent);
|
|
48
|
+
}
|
|
49
|
+
@media (prefers-color-scheme: light) {
|
|
50
|
+
:root[data-theme="system"] .pk-input-otp-slot { background: transparent; }
|
|
51
|
+
:root[data-theme="system"] .pk-input-otp-slot[aria-invalid="true"]:focus,
|
|
52
|
+
:root[data-theme="system"] .pk-input-otp-group:has([aria-invalid="true"]) {
|
|
53
|
+
box-shadow: 0 0 0 3px color-mix(in oklab, var(--pk-red) 20%, transparent);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
32
56
|
.pk-input-otp-separator { color: var(--pk-muted); }
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
/* Co-located with item.rb —
|
|
1
|
+
/* Co-located with item.rb — PhlexKit::Item family, matched to shadcn/ui's
|
|
2
|
+
current item (nova): rounded-lg rows, sizes (sm keeps default paddings per
|
|
3
|
+
nova; xs tightens), media variants (icon glyph / square image thumb sized
|
|
4
|
+
by the item size), media nudges down beside a description, link items
|
|
5
|
+
(href:) hover-fill, Header stacks the item into a column. */
|
|
2
6
|
.pk-item {
|
|
3
7
|
display: flex;
|
|
4
8
|
align-items: center;
|
|
@@ -6,27 +10,71 @@
|
|
|
6
10
|
width: 100%;
|
|
7
11
|
border: 1px solid transparent;
|
|
8
12
|
border-radius: var(--pk-radius);
|
|
9
|
-
padding: .625rem .
|
|
13
|
+
padding: .625rem .75rem;
|
|
10
14
|
font-size: .875rem;
|
|
15
|
+
color: inherit;
|
|
16
|
+
text-decoration: none;
|
|
11
17
|
}
|
|
18
|
+
a.pk-item:hover { background: var(--pk-surface-2); }
|
|
12
19
|
.pk-item.outline { border-color: var(--pk-border); }
|
|
13
|
-
.pk-item.muted { background: var(--pk-surface-2); }
|
|
20
|
+
.pk-item.muted { background: color-mix(in oklab, var(--pk-surface-2) 50%, transparent); }
|
|
21
|
+
.pk-item.xs { gap: .5rem; padding: .5rem .625rem; }
|
|
22
|
+
.pk-item:has(> .pk-item-header),
|
|
23
|
+
.pk-item:has(> .pk-item-footer) { flex-direction: column; align-items: stretch; }
|
|
24
|
+
|
|
14
25
|
.pk-item-media {
|
|
15
26
|
display: flex;
|
|
16
27
|
align-items: center;
|
|
17
28
|
justify-content: center;
|
|
29
|
+
gap: .5rem;
|
|
18
30
|
flex: none;
|
|
19
31
|
color: var(--pk-muted);
|
|
20
32
|
}
|
|
21
|
-
.pk-item-media
|
|
22
|
-
|
|
23
|
-
|
|
33
|
+
.pk-item:has(.pk-item-description) > .pk-item-media {
|
|
34
|
+
transform: translateY(.125rem);
|
|
35
|
+
align-self: flex-start;
|
|
36
|
+
}
|
|
37
|
+
.pk-item-media.icon svg:not([class*="size-"]) { width: 1rem; height: 1rem; }
|
|
38
|
+
.pk-item-media.image {
|
|
39
|
+
width: 2.5rem;
|
|
40
|
+
height: 2.5rem;
|
|
41
|
+
overflow: hidden;
|
|
42
|
+
border-radius: calc(var(--pk-radius) - 4px);
|
|
43
|
+
}
|
|
44
|
+
.pk-item.sm .pk-item-media.image { width: 2rem; height: 2rem; }
|
|
45
|
+
.pk-item.xs .pk-item-media.image { width: 1.5rem; height: 1.5rem; }
|
|
46
|
+
.pk-item-media.image img { width: 100%; height: 100%; object-fit: cover; }
|
|
47
|
+
|
|
48
|
+
.pk-item-content { display: flex; flex: 1; flex-direction: column; gap: .25rem; min-width: 0; }
|
|
49
|
+
.pk-item.xs .pk-item-content { gap: 0; }
|
|
50
|
+
.pk-item-title {
|
|
51
|
+
display: flex;
|
|
52
|
+
align-items: center;
|
|
53
|
+
gap: .5rem;
|
|
54
|
+
font-weight: 500;
|
|
55
|
+
font-size: .875rem;
|
|
56
|
+
line-height: 1.375;
|
|
57
|
+
text-underline-offset: 4px;
|
|
58
|
+
}
|
|
24
59
|
.pk-item-description {
|
|
25
60
|
color: var(--pk-muted);
|
|
26
61
|
font-size: .875rem;
|
|
27
|
-
line-height: 1.
|
|
62
|
+
line-height: 1.5;
|
|
63
|
+
text-align: left;
|
|
28
64
|
overflow: hidden;
|
|
29
65
|
text-overflow: ellipsis;
|
|
30
66
|
}
|
|
67
|
+
.pk-item.xs .pk-item-description { font-size: .75rem; }
|
|
31
68
|
.pk-item-actions { display: flex; align-items: center; gap: .5rem; flex: none; }
|
|
32
|
-
.pk-item-
|
|
69
|
+
.pk-item-header,
|
|
70
|
+
.pk-item-footer {
|
|
71
|
+
display: flex;
|
|
72
|
+
align-items: center;
|
|
73
|
+
justify-content: space-between;
|
|
74
|
+
gap: .5rem;
|
|
75
|
+
width: 100%;
|
|
76
|
+
}
|
|
77
|
+
.pk-item-separator { height: 1px; margin-block: .5rem; background: var(--pk-border); }
|
|
78
|
+
.pk-item-group { display: flex; flex-direction: column; width: 100%; gap: 1rem; }
|
|
79
|
+
.pk-item-group:has(.pk-item.sm) { gap: .625rem; }
|
|
80
|
+
.pk-item-group:has(.pk-item.xs) { gap: .5rem; }
|
|
@@ -1,18 +1,28 @@
|
|
|
1
1
|
module PhlexKit
|
|
2
2
|
# List row, ported from shadcn/ui's Item: a flex row of ItemMedia +
|
|
3
3
|
# ItemContent(ItemTitle + ItemDescription) + ItemActions, optionally grouped
|
|
4
|
-
# in an ItemGroup
|
|
4
|
+
# in an ItemGroup (with ItemSeparator) or stacked under an ItemHeader.
|
|
5
|
+
# variant :outline draws a bordered card row; `size:` tightens (:sm/:xs);
|
|
6
|
+
# `href:` renders an <a> (their asChild link item — hover fills apply).
|
|
5
7
|
# `.pk-item*` (item.css).
|
|
6
8
|
class Item < BaseComponent
|
|
7
9
|
VARIANTS = { default: nil, outline: "outline", muted: "muted" }.freeze
|
|
10
|
+
SIZES = { md: nil, sm: "sm", xs: "xs" }.freeze
|
|
8
11
|
|
|
9
|
-
def initialize(variant: :default, **attrs)
|
|
12
|
+
def initialize(variant: :default, size: :md, href: nil, **attrs)
|
|
10
13
|
@variant = variant.to_sym
|
|
14
|
+
@size = size.to_sym
|
|
15
|
+
@href = href
|
|
11
16
|
@attrs = attrs
|
|
12
17
|
end
|
|
13
18
|
|
|
14
19
|
def view_template(&)
|
|
15
|
-
|
|
20
|
+
classes = [ "pk-item", VARIANTS.fetch(@variant), SIZES.fetch(@size) ].compact.join(" ")
|
|
21
|
+
if @href
|
|
22
|
+
a(**mix({ class: classes, href: @href }, @attrs), &)
|
|
23
|
+
else
|
|
24
|
+
div(**mix({ class: classes }, @attrs), &)
|
|
25
|
+
end
|
|
16
26
|
end
|
|
17
27
|
end
|
|
18
28
|
end
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
module PhlexKit
|
|
2
|
+
# Full-width footer band of an Item. Ported from shadcn/ui's ItemFooter.
|
|
3
|
+
# See item.rb.
|
|
4
|
+
class ItemFooter < BaseComponent
|
|
5
|
+
def initialize(**attrs) = (@attrs = attrs)
|
|
6
|
+
def view_template(&) = div(**mix({ class: "pk-item-footer" }, @attrs), &)
|
|
7
|
+
end
|
|
8
|
+
end
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
module PhlexKit
|
|
2
|
+
# Full-width header band of an Item (e.g. a cover image) — the item stacks
|
|
3
|
+
# into a column when present. Ported from shadcn/ui's ItemHeader. See item.rb.
|
|
4
|
+
class ItemHeader < BaseComponent
|
|
5
|
+
def initialize(**attrs) = (@attrs = attrs)
|
|
6
|
+
def view_template(&) = div(**mix({ class: "pk-item-header" }, @attrs), &)
|
|
7
|
+
end
|
|
8
|
+
end
|
|
@@ -1,7 +1,17 @@
|
|
|
1
1
|
module PhlexKit
|
|
2
|
-
# Leading
|
|
2
|
+
# Leading media slot of an Item. variant :icon sizes a glyph; :image crops a
|
|
3
|
+
# square thumbnail (sized by the item's size modifier); :default is bare.
|
|
4
|
+
# See item.rb.
|
|
3
5
|
class ItemMedia < BaseComponent
|
|
4
|
-
|
|
5
|
-
|
|
6
|
+
VARIANTS = { default: nil, icon: "icon", image: "image" }.freeze
|
|
7
|
+
|
|
8
|
+
def initialize(variant: :default, **attrs)
|
|
9
|
+
@variant = variant.to_sym
|
|
10
|
+
@attrs = attrs
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def view_template(&)
|
|
14
|
+
div(**mix({ class: [ "pk-item-media", VARIANTS.fetch(@variant) ].compact.join(" ") }, @attrs), &)
|
|
15
|
+
end
|
|
6
16
|
end
|
|
7
17
|
end
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
module PhlexKit
|
|
2
|
+
# Hairline between Items in an ItemGroup, ported from shadcn/ui's
|
|
3
|
+
# ItemSeparator. See item.rb.
|
|
4
|
+
class ItemSeparator < BaseComponent
|
|
5
|
+
def initialize(**attrs) = (@attrs = attrs)
|
|
6
|
+
def view_template
|
|
7
|
+
div(**mix({ class: "pk-item-separator", role: "separator", aria: { hidden: true } }, @attrs))
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
end
|
|
@@ -15,3 +15,13 @@
|
|
|
15
15
|
font-weight: 500;
|
|
16
16
|
}
|
|
17
17
|
.pk-kbd-group { display: inline-flex; align-items: center; gap: .25rem; font-family: inherit; }
|
|
18
|
+
.pk-kbd svg:not([class*="size-"]) { width: .75rem; height: .75rem; }
|
|
19
|
+
/* Inside an (inverted) tooltip the chip tints from the page background. */
|
|
20
|
+
.pk-tooltip-content .pk-kbd {
|
|
21
|
+
background: color-mix(in oklab, var(--pk-bg) 10%, transparent);
|
|
22
|
+
color: var(--pk-bg);
|
|
23
|
+
}
|
|
24
|
+
:root[data-theme="light"] .pk-tooltip-content .pk-kbd { background: color-mix(in oklab, var(--pk-bg) 20%, transparent); }
|
|
25
|
+
@media (prefers-color-scheme: light) {
|
|
26
|
+
:root[data-theme="system"] .pk-tooltip-content .pk-kbd { background: color-mix(in oklab, var(--pk-bg) 20%, transparent); }
|
|
27
|
+
}
|