@kouji-ui/components 0.9.2 → 0.9.4

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 (73) hide show
  1. package/fesm2022/kouji-ui-components.mjs +49 -3
  2. package/fesm2022/kouji-ui-components.mjs.map +1 -1
  3. package/package.json +1 -1
  4. package/src/accordion/accordion.css +68 -0
  5. package/src/action-sheet/action-sheet.css +79 -0
  6. package/src/alert/alert.css +133 -0
  7. package/src/avatar/avatar.css +93 -0
  8. package/src/badge/badge.css +59 -0
  9. package/src/breadcrumb/breadcrumb.css +145 -0
  10. package/src/button/button.css +295 -0
  11. package/src/button-group/button-group.css +114 -0
  12. package/src/calendar/calendar.css +129 -0
  13. package/src/card/card.css +120 -0
  14. package/src/carousel/carousel.css +181 -0
  15. package/src/cascade-select/cascade-select.css +120 -0
  16. package/src/chart/chart.css +26 -0
  17. package/src/chat/chat-ai.css +352 -0
  18. package/src/chat/chat.css +231 -0
  19. package/src/checkbox/checkbox.css +74 -0
  20. package/src/color-picker/color-picker.css +183 -0
  21. package/src/combobox/combobox.css +101 -0
  22. package/src/command-palette/command-palette.css +234 -0
  23. package/src/confirm-popup/confirm-popup.css +47 -0
  24. package/src/date-picker/date-picker.css +61 -0
  25. package/src/date-range-presets/date-range-presets.css +49 -0
  26. package/src/datetime-picker/datetime-picker.css +84 -0
  27. package/src/dialog/dialog.css +56 -0
  28. package/src/direction-toggle/direction-toggle.css +55 -0
  29. package/src/divider/divider.css +93 -0
  30. package/src/drawer/drawer.css +130 -0
  31. package/src/dropdown-menu/dropdown-menu.css +108 -0
  32. package/src/editor/editor.css +140 -0
  33. package/src/empty-state/empty-state.css +114 -0
  34. package/src/field/field.css +148 -0
  35. package/src/file-upload/file-upload.css +207 -0
  36. package/src/form/form.css +63 -0
  37. package/src/input/input.css +70 -0
  38. package/src/input-group/input-group.css +125 -0
  39. package/src/input-mask/input-mask.css +39 -0
  40. package/src/input-otp/input-otp.css +87 -0
  41. package/src/kbd/kbd.css +67 -0
  42. package/src/link/link.css +104 -0
  43. package/src/list/list.css +166 -0
  44. package/src/menubar/menubar.css +62 -0
  45. package/src/number-input/number-input.css +115 -0
  46. package/src/overflow/overflow.css +43 -0
  47. package/src/overlay/overlay.css +45 -0
  48. package/src/overlay-badge/overlay-badge.css +118 -0
  49. package/src/pagination/pagination.css +146 -0
  50. package/src/password-input/password-input.css +155 -0
  51. package/src/popover/popover.css +71 -0
  52. package/src/progress-bar/progress-bar.css +113 -0
  53. package/src/radio/radio.css +58 -0
  54. package/src/rich-text/rich-text-editor.css +351 -0
  55. package/src/select/select.css +91 -0
  56. package/src/sheet/sheet.css +121 -0
  57. package/src/skeleton/skeleton.css +111 -0
  58. package/src/skip-link/skip-link.css +58 -0
  59. package/src/slider/slider.css +172 -0
  60. package/src/speed-dial/speed-dial.css +137 -0
  61. package/src/spinner/spinner.css +161 -0
  62. package/src/stepper/stepper.css +147 -0
  63. package/src/table/table-filters/filters.css +53 -0
  64. package/src/table/table.css +509 -0
  65. package/src/tabs/tabs.css +162 -0
  66. package/src/tag/tag.css +183 -0
  67. package/src/textarea/textarea.css +117 -0
  68. package/src/time-picker/time-picker.css +126 -0
  69. package/src/toast/toast.css +115 -0
  70. package/src/toggle/toggle.css +123 -0
  71. package/src/tooltip/tooltip.css +61 -0
  72. package/src/tree-select/tree-select.css +220 -0
  73. package/types/kouji-ui-components.d.ts +19 -0
@@ -0,0 +1,104 @@
1
+ /* ─────────────────────────────────────────────────────────────
2
+ .kj-link — component-layer tokens + structural CSS
3
+ Tokens default to shared-layer values from @kouji-ui/themes.
4
+ Variants / sizes / states retarget the component tokens; the
5
+ structural rules below are the only place raw var(--kj-link-*)
6
+ is read.
7
+ ──────────────────────────────────────────────────────────── */
8
+
9
+ @layer kj.component {
10
+ .kj-link {
11
+ /* component knobs — themes / users can override any of these */
12
+ --kj-link-fg: var(--kj-fg-primary);
13
+ --kj-link-hover-fg: color-mix(in oklch, var(--kj-fg-primary), black 8%);
14
+ --kj-link-visited-fg: var(--kj-fg-accent);
15
+ --kj-link-decoration: underline;
16
+ --kj-link-underline-offset: 3px;
17
+ --kj-link-font-size: inherit;
18
+ --kj-link-radius: var(--kj-radius-field, 0.25rem);
19
+
20
+ /* the only place raw var(--kj-link-*) is read */
21
+ color: var(--kj-link-fg);
22
+ text-decoration: var(--kj-link-decoration);
23
+ text-underline-offset: var(--kj-link-underline-offset);
24
+ font-size: var(--kj-link-font-size);
25
+ border-radius: var(--kj-link-radius);
26
+ cursor: pointer;
27
+ transition: color var(--kj-transition), text-decoration-color var(--kj-transition);
28
+ }
29
+
30
+ .kj-link:hover {
31
+ color: var(--kj-link-hover-fg);
32
+ }
33
+
34
+ .kj-link:visited {
35
+ color: var(--kj-link-visited-fg);
36
+ }
37
+
38
+ .kj-link:focus-visible {
39
+ outline: 2px solid var(--kj-fg-primary);
40
+ outline-offset: 2px;
41
+ }
42
+
43
+ /* ── Underline modes ────────────────────────────────────────── */
44
+ .kj-link[data-underline="always"] { --kj-link-decoration: underline; }
45
+ .kj-link[data-underline="hover"] { --kj-link-decoration: none; }
46
+ .kj-link[data-underline="hover"]:hover,
47
+ .kj-link[data-underline="hover"]:focus-visible { --kj-link-decoration: underline; }
48
+ .kj-link[data-underline="none"] { --kj-link-decoration: none; }
49
+
50
+ /* ── Variants ───────────────────────────────────────────────── */
51
+ .kj-link[data-variant="primary"] {
52
+ --kj-link-fg: var(--kj-fg-primary);
53
+ --kj-link-hover-fg: color-mix(in oklab, var(--kj-fg-primary) 80%, var(--kj-fg-default));
54
+ }
55
+
56
+ .kj-link[data-variant="secondary"] {
57
+ --kj-link-fg: var(--kj-fg-accent);
58
+ --kj-link-hover-fg: color-mix(in oklab, var(--kj-fg-accent) 80%, var(--kj-fg-default));
59
+ }
60
+
61
+ .kj-link[data-variant="muted"] {
62
+ --kj-link-fg: color-mix(in oklab, var(--kj-fg-default) 70%, transparent);
63
+ --kj-link-hover-fg: var(--kj-fg-default);
64
+ }
65
+
66
+ .kj-link[data-variant="destructive"] {
67
+ --kj-link-fg: var(--kj-fg-danger);
68
+ --kj-link-hover-fg: color-mix(in oklab, var(--kj-fg-danger) 80%, var(--kj-fg-default));
69
+ }
70
+
71
+ /* ── Sizes ──────────────────────────────────────────────────── */
72
+ .kj-link[data-size="sm"] { --kj-link-font-size: 0.8125rem; }
73
+ .kj-link[data-size="md"] { --kj-link-font-size: 0.9375rem; }
74
+ .kj-link[data-size="lg"] { --kj-link-font-size: 1.0625rem; }
75
+ .kj-link[data-size="inherit"] { --kj-link-font-size: inherit; }
76
+
77
+ /* ── External-link icon (sighted-user story) ───────────────── */
78
+ .kj-link[data-external="true"]::after {
79
+ content: "";
80
+ display: inline-block;
81
+ width: 0.75em;
82
+ height: 0.75em;
83
+ margin-inline-start: 0.25em;
84
+ background-color: currentColor;
85
+ /* Mask is a tiny "arrow leaving box" glyph; aria-hidden by virtue of CSS. */
86
+ -webkit-mask: var(
87
+ --kj-link-external-icon,
88
+ url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><path fill='none' stroke='currentColor' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round' d='M9 3h4v4M13 3 7 9M11 9v4H3V5h4'/></svg>")
89
+ ) center / contain no-repeat;
90
+ mask: var(
91
+ --kj-link-external-icon,
92
+ url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><path fill='none' stroke='currentColor' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round' d='M9 3h4v4M13 3 7 9M11 9v4H3V5h4'/></svg>")
93
+ ) center / contain no-repeat;
94
+ vertical-align: -0.05em;
95
+ }
96
+
97
+ /* ── Disabled state ─────────────────────────────────────────── */
98
+ .kj-link[aria-disabled="true"],
99
+ .kj-link[data-disabled] {
100
+ opacity: 0.5;
101
+ cursor: not-allowed;
102
+ text-decoration: none;
103
+ }
104
+ }
@@ -0,0 +1,166 @@
1
+ /* ─────────────────────────────────────────────────────────────
2
+ .kj-list — component-layer tokens + structural CSS
3
+ The list root carries layout (flex direction, gap) and reads
4
+ `data-orientation` / `data-divided` / `data-hoverable` from the
5
+ composed `KjList` directive to flip variants. Per-row chrome is
6
+ driven by `data-active` / `data-disabled` on `<kj-list-item>`.
7
+ ──────────────────────────────────────────────────────────── */
8
+
9
+ @layer kj.component {
10
+ .kj-list {
11
+ --kj-list-bg: transparent;
12
+ --kj-list-fg: var(--kj-fg-default);
13
+ --kj-list-border-color: var(--kj-border-default);
14
+ --kj-list-radius: var(--kj-radius-box);
15
+ --kj-list-row-gap: 0;
16
+ --kj-list-row-padding-x: var(--kj-space-md);
17
+ --kj-list-row-padding-y: var(--kj-space-sm);
18
+ --kj-list-row-radius: var(--kj-radius-field);
19
+ --kj-list-row-min-h: 2.75rem; /* 44px — WCAG 2.5.5 */
20
+
21
+ display: flex;
22
+ flex-direction: column;
23
+ gap: var(--kj-list-row-gap);
24
+ background: var(--kj-list-bg);
25
+ color: var(--kj-list-fg);
26
+ border-radius: var(--kj-list-radius);
27
+ margin: 0;
28
+ padding: 0;
29
+ list-style: none;
30
+ }
31
+
32
+ /* Horizontal axis flips flex-direction. The `data-orientation` mirror is
33
+ emitted by `KjList` so themes do not need the JS-level orientation. */
34
+ .kj-list[data-orientation='horizontal'] {
35
+ flex-direction: row;
36
+ flex-wrap: wrap;
37
+ }
38
+
39
+ /* Sizes — drive row density via the row-padding / min-height tokens. */
40
+ .kj-list[data-size='xs'] {
41
+ --kj-list-row-padding-y: 0.125rem;
42
+ --kj-list-row-padding-x: var(--kj-space-sm);
43
+ --kj-list-row-min-h: 1.5rem;
44
+ }
45
+ .kj-list[data-size='sm'] {
46
+ --kj-list-row-padding-y: 0.25rem;
47
+ --kj-list-row-padding-x: var(--kj-space-sm);
48
+ --kj-list-row-min-h: 2rem;
49
+ }
50
+ .kj-list[data-size='lg'] {
51
+ --kj-list-row-padding-y: var(--kj-space-md);
52
+ --kj-list-row-padding-x: var(--kj-space-lg);
53
+ --kj-list-row-min-h: 3rem;
54
+ }
55
+
56
+ /* Variants — chrome on the container itself. */
57
+ .kj-list[data-variant='bordered'] {
58
+ border: var(--kj-border) solid var(--kj-list-border-color);
59
+ overflow: hidden;
60
+ }
61
+ .kj-list[data-variant='flat'] {
62
+ border: 0;
63
+ --kj-list-bg: transparent;
64
+ }
65
+
66
+ /* Per-row layout. Rows are flex containers so consumers can drop in
67
+ leading icons / trailing badges without juggling spans. */
68
+ .kj-list-item {
69
+ display: flex;
70
+ align-items: center;
71
+ gap: var(--kj-space-sm);
72
+ padding: var(--kj-list-row-padding-y) var(--kj-list-row-padding-x);
73
+ min-height: var(--kj-list-row-min-h);
74
+ border-radius: var(--kj-list-row-radius);
75
+ color: inherit;
76
+ /* Transition only state-tied properties so the active / hover flip is
77
+ buttery without animating layout. */
78
+ transition: background-color var(--kj-transition),
79
+ color var(--kj-transition),
80
+ opacity var(--kj-transition);
81
+ }
82
+
83
+ /* One projected link/button: move row padding onto it so the clickable box
84
+ matches the full row (padding on the host sat outside the hit target).
85
+ Use `nth-child(1 of …)` / `nth-last-child(1 of …)` so whitespace text
86
+ nodes between the host and the control do not break `:only-child`. */
87
+ .kj-list-item:has(
88
+ > :where(a, button):nth-child(1 of :where(a, button)):nth-last-child(1 of :where(a, button))
89
+ ) {
90
+ padding: 0;
91
+ align-items: stretch;
92
+ min-height: 0;
93
+ }
94
+
95
+ /* Divided rows — between-row border via `:not(:last-child)` so the final
96
+ row stays clean. The `data-divided` mirror lives on the root. */
97
+ .kj-list[data-divided] .kj-list-item {
98
+ border-radius: 0;
99
+ }
100
+ .kj-list[data-divided][data-orientation='vertical'] .kj-list-item:not(:last-child) {
101
+ border-bottom: var(--kj-border) solid var(--kj-list-border-color);
102
+ }
103
+ .kj-list[data-divided][data-orientation='horizontal'] .kj-list-item:not(:last-child) {
104
+ border-right: var(--kj-border) solid var(--kj-list-border-color);
105
+ }
106
+
107
+ /* Hoverable rows. Cascade order matters: the `:hover` rule comes BEFORE
108
+ the `[data-active]` rule so the active row wins when both are true. */
109
+ .kj-list[data-hoverable] .kj-list-item:hover {
110
+ background: color-mix(in oklab, var(--kj-fg-default) 6%, transparent);
111
+ cursor: pointer;
112
+ }
113
+
114
+ /* Active row — current location in a nav. Painted via background tint plus
115
+ a left bar (≥3:1 against background per WCAG 1.4.11). */
116
+ .kj-list-item[data-active] {
117
+ background: color-mix(in oklab, var(--kj-bg-primary) 12%, transparent);
118
+ color: var(--kj-fg-primary);
119
+ box-shadow: inset 3px 0 0 0 var(--kj-bg-primary);
120
+ }
121
+ .kj-list[data-orientation='horizontal'] .kj-list-item[data-active] {
122
+ box-shadow: inset 0 -3px 0 0 var(--kj-bg-primary);
123
+ }
124
+
125
+ /* Disabled row — dim the chrome and any projected interactive child.
126
+ Keyboard reachability is the projected child's responsibility. */
127
+ .kj-list-item[data-disabled] {
128
+ opacity: 0.45;
129
+ cursor: not-allowed;
130
+ }
131
+ .kj-list-item[data-disabled] :where(a, button) {
132
+ pointer-events: none;
133
+ }
134
+
135
+ /* Shared styles for interactive children */
136
+ .kj-list-item > :where(a, button) {
137
+ display: flex;
138
+ align-items: center;
139
+ gap: var(--kj-space-sm);
140
+ color: inherit;
141
+ text-decoration: none;
142
+ background: none;
143
+ border: 0;
144
+ padding: 0;
145
+ font: inherit;
146
+ cursor: pointer;
147
+ }
148
+
149
+ .kj-list-item:has(
150
+ > :where(a, button):nth-child(1 of :where(a, button)):nth-last-child(1 of :where(a, button))
151
+ )
152
+ > :where(a, button) {
153
+ flex: 1 1 auto;
154
+ align-self: stretch;
155
+ min-width: 0;
156
+ min-height: var(--kj-list-row-min-h);
157
+ padding: var(--kj-list-row-padding-y) var(--kj-list-row-padding-x);
158
+ box-sizing: border-box;
159
+ }
160
+
161
+ .kj-list-item > :where(a, button):focus-visible {
162
+ outline: 2px solid var(--kj-fg-primary);
163
+ outline-offset: 2px;
164
+ border-radius: var(--kj-list-row-radius);
165
+ }
166
+ }
@@ -0,0 +1,62 @@
1
+ @layer kj.component {
2
+ .kj-menubar {
3
+ --kj-menubar-bg: var(--kj-bg-body);
4
+ --kj-menubar-fg: var(--kj-fg-default);
5
+ --kj-menubar-border-color: var(--kj-border-default);
6
+ --kj-menubar-radius: var(--kj-radius-box);
7
+ --kj-menubar-padding: var(--kj-space-2xs);
8
+ --kj-menubar-gap: var(--kj-space-2xs);
9
+
10
+ display: flex;
11
+ flex-direction: row;
12
+ align-items: stretch;
13
+ gap: var(--kj-menubar-gap);
14
+ box-sizing: border-box;
15
+ background: var(--kj-menubar-bg);
16
+ color: var(--kj-menubar-fg);
17
+ border: var(--kj-border) solid var(--kj-menubar-border-color);
18
+ border-radius: var(--kj-menubar-radius);
19
+ padding: var(--kj-menubar-padding);
20
+ font: 0.875rem / 1.5 var(--kj-font-sans);
21
+ }
22
+
23
+ .kj-menubar-item {
24
+ --kj-menubar-item-padding-x: var(--kj-space-md);
25
+ --kj-menubar-item-padding-y: var(--kj-space-xs);
26
+ --kj-menubar-item-min-height: 2.75rem; /* 44px AAA touch target */
27
+ --kj-menubar-item-radius: var(--kj-radius-field);
28
+ --kj-menubar-item-hover-bg: var(--kj-bg-surface);
29
+ --kj-menubar-item-active-bg: var(--kj-bg-elevated);
30
+
31
+ display: inline-flex;
32
+ align-items: center;
33
+ justify-content: center;
34
+ min-height: var(--kj-menubar-item-min-height);
35
+ box-sizing: border-box;
36
+ padding: var(--kj-menubar-item-padding-y) var(--kj-menubar-item-padding-x);
37
+ margin: 0;
38
+ background: transparent;
39
+ color: inherit;
40
+ border: none;
41
+ border-radius: var(--kj-menubar-item-radius);
42
+ font: inherit;
43
+ line-height: 1;
44
+ cursor: pointer;
45
+ user-select: none;
46
+ }
47
+
48
+ .kj-menubar-item:hover:not([aria-disabled="true"]) {
49
+ background: var(--kj-menubar-item-hover-bg);
50
+ }
51
+ .kj-menubar-item:focus-visible {
52
+ outline: 2px solid var(--kj-fg-primary);
53
+ outline-offset: -2px;
54
+ }
55
+ .kj-menubar-item[data-state="active"] {
56
+ background: var(--kj-menubar-item-active-bg);
57
+ }
58
+ .kj-menubar-item[aria-disabled="true"] {
59
+ cursor: not-allowed;
60
+ opacity: 0.5;
61
+ }
62
+ }
@@ -0,0 +1,115 @@
1
+ /* ─────────────────────────────────────────────────────────────
2
+ .kj-number-input — wrapper around KjNumberInput + KjNumberStepper.
3
+
4
+ Tokens default to shared layer values; the stepper buttons live as
5
+ sibling buttons inside the group. Bounds-aware aria-disabled is
6
+ reflected from the directive — CSS only reads the attribute.
7
+ ──────────────────────────────────────────────────────────── */
8
+
9
+ @layer kj.component {
10
+ .kj-number-input {
11
+ --kj-number-input-bg: var(--kj-bg-field);
12
+ --kj-number-input-fg: var(--kj-fg-default);
13
+ --kj-number-input-border-color: var(--kj-border-default);
14
+ --kj-number-input-border-width: var(--kj-border);
15
+ --kj-number-input-radius: var(--kj-radius-field);
16
+ --kj-number-input-padding-x: var(--kj-space-md);
17
+ --kj-number-input-font: var(--kj-font-sans);
18
+ --kj-number-input-font-size: var(--kj-text-sm);
19
+ --kj-number-input-stepper-bg: var(--kj-bg-surface);
20
+ --kj-number-input-stepper-fg: var(--kj-fg-default);
21
+ --kj-number-input-stepper-size: 2.25rem;
22
+ /* Same height contract as .kj-input — keep form rows aligned. */
23
+ --kj-number-input-height: 2.25rem;
24
+
25
+ display: inline-flex;
26
+ align-items: stretch;
27
+ height: var(--kj-number-input-height);
28
+ background: var(--kj-number-input-bg);
29
+ color: var(--kj-number-input-fg);
30
+ border: var(--kj-number-input-border-width) solid var(--kj-number-input-border-color);
31
+ border-radius: var(--kj-number-input-radius);
32
+ overflow: hidden;
33
+ font: var(--kj-number-input-font-size) / 1.4 var(--kj-number-input-font);
34
+ transition: var(--kj-transition);
35
+ }
36
+ .kj-number-input[data-size="xs"] { --kj-number-input-height: 1.75rem; --kj-number-input-stepper-size: 1.75rem; --kj-number-input-font-size: 0.75rem; --kj-number-input-padding-x: var(--kj-space-sm); }
37
+ .kj-number-input[data-size="sm"] { --kj-number-input-height: 2rem; --kj-number-input-stepper-size: 2rem; --kj-number-input-font-size: 0.8125rem; }
38
+ .kj-number-input[data-size="lg"] { --kj-number-input-height: 2.75rem; --kj-number-input-stepper-size: 2.75rem; --kj-number-input-font-size: 1rem; }
39
+
40
+ /* At `xs` we hide the stepper buttons — at 28px the +/- glyphs become
41
+ too cramped to hit accurately, and the typical xs context (filter rows,
42
+ inline editors) doesn't want a stepper anyway. The keyboard contract
43
+ (Arrow/Page/Home/End) still works on the inner <input>. */
44
+ .kj-number-input[data-size="xs"] .kj-number-input__stepper { display: none; }
45
+ .kj-number-input[data-size="xs"] .kj-number-input__field { text-align: left; }
46
+
47
+ .kj-number-input:focus-within {
48
+ --kj-number-input-border-color: var(--kj-fg-primary);
49
+ outline: 2px solid var(--kj-fg-primary);
50
+ outline-offset: 2px;
51
+ }
52
+
53
+ .kj-number-input[data-invalid] {
54
+ --kj-number-input-border-color: var(--kj-fg-danger);
55
+ }
56
+
57
+ .kj-number-input[data-disabled] {
58
+ opacity: 0.55;
59
+ cursor: not-allowed;
60
+ }
61
+
62
+ .kj-number-input__field {
63
+ flex: 1 1 auto;
64
+ min-width: 0;
65
+ height: 100%;
66
+ background: transparent;
67
+ color: inherit;
68
+ border: 0;
69
+ padding: 0 var(--kj-number-input-padding-x);
70
+ font: inherit;
71
+ text-align: right;
72
+ }
73
+ .kj-number-input__field:focus { outline: none; }
74
+ .kj-number-input__field::placeholder { color: var(--kj-fg-muted); }
75
+
76
+ .kj-number-input__stepper {
77
+ display: inline-flex;
78
+ align-items: center;
79
+ justify-content: center;
80
+ min-width: var(--kj-number-input-stepper-size);
81
+ min-height: var(--kj-number-input-stepper-size);
82
+ background: var(--kj-number-input-stepper-bg);
83
+ color: var(--kj-number-input-stepper-fg);
84
+ border: 0;
85
+ cursor: pointer;
86
+ font: inherit;
87
+ line-height: 1;
88
+ transition: background var(--kj-transition), color var(--kj-transition), opacity var(--kj-transition);
89
+ }
90
+ .kj-number-input__stepper:hover {
91
+ background: color-mix(in oklab, var(--kj-fg-default) 8%, var(--kj-number-input-stepper-bg));
92
+ }
93
+ .kj-number-input__stepper[aria-disabled="true"] {
94
+ opacity: 0.4;
95
+ cursor: not-allowed;
96
+ pointer-events: none;
97
+ }
98
+
99
+ /* Flanking layout (default): decrement | input | increment */
100
+ .kj-number-input[data-stepper-layout="flanking"] .kj-number-input__stepper--down { order: -1; }
101
+ .kj-number-input[data-stepper-layout="flanking"] .kj-number-input__stepper--up { order: 1; }
102
+
103
+ /* Stacked layout: input on the left, up / down stacked vertically on the right. */
104
+ .kj-number-input[data-stepper-layout="stacked"] {
105
+ align-items: stretch;
106
+ }
107
+ .kj-number-input[data-stepper-layout="stacked"] .kj-number-input__steppers {
108
+ display: flex;
109
+ flex-direction: column;
110
+ }
111
+ .kj-number-input[data-stepper-layout="stacked"] .kj-number-input__stepper {
112
+ flex: 1 1 auto;
113
+ min-height: calc(var(--kj-number-input-stepper-size) / 2);
114
+ }
115
+ }
@@ -0,0 +1,43 @@
1
+ /* ─────────────────────────────────────────────────────────────
2
+ kj-overflow-panel — the "+N" chip's panel (tag list / avatar group).
3
+ The panel itself is a kj-popover-content; only the default label
4
+ list and the trigger chips' shared reset live here.
5
+ ──────────────────────────────────────────────────────────── */
6
+
7
+ @layer kj.component {
8
+ .kj-overflow-panel {
9
+ --kj-popover-padding-x: var(--kj-space-sm);
10
+ --kj-popover-padding-y: var(--kj-space-sm);
11
+ min-width: 10rem;
12
+ }
13
+
14
+ .kj-overflow-list {
15
+ display: flex;
16
+ flex-direction: column;
17
+ gap: var(--kj-space-xs);
18
+ margin: 0;
19
+ padding: 0;
20
+ list-style: none;
21
+ }
22
+ .kj-overflow-item {
23
+ padding: var(--kj-space-xs) var(--kj-space-sm);
24
+ font-size: var(--kj-text-sm);
25
+ line-height: 1.4;
26
+ color: var(--kj-fg-default);
27
+ white-space: nowrap;
28
+ overflow: hidden;
29
+ text-overflow: ellipsis;
30
+ }
31
+
32
+ /* The "+N" chip is a native <button> so it is focusable and reachable on
33
+ touch; strip the UA chrome, the group's own chip styles paint it. */
34
+ .kj-overflow-trigger {
35
+ appearance: none;
36
+ font: inherit;
37
+ cursor: pointer;
38
+ }
39
+ .kj-overflow-trigger:focus-visible {
40
+ outline: 2px solid var(--kj-bg-primary);
41
+ outline-offset: 2px;
42
+ }
43
+ }
@@ -0,0 +1,45 @@
1
+ /* ─────────────────────────────────────────────────────────────
2
+ @kouji-ui/components — overlay-family surface stylesheet
3
+ --------------------------------------------------------------
4
+ Register this file once in your build, next to the themes and
5
+ the core overlay primitive:
6
+
7
+ "styles": [
8
+ "node_modules/@kouji-ui/themes/src/index.css",
9
+ "node_modules/@kouji-ui/core/overlay/overlay.css",
10
+ "node_modules/@kouji-ui/components/src/overlay/overlay.css"
11
+ ]
12
+
13
+ WHY IT MUST BE REGISTERED. Every overlay panel in the kit is
14
+ rendered by a HEADLESS directive from `@kouji-ui/core` —
15
+ `<kj-popover-content>`, `<kj-dropdown-menu-content>`,
16
+ `<kj-tooltip-content>`, `<kj-dialog-content>`. Directives carry
17
+ no styles, and Angular injects a component's `styleUrl` only
18
+ when that component is INSTANTIATED. So a panel composed the
19
+ documented way (trigger directive + content directive, with no
20
+ `<kj-popover>`-style wrapper anywhere on the page) used to
21
+ paint with no background, no border and no shadow: the surface
22
+ CSS lived solely inside a wrapper component nobody rendered.
23
+ This stylesheet is that surface CSS, decoupled from the
24
+ wrappers — the wrapper components still `styleUrl` the same
25
+ files, so registering it is idempotent.
26
+
27
+ Kept in the SOURCE layout on purpose: `ng-packagr` copies every
28
+ stylesheet under `src` into the published package unchanged, so
29
+ these relative imports resolve identically from
30
+ `node_modules/@kouji-ui/components/src/overlay/overlay.css`.
31
+ The core primitive is NOT imported from here — CSS cannot reach
32
+ across packages by relative path once installed; register
33
+ `@kouji-ui/core/overlay/overlay.css` for the container /
34
+ wrapper / backdrop chrome.
35
+ ──────────────────────────────────────────────────────────── */
36
+
37
+ @import "../popover/popover.css";
38
+ @import "../tooltip/tooltip.css";
39
+ @import "../dropdown-menu/dropdown-menu.css";
40
+ @import "../dialog/dialog.css";
41
+ @import "../drawer/drawer.css";
42
+ @import "../toast/toast.css";
43
+ @import "../confirm-popup/confirm-popup.css";
44
+ @import "../sheet/sheet.css";
45
+ @import "../action-sheet/action-sheet.css";
@@ -0,0 +1,118 @@
1
+ @layer kj.component {
2
+ .kj-overlay-badge-anchor {
3
+ /* The anchor is also the positioning context. `position: relative` is
4
+ inline-style-bound by `KjOverlayBadge`; we add the inline-flex layout
5
+ here so projected anchors keep their inline rhythm. */
6
+ display: inline-flex;
7
+ vertical-align: middle;
8
+ }
9
+
10
+ .kj-overlay-badge {
11
+ /* Visual chrome inherited from <kj-badge>. We re-apply the same token
12
+ set here so the slot renders identically whether the consumer uses
13
+ <kj-overlay-badge> or projects a manual <span kjOverlayBadgeContent>. */
14
+ --kj-badge-bg: var(--kj-bg-primary);
15
+ --kj-badge-fg: var(--kj-fg-on-primary);
16
+ --kj-badge-border-color: transparent;
17
+ --kj-badge-radius: var(--kj-radius-selector);
18
+ --kj-badge-padding-x: var(--kj-space-sm);
19
+ --kj-badge-padding-y: 0.125rem;
20
+ --kj-badge-font-size: 0.6875rem;
21
+
22
+ display: inline-flex;
23
+ align-items: center;
24
+ justify-content: center;
25
+ min-width: 1.25rem;
26
+ height: 1.25rem;
27
+ background: var(--kj-badge-bg);
28
+ color: var(--kj-badge-fg);
29
+ border: var(--kj-border) solid var(--kj-badge-border-color);
30
+ border-radius: var(--kj-badge-radius);
31
+ padding: var(--kj-badge-padding-y) var(--kj-badge-padding-x);
32
+ font: var(--kj-badge-font-size) / 1 var(--kj-font-sans);
33
+ font-weight: 600;
34
+
35
+ /* 2px halo against the anchor for WCAG 1.4.11 non-text contrast — the
36
+ badge is a small element sitting on top of arbitrary surfaces. */
37
+ box-shadow: 0 0 0 2px var(--kj-bg-body);
38
+
39
+ /* Logical-property positioning so RTL flips for free. The translate
40
+ pulls the badge half-out of the anchor at each corner — keeps the
41
+ majority of the chip visible above an avatar's overflow mask, etc. */
42
+ inset-block-start: auto;
43
+ inset-block-end: auto;
44
+ inset-inline-start: auto;
45
+ inset-inline-end: auto;
46
+ }
47
+
48
+ /* Variant overrides — mirror <kj-badge>'s data-variant rules. */
49
+ .kj-overlay-badge[data-variant="secondary"] {
50
+ --kj-badge-bg: var(--kj-bg-surface);
51
+ --kj-badge-fg: var(--kj-fg-default);
52
+ }
53
+ .kj-overlay-badge[data-variant="destructive"] {
54
+ --kj-badge-bg: var(--kj-bg-danger);
55
+ --kj-badge-fg: var(--kj-fg-on-danger);
56
+ }
57
+ .kj-overlay-badge[data-variant="outline"] {
58
+ --kj-badge-bg: transparent;
59
+ --kj-badge-fg: var(--kj-fg-default);
60
+ --kj-badge-border-color: var(--kj-border-default);
61
+ }
62
+
63
+ /* Sizes. */
64
+ .kj-overlay-badge[data-size="sm"] {
65
+ min-width: 1rem;
66
+ height: 1rem;
67
+ --kj-badge-font-size: 0.625rem;
68
+ --kj-badge-padding-x: 0.25rem;
69
+ }
70
+ .kj-overlay-badge[data-size="lg"] {
71
+ min-width: 1.5rem;
72
+ height: 1.5rem;
73
+ --kj-badge-font-size: 0.75rem;
74
+ --kj-badge-padding-x: 0.5rem;
75
+ }
76
+
77
+ /* Dot mode — fixed 0.5rem circle, no padding, no min-width. */
78
+ .kj-overlay-badge[data-dot] {
79
+ min-width: 0;
80
+ width: 0.625rem;
81
+ height: 0.625rem;
82
+ padding: 0;
83
+ border-radius: 9999px;
84
+ }
85
+ .kj-overlay-badge[data-dot][data-size="sm"] { width: 0.5rem; height: 0.5rem; }
86
+ .kj-overlay-badge[data-dot][data-size="lg"] { width: 0.75rem; height: 0.75rem; }
87
+
88
+ /* Logical-corner placement. RTL flips automatically because we use
89
+ inline-start / inline-end. */
90
+ .kj-overlay-badge[data-position="top-end"] {
91
+ inset-block-start: 0;
92
+ inset-inline-end: 0;
93
+ transform: translate(50%, -50%);
94
+ }
95
+ .kj-overlay-badge[data-position="top-start"] {
96
+ inset-block-start: 0;
97
+ inset-inline-start: 0;
98
+ transform: translate(-50%, -50%);
99
+ }
100
+ .kj-overlay-badge[data-position="bottom-end"] {
101
+ inset-block-end: 0;
102
+ inset-inline-end: 0;
103
+ transform: translate(50%, 50%);
104
+ }
105
+ .kj-overlay-badge[data-position="bottom-start"] {
106
+ inset-block-end: 0;
107
+ inset-inline-start: 0;
108
+ transform: translate(-50%, 50%);
109
+ }
110
+
111
+ /* RTL inverts the inline-axis translation so the badge stays outside the
112
+ anchor's edge rather than tucking inward. Logical insets handle the
113
+ position itself; only the translate sign needs flipping. */
114
+ [dir="rtl"] .kj-overlay-badge[data-position="top-end"] { transform: translate(-50%, -50%); }
115
+ [dir="rtl"] .kj-overlay-badge[data-position="top-start"] { transform: translate(50%, -50%); }
116
+ [dir="rtl"] .kj-overlay-badge[data-position="bottom-end"] { transform: translate(-50%, 50%); }
117
+ [dir="rtl"] .kj-overlay-badge[data-position="bottom-start"] { transform: translate(50%, 50%); }
118
+ }